LCOV - code coverage report
Current view: top level - src/backend/utils/adt - enum.c (source / functions) Coverage Total Hit
Test: Code coverage Lines: 81.7 % 257 210
Test Date: 2026-01-26 10:56:24 Functions: 90.5 % 21 19
Legend: Lines:     hit not hit
Branches: + taken - not taken # not executed
Branches: 45.7 % 127 58

             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                 :         170 : check_safe_enum_use(HeapTuple enumval_tup)
      64                 :             : {
      65                 :         170 :         TransactionId xmin;
      66                 :         170 :         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         [ +  + ]:         170 :         if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
      73                 :         154 :                 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                 :          16 :         xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
      80   [ +  +  -  + ]:          16 :         if (!TransactionIdIsInProgress(xmin) &&
      81                 :           2 :                 TransactionIdDidCommit(xmin))
      82                 :           2 :                 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         [ +  + ]:          14 :         if (!EnumUncommitted(en->oid))
      91                 :          10 :                 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   [ +  -  +  - ]:           4 :         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         [ -  + ]:         166 : }
     104                 :             : 
     105                 :             : 
     106                 :             : /* Basic I/O support */
     107                 :             : 
     108                 :             : Datum
     109                 :         132 : enum_in(PG_FUNCTION_ARGS)
     110                 :             : {
     111                 :         132 :         char       *name = PG_GETARG_CSTRING(0);
     112                 :         132 :         Oid                     enumtypoid = PG_GETARG_OID(1);
     113                 :         132 :         Node       *escontext = fcinfo->context;
     114                 :         132 :         Oid                     enumoid;
     115                 :         132 :         HeapTuple       tup;
     116                 :             : 
     117                 :             :         /* must check length to prevent Assert failure within SearchSysCache */
     118         [ +  + ]:         132 :         if (strlen(name) >= NAMEDATALEN)
     119         [ -  + ]:           1 :                 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                 :         131 :         tup = SearchSysCache2(ENUMTYPOIDNAME,
     126                 :         131 :                                                   ObjectIdGetDatum(enumtypoid),
     127                 :         131 :                                                   CStringGetDatum(name));
     128         [ +  + ]:         131 :         if (!HeapTupleIsValid(tup))
     129         [ -  + ]:           2 :                 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                 :         129 :         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                 :         129 :         enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
     148                 :             : 
     149                 :         129 :         ReleaseSysCache(tup);
     150                 :             : 
     151                 :         129 :         PG_RETURN_OID(enumoid);
     152                 :         132 : }
     153                 :             : 
     154                 :             : Datum
     155                 :         139 : enum_out(PG_FUNCTION_ARGS)
     156                 :             : {
     157                 :         139 :         Oid                     enumval = PG_GETARG_OID(0);
     158                 :         139 :         char       *result;
     159                 :         139 :         HeapTuple       tup;
     160                 :         139 :         Form_pg_enum en;
     161                 :             : 
     162                 :         139 :         tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
     163         [ +  - ]:         139 :         if (!HeapTupleIsValid(tup))
     164   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     165                 :             :                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     166                 :             :                                  errmsg("invalid internal value for enum: %u",
     167                 :             :                                                 enumval)));
     168                 :         139 :         en = (Form_pg_enum) GETSTRUCT(tup);
     169                 :             : 
     170                 :         139 :         result = pstrdup(NameStr(en->enumlabel));
     171                 :             : 
     172                 :         139 :         ReleaseSysCache(tup);
     173                 :             : 
     174                 :         278 :         PG_RETURN_CSTRING(result);
     175                 :         139 : }
     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                 :           0 :         Oid                     enumoid;
     184                 :           0 :         HeapTuple       tup;
     185                 :           0 :         char       *name;
     186                 :           0 :         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                 :           0 :                                                   ObjectIdGetDatum(enumtypoid),
     200                 :           0 :                                                   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                 :           0 : }
     219                 :             : 
     220                 :             : Datum
     221                 :           0 : enum_send(PG_FUNCTION_ARGS)
     222                 :             : {
     223                 :           0 :         Oid                     enumval = PG_GETARG_OID(0);
     224                 :           0 :         StringInfoData buf;
     225                 :           0 :         HeapTuple       tup;
     226                 :           0 :         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                 :           0 : }
     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                 :         134 : enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
     253                 :             : {
     254                 :         134 :         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         [ +  - ]:         134 :         Assert(fcinfo->flinfo != NULL);
     264                 :             : 
     265                 :             :         /* Equal OIDs are equal no matter what */
     266         [ +  + ]:         134 :         if (arg1 == arg2)
     267                 :          15 :                 return 0;
     268                 :             : 
     269                 :             :         /* Fast path: even-numbered Oids are known to compare correctly */
     270   [ +  +  +  + ]:         119 :         if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
     271                 :             :         {
     272         [ +  + ]:         108 :                 if (arg1 < arg2)
     273                 :          77 :                         return -1;
     274                 :             :                 else
     275                 :          31 :                         return 1;
     276                 :             :         }
     277                 :             : 
     278                 :             :         /* Locate the typcache entry for the enum type */
     279                 :          11 :         tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
     280         [ +  + ]:          11 :         if (tcache == NULL)
     281                 :             :         {
     282                 :           1 :                 HeapTuple       enum_tup;
     283                 :           1 :                 Form_pg_enum en;
     284                 :           1 :                 Oid                     typeoid;
     285                 :             : 
     286                 :             :                 /* Get the OID of the enum type containing arg1 */
     287                 :           1 :                 enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
     288         [ +  - ]:           1 :                 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                 :           1 :                 en = (Form_pg_enum) GETSTRUCT(enum_tup);
     294                 :           1 :                 typeoid = en->enumtypid;
     295                 :           1 :                 ReleaseSysCache(enum_tup);
     296                 :             :                 /* Now locate and remember the typcache entry */
     297                 :           1 :                 tcache = lookup_type_cache(typeoid, 0);
     298                 :           1 :                 fcinfo->flinfo->fn_extra = tcache;
     299                 :           1 :         }
     300                 :             : 
     301                 :             :         /* The remaining comparison logic is in typcache.c */
     302                 :          11 :         return compare_values_of_enum(tcache, arg1, arg2);
     303                 :         134 : }
     304                 :             : 
     305                 :             : Datum
     306                 :          19 : enum_lt(PG_FUNCTION_ARGS)
     307                 :             : {
     308                 :          19 :         Oid                     a = PG_GETARG_OID(0);
     309                 :          19 :         Oid                     b = PG_GETARG_OID(1);
     310                 :             : 
     311                 :          38 :         PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
     312                 :          19 : }
     313                 :             : 
     314                 :             : Datum
     315                 :          11 : enum_le(PG_FUNCTION_ARGS)
     316                 :             : {
     317                 :          11 :         Oid                     a = PG_GETARG_OID(0);
     318                 :          11 :         Oid                     b = PG_GETARG_OID(1);
     319                 :             : 
     320                 :          22 :         PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
     321                 :          11 : }
     322                 :             : 
     323                 :             : Datum
     324                 :          34 : enum_eq(PG_FUNCTION_ARGS)
     325                 :             : {
     326                 :          34 :         Oid                     a = PG_GETARG_OID(0);
     327                 :          34 :         Oid                     b = PG_GETARG_OID(1);
     328                 :             : 
     329                 :          68 :         PG_RETURN_BOOL(a == b);
     330                 :          34 : }
     331                 :             : 
     332                 :             : Datum
     333                 :          12 : enum_ne(PG_FUNCTION_ARGS)
     334                 :             : {
     335                 :          12 :         Oid                     a = PG_GETARG_OID(0);
     336                 :          12 :         Oid                     b = PG_GETARG_OID(1);
     337                 :             : 
     338                 :          24 :         PG_RETURN_BOOL(a != b);
     339                 :          12 : }
     340                 :             : 
     341                 :             : Datum
     342                 :          10 : enum_ge(PG_FUNCTION_ARGS)
     343                 :             : {
     344                 :          10 :         Oid                     a = PG_GETARG_OID(0);
     345                 :          10 :         Oid                     b = PG_GETARG_OID(1);
     346                 :             : 
     347                 :          20 :         PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
     348                 :          10 : }
     349                 :             : 
     350                 :             : Datum
     351                 :           9 : enum_gt(PG_FUNCTION_ARGS)
     352                 :             : {
     353                 :           9 :         Oid                     a = PG_GETARG_OID(0);
     354                 :           9 :         Oid                     b = PG_GETARG_OID(1);
     355                 :             : 
     356                 :          18 :         PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
     357                 :           9 : }
     358                 :             : 
     359                 :             : Datum
     360                 :           5 : enum_smaller(PG_FUNCTION_ARGS)
     361                 :             : {
     362                 :           5 :         Oid                     a = PG_GETARG_OID(0);
     363                 :           5 :         Oid                     b = PG_GETARG_OID(1);
     364                 :             : 
     365         [ -  + ]:           5 :         PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
     366                 :           5 : }
     367                 :             : 
     368                 :             : Datum
     369                 :          16 : enum_larger(PG_FUNCTION_ARGS)
     370                 :             : {
     371                 :          16 :         Oid                     a = PG_GETARG_OID(0);
     372                 :          16 :         Oid                     b = PG_GETARG_OID(1);
     373                 :             : 
     374         [ +  + ]:          16 :         PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
     375                 :          16 : }
     376                 :             : 
     377                 :             : Datum
     378                 :          64 : enum_cmp(PG_FUNCTION_ARGS)
     379                 :             : {
     380                 :          64 :         Oid                     a = PG_GETARG_OID(0);
     381                 :          64 :         Oid                     b = PG_GETARG_OID(1);
     382                 :             : 
     383                 :         128 :         PG_RETURN_INT32(enum_cmp_internal(a, b, fcinfo));
     384                 :          64 : }
     385                 :             : 
     386                 :             : /* Enum programming support functions */
     387                 :             : 
     388                 :             : /*
     389                 :             :  * enum_endpoint: common code for enum_first/enum_last
     390                 :             :  */
     391                 :             : static Oid
     392                 :           6 : enum_endpoint(Oid enumtypoid, ScanDirection direction)
     393                 :             : {
     394                 :           6 :         Relation        enum_rel;
     395                 :           6 :         Relation        enum_idx;
     396                 :           6 :         SysScanDesc enum_scan;
     397                 :           6 :         HeapTuple       enum_tuple;
     398                 :           6 :         ScanKeyData skey;
     399                 :           6 :         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                 :           6 :         ScanKeyInit(&skey,
     407                 :             :                                 Anum_pg_enum_enumtypid,
     408                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
     409                 :           6 :                                 ObjectIdGetDatum(enumtypoid));
     410                 :             : 
     411                 :           6 :         enum_rel = table_open(EnumRelationId, AccessShareLock);
     412                 :           6 :         enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
     413                 :           6 :         enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
     414                 :             :                                                                                    1, &skey);
     415                 :             : 
     416                 :           6 :         enum_tuple = systable_getnext_ordered(enum_scan, direction);
     417         [ +  - ]:           6 :         if (HeapTupleIsValid(enum_tuple))
     418                 :             :         {
     419                 :             :                 /* check it's safe to use in SQL */
     420                 :           6 :                 check_safe_enum_use(enum_tuple);
     421                 :           6 :                 minmax = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
     422                 :           6 :         }
     423                 :             :         else
     424                 :             :         {
     425                 :             :                 /* should only happen with an empty enum */
     426                 :           0 :                 minmax = InvalidOid;
     427                 :             :         }
     428                 :             : 
     429                 :           6 :         systable_endscan_ordered(enum_scan);
     430                 :           6 :         index_close(enum_idx, AccessShareLock);
     431                 :           6 :         table_close(enum_rel, AccessShareLock);
     432                 :             : 
     433                 :          12 :         return minmax;
     434                 :           6 : }
     435                 :             : 
     436                 :             : Datum
     437                 :           2 : enum_first(PG_FUNCTION_ARGS)
     438                 :             : {
     439                 :           2 :         Oid                     enumtypoid;
     440                 :           2 :         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                 :           2 :         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     448         [ +  - ]:           2 :         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                 :           2 :         min = enum_endpoint(enumtypoid, ForwardScanDirection);
     455                 :             : 
     456         [ +  - ]:           2 :         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                 :           4 :         PG_RETURN_OID(min);
     463                 :           2 : }
     464                 :             : 
     465                 :             : Datum
     466                 :           4 : enum_last(PG_FUNCTION_ARGS)
     467                 :             : {
     468                 :           4 :         Oid                     enumtypoid;
     469                 :           4 :         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                 :           4 :         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     477         [ +  - ]:           4 :         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                 :           4 :         max = enum_endpoint(enumtypoid, BackwardScanDirection);
     484                 :             : 
     485         [ +  - ]:           4 :         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                 :           8 :         PG_RETURN_OID(max);
     492                 :           4 : }
     493                 :             : 
     494                 :             : /* 2-argument variant of enum_range */
     495                 :             : Datum
     496                 :           4 : enum_range_bounds(PG_FUNCTION_ARGS)
     497                 :             : {
     498                 :           4 :         Oid                     lower;
     499                 :           4 :         Oid                     upper;
     500                 :           4 :         Oid                     enumtypoid;
     501                 :             : 
     502         [ +  + ]:           4 :         if (PG_ARGISNULL(0))
     503                 :           2 :                 lower = InvalidOid;
     504                 :             :         else
     505                 :           2 :                 lower = PG_GETARG_OID(0);
     506         [ +  + ]:           4 :         if (PG_ARGISNULL(1))
     507                 :           2 :                 upper = InvalidOid;
     508                 :             :         else
     509                 :           2 :                 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                 :           4 :         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     517         [ +  - ]:           4 :         if (enumtypoid == InvalidOid)
     518   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     519                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     520                 :             :                                  errmsg("could not determine actual enum type")));
     521                 :             : 
     522                 :           8 :         PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
     523                 :           4 : }
     524                 :             : 
     525                 :             : /* 1-argument variant of enum_range */
     526                 :             : Datum
     527                 :           5 : enum_range_all(PG_FUNCTION_ARGS)
     528                 :             : {
     529                 :           5 :         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                 :           5 :         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
     537         [ +  - ]:           5 :         if (enumtypoid == InvalidOid)
     538   [ #  #  #  # ]:           0 :                 ereport(ERROR,
     539                 :             :                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     540                 :             :                                  errmsg("could not determine actual enum type")));
     541                 :             : 
     542                 :          10 :         PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
     543                 :             :                                                                                           InvalidOid, InvalidOid));
     544                 :           5 : }
     545                 :             : 
     546                 :             : static ArrayType *
     547                 :           9 : enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
     548                 :             : {
     549                 :           9 :         ArrayType  *result;
     550                 :           9 :         Relation        enum_rel;
     551                 :           9 :         Relation        enum_idx;
     552                 :           9 :         SysScanDesc enum_scan;
     553                 :           9 :         HeapTuple       enum_tuple;
     554                 :           9 :         ScanKeyData skey;
     555                 :           9 :         Datum      *elems;
     556                 :           9 :         int                     max,
     557                 :             :                                 cnt;
     558                 :           9 :         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                 :           9 :         ScanKeyInit(&skey,
     566                 :             :                                 Anum_pg_enum_enumtypid,
     567                 :             :                                 BTEqualStrategyNumber, F_OIDEQ,
     568                 :           9 :                                 ObjectIdGetDatum(enumtypoid));
     569                 :             : 
     570                 :           9 :         enum_rel = table_open(EnumRelationId, AccessShareLock);
     571                 :           9 :         enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
     572                 :           9 :         enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);
     573                 :             : 
     574                 :           9 :         max = 64;
     575                 :           9 :         elems = (Datum *) palloc(max * sizeof(Datum));
     576                 :           9 :         cnt = 0;
     577                 :           9 :         left_found = !OidIsValid(lower);
     578                 :             : 
     579         [ +  + ]:          43 :         while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
     580                 :             :         {
     581                 :          36 :                 Oid                     enum_oid = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
     582                 :             : 
     583   [ +  +  +  + ]:          36 :                 if (!left_found && lower == enum_oid)
     584                 :           2 :                         left_found = true;
     585                 :             : 
     586         [ +  + ]:          36 :                 if (left_found)
     587                 :             :                 {
     588                 :             :                         /* check it's safe to use in SQL */
     589                 :          34 :                         check_safe_enum_use(enum_tuple);
     590                 :             : 
     591         [ +  - ]:          34 :                         if (cnt >= max)
     592                 :             :                         {
     593                 :           0 :                                 max *= 2;
     594                 :           0 :                                 elems = (Datum *) repalloc(elems, max * sizeof(Datum));
     595                 :           0 :                         }
     596                 :             : 
     597                 :          34 :                         elems[cnt++] = ObjectIdGetDatum(enum_oid);
     598                 :          34 :                 }
     599                 :             : 
     600   [ +  +  +  + ]:          36 :                 if (OidIsValid(upper) && upper == enum_oid)
     601                 :           2 :                         break;
     602      [ -  +  + ]:          36 :         }
     603                 :             : 
     604                 :           9 :         systable_endscan_ordered(enum_scan);
     605                 :           9 :         index_close(enum_idx, AccessShareLock);
     606                 :           9 :         table_close(enum_rel, AccessShareLock);
     607                 :             : 
     608                 :             :         /* and build the result array */
     609                 :             :         /* note this hardwires some details about the representation of Oid */
     610                 :           9 :         result = construct_array(elems, cnt, enumtypoid,
     611                 :             :                                                          sizeof(Oid), true, TYPALIGN_INT);
     612                 :             : 
     613                 :           9 :         pfree(elems);
     614                 :             : 
     615                 :          18 :         return result;
     616                 :           9 : }
        

Generated by: LCOV version 2.3.2-1