mirror of
https://github.com/postgres/postgres.git
synced 2025-11-01 21:31:19 +03:00
MERGE SQL Command following SQL:2016
MERGE performs actions that modify rows in the target table using a source table or query. MERGE provides a single SQL statement that can conditionally INSERT/UPDATE/DELETE rows a task that would other require multiple PL statements. e.g. MERGE INTO target AS t USING source AS s ON t.tid = s.sid WHEN MATCHED AND t.balance > s.delta THEN UPDATE SET balance = t.balance - s.delta WHEN MATCHED THEN DELETE WHEN NOT MATCHED AND s.delta > 0 THEN INSERT VALUES (s.sid, s.delta) WHEN NOT MATCHED THEN DO NOTHING; MERGE works with regular and partitioned tables, including column and row security enforcement, as well as support for row, statement and transition triggers. MERGE is optimized for OLTP and is parameterizable, though also useful for large scale ETL/ELT. MERGE is not intended to be used in preference to existing single SQL commands for INSERT, UPDATE or DELETE since there is some overhead. MERGE can be used statically from PL/pgSQL. MERGE does not yet support inheritance, write rules, RETURNING clauses, updatable views or foreign tables. MERGE follows SQL Standard per the most recent SQL:2016. Includes full tests and documentation, including full isolation tests to demonstrate the concurrent behavior. This version written from scratch in 2017 by Simon Riggs, using docs and tests originally written in 2009. Later work from Pavan Deolasee has been both complex and deep, leaving the lead author credit now in his hands. Extensive discussion of concurrency from Peter Geoghegan, with thanks for the time and effort contributed. Various issues reported via sqlsmith by Andreas Seltenreich Authors: Pavan Deolasee, Simon Riggs Reviewer: Peter Geoghegan, Amit Langote, Tomas Vondra, Simon Riggs Discussion: https://postgr.es/m/CANP8+jKitBSrB7oTgT9CY2i1ObfOt36z0XMraQc+Xrz8QB0nXA@mail.gmail.com https://postgr.es/m/CAH2-WzkJdBuxj9PO=2QaO9-3h3xGbQPZ34kJH=HukRekwM-GZg@mail.gmail.com
This commit is contained in:
@@ -207,6 +207,7 @@ _copyModifyTable(const ModifyTable *from)
|
||||
COPY_NODE_FIELD(partitioned_rels);
|
||||
COPY_SCALAR_FIELD(partColsUpdated);
|
||||
COPY_NODE_FIELD(resultRelations);
|
||||
COPY_SCALAR_FIELD(mergeTargetRelation);
|
||||
COPY_SCALAR_FIELD(resultRelIndex);
|
||||
COPY_SCALAR_FIELD(rootResultRelIndex);
|
||||
COPY_NODE_FIELD(plans);
|
||||
@@ -222,6 +223,8 @@ _copyModifyTable(const ModifyTable *from)
|
||||
COPY_NODE_FIELD(onConflictWhere);
|
||||
COPY_SCALAR_FIELD(exclRelRTI);
|
||||
COPY_NODE_FIELD(exclRelTlist);
|
||||
COPY_NODE_FIELD(mergeSourceTargetList);
|
||||
COPY_NODE_FIELD(mergeActionList);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
@@ -2977,6 +2980,9 @@ _copyQuery(const Query *from)
|
||||
COPY_NODE_FIELD(setOperations);
|
||||
COPY_NODE_FIELD(constraintDeps);
|
||||
COPY_NODE_FIELD(withCheckOptions);
|
||||
COPY_SCALAR_FIELD(mergeTarget_relation);
|
||||
COPY_NODE_FIELD(mergeSourceTargetList);
|
||||
COPY_NODE_FIELD(mergeActionList);
|
||||
COPY_LOCATION_FIELD(stmt_location);
|
||||
COPY_LOCATION_FIELD(stmt_len);
|
||||
|
||||
@@ -3040,6 +3046,34 @@ _copyUpdateStmt(const UpdateStmt *from)
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static MergeStmt *
|
||||
_copyMergeStmt(const MergeStmt *from)
|
||||
{
|
||||
MergeStmt *newnode = makeNode(MergeStmt);
|
||||
|
||||
COPY_NODE_FIELD(relation);
|
||||
COPY_NODE_FIELD(source_relation);
|
||||
COPY_NODE_FIELD(join_condition);
|
||||
COPY_NODE_FIELD(mergeActionList);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static MergeAction *
|
||||
_copyMergeAction(const MergeAction *from)
|
||||
{
|
||||
MergeAction *newnode = makeNode(MergeAction);
|
||||
|
||||
COPY_SCALAR_FIELD(matched);
|
||||
COPY_SCALAR_FIELD(commandType);
|
||||
COPY_NODE_FIELD(condition);
|
||||
COPY_NODE_FIELD(qual);
|
||||
COPY_NODE_FIELD(stmt);
|
||||
COPY_NODE_FIELD(targetList);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
static SelectStmt *
|
||||
_copySelectStmt(const SelectStmt *from)
|
||||
{
|
||||
@@ -5102,6 +5136,12 @@ copyObjectImpl(const void *from)
|
||||
case T_UpdateStmt:
|
||||
retval = _copyUpdateStmt(from);
|
||||
break;
|
||||
case T_MergeStmt:
|
||||
retval = _copyMergeStmt(from);
|
||||
break;
|
||||
case T_MergeAction:
|
||||
retval = _copyMergeAction(from);
|
||||
break;
|
||||
case T_SelectStmt:
|
||||
retval = _copySelectStmt(from);
|
||||
break;
|
||||
|
||||
@@ -987,6 +987,8 @@ _equalQuery(const Query *a, const Query *b)
|
||||
COMPARE_NODE_FIELD(setOperations);
|
||||
COMPARE_NODE_FIELD(constraintDeps);
|
||||
COMPARE_NODE_FIELD(withCheckOptions);
|
||||
COMPARE_NODE_FIELD(mergeSourceTargetList);
|
||||
COMPARE_NODE_FIELD(mergeActionList);
|
||||
COMPARE_LOCATION_FIELD(stmt_location);
|
||||
COMPARE_LOCATION_FIELD(stmt_len);
|
||||
|
||||
@@ -1042,6 +1044,30 @@ _equalUpdateStmt(const UpdateStmt *a, const UpdateStmt *b)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalMergeAction(const MergeAction *a, const MergeAction *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);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
_equalSelectStmt(const SelectStmt *a, const SelectStmt *b)
|
||||
{
|
||||
@@ -3233,6 +3259,12 @@ equal(const void *a, const void *b)
|
||||
case T_UpdateStmt:
|
||||
retval = _equalUpdateStmt(a, b);
|
||||
break;
|
||||
case T_MergeStmt:
|
||||
retval = _equalMergeStmt(a, b);
|
||||
break;
|
||||
case T_MergeAction:
|
||||
retval = _equalMergeAction(a, b);
|
||||
break;
|
||||
case T_SelectStmt:
|
||||
retval = _equalSelectStmt(a, b);
|
||||
break;
|
||||
|
||||
@@ -2146,6 +2146,16 @@ expression_tree_walker(Node *node,
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_MergeAction:
|
||||
{
|
||||
MergeAction *action = (MergeAction *) node;
|
||||
|
||||
if (walker(action->targetList, context))
|
||||
return true;
|
||||
if (walker(action->qual, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
{
|
||||
JoinExpr *join = (JoinExpr *) node;
|
||||
@@ -2255,6 +2265,10 @@ query_tree_walker(Query *query,
|
||||
return true;
|
||||
if (walker((Node *) query->onConflict, context))
|
||||
return true;
|
||||
if (walker((Node *) query->mergeSourceTargetList, context))
|
||||
return true;
|
||||
if (walker((Node *) query->mergeActionList, context))
|
||||
return true;
|
||||
if (walker((Node *) query->returningList, context))
|
||||
return true;
|
||||
if (walker((Node *) query->jointree, context))
|
||||
@@ -2932,6 +2946,18 @@ expression_tree_mutator(Node *node,
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_MergeAction:
|
||||
{
|
||||
MergeAction *action = (MergeAction *) node;
|
||||
MergeAction *newnode;
|
||||
|
||||
FLATCOPY(newnode, action, MergeAction);
|
||||
MUTATE(newnode->qual, action->qual, Node *);
|
||||
MUTATE(newnode->targetList, action->targetList, List *);
|
||||
|
||||
return (Node *) newnode;
|
||||
}
|
||||
break;
|
||||
case T_JoinExpr:
|
||||
{
|
||||
JoinExpr *join = (JoinExpr *) node;
|
||||
@@ -3083,6 +3109,8 @@ query_tree_mutator(Query *query,
|
||||
MUTATE(query->targetList, query->targetList, List *);
|
||||
MUTATE(query->withCheckOptions, query->withCheckOptions, List *);
|
||||
MUTATE(query->onConflict, query->onConflict, OnConflictExpr *);
|
||||
MUTATE(query->mergeSourceTargetList, query->mergeSourceTargetList, List *);
|
||||
MUTATE(query->mergeActionList, query->mergeActionList, List *);
|
||||
MUTATE(query->returningList, query->returningList, List *);
|
||||
MUTATE(query->jointree, query->jointree, FromExpr *);
|
||||
MUTATE(query->setOperations, query->setOperations, Node *);
|
||||
@@ -3224,9 +3252,9 @@ query_or_expression_tree_mutator(Node *node,
|
||||
* boundaries: we descend to everything that's possibly interesting.
|
||||
*
|
||||
* Currently, the node type coverage here extends only to DML statements
|
||||
* (SELECT/INSERT/UPDATE/DELETE) and nodes that can appear in them, because
|
||||
* this is used mainly during analysis of CTEs, and only DML statements can
|
||||
* appear in CTEs.
|
||||
* (SELECT/INSERT/UPDATE/DELETE/MERGE) and nodes that can appear in them,
|
||||
* because this is used mainly during analysis of CTEs, and only DML
|
||||
* statements can appear in CTEs.
|
||||
*/
|
||||
bool
|
||||
raw_expression_tree_walker(Node *node,
|
||||
@@ -3406,6 +3434,30 @@ raw_expression_tree_walker(Node *node,
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_MergeStmt:
|
||||
{
|
||||
MergeStmt *stmt = (MergeStmt *) node;
|
||||
|
||||
if (walker(stmt->relation, context))
|
||||
return true;
|
||||
if (walker(stmt->source_relation, context))
|
||||
return true;
|
||||
if (walker(stmt->join_condition, context))
|
||||
return true;
|
||||
if (walker(stmt->mergeActionList, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_MergeAction:
|
||||
{
|
||||
MergeAction *action = (MergeAction *) node;
|
||||
|
||||
if (walker(action->targetList, context))
|
||||
return true;
|
||||
if (walker(action->qual, context))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case T_SelectStmt:
|
||||
{
|
||||
SelectStmt *stmt = (SelectStmt *) node;
|
||||
|
||||
@@ -375,6 +375,7 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
|
||||
WRITE_NODE_FIELD(partitioned_rels);
|
||||
WRITE_BOOL_FIELD(partColsUpdated);
|
||||
WRITE_NODE_FIELD(resultRelations);
|
||||
WRITE_INT_FIELD(mergeTargetRelation);
|
||||
WRITE_INT_FIELD(resultRelIndex);
|
||||
WRITE_INT_FIELD(rootResultRelIndex);
|
||||
WRITE_NODE_FIELD(plans);
|
||||
@@ -390,6 +391,21 @@ _outModifyTable(StringInfo str, const ModifyTable *node)
|
||||
WRITE_NODE_FIELD(onConflictWhere);
|
||||
WRITE_UINT_FIELD(exclRelRTI);
|
||||
WRITE_NODE_FIELD(exclRelTlist);
|
||||
WRITE_NODE_FIELD(mergeSourceTargetList);
|
||||
WRITE_NODE_FIELD(mergeActionList);
|
||||
}
|
||||
|
||||
static void
|
||||
_outMergeAction(StringInfo str, const MergeAction *node)
|
||||
{
|
||||
WRITE_NODE_TYPE("MERGEACTION");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2114,6 +2130,7 @@ _outModifyTablePath(StringInfo str, const ModifyTablePath *node)
|
||||
WRITE_NODE_FIELD(partitioned_rels);
|
||||
WRITE_BOOL_FIELD(partColsUpdated);
|
||||
WRITE_NODE_FIELD(resultRelations);
|
||||
WRITE_INT_FIELD(mergeTargetRelation);
|
||||
WRITE_NODE_FIELD(subpaths);
|
||||
WRITE_NODE_FIELD(subroots);
|
||||
WRITE_NODE_FIELD(withCheckOptionLists);
|
||||
@@ -2121,6 +2138,8 @@ _outModifyTablePath(StringInfo str, const ModifyTablePath *node)
|
||||
WRITE_NODE_FIELD(rowMarks);
|
||||
WRITE_NODE_FIELD(onconflict);
|
||||
WRITE_INT_FIELD(epqParam);
|
||||
WRITE_NODE_FIELD(mergeSourceTargetList);
|
||||
WRITE_NODE_FIELD(mergeActionList);
|
||||
}
|
||||
|
||||
static void
|
||||
@@ -2942,6 +2961,9 @@ _outQuery(StringInfo str, const Query *node)
|
||||
WRITE_NODE_FIELD(setOperations);
|
||||
WRITE_NODE_FIELD(constraintDeps);
|
||||
/* withCheckOptions intentionally omitted, see comment in parsenodes.h */
|
||||
WRITE_INT_FIELD(mergeTarget_relation);
|
||||
WRITE_NODE_FIELD(mergeSourceTargetList);
|
||||
WRITE_NODE_FIELD(mergeActionList);
|
||||
WRITE_LOCATION_FIELD(stmt_location);
|
||||
WRITE_LOCATION_FIELD(stmt_len);
|
||||
}
|
||||
@@ -3657,6 +3679,9 @@ outNode(StringInfo str, const void *obj)
|
||||
case T_ModifyTable:
|
||||
_outModifyTable(str, obj);
|
||||
break;
|
||||
case T_MergeAction:
|
||||
_outMergeAction(str, obj);
|
||||
break;
|
||||
case T_Append:
|
||||
_outAppend(str, obj);
|
||||
break;
|
||||
|
||||
@@ -270,6 +270,9 @@ _readQuery(void)
|
||||
READ_NODE_FIELD(setOperations);
|
||||
READ_NODE_FIELD(constraintDeps);
|
||||
/* withCheckOptions intentionally omitted, see comment in parsenodes.h */
|
||||
READ_INT_FIELD(mergeTarget_relation);
|
||||
READ_NODE_FIELD(mergeSourceTargetList);
|
||||
READ_NODE_FIELD(mergeActionList);
|
||||
READ_LOCATION_FIELD(stmt_location);
|
||||
READ_LOCATION_FIELD(stmt_len);
|
||||
|
||||
@@ -1576,6 +1579,7 @@ _readModifyTable(void)
|
||||
READ_NODE_FIELD(partitioned_rels);
|
||||
READ_BOOL_FIELD(partColsUpdated);
|
||||
READ_NODE_FIELD(resultRelations);
|
||||
READ_INT_FIELD(mergeTargetRelation);
|
||||
READ_INT_FIELD(resultRelIndex);
|
||||
READ_INT_FIELD(rootResultRelIndex);
|
||||
READ_NODE_FIELD(plans);
|
||||
@@ -1591,6 +1595,26 @@ _readModifyTable(void)
|
||||
READ_NODE_FIELD(onConflictWhere);
|
||||
READ_UINT_FIELD(exclRelRTI);
|
||||
READ_NODE_FIELD(exclRelTlist);
|
||||
READ_NODE_FIELD(mergeSourceTargetList);
|
||||
READ_NODE_FIELD(mergeActionList);
|
||||
|
||||
READ_DONE();
|
||||
}
|
||||
|
||||
/*
|
||||
* _readMergeAction
|
||||
*/
|
||||
static MergeAction *
|
||||
_readMergeAction(void)
|
||||
{
|
||||
READ_LOCALS(MergeAction);
|
||||
|
||||
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_DONE();
|
||||
}
|
||||
@@ -2594,6 +2618,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("APPEND", 6))
|
||||
return_value = _readAppend();
|
||||
else if (MATCH("MERGEAPPEND", 11))
|
||||
|
||||
Reference in New Issue
Block a user