Add a concept of "placeholder" variables to the planner. These are variables

that represent some expression that we desire to compute below the top level
of the plan, and then let that value "bubble up" as though it were a plain
Var (ie, a column value).

The immediate application is to allow sub-selects to be flattened even when
they are below an outer join and have non-nullable output expressions.
Formerly we couldn't flatten because such an expression wouldn't properly
go to NULL when evaluated above the outer join.  Now, we wrap it in a
PlaceHolderVar and arrange for the actual evaluation to occur below the outer
join.  When the resulting Var bubbles up through the join, it will be set to
NULL if necessary, yielding the correct results.  This fixes a planner
limitation that's existed since 7.1.

In future we might want to use this mechanism to re-introduce some form of
Hellerstein's "expensive functions" optimization, ie place the evaluation of
an expensive function at the most suitable point in the plan tree.
This commit is contained in:
Tom Lane 2008-10-21 20:42:53 +00:00
parent 831abae506
commit e6ae3b5dbf
28 changed files with 1135 additions and 226 deletions

View File

@ -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

View File

@ -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:

View File

@ -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));

View File

@ -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;

View File

@ -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 */

View File

@ -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;

View File

@ -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);

View File

@ -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))

View File

@ -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);

View File

@ -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.
*/

View File

@ -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 */

View File

@ -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))

View File

@ -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;

View File

@ -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);
}

View File

@ -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))

View File

@ -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));

View File

@ -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

View File

@ -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

View File

@ -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;
}
}
}
}

View File

@ -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))

View File

@ -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;

View File

@ -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))
{

View File

@ -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))
{

View File

@ -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

View File

@ -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,
/*

View File

@ -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

View File

@ -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 */

View File

@ -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 */