summaryrefslogtreecommitdiff
path: root/src/include/nodes
diff options
context:
space:
mode:
authorDean Rasheed2025-01-16 14:57:35 +0000
committerDean Rasheed2025-01-16 14:57:35 +0000
commit80feb727c869cc0b2e12bd1543bafa449be9c8e2 (patch)
tree27fb43ef4b09067e3d725e1b918539d492a8550c /src/include/nodes
parent7407b2d48cf37bc8847ae6c47dde2164ef2faa34 (diff)
Add OLD/NEW support to RETURNING in DML queries.
This allows the RETURNING list of INSERT/UPDATE/DELETE/MERGE queries to explicitly return old and new values by using the special aliases "old" and "new", which are automatically added to the query (if not already defined) while parsing its RETURNING list, allowing things like: RETURNING old.colname, new.colname, ... RETURNING old.*, new.* Additionally, a new syntax is supported, allowing the names "old" and "new" to be changed to user-supplied alias names, e.g.: RETURNING WITH (OLD AS o, NEW AS n) o.colname, n.colname, ... This is useful when the names "old" and "new" are already defined, such as inside trigger functions, allowing backwards compatibility to be maintained -- the interpretation of any existing queries that happen to already refer to relations called "old" or "new", or use those as aliases for other relations, is not changed. For an INSERT, old values will generally be NULL, and for a DELETE, new values will generally be NULL, but that may change for an INSERT with an ON CONFLICT ... DO UPDATE clause, or if a query rewrite rule changes the command type. Therefore, we put no restrictions on the use of old and new in any DML queries. Dean Rasheed, reviewed by Jian He and Jeff Davis. Discussion: https://postgr.es/m/CAEZATCWx0J0-v=Qjc6gXzR=KtsdvAE7Ow=D=mu50AgOe+pvisQ@mail.gmail.com
Diffstat (limited to 'src/include/nodes')
-rw-r--r--src/include/nodes/execnodes.h16
-rw-r--r--src/include/nodes/parsenodes.h52
-rw-r--r--src/include/nodes/plannodes.h2
-rw-r--r--src/include/nodes/primnodes.h40
4 files changed, 106 insertions, 4 deletions
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index b3f7aa299f5..d0f2dca5928 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -74,11 +74,20 @@ typedef Datum (*ExprStateEvalFunc) (struct ExprState *expression,
/* Bits in ExprState->flags (see also execExpr.h for private flag bits): */
/* expression is for use with ExecQual() */
#define EEO_FLAG_IS_QUAL (1 << 0)
+/* expression refers to OLD table columns */
+#define EEO_FLAG_HAS_OLD (1 << 1)
+/* expression refers to NEW table columns */
+#define EEO_FLAG_HAS_NEW (1 << 2)
+/* OLD table row is NULL in RETURNING list */
+#define EEO_FLAG_OLD_IS_NULL (1 << 3)
+/* NEW table row is NULL in RETURNING list */
+#define EEO_FLAG_NEW_IS_NULL (1 << 4)
typedef struct ExprState
{
NodeTag type;
+#define FIELDNO_EXPRSTATE_FLAGS 1
uint8 flags; /* bitmask of EEO_FLAG_* bits, see above */
/*
@@ -290,6 +299,12 @@ typedef struct ExprContext
#define FIELDNO_EXPRCONTEXT_DOMAINNULL 13
bool domainValue_isNull;
+ /* Tuples that OLD/NEW Var nodes in RETURNING may refer to */
+#define FIELDNO_EXPRCONTEXT_OLDTUPLE 14
+ TupleTableSlot *ecxt_oldtuple;
+#define FIELDNO_EXPRCONTEXT_NEWTUPLE 15
+ TupleTableSlot *ecxt_newtuple;
+
/* Link to containing EState (NULL if a standalone ExprContext) */
struct EState *ecxt_estate;
@@ -504,6 +519,7 @@ typedef struct ResultRelInfo
TupleTableSlot *ri_ReturningSlot; /* for trigger output tuples */
TupleTableSlot *ri_TrigOldSlot; /* for a trigger's old tuple */
TupleTableSlot *ri_TrigNewSlot; /* for a trigger's new tuple */
+ TupleTableSlot *ri_AllNullSlot; /* for RETURNING OLD/NEW */
/* FDW callback functions, if foreign table */
struct FdwRoutine *ri_FdwRoutine;
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index b191eaaecab..ffe155ee20e 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -197,6 +197,15 @@ typedef struct Query
OnConflictExpr *onConflict; /* ON CONFLICT DO [NOTHING | UPDATE] */
+ /*
+ * The following three fields describe the contents of the RETURNING list
+ * for INSERT/UPDATE/DELETE/MERGE. returningOldAlias and returningNewAlias
+ * are the alias names for OLD and NEW, which may be user-supplied values,
+ * the defaults "old" and "new", or NULL (if the default "old"/"new" is
+ * already in use as the alias for some other relation).
+ */
+ char *returningOldAlias pg_node_attr(query_jumble_ignore);
+ char *returningNewAlias pg_node_attr(query_jumble_ignore);
List *returningList; /* return-values list (of TargetEntry) */
List *groupClause; /* a list of SortGroupClause's */
@@ -1727,6 +1736,41 @@ typedef struct MergeWhenClause
} MergeWhenClause;
/*
+ * ReturningOptionKind -
+ * Possible kinds of option in RETURNING WITH(...) list
+ *
+ * Currently, this is used only for specifying OLD/NEW aliases.
+ */
+typedef enum ReturningOptionKind
+{
+ RETURNING_OPTION_OLD, /* specify alias for OLD in RETURNING */
+ RETURNING_OPTION_NEW, /* specify alias for NEW in RETURNING */
+} ReturningOptionKind;
+
+/*
+ * ReturningOption -
+ * An individual option in the RETURNING WITH(...) list
+ */
+typedef struct ReturningOption
+{
+ NodeTag type;
+ ReturningOptionKind option; /* specified option */
+ char *value; /* option's value */
+ ParseLoc location; /* token location, or -1 if unknown */
+} ReturningOption;
+
+/*
+ * ReturningClause -
+ * List of RETURNING expressions, together with any WITH(...) options
+ */
+typedef struct ReturningClause
+{
+ NodeTag type;
+ List *options; /* list of ReturningOption elements */
+ List *exprs; /* list of expressions to return */
+} ReturningClause;
+
+/*
* TriggerTransition -
* representation of transition row or table naming clause
*
@@ -2043,7 +2087,7 @@ typedef struct InsertStmt
List *cols; /* optional: names of the target columns */
Node *selectStmt; /* the source SELECT/VALUES, or NULL */
OnConflictClause *onConflictClause; /* ON CONFLICT clause */
- List *returningList; /* list of expressions to return */
+ ReturningClause *returningClause; /* RETURNING clause */
WithClause *withClause; /* WITH clause */
OverridingKind override; /* OVERRIDING clause */
ParseLoc stmt_location; /* start location, or -1 if unknown */
@@ -2060,7 +2104,7 @@ typedef struct DeleteStmt
RangeVar *relation; /* relation to delete from */
List *usingClause; /* optional using clause for more tables */
Node *whereClause; /* qualifications */
- List *returningList; /* list of expressions to return */
+ ReturningClause *returningClause; /* RETURNING clause */
WithClause *withClause; /* WITH clause */
ParseLoc stmt_location; /* start location, or -1 if unknown */
ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */
@@ -2077,7 +2121,7 @@ typedef struct UpdateStmt
List *targetList; /* the target list (of ResTarget) */
Node *whereClause; /* qualifications */
List *fromClause; /* optional from clause for more tables */
- List *returningList; /* list of expressions to return */
+ ReturningClause *returningClause; /* RETURNING clause */
WithClause *withClause; /* WITH clause */
ParseLoc stmt_location; /* start location, or -1 if unknown */
ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */
@@ -2094,7 +2138,7 @@ typedef struct MergeStmt
Node *sourceRelation; /* source relation */
Node *joinCondition; /* join condition between source and target */
List *mergeWhenClauses; /* list of MergeWhenClause(es) */
- List *returningList; /* list of expressions to return */
+ ReturningClause *returningClause; /* RETURNING clause */
WithClause *withClause; /* WITH clause */
ParseLoc stmt_location; /* start location, or -1 if unknown */
ParseLoc stmt_len; /* length in bytes; 0 means "rest of string" */
diff --git a/src/include/nodes/plannodes.h b/src/include/nodes/plannodes.h
index ef9ea7ee982..9e19cdd284d 100644
--- a/src/include/nodes/plannodes.h
+++ b/src/include/nodes/plannodes.h
@@ -238,6 +238,8 @@ typedef struct ModifyTable
List *resultRelations; /* integer list of RT indexes */
List *updateColnosLists; /* per-target-table update_colnos lists */
List *withCheckOptionLists; /* per-target-table WCO lists */
+ char *returningOldAlias; /* alias for OLD in RETURNING lists */
+ char *returningNewAlias; /* alias for NEW in RETURNING lists */
List *returningLists; /* per-target-table RETURNING tlists */
List *fdwPrivLists; /* per-target-table FDW private data lists */
Bitmapset *fdwDirectModifyPlans; /* indices of FDW DM plans */
diff --git a/src/include/nodes/primnodes.h b/src/include/nodes/primnodes.h
index cb09df9e745..59e7bb26bbd 100644
--- a/src/include/nodes/primnodes.h
+++ b/src/include/nodes/primnodes.h
@@ -223,6 +223,11 @@ typedef struct Expr
* Note that it affects the meaning of all of varno, varnullingrels, and
* varnosyn, all of which refer to the range table of that query level.
*
+ * varreturningtype is used for Vars that refer to the target relation in the
+ * RETURNING list of data-modifying queries. The default behavior is to
+ * return old values for DELETE and new values for INSERT and UPDATE, but it
+ * is also possible to explicitly request old or new values.
+ *
* In the parser, varnosyn and varattnosyn are either identical to
* varno/varattno, or they specify the column's position in an aliased JOIN
* RTE that hides the semantic referent RTE's refname. This is a syntactic
@@ -244,6 +249,14 @@ typedef struct Expr
#define PRS2_OLD_VARNO 1
#define PRS2_NEW_VARNO 2
+/* Returning behavior for Vars in RETURNING list */
+typedef enum VarReturningType
+{
+ VAR_RETURNING_DEFAULT, /* return OLD for DELETE, else return NEW */
+ VAR_RETURNING_OLD, /* return OLD for DELETE/UPDATE, else NULL */
+ VAR_RETURNING_NEW, /* return NEW for INSERT/UPDATE, else NULL */
+} VarReturningType;
+
typedef struct Var
{
Expr xpr;
@@ -279,6 +292,9 @@ typedef struct Var
*/
Index varlevelsup;
+ /* returning type of this var (see above) */
+ VarReturningType varreturningtype;
+
/*
* varnosyn/varattnosyn are ignored for equality, because Vars with
* different syntactic identifiers are semantically the same as long as
@@ -2143,6 +2159,30 @@ typedef struct InferenceElem
Oid inferopclass; /* OID of att opclass, or InvalidOid */
} InferenceElem;
+/*
+ * ReturningExpr - return OLD/NEW.(expression) in RETURNING list
+ *
+ * This is used when updating an auto-updatable view and returning a view
+ * column that is not simply a Var referring to the base relation. In such
+ * cases, OLD/NEW.viewcol can expand to an arbitrary expression, but the
+ * result is required to be NULL if the OLD/NEW row doesn't exist. To handle
+ * this, the rewriter wraps the expanded expression in a ReturningExpr, which
+ * is equivalent to "CASE WHEN (OLD/NEW row exists) THEN (expr) ELSE NULL".
+ *
+ * A similar situation can arise when rewriting the RETURNING clause of a
+ * rule, which may also contain arbitrary expressions.
+ *
+ * ReturningExpr nodes never appear in a parsed Query --- they are only ever
+ * inserted by the rewriter.
+ */
+typedef struct ReturningExpr
+{
+ Expr xpr;
+ int retlevelsup; /* > 0 if it belongs to outer query */
+ bool retold; /* true for OLD, false for NEW */
+ Expr *retexpr; /* expression to be returned */
+} ReturningExpr;
+
/*--------------------
* TargetEntry -
* a target entry (used in query target lists)