From a87da89dea951c78b62e796eb13e4f75f171a6c8 Mon Sep 17 00:00:00 2001
From: Euler Taveira <euler@eulerto.com>
Date: Mon, 15 Dec 2025 11:11:43 -0300
Subject: [PATCH v6 2/2] fixup! create table like incluing parameters

---
 src/backend/access/common/reloptions.c        | 40 ++-------------
 src/backend/parser/parse_utilcmd.c            | 33 ++++++-------
 src/include/access/reloptions.h               |  2 +-
 .../regress/expected/create_table_like.out    | 49 +++++++++----------
 src/test/regress/sql/create_table_like.sql    | 36 +++++++-------
 5 files changed, 60 insertions(+), 100 deletions(-)

diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 9289caef7ba..93db86acad3 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1359,48 +1359,14 @@ transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace,
 List *
 untransformRelOptions(Datum options)
 {
-	List	   *result = NIL;
-	ArrayType  *array;
-	Datum	   *optiondatums;
-	int			noptions;
-	int			i;
-
-	/* Nothing to do if no options */
-	if (DatumGetPointer(options) == NULL)
-		return result;
-
-	array = DatumGetArrayTypeP(options);
-
-	deconstruct_array_builtin(array, TEXTOID, &optiondatums, NULL, &noptions);
-
-	for (i = 0; i < noptions; i++)
-	{
-		char	   *s;
-		char	   *p;
-		Node	   *val = NULL;
-
-		s = TextDatumGetCString(optiondatums[i]);
-		p = strchr(s, '=');
-		if (p)
-		{
-			*p++ = '\0';
-			val = (Node *) makeString(p);
-		}
-		result = lappend(result, makeDefElem(s, val, -1));
-	}
-
-	return result;
+	return untransformRelOptionsExtended(options, NULL);
 }
 
 /*
- * Convert the reloptions from text-array format into a List of DefElem.  This
- * is the reverse operation of transformRelOptions().
- *
- * If any option includes a namespace qualification, create the DefElem in that
- * namespace, otherwise this behave the same as untransformRelOptions.
+ * Same as untransformRelOptions() but includes a namespace into DefElem.
  */
 List *
-untransformRelOptionsExtended(Datum options, char* nameSpace)
+untransformRelOptionsExtended(Datum options, char *nameSpace)
 {
 	List	   *result = NIL;
 	ArrayType  *array;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 2e77c2db19b..c9056c4f22e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -88,7 +88,8 @@ typedef struct
 	List	   *fkconstraints;	/* FOREIGN KEY constraints */
 	List	   *ixconstraints;	/* index-creating constraints */
 	List	   *likeclauses;	/* LIKE clauses that need post-processing */
-	List	   *options;		/* options from WITH clause, table AM specific parameters */
+	List	   *options;		/* options from WITH clause, table AM specific
+								 * parameters */
 	List	   *blist;			/* "before list" of things to do before
 								 * creating the table */
 	List	   *alist;			/* "after list" of things to do after creating
@@ -1125,8 +1126,8 @@ transformTableConstraint(CreateStmtContext *cxt, Constraint *constraint)
  * table has been created.
  *
  * Some options are ignored.  For example, as foreign tables have no storage,
- * these INCLUDING options have no effect: STORAGE, COMPRESSION, IDENTITY
- * and INDEXES.  Similarly, INCLUDING INDEXES is ignored from a view.
+ * these INCLUDING options have no effect: STORAGE, COMPRESSION, IDENTITY,
+ * INDEXES and PARAMETERS.  Similarly, INCLUDING INDEXES is ignored from a view.
  */
 static void
 transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_clause)
@@ -1274,17 +1275,16 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
 		}
 	}
 
-	/* Likewise, copy stoarge paramters if requested */
+	/* Likewise, copy storage parameters if requested */
 	if ((table_like_clause->options & CREATE_TABLE_LIKE_PARAMETERS) &&
 		!cxt->isforeign)
 	{
 		HeapTuple	tuple;
-		HeapTuple	toast_tuple;
 		Datum		reloptions;
 		bool		isnull;
 		Oid			relid;
-		List		*oldoptions = NIL;
-		List		*oldtoastoptions = NIL;
+		List	   *oldoptions = NIL;
+		List	   *oldtoastoptions = NIL;
 
 		relid = RelationGetRelid(relation);
 		tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
@@ -1308,7 +1308,6 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
 		if (OidIsValid(relation->rd_rel->reltoastrelid))
 		{
 			Relation	toastrel;
-
 			Oid			toastid = relation->rd_rel->reltoastrelid;
 
 			/*
@@ -1317,14 +1316,12 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
 			 */
 			toastrel = table_open(toastid, AccessShareLock);
 
-			/* Fetch heap tuple */
-			toast_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
+			tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
 
-			if (!HeapTupleIsValid(toast_tuple))
-				elog(ERROR, "cache lookup failed for toast relation %u", toastid);
+			if (!HeapTupleIsValid(tuple))
+				elog(ERROR, "cache lookup failed for relation %u", toastid);
 
-			/* Get the toast reloptions */
-			reloptions = SysCacheGetAttr(RELOID, toast_tuple, Anum_pg_class_reloptions,
+			reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
 										 &isnull);
 			if (!isnull)
 			{
@@ -1334,12 +1331,12 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
 					cxt->options = lappend(cxt->options, option);
 			}
 
-			ReleaseSysCache(toast_tuple);
+			ReleaseSysCache(tuple);
 
 			/*
-			 * Close the parent TOAST rel, but keep our AccessShareLock on it
-			 * until xact commit.  That will prevent someone else from deleting
-			 * or ALTERing the parent TOAST before we can run
+			 * Close the toast relation, but keep our AccessShareLock on it
+			 * until xact commit.  That will prevent someone else from
+			 * deleting or ALTERing the parent TOAST before we can run
 			 * expandTableLikeClause.
 			 */
 			table_close(toastrel, NoLock);
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index 8589616d08b..0c29f5df229 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -236,7 +236,7 @@ extern Datum transformRelOptions(Datum oldOptions, List *defList,
 								 const char *nameSpace, const char *const validnsps[],
 								 bool acceptOidsOff, bool isReset);
 extern List *untransformRelOptions(Datum options);
-extern List *untransformRelOptionsExtended(Datum options, char* nameSpace);
+extern List *untransformRelOptionsExtended(Datum options, char *nameSpace);
 extern bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
 								amoptions_function amoptions);
 extern void *build_reloptions(Datum reloptions, bool validate,
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index 3eb079e24ed..89145882aef 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -700,18 +700,18 @@ DROP FOREIGN TABLE ctl_foreign_table2;
 DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
 NOTICE:  drop cascades to server ctl_s0
 --CREATE TABLE LIKE with PARAMETERS
-create table t_sp(a text) with (
-    fillfactor = 100,
-    toast_tuple_target = 128,
-    vacuum_index_cleanup = auto,
-    toast.vacuum_index_cleanup = auto,
-    vacuum_truncate = true,
-    toast.vacuum_truncate = false,
-    log_autovacuum_min_duration = 100,
-    toast.log_autovacuum_min_duration = 100);
-create table t_sp1(like t_sp including parameters) with (fillfactor = 100); --error
+CREATE TABLE t_sp (a text) WITH (
+  fillfactor = 100,
+  toast_tuple_target = 128,
+  vacuum_index_cleanup = auto,
+  toast.vacuum_index_cleanup = auto,
+  vacuum_truncate = true,
+  toast.vacuum_truncate = false,
+  log_autovacuum_min_duration = 100,
+  toast.log_autovacuum_min_duration = 100);
+CREATE TABLE t_sp1 (LIKE t_sp INCLUDING PARAMETERS) WITH (fillfactor = 100); -- fail
 ERROR:  parameter "fillfactor" specified more than once
-create table t_sp1(like t_sp excluding parameters) with (fillfactor = 100); --ok
+CREATE TABLE T_sp1 (LIKE t_sp EXCLUDING PARAMETERS) WITH (fillfactor = 100); -- ok
 \d+ t_sp1
                                   Table "public.t_sp1"
  Column | Type | Collation | Nullable | Default | Storage  | Stats target | Description 
@@ -719,22 +719,19 @@ create table t_sp1(like t_sp excluding parameters) with (fillfactor = 100); --ok
  a      | text |           |          |         | extended |              | 
 Options: fillfactor=100
 
-create table t_sp2(like t_sp including parameters) with (
+CREATE TABLE t_sp2 (LIKE t_sp INCLUDING PARAMETERS) WITH (
   parallel_workers = 3,
   toast.autovacuum_vacuum_threshold = 101,
   toast.autovacuum_vacuum_scale_factor = 0.3);
-select    c.relname, c.reloptions, tc.reloptions as toast_options
-from      pg_catalog.pg_class c
-left join pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)
-where     c.relname in ('t_sp1', 't_sp2')
-order by  c.relname \gx
--[ RECORD 1 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
-relname       | t_sp1
-reloptions    | {fillfactor=100}
-toast_options | 
--[ RECORD 2 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
-relname       | t_sp2
-reloptions    | {parallel_workers=3,fillfactor=100,toast_tuple_target=128,vacuum_index_cleanup=auto,vacuum_truncate=true,log_autovacuum_min_duration=100}
-toast_options | {autovacuum_vacuum_threshold=101,autovacuum_vacuum_scale_factor=0.3,vacuum_index_cleanup=auto,vacuum_truncate=false,log_autovacuum_min_duration=100}
+SELECT c.relname, c.reloptions, t.reloptions as toast_reloptions
+  FROM pg_catalog.pg_class c
+  LEFT JOIN pg_catalog.pg_class t ON (c.reltoastrelid = t.oid)
+  WHERE c.relname IN ('t_sp1', 't_sp2')
+  ORDER BY c.relname;
+ relname |                                                                reloptions                                                                 |                                                                   toast_reloptions                                                                   
+---------+-------------------------------------------------------------------------------------------------------------------------------------------+------------------------------------------------------------------------------------------------------------------------------------------------------
+ t_sp1   | {fillfactor=100}                                                                                                                          | 
+ t_sp2   | {parallel_workers=3,fillfactor=100,toast_tuple_target=128,vacuum_index_cleanup=auto,vacuum_truncate=true,log_autovacuum_min_duration=100} | {autovacuum_vacuum_threshold=101,autovacuum_vacuum_scale_factor=0.3,vacuum_index_cleanup=auto,vacuum_truncate=false,log_autovacuum_min_duration=100}
+(2 rows)
 
-drop table t_sp, t_sp1, t_sp2;
+DROP TABLE t_sp, t_sp1, t_sp2;
diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql
index afc8ffe6ad5..c357c2dec0d 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -282,29 +282,29 @@ DROP FOREIGN TABLE ctl_foreign_table2;
 DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
 
 --CREATE TABLE LIKE with PARAMETERS
-create table t_sp(a text) with (
-    fillfactor = 100,
-    toast_tuple_target = 128,
-    vacuum_index_cleanup = auto,
-    toast.vacuum_index_cleanup = auto,
-    vacuum_truncate = true,
-    toast.vacuum_truncate = false,
-    log_autovacuum_min_duration = 100,
-    toast.log_autovacuum_min_duration = 100);
+CREATE TABLE t_sp (a text) WITH (
+  fillfactor = 100,
+  toast_tuple_target = 128,
+  vacuum_index_cleanup = auto,
+  toast.vacuum_index_cleanup = auto,
+  vacuum_truncate = true,
+  toast.vacuum_truncate = false,
+  log_autovacuum_min_duration = 100,
+  toast.log_autovacuum_min_duration = 100);
 
-create table t_sp1(like t_sp including parameters) with (fillfactor = 100); --error
-create table t_sp1(like t_sp excluding parameters) with (fillfactor = 100); --ok
+CREATE TABLE t_sp1 (LIKE t_sp INCLUDING PARAMETERS) WITH (fillfactor = 100); -- fail
+CREATE TABLE T_sp1 (LIKE t_sp EXCLUDING PARAMETERS) WITH (fillfactor = 100); -- ok
 \d+ t_sp1
 
-create table t_sp2(like t_sp including parameters) with (
+CREATE TABLE t_sp2 (LIKE t_sp INCLUDING PARAMETERS) WITH (
   parallel_workers = 3,
   toast.autovacuum_vacuum_threshold = 101,
   toast.autovacuum_vacuum_scale_factor = 0.3);
 
-select    c.relname, c.reloptions, tc.reloptions as toast_options
-from      pg_catalog.pg_class c
-left join pg_catalog.pg_class tc ON (c.reltoastrelid = tc.oid)
-where     c.relname in ('t_sp1', 't_sp2')
-order by  c.relname \gx
+SELECT c.relname, c.reloptions, t.reloptions as toast_reloptions
+  FROM pg_catalog.pg_class c
+  LEFT JOIN pg_catalog.pg_class t ON (c.reltoastrelid = t.oid)
+  WHERE c.relname IN ('t_sp1', 't_sp2')
+  ORDER BY c.relname;
 
-drop table t_sp, t_sp1, t_sp2;
+DROP TABLE t_sp, t_sp1, t_sp2;
-- 
2.39.5

