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 96 of file parse_merge.c.

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

References ACL_DELETE, ACL_INSERT, ACL_NO_RIGHTS, 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_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, RuleLock::numLocks, 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, RelationData::rd_rules, RelationGetRelationName, Query::returningList, Query::rtable, setNamespaceForMergeWhen(), setTargetTable(), stmt, Query::targetList, MergeWhenClause::targetList, transformExpr(), transformExpressionList(), transformFromClause(), transformInsertRow(), transformUpdateTargetList(), transformWhereClause(), transformWithClause(), and MergeWhenClause::values.

Referenced by transformStmt().