1
0
mirror of https://github.com/postgres/postgres.git synced 2025-07-28 23:42:10 +03:00

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.
This commit is contained in:
Andrew Dunstan
2012-09-22 12:53:31 -04:00
parent 11e131854f
commit 6d12b68cd7
10 changed files with 89 additions and 14 deletions

View File

@ -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 */

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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; }
;
/*****************************************************************************
*

View File

@ -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 */

View File

@ -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;
/* ----------------------

View File

@ -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
--

View File

@ -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
--