Commit 6d12b68c authored by Andrew Dunstan's avatar Andrew Dunstan

Allow IF NOT EXISTS when add a new enum label.

If the label is already in the enum the statement becomes a no-op.
This will reduce the pain that comes from our not allowing this
operation inside a transaction block.

Andrew Dunstan, reviewed by Tom Lane and Magnus Hagander.
parent 11e13185
......@@ -28,7 +28,7 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> OWNER TO <replaceab
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME ATTRIBUTE <replaceable class="PARAMETER">attribute_name</replaceable> TO <replaceable class="PARAMETER">new_attribute_name</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> RENAME TO <replaceable class="PARAMETER">new_name</replaceable> [ CASCADE | RESTRICT ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> SET SCHEMA <replaceable class="PARAMETER">new_schema</replaceable>
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD VALUE <replaceable class="PARAMETER">new_enum_value</replaceable> [ { BEFORE | AFTER } <replaceable class="PARAMETER">existing_enum_value</replaceable> ]
ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD VALUE [ IF NOT EXISTS ] <replaceable class="PARAMETER">new_enum_value</replaceable> [ { BEFORE | AFTER } <replaceable class="PARAMETER">existing_enum_value</replaceable> ]
<phrase>where <replaceable class="PARAMETER">action</replaceable> is one of:</phrase>
......@@ -106,7 +106,7 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD VALUE <replacea
</varlistentry>
<varlistentry>
<term><literal>ADD VALUE [ BEFORE | AFTER ]</literal></term>
<term><literal>ADD VALUE [ IF NOT EXISTS ] [ BEFORE | AFTER ]</literal></term>
<listitem>
<para>
This form adds a new value to an enum type. If the new value's place in
......@@ -114,6 +114,11 @@ ALTER TYPE <replaceable class="PARAMETER">name</replaceable> ADD VALUE <replacea
<literal>AFTER</literal>, then the new item is placed at the end of the
list of values.
</para>
<para>
If <literal>IF NOT EXISTS</literal is used, it is not an error if the
type already contains the new value, and no action is taken. Otherwise,
an error will occur if the new value is already present.
</para>
</listitem>
</varlistentry>
......
......@@ -179,7 +179,8 @@ void
AddEnumLabel(Oid enumTypeOid,
const char *newVal,
const char *neighbor,
bool newValIsAfter)
bool newValIsAfter,
bool skipIfExists)
{
Relation pg_enum;
Oid newOid;
......@@ -211,6 +212,21 @@ AddEnumLabel(Oid enumTypeOid,
*/
LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
/* Do the "IF NOT EXISTS" test if specified */
if (skipIfExists)
{
HeapTuple tup;
tup = SearchSysCache2(ENUMTYPOIDNAME,
ObjectIdGetDatum(enumTypeOid),
CStringGetDatum(newVal));
if (HeapTupleIsValid(tup))
{
ReleaseSysCache(tup);
return;
}
}
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
/* If we have to renumber the existing members, we restart from here */
......
......@@ -1188,7 +1188,8 @@ AlterEnum(AlterEnumStmt *stmt)
/* Add the new label */
AddEnumLabel(enum_type_oid, stmt->newVal,
stmt->newValNeighbor, stmt->newValIsAfter);
stmt->newValNeighbor, stmt->newValIsAfter,
stmt->skipIfExists);
ReleaseSysCache(tup);
}
......
......@@ -3055,6 +3055,7 @@ _copyAlterEnumStmt(const AlterEnumStmt *from)
COPY_STRING_FIELD(newVal);
COPY_STRING_FIELD(newValNeighbor);
COPY_SCALAR_FIELD(newValIsAfter);
COPY_SCALAR_FIELD(skipIfExists);
return newnode;
}
......
......@@ -1439,6 +1439,7 @@ _equalAlterEnumStmt(const AlterEnumStmt *a, const AlterEnumStmt *b)
COMPARE_STRING_FIELD(newVal);
COMPARE_STRING_FIELD(newValNeighbor);
COMPARE_SCALAR_FIELD(newValIsAfter);
COMPARE_SCALAR_FIELD(skipIfExists);
return true;
}
......
......@@ -470,7 +470,7 @@ static void processCASbits(int cas_bits, int location, const char *constrType,
%type <windef> window_definition over_clause window_specification
opt_frame_clause frame_extent frame_bound
%type <str> opt_existing_window_name
%type <boolean> opt_if_not_exists
/*
* Non-keyword token types. These are hard-wired into the "flex" lexer.
......@@ -4618,35 +4618,42 @@ enum_val_list: Sconst
*****************************************************************************/
AlterEnumStmt:
ALTER TYPE_P any_name ADD_P VALUE_P Sconst
ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst
{
AlterEnumStmt *n = makeNode(AlterEnumStmt);
n->typeName = $3;
n->newVal = $6;
n->newVal = $7;
n->newValNeighbor = NULL;
n->newValIsAfter = true;
n->skipIfExists = $6;
$$ = (Node *) n;
}
| ALTER TYPE_P any_name ADD_P VALUE_P Sconst BEFORE Sconst
| ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst BEFORE Sconst
{
AlterEnumStmt *n = makeNode(AlterEnumStmt);
n->typeName = $3;
n->newVal = $6;
n->newValNeighbor = $8;
n->newVal = $7;
n->newValNeighbor = $9;
n->newValIsAfter = false;
n->skipIfExists = $6;
$$ = (Node *) n;
}
| ALTER TYPE_P any_name ADD_P VALUE_P Sconst AFTER Sconst
| ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst AFTER Sconst
{
AlterEnumStmt *n = makeNode(AlterEnumStmt);
n->typeName = $3;
n->newVal = $6;
n->newValNeighbor = $8;
n->newVal = $7;
n->newValNeighbor = $9;
n->newValIsAfter = true;
n->skipIfExists = $6;
$$ = (Node *) n;
}
;
opt_if_not_exists: IF_P NOT EXISTS { $$ = true; }
| /* empty */ { $$ = false; }
;
/*****************************************************************************
*
......
......@@ -65,6 +65,7 @@ typedef FormData_pg_enum *Form_pg_enum;
extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
extern void EnumValuesDelete(Oid enumTypeOid);
extern void AddEnumLabel(Oid enumTypeOid, const char *newVal,
const char *neighbor, bool newValIsAfter);
const char *neighbor, bool newValIsAfter,
bool skipIfExists);
#endif /* PG_ENUM_H */
......@@ -2306,6 +2306,7 @@ typedef struct AlterEnumStmt
char *newVal; /* new enum value's name */
char *newValNeighbor; /* neighboring enum value, if specified */
bool newValIsAfter; /* place new enum value after neighbor? */
bool skipIfExists; /* ignore statement if label already exists */
} AlterEnumStmt;
/* ----------------------
......
......@@ -95,6 +95,28 @@ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutopl
DETAIL: Labels must be 63 characters or less.
ALTER TYPE planets ADD VALUE 'pluto' AFTER 'zeus';
ERROR: "zeus" is not an existing enum label
-- if not exists tests
-- existing value gives error
-- We can't do this test because the error contains the
-- offending Oid value, which is unpredictable.
-- ALTER TYPE planets ADD VALUE 'mercury';
-- unless IF NOT EXISTS is specified
ALTER TYPE planets ADD VALUE IF NOT EXISTS 'mercury';
-- should be neptune, not mercury
SELECT enum_last(NULL::planets);
enum_last
-----------
neptune
(1 row)
ALTER TYPE planets ADD VALUE IF NOT EXISTS 'pluto';
-- should be pluto, i.e. the new value
SELECT enum_last(NULL::planets);
enum_last
-----------
pluto
(1 row)
--
-- Test inserting so many values that we have to renumber
--
......
......@@ -54,6 +54,26 @@ ALTER TYPE planets ADD VALUE
ALTER TYPE planets ADD VALUE 'pluto' AFTER 'zeus';
-- if not exists tests
-- existing value gives error
-- We can't do this test because the error contains the
-- offending Oid value, which is unpredictable.
-- ALTER TYPE planets ADD VALUE 'mercury';
-- unless IF NOT EXISTS is specified
ALTER TYPE planets ADD VALUE IF NOT EXISTS 'mercury';
-- should be neptune, not mercury
SELECT enum_last(NULL::planets);
ALTER TYPE planets ADD VALUE IF NOT EXISTS 'pluto';
-- should be pluto, i.e. the new value
SELECT enum_last(NULL::planets);
--
-- Test inserting so many values that we have to renumber
--
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment