Header And Logo

PostgreSQL
| The world's most advanced open source database.

Defines | Typedefs | Functions | Variables

pg_enum.h File Reference

#include "catalog/genbki.h"
#include "nodes/pg_list.h"
Include dependency graph for pg_enum.h:
This graph shows which files directly or indirectly include this file:

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_enumForm_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 Documentation

#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
#define EnumRelationId   3501
#define Natts_pg_enum   3

Definition at line 52 of file pg_enum.h.


Typedef Documentation

Definition at line 46 of file pg_enum.h.


Function Documentation

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 34 of file pg_enum.h.

{
    Oid         enumtypid;      /* OID of owning enum type */
    float4      enumsortorder;  /* sort position of this enum value */
    NameData    enumlabel;      /* text representation of enum value */
} FormData_pg_enum;

void EnumValuesCreate ( Oid  enumTypeOid,
List vals 
)

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  ) 

Variable Documentation

Definition at line 39 of file pg_enum.h.