LCOV - differential code coverage report
Current view: top level - src/backend/catalog - pg_enum.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DUB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 93.9 % 229 215 3 10 1 2 123 25 65 10 143 1 4
Current Date: 2023-04-08 17:13:01 Functions: 100.0 % 12 12 11 1 11
Baseline: 15 Line coverage date bins:
Baseline Date: 2023-04-08 15:09:40 (120,180] days: 100.0 % 25 25 25
Legend: Lines: hit not hit (240..) days: 93.1 % 204 190 3 10 1 2 123 65 7 136
Function coverage date bins:
(240..) days: 54.5 % 22 12 11 1 10

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

Generated by: LCOV version v1.16-55-g56c0a2a