create table like including storage parameter
hi.
attached patch is to make CREATE TABLE LIKE copy source relation
storage parameter
demo:
create table t(a text) with (fillfactor = 100, toast.vacuum_truncate=true);
create table t2(like t including storage parameter) with (parallel_workers = 3);
Table "public.t2"
Column | Type | Collation | Nullable | Default | Storage |
Compression | Stats target | Description
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
a | text | | | | extended |
| |
Access method: heap
Options: parallel_workers=3, fillfactor=100, toast.vacuum_truncate=true
of course, duplicate storage mention would result error,
for example:
create table t3(like t including storage parameter) with (fillfactor = 100);
Since we already support INCLUDING STORAGE, I’m not sure that
INCLUDING STORAGE PARAMETER is the right syntax to go with.
Attachments:
v1-0001-create-table-like-incluing-storage-parameter.patchtext/x-patch; charset=US-ASCII; name=v1-0001-create-table-like-incluing-storage-parameter.patchDownload
From a497b0309772b7c202d54b65f9c96b655ca40179 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sun, 28 Sep 2025 19:20:00 +0800
Subject: [PATCH v1 1/1] create table like incluing storage parameter
demo:
create table t(a text) with (fillfactor = 100, toast.vacuum_truncate=true);
create table t2(like t including storage parameter);
Table "public.t2"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
a | text | | | | extended | | |
Access method: heap
Options: fillfactor=100, toast.vacuum_truncate=true
discussion: https://postgr.es/m/
---
doc/src/sgml/ref/create_table.sgml | 19 +++++-
src/backend/access/common/reloptions.c | 49 +++++++++++++++
src/backend/parser/gram.y | 1 +
src/backend/parser/parse_utilcmd.c | 63 +++++++++++++++++++
src/include/access/reloptions.h | 1 +
src/include/nodes/parsenodes.h | 1 +
.../regress/expected/create_table_like.out | 39 ++++++++++++
src/test/regress/sql/create_table_like.sql | 28 +++++++++
8 files changed, 200 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index dc000e913c1..a389ce597a9 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -88,7 +88,7 @@ class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable cl
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
-{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | STORAGE PARAMETER | ALL }
<phrase>and <replaceable class="parameter">partition_bound_spec</replaceable> is:</phrase>
@@ -776,6 +776,23 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-parms-like-opt-storage-paramter">
+ <term><literal>INCLUDING STORAGE PARAMETER</literal></term>
+ <listitem>
+ <para>
+ <literal>STORAGE PARAMETER</literal> settings for the copied table will be copied.
+ For table storage parameters, see <xref linkend="sql-createtable-storage-parameters"/> below for more information.
+ </para>
+
+ <para>
+ Do not confuse this with <literal>INCLUDING STORAGE</literal>.
+ <literal>INCLUDING STORAGE</literal> is for copying table column storage property,
+ this is for copying table storage parameters.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-parms-like-opt-all">
<term><literal>INCLUDING ALL</literal></term>
<listitem>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 0af3fea68fa..745a71d5192 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1383,6 +1383,55 @@ untransformRelOptions(Datum options)
return result;
}
+/*
+ * 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.
+ * If nameSpace is NULL, this behave the same as untransformRelOptions.
+ */
+List *
+untransformRelOptionsExtended(Datum options, char* nameSpace)
+{
+ List *result = NIL;
+ ArrayType *array;
+ Datum *optiondatums;
+ int noptions;
+ int i;
+
+ /* Nothing to do if no options */
+ if (!PointerIsValid(DatumGetPointer(options)))
+ 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);
+ }
+
+ if (nameSpace == NULL)
+ result = lappend(result, makeDefElem(s, val, -1));
+ else
+ result = lappend(result, makeDefElemExtended(nameSpace, s, val,
+ DEFELEM_UNSPEC,
+ -1));
+ }
+
+ return result;
+}
+
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 9fd48acb1f8..baa9108ac43 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4216,6 +4216,7 @@ TableLikeOption:
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
+ | STORAGE PARAMETER { $$ = CREATE_TABLE_LIKE_STORAGE_PARAMETER; }
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e96b38a59d5..bd401cba5e4 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -85,6 +85,7 @@ 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 *blist; /* "before list" of things to do before
* creating the table */
List *alist; /* "after list" of things to do after creating
@@ -245,6 +246,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = stmt->options;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
@@ -369,6 +371,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
stmt->nnconstraints = cxt.nnconstraints;
+ stmt->options = cxt.options;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -1268,6 +1271,65 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
}
}
+ /* Likewise, copy storage paramter if requested */
+ if ((table_like_clause->options & CREATE_TABLE_LIKE_STORAGE_PARAMETER) &&
+ !cxt->isforeign)
+ {
+ HeapTuple tuple;
+ Datum reloptions;
+ bool isnull;
+ Oid relid;
+ List *oldoptions = NIL;
+ List *oldtoastoptions = NIL;
+
+ relid = RelationGetRelid(relation);
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ reloptions = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_reloptions, &isnull);
+
+ if (!isnull)
+ {
+ oldoptions = untransformRelOptions(reloptions);
+
+ foreach_node(DefElem, option, oldoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ /* now get the source relation toast relation reloptions */
+ if (OidIsValid(relation->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ Oid toastid = relation->rd_rel->reltoastrelid;
+
+ toastrel = table_open(toastid, AccessShareLock);
+
+ /* Fetch heap tuple */
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for toast relation %u", toastid);
+
+ /* Get the toast reloptions */
+ reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
+ &isnull);
+ if (!isnull)
+ {
+ oldtoastoptions = untransformRelOptionsExtended(reloptions, "toast");
+ foreach_node(DefElem, option, oldtoastoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ table_close(toastrel, NoLock);
+ }
+ }
+
/*
* Reproduce not-null constraints, if any, by copying them. We do this
* regardless of options given.
@@ -3575,6 +3637,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a604a4702c3..8589616d08b 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -236,6 +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 bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern void *build_reloptions(Datum reloptions, bool validate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 86a236bd58b..97dbb70dfc0 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -793,6 +793,7 @@ typedef enum TableLikeOption
CREATE_TABLE_LIKE_INDEXES = 1 << 6,
CREATE_TABLE_LIKE_STATISTICS = 1 << 7,
CREATE_TABLE_LIKE_STORAGE = 1 << 8,
+ CREATE_TABLE_LIKE_STORAGE_PARAMETER = 1 << 9,
CREATE_TABLE_LIKE_ALL = PG_INT32_MAX
} TableLikeOption;
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index d3c35c14847..193771f3408 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -699,3 +699,42 @@ DROP FOREIGN TABLE ctl_foreign_table1;
DROP FOREIGN TABLE ctl_foreign_table2;
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
NOTICE: drop cascades to server ctl_s0
+--CREATE TABLE LIKE with STORAGE PARAMETER
+create table t(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 t1(like t including storage parameter) with (fillfactor = 100); --error
+ERROR: parameter "fillfactor" specified more than once
+create table t1(like t excluding storage parameter) with (fillfactor = 100); --ok
+\d+ t1
+ Table "public.t1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+------+-----------+----------+---------+----------+--------------+-------------
+ a | text | | | | extended | |
+Options: fillfactor=100
+
+create table t2(like t including storage parameter) 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', 't2')
+order by c.relname \gx
+-[ RECORD 1 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t
+reloptions | {fillfactor=100,toast_tuple_target=128,vacuum_index_cleanup=auto,vacuum_truncate=true,log_autovacuum_min_duration=100}
+toast_options | {vacuum_index_cleanup=auto,vacuum_truncate=false,log_autovacuum_min_duration=100}
+-[ RECORD 2 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t2
+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}
+
+drop table t, t1, t2;
diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql
index 93389b57dbf..47f67d244e2 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -280,3 +280,31 @@ DROP TABLE ctl_table;
DROP FOREIGN TABLE ctl_foreign_table1;
DROP FOREIGN TABLE ctl_foreign_table2;
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
+
+--CREATE TABLE LIKE with STORAGE PARAMETER
+create table t(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 t1(like t including storage parameter) with (fillfactor = 100); --error
+create table t1(like t excluding storage parameter) with (fillfactor = 100); --ok
+\d+ t1
+
+create table t2(like t including storage parameter) 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', 't2')
+order by c.relname \gx
+
+drop table t, t1, t2;
--
2.34.1
On Sunday, September 28, 2025, jian he <jian.universality@gmail.com> wrote:
Since we already support INCLUDING STORAGE, I’m not sure that
INCLUDING STORAGE PARAMETER is the right syntax to go with.
I’d just call it “including parameters”; sure, all of the existing ones are
storage related, but if there were non-storage ones we’d include those as
well, so qualifying with a parameter type doesn’t seem correct.
David J.
On Mon, Sep 29, 2025 at 9:28 AM David G. Johnston
<david.g.johnston@gmail.com> wrote:
On Sunday, September 28, 2025, jian he <jian.universality@gmail.com> wrote:
Since we already support INCLUDING STORAGE, I’m not sure that
INCLUDING STORAGE PARAMETER is the right syntax to go with.I’d just call it “including parameters”; sure, all of the existing ones are storage related, but if there were non-storage ones we’d include those as well, so qualifying with a parameter type doesn’t seem correct.
“including parameters” would add another keyword "PARAMETERS''.
currently "INCLUDING STORAGE PARAMETER" no need to add a new keyword.
If other people favor “including parameters”, then we can do it that
way in the future.
attached is just a simple rebase of v1.
Attachments:
v2-0001-create-table-like-incluing-storage-parameter.patchapplication/x-patch; name=v2-0001-create-table-like-incluing-storage-parameter.patchDownload
From e3f87ac6080a229e33aa631d1adb9c24c3dfcf79 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Wed, 1 Oct 2025 14:29:19 +0800
Subject: [PATCH v2 1/1] create table like incluing storage parameter
demo:
create table t(a text) with (fillfactor = 100, toast.vacuum_truncate=true);
create table t2(like t including storage parameter);
Table "public.t2"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
a | text | | | | extended | | |
Access method: heap
Options: fillfactor=100, toast.vacuum_truncate=true
discussion: https://postgr.es/m/CACJufxHr=nKEsS66M7rTHgB+mXdQ948=_eJ1jztAc7PT-eJefA@mail.gmail.com
commitfest entry: https://commitfest.postgresql.org/patch/6088
---
doc/src/sgml/ref/create_table.sgml | 19 +++++-
src/backend/access/common/reloptions.c | 49 +++++++++++++++
src/backend/parser/gram.y | 1 +
src/backend/parser/parse_utilcmd.c | 63 +++++++++++++++++++
src/include/access/reloptions.h | 1 +
src/include/nodes/parsenodes.h | 1 +
.../regress/expected/create_table_like.out | 39 ++++++++++++
src/test/regress/sql/create_table_like.sql | 28 +++++++++
8 files changed, 200 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index dc000e913c1..a389ce597a9 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -88,7 +88,7 @@ class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable cl
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
-{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | STORAGE PARAMETER | ALL }
<phrase>and <replaceable class="parameter">partition_bound_spec</replaceable> is:</phrase>
@@ -776,6 +776,23 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-parms-like-opt-storage-paramter">
+ <term><literal>INCLUDING STORAGE PARAMETER</literal></term>
+ <listitem>
+ <para>
+ <literal>STORAGE PARAMETER</literal> settings for the copied table will be copied.
+ For table storage parameters, see <xref linkend="sql-createtable-storage-parameters"/> below for more information.
+ </para>
+
+ <para>
+ Do not confuse this with <literal>INCLUDING STORAGE</literal>.
+ <literal>INCLUDING STORAGE</literal> is for copying table column storage property,
+ this is for copying table storage parameters.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-parms-like-opt-all">
<term><literal>INCLUDING ALL</literal></term>
<listitem>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 35150bf237b..6032cd5813e 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1383,6 +1383,55 @@ untransformRelOptions(Datum options)
return result;
}
+/*
+ * 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.
+ * If nameSpace is NULL, this behave the same as untransformRelOptions.
+ */
+List *
+untransformRelOptionsExtended(Datum options, char* nameSpace)
+{
+ 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);
+ }
+
+ if (nameSpace == NULL)
+ result = lappend(result, makeDefElem(s, val, -1));
+ else
+ result = lappend(result, makeDefElemExtended(nameSpace, s, val,
+ DEFELEM_UNSPEC,
+ -1));
+ }
+
+ return result;
+}
+
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 664b0483dbd..fc59268a076 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4225,6 +4225,7 @@ TableLikeOption:
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
+ | STORAGE PARAMETER { $$ = CREATE_TABLE_LIKE_STORAGE_PARAMETER; }
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e96b38a59d5..bd401cba5e4 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -85,6 +85,7 @@ 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 *blist; /* "before list" of things to do before
* creating the table */
List *alist; /* "after list" of things to do after creating
@@ -245,6 +246,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = stmt->options;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
@@ -369,6 +371,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
stmt->nnconstraints = cxt.nnconstraints;
+ stmt->options = cxt.options;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -1268,6 +1271,65 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
}
}
+ /* Likewise, copy storage paramter if requested */
+ if ((table_like_clause->options & CREATE_TABLE_LIKE_STORAGE_PARAMETER) &&
+ !cxt->isforeign)
+ {
+ HeapTuple tuple;
+ Datum reloptions;
+ bool isnull;
+ Oid relid;
+ List *oldoptions = NIL;
+ List *oldtoastoptions = NIL;
+
+ relid = RelationGetRelid(relation);
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ reloptions = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_reloptions, &isnull);
+
+ if (!isnull)
+ {
+ oldoptions = untransformRelOptions(reloptions);
+
+ foreach_node(DefElem, option, oldoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ /* now get the source relation toast relation reloptions */
+ if (OidIsValid(relation->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ Oid toastid = relation->rd_rel->reltoastrelid;
+
+ toastrel = table_open(toastid, AccessShareLock);
+
+ /* Fetch heap tuple */
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for toast relation %u", toastid);
+
+ /* Get the toast reloptions */
+ reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
+ &isnull);
+ if (!isnull)
+ {
+ oldtoastoptions = untransformRelOptionsExtended(reloptions, "toast");
+ foreach_node(DefElem, option, oldtoastoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ table_close(toastrel, NoLock);
+ }
+ }
+
/*
* Reproduce not-null constraints, if any, by copying them. We do this
* regardless of options given.
@@ -3575,6 +3637,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a604a4702c3..8589616d08b 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -236,6 +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 bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern void *build_reloptions(Datum reloptions, bool validate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ac0e02a1db7..4839f579cf6 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -794,6 +794,7 @@ typedef enum TableLikeOption
CREATE_TABLE_LIKE_INDEXES = 1 << 6,
CREATE_TABLE_LIKE_STATISTICS = 1 << 7,
CREATE_TABLE_LIKE_STORAGE = 1 << 8,
+ CREATE_TABLE_LIKE_STORAGE_PARAMETER = 1 << 9,
CREATE_TABLE_LIKE_ALL = PG_INT32_MAX
} TableLikeOption;
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index d3c35c14847..193771f3408 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -699,3 +699,42 @@ DROP FOREIGN TABLE ctl_foreign_table1;
DROP FOREIGN TABLE ctl_foreign_table2;
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
NOTICE: drop cascades to server ctl_s0
+--CREATE TABLE LIKE with STORAGE PARAMETER
+create table t(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 t1(like t including storage parameter) with (fillfactor = 100); --error
+ERROR: parameter "fillfactor" specified more than once
+create table t1(like t excluding storage parameter) with (fillfactor = 100); --ok
+\d+ t1
+ Table "public.t1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+------+-----------+----------+---------+----------+--------------+-------------
+ a | text | | | | extended | |
+Options: fillfactor=100
+
+create table t2(like t including storage parameter) 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', 't2')
+order by c.relname \gx
+-[ RECORD 1 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t
+reloptions | {fillfactor=100,toast_tuple_target=128,vacuum_index_cleanup=auto,vacuum_truncate=true,log_autovacuum_min_duration=100}
+toast_options | {vacuum_index_cleanup=auto,vacuum_truncate=false,log_autovacuum_min_duration=100}
+-[ RECORD 2 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t2
+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}
+
+drop table t, t1, t2;
diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql
index 93389b57dbf..47f67d244e2 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -280,3 +280,31 @@ DROP TABLE ctl_table;
DROP FOREIGN TABLE ctl_foreign_table1;
DROP FOREIGN TABLE ctl_foreign_table2;
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
+
+--CREATE TABLE LIKE with STORAGE PARAMETER
+create table t(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 t1(like t including storage parameter) with (fillfactor = 100); --error
+create table t1(like t excluding storage parameter) with (fillfactor = 100); --ok
+\d+ t1
+
+create table t2(like t including storage parameter) 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', 't2')
+order by c.relname \gx
+
+drop table t, t1, t2;
--
2.34.1
On Wed, Oct 1, 2025 at 2:42 PM jian he <jian.universality@gmail.com> wrote:
“including parameters” would add another keyword "PARAMETERS''.
currently "INCLUDING STORAGE PARAMETER" no need to add a new keyword.
If other people favor “including parameters”, then we can do it that
way in the future.attached is just a simple rebase of v1.
+ERROR: relation "t" already exists
I made the same mistake, regress test using a simple name like "t" will conflict
with other regress tests.
please check the attached, mainly regress tests changes.
Attachments:
v3-0001-create-table-like-incluing-storage-parameter.patchtext/x-patch; charset=US-ASCII; name=v3-0001-create-table-like-incluing-storage-parameter.patchDownload
From 58e527e08e0673683295465e07e049d41b29d79e Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Sat, 11 Oct 2025 11:03:51 +0800
Subject: [PATCH v3 1/1] create table like incluing storage parameter
demo:
create table t(a text) with (fillfactor = 100, toast.vacuum_truncate=true);
create table t2(like t including storage parameter);
Table "public.t2"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
a | text | | | | extended | | |
Access method: heap
Options: fillfactor=100, toast.vacuum_truncate=true
discussion: https://postgr.es/m/CACJufxHr=nKEsS66M7rTHgB+mXdQ948=_eJ1jztAc7PT-eJefA@mail.gmail.com
commitfest entry: https://commitfest.postgresql.org/patch/6088
---
doc/src/sgml/ref/create_table.sgml | 19 +++++-
src/backend/access/common/reloptions.c | 49 +++++++++++++++
src/backend/parser/gram.y | 1 +
src/backend/parser/parse_utilcmd.c | 63 +++++++++++++++++++
src/include/access/reloptions.h | 1 +
src/include/nodes/parsenodes.h | 1 +
.../regress/expected/create_table_like.out | 39 ++++++++++++
src/test/regress/sql/create_table_like.sql | 28 +++++++++
8 files changed, 200 insertions(+), 1 deletion(-)
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index dc000e913c1..a389ce597a9 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -88,7 +88,7 @@ class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable cl
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
-{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | STORAGE PARAMETER | ALL }
<phrase>and <replaceable class="parameter">partition_bound_spec</replaceable> is:</phrase>
@@ -776,6 +776,23 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-parms-like-opt-storage-paramter">
+ <term><literal>INCLUDING STORAGE PARAMETER</literal></term>
+ <listitem>
+ <para>
+ <literal>STORAGE PARAMETER</literal> settings for the copied table will be copied.
+ For table storage parameters, see <xref linkend="sql-createtable-storage-parameters"/> below for more information.
+ </para>
+
+ <para>
+ Do not confuse this with <literal>INCLUDING STORAGE</literal>.
+ <literal>INCLUDING STORAGE</literal> is for copying table column storage property,
+ this is for copying table storage parameters.
+ </para>
+
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-parms-like-opt-all">
<term><literal>INCLUDING ALL</literal></term>
<listitem>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 35150bf237b..6032cd5813e 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1383,6 +1383,55 @@ untransformRelOptions(Datum options)
return result;
}
+/*
+ * 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.
+ * If nameSpace is NULL, this behave the same as untransformRelOptions.
+ */
+List *
+untransformRelOptionsExtended(Datum options, char* nameSpace)
+{
+ 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);
+ }
+
+ if (nameSpace == NULL)
+ result = lappend(result, makeDefElem(s, val, -1));
+ else
+ result = lappend(result, makeDefElemExtended(nameSpace, s, val,
+ DEFELEM_UNSPEC,
+ -1));
+ }
+
+ return result;
+}
+
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 21caf2d43bf..ef8498ddb23 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -4223,6 +4223,7 @@ TableLikeOption:
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
+ | STORAGE PARAMETER { $$ = CREATE_TABLE_LIKE_STORAGE_PARAMETER; }
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e96b38a59d5..bd401cba5e4 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -85,6 +85,7 @@ 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 *blist; /* "before list" of things to do before
* creating the table */
List *alist; /* "after list" of things to do after creating
@@ -245,6 +246,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = stmt->options;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
@@ -369,6 +371,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
stmt->nnconstraints = cxt.nnconstraints;
+ stmt->options = cxt.options;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -1268,6 +1271,65 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
}
}
+ /* Likewise, copy storage paramter if requested */
+ if ((table_like_clause->options & CREATE_TABLE_LIKE_STORAGE_PARAMETER) &&
+ !cxt->isforeign)
+ {
+ HeapTuple tuple;
+ Datum reloptions;
+ bool isnull;
+ Oid relid;
+ List *oldoptions = NIL;
+ List *oldtoastoptions = NIL;
+
+ relid = RelationGetRelid(relation);
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ reloptions = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_reloptions, &isnull);
+
+ if (!isnull)
+ {
+ oldoptions = untransformRelOptions(reloptions);
+
+ foreach_node(DefElem, option, oldoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ /* now get the source relation toast relation reloptions */
+ if (OidIsValid(relation->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ Oid toastid = relation->rd_rel->reltoastrelid;
+
+ toastrel = table_open(toastid, AccessShareLock);
+
+ /* Fetch heap tuple */
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for toast relation %u", toastid);
+
+ /* Get the toast reloptions */
+ reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
+ &isnull);
+ if (!isnull)
+ {
+ oldtoastoptions = untransformRelOptionsExtended(reloptions, "toast");
+ foreach_node(DefElem, option, oldtoastoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ table_close(toastrel, NoLock);
+ }
+ }
+
/*
* Reproduce not-null constraints, if any, by copying them. We do this
* regardless of options given.
@@ -3575,6 +3637,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a604a4702c3..8589616d08b 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -236,6 +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 bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern void *build_reloptions(Datum reloptions, bool validate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index dc09d1a3f03..4f9b8e3e381 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -795,6 +795,7 @@ typedef enum TableLikeOption
CREATE_TABLE_LIKE_INDEXES = 1 << 6,
CREATE_TABLE_LIKE_STATISTICS = 1 << 7,
CREATE_TABLE_LIKE_STORAGE = 1 << 8,
+ CREATE_TABLE_LIKE_STORAGE_PARAMETER = 1 << 9,
CREATE_TABLE_LIKE_ALL = PG_INT32_MAX
} TableLikeOption;
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index d3c35c14847..b8336de3343 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -699,3 +699,42 @@ DROP FOREIGN TABLE ctl_foreign_table1;
DROP FOREIGN TABLE ctl_foreign_table2;
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
NOTICE: drop cascades to server ctl_s0
+--CREATE TABLE LIKE with STORAGE PARAMETER
+create table t_storage(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 t1(like t_storage including storage parameter) with (fillfactor = 100); --error
+ERROR: parameter "fillfactor" specified more than once
+create table t_storage1(like t_storage excluding storage parameter) with (fillfactor = 100); --ok
+\d+ t_storage1
+ Table "public.t_storage1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+------+-----------+----------+---------+----------+--------------+-------------
+ a | text | | | | extended | |
+Options: fillfactor=100
+
+create table t_storage2(like t_storage including storage parameter) 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_storage', 't_storage2')
+order by c.relname \gx
+-[ RECORD 1 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t_storage
+reloptions | {fillfactor=100,toast_tuple_target=128,vacuum_index_cleanup=auto,vacuum_truncate=true,log_autovacuum_min_duration=100}
+toast_options | {vacuum_index_cleanup=auto,vacuum_truncate=false,log_autovacuum_min_duration=100}
+-[ RECORD 2 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t_storage2
+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}
+
+drop table t_storage, t_storage1, t_storage2;
diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql
index 93389b57dbf..dddaa80b846 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -280,3 +280,31 @@ DROP TABLE ctl_table;
DROP FOREIGN TABLE ctl_foreign_table1;
DROP FOREIGN TABLE ctl_foreign_table2;
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
+
+--CREATE TABLE LIKE with STORAGE PARAMETER
+create table t_storage(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 t1(like t_storage including storage parameter) with (fillfactor = 100); --error
+create table t_storage1(like t_storage excluding storage parameter) with (fillfactor = 100); --ok
+\d+ t_storage1
+
+create table t_storage2(like t_storage including storage parameter) 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_storage', 't_storage2')
+order by c.relname \gx
+
+drop table t_storage, t_storage1, t_storage2;
--
2.34.1
On Sun, Sep 28, 2025 at 08:28:45PM -0500, David G. Johnston wrote:
On Sunday, September 28, 2025, jian he <jian.universality@gmail.com> wrote:
Since we already support INCLUDING STORAGE, I’m not sure that
INCLUDING STORAGE PARAMETER is the right syntax to go with.I’d just call it “including parameters”; sure, all of the existing ones are
storage related, but if there were non-storage ones we’d include those as
well, so qualifying with a parameter type doesn’t seem correct.
+1
--
nathan
On Sat, Oct 25, 2025 at 1:48 AM Nathan Bossart <nathandbossart@gmail.com> wrote:
On Sun, Sep 28, 2025 at 08:28:45PM -0500, David G. Johnston wrote:
On Sunday, September 28, 2025, jian he <jian.universality@gmail.com> wrote:
Since we already support INCLUDING STORAGE, I’m not sure that
INCLUDING STORAGE PARAMETER is the right syntax to go with.I’d just call it “including parameters”; sure, all of the existing ones are
storage related, but if there were non-storage ones we’d include those as
well, so qualifying with a parameter type doesn’t seem correct.+1
--
nathan
hi.
attached patch using syntax:
CREATE TABLE LIKE INCLUDING PARAMETERS.
Attachments:
v4-0001-create-table-like-incluing-parameters.patchtext/x-patch; charset=US-ASCII; name=v4-0001-create-table-like-incluing-parameters.patchDownload
From 16dd551ad89c999d5e4ec654e20df7ac62df6a2d Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Thu, 30 Oct 2025 09:53:22 +0800
Subject: [PATCH v4 1/1] create table like incluing parameters
demo:
create table t(a text) with (fillfactor = 100, toast.vacuum_truncate=true);
create table t2(like t including parameters);
\d+ t2
Table "public.t2"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
a | text | | | | extended | | |
Access method: heap
Options: fillfactor=100, toast.vacuum_truncate=true
discussion: https://postgr.es/m/CACJufxHr=nKEsS66M7rTHgB+mXdQ948=_eJ1jztAc7PT-eJefA@mail.gmail.com
---
doc/src/sgml/ref/create_table.sgml | 12 +++-
src/backend/access/common/reloptions.c | 50 +++++++++++++++
src/backend/parser/gram.y | 5 +-
src/backend/parser/parse_utilcmd.c | 64 +++++++++++++++++++
src/include/access/reloptions.h | 1 +
src/include/nodes/parsenodes.h | 1 +
src/include/parser/kwlist.h | 1 +
.../regress/expected/create_table_like.out | 39 +++++++++++
src/test/regress/sql/create_table_like.sql | 28 ++++++++
9 files changed, 199 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index a157a244e4e..cad839b452d 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -88,7 +88,7 @@ class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable cl
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
-{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | PARAMETERS | ALL }
<phrase>and <replaceable class="parameter">partition_bound_spec</replaceable> is:</phrase>
@@ -753,6 +753,16 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-parms-like-opt-storage-paramter">
+ <term><literal>INCLUDING PARAMETERS</literal></term>
+ <listitem>
+ <para>
+ All parameters, such as the <literal>STORAGE PARAMETER</literal> settings of the source table, will be copied.
+ For table storage parameters, see <xref linkend="sql-createtable-storage-parameters"/> below for more information.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-parms-like-opt-statistics">
<term><literal>INCLUDING STATISTICS</literal></term>
<listitem>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 9e288dfecbf..365f30402f0 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1392,6 +1392,56 @@ untransformRelOptions(Datum options)
return result;
}
+/*
+ * 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.
+ */
+List *
+untransformRelOptionsExtended(Datum options, char* nameSpace)
+{
+ 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);
+ }
+
+ if (nameSpace == NULL)
+ result = lappend(result, makeDefElem(s, val, -1));
+ else
+ result = lappend(result, makeDefElemExtended(nameSpace, s, val,
+ DEFELEM_UNSPEC,
+ -1));
+ }
+
+ return result;
+}
+
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a4b29c822e8..fe4b035fe34 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -762,7 +762,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ORDER ORDINALITY OTHERS OUT_P OUTER_P
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
- PARALLEL PARAMETER PARSER PARTIAL PARTITION PASSING PASSWORD PATH
+ PARALLEL PARAMETER PARAMETERS PARSER PARTIAL PARTITION PASSING PASSWORD PATH
PERIOD PLACING PLAN PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
@@ -4223,6 +4223,7 @@ TableLikeOption:
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
+ | PARAMETERS { $$ = CREATE_TABLE_LIKE_PARAMETERS; }
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
@@ -17994,6 +17995,7 @@ unreserved_keyword:
| OWNER
| PARALLEL
| PARAMETER
+ | PARAMETERS
| PARSER
| PARTIAL
| PARTITION
@@ -18622,6 +18624,7 @@ bare_label_keyword:
| OWNER
| PARALLEL
| PARAMETER
+ | PARAMETERS
| PARSER
| PARTIAL
| PARTITION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index e96b38a59d5..791ffc5150c 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -85,6 +85,7 @@ 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 *blist; /* "before list" of things to do before
* creating the table */
List *alist; /* "after list" of things to do after creating
@@ -245,6 +246,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = stmt->options;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
@@ -369,6 +371,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
stmt->nnconstraints = cxt.nnconstraints;
+ stmt->options = cxt.options;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -1268,6 +1271,66 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
}
}
+ /* Likewise, copy paramters if requested */
+ if ((table_like_clause->options & CREATE_TABLE_LIKE_PARAMETERS) &&
+ !cxt->isforeign)
+ {
+ HeapTuple tuple;
+ Datum reloptions;
+ bool isnull;
+ Oid relid;
+ List *oldoptions = NIL;
+ List *oldtoastoptions = NIL;
+
+ relid = RelationGetRelid(relation);
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ reloptions = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_reloptions, &isnull);
+
+ if (!isnull)
+ {
+ oldoptions = untransformRelOptions(reloptions);
+
+ foreach_node(DefElem, option, oldoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+ ReleaseSysCache(tuple);
+
+ /* get the toast relation reloptions */
+ if (OidIsValid(relation->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ Oid toastid = relation->rd_rel->reltoastrelid;
+
+ toastrel = table_open(toastid, AccessShareLock);
+
+ /* Fetch heap tuple */
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for toast relation %u", toastid);
+
+ /* Get the toast reloptions */
+ reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
+ &isnull);
+ if (!isnull)
+ {
+ oldtoastoptions = untransformRelOptionsExtended(reloptions, "toast");
+
+ foreach_node(DefElem, option, oldtoastoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+ ReleaseSysCache(tuple);
+
+ table_close(toastrel, NoLock);
+ }
+ }
+
/*
* Reproduce not-null constraints, if any, by copying them. We do this
* regardless of options given.
@@ -3575,6 +3638,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a604a4702c3..8589616d08b 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -236,6 +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 bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern void *build_reloptions(Datum reloptions, bool validate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index ecbddd12e1b..a8e5538119c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -795,6 +795,7 @@ typedef enum TableLikeOption
CREATE_TABLE_LIKE_INDEXES = 1 << 6,
CREATE_TABLE_LIKE_STATISTICS = 1 << 7,
CREATE_TABLE_LIKE_STORAGE = 1 << 8,
+ CREATE_TABLE_LIKE_PARAMETERS = 1 << 9,
CREATE_TABLE_LIKE_ALL = PG_INT32_MAX
} TableLikeOption;
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 84182eaaae2..d132eca0068 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -335,6 +335,7 @@ PG_KEYWORD("owned", OWNED, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parallel", PARALLEL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parameter", PARAMETER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("parameters", PARAMETERS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index d3c35c14847..2e8962590a0 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -699,3 +699,42 @@ DROP FOREIGN TABLE ctl_foreign_table1;
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_storage(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 t1(like t_storage including parameters) with (fillfactor = 100); --error
+ERROR: parameter "fillfactor" specified more than once
+create table t_storage1(like t_storage excluding parameters) with (fillfactor = 100); --ok
+\d+ t_storage1
+ Table "public.t_storage1"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+------+-----------+----------+---------+----------+--------------+-------------
+ a | text | | | | extended | |
+Options: fillfactor=100
+
+create table t_storage2(like t_storage 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_storage', 't_storage2')
+order by c.relname \gx
+-[ RECORD 1 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t_storage
+reloptions | {fillfactor=100,toast_tuple_target=128,vacuum_index_cleanup=auto,vacuum_truncate=true,log_autovacuum_min_duration=100}
+toast_options | {vacuum_index_cleanup=auto,vacuum_truncate=false,log_autovacuum_min_duration=100}
+-[ RECORD 2 ]-+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t_storage2
+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}
+
+drop table t_storage, t_storage1, t_storage2;
diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql
index 93389b57dbf..e1abf5a4262 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -280,3 +280,31 @@ DROP TABLE ctl_table;
DROP FOREIGN TABLE ctl_foreign_table1;
DROP FOREIGN TABLE ctl_foreign_table2;
DROP FOREIGN DATA WRAPPER ctl_dummy CASCADE;
+
+--CREATE TABLE LIKE with PARAMETERS
+create table t_storage(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 t1(like t_storage including parameters) with (fillfactor = 100); --error
+create table t_storage1(like t_storage excluding parameters) with (fillfactor = 100); --ok
+\d+ t_storage1
+
+create table t_storage2(like t_storage 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_storage', 't_storage2')
+order by c.relname \gx
+
+drop table t_storage, t_storage1, t_storage2;
--
2.34.1
On Thu, Oct 30, 2025 at 9:57 AM jian he <jian.universality@gmail.com> wrote:
attached patch using syntax:
CREATE TABLE LIKE INCLUDING PARAMETERS.
hi.
rebased, with minor comment polishing.
Attachments:
v5-0001-create-table-like-incluing-parameters.patchtext/x-patch; charset=US-ASCII; name=v5-0001-create-table-like-incluing-parameters.patchDownload
From f0226ac62a162d0cf6d0963ac1aa4cfd54363664 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 15 Dec 2025 16:41:59 +0800
Subject: [PATCH v5 1/1] create table like incluing parameters
demo:
create table t(a text) with (fillfactor = 100, toast.vacuum_truncate=true);
create table t2(like t including parameters);
\d+ t2
Table "public.t2"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
a | text | | | | extended | | |
Access method: heap
Options: fillfactor=100, toast.vacuum_truncate=true
commitfest: https://commitfest.postgresql.org/patch/6088
discussion: https://postgr.es/m/CACJufxHr=nKEsS66M7rTHgB+mXdQ948=_eJ1jztAc7PT-eJefA@mail.gmail.com
---
doc/src/sgml/ref/create_table.sgml | 14 +++-
src/backend/access/common/reloptions.c | 50 ++++++++++++
src/backend/parser/gram.y | 5 +-
src/backend/parser/parse_utilcmd.c | 76 +++++++++++++++++++
src/include/access/reloptions.h | 1 +
src/include/nodes/parsenodes.h | 1 +
src/include/parser/kwlist.h | 1 +
.../regress/expected/create_table_like.out | 39 ++++++++++
src/test/regress/sql/create_table_like.sql | 28 +++++++
9 files changed, 213 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 77c5a763d45..d645ab4ed31 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -88,7 +88,7 @@ class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable cl
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
-{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | PARAMETERS | ALL }
<phrase>and <replaceable class="parameter">partition_bound_spec</replaceable> is:</phrase>
@@ -753,6 +753,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-parms-like-opt-storage-paramter">
+ <term><literal>INCLUDING PARAMETERS</literal></term>
+ <listitem>
+ <para>
+ All table parameters, such as the <literal>storage parameter</literal>
+ settings of the source table, will be copied. For table storage
+ parameters, see <xref linkend="sql-createtable-storage-parameters"/>
+ below for more information.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-parms-like-opt-statistics">
<term><literal>INCLUDING STATISTICS</literal></term>
<listitem>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 31926d8a368..9289caef7ba 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1392,6 +1392,56 @@ untransformRelOptions(Datum options)
return result;
}
+/*
+ * 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.
+ */
+List *
+untransformRelOptionsExtended(Datum options, char* nameSpace)
+{
+ 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);
+ }
+
+ if (nameSpace == NULL)
+ result = lappend(result, makeDefElem(s, val, -1));
+ else
+ result = lappend(result, makeDefElemExtended(nameSpace, s, val,
+ DEFELEM_UNSPEC,
+ -1));
+ }
+
+ return result;
+}
+
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 28f4e11e30f..732cbe8dbab 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -765,7 +765,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ORDER ORDINALITY OTHERS OUT_P OUTER_P
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
- PARALLEL PARAMETER PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH
+ PARALLEL PARAMETER PARAMETERS PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH
PERIOD PLACING PLAN PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
@@ -4296,6 +4296,7 @@ TableLikeOption:
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
+ | PARAMETERS { $$ = CREATE_TABLE_LIKE_PARAMETERS; }
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
@@ -18087,6 +18088,7 @@ unreserved_keyword:
| OWNER
| PARALLEL
| PARAMETER
+ | PARAMETERS
| PARSER
| PARTIAL
| PARTITION
@@ -18719,6 +18721,7 @@ bare_label_keyword:
| OWNER
| PARALLEL
| PARAMETER
+ | PARAMETERS
| PARSER
| PARTIAL
| PARTITION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 375b40b29af..2e77c2db19b 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -88,6 +88,7 @@ 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 *blist; /* "before list" of things to do before
* creating the table */
List *alist; /* "after list" of things to do after creating
@@ -248,6 +249,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = stmt->options;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
@@ -372,6 +374,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
stmt->nnconstraints = cxt.nnconstraints;
+ stmt->options = cxt.options;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -1271,6 +1274,78 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
}
}
+ /* Likewise, copy stoarge paramters 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;
+
+ relid = RelationGetRelid(relation);
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ reloptions = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_reloptions, &isnull);
+ if (!isnull)
+ {
+ oldoptions = untransformRelOptions(reloptions);
+
+ foreach_node(DefElem, option, oldoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ /* get the toast relation's reloptions */
+ if (OidIsValid(relation->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ Oid toastid = relation->rd_rel->reltoastrelid;
+
+ /*
+ * The referenced table is already locked; acquire the lock on its
+ * associated TOAST relation now.
+ */
+ toastrel = table_open(toastid, AccessShareLock);
+
+ /* Fetch heap tuple */
+ toast_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
+
+ if (!HeapTupleIsValid(toast_tuple))
+ elog(ERROR, "cache lookup failed for toast relation %u", toastid);
+
+ /* Get the toast reloptions */
+ reloptions = SysCacheGetAttr(RELOID, toast_tuple, Anum_pg_class_reloptions,
+ &isnull);
+ if (!isnull)
+ {
+ oldtoastoptions = untransformRelOptionsExtended(reloptions, "toast");
+
+ foreach_node(DefElem, option, oldtoastoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(toast_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
+ * expandTableLikeClause.
+ */
+ table_close(toastrel, NoLock);
+ }
+ }
+
/*
* Reproduce not-null constraints, if any, by copying them. We do this
* regardless of options given.
@@ -3860,6 +3935,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a604a4702c3..8589616d08b 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -236,6 +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 bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern void *build_reloptions(Datum reloptions, bool validate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index bc7adba4a0f..9e0296aee39 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -795,6 +795,7 @@ typedef enum TableLikeOption
CREATE_TABLE_LIKE_INDEXES = 1 << 6,
CREATE_TABLE_LIKE_STATISTICS = 1 << 7,
CREATE_TABLE_LIKE_STORAGE = 1 << 8,
+ CREATE_TABLE_LIKE_PARAMETERS = 1 << 9,
CREATE_TABLE_LIKE_ALL = PG_INT32_MAX
} TableLikeOption;
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 9fde58f541c..aad64e7ccca 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -336,6 +336,7 @@ PG_KEYWORD("owned", OWNED, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parallel", PARALLEL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parameter", PARAMETER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("parameters", PARAMETERS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index d3c35c14847..3eb079e24ed 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -699,3 +699,42 @@ DROP FOREIGN TABLE ctl_foreign_table1;
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
+ERROR: parameter "fillfactor" specified more than once
+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
+--------+------+-----------+----------+---------+----------+--------------+-------------
+ a | text | | | | extended | |
+Options: fillfactor=100
+
+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}
+
+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 93389b57dbf..afc8ffe6ad5 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -280,3 +280,31 @@ DROP TABLE ctl_table;
DROP FOREIGN TABLE ctl_foreign_table1;
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_sp1(like t_sp including parameters) with (fillfactor = 100); --error
+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 (
+ 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
+
+drop table t_sp, t_sp1, t_sp2;
--
2.34.1
On Mon, Dec 15, 2025, at 5:48 AM, jian he wrote:
rebased, with minor comment polishing.
I started reviewing this patch. Why don't you reuse untransformRelOptions()?
Even if this function is used by various extensions [1]https://codesearch.debian.net/search?q=untransformRelOptions&literal=1[2]https://github.com/search?q=untransformRelOptions&type=code, that's not an
excuse to duplicate code. I adjusted the code in the fixup. I did a bunch of
stylish changes and fixed a few typos. I also ran pgindent. I removed some
superfluous comments ("Fetch heap tuple", "Get the toast reloptions",). Don't
think they are saying something important. Coverage looks good. As a homework,
add a nice commit message. Showing how it works is not a good commit message.
This v6 is your v5 plus my suggestions.
[1]: https://codesearch.debian.net/search?q=untransformRelOptions&literal=1
[2]: https://github.com/search?q=untransformRelOptions&type=code
--
Euler Taveira
EDB https://www.enterprisedb.com/
Attachments:
v6-0001-create-table-like-incluing-parameters.patchtext/x-patch; name="=?UTF-8?Q?v6-0001-create-table-like-incluing-parameters.patch?="Download
From 423eeaeef069ee3c1fa9725e20bbfcfa0464916d Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Mon, 15 Dec 2025 16:41:59 +0800
Subject: [PATCH v6 1/2] create table like incluing parameters
demo:
create table t(a text) with (fillfactor = 100, toast.vacuum_truncate=true);
create table t2(like t including parameters);
\d+ t2
Table "public.t2"
Column | Type | Collation | Nullable | Default | Storage | Compression | Stats target | Description
--------+------+-----------+----------+---------+----------+-------------+--------------+-------------
a | text | | | | extended | | |
Access method: heap
Options: fillfactor=100, toast.vacuum_truncate=true
commitfest: https://commitfest.postgresql.org/patch/6088
discussion: https://postgr.es/m/CACJufxHr=nKEsS66M7rTHgB+mXdQ948=_eJ1jztAc7PT-eJefA@mail.gmail.com
---
doc/src/sgml/ref/create_table.sgml | 14 +++-
src/backend/access/common/reloptions.c | 50 ++++++++++++
src/backend/parser/gram.y | 5 +-
src/backend/parser/parse_utilcmd.c | 76 +++++++++++++++++++
src/include/access/reloptions.h | 1 +
src/include/nodes/parsenodes.h | 1 +
src/include/parser/kwlist.h | 1 +
.../regress/expected/create_table_like.out | 39 ++++++++++
src/test/regress/sql/create_table_like.sql | 28 +++++++
9 files changed, 213 insertions(+), 2 deletions(-)
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 77c5a763d45..d645ab4ed31 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -88,7 +88,7 @@ class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable cl
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
-{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | PARAMETERS | ALL }
<phrase>and <replaceable class="parameter">partition_bound_spec</replaceable> is:</phrase>
@@ -753,6 +753,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-parms-like-opt-storage-paramter">
+ <term><literal>INCLUDING PARAMETERS</literal></term>
+ <listitem>
+ <para>
+ All table parameters, such as the <literal>storage parameter</literal>
+ settings of the source table, will be copied. For table storage
+ parameters, see <xref linkend="sql-createtable-storage-parameters"/>
+ below for more information.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-parms-like-opt-statistics">
<term><literal>INCLUDING STATISTICS</literal></term>
<listitem>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 31926d8a368..9289caef7ba 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1392,6 +1392,56 @@ untransformRelOptions(Datum options)
return result;
}
+/*
+ * 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.
+ */
+List *
+untransformRelOptionsExtended(Datum options, char* nameSpace)
+{
+ 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);
+ }
+
+ if (nameSpace == NULL)
+ result = lappend(result, makeDefElem(s, val, -1));
+ else
+ result = lappend(result, makeDefElemExtended(nameSpace, s, val,
+ DEFELEM_UNSPEC,
+ -1));
+ }
+
+ return result;
+}
+
+
/*
* Extract and parse reloptions from a pg_class tuple.
*
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 28f4e11e30f..732cbe8dbab 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -765,7 +765,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ORDER ORDINALITY OTHERS OUT_P OUTER_P
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
- PARALLEL PARAMETER PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH
+ PARALLEL PARAMETER PARAMETERS PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH
PERIOD PLACING PLAN PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
@@ -4296,6 +4296,7 @@ TableLikeOption:
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
+ | PARAMETERS { $$ = CREATE_TABLE_LIKE_PARAMETERS; }
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
@@ -18087,6 +18088,7 @@ unreserved_keyword:
| OWNER
| PARALLEL
| PARAMETER
+ | PARAMETERS
| PARSER
| PARTIAL
| PARTITION
@@ -18719,6 +18721,7 @@ bare_label_keyword:
| OWNER
| PARALLEL
| PARAMETER
+ | PARAMETERS
| PARSER
| PARTIAL
| PARTITION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 375b40b29af..2e77c2db19b 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -88,6 +88,7 @@ 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 *blist; /* "before list" of things to do before
* creating the table */
List *alist; /* "after list" of things to do after creating
@@ -248,6 +249,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = stmt->options;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
@@ -372,6 +374,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
stmt->nnconstraints = cxt.nnconstraints;
+ stmt->options = cxt.options;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -1271,6 +1274,78 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
}
}
+ /* Likewise, copy stoarge paramters 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;
+
+ relid = RelationGetRelid(relation);
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ reloptions = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_reloptions, &isnull);
+ if (!isnull)
+ {
+ oldoptions = untransformRelOptions(reloptions);
+
+ foreach_node(DefElem, option, oldoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ /* get the toast relation's reloptions */
+ if (OidIsValid(relation->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+
+ Oid toastid = relation->rd_rel->reltoastrelid;
+
+ /*
+ * The referenced table is already locked; acquire the lock on its
+ * associated TOAST relation now.
+ */
+ toastrel = table_open(toastid, AccessShareLock);
+
+ /* Fetch heap tuple */
+ toast_tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
+
+ if (!HeapTupleIsValid(toast_tuple))
+ elog(ERROR, "cache lookup failed for toast relation %u", toastid);
+
+ /* Get the toast reloptions */
+ reloptions = SysCacheGetAttr(RELOID, toast_tuple, Anum_pg_class_reloptions,
+ &isnull);
+ if (!isnull)
+ {
+ oldtoastoptions = untransformRelOptionsExtended(reloptions, "toast");
+
+ foreach_node(DefElem, option, oldtoastoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(toast_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
+ * expandTableLikeClause.
+ */
+ table_close(toastrel, NoLock);
+ }
+ }
+
/*
* Reproduce not-null constraints, if any, by copying them. We do this
* regardless of options given.
@@ -3860,6 +3935,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a604a4702c3..8589616d08b 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -236,6 +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 bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern void *build_reloptions(Datum reloptions, bool validate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index bc7adba4a0f..9e0296aee39 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -795,6 +795,7 @@ typedef enum TableLikeOption
CREATE_TABLE_LIKE_INDEXES = 1 << 6,
CREATE_TABLE_LIKE_STATISTICS = 1 << 7,
CREATE_TABLE_LIKE_STORAGE = 1 << 8,
+ CREATE_TABLE_LIKE_PARAMETERS = 1 << 9,
CREATE_TABLE_LIKE_ALL = PG_INT32_MAX
} TableLikeOption;
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 9fde58f541c..aad64e7ccca 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -336,6 +336,7 @@ PG_KEYWORD("owned", OWNED, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parallel", PARALLEL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parameter", PARAMETER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("parameters", PARAMETERS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index d3c35c14847..3eb079e24ed 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -699,3 +699,42 @@ DROP FOREIGN TABLE ctl_foreign_table1;
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
+ERROR: parameter "fillfactor" specified more than once
+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
+--------+------+-----------+----------+---------+----------+--------------+-------------
+ a | text | | | | extended | |
+Options: fillfactor=100
+
+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}
+
+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 93389b57dbf..afc8ffe6ad5 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -280,3 +280,31 @@ DROP TABLE ctl_table;
DROP FOREIGN TABLE ctl_foreign_table1;
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_sp1(like t_sp including parameters) with (fillfactor = 100); --error
+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 (
+ 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
+
+drop table t_sp, t_sp1, t_sp2;
--
2.39.5
v6-0002-fixup-create-table-like-incluing-parameters.patchtext/x-patch; name="=?UTF-8?Q?v6-0002-fixup-create-table-like-incluing-parameters.patch?="Download
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
On Tue, Dec 16, 2025 at 1:52 AM Euler Taveira <euler@eulerto.com> wrote:
I started reviewing this patch. Why don't you reuse untransformRelOptions()?
Even if this function is used by various extensions [1][2], that's not an
excuse to duplicate code.
yech, duplicate code is not good. I encountered the same issue in the CAST
DEFAULT patch. I guess I was posting it in the "post early post often" spirit
for the rebase.
This v6 is your v5 plus my suggestions.
your suggestions are great.
I have incorporated all the changes you proposed.
in transformTableLikeClause, we have:
if (relation->rd_rel->relkind != RELKIND_RELATION &&
relation->rd_rel->relkind != RELKIND_VIEW &&
relation->rd_rel->relkind != RELKIND_MATVIEW &&
relation->rd_rel->relkind != RELKIND_COMPOSITE_TYPE &&
relation->rd_rel->relkind != RELKIND_FOREIGN_TABLE &&
relation->rd_rel->relkind != RELKIND_PARTITIONED_TABLE)
ereport(ERROR,
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
errmsg("relation \"%s\" is invalid in LIKE clause",
RelationGetRelationName(relation)),
errdetail_relkind_not_supported(relation->rd_rel->relkind)));
except RELKIND_RELATION, RELKIND_MATVIEW can also specify storage parameters, I
added a test for it too.
to test CREATE FOREIGN LIKE will silently ignore INCLUDING PARAMETERS, i also
slightly modified create_table_like.sql.
The last SELECT query output is too wide, to make it more readable, I
have to use \gx.
Below is the commit message I have drafted:
----------------------------------
Add INCLUDING PARAMETERS to CREATE TABLE LIKE
Introduce the PARAMETERS option to copy parameters when using CREATE TABLE LIKE.
Currently, this primarily applies to the storage parameters, but in the future,
it may encompass other kind parameters in the table.
Since storage parameters are not in the standard, INCLUDING PARAMETERS does not
comply with the standard. Also because foreign tables cannot specify storage
parameters, CREATE FOREIGN TABLE LIKE will silently ignore INCLUDING PARAMETERS.
This makes creating a new table via CREATE TABLE LIKE more convenient, as the
user would otherwise have to manually specify every storage parameter. It is a
further step toward making a new table created with CREATE TABLE LIKE more
identical to the original table.
Author: jian he <jian.universality@gmail.com>
Reviewed-by: Euler Taveira <euler@eulerto.com>
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: David G. Johnston <david.g.johnston@gmail.com>
(delete this line later) commitfest:
https://commitfest.postgresql.org/patch/6088
discussion: /messages/by-id/CACJufxHr=nKEsS66M7rTHgB+mXdQ948=_eJ1jztAc7PT-eJefA@mail.gmail.com
----------------------------------
Attachments:
v7-0001-Add-INCLUDING-PARAMETERS-to-CREATE-TABLE-LIKE.patchtext/x-patch; charset=US-ASCII; name=v7-0001-Add-INCLUDING-PARAMETERS-to-CREATE-TABLE-LIKE.patchDownload
From 0a1bbe140213168dc8df0b64e29d7a814cdecd06 Mon Sep 17 00:00:00 2001
From: jian he <jian.universality@gmail.com>
Date: Tue, 16 Dec 2025 10:36:50 +0800
Subject: [PATCH v7 1/1] Add INCLUDING PARAMETERS to CREATE TABLE LIKE
Introduce the PARAMETERS option to copy parameters when using CREATE TABLE LIKE.
Currently, this primarily applies to the storage parameters, but in the future,
it may encompass other kind parameters in the table.
Since storage parameters are not in the standard, INCLUDING PARAMETERS does not
comply with the standard. Also because foreign tables cannot specify storage
parameters, CREATE FOREIGN TABLE LIKE will silently ignore INCLUDING PARAMETERS.
This makes creating a new table via CREATE TABLE LIKE more convenient, as the
user would otherwise have to manually specify every storage parameter. It is a
further step toward making a new table created with CREATE TABLE LIKE more
identical to the original table.
Author: jian he <jian.universality@gmail.com>
Reviewed-by: Euler Taveira <euler@eulerto.com>
Reviewed-by: Nathan Bossart <nathandbossart@gmail.com>
Reviewed-by: David G. Johnston <david.g.johnston@gmail.com>
(delete this line later) commitfest: https://commitfest.postgresql.org/patch/6088
discussion: https://postgr.es/m/CACJufxHr=nKEsS66M7rTHgB+mXdQ948=_eJ1jztAc7PT-eJefA@mail.gmail.com
---
doc/src/sgml/ref/create_table.sgml | 14 +++-
src/backend/access/common/reloptions.c | 17 +++-
src/backend/parser/gram.y | 5 +-
src/backend/parser/parse_utilcmd.c | 77 ++++++++++++++++++-
src/include/access/reloptions.h | 1 +
src/include/nodes/parsenodes.h | 1 +
src/include/parser/kwlist.h | 1 +
.../regress/expected/create_table_like.out | 55 ++++++++++++-
src/test/regress/sql/create_table_like.sql | 39 +++++++++-
9 files changed, 201 insertions(+), 9 deletions(-)
diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 77c5a763d45..d645ab4ed31 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -88,7 +88,7 @@ class="parameter">referential_action</replaceable> ] [ ON UPDATE <replaceable cl
<phrase>and <replaceable class="parameter">like_option</replaceable> is:</phrase>
-{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | ALL }
+{ INCLUDING | EXCLUDING } { COMMENTS | COMPRESSION | CONSTRAINTS | DEFAULTS | GENERATED | IDENTITY | INDEXES | STATISTICS | STORAGE | PARAMETERS | ALL }
<phrase>and <replaceable class="parameter">partition_bound_spec</replaceable> is:</phrase>
@@ -753,6 +753,18 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
</listitem>
</varlistentry>
+ <varlistentry id="sql-createtable-parms-like-opt-storage-paramter">
+ <term><literal>INCLUDING PARAMETERS</literal></term>
+ <listitem>
+ <para>
+ All table parameters, such as the <literal>storage parameter</literal>
+ settings of the source table, will be copied. For table storage
+ parameters, see <xref linkend="sql-createtable-storage-parameters"/>
+ below for more information.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry id="sql-createtable-parms-like-opt-statistics">
<term><literal>INCLUDING STATISTICS</literal></term>
<listitem>
diff --git a/src/backend/access/common/reloptions.c b/src/backend/access/common/reloptions.c
index 31926d8a368..57c337377f5 100644
--- a/src/backend/access/common/reloptions.c
+++ b/src/backend/access/common/reloptions.c
@@ -1358,6 +1358,15 @@ transformRelOptions(Datum oldOptions, List *defList, const char *nameSpace,
*/
List *
untransformRelOptions(Datum options)
+{
+ return untransformRelOptionsExtended(options, NULL);
+}
+
+/*
+ * Same as untransformRelOptions() but includes a namespace into DefElem.
+ */
+List *
+untransformRelOptionsExtended(Datum options, char *nameSpace)
{
List *result = NIL;
ArrayType *array;
@@ -1386,7 +1395,13 @@ untransformRelOptions(Datum options)
*p++ = '\0';
val = (Node *) makeString(p);
}
- result = lappend(result, makeDefElem(s, val, -1));
+
+ if (nameSpace == NULL)
+ result = lappend(result, makeDefElem(s, val, -1));
+ else
+ result = lappend(result, makeDefElemExtended(nameSpace, s, val,
+ DEFELEM_UNSPEC,
+ -1));
}
return result;
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 28f4e11e30f..732cbe8dbab 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -765,7 +765,7 @@ static Node *makeRecursiveViewSelect(char *relname, List *aliases, Node *query);
ORDER ORDINALITY OTHERS OUT_P OUTER_P
OVER OVERLAPS OVERLAY OVERRIDING OWNED OWNER
- PARALLEL PARAMETER PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH
+ PARALLEL PARAMETER PARAMETERS PARSER PARTIAL PARTITION PARTITIONS PASSING PASSWORD PATH
PERIOD PLACING PLAN PLANS POLICY
POSITION PRECEDING PRECISION PRESERVE PREPARE PREPARED PRIMARY
PRIOR PRIVILEGES PROCEDURAL PROCEDURE PROCEDURES PROGRAM PUBLICATION
@@ -4296,6 +4296,7 @@ TableLikeOption:
| INDEXES { $$ = CREATE_TABLE_LIKE_INDEXES; }
| STATISTICS { $$ = CREATE_TABLE_LIKE_STATISTICS; }
| STORAGE { $$ = CREATE_TABLE_LIKE_STORAGE; }
+ | PARAMETERS { $$ = CREATE_TABLE_LIKE_PARAMETERS; }
| ALL { $$ = CREATE_TABLE_LIKE_ALL; }
;
@@ -18087,6 +18088,7 @@ unreserved_keyword:
| OWNER
| PARALLEL
| PARAMETER
+ | PARAMETERS
| PARSER
| PARTIAL
| PARTITION
@@ -18719,6 +18721,7 @@ bare_label_keyword:
| OWNER
| PARALLEL
| PARAMETER
+ | PARAMETERS
| PARSER
| PARTIAL
| PARTITION
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index 375b40b29af..c9056c4f22e 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -88,6 +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 *blist; /* "before list" of things to do before
* creating the table */
List *alist; /* "after list" of things to do after creating
@@ -248,6 +250,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = stmt->options;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
@@ -372,6 +375,7 @@ transformCreateStmt(CreateStmt *stmt, const char *queryString)
stmt->tableElts = cxt.columns;
stmt->constraints = cxt.ckconstraints;
stmt->nnconstraints = cxt.nnconstraints;
+ stmt->options = cxt.options;
result = lappend(cxt.blist, stmt);
result = list_concat(result, cxt.alist);
@@ -1122,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)
@@ -1271,6 +1275,74 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
}
}
+ /* Likewise, copy storage parameters if requested */
+ if ((table_like_clause->options & CREATE_TABLE_LIKE_PARAMETERS) &&
+ !cxt->isforeign)
+ {
+ HeapTuple tuple;
+ Datum reloptions;
+ bool isnull;
+ Oid relid;
+ List *oldoptions = NIL;
+ List *oldtoastoptions = NIL;
+
+ relid = RelationGetRelid(relation);
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", relid);
+
+ reloptions = SysCacheGetAttr(RELOID, tuple,
+ Anum_pg_class_reloptions, &isnull);
+ if (!isnull)
+ {
+ oldoptions = untransformRelOptions(reloptions);
+
+ foreach_node(DefElem, option, oldoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ /* get the toast relation's reloptions */
+ if (OidIsValid(relation->rd_rel->reltoastrelid))
+ {
+ Relation toastrel;
+ Oid toastid = relation->rd_rel->reltoastrelid;
+
+ /*
+ * The referenced table is already locked; acquire the lock on its
+ * associated TOAST relation now.
+ */
+ toastrel = table_open(toastid, AccessShareLock);
+
+ tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(toastid));
+
+ if (!HeapTupleIsValid(tuple))
+ elog(ERROR, "cache lookup failed for relation %u", toastid);
+
+ reloptions = SysCacheGetAttr(RELOID, tuple, Anum_pg_class_reloptions,
+ &isnull);
+ if (!isnull)
+ {
+ oldtoastoptions = untransformRelOptionsExtended(reloptions, "toast");
+
+ foreach_node(DefElem, option, oldtoastoptions)
+ cxt->options = lappend(cxt->options, option);
+ }
+
+ ReleaseSysCache(tuple);
+
+ /*
+ * 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);
+ }
+ }
+
/*
* Reproduce not-null constraints, if any, by copying them. We do this
* regardless of options given.
@@ -3860,6 +3932,7 @@ transformAlterTableStmt(Oid relid, AlterTableStmt *stmt,
cxt.fkconstraints = NIL;
cxt.ixconstraints = NIL;
cxt.likeclauses = NIL;
+ cxt.options = NIL;
cxt.blist = NIL;
cxt.alist = NIL;
cxt.pkey = NULL;
diff --git a/src/include/access/reloptions.h b/src/include/access/reloptions.h
index a604a4702c3..0c29f5df229 100644
--- a/src/include/access/reloptions.h
+++ b/src/include/access/reloptions.h
@@ -236,6 +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 bytea *extractRelOptions(HeapTuple tuple, TupleDesc tupdesc,
amoptions_function amoptions);
extern void *build_reloptions(Datum reloptions, bool validate,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index bc7adba4a0f..9e0296aee39 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -795,6 +795,7 @@ typedef enum TableLikeOption
CREATE_TABLE_LIKE_INDEXES = 1 << 6,
CREATE_TABLE_LIKE_STATISTICS = 1 << 7,
CREATE_TABLE_LIKE_STORAGE = 1 << 8,
+ CREATE_TABLE_LIKE_PARAMETERS = 1 << 9,
CREATE_TABLE_LIKE_ALL = PG_INT32_MAX
} TableLikeOption;
diff --git a/src/include/parser/kwlist.h b/src/include/parser/kwlist.h
index 9fde58f541c..aad64e7ccca 100644
--- a/src/include/parser/kwlist.h
+++ b/src/include/parser/kwlist.h
@@ -336,6 +336,7 @@ PG_KEYWORD("owned", OWNED, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("owner", OWNER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parallel", PARALLEL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parameter", PARAMETER, UNRESERVED_KEYWORD, BARE_LABEL)
+PG_KEYWORD("parameters", PARAMETERS, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("parser", PARSER, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partial", PARTIAL, UNRESERVED_KEYWORD, BARE_LABEL)
PG_KEYWORD("partition", PARTITION, UNRESERVED_KEYWORD, BARE_LABEL)
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index d3c35c14847..dd8928bb262 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -605,7 +605,8 @@ CREATE TABLE ctl_table(a int PRIMARY KEY,
b varchar COMPRESSION pglz,
c int GENERATED ALWAYS AS (a * 2) STORED,
d bigint GENERATED ALWAYS AS IDENTITY,
- e int DEFAULT 1);
+ e int DEFAULT 1)
+ WITH (fillfactor = 100);
CREATE INDEX ctl_table_a_key ON ctl_table(a);
COMMENT ON COLUMN ctl_table.b IS 'Column b';
CREATE STATISTICS ctl_table_stat ON a,b FROM ctl_table;
@@ -630,6 +631,7 @@ Statistics objects:
Not-null constraints:
"ctl_table_a_not_null" NOT NULL "a"
"ctl_table_d_not_null" NOT NULL "d"
+Options: fillfactor=100
-- Test EXCLUDING ALL
CREATE FOREIGN TABLE ctl_foreign_table1(LIKE ctl_table EXCLUDING ALL) SERVER ctl_s0;
@@ -661,7 +663,7 @@ SELECT attname, attcompression FROM pg_attribute
(5 rows)
-- Test INCLUDING ALL
--- INDEXES, IDENTITY, COMPRESSION, STORAGE are not copied.
+-- INDEXES, IDENTITY, COMPRESSION, STORAGE, PARAMETERS are not copied.
CREATE FOREIGN TABLE ctl_foreign_table2(LIKE ctl_table INCLUDING ALL) SERVER ctl_s0;
\d+ ctl_foreign_table2
Foreign table "public.ctl_foreign_table2"
@@ -699,3 +701,52 @@ DROP FOREIGN TABLE ctl_foreign_table1;
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); -- fail
+ERROR: parameter "fillfactor" specified more than once
+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
+--------+------+-----------+----------+---------+----------+--------------+-------------
+ a | text | | | | extended | |
+Options: fillfactor=100
+
+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, 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 \gx
+-[ RECORD 1 ]----+-----------------------------------------------------------------------------------------------------------------------------------------------------
+relname | t_sp1
+reloptions | {fillfactor=100}
+toast_reloptions |
+-[ 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_reloptions | {autovacuum_vacuum_threshold=101,autovacuum_vacuum_scale_factor=0.3,vacuum_index_cleanup=auto,vacuum_truncate=false,log_autovacuum_min_duration=100}
+
+CREATE MATERIALIZED VIEW mv_dummy WITH (fillfactor = 10) AS SELECT 1 AS a;
+CREATE TABLE t_sp3 (LIKE mv_dummy INCLUDING PARAMETERS);
+\d+ t_sp3
+ Table "public.t_sp3"
+ Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
+--------+---------+-----------+----------+---------+---------+--------------+-------------
+ a | integer | | | | plain | |
+Options: fillfactor=10
+
+DROP MATERIALIZED VIEW mv_dummy;
+DROP TABLE t_sp, t_sp1, t_sp2, t_sp3;
diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql
index 93389b57dbf..28f2d23f84c 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -249,7 +249,8 @@ CREATE TABLE ctl_table(a int PRIMARY KEY,
b varchar COMPRESSION pglz,
c int GENERATED ALWAYS AS (a * 2) STORED,
d bigint GENERATED ALWAYS AS IDENTITY,
- e int DEFAULT 1);
+ e int DEFAULT 1)
+ WITH (fillfactor = 100);
CREATE INDEX ctl_table_a_key ON ctl_table(a);
COMMENT ON COLUMN ctl_table.b IS 'Column b';
@@ -268,7 +269,7 @@ SELECT attname, attcompression FROM pg_attribute
WHERE attrelid = 'ctl_foreign_table1'::regclass and attnum > 0 ORDER BY attnum;
-- Test INCLUDING ALL
--- INDEXES, IDENTITY, COMPRESSION, STORAGE are not copied.
+-- INDEXES, IDENTITY, COMPRESSION, STORAGE, PARAMETERS are not copied.
CREATE FOREIGN TABLE ctl_foreign_table2(LIKE ctl_table INCLUDING ALL) SERVER ctl_s0;
\d+ ctl_foreign_table2
-- \d+ does not report the value of attcompression for a foreign table, so
@@ -280,3 +281,37 @@ DROP TABLE ctl_table;
DROP FOREIGN TABLE ctl_foreign_table1;
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_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 (
+ parallel_workers = 3,
+ toast.autovacuum_vacuum_threshold = 101,
+ toast.autovacuum_vacuum_scale_factor = 0.3);
+
+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 \gx
+
+CREATE MATERIALIZED VIEW mv_dummy WITH (fillfactor = 10) AS SELECT 1 AS a;
+
+CREATE TABLE t_sp3 (LIKE mv_dummy INCLUDING PARAMETERS);
+\d+ t_sp3
+
+DROP MATERIALIZED VIEW mv_dummy;
+DROP TABLE t_sp, t_sp1, t_sp2, t_sp3;
--
2.34.1
Hi,
current, v7 the DOC changes:
+ <varlistentry id="sql-createtable-parms-like-opt-storage-paramter">
+ <term><literal>INCLUDING PARAMETERS</literal></term>
+ <listitem>
+ <para>
+ All table parameters, such as the <literal>storage
parameter</literal>
+ settings of the source table, will be copied. For table storage
+ parameters, see <xref linkend="sql-createtable-storage-parameters"/>
+ below for more information.
+ </para>
+ </listitem>
+ </varlistentry>
https://www.postgresql.org/docs/current/sql-altertable.html#SQL-ALTERTABLE-DESC-SET-ATTRIBUTE-OPTION
Does the "parameter" (INCLUDING PARAMETERS) definition is close to "option"?
This raises the question of whether we should also copy column-level options to
the new table.
What do you think?
On Wed, Dec 17, 2025, at 3:41 AM, jian he wrote:
Does the "parameter" (INCLUDING PARAMETERS) definition is close to "option"?
This raises the question of whether we should also copy column-level options to
the new table.
I don't think so. The current attribute options (n_distinct and
n_distinct_inherited) are closely tied to the data. I'm afraid that this
decision would impose bad plans in the future.
--
Euler Taveira
EDB https://www.enterprisedb.com/