Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
Skip to content

Commit 7b90469

Browse files
committed
Allow adding values to an enum type created in the current transaction.
Normally it is unsafe to allow ALTER TYPE ADD VALUE in a transaction block, because instances of the value could be added to indexes later in the same transaction, and then they would still be accessible even if the transaction rolls back. However, we can allow this if the enum type itself was created in the current transaction, because then any such indexes would have to go away entirely on rollback. The reason for allowing this is to support pg_upgrade's new usage of pg_restore --single-transaction: in --binary-upgrade mode, pg_dump emits enum types as a succession of ALTER TYPE ADD VALUE commands so that it can preserve the values' OIDs. The support is a bit limited, so we'll leave it undocumented. Andres Freund
1 parent 452739d commit 7b90469

File tree

5 files changed

+74
-11
lines changed

5 files changed

+74
-11
lines changed

src/backend/commands/typecmds.c

+21-2
Original file line numberDiff line numberDiff line change
@@ -1169,7 +1169,7 @@ DefineEnum(CreateEnumStmt *stmt)
11691169
* Adds a new label to an existing enum.
11701170
*/
11711171
void
1172-
AlterEnum(AlterEnumStmt *stmt)
1172+
AlterEnum(AlterEnumStmt *stmt, bool isTopLevel)
11731173
{
11741174
Oid enum_type_oid;
11751175
TypeName *typename;
@@ -1183,12 +1183,31 @@ AlterEnum(AlterEnumStmt *stmt)
11831183
if (!HeapTupleIsValid(tup))
11841184
elog(ERROR, "cache lookup failed for type %u", enum_type_oid);
11851185

1186+
/*
1187+
* Ordinarily we disallow adding values within transaction blocks, because
1188+
* we can't cope with enum OID values getting into indexes and then having
1189+
* their defining pg_enum entries go away. However, it's okay if the enum
1190+
* type was created in the current transaction, since then there can be
1191+
* no such indexes that wouldn't themselves go away on rollback. (We
1192+
* support this case because pg_dump --binary-upgrade needs it.) We test
1193+
* this by seeing if the pg_type row has xmin == current XID and is not
1194+
* HEAP_UPDATED. If it is HEAP_UPDATED, we can't be sure whether the
1195+
* type was created or only modified in this xact. So we are disallowing
1196+
* some cases that could theoretically be safe; but fortunately pg_dump
1197+
* only needs the simplest case.
1198+
*/
1199+
if (HeapTupleHeaderGetXmin(tup->t_data) == GetCurrentTransactionId() &&
1200+
!(tup->t_data->t_infomask & HEAP_UPDATED))
1201+
/* safe to do inside transaction block */ ;
1202+
else
1203+
PreventTransactionChain(isTopLevel, "ALTER TYPE ... ADD");
1204+
11861205
/* Check it's an enum and check user has permission to ALTER the enum */
11871206
checkEnumOwner(tup);
11881207

11891208
/* Add the new label */
11901209
AddEnumLabel(enum_type_oid, stmt->newVal,
1191-
stmt->newValNeighbor, stmt->newValIsAfter,
1210+
stmt->newValNeighbor, stmt->newValIsAfter,
11921211
stmt->skipIfExists);
11931212

11941213
ReleaseSysCache(tup);

src/backend/tcop/utility.c

+1-8
Original file line numberDiff line numberDiff line change
@@ -972,14 +972,7 @@ standard_ProcessUtility(Node *parsetree,
972972
case T_AlterEnumStmt: /* ALTER TYPE (enum) */
973973
if (isCompleteQuery)
974974
EventTriggerDDLCommandStart(parsetree);
975-
976-
/*
977-
* We disallow this in transaction blocks, because we can't cope
978-
* with enum OID values getting into indexes and then having their
979-
* defining pg_enum entries go away.
980-
*/
981-
PreventTransactionChain(isTopLevel, "ALTER TYPE ... ADD");
982-
AlterEnum((AlterEnumStmt *) parsetree);
975+
AlterEnum((AlterEnumStmt *) parsetree, isTopLevel);
983976
break;
984977

985978
case T_ViewStmt: /* CREATE VIEW */

src/include/commands/typecmds.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ extern void RemoveTypeById(Oid typeOid);
2626
extern void DefineDomain(CreateDomainStmt *stmt);
2727
extern void DefineEnum(CreateEnumStmt *stmt);
2828
extern void DefineRange(CreateRangeStmt *stmt);
29-
extern void AlterEnum(AlterEnumStmt *stmt);
29+
extern void AlterEnum(AlterEnumStmt *stmt, bool isTopLevel);
3030
extern Oid DefineCompositeType(RangeVar *typevar, List *coldeflist);
3131
extern Oid AssignTypeArrayOid(void);
3232

src/test/regress/expected/enum.out

+24
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,30 @@ ERROR: foreign key constraint "enumtest_bogus_child_parent_fkey" cannot be impl
556556
DETAIL: Key columns "parent" and "id" are of incompatible types: bogus and rainbow.
557557
DROP TYPE bogus;
558558
--
559+
-- check transactional behaviour of ALTER TYPE ... ADD VALUE
560+
--
561+
CREATE TYPE bogus AS ENUM('good');
562+
-- check that we can't add new values to existing enums in a transaction
563+
BEGIN;
564+
ALTER TYPE bogus ADD VALUE 'bad';
565+
ERROR: ALTER TYPE ... ADD cannot run inside a transaction block
566+
COMMIT;
567+
-- check that we recognize the case where the enum already existed but was
568+
-- modified in the current txn
569+
BEGIN;
570+
ALTER TYPE bogus RENAME TO bogon;
571+
ALTER TYPE bogon ADD VALUE 'bad';
572+
ERROR: ALTER TYPE ... ADD cannot run inside a transaction block
573+
ROLLBACK;
574+
DROP TYPE bogus;
575+
-- check that we *can* add new values to existing enums in a transaction,
576+
-- if the type is new as well
577+
BEGIN;
578+
CREATE TYPE bogus AS ENUM();
579+
ALTER TYPE bogus ADD VALUE 'good';
580+
ALTER TYPE bogus ADD VALUE 'ugly';
581+
ROLLBACK;
582+
--
559583
-- Cleanup
560584
--
561585
DROP TABLE enumtest_child;

src/test/regress/sql/enum.sql

+27
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,33 @@ CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly');
257257
CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent);
258258
DROP TYPE bogus;
259259

260+
--
261+
-- check transactional behaviour of ALTER TYPE ... ADD VALUE
262+
--
263+
CREATE TYPE bogus AS ENUM('good');
264+
265+
-- check that we can't add new values to existing enums in a transaction
266+
BEGIN;
267+
ALTER TYPE bogus ADD VALUE 'bad';
268+
COMMIT;
269+
270+
-- check that we recognize the case where the enum already existed but was
271+
-- modified in the current txn
272+
BEGIN;
273+
ALTER TYPE bogus RENAME TO bogon;
274+
ALTER TYPE bogon ADD VALUE 'bad';
275+
ROLLBACK;
276+
277+
DROP TYPE bogus;
278+
279+
-- check that we *can* add new values to existing enums in a transaction,
280+
-- if the type is new as well
281+
BEGIN;
282+
CREATE TYPE bogus AS ENUM();
283+
ALTER TYPE bogus ADD VALUE 'good';
284+
ALTER TYPE bogus ADD VALUE 'ugly';
285+
ROLLBACK;
286+
260287
--
261288
-- Cleanup
262289
--

0 commit comments

Comments
 (0)