From f49b512337b261c2824a4d495af68d39231c367e Mon Sep 17 00:00:00 2001
From: Jim Jones <jim.jones@uni-muenster.de>
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

