#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);
}
1.7.1