3636Oid binary_upgrade_next_pg_enum_oid = InvalidOid ;
3737
3838/*
39- * Hash table of enum value OIDs created during the current transaction by
40- * AddEnumLabel. We disallow using these values until the transaction is
39+ * We keep two transaction-lifespan hash tables, one containing the OIDs
40+ * of enum types made in the current transaction, and one containing the
41+ * OIDs of enum values created during the current transaction by
42+ * AddEnumLabel (but only if their enum type is not in the first hash).
43+ *
44+ * We disallow using enum values in the second hash until the transaction is
4145 * committed; otherwise, they might get into indexes where we can't clean
4246 * them up, and then if the transaction rolls back we have a broken index.
4347 * (See comments for check_safe_enum_use() in enum.c.) Values created by
4448 * EnumValuesCreate are *not* entered into the table; we assume those are
4549 * created during CREATE TYPE, so they can't go away unless the enum type
4650 * itself does.
51+ *
52+ * The motivation for treating enum values as safe if their type OID is
53+ * in the first hash is to allow CREATE TYPE AS ENUM; ALTER TYPE ADD VALUE;
54+ * followed by a use of the value in the same transaction. This pattern
55+ * is really just as safe as creating the value during CREATE TYPE.
56+ * We need to support this because pg_dump in binary upgrade mode produces
57+ * commands like that. But currently we only support it when the commands
58+ * are at the outermost transaction level, which is as much as we need for
59+ * pg_dump. We could track subtransaction nesting of the commands to
60+ * analyze things more precisely, but for now we don't bother.
4761 */
48- static HTAB * uncommitted_enums = NULL ;
62+ static HTAB * uncommitted_enum_types = NULL ;
63+ static HTAB * uncommitted_enum_values = NULL ;
4964
65+ static void init_uncommitted_enum_types (void );
66+ static void init_uncommitted_enum_values (void );
67+ static bool EnumTypeUncommitted (Oid typ_id );
5068static void RenumberEnumType (Relation pg_enum , HeapTuple * existing , int nelems );
5169static int sort_order_cmp (const void * p1 , const void * p2 );
5270
@@ -56,6 +74,11 @@ static int sort_order_cmp(const void *p1, const void *p2);
5674 * Create an entry in pg_enum for each of the supplied enum values.
5775 *
5876 * vals is a list of String values.
77+ *
78+ * We assume that this is called only by CREATE TYPE AS ENUM, and that it
79+ * will be called even if the vals list is empty. So we can enter the
80+ * enum type's OID into uncommitted_enum_types here, rather than needing
81+ * another entry point to do it.
5982 */
6083void
6184EnumValuesCreate (Oid enumTypeOid , List * vals )
@@ -70,6 +93,21 @@ EnumValuesCreate(Oid enumTypeOid, List *vals)
7093 CatalogIndexState indstate ;
7194 TupleTableSlot * * slot ;
7295
96+ /*
97+ * Remember the type OID as being made in the current transaction, but not
98+ * if we're in a subtransaction. (We could remember the OID anyway, in
99+ * case a subsequent ALTER ADD VALUE occurs at outer level. But that
100+ * usage pattern seems unlikely enough that we'd probably just be wasting
101+ * hashtable maintenance effort.)
102+ */
103+ if (GetCurrentTransactionNestLevel () == 1 )
104+ {
105+ if (uncommitted_enum_types == NULL )
106+ init_uncommitted_enum_types ();
107+ (void ) hash_search (uncommitted_enum_types , & enumTypeOid ,
108+ HASH_ENTER , NULL );
109+ }
110+
73111 num_elems = list_length (vals );
74112
75113 /*
@@ -211,20 +249,37 @@ EnumValuesDelete(Oid enumTypeOid)
211249}
212250
213251/*
214- * Initialize the uncommitted enum table for this transaction.
252+ * Initialize the uncommitted enum types table for this transaction.
253+ */
254+ static void
255+ init_uncommitted_enum_types (void )
256+ {
257+ HASHCTL hash_ctl ;
258+
259+ hash_ctl .keysize = sizeof (Oid );
260+ hash_ctl .entrysize = sizeof (Oid );
261+ hash_ctl .hcxt = TopTransactionContext ;
262+ uncommitted_enum_types = hash_create ("Uncommitted enum types" ,
263+ 32 ,
264+ & hash_ctl ,
265+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
266+ }
267+
268+ /*
269+ * Initialize the uncommitted enum values table for this transaction.
215270 */
216271static void
217- init_uncommitted_enums (void )
272+ init_uncommitted_enum_values (void )
218273{
219274 HASHCTL hash_ctl ;
220275
221276 hash_ctl .keysize = sizeof (Oid );
222277 hash_ctl .entrysize = sizeof (Oid );
223278 hash_ctl .hcxt = TopTransactionContext ;
224- uncommitted_enums = hash_create ("Uncommitted enums " ,
225- 32 ,
226- & hash_ctl ,
227- HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
279+ uncommitted_enum_values = hash_create ("Uncommitted enum values " ,
280+ 32 ,
281+ & hash_ctl ,
282+ HASH_ELEM | HASH_BLOBS | HASH_CONTEXT );
228283}
229284
230285/*
@@ -520,12 +575,27 @@ AddEnumLabel(Oid enumTypeOid,
520575
521576 table_close (pg_enum , RowExclusiveLock );
522577
523- /* Set up the uncommitted enum table if not already done in this tx */
524- if (uncommitted_enums == NULL )
525- init_uncommitted_enums ();
578+ /*
579+ * If the enum type itself is uncommitted, we need not enter the new enum
580+ * value into uncommitted_enum_values, because the type won't survive if
581+ * the value doesn't. (This is basically the same reasoning as for values
582+ * made directly by CREATE TYPE AS ENUM.) However, apply this rule only
583+ * when we are not inside a subtransaction; if we're more deeply nested
584+ * than the CREATE TYPE then the conclusion doesn't hold. We could expend
585+ * more effort to track the subtransaction level of CREATE TYPE, but for
586+ * now we're only concerned about making the world safe for pg_dump in
587+ * binary upgrade mode, and that won't use subtransactions.
588+ */
589+ if (GetCurrentTransactionNestLevel () == 1 &&
590+ EnumTypeUncommitted (enumTypeOid ))
591+ return ;
592+
593+ /* Set up the uncommitted values table if not already done in this tx */
594+ if (uncommitted_enum_values == NULL )
595+ init_uncommitted_enum_values ();
526596
527597 /* Add the new value to the table */
528- (void ) hash_search (uncommitted_enums , & newOid , HASH_ENTER , NULL );
598+ (void ) hash_search (uncommitted_enum_values , & newOid , HASH_ENTER , NULL );
529599}
530600
531601
@@ -614,19 +684,37 @@ RenameEnumLabel(Oid enumTypeOid,
614684
615685
616686/*
617- * Test if the given enum value is in the table of uncommitted enums.
687+ * Test if the given type OID is in the table of uncommitted enum types.
688+ */
689+ static bool
690+ EnumTypeUncommitted (Oid typ_id )
691+ {
692+ bool found ;
693+
694+ /* If we've made no uncommitted types table, it's not in the table */
695+ if (uncommitted_enum_types == NULL )
696+ return false;
697+
698+ /* Else, is it in the table? */
699+ (void ) hash_search (uncommitted_enum_types , & typ_id , HASH_FIND , & found );
700+ return found ;
701+ }
702+
703+
704+ /*
705+ * Test if the given enum value is in the table of uncommitted enum values.
618706 */
619707bool
620708EnumUncommitted (Oid enum_id )
621709{
622710 bool found ;
623711
624- /* If we've made no uncommitted table, all values are safe */
625- if (uncommitted_enums == NULL )
712+ /* If we've made no uncommitted values table, it's not in the table */
713+ if (uncommitted_enum_values == NULL )
626714 return false;
627715
628716 /* Else, is it in the table? */
629- (void ) hash_search (uncommitted_enums , & enum_id , HASH_FIND , & found );
717+ (void ) hash_search (uncommitted_enum_values , & enum_id , HASH_FIND , & found );
630718 return found ;
631719}
632720
@@ -638,11 +726,12 @@ void
638726AtEOXact_Enum (void )
639727{
640728 /*
641- * Reset the uncommitted table , as all our enum values are now committed.
642- * The memory will go away automatically when TopTransactionContext is
643- * freed; it's sufficient to clear our pointer .
729+ * Reset the uncommitted tables , as all our tuples are now committed. The
730+ * memory will go away automatically when TopTransactionContext is freed;
731+ * it's sufficient to clear our pointers .
644732 */
645- uncommitted_enums = NULL ;
733+ uncommitted_enum_types = NULL ;
734+ uncommitted_enum_values = NULL ;
646735}
647736
648737
@@ -723,15 +812,15 @@ sort_order_cmp(const void *p1, const void *p2)
723812Size
724813EstimateUncommittedEnumsSpace (void )
725814{
726- size_t entries ;
815+ size_t entries = 0 ;
727816
728- if (uncommitted_enums )
729- entries = hash_get_num_entries (uncommitted_enums );
730- else
731- entries = 0 ;
817+ if (uncommitted_enum_types )
818+ entries + = hash_get_num_entries (uncommitted_enum_types );
819+ if ( uncommitted_enum_values )
820+ entries += hash_get_num_entries ( uncommitted_enum_values ) ;
732821
733- /* Add one for the terminator . */
734- return sizeof (Oid ) * (entries + 1 );
822+ /* Add two for the terminators . */
823+ return sizeof (Oid ) * (entries + 2 );
735824}
736825
737826void
@@ -740,51 +829,78 @@ SerializeUncommittedEnums(void *space, Size size)
740829 Oid * serialized = (Oid * ) space ;
741830
742831 /*
743- * Make sure the hash table hasn 't changed in size since the caller
832+ * Make sure the hash tables haven 't changed in size since the caller
744833 * reserved the space.
745834 */
746835 Assert (size == EstimateUncommittedEnumsSpace ());
747836
748- /* Write out all the values from the hash table, if there is one. */
749- if (uncommitted_enums )
837+ /* Write out all the OIDs from the types hash table, if there is one. */
838+ if (uncommitted_enum_types )
750839 {
751840 HASH_SEQ_STATUS status ;
752841 Oid * value ;
753842
754- hash_seq_init (& status , uncommitted_enums );
843+ hash_seq_init (& status , uncommitted_enum_types );
755844 while ((value = (Oid * ) hash_seq_search (& status )))
756845 * serialized ++ = * value ;
757846 }
758847
759848 /* Write out the terminator. */
760- * serialized = InvalidOid ;
849+ * serialized ++ = InvalidOid ;
850+
851+ /* Write out all the OIDs from the values hash table, if there is one. */
852+ if (uncommitted_enum_values )
853+ {
854+ HASH_SEQ_STATUS status ;
855+ Oid * value ;
856+
857+ hash_seq_init (& status , uncommitted_enum_values );
858+ while ((value = (Oid * ) hash_seq_search (& status )))
859+ * serialized ++ = * value ;
860+ }
861+
862+ /* Write out the terminator. */
863+ * serialized ++ = InvalidOid ;
761864
762865 /*
763866 * Make sure the amount of space we actually used matches what was
764867 * estimated.
765868 */
766- Assert ((char * ) ( serialized + 1 ) == ((char * ) space ) + size );
869+ Assert ((char * ) serialized == ((char * ) space ) + size );
767870}
768871
769872void
770873RestoreUncommittedEnums (void * space )
771874{
772875 Oid * serialized = (Oid * ) space ;
773876
774- Assert (!uncommitted_enums );
877+ Assert (!uncommitted_enum_types );
878+ Assert (!uncommitted_enum_values );
775879
776880 /*
777- * As a special case, if the list is empty then don't even bother to
778- * create the hash table. This is the usual case, since enum alteration
779- * is expected to be rare .
881+ * If either list is empty then don't even bother to create that hash
882+ * table. This is the common case, since most transactions don't create
883+ * or alter enums .
780884 */
781- if (!OidIsValid (* serialized ))
782- return ;
783-
784- /* Read all the values into a new hash table. */
785- init_uncommitted_enums ();
786- do
885+ if (OidIsValid (* serialized ))
787886 {
788- hash_search (uncommitted_enums , serialized ++ , HASH_ENTER , NULL );
789- } while (OidIsValid (* serialized ));
887+ /* Read all the types into a new hash table. */
888+ init_uncommitted_enum_types ();
889+ do
890+ {
891+ (void ) hash_search (uncommitted_enum_types , serialized ++ ,
892+ HASH_ENTER , NULL );
893+ } while (OidIsValid (* serialized ));
894+ }
895+ serialized ++ ;
896+ if (OidIsValid (* serialized ))
897+ {
898+ /* Read all the values into a new hash table. */
899+ init_uncommitted_enum_values ();
900+ do
901+ {
902+ (void ) hash_search (uncommitted_enum_values , serialized ++ ,
903+ HASH_ENTER , NULL );
904+ } while (OidIsValid (* serialized ));
905+ }
790906}
0 commit comments