DROP IF EXISTS for columns and constraints. Andres Freund.
authorAndrew Dunstan <andrew@dunslane.net>
Mon, 20 Jul 2009 02:42:28 +0000 (02:42 +0000)
committerAndrew Dunstan <andrew@dunslane.net>
Mon, 20 Jul 2009 02:42:28 +0000 (02:42 +0000)
doc/src/sgml/ref/alter_table.sgml
src/backend/commands/tablecmds.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/alter_table.out
src/test/regress/sql/alter_table.sql

index fe3f3889be8fd2f4d36958f66eb3426b19660bb7..f065e7822ffa90143c6e610758869a903f4ccbc4 100644 (file)
@@ -33,7 +33,7 @@ ALTER TABLE <replaceable class="PARAMETER">name</replaceable>
 where <replaceable class="PARAMETER">action</replaceable> is one of:
 
     ADD [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> <replaceable class="PARAMETER">type</replaceable> [ <replaceable class="PARAMETER">column_constraint</replaceable> [ ... ] ]
-    DROP [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
+    DROP [ COLUMN ] [ IF EXISTS ] <replaceable class="PARAMETER">column</replaceable> [ RESTRICT | CASCADE ]
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> [ SET DATA ] TYPE <replaceable class="PARAMETER">type</replaceable> [ USING <replaceable class="PARAMETER">expression</replaceable> ]
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET DEFAULT <replaceable class="PARAMETER">expression</replaceable>
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> DROP DEFAULT
@@ -41,7 +41,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STATISTICS <replaceable class="PARAMETER">integer</replaceable>
     ALTER [ COLUMN ] <replaceable class="PARAMETER">column</replaceable> SET STORAGE { PLAIN | EXTERNAL | EXTENDED | MAIN }
     ADD <replaceable class="PARAMETER">table_constraint</replaceable>
-    DROP CONSTRAINT <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
+    DROP CONSTRAINT [ IF EXISTS ]  <replaceable class="PARAMETER">constraint_name</replaceable> [ RESTRICT | CASCADE ]
     DISABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ]
     ENABLE TRIGGER [ <replaceable class="PARAMETER">trigger_name</replaceable> | ALL | USER ]
     ENABLE REPLICA TRIGGER <replaceable class="PARAMETER">trigger_name</replaceable>
@@ -82,7 +82,7 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
    </varlistentry>
 
    <varlistentry>
-    <term><literal>DROP COLUMN</literal></term>
+    <term><literal>DROP COLUMN [ IF EXISTS ]</literal></term>
     <listitem>
      <para>
       This form drops a column from a table.  Indexes and
@@ -90,6 +90,9 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
       dropped as well.  You will need to say <literal>CASCADE</> if
       anything outside the table depends on the column, for example,
       foreign key references or views.
+      If <literal>IF EXISTS</literal> is specified and the column 
+      does not exist, no error is thrown. In this case a notice 
+      is issued instead.
      </para>
     </listitem>
    </varlistentry>
@@ -192,10 +195,12 @@ where <replaceable class="PARAMETER">action</replaceable> is one of:
    </varlistentry>
 
    <varlistentry>
-    <term><literal>DROP CONSTRAINT</literal></term>
+    <term><literal>DROP CONSTRAINT [ IF EXISTS ]</literal></term>
     <listitem>
      <para>
       This form drops the specified constraint on a table.
+      If <literal>IF EXISTS</literal> is specified and the constraint
+      does not exist, no error is thrown. In this case a notice is issued instead.
      </para>
     </listitem>
    </varlistentry>
index 656399c66ce8030f03e0f820e3b17f19b6aac0d5..b0a633c5056dbe63dcb8e33cacd41266acf394d4 100644 (file)
@@ -285,7 +285,8 @@ static void ATExecSetStorage(Relation rel, const char *colName,
                                 Node *newValue);
 static void ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                                 DropBehavior behavior,
-                                bool recurse, bool recursing);
+                                bool recurse, bool recursing,
+                                bool missing_ok);
 static void ATExecAddIndex(AlteredTableInfo *tab, Relation rel,
                           IndexStmt *stmt, bool is_rebuild);
 static void ATExecAddConstraint(List **wqueue,
@@ -298,8 +299,9 @@ static void ATAddCheckConstraint(List **wqueue,
 static void ATAddForeignKeyConstraint(AlteredTableInfo *tab, Relation rel,
                                                  FkConstraint *fkconstraint);
 static void ATExecDropConstraint(Relation rel, const char *constrName,
-                                        DropBehavior behavior,
-                                        bool recurse, bool recursing);
+                                                                DropBehavior behavior,
+                                                                bool recurse, bool recursing,
+                                                                bool missing_ok);
 static void ATPrepAlterColumnType(List **wqueue,
                                          AlteredTableInfo *tab, Relation rel,
                                          bool recurse, bool recursing,
@@ -2620,11 +2622,11 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                        break;
                case AT_DropColumn:             /* DROP COLUMN */
                        ATExecDropColumn(wqueue, rel, cmd->name,
-                                                        cmd->behavior, false, false);
+                                                        cmd->behavior, false, false, cmd->missing_ok);
                        break;
                case AT_DropColumnRecurse:              /* DROP COLUMN with recursion */
                        ATExecDropColumn(wqueue, rel, cmd->name,
-                                                        cmd->behavior, true, false);
+                                                        cmd->behavior, true, false, cmd->missing_ok);
                        break;
                case AT_AddIndex:               /* ADD INDEX */
                        ATExecAddIndex(tab, rel, (IndexStmt *) cmd->def, false);
@@ -2639,10 +2641,14 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, Relation rel,
                        ATExecAddConstraint(wqueue, tab, rel, cmd->def, true);
                        break;
                case AT_DropConstraint: /* DROP CONSTRAINT */
-                       ATExecDropConstraint(rel, cmd->name, cmd->behavior, false, false);
+                       ATExecDropConstraint(rel, cmd->name, cmd->behavior,
+                                                                false, false,
+                                                                cmd->missing_ok);
                        break;
                case AT_DropConstraintRecurse:  /* DROP CONSTRAINT with recursion */
-                       ATExecDropConstraint(rel, cmd->name, cmd->behavior, true, false);
+                       ATExecDropConstraint(rel, cmd->name, cmd->behavior,
+                                                                true, false,
+                                                                cmd->missing_ok);
                        break;
                case AT_AlterColumnType:                /* ALTER COLUMN TYPE */
                        ATExecAlterColumnType(tab, rel, cmd->name, (TypeName *) cmd->def);
@@ -4160,7 +4166,8 @@ ATExecSetStorage(Relation rel, const char *colName, Node *newValue)
 static void
 ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                                 DropBehavior behavior,
-                                bool recurse, bool recursing)
+                                bool recurse, bool recursing,
+                                bool missing_ok)
 {
        HeapTuple       tuple;
        Form_pg_attribute targetatt;
@@ -4176,11 +4183,21 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
         * get the number of the attribute
         */
        tuple = SearchSysCacheAttName(RelationGetRelid(rel), colName);
-       if (!HeapTupleIsValid(tuple))
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_COLUMN),
-                                errmsg("column \"%s\" of relation \"%s\" does not exist",
-                                               colName, RelationGetRelationName(rel))));
+       if (!HeapTupleIsValid(tuple)){
+               if (!missing_ok){
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_COLUMN),
+                                        errmsg("column \"%s\" of relation \"%s\" does not exist",
+                                                       colName, RelationGetRelationName(rel))));
+               }
+               else
+               {
+                       ereport(NOTICE,
+                                       (errmsg("column \"%s\" of relation \"%s\" does not exist, skipping",
+                                                       colName, RelationGetRelationName(rel))));
+                       return;
+               }
+       }
        targetatt = (Form_pg_attribute) GETSTRUCT(tuple);
 
        attnum = targetatt->attnum;
@@ -4246,7 +4263,8 @@ ATExecDropColumn(List **wqueue, Relation rel, const char *colName,
                                {
                                        /* Time to delete this child column, too */
                                        ATExecDropColumn(wqueue, childrel, colName,
-                                                                        behavior, true, true);
+                                                                        behavior, true, true,
+                                                                        false);
                                }
                                else
                                {
@@ -5360,7 +5378,8 @@ createForeignKeyTriggers(Relation rel, FkConstraint *fkconstraint,
 static void
 ATExecDropConstraint(Relation rel, const char *constrName,
                                         DropBehavior behavior,
-                                        bool recurse, bool recursing)
+                                        bool recurse, bool recursing,
+                                        bool missing_ok)
 {
        List       *children;
        ListCell   *child;
@@ -5422,12 +5441,22 @@ ATExecDropConstraint(Relation rel, const char *constrName,
 
        systable_endscan(scan);
 
-       if (!found)
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("constraint \"%s\" of relation \"%s\" does not exist",
-                                               constrName, RelationGetRelationName(rel))));
-
+       if (!found){
+               if (!missing_ok){
+                       ereport(ERROR,
+                                       (errcode(ERRCODE_UNDEFINED_OBJECT),
+                                        errmsg("constraint \"%s\" of relation \"%s\" does not exist",
+                                                       constrName, RelationGetRelationName(rel))));
+               }
+               else
+               {
+                       ereport(NOTICE,
+                                       (errmsg("constraint \"%s\" of relation \"%s\" does not exist, skipping",
+                                                       constrName, RelationGetRelationName(rel))));
+                       heap_close(conrel, RowExclusiveLock);
+                       return;
+               }
+       }
        /*
         * Propagate to children as appropriate.  Unlike most other ALTER
         * routines, we have to do this one level of recursion at a time; we can't
@@ -5490,7 +5519,8 @@ ATExecDropConstraint(Relation rel, const char *constrName,
                                {
                                        /* Time to delete this child constraint, too */
                                        ATExecDropConstraint(childrel, constrName, behavior,
-                                                                                true, true);
+                                                                                true, true,
+                                                                                false);
                                }
                                else
                                {
index 1976648dd8a7d5d8288dd36c3a7393aca2025f77..e1cd3fea401ac1463b296acf2330e5c6fe45ccec 100644 (file)
@@ -2272,6 +2272,7 @@ _copyAlterTableCmd(AlterTableCmd *from)
        COPY_NODE_FIELD(def);
        COPY_NODE_FIELD(transform);
        COPY_SCALAR_FIELD(behavior);
+       COPY_SCALAR_FIELD(missing_ok);
 
        return newnode;
 }
index 8b466f4d223a5f1a09a698d2307a2b56c201e163..7c541bc0356de313b2737590314b8c82863d302c 100644 (file)
@@ -958,6 +958,7 @@ _equalAlterTableCmd(AlterTableCmd *a, AlterTableCmd *b)
        COMPARE_NODE_FIELD(def);
        COMPARE_NODE_FIELD(transform);
        COMPARE_SCALAR_FIELD(behavior);
+       COMPARE_SCALAR_FIELD(missing_ok);
 
        return true;
 }
index 858e16c1c02cb9a6e8895634b192ea133434a3c0..eb786e96eabe7519ffcb1aae06bfede01b574252 100644 (file)
@@ -1610,6 +1610,16 @@ alter_table_cmd:
                                        n->def = (Node *) makeString($6);
                                        $$ = (Node *)n;
                                }
+                       /* ALTER TABLE <name> DROP [COLUMN] IF EXISTS <colname> [RESTRICT|CASCADE] */
+                       | DROP opt_column IF_P EXISTS ColId opt_drop_behavior
+                               {
+                                       AlterTableCmd *n = makeNode(AlterTableCmd);
+                                       n->subtype = AT_DropColumn;
+                                       n->name = $5;
+                                       n->behavior = $6;
+                                       n->missing_ok = TRUE;
+                                       $$ = (Node *)n;
+                               }
                        /* ALTER TABLE <name> DROP [COLUMN] <colname> [RESTRICT|CASCADE] */
                        | DROP opt_column ColId opt_drop_behavior
                                {
@@ -1617,6 +1627,7 @@ alter_table_cmd:
                                        n->subtype = AT_DropColumn;
                                        n->name = $3;
                                        n->behavior = $4;
+                                       n->missing_ok = FALSE;
                                        $$ = (Node *)n;
                                }
                        /*
@@ -1640,6 +1651,16 @@ alter_table_cmd:
                                        n->def = $2;
                                        $$ = (Node *)n;
                                }
+                       /* ALTER TABLE <name> DROP CONSTRAINT IF EXISTS <name> [RESTRICT|CASCADE] */
+                       | DROP CONSTRAINT IF_P EXISTS name opt_drop_behavior
+                               {
+                                       AlterTableCmd *n = makeNode(AlterTableCmd);
+                                       n->subtype = AT_DropConstraint;
+                                       n->name = $5;
+                                       n->behavior = $6;
+                                       n->missing_ok = TRUE;
+                                       $$ = (Node *)n;
+                               }
                        /* ALTER TABLE <name> DROP CONSTRAINT <name> [RESTRICT|CASCADE] */
                        | DROP CONSTRAINT name opt_drop_behavior
                                {
@@ -1647,6 +1668,7 @@ alter_table_cmd:
                                        n->subtype = AT_DropConstraint;
                                        n->name = $3;
                                        n->behavior = $4;
+                                       n->missing_ok = FALSE;
                                        $$ = (Node *)n;
                                }
                        /* ALTER TABLE <name> SET WITH OIDS  */
index 9d53ab97b91404f139a3726a14ddfb79e1c10453..183a6c73a1ad822a58d7503a8ded1cf39e3afda2 100644 (file)
@@ -1145,6 +1145,7 @@ typedef struct AlterTableCmd      /* one subcommand of an ALTER TABLE */
                                                                 * index, constraint, or parent table */
        Node       *transform;          /* transformation expr for ALTER TYPE */
        DropBehavior behavior;          /* RESTRICT or CASCADE for DROP cases */
+       bool            missing_ok;             /* skip error if missing? */
 } AlterTableCmd;
 
 
index b14d61fd34fb6d25cf13064d1722a4c4e22778da..031e59ca8f0be3bf1348d477d54d3ebd036ec9d7 100644 (file)
@@ -1150,6 +1150,12 @@ alter table gc1 drop column name;
 ERROR:  column "name" of relation "gc1" does not exist
 -- should work and drop the attribute in all tables
 alter table p2 drop column height;
+-- IF EXISTS test
+create table dropColumnExists ();
+alter table dropColumnExists drop column non_existing; --fail
+ERROR:  column "non_existing" of relation "dropcolumnexists" does not exist
+alter table dropColumnExists drop column if exists non_existing; --succeed
+NOTICE:  column "non_existing" of relation "dropcolumnexists" does not exist, skipping
 select relname, attname, attinhcount, attislocal
 from pg_class join pg_attribute on (pg_class.oid = pg_attribute.attrelid)
 where relname in ('p1','p2','c1','gc1') and attnum > 0 and not attisdropped
@@ -1421,6 +1427,10 @@ alter table anothertab alter column atcol1 type boolean
 ERROR:  operator does not exist: boolean <= integer
 HINT:  No operator matches the given name and argument type(s). You might need to add explicit type casts.
 alter table anothertab drop constraint anothertab_chk;
+alter table anothertab drop constraint anothertab_chk; -- fails
+ERROR:  constraint "anothertab_chk" of relation "anothertab" does not exist
+alter table anothertab drop constraint IF EXISTS anothertab_chk; -- succeeds
+NOTICE:  constraint "anothertab_chk" of relation "anothertab" does not exist, skipping
 alter table anothertab alter column atcol1 type boolean
         using case when atcol1 % 2 = 0 then true else false end;
 select * from anothertab;
index e041cec8439d539d0eb1adfe1fa9b4113b707c3b..82c2e4ee0172cfc23e845709ab9b9c2d93aa2ce8 100644 (file)
@@ -816,6 +816,8 @@ create table dropColumnAnother (d int) inherits (dropColumnChild);
 alter table dropColumnchild drop column a;
 alter table only dropColumnChild drop column b;
 
+
+
 -- these three should work
 alter table only dropColumn drop column e;
 alter table dropColumnChild drop column c;
@@ -913,6 +915,11 @@ alter table gc1 drop column name;
 -- should work and drop the attribute in all tables
 alter table p2 drop column height;
 
+-- IF EXISTS test
+create table dropColumnExists ();
+alter table dropColumnExists drop column non_existing; --fail
+alter table dropColumnExists drop column if exists non_existing; --succeed
+
 select relname, attname, attinhcount, attislocal
 from pg_class join pg_attribute on (pg_class.oid = pg_attribute.attrelid)
 where relname in ('p1','p2','c1','gc1') and attnum > 0 and not attisdropped
@@ -1057,6 +1064,8 @@ alter table anothertab alter column atcol1 drop default;
 alter table anothertab alter column atcol1 type boolean
         using case when atcol1 % 2 = 0 then true else false end; -- fails
 alter table anothertab drop constraint anothertab_chk;
+alter table anothertab drop constraint anothertab_chk; -- fails
+alter table anothertab drop constraint IF EXISTS anothertab_chk; -- succeeds
 
 alter table anothertab alter column atcol1 type boolean
         using case when atcol1 % 2 = 0 then true else false end;