PostgreSQL Source Code  git master
parse_merge.c File Reference
#include "postgres.h"
#include "access/sysattr.h"
#include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "parser/analyze.h"
#include "parser/parse_collate.h"
#include "parser/parsetree.h"
#include "parser/parser.h"
#include "parser/parse_clause.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 "utils/rel.h"
#include "utils/relcache.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 55 of file parse_merge.c.

57 {
58  RangeTblEntry *targetRelRTE,
59  *sourceRelRTE;
60 
61  targetRelRTE = rt_fetch(targetRTI, pstate->p_rtable);
62  sourceRelRTE = rt_fetch(sourceRTI, pstate->p_rtable);
63 
64  if (mergeWhenClause->matched)
65  {
66  Assert(mergeWhenClause->commandType == CMD_UPDATE ||
67  mergeWhenClause->commandType == CMD_DELETE ||
68  mergeWhenClause->commandType == CMD_NOTHING);
69 
70  /* MATCHED actions can see both target and source relations. */
72  targetRelRTE, true, true);
74  sourceRelRTE, true, true);
75  }
76  else
77  {
78  /*
79  * NOT MATCHED actions can't see target relation, but they can see
80  * source relation.
81  */
82  Assert(mergeWhenClause->commandType == CMD_INSERT ||
83  mergeWhenClause->commandType == CMD_NOTHING);
85  targetRelRTE, false, false);
87  sourceRelRTE, true, true);
88  }
89 }
Assert(fmt[strlen(fmt) - 1] !='\n')
@ 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:405
#define rt_fetch(rangetable_index, rangetable)
Definition: parsetree.h:31
CmdType commandType
Definition: parsenodes.h:1599
List * p_namespace
Definition: parse_node.h:192
List * p_rtable
Definition: parse_node.h:186

References Assert(), CMD_DELETE, CMD_INSERT, CMD_NOTHING, CMD_UPDATE, MergeWhenClause::commandType, MergeWhenClause::matched, 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 405 of file parse_merge.c.

408 {
409  ListCell *lc;
410 
411  foreach(lc, namespace)
412  {
413  ParseNamespaceItem *nsitem = (ParseNamespaceItem *) lfirst(lc);
414 
415  if (nsitem->p_rte == rte)
416  {
417  nsitem->p_rel_visible = rel_visible;
418  nsitem->p_cols_visible = cols_visible;
419  break;
420  }
421  }
422 }
#define lfirst(lc)
Definition: pg_list.h:170
RangeTblEntry * p_rte
Definition: parse_node.h:278

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 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 (mergeWhenClause->condition == NULL)
159  is_terminal[when_type] = true;
160  else if (is_terminal[when_type])
161  ereport(ERROR,
162  (errcode(ERRCODE_SYNTAX_ERROR),
163  errmsg("unreachable WHEN clause specified after unconditional WHEN clause")));
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
169  * do 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:739
unsigned int Index
Definition: c.h:550
int errdetail(const char *fmt,...)
Definition: elog.c:1079
int errcode(int sqlerrcode)
Definition: elog.c:735
int errmsg(const char *fmt,...)
Definition: elog.c:946
#define ERROR
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:145
List * lappend(List *list, void *datum)
Definition: list.c:338
TargetEntry * makeTargetEntry(Expr *expr, AttrNumber resno, char *resname, bool resjunk)
Definition: makefuncs.c:238
FromExpr * makeFromExpr(List *fromlist, Node *quals)
Definition: makefuncs.c:285
@ CMD_MERGE
Definition: nodes.h:269
#define makeNode(_type_)
Definition: nodes.h:165
Node * transformWhereClause(ParseState *pstate, Node *clause, ParseExprKind exprKind, const char *constructName)
void transformFromClause(ParseState *pstate, List *frmList)
Definition: parse_clause.c:113
int setTargetTable(ParseState *pstate, RangeVar *relation, bool inh, bool alsoSource, AclMode requiredPerms)
Definition: parse_clause.c:179
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:92
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:86
uint64 AclMode
Definition: parsenodes.h:81
#define ACL_INSERT
Definition: parsenodes.h:83
#define ACL_NO_RIGHTS
Definition: parsenodes.h:100
#define ACL_UPDATE
Definition: parsenodes.h:85
List * transformUpdateTargetList(ParseState *pstate, List *origTlist)
Definition: analyze.c:2432
List * transformInsertRow(ParseState *pstate, List *exprlist, List *stmtcols, List *icolumns, List *attrnos, bool strip_indirection)
Definition: analyze.c:968
int errdetail_relkind_not_supported(char relkind)
Definition: pg_class.c:24
#define lfirst_node(type, lc)
Definition: pg_list.h:174
static int list_length(const List *l)
Definition: pg_list.h:150
#define NIL
Definition: pg_list.h:66
#define lfirst_int(lc)
Definition: pg_list.h:171
#define list_make1(x1)
Definition: pg_list.h:210
#define forthree(cell1, list1, cell2, list2, cell3, list3)
Definition: pg_list.h:510
#define RelationGetRelationName(relation)
Definition: rel.h:535
char * aliasname
Definition: primnodes.h:42
Definition: pg_list.h:52
Node * sourceRelation
Definition: parsenodes.h:1723
List * mergeWhenClauses
Definition: parsenodes.h:1725
RangeVar * relation
Definition: parsenodes.h:1722
Node * joinCondition
Definition: parsenodes.h:1724
WithClause * withClause
Definition: parsenodes.h:1726
OverridingKind override
Definition: parsenodes.h:1600
Definition: nodes.h:118
RTEPermissionInfo * p_perminfo
Definition: parse_node.h:280
List * p_ctenamespace
Definition: parse_node.h:195
ParseNamespaceItem * p_target_nsitem
Definition: parse_node.h:199
bool p_is_insert
Definition: parse_node.h:200
bool p_hasModifyingCTE
Definition: parse_node.h:218
List * p_rteperminfos
Definition: parse_node.h:187
Relation p_target_relation
Definition: parse_node.h:198
bool p_hasSubLinks
Definition: parse_node.h:217
List * p_joinlist
Definition: parse_node.h:190
FromExpr * jointree
Definition: parsenodes.h:158
List * returningList
Definition: parsenodes.h:170
List * rteperminfos
Definition: parsenodes.h:156
List * cteList
Definition: parsenodes.h:153
bool hasTargetSRFs
Definition: parsenodes.h:143
List * rtable
Definition: parsenodes.h:155
int resultRelation
Definition: parsenodes.h:138
CmdType commandType
Definition: parsenodes.h:124
bool hasRecursive
Definition: parsenodes.h:146
bool hasModifyingCTE
Definition: parsenodes.h:147
List * mergeActionList
Definition: parsenodes.h:161
List * targetList
Definition: parsenodes.h:164
bool mergeUseOuterJoin
Definition: parsenodes.h:162
bool hasSubLinks
Definition: parsenodes.h:144
Bitmapset * insertedCols
Definition: parsenodes.h:1211
bool inh
Definition: primnodes.h:80
RuleLock * rd_rules
Definition: rel.h:114
Form_pg_class rd_rel
Definition: rel.h:110
char * name
Definition: parsenodes.h:484
int numLocks
Definition: prs2lock.h:42
bool recursive
Definition: parsenodes.h:1489
#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(), Query::hasModifyingCTE, Query::hasRecursive, Query::hasSubLinks, Query::hasTargetSRFs, RangeVar::inh, RTEPermissionInfo::insertedCols, MergeStmt::joinCondition, Query::jointree, lappend(), lfirst, lfirst_int, lfirst_node, list_length(), list_make1, makeFromExpr(), makeNode, makeTargetEntry(), MergeWhenClause::matched, Query::mergeActionList, Query::mergeUseOuterJoin, MergeStmt::mergeWhenClauses, 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, WithClause::recursive, MergeStmt::relation, RelationGetRelationName, Query::resultRelation, Query::returningList, Query::rtable, Query::rteperminfos, setNamespaceForMergeWhen(), setTargetTable(), MergeStmt::sourceRelation, Query::targetList, MergeWhenClause::targetList, transformExpr(), transformExpressionList(), transformFromClause(), transformInsertRow(), transformUpdateTargetList(), transformWhereClause(), transformWithClause(), MergeWhenClause::values, and MergeStmt::withClause.

Referenced by transformStmt().