prepqual.c
来自「PostgreSQL7.4.6 for Linux」· C语言 代码 · 共 979 行 · 第 1/2 页
C
979 行
/*------------------------------------------------------------------------- * * prepqual.c * Routines for preprocessing qualification expressions * * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepqual.c,v 1.38 2003/08/08 21:41:52 momjian Exp $ * *------------------------------------------------------------------------- */#include "postgres.h"#include "nodes/makefuncs.h"#include "optimizer/clauses.h"#include "optimizer/prep.h"#include "utils/lsyscache.h"static Expr *flatten_andors(Expr *qual);static void flatten_andors_and_walker(FastList *out_list, List *andlist);static void flatten_andors_or_walker(FastList *out_list, List *orlist);static List *pull_ands(List *andlist);static void pull_ands_walker(FastList *out_list, List *andlist);static List *pull_ors(List *orlist);static void pull_ors_walker(FastList *out_list, List *orlist);static Expr *find_nots(Expr *qual);static Expr *push_nots(Expr *qual);static Expr *find_ors(Expr *qual);static Expr *or_normalize(List *orlist);static Expr *find_ands(Expr *qual);static Expr *and_normalize(List *andlist);static Expr *qual_cleanup(Expr *qual);static List *remove_duplicates(List *list);static void count_bool_nodes(Expr *qual, double *nodes, double *cnfnodes, double *dnfnodes);/***************************************************************************** * * CNF/DNF CONVERSION ROUTINES * * These routines convert an arbitrary boolean expression into * conjunctive normal form or disjunctive normal form. * * Normalization is only carried out in the top AND/OR/NOT portion * of the given tree; we do not attempt to normalize boolean expressions * that may appear as arguments of operators or functions in the tree. * * Query qualifications (WHERE clauses) are ordinarily transformed into * CNF, ie, AND-of-ORs form, because then the optimizer can use any one * of the independent AND clauses as a filtering qualification. However, * quals that are naturally expressed as OR-of-ANDs can suffer an * exponential growth in size in this transformation, so we also consider * converting to DNF (OR-of-ANDs), and we may also leave well enough alone * if both transforms cause unreasonable growth. The OR-of-ANDs format * is useful for indexscan implementation, so we prefer that format when * there is just one relation involved. * * canonicalize_qual() does "smart" conversion to either CNF or DNF, per * the above considerations, while cnfify() and dnfify() simply perform * the demanded transformation. The latter two may become dead code * eventually. *****************************************************************************//* * canonicalize_qual * Convert a qualification to the most useful normalized form. * * Returns the modified qualification. * * If 'removeAndFlag' is true then it removes explicit AND at the top level, * producing a list of implicitly-ANDed conditions. Otherwise, a regular * boolean expression is returned. Since most callers pass 'true', we * prefer to declare the result as List *, not Expr *. * * XXX This code could be much smarter, at the cost of also being slower, * if we tried to compute selectivities and/or see whether there are * actually indexes to support an indexscan implementation of a DNF qual. * We could even try converting the CNF clauses that mention a single * relation into a single DNF clause to see if that looks cheaper to * implement. For now, though, we just try to avoid doing anything * quite as stupid as unconditionally converting to CNF was... */List *canonicalize_qual(Expr *qual, bool removeAndFlag){ Expr *newqual; double nodes, cnfnodes, dnfnodes; bool cnfok, dnfok; if (qual == NULL) return NIL; /* * Flatten AND and OR groups throughout the tree. This improvement is * always worthwhile, so do it unconditionally. */ qual = flatten_andors(qual); /* * Push down NOTs. We do this only in the top-level boolean * expression, without examining arguments of operators/functions. * Even so, it might not be a win if we are unable to find negators * for all the operators involved; perhaps we should compare before- * and-after tree sizes? */ newqual = find_nots(qual); /* * Choose whether to convert to CNF, or DNF, or leave well enough * alone. * * We make an approximate estimate of the number of bottom-level nodes * that will appear in the CNF and DNF forms of the query. */ count_bool_nodes(newqual, &nodes, &cnfnodes, &dnfnodes); /* * First heuristic is to forget about *both* normal forms if there are * a huge number of terms in the qual clause. This would only happen * with machine-generated queries, presumably; and most likely such a * query is already in either CNF or DNF. */ cnfok = dnfok = true; if (nodes >= 500.0) cnfok = dnfok = false; /* * Second heuristic is to forget about either CNF or DNF if it shows * unreasonable growth compared to the original form of the qual, * where we define "unreasonable" a tad arbitrarily as 4x more * operators. */ if (cnfnodes >= 4.0 * nodes) cnfok = false; if (dnfnodes >= 4.0 * nodes) dnfok = false; /* * Third heuristic is to prefer DNF if top level is already an OR, and * only one relation is mentioned, and DNF is no larger than the CNF * representation. (Pretty shaky; can we improve on this?) */ if (cnfok && dnfok && dnfnodes <= cnfnodes && or_clause((Node *) newqual) && NumRelids((Node *) newqual) == 1) cnfok = false; /* * Otherwise, we prefer CNF. * * XXX obviously, these rules could be improved upon. */ if (cnfok) { /* * Normalize into conjunctive normal form, and clean up the * result. */ newqual = qual_cleanup(find_ors(newqual)); } else if (dnfok) { /* * Normalize into disjunctive normal form, and clean up the * result. */ newqual = qual_cleanup(find_ands(newqual)); } /* Convert to implicit-AND list if requested */ if (removeAndFlag) newqual = (Expr *) make_ands_implicit(newqual); return (List *) newqual;}/* * cnfify * Convert a qualification to conjunctive normal form by applying * successive normalizations. * * Returns the modified qualification. * * If 'removeAndFlag' is true then it removes explicit AND at the top level, * producing a list of implicitly-ANDed conditions. Otherwise, a regular * boolean expression is returned. Since most callers pass 'true', we * prefer to declare the result as List *, not Expr *. */List *cnfify(Expr *qual, bool removeAndFlag){ Expr *newqual; if (qual == NULL) return NIL; /* * Flatten AND and OR groups throughout the tree. This improvement is * always worthwhile. */ newqual = flatten_andors(qual); /* * Push down NOTs. We do this only in the top-level boolean * expression, without examining arguments of operators/functions. */ newqual = find_nots(newqual); /* Normalize into conjunctive normal form. */ newqual = find_ors(newqual); /* Clean up the result. */ newqual = qual_cleanup(newqual); if (removeAndFlag) newqual = (Expr *) make_ands_implicit(newqual); return (List *) newqual;}#ifdef NOT_USED/* * dnfify * Convert a qualification to disjunctive normal form by applying * successive normalizations. * * Returns the modified qualification. * * We do not offer a 'removeOrFlag' in this case; the usages are * different. */static Expr *dnfify(Expr *qual){ Expr *newqual; if (qual == NULL) return NULL; /* * Flatten AND and OR groups throughout the tree. This improvement is * always worthwhile. */ newqual = flatten_andors(qual); /* * Push down NOTs. We do this only in the top-level boolean * expression, without examining arguments of operators/functions. */ newqual = find_nots(newqual); /* Normalize into disjunctive normal form. */ newqual = find_ands(newqual); /* Clean up the result. */ newqual = qual_cleanup(newqual); return newqual;}#endif/*-------------------- * The parser regards AND and OR as purely binary operators, so a qual like * (A = 1) OR (A = 2) OR (A = 3) ... * will produce a nested parsetree * (OR (A = 1) (OR (A = 2) (OR (A = 3) ...))) * In reality, the optimizer and executor regard AND and OR as n-argument * operators, so this tree can be flattened to * (OR (A = 1) (A = 2) (A = 3) ...) * which is the responsibility of the routines below. * * flatten_andors() does the basic transformation with no initial assumptions. * pull_ands() and pull_ors() are used to maintain flatness of the AND/OR * tree after local transformations that might introduce nested AND/ORs. *-------------------- *//*-------------------- * flatten_andors * Given a qualification, simplify nested AND/OR clauses into flat * AND/OR clauses with more arguments. * * Returns the rebuilt expr (note original list structure is not touched). *-------------------- */static Expr *flatten_andors(Expr *qual){ if (qual == NULL) return NULL; if (and_clause((Node *) qual)) { FastList out_list; FastListInit(&out_list); flatten_andors_and_walker(&out_list, ((BoolExpr *) qual)->args); return make_andclause(FastListValue(&out_list)); } else if (or_clause((Node *) qual)) { FastList out_list; FastListInit(&out_list); flatten_andors_or_walker(&out_list, ((BoolExpr *) qual)->args); return make_orclause(FastListValue(&out_list)); } else if (not_clause((Node *) qual)) return make_notclause(flatten_andors(get_notclausearg(qual))); else if (is_opclause(qual)) { OpExpr *opexpr = (OpExpr *) qual; Expr *left = (Expr *) get_leftop(qual); Expr *right = (Expr *) get_rightop(qual); return make_opclause(opexpr->opno, opexpr->opresulttype, opexpr->opretset, flatten_andors(left), flatten_andors(right)); } else return qual;}static voidflatten_andors_and_walker(FastList *out_list, List *andlist){ List *arg; foreach(arg, andlist) { Expr *subexpr = (Expr *) lfirst(arg); if (and_clause((Node *) subexpr)) flatten_andors_and_walker(out_list, ((BoolExpr *) subexpr)->args); else FastAppend(out_list, flatten_andors(subexpr)); }}static voidflatten_andors_or_walker(FastList *out_list, List *orlist){ List *arg; foreach(arg, orlist) { Expr *subexpr = (Expr *) lfirst(arg); if (or_clause((Node *) subexpr)) flatten_andors_or_walker(out_list, ((BoolExpr *) subexpr)->args); else FastAppend(out_list, flatten_andors(subexpr)); }}/* * pull_ands * Recursively flatten nested AND clauses into a single and-clause list. * * Input is the arglist of an AND clause. * Returns the rebuilt arglist (note original list structure is not touched). */static List *pull_ands(List *andlist){ FastList out_list; FastListInit(&out_list); pull_ands_walker(&out_list, andlist); return FastListValue(&out_list);}static voidpull_ands_walker(FastList *out_list, List *andlist){ List *arg; foreach(arg, andlist) { Expr *subexpr = (Expr *) lfirst(arg); if (and_clause((Node *) subexpr)) pull_ands_walker(out_list, ((BoolExpr *) subexpr)->args); else FastAppend(out_list, subexpr); }}/* * pull_ors * Recursively flatten nested OR clauses into a single or-clause list. * * Input is the arglist of an OR clause. * Returns the rebuilt arglist (note original list structure is not touched). */static List *pull_ors(List *orlist){ FastList out_list; FastListInit(&out_list); pull_ors_walker(&out_list, orlist); return FastListValue(&out_list);}static voidpull_ors_walker(FastList *out_list, List *orlist){ List *arg; foreach(arg, orlist) { Expr *subexpr = (Expr *) lfirst(arg); if (or_clause((Node *) subexpr)) pull_ors_walker(out_list, ((BoolExpr *) subexpr)->args); else FastAppend(out_list, subexpr); }}/* * find_nots * Traverse the qualification, looking for 'NOT's to take care of. * For 'NOT' clauses, apply push_not() to try to push down the 'NOT'. * For all other clause types, simply recurse. * * Returns the modified qualification. AND/OR flatness is preserved. */static Expr *find_nots(Expr *qual){ if (qual == NULL) return NULL;#ifdef NOT_USED /* recursing into operator expressions is probably not worth it. */ if (is_opclause(qual)) { OpExpr *opexpr = (OpExpr *) qual; Expr *left = (Expr *) get_leftop(qual); Expr *right = (Expr *) get_rightop(qual); return make_opclause(opexpr->opno, opexpr->opresulttype, opexpr->opretset, find_nots(left), find_nots(right)); }#endif if (and_clause((Node *) qual)) { FastList t_list; List *temp; FastListInit(&t_list); foreach(temp, ((BoolExpr *) qual)->args) FastAppend(&t_list, find_nots(lfirst(temp))); return make_andclause(pull_ands(FastListValue(&t_list))); } else if (or_clause((Node *) qual)) { FastList t_list; List *temp; FastListInit(&t_list); foreach(temp, ((BoolExpr *) qual)->args) FastAppend(&t_list, find_nots(lfirst(temp))); return make_orclause(pull_ors(FastListValue(&t_list))); } else if (not_clause((Node *) qual)) return push_nots(get_notclausearg(qual)); else return qual;}/* * push_nots * Push down a 'NOT' as far as possible. * * Input is an expression to be negated (e.g., the argument of a NOT clause). * Returns a new qual equivalent to the negation of the given qual. */static Expr *
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?