1 /*-------------------------------------------------------------------------
4 * routines to support manipulation of the pg_enum relation
6 * Copyright (c) 2006-2024, PostgreSQL Global Development Group
10 * src/backend/catalog/pg_enum.c
12 *-------------------------------------------------------------------------
16 #include "access/genam.h"
17 #include "access/htup_details.h"
18 #include "access/table.h"
19 #include "access/xact.h"
20 #include "catalog/binary_upgrade.h"
21 #include "catalog/catalog.h"
22 #include "catalog/indexing.h"
23 #include "catalog/pg_enum.h"
24 #include "catalog/pg_type.h"
25 #include "miscadmin.h"
26 #include "nodes/value.h"
27 #include "storage/lmgr.h"
28 #include "utils/builtins.h"
29 #include "utils/catcache.h"
30 #include "utils/fmgroids.h"
31 #include "utils/hsearch.h"
32 #include "utils/memutils.h"
33 #include "utils/syscache.h"
35 /* Potentially set by pg_upgrade_support functions */
36 Oid binary_upgrade_next_pg_enum_oid
= InvalidOid
;
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).
44 * We disallow using enum values in the second hash until the transaction is
45 * committed; otherwise, they might get into indexes where we can't clean
46 * them up, and then if the transaction rolls back we have a broken index.
47 * (See comments for check_safe_enum_use() in enum.c.) Values created by
48 * EnumValuesCreate are *not* entered into the table; we assume those are
49 * created during CREATE TYPE, so they can't go away unless the enum type
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.
62 static HTAB
*uncommitted_enum_types
= NULL
;
63 static HTAB
*uncommitted_enum_values
= NULL
;
65 static void init_uncommitted_enum_types(void);
66 static void init_uncommitted_enum_values(void);
67 static bool EnumTypeUncommitted(Oid typ_id
);
68 static void RenumberEnumType(Relation pg_enum
, HeapTuple
*existing
, int nelems
);
69 static int sort_order_cmp(const void *p1
, const void *p2
);
74 * Create an entry in pg_enum for each of the supplied enum values.
76 * vals is a list of String values.
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.
84 EnumValuesCreate(Oid enumTypeOid
, List
*vals
)
93 CatalogIndexState indstate
;
94 TupleTableSlot
**slot
;
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.)
103 if (GetCurrentTransactionNestLevel() == 1)
105 if (uncommitted_enum_types
== NULL
)
106 init_uncommitted_enum_types();
107 (void) hash_search(uncommitted_enum_types
, &enumTypeOid
,
111 num_elems
= list_length(vals
);
114 * We do not bother to check the list of values for duplicates --- if you
115 * have any, you'll get a less-than-friendly unique-index violation. It is
116 * probably not worth trying harder.
119 pg_enum
= table_open(EnumRelationId
, RowExclusiveLock
);
122 * Allocate OIDs for the enum's members.
124 * While this method does not absolutely guarantee that we generate no
125 * duplicate OIDs (since we haven't entered each oid into the table before
126 * allocating the next), trouble could only occur if the OID counter wraps
127 * all the way around before we finish. Which seems unlikely.
129 oids
= (Oid
*) palloc(num_elems
* sizeof(Oid
));
131 for (elemno
= 0; elemno
< num_elems
; elemno
++)
134 * We assign even-numbered OIDs to all the new enum labels. This
135 * tells the comparison functions the OIDs are in the correct sort
136 * order and can be compared directly.
142 new_oid
= GetNewOidWithIndex(pg_enum
, EnumOidIndexId
,
144 } while (new_oid
& 1);
145 oids
[elemno
] = new_oid
;
148 /* sort them, just in case OID counter wrapped from high to low */
149 qsort(oids
, num_elems
, sizeof(Oid
), oid_cmp
);
151 /* and make the entries */
152 indstate
= CatalogOpenIndexes(pg_enum
);
154 /* allocate the slots to use and initialize them */
155 nslots
= Min(num_elems
,
156 MAX_CATALOG_MULTI_INSERT_BYTES
/ sizeof(FormData_pg_enum
));
157 slot
= palloc(sizeof(TupleTableSlot
*) * nslots
);
158 for (int i
= 0; i
< nslots
; i
++)
159 slot
[i
] = MakeSingleTupleTableSlot(RelationGetDescr(pg_enum
),
165 char *lab
= strVal(lfirst(lc
));
166 Name enumlabel
= palloc0(NAMEDATALEN
);
169 * labels are stored in a name field, for easier syscache lookup, so
170 * check the length to make sure it's within range.
172 if (strlen(lab
) > (NAMEDATALEN
- 1))
174 (errcode(ERRCODE_INVALID_NAME
),
175 errmsg("invalid enum label \"%s\"", lab
),
176 errdetail("Labels must be %d bytes or less.",
179 ExecClearTuple(slot
[slotCount
]);
181 memset(slot
[slotCount
]->tts_isnull
, false,
182 slot
[slotCount
]->tts_tupleDescriptor
->natts
* sizeof(bool));
184 slot
[slotCount
]->tts_values
[Anum_pg_enum_oid
- 1] = ObjectIdGetDatum(oids
[elemno
]);
185 slot
[slotCount
]->tts_values
[Anum_pg_enum_enumtypid
- 1] = ObjectIdGetDatum(enumTypeOid
);
186 slot
[slotCount
]->tts_values
[Anum_pg_enum_enumsortorder
- 1] = Float4GetDatum(elemno
+ 1);
188 namestrcpy(enumlabel
, lab
);
189 slot
[slotCount
]->tts_values
[Anum_pg_enum_enumlabel
- 1] = NameGetDatum(enumlabel
);
191 ExecStoreVirtualTuple(slot
[slotCount
]);
194 /* if slots are full, insert a batch of tuples */
195 if (slotCount
== nslots
)
197 CatalogTuplesMultiInsertWithInfo(pg_enum
, slot
, slotCount
,
205 /* Insert any tuples left in the buffer */
207 CatalogTuplesMultiInsertWithInfo(pg_enum
, slot
, slotCount
,
212 for (int i
= 0; i
< nslots
; i
++)
213 ExecDropSingleTupleTableSlot(slot
[i
]);
214 CatalogCloseIndexes(indstate
);
215 table_close(pg_enum
, RowExclusiveLock
);
221 * Remove all the pg_enum entries for the specified enum type.
224 EnumValuesDelete(Oid enumTypeOid
)
231 pg_enum
= table_open(EnumRelationId
, RowExclusiveLock
);
234 Anum_pg_enum_enumtypid
,
235 BTEqualStrategyNumber
, F_OIDEQ
,
236 ObjectIdGetDatum(enumTypeOid
));
238 scan
= systable_beginscan(pg_enum
, EnumTypIdLabelIndexId
, true,
241 while (HeapTupleIsValid(tup
= systable_getnext(scan
)))
243 CatalogTupleDelete(pg_enum
, &tup
->t_self
);
246 systable_endscan(scan
);
248 table_close(pg_enum
, RowExclusiveLock
);
252 * Initialize the uncommitted enum types table for this transaction.
255 init_uncommitted_enum_types(void)
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",
265 HASH_ELEM
| HASH_BLOBS
| HASH_CONTEXT
);
269 * Initialize the uncommitted enum values table for this transaction.
272 init_uncommitted_enum_values(void)
276 hash_ctl
.keysize
= sizeof(Oid
);
277 hash_ctl
.entrysize
= sizeof(Oid
);
278 hash_ctl
.hcxt
= TopTransactionContext
;
279 uncommitted_enum_values
= hash_create("Uncommitted enum values",
282 HASH_ELEM
| HASH_BLOBS
| HASH_CONTEXT
);
287 * Add a new label to the enum set. By default it goes at
288 * the end, but the user can choose to place it before or
289 * after any existing set member.
292 AddEnumLabel(Oid enumTypeOid
,
294 const char *neighbor
,
300 Datum values
[Natts_pg_enum
];
301 bool nulls
[Natts_pg_enum
];
310 /* check length of new label is ok */
311 if (strlen(newVal
) > (NAMEDATALEN
- 1))
313 (errcode(ERRCODE_INVALID_NAME
),
314 errmsg("invalid enum label \"%s\"", newVal
),
315 errdetail("Labels must be %d bytes or less.",
319 * Acquire a lock on the enum type, which we won't release until commit.
320 * This ensures that two backends aren't concurrently modifying the same
321 * enum type. Without that, we couldn't be sure to get a consistent view
322 * of the enum members via the syscache. Note that this does not block
323 * other backends from inspecting the type; see comments for
326 LockDatabaseObject(TypeRelationId
, enumTypeOid
, 0, ExclusiveLock
);
329 * Check if label is already in use. The unique index on pg_enum would
330 * catch this anyway, but we prefer a friendlier error message, and
331 * besides we need a check to support IF NOT EXISTS.
333 enum_tup
= SearchSysCache2(ENUMTYPOIDNAME
,
334 ObjectIdGetDatum(enumTypeOid
),
335 CStringGetDatum(newVal
));
336 if (HeapTupleIsValid(enum_tup
))
338 ReleaseSysCache(enum_tup
);
342 (errcode(ERRCODE_DUPLICATE_OBJECT
),
343 errmsg("enum label \"%s\" already exists, skipping",
349 (errcode(ERRCODE_DUPLICATE_OBJECT
),
350 errmsg("enum label \"%s\" already exists",
354 pg_enum
= table_open(EnumRelationId
, RowExclusiveLock
);
356 /* If we have to renumber the existing members, we restart from here */
359 /* Get the list of existing members of the enum */
360 list
= SearchSysCacheList1(ENUMTYPOIDNAME
,
361 ObjectIdGetDatum(enumTypeOid
));
362 nelems
= list
->n_members
;
364 /* Sort the existing members by enumsortorder */
365 existing
= (HeapTuple
*) palloc(nelems
* sizeof(HeapTuple
));
366 for (i
= 0; i
< nelems
; i
++)
367 existing
[i
] = &(list
->members
[i
]->tuple
);
369 qsort(existing
, nelems
, sizeof(HeapTuple
), sort_order_cmp
);
371 if (neighbor
== NULL
)
374 * Put the new label at the end of the list. No change to existing
375 * tuples is required.
379 Form_pg_enum en
= (Form_pg_enum
) GETSTRUCT(existing
[nelems
- 1]);
381 newelemorder
= en
->enumsortorder
+ 1;
388 /* BEFORE or AFTER was specified */
392 Form_pg_enum other_nbr_en
;
394 /* Locate the neighbor element */
395 for (nbr_index
= 0; nbr_index
< nelems
; nbr_index
++)
397 Form_pg_enum en
= (Form_pg_enum
) GETSTRUCT(existing
[nbr_index
]);
399 if (strcmp(NameStr(en
->enumlabel
), neighbor
) == 0)
402 if (nbr_index
>= nelems
)
404 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
405 errmsg("\"%s\" is not an existing enum label",
407 nbr_en
= (Form_pg_enum
) GETSTRUCT(existing
[nbr_index
]);
410 * Attempt to assign an appropriate enumsortorder value: one less than
411 * the smallest member, one more than the largest member, or halfway
412 * between two existing members.
414 * In the "halfway" case, because of the finite precision of float4,
415 * we might compute a value that's actually equal to one or the other
416 * of its neighbors. In that case we renumber the existing members
420 other_nbr_index
= nbr_index
+ 1;
422 other_nbr_index
= nbr_index
- 1;
424 if (other_nbr_index
< 0)
425 newelemorder
= nbr_en
->enumsortorder
- 1;
426 else if (other_nbr_index
>= nelems
)
427 newelemorder
= nbr_en
->enumsortorder
+ 1;
431 * The midpoint value computed here has to be rounded to float4
432 * precision, else our equality comparisons against the adjacent
433 * values are meaningless. The most portable way of forcing that
434 * to happen with non-C-standard-compliant compilers is to store
435 * it into a volatile variable.
437 volatile float4 midpoint
;
439 other_nbr_en
= (Form_pg_enum
) GETSTRUCT(existing
[other_nbr_index
]);
440 midpoint
= (nbr_en
->enumsortorder
+
441 other_nbr_en
->enumsortorder
) / 2;
443 if (midpoint
== nbr_en
->enumsortorder
||
444 midpoint
== other_nbr_en
->enumsortorder
)
446 RenumberEnumType(pg_enum
, existing
, nelems
);
447 /* Clean up and start over */
449 ReleaseCatCacheList(list
);
453 newelemorder
= midpoint
;
457 /* Get a new OID for the new label */
460 if (!OidIsValid(binary_upgrade_next_pg_enum_oid
))
462 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
463 errmsg("pg_enum OID value not set when in binary upgrade mode")));
466 * Use binary-upgrade override for pg_enum.oid, if supplied. During
467 * binary upgrade, all pg_enum.oid's are set this way so they are
468 * guaranteed to be consistent.
470 if (neighbor
!= NULL
)
472 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
473 errmsg("ALTER TYPE ADD BEFORE/AFTER is incompatible with binary upgrade")));
475 newOid
= binary_upgrade_next_pg_enum_oid
;
476 binary_upgrade_next_pg_enum_oid
= InvalidOid
;
481 * Normal case: we need to allocate a new Oid for the value.
483 * We want to give the new element an even-numbered Oid if it's safe,
484 * which is to say it compares correctly to all pre-existing even
485 * numbered Oids in the enum. Otherwise, we must give it an odd Oid.
491 /* Get a new OID (different from all existing pg_enum tuples) */
492 newOid
= GetNewOidWithIndex(pg_enum
, EnumOidIndexId
,
496 * Detect whether it sorts correctly relative to existing
497 * even-numbered labels of the enum. We can ignore existing
498 * labels with odd Oids, since a comparison involving one of those
499 * will not take the fast path anyway.
502 for (i
= 0; i
< nelems
; i
++)
504 HeapTuple exists_tup
= existing
[i
];
505 Form_pg_enum exists_en
= (Form_pg_enum
) GETSTRUCT(exists_tup
);
506 Oid exists_oid
= exists_en
->oid
;
509 continue; /* ignore odd Oids */
511 if (exists_en
->enumsortorder
< newelemorder
)
513 /* should sort before */
514 if (exists_oid
>= newOid
)
522 /* should sort after */
523 if (exists_oid
<= newOid
)
533 /* If it's even and sorts OK, we're done. */
534 if ((newOid
& 1) == 0)
538 * If it's odd, and sorts OK, loop back to get another OID and
539 * try again. Probably, the next available even OID will sort
540 * correctly too, so it's worth trying.
546 * If it's odd, and does not sort correctly, we're done.
547 * (Probably, the next available even OID would sort
548 * incorrectly too, so no point in trying again.)
554 * If it's even, and does not sort correctly, loop back to get
555 * another OID and try again. (We *must* reject this case.)
561 /* Done with info about existing members */
563 ReleaseCatCacheList(list
);
565 /* Create the new pg_enum entry */
566 memset(nulls
, false, sizeof(nulls
));
567 values
[Anum_pg_enum_oid
- 1] = ObjectIdGetDatum(newOid
);
568 values
[Anum_pg_enum_enumtypid
- 1] = ObjectIdGetDatum(enumTypeOid
);
569 values
[Anum_pg_enum_enumsortorder
- 1] = Float4GetDatum(newelemorder
);
570 namestrcpy(&enumlabel
, newVal
);
571 values
[Anum_pg_enum_enumlabel
- 1] = NameGetDatum(&enumlabel
);
572 enum_tup
= heap_form_tuple(RelationGetDescr(pg_enum
), values
, nulls
);
573 CatalogTupleInsert(pg_enum
, enum_tup
);
574 heap_freetuple(enum_tup
);
576 table_close(pg_enum
, RowExclusiveLock
);
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.
589 if (GetCurrentTransactionNestLevel() == 1 &&
590 EnumTypeUncommitted(enumTypeOid
))
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();
597 /* Add the new value to the table */
598 (void) hash_search(uncommitted_enum_values
, &newOid
, HASH_ENTER
, NULL
);
604 * Rename a label in an enum set.
607 RenameEnumLabel(Oid enumTypeOid
,
620 /* check length of new label is ok */
621 if (strlen(newVal
) > (NAMEDATALEN
- 1))
623 (errcode(ERRCODE_INVALID_NAME
),
624 errmsg("invalid enum label \"%s\"", newVal
),
625 errdetail("Labels must be %d bytes or less.",
629 * Acquire a lock on the enum type, which we won't release until commit.
630 * This ensures that two backends aren't concurrently modifying the same
631 * enum type. Since we are not changing the type's sort order, this is
632 * probably not really necessary, but there seems no reason not to take
633 * the lock to be sure.
635 LockDatabaseObject(TypeRelationId
, enumTypeOid
, 0, ExclusiveLock
);
637 pg_enum
= table_open(EnumRelationId
, RowExclusiveLock
);
639 /* Get the list of existing members of the enum */
640 list
= SearchSysCacheList1(ENUMTYPOIDNAME
,
641 ObjectIdGetDatum(enumTypeOid
));
642 nelems
= list
->n_members
;
645 * Locate the element to rename and check if the new label is already in
646 * use. (The unique index on pg_enum would catch that anyway, but we
647 * prefer a friendlier error message.)
651 for (i
= 0; i
< nelems
; i
++)
653 enum_tup
= &(list
->members
[i
]->tuple
);
654 en
= (Form_pg_enum
) GETSTRUCT(enum_tup
);
655 if (strcmp(NameStr(en
->enumlabel
), oldVal
) == 0)
657 if (strcmp(NameStr(en
->enumlabel
), newVal
) == 0)
662 (errcode(ERRCODE_INVALID_PARAMETER_VALUE
),
663 errmsg("\"%s\" is not an existing enum label",
667 (errcode(ERRCODE_DUPLICATE_OBJECT
),
668 errmsg("enum label \"%s\" already exists",
671 /* OK, make a writable copy of old tuple */
672 enum_tup
= heap_copytuple(old_tup
);
673 en
= (Form_pg_enum
) GETSTRUCT(enum_tup
);
675 ReleaseCatCacheList(list
);
677 /* Update the pg_enum entry */
678 namestrcpy(&en
->enumlabel
, newVal
);
679 CatalogTupleUpdate(pg_enum
, &enum_tup
->t_self
, enum_tup
);
680 heap_freetuple(enum_tup
);
682 table_close(pg_enum
, RowExclusiveLock
);
687 * Test if the given type OID is in the table of uncommitted enum types.
690 EnumTypeUncommitted(Oid typ_id
)
694 /* If we've made no uncommitted types table, it's not in the table */
695 if (uncommitted_enum_types
== NULL
)
698 /* Else, is it in the table? */
699 (void) hash_search(uncommitted_enum_types
, &typ_id
, HASH_FIND
, &found
);
705 * Test if the given enum value is in the table of uncommitted enum values.
708 EnumUncommitted(Oid enum_id
)
712 /* If we've made no uncommitted values table, it's not in the table */
713 if (uncommitted_enum_values
== NULL
)
716 /* Else, is it in the table? */
717 (void) hash_search(uncommitted_enum_values
, &enum_id
, HASH_FIND
, &found
);
723 * Clean up enum stuff after end of top-level transaction.
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.
733 uncommitted_enum_types
= NULL
;
734 uncommitted_enum_values
= NULL
;
740 * Renumber existing enum elements to have sort positions 1..n.
742 * We avoid doing this unless absolutely necessary; in most installations
743 * it will never happen. The reason is that updating existing pg_enum
744 * entries creates hazards for other backends that are concurrently reading
745 * pg_enum. Although system catalog scans now use MVCC semantics, the
746 * syscache machinery might read different pg_enum entries under different
747 * snapshots, so some other backend might get confused about the proper
748 * ordering if a concurrent renumbering occurs.
750 * We therefore make the following choices:
752 * 1. Any code that is interested in the enumsortorder values MUST read
753 * all the relevant pg_enum entries with a single MVCC snapshot, or else
754 * acquire lock on the enum type to prevent concurrent execution of
757 * 2. Code that is not examining enumsortorder can use a syscache
758 * (for example, enum_in and enum_out do so).
761 RenumberEnumType(Relation pg_enum
, HeapTuple
*existing
, int nelems
)
766 * We should only need to increase existing elements' enumsortorders,
767 * never decrease them. Therefore, work from the end backwards, to avoid
768 * unwanted uniqueness violations.
770 for (i
= nelems
- 1; i
>= 0; i
--)
776 newtup
= heap_copytuple(existing
[i
]);
777 en
= (Form_pg_enum
) GETSTRUCT(newtup
);
779 newsortorder
= i
+ 1;
780 if (en
->enumsortorder
!= newsortorder
)
782 en
->enumsortorder
= newsortorder
;
784 CatalogTupleUpdate(pg_enum
, &newtup
->t_self
, newtup
);
787 heap_freetuple(newtup
);
790 /* Make the updates visible */
791 CommandCounterIncrement();
795 /* qsort comparison function for tuples by sort order */
797 sort_order_cmp(const void *p1
, const void *p2
)
799 HeapTuple v1
= *((const HeapTuple
*) p1
);
800 HeapTuple v2
= *((const HeapTuple
*) p2
);
801 Form_pg_enum en1
= (Form_pg_enum
) GETSTRUCT(v1
);
802 Form_pg_enum en2
= (Form_pg_enum
) GETSTRUCT(v2
);
804 if (en1
->enumsortorder
< en2
->enumsortorder
)
806 else if (en1
->enumsortorder
> en2
->enumsortorder
)
813 EstimateUncommittedEnumsSpace(void)
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
);
822 /* Add two for the terminators. */
823 return sizeof(Oid
) * (entries
+ 2);
827 SerializeUncommittedEnums(void *space
, Size size
)
829 Oid
*serialized
= (Oid
*) space
;
832 * Make sure the hash tables haven't changed in size since the caller
833 * reserved the space.
835 Assert(size
== EstimateUncommittedEnumsSpace());
837 /* Write out all the OIDs from the types hash table, if there is one. */
838 if (uncommitted_enum_types
)
840 HASH_SEQ_STATUS status
;
843 hash_seq_init(&status
, uncommitted_enum_types
);
844 while ((value
= (Oid
*) hash_seq_search(&status
)))
845 *serialized
++ = *value
;
848 /* Write out the terminator. */
849 *serialized
++ = InvalidOid
;
851 /* Write out all the OIDs from the values hash table, if there is one. */
852 if (uncommitted_enum_values
)
854 HASH_SEQ_STATUS status
;
857 hash_seq_init(&status
, uncommitted_enum_values
);
858 while ((value
= (Oid
*) hash_seq_search(&status
)))
859 *serialized
++ = *value
;
862 /* Write out the terminator. */
863 *serialized
++ = InvalidOid
;
866 * Make sure the amount of space we actually used matches what was
869 Assert((char *) serialized
== ((char *) space
) + size
);
873 RestoreUncommittedEnums(void *space
)
875 Oid
*serialized
= (Oid
*) space
;
877 Assert(!uncommitted_enum_types
);
878 Assert(!uncommitted_enum_values
);
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
885 if (OidIsValid(*serialized
))
887 /* Read all the types into a new hash table. */
888 init_uncommitted_enum_types();
891 (void) hash_search(uncommitted_enum_types
, serialized
++,
893 } while (OidIsValid(*serialized
));
896 if (OidIsValid(*serialized
))
898 /* Read all the values into a new hash table. */
899 init_uncommitted_enum_values();
902 (void) hash_search(uncommitted_enum_values
, serialized
++,
904 } while (OidIsValid(*serialized
));