aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_merge.c
diff options
context:
space:
mode:
authorSimon Riggs <simon@2ndQuadrant.com>2018-04-06 09:38:59 +0100
committerSimon Riggs <simon@2ndQuadrant.com>2018-04-06 09:38:59 +0100
commitf1464c53804fa7280a7942f6ac08038440f73b11 (patch)
tree92667694010e18a435bd7d409b341d935a1ee90b /src/backend/parser/parse_merge.c
parent3b0b4f31f73a5f45f8e122d826211c13cd2412f7 (diff)
downloadpostgresql-f1464c53804fa7280a7942f6ac08038440f73b11.tar.gz
postgresql-f1464c53804fa7280a7942f6ac08038440f73b11.zip
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
Diffstat (limited to 'src/backend/parser/parse_merge.c')
-rw-r--r--src/backend/parser/parse_merge.c86
1 files changed, 33 insertions, 53 deletions
diff --git a/src/backend/parser/parse_merge.c b/src/backend/parser/parse_merge.c
index eb4c615ce1c..722cb23b86c 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;