summaryrefslogtreecommitdiff
path: root/src/include
diff options
context:
space:
mode:
Diffstat (limited to 'src/include')
-rw-r--r--src/include/access/heapam.h13
-rw-r--r--src/include/commands/trigger.h6
-rw-r--r--src/include/executor/execPartition.h1
-rw-r--r--src/include/executor/instrument.h7
-rw-r--r--src/include/executor/nodeModifyTable.h21
-rw-r--r--src/include/executor/spi.h1
-rw-r--r--src/include/nodes/execnodes.h65
-rw-r--r--src/include/nodes/nodes.h6
-rw-r--r--src/include/nodes/parsenodes.h37
-rw-r--r--src/include/nodes/plannodes.h8
-rw-r--r--src/include/nodes/relation.h7
-rw-r--r--src/include/optimizer/pathnode.h7
-rw-r--r--src/include/parser/analyze.h5
-rw-r--r--src/include/parser/kwlist.h2
-rw-r--r--src/include/parser/parse_clause.h5
-rw-r--r--src/include/parser/parse_node.h5
-rw-r--r--src/include/rewrite/rewriteHandler.h1
17 files changed, 177 insertions, 20 deletions
diff --git a/src/include/access/heapam.h b/src/include/access/heapam.h
index 4c0256b18a4..608f50b0616 100644
--- a/src/include/access/heapam.h
+++ b/src/include/access/heapam.h
@@ -53,23 +53,34 @@ typedef enum LockTupleMode
* When heap_update, heap_delete, or heap_lock_tuple fail because the target
* tuple is already outdated, they fill in this struct to provide information
* to the caller about what happened.
+ *
+ * result is the result of HeapTupleSatisfiesUpdate, leading to the failure.
+ * It's set to HeapTupleMayBeUpdated when there is no failure.
+ *
* ctid is the target's ctid link: it is the same as the target's TID if the
* target was deleted, or the location of the replacement tuple if the target
* was updated.
+ *
* xmax is the outdating transaction's XID. If the caller wants to visit the
* replacement tuple, it must check that this matches before believing the
* replacement is really a match.
+ *
* cmax is the outdating command's CID, but only when the failure code is
* HeapTupleSelfUpdated (i.e., something in the current transaction outdated
* the tuple); otherwise cmax is zero. (We make this restriction because
* HeapTupleHeaderGetCmax doesn't work for tuples outdated in other
* transactions.)
+ *
+ * lockmode is only relevant for callers of heap_update() and is the mode which
+ * the caller should use in case it needs to lock the updated tuple.
*/
typedef struct HeapUpdateFailureData
{
+ HTSU_Result result;
ItemPointerData ctid;
TransactionId xmax;
CommandId cmax;
+ LockTupleMode lockmode;
} HeapUpdateFailureData;
@@ -162,7 +173,7 @@ extern void heap_abort_speculative(Relation relation, HeapTuple tuple);
extern HTSU_Result heap_update(Relation relation, ItemPointer otid,
HeapTuple newtup,
CommandId cid, Snapshot crosscheck, bool wait,
- HeapUpdateFailureData *hufd, LockTupleMode *lockmode);
+ HeapUpdateFailureData *hufd);
extern HTSU_Result heap_lock_tuple(Relation relation, HeapTuple tuple,
CommandId cid, LockTupleMode mode, LockWaitPolicy wait_policy,
bool follow_update,
diff --git a/src/include/commands/trigger.h b/src/include/commands/trigger.h
index a5b8610fa22..1b79a803103 100644
--- a/src/include/commands/trigger.h
+++ b/src/include/commands/trigger.h
@@ -206,7 +206,8 @@ extern bool ExecBRDeleteTriggers(EState *estate,
EPQState *epqstate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
- HeapTuple fdw_trigtuple);
+ HeapTuple fdw_trigtuple,
+ HeapUpdateFailureData *hufdp);
extern void ExecARDeleteTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
@@ -225,7 +226,8 @@ extern TupleTableSlot *ExecBRUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
HeapTuple fdw_trigtuple,
- TupleTableSlot *slot);
+ TupleTableSlot *slot,
+ HeapUpdateFailureData *hufdp);
extern void ExecARUpdateTriggers(EState *estate,
ResultRelInfo *relinfo,
ItemPointer tupleid,
diff --git a/src/include/executor/execPartition.h b/src/include/executor/execPartition.h
index 03a599ad57a..9f55f6409ef 100644
--- a/src/include/executor/execPartition.h
+++ b/src/include/executor/execPartition.h
@@ -114,6 +114,7 @@ extern int ExecFindPartition(ResultRelInfo *resultRelInfo,
PartitionDispatch *pd,
TupleTableSlot *slot,
EState *estate);
+extern int ExecFindPartitionByOid(PartitionTupleRouting *proute, Oid partoid);
extern ResultRelInfo *ExecInitPartitionInfo(ModifyTableState *mtstate,
ResultRelInfo *resultRelInfo,
PartitionTupleRouting *proute,
diff --git a/src/include/executor/instrument.h b/src/include/executor/instrument.h
index b72f91898ad..28eb0093d47 100644
--- a/src/include/executor/instrument.h
+++ b/src/include/executor/instrument.h
@@ -58,8 +58,11 @@ typedef struct Instrumentation
double total; /* Total total time (in seconds) */
double ntuples; /* Total tuples produced */
double nloops; /* # of run cycles for this node */
- double nfiltered1; /* # tuples removed by scanqual or joinqual */
- double nfiltered2; /* # tuples removed by "other" quals */
+ double nfiltered1; /* # tuples removed by scanqual or joinqual OR
+ * # tuples inserted by MERGE */
+ double nfiltered2; /* # tuples removed by "other" quals OR
+ * # tuples updated by MERGE */
+ double nfiltered3; /* # tuples deleted by MERGE */
BufferUsage bufusage; /* Total buffer usage */
} Instrumentation;
diff --git a/src/include/executor/nodeModifyTable.h b/src/include/executor/nodeModifyTable.h
index 0d7e579e1cb..686cfa61710 100644
--- a/src/include/executor/nodeModifyTable.h
+++ b/src/include/executor/nodeModifyTable.h
@@ -18,5 +18,26 @@
extern ModifyTableState *ExecInitModifyTable(ModifyTable *node, EState *estate, int eflags);
extern void ExecEndModifyTable(ModifyTableState *node);
extern void ExecReScanModifyTable(ModifyTableState *node);
+extern TupleTableSlot *ExecPrepareTupleRouting(ModifyTableState *mtstate,
+ EState *estate,
+ struct PartitionTupleRouting *proute,
+ ResultRelInfo *targetRelInfo,
+ TupleTableSlot *slot);
+extern TupleTableSlot *ExecDelete(ModifyTableState *mtstate,
+ ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *planSlot,
+ EPQState *epqstate, EState *estate, bool *tupleDeleted,
+ bool processReturning, HeapUpdateFailureData *hufdp,
+ MergeActionState *actionState, bool canSetTag);
+extern TupleTableSlot *ExecUpdate(ModifyTableState *mtstate,
+ ItemPointer tupleid, HeapTuple oldtuple, TupleTableSlot *slot,
+ TupleTableSlot *planSlot, EPQState *epqstate, EState *estate,
+ bool *tuple_updated, HeapUpdateFailureData *hufdp,
+ MergeActionState *actionState, bool canSetTag);
+extern TupleTableSlot *ExecInsert(ModifyTableState *mtstate,
+ TupleTableSlot *slot,
+ TupleTableSlot *planSlot,
+ EState *estate,
+ MergeActionState *actionState,
+ bool canSetTag);
#endif /* NODEMODIFYTABLE_H */
diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h
index e5bdaecc4e3..78410b9f772 100644
--- a/src/include/executor/spi.h
+++ b/src/include/executor/spi.h
@@ -64,6 +64,7 @@ typedef struct _SPI_plan *SPIPlanPtr;
#define SPI_OK_REL_REGISTER 15
#define SPI_OK_REL_UNREGISTER 16
#define SPI_OK_TD_REGISTER 17
+#define SPI_OK_MERGE 18
#define SPI_OPT_NONATOMIC (1 << 0)
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 6070a42b6fe..ff63d179b2a 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -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
@@ -1013,13 +1057,27 @@ typedef struct ProjectSetState
} 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
* ----------------
*/
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;
/* ----------------
diff --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h
index 443de227041..fce48026b6d 100644
--- a/src/include/nodes/nodes.h
+++ b/src/include/nodes/nodes.h
@@ -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
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 92082b3a7a2..d005beeba82 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -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
@@ -1504,6 +1509,30 @@ typedef struct UpdateStmt
} 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
*
* A "simple" SELECT is represented in the output of gram.y by a single
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index c922216b7d1..0a797f0a052 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -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;
/* ----------------
diff --git a/src/include/nodes/relation.h b/src/include/nodes/relation.h
index ea5251c6be2..a2dde70de58 100644
--- a/src/include/nodes/relation.h
+++ b/src/include/nodes/relation.h
@@ -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;
/*
diff --git a/src/include/optimizer/pathnode.h b/src/include/optimizer/pathnode.h
index 381bc30813c..895bf6959da 100644
--- a/src/include/optimizer/pathnode.h
+++ b/src/include/optimizer/pathnode.h
@@ -241,11 +241,14 @@ extern ModifyTablePath *create_modifytable_path(PlannerInfo *root,
CmdType operation, bool canSetTag,
Index nominalRelation, List *partitioned_rels,
bool partColsUpdated,
- List *resultRelations, List *subpaths,
+ List *resultRelations,
+ Index mergeTargetRelation,
+ List *subpaths,
List *subroots,
List *withCheckOptionLists, List *returningLists,
List *rowMarks, OnConflictExpr *onconflict,
- int epqParam);
+ List *mergeSourceTargetList,
+ List *mergeActionList, int epqParam);
extern LimitPath *create_limit_path(PlannerInfo *root, RelOptInfo *rel,
Path *subpath,
Node *limitOffset, Node *limitCount,
diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h
index 687ae1b5b7c..41fb10666e5 100644
--- a/src/include/parser/analyze.h
+++ b/src/include/parser/analyze.h
@@ -32,6 +32,11 @@ extern Query *parse_sub_analyze(Node *parseTree, ParseState *parentParseState,
bool locked_from_parent,
bool resolve_unknowns);
+extern List *transformInsertRow(ParseState *pstate, List *exprlist,
+ List *stmtcols, List *icolumns, List *attrnos,
+ bool strip_indirection);
+extern List *transformUpdateTargetList(ParseState *pstate,
+ List *targetList);
extern Query *transformTopLevelStmt(ParseState *pstate, RawStmt *parseTree);
extern Query *transformStmt(ParseState *pstate, Node *parseTree);
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index cf32197bc3e..4dff55a8e99 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -244,8 +244,10 @@ PG_KEYWORD("locked", LOCKED, UNRESERVED_KEYWORD)
PG_KEYWORD("logged", LOGGED, UNRESERVED_KEYWORD)
PG_KEYWORD("mapping", MAPPING, UNRESERVED_KEYWORD)
PG_KEYWORD("match", MATCH, UNRESERVED_KEYWORD)
+PG_KEYWORD("matched", MATCHED, UNRESERVED_KEYWORD)
PG_KEYWORD("materialized", MATERIALIZED, UNRESERVED_KEYWORD)
PG_KEYWORD("maxvalue", MAXVALUE, UNRESERVED_KEYWORD)
+PG_KEYWORD("merge", MERGE, UNRESERVED_KEYWORD)
PG_KEYWORD("method", METHOD, UNRESERVED_KEYWORD)
PG_KEYWORD("minute", MINUTE_P, UNRESERVED_KEYWORD)
PG_KEYWORD("minvalue", MINVALUE, UNRESERVED_KEYWORD)
diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h
index 2c0e0928628..30121c98ed8 100644
--- a/src/include/parser/parse_clause.h
+++ b/src/include/parser/parse_clause.h
@@ -20,7 +20,10 @@ extern void transformFromClause(ParseState *pstate, List *frmList);
extern int setTargetTable(ParseState *pstate, RangeVar *relation,
bool inh, bool alsoSource, AclMode requiredPerms);
extern bool interpretOidsOption(List *defList, bool allowOids);
-
+extern Node *transformFromClauseItem(ParseState *pstate, Node *n,
+ RangeTblEntry **top_rte, int *top_rti,
+ RangeTblEntry **right_rte, int *right_rti,
+ List **namespace);
extern Node *transformWhereClause(ParseState *pstate, Node *clause,
ParseExprKind exprKind, const char *constructName);
extern Node *transformLimitClause(ParseState *pstate, Node *clause,
diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h
index 0230543810f..3fd2151ccbe 100644
--- a/src/include/parser/parse_node.h
+++ b/src/include/parser/parse_node.h
@@ -50,6 +50,7 @@ typedef enum ParseExprKind
EXPR_KIND_INSERT_TARGET, /* INSERT target list item */
EXPR_KIND_UPDATE_SOURCE, /* UPDATE assignment source item */
EXPR_KIND_UPDATE_TARGET, /* UPDATE assignment target item */
+ EXPR_KIND_MERGE_WHEN_AND, /* MERGE WHEN ... AND condition */
EXPR_KIND_GROUP_BY, /* GROUP BY */
EXPR_KIND_ORDER_BY, /* ORDER BY */
EXPR_KIND_DISTINCT_ON, /* DISTINCT ON */
@@ -127,7 +128,7 @@ typedef Node *(*CoerceParamHook) (ParseState *pstate, Param *param,
* p_parent_cte: CommonTableExpr that immediately contains the current query,
* if any.
*
- * p_target_relation: target relation, if query is INSERT, UPDATE, or DELETE.
+ * p_target_relation: target relation, if query is INSERT/UPDATE/DELETE/MERGE
*
* p_target_rangetblentry: target relation's entry in the rtable list.
*
@@ -181,7 +182,7 @@ struct ParseState
List *p_ctenamespace; /* current namespace for common table exprs */
List *p_future_ctes; /* common table exprs not yet in namespace */
CommonTableExpr *p_parent_cte; /* this query's containing CTE */
- Relation p_target_relation; /* INSERT/UPDATE/DELETE target rel */
+ Relation p_target_relation; /* INSERT/UPDATE/DELETE/MERGE target rel */
RangeTblEntry *p_target_rangetblentry; /* target rel's RTE */
bool p_is_insert; /* process assignment like INSERT not UPDATE */
List *p_windowdefs; /* raw representations of window clauses */
diff --git a/src/include/rewrite/rewriteHandler.h b/src/include/rewrite/rewriteHandler.h
index 8128199fc31..1ab5de39422 100644
--- a/src/include/rewrite/rewriteHandler.h
+++ b/src/include/rewrite/rewriteHandler.h
@@ -25,6 +25,7 @@ extern void AcquireRewriteLocks(Query *parsetree,
extern Node *build_column_default(Relation rel, int attrno);
extern void rewriteTargetListUD(Query *parsetree, RangeTblEntry *target_rte,
Relation target_relation);
+extern void rewriteTargetListMerge(Query *parsetree, Relation target_relation);
extern Query *get_view_query(Relation view);
extern const char *view_query_is_auto_updatable(Query *viewquery,