bool is_view, AlterTableCmd *cmd, LOCKMODE lockmode);
static ObjectAddress ATExecAddColumn(List **wqueue, AlteredTableInfo *tab,
Relation rel, ColumnDef *colDef, bool isOid,
- bool recurse, bool recursing, LOCKMODE lockmode);
-static void check_for_column_name_collision(Relation rel, const char *colname);
+ bool recurse, bool recursing, bool if_not_exists, LOCKMODE lockmode);
+static bool check_for_column_name_collision(Relation rel, const char *colname,
+ bool if_not_exists);
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 ATPrepAddOids(List **wqueue, Relation rel, bool recurse,
oldattname)));
/* new name should not already exist */
- check_for_column_name_collision(targetrelation, newattname);
+ (void) check_for_column_name_collision(targetrelation, newattname, false);
/* apply the update */
namestrcpy(&(attform->attname), newattname);
case AT_AddColumnToView: /* add column via CREATE OR REPLACE
* VIEW */
address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
- false, false, false, lockmode);
+ false, false, false, false, lockmode);
break;
case AT_AddColumnRecurse:
address = ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
- false, true, false, lockmode);
+ false, true, false, cmd->missing_ok, lockmode);
break;
case AT_ColumnDefault: /* ALTER COLUMN DEFAULT */
address = ATExecColumnDefault(rel, cmd->name, cmd->def, lockmode);
if (cmd->def != NULL)
address =
ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
- true, false, false, lockmode);
+ true, false, false, cmd->missing_ok, lockmode);
break;
case AT_AddOidsRecurse: /* SET WITH OIDS */
/* Use the ADD COLUMN code, unless prep decided to do nothing */
if (cmd->def != NULL)
address =
ATExecAddColumn(wqueue, tab, rel, (ColumnDef *) cmd->def,
- true, true, false, lockmode);
+ true, true, false, cmd->missing_ok, lockmode);
break;
case AT_DropOids: /* SET WITHOUT OIDS */
static ObjectAddress
ATExecAddColumn(List **wqueue, AlteredTableInfo *tab, Relation rel,
ColumnDef *colDef, bool isOid,
- bool recurse, bool recursing, LOCKMODE lockmode)
+ bool recurse, bool recursing, bool if_not_exists, LOCKMODE lockmode)
{
Oid myrelid = RelationGetRelid(rel);
Relation pgclass,
elog(ERROR, "cache lookup failed for relation %u", myrelid);
relkind = ((Form_pg_class) GETSTRUCT(reltup))->relkind;
- /* new name should not already exist */
- check_for_column_name_collision(rel, colDef->colname);
+ /* skip if the name already exists and if_not_exists is true */
+ if (!check_for_column_name_collision(rel, colDef->colname, if_not_exists))
+ {
+ heap_close(attrdesc, RowExclusiveLock);
+ heap_freetuple(reltup);
+ heap_close(pgclass, RowExclusiveLock);
+ return InvalidObjectAddress;
+ }
/* Determine the new attribute's number */
if (isOid)
/* Recurse to child; return value is ignored */
ATExecAddColumn(wqueue, childtab, childrel,
- colDef, isOid, recurse, true, lockmode);
+ colDef, isOid, recurse, true,
+ if_not_exists, lockmode);
heap_close(childrel, NoLock);
}
/*
* If a new or renamed column will collide with the name of an existing
- * column, error out.
+ * column and if_not_exists is false then error out, else do nothing.
*/
-static void
-check_for_column_name_collision(Relation rel, const char *colname)
+static bool
+check_for_column_name_collision(Relation rel, const char *colname,
+ bool if_not_exists)
{
HeapTuple attTuple;
int attnum;
ObjectIdGetDatum(RelationGetRelid(rel)),
PointerGetDatum(colname));
if (!HeapTupleIsValid(attTuple))
- return;
+ return true;
attnum = ((Form_pg_attribute) GETSTRUCT(attTuple))->attnum;
ReleaseSysCache(attTuple);
errmsg("column name \"%s\" conflicts with a system column name",
colname)));
else
+ {
+ if (if_not_exists)
+ {
+ ereport(NOTICE,
+ (errcode(ERRCODE_DUPLICATE_COLUMN),
+ errmsg("column \"%s\" of relation \"%s\" already exists, skipping",
+ colname, RelationGetRelationName(rel))));
+ return false;
+ }
+
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_COLUMN),
errmsg("column \"%s\" of relation \"%s\" already exists",
colname, RelationGetRelationName(rel))));
+ }
+
+ return true;
}
/*
DROP TABLE logged3;
DROP TABLE logged2;
DROP TABLE logged1;
+-- test ADD COLUMN IF NOT EXISTS
+CREATE TABLE test_add_column(c1 integer);
+\d test_add_column
+Table "public.test_add_column"
+ Column | Type | Modifiers
+--------+---------+-----------
+ c1 | integer |
+
+ALTER TABLE test_add_column
+ ADD COLUMN c2 integer;
+\d test_add_column
+Table "public.test_add_column"
+ Column | Type | Modifiers
+--------+---------+-----------
+ c1 | integer |
+ c2 | integer |
+
+ALTER TABLE test_add_column
+ ADD COLUMN c2 integer; -- fail because c2 already exists
+ERROR: column "c2" of relation "test_add_column" already exists
+\d test_add_column
+Table "public.test_add_column"
+ Column | Type | Modifiers
+--------+---------+-----------
+ c1 | integer |
+ c2 | integer |
+
+ALTER TABLE test_add_column
+ ADD COLUMN IF NOT EXISTS c2 integer; -- skipping because c2 already exists
+NOTICE: column "c2" of relation "test_add_column" already exists, skipping
+\d test_add_column
+Table "public.test_add_column"
+ Column | Type | Modifiers
+--------+---------+-----------
+ c1 | integer |
+ c2 | integer |
+
+ALTER TABLE test_add_column
+ ADD COLUMN c2 integer, -- fail because c2 already exists
+ ADD COLUMN c3 integer;
+ERROR: column "c2" of relation "test_add_column" already exists
+\d test_add_column
+Table "public.test_add_column"
+ Column | Type | Modifiers
+--------+---------+-----------
+ c1 | integer |
+ c2 | integer |
+
+ALTER TABLE test_add_column
+ ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists
+ ADD COLUMN c3 integer; -- fail because c3 already exists
+NOTICE: column "c2" of relation "test_add_column" already exists, skipping
+\d test_add_column
+Table "public.test_add_column"
+ Column | Type | Modifiers
+--------+---------+-----------
+ c1 | integer |
+ c2 | integer |
+ c3 | integer |
+
+ALTER TABLE test_add_column
+ ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists
+ ADD COLUMN IF NOT EXISTS c3 integer; -- skipping because c3 already exists
+NOTICE: column "c2" of relation "test_add_column" already exists, skipping
+NOTICE: column "c3" of relation "test_add_column" already exists, skipping
+\d test_add_column
+Table "public.test_add_column"
+ Column | Type | Modifiers
+--------+---------+-----------
+ c1 | integer |
+ c2 | integer |
+ c3 | integer |
+
+ALTER TABLE test_add_column
+ ADD COLUMN IF NOT EXISTS c2 integer, -- skipping because c2 already exists
+ ADD COLUMN IF NOT EXISTS c3 integer, -- skipping because c3 already exists
+ ADD COLUMN c4 integer;
+NOTICE: column "c2" of relation "test_add_column" already exists, skipping
+NOTICE: column "c3" of relation "test_add_column" already exists, skipping
+\d test_add_column
+Table "public.test_add_column"
+ Column | Type | Modifiers
+--------+---------+-----------
+ c1 | integer |
+ c2 | integer |
+ c3 | integer |
+ c4 | integer |
+
+DROP TABLE test_add_column;