Support CREATE SCHEMA IF NOT EXISTS.
authorTom Lane <tgl@sss.pgh.pa.us>
Wed, 3 Oct 2012 23:47:11 +0000 (19:47 -0400)
committerTom Lane <tgl@sss.pgh.pa.us>
Wed, 3 Oct 2012 23:47:11 +0000 (19:47 -0400)
Per discussion, schema-element subcommands are not allowed together with
this option, since it's not very obvious what should happen to the element
objects.

Fabrízio de Royes Mello

doc/src/sgml/ref/create_schema.sgml
src/backend/commands/extension.c
src/backend/commands/schemacmds.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/parser/gram.y
src/include/nodes/parsenodes.h
src/test/regress/expected/namespace.out
src/test/regress/sql/namespace.sql

index 930d876814d9ae0ffd1f94badfbf0266e800c4c0..2602bb1d56f98562b571ae240620b549728d2b84 100644 (file)
@@ -23,6 +23,8 @@ PostgreSQL documentation
 <synopsis>
 CREATE SCHEMA <replaceable class="parameter">schema_name</replaceable> [ AUTHORIZATION <replaceable class="parameter">user_name</replaceable> ] [ <replaceable class="parameter">schema_element</replaceable> [ ... ] ]
 CREATE SCHEMA AUTHORIZATION <replaceable class="parameter">user_name</replaceable> [ <replaceable class="parameter">schema_element</replaceable> [ ... ] ]
+CREATE SCHEMA IF NOT EXISTS <replaceable class="parameter">schema_name</replaceable> [ AUTHORIZATION <replaceable class="parameter">user_name</replaceable> ]
+CREATE SCHEMA IF NOT EXISTS AUTHORIZATION <replaceable class="parameter">user_name</replaceable>
 </synopsis>
  </refsynopsisdiv>
 
@@ -98,6 +100,17 @@ CREATE SCHEMA AUTHORIZATION <replaceable class="parameter">user_name</replaceabl
        </para>
       </listitem>
      </varlistentry>
+
+     <varlistentry>
+      <term><literal>IF NOT EXISTS</literal></term>
+      <listitem>
+       <para>
+        Do nothing (except issuing a notice) if a schema with the same name
+        already exists.  <replaceable class="parameter">schema_element</>
+        subcommands cannot be included when this option is used.
+       </para>
+      </listitem>
+     </varlistentry>
     </variablelist>
  </refsect1>
 
@@ -129,6 +142,15 @@ CREATE SCHEMA AUTHORIZATION joe;
 </programlisting>
   </para>
 
+  <para>
+   Create a schema named <literal>test</> that will be owned by user
+   <literal>joe</>, unless there already is a schema named <literal>test</>.
+   (It does not matter whether <literal>joe</> owns the pre-existing schema.)
+<programlisting>
+CREATE SCHEMA IF NOT EXISTS test AUTHORIZATION joe;
+</programlisting>
+  </para>
+
   <para>
    Create a schema and create a table and view within it:
 <programlisting>
@@ -177,6 +199,11 @@ CREATE VIEW hollywood.winners AS
    schema owner.  This can happen only if the schema owner grants the
    <literal>CREATE</> privilege on his schema to someone else.
   </para>
+
+  <para>
+   The <literal>IF NOT EXISTS</literal> option is a
+   <productname>PostgreSQL</productname> extension.
+  </para>
  </refsect1>
 
  <refsect1>
index 5712fe19c5ce9b779e199d2756f5797c302975ef..5aa9bbb19c16317193dcbc5cb3429a7695e0b1c3 100644 (file)
@@ -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);
 
            /*
index cd5ce06ca76ac3360c31c0a32391991b1a0515a7..e69c86bbabfe69a528643eec737eb85696697600 100644 (file)
@@ -83,6 +83,23 @@ CreateSchemaCommand(CreateSchemaStmt *stmt, const char *queryString)
                 errmsg("unacceptable schema name \"%s\"", schemaName),
           errdetail("The prefix \"pg_\" is reserved for system schemas.")));
 
+   /*
+    * If if_not_exists was given and the schema already exists, bail out.
+    * (Note: we needn't check this when not if_not_exists, because
+    * NamespaceCreate will complain anyway.)  We could do this before making
+    * the permissions checks, but since CREATE TABLE IF NOT EXISTS makes its
+    * creation-permission check first, we do likewise.
+    */
+   if (stmt->if_not_exists &&
+       SearchSysCacheExists1(NAMESPACENAME, PointerGetDatum(schemaName)))
+   {
+       ereport(NOTICE,
+               (errcode(ERRCODE_DUPLICATE_SCHEMA),
+                errmsg("schema \"%s\" already exists, skipping",
+                       schemaName)));
+       return;
+   }
+
    /*
     * If the requested authorization is different from the current user,
     * temporarily set the current user so that the object(s) will be created
index 84ab16d61f1a7639d0a1316a56443c11a11f0776..9387ee90c9f4a7fd52e2145730758d4bf851376c 100644 (file)
@@ -3613,6 +3613,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;
 }
index e537e43809ca49d486e48705efc4eae64d785a84..226b99a1d271d425fdc394dd3d3016661cecabf5 100644 (file)
@@ -1909,6 +1909,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;
 }
index 62ff91782868b9a0a95cdee89a05d65950776d18..7feadeac1693e7ff46f44ef3ad9ea2fa86bf46a8 100644 (file)
@@ -1169,6 +1169,7 @@ CreateSchemaStmt:
                        n->schemaname = $5;
                    n->authid = $5;
                    n->schemaElts = $6;
+                   n->if_not_exists = false;
                    $$ = (Node *)n;
                }
            | CREATE SCHEMA ColId OptSchemaEltList
@@ -1178,6 +1179,40 @@ CreateSchemaStmt:
                    n->schemaname = $3;
                    n->authid = NULL;
                    n->schemaElts = $4;
+                   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;
+                   if ($9 != NIL)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("CREATE SCHEMA IF NOT EXISTS cannot include schema elements"),
+                                parser_errposition(@9)));
+                   n->schemaElts = $9;
+                   n->if_not_exists = true;
+                   $$ = (Node *)n;
+               }
+           | CREATE SCHEMA IF_P NOT EXISTS ColId OptSchemaEltList
+               {
+                   CreateSchemaStmt *n = makeNode(CreateSchemaStmt);
+                   /* ...but not both */
+                   n->schemaname = $6;
+                   n->authid = NULL;
+                   if ($7 != NIL)
+                       ereport(ERROR,
+                               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                                errmsg("CREATE SCHEMA IF NOT EXISTS cannot include schema elements"),
+                                parser_errposition(@7)));
+                   n->schemaElts = $7;
+                   n->if_not_exists = true;
                    $$ = (Node *)n;
                }
        ;
index e10e3a1b345178b80a2a89babf41cbdc93871aff..09b15e7694cde1932b3640a8bb8f69e7606ea6b9 100644 (file)
@@ -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
index 7c26da5636e63572a4a51f655c974f43e4faa32f..5fcd46daf42705147f53599d657e52d8fe7b5b1f 100644 (file)
@@ -36,6 +36,19 @@ SELECT * FROM test_schema_1.abc_view;
  4 |  
 (3 rows)
 
+-- test IF NOT EXISTS cases
+CREATE SCHEMA test_schema_1; -- fail, already exists
+ERROR:  schema "test_schema_1" already exists
+CREATE SCHEMA IF NOT EXISTS test_schema_1; -- ok with notice
+NOTICE:  schema "test_schema_1" already exists, skipping
+CREATE SCHEMA IF NOT EXISTS test_schema_1 -- fail, disallowed
+       CREATE TABLE abc (
+              a serial,
+              b int UNIQUE
+       );
+ERROR:  CREATE SCHEMA IF NOT EXISTS cannot include schema elements
+LINE 1: CREATE SCHEMA IF NOT EXISTS test_schema_1 
+                                    ^
 DROP SCHEMA test_schema_1 CASCADE;
 NOTICE:  drop cascades to 2 other objects
 DETAIL:  drop cascades to table test_schema_1.abc
index 919f72ada2b513629cf4a995d825163ab672619d..879b6c35b05cd2d101ca3b69431aa3ea8f7b17eb 100644 (file)
@@ -24,6 +24,15 @@ INSERT INTO test_schema_1.abc DEFAULT VALUES;
 SELECT * FROM test_schema_1.abc;
 SELECT * FROM test_schema_1.abc_view;
 
+-- test IF NOT EXISTS cases
+CREATE SCHEMA test_schema_1; -- fail, already exists
+CREATE SCHEMA IF NOT EXISTS test_schema_1; -- ok with notice
+CREATE SCHEMA IF NOT EXISTS test_schema_1 -- fail, disallowed
+       CREATE TABLE abc (
+              a serial,
+              b int UNIQUE
+       );
+
 DROP SCHEMA test_schema_1 CASCADE;
 
 -- verify that the objects were dropped