LCOV - differential code coverage report
Current view: top level - src/backend/utils/adt - enum.c (source / functions) Coverage Total Hit LBC UIC UBC GBC GIC GNC CBC EUB ECB DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 81.1 % 185 150 1 25 9 3 82 3 62 23 85 2
Current Date: 2023-04-08 15:15:32 Functions: 90.5 % 21 19 2 17 1 1 2 17
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /*-------------------------------------------------------------------------
       2                 :  *
       3                 :  * enum.c
       4                 :  *    I/O functions, operators, aggregates etc for enum types
       5                 :  *
       6                 :  * Copyright (c) 2006-2023, 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 CBC      118376 : check_safe_enum_use(HeapTuple enumval_tup)
      64                 : {
      65                 :     TransactionId xmin;
      66          118376 :     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          118376 :     if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
      73          118311 :         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              65 :     xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
      80              91 :     if (!TransactionIdIsInProgress(xmin) &&
      81              26 :         TransactionIdDidCommit(xmin))
      82              26 :         return;
      83                 : 
      84                 :     /*
      85                 :      * Check if the enum value is uncommitted.  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              39 :     if (!EnumUncommitted(en->oid))
      91              24 :         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              15 :     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          118268 : enum_in(PG_FUNCTION_ARGS)
     110                 : {
     111          118268 :     char       *name = PG_GETARG_CSTRING(0);
     112          118268 :     Oid         enumtypoid = PG_GETARG_OID(1);
     113 GNC      118268 :     Node       *escontext = fcinfo->context;
     114 ECB             :     Oid         enumoid;
     115                 :     HeapTuple   tup;
     116                 : 
     117                 :     /* must check length to prevent Assert failure within SearchSysCache */
     118 GIC      118268 :     if (strlen(name) >= NAMEDATALEN)
     119 GNC           3 :         ereturn(escontext, (Datum) 0,
     120 ECB             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     121                 :                  errmsg("invalid input value for enum %s: \"%s\"",
     122                 :                         format_type_be(enumtypoid),
     123                 :                         name)));
     124                 : 
     125 GIC      118265 :     tup = SearchSysCache2(ENUMTYPOIDNAME,
     126 ECB             :                           ObjectIdGetDatum(enumtypoid),
     127                 :                           CStringGetDatum(name));
     128 GIC      118265 :     if (!HeapTupleIsValid(tup))
     129 GNC           9 :         ereturn(escontext, (Datum) 0,
     130 ECB             :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     131                 :                  errmsg("invalid input value for enum %s: \"%s\"",
     132                 :                         format_type_be(enumtypoid),
     133                 :                         name)));
     134                 : 
     135                 :     /*
     136                 :      * Check it's safe to use in SQL.  Perhaps we should take the trouble to
     137                 :      * report "unsafe use" softly; but it's unclear that it's worth the
     138                 :      * trouble, or indeed that that is a legitimate bad-input case at all
     139                 :      * rather than an implementation shortcoming.
     140                 :      */
     141 GIC      118256 :     check_safe_enum_use(tup);
     142                 : 
     143                 :     /*
     144                 :      * This comes from pg_enum.oid and stores system oids in user tables. This
     145                 :      * oid must be preserved by binary upgrades.
     146                 :      */
     147 CBC      118250 :     enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
     148                 : 
     149 GIC      118250 :     ReleaseSysCache(tup);
     150                 : 
     151          118250 :     PG_RETURN_OID(enumoid);
     152                 : }
     153 ECB             : 
     154                 : Datum
     155 CBC       25430 : enum_out(PG_FUNCTION_ARGS)
     156                 : {
     157           25430 :     Oid         enumval = PG_GETARG_OID(0);
     158                 :     char       *result;
     159                 :     HeapTuple   tup;
     160                 :     Form_pg_enum en;
     161 ECB             : 
     162 GIC       25430 :     tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
     163 CBC       25430 :     if (!HeapTupleIsValid(tup))
     164 UIC           0 :         ereport(ERROR,
     165                 :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     166                 :                  errmsg("invalid internal value for enum: %u",
     167                 :                         enumval)));
     168 CBC       25430 :     en = (Form_pg_enum) GETSTRUCT(tup);
     169 ECB             : 
     170 GBC       25430 :     result = pstrdup(NameStr(en->enumlabel));
     171                 : 
     172 GIC       25430 :     ReleaseSysCache(tup);
     173                 : 
     174 CBC       25430 :     PG_RETURN_CSTRING(result);
     175                 : }
     176 ECB             : 
     177                 : /* Binary I/O support */
     178                 : Datum
     179 UIC           0 : enum_recv(PG_FUNCTION_ARGS)
     180 ECB             : {
     181 UIC           0 :     StringInfo  buf = (StringInfo) PG_GETARG_POINTER(0);
     182               0 :     Oid         enumtypoid = PG_GETARG_OID(1);
     183                 :     Oid         enumoid;
     184                 :     HeapTuple   tup;
     185 EUB             :     char       *name;
     186                 :     int         nbytes;
     187                 : 
     188 UBC           0 :     name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
     189                 : 
     190                 :     /* must check length to prevent Assert failure within SearchSysCache */
     191 UIC           0 :     if (strlen(name) >= NAMEDATALEN)
     192               0 :         ereport(ERROR,
     193                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     194 EUB             :                  errmsg("invalid input value for enum %s: \"%s\"",
     195                 :                         format_type_be(enumtypoid),
     196                 :                         name)));
     197                 : 
     198 UBC           0 :     tup = SearchSysCache2(ENUMTYPOIDNAME,
     199                 :                           ObjectIdGetDatum(enumtypoid),
     200                 :                           CStringGetDatum(name));
     201 UIC           0 :     if (!HeapTupleIsValid(tup))
     202               0 :         ereport(ERROR,
     203                 :                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     204 EUB             :                  errmsg("invalid input value for enum %s: \"%s\"",
     205                 :                         format_type_be(enumtypoid),
     206                 :                         name)));
     207                 : 
     208                 :     /* check it's safe to use in SQL */
     209 UIC           0 :     check_safe_enum_use(tup);
     210                 : 
     211               0 :     enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
     212                 : 
     213               0 :     ReleaseSysCache(tup);
     214                 : 
     215 UBC           0 :     pfree(name);
     216                 : 
     217               0 :     PG_RETURN_OID(enumoid);
     218                 : }
     219 EUB             : 
     220                 : Datum
     221 UBC           0 : enum_send(PG_FUNCTION_ARGS)
     222                 : {
     223               0 :     Oid         enumval = PG_GETARG_OID(0);
     224                 :     StringInfoData buf;
     225                 :     HeapTuple   tup;
     226                 :     Form_pg_enum en;
     227 EUB             : 
     228 UIC           0 :     tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
     229 UBC           0 :     if (!HeapTupleIsValid(tup))
     230 UIC           0 :         ereport(ERROR,
     231                 :                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     232                 :                  errmsg("invalid internal value for enum: %u",
     233                 :                         enumval)));
     234 UBC           0 :     en = (Form_pg_enum) GETSTRUCT(tup);
     235 EUB             : 
     236 UBC           0 :     pq_begintypsend(&buf);
     237 UIC           0 :     pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
     238                 : 
     239               0 :     ReleaseSysCache(tup);
     240 EUB             : 
     241 UIC           0 :     PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
     242 EUB             : }
     243                 : 
     244                 : /* Comparison functions and related */
     245                 : 
     246                 : /*
     247                 :  * enum_cmp_internal is the common engine for all the visible comparison
     248                 :  * functions, except for enum_eq and enum_ne which can just check for OID
     249                 :  * equality directly.
     250                 :  */
     251                 : static int
     252 GIC     1237587 : enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
     253                 : {
     254                 :     TypeCacheEntry *tcache;
     255                 : 
     256                 :     /*
     257                 :      * We don't need the typcache except in the hopefully-uncommon case that
     258 ECB             :      * one or both Oids are odd.  This means that cursory testing of code that
     259                 :      * fails to pass flinfo to an enum comparison function might not disclose
     260                 :      * the oversight.  To make such errors more obvious, Assert that we have a
     261                 :      * place to cache even when we take a fast-path exit.
     262                 :      */
     263 GIC     1237587 :     Assert(fcinfo->flinfo != NULL);
     264                 : 
     265                 :     /* Equal OIDs are equal no matter what */
     266         1237587 :     if (arg1 == arg2)
     267         1124068 :         return 0;
     268                 : 
     269 ECB             :     /* Fast path: even-numbered Oids are known to compare correctly */
     270 GIC      113519 :     if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
     271                 :     {
     272 CBC       38482 :         if (arg1 < arg2)
     273            5454 :             return -1;
     274                 :         else
     275 GIC       33028 :             return 1;
     276 ECB             :     }
     277                 : 
     278                 :     /* Locate the typcache entry for the enum type */
     279 CBC       75037 :     tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
     280 GIC       75037 :     if (tcache == NULL)
     281 ECB             :     {
     282                 :         HeapTuple   enum_tup;
     283                 :         Form_pg_enum en;
     284                 :         Oid         typeoid;
     285                 : 
     286                 :         /* Get the OID of the enum type containing arg1 */
     287 GIC           4 :         enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
     288               4 :         if (!HeapTupleIsValid(enum_tup))
     289 UIC           0 :             ereport(ERROR,
     290                 :                     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     291                 :                      errmsg("invalid internal value for enum: %u",
     292                 :                             arg1)));
     293 CBC           4 :         en = (Form_pg_enum) GETSTRUCT(enum_tup);
     294               4 :         typeoid = en->enumtypid;
     295 GBC           4 :         ReleaseSysCache(enum_tup);
     296                 :         /* Now locate and remember the typcache entry */
     297 GIC           4 :         tcache = lookup_type_cache(typeoid, 0);
     298               4 :         fcinfo->flinfo->fn_extra = (void *) tcache;
     299 ECB             :     }
     300                 : 
     301                 :     /* The remaining comparison logic is in typcache.c */
     302 GIC       75037 :     return compare_values_of_enum(tcache, arg1, arg2);
     303 ECB             : }
     304                 : 
     305                 : Datum
     306 GIC        2017 : enum_lt(PG_FUNCTION_ARGS)
     307                 : {
     308 CBC        2017 :     Oid         a = PG_GETARG_OID(0);
     309 GIC        2017 :     Oid         b = PG_GETARG_OID(1);
     310                 : 
     311            2017 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
     312 ECB             : }
     313                 : 
     314                 : Datum
     315 CBC        1105 : enum_le(PG_FUNCTION_ARGS)
     316                 : {
     317            1105 :     Oid         a = PG_GETARG_OID(0);
     318 GIC        1105 :     Oid         b = PG_GETARG_OID(1);
     319                 : 
     320            1105 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
     321 ECB             : }
     322                 : 
     323                 : Datum
     324 CBC        4317 : enum_eq(PG_FUNCTION_ARGS)
     325                 : {
     326            4317 :     Oid         a = PG_GETARG_OID(0);
     327 GIC        4317 :     Oid         b = PG_GETARG_OID(1);
     328                 : 
     329            4317 :     PG_RETURN_BOOL(a == b);
     330 ECB             : }
     331                 : 
     332                 : Datum
     333 CBC          36 : enum_ne(PG_FUNCTION_ARGS)
     334                 : {
     335              36 :     Oid         a = PG_GETARG_OID(0);
     336 GIC          36 :     Oid         b = PG_GETARG_OID(1);
     337                 : 
     338              36 :     PG_RETURN_BOOL(a != b);
     339 ECB             : }
     340                 : 
     341                 : Datum
     342 CBC        1098 : enum_ge(PG_FUNCTION_ARGS)
     343                 : {
     344            1098 :     Oid         a = PG_GETARG_OID(0);
     345 GIC        1098 :     Oid         b = PG_GETARG_OID(1);
     346                 : 
     347            1098 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
     348 ECB             : }
     349                 : 
     350                 : Datum
     351 CBC        1989 : enum_gt(PG_FUNCTION_ARGS)
     352                 : {
     353            1989 :     Oid         a = PG_GETARG_OID(0);
     354 GIC        1989 :     Oid         b = PG_GETARG_OID(1);
     355                 : 
     356            1989 :     PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
     357 ECB             : }
     358                 : 
     359                 : Datum
     360 CBC          15 : enum_smaller(PG_FUNCTION_ARGS)
     361                 : {
     362              15 :     Oid         a = PG_GETARG_OID(0);
     363 GIC          15 :     Oid         b = PG_GETARG_OID(1);
     364                 : 
     365              15 :     PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
     366 ECB             : }
     367                 : 
     368                 : Datum
     369 CBC          48 : enum_larger(PG_FUNCTION_ARGS)
     370                 : {
     371              48 :     Oid         a = PG_GETARG_OID(0);
     372 GIC          48 :     Oid         b = PG_GETARG_OID(1);
     373                 : 
     374              48 :     PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
     375 ECB             : }
     376                 : 
     377                 : Datum
     378 CBC     1231315 : enum_cmp(PG_FUNCTION_ARGS)
     379                 : {
     380         1231315 :     Oid         a = PG_GETARG_OID(0);
     381 GIC     1231315 :     Oid         b = PG_GETARG_OID(1);
     382                 : 
     383         1231315 :     PG_RETURN_INT32(enum_cmp_internal(a, b, fcinfo));
     384 ECB             : }
     385                 : 
     386                 : /* Enum programming support functions */
     387                 : 
     388                 : /*
     389                 :  * enum_endpoint: common code for enum_first/enum_last
     390                 :  */
     391                 : static Oid
     392 GIC          18 : enum_endpoint(Oid enumtypoid, ScanDirection direction)
     393                 : {
     394                 :     Relation    enum_rel;
     395                 :     Relation    enum_idx;
     396                 :     SysScanDesc enum_scan;
     397                 :     HeapTuple   enum_tuple;
     398 ECB             :     ScanKeyData skey;
     399                 :     Oid         minmax;
     400                 : 
     401                 :     /*
     402                 :      * Find the first/last enum member using pg_enum_typid_sortorder_index.
     403                 :      * Note we must not use the syscache.  See comments for RenumberEnumType
     404                 :      * in catalog/pg_enum.c for more info.
     405                 :      */
     406 GIC          18 :     ScanKeyInit(&skey,
     407                 :                 Anum_pg_enum_enumtypid,
     408                 :                 BTEqualStrategyNumber, F_OIDEQ,
     409                 :                 ObjectIdGetDatum(enumtypoid));
     410                 : 
     411              18 :     enum_rel = table_open(EnumRelationId, AccessShareLock);
     412 CBC          18 :     enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
     413 GIC          18 :     enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
     414                 :                                            1, &skey);
     415                 : 
     416              18 :     enum_tuple = systable_getnext_ordered(enum_scan, direction);
     417 CBC          18 :     if (HeapTupleIsValid(enum_tuple))
     418 ECB             :     {
     419                 :         /* check it's safe to use in SQL */
     420 GIC          18 :         check_safe_enum_use(enum_tuple);
     421              15 :         minmax = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
     422 ECB             :     }
     423                 :     else
     424                 :     {
     425                 :         /* should only happen with an empty enum */
     426 LBC           0 :         minmax = InvalidOid;
     427 ECB             :     }
     428                 : 
     429 GIC          15 :     systable_endscan_ordered(enum_scan);
     430              15 :     index_close(enum_idx, AccessShareLock);
     431              15 :     table_close(enum_rel, AccessShareLock);
     432 EUB             : 
     433 GIC          15 :     return minmax;
     434                 : }
     435 ECB             : 
     436                 : Datum
     437 CBC           6 : enum_first(PG_FUNCTION_ARGS)
     438                 : {
     439 ECB             :     Oid         enumtypoid;
     440                 :     Oid         min;
     441                 : 
     442                 :     /*
     443                 :      * We rely on being able to get the specific enum type from the calling
     444                 :      * expression tree.  Notice that the actual value of the argument isn't
     445                 :      * examined at all; in particular it might be NULL.
     446                 :      */
     447 GIC           6 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     448               6 :     if (enumtypoid == InvalidOid)
     449 UIC           0 :         ereport(ERROR,
     450                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     451                 :                  errmsg("could not determine actual enum type")));
     452                 : 
     453 ECB             :     /* Get the OID using the index */
     454 CBC           6 :     min = enum_endpoint(enumtypoid, ForwardScanDirection);
     455 EUB             : 
     456 GIC           6 :     if (!OidIsValid(min))
     457 UIC           0 :         ereport(ERROR,
     458                 :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     459                 :                  errmsg("enum %s contains no values",
     460 ECB             :                         format_type_be(enumtypoid))));
     461                 : 
     462 CBC           6 :     PG_RETURN_OID(min);
     463 EUB             : }
     464                 : 
     465                 : Datum
     466 GIC          12 : enum_last(PG_FUNCTION_ARGS)
     467                 : {
     468 ECB             :     Oid         enumtypoid;
     469                 :     Oid         max;
     470                 : 
     471                 :     /*
     472                 :      * We rely on being able to get the specific enum type from the calling
     473                 :      * expression tree.  Notice that the actual value of the argument isn't
     474                 :      * examined at all; in particular it might be NULL.
     475                 :      */
     476 GIC          12 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     477              12 :     if (enumtypoid == InvalidOid)
     478 UIC           0 :         ereport(ERROR,
     479                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     480                 :                  errmsg("could not determine actual enum type")));
     481                 : 
     482 ECB             :     /* Get the OID using the index */
     483 CBC          12 :     max = enum_endpoint(enumtypoid, BackwardScanDirection);
     484 EUB             : 
     485 GIC           9 :     if (!OidIsValid(max))
     486 UIC           0 :         ereport(ERROR,
     487                 :                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     488                 :                  errmsg("enum %s contains no values",
     489 ECB             :                         format_type_be(enumtypoid))));
     490                 : 
     491 CBC           9 :     PG_RETURN_OID(max);
     492 EUB             : }
     493                 : 
     494                 : /* 2-argument variant of enum_range */
     495                 : Datum
     496 GIC          12 : enum_range_bounds(PG_FUNCTION_ARGS)
     497 ECB             : {
     498                 :     Oid         lower;
     499                 :     Oid         upper;
     500                 :     Oid         enumtypoid;
     501                 : 
     502 CBC          12 :     if (PG_ARGISNULL(0))
     503 GIC           6 :         lower = InvalidOid;
     504                 :     else
     505               6 :         lower = PG_GETARG_OID(0);
     506              12 :     if (PG_ARGISNULL(1))
     507               6 :         upper = InvalidOid;
     508 ECB             :     else
     509 CBC           6 :         upper = PG_GETARG_OID(1);
     510                 : 
     511 ECB             :     /*
     512                 :      * We rely on being able to get the specific enum type from the calling
     513                 :      * expression tree.  The generic type mechanism should have ensured that
     514                 :      * both are of the same type.
     515                 :      */
     516 GIC          12 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     517              12 :     if (enumtypoid == InvalidOid)
     518 UIC           0 :         ereport(ERROR,
     519                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     520                 :                  errmsg("could not determine actual enum type")));
     521                 : 
     522 CBC          12 :     PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
     523 ECB             : }
     524 EUB             : 
     525                 : /* 1-argument variant of enum_range */
     526                 : Datum
     527 GIC          15 : enum_range_all(PG_FUNCTION_ARGS)
     528 ECB             : {
     529                 :     Oid         enumtypoid;
     530                 : 
     531                 :     /*
     532                 :      * We rely on being able to get the specific enum type from the calling
     533                 :      * expression tree.  Notice that the actual value of the argument isn't
     534                 :      * examined at all; in particular it might be NULL.
     535                 :      */
     536 GIC          15 :     enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     537              15 :     if (enumtypoid == InvalidOid)
     538 UIC           0 :         ereport(ERROR,
     539                 :                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     540                 :                  errmsg("could not determine actual enum type")));
     541                 : 
     542 CBC          15 :     PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
     543 ECB             :                                               InvalidOid, InvalidOid));
     544 EUB             : }
     545                 : 
     546                 : static ArrayType *
     547 GIC          27 : enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
     548 ECB             : {
     549                 :     ArrayType  *result;
     550                 :     Relation    enum_rel;
     551                 :     Relation    enum_idx;
     552                 :     SysScanDesc enum_scan;
     553                 :     HeapTuple   enum_tuple;
     554                 :     ScanKeyData skey;
     555                 :     Datum      *elems;
     556                 :     int         max,
     557                 :                 cnt;
     558                 :     bool        left_found;
     559                 : 
     560                 :     /*
     561                 :      * Scan the enum members in order using pg_enum_typid_sortorder_index.
     562                 :      * Note we must not use the syscache.  See comments for RenumberEnumType
     563                 :      * in catalog/pg_enum.c for more info.
     564                 :      */
     565 GIC          27 :     ScanKeyInit(&skey,
     566                 :                 Anum_pg_enum_enumtypid,
     567                 :                 BTEqualStrategyNumber, F_OIDEQ,
     568                 :                 ObjectIdGetDatum(enumtypoid));
     569                 : 
     570              27 :     enum_rel = table_open(EnumRelationId, AccessShareLock);
     571 CBC          27 :     enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
     572 GIC          27 :     enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);
     573                 : 
     574              27 :     max = 64;
     575              27 :     elems = (Datum *) palloc(max * sizeof(Datum));
     576 CBC          27 :     cnt = 0;
     577              27 :     left_found = !OidIsValid(lower);
     578 ECB             : 
     579 GIC         123 :     while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
     580 ECB             :     {
     581 CBC         108 :         Oid         enum_oid = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
     582 ECB             : 
     583 CBC         108 :         if (!left_found && lower == enum_oid)
     584 GIC           6 :             left_found = true;
     585 ECB             : 
     586 GIC         108 :         if (left_found)
     587 ECB             :         {
     588                 :             /* check it's safe to use in SQL */
     589 CBC         102 :             check_safe_enum_use(enum_tuple);
     590 ECB             : 
     591 GIC          96 :             if (cnt >= max)
     592 ECB             :             {
     593 UIC           0 :                 max *= 2;
     594               0 :                 elems = (Datum *) repalloc(elems, max * sizeof(Datum));
     595 ECB             :             }
     596                 : 
     597 CBC          96 :             elems[cnt++] = ObjectIdGetDatum(enum_oid);
     598                 :         }
     599 EUB             : 
     600 GBC         102 :         if (OidIsValid(upper) && upper == enum_oid)
     601 GIC           6 :             break;
     602                 :     }
     603 ECB             : 
     604 GIC          21 :     systable_endscan_ordered(enum_scan);
     605              21 :     index_close(enum_idx, AccessShareLock);
     606 CBC          21 :     table_close(enum_rel, AccessShareLock);
     607 ECB             : 
     608                 :     /* and build the result array */
     609                 :     /* note this hardwires some details about the representation of Oid */
     610 CBC          21 :     result = construct_array(elems, cnt, enumtypoid,
     611 ECB             :                              sizeof(Oid), true, TYPALIGN_INT);
     612                 : 
     613 GIC          21 :     pfree(elems);
     614                 : 
     615              21 :     return result;
     616 ECB             : }
        

Generated by: LCOV version v1.16-55-g56c0a2a