aboutsummaryrefslogtreecommitdiff
path: root/src/backend/rewrite/rewriteManip.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/rewrite/rewriteManip.c')
-rw-r--r--src/backend/rewrite/rewriteManip.c306
1 files changed, 176 insertions, 130 deletions
diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c
index 570434ed83e..d3dad8ed8d5 100644
--- a/src/backend/rewrite/rewriteManip.c
+++ b/src/backend/rewrite/rewriteManip.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.122 2009/06/11 14:49:01 momjian Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.122.2.1 2009/09/02 17:52:33 tgl Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1067,16 +1067,14 @@ AddInvertedQual(Query *parsetree, Node *qual)
/*
- * ResolveNew - replace Vars with corresponding items from a targetlist
- *
- * Vars matching target_varno and sublevels_up are replaced by the
- * entry with matching resno from targetlist, if there is one.
- * If not, we either change the unmatched Var's varno to update_varno
- * (when event == CMD_UPDATE) or replace it with a constant NULL.
+ * replace_rte_variables() finds all Vars in an expression tree
+ * that reference a particular RTE, and replaces them with substitute
+ * expressions obtained from a caller-supplied callback function.
*
- * The caller must also provide target_rte, the RTE describing the target
- * relation. This is needed to handle whole-row Vars referencing the target.
- * We expand such Vars into RowExpr constructs.
+ * When invoking replace_rte_variables on a portion of a Query, pass the
+ * address of the containing Query's hasSubLinks field as outer_hasSubLinks.
+ * Otherwise, pass NULL, but inserting a SubLink into a non-Query expression
+ * will then cause an error.
*
* Note: the business with inserted_sublink is needed to update hasSubLinks
* in subqueries when the replacement adds a subquery inside a subquery.
@@ -1084,122 +1082,88 @@ AddInvertedQual(Query *parsetree, Node *qual)
* because it isn't possible for this transformation to insert a level-zero
* aggregate reference into a subquery --- it could only insert outer aggs.
* Likewise for hasWindowFuncs.
+ *
+ * Note: usually, we'd not expose the mutator function or context struct
+ * for a function like this. We do so because callbacks often find it
+ * convenient to recurse directly to the mutator on sub-expressions of
+ * what they will return.
*/
-
-typedef struct
+Node *
+replace_rte_variables(Node *node, int target_varno, int sublevels_up,
+ replace_rte_variables_callback callback,
+ void *callback_arg,
+ bool *outer_hasSubLinks)
{
- int target_varno;
- int sublevels_up;
- RangeTblEntry *target_rte;
- List *targetlist;
- int event;
- int update_varno;
- bool inserted_sublink;
-} ResolveNew_context;
+ Node *result;
+ replace_rte_variables_context context;
-static Node *
-resolve_one_var(Var *var, ResolveNew_context *context)
-{
- TargetEntry *tle;
+ context.callback = callback;
+ context.callback_arg = callback_arg;
+ context.target_varno = target_varno;
+ context.sublevels_up = sublevels_up;
- tle = get_tle_by_resno(context->targetlist, var->varattno);
+ /*
+ * We try to initialize inserted_sublink to true if there is no need to
+ * detect new sublinks because the query already has some.
+ */
+ if (node && IsA(node, Query))
+ context.inserted_sublink = ((Query *) node)->hasSubLinks;
+ else if (outer_hasSubLinks)
+ context.inserted_sublink = *outer_hasSubLinks;
+ else
+ context.inserted_sublink = false;
- if (tle == NULL)
+ /*
+ * Must be prepared to start with a Query or a bare expression tree; if
+ * it's a Query, we don't want to increment sublevels_up.
+ */
+ result = query_or_expression_tree_mutator(node,
+ replace_rte_variables_mutator,
+ (void *) &context,
+ 0);
+
+ if (context.inserted_sublink)
{
- /* Failed to find column in insert/update tlist */
- if (context->event == CMD_UPDATE)
- {
- /* For update, just change unmatched var's varno */
- var = (Var *) copyObject(var);
- var->varno = context->update_varno;
- var->varnoold = context->update_varno;
- return (Node *) var;
- }
+ if (result && IsA(result, Query))
+ ((Query *) result)->hasSubLinks = true;
+ else if (outer_hasSubLinks)
+ *outer_hasSubLinks = true;
else
- {
- /* Otherwise replace unmatched var with a null */
- /* need coerce_to_domain in case of NOT NULL domain constraint */
- return coerce_to_domain((Node *) makeNullConst(var->vartype,
- var->vartypmod),
- InvalidOid, -1,
- var->vartype,
- COERCE_IMPLICIT_CAST,
- -1,
- false,
- false);
- }
+ elog(ERROR, "replace_rte_variables inserted a SubLink, but has noplace to record it");
}
- else
- {
- /* Make a copy of the tlist item to return */
- Node *n = copyObject(tle->expr);
- /* Adjust varlevelsup if tlist item is from higher query */
- if (var->varlevelsup > 0)
- IncrementVarSublevelsUp(n, var->varlevelsup, 0);
- /* Report it if we are adding a sublink to query */
- if (!context->inserted_sublink)
- context->inserted_sublink = checkExprHasSubLink(n);
- return n;
- }
+ return result;
}
-static Node *
-ResolveNew_mutator(Node *node, ResolveNew_context *context)
+Node *
+replace_rte_variables_mutator(Node *node,
+ replace_rte_variables_context *context)
{
if (node == NULL)
return NULL;
if (IsA(node, Var))
{
Var *var = (Var *) node;
- int this_varno = (int) var->varno;
- int this_varlevelsup = (int) var->varlevelsup;
- if (this_varno == context->target_varno &&
- this_varlevelsup == context->sublevels_up)
+ if (var->varno == context->target_varno &&
+ var->varlevelsup == context->sublevels_up)
{
- if (var->varattno == InvalidAttrNumber)
- {
- /* Must expand whole-tuple reference into RowExpr */
- RowExpr *rowexpr;
- List *colnames;
- List *fields;
-
- /*
- * If generating an expansion for a var of a named rowtype
- * (ie, this is a plain relation RTE), then we must include
- * dummy items for dropped columns. If the var is RECORD (ie,
- * this is a JOIN), then omit dropped columns. Either way,
- * attach column names to the RowExpr for use of ruleutils.c.
- */
- expandRTE(context->target_rte,
- this_varno, this_varlevelsup, var->location,
- (var->vartype != RECORDOID),
- &colnames, &fields);
- /* Adjust the generated per-field Vars... */
- fields = (List *) ResolveNew_mutator((Node *) fields,
- context);
- rowexpr = makeNode(RowExpr);
- rowexpr->args = fields;
- rowexpr->row_typeid = var->vartype;
- rowexpr->row_format = COERCE_IMPLICIT_CAST;
- rowexpr->colnames = colnames;
- rowexpr->location = -1;
-
- return (Node *) rowexpr;
- }
-
- /* Normal case for scalar variable */
- return resolve_one_var(var, context);
+ /* Found a matching variable, make the substitution */
+ Node *newnode;
+
+ newnode = (*context->callback) (var, context);
+ /* Detect if we are adding a sublink to query */
+ if (!context->inserted_sublink)
+ context->inserted_sublink = checkExprHasSubLink(newnode);
+ return newnode;
}
/* otherwise fall through to copy the var normally */
}
else if (IsA(node, CurrentOfExpr))
{
CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
- int this_varno = (int) cexpr->cvarno;
- if (this_varno == context->target_varno &&
+ if (cexpr->cvarno == context->target_varno &&
context->sublevels_up == 0)
{
/*
@@ -1222,9 +1186,9 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
context->sublevels_up++;
save_inserted_sublink = context->inserted_sublink;
- context->inserted_sublink = false;
+ context->inserted_sublink = ((Query *) node)->hasSubLinks;
newnode = query_tree_mutator((Query *) node,
- ResolveNew_mutator,
+ replace_rte_variables_mutator,
(void *) context,
0);
newnode->hasSubLinks |= context->inserted_sublink;
@@ -1232,46 +1196,128 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
context->sublevels_up--;
return (Node *) newnode;
}
- return expression_tree_mutator(node, ResolveNew_mutator,
+ return expression_tree_mutator(node, replace_rte_variables_mutator,
(void *) context);
}
+
+/*
+ * ResolveNew - replace Vars with corresponding items from a targetlist
+ *
+ * Vars matching target_varno and sublevels_up are replaced by the
+ * entry with matching resno from targetlist, if there is one.
+ * If not, we either change the unmatched Var's varno to update_varno
+ * (when event == CMD_UPDATE) or replace it with a constant NULL.
+ *
+ * The caller must also provide target_rte, the RTE describing the target
+ * relation. This is needed to handle whole-row Vars referencing the target.
+ * We expand such Vars into RowExpr constructs.
+ *
+ * outer_hasSubLinks works the same as for replace_rte_variables().
+ */
+
+typedef struct
+{
+ RangeTblEntry *target_rte;
+ List *targetlist;
+ int event;
+ int update_varno;
+} ResolveNew_context;
+
+static Node *
+ResolveNew_callback(Var *var,
+ replace_rte_variables_context *context)
+{
+ ResolveNew_context *rcon = (ResolveNew_context *) context->callback_arg;
+ TargetEntry *tle;
+
+ if (var->varattno == InvalidAttrNumber)
+ {
+ /* Must expand whole-tuple reference into RowExpr */
+ RowExpr *rowexpr;
+ List *colnames;
+ List *fields;
+
+ /*
+ * If generating an expansion for a var of a named rowtype
+ * (ie, this is a plain relation RTE), then we must include
+ * dummy items for dropped columns. If the var is RECORD (ie,
+ * this is a JOIN), then omit dropped columns. Either way,
+ * attach column names to the RowExpr for use of ruleutils.c.
+ */
+ expandRTE(rcon->target_rte,
+ var->varno, var->varlevelsup, var->location,
+ (var->vartype != RECORDOID),
+ &colnames, &fields);
+ /* Adjust the generated per-field Vars... */
+ fields = (List *) replace_rte_variables_mutator((Node *) fields,
+ context);
+ rowexpr = makeNode(RowExpr);
+ rowexpr->args = fields;
+ rowexpr->row_typeid = var->vartype;
+ rowexpr->row_format = COERCE_IMPLICIT_CAST;
+ rowexpr->colnames = colnames;
+ rowexpr->location = var->location;
+
+ return (Node *) rowexpr;
+ }
+
+ /* Normal case referencing one targetlist element */
+ tle = get_tle_by_resno(rcon->targetlist, var->varattno);
+
+ if (tle == NULL)
+ {
+ /* Failed to find column in insert/update tlist */
+ if (rcon->event == CMD_UPDATE)
+ {
+ /* For update, just change unmatched var's varno */
+ var = (Var *) copyObject(var);
+ var->varno = rcon->update_varno;
+ var->varnoold = rcon->update_varno;
+ return (Node *) var;
+ }
+ else
+ {
+ /* Otherwise replace unmatched var with a null */
+ /* need coerce_to_domain in case of NOT NULL domain constraint */
+ return coerce_to_domain((Node *) makeNullConst(var->vartype,
+ var->vartypmod),
+ InvalidOid, -1,
+ var->vartype,
+ COERCE_IMPLICIT_CAST,
+ -1,
+ false,
+ false);
+ }
+ }
+ else
+ {
+ /* Make a copy of the tlist item to return */
+ Node *newnode = copyObject(tle->expr);
+
+ /* Must adjust varlevelsup if tlist item is from higher query */
+ if (var->varlevelsup > 0)
+ IncrementVarSublevelsUp(newnode, var->varlevelsup, 0);
+
+ return newnode;
+ }
+}
+
Node *
ResolveNew(Node *node, int target_varno, int sublevels_up,
RangeTblEntry *target_rte,
- List *targetlist, int event, int update_varno)
+ List *targetlist, int event, int update_varno,
+ bool *outer_hasSubLinks)
{
- Node *result;
ResolveNew_context context;
- context.target_varno = target_varno;
- context.sublevels_up = sublevels_up;
context.target_rte = target_rte;
context.targetlist = targetlist;
context.event = event;
context.update_varno = update_varno;
- context.inserted_sublink = false;
-
- /*
- * Must be prepared to start with a Query or a bare expression tree; if
- * it's a Query, we don't want to increment sublevels_up.
- */
- result = query_or_expression_tree_mutator(node,
- ResolveNew_mutator,
- (void *) &context,
- 0);
- if (context.inserted_sublink)
- {
- if (IsA(result, Query))
- ((Query *) result)->hasSubLinks = true;
-
- /*
- * Note: if we're called on a non-Query node then it's the caller's
- * responsibility to update hasSubLinks in the ancestor Query. This is
- * pretty fragile and perhaps should be rethought ...
- */
- }
-
- return result;
+ return replace_rte_variables(node, target_varno, sublevels_up,
+ ResolveNew_callback,
+ (void *) &context,
+ outer_hasSubLinks);
}