LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_enum.c (source / functions) Hit Total Coverage
Test: PostgreSQL Lines: 160 172 93.0 %
Date: 2017-09-29 13:40:31 Functions: 6 6 100.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*-------------------------------------------------------------------------
       2             :  *
       3             :  * pg_enum.c
       4             :  *    routines to support manipulation of the pg_enum relation
       5             :  *
       6             :  * Copyright (c) 2006-2017, PostgreSQL Global Development Group
       7             :  *
       8             :  *
       9             :  * IDENTIFICATION
      10             :  *    src/backend/catalog/pg_enum.c
      11             :  *
      12             :  *-------------------------------------------------------------------------
      13             :  */
      14             : #include "postgres.h"
      15             : 
      16             : #include "access/genam.h"
      17             : #include "access/heapam.h"
      18             : #include "access/htup_details.h"
      19             : #include "access/xact.h"
      20             : #include "catalog/binary_upgrade.h"
      21             : #include "catalog/catalog.h"
      22             : #include "catalog/indexing.h"
      23             : #include "catalog/pg_enum.h"
      24             : #include "catalog/pg_type.h"
      25             : #include "storage/lmgr.h"
      26             : #include "miscadmin.h"
      27             : #include "nodes/value.h"
      28             : #include "utils/builtins.h"
      29             : #include "utils/catcache.h"
      30             : #include "utils/fmgroids.h"
      31             : #include "utils/syscache.h"
      32             : #include "utils/tqual.h"
      33             : 
      34             : 
      35             : /* Potentially set by pg_upgrade_support functions */
      36             : Oid         binary_upgrade_next_pg_enum_oid = InvalidOid;
      37             : 
      38             : static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems);
      39             : static int  sort_order_cmp(const void *p1, const void *p2);
      40             : 
      41             : 
      42             : /*
      43             :  * EnumValuesCreate
      44             :  *      Create an entry in pg_enum for each of the supplied enum values.
      45             :  *
      46             :  * vals is a list of Value strings.
      47             :  */
      48             : void
      49          15 : EnumValuesCreate(Oid enumTypeOid, List *vals)
      50             : {
      51             :     Relation    pg_enum;
      52             :     NameData    enumlabel;
      53             :     Oid        *oids;
      54             :     int         elemno,
      55             :                 num_elems;
      56             :     Datum       values[Natts_pg_enum];
      57             :     bool        nulls[Natts_pg_enum];
      58             :     ListCell   *lc;
      59             :     HeapTuple   tup;
      60             : 
      61          15 :     num_elems = list_length(vals);
      62             : 
      63             :     /*
      64             :      * We do not bother to check the list of values for duplicates --- if you
      65             :      * have any, you'll get a less-than-friendly unique-index violation. It is
      66             :      * probably not worth trying harder.
      67             :      */
      68             : 
      69          15 :     pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
      70             : 
      71             :     /*
      72             :      * Allocate OIDs for the enum's members.
      73             :      *
      74             :      * While this method does not absolutely guarantee that we generate no
      75             :      * duplicate OIDs (since we haven't entered each oid into the table before
      76             :      * allocating the next), trouble could only occur if the OID counter wraps
      77             :      * all the way around before we finish. Which seems unlikely.
      78             :      */
      79          15 :     oids = (Oid *) palloc(num_elems * sizeof(Oid));
      80             : 
      81          59 :     for (elemno = 0; elemno < num_elems; elemno++)
      82             :     {
      83             :         /*
      84             :          * We assign even-numbered OIDs to all the new enum labels.  This
      85             :          * tells the comparison functions the OIDs are in the correct sort
      86             :          * order and can be compared directly.
      87             :          */
      88             :         Oid         new_oid;
      89             : 
      90             :         do
      91             :         {
      92          82 :             new_oid = GetNewOid(pg_enum);
      93          82 :         } while (new_oid & 1);
      94          44 :         oids[elemno] = new_oid;
      95             :     }
      96             : 
      97             :     /* sort them, just in case OID counter wrapped from high to low */
      98          15 :     qsort(oids, num_elems, sizeof(Oid), oid_cmp);
      99             : 
     100             :     /* and make the entries */
     101          15 :     memset(nulls, false, sizeof(nulls));
     102             : 
     103          15 :     elemno = 0;
     104          59 :     foreach(lc, vals)
     105             :     {
     106          44 :         char       *lab = strVal(lfirst(lc));
     107             : 
     108             :         /*
     109             :          * labels are stored in a name field, for easier syscache lookup, so
     110             :          * check the length to make sure it's within range.
     111             :          */
     112          44 :         if (strlen(lab) > (NAMEDATALEN - 1))
     113           0 :             ereport(ERROR,
     114             :                     (errcode(ERRCODE_INVALID_NAME),
     115             :                      errmsg("invalid enum label \"%s\"", lab),
     116             :                      errdetail("Labels must be %d characters or less.",
     117             :                                NAMEDATALEN - 1)));
     118             : 
     119          44 :         values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
     120          44 :         values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1);
     121          44 :         namestrcpy(&enumlabel, lab);
     122          44 :         values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
     123             : 
     124          44 :         tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
     125          44 :         HeapTupleSetOid(tup, oids[elemno]);
     126             : 
     127          44 :         CatalogTupleInsert(pg_enum, tup);
     128          44 :         heap_freetuple(tup);
     129             : 
     130          44 :         elemno++;
     131             :     }
     132             : 
     133             :     /* clean up */
     134          15 :     pfree(oids);
     135          15 :     heap_close(pg_enum, RowExclusiveLock);
     136          15 : }
     137             : 
     138             : 
     139             : /*
     140             :  * EnumValuesDelete
     141             :  *      Remove all the pg_enum entries for the specified enum type.
     142             :  */
     143             : void
     144           8 : EnumValuesDelete(Oid enumTypeOid)
     145             : {
     146             :     Relation    pg_enum;
     147             :     ScanKeyData key[1];
     148             :     SysScanDesc scan;
     149             :     HeapTuple   tup;
     150             : 
     151           8 :     pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
     152             : 
     153           8 :     ScanKeyInit(&key[0],
     154             :                 Anum_pg_enum_enumtypid,
     155             :                 BTEqualStrategyNumber, F_OIDEQ,
     156             :                 ObjectIdGetDatum(enumTypeOid));
     157             : 
     158           8 :     scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true,
     159             :                               NULL, 1, key);
     160             : 
     161          43 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
     162             :     {
     163          27 :         CatalogTupleDelete(pg_enum, &tup->t_self);
     164             :     }
     165             : 
     166           8 :     systable_endscan(scan);
     167             : 
     168           8 :     heap_close(pg_enum, RowExclusiveLock);
     169           8 : }
     170             : 
     171             : 
     172             : /*
     173             :  * AddEnumLabel
     174             :  *      Add a new label to the enum set. By default it goes at
     175             :  *      the end, but the user can choose to place it before or
     176             :  *      after any existing set member.
     177             :  */
     178             : void
     179          44 : AddEnumLabel(Oid enumTypeOid,
     180             :              const char *newVal,
     181             :              const char *neighbor,
     182             :              bool newValIsAfter,
     183             :              bool skipIfExists)
     184             : {
     185             :     Relation    pg_enum;
     186             :     Oid         newOid;
     187             :     Datum       values[Natts_pg_enum];
     188             :     bool        nulls[Natts_pg_enum];
     189             :     NameData    enumlabel;
     190             :     HeapTuple   enum_tup;
     191             :     float4      newelemorder;
     192             :     HeapTuple  *existing;
     193             :     CatCList   *list;
     194             :     int         nelems;
     195             :     int         i;
     196             : 
     197             :     /* check length of new label is ok */
     198          44 :     if (strlen(newVal) > (NAMEDATALEN - 1))
     199           1 :         ereport(ERROR,
     200             :                 (errcode(ERRCODE_INVALID_NAME),
     201             :                  errmsg("invalid enum label \"%s\"", newVal),
     202             :                  errdetail("Labels must be %d characters or less.",
     203             :                            NAMEDATALEN - 1)));
     204             : 
     205             :     /*
     206             :      * Acquire a lock on the enum type, which we won't release until commit.
     207             :      * This ensures that two backends aren't concurrently modifying the same
     208             :      * enum type.  Without that, we couldn't be sure to get a consistent view
     209             :      * of the enum members via the syscache.  Note that this does not block
     210             :      * other backends from inspecting the type; see comments for
     211             :      * RenumberEnumType.
     212             :      */
     213          43 :     LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
     214             : 
     215             :     /*
     216             :      * Check if label is already in use.  The unique index on pg_enum would
     217             :      * catch this anyway, but we prefer a friendlier error message, and
     218             :      * besides we need a check to support IF NOT EXISTS.
     219             :      */
     220          43 :     enum_tup = SearchSysCache2(ENUMTYPOIDNAME,
     221             :                                ObjectIdGetDatum(enumTypeOid),
     222             :                                CStringGetDatum(newVal));
     223          43 :     if (HeapTupleIsValid(enum_tup))
     224             :     {
     225           2 :         ReleaseSysCache(enum_tup);
     226           2 :         if (skipIfExists)
     227             :         {
     228           1 :             ereport(NOTICE,
     229             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     230             :                      errmsg("enum label \"%s\" already exists, skipping",
     231             :                             newVal)));
     232          42 :             return;
     233             :         }
     234             :         else
     235           1 :             ereport(ERROR,
     236             :                     (errcode(ERRCODE_DUPLICATE_OBJECT),
     237             :                      errmsg("enum label \"%s\" already exists",
     238             :                             newVal)));
     239             :     }
     240             : 
     241          41 :     pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
     242             : 
     243             :     /* If we have to renumber the existing members, we restart from here */
     244             : restart:
     245             : 
     246             :     /* Get the list of existing members of the enum */
     247          42 :     list = SearchSysCacheList1(ENUMTYPOIDNAME,
     248             :                                ObjectIdGetDatum(enumTypeOid));
     249          42 :     nelems = list->n_members;
     250             : 
     251             :     /* Sort the existing members by enumsortorder */
     252          42 :     existing = (HeapTuple *) palloc(nelems * sizeof(HeapTuple));
     253         609 :     for (i = 0; i < nelems; i++)
     254         567 :         existing[i] = &(list->members[i]->tuple);
     255             : 
     256          42 :     qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
     257             : 
     258          42 :     if (neighbor == NULL)
     259             :     {
     260             :         /*
     261             :          * Put the new label at the end of the list. No change to existing
     262             :          * tuples is required.
     263             :          */
     264           6 :         if (nelems > 0)
     265             :         {
     266           6 :             Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nelems - 1]);
     267             : 
     268           6 :             newelemorder = en->enumsortorder + 1;
     269             :         }
     270             :         else
     271           0 :             newelemorder = 1;
     272             :     }
     273             :     else
     274             :     {
     275             :         /* BEFORE or AFTER was specified */
     276             :         int         nbr_index;
     277             :         int         other_nbr_index;
     278             :         Form_pg_enum nbr_en;
     279             :         Form_pg_enum other_nbr_en;
     280             : 
     281             :         /* Locate the neighbor element */
     282         546 :         for (nbr_index = 0; nbr_index < nelems; nbr_index++)
     283             :         {
     284         545 :             Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
     285             : 
     286         545 :             if (strcmp(NameStr(en->enumlabel), neighbor) == 0)
     287          35 :                 break;
     288             :         }
     289          36 :         if (nbr_index >= nelems)
     290           1 :             ereport(ERROR,
     291             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     292             :                      errmsg("\"%s\" is not an existing enum label",
     293             :                             neighbor)));
     294          35 :         nbr_en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
     295             : 
     296             :         /*
     297             :          * Attempt to assign an appropriate enumsortorder value: one less than
     298             :          * the smallest member, one more than the largest member, or halfway
     299             :          * between two existing members.
     300             :          *
     301             :          * In the "halfway" case, because of the finite precision of float4,
     302             :          * we might compute a value that's actually equal to one or the other
     303             :          * of its neighbors.  In that case we renumber the existing members
     304             :          * and try again.
     305             :          */
     306          35 :         if (newValIsAfter)
     307           2 :             other_nbr_index = nbr_index + 1;
     308             :         else
     309          33 :             other_nbr_index = nbr_index - 1;
     310             : 
     311          35 :         if (other_nbr_index < 0)
     312           1 :             newelemorder = nbr_en->enumsortorder - 1;
     313          34 :         else if (other_nbr_index >= nelems)
     314           1 :             newelemorder = nbr_en->enumsortorder + 1;
     315             :         else
     316             :         {
     317             :             /*
     318             :              * The midpoint value computed here has to be rounded to float4
     319             :              * precision, else our equality comparisons against the adjacent
     320             :              * values are meaningless.  The most portable way of forcing that
     321             :              * to happen with non-C-standard-compliant compilers is to store
     322             :              * it into a volatile variable.
     323             :              */
     324             :             volatile float4 midpoint;
     325             : 
     326          33 :             other_nbr_en = (Form_pg_enum) GETSTRUCT(existing[other_nbr_index]);
     327          66 :             midpoint = (nbr_en->enumsortorder +
     328          33 :                         other_nbr_en->enumsortorder) / 2;
     329             : 
     330          65 :             if (midpoint == nbr_en->enumsortorder ||
     331          32 :                 midpoint == other_nbr_en->enumsortorder)
     332             :             {
     333           1 :                 RenumberEnumType(pg_enum, existing, nelems);
     334             :                 /* Clean up and start over */
     335           1 :                 pfree(existing);
     336           1 :                 ReleaseCatCacheList(list);
     337           1 :                 goto restart;
     338             :             }
     339             : 
     340          32 :             newelemorder = midpoint;
     341             :         }
     342             :     }
     343             : 
     344             :     /* Get a new OID for the new label */
     345          40 :     if (IsBinaryUpgrade)
     346             :     {
     347           0 :         if (!OidIsValid(binary_upgrade_next_pg_enum_oid))
     348           0 :             ereport(ERROR,
     349             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     350             :                      errmsg("pg_enum OID value not set when in binary upgrade mode")));
     351             : 
     352             :         /*
     353             :          * Use binary-upgrade override for pg_enum.oid, if supplied. During
     354             :          * binary upgrade, all pg_enum.oid's are set this way so they are
     355             :          * guaranteed to be consistent.
     356             :          */
     357           0 :         if (neighbor != NULL)
     358           0 :             ereport(ERROR,
     359             :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     360             :                      errmsg("ALTER TYPE ADD BEFORE/AFTER is incompatible with binary upgrade")));
     361             : 
     362           0 :         newOid = binary_upgrade_next_pg_enum_oid;
     363           0 :         binary_upgrade_next_pg_enum_oid = InvalidOid;
     364             :     }
     365             :     else
     366             :     {
     367             :         /*
     368             :          * Normal case: we need to allocate a new Oid for the value.
     369             :          *
     370             :          * We want to give the new element an even-numbered Oid if it's safe,
     371             :          * which is to say it compares correctly to all pre-existing even
     372             :          * numbered Oids in the enum.  Otherwise, we must give it an odd Oid.
     373             :          */
     374             :         for (;;)
     375             :         {
     376             :             bool        sorts_ok;
     377             : 
     378             :             /* Get a new OID (different from all existing pg_enum tuples) */
     379          76 :             newOid = GetNewOid(pg_enum);
     380             : 
     381             :             /*
     382             :              * Detect whether it sorts correctly relative to existing
     383             :              * even-numbered labels of the enum.  We can ignore existing
     384             :              * labels with odd Oids, since a comparison involving one of those
     385             :              * will not take the fast path anyway.
     386             :              */
     387          76 :             sorts_ok = true;
     388        1063 :             for (i = 0; i < nelems; i++)
     389             :             {
     390        1051 :                 HeapTuple   exists_tup = existing[i];
     391        1051 :                 Form_pg_enum exists_en = (Form_pg_enum) GETSTRUCT(exists_tup);
     392        1051 :                 Oid         exists_oid = HeapTupleGetOid(exists_tup);
     393             : 
     394        1051 :                 if (exists_oid & 1)
     395         885 :                     continue;   /* ignore odd Oids */
     396             : 
     397         166 :                 if (exists_en->enumsortorder < newelemorder)
     398             :                 {
     399             :                     /* should sort before */
     400         102 :                     if (exists_oid >= newOid)
     401             :                     {
     402           0 :                         sorts_ok = false;
     403           0 :                         break;
     404             :                     }
     405             :                 }
     406             :                 else
     407             :                 {
     408             :                     /* should sort after */
     409          64 :                     if (exists_oid <= newOid)
     410             :                     {
     411          64 :                         sorts_ok = false;
     412          64 :                         break;
     413             :                     }
     414             :                 }
     415             :             }
     416             : 
     417          76 :             if (sorts_ok)
     418             :             {
     419             :                 /* If it's even and sorts OK, we're done. */
     420          12 :                 if ((newOid & 1) == 0)
     421           7 :                     break;
     422             : 
     423             :                 /*
     424             :                  * If it's odd, and sorts OK, loop back to get another OID and
     425             :                  * try again.  Probably, the next available even OID will sort
     426             :                  * correctly too, so it's worth trying.
     427             :                  */
     428             :             }
     429             :             else
     430             :             {
     431             :                 /*
     432             :                  * If it's odd, and does not sort correctly, we're done.
     433             :                  * (Probably, the next available even OID would sort
     434             :                  * incorrectly too, so no point in trying again.)
     435             :                  */
     436          64 :                 if (newOid & 1)
     437          33 :                     break;
     438             : 
     439             :                 /*
     440             :                  * If it's even, and does not sort correctly, loop back to get
     441             :                  * another OID and try again.  (We *must* reject this case.)
     442             :                  */
     443             :             }
     444          36 :         }
     445             :     }
     446             : 
     447             :     /* Done with info about existing members */
     448          40 :     pfree(existing);
     449          40 :     ReleaseCatCacheList(list);
     450             : 
     451             :     /* Create the new pg_enum entry */
     452          40 :     memset(nulls, false, sizeof(nulls));
     453          40 :     values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
     454          40 :     values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(newelemorder);
     455          40 :     namestrcpy(&enumlabel, newVal);
     456          40 :     values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
     457          40 :     enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
     458          40 :     HeapTupleSetOid(enum_tup, newOid);
     459          40 :     CatalogTupleInsert(pg_enum, enum_tup);
     460          40 :     heap_freetuple(enum_tup);
     461             : 
     462          40 :     heap_close(pg_enum, RowExclusiveLock);
     463             : }
     464             : 
     465             : 
     466             : /*
     467             :  * RenameEnumLabel
     468             :  *      Rename a label in an enum set.
     469             :  */
     470             : void
     471           3 : RenameEnumLabel(Oid enumTypeOid,
     472             :                 const char *oldVal,
     473             :                 const char *newVal)
     474             : {
     475             :     Relation    pg_enum;
     476             :     HeapTuple   enum_tup;
     477             :     Form_pg_enum en;
     478             :     CatCList   *list;
     479             :     int         nelems;
     480             :     HeapTuple   old_tup;
     481             :     bool        found_new;
     482             :     int         i;
     483             : 
     484             :     /* check length of new label is ok */
     485           3 :     if (strlen(newVal) > (NAMEDATALEN - 1))
     486           0 :         ereport(ERROR,
     487             :                 (errcode(ERRCODE_INVALID_NAME),
     488             :                  errmsg("invalid enum label \"%s\"", newVal),
     489             :                  errdetail("Labels must be %d characters or less.",
     490             :                            NAMEDATALEN - 1)));
     491             : 
     492             :     /*
     493             :      * Acquire a lock on the enum type, which we won't release until commit.
     494             :      * This ensures that two backends aren't concurrently modifying the same
     495             :      * enum type.  Since we are not changing the type's sort order, this is
     496             :      * probably not really necessary, but there seems no reason not to take
     497             :      * the lock to be sure.
     498             :      */
     499           3 :     LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
     500             : 
     501           3 :     pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
     502             : 
     503             :     /* Get the list of existing members of the enum */
     504           3 :     list = SearchSysCacheList1(ENUMTYPOIDNAME,
     505             :                                ObjectIdGetDatum(enumTypeOid));
     506           3 :     nelems = list->n_members;
     507             : 
     508             :     /*
     509             :      * Locate the element to rename and check if the new label is already in
     510             :      * use.  (The unique index on pg_enum would catch that anyway, but we
     511             :      * prefer a friendlier error message.)
     512             :      */
     513           3 :     old_tup = NULL;
     514           3 :     found_new = false;
     515          21 :     for (i = 0; i < nelems; i++)
     516             :     {
     517          18 :         enum_tup = &(list->members[i]->tuple);
     518          18 :         en = (Form_pg_enum) GETSTRUCT(enum_tup);
     519          18 :         if (strcmp(NameStr(en->enumlabel), oldVal) == 0)
     520           2 :             old_tup = enum_tup;
     521          18 :         if (strcmp(NameStr(en->enumlabel), newVal) == 0)
     522           2 :             found_new = true;
     523             :     }
     524           3 :     if (!old_tup)
     525           1 :         ereport(ERROR,
     526             :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     527             :                  errmsg("\"%s\" is not an existing enum label",
     528             :                         oldVal)));
     529           2 :     if (found_new)
     530           1 :         ereport(ERROR,
     531             :                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     532             :                  errmsg("enum label \"%s\" already exists",
     533             :                         newVal)));
     534             : 
     535             :     /* OK, make a writable copy of old tuple */
     536           1 :     enum_tup = heap_copytuple(old_tup);
     537           1 :     en = (Form_pg_enum) GETSTRUCT(enum_tup);
     538             : 
     539           1 :     ReleaseCatCacheList(list);
     540             : 
     541             :     /* Update the pg_enum entry */
     542           1 :     namestrcpy(&en->enumlabel, newVal);
     543           1 :     CatalogTupleUpdate(pg_enum, &enum_tup->t_self, enum_tup);
     544           1 :     heap_freetuple(enum_tup);
     545             : 
     546           1 :     heap_close(pg_enum, RowExclusiveLock);
     547           1 : }
     548             : 
     549             : 
     550             : /*
     551             :  * RenumberEnumType
     552             :  *      Renumber existing enum elements to have sort positions 1..n.
     553             :  *
     554             :  * We avoid doing this unless absolutely necessary; in most installations
     555             :  * it will never happen.  The reason is that updating existing pg_enum
     556             :  * entries creates hazards for other backends that are concurrently reading
     557             :  * pg_enum.  Although system catalog scans now use MVCC semantics, the
     558             :  * syscache machinery might read different pg_enum entries under different
     559             :  * snapshots, so some other backend might get confused about the proper
     560             :  * ordering if a concurrent renumbering occurs.
     561             :  *
     562             :  * We therefore make the following choices:
     563             :  *
     564             :  * 1. Any code that is interested in the enumsortorder values MUST read
     565             :  * all the relevant pg_enum entries with a single MVCC snapshot, or else
     566             :  * acquire lock on the enum type to prevent concurrent execution of
     567             :  * AddEnumLabel().
     568             :  *
     569             :  * 2. Code that is not examining enumsortorder can use a syscache
     570             :  * (for example, enum_in and enum_out do so).
     571             :  */
     572             : static void
     573           1 : RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
     574             : {
     575             :     int         i;
     576             : 
     577             :     /*
     578             :      * We should only need to increase existing elements' enumsortorders,
     579             :      * never decrease them.  Therefore, work from the end backwards, to avoid
     580             :      * unwanted uniqueness violations.
     581             :      */
     582          26 :     for (i = nelems - 1; i >= 0; i--)
     583             :     {
     584             :         HeapTuple   newtup;
     585             :         Form_pg_enum en;
     586             :         float4      newsortorder;
     587             : 
     588          25 :         newtup = heap_copytuple(existing[i]);
     589          25 :         en = (Form_pg_enum) GETSTRUCT(newtup);
     590             : 
     591          25 :         newsortorder = i + 1;
     592          25 :         if (en->enumsortorder != newsortorder)
     593             :         {
     594          24 :             en->enumsortorder = newsortorder;
     595             : 
     596          24 :             CatalogTupleUpdate(pg_enum, &newtup->t_self, newtup);
     597             :         }
     598             : 
     599          25 :         heap_freetuple(newtup);
     600             :     }
     601             : 
     602             :     /* Make the updates visible */
     603           1 :     CommandCounterIncrement();
     604           1 : }
     605             : 
     606             : 
     607             : /* qsort comparison function for tuples by sort order */
     608             : static int
     609        2240 : sort_order_cmp(const void *p1, const void *p2)
     610             : {
     611        2240 :     HeapTuple   v1 = *((const HeapTuple *) p1);
     612        2240 :     HeapTuple   v2 = *((const HeapTuple *) p2);
     613        2240 :     Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
     614        2240 :     Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
     615             : 
     616        2240 :     if (en1->enumsortorder < en2->enumsortorder)
     617         937 :         return -1;
     618        1303 :     else if (en1->enumsortorder > en2->enumsortorder)
     619        1303 :         return 1;
     620             :     else
     621           0 :         return 0;
     622             : }

Generated by: LCOV version 1.11