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

Generated by: LCOV version 1.11