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 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)
 
static void init_uncommitted_enums (void)
 
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_enums = NULL
 

Function Documentation

◆ AddEnumLabel()

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

Definition at line 237 of file pg_enum.c.

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 }
static Datum values[MAXATTR]
Definition: bootstrap.c:156
#define NameStr(name)
Definition: c.h:682
float float4
Definition: c.h:565
#define OidIsValid(objectId)
Definition: c.h:711
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:393
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1777
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:953
int errdetail(const char *fmt,...)
Definition: elog.c:1039
int errcode(int sqlerrcode)
Definition: elog.c:695
int errmsg(const char *fmt,...)
Definition: elog.c:906
#define ERROR
Definition: elog.h:35
#define NOTICE
Definition: elog.h:31
#define ereport(elevel,...)
Definition: elog.h:145
bool IsBinaryUpgrade
Definition: globals.c:114
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
@ HASH_ENTER
Definition: hsearch.h:114
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
#define GETSTRUCT(TUP)
Definition: htup_details.h:649
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:221
int i
Definition: isn.c:73
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
void pfree(void *pointer)
Definition: mcxt.c:1306
void * palloc(Size size)
Definition: mcxt.c:1199
void namestrcpy(Name name, const char *str)
Definition: name.c:233
#define NAMEDATALEN
static HTAB * uncommitted_enums
Definition: pg_enum.c:48
static void init_uncommitted_enums(void)
Definition: pg_enum.c:217
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
static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
Definition: pg_enum.c:672
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:44
#define qsort(a, b, c, d)
Definition: port.h:445
static Datum Float4GetDatum(float4 X)
Definition: postgres.h:823
uintptr_t Datum
Definition: postgres.h:412
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:600
static Datum NameGetDatum(const NameData *X)
Definition: postgres.h:721
static Datum CStringGetDatum(const char *X)
Definition: postgres.h:698
#define InvalidOid
Definition: postgres_ext.h:36
unsigned int Oid
Definition: postgres_ext.h:31
#define RelationGetDescr(relation)
Definition: rel.h:527
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:32
Definition: c.h:677
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1221
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:1184
@ ENUMTYPOIDNAME
Definition: syscache.h:58
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:215
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:126
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:40

References binary_upgrade_next_pg_enum_oid, CatalogTupleInsert(), CStringGetDatum(), ENUMTYPOIDNAME, ereport, errcode(), ERRCODE_DUPLICATE_OBJECT, errdetail(), errmsg(), ERROR, ExclusiveLock, Float4GetDatum(), GetNewOidWithIndex(), GETSTRUCT, HASH_ENTER, hash_search(), heap_form_tuple(), heap_freetuple(), HeapTupleIsValid, i, init_uncommitted_enums(), 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_enums, and values.

Referenced by AlterEnum().

◆ AtEOXact_Enum()

void AtEOXact_Enum ( void  )

Definition at line 638 of file pg_enum.c.

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 }

References uncommitted_enums.

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

◆ EnumUncommitted()

bool EnumUncommitted ( Oid  enum_id)

Definition at line 620 of file pg_enum.c.

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

References HASH_FIND, hash_search(), and uncommitted_enums.

Referenced by check_safe_enum_use().

◆ EnumValuesCreate()

void EnumValuesCreate ( Oid  enumTypeOid,
List vals 
)

Definition at line 61 of file pg_enum.c.

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 }
#define Min(x, y)
Definition: c.h:937
TupleTableSlot * ExecStoreVirtualTuple(TupleTableSlot *slot)
Definition: execTuples.c:1552
void ExecDropSingleTupleTableSlot(TupleTableSlot *slot)
Definition: execTuples.c:1254
const TupleTableSlotOps TTSOpsHeapTuple
Definition: execTuples.c:84
TupleTableSlot * MakeSingleTupleTableSlot(TupleDesc tupdesc, const TupleTableSlotOps *tts_ops)
Definition: execTuples.c:1238
void CatalogTuplesMultiInsertWithInfo(Relation heapRel, TupleTableSlot **slot, int ntuples, CatalogIndexState indstate)
Definition: indexing.c:261
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:1230
int oid_cmp(const void *p1, const void *p2)
Definition: oid.c:336
FormData_pg_enum
Definition: pg_enum.h:37
#define lfirst(lc)
Definition: pg_list.h:170
static int list_length(const List *l)
Definition: pg_list.h:150
Datum * tts_values
Definition: tuptable.h:126
static TupleTableSlot * ExecClearTuple(TupleTableSlot *slot)
Definition: tuptable.h:433
#define strVal(v)
Definition: value.h:82

References CatalogCloseIndexes(), CatalogOpenIndexes(), CatalogTuplesMultiInsertWithInfo(), ereport, errcode(), errdetail(), errmsg(), ERROR, ExecClearTuple(), ExecDropSingleTupleTableSlot(), ExecStoreVirtualTuple(), Float4GetDatum(), FormData_pg_enum, GetNewOidWithIndex(), i, 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, and TTSOpsHeapTuple.

Referenced by DefineEnum().

◆ EnumValuesDelete()

void EnumValuesDelete ( Oid  enumTypeOid)

Definition at line 186 of file pg_enum.c.

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 }
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
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:350
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 724 of file pg_enum.c.

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 }
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1377

References hash_get_num_entries(), and uncommitted_enums.

Referenced by InitializeParallelDSM(), and SerializeUncommittedEnums().

◆ init_uncommitted_enums()

static void init_uncommitted_enums ( void  )
static

Definition at line 217 of file pg_enum.c.

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 }
HTAB * hash_create(const char *tabname, long nelem, const HASHCTL *info, int flags)
Definition: dynahash.c:350
#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:135
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_enums.

Referenced by AddEnumLabel(), and RestoreUncommittedEnums().

◆ RenameEnumLabel()

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

Definition at line 537 of file pg_enum.c.

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 }
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:301

References CatalogTupleUpdate(), ENUMTYPOIDNAME, 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 672 of file pg_enum.c.

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 }
void CommandCounterIncrement(void)
Definition: xact.c:1077

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

Referenced by AddEnumLabel().

◆ RestoreUncommittedEnums()

void RestoreUncommittedEnums ( void *  space)

Definition at line 770 of file pg_enum.c.

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 }
Assert(fmt[strlen(fmt) - 1] !='\n')

References Assert(), HASH_ENTER, hash_search(), init_uncommitted_enums(), OidIsValid, and uncommitted_enums.

Referenced by ParallelWorkerMain().

◆ SerializeUncommittedEnums()

void SerializeUncommittedEnums ( void *  space,
Size  size 
)

Definition at line 738 of file pg_enum.c.

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  {
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 }
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
static struct @143 value
Size EstimateUncommittedEnumsSpace(void)
Definition: pg_enum.c:724
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:225

References Assert(), EstimateUncommittedEnumsSpace(), hash_seq_init(), hash_seq_search(), InvalidOid, status(), uncommitted_enums, and value.

Referenced by InitializeParallelDSM().

◆ sort_order_cmp()

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

Definition at line 708 of file pg_enum.c.

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 }

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_enums