From 1a255ed67ae9a020c20ad0c3297b91e40ec7ddd0 Mon Sep 17 00:00:00 2001 From: jian he Date: Tue, 10 Mar 2026 17:53:47 +0800 Subject: [PATCH v10 3/5] CREATE SCHEMA CREATE COLLATION The SQL standard allows collation to be specified in a CREATE SCHEMA statement. This adds support for that capability. For example: CREATE SCHEMA schema_name CREATE COLLATION coll_icu_und FROM "und-x-icu"; The collation will be created within the to be created schema. The collation name can be schema-qualified or database-qualified, however it's not allowed to let collation create within a different schema. Discussion: https://postgr.es/m/CALdSSPh4jUSDsWu3K58hjO60wnTRR0DuO4CKRcwa8EVuOSfXxg@mail.gmail.com Commitfest: https://commitfest.postgresql.org/patch/5985 --- doc/src/sgml/ref/create_schema.sgml | 3 +- src/backend/catalog/objectaddress.c | 18 ++++++++ src/backend/parser/gram.y | 1 + src/backend/parser/parse_utilcmd.c | 40 +++++++++++++++++ src/include/catalog/objectaddress.h | 1 + .../expected/create_schema.out | 7 ++- .../test_ddl_deparse/sql/create_schema.sql | 3 +- .../regress/expected/collate.icu.utf8.out | 15 +++++++ src/test/regress/expected/create_schema.out | 43 +++++++++++++++++++ src/test/regress/sql/collate.icu.utf8.sql | 7 +++ src/test/regress/sql/create_schema.sql | 29 +++++++++++++ 11 files changed, 163 insertions(+), 4 deletions(-) diff --git a/doc/src/sgml/ref/create_schema.sgml b/doc/src/sgml/ref/create_schema.sgml index 79186d2b936..d8273bb2d0c 100644 --- a/doc/src/sgml/ref/create_schema.sgml +++ b/doc/src/sgml/ref/create_schema.sgml @@ -100,7 +100,8 @@ CREATE SCHEMA IF NOT EXISTS AUTHORIZATION role_sp An SQL statement defining an object to be created within the - schema. Currently, only CREATE DOMAIN + schema. Currently, only CREATE COLLATION, + CREATE DOMAIN CREATE TABLE, CREATE VIEW, CREATE INDEX, CREATE SEQUENCE, CREATE TRIGGER and GRANT are accepted as clauses diff --git a/src/backend/catalog/objectaddress.c b/src/backend/catalog/objectaddress.c index d32aaff2821..9bd5c76c016 100644 --- a/src/backend/catalog/objectaddress.c +++ b/src/backend/catalog/objectaddress.c @@ -2622,6 +2622,24 @@ read_objtype_from_string(const char *objtype) return -1; /* keep compiler quiet */ } +/* get the ObjectType name */ +const char * +stringify_objtype(ObjectType objtype) +{ + for (int i = 0; i < lengthof(ObjectTypeMap); i++) + { + if (ObjectTypeMap[i].tm_type == objtype) + return ObjectTypeMap[i].tm_name; + } + + ereport(ERROR, + errcode(ERRCODE_INVALID_PARAMETER_VALUE), + errmsg("unrecognized object type %d", objtype)); + + return NULL; /* keep compiler quiet */ +} + + /* * Interfaces to reference fields of ObjectPropertyType */ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1a9b3759376..e867d1d5661 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -1641,6 +1641,7 @@ schema_stmt: | GrantStmt | ViewStmt | CreateDomainStmt + | DefineStmt ; diff --git a/src/backend/parser/parse_utilcmd.c b/src/backend/parser/parse_utilcmd.c index 9b4a209378d..723a017a4cb 100644 --- a/src/backend/parser/parse_utilcmd.c +++ b/src/backend/parser/parse_utilcmd.c @@ -64,6 +64,7 @@ #include "rewrite/rewriteManip.h" #include "utils/acl.h" #include "utils/builtins.h" +#include "utils/formatting.h" #include "utils/lsyscache.h" #include "utils/partcache.h" #include "utils/rel.h" @@ -4490,6 +4491,45 @@ transformCreateSchemaStmtElements(ParseState *pstate, List *schemaElts, } break; + case T_DefineStmt: + { + char *coll_schema = NULL; + char *collName; + char *obj_type; + + DefineStmt *stmt = castNode(DefineStmt, element); + + obj_type = asc_toupper(stringify_objtype(stmt->kind), + strlen(stringify_objtype(stmt->kind))); + + if (stmt->kind != OBJECT_COLLATION) + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE SCHEMA ... CREATE %s currently not supported", obj_type)); + + DeconstructQualifiedName(stmt->defnames, &coll_schema, &collName); + + if (coll_schema && strcmp(coll_schema, schemaName) != 0) + ereport(ERROR, + errcode(ERRCODE_INVALID_SCHEMA_DEFINITION), + errmsg("CREATE %s specifies a schema (%s) different from the one being created (%s)", + obj_type, coll_schema, schemaName)); + + elements = lappend(elements, element); + } + break; + + /* + * gram.y classifies these as DefineStmt as well; therefore, + * we must explicitly raise an error for these cases. + */ + case T_CompositeTypeStmt: + case T_CreateEnumStmt: + case T_CreateRangeStmt: + ereport(ERROR, + errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("CREATE SCHEMA ... CREATE TYPE currently not supported")); + break; default: elog(ERROR, "unrecognized node type: %d", (int) nodeTag(element)); diff --git a/src/include/catalog/objectaddress.h b/src/include/catalog/objectaddress.h index b549be2d523..20d52ee589a 100644 --- a/src/include/catalog/objectaddress.h +++ b/src/include/catalog/objectaddress.h @@ -80,6 +80,7 @@ extern char *getObjectDescription(const ObjectAddress *object, extern char *getObjectDescriptionOids(Oid classid, Oid objid); extern int read_objtype_from_string(const char *objtype); +extern const char *stringify_objtype(ObjectType objtype); extern char *getObjectTypeDescription(const ObjectAddress *object, bool missing_ok); extern char *getObjectIdentity(const ObjectAddress *object, diff --git a/src/test/modules/test_ddl_deparse/expected/create_schema.out b/src/test/modules/test_ddl_deparse/expected/create_schema.out index 634900d8456..e3c197af118 100644 --- a/src/test/modules/test_ddl_deparse/expected/create_schema.out +++ b/src/test/modules/test_ddl_deparse/expected/create_schema.out @@ -14,13 +14,16 @@ NOTICE: schema "baz" already exists, skipping CREATE SCHEMA element_test CREATE TABLE foo (id int) CREATE VIEW bar AS SELECT * FROM foo - CREATE DOMAIN d1 AS INT; + CREATE DOMAIN d1 AS INT + CREATE COLLATION coll (LOCALE="C"); NOTICE: DDL test: type simple, tag CREATE SCHEMA NOTICE: DDL test: type simple, tag CREATE TABLE NOTICE: DDL test: type simple, tag CREATE VIEW NOTICE: DDL test: type simple, tag CREATE DOMAIN +NOTICE: DDL test: type simple, tag CREATE COLLATION DROP SCHEMA element_test CASCADE; -NOTICE: drop cascades to 3 other objects +NOTICE: drop cascades to 4 other objects DETAIL: drop cascades to table element_test.foo drop cascades to view element_test.bar drop cascades to type element_test.d1 +drop cascades to collation element_test.coll diff --git a/src/test/modules/test_ddl_deparse/sql/create_schema.sql b/src/test/modules/test_ddl_deparse/sql/create_schema.sql index b822517b64d..0c61db13935 100644 --- a/src/test/modules/test_ddl_deparse/sql/create_schema.sql +++ b/src/test/modules/test_ddl_deparse/sql/create_schema.sql @@ -15,6 +15,7 @@ CREATE SCHEMA IF NOT EXISTS baz; CREATE SCHEMA element_test CREATE TABLE foo (id int) CREATE VIEW bar AS SELECT * FROM foo - CREATE DOMAIN d1 AS INT; + CREATE DOMAIN d1 AS INT + CREATE COLLATION coll (LOCALE="C"); DROP SCHEMA element_test CASCADE; diff --git a/src/test/regress/expected/collate.icu.utf8.out b/src/test/regress/expected/collate.icu.utf8.out index 1325e123877..fdd4439674f 100644 --- a/src/test/regress/expected/collate.icu.utf8.out +++ b/src/test/regress/expected/collate.icu.utf8.out @@ -1297,6 +1297,21 @@ DROP TABLE test7; CREATE COLLATION testcoll_rulesx (provider = icu, locale = '', rules = '!!wrong!!'); NOTICE: using standard form "und" for ICU locale "" ERROR: could not open collator for locale "und" with rules "!!wrong!!": U_INVALID_FORMAT_ERROR +--CREATE SCHEMA CREATE COLLATION +CREATE SCHEMA regress_schema_4 + CREATE COLLATION coll_icu_und FROM "und-x-icu" + CREATE TABLE tts(a TEXT COLLATE coll_icu_und); +\dO regress_schema_4.* + List of collations + Schema | Name | Provider | Collate | Ctype | Locale | ICU Rules | Deterministic? +------------------+--------------+----------+---------+-------+--------+-----------+---------------- + regress_schema_4 | coll_icu_und | icu | | | und | | yes +(1 row) + +DROP SCHEMA regress_schema_4 CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to collation regress_schema_4.coll_icu_und +drop cascades to table regress_schema_4.tts -- nondeterministic collations CREATE COLLATION ctest_det (provider = icu, locale = '', deterministic = true); NOTICE: using standard form "und" for ICU locale "" diff --git a/src/test/regress/expected/create_schema.out b/src/test/regress/expected/create_schema.out index 0533c29a311..3e061c7a0ef 100644 --- a/src/test/regress/expected/create_schema.out +++ b/src/test/regress/expected/create_schema.out @@ -177,6 +177,41 @@ CREATE SCHEMA regress_schema_3 AUTHORIZATION CURRENT_ROLE regress_schema_3 | ss1 | regress_schema_3.ss | | | 'hello'::text | (2 rows) +-- Cases where the schema creation with collations +--fail. can not CREATE COLLATION to other schema +CREATE SCHEMA regress_schema_4 AUTHORIZATION CURRENT_ROLE + CREATE COLLATION public.coll_icu_und FROM "und-x-icu"; +ERROR: CREATE COLLATION specifies a schema (public) different from the one being created (regress_schema_4) +--fail. improper qualified name +CREATE SCHEMA regress_schema_4 AUTHORIZATION CURRENT_ROLE + CREATE COLLATION postgres.public.coll_icu_und FROM "und-x-icu"; +ERROR: cross-database references are not implemented: postgres.public.coll_icu_und +--fail. only support collation object for DefineStmt node +CREATE SCHEMA regress_schema_4 AUTHORIZATION CURRENT_ROLE + CREATE AGGREGATE balk(int4) (SFUNC = int4_sum(int8, int4), STYPE = int8, PARALLEL = SAFE, INITCOND = '0'); +ERROR: CREATE SCHEMA ... CREATE AGGREGATE currently not supported +--ok, qualified schema name for collation should be same as the created schema +CREATE SCHEMA regress_schema_4 AUTHORIZATION CURRENT_ROLE + CREATE COLLATION regress_schema_4.coll (LOCALE="C") + CREATE TABLE t(a TEXT COLLATE regress_schema_4.coll); +\dO regress_schema_4.* + List of collations + Schema | Name | Provider | Collate | Ctype | Locale | ICU Rules | Deterministic? +------------------+------+----------+---------+-------+--------+-----------+---------------- + regress_schema_4 | coll | libc | C | C | | | yes +(1 row) + +--ok, no qualified schema name for collation +CREATE SCHEMA regress_schema_5 AUTHORIZATION CURRENT_ROLE + CREATE COLLATION coll (LOCALE="C") + CREATE TABLE t(a TEXT COLLATE coll); +\dO regress_schema_5.* + List of collations + Schema | Name | Provider | Collate | Ctype | Locale | ICU Rules | Deterministic? +------------------+------+----------+---------+-------+--------+-----------+---------------- + regress_schema_5 | coll | libc | C | C | | | yes +(1 row) + DROP SCHEMA regress_schema_2 CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to type regress_schema_2.ss @@ -187,5 +222,13 @@ DETAIL: drop cascades to type regress_schema_3.ss drop cascades to type regress_schema_3.ss1 drop cascades to view regress_schema_3.test drop cascades to table regress_schema_3.t +DROP SCHEMA regress_schema_4 CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to collation regress_schema_4.coll +drop cascades to table regress_schema_4.t +DROP SCHEMA regress_schema_5 CASCADE; +NOTICE: drop cascades to 2 other objects +DETAIL: drop cascades to collation regress_schema_5.coll +drop cascades to table regress_schema_5.t -- Clean up DROP ROLE regress_create_schema_role; diff --git a/src/test/regress/sql/collate.icu.utf8.sql b/src/test/regress/sql/collate.icu.utf8.sql index b6c54503d21..243d69e4d32 100644 --- a/src/test/regress/sql/collate.icu.utf8.sql +++ b/src/test/regress/sql/collate.icu.utf8.sql @@ -513,6 +513,13 @@ DROP TABLE test7; CREATE COLLATION testcoll_rulesx (provider = icu, locale = '', rules = '!!wrong!!'); +--CREATE SCHEMA CREATE COLLATION +CREATE SCHEMA regress_schema_4 + CREATE COLLATION coll_icu_und FROM "und-x-icu" + CREATE TABLE tts(a TEXT COLLATE coll_icu_und); +\dO regress_schema_4.* +DROP SCHEMA regress_schema_4 CASCADE; + -- nondeterministic collations diff --git a/src/test/regress/sql/create_schema.sql b/src/test/regress/sql/create_schema.sql index 54a07054767..0f802bcaffe 100644 --- a/src/test/regress/sql/create_schema.sql +++ b/src/test/regress/sql/create_schema.sql @@ -104,8 +104,37 @@ CREATE SCHEMA regress_schema_3 AUTHORIZATION CURRENT_ROLE CREATE TABLE t(a ss1); \dD regress_schema_3.* +-- Cases where the schema creation with collations +--fail. can not CREATE COLLATION to other schema +CREATE SCHEMA regress_schema_4 AUTHORIZATION CURRENT_ROLE + CREATE COLLATION public.coll_icu_und FROM "und-x-icu"; + +--fail. improper qualified name +CREATE SCHEMA regress_schema_4 AUTHORIZATION CURRENT_ROLE + CREATE COLLATION postgres.public.coll_icu_und FROM "und-x-icu"; + +--fail. only support collation object for DefineStmt node +CREATE SCHEMA regress_schema_4 AUTHORIZATION CURRENT_ROLE + CREATE AGGREGATE balk(int4) (SFUNC = int4_sum(int8, int4), STYPE = int8, PARALLEL = SAFE, INITCOND = '0'); + +--ok, qualified schema name for collation should be same as the created schema +CREATE SCHEMA regress_schema_4 AUTHORIZATION CURRENT_ROLE + CREATE COLLATION regress_schema_4.coll (LOCALE="C") + CREATE TABLE t(a TEXT COLLATE regress_schema_4.coll); + +\dO regress_schema_4.* + +--ok, no qualified schema name for collation +CREATE SCHEMA regress_schema_5 AUTHORIZATION CURRENT_ROLE + CREATE COLLATION coll (LOCALE="C") + CREATE TABLE t(a TEXT COLLATE coll); +\dO regress_schema_5.* + + DROP SCHEMA regress_schema_2 CASCADE; DROP SCHEMA regress_schema_3 CASCADE; +DROP SCHEMA regress_schema_4 CASCADE; +DROP SCHEMA regress_schema_5 CASCADE; -- Clean up DROP ROLE regress_create_schema_role; -- 2.34.1