From cbffe37aa9ea8ab4303a0723605b1b8def1b966b Mon Sep 17 00:00:00 2001 From: Bradley Ayers Date: Fri, 1 Apr 2022 00:25:16 +1100 Subject: [PATCH v1] =?UTF-8?q?Add=20IF=20EXISTS=20support=20to=20ALTER=20C?= =?UTF-8?q?OLUMN=20=E2=80=A6=20{SET|DROP}=20NOT=20NULL?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This brings the ergonomics of the ADD COLUMN IF EXISTS command to the ALTER TABLE ... SET/DROP NOT NULL commands. --- src/backend/commands/tablecmds.c | 50 ++++++++++++++++------- src/backend/parser/gram.y | 20 +++++++++ src/test/regress/expected/alter_table.out | 12 ++++++ src/test/regress/sql/alter_table.sql | 10 +++++ 4 files changed, 78 insertions(+), 14 deletions(-) diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c index 3e83f375b5..b28c5fc8ad 100644 --- a/src/backend/commands/tablecmds.c +++ b/src/backend/commands/tablecmds.c @@ -422,13 +422,13 @@ static bool check_for_column_name_collision(Relation rel, const char *colname, static void add_column_datatype_dependency(Oid relid, int32 attnum, Oid typid); static void add_column_collation_dependency(Oid relid, int32 attnum, Oid collid); static void ATPrepDropNotNull(Relation rel, bool recurse, bool recursing); -static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode); +static ObjectAddress ATExecDropNotNull(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode); static void ATPrepSetNotNull(List **wqueue, Relation rel, AlterTableCmd *cmd, bool recurse, bool recursing, LOCKMODE lockmode, AlterTableUtilityContext *context); static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, - const char *colName, LOCKMODE lockmode); + const char *colName, bool missing_ok, LOCKMODE lockmode); static void ATExecCheckNotNull(AlteredTableInfo *tab, Relation rel, const char *colName, LOCKMODE lockmode); static bool NotNullImpliedByRelConstraints(Relation rel, Form_pg_attribute attr); @@ -4915,10 +4915,10 @@ ATExecCmd(List **wqueue, AlteredTableInfo *tab, address = ATExecDropIdentity(rel, cmd->name, cmd->missing_ok, lockmode); break; case AT_DropNotNull: /* ALTER COLUMN DROP NOT NULL */ - address = ATExecDropNotNull(rel, cmd->name, lockmode); + address = ATExecDropNotNull(rel, cmd->name, cmd->missing_ok, lockmode); break; case AT_SetNotNull: /* ALTER COLUMN SET NOT NULL */ - address = ATExecSetNotNull(tab, rel, cmd->name, lockmode); + address = ATExecSetNotNull(tab, rel, cmd->name, cmd->missing_ok, lockmode); break; case AT_CheckNotNull: /* check column is already marked NOT NULL */ ATExecCheckNotNull(tab, rel, cmd->name, lockmode); @@ -7128,7 +7128,7 @@ ATPrepDropNotNull(Relation rel, bool recurse, bool recursing) * nullable, InvalidObjectAddress is returned. */ static ObjectAddress -ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode) +ATExecDropNotNull(Relation rel, const char *colName, bool missing_ok, LOCKMODE lockmode) { HeapTuple tuple; Form_pg_attribute attTup; @@ -7145,10 +7145,21 @@ ATExecDropNotNull(Relation rel, const char *colName, LOCKMODE lockmode) tuple = SearchSysCacheCopyAttName(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 (!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)))); + table_close(attr_rel, RowExclusiveLock); + return InvalidObjectAddress; + } + } attTup = (Form_pg_attribute) GETSTRUCT(tuple); attnum = attTup->attnum; @@ -7336,7 +7347,7 @@ ATPrepSetNotNull(List **wqueue, Relation rel, */ static ObjectAddress ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, - const char *colName, LOCKMODE lockmode) + const char *colName, bool missing_ok, LOCKMODE lockmode) { HeapTuple tuple; AttrNumber attnum; @@ -7351,10 +7362,21 @@ ATExecSetNotNull(AlteredTableInfo *tab, Relation rel, tuple = SearchSysCacheCopyAttName(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 (!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)))); + table_close(attr_rel, RowExclusiveLock); + return InvalidObjectAddress; + } + } attnum = ((Form_pg_attribute) GETSTRUCT(tuple))->attnum; diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index a03b33b53b..aa816247d7 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -2247,6 +2247,16 @@ alter_table_cmd: AlterTableCmd *n = makeNode(AlterTableCmd); n->subtype = AT_DropNotNull; n->name = $3; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER TABLE ALTER [COLUMN] IF EXISTS DROP NOT NULL */ + | ALTER opt_column IF_P EXISTS ColId DROP NOT NULL_P + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_DropNotNull; + n->name = $5; + n->missing_ok = true; $$ = (Node *)n; } /* ALTER TABLE ALTER [COLUMN] SET NOT NULL */ @@ -2255,6 +2265,16 @@ alter_table_cmd: AlterTableCmd *n = makeNode(AlterTableCmd); n->subtype = AT_SetNotNull; n->name = $3; + n->missing_ok = false; + $$ = (Node *)n; + } + /* ALTER TABLE ALTER [COLUMN] IF EXISTS SET NOT NULL */ + | ALTER opt_column IF_P EXISTS ColId SET NOT NULL_P + { + AlterTableCmd *n = makeNode(AlterTableCmd); + n->subtype = AT_SetNotNull; + n->name = $5; + n->missing_ok = true; $$ = (Node *)n; } /* ALTER TABLE ALTER [COLUMN] DROP EXPRESSION */ diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out index 16e0475663..cc6c9065d1 100644 --- a/src/test/regress/expected/alter_table.out +++ b/src/test/regress/expected/alter_table.out @@ -1787,6 +1787,18 @@ 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 +-- ALTER TABLE ... ALTER COLUMN IF EXISTS ... SET NOT NULL test +create table setNotNullColumnExists (); +alter table setNotNullColumnExists alter column non_existing set not null; --fail +ERROR: column "non_existing" of relation "setnotnullcolumnexists" does not exist +alter table setNotNullColumnExists alter column if exists non_existing set not null; --succeed +NOTICE: column "non_existing" of relation "setnotnullcolumnexists" does not exist, skipping +-- ALTER TABLE ... ALTER COLUMN IF EXISTS ... DROP NOT NULL test +create table dropNotNullColumnExists (); +alter table dropNotNullColumnExists alter column non_existing drop not null; --fail +ERROR: column "non_existing" of relation "dropnotnullcolumnexists" does not exist +alter table dropNotNullColumnExists alter column if exists non_existing drop not null; --succeed +NOTICE: column "non_existing" of relation "dropnotnullcolumnexists" 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 diff --git a/src/test/regress/sql/alter_table.sql b/src/test/regress/sql/alter_table.sql index ac894c0602..18e1440b8c 100644 --- a/src/test/regress/sql/alter_table.sql +++ b/src/test/regress/sql/alter_table.sql @@ -1271,6 +1271,16 @@ create table dropColumnExists (); alter table dropColumnExists drop column non_existing; --fail alter table dropColumnExists drop column if exists non_existing; --succeed +-- ALTER TABLE ... ALTER COLUMN IF EXISTS ... SET NOT NULL test +create table setNotNullColumnExists (); +alter table setNotNullColumnExists alter column non_existing set not null; --fail +alter table setNotNullColumnExists alter column if exists non_existing set not null; --succeed + +-- ALTER TABLE ... ALTER COLUMN IF EXISTS ... DROP NOT NULL test +create table dropNotNullColumnExists (); +alter table dropNotNullColumnExists alter column non_existing drop not null; --fail +alter table dropNotNullColumnExists alter column if exists non_existing drop not null; --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 -- 2.34.1