mirror of
https://github.com/postgres/postgres.git
synced 2025-12-09 02:08:45 +03:00
Support renaming an existing value of an enum type.
Not much to be said about this patch: it does what it says on the tin. In passing, rename AlterEnumStmt.skipIfExists to skipIfNewValExists to clarify what it actually does. In the discussion of this patch we considered supporting other similar options, such as IF EXISTS on the type as a whole or IF NOT EXISTS on the target name. This patch doesn't actually add any such feature, but it might happen later. Dagfinn Ilmari Mannsåker, reviewed by Emre Hasegeli Discussion: <CAO=2mx6uvgPaPDf-rHqG8=1MZnGyVDMQeh8zS4euRyyg4D35OQ@mail.gmail.com>
This commit is contained in:
@@ -465,6 +465,91 @@ restart:
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RenameEnumLabel
|
||||
* Rename a label in an enum set.
|
||||
*/
|
||||
void
|
||||
RenameEnumLabel(Oid enumTypeOid,
|
||||
const char *oldVal,
|
||||
const char *newVal)
|
||||
{
|
||||
Relation pg_enum;
|
||||
HeapTuple enum_tup;
|
||||
Form_pg_enum en;
|
||||
CatCList *list;
|
||||
int nelems;
|
||||
HeapTuple old_tup;
|
||||
bool found_new;
|
||||
int i;
|
||||
|
||||
/* check length of new label is ok */
|
||||
if (strlen(newVal) > (NAMEDATALEN - 1))
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_NAME),
|
||||
errmsg("invalid enum label \"%s\"", newVal),
|
||||
errdetail("Labels must be %d characters or less.",
|
||||
NAMEDATALEN - 1)));
|
||||
|
||||
/*
|
||||
* Acquire a lock on the enum type, which we won't release until commit.
|
||||
* This ensures that two backends aren't concurrently modifying the same
|
||||
* enum type. Since we are not changing the type's sort order, this is
|
||||
* probably not really necessary, but there seems no reason not to take
|
||||
* the lock to be sure.
|
||||
*/
|
||||
LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
|
||||
|
||||
pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
|
||||
|
||||
/* Get the list of existing members of the enum */
|
||||
list = SearchSysCacheList1(ENUMTYPOIDNAME,
|
||||
ObjectIdGetDatum(enumTypeOid));
|
||||
nelems = list->n_members;
|
||||
|
||||
/*
|
||||
* Locate the element to rename and check if the new label is already in
|
||||
* use. (The unique index on pg_enum would catch that anyway, but we
|
||||
* prefer a friendlier error message.)
|
||||
*/
|
||||
old_tup = NULL;
|
||||
found_new = false;
|
||||
for (i = 0; i < nelems; i++)
|
||||
{
|
||||
enum_tup = &(list->members[i]->tuple);
|
||||
en = (Form_pg_enum) GETSTRUCT(enum_tup);
|
||||
if (strcmp(NameStr(en->enumlabel), oldVal) == 0)
|
||||
old_tup = enum_tup;
|
||||
if (strcmp(NameStr(en->enumlabel), newVal) == 0)
|
||||
found_new = true;
|
||||
}
|
||||
if (!old_tup)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
|
||||
errmsg("\"%s\" is not an existing enum label",
|
||||
oldVal)));
|
||||
if (found_new)
|
||||
ereport(ERROR,
|
||||
(errcode(ERRCODE_DUPLICATE_OBJECT),
|
||||
errmsg("enum label \"%s\" already exists",
|
||||
newVal)));
|
||||
|
||||
/* OK, make a writable copy of old tuple */
|
||||
enum_tup = heap_copytuple(old_tup);
|
||||
en = (Form_pg_enum) GETSTRUCT(enum_tup);
|
||||
|
||||
ReleaseCatCacheList(list);
|
||||
|
||||
/* Update the pg_enum entry */
|
||||
namestrcpy(&en->enumlabel, newVal);
|
||||
simple_heap_update(pg_enum, &enum_tup->t_self, enum_tup);
|
||||
CatalogUpdateIndexes(pg_enum, enum_tup);
|
||||
heap_freetuple(enum_tup);
|
||||
|
||||
heap_close(pg_enum, RowExclusiveLock);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* RenumberEnumType
|
||||
* Renumber existing enum elements to have sort positions 1..n.
|
||||
|
||||
@@ -1241,17 +1241,25 @@ AlterEnum(AlterEnumStmt *stmt)
|
||||
/* Check it's an enum and check user has permission to ALTER the enum */
|
||||
checkEnumOwner(tup);
|
||||
|
||||
/* Add the new label */
|
||||
AddEnumLabel(enum_type_oid, stmt->newVal,
|
||||
stmt->newValNeighbor, stmt->newValIsAfter,
|
||||
stmt->skipIfExists);
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
if (stmt->oldVal)
|
||||
{
|
||||
/* Rename an existing label */
|
||||
RenameEnumLabel(enum_type_oid, stmt->oldVal, stmt->newVal);
|
||||
}
|
||||
else
|
||||
{
|
||||
/* Add a new label */
|
||||
AddEnumLabel(enum_type_oid, stmt->newVal,
|
||||
stmt->newValNeighbor, stmt->newValIsAfter,
|
||||
stmt->skipIfNewValExists);
|
||||
}
|
||||
|
||||
InvokeObjectPostAlterHook(TypeRelationId, enum_type_oid, 0);
|
||||
|
||||
ObjectAddressSet(address, TypeRelationId, enum_type_oid);
|
||||
|
||||
ReleaseSysCache(tup);
|
||||
|
||||
return address;
|
||||
}
|
||||
|
||||
|
||||
@@ -3375,10 +3375,11 @@ _copyAlterEnumStmt(const AlterEnumStmt *from)
|
||||
AlterEnumStmt *newnode = makeNode(AlterEnumStmt);
|
||||
|
||||
COPY_NODE_FIELD(typeName);
|
||||
COPY_STRING_FIELD(oldVal);
|
||||
COPY_STRING_FIELD(newVal);
|
||||
COPY_STRING_FIELD(newValNeighbor);
|
||||
COPY_SCALAR_FIELD(newValIsAfter);
|
||||
COPY_SCALAR_FIELD(skipIfExists);
|
||||
COPY_SCALAR_FIELD(skipIfNewValExists);
|
||||
|
||||
return newnode;
|
||||
}
|
||||
|
||||
@@ -1465,10 +1465,11 @@ static bool
|
||||
_equalAlterEnumStmt(const AlterEnumStmt *a, const AlterEnumStmt *b)
|
||||
{
|
||||
COMPARE_NODE_FIELD(typeName);
|
||||
COMPARE_STRING_FIELD(oldVal);
|
||||
COMPARE_STRING_FIELD(newVal);
|
||||
COMPARE_STRING_FIELD(newValNeighbor);
|
||||
COMPARE_SCALAR_FIELD(newValIsAfter);
|
||||
COMPARE_SCALAR_FIELD(skipIfExists);
|
||||
COMPARE_SCALAR_FIELD(skipIfNewValExists);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -5257,30 +5257,44 @@ AlterEnumStmt:
|
||||
{
|
||||
AlterEnumStmt *n = makeNode(AlterEnumStmt);
|
||||
n->typeName = $3;
|
||||
n->oldVal = NULL;
|
||||
n->newVal = $7;
|
||||
n->newValNeighbor = NULL;
|
||||
n->newValIsAfter = true;
|
||||
n->skipIfExists = $6;
|
||||
n->skipIfNewValExists = $6;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst BEFORE Sconst
|
||||
{
|
||||
AlterEnumStmt *n = makeNode(AlterEnumStmt);
|
||||
n->typeName = $3;
|
||||
n->oldVal = NULL;
|
||||
n->newVal = $7;
|
||||
n->newValNeighbor = $9;
|
||||
n->newValIsAfter = false;
|
||||
n->skipIfExists = $6;
|
||||
n->skipIfNewValExists = $6;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| ALTER TYPE_P any_name ADD_P VALUE_P opt_if_not_exists Sconst AFTER Sconst
|
||||
{
|
||||
AlterEnumStmt *n = makeNode(AlterEnumStmt);
|
||||
n->typeName = $3;
|
||||
n->oldVal = NULL;
|
||||
n->newVal = $7;
|
||||
n->newValNeighbor = $9;
|
||||
n->newValIsAfter = true;
|
||||
n->skipIfExists = $6;
|
||||
n->skipIfNewValExists = $6;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
| ALTER TYPE_P any_name RENAME VALUE_P Sconst TO Sconst
|
||||
{
|
||||
AlterEnumStmt *n = makeNode(AlterEnumStmt);
|
||||
n->typeName = $3;
|
||||
n->oldVal = $6;
|
||||
n->newVal = $8;
|
||||
n->newValNeighbor = NULL;
|
||||
n->newValIsAfter = false;
|
||||
n->skipIfNewValExists = false;
|
||||
$$ = (Node *) n;
|
||||
}
|
||||
;
|
||||
|
||||
Reference in New Issue
Block a user