LCOV - code coverage report
Current view: top level - src/backend/utils/adt - enum.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 79.9 % 184 147
Test Date: 2026-03-12 09:14:38 Functions: 90.5 % 21 19
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * enum.c
       4              :  *    I/O functions, operators, aggregates etc for enum types
       5              :  *
       6              :  * Copyright (c) 2006-2026, PostgreSQL Global Development Group
       7              :  *
       8              :  *
       9              :  * IDENTIFICATION
      10              :  *    src/backend/utils/adt/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 "catalog/pg_enum.h"
      20              : #include "libpq/pqformat.h"
      21              : #include "storage/procarray.h"
      22              : #include "utils/array.h"
      23              : #include "utils/builtins.h"
      24              : #include "utils/fmgroids.h"
      25              : #include "utils/syscache.h"
      26              : #include "utils/typcache.h"
      27              : 
      28              : 
      29              : static Oid  enum_endpoint(Oid enumtypoid, ScanDirection direction);
      30              : static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
      31              : 
      32              : 
      33              : /*
      34              :  * Disallow use of an uncommitted pg_enum tuple.
      35              :  *
      36              :  * We need to make sure that uncommitted enum values don't get into indexes.
      37              :  * If they did, and if we then rolled back the pg_enum addition, we'd have
      38              :  * broken the index because value comparisons will not work reliably without
      39              :  * an underlying pg_enum entry.  (Note that removal of the heap entry
      40              :  * containing an enum value is not sufficient to ensure that it doesn't appear
      41              :  * in upper levels of indexes.)  To do this we prevent an uncommitted row from
      42              :  * being used for any SQL-level purpose.  This is stronger than necessary,
      43              :  * since the value might not be getting inserted into a table or there might
      44              :  * be no index on its column, but it's easy to enforce centrally.
      45              :  *
      46              :  * However, it's okay to allow use of uncommitted values belonging to enum
      47              :  * types that were themselves created in the same transaction, because then
      48              :  * any such index would also be new and would go away altogether on rollback.
      49              :  * We don't implement that fully right now, but we do allow free use of enum
      50              :  * values created during CREATE TYPE AS ENUM, which are surely of the same
      51              :  * lifespan as the enum type.  (This case is required by "pg_restore -1".)
      52              :  * Values added by ALTER TYPE ADD VALUE are also allowed if the enum type
      53              :  * is known to have been created earlier in the same transaction.  (Note that
      54              :  * we have to track that explicitly; comparing tuple xmins is insufficient,
      55              :  * because the type tuple might have been updated in the current transaction.
      56              :  * Subtransactions also create hazards to be accounted for; currently,
      57              :  * pg_enum.c only handles ADD VALUE at the outermost transaction level.)
      58              :  *
      59              :  * This function needs to be called (directly or indirectly) in any of the
      60              :  * functions below that could return an enum value to SQL operations.
      61              :  */
      62              : static void
      63       117462 : check_safe_enum_use(HeapTuple enumval_tup)
      64              : {
      65              :     TransactionId xmin;
      66       117462 :     Form_pg_enum en = (Form_pg_enum) GETSTRUCT(enumval_tup);
      67              : 
      68              :     /*
      69              :      * If the row is hinted as committed, it's surely safe.  This provides a
      70              :      * fast path for all normal use-cases.
      71              :      */
      72       117462 :     if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
      73       117417 :         return;
      74              : 
      75              :     /*
      76              :      * Usually, a row would get hinted as committed when it's read or loaded
      77              :      * into syscache; but just in case not, let's check the xmin directly.
      78              :      */
      79           45 :     xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
      80           45 :     if (!TransactionIdIsInProgress(xmin) &&
      81            0 :         TransactionIdDidCommit(xmin))
      82            0 :         return;
      83              : 
      84              :     /*
      85              :      * Check if the enum value is listed as uncommitted.  If not, it's safe,
      86              :      * because it can't be shorter-lived than its owning type.  (This'd also
      87              :      * be false for values made by other transactions; but the previous tests
      88              :      * should have handled all of those.)
      89              :      */
      90           45 :     if (!EnumUncommitted(en->oid))
      91           33 :         return;
      92              : 
      93              :     /*
      94              :      * There might well be other tests we could do here to narrow down the
      95              :      * unsafe conditions, but for now just raise an exception.
      96              :      */
      97           12 :     ereport(ERROR,
      98              :             (errcode(ERRCODE_UNSAFE_NEW_ENUM_VALUE_USAGE),
      99              :              errmsg("unsafe use of new value \"%s\" of enum type %s",
     100              :                     NameStr(en->enumlabel),
     101              :                     format_type_be(en->enumtypid)),
     102              :              errhint("New enum values must be committed before they can be used.")));
     103              : }
     104              : 
     105              : 
     106              : /* Basic I/O support */
     107              : 
     108              : Datum
     109       117351 : enum_in(PG_FUNCTION_ARGS)
     110              : {
     111       117351 :     char       *name = PG_GETARG_CSTRING(0);
     112       117351 :     Oid         enumtypoid = PG_GETARG_OID(1);
     113       117351 :     Node       *escontext = fcinfo->context;
     114              :     Oid         enumoid;
     115              :     HeapTuple   tup;
     116              : 
     117              :     /* must check length to prevent Assert failure within SearchSysCache */
     118       117351 :     if (strlen(name) >= NAMEDATALEN)
     119            3 :         ereturn(escontext, (Datum) 0,
     120              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     121              :                  errmsg("invalid input value for enum %s: \"%s\"",
     122              :                         format_type_be(enumtypoid),
     123              :                         name)));
     124              : 
     125       117348 :     tup = SearchSysCache2(ENUMTYPOIDNAME,
     126              :                           ObjectIdGetDatum(enumtypoid),
     127              :                           CStringGetDatum(name));
     128       117348 :     if (!HeapTupleIsValid(tup))
     129            9 :         ereturn(escontext, (Datum) 0,
     130              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     131              :                  errmsg("invalid input value for enum %s: \"%s\"",
     132              :                         format_type_be(enumtypoid),
     133              :                         name)));
     134              : 
     135              :     /*
     136              :      * Check it's safe to use in SQL.  Perhaps we should take the trouble to
     137              :      * report "unsafe use" softly; but it's unclear that it's worth the
     138              :      * trouble, or indeed that that is a legitimate bad-input case at all
     139              :      * rather than an implementation shortcoming.
     140              :      */
     141       117339 :     check_safe_enum_use(tup);
     142              : 
     143              :     /*
     144              :      * This comes from pg_enum.oid and stores system oids in user tables. This
     145              :      * oid must be preserved by binary upgrades.
     146              :      */
     147       117333 :     enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
     148              : 
     149       117333 :     ReleaseSysCache(tup);
     150              : 
     151       117333 :     PG_RETURN_OID(enumoid);
     152              : }
     153              : 
     154              : Datum
     155        24622 : enum_out(PG_FUNCTION_ARGS)
     156              : {
     157        24622 :     Oid         enumval = PG_GETARG_OID(0);
     158              :     char       *result;
     159              :     HeapTuple   tup;
     160              :     Form_pg_enum en;
     161              : 
     162        24622 :     tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
     163        24622 :     if (!HeapTupleIsValid(tup))
     164            0 :         ereport(ERROR,
     165              :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     166              :                  errmsg("invalid internal value for enum: %u",
     167              :                         enumval)));
     168        24622 :     en = (Form_pg_enum) GETSTRUCT(tup);
     169              : 
     170        24622 :     result = pstrdup(NameStr(en->enumlabel));
     171              : 
     172        24622 :     ReleaseSysCache(tup);
     173              : 
     174        24622 :     PG_RETURN_CSTRING(result);
     175              : }
     176              : 
     177              : /* Binary I/O support */
     178              : Datum
     179            0 : enum_recv(PG_FUNCTION_ARGS)
     180              : {
     181            0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     182            0 :     Oid         enumtypoid = PG_GETARG_OID(1);
     183              :     Oid         enumoid;
     184              :     HeapTuple   tup;
     185              :     char       *name;
     186              :     int         nbytes;
     187              : 
     188            0 :     name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     189              : 
     190              :     /* must check length to prevent Assert failure within SearchSysCache */
     191            0 :     if (strlen(name) >= NAMEDATALEN)
     192            0 :         ereport(ERROR,
     193              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     194              :                  errmsg("invalid input value for enum %s: \"%s\"",
     195              :                         format_type_be(enumtypoid),
     196              :                         name)));
     197              : 
     198            0 :     tup = SearchSysCache2(ENUMTYPOIDNAME,
     199              :                           ObjectIdGetDatum(enumtypoid),
     200              :                           CStringGetDatum(name));
     201            0 :     if (!HeapTupleIsValid(tup))
     202            0 :         ereport(ERROR,
     203              :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     204              :                  errmsg("invalid input value for enum %s: \"%s\"",
     205              :                         format_type_be(enumtypoid),
     206              :                         name)));
     207              : 
     208              :     /* check it's safe to use in SQL */
     209            0 :     check_safe_enum_use(tup);
     210              : 
     211            0 :     enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
     212              : 
     213            0 :     ReleaseSysCache(tup);
     214              : 
     215            0 :     pfree(name);
     216              : 
     217            0 :     PG_RETURN_OID(enumoid);
     218              : }
     219              : 
     220              : Datum
     221            0 : enum_send(PG_FUNCTION_ARGS)
     222              : {
     223            0 :     Oid         enumval = PG_GETARG_OID(0);
     224              :     StringInfoData buf;
     225              :     HeapTuple   tup;
     226              :     Form_pg_enum en;
     227              : 
     228            0 :     tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
     229            0 :     if (!HeapTupleIsValid(tup))
     230            0 :         ereport(ERROR,
     231              :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     232              :                  errmsg("invalid internal value for enum: %u",
     233              :                         enumval)));
     234            0 :     en = (Form_pg_enum) GETSTRUCT(tup);
     235              : 
     236            0 :     pq_begintypsend(&buf);
     237            0 :     pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
     238              : 
     239            0 :     ReleaseSysCache(tup);
     240              : 
     241            0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     242              : }
     243              : 
     244              : /* Comparison functions and related */
     245              : 
     246              : /*
     247              :  * enum_cmp_internal is the common engine for all the visible comparison
     248              :  * functions, except for enum_eq and enum_ne which can just check for OID
     249              :  * equality directly.
     250              :  */
     251              : static int
     252      1240927 : enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
     253              : {
     254              :     TypeCacheEntry *tcache;
     255              : 
     256              :     /*
     257              :      * We don't need the typcache except in the hopefully-uncommon case that
     258              :      * one or both Oids are odd.  This means that cursory testing of code that
     259              :      * fails to pass flinfo to an enum comparison function might not disclose
     260              :      * the oversight.  To make such errors more obvious, Assert that we have a
     261              :      * place to cache even when we take a fast-path exit.
     262              :      */
     263              :     Assert(fcinfo->flinfo != NULL);
     264              : 
     265              :     /* Equal OIDs are equal no matter what */
     266      1240927 :     if (arg1 == arg2)
     267      1127715 :         return 0;
     268              : 
     269              :     /* Fast path: even-numbered Oids are known to compare correctly */
     270       113212 :     if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
     271              :     {
     272        36999 :         if (arg1 < arg2)
     273         5219 :             return -1;
     274              :         else
     275        31780 :             return 1;
     276              :     }
     277              : 
     278              :     /* Locate the typcache entry for the enum type */
     279        76213 :     tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
     280        76213 :     if (tcache == NULL)
     281              :     {
     282              :         HeapTuple   enum_tup;
     283              :         Form_pg_enum en;
     284              :         Oid         typeoid;
     285              : 
     286              :         /* Get the OID of the enum type containing arg1 */
     287           16 :         enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
     288           16 :         if (!HeapTupleIsValid(enum_tup))
     289            0 :             ereport(ERROR,
     290              :                     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     291              :                      errmsg("invalid internal value for enum: %u",
     292              :                             arg1)));
     293           16 :         en = (Form_pg_enum) GETSTRUCT(enum_tup);
     294           16 :         typeoid = en->enumtypid;
     295           16 :         ReleaseSysCache(enum_tup);
     296              :         /* Now locate and remember the typcache entry */
     297           16 :         tcache = lookup_type_cache(typeoid, 0);
     298           16 :         fcinfo->flinfo->fn_extra = tcache;
     299              :     }
     300              : 
     301              :     /* The remaining comparison logic is in typcache.c */
     302        76213 :     return compare_values_of_enum(tcache, arg1, arg2);
     303              : }
     304              : 
     305              : Datum
     306         2182 : enum_lt(PG_FUNCTION_ARGS)
     307              : {
     308         2182 :     Oid         a = PG_GETARG_OID(0);
     309         2182 :     Oid         b = PG_GETARG_OID(1);
     310              : 
     311         2182 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
     312              : }
     313              : 
     314              : Datum
     315         1105 : enum_le(PG_FUNCTION_ARGS)
     316              : {
     317         1105 :     Oid         a = PG_GETARG_OID(0);
     318         1105 :     Oid         b = PG_GETARG_OID(1);
     319              : 
     320         1105 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
     321              : }
     322              : 
     323              : Datum
     324         4335 : enum_eq(PG_FUNCTION_ARGS)
     325              : {
     326         4335 :     Oid         a = PG_GETARG_OID(0);
     327         4335 :     Oid         b = PG_GETARG_OID(1);
     328              : 
     329         4335 :     PG_RETURN_BOOL(a == b);
     330              : }
     331              : 
     332              : Datum
     333           36 : enum_ne(PG_FUNCTION_ARGS)
     334              : {
     335           36 :     Oid         a = PG_GETARG_OID(0);
     336           36 :     Oid         b = PG_GETARG_OID(1);
     337              : 
     338           36 :     PG_RETURN_BOOL(a != b);
     339              : }
     340              : 
     341              : Datum
     342         1098 : enum_ge(PG_FUNCTION_ARGS)
     343              : {
     344         1098 :     Oid         a = PG_GETARG_OID(0);
     345         1098 :     Oid         b = PG_GETARG_OID(1);
     346              : 
     347         1098 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
     348              : }
     349              : 
     350              : Datum
     351         2154 : enum_gt(PG_FUNCTION_ARGS)
     352              : {
     353         2154 :     Oid         a = PG_GETARG_OID(0);
     354         2154 :     Oid         b = PG_GETARG_OID(1);
     355              : 
     356         2154 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
     357              : }
     358              : 
     359              : Datum
     360           15 : enum_smaller(PG_FUNCTION_ARGS)
     361              : {
     362           15 :     Oid         a = PG_GETARG_OID(0);
     363           15 :     Oid         b = PG_GETARG_OID(1);
     364              : 
     365           15 :     PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
     366              : }
     367              : 
     368              : Datum
     369           48 : enum_larger(PG_FUNCTION_ARGS)
     370              : {
     371           48 :     Oid         a = PG_GETARG_OID(0);
     372           48 :     Oid         b = PG_GETARG_OID(1);
     373              : 
     374           48 :     PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
     375              : }
     376              : 
     377              : Datum
     378      1234325 : enum_cmp(PG_FUNCTION_ARGS)
     379              : {
     380      1234325 :     Oid         a = PG_GETARG_OID(0);
     381      1234325 :     Oid         b = PG_GETARG_OID(1);
     382              : 
     383      1234325 :     PG_RETURN_INT32(enum_cmp_internal(a, b, fcinfo));
     384              : }
     385              : 
     386              : /* Enum programming support functions */
     387              : 
     388              : /*
     389              :  * enum_endpoint: common code for enum_first/enum_last
     390              :  */
     391              : static Oid
     392           18 : enum_endpoint(Oid enumtypoid, ScanDirection direction)
     393              : {
     394              :     Relation    enum_rel;
     395              :     Relation    enum_idx;
     396              :     SysScanDesc enum_scan;
     397              :     HeapTuple   enum_tuple;
     398              :     ScanKeyData skey;
     399              :     Oid         minmax;
     400              : 
     401              :     /*
     402              :      * Find the first/last enum member using pg_enum_typid_sortorder_index.
     403              :      * Note we must not use the syscache.  See comments for RenumberEnumType
     404              :      * in catalog/pg_enum.c for more info.
     405              :      */
     406           18 :     ScanKeyInit(&skey,
     407              :                 Anum_pg_enum_enumtypid,
     408              :                 BTEqualStrategyNumber, F_OIDEQ,
     409              :                 ObjectIdGetDatum(enumtypoid));
     410              : 
     411           18 :     enum_rel = table_open(EnumRelationId, AccessShareLock);
     412           18 :     enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
     413           18 :     enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
     414              :                                            1, &skey);
     415              : 
     416           18 :     enum_tuple = systable_getnext_ordered(enum_scan, direction);
     417           18 :     if (HeapTupleIsValid(enum_tuple))
     418              :     {
     419              :         /* check it's safe to use in SQL */
     420           18 :         check_safe_enum_use(enum_tuple);
     421           15 :         minmax = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
     422              :     }
     423              :     else
     424              :     {
     425              :         /* should only happen with an empty enum */
     426            0 :         minmax = InvalidOid;
     427              :     }
     428              : 
     429           15 :     systable_endscan_ordered(enum_scan);
     430           15 :     index_close(enum_idx, AccessShareLock);
     431           15 :     table_close(enum_rel, AccessShareLock);
     432              : 
     433           15 :     return minmax;
     434              : }
     435              : 
     436              : Datum
     437            6 : enum_first(PG_FUNCTION_ARGS)
     438              : {
     439              :     Oid         enumtypoid;
     440              :     Oid         min;
     441              : 
     442              :     /*
     443              :      * We rely on being able to get the specific enum type from the calling
     444              :      * expression tree.  Notice that the actual value of the argument isn't
     445              :      * examined at all; in particular it might be NULL.
     446              :      */
     447            6 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     448            6 :     if (enumtypoid == InvalidOid)
     449            0 :         ereport(ERROR,
     450              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     451              :                  errmsg("could not determine actual enum type")));
     452              : 
     453              :     /* Get the OID using the index */
     454            6 :     min = enum_endpoint(enumtypoid, ForwardScanDirection);
     455              : 
     456            6 :     if (!OidIsValid(min))
     457            0 :         ereport(ERROR,
     458              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     459              :                  errmsg("enum %s contains no values",
     460              :                         format_type_be(enumtypoid))));
     461              : 
     462            6 :     PG_RETURN_OID(min);
     463              : }
     464              : 
     465              : Datum
     466           12 : enum_last(PG_FUNCTION_ARGS)
     467              : {
     468              :     Oid         enumtypoid;
     469              :     Oid         max;
     470              : 
     471              :     /*
     472              :      * We rely on being able to get the specific enum type from the calling
     473              :      * expression tree.  Notice that the actual value of the argument isn't
     474              :      * examined at all; in particular it might be NULL.
     475              :      */
     476           12 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     477           12 :     if (enumtypoid == InvalidOid)
     478            0 :         ereport(ERROR,
     479              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     480              :                  errmsg("could not determine actual enum type")));
     481              : 
     482              :     /* Get the OID using the index */
     483           12 :     max = enum_endpoint(enumtypoid, BackwardScanDirection);
     484              : 
     485            9 :     if (!OidIsValid(max))
     486            0 :         ereport(ERROR,
     487              :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     488              :                  errmsg("enum %s contains no values",
     489              :                         format_type_be(enumtypoid))));
     490              : 
     491            9 :     PG_RETURN_OID(max);
     492              : }
     493              : 
     494              : /* 2-argument variant of enum_range */
     495              : Datum
     496           12 : enum_range_bounds(PG_FUNCTION_ARGS)
     497              : {
     498              :     Oid         lower;
     499              :     Oid         upper;
     500              :     Oid         enumtypoid;
     501              : 
     502           12 :     if (PG_ARGISNULL(0))
     503            6 :         lower = InvalidOid;
     504              :     else
     505            6 :         lower = PG_GETARG_OID(0);
     506           12 :     if (PG_ARGISNULL(1))
     507            6 :         upper = InvalidOid;
     508              :     else
     509            6 :         upper = PG_GETARG_OID(1);
     510              : 
     511              :     /*
     512              :      * We rely on being able to get the specific enum type from the calling
     513              :      * expression tree.  The generic type mechanism should have ensured that
     514              :      * both are of the same type.
     515              :      */
     516           12 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     517           12 :     if (enumtypoid == InvalidOid)
     518            0 :         ereport(ERROR,
     519              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     520              :                  errmsg("could not determine actual enum type")));
     521              : 
     522           12 :     PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
     523              : }
     524              : 
     525              : /* 1-argument variant of enum_range */
     526              : Datum
     527           15 : enum_range_all(PG_FUNCTION_ARGS)
     528              : {
     529              :     Oid         enumtypoid;
     530              : 
     531              :     /*
     532              :      * We rely on being able to get the specific enum type from the calling
     533              :      * expression tree.  Notice that the actual value of the argument isn't
     534              :      * examined at all; in particular it might be NULL.
     535              :      */
     536           15 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     537           15 :     if (enumtypoid == InvalidOid)
     538            0 :         ereport(ERROR,
     539              :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     540              :                  errmsg("could not determine actual enum type")));
     541              : 
     542           15 :     PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
     543              :                                               InvalidOid, InvalidOid));
     544              : }
     545              : 
     546              : static ArrayType *
     547           27 : enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
     548              : {
     549              :     ArrayType  *result;
     550              :     Relation    enum_rel;
     551              :     Relation    enum_idx;
     552              :     SysScanDesc enum_scan;
     553              :     HeapTuple   enum_tuple;
     554              :     ScanKeyData skey;
     555              :     Datum      *elems;
     556              :     int         max,
     557              :                 cnt;
     558              :     bool        left_found;
     559              : 
     560              :     /*
     561              :      * Scan the enum members in order using pg_enum_typid_sortorder_index.
     562              :      * Note we must not use the syscache.  See comments for RenumberEnumType
     563              :      * in catalog/pg_enum.c for more info.
     564              :      */
     565           27 :     ScanKeyInit(&skey,
     566              :                 Anum_pg_enum_enumtypid,
     567              :                 BTEqualStrategyNumber, F_OIDEQ,
     568              :                 ObjectIdGetDatum(enumtypoid));
     569              : 
     570           27 :     enum_rel = table_open(EnumRelationId, AccessShareLock);
     571           27 :     enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
     572           27 :     enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);
     573              : 
     574           27 :     max = 64;
     575           27 :     elems = (Datum *) palloc(max * sizeof(Datum));
     576           27 :     cnt = 0;
     577           27 :     left_found = !OidIsValid(lower);
     578              : 
     579          129 :     while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
     580              :     {
     581          111 :         Oid         enum_oid = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
     582              : 
     583          111 :         if (!left_found && lower == enum_oid)
     584            6 :             left_found = true;
     585              : 
     586          111 :         if (left_found)
     587              :         {
     588              :             /* check it's safe to use in SQL */
     589          105 :             check_safe_enum_use(enum_tuple);
     590              : 
     591          102 :             if (cnt >= max)
     592              :             {
     593            0 :                 max *= 2;
     594            0 :                 elems = (Datum *) repalloc(elems, max * sizeof(Datum));
     595              :             }
     596              : 
     597          102 :             elems[cnt++] = ObjectIdGetDatum(enum_oid);
     598              :         }
     599              : 
     600          108 :         if (OidIsValid(upper) && upper == enum_oid)
     601            6 :             break;
     602              :     }
     603              : 
     604           24 :     systable_endscan_ordered(enum_scan);
     605           24 :     index_close(enum_idx, AccessShareLock);
     606           24 :     table_close(enum_rel, AccessShareLock);
     607              : 
     608              :     /* and build the result array */
     609              :     /* note this hardwires some details about the representation of Oid */
     610           24 :     result = construct_array(elems, cnt, enumtypoid,
     611              :                              sizeof(Oid), true, TYPALIGN_INT);
     612              : 
     613           24 :     pfree(elems);
     614              : 
     615           24 :     return result;
     616              : }
        

Generated by: LCOV version 2.0-1