| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| /*------------------------------------------------------------------------- |
| * |
| * rewriteManip.c |
| * |
| * Portions Copyright (c) 2006-2008, Greenplum inc |
| * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group |
| * Portions Copyright (c) 1994, Regents of the University of California |
| * |
| * |
| * IDENTIFICATION |
| * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.102 2006/10/04 00:29:56 momjian Exp $ |
| * |
| *------------------------------------------------------------------------- |
| */ |
| #include "postgres.h" |
| |
| #include "catalog/pg_type.h" |
| #include "nodes/makefuncs.h" |
| #include "optimizer/clauses.h" |
| #include "parser/parse_coerce.h" |
| #include "parser/parse_relation.h" |
| #include "parser/parsetree.h" |
| #include "rewrite/rewriteManip.h" |
| |
| |
| typedef struct |
| { |
| int sublevels_up; |
| } checkExprHasAggs_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 - |
| * 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; |
| |
| /* |
| * 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); |
| } |
| |
| 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, PercentileExpr)) |
| { |
| /* PercentileExpr is always levelsup == 0 */ |
| if (context->sublevels_up == 0) |
| return true; |
| } |
| |
| 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 - |
| * Check if an expression contains a SubLink. |
| */ |
| bool |
| checkExprHasSubLink(Node *node) |
| { |
| /* |
| * If a Query is passed, examine it --- but we need not recurse into |
| * sub-Queries or CTE list. |
| */ |
| return query_or_expression_tree_walker(node, |
| checkExprHasSubLink_walker, |
| NULL, |
| QTW_IGNORE_RC_SUBQUERIES); |
| } |
| |
| 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); |
| } |
| |
| |
| /* |
| * 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! |
| */ |
| |
| typedef struct |
| { |
| int offset; |
| int sublevels_up; |
| } OffsetVarNodes_context; |
| |
| static bool |
| OffsetVarNodes_walker(Node *node, OffsetVarNodes_context *context) |
| { |
| if (node == NULL) |
| return false; |
| if (IsA(node, Var)) |
| { |
| Var *var = (Var *) node; |
| |
| if (var->varlevelsup == context->sublevels_up) |
| { |
| var->varno += context->offset; |
| var->varnoold += context->offset; |
| } |
| return false; |
| } |
| if (IsA(node, RangeTblRef)) |
| { |
| RangeTblRef *rtr = (RangeTblRef *) node; |
| |
| 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; |
| |
| if (context->sublevels_up == 0) |
| j->rtindex += context->offset; |
| /* fall through to examine children */ |
| } |
| if (IsA(node, InClauseInfo)) |
| { |
| InClauseInfo *ininfo = (InClauseInfo *) node; |
| |
| if (context->sublevels_up == 0) |
| ininfo->righthand = offset_relid_set(ininfo->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 */ |
| 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); |
| } |
| |
| void |
| OffsetVarNodes(Node *node, int offset, int sublevels_up) |
| { |
| OffsetVarNodes_context context; |
| |
| context.offset = offset; |
| 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. |
| */ |
| if (node && IsA(node, Query)) |
| { |
| Query *qry = (Query *) node; |
| |
| /* |
| * 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) |
| { |
| RowMarkClause *rc = (RowMarkClause *) lfirst(l); |
| |
| rc->rti += offset; |
| } |
| } |
| query_tree_walker(qry, OffsetVarNodes_walker, |
| (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, 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! |
| */ |
| |
| typedef struct |
| { |
| int rt_index; |
| int new_index; |
| int sublevels_up; |
| } ChangeVarNodes_context; |
| |
| static bool |
| ChangeVarNodes_walker(Node *node, ChangeVarNodes_context *context) |
| { |
| if (node == NULL) |
| return false; |
| if (IsA(node, Var)) |
| { |
| Var *var = (Var *) node; |
| |
| 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; |
| |
| 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->righthand = adjust_relid_set(ininfo->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 */ |
| 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); |
| } |
| |
| void |
| ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up) |
| { |
| ChangeVarNodes_context context; |
| |
| context.rt_index = rt_index; |
| context.new_index = new_index; |
| 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. |
| */ |
| if (node && IsA(node, Query)) |
| { |
| Query *qry = (Query *) node; |
| |
| /* |
| * 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) |
| { |
| RowMarkClause *rc = (RowMarkClause *) lfirst(l); |
| |
| if (rc->rti == rt_index) |
| rc->rti = new_index; |
| } |
| } |
| query_tree_walker(qry, ChangeVarNodes_walker, |
| (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 |
| * |
| * 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! |
| */ |
| |
| typedef struct |
| { |
| int delta_sublevels_up; |
| int min_sublevels_up; |
| |
| /* |
| * MPP-19436: when a query mixes window function with group by or aggregates, |
| * then a transformation will turn the original structure Q into an outer |
| * query Q' and an inner query Q'' |
| * Q -> Q' |
| * | |
| * |->Q'' |
| * All the structures will be copied from Q to Q'' except ctelists. |
| * This causes Q'', and its inner structures, to have dangling cte references, since |
| * ctelists are kept in Q'. In such a case, we need to ignore min_sublevels_up and |
| * increment by delta_sublevels_up. |
| * |
| */ |
| bool ignore_min_sublevels_up; |
| } IncrementVarSublevelsUp_context; |
| |
| static bool |
| IncrementVarSublevelsUp_walker(Node *node, |
| IncrementVarSublevelsUp_context *context) |
| { |
| if (node == NULL) |
| return false; |
| if (IsA(node, Var)) |
| { |
| Var *var = (Var *) node; |
| |
| 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; |
| |
| if (agg->agglevelsup >= context->min_sublevels_up) |
| agg->agglevelsup += context->delta_sublevels_up; |
| /* fall through to recurse into argument */ |
| } |
| if (IsA(node, WindowRef)) |
| { |
| WindowRef *wref = (WindowRef *) node; |
| |
| if (wref->winlevelsup >= context->min_sublevels_up) |
| wref->winlevelsup += context->delta_sublevels_up; |
| /* fall through to recurse into argument */ |
| } |
| if (IsA(node, RangeTblEntry)) |
| { |
| RangeTblEntry *rte = (RangeTblEntry *) node; |
| |
| if (rte->rtekind == RTE_CTE) |
| { |
| if (rte->ctelevelsup >= context->min_sublevels_up) |
| rte->ctelevelsup += context->delta_sublevels_up; |
| |
| /* |
| * Fix for MPP-19436: in transformGroupedWindows, min_sublevels_up |
| * is ignored. For RTE refer to the original query ctelist should |
| * all be incremented. |
| */ |
| if(context->ignore_min_sublevels_up && rte->ctelevelsup == context->min_sublevels_up - 1) |
| { |
| rte->ctelevelsup += context->delta_sublevels_up; |
| } |
| } |
| return false; /* allow range_table_walker to continue */ |
| } |
| if (IsA(node, Query)) |
| { |
| /* Recurse into subselects */ |
| bool result; |
| |
| context->min_sublevels_up++; |
| result = query_tree_walker((Query *) node, |
| IncrementVarSublevelsUp_walker, |
| (void *) context, QTW_EXAMINE_RTES); |
| context->min_sublevels_up--; |
| return result; |
| } |
| return expression_tree_walker(node, IncrementVarSublevelsUp_walker, |
| (void *) context); |
| } |
| |
| void |
| IncrementVarSublevelsUp(Node *node, int delta_sublevels_up, |
| int min_sublevels_up) |
| { |
| IncrementVarSublevelsUp_context context; |
| |
| context.delta_sublevels_up = 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, we don't want to increment sublevels_up. |
| */ |
| query_or_expression_tree_walker(node, |
| IncrementVarSublevelsUp_walker, |
| (void *) &context, |
| QTW_EXAMINE_RTES); |
| } |
| |
| void |
| IncrementVarSublevelsUpInTransformGroupedWindows(Node *node, |
| int delta_sublevels_up, int min_sublevels_up) |
| { |
| IncrementVarSublevelsUp_context context; |
| |
| context.delta_sublevels_up = delta_sublevels_up; |
| context.min_sublevels_up = min_sublevels_up; |
| context.ignore_min_sublevels_up = true; |
| |
| /* |
| * 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, |
| QTW_EXAMINE_RTES); |
| } |
| |
| |
| /* |
| * rangeTableEntry_used - detect whether an RTE is referenced somewhere |
| * in var nodes or join or setOp trees of a query or expression. |
| */ |
| |
| typedef struct |
| { |
| int rt_index; |
| int sublevels_up; |
| } rangeTableEntry_used_context; |
| |
| static bool |
| rangeTableEntry_used_walker(Node *node, |
| rangeTableEntry_used_context *context) |
| { |
| if (node == NULL) |
| return false; |
| if (IsA(node, Var)) |
| { |
| Var *var = (Var *) node; |
| |
| if (var->varlevelsup == context->sublevels_up && |
| var->varno == context->rt_index) |
| return true; |
| return false; |
| } |
| if (IsA(node, RangeTblRef)) |
| { |
| RangeTblRef *rtr = (RangeTblRef *) node; |
| |
| 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; |
| |
| 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, OuterJoinInfo)); |
| Assert(!IsA(node, InClauseInfo)); |
| Assert(!IsA(node, AppendRelInfo)); |
| |
| 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); |
| } |
| |
| 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. |
| */ |
| |
| typedef struct |
| { |
| 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; |
| |
| 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); |
| } |
| |
| bool |
| attribute_used(Node *node, int rt_index, int attno, int sublevels_up) |
| { |
| attribute_used_context context; |
| |
| context.rt_index = rt_index; |
| context.attno = attno; |
| 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, |
| attribute_used_walker, |
| (void *) &context, |
| 0); |
| } |
| |
| |
| /* |
| * 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) |
| { |
| Query *selectquery; |
| RangeTblEntry *selectrte; |
| RangeTblRef *rtr; |
| |
| if (subquery_ptr) |
| *subquery_ptr = NULL; |
| |
| if (parsetree == NULL) |
| return parsetree; |
| if (parsetree->commandType != CMD_INSERT) |
| return parsetree; |
| |
| /* |
| * 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 (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 (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, "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, "could not find rule placeholders"); |
| return NULL; /* not reached */ |
| } |
| |
| |
| /* |
| * Add the given qualifier condition to the query's WHERE clause |
| */ |
| void |
| 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 = copyObject(qual); |
| |
| parsetree->jointree->quals = make_and_qual(parsetree->jointree->quals, |
| copy); |
| |
| /* |
| * We had better not have stuck an aggregate into the WHERE clause. |
| */ |
| Assert(!checkExprHasAggs(copy)); |
| |
| /* |
| * 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); |
| } |
| |
| |
| /* |
| * 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 |
| AddInvertedQual(Query *parsetree, Node *qual) |
| { |
| BooleanTest *invqual; |
| |
| if (qual == NULL) |
| return; |
| |
| /* Need not copy input qual, because AddQual will... */ |
| invqual = makeNode(BooleanTest); |
| invqual->arg = (Expr *) qual; |
| invqual->booltesttype = IS_NOT_TRUE; |
| |
| AddQual(parsetree, (Node *) invqual); |
| } |
| |
| |
| /* |
| * ResolveNew - replace Vars with corresponding items from a targetlist |
| * |
| * Vars matching target_varno and sublevels_up are replaced by the |
| * entry with matching resno from targetlist, if there is one. |
| * If not, we either change the unmatched Var's varno to update_varno |
| * (when event == CMD_UPDATE) or replace it with a constant NULL. |
| * |
| * The caller must also provide target_rte, the RTE describing the target |
| * relation. This is needed to handle whole-row Vars referencing the target. |
| * We expand such Vars into RowExpr constructs. |
| * |
| * 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, -1), |
| InvalidOid, -1, |
| var->vartype, |
| COERCE_IMPLICIT_CAST, |
| -1, |
| 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 (node == NULL) |
| return NULL; |
| if (IsA(node, Var)) |
| { |
| Var *var = (Var *) node; |
| int this_varno = (int) var->varno; |
| int this_varlevelsup = (int) var->varlevelsup; |
| |
| if (this_varno == context->target_varno && |
| this_varlevelsup == context->sublevels_up) |
| { |
| if (var->varattno == InvalidAttrNumber) |
| { |
| /* 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, -1, |
| (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 */ |
| } |
| |
| 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; |
| } |
| return expression_tree_mutator(node, ResolveNew_mutator, |
| (void *) 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, we don't want to increment sublevels_up. |
| */ |
| result = query_or_expression_tree_mutator(node, |
| ResolveNew_mutator, |
| (void *) &context, |
| 0); |
| |
| if (context.inserted_sublink) |
| { |
| if (IsA(result, Query)) |
| ((Query *) result)->hasSubLinks = true; |
| |
| /* |
| * Note: if we're called on a non-Query node then it's the caller's |
| * responsibility to update hasSubLinks in the ancestor Query. This is |
| * pretty fragile and perhaps should be rethought ... |
| */ |
| } |
| |
| return result; |
| } |