Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Postgres/src/backend/replication/logical/   (Postgres Database Version 18.4©)  Datei vom 11.4.2026 mit Größe 56 kB image not shown  

SSL enum.c  Interaktion und
Portierbarkeitunbekannt

 
/*-------------------------------------------------------------------------
 *
 * enum.c
 *   I/O functions, operators, aggregates etc for enum types
 *
 * Copyright (c) 2006-2025, PostgreSQL Global Development Group
 *
 *
 * IDENTIFICATION
 *   src/backend/utils/adt/enum.c
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"

#include "access/genam.h"
#include "access/htup_details.h"
#include "access/table.h"
#include "catalog/pg_enum.h"
#include "libpq/pqformat.h"
#include "storage/procarray.h"
#include "utils/array.h"
#include "utils/builtins.h"
#include "utils/fmgroids.h"
#include "utils/syscache.h"
#include "utils/typcache.h"


static Oid enum_endpoint(Oid enumtypoid, ScanDirection direction);
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);


/*
 * Disallow use of an uncommitted pg_enum tuple.
 *
 * We need to make sure that uncommitted enum values don't get into indexes.
 * If they did, and if we then rolled back the pg_enum addition, we'd have
 * broken the index because value comparisons will not work reliably without
 * an underlying pg_enum entry.  (Note that removal of the heap entry
 * containing an enum value is not sufficient to ensure that it doesn't appear
 * in upper levels of indexes.)  To do this we prevent an uncommitted row from
 * being used for any SQL-level purpose.  This is stronger than necessary,
 * since the value might not be getting inserted into a table or there might
 * be no index on its column, but it's easy to enforce centrally.
 *
 * However, it's okay to allow use of uncommitted values belonging to enum
 * types that were themselves created in the same transaction, because then
 * any such index would also be new and would go away altogether on rollback.
 * We don't implement that fully right now, but we do allow free use of enum
 * values created during CREATE TYPE AS ENUM, which are surely of the same
 * lifespan as the enum type.  (This case is required by "pg_restore -1".)
 * Values added by ALTER TYPE ADD VALUE are also allowed if the enum type
 * is known to have been created earlier in the same transaction.  (Note that
 * we have to track that explicitly; comparing tuple xmins is insufficient,
 * because the type tuple might have been updated in the current transaction.
 * Subtransactions also create hazards to be accounted for; currently,
 * pg_enum.c only handles ADD VALUE at the outermost transaction level.)
 *
 * This function needs to be called (directly or indirectly) in any of the
 * functions below that could return an enum value to SQL operations.
 */

static void
check_safe_enum_use(HeapTuple enumval_tup)
{
 TransactionId xmin;
 Form_pg_enum en = (Form_pg_enum) GETSTRUCT(enumval_tup);

 /*
  * If the row is hinted as committed, it's surely safe.  This provides a
  * fast path for all normal use-cases.
 */

 if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
  return;

 /*
  * Usually, a row would get hinted as committed when it's read or loaded
  * into syscache; but just in case not, let's check the xmin directly.
 */

 xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
 if (!TransactionIdIsInProgress(xmin) &&
  TransactionIdDidCommit(xmin))
  return;

 /*
  * Check if the enum value is listed as uncommitted.  If not, it's safe,
  * because it can't be shorter-lived than its owning type.  (This'd also
  * be false for values made by other transactions; but the previous tests
  * should have handled all of those.)
 */

 if (!EnumUncommitted(en->oid))
  return;

 /*
  * There might well be other tests we could do here to narrow down the
  * unsafe conditions, but for now just raise an exception.
 */

 ereport(ERROR,
   (errcode(ERRCODE_UNSAFE_NEW_ENUM_VALUE_USAGE),
    errmsg("unsafe use of new value \"%s\" of enum type %s",
     NameStr(en->enumlabel),
     format_type_be(en->enumtypid)),
    errhint("New enum values must be committed before they can be used.")));
}


/* Basic I/O support */

Datum
enum_in(PG_FUNCTION_ARGS)
{
 char    *name = PG_GETARG_CSTRING(0);
 Oid   enumtypoid = PG_GETARG_OID(1);
 Node    *escontext = fcinfo->context;
 Oid   enumoid;
 HeapTuple tup;

 /* must check length to prevent Assert failure within SearchSysCache */
 if (strlen(name) >= NAMEDATALEN)
  ereturn(escontext, (Datum) 0,
    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     errmsg("invalid input value for enum %s: \"%s\"",
      format_type_be(enumtypoid),
      name)));

 tup = SearchSysCache2(ENUMTYPOIDNAME,
        ObjectIdGetDatum(enumtypoid),
        CStringGetDatum(name));
 if (!HeapTupleIsValid(tup))
  ereturn(escontext, (Datum) 0,
    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     errmsg("invalid input value for enum %s: \"%s\"",
      format_type_be(enumtypoid),
      name)));

 /*
  * Check it's safe to use in SQL.  Perhaps we should take the trouble to
  * report "unsafe use" softly; but it's unclear that it's worth the
  * trouble, or indeed that that is a legitimate bad-input case at all
  * rather than an implementation shortcoming.
 */

 check_safe_enum_use(tup);

 /*
  * This comes from pg_enum.oid and stores system oids in user tables. This
  * oid must be preserved by binary upgrades.
 */

 enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;

 ReleaseSysCache(tup);

 PG_RETURN_OID(enumoid);
}

Datum
enum_out(PG_FUNCTION_ARGS)
{
 Oid   enumval = PG_GETARG_OID(0);
 char    *result;
 HeapTuple tup;
 Form_pg_enum en;

 tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
 if (!HeapTupleIsValid(tup))
  ereport(ERROR,
    (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     errmsg("invalid internal value for enum: %u",
      enumval)));
 en = (Form_pg_enum) GETSTRUCT(tup);

 result = pstrdup(NameStr(en->enumlabel));

 ReleaseSysCache(tup);

 PG_RETURN_CSTRING(result);
}

/* Binary I/O support */
Datum
enum_recv(PG_FUNCTION_ARGS)
{
 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
 Oid   enumtypoid = PG_GETARG_OID(1);
 Oid   enumoid;
 HeapTuple tup;
 char    *name;
 int   nbytes;

 name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);

 /* must check length to prevent Assert failure within SearchSysCache */
 if (strlen(name) >= NAMEDATALEN)
  ereport(ERROR,
    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     errmsg("invalid input value for enum %s: \"%s\"",
      format_type_be(enumtypoid),
      name)));

 tup = SearchSysCache2(ENUMTYPOIDNAME,
        ObjectIdGetDatum(enumtypoid),
        CStringGetDatum(name));
 if (!HeapTupleIsValid(tup))
  ereport(ERROR,
    (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
     errmsg("invalid input value for enum %s: \"%s\"",
      format_type_be(enumtypoid),
      name)));

 /* check it's safe to use in SQL */
 check_safe_enum_use(tup);

 enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;

 ReleaseSysCache(tup);

 pfree(name);

 PG_RETURN_OID(enumoid);
}

Datum
enum_send(PG_FUNCTION_ARGS)
{
 Oid   enumval = PG_GETARG_OID(0);
 StringInfoData buf;
 HeapTuple tup;
 Form_pg_enum en;

 tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
 if (!HeapTupleIsValid(tup))
  ereport(ERROR,
    (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
     errmsg("invalid internal value for enum: %u",
      enumval)));
 en = (Form_pg_enum) GETSTRUCT(tup);

 pq_begintypsend(&buf);
 pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));

 ReleaseSysCache(tup);

 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
}

/* Comparison functions and related */

/*
 * enum_cmp_internal is the common engine for all the visible comparison
 * functions, except for enum_eq and enum_ne which can just check for OID
 * equality directly.
 */

static int
enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
{
 TypeCacheEntry *tcache;

 /*
  * We don't need the typcache except in the hopefully-uncommon case that
  * one or both Oids are odd.  This means that cursory testing of code that
  * fails to pass flinfo to an enum comparison function might not disclose
  * the oversight.  To make such errors more obvious, Assert that we have a
  * place to cache even when we take a fast-path exit.
 */

 Assert(fcinfo->flinfo != NULL);

 /* Equal OIDs are equal no matter what */
 if (arg1 == arg2)
  return 0;

 /* Fast path: even-numbered Oids are known to compare correctly */
 if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
 {
  if (arg1 < arg2)
   return -1;
  else
   return 1;
 }

 /* Locate the typcache entry for the enum type */
 tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
 if (tcache == NULL)
 {
  HeapTuple enum_tup;
  Form_pg_enum en;
  Oid   typeoid;

  /* Get the OID of the enum type containing arg1 */
  enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
  if (!HeapTupleIsValid(enum_tup))
   ereport(ERROR,
     (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
      errmsg("invalid internal value for enum: %u",
       arg1)));
  en = (Form_pg_enum) GETSTRUCT(enum_tup);
  typeoid = en->enumtypid;
  ReleaseSysCache(enum_tup);
  /* Now locate and remember the typcache entry */
  tcache = lookup_type_cache(typeoid, 0);
  fcinfo->flinfo->fn_extra = tcache;
 }

 /* The remaining comparison logic is in typcache.c */
 return compare_values_of_enum(tcache, arg1, arg2);
}

Datum
enum_lt(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
}

Datum
enum_le(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
}

Datum
enum_eq(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_BOOL(a == b);
}

Datum
enum_ne(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_BOOL(a != b);
}

Datum
enum_ge(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
}

Datum
enum_gt(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
}

Datum
enum_smaller(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
}

Datum
enum_larger(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
}

Datum
enum_cmp(PG_FUNCTION_ARGS)
{
 Oid   a = PG_GETARG_OID(0);
 Oid   b = PG_GETARG_OID(1);

 PG_RETURN_INT32(enum_cmp_internal(a, b, fcinfo));
}

/* Enum programming support functions */

/*
 * enum_endpoint: common code for enum_first/enum_last
 */

static Oid
enum_endpoint(Oid enumtypoid, ScanDirection direction)
{
 Relation enum_rel;
 Relation enum_idx;
 SysScanDesc enum_scan;
 HeapTuple enum_tuple;
 ScanKeyData skey;
 Oid   minmax;

 /*
  * Find the first/last enum member using pg_enum_typid_sortorder_index.
  * Note we must not use the syscache.  See comments for RenumberEnumType
  * in catalog/pg_enum.c for more info.
 */

 ScanKeyInit(&skey,
    Anum_pg_enum_enumtypid,
    BTEqualStrategyNumber, F_OIDEQ,
    ObjectIdGetDatum(enumtypoid));

 enum_rel = table_open(EnumRelationId, AccessShareLock);
 enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
 enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
             1, &skey);

 enum_tuple = systable_getnext_ordered(enum_scan, direction);
 if (HeapTupleIsValid(enum_tuple))
 {
  /* check it's safe to use in SQL */
  check_safe_enum_use(enum_tuple);
  minmax = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
 }
 else
 {
  /* should only happen with an empty enum */
  minmax = InvalidOid;
 }

 systable_endscan_ordered(enum_scan);
 index_close(enum_idx, AccessShareLock);
 table_close(enum_rel, AccessShareLock);

 return minmax;
}

Datum
enum_first(PG_FUNCTION_ARGS)
{
 Oid   enumtypoid;
 Oid   min;

 /*
  * We rely on being able to get the specific enum type from the calling
  * expression tree.  Notice that the actual value of the argument isn't
  * examined at all; in particular it might be NULL.
 */

 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
 if (enumtypoid == InvalidOid)
  ereport(ERROR,
    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     errmsg("could not determine actual enum type")));

 /* Get the OID using the index */
 min = enum_endpoint(enumtypoid, ForwardScanDirection);

 if (!OidIsValid(min))
  ereport(ERROR,
    (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     errmsg("enum %s contains no values",
      format_type_be(enumtypoid))));

 PG_RETURN_OID(min);
}

Datum
enum_last(PG_FUNCTION_ARGS)
{
 Oid   enumtypoid;
 Oid   max;

 /*
  * We rely on being able to get the specific enum type from the calling
  * expression tree.  Notice that the actual value of the argument isn't
  * examined at all; in particular it might be NULL.
 */

 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
 if (enumtypoid == InvalidOid)
  ereport(ERROR,
    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     errmsg("could not determine actual enum type")));

 /* Get the OID using the index */
 max = enum_endpoint(enumtypoid, BackwardScanDirection);

 if (!OidIsValid(max))
  ereport(ERROR,
    (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
     errmsg("enum %s contains no values",
      format_type_be(enumtypoid))));

 PG_RETURN_OID(max);
}

/* 2-argument variant of enum_range */
Datum
enum_range_bounds(PG_FUNCTION_ARGS)
{
 Oid   lower;
 Oid   upper;
 Oid   enumtypoid;

 if (PG_ARGISNULL(0))
  lower = InvalidOid;
 else
  lower = PG_GETARG_OID(0);
 if (PG_ARGISNULL(1))
  upper = InvalidOid;
 else
  upper = PG_GETARG_OID(1);

 /*
  * We rely on being able to get the specific enum type from the calling
  * expression tree.  The generic type mechanism should have ensured that
  * both are of the same type.
 */

 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
 if (enumtypoid == InvalidOid)
  ereport(ERROR,
    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     errmsg("could not determine actual enum type")));

 PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
}

/* 1-argument variant of enum_range */
Datum
enum_range_all(PG_FUNCTION_ARGS)
{
 Oid   enumtypoid;

 /*
  * We rely on being able to get the specific enum type from the calling
  * expression tree.  Notice that the actual value of the argument isn't
  * examined at all; in particular it might be NULL.
 */

 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
 if (enumtypoid == InvalidOid)
  ereport(ERROR,
    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
     errmsg("could not determine actual enum type")));

 PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
             InvalidOid, InvalidOid));
}

static ArrayType *
enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
{
 ArrayType  *result;
 Relation enum_rel;
 Relation enum_idx;
 SysScanDesc enum_scan;
 HeapTuple enum_tuple;
 ScanKeyData skey;
 Datum    *elems;
 int   max,
    cnt;
 bool  left_found;

 /*
  * Scan the enum members in order using pg_enum_typid_sortorder_index.
  * Note we must not use the syscache.  See comments for RenumberEnumType
  * in catalog/pg_enum.c for more info.
 */

 ScanKeyInit(&skey,
    Anum_pg_enum_enumtypid,
    BTEqualStrategyNumber, F_OIDEQ,
    ObjectIdGetDatum(enumtypoid));

 enum_rel = table_open(EnumRelationId, AccessShareLock);
 enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
 enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);

 max = 64;
 elems = (Datum *) palloc(max * sizeof(Datum));
 cnt = 0;
 left_found = !OidIsValid(lower);

 while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
 {
  Oid   enum_oid = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;

  if (!left_found && lower == enum_oid)
   left_found = true;

  if (left_found)
  {
   /* check it's safe to use in SQL */
   check_safe_enum_use(enum_tuple);

   if (cnt >= max)
   {
    max *= 2;
    elems = (Datum *) repalloc(elems, max * sizeof(Datum));
   }

   elems[cnt++] = ObjectIdGetDatum(enum_oid);
  }

  if (OidIsValid(upper) && upper == enum_oid)
   break;
 }

 systable_endscan_ordered(enum_scan);
 index_close(enum_idx, AccessShareLock);
 table_close(enum_rel, AccessShareLock);

 /* and build the result array */
 /* note this hardwires some details about the representation of Oid */
 result = construct_array(elems, cnt, enumtypoid,
        sizeof(Oid), true, TYPALIGN_INT);

 pfree(elems);

 return result;
}

Messung V0.5 in Prozent
C=96 H=94 G=94

[Verzeichnis aufwärts0.25unsichere VerbindungÜbersetzung europäischer Sprachen durch Browser2026-09-28]