PostgreSQL Source Code  git master
rewriteHandler.c File Reference
#include "postgres.h"
#include "access/sysattr.h"
#include "catalog/dependency.h"
#include "catalog/pg_type.h"
#include "commands/trigger.h"
#include "foreign/fdwapi.h"
#include "nodes/makefuncs.h"
#include "nodes/nodeFuncs.h"
#include "parser/analyze.h"
#include "parser/parse_coerce.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteHandler.h"
#include "rewrite/rewriteManip.h"
#include "rewrite/rowsecurity.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
Include dependency graph for rewriteHandler.c:

Go to the source code of this file.

Data Structures

struct  rewrite_event
 
struct  acquireLocksOnSubLinks_context
 

Macros

#define ALL_EVENTS   ((1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE))
 

Typedefs

typedef struct rewrite_event rewrite_event
 
typedef struct acquireLocksOnSubLinks_context acquireLocksOnSubLinks_context
 

Functions

static bool acquireLocksOnSubLinks (Node *node, acquireLocksOnSubLinks_context *context)
 
static QueryrewriteRuleAction (Query *parsetree, Query *rule_action, Node *rule_qual, int rt_index, CmdType event, bool *returning_flag)
 
static ListadjustJoinTreeList (Query *parsetree, bool removert, int rt_index)
 
static ListrewriteTargetListIU (List *targetList, CmdType commandType, OverridingKind override, Relation target_relation, int result_rti, List **attrno_list)
 
static TargetEntryprocess_matched_tle (TargetEntry *src_tle, TargetEntry *prior_tle, const char *attrName)
 
static Nodeget_assignment_input (Node *node)
 
static void rewriteValuesRTE (RangeTblEntry *rte, Relation target_relation, List *attrnos)
 
static void markQueryForLocking (Query *qry, Node *jtnode, LockClauseStrength strength, LockWaitPolicy waitPolicy, bool pushedDown)
 
static ListmatchLocks (CmdType event, RuleLock *rulelocks, int varno, Query *parsetree, bool *hasUpdate)
 
static QueryfireRIRrules (Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
 
static bool view_has_instead_trigger (Relation view, CmdType event)
 
static Bitmapsetadjust_view_column_set (Bitmapset *cols, List *targetlist)
 
void AcquireRewriteLocks (Query *parsetree, bool forExecute, bool forUpdatePushedDown)
 
Nodebuild_column_default (Relation rel, int attrno)
 
static bool searchForDefault (RangeTblEntry *rte)
 
void rewriteTargetListUD (Query *parsetree, RangeTblEntry *target_rte, Relation target_relation)
 
static QueryApplyRetrieveRule (Query *parsetree, RewriteRule *rule, int rt_index, Relation relation, List *activeRIRs, bool forUpdatePushedDown)
 
static bool fireRIRonSubLink (Node *node, List *activeRIRs)
 
static QueryCopyAndAddInvertedQual (Query *parsetree, Node *rule_qual, int rt_index, CmdType event)
 
static ListfireRules (Query *parsetree, int rt_index, CmdType event, List *locks, bool *instead_flag, bool *returning_flag, Query **qual_product)
 
Queryget_view_query (Relation view)
 
static const char * view_col_is_auto_updatable (RangeTblRef *rtr, TargetEntry *tle)
 
const char * view_query_is_auto_updatable (Query *viewquery, bool check_cols)
 
static const char * view_cols_are_auto_updatable (Query *viewquery, Bitmapset *required_cols, Bitmapset **updatable_cols, char **non_updatable_col)
 
int relation_is_updatable (Oid reloid, bool include_triggers, Bitmapset *include_cols)
 
static QueryrewriteTargetView (Query *parsetree, Relation view)
 
static ListRewriteQuery (Query *parsetree, List *rewrite_events)
 
ListQueryRewrite (Query *parsetree)
 

Macro Definition Documentation

◆ ALL_EVENTS

#define ALL_EVENTS   ((1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE))

Referenced by relation_is_updatable().

Typedef Documentation

◆ acquireLocksOnSubLinks_context

◆ rewrite_event

Function Documentation

◆ acquireLocksOnSubLinks()

static bool acquireLocksOnSubLinks ( Node node,
acquireLocksOnSubLinks_context context 
)
static

Definition at line 291 of file rewriteHandler.c.

References AcquireRewriteLocks(), expression_tree_walker(), acquireLocksOnSubLinks_context::for_execute, IsA, and SubLink::subselect.

Referenced by AcquireRewriteLocks(), CopyAndAddInvertedQual(), fireRIRrules(), rewriteRuleAction(), and rewriteTargetView().

292 {
293  if (node == NULL)
294  return false;
295  if (IsA(node, SubLink))
296  {
297  SubLink *sub = (SubLink *) node;
298 
299  /* Do what we came for */
301  context->for_execute,
302  false);
303  /* Fall through to process lefthand args of SubLink */
304  }
305 
306  /*
307  * Do NOT recurse into Query nodes, because AcquireRewriteLocks already
308  * processed subselects of subselects for us.
309  */
310  return expression_tree_walker(node, acquireLocksOnSubLinks, context);
311 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
void AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown)
bool expression_tree_walker(Node *node, bool(*walker)(), void *context)
Definition: nodeFuncs.c:1834

◆ AcquireRewriteLocks()

void AcquireRewriteLocks ( Query parsetree,
bool  forExecute,
bool  forUpdatePushedDown 
)

Definition at line 132 of file rewriteHandler.c.

References AccessShareLock, acquireLocksOnSubLinks(), AcquireRewriteLocks(), Assert, Query::cteList, CommonTableExpr::ctequery, elog, ERROR, acquireLocksOnSubLinks_context::for_execute, get_parse_rowmark(), get_rte_attribute_is_dropped(), Query::hasSubLinks, heap_close, heap_open(), IsA, RangeTblEntry::joinaliasvars, lappend(), lfirst, NIL, NoLock, QTW_IGNORE_RC_SUBQUERIES, query_tree_walker(), RelationData::rd_rel, RangeTblEntry::relid, RangeTblEntry::relkind, Query::resultRelation, RowExclusiveLock, RowShareLock, rt_fetch, Query::rtable, RTE_JOIN, RTE_RELATION, RTE_SUBQUERY, RangeTblEntry::rtekind, strip_implicit_coercions(), RangeTblEntry::subquery, Var::varattno, Var::varlevelsup, and Var::varno.

Referenced by acquireLocksOnSubLinks(), AcquireRewriteLocks(), ApplyRetrieveRule(), get_query_def(), make_ruledef(), refresh_matview_datafill(), and rewriteRuleAction().

135 {
136  ListCell *l;
137  int rt_index;
139 
140  context.for_execute = forExecute;
141 
142  /*
143  * First, process RTEs of the current query level.
144  */
145  rt_index = 0;
146  foreach(l, parsetree->rtable)
147  {
148  RangeTblEntry *rte = (RangeTblEntry *) lfirst(l);
149  Relation rel;
150  LOCKMODE lockmode;
151  List *newaliasvars;
152  Index curinputvarno;
153  RangeTblEntry *curinputrte;
154  ListCell *ll;
155 
156  ++rt_index;
157  switch (rte->rtekind)
158  {
159  case RTE_RELATION:
160 
161  /*
162  * Grab the appropriate lock type for the relation, and do not
163  * release it until end of transaction. This protects the
164  * rewriter and planner against schema changes mid-query.
165  *
166  * Assuming forExecute is true, this logic must match what the
167  * executor will do, else we risk lock-upgrade deadlocks.
168  */
169  if (!forExecute)
170  lockmode = AccessShareLock;
171  else if (rt_index == parsetree->resultRelation)
172  lockmode = RowExclusiveLock;
173  else if (forUpdatePushedDown ||
174  get_parse_rowmark(parsetree, rt_index) != NULL)
175  lockmode = RowShareLock;
176  else
177  lockmode = AccessShareLock;
178 
179  rel = heap_open(rte->relid, lockmode);
180 
181  /*
182  * While we have the relation open, update the RTE's relkind,
183  * just in case it changed since this rule was made.
184  */
185  rte->relkind = rel->rd_rel->relkind;
186 
187  heap_close(rel, NoLock);
188  break;
189 
190  case RTE_JOIN:
191 
192  /*
193  * Scan the join's alias var list to see if any columns have
194  * been dropped, and if so replace those Vars with null
195  * pointers.
196  *
197  * Since a join has only two inputs, we can expect to see
198  * multiple references to the same input RTE; optimize away
199  * multiple fetches.
200  */
201  newaliasvars = NIL;
202  curinputvarno = 0;
203  curinputrte = NULL;
204  foreach(ll, rte->joinaliasvars)
205  {
206  Var *aliasitem = (Var *) lfirst(ll);
207  Var *aliasvar = aliasitem;
208 
209  /* Look through any implicit coercion */
210  aliasvar = (Var *) strip_implicit_coercions((Node *) aliasvar);
211 
212  /*
213  * If the list item isn't a simple Var, then it must
214  * represent a merged column, ie a USING column, and so it
215  * couldn't possibly be dropped, since it's referenced in
216  * the join clause. (Conceivably it could also be a null
217  * pointer already? But that's OK too.)
218  */
219  if (aliasvar && IsA(aliasvar, Var))
220  {
221  /*
222  * The elements of an alias list have to refer to
223  * earlier RTEs of the same rtable, because that's the
224  * order the planner builds things in. So we already
225  * processed the referenced RTE, and so it's safe to
226  * use get_rte_attribute_is_dropped on it. (This might
227  * not hold after rewriting or planning, but it's OK
228  * to assume here.)
229  */
230  Assert(aliasvar->varlevelsup == 0);
231  if (aliasvar->varno != curinputvarno)
232  {
233  curinputvarno = aliasvar->varno;
234  if (curinputvarno >= rt_index)
235  elog(ERROR, "unexpected varno %d in JOIN RTE %d",
236  curinputvarno, rt_index);
237  curinputrte = rt_fetch(curinputvarno,
238  parsetree->rtable);
239  }
240  if (get_rte_attribute_is_dropped(curinputrte,
241  aliasvar->varattno))
242  {
243  /* Replace the join alias item with a NULL */
244  aliasitem = NULL;
245  }
246  }
247  newaliasvars = lappend(newaliasvars, aliasitem);
248  }
249  rte->joinaliasvars = newaliasvars;
250  break;
251 
252  case RTE_SUBQUERY:
253 
254  /*
255  * The subquery RTE itself is all right, but we have to
256  * recurse to process the represented subquery.
257  */
259  forExecute,
260  (forUpdatePushedDown ||
261  get_parse_rowmark(parsetree, rt_index) != NULL));
262  break;
263 
264  default:
265  /* ignore other types of RTEs */
266  break;
267  }
268  }
269 
270  /* Recurse into subqueries in WITH */
271  foreach(l, parsetree->cteList)
272  {
273  CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
274 
275  AcquireRewriteLocks((Query *) cte->ctequery, forExecute, false);
276  }
277 
278  /*
279  * Recurse into sublink subqueries, too. But we already did the ones in
280  * the rtable and cteList.
281  */
282  if (parsetree->hasSubLinks)
283  query_tree_walker(parsetree, acquireLocksOnSubLinks, &context,
285 }
#define NIL
Definition: pg_list.h:69
bool query_tree_walker(Query *query, bool(*walker)(), void *context, int flags)
Definition: nodeFuncs.c:2245
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
List * joinaliasvars
Definition: parsenodes.h:995
Index varlevelsup
Definition: primnodes.h:173
RowMarkClause * get_parse_rowmark(Query *qry, Index rtindex)
static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
int LOCKMODE
Definition: lockdefs.h:26
int resultRelation
Definition: parsenodes.h:120
#define AccessShareLock
Definition: lockdefs.h:36
Definition: nodes.h:512
AttrNumber varattno
Definition: primnodes.h:168
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:114
Definition: primnodes.h:163
#define QTW_IGNORE_RC_SUBQUERIES
Definition: nodeFuncs.h:22
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
void AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown)
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
List * lappend(List *list, void *datum)
Definition: list.c:128
Index varno
Definition: primnodes.h:166
#define RowShareLock
Definition: lockdefs.h:37
bool get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
unsigned int Index
Definition: c.h:423
#define Assert(condition)
Definition: c.h:680
#define lfirst(lc)
Definition: pg_list.h:106
RTEKind rtekind
Definition: parsenodes.h:951
List * cteList
Definition: parsenodes.h:133
Query * subquery
Definition: parsenodes.h:974
bool hasSubLinks
Definition: parsenodes.h:126
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
Node * strip_implicit_coercions(Node *node)
Definition: nodeFuncs.c:612

◆ adjust_view_column_set()

static Bitmapset * adjust_view_column_set ( Bitmapset cols,
List targetlist 
)
static

Definition at line 2641 of file rewriteHandler.c.

References bms_add_member(), bms_next_member(), castNode, elog, ERROR, TargetEntry::expr, FirstLowInvalidHeapAttributeNumber, get_tle_by_resno(), InvalidAttrNumber, IsA, lfirst_node, TargetEntry::resjunk, and Var::varattno.

Referenced by relation_is_updatable(), and rewriteTargetView().

2642 {
2643  Bitmapset *result = NULL;
2644  int col;
2645 
2646  col = -1;
2647  while ((col = bms_next_member(cols, col)) >= 0)
2648  {
2649  /* bit numbers are offset by FirstLowInvalidHeapAttributeNumber */
2651 
2652  if (attno == InvalidAttrNumber)
2653  {
2654  /*
2655  * There's a whole-row reference to the view. For permissions
2656  * purposes, treat it as a reference to each column available from
2657  * the view. (We should *not* convert this to a whole-row
2658  * reference to the base relation, since the view may not touch
2659  * all columns of the base relation.)
2660  */
2661  ListCell *lc;
2662 
2663  foreach(lc, targetlist)
2664  {
2665  TargetEntry *tle = lfirst_node(TargetEntry, lc);
2666  Var *var;
2667 
2668  if (tle->resjunk)
2669  continue;
2670  var = castNode(Var, tle->expr);
2671  result = bms_add_member(result,
2672  var->varattno - FirstLowInvalidHeapAttributeNumber);
2673  }
2674  }
2675  else
2676  {
2677  /*
2678  * Views do not have system columns, so we do not expect to see
2679  * any other system attnos here. If we do find one, the error
2680  * case will apply.
2681  */
2682  TargetEntry *tle = get_tle_by_resno(targetlist, attno);
2683 
2684  if (tle != NULL && !tle->resjunk && IsA(tle->expr, Var))
2685  {
2686  Var *var = (Var *) tle->expr;
2687 
2688  result = bms_add_member(result,
2689  var->varattno - FirstLowInvalidHeapAttributeNumber);
2690  }
2691  else
2692  elog(ERROR, "attribute number %d not found in view targetlist",
2693  attno);
2694  }
2695  }
2696 
2697  return result;
2698 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
int bms_next_member(const Bitmapset *a, int prevbit)
Definition: bitmapset.c:1053
AttrNumber varattno
Definition: primnodes.h:168
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:28
Definition: primnodes.h:163
bool resjunk
Definition: primnodes.h:1382
#define ERROR
Definition: elog.h:43
#define lfirst_node(type, lc)
Definition: pg_list.h:109
Expr * expr
Definition: primnodes.h:1375
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:742
#define InvalidAttrNumber
Definition: attnum.h:23
TargetEntry * get_tle_by_resno(List *tlist, AttrNumber resno)
#define elog
Definition: elog.h:219
int16 AttrNumber
Definition: attnum.h:21

◆ adjustJoinTreeList()

static List * adjustJoinTreeList ( Query parsetree,
bool  removert,
int  rt_index 
)
static

Definition at line 635 of file rewriteHandler.c.

References copyObject, FromExpr::fromlist, IsA, Query::jointree, lfirst, list_delete_ptr(), and RangeTblRef::rtindex.

Referenced by rewriteRuleAction().

636 {
637  List *newjointree = copyObject(parsetree->jointree->fromlist);
638  ListCell *l;
639 
640  if (removert)
641  {
642  foreach(l, newjointree)
643  {
644  RangeTblRef *rtr = lfirst(l);
645 
646  if (IsA(rtr, RangeTblRef) &&
647  rtr->rtindex == rt_index)
648  {
649  newjointree = list_delete_ptr(newjointree, rtr);
650 
651  /*
652  * foreach is safe because we exit loop after list_delete...
653  */
654  break;
655  }
656  }
657  }
658  return newjointree;
659 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
FromExpr * jointree
Definition: parsenodes.h:136
List * fromlist
Definition: primnodes.h:1478
List * list_delete_ptr(List *list, void *datum)
Definition: list.c:590
#define lfirst(lc)
Definition: pg_list.h:106
#define copyObject(obj)
Definition: nodes.h:625
Definition: pg_list.h:45

◆ ApplyRetrieveRule()

static Query* ApplyRetrieveRule ( Query parsetree,
RewriteRule rule,
int  rt_index,
Relation  relation,
List activeRIRs,
bool  forUpdatePushedDown 
)
static

Definition at line 1451 of file rewriteHandler.c.

References AcquireRewriteLocks(), RewriteRule::actions, Assert, ChangeVarNodes(), RangeTblEntry::checkAsUser, CMD_DELETE, CMD_INSERT, CMD_UPDATE, Query::commandType, copyObject, elog, ERROR, fireRIRrules(), get_parse_rowmark(), RangeTblEntry::inh, RangeTblEntry::insertedCols, InvalidOid, Query::jointree, lappend(), linitial, list_length(), makeTargetEntry(), makeWholeRowVar(), markQueryForLocking(), PRS2_OLD_VARNO, pstrdup(), RewriteRule::qual, RelationData::rd_id, RelationIsSecurityView, RangeTblEntry::relid, RangeTblEntry::requiredPerms, Query::resultRelation, Query::returningList, rt_fetch, Query::rtable, RTE_SUBQUERY, RangeTblEntry::rtekind, RangeTblEntry::security_barrier, RangeTblEntry::selectedCols, RowMarkClause::strength, RangeTblEntry::subquery, Query::targetList, RangeTblEntry::updatedCols, and RowMarkClause::waitPolicy.

Referenced by fireRIRrules().

1457 {
1458  Query *rule_action;
1459  RangeTblEntry *rte,
1460  *subrte;
1461  RowMarkClause *rc;
1462 
1463  if (list_length(rule->actions) != 1)
1464  elog(ERROR, "expected just one rule action");
1465  if (rule->qual != NULL)
1466  elog(ERROR, "cannot handle qualified ON SELECT rule");
1467 
1468  if (rt_index == parsetree->resultRelation)
1469  {
1470  /*
1471  * We have a view as the result relation of the query, and it wasn't
1472  * rewritten by any rule. This case is supported if there is an
1473  * INSTEAD OF trigger that will trap attempts to insert/update/delete
1474  * view rows. The executor will check that; for the moment just plow
1475  * ahead. We have two cases:
1476  *
1477  * For INSERT, we needn't do anything. The unmodified RTE will serve
1478  * fine as the result relation.
1479  *
1480  * For UPDATE/DELETE, we need to expand the view so as to have source
1481  * data for the operation. But we also need an unmodified RTE to
1482  * serve as the target. So, copy the RTE and add the copy to the
1483  * rangetable. Note that the copy does not get added to the jointree.
1484  * Also note that there's a hack in fireRIRrules to avoid calling this
1485  * function again when it arrives at the copied RTE.
1486  */
1487  if (parsetree->commandType == CMD_INSERT)
1488  return parsetree;
1489  else if (parsetree->commandType == CMD_UPDATE ||
1490  parsetree->commandType == CMD_DELETE)
1491  {
1492  RangeTblEntry *newrte;
1493  Var *var;
1494  TargetEntry *tle;
1495 
1496  rte = rt_fetch(rt_index, parsetree->rtable);
1497  newrte = copyObject(rte);
1498  parsetree->rtable = lappend(parsetree->rtable, newrte);
1499  parsetree->resultRelation = list_length(parsetree->rtable);
1500 
1501  /*
1502  * There's no need to do permissions checks twice, so wipe out the
1503  * permissions info for the original RTE (we prefer to keep the
1504  * bits set on the result RTE).
1505  */
1506  rte->requiredPerms = 0;
1507  rte->checkAsUser = InvalidOid;
1508  rte->selectedCols = NULL;
1509  rte->insertedCols = NULL;
1510  rte->updatedCols = NULL;
1511 
1512  /*
1513  * For the most part, Vars referencing the view should remain as
1514  * they are, meaning that they implicitly represent OLD values.
1515  * But in the RETURNING list if any, we want such Vars to
1516  * represent NEW values, so change them to reference the new RTE.
1517  *
1518  * Since ChangeVarNodes scribbles on the tree in-place, copy the
1519  * RETURNING list first for safety.
1520  */
1521  parsetree->returningList = copyObject(parsetree->returningList);
1522  ChangeVarNodes((Node *) parsetree->returningList, rt_index,
1523  parsetree->resultRelation, 0);
1524 
1525  /*
1526  * To allow the executor to compute the original view row to pass
1527  * to the INSTEAD OF trigger, we add a resjunk whole-row Var
1528  * referencing the original RTE. This will later get expanded
1529  * into a RowExpr computing all the OLD values of the view row.
1530  */
1531  var = makeWholeRowVar(rte, rt_index, 0, false);
1532  tle = makeTargetEntry((Expr *) var,
1533  list_length(parsetree->targetList) + 1,
1534  pstrdup("wholerow"),
1535  true);
1536 
1537  parsetree->targetList = lappend(parsetree->targetList, tle);
1538 
1539  /* Now, continue with expanding the original view RTE */
1540  }
1541  else
1542  elog(ERROR, "unrecognized commandType: %d",
1543  (int) parsetree->commandType);
1544  }
1545 
1546  /*
1547  * If FOR [KEY] UPDATE/SHARE of view, be sure we get right initial lock on
1548  * the relations it references.
1549  */
1550  rc = get_parse_rowmark(parsetree, rt_index);
1551  forUpdatePushedDown |= (rc != NULL);
1552 
1553  /*
1554  * Make a modifiable copy of the view query, and acquire needed locks on
1555  * the relations it mentions.
1556  */
1557  rule_action = copyObject(linitial(rule->actions));
1558 
1559  AcquireRewriteLocks(rule_action, true, forUpdatePushedDown);
1560 
1561  /*
1562  * Recursively expand any view references inside the view.
1563  */
1564  rule_action = fireRIRrules(rule_action, activeRIRs, forUpdatePushedDown);
1565 
1566  /*
1567  * Now, plug the view query in as a subselect, replacing the relation's
1568  * original RTE.
1569  */
1570  rte = rt_fetch(rt_index, parsetree->rtable);
1571 
1572  rte->rtekind = RTE_SUBQUERY;
1573  rte->relid = InvalidOid;
1574  rte->security_barrier = RelationIsSecurityView(relation);
1575  rte->subquery = rule_action;
1576  rte->inh = false; /* must not be set for a subquery */
1577 
1578  /*
1579  * We move the view's permission check data down to its rangetable. The
1580  * checks will actually be done against the OLD entry therein.
1581  */
1582  subrte = rt_fetch(PRS2_OLD_VARNO, rule_action->rtable);
1583  Assert(subrte->relid == relation->rd_id);
1584  subrte->requiredPerms = rte->requiredPerms;
1585  subrte->checkAsUser = rte->checkAsUser;
1586  subrte->selectedCols = rte->selectedCols;
1587  subrte->insertedCols = rte->insertedCols;
1588  subrte->updatedCols = rte->updatedCols;
1589 
1590  rte->requiredPerms = 0; /* no permission check on subquery itself */
1591  rte->checkAsUser = InvalidOid;
1592  rte->selectedCols = NULL;
1593  rte->insertedCols = NULL;
1594  rte->updatedCols = NULL;
1595 
1596  /*
1597  * If FOR [KEY] UPDATE/SHARE of view, mark all the contained tables as
1598  * implicit FOR [KEY] UPDATE/SHARE, the same as the parser would have done
1599  * if the view's subquery had been written out explicitly.
1600  *
1601  * Note: we don't consider forUpdatePushedDown here; such marks will be
1602  * made by recursing from the upper level in markQueryForLocking.
1603  */
1604  if (rc != NULL)
1605  markQueryForLocking(rule_action, (Node *) rule_action->jointree,
1606  rc->strength, rc->waitPolicy, true);
1607 
1608  return parsetree;
1609 }
static void markQueryForLocking(Query *qry, Node *jtnode, LockClauseStrength strength, LockWaitPolicy waitPolicy, bool pushedDown)
Node * qual
Definition: prs2lock.h:28
RowMarkClause * get_parse_rowmark(Query *qry, Index rtindex)
FromExpr * jointree
Definition: parsenodes.h:136
char * pstrdup(const char *in)
Definition: mcxt.c:1063
int resultRelation
Definition: parsenodes.h:120
Definition: nodes.h:512
Var * makeWholeRowVar(RangeTblEntry *rte, Index varno, Index varlevelsup, bool allowScalar)
Definition: makefuncs.c:134
AclMode requiredPerms
Definition: parsenodes.h:1059
Definition: primnodes.h:163
LockClauseStrength strength
Definition: parsenodes.h:1314
List * targetList
Definition: parsenodes.h:138
Bitmapset * selectedCols
Definition: parsenodes.h:1061
#define linitial(l)
Definition: pg_list.h:111
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
void AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown)
List * actions
Definition: prs2lock.h:29
List * returningList
Definition: parsenodes.h:144
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:237
Oid rd_id
Definition: rel.h:116
List * lappend(List *list, void *datum)
Definition: list.c:128
#define PRS2_OLD_VARNO
Definition: primnodes.h:160
bool security_barrier
Definition: parsenodes.h:975
#define InvalidOid
Definition: postgres_ext.h:36
Bitmapset * updatedCols
Definition: parsenodes.h:1063
CmdType commandType
Definition: parsenodes.h:110
#define Assert(condition)
Definition: c.h:680
static Query * fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
static int list_length(const List *l)
Definition: pg_list.h:89
LockWaitPolicy waitPolicy
Definition: parsenodes.h:1315
RTEKind rtekind
Definition: parsenodes.h:951
Query * subquery
Definition: parsenodes.h:974
Bitmapset * insertedCols
Definition: parsenodes.h:1062
#define RelationIsSecurityView(relation)
Definition: rel.h:356
void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
Definition: rewriteManip.c:607
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:625

◆ build_column_default()

Node* build_column_default ( Relation  rel,
int  attrno 
)

Definition at line 1117 of file rewriteHandler.c.

References COERCE_IMPLICIT_CAST, coerce_to_target_type(), COERCION_ASSIGNMENT, tupleDesc::constr, tupleConstr::defval, ereport, errcode(), errhint(), errmsg(), ERROR, exprType(), format_type_be(), get_typdefault(), NameStr, tupleConstr::num_defval, RelationData::rd_att, stringToNode(), and TupleDescAttr.

Referenced by ATExecAddColumn(), ATExecAlterColumnType(), BeginCopyFrom(), rewriteTargetListIU(), rewriteValuesRTE(), and slot_fill_defaults().

1118 {
1119  TupleDesc rd_att = rel->rd_att;
1120  Form_pg_attribute att_tup = TupleDescAttr(rd_att, attrno - 1);
1121  Oid atttype = att_tup->atttypid;
1122  int32 atttypmod = att_tup->atttypmod;
1123  Node *expr = NULL;
1124  Oid exprtype;
1125 
1126  /*
1127  * Scan to see if relation has a default for this column.
1128  */
1129  if (rd_att->constr && rd_att->constr->num_defval > 0)
1130  {
1131  AttrDefault *defval = rd_att->constr->defval;
1132  int ndef = rd_att->constr->num_defval;
1133 
1134  while (--ndef >= 0)
1135  {
1136  if (attrno == defval[ndef].adnum)
1137  {
1138  /*
1139  * Found it, convert string representation to node tree.
1140  */
1141  expr = stringToNode(defval[ndef].adbin);
1142  break;
1143  }
1144  }
1145  }
1146 
1147  if (expr == NULL)
1148  {
1149  /*
1150  * No per-column default, so look for a default for the type itself.
1151  */
1152  expr = get_typdefault(atttype);
1153  }
1154 
1155  if (expr == NULL)
1156  return NULL; /* No default anywhere */
1157 
1158  /*
1159  * Make sure the value is coerced to the target column type; this will
1160  * generally be true already, but there seem to be some corner cases
1161  * involving domain defaults where it might not be true. This should match
1162  * the parser's processing of non-defaulted expressions --- see
1163  * transformAssignedExpr().
1164  */
1165  exprtype = exprType(expr);
1166 
1167  expr = coerce_to_target_type(NULL, /* no UNKNOWN params here */
1168  expr, exprtype,
1169  atttype, atttypmod,
1172  -1);
1173  if (expr == NULL)
1174  ereport(ERROR,
1175  (errcode(ERRCODE_DATATYPE_MISMATCH),
1176  errmsg("column \"%s\" is of type %s"
1177  " but default expression is of type %s",
1178  NameStr(att_tup->attname),
1179  format_type_be(atttype),
1180  format_type_be(exprtype)),
1181  errhint("You will need to rewrite or cast the expression.")));
1182 
1183  return expr;
1184 }
void * stringToNode(char *str)
Definition: read.c:38
int errhint(const char *fmt,...)
Definition: elog.c:987
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
Definition: nodes.h:512
int errcode(int sqlerrcode)
Definition: elog.c:575
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
unsigned int Oid
Definition: postgres_ext.h:31
signed int int32
Definition: c.h:294
AttrDefault * defval
Definition: tupdesc.h:39
#define ERROR
Definition: elog.h:43
Node * coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype, Oid targettype, int32 targettypmod, CoercionContext ccontext, CoercionForm cformat, int location)
Definition: parse_coerce.c:78
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define ereport(elevel, rest)
Definition: elog.h:122
TupleDesc rd_att
Definition: rel.h:115
uint16 num_defval
Definition: tupdesc.h:41
Node * get_typdefault(Oid typid)
Definition: lsyscache.c:2217
TupleConstr * constr
Definition: tupdesc.h:84
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define NameStr(name)
Definition: c.h:557

◆ CopyAndAddInvertedQual()

static Query* CopyAndAddInvertedQual ( Query parsetree,
Node rule_qual,
int  rt_index,
CmdType  event 
)
static

Definition at line 1978 of file rewriteHandler.c.

References acquireLocksOnSubLinks(), AddInvertedQual(), ChangeVarNodes(), CMD_INSERT, CMD_UPDATE, copyObject, acquireLocksOnSubLinks_context::for_execute, Query::hasSubLinks, PRS2_NEW_VARNO, PRS2_OLD_VARNO, REPLACEVARS_CHANGE_VARNO, REPLACEVARS_SUBSTITUTE_NULL, ReplaceVarsFromTargetList(), rt_fetch, Query::rtable, and Query::targetList.

Referenced by fireRules().

1982 {
1983  /* Don't scribble on the passed qual (it's in the relcache!) */
1984  Node *new_qual = copyObject(rule_qual);
1986 
1987  context.for_execute = true;
1988 
1989  /*
1990  * In case there are subqueries in the qual, acquire necessary locks and
1991  * fix any deleted JOIN RTE entries. (This is somewhat redundant with
1992  * rewriteRuleAction, but not entirely ... consider restructuring so that
1993  * we only need to process the qual this way once.)
1994  */
1995  (void) acquireLocksOnSubLinks(new_qual, &context);
1996 
1997  /* Fix references to OLD */
1998  ChangeVarNodes(new_qual, PRS2_OLD_VARNO, rt_index, 0);
1999  /* Fix references to NEW */
2000  if (event == CMD_INSERT || event == CMD_UPDATE)
2001  new_qual = ReplaceVarsFromTargetList(new_qual,
2003  0,
2004  rt_fetch(rt_index,
2005  parsetree->rtable),
2006  parsetree->targetList,
2007  (event == CMD_UPDATE) ?
2010  rt_index,
2011  &parsetree->hasSubLinks);
2012  /* And attach the fixed qual */
2013  AddInvertedQual(parsetree, new_qual);
2014 
2015  return parsetree;
2016 }
static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
void AddInvertedQual(Query *parsetree, Node *qual)
Definition: nodes.h:512
List * targetList
Definition: parsenodes.h:138
List * rtable
Definition: parsenodes.h:135
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
#define PRS2_OLD_VARNO
Definition: primnodes.h:160
Node * ReplaceVarsFromTargetList(Node *node, int target_varno, int sublevels_up, RangeTblEntry *target_rte, List *targetlist, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks)
bool hasSubLinks
Definition: parsenodes.h:126
void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
Definition: rewriteManip.c:607
#define copyObject(obj)
Definition: nodes.h:625
#define PRS2_NEW_VARNO
Definition: primnodes.h:161

◆ fireRIRonSubLink()

static bool fireRIRonSubLink ( Node node,
List activeRIRs 
)
static

Definition at line 1683 of file rewriteHandler.c.

References expression_tree_walker(), fireRIRrules(), IsA, and SubLink::subselect.

Referenced by fireRIRrules().

1684 {
1685  if (node == NULL)
1686  return false;
1687  if (IsA(node, SubLink))
1688  {
1689  SubLink *sub = (SubLink *) node;
1690 
1691  /* Do what we came for */
1692  sub->subselect = (Node *) fireRIRrules((Query *) sub->subselect,
1693  activeRIRs, false);
1694  /* Fall through to process lefthand args of SubLink */
1695  }
1696 
1697  /*
1698  * Do NOT recurse into Query nodes, because fireRIRrules already processed
1699  * subselects of subselects for us.
1700  */
1702  (void *) activeRIRs);
1703 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
Definition: nodes.h:512
static bool fireRIRonSubLink(Node *node, List *activeRIRs)
static Query * fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
bool expression_tree_walker(Node *node, bool(*walker)(), void *context)
Definition: nodeFuncs.c:1834

◆ fireRIRrules()

static Query * fireRIRrules ( Query parsetree,
List activeRIRs,
bool  forUpdatePushedDown 
)
static

Definition at line 1711 of file rewriteHandler.c.

References acquireLocksOnSubLinks(), ApplyRetrieveRule(), CMD_SELECT, Query::cteList, CommonTableExpr::ctequery, ereport, errcode(), errmsg(), ERROR, RewriteRule::event, expression_tree_walker(), fireRIRonSubLink(), acquireLocksOnSubLinks_context::for_execute, get_parse_rowmark(), get_row_security_policies(), Query::hasRowSecurity, Query::hasSubLinks, heap_close, heap_open(), i, lappend(), lcons_oid(), lfirst, list_concat(), list_delete_first(), list_length(), list_member_oid(), NIL, NoLock, RuleLock::numLocks, QTW_IGNORE_RC_SUBQUERIES, query_tree_walker(), rangeTableEntry_used(), RelationData::rd_rules, RelationGetRelationName, RelationGetRelid, RangeTblEntry::relid, RangeTblEntry::relkind, RELKIND_MATVIEW, RELKIND_PARTITIONED_TABLE, RELKIND_RELATION, Query::resultRelation, rt_fetch, Query::rtable, RTE_RELATION, RTE_SUBQUERY, RangeTblEntry::rtekind, RuleLock::rules, rules, RangeTblEntry::securityQuals, RangeTblEntry::subquery, and Query::withCheckOptions.

Referenced by ApplyRetrieveRule(), fireRIRonSubLink(), and QueryRewrite().

1712 {
1713  int origResultRelation = parsetree->resultRelation;
1714  int rt_index;
1715  ListCell *lc;
1716 
1717  /*
1718  * don't try to convert this into a foreach loop, because rtable list can
1719  * get changed each time through...
1720  */
1721  rt_index = 0;
1722  while (rt_index < list_length(parsetree->rtable))
1723  {
1724  RangeTblEntry *rte;
1725  Relation rel;
1726  List *locks;
1727  RuleLock *rules;
1728  RewriteRule *rule;
1729  int i;
1730 
1731  ++rt_index;
1732 
1733  rte = rt_fetch(rt_index, parsetree->rtable);
1734 
1735  /*
1736  * A subquery RTE can't have associated rules, so there's nothing to
1737  * do to this level of the query, but we must recurse into the
1738  * subquery to expand any rule references in it.
1739  */
1740  if (rte->rtekind == RTE_SUBQUERY)
1741  {
1742  rte->subquery = fireRIRrules(rte->subquery, activeRIRs,
1743  (forUpdatePushedDown ||
1744  get_parse_rowmark(parsetree, rt_index) != NULL));
1745  continue;
1746  }
1747 
1748  /*
1749  * Joins and other non-relation RTEs can be ignored completely.
1750  */
1751  if (rte->rtekind != RTE_RELATION)
1752  continue;
1753 
1754  /*
1755  * Always ignore RIR rules for materialized views referenced in
1756  * queries. (This does not prevent refreshing MVs, since they aren't
1757  * referenced in their own query definitions.)
1758  *
1759  * Note: in the future we might want to allow MVs to be conditionally
1760  * expanded as if they were regular views, if they are not scannable.
1761  * In that case this test would need to be postponed till after we've
1762  * opened the rel, so that we could check its state.
1763  */
1764  if (rte->relkind == RELKIND_MATVIEW)
1765  continue;
1766 
1767  /*
1768  * If the table is not referenced in the query, then we ignore it.
1769  * This prevents infinite expansion loop due to new rtable entries
1770  * inserted by expansion of a rule. A table is referenced if it is
1771  * part of the join set (a source table), or is referenced by any Var
1772  * nodes, or is the result table.
1773  */
1774  if (rt_index != parsetree->resultRelation &&
1775  !rangeTableEntry_used((Node *) parsetree, rt_index, 0))
1776  continue;
1777 
1778  /*
1779  * Also, if this is a new result relation introduced by
1780  * ApplyRetrieveRule, we don't want to do anything more with it.
1781  */
1782  if (rt_index == parsetree->resultRelation &&
1783  rt_index != origResultRelation)
1784  continue;
1785 
1786  /*
1787  * We can use NoLock here since either the parser or
1788  * AcquireRewriteLocks should have locked the rel already.
1789  */
1790  rel = heap_open(rte->relid, NoLock);
1791 
1792  /*
1793  * Collect the RIR rules that we must apply
1794  */
1795  rules = rel->rd_rules;
1796  if (rules != NULL)
1797  {
1798  locks = NIL;
1799  for (i = 0; i < rules->numLocks; i++)
1800  {
1801  rule = rules->rules[i];
1802  if (rule->event != CMD_SELECT)
1803  continue;
1804 
1805  locks = lappend(locks, rule);
1806  }
1807 
1808  /*
1809  * If we found any, apply them --- but first check for recursion!
1810  */
1811  if (locks != NIL)
1812  {
1813  ListCell *l;
1814 
1815  if (list_member_oid(activeRIRs, RelationGetRelid(rel)))
1816  ereport(ERROR,
1817  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1818  errmsg("infinite recursion detected in rules for relation \"%s\"",
1819  RelationGetRelationName(rel))));
1820  activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs);
1821 
1822  foreach(l, locks)
1823  {
1824  rule = lfirst(l);
1825 
1826  parsetree = ApplyRetrieveRule(parsetree,
1827  rule,
1828  rt_index,
1829  rel,
1830  activeRIRs,
1831  forUpdatePushedDown);
1832  }
1833 
1834  activeRIRs = list_delete_first(activeRIRs);
1835  }
1836  }
1837 
1838  heap_close(rel, NoLock);
1839  }
1840 
1841  /* Recurse into subqueries in WITH */
1842  foreach(lc, parsetree->cteList)
1843  {
1844  CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
1845 
1846  cte->ctequery = (Node *)
1847  fireRIRrules((Query *) cte->ctequery, activeRIRs, false);
1848  }
1849 
1850  /*
1851  * Recurse into sublink subqueries, too. But we already did the ones in
1852  * the rtable and cteList.
1853  */
1854  if (parsetree->hasSubLinks)
1855  query_tree_walker(parsetree, fireRIRonSubLink, (void *) activeRIRs,
1857 
1858  /*
1859  * Apply any row level security policies. We do this last because it
1860  * requires special recursion detection if the new quals have sublink
1861  * subqueries, and if we did it in the loop above query_tree_walker would
1862  * then recurse into those quals a second time.
1863  */
1864  rt_index = 0;
1865  foreach(lc, parsetree->rtable)
1866  {
1867  RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
1868  Relation rel;
1869  List *securityQuals;
1870  List *withCheckOptions;
1871  bool hasRowSecurity;
1872  bool hasSubLinks;
1873 
1874  ++rt_index;
1875 
1876  /* Only normal relations can have RLS policies */
1877  if (rte->rtekind != RTE_RELATION ||
1878  (rte->relkind != RELKIND_RELATION &&
1880  continue;
1881 
1882  rel = heap_open(rte->relid, NoLock);
1883 
1884  /*
1885  * Fetch any new security quals that must be applied to this RTE.
1886  */
1887  get_row_security_policies(parsetree, rte, rt_index,
1888  &securityQuals, &withCheckOptions,
1889  &hasRowSecurity, &hasSubLinks);
1890 
1891  if (securityQuals != NIL || withCheckOptions != NIL)
1892  {
1893  if (hasSubLinks)
1894  {
1896 
1897  /*
1898  * Recursively process the new quals, checking for infinite
1899  * recursion.
1900  */
1901  if (list_member_oid(activeRIRs, RelationGetRelid(rel)))
1902  ereport(ERROR,
1903  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
1904  errmsg("infinite recursion detected in policy for relation \"%s\"",
1905  RelationGetRelationName(rel))));
1906 
1907  activeRIRs = lcons_oid(RelationGetRelid(rel), activeRIRs);
1908 
1909  /*
1910  * get_row_security_policies just passed back securityQuals
1911  * and/or withCheckOptions, and there were SubLinks, make sure
1912  * we lock any relations which are referenced.
1913  *
1914  * These locks would normally be acquired by the parser, but
1915  * securityQuals and withCheckOptions are added post-parsing.
1916  */
1917  context.for_execute = true;
1918  (void) acquireLocksOnSubLinks((Node *) securityQuals, &context);
1919  (void) acquireLocksOnSubLinks((Node *) withCheckOptions,
1920  &context);
1921 
1922  /*
1923  * Now that we have the locks on anything added by
1924  * get_row_security_policies, fire any RIR rules for them.
1925  */
1926  expression_tree_walker((Node *) securityQuals,
1927  fireRIRonSubLink, (void *) activeRIRs);
1928 
1929  expression_tree_walker((Node *) withCheckOptions,
1930  fireRIRonSubLink, (void *) activeRIRs);
1931 
1932  activeRIRs = list_delete_first(activeRIRs);
1933  }
1934 
1935  /*
1936  * Add the new security barrier quals to the start of the RTE's
1937  * list so that they get applied before any existing barrier quals
1938  * (which would have come from a security-barrier view, and should
1939  * get lower priority than RLS conditions on the table itself).
1940  */
1941  rte->securityQuals = list_concat(securityQuals,
1942  rte->securityQuals);
1943 
1944  parsetree->withCheckOptions = list_concat(withCheckOptions,
1945  parsetree->withCheckOptions);
1946  }
1947 
1948  /*
1949  * Make sure the query is marked correctly if row level security
1950  * applies, or if the new quals had sublinks.
1951  */
1952  if (hasRowSecurity)
1953  parsetree->hasRowSecurity = true;
1954  if (hasSubLinks)
1955  parsetree->hasSubLinks = true;
1956 
1957  heap_close(rel, NoLock);
1958  }
1959 
1960  return parsetree;
1961 }
#define NIL
Definition: pg_list.h:69
bool query_tree_walker(Query *query, bool(*walker)(), void *context, int flags)
Definition: nodeFuncs.c:2245
int numLocks
Definition: prs2lock.h:42
RowMarkClause * get_parse_rowmark(Query *qry, Index rtindex)
static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
void get_row_security_policies(Query *root, RangeTblEntry *rte, int rt_index, List **securityQuals, List **withCheckOptions, bool *hasRowSecurity, bool *hasSubLinks)
Definition: rowsecurity.c:107
List * securityQuals
Definition: parsenodes.h:1064
int resultRelation
Definition: parsenodes.h:120
#define RELKIND_MATVIEW
Definition: pg_class.h:165
Definition: nodes.h:512
int errcode(int sqlerrcode)
Definition: elog.c:575
List * list_concat(List *list1, List *list2)
Definition: list.c:321
List * lcons_oid(Oid datum, List *list)
Definition: list.c:295
#define heap_close(r, l)
Definition: heapam.h:97
static Query * ApplyRetrieveRule(Query *parsetree, RewriteRule *rule, int rt_index, Relation relation, List *activeRIRs, bool forUpdatePushedDown)
Definition: localtime.c:78
#define QTW_IGNORE_RC_SUBQUERIES
Definition: nodeFuncs.h:22
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
static bool fireRIRonSubLink(Node *node, List *activeRIRs)
#define NoLock
Definition: lockdefs.h:34
CmdType event
Definition: prs2lock.h:27
static struct rule * rules
Definition: zic.c:269
#define RelationGetRelationName(relation)
Definition: rel.h:445
RewriteRule ** rules
Definition: prs2lock.h:43
#define ereport(elevel, rest)
Definition: elog.h:122
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
List * lappend(List *list, void *datum)
Definition: list.c:128
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
bool list_member_oid(const List *list, Oid datum)
Definition: list.c:505
#define lfirst(lc)
Definition: pg_list.h:106
static Query * fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
RuleLock * rd_rules
Definition: rel.h:118
bool expression_tree_walker(Node *node, bool(*walker)(), void *context)
Definition: nodeFuncs.c:1834
static int list_length(const List *l)
Definition: pg_list.h:89
RTEKind rtekind
Definition: parsenodes.h:951
Query * subquery
Definition: parsenodes.h:974
int errmsg(const char *fmt,...)
Definition: elog.c:797
int i
bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
Definition: rewriteManip.c:889
#define RELKIND_RELATION
Definition: pg_class.h:160
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:425
List * list_delete_first(List *list)
Definition: list.c:666

◆ fireRules()

static List* fireRules ( Query parsetree,
int  rt_index,
CmdType  event,
List locks,
bool instead_flag,
bool returning_flag,
Query **  qual_product 
)
static

Definition at line 2048 of file rewriteHandler.c.

References RewriteRule::actions, Query::canSetTag, CMD_NOTHING, Query::commandType, CopyAndAddInvertedQual(), copyObject, RewriteRule::isInstead, lappend(), lfirst, NIL, QSRC_INSTEAD_RULE, QSRC_NON_INSTEAD_RULE, QSRC_QUAL_INSTEAD_RULE, RewriteRule::qual, Query::querySource, and rewriteRuleAction().

Referenced by RewriteQuery().

2055 {
2056  List *results = NIL;
2057  ListCell *l;
2058 
2059  foreach(l, locks)
2060  {
2061  RewriteRule *rule_lock = (RewriteRule *) lfirst(l);
2062  Node *event_qual = rule_lock->qual;
2063  List *actions = rule_lock->actions;
2064  QuerySource qsrc;
2065  ListCell *r;
2066 
2067  /* Determine correct QuerySource value for actions */
2068  if (rule_lock->isInstead)
2069  {
2070  if (event_qual != NULL)
2071  qsrc = QSRC_QUAL_INSTEAD_RULE;
2072  else
2073  {
2074  qsrc = QSRC_INSTEAD_RULE;
2075  *instead_flag = true; /* report unqualified INSTEAD */
2076  }
2077  }
2078  else
2079  qsrc = QSRC_NON_INSTEAD_RULE;
2080 
2081  if (qsrc == QSRC_QUAL_INSTEAD_RULE)
2082  {
2083  /*
2084  * If there are INSTEAD rules with qualifications, the original
2085  * query is still performed. But all the negated rule
2086  * qualifications of the INSTEAD rules are added so it does its
2087  * actions only in cases where the rule quals of all INSTEAD rules
2088  * are false. Think of it as the default action in a case. We save
2089  * this in *qual_product so RewriteQuery() can add it to the query
2090  * list after we mangled it up enough.
2091  *
2092  * If we have already found an unqualified INSTEAD rule, then
2093  * *qual_product won't be used, so don't bother building it.
2094  */
2095  if (!*instead_flag)
2096  {
2097  if (*qual_product == NULL)
2098  *qual_product = copyObject(parsetree);
2099  *qual_product = CopyAndAddInvertedQual(*qual_product,
2100  event_qual,
2101  rt_index,
2102  event);
2103  }
2104  }
2105 
2106  /* Now process the rule's actions and add them to the result list */
2107  foreach(r, actions)
2108  {
2109  Query *rule_action = lfirst(r);
2110 
2111  if (rule_action->commandType == CMD_NOTHING)
2112  continue;
2113 
2114  rule_action = rewriteRuleAction(parsetree, rule_action,
2115  event_qual, rt_index, event,
2116  returning_flag);
2117 
2118  rule_action->querySource = qsrc;
2119  rule_action->canSetTag = false; /* might change later */
2120 
2121  results = lappend(results, rule_action);
2122  }
2123  }
2124 
2125  return results;
2126 }
#define NIL
Definition: pg_list.h:69
Node * qual
Definition: prs2lock.h:28
QuerySource
Definition: parsenodes.h:38
Definition: nodes.h:512
bool isInstead
Definition: prs2lock.h:31
static Query * CopyAndAddInvertedQual(Query *parsetree, Node *rule_qual, int rt_index, CmdType event)
List * actions
Definition: prs2lock.h:29
List * lappend(List *list, void *datum)
Definition: list.c:128
CmdType commandType
Definition: parsenodes.h:110
QuerySource querySource
Definition: parsenodes.h:112
#define lfirst(lc)
Definition: pg_list.h:106
bool canSetTag
Definition: parsenodes.h:116
static Query * rewriteRuleAction(Query *parsetree, Query *rule_action, Node *rule_qual, int rt_index, CmdType event, bool *returning_flag)
#define copyObject(obj)
Definition: nodes.h:625
Definition: pg_list.h:45

◆ get_assignment_input()

static Node * get_assignment_input ( Node node)
static

Definition at line 1090 of file rewriteHandler.c.

References FieldStore::arg, IsA, ArrayRef::refassgnexpr, and ArrayRef::refexpr.

Referenced by process_matched_tle().

1091 {
1092  if (node == NULL)
1093  return NULL;
1094  if (IsA(node, FieldStore))
1095  {
1096  FieldStore *fstore = (FieldStore *) node;
1097 
1098  return (Node *) fstore->arg;
1099  }
1100  else if (IsA(node, ArrayRef))
1101  {
1102  ArrayRef *aref = (ArrayRef *) node;
1103 
1104  if (aref->refassgnexpr == NULL)
1105  return NULL;
1106  return (Node *) aref->refexpr;
1107  }
1108  return NULL;
1109 }
Expr * refassgnexpr
Definition: primnodes.h:410
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
Expr * arg
Definition: primnodes.h:771
Definition: nodes.h:512
Expr * refexpr
Definition: primnodes.h:408

◆ get_view_query()

Query* get_view_query ( Relation  view)

Definition at line 2139 of file rewriteHandler.c.

References RewriteRule::actions, Assert, CMD_SELECT, elog, ERROR, RewriteRule::event, i, linitial, list_length(), RuleLock::numLocks, RelationData::rd_rel, RelationData::rd_rules, RELKIND_VIEW, and RuleLock::rules.

Referenced by ATExecSetRelOptions(), relation_is_updatable(), and rewriteTargetView().

2140 {
2141  int i;
2142 
2143  Assert(view->rd_rel->relkind == RELKIND_VIEW);
2144 
2145  for (i = 0; i < view->rd_rules->numLocks; i++)
2146  {
2147  RewriteRule *rule = view->rd_rules->rules[i];
2148 
2149  if (rule->event == CMD_SELECT)
2150  {
2151  /* A _RETURN rule should have only one action */
2152  if (list_length(rule->actions) != 1)
2153  elog(ERROR, "invalid _RETURN rule action specification");
2154 
2155  return (Query *) linitial(rule->actions);
2156  }
2157  }
2158 
2159  elog(ERROR, "failed to find _RETURN rule for view");
2160  return NULL; /* keep compiler quiet */
2161 }
int numLocks
Definition: prs2lock.h:42
Form_pg_class rd_rel
Definition: rel.h:114
Definition: localtime.c:78
#define linitial(l)
Definition: pg_list.h:111
#define ERROR
Definition: elog.h:43
CmdType event
Definition: prs2lock.h:27
RewriteRule ** rules
Definition: prs2lock.h:43
List * actions
Definition: prs2lock.h:29
#define Assert(condition)
Definition: c.h:680
RuleLock * rd_rules
Definition: rel.h:118
static int list_length(const List *l)
Definition: pg_list.h:89
#define RELKIND_VIEW
Definition: pg_class.h:164
int i
#define elog
Definition: elog.h:219

◆ markQueryForLocking()

static void markQueryForLocking ( Query qry,
Node jtnode,
LockClauseStrength  strength,
LockWaitPolicy  waitPolicy,
bool  pushedDown 
)
static

Definition at line 1623 of file rewriteHandler.c.

References ACL_SELECT_FOR_UPDATE, applyLockingClause(), elog, ERROR, FromExpr::fromlist, IsA, Query::jointree, JoinExpr::larg, lfirst, nodeTag, JoinExpr::rarg, RangeTblEntry::requiredPerms, rt_fetch, Query::rtable, RTE_RELATION, RTE_SUBQUERY, RangeTblEntry::rtekind, and RangeTblEntry::subquery.

Referenced by ApplyRetrieveRule().

1626 {
1627  if (jtnode == NULL)
1628  return;
1629  if (IsA(jtnode, RangeTblRef))
1630  {
1631  int rti = ((RangeTblRef *) jtnode)->rtindex;
1632  RangeTblEntry *rte = rt_fetch(rti, qry->rtable);
1633 
1634  if (rte->rtekind == RTE_RELATION)
1635  {
1636  applyLockingClause(qry, rti, strength, waitPolicy, pushedDown);
1638  }
1639  else if (rte->rtekind == RTE_SUBQUERY)
1640  {
1641  applyLockingClause(qry, rti, strength, waitPolicy, pushedDown);
1642  /* FOR UPDATE/SHARE of subquery is propagated to subquery's rels */
1644  strength, waitPolicy, true);
1645  }
1646  /* other RTE types are unaffected by FOR UPDATE */
1647  }
1648  else if (IsA(jtnode, FromExpr))
1649  {
1650  FromExpr *f = (FromExpr *) jtnode;
1651  ListCell *l;
1652 
1653  foreach(l, f->fromlist)
1654  markQueryForLocking(qry, lfirst(l), strength, waitPolicy, pushedDown);
1655  }
1656  else if (IsA(jtnode, JoinExpr))
1657  {
1658  JoinExpr *j = (JoinExpr *) jtnode;
1659 
1660  markQueryForLocking(qry, j->larg, strength, waitPolicy, pushedDown);
1661  markQueryForLocking(qry, j->rarg, strength, waitPolicy, pushedDown);
1662  }
1663  else
1664  elog(ERROR, "unrecognized node type: %d",
1665  (int) nodeTag(jtnode));
1666 }
static void markQueryForLocking(Query *qry, Node *jtnode, LockClauseStrength strength, LockWaitPolicy waitPolicy, bool pushedDown)
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
FromExpr * jointree
Definition: parsenodes.h:136
Definition: nodes.h:512
AclMode requiredPerms
Definition: parsenodes.h:1059
List * fromlist
Definition: primnodes.h:1478
Node * larg
Definition: primnodes.h:1458
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
Node * rarg
Definition: primnodes.h:1459
#define lfirst(lc)
Definition: pg_list.h:106
#define ACL_SELECT_FOR_UPDATE
Definition: parsenodes.h:88
#define nodeTag(nodeptr)
Definition: nodes.h:517
RTEKind rtekind
Definition: parsenodes.h:951
Query * subquery
Definition: parsenodes.h:974
#define elog
Definition: elog.h:219
void applyLockingClause(Query *qry, Index rtindex, LockClauseStrength strength, LockWaitPolicy waitPolicy, bool pushedDown)
Definition: analyze.c:2833

◆ matchLocks()

static List * matchLocks ( CmdType  event,
RuleLock rulelocks,
int  varno,
Query parsetree,
bool hasUpdate 
)
static

Definition at line 1385 of file rewriteHandler.c.

References CMD_SELECT, CMD_UPDATE, Query::commandType, RewriteRule::enabled, RewriteRule::event, i, lappend(), NIL, RuleLock::numLocks, rangeTableEntry_used(), Query::resultRelation, RULE_DISABLED, RULE_FIRES_ON_ORIGIN, RULE_FIRES_ON_REPLICA, RuleLock::rules, SESSION_REPLICATION_ROLE_REPLICA, and SessionReplicationRole.

Referenced by RewriteQuery().

1390 {
1391  List *matching_locks = NIL;
1392  int nlocks;
1393  int i;
1394 
1395  if (rulelocks == NULL)
1396  return NIL;
1397 
1398  if (parsetree->commandType != CMD_SELECT)
1399  {
1400  if (parsetree->resultRelation != varno)
1401  return NIL;
1402  }
1403 
1404  nlocks = rulelocks->numLocks;
1405 
1406  for (i = 0; i < nlocks; i++)
1407  {
1408  RewriteRule *oneLock = rulelocks->rules[i];
1409 
1410  if (oneLock->event == CMD_UPDATE)
1411  *hasUpdate = true;
1412 
1413  /*
1414  * Suppress ON INSERT/UPDATE/DELETE rules that are disabled or
1415  * configured to not fire during the current sessions replication
1416  * role. ON SELECT rules will always be applied in order to keep views
1417  * working even in LOCAL or REPLICA role.
1418  */
1419  if (oneLock->event != CMD_SELECT)
1420  {
1422  {
1423  if (oneLock->enabled == RULE_FIRES_ON_ORIGIN ||
1424  oneLock->enabled == RULE_DISABLED)
1425  continue;
1426  }
1427  else /* ORIGIN or LOCAL ROLE */
1428  {
1429  if (oneLock->enabled == RULE_FIRES_ON_REPLICA ||
1430  oneLock->enabled == RULE_DISABLED)
1431  continue;
1432  }
1433  }
1434 
1435  if (oneLock->event == event)
1436  {
1437  if (parsetree->commandType != CMD_SELECT ||
1438  rangeTableEntry_used((Node *) parsetree, varno, 0))
1439  matching_locks = lappend(matching_locks, oneLock);
1440  }
1441  }
1442 
1443  return matching_locks;
1444 }
#define NIL
Definition: pg_list.h:69
int numLocks
Definition: prs2lock.h:42
int resultRelation
Definition: parsenodes.h:120
Definition: nodes.h:512
int SessionReplicationRole
Definition: trigger.c:65
CmdType event
Definition: prs2lock.h:27
RewriteRule ** rules
Definition: prs2lock.h:43
#define RULE_DISABLED
Definition: rewriteDefine.h:24
List * lappend(List *list, void *datum)
Definition: list.c:128
#define RULE_FIRES_ON_ORIGIN
Definition: rewriteDefine.h:21
#define SESSION_REPLICATION_ROLE_REPLICA
Definition: trigger.h:147
#define RULE_FIRES_ON_REPLICA
Definition: rewriteDefine.h:23
CmdType commandType
Definition: parsenodes.h:110
int i
bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
Definition: rewriteManip.c:889
Definition: pg_list.h:45
char enabled
Definition: prs2lock.h:30

◆ process_matched_tle()

static TargetEntry * process_matched_tle ( TargetEntry src_tle,
TargetEntry prior_tle,
const char *  attrName 
)
static

Definition at line 937 of file rewriteHandler.c.

References arg, FieldStore::arg, CoerceToDomain::arg, elog, equal(), ereport, errcode(), errmsg(), ERROR, TargetEntry::expr, exprType(), FieldStore::fieldnums, flatCopyTargetEntry(), get_assignment_input(), IsA, list_concat(), list_copy(), makeNode, FieldStore::newvals, and ArrayRef::refexpr.

Referenced by rewriteTargetListIU().

940 {
941  TargetEntry *result;
942  CoerceToDomain *coerce_expr = NULL;
943  Node *src_expr;
944  Node *prior_expr;
945  Node *src_input;
946  Node *prior_input;
947  Node *priorbottom;
948  Node *newexpr;
949 
950  if (prior_tle == NULL)
951  {
952  /*
953  * Normal case where this is the first assignment to the attribute.
954  */
955  return src_tle;
956  }
957 
958  /*----------
959  * Multiple assignments to same attribute. Allow only if all are
960  * FieldStore or ArrayRef assignment operations. This is a bit
961  * tricky because what we may actually be looking at is a nest of
962  * such nodes; consider
963  * UPDATE tab SET col.fld1.subfld1 = x, col.fld2.subfld2 = y
964  * The two expressions produced by the parser will look like
965  * FieldStore(col, fld1, FieldStore(placeholder, subfld1, x))
966  * FieldStore(col, fld2, FieldStore(placeholder, subfld2, y))
967  * However, we can ignore the substructure and just consider the top
968  * FieldStore or ArrayRef from each assignment, because it works to
969  * combine these as
970  * FieldStore(FieldStore(col, fld1,
971  * FieldStore(placeholder, subfld1, x)),
972  * fld2, FieldStore(placeholder, subfld2, y))
973  * Note the leftmost expression goes on the inside so that the
974  * assignments appear to occur left-to-right.
975  *
976  * For FieldStore, instead of nesting we can generate a single
977  * FieldStore with multiple target fields. We must nest when
978  * ArrayRefs are involved though.
979  *
980  * As a further complication, the destination column might be a domain,
981  * resulting in each assignment containing a CoerceToDomain node over a
982  * FieldStore or ArrayRef. These should have matching target domains,
983  * so we strip them and reconstitute a single CoerceToDomain over the
984  * combined FieldStore/ArrayRef nodes. (Notice that this has the result
985  * that the domain's checks are applied only after we do all the field or
986  * element updates, not after each one. This is arguably desirable.)
987  *----------
988  */
989  src_expr = (Node *) src_tle->expr;
990  prior_expr = (Node *) prior_tle->expr;
991 
992  if (src_expr && IsA(src_expr, CoerceToDomain) &&
993  prior_expr && IsA(prior_expr, CoerceToDomain) &&
994  ((CoerceToDomain *) src_expr)->resulttype ==
995  ((CoerceToDomain *) prior_expr)->resulttype)
996  {
997  /* we assume without checking that resulttypmod/resultcollid match */
998  coerce_expr = (CoerceToDomain *) src_expr;
999  src_expr = (Node *) ((CoerceToDomain *) src_expr)->arg;
1000  prior_expr = (Node *) ((CoerceToDomain *) prior_expr)->arg;
1001  }
1002 
1003  src_input = get_assignment_input(src_expr);
1004  prior_input = get_assignment_input(prior_expr);
1005  if (src_input == NULL ||
1006  prior_input == NULL ||
1007  exprType(src_expr) != exprType(prior_expr))
1008  ereport(ERROR,
1009  (errcode(ERRCODE_SYNTAX_ERROR),
1010  errmsg("multiple assignments to same column \"%s\"",
1011  attrName)));
1012 
1013  /*
1014  * Prior TLE could be a nest of assignments if we do this more than once.
1015  */
1016  priorbottom = prior_input;
1017  for (;;)
1018  {
1019  Node *newbottom = get_assignment_input(priorbottom);
1020 
1021  if (newbottom == NULL)
1022  break; /* found the original Var reference */
1023  priorbottom = newbottom;
1024  }
1025  if (!equal(priorbottom, src_input))
1026  ereport(ERROR,
1027  (errcode(ERRCODE_SYNTAX_ERROR),
1028  errmsg("multiple assignments to same column \"%s\"",
1029  attrName)));
1030 
1031  /*
1032  * Looks OK to nest 'em.
1033  */
1034  if (IsA(src_expr, FieldStore))
1035  {
1036  FieldStore *fstore = makeNode(FieldStore);
1037 
1038  if (IsA(prior_expr, FieldStore))
1039  {
1040  /* combine the two */
1041  memcpy(fstore, prior_expr, sizeof(FieldStore));
1042  fstore->newvals =
1043  list_concat(list_copy(((FieldStore *) prior_expr)->newvals),
1044  list_copy(((FieldStore *) src_expr)->newvals));
1045  fstore->fieldnums =
1046  list_concat(list_copy(((FieldStore *) prior_expr)->fieldnums),
1047  list_copy(((FieldStore *) src_expr)->fieldnums));
1048  }
1049  else
1050  {
1051  /* general case, just nest 'em */
1052  memcpy(fstore, src_expr, sizeof(FieldStore));
1053  fstore->arg = (Expr *) prior_expr;
1054  }
1055  newexpr = (Node *) fstore;
1056  }
1057  else if (IsA(src_expr, ArrayRef))
1058  {
1059  ArrayRef *aref = makeNode(ArrayRef);
1060 
1061  memcpy(aref, src_expr, sizeof(ArrayRef));
1062  aref->refexpr = (Expr *) prior_expr;
1063  newexpr = (Node *) aref;
1064  }
1065  else
1066  {
1067  elog(ERROR, "cannot happen");
1068  newexpr = NULL;
1069  }
1070 
1071  if (coerce_expr)
1072  {
1073  /* put back the CoerceToDomain */
1074  CoerceToDomain *newcoerce = makeNode(CoerceToDomain);
1075 
1076  memcpy(newcoerce, coerce_expr, sizeof(CoerceToDomain));
1077  newcoerce->arg = (Expr *) newexpr;
1078  newexpr = (Node *) newcoerce;
1079  }
1080 
1081  result = flatCopyTargetEntry(src_tle);
1082  result->expr = (Expr *) newexpr;
1083  return result;
1084 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
Expr * arg
Definition: primnodes.h:771
bool equal(const void *a, const void *b)
Definition: equalfuncs.c:2984
List * list_copy(const List *oldlist)
Definition: list.c:1160
Definition: nodes.h:512
int errcode(int sqlerrcode)
Definition: elog.c:575
List * list_concat(List *list1, List *list2)
Definition: list.c:321
#define ERROR
Definition: elog.h:43
TargetEntry * flatCopyTargetEntry(TargetEntry *src_tle)
Definition: makefuncs.c:270
#define ereport(elevel, rest)
Definition: elog.h:122
List * newvals
Definition: primnodes.h:772
static Node * get_assignment_input(Node *node)
#define makeNode(_type_)
Definition: nodes.h:560
Expr * expr
Definition: primnodes.h:1375
Oid exprType(const Node *expr)
Definition: nodeFuncs.c:42
int errmsg(const char *fmt,...)
Definition: elog.c:797
List * fieldnums
Definition: primnodes.h:773
void * arg
#define elog
Definition: elog.h:219
Expr * refexpr
Definition: primnodes.h:408

◆ QueryRewrite()

List* QueryRewrite ( Query parsetree)

Definition at line 3564 of file rewriteHandler.c.

References Assert, Query::canSetTag, Query::commandType, fireRIRrules(), lappend(), lfirst, NIL, QSRC_INSTEAD_RULE, QSRC_ORIGINAL, QSRC_QUAL_INSTEAD_RULE, Query::queryId, Query::querySource, and RewriteQuery().

Referenced by ExecCreateTableAs(), ExplainOneUtility(), ExplainQuery(), PerformCursorOpen(), pg_rewrite_query(), PrepareQuery(), and refresh_matview_datafill().

3565 {
3566  uint64 input_query_id = parsetree->queryId;
3567  List *querylist;
3568  List *results;
3569  ListCell *l;
3570  CmdType origCmdType;
3571  bool foundOriginalQuery;
3572  Query *lastInstead;
3573 
3574  /*
3575  * This function is only applied to top-level original queries
3576  */
3577  Assert(parsetree->querySource == QSRC_ORIGINAL);
3578  Assert(parsetree->canSetTag);
3579 
3580  /*
3581  * Step 1
3582  *
3583  * Apply all non-SELECT rules possibly getting 0 or many queries
3584  */
3585  querylist = RewriteQuery(parsetree, NIL);
3586 
3587  /*
3588  * Step 2
3589  *
3590  * Apply all the RIR rules on each query
3591  *
3592  * This is also a handy place to mark each query with the original queryId
3593  */
3594  results = NIL;
3595  foreach(l, querylist)
3596  {
3597  Query *query = (Query *) lfirst(l);
3598 
3599  query = fireRIRrules(query, NIL, false);
3600 
3601  query->queryId = input_query_id;
3602 
3603  results = lappend(results, query);
3604  }
3605 
3606  /*
3607  * Step 3
3608  *
3609  * Determine which, if any, of the resulting queries is supposed to set
3610  * the command-result tag; and update the canSetTag fields accordingly.
3611  *
3612  * If the original query is still in the list, it sets the command tag.
3613  * Otherwise, the last INSTEAD query of the same kind as the original is
3614  * allowed to set the tag. (Note these rules can leave us with no query
3615  * setting the tag. The tcop code has to cope with this by setting up a
3616  * default tag based on the original un-rewritten query.)
3617  *
3618  * The Asserts verify that at most one query in the result list is marked
3619  * canSetTag. If we aren't checking asserts, we can fall out of the loop
3620  * as soon as we find the original query.
3621  */
3622  origCmdType = parsetree->commandType;
3623  foundOriginalQuery = false;
3624  lastInstead = NULL;
3625 
3626  foreach(l, results)
3627  {
3628  Query *query = (Query *) lfirst(l);
3629 
3630  if (query->querySource == QSRC_ORIGINAL)
3631  {
3632  Assert(query->canSetTag);
3633  Assert(!foundOriginalQuery);
3634  foundOriginalQuery = true;
3635 #ifndef USE_ASSERT_CHECKING
3636  break;
3637 #endif
3638  }
3639  else
3640  {
3641  Assert(!query->canSetTag);
3642  if (query->commandType == origCmdType &&
3643  (query->querySource == QSRC_INSTEAD_RULE ||
3645  lastInstead = query;
3646  }
3647  }
3648 
3649  if (!foundOriginalQuery && lastInstead != NULL)
3650  lastInstead->canSetTag = true;
3651 
3652  return results;
3653 }
#define NIL
Definition: pg_list.h:69
static List * RewriteQuery(Query *parsetree, List *rewrite_events)
List * lappend(List *list, void *datum)
Definition: list.c:128
uint64 queryId
Definition: parsenodes.h:114
CmdType commandType
Definition: parsenodes.h:110
QuerySource querySource
Definition: parsenodes.h:112
#define Assert(condition)
Definition: c.h:680
#define lfirst(lc)
Definition: pg_list.h:106
static Query * fireRIRrules(Query *parsetree, List *activeRIRs, bool forUpdatePushedDown)
bool canSetTag
Definition: parsenodes.h:116
Definition: pg_list.h:45
CmdType
Definition: nodes.h:652

◆ relation_is_updatable()

int relation_is_updatable ( Oid  reloid,
bool  include_triggers,
Bitmapset include_cols 
)

Definition at line 2475 of file rewriteHandler.c.

References AccessShareLock, adjust_view_column_set(), ALL_EVENTS, Assert, bms_int_members(), bms_is_empty(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, RewriteRule::event, FdwRoutine::ExecForeignDelete, FdwRoutine::ExecForeignInsert, FdwRoutine::ExecForeignUpdate, FromExpr::fromlist, get_view_query(), GetFdwRoutineForRelation(), i, FdwRoutine::IsForeignRelUpdatable, RewriteRule::isInstead, Query::jointree, linitial, RuleLock::numLocks, RewriteRule::qual, RelationData::rd_rel, RelationData::rd_rules, relation_close(), relation_is_updatable(), RangeTblEntry::relid, RangeTblEntry::relkind, RELKIND_FOREIGN_TABLE, RELKIND_PARTITIONED_TABLE, RELKIND_RELATION, RELKIND_VIEW, rt_fetch, Query::rtable, RTE_RELATION, RangeTblEntry::rtekind, RangeTblRef::rtindex, RuleLock::rules, Query::targetList, TriggerDesc::trig_delete_instead_row, TriggerDesc::trig_insert_instead_row, TriggerDesc::trig_update_instead_row, RelationData::trigdesc, try_relation_open(), view_cols_are_auto_updatable(), and view_query_is_auto_updatable().

Referenced by pg_column_is_updatable(), pg_relation_is_updatable(), and relation_is_updatable().

2478 {
2479  int events = 0;
2480  Relation rel;
2481  RuleLock *rulelocks;
2482 
2483 #define ALL_EVENTS ((1 << CMD_INSERT) | (1 << CMD_UPDATE) | (1 << CMD_DELETE))
2484 
2485  rel = try_relation_open(reloid, AccessShareLock);
2486 
2487  /*
2488  * If the relation doesn't exist, return zero rather than throwing an
2489  * error. This is helpful since scanning an information_schema view under
2490  * MVCC rules can result in referencing rels that have actually been
2491  * deleted already.
2492  */
2493  if (rel == NULL)
2494  return 0;
2495 
2496  /* If the relation is a table, it is always updatable */
2497  if (rel->rd_rel->relkind == RELKIND_RELATION ||
2498  rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
2499  {
2501  return ALL_EVENTS;
2502  }
2503 
2504  /* Look for unconditional DO INSTEAD rules, and note supported events */
2505  rulelocks = rel->rd_rules;
2506  if (rulelocks != NULL)
2507  {
2508  int i;
2509 
2510  for (i = 0; i < rulelocks->numLocks; i++)
2511  {
2512  if (rulelocks->rules[i]->isInstead &&
2513  rulelocks->rules[i]->qual == NULL)
2514  {
2515  events |= ((1 << rulelocks->rules[i]->event) & ALL_EVENTS);
2516  }
2517  }
2518 
2519  /* If we have rules for all events, we're done */
2520  if (events == ALL_EVENTS)
2521  {
2523  return events;
2524  }
2525  }
2526 
2527  /* Similarly look for INSTEAD OF triggers, if they are to be included */
2528  if (include_triggers)
2529  {
2530  TriggerDesc *trigDesc = rel->trigdesc;
2531 
2532  if (trigDesc)
2533  {
2534  if (trigDesc->trig_insert_instead_row)
2535  events |= (1 << CMD_INSERT);
2536  if (trigDesc->trig_update_instead_row)
2537  events |= (1 << CMD_UPDATE);
2538  if (trigDesc->trig_delete_instead_row)
2539  events |= (1 << CMD_DELETE);
2540 
2541  /* If we have triggers for all events, we're done */
2542  if (events == ALL_EVENTS)
2543  {
2545  return events;
2546  }
2547  }
2548  }
2549 
2550  /* If this is a foreign table, check which update events it supports */
2551  if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
2552  {
2553  FdwRoutine *fdwroutine = GetFdwRoutineForRelation(rel, false);
2554 
2555  if (fdwroutine->IsForeignRelUpdatable != NULL)
2556  events |= fdwroutine->IsForeignRelUpdatable(rel);
2557  else
2558  {
2559  /* Assume presence of executor functions is sufficient */
2560  if (fdwroutine->ExecForeignInsert != NULL)
2561  events |= (1 << CMD_INSERT);
2562  if (fdwroutine->ExecForeignUpdate != NULL)
2563  events |= (1 << CMD_UPDATE);
2564  if (fdwroutine->ExecForeignDelete != NULL)
2565  events |= (1 << CMD_DELETE);
2566  }
2567 
2569  return events;
2570  }
2571 
2572  /* Check if this is an automatically updatable view */
2573  if (rel->rd_rel->relkind == RELKIND_VIEW)
2574  {
2575  Query *viewquery = get_view_query(rel);
2576 
2577  if (view_query_is_auto_updatable(viewquery, false) == NULL)
2578  {
2579  Bitmapset *updatable_cols;
2580  int auto_events;
2581  RangeTblRef *rtr;
2582  RangeTblEntry *base_rte;
2583  Oid baseoid;
2584 
2585  /*
2586  * Determine which of the view's columns are updatable. If there
2587  * are none within the set of columns we are looking at, then the
2588  * view doesn't support INSERT/UPDATE, but it may still support
2589  * DELETE.
2590  */
2591  view_cols_are_auto_updatable(viewquery, NULL,
2592  &updatable_cols, NULL);
2593 
2594  if (include_cols != NULL)
2595  updatable_cols = bms_int_members(updatable_cols, include_cols);
2596 
2597  if (bms_is_empty(updatable_cols))
2598  auto_events = (1 << CMD_DELETE); /* May support DELETE */
2599  else
2600  auto_events = ALL_EVENTS; /* May support all events */
2601 
2602  /*
2603  * The base relation must also support these update commands.
2604  * Tables are always updatable, but for any other kind of base
2605  * relation we must do a recursive check limited to the columns
2606  * referenced by the locally updatable columns in this view.
2607  */
2608  rtr = (RangeTblRef *) linitial(viewquery->jointree->fromlist);
2609  base_rte = rt_fetch(rtr->rtindex, viewquery->rtable);
2610  Assert(base_rte->rtekind == RTE_RELATION);
2611 
2612  if (base_rte->relkind != RELKIND_RELATION &&
2613  base_rte->relkind != RELKIND_PARTITIONED_TABLE)
2614  {
2615  baseoid = base_rte->relid;
2616  include_cols = adjust_view_column_set(updatable_cols,
2617  viewquery->targetList);
2618  auto_events &= relation_is_updatable(baseoid,
2619  include_triggers,
2620  include_cols);
2621  }
2622  events |= auto_events;
2623  }
2624  }
2625 
2626  /* If we reach here, the relation may support some update commands */
2628  return events;
2629 }
ExecForeignDelete_function ExecForeignDelete
Definition: fdwapi.h:205
Node * qual
Definition: prs2lock.h:28
int numLocks
Definition: prs2lock.h:42
FromExpr * jointree
Definition: parsenodes.h:136
Relation try_relation_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1153
ExecForeignInsert_function ExecForeignInsert
Definition: fdwapi.h:203
static const char * view_cols_are_auto_updatable(Query *viewquery, Bitmapset *required_cols, Bitmapset **updatable_cols, char **non_updatable_col)
#define AccessShareLock
Definition: lockdefs.h:36
void relation_close(Relation relation, LOCKMODE lockmode)
Definition: heapam.c:1266
List * fromlist
Definition: primnodes.h:1478
Form_pg_class rd_rel
Definition: rel.h:114
unsigned int Oid
Definition: postgres_ext.h:31
bool isInstead
Definition: prs2lock.h:31
List * targetList
Definition: parsenodes.h:138
bool trig_insert_instead_row
Definition: reltrigger.h:57
#define linitial(l)
Definition: pg_list.h:111
List * rtable
Definition: parsenodes.h:135
#define ALL_EVENTS
TriggerDesc * trigdesc
Definition: rel.h:120
int relation_is_updatable(Oid reloid, bool include_triggers, Bitmapset *include_cols)
CmdType event
Definition: prs2lock.h:27
RewriteRule ** rules
Definition: prs2lock.h:43
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
bool trig_update_instead_row
Definition: reltrigger.h:62
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:707
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
bool trig_delete_instead_row
Definition: reltrigger.h:67
static Bitmapset * adjust_view_column_set(Bitmapset *cols, List *targetlist)
ExecForeignUpdate_function ExecForeignUpdate
Definition: fdwapi.h:204
#define Assert(condition)
Definition: c.h:680
RuleLock * rd_rules
Definition: rel.h:118
RTEKind rtekind
Definition: parsenodes.h:951
FdwRoutine * GetFdwRoutineForRelation(Relation relation, bool makecopy)
Definition: foreign.c:395
#define RELKIND_VIEW
Definition: pg_class.h:164
Query * get_view_query(Relation view)
int i
IsForeignRelUpdatable_function IsForeignRelUpdatable
Definition: fdwapi.h:207
Bitmapset * bms_int_members(Bitmapset *a, const Bitmapset *b)
Definition: bitmapset.c:907
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
#define RELKIND_RELATION
Definition: pg_class.h:160

◆ RewriteQuery()

static List* RewriteQuery ( Query parsetree,
List rewrite_events 
)
static

Definition at line 3171 of file rewriteHandler.c.

References OnConflictExpr::action, Assert, Query::canSetTag, castNode, CMD_DELETE, CMD_INSERT, CMD_SELECT, CMD_UPDATE, CMD_UTILITY, Query::commandType, Query::cteList, CommonTableExpr::ctequery, elog, ereport, errcode(), errhint(), errmsg(), ERROR, rewrite_event::event, fireRules(), FromExpr::fromlist, heap_close, heap_open(), IsA, Query::jointree, lappend(), lcons(), lfirst, lfirst_node, linitial, linitial_node, list_concat(), list_delete_first(), list_length(), matchLocks(), NIL, NoLock, Query::onConflict, ONCONFLICT_UPDATE, OnConflictExpr::onConflictSet, Query::override, palloc(), QSRC_NON_INSTEAD_RULE, QSRC_QUAL_INSTEAD_RULE, Query::querySource, RelationData::rd_rel, RelationData::rd_rules, rewrite_event::relation, RelationGetRelationName, RelationGetRelid, RangeTblEntry::relid, RELKIND_VIEW, Query::resultRelation, Query::returningList, rewriteTargetListIU(), rewriteTargetView(), rewriteValuesRTE(), rt_fetch, Query::rtable, RTE_RELATION, RTE_VALUES, RangeTblEntry::rtekind, RangeTblRef::rtindex, Query::targetList, and view_has_instead_trigger().

Referenced by QueryRewrite().

3172 {
3173  CmdType event = parsetree->commandType;
3174  bool instead = false;
3175  bool returning = false;
3176  bool updatableview = false;
3177  Query *qual_product = NULL;
3178  List *rewritten = NIL;
3179  ListCell *lc1;
3180 
3181  /*
3182  * First, recursively process any insert/update/delete statements in WITH
3183  * clauses. (We have to do this first because the WITH clauses may get
3184  * copied into rule actions below.)
3185  */
3186  foreach(lc1, parsetree->cteList)
3187  {
3189  Query *ctequery = castNode(Query, cte->ctequery);
3190  List *newstuff;
3191 
3192  if (ctequery->commandType == CMD_SELECT)
3193  continue;
3194 
3195  newstuff = RewriteQuery(ctequery, rewrite_events);
3196 
3197  /*
3198  * Currently we can only handle unconditional, single-statement DO
3199  * INSTEAD rules correctly; we have to get exactly one Query out of
3200  * the rewrite operation to stuff back into the CTE node.
3201  */
3202  if (list_length(newstuff) == 1)
3203  {
3204  /* Push the single Query back into the CTE node */
3205  ctequery = linitial_node(Query, newstuff);
3206  /* WITH queries should never be canSetTag */
3207  Assert(!ctequery->canSetTag);
3208  cte->ctequery = (Node *) ctequery;
3209  }
3210  else if (newstuff == NIL)
3211  {
3212  ereport(ERROR,
3213  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3214  errmsg("DO INSTEAD NOTHING rules are not supported for data-modifying statements in WITH")));
3215  }
3216  else
3217  {
3218  ListCell *lc2;
3219 
3220  /* examine queries to determine which error message to issue */
3221  foreach(lc2, newstuff)
3222  {
3223  Query *q = (Query *) lfirst(lc2);
3224 
3226  ereport(ERROR,
3227  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3228  errmsg("conditional DO INSTEAD rules are not supported for data-modifying statements in WITH")));
3230  ereport(ERROR,
3231  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3232  errmsg("DO ALSO rules are not supported for data-modifying statements in WITH")));
3233  }
3234 
3235  ereport(ERROR,
3236  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3237  errmsg("multi-statement DO INSTEAD rules are not supported for data-modifying statements in WITH")));
3238  }
3239  }
3240 
3241  /*
3242  * If the statement is an insert, update, or delete, adjust its targetlist
3243  * as needed, and then fire INSERT/UPDATE/DELETE rules on it.
3244  *
3245  * SELECT rules are handled later when we have all the queries that should
3246  * get executed. Also, utilities aren't rewritten at all (do we still
3247  * need that check?)
3248  */
3249  if (event != CMD_SELECT && event != CMD_UTILITY)
3250  {
3251  int result_relation;
3252  RangeTblEntry *rt_entry;
3253  Relation rt_entry_relation;
3254  List *locks;
3255  List *product_queries;
3256  bool hasUpdate = false;
3257 
3258  result_relation = parsetree->resultRelation;
3259  Assert(result_relation != 0);
3260  rt_entry = rt_fetch(result_relation, parsetree->rtable);
3261  Assert(rt_entry->rtekind == RTE_RELATION);
3262 
3263  /*
3264  * We can use NoLock here since either the parser or
3265  * AcquireRewriteLocks should have locked the rel already.
3266  */
3267  rt_entry_relation = heap_open(rt_entry->relid, NoLock);
3268 
3269  /*
3270  * Rewrite the targetlist as needed for the command type.
3271  */
3272  if (event == CMD_INSERT)
3273  {
3274  RangeTblEntry *values_rte = NULL;
3275 
3276  /*
3277  * If it's an INSERT ... VALUES (...), (...), ... there will be a
3278  * single RTE for the VALUES targetlists.
3279  */
3280  if (list_length(parsetree->jointree->fromlist) == 1)
3281  {
3282  RangeTblRef *rtr = (RangeTblRef *) linitial(parsetree->jointree->fromlist);
3283 
3284  if (IsA(rtr, RangeTblRef))
3285  {
3286  RangeTblEntry *rte = rt_fetch(rtr->rtindex,
3287  parsetree->rtable);
3288 
3289  if (rte->rtekind == RTE_VALUES)
3290  values_rte = rte;
3291  }
3292  }
3293 
3294  if (values_rte)
3295  {
3296  List *attrnos;
3297 
3298  /* Process the main targetlist ... */
3299  parsetree->targetList = rewriteTargetListIU(parsetree->targetList,
3300  parsetree->commandType,
3301  parsetree->override,
3302  rt_entry_relation,
3303  parsetree->resultRelation,
3304  &attrnos);
3305  /* ... and the VALUES expression lists */
3306  rewriteValuesRTE(values_rte, rt_entry_relation, attrnos);
3307  }
3308  else
3309  {
3310  /* Process just the main targetlist */
3311  parsetree->targetList =
3312  rewriteTargetListIU(parsetree->targetList,
3313  parsetree->commandType,
3314  parsetree->override,
3315  rt_entry_relation,
3316  parsetree->resultRelation, NULL);
3317  }
3318 
3319  if (parsetree->onConflict &&
3320  parsetree->onConflict->action == ONCONFLICT_UPDATE)
3321  {
3322  parsetree->onConflict->onConflictSet =
3324  CMD_UPDATE,
3325  parsetree->override,
3326  rt_entry_relation,
3327  parsetree->resultRelation,
3328  NULL);
3329  }
3330  }
3331  else if (event == CMD_UPDATE)
3332  {
3333  parsetree->targetList =
3334  rewriteTargetListIU(parsetree->targetList,
3335  parsetree->commandType,
3336  parsetree->override,
3337  rt_entry_relation,
3338  parsetree->resultRelation, NULL);
3339  }
3340  else if (event == CMD_DELETE)
3341  {
3342  /* Nothing to do here */
3343  }
3344  else
3345  elog(ERROR, "unrecognized commandType: %d", (int) event);
3346 
3347  /*
3348  * Collect and apply the appropriate rules.
3349  */
3350  locks = matchLocks(event, rt_entry_relation->rd_rules,
3351  result_relation, parsetree, &hasUpdate);
3352 
3353  product_queries = fireRules(parsetree,
3354  result_relation,
3355  event,
3356  locks,
3357  &instead,
3358  &returning,
3359  &qual_product);
3360 
3361  /*
3362  * If there were no INSTEAD rules, and the target relation is a view
3363  * without any INSTEAD OF triggers, see if the view can be
3364  * automatically updated. If so, we perform the necessary query
3365  * transformation here and add the resulting query to the
3366  * product_queries list, so that it gets recursively rewritten if
3367  * necessary.
3368  */
3369  if (!instead && qual_product == NULL &&
3370  rt_entry_relation->rd_rel->relkind == RELKIND_VIEW &&
3371  !view_has_instead_trigger(rt_entry_relation, event))
3372  {
3373  /*
3374  * This throws an error if the view can't be automatically
3375  * updated, but that's OK since the query would fail at runtime
3376  * anyway.
3377  */
3378  parsetree = rewriteTargetView(parsetree, rt_entry_relation);
3379 
3380  /*
3381  * At this point product_queries contains any DO ALSO rule
3382  * actions. Add the rewritten query before or after those. This
3383  * must match the handling the original query would have gotten
3384  * below, if we allowed it to be included again.
3385  */
3386  if (parsetree->commandType == CMD_INSERT)
3387  product_queries = lcons(parsetree, product_queries);
3388  else
3389  product_queries = lappend(product_queries, parsetree);
3390 
3391  /*
3392  * Set the "instead" flag, as if there had been an unqualified
3393  * INSTEAD, to prevent the original query from being included a
3394  * second time below. The transformation will have rewritten any
3395  * RETURNING list, so we can also set "returning" to forestall
3396  * throwing an error below.
3397  */
3398  instead = true;
3399  returning = true;
3400  updatableview = true;
3401  }
3402 
3403  /*
3404  * If we got any product queries, recursively rewrite them --- but
3405  * first check for recursion!
3406  */
3407  if (product_queries != NIL)
3408  {
3409  ListCell *n;
3410  rewrite_event *rev;
3411 
3412  foreach(n, rewrite_events)
3413  {
3414  rev = (rewrite_event *) lfirst(n);
3415  if (rev->relation == RelationGetRelid(rt_entry_relation) &&
3416  rev->event == event)
3417  ereport(ERROR,
3418  (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
3419  errmsg("infinite recursion detected in rules for relation \"%s\"",
3420  RelationGetRelationName(rt_entry_relation))));
3421  }
3422 
3423  rev = (rewrite_event *) palloc(sizeof(rewrite_event));
3424  rev->relation = RelationGetRelid(rt_entry_relation);
3425  rev->event = event;
3426  rewrite_events = lcons(rev, rewrite_events);
3427 
3428  foreach(n, product_queries)
3429  {
3430  Query *pt = (Query *) lfirst(n);
3431  List *newstuff;
3432 
3433  newstuff = RewriteQuery(pt, rewrite_events);
3434  rewritten = list_concat(rewritten, newstuff);
3435  }
3436 
3437  rewrite_events = list_delete_first(rewrite_events);
3438  }
3439 
3440  /*
3441  * If there is an INSTEAD, and the original query has a RETURNING, we
3442  * have to have found a RETURNING in the rule(s), else fail. (Because
3443  * DefineQueryRewrite only allows RETURNING in unconditional INSTEAD
3444  * rules, there's no need to worry whether the substituted RETURNING
3445  * will actually be executed --- it must be.)
3446  */
3447  if ((instead || qual_product != NULL) &&
3448  parsetree->returningList &&
3449  !returning)
3450  {
3451  switch (event)
3452  {
3453  case CMD_INSERT:
3454  ereport(ERROR,
3455  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3456  errmsg("cannot perform INSERT RETURNING on relation \"%s\"",
3457  RelationGetRelationName(rt_entry_relation)),
3458  errhint("You need an unconditional ON INSERT DO INSTEAD rule with a RETURNING clause.")));
3459  break;
3460  case CMD_UPDATE:
3461  ereport(ERROR,
3462  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3463  errmsg("cannot perform UPDATE RETURNING on relation \"%s\"",
3464  RelationGetRelationName(rt_entry_relation)),
3465  errhint("You need an unconditional ON UPDATE DO INSTEAD rule with a RETURNING clause.")));
3466  break;
3467  case CMD_DELETE:
3468  ereport(ERROR,
3469  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3470  errmsg("cannot perform DELETE RETURNING on relation \"%s\"",
3471  RelationGetRelationName(rt_entry_relation)),
3472  errhint("You need an unconditional ON DELETE DO INSTEAD rule with a RETURNING clause.")));
3473  break;
3474  default:
3475  elog(ERROR, "unrecognized commandType: %d",
3476  (int) event);
3477  break;
3478  }
3479  }
3480 
3481  /*
3482  * Updatable views are supported by ON CONFLICT, so don't prevent that
3483  * case from proceeding
3484  */
3485  if (parsetree->onConflict &&
3486  (product_queries != NIL || hasUpdate) &&
3487  !updatableview)
3488  ereport(ERROR,
3489  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3490  errmsg("INSERT with ON CONFLICT clause cannot be used with table that has INSERT or UPDATE rules")));
3491 
3492  heap_close(rt_entry_relation, NoLock);
3493  }
3494 
3495  /*
3496  * For INSERTs, the original query is done first; for UPDATE/DELETE, it is
3497  * done last. This is needed because update and delete rule actions might
3498  * not do anything if they are invoked after the update or delete is
3499  * performed. The command counter increment between the query executions
3500  * makes the deleted (and maybe the updated) tuples disappear so the scans
3501  * for them in the rule actions cannot find them.
3502  *
3503  * If we found any unqualified INSTEAD, the original query is not done at
3504  * all, in any form. Otherwise, we add the modified form if qualified
3505  * INSTEADs were found, else the unmodified form.
3506  */
3507  if (!instead)
3508  {
3509  if (parsetree->commandType == CMD_INSERT)
3510  {
3511  if (qual_product != NULL)
3512  rewritten = lcons(qual_product, rewritten);
3513  else
3514  rewritten = lcons(parsetree, rewritten);
3515  }
3516  else
3517  {
3518  if (qual_product != NULL)
3519  rewritten = lappend(rewritten, qual_product);
3520  else
3521  rewritten = lappend(rewritten, parsetree);
3522  }
3523  }
3524 
3525  /*
3526  * If the original query has a CTE list, and we generated more than one
3527  * non-utility result query, we have to fail because we'll have copied the
3528  * CTE list into each result query. That would break the expectation of
3529  * single evaluation of CTEs. This could possibly be fixed by
3530  * restructuring so that a CTE list can be shared across multiple Query
3531  * and PlannableStatement nodes.
3532  */
3533  if (parsetree->cteList != NIL)
3534  {
3535  int qcount = 0;
3536 
3537  foreach(lc1, rewritten)
3538  {
3539  Query *q = (Query *) lfirst(lc1);
3540 
3541  if (q->commandType != CMD_UTILITY)
3542  qcount++;
3543  }
3544  if (qcount > 1)
3545  ereport(ERROR,
3546  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
3547  errmsg("WITH cannot be used in a query that is rewritten by rules into multiple queries")));
3548  }
3549 
3550  return rewritten;
3551 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
int errhint(const char *fmt,...)
Definition: elog.c:987
static bool view_has_instead_trigger(Relation view, CmdType event)
FromExpr * jointree
Definition: parsenodes.h:136
OnConflictExpr * onConflict
Definition: parsenodes.h:142
#define castNode(_type_, nodeptr)
Definition: nodes.h:581
int resultRelation
Definition: parsenodes.h:120
Definition: nodes.h:512
static List * RewriteQuery(Query *parsetree, List *rewrite_events)
int errcode(int sqlerrcode)
Definition: elog.c:575
List * list_concat(List *list1, List *list2)
Definition: list.c:321
static void rewriteValuesRTE(RangeTblEntry *rte, Relation target_relation, List *attrnos)
List * fromlist
Definition: primnodes.h:1478
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:114
#define linitial_node(type, l)
Definition: pg_list.h:114
static List * rewriteTargetListIU(List *targetList, CmdType commandType, OverridingKind override, Relation target_relation, int result_rti, List **attrno_list)
List * targetList
Definition: parsenodes.h:138
#define linitial(l)
Definition: pg_list.h:111
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
#define lfirst_node(type, lc)
Definition: pg_list.h:109
#define NoLock
Definition: lockdefs.h:34
static Query * rewriteTargetView(Query *parsetree, Relation view)
#define RelationGetRelationName(relation)
Definition: rel.h:445
List * returningList
Definition: parsenodes.h:144
#define ereport(elevel, rest)
Definition: elog.h:122
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
List * lappend(List *list, void *datum)
Definition: list.c:128
OnConflictAction action
Definition: primnodes.h:1494
static List * fireRules(Query *parsetree, int rt_index, CmdType event, List *locks, bool *instead_flag, bool *returning_flag, Query **qual_product)
OverridingKind override
Definition: parsenodes.h:140
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
CmdType commandType
Definition: parsenodes.h:110
List * lcons(void *datum, List *list)
Definition: list.c:259
QuerySource querySource
Definition: parsenodes.h:112
#define Assert(condition)
Definition: c.h:680
#define lfirst(lc)
Definition: pg_list.h:106
RuleLock * rd_rules
Definition: rel.h:118
bool canSetTag
Definition: parsenodes.h:116
static int list_length(const List *l)
Definition: pg_list.h:89
RTEKind rtekind
Definition: parsenodes.h:951
List * cteList
Definition: parsenodes.h:133
void * palloc(Size size)
Definition: mcxt.c:835
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define RELKIND_VIEW
Definition: pg_class.h:164
List * onConflictSet
Definition: primnodes.h:1503
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
static List * matchLocks(CmdType event, RuleLock *rulelocks, int varno, Query *parsetree, bool *hasUpdate)
#define RelationGetRelid(relation)
Definition: rel.h:425
CmdType
Definition: nodes.h:652
List * list_delete_first(List *list)
Definition: list.c:666

◆ rewriteRuleAction()

static Query * rewriteRuleAction ( Query parsetree,
Query rule_action,
Node rule_qual,
int  rt_index,
CmdType  event,
bool returning_flag 
)
static

Definition at line 332 of file rewriteHandler.c.

References acquireLocksOnSubLinks(), AcquireRewriteLocks(), AddQual(), adjustJoinTreeList(), Assert, ChangeVarNodes(), checkExprHasSubLink(), CMD_INSERT, CMD_UPDATE, CMD_UTILITY, copyObject, Query::cteList, CommonTableExpr::ctename, ereport, errcode(), errmsg(), ERROR, acquireLocksOnSubLinks_context::for_execute, RangeTblEntry::functions, getInsertSelectQuery(), Query::hasRowSecurity, Query::hasSubLinks, Query::jointree, lfirst, list_concat(), list_length(), NIL, OffsetVarNodes(), PRS2_NEW_VARNO, PRS2_OLD_VARNO, FromExpr::quals, rangeTableEntry_used(), REPLACEVARS_CHANGE_VARNO, REPLACEVARS_REPORT_ERROR, REPLACEVARS_SUBSTITUTE_NULL, ReplaceVarsFromTargetList(), Query::resultRelation, Query::returningList, rt_fetch, Query::rtable, RTE_FUNCTION, RTE_RELATION, RTE_TABLEFUNC, RTE_VALUES, RangeTblEntry::rtekind, RangeTblEntry::tablefunc, RangeTblEntry::tablesample, Query::targetList, and RangeTblEntry::values_lists.

Referenced by fireRules().

338 {
339  int current_varno,
340  new_varno;
341  int rt_length;
342  Query *sub_action;
343  Query **sub_action_ptr;
345 
346  context.for_execute = true;
347 
348  /*
349  * Make modifiable copies of rule action and qual (what we're passed are
350  * the stored versions in the relcache; don't touch 'em!).
351  */
352  rule_action = copyObject(rule_action);
353  rule_qual = copyObject(rule_qual);
354 
355  /*
356  * Acquire necessary locks and fix any deleted JOIN RTE entries.
357  */
358  AcquireRewriteLocks(rule_action, true, false);
359  (void) acquireLocksOnSubLinks(rule_qual, &context);
360 
361  current_varno = rt_index;
362  rt_length = list_length(parsetree->rtable);
363  new_varno = PRS2_NEW_VARNO + rt_length;
364 
365  /*
366  * Adjust rule action and qual to offset its varnos, so that we can merge
367  * its rtable with the main parsetree's rtable.
368  *
369  * If the rule action is an INSERT...SELECT, the OLD/NEW rtable entries
370  * will be in the SELECT part, and we have to modify that rather than the
371  * top-level INSERT (kluge!).
372  */
373  sub_action = getInsertSelectQuery(rule_action, &sub_action_ptr);
374 
375  OffsetVarNodes((Node *) sub_action, rt_length, 0);
376  OffsetVarNodes(rule_qual, rt_length, 0);
377  /* but references to OLD should point at original rt_index */
378  ChangeVarNodes((Node *) sub_action,
379  PRS2_OLD_VARNO + rt_length, rt_index, 0);
380  ChangeVarNodes(rule_qual,
381  PRS2_OLD_VARNO + rt_length, rt_index, 0);
382 
383  /*
384  * Generate expanded rtable consisting of main parsetree's rtable plus
385  * rule action's rtable; this becomes the complete rtable for the rule
386  * action. Some of the entries may be unused after we finish rewriting,
387  * but we leave them all in place for two reasons:
388  *
389  * We'd have a much harder job to adjust the query's varnos if we
390  * selectively removed RT entries.
391  *
392  * If the rule is INSTEAD, then the original query won't be executed at
393  * all, and so its rtable must be preserved so that the executor will do
394  * the correct permissions checks on it.
395  *
396  * RT entries that are not referenced in the completed jointree will be
397  * ignored by the planner, so they do not affect query semantics. But any
398  * permissions checks specified in them will be applied during executor
399  * startup (see ExecCheckRTEPerms()). This allows us to check that the
400  * caller has, say, insert-permission on a view, when the view is not
401  * semantically referenced at all in the resulting query.
402  *
403  * When a rule is not INSTEAD, the permissions checks done on its copied
404  * RT entries will be redundant with those done during execution of the
405  * original query, but we don't bother to treat that case differently.
406  *
407  * NOTE: because planner will destructively alter rtable, we must ensure
408  * that rule action's rtable is separate and shares no substructure with
409  * the main rtable. Hence do a deep copy here.
410  */
411  sub_action->rtable = list_concat(copyObject(parsetree->rtable),
412  sub_action->rtable);
413 
414  /*
415  * There could have been some SubLinks in parsetree's rtable, in which
416  * case we'd better mark the sub_action correctly.
417  */
418  if (parsetree->hasSubLinks && !sub_action->hasSubLinks)
419  {
420  ListCell *lc;
421 
422  foreach(lc, parsetree->rtable)
423  {
424  RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
425 
426  switch (rte->rtekind)
427  {
428  case RTE_RELATION:
429  sub_action->hasSubLinks =
431  break;
432  case RTE_FUNCTION:
433  sub_action->hasSubLinks =
435  break;
436  case RTE_TABLEFUNC:
437  sub_action->hasSubLinks =
439  break;
440  case RTE_VALUES:
441  sub_action->hasSubLinks =
443  break;
444  default:
445  /* other RTE types don't contain bare expressions */
446  break;
447  }
448  if (sub_action->hasSubLinks)
449  break; /* no need to keep scanning rtable */
450  }
451  }
452 
453  /*
454  * Also, we might have absorbed some RTEs with RLS conditions into the
455  * sub_action. If so, mark it as hasRowSecurity, whether or not those
456  * RTEs will be referenced after we finish rewriting. (Note: currently
457  * this is a no-op because RLS conditions aren't added till later, but it
458  * seems like good future-proofing to do this anyway.)
459  */
460  sub_action->hasRowSecurity |= parsetree->hasRowSecurity;
461 
462  /*
463  * Each rule action's jointree should be the main parsetree's jointree
464  * plus that rule's jointree, but usually *without* the original rtindex
465  * that we're replacing (if present, which it won't be for INSERT). Note
466  * that if the rule action refers to OLD, its jointree will add a
467  * reference to rt_index. If the rule action doesn't refer to OLD, but
468  * either the rule_qual or the user query quals do, then we need to keep
469  * the original rtindex in the jointree to provide data for the quals. We
470  * don't want the original rtindex to be joined twice, however, so avoid
471  * keeping it if the rule action mentions it.
472  *
473  * As above, the action's jointree must not share substructure with the
474  * main parsetree's.
475  */
476  if (sub_action->commandType != CMD_UTILITY)
477  {
478  bool keeporig;
479  List *newjointree;
480 
481  Assert(sub_action->jointree != NULL);
482  keeporig = (!rangeTableEntry_used((Node *) sub_action->jointree,
483  rt_index, 0)) &&
484  (rangeTableEntry_used(rule_qual, rt_index, 0) ||
485  rangeTableEntry_used(parsetree->jointree->quals, rt_index, 0));
486  newjointree = adjustJoinTreeList(parsetree, !keeporig, rt_index);
487  if (newjointree != NIL)
488  {
489  /*
490  * If sub_action is a setop, manipulating its jointree will do no
491  * good at all, because the jointree is dummy. (Perhaps someday
492  * we could push the joining and quals down to the member
493  * statements of the setop?)
494  */
495  if (sub_action->setOperations != NULL)
496  ereport(ERROR,
497  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
498  errmsg("conditional UNION/INTERSECT/EXCEPT statements are not implemented")));
499 
500  sub_action->jointree->fromlist =
501  list_concat(newjointree, sub_action->jointree->fromlist);
502 
503  /*
504  * There could have been some SubLinks in newjointree, in which
505  * case we'd better mark the sub_action correctly.
506  */
507  if (parsetree->hasSubLinks && !sub_action->hasSubLinks)
508  sub_action->hasSubLinks =
509  checkExprHasSubLink((Node *) newjointree);
510  }
511  }
512 
513  /*
514  * If the original query has any CTEs, copy them into the rule action. But
515  * we don't need them for a utility action.
516  */
517  if (parsetree->cteList != NIL && sub_action->commandType != CMD_UTILITY)
518  {
519  ListCell *lc;
520 
521  /*
522  * Annoying implementation restriction: because CTEs are identified by
523  * name within a cteList, we can't merge a CTE from the original query
524  * if it has the same name as any CTE in the rule action.
525  *
526  * This could possibly be fixed by using some sort of internally
527  * generated ID, instead of names, to link CTE RTEs to their CTEs.
528  */
529  foreach(lc, parsetree->cteList)
530  {
531  CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
532  ListCell *lc2;
533 
534  foreach(lc2, sub_action->cteList)
535  {
536  CommonTableExpr *cte2 = (CommonTableExpr *) lfirst(lc2);
537 
538  if (strcmp(cte->ctename, cte2->ctename) == 0)
539  ereport(ERROR,
540  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
541  errmsg("WITH query name \"%s\" appears in both a rule action and the query being rewritten",
542  cte->ctename)));
543  }
544  }
545 
546  /* OK, it's safe to combine the CTE lists */
547  sub_action->cteList = list_concat(sub_action->cteList,
548  copyObject(parsetree->cteList));
549  }
550 
551  /*
552  * Event Qualification forces copying of parsetree and splitting into two
553  * queries one w/rule_qual, one w/NOT rule_qual. Also add user query qual
554  * onto rule action
555  */
556  AddQual(sub_action, rule_qual);
557 
558  AddQual(sub_action, parsetree->jointree->quals);
559 
560  /*
561  * Rewrite new.attribute with right hand side of target-list entry for
562  * appropriate field name in insert/update.
563  *
564  * KLUGE ALERT: since ReplaceVarsFromTargetList returns a mutated copy, we
565  * can't just apply it to sub_action; we have to remember to update the
566  * sublink inside rule_action, too.
567  */
568  if ((event == CMD_INSERT || event == CMD_UPDATE) &&
569  sub_action->commandType != CMD_UTILITY)
570  {
571  sub_action = (Query *)
572  ReplaceVarsFromTargetList((Node *) sub_action,
573  new_varno,
574  0,
575  rt_fetch(new_varno, sub_action->rtable),
576  parsetree->targetList,
577  (event == CMD_UPDATE) ?
580  current_varno,
581  NULL);
582  if (sub_action_ptr)
583  *sub_action_ptr = sub_action;
584  else
585  rule_action = sub_action;
586  }
587 
588  /*
589  * If rule_action has a RETURNING clause, then either throw it away if the
590  * triggering query has no RETURNING clause, or rewrite it to emit what
591  * the triggering query's RETURNING clause asks for. Throw an error if
592  * more than one rule has a RETURNING clause.
593  */
594  if (!parsetree->returningList)
595  rule_action->returningList = NIL;
596  else if (rule_action->returningList)
597  {
598  if (*returning_flag)
599  ereport(ERROR,
600  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
601  errmsg("cannot have RETURNING lists in multiple rules")));
602  *returning_flag = true;
603  rule_action->returningList = (List *)
605  parsetree->resultRelation,
606  0,
607  rt_fetch(parsetree->resultRelation,
608  parsetree->rtable),
609  rule_action->returningList,
611  0,
612  &rule_action->hasSubLinks);
613 
614  /*
615  * There could have been some SubLinks in parsetree's returningList,
616  * in which case we'd better mark the rule_action correctly.
617  */
618  if (parsetree->hasSubLinks && !rule_action->hasSubLinks)
619  rule_action->hasSubLinks =
620  checkExprHasSubLink((Node *) rule_action->returningList);
621  }
622 
623  return rule_action;
624 }
#define NIL
Definition: pg_list.h:69
Query * getInsertSelectQuery(Query *parsetree, Query ***subquery_ptr)
Definition: rewriteManip.c:921
void OffsetVarNodes(Node *node, int offset, int sublevels_up)
Definition: rewriteManip.c:424
static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
FromExpr * jointree
Definition: parsenodes.h:136
bool checkExprHasSubLink(Node *node)
Definition: rewriteManip.c:277
int resultRelation
Definition: parsenodes.h:120
Definition: nodes.h:512
int errcode(int sqlerrcode)
Definition: elog.c:575
static List * adjustJoinTreeList(Query *parsetree, bool removert, int rt_index)
List * list_concat(List *list1, List *list2)
Definition: list.c:321
List * fromlist
Definition: primnodes.h:1478
List * values_lists
Definition: parsenodes.h:1016
Node * quals
Definition: primnodes.h:1479
List * targetList
Definition: parsenodes.h:138
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
TableFunc * tablefunc
Definition: parsenodes.h:1011
void AcquireRewriteLocks(Query *parsetree, bool forExecute, bool forUpdatePushedDown)
List * returningList
Definition: parsenodes.h:144
#define ereport(elevel, rest)
Definition: elog.h:122
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
#define PRS2_OLD_VARNO
Definition: primnodes.h:160
Node * ReplaceVarsFromTargetList(Node *node, int target_varno, int sublevels_up, RangeTblEntry *target_rte, List *targetlist, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks)
CmdType commandType
Definition: parsenodes.h:110
#define Assert(condition)
Definition: c.h:680
#define lfirst(lc)
Definition: pg_list.h:106
List * functions
Definition: parsenodes.h:1005
void AddQual(Query *parsetree, Node *qual)
Definition: rewriteManip.c:976
static int list_length(const List *l)
Definition: pg_list.h:89
RTEKind rtekind
Definition: parsenodes.h:951
List * cteList
Definition: parsenodes.h:133
Node * setOperations
Definition: parsenodes.h:163
int errmsg(const char *fmt,...)
Definition: elog.c:797
bool hasSubLinks
Definition: parsenodes.h:126
void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
Definition: rewriteManip.c:607
bool rangeTableEntry_used(Node *node, int rt_index, int sublevels_up)
Definition: rewriteManip.c:889
#define copyObject(obj)
Definition: nodes.h:625
Definition: pg_list.h:45
struct TableSampleClause * tablesample
Definition: parsenodes.h:969
#define PRS2_NEW_VARNO
Definition: primnodes.h:161
bool hasRowSecurity
Definition: parsenodes.h:131

◆ rewriteTargetListIU()

static List * rewriteTargetListIU ( List targetList,
CmdType  commandType,
OverridingKind  override,
Relation  target_relation,
int  result_rti,
List **  attrno_list 
)
static

Definition at line 717 of file rewriteHandler.c.

References ATTRIBUTE_IDENTITY_ALWAYS, ATTRIBUTE_IDENTITY_BY_DEFAULT, build_column_default(), CMD_INSERT, CMD_UPDATE, COERCE_IMPLICIT_CAST, coerce_to_domain(), COERCION_IMPLICIT, elog, ereport, errcode(), errdetail(), errhint(), errmsg(), ERROR, TargetEntry::expr, flatCopyTargetEntry(), getOwnedSequence(), InvalidOid, IsA, lappend(), lappend_int(), lfirst, list_concat(), makeConst(), makeNode, makeTargetEntry(), makeVar(), NameStr, NIL, OVERRIDING_SYSTEM_VALUE, OVERRIDING_USER_VALUE, palloc0(), pfree(), process_matched_tle(), pstrdup(), RelationData::rd_att, RelationData::rd_rel, RelationGetNumberOfAttributes, RelationGetRelid, RELKIND_VIEW, TargetEntry::resjunk, TargetEntry::resno, NextValueExpr::seqid, TupleDescAttr, NextValueExpr::typeId, and view_has_instead_trigger().

Referenced by RewriteQuery().

723 {
724  TargetEntry **new_tles;
725  List *new_tlist = NIL;
726  List *junk_tlist = NIL;
727  Form_pg_attribute att_tup;
728  int attrno,
729  next_junk_attrno,
730  numattrs;
731  ListCell *temp;
732 
733  if (attrno_list) /* initialize optional result list */
734  *attrno_list = NIL;
735 
736  /*
737  * We process the normal (non-junk) attributes by scanning the input tlist
738  * once and transferring TLEs into an array, then scanning the array to
739  * build an output tlist. This avoids O(N^2) behavior for large numbers
740  * of attributes.
741  *
742  * Junk attributes are tossed into a separate list during the same tlist
743  * scan, then appended to the reconstructed tlist.
744  */
745  numattrs = RelationGetNumberOfAttributes(target_relation);
746  new_tles = (TargetEntry **) palloc0(numattrs * sizeof(TargetEntry *));
747  next_junk_attrno = numattrs + 1;
748 
749  foreach(temp, targetList)
750  {
751  TargetEntry *old_tle = (TargetEntry *) lfirst(temp);
752 
753  if (!old_tle->resjunk)
754  {
755  /* Normal attr: stash it into new_tles[] */
756  attrno = old_tle->resno;
757  if (attrno < 1 || attrno > numattrs)
758  elog(ERROR, "bogus resno %d in targetlist", attrno);
759  att_tup = TupleDescAttr(target_relation->rd_att, attrno - 1);
760 
761  /* put attrno into attrno_list even if it's dropped */
762  if (attrno_list)
763  *attrno_list = lappend_int(*attrno_list, attrno);
764 
765  /* We can (and must) ignore deleted attributes */
766  if (att_tup->attisdropped)
767  continue;
768 
769  /* Merge with any prior assignment to same attribute */
770  new_tles[attrno - 1] =
771  process_matched_tle(old_tle,
772  new_tles[attrno - 1],
773  NameStr(att_tup->attname));
774  }
775  else
776  {
777  /*
778  * Copy all resjunk tlist entries to junk_tlist, and assign them
779  * resnos above the last real resno.
780  *
781  * Typical junk entries include ORDER BY or GROUP BY expressions
782  * (are these actually possible in an INSERT or UPDATE?), system
783  * attribute references, etc.
784  */
785 
786  /* Get the resno right, but don't copy unnecessarily */
787  if (old_tle->resno != next_junk_attrno)
788  {
789  old_tle = flatCopyTargetEntry(old_tle);
790  old_tle->resno = next_junk_attrno;
791  }
792  junk_tlist = lappend(junk_tlist, old_tle);
793  next_junk_attrno++;
794  }
795  }
796 
797  for (attrno = 1; attrno <= numattrs; attrno++)
798  {
799  TargetEntry *new_tle = new_tles[attrno - 1];
800  bool apply_default;
801 
802  att_tup = TupleDescAttr(target_relation->rd_att, attrno - 1);
803 
804  /* We can (and must) ignore deleted attributes */
805  if (att_tup->attisdropped)
806  continue;
807 
808  /*
809  * Handle the two cases where we need to insert a default expression:
810  * it's an INSERT and there's no tlist entry for the column, or the
811  * tlist entry is a DEFAULT placeholder node.
812  */
813  apply_default = ((new_tle == NULL && commandType == CMD_INSERT) ||
814  (new_tle && new_tle->expr && IsA(new_tle->expr, SetToDefault)));
815 
816  if (commandType == CMD_INSERT)
817  {
818  if (att_tup->attidentity == ATTRIBUTE_IDENTITY_ALWAYS && !apply_default)
819  {
820  if (override != OVERRIDING_SYSTEM_VALUE)
821  ereport(ERROR,
822  (errcode(ERRCODE_GENERATED_ALWAYS),
823  errmsg("cannot insert into column \"%s\"", NameStr(att_tup->attname)),
824  errdetail("Column \"%s\" is an identity column defined as GENERATED ALWAYS.",
825  NameStr(att_tup->attname)),
826  errhint("Use OVERRIDING SYSTEM VALUE to override.")));
827  }
828 
829  if (att_tup->attidentity == ATTRIBUTE_IDENTITY_BY_DEFAULT && override == OVERRIDING_USER_VALUE)
830  apply_default = true;
831  }
832 
833  if (commandType == CMD_UPDATE)
834  {
835  if (att_tup->attidentity == ATTRIBUTE_IDENTITY_ALWAYS && new_tle && !apply_default)
836  ereport(ERROR,
837  (errcode(ERRCODE_GENERATED_ALWAYS),
838  errmsg("column \"%s\" can only be updated to DEFAULT", NameStr(att_tup->attname)),
839  errdetail("Column \"%s\" is an identity column defined as GENERATED ALWAYS.",
840  NameStr(att_tup->attname))));
841  }
842 
843  if (apply_default)
844  {
845  Node *new_expr;
846 
847  if (att_tup->attidentity)
848  {
850 
851  nve->seqid = getOwnedSequence(RelationGetRelid(target_relation), attrno);
852  nve->typeId = att_tup->atttypid;
853 
854  new_expr = (Node *) nve;
855  }
856  else
857  new_expr = build_column_default(target_relation, attrno);
858 
859  /*
860  * If there is no default (ie, default is effectively NULL), we
861  * can omit the tlist entry in the INSERT case, since the planner
862  * can insert a NULL for itself, and there's no point in spending
863  * any more rewriter cycles on the entry. But in the UPDATE case
864  * we've got to explicitly set the column to NULL.
865  */
866  if (!new_expr)
867  {
868  if (commandType == CMD_INSERT)
869  new_tle = NULL;
870  else
871  {
872  new_expr = (Node *) makeConst(att_tup->atttypid,
873  -1,
874  att_tup->attcollation,
875  att_tup->attlen,
876  (Datum) 0,
877  true, /* isnull */
878  att_tup->attbyval);
879  /* this is to catch a NOT NULL domain constraint */
880  new_expr = coerce_to_domain(new_expr,
881  InvalidOid, -1,
882  att_tup->atttypid,
885  -1,
886  false);
887  }
888  }
889 
890  if (new_expr)
891  new_tle = makeTargetEntry((Expr *) new_expr,
892  attrno,
893  pstrdup(NameStr(att_tup->attname)),
894  false);
895  }
896 
897  /*
898  * For an UPDATE on a trigger-updatable view, provide a dummy entry
899  * whenever there is no explicit assignment.
900  */
901  if (new_tle == NULL && commandType == CMD_UPDATE &&
902  target_relation->rd_rel->relkind == RELKIND_VIEW &&
903  view_has_instead_trigger(target_relation, CMD_UPDATE))
904  {
905  Node *new_expr;
906 
907  new_expr = (Node *) makeVar(result_rti,
908  attrno,
909  att_tup->atttypid,
910  att_tup->atttypmod,
911  att_tup->attcollation,
912  0);
913 
914  new_tle = makeTargetEntry((Expr *) new_expr,
915  attrno,
916  pstrdup(NameStr(att_tup->attname)),
917  false);
918  }
919 
920  if (new_tle)
921  new_tlist = lappend(new_tlist, new_tle);
922  }
923 
924  pfree(new_tles);
925 
926  return list_concat(new_tlist, junk_tlist);
927 }
#define NIL
Definition: pg_list.h:69
Oid getOwnedSequence(Oid relid, AttrNumber attnum)
Definition: pg_depend.c:605
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
int errhint(const char *fmt,...)
Definition: elog.c:987
static bool view_has_instead_trigger(Relation view, CmdType event)
#define RelationGetNumberOfAttributes(relation)
Definition: rel.h:431
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
char * pstrdup(const char *in)
Definition: mcxt.c:1063
Definition: nodes.h:512
int errcode(int sqlerrcode)
Definition: elog.c:575
List * list_concat(List *list1, List *list2)
Definition: list.c:321
Form_pg_class rd_rel
Definition: rel.h:114
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:298
void pfree(void *pointer)
Definition: mcxt.c:936
bool resjunk
Definition: primnodes.h:1382
#define ERROR
Definition: elog.h:43
#define ATTRIBUTE_IDENTITY_BY_DEFAULT
Definition: pg_attribute.h:229
Node * coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, CoercionContext ccontext, CoercionForm cformat, int location, bool hideInputCoercion)
Definition: parse_coerce.c:663
int errdetail(const char *fmt,...)
Definition: elog.c:873
AttrNumber resno
Definition: primnodes.h:1376
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
#define ATTRIBUTE_IDENTITY_ALWAYS
Definition: pg_attribute.h:228
TargetEntry * flatCopyTargetEntry(TargetEntry *src_tle)
Definition: makefuncs.c:270
#define ereport(elevel, rest)
Definition: elog.h:122
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:237
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
List * lappend_int(List *list, int datum)
Definition: list.c:146
List * lappend(List *list, void *datum)
Definition: list.c:128
Node * build_column_default(Relation rel, int attrno)
void * palloc0(Size size)
Definition: mcxt.c:864
uintptr_t Datum
Definition: postgres.h:372
TupleDesc rd_att
Definition: rel.h:115
#define InvalidOid
Definition: postgres_ext.h:36
#define makeNode(_type_)
Definition: nodes.h:560
#define lfirst(lc)
Definition: pg_list.h:106
Expr * expr
Definition: primnodes.h:1375
int errmsg(const char *fmt,...)
Definition: elog.c:797
#define RELKIND_VIEW
Definition: pg_class.h:164
static TargetEntry * process_matched_tle(TargetEntry *src_tle, TargetEntry *prior_tle, const char *attrName)
#define NameStr(name)
Definition: c.h:557
#define elog
Definition: elog.h:219
Definition: pg_list.h:45
#define RelationGetRelid(relation)
Definition: rel.h:425

◆ rewriteTargetListUD()

void rewriteTargetListUD ( Query parsetree,
RangeTblEntry target_rte,
Relation  target_relation 
)

Definition at line 1310 of file rewriteHandler.c.

References FdwRoutine::AddForeignUpdateTargets, CMD_DELETE, CMD_UPDATE, Query::commandType, GetFdwRoutineForRelation(), InvalidOid, lappend(), list_length(), makeTargetEntry(), makeVar(), makeWholeRowVar(), pstrdup(), RelationData::rd_rel, RELKIND_FOREIGN_TABLE, RELKIND_MATVIEW, RELKIND_PARTITIONED_TABLE, RELKIND_RELATION, Query::resultRelation, SelfItemPointerAttributeNumber, Query::targetList, TIDOID, TriggerDesc::trig_delete_after_row, TriggerDesc::trig_delete_before_row, TriggerDesc::trig_update_after_row, TriggerDesc::trig_update_before_row, and RelationData::trigdesc.

Referenced by preprocess_targetlist().

1312 {
1313  Var *var = NULL;
1314  const char *attrname;
1315  TargetEntry *tle;
1316 
1317  if (target_relation->rd_rel->relkind == RELKIND_RELATION ||
1318  target_relation->rd_rel->relkind == RELKIND_MATVIEW ||
1319  target_relation->rd_rel->relkind == RELKIND_PARTITIONED_TABLE)
1320  {
1321  /*
1322  * Emit CTID so that executor can find the row to update or delete.
1323  */
1324  var = makeVar(parsetree->resultRelation,
1326  TIDOID,
1327  -1,
1328  InvalidOid,
1329  0);
1330 
1331  attrname = "ctid";
1332  }
1333  else if (target_relation->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
1334  {
1335  /*
1336  * Let the foreign table's FDW add whatever junk TLEs it wants.
1337  */
1338  FdwRoutine *fdwroutine;
1339 
1340  fdwroutine = GetFdwRoutineForRelation(target_relation, false);
1341 
1342  if (fdwroutine->AddForeignUpdateTargets != NULL)
1343  fdwroutine->AddForeignUpdateTargets(parsetree, target_rte,
1344  target_relation);
1345 
1346  /*
1347  * If we have a row-level trigger corresponding to the operation, emit
1348  * a whole-row Var so that executor will have the "old" row to pass to
1349  * the trigger. Alas, this misses system columns.
1350  */
1351  if (target_relation->trigdesc &&
1352  ((parsetree->commandType == CMD_UPDATE &&
1353  (target_relation->trigdesc->trig_update_after_row ||
1354  target_relation->trigdesc->trig_update_before_row)) ||
1355  (parsetree->commandType == CMD_DELETE &&
1356  (target_relation->trigdesc->trig_delete_after_row ||
1357  target_relation->trigdesc->trig_delete_before_row))))
1358  {
1359  var = makeWholeRowVar(target_rte,
1360  parsetree->resultRelation,
1361  0,
1362  false);
1363 
1364  attrname = "wholerow";
1365  }
1366  }
1367 
1368  if (var != NULL)
1369  {
1370  tle = makeTargetEntry((Expr *) var,
1371  list_length(parsetree->targetList) + 1,
1372  pstrdup(attrname),
1373  true);
1374 
1375  parsetree->targetList = lappend(parsetree->targetList, tle);
1376  }
1377 }
char * pstrdup(const char *in)
Definition: mcxt.c:1063
int resultRelation
Definition: parsenodes.h:120
#define RELKIND_MATVIEW
Definition: pg_class.h:165
Var * makeWholeRowVar(RangeTblEntry *rte, Index varno, Index varlevelsup, bool allowScalar)
Definition: makefuncs.c:134
Form_pg_class rd_rel
Definition: rel.h:114
Definition: primnodes.h:163
AddForeignUpdateTargets_function AddForeignUpdateTargets
Definition: fdwapi.h:200
List * targetList
Definition: parsenodes.h:138
#define TIDOID
Definition: pg_type.h:332
TriggerDesc * trigdesc
Definition: rel.h:120
bool trig_delete_after_row
Definition: reltrigger.h:66
bool trig_update_before_row
Definition: reltrigger.h:60
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:237
Var * makeVar(Index varno, AttrNumber varattno, Oid vartype, int32 vartypmod, Oid varcollid, Index varlevelsup)
Definition: makefuncs.c:67
bool trig_update_after_row
Definition: reltrigger.h:61
List * lappend(List *list, void *datum)
Definition: list.c:128
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
#define InvalidOid
Definition: postgres_ext.h:36
CmdType commandType
Definition: parsenodes.h:110
static int list_length(const List *l)
Definition: pg_list.h:89
FdwRoutine * GetFdwRoutineForRelation(Relation relation, bool makecopy)
Definition: foreign.c:395
#define SelfItemPointerAttributeNumber
Definition: sysattr.h:21
#define RELKIND_RELATION
Definition: pg_class.h:160
bool trig_delete_before_row
Definition: reltrigger.h:65

◆ rewriteTargetView()

static Query* rewriteTargetView ( Query parsetree,
Relation  view 
)
static

Definition at line 2711 of file rewriteHandler.c.

References _, acquireLocksOnSubLinks(), AddQual(), adjust_view_column_set(), Assert, bms_add_member(), bms_is_empty(), WithCheckOption::cascaded, ChangeVarNodes(), RangeTblEntry::checkAsUser, checkExprHasSubLink(), CMD_DELETE, CMD_INSERT, CMD_UPDATE, Query::commandType, copyObject, elog, ereport, errcode(), errdetail_internal(), errhint(), errmsg(), ERROR, TargetEntry::expr, FirstLowInvalidHeapAttributeNumber, acquireLocksOnSubLinks_context::for_execute, FromExpr::fromlist, get_tle_by_resno(), get_view_query(), Query::hasSubLinks, heap_close, heap_open(), RangeTblEntry::inh, RangeTblEntry::insertedCols, IsA, Query::jointree, WithCheckOption::kind, lappend(), lcons(), lfirst, linitial, linitial_node, list_length(), makeNode, NIL, NoLock, Query::onConflict, OnConflictExpr::onConflictSet, WithCheckOption::polname, pstrdup(), QTW_IGNORE_RC_SUBQUERIES, WithCheckOption::qual, FromExpr::quals, query_tree_walker(), RelationData::rd_rel, RelationGetRelationName, RelationHasCascadedCheckOption, RelationHasCheckOption, RelationIsSecurityView, RangeTblEntry::relid, RangeTblEntry::relkind, WithCheckOption::relname, REPLACEVARS_REPORT_ERROR, ReplaceVarsFromTargetList(), RangeTblEntry::requiredPerms, TargetEntry::resjunk, TargetEntry::resno, Query::resultRelation, RowExclusiveLock, rt_fetch, Query::rtable, RTE_RELATION, RangeTblEntry::rtekind, RangeTblRef::rtindex, RangeTblEntry::securityQuals, Query::targetList, RangeTblEntry::updatedCols, view_cols_are_auto_updatable(), view_query_is_auto_updatable(), and WCO_VIEW_CHECK.

Referenced by RewriteQuery().

2712 {
2713  Query *viewquery;
2714  const char *auto_update_detail;
2715  RangeTblRef *rtr;
2716  int base_rt_index;
2717  int new_rt_index;
2718  RangeTblEntry *base_rte;
2719  RangeTblEntry *view_rte;
2720  RangeTblEntry *new_rte;
2721  Relation base_rel;
2722  List *view_targetlist;
2723  ListCell *lc;
2724 
2725  /*
2726  * Get the Query from the view's ON SELECT rule. We're going to munge the
2727  * Query to change the view's base relation into the target relation,
2728  * along with various other changes along the way, so we need to make a
2729  * copy of it (get_view_query() returns a pointer into the relcache, so we
2730  * have to treat it as read-only).
2731  */
2732  viewquery = copyObject(get_view_query(view));
2733 
2734  /* The view must be updatable, else fail */
2735  auto_update_detail =
2736  view_query_is_auto_updatable(viewquery,
2737  parsetree->commandType != CMD_DELETE);
2738 
2739  if (auto_update_detail)
2740  {
2741  /* messages here should match execMain.c's CheckValidResultRel */
2742  switch (parsetree->commandType)
2743  {
2744  case CMD_INSERT:
2745  ereport(ERROR,
2746  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2747  errmsg("cannot insert into view \"%s\"",
2748  RelationGetRelationName(view)),
2749  errdetail_internal("%s", _(auto_update_detail)),
2750  errhint("To enable inserting into the view, provide an INSTEAD OF INSERT trigger or an unconditional ON INSERT DO INSTEAD rule.")));
2751  break;
2752  case CMD_UPDATE:
2753  ereport(ERROR,
2754  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2755  errmsg("cannot update view \"%s\"",
2756  RelationGetRelationName(view)),
2757  errdetail_internal("%s", _(auto_update_detail)),
2758  errhint("To enable updating the view, provide an INSTEAD OF UPDATE trigger or an unconditional ON UPDATE DO INSTEAD rule.")));
2759  break;
2760  case CMD_DELETE:
2761  ereport(ERROR,
2762  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
2763  errmsg("cannot delete from view \"%s\"",
2764  RelationGetRelationName(view)),
2765  errdetail_internal("%s", _(auto_update_detail)),
2766  errhint("To enable deleting from the view, provide an INSTEAD OF DELETE trigger or an unconditional ON DELETE DO INSTEAD rule.")));
2767  break;
2768  default:
2769  elog(ERROR, "unrecognized CmdType: %d",
2770  (int) parsetree->commandType);
2771  break;
2772  }
2773  }
2774 
2775  /*
2776  * For INSERT/UPDATE the modified columns must all be updatable. Note that
2777  * we get the modified columns from the query's targetlist, not from the
2778  * result RTE's insertedCols and/or updatedCols set, since
2779  * rewriteTargetListIU may have added additional targetlist entries for
2780  * view defaults, and these must also be updatable.
2781  */
2782  if (parsetree->commandType != CMD_DELETE)
2783  {
2784  Bitmapset *modified_cols = NULL;
2785  char *non_updatable_col;
2786 
2787  foreach(lc, parsetree->targetList)
2788  {
2789  TargetEntry *tle = (TargetEntry *) lfirst(lc);
2790 
2791  if (!tle->resjunk)
2792  modified_cols = bms_add_member(modified_cols,
2794  }
2795 
2796  if (parsetree->onConflict)
2797  {
2798  foreach(lc, parsetree->onConflict->onConflictSet)
2799  {
2800  TargetEntry *tle = (TargetEntry *) lfirst(lc);
2801 
2802  if (!tle->resjunk)
2803  modified_cols = bms_add_member(modified_cols,
2805  }
2806  }
2807 
2808  auto_update_detail = view_cols_are_auto_updatable(viewquery,
2809  modified_cols,
2810  NULL,
2811  &non_updatable_col);
2812  if (auto_update_detail)
2813  {
2814  /*
2815  * This is a different error, caused by an attempt to update a
2816  * non-updatable column in an otherwise updatable view.
2817  */
2818  switch (parsetree->commandType)
2819  {
2820  case CMD_INSERT:
2821  ereport(ERROR,
2822  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2823  errmsg("cannot insert into column \"%s\" of view \"%s\"",
2824  non_updatable_col,
2825  RelationGetRelationName(view)),
2826  errdetail_internal("%s", _(auto_update_detail))));
2827  break;
2828  case CMD_UPDATE:
2829  ereport(ERROR,
2830  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
2831  errmsg("cannot update column \"%s\" of view \"%s\"",
2832  non_updatable_col,
2833  RelationGetRelationName(view)),
2834  errdetail_internal("%s", _(auto_update_detail))));
2835  break;
2836  default:
2837  elog(ERROR, "unrecognized CmdType: %d",
2838  (int) parsetree->commandType);
2839  break;
2840  }
2841  }
2842  }
2843 
2844  /* Locate RTE describing the view in the outer query */
2845  view_rte = rt_fetch(parsetree->resultRelation, parsetree->rtable);
2846 
2847  /*
2848  * If we get here, view_query_is_auto_updatable() has verified that the
2849  * view contains a single base relation.
2850  */
2851  Assert(list_length(viewquery->jointree->fromlist) == 1);
2852  rtr = linitial_node(RangeTblRef, viewquery->jointree->fromlist);
2853 
2854  base_rt_index = rtr->rtindex;
2855  base_rte = rt_fetch(base_rt_index, viewquery->rtable);
2856  Assert(base_rte->rtekind == RTE_RELATION);
2857 
2858  /*
2859  * Up to now, the base relation hasn't been touched at all in our query.
2860  * We need to acquire lock on it before we try to do anything with it.
2861  * (The subsequent recursive call of RewriteQuery will suppose that we
2862  * already have the right lock!) Since it will become the query target
2863  * relation, RowExclusiveLock is always the right thing.
2864  */
2865  base_rel = heap_open(base_rte->relid, RowExclusiveLock);
2866 
2867  /*
2868  * While we have the relation open, update the RTE's relkind, just in case
2869  * it changed since this view was made (cf. AcquireRewriteLocks).
2870  */
2871  base_rte->relkind = base_rel->rd_rel->relkind;
2872 
2873  heap_close(base_rel, NoLock);
2874 
2875  /*
2876  * If the view query contains any sublink subqueries then we need to also
2877  * acquire locks on any relations they refer to. We know that there won't
2878  * be any subqueries in the range table or CTEs, so we can skip those, as
2879  * in AcquireRewriteLocks.
2880  */
2881  if (viewquery->hasSubLinks)
2882  {
2884 
2885  context.for_execute = true;
2886  query_tree_walker(viewquery, acquireLocksOnSubLinks, &context,
2888  }
2889 
2890  /*
2891  * Create a new target RTE describing the base relation, and add it to the
2892  * outer query's rangetable. (What's happening in the next few steps is
2893  * very much like what the planner would do to "pull up" the view into the
2894  * outer query. Perhaps someday we should refactor things enough so that
2895  * we can share code with the planner.)
2896  */
2897  new_rte = (RangeTblEntry *) base_rte;
2898  parsetree->rtable = lappend(parsetree->rtable, new_rte);
2899  new_rt_index = list_length(parsetree->rtable);
2900 
2901  /*
2902  * INSERTs never inherit. For UPDATE/DELETE, we use the view query's
2903  * inheritance flag for the base relation.
2904  */
2905  if (parsetree->commandType == CMD_INSERT)
2906  new_rte->inh = false;
2907 
2908  /*
2909  * Adjust the view's targetlist Vars to reference the new target RTE, ie
2910  * make their varnos be new_rt_index instead of base_rt_index. There can
2911  * be no Vars for other rels in the tlist, so this is sufficient to pull
2912  * up the tlist expressions for use in the outer query. The tlist will
2913  * provide the replacement expressions used by ReplaceVarsFromTargetList
2914  * below.
2915  */
2916  view_targetlist = viewquery->targetList;
2917 
2918  ChangeVarNodes((Node *) view_targetlist,
2919  base_rt_index,
2920  new_rt_index,
2921  0);
2922 
2923  /*
2924  * Mark the new target RTE for the permissions checks that we want to
2925  * enforce against the view owner, as distinct from the query caller. At
2926  * the relation level, require the same INSERT/UPDATE/DELETE permissions
2927  * that the query caller needs against the view. We drop the ACL_SELECT
2928  * bit that is presumably in new_rte->requiredPerms initially.
2929  *
2930  * Note: the original view RTE remains in the query's rangetable list.
2931  * Although it will be unused in the query plan, we need it there so that
2932  * the executor still performs appropriate permissions checks for the
2933  * query caller's use of the view.
2934  */
2935  new_rte->checkAsUser = view->rd_rel->relowner;
2936  new_rte->requiredPerms = view_rte->requiredPerms;
2937 
2938  /*
2939  * Now for the per-column permissions bits.
2940  *
2941  * Initially, new_rte contains selectedCols permission check bits for all
2942  * base-rel columns referenced by the view, but since the view is a SELECT
2943  * query its insertedCols/updatedCols is empty. We set insertedCols and
2944  * updatedCols to include all the columns the outer query is trying to
2945  * modify, adjusting the column numbers as needed. But we leave
2946  * selectedCols as-is, so the view owner must have read permission for all
2947  * columns used in the view definition, even if some of them are not read
2948  * by the outer query. We could try to limit selectedCols to only columns
2949  * used in the transformed query, but that does not correspond to what
2950  * happens in ordinary SELECT usage of a view: all referenced columns must
2951  * have read permission, even if optimization finds that some of them can
2952  * be discarded during query transformation. The flattening we're doing
2953  * here is an optional optimization, too. (If you are unpersuaded and
2954  * want to change this, note that applying adjust_view_column_set to
2955  * view_rte->selectedCols is clearly *not* the right answer, since that
2956  * neglects base-rel columns used in the view's WHERE quals.)
2957  *
2958  * This step needs the modified view targetlist, so we have to do things
2959  * in this order.
2960  */
2961  Assert(bms_is_empty(new_rte->insertedCols) &&
2962  bms_is_empty(new_rte->updatedCols));
2963 
2964  new_rte->insertedCols = adjust_view_column_set(view_rte->insertedCols,
2965  view_targetlist);
2966 
2967  new_rte->updatedCols = adjust_view_column_set(view_rte->updatedCols,
2968  view_targetlist);
2969 
2970  /*
2971  * Move any security barrier quals from the view RTE onto the new target
2972  * RTE. Any such quals should now apply to the new target RTE and will
2973  * not reference the original view RTE in the rewritten query.
2974  */
2975  new_rte->securityQuals = view_rte->securityQuals;
2976  view_rte->securityQuals = NIL;
2977 
2978  /*
2979  * Now update all Vars in the outer query that reference the view to
2980  * reference the appropriate column of the base relation instead.
2981  */
2982  parsetree = (Query *)
2983  ReplaceVarsFromTargetList((Node *) parsetree,
2984  parsetree->resultRelation,
2985  0,
2986  view_rte,
2987  view_targetlist,
2989  0,
2990  &parsetree->hasSubLinks);
2991 
2992  /*
2993  * Update all other RTI references in the query that point to the view
2994  * (for example, parsetree->resultRelation itself) to point to the new
2995  * base relation instead. Vars will not be affected since none of them
2996  * reference parsetree->resultRelation any longer.
2997  */
2998  ChangeVarNodes((Node *) parsetree,
2999  parsetree->resultRelation,
3000  new_rt_index,
3001  0);
3002  Assert(parsetree->resultRelation == new_rt_index);
3003 
3004  /*
3005  * For INSERT/UPDATE we must also update resnos in the targetlist to refer
3006  * to columns of the base relation, since those indicate the target
3007  * columns to be affected.
3008  *
3009  * Note that this destroys the resno ordering of the targetlist, but that
3010  * will be fixed when we recurse through rewriteQuery, which will invoke
3011  * rewriteTargetListIU again on the updated targetlist.
3012  */
3013  if (parsetree->commandType != CMD_DELETE)
3014  {
3015  foreach(lc, parsetree->targetList)
3016  {
3017  TargetEntry *tle = (TargetEntry *) lfirst(lc);
3018  TargetEntry *view_tle;
3019 
3020  if (tle->resjunk)
3021  continue;
3022 
3023  view_tle = get_tle_by_resno(view_targetlist, tle->resno);
3024  if (view_tle != NULL && !view_tle->resjunk && IsA(view_tle->expr, Var))
3025  tle->resno = ((Var *) view_tle->expr)->varattno;
3026  else
3027  elog(ERROR, "attribute number %d not found in view targetlist",
3028  tle->resno);
3029  }
3030  }
3031 
3032  /*
3033  * For UPDATE/DELETE, pull up any WHERE quals from the view. We know that
3034  * any Vars in the quals must reference the one base relation, so we need
3035  * only adjust their varnos to reference the new target (just the same as
3036  * we did with the view targetlist).
3037  *
3038  * If it's a security-barrier view, its WHERE quals must be applied before
3039  * quals from the outer query, so we attach them to the RTE as security
3040  * barrier quals rather than adding them to the main WHERE clause.
3041  *
3042  * For INSERT, the view's quals can be ignored in the main query.
3043  */
3044  if (parsetree->commandType != CMD_INSERT &&
3045  viewquery->jointree->quals != NULL)
3046  {
3047  Node *viewqual = (Node *) viewquery->jointree->quals;
3048 
3049  /*
3050  * Even though we copied viewquery already at the top of this
3051  * function, we must duplicate the viewqual again here, because we may
3052  * need to use the quals again below for a WithCheckOption clause.
3053  */
3054  viewqual = copyObject(viewqual);
3055 
3056  ChangeVarNodes(viewqual, base_rt_index, new_rt_index, 0);
3057 
3058  if (RelationIsSecurityView(view))
3059  {
3060  /*
3061  * The view's quals go in front of existing barrier quals: those
3062  * would have come from an outer level of security-barrier view,
3063  * and so must get evaluated later.
3064  *
3065  * Note: the parsetree has been mutated, so the new_rte pointer is
3066  * stale and needs to be re-computed.
3067  */
3068  new_rte = rt_fetch(new_rt_index, parsetree->rtable);
3069  new_rte->securityQuals = lcons(viewqual, new_rte->securityQuals);
3070 
3071  /*
3072  * Do not set parsetree->hasRowSecurity, because these aren't RLS
3073  * conditions (they aren't affected by enabling/disabling RLS).
3074  */
3075 
3076  /*
3077  * Make sure that the query is marked correctly if the added qual
3078  * has sublinks.
3079  */
3080  if (!parsetree->hasSubLinks)
3081  parsetree->hasSubLinks = checkExprHasSubLink(viewqual);
3082  }
3083  else
3084  AddQual(parsetree, (Node *) viewqual);
3085  }
3086 
3087  /*
3088  * For INSERT/UPDATE, if the view has the WITH CHECK OPTION, or any parent
3089  * view specified WITH CASCADED CHECK OPTION, add the quals from the view
3090  * to the query's withCheckOptions list.
3091  */
3092  if (parsetree->commandType != CMD_DELETE)
3093  {
3094  bool has_wco = RelationHasCheckOption(view);
3095  bool cascaded = RelationHasCascadedCheckOption(view);
3096 
3097  /*
3098  * If the parent view has a cascaded check option, treat this view as
3099  * if it also had a cascaded check option.
3100  *
3101  * New WithCheckOptions are added to the start of the list, so if
3102  * there is a cascaded check option, it will be the first item in the
3103  * list.
3104  */
3105  if (parsetree->withCheckOptions != NIL)
3106  {
3107  WithCheckOption *parent_wco =
3108  (WithCheckOption *) linitial(parsetree->withCheckOptions);
3109 
3110  if (parent_wco->cascaded)
3111  {
3112  has_wco = true;
3113  cascaded = true;
3114  }
3115  }
3116 
3117  /*
3118  * Add the new WithCheckOption to the start of the list, so that
3119  * checks on inner views are run before checks on outer views, as
3120  * required by the SQL standard.
3121  *
3122  * If the new check is CASCADED, we need to add it even if this view
3123  * has no quals, since there may be quals on child views. A LOCAL
3124  * check can be omitted if this view has no quals.
3125  */
3126  if (has_wco && (cascaded || viewquery->jointree->quals != NULL))
3127  {
3128  WithCheckOption *wco;
3129 
3130  wco = makeNode(WithCheckOption);
3131  wco->kind = WCO_VIEW_CHECK;
3132  wco->relname = pstrdup(RelationGetRelationName(view));
3133  wco->polname = NULL;
3134  wco->qual = NULL;
3135  wco->cascaded = cascaded;
3136 
3137  parsetree->withCheckOptions = lcons(wco,
3138  parsetree->withCheckOptions);
3139 
3140  if (viewquery->jointree->quals != NULL)
3141  {
3142  wco->qual = (Node *) viewquery->jointree->quals;
3143  ChangeVarNodes(wco->qual, base_rt_index, new_rt_index, 0);
3144 
3145  /*
3146  * Make sure that the query is marked correctly if the added
3147  * qual has sublinks. We can skip this check if the query is
3148  * already marked, or if the command is an UPDATE, in which
3149  * case the same qual will have already been added, and this
3150  * check will already have been done.
3151  */
3152  if (!parsetree->hasSubLinks &&
3153  parsetree->commandType != CMD_UPDATE)
3154  parsetree->hasSubLinks = checkExprHasSubLink(wco->qual);
3155  }
3156  }
3157  }
3158 
3159  return parsetree;
3160 }
#define NIL
Definition: pg_list.h:69
bool query_tree_walker(Query *query, bool(*walker)(), void *context, int flags)
Definition: nodeFuncs.c:2245
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
int errhint(const char *fmt,...)
Definition: elog.c:987
static bool acquireLocksOnSubLinks(Node *node, acquireLocksOnSubLinks_context *context)
FromExpr * jointree
Definition: parsenodes.h:136
bool checkExprHasSubLink(Node *node)
Definition: rewriteManip.c:277
OnConflictExpr * onConflict
Definition: parsenodes.h:142
List * withCheckOptions
Definition: parsenodes.h:169
List * securityQuals
Definition: parsenodes.h:1064
char * pstrdup(const char *in)
Definition: mcxt.c:1063
static const char * view_cols_are_auto_updatable(Query *viewquery, Bitmapset *required_cols, Bitmapset **updatable_cols, char **non_updatable_col)
int resultRelation
Definition: parsenodes.h:120
Definition: nodes.h:512
int errcode(int sqlerrcode)
Definition: elog.c:575
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:28
AclMode requiredPerms
Definition: parsenodes.h:1059
List * fromlist
Definition: primnodes.h:1478
#define heap_close(r, l)
Definition: heapam.h:97
Form_pg_class rd_rel
Definition: rel.h:114
Definition: primnodes.h:163
#define linitial_node(type, l)
Definition: pg_list.h:114
Node * quals
Definition: primnodes.h:1479
int errdetail_internal(const char *fmt,...)
Definition: elog.c:900
List * targetList
Definition: parsenodes.h:138
#define QTW_IGNORE_RC_SUBQUERIES
Definition: nodeFuncs.h:22
bool resjunk
Definition: primnodes.h:1382
#define linitial(l)
Definition: pg_list.h:111
List * rtable
Definition: parsenodes.h:135
#define ERROR
Definition: elog.h:43
#define NoLock
Definition: lockdefs.h:34
#define RowExclusiveLock
Definition: lockdefs.h:38
AttrNumber resno
Definition: primnodes.h:1376
#define RelationGetRelationName(relation)
Definition: rel.h:445
#define ereport(elevel, rest)
Definition: elog.h:122
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
List * lappend(List *list, void *datum)
Definition: list.c:128
bool bms_is_empty(const Bitmapset *a)
Definition: bitmapset.c:707
Node * ReplaceVarsFromTargetList(Node *node, int target_varno, int sublevels_up, RangeTblEntry *target_rte, List *targetlist, ReplaceVarsNoMatchOption nomatch_option, int nomatch_varno, bool *outer_hasSubLinks)
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1290
Bitmapset * updatedCols
Definition: parsenodes.h:1063
static Bitmapset * adjust_view_column_set(Bitmapset *cols, List *targetlist)
CmdType commandType
Definition: parsenodes.h:110
List * lcons(void *datum, List *list)
Definition: list.c:259
#define makeNode(_type_)
Definition: nodes.h:560
#define Assert(condition)
Definition: c.h:680
#define lfirst(lc)
Definition: pg_list.h:106
void AddQual(Query *parsetree, Node *qual)
Definition: rewriteManip.c:976
Expr * expr
Definition: primnodes.h:1375
static int list_length(const List *l)
Definition: pg_list.h:89
#define RelationHasCascadedCheckOption(relation)
Definition: rel.h:386
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:742
RTEKind rtekind
Definition: parsenodes.h:951
int errmsg(const char *fmt,...)
Definition: elog.c:797
bool hasSubLinks
Definition: parsenodes.h:126
Query * get_view_query(Relation view)
Bitmapset * insertedCols
Definition: parsenodes.h:1062
#define RelationIsSecurityView(relation)
Definition: rel.h:356
TargetEntry * get_tle_by_resno(List *tlist, AttrNumber resno)
List * onConflictSet
Definition: primnodes.h:1503
void ChangeVarNodes(Node *node, int rt_index, int new_index, int sublevels_up)
Definition: rewriteManip.c:607
#define elog
Definition: elog.h:219
#define copyObject(obj)
Definition: nodes.h:625
const char * view_query_is_auto_updatable(Query *viewquery, bool check_cols)
Definition: pg_list.h:45
#define _(x)
Definition: elog.c:84
#define RelationHasCheckOption(relation)
Definition: rel.h:365

◆ rewriteValuesRTE()

static void rewriteValuesRTE ( RangeTblEntry rte,
Relation  target_relation,
List attrnos 
)
static

Definition at line 1221 of file rewriteHandler.c.

References Assert, build_column_default(), COERCE_IMPLICIT_CAST, coerce_to_domain(), COERCION_IMPLICIT, forboth, InvalidOid, IsA, lappend(), lfirst, lfirst_int, linitial, list_length(), makeConst(), NIL, RelationData::rd_att, searchForDefault(), TupleDescAttr, and RangeTblEntry::values_lists.

Referenced by RewriteQuery().

1222 {
1223  List *newValues;
1224  ListCell *lc;
1225 
1226  /*
1227  * Rebuilding all the lists is a pretty expensive proposition in a big
1228  * VALUES list, and it's a waste of time if there aren't any DEFAULT
1229  * placeholders. So first scan to see if there are any.
1230  */
1231  if (!searchForDefault(rte))
1232  return; /* nothing to do */
1233 
1234  /* Check list lengths (we can assume all the VALUES sublists are alike) */
1235  Assert(list_length(attrnos) == list_length(linitial(rte->values_lists)));
1236 
1237  newValues = NIL;
1238  foreach(lc, rte->values_lists)
1239  {
1240  List *sublist = (List *) lfirst(lc);
1241  List *newList = NIL;
1242  ListCell *lc2;
1243  ListCell *lc3;
1244 
1245  forboth(lc2, sublist, lc3, attrnos)
1246  {
1247  Node *col = (Node *) lfirst(lc2);
1248  int attrno = lfirst_int(lc3);
1249 
1250  if (IsA(col, SetToDefault))
1251  {
1252  Form_pg_attribute att_tup;
1253  Node *new_expr;
1254 
1255  att_tup = TupleDescAttr(target_relation->rd_att, attrno - 1);
1256 
1257  if (!att_tup->attisdropped)
1258  new_expr = build_column_default(target_relation, attrno);
1259  else
1260  new_expr = NULL; /* force a NULL if dropped */
1261 
1262  /*
1263  * If there is no default (ie, default is effectively NULL),
1264  * we've got to explicitly set the column to NULL.
1265  */
1266  if (!new_expr)
1267  {
1268  new_expr = (Node *) makeConst(att_tup->atttypid,
1269  -1,
1270  att_tup->attcollation,
1271  att_tup->attlen,
1272  (Datum) 0,
1273  true, /* isnull */
1274  att_tup->attbyval);
1275  /* this is to catch a NOT NULL domain constraint */
1276  new_expr = coerce_to_domain(new_expr,
1277  InvalidOid, -1,
1278  att_tup->atttypid,
1281  -1,
1282  false);
1283  }
1284  newList = lappend(newList, new_expr);
1285  }
1286  else
1287  newList = lappend(newList, col);
1288  }
1289  newValues = lappend(newValues, newList);
1290  }
1291  rte->values_lists = newValues;
1292 }
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
#define forboth(cell1, list1, cell2, list2)
Definition: pg_list.h:180
#define TupleDescAttr(tupdesc, i)
Definition: tupdesc.h:90
Definition: nodes.h:512
Const * makeConst(Oid consttype, int32 consttypmod, Oid constcollid, int constlen, Datum constvalue, bool constisnull, bool constbyval)
Definition: makefuncs.c:298
List * values_lists
Definition: parsenodes.h:1016
#define linitial(l)
Definition: pg_list.h:111
static bool searchForDefault(RangeTblEntry *rte)
#define lfirst_int(lc)
Definition: pg_list.h:107
Node * coerce_to_domain(Node *arg, Oid baseTypeId, int32 baseTypeMod, Oid typeId, CoercionContext ccontext, CoercionForm cformat, int location, bool hideInputCoercion)
Definition: parse_coerce.c:663
FormData_pg_attribute * Form_pg_attribute
Definition: pg_attribute.h:187
List * lappend(List *list, void *datum)
Definition: list.c:128
Node * build_column_default(Relation rel, int attrno)
uintptr_t Datum
Definition: postgres.h:372
TupleDesc rd_att
Definition: rel.h:115
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:680
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89
Definition: pg_list.h:45

◆ searchForDefault()

static bool searchForDefault ( RangeTblEntry rte)
static

Definition at line 1189 of file rewriteHandler.c.

References IsA, lfirst, and RangeTblEntry::values_lists.

Referenced by rewriteValuesRTE().

1190 {
1191  ListCell *lc;
1192 
1193  foreach(lc, rte->values_lists)
1194  {
1195  List *sublist = (List *) lfirst(lc);
1196  ListCell *lc2;
1197 
1198  foreach(lc2, sublist)
1199  {
1200  Node *col = (Node *) lfirst(lc2);
1201 
1202  if (IsA(col, SetToDefault))
1203  return true;
1204  }
1205  }
1206  return false;
1207 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
Definition: nodes.h:512
List * values_lists
Definition: parsenodes.h:1016
#define lfirst(lc)
Definition: pg_list.h:106
Definition: pg_list.h:45

◆ view_col_is_auto_updatable()

static const char* view_col_is_auto_updatable ( RangeTblRef rtr,
TargetEntry tle 
)
static

Definition at line 2207 of file rewriteHandler.c.

References TargetEntry::expr, gettext_noop, IsA, TargetEntry::resjunk, RangeTblRef::rtindex, Var::varattno, Var::varlevelsup, and Var::varno.

Referenced by view_cols_are_auto_updatable(), and view_query_is_auto_updatable().

2208 {
2209  Var *var = (Var *) tle->expr;
2210 
2211  /*
2212  * For now, the only updatable columns we support are those that are Vars
2213  * referring to user columns of the underlying base relation.
2214  *
2215  * The view targetlist may contain resjunk columns (e.g., a view defined
2216  * like "SELECT * FROM t ORDER BY a+b" is auto-updatable) but such columns
2217  * are not auto-updatable, and in fact should never appear in the outer
2218  * query's targetlist.
2219  */
2220  if (tle->resjunk)
2221  return gettext_noop("Junk view columns are not updatable.");
2222 
2223  if (!IsA(var, Var) ||
2224  var->varno != rtr->rtindex ||
2225  var->varlevelsup != 0)
2226  return gettext_noop("View columns that are not columns of their base relation are not updatable.");
2227 
2228  if (var->varattno < 0)
2229  return gettext_noop("View columns that refer to system columns are not updatable.");
2230 
2231  if (var->varattno == 0)
2232  return gettext_noop("View columns that return whole-row references are not updatable.");
2233 
2234  return NULL; /* the view column is updatable */
2235 }
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
Index varlevelsup
Definition: primnodes.h:173
#define gettext_noop(x)
Definition: c.h:1005
AttrNumber varattno
Definition: primnodes.h:168
Definition: primnodes.h:163
bool resjunk
Definition: primnodes.h:1382
Index varno
Definition: primnodes.h:166
Expr * expr
Definition: primnodes.h:1375

◆ view_cols_are_auto_updatable()

static const char* view_cols_are_auto_updatable ( Query viewquery,
Bitmapset required_cols,
Bitmapset **  updatable_cols,
char **  non_updatable_col 
)
static

Definition at line 2397 of file rewriteHandler.c.

References Assert, bms_add_member(), bms_is_member(), FirstLowInvalidHeapAttributeNumber, FromExpr::fromlist, Query::jointree, lfirst, linitial_node, list_length(), TargetEntry::resname, Query::targetList, and view_col_is_auto_updatable().

Referenced by relation_is_updatable(), and rewriteTargetView().

2401 {
2402  RangeTblRef *rtr;
2403  AttrNumber col;
2404  ListCell *cell;
2405 
2406  /*
2407  * The caller should have verified that this view is auto-updatable and so
2408  * there should be a single base relation.
2409  */
2410  Assert(list_length(viewquery->jointree->fromlist) == 1);
2411  rtr = linitial_node(RangeTblRef, viewquery->jointree->fromlist);
2412 
2413  /* Initialize the optional return values */
2414  if (updatable_cols != NULL)
2415  *updatable_cols = NULL;
2416  if (non_updatable_col != NULL)
2417  *non_updatable_col = NULL;
2418 
2419  /* Test each view column for updatability */
2421  foreach(cell, viewquery->targetList)
2422  {
2423  TargetEntry *tle = (TargetEntry *) lfirst(cell);
2424  const char *col_update_detail;
2425 
2426  col++;
2427  col_update_detail = view_col_is_auto_updatable(rtr, tle);
2428 
2429  if (col_update_detail == NULL)
2430  {
2431  /* The column is updatable */
2432  if (updatable_cols != NULL)
2433  *updatable_cols = bms_add_member(*updatable_cols, col);
2434  }
2435  else if (bms_is_member(col, required_cols))
2436  {
2437  /* The required column is not updatable */
2438  if (non_updatable_col != NULL)
2439  *non_updatable_col = tle->resname;
2440  return col_update_detail;
2441  }
2442  }
2443 
2444  return NULL; /* all the required view columns are updatable */
2445 }
FromExpr * jointree
Definition: parsenodes.h:136
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:28
List * fromlist
Definition: primnodes.h:1478
char * resname
Definition: primnodes.h:1377
#define linitial_node(type, l)
Definition: pg_list.h:114
List * targetList
Definition: parsenodes.h:138
#define Assert(condition)
Definition: c.h:680
#define lfirst(lc)
Definition: pg_list.h:106
static int list_length(const List *l)
Definition: pg_list.h:89
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:742
static const char * view_col_is_auto_updatable(RangeTblRef *rtr, TargetEntry *tle)
bool bms_is_member(int x, const Bitmapset *a)
Definition: bitmapset.c:464
int16 AttrNumber
Definition: attnum.h:21

◆ view_has_instead_trigger()

static bool view_has_instead_trigger ( Relation  view,
CmdType  event 
)
static

Definition at line 2172 of file rewriteHandler.c.

References CMD_DELETE, CMD_INSERT, CMD_UPDATE, elog, ERROR, TriggerDesc::trig_delete_instead_row, TriggerDesc::trig_insert_instead_row, TriggerDesc::trig_update_instead_row, and RelationData::trigdesc.

Referenced by RewriteQuery(), and rewriteTargetListIU().

2173 {
2174  TriggerDesc *trigDesc = view->trigdesc;
2175 
2176  switch (event)
2177  {
2178  case CMD_INSERT:
2179  if (trigDesc && trigDesc->trig_insert_instead_row)
2180  return true;
2181  break;
2182  case CMD_UPDATE:
2183  if (trigDesc && trigDesc->trig_update_instead_row)
2184  return true;
2185  break;
2186  case CMD_DELETE:
2187  if (trigDesc && trigDesc->trig_delete_instead_row)
2188  return true;
2189  break;
2190  default:
2191  elog(ERROR, "unrecognized CmdType: %d", (int) event);
2192  break;
2193  }
2194  return false;
2195 }
bool trig_insert_instead_row
Definition: reltrigger.h:57
#define ERROR
Definition: elog.h:43
TriggerDesc * trigdesc
Definition: rel.h:120
bool trig_update_instead_row
Definition: reltrigger.h:62
bool trig_delete_instead_row
Definition: reltrigger.h:67
#define elog
Definition: elog.h:219

◆ view_query_is_auto_updatable()

const char* view_query_is_auto_updatable ( Query viewquery,
bool  check_cols 
)

Definition at line 2252 of file rewriteHandler.c.

References Query::cteList, Query::distinctClause, FromExpr::fromlist, gettext_noop, Query::groupClause, Query::groupingSets, Query::hasAggs, Query::hasTargetSRFs, Query::hasWindowFuncs, Query::havingQual, IsA, Query::jointree, lfirst, Query::limitCount, Query::limitOffset, linitial, list_length(), NIL, RangeTblEntry::relkind, RELKIND_FOREIGN_TABLE, RELKIND_PARTITIONED_TABLE, RELKIND_RELATION, RELKIND_VIEW, rt_fetch, Query::rtable, RTE_RELATION, RangeTblEntry::rtekind, RangeTblRef::rtindex, Query::setOperations, RangeTblEntry::tablesample, Query::targetList, and view_col_is_auto_updatable().

Referenced by ATExecSetRelOptions(), DefineView(), relation_is_updatable(), and rewriteTargetView().

2253 {
2254  RangeTblRef *rtr;
2255  RangeTblEntry *base_rte;
2256 
2257  /*----------
2258  * Check if the view is simply updatable. According to SQL-92 this means:
2259  * - No DISTINCT clause.
2260  * - Each TLE is a column reference, and each column appears at most once.
2261  * - FROM contains exactly one base relation.
2262  * - No GROUP BY or HAVING clauses.
2263  * - No set operations (UNION, INTERSECT or EXCEPT).
2264  * - No sub-queries in the WHERE clause that reference the target table.
2265  *
2266  * We ignore that last restriction since it would be complex to enforce
2267  * and there isn't any actual benefit to disallowing sub-queries. (The
2268  * semantic issues that the standard is presumably concerned about don't
2269  * arise in Postgres, since any such sub-query will not see any updates
2270  * executed by the outer query anyway, thanks to MVCC snapshotting.)
2271  *
2272  * We also relax the second restriction by supporting part of SQL:1999
2273  * feature T111, which allows for a mix of updatable and non-updatable
2274  * columns, provided that an INSERT or UPDATE doesn't attempt to assign to
2275  * a non-updatable column.
2276  *
2277  * In addition we impose these constraints, involving features that are
2278  * not part of SQL-92:
2279  * - No CTEs (WITH clauses).
2280  * - No OFFSET or LIMIT clauses (this matches a SQL:2008 restriction).
2281  * - No system columns (including whole-row references) in the tlist.
2282  * - No window functions in the tlist.
2283  * - No set-returning functions in the tlist.
2284  *
2285  * Note that we do these checks without recursively expanding the view.
2286  * If the base relation is a view, we'll recursively deal with it later.
2287  *----------
2288  */
2289  if (viewquery->distinctClause != NIL)
2290  return gettext_noop("Views containing DISTINCT are not automatically updatable.");
2291 
2292  if (viewquery->groupClause != NIL || viewquery->groupingSets)
2293  return gettext_noop("Views containing GROUP BY are not automatically updatable.");
2294 
2295  if (viewquery->havingQual != NULL)
2296  return gettext_noop("Views containing HAVING are not automatically updatable.");
2297 
2298  if (viewquery->setOperations != NULL)
2299  return gettext_noop("Views containing UNION, INTERSECT, or EXCEPT are not automatically updatable.");
2300 
2301  if (viewquery->cteList != NIL)
2302  return gettext_noop("Views containing WITH are not automatically updatable.");
2303 
2304  if (viewquery->limitOffset != NULL || viewquery->limitCount != NULL)
2305  return gettext_noop("Views containing LIMIT or OFFSET are not automatically updatable.");
2306 
2307  /*
2308  * We must not allow window functions or set returning functions in the
2309  * targetlist. Otherwise we might end up inserting them into the quals of
2310  * the main query. We must also check for aggregates in the targetlist in
2311  * case they appear without a GROUP BY.
2312  *
2313  * These restrictions ensure that each row of the view corresponds to a
2314  * unique row in the underlying base relation.
2315  */
2316  if (viewquery->hasAggs)
2317  return gettext_noop("Views that return aggregate functions are not automatically updatable.");
2318 
2319  if (viewquery->hasWindowFuncs)
2320  return gettext_noop("Views that return window functions are not automatically updatable.");
2321 
2322  if (viewquery->hasTargetSRFs)
2323  return gettext_noop("Views that return set-returning functions are not automatically updatable.");
2324 
2325  /*
2326  * The view query should select from a single base relation, which must be
2327  * a table or another view.
2328  */
2329  if (list_length(viewquery->jointree->fromlist) != 1)
2330  return gettext_noop("Views that do not select from a single table or view are not automatically updatable.");
2331 
2332  rtr = (RangeTblRef *) linitial(viewquery->jointree->fromlist);
2333  if (!IsA(rtr, RangeTblRef))
2334  return gettext_noop("Views that do not select from a single table or view are not automatically updatable.");
2335 
2336  base_rte = rt_fetch(rtr->rtindex, viewquery->rtable);
2337  if (base_rte->rtekind != RTE_RELATION ||
2338  (base_rte->relkind != RELKIND_RELATION &&
2339  base_rte->relkind != RELKIND_FOREIGN_TABLE &&
2340  base_rte->relkind != RELKIND_VIEW &&
2341  base_rte->relkind != RELKIND_PARTITIONED_TABLE))
2342  return gettext_noop("Views that do not select from a single table or view are not automatically updatable.");
2343 
2344  if (base_rte->tablesample)
2345  return gettext_noop("Views containing TABLESAMPLE are not automatically updatable.");
2346 
2347  /*
2348  * Check that the view has at least one updatable column. This is required
2349  * for INSERT/UPDATE but not for DELETE.
2350  */
2351  if (check_cols)
2352  {
2353  ListCell *cell;
2354  bool found;
2355 
2356  found = false;
2357  foreach(cell, viewquery->targetList)
2358  {
2359  TargetEntry *tle = (TargetEntry *) lfirst(cell);
2360 
2361  if (view_col_is_auto_updatable(rtr, tle) == NULL)
2362  {
2363  found = true;
2364  break;
2365  }
2366  }
2367 
2368  if (!found)
2369  return gettext_noop("Views that have no updatable columns are not automatically updatable.");
2370  }
2371 
2372  return NULL; /* the view is updatable */
2373 }
Node * limitOffset
Definition: parsenodes.h:158
#define NIL
Definition: pg_list.h:69
#define IsA(nodeptr, _type_)
Definition: nodes.h:563
FromExpr * jointree
Definition: parsenodes.h:136
bool hasAggs
Definition: parsenodes.h:123
List * groupingSets
Definition: parsenodes.h:148
#define gettext_noop(x)
Definition: c.h:1005
List * fromlist
Definition: primnodes.h:1478
List * targetList
Definition: parsenodes.h:138
#define linitial(l)
Definition: pg_list.h:111
List * rtable
Definition: parsenodes.h:135
List * distinctClause
Definition: parsenodes.h:154
Node * limitCount
Definition: parsenodes.h:159
#define RELKIND_FOREIGN_TABLE
Definition: pg_class.h:167
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
#define RELKIND_PARTITIONED_TABLE
Definition: pg_class.h:168
bool hasTargetSRFs
Definition: parsenodes.h:125
#define lfirst(lc)
Definition: pg_list.h:106
bool hasWindowFuncs
Definition: parsenodes.h:124
static int list_length(const List *l)
Definition: pg_list.h:89
RTEKind rtekind
Definition: parsenodes.h:951
List * cteList
Definition: parsenodes.h:133
Node * setOperations
Definition: parsenodes.h:163
List * groupClause
Definition: parsenodes.h:146
#define RELKIND_VIEW
Definition: pg_class.h:164
static const char * view_col_is_auto_updatable(RangeTblRef *rtr, TargetEntry *tle)
Node * havingQual
Definition: parsenodes.h:150
#define RELKIND_RELATION
Definition: pg_class.h:160
struct TableSampleClause * tablesample
Definition: parsenodes.h:969