Thanks everyone for the comments!

On 13/02/2026 05:30, Tom Lane wrote:
> Chao Li <[email protected]> writes:
>> I feel the current behavior is proper. When you create a table using
>> LIKE, you are cloning the structure, not the identity.

The concern regarding identity is certainly a valid one, but in this
case I do not see how it applies. Copying the comment would not, IMHO,
transfer the identity of the source table (in a semantic sense), but
would instead merely indicate its provenance.

> Yeah, I was about to make a similar comment.  We do not for example
> clone the ownership or permissions of the source table.  Maybe there
> is an argument for cloning the table-level comment but it's by no
> means open-and-shut.  So I think the current behavior is intentional
> not an oversight.  Might be good to go find the thread in which the
> INCLUDING COMMENTS functionality was developed and see if there was
> discussion.

I did a bit of digging in the mailing list and found this old thread[1]
where INCLUDING COMMENTS was introduced. I couldn't see anything related
to table-level comments there. Perhaps it was discussed elsewhere?

On 13/02/2026 05:13, Fujii Masao wrote:
> For example, if two source tables each have a table comment and both are
> specified in LIKE, which comment should be applied to the new table?

On 13/02/2026 05:22, David G. Johnston wrote:
> Both, with a new line between them.

I supposed we could, as David mentioned, simply concatenate them. How it
should be done can be discussed, but a \n (or two) would IMO work just fine.

Example:

CREATE TABLE t1 (a int);
COMMENT ON TABLE t1 IS 'comment from table 1';
CREATE TABLE t2 (b int);
COMMENT ON TABLE t2 IS 'comment from table 2';
CREATE TABLE t3 (c int);
COMMENT ON TABLE t3 IS 'comment from table 3';

CREATE TABLE tm (
    LIKE t1 INCLUDING COMMENTS,
    LIKE t3 INCLUDING COMMENTS,
    LIKE t2 INCLUDING COMMENTS
);

SELECT obj_description('tm'::regclass, 'pg_class') AS table_comment;
    table_comment
----------------------
 comment from table 1+
 comment from table 3+
 comment from table 2
(1 row)


Any thoughts on that?

Best, Jim

1 -
https://www.postgresql.org/message-id/flat/20090907114058.C855.52131E4D%40oss.ntt.co.jp
From f49b512337b261c2824a4d495af68d39231c367e Mon Sep 17 00:00:00 2001
From: Jim Jones <[email protected]>
Date: Fri, 13 Feb 2026 09:30:31 +0100
Subject: [PATCH v2] Add table comments in CREATE TABLE LIKE INCLUDING COMMENTS

When using CREATE TABLE ... LIKE ... INCLUDING COMMENTS (or INCLUDING
ALL), table-level comments were not being copied to the new table, even
though column comments, constraint comments, index comments, and
statistics comments were properly copied. This patch extends the
feature to also copy the table's own comment to the target table.

When multiple LIKE clauses specify INCLUDING COMMENTS and the source
tables have table-level comments, the comments are now concatenated in
the target table, separated by newlines, in the order that the LIKE
clauses appear. This allows users to preserve comment information from
all source tables when creating tables that combine properties from
multiple sources.
---
 doc/src/sgml/ref/create_table.sgml            | 15 +++--
 src/backend/parser/parse_utilcmd.c            | 59 +++++++++++++++++--
 .../regress/expected/create_table_like.out    | 30 ++++++++++
 src/test/regress/sql/create_table_like.sql    | 21 +++++++
 4 files changed, 115 insertions(+), 10 deletions(-)

diff --git a/doc/src/sgml/ref/create_table.sgml b/doc/src/sgml/ref/create_table.sgml
index 77c5a763d4..223f1eac5c 100644
--- a/doc/src/sgml/ref/create_table.sgml
+++ b/doc/src/sgml/ref/create_table.sgml
@@ -672,10 +672,17 @@ WITH ( MODULUS <replaceable class="parameter">numeric_literal</replaceable>, REM
         <term><literal>INCLUDING COMMENTS</literal></term>
         <listitem>
          <para>
-          Comments for the copied columns, constraints, and indexes will be
-          copied.  The default behavior is to exclude comments, resulting in
-          the copied columns and constraints in the new table having no
-          comments.
+          Comments for the table itself and for the copied columns,
+          constraints, and indexes will be copied.  The default behavior is to
+          exclude comments, resulting in the new table and its copied columns
+          and constraints having no comments.
+         </para>
+         <para>
+          If multiple <literal>LIKE</literal> clauses specify
+          <literal>INCLUDING COMMENTS</literal> and the source tables have
+          table-level comments, the comments will be concatenated in the new
+          table, separated by newlines, in the order that the
+          <literal>LIKE</literal> clauses appear.
          </para>
         </listitem>
        </varlistentry>
diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c
index b5f4c72459..2c247312b9 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -43,6 +43,7 @@
 #include "commands/comment.h"
 #include "commands/defrem.h"
 #include "commands/sequence.h"
+#include "lib/stringinfo.h"
 #include "commands/tablecmds.h"
 #include "commands/tablespace.h"
 #include "miscadmin.h"
@@ -1307,17 +1308,18 @@ transformTableLikeClause(CreateStmtContext *cxt, TableLikeClause *table_like_cla
 	}
 
 	/*
-	 * We cannot yet deal with defaults, CHECK constraints, indexes, or
-	 * statistics, since we don't yet know what column numbers the copied
-	 * columns will have in the finished table.  If any of those options are
-	 * specified, add the LIKE clause to cxt->likeclauses so that
-	 * expandTableLikeClause will be called after we do know that.
+	 * We cannot yet deal with defaults, CHECK constraints, indexes,
+	 * statistics, or table comments, since we don't yet know what column
+	 * numbers the copied columns will have in the finished table.  If any of
+	 * those options are specified, add the LIKE clause to cxt->likeclauses
+	 * so that expandTableLikeClause will be called after we do know that.
 	 *
 	 * In order for this to work, we remember the relation OID so that
 	 * expandTableLikeClause is certain to open the same table.
 	 */
 	if (table_like_clause->options &
-		(CREATE_TABLE_LIKE_DEFAULTS |
+		(CREATE_TABLE_LIKE_COMMENTS |
+		 CREATE_TABLE_LIKE_DEFAULTS |
 		 CREATE_TABLE_LIKE_GENERATED |
 		 CREATE_TABLE_LIKE_CONSTRAINTS |
 		 CREATE_TABLE_LIKE_INDEXES |
@@ -1625,6 +1627,51 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause *table_like_clause)
 		list_free(parent_extstats);
 	}
 
+	/*
+	 * Copy comment on the relation itself, if requested.
+	 */
+	if (table_like_clause->options & CREATE_TABLE_LIKE_COMMENTS)
+	{
+		comment = GetComment(RelationGetRelid(relation), RelationRelationId, 0);
+
+		if (comment != NULL)
+		{
+			CommentStmt *stmt;
+			char *existing_comment;
+
+			/*
+			 * Check if the target table already has a comment from a previous
+			 * LIKE clause.  If so, append this comment to it with a newline
+			 * separator.
+			 */
+			existing_comment = GetComment(RelationGetRelid(childrel), RelationRelationId, 0);
+
+			stmt = makeNode(CommentStmt);
+			stmt->objtype = OBJECT_TABLE;
+			if (heapRel->schemaname)
+				stmt->object = (Node *)list_make2(makeString(heapRel->schemaname),
+												  makeString(heapRel->relname));
+			else
+				stmt->object = (Node *)list_make1(makeString(heapRel->relname));
+
+			/* Combine comments if there was a previous one */
+			if (existing_comment != NULL)
+			{
+				StringInfoData buf;
+
+				initStringInfo(&buf);
+				appendStringInfoString(&buf, existing_comment);
+				appendStringInfoChar(&buf, '\n');
+				appendStringInfoString(&buf, comment);
+				stmt->comment = buf.data;
+			}
+			else
+				stmt->comment = comment;
+
+			result = lappend(result, stmt);
+		}
+	}
+
 	/* Done with child rel */
 	table_close(childrel, NoLock);
 
diff --git a/src/test/regress/expected/create_table_like.out b/src/test/regress/expected/create_table_like.out
index d3c35c1484..e62ccd1ad5 100644
--- a/src/test/regress/expected/create_table_like.out
+++ b/src/test/regress/expected/create_table_like.out
@@ -341,6 +341,7 @@ CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text CHECK (length(c) < 7));
 ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL;
 ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN;
 CREATE INDEX ctlt3_fnidx ON ctlt3 ((a || c));
+COMMENT ON TABLE ctlt3 IS 'ctlt3 table comment';
 COMMENT ON COLUMN ctlt3.a IS 'A3';
 COMMENT ON COLUMN ctlt3.c IS 'C';
 COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check';
@@ -449,6 +450,35 @@ SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_con
  t3_a_check
 (1 row)
 
+SELECT obj_description('ctlt13_like'::regclass, 'pg_class') AS table_comment;
+    table_comment    
+---------------------
+ ctlt3 table comment
+(1 row)
+
+-- Test multiple LIKE clauses with table comments
+CREATE TABLE ctlt_comment1 (a int);
+COMMENT ON TABLE ctlt_comment1 IS 'table 1 comment';
+CREATE TABLE ctlt_comment2 (b int);
+COMMENT ON TABLE ctlt_comment2 IS 'table 2 comment';
+CREATE TABLE ctlt_comment3 (c int);
+COMMENT ON TABLE ctlt_comment3 IS 'table 3 comment';
+-- Multiple LIKE clauses should concatenate table comments
+CREATE TABLE ctlt_multi_comments (
+    LIKE ctlt_comment1 INCLUDING COMMENTS,
+    LIKE ctlt_comment3 INCLUDING COMMENTS,
+    LIKE ctlt_comment2 INCLUDING COMMENTS
+);
+-- The order of comments should be the same as the order of LIKE clauses
+SELECT obj_description('ctlt_multi_comments'::regclass, 'pg_class') AS table_comment;
+  table_comment  
+-----------------
+ table 1 comment+
+ table 3 comment+
+ table 2 comment
+(1 row)
+
+DROP TABLE ctlt_comment1, ctlt_comment2, ctlt_comment3, ctlt_multi_comments;
 CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
 \d+ ctlt_all
                                 Table "public.ctlt_all"
diff --git a/src/test/regress/sql/create_table_like.sql b/src/test/regress/sql/create_table_like.sql
index 93389b57db..903c51d062 100644
--- a/src/test/regress/sql/create_table_like.sql
+++ b/src/test/regress/sql/create_table_like.sql
@@ -153,6 +153,7 @@ CREATE TABLE ctlt3 (a text CHECK (length(a) < 5), c text CHECK (length(c) < 7));
 ALTER TABLE ctlt3 ALTER COLUMN c SET STORAGE EXTERNAL;
 ALTER TABLE ctlt3 ALTER COLUMN a SET STORAGE MAIN;
 CREATE INDEX ctlt3_fnidx ON ctlt3 ((a || c));
+COMMENT ON TABLE ctlt3 IS 'ctlt3 table comment';
 COMMENT ON COLUMN ctlt3.a IS 'A3';
 COMMENT ON COLUMN ctlt3.c IS 'C';
 COMMENT ON CONSTRAINT ctlt3_a_check ON ctlt3 IS 't3_a_check';
@@ -173,6 +174,26 @@ CREATE TABLE ctlt13_inh () INHERITS (ctlt1, ctlt3);
 CREATE TABLE ctlt13_like (LIKE ctlt3 INCLUDING CONSTRAINTS INCLUDING INDEXES INCLUDING COMMENTS INCLUDING STORAGE) INHERITS (ctlt1);
 \d+ ctlt13_like
 SELECT description FROM pg_description, pg_constraint c WHERE classoid = 'pg_constraint'::regclass AND objoid = c.oid AND c.conrelid = 'ctlt13_like'::regclass;
+SELECT obj_description('ctlt13_like'::regclass, 'pg_class') AS table_comment;
+
+-- Test multiple LIKE clauses with table comments
+CREATE TABLE ctlt_comment1 (a int);
+COMMENT ON TABLE ctlt_comment1 IS 'table 1 comment';
+CREATE TABLE ctlt_comment2 (b int);
+COMMENT ON TABLE ctlt_comment2 IS 'table 2 comment';
+CREATE TABLE ctlt_comment3 (c int);
+COMMENT ON TABLE ctlt_comment3 IS 'table 3 comment';
+
+
+-- Multiple LIKE clauses should concatenate table comments
+CREATE TABLE ctlt_multi_comments (
+    LIKE ctlt_comment1 INCLUDING COMMENTS,
+    LIKE ctlt_comment3 INCLUDING COMMENTS,
+    LIKE ctlt_comment2 INCLUDING COMMENTS
+);
+-- The order of comments should be the same as the order of LIKE clauses
+SELECT obj_description('ctlt_multi_comments'::regclass, 'pg_class') AS table_comment;
+DROP TABLE ctlt_comment1, ctlt_comment2, ctlt_comment3, ctlt_multi_comments;
 
 CREATE TABLE ctlt_all (LIKE ctlt1 INCLUDING ALL);
 \d+ ctlt_all
-- 
2.43.0

Reply via email to