|
28 | 28 | #include "utils/builtins.h"
|
29 | 29 | #include "utils/catcache.h"
|
30 | 30 | #include "utils/fmgroids.h"
|
| 31 | +#include "utils/hsearch.h" |
| 32 | +#include "utils/memutils.h" |
31 | 33 | #include "utils/syscache.h"
|
32 | 34 | #include "utils/tqual.h"
|
33 | 35 |
|
34 | 36 |
|
35 | 37 | /* Potentially set by pg_upgrade_support functions */
|
36 | 38 | Oid binary_upgrade_next_pg_enum_oid = InvalidOid;
|
37 | 39 |
|
| 40 | +/* |
| 41 | + * Hash table of enum value OIDs created during the current transaction by |
| 42 | + * AddEnumLabel. We disallow using these values until the transaction is |
| 43 | + * committed; otherwise, they might get into indexes where we can't clean |
| 44 | + * them up, and then if the transaction rolls back we have a broken index. |
| 45 | + * (See comments for check_safe_enum_use() in enum.c.) Values created by |
| 46 | + * EnumValuesCreate are *not* blacklisted; we assume those are created during |
| 47 | + * CREATE TYPE, so they can't go away unless the enum type itself does. |
| 48 | + */ |
| 49 | +static HTAB *enum_blacklist = NULL; |
| 50 | + |
38 | 51 | static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems);
|
39 | 52 | static int sort_order_cmp(const void *p1, const void *p2);
|
40 | 53 |
|
@@ -168,6 +181,23 @@ EnumValuesDelete(Oid enumTypeOid)
|
168 | 181 | heap_close(pg_enum, RowExclusiveLock);
|
169 | 182 | }
|
170 | 183 |
|
| 184 | +/* |
| 185 | + * Initialize the enum blacklist for this transaction. |
| 186 | + */ |
| 187 | +static void |
| 188 | +init_enum_blacklist(void) |
| 189 | +{ |
| 190 | + HASHCTL hash_ctl; |
| 191 | + |
| 192 | + memset(&hash_ctl, 0, sizeof(hash_ctl)); |
| 193 | + hash_ctl.keysize = sizeof(Oid); |
| 194 | + hash_ctl.entrysize = sizeof(Oid); |
| 195 | + hash_ctl.hcxt = TopTransactionContext; |
| 196 | + enum_blacklist = hash_create("Enum value blacklist", |
| 197 | + 32, |
| 198 | + &hash_ctl, |
| 199 | + HASH_ELEM | HASH_BLOBS | HASH_CONTEXT); |
| 200 | +} |
171 | 201 |
|
172 | 202 | /*
|
173 | 203 | * AddEnumLabel
|
@@ -460,6 +490,13 @@ AddEnumLabel(Oid enumTypeOid,
|
460 | 490 | heap_freetuple(enum_tup);
|
461 | 491 |
|
462 | 492 | heap_close(pg_enum, RowExclusiveLock);
|
| 493 | + |
| 494 | + /* Set up the blacklist hash if not already done in this transaction */ |
| 495 | + if (enum_blacklist == NULL) |
| 496 | + init_enum_blacklist(); |
| 497 | + |
| 498 | + /* Add the new value to the blacklist */ |
| 499 | + (void) hash_search(enum_blacklist, &newOid, HASH_ENTER, NULL); |
463 | 500 | }
|
464 | 501 |
|
465 | 502 |
|
@@ -547,6 +584,39 @@ RenameEnumLabel(Oid enumTypeOid,
|
547 | 584 | }
|
548 | 585 |
|
549 | 586 |
|
| 587 | +/* |
| 588 | + * Test if the given enum value is on the blacklist |
| 589 | + */ |
| 590 | +bool |
| 591 | +EnumBlacklisted(Oid enum_id) |
| 592 | +{ |
| 593 | + bool found; |
| 594 | + |
| 595 | + /* If we've made no blacklist table, all values are safe */ |
| 596 | + if (enum_blacklist == NULL) |
| 597 | + return false; |
| 598 | + |
| 599 | + /* Else, is it in the table? */ |
| 600 | + (void) hash_search(enum_blacklist, &enum_id, HASH_FIND, &found); |
| 601 | + return found; |
| 602 | +} |
| 603 | + |
| 604 | + |
| 605 | +/* |
| 606 | + * Clean up enum stuff after end of top-level transaction. |
| 607 | + */ |
| 608 | +void |
| 609 | +AtEOXact_Enum(void) |
| 610 | +{ |
| 611 | + /* |
| 612 | + * Reset the blacklist table, as all our enum values are now committed. |
| 613 | + * The memory will go away automatically when TopTransactionContext is |
| 614 | + * freed; it's sufficient to clear our pointer. |
| 615 | + */ |
| 616 | + enum_blacklist = NULL; |
| 617 | +} |
| 618 | + |
| 619 | + |
550 | 620 | /*
|
551 | 621 | * RenumberEnumType
|
552 | 622 | * Renumber existing enum elements to have sort positions 1..n.
|
@@ -620,3 +690,72 @@ sort_order_cmp(const void *p1, const void *p2)
|
620 | 690 | else
|
621 | 691 | return 0;
|
622 | 692 | }
|
| 693 | + |
| 694 | +Size |
| 695 | +EstimateEnumBlacklistSpace(void) |
| 696 | +{ |
| 697 | + size_t entries; |
| 698 | + |
| 699 | + if (enum_blacklist) |
| 700 | + entries = hash_get_num_entries(enum_blacklist); |
| 701 | + else |
| 702 | + entries = 0; |
| 703 | + |
| 704 | + /* Add one for the terminator. */ |
| 705 | + return sizeof(Oid) * (entries + 1); |
| 706 | +} |
| 707 | + |
| 708 | +void |
| 709 | +SerializeEnumBlacklist(void *space, Size size) |
| 710 | +{ |
| 711 | + Oid *serialized = (Oid *) space; |
| 712 | + |
| 713 | + /* |
| 714 | + * Make sure the hash table hasn't changed in size since the caller |
| 715 | + * reserved the space. |
| 716 | + */ |
| 717 | + Assert(size == EstimateEnumBlacklistSpace()); |
| 718 | + |
| 719 | + /* Write out all the values from the hash table, if there is one. */ |
| 720 | + if (enum_blacklist) |
| 721 | + { |
| 722 | + HASH_SEQ_STATUS status; |
| 723 | + Oid *value; |
| 724 | + |
| 725 | + hash_seq_init(&status, enum_blacklist); |
| 726 | + while ((value = (Oid *) hash_seq_search(&status))) |
| 727 | + *serialized++ = *value; |
| 728 | + } |
| 729 | + |
| 730 | + /* Write out the terminator. */ |
| 731 | + *serialized = InvalidOid; |
| 732 | + |
| 733 | + /* |
| 734 | + * Make sure the amount of space we actually used matches what was |
| 735 | + * estimated. |
| 736 | + */ |
| 737 | + Assert((char *) (serialized + 1) == ((char *) space) + size); |
| 738 | +} |
| 739 | + |
| 740 | +void |
| 741 | +RestoreEnumBlacklist(void *space) |
| 742 | +{ |
| 743 | + Oid *serialized = (Oid *) space; |
| 744 | + |
| 745 | + Assert(!enum_blacklist); |
| 746 | + |
| 747 | + /* |
| 748 | + * As a special case, if the list is empty then don't even bother to |
| 749 | + * create the hash table. This is the usual case, since enum alteration |
| 750 | + * is expected to be rare. |
| 751 | + */ |
| 752 | + if (!OidIsValid(*serialized)) |
| 753 | + return; |
| 754 | + |
| 755 | + /* Read all the values into a new hash table. */ |
| 756 | + init_enum_blacklist(); |
| 757 | + do |
| 758 | + { |
| 759 | + hash_search(enum_blacklist, serialized++, HASH_ENTER, NULL); |
| 760 | + } while (OidIsValid(*serialized)); |
| 761 | +} |
0 commit comments