diff --git a/doc/src/sgml/ref/create_schema.sgml b/doc/src/sgml/ref/create_schema.sgml index 930d876..70aeec3 100644 --- a/doc/src/sgml/ref/create_schema.sgml +++ b/doc/src/sgml/ref/create_schema.sgml @@ -21,8 +21,8 @@ PostgreSQL documentation -CREATE SCHEMA schema_name [ AUTHORIZATION user_name ] [ schema_element [ ... ] ] -CREATE SCHEMA AUTHORIZATION user_name [ schema_element [ ... ] ] +CREATE SCHEMA [ IF NOT EXISTS ] schema_name [ AUTHORIZATION user_name ] [ schema_element [ ... ] ] +CREATE SCHEMA [ IF NOT EXISTS ] AUTHORIZATION user_name [ schema_element [ ... ] ] @@ -62,6 +62,16 @@ CREATE SCHEMA AUTHORIZATION user_name + IF NOT EXISTS + + + Do not throw an error if a schema with the same name already exists. + A notice is issued in this case. + + + + + schema_name diff --git a/src/backend/commands/extension.c b/src/backend/commands/extension.c index ec8aa17..8a62341 100644 --- a/src/backend/commands/extension.c +++ b/src/backend/commands/extension.c @@ -1376,6 +1376,7 @@ CreateExtension(CreateExtensionStmt *stmt) csstmt->schemaname = schemaName; csstmt->authid = NULL; /* will be created by current user */ csstmt->schemaElts = NIL; + csstmt->if_not_exists = false; CreateSchemaCommand(csstmt, NULL); /* diff --git a/src/backend/commands/schemacmds.c b/src/backend/commands/schemacmds.c index cd5ce06..b01c571 100644 --- a/src/backend/commands/schemacmds.c +++ b/src/backend/commands/schemacmds.c @@ -51,6 +51,7 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) Oid saved_uid; int save_sec_context; AclResult aclresult; + bool if_not_exists = stmt->if_not_exists; GetUserIdAndSecContext(&saved_uid, &save_sec_context); @@ -95,12 +96,21 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString) SetUserIdAndSecContext(owner_uid, save_sec_context | SECURITY_LOCAL_USERID_CHANGE); - /* Create the schema's namespace */ - namespaceId = NamespaceCreate(schemaName, owner_uid, false); - - /* Advance cmd counter to make the namespace visible */ - CommandCounterIncrement(); - + /* If schema already exists, skip */ + namespaceId = get_namespace_oid(schemaName, true); + if (OidIsValid(namespaceId) && if_not_exists) { + ereport(NOTICE, + (errcode(ERRCODE_DUPLICATE_SCHEMA), + errmsg("schema \"%s\" already exists, skipping", + schemaName))); + } else { + + /* Create the schema's namespace */ + namespaceId = NamespaceCreate(schemaName, owner_uid, false); + + /* Advance cmd counter to make the namespace visible */ + CommandCounterIncrement(); + } /* * Temporarily make the new namespace be the front of the search path, as * well as the default creation target namespace. This will be undone at diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index f34f704..9f6458b 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -3614,6 +3614,7 @@ _copyCreateSchemaStmt(const CreateSchemaStmt *from) COPY_STRING_FIELD(schemaname); COPY_STRING_FIELD(authid); COPY_NODE_FIELD(schemaElts); + COPY_SCALAR_FIELD(if_not_exists); return newnode; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index b4b1c22..89ae94d 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -1910,6 +1910,7 @@ _equalCreateSchemaStmt(const CreateSchemaStmt *a, const CreateSchemaStmt *b) COMPARE_STRING_FIELD(schemaname); COMPARE_STRING_FIELD(authid); COMPARE_NODE_FIELD(schemaElts); + COMPARE_SCALAR_FIELD(if_not_exists); return true; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 5894cb0..46b17ce 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -1169,8 +1169,23 @@ CreateSchemaStmt: n->schemaname = $5; n->authid = $5; n->schemaElts = $6; + n->if_not_exists = false; $$ = (Node *)n; } + | CREATE SCHEMA IF_P NOT EXISTS OptSchemaName AUTHORIZATION RoleId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* One can omit the schema name or the authorization id. */ + if ($6 != NULL) + n->schemaname = $6; + else + n->schemaname = $8; + n->authid = $8; + n->schemaElts = $9; + n->if_not_exists = true; + $$ = (Node *)n; + } + | CREATE SCHEMA ColId OptSchemaEltList { CreateSchemaStmt *n = makeNode(CreateSchemaStmt); @@ -1178,8 +1193,20 @@ CreateSchemaStmt: n->schemaname = $3; n->authid = NULL; n->schemaElts = $4; + n->if_not_exists = false; $$ = (Node *)n; } + | CREATE SCHEMA IF_P NOT EXISTS ColId OptSchemaEltList + { + CreateSchemaStmt *n = makeNode(CreateSchemaStmt); + /* ...but not both */ + n->schemaname = $6; + n->authid = NULL; + n->schemaElts = $7; + n->if_not_exists = true; + $$ = (Node *)n; + } + ; OptSchemaName: diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index 19178b5..dee2004 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -1157,6 +1157,7 @@ typedef struct CreateSchemaStmt char *schemaname; /* the name of the schema to create */ char *authid; /* the owner of the created schema */ List *schemaElts; /* schema components (list of parsenodes) */ + bool if_not_exists; /* just do nothing if schema already exists? */ } CreateSchemaStmt; typedef enum DropBehavior diff --git a/src/test/regress/expected/namespace.out b/src/test/regress/expected/namespace.out index 7c26da5..611072b 100644 --- a/src/test/regress/expected/namespace.out +++ b/src/test/regress/expected/namespace.out @@ -9,6 +9,25 @@ CREATE SCHEMA test_schema_1 a serial, b int UNIQUE ); +-- verify if schema already exists +CREATE SCHEMA test_schema_1; +ERROR: schema "test_schema_1" already exists +CREATE SCHEMA IF NOT EXISTS test_schema_1; +NOTICE: schema "test_schema_1" already exists, skipping +CREATE SCHEMA test_schema_2; +CREATE SCHEMA IF NOT EXISTS test_schema_2 + CREATE TABLE abc ( + a serial, + b int UNIQUE + ); +NOTICE: schema "test_schema_2" already exists, skipping +CREATE SCHEMA IF NOT EXISTS test_schema_2 + CREATE TABLE IF NOT EXISTS abc ( + a serial, + b int UNIQUE + ); +NOTICE: schema "test_schema_2" already exists, skipping +NOTICE: relation "abc" already exists, skipping -- verify that the objects were created SELECT COUNT(*) FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'test_schema_1'); @@ -17,6 +36,13 @@ SELECT COUNT(*) FROM pg_class WHERE relnamespace = 5 (1 row) +SELECT count(*) FROM pg_class + JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace WHERE relkind = 'r' AND nspname = 'test_schema_2'; + count +------- + 1 +(1 row) + INSERT INTO test_schema_1.abc DEFAULT VALUES; INSERT INTO test_schema_1.abc DEFAULT VALUES; INSERT INTO test_schema_1.abc DEFAULT VALUES; @@ -40,6 +66,8 @@ DROP SCHEMA test_schema_1 CASCADE; NOTICE: drop cascades to 2 other objects DETAIL: drop cascades to table test_schema_1.abc drop cascades to view test_schema_1.abc_view +DROP SCHEMA test_schema_2 CASCADE; +NOTICE: drop cascades to table test_schema_2.abc -- verify that the objects were dropped SELECT COUNT(*) FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'test_schema_1'); diff --git a/src/test/regress/sql/namespace.sql b/src/test/regress/sql/namespace.sql index 919f72a..c2ef257 100644 --- a/src/test/regress/sql/namespace.sql +++ b/src/test/regress/sql/namespace.sql @@ -13,10 +13,27 @@ CREATE SCHEMA test_schema_1 b int UNIQUE ); +-- verify if schema already exists +CREATE SCHEMA test_schema_1; +CREATE SCHEMA IF NOT EXISTS test_schema_1; +CREATE SCHEMA test_schema_2; +CREATE SCHEMA IF NOT EXISTS test_schema_2 + CREATE TABLE abc ( + a serial, + b int UNIQUE + ); +CREATE SCHEMA IF NOT EXISTS test_schema_2 + CREATE TABLE IF NOT EXISTS abc ( + a serial, + b int UNIQUE + ); -- verify that the objects were created SELECT COUNT(*) FROM pg_class WHERE relnamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'test_schema_1'); +SELECT count(*) FROM pg_class + JOIN pg_namespace ON pg_namespace.oid = pg_class.relnamespace WHERE relkind = 'r' AND nspname = 'test_schema_2'; + INSERT INTO test_schema_1.abc DEFAULT VALUES; INSERT INTO test_schema_1.abc DEFAULT VALUES; INSERT INTO test_schema_1.abc DEFAULT VALUES; @@ -25,6 +42,7 @@ SELECT * FROM test_schema_1.abc; SELECT * FROM test_schema_1.abc_view; DROP SCHEMA test_schema_1 CASCADE; +DROP SCHEMA test_schema_2 CASCADE; -- verify that the objects were dropped SELECT COUNT(*) FROM pg_class WHERE relnamespace =