mirror of
https://git.postgresql.org/git/postgresql.git
synced 2024-12-27 08:39:28 +08: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:
parent
11e131854f
commit
6d12b68cd7
@ -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 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> 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> 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>
|
<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>
|
||||||
|
|
||||||
<varlistentry>
|
<varlistentry>
|
||||||
<term><literal>ADD VALUE [ BEFORE | AFTER ]</literal></term>
|
<term><literal>ADD VALUE [ IF NOT EXISTS ] [ BEFORE | AFTER ]</literal></term>
|
||||||
<listitem>
|
<listitem>
|
||||||
<para>
|
<para>
|
||||||
This form adds a new value to an enum type. If the new value's place in
|
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
|
<literal>AFTER</literal>, then the new item is placed at the end of the
|
||||||
list of values.
|
list of values.
|
||||||
</para>
|
</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>
|
</listitem>
|
||||||
</varlistentry>
|
</varlistentry>
|
||||||
|
|
||||||
|
@ -179,7 +179,8 @@ void
|
|||||||
AddEnumLabel(Oid enumTypeOid,
|
AddEnumLabel(Oid enumTypeOid,
|
||||||
const char *newVal,
|
const char *newVal,
|
||||||
const char *neighbor,
|
const char *neighbor,
|
||||||
bool newValIsAfter)
|
bool newValIsAfter,
|
||||||
|
bool skipIfExists)
|
||||||
{
|
{
|
||||||
Relation pg_enum;
|
Relation pg_enum;
|
||||||
Oid newOid;
|
Oid newOid;
|
||||||
@ -211,6 +212,21 @@ AddEnumLabel(Oid enumTypeOid,
|
|||||||
*/
|
*/
|
||||||
LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
|
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);
|
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
|
||||||
|
|
||||||
/* If we have to renumber the existing members, we restart from here */
|
/* If we have to renumber the existing members, we restart from here */
|
||||||
|
@ -1188,7 +1188,8 @@ AlterEnum(AlterEnumStmt *stmt)
|
|||||||
|
|
||||||
/* Add the new label */
|
/* Add the new label */
|
||||||
AddEnumLabel(enum_type_oid, stmt->newVal,
|
AddEnumLabel(enum_type_oid, stmt->newVal,
|
||||||
stmt->newValNeighbor, stmt->newValIsAfter);
|
stmt->newValNeighbor, stmt->newValIsAfter,
|
||||||
|
stmt->skipIfExists);
|
||||||
|
|
||||||
ReleaseSysCache(tup);
|
ReleaseSysCache(tup);
|
||||||
}
|
}
|
||||||
|
@ -3055,6 +3055,7 @@ _copyAlterEnumStmt(const AlterEnumStmt *from)
|
|||||||
COPY_STRING_FIELD(newVal);
|
COPY_STRING_FIELD(newVal);
|
||||||
COPY_STRING_FIELD(newValNeighbor);
|
COPY_STRING_FIELD(newValNeighbor);
|
||||||
COPY_SCALAR_FIELD(newValIsAfter);
|
COPY_SCALAR_FIELD(newValIsAfter);
|
||||||
|
COPY_SCALAR_FIELD(skipIfExists);
|
||||||
|
|
||||||
return newnode;
|
return newnode;
|
||||||
}
|
}
|
||||||
|
@ -1439,6 +1439,7 @@ _equalAlterEnumStmt(const AlterEnumStmt *a, const AlterEnumStmt *b)
|
|||||||
COMPARE_STRING_FIELD(newVal);
|
COMPARE_STRING_FIELD(newVal);
|
||||||
COMPARE_STRING_FIELD(newValNeighbor);
|
COMPARE_STRING_FIELD(newValNeighbor);
|
||||||
COMPARE_SCALAR_FIELD(newValIsAfter);
|
COMPARE_SCALAR_FIELD(newValIsAfter);
|
||||||
|
COMPARE_SCALAR_FIELD(skipIfExists);
|
||||||
|
|
||||||
return true;
|
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
|
%type <windef> window_definition over_clause window_specification
|
||||||
opt_frame_clause frame_extent frame_bound
|
opt_frame_clause frame_extent frame_bound
|
||||||
%type <str> opt_existing_window_name
|
%type <str> opt_existing_window_name
|
||||||
|
%type <boolean> opt_if_not_exists
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Non-keyword token types. These are hard-wired into the "flex" lexer.
|
* Non-keyword token types. These are hard-wired into the "flex" lexer.
|
||||||
@ -4618,35 +4618,42 @@ enum_val_list: Sconst
|
|||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
AlterEnumStmt:
|
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);
|
AlterEnumStmt *n = makeNode(AlterEnumStmt);
|
||||||
n->typeName = $3;
|
n->typeName = $3;
|
||||||
n->newVal = $6;
|
n->newVal = $7;
|
||||||
n->newValNeighbor = NULL;
|
n->newValNeighbor = NULL;
|
||||||
n->newValIsAfter = true;
|
n->newValIsAfter = true;
|
||||||
|
n->skipIfExists = $6;
|
||||||
$$ = (Node *) n;
|
$$ = (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);
|
AlterEnumStmt *n = makeNode(AlterEnumStmt);
|
||||||
n->typeName = $3;
|
n->typeName = $3;
|
||||||
n->newVal = $6;
|
n->newVal = $7;
|
||||||
n->newValNeighbor = $8;
|
n->newValNeighbor = $9;
|
||||||
n->newValIsAfter = false;
|
n->newValIsAfter = false;
|
||||||
|
n->skipIfExists = $6;
|
||||||
$$ = (Node *) n;
|
$$ = (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);
|
AlterEnumStmt *n = makeNode(AlterEnumStmt);
|
||||||
n->typeName = $3;
|
n->typeName = $3;
|
||||||
n->newVal = $6;
|
n->newVal = $7;
|
||||||
n->newValNeighbor = $8;
|
n->newValNeighbor = $9;
|
||||||
n->newValIsAfter = true;
|
n->newValIsAfter = true;
|
||||||
|
n->skipIfExists = $6;
|
||||||
$$ = (Node *) n;
|
$$ = (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 EnumValuesCreate(Oid enumTypeOid, List *vals);
|
||||||
extern void EnumValuesDelete(Oid enumTypeOid);
|
extern void EnumValuesDelete(Oid enumTypeOid);
|
||||||
extern void AddEnumLabel(Oid enumTypeOid, const char *newVal,
|
extern void AddEnumLabel(Oid enumTypeOid, const char *newVal,
|
||||||
const char *neighbor, bool newValIsAfter);
|
const char *neighbor, bool newValIsAfter,
|
||||||
|
bool skipIfExists);
|
||||||
|
|
||||||
#endif /* PG_ENUM_H */
|
#endif /* PG_ENUM_H */
|
||||||
|
@ -2306,6 +2306,7 @@ typedef struct AlterEnumStmt
|
|||||||
char *newVal; /* new enum value's name */
|
char *newVal; /* new enum value's name */
|
||||||
char *newValNeighbor; /* neighboring enum value, if specified */
|
char *newValNeighbor; /* neighboring enum value, if specified */
|
||||||
bool newValIsAfter; /* place new enum value after neighbor? */
|
bool newValIsAfter; /* place new enum value after neighbor? */
|
||||||
|
bool skipIfExists; /* ignore statement if label already exists */
|
||||||
} AlterEnumStmt;
|
} AlterEnumStmt;
|
||||||
|
|
||||||
/* ----------------------
|
/* ----------------------
|
||||||
|
@ -95,6 +95,28 @@ ERROR: invalid enum label "plutoplutoplutoplutoplutoplutoplutoplutoplutoplutopl
|
|||||||
DETAIL: Labels must be 63 characters or less.
|
DETAIL: Labels must be 63 characters or less.
|
||||||
ALTER TYPE planets ADD VALUE 'pluto' AFTER 'zeus';
|
ALTER TYPE planets ADD VALUE 'pluto' AFTER 'zeus';
|
||||||
ERROR: "zeus" is not an existing enum label
|
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
|
-- 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';
|
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
|
-- Test inserting so many values that we have to renumber
|
||||||
--
|
--
|
||||||
|
Loading…
Reference in New Issue
Block a user