diff --git a/doc/src/sgml/ref/alter_table.sgml b/doc/src/sgml/ref/alter_table.sgml index cccbfd2da6d76e34f7425b55855a96f9d3651188..87892f84b1e792193439cc287a5704da841f4a06 100644 --- a/doc/src/sgml/ref/alter_table.sgml +++ b/doc/src/sgml/ref/alter_table.sgml @@ -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> diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 50ab811039b74769923fe86f00a404fa21b690d2..20253e1523fcb35a4b13577e1b834944d32cf27f 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -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 { diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index ca920d11f795dcda70878c3897d30d24317ec995..0111ac4a9d90d65537298ec4cb23593141965763 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -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; } diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index ddb92a18e8bf5c0df9267d292858c58792890235..c2e3f643d3ee5545e864e5198bd8b1748ac25885 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -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; } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 1e633ca73ca47af22a078e79982aa1f78e5c6abf..7e6d55be71274da9c7636270d328e5e8388d783d 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -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 */ diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index ecd76d7b41c3e97b21ba6898cf7f825e64847c41..b54b170425cd1a3a47c91e4df90ade81d85aa376 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -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; diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index b14d61fd34fb6d25cf13064d1722a4c4e22778da..031e59ca8f0be3bf1348d477d54d3ebd036ec9d7 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -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; diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index e041cec8439d539d0eb1adfe1fa9b4113b707c3b..82c2e4ee0172cfc23e845709ab9b9c2d93aa2ce8 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -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;