/src/postgres/src/backend/optimizer/prep/prepqual.c
Line | Count | Source (jump to first uncovered line) |
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-2025, 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 | | negate_clause(Node *node) |
74 | 0 | { |
75 | 0 | if (node == NULL) /* should not happen */ |
76 | 0 | elog(ERROR, "can't negate an empty subexpression"); |
77 | 0 | switch (nodeTag(node)) |
78 | 0 | { |
79 | 0 | case T_Const: |
80 | 0 | { |
81 | 0 | Const *c = (Const *) node; |
82 | | |
83 | | /* NOT NULL is still NULL */ |
84 | 0 | if (c->constisnull) |
85 | 0 | return makeBoolConst(false, true); |
86 | | /* otherwise pretty easy */ |
87 | 0 | return makeBoolConst(!DatumGetBool(c->constvalue), false); |
88 | 0 | } |
89 | 0 | break; |
90 | 0 | case T_OpExpr: |
91 | 0 | { |
92 | | /* |
93 | | * Negate operator if possible: (NOT (< A B)) => (>= A B) |
94 | | */ |
95 | 0 | OpExpr *opexpr = (OpExpr *) node; |
96 | 0 | Oid negator = get_negator(opexpr->opno); |
97 | |
|
98 | 0 | if (negator) |
99 | 0 | { |
100 | 0 | OpExpr *newopexpr = makeNode(OpExpr); |
101 | |
|
102 | 0 | newopexpr->opno = negator; |
103 | 0 | newopexpr->opfuncid = InvalidOid; |
104 | 0 | newopexpr->opresulttype = opexpr->opresulttype; |
105 | 0 | newopexpr->opretset = opexpr->opretset; |
106 | 0 | newopexpr->opcollid = opexpr->opcollid; |
107 | 0 | newopexpr->inputcollid = opexpr->inputcollid; |
108 | 0 | newopexpr->args = opexpr->args; |
109 | 0 | newopexpr->location = opexpr->location; |
110 | 0 | return (Node *) newopexpr; |
111 | 0 | } |
112 | 0 | } |
113 | 0 | break; |
114 | 0 | case T_ScalarArrayOpExpr: |
115 | 0 | { |
116 | | /* |
117 | | * Negate a ScalarArrayOpExpr if its operator has a negator; |
118 | | * for example x = ANY (list) becomes x <> ALL (list) |
119 | | */ |
120 | 0 | ScalarArrayOpExpr *saopexpr = (ScalarArrayOpExpr *) node; |
121 | 0 | Oid negator = get_negator(saopexpr->opno); |
122 | |
|
123 | 0 | if (negator) |
124 | 0 | { |
125 | 0 | ScalarArrayOpExpr *newopexpr = makeNode(ScalarArrayOpExpr); |
126 | |
|
127 | 0 | newopexpr->opno = negator; |
128 | 0 | newopexpr->opfuncid = InvalidOid; |
129 | 0 | newopexpr->hashfuncid = InvalidOid; |
130 | 0 | newopexpr->negfuncid = InvalidOid; |
131 | 0 | newopexpr->useOr = !saopexpr->useOr; |
132 | 0 | newopexpr->inputcollid = saopexpr->inputcollid; |
133 | 0 | newopexpr->args = saopexpr->args; |
134 | 0 | newopexpr->location = saopexpr->location; |
135 | 0 | return (Node *) newopexpr; |
136 | 0 | } |
137 | 0 | } |
138 | 0 | break; |
139 | 0 | case T_BoolExpr: |
140 | 0 | { |
141 | 0 | BoolExpr *expr = (BoolExpr *) node; |
142 | |
|
143 | 0 | switch (expr->boolop) |
144 | 0 | { |
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 | 0 | case AND_EXPR: |
161 | 0 | { |
162 | 0 | List *nargs = NIL; |
163 | 0 | ListCell *lc; |
164 | |
|
165 | 0 | foreach(lc, expr->args) |
166 | 0 | { |
167 | 0 | nargs = lappend(nargs, |
168 | 0 | negate_clause(lfirst(lc))); |
169 | 0 | } |
170 | 0 | return (Node *) make_orclause(nargs); |
171 | 0 | } |
172 | 0 | break; |
173 | 0 | case OR_EXPR: |
174 | 0 | { |
175 | 0 | List *nargs = NIL; |
176 | 0 | ListCell *lc; |
177 | |
|
178 | 0 | foreach(lc, expr->args) |
179 | 0 | { |
180 | 0 | nargs = lappend(nargs, |
181 | 0 | negate_clause(lfirst(lc))); |
182 | 0 | } |
183 | 0 | return (Node *) make_andclause(nargs); |
184 | 0 | } |
185 | 0 | break; |
186 | 0 | 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 | 0 | return (Node *) linitial(expr->args); |
193 | 0 | default: |
194 | 0 | elog(ERROR, "unrecognized boolop: %d", |
195 | 0 | (int) expr->boolop); |
196 | 0 | break; |
197 | 0 | } |
198 | 0 | } |
199 | 0 | break; |
200 | 0 | case T_NullTest: |
201 | 0 | { |
202 | 0 | 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 | 0 | if (!expr->argisrow) |
210 | 0 | { |
211 | 0 | NullTest *newexpr = makeNode(NullTest); |
212 | |
|
213 | 0 | newexpr->arg = expr->arg; |
214 | 0 | newexpr->nulltesttype = (expr->nulltesttype == IS_NULL ? |
215 | 0 | IS_NOT_NULL : IS_NULL); |
216 | 0 | newexpr->argisrow = expr->argisrow; |
217 | 0 | newexpr->location = expr->location; |
218 | 0 | return (Node *) newexpr; |
219 | 0 | } |
220 | 0 | } |
221 | 0 | break; |
222 | 0 | case T_BooleanTest: |
223 | 0 | { |
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 | 0 | { |
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 | 0 | (int) expr->booltesttype); |
251 | 0 | break; |
252 | 0 | } |
253 | 0 | newexpr->location = expr->location; |
254 | 0 | return (Node *) newexpr; |
255 | 0 | } |
256 | 0 | break; |
257 | 0 | default: |
258 | | /* else fall through */ |
259 | 0 | break; |
260 | 0 | } |
261 | | |
262 | | /* |
263 | | * Otherwise we don't know how to simplify this, so just tack on an |
264 | | * explicit NOT node. |
265 | | */ |
266 | 0 | return (Node *) make_notclause((Expr *) node); |
267 | 0 | } |
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 | | canonicalize_qual(Expr *qual, bool is_check) |
294 | 0 | { |
295 | 0 | Expr *newqual; |
296 | | |
297 | | /* Quick exit for empty qual */ |
298 | 0 | if (qual == NULL) |
299 | 0 | return NULL; |
300 | | |
301 | | /* This should not be invoked on quals in implicit-AND format */ |
302 | 0 | 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 | 0 | newqual = find_duplicate_ors(qual, is_check); |
310 | |
|
311 | 0 | return newqual; |
312 | 0 | } |
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 | | pull_ands(List *andlist) |
324 | 0 | { |
325 | 0 | List *out_list = NIL; |
326 | 0 | ListCell *arg; |
327 | |
|
328 | 0 | foreach(arg, andlist) |
329 | 0 | { |
330 | 0 | Node *subexpr = (Node *) lfirst(arg); |
331 | |
|
332 | 0 | if (is_andclause(subexpr)) |
333 | 0 | out_list = list_concat(out_list, |
334 | 0 | pull_ands(((BoolExpr *) subexpr)->args)); |
335 | 0 | else |
336 | 0 | out_list = lappend(out_list, subexpr); |
337 | 0 | } |
338 | 0 | return out_list; |
339 | 0 | } |
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 | | pull_ors(List *orlist) |
350 | 0 | { |
351 | 0 | List *out_list = NIL; |
352 | 0 | ListCell *arg; |
353 | |
|
354 | 0 | foreach(arg, orlist) |
355 | 0 | { |
356 | 0 | Node *subexpr = (Node *) lfirst(arg); |
357 | |
|
358 | 0 | if (is_orclause(subexpr)) |
359 | 0 | out_list = list_concat(out_list, |
360 | 0 | pull_ors(((BoolExpr *) subexpr)->args)); |
361 | 0 | else |
362 | 0 | out_list = lappend(out_list, subexpr); |
363 | 0 | } |
364 | 0 | return out_list; |
365 | 0 | } |
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 | | find_duplicate_ors(Expr *qual, bool is_check) |
407 | 0 | { |
408 | 0 | if (is_orclause(qual)) |
409 | 0 | { |
410 | 0 | List *orlist = NIL; |
411 | 0 | ListCell *temp; |
412 | | |
413 | | /* Recurse */ |
414 | 0 | foreach(temp, ((BoolExpr *) qual)->args) |
415 | 0 | { |
416 | 0 | Expr *arg = (Expr *) lfirst(temp); |
417 | |
|
418 | 0 | arg = find_duplicate_ors(arg, is_check); |
419 | | |
420 | | /* Get rid of any constant inputs */ |
421 | 0 | if (arg && IsA(arg, Const)) |
422 | 0 | { |
423 | 0 | Const *carg = (Const *) arg; |
424 | |
|
425 | 0 | if (is_check) |
426 | 0 | { |
427 | | /* Within OR in CHECK, drop constant FALSE */ |
428 | 0 | if (!carg->constisnull && !DatumGetBool(carg->constvalue)) |
429 | 0 | continue; |
430 | | /* Constant TRUE or NULL, so OR reduces to TRUE */ |
431 | 0 | return (Expr *) makeBoolConst(true, false); |
432 | 0 | } |
433 | 0 | else |
434 | 0 | { |
435 | | /* Within OR in WHERE, drop constant FALSE or NULL */ |
436 | 0 | if (carg->constisnull || !DatumGetBool(carg->constvalue)) |
437 | 0 | continue; |
438 | | /* Constant TRUE, so OR reduces to TRUE */ |
439 | 0 | return arg; |
440 | 0 | } |
441 | 0 | } |
442 | | |
443 | 0 | orlist = lappend(orlist, arg); |
444 | 0 | } |
445 | | |
446 | | /* Flatten any ORs pulled up to just below here */ |
447 | 0 | orlist = pull_ors(orlist); |
448 | | |
449 | | /* Now we can look for duplicate ORs */ |
450 | 0 | return process_duplicate_ors(orlist); |
451 | 0 | } |
452 | 0 | else if (is_andclause(qual)) |
453 | 0 | { |
454 | 0 | List *andlist = NIL; |
455 | 0 | ListCell *temp; |
456 | | |
457 | | /* Recurse */ |
458 | 0 | foreach(temp, ((BoolExpr *) qual)->args) |
459 | 0 | { |
460 | 0 | Expr *arg = (Expr *) lfirst(temp); |
461 | |
|
462 | 0 | arg = find_duplicate_ors(arg, is_check); |
463 | | |
464 | | /* Get rid of any constant inputs */ |
465 | 0 | if (arg && IsA(arg, Const)) |
466 | 0 | { |
467 | 0 | Const *carg = (Const *) arg; |
468 | |
|
469 | 0 | if (is_check) |
470 | 0 | { |
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 | 0 | } |
477 | 0 | else |
478 | 0 | { |
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 | 0 | } |
485 | 0 | } |
486 | | |
487 | 0 | andlist = lappend(andlist, arg); |
488 | 0 | } |
489 | | |
490 | | /* Flatten any ANDs introduced just below here */ |
491 | 0 | andlist = pull_ands(andlist); |
492 | | |
493 | | /* AND of no inputs reduces to TRUE */ |
494 | 0 | if (andlist == NIL) |
495 | 0 | return (Expr *) makeBoolConst(true, false); |
496 | | |
497 | | /* Single-expression AND just reduces to that expression */ |
498 | 0 | if (list_length(andlist) == 1) |
499 | 0 | return (Expr *) linitial(andlist); |
500 | | |
501 | | /* Else we still need an AND node */ |
502 | 0 | return make_andclause(andlist); |
503 | 0 | } |
504 | 0 | else |
505 | 0 | return qual; |
506 | 0 | } |
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 | | process_duplicate_ors(List *orlist) |
518 | 0 | { |
519 | 0 | List *reference = NIL; |
520 | 0 | int num_subclauses = 0; |
521 | 0 | List *winners; |
522 | 0 | List *neworlist; |
523 | 0 | ListCell *temp; |
524 | | |
525 | | /* OR of no inputs reduces to FALSE */ |
526 | 0 | if (orlist == NIL) |
527 | 0 | return (Expr *) makeBoolConst(false, false); |
528 | | |
529 | | /* Single-expression OR just reduces to that expression */ |
530 | 0 | if (list_length(orlist) == 1) |
531 | 0 | 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 | 0 | foreach(temp, orlist) |
540 | 0 | { |
541 | 0 | Expr *clause = (Expr *) lfirst(temp); |
542 | |
|
543 | 0 | if (is_andclause(clause)) |
544 | 0 | { |
545 | 0 | List *subclauses = ((BoolExpr *) clause)->args; |
546 | 0 | int nclauses = list_length(subclauses); |
547 | |
|
548 | 0 | if (reference == NIL || nclauses < num_subclauses) |
549 | 0 | { |
550 | 0 | reference = subclauses; |
551 | 0 | num_subclauses = nclauses; |
552 | 0 | } |
553 | 0 | } |
554 | 0 | else |
555 | 0 | { |
556 | 0 | reference = list_make1(clause); |
557 | 0 | break; |
558 | 0 | } |
559 | 0 | } |
560 | | |
561 | | /* |
562 | | * Just in case, eliminate any duplicates in the reference list. |
563 | | */ |
564 | 0 | 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 | 0 | winners = NIL; |
571 | 0 | foreach(temp, reference) |
572 | 0 | { |
573 | 0 | Expr *refclause = (Expr *) lfirst(temp); |
574 | 0 | bool win = true; |
575 | 0 | ListCell *temp2; |
576 | |
|
577 | 0 | foreach(temp2, orlist) |
578 | 0 | { |
579 | 0 | Expr *clause = (Expr *) lfirst(temp2); |
580 | |
|
581 | 0 | if (is_andclause(clause)) |
582 | 0 | { |
583 | 0 | if (!list_member(((BoolExpr *) clause)->args, refclause)) |
584 | 0 | { |
585 | 0 | win = false; |
586 | 0 | break; |
587 | 0 | } |
588 | 0 | } |
589 | 0 | else |
590 | 0 | { |
591 | 0 | if (!equal(refclause, clause)) |
592 | 0 | { |
593 | 0 | win = false; |
594 | 0 | break; |
595 | 0 | } |
596 | 0 | } |
597 | 0 | } |
598 | |
|
599 | 0 | if (win) |
600 | 0 | winners = lappend(winners, refclause); |
601 | 0 | } |
602 | | |
603 | | /* |
604 | | * If no winners, we can't transform the OR |
605 | | */ |
606 | 0 | if (winners == NIL) |
607 | 0 | 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 | 0 | { |
622 | 0 | Expr *clause = (Expr *) lfirst(temp); |
623 | |
|
624 | 0 | if (is_andclause(clause)) |
625 | 0 | { |
626 | 0 | List *subclauses = ((BoolExpr *) clause)->args; |
627 | |
|
628 | 0 | subclauses = list_difference(subclauses, winners); |
629 | 0 | if (subclauses != NIL) |
630 | 0 | { |
631 | 0 | if (list_length(subclauses) == 1) |
632 | 0 | neworlist = lappend(neworlist, linitial(subclauses)); |
633 | 0 | else |
634 | 0 | neworlist = lappend(neworlist, make_andclause(subclauses)); |
635 | 0 | } |
636 | 0 | else |
637 | 0 | { |
638 | 0 | neworlist = NIL; /* degenerate case, see above */ |
639 | 0 | break; |
640 | 0 | } |
641 | 0 | } |
642 | 0 | else |
643 | 0 | { |
644 | 0 | if (!list_member(winners, clause)) |
645 | 0 | neworlist = lappend(neworlist, clause); |
646 | 0 | else |
647 | 0 | { |
648 | 0 | neworlist = NIL; /* degenerate case, see above */ |
649 | 0 | break; |
650 | 0 | } |
651 | 0 | } |
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 | 0 | { |
662 | 0 | if (list_length(neworlist) == 1) |
663 | 0 | winners = lappend(winners, linitial(neworlist)); |
664 | 0 | 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 | 0 | else |
675 | 0 | return make_andclause(pull_ands(winners)); |
676 | 0 | } |