PostgreSQL Source Code  git master
All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
parse_merge.c File Reference
#include "postgres.h"
#include "access/sysattr.h"
#include "nodes/makefuncs.h"
#include "parser/analyze.h"
#include "parser/parse_clause.h"
#include "parser/parse_collate.h"
#include "parser/parse_cte.h"
#include "parser/parse_expr.h"
#include "parser/parse_merge.h"
#include "parser/parse_relation.h"
#include "parser/parse_target.h"
#include "parser/parsetree.h"
#include "utils/rel.h"
Include dependency graph for parse_merge.c:

Go to the source code of this file.

Functions

static void setNamespaceForMergeWhen (ParseState *pstate, MergeWhenClause *mergeWhenClause, Index targetRTI, Index sourceRTI)
 
static void setNamespaceVisibilityForRTE (List *namespace, RangeTblEntry *rte, bool rel_visible, bool cols_visible)
 
QuerytransformMergeStmt (ParseState *pstate, MergeStmt *stmt)
 

Function Documentation

◆ setNamespaceForMergeWhen()

static void setNamespaceForMergeWhen ( ParseState pstate,
MergeWhenClause mergeWhenClause,
Index  targetRTI,
Index  sourceRTI 
)
static

Definition at line 52 of file parse_merge.c.

54 {
55  RangeTblEntry *targetRelRTE,
56  *sourceRelRTE;
57 
58  targetRelRTE = rt_fetch(targetRTI, pstate->p_rtable);
59  sourceRelRTE = rt_fetch(sourceRTI, pstate->p_rtable);
60 
61  if (mergeWhenClause->matchKind == MERGE_WHEN_MATCHED)
62  {
63  Assert(mergeWhenClause->commandType == CMD_UPDATE ||
64  mergeWhenClause->commandType == CMD_DELETE ||
65  mergeWhenClause->commandType == CMD_NOTHING);
66 
67  /* MATCHED actions can see both target and source relations. */
69  targetRelRTE, true, true);
71  sourceRelRTE, true, true);
72  }
73  else if (mergeWhenClause->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
74  {
75  /*
76  * NOT MATCHED BY SOURCE actions can see the target relation, but they
77  * can't see the source relation.
78  */
79  Assert(mergeWhenClause->commandType == CMD_UPDATE ||
80  mergeWhenClause->commandType == CMD_DELETE ||
81  mergeWhenClause->commandType == CMD_NOTHING);
83  targetRelRTE, true, true);
85  sourceRelRTE, false, false);
86  }
87  else /* MERGE_WHEN_NOT_MATCHED_BY_TARGET */
88  {
89  /*
90  * NOT MATCHED [BY TARGET] actions can't see target relation, but they
91  * can see source relation.
92  */
93  Assert(mergeWhenClause->commandType == CMD_INSERT ||
94  mergeWhenClause->commandType == CMD_NOTHING);
96  targetRelRTE, false, false);
98  sourceRelRTE, true, true);
99  }
100 }
#define Assert(condition)
Definition: c.h:863
@ CMD_INSERT
Definition: nodes.h:267
@ CMD_DELETE
Definition: nodes.h:268
@ CMD_UPDATE
Definition: nodes.h:266
@ CMD_NOTHING
Definition: nodes.h:272
static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte, bool rel_visible, bool cols_visible)
Definition: parse_merge.c:415
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
@ MERGE_WHEN_NOT_MATCHED_BY_SOURCE
Definition: primnodes.h:1997
@ MERGE_WHEN_MATCHED
Definition: primnodes.h:1996
CmdType commandType
Definition: parsenodes.h:1721
MergeMatchKind matchKind
Definition: parsenodes.h:1720
List * p_namespace
Definition: parse_node.h:219
List * p_rtable
Definition: parse_node.h:212

References Assert, CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_UPDATE, MergeWhenClause::commandType, MergeWhenClause::matchKind, MERGE_WHEN_MATCHED, MERGE_WHEN_NOT_MATCHED_BY_SOURCE, ParseState::p_namespace, ParseState::p_rtable, rt_fetch, and setNamespaceVisibilityForRTE().

Referenced by transformMergeStmt().

◆ setNamespaceVisibilityForRTE()

static void setNamespaceVisibilityForRTE ( List namespace,
RangeTblEntry rte,
bool  rel_visible,
bool  cols_visible 
)
static

Definition at line 415 of file parse_merge.c.

418 {
419  ListCell *lc;
420 
421  foreach(lc, namespace)
422  {
423  ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
424 
425  if (nsitem->p_rte == rte)
426  {
427  nsitem->p_rel_visible = rel_visible;
428  nsitem->p_cols_visible = cols_visible;
429  break;
430  }
431  }
432 }
#define lfirst(lc)
Definition: pg_list.h:172
RangeTblEntry * p_rte
Definition: parse_node.h:306

References lfirst, ParseNamespaceItem::p_cols_visible, ParseNamespaceItem::p_rel_visible, and ParseNamespaceItem::p_rte.

Referenced by setNamespaceForMergeWhen().

◆ transformMergeStmt()

Query* transformMergeStmt ( ParseState pstate,
MergeStmt stmt 
)

Definition at line 107 of file parse_merge.c.

108 {
109  Query *qry = makeNode(Query);
110  ListCell *l;
111  AclMode targetPerms = ACL_NO_RIGHTS;
112  bool is_terminal[NUM_MERGE_MATCH_KINDS];
113  Index sourceRTI;
114  List *mergeActionList;
115  ParseNamespaceItem *nsitem;
116 
117  /* There can't be any outer WITH to worry about */
118  Assert(pstate->p_ctenamespace == NIL);
119 
120  qry->commandType = CMD_MERGE;
121  qry->hasRecursive = false;
122 
123  /* process the WITH clause independently of all else */
124  if (stmt->withClause)
125  {
126  if (stmt->withClause->recursive)
127  ereport(ERROR,
128  (errcode(ERRCODE_SYNTAX_ERROR),
129  errmsg("WITH RECURSIVE is not supported for MERGE statement")));
130 
131  qry->cteList = transformWithClause(pstate, stmt->withClause);
132  qry->hasModifyingCTE = pstate->p_hasModifyingCTE;
133  }
134 
135  /*
136  * Check WHEN clauses for permissions and sanity
137  */
138  is_terminal[MERGE_WHEN_MATCHED] = false;
139  is_terminal[MERGE_WHEN_NOT_MATCHED_BY_SOURCE] = false;
140  is_terminal[MERGE_WHEN_NOT_MATCHED_BY_TARGET] = false;
141  foreach(l, stmt->mergeWhenClauses)
142  {
143  MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l);
144 
145  /*
146  * Collect permissions to check, according to action types. We require
147  * SELECT privileges for DO NOTHING because it'd be irregular to have
148  * a target relation with zero privileges checked, in case DO NOTHING
149  * is the only action. There's no damage from that: any meaningful
150  * MERGE command requires at least some access to the table anyway.
151  */
152  switch (mergeWhenClause->commandType)
153  {
154  case CMD_INSERT:
155  targetPerms |= ACL_INSERT;
156  break;
157  case CMD_UPDATE:
158  targetPerms |= ACL_UPDATE;
159  break;
160  case CMD_DELETE:
161  targetPerms |= ACL_DELETE;
162  break;
163  case CMD_NOTHING:
164  targetPerms |= ACL_SELECT;
165  break;
166  default:
167  elog(ERROR, "unknown action in MERGE WHEN clause");
168  }
169 
170  /*
171  * Check for unreachable WHEN clauses
172  */
173  if (is_terminal[mergeWhenClause->matchKind])
174  ereport(ERROR,
175  (errcode(ERRCODE_SYNTAX_ERROR),
176  errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
177  if (mergeWhenClause->condition == NULL)
178  is_terminal[mergeWhenClause->matchKind] = true;
179  }
180 
181  /*
182  * Set up the MERGE target table. The target table is added to the
183  * namespace below and to joinlist in transform_MERGE_to_join, so don't do
184  * it here.
185  *
186  * Initially mergeTargetRelation is the same as resultRelation, so data is
187  * read from the table being updated. However, that might be changed by
188  * the rewriter, if the target is a trigger-updatable view, to allow
189  * target data to be read from the expanded view query while updating the
190  * original view relation.
191  */
192  qry->resultRelation = setTargetTable(pstate, stmt->relation,
193  stmt->relation->inh,
194  false, targetPerms);
195  qry->mergeTargetRelation = qry->resultRelation;
196 
197  /* The target relation must be a table or a view */
198  if (pstate->p_target_relation->rd_rel->relkind != RELKIND_RELATION &&
199  pstate->p_target_relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE &&
200  pstate->p_target_relation->rd_rel->relkind != RELKIND_VIEW)
201  ereport(ERROR,
202  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
203  errmsg("cannot execute MERGE on relation \"%s\"",
206 
207  /* Now transform the source relation to produce the source RTE. */
208  transformFromClause(pstate,
209  list_make1(stmt->sourceRelation));
210  sourceRTI = list_length(pstate->p_rtable);
211  nsitem = GetNSItemByRangeTablePosn(pstate, sourceRTI, 0);
212 
213  /*
214  * Check that the target table doesn't conflict with the source table.
215  * This would typically be a checkNameSpaceConflicts call, but we want a
216  * more specific error message.
217  */
218  if (strcmp(pstate->p_target_nsitem->p_names->aliasname,
219  nsitem->p_names->aliasname) == 0)
220  ereport(ERROR,
221  errcode(ERRCODE_DUPLICATE_ALIAS),
222  errmsg("name \"%s\" specified more than once",
223  pstate->p_target_nsitem->p_names->aliasname),
224  errdetail("The name is used both as MERGE target table and data source."));
225 
226  /*
227  * There's no need for a targetlist here; it'll be set up by
228  * preprocess_targetlist later.
229  */
230  qry->targetList = NIL;
231  qry->rtable = pstate->p_rtable;
232  qry->rteperminfos = pstate->p_rteperminfos;
233 
234  /*
235  * Transform the join condition. This includes references to the target
236  * side, so add that to the namespace.
237  */
238  addNSItemToQuery(pstate, pstate->p_target_nsitem, false, true, true);
239  qry->mergeJoinCondition = transformExpr(pstate, stmt->joinCondition,
241 
242  /*
243  * Create the temporary query's jointree using the joinlist we built using
244  * just the source relation; the target relation is not included. The join
245  * will be constructed fully by transform_MERGE_to_join.
246  */
247  qry->jointree = makeFromExpr(pstate->p_joinlist, NULL);
248 
249  /* Transform the RETURNING list, if any */
250  qry->returningList = transformReturningList(pstate, stmt->returningList,
252 
253  /*
254  * We now have a good query shape, so now look at the WHEN conditions and
255  * action targetlists.
256  *
257  * Overall, the MERGE Query's targetlist is NIL.
258  *
259  * Each individual action has its own targetlist that needs separate
260  * transformation. These transforms don't do anything to the overall
261  * targetlist, since that is only used for resjunk columns.
262  *
263  * We can reference any column in Target or Source, which is OK because
264  * both of those already have RTEs. There is nothing like the EXCLUDED
265  * pseudo-relation for INSERT ON CONFLICT.
266  */
267  mergeActionList = NIL;
268  foreach(l, stmt->mergeWhenClauses)
269  {
270  MergeWhenClause *mergeWhenClause = lfirst_node(MergeWhenClause, l);
272 
274  action->commandType = mergeWhenClause->commandType;
275  action->matchKind = mergeWhenClause->matchKind;
276 
277  /*
278  * Set namespace for the specific action. This must be done before
279  * analyzing the WHEN quals and the action targetlist.
280  */
281  setNamespaceForMergeWhen(pstate, mergeWhenClause,
282  qry->resultRelation,
283  sourceRTI);
284 
285  /*
286  * Transform the WHEN condition.
287  *
288  * Note that these quals are NOT added to the join quals; instead they
289  * are evaluated separately during execution to decide which of the
290  * WHEN MATCHED or WHEN NOT MATCHED actions to execute.
291  */
292  action->qual = transformWhereClause(pstate, mergeWhenClause->condition,
293  EXPR_KIND_MERGE_WHEN, "WHEN");
294 
295  /*
296  * Transform target lists for each INSERT and UPDATE action stmt
297  */
298  switch (action->commandType)
299  {
300  case CMD_INSERT:
301  {
302  List *exprList = NIL;
303  ListCell *lc;
304  RTEPermissionInfo *perminfo;
305  ListCell *icols;
306  ListCell *attnos;
307  List *icolumns;
308  List *attrnos;
309 
310  pstate->p_is_insert = true;
311 
312  icolumns = checkInsertTargets(pstate,
313  mergeWhenClause->targetList,
314  &attrnos);
315  Assert(list_length(icolumns) == list_length(attrnos));
316 
317  action->override = mergeWhenClause->override;
318 
319  /*
320  * Handle INSERT much like in transformInsertStmt
321  */
322  if (mergeWhenClause->values == NIL)
323  {
324  /*
325  * We have INSERT ... DEFAULT VALUES. We can handle
326  * this case by emitting an empty targetlist --- all
327  * columns will be defaulted when the planner expands
328  * the targetlist.
329  */
330  exprList = NIL;
331  }
332  else
333  {
334  /*
335  * Process INSERT ... VALUES with a single VALUES
336  * sublist. We treat this case separately for
337  * efficiency. The sublist is just computed directly
338  * as the Query's targetlist, with no VALUES RTE. So
339  * it works just like a SELECT without any FROM.
340  */
341 
342  /*
343  * Do basic expression transformation (same as a ROW()
344  * expr, but allow SetToDefault at top level)
345  */
346  exprList = transformExpressionList(pstate,
347  mergeWhenClause->values,
349  true);
350 
351  /* Prepare row for assignment to target table */
352  exprList = transformInsertRow(pstate, exprList,
353  mergeWhenClause->targetList,
354  icolumns, attrnos,
355  false);
356  }
357 
358  /*
359  * Generate action's target list using the computed list
360  * of expressions. Also, mark all the target columns as
361  * needing insert permissions.
362  */
363  perminfo = pstate->p_target_nsitem->p_perminfo;
364  forthree(lc, exprList, icols, icolumns, attnos, attrnos)
365  {
366  Expr *expr = (Expr *) lfirst(lc);
367  ResTarget *col = lfirst_node(ResTarget, icols);
368  AttrNumber attr_num = (AttrNumber) lfirst_int(attnos);
369  TargetEntry *tle;
370 
371  tle = makeTargetEntry(expr,
372  attr_num,
373  col->name,
374  false);
375  action->targetList = lappend(action->targetList, tle);
376 
377  perminfo->insertedCols =
378  bms_add_member(perminfo->insertedCols,
380  }
381  }
382  break;
383  case CMD_UPDATE:
384  {
385  pstate->p_is_insert = false;
386  action->targetList =
388  mergeWhenClause->targetList);
389  }
390  break;
391  case CMD_DELETE:
392  break;
393 
394  case CMD_NOTHING:
395  action->targetList = NIL;
396  break;
397  default:
398  elog(ERROR, "unknown action in MERGE WHEN clause");
399  }
400 
401  mergeActionList = lappend(mergeActionList, action);
402  }
403 
404  qry->mergeActionList = mergeActionList;
405 
406  qry->hasTargetSRFs = false;
407  qry->hasSubLinks = pstate->p_hasSubLinks;
408 
409  assign_query_collations(pstate, qry);
410 
411  return qry;
412 }
int16 AttrNumber
Definition: attnum.h:21
Bitmapset * bms_add_member(Bitmapset *a, int x)
Definition: bitmapset.c:815
unsigned int Index
Definition: c.h:619
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:225
#define ereport(elevel,...)
Definition: elog.h:149
#define stmt
Definition: indent_codes.h:59
List * lappend(List *list, void *datum)
Definition: list.c:339
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:240
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:287
@ CMD_MERGE
Definition: nodes.h:269
#define makeNode(_type_)
Definition: nodes.h:155
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
void transformFromClause(ParseState *pstate, List *frmList)
Definition: parse_clause.c:112
int setTargetTable(ParseState *pstate, RangeVar *relation, bool inh, bool alsoSource, AclMode requiredPerms)
Definition: parse_clause.c:178
void assign_query_collations(ParseState *pstate, Query *query)
List * transformWithClause(ParseState *pstate, WithClause *withClause)
Definition: parse_cte.c:109
Node * transformExpr(ParseState *pstate, Node *expr, ParseExprKind exprKind)
Definition: parse_expr.c:118
static void setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause, Index targetRTI, Index sourceRTI)
Definition: parse_merge.c:52
@ EXPR_KIND_MERGE_WHEN
Definition: parse_node.h:58
@ EXPR_KIND_MERGE_RETURNING
Definition: parse_node.h:65
@ EXPR_KIND_JOIN_ON
Definition: parse_node.h:42
@ EXPR_KIND_VALUES_SINGLE
Definition: parse_node.h:67
void addNSItemToQuery(ParseState *pstate, ParseNamespaceItem *nsitem, bool addToJoinList, bool addToRelNameSpace, bool addToVarNameSpace)
ParseNamespaceItem * GetNSItemByRangeTablePosn(ParseState *pstate, int varno, int sublevels_up)
List * checkInsertTargets(ParseState *pstate, List *cols, List **attrnos)
List * transformExpressionList(ParseState *pstate, List *exprlist, ParseExprKind exprKind, bool allowDefault)
Definition: parse_target.c:220
#define ACL_DELETE
Definition: parsenodes.h:79
uint64 AclMode
Definition: parsenodes.h:74
#define ACL_INSERT
Definition: parsenodes.h:76
#define ACL_NO_RIGHTS
Definition: parsenodes.h:92
#define ACL_UPDATE
Definition: parsenodes.h:78
#define ACL_SELECT
Definition: parsenodes.h:77
List * transformUpdateTargetList(ParseState *pstate, List *origTlist)
Definition: analyze.c:2577
List * transformInsertRow(ParseState *pstate, List *exprlist, List *stmtcols, List *icolumns, List *attrnos, bool strip_indirection)
Definition: analyze.c:1099
List * transformReturningList(ParseState *pstate, List *returningList, ParseExprKind exprKind)
Definition: analyze.c:2652
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
#define lfirst_node(type, lc)
Definition: pg_list.h:176
static int list_length(const List *l)
Definition: pg_list.h:152
#define NIL
Definition: pg_list.h:68
#define lfirst_int(lc)
Definition: pg_list.h:173
#define list_make1(x1)
Definition: pg_list.h:212
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition: pg_list.h:563
#define NUM_MERGE_MATCH_KINDS
Definition: primnodes.h:2001
@ MERGE_WHEN_NOT_MATCHED_BY_TARGET
Definition: primnodes.h:1998
#define RelationGetRelationName(relation)
Definition: rel.h:539
char * aliasname
Definition: primnodes.h:50
Definition: pg_list.h:54
OverridingKind override
Definition: parsenodes.h:1722
RTEPermissionInfo * p_perminfo
Definition: parse_node.h:308
List * p_ctenamespace
Definition: parse_node.h:222
ParseNamespaceItem * p_target_nsitem
Definition: parse_node.h:226
bool p_is_insert
Definition: parse_node.h:228
bool p_hasModifyingCTE
Definition: parse_node.h:246
List * p_rteperminfos
Definition: parse_node.h:213
Relation p_target_relation
Definition: parse_node.h:225
bool p_hasSubLinks
Definition: parse_node.h:245
List * p_joinlist
Definition: parse_node.h:217
Node * mergeJoinCondition
Definition: parsenodes.h:191
FromExpr * jointree
Definition: parsenodes.h:177
List * returningList
Definition: parsenodes.h:200
List * cteList
Definition: parsenodes.h:168
List * rtable
Definition: parsenodes.h:170
CmdType commandType
Definition: parsenodes.h:121
List * mergeActionList
Definition: parsenodes.h:180
List * targetList
Definition: parsenodes.h:193
Bitmapset * insertedCols
Definition: parsenodes.h:1294
Form_pg_class rd_rel
Definition: rel.h:111
char * name
Definition: parsenodes.h:519
#define FirstLowInvalidHeapAttributeNumber
Definition: sysattr.h:27

References ACL_DELETE, ACL_INSERT, ACL_NO_RIGHTS, ACL_SELECT, ACL_UPDATE, generate_unaccent_rules::action, addNSItemToQuery(), Alias::aliasname, Assert, assign_query_collations(), bms_add_member(), checkInsertTargets(), CMD_DELETE, CMD_INSERT, CMD_MERGE, CMD_NOTHING, CMD_UPDATE, Query::commandType, MergeWhenClause::commandType, MergeWhenClause::condition, Query::cteList, elog, ereport, errcode(), errdetail(), errdetail_relkind_not_supported(), errmsg(), ERROR, EXPR_KIND_JOIN_ON, EXPR_KIND_MERGE_RETURNING, EXPR_KIND_MERGE_WHEN, EXPR_KIND_VALUES_SINGLE, FirstLowInvalidHeapAttributeNumber, forthree, GetNSItemByRangeTablePosn(), RTEPermissionInfo::insertedCols, Query::jointree, lappend(), lfirst, lfirst_int, lfirst_node, list_length(), list_make1, makeFromExpr(), makeNode, makeTargetEntry(), MergeWhenClause::matchKind, MERGE_WHEN_MATCHED, MERGE_WHEN_NOT_MATCHED_BY_SOURCE, MERGE_WHEN_NOT_MATCHED_BY_TARGET, Query::mergeActionList, Query::mergeJoinCondition, ResTarget::name, NIL, NUM_MERGE_MATCH_KINDS, MergeWhenClause::override, ParseState::p_ctenamespace, ParseState::p_hasModifyingCTE, ParseState::p_hasSubLinks, ParseState::p_is_insert, ParseState::p_joinlist, ParseNamespaceItem::p_names, ParseNamespaceItem::p_perminfo, ParseState::p_rtable, ParseState::p_rteperminfos, ParseState::p_target_nsitem, ParseState::p_target_relation, RelationData::rd_rel, RelationGetRelationName, Query::returningList, Query::rtable, setNamespaceForMergeWhen(), setTargetTable(), stmt, Query::targetList, MergeWhenClause::targetList, transformExpr(), transformExpressionList(), transformFromClause(), transformInsertRow(), transformReturningList(), transformUpdateTargetList(), transformWhereClause(), transformWithClause(), and MergeWhenClause::values.

Referenced by transformStmt().