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

Generated by: LCOV version 1.13