*
* rewriteManip.c
*
- * Copyright (c) 1994, Regents of the University of California
+ * Portions Copyright (c) 1996-2003, 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.40 1999/08/25 23:21:43 tgl Exp $
+ * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteManip.c,v 1.76 2003/08/04 02:40:03 momjian Exp $
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
+#include "nodes/makefuncs.h"
#include "optimizer/clauses.h"
+#include "optimizer/tlist.h"
#include "parser/parsetree.h"
#include "parser/parse_clause.h"
#include "rewrite/rewriteManip.h"
-#include "utils/builtins.h"
#include "utils/lsyscache.h"
-static void ResolveNew(RewriteInfo *info, List *targetlist,
- Node **node, int sublevels_up);
-
-/*
- * OffsetVarnodes -
- */
-void
-OffsetVarNodes(Node *node, int offset, int sublevels_up)
+typedef struct
{
- if (node == NULL)
- return;
+ int sublevels_up;
+} checkExprHasAggs_context;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- OffsetVarNodes(
- (Node *) (tle->expr),
- offset,
- sublevels_up);
- }
- break;
-
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
+static bool checkExprHasAggs_walker(Node *node,
+ 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);
- OffsetVarNodes(
- (Node *) (aggref->target),
- offset,
- sublevels_up);
- }
- break;
- case T_GroupClause:
- break;
+/*
+ * checkExprHasAggs -
+ * Queries marked hasAggs might not have them any longer after
+ * rewriting. Check it.
+ *
+ * The objective of this routine is to detect whether there are aggregates
+ * belonging to the initial query level. Aggregates belonging to subqueries
+ * or outer queries do NOT cause a true result. We must recurse into
+ * subqueries to detect outer-reference aggregates that logically belong to
+ * the initial query level.
+ */
+bool
+checkExprHasAggs(Node *node)
+{
+ checkExprHasAggs_context context;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
+ context.sublevels_up = 0;
- OffsetVarNodes(
- (Node *) (exp->args),
- offset,
- sublevels_up);
- }
- break;
+ /*
+ * 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,
+ (void *) &context,
+ 0);
+}
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
+static bool
+checkExprHasAggs_walker(Node *node, checkExprHasAggs_context * context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Aggref))
+ {
+ if (((Aggref *) node)->agglevelsup == context->sublevels_up)
+ return true; /* abort the tree traversal and return
+ * true */
+ /* else fall through to examine argument */
+ }
+ if (IsA(node, Query))
+ {
+ /* Recurse into subselects */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node,
+ checkExprHasAggs_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, checkExprHasAggs_walker,
+ (void *) context);
+}
- OffsetVarNodes(
- (Node *) (iter->iterexpr),
- offset,
- sublevels_up);
- }
- break;
+/*
+ * checkExprHasSubLink -
+ * Queries marked hasSubLinks might not have them any longer after
+ * rewriting. Check it.
+ */
+bool
+checkExprHasSubLink(Node *node)
+{
+ /*
+ * If a Query is passed, examine it --- but we will not recurse into
+ * sub-Queries.
+ */
+ return query_or_expression_tree_walker(node,
+ checkExprHasSubLink_walker,
+ NULL,
+ QTW_IGNORE_RT_SUBQUERIES);
+}
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
-
- OffsetVarNodes(
- (Node *) (ref->refupperindexpr),
- offset,
- sublevels_up);
- OffsetVarNodes(
- (Node *) (ref->reflowerindexpr),
- offset,
- sublevels_up);
- OffsetVarNodes(
- (Node *) (ref->refexpr),
- offset,
- sublevels_up);
- OffsetVarNodes(
- (Node *) (ref->refassgnexpr),
- offset,
- sublevels_up);
- }
- break;
+static bool
+checkExprHasSubLink_walker(Node *node, void *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, SubLink))
+ return true; /* abort the tree traversal and return
+ * true */
+ return expression_tree_walker(node, checkExprHasSubLink_walker, context);
+}
- case T_Var:
- {
- Var *var = (Var *) node;
- if (var->varlevelsup == sublevels_up)
- {
- var->varno += offset;
- var->varnoold += offset;
- }
- }
- break;
+/*
+ * OffsetVarNodes - adjust Vars when appending one query's RT to another
+ *
+ * Find all Var nodes in the given tree with varlevelsup == sublevels_up,
+ * and increment their varno fields (rangetable indexes) by 'offset'.
+ * The varnoold fields are adjusted similarly. Also, adjust other nodes
+ * that contain rangetable indexes, such as RangeTblRef and JoinExpr.
+ *
+ * NOTE: although this has the form of a walker, we cheat and modify the
+ * nodes in-place. The given expression tree should have been copied
+ * earlier to ensure that no unwanted side-effects occur!
+ */
- case T_Param:
- break;
+typedef struct
+{
+ int offset;
+ int sublevels_up;
+} OffsetVarNodes_context;
- case T_Const:
- break;
+static bool
+OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
- case T_List:
- {
- List *l;
+ if (var->varlevelsup == context->sublevels_up)
+ {
+ var->varno += context->offset;
+ var->varnoold += context->offset;
+ }
+ return false;
+ }
+ if (IsA(node, RangeTblRef))
+ {
+ RangeTblRef *rtr = (RangeTblRef *) node;
- foreach(l, (List *) node)
- OffsetVarNodes(
- (Node *) lfirst(l),
- offset,
- sublevels_up);
- }
- break;
+ if (context->sublevels_up == 0)
+ rtr->rtindex += context->offset;
+ /* the subquery itself is visited separately */
+ return false;
+ }
+ if (IsA(node, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) node;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
+ if (context->sublevels_up == 0)
+ j->rtindex += context->offset;
+ /* fall through to examine children */
+ }
+ if (IsA(node, InClauseInfo))
+ {
+ InClauseInfo *ininfo = (InClauseInfo *) node;
- OffsetVarNodes(
- (Node *) (sub->lefthand),
- offset,
- sublevels_up);
+ if (context->sublevels_up == 0)
+ {
+ ininfo->lefthand = offset_relid_set(ininfo->lefthand,
+ context->offset);
+ ininfo->righthand = offset_relid_set(ininfo->righthand,
+ context->offset);
+ }
+ /* fall through to examine children */
+ }
+ if (IsA(node, Query))
+ {
+ /* Recurse into subselects */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node, OffsetVarNodes_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, OffsetVarNodes_walker,
+ (void *) context);
+}
- OffsetVarNodes(
- (Node *) (sub->subselect),
- offset,
- sublevels_up + 1);
- }
- break;
+void
+OffsetVarNodes(Node *node, int offset, int sublevels_up)
+{
+ OffsetVarNodes_context context;
- case T_Query:
- {
- Query *qry = (Query *) node;
-
- OffsetVarNodes(
- (Node *) (qry->targetList),
- offset,
- sublevels_up);
-
- OffsetVarNodes(
- (Node *) (qry->qual),
- offset,
- sublevels_up);
-
- OffsetVarNodes(
- (Node *) (qry->havingQual),
- offset,
- sublevels_up);
- }
- break;
+ context.offset = offset;
+ context.sublevels_up = sublevels_up;
- case T_CaseExpr:
- {
- CaseExpr *exp = (CaseExpr *) node;
+ /*
+ * 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 (sublevels_up == 0)
+ {
+ if (qry->resultRelation)
+ qry->resultRelation += offset;
+ foreach(l, qry->rowMarks)
+ lfirsti(l) += offset;
+ }
+ query_tree_walker(qry, OffsetVarNodes_walker,
+ (void *) &context, 0);
+ }
+ else
+ OffsetVarNodes_walker(node, &context);
+}
- OffsetVarNodes(
- (Node *) (exp->args),
- offset,
- sublevels_up);
+static Relids
+offset_relid_set(Relids relids, int offset)
+{
+ Relids result = NULL;
+ Relids tmprelids;
+ int rtindex;
+
+ tmprelids = bms_copy(relids);
+ while ((rtindex = bms_first_member(tmprelids)) >= 0)
+ result = bms_add_member(result, rtindex + offset);
+ bms_free(tmprelids);
+ return result;
+}
- OffsetVarNodes(
- (Node *) (exp->defresult),
- offset,
- sublevels_up);
- }
- break;
+/*
+ * ChangeVarNodes - adjust Var nodes for a specific change of RT index
+ *
+ * Find all Var nodes in the given tree belonging to a specific relation
+ * (identified by sublevels_up and rt_index), and change their varno fields
+ * to 'new_index'. The varnoold fields are changed too. Also, adjust other
+ * nodes that contain rangetable indexes, such as RangeTblRef and JoinExpr.
+ *
+ * NOTE: although this has the form of a walker, we cheat and modify the
+ * nodes in-place. The given expression tree should have been copied
+ * earlier to ensure that no unwanted side-effects occur!
+ */
- case T_CaseWhen:
- {
- CaseWhen *exp = (CaseWhen *) node;
+typedef struct
+{
+ int rt_index;
+ int new_index;
+ int sublevels_up;
+} ChangeVarNodes_context;
- OffsetVarNodes(
- (Node *) (exp->expr),
- offset,
- sublevels_up);
+static bool
+ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
- OffsetVarNodes(
- (Node *) (exp->result),
- offset,
- sublevels_up);
- }
- break;
+ if (var->varlevelsup == context->sublevels_up &&
+ var->varno == context->rt_index)
+ {
+ var->varno = context->new_index;
+ var->varnoold = context->new_index;
+ }
+ return false;
+ }
+ if (IsA(node, RangeTblRef))
+ {
+ RangeTblRef *rtr = (RangeTblRef *) node;
- default:
- elog(NOTICE, "unknown node tag %d in OffsetVarNodes()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
+ if (context->sublevels_up == 0 &&
+ rtr->rtindex == context->rt_index)
+ rtr->rtindex = context->new_index;
+ /* the subquery itself is visited separately */
+ return false;
+ }
+ if (IsA(node, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) node;
+ if (context->sublevels_up == 0 &&
+ j->rtindex == context->rt_index)
+ j->rtindex = context->new_index;
+ /* fall through to examine children */
+ }
+ if (IsA(node, InClauseInfo))
+ {
+ InClauseInfo *ininfo = (InClauseInfo *) node;
+ if (context->sublevels_up == 0)
+ {
+ ininfo->lefthand = adjust_relid_set(ininfo->lefthand,
+ context->rt_index,
+ context->new_index);
+ ininfo->righthand = adjust_relid_set(ininfo->righthand,
+ context->rt_index,
+ context->new_index);
+ }
+ /* fall through to examine children */
}
+ if (IsA(node, Query))
+ {
+ /* Recurse into subselects */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node, ChangeVarNodes_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, ChangeVarNodes_walker,
+ (void *) context);
}
-
-/*
- * ChangeVarNodes -
- */
void
ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
{
- if (node == NULL)
- return;
+ ChangeVarNodes_context context;
+
+ context.rt_index = rt_index;
+ context.new_index = new_index;
+ context.sublevels_up = sublevels_up;
- switch (nodeTag(node))
+ /*
+ * 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))
{
- case T_TargetEntry:
+ 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 (sublevels_up == 0)
+ {
+ if (qry->resultRelation == rt_index)
+ qry->resultRelation = new_index;
+ foreach(l, qry->rowMarks)
{
- TargetEntry *tle = (TargetEntry *) node;
-
- ChangeVarNodes(
- (Node *) (tle->expr),
- rt_index,
- new_index,
- sublevels_up);
+ if (lfirsti(l) == rt_index)
+ lfirsti(l) = new_index;
}
- break;
+ }
+ query_tree_walker(qry, ChangeVarNodes_walker,
+ (void *) &context, 0);
+ }
+ else
+ ChangeVarNodes_walker(node, &context);
+}
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
+/*
+ * Substitute newrelid for oldrelid in a Relid set
+ */
+static Relids
+adjust_relid_set(Relids relids, int oldrelid, int newrelid)
+{
+ if (bms_is_member(oldrelid, relids))
+ {
+ /* Ensure we have a modifiable copy */
+ relids = bms_copy(relids);
+ /* Remove old, add new */
+ relids = bms_del_member(relids, oldrelid);
+ relids = bms_add_member(relids, newrelid);
+ }
+ return relids;
+}
- ChangeVarNodes(
- (Node *) (aggref->target),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
+/*
+ * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree
+ *
+ * Find all Var nodes in the given tree having varlevelsup >= min_sublevels_up,
+ * and add delta_sublevels_up to their varlevelsup value. This is needed when
+ * an expression that's correct for some nesting level is inserted into a
+ * subquery. Ordinarily the initial call has min_sublevels_up == 0 so that
+ * all Vars are affected. The point of min_sublevels_up is that we can
+ * increment it when we recurse into a sublink, so that local variables in
+ * that sublink are not affected, only outer references to vars that belong
+ * to the expression's original query level or parents thereof.
+ *
+ * Aggref nodes are adjusted similarly.
+ *
+ * NOTE: although this has the form of a walker, we cheat and modify the
+ * Var nodes in-place. The given expression tree should have been copied
+ * earlier to ensure that no unwanted side-effects occur!
+ */
- case T_GroupClause:
- break;
+typedef struct
+{
+ int delta_sublevels_up;
+ int min_sublevels_up;
+} IncrementVarSublevelsUp_context;
- case T_Expr:
- {
- Expr *exp = (Expr *) node;
+static bool
+IncrementVarSublevelsUp_walker(Node *node,
+ IncrementVarSublevelsUp_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
- ChangeVarNodes(
- (Node *) (exp->args),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
+ if (var->varlevelsup >= context->min_sublevels_up)
+ var->varlevelsup += context->delta_sublevels_up;
+ return false; /* done here */
+ }
+ if (IsA(node, Aggref))
+ {
+ Aggref *agg = (Aggref *) node;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
+ if (agg->agglevelsup >= context->min_sublevels_up)
+ agg->agglevelsup += context->delta_sublevels_up;
+ /* fall through to recurse into argument */
+ }
+ if (IsA(node, Query))
+ {
+ /* Recurse into subselects */
+ bool result;
+
+ context->min_sublevels_up++;
+ result = query_tree_walker((Query *) node,
+ IncrementVarSublevelsUp_walker,
+ (void *) context, 0);
+ context->min_sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, IncrementVarSublevelsUp_walker,
+ (void *) context);
+}
- ChangeVarNodes(
- (Node *) (iter->iterexpr),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
+void
+IncrementVarSublevelsUp(Node *node, int delta_sublevels_up,
+ int min_sublevels_up)
+{
+ IncrementVarSublevelsUp_context context;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
-
- ChangeVarNodes(
- (Node *) (ref->refupperindexpr),
- rt_index,
- new_index,
- sublevels_up);
- ChangeVarNodes(
- (Node *) (ref->reflowerindexpr),
- rt_index,
- new_index,
- sublevels_up);
- ChangeVarNodes(
- (Node *) (ref->refexpr),
- rt_index,
- new_index,
- sublevels_up);
- ChangeVarNodes(
- (Node *) (ref->refassgnexpr),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
+ context.delta_sublevels_up = delta_sublevels_up;
+ context.min_sublevels_up = min_sublevels_up;
- case T_Var:
- {
- Var *var = (Var *) node;
+ /*
+ * 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,
+ (void *) &context,
+ 0);
+}
- if (var->varlevelsup == sublevels_up &&
- var->varno == rt_index)
- {
- var->varno = new_index;
- var->varnoold = new_index;
- }
- }
- break;
- case T_Param:
- break;
+/*
+ * rangeTableEntry_used - detect whether an RTE is referenced somewhere
+ * in var nodes or join or setOp trees of a query or expression.
+ */
- case T_Const:
- break;
+typedef struct
+{
+ int rt_index;
+ int sublevels_up;
+} rangeTableEntry_used_context;
- case T_List:
- {
- List *l;
-
- foreach(l, (List *) node)
- ChangeVarNodes(
- (Node *) lfirst(l),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
+static bool
+rangeTableEntry_used_walker(Node *node,
+ rangeTableEntry_used_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
- case T_SubLink:
- {
- SubLink *sub = (SubLink *) node;
-
- ChangeVarNodes(
- (Node *) (sub->lefthand),
- rt_index,
- new_index,
- sublevels_up);
-
- ChangeVarNodes(
- (Node *) (sub->subselect),
- rt_index,
- new_index,
- sublevels_up + 1);
- }
- break;
+ if (var->varlevelsup == context->sublevels_up &&
+ var->varno == context->rt_index)
+ return true;
+ return false;
+ }
+ if (IsA(node, RangeTblRef))
+ {
+ RangeTblRef *rtr = (RangeTblRef *) node;
- case T_Query:
- {
- Query *qry = (Query *) node;
-
- ChangeVarNodes(
- (Node *) (qry->targetList),
- rt_index,
- new_index,
- sublevels_up);
-
- ChangeVarNodes(
- (Node *) (qry->qual),
- rt_index,
- new_index,
- sublevels_up);
-
- ChangeVarNodes(
- (Node *) (qry->havingQual),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
+ if (rtr->rtindex == context->rt_index &&
+ context->sublevels_up == 0)
+ return true;
+ /* the subquery itself is visited separately */
+ return false;
+ }
+ if (IsA(node, JoinExpr))
+ {
+ JoinExpr *j = (JoinExpr *) node;
- case T_CaseExpr:
- {
- CaseExpr *exp = (CaseExpr *) node;
-
- ChangeVarNodes(
- (Node *) (exp->args),
- rt_index,
- new_index,
- sublevels_up);
-
- ChangeVarNodes(
- (Node *) (exp->defresult),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
+ if (j->rtindex == context->rt_index &&
+ context->sublevels_up == 0)
+ return true;
+ /* fall through to examine children */
+ }
+ if (IsA(node, InClauseInfo))
+ {
+ InClauseInfo *ininfo = (InClauseInfo *) node;
- case T_CaseWhen:
- {
- CaseWhen *exp = (CaseWhen *) node;
-
- ChangeVarNodes(
- (Node *) (exp->expr),
- rt_index,
- new_index,
- sublevels_up);
-
- ChangeVarNodes(
- (Node *) (exp->result),
- rt_index,
- new_index,
- sublevels_up);
- }
- break;
+ 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 */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node, rangeTableEntry_used_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, rangeTableEntry_used_walker,
+ (void *) context);
+}
- default:
- elog(NOTICE, "unknown node tag %d in ChangeVarNodes()", nodeTag(node));
- elog(NOTICE, "Node is: %s", nodeToString(node));
- break;
+bool
+rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
+{
+ rangeTableEntry_used_context context;
+ context.rt_index = rt_index;
+ 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.
+ */
+ return query_or_expression_tree_walker(node,
+ rangeTableEntry_used_walker,
+ (void *) &context,
+ 0);
}
+/*
+ * attribute_used -
+ * Check if a specific attribute number of a RTE is used
+ * somewhere in the query or expression.
+ */
-void
-AddQual(Query *parsetree, Node *qual)
+typedef struct
{
- Node *copy,
- *old;
-
- if (qual == NULL)
- return;
-
- /* INTERSECT want's the original, but we need to copy - Jan */
- /* copy = qual; */
- copy = copyObject(qual);
+ int rt_index;
+ int attno;
+ int sublevels_up;
+} attribute_used_context;
+
+static bool
+attribute_used_walker(Node *node,
+ attribute_used_context *context)
+{
+ if (node == NULL)
+ return false;
+ if (IsA(node, Var))
+ {
+ Var *var = (Var *) node;
- old = parsetree->qual;
- if (old == NULL)
- parsetree->qual = copy;
- else
- parsetree->qual = (Node *) make_andclause(makeList(parsetree->qual, copy, -1));
+ if (var->varlevelsup == context->sublevels_up &&
+ var->varno == context->rt_index &&
+ var->varattno == context->attno)
+ return true;
+ return false;
+ }
+ if (IsA(node, Query))
+ {
+ /* Recurse into subselects */
+ bool result;
+
+ context->sublevels_up++;
+ result = query_tree_walker((Query *) node, attribute_used_walker,
+ (void *) context, 0);
+ context->sublevels_up--;
+ return result;
+ }
+ return expression_tree_walker(node, attribute_used_walker,
+ (void *) context);
}
-/* Adds 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)
+bool
+attribute_used(Node *node, int rt_index, int attno, int sublevels_up)
{
- Node *copy,
- *old;
+ attribute_used_context context;
- if (havingQual == NULL)
- return;
-
- /* INTERSECT want's the original, but we need to copy - Jan */
- /* copy = havingQual; */
- copy = copyObject(havingQual);
+ context.rt_index = rt_index;
+ context.attno = attno;
+ context.sublevels_up = sublevels_up;
- old = parsetree->havingQual;
- if (old == NULL)
- parsetree->havingQual = copy;
- else
- parsetree->havingQual = (Node *) make_andclause(makeList(parsetree->havingQual, copy, -1));
+ /*
+ * 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,
+ (void *) &context,
+ 0);
}
-#ifdef NOT_USED
-void
-AddNotHavingQual(Query *parsetree, Node *havingQual)
+
+/*
+ * If the given Query is an INSERT ... SELECT construct, extract and
+ * return the sub-Query node that represents the SELECT part. Otherwise
+ * return the given Query.
+ *
+ * If subquery_ptr is not NULL, then *subquery_ptr is set to the location
+ * of the link to the SELECT subquery inside parsetree, or NULL if not an
+ * INSERT ... SELECT.
+ *
+ * This is a hack needed because transformations on INSERT ... SELECTs that
+ * appear in rule actions should be applied to the source SELECT, not to the
+ * INSERT part. Perhaps this can be cleaned up with redesigned querytrees.
+ */
+Query *
+getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
{
- Node *copy;
+ Query *selectquery;
+ RangeTblEntry *selectrte;
+ RangeTblRef *rtr;
- if (havingQual == NULL)
- return;
+ if (subquery_ptr)
+ *subquery_ptr = NULL;
- /* INTERSECT want's the original, but we need to copy - Jan */
- /* copy = (Node *) make_notclause((Expr *)havingQual); */
- copy = (Node *) make_notclause((Expr *) copyObject(havingQual));
+ if (parsetree == NULL)
+ return parsetree;
+ if (parsetree->commandType != CMD_INSERT)
+ return parsetree;
- AddHavingQual(parsetree, copy);
+ /*
+ * Currently, this is ONLY applied to rule-action queries, and so we
+ * expect to find the *OLD* and *NEW* placeholder entries in the given
+ * 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)
+ return parsetree;
+ Assert(parsetree->jointree && IsA(parsetree->jointree, FromExpr));
+ if (length(parsetree->jointree->fromlist) != 1)
+ elog(ERROR, "expected to find SELECT subquery");
+ rtr = (RangeTblRef *) lfirst(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, "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)
+ {
+ if (subquery_ptr)
+ *subquery_ptr = &(selectrte->subquery);
+ return selectquery;
+ }
+ elog(ERROR, "could not find rule placeholders");
+ return NULL; /* not reached */
}
-#endif
+
+/*
+ * Add the given qualifier condition to the query's WHERE clause
+ */
void
-AddNotQual(Query *parsetree, Node *qual)
+AddQual(Query *parsetree, Node *qual)
{
Node *copy;
if (qual == NULL)
return;
+ if (parsetree->commandType == CMD_UTILITY)
+ {
+ /*
+ * 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.
+ *
+ * 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.)
+ */
+ if (parsetree->utilityStmt && IsA(parsetree->utilityStmt, NotifyStmt))
+ return;
+ else
+ 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 = (Node *) make_notclause((Expr *)qual); */
- copy = (Node *) make_notclause((Expr *) copyObject(qual));
+ copy = copyObject(qual);
- AddQual(parsetree, copy);
-}
+ parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals,
+ copy);
+ /*
+ * Make sure query is marked correctly if added qual has sublinks or
+ * aggregates (not sure it can ever have aggs, but sublinks
+ * definitely).
+ */
+ parsetree->hasAggs |= checkExprHasAggs(copy);
+ parsetree->hasSubLinks |= checkExprHasSubLink(copy);
+}
/*
- * Add all expressions used by the given GroupClause list to the
- * parsetree's targetlist and groupclause list.
- *
- * tlist is the old targetlist associated with the input groupclauses.
- *
- * XXX shouldn't we be checking to see if there are already matching
- * entries in parsetree->targetlist?
+ * Add the given havingQual to the one already contained in the parsetree
+ * just as AddQual does for the normal 'where' qual
*/
void
-AddGroupClause(Query *parsetree, List *group_by, List *tlist)
+AddHavingQual(Query *parsetree, Node *havingQual)
{
- List *l;
+ Node *copy;
- foreach(l, group_by)
- {
- GroupClause *groupclause = (GroupClause *) copyObject(lfirst(l));
- Index refnumber = groupclause->tleSortGroupRef;
- TargetEntry *tle = NULL;
- List *tl;
+ if (havingQual == NULL)
+ return;
- /* Find and copy the groupclause's TLE in the old tlist */
- foreach(tl, tlist)
- {
- if (((TargetEntry *) lfirst(tl))->resdom->ressortgroupref ==
- refnumber)
- {
- tle = (TargetEntry *) copyObject(lfirst(tl));
- break;
- }
- }
- if (tle == NULL)
- elog(ERROR, "AddGroupClause(): GROUP BY entry not found in rules targetlist");
+ 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
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("conditional utility statements are not implemented")));
+ }
- /* The ressortgroupref number in the old tlist might be already
- * taken in the new tlist, so force assignment of a new number.
+ 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.)
*/
- tle->resdom->ressortgroupref = 0;
- groupclause->tleSortGroupRef =
- assignSortGroupRef(tle, parsetree->targetList);
+ 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 = copyObject(havingQual);
- /* Also need to set the resno and mark it resjunk. */
- tle->resdom->resno = length(parsetree->targetList) + 1;
- tle->resdom->resjunk = true;
+ parsetree->havingQual = make_and_qual(parsetree->havingQual,
+ copy);
- parsetree->targetList = lappend(parsetree->targetList, tle);
- parsetree->groupClause = lappend(parsetree->groupClause, groupclause);
- }
+ /*
+ * Make sure query is marked correctly if added qual has sublinks or
+ * aggregates (not sure it can ever have aggs, but sublinks
+ * definitely).
+ */
+ parsetree->hasAggs |= checkExprHasAggs(copy);
+ parsetree->hasSubLinks |= checkExprHasSubLink(copy);
}
-static Node *
-make_null(Oid type)
-{
- Const *c = makeNode(Const);
-
- c->consttype = type;
- c->constlen = get_typlen(type);
- c->constvalue = PointerGetDatum(NULL);
- c->constisnull = true;
- c->constbyval = get_typbyval(type);
- return (Node *) c;
-}
-#ifdef NOT_USED
+/*
+ * Invert the given clause and add it to the WHERE qualifications of the
+ * given querytree. Inversion means "x IS NOT TRUE", not just "NOT x",
+ * else we will do the wrong thing when x evaluates to NULL.
+ */
void
-FixResdomTypes(List *tlist)
+AddInvertedQual(Query *parsetree, Node *qual)
{
- List *i;
+ BooleanTest *invqual;
- foreach(i, tlist)
- {
- TargetEntry *tle = lfirst(i);
+ if (qual == NULL)
+ return;
- if (nodeTag(tle->expr) == T_Var)
- {
- Var *var = (Var *) tle->expr;
+ /* Need not copy input qual, because AddQual will... */
+ invqual = makeNode(BooleanTest);
+ invqual->arg = (Expr *) qual;
+ invqual->booltesttype = IS_NOT_TRUE;
- tle->resdom->restype = var->vartype;
- tle->resdom->restypmod = var->vartypmod;
- }
- }
+ AddQual(parsetree, (Node *) invqual);
}
-#endif
+/* Find a targetlist entry by resno */
static Node *
FindMatchingNew(List *tlist, int attno)
{
TargetEntry *tle = lfirst(i);
if (tle->resdom->resno == attno)
- return tle->expr;
+ return (Node *) tle->expr;
}
return NULL;
}
+#ifdef NOT_USED
+
+/* Find a targetlist entry by resname */
static Node *
FindMatchingTLEntry(List *tlist, char *e_attname)
{
char *resname;
resname = tle->resdom->resname;
- if (!strcmp(e_attname, resname))
+ if (strcmp(e_attname, resname) == 0)
return tle->expr;
}
return NULL;
}
+#endif
-static void
-ResolveNew(RewriteInfo *info, List *targetlist, Node **nodePtr,
- int sublevels_up)
-{
- Node *node = *nodePtr;
-
- if (node == NULL)
- return;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- ResolveNew(info, targetlist, &((TargetEntry *) node)->expr,
- sublevels_up);
- break;
- case T_Aggref:
- ResolveNew(info, targetlist, &((Aggref *) node)->target,
- sublevels_up);
- break;
- case T_Expr:
- ResolveNew(info, targetlist, (Node **) (&(((Expr *) node)->args)),
- sublevels_up);
- break;
- case T_Iter:
- ResolveNew(info, targetlist, (Node **) (&(((Iter *) node)->iterexpr)),
- sublevels_up);
- break;
- case T_ArrayRef:
- ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refupperindexpr)),
- sublevels_up);
- ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->reflowerindexpr)),
- sublevels_up);
- ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refexpr)),
- sublevels_up);
- ResolveNew(info, targetlist, (Node **) (&(((ArrayRef *) node)->refassgnexpr)),
- sublevels_up);
- break;
- case T_Var:
- {
- int this_varno = (int) ((Var *) node)->varno;
- int this_varlevelsup = (int) ((Var *) node)->varlevelsup;
- Node *n;
-
- if (this_varno == info->new_varno &&
- this_varlevelsup == sublevels_up)
- {
- n = FindMatchingNew(targetlist,
- ((Var *) node)->varattno);
- if (n == NULL)
- {
- if (info->event == CMD_UPDATE)
- {
- *nodePtr = n = copyObject(node);
- ((Var *) n)->varno = info->current_varno;
- ((Var *) n)->varnoold = info->current_varno;
- }
- else
- *nodePtr = make_null(((Var *) node)->vartype);
- }
- else
- {
- *nodePtr = copyObject(n);
- ((Var *) *nodePtr)->varlevelsup = this_varlevelsup;
- }
- }
- break;
- }
- case T_List:
- {
- List *l;
-
- foreach(l, (List *) node)
- ResolveNew(info, targetlist, (Node **) &(lfirst(l)),
- sublevels_up);
- break;
- }
- case T_SubLink:
- {
- SubLink *sublink = (SubLink *) node;
- Query *query = (Query *) sublink->subselect;
-
- /* XXX what about lefthand? What about rest of subquery? */
- ResolveNew(info, targetlist, (Node **) &(query->qual), sublevels_up + 1);
- }
- break;
- case T_GroupClause:
- break;
- default:
- /* ignore the others */
- break;
- }
-}
+/*
+ * 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.
+ */
-void
-FixNew(RewriteInfo *info, Query *parsetree)
+typedef struct
{
- ResolveNew(info, parsetree->targetList,
- (Node **) &(info->rule_action->targetList), 0);
- ResolveNew(info, parsetree->targetList,
- (Node **) &info->rule_action->qual, 0);
- ResolveNew(info, parsetree->targetList,
- (Node **) &(info->rule_action->groupClause), 0);
-}
+ int target_varno;
+ int sublevels_up;
+ List *targetlist;
+ int event;
+ int update_varno;
+} ResolveNew_context;
-static void
-nodeHandleRIRAttributeRule(Node **nodePtr,
- List *rtable,
- List *targetlist,
- int rt_index,
- int attr_num,
- int *modified,
- int *badsql,
- int sublevels_up)
+static Node *
+ResolveNew_mutator(Node *node, ResolveNew_context *context)
{
- Node *node = *nodePtr;
-
if (node == NULL)
- return;
- switch (nodeTag(node))
+ return NULL;
+ if (IsA(node, Var))
{
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
+ Var *var = (Var *) node;
+ int this_varno = (int) var->varno;
+ int this_varlevelsup = (int) var->varlevelsup;
- nodeHandleRIRAttributeRule(&tle->expr, rtable, targetlist,
- rt_index, attr_num, modified, badsql,
- sublevels_up);
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
+ if (this_varno == context->target_varno &&
+ this_varlevelsup == context->sublevels_up)
+ {
+ Node *n;
- nodeHandleRIRAttributeRule(&aggref->target, rtable, targetlist,
- rt_index, attr_num, modified, badsql,
- sublevels_up);
- }
- break;
- case T_Expr:
- {
- Expr *expr = (Expr *) node;
+ /* band-aid: don't do the wrong thing with a whole-tuple Var */
+ if (var->varattno == InvalidAttrNumber)
+ ereport(ERROR,
+ (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+ errmsg("cannot handle whole-tuple reference")));
- nodeHandleRIRAttributeRule((Node **) (&(expr->args)), rtable,
- targetlist, rt_index, attr_num,
- modified, badsql,
- sublevels_up);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
+ n = FindMatchingNew(context->targetlist, var->varattno);
- nodeHandleRIRAttributeRule((Node **) (&(iter->iterexpr)), rtable,
- targetlist, rt_index, attr_num,
- modified, badsql,
- sublevels_up);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
-
- nodeHandleRIRAttributeRule((Node **) (&(ref->refupperindexpr)), rtable,
- targetlist, rt_index, attr_num,
- modified, badsql,
- sublevels_up);
- nodeHandleRIRAttributeRule((Node **) (&(ref->reflowerindexpr)), rtable,
- targetlist, rt_index, attr_num,
- modified, badsql,
- sublevels_up);
- nodeHandleRIRAttributeRule((Node **) (&(ref->refexpr)), rtable,
- targetlist, rt_index, attr_num,
- modified, badsql,
- sublevels_up);
- nodeHandleRIRAttributeRule((Node **) (&(ref->refassgnexpr)), rtable,
- targetlist, rt_index, attr_num,
- modified, badsql,
- sublevels_up);
- }
- break;
- case T_Var:
+ if (n == NULL)
{
- int this_varno = ((Var *) node)->varno;
- int this_varattno = ((Var *) node)->varattno;
- int this_varlevelsup = ((Var *) node)->varlevelsup;
-
- if (this_varno == rt_index &&
- this_varattno == attr_num &&
- this_varlevelsup == sublevels_up)
+ if (context->event == CMD_UPDATE)
{
- if (((Var *) node)->vartype == 32)
- { /* HACK */
- *nodePtr = make_null(((Var *) node)->vartype);
- *modified = TRUE;
- *badsql = TRUE;
- break;
- }
- else
- {
- NameData name_to_look_for;
-
- name_to_look_for.data[0] = '\0';
- namestrcpy(&name_to_look_for,
- (char *) get_attname(getrelid(this_varno,
- rtable),
- attr_num));
- if (name_to_look_for.data[0])
- {
- Node *n;
-
- n = FindMatchingTLEntry(targetlist, (char *) &name_to_look_for);
- if (n == NULL)
- *nodePtr = make_null(((Var *) node)->vartype);
- else
- *nodePtr = n;
- *modified = TRUE;
- }
- }
+ /* 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;
}
- }
- break;
- case T_List:
- {
- List *i;
-
- foreach(i, (List *) node)
+ else
{
- nodeHandleRIRAttributeRule((Node **) (&(lfirst(i))), rtable,
- targetlist, rt_index, attr_num,
- modified, badsql, sublevels_up);
+ /* Otherwise replace unmatched var with a null */
+ return (Node *) makeNullConst(var->vartype);
}
}
- break;
- case T_SubLink:
+ else
{
- SubLink *sublink = (SubLink *) node;
- Query *query = (Query *) sublink->subselect;
-
- /* XXX what about lefthand? What about rest of subquery? */
- nodeHandleRIRAttributeRule((Node **) &(query->qual), rtable, targetlist,
- rt_index, attr_num, modified, badsql,
- sublevels_up + 1);
+ /* 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;
}
- break;
- default:
- /* ignore the others */
- break;
+ }
+ /* otherwise fall through to copy the var normally */
}
-}
-
-/*
- * Handles 'on retrieve to relation.attribute
- * do instead retrieve (attribute = expression) w/qual'
- */
-void
-HandleRIRAttributeRule(Query *parsetree,
- List *rtable,
- List *targetlist,
- int rt_index,
- int attr_num,
- int *modified,
- int *badsql)
-{
- nodeHandleRIRAttributeRule((Node **) (&(parsetree->targetList)), rtable,
- targetlist, rt_index, attr_num,
- modified, badsql, 0);
- nodeHandleRIRAttributeRule(&parsetree->qual, rtable, targetlist,
- rt_index, attr_num, modified, badsql, 0);
+ if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ Query *newnode;
+
+ context->sublevels_up++;
+ newnode = query_tree_mutator((Query *) node,
+ ResolveNew_mutator,
+ (void *) context,
+ 0);
+ context->sublevels_up--;
+ return (Node *) newnode;
+ }
+ return expression_tree_mutator(node, ResolveNew_mutator,
+ (void *) context);
}
-#ifdef NOT_USED
-static void
-nodeHandleViewRule(Node **nodePtr,
- List *rtable,
- List *targetlist,
- int rt_index,
- int *modified,
- int sublevels_up)
+Node *
+ResolveNew(Node *node, int target_varno, int sublevels_up,
+ List *targetlist, int event, int update_varno)
{
- Node *node = *nodePtr;
+ ResolveNew_context context;
- if (node == NULL)
- return;
+ context.target_varno = target_varno;
+ context.sublevels_up = sublevels_up;
+ context.targetlist = targetlist;
+ context.event = event;
+ context.update_varno = update_varno;
- switch (nodeTag(node))
- {
- case T_TargetEntry:
- {
- TargetEntry *tle = (TargetEntry *) node;
-
- nodeHandleViewRule(&(tle->expr), rtable, targetlist,
- rt_index, modified, sublevels_up);
- }
- break;
- case T_Aggref:
- {
- Aggref *aggref = (Aggref *) node;
+ /*
+ * 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);
+}
- nodeHandleViewRule(&(aggref->target), rtable, targetlist,
- rt_index, modified, sublevels_up);
- }
- break;
- /*
- * This has to be done to make queries using groupclauses work
- * on views
- */
- case T_GroupClause:
- {
- GroupClause *group = (GroupClause *) node;
+#ifdef NOT_USED
- nodeHandleViewRule((Node **) (&(group->entry)), rtable, targetlist,
- rt_index, modified, sublevels_up);
- }
- break;
- case T_Expr:
- {
- Expr *expr = (Expr *) node;
+/*
+ * 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'
+ */
- nodeHandleViewRule((Node **) (&(expr->args)),
- rtable, targetlist,
- rt_index, modified, sublevels_up);
- }
- break;
- case T_Iter:
- {
- Iter *iter = (Iter *) node;
+typedef struct
+{
+ List *rtable;
+ List *targetlist;
+ int rt_index;
+ int attr_num;
+ int *modified;
+ int *badsql;
+ int sublevels_up;
+} HandleRIRAttributeRule_context;
- nodeHandleViewRule((Node **) (&(iter->iterexpr)),
- rtable, targetlist,
- rt_index, modified, sublevels_up);
- }
- break;
- case T_ArrayRef:
- {
- ArrayRef *ref = (ArrayRef *) node;
-
- nodeHandleViewRule((Node **) (&(ref->refupperindexpr)),
- rtable, targetlist,
- rt_index, modified, sublevels_up);
- nodeHandleViewRule((Node **) (&(ref->reflowerindexpr)),
- rtable, targetlist,
- rt_index, modified, sublevels_up);
- nodeHandleViewRule((Node **) (&(ref->refexpr)),
- rtable, targetlist,
- rt_index, modified, sublevels_up);
- nodeHandleViewRule((Node **) (&(ref->refassgnexpr)),
- rtable, targetlist,
- rt_index, modified, sublevels_up);
+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);
}
- break;
- case T_Var:
+ else
{
- Var *var = (Var *) node;
- int this_varno = var->varno;
- int this_varlevelsup = var->varlevelsup;
- Node *n;
+ char *name_to_look_for;
- if (this_varno == rt_index &&
- this_varlevelsup == sublevels_up)
+ name_to_look_for = get_attname(getrelid(this_varno,
+ context->rtable),
+ this_varattno);
+ if (name_to_look_for)
{
- n = FindMatchingTLEntry(targetlist,
- get_attname(getrelid(this_varno,
- rtable),
- var->varattno));
- if (n == NULL)
- *nodePtr = make_null(((Var *) node)->vartype);
- else
- {
-
- /*
- * This is a hack: The varlevelsup of the orignal
- * variable and the new one should be the same.
- * Normally we adapt the node by changing a
- * pointer to point to a var contained in
- * 'targetlist'. In the targetlist all
- * varlevelsups are 0 so if we want to change it
- * to the original value we have to copy the node
- * before! (Maybe this will cause troubles with
- * some sophisticated queries on views?)
- */
- if (this_varlevelsup > 0)
- *nodePtr = copyObject(n);
- else
- *nodePtr = n;
-
- if (nodeTag(nodePtr) == T_Var)
- ((Var *) *nodePtr)->varlevelsup = this_varlevelsup;
- else
- nodeHandleViewRule(&n, rtable, targetlist,
- rt_index, modified, sublevels_up);
- }
- *modified = TRUE;
- }
- break;
- }
- case T_List:
- {
- List *l;
+ Node *n;
- foreach(l, (List *) node)
- {
- nodeHandleViewRule((Node **) (&(lfirst(l))),
- rtable, targetlist,
- rt_index, modified, sublevels_up);
+ *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;
}
}
- break;
- case T_SubLink:
- {
- SubLink *sublink = (SubLink *) node;
- Query *query = (Query *) sublink->subselect;
-
- nodeHandleViewRule((Node **) &(query->qual), rtable, targetlist,
- rt_index, modified, sublevels_up + 1);
-
- /***S*H*D***/
- nodeHandleViewRule((Node **) &(query->havingQual), rtable, targetlist,
- rt_index, modified, sublevels_up + 1);
- nodeHandleViewRule((Node **) &(query->targetList), rtable, targetlist,
- rt_index, modified, sublevels_up + 1);
-
+ }
+ /* otherwise fall through to copy the var normally */
+ }
- /*
- * We also have to adapt the variables used in
- * sublink->lefthand
- */
- nodeHandleViewRule((Node **) &(sublink->lefthand), rtable,
- targetlist, rt_index, modified, sublevels_up);
- }
- break;
- default:
- /* ignore the others */
- break;
+ if (IsA(node, Query))
+ {
+ /* Recurse into RTE subquery or not-yet-planned sublink subquery */
+ Query *newnode;
+
+ context->sublevels_up++;
+ newnode = query_tree_mutator((Query *) node,
+ HandleRIRAttributeRule_mutator,
+ (void *) context,
+ 0);
+ context->sublevels_up--;
+ return (Node *) newnode;
}
+ return expression_tree_mutator(node, HandleRIRAttributeRule_mutator,
+ (void *) context);
}
void
-HandleViewRule(Query *parsetree,
- List *rtable,
- List *targetlist,
- int rt_index,
- int *modified)
+HandleRIRAttributeRule(Query *parsetree,
+ List *rtable,
+ List *targetlist,
+ int rt_index,
+ int attr_num,
+ int *modified,
+ int *badsql)
{
- nodeHandleViewRule(&parsetree->qual, rtable, targetlist, rt_index,
- modified, 0);
- nodeHandleViewRule((Node **) (&(parsetree->targetList)), rtable, targetlist,
- rt_index, modified, 0);
-
- /*
- * The variables in the havingQual and groupClause also have to be
- * adapted
- */
- nodeHandleViewRule(&parsetree->havingQual, rtable, targetlist, rt_index,
- modified, 0);
- nodeHandleViewRule((Node **) (&(parsetree->groupClause)), rtable, targetlist, rt_index,
- modified, 0);
+ 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);
}
-#endif
+#endif /* NOT_USED */