From f1464c53804fa7280a7942f6ac08038440f73b11 Mon Sep 17 00:00:00 2001 From: Simon Riggs Date: Fri, 6 Apr 2018 09:38:59 +0100 Subject: [PATCH] Improve parse representation for MERGE Separation of parser data structures from executor, as requested by Tom Lane. Further improvements possible. While there, implement error for multiple VALUES clauses via parser to allow line number of error, as requested by Andres Freund. Author: Pavan Deolasee Discussion: https://www.postgresql.org/message-id/CABOikdPpqjectFchg0FyTOpsGXyPoqwgC==OLKWuxgBOsrDDZw@mail.gmail.com --- src/backend/nodes/copyfuncs.c | 35 ++++++++--- src/backend/nodes/equalfuncs.c | 28 +++++++-- src/backend/nodes/nodeFuncs.c | 14 +++-- src/backend/nodes/outfuncs.c | 27 ++++++-- src/backend/nodes/readfuncs.c | 35 ++++++++--- src/backend/parser/gram.y | 92 +++++++++++++--------------- src/backend/parser/parse_merge.c | 86 ++++++++++---------------- src/backend/rewrite/rewriteHandler.c | 4 +- src/include/nodes/nodes.h | 3 +- src/include/nodes/parsenodes.h | 27 ++++++-- src/test/regress/expected/merge.out | 4 +- 11 files changed, 207 insertions(+), 148 deletions(-) diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index c3efca3c45..d2e4aa3c2f 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -2136,6 +2136,20 @@ _copyOnConflictExpr(const OnConflictExpr *from) 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 * @@ -3054,24 +3068,24 @@ _copyMergeStmt(const MergeStmt *from) 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; } @@ -5059,6 +5073,9 @@ copyObjectImpl(const void *from) case T_OnConflictExpr: retval = _copyOnConflictExpr(from); break; + case T_MergeAction: + retval = _copyMergeAction(from); + break; /* * RELATION NODES @@ -5140,8 +5157,8 @@ copyObjectImpl(const void *from) 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); diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 45ceba2830..f2dd9035df 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -812,6 +812,18 @@ _equalOnConflictExpr(const OnConflictExpr *a, const OnConflictExpr *b) 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 */ @@ -1050,21 +1062,22 @@ _equalMergeStmt(const MergeStmt *a, const MergeStmt *b) 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; } @@ -3192,6 +3205,9 @@ equal(const void *a, const void *b) case T_OnConflictExpr: retval = _equalOnConflictExpr(a, b); break; + case T_MergeAction: + retval = _equalMergeAction(a, b); + break; case T_JoinExpr: retval = _equalJoinExpr(a, b); break; @@ -3263,8 +3279,8 @@ equal(const void *a, const void *b) 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); diff --git a/src/backend/nodes/nodeFuncs.c b/src/backend/nodes/nodeFuncs.c index 4157e7eb9a..f2f8227eb2 100644 --- a/src/backend/nodes/nodeFuncs.c +++ b/src/backend/nodes/nodeFuncs.c @@ -3444,19 +3444,23 @@ raw_expression_tree_walker(Node *node, 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; diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c index c8d962670e..a6a1c16164 100644 --- a/src/backend/nodes/outfuncs.c +++ b/src/backend/nodes/outfuncs.c @@ -396,16 +396,17 @@ _outModifyTable(StringInfo str, const ModifyTable *node) } 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 @@ -1724,6 +1725,17 @@ _outOnConflictExpr(StringInfo str, const OnConflictExpr *node) 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. @@ -3679,8 +3691,8 @@ outNode(StringInfo str, const void *obj) 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); @@ -3958,6 +3970,9 @@ outNode(StringInfo str, const void *obj) case T_OnConflictExpr: _outOnConflictExpr(str, obj); break; + case T_MergeAction: + _outMergeAction(str, obj); + break; case T_Path: _outPath(str, obj); break; diff --git a/src/backend/nodes/readfuncs.c b/src/backend/nodes/readfuncs.c index 4518fa0cdb..37e3568595 100644 --- a/src/backend/nodes/readfuncs.c +++ b/src/backend/nodes/readfuncs.c @@ -1331,6 +1331,22 @@ _readOnConflictExpr(void) 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. */ @@ -1602,19 +1618,20 @@ _readModifyTable(void) } /* - * _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(); } @@ -2596,6 +2613,8 @@ parseNodeString(void) 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)) @@ -2618,8 +2637,8 @@ parseNodeString(void) 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)) diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1592b58bb4..177906e083 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -241,6 +241,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); PartitionSpec *partspec; PartitionBoundSpec *partboundspec; RoleSpec *rolespec; + MergeWhenClause *mergewhen; } %type stmt schema_stmt @@ -400,6 +401,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); TriggerTransitions TriggerReferencing publication_name_list vacuum_relation_list opt_vacuum_relation_list + merge_values_clause %type group_by_list %type group_by_item empty_grouping_set rollup_clause cube_clause @@ -460,6 +462,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type insert_rest %type opt_conf_expr %type opt_on_conflict +%type merge_insert merge_update merge_delete %type generic_set set_rest set_rest_more generic_reset reset_rest SetResetClause FunctionSetResetClause @@ -587,7 +590,6 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query); %type merge_when_clause opt_merge_when_and_condition %type merge_when_list -%type merge_update merge_delete merge_insert /* * Non-keyword token types. These are hard-wired into the "flex" lexer. @@ -11116,7 +11118,7 @@ MergeStmt: m->relation = $4; m->source_relation = $6; m->join_condition = $8; - m->mergeActionList = $9; + m->mergeWhenClauses = $9; $$ = (Node *)m; } @@ -11131,45 +11133,37 @@ merge_when_list: 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; } @@ -11181,65 +11175,63 @@ opt_merge_when_and_condition: ; 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; } ; diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c index eb4c615ce1..722cb23b86 100644 --- a/src/backend/parser/parse_merge.c +++ b/src/backend/parser/parse_merge.c @@ -33,8 +33,8 @@ 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); @@ -138,7 +138,7 @@ transformMergeJoinClause(ParseState *pstate, Node *merge, * that columns can be referenced unqualified from these relations. */ static void -setNamespaceForMergeAction(ParseState *pstate, MergeAction *action) +setNamespaceForMergeWhen(ParseState *pstate, MergeWhenClause *mergeWhenClause) { RangeTblEntry *targetRelRTE, *sourceRelRTE; @@ -152,7 +152,7 @@ setNamespaceForMergeAction(ParseState *pstate, MergeAction *action) */ sourceRelRTE = rt_fetch(list_length(pstate->p_rtable) - 1, pstate->p_rtable); - switch (action->commandType) + switch (mergeWhenClause->commandType) { case CMD_INSERT: @@ -198,6 +198,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) 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); @@ -222,43 +223,18 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) */ 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; @@ -275,7 +251,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) /* * 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, @@ -461,15 +437,20 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * 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. @@ -478,7 +459,7 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * 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"); /* @@ -488,8 +469,6 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) { case CMD_INSERT: { - InsertStmt *istmt = (InsertStmt *) action->stmt; - SelectStmt *selectStmt = (SelectStmt *) istmt->selectStmt; List *exprList = NIL; ListCell *lc; RangeTblEntry *rte; @@ -500,13 +479,17 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) 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 @@ -525,23 +508,19 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) * 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); } @@ -580,10 +559,9 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) 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: @@ -595,9 +573,11 @@ transformMergeStmt(ParseState *pstate, MergeStmt *stmt) 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; diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c index 98239f569a..cb4bcd58d1 100644 --- a/src/backend/rewrite/rewriteHandler.c +++ b/src/backend/rewrite/rewriteHandler.c @@ -3417,12 +3417,10 @@ RewriteQuery(Query *parsetree, List *rewrite_events) 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); diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index fce48026b6..b1e3d53f78 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -269,6 +269,7 @@ typedef enum NodeTag T_RollupData, T_GroupingSetData, T_StatisticExtInfo, + T_MergeAction, /* * TAGS FOR MEMORY NODES (memnodes.h) @@ -310,7 +311,6 @@ typedef enum NodeTag T_DeleteStmt, T_UpdateStmt, T_MergeStmt, - T_MergeAction, T_SelectStmt, T_AlterTableStmt, T_AlterTableCmd, @@ -475,6 +475,7 @@ typedef enum NodeTag T_PartitionRangeDatum, T_PartitionCmd, T_VacuumRelation, + T_MergeWhenClause, /* * TAGS FOR REPLICATION GRAMMAR PARSE NODES (replnodes.h) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 699fa77bc7..06abb70e94 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1518,19 +1518,34 @@ typedef struct MergeStmt 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; /* ---------------------- diff --git a/src/test/regress/expected/merge.out b/src/test/regress/expected/merge.out index 389eeedf28..03e30ef559 100644 --- a/src/test/regress/expected/merge.out +++ b/src/test/regress/expected/merge.out @@ -90,7 +90,9 @@ USING source AS s 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