PostgreSQL Source Code  git master
pg_enum.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pg_enum.c
4  * routines to support manipulation of the pg_enum relation
5  *
6  * Copyright (c) 2006-2023, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  * src/backend/catalog/pg_enum.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
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"
34 
35 /* Potentially set by pg_upgrade_support functions */
37 
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
41  * committed; otherwise, they might get into indexes where we can't clean
42  * them up, and then if the transaction rolls back we have a broken index.
43  * (See comments for check_safe_enum_use() in enum.c.) Values created by
44  * EnumValuesCreate are *not* entered into the table; we assume those are
45  * created during CREATE TYPE, so they can't go away unless the enum type
46  * itself does.
47  */
48 static HTAB *uncommitted_enums = NULL;
49 
50 static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems);
51 static int sort_order_cmp(const void *p1, const void *p2);
52 
53 
54 /*
55  * EnumValuesCreate
56  * Create an entry in pg_enum for each of the supplied enum values.
57  *
58  * vals is a list of String values.
59  */
60 void
61 EnumValuesCreate(Oid enumTypeOid, List *vals)
62 {
63  Relation pg_enum;
64  Oid *oids;
65  int elemno,
66  num_elems;
67  ListCell *lc;
68  int slotCount = 0;
69  int nslots;
70  CatalogIndexState indstate;
71  TupleTableSlot **slot;
72 
73  num_elems = list_length(vals);
74 
75  /*
76  * We do not bother to check the list of values for duplicates --- if you
77  * have any, you'll get a less-than-friendly unique-index violation. It is
78  * probably not worth trying harder.
79  */
80 
81  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
82 
83  /*
84  * Allocate OIDs for the enum's members.
85  *
86  * While this method does not absolutely guarantee that we generate no
87  * duplicate OIDs (since we haven't entered each oid into the table before
88  * allocating the next), trouble could only occur if the OID counter wraps
89  * all the way around before we finish. Which seems unlikely.
90  */
91  oids = (Oid *) palloc(num_elems * sizeof(Oid));
92 
93  for (elemno = 0; elemno < num_elems; elemno++)
94  {
95  /*
96  * We assign even-numbered OIDs to all the new enum labels. This
97  * tells the comparison functions the OIDs are in the correct sort
98  * order and can be compared directly.
99  */
100  Oid new_oid;
101 
102  do
103  {
104  new_oid = GetNewOidWithIndex(pg_enum, EnumOidIndexId,
105  Anum_pg_enum_oid);
106  } while (new_oid & 1);
107  oids[elemno] = new_oid;
108  }
109 
110  /* sort them, just in case OID counter wrapped from high to low */
111  qsort(oids, num_elems, sizeof(Oid), oid_cmp);
112 
113  /* and make the entries */
114  indstate = CatalogOpenIndexes(pg_enum);
115 
116  /* allocate the slots to use and initialize them */
117  nslots = Min(num_elems,
119  slot = palloc(sizeof(TupleTableSlot *) * nslots);
120  for (int i = 0; i < nslots; i++)
122  &TTSOpsHeapTuple);
123 
124  elemno = 0;
125  foreach(lc, vals)
126  {
127  char *lab = strVal(lfirst(lc));
128  Name enumlabel = palloc0(NAMEDATALEN);
129 
130  /*
131  * labels are stored in a name field, for easier syscache lookup, so
132  * check the length to make sure it's within range.
133  */
134  if (strlen(lab) > (NAMEDATALEN - 1))
135  ereport(ERROR,
136  (errcode(ERRCODE_INVALID_NAME),
137  errmsg("invalid enum label \"%s\"", lab),
138  errdetail("Labels must be %d bytes or less.",
139  NAMEDATALEN - 1)));
140 
141  ExecClearTuple(slot[slotCount]);
142 
143  memset(slot[slotCount]->tts_isnull, false,
144  slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool));
145 
146  slot[slotCount]->tts_values[Anum_pg_enum_oid - 1] = ObjectIdGetDatum(oids[elemno]);
147  slot[slotCount]->tts_values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
148  slot[slotCount]->tts_values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1);
149 
150  namestrcpy(enumlabel, lab);
151  slot[slotCount]->tts_values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(enumlabel);
152 
153  ExecStoreVirtualTuple(slot[slotCount]);
154  slotCount++;
155 
156  /* if slots are full, insert a batch of tuples */
157  if (slotCount == nslots)
158  {
159  CatalogTuplesMultiInsertWithInfo(pg_enum, slot, slotCount,
160  indstate);
161  slotCount = 0;
162  }
163 
164  elemno++;
165  }
166 
167  /* Insert any tuples left in the buffer */
168  if (slotCount > 0)
169  CatalogTuplesMultiInsertWithInfo(pg_enum, slot, slotCount,
170  indstate);
171 
172  /* clean up */
173  pfree(oids);
174  for (int i = 0; i < nslots; i++)
176  CatalogCloseIndexes(indstate);
177  table_close(pg_enum, RowExclusiveLock);
178 }
179 
180 
181 /*
182  * EnumValuesDelete
183  * Remove all the pg_enum entries for the specified enum type.
184  */
185 void
186 EnumValuesDelete(Oid enumTypeOid)
187 {
188  Relation pg_enum;
189  ScanKeyData key[1];
190  SysScanDesc scan;
191  HeapTuple tup;
192 
193  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
194 
195  ScanKeyInit(&key[0],
196  Anum_pg_enum_enumtypid,
197  BTEqualStrategyNumber, F_OIDEQ,
198  ObjectIdGetDatum(enumTypeOid));
199 
200  scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true,
201  NULL, 1, key);
202 
203  while (HeapTupleIsValid(tup = systable_getnext(scan)))
204  {
205  CatalogTupleDelete(pg_enum, &tup->t_self);
206  }
207 
208  systable_endscan(scan);
209 
210  table_close(pg_enum, RowExclusiveLock);
211 }
212 
213 /*
214  * Initialize the uncommitted enum table for this transaction.
215  */
216 static void
218 {
219  HASHCTL hash_ctl;
220 
221  hash_ctl.keysize = sizeof(Oid);
222  hash_ctl.entrysize = sizeof(Oid);
223  hash_ctl.hcxt = TopTransactionContext;
224  uncommitted_enums = hash_create("Uncommitted enums",
225  32,
226  &hash_ctl,
228 }
229 
230 /*
231  * AddEnumLabel
232  * Add a new label to the enum set. By default it goes at
233  * the end, but the user can choose to place it before or
234  * after any existing set member.
235  */
236 void
237 AddEnumLabel(Oid enumTypeOid,
238  const char *newVal,
239  const char *neighbor,
240  bool newValIsAfter,
241  bool skipIfExists)
242 {
243  Relation pg_enum;
244  Oid newOid;
245  Datum values[Natts_pg_enum];
246  bool nulls[Natts_pg_enum];
247  NameData enumlabel;
248  HeapTuple enum_tup;
249  float4 newelemorder;
250  HeapTuple *existing;
251  CatCList *list;
252  int nelems;
253  int i;
254 
255  /* check length of new label is ok */
256  if (strlen(newVal) > (NAMEDATALEN - 1))
257  ereport(ERROR,
258  (errcode(ERRCODE_INVALID_NAME),
259  errmsg("invalid enum label \"%s\"", newVal),
260  errdetail("Labels must be %d bytes or less.",
261  NAMEDATALEN - 1)));
262 
263  /*
264  * Acquire a lock on the enum type, which we won't release until commit.
265  * This ensures that two backends aren't concurrently modifying the same
266  * enum type. Without that, we couldn't be sure to get a consistent view
267  * of the enum members via the syscache. Note that this does not block
268  * other backends from inspecting the type; see comments for
269  * RenumberEnumType.
270  */
271  LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
272 
273  /*
274  * Check if label is already in use. The unique index on pg_enum would
275  * catch this anyway, but we prefer a friendlier error message, and
276  * besides we need a check to support IF NOT EXISTS.
277  */
278  enum_tup = SearchSysCache2(ENUMTYPOIDNAME,
279  ObjectIdGetDatum(enumTypeOid),
280  CStringGetDatum(newVal));
281  if (HeapTupleIsValid(enum_tup))
282  {
283  ReleaseSysCache(enum_tup);
284  if (skipIfExists)
285  {
286  ereport(NOTICE,
288  errmsg("enum label \"%s\" already exists, skipping",
289  newVal)));
290  return;
291  }
292  else
293  ereport(ERROR,
295  errmsg("enum label \"%s\" already exists",
296  newVal)));
297  }
298 
299  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
300 
301  /* If we have to renumber the existing members, we restart from here */
302 restart:
303 
304  /* Get the list of existing members of the enum */
306  ObjectIdGetDatum(enumTypeOid));
307  nelems = list->n_members;
308 
309  /* Sort the existing members by enumsortorder */
310  existing = (HeapTuple *) palloc(nelems * sizeof(HeapTuple));
311  for (i = 0; i < nelems; i++)
312  existing[i] = &(list->members[i]->tuple);
313 
314  qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
315 
316  if (neighbor == NULL)
317  {
318  /*
319  * Put the new label at the end of the list. No change to existing
320  * tuples is required.
321  */
322  if (nelems > 0)
323  {
324  Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nelems - 1]);
325 
326  newelemorder = en->enumsortorder + 1;
327  }
328  else
329  newelemorder = 1;
330  }
331  else
332  {
333  /* BEFORE or AFTER was specified */
334  int nbr_index;
335  int other_nbr_index;
336  Form_pg_enum nbr_en;
337  Form_pg_enum other_nbr_en;
338 
339  /* Locate the neighbor element */
340  for (nbr_index = 0; nbr_index < nelems; nbr_index++)
341  {
342  Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
343 
344  if (strcmp(NameStr(en->enumlabel), neighbor) == 0)
345  break;
346  }
347  if (nbr_index >= nelems)
348  ereport(ERROR,
349  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
350  errmsg("\"%s\" is not an existing enum label",
351  neighbor)));
352  nbr_en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
353 
354  /*
355  * Attempt to assign an appropriate enumsortorder value: one less than
356  * the smallest member, one more than the largest member, or halfway
357  * between two existing members.
358  *
359  * In the "halfway" case, because of the finite precision of float4,
360  * we might compute a value that's actually equal to one or the other
361  * of its neighbors. In that case we renumber the existing members
362  * and try again.
363  */
364  if (newValIsAfter)
365  other_nbr_index = nbr_index + 1;
366  else
367  other_nbr_index = nbr_index - 1;
368 
369  if (other_nbr_index < 0)
370  newelemorder = nbr_en->enumsortorder - 1;
371  else if (other_nbr_index >= nelems)
372  newelemorder = nbr_en->enumsortorder + 1;
373  else
374  {
375  /*
376  * The midpoint value computed here has to be rounded to float4
377  * precision, else our equality comparisons against the adjacent
378  * values are meaningless. The most portable way of forcing that
379  * to happen with non-C-standard-compliant compilers is to store
380  * it into a volatile variable.
381  */
382  volatile float4 midpoint;
383 
384  other_nbr_en = (Form_pg_enum) GETSTRUCT(existing[other_nbr_index]);
385  midpoint = (nbr_en->enumsortorder +
386  other_nbr_en->enumsortorder) / 2;
387 
388  if (midpoint == nbr_en->enumsortorder ||
389  midpoint == other_nbr_en->enumsortorder)
390  {
391  RenumberEnumType(pg_enum, existing, nelems);
392  /* Clean up and start over */
393  pfree(existing);
395  goto restart;
396  }
397 
398  newelemorder = midpoint;
399  }
400  }
401 
402  /* Get a new OID for the new label */
403  if (IsBinaryUpgrade)
404  {
406  ereport(ERROR,
407  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
408  errmsg("pg_enum OID value not set when in binary upgrade mode")));
409 
410  /*
411  * Use binary-upgrade override for pg_enum.oid, if supplied. During
412  * binary upgrade, all pg_enum.oid's are set this way so they are
413  * guaranteed to be consistent.
414  */
415  if (neighbor != NULL)
416  ereport(ERROR,
417  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
418  errmsg("ALTER TYPE ADD BEFORE/AFTER is incompatible with binary upgrade")));
419 
422  }
423  else
424  {
425  /*
426  * Normal case: we need to allocate a new Oid for the value.
427  *
428  * We want to give the new element an even-numbered Oid if it's safe,
429  * which is to say it compares correctly to all pre-existing even
430  * numbered Oids in the enum. Otherwise, we must give it an odd Oid.
431  */
432  for (;;)
433  {
434  bool sorts_ok;
435 
436  /* Get a new OID (different from all existing pg_enum tuples) */
437  newOid = GetNewOidWithIndex(pg_enum, EnumOidIndexId,
438  Anum_pg_enum_oid);
439 
440  /*
441  * Detect whether it sorts correctly relative to existing
442  * even-numbered labels of the enum. We can ignore existing
443  * labels with odd Oids, since a comparison involving one of those
444  * will not take the fast path anyway.
445  */
446  sorts_ok = true;
447  for (i = 0; i < nelems; i++)
448  {
449  HeapTuple exists_tup = existing[i];
450  Form_pg_enum exists_en = (Form_pg_enum) GETSTRUCT(exists_tup);
451  Oid exists_oid = exists_en->oid;
452 
453  if (exists_oid & 1)
454  continue; /* ignore odd Oids */
455 
456  if (exists_en->enumsortorder < newelemorder)
457  {
458  /* should sort before */
459  if (exists_oid >= newOid)
460  {
461  sorts_ok = false;
462  break;
463  }
464  }
465  else
466  {
467  /* should sort after */
468  if (exists_oid <= newOid)
469  {
470  sorts_ok = false;
471  break;
472  }
473  }
474  }
475 
476  if (sorts_ok)
477  {
478  /* If it's even and sorts OK, we're done. */
479  if ((newOid & 1) == 0)
480  break;
481 
482  /*
483  * If it's odd, and sorts OK, loop back to get another OID and
484  * try again. Probably, the next available even OID will sort
485  * correctly too, so it's worth trying.
486  */
487  }
488  else
489  {
490  /*
491  * If it's odd, and does not sort correctly, we're done.
492  * (Probably, the next available even OID would sort
493  * incorrectly too, so no point in trying again.)
494  */
495  if (newOid & 1)
496  break;
497 
498  /*
499  * If it's even, and does not sort correctly, loop back to get
500  * another OID and try again. (We *must* reject this case.)
501  */
502  }
503  }
504  }
505 
506  /* Done with info about existing members */
507  pfree(existing);
509 
510  /* Create the new pg_enum entry */
511  memset(nulls, false, sizeof(nulls));
512  values[Anum_pg_enum_oid - 1] = ObjectIdGetDatum(newOid);
513  values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
514  values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(newelemorder);
515  namestrcpy(&enumlabel, newVal);
516  values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
517  enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
518  CatalogTupleInsert(pg_enum, enum_tup);
519  heap_freetuple(enum_tup);
520 
521  table_close(pg_enum, RowExclusiveLock);
522 
523  /* Set up the uncommitted enum table if not already done in this tx */
524  if (uncommitted_enums == NULL)
526 
527  /* Add the new value to the table */
528  (void) hash_search(uncommitted_enums, &newOid, HASH_ENTER, NULL);
529 }
530 
531 
532 /*
533  * RenameEnumLabel
534  * Rename a label in an enum set.
535  */
536 void
537 RenameEnumLabel(Oid enumTypeOid,
538  const char *oldVal,
539  const char *newVal)
540 {
541  Relation pg_enum;
542  HeapTuple enum_tup;
543  Form_pg_enum en;
544  CatCList *list;
545  int nelems;
546  HeapTuple old_tup;
547  bool found_new;
548  int i;
549 
550  /* check length of new label is ok */
551  if (strlen(newVal) > (NAMEDATALEN - 1))
552  ereport(ERROR,
553  (errcode(ERRCODE_INVALID_NAME),
554  errmsg("invalid enum label \"%s\"", newVal),
555  errdetail("Labels must be %d bytes or less.",
556  NAMEDATALEN - 1)));
557 
558  /*
559  * Acquire a lock on the enum type, which we won't release until commit.
560  * This ensures that two backends aren't concurrently modifying the same
561  * enum type. Since we are not changing the type's sort order, this is
562  * probably not really necessary, but there seems no reason not to take
563  * the lock to be sure.
564  */
565  LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
566 
567  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
568 
569  /* Get the list of existing members of the enum */
571  ObjectIdGetDatum(enumTypeOid));
572  nelems = list->n_members;
573 
574  /*
575  * Locate the element to rename and check if the new label is already in
576  * use. (The unique index on pg_enum would catch that anyway, but we
577  * prefer a friendlier error message.)
578  */
579  old_tup = NULL;
580  found_new = false;
581  for (i = 0; i < nelems; i++)
582  {
583  enum_tup = &(list->members[i]->tuple);
584  en = (Form_pg_enum) GETSTRUCT(enum_tup);
585  if (strcmp(NameStr(en->enumlabel), oldVal) == 0)
586  old_tup = enum_tup;
587  if (strcmp(NameStr(en->enumlabel), newVal) == 0)
588  found_new = true;
589  }
590  if (!old_tup)
591  ereport(ERROR,
592  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
593  errmsg("\"%s\" is not an existing enum label",
594  oldVal)));
595  if (found_new)
596  ereport(ERROR,
598  errmsg("enum label \"%s\" already exists",
599  newVal)));
600 
601  /* OK, make a writable copy of old tuple */
602  enum_tup = heap_copytuple(old_tup);
603  en = (Form_pg_enum) GETSTRUCT(enum_tup);
604 
606 
607  /* Update the pg_enum entry */
608  namestrcpy(&en->enumlabel, newVal);
609  CatalogTupleUpdate(pg_enum, &enum_tup->t_self, enum_tup);
610  heap_freetuple(enum_tup);
611 
612  table_close(pg_enum, RowExclusiveLock);
613 }
614 
615 
616 /*
617  * Test if the given enum value is in the table of uncommitted enums.
618  */
619 bool
621 {
622  bool found;
623 
624  /* If we've made no uncommitted table, all values are safe */
625  if (uncommitted_enums == NULL)
626  return false;
627 
628  /* Else, is it in the table? */
629  (void) hash_search(uncommitted_enums, &enum_id, HASH_FIND, &found);
630  return found;
631 }
632 
633 
634 /*
635  * Clean up enum stuff after end of top-level transaction.
636  */
637 void
639 {
640  /*
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.
644  */
645  uncommitted_enums = NULL;
646 }
647 
648 
649 /*
650  * RenumberEnumType
651  * Renumber existing enum elements to have sort positions 1..n.
652  *
653  * We avoid doing this unless absolutely necessary; in most installations
654  * it will never happen. The reason is that updating existing pg_enum
655  * entries creates hazards for other backends that are concurrently reading
656  * pg_enum. Although system catalog scans now use MVCC semantics, the
657  * syscache machinery might read different pg_enum entries under different
658  * snapshots, so some other backend might get confused about the proper
659  * ordering if a concurrent renumbering occurs.
660  *
661  * We therefore make the following choices:
662  *
663  * 1. Any code that is interested in the enumsortorder values MUST read
664  * all the relevant pg_enum entries with a single MVCC snapshot, or else
665  * acquire lock on the enum type to prevent concurrent execution of
666  * AddEnumLabel().
667  *
668  * 2. Code that is not examining enumsortorder can use a syscache
669  * (for example, enum_in and enum_out do so).
670  */
671 static void
672 RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
673 {
674  int i;
675 
676  /*
677  * We should only need to increase existing elements' enumsortorders,
678  * never decrease them. Therefore, work from the end backwards, to avoid
679  * unwanted uniqueness violations.
680  */
681  for (i = nelems - 1; i >= 0; i--)
682  {
683  HeapTuple newtup;
684  Form_pg_enum en;
685  float4 newsortorder;
686 
687  newtup = heap_copytuple(existing[i]);
688  en = (Form_pg_enum) GETSTRUCT(newtup);
689 
690  newsortorder = i + 1;
691  if (en->enumsortorder != newsortorder)
692  {
693  en->enumsortorder = newsortorder;
694 
695  CatalogTupleUpdate(pg_enum, &newtup->t_self, newtup);
696  }
697 
698  heap_freetuple(newtup);
699  }
700 
701  /* Make the updates visible */
703 }
704 
705 
706 /* qsort comparison function for tuples by sort order */
707 static int
708 sort_order_cmp(const void *p1, const void *p2)
709 {
710  HeapTuple v1 = *((const HeapTuple *) p1);
711  HeapTuple v2 = *((const HeapTuple *) p2);
712  Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
713  Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
714 
715  if (en1->enumsortorder < en2->enumsortorder)
716  return -1;
717  else if (en1->enumsortorder > en2->enumsortorder)
718  return 1;
719  else
720  return 0;
721 }
722 
723 Size
725 {
726  size_t entries;
727 
728  if (uncommitted_enums)
730  else
731  entries = 0;
732 
733  /* Add one for the terminator. */
734  return sizeof(Oid) * (entries + 1);
735 }
736 
737 void
738 SerializeUncommittedEnums(void *space, Size size)
739 {
740  Oid *serialized = (Oid *) space;
741 
742  /*
743  * Make sure the hash table hasn't changed in size since the caller
744  * reserved the space.
745  */
747 
748  /* Write out all the values from the hash table, if there is one. */
749  if (uncommitted_enums)
750  {
751  HASH_SEQ_STATUS status;
752  Oid *value;
753 
755  while ((value = (Oid *) hash_seq_search(&status)))
756  *serialized++ = *value;
757  }
758 
759  /* Write out the terminator. */
760  *serialized = InvalidOid;
761 
762  /*
763  * Make sure the amount of space we actually used matches what was
764  * estimated.
765  */
766  Assert((char *) (serialized + 1) == ((char *) space) + size);
767 }
768 
769 void
771 {
772  Oid *serialized = (Oid *) space;
773 
775 
776  /*
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.
780  */
781  if (!OidIsValid(*serialized))
782  return;
783 
784  /* Read all the values into a new hash table. */
786  do
787  {
788  hash_search(uncommitted_enums, serialized++, HASH_ENTER, NULL);
789  } while (OidIsValid(*serialized));
790 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define NameStr(name)
Definition: c.h:735
#define Min(x, y)
Definition: c.h:993
float float4
Definition: c.h:618
#define OidIsValid(objectId)
Definition: c.h:764
size_t Size
Definition: c.h:594
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:393
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1779
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:953
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:350
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1377
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1431
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1421
int errdetail(const char *fmt,...)
Definition: elog.c:1202
int errcode(int sqlerrcode)
Definition: elog.c:858
int errmsg(const char *fmt,...)
Definition: elog.c:1069
#define ERROR
Definition: elog.h:39
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1553
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1255
const TupleTableSlotOps TTSOpsHeapTuple
Definition: execTuples.c:84
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1239
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:599
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:506
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:387
bool IsBinaryUpgrade
Definition: globals.c:114
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1108
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:768
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1426
@ HASH_FIND
Definition: hsearch.h:113
@ HASH_ENTER
Definition: hsearch.h:114
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313
void CatalogTuplesMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot, int ntuples, CatalogIndexState indstate)
Definition: indexing.c:273
void CatalogCloseIndexes(CatalogIndexState indstate)
Definition: indexing.c:61
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
Definition: indexing.c:43
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
#define MAX_CATALOG_MULTI_INSERT_BYTES
Definition: indexing.h:33
static struct @148 value
int i
Definition: isn.c:73
Assert(fmt[strlen(fmt) - 1] !='\n')
void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode)
Definition: lmgr.c:1005
#define ExclusiveLock
Definition: lockdefs.h:42
#define RowExclusiveLock
Definition: lockdefs.h:38
MemoryContext TopTransactionContext
Definition: mcxt.c:146
void pfree(void *pointer)
Definition: mcxt.c:1456
void * palloc0(Size size)
Definition: mcxt.c:1257
void * palloc(Size size)
Definition: mcxt.c:1226
void namestrcpy(Name name, const char *str)
Definition: name.c:233
int oid_cmp(const void *p1, const void *p2)
Definition: oid.c:257
#define NAMEDATALEN
static HTAB * uncommitted_enums
Definition: pg_enum.c:48
void RestoreUncommittedEnums(void *space)
Definition: pg_enum.c:770
void RenameEnumLabel(Oid enumTypeOid, const char *oldVal, const char *newVal)
Definition: pg_enum.c:537
Size EstimateUncommittedEnumsSpace(void)
Definition: pg_enum.c:724
static void init_uncommitted_enums(void)
Definition: pg_enum.c:217
bool EnumUncommitted(Oid enum_id)
Definition: pg_enum.c:620
void EnumValuesDelete(Oid enumTypeOid)
Definition: pg_enum.c:186
void AddEnumLabel(Oid enumTypeOid, const char *newVal, const char *neighbor, bool newValIsAfter, bool skipIfExists)
Definition: pg_enum.c:237
void SerializeUncommittedEnums(void *space, Size size)
Definition: pg_enum.c:738
Oid binary_upgrade_next_pg_enum_oid
Definition: pg_enum.c:36
static int sort_order_cmp(const void *p1, const void *p2)
Definition: pg_enum.c:708
void AtEOXact_Enum(void)
Definition: pg_enum.c:638
static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
Definition: pg_enum.c:672
void EnumValuesCreate(Oid enumTypeOid, List *vals)
Definition: pg_enum.c:61
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:44
FormData_pg_enum
Definition: pg_enum.h:37
#define lfirst(lc)
Definition: pg_list.h:172
static int list_length(const List *l)
Definition: pg_list.h:152
#define qsort(a, b, c, d)
Definition: port.h:445
static Datum Float4GetDatum(float4 X)
Definition: postgres.h:475
uintptr_t Datum
Definition: postgres.h:64
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:373
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:350
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetDescr(relation)
Definition: rel.h:530
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86
Definition: dynahash.c:220
ItemPointerData t_self
Definition: htup.h:65
Definition: pg_list.h:54
Datum * tts_values
Definition: tuptable.h:125
Definition: c.h:730
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:868
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:831
@ ENUMTYPOIDNAME
Definition: syscache.h:58
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:218
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:432
#define strVal(v)
Definition: value.h:82
void CommandCounterIncrement(void)
Definition: xact.c:1078