LCOV - code coverage report
Current view: top level - src/backend/optimizer/prep - prepqual.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 75.9 % 220 167
Test Date: 2026-07-08 05:15:38 Functions: 100.0 % 6 6
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 64.7 % 170 110

             Branch data     Line data    Source code
       1                 :             : /*-------------------------------------------------------------------------
       2                 :             :  *
       3                 :             :  * prepqual.c
       4                 :             :  *    Routines for preprocessing qualification expressions
       5                 :             :  *
       6                 :             :  *
       7                 :             :  * While the parser will produce flattened (N-argument) AND/OR trees from
       8                 :             :  * simple sequences of AND'ed or OR'ed clauses, there might be an AND clause
       9                 :             :  * directly underneath another AND, or OR underneath OR, if the input was
      10                 :             :  * oddly parenthesized.  Also, rule expansion and subquery flattening could
      11                 :             :  * produce such parsetrees.  The planner wants to flatten all such cases
      12                 :             :  * to ensure consistent optimization behavior.
      13                 :             :  *
      14                 :             :  * Formerly, this module was responsible for doing the initial flattening,
      15                 :             :  * but now we leave it to eval_const_expressions to do that since it has to
      16                 :             :  * make a complete pass over the expression tree anyway.  Instead, we just
      17                 :             :  * have to ensure that our manipulations preserve AND/OR flatness.
      18                 :             :  * pull_ands() and pull_ors() are used to maintain flatness of the AND/OR
      19                 :             :  * tree after local transformations that might introduce nested AND/ORs.
      20                 :             :  *
      21                 :             :  *
      22                 :             :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
      23                 :             :  * Portions Copyright (c) 1994, Regents of the University of California
      24                 :             :  *
      25                 :             :  *
      26                 :             :  * IDENTIFICATION
      27                 :             :  *    src/backend/optimizer/prep/prepqual.c
      28                 :             :  *
      29                 :             :  *-------------------------------------------------------------------------
      30                 :             :  */
      31                 :             : 
      32                 :             : #include "postgres.h"
      33                 :             : 
      34                 :             : #include "nodes/makefuncs.h"
      35                 :             : #include "nodes/nodeFuncs.h"
      36                 :             : #include "optimizer/optimizer.h"
      37                 :             : #include "utils/lsyscache.h"
      38                 :             : 
      39                 :             : 
      40                 :             : static List *pull_ands(List *andlist);
      41                 :             : static List *pull_ors(List *orlist);
      42                 :             : static Expr *find_duplicate_ors(Expr *qual, bool is_check);
      43                 :             : static Expr *process_duplicate_ors(List *orlist);
      44                 :             : 
      45                 :             : 
      46                 :             : /*
      47                 :             :  * negate_clause
      48                 :             :  *    Negate a Boolean expression.
      49                 :             :  *
      50                 :             :  * Input is a clause to be negated (e.g., the argument of a NOT clause).
      51                 :             :  * Returns a new clause equivalent to the negation of the given clause.
      52                 :             :  *
      53                 :             :  * Although this can be invoked on its own, it's mainly intended as a helper
      54                 :             :  * for eval_const_expressions(), and that context drives several design
      55                 :             :  * decisions.  In particular, if the input is already AND/OR flat, we must
      56                 :             :  * preserve that property.  We also don't bother to recurse in situations
      57                 :             :  * where we can assume that lower-level executions of eval_const_expressions
      58                 :             :  * would already have simplified sub-clauses of the input.
      59                 :             :  *
      60                 :             :  * The difference between this and a simple make_notclause() is that this
      61                 :             :  * tries to get rid of the NOT node by logical simplification.  It's clearly
      62                 :             :  * always a win if the NOT node can be eliminated altogether.  However, our
      63                 :             :  * use of DeMorgan's laws could result in having more NOT nodes rather than
      64                 :             :  * fewer.  We do that unconditionally anyway, because in WHERE clauses it's
      65                 :             :  * important to expose as much top-level AND/OR structure as possible.
      66                 :             :  * Also, eliminating an intermediate NOT may allow us to flatten two levels
      67                 :             :  * of AND or OR together that we couldn't have otherwise.  Finally, one of
      68                 :             :  * the motivations for doing this is to ensure that logically equivalent
      69                 :             :  * expressions will be seen as physically equal(), so we should always apply
      70                 :             :  * the same transformations.
      71                 :             :  */
      72                 :             : Node *
      73                 :       23923 : negate_clause(Node *node)
      74                 :             : {
      75         [ -  + ]:       23923 :     if (node == NULL)           /* should not happen */
      76         [ #  # ]:           0 :         elog(ERROR, "can't negate an empty subexpression");
      77   [ +  +  +  +  :       23923 :     switch (nodeTag(node))
                +  -  + ]
      78                 :             :     {
      79                 :          73 :         case T_Const:
      80                 :             :             {
      81                 :          73 :                 Const      *c = (Const *) node;
      82                 :             : 
      83                 :             :                 /* NOT NULL is still NULL */
      84         [ -  + ]:          73 :                 if (c->constisnull)
      85                 :           0 :                     return makeBoolConst(false, true);
      86                 :             :                 /* otherwise pretty easy */
      87                 :          73 :                 return makeBoolConst(!DatumGetBool(c->constvalue), false);
      88                 :             :             }
      89                 :             :             break;
      90                 :        4990 :         case T_OpExpr:
      91                 :             :             {
      92                 :             :                 /*
      93                 :             :                  * Negate operator if possible: (NOT (< A B)) => (>= A B)
      94                 :             :                  */
      95                 :        4990 :                 OpExpr     *opexpr = (OpExpr *) node;
      96                 :        4990 :                 Oid         negator = get_negator(opexpr->opno);
      97                 :             : 
      98         [ +  + ]:        4990 :                 if (negator)
      99                 :             :                 {
     100                 :        4834 :                     OpExpr     *newopexpr = makeNode(OpExpr);
     101                 :             : 
     102                 :        4834 :                     newopexpr->opno = negator;
     103                 :        4834 :                     newopexpr->opfuncid = InvalidOid;
     104                 :        4834 :                     newopexpr->opresulttype = opexpr->opresulttype;
     105                 :        4834 :                     newopexpr->opretset = opexpr->opretset;
     106                 :        4834 :                     newopexpr->opcollid = opexpr->opcollid;
     107                 :        4834 :                     newopexpr->inputcollid = opexpr->inputcollid;
     108                 :        4834 :                     newopexpr->args = opexpr->args;
     109                 :        4834 :                     newopexpr->location = opexpr->location;
     110                 :        4834 :                     return (Node *) newopexpr;
     111                 :             :                 }
     112                 :             :             }
     113                 :         156 :             break;
     114                 :         455 :         case T_ScalarArrayOpExpr:
     115                 :             :             {
     116                 :             :                 /*
     117                 :             :                  * Negate a ScalarArrayOpExpr if its operator has a negator;
     118                 :             :                  * for example x = ANY (list) becomes x <> ALL (list)
     119                 :             :                  */
     120                 :         455 :                 ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node;
     121                 :         455 :                 Oid         negator = get_negator(saopexpr->opno);
     122                 :             : 
     123         [ +  - ]:         455 :                 if (negator)
     124                 :             :                 {
     125                 :         455 :                     ScalarArrayOpExpr *newopexpr = makeNode(ScalarArrayOpExpr);
     126                 :             : 
     127                 :         455 :                     newopexpr->opno = negator;
     128                 :         455 :                     newopexpr->opfuncid = InvalidOid;
     129                 :         455 :                     newopexpr->hashfuncid = InvalidOid;
     130                 :         455 :                     newopexpr->negfuncid = InvalidOid;
     131                 :         455 :                     newopexpr->useOr = !saopexpr->useOr;
     132                 :         455 :                     newopexpr->inputcollid = saopexpr->inputcollid;
     133                 :         455 :                     newopexpr->args = saopexpr->args;
     134                 :         455 :                     newopexpr->location = saopexpr->location;
     135                 :         455 :                     return (Node *) newopexpr;
     136                 :             :                 }
     137                 :             :             }
     138                 :           0 :             break;
     139                 :        3062 :         case T_BoolExpr:
     140                 :             :             {
     141                 :        3062 :                 BoolExpr   *expr = (BoolExpr *) node;
     142                 :             : 
     143   [ +  +  +  - ]:        3062 :                 switch (expr->boolop)
     144                 :             :                 {
     145                 :             :                         /*--------------------
     146                 :             :                          * Apply DeMorgan's Laws:
     147                 :             :                          *      (NOT (AND A B)) => (OR (NOT A) (NOT B))
     148                 :             :                          *      (NOT (OR A B))  => (AND (NOT A) (NOT B))
     149                 :             :                          * i.e., swap AND for OR and negate each subclause.
     150                 :             :                          *
     151                 :             :                          * If the input is already AND/OR flat and has no NOT
     152                 :             :                          * directly above AND or OR, this transformation preserves
     153                 :             :                          * those properties.  For example, if no direct child of
     154                 :             :                          * the given AND clause is an AND or a NOT-above-OR, then
     155                 :             :                          * the recursive calls of negate_clause() can't return any
     156                 :             :                          * OR clauses.  So we needn't call pull_ors() before
     157                 :             :                          * building a new OR clause.  Similarly for the OR case.
     158                 :             :                          *--------------------
     159                 :             :                          */
     160                 :        2500 :                     case AND_EXPR:
     161                 :             :                         {
     162                 :        2500 :                             List       *nargs = NIL;
     163                 :             :                             ListCell   *lc;
     164                 :             : 
     165   [ +  -  +  +  :        8621 :                             foreach(lc, expr->args)
                   +  + ]
     166                 :             :                             {
     167                 :        6121 :                                 nargs = lappend(nargs,
     168                 :        6121 :                                                 negate_clause(lfirst(lc)));
     169                 :             :                             }
     170                 :        2500 :                             return (Node *) make_orclause(nargs);
     171                 :             :                         }
     172                 :             :                         break;
     173                 :         485 :                     case OR_EXPR:
     174                 :             :                         {
     175                 :         485 :                             List       *nargs = NIL;
     176                 :             :                             ListCell   *lc;
     177                 :             : 
     178   [ +  -  +  +  :        2078 :                             foreach(lc, expr->args)
                   +  + ]
     179                 :             :                             {
     180                 :        1593 :                                 nargs = lappend(nargs,
     181                 :        1593 :                                                 negate_clause(lfirst(lc)));
     182                 :             :                             }
     183                 :         485 :                             return (Node *) make_andclause(nargs);
     184                 :             :                         }
     185                 :             :                         break;
     186                 :          77 :                     case NOT_EXPR:
     187                 :             : 
     188                 :             :                         /*
     189                 :             :                          * NOT underneath NOT: they cancel.  We assume the
     190                 :             :                          * input is already simplified, so no need to recurse.
     191                 :             :                          */
     192                 :          77 :                         return (Node *) linitial(expr->args);
     193                 :           0 :                     default:
     194         [ #  # ]:           0 :                         elog(ERROR, "unrecognized boolop: %d",
     195                 :             :                              (int) expr->boolop);
     196                 :             :                         break;
     197                 :             :                 }
     198                 :             :             }
     199                 :             :             break;
     200                 :        1369 :         case T_NullTest:
     201                 :             :             {
     202                 :        1369 :                 NullTest   *expr = (NullTest *) node;
     203                 :             : 
     204                 :             :                 /*
     205                 :             :                  * In the rowtype case, the two flavors of NullTest are *not*
     206                 :             :                  * logical inverses, so we can't simplify.  But it does work
     207                 :             :                  * for scalar datatypes.
     208                 :             :                  */
     209         [ +  - ]:        1369 :                 if (!expr->argisrow)
     210                 :             :                 {
     211                 :        1369 :                     NullTest   *newexpr = makeNode(NullTest);
     212                 :             : 
     213                 :        1369 :                     newexpr->arg = expr->arg;
     214                 :        1369 :                     newexpr->nulltesttype = (expr->nulltesttype == IS_NULL ?
     215                 :        1369 :                                              IS_NOT_NULL : IS_NULL);
     216                 :        1369 :                     newexpr->argisrow = expr->argisrow;
     217                 :        1369 :                     newexpr->location = expr->location;
     218                 :        1369 :                     return (Node *) newexpr;
     219                 :             :                 }
     220                 :             :             }
     221                 :           0 :             break;
     222                 :           0 :         case T_BooleanTest:
     223                 :             :             {
     224                 :           0 :                 BooleanTest *expr = (BooleanTest *) node;
     225                 :           0 :                 BooleanTest *newexpr = makeNode(BooleanTest);
     226                 :             : 
     227                 :           0 :                 newexpr->arg = expr->arg;
     228   [ #  #  #  #  :           0 :                 switch (expr->booltesttype)
                #  #  # ]
     229                 :             :                 {
     230                 :           0 :                     case IS_TRUE:
     231                 :           0 :                         newexpr->booltesttype = IS_NOT_TRUE;
     232                 :           0 :                         break;
     233                 :           0 :                     case IS_NOT_TRUE:
     234                 :           0 :                         newexpr->booltesttype = IS_TRUE;
     235                 :           0 :                         break;
     236                 :           0 :                     case IS_FALSE:
     237                 :           0 :                         newexpr->booltesttype = IS_NOT_FALSE;
     238                 :           0 :                         break;
     239                 :           0 :                     case IS_NOT_FALSE:
     240                 :           0 :                         newexpr->booltesttype = IS_FALSE;
     241                 :           0 :                         break;
     242                 :           0 :                     case IS_UNKNOWN:
     243                 :           0 :                         newexpr->booltesttype = IS_NOT_UNKNOWN;
     244                 :           0 :                         break;
     245                 :           0 :                     case IS_NOT_UNKNOWN:
     246                 :           0 :                         newexpr->booltesttype = IS_UNKNOWN;
     247                 :           0 :                         break;
     248                 :           0 :                     default:
     249         [ #  # ]:           0 :                         elog(ERROR, "unrecognized booltesttype: %d",
     250                 :             :                              (int) expr->booltesttype);
     251                 :             :                         break;
     252                 :             :                 }
     253                 :           0 :                 newexpr->location = expr->location;
     254                 :           0 :                 return (Node *) newexpr;
     255                 :             :             }
     256                 :             :             break;
     257                 :       13974 :         default:
     258                 :             :             /* else fall through */
     259                 :       13974 :             break;
     260                 :             :     }
     261                 :             : 
     262                 :             :     /*
     263                 :             :      * Otherwise we don't know how to simplify this, so just tack on an
     264                 :             :      * explicit NOT node.
     265                 :             :      */
     266                 :       14130 :     return (Node *) make_notclause((Expr *) node);
     267                 :             : }
     268                 :             : 
     269                 :             : 
     270                 :             : /*
     271                 :             :  * canonicalize_qual
     272                 :             :  *    Convert a qualification expression to the most useful form.
     273                 :             :  *
     274                 :             :  * This is primarily intended to be used on top-level WHERE (or JOIN/ON)
     275                 :             :  * clauses.  It can also be used on top-level CHECK constraints, for which
     276                 :             :  * pass is_check = true.  DO NOT call it on any expression that is not known
     277                 :             :  * to be one or the other, as it might apply inappropriate simplifications.
     278                 :             :  *
     279                 :             :  * The name of this routine is a holdover from a time when it would try to
     280                 :             :  * force the expression into canonical AND-of-ORs or OR-of-ANDs form.
     281                 :             :  * Eventually, we recognized that that had more theoretical purity than
     282                 :             :  * actual usefulness, and so now the transformation doesn't involve any
     283                 :             :  * notion of reaching a canonical form.
     284                 :             :  *
     285                 :             :  * NOTE: we assume the input has already been through eval_const_expressions
     286                 :             :  * and therefore possesses AND/OR flatness.  Formerly this function included
     287                 :             :  * its own flattening logic, but that requires a useless extra pass over the
     288                 :             :  * tree.
     289                 :             :  *
     290                 :             :  * Returns the modified qualification.
     291                 :             :  */
     292                 :             : Expr *
     293                 :      261792 : canonicalize_qual(Expr *qual, bool is_check)
     294                 :             : {
     295                 :             :     Expr       *newqual;
     296                 :             : 
     297                 :             :     /* Quick exit for empty qual */
     298         [ -  + ]:      261792 :     if (qual == NULL)
     299                 :           0 :         return NULL;
     300                 :             : 
     301                 :             :     /* This should not be invoked on quals in implicit-AND format */
     302                 :             :     Assert(!IsA(qual, List));
     303                 :             : 
     304                 :             :     /*
     305                 :             :      * Pull up redundant subclauses in OR-of-AND trees.  We do this only
     306                 :             :      * within the top-level AND/OR structure; there's no point in looking
     307                 :             :      * deeper.  Also remove any NULL constants in the top-level structure.
     308                 :             :      */
     309                 :      261792 :     newqual = find_duplicate_ors(qual, is_check);
     310                 :             : 
     311                 :      261792 :     return newqual;
     312                 :             : }
     313                 :             : 
     314                 :             : 
     315                 :             : /*
     316                 :             :  * pull_ands
     317                 :             :  *    Recursively flatten nested AND clauses into a single and-clause list.
     318                 :             :  *
     319                 :             :  * Input is the arglist of an AND clause.
     320                 :             :  * Returns the rebuilt arglist (note original list structure is not touched).
     321                 :             :  */
     322                 :             : static List *
     323                 :      102400 : pull_ands(List *andlist)
     324                 :             : {
     325                 :      102400 :     List       *out_list = NIL;
     326                 :             :     ListCell   *arg;
     327                 :             : 
     328   [ +  -  +  +  :      373066 :     foreach(arg, andlist)
                   +  + ]
     329                 :             :     {
     330                 :      270666 :         Node       *subexpr = (Node *) lfirst(arg);
     331                 :             : 
     332         [ -  + ]:      270666 :         if (is_andclause(subexpr))
     333                 :           0 :             out_list = list_concat(out_list,
     334                 :           0 :                                    pull_ands(((BoolExpr *) subexpr)->args));
     335                 :             :         else
     336                 :      270666 :             out_list = lappend(out_list, subexpr);
     337                 :             :     }
     338                 :      102400 :     return out_list;
     339                 :             : }
     340                 :             : 
     341                 :             : /*
     342                 :             :  * pull_ors
     343                 :             :  *    Recursively flatten nested OR clauses into a single or-clause list.
     344                 :             :  *
     345                 :             :  * Input is the arglist of an OR clause.
     346                 :             :  * Returns the rebuilt arglist (note original list structure is not touched).
     347                 :             :  */
     348                 :             : static List *
     349                 :       12059 : pull_ors(List *orlist)
     350                 :             : {
     351                 :       12059 :     List       *out_list = NIL;
     352                 :             :     ListCell   *arg;
     353                 :             : 
     354   [ +  -  +  +  :       35001 :     foreach(arg, orlist)
                   +  + ]
     355                 :             :     {
     356                 :       22942 :         Node       *subexpr = (Node *) lfirst(arg);
     357                 :             : 
     358         [ -  + ]:       22942 :         if (is_orclause(subexpr))
     359                 :           0 :             out_list = list_concat(out_list,
     360                 :           0 :                                    pull_ors(((BoolExpr *) subexpr)->args));
     361                 :             :         else
     362                 :       22942 :             out_list = lappend(out_list, subexpr);
     363                 :             :     }
     364                 :       12059 :     return out_list;
     365                 :             : }
     366                 :             : 
     367                 :             : 
     368                 :             : /*--------------------
     369                 :             :  * The following code attempts to apply the inverse OR distributive law:
     370                 :             :  *      ((A AND B) OR (A AND C))  =>  (A AND (B OR C))
     371                 :             :  * That is, locate OR clauses in which every subclause contains an
     372                 :             :  * identical term, and pull out the duplicated terms.
     373                 :             :  *
     374                 :             :  * This may seem like a fairly useless activity, but it turns out to be
     375                 :             :  * applicable to many machine-generated queries, and there are also queries
     376                 :             :  * in some of the TPC benchmarks that need it.  This was in fact almost the
     377                 :             :  * sole useful side-effect of the old prepqual code that tried to force
     378                 :             :  * the query into canonical AND-of-ORs form: the canonical equivalent of
     379                 :             :  *      ((A AND B) OR (A AND C))
     380                 :             :  * is
     381                 :             :  *      ((A OR A) AND (A OR C) AND (B OR A) AND (B OR C))
     382                 :             :  * which the code was able to simplify to
     383                 :             :  *      (A AND (A OR C) AND (B OR A) AND (B OR C))
     384                 :             :  * thus successfully extracting the common condition A --- but at the cost
     385                 :             :  * of cluttering the qual with many redundant clauses.
     386                 :             :  *--------------------
     387                 :             :  */
     388                 :             : 
     389                 :             : /*
     390                 :             :  * find_duplicate_ors
     391                 :             :  *    Given a qualification tree with the NOTs pushed down, search for
     392                 :             :  *    OR clauses to which the inverse OR distributive law might apply.
     393                 :             :  *    Only the top-level AND/OR structure is searched.
     394                 :             :  *
     395                 :             :  * While at it, we remove any NULL constants within the top-level AND/OR
     396                 :             :  * structure, eg in a WHERE clause, "x OR NULL::boolean" is reduced to "x".
     397                 :             :  * In general that would change the result, so eval_const_expressions can't
     398                 :             :  * do it; but at top level of WHERE, we don't need to distinguish between
     399                 :             :  * FALSE and NULL results, so it's valid to treat NULL::boolean the same
     400                 :             :  * as FALSE and then simplify AND/OR accordingly.  Conversely, in a top-level
     401                 :             :  * CHECK constraint, we may treat a NULL the same as TRUE.
     402                 :             :  *
     403                 :             :  * Returns the modified qualification.  AND/OR flatness is preserved.
     404                 :             :  */
     405                 :             : static Expr *
     406                 :      559215 : find_duplicate_ors(Expr *qual, bool is_check)
     407                 :             : {
     408         [ +  + ]:      559215 :     if (is_orclause(qual))
     409                 :             :     {
     410                 :       12064 :         List       *orlist = NIL;
     411                 :             :         ListCell   *temp;
     412                 :             : 
     413                 :             :         /* Recurse */
     414   [ +  -  +  +  :       38796 :         foreach(temp, ((BoolExpr *) qual)->args)
                   +  + ]
     415                 :             :         {
     416                 :       26737 :             Expr       *arg = (Expr *) lfirst(temp);
     417                 :             : 
     418                 :       26737 :             arg = find_duplicate_ors(arg, is_check);
     419                 :             : 
     420                 :             :             /* Get rid of any constant inputs */
     421   [ +  -  +  + ]:       26737 :             if (arg && IsA(arg, Const))
     422                 :             :             {
     423                 :        3790 :                 Const      *carg = (Const *) arg;
     424                 :             : 
     425         [ +  + ]:        3790 :                 if (is_check)
     426                 :             :                 {
     427                 :             :                     /* Within OR in CHECK, drop constant FALSE */
     428   [ -  +  -  - ]:           5 :                     if (!carg->constisnull && !DatumGetBool(carg->constvalue))
     429                 :           0 :                         continue;
     430                 :             :                     /* Constant TRUE or NULL, so OR reduces to TRUE */
     431                 :           5 :                     return (Expr *) makeBoolConst(true, false);
     432                 :             :                 }
     433                 :             :                 else
     434                 :             :                 {
     435                 :             :                     /* Within OR in WHERE, drop constant FALSE or NULL */
     436   [ -  +  -  - ]:        3785 :                     if (carg->constisnull || !DatumGetBool(carg->constvalue))
     437                 :        3785 :                         continue;
     438                 :             :                     /* Constant TRUE, so OR reduces to TRUE */
     439                 :           0 :                     return arg;
     440                 :             :                 }
     441                 :             :             }
     442                 :             : 
     443                 :       22947 :             orlist = lappend(orlist, arg);
     444                 :             :         }
     445                 :             : 
     446                 :             :         /* Flatten any ORs pulled up to just below here */
     447                 :       12059 :         orlist = pull_ors(orlist);
     448                 :             : 
     449                 :             :         /* Now we can look for duplicate ORs */
     450                 :       12059 :         return process_duplicate_ors(orlist);
     451                 :             :     }
     452         [ +  + ]:      547151 :     else if (is_andclause(qual))
     453                 :             :     {
     454                 :      102410 :         List       *andlist = NIL;
     455                 :             :         ListCell   *temp;
     456                 :             : 
     457                 :             :         /* Recurse */
     458   [ +  -  +  +  :      373076 :         foreach(temp, ((BoolExpr *) qual)->args)
                   +  + ]
     459                 :             :         {
     460                 :      270686 :             Expr       *arg = (Expr *) lfirst(temp);
     461                 :             : 
     462                 :      270686 :             arg = find_duplicate_ors(arg, is_check);
     463                 :             : 
     464                 :             :             /* Get rid of any constant inputs */
     465   [ +  -  +  + ]:      270686 :             if (arg && IsA(arg, Const))
     466                 :             :             {
     467                 :          20 :                 Const      *carg = (Const *) arg;
     468                 :             : 
     469         [ -  + ]:          20 :                 if (is_check)
     470                 :             :                 {
     471                 :             :                     /* Within AND in CHECK, drop constant TRUE or NULL */
     472   [ #  #  #  # ]:           0 :                     if (carg->constisnull || DatumGetBool(carg->constvalue))
     473                 :           0 :                         continue;
     474                 :             :                     /* Constant FALSE, so AND reduces to FALSE */
     475                 :          20 :                     return arg;
     476                 :             :                 }
     477                 :             :                 else
     478                 :             :                 {
     479                 :             :                     /* Within AND in WHERE, drop constant TRUE */
     480   [ -  +  -  - ]:          20 :                     if (!carg->constisnull && DatumGetBool(carg->constvalue))
     481                 :           0 :                         continue;
     482                 :             :                     /* Constant FALSE or NULL, so AND reduces to FALSE */
     483                 :          20 :                     return (Expr *) makeBoolConst(false, false);
     484                 :             :                 }
     485                 :             :             }
     486                 :             : 
     487                 :      270666 :             andlist = lappend(andlist, arg);
     488                 :             :         }
     489                 :             : 
     490                 :             :         /* Flatten any ANDs introduced just below here */
     491                 :      102390 :         andlist = pull_ands(andlist);
     492                 :             : 
     493                 :             :         /* AND of no inputs reduces to TRUE */
     494         [ -  + ]:      102390 :         if (andlist == NIL)
     495                 :           0 :             return (Expr *) makeBoolConst(true, false);
     496                 :             : 
     497                 :             :         /* Single-expression AND just reduces to that expression */
     498         [ -  + ]:      102390 :         if (list_length(andlist) == 1)
     499                 :           0 :             return (Expr *) linitial(andlist);
     500                 :             : 
     501                 :             :         /* Else we still need an AND node */
     502                 :      102390 :         return make_andclause(andlist);
     503                 :             :     }
     504                 :             :     else
     505                 :      444741 :         return qual;
     506                 :             : }
     507                 :             : 
     508                 :             : /*
     509                 :             :  * process_duplicate_ors
     510                 :             :  *    Given a list of exprs which are ORed together, try to apply
     511                 :             :  *    the inverse OR distributive law.
     512                 :             :  *
     513                 :             :  * Returns the resulting expression (could be an AND clause, an OR
     514                 :             :  * clause, or maybe even a single subexpression).
     515                 :             :  */
     516                 :             : static Expr *
     517                 :       12059 : process_duplicate_ors(List *orlist)
     518                 :             : {
     519                 :       12059 :     List       *reference = NIL;
     520                 :       12059 :     int         num_subclauses = 0;
     521                 :             :     List       *winners;
     522                 :             :     List       *neworlist;
     523                 :             :     ListCell   *temp;
     524                 :             : 
     525                 :             :     /* OR of no inputs reduces to FALSE */
     526         [ -  + ]:       12059 :     if (orlist == NIL)
     527                 :           0 :         return (Expr *) makeBoolConst(false, false);
     528                 :             : 
     529                 :             :     /* Single-expression OR just reduces to that expression */
     530         [ +  + ]:       12059 :     if (list_length(orlist) == 1)
     531                 :        3785 :         return (Expr *) linitial(orlist);
     532                 :             : 
     533                 :             :     /*
     534                 :             :      * Choose the shortest AND clause as the reference list --- obviously, any
     535                 :             :      * subclause not in this clause isn't in all the clauses. If we find a
     536                 :             :      * clause that's not an AND, we can treat it as a one-element AND clause,
     537                 :             :      * which necessarily wins as shortest.
     538                 :             :      */
     539   [ +  -  +  +  :        9250 :     foreach(temp, orlist)
                   +  + ]
     540                 :             :     {
     541                 :        8919 :         Expr       *clause = (Expr *) lfirst(temp);
     542                 :             : 
     543         [ +  + ]:        8919 :         if (is_andclause(clause))
     544                 :             :         {
     545                 :         976 :             List       *subclauses = ((BoolExpr *) clause)->args;
     546                 :         976 :             int         nclauses = list_length(subclauses);
     547                 :             : 
     548   [ +  +  +  + ]:         976 :             if (reference == NIL || nclauses < num_subclauses)
     549                 :             :             {
     550                 :         719 :                 reference = subclauses;
     551                 :         719 :                 num_subclauses = nclauses;
     552                 :             :             }
     553                 :             :         }
     554                 :             :         else
     555                 :             :         {
     556                 :        7943 :             reference = list_make1(clause);
     557                 :        7943 :             break;
     558                 :             :         }
     559                 :             :     }
     560                 :             : 
     561                 :             :     /*
     562                 :             :      * Just in case, eliminate any duplicates in the reference list.
     563                 :             :      */
     564                 :        8274 :     reference = list_union(NIL, reference);
     565                 :             : 
     566                 :             :     /*
     567                 :             :      * Check each element of the reference list to see if it's in all the OR
     568                 :             :      * clauses.  Build a new list of winning clauses.
     569                 :             :      */
     570                 :        8274 :     winners = NIL;
     571   [ +  -  +  +  :       16889 :     foreach(temp, reference)
                   +  + ]
     572                 :             :     {
     573                 :        8615 :         Expr       *refclause = (Expr *) lfirst(temp);
     574                 :        8615 :         bool        win = true;
     575                 :             :         ListCell   *temp2;
     576                 :             : 
     577   [ +  -  +  +  :       17165 :         foreach(temp2, orlist)
                   +  + ]
     578                 :             :         {
     579                 :       16743 :             Expr       *clause = (Expr *) lfirst(temp2);
     580                 :             : 
     581         [ +  + ]:       16743 :             if (is_andclause(clause))
     582                 :             :             {
     583         [ +  + ]:        1720 :                 if (!list_member(((BoolExpr *) clause)->args, refclause))
     584                 :             :                 {
     585                 :        1256 :                     win = false;
     586                 :        1256 :                     break;
     587                 :             :                 }
     588                 :             :             }
     589                 :             :             else
     590                 :             :             {
     591         [ +  + ]:       15023 :                 if (!equal(refclause, clause))
     592                 :             :                 {
     593                 :        6937 :                     win = false;
     594                 :        6937 :                     break;
     595                 :             :                 }
     596                 :             :             }
     597                 :             :         }
     598                 :             : 
     599         [ +  + ]:        8615 :         if (win)
     600                 :         422 :             winners = lappend(winners, refclause);
     601                 :             :     }
     602                 :             : 
     603                 :             :     /*
     604                 :             :      * If no winners, we can't transform the OR
     605                 :             :      */
     606         [ +  + ]:        8274 :     if (winners == NIL)
     607                 :        7862 :         return make_orclause(orlist);
     608                 :             : 
     609                 :             :     /*
     610                 :             :      * Generate new OR list consisting of the remaining sub-clauses.
     611                 :             :      *
     612                 :             :      * If any clause degenerates to empty, then we have a situation like (A
     613                 :             :      * AND B) OR (A), which can be reduced to just A --- that is, the
     614                 :             :      * additional conditions in other arms of the OR are irrelevant.
     615                 :             :      *
     616                 :             :      * Note that because we use list_difference, any multiple occurrences of a
     617                 :             :      * winning clause in an AND sub-clause will be removed automatically.
     618                 :             :      */
     619                 :         412 :     neworlist = NIL;
     620   [ +  -  +  -  :         412 :     foreach(temp, orlist)
                   +  - ]
     621                 :             :     {
     622                 :         412 :         Expr       *clause = (Expr *) lfirst(temp);
     623                 :             : 
     624         [ +  + ]:         412 :         if (is_andclause(clause))
     625                 :             :         {
     626                 :          10 :             List       *subclauses = ((BoolExpr *) clause)->args;
     627                 :             : 
     628                 :          10 :             subclauses = list_difference(subclauses, winners);
     629         [ -  + ]:          10 :             if (subclauses != NIL)
     630                 :             :             {
     631         [ #  # ]:           0 :                 if (list_length(subclauses) == 1)
     632                 :           0 :                     neworlist = lappend(neworlist, linitial(subclauses));
     633                 :             :                 else
     634                 :           0 :                     neworlist = lappend(neworlist, make_andclause(subclauses));
     635                 :             :             }
     636                 :             :             else
     637                 :             :             {
     638                 :          10 :                 neworlist = NIL;    /* degenerate case, see above */
     639                 :          10 :                 break;
     640                 :             :             }
     641                 :             :         }
     642                 :             :         else
     643                 :             :         {
     644         [ -  + ]:         402 :             if (!list_member(winners, clause))
     645                 :           0 :                 neworlist = lappend(neworlist, clause);
     646                 :             :             else
     647                 :             :             {
     648                 :         402 :                 neworlist = NIL;    /* degenerate case, see above */
     649                 :         402 :                 break;
     650                 :             :             }
     651                 :             :         }
     652                 :             :     }
     653                 :             : 
     654                 :             :     /*
     655                 :             :      * Append reduced OR to the winners list, if it's not degenerate, handling
     656                 :             :      * the special case of one element correctly (can that really happen?).
     657                 :             :      * Also be careful to maintain AND/OR flatness in case we pulled up a
     658                 :             :      * sub-sub-OR-clause.
     659                 :             :      */
     660         [ -  + ]:         412 :     if (neworlist != NIL)
     661                 :             :     {
     662         [ #  # ]:           0 :         if (list_length(neworlist) == 1)
     663                 :           0 :             winners = lappend(winners, linitial(neworlist));
     664                 :             :         else
     665                 :           0 :             winners = lappend(winners, make_orclause(pull_ors(neworlist)));
     666                 :             :     }
     667                 :             : 
     668                 :             :     /*
     669                 :             :      * And return the constructed AND clause, again being wary of a single
     670                 :             :      * element and AND/OR flatness.
     671                 :             :      */
     672         [ +  + ]:         412 :     if (list_length(winners) == 1)
     673                 :         402 :         return (Expr *) linitial(winners);
     674                 :             :     else
     675                 :          10 :         return make_andclause(pull_ands(winners));
     676                 :             : }
        

Generated by: LCOV version 2.0-1