diff --git a/src/backend/access/transam/xact.c b/src/backend/access/transam/xact.c index 93dca7a..1d6f774 100644 --- a/src/backend/access/transam/xact.c +++ b/src/backend/access/transam/xact.c @@ -32,6 +32,7 @@ #include "access/xlogutils.h" #include "catalog/catalog.h" #include "catalog/namespace.h" +#include "catalog/pg_enum.h" #include "catalog/storage.h" #include "commands/async.h" #include "commands/tablecmds.h" @@ -2126,6 +2127,7 @@ CommitTransaction(void) smgrDoPendingDeletes(true); AtCommit_Notify(); + AtEOXact_Enum(); AtEOXact_GUC(true, 1); AtEOXact_SPI(true); AtEOXact_on_commit_actions(true); @@ -2405,6 +2407,7 @@ PrepareTransaction(void) /* PREPARE acts the same as COMMIT as far as GUC is concerned */ AtEOXact_GUC(true, 1); + AtEOXact_Enum(); AtEOXact_SPI(true); AtEOXact_on_commit_actions(true); AtEOXact_Namespace(true, false); @@ -2606,6 +2609,7 @@ AbortTransaction(void) false, true); smgrDoPendingDeletes(false); + AtEOXact_Enum(); AtEOXact_GUC(false, 1); AtEOXact_SPI(false); AtEOXact_on_commit_actions(false); diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c index fe61d4d..3056f68 100644 --- a/src/backend/catalog/pg_enum.c +++ b/src/backend/catalog/pg_enum.c @@ -28,6 +28,8 @@ #include "utils/builtins.h" #include "utils/catcache.h" #include "utils/fmgroids.h" +#include "utils/hsearch.h" +#include "utils/memutils.h" #include "utils/syscache.h" #include "utils/tqual.h" @@ -38,6 +40,8 @@ Oid binary_upgrade_next_pg_enum_oid = InvalidOid; static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems); static int sort_order_cmp(const void *p1, const void *p2); +/* hash table of values added in the current transaction by AddEnumLabel */ +static HTAB *enum_blacklist = NULL; /* * EnumValuesCreate @@ -460,8 +464,49 @@ restart: heap_freetuple(enum_tup); heap_close(pg_enum, RowExclusiveLock); + + /* Set up the blacklist hash if required */ + if (enum_blacklist == NULL) + { + HASHCTL hash_ctl; + memset(&hash_ctl, 0, sizeof(hash_ctl)); + hash_ctl.keysize = sizeof(Oid); + hash_ctl.entrysize = sizeof(Oid); + hash_ctl.hcxt = TopTransactionContext; + enum_blacklist = hash_create("Enum blacklist for current transaction", + 32, + &hash_ctl, + HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); + + } + + /* Add the new value to the blacklist */ + (void) hash_search(enum_blacklist, &newOid, HASH_ENTER, NULL); } +/* Test if the enum is on the blacklist */ +bool +EnumBlacklisted(Oid enum_id) +{ + bool found; + + if (enum_blacklist == NULL) + return false; + + (void) hash_search(enum_blacklist, &enum_id, HASH_FIND, &found); + return found; +} + +/* + * Clean up the blacklist hash at the end of the transaction. The memory will + * have been deallocated, so all we need to do is set the pointer back to + * NULL for the next transaction. + */ +void +AtEOXact_Enum(void) +{ + enum_blacklist = NULL; +} /* * RenameEnumLabel diff --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c index 973397c..a7ba3d0 100644 --- a/src/backend/utils/adt/enum.c +++ b/src/backend/utils/adt/enum.c @@ -76,6 +76,10 @@ check_safe_enum_use(HeapTuple enumval_tup) TransactionIdDidCommit(xmin)) return; + /* Check if the enum value is blacklisted. If not, it's safe */ + if (! EnumBlacklisted(HeapTupleGetOid(enumval_tup))) + return; + /* It is a new enum value, so check to see if the whole enum is new */ en = (Form_pg_enum) GETSTRUCT(enumval_tup); enumtyp_tup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(en->enumtypid)); diff --git a/src/include/catalog/pg_enum.h b/src/include/catalog/pg_enum.h index 5938ba5..dff3d2f 100644 --- a/src/include/catalog/pg_enum.h +++ b/src/include/catalog/pg_enum.h @@ -69,5 +69,7 @@ extern void AddEnumLabel(Oid enumTypeOid, const char *newVal, bool skipIfExists); extern void RenameEnumLabel(Oid enumTypeOid, const char *oldVal, const char *newVal); +extern bool EnumBlacklisted(Oid enum_id); +extern void AtEOXact_Enum(void); #endif /* PG_ENUM_H */ diff --git a/src/test/regress/expected/enum.out b/src/test/regress/expected/enum.out index 0e60304..96345e4 100644 --- a/src/test/regress/expected/enum.out +++ b/src/test/regress/expected/enum.out @@ -648,6 +648,29 @@ SELECT enum_range(null::bogus); (1 row) ROLLBACK; +-- check that we don't forbid anything except use of a new value on an +-- existing enum +BEGIN; +CREATE ROLE newowner; +CREATE TYPE bogus AS ENUM('good','bad','ugly'); +ALTER TYPE bogus OWNER TO newowner; +select enum_range(null::bogus); + enum_range +----------------- + {good,bad,ugly} +(1 row) + +ROLLBACK; +BEGIN; +CREATE ROLE newowner; +ALTER TYPE rainbow OWNER TO newowner; +select enum_range(null::rainbow); + enum_range +------------------------------------------- + {crimson,orange,yellow,green,blue,purple} +(1 row) + +ROLLBACK; -- -- Cleanup -- diff --git a/src/test/regress/sql/enum.sql b/src/test/regress/sql/enum.sql index d7e8714..2129f64 100644 --- a/src/test/regress/sql/enum.sql +++ b/src/test/regress/sql/enum.sql @@ -311,6 +311,22 @@ ALTER TYPE bogus ADD VALUE 'ugly'; SELECT enum_range(null::bogus); ROLLBACK; +-- check that we don't forbid anything except use of a new value on an +-- existing enum + +BEGIN; +CREATE ROLE newowner; +CREATE TYPE bogus AS ENUM('good','bad','ugly'); +ALTER TYPE bogus OWNER TO newowner; +select enum_range(null::bogus); +ROLLBACK; + +BEGIN; +CREATE ROLE newowner; +ALTER TYPE rainbow OWNER TO newowner; +select enum_range(null::rainbow); +ROLLBACK; + -- -- Cleanup --