LCOV - code coverage report
Current view: top level - src/backend/utils/adt - enum.c (source / functions) Coverage Total Hit
Test: PostgreSQL 20devel Lines: 81.0 % 184 149
Test Date: 2026-07-22 12:15:41 Functions: 90.5 % 21 19
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 56.2 % 96 54

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

Generated by: LCOV version 2.0-1