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 "storage/lmgr.h"
#include "miscadmin.h"
#include "nodes/value.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_enum_blacklist (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 EnumBlacklisted (Oid enum_id)
 
void AtEOXact_Enum (void)
 
Size EstimateEnumBlacklistSpace (void)
 
void SerializeEnumBlacklist (void *space, Size size)
 
void RestoreEnumBlacklist (void *space)
 

Variables

Oid binary_upgrade_next_pg_enum_oid = InvalidOid
 
static HTABenum_blacklist = NULL
 

Function Documentation

◆ AddEnumLabel()

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

Definition at line 209 of file pg_enum.c.

References binary_upgrade_next_pg_enum_oid, CatalogTupleInsert(), CStringGetDatum, EnumOidIndexId, 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_enum_blacklist(), InvalidOid, IsBinaryUpgrade, sort-test::list, LockDatabaseObject(), catclist::members, catclist::n_members, NAMEDATALEN, NameGetDatum, NameStr, namestrcpy(), NOTICE, ObjectIdGetDatum, OidIsValid, palloc(), pfree(), qsort, RelationGetDescr, ReleaseCatCacheList(), ReleaseSysCache(), RenumberEnumType(), RowExclusiveLock, SearchSysCache2(), SearchSysCacheList1, sort_order_cmp(), table_close(), table_open(), catctup::tuple, and values.

Referenced by AlterEnum().

214 {
215  Relation pg_enum;
216  Oid newOid;
217  Datum values[Natts_pg_enum];
218  bool nulls[Natts_pg_enum];
219  NameData enumlabel;
220  HeapTuple enum_tup;
221  float4 newelemorder;
222  HeapTuple *existing;
223  CatCList *list;
224  int nelems;
225  int i;
226 
227  /* check length of new label is ok */
228  if (strlen(newVal) > (NAMEDATALEN - 1))
229  ereport(ERROR,
230  (errcode(ERRCODE_INVALID_NAME),
231  errmsg("invalid enum label \"%s\"", newVal),
232  errdetail("Labels must be %d characters or less.",
233  NAMEDATALEN - 1)));
234 
235  /*
236  * Acquire a lock on the enum type, which we won't release until commit.
237  * This ensures that two backends aren't concurrently modifying the same
238  * enum type. Without that, we couldn't be sure to get a consistent view
239  * of the enum members via the syscache. Note that this does not block
240  * other backends from inspecting the type; see comments for
241  * RenumberEnumType.
242  */
243  LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
244 
245  /*
246  * Check if label is already in use. The unique index on pg_enum would
247  * catch this anyway, but we prefer a friendlier error message, and
248  * besides we need a check to support IF NOT EXISTS.
249  */
250  enum_tup = SearchSysCache2(ENUMTYPOIDNAME,
251  ObjectIdGetDatum(enumTypeOid),
252  CStringGetDatum(newVal));
253  if (HeapTupleIsValid(enum_tup))
254  {
255  ReleaseSysCache(enum_tup);
256  if (skipIfExists)
257  {
258  ereport(NOTICE,
260  errmsg("enum label \"%s\" already exists, skipping",
261  newVal)));
262  return;
263  }
264  else
265  ereport(ERROR,
267  errmsg("enum label \"%s\" already exists",
268  newVal)));
269  }
270 
271  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
272 
273  /* If we have to renumber the existing members, we restart from here */
274 restart:
275 
276  /* Get the list of existing members of the enum */
278  ObjectIdGetDatum(enumTypeOid));
279  nelems = list->n_members;
280 
281  /* Sort the existing members by enumsortorder */
282  existing = (HeapTuple *) palloc(nelems * sizeof(HeapTuple));
283  for (i = 0; i < nelems; i++)
284  existing[i] = &(list->members[i]->tuple);
285 
286  qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
287 
288  if (neighbor == NULL)
289  {
290  /*
291  * Put the new label at the end of the list. No change to existing
292  * tuples is required.
293  */
294  if (nelems > 0)
295  {
296  Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nelems - 1]);
297 
298  newelemorder = en->enumsortorder + 1;
299  }
300  else
301  newelemorder = 1;
302  }
303  else
304  {
305  /* BEFORE or AFTER was specified */
306  int nbr_index;
307  int other_nbr_index;
308  Form_pg_enum nbr_en;
309  Form_pg_enum other_nbr_en;
310 
311  /* Locate the neighbor element */
312  for (nbr_index = 0; nbr_index < nelems; nbr_index++)
313  {
314  Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
315 
316  if (strcmp(NameStr(en->enumlabel), neighbor) == 0)
317  break;
318  }
319  if (nbr_index >= nelems)
320  ereport(ERROR,
321  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
322  errmsg("\"%s\" is not an existing enum label",
323  neighbor)));
324  nbr_en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
325 
326  /*
327  * Attempt to assign an appropriate enumsortorder value: one less than
328  * the smallest member, one more than the largest member, or halfway
329  * between two existing members.
330  *
331  * In the "halfway" case, because of the finite precision of float4,
332  * we might compute a value that's actually equal to one or the other
333  * of its neighbors. In that case we renumber the existing members
334  * and try again.
335  */
336  if (newValIsAfter)
337  other_nbr_index = nbr_index + 1;
338  else
339  other_nbr_index = nbr_index - 1;
340 
341  if (other_nbr_index < 0)
342  newelemorder = nbr_en->enumsortorder - 1;
343  else if (other_nbr_index >= nelems)
344  newelemorder = nbr_en->enumsortorder + 1;
345  else
346  {
347  /*
348  * The midpoint value computed here has to be rounded to float4
349  * precision, else our equality comparisons against the adjacent
350  * values are meaningless. The most portable way of forcing that
351  * to happen with non-C-standard-compliant compilers is to store
352  * it into a volatile variable.
353  */
354  volatile float4 midpoint;
355 
356  other_nbr_en = (Form_pg_enum) GETSTRUCT(existing[other_nbr_index]);
357  midpoint = (nbr_en->enumsortorder +
358  other_nbr_en->enumsortorder) / 2;
359 
360  if (midpoint == nbr_en->enumsortorder ||
361  midpoint == other_nbr_en->enumsortorder)
362  {
363  RenumberEnumType(pg_enum, existing, nelems);
364  /* Clean up and start over */
365  pfree(existing);
366  ReleaseCatCacheList(list);
367  goto restart;
368  }
369 
370  newelemorder = midpoint;
371  }
372  }
373 
374  /* Get a new OID for the new label */
375  if (IsBinaryUpgrade)
376  {
378  ereport(ERROR,
379  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
380  errmsg("pg_enum OID value not set when in binary upgrade mode")));
381 
382  /*
383  * Use binary-upgrade override for pg_enum.oid, if supplied. During
384  * binary upgrade, all pg_enum.oid's are set this way so they are
385  * guaranteed to be consistent.
386  */
387  if (neighbor != NULL)
388  ereport(ERROR,
389  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
390  errmsg("ALTER TYPE ADD BEFORE/AFTER is incompatible with binary upgrade")));
391 
394  }
395  else
396  {
397  /*
398  * Normal case: we need to allocate a new Oid for the value.
399  *
400  * We want to give the new element an even-numbered Oid if it's safe,
401  * which is to say it compares correctly to all pre-existing even
402  * numbered Oids in the enum. Otherwise, we must give it an odd Oid.
403  */
404  for (;;)
405  {
406  bool sorts_ok;
407 
408  /* Get a new OID (different from all existing pg_enum tuples) */
409  newOid = GetNewOidWithIndex(pg_enum, EnumOidIndexId,
410  Anum_pg_enum_oid);
411 
412  /*
413  * Detect whether it sorts correctly relative to existing
414  * even-numbered labels of the enum. We can ignore existing
415  * labels with odd Oids, since a comparison involving one of those
416  * will not take the fast path anyway.
417  */
418  sorts_ok = true;
419  for (i = 0; i < nelems; i++)
420  {
421  HeapTuple exists_tup = existing[i];
422  Form_pg_enum exists_en = (Form_pg_enum) GETSTRUCT(exists_tup);
423  Oid exists_oid = exists_en->oid;
424 
425  if (exists_oid & 1)
426  continue; /* ignore odd Oids */
427 
428  if (exists_en->enumsortorder < newelemorder)
429  {
430  /* should sort before */
431  if (exists_oid >= newOid)
432  {
433  sorts_ok = false;
434  break;
435  }
436  }
437  else
438  {
439  /* should sort after */
440  if (exists_oid <= newOid)
441  {
442  sorts_ok = false;
443  break;
444  }
445  }
446  }
447 
448  if (sorts_ok)
449  {
450  /* If it's even and sorts OK, we're done. */
451  if ((newOid & 1) == 0)
452  break;
453 
454  /*
455  * If it's odd, and sorts OK, loop back to get another OID and
456  * try again. Probably, the next available even OID will sort
457  * correctly too, so it's worth trying.
458  */
459  }
460  else
461  {
462  /*
463  * If it's odd, and does not sort correctly, we're done.
464  * (Probably, the next available even OID would sort
465  * incorrectly too, so no point in trying again.)
466  */
467  if (newOid & 1)
468  break;
469 
470  /*
471  * If it's even, and does not sort correctly, loop back to get
472  * another OID and try again. (We *must* reject this case.)
473  */
474  }
475  }
476  }
477 
478  /* Done with info about existing members */
479  pfree(existing);
480  ReleaseCatCacheList(list);
481 
482  /* Create the new pg_enum entry */
483  memset(nulls, false, sizeof(nulls));
484  values[Anum_pg_enum_oid - 1] = ObjectIdGetDatum(newOid);
485  values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
486  values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(newelemorder);
487  namestrcpy(&enumlabel, newVal);
488  values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
489  enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
490  CatalogTupleInsert(pg_enum, enum_tup);
491  heap_freetuple(enum_tup);
492 
493  table_close(pg_enum, RowExclusiveLock);
494 
495  /* Set up the blacklist hash if not already done in this transaction */
496  if (enum_blacklist == NULL)
498 
499  /* Add the new value to the blacklist */
500  (void) hash_search(enum_blacklist, &newOid, HASH_ENTER, NULL);
501 }
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:323
static int sort_order_cmp(const void *p1, const void *p2)
Definition: pg_enum.c:680
#define NameGetDatum(X)
Definition: postgres.h:595
int n_members
Definition: catcache.h:176
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
#define RelationGetDescr(relation)
Definition: rel.h:442
#define ExclusiveLock
Definition: lockdefs.h:44
int errcode(int sqlerrcode)
Definition: elog.c:570
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
int namestrcpy(Name name, const char *str)
Definition: name.c:250
#define OidIsValid(objectId)
Definition: c.h:638
bool IsBinaryUpgrade
Definition: globals.c:110
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1782
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
#define NAMEDATALEN
void pfree(void *pointer)
Definition: mcxt.c:1031
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
Datum Float4GetDatum(float4 X)
Definition: fmgr.c:1711
Definition: c.h:603
Oid binary_upgrade_next_pg_enum_oid
Definition: pg_enum.c:37
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:860
#define CStringGetDatum(X)
Definition: postgres.h:578
void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode)
Definition: lmgr.c:963
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:44
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:210
#define ereport(elevel, rest)
Definition: elog.h:141
float float4
Definition: c.h:490
uintptr_t Datum
Definition: postgres.h:367
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:1172
#define EnumOidIndexId
Definition: indexing.h:159
static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
Definition: pg_enum.c:644
#define InvalidOid
Definition: postgres_ext.h:36
#define NOTICE
Definition: elog.h:37
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
Definition: syscache.c:1135
static HTAB * enum_blacklist
Definition: pg_enum.c:48
static Datum values[MAXATTR]
Definition: bootstrap.c:167
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:784
int i
#define NameStr(name)
Definition: c.h:609
HeapTupleData tuple
Definition: catcache.h:121
#define qsort(a, b, c, d)
Definition: port.h:491
static void init_enum_blacklist(void)
Definition: pg_enum.c:188
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:33
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:183

◆ AtEOXact_Enum()

void AtEOXact_Enum ( void  )

Definition at line 610 of file pg_enum.c.

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

611 {
612  /*
613  * Reset the blacklist table, as all our enum values are now committed.
614  * The memory will go away automatically when TopTransactionContext is
615  * freed; it's sufficient to clear our pointer.
616  */
617  enum_blacklist = NULL;
618 }
static HTAB * enum_blacklist
Definition: pg_enum.c:48

◆ EnumBlacklisted()

bool EnumBlacklisted ( Oid  enum_id)

Definition at line 592 of file pg_enum.c.

References HASH_FIND, and hash_search().

Referenced by check_safe_enum_use().

593 {
594  bool found;
595 
596  /* If we've made no blacklist table, all values are safe */
597  if (enum_blacklist == NULL)
598  return false;
599 
600  /* Else, is it in the table? */
601  (void) hash_search(enum_blacklist, &enum_id, HASH_FIND, &found);
602  return found;
603 }
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
static HTAB * enum_blacklist
Definition: pg_enum.c:48

◆ EnumValuesCreate()

void EnumValuesCreate ( Oid  enumTypeOid,
List vals 
)

Definition at line 61 of file pg_enum.c.

References CatalogTupleInsert(), EnumOidIndexId, ereport, errcode(), errdetail(), errmsg(), ERROR, Float4GetDatum(), GetNewOidWithIndex(), heap_form_tuple(), heap_freetuple(), lfirst, list_length(), NAMEDATALEN, NameGetDatum, namestrcpy(), ObjectIdGetDatum, oid_cmp(), palloc(), pfree(), qsort, RelationGetDescr, RowExclusiveLock, strVal, table_close(), table_open(), and values.

Referenced by DefineEnum().

62 {
63  Relation pg_enum;
64  NameData enumlabel;
65  Oid *oids;
66  int elemno,
67  num_elems;
68  Datum values[Natts_pg_enum];
69  bool nulls[Natts_pg_enum];
70  ListCell *lc;
71  HeapTuple tup;
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  memset(nulls, false, sizeof(nulls));
115 
116  elemno = 0;
117  foreach(lc, vals)
118  {
119  char *lab = strVal(lfirst(lc));
120 
121  /*
122  * labels are stored in a name field, for easier syscache lookup, so
123  * check the length to make sure it's within range.
124  */
125  if (strlen(lab) > (NAMEDATALEN - 1))
126  ereport(ERROR,
127  (errcode(ERRCODE_INVALID_NAME),
128  errmsg("invalid enum label \"%s\"", lab),
129  errdetail("Labels must be %d characters or less.",
130  NAMEDATALEN - 1)));
131 
132  values[Anum_pg_enum_oid - 1] = ObjectIdGetDatum(oids[elemno]);
133  values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
134  values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1);
135  namestrcpy(&enumlabel, lab);
136  values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
137 
138  tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
139 
140  CatalogTupleInsert(pg_enum, tup);
141  heap_freetuple(tup);
142 
143  elemno++;
144  }
145 
146  /* clean up */
147  pfree(oids);
148  table_close(pg_enum, RowExclusiveLock);
149 }
Oid GetNewOidWithIndex(Relation relation, Oid indexId, AttrNumber oidcolumn)
Definition: catalog.c:323
#define NameGetDatum(X)
Definition: postgres.h:595
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define RelationGetDescr(relation)
Definition: rel.h:442
#define strVal(v)
Definition: value.h:54
int errcode(int sqlerrcode)
Definition: elog.c:570
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
unsigned int Oid
Definition: postgres_ext.h:31
int namestrcpy(Name name, const char *str)
Definition: name.c:250
#define NAMEDATALEN
void pfree(void *pointer)
Definition: mcxt.c:1031
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
Datum Float4GetDatum(float4 X)
Definition: fmgr.c:1711
Definition: c.h:603
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:860
#define ereport(elevel, rest)
Definition: elog.h:141
uintptr_t Datum
Definition: postgres.h:367
#define EnumOidIndexId
Definition: indexing.h:159
#define lfirst(lc)
Definition: pg_list.h:190
int oid_cmp(const void *p1, const void *p2)
Definition: oid.c:336
static int list_length(const List *l)
Definition: pg_list.h:169
static Datum values[MAXATTR]
Definition: bootstrap.c:167
void * palloc(Size size)
Definition: mcxt.c:924
int errmsg(const char *fmt,...)
Definition: elog.c:784
#define qsort(a, b, c, d)
Definition: port.h:491
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
void CatalogTupleInsert(Relation heapRel, HeapTuple tup)
Definition: indexing.c:183

◆ EnumValuesDelete()

void EnumValuesDelete ( Oid  enumTypeOid)

Definition at line 157 of file pg_enum.c.

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

Referenced by RemoveTypeById().

158 {
159  Relation pg_enum;
160  ScanKeyData key[1];
161  SysScanDesc scan;
162  HeapTuple tup;
163 
164  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
165 
166  ScanKeyInit(&key[0],
167  Anum_pg_enum_enumtypid,
168  BTEqualStrategyNumber, F_OIDEQ,
169  ObjectIdGetDatum(enumTypeOid));
170 
171  scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true,
172  NULL, 1, key);
173 
174  while (HeapTupleIsValid(tup = systable_getnext(scan)))
175  {
176  CatalogTupleDelete(pg_enum, &tup->t_self);
177  }
178 
179  systable_endscan(scan);
180 
181  table_close(pg_enum, RowExclusiveLock);
182 }
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
void systable_endscan(SysScanDesc sysscan)
Definition: genam.c:525
void CatalogTupleDelete(Relation heapRel, ItemPointer tid)
Definition: indexing.c:269
#define EnumTypIdLabelIndexId
Definition: indexing.h:161
SysScanDesc systable_beginscan(Relation heapRelation, Oid indexId, bool indexOK, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:352
HeapTuple systable_getnext(SysScanDesc sysscan)
Definition: genam.c:444
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
ItemPointerData t_self
Definition: htup.h:65
#define RowExclusiveLock
Definition: lockdefs.h:38
#define HeapTupleIsValid(tuple)
Definition: htup.h:78
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define BTEqualStrategyNumber
Definition: stratnum.h:31

◆ EstimateEnumBlacklistSpace()

Size EstimateEnumBlacklistSpace ( void  )

Definition at line 696 of file pg_enum.c.

References hash_get_num_entries().

Referenced by InitializeParallelDSM(), and SerializeEnumBlacklist().

697 {
698  size_t entries;
699 
700  if (enum_blacklist)
702  else
703  entries = 0;
704 
705  /* Add one for the terminator. */
706  return sizeof(Oid) * (entries + 1);
707 }
long hash_get_num_entries(HTAB *hashp)
Definition: dynahash.c:1335
unsigned int Oid
Definition: postgres_ext.h:31
static HTAB * enum_blacklist
Definition: pg_enum.c:48

◆ init_enum_blacklist()

static void init_enum_blacklist ( void  )
static

Definition at line 188 of file pg_enum.c.

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

Referenced by AddEnumLabel(), and RestoreEnumBlacklist().

189 {
190  HASHCTL hash_ctl;
191 
192  memset(&hash_ctl, 0, sizeof(hash_ctl));
193  hash_ctl.keysize = sizeof(Oid);
194  hash_ctl.entrysize = sizeof(Oid);
195  hash_ctl.hcxt = TopTransactionContext;
196  enum_blacklist = hash_create("Enum value blacklist",
197  32,
198  &hash_ctl,
200 }
MemoryContext TopTransactionContext
Definition: mcxt.c:49
#define HASH_CONTEXT
Definition: hsearch.h:93
#define HASH_ELEM
Definition: hsearch.h:87
MemoryContext hcxt
Definition: hsearch.h:78
Size entrysize
Definition: hsearch.h:73
unsigned int Oid
Definition: postgres_ext.h:31
#define HASH_BLOBS
Definition: hsearch.h:88
HTAB * hash_create(const char *tabname, long nelem, HASHCTL *info, int flags)
Definition: dynahash.c:316
Size keysize
Definition: hsearch.h:72
static HTAB * enum_blacklist
Definition: pg_enum.c:48

◆ RenameEnumLabel()

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

Definition at line 509 of file pg_enum.c.

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

Referenced by AlterEnum().

512 {
513  Relation pg_enum;
514  HeapTuple enum_tup;
515  Form_pg_enum en;
516  CatCList *list;
517  int nelems;
518  HeapTuple old_tup;
519  bool found_new;
520  int i;
521 
522  /* check length of new label is ok */
523  if (strlen(newVal) > (NAMEDATALEN - 1))
524  ereport(ERROR,
525  (errcode(ERRCODE_INVALID_NAME),
526  errmsg("invalid enum label \"%s\"", newVal),
527  errdetail("Labels must be %d characters or less.",
528  NAMEDATALEN - 1)));
529 
530  /*
531  * Acquire a lock on the enum type, which we won't release until commit.
532  * This ensures that two backends aren't concurrently modifying the same
533  * enum type. Since we are not changing the type's sort order, this is
534  * probably not really necessary, but there seems no reason not to take
535  * the lock to be sure.
536  */
537  LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
538 
539  pg_enum = table_open(EnumRelationId, RowExclusiveLock);
540 
541  /* Get the list of existing members of the enum */
543  ObjectIdGetDatum(enumTypeOid));
544  nelems = list->n_members;
545 
546  /*
547  * Locate the element to rename and check if the new label is already in
548  * use. (The unique index on pg_enum would catch that anyway, but we
549  * prefer a friendlier error message.)
550  */
551  old_tup = NULL;
552  found_new = false;
553  for (i = 0; i < nelems; i++)
554  {
555  enum_tup = &(list->members[i]->tuple);
556  en = (Form_pg_enum) GETSTRUCT(enum_tup);
557  if (strcmp(NameStr(en->enumlabel), oldVal) == 0)
558  old_tup = enum_tup;
559  if (strcmp(NameStr(en->enumlabel), newVal) == 0)
560  found_new = true;
561  }
562  if (!old_tup)
563  ereport(ERROR,
564  (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
565  errmsg("\"%s\" is not an existing enum label",
566  oldVal)));
567  if (found_new)
568  ereport(ERROR,
570  errmsg("enum label \"%s\" already exists",
571  newVal)));
572 
573  /* OK, make a writable copy of old tuple */
574  enum_tup = heap_copytuple(old_tup);
575  en = (Form_pg_enum) GETSTRUCT(enum_tup);
576 
577  ReleaseCatCacheList(list);
578 
579  /* Update the pg_enum entry */
580  namestrcpy(&en->enumlabel, newVal);
581  CatalogTupleUpdate(pg_enum, &enum_tup->t_self, enum_tup);
582  heap_freetuple(enum_tup);
583 
584  table_close(pg_enum, RowExclusiveLock);
585 }
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
int n_members
Definition: catcache.h:176
void table_close(Relation relation, LOCKMODE lockmode)
Definition: table.c:133
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
#define ExclusiveLock
Definition: lockdefs.h:44
int errcode(int sqlerrcode)
Definition: elog.c:570
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
int namestrcpy(Name name, const char *str)
Definition: name.c:250
void ReleaseCatCacheList(CatCList *list)
Definition: catcache.c:1782
CatCTup * members[FLEXIBLE_ARRAY_MEMBER]
Definition: catcache.h:178
#define NAMEDATALEN
#define ObjectIdGetDatum(X)
Definition: postgres.h:507
#define ERROR
Definition: elog.h:43
ItemPointerData t_self
Definition: htup.h:65
#define RowExclusiveLock
Definition: lockdefs.h:38
int errdetail(const char *fmt,...)
Definition: elog.c:860
void LockDatabaseObject(Oid classid, Oid objid, uint16 objsubid, LOCKMODE lockmode)
Definition: lmgr.c:963
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:44
#define SearchSysCacheList1(cacheId, key1)
Definition: syscache.h:210
#define ereport(elevel, rest)
Definition: elog.h:141
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:224
int errmsg(const char *fmt,...)
Definition: elog.c:784
int i
#define NameStr(name)
Definition: c.h:609
HeapTupleData tuple
Definition: catcache.h:121
Relation table_open(Oid relationId, LOCKMODE lockmode)
Definition: table.c:39
#define ERRCODE_DUPLICATE_OBJECT
Definition: streamutil.c:33

◆ RenumberEnumType()

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

Definition at line 644 of file pg_enum.c.

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

Referenced by AddEnumLabel().

645 {
646  int i;
647 
648  /*
649  * We should only need to increase existing elements' enumsortorders,
650  * never decrease them. Therefore, work from the end backwards, to avoid
651  * unwanted uniqueness violations.
652  */
653  for (i = nelems - 1; i >= 0; i--)
654  {
655  HeapTuple newtup;
656  Form_pg_enum en;
657  float4 newsortorder;
658 
659  newtup = heap_copytuple(existing[i]);
660  en = (Form_pg_enum) GETSTRUCT(newtup);
661 
662  newsortorder = i + 1;
663  if (en->enumsortorder != newsortorder)
664  {
665  en->enumsortorder = newsortorder;
666 
667  CatalogTupleUpdate(pg_enum, &newtup->t_self, newtup);
668  }
669 
670  heap_freetuple(newtup);
671  }
672 
673  /* Make the updates visible */
675 }
HeapTuple heap_copytuple(HeapTuple tuple)
Definition: heaptuple.c:680
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
void heap_freetuple(HeapTuple htup)
Definition: heaptuple.c:1338
ItemPointerData t_self
Definition: htup.h:65
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:44
float float4
Definition: c.h:490
void CommandCounterIncrement(void)
Definition: xact.c:1003
void CatalogTupleUpdate(Relation heapRel, ItemPointer otid, HeapTuple tup)
Definition: indexing.c:224
int i

◆ RestoreEnumBlacklist()

void RestoreEnumBlacklist ( void *  space)

Definition at line 742 of file pg_enum.c.

References Assert, HASH_ENTER, hash_search(), init_enum_blacklist(), and OidIsValid.

Referenced by ParallelWorkerMain().

743 {
744  Oid *serialized = (Oid *) space;
745 
747 
748  /*
749  * As a special case, if the list is empty then don't even bother to
750  * create the hash table. This is the usual case, since enum alteration
751  * is expected to be rare.
752  */
753  if (!OidIsValid(*serialized))
754  return;
755 
756  /* Read all the values into a new hash table. */
758  do
759  {
760  hash_search(enum_blacklist, serialized++, HASH_ENTER, NULL);
761  } while (OidIsValid(*serialized));
762 }
void * hash_search(HTAB *hashp, const void *keyPtr, HASHACTION action, bool *foundPtr)
Definition: dynahash.c:906
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:638
#define Assert(condition)
Definition: c.h:732
static HTAB * enum_blacklist
Definition: pg_enum.c:48
static void init_enum_blacklist(void)
Definition: pg_enum.c:188

◆ SerializeEnumBlacklist()

void SerializeEnumBlacklist ( void *  space,
Size  size 
)

Definition at line 710 of file pg_enum.c.

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

Referenced by InitializeParallelDSM().

711 {
712  Oid *serialized = (Oid *) space;
713 
714  /*
715  * Make sure the hash table hasn't changed in size since the caller
716  * reserved the space.
717  */
719 
720  /* Write out all the values from the hash table, if there is one. */
721  if (enum_blacklist)
722  {
724  Oid *value;
725 
726  hash_seq_init(&status, enum_blacklist);
727  while ((value = (Oid *) hash_seq_search(&status)))
728  *serialized++ = *value;
729  }
730 
731  /* Write out the terminator. */
732  *serialized = InvalidOid;
733 
734  /*
735  * Make sure the amount of space we actually used matches what was
736  * estimated.
737  */
738  Assert((char *) (serialized + 1) == ((char *) space) + size);
739 }
static struct @144 value
unsigned int Oid
Definition: postgres_ext.h:31
Size EstimateEnumBlacklistSpace(void)
Definition: pg_enum.c:696
#define InvalidOid
Definition: postgres_ext.h:36
#define Assert(condition)
Definition: c.h:732
void * hash_seq_search(HASH_SEQ_STATUS *status)
Definition: dynahash.c:1389
void hash_seq_init(HASH_SEQ_STATUS *status, HTAB *hashp)
Definition: dynahash.c:1379
static HTAB * enum_blacklist
Definition: pg_enum.c:48
static void static void status(const char *fmt,...) pg_attribute_printf(1
Definition: pg_regress.c:227

◆ sort_order_cmp()

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

Definition at line 680 of file pg_enum.c.

References GETSTRUCT.

Referenced by AddEnumLabel().

681 {
682  HeapTuple v1 = *((const HeapTuple *) p1);
683  HeapTuple v2 = *((const HeapTuple *) p2);
684  Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
685  Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
686 
687  if (en1->enumsortorder < en2->enumsortorder)
688  return -1;
689  else if (en1->enumsortorder > en2->enumsortorder)
690  return 1;
691  else
692  return 0;
693 }
#define GETSTRUCT(TUP)
Definition: htup_details.h:655
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:44

Variable Documentation

◆ binary_upgrade_next_pg_enum_oid

Oid binary_upgrade_next_pg_enum_oid = InvalidOid

Definition at line 37 of file pg_enum.c.

Referenced by AddEnumLabel(), and binary_upgrade_set_next_pg_enum_oid().

◆ enum_blacklist

HTAB* enum_blacklist = NULL
static

Definition at line 48 of file pg_enum.c.