diff options
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/plan/initsplan.c | 46 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planmain.c | 160 | ||||
-rw-r--r-- | src/backend/optimizer/plan/planner.c | 53 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 39 |
4 files changed, 157 insertions, 141 deletions
diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 20a01902efc..ef219245aab 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.39 1999/08/26 05:07:41 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/initsplan.c,v 1.40 1999/10/07 04:23:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -77,15 +77,21 @@ add_vars_to_targetlist(Query *root, List *vars) } /* - * add_missing_vars_to_tlist - * If we have range variable(s) in the FROM clause that does not appear - * in the target list nor qualifications, we add it to the base relation - * list. For instance, "select f.x from foo f, foo f2" is a join of f and - * f2. Note that if we have "select foo.x from foo f", it also gets turned - * into a join. + * add_missing_rels_to_query + * + * If we have a range variable in the FROM clause that does not appear + * in the target list nor qualifications, we must add it to the base + * relation list so that it will be joined. For instance, "select f.x + * from foo f, foo f2" is a join of f and f2. Note that if we have + * "select foo.x from foo f", it also gets turned into a join (between + * foo as foo and foo as f). + * + * To avoid putting useless entries into the per-relation targetlists, + * this should only be called after all the variables in the targetlist + * and quals have been processed by the routines above. */ void -add_missing_vars_to_tlist(Query *root, List *tlist) +add_missing_rels_to_query(Query *root) { int varno = 1; List *l; @@ -93,21 +99,21 @@ add_missing_vars_to_tlist(Query *root, List *tlist) foreach(l, root->rtable) { RangeTblEntry *rte = (RangeTblEntry *) lfirst(l); - Relids relids; - relids = lconsi(varno, NIL); - if (rte->inFromCl && !rel_member(relids, root->base_rel_list)) + if (rte->inJoinSet) { - RelOptInfo *rel; - Var *var; - - /* add it to base_rel_list */ - rel = get_base_rel(root, varno); - /* give it a dummy tlist entry for its OID */ - var = makeVar(varno, ObjectIdAttributeNumber, OIDOID, -1, 0); - add_var_to_tlist(rel, var); + RelOptInfo *rel = get_base_rel(root, varno); + + /* If the rel isn't otherwise referenced, give it a dummy + * targetlist consisting of its own OID. + */ + if (rel->targetlist == NIL) + { + Var *var = makeVar(varno, ObjectIdAttributeNumber, + OIDOID, -1, 0); + add_var_to_tlist(rel, var); + } } - pfree(relids); varno++; } } diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 3cc3f0a6940..3b289fb3d0e 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.45 1999/09/26 02:28:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planmain.c,v 1.46 1999/10/07 04:23:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -28,6 +28,7 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual); + /* * query_planner * Routine to create a query plan. It does so by first creating a @@ -39,9 +40,8 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual); * be placed where and any relation level qualifications to be * satisfied. * - * command-type is the query command, e.g., select, delete, etc. - * tlist is the target list of the query - * qual is the qualification of the query + * tlist is the target list of the query (do NOT use root->targetList!) + * qual is the qualification of the query (likewise!) * * Note: the Query node now also includes a query_pathkeys field, which * is both an input and an output of query_planner(). The input value @@ -57,25 +57,20 @@ static Plan *subplanner(Query *root, List *flat_tlist, List *qual); */ Plan * query_planner(Query *root, - int command_type, List *tlist, List *qual) { List *constant_qual = NIL; List *var_only_tlist; - List *level_tlist; Plan *subplan; /* - * Simplify constant expressions in both targetlist and qual. + * Note: union_planner should already have done constant folding + * in both the tlist and qual, so we don't do it again here + * (indeed, we may be getting a flattened var-only tlist anyway). * - * Note that at this point the qual has not yet been converted to - * implicit-AND form, so we can apply eval_const_expressions directly. - * Also note that we need to do this before SS_process_sublinks, - * because that routine inserts bogus "Const" nodes. + * Is there any value in re-folding the qual after canonicalize_qual? */ - tlist = (List *) eval_const_expressions((Node *) tlist); - qual = (List *) eval_const_expressions((Node *) qual); /* * Canonicalize the qual, and convert it to implicit-AND format. @@ -97,97 +92,75 @@ query_planner(Query *root, qual = (List *) SS_process_sublinks((Node *) qual); /* - * Pull out any non-variable qualifications so these can be put in the - * topmost result node. (Any *really* non-variable quals will probably + * If the query contains no relation references at all, it must be + * something like "SELECT 2+2;". Build a trivial "Result" plan. + */ + if (root->rtable == NIL) + { + /* If it's not a select, it should have had a target relation... */ + if (root->commandType != CMD_SELECT) + elog(ERROR, "Empty range table for non-SELECT query"); + + root->query_pathkeys = NIL; /* signal unordered result */ + + /* Make childless Result node to evaluate given tlist. */ + return (Plan *) make_result(tlist, (Node *) qual, (Plan *) NULL); + } + + /* + * Pull out any non-variable qual clauses so these can be put in a + * toplevel "Result" node, where they will gate execution of the whole + * plan (the Result will not invoke its descendant plan unless the + * quals are true). Note that any *really* non-variable quals will * have been optimized away by eval_const_expressions(). What we're - * looking for here is quals that depend only on outer-level vars...) + * mostly interested in here is quals that depend only on outer-level + * vars, although if the qual reduces to "WHERE FALSE" this path will + * also be taken. */ qual = pull_constant_clauses(qual, &constant_qual); /* * Create a target list that consists solely of (resdom var) target * list entries, i.e., contains no arbitrary expressions. + * + * All subplan nodes will have "flat" (var-only) tlists. + * + * This implies that all expression evaluations are done at the root + * of the plan tree. Once upon a time there was code to try to push + * expensive function calls down to lower plan nodes, but that's dead + * code and has been for a long time... */ var_only_tlist = flatten_tlist(tlist); - if (var_only_tlist) - level_tlist = var_only_tlist; - else - /* from old code. the logic is beyond me. - ay 2/95 */ - level_tlist = tlist; - - /* - * A query may have a non-variable target list and a non-variable - * qualification only under certain conditions: - the query creates - * all-new tuples, or - the query is a replace (a scan must still be - * done in this case). - */ - if (var_only_tlist == NULL && qual == NULL) - { - root->query_pathkeys = NIL; /* these plans make unordered results */ - - switch (command_type) - { - case CMD_SELECT: - case CMD_INSERT: - return ((Plan *) make_result(tlist, - (Node *) constant_qual, - (Plan *) NULL)); - break; - case CMD_DELETE: - case CMD_UPDATE: - { - SeqScan *scan = make_seqscan(tlist, - NIL, - root->resultRelation); - - if (constant_qual != NULL) - return ((Plan *) make_result(tlist, - (Node *) constant_qual, - (Plan *) scan)); - else - return (Plan *) scan; - } - break; - default: - return (Plan *) NULL; - } - } /* * Choose the best access path and build a plan for it. */ - subplan = subplanner(root, level_tlist, qual); + subplan = subplanner(root, var_only_tlist, qual); /* - * Build a result node linking the plan if we have constant quals + * Build a result node to control the plan if we have constant quals. */ if (constant_qual) { + /* + * The result node will also be responsible for evaluating + * the originally requested tlist. + */ subplan = (Plan *) make_result(tlist, (Node *) constant_qual, subplan); - - root->query_pathkeys = NIL; /* result is unordered, no? */ - - return subplan; } - - /* - * Replace the toplevel plan node's flattened target list with the - * targetlist given by my caller, so that expressions are evaluated. - * - * This implies that all expression evaluations are done at the root - * of the plan tree. Once upon a time there was code to try to push - * expensive function calls down to lower plan nodes, but that's dead - * code and has been for a long time... - */ else { + /* + * Replace the toplevel plan node's flattened target list with the + * targetlist given by my caller, so that expressions are evaluated. + */ subplan->targetlist = tlist; - - return subplan; } + return subplan; + #ifdef NOT_USED /* @@ -230,12 +203,31 @@ subplanner(Query *root, make_var_only_tlist(root, flat_tlist); add_restrict_and_join_to_rels(root, qual); - add_missing_vars_to_tlist(root, flat_tlist); + add_missing_rels_to_query(root); set_joininfo_mergeable_hashable(root->base_rel_list); final_rel = make_one_rel(root, root->base_rel_list); + if (! final_rel) + { + /* + * We expect to end up here for a trivial INSERT ... VALUES query + * (which will have a target relation, so it gets past query_planner's + * check for empty range table; but the target rel is unreferenced + * and not marked inJoinSet, so we find there is nothing to join). + * + * It's also possible to get here if the query was rewritten by the + * rule processor (creating rangetable entries not marked inJoinSet) + * but the rules either did nothing or were simplified to nothing + * by constant-expression folding. So, don't complain. + */ + root->query_pathkeys = NIL; /* signal unordered result */ + + /* Make childless Result node to evaluate given tlist. */ + return (Plan *) make_result(flat_tlist, (Node *) qual, (Plan *) NULL); + } + #ifdef NOT_USED /* fix xfunc */ /* @@ -259,13 +251,6 @@ subplanner(Query *root, } #endif - if (! final_rel) - { - elog(NOTICE, "final relation is null"); - root->query_pathkeys = NIL; /* result is unordered, no? */ - return create_plan((Path *) NULL); - } - /* * Determine the cheapest path and create a subplan to execute it. * @@ -344,10 +329,11 @@ subplanner(Query *root, } } - /* Nothing for it but to sort the cheapestpath --- but we let the + /* + * Nothing for it but to sort the cheapestpath --- but we let the * caller do that. union_planner has to be able to add a sort node * anyway, so no need for extra code here. (Furthermore, the given - * pathkeys might involve something we can't compute yet, such as + * pathkeys might involve something we can't compute here, such as * an aggregate function...) */ root->query_pathkeys = final_rel->cheapestpath->pathkeys; diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index d78a650c05d..fce3800dc49 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.69 1999/09/26 02:28:27 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.70 1999/10/07 04:23:06 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -94,6 +94,37 @@ union_planner(Query *parse) List *current_pathkeys = NIL; Index rt_index; + /* + * A HAVING clause without aggregates is equivalent to a WHERE clause + * (except it can only refer to grouped fields). If there are no + * aggs anywhere in the query, then we don't want to create an Agg + * plan node, so merge the HAVING condition into WHERE. (We used to + * consider this an error condition, but it seems to be legal SQL.) + */ + if (parse->havingQual != NULL && ! parse->hasAggs) + { + if (parse->qual == NULL) + parse->qual = parse->havingQual; + else + parse->qual = (Node *) make_andclause(lappend(lcons(parse->qual, + NIL), + parse->havingQual)); + parse->havingQual = NULL; + } + + /* + * Simplify constant expressions in targetlist and quals. + * + * Note that at this point the qual has not yet been converted to + * implicit-AND form, so we can apply eval_const_expressions directly. + * Also note that we need to do this before SS_process_sublinks, + * because that routine inserts bogus "Const" nodes. + */ + tlist = (List *) eval_const_expressions((Node *) tlist); + parse->qual = eval_const_expressions(parse->qual); + parse->havingQual = eval_const_expressions(parse->havingQual); + + if (parse->unionClause) { result_plan = (Plan *) plan_union_queries(parse); @@ -221,7 +252,6 @@ union_planner(Query *parse) /* Generate the (sub) plan */ result_plan = query_planner(parse, - parse->commandType, sub_tlist, (List *) parse->qual); @@ -301,25 +331,6 @@ union_planner(Query *parse) */ if (parse->havingQual) { - /*-------------------- - * Require the havingQual to contain at least one aggregate function - * (else it could have been done as a WHERE constraint). This check - * used to be much stricter, requiring an aggregate in each clause of - * the CNF-ified qual. However, that's probably overly anal-retentive. - * We now do it first so that we will not complain if there is an - * aggregate but it gets optimized away by eval_const_expressions(). - * The agg itself is never const, of course, but consider - * SELECT ... HAVING xyz OR (COUNT(*) > 1) - * where xyz reduces to constant true in a particular query. - * We probably should not refuse this query. - *-------------------- - */ - if (pull_agg_clause(parse->havingQual) == NIL) - elog(ERROR, "SELECT/HAVING requires aggregates to be valid"); - - /* Simplify constant expressions in havingQual */ - parse->havingQual = eval_const_expressions(parse->havingQual); - /* Convert the havingQual to implicit-AND normal form */ parse->havingQual = (Node *) canonicalize_qual((Expr *) parse->havingQual, true); diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 1fc91f0cae8..066b3918264 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.53 1999/10/02 04:37:52 tgl Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/clauses.c,v 1.54 1999/10/07 04:23:08 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -34,6 +34,11 @@ #include "utils/syscache.h" +/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */ +#define MAKEBOOLCONST(val,isnull) \ + ((Node *) makeConst(BOOLOID, 1, (Datum) (val), \ + (isnull), true, false, false)) + typedef struct { List *groupClause; List *targetList; @@ -312,17 +317,20 @@ make_andclause(List *andclauses) } /* - * Sometimes (such as in the result of cnfify), we use lists of expression - * nodes with implicit AND semantics. These functions convert between an - * AND-semantics expression list and the ordinary representation of a - * boolean expression. + * Sometimes (such as in the result of canonicalize_qual or the input of + * ExecQual), we use lists of expression nodes with implicit AND semantics. + * + * These functions convert between an AND-semantics expression list and the + * ordinary representation of a boolean expression. + * + * Note that an empty list is considered equivalent to TRUE. */ Expr * make_ands_explicit(List *andclauses) { if (andclauses == NIL) - return NULL; - else if (length(andclauses) == 1) + return (Expr *) MAKEBOOLCONST(true, false); + else if (lnext(andclauses) == NIL) return (Expr *) lfirst(andclauses); else return make_andclause(andclauses); @@ -331,10 +339,20 @@ make_ands_explicit(List *andclauses) List * make_ands_implicit(Expr *clause) { + /* + * NB: because the parser sets the qual field to NULL in a query that + * has no WHERE clause, we must consider a NULL input clause as TRUE, + * even though one might more reasonably think it FALSE. Grumble. + * If this causes trouble, consider changing the parser's behavior. + */ if (clause == NULL) - return NIL; + return NIL; /* NULL -> NIL list == TRUE */ else if (and_clause((Node *) clause)) return clause->args; + else if (IsA(clause, Const) && + ! ((Const *) clause)->constisnull && + DatumGetInt32(((Const *) clause)->constvalue)) + return NIL; /* constant TRUE input -> NIL list */ else return lcons(clause, NIL); } @@ -808,11 +826,6 @@ eval_const_expressions(Node *node) return eval_const_expressions_mutator(node, NULL); } -/* note that pg_type.h hardwires size of bool as 1 ... duplicate it */ -#define MAKEBOOLCONST(val,isnull) \ - ((Node *) makeConst(BOOLOID, 1, (Datum) (val), \ - (isnull), true, false, false)) - static Node * eval_const_expressions_mutator (Node *node, void *context) { |