diff options
author | Tom Lane <tgl@sss.pgh.pa.us> | 2004-05-10 22:44:49 +0000 |
---|---|---|
committer | Tom Lane <tgl@sss.pgh.pa.us> | 2004-05-10 22:44:49 +0000 |
commit | 2f63232d30ca64a8f2684af855230f23a701d371 (patch) | |
tree | b7a7707d1ec9edf368780cd3f4a23755527c5884 /src/backend/optimizer | |
parent | 9a939886ac782cfee3cd5fdd1c58689163ed84be (diff) | |
download | postgresql-2f63232d30ca64a8f2684af855230f23a701d371.tar.gz postgresql-2f63232d30ca64a8f2684af855230f23a701d371.zip |
Promote row expressions to full-fledged citizens of the expression syntax,
rather than allowing them only in a few special cases as before. In
particular you can now pass a ROW() construct to a function that accepts
a rowtype parameter. Internal generation of RowExprs fixes a number of
corner cases that used to not work very well, such as referencing the
whole-row result of a JOIN or subquery. This represents a further step in
the work I started a month or so back to make rowtype values into
first-class citizens.
Diffstat (limited to 'src/backend/optimizer')
-rw-r--r-- | src/backend/optimizer/path/allpaths.c | 27 | ||||
-rw-r--r-- | src/backend/optimizer/path/clausesel.c | 9 | ||||
-rw-r--r-- | src/backend/optimizer/prep/prepjointree.c | 50 | ||||
-rw-r--r-- | src/backend/optimizer/util/clauses.c | 109 | ||||
-rw-r--r-- | src/backend/optimizer/util/var.c | 53 |
5 files changed, 135 insertions, 113 deletions
diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index 5d92fc4c5d1..217f06a6b2b 100644 --- a/src/backend/optimizer/path/allpaths.c +++ b/src/backend/optimizer/path/allpaths.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.113 2004/04/25 18:23:56 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.114 2004/05/10 22:44:44 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -57,9 +57,10 @@ static void compare_tlist_datatypes(List *tlist, List *colTypes, bool *differentTypes); static bool qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, bool *differentTypes); -static void subquery_push_qual(Query *subquery, Index rti, Node *qual); +static void subquery_push_qual(Query *subquery, + RangeTblEntry *rte, Index rti, Node *qual); static void recurse_push_qual(Node *setOp, Query *topquery, - Index rti, Node *qual); + RangeTblEntry *rte, Index rti, Node *qual); /* @@ -375,7 +376,7 @@ set_subquery_pathlist(Query *root, RelOptInfo *rel, if (qual_is_pushdown_safe(subquery, rti, clause, differentTypes)) { /* Push it down */ - subquery_push_qual(subquery, rti, clause); + subquery_push_qual(subquery, rte, rti, clause); } else { @@ -778,12 +779,12 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, * subquery_push_qual - push down a qual that we have determined is safe */ static void -subquery_push_qual(Query *subquery, Index rti, Node *qual) +subquery_push_qual(Query *subquery, RangeTblEntry *rte, Index rti, Node *qual) { if (subquery->setOperations != NULL) { /* Recurse to push it separately to each component query */ - recurse_push_qual(subquery->setOperations, subquery, rti, qual); + recurse_push_qual(subquery->setOperations, subquery, rte, rti, qual); } else { @@ -797,7 +798,7 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual) * This step also ensures that when we are pushing into a setop tree, * each component query gets its own copy of the qual. */ - qual = ResolveNew(qual, rti, 0, + qual = ResolveNew(qual, rti, 0, rte, subquery->targetList, CMD_SELECT, 0); subquery->havingQual = make_and_qual(subquery->havingQual, @@ -816,23 +817,23 @@ subquery_push_qual(Query *subquery, Index rti, Node *qual) */ static void recurse_push_qual(Node *setOp, Query *topquery, - Index rti, Node *qual) + RangeTblEntry *rte, Index rti, Node *qual) { if (IsA(setOp, RangeTblRef)) { RangeTblRef *rtr = (RangeTblRef *) setOp; - RangeTblEntry *rte = rt_fetch(rtr->rtindex, topquery->rtable); - Query *subquery = rte->subquery; + RangeTblEntry *subrte = rt_fetch(rtr->rtindex, topquery->rtable); + Query *subquery = subrte->subquery; Assert(subquery != NULL); - subquery_push_qual(subquery, rti, qual); + subquery_push_qual(subquery, rte, rti, qual); } else if (IsA(setOp, SetOperationStmt)) { SetOperationStmt *op = (SetOperationStmt *) setOp; - recurse_push_qual(op->larg, topquery, rti, qual); - recurse_push_qual(op->rarg, topquery, rti, qual); + recurse_push_qual(op->larg, topquery, rte, rti, qual); + recurse_push_qual(op->rarg, topquery, rte, rti, qual); } else { diff --git a/src/backend/optimizer/path/clausesel.c b/src/backend/optimizer/path/clausesel.c index 2cd445961e9..96ce94c8fcb 100644 --- a/src/backend/optimizer/path/clausesel.c +++ b/src/backend/optimizer/path/clausesel.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.64 2004/01/05 16:44:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/clausesel.c,v 1.65 2004/05/10 22:44:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -27,11 +27,6 @@ #include "utils/selfuncs.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)) - - /* * Data structure for accumulating info about possible range-query * clause pairs in clauselist_selectivity. @@ -486,7 +481,7 @@ clause_selectivity(Query *root, s1 = restriction_selectivity(root, BooleanEqualOperator, makeList2(var, - MAKEBOOLCONST(true, + makeBoolConst(true, false)), varRelid); } diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 033aaba494e..0ddf1ccd67f 100644 --- a/src/backend/optimizer/prep/prepjointree.c +++ b/src/backend/optimizer/prep/prepjointree.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.16 2004/01/10 18:13:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.17 2004/05/10 22:44:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -45,7 +45,8 @@ typedef struct reduce_outer_joins_state static bool is_simple_subquery(Query *subquery); static bool has_nullable_targetlist(Query *subquery); -static void resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist); +static void resolvenew_in_jointree(Node *jtnode, int varno, + RangeTblEntry *rte, List *subtlist); static reduce_outer_joins_state *reduce_outer_joins_pass1(Node *jtnode); static void reduce_outer_joins_pass2(Node *jtnode, reduce_outer_joins_state *state, @@ -154,14 +155,10 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) * such expressions; we'd have to figure out how to get the pseudo- * variables evaluated at the right place in the modified plan * tree. Fix it someday. - * - * Note: even if the subquery itself is simple enough, we can't pull - * it up if there is a reference to its whole tuple result. - * Perhaps a pseudo-variable is the answer here too. */ - if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(subquery) && - (!below_outer_join || has_nullable_targetlist(subquery)) && - !contain_whole_tuple_var((Node *) parse, varno, 0)) + if (rte->rtekind == RTE_SUBQUERY && + is_simple_subquery(subquery) && + (!below_outer_join || has_nullable_targetlist(subquery))) { int rtoffset; List *subtlist; @@ -206,8 +203,7 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) * the one above. */ if (is_simple_subquery(subquery) && - (!below_outer_join || has_nullable_targetlist(subquery)) && - !contain_whole_tuple_var((Node *) parse, varno, 0)) + (!below_outer_join || has_nullable_targetlist(subquery))) { /* good to go */ } @@ -247,24 +243,25 @@ pull_up_subqueries(Query *parse, Node *jtnode, bool below_outer_join) subtlist = subquery->targetList; parse->targetList = (List *) ResolveNew((Node *) parse->targetList, - varno, 0, subtlist, CMD_SELECT, 0); - resolvenew_in_jointree((Node *) parse->jointree, varno, subtlist); + varno, 0, rte, subtlist, CMD_SELECT, 0); + resolvenew_in_jointree((Node *) parse->jointree, varno, + rte, subtlist); Assert(parse->setOperations == NULL); parse->havingQual = ResolveNew(parse->havingQual, - varno, 0, subtlist, CMD_SELECT, 0); + varno, 0, rte, subtlist, CMD_SELECT, 0); parse->in_info_list = (List *) ResolveNew((Node *) parse->in_info_list, - varno, 0, subtlist, CMD_SELECT, 0); + varno, 0, rte, subtlist, CMD_SELECT, 0); foreach(rt, parse->rtable) { - RangeTblEntry *rte = (RangeTblEntry *) lfirst(rt); + RangeTblEntry *otherrte = (RangeTblEntry *) lfirst(rt); - if (rte->rtekind == RTE_JOIN) - rte->joinaliasvars = (List *) - ResolveNew((Node *) rte->joinaliasvars, - varno, 0, subtlist, CMD_SELECT, 0); + if (otherrte->rtekind == RTE_JOIN) + otherrte->joinaliasvars = (List *) + ResolveNew((Node *) otherrte->joinaliasvars, + varno, 0, rte, subtlist, CMD_SELECT, 0); } /* @@ -479,7 +476,8 @@ has_nullable_targetlist(Query *subquery) * but there's no other way... */ static void -resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist) +resolvenew_in_jointree(Node *jtnode, int varno, + RangeTblEntry *rte, List *subtlist) { if (jtnode == NULL) return; @@ -493,18 +491,18 @@ resolvenew_in_jointree(Node *jtnode, int varno, List *subtlist) List *l; foreach(l, f->fromlist) - resolvenew_in_jointree(lfirst(l), varno, subtlist); + resolvenew_in_jointree(lfirst(l), varno, rte, subtlist); f->quals = ResolveNew(f->quals, - varno, 0, subtlist, CMD_SELECT, 0); + varno, 0, rte, subtlist, CMD_SELECT, 0); } else if (IsA(jtnode, JoinExpr)) { JoinExpr *j = (JoinExpr *) jtnode; - resolvenew_in_jointree(j->larg, varno, subtlist); - resolvenew_in_jointree(j->rarg, varno, subtlist); + resolvenew_in_jointree(j->larg, varno, rte, subtlist); + resolvenew_in_jointree(j->rarg, varno, rte, subtlist); j->quals = ResolveNew(j->quals, - varno, 0, subtlist, CMD_SELECT, 0); + varno, 0, rte, subtlist, CMD_SELECT, 0); /* * We don't bother to update the colvars list, since it won't be diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index f653640ee8c..4dabbf50dac 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.169 2004/04/02 23:14:08 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.170 2004/05/10 22:44:45 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -41,10 +41,6 @@ #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)) - typedef struct { int nargs; @@ -281,7 +277,7 @@ Expr * make_ands_explicit(List *andclauses) { if (andclauses == NIL) - return (Expr *) MAKEBOOLCONST(true, false); + return (Expr *) makeBoolConst(true, false); else if (lnext(andclauses) == NIL) return (Expr *) lfirst(andclauses); else @@ -484,6 +480,8 @@ expression_returns_set_walker(Node *node, void *context) return false; if (IsA(node, ArrayExpr)) return false; + if (IsA(node, RowExpr)) + return false; if (IsA(node, CoalesceExpr)) return false; if (IsA(node, NullIfExpr)) @@ -778,6 +776,8 @@ contain_nonstrict_functions_walker(Node *node, void *context) if (IsA(node, CaseWhen)) return true; /* NB: ArrayExpr might someday be nonstrict */ + if (IsA(node, RowExpr)) + return true; if (IsA(node, CoalesceExpr)) return true; if (IsA(node, NullIfExpr)) @@ -1030,6 +1030,8 @@ set_coercionform_dontcare_walker(Node *node, void *context) ((FuncExpr *) node)->funcformat = COERCE_DONTCARE; if (IsA(node, RelabelType)) ((RelabelType *) node)->relabelformat = COERCE_DONTCARE; + if (IsA(node, RowExpr)) + ((RowExpr *) node)->row_format = COERCE_DONTCARE; if (IsA(node, CoerceToDomain)) ((CoerceToDomain *) node)->coercionformat = COERCE_DONTCARE; return expression_tree_walker(node, set_coercionform_dontcare_walker, @@ -1197,11 +1199,11 @@ eval_const_expressions_mutator(Node *node, List *active_fns) { /* all nulls? then not distinct */ if (all_null_input) - return MAKEBOOLCONST(false, false); + return makeBoolConst(false, false); /* one null? then distinct */ if (has_null_input) - return MAKEBOOLCONST(true, false); + return makeBoolConst(true, false); /* otherwise try to evaluate the '=' operator */ /* (NOT okay to try to inline it, though!) */ @@ -1272,12 +1274,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns) newargs = simplify_or_arguments(args, &haveNull, &forceTrue); if (forceTrue) - return MAKEBOOLCONST(true, false); + return makeBoolConst(true, false); if (haveNull) - newargs = lappend(newargs, MAKEBOOLCONST(false, true)); + newargs = lappend(newargs, makeBoolConst(false, true)); /* If all the inputs are FALSE, result is FALSE */ if (newargs == NIL) - return MAKEBOOLCONST(false, false); + return makeBoolConst(false, false); /* If only one nonconst-or-NULL input, it's the result */ if (lnext(newargs) == NIL) return (Node *) lfirst(newargs); @@ -1293,12 +1295,12 @@ eval_const_expressions_mutator(Node *node, List *active_fns) newargs = simplify_and_arguments(args, &haveNull, &forceFalse); if (forceFalse) - return MAKEBOOLCONST(false, false); + return makeBoolConst(false, false); if (haveNull) - newargs = lappend(newargs, MAKEBOOLCONST(false, true)); + newargs = lappend(newargs, makeBoolConst(false, true)); /* If all the inputs are TRUE, result is TRUE */ if (newargs == NIL) - return MAKEBOOLCONST(true, false); + return makeBoolConst(true, false); /* If only one nonconst-or-NULL input, it's the result */ if (lnext(newargs) == NIL) return (Node *) lfirst(newargs); @@ -1313,9 +1315,9 @@ eval_const_expressions_mutator(Node *node, List *active_fns) /* NOT NULL => NULL */ if (const_input->constisnull) - return MAKEBOOLCONST(false, true); + return makeBoolConst(false, true); /* otherwise pretty easy */ - return MAKEBOOLCONST(!DatumGetBool(const_input->constvalue), + return makeBoolConst(!DatumGetBool(const_input->constvalue), false); } else if (not_clause((Node *) lfirst(args))) @@ -1387,7 +1389,6 @@ eval_const_expressions_mutator(Node *node, List *active_fns) } if (IsA(node, CaseExpr)) { - /*---------- * CASE expressions can be simplified if there are constant * condition clauses: @@ -1546,22 +1547,38 @@ eval_const_expressions_mutator(Node *node, List *active_fns) * We can optimize field selection from a whole-row Var into a * simple Var. (This case won't be generated directly by the * parser, because ParseComplexProjection short-circuits it. But - * it can arise while simplifying functions.) If the argument - * isn't a whole-row Var, just fall through to do generic - * processing. + * it can arise while simplifying functions.) Also, we can + * optimize field selection from a RowExpr construct. */ FieldSelect *fselect = (FieldSelect *) node; - Var *argvar = (Var *) fselect->arg; + FieldSelect *newfselect; + Node *arg; - if (argvar && IsA(argvar, Var) && - argvar->varattno == InvalidAttrNumber) + arg = eval_const_expressions_mutator((Node *) fselect->arg, + active_fns); + if (arg && IsA(arg, Var) && + ((Var *) arg)->varattno == InvalidAttrNumber) { - return (Node *) makeVar(argvar->varno, + return (Node *) makeVar(((Var *) arg)->varno, fselect->fieldnum, fselect->resulttype, fselect->resulttypmod, - argvar->varlevelsup); + ((Var *) arg)->varlevelsup); } + if (arg && IsA(arg, RowExpr)) + { + RowExpr *rowexpr = (RowExpr *) arg; + + if (fselect->fieldnum > 0 && + fselect->fieldnum <= length(rowexpr->args)) + return (Node *) nth(fselect->fieldnum - 1, rowexpr->args); + } + newfselect = makeNode(FieldSelect); + newfselect->arg = (Expr *) arg; + newfselect->fieldnum = fselect->fieldnum; + newfselect->resulttype = fselect->resulttype; + newfselect->resulttypmod = fselect->resulttypmod; + return (Node *) newfselect; } /* @@ -1759,7 +1776,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args, bool has_null_input = false; List *arg; FuncExpr *newexpr; - char result_typtype; /* * Can't simplify if it returns a set. @@ -1797,15 +1813,6 @@ evaluate_function(Oid funcid, Oid result_type, List *args, return NULL; /* - * Can't simplify functions returning composite types (mainly because - * datumCopy() doesn't cope; FIXME someday when we have a saner - * representation for whole-tuple results). - */ - result_typtype = get_typtype(funcform->prorettype); - if (result_typtype == 'c') - return NULL; - - /* * OK, looks like we can simplify this operator/function. * * Build a new FuncExpr node containing the already-simplified arguments. @@ -1850,7 +1857,6 @@ inline_function(Oid funcid, Oid result_type, List *args, HeapTuple func_tuple, List *active_fns) { Form_pg_proc funcform = (Form_pg_proc) GETSTRUCT(func_tuple); - char result_typtype; bool polymorphic = false; Oid argtypes[FUNC_MAX_ARGS]; char *src; @@ -1877,21 +1883,6 @@ inline_function(Oid funcid, Oid result_type, List *args, funcform->pronargs != length(args)) return NULL; - /* - * Forget it if declared return type is not base, domain, or - * polymorphic - */ - result_typtype = get_typtype(funcform->prorettype); - if (result_typtype != 'b' && - result_typtype != 'd') - { - if (funcform->prorettype == ANYARRAYOID || - funcform->prorettype == ANYELEMENTOID) - polymorphic = true; - else - return NULL; - } - /* Check for recursive function, and give up trying to expand if so */ if (oidMember(funcid, active_fns)) return NULL; @@ -1912,6 +1903,10 @@ inline_function(Oid funcid, Oid result_type, List *args, } } + if (funcform->prorettype == ANYARRAYOID || + funcform->prorettype == ANYELEMENTOID) + polymorphic = true; + /* * Setup error traceback support for ereport(). This is so that we * can finger the function that bad information came from. @@ -2483,6 +2478,8 @@ expression_tree_walker(Node *node, break; case T_ArrayExpr: return walker(((ArrayExpr *) node)->elements, context); + case T_RowExpr: + return walker(((RowExpr *) node)->args, context); case T_CoalesceExpr: return walker(((CoalesceExpr *) node)->args, context); case T_NullIfExpr: @@ -2889,6 +2886,16 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_RowExpr: + { + RowExpr *rowexpr = (RowExpr *) node; + RowExpr *newnode; + + FLATCOPY(newnode, rowexpr, RowExpr); + MUTATE(newnode->args, rowexpr->args, List *); + return (Node *) newnode; + } + break; case T_CoalesceExpr: { CoalesceExpr *coalesceexpr = (CoalesceExpr *) node; diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index dac90809de1..47b1fdbf70e 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.55 2003/11/29 19:51:51 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.56 2004/05/10 22:44:45 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -192,19 +192,6 @@ contain_var_reference_walker(Node *node, /* - * contain_whole_tuple_var - * - * Detect whether a parsetree contains any references to the whole - * tuple of a given rtable entry (ie, a Var with varattno = 0). - */ -bool -contain_whole_tuple_var(Node *node, int varno, int levelsup) -{ - return contain_var_reference(node, varno, InvalidAttrNumber, levelsup); -} - - -/* * contain_var_clause * Recursively scan a clause to discover whether it contains any Var nodes * (of the current query level). @@ -486,7 +473,10 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) * flatten_join_alias_vars * Replace Vars that reference JOIN outputs with references to the original * relation variables instead. This allows quals involving such vars to be - * pushed down. + * pushed down. Whole-row Vars that reference JOIN relations are expanded + * into RowExpr constructs that name the individual output Vars. This + * is necessary since we will not scan the JOIN as a base relation, which + * is the only way that the executor can directly handle whole-row Vars. * * NOTE: this is used on not-yet-planned expressions. We do not expect it * to be applied directly to a Query node. @@ -520,8 +510,39 @@ flatten_join_alias_vars_mutator(Node *node, rte = rt_fetch(var->varno, context->root->rtable); if (rte->rtekind != RTE_JOIN) return node; + if (var->varattno == InvalidAttrNumber) + { + /* Must expand whole-row reference */ + RowExpr *rowexpr; + List *fields = NIL; + List *l; + + foreach(l, rte->joinaliasvars) + { + newvar = (Node *) lfirst(l); + /* + * If we are expanding an alias carried down from an upper + * query, must adjust its varlevelsup fields. + */ + if (context->sublevels_up != 0) + { + newvar = copyObject(newvar); + IncrementVarSublevelsUp(newvar, context->sublevels_up, 0); + } + /* Recurse in case join input is itself a join */ + newvar = flatten_join_alias_vars_mutator(newvar, context); + fields = lappend(fields, newvar); + } + rowexpr = makeNode(RowExpr); + rowexpr->args = fields; + rowexpr->row_typeid = var->vartype; + rowexpr->row_format = COERCE_IMPLICIT_CAST; + + return (Node *) rowexpr; + } + + /* Expand join alias reference */ Assert(var->varattno > 0); - /* Okay, must expand it */ newvar = (Node *) nth(var->varattno - 1, rte->joinaliasvars); /* |