return newnode;
}
+static MergeAction *
+_copyMergeAction(const MergeAction *from)
+{
+ MergeAction *newnode = makeNode(MergeAction);
+
+ COPY_SCALAR_FIELD(matched);
+ COPY_SCALAR_FIELD(commandType);
+ COPY_SCALAR_FIELD(override);
+ COPY_NODE_FIELD(qual);
+ COPY_NODE_FIELD(targetList);
+
+ return newnode;
+}
+
/* ****************************************************************
* relation.h copy functions
*
COPY_NODE_FIELD(relation);
COPY_NODE_FIELD(source_relation);
COPY_NODE_FIELD(join_condition);
- COPY_NODE_FIELD(mergeActionList);
+ COPY_NODE_FIELD(mergeWhenClauses);
COPY_NODE_FIELD(withClause);
return newnode;
}
-static MergeAction *
-_copyMergeAction(const MergeAction *from)
+static MergeWhenClause *
+_copyMergeWhenClause(const MergeWhenClause *from)
{
- MergeAction *newnode = makeNode(MergeAction);
+ MergeWhenClause *newnode = makeNode(MergeWhenClause);
COPY_SCALAR_FIELD(matched);
COPY_SCALAR_FIELD(commandType);
COPY_NODE_FIELD(condition);
- COPY_NODE_FIELD(qual);
- COPY_NODE_FIELD(stmt);
COPY_NODE_FIELD(targetList);
-
+ COPY_NODE_FIELD(cols);
+ COPY_NODE_FIELD(values);
+ COPY_SCALAR_FIELD(override);
return newnode;
}
case T_OnConflictExpr:
retval = _copyOnConflictExpr(from);
break;
+ case T_MergeAction:
+ retval = _copyMergeAction(from);
+ break;
/*
* RELATION NODES
case T_MergeStmt:
retval = _copyMergeStmt(from);
break;
- case T_MergeAction:
- retval = _copyMergeAction(from);
+ case T_MergeWhenClause:
+ retval = _copyMergeWhenClause(from);
break;
case T_SelectStmt:
retval = _copySelectStmt(from);
return true;
}
+
+static bool
+_equalMergeAction(const MergeAction *a, const MergeAction *b)
+{
+ COMPARE_SCALAR_FIELD(matched);
+ COMPARE_SCALAR_FIELD(commandType);
+ COMPARE_SCALAR_FIELD(override);
+ COMPARE_NODE_FIELD(qual);
+ COMPARE_NODE_FIELD(targetList);
+
+ return true;
+}
/*
* Stuff from relation.h
*/
COMPARE_NODE_FIELD(relation);
COMPARE_NODE_FIELD(source_relation);
COMPARE_NODE_FIELD(join_condition);
- COMPARE_NODE_FIELD(mergeActionList);
+ COMPARE_NODE_FIELD(mergeWhenClauses);
COMPARE_NODE_FIELD(withClause);
return true;
}
static bool
-_equalMergeAction(const MergeAction *a, const MergeAction *b)
+_equalMergeWhenClause(const MergeWhenClause *a, const MergeWhenClause *b)
{
COMPARE_SCALAR_FIELD(matched);
COMPARE_SCALAR_FIELD(commandType);
COMPARE_NODE_FIELD(condition);
- COMPARE_NODE_FIELD(qual);
- COMPARE_NODE_FIELD(stmt);
COMPARE_NODE_FIELD(targetList);
+ COMPARE_NODE_FIELD(cols);
+ COMPARE_NODE_FIELD(values);
+ COMPARE_SCALAR_FIELD(override);
return true;
}
case T_OnConflictExpr:
retval = _equalOnConflictExpr(a, b);
break;
+ case T_MergeAction:
+ retval = _equalMergeAction(a, b);
+ break;
case T_JoinExpr:
retval = _equalJoinExpr(a, b);
break;
case T_MergeStmt:
retval = _equalMergeStmt(a, b);
break;
- case T_MergeAction:
- retval = _equalMergeAction(a, b);
+ case T_MergeWhenClause:
+ retval = _equalMergeWhenClause(a, b);
break;
case T_SelectStmt:
retval = _equalSelectStmt(a, b);
return true;
if (walker(stmt->join_condition, context))
return true;
- if (walker(stmt->mergeActionList, context))
+ if (walker(stmt->mergeWhenClauses, context))
return true;
if (walker(stmt->withClause, context))
return true;
}
break;
- case T_MergeAction:
+ case T_MergeWhenClause:
{
- MergeAction *action = (MergeAction *) node;
+ MergeWhenClause *mergeWhenClause = (MergeWhenClause *) node;
- if (walker(action->targetList, context))
+ if (walker(mergeWhenClause->condition, context))
return true;
- if (walker(action->qual, context))
+ if (walker(mergeWhenClause->targetList, context))
+ return true;
+ if (walker(mergeWhenClause->cols, context))
+ return true;
+ if (walker(mergeWhenClause->values, context))
return true;
}
break;
}
static void
-_outMergeAction(StringInfo str, const MergeAction *node)
+_outMergeWhenClause(StringInfo str, const MergeWhenClause *node)
{
- WRITE_NODE_TYPE("MERGEACTION");
+ WRITE_NODE_TYPE("MERGEWHENCLAUSE");
WRITE_BOOL_FIELD(matched);
WRITE_ENUM_FIELD(commandType, CmdType);
WRITE_NODE_FIELD(condition);
- WRITE_NODE_FIELD(qual);
- WRITE_NODE_FIELD(stmt);
WRITE_NODE_FIELD(targetList);
+ WRITE_NODE_FIELD(cols);
+ WRITE_NODE_FIELD(values);
+ WRITE_ENUM_FIELD(override, OverridingKind);
}
static void
WRITE_NODE_FIELD(exclRelTlist);
}
+static void
+_outMergeAction(StringInfo str, const MergeAction *node)
+{
+ WRITE_NODE_TYPE("MERGEACTION");
+
+ WRITE_BOOL_FIELD(matched);
+ WRITE_ENUM_FIELD(commandType, CmdType);
+ WRITE_NODE_FIELD(qual);
+ WRITE_NODE_FIELD(targetList);
+}
+
/*****************************************************************************
*
* Stuff from relation.h.
case T_ModifyTable:
_outModifyTable(str, obj);
break;
- case T_MergeAction:
- _outMergeAction(str, obj);
+ case T_MergeWhenClause:
+ _outMergeWhenClause(str, obj);
break;
case T_Append:
_outAppend(str, obj);
case T_OnConflictExpr:
_outOnConflictExpr(str, obj);
break;
+ case T_MergeAction:
+ _outMergeAction(str, obj);
+ break;
case T_Path:
_outPath(str, obj);
break;
READ_DONE();
}
+/*
+ * _readMergeAction
+ */
+static MergeAction *
+_readMergeAction(void)
+{
+ READ_LOCALS(MergeAction);
+
+ READ_BOOL_FIELD(matched);
+ READ_ENUM_FIELD(commandType, CmdType);
+ READ_NODE_FIELD(qual);
+ READ_NODE_FIELD(targetList);
+
+ READ_DONE();
+}
+
/*
* Stuff from parsenodes.h.
*/
}
/*
- * _readMergeAction
+ * _readMergeWhenClause
*/
-static MergeAction *
-_readMergeAction(void)
+static MergeWhenClause *
+_readMergeWhenClause(void)
{
- READ_LOCALS(MergeAction);
+ READ_LOCALS(MergeWhenClause);
READ_BOOL_FIELD(matched);
READ_ENUM_FIELD(commandType, CmdType);
READ_NODE_FIELD(condition);
- READ_NODE_FIELD(qual);
- READ_NODE_FIELD(stmt);
READ_NODE_FIELD(targetList);
+ READ_NODE_FIELD(cols);
+ READ_NODE_FIELD(values);
+ READ_ENUM_FIELD(override, OverridingKind);
READ_DONE();
}
return_value = _readFromExpr();
else if (MATCH("ONCONFLICTEXPR", 14))
return_value = _readOnConflictExpr();
+ else if (MATCH("MERGEACTION", 11))
+ return_value = _readMergeAction();
else if (MATCH("RTE", 3))
return_value = _readRangeTblEntry();
else if (MATCH("RANGETBLFUNCTION", 16))
return_value = _readProjectSet();
else if (MATCH("MODIFYTABLE", 11))
return_value = _readModifyTable();
- else if (MATCH("MERGEACTION", 11))
- return_value = _readMergeAction();
+ else if (MATCH("MERGEWHENCLAUSE", 15))
+ return_value = _readMergeWhenClause();
else if (MATCH("APPEND", 6))
return_value = _readAppend();
else if (MATCH("MERGEAPPEND", 11))
PartitionSpec *partspec;
PartitionBoundSpec *partboundspec;
RoleSpec *rolespec;
+ MergeWhenClause *mergewhen;
}
%type <node> stmt schema_stmt
TriggerTransitions TriggerReferencing
publication_name_list
vacuum_relation_list opt_vacuum_relation_list
+ merge_values_clause
%type <list> group_by_list
%type <node> group_by_item empty_grouping_set rollup_clause cube_clause
%type <istmt> insert_rest
%type <infer> opt_conf_expr
%type <onconflict> opt_on_conflict
+%type <mergewhen> merge_insert merge_update merge_delete
%type <vsetstmt> generic_set set_rest set_rest_more generic_reset reset_rest
SetResetClause FunctionSetResetClause
%type <node> merge_when_clause opt_merge_when_and_condition
%type <list> merge_when_list
-%type <node> merge_update merge_delete merge_insert
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
m->relation = $4;
m->source_relation = $6;
m->join_condition = $8;
- m->mergeActionList = $9;
+ m->mergeWhenClauses = $9;
$$ = (Node *)m;
}
merge_when_clause:
WHEN MATCHED opt_merge_when_and_condition THEN merge_update
{
- MergeAction *m = makeNode(MergeAction);
+ $5->matched = true;
+ $5->commandType = CMD_UPDATE;
+ $5->condition = $3;
- m->matched = true;
- m->commandType = CMD_UPDATE;
- m->condition = $3;
- m->stmt = $5;
-
- $$ = (Node *)m;
+ $$ = (Node *) $5;
}
| WHEN MATCHED opt_merge_when_and_condition THEN merge_delete
{
- MergeAction *m = makeNode(MergeAction);
+ MergeWhenClause *m = makeNode(MergeWhenClause);
m->matched = true;
m->commandType = CMD_DELETE;
m->condition = $3;
- m->stmt = $5;
$$ = (Node *)m;
}
| WHEN NOT MATCHED opt_merge_when_and_condition THEN merge_insert
{
- MergeAction *m = makeNode(MergeAction);
+ $6->matched = false;
+ $6->commandType = CMD_INSERT;
+ $6->condition = $4;
- m->matched = false;
- m->commandType = CMD_INSERT;
- m->condition = $4;
- m->stmt = $6;
-
- $$ = (Node *)m;
+ $$ = (Node *) $6;
}
| WHEN NOT MATCHED opt_merge_when_and_condition THEN DO NOTHING
{
- MergeAction *m = makeNode(MergeAction);
+ MergeWhenClause *m = makeNode(MergeWhenClause);
m->matched = false;
m->commandType = CMD_NOTHING;
m->condition = $4;
- m->stmt = NULL;
$$ = (Node *)m;
}
;
merge_delete:
- DELETE_P
- {
- DeleteStmt *n = makeNode(DeleteStmt);
- $$ = (Node *)n;
- }
+ DELETE_P { $$ = NULL; }
;
merge_update:
UPDATE SET set_clause_list
{
- UpdateStmt *n = makeNode(UpdateStmt);
+ MergeWhenClause *n = makeNode(MergeWhenClause);
n->targetList = $3;
- $$ = (Node *)n;
+ $$ = n;
}
;
merge_insert:
- INSERT values_clause
+ INSERT merge_values_clause
{
- InsertStmt *n = makeNode(InsertStmt);
+ MergeWhenClause *n = makeNode(MergeWhenClause);
n->cols = NIL;
- n->selectStmt = $2;
-
- $$ = (Node *)n;
+ n->values = $2;
+ $$ = n;
}
- | INSERT OVERRIDING override_kind VALUE_P values_clause
+ | INSERT OVERRIDING override_kind VALUE_P merge_values_clause
{
- InsertStmt *n = makeNode(InsertStmt);
+ MergeWhenClause *n = makeNode(MergeWhenClause);
n->cols = NIL;
n->override = $3;
- n->selectStmt = $5;
-
- $$ = (Node *)n;
+ n->values = $5;
+ $$ = n;
}
- | INSERT '(' insert_column_list ')' values_clause
+ | INSERT '(' insert_column_list ')' merge_values_clause
{
- InsertStmt *n = makeNode(InsertStmt);
+ MergeWhenClause *n = makeNode(MergeWhenClause);
n->cols = $3;
- n->selectStmt = $5;
-
- $$ = (Node *)n;
+ n->values = $5;
+ $$ = n;
}
- | INSERT '(' insert_column_list ')' OVERRIDING override_kind VALUE_P values_clause
+ | INSERT '(' insert_column_list ')' OVERRIDING override_kind VALUE_P merge_values_clause
{
- InsertStmt *n = makeNode(InsertStmt);
+ MergeWhenClause *n = makeNode(MergeWhenClause);
n->cols = $3;
n->override = $6;
- n->selectStmt = $8;
-
- $$ = (Node *)n;
+ n->values = $8;
+ $$ = n;
}
| INSERT DEFAULT VALUES
{
- InsertStmt *n = makeNode(InsertStmt);
+ MergeWhenClause *n = makeNode(MergeWhenClause);
n->cols = NIL;
- n->selectStmt = NULL;
+ n->values = NIL;
+ $$ = n;
+ }
+ ;
- $$ = (Node *)n;
+merge_values_clause:
+ VALUES '(' expr_list ')'
+ {
+ $$ = $3;
}
;
static int transformMergeJoinClause(ParseState *pstate, Node *merge,
List **mergeSourceTargetList);
-static void setNamespaceForMergeAction(ParseState *pstate,
- MergeAction *action);
+static void setNamespaceForMergeWhen(ParseState *pstate,
+ MergeWhenClause *mergeWhenClause);
static void setNamespaceVisibilityForRTE(List *namespace, RangeTblEntry *rte,
bool rel_visible,
bool cols_visible);
* that columns can be referenced unqualified from these relations.
*/
static void
-setNamespaceForMergeAction(ParseState *pstate, MergeAction *action)
+setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause)
{
RangeTblEntry *targetRelRTE,
*sourceRelRTE;
*/
sourceRelRTE = rt_fetch(list_length(pstate->p_rtable) - 1, pstate->p_rtable);
- switch (action->commandType)
+ switch (mergeWhenClause->commandType)
{
case CMD_INSERT:
bool is_terminal[2];
JoinExpr *joinexpr;
RangeTblEntry *resultRelRTE, *mergeRelRTE;
+ List *mergeActionList;
/* There can't be any outer WITH to worry about */
Assert(pstate->p_ctenamespace == NIL);
*/
is_terminal[0] = false;
is_terminal[1] = false;
- foreach(l, stmt->mergeActionList)
+ foreach(l, stmt->mergeWhenClauses)
{
- MergeAction *action = (MergeAction *) lfirst(l);
- int when_type = (action->matched ? 0 : 1);
+ MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l);
+ int when_type = (mergeWhenClause->matched ? 0 : 1);
/*
* Collect action types so we can check Target permissions
*/
- switch (action->commandType)
+ switch (mergeWhenClause->commandType)
{
case CMD_INSERT:
- {
- InsertStmt *istmt = (InsertStmt *) action->stmt;
- SelectStmt *selectStmt = (SelectStmt *) istmt->selectStmt;
-
- /*
- * The grammar allows attaching ORDER BY, LIMIT, FOR
- * UPDATE, or WITH to a VALUES clause and also multiple
- * VALUES clauses. If we have any of those, ERROR.
- */
- if (selectStmt && (selectStmt->valuesLists == NIL ||
- selectStmt->sortClause != NIL ||
- selectStmt->limitOffset != NULL ||
- selectStmt->limitCount != NULL ||
- selectStmt->lockingClause != NIL ||
- selectStmt->withClause != NULL))
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("SELECT not allowed in MERGE INSERT statement")));
-
- if (selectStmt && list_length(selectStmt->valuesLists) > 1)
- ereport(ERROR,
- (errcode(ERRCODE_SYNTAX_ERROR),
- errmsg("Multiple VALUES clauses not allowed in MERGE INSERT statement")));
-
- targetPerms |= ACL_INSERT;
- }
+ targetPerms |= ACL_INSERT;
break;
case CMD_UPDATE:
targetPerms |= ACL_UPDATE;
/*
* Check for unreachable WHEN clauses
*/
- if (action->condition == NULL)
+ if (mergeWhenClause->condition == NULL)
is_terminal[when_type] = true;
else if (is_terminal[when_type])
ereport(ERROR,
* both of those already have RTEs. There is nothing like the EXCLUDED
* pseudo-relation for INSERT ON CONFLICT.
*/
- foreach(l, stmt->mergeActionList)
+ mergeActionList = NIL;
+ foreach(l, stmt->mergeWhenClauses)
{
- MergeAction *action = (MergeAction *) lfirst(l);
+ MergeWhenClause *mergeWhenClause = (MergeWhenClause *) lfirst(l);
+ MergeAction *action = makeNode(MergeAction);
+
+ action->commandType = mergeWhenClause->commandType;
+ action->matched = mergeWhenClause->matched;
/*
* Set namespace for the specific action. This must be done before
* analyzing the WHEN quals and the action targetlisst.
*/
- setNamespaceForMergeAction(pstate, action);
+ setNamespaceForMergeWhen(pstate, mergeWhenClause);
/*
* Transform the when condition.
* are evaluated separately during execution to decide which of the
* WHEN MATCHED or WHEN NOT MATCHED actions to execute.
*/
- action->qual = transformWhereClause(pstate, action->condition,
+ action->qual = transformWhereClause(pstate, mergeWhenClause->condition,
EXPR_KIND_MERGE_WHEN_AND, "WHEN");
/*
{
case CMD_INSERT:
{
- InsertStmt *istmt = (InsertStmt *) action->stmt;
- SelectStmt *selectStmt = (SelectStmt *) istmt->selectStmt;
List *exprList = NIL;
ListCell *lc;
RangeTblEntry *rte;
pstate->p_is_insert = true;
- icolumns = checkInsertTargets(pstate, istmt->cols, &attrnos);
+ icolumns = checkInsertTargets(pstate,
+ mergeWhenClause->cols,
+ &attrnos);
Assert(list_length(icolumns) == list_length(attrnos));
+ action->override = mergeWhenClause->override;
+
/*
* Handle INSERT much like in transformInsertStmt
*/
- if (selectStmt == NULL)
+ if (mergeWhenClause->values == NIL)
{
/*
* We have INSERT ... DEFAULT VALUES. We can handle
* as the Query's targetlist, with no VALUES RTE. So
* it works just like a SELECT without any FROM.
*/
- List *valuesLists = selectStmt->valuesLists;
-
- Assert(list_length(valuesLists) == 1);
- Assert(selectStmt->intoClause == NULL);
/*
* Do basic expression transformation (same as a ROW()
* expr, but allow SetToDefault at top level)
*/
exprList = transformExpressionList(pstate,
- (List *) linitial(valuesLists),
+ mergeWhenClause->values,
EXPR_KIND_VALUES_SINGLE,
true);
/* Prepare row for assignment to target table */
exprList = transformInsertRow(pstate, exprList,
- istmt->cols,
+ mergeWhenClause->cols,
icolumns, attrnos,
false);
}
break;
case CMD_UPDATE:
{
- UpdateStmt *ustmt = (UpdateStmt *) action->stmt;
-
pstate->p_is_insert = false;
- action->targetList = transformUpdateTargetList(pstate, ustmt->targetList);
+ action->targetList = transformUpdateTargetList(pstate,
+ mergeWhenClause->targetList);
}
break;
case CMD_DELETE:
default:
elog(ERROR, "unknown action in MERGE WHEN clause");
}
+
+ mergeActionList = lappend(mergeActionList, action);
}
- qry->mergeActionList = stmt->mergeActionList;
+ qry->mergeActionList = mergeActionList;
/* XXX maybe later */
qry->returningList = NULL;
break;
case CMD_INSERT:
{
- InsertStmt *istmt = (InsertStmt *) action->stmt;
-
action->targetList =
rewriteTargetListIU(action->targetList,
action->commandType,
- istmt->override,
+ action->override,
rt_entry_relation,
parsetree->resultRelation,
NULL);
T_RollupData,
T_GroupingSetData,
T_StatisticExtInfo,
+ T_MergeAction,
/*
* TAGS FOR MEMORY NODES (memnodes.h)
T_DeleteStmt,
T_UpdateStmt,
T_MergeStmt,
- T_MergeAction,
T_SelectStmt,
T_AlterTableStmt,
T_AlterTableCmd,
T_PartitionRangeDatum,
T_PartitionCmd,
T_VacuumRelation,
+ T_MergeWhenClause,
/*
* TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h)
RangeVar *relation; /* target relation to merge into */
Node *source_relation; /* source relation */
Node *join_condition; /* join condition between source and target */
- List *mergeActionList; /* list of MergeAction(s) */
+ List *mergeWhenClauses; /* list of MergeWhenClause(es) */
WithClause *withClause; /* WITH clause */
} MergeStmt;
-typedef struct MergeAction
+typedef struct MergeWhenClause
{
NodeTag type;
bool matched; /* true=MATCHED, false=NOT MATCHED */
- Node *condition; /* WHEN AND conditions (raw parser) */
- Node *qual; /* transformed WHEN AND conditions */
CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
- Node *stmt; /* T_UpdateStmt etc */
- List *targetList; /* the target list (of ResTarget) */
+ Node *condition; /* WHEN AND conditions (raw parser) */
+ List *targetList; /* INSERT/UPDATE targetlist */
+ /* the following members are only useful for INSERT action */
+ List *cols; /* optional: names of the target columns */
+ List *values; /* VALUES to INSERT, or NULL */
+ OverridingKind override; /* OVERRIDING clause */
+} MergeWhenClause;
+
+/*
+ * WHEN [NOT] MATCHED THEN action info
+ */
+typedef struct MergeAction
+{
+ NodeTag type;
+ bool matched; /* true=MATCHED, false=NOT MATCHED */
+ OverridingKind override; /* OVERRIDING clause */
+ Node *qual; /* transformed WHEN AND conditions */
+ CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
+ List *targetList; /* the target list (of ResTarget) */
} MergeAction;
/* ----------------------
ON t.tid = s.sid
WHEN NOT MATCHED THEN
INSERT VALUES (1,1), (2,2);
-ERROR: Multiple VALUES clauses not allowed in MERGE INSERT statement
+ERROR: syntax error at or near ","
+LINE 5: INSERT VALUES (1,1), (2,2);
+ ^
;
-- SELECT query for INSERT
MERGE INTO target t