mirror of
https://github.com/postgres/postgres.git
synced 2025-11-03 09:13:20 +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:
@@ -360,8 +360,17 @@ typedef struct JunkFilter
|
||||
AttrNumber *jf_cleanMap;
|
||||
TupleTableSlot *jf_resultSlot;
|
||||
AttrNumber jf_junkAttNo;
|
||||
AttrNumber jf_otherJunkAttNo;
|
||||
} JunkFilter;
|
||||
|
||||
typedef struct MergeState
|
||||
{
|
||||
/* List of MERGE MATCHED action states */
|
||||
List *matchedActionStates;
|
||||
/* List of MERGE NOT MATCHED action states */
|
||||
List *notMatchedActionStates;
|
||||
} MergeState;
|
||||
|
||||
/*
|
||||
* OnConflictSetState
|
||||
*
|
||||
@@ -452,8 +461,38 @@ typedef struct ResultRelInfo
|
||||
|
||||
/* relation descriptor for root partitioned table */
|
||||
Relation ri_PartitionRoot;
|
||||
|
||||
int ri_PartitionLeafIndex;
|
||||
/* for running MERGE on this result relation */
|
||||
MergeState *ri_mergeState;
|
||||
|
||||
/*
|
||||
* While executing MERGE, the target relation is processed twice; once
|
||||
* as a target relation and once to run a join between the target and the
|
||||
* source. We generate two different RTEs for these two purposes, one with
|
||||
* rte->inh set to false and other with rte->inh set to true.
|
||||
*
|
||||
* Since the plan re-evaluated by EvalPlanQual uses the join RTE, we must
|
||||
* install the updated tuple in the scan corresponding to that RTE. The
|
||||
* following member tracks the index of the second RTE for EvalPlanQual
|
||||
* purposes. ri_mergeTargetRTI is non-zero only when MERGE is in-progress.
|
||||
* We use ri_mergeTargetRTI to run EvalPlanQual for MERGE and
|
||||
* ri_RangeTableIndex elsewhere.
|
||||
*/
|
||||
Index ri_mergeTargetRTI;
|
||||
} ResultRelInfo;
|
||||
|
||||
/*
|
||||
* Get the Range table index for EvalPlanQual.
|
||||
*
|
||||
* We use the ri_mergeTargetRTI if set, otherwise use ri_RangeTableIndex.
|
||||
* ri_mergeTargetRTI should really be ever set iff we're running MERGE.
|
||||
*/
|
||||
#define GetEPQRangeTableIndex(r) \
|
||||
(((r)->ri_mergeTargetRTI > 0) \
|
||||
? (r)->ri_mergeTargetRTI \
|
||||
: (r)->ri_RangeTableIndex)
|
||||
|
||||
/* ----------------
|
||||
* EState information
|
||||
*
|
||||
@@ -966,6 +1005,11 @@ typedef struct PlanState
|
||||
if (((PlanState *)(node))->instrument) \
|
||||
((PlanState *)(node))->instrument->nfiltered2 += (delta); \
|
||||
} while(0)
|
||||
#define InstrCountFiltered3(node, delta) \
|
||||
do { \
|
||||
if (((PlanState *)(node))->instrument) \
|
||||
((PlanState *)(node))->instrument->nfiltered3 += (delta); \
|
||||
} while(0)
|
||||
|
||||
/*
|
||||
* EPQState is state for executing an EvalPlanQual recheck on a candidate
|
||||
@@ -1012,6 +1056,20 @@ typedef struct ProjectSetState
|
||||
MemoryContext argcontext; /* context for SRF arguments */
|
||||
} ProjectSetState;
|
||||
|
||||
/* ----------------
|
||||
* MergeActionState information
|
||||
* ----------------
|
||||
*/
|
||||
typedef struct MergeActionState
|
||||
{
|
||||
NodeTag type;
|
||||
bool matched; /* true=MATCHED, false=NOT MATCHED */
|
||||
ExprState *whenqual; /* WHEN AND conditions */
|
||||
CmdType commandType; /* INSERT/UPDATE/DELETE/DO NOTHING */
|
||||
ProjectionInfo *proj; /* tuple projection info */
|
||||
TupleDesc tupDesc; /* tuple descriptor for projection */
|
||||
} MergeActionState;
|
||||
|
||||
/* ----------------
|
||||
* ModifyTableState information
|
||||
* ----------------
|
||||
@@ -1019,7 +1077,7 @@ typedef struct ProjectSetState
|
||||
typedef struct ModifyTableState
|
||||
{
|
||||
PlanState ps; /* its first field is NodeTag */
|
||||
CmdType operation; /* INSERT, UPDATE, or DELETE */
|
||||
CmdType operation; /* INSERT, UPDATE, DELETE or MERGE */
|
||||
bool canSetTag; /* do we set the command tag/es_processed? */
|
||||
bool mt_done; /* are we done? */
|
||||
PlanState **mt_plans; /* subplans (one per target rel) */
|
||||
@@ -1035,6 +1093,8 @@ typedef struct ModifyTableState
|
||||
List *mt_excludedtlist; /* the excluded pseudo relation's tlist */
|
||||
TupleTableSlot *mt_conflproj; /* CONFLICT ... SET ... projection target */
|
||||
|
||||
TupleTableSlot *mt_mergeproj; /* MERGE action projection target */
|
||||
|
||||
/* Tuple-routing support info */
|
||||
struct PartitionTupleRouting *mt_partition_tuple_routing;
|
||||
|
||||
@@ -1046,6 +1106,9 @@ typedef struct ModifyTableState
|
||||
|
||||
/* Per plan map for tuple conversion from child to root */
|
||||
TupleConversionMap **mt_per_subplan_tupconv_maps;
|
||||
|
||||
/* Flags showing which subcommands are present INS/UPD/DEL/DO NOTHING */
|
||||
int mt_merge_subcommands;
|
||||
} ModifyTableState;
|
||||
|
||||
/* ----------------
|
||||
|
||||
@@ -97,6 +97,7 @@ typedef enum NodeTag
|
||||
T_PlanState,
|
||||
T_ResultState,
|
||||
T_ProjectSetState,
|
||||
T_MergeActionState,
|
||||
T_ModifyTableState,
|
||||
T_AppendState,
|
||||
T_MergeAppendState,
|
||||
@@ -308,6 +309,8 @@ typedef enum NodeTag
|
||||
T_InsertStmt,
|
||||
T_DeleteStmt,
|
||||
T_UpdateStmt,
|
||||
T_MergeStmt,
|
||||
T_MergeAction,
|
||||
T_SelectStmt,
|
||||
T_AlterTableStmt,
|
||||
T_AlterTableCmd,
|
||||
@@ -657,7 +660,8 @@ typedef enum CmdType
|
||||
CMD_SELECT, /* select stmt */
|
||||
CMD_UPDATE, /* update stmt */
|
||||
CMD_INSERT, /* insert stmt */
|
||||
CMD_DELETE,
|
||||
CMD_DELETE, /* delete stmt */
|
||||
CMD_MERGE, /* merge stmt */
|
||||
CMD_UTILITY, /* cmds like create, destroy, copy, vacuum,
|
||||
* etc. */
|
||||
CMD_NOTHING /* dummy command for instead nothing rules
|
||||
|
||||
@@ -38,7 +38,7 @@ typedef enum OverridingKind
|
||||
typedef enum QuerySource
|
||||
{
|
||||
QSRC_ORIGINAL, /* original parsetree (explicit query) */
|
||||
QSRC_PARSER, /* added by parse analysis (now unused) */
|
||||
QSRC_PARSER, /* added by parse analysis in MERGE */
|
||||
QSRC_INSTEAD_RULE, /* added by unconditional INSTEAD rule */
|
||||
QSRC_QUAL_INSTEAD_RULE, /* added by conditional INSTEAD rule */
|
||||
QSRC_NON_INSTEAD_RULE /* added by non-INSTEAD rule */
|
||||
@@ -107,7 +107,7 @@ typedef struct Query
|
||||
{
|
||||
NodeTag type;
|
||||
|
||||
CmdType commandType; /* select|insert|update|delete|utility */
|
||||
CmdType commandType; /* select|insert|update|delete|merge|utility */
|
||||
|
||||
QuerySource querySource; /* where did I come from? */
|
||||
|
||||
@@ -118,7 +118,7 @@ typedef struct Query
|
||||
Node *utilityStmt; /* non-null if commandType == CMD_UTILITY */
|
||||
|
||||
int resultRelation; /* rtable index of target relation for
|
||||
* INSERT/UPDATE/DELETE; 0 for SELECT */
|
||||
* INSERT/UPDATE/DELETE/MERGE; 0 for SELECT */
|
||||
|
||||
bool hasAggs; /* has aggregates in tlist or havingQual */
|
||||
bool hasWindowFuncs; /* has window functions in tlist */
|
||||
@@ -169,6 +169,9 @@ typedef struct Query
|
||||
List *withCheckOptions; /* a list of WithCheckOption's, which are
|
||||
* only added during rewrite and therefore
|
||||
* are not written out as part of Query. */
|
||||
int mergeTarget_relation;
|
||||
List *mergeSourceTargetList;
|
||||
List *mergeActionList; /* list of actions for MERGE (only) */
|
||||
|
||||
/*
|
||||
* The following two fields identify the portion of the source text string
|
||||
@@ -1128,7 +1131,9 @@ typedef enum WCOKind
|
||||
WCO_VIEW_CHECK, /* WCO on an auto-updatable view */
|
||||
WCO_RLS_INSERT_CHECK, /* RLS INSERT WITH CHECK policy */
|
||||
WCO_RLS_UPDATE_CHECK, /* RLS UPDATE WITH CHECK policy */
|
||||
WCO_RLS_CONFLICT_CHECK /* RLS ON CONFLICT DO UPDATE USING policy */
|
||||
WCO_RLS_CONFLICT_CHECK, /* RLS ON CONFLICT DO UPDATE USING policy */
|
||||
WCO_RLS_MERGE_UPDATE_CHECK, /* RLS MERGE UPDATE USING policy */
|
||||
WCO_RLS_MERGE_DELETE_CHECK /* RLS MERGE DELETE USING policy */
|
||||
} WCOKind;
|
||||
|
||||
typedef struct WithCheckOption
|
||||
@@ -1503,6 +1508,30 @@ typedef struct UpdateStmt
|
||||
WithClause *withClause; /* WITH clause */
|
||||
} UpdateStmt;
|
||||
|
||||
/* ----------------------
|
||||
* Merge Statement
|
||||
* ----------------------
|
||||
*/
|
||||
typedef struct MergeStmt
|
||||
{
|
||||
NodeTag type;
|
||||
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) */
|
||||
} MergeStmt;
|
||||
|
||||
typedef struct MergeAction
|
||||
{
|
||||
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) */
|
||||
} MergeAction;
|
||||
|
||||
/* ----------------------
|
||||
* Select Statement
|
||||
*
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "lib/stringinfo.h"
|
||||
#include "nodes/bitmapset.h"
|
||||
#include "nodes/lockoptions.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/primnodes.h"
|
||||
|
||||
|
||||
@@ -42,7 +43,7 @@ typedef struct PlannedStmt
|
||||
{
|
||||
NodeTag type;
|
||||
|
||||
CmdType commandType; /* select|insert|update|delete|utility */
|
||||
CmdType commandType; /* select|insert|update|delete|merge|utility */
|
||||
|
||||
uint64 queryId; /* query identifier (copied from Query) */
|
||||
|
||||
@@ -216,13 +217,14 @@ typedef struct ProjectSet
|
||||
typedef struct ModifyTable
|
||||
{
|
||||
Plan plan;
|
||||
CmdType operation; /* INSERT, UPDATE, or DELETE */
|
||||
CmdType operation; /* INSERT, UPDATE, DELETE or MERGE */
|
||||
bool canSetTag; /* do we set the command tag/es_processed? */
|
||||
Index nominalRelation; /* Parent RT index for use of EXPLAIN */
|
||||
/* RT indexes of non-leaf tables in a partition tree */
|
||||
List *partitioned_rels;
|
||||
bool partColsUpdated; /* some part key in hierarchy updated */
|
||||
List *resultRelations; /* integer list of RT indexes */
|
||||
Index mergeTargetRelation; /* RT index of the merge target */
|
||||
int resultRelIndex; /* index of first resultRel in plan's list */
|
||||
int rootResultRelIndex; /* index of the partitioned table root */
|
||||
List *plans; /* plan(s) producing source data */
|
||||
@@ -238,6 +240,8 @@ typedef struct ModifyTable
|
||||
Node *onConflictWhere; /* WHERE for ON CONFLICT UPDATE */
|
||||
Index exclRelRTI; /* RTI of the EXCLUDED pseudo relation */
|
||||
List *exclRelTlist; /* tlist of the EXCLUDED pseudo relation */
|
||||
List *mergeSourceTargetList;
|
||||
List *mergeActionList; /* actions for MERGE */
|
||||
} ModifyTable;
|
||||
|
||||
/* ----------------
|
||||
|
||||
@@ -1670,7 +1670,7 @@ typedef struct LockRowsPath
|
||||
} LockRowsPath;
|
||||
|
||||
/*
|
||||
* ModifyTablePath represents performing INSERT/UPDATE/DELETE modifications
|
||||
* ModifyTablePath represents performing INSERT/UPDATE/DELETE/MERGE
|
||||
*
|
||||
* We represent most things that will be in the ModifyTable plan node
|
||||
* literally, except we have child Path(s) not Plan(s). But analysis of the
|
||||
@@ -1679,13 +1679,14 @@ typedef struct LockRowsPath
|
||||
typedef struct ModifyTablePath
|
||||
{
|
||||
Path path;
|
||||
CmdType operation; /* INSERT, UPDATE, or DELETE */
|
||||
CmdType operation; /* INSERT, UPDATE, DELETE or MERGE */
|
||||
bool canSetTag; /* do we set the command tag/es_processed? */
|
||||
Index nominalRelation; /* Parent RT index for use of EXPLAIN */
|
||||
/* RT indexes of non-leaf tables in a partition tree */
|
||||
List *partitioned_rels;
|
||||
bool partColsUpdated; /* some part key in hierarchy updated */
|
||||
List *resultRelations; /* integer list of RT indexes */
|
||||
Index mergeTargetRelation;/* RT index of merge target relation */
|
||||
List *subpaths; /* Path(s) producing source data */
|
||||
List *subroots; /* per-target-table PlannerInfos */
|
||||
List *withCheckOptionLists; /* per-target-table WCO lists */
|
||||
@@ -1693,6 +1694,8 @@ typedef struct ModifyTablePath
|
||||
List *rowMarks; /* PlanRowMarks (non-locking only) */
|
||||
OnConflictExpr *onconflict; /* ON CONFLICT clause, or NULL */
|
||||
int epqParam; /* ID of Param for EvalPlanQual re-eval */
|
||||
List *mergeSourceTargetList;
|
||||
List *mergeActionList; /* actions for MERGE */
|
||||
} ModifyTablePath;
|
||||
|
||||
/*
|
||||
|
||||
Reference in New Issue
Block a user