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

Generated by: LCOV version 1.13