LCOV - code coverage report
Current view: top level - src/backend/utils/adt - enum.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 149 184 81.0 %
Date: 2024-04-18 21:11:14 Functions: 19 21 90.5 %
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-2024, 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      234838 : check_safe_enum_use(HeapTuple enumval_tup)
      64             : {
      65             :     TransactionId xmin;
      66      234838 :     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      234838 :     if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
      73      234744 :         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          94 :     xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
      80          98 :     if (!TransactionIdIsInProgress(xmin) &&
      81           4 :         TransactionIdDidCommit(xmin))
      82           4 :         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          90 :     if (!EnumUncommitted(en->oid))
      91          66 :         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          24 :     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      234616 : enum_in(PG_FUNCTION_ARGS)
     110             : {
     111      234616 :     char       *name = PG_GETARG_CSTRING(0);
     112      234616 :     Oid         enumtypoid = PG_GETARG_OID(1);
     113      234616 :     Node       *escontext = fcinfo->context;
     114             :     Oid         enumoid;
     115             :     HeapTuple   tup;
     116             : 
     117             :     /* must check length to prevent Assert failure within SearchSysCache */
     118      234616 :     if (strlen(name) >= NAMEDATALEN)
     119           6 :         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      234610 :     tup = SearchSysCache2(ENUMTYPOIDNAME,
     126             :                           ObjectIdGetDatum(enumtypoid),
     127             :                           CStringGetDatum(name));
     128      234610 :     if (!HeapTupleIsValid(tup))
     129          18 :         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      234592 :     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      234580 :     enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
     148             : 
     149      234580 :     ReleaseSysCache(tup);
     150             : 
     151      234580 :     PG_RETURN_OID(enumoid);
     152             : }
     153             : 
     154             : Datum
     155       49090 : enum_out(PG_FUNCTION_ARGS)
     156             : {
     157       49090 :     Oid         enumval = PG_GETARG_OID(0);
     158             :     char       *result;
     159             :     HeapTuple   tup;
     160             :     Form_pg_enum en;
     161             : 
     162       49090 :     tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
     163       49090 :     if (!HeapTupleIsValid(tup))
     164           0 :         ereport(ERROR,
     165             :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     166             :                  errmsg("invalid internal value for enum: %u",
     167             :                         enumval)));
     168       49090 :     en = (Form_pg_enum) GETSTRUCT(tup);
     169             : 
     170       49090 :     result = pstrdup(NameStr(en->enumlabel));
     171             : 
     172       49090 :     ReleaseSysCache(tup);
     173             : 
     174       49090 :     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     2473900 : 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     2473900 :     if (arg1 == arg2)
     267     2247526 :         return 0;
     268             : 
     269             :     /* Fast path: even-numbered Oids are known to compare correctly */
     270      226374 :     if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
     271             :     {
     272       76300 :         if (arg1 < arg2)
     273       10798 :             return -1;
     274             :         else
     275       65502 :             return 1;
     276             :     }
     277             : 
     278             :     /* Locate the typcache entry for the enum type */
     279      150074 :     tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
     280      150074 :     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           8 :         enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
     288           8 :         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           8 :         en = (Form_pg_enum) GETSTRUCT(enum_tup);
     294           8 :         typeoid = en->enumtypid;
     295           8 :         ReleaseSysCache(enum_tup);
     296             :         /* Now locate and remember the typcache entry */
     297           8 :         tcache = lookup_type_cache(typeoid, 0);
     298           8 :         fcinfo->flinfo->fn_extra = (void *) tcache;
     299             :     }
     300             : 
     301             :     /* The remaining comparison logic is in typcache.c */
     302      150074 :     return compare_values_of_enum(tcache, arg1, arg2);
     303             : }
     304             : 
     305             : Datum
     306        4022 : enum_lt(PG_FUNCTION_ARGS)
     307             : {
     308        4022 :     Oid         a = PG_GETARG_OID(0);
     309        4022 :     Oid         b = PG_GETARG_OID(1);
     310             : 
     311        4022 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
     312             : }
     313             : 
     314             : Datum
     315        2210 : enum_le(PG_FUNCTION_ARGS)
     316             : {
     317        2210 :     Oid         a = PG_GETARG_OID(0);
     318        2210 :     Oid         b = PG_GETARG_OID(1);
     319             : 
     320        2210 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
     321             : }
     322             : 
     323             : Datum
     324        8670 : enum_eq(PG_FUNCTION_ARGS)
     325             : {
     326        8670 :     Oid         a = PG_GETARG_OID(0);
     327        8670 :     Oid         b = PG_GETARG_OID(1);
     328             : 
     329        8670 :     PG_RETURN_BOOL(a == b);
     330             : }
     331             : 
     332             : Datum
     333          72 : enum_ne(PG_FUNCTION_ARGS)
     334             : {
     335          72 :     Oid         a = PG_GETARG_OID(0);
     336          72 :     Oid         b = PG_GETARG_OID(1);
     337             : 
     338          72 :     PG_RETURN_BOOL(a != b);
     339             : }
     340             : 
     341             : Datum
     342        2178 : enum_ge(PG_FUNCTION_ARGS)
     343             : {
     344        2178 :     Oid         a = PG_GETARG_OID(0);
     345        2178 :     Oid         b = PG_GETARG_OID(1);
     346             : 
     347        2178 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
     348             : }
     349             : 
     350             : Datum
     351        3964 : enum_gt(PG_FUNCTION_ARGS)
     352             : {
     353        3964 :     Oid         a = PG_GETARG_OID(0);
     354        3964 :     Oid         b = PG_GETARG_OID(1);
     355             : 
     356        3964 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
     357             : }
     358             : 
     359             : Datum
     360          30 : enum_smaller(PG_FUNCTION_ARGS)
     361             : {
     362          30 :     Oid         a = PG_GETARG_OID(0);
     363          30 :     Oid         b = PG_GETARG_OID(1);
     364             : 
     365          30 :     PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
     366             : }
     367             : 
     368             : Datum
     369          96 : enum_larger(PG_FUNCTION_ARGS)
     370             : {
     371          96 :     Oid         a = PG_GETARG_OID(0);
     372          96 :     Oid         b = PG_GETARG_OID(1);
     373             : 
     374          96 :     PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
     375             : }
     376             : 
     377             : Datum
     378     2461400 : enum_cmp(PG_FUNCTION_ARGS)
     379             : {
     380     2461400 :     Oid         a = PG_GETARG_OID(0);
     381     2461400 :     Oid         b = PG_GETARG_OID(1);
     382             : 
     383     2461400 :     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          36 : 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          36 :     ScanKeyInit(&skey,
     407             :                 Anum_pg_enum_enumtypid,
     408             :                 BTEqualStrategyNumber, F_OIDEQ,
     409             :                 ObjectIdGetDatum(enumtypoid));
     410             : 
     411          36 :     enum_rel = table_open(EnumRelationId, AccessShareLock);
     412          36 :     enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
     413          36 :     enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
     414             :                                            1, &skey);
     415             : 
     416          36 :     enum_tuple = systable_getnext_ordered(enum_scan, direction);
     417          36 :     if (HeapTupleIsValid(enum_tuple))
     418             :     {
     419             :         /* check it's safe to use in SQL */
     420          36 :         check_safe_enum_use(enum_tuple);
     421          30 :         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          30 :     systable_endscan_ordered(enum_scan);
     430          30 :     index_close(enum_idx, AccessShareLock);
     431          30 :     table_close(enum_rel, AccessShareLock);
     432             : 
     433          30 :     return minmax;
     434             : }
     435             : 
     436             : Datum
     437          12 : 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          12 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     448          12 :     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          12 :     min = enum_endpoint(enumtypoid, ForwardScanDirection);
     455             : 
     456          12 :     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          12 :     PG_RETURN_OID(min);
     463             : }
     464             : 
     465             : Datum
     466          24 : 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          24 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     477          24 :     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          24 :     max = enum_endpoint(enumtypoid, BackwardScanDirection);
     484             : 
     485          18 :     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          18 :     PG_RETURN_OID(max);
     492             : }
     493             : 
     494             : /* 2-argument variant of enum_range */
     495             : Datum
     496          24 : enum_range_bounds(PG_FUNCTION_ARGS)
     497             : {
     498             :     Oid         lower;
     499             :     Oid         upper;
     500             :     Oid         enumtypoid;
     501             : 
     502          24 :     if (PG_ARGISNULL(0))
     503          12 :         lower = InvalidOid;
     504             :     else
     505          12 :         lower = PG_GETARG_OID(0);
     506          24 :     if (PG_ARGISNULL(1))
     507          12 :         upper = InvalidOid;
     508             :     else
     509          12 :         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          24 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     517          24 :     if (enumtypoid == InvalidOid)
     518           0 :         ereport(ERROR,
     519             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     520             :                  errmsg("could not determine actual enum type")));
     521             : 
     522          24 :     PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
     523             : }
     524             : 
     525             : /* 1-argument variant of enum_range */
     526             : Datum
     527          30 : 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          30 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     537          30 :     if (enumtypoid == InvalidOid)
     538           0 :         ereport(ERROR,
     539             :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     540             :                  errmsg("could not determine actual enum type")));
     541             : 
     542          30 :     PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
     543             :                                               InvalidOid, InvalidOid));
     544             : }
     545             : 
     546             : static ArrayType *
     547          54 : 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          54 :     ScanKeyInit(&skey,
     566             :                 Anum_pg_enum_enumtypid,
     567             :                 BTEqualStrategyNumber, F_OIDEQ,
     568             :                 ObjectIdGetDatum(enumtypoid));
     569             : 
     570          54 :     enum_rel = table_open(EnumRelationId, AccessShareLock);
     571          54 :     enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
     572          54 :     enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);
     573             : 
     574          54 :     max = 64;
     575          54 :     elems = (Datum *) palloc(max * sizeof(Datum));
     576          54 :     cnt = 0;
     577          54 :     left_found = !OidIsValid(lower);
     578             : 
     579         258 :     while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
     580             :     {
     581         222 :         Oid         enum_oid = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
     582             : 
     583         222 :         if (!left_found && lower == enum_oid)
     584          12 :             left_found = true;
     585             : 
     586         222 :         if (left_found)
     587             :         {
     588             :             /* check it's safe to use in SQL */
     589         210 :             check_safe_enum_use(enum_tuple);
     590             : 
     591         204 :             if (cnt >= max)
     592             :             {
     593           0 :                 max *= 2;
     594           0 :                 elems = (Datum *) repalloc(elems, max * sizeof(Datum));
     595             :             }
     596             : 
     597         204 :             elems[cnt++] = ObjectIdGetDatum(enum_oid);
     598             :         }
     599             : 
     600         216 :         if (OidIsValid(upper) && upper == enum_oid)
     601          12 :             break;
     602             :     }
     603             : 
     604          48 :     systable_endscan_ordered(enum_scan);
     605          48 :     index_close(enum_idx, AccessShareLock);
     606          48 :     table_close(enum_rel, AccessShareLock);
     607             : 
     608             :     /* and build the result array */
     609             :     /* note this hardwires some details about the representation of Oid */
     610          48 :     result = construct_array(elems, cnt, enumtypoid,
     611             :                              sizeof(Oid), true, TYPALIGN_INT);
     612             : 
     613          48 :     pfree(elems);
     614             : 
     615          48 :     return result;
     616             : }

Generated by: LCOV version 1.14