v4-0001-Virtual-generated-columns_minorchange.no-cfbot

application/octet-stream

Filename: v4-0001-Virtual-generated-columns_minorchange.no-cfbot
Type: application/octet-stream
Part: 0
Message: Re: Virtual generated columns
From 8f0e74efdb4ae92f6b0a27d2446688461f7346f5 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 29 Aug 2024 21:22:06 +0800
Subject: [PATCH v4 1/1] Virtual generated columns

address the get_attgenerated overhead.

Discussion: https://www.postgresql.org/message-id/flat/a368248e-69e4-40be-9c07-6c3b5880b0a6@eisentraut.org
---
 src/backend/rewrite/rewriteHandler.c | 135 +++++++++++++++++++--------
 1 file changed, 98 insertions(+), 37 deletions(-)

diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index f2713eaef2..d4da0178f2 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -4380,6 +4380,12 @@ struct expand_generated_context
 
 	/* incremented for every level where it's true */
 	int			ancestor_has_virtual;
+
+	/* list of relation oids that have virtual generated column */
+	List		*virtual_gen_rel;
+
+	/* list of attnums that have virtual generated column */
+	List		*virtual_attnums;
 };
 
 static Node *
@@ -4393,6 +4399,9 @@ expand_generated_columns_mutator(Node *node, struct expand_generated_context *co
 		Var		   *v = (Var *) node;
 		Oid			relid;
 		AttrNumber	attnum;
+		ListCell   *lc,
+				   *lc2;
+		Oid			attcollid;
 		List	   *rtable = list_nth_node(List,
 										   context->rtables,
 										   list_length(context->rtables) - v->varlevelsup - 1);
@@ -4403,39 +4412,45 @@ expand_generated_columns_mutator(Node *node, struct expand_generated_context *co
 		if (!relid || !attnum)
 			return node;
 
-		if (get_attgenerated(relid, attnum) == ATTRIBUTE_GENERATED_VIRTUAL)
+		forboth(lc, context->virtual_gen_rel, lc2, context->virtual_attnums)
 		{
-			Relation	rt_entry_relation = table_open(relid, NoLock);
-			Oid			attcollid;
+			Oid	 virtual_rel = lfirst_oid(lc);
+			AttrNumber	attno = lfirst_int(lc2);
 
-			node = build_column_default(rt_entry_relation, attnum);
-			if (node == NULL)
-				elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
-					 attnum, RelationGetRelationName(rt_entry_relation));
-
-			/*
-			 * If the column definition has a collation and it is different
-			 * from the collation of the generation expression, put a COLLATE
-			 * clause around the expression.
-			 */
-			attcollid = GetSysCacheOid(ATTNUM, Anum_pg_attribute_attcollation, relid, attnum, 0, 0);
-			if (attcollid && attcollid != exprCollation(node))
+			if(attno == attnum && virtual_rel == relid)
 			{
-				CollateExpr *ce = makeNode(CollateExpr);
+				Relation	rt_entry_relation = table_open(relid, NoLock);
 
-				ce->arg = (Expr *) node;
-				ce->collOid = attcollid;
-				ce->location = -1;
+				Assert(get_attgenerated(relid, attnum) == ATTRIBUTE_GENERATED_VIRTUAL);
 
-				node = (Node *) ce;
+				node = build_column_default(rt_entry_relation, attnum);
+				if (node == NULL)
+					elog(ERROR, "no generation expression found for column number %d of table \"%s\"",
+						attnum, RelationGetRelationName(rt_entry_relation));
+
+				/*
+				* If the column definition has a collation and it is different
+				* from the collation of the generation expression, put a COLLATE
+				* clause around the expression.
+				*/
+				attcollid = GetSysCacheOid(ATTNUM, Anum_pg_attribute_attcollation, relid, attnum, 0, 0);
+				if (attcollid && attcollid != exprCollation(node))
+				{
+					CollateExpr *ce = makeNode(CollateExpr);
+
+					ce->arg = (Expr *) node;
+					ce->collOid = attcollid;
+					ce->location = -1;
+
+					node = (Node *) ce;
+				}
+
+				IncrementVarSublevelsUp(node, v->varlevelsup, 0);
+				ChangeVarNodes(node, 1, v->varno, v->varlevelsup);
+
+				table_close(rt_entry_relation, NoLock);
 			}
-
-			IncrementVarSublevelsUp(node, v->varlevelsup, 0);
-			ChangeVarNodes(node, 1, v->varno, v->varlevelsup);
-
-			table_close(rt_entry_relation, NoLock);
 		}
-
 		return node;
 	}
 	else if (IsA(node, Query))
@@ -4457,6 +4472,7 @@ expand_generated_columns_in_expr(Node *node, Relation rel)
 
 	if (tupdesc->constr && tupdesc->constr->has_generated_virtual)
 	{
+		int	i;
 		RangeTblEntry *rte;
 		List	   *rtable;
 		struct expand_generated_context context;
@@ -4470,6 +4486,23 @@ expand_generated_columns_in_expr(Node *node, Relation rel)
 		rte->relid = RelationGetRelid(rel);
 		rtable = list_make2(rte, rte);
 		context.rtables = list_make1(rtable);
+		context.virtual_gen_rel = NIL;
+		context.virtual_attnums = NIL;
+
+		for (i = 0; i < tupdesc->natts; i++)
+		{
+			Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+			if (att->attisdropped)
+				continue;
+
+			if (att->attgenerated == 'v')
+			{
+				context.virtual_gen_rel = lappend_oid(context.virtual_gen_rel, rte->relid);
+				context.virtual_attnums = lappend_int(context.virtual_attnums, att->attnum);
+			}
+		}
+		Assert(list_length(context.virtual_gen_rel) > 0);
+		Assert(list_length(context.virtual_gen_rel) == list_length(context.virtual_attnums));
 
 		return expression_tree_mutator(node, expand_generated_columns_mutator, &context);
 	}
@@ -4486,13 +4519,49 @@ expand_generated_columns_in_query(Query *query, struct expand_generated_context
 {
 	context->rtables = lappend(context->rtables, query->rtable);
 	if (query->hasGeneratedVirtual)
+	{
+		List		*rte_relids = NIL;
 		context->ancestor_has_virtual++;
+		context->ancestor_has_virtual++;
+
+		foreach_node(RangeTblEntry, rte, query->rtable)
+		{
+			if(OidIsValid(rte->relid))
+				rte_relids = list_append_unique_oid(rte_relids, rte->relid);
+		}
+
+		foreach_oid(relid, rte_relids)
+		{
+			int	i;
+			Relation	rt_entry_relation = table_open(relid, NoLock);
+			TupleDesc	tupdesc = RelationGetDescr(rt_entry_relation);
+
+			if (tupdesc->constr && tupdesc->constr->has_generated_virtual)
+			{
+				for (i = 0; i < tupdesc->natts; i++)
+				{
+					Form_pg_attribute att = TupleDescAttr(tupdesc, i);
+					if (att->attisdropped)
+						continue;
+
+					if (att->attgenerated == 'v')
+					{
+						context->virtual_gen_rel = lappend_oid(context->virtual_gen_rel, relid);
+						context->virtual_attnums = lappend_int(context->virtual_attnums, att->attnum);
+					}
+				}
+				Assert(list_length(context->virtual_gen_rel) > 0);
+				Assert(list_length(context->virtual_gen_rel) == list_length(context->virtual_attnums));
+			}
+			table_close(rt_entry_relation, NoLock);
+		}
 
+	}
 	/*
 	 * If any table in the query has a virtual column or there is a sublink,
 	 * then we need to do the whole walk.
 	 */
-	if (query->hasGeneratedVirtual || query->hasSubLinks || context->ancestor_has_virtual)
+	if (context->ancestor_has_virtual)
 	{
 		query = query_tree_mutator(query,
 								   expand_generated_columns_mutator,
@@ -4505,22 +4574,14 @@ expand_generated_columns_in_query(Query *query, struct expand_generated_context
 	 */
 	else
 	{
-		ListCell   *lc;
-
-		foreach(lc, query->rtable)
+		foreach_node(RangeTblEntry, rte, query->rtable)
 		{
-			RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
-
 			if (rte->rtekind == RTE_SUBQUERY)
 				rte->subquery = expand_generated_columns_in_query(rte->subquery, context);
 		}
 
-		foreach(lc, query->cteList)
-		{
-			CommonTableExpr *cte = (CommonTableExpr *) lfirst(lc);
-
+		foreach_node(CommonTableExpr, cte, query->cteList)
 			cte->ctequery = (Node *) expand_generated_columns_in_query(castNode(Query, cte->ctequery), context);
-		}
 	}
 
 	if (query->hasGeneratedVirtual)
-- 
2.34.1