1/*-------------------------------------------------------------------------
4 * I/O functions, operators, aggregates etc for enum types
6 * Copyright (c) 2006-2025, PostgreSQL Global Development Group
10 * src/backend/utils/adt/enum.c
12 *-------------------------------------------------------------------------
24#include "utils/fmgroids.h"
34 * Disallow use of an uncommitted pg_enum tuple.
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.
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.)
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.
69 * If the row is hinted as committed, it's surely safe. This provides a
70 * fast path for all normal use-cases.
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.
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.)
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.
98 (
errcode(ERRCODE_UNSAFE_NEW_ENUM_VALUE_USAGE),
99 errmsg(
"unsafe use of new value \"%s\" of enum type %s",
102 errhint(
"New enum values must be committed before they can be used.")));
106/* Basic I/O support */
113 Node *escontext = fcinfo->context;
117 /* must check length to prevent Assert failure within SearchSysCache */
120 (
errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
121 errmsg(
"invalid input value for enum %s: \"%s\"",
130 (
errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
131 errmsg(
"invalid input value for enum %s: \"%s\"",
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.
144 * This comes from pg_enum.oid and stores system oids in user tables. This
145 * oid must be preserved by binary upgrades.
165 (
errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
166 errmsg(
"invalid internal value for enum: %u",
177/* Binary I/O support */
190 /* must check length to prevent Assert failure within SearchSysCache */
193 (
errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
194 errmsg(
"invalid input value for enum %s: \"%s\"",
203 (
errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
204 errmsg(
"invalid input value for enum %s: \"%s\"",
208 /* check it's safe to use in SQL */
231 (
errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
232 errmsg(
"invalid internal value for enum: %u",
244/* Comparison functions and related */
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
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.
265 /* Equal OIDs are equal no matter what */
269 /* Fast path: even-numbered Oids are known to compare correctly */
270 if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
278 /* Locate the typcache entry for the enum type */
286 /* Get the OID of the enum type containing arg1 */
290 (
errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
291 errmsg(
"invalid internal value for enum: %u",
294 typeoid = en->enumtypid;
296 /* Now locate and remember the typcache entry */
301 /* The remaining comparison logic is in typcache.c */
386/* Enum programming support functions */
389 * enum_endpoint: common code for enum_first/enum_last
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.
407 Anum_pg_enum_enumtypid,
419 /* check it's safe to use in SQL */
425 /* should only happen with an empty enum */
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.
450 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
451 errmsg(
"could not determine actual enum type")));
453 /* Get the OID using the index */
458 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
459 errmsg(
"enum %s contains no values",
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.
479 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
480 errmsg(
"could not determine actual enum type")));
482 /* Get the OID using the index */
487 (
errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
488 errmsg(
"enum %s contains no values",
494/* 2-argument variant of enum_range */
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.
519 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
520 errmsg(
"could not determine actual enum type")));
525/* 1-argument variant of enum_range */
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.
539 (
errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
540 errmsg(
"could not determine actual enum type")));
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.
566 Anum_pg_enum_enumtypid,
583 if (!left_found &&
lower == enum_oid)
588 /* check it's safe to use in SQL */
608 /* and build the result array */
609 /* note this hardwires some details about the representation of Oid */
611 sizeof(
Oid),
true, TYPALIGN_INT);
#define PG_RETURN_ARRAYTYPE_P(x)
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
#define OidIsValid(objectId)
int errhint(const char *fmt,...)
int errcode(int sqlerrcode)
int errmsg(const char *fmt,...)
#define ereturn(context, dummy_value,...)
#define ereport(elevel,...)
Datum enum_range_all(PG_FUNCTION_ARGS)
Datum enum_last(PG_FUNCTION_ARGS)
static void check_safe_enum_use(HeapTuple enumval_tup)
Datum enum_gt(PG_FUNCTION_ARGS)
Datum enum_first(PG_FUNCTION_ARGS)
static Oid enum_endpoint(Oid enumtypoid, ScanDirection direction)
Datum enum_smaller(PG_FUNCTION_ARGS)
Datum enum_lt(PG_FUNCTION_ARGS)
Datum enum_cmp(PG_FUNCTION_ARGS)
Datum enum_eq(PG_FUNCTION_ARGS)
Datum enum_range_bounds(PG_FUNCTION_ARGS)
Datum enum_send(PG_FUNCTION_ARGS)
static ArrayType * enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
Datum enum_ge(PG_FUNCTION_ARGS)
Datum enum_in(PG_FUNCTION_ARGS)
Datum enum_recv(PG_FUNCTION_ARGS)
static int enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
Datum enum_out(PG_FUNCTION_ARGS)
Datum enum_larger(PG_FUNCTION_ARGS)
Datum enum_le(PG_FUNCTION_ARGS)
Datum enum_ne(PG_FUNCTION_ARGS)
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
#define PG_RETURN_BYTEA_P(x)
#define PG_GETARG_POINTER(n)
#define PG_RETURN_CSTRING(x)
#define PG_GETARG_CSTRING(n)
#define PG_RETURN_INT32(x)
#define PG_RETURN_BOOL(x)
SysScanDesc systable_beginscan_ordered(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, ScanKey key)
void systable_endscan_ordered(SysScanDesc sysscan)
HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
Assert(PointerIsAligned(start, uint64))
#define HeapTupleIsValid(tuple)
static TransactionId HeapTupleHeaderGetXmin(const HeapTupleHeaderData *tup)
static void * GETSTRUCT(const HeapTupleData *tuple)
static bool HeapTupleHeaderXminCommitted(const HeapTupleHeaderData *tup)
void index_close(Relation relation, LOCKMODE lockmode)
Relation index_open(Oid relationId, LOCKMODE lockmode)
if(TABLE==NULL||TABLE_index==NULL)
char * pstrdup(const char *in)
void * repalloc(void *pointer, Size size)
void pfree(void *pointer)
Datum lower(PG_FUNCTION_ARGS)
Datum upper(PG_FUNCTION_ARGS)
bool EnumUncommitted(Oid enum_id)
FormData_pg_enum * Form_pg_enum
static Datum ObjectIdGetDatum(Oid X)
static Datum CStringGetDatum(const char *X)
bool TransactionIdIsInProgress(TransactionId xid)
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
#define BTEqualStrategyNumber
struct StringInfoData * StringInfo
void ReleaseSysCache(HeapTuple tuple)
HeapTuple SearchSysCache1(int cacheId, Datum key1)
HeapTuple SearchSysCache2(int cacheId, Datum key1, Datum key2)
void table_close(Relation relation, LOCKMODE lockmode)
Relation table_open(Oid relationId, LOCKMODE lockmode)
bool TransactionIdDidCommit(TransactionId transactionId)
int compare_values_of_enum(TypeCacheEntry *tcache, Oid arg1, Oid arg2)
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)