#include "catalog/genbki.h"
#include "nodes/pg_list.h"
Go to the source code of this file.
Defines | |
#define | EnumRelationId 3501 |
#define | Natts_pg_enum 3 |
#define | Anum_pg_enum_enumtypid 1 |
#define | Anum_pg_enum_enumsortorder 2 |
#define | Anum_pg_enum_enumlabel 3 |
Typedefs | |
typedef FormData_pg_enum * | Form_pg_enum |
Functions | |
CATALOG (pg_enum, 3501) | |
void | EnumValuesCreate (Oid enumTypeOid, List *vals) |
void | EnumValuesDelete (Oid enumTypeOid) |
void | AddEnumLabel (Oid enumTypeOid, const char *newVal, const char *neighbor, bool newValIsAfter, bool skipIfExists) |
Variables | |
FormData_pg_enum |
#define Anum_pg_enum_enumlabel 3 |
Definition at line 55 of file pg_enum.h.
Referenced by AddEnumLabel(), and EnumValuesCreate().
#define Anum_pg_enum_enumsortorder 2 |
Definition at line 54 of file pg_enum.h.
Referenced by AddEnumLabel(), and EnumValuesCreate().
#define Anum_pg_enum_enumtypid 1 |
Definition at line 53 of file pg_enum.h.
Referenced by AddEnumLabel(), enum_endpoint(), enum_range_internal(), EnumValuesCreate(), EnumValuesDelete(), and load_enum_cache_data().
#define EnumRelationId 3501 |
Definition at line 32 of file pg_enum.h.
Referenced by AddEnumLabel(), enum_endpoint(), enum_range_internal(), EnumValuesCreate(), EnumValuesDelete(), and load_enum_cache_data().
typedef FormData_pg_enum* Form_pg_enum |
void AddEnumLabel | ( | Oid | enumTypeOid, | |
const char * | newVal, | |||
const char * | neighbor, | |||
bool | newValIsAfter, | |||
bool | skipIfExists | |||
) |
Definition at line 179 of file pg_enum.c.
References Anum_pg_enum_enumlabel, Anum_pg_enum_enumsortorder, Anum_pg_enum_enumtypid, binary_upgrade_next_pg_enum_oid, CatalogUpdateIndexes(), CStringGetDatum, DatumGetFloat4, EnumRelationId, ENUMTYPOIDNAME, ereport, errcode(), errdetail(), errmsg(), ERROR, ExclusiveLock, Float4GetDatum(), GetNewOid(), GETSTRUCT, heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), HeapTupleGetOid, HeapTupleIsValid, HeapTupleSetOid, i, IsBinaryUpgrade, sort-test::list, LockDatabaseObject(), catclist::members, catclist::n_members, NAMEDATALEN, NameGetDatum, NameStr, namestrcpy(), NOTICE, NULL, ObjectIdGetDatum, OidIsValid, palloc(), pfree(), qsort, RelationGetDescr, ReleaseCatCacheList(), ReleaseSysCache(), RenumberEnumType(), RowExclusiveLock, SearchSysCache2, SearchSysCacheList1, simple_heap_insert(), sort_order_cmp(), catctup::tuple, TypeRelationId, and values.
Referenced by AlterEnum().
{ Relation pg_enum; Oid newOid; Datum values[Natts_pg_enum]; bool nulls[Natts_pg_enum]; NameData enumlabel; HeapTuple enum_tup; float4 newelemorder; HeapTuple *existing; CatCList *list; int nelems; int i; /* check length of new label is ok */ if (strlen(newVal) > (NAMEDATALEN - 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid enum label \"%s\"", newVal), errdetail("Labels must be %d characters or less.", NAMEDATALEN - 1))); /* * Acquire a lock on the enum type, which we won't release until commit. * This ensures that two backends aren't concurrently modifying the same * enum type. Without that, we couldn't be sure to get a consistent view * of the enum members via the syscache. Note that this does not block * other backends from inspecting the type; see comments for * RenumberEnumType. */ LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock); /* * Check if label is already in use. The unique index on pg_enum would * catch this anyway, but we prefer a friendlier error message, and * besides we need a check to support IF NOT EXISTS. */ enum_tup = SearchSysCache2(ENUMTYPOIDNAME, ObjectIdGetDatum(enumTypeOid), CStringGetDatum(newVal)); if (HeapTupleIsValid(enum_tup)) { ReleaseSysCache(enum_tup); if (skipIfExists) { ereport(NOTICE, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("enum label \"%s\" already exists, skipping", newVal))); return; } else ereport(ERROR, (errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("enum label \"%s\" already exists", newVal))); } pg_enum = heap_open(EnumRelationId, RowExclusiveLock); /* If we have to renumber the existing members, we restart from here */ restart: /* Get the list of existing members of the enum */ list = SearchSysCacheList1(ENUMTYPOIDNAME, ObjectIdGetDatum(enumTypeOid)); nelems = list->n_members; /* Sort the existing members by enumsortorder */ existing = (HeapTuple *) palloc(nelems * sizeof(HeapTuple)); for (i = 0; i < nelems; i++) existing[i] = &(list->members[i]->tuple); qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp); if (neighbor == NULL) { /* * Put the new label at the end of the list. No change to existing * tuples is required. */ if (nelems > 0) { Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nelems - 1]); newelemorder = en->enumsortorder + 1; } else newelemorder = 1; } else { /* BEFORE or AFTER was specified */ int nbr_index; int other_nbr_index; Form_pg_enum nbr_en; Form_pg_enum other_nbr_en; /* Locate the neighbor element */ for (nbr_index = 0; nbr_index < nelems; nbr_index++) { Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]); if (strcmp(NameStr(en->enumlabel), neighbor) == 0) break; } if (nbr_index >= nelems) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("\"%s\" is not an existing enum label", neighbor))); nbr_en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]); /* * Attempt to assign an appropriate enumsortorder value: one less than * the smallest member, one more than the largest member, or halfway * between two existing members. * * In the "halfway" case, because of the finite precision of float4, * we might compute a value that's actually equal to one or the other * of its neighbors. In that case we renumber the existing members * and try again. */ if (newValIsAfter) other_nbr_index = nbr_index + 1; else other_nbr_index = nbr_index - 1; if (other_nbr_index < 0) newelemorder = nbr_en->enumsortorder - 1; else if (other_nbr_index >= nelems) newelemorder = nbr_en->enumsortorder + 1; else { other_nbr_en = (Form_pg_enum) GETSTRUCT(existing[other_nbr_index]); newelemorder = (nbr_en->enumsortorder + other_nbr_en->enumsortorder) / 2; /* * On some machines, newelemorder may be in a register that's * wider than float4. We need to force it to be rounded to float4 * precision before making the following comparisons, or we'll get * wrong results. (Such behavior violates the C standard, but * fixing the compilers is out of our reach.) */ newelemorder = DatumGetFloat4(Float4GetDatum(newelemorder)); if (newelemorder == nbr_en->enumsortorder || newelemorder == other_nbr_en->enumsortorder) { RenumberEnumType(pg_enum, existing, nelems); /* Clean up and start over */ pfree(existing); ReleaseCatCacheList(list); goto restart; } } } /* Get a new OID for the new label */ if (IsBinaryUpgrade && OidIsValid(binary_upgrade_next_pg_enum_oid)) { /* * Use binary-upgrade override for pg_enum.oid, if supplied. During * binary upgrade, all pg_enum.oid's are set this way so they are * guaranteed to be consistent. */ if (neighbor != NULL) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("ALTER TYPE ADD BEFORE/AFTER is incompatible with binary upgrade"))); newOid = binary_upgrade_next_pg_enum_oid; binary_upgrade_next_pg_enum_oid = InvalidOid; } else { /* * Normal case: we need to allocate a new Oid for the value. * * We want to give the new element an even-numbered Oid if it's safe, * which is to say it compares correctly to all pre-existing even * numbered Oids in the enum. Otherwise, we must give it an odd Oid. */ for (;;) { bool sorts_ok; /* Get a new OID (different from all existing pg_enum tuples) */ newOid = GetNewOid(pg_enum); /* * Detect whether it sorts correctly relative to existing * even-numbered labels of the enum. We can ignore existing * labels with odd Oids, since a comparison involving one of those * will not take the fast path anyway. */ sorts_ok = true; for (i = 0; i < nelems; i++) { HeapTuple exists_tup = existing[i]; Form_pg_enum exists_en = (Form_pg_enum) GETSTRUCT(exists_tup); Oid exists_oid = HeapTupleGetOid(exists_tup); if (exists_oid & 1) continue; /* ignore odd Oids */ if (exists_en->enumsortorder < newelemorder) { /* should sort before */ if (exists_oid >= newOid) { sorts_ok = false; break; } } else { /* should sort after */ if (exists_oid <= newOid) { sorts_ok = false; break; } } } if (sorts_ok) { /* If it's even and sorts OK, we're done. */ if ((newOid & 1) == 0) break; /* * If it's odd, and sorts OK, loop back to get another OID and * try again. Probably, the next available even OID will sort * correctly too, so it's worth trying. */ } else { /* * If it's odd, and does not sort correctly, we're done. * (Probably, the next available even OID would sort * incorrectly too, so no point in trying again.) */ if (newOid & 1) break; /* * If it's even, and does not sort correctly, loop back to get * another OID and try again. (We *must* reject this case.) */ } } } /* Done with info about existing members */ pfree(existing); ReleaseCatCacheList(list); /* Create the new pg_enum entry */ memset(nulls, false, sizeof(nulls)); values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(newelemorder); namestrcpy(&enumlabel, newVal); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls); HeapTupleSetOid(enum_tup, newOid); simple_heap_insert(pg_enum, enum_tup); CatalogUpdateIndexes(pg_enum, enum_tup); heap_freetuple(enum_tup); heap_close(pg_enum, RowExclusiveLock); }
CATALOG | ( | pg_enum | , | |
3501 | ||||
) |
Definition at line 48 of file pg_enum.c.
References Anum_pg_enum_enumlabel, Anum_pg_enum_enumsortorder, Anum_pg_enum_enumtypid, CatalogUpdateIndexes(), EnumRelationId, ereport, errcode(), errdetail(), errmsg(), ERROR, Float4GetDatum(), GetNewOid(), heap_close, heap_form_tuple(), heap_freetuple(), heap_open(), HeapTupleSetOid, lfirst, list_length(), NAMEDATALEN, NameGetDatum, namestrcpy(), ObjectIdGetDatum, oid_cmp(), palloc(), pfree(), qsort, RelationGetDescr, RowExclusiveLock, simple_heap_insert(), strVal, and values.
Referenced by DefineEnum().
{ Relation pg_enum; NameData enumlabel; Oid *oids; int elemno, num_elems; Datum values[Natts_pg_enum]; bool nulls[Natts_pg_enum]; ListCell *lc; HeapTuple tup; num_elems = list_length(vals); /* * We do not bother to check the list of values for duplicates --- if you * have any, you'll get a less-than-friendly unique-index violation. It is * probably not worth trying harder. */ pg_enum = heap_open(EnumRelationId, RowExclusiveLock); /* * Allocate OIDs for the enum's members. * * While this method does not absolutely guarantee that we generate no * duplicate OIDs (since we haven't entered each oid into the table before * allocating the next), trouble could only occur if the OID counter wraps * all the way around before we finish. Which seems unlikely. */ oids = (Oid *) palloc(num_elems * sizeof(Oid)); for (elemno = 0; elemno < num_elems; elemno++) { /* * We assign even-numbered OIDs to all the new enum labels. This * tells the comparison functions the OIDs are in the correct sort * order and can be compared directly. */ Oid new_oid; do { new_oid = GetNewOid(pg_enum); } while (new_oid & 1); oids[elemno] = new_oid; } /* sort them, just in case OID counter wrapped from high to low */ qsort(oids, num_elems, sizeof(Oid), oid_cmp); /* and make the entries */ memset(nulls, false, sizeof(nulls)); elemno = 0; foreach(lc, vals) { char *lab = strVal(lfirst(lc)); /* * labels are stored in a name field, for easier syscache lookup, so * check the length to make sure it's within range. */ if (strlen(lab) > (NAMEDATALEN - 1)) ereport(ERROR, (errcode(ERRCODE_INVALID_NAME), errmsg("invalid enum label \"%s\"", lab), errdetail("Labels must be %d characters or less.", NAMEDATALEN - 1))); values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1); namestrcpy(&enumlabel, lab); values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls); HeapTupleSetOid(tup, oids[elemno]); simple_heap_insert(pg_enum, tup); CatalogUpdateIndexes(pg_enum, tup); heap_freetuple(tup); elemno++; } /* clean up */ pfree(oids); heap_close(pg_enum, RowExclusiveLock); }
void EnumValuesDelete | ( | Oid | enumTypeOid | ) |
Definition at line 144 of file pg_enum.c.
References Anum_pg_enum_enumtypid, BTEqualStrategyNumber, EnumRelationId, EnumTypIdLabelIndexId, heap_close, heap_open(), HeapTupleIsValid, ObjectIdGetDatum, RowExclusiveLock, ScanKeyInit(), simple_heap_delete(), SnapshotNow, systable_beginscan(), systable_endscan(), systable_getnext(), and HeapTupleData::t_self.
Referenced by RemoveTypeById().
{ Relation pg_enum; ScanKeyData key[1]; SysScanDesc scan; HeapTuple tup; pg_enum = heap_open(EnumRelationId, RowExclusiveLock); ScanKeyInit(&key[0], Anum_pg_enum_enumtypid, BTEqualStrategyNumber, F_OIDEQ, ObjectIdGetDatum(enumTypeOid)); scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true, SnapshotNow, 1, key); while (HeapTupleIsValid(tup = systable_getnext(scan))) { simple_heap_delete(pg_enum, &tup->t_self); } systable_endscan(scan); heap_close(pg_enum, RowExclusiveLock); }