X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=src%2Fbackend%2Frewrite%2FrewriteManip.c;h=60492fe3d091fa52b349f5190a082531ec5277ea;hb=e006a24ad152b3faec748afe8c1ff0829699b2e6;hp=238897d58ebd7dfd41c1cd09de5688e660436500;hpb=17c03b30b0f191e62e992dc6af8c995fd2b007a2;p=postgresql diff --git a/src/backend/rewrite/rewriteManip.c b/src/backend/rewrite/rewriteManip.c index 238897d58e..60492fe3d0 100644 --- a/src/backend/rewrite/rewriteManip.c +++ b/src/backend/rewrite/rewriteManip.c @@ -2,88 +2,107 @@ * * rewriteManip.c * - * Portions Copyright (c) 1996-2001, 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.58 2001/09/07 20:52:31 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" -/* macros borrowed from expression_tree_mutator */ - -#define FLATCOPY(newnode, node, nodetype) \ - ( (newnode) = makeNode(nodetype), \ - memcpy((newnode), (node), sizeof(nodetype)) ) - -#define MUTATE(newfield, oldfield, fieldtype, mutator, context) \ - ( (newfield) = (fieldtype) mutator((Node *) (oldfield), (context)) ) +typedef struct +{ + int sublevels_up; +} checkExprHasAggs_context; -static bool checkExprHasAggs_walker(Node *node, void *context); +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); /* * 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 + * 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; + + context.sublevels_up = 0; /* - * If a Query is passed, examine it --- but we will not recurse into - * sub-Queries. + * 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. */ - if (node && IsA(node, Query)) - return query_tree_walker((Query *) node, checkExprHasAggs_walker, - NULL, false); - else - return checkExprHasAggs_walker(node, NULL); + return query_or_expression_tree_walker(node, + checkExprHasAggs_walker, + (void *) &context, + 0); } static bool -checkExprHasAggs_walker(Node *node, void *context) +checkExprHasAggs_walker(Node *node, checkExprHasAggs_context *context) { if (node == NULL) return false; if (IsA(node, Aggref)) - return true; /* abort the tree traversal and return - * true */ - return expression_tree_walker(node, checkExprHasAggs_walker, context); + { + 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); } /* * 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. */ - if (node && IsA(node, Query)) - return query_tree_walker((Query *) node, checkExprHasSubLink_walker, - NULL, false); - else - return checkExprHasSubLink_walker(node, NULL); + return query_or_expression_tree_walker(node, + checkExprHasSubLink_walker, + NULL, + QTW_IGNORE_RT_SUBQUERIES); } static bool @@ -92,8 +111,7 @@ 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 true; /* abort the tree traversal and return true */ return expression_tree_walker(node, checkExprHasSubLink_walker, context); } @@ -103,8 +121,8 @@ checkExprHasSubLink_walker(Node *node, void *context) * * 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, RangeTblRef nodes - * in join trees and setOp trees are adjusted. + * 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 @@ -133,6 +151,14 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *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; @@ -142,6 +168,38 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) /* the subquery itself is visited separately */ return false; } + if (IsA(node, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) node; + + if (context->sublevels_up == 0) + j->rtindex += context->offset; + /* fall through to examine children */ + } + if (IsA(node, FlattenedSubLink)) + { + FlattenedSubLink *fslink = (FlattenedSubLink *) node; + + if (context->sublevels_up == 0) + { + fslink->lefthand = offset_relid_set(fslink->lefthand, + context->offset); + 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 */ @@ -149,7 +207,7 @@ OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) context->sublevels_up++; result = query_tree_walker((Query *) node, OffsetVarNodes_walker, - (void *) context, true); + (void *) context, 0); context->sublevels_up--; return result; } @@ -166,43 +224,62 @@ OffsetVarNodes(Node *node, int offset, int sublevels_up) 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, true); + (void *) &context, 0); } else OffsetVarNodes_walker(node, &context); } +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; +} + /* * 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, RangeTblRef - * nodes in join trees and setOp trees are adjusted. + * 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 @@ -233,6 +310,15 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) } 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; @@ -243,6 +329,43 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) /* 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, FlattenedSubLink)) + { + FlattenedSubLink *fslink = (FlattenedSubLink *) node; + + if (context->sublevels_up == 0) + { + fslink->lefthand = adjust_relid_set(fslink->lefthand, + context->rt_index, + context->new_index); + 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 */ @@ -250,7 +373,7 @@ ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) context->sublevels_up++; result = query_tree_walker((Query *) node, ChangeVarNodes_walker, - (void *) context, true); + (void *) context, 0); context->sublevels_up--; return result; } @@ -268,39 +391,59 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) 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, - (void *) &context, true); + (void *) &context, 0); } else ChangeVarNodes_walker(node, &context); } +/* + * 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; +} + /* * IncrementVarSublevelsUp - adjust Var nodes when pushing them down in tree * @@ -313,6 +456,8 @@ ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) * 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! @@ -336,8 +481,23 @@ IncrementVarSublevelsUp_walker(Node *node, if (var->varlevelsup >= context->min_sublevels_up) 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; + + 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 */ @@ -346,7 +506,7 @@ IncrementVarSublevelsUp_walker(Node *node, context->min_sublevels_up++; result = query_tree_walker((Query *) node, IncrementVarSublevelsUp_walker, - (void *) context, true); + (void *) context, 0); context->min_sublevels_up--; return result; } @@ -364,15 +524,13 @@ IncrementVarSublevelsUp(Node *node, int delta_sublevels_up, 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, go straight to query_tree_walker to make sure that - * sublevels_up doesn't get incremented prematurely. + * 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. */ - if (node && IsA(node, Query)) - query_tree_walker((Query *) node, IncrementVarSublevelsUp_walker, - (void *) &context, true); - else - IncrementVarSublevelsUp_walker(node, &context); + query_or_expression_tree_walker(node, + IncrementVarSublevelsUp_walker, + (void *) &context, + 0); } @@ -402,6 +560,15 @@ rangeTableEntry_used_walker(Node *node, 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; @@ -412,6 +579,20 @@ rangeTableEntry_used_walker(Node *node, /* the subquery itself is visited separately */ return false; } + if (IsA(node, JoinExpr)) + { + JoinExpr *j = (JoinExpr *) node; + + if (j->rtindex == context->rt_index && + context->sublevels_up == 0) + return true; + /* fall through to examine children */ + } + /* Shouldn't need to handle planner auxiliary nodes here */ + Assert(!IsA(node, FlattenedSubLink)); + Assert(!IsA(node, SpecialJoinInfo)); + Assert(!IsA(node, AppendRelInfo)); + if (IsA(node, Query)) { /* Recurse into subselects */ @@ -419,7 +600,7 @@ rangeTableEntry_used_walker(Node *node, context->sublevels_up++; result = query_tree_walker((Query *) node, rangeTableEntry_used_walker, - (void *) context, true); + (void *) context, 0); context->sublevels_up--; return result; } @@ -436,15 +617,13 @@ rangeTableEntry_used(Node *node, int rt_index, int sublevels_up) 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 - * sublevels_up doesn't get incremented prematurely. + * 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. */ - if (node && IsA(node, Query)) - return query_tree_walker((Query *) node, rangeTableEntry_used_walker, - (void *) &context, true); - else - return rangeTableEntry_used_walker(node, &context); + return query_or_expression_tree_walker(node, + rangeTableEntry_used_walker, + (void *) &context, + 0); } @@ -484,7 +663,7 @@ attribute_used_walker(Node *node, context->sublevels_up++; result = query_tree_walker((Query *) node, attribute_used_walker, - (void *) context, true); + (void *) context, 0); context->sublevels_up--; return result; } @@ -502,15 +681,13 @@ attribute_used(Node *node, int rt_index, int attno, int sublevels_up) 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 - * sublevels_up doesn't get incremented prematurely. + * 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. */ - if (node && IsA(node, Query)) - return query_tree_walker((Query *) node, attribute_used_walker, - (void *) &context, true); - else - return attribute_used_walker(node, &context); + return query_or_expression_tree_walker(node, + attribute_used_walker, + (void *) &context, + 0); } @@ -548,33 +725,33 @@ getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr) * 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->relname, + 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->relname, + 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->relname, - "*OLD*") == 0 && - strcmp(rt_fetch(PRS2_NEW_VARNO, selectquery->rtable)->eref->relname, - "*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 */ } @@ -601,142 +778,72 @@ AddQual(Query *parsetree, Node *qual) * 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 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 - elog(ERROR, "Conditional utility statements are not implemented"); + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("conditional utility statements are not implemented"))); } - /* INTERSECT want's the original, but we need to copy - Jan */ - copy = copyObject(qual); - - 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 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) + if (parsetree->setOperations != NULL) { /* - * There's noplace to put the qual on a utility statement. - * - * See comments in AddQual for motivation. + * 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.) */ - 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 UNION/INTERSECT/EXCEPT statements are not implemented"))); } /* INTERSECT want's the original, but we need to copy - Jan */ - copy = copyObject(havingQual); + copy = copyObject(qual); - parsetree->havingQual = make_and_qual(parsetree->havingQual, - 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). + * We had better not have stuck an aggregate into the WHERE clause. */ - parsetree->hasAggs |= checkExprHasAggs(copy); - parsetree->hasSubLinks |= checkExprHasSubLink(copy); -} + Assert(!checkExprHasAggs(copy)); -#ifdef NOT_USED -void -AddNotHavingQual(Query *parsetree, Node *havingQual) -{ - Node *notqual; - - if (havingQual == NULL) - return; - - /* Need not copy input qual, because AddHavingQual will... */ - notqual = (Node *) make_notclause((Expr *) havingQual); - - AddHavingQual(parsetree, notqual); + /* + * Make sure query is marked correctly if added qual has sublinks. Need + * not search qual when query is already marked. + */ + if (!parsetree->hasSubLinks) + parsetree->hasSubLinks = checkExprHasSubLink(copy); } -#endif +/* + * 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 -AddNotQual(Query *parsetree, Node *qual) +AddInvertedQual(Query *parsetree, Node *qual) { - Node *notqual; + BooleanTest *invqual; if (qual == NULL) return; /* Need not copy input qual, because AddQual will... */ - notqual = (Node *) make_notclause((Expr *) qual); + invqual = makeNode(BooleanTest); + invqual->arg = (Expr *) qual; + invqual->booltesttype = IS_NOT_TRUE; - AddQual(parsetree, notqual); + AddQual(parsetree, (Node *) invqual); } -/* 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 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 * @@ -744,17 +851,75 @@ FindMatchingTLEntry(List *tlist, char *e_attname) * 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) { @@ -769,67 +934,73 @@ 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 */ } - - /* - * Since expression_tree_mutator won't touch subselects, we have to - * handle them specially. - */ - if (IsA(node, SubLink)) + else if (IsA(node, CurrentOfExpr)) { - SubLink *sublink = (SubLink *) node; - SubLink *newnode; + CurrentOfExpr *cexpr = (CurrentOfExpr *) node; + int this_varno = (int) cexpr->cvarno; - FLATCOPY(newnode, sublink, SubLink); - MUTATE(newnode->lefthand, sublink->lefthand, List *, - ResolveNew_mutator, context); - MUTATE(newnode->subselect, sublink->subselect, Node *, - ResolveNew_mutator, context); - return (Node *) newnode; + 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 */ } - if (IsA(node, Query)) + else if (IsA(node, Query)) { - Query *query = (Query *) node; + /* Recurse into RTE subquery or not-yet-planned sublink subquery */ Query *newnode; + bool save_inserted_sublink; - FLATCOPY(newnode, query, Query); context->sublevels_up++; - query_tree_mutator(newnode, ResolveNew_mutator, context, true); + 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; } @@ -839,165 +1010,40 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) 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, go straight to query_tree_mutator to make sure - * that sublevels_up doesn't get incremented prematurely. - */ - if (node && IsA(node, Query)) - { - Query *query = (Query *) node; - Query *newnode; - - FLATCOPY(newnode, query, Query); - query_tree_mutator(newnode, ResolveNew_mutator, - (void *) &context, true); - return (Node *) newnode; - } - else - return ResolveNew_mutator(node, &context); -} - - -#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 */ - } - - /* - * Since expression_tree_mutator won't touch subselects, we have to - * handle them specially. + * 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. */ - if (IsA(node, SubLink)) - { - SubLink *sublink = (SubLink *) node; - SubLink *newnode; + result = query_or_expression_tree_mutator(node, + ResolveNew_mutator, + (void *) &context, + 0); - FLATCOPY(newnode, sublink, SubLink); - MUTATE(newnode->lefthand, sublink->lefthand, List *, - HandleRIRAttributeRule_mutator, context); - MUTATE(newnode->subselect, sublink->subselect, Node *, - HandleRIRAttributeRule_mutator, context); - return (Node *) newnode; - } - if (IsA(node, Query)) + if (context.inserted_sublink) { - Query *query = (Query *) node; - Query *newnode; + if (IsA(result, Query)) + ((Query *) result)->hasSubLinks = true; - FLATCOPY(newnode, query, Query); - context->sublevels_up++; - query_tree_mutator(newnode, HandleRIRAttributeRule_mutator, - context, true); - 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, true); + return result; } - -#endif /* NOT_USED */