Update copyright for 2022
[pgsql.git] / src / backend / utils / adt / enum.c
blob0cc7a6d8ad0552bf51c4a2701b7f58fa254493bb
1 /*-------------------------------------------------------------------------
3 * enum.c
4 * I/O functions, operators, aggregates etc for enum types
6 * Copyright (c) 2006-2022, PostgreSQL Global Development Group
9 * IDENTIFICATION
10 * src/backend/utils/adt/enum.c
12 *-------------------------------------------------------------------------
14 #include "postgres.h"
16 #include "access/genam.h"
17 #include "access/htup_details.h"
18 #include "access/table.h"
19 #include "catalog/pg_enum.h"
20 #include "libpq/pqformat.h"
21 #include "storage/procarray.h"
22 #include "utils/array.h"
23 #include "utils/builtins.h"
24 #include "utils/fmgroids.h"
25 #include "utils/snapmgr.h"
26 #include "utils/syscache.h"
27 #include "utils/typcache.h"
30 static Oid enum_endpoint(Oid enumtypoid, ScanDirection direction);
31 static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
35 * Disallow use of an uncommitted pg_enum tuple.
37 * We need to make sure that uncommitted enum values don't get into indexes.
38 * If they did, and if we then rolled back the pg_enum addition, we'd have
39 * broken the index because value comparisons will not work reliably without
40 * an underlying pg_enum entry. (Note that removal of the heap entry
41 * containing an enum value is not sufficient to ensure that it doesn't appear
42 * in upper levels of indexes.) To do this we prevent an uncommitted row from
43 * being used for any SQL-level purpose. This is stronger than necessary,
44 * since the value might not be getting inserted into a table or there might
45 * be no index on its column, but it's easy to enforce centrally.
47 * However, it's okay to allow use of uncommitted values belonging to enum
48 * types that were themselves created in the same transaction, because then
49 * any such index would also be new and would go away altogether on rollback.
50 * We don't implement that fully right now, but we do allow free use of enum
51 * values created during CREATE TYPE AS ENUM, which are surely of the same
52 * lifespan as the enum type. (This case is required by "pg_restore -1".)
53 * Values added by ALTER TYPE ADD VALUE are currently restricted, but could
54 * be allowed if the enum type could be proven to have been created earlier
55 * in the same transaction. (Note that comparing tuple xmins would not work
56 * for that, because the type tuple might have been updated in the current
57 * transaction. Subtransactions also create hazards to be accounted for.)
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.
62 static void
63 check_safe_enum_use(HeapTuple enumval_tup)
65 TransactionId xmin;
66 Form_pg_enum en = (Form_pg_enum) GETSTRUCT(enumval_tup);
69 * If the row is hinted as committed, it's surely safe. This provides a
70 * fast path for all normal use-cases.
72 if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
73 return;
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.
79 xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
80 if (!TransactionIdIsInProgress(xmin) &&
81 TransactionIdDidCommit(xmin))
82 return;
85 * Check if the enum value is uncommitted. If not, it's safe, because it
86 * was made during CREATE TYPE AS ENUM and can't be shorter-lived than its
87 * owning type. (This'd also be false for values made by other
88 * transactions; but the previous tests should have handled all of those.)
90 if (!EnumUncommitted(en->oid))
91 return;
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.
97 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.")));
106 /* Basic I/O support */
108 Datum
109 enum_in(PG_FUNCTION_ARGS)
111 char *name = PG_GETARG_CSTRING(0);
112 Oid enumtypoid = PG_GETARG_OID(1);
113 Oid enumoid;
114 HeapTuple tup;
116 /* must check length to prevent Assert failure within SearchSysCache */
117 if (strlen(name) >= NAMEDATALEN)
118 ereport(ERROR,
119 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
120 errmsg("invalid input value for enum %s: \"%s\"",
121 format_type_be(enumtypoid),
122 name)));
124 tup = SearchSysCache2(ENUMTYPOIDNAME,
125 ObjectIdGetDatum(enumtypoid),
126 CStringGetDatum(name));
127 if (!HeapTupleIsValid(tup))
128 ereport(ERROR,
129 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
130 errmsg("invalid input value for enum %s: \"%s\"",
131 format_type_be(enumtypoid),
132 name)));
134 /* check it's safe to use in SQL */
135 check_safe_enum_use(tup);
138 * This comes from pg_enum.oid and stores system oids in user tables. This
139 * oid must be preserved by binary upgrades.
141 enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
143 ReleaseSysCache(tup);
145 PG_RETURN_OID(enumoid);
148 Datum
149 enum_out(PG_FUNCTION_ARGS)
151 Oid enumval = PG_GETARG_OID(0);
152 char *result;
153 HeapTuple tup;
154 Form_pg_enum en;
156 tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
157 if (!HeapTupleIsValid(tup))
158 ereport(ERROR,
159 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
160 errmsg("invalid internal value for enum: %u",
161 enumval)));
162 en = (Form_pg_enum) GETSTRUCT(tup);
164 result = pstrdup(NameStr(en->enumlabel));
166 ReleaseSysCache(tup);
168 PG_RETURN_CSTRING(result);
171 /* Binary I/O support */
172 Datum
173 enum_recv(PG_FUNCTION_ARGS)
175 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
176 Oid enumtypoid = PG_GETARG_OID(1);
177 Oid enumoid;
178 HeapTuple tup;
179 char *name;
180 int nbytes;
182 name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
184 /* must check length to prevent Assert failure within SearchSysCache */
185 if (strlen(name) >= NAMEDATALEN)
186 ereport(ERROR,
187 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
188 errmsg("invalid input value for enum %s: \"%s\"",
189 format_type_be(enumtypoid),
190 name)));
192 tup = SearchSysCache2(ENUMTYPOIDNAME,
193 ObjectIdGetDatum(enumtypoid),
194 CStringGetDatum(name));
195 if (!HeapTupleIsValid(tup))
196 ereport(ERROR,
197 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
198 errmsg("invalid input value for enum %s: \"%s\"",
199 format_type_be(enumtypoid),
200 name)));
202 /* check it's safe to use in SQL */
203 check_safe_enum_use(tup);
205 enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
207 ReleaseSysCache(tup);
209 pfree(name);
211 PG_RETURN_OID(enumoid);
214 Datum
215 enum_send(PG_FUNCTION_ARGS)
217 Oid enumval = PG_GETARG_OID(0);
218 StringInfoData buf;
219 HeapTuple tup;
220 Form_pg_enum en;
222 tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
223 if (!HeapTupleIsValid(tup))
224 ereport(ERROR,
225 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
226 errmsg("invalid internal value for enum: %u",
227 enumval)));
228 en = (Form_pg_enum) GETSTRUCT(tup);
230 pq_begintypsend(&buf);
231 pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
233 ReleaseSysCache(tup);
235 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
238 /* Comparison functions and related */
241 * enum_cmp_internal is the common engine for all the visible comparison
242 * functions, except for enum_eq and enum_ne which can just check for OID
243 * equality directly.
245 static int
246 enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
248 TypeCacheEntry *tcache;
251 * We don't need the typcache except in the hopefully-uncommon case that
252 * one or both Oids are odd. This means that cursory testing of code that
253 * fails to pass flinfo to an enum comparison function might not disclose
254 * the oversight. To make such errors more obvious, Assert that we have a
255 * place to cache even when we take a fast-path exit.
257 Assert(fcinfo->flinfo != NULL);
259 /* Equal OIDs are equal no matter what */
260 if (arg1 == arg2)
261 return 0;
263 /* Fast path: even-numbered Oids are known to compare correctly */
264 if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
266 if (arg1 < arg2)
267 return -1;
268 else
269 return 1;
272 /* Locate the typcache entry for the enum type */
273 tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
274 if (tcache == NULL)
276 HeapTuple enum_tup;
277 Form_pg_enum en;
278 Oid typeoid;
280 /* Get the OID of the enum type containing arg1 */
281 enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
282 if (!HeapTupleIsValid(enum_tup))
283 ereport(ERROR,
284 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
285 errmsg("invalid internal value for enum: %u",
286 arg1)));
287 en = (Form_pg_enum) GETSTRUCT(enum_tup);
288 typeoid = en->enumtypid;
289 ReleaseSysCache(enum_tup);
290 /* Now locate and remember the typcache entry */
291 tcache = lookup_type_cache(typeoid, 0);
292 fcinfo->flinfo->fn_extra = (void *) tcache;
295 /* The remaining comparison logic is in typcache.c */
296 return compare_values_of_enum(tcache, arg1, arg2);
299 Datum
300 enum_lt(PG_FUNCTION_ARGS)
302 Oid a = PG_GETARG_OID(0);
303 Oid b = PG_GETARG_OID(1);
305 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
308 Datum
309 enum_le(PG_FUNCTION_ARGS)
311 Oid a = PG_GETARG_OID(0);
312 Oid b = PG_GETARG_OID(1);
314 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
317 Datum
318 enum_eq(PG_FUNCTION_ARGS)
320 Oid a = PG_GETARG_OID(0);
321 Oid b = PG_GETARG_OID(1);
323 PG_RETURN_BOOL(a == b);
326 Datum
327 enum_ne(PG_FUNCTION_ARGS)
329 Oid a = PG_GETARG_OID(0);
330 Oid b = PG_GETARG_OID(1);
332 PG_RETURN_BOOL(a != b);
335 Datum
336 enum_ge(PG_FUNCTION_ARGS)
338 Oid a = PG_GETARG_OID(0);
339 Oid b = PG_GETARG_OID(1);
341 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
344 Datum
345 enum_gt(PG_FUNCTION_ARGS)
347 Oid a = PG_GETARG_OID(0);
348 Oid b = PG_GETARG_OID(1);
350 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
353 Datum
354 enum_smaller(PG_FUNCTION_ARGS)
356 Oid a = PG_GETARG_OID(0);
357 Oid b = PG_GETARG_OID(1);
359 PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
362 Datum
363 enum_larger(PG_FUNCTION_ARGS)
365 Oid a = PG_GETARG_OID(0);
366 Oid b = PG_GETARG_OID(1);
368 PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
371 Datum
372 enum_cmp(PG_FUNCTION_ARGS)
374 Oid a = PG_GETARG_OID(0);
375 Oid b = PG_GETARG_OID(1);
377 PG_RETURN_INT32(enum_cmp_internal(a, b, fcinfo));
380 /* Enum programming support functions */
383 * enum_endpoint: common code for enum_first/enum_last
385 static Oid
386 enum_endpoint(Oid enumtypoid, ScanDirection direction)
388 Relation enum_rel;
389 Relation enum_idx;
390 SysScanDesc enum_scan;
391 HeapTuple enum_tuple;
392 ScanKeyData skey;
393 Oid minmax;
396 * Find the first/last enum member using pg_enum_typid_sortorder_index.
397 * Note we must not use the syscache. See comments for RenumberEnumType
398 * in catalog/pg_enum.c for more info.
400 ScanKeyInit(&skey,
401 Anum_pg_enum_enumtypid,
402 BTEqualStrategyNumber, F_OIDEQ,
403 ObjectIdGetDatum(enumtypoid));
405 enum_rel = table_open(EnumRelationId, AccessShareLock);
406 enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
407 enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
408 1, &skey);
410 enum_tuple = systable_getnext_ordered(enum_scan, direction);
411 if (HeapTupleIsValid(enum_tuple))
413 /* check it's safe to use in SQL */
414 check_safe_enum_use(enum_tuple);
415 minmax = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
417 else
419 /* should only happen with an empty enum */
420 minmax = InvalidOid;
423 systable_endscan_ordered(enum_scan);
424 index_close(enum_idx, AccessShareLock);
425 table_close(enum_rel, AccessShareLock);
427 return minmax;
430 Datum
431 enum_first(PG_FUNCTION_ARGS)
433 Oid enumtypoid;
434 Oid min;
437 * We rely on being able to get the specific enum type from the calling
438 * expression tree. Notice that the actual value of the argument isn't
439 * examined at all; in particular it might be NULL.
441 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
442 if (enumtypoid == InvalidOid)
443 ereport(ERROR,
444 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
445 errmsg("could not determine actual enum type")));
447 /* Get the OID using the index */
448 min = enum_endpoint(enumtypoid, ForwardScanDirection);
450 if (!OidIsValid(min))
451 ereport(ERROR,
452 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
453 errmsg("enum %s contains no values",
454 format_type_be(enumtypoid))));
456 PG_RETURN_OID(min);
459 Datum
460 enum_last(PG_FUNCTION_ARGS)
462 Oid enumtypoid;
463 Oid max;
466 * We rely on being able to get the specific enum type from the calling
467 * expression tree. Notice that the actual value of the argument isn't
468 * examined at all; in particular it might be NULL.
470 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
471 if (enumtypoid == InvalidOid)
472 ereport(ERROR,
473 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
474 errmsg("could not determine actual enum type")));
476 /* Get the OID using the index */
477 max = enum_endpoint(enumtypoid, BackwardScanDirection);
479 if (!OidIsValid(max))
480 ereport(ERROR,
481 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
482 errmsg("enum %s contains no values",
483 format_type_be(enumtypoid))));
485 PG_RETURN_OID(max);
488 /* 2-argument variant of enum_range */
489 Datum
490 enum_range_bounds(PG_FUNCTION_ARGS)
492 Oid lower;
493 Oid upper;
494 Oid enumtypoid;
496 if (PG_ARGISNULL(0))
497 lower = InvalidOid;
498 else
499 lower = PG_GETARG_OID(0);
500 if (PG_ARGISNULL(1))
501 upper = InvalidOid;
502 else
503 upper = PG_GETARG_OID(1);
506 * We rely on being able to get the specific enum type from the calling
507 * expression tree. The generic type mechanism should have ensured that
508 * both are of the same type.
510 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
511 if (enumtypoid == InvalidOid)
512 ereport(ERROR,
513 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
514 errmsg("could not determine actual enum type")));
516 PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
519 /* 1-argument variant of enum_range */
520 Datum
521 enum_range_all(PG_FUNCTION_ARGS)
523 Oid enumtypoid;
526 * We rely on being able to get the specific enum type from the calling
527 * expression tree. Notice that the actual value of the argument isn't
528 * examined at all; in particular it might be NULL.
530 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
531 if (enumtypoid == InvalidOid)
532 ereport(ERROR,
533 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
534 errmsg("could not determine actual enum type")));
536 PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
537 InvalidOid, InvalidOid));
540 static ArrayType *
541 enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
543 ArrayType *result;
544 Relation enum_rel;
545 Relation enum_idx;
546 SysScanDesc enum_scan;
547 HeapTuple enum_tuple;
548 ScanKeyData skey;
549 Datum *elems;
550 int max,
551 cnt;
552 bool left_found;
555 * Scan the enum members in order using pg_enum_typid_sortorder_index.
556 * Note we must not use the syscache. See comments for RenumberEnumType
557 * in catalog/pg_enum.c for more info.
559 ScanKeyInit(&skey,
560 Anum_pg_enum_enumtypid,
561 BTEqualStrategyNumber, F_OIDEQ,
562 ObjectIdGetDatum(enumtypoid));
564 enum_rel = table_open(EnumRelationId, AccessShareLock);
565 enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
566 enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);
568 max = 64;
569 elems = (Datum *) palloc(max * sizeof(Datum));
570 cnt = 0;
571 left_found = !OidIsValid(lower);
573 while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
575 Oid enum_oid = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
577 if (!left_found && lower == enum_oid)
578 left_found = true;
580 if (left_found)
582 /* check it's safe to use in SQL */
583 check_safe_enum_use(enum_tuple);
585 if (cnt >= max)
587 max *= 2;
588 elems = (Datum *) repalloc(elems, max * sizeof(Datum));
591 elems[cnt++] = ObjectIdGetDatum(enum_oid);
594 if (OidIsValid(upper) && upper == enum_oid)
595 break;
598 systable_endscan_ordered(enum_scan);
599 index_close(enum_idx, AccessShareLock);
600 table_close(enum_rel, AccessShareLock);
602 /* and build the result array */
603 /* note this hardwires some details about the representation of Oid */
604 result = construct_array(elems, cnt, enumtypoid,
605 sizeof(Oid), true, TYPALIGN_INT);
607 pfree(elems);
609 return result;