PostgreSQL Source Code  git master
parse_merge.h File Reference
Include dependency graph for parse_merge.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

QuerytransformMergeStmt (ParseState *pstate, MergeStmt *stmt)
 

Function Documentation

◆ transformMergeStmt()

Query* transformMergeStmt ( ParseState pstate,
MergeStmt stmt 
)

Definition at line 93 of file parse_merge.c.

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