int num_vars; /* number of plain Var tlist 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 */
+ tlist_vinfo vars[FLEXIBLE_ARRAY_MEMBER]; /* has num_vars entries */
+} indexed_tlist;
typedef struct
{
SubqueryScan *plan,
int rtoffset);
static bool trivial_subqueryscan(SubqueryScan *plan);
+static void set_foreignscan_references(PlannerInfo *root,
+ ForeignScan *fscan,
+ int rtoffset);
+static void set_customscan_references(PlannerInfo *root,
+ CustomScan *cscan,
+ int rtoffset);
static Node *fix_scan_expr(PlannerInfo *root, Node *node, int rtoffset);
static Node *fix_scan_expr_mutator(Node *node, fix_scan_expr_context *context);
static bool fix_scan_expr_walker(Node *node, fix_scan_expr_context *context);
static bool extract_query_dependencies_walker(Node *node,
PlannerInfo *context);
-
/*****************************************************************************
*
* SUBPLAN REFERENCES
* Currently, relations and user-defined functions are the only types of
* objects that are explicitly tracked this way.
*
+ * 7. We assign every plan node in the tree a unique ID.
+ *
* We also perform one final optimization step, which is to delete
* SubqueryScan plan nodes that aren't doing anything useful (ie, have
* no qual and a no-op targetlist). The reason for doing this last is that
*
* In the flat rangetable, we zero out substructure pointers that are not
* needed by the executor; this reduces the storage space and copying cost
- * for cached plans. We keep only the alias and eref Alias fields, which
- * are needed by EXPLAIN, and the selectedCols and modifiedCols bitmaps,
- * which are needed for executor-startup permissions checking and for
- * trigger event checking.
+ * for cached plans. We keep only the ctename, alias and eref Alias fields,
+ * which are needed by EXPLAIN, and the selectedCols, insertedCols and
+ * updatedCols bitmaps, which are needed for executor-startup permissions
+ * checking and for trigger event checking.
*/
static void
add_rte_to_flat_rtable(PlannerGlobal *glob, RangeTblEntry *rte)
memcpy(newrte, rte, sizeof(RangeTblEntry));
/* zap unneeded sub-structure */
+ newrte->tablesample = NULL;
newrte->subquery = NULL;
newrte->joinaliasvars = NIL;
newrte->functions = NIL;
newrte->ctecoltypes = NIL;
newrte->ctecoltypmods = NIL;
newrte->ctecolcollations = NIL;
+ newrte->securityQuals = NIL;
glob->finalrtable = lappend(glob->finalrtable, newrte);
if (plan == NULL)
return NULL;
+ /* Assign this node a unique ID. */
+ plan->plan_node_id = root->glob->lastPlanNodeId++;
+
/*
* Plan-type-specific fixes
*/
fix_scan_list(root, splan->plan.qual, rtoffset);
}
break;
+ case T_SampleScan:
+ {
+ SampleScan *splan = (SampleScan *) plan;
+
+ splan->scan.scanrelid += rtoffset;
+ splan->scan.plan.targetlist =
+ fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
+ splan->scan.plan.qual =
+ fix_scan_list(root, splan->scan.plan.qual, rtoffset);
+ splan->tablesample = (TableSampleClause *)
+ fix_scan_expr(root, (Node *) splan->tablesample, rtoffset);
+ }
+ break;
case T_IndexScan:
{
IndexScan *splan = (IndexScan *) plan;
}
break;
case T_ForeignScan:
- {
- ForeignScan *splan = (ForeignScan *) plan;
-
- splan->scan.scanrelid += rtoffset;
- splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
- splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
- splan->fdw_exprs =
- fix_scan_list(root, splan->fdw_exprs, rtoffset);
- }
+ set_foreignscan_references(root, (ForeignScan *) plan, rtoffset);
break;
-
case T_CustomScan:
- {
- CustomScan *splan = (CustomScan *) plan;
-
- splan->scan.scanrelid += rtoffset;
- splan->scan.plan.targetlist =
- fix_scan_list(root, splan->scan.plan.targetlist, rtoffset);
- splan->scan.plan.qual =
- fix_scan_list(root, splan->scan.plan.qual, rtoffset);
- splan->custom_exprs =
- fix_scan_list(root, splan->custom_exprs, rtoffset);
- }
+ set_customscan_references(root, (CustomScan *) plan, rtoffset);
break;
case T_NestLoop:
case T_Sort:
case T_Unique:
case T_SetOp:
+ case T_Gather:
/*
* These plan types don't actually bother to evaluate their
}
break;
case T_Agg:
+ set_upper_references(root, plan, rtoffset);
+ break;
case T_Group:
set_upper_references(root, plan, rtoffset);
break;
splan->plan.targetlist = copyObject(linitial(newRL));
}
+ /*
+ * We treat ModifyTable with ON CONFLICT as a form of 'pseudo
+ * join', where the inner side is the EXCLUDED tuple.
+ * Therefore use fix_join_expr to setup the relevant variables
+ * to INNER_VAR. We explicitly don't create any OUTER_VARs as
+ * those are already used by RETURNING and it seems better to
+ * be non-conflicting.
+ */
+ if (splan->onConflictSet)
+ {
+ indexed_tlist *itlist;
+
+ itlist = build_tlist_index(splan->exclRelTlist);
+
+ splan->onConflictSet =
+ fix_join_expr(root, splan->onConflictSet,
+ NULL, itlist,
+ linitial_int(splan->resultRelations),
+ rtoffset);
+
+ splan->onConflictWhere = (Node *)
+ fix_join_expr(root, (List *) splan->onConflictWhere,
+ NULL, itlist,
+ linitial_int(splan->resultRelations),
+ rtoffset);
+
+ pfree(itlist);
+
+ splan->exclRelTlist =
+ fix_scan_list(root, splan->exclRelTlist, rtoffset);
+ }
+
+ splan->nominalRelation += rtoffset;
+ splan->exclRelRTI += rtoffset;
+
foreach(l, splan->resultRelations)
{
lfirst_int(l) += rtoffset;
return true;
}
+/*
+ * set_foreignscan_references
+ * Do set_plan_references processing on a ForeignScan
+ */
+static void
+set_foreignscan_references(PlannerInfo *root,
+ ForeignScan *fscan,
+ int rtoffset)
+{
+ /* Adjust scanrelid if it's valid */
+ if (fscan->scan.scanrelid > 0)
+ fscan->scan.scanrelid += rtoffset;
+
+ if (fscan->fdw_scan_tlist != NIL || fscan->scan.scanrelid == 0)
+ {
+ /* Adjust tlist, qual, fdw_exprs to reference foreign scan tuple */
+ indexed_tlist *itlist = build_tlist_index(fscan->fdw_scan_tlist);
+
+ fscan->scan.plan.targetlist = (List *)
+ fix_upper_expr(root,
+ (Node *) fscan->scan.plan.targetlist,
+ itlist,
+ INDEX_VAR,
+ rtoffset);
+ fscan->scan.plan.qual = (List *)
+ fix_upper_expr(root,
+ (Node *) fscan->scan.plan.qual,
+ itlist,
+ INDEX_VAR,
+ rtoffset);
+ fscan->fdw_exprs = (List *)
+ fix_upper_expr(root,
+ (Node *) fscan->fdw_exprs,
+ itlist,
+ INDEX_VAR,
+ rtoffset);
+ pfree(itlist);
+ /* fdw_scan_tlist itself just needs fix_scan_list() adjustments */
+ fscan->fdw_scan_tlist =
+ fix_scan_list(root, fscan->fdw_scan_tlist, rtoffset);
+ }
+ else
+ {
+ /* Adjust tlist, qual, fdw_exprs in the standard way */
+ fscan->scan.plan.targetlist =
+ fix_scan_list(root, fscan->scan.plan.targetlist, rtoffset);
+ fscan->scan.plan.qual =
+ fix_scan_list(root, fscan->scan.plan.qual, rtoffset);
+ fscan->fdw_exprs =
+ fix_scan_list(root, fscan->fdw_exprs, rtoffset);
+ }
+
+ /* Adjust fs_relids if needed */
+ if (rtoffset > 0)
+ {
+ Bitmapset *tempset = NULL;
+ int x = -1;
+
+ while ((x = bms_next_member(fscan->fs_relids, x)) >= 0)
+ tempset = bms_add_member(tempset, x + rtoffset);
+ fscan->fs_relids = tempset;
+ }
+}
+
+/*
+ * set_customscan_references
+ * Do set_plan_references processing on a CustomScan
+ */
+static void
+set_customscan_references(PlannerInfo *root,
+ CustomScan *cscan,
+ int rtoffset)
+{
+ ListCell *lc;
+
+ /* Adjust scanrelid if it's valid */
+ if (cscan->scan.scanrelid > 0)
+ cscan->scan.scanrelid += rtoffset;
+
+ if (cscan->custom_scan_tlist != NIL || cscan->scan.scanrelid == 0)
+ {
+ /* Adjust tlist, qual, custom_exprs to reference custom scan tuple */
+ indexed_tlist *itlist = build_tlist_index(cscan->custom_scan_tlist);
+
+ cscan->scan.plan.targetlist = (List *)
+ fix_upper_expr(root,
+ (Node *) cscan->scan.plan.targetlist,
+ itlist,
+ INDEX_VAR,
+ rtoffset);
+ cscan->scan.plan.qual = (List *)
+ fix_upper_expr(root,
+ (Node *) cscan->scan.plan.qual,
+ itlist,
+ INDEX_VAR,
+ rtoffset);
+ cscan->custom_exprs = (List *)
+ fix_upper_expr(root,
+ (Node *) cscan->custom_exprs,
+ itlist,
+ INDEX_VAR,
+ rtoffset);
+ pfree(itlist);
+ /* custom_scan_tlist itself just needs fix_scan_list() adjustments */
+ cscan->custom_scan_tlist =
+ fix_scan_list(root, cscan->custom_scan_tlist, rtoffset);
+ }
+ else
+ {
+ /* Adjust tlist, qual, custom_exprs in the standard way */
+ cscan->scan.plan.targetlist =
+ fix_scan_list(root, cscan->scan.plan.targetlist, rtoffset);
+ cscan->scan.plan.qual =
+ fix_scan_list(root, cscan->scan.plan.qual, rtoffset);
+ cscan->custom_exprs =
+ fix_scan_list(root, cscan->custom_exprs, rtoffset);
+ }
+
+ /* Adjust child plan-nodes recursively, if needed */
+ foreach(lc, cscan->custom_plans)
+ {
+ lfirst(lc) = set_plan_refs(root, (Plan *) lfirst(lc), rtoffset);
+ }
+
+ /* Adjust custom_relids if needed */
+ if (rtoffset > 0)
+ {
+ Bitmapset *tempset = NULL;
+ int x = -1;
+
+ while ((x = bms_next_member(cscan->custom_relids, x)) >= 0)
+ tempset = bms_add_member(tempset, x + rtoffset);
+ cscan->custom_relids = tempset;
+ }
+}
+
/*
* copyVar
* Copy a Var node.
* This is code that is common to all variants of expression-fixing.
* We must look up operator opcode info for OpExpr and related nodes,
* add OIDs from regclass Const nodes into root->glob->relationOids, and
- * add catalog TIDs for user-defined functions into root->glob->invalItems.
+ * add PlanInvalItems for user-defined functions into root->glob->invalItems.
+ * We also fill in column index lists for GROUPING() expressions.
*
* We assume it's okay to update opcode info in-place. So this could possibly
* scribble on the planner's input data structures, but it's OK.
lappend_oid(root->glob->relationOids,
DatumGetObjectId(con->constvalue));
}
+ else if (IsA(node, GroupingFunc))
+ {
+ GroupingFunc *g = (GroupingFunc *) node;
+ AttrNumber *grouping_map = root->grouping_map;
+
+ /* If there are no grouping sets, we don't need this. */
+
+ Assert(grouping_map || g->cols == NIL);
+
+ if (grouping_map)
+ {
+ ListCell *lc;
+ List *cols = NIL;
+
+ foreach(lc, g->refs)
+ {
+ cols = lappend_int(cols, grouping_map[lfirst_int(lc)]);
+ }
+
+ Assert(!g->cols || equal(cols, g->cols));
+
+ if (!g->cols)
+ g->cols = cols;
+ }
+ }
}
/*
* subplans, by setting the varnos to OUTER_VAR or INNER_VAR and setting
* attno values to the result domain number of either the corresponding
* outer or inner join tuple item. Also perform opcode lookup for these
- * expressions. and add regclass OIDs to root->glob->relationOids.
+ * expressions, and add regclass OIDs to root->glob->relationOids.
*/
static void
set_join_references(PlannerInfo *root, Join *join, int rtoffset)
outer_itlist = build_tlist_index(outer_plan->targetlist);
inner_itlist = build_tlist_index(inner_plan->targetlist);
- /* All join plans have tlist, qual, and joinqual */
- join->plan.targetlist = fix_join_expr(root,
- join->plan.targetlist,
- outer_itlist,
- inner_itlist,
- (Index) 0,
- rtoffset);
- join->plan.qual = fix_join_expr(root,
- join->plan.qual,
- outer_itlist,
- inner_itlist,
- (Index) 0,
- rtoffset);
+ /*
+ * First process the joinquals (including merge or hash clauses). These
+ * are logically below the join so they can always use all values
+ * available from the input tlists. It's okay to also handle
+ * NestLoopParams now, because those couldn't refer to nullable
+ * subexpressions.
+ */
join->joinqual = fix_join_expr(root,
join->joinqual,
outer_itlist,
rtoffset);
}
+ /*
+ * Now we need to fix up the targetlist and qpqual, which are logically
+ * above the join. This means they should not re-use any input expression
+ * that was computed in the nullable side of an outer join. Vars and
+ * PlaceHolderVars are fine, so we can implement this restriction just by
+ * clearing has_non_vars in the indexed_tlist structs.
+ *
+ * XXX This is a grotty workaround for the fact that we don't clearly
+ * distinguish between a Var appearing below an outer join and the "same"
+ * Var appearing above it. If we did, we'd not need to hack the matching
+ * rules this way.
+ */
+ switch (join->jointype)
+ {
+ case JOIN_LEFT:
+ case JOIN_SEMI:
+ case JOIN_ANTI:
+ inner_itlist->has_non_vars = false;
+ break;
+ case JOIN_RIGHT:
+ outer_itlist->has_non_vars = false;
+ break;
+ case JOIN_FULL:
+ outer_itlist->has_non_vars = false;
+ inner_itlist->has_non_vars = false;
+ break;
+ default:
+ break;
+ }
+
+ join->plan.targetlist = fix_join_expr(root,
+ join->plan.targetlist,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
+ join->plan.qual = fix_join_expr(root,
+ join->plan.qual,
+ outer_itlist,
+ inner_itlist,
+ (Index) 0,
+ rtoffset);
+
pfree(outer_itlist);
pfree(inner_itlist);
}
* If no match, return NULL.
*
* NOTE: it is a waste of time to call this unless itlist->has_ph_vars or
- * itlist->has_non_vars
+ * itlist->has_non_vars. Furthermore, set_join_references() relies on being
+ * able to prevent matching of non-Vars by clearing itlist->has_non_vars,
+ * so there's a correctness reason not to call it unless that's set.
*/
static Var *
search_indexed_tlist_for_non_var(Node *node,
* inner_itlist = NULL and acceptable_rel = the ID of the target relation.
*
* 'clauses' is the targetlist or list of join clauses
- * 'outer_itlist' is the indexed target list of the outer join relation
+ * 'outer_itlist' is the indexed target list of the outer join relation,
+ * or NULL
* 'inner_itlist' is the indexed target list of the inner join relation,
* or NULL
* 'acceptable_rel' is either zero or the rangetable index of a relation
Var *var = (Var *) node;
/* First look for the var in the input tlists */
- newvar = search_indexed_tlist_for_var(var,
- context->outer_itlist,
- OUTER_VAR,
- context->rtoffset);
- if (newvar)
- return (Node *) newvar;
+ if (context->outer_itlist)
+ {
+ newvar = search_indexed_tlist_for_var(var,
+ context->outer_itlist,
+ OUTER_VAR,
+ context->rtoffset);
+ if (newvar)
+ return (Node *) newvar;
+ }
+
+ /* Then in the outer */
if (context->inner_itlist)
{
newvar = search_indexed_tlist_for_var(var,
PlaceHolderVar *phv = (PlaceHolderVar *) node;
/* See if the PlaceHolderVar has bubbled up from a lower plan node */
- if (context->outer_itlist->has_ph_vars)
+ if (context->outer_itlist && context->outer_itlist->has_ph_vars)
{
newvar = search_indexed_tlist_for_non_var((Node *) phv,
context->outer_itlist,
if (IsA(node, Param))
return fix_param_node(context->root, (Param *) node);
/* Try matching more complex expressions too, if tlists have any */
- if (context->outer_itlist->has_non_vars)
+ if (context->outer_itlist && context->outer_itlist->has_non_vars)
{
newvar = search_indexed_tlist_for_non_var(node,
context->outer_itlist,
return rlist;
}
+
/*****************************************************************************
* OPERATOR REGPROC LOOKUP
*****************************************************************************/