From 30ae89e0a459b2808b05107f5fbc238704c992f1 Mon Sep 17 00:00:00 2001
From: Alexander Korotkov <akorotkov@postgresql.org>
Date: Sat, 29 Mar 2025 09:46:33 +0200
Subject: [PATCH v8 1/2] Extract make_SAOP_expr() function from
 match_orclause_to_indexcol()

This commit extracts the code to generate ScalarArrayOpExpr on top of the list
of expressions from match_orclause_to_indexcol() into a separate function
make_SAOP_expr().  This function was extracted to be used in optimization for
conversion of 'x IN (VALUES ...)' to 'x = ANY ...'.  make_SAOP_expr() is
placed in clauses.c file as only two additional headers were needed there
compared with other places.

Discussion: https://postgr.es/m/0184212d-1248-4f1f-a42d-f5cb1c1976d2%40tantorlabs.com
Author: Alena Rybakina <a.rybakina@postgrespro.ru>
Author: Andrei Lepikhov <lepihov@gmail.com>
Reviewed-by: Ivan Kush <ivan.kush@tantorlabs.com>
Reviewed-by: Alexander Korotkov <aekorotkov@gmail.com>
---
 src/backend/optimizer/path/indxpath.c | 62 +-------------------
 src/backend/optimizer/util/clauses.c  | 81 +++++++++++++++++++++++++++
 src/include/optimizer/optimizer.h     |  5 ++
 3 files changed, 88 insertions(+), 60 deletions(-)

diff --git a/src/backend/optimizer/path/indxpath.c b/src/backend/optimizer/path/indxpath.c
index 6386ce82253..8bb4c401f84 100644
--- a/src/backend/optimizer/path/indxpath.c
+++ b/src/backend/optimizer/path/indxpath.c
@@ -33,10 +33,8 @@
 #include "optimizer/paths.h"
 #include "optimizer/prep.h"
 #include "optimizer/restrictinfo.h"
-#include "utils/array.h"
 #include "utils/lsyscache.h"
 #include "utils/selfuncs.h"
-#include "utils/syscache.h"
 
 
 /* XXX see PartCollMatchesExprColl */
@@ -3304,7 +3302,6 @@ match_orclause_to_indexcol(PlannerInfo *root,
 	BoolExpr   *orclause = (BoolExpr *) rinfo->orclause;
 	Node	   *indexExpr = NULL;
 	List	   *consts = NIL;
-	Node	   *arrayNode = NULL;
 	ScalarArrayOpExpr *saopexpr = NULL;
 	Oid			matchOpno = InvalidOid;
 	IndexClause *iclause;
@@ -3475,63 +3472,8 @@ match_orclause_to_indexcol(PlannerInfo *root,
 		return NULL;
 	}
 
-	/*
-	 * Assemble an array from the list of constants.  It seems more profitable
-	 * to build a const array.  But in the presence of other nodes, we don't
-	 * have a specific value here and must employ an ArrayExpr instead.
-	 */
-	if (haveNonConst)
-	{
-		ArrayExpr  *arrayExpr = makeNode(ArrayExpr);
-
-		/* array_collid will be set by parse_collate.c */
-		arrayExpr->element_typeid = consttype;
-		arrayExpr->array_typeid = arraytype;
-		arrayExpr->multidims = false;
-		arrayExpr->elements = consts;
-		arrayExpr->location = -1;
-
-		arrayNode = (Node *) arrayExpr;
-	}
-	else
-	{
-		int16		typlen;
-		bool		typbyval;
-		char		typalign;
-		Datum	   *elems;
-		int			i = 0;
-		ArrayType  *arrayConst;
-
-		get_typlenbyvalalign(consttype, &typlen, &typbyval, &typalign);
-
-		elems = (Datum *) palloc(sizeof(Datum) * list_length(consts));
-		foreach_node(Const, value, consts)
-		{
-			Assert(!value->constisnull);
-
-			elems[i++] = value->constvalue;
-		}
-
-		arrayConst = construct_array(elems, i, consttype,
-									 typlen, typbyval, typalign);
-		arrayNode = (Node *) makeConst(arraytype, -1, inputcollid,
-									   -1, PointerGetDatum(arrayConst),
-									   false, false);
-
-		pfree(elems);
-		list_free(consts);
-	}
-
-	/* Build the SAOP expression node */
-	saopexpr = makeNode(ScalarArrayOpExpr);
-	saopexpr->opno = matchOpno;
-	saopexpr->opfuncid = get_opcode(matchOpno);
-	saopexpr->hashfuncid = InvalidOid;
-	saopexpr->negfuncid = InvalidOid;
-	saopexpr->useOr = true;
-	saopexpr->inputcollid = inputcollid;
-	saopexpr->args = list_make2(indexExpr, arrayNode);
-	saopexpr->location = -1;
+	saopexpr = make_SAOP_expr(matchOpno, indexExpr, consttype, inputcollid,
+							  inputcollid, consts, haveNonConst);
 
 	/*
 	 * Finally, build an IndexClause based on the SAOP node.  Use
diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c
index 43dfecfb47f..92f347d0d24 100644
--- a/src/backend/optimizer/util/clauses.c
+++ b/src/backend/optimizer/util/clauses.c
@@ -40,7 +40,9 @@
 #include "optimizer/planmain.h"
 #include "parser/analyze.h"
 #include "parser/parse_coerce.h"
+#include "parser/parse_collate.h"
 #include "parser/parse_func.h"
+#include "parser/parse_oper.h"
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
 #include "tcop/tcopprot.h"
@@ -5439,3 +5441,82 @@ pull_paramids_walker(Node *node, Bitmapset **context)
 	}
 	return expression_tree_walker(node, pull_paramids_walker, context);
 }
+
+/*
+ * Build ScalarArrayOpExpr on top of 'exprs.' 'haveNonConst' indicates
+ * whether at least one of the expressions is not Const.  When it's false,
+ * the array constant is built directly; otherwise, we have to build a child
+ * ArrayExpr. The 'exprs' list gets freed if not directly used in the output
+ * expression tree.
+ */
+ScalarArrayOpExpr *
+make_SAOP_expr(Oid oper, Node *leftexpr, Oid coltype, Oid arraycollid,
+			   Oid inputcollid, List *exprs, bool haveNonConst)
+{
+	Node	   *arrayNode = NULL;
+	ScalarArrayOpExpr *saopexpr = NULL;
+	Oid			arraytype = get_array_type(coltype);
+
+	if (!OidIsValid(arraytype))
+		return NULL;
+
+	/*
+	 * Assemble an array from the list of constants.  It seems more profitable
+	 * to build a const array.  But in the presence of other nodes, we don't
+	 * have a specific value here and must employ an ArrayExpr instead.
+	 */
+	if (haveNonConst)
+	{
+		ArrayExpr  *arrayExpr = makeNode(ArrayExpr);
+
+		/* array_collid will be set by parse_collate.c */
+		arrayExpr->element_typeid = coltype;
+		arrayExpr->array_typeid = arraytype;
+		arrayExpr->multidims = false;
+		arrayExpr->elements = exprs;
+		arrayExpr->location = -1;
+
+		arrayNode = (Node *) arrayExpr;
+	}
+	else
+	{
+		int16		typlen;
+		bool		typbyval;
+		char		typalign;
+		Datum	   *elems;
+		int			i = 0;
+		ArrayType  *arrayConst;
+
+		get_typlenbyvalalign(coltype, &typlen, &typbyval, &typalign);
+
+		elems = (Datum *) palloc(sizeof(Datum) * list_length(exprs));
+		foreach_node(Const, value, exprs)
+		{
+			Assert(!value->constisnull);
+
+			elems[i++] = value->constvalue;
+		}
+
+		arrayConst = construct_array(elems, i, coltype,
+									 typlen, typbyval, typalign);
+		arrayNode = (Node *) makeConst(arraytype, -1, arraycollid,
+									   -1, PointerGetDatum(arrayConst),
+									   false, false);
+
+		pfree(elems);
+		list_free(exprs);
+	}
+
+	/* Build the SAOP expression node */
+	saopexpr = makeNode(ScalarArrayOpExpr);
+	saopexpr->opno = oper;
+	saopexpr->opfuncid = get_opcode(oper);
+	saopexpr->hashfuncid = InvalidOid;
+	saopexpr->negfuncid = InvalidOid;
+	saopexpr->useOr = true;
+	saopexpr->inputcollid = inputcollid;
+	saopexpr->args = list_make2(leftexpr, arrayNode);
+	saopexpr->location = -1;
+
+	return saopexpr;
+}
diff --git a/src/include/optimizer/optimizer.h b/src/include/optimizer/optimizer.h
index 78e05d88c8e..546828b54bd 100644
--- a/src/include/optimizer/optimizer.h
+++ b/src/include/optimizer/optimizer.h
@@ -158,6 +158,11 @@ extern List *expand_function_arguments(List *args, bool include_out_arguments,
 									   Oid result_type,
 									   struct HeapTupleData *func_tuple);
 
+extern ScalarArrayOpExpr *make_SAOP_expr(Oid oper, Node *leftexpr,
+										 Oid coltype, Oid arraycollid,
+										 Oid inputcollid, List *exprs,
+										 bool haveNonConst);
+
 /* in util/predtest.c: */
 
 extern bool predicate_implied_by(List *predicate_list, List *clause_list,
-- 
2.39.5 (Apple Git-154)

