1
0
mirror of https://github.com/postgres/postgres.git synced 2025-08-21 10:42:50 +03:00

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
This commit is contained in:
Simon Riggs
2018-04-06 09:38:59 +01:00
parent 3b0b4f31f7
commit f1464c5380
11 changed files with 207 additions and 148 deletions

View File

@@ -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;