LCOV - code coverage report
Current view: top level - src/backend/catalog - pg_enum.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 88.6 % 351 311
Test Date: 2026-01-26 10:56:24 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 65.2 % 201 131

             Branch data     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-2026, 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/htup_details.h"
      18                 :             : #include "access/table.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 "miscadmin.h"
      26                 :             : #include "nodes/value.h"
      27                 :             : #include "storage/lmgr.h"
      28                 :             : #include "utils/builtins.h"
      29                 :             : #include "utils/catcache.h"
      30                 :             : #include "utils/fmgroids.h"
      31                 :             : #include "utils/hsearch.h"
      32                 :             : #include "utils/memutils.h"
      33                 :             : #include "utils/syscache.h"
      34                 :             : 
      35                 :             : /* Potentially set by pg_upgrade_support functions */
      36                 :             : Oid                     binary_upgrade_next_pg_enum_oid = InvalidOid;
      37                 :             : 
      38                 :             : /*
      39                 :             :  * We keep two transaction-lifespan hash tables, one containing the OIDs
      40                 :             :  * of enum types made in the current transaction, and one containing the
      41                 :             :  * OIDs of enum values created during the current transaction by
      42                 :             :  * AddEnumLabel (but only if their enum type is not in the first hash).
      43                 :             :  *
      44                 :             :  * We disallow using enum values in the second hash until the transaction is
      45                 :             :  * committed; otherwise, they might get into indexes where we can't clean
      46                 :             :  * them up, and then if the transaction rolls back we have a broken index.
      47                 :             :  * (See comments for check_safe_enum_use() in enum.c.)  Values created by
      48                 :             :  * EnumValuesCreate are *not* entered into the table; we assume those are
      49                 :             :  * created during CREATE TYPE, so they can't go away unless the enum type
      50                 :             :  * itself does.
      51                 :             :  *
      52                 :             :  * The motivation for treating enum values as safe if their type OID is
      53                 :             :  * in the first hash is to allow CREATE TYPE AS ENUM; ALTER TYPE ADD VALUE;
      54                 :             :  * followed by a use of the value in the same transaction.  This pattern
      55                 :             :  * is really just as safe as creating the value during CREATE TYPE.
      56                 :             :  * We need to support this because pg_dump in binary upgrade mode produces
      57                 :             :  * commands like that.  But currently we only support it when the commands
      58                 :             :  * are at the outermost transaction level, which is as much as we need for
      59                 :             :  * pg_dump.  We could track subtransaction nesting of the commands to
      60                 :             :  * analyze things more precisely, but for now we don't bother.
      61                 :             :  */
      62                 :             : static HTAB *uncommitted_enum_types = NULL;
      63                 :             : static HTAB *uncommitted_enum_values = NULL;
      64                 :             : 
      65                 :             : static void init_uncommitted_enum_types(void);
      66                 :             : static void init_uncommitted_enum_values(void);
      67                 :             : static bool EnumTypeUncommitted(Oid typ_id);
      68                 :             : static void RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems);
      69                 :             : static int      sort_order_cmp(const void *p1, const void *p2);
      70                 :             : 
      71                 :             : 
      72                 :             : /*
      73                 :             :  * EnumValuesCreate
      74                 :             :  *              Create an entry in pg_enum for each of the supplied enum values.
      75                 :             :  *
      76                 :             :  * vals is a list of String values.
      77                 :             :  *
      78                 :             :  * We assume that this is called only by CREATE TYPE AS ENUM, and that it
      79                 :             :  * will be called even if the vals list is empty.  So we can enter the
      80                 :             :  * enum type's OID into uncommitted_enum_types here, rather than needing
      81                 :             :  * another entry point to do it.
      82                 :             :  */
      83                 :             : void
      84                 :          24 : EnumValuesCreate(Oid enumTypeOid, List *vals)
      85                 :             : {
      86                 :          24 :         Relation        pg_enum;
      87                 :          24 :         Oid                *oids;
      88                 :          24 :         int                     elemno,
      89                 :             :                                 num_elems;
      90                 :          24 :         ListCell   *lc;
      91                 :          24 :         int                     slotCount = 0;
      92                 :          24 :         int                     nslots;
      93                 :          24 :         CatalogIndexState indstate;
      94                 :          24 :         TupleTableSlot **slot;
      95                 :             : 
      96                 :             :         /*
      97                 :             :          * Remember the type OID as being made in the current transaction, but not
      98                 :             :          * if we're in a subtransaction.  (We could remember the OID anyway, in
      99                 :             :          * case a subsequent ALTER ADD VALUE occurs at outer level.  But that
     100                 :             :          * usage pattern seems unlikely enough that we'd probably just be wasting
     101                 :             :          * hashtable maintenance effort.)
     102                 :             :          */
     103         [ -  + ]:          24 :         if (GetCurrentTransactionNestLevel() == 1)
     104                 :             :         {
     105         [ -  + ]:          24 :                 if (uncommitted_enum_types == NULL)
     106                 :          24 :                         init_uncommitted_enum_types();
     107                 :          24 :                 (void) hash_search(uncommitted_enum_types, &enumTypeOid,
     108                 :             :                                                    HASH_ENTER, NULL);
     109                 :          24 :         }
     110                 :             : 
     111                 :          24 :         num_elems = list_length(vals);
     112                 :             : 
     113                 :          24 :         pg_enum = table_open(EnumRelationId, RowExclusiveLock);
     114                 :             : 
     115                 :             :         /*
     116                 :             :          * Allocate OIDs for the enum's members.
     117                 :             :          *
     118                 :             :          * While this method does not absolutely guarantee that we generate no
     119                 :             :          * duplicate OIDs (since we haven't entered each oid into the table before
     120                 :             :          * allocating the next), trouble could only occur if the OID counter wraps
     121                 :             :          * all the way around before we finish. Which seems unlikely.
     122                 :             :          */
     123                 :          24 :         oids = palloc_array(Oid, num_elems);
     124                 :             : 
     125         [ +  + ]:          96 :         for (elemno = 0; elemno < num_elems; elemno++)
     126                 :             :         {
     127                 :             :                 /*
     128                 :             :                  * We assign even-numbered OIDs to all the new enum labels.  This
     129                 :             :                  * tells the comparison functions the OIDs are in the correct sort
     130                 :             :                  * order and can be compared directly.
     131                 :             :                  */
     132                 :          72 :                 Oid                     new_oid;
     133                 :             : 
     134                 :          72 :                 do
     135                 :             :                 {
     136                 :         133 :                         new_oid = GetNewOidWithIndex(pg_enum, EnumOidIndexId,
     137                 :             :                                                                                  Anum_pg_enum_oid);
     138         [ +  + ]:         133 :                 } while (new_oid & 1);
     139                 :          72 :                 oids[elemno] = new_oid;
     140                 :          72 :         }
     141                 :             : 
     142                 :             :         /* sort them, just in case OID counter wrapped from high to low */
     143                 :          24 :         qsort(oids, num_elems, sizeof(Oid), oid_cmp);
     144                 :             : 
     145                 :             :         /* and make the entries */
     146                 :          24 :         indstate = CatalogOpenIndexes(pg_enum);
     147                 :             : 
     148                 :             :         /* allocate the slots to use and initialize them */
     149         [ +  - ]:          24 :         nslots = Min(num_elems,
     150                 :             :                                  MAX_CATALOG_MULTI_INSERT_BYTES / sizeof(FormData_pg_enum));
     151                 :          24 :         slot = palloc_array(TupleTableSlot *, nslots);
     152         [ +  + ]:          96 :         for (int i = 0; i < nslots; i++)
     153                 :          72 :                 slot[i] = MakeSingleTupleTableSlot(RelationGetDescr(pg_enum),
     154                 :             :                                                                                    &TTSOpsHeapTuple);
     155                 :             : 
     156                 :          24 :         elemno = 0;
     157   [ +  -  +  +  :          95 :         foreach(lc, vals)
                   +  + ]
     158                 :             :         {
     159                 :          72 :                 char       *lab = strVal(lfirst(lc));
     160                 :          72 :                 Name            enumlabel = palloc0(NAMEDATALEN);
     161                 :          72 :                 ListCell   *lc2;
     162                 :             : 
     163                 :             :                 /*
     164                 :             :                  * labels are stored in a name field, for easier syscache lookup, so
     165                 :             :                  * check the length to make sure it's within range.
     166                 :             :                  */
     167         [ +  - ]:          72 :                 if (strlen(lab) > (NAMEDATALEN - 1))
     168   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     169                 :             :                                         (errcode(ERRCODE_INVALID_NAME),
     170                 :             :                                          errmsg("invalid enum label \"%s\"", lab),
     171                 :             :                                          errdetail("Labels must be %d bytes or less.",
     172                 :             :                                                            NAMEDATALEN - 1)));
     173                 :             : 
     174                 :             :                 /*
     175                 :             :                  * Check for duplicate labels. The unique index on pg_enum would catch
     176                 :             :                  * that anyway, but we prefer a friendlier error message.
     177                 :             :                  */
     178   [ +  -  -  +  :         251 :                 foreach(lc2, vals)
                   +  - ]
     179                 :             :                 {
     180                 :             :                         /* Only need to compare lc to earlier entries */
     181         [ +  + ]:         180 :                         if (lc2 == lc)
     182                 :          71 :                                 break;
     183                 :             : 
     184         [ +  + ]:         109 :                         if (strcmp(lab, strVal(lfirst(lc2))) == 0)
     185   [ +  -  +  - ]:           1 :                                 ereport(ERROR,
     186                 :             :                                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     187                 :             :                                                  errmsg("enum label \"%s\" used more than once",
     188                 :             :                                                                 lab)));
     189                 :         108 :                 }
     190                 :             : 
     191                 :             :                 /* OK, construct a tuple for this label */
     192                 :          71 :                 ExecClearTuple(slot[slotCount]);
     193                 :             : 
     194                 :          71 :                 memset(slot[slotCount]->tts_isnull, false,
     195                 :             :                            slot[slotCount]->tts_tupleDescriptor->natts * sizeof(bool));
     196                 :             : 
     197                 :          71 :                 slot[slotCount]->tts_values[Anum_pg_enum_oid - 1] = ObjectIdGetDatum(oids[elemno]);
     198                 :          71 :                 slot[slotCount]->tts_values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
     199                 :          71 :                 slot[slotCount]->tts_values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(elemno + 1);
     200                 :             : 
     201                 :          71 :                 namestrcpy(enumlabel, lab);
     202                 :          71 :                 slot[slotCount]->tts_values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(enumlabel);
     203                 :             : 
     204                 :          71 :                 ExecStoreVirtualTuple(slot[slotCount]);
     205                 :          71 :                 slotCount++;
     206                 :             : 
     207                 :             :                 /* if slots are full, insert a batch of tuples */
     208         [ +  + ]:          71 :                 if (slotCount == nslots)
     209                 :             :                 {
     210                 :          46 :                         CatalogTuplesMultiInsertWithInfo(pg_enum, slot, slotCount,
     211                 :          23 :                                                                                          indstate);
     212                 :          23 :                         slotCount = 0;
     213                 :          23 :                 }
     214                 :             : 
     215                 :          71 :                 elemno++;
     216                 :          71 :         }
     217                 :             : 
     218                 :             :         /* Insert any tuples left in the buffer */
     219         [ -  + ]:          23 :         if (slotCount > 0)
     220                 :           0 :                 CatalogTuplesMultiInsertWithInfo(pg_enum, slot, slotCount,
     221                 :           0 :                                                                                  indstate);
     222                 :             : 
     223                 :             :         /* clean up */
     224                 :          23 :         pfree(oids);
     225         [ +  + ]:          92 :         for (int i = 0; i < nslots; i++)
     226                 :          69 :                 ExecDropSingleTupleTableSlot(slot[i]);
     227                 :          23 :         CatalogCloseIndexes(indstate);
     228                 :          23 :         table_close(pg_enum, RowExclusiveLock);
     229                 :          23 : }
     230                 :             : 
     231                 :             : 
     232                 :             : /*
     233                 :             :  * EnumValuesDelete
     234                 :             :  *              Remove all the pg_enum entries for the specified enum type.
     235                 :             :  */
     236                 :             : void
     237                 :          12 : EnumValuesDelete(Oid enumTypeOid)
     238                 :             : {
     239                 :          12 :         Relation        pg_enum;
     240                 :          12 :         ScanKeyData key[1];
     241                 :          12 :         SysScanDesc scan;
     242                 :          12 :         HeapTuple       tup;
     243                 :             : 
     244                 :          12 :         pg_enum = table_open(EnumRelationId, RowExclusiveLock);
     245                 :             : 
     246                 :          24 :         ScanKeyInit(&key[0],
     247                 :             :                                 Anum_pg_enum_enumtypid,
     248                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
     249                 :          12 :                                 ObjectIdGetDatum(enumTypeOid));
     250                 :             : 
     251                 :          24 :         scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true,
     252                 :          12 :                                                           NULL, 1, key);
     253                 :             : 
     254         [ +  + ]:          49 :         while (HeapTupleIsValid(tup = systable_getnext(scan)))
     255                 :             :         {
     256                 :          37 :                 CatalogTupleDelete(pg_enum, &tup->t_self);
     257                 :             :         }
     258                 :             : 
     259                 :          12 :         systable_endscan(scan);
     260                 :             : 
     261                 :          12 :         table_close(pg_enum, RowExclusiveLock);
     262                 :          12 : }
     263                 :             : 
     264                 :             : /*
     265                 :             :  * Initialize the uncommitted enum types table for this transaction.
     266                 :             :  */
     267                 :             : static void
     268                 :          24 : init_uncommitted_enum_types(void)
     269                 :             : {
     270                 :          24 :         HASHCTL         hash_ctl;
     271                 :             : 
     272                 :          24 :         hash_ctl.keysize = sizeof(Oid);
     273                 :          24 :         hash_ctl.entrysize = sizeof(Oid);
     274                 :          24 :         hash_ctl.hcxt = TopTransactionContext;
     275                 :          24 :         uncommitted_enum_types = hash_create("Uncommitted enum types",
     276                 :             :                                                                                  32,
     277                 :             :                                                                                  &hash_ctl,
     278                 :             :                                                                                  HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     279                 :          24 : }
     280                 :             : 
     281                 :             : /*
     282                 :             :  * Initialize the uncommitted enum values table for this transaction.
     283                 :             :  */
     284                 :             : static void
     285                 :          38 : init_uncommitted_enum_values(void)
     286                 :             : {
     287                 :          38 :         HASHCTL         hash_ctl;
     288                 :             : 
     289                 :          38 :         hash_ctl.keysize = sizeof(Oid);
     290                 :          38 :         hash_ctl.entrysize = sizeof(Oid);
     291                 :          38 :         hash_ctl.hcxt = TopTransactionContext;
     292                 :          38 :         uncommitted_enum_values = hash_create("Uncommitted enum values",
     293                 :             :                                                                                   32,
     294                 :             :                                                                                   &hash_ctl,
     295                 :             :                                                                                   HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
     296                 :          38 : }
     297                 :             : 
     298                 :             : /*
     299                 :             :  * AddEnumLabel
     300                 :             :  *              Add a new label to the enum set. By default it goes at
     301                 :             :  *              the end, but the user can choose to place it before or
     302                 :             :  *              after any existing set member.
     303                 :             :  */
     304                 :             : void
     305                 :          44 : AddEnumLabel(Oid enumTypeOid,
     306                 :             :                          const char *newVal,
     307                 :             :                          const char *neighbor,
     308                 :             :                          bool newValIsAfter,
     309                 :             :                          bool skipIfExists)
     310                 :             : {
     311                 :          44 :         Relation        pg_enum;
     312                 :          44 :         Oid                     newOid;
     313                 :          44 :         Datum           values[Natts_pg_enum];
     314                 :          44 :         bool            nulls[Natts_pg_enum];
     315                 :          44 :         NameData        enumlabel;
     316                 :          44 :         HeapTuple       enum_tup;
     317                 :          44 :         float4          newelemorder;
     318                 :          44 :         HeapTuple  *existing;
     319                 :          44 :         CatCList   *list;
     320                 :          44 :         int                     nelems;
     321                 :          44 :         int                     i;
     322                 :             : 
     323                 :             :         /* check length of new label is ok */
     324         [ +  + ]:          44 :         if (strlen(newVal) > (NAMEDATALEN - 1))
     325   [ +  -  +  - ]:           1 :                 ereport(ERROR,
     326                 :             :                                 (errcode(ERRCODE_INVALID_NAME),
     327                 :             :                                  errmsg("invalid enum label \"%s\"", newVal),
     328                 :             :                                  errdetail("Labels must be %d bytes or less.",
     329                 :             :                                                    NAMEDATALEN - 1)));
     330                 :             : 
     331                 :             :         /*
     332                 :             :          * Acquire a lock on the enum type, which we won't release until commit.
     333                 :             :          * This ensures that two backends aren't concurrently modifying the same
     334                 :             :          * enum type.  Without that, we couldn't be sure to get a consistent view
     335                 :             :          * of the enum members via the syscache.  Note that this does not block
     336                 :             :          * other backends from inspecting the type; see comments for
     337                 :             :          * RenumberEnumType.
     338                 :             :          */
     339                 :          43 :         LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
     340                 :             : 
     341                 :             :         /*
     342                 :             :          * Check if label is already in use.  The unique index on pg_enum would
     343                 :             :          * catch this anyway, but we prefer a friendlier error message, and
     344                 :             :          * besides we need a check to support IF NOT EXISTS.
     345                 :             :          */
     346                 :          43 :         enum_tup = SearchSysCache2(ENUMTYPOIDNAME,
     347                 :          43 :                                                            ObjectIdGetDatum(enumTypeOid),
     348                 :          43 :                                                            CStringGetDatum(newVal));
     349         [ +  + ]:          43 :         if (HeapTupleIsValid(enum_tup))
     350                 :             :         {
     351                 :           2 :                 ReleaseSysCache(enum_tup);
     352         [ +  + ]:           2 :                 if (skipIfExists)
     353                 :             :                 {
     354   [ -  +  +  - ]:           1 :                         ereport(NOTICE,
     355                 :             :                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
     356                 :             :                                          errmsg("enum label \"%s\" already exists, skipping",
     357                 :             :                                                         newVal)));
     358                 :           1 :                         return;
     359                 :             :                 }
     360                 :             :                 else
     361   [ +  -  +  - ]:           1 :                         ereport(ERROR,
     362                 :             :                                         (errcode(ERRCODE_DUPLICATE_OBJECT),
     363                 :             :                                          errmsg("enum label \"%s\" already exists",
     364                 :             :                                                         newVal)));
     365                 :           0 :         }
     366                 :             : 
     367                 :          41 :         pg_enum = table_open(EnumRelationId, RowExclusiveLock);
     368                 :             : 
     369                 :             :         /* If we have to renumber the existing members, we restart from here */
     370                 :             : restart:
     371                 :             : 
     372                 :             :         /* Get the list of existing members of the enum */
     373                 :          42 :         list = SearchSysCacheList1(ENUMTYPOIDNAME,
     374                 :             :                                                            ObjectIdGetDatum(enumTypeOid));
     375                 :          42 :         nelems = list->n_members;
     376                 :             : 
     377                 :             :         /* Sort the existing members by enumsortorder */
     378                 :          42 :         existing = palloc_array(HeapTuple, nelems);
     379         [ +  + ]:         609 :         for (i = 0; i < nelems; i++)
     380                 :         567 :                 existing[i] = &(list->members[i]->tuple);
     381                 :             : 
     382                 :          42 :         qsort(existing, nelems, sizeof(HeapTuple), sort_order_cmp);
     383                 :             : 
     384         [ +  + ]:          42 :         if (neighbor == NULL)
     385                 :             :         {
     386                 :             :                 /*
     387                 :             :                  * Put the new label at the end of the list. No change to existing
     388                 :             :                  * tuples is required.
     389                 :             :                  */
     390         [ +  - ]:           6 :                 if (nelems > 0)
     391                 :             :                 {
     392                 :           6 :                         Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nelems - 1]);
     393                 :             : 
     394                 :           6 :                         newelemorder = en->enumsortorder + 1;
     395                 :           6 :                 }
     396                 :             :                 else
     397                 :           0 :                         newelemorder = 1;
     398                 :           6 :         }
     399                 :             :         else
     400                 :             :         {
     401                 :             :                 /* BEFORE or AFTER was specified */
     402                 :          36 :                 int                     nbr_index;
     403                 :          36 :                 int                     other_nbr_index;
     404                 :          36 :                 Form_pg_enum nbr_en;
     405                 :          36 :                 Form_pg_enum other_nbr_en;
     406                 :             : 
     407                 :             :                 /* Locate the neighbor element */
     408         [ +  + ]:         546 :                 for (nbr_index = 0; nbr_index < nelems; nbr_index++)
     409                 :             :                 {
     410                 :         545 :                         Form_pg_enum en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
     411                 :             : 
     412         [ +  + ]:         545 :                         if (strcmp(NameStr(en->enumlabel), neighbor) == 0)
     413                 :          35 :                                 break;
     414         [ +  + ]:         545 :                 }
     415         [ +  + ]:          36 :                 if (nbr_index >= nelems)
     416   [ +  -  +  - ]:           1 :                         ereport(ERROR,
     417                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     418                 :             :                                          errmsg("\"%s\" is not an existing enum label",
     419                 :             :                                                         neighbor)));
     420                 :          35 :                 nbr_en = (Form_pg_enum) GETSTRUCT(existing[nbr_index]);
     421                 :             : 
     422                 :             :                 /*
     423                 :             :                  * Attempt to assign an appropriate enumsortorder value: one less than
     424                 :             :                  * the smallest member, one more than the largest member, or halfway
     425                 :             :                  * between two existing members.
     426                 :             :                  *
     427                 :             :                  * In the "halfway" case, because of the finite precision of float4,
     428                 :             :                  * we might compute a value that's actually equal to one or the other
     429                 :             :                  * of its neighbors.  In that case we renumber the existing members
     430                 :             :                  * and try again.
     431                 :             :                  */
     432         [ +  + ]:          35 :                 if (newValIsAfter)
     433                 :           2 :                         other_nbr_index = nbr_index + 1;
     434                 :             :                 else
     435                 :          33 :                         other_nbr_index = nbr_index - 1;
     436                 :             : 
     437         [ +  + ]:          35 :                 if (other_nbr_index < 0)
     438                 :           1 :                         newelemorder = nbr_en->enumsortorder - 1;
     439         [ +  + ]:          34 :                 else if (other_nbr_index >= nelems)
     440                 :           1 :                         newelemorder = nbr_en->enumsortorder + 1;
     441                 :             :                 else
     442                 :             :                 {
     443                 :             :                         /*
     444                 :             :                          * The midpoint value computed here has to be rounded to float4
     445                 :             :                          * precision, else our equality comparisons against the adjacent
     446                 :             :                          * values are meaningless.  The most portable way of forcing that
     447                 :             :                          * to happen with non-C-standard-compliant compilers is to store
     448                 :             :                          * it into a volatile variable.
     449                 :             :                          */
     450                 :          33 :                         volatile float4 midpoint;
     451                 :             : 
     452                 :          33 :                         other_nbr_en = (Form_pg_enum) GETSTRUCT(existing[other_nbr_index]);
     453                 :          99 :                         midpoint = (nbr_en->enumsortorder +
     454                 :          66 :                                                 other_nbr_en->enumsortorder) / 2;
     455                 :             : 
     456   [ +  +  -  + ]:          33 :                         if (midpoint == nbr_en->enumsortorder ||
     457                 :          32 :                                 midpoint == other_nbr_en->enumsortorder)
     458                 :             :                         {
     459                 :           1 :                                 RenumberEnumType(pg_enum, existing, nelems);
     460                 :             :                                 /* Clean up and start over */
     461                 :           1 :                                 pfree(existing);
     462                 :           1 :                                 ReleaseCatCacheList(list);
     463                 :           1 :                                 goto restart;
     464                 :             :                         }
     465                 :             : 
     466                 :          32 :                         newelemorder = midpoint;
     467         [ +  + ]:          33 :                 }
     468         [ +  + ]:          35 :         }
     469                 :             : 
     470                 :             :         /* Get a new OID for the new label */
     471         [ -  + ]:          40 :         if (IsBinaryUpgrade)
     472                 :             :         {
     473         [ #  # ]:           0 :                 if (!OidIsValid(binary_upgrade_next_pg_enum_oid))
     474   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     475                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     476                 :             :                                          errmsg("pg_enum OID value not set when in binary upgrade mode")));
     477                 :             : 
     478                 :             :                 /*
     479                 :             :                  * Use binary-upgrade override for pg_enum.oid, if supplied. During
     480                 :             :                  * binary upgrade, all pg_enum.oid's are set this way so they are
     481                 :             :                  * guaranteed to be consistent.
     482                 :             :                  */
     483         [ #  # ]:           0 :                 if (neighbor != NULL)
     484   [ #  #  #  # ]:           0 :                         ereport(ERROR,
     485                 :             :                                         (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     486                 :             :                                          errmsg("ALTER TYPE ADD BEFORE/AFTER is incompatible with binary upgrade")));
     487                 :             : 
     488                 :           0 :                 newOid = binary_upgrade_next_pg_enum_oid;
     489                 :           0 :                 binary_upgrade_next_pg_enum_oid = InvalidOid;
     490                 :           0 :         }
     491                 :             :         else
     492                 :             :         {
     493                 :             :                 /*
     494                 :             :                  * Normal case: we need to allocate a new Oid for the value.
     495                 :             :                  *
     496                 :             :                  * We want to give the new element an even-numbered Oid if it's safe,
     497                 :             :                  * which is to say it compares correctly to all pre-existing even
     498                 :             :                  * numbered Oids in the enum.  Otherwise, we must give it an odd Oid.
     499                 :             :                  */
     500                 :          68 :                 for (;;)
     501                 :             :                 {
     502                 :          68 :                         bool            sorts_ok;
     503                 :             : 
     504                 :             :                         /* Get a new OID (different from all existing pg_enum tuples) */
     505                 :          68 :                         newOid = GetNewOidWithIndex(pg_enum, EnumOidIndexId,
     506                 :             :                                                                                 Anum_pg_enum_oid);
     507                 :             : 
     508                 :             :                         /*
     509                 :             :                          * Detect whether it sorts correctly relative to existing
     510                 :             :                          * even-numbered labels of the enum.  We can ignore existing
     511                 :             :                          * labels with odd Oids, since a comparison involving one of those
     512                 :             :                          * will not take the fast path anyway.
     513                 :             :                          */
     514                 :          68 :                         sorts_ok = true;
     515         [ +  + ]:         977 :                         for (i = 0; i < nelems; i++)
     516                 :             :                         {
     517                 :         966 :                                 HeapTuple       exists_tup = existing[i];
     518                 :         966 :                                 Form_pg_enum exists_en = (Form_pg_enum) GETSTRUCT(exists_tup);
     519                 :         966 :                                 Oid                     exists_oid = exists_en->oid;
     520                 :             : 
     521         [ +  + ]:         966 :                                 if (exists_oid & 1)
     522                 :         820 :                                         continue;       /* ignore odd Oids */
     523                 :             : 
     524         [ +  + ]:         146 :                                 if (exists_en->enumsortorder < newelemorder)
     525                 :             :                                 {
     526                 :             :                                         /* should sort before */
     527         [ -  + ]:          89 :                                         if (exists_oid >= newOid)
     528                 :             :                                         {
     529                 :           0 :                                                 sorts_ok = false;
     530                 :           0 :                                                 break;
     531                 :             :                                         }
     532                 :          89 :                                 }
     533                 :             :                                 else
     534                 :             :                                 {
     535                 :             :                                         /* should sort after */
     536         [ +  - ]:          57 :                                         if (exists_oid <= newOid)
     537                 :             :                                         {
     538                 :          57 :                                                 sorts_ok = false;
     539                 :          57 :                                                 break;
     540                 :             :                                         }
     541                 :             :                                 }
     542      [ +  +  + ]:         966 :                         }
     543                 :             : 
     544         [ +  + ]:          68 :                         if (sorts_ok)
     545                 :             :                         {
     546                 :             :                                 /* If it's even and sorts OK, we're done. */
     547         [ +  + ]:          11 :                                 if ((newOid & 1) == 0)
     548                 :           7 :                                         break;
     549                 :             : 
     550                 :             :                                 /*
     551                 :             :                                  * If it's odd, and sorts OK, loop back to get another OID and
     552                 :             :                                  * try again.  Probably, the next available even OID will sort
     553                 :             :                                  * correctly too, so it's worth trying.
     554                 :             :                                  */
     555                 :           4 :                         }
     556                 :             :                         else
     557                 :             :                         {
     558                 :             :                                 /*
     559                 :             :                                  * If it's odd, and does not sort correctly, we're done.
     560                 :             :                                  * (Probably, the next available even OID would sort
     561                 :             :                                  * incorrectly too, so no point in trying again.)
     562                 :             :                                  */
     563         [ +  + ]:          57 :                                 if (newOid & 1)
     564                 :          33 :                                         break;
     565                 :             : 
     566                 :             :                                 /*
     567                 :             :                                  * If it's even, and does not sort correctly, loop back to get
     568                 :             :                                  * another OID and try again.  (We *must* reject this case.)
     569                 :             :                                  */
     570                 :             :                         }
     571         [ +  + ]:          68 :                 }
     572                 :             :         }
     573                 :             : 
     574                 :             :         /* Done with info about existing members */
     575                 :          40 :         pfree(existing);
     576                 :          40 :         ReleaseCatCacheList(list);
     577                 :             : 
     578                 :             :         /* Create the new pg_enum entry */
     579                 :          40 :         memset(nulls, false, sizeof(nulls));
     580                 :          40 :         values[Anum_pg_enum_oid - 1] = ObjectIdGetDatum(newOid);
     581                 :          40 :         values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
     582                 :          40 :         values[Anum_pg_enum_enumsortorder - 1] = Float4GetDatum(newelemorder);
     583                 :          40 :         namestrcpy(&enumlabel, newVal);
     584                 :          40 :         values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
     585                 :          40 :         enum_tup = heap_form_tuple(RelationGetDescr(pg_enum), values, nulls);
     586                 :          40 :         CatalogTupleInsert(pg_enum, enum_tup);
     587                 :          40 :         heap_freetuple(enum_tup);
     588                 :             : 
     589                 :          40 :         table_close(pg_enum, RowExclusiveLock);
     590                 :             : 
     591                 :             :         /*
     592                 :             :          * If the enum type itself is uncommitted, we need not enter the new enum
     593                 :             :          * value into uncommitted_enum_values, because the type won't survive if
     594                 :             :          * the value doesn't.  (This is basically the same reasoning as for values
     595                 :             :          * made directly by CREATE TYPE AS ENUM.)  However, apply this rule only
     596                 :             :          * when we are not inside a subtransaction; if we're more deeply nested
     597                 :             :          * than the CREATE TYPE then the conclusion doesn't hold.  We could expend
     598                 :             :          * more effort to track the subtransaction level of CREATE TYPE, but for
     599                 :             :          * now we're only concerned about making the world safe for pg_dump in
     600                 :             :          * binary upgrade mode, and that won't use subtransactions.
     601                 :             :          */
     602   [ +  -  +  + ]:          40 :         if (GetCurrentTransactionNestLevel() == 1 &&
     603                 :          40 :                 EnumTypeUncommitted(enumTypeOid))
     604                 :           2 :                 return;
     605                 :             : 
     606                 :             :         /* Set up the uncommitted values table if not already done in this tx */
     607         [ -  + ]:          38 :         if (uncommitted_enum_values == NULL)
     608                 :          38 :                 init_uncommitted_enum_values();
     609                 :             : 
     610                 :             :         /* Add the new value to the table */
     611                 :          38 :         (void) hash_search(uncommitted_enum_values, &newOid, HASH_ENTER, NULL);
     612                 :          41 : }
     613                 :             : 
     614                 :             : 
     615                 :             : /*
     616                 :             :  * RenameEnumLabel
     617                 :             :  *              Rename a label in an enum set.
     618                 :             :  */
     619                 :             : void
     620                 :           4 : RenameEnumLabel(Oid enumTypeOid,
     621                 :             :                                 const char *oldVal,
     622                 :             :                                 const char *newVal)
     623                 :             : {
     624                 :           4 :         Relation        pg_enum;
     625                 :           4 :         HeapTuple       enum_tup;
     626                 :           4 :         Form_pg_enum en;
     627                 :           4 :         CatCList   *list;
     628                 :           4 :         int                     nelems;
     629                 :           4 :         HeapTuple       old_tup;
     630                 :           4 :         bool            found_new;
     631                 :           4 :         int                     i;
     632                 :             : 
     633                 :             :         /* check length of new label is ok */
     634         [ +  - ]:           4 :         if (strlen(newVal) > (NAMEDATALEN - 1))
     635   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     636                 :             :                                 (errcode(ERRCODE_INVALID_NAME),
     637                 :             :                                  errmsg("invalid enum label \"%s\"", newVal),
     638                 :             :                                  errdetail("Labels must be %d bytes or less.",
     639                 :             :                                                    NAMEDATALEN - 1)));
     640                 :             : 
     641                 :             :         /*
     642                 :             :          * Acquire a lock on the enum type, which we won't release until commit.
     643                 :             :          * This ensures that two backends aren't concurrently modifying the same
     644                 :             :          * enum type.  Since we are not changing the type's sort order, this is
     645                 :             :          * probably not really necessary, but there seems no reason not to take
     646                 :             :          * the lock to be sure.
     647                 :             :          */
     648                 :           4 :         LockDatabaseObject(TypeRelationId, enumTypeOid, 0, ExclusiveLock);
     649                 :             : 
     650                 :           4 :         pg_enum = table_open(EnumRelationId, RowExclusiveLock);
     651                 :             : 
     652                 :             :         /* Get the list of existing members of the enum */
     653                 :           4 :         list = SearchSysCacheList1(ENUMTYPOIDNAME,
     654                 :             :                                                            ObjectIdGetDatum(enumTypeOid));
     655                 :           4 :         nelems = list->n_members;
     656                 :             : 
     657                 :             :         /*
     658                 :             :          * Locate the element to rename and check if the new label is already in
     659                 :             :          * use.  (The unique index on pg_enum would catch that anyway, but we
     660                 :             :          * prefer a friendlier error message.)
     661                 :             :          */
     662                 :           4 :         old_tup = NULL;
     663                 :           4 :         found_new = false;
     664         [ +  + ]:          24 :         for (i = 0; i < nelems; i++)
     665                 :             :         {
     666                 :          20 :                 enum_tup = &(list->members[i]->tuple);
     667                 :          20 :                 en = (Form_pg_enum) GETSTRUCT(enum_tup);
     668         [ +  + ]:          20 :                 if (strcmp(NameStr(en->enumlabel), oldVal) == 0)
     669                 :           3 :                         old_tup = enum_tup;
     670         [ +  + ]:          20 :                 if (strcmp(NameStr(en->enumlabel), newVal) == 0)
     671                 :           2 :                         found_new = true;
     672                 :          20 :         }
     673         [ +  + ]:           4 :         if (!old_tup)
     674   [ +  -  +  - ]:           1 :                 ereport(ERROR,
     675                 :             :                                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
     676                 :             :                                  errmsg("\"%s\" is not an existing enum label",
     677                 :             :                                                 oldVal)));
     678         [ +  + ]:           3 :         if (found_new)
     679   [ +  -  +  - ]:           1 :                 ereport(ERROR,
     680                 :             :                                 (errcode(ERRCODE_DUPLICATE_OBJECT),
     681                 :             :                                  errmsg("enum label \"%s\" already exists",
     682                 :             :                                                 newVal)));
     683                 :             : 
     684                 :             :         /* OK, make a writable copy of old tuple */
     685                 :           2 :         enum_tup = heap_copytuple(old_tup);
     686                 :           2 :         en = (Form_pg_enum) GETSTRUCT(enum_tup);
     687                 :             : 
     688                 :           2 :         ReleaseCatCacheList(list);
     689                 :             : 
     690                 :             :         /* Update the pg_enum entry */
     691                 :           2 :         namestrcpy(&en->enumlabel, newVal);
     692                 :           2 :         CatalogTupleUpdate(pg_enum, &enum_tup->t_self, enum_tup);
     693                 :           2 :         heap_freetuple(enum_tup);
     694                 :             : 
     695                 :           2 :         table_close(pg_enum, RowExclusiveLock);
     696                 :           2 : }
     697                 :             : 
     698                 :             : 
     699                 :             : /*
     700                 :             :  * Test if the given type OID is in the table of uncommitted enum types.
     701                 :             :  */
     702                 :             : static bool
     703                 :          40 : EnumTypeUncommitted(Oid typ_id)
     704                 :             : {
     705                 :          40 :         bool            found;
     706                 :             : 
     707                 :             :         /* If we've made no uncommitted types table, it's not in the table */
     708         [ +  + ]:          40 :         if (uncommitted_enum_types == NULL)
     709                 :          38 :                 return false;
     710                 :             : 
     711                 :             :         /* Else, is it in the table? */
     712                 :           2 :         (void) hash_search(uncommitted_enum_types, &typ_id, HASH_FIND, &found);
     713                 :           2 :         return found;
     714                 :          40 : }
     715                 :             : 
     716                 :             : 
     717                 :             : /*
     718                 :             :  * Test if the given enum value is in the table of uncommitted enum values.
     719                 :             :  */
     720                 :             : bool
     721                 :          14 : EnumUncommitted(Oid enum_id)
     722                 :             : {
     723                 :          14 :         bool            found;
     724                 :             : 
     725                 :             :         /* If we've made no uncommitted values table, it's not in the table */
     726         [ +  + ]:          14 :         if (uncommitted_enum_values == NULL)
     727                 :          10 :                 return false;
     728                 :             : 
     729                 :             :         /* Else, is it in the table? */
     730                 :           4 :         (void) hash_search(uncommitted_enum_values, &enum_id, HASH_FIND, &found);
     731                 :           4 :         return found;
     732                 :          14 : }
     733                 :             : 
     734                 :             : 
     735                 :             : /*
     736                 :             :  * Clean up enum stuff after end of top-level transaction.
     737                 :             :  */
     738                 :             : void
     739                 :       57917 : AtEOXact_Enum(void)
     740                 :             : {
     741                 :             :         /*
     742                 :             :          * Reset the uncommitted tables, as all our tuples are now committed. The
     743                 :             :          * memory will go away automatically when TopTransactionContext is freed;
     744                 :             :          * it's sufficient to clear our pointers.
     745                 :             :          */
     746                 :       57917 :         uncommitted_enum_types = NULL;
     747                 :       57917 :         uncommitted_enum_values = NULL;
     748                 :       57917 : }
     749                 :             : 
     750                 :             : 
     751                 :             : /*
     752                 :             :  * RenumberEnumType
     753                 :             :  *              Renumber existing enum elements to have sort positions 1..n.
     754                 :             :  *
     755                 :             :  * We avoid doing this unless absolutely necessary; in most installations
     756                 :             :  * it will never happen.  The reason is that updating existing pg_enum
     757                 :             :  * entries creates hazards for other backends that are concurrently reading
     758                 :             :  * pg_enum.  Although system catalog scans now use MVCC semantics, the
     759                 :             :  * syscache machinery might read different pg_enum entries under different
     760                 :             :  * snapshots, so some other backend might get confused about the proper
     761                 :             :  * ordering if a concurrent renumbering occurs.
     762                 :             :  *
     763                 :             :  * We therefore make the following choices:
     764                 :             :  *
     765                 :             :  * 1. Any code that is interested in the enumsortorder values MUST read
     766                 :             :  * all the relevant pg_enum entries with a single MVCC snapshot, or else
     767                 :             :  * acquire lock on the enum type to prevent concurrent execution of
     768                 :             :  * AddEnumLabel().
     769                 :             :  *
     770                 :             :  * 2. Code that is not examining enumsortorder can use a syscache
     771                 :             :  * (for example, enum_in and enum_out do so).
     772                 :             :  */
     773                 :             : static void
     774                 :           1 : RenumberEnumType(Relation pg_enum, HeapTuple *existing, int nelems)
     775                 :             : {
     776                 :           1 :         int                     i;
     777                 :             : 
     778                 :             :         /*
     779                 :             :          * We should only need to increase existing elements' enumsortorders,
     780                 :             :          * never decrease them.  Therefore, work from the end backwards, to avoid
     781                 :             :          * unwanted uniqueness violations.
     782                 :             :          */
     783         [ +  + ]:          26 :         for (i = nelems - 1; i >= 0; i--)
     784                 :             :         {
     785                 :          25 :                 HeapTuple       newtup;
     786                 :          25 :                 Form_pg_enum en;
     787                 :          25 :                 float4          newsortorder;
     788                 :             : 
     789                 :          25 :                 newtup = heap_copytuple(existing[i]);
     790                 :          25 :                 en = (Form_pg_enum) GETSTRUCT(newtup);
     791                 :             : 
     792                 :          25 :                 newsortorder = i + 1;
     793         [ +  + ]:          25 :                 if (en->enumsortorder != newsortorder)
     794                 :             :                 {
     795                 :          24 :                         en->enumsortorder = newsortorder;
     796                 :             : 
     797                 :          24 :                         CatalogTupleUpdate(pg_enum, &newtup->t_self, newtup);
     798                 :          24 :                 }
     799                 :             : 
     800                 :          25 :                 heap_freetuple(newtup);
     801                 :          25 :         }
     802                 :             : 
     803                 :             :         /* Make the updates visible */
     804                 :           1 :         CommandCounterIncrement();
     805                 :           1 : }
     806                 :             : 
     807                 :             : 
     808                 :             : /* qsort comparison function for tuples by sort order */
     809                 :             : static int
     810                 :        2240 : sort_order_cmp(const void *p1, const void *p2)
     811                 :             : {
     812                 :        2240 :         HeapTuple       v1 = *((const HeapTuple *) p1);
     813                 :        2240 :         HeapTuple       v2 = *((const HeapTuple *) p2);
     814                 :        2240 :         Form_pg_enum en1 = (Form_pg_enum) GETSTRUCT(v1);
     815                 :        2240 :         Form_pg_enum en2 = (Form_pg_enum) GETSTRUCT(v2);
     816                 :             : 
     817         [ +  + ]:        2240 :         if (en1->enumsortorder < en2->enumsortorder)
     818                 :         937 :                 return -1;
     819         [ +  - ]:        1303 :         else if (en1->enumsortorder > en2->enumsortorder)
     820                 :        1303 :                 return 1;
     821                 :             :         else
     822                 :           0 :                 return 0;
     823                 :        2240 : }
     824                 :             : 
     825                 :             : Size
     826                 :         310 : EstimateUncommittedEnumsSpace(void)
     827                 :             : {
     828                 :         310 :         size_t          entries = 0;
     829                 :             : 
     830         [ +  - ]:         310 :         if (uncommitted_enum_types)
     831                 :           0 :                 entries += hash_get_num_entries(uncommitted_enum_types);
     832         [ +  - ]:         310 :         if (uncommitted_enum_values)
     833                 :           0 :                 entries += hash_get_num_entries(uncommitted_enum_values);
     834                 :             : 
     835                 :             :         /* Add two for the terminators. */
     836                 :         620 :         return sizeof(Oid) * (entries + 2);
     837                 :         310 : }
     838                 :             : 
     839                 :             : void
     840                 :         155 : SerializeUncommittedEnums(void *space, Size size)
     841                 :             : {
     842                 :         155 :         Oid                *serialized = (Oid *) space;
     843                 :             : 
     844                 :             :         /*
     845                 :             :          * Make sure the hash tables haven't changed in size since the caller
     846                 :             :          * reserved the space.
     847                 :             :          */
     848         [ +  - ]:         155 :         Assert(size == EstimateUncommittedEnumsSpace());
     849                 :             : 
     850                 :             :         /* Write out all the OIDs from the types hash table, if there is one. */
     851         [ -  + ]:         155 :         if (uncommitted_enum_types)
     852                 :             :         {
     853                 :           0 :                 HASH_SEQ_STATUS status;
     854                 :           0 :                 Oid                *value;
     855                 :             : 
     856                 :           0 :                 hash_seq_init(&status, uncommitted_enum_types);
     857         [ #  # ]:           0 :                 while ((value = (Oid *) hash_seq_search(&status)))
     858                 :           0 :                         *serialized++ = *value;
     859                 :           0 :         }
     860                 :             : 
     861                 :             :         /* Write out the terminator. */
     862                 :         155 :         *serialized++ = InvalidOid;
     863                 :             : 
     864                 :             :         /* Write out all the OIDs from the values hash table, if there is one. */
     865         [ -  + ]:         155 :         if (uncommitted_enum_values)
     866                 :             :         {
     867                 :           0 :                 HASH_SEQ_STATUS status;
     868                 :           0 :                 Oid                *value;
     869                 :             : 
     870                 :           0 :                 hash_seq_init(&status, uncommitted_enum_values);
     871         [ #  # ]:           0 :                 while ((value = (Oid *) hash_seq_search(&status)))
     872                 :           0 :                         *serialized++ = *value;
     873                 :           0 :         }
     874                 :             : 
     875                 :             :         /* Write out the terminator. */
     876                 :         155 :         *serialized++ = InvalidOid;
     877                 :             : 
     878                 :             :         /*
     879                 :             :          * Make sure the amount of space we actually used matches what was
     880                 :             :          * estimated.
     881                 :             :          */
     882         [ +  - ]:         155 :         Assert((char *) serialized == ((char *) space) + size);
     883                 :         155 : }
     884                 :             : 
     885                 :             : void
     886                 :         477 : RestoreUncommittedEnums(void *space)
     887                 :             : {
     888                 :         477 :         Oid                *serialized = (Oid *) space;
     889                 :             : 
     890         [ +  - ]:         477 :         Assert(!uncommitted_enum_types);
     891         [ +  - ]:         477 :         Assert(!uncommitted_enum_values);
     892                 :             : 
     893                 :             :         /*
     894                 :             :          * If either list is empty then don't even bother to create that hash
     895                 :             :          * table.  This is the common case, since most transactions don't create
     896                 :             :          * or alter enums.
     897                 :             :          */
     898         [ -  + ]:         477 :         if (OidIsValid(*serialized))
     899                 :             :         {
     900                 :             :                 /* Read all the types into a new hash table. */
     901                 :           0 :                 init_uncommitted_enum_types();
     902                 :           0 :                 do
     903                 :             :                 {
     904                 :           0 :                         (void) hash_search(uncommitted_enum_types, serialized++,
     905                 :             :                                                            HASH_ENTER, NULL);
     906         [ #  # ]:           0 :                 } while (OidIsValid(*serialized));
     907                 :           0 :         }
     908                 :         477 :         serialized++;
     909         [ -  + ]:         477 :         if (OidIsValid(*serialized))
     910                 :             :         {
     911                 :             :                 /* Read all the values into a new hash table. */
     912                 :           0 :                 init_uncommitted_enum_values();
     913                 :           0 :                 do
     914                 :             :                 {
     915                 :           0 :                         (void) hash_search(uncommitted_enum_values, serialized++,
     916                 :             :                                                            HASH_ENTER, NULL);
     917         [ #  # ]:           0 :                 } while (OidIsValid(*serialized));
     918                 :           0 :         }
     919                 :         477 : }
        

Generated by: LCOV version 2.3.2-1