36
36
Oid binary_upgrade_next_pg_enum_oid = InvalidOid ;
37
37
38
38
/*
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
41
45
* committed; otherwise, they might get into indexes where we can't clean
42
46
* them up, and then if the transaction rolls back we have a broken index.
43
47
* (See comments for check_safe_enum_use() in enum.c.) Values created by
44
48
* EnumValuesCreate are *not* entered into the table; we assume those are
45
49
* created during CREATE TYPE, so they can't go away unless the enum type
46
50
* 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.
47
61
*/
48
- static HTAB * uncommitted_enums = NULL ;
62
+ static HTAB * uncommitted_enum_types = NULL ;
63
+ static HTAB * uncommitted_enum_values = NULL ;
49
64
65
+ static void init_uncommitted_enum_types (void );
66
+ static void init_uncommitted_enum_values (void );
67
+ static bool EnumTypeUncommitted (Oid typ_id );
50
68
static void RenumberEnumType (Relation pg_enum , HeapTuple * existing , int nelems );
51
69
static int sort_order_cmp (const void * p1 , const void * p2 );
52
70
@@ -56,6 +74,11 @@ static int sort_order_cmp(const void *p1, const void *p2);
56
74
* Create an entry in pg_enum for each of the supplied enum values.
57
75
*
58
76
* 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.
59
82
*/
60
83
void
61
84
EnumValuesCreate (Oid enumTypeOid , List * vals )
@@ -70,6 +93,21 @@ EnumValuesCreate(Oid enumTypeOid, List *vals)
70
93
CatalogIndexState indstate ;
71
94
TupleTableSlot * * slot ;
72
95
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
+
73
111
num_elems = list_length (vals );
74
112
75
113
/*
@@ -211,20 +249,37 @@ EnumValuesDelete(Oid enumTypeOid)
211
249
}
212
250
213
251
/*
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.
215
270
*/
216
271
static void
217
- init_uncommitted_enums (void )
272
+ init_uncommitted_enum_values (void )
218
273
{
219
274
HASHCTL hash_ctl ;
220
275
221
276
hash_ctl .keysize = sizeof (Oid );
222
277
hash_ctl .entrysize = sizeof (Oid );
223
278
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 );
228
283
}
229
284
230
285
/*
@@ -520,12 +575,27 @@ AddEnumLabel(Oid enumTypeOid,
520
575
521
576
table_close (pg_enum , RowExclusiveLock );
522
577
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 ();
526
596
527
597
/* 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 );
529
599
}
530
600
531
601
@@ -614,19 +684,37 @@ RenameEnumLabel(Oid enumTypeOid,
614
684
615
685
616
686
/*
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.
618
706
*/
619
707
bool
620
708
EnumUncommitted (Oid enum_id )
621
709
{
622
710
bool found ;
623
711
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 )
626
714
return false;
627
715
628
716
/* 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 );
630
718
return found ;
631
719
}
632
720
@@ -638,11 +726,12 @@ void
638
726
AtEOXact_Enum (void )
639
727
{
640
728
/*
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 .
644
732
*/
645
- uncommitted_enums = NULL ;
733
+ uncommitted_enum_types = NULL ;
734
+ uncommitted_enum_values = NULL ;
646
735
}
647
736
648
737
@@ -723,15 +812,15 @@ sort_order_cmp(const void *p1, const void *p2)
723
812
Size
724
813
EstimateUncommittedEnumsSpace (void )
725
814
{
726
- size_t entries ;
815
+ size_t entries = 0 ;
727
816
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 ) ;
732
821
733
- /* Add one for the terminator . */
734
- return sizeof (Oid ) * (entries + 1 );
822
+ /* Add two for the terminators . */
823
+ return sizeof (Oid ) * (entries + 2 );
735
824
}
736
825
737
826
void
@@ -740,51 +829,78 @@ SerializeUncommittedEnums(void *space, Size size)
740
829
Oid * serialized = (Oid * ) space ;
741
830
742
831
/*
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
744
833
* reserved the space.
745
834
*/
746
835
Assert (size == EstimateUncommittedEnumsSpace ());
747
836
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 )
750
839
{
751
840
HASH_SEQ_STATUS status ;
752
841
Oid * value ;
753
842
754
- hash_seq_init (& status , uncommitted_enums );
843
+ hash_seq_init (& status , uncommitted_enum_types );
755
844
while ((value = (Oid * ) hash_seq_search (& status )))
756
845
* serialized ++ = * value ;
757
846
}
758
847
759
848
/* 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 ;
761
864
762
865
/*
763
866
* Make sure the amount of space we actually used matches what was
764
867
* estimated.
765
868
*/
766
- Assert ((char * ) ( serialized + 1 ) == ((char * ) space ) + size );
869
+ Assert ((char * ) serialized == ((char * ) space ) + size );
767
870
}
768
871
769
872
void
770
873
RestoreUncommittedEnums (void * space )
771
874
{
772
875
Oid * serialized = (Oid * ) space ;
773
876
774
- Assert (!uncommitted_enums );
877
+ Assert (!uncommitted_enum_types );
878
+ Assert (!uncommitted_enum_values );
775
879
776
880
/*
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 .
780
884
*/
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 ))
787
886
{
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
+ }
790
906
}
0 commit comments