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 : 3262 : negate_clause(Node *node)
74 : : {
75 [ + - ]: 3262 : if (node == NULL) /* should not happen */
76 [ # # # # ]: 0 : elog(ERROR, "can't negate an empty subexpression");
77 [ + + + + : 3262 : switch (nodeTag(node))
+ + - ]
78 : : {
79 : : case T_Const:
80 : : {
81 : 9 : Const *c = (Const *) node;
82 : :
83 : : /* NOT NULL is still NULL */
84 [ - + ]: 9 : if (c->constisnull)
85 : 0 : return makeBoolConst(false, true);
86 : : /* otherwise pretty easy */
87 : 9 : return makeBoolConst(!DatumGetBool(c->constvalue), false);
88 : 9 : }
89 : : break;
90 : : case T_OpExpr:
91 : : {
92 : : /*
93 : : * Negate operator if possible: (NOT (< A B)) => (>= A B)
94 : : */
95 : 852 : OpExpr *opexpr = (OpExpr *) node;
96 : 852 : Oid negator = get_negator(opexpr->opno);
97 : :
98 [ + + ]: 852 : if (negator)
99 : : {
100 : 834 : OpExpr *newopexpr = makeNode(OpExpr);
101 : :
102 : 834 : newopexpr->opno = negator;
103 : 834 : newopexpr->opfuncid = InvalidOid;
104 : 834 : newopexpr->opresulttype = opexpr->opresulttype;
105 : 834 : newopexpr->opretset = opexpr->opretset;
106 : 834 : newopexpr->opcollid = opexpr->opcollid;
107 : 834 : newopexpr->inputcollid = opexpr->inputcollid;
108 : 834 : newopexpr->args = opexpr->args;
109 : 834 : newopexpr->location = opexpr->location;
110 : 834 : return (Node *) newopexpr;
111 : 834 : }
112 [ + + ]: 852 : }
113 : 18 : break;
114 : : 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 : 70 : ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node;
121 : 70 : Oid negator = get_negator(saopexpr->opno);
122 : :
123 [ + - ]: 70 : if (negator)
124 : : {
125 : 70 : ScalarArrayOpExpr *newopexpr = makeNode(ScalarArrayOpExpr);
126 : :
127 : 70 : newopexpr->opno = negator;
128 : 70 : newopexpr->opfuncid = InvalidOid;
129 : 70 : newopexpr->hashfuncid = InvalidOid;
130 : 70 : newopexpr->negfuncid = InvalidOid;
131 : 70 : newopexpr->useOr = !saopexpr->useOr;
132 : 70 : newopexpr->inputcollid = saopexpr->inputcollid;
133 : 70 : newopexpr->args = saopexpr->args;
134 : 70 : newopexpr->location = saopexpr->location;
135 : 70 : return (Node *) newopexpr;
136 : 70 : }
137 [ + - ]: 70 : }
138 : 0 : break;
139 : : case T_BoolExpr:
140 : : {
141 : 550 : BoolExpr *expr = (BoolExpr *) node;
142 : :
143 [ + + + - ]: 550 : 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 : : case AND_EXPR:
161 : : {
162 : 429 : List *nargs = NIL;
163 : 429 : ListCell *lc;
164 : :
165 [ + - + + : 1537 : foreach(lc, expr->args)
+ + ]
166 : : {
167 : 2216 : nargs = lappend(nargs,
168 : 1108 : negate_clause(lfirst(lc)));
169 : 1108 : }
170 : 429 : return (Node *) make_orclause(nargs);
171 : 429 : }
172 : : break;
173 : : case OR_EXPR:
174 : : {
175 : 105 : List *nargs = NIL;
176 : 105 : ListCell *lc;
177 : :
178 [ + - + + : 448 : foreach(lc, expr->args)
+ + ]
179 : : {
180 : 686 : nargs = lappend(nargs,
181 : 343 : negate_clause(lfirst(lc)));
182 : 343 : }
183 : 105 : return (Node *) make_andclause(nargs);
184 : 105 : }
185 : : break;
186 : : 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 : 16 : return (Node *) linitial(expr->args);
193 : : default:
194 [ # # # # ]: 0 : elog(ERROR, "unrecognized boolop: %d",
195 : : (int) expr->boolop);
196 : 0 : break;
197 : : }
198 [ + - ]: 550 : }
199 : 0 : break;
200 : : case T_NullTest:
201 : : {
202 : 292 : 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 [ - + ]: 292 : if (!expr->argisrow)
210 : : {
211 : 292 : NullTest *newexpr = makeNode(NullTest);
212 : :
213 : 292 : newexpr->arg = expr->arg;
214 : 292 : newexpr->nulltesttype = (expr->nulltesttype == IS_NULL ?
215 : : IS_NOT_NULL : IS_NULL);
216 : 292 : newexpr->argisrow = expr->argisrow;
217 : 292 : newexpr->location = expr->location;
218 : 292 : return (Node *) newexpr;
219 : 292 : }
220 [ + - ]: 292 : }
221 : 0 : break;
222 : : 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 : : case IS_TRUE:
231 : 0 : newexpr->booltesttype = IS_NOT_TRUE;
232 : 0 : break;
233 : : case IS_NOT_TRUE:
234 : 0 : newexpr->booltesttype = IS_TRUE;
235 : 0 : break;
236 : : case IS_FALSE:
237 : 0 : newexpr->booltesttype = IS_NOT_FALSE;
238 : 0 : break;
239 : : case IS_NOT_FALSE:
240 : 0 : newexpr->booltesttype = IS_FALSE;
241 : 0 : break;
242 : : case IS_UNKNOWN:
243 : 0 : newexpr->booltesttype = IS_NOT_UNKNOWN;
244 : 0 : break;
245 : : case IS_NOT_UNKNOWN:
246 : 0 : newexpr->booltesttype = IS_UNKNOWN;
247 : 0 : break;
248 : : default:
249 [ # # # # ]: 0 : elog(ERROR, "unrecognized booltesttype: %d",
250 : : (int) expr->booltesttype);
251 : 0 : break;
252 : : }
253 : 0 : newexpr->location = expr->location;
254 : 0 : return (Node *) newexpr;
255 : 0 : }
256 : : break;
257 : : default:
258 : : /* else fall through */
259 : 1489 : 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 : 1507 : return (Node *) make_notclause((Expr *) node);
267 : 3262 : }
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 : 33441 : canonicalize_qual(Expr *qual, bool is_check)
294 : : {
295 : 33441 : Expr *newqual;
296 : :
297 : : /* Quick exit for empty qual */
298 [ + - ]: 33441 : if (qual == NULL)
299 : 0 : return NULL;
300 : :
301 : : /* This should not be invoked on quals in implicit-AND format */
302 [ + - ]: 33441 : 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 : 33441 : newqual = find_duplicate_ors(qual, is_check);
310 : :
311 : 33441 : return newqual;
312 : 33441 : }
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 : 12091 : pull_ands(List *andlist)
324 : : {
325 : 12091 : List *out_list = NIL;
326 : 12091 : ListCell *arg;
327 : :
328 [ + - + + : 42696 : foreach(arg, andlist)
+ + ]
329 : : {
330 : 30605 : Node *subexpr = (Node *) lfirst(arg);
331 : :
332 [ - + ]: 30605 : if (is_andclause(subexpr))
333 : 0 : out_list = list_concat(out_list,
334 : 0 : pull_ands(((BoolExpr *) subexpr)->args));
335 : : else
336 : 30605 : out_list = lappend(out_list, subexpr);
337 : 30605 : }
338 : 24182 : return out_list;
339 : 12091 : }
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 : 795 : pull_ors(List *orlist)
350 : : {
351 : 795 : List *out_list = NIL;
352 : 795 : ListCell *arg;
353 : :
354 [ + - + + : 2813 : foreach(arg, orlist)
+ + ]
355 : : {
356 : 2018 : Node *subexpr = (Node *) lfirst(arg);
357 : :
358 [ - + ]: 2018 : if (is_orclause(subexpr))
359 : 0 : out_list = list_concat(out_list,
360 : 0 : pull_ors(((BoolExpr *) subexpr)->args));
361 : : else
362 : 2018 : out_list = lappend(out_list, subexpr);
363 : 2018 : }
364 : 1590 : return out_list;
365 : 795 : }
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 : 66067 : find_duplicate_ors(Expr *qual, bool is_check)
407 : : {
408 [ + + ]: 66067 : if (is_orclause(qual))
409 : : {
410 : 796 : List *orlist = NIL;
411 : 796 : ListCell *temp;
412 : :
413 : : /* Recurse */
414 [ + - + + : 2817 : foreach(temp, ((BoolExpr *) qual)->args)
+ + + + ]
415 : : {
416 : 2021 : Expr *arg = (Expr *) lfirst(temp);
417 : :
418 : 2021 : arg = find_duplicate_ors(arg, is_check);
419 : :
420 : : /* Get rid of any constant inputs */
421 [ + - + + ]: 2021 : if (arg && IsA(arg, Const))
422 : : {
423 : 2 : Const *carg = (Const *) arg;
424 : :
425 [ + + ]: 2 : if (is_check)
426 : : {
427 : : /* Within OR in CHECK, drop constant FALSE */
428 [ - + # # ]: 1 : if (!carg->constisnull && !DatumGetBool(carg->constvalue))
429 : 0 : continue;
430 : : /* Constant TRUE or NULL, so OR reduces to TRUE */
431 : 1 : return (Expr *) makeBoolConst(true, false);
432 : : }
433 : : else
434 : : {
435 : : /* Within OR in WHERE, drop constant FALSE or NULL */
436 [ - + # # ]: 1 : if (carg->constisnull || !DatumGetBool(carg->constvalue))
437 : 1 : continue;
438 : : /* Constant TRUE, so OR reduces to TRUE */
439 : 0 : return arg;
440 : : }
441 : 2 : }
442 : :
443 : 2019 : orlist = lappend(orlist, arg);
444 [ + + + ]: 2021 : }
445 : :
446 : : /* Flatten any ORs pulled up to just below here */
447 : 795 : orlist = pull_ors(orlist);
448 : :
449 : : /* Now we can look for duplicate ORs */
450 : 795 : return process_duplicate_ors(orlist);
451 : 796 : }
452 [ + + ]: 65271 : else if (is_andclause(qual))
453 : : {
454 : 12091 : List *andlist = NIL;
455 : 12091 : ListCell *temp;
456 : :
457 : : /* Recurse */
458 [ + - + + : 42696 : foreach(temp, ((BoolExpr *) qual)->args)
+ + - + ]
459 : : {
460 : 30605 : Expr *arg = (Expr *) lfirst(temp);
461 : :
462 : 30605 : arg = find_duplicate_ors(arg, is_check);
463 : :
464 : : /* Get rid of any constant inputs */
465 [ + - + - ]: 30605 : if (arg && IsA(arg, Const))
466 : : {
467 : 0 : Const *carg = (Const *) arg;
468 : :
469 [ # # ]: 0 : 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 : 0 : return arg;
476 : : }
477 : : else
478 : : {
479 : : /* Within AND in WHERE, drop constant TRUE */
480 [ # # # # ]: 0 : if (!carg->constisnull && DatumGetBool(carg->constvalue))
481 : 0 : continue;
482 : : /* Constant FALSE or NULL, so AND reduces to FALSE */
483 : 0 : return (Expr *) makeBoolConst(false, false);
484 : : }
485 : 0 : }
486 : :
487 : 30605 : andlist = lappend(andlist, arg);
488 [ - - + ]: 30605 : }
489 : :
490 : : /* Flatten any ANDs introduced just below here */
491 : 12091 : andlist = pull_ands(andlist);
492 : :
493 : : /* AND of no inputs reduces to TRUE */
494 [ + - ]: 12091 : if (andlist == NIL)
495 : 0 : return (Expr *) makeBoolConst(true, false);
496 : :
497 : : /* Single-expression AND just reduces to that expression */
498 [ - + ]: 12091 : if (list_length(andlist) == 1)
499 : 0 : return (Expr *) linitial(andlist);
500 : :
501 : : /* Else we still need an AND node */
502 : 12091 : return make_andclause(andlist);
503 : 12091 : }
504 : : else
505 : 53180 : return qual;
506 : 66067 : }
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 : 795 : process_duplicate_ors(List *orlist)
518 : : {
519 : 795 : List *reference = NIL;
520 : 795 : int num_subclauses = 0;
521 : 795 : List *winners;
522 : 795 : List *neworlist;
523 : 795 : ListCell *temp;
524 : :
525 : : /* OR of no inputs reduces to FALSE */
526 [ + - ]: 795 : if (orlist == NIL)
527 : 0 : return (Expr *) makeBoolConst(false, false);
528 : :
529 : : /* Single-expression OR just reduces to that expression */
530 [ + + ]: 795 : if (list_length(orlist) == 1)
531 : 1 : 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 [ + - + + : 1646 : foreach(temp, orlist)
+ + ]
540 : : {
541 : 852 : Expr *clause = (Expr *) lfirst(temp);
542 : :
543 [ + + ]: 852 : if (is_andclause(clause))
544 : : {
545 : 92 : List *subclauses = ((BoolExpr *) clause)->args;
546 : 92 : int nclauses = list_length(subclauses);
547 : :
548 [ + + + + ]: 92 : if (reference == NIL || nclauses < num_subclauses)
549 : : {
550 : 50 : reference = subclauses;
551 : 50 : num_subclauses = nclauses;
552 : 50 : }
553 : 92 : }
554 : : else
555 : : {
556 : 760 : reference = list_make1(clause);
557 : 760 : break;
558 : : }
559 [ + + ]: 852 : }
560 : :
561 : : /*
562 : : * Just in case, eliminate any duplicates in the reference list.
563 : : */
564 : 794 : 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 : 794 : winners = NIL;
571 [ + - + + : 1624 : foreach(temp, reference)
+ + ]
572 : : {
573 : 830 : Expr *refclause = (Expr *) lfirst(temp);
574 : 830 : bool win = true;
575 : 830 : ListCell *temp2;
576 : :
577 [ + - - + : 2477 : foreach(temp2, orlist)
+ - ]
578 : : {
579 : 1647 : Expr *clause = (Expr *) lfirst(temp2);
580 : :
581 [ + + ]: 1647 : if (is_andclause(clause))
582 : : {
583 [ + + ]: 218 : if (!list_member(((BoolExpr *) clause)->args, refclause))
584 : : {
585 : 148 : win = false;
586 : 148 : break;
587 : : }
588 : 70 : }
589 : : else
590 : : {
591 [ + + ]: 1429 : if (!equal(refclause, clause))
592 : : {
593 : 682 : win = false;
594 : 682 : break;
595 : : }
596 : : }
597 [ + + ]: 1647 : }
598 : :
599 [ + - ]: 830 : if (win)
600 : 0 : winners = lappend(winners, refclause);
601 : 830 : }
602 : :
603 : : /*
604 : : * If no winners, we can't transform the OR
605 : : */
606 [ - + ]: 794 : if (winners == NIL)
607 : 794 : 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 : 0 : neworlist = NIL;
620 [ # # # # : 0 : foreach(temp, orlist)
# # ]
621 : : {
622 : 0 : Expr *clause = (Expr *) lfirst(temp);
623 : :
624 [ # # ]: 0 : if (is_andclause(clause))
625 : : {
626 : 0 : List *subclauses = ((BoolExpr *) clause)->args;
627 : :
628 : 0 : subclauses = list_difference(subclauses, winners);
629 [ # # ]: 0 : 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 : 0 : }
636 : : else
637 : : {
638 : 0 : neworlist = NIL; /* degenerate case, see above */
639 : 0 : break;
640 : : }
641 [ # # ]: 0 : }
642 : : else
643 : : {
644 [ # # ]: 0 : if (!list_member(winners, clause))
645 : 0 : neworlist = lappend(neworlist, clause);
646 : : else
647 : : {
648 : 0 : neworlist = NIL; /* degenerate case, see above */
649 : 0 : break;
650 : : }
651 : : }
652 [ # # ]: 0 : }
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 [ # # ]: 0 : 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 : 0 : }
667 : :
668 : : /*
669 : : * And return the constructed AND clause, again being wary of a single
670 : : * element and AND/OR flatness.
671 : : */
672 [ # # ]: 0 : if (list_length(winners) == 1)
673 : 0 : return (Expr *) linitial(winners);
674 : : else
675 : 0 : return make_andclause(pull_ands(winners));
676 : 795 : }
|