*
* rewriteManip.c
*
- * Portions Copyright (c) 1996-2002, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.72 2003/06/06 15:04:02 tgl Exp $
+ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.108 2008/08/14 18:47:59 tgl Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "catalog/pg_type.h"
#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
-#include "optimizer/tlist.h"
+#include "parser/parse_coerce.h"
+#include "parser/parse_relation.h"
#include "parser/parsetree.h"
-#include "parser/parse_clause.h"
#include "rewrite/rewriteManip.h"
-#include "utils/lsyscache.h"
typedef struct
} checkExprHasAggs_context;
static bool checkExprHasAggs_walker(Node *node,
- checkExprHasAggs_context *context);
+ checkExprHasAggs_context *context);
static bool checkExprHasSubLink_walker(Node *node, void *context);
static Relids offset_relid_set(Relids relids, int offset);
static Relids adjust_relid_set(Relids relids, int oldrelid, int newrelid);
/*
* checkExprHasAggs -
- * Queries marked hasAggs might not have them any longer after
- * rewriting. Check it.
+ * Check if an expression contains an aggregate function call.
*
* The objective of this routine is to detect whether there are aggregates
* belonging to the initial query level. Aggregates belonging to subqueries
checkExprHasAggs_context context;
context.sublevels_up = 0;
+
/*
- * 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.
+ * 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.
*/
return query_or_expression_tree_walker(node,
checkExprHasAggs_walker,
if (IsA(node, Aggref))
{
if (((Aggref *) node)->agglevelsup == context->sublevels_up)
- return true; /* abort the tree traversal and return
- * true */
+ return true; /* abort the tree traversal and return true */
/* else fall through to examine argument */
}
if (IsA(node, Query))
/*
* checkExprHasSubLink -
- * Queries marked hasSubLinks might not have them any longer after
- * rewriting. Check it.
+ * Check if an expression contains a SubLink.
*/
bool
checkExprHasSubLink(Node *node)
{
/*
- * If a Query is passed, examine it --- but we will not recurse into
+ * If a Query is passed, examine it --- but we need not recurse into
* sub-Queries.
*/
return query_or_expression_tree_walker(node,
if (node == NULL)
return false;
if (IsA(node, SubLink))
- return true; /* abort the tree traversal and return
- * true */
+ return true; /* abort the tree traversal and return true */
return expression_tree_walker(node, checkExprHasSubLink_walker, context);
}
}
return false;
}
+ if (IsA(node, CurrentOfExpr))
+ {
+ CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+
+ if (context->sublevels_up == 0)
+ cexpr->cvarno += context->offset;
+ return false;
+ }
if (IsA(node, RangeTblRef))
{
RangeTblRef *rtr = (RangeTblRef *) node;
j->rtindex += context->offset;
/* fall through to examine children */
}
- if (IsA(node, InClauseInfo))
+ if (IsA(node, FlattenedSubLink))
{
- InClauseInfo *ininfo = (InClauseInfo *) node;
+ FlattenedSubLink *fslink = (FlattenedSubLink *) node;
if (context->sublevels_up == 0)
{
- ininfo->lefthand = offset_relid_set(ininfo->lefthand,
+ fslink->lefthand = offset_relid_set(fslink->lefthand,
context->offset);
- ininfo->righthand = offset_relid_set(ininfo->righthand,
+ fslink->righthand = offset_relid_set(fslink->righthand,
context->offset);
}
/* fall through to examine children */
}
+ if (IsA(node, AppendRelInfo))
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+ if (context->sublevels_up == 0)
+ {
+ appinfo->parent_relid += context->offset;
+ appinfo->child_relid += context->offset;
+ }
+ /* fall through to examine children */
+ }
if (IsA(node, Query))
{
/* Recurse into subselects */
context.sublevels_up = sublevels_up;
/*
- * Must be prepared to start with a Query or a bare expression tree;
- * if it's a Query, go straight to query_tree_walker to make sure that
+ * Must be prepared to start with a Query or a bare expression tree; if
+ * it's a Query, go straight to query_tree_walker to make sure that
* sublevels_up doesn't get incremented prematurely.
*/
if (node && IsA(node, Query))
{
Query *qry = (Query *) node;
- List *l;
/*
- * If we are starting at a Query, and sublevels_up is zero, then
- * we must also fix rangetable indexes in the Query itself ---
- * namely resultRelation and rowMarks entries. sublevels_up
- * cannot be zero when recursing into a subquery, so there's no
- * need to have the same logic inside OffsetVarNodes_walker.
+ * If we are starting at a Query, and sublevels_up is zero, then we
+ * must also fix rangetable indexes in the Query itself --- namely
+ * resultRelation and rowMarks entries. sublevels_up cannot be zero
+ * when recursing into a subquery, so there's no need to have the same
+ * logic inside OffsetVarNodes_walker.
*/
if (sublevels_up == 0)
{
+ ListCell *l;
+
if (qry->resultRelation)
qry->resultRelation += offset;
foreach(l, qry->rowMarks)
- lfirsti(l) += offset;
+ {
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+ rc->rti += offset;
+ }
}
query_tree_walker(qry, OffsetVarNodes_walker,
(void *) &context, 0);
tmprelids = bms_copy(relids);
while ((rtindex = bms_first_member(tmprelids)) >= 0)
- {
result = bms_add_member(result, rtindex + offset);
- }
bms_free(tmprelids);
return result;
}
}
return false;
}
+ if (IsA(node, CurrentOfExpr))
+ {
+ CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+
+ if (context->sublevels_up == 0 &&
+ cexpr->cvarno == context->rt_index)
+ cexpr->cvarno = context->new_index;
+ return false;
+ }
if (IsA(node, RangeTblRef))
{
RangeTblRef *rtr = (RangeTblRef *) node;
j->rtindex = context->new_index;
/* fall through to examine children */
}
- if (IsA(node, InClauseInfo))
+ if (IsA(node, FlattenedSubLink))
{
- InClauseInfo *ininfo = (InClauseInfo *) node;
+ FlattenedSubLink *fslink = (FlattenedSubLink *) node;
if (context->sublevels_up == 0)
{
- ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
+ fslink->lefthand = adjust_relid_set(fslink->lefthand,
context->rt_index,
context->new_index);
- ininfo->righthand = adjust_relid_set(ininfo->righthand,
+ fslink->righthand = adjust_relid_set(fslink->righthand,
context->rt_index,
context->new_index);
}
/* fall through to examine children */
}
+ if (IsA(node, AppendRelInfo))
+ {
+ AppendRelInfo *appinfo = (AppendRelInfo *) node;
+
+ if (context->sublevels_up == 0)
+ {
+ if (appinfo->parent_relid == context->rt_index)
+ appinfo->parent_relid = context->new_index;
+ if (appinfo->child_relid == context->rt_index)
+ appinfo->child_relid = context->new_index;
+ }
+ /* fall through to examine children */
+ }
if (IsA(node, Query))
{
/* Recurse into subselects */
context.sublevels_up = sublevels_up;
/*
- * Must be prepared to start with a Query or a bare expression tree;
- * if it's a Query, go straight to query_tree_walker to make sure that
+ * Must be prepared to start with a Query or a bare expression tree; if
+ * it's a Query, go straight to query_tree_walker to make sure that
* sublevels_up doesn't get incremented prematurely.
*/
if (node && IsA(node, Query))
{
Query *qry = (Query *) node;
- List *l;
/*
- * If we are starting at a Query, and sublevels_up is zero, then
- * we must also fix rangetable indexes in the Query itself ---
- * namely resultRelation and rowMarks entries. sublevels_up
- * cannot be zero when recursing into a subquery, so there's no
- * need to have the same logic inside ChangeVarNodes_walker.
+ * If we are starting at a Query, and sublevels_up is zero, then we
+ * must also fix rangetable indexes in the Query itself --- namely
+ * resultRelation and rowMarks entries. sublevels_up cannot be zero
+ * when recursing into a subquery, so there's no need to have the same
+ * logic inside ChangeVarNodes_walker.
*/
if (sublevels_up == 0)
{
+ ListCell *l;
+
if (qry->resultRelation == rt_index)
qry->resultRelation = new_index;
foreach(l, qry->rowMarks)
{
- if (lfirsti(l) == rt_index)
- lfirsti(l) = new_index;
+ RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+
+ if (rc->rti == rt_index)
+ rc->rti = new_index;
}
}
query_tree_walker(qry, ChangeVarNodes_walker,
var->varlevelsup += context->delta_sublevels_up;
return false; /* done here */
}
+ if (IsA(node, CurrentOfExpr))
+ {
+ /* this should not happen */
+ if (context->min_sublevels_up == 0)
+ elog(ERROR, "cannot push down CurrentOfExpr");
+ return false;
+ }
if (IsA(node, Aggref))
{
Aggref *agg = (Aggref *) node;
context.min_sublevels_up = min_sublevels_up;
/*
- * 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.
+ * 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.
*/
query_or_expression_tree_walker(node,
IncrementVarSublevelsUp_walker,
return true;
return false;
}
+ if (IsA(node, CurrentOfExpr))
+ {
+ CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+
+ if (context->sublevels_up == 0 &&
+ cexpr->cvarno == context->rt_index)
+ return true;
+ return false;
+ }
if (IsA(node, RangeTblRef))
{
RangeTblRef *rtr = (RangeTblRef *) node;
return true;
/* fall through to examine children */
}
- if (IsA(node, InClauseInfo))
- {
- InClauseInfo *ininfo = (InClauseInfo *) node;
+ /* Shouldn't need to handle planner auxiliary nodes here */
+ Assert(!IsA(node, FlattenedSubLink));
+ Assert(!IsA(node, SpecialJoinInfo));
+ Assert(!IsA(node, AppendRelInfo));
- if (context->sublevels_up == 0 &&
- (bms_is_member(context->rt_index, ininfo->lefthand) ||
- bms_is_member(context->rt_index, ininfo->righthand)))
- return true;
- /* fall through to examine children */
- }
if (IsA(node, Query))
{
/* Recurse into subselects */
context.sublevels_up = sublevels_up;
/*
- * 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.
+ * 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.
*/
return query_or_expression_tree_walker(node,
rangeTableEntry_used_walker,
context.sublevels_up = sublevels_up;
/*
- * 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.
+ * 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.
*/
return query_or_expression_tree_walker(node,
attribute_used_walker,
* query. If they're not there, it must be an INSERT/SELECT in which
* they've been pushed down to the SELECT.
*/
- if (length(parsetree->rtable) >= 2 &&
- strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
- "*OLD*") == 0 &&
- strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
- "*NEW*") == 0)
+ if (list_length(parsetree->rtable) >= 2 &&
+ strcmp(rt_fetch(PRS2_OLD_VARNO, parsetree->rtable)->eref->aliasname,
+ "*OLD*") == 0 &&
+ strcmp(rt_fetch(PRS2_NEW_VARNO, parsetree->rtable)->eref->aliasname,
+ "*NEW*") == 0)
return parsetree;
Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
- if (length(parsetree->jointree->fromlist) != 1)
- elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
- rtr = (RangeTblRef *) lfirst(parsetree->jointree->fromlist);
+ if (list_length(parsetree->jointree->fromlist) != 1)
+ elog(ERROR, "expected to find SELECT subquery");
+ rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
Assert(IsA(rtr, RangeTblRef));
selectrte = rt_fetch(rtr->rtindex, parsetree->rtable);
selectquery = selectrte->subquery;
if (!(selectquery && IsA(selectquery, Query) &&
selectquery->commandType == CMD_SELECT))
- elog(ERROR, "getInsertSelectQuery: expected to find SELECT subquery");
- if (length(selectquery->rtable) >= 2 &&
- strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
- "*OLD*") == 0 &&
- strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
- "*NEW*") == 0)
+ elog(ERROR, "expected to find SELECT subquery");
+ if (list_length(selectquery->rtable) >= 2 &&
+ strcmp(rt_fetch(PRS2_OLD_VARNO, selectquery->rtable)->eref->aliasname,
+ "*OLD*") == 0 &&
+ strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->aliasname,
+ "*NEW*") == 0)
{
if (subquery_ptr)
*subquery_ptr = &(selectrte->subquery);
return selectquery;
}
- elog(ERROR, "getInsertSelectQuery: can't find rule placeholders");
+ elog(ERROR, "could not find rule placeholders");
return NULL; /* not reached */
}
* There's noplace to put the qual on a utility statement.
*
* If it's a NOTIFY, silently ignore the qual; this means that the
- * NOTIFY will execute, whether or not there are any qualifying
- * rows. While clearly wrong, this is much more useful than
- * refusing to execute the rule at all, and extra NOTIFY events
- * are harmless for typical uses of NOTIFY.
+ * NOTIFY will execute, whether or not there are any qualifying rows.
+ * While clearly wrong, this is much more useful than refusing to
+ * execute the rule at all, and extra NOTIFY events are harmless for
+ * typical uses of NOTIFY.
*
* If it isn't a NOTIFY, error out, since unconditional execution of
- * other utility stmts is unlikely to be wanted. (This case is
- * not currently allowed anyway, but keep the test for safety.)
+ * other utility stmts is unlikely to be wanted. (This case is not
+ * currently allowed anyway, but keep the test for safety.)
*/
if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
return;
else
- elog(ERROR, "Conditional utility statements are not implemented");
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("conditional utility statements are not implemented")));
+ }
+
+ if (parsetree->setOperations != NULL)
+ {
+ /*
+ * There's noplace to put the qual on a setop statement, either. (This
+ * could be fixed, but right now the planner simply ignores any qual
+ * condition on a setop query.)
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
}
/* INTERSECT want's the original, but we need to copy - Jan */
copy);
/*
- * Make sure query is marked correctly if added qual has sublinks or
- * aggregates (not sure it can ever have aggs, but sublinks
- * definitely).
+ * We had better not have stuck an aggregate into the WHERE clause.
*/
- parsetree->hasAggs |= checkExprHasAggs(copy);
- parsetree->hasSubLinks |= checkExprHasSubLink(copy);
-}
-
-/*
- * Add the given havingQual to the one already contained in the parsetree
- * just as AddQual does for the normal 'where' qual
- */
-void
-AddHavingQual(Query *parsetree, Node *havingQual)
-{
- Node *copy;
-
- if (havingQual == NULL)
- return;
-
- if (parsetree->commandType == CMD_UTILITY)
- {
- /*
- * There's noplace to put the qual on a utility statement.
- *
- * See comments in AddQual for motivation.
- */
- if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
- return;
- else
- elog(ERROR, "Conditional utility statements are not implemented");
- }
-
- /* INTERSECT want's the original, but we need to copy - Jan */
- copy = copyObject(havingQual);
-
- parsetree->havingQual = make_and_qual(parsetree->havingQual,
- copy);
+ Assert(!checkExprHasAggs(copy));
/*
- * Make sure query is marked correctly if added qual has sublinks or
- * aggregates (not sure it can ever have aggs, but sublinks
- * definitely).
+ * Make sure query is marked correctly if added qual has sublinks. Need
+ * not search qual when query is already marked.
*/
- parsetree->hasAggs |= checkExprHasAggs(copy);
- parsetree->hasSubLinks |= checkExprHasSubLink(copy);
+ if (!parsetree->hasSubLinks)
+ parsetree->hasSubLinks = checkExprHasSubLink(copy);
}
}
-/* Find a targetlist entry by resno */
-static Node *
-FindMatchingNew(List *tlist, int attno)
-{
- List *i;
-
- foreach(i, tlist)
- {
- TargetEntry *tle = lfirst(i);
-
- if (tle->resdom->resno == attno)
- return (Node *) tle->expr;
- }
- return NULL;
-}
-
-#ifdef NOT_USED
-
-/* Find a targetlist entry by resname */
-static Node *
-FindMatchingTLEntry(List *tlist, char *e_attname)
-{
- List *i;
-
- foreach(i, tlist)
- {
- TargetEntry *tle = lfirst(i);
- char *resname;
-
- resname = tle->resdom->resname;
- if (strcmp(e_attname, resname) == 0)
- return tle->expr;
- }
- return NULL;
-}
-#endif
-
-
/*
* ResolveNew - replace Vars with corresponding items from a targetlist
*
* 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.
+ *
+ * Note: the business with inserted_sublink is needed to update hasSubLinks
+ * in subqueries when the replacement adds a subquery inside a subquery.
+ * Messy, isn't it? We do not need to do similar pushups for hasAggs,
+ * because it isn't possible for this transformation to insert a level-zero
+ * aggregate reference into a subquery --- it could only insert outer aggs.
*/
typedef struct
{
int target_varno;
int sublevels_up;
+ RangeTblEntry *target_rte;
List *targetlist;
int event;
int update_varno;
+ bool inserted_sublink;
} ResolveNew_context;
+static Node *
+resolve_one_var(Var *var, ResolveNew_context *context)
+{
+ TargetEntry *tle;
+
+ tle = get_tle_by_resno(context->targetlist, var->varattno);
+
+ if (tle == NULL)
+ {
+ /* 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;
+ }
+ 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,
+ false,
+ false);
+ }
+ }
+ 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;
+ }
+}
+
static Node *
ResolveNew_mutator(Node *node, ResolveNew_context *context)
{
if (this_varno == context->target_varno &&
this_varlevelsup == context->sublevels_up)
{
- Node *n;
-
- /* band-aid: don't do the wrong thing with a whole-tuple Var */
if (var->varattno == InvalidAttrNumber)
- elog(ERROR, "ResolveNew: can't handle whole-tuple reference");
-
- n = FindMatchingNew(context->targetlist, var->varattno);
-
- if (n == NULL)
- {
- if (context->event == CMD_UPDATE)
- {
- /* For update, just change unmatched var's varno */
- var = (Var *) copyObject(node);
- var->varno = context->update_varno;
- var->varnoold = context->update_varno;
- return (Node *) var;
- }
- else
- {
- /* Otherwise replace unmatched var with a null */
- return (Node *) makeNullConst(var->vartype);
- }
- }
- else
{
- /* Make a copy of the tlist item to return */
- n = copyObject(n);
- /* Adjust varlevelsup if tlist item is from higher query */
- if (this_varlevelsup > 0)
- IncrementVarSublevelsUp(n, this_varlevelsup, 0);
- return n;
+ /* Must expand whole-tuple reference into RowExpr */
+ RowExpr *rowexpr;
+ 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.
+ */
+ expandRTE(context->target_rte,
+ this_varno, this_varlevelsup,
+ (var->vartype != RECORDOID),
+ NULL, &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;
+
+ return (Node *) rowexpr;
}
+
+ /* Normal case for scalar variable */
+ return resolve_one_var(var, context);
}
/* otherwise fall through to copy the var normally */
}
+ else if (IsA(node, CurrentOfExpr))
+ {
+ CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
+ int this_varno = (int) cexpr->cvarno;
- if (IsA(node, Query))
+ if (this_varno == context->target_varno &&
+ context->sublevels_up == 0)
+ {
+ /*
+ * We get here if a WHERE CURRENT OF expression turns out to apply
+ * to a view. Someday we might be able to translate the
+ * expression to apply to an underlying table of the view, but
+ * right now it's not implemented.
+ */
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("WHERE CURRENT OF on a view is not implemented")));
+ }
+ /* otherwise fall through to copy the expr normally */
+ }
+ else if (IsA(node, Query))
{
/* Recurse into RTE subquery or not-yet-planned sublink subquery */
Query *newnode;
+ bool save_inserted_sublink;
context->sublevels_up++;
+ save_inserted_sublink = context->inserted_sublink;
+ context->inserted_sublink = false;
newnode = query_tree_mutator((Query *) node,
ResolveNew_mutator,
(void *) context,
0);
+ newnode->hasSubLinks |= context->inserted_sublink;
+ context->inserted_sublink = save_inserted_sublink;
context->sublevels_up--;
return (Node *) newnode;
}
Node *
ResolveNew(Node *node, int target_varno, int sublevels_up,
+ RangeTblEntry *target_rte,
List *targetlist, int event, int update_varno)
{
+ 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.
+ * 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.
*/
- return query_or_expression_tree_mutator(node,
- ResolveNew_mutator,
- (void *) &context,
- 0);
-}
-
-
-#ifdef NOT_USED
-
-/*
- * HandleRIRAttributeRule
- * Replace Vars matching a given RT index with copies of TL expressions.
- *
- * Handles 'on retrieve to relation.attribute
- * do instead retrieve (attribute = expression) w/qual'
- */
-
-typedef struct
-{
- List *rtable;
- List *targetlist;
- int rt_index;
- int attr_num;
- int *modified;
- int *badsql;
- int sublevels_up;
-} HandleRIRAttributeRule_context;
-
-static Node *
-HandleRIRAttributeRule_mutator(Node *node,
- HandleRIRAttributeRule_context * context)
-{
- if (node == NULL)
- return NULL;
- if (IsA(node, Var))
- {
- Var *var = (Var *) node;
- int this_varno = var->varno;
- int this_varattno = var->varattno;
- int this_varlevelsup = var->varlevelsup;
-
- if (this_varno == context->rt_index &&
- this_varattno == context->attr_num &&
- this_varlevelsup == context->sublevels_up)
- {
- if (var->vartype == 32)
- { /* HACK: disallow SET variables */
- *context->modified = TRUE;
- *context->badsql = TRUE;
- return (Node *) makeNullConst(var->vartype);
- }
- else
- {
- char *name_to_look_for;
-
- name_to_look_for = get_attname(getrelid(this_varno,
- context->rtable),
- this_varattno);
- if (name_to_look_for)
- {
- Node *n;
-
- *context->modified = TRUE;
- n = FindMatchingTLEntry(context->targetlist,
- name_to_look_for);
- if (n == NULL)
- return (Node *) makeNullConst(var->vartype);
- /* Make a copy of the tlist item to return */
- n = copyObject(n);
-
- /*
- * Adjust varlevelsup if tlist item is from higher
- * query
- */
- if (this_varlevelsup > 0)
- IncrementVarSublevelsUp(n, this_varlevelsup, 0);
- return n;
- }
- }
- }
- /* otherwise fall through to copy the var normally */
- }
+ result = query_or_expression_tree_mutator(node,
+ ResolveNew_mutator,
+ (void *) &context,
+ 0);
- if (IsA(node, Query))
+ if (context.inserted_sublink)
{
- /* Recurse into RTE subquery or not-yet-planned sublink subquery */
- Query *newnode;
+ if (IsA(result, Query))
+ ((Query *) result)->hasSubLinks = true;
- context->sublevels_up++;
- newnode = query_tree_mutator((Query *) node,
- HandleRIRAttributeRule_mutator,
- (void *) context,
- 0);
- context->sublevels_up--;
- return (Node *) newnode;
+ /*
+ * 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 expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
- (void *) context);
-}
-
-void
-HandleRIRAttributeRule(Query *parsetree,
- List *rtable,
- List *targetlist,
- int rt_index,
- int attr_num,
- int *modified,
- int *badsql)
-{
- HandleRIRAttributeRule_context context;
- context.rtable = rtable;
- context.targetlist = targetlist;
- context.rt_index = rt_index;
- context.attr_num = attr_num;
- context.modified = modified;
- context.badsql = badsql;
- context.sublevels_up = 0;
-
- query_tree_mutator(parsetree,
- HandleRIRAttributeRule_mutator,
- (void *) &context,
- QTW_DONT_COPY_QUERY);
+ return result;
}
-
-#endif /* NOT_USED */