On 06/02/26 12:46, I wrote:
More generally maybe start with the documentation to define exactly how it should behave and what limitations it would have (i.e., it isn’t going to
re-point schema references in black-box function bodies).

Good point, I'll also include some initial documentation changes on the next version.


So here is V2 with some documentation changes and also with the index name not being preserved issue that Marcos have mentioned earlier fixed.

I'm still working on the function support.

--
Matheus Alcantara
EDB: https://www.enterprisedb.com
From 1436d7a2758ef8913e09f34cdee8f2f13c0ad01b Mon Sep 17 00:00:00 2001
From: Matheus Alcantara <[email protected]>
Date: Tue, 3 Feb 2026 17:57:47 -0300
Subject: [PATCH v2 1/2] Add CREATE SCHEMA ... LIKE support

This patch introduces a new LIKE clause for CREATE SCHEMA that allows
copying table definitions from an existing schema to a new one.

Syntax:
  CREATE SCHEMA <name> LIKE <source_schema> [like_options...]

Where like_options is:
  { INCLUDING | EXCLUDING } { TABLE | INDEX | ALL }

The implementation reuses the existing CREATE TABLE ... LIKE
infrastructure to copy table structures, including columns, defaults,
constraints, and optionally indexes.
---
 doc/src/sgml/ref/create_schema.sgml         |  70 ++++++++-
 src/backend/commands/indexcmds.c            |   3 +-
 src/backend/commands/schemacmds.c           | 157 ++++++++++++++++++++
 src/backend/commands/tablecmds.c            |   4 +-
 src/backend/parser/gram.y                   |  53 +++++++
 src/backend/parser/parse_utilcmd.c          |  17 ++-
 src/include/nodes/parsenodes.h              |  17 +++
 src/include/parser/parse_utilcmd.h          |   3 +-
 src/test/regress/expected/create_schema.out | 109 ++++++++++++++
 src/test/regress/sql/create_schema.sql      |  68 +++++++++
 src/tools/pgindent/typedefs.list            |   1 +
 11 files changed, 492 insertions(+), 10 deletions(-)

diff --git a/doc/src/sgml/ref/create_schema.sgml 
b/doc/src/sgml/ref/create_schema.sgml
index ed69298ccc6..a96a43f9a31 100644
--- a/doc/src/sgml/ref/create_schema.sgml
+++ b/doc/src/sgml/ref/create_schema.sgml
@@ -22,6 +22,7 @@ PostgreSQL documentation
  <refsynopsisdiv>
 <synopsis>
 CREATE SCHEMA <replaceable class="parameter">schema_name</replaceable> [ 
AUTHORIZATION <replaceable class="parameter">role_specification</replaceable> ] 
[ <replaceable class="parameter">schema_element</replaceable> [ ... ] ]
+CREATE SCHEMA <replaceable class="parameter">schema_name</replaceable> [ IF 
NOT EXISTS ] LIKE <replaceable class="parameter">source_schema</replaceable> [ 
<replaceable class="parameter">like_option</replaceable> [ ... ] ]
 CREATE SCHEMA AUTHORIZATION <replaceable 
class="parameter">role_specification</replaceable> [ <replaceable 
class="parameter">schema_element</replaceable> [ ... ] ]
 CREATE SCHEMA IF NOT EXISTS <replaceable 
class="parameter">schema_name</replaceable> [ AUTHORIZATION <replaceable 
class="parameter">role_specification</replaceable> ]
 CREATE SCHEMA IF NOT EXISTS AUTHORIZATION <replaceable 
class="parameter">role_specification</replaceable>
@@ -32,6 +33,10 @@ CREATE SCHEMA IF NOT EXISTS AUTHORIZATION <replaceable 
class="parameter">role_sp
   | CURRENT_ROLE
   | CURRENT_USER
   | SESSION_USER
+
+<phrase>and <replaceable class="parameter">like_option</replaceable> can 
be:</phrase>
+
+    { INCLUDING | EXCLUDING } { TABLE | INDEX | ALL }
 </synopsis>
  </refsynopsisdiv>
 
@@ -120,6 +125,65 @@ CREATE SCHEMA IF NOT EXISTS AUTHORIZATION <replaceable 
class="parameter">role_sp
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><literal>LIKE <replaceable>source_schema</replaceable> [ 
<replaceable>like_option</replaceable> ... ]</literal></term>
+      <listitem>
+        <para>
+        The <literal>LIKE</literal> clause specifies a schema from which the 
new
+        schema automatically copies the definitions of all supported objects
+        (such as tables and indexes).
+        </para>
+
+        <para>
+        The new schema and the original schema are completely decoupled after
+        creation is complete. Changes to objects in the original schema will
+        not be applied to the new schema, and changes to the new schema will
+        not affect the original.
+        </para>
+
+        <para>
+        The optional <replaceable>like_option</replaceable> clauses specify
+        which additional properties of the original schema's objects to copy.
+        Specifying <literal>INCLUDING</literal> copies the object, while
+        specifying <literal>EXCLUDING</literal> omits the object.
+        <literal>EXCLUDING</literal> is the default. If multiple
+        specifications are made for the same kind of object, the last one
+        is used. The available options are:
+        </para>
+
+        <variablelist>
+         <varlistentry>
+          <term><literal>INCLUDING TABLE</literal></term>
+          <listitem>
+            <para>
+              Definitions of tables and their associated sequences for identity
+              columns are copied to the new schema. Note that for
+              <type>SERIAL</type> data types, the sequence is shared with the
+              source schema; a new sequence is not created for the new table,
+              meaning the new table's column will default to the original 
sequence.
+            </para>
+          </listitem>
+         </varlistentry>
+
+        <varlistentry>
+          <term><literal>INCLUDING INDEX</literal></term>
+          <listitem>
+            <para>
+              Indexes from the source schema will be created on the new tables.
+              However, this option only takes effect if <literal>INCLUDING 
TABLE</literal>
+              is also active. If tables are excluded (either by default or 
explicitly),
+              indexes will not be created, as they have no parent object to 
attach to.
+              For example, <literal>CREATE SCHEMA foo LIKE bar INCLUDING 
INDEX</literal>
+              or <literal>CREATE SCHEMA foo LIKE bar INCLUDING ALL EXCLUDING 
TABLE</literal>
+              will result in no indexes being created.
+            </para>
+          </listitem>
+        </varlistentry>
+
+        </variablelist>
+      </listitem>
+     </varlistentry>
     </variablelist>
  </refsect1>
 
@@ -128,8 +192,10 @@ CREATE SCHEMA IF NOT EXISTS AUTHORIZATION <replaceable 
class="parameter">role_sp
 
   <para>
    To create a schema, the invoking user must have the
-   <literal>CREATE</literal> privilege for the current database.
-   (Of course, superusers bypass this check.)
+   <literal>CREATE</literal> privilege for the current database. For
+   <literal>LIKE</literal> the user also must have the <literal>USAGE</literal>
+   privilege on the source schema.
+   (Of course, superusers bypass these checks.)
   </para>
  </refsect1>
 
diff --git a/src/backend/commands/indexcmds.c b/src/backend/commands/indexcmds.c
index 635679cc1f2..eeac9919fdd 100644
--- a/src/backend/commands/indexcmds.c
+++ b/src/backend/commands/indexcmds.c
@@ -1515,7 +1515,8 @@ DefineIndex(ParseState *pstate,
                                        childStmt = 
generateClonedIndexStmt(NULL,
                                                                                
                                parentIndex,
                                                                                
                                attmap,
-                                                                               
                                NULL);
+                                                                               
                                NULL,
+                                                                               
                                false);
 
                                        /*
                                         * Recurse as the starting user ID.  
Callee will use that
diff --git a/src/backend/commands/schemacmds.c 
b/src/backend/commands/schemacmds.c
index 4c51e920626..e7cb7ec898d 100644
--- a/src/backend/commands/schemacmds.c
+++ b/src/backend/commands/schemacmds.c
@@ -14,6 +14,7 @@
  */
 #include "postgres.h"
 
+#include "access/genam.h"
 #include "access/htup_details.h"
 #include "access/table.h"
 #include "access/xact.h"
@@ -27,17 +28,140 @@
 #include "catalog/pg_namespace.h"
 #include "commands/event_trigger.h"
 #include "commands/schemacmds.h"
+#include "commands/tablespace.h"
 #include "miscadmin.h"
+#include "nodes/makefuncs.h"
 #include "parser/parse_utilcmd.h"
 #include "parser/scansup.h"
 #include "tcop/utility.h"
 #include "utils/acl.h"
 #include "utils/builtins.h"
+#include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/syscache.h"
 
 static void AlterSchemaOwner_internal(HeapTuple tup, Relation rel, Oid 
newOwnerId);
+static List *collectSchemaTablesLike(Oid srcNspOid, const char *newSchemaName,
+                                                                        bits32 
options);
+
+/*
+ * Subroutine for CREATE SCHEMA LIKE.
+ *
+ * It return a list of CreateStmt statements for tables that are on source
+ * schema that should be created on target schema.
+ *
+ * It uses CREATE TABLE ... LIKE existing infrastructure.
+ */
+static List *
+collectSchemaTablesLike(Oid srcNspOid, const char *newSchemaName,
+                                               bits32 options)
+{
+       List       *result = NIL;
+       bool            preserveIndexNames = false;
+       Relation        pg_class;
+       SysScanDesc scan;
+       ScanKeyData key;
+       HeapTuple       tuple;
+       bits32          tableOptions;
+
+       /*
+        * Determine CREATE TABLE LIKE options. We copy most properties by
+        * default, but indexes are controlled by the CREATE_SCHEMA_LIKE_INDEX
+        * option.
+        */
+       tableOptions = CREATE_TABLE_LIKE_COMMENTS |
+               CREATE_TABLE_LIKE_COMPRESSION |
+               CREATE_TABLE_LIKE_CONSTRAINTS |
+               CREATE_TABLE_LIKE_DEFAULTS |
+               CREATE_TABLE_LIKE_GENERATED |
+               CREATE_TABLE_LIKE_IDENTITY |
+               CREATE_TABLE_LIKE_STATISTICS |
+               CREATE_TABLE_LIKE_STORAGE;
+
+       /*
+        * If indexes are enable to be created we want to preserve the index 
names
+        * for the new schema.
+        */
+       if (options & CREATE_SCHEMA_LIKE_INDEX)
+       {
+               tableOptions |= CREATE_TABLE_LIKE_INDEXES;
+               preserveIndexNames = true;
+       }
+
+       pg_class = table_open(RelationRelationId, AccessShareLock);
+
+       /*
+        * Scan the pg_class filtering relations of source schema that need to 
be
+        * created on the target schema.
+        */
+       ScanKeyInit(&key,
+                               Anum_pg_class_relnamespace,
+                               BTEqualStrategyNumber, F_OIDEQ,
+                               ObjectIdGetDatum(srcNspOid));
+
+       scan = systable_beginscan(pg_class, ClassNameNspIndexId, true,
+                                                         NULL, 1, &key);
+
+       while (HeapTupleIsValid(tuple = systable_getnext(scan)))
+       {
+               Form_pg_class classForm = (Form_pg_class) GETSTRUCT(tuple);
+               CreateStmt *createStmt;
+               TableLikeClause *likeClause;
+               RangeVar   *newRelation;
+               RangeVar   *sourceRelation;
+
+               /* Only process regular and partitioned tables */
+               if (classForm->relkind != RELKIND_RELATION &&
+                       classForm->relkind != RELKIND_PARTITIONED_TABLE)
+                       continue;
+
+               createStmt = makeNode(CreateStmt);
+               likeClause = makeNode(TableLikeClause);
+
+               /* Target table in new schema */
+               newRelation = makeRangeVar(pstrdup(newSchemaName),
+                                                                  
pstrdup(NameStr(classForm->relname)),
+                                                                  -1);
+
+               /* Source table reference */
+               sourceRelation = makeRangeVar(get_namespace_name(srcNspOid),
+                                                                         
pstrdup(NameStr(classForm->relname)),
+                                                                         -1);
+
+               likeClause->relation = sourceRelation;
+               likeClause->options = tableOptions;
+               likeClause->relationOid = InvalidOid;
+               likeClause->preserveIndexNames = preserveIndexNames;
+
+               createStmt->relation = newRelation;
+               createStmt->tableElts = list_make1(likeClause);
+               createStmt->inhRelations = NIL;
+               createStmt->partbound = NULL;
+               createStmt->partspec = NULL;
+               createStmt->ofTypename = NULL;
+               createStmt->constraints = NIL;
+               createStmt->nnconstraints = NIL;
+               createStmt->options = NIL;
+               createStmt->oncommit = ONCOMMIT_NOOP;
+
+               /*
+                * XXX: Should we have INCLUDING TABLESPACE? If not, should we 
use the
+                * same tablespace of source table?
+                */
+               createStmt->tablespacename = NULL;
+               createStmt->accessMethod = NULL;
+               createStmt->if_not_exists = false;
+
+
+               result = lappend(result, createStmt);
+       }
+
+       systable_endscan(scan);
+       table_close(pg_class, AccessShareLock);
+
+       return result;
+}
 
 /*
  * CREATE SCHEMA
@@ -187,6 +311,39 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char 
*queryString,
        EventTriggerCollectSimpleCommand(address, InvalidObjectAddress,
                                                                         (Node 
*) stmt);
 
+       /*
+        * Process LIKE clause if present.  We collect objects from the source
+        * schema and append them to the schema elements list.
+        */
+       if (stmt->like_clause != NULL)
+       {
+               SchemaLikeClause *like = stmt->like_clause;
+               List       *like_stmts = NIL;
+               Oid                     srcNspOid;
+               AclResult       like_aclresult;
+
+               /* Look up source schema */
+               srcNspOid = get_namespace_oid(like->schemaname, false);
+
+               /* Check permission to read from source schema */
+               like_aclresult = object_aclcheck(NamespaceRelationId, srcNspOid,
+                                                                               
 GetUserId(), ACL_USAGE);
+               if (like_aclresult != ACLCHECK_OK)
+                       aclcheck_error(like_aclresult, OBJECT_SCHEMA, 
like->schemaname);
+
+               /* Collect tables if requested */
+               if ((like->options & CREATE_SCHEMA_LIKE_TABLE) ||
+                       (like->options == CREATE_SCHEMA_LIKE_ALL))
+               {
+                       like_stmts = collectSchemaTablesLike(srcNspOid,
+                                                                               
                 schemaName,
+                                                                               
                 like->options);
+               }
+
+               /* Append LIKE-generated statements to explicit schema elements 
*/
+               stmt->schemaElts = list_concat(like_stmts, stmt->schemaElts);
+       }
+
        /*
         * Examine the list of commands embedded in the CREATE SCHEMA command, 
and
         * reorganize them into a sequentially executable order with no forward
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index f976c0e5c7e..8010fe71a40 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -1301,7 +1301,7 @@ DefineRelation(CreateStmt *stmt, char relkind, Oid 
ownerId,
                                                                                
   false);
                        idxstmt =
                                generateClonedIndexStmt(NULL, idxRel,
-                                                                               
attmap, &constraintOid);
+                                                                               
attmap, &constraintOid, false);
                        DefineIndex(NULL,
                                                RelationGetRelid(rel),
                                                idxstmt,
@@ -20771,7 +20771,7 @@ AttachPartitionEnsureIndexes(List **wqueue, Relation 
rel, Relation attachrel)
 
                        stmt = generateClonedIndexStmt(NULL,
                                                                                
   idxRel, attmap,
-                                                                               
   &conOid);
+                                                                               
   &conOid, false);
                        DefineIndex(NULL,
                                                RelationGetRelid(attachrel), 
stmt, InvalidOid,
                                                RelationGetRelid(idxRel),
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index 713ee5c10a2..0c23b23561d 100644
--- a/src/backend/parser/gram.y
+++ b/src/backend/parser/gram.y
@@ -600,6 +600,7 @@ static Node *makeRecursiveViewSelect(char *relname, List 
*aliases, Node *query);
 
 %type <node>   DomainConstraint TableConstraint TableLikeClause
 %type <ival>   TableLikeOptionList TableLikeOption
+%type <ival>   SchemaLikeOptionList SchemaLikeOption
 %type <str>            column_compression opt_column_compression 
column_storage opt_column_storage
 %type <list>   ColQualList
 %type <node>   ColConstraint ColConstraintElem ConstraintAttr
@@ -1570,6 +1571,7 @@ CreateSchemaStmt:
                                        n->authrole = $5;
                                        n->schemaElts = $6;
                                        n->if_not_exists = false;
+                                       n->like_clause = NULL;
                                        $$ = (Node *) n;
                                }
                        | CREATE SCHEMA ColId OptSchemaEltList
@@ -1581,6 +1583,7 @@ CreateSchemaStmt:
                                        n->authrole = NULL;
                                        n->schemaElts = $4;
                                        n->if_not_exists = false;
+                                       n->like_clause = NULL;
                                        $$ = (Node *) n;
                                }
                        | CREATE SCHEMA IF_P NOT EXISTS opt_single_name 
AUTHORIZATION RoleSpec OptSchemaEltList
@@ -1597,6 +1600,7 @@ CreateSchemaStmt:
                                                                 
parser_errposition(@9)));
                                        n->schemaElts = $9;
                                        n->if_not_exists = true;
+                                       n->like_clause = NULL;
                                        $$ = (Node *) n;
                                }
                        | CREATE SCHEMA IF_P NOT EXISTS ColId OptSchemaEltList
@@ -1613,6 +1617,40 @@ CreateSchemaStmt:
                                                                 
parser_errposition(@7)));
                                        n->schemaElts = $7;
                                        n->if_not_exists = true;
+                                       n->like_clause = NULL;
+                                       $$ = (Node *) n;
+                               }
+                       /* CREATE SCHEMA ... LIKE variants */
+                       | CREATE SCHEMA ColId LIKE ColId SchemaLikeOptionList
+                               {
+                                       CreateSchemaStmt *n = 
makeNode(CreateSchemaStmt);
+                                       SchemaLikeClause *l = 
makeNode(SchemaLikeClause);
+
+                                       n->schemaname = $3;
+                                       n->authrole = NULL;
+                                       n->schemaElts = NIL;
+                                       n->if_not_exists = false;
+
+                                       l->schemaname = $5;
+                                       l->options = $6;
+                                       n->like_clause = l;
+
+                                       $$ = (Node *) n;
+                               }
+                       | CREATE SCHEMA IF_P NOT EXISTS ColId LIKE ColId 
SchemaLikeOptionList
+                               {
+                                       CreateSchemaStmt *n = 
makeNode(CreateSchemaStmt);
+                                       SchemaLikeClause *l = 
makeNode(SchemaLikeClause);
+
+                                       n->schemaname = $6;
+                                       n->authrole = NULL;
+                                       n->schemaElts = NIL;
+                                       n->if_not_exists = true;
+
+                                       l->schemaname = $8;
+                                       l->options = $9;
+                                       n->like_clause = l;
+
                                        $$ = (Node *) n;
                                }
                ;
@@ -1639,6 +1677,21 @@ schema_stmt:
                        | ViewStmt
                ;
 
+/*
+ * Options for CREATE SCHEMA ... LIKE
+ */
+SchemaLikeOptionList:
+                       SchemaLikeOptionList INCLUDING SchemaLikeOption         
{ $$ = $1 | $3; }
+                       | SchemaLikeOptionList EXCLUDING SchemaLikeOption       
{ $$ = $1 & ~$3; }
+                       | /* EMPTY */                                           
                                { $$ = 0; }
+               ;
+
+SchemaLikeOption:
+                       TABLE                   { $$ = 
CREATE_SCHEMA_LIKE_TABLE; }
+                       | INDEX                 { $$ = 
CREATE_SCHEMA_LIKE_INDEX; }
+                       | ALL                   { $$ = CREATE_SCHEMA_LIKE_ALL; }
+               ;
+
 
 /*****************************************************************************
  *
diff --git a/src/backend/parser/parse_utilcmd.c 
b/src/backend/parser/parse_utilcmd.c
index b5f4c72459d..b3188d87209 100644
--- a/src/backend/parser/parse_utilcmd.c
+++ b/src/backend/parser/parse_utilcmd.c
@@ -1567,7 +1567,8 @@ expandTableLikeClause(RangeVar *heapRel, TableLikeClause 
*table_like_clause)
                        index_stmt = generateClonedIndexStmt(heapRel,
                                                                                
                 parent_index,
                                                                                
                 attmap,
-                                                                               
                 NULL);
+                                                                               
                 NULL,
+                                                                               
                 table_like_clause->preserveIndexNames);
 
                        /* Copy comment on index, if requested */
                        if (table_like_clause->options & 
CREATE_TABLE_LIKE_COMMENTS)
@@ -1694,7 +1695,8 @@ transformOfType(CreateStmtContext *cxt, TypeName 
*ofTypename)
 IndexStmt *
 generateClonedIndexStmt(RangeVar *heapRel, Relation source_idx,
                                                const AttrMap *attmap,
-                                               Oid *constraintOid)
+                                               Oid *constraintOid,
+                                               bool preserveIndexName)
 {
        Oid                     source_relid = RelationGetRelid(source_idx);
        HeapTuple       ht_idxrel;
@@ -1771,12 +1773,19 @@ generateClonedIndexStmt(RangeVar *heapRel, Relation 
source_idx,
        index->reset_default_tblspc = false;
 
        /*
-        * We don't try to preserve the name of the source index; instead, just
+        * By default, we don't preserve the name of the source index; instead,
         * let DefineIndex() choose a reasonable name.  (If we tried to preserve
         * the name, we'd get duplicate-relation-name failures unless the source
         * table was in a different schema.)
+        *
+        * However, when preserveIndexName is true (e.g., when copying tables
+        * between schemas via CREATE SCHEMA ... LIKE), we preserve the original
+        * name since it won't conflict in the different namespace.
         */
-       index->idxname = NULL;
+       if (preserveIndexName)
+               index->idxname = pstrdup(NameStr(idxrelrec->relname));
+       else
+               index->idxname = NULL;
 
        /*
         * If the index is marked PRIMARY or has an exclusion condition, it's
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index 646d6ced763..2171c778f84 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -782,6 +782,8 @@ typedef struct TableLikeClause
        RangeVar   *relation;
        bits32          options;                /* OR of TableLikeOption flags 
*/
        Oid                     relationOid;    /* If table has been looked up, 
its OID */
+       bool            preserveIndexNames; /* true if index name should be 
preserved
+                                                                        * for 
the new table */
 } TableLikeClause;
 
 typedef enum TableLikeOption
@@ -2411,6 +2413,20 @@ typedef enum ObjectType
  * executed after the schema itself is created.
  * ----------------------
  */
+typedef struct SchemaLikeClause
+{
+       NodeTag         type;
+       char       *schemaname;         /* source schema name */
+       bits32          options;                /* OR of SchemaLikeOption flags 
*/
+} SchemaLikeClause;
+
+typedef enum SchemaLikeOption
+{
+       CREATE_SCHEMA_LIKE_TABLE = 1 << 0,
+       CREATE_SCHEMA_LIKE_INDEX = 1 << 1,
+       CREATE_SCHEMA_LIKE_ALL = PG_INT32_MAX
+} SchemaLikeOption;
+
 typedef struct CreateSchemaStmt
 {
        NodeTag         type;
@@ -2418,6 +2434,7 @@ typedef struct CreateSchemaStmt
        RoleSpec   *authrole;           /* the owner of the created schema */
        List       *schemaElts;         /* schema components (list of 
parsenodes) */
        bool            if_not_exists;  /* just do nothing if schema already 
exists? */
+       SchemaLikeClause *like_clause;  /* LIKE clause if present, else NULL */
 } CreateSchemaStmt;
 
 typedef enum DropBehavior
diff --git a/src/include/parser/parse_utilcmd.h 
b/src/include/parser/parse_utilcmd.h
index 53f282f77ec..6841d7b16bc 100644
--- a/src/include/parser/parse_utilcmd.h
+++ b/src/include/parser/parse_utilcmd.h
@@ -39,6 +39,7 @@ extern List *expandTableLikeClause(RangeVar *heapRel,
 extern IndexStmt *generateClonedIndexStmt(RangeVar *heapRel,
                                                                                
  Relation source_idx,
                                                                                
  const AttrMap *attmap,
-                                                                               
  Oid *constraintOid);
+                                                                               
  Oid *constraintOid,
+                                                                               
  bool preserveIndexName);
 
 #endif                                                 /* PARSE_UTILCMD_H */
diff --git a/src/test/regress/expected/create_schema.out 
b/src/test/regress/expected/create_schema.out
index 93302a07efc..f2760245f0c 100644
--- a/src/test/regress/expected/create_schema.out
+++ b/src/test/regress/expected/create_schema.out
@@ -94,5 +94,114 @@ CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE
 DROP SCHEMA regress_schema_1 CASCADE;
 NOTICE:  drop cascades to table regress_schema_1.tab
 RESET ROLE;
+--
+-- CREATE SCHEMA ... LIKE tests
+--
+-- Create a source schema with various objects
+CREATE SCHEMA regress_source_schema;
+CREATE TABLE regress_source_schema.t1 (
+    id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+    name text NOT NULL,
+    created_at timestamp DEFAULT now()
+);
+CREATE TABLE regress_source_schema.t2 (
+    id int REFERENCES regress_source_schema.t1(id),
+    data jsonb
+);
+CREATE INDEX idx_t1_name ON regress_source_schema.t1(name);
+CREATE INDEX idx_t2_data ON regress_source_schema.t2 USING gin(data);
+-- Test basic LIKE with TABLE
+CREATE SCHEMA regress_copy1 LIKE regress_source_schema INCLUDING TABLE;
+-- Verify tables were copied
+SELECT table_name FROM information_schema.tables
+WHERE table_schema = 'regress_copy1' ORDER BY table_name;
+ table_name 
+------------
+ t1
+ t2
+(2 rows)
+
+-- Verify table structure (should have columns but not indexes)
+\d regress_copy1.t1
+                                    Table "regress_copy1.t1"
+   Column   |            Type             | Collation | Nullable |           
Default            
+------------+-----------------------------+-----------+----------+------------------------------
+ id         | integer                     |           | not null | generated 
always as identity
+ name       | text                        |           | not null | 
+ created_at | timestamp without time zone |           |          | now()
+
+-- Test LIKE with TABLE and INDEX
+CREATE SCHEMA regress_copy2 LIKE regress_source_schema INCLUDING TABLE 
INCLUDING INDEX;
+-- Verify indexes were copied (the name should be the same)
+SELECT indexname FROM pg_indexes WHERE schemaname = 'regress_copy2' ORDER BY 
indexname;
+  indexname  
+-------------
+ idx_t1_name
+ idx_t2_data
+ t1_pkey
+(3 rows)
+
+-- Verify table structure (should have columns and indexes)
+\d regress_copy2.t1
+                                    Table "regress_copy2.t1"
+   Column   |            Type             | Collation | Nullable |           
Default            
+------------+-----------------------------+-----------+----------+------------------------------
+ id         | integer                     |           | not null | generated 
always as identity
+ name       | text                        |           | not null | 
+ created_at | timestamp without time zone |           |          | now()
+Indexes:
+    "t1_pkey" PRIMARY KEY, btree (id)
+    "idx_t1_name" btree (name)
+
+-- Test EXCLUDING option
+CREATE SCHEMA regress_copy3 LIKE regress_source_schema INCLUDING ALL EXCLUDING 
INDEX;
+-- Should have tables but no indexes
+SELECT table_name FROM information_schema.tables
+WHERE table_schema = 'regress_copy3' ORDER BY table_name;
+ table_name 
+------------
+ t1
+ t2
+(2 rows)
+
+SELECT indexname FROM pg_indexes WHERE schemaname = 'regress_copy3' ORDER BY 
indexname;
+ indexname 
+-----------
+(0 rows)
+
+-- Test IF NOT EXISTS with LIKE
+CREATE SCHEMA IF NOT EXISTS regress_copy1 LIKE regress_source_schema INCLUDING 
TABLE;
+NOTICE:  schema "regress_copy1" already exists, skipping
+-- Test empty source schema
+CREATE SCHEMA regress_empty_source;
+CREATE SCHEMA regress_copy4 LIKE regress_empty_source INCLUDING ALL;
+SELECT table_name FROM information_schema.tables
+WHERE table_schema = 'regress_copy4' ORDER BY table_name;
+ table_name 
+------------
+(0 rows)
+
+-- Test source schema does not exist
+CREATE SCHEMA regress_copy_fail LIKE nonexistent_schema INCLUDING TABLE;
+ERROR:  schema "nonexistent_schema" does not exist
+-- Clean up LIKE tests
+DROP SCHEMA regress_copy1 CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to table regress_copy1.t1
+drop cascades to table regress_copy1.t2
+DROP SCHEMA regress_copy2 CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to table regress_copy2.t1
+drop cascades to table regress_copy2.t2
+DROP SCHEMA regress_copy3 CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to table regress_copy3.t1
+drop cascades to table regress_copy3.t2
+DROP SCHEMA regress_copy4 CASCADE;
+DROP SCHEMA regress_empty_source CASCADE;
+DROP SCHEMA regress_source_schema CASCADE;
+NOTICE:  drop cascades to 2 other objects
+DETAIL:  drop cascades to table regress_source_schema.t1
+drop cascades to table regress_source_schema.t2
 -- Clean up
 DROP ROLE regress_create_schema_role;
diff --git a/src/test/regress/sql/create_schema.sql 
b/src/test/regress/sql/create_schema.sql
index 1b7064247a1..39a35e95150 100644
--- a/src/test/regress/sql/create_schema.sql
+++ b/src/test/regress/sql/create_schema.sql
@@ -66,5 +66,73 @@ CREATE SCHEMA regress_schema_1 AUTHORIZATION CURRENT_ROLE
 DROP SCHEMA regress_schema_1 CASCADE;
 RESET ROLE;
 
+--
+-- CREATE SCHEMA ... LIKE tests
+--
+
+-- Create a source schema with various objects
+CREATE SCHEMA regress_source_schema;
+
+CREATE TABLE regress_source_schema.t1 (
+    id int GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
+    name text NOT NULL,
+    created_at timestamp DEFAULT now()
+);
+
+CREATE TABLE regress_source_schema.t2 (
+    id int REFERENCES regress_source_schema.t1(id),
+    data jsonb
+);
+
+CREATE INDEX idx_t1_name ON regress_source_schema.t1(name);
+CREATE INDEX idx_t2_data ON regress_source_schema.t2 USING gin(data);
+
+-- Test basic LIKE with TABLE
+CREATE SCHEMA regress_copy1 LIKE regress_source_schema INCLUDING TABLE;
+
+-- Verify tables were copied
+SELECT table_name FROM information_schema.tables
+WHERE table_schema = 'regress_copy1' ORDER BY table_name;
+
+-- Verify table structure (should have columns but not indexes)
+\d regress_copy1.t1
+
+-- Test LIKE with TABLE and INDEX
+CREATE SCHEMA regress_copy2 LIKE regress_source_schema INCLUDING TABLE 
INCLUDING INDEX;
+
+-- Verify indexes were copied (the name should be the same)
+SELECT indexname FROM pg_indexes WHERE schemaname = 'regress_copy2' ORDER BY 
indexname;
+
+-- Verify table structure (should have columns and indexes)
+\d regress_copy2.t1
+
+-- Test EXCLUDING option
+CREATE SCHEMA regress_copy3 LIKE regress_source_schema INCLUDING ALL EXCLUDING 
INDEX;
+
+-- Should have tables but no indexes
+SELECT table_name FROM information_schema.tables
+WHERE table_schema = 'regress_copy3' ORDER BY table_name;
+SELECT indexname FROM pg_indexes WHERE schemaname = 'regress_copy3' ORDER BY 
indexname;
+
+-- Test IF NOT EXISTS with LIKE
+CREATE SCHEMA IF NOT EXISTS regress_copy1 LIKE regress_source_schema INCLUDING 
TABLE;
+
+-- Test empty source schema
+CREATE SCHEMA regress_empty_source;
+CREATE SCHEMA regress_copy4 LIKE regress_empty_source INCLUDING ALL;
+SELECT table_name FROM information_schema.tables
+WHERE table_schema = 'regress_copy4' ORDER BY table_name;
+
+-- Test source schema does not exist
+CREATE SCHEMA regress_copy_fail LIKE nonexistent_schema INCLUDING TABLE;
+
+-- Clean up LIKE tests
+DROP SCHEMA regress_copy1 CASCADE;
+DROP SCHEMA regress_copy2 CASCADE;
+DROP SCHEMA regress_copy3 CASCADE;
+DROP SCHEMA regress_copy4 CASCADE;
+DROP SCHEMA regress_empty_source CASCADE;
+DROP SCHEMA regress_source_schema CASCADE;
+
 -- Clean up
 DROP ROLE regress_create_schema_role;
diff --git a/src/tools/pgindent/typedefs.list b/src/tools/pgindent/typedefs.list
index 7619845fba9..cf86b26558d 100644
--- a/src/tools/pgindent/typedefs.list
+++ b/src/tools/pgindent/typedefs.list
@@ -2716,6 +2716,7 @@ ScanKeywordList
 ScanState
 ScanTypeControl
 ScannerCallbackState
+SchemaLikeClause
 SchemaQuery
 SearchPathCacheEntry
 SearchPathCacheKey
-- 
2.52.0

Reply via email to