v10-0001-refactor-ExecGetExtraUpdatedCols.no-cfbot

application/octet-stream

Filename: v10-0001-refactor-ExecGetExtraUpdatedCols.no-cfbot
Type: application/octet-stream
Part: 0
Message: Re: Virtual generated columns
From ea27806ed7ce524cff66d2f250e9625ae96bdc26 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Tue, 3 Dec 2024 17:04:55 +0800
Subject: [PATCH v10 1/1] refactor ExecGetExtraUpdatedCols

---
 src/backend/executor/execUtils.c       | 68 ++++++++++++++++++++++++--
 src/backend/executor/nodeModifyTable.c | 21 +++-----
 src/include/executor/nodeModifyTable.h |  2 +-
 src/include/nodes/execnodes.h          |  3 --
 4 files changed, 73 insertions(+), 21 deletions(-)

diff --git a/src/backend/executor/execUtils.c b/src/backend/executor/execUtils.c
index 5e1bac04ee..81afeabdf9 100644
--- a/src/backend/executor/execUtils.c
+++ b/src/backend/executor/execUtils.c
@@ -55,6 +55,8 @@
 #include "miscadmin.h"
 #include "parser/parse_relation.h"
 #include "partitioning/partdesc.h"
+#include "rewrite/rewriteHandler.h"
+#include "optimizer/optimizer.h"
 #include "storage/lmgr.h"
 #include "utils/builtins.h"
 #include "utils/memutils.h"
@@ -1306,13 +1308,73 @@ ExecGetUpdatedCols(ResultRelInfo *relinfo, EState *estate)
 	return perminfo->updatedCols;
 }
 
-/* Return a bitmap representing generated columns being updated */
+/* Return a bitmap representing (viirtual|stored) generated columns being
+ * updated first, through ExecInitStoredGenerated colect all stored generated
+ * column.  then collect the virtual one.
+*/
 Bitmapset *
 ExecGetExtraUpdatedCols(ResultRelInfo *relinfo, EState *estate)
 {
+	Relation	rel = relinfo->ri_RelationDesc;
+	TupleDesc	tupdesc = RelationGetDescr(rel);
+	int			natts = tupdesc->natts;
+	Bitmapset  *updatedCols;
+	MemoryContext oldContext;
+
 	/* Compute the info if we didn't already */
-	if (!relinfo->ri_Generated_valid)
-		ExecInitGenerated(relinfo, estate, CMD_UPDATE);
+	if (relinfo->ri_GeneratedExprsU == NULL)
+		ExecInitStoredGenerated(relinfo, estate, CMD_UPDATE);
+
+	/* Nothing else to do if no virtual generated columns */
+	if (!(tupdesc->constr && tupdesc->constr->has_generated_virtual))
+		return relinfo->ri_extraUpdatedCols;
+
+	/*
+	 * In an UPDATE, we can skip computing any generated columns that do not
+	 * depend on any UPDATE target column.  But if there is a BEFORE ROW
+	 * UPDATE trigger, we cannot skip because the trigger might change more
+	 * columns.
+	 */
+	if (!(rel->trigdesc && rel->trigdesc->trig_update_before_row))
+		updatedCols = ExecGetUpdatedCols(relinfo, estate);
+	else
+		updatedCols = NULL;
+
+	oldContext = MemoryContextSwitchTo(estate->es_query_cxt);
+	for (int i = 0; i < natts; i++)
+	{
+		char		attgenerated = TupleDescAttr(tupdesc, i)->attgenerated;
+
+		if (attgenerated == ATTRIBUTE_GENERATED_VIRTUAL)
+		{
+			Expr	   *expr;
+
+			/* Fetch the GENERATED AS expression tree */
+			expr = (Expr *) build_column_default(rel, i + 1);
+			if (expr == NULL)
+				elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
+					 i + 1, RelationGetRelationName(rel));
+
+			/*
+			 * If it's an update with a known set of update target columns,
+			 * see if we can skip the computation.
+			 */
+			if (updatedCols)
+			{
+				Bitmapset  *attrs_used = NULL;
+
+				pull_varattnos((Node *) expr, 1, &attrs_used);
+
+				if (!bms_overlap(updatedCols, attrs_used))
+					continue;	/* need not update this column */
+			}
+
+			relinfo->ri_extraUpdatedCols =
+				bms_add_member(relinfo->ri_extraUpdatedCols,
+								i + 1 - FirstLowInvalidHeapAttributeNumber);
+		}
+	}
+	MemoryContextSwitchTo(oldContext);
 	return relinfo->ri_extraUpdatedCols;
 }
 
diff --git a/src/backend/executor/nodeModifyTable.c b/src/backend/executor/nodeModifyTable.c
index fc488231f3..3a8dbe6dd7 100644
--- a/src/backend/executor/nodeModifyTable.c
+++ b/src/backend/executor/nodeModifyTable.c
@@ -349,7 +349,7 @@ ExecCheckTIDVisible(EState *estate,
  * UPDATE and INSERT actions.
  */
 void
-ExecInitGenerated(ResultRelInfo *resultRelInfo,
+ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
 				  EState *estate,
 				  CmdType cmdtype)
 {
@@ -362,7 +362,7 @@ ExecInitGenerated(ResultRelInfo *resultRelInfo,
 	MemoryContext oldContext;
 
 	/* Nothing to do if no generated columns */
-	if (!(tupdesc->constr && (tupdesc->constr->has_generated_stored || tupdesc->constr->has_generated_virtual)))
+	if (!(tupdesc->constr && tupdesc->constr->has_generated_stored))
 		return;
 
 	/*
@@ -388,9 +388,7 @@ ExecInitGenerated(ResultRelInfo *resultRelInfo,
 
 	for (int i = 0; i < natts; i++)
 	{
-		char		attgenerated = TupleDescAttr(tupdesc, i)->attgenerated;
-
-		if (attgenerated)
+		if (TupleDescAttr(tupdesc, i)->attgenerated == ATTRIBUTE_GENERATED_STORED)
 		{
 			Expr	   *expr;
 
@@ -415,11 +413,8 @@ ExecInitGenerated(ResultRelInfo *resultRelInfo,
 			}
 
 			/* No luck, so prepare the expression for execution */
-			if (attgenerated == ATTRIBUTE_GENERATED_STORED)
-			{
-				ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
-				ri_NumGeneratedNeeded++;
-			}
+			ri_GeneratedExprs[i] = ExecPrepareExpr(expr, estate);
+			ri_NumGeneratedNeeded++;
 
 			/* If UPDATE, mark column in resultRelInfo->ri_extraUpdatedCols */
 			if (cmdtype == CMD_UPDATE)
@@ -447,8 +442,6 @@ ExecInitGenerated(ResultRelInfo *resultRelInfo,
 		resultRelInfo->ri_NumGeneratedNeededI = ri_NumGeneratedNeeded;
 	}
 
-	resultRelInfo->ri_Generated_valid = true;
-
 	MemoryContextSwitchTo(oldContext);
 }
 
@@ -479,7 +472,7 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
 	if (cmdtype == CMD_UPDATE)
 	{
 		if (resultRelInfo->ri_GeneratedExprsU == NULL)
-			ExecInitGenerated(resultRelInfo, estate, cmdtype);
+			ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
 		if (resultRelInfo->ri_NumGeneratedNeededU == 0)
 			return;
 		ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsU;
@@ -487,7 +480,7 @@ ExecComputeStoredGenerated(ResultRelInfo *resultRelInfo,
 	else
 	{
 		if (resultRelInfo->ri_GeneratedExprsI == NULL)
-			ExecInitGenerated(resultRelInfo, estate, cmdtype);
+			ExecInitStoredGenerated(resultRelInfo, estate, cmdtype);
 		/* Early exit is impossible given the prior Assert */
 		Assert(resultRelInfo->ri_NumGeneratedNeededI > 0);
 		ri_GeneratedExprs = resultRelInfo->ri_GeneratedExprsI;
diff --git a/src/include/executor/nodeModifyTable.h b/src/include/executor/nodeModifyTable.h
index 0d8a5f6613..2e6a2bab20 100644
--- a/src/include/executor/nodeModifyTable.h
+++ b/src/include/executor/nodeModifyTable.h
@@ -15,7 +15,7 @@
 
 #include "nodes/execnodes.h"
 
-extern void ExecInitGenerated(ResultRelInfo *resultRelInfo,
+extern void ExecInitStoredGenerated(ResultRelInfo *resultRelInfo,
 							  EState *estate,
 							  CmdType cmdtype);
 
diff --git a/src/include/nodes/execnodes.h b/src/include/nodes/execnodes.h
index 5acf3c29de..182a6956bb 100644
--- a/src/include/nodes/execnodes.h
+++ b/src/include/nodes/execnodes.h
@@ -540,9 +540,6 @@ typedef struct ResultRelInfo
 	int			ri_NumGeneratedNeededI;
 	int			ri_NumGeneratedNeededU;
 
-	/* true if the above have been computed */
-	bool		ri_Generated_valid;
-
 	/* list of RETURNING expressions */
 	List	   *ri_returningList;
 
-- 
2.34.1