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