DROP IF EXISTS for columns and constraints. Andres Freund.
authorAndrew Dunstan <[email protected]>
Mon, 20 Jul 2009 02:42:28 +0000 (02:42 +0000)
committerAndrew Dunstan <[email protected]>
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 cccbfd2da6d76e34f7425b55855a96f9d3651188..87892f84b1e792193439cc287a5704da841f4a06 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.106 2009/05/03 20:45:43 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/alter_table.sgml,v 1.107 2009/07/20 02:42:27 adunstan Exp $
 PostgreSQL documentation
 -->
 
@@ -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 50ab811039b74769923fe86f00a404fa21b690d2..20253e1523fcb35a4b13577e1b834944d32cf27f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.290 2009/07/16 06:33:42 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.291 2009/07/20 02:42:27 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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 ca920d11f795dcda70878c3897d30d24317ec995..0111ac4a9d90d65537298ec4cb23593141965763 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.433 2009/07/16 06:33:42 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.434 2009/07/20 02:42:27 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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 ddb92a18e8bf5c0df9267d292858c58792890235..c2e3f643d3ee5545e864e5198bd8b1748ac25885 100644 (file)
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.356 2009/07/16 06:33:42 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.357 2009/07/20 02:42:27 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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 1e633ca73ca47af22a078e79982aa1f78e5c6abf..7e6d55be71274da9c7636270d328e5e8388d783d 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.670 2009/07/16 06:33:43 petere Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.671 2009/07/20 02:42:28 adunstan Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -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 ecd76d7b41c3e97b21ba6898cf7f825e64847c41..b54b170425cd1a3a47c91e4df90ade81d85aa376 100644 (file)
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1996-2009, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.396 2009/07/16 06:33:45 petere Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.397 2009/07/20 02:42:28 adunstan Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -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;