PostgreSQL Source Code  git master
pg_enum.c File Reference
#include "postgres.h"
#include "access/genam.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "access/xact.h"
#include "catalog/binary_upgrade.h"
#include "catalog/catalog.h"
#include "catalog/indexing.h"
#include "catalog/pg_enum.h"
#include "catalog/pg_type.h"
#include "miscadmin.h"
#include "nodes/value.h"
#include "storage/lmgr.h"
#include "utils/builtins.h"
#include "utils/catcache.h"
#include "utils/fmgroids.h"
#include "utils/hsearch.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
Include dependency graph for pg_enum.c:

Go to the source code of this file.

Functions

static void init_uncommitted_enum_types (void)
 
static void init_uncommitted_enum_values (void)
 
static bool EnumTypeUncommitted (Oid typ_id)
 
static void RenumberEnumType (Relation pg_enum, HeapTuple *existing, int nelems)
 
static int sort_order_cmp (const void *p1, const void *p2)
 
void EnumValuesCreate (Oid enumTypeOid, List *vals)
 
void EnumValuesDelete (Oid enumTypeOid)
 
void AddEnumLabel (Oid enumTypeOid, const char *newVal, const char *neighbor, bool newValIsAfter, bool skipIfExists)
 
void RenameEnumLabel (Oid enumTypeOid, const char *oldVal, const char *newVal)
 
bool EnumUncommitted (Oid enum_id)
 
void AtEOXact_Enum (void)
 
Size EstimateUncommittedEnumsSpace (void)
 
void SerializeUncommittedEnums (void *space, Size size)
 
void RestoreUncommittedEnums (void *space)
 

Variables

Oid binary_upgrade_next_pg_enum_oid = InvalidOid
 
static HTABuncommitted_enum_types = NULL
 
static HTABuncommitted_enum_values = NULL
 

Function Documentation

◆ AddEnumLabel()

void AddEnumLabel ( Oid  enumTypeOid,
const char *  newVal,
const char *  neighbor,
bool  newValIsAfter,
bool  skipIfExists 
)

Definition at line 292 of file pg_enum.c.

297 {
298  Relation pg_enum;
299  Oid newOid;
300  Datum values[Natts_pg_enum];
301  bool nulls[Natts_pg_enum];
302  NameData enumlabel;
303  HeapTuple enum_tup;
304  float4 newelemorder;
305  HeapTuple *existing;
306  CatCList *list;
307  int nelems;
308  int i;
309 
310  /* check length of new label is ok */
311  if (strlen(newVal) > (NAMEDATALEN - 1))
312  ereport(ERROR,
313  (errcode(ERRCODE_INVALID_NAME),
314  errmsg("invalid enum label \"%s\"", newVal),
315  errdetail("Labels must be %d bytes or less.",
316  NAMEDATALEN - 1)));
317 
318  /*
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
324  * RenumberEnumType.
325  */
326  LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
327 
328  /*
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.
332  */
333  enum_tup = SearchSysCache2(ENUMTYPOIDNAME,
334  ObjectIdGetDatum(enumTypeOid),
335  CStringGetDatum(newVal));
336  if (HeapTupleIsValid(enum_tup))
337  {
338  ReleaseSysCache(enum_tup);
339  if (skipIfExists)
340  {
341  ereport(NOTICE,
343  errmsg("enum label \"%s\" already exists, skipping",
344  newVal)));
345  return;
346  }
347  else
348  ereport(ERROR,
350  errmsg("enum label \"%s\" already exists",
351  newVal)));
352  }
353 
354  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
355 
356  /* If we have to renumber the existing members, we restart from here */
357 restart:
358 
359  /* Get the list of existing members of the enum */
360  list = SearchSysCacheList1(ENUMTYPOIDNAME,
361  ObjectIdGetDatum(enumTypeOid));
362  nelems = list->n_members;
363 
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);
368 
369  qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
370 
371  if (neighbor == NULL)
372  {
373  /*
374  * Put the new label at the end of the list. No change to existing
375  * tuples is required.
376  */
377  if (nelems > 0)
378  {
379  Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nelems - 1]);
380 
381  newelemorder = en->enumsortorder + 1;
382  }
383  else
384  newelemorder = 1;
385  }
386  else
387  {
388  /* BEFORE or AFTER was specified */
389  int nbr_index;
390  int other_nbr_index;
391  Form_pg_enum nbr_en;
392  Form_pg_enum other_nbr_en;
393 
394  /* Locate the neighbor element */
395  for (nbr_index = 0; nbr_index < nelems; nbr_index++)
396  {
397  Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
398 
399  if (strcmp(NameStr(en->enumlabel), neighbor) == 0)
400  break;
401  }
402  if (nbr_index >= nelems)
403  ereport(ERROR,
404  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
405  errmsg("\"%s\" is not an existing enum label",
406  neighbor)));
407  nbr_en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
408 
409  /*
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.
413  *
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
417  * and try again.
418  */
419  if (newValIsAfter)
420  other_nbr_index = nbr_index + 1;
421  else
422  other_nbr_index = nbr_index - 1;
423 
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;
428  else
429  {
430  /*
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.
436  */
437  volatile float4 midpoint;
438 
439  other_nbr_en = (Form_pg_enum) GETSTRUCT(existing[other_nbr_index]);
440  midpoint = (nbr_en->enumsortorder +
441  other_nbr_en->enumsortorder) / 2;
442 
443  if (midpoint == nbr_en->enumsortorder ||
444  midpoint == other_nbr_en->enumsortorder)
445  {
446  RenumberEnumType(pg_enum, existing, nelems);
447  /* Clean up and start over */
448  pfree(existing);
450  goto restart;
451  }
452 
453  newelemorder = midpoint;
454  }
455  }
456 
457  /* Get a new OID for the new label */
458  if (IsBinaryUpgrade)
459  {
461  ereport(ERROR,
462  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
463  errmsg("pg_enum OID value not set when in binary upgrade mode")));
464 
465  /*
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.
469  */
470  if (neighbor != NULL)
471  ereport(ERROR,
472  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
473  errmsg("ALTER TYPE ADD BEFORE/AFTER is incompatible with binary upgrade")));
474 
477  }
478  else
479  {
480  /*
481  * Normal case: we need to allocate a new Oid for the value.
482  *
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.
486  */
487  for (;;)
488  {
489  bool sorts_ok;
490 
491  /* Get a new OID (different from all existing pg_enum tuples) */
492  newOid = GetNewOidWithIndex(pg_enum, EnumOidIndexId,
493  Anum_pg_enum_oid);
494 
495  /*
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.
500  */
501  sorts_ok = true;
502  for (i = 0; i < nelems; i++)
503  {
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;
507 
508  if (exists_oid & 1)
509  continue; /* ignore odd Oids */
510 
511  if (exists_en->enumsortorder < newelemorder)
512  {
513  /* should sort before */
514  if (exists_oid >= newOid)
515  {
516  sorts_ok = false;
517  break;
518  }
519  }
520  else
521  {
522  /* should sort after */
523  if (exists_oid <= newOid)
524  {
525  sorts_ok = false;
526  break;
527  }
528  }
529  }
530 
531  if (sorts_ok)
532  {
533  /* If it's even and sorts OK, we're done. */
534  if ((newOid & 1) == 0)
535  break;
536 
537  /*
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.
541  */
542  }
543  else
544  {
545  /*
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.)
549  */
550  if (newOid & 1)
551  break;
552 
553  /*
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.)
556  */
557  }
558  }
559  }
560 
561  /* Done with info about existing members */
562  pfree(existing);
564 
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);
575 
576  table_close(pg_enum, RowExclusiveLock);
577 
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)
596 
597  /* Add the new value to the table */
598  (void) hash_search(uncommitted_enum_values, &newOid, HASH_ENTER, NULL);
599 }
static Datum values[MAXATTR]
Definition: bootstrap.c:151
#define NameStr(name)
Definition: c.h:700
float float4
Definition: c.h:583
#define OidIsValid(objectId)
Definition: c.h:729
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:419
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1985
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:955
int errdetail(const char *fmt,...)
Definition: elog.c:1203
int errcode(int sqlerrcode)
Definition: elog.c:853
int errmsg(const char *fmt,...)
Definition: elog.c:1070
#define ERROR
Definition: elog.h:39
#define NOTICE
Definition: elog.h:35
#define ereport(elevel,...)
Definition: elog.h:149
bool IsBinaryUpgrade
Definition: globals.c:120
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1434
@ HASH_ENTER
Definition: hsearch.h:114
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:653
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:233
int i
Definition: isn.c:72
void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode)
Definition: lmgr.c:993
#define ExclusiveLock
Definition: lockdefs.h:42
#define RowExclusiveLock
Definition: lockdefs.h:38
void pfree(void *pointer)
Definition: mcxt.c:1521
void * palloc(Size size)
Definition: mcxt.c:1317
void namestrcpy(Name name, const char *str)
Definition: name.c:233
#define NAMEDATALEN
static HTAB * uncommitted_enum_values
Definition: pg_enum.c:63
static void init_uncommitted_enum_values(void)
Definition: pg_enum.c:272
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:797
static bool EnumTypeUncommitted(Oid typ_id)
Definition: pg_enum.c:690
static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
Definition: pg_enum.c:761
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:44
#define qsort(a, b, c, d)
Definition: port.h:447
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:531
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:30
Definition: c.h:695
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:269
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:232
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:127
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40
int GetCurrentTransactionNestLevel(void)
Definition: xact.c:928

References binary_upgrade_next_pg_enum_oid, CatalogTupleInsert(), CStringGetDatum(), EnumTypeUncommitted(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errmsg(), ERROR, ExclusiveLock, Float4GetDatum(), GetCurrentTransactionNestLevel(), GetNewOidWithIndex(), GETSTRUCT, HASH_ENTER, hash_search(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, init_uncommitted_enum_values(), InvalidOid, IsBinaryUpgrade, sort-test::list, LockDatabaseObject(), NAMEDATALEN, NameGetDatum(), NameStr, namestrcpy(), NOTICE, ObjectIdGetDatum(), OidIsValid, palloc(), pfree(), qsort, RelationGetDescr, ReleaseCatCacheList(), ReleaseSysCache(), RenumberEnumType(), RowExclusiveLock, SearchSysCache2(), SearchSysCacheList1, sort_order_cmp(), table_close(), table_open(), uncommitted_enum_values, and values.

Referenced by AlterEnum().

◆ AtEOXact_Enum()

void AtEOXact_Enum ( void  )

Definition at line 726 of file pg_enum.c.

727 {
728  /*
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.
732  */
733  uncommitted_enum_types = NULL;
735 }
static HTAB * uncommitted_enum_types
Definition: pg_enum.c:62

References uncommitted_enum_types, and uncommitted_enum_values.

Referenced by AbortTransaction(), CommitTransaction(), and PrepareTransaction().

◆ EnumTypeUncommitted()

static bool EnumTypeUncommitted ( Oid  typ_id)
static

Definition at line 690 of file pg_enum.c.

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 }
@ HASH_FIND
Definition: hsearch.h:113

References HASH_FIND, hash_search(), and uncommitted_enum_types.

Referenced by AddEnumLabel().

◆ EnumUncommitted()

bool EnumUncommitted ( Oid  enum_id)

Definition at line 708 of file pg_enum.c.

709 {
710  bool found;
711 
712  /* If we've made no uncommitted values table, it's not in the table */
713  if (uncommitted_enum_values == NULL)
714  return false;
715 
716  /* Else, is it in the table? */
717  (void) hash_search(uncommitted_enum_values, &enum_id, HASH_FIND, &found);
718  return found;
719 }

References HASH_FIND, hash_search(), and uncommitted_enum_values.

Referenced by check_safe_enum_use().

◆ EnumValuesCreate()

void EnumValuesCreate ( Oid  enumTypeOid,
List vals 
)

Definition at line 84 of file pg_enum.c.

85 {
86  Relation pg_enum;
87  Oid *oids;
88  int elemno,
89  num_elems;
90  ListCell *lc;
91  int slotCount = 0;
92  int nslots;
93  CatalogIndexState indstate;
94  TupleTableSlot **slot;
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  */
104  {
105  if (uncommitted_enum_types == NULL)
107  (void) hash_search(uncommitted_enum_types, &enumTypeOid,
108  HASH_ENTER, NULL);
109  }
110 
111  num_elems = list_length(vals);
112 
113  /*
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.
117  */
118 
119  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
120 
121  /*
122  * Allocate OIDs for the enum's members.
123  *
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.
128  */
129  oids = (Oid *) palloc(num_elems * sizeof(Oid));
130 
131  for (elemno = 0; elemno < num_elems; elemno++)
132  {
133  /*
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.
137  */
138  Oid new_oid;
139 
140  do
141  {
142  new_oid = GetNewOidWithIndex(pg_enum, EnumOidIndexId,
143  Anum_pg_enum_oid);
144  } while (new_oid & 1);
145  oids[elemno] = new_oid;
146  }
147 
148  /* sort them, just in case OID counter wrapped from high to low */
149  qsort(oids, num_elems, sizeof(Oid), oid_cmp);
150 
151  /* and make the entries */
152  indstate = CatalogOpenIndexes(pg_enum);
153 
154  /* allocate the slots to use and initialize them */
155  nslots = Min(num_elems,
157  slot = palloc(sizeof(TupleTableSlot *) * nslots);
158  for (int i = 0; i < nslots; i++)
160  &TTSOpsHeapTuple);
161 
162  elemno = 0;
163  foreach(lc, vals)
164  {
165  char *lab = strVal(lfirst(lc));
166  Name enumlabel = palloc0(NAMEDATALEN);
167 
168  /*
169  * labels are stored in a name field, for easier syscache lookup, so
170  * check the length to make sure it's within range.
171  */
172  if (strlen(lab) > (NAMEDATALEN - 1))
173  ereport(ERROR,
174  (errcode(ERRCODE_INVALID_NAME),
175  errmsg("invalid enum label \"%s\"", lab),
176  errdetail("Labels must be %d bytes or less.",
177  NAMEDATALEN - 1)));
178 
179  ExecClearTuple(slot[slotCount]);
180 
181  memset(slot[slotCount]->tts_isnull, false,
182  slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool));
183 
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);
187 
188  namestrcpy(enumlabel, lab);
189  slot[slotCount]->tts_values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(enumlabel);
190 
191  ExecStoreVirtualTuple(slot[slotCount]);
192  slotCount++;
193 
194  /* if slots are full, insert a batch of tuples */
195  if (slotCount == nslots)
196  {
197  CatalogTuplesMultiInsertWithInfo(pg_enum, slot, slotCount,
198  indstate);
199  slotCount = 0;
200  }
201 
202  elemno++;
203  }
204 
205  /* Insert any tuples left in the buffer */
206  if (slotCount > 0)
207  CatalogTuplesMultiInsertWithInfo(pg_enum, slot, slotCount,
208  indstate);
209 
210  /* clean up */
211  pfree(oids);
212  for (int i = 0; i < nslots; i++)
214  CatalogCloseIndexes(indstate);
215  table_close(pg_enum, RowExclusiveLock);
216 }
#define Min(x, y)
Definition: c.h:958
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1639
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1341
const TupleTableSlotOps TTSOpsHeapTuple
Definition: execTuples.c:85
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1325
void CatalogTuplesMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot, int ntuples, CatalogIndexState indstate)
Definition: indexing.c:273
void CatalogCloseIndexes(CatalogIndexState indstate)
Definition: indexing.c:61
CatalogIndexState CatalogOpenIndexes(Relation heapRel)
Definition: indexing.c:43
#define MAX_CATALOG_MULTI_INSERT_BYTES
Definition: indexing.h:33
void * palloc0(Size size)
Definition: mcxt.c:1347
int oid_cmp(const void *p1, const void *p2)
Definition: oid.c:258
static void init_uncommitted_enum_types(void)
Definition: pg_enum.c:255
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
Datum * tts_values
Definition: tuptable.h:125
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:454
#define strVal(v)
Definition: value.h:82

References CatalogCloseIndexes(), CatalogOpenIndexes(), CatalogTuplesMultiInsertWithInfo(), ereport, errcode(), errdetail(), errmsg(), ERROR, ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecStoreVirtualTuple(), Float4GetDatum(), FormData_pg_enum, GetCurrentTransactionNestLevel(), GetNewOidWithIndex(), HASH_ENTER, hash_search(), i, init_uncommitted_enum_types(), lfirst, list_length(), MakeSingleTupleTableSlot(), MAX_CATALOG_MULTI_INSERT_BYTES, Min, NAMEDATALEN, NameGetDatum(), namestrcpy(), ObjectIdGetDatum(), oid_cmp(), palloc(), palloc0(), pfree(), qsort, RelationGetDescr, RowExclusiveLock, strVal, table_close(), table_open(), TupleTableSlot::tts_values, TTSOpsHeapTuple, and uncommitted_enum_types.

Referenced by DefineEnum().

◆ EnumValuesDelete()

void EnumValuesDelete ( Oid  enumTypeOid)

Definition at line 224 of file pg_enum.c.

225 {
226  Relation pg_enum;
227  ScanKeyData key[1];
228  SysScanDesc scan;
229  HeapTuple tup;
230 
231  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
232 
233  ScanKeyInit(&key[0],
234  Anum_pg_enum_enumtypid,
235  BTEqualStrategyNumber, F_OIDEQ,
236  ObjectIdGetDatum(enumTypeOid));
237 
238  scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true,
239  NULL, 1, key);
240 
241  while (HeapTupleIsValid(tup = systable_getnext(scan)))
242  {
243  CatalogTupleDelete(pg_enum, &tup->t_self);
244  }
245 
246  systable_endscan(scan);
247 
248  table_close(pg_enum, RowExclusiveLock);
249 }
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:606
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:513
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:387
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:365
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define BTEqualStrategyNumber
Definition: stratnum.h:31
ItemPointerData t_self
Definition: htup.h:65

References BTEqualStrategyNumber, CatalogTupleDelete(), HeapTupleIsValid, sort-test::key, ObjectIdGetDatum(), RowExclusiveLock, ScanKeyInit(), systable_beginscan(), systable_endscan(), systable_getnext(), HeapTupleData::t_self, table_close(), and table_open().

Referenced by RemoveTypeById().

◆ EstimateUncommittedEnumsSpace()

Size EstimateUncommittedEnumsSpace ( void  )

Definition at line 813 of file pg_enum.c.

814 {
815  size_t entries = 0;
816 
821 
822  /* Add two for the terminators. */
823  return sizeof(Oid) * (entries + 2);
824 }
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1341

References hash_get_num_entries(), uncommitted_enum_types, and uncommitted_enum_values.

Referenced by InitializeParallelDSM(), and SerializeUncommittedEnums().

◆ init_uncommitted_enum_types()

static void init_uncommitted_enum_types ( void  )
static

Definition at line 255 of file pg_enum.c.

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,
266 }
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:352
#define HASH_CONTEXT
Definition: hsearch.h:102
#define HASH_ELEM
Definition: hsearch.h:95
#define HASH_BLOBS
Definition: hsearch.h:97
MemoryContext TopTransactionContext
Definition: mcxt.c:154
Size keysize
Definition: hsearch.h:75
Size entrysize
Definition: hsearch.h:76
MemoryContext hcxt
Definition: hsearch.h:86

References HASHCTL::entrysize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASHCTL::hcxt, HASHCTL::keysize, TopTransactionContext, and uncommitted_enum_types.

Referenced by EnumValuesCreate(), and RestoreUncommittedEnums().

◆ init_uncommitted_enum_values()

static void init_uncommitted_enum_values ( void  )
static

Definition at line 272 of file pg_enum.c.

273 {
274  HASHCTL hash_ctl;
275 
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",
280  32,
281  &hash_ctl,
283 }

References HASHCTL::entrysize, HASH_BLOBS, HASH_CONTEXT, hash_create(), HASH_ELEM, HASHCTL::hcxt, HASHCTL::keysize, TopTransactionContext, and uncommitted_enum_values.

Referenced by AddEnumLabel(), and RestoreUncommittedEnums().

◆ RenameEnumLabel()

void RenameEnumLabel ( Oid  enumTypeOid,
const char *  oldVal,
const char *  newVal 
)

Definition at line 607 of file pg_enum.c.

610 {
611  Relation pg_enum;
612  HeapTuple enum_tup;
613  Form_pg_enum en;
614  CatCList *list;
615  int nelems;
616  HeapTuple old_tup;
617  bool found_new;
618  int i;
619 
620  /* check length of new label is ok */
621  if (strlen(newVal) > (NAMEDATALEN - 1))
622  ereport(ERROR,
623  (errcode(ERRCODE_INVALID_NAME),
624  errmsg("invalid enum label \"%s\"", newVal),
625  errdetail("Labels must be %d bytes or less.",
626  NAMEDATALEN - 1)));
627 
628  /*
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.
634  */
635  LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
636 
637  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
638 
639  /* Get the list of existing members of the enum */
640  list = SearchSysCacheList1(ENUMTYPOIDNAME,
641  ObjectIdGetDatum(enumTypeOid));
642  nelems = list->n_members;
643 
644  /*
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.)
648  */
649  old_tup = NULL;
650  found_new = false;
651  for (i = 0; i < nelems; i++)
652  {
653  enum_tup = &(list->members[i]->tuple);
654  en = (Form_pg_enum) GETSTRUCT(enum_tup);
655  if (strcmp(NameStr(en->enumlabel), oldVal) == 0)
656  old_tup = enum_tup;
657  if (strcmp(NameStr(en->enumlabel), newVal) == 0)
658  found_new = true;
659  }
660  if (!old_tup)
661  ereport(ERROR,
662  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
663  errmsg("\"%s\" is not an existing enum label",
664  oldVal)));
665  if (found_new)
666  ereport(ERROR,
668  errmsg("enum label \"%s\" already exists",
669  newVal)));
670 
671  /* OK, make a writable copy of old tuple */
672  enum_tup = heap_copytuple(old_tup);
673  en = (Form_pg_enum) GETSTRUCT(enum_tup);
674 
676 
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);
681 
682  table_close(pg_enum, RowExclusiveLock);
683 }
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:776
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:313

References CatalogTupleUpdate(), ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errmsg(), ERROR, ExclusiveLock, GETSTRUCT, heap_copytuple(), heap_freetuple(), i, sort-test::list, LockDatabaseObject(), NAMEDATALEN, NameStr, namestrcpy(), ObjectIdGetDatum(), ReleaseCatCacheList(), RowExclusiveLock, SearchSysCacheList1, HeapTupleData::t_self, table_close(), and table_open().

Referenced by AlterEnum().

◆ RenumberEnumType()

static void RenumberEnumType ( Relation  pg_enum,
HeapTuple existing,
int  nelems 
)
static

Definition at line 761 of file pg_enum.c.

762 {
763  int i;
764 
765  /*
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.
769  */
770  for (i = nelems - 1; i >= 0; i--)
771  {
772  HeapTuple newtup;
773  Form_pg_enum en;
774  float4 newsortorder;
775 
776  newtup = heap_copytuple(existing[i]);
777  en = (Form_pg_enum) GETSTRUCT(newtup);
778 
779  newsortorder = i + 1;
780  if (en->enumsortorder != newsortorder)
781  {
782  en->enumsortorder = newsortorder;
783 
784  CatalogTupleUpdate(pg_enum, &newtup->t_self, newtup);
785  }
786 
787  heap_freetuple(newtup);
788  }
789 
790  /* Make the updates visible */
792 }
void CommandCounterIncrement(void)
Definition: xact.c:1099

References CatalogTupleUpdate(), CommandCounterIncrement(), GETSTRUCT, heap_copytuple(), heap_freetuple(), i, and HeapTupleData::t_self.

Referenced by AddEnumLabel().

◆ RestoreUncommittedEnums()

void RestoreUncommittedEnums ( void *  space)

Definition at line 873 of file pg_enum.c.

874 {
875  Oid *serialized = (Oid *) space;
876 
879 
880  /*
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.
884  */
885  if (OidIsValid(*serialized))
886  {
887  /* Read all the types into a new hash table. */
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. */
900  do
901  {
902  (void) hash_search(uncommitted_enum_values, serialized++,
903  HASH_ENTER, NULL);
904  } while (OidIsValid(*serialized));
905  }
906 }
#define Assert(condition)
Definition: c.h:812

References Assert, HASH_ENTER, hash_search(), init_uncommitted_enum_types(), init_uncommitted_enum_values(), OidIsValid, uncommitted_enum_types, and uncommitted_enum_values.

Referenced by ParallelWorkerMain().

◆ SerializeUncommittedEnums()

void SerializeUncommittedEnums ( void *  space,
Size  size 
)

Definition at line 827 of file pg_enum.c.

828 {
829  Oid *serialized = (Oid *) space;
830 
831  /*
832  * Make sure the hash tables haven't changed in size since the caller
833  * reserved the space.
834  */
836 
837  /* Write out all the OIDs from the types hash table, if there is one. */
839  {
840  HASH_SEQ_STATUS status;
841  Oid *value;
842 
844  while ((value = (Oid *) hash_seq_search(&status)))
845  *serialized++ = *value;
846  }
847 
848  /* Write out the terminator. */
849  *serialized++ = InvalidOid;
850 
851  /* Write out all the OIDs from the values hash table, if there is one. */
853  {
854  HASH_SEQ_STATUS status;
855  Oid *value;
856 
858  while ((value = (Oid *) hash_seq_search(&status)))
859  *serialized++ = *value;
860  }
861 
862  /* Write out the terminator. */
863  *serialized++ = InvalidOid;
864 
865  /*
866  * Make sure the amount of space we actually used matches what was
867  * estimated.
868  */
869  Assert((char *) serialized == ((char *) space) + size);
870 }
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1420
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1385
static struct @160 value
Size EstimateUncommittedEnumsSpace(void)
Definition: pg_enum.c:813
static pg_noinline void Size size
Definition: slab.c:607

References Assert, EstimateUncommittedEnumsSpace(), hash_seq_init(), hash_seq_search(), InvalidOid, size, uncommitted_enum_types, uncommitted_enum_values, and value.

Referenced by InitializeParallelDSM().

◆ sort_order_cmp()

static int sort_order_cmp ( const void *  p1,
const void *  p2 
)
static

Definition at line 797 of file pg_enum.c.

798 {
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);
803 
804  if (en1->enumsortorder < en2->enumsortorder)
805  return -1;
806  else if (en1->enumsortorder > en2->enumsortorder)
807  return 1;
808  else
809  return 0;
810 }

References GETSTRUCT, and p2.

Referenced by AddEnumLabel().

Variable Documentation

◆ binary_upgrade_next_pg_enum_oid

Oid binary_upgrade_next_pg_enum_oid = InvalidOid

Definition at line 36 of file pg_enum.c.

Referenced by AddEnumLabel(), and binary_upgrade_set_next_pg_enum_oid().

◆ uncommitted_enum_types

◆ uncommitted_enum_values