diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index f0a4915502..2dce7bab18 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.408 2008/10/07 19:27:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.409 2008/10/21 20:42:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1592,6 +1592,22 @@ _copyFlattenedSubLink(FlattenedSubLink *from) return newnode; } +/* + * _copyPlaceHolderVar + */ +static PlaceHolderVar * +_copyPlaceHolderVar(PlaceHolderVar *from) +{ + PlaceHolderVar *newnode = makeNode(PlaceHolderVar); + + COPY_NODE_FIELD(phexpr); + COPY_BITMAPSET_FIELD(phrels); + COPY_SCALAR_FIELD(phid); + COPY_SCALAR_FIELD(phlevelsup); + + return newnode; +} + /* * _copySpecialJoinInfo */ @@ -1631,6 +1647,23 @@ _copyAppendRelInfo(AppendRelInfo *from) return newnode; } +/* + * _copyPlaceHolderInfo + */ +static PlaceHolderInfo * +_copyPlaceHolderInfo(PlaceHolderInfo *from) +{ + PlaceHolderInfo *newnode = makeNode(PlaceHolderInfo); + + COPY_SCALAR_FIELD(phid); + COPY_NODE_FIELD(ph_var); + COPY_BITMAPSET_FIELD(ph_eval_at); + COPY_BITMAPSET_FIELD(ph_needed); + COPY_SCALAR_FIELD(ph_width); + + return newnode; +} + /* **************************************************************** * parsenodes.h copy functions * **************************************************************** @@ -3438,12 +3471,18 @@ copyObject(void *from) case T_FlattenedSubLink: retval = _copyFlattenedSubLink(from); break; + case T_PlaceHolderVar: + retval = _copyPlaceHolderVar(from); + break; case T_SpecialJoinInfo: retval = _copySpecialJoinInfo(from); break; case T_AppendRelInfo: retval = _copyAppendRelInfo(from); break; + case T_PlaceHolderInfo: + retval = _copyPlaceHolderInfo(from); + break; /* * VALUE NODES diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 96eec16ebc..e0b8bdcecb 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -22,7 +22,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.333 2008/10/06 17:39:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.334 2008/10/21 20:42:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -763,6 +763,27 @@ _equalFlattenedSubLink(FlattenedSubLink *a, FlattenedSubLink *b) return true; } +static bool +_equalPlaceHolderVar(PlaceHolderVar *a, PlaceHolderVar *b) +{ + /* + * We intentionally do not compare phexpr. Two PlaceHolderVars with the + * same ID and levelsup should be considered equal even if the contained + * expressions have managed to mutate to different states. One way in + * which that can happen is that initplan sublinks would get replaced by + * differently-numbered Params when sublink folding is done. (The end + * result of such a situation would be some unreferenced initplans, which + * is annoying but not really a problem.) + * + * COMPARE_NODE_FIELD(phexpr); + */ + COMPARE_BITMAPSET_FIELD(phrels); + COMPARE_SCALAR_FIELD(phid); + COMPARE_SCALAR_FIELD(phlevelsup); + + return true; +} + static bool _equalSpecialJoinInfo(SpecialJoinInfo *a, SpecialJoinInfo *b) { @@ -792,6 +813,18 @@ _equalAppendRelInfo(AppendRelInfo *a, AppendRelInfo *b) return true; } +static bool +_equalPlaceHolderInfo(PlaceHolderInfo *a, PlaceHolderInfo *b) +{ + COMPARE_SCALAR_FIELD(phid); + COMPARE_NODE_FIELD(ph_var); + COMPARE_BITMAPSET_FIELD(ph_eval_at); + COMPARE_BITMAPSET_FIELD(ph_needed); + COMPARE_SCALAR_FIELD(ph_width); + + return true; +} + /* * Stuff from parsenodes.h @@ -2289,12 +2322,19 @@ equal(void *a, void *b) case T_FlattenedSubLink: retval = _equalFlattenedSubLink(a, b); break; + case T_PlaceHolderVar: + retval = _equalPlaceHolderVar(a, b); + break; case T_SpecialJoinInfo: retval = _equalSpecialJoinInfo(a, b); break; case T_AppendRelInfo: retval = _equalAppendRelInfo(a, b); break; + case T_PlaceHolderInfo: + retval = _equalPlaceHolderInfo(a, b); + break; + case T_List: case T_IntList: case T_OidList: diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index baf98b6763..7236360347 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.34 2008/10/06 17:39:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/nodeFuncs.c,v 1.35 2008/10/21 20:42:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -220,6 +220,9 @@ exprType(Node *expr) case T_CurrentOfExpr: type = BOOLOID; break; + case T_PlaceHolderVar: + type = exprType((Node *) ((PlaceHolderVar *) expr)->phexpr); + break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(expr)); type = InvalidOid; /* keep compiler quiet */ @@ -420,6 +423,8 @@ exprTypmod(Node *expr) return ((CoerceToDomainValue *) expr)->typeMod; case T_SetToDefault: return ((SetToDefault *) expr)->typeMod; + case T_PlaceHolderVar: + return exprTypmod((Node *) ((PlaceHolderVar *) expr)->phexpr); default: break; } @@ -876,6 +881,10 @@ exprLocation(Node *expr) case T_CommonTableExpr: loc = ((CommonTableExpr *) expr)->location; break; + case T_PlaceHolderVar: + /* just use argument's location */ + loc = exprLocation((Node *) ((PlaceHolderVar *) expr)->phexpr); + break; default: /* for any other node type it's just unknown... */ loc = -1; @@ -1272,11 +1281,12 @@ expression_tree_walker(Node *node, { FlattenedSubLink *fslink = (FlattenedSubLink *) node; - if (expression_tree_walker((Node *) fslink->quals, - walker, context)) + if (walker(fslink->quals, context)) return true; } break; + case T_PlaceHolderVar: + return walker(((PlaceHolderVar *) node)->phexpr, context); case T_AppendRelInfo: { AppendRelInfo *appinfo = (AppendRelInfo *) node; @@ -1286,6 +1296,8 @@ expression_tree_walker(Node *node, return true; } break; + case T_PlaceHolderInfo: + return walker(((PlaceHolderInfo *) node)->ph_var, context); default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); @@ -1918,6 +1930,17 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_PlaceHolderVar: + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + PlaceHolderVar *newnode; + + FLATCOPY(newnode, phv, PlaceHolderVar); + MUTATE(newnode->phexpr, phv->phexpr, Expr *); + /* Assume we need not copy the relids bitmapset */ + return (Node *) newnode; + } + break; case T_AppendRelInfo: { AppendRelInfo *appinfo = (AppendRelInfo *) node; @@ -1928,6 +1951,17 @@ expression_tree_mutator(Node *node, return (Node *) newnode; } break; + case T_PlaceHolderInfo: + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node; + PlaceHolderInfo *newnode; + + FLATCOPY(newnode, phinfo, PlaceHolderInfo); + MUTATE(newnode->ph_var, phinfo->ph_var, PlaceHolderVar *); + /* Assume we need not copy the relids bitmapsets */ + return (Node *) newnode; + } + break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index 75a04ce2fa..d1130760f9 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.342 2008/10/07 19:27:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.343 2008/10/21 20:42:52 tgl Exp $ * * NOTES * Every node type that can appear in stored rules' parsetrees *must* @@ -1412,6 +1412,8 @@ _outPlannerGlobal(StringInfo str, PlannerGlobal *node) WRITE_NODE_FIELD(finalrtable); WRITE_NODE_FIELD(relationOids); WRITE_NODE_FIELD(invalItems); + WRITE_UINT_FIELD(lastPHId); + WRITE_BOOL_FIELD(transientPlan); } static void @@ -1435,6 +1437,7 @@ _outPlannerInfo(StringInfo str, PlannerInfo *node) WRITE_NODE_FIELD(full_join_clauses); WRITE_NODE_FIELD(join_info_list); WRITE_NODE_FIELD(append_rel_list); + WRITE_NODE_FIELD(placeholder_list); WRITE_NODE_FIELD(query_pathkeys); WRITE_NODE_FIELD(group_pathkeys); WRITE_NODE_FIELD(distinct_pathkeys); @@ -1590,6 +1593,17 @@ _outFlattenedSubLink(StringInfo str, FlattenedSubLink *node) WRITE_NODE_FIELD(quals); } +static void +_outPlaceHolderVar(StringInfo str, PlaceHolderVar *node) +{ + WRITE_NODE_TYPE("PLACEHOLDERVAR"); + + WRITE_NODE_FIELD(phexpr); + WRITE_BITMAPSET_FIELD(phrels); + WRITE_UINT_FIELD(phid); + WRITE_UINT_FIELD(phlevelsup); +} + static void _outSpecialJoinInfo(StringInfo str, SpecialJoinInfo *node) { @@ -1619,6 +1633,18 @@ _outAppendRelInfo(StringInfo str, AppendRelInfo *node) WRITE_OID_FIELD(parent_reloid); } +static void +_outPlaceHolderInfo(StringInfo str, PlaceHolderInfo *node) +{ + WRITE_NODE_TYPE("PLACEHOLDERINFO"); + + WRITE_UINT_FIELD(phid); + WRITE_NODE_FIELD(ph_var); + WRITE_BITMAPSET_FIELD(ph_eval_at); + WRITE_BITMAPSET_FIELD(ph_needed); + WRITE_INT_FIELD(ph_width); +} + static void _outPlannerParamItem(StringInfo str, PlannerParamItem *node) { @@ -2539,12 +2565,18 @@ _outNode(StringInfo str, void *obj) case T_FlattenedSubLink: _outFlattenedSubLink(str, obj); break; + case T_PlaceHolderVar: + _outPlaceHolderVar(str, obj); + break; case T_SpecialJoinInfo: _outSpecialJoinInfo(str, obj); break; case T_AppendRelInfo: _outAppendRelInfo(str, obj); break; + case T_PlaceHolderInfo: + _outPlaceHolderInfo(str, obj); + break; case T_PlannerParamItem: _outPlannerParamItem(str, obj); break; diff --git a/src/backend/optimizer/path/allpaths.c b/src/backend/optimizer/path/allpaths.c index a942249fd0..7d6a3b8d6b 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.174 2008/10/04 21:56:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/allpaths.c,v 1.175 2008/10/21 20:42:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -423,6 +423,10 @@ set_append_rel_pathlist(PlannerInfo *root, RelOptInfo *rel, Var *parentvar = (Var *) lfirst(parentvars); Var *childvar = (Var *) lfirst(childvars); + /* + * Accumulate per-column estimates too. Whole-row Vars and + * PlaceHolderVars can be ignored here. + */ if (IsA(parentvar, Var) && IsA(childvar, Var)) { @@ -1105,12 +1109,25 @@ qual_is_pushdown_safe(Query *subquery, Index rti, Node *qual, * Examine all Vars used in clause; since it's a restriction clause, all * such Vars must refer to subselect output columns. */ - vars = pull_var_clause(qual, false); + vars = pull_var_clause(qual, true); foreach(vl, vars) { Var *var = (Var *) lfirst(vl); TargetEntry *tle; + /* + * XXX Punt if we find any PlaceHolderVars in the restriction clause. + * It's not clear whether a PHV could safely be pushed down, and even + * less clear whether such a situation could arise in any cases of + * practical interest anyway. So for the moment, just refuse to push + * down. + */ + if (!IsA(var, Var)) + { + safe = false; + break; + } + Assert(var->varno == rti); /* Check point 2 */ diff --git a/src/backend/optimizer/path/costsize.c b/src/backend/optimizer/path/costsize.c index fa580cf14e..ba33da829a 100644 --- a/src/backend/optimizer/path/costsize.c +++ b/src/backend/optimizer/path/costsize.c @@ -54,7 +54,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.199 2008/10/17 20:27:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/costsize.c,v 1.200 2008/10/21 20:42:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -69,6 +69,7 @@ #include "optimizer/clauses.h" #include "optimizer/cost.h" #include "optimizer/pathnode.h" +#include "optimizer/placeholder.h" #include "optimizer/planmain.h" #include "parser/parsetree.h" #include "utils/lsyscache.h" @@ -2610,55 +2611,66 @@ set_rel_width(PlannerInfo *root, RelOptInfo *rel) { Oid reloid = planner_rt_fetch(rel->relid, root)->relid; int32 tuple_width = 0; - ListCell *tllist; + ListCell *lc; - foreach(tllist, rel->reltargetlist) + foreach(lc, rel->reltargetlist) { - Var *var = (Var *) lfirst(tllist); - int ndx; - int32 item_width; + Node *node = (Node *) lfirst(lc); - /* For now, punt on whole-row child Vars */ - if (!IsA(var, Var)) + if (IsA(node, Var)) { - tuple_width += 32; /* arbitrary */ - continue; - } + Var *var = (Var *) node; + int ndx; + int32 item_width; - Assert(var->varno == rel->relid); - Assert(var->varattno >= rel->min_attr); - Assert(var->varattno <= rel->max_attr); + Assert(var->varno == rel->relid); + Assert(var->varattno >= rel->min_attr); + Assert(var->varattno <= rel->max_attr); - ndx = var->varattno - rel->min_attr; + ndx = var->varattno - rel->min_attr; - /* - * The width probably hasn't been cached yet, but may as well check - */ - if (rel->attr_widths[ndx] > 0) - { - tuple_width += rel->attr_widths[ndx]; - continue; - } - - if (reloid != InvalidOid) - { - item_width = get_attavgwidth(reloid, var->varattno); - if (item_width > 0) + /* + * The width probably hasn't been cached yet, but may as well check + */ + if (rel->attr_widths[ndx] > 0) { - rel->attr_widths[ndx] = item_width; - tuple_width += item_width; + tuple_width += rel->attr_widths[ndx]; continue; } - } - /* - * Not a plain relation, or can't find statistics for it. Estimate - * using just the type info. - */ - item_width = get_typavgwidth(var->vartype, var->vartypmod); - Assert(item_width > 0); - rel->attr_widths[ndx] = item_width; - tuple_width += item_width; + /* Try to get column width from statistics */ + if (reloid != InvalidOid) + { + item_width = get_attavgwidth(reloid, var->varattno); + if (item_width > 0) + { + rel->attr_widths[ndx] = item_width; + tuple_width += item_width; + continue; + } + } + + /* + * Not a plain relation, or can't find statistics for it. Estimate + * using just the type info. + */ + item_width = get_typavgwidth(var->vartype, var->vartypmod); + Assert(item_width > 0); + rel->attr_widths[ndx] = item_width; + tuple_width += item_width; + } + else if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + PlaceHolderInfo *phinfo = find_placeholder_info(root, phv); + + tuple_width += phinfo->ph_width; + } + else + { + /* For now, punt on whole-row child Vars */ + tuple_width += 32; /* arbitrary */ + } } Assert(tuple_width >= 0); rel->width = tuple_width; diff --git a/src/backend/optimizer/path/equivclass.c b/src/backend/optimizer/path/equivclass.c index 0847b95574..c035100875 100644 --- a/src/backend/optimizer/path/equivclass.c +++ b/src/backend/optimizer/path/equivclass.c @@ -10,7 +10,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.12 2008/08/25 22:42:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/path/equivclass.c,v 1.13 2008/10/21 20:42:52 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -687,7 +687,7 @@ generate_base_implied_equalities_no_const(PlannerInfo *root, foreach(lc, ec->ec_members) { EquivalenceMember *cur_em = (EquivalenceMember *) lfirst(lc); - List *vars = pull_var_clause((Node *) cur_em->em_expr, false); + List *vars = pull_var_clause((Node *) cur_em->em_expr, true); add_vars_to_targetlist(root, vars, ec->ec_relids); list_free(vars); diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index f812c93979..7bddf33e5c 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.250 2008/10/07 19:27:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/createplan.c,v 1.251 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -37,7 +37,7 @@ static Plan *create_scan_plan(PlannerInfo *root, Path *best_path); static List *build_relation_tlist(RelOptInfo *rel); -static bool use_physical_tlist(RelOptInfo *rel); +static bool use_physical_tlist(PlannerInfo *root, RelOptInfo *rel); static void disuse_physical_tlist(Plan *plan, Path *path); static Plan *create_gating_plan(PlannerInfo *root, Plan *plan, List *quals); static Plan *create_join_plan(PlannerInfo *root, JoinPath *best_path); @@ -212,7 +212,7 @@ create_scan_plan(PlannerInfo *root, Path *best_path) * planner.c may replace the tlist we generate here, forcing projection to * occur.) */ - if (use_physical_tlist(rel)) + if (use_physical_tlist(root, rel)) { tlist = build_physical_tlist(root, rel); /* if fail because of dropped cols, use regular method */ @@ -325,9 +325,9 @@ build_relation_tlist(RelOptInfo *rel) foreach(v, rel->reltargetlist) { /* Do we really need to copy here? Not sure */ - Var *var = (Var *) copyObject(lfirst(v)); + Node *node = (Node *) copyObject(lfirst(v)); - tlist = lappend(tlist, makeTargetEntry((Expr *) var, + tlist = lappend(tlist, makeTargetEntry((Expr *) node, resno, NULL, false)); @@ -342,9 +342,10 @@ build_relation_tlist(RelOptInfo *rel) * rather than only those Vars actually referenced. */ static bool -use_physical_tlist(RelOptInfo *rel) +use_physical_tlist(PlannerInfo *root, RelOptInfo *rel) { int i; + ListCell *lc; /* * We can do this for real relation scans, subquery scans, function scans, @@ -365,9 +366,9 @@ use_physical_tlist(RelOptInfo *rel) return false; /* - * Can't do it if any system columns or whole-row Vars are requested, - * either. (This could possibly be fixed but would take some fragile - * assumptions in setrefs.c, I think.) + * Can't do it if any system columns or whole-row Vars are requested. + * (This could possibly be fixed but would take some fragile assumptions + * in setrefs.c, I think.) */ for (i = rel->min_attr; i <= 0; i++) { @@ -375,6 +376,19 @@ use_physical_tlist(RelOptInfo *rel) return false; } + /* + * Can't do it if the rel is required to emit any placeholder expressions, + * either. + */ + foreach(lc, root->placeholder_list) + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); + + if (bms_nonempty_difference(phinfo->ph_needed, rel->relids) && + bms_is_subset(phinfo->ph_eval_at, rel->relids)) + return false; + } + return true; } @@ -2925,7 +2939,7 @@ make_sort_from_pathkeys(PlannerInfo *root, Plan *lefttree, List *pathkeys, if (em->em_is_const || em->em_is_child) continue; sortexpr = em->em_expr; - exprvars = pull_var_clause((Node *) sortexpr, false); + exprvars = pull_var_clause((Node *) sortexpr, true); foreach(k, exprvars) { if (!tlist_member_ignore_relabel(lfirst(k), tlist)) diff --git a/src/backend/optimizer/plan/initsplan.c b/src/backend/optimizer/plan/initsplan.c index 49c2cdf2f5..1208262b8e 100644 --- a/src/backend/optimizer/plan/initsplan.c +++ b/src/backend/optimizer/plan/initsplan.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.142 2008/08/17 01:19:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/initsplan.c,v 1.143 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -21,6 +21,7 @@ #include "optimizer/joininfo.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/placeholder.h" #include "optimizer/planmain.h" #include "optimizer/prep.h" #include "optimizer/restrictinfo.h" @@ -131,7 +132,7 @@ add_base_rels_to_query(PlannerInfo *root, Node *jtnode) void build_base_rel_tlists(PlannerInfo *root, List *final_tlist) { - List *tlist_vars = pull_var_clause((Node *) final_tlist, false); + List *tlist_vars = pull_var_clause((Node *) final_tlist, true); if (tlist_vars != NIL) { @@ -146,6 +147,10 @@ build_base_rel_tlists(PlannerInfo *root, List *final_tlist) * relation's targetlist if not already present, and mark the variable * as being needed for the indicated join (or for final output if * where_needed includes "relation 0"). + * + * The list may also contain PlaceHolderVars. These don't necessarily + * have a single owning relation; we keep their attr_needed info in + * root->placeholder_list instead. */ void add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed) @@ -156,20 +161,36 @@ add_vars_to_targetlist(PlannerInfo *root, List *vars, Relids where_needed) foreach(temp, vars) { - Var *var = (Var *) lfirst(temp); - RelOptInfo *rel = find_base_rel(root, var->varno); - int attrno = var->varattno; + Node *node = (Node *) lfirst(temp); - Assert(attrno >= rel->min_attr && attrno <= rel->max_attr); - attrno -= rel->min_attr; - if (bms_is_empty(rel->attr_needed[attrno])) + if (IsA(node, Var)) { - /* Variable not yet requested, so add to reltargetlist */ - /* XXX is copyObject necessary here? */ - rel->reltargetlist = lappend(rel->reltargetlist, copyObject(var)); + Var *var = (Var *) node; + RelOptInfo *rel = find_base_rel(root, var->varno); + int attno = var->varattno; + + Assert(attno >= rel->min_attr && attno <= rel->max_attr); + attno -= rel->min_attr; + if (rel->attr_needed[attno] == NULL) + { + /* Variable not yet requested, so add to reltargetlist */ + /* XXX is copyObject necessary here? */ + rel->reltargetlist = lappend(rel->reltargetlist, + copyObject(var)); + } + rel->attr_needed[attno] = bms_add_members(rel->attr_needed[attno], + where_needed); } - rel->attr_needed[attrno] = bms_add_members(rel->attr_needed[attrno], - where_needed); + else if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + PlaceHolderInfo *phinfo = find_placeholder_info(root, phv); + + phinfo->ph_needed = bms_add_members(phinfo->ph_needed, + where_needed); + } + else + elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node)); } } @@ -934,7 +955,7 @@ distribute_qual_to_rels(PlannerInfo *root, Node *clause, */ if (bms_membership(relids) == BMS_MULTIPLE) { - List *vars = pull_var_clause(clause, false); + List *vars = pull_var_clause(clause, true); add_vars_to_targetlist(root, vars, relids); list_free(vars); diff --git a/src/backend/optimizer/plan/planmain.c b/src/backend/optimizer/plan/planmain.c index 7dcdaf250f..171724983d 100644 --- a/src/backend/optimizer/plan/planmain.c +++ b/src/backend/optimizer/plan/planmain.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.110 2008/08/14 18:47:59 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planmain.c,v 1.111 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -23,6 +23,7 @@ #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/placeholder.h" #include "optimizer/planmain.h" #include "optimizer/tlist.h" #include "utils/selfuncs.h" @@ -132,7 +133,7 @@ query_planner(PlannerInfo *root, List *tlist, * for "simple" rels. * * NOTE: append_rel_list was set up by subquery_planner, so do not touch - * here; eq_classes may contain data already, too. + * here; ditto placeholder_list; eq_classes may contain data already, too. */ root->simple_rel_array_size = list_length(parse->rtable) + 1; root->simple_rel_array = (RelOptInfo **) @@ -204,12 +205,6 @@ query_planner(PlannerInfo *root, List *tlist, * added to appropriate lists belonging to the mentioned relations. We * also build EquivalenceClasses for provably equivalent expressions, and * form a target joinlist for make_one_rel() to work from. - * - * Note: 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... */ build_base_rel_tlists(root, tlist); @@ -240,6 +235,13 @@ query_planner(PlannerInfo *root, List *tlist, root->distinct_pathkeys = canonicalize_pathkeys(root, root->distinct_pathkeys); root->sort_pathkeys = canonicalize_pathkeys(root, root->sort_pathkeys); + /* + * Examine any "placeholder" expressions generated during subquery pullup. + * Make sure that we know what level to evaluate them at, and that the + * Vars they need are marked as needed. + */ + fix_placeholder_eval_levels(root); + /* * Ready to do the primary planning. */ diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index 0a704dc654..05de001a87 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.244 2008/10/04 21:56:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/planner.c,v 1.245 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -55,7 +55,7 @@ planner_hook_type planner_hook = NULL; #define EXPRKIND_RTFUNC 2 #define EXPRKIND_VALUES 3 #define EXPRKIND_LIMIT 4 -#define EXPRKIND_APPINFO 5 +#define EXPRKIND_AUXINFO 5 static Node *preprocess_expression(PlannerInfo *root, Node *expr, int kind); @@ -141,6 +141,7 @@ standard_planner(Query *parse, int cursorOptions, ParamListInfo boundParams) glob->finalrtable = NIL; glob->relationOids = NIL; glob->invalItems = NIL; + glob->lastPHId = 0; glob->transientPlan = false; /* Determine what fraction of the plan is likely to be scanned */ @@ -273,6 +274,7 @@ subquery_planner(PlannerGlobal *glob, Query *parse, root->cte_plan_ids = NIL; root->eq_classes = NIL; root->append_rel_list = NIL; + root->placeholder_list = NIL; root->hasRecursion = hasRecursion; if (hasRecursion) @@ -378,7 +380,10 @@ subquery_planner(PlannerGlobal *glob, Query *parse, root->append_rel_list = (List *) preprocess_expression(root, (Node *) root->append_rel_list, - EXPRKIND_APPINFO); + EXPRKIND_AUXINFO); + root->placeholder_list = (List *) + preprocess_expression(root, (Node *) root->placeholder_list, + EXPRKIND_AUXINFO); /* Also need to preprocess expressions for function and values RTEs */ foreach(l, parse->rtable) @@ -656,7 +661,12 @@ inheritance_planner(PlannerInfo *root) subroot.parse = (Query *) adjust_appendrel_attrs((Node *) parse, appinfo); + subroot.returningLists = NIL; subroot.init_plans = NIL; + /* We needn't modify the child's append_rel_list */ + subroot.placeholder_list = (List *) + adjust_appendrel_attrs((Node *) root->placeholder_list, + appinfo); /* There shouldn't be any OJ info to translate, as yet */ Assert(subroot.join_info_list == NIL); @@ -2046,7 +2056,7 @@ make_subplanTargetList(PlannerInfo *root, * Vars; they will be replaced by Params later on). */ sub_tlist = flatten_tlist(tlist); - extravars = pull_var_clause(parse->havingQual, false); + extravars = pull_var_clause(parse->havingQual, true); sub_tlist = add_to_flat_tlist(sub_tlist, extravars); list_free(extravars); *need_tlist_eval = false; /* only eval if not flat tlist */ diff --git a/src/backend/optimizer/plan/setrefs.c b/src/backend/optimizer/plan/setrefs.c index 6d7ec283b3..9bec109f6f 100644 --- a/src/backend/optimizer/plan/setrefs.c +++ b/src/backend/optimizer/plan/setrefs.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.145 2008/10/04 21:56:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/setrefs.c,v 1.146 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -38,7 +38,8 @@ typedef struct { List *tlist; /* underlying target list */ int num_vars; /* number of plain Var tlist entries */ - bool has_non_vars; /* are there non-plain-Var entries? */ + bool has_ph_vars; /* are there PlaceHolderVar entries? */ + bool has_non_vars; /* are there other entries? */ /* array of num_vars entries: */ tlist_vinfo vars[1]; /* VARIABLE LENGTH ARRAY */ } indexed_tlist; /* VARIABLE LENGTH STRUCT */ @@ -741,15 +742,16 @@ fix_scan_expr(PlannerGlobal *glob, Node *node, int rtoffset) context.glob = glob; context.rtoffset = rtoffset; - if (rtoffset != 0) + if (rtoffset != 0 || glob->lastPHId != 0) { return fix_scan_expr_mutator(node, &context); } else { /* - * If rtoffset == 0, we don't need to change any Vars, which makes - * it OK to just scribble on the input node tree instead of copying + * If rtoffset == 0, we don't need to change any Vars, and if there + * are no placeholders anywhere we won't need to remove them. Then + * it's OK to just scribble on the input node tree instead of copying * (since the only change, filling in any unset opfuncid fields, * is harmless). This saves just enough cycles to be noticeable on * trivial queries. @@ -790,6 +792,13 @@ fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context) cexpr->cvarno += context->rtoffset; return (Node *) cexpr; } + if (IsA(node, PlaceHolderVar)) + { + /* At scan level, we should always just evaluate the contained expr */ + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + return fix_scan_expr_mutator((Node *) phv->phexpr, context); + } fix_expr_common(context->glob, node); return expression_tree_mutator(node, fix_scan_expr_mutator, (void *) context); @@ -800,6 +809,7 @@ fix_scan_expr_walker(Node *node, fix_scan_expr_context *context) { if (node == NULL) return false; + Assert(!IsA(node, PlaceHolderVar)); fix_expr_common(context->glob, node); return expression_tree_walker(node, fix_scan_expr_walker, (void *) context); @@ -1199,6 +1209,7 @@ build_tlist_index(List *tlist) list_length(tlist) * sizeof(tlist_vinfo)); itlist->tlist = tlist; + itlist->has_ph_vars = false; itlist->has_non_vars = false; /* Find the Vars and fill in the index array */ @@ -1216,6 +1227,8 @@ build_tlist_index(List *tlist) vinfo->resno = tle->resno; vinfo++; } + else if (tle->expr && IsA(tle->expr, PlaceHolderVar)) + itlist->has_ph_vars = true; else itlist->has_non_vars = true; } @@ -1229,7 +1242,9 @@ build_tlist_index(List *tlist) * build_tlist_index_other_vars --- build a restricted tlist index * * This is like build_tlist_index, but we only index tlist entries that - * are Vars and belong to some rel other than the one specified. + * are Vars belonging to some rel other than the one specified. We will set + * has_ph_vars (allowing PlaceHolderVars to be matched), but not has_non_vars + * (so nothing other than Vars and PlaceHolderVars can be matched). */ static indexed_tlist * build_tlist_index_other_vars(List *tlist, Index ignore_rel) @@ -1244,6 +1259,7 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel) list_length(tlist) * sizeof(tlist_vinfo)); itlist->tlist = tlist; + itlist->has_ph_vars = false; itlist->has_non_vars = false; /* Find the desired Vars and fill in the index array */ @@ -1264,6 +1280,8 @@ build_tlist_index_other_vars(List *tlist, Index ignore_rel) vinfo++; } } + else if (tle->expr && IsA(tle->expr, PlaceHolderVar)) + itlist->has_ph_vars = true; } itlist->num_vars = (vinfo - itlist->vars); @@ -1314,7 +1332,8 @@ search_indexed_tlist_for_var(Var *var, indexed_tlist *itlist, * If a match is found, return a Var constructed to reference the tlist item. * If no match, return NULL. * - * NOTE: it is a waste of time to call this if !itlist->has_non_vars + * NOTE: it is a waste of time to call this unless itlist->has_ph_vars or + * itlist->has_non_vars */ static Var * search_indexed_tlist_for_non_var(Node *node, @@ -1429,6 +1448,31 @@ fix_join_expr_mutator(Node *node, fix_join_expr_context *context) /* No referent found for Var */ elog(ERROR, "variable not found in subplan target lists"); } + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + /* See if the PlaceHolderVar has bubbled up from a lower plan node */ + if (context->outer_itlist->has_ph_vars) + { + newvar = search_indexed_tlist_for_non_var((Node *) phv, + context->outer_itlist, + OUTER); + if (newvar) + return (Node *) newvar; + } + if (context->inner_itlist && context->inner_itlist->has_ph_vars) + { + newvar = search_indexed_tlist_for_non_var((Node *) phv, + context->inner_itlist, + INNER); + if (newvar) + return (Node *) newvar; + } + + /* If not supplied by input plans, evaluate the contained expr */ + return fix_join_expr_mutator((Node *) phv->phexpr, context); + } /* Try matching more complex expressions too, if tlists have any */ if (context->outer_itlist->has_non_vars) { @@ -1512,6 +1556,22 @@ fix_upper_expr_mutator(Node *node, fix_upper_expr_context *context) elog(ERROR, "variable not found in subplan target list"); return (Node *) newvar; } + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + /* See if the PlaceHolderVar has bubbled up from a lower plan node */ + if (context->subplan_itlist->has_ph_vars) + { + newvar = search_indexed_tlist_for_non_var((Node *) phv, + context->subplan_itlist, + OUTER); + if (newvar) + return (Node *) newvar; + } + /* If not supplied by input plan, evaluate the contained expr */ + return fix_upper_expr_mutator((Node *) phv->phexpr, context); + } /* Try matching more complex expressions too, if tlist has any */ if (context->subplan_itlist->has_non_vars) { @@ -1564,6 +1624,13 @@ set_returning_clause_references(PlannerGlobal *glob, * top plan's targetlist for Vars of non-result relations, and use * fix_join_expr to convert RETURNING Vars into references to those tlist * entries, while leaving result-rel Vars as-is. + * + * PlaceHolderVars will also be sought in the targetlist, but no + * more-complex expressions will be. Note that it is not possible for + * a PlaceHolderVar to refer to the result relation, since the result + * is never below an outer join. If that case could happen, we'd have + * to be prepared to pick apart the PlaceHolderVar and evaluate its + * contained expression instead. */ itlist = build_tlist_index_other_vars(topplan->targetlist, resultRelation); @@ -1721,6 +1788,7 @@ extract_query_dependencies_walker(Node *node, PlannerGlobal *context) { if (node == NULL) return false; + Assert(!IsA(node, PlaceHolderVar)); /* Extract function dependencies and check for regclass Consts */ fix_expr_common(context, node); if (IsA(node, Query)) diff --git a/src/backend/optimizer/plan/subselect.c b/src/backend/optimizer/plan/subselect.c index 00d84d5682..b80427f041 100644 --- a/src/backend/optimizer/plan/subselect.c +++ b/src/backend/optimizer/plan/subselect.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.141 2008/10/04 21:56:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/plan/subselect.c,v 1.142 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1040,6 +1040,10 @@ convert_ANY_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* * And finally, build the FlattenedSubLink node. + * + * Note: at this point left_varnos may well contain join relids, since + * the testexpr hasn't been run through flatten_join_alias_vars. This + * will get fixed when flatten_join_alias_vars is run. */ fslink = makeNode(FlattenedSubLink); fslink->jointype = JOIN_SEMI; @@ -1189,6 +1193,9 @@ convert_EXISTS_sublink_to_join(PlannerInfo *root, SubLink *sublink, /* * And finally, build the FlattenedSubLink node. + * + * Note: at this point left_varnos and subselect_varnos may well contain + * join relids. This will get fixed when flatten_join_alias_vars is run. */ fslink = makeNode(FlattenedSubLink); fslink->jointype = under_not ? JOIN_ANTI : JOIN_SEMI; diff --git a/src/backend/optimizer/prep/prepjointree.c b/src/backend/optimizer/prep/prepjointree.c index 1c73c57dad..45de509619 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.56 2008/10/09 19:27:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepjointree.c,v 1.57 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -25,6 +25,7 @@ #include "nodes/makefuncs.h" #include "nodes/nodeFuncs.h" #include "optimizer/clauses.h" +#include "optimizer/placeholder.h" #include "optimizer/prep.h" #include "optimizer/subselect.h" #include "optimizer/tlist.h" @@ -60,7 +61,8 @@ static bool is_simple_subquery(Query *subquery); static bool is_simple_union_all(Query *subquery); static bool is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes); -static bool has_nullable_targetlist(Query *subquery); +static List *insert_targetlist_placeholders(PlannerInfo *root, List *tlist, + int varno, bool wrap_non_vars); static bool is_safe_append_member(Query *subquery); static void resolvenew_in_jointree(Node *jtnode, int varno, RangeTblEntry *rte, List *subtlist); @@ -71,8 +73,8 @@ static void reduce_outer_joins_pass2(Node *jtnode, Relids nonnullable_rels, List *nonnullable_vars, List *forced_null_vars); -static void fix_flattened_sublink_relids(Node *node, - int varno, Relids subrelids); +static void substitute_multiple_relids(Node *node, + int varno, Relids subrelids); static void fix_append_rel_relids(List *append_rel_list, int varno, Relids subrelids); static Node *find_jointree_node_for_rel(Node *jtnode, int relid); @@ -406,11 +408,13 @@ inline_set_returning_functions(PlannerInfo *root) * converted into "append relations". * * below_outer_join is true if this jointree node is within the nullable - * side of an outer join. This restricts what we can do. + * side of an outer join. This forces use of the PlaceHolderVar mechanism + * for non-nullable targetlist items. * * append_rel_member is true if we are looking at a member subquery of - * an append relation. This puts some different restrictions on what - * we can do. + * an append relation. This forces use of the PlaceHolderVar mechanism + * for all non-Var targetlist items, and puts some additional restrictions + * on what can be pulled up. * * A tricky aspect of this code is that if we pull up a subquery we have * to replace Vars that reference the subquery's outputs throughout the @@ -434,24 +438,13 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, /* * Is this a subquery RTE, and if so, is the subquery simple enough to - * pull up? (If not, do nothing at this node.) - * - * If we are inside an outer join, only pull up subqueries whose - * targetlists are nullable --- otherwise substituting their tlist - * entries for upper Var references would do the wrong thing (the - * results wouldn't become NULL when they're supposed to). - * - * XXX This could be improved by generating pseudo-variables for 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. + * pull up? * * If we are looking at an append-relation member, we can't pull it up * unless is_safe_append_member says so. */ if (rte->rtekind == RTE_SUBQUERY && is_simple_subquery(rte->subquery) && - (!below_outer_join || has_nullable_targetlist(rte->subquery)) && (!append_rel_member || is_safe_append_member(rte->subquery))) return pull_up_simple_subquery(root, jtnode, rte, below_outer_join, @@ -459,12 +452,9 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, /* * Alternatively, is it a simple UNION ALL subquery? If so, flatten - * into an "append relation". We can do this regardless of - * nullability considerations since this transformation does not - * result in propagating non-Var expressions into upper levels of the - * query. + * into an "append relation". * - * It's also safe to do this regardless of whether this query is + * It's safe to do this regardless of whether this query is * itself an appendrel member. (If you're thinking we should try to * flatten the two levels of appendrel together, you're right; but we * handle that in set_append_rel_pathlist, not here.) @@ -472,6 +462,8 @@ pull_up_subqueries(PlannerInfo *root, Node *jtnode, if (rte->rtekind == RTE_SUBQUERY && is_simple_union_all(rte->subquery)) return pull_up_simple_union_all(root, jtnode, rte); + + /* Otherwise, do nothing at this node. */ } else if (IsA(jtnode, FromExpr)) { @@ -573,6 +565,7 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, subroot->cte_plan_ids = NIL; subroot->eq_classes = NIL; subroot->append_rel_list = NIL; + subroot->placeholder_list = NIL; subroot->hasRecursion = false; subroot->wt_param_id = -1; subroot->non_recursive_plan = NULL; @@ -614,7 +607,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, * pull_up_subqueries. */ if (is_simple_subquery(subquery) && - (!below_outer_join || has_nullable_targetlist(subquery)) && (!append_rel_member || is_safe_append_member(subquery))) { /* good to go */ @@ -635,11 +627,12 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, /* * Adjust level-0 varnos in subquery so that we can append its rangetable * to upper query's. We have to fix the subquery's append_rel_list - * as well. + * and placeholder_list as well. */ rtoffset = list_length(parse->rtable); OffsetVarNodes((Node *) subquery, rtoffset, 0); OffsetVarNodes((Node *) subroot->append_rel_list, rtoffset, 0); + OffsetVarNodes((Node *) subroot->placeholder_list, rtoffset, 0); /* * Upper-level vars in subquery are now one level closer to their parent @@ -647,6 +640,20 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, */ IncrementVarSublevelsUp((Node *) subquery, -1, 1); IncrementVarSublevelsUp((Node *) subroot->append_rel_list, -1, 1); + IncrementVarSublevelsUp((Node *) subroot->placeholder_list, -1, 1); + + /* + * The subquery's targetlist items are now in the appropriate form to + * insert into the top query, but if we are under an outer join then + * non-nullable items have to be turned into PlaceHolderVars. If we + * are dealing with an appendrel member then anything that's not a + * simple Var has to be turned into a PlaceHolderVar. + */ + if (below_outer_join || append_rel_member) + subtlist = insert_targetlist_placeholders(root, subquery->targetList, + varno, append_rel_member); + else + subtlist = subquery->targetList; /* * Replace all of the top query's references to the subquery's outputs @@ -654,7 +661,6 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, * replace any of the jointree structure. (This'd be a lot cleaner if we * could use query_tree_mutator.) */ - subtlist = subquery->targetList; parse->targetList = (List *) ResolveNew((Node *) parse->targetList, varno, 0, rte, @@ -700,29 +706,41 @@ pull_up_simple_subquery(PlannerInfo *root, Node *jtnode, RangeTblEntry *rte, parse->rowMarks = list_concat(parse->rowMarks, subquery->rowMarks); /* - * We also have to fix the relid sets of any FlattenedSubLink nodes in - * the parent query. (This could perhaps be done by ResolveNew, but it - * would clutter that routine's API unreasonably.) + * We also have to fix the relid sets of any FlattenedSubLink, + * PlaceHolderVar, and PlaceHolderInfo nodes in the parent query. + * (This could perhaps be done by ResolveNew, but it would clutter that + * routine's API unreasonably.) Note in particular that any placeholder + * nodes just created by insert_targetlist_placeholders() wiil be adjusted. * * Likewise, relids appearing in AppendRelInfo nodes have to be fixed (but * we took care of their translated_vars lists above). We already checked * that this won't require introducing multiple subrelids into the * single-slot AppendRelInfo structs. */ - if (parse->hasSubLinks || root->append_rel_list) + if (parse->hasSubLinks || root->placeholder_list || root->append_rel_list) { Relids subrelids; subrelids = get_relids_in_jointree((Node *) subquery->jointree, false); - fix_flattened_sublink_relids((Node *) parse, varno, subrelids); - fix_append_rel_relids(root->append_rel_list, varno, subrelids); + substitute_multiple_relids((Node *) parse, + varno, subrelids); + substitute_multiple_relids((Node *) root->placeholder_list, + varno, subrelids); + fix_append_rel_relids(root->append_rel_list, + varno, subrelids); } /* - * And now add subquery's AppendRelInfos to our list. + * And now add subquery's AppendRelInfos and PlaceHolderInfos to our lists. + * Note that any placeholders pulled up from the subquery will appear + * after any we just created; this preserves the property that placeholders + * can only refer to other placeholders that appear later in the list + * (needed by fix_placeholder_eval_levels). */ root->append_rel_list = list_concat(root->append_rel_list, subroot->append_rel_list); + root->placeholder_list = list_concat(root->placeholder_list, + subroot->placeholder_list); /* * We don't have to do the equivalent bookkeeping for outer-join info, @@ -950,7 +968,9 @@ is_simple_subquery(Query *subquery) * Don't pull up a subquery that has any volatile functions in its * targetlist. Otherwise we might introduce multiple evaluations of these * functions, if they get copied to multiple places in the upper query, - * leading to surprising results. + * leading to surprising results. (Note: the PlaceHolderVar mechanism + * doesn't quite guarantee single evaluation; else we could pull up anyway + * and just wrap such items in PlaceHolderVars ...) */ if (contain_volatile_functions((Node *) subquery->targetList)) return false; @@ -959,8 +979,12 @@ is_simple_subquery(Query *subquery) * Hack: don't try to pull up a subquery with an empty jointree. * query_planner() will correctly generate a Result plan for a jointree * that's totally empty, but I don't think the right things happen if an - * empty FromExpr appears lower down in a jointree. Not worth working hard - * on this, just to collapse SubqueryScan/Result into Result... + * empty FromExpr appears lower down in a jointree. It would pose a + * problem for the PlaceHolderVar mechanism too, since we'd have no + * way to identify where to evaluate a PHV coming out of the subquery. + * Not worth working hard on this, just to collapse SubqueryScan/Result + * into Result; especially since the SubqueryScan can often be optimized + * away by setrefs.c anyway. */ if (subquery->jointree->fromlist == NIL) return false; @@ -1043,40 +1067,59 @@ is_simple_union_all_recurse(Node *setOp, Query *setOpQuery, List *colTypes) } /* - * has_nullable_targetlist - * Check a subquery in the range table to see if all the non-junk - * targetlist items are simple variables or strict functions of simple - * variables (and, hence, will correctly go to NULL when examined above - * the point of an outer join). + * insert_targetlist_placeholders + * Insert PlaceHolderVar nodes into any non-junk targetlist items that are + * not simple variables or strict functions of simple variables (and hence + * might not correctly go to NULL when examined above the point of an outer + * join). We assume we can modify the tlist items in-place. * - * NOTE: it would be correct (and useful) to ignore output columns that aren't - * actually referenced by the enclosing query ... but we do not have that - * information available at this point. + * varno is the upper-query relid of the subquery; this is used as the + * syntactic location of the PlaceHolderVars. + * If wrap_non_vars is true then *only* simple Var references escape being + * wrapped with PlaceHolderVars. */ -static bool -has_nullable_targetlist(Query *subquery) +static List * +insert_targetlist_placeholders(PlannerInfo *root, List *tlist, + int varno, bool wrap_non_vars) { - ListCell *l; + ListCell *lc; - foreach(l, subquery->targetList) + foreach(lc, tlist) { - TargetEntry *tle = (TargetEntry *) lfirst(l); + TargetEntry *tle = (TargetEntry *) lfirst(lc); /* ignore resjunk columns */ if (tle->resjunk) continue; - /* Must contain a Var of current level */ - if (!contain_vars_of_level((Node *) tle->expr, 0)) - return false; + /* + * Simple Vars always escape being wrapped. This is common enough + * to deserve a fast path even if we aren't doing wrap_non_vars. + */ + if (tle->expr && IsA(tle->expr, Var) && + ((Var *) tle->expr)->varlevelsup == 0) + continue; - /* Must not contain any non-strict constructs */ - if (contain_nonstrict_functions((Node *) tle->expr)) - return false; + if (!wrap_non_vars) + { + /* + * If it contains a Var of current level, and does not contain + * any non-strict constructs, then it's certainly nullable and we + * don't need to insert a PlaceHolderVar. (Note: in future maybe + * we should insert PlaceHolderVars anyway, when a tlist item is + * expensive to evaluate? + */ + if (contain_vars_of_level((Node *) tle->expr, 0) && + !contain_nonstrict_functions((Node *) tle->expr)) + continue; + } - /* This one's OK, keep scanning */ + /* Else wrap it in a PlaceHolderVar */ + tle->expr = (Expr *) make_placeholder_expr(root, + tle->expr, + bms_make_singleton(varno)); } - return true; + return tlist; } /* @@ -1088,7 +1131,6 @@ static bool is_safe_append_member(Query *subquery) { FromExpr *jtnode; - ListCell *l; /* * It's only safe to pull up the child if its jointree contains exactly @@ -1113,24 +1155,6 @@ is_safe_append_member(Query *subquery) if (!IsA(jtnode, RangeTblRef)) return false; - /* - * XXX For the moment we also have to insist that the subquery's tlist - * includes only simple Vars. This is pretty annoying, but fixing it - * seems to require nontrivial changes --- mainly because joinrel tlists - * are presently assumed to contain only Vars. Perhaps a pseudo-variable - * mechanism similar to the one speculated about in pull_up_subqueries' - * comments would help? FIXME someday. - */ - foreach(l, subquery->targetList) - { - TargetEntry *tle = (TargetEntry *) lfirst(l); - - if (tle->resjunk) - continue; - if (!(tle->expr && IsA(tle->expr, Var))) - return false; - } - return true; } @@ -1579,28 +1603,29 @@ reduce_outer_joins_pass2(Node *jtnode, } /* - * fix_flattened_sublink_relids - adjust FlattenedSubLink nodes after - * pulling up a subquery + * substitute_multiple_relids - adjust node relid sets after pulling up + * a subquery * - * Find any FlattenedSubLink nodes in the given tree that reference the - * pulled-up relid, and change them to reference the replacement relid(s). - * We do not need to recurse into subqueries, since no subquery of the - * current top query could contain such a reference. + * Find any FlattenedSubLink, PlaceHolderVar, or PlaceHolderInfo nodes in the + * given tree that reference the pulled-up relid, and change them to reference + * the replacement relid(s). We do not need to recurse into subqueries, since + * no subquery of the current top query could (yet) contain such a reference. * * NOTE: although this has the form of a walker, we cheat and modify the * nodes in-place. This should be OK since the tree was copied by ResolveNew - * earlier. + * earlier. Avoid scribbling on the original values of the bitmapsets, though, + * because expression_tree_mutator doesn't copy those. */ typedef struct { int varno; Relids subrelids; -} fix_flattened_sublink_relids_context; +} substitute_multiple_relids_context; static bool -fix_flattened_sublink_relids_walker(Node *node, - fix_flattened_sublink_relids_context *context) +substitute_multiple_relids_walker(Node *node, + substitute_multiple_relids_context *context) { if (node == NULL) return false; @@ -1610,28 +1635,61 @@ fix_flattened_sublink_relids_walker(Node *node, if (bms_is_member(context->varno, fslink->lefthand)) { + fslink->lefthand = bms_union(fslink->lefthand, + context->subrelids); fslink->lefthand = bms_del_member(fslink->lefthand, context->varno); - fslink->lefthand = bms_add_members(fslink->lefthand, - context->subrelids); } if (bms_is_member(context->varno, fslink->righthand)) { + fslink->righthand = bms_union(fslink->righthand, + context->subrelids); fslink->righthand = bms_del_member(fslink->righthand, context->varno); - fslink->righthand = bms_add_members(fslink->righthand, - context->subrelids); } /* fall through to examine children */ } - return expression_tree_walker(node, fix_flattened_sublink_relids_walker, + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + if (bms_is_member(context->varno, phv->phrels)) + { + phv->phrels = bms_union(phv->phrels, + context->subrelids); + phv->phrels = bms_del_member(phv->phrels, + context->varno); + } + /* fall through to examine children */ + } + if (IsA(node, PlaceHolderInfo)) + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node; + + if (bms_is_member(context->varno, phinfo->ph_eval_at)) + { + phinfo->ph_eval_at = bms_union(phinfo->ph_eval_at, + context->subrelids); + phinfo->ph_eval_at = bms_del_member(phinfo->ph_eval_at, + context->varno); + } + if (bms_is_member(context->varno, phinfo->ph_needed)) + { + phinfo->ph_needed = bms_union(phinfo->ph_needed, + context->subrelids); + phinfo->ph_needed = bms_del_member(phinfo->ph_needed, + context->varno); + } + /* fall through to examine children */ + } + return expression_tree_walker(node, substitute_multiple_relids_walker, (void *) context); } static void -fix_flattened_sublink_relids(Node *node, int varno, Relids subrelids) +substitute_multiple_relids(Node *node, int varno, Relids subrelids) { - fix_flattened_sublink_relids_context context; + substitute_multiple_relids_context context; context.varno = varno; context.subrelids = subrelids; @@ -1640,7 +1698,7 @@ fix_flattened_sublink_relids(Node *node, int varno, Relids subrelids) * Must be prepared to start with a Query or a bare expression tree. */ query_or_expression_tree_walker(node, - fix_flattened_sublink_relids_walker, + substitute_multiple_relids_walker, (void *) &context, 0); } diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 08b70c8ee5..45aa0e9ca5 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -16,7 +16,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.91 2008/08/28 23:09:46 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.92 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -168,13 +168,14 @@ preprocess_targetlist(PlannerInfo *root, List *tlist) List *vars; ListCell *l; - vars = pull_var_clause((Node *) parse->returningList, false); + vars = pull_var_clause((Node *) parse->returningList, true); foreach(l, vars) { Var *var = (Var *) lfirst(l); TargetEntry *tle; - if (var->varno == result_relation) + if (IsA(var, Var) && + var->varno == result_relation) continue; /* don't need it */ if (tlist_member((Node *) var, tlist)) diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index 2e22d09b4a..0efd150f6b 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -22,7 +22,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.158 2008/10/07 19:27:04 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/prep/prepunion.c,v 1.159 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1584,7 +1584,39 @@ adjust_appendrel_attrs_mutator(Node *node, AppendRelInfo *context) context->child_relid); return (Node *) fslink; } - /* Shouldn't need to handle SpecialJoinInfo or AppendRelInfo here */ + if (IsA(node, PlaceHolderVar)) + { + /* Copy the PlaceHolderVar node with correct mutation of subnodes */ + PlaceHolderVar *phv; + + phv = (PlaceHolderVar *) expression_tree_mutator(node, + adjust_appendrel_attrs_mutator, + (void *) context); + /* now fix PlaceHolderVar's relid sets */ + if (phv->phlevelsup == 0) + phv->phrels = adjust_relid_set(phv->phrels, + context->parent_relid, + context->child_relid); + return (Node *) phv; + } + if (IsA(node, PlaceHolderInfo)) + { + /* Copy the PlaceHolderInfo node with correct mutation of subnodes */ + PlaceHolderInfo *phinfo; + + phinfo = (PlaceHolderInfo *) expression_tree_mutator(node, + adjust_appendrel_attrs_mutator, + (void *) context); + /* now fix PlaceHolderInfo's relid sets */ + phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at, + context->parent_relid, + context->child_relid); + phinfo->ph_needed = adjust_relid_set(phinfo->ph_needed, + context->parent_relid, + context->child_relid); + return (Node *) phinfo; + } + /* Shouldn't need to handle other planner auxiliary nodes here */ Assert(!IsA(node, SpecialJoinInfo)); Assert(!IsA(node, AppendRelInfo)); diff --git a/src/backend/optimizer/util/Makefile b/src/backend/optimizer/util/Makefile index 0ea1dc32cc..d13b18c53f 100644 --- a/src/backend/optimizer/util/Makefile +++ b/src/backend/optimizer/util/Makefile @@ -4,7 +4,7 @@ # Makefile for optimizer/util # # IDENTIFICATION -# $PostgreSQL: pgsql/src/backend/optimizer/util/Makefile,v 1.18 2008/02/19 10:30:07 petere Exp $ +# $PostgreSQL: pgsql/src/backend/optimizer/util/Makefile,v 1.19 2008/10/21 20:42:53 tgl Exp $ # #------------------------------------------------------------------------- @@ -12,7 +12,7 @@ subdir = src/backend/optimizer/util top_builddir = ../../../.. include $(top_builddir)/src/Makefile.global -OBJS = clauses.o joininfo.o pathnode.o plancat.o predtest.o \ +OBJS = clauses.o joininfo.o pathnode.o placeholder.o plancat.o predtest.o \ relnode.o restrictinfo.o tlist.o var.o include $(top_srcdir)/src/backend/common.mk diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 14b9313a9a..c826ecb2ad 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.269 2008/10/09 19:27:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.270 2008/10/21 20:42:53 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -1188,6 +1188,12 @@ find_nonnullable_rels_walker(Node *node, bool top_level) result = find_nonnullable_rels_walker((Node *) expr->quals, top_level); } + else if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + result = find_nonnullable_rels_walker((Node *) phv->phexpr, top_level); + } return result; } @@ -1393,6 +1399,12 @@ find_nonnullable_vars_walker(Node *node, bool top_level) result = find_nonnullable_vars_walker((Node *) expr->quals, top_level); } + else if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + result = find_nonnullable_vars_walker((Node *) phv->phexpr, top_level); + } return result; } @@ -1921,6 +1933,7 @@ eval_const_expressions(PlannerInfo *root, Node *node) * constant. This effectively means that we plan using the first supplied * value of the Param. * 2. Fold stable, as well as immutable, functions to constants. + * 3. Reduce PlaceHolderVar nodes to their contained expressions. *-------------------- */ Node * @@ -2823,6 +2836,20 @@ eval_const_expressions_mutator(Node *node, newfslink->quals = quals; return (Node *) newfslink; } + if (IsA(node, PlaceHolderVar) && context->estimate) + { + /* + * In estimation mode, just strip the PlaceHolderVar node altogether; + * this amounts to estimating that the contained value won't be forced + * to null by an outer join. In regular mode we just use the default + * behavior (ie, simplify the expression but leave the PlaceHolderVar + * node intact). + */ + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + return eval_const_expressions_mutator((Node *) phv->phexpr, + context); + } /* * For any node type not handled above, we recurse using diff --git a/src/backend/optimizer/util/placeholder.c b/src/backend/optimizer/util/placeholder.c new file mode 100644 index 0000000000..aef212b360 --- /dev/null +++ b/src/backend/optimizer/util/placeholder.c @@ -0,0 +1,226 @@ +/*------------------------------------------------------------------------- + * + * placeholder.c + * PlaceHolderVar and PlaceHolderInfo manipulation routines + * + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/optimizer/util/placeholder.c,v 1.1 2008/10/21 20:42:53 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "nodes/nodeFuncs.h" +#include "optimizer/pathnode.h" +#include "optimizer/placeholder.h" +#include "optimizer/planmain.h" +#include "optimizer/var.h" +#include "utils/lsyscache.h" + + +/* + * make_placeholder_expr + * Make a PlaceHolderVar (and corresponding PlaceHolderInfo) + * for the given expression. + * + * phrels is the syntactic location (as a set of baserels) to attribute + * to the expression. + */ +PlaceHolderVar * +make_placeholder_expr(PlannerInfo *root, Expr *expr, Relids phrels) +{ + PlaceHolderVar *phv = makeNode(PlaceHolderVar); + PlaceHolderInfo *phinfo = makeNode(PlaceHolderInfo); + + phv->phexpr = expr; + phv->phrels = phrels; + phv->phid = ++(root->glob->lastPHId); + phv->phlevelsup = 0; + + phinfo->phid = phv->phid; + phinfo->ph_var = copyObject(phv); + phinfo->ph_eval_at = pull_varnos((Node *) phv); + /* ph_eval_at may change later, see fix_placeholder_eval_levels */ + phinfo->ph_needed = NULL; /* initially it's unused */ + /* for the moment, estimate width using just the datatype info */ + phinfo->ph_width = get_typavgwidth(exprType((Node *) expr), + exprTypmod((Node *) expr)); + + root->placeholder_list = lappend(root->placeholder_list, phinfo); + + return phv; +} + +/* + * find_placeholder_info + * Fetch the PlaceHolderInfo for the given PHV; error if not found + */ +PlaceHolderInfo * +find_placeholder_info(PlannerInfo *root, PlaceHolderVar *phv) +{ + ListCell *lc; + + /* if this ever isn't true, we'd need to be able to look in parent lists */ + Assert(phv->phlevelsup == 0); + + foreach(lc, root->placeholder_list) + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); + + if (phinfo->phid == phv->phid) + return phinfo; + } + elog(ERROR, "could not find PlaceHolderInfo with id %u", phv->phid); + return NULL; /* keep compiler quiet */ +} + +/* + * fix_placeholder_eval_levels + * Adjust the target evaluation levels for placeholders + * + * The initial eval_at level set by make_placeholder_expr was the set of + * rels used in the placeholder's expression (or the whole subselect if + * the expr is variable-free). If the subselect contains any outer joins + * that can null any of those rels, we must delay evaluation to above those + * joins. + * + * In future we might want to put additional policy/heuristics here to + * try to determine an optimal evaluation level. The current rules will + * result in evaluation at the lowest possible level. + */ +void +fix_placeholder_eval_levels(PlannerInfo *root) +{ + ListCell *lc1; + + foreach(lc1, root->placeholder_list) + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc1); + Relids syn_level = phinfo->ph_var->phrels; + Relids eval_at = phinfo->ph_eval_at; + BMS_Membership eval_membership; + bool found_some; + ListCell *lc2; + + /* + * Ignore unreferenced placeholders. Note: if a placeholder is + * referenced only by some other placeholder's expr, we will do + * the right things because the referencing placeholder must appear + * earlier in the list. + */ + if (bms_is_empty(phinfo->ph_needed)) + continue; + + /* + * Check for delays due to lower outer joins. This is the same logic + * as in check_outerjoin_delay in initsplan.c, except that we don't + * want to modify the delay_upper_joins flags; that was all handled + * already during distribute_qual_to_rels. + */ + do + { + found_some = false; + foreach(lc2, root->join_info_list) + { + SpecialJoinInfo *sjinfo = (SpecialJoinInfo *) lfirst(lc2); + + /* disregard joins not within the expr's sub-select */ + if (!bms_is_subset(sjinfo->syn_lefthand, syn_level) || + !bms_is_subset(sjinfo->syn_righthand, syn_level)) + continue; + + /* do we reference any nullable rels of this OJ? */ + if (bms_overlap(eval_at, sjinfo->min_righthand) || + (sjinfo->jointype == JOIN_FULL && + bms_overlap(eval_at, sjinfo->min_lefthand))) + { + /* yes; have we included all its rels in eval_at? */ + if (!bms_is_subset(sjinfo->min_lefthand, eval_at) || + !bms_is_subset(sjinfo->min_righthand, eval_at)) + { + /* no, so add them in */ + eval_at = bms_add_members(eval_at, + sjinfo->min_lefthand); + eval_at = bms_add_members(eval_at, + sjinfo->min_righthand); + /* we'll need another iteration */ + found_some = true; + } + } + } + } while (found_some); + + phinfo->ph_eval_at = eval_at; + + /* + * Now that we know where to evaluate the placeholder, make sure that + * any vars or placeholders it uses will be available at that join + * level. (Note that this has to be done within this loop to make + * sure we don't skip over such placeholders when we get to them.) + */ + eval_membership = bms_membership(eval_at); + if (eval_membership == BMS_MULTIPLE) + { + List *vars = pull_var_clause((Node *) phinfo->ph_var->phexpr, + true); + + add_vars_to_targetlist(root, vars, eval_at); + list_free(vars); + } + + /* + * Also, if the placeholder can be computed at a base rel and is + * needed above it, add it to that rel's targetlist. (This is + * essentially the same logic as in add_placeholders_to_joinrel, but + * we can't do that part until joinrels are formed.) + */ + if (eval_membership == BMS_SINGLETON) + { + int varno = bms_singleton_member(eval_at); + RelOptInfo *rel = find_base_rel(root, varno); + + if (bms_nonempty_difference(phinfo->ph_needed, rel->relids)) + rel->reltargetlist = lappend(rel->reltargetlist, + copyObject(phinfo->ph_var)); + } + } +} + +/* + * add_placeholders_to_joinrel + * Add any required PlaceHolderVars to a join rel's targetlist. + * + * A join rel should emit a PlaceHolderVar if (a) the PHV is needed above + * this join level and (b) the PHV can be computed at or below this level. + * At this time we do not need to distinguish whether the PHV will be + * computed here or copied up from below. + */ +void +add_placeholders_to_joinrel(PlannerInfo *root, RelOptInfo *joinrel) +{ + Relids relids = joinrel->relids; + ListCell *lc; + + foreach(lc, root->placeholder_list) + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) lfirst(lc); + + /* Is it still needed above this joinrel? */ + if (bms_nonempty_difference(phinfo->ph_needed, relids)) + { + /* Is it computable here? */ + if (bms_is_subset(phinfo->ph_eval_at, relids)) + { + /* Yup, add it to the output */ + joinrel->reltargetlist = lappend(joinrel->reltargetlist, + phinfo->ph_var); + joinrel->width += phinfo->ph_width; + } + } + } +} diff --git a/src/backend/optimizer/util/relnode.c b/src/backend/optimizer/util/relnode.c index 2fb6cd2efe..ed177fa929 100644 --- a/src/backend/optimizer/util/relnode.c +++ b/src/backend/optimizer/util/relnode.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.91 2008/10/04 21:56:53 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/relnode.c,v 1.92 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -17,6 +17,7 @@ #include "optimizer/cost.h" #include "optimizer/pathnode.h" #include "optimizer/paths.h" +#include "optimizer/placeholder.h" #include "optimizer/plancat.h" #include "optimizer/restrictinfo.h" #include "parser/parsetree.h" @@ -354,6 +355,7 @@ build_join_rel(PlannerInfo *root, */ build_joinrel_tlist(root, joinrel, outer_rel); build_joinrel_tlist(root, joinrel, inner_rel); + add_placeholders_to_joinrel(root, joinrel); /* * Construct restrict and join clause lists for the new joinrel. (The @@ -403,7 +405,8 @@ build_join_rel(PlannerInfo *root, /* * build_joinrel_tlist - * Builds a join relation's target list. + * Builds a join relation's target list from an input relation. + * (This is invoked twice to handle the two input relations.) * * The join's targetlist includes all Vars of its member relations that * will still be needed above the join. This subroutine adds all such @@ -421,16 +424,23 @@ build_joinrel_tlist(PlannerInfo *root, RelOptInfo *joinrel, foreach(vars, input_rel->reltargetlist) { - Var *origvar = (Var *) lfirst(vars); + Node *origvar = (Node *) lfirst(vars); Var *var; RelOptInfo *baserel; int ndx; + /* + * Ignore PlaceHolderVars in the input tlists; we'll make our + * own decisions about whether to copy them. + */ + if (IsA(origvar, PlaceHolderVar)) + continue; + /* * We can't run into any child RowExprs here, but we could find a * whole-row Var with a ConvertRowtypeExpr atop it. */ - var = origvar; + var = (Var *) origvar; while (!IsA(var, Var)) { if (IsA(var, ConvertRowtypeExpr)) diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 2fd16afb5f..968f4ae367 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.82 2008/08/25 22:42:33 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/tlist.c,v 1.83 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -91,7 +91,7 @@ tlist_member_ignore_relabel(Node *node, List *targetlist) List * flatten_tlist(List *tlist) { - List *vlist = pull_var_clause((Node *) tlist, false); + List *vlist = pull_var_clause((Node *) tlist, true); List *new_tlist; new_tlist = add_to_flat_tlist(NIL, vlist); @@ -104,7 +104,7 @@ flatten_tlist(List *tlist) * Add more vars to a flattened tlist (if they're not already in it) * * 'tlist' is the flattened tlist - * 'vars' is a list of var nodes + * 'vars' is a list of Var and/or PlaceHolderVar nodes * * Returns the extended tlist. */ @@ -116,9 +116,9 @@ add_to_flat_tlist(List *tlist, List *vars) foreach(v, vars) { - Var *var = (Var *) lfirst(v); + Node *var = (Node *) lfirst(v); - if (!tlist_member((Node *) var, tlist)) + if (!tlist_member(var, tlist)) { TargetEntry *tle; diff --git a/src/backend/optimizer/util/var.c b/src/backend/optimizer/util/var.c index 2e23091396..31749e46c0 100644 --- a/src/backend/optimizer/util/var.c +++ b/src/backend/optimizer/util/var.c @@ -3,12 +3,18 @@ * var.c * Var node manipulation routines * + * Note: for most purposes, PlaceHolderVar is considered a Var too, + * even if its contained expression is variable-free. Also, CurrentOfExpr + * is treated as a Var for purposes of determining whether an expression + * contains variables. + * + * * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.80 2008/10/06 17:39:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/var.c,v 1.81 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -50,7 +56,7 @@ typedef struct typedef struct { List *varlist; - bool includeUpperVars; + bool includePlaceHolderVars; } pull_var_clause_context; typedef struct @@ -128,6 +134,26 @@ pull_varnos_walker(Node *node, pull_varnos_context *context) context->varnos = bms_add_member(context->varnos, cexpr->cvarno); return false; } + if (IsA(node, PlaceHolderVar)) + { + /* + * Normally, we can just take the varnos in the contained expression. + * But if it is variable-free, use the PHV's syntactic relids. + */ + PlaceHolderVar *phv = (PlaceHolderVar *) node; + pull_varnos_context subcontext; + + subcontext.varnos = NULL; + subcontext.sublevels_up = context->sublevels_up; + (void) pull_varnos_walker((Node *) phv->phexpr, &subcontext); + + if (bms_is_empty(subcontext.varnos) && + phv->phlevelsup == context->sublevels_up) + context->varnos = bms_add_members(context->varnos, phv->phrels); + else + context->varnos = bms_join(context->varnos, subcontext.varnos); + return false; + } if (IsA(node, Query)) { /* Recurse into RTE subquery or not-yet-planned sublink subquery */ @@ -215,6 +241,12 @@ contain_var_clause_walker(Node *node, void *context) } if (IsA(node, CurrentOfExpr)) return true; + if (IsA(node, PlaceHolderVar)) + { + if (((PlaceHolderVar *) node)->phlevelsup == 0) + return true; /* abort the tree traversal and return true */ + /* else fall through to check the contained expr */ + } return expression_tree_walker(node, contain_var_clause_walker, context); } @@ -256,6 +288,12 @@ contain_vars_of_level_walker(Node *node, int *sublevels_up) return true; return false; } + if (IsA(node, PlaceHolderVar)) + { + if (((PlaceHolderVar *) node)->phlevelsup == *sublevels_up) + return true; /* abort the tree traversal and return true */ + /* else fall through to check the contained expr */ + } if (IsA(node, Query)) { /* Recurse into subselects */ @@ -329,6 +367,7 @@ locate_var_of_level_walker(Node *node, /* since CurrentOfExpr doesn't carry location, nothing we can do */ return false; } + /* No extra code needed for PlaceHolderVar; just look in contained expr */ if (IsA(node, Query)) { /* Recurse into subselects */ @@ -398,6 +437,7 @@ locate_var_of_relation_walker(Node *node, /* since CurrentOfExpr doesn't carry location, nothing we can do */ return false; } + /* No extra code needed for PlaceHolderVar; just look in contained expr */ if (IsA(node, Query)) { /* Recurse into subselects */ @@ -527,6 +567,30 @@ find_minimum_var_level_walker(Node *node, } } } + /* Likewise, make sure PlaceHolderVar is treated correctly */ + if (IsA(node, PlaceHolderVar)) + { + int phlevelsup = ((PlaceHolderVar *) node)->phlevelsup; + + /* convert levelsup to frame of reference of original query */ + phlevelsup -= context->sublevels_up; + /* ignore local vars of subqueries */ + if (phlevelsup >= 0) + { + if (context->min_varlevel < 0 || + context->min_varlevel > phlevelsup) + { + context->min_varlevel = phlevelsup; + + /* + * As soon as we find a local variable, we can abort the tree + * traversal, since min_varlevel is then certainly 0. + */ + if (phlevelsup == 0) + return true; + } + } + } if (IsA(node, Query)) { /* Recurse into subselects */ @@ -548,25 +612,30 @@ find_minimum_var_level_walker(Node *node, /* * pull_var_clause - * Recursively pulls all var nodes from an expression clause. + * Recursively pulls all Var nodes from an expression clause. * - * Upper-level vars (with varlevelsup > 0) are included only - * if includeUpperVars is true. Most callers probably want - * to ignore upper-level vars. + * PlaceHolderVars are included too, if includePlaceHolderVars is true. + * If it isn't true, an error is thrown if any are found. + * Note that Vars within a PHV's expression are *not* included. * - * Returns list of varnodes found. Note the varnodes themselves are not + * CurrentOfExpr nodes are *not* included. + * + * Upper-level vars (with varlevelsup > 0) are not included. + * (These probably represent errors too, but we don't complain.) + * + * Returns list of nodes found. Note the nodes themselves are not * copied, only referenced. * * Does not examine subqueries, therefore must only be used after reduction * of sublinks to subplans! */ List * -pull_var_clause(Node *node, bool includeUpperVars) +pull_var_clause(Node *node, bool includePlaceHolderVars) { pull_var_clause_context context; context.varlist = NIL; - context.includeUpperVars = includeUpperVars; + context.includePlaceHolderVars = includePlaceHolderVars; pull_var_clause_walker(node, &context); return context.varlist; @@ -579,10 +648,19 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) return false; if (IsA(node, Var)) { - if (((Var *) node)->varlevelsup == 0 || context->includeUpperVars) + if (((Var *) node)->varlevelsup == 0) context->varlist = lappend(context->varlist, node); return false; } + if (IsA(node, PlaceHolderVar)) + { + if (!context->includePlaceHolderVars) + elog(ERROR, "PlaceHolderVar found where not expected"); + if (((PlaceHolderVar *) node)->phlevelsup == 0) + context->varlist = lappend(context->varlist, node); + /* we do NOT descend into the contained expression */ + return false; + } return expression_tree_walker(node, pull_var_clause_walker, (void *) context); } @@ -597,6 +675,9 @@ pull_var_clause_walker(Node *node, pull_var_clause_context *context) * 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. * + * This also adjusts relid sets found in some expression node types to + * substitute the contained base rels for any join relid. + * * NOTE: this is used on not-yet-planned expressions. We do not expect it * to be applied directly to a Query node. */ @@ -703,6 +784,40 @@ flatten_join_alias_vars_mutator(Node *node, } return (Node *) fslink; } + if (IsA(node, PlaceHolderVar)) + { + /* Copy the PlaceHolderVar node with correct mutation of subnodes */ + PlaceHolderVar *phv; + + phv = (PlaceHolderVar *) expression_tree_mutator(node, + flatten_join_alias_vars_mutator, + (void *) context); + /* now fix PlaceHolderVar's relid sets */ + if (phv->phlevelsup == context->sublevels_up) + { + phv->phrels = alias_relid_set(context->root, + phv->phrels); + } + return (Node *) phv; + } + if (IsA(node, PlaceHolderInfo)) + { + /* Copy the PlaceHolderInfo node with correct mutation of subnodes */ + PlaceHolderInfo *phinfo; + + phinfo = (PlaceHolderInfo *) expression_tree_mutator(node, + flatten_join_alias_vars_mutator, + (void *) context); + /* now fix PlaceHolderInfo's relid sets */ + if (context->sublevels_up == 0) + { + phinfo->ph_eval_at = alias_relid_set(context->root, + phinfo->ph_eval_at); + phinfo->ph_needed = alias_relid_set(context->root, + phinfo->ph_needed); + } + return (Node *) phinfo; + } if (IsA(node, Query)) { diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 2a9645bf42..8ab486bfc2 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.115 2008/10/06 17:39:26 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.116 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -276,6 +276,17 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) } /* fall through to examine children */ } + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + if (phv->phlevelsup == context->sublevels_up) + { + phv->phrels = offset_relid_set(phv->phrels, + context->offset); + } + /* fall through to examine children */ + } if (IsA(node, AppendRelInfo)) { AppendRelInfo *appinfo = (AppendRelInfo *) node; @@ -287,6 +298,19 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) } /* fall through to examine children */ } + if (IsA(node, PlaceHolderInfo)) + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node; + + if (context->sublevels_up == 0) + { + phinfo->ph_eval_at = offset_relid_set(phinfo->ph_eval_at, + context->offset); + phinfo->ph_needed = offset_relid_set(phinfo->ph_needed, + context->offset); + } + /* fall through to examine children */ + } if (IsA(node, Query)) { /* Recurse into subselects */ @@ -440,6 +464,18 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) } /* fall through to examine children */ } + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + if (phv->phlevelsup == context->sublevels_up) + { + phv->phrels = adjust_relid_set(phv->phrels, + context->rt_index, + context->new_index); + } + /* fall through to examine children */ + } if (IsA(node, AppendRelInfo)) { AppendRelInfo *appinfo = (AppendRelInfo *) node; @@ -453,6 +489,21 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) } /* fall through to examine children */ } + if (IsA(node, PlaceHolderInfo)) + { + PlaceHolderInfo *phinfo = (PlaceHolderInfo *) node; + + if (context->sublevels_up == 0) + { + phinfo->ph_eval_at = adjust_relid_set(phinfo->ph_eval_at, + context->rt_index, + context->new_index); + phinfo->ph_needed = adjust_relid_set(phinfo->ph_needed, + context->rt_index, + context->new_index); + } + /* fall through to examine children */ + } if (IsA(node, Query)) { /* Recurse into subselects */ @@ -585,6 +636,14 @@ IncrementVarSublevelsUp_walker(Node *node, agg->agglevelsup += context->delta_sublevels_up; /* fall through to recurse into argument */ } + if (IsA(node, PlaceHolderVar)) + { + PlaceHolderVar *phv = (PlaceHolderVar *) node; + + if (phv->phlevelsup >= context->min_sublevels_up) + phv->phlevelsup += context->delta_sublevels_up; + /* fall through to recurse into argument */ + } if (IsA(node, RangeTblEntry)) { RangeTblEntry *rte = (RangeTblEntry *) node; @@ -708,8 +767,10 @@ rangeTableEntry_used_walker(Node *node, } /* Shouldn't need to handle planner auxiliary nodes here */ Assert(!IsA(node, FlattenedSubLink)); + Assert(!IsA(node, PlaceHolderVar)); Assert(!IsA(node, SpecialJoinInfo)); Assert(!IsA(node, AppendRelInfo)); + Assert(!IsA(node, PlaceHolderInfo)); if (IsA(node, Query)) { diff --git a/src/backend/utils/adt/selfuncs.c b/src/backend/utils/adt/selfuncs.c index fc9853a43a..b659013bde 100644 --- a/src/backend/utils/adt/selfuncs.c +++ b/src/backend/utils/adt/selfuncs.c @@ -15,7 +15,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.255 2008/09/28 20:42:12 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/selfuncs.c,v 1.256 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2951,7 +2951,7 @@ estimate_num_groups(PlannerInfo *root, List *groupExprs, double input_rows) /* * Else pull out the component Vars */ - varshere = pull_var_clause(groupexpr, false); + varshere = pull_var_clause(groupexpr, true); /* * If we find any variable-free GROUP BY item, then either it is a diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index 4aa5ce4345..ddd9934cf1 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.213 2008/10/04 21:56:55 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.214 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -213,8 +213,10 @@ typedef enum NodeTag T_RestrictInfo, T_InnerIndexscanInfo, T_FlattenedSubLink, + T_PlaceHolderVar, T_SpecialJoinInfo, T_AppendRelInfo, + T_PlaceHolderInfo, T_PlannerParamItem, /* diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h index 9934daa64a..b5eb00a7a6 100644 --- a/src/include/nodes/relation.h +++ b/src/include/nodes/relation.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.161 2008/10/17 20:23:45 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/relation.h,v 1.162 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -76,6 +76,8 @@ typedef struct PlannerGlobal List *invalItems; /* other dependencies, as PlanInvalItems */ + Index lastPHId; /* highest PlaceHolderVar ID assigned */ + bool transientPlan; /* redo plan when TransactionXmin changes? */ } PlannerGlobal; @@ -163,6 +165,8 @@ typedef struct PlannerInfo List *append_rel_list; /* list of AppendRelInfos */ + List *placeholder_list; /* list of PlaceHolderInfos */ + List *query_pathkeys; /* desired pathkeys for query_planner(), and * actual pathkeys afterwards */ @@ -243,11 +247,12 @@ typedef struct PlannerInfo * clauses have been applied (ie, output rows of a plan for it) * width - avg. number of bytes per tuple in the relation after the * appropriate projections have been done (ie, output width) - * reltargetlist - List of Var nodes for the attributes we need to - * output from this relation (in no particular order, - * but all rels of an appendrel set must use same order) + * reltargetlist - List of Var and PlaceHolderVar nodes for the values + * we need to output from this relation. + * List is in no particular order, but all rels of an + * appendrel set must use corresponding orders. * NOTE: in a child relation, may contain RowExpr or - * ConvertRowtypeExpr representing a whole-row Var + * ConvertRowtypeExpr representing a whole-row Var. * pathlist - List of Path nodes, one for each potentially useful * method of generating the relation * cheapest_startup_path - the pathlist member with lowest startup cost @@ -1089,6 +1094,29 @@ typedef struct FlattenedSubLink Expr *quals; /* join quals (in explicit-AND format) */ } FlattenedSubLink; +/* + * Placeholder node for an expression to be evaluated below the top level + * of a plan tree. This is used during planning to represent the contained + * expression. At the end of the planning process it is replaced by either + * the contained expression or a Var referring to a lower-level evaluation of + * the contained expression. Typically the evaluation occurs below an outer + * join, and Var references above the outer join might thereby yield NULL + * instead of the expression value. + * + * Although the planner treats this as an expression node type, it is not + * recognized by the parser or executor, so we declare it here rather than + * in primnodes.h. + */ + +typedef struct PlaceHolderVar +{ + Expr xpr; + Expr *phexpr; /* the represented expression */ + Relids phrels; /* base relids syntactically within expr src */ + Index phid; /* ID for PHV (unique within planner run) */ + Index phlevelsup; /* > 0 if PHV belongs to outer query */ +} PlaceHolderVar; + /* * "Special join" info. * @@ -1250,6 +1278,31 @@ typedef struct AppendRelInfo Oid parent_reloid; /* OID of parent relation */ } AppendRelInfo; +/* + * For each distinct placeholder expression generated during planning, we + * store a PlaceHolderInfo node in the PlannerInfo node's placeholder_list. + * This stores info that is needed centrally rather than in each copy of the + * PlaceHolderVar. The phid fields identify which PlaceHolderInfo goes with + * each PlaceHolderVar. Note that phid is unique throughout a planner run, + * not just within a query level --- this is so that we need not reassign ID's + * when pulling a subquery into its parent. + * + * The idea is to evaluate the expression at (only) the ph_eval_at join level, + * then allow it to bubble up like a Var until the ph_needed join level. + * ph_needed has the same definition as attr_needed for a regular Var. + */ + +typedef struct PlaceHolderInfo +{ + NodeTag type; + + Index phid; /* ID for PH (unique within planner run) */ + PlaceHolderVar *ph_var; /* copy of PlaceHolderVar tree */ + Relids ph_eval_at; /* lowest level we can evaluate value at */ + Relids ph_needed; /* highest level the value is needed at */ + int32 ph_width; /* estimated attribute width */ +} PlaceHolderInfo; + /* * glob->paramlist keeps track of the PARAM_EXEC slots that we have decided * we need for the query. At runtime these slots are used to pass values diff --git a/src/include/optimizer/placeholder.h b/src/include/optimizer/placeholder.h new file mode 100644 index 0000000000..4f39aedcfe --- /dev/null +++ b/src/include/optimizer/placeholder.h @@ -0,0 +1,28 @@ +/*------------------------------------------------------------------------- + * + * placeholder.h + * prototypes for optimizer/util/placeholder.c. + * + * + * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group + * Portions Copyright (c) 1994, Regents of the University of California + * + * $PostgreSQL: pgsql/src/include/optimizer/placeholder.h,v 1.1 2008/10/21 20:42:53 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PLACEHOLDER_H +#define PLACEHOLDER_H + +#include "nodes/relation.h" + + +extern PlaceHolderVar *make_placeholder_expr(PlannerInfo *root, Expr *expr, + Relids phrels); +extern PlaceHolderInfo *find_placeholder_info(PlannerInfo *root, + PlaceHolderVar *phv); +extern void fix_placeholder_eval_levels(PlannerInfo *root); +extern void add_placeholders_to_joinrel(PlannerInfo *root, + RelOptInfo *joinrel); + +#endif /* PLACEHOLDER_H */ diff --git a/src/include/optimizer/var.h b/src/include/optimizer/var.h index 4a8f84e553..c478259bcd 100644 --- a/src/include/optimizer/var.h +++ b/src/include/optimizer/var.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.38 2008/09/01 20:42:45 tgl Exp $ + * $PostgreSQL: pgsql/src/include/optimizer/var.h,v 1.39 2008/10/21 20:42:53 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,7 +24,7 @@ extern bool contain_vars_of_level(Node *node, int levelsup); extern int locate_var_of_level(Node *node, int levelsup); extern int locate_var_of_relation(Node *node, int relid, int levelsup); extern int find_minimum_var_level(Node *node); -extern List *pull_var_clause(Node *node, bool includeUpperVars); +extern List *pull_var_clause(Node *node, bool includePlaceHolderVars); extern Node *flatten_join_alias_vars(PlannerInfo *root, Node *node); #endif /* VAR_H */