Age Owner TLA Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * enum.c
4 : * I/O functions, operators, aggregates etc for enum types
5 : *
6 : * Copyright (c) 2006-2023, 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/snapmgr.h"
26 : #include "utils/syscache.h"
27 : #include "utils/typcache.h"
28 :
29 :
30 : static Oid enum_endpoint(Oid enumtypoid, ScanDirection direction);
31 : static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
32 :
33 :
34 : /*
35 : * Disallow use of an uncommitted pg_enum tuple.
36 : *
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.
46 : *
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.)
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
1643 tmunro 63 CBC 118376 : check_safe_enum_use(HeapTuple enumval_tup)
64 : {
65 : TransactionId xmin;
1601 andres 66 118376 : 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 : */
1643 tmunro 72 118376 : if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
73 118311 : 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 65 : xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
80 91 : if (!TransactionIdIsInProgress(xmin) &&
81 26 : TransactionIdDidCommit(xmin))
82 26 : return;
83 :
84 : /*
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.)
89 : */
824 90 39 : if (!EnumUncommitted(en->oid))
1643 91 24 : 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 15 : 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
5851 tgl 109 118268 : enum_in(PG_FUNCTION_ARGS)
110 : {
5624 bruce 111 118268 : char *name = PG_GETARG_CSTRING(0);
112 118268 : Oid enumtypoid = PG_GETARG_OID(1);
105 tgl 113 GNC 118268 : Node *escontext = fcinfo->context;
5624 bruce 114 ECB : Oid enumoid;
115 : HeapTuple tup;
116 :
117 : /* must check length to prevent Assert failure within SearchSysCache */
5851 andrew 118 GIC 118268 : if (strlen(name) >= NAMEDATALEN)
105 tgl 119 GNC 3 : ereturn(escontext, (Datum) 0,
5787 tgl 120 ECB : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
121 : errmsg("invalid input value for enum %s: \"%s\"",
122 : format_type_be(enumtypoid),
123 : name)));
124 :
4802 rhaas 125 GIC 118265 : tup = SearchSysCache2(ENUMTYPOIDNAME,
4802 rhaas 126 ECB : ObjectIdGetDatum(enumtypoid),
127 : CStringGetDatum(name));
5787 tgl 128 GIC 118265 : if (!HeapTupleIsValid(tup))
105 tgl 129 GNC 9 : ereturn(escontext, (Datum) 0,
5787 tgl 130 ECB : (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 : */
1643 tmunro 141 GIC 118256 : 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 : */
1601 andres 147 CBC 118250 : enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
148 :
5851 tgl 149 GIC 118250 : ReleaseSysCache(tup);
150 :
5787 151 118250 : PG_RETURN_OID(enumoid);
152 : }
5851 tgl 153 ECB :
154 : Datum
5851 tgl 155 CBC 25430 : enum_out(PG_FUNCTION_ARGS)
156 : {
5624 bruce 157 25430 : Oid enumval = PG_GETARG_OID(0);
158 : char *result;
159 : HeapTuple tup;
160 : Form_pg_enum en;
5851 tgl 161 ECB :
4802 rhaas 162 GIC 25430 : tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
5787 tgl 163 CBC 25430 : if (!HeapTupleIsValid(tup))
5787 tgl 164 UIC 0 : ereport(ERROR,
165 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
166 : errmsg("invalid internal value for enum: %u",
167 : enumval)));
5851 tgl 168 CBC 25430 : en = (Form_pg_enum) GETSTRUCT(tup);
5851 tgl 169 ECB :
5787 tgl 170 GBC 25430 : result = pstrdup(NameStr(en->enumlabel));
171 :
5851 tgl 172 GIC 25430 : ReleaseSysCache(tup);
173 :
5787 tgl 174 CBC 25430 : PG_RETURN_CSTRING(result);
175 : }
5851 tgl 176 ECB :
177 : /* Binary I/O support */
5696 andrew 178 : Datum
5696 andrew 179 UIC 0 : enum_recv(PG_FUNCTION_ARGS)
5696 andrew 180 ECB : {
5624 bruce 181 UIC 0 : StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
182 0 : Oid enumtypoid = PG_GETARG_OID(1);
183 : Oid enumoid;
184 : HeapTuple tup;
5624 bruce 185 EUB : char *name;
186 : int nbytes;
5696 andrew 187 :
5696 andrew 188 UBC 0 : name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
189 :
190 : /* must check length to prevent Assert failure within SearchSysCache */
5696 andrew 191 UIC 0 : if (strlen(name) >= NAMEDATALEN)
192 0 : ereport(ERROR,
193 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
5696 andrew 194 EUB : errmsg("invalid input value for enum %s: \"%s\"",
195 : format_type_be(enumtypoid),
196 : name)));
197 :
4802 rhaas 198 UBC 0 : tup = SearchSysCache2(ENUMTYPOIDNAME,
199 : ObjectIdGetDatum(enumtypoid),
200 : CStringGetDatum(name));
5696 andrew 201 UIC 0 : if (!HeapTupleIsValid(tup))
202 0 : ereport(ERROR,
203 : (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
5696 andrew 204 EUB : errmsg("invalid input value for enum %s: \"%s\"",
205 : format_type_be(enumtypoid),
206 : name)));
207 :
1643 tmunro 208 : /* check it's safe to use in SQL */
1643 tmunro 209 UIC 0 : check_safe_enum_use(tup);
210 :
1601 andres 211 0 : enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
212 :
5696 andrew 213 0 : ReleaseSysCache(tup);
214 :
5696 andrew 215 UBC 0 : pfree(name);
216 :
217 0 : PG_RETURN_OID(enumoid);
218 : }
5696 andrew 219 EUB :
220 : Datum
5696 andrew 221 UBC 0 : enum_send(PG_FUNCTION_ARGS)
222 : {
5624 bruce 223 0 : Oid enumval = PG_GETARG_OID(0);
224 : StringInfoData buf;
225 : HeapTuple tup;
226 : Form_pg_enum en;
5696 andrew 227 EUB :
4802 rhaas 228 UIC 0 : tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
5696 andrew 229 UBC 0 : if (!HeapTupleIsValid(tup))
5696 andrew 230 UIC 0 : ereport(ERROR,
231 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
232 : errmsg("invalid internal value for enum: %u",
233 : enumval)));
5696 andrew 234 UBC 0 : en = (Form_pg_enum) GETSTRUCT(tup);
5696 andrew 235 EUB :
5696 andrew 236 UBC 0 : pq_begintypsend(&buf);
5696 andrew 237 UIC 0 : pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
238 :
239 0 : ReleaseSysCache(tup);
5696 andrew 240 EUB :
5696 andrew 241 UIC 0 : PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
5696 andrew 242 EUB : }
243 :
244 : /* Comparison functions and related */
5851 tgl 245 :
246 : /*
4550 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
4550 tgl 252 GIC 1237587 : 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
2236 tgl 258 ECB : * 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 : */
2236 tgl 263 GIC 1237587 : Assert(fcinfo->flinfo != NULL);
264 :
265 : /* Equal OIDs are equal no matter what */
4550 266 1237587 : if (arg1 == arg2)
267 1124068 : return 0;
268 :
4550 tgl 269 ECB : /* Fast path: even-numbered Oids are known to compare correctly */
4550 tgl 270 GIC 113519 : if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
271 : {
4550 tgl 272 CBC 38482 : if (arg1 < arg2)
273 5454 : return -1;
274 : else
4550 tgl 275 GIC 33028 : return 1;
4550 tgl 276 ECB : }
277 :
278 : /* Locate the typcache entry for the enum type */
4550 tgl 279 CBC 75037 : tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
4550 tgl 280 GIC 75037 : if (tcache == NULL)
4550 tgl 281 ECB : {
282 : HeapTuple enum_tup;
283 : Form_pg_enum en;
284 : Oid typeoid;
285 :
286 : /* Get the OID of the enum type containing arg1 */
4550 tgl 287 GIC 4 : enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
288 4 : if (!HeapTupleIsValid(enum_tup))
4550 tgl 289 UIC 0 : ereport(ERROR,
290 : (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
291 : errmsg("invalid internal value for enum: %u",
292 : arg1)));
4550 tgl 293 CBC 4 : en = (Form_pg_enum) GETSTRUCT(enum_tup);
294 4 : typeoid = en->enumtypid;
4550 tgl 295 GBC 4 : ReleaseSysCache(enum_tup);
296 : /* Now locate and remember the typcache entry */
4550 tgl 297 GIC 4 : tcache = lookup_type_cache(typeoid, 0);
298 4 : fcinfo->flinfo->fn_extra = (void *) tcache;
4550 tgl 299 ECB : }
300 :
301 : /* The remaining comparison logic is in typcache.c */
4550 tgl 302 GIC 75037 : return compare_values_of_enum(tcache, arg1, arg2);
4550 tgl 303 ECB : }
304 :
305 : Datum
5851 tgl 306 GIC 2017 : enum_lt(PG_FUNCTION_ARGS)
307 : {
5624 bruce 308 CBC 2017 : Oid a = PG_GETARG_OID(0);
5624 bruce 309 GIC 2017 : Oid b = PG_GETARG_OID(1);
310 :
4550 tgl 311 2017 : PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
5851 tgl 312 ECB : }
313 :
314 : Datum
5851 tgl 315 CBC 1105 : enum_le(PG_FUNCTION_ARGS)
316 : {
5624 bruce 317 1105 : Oid a = PG_GETARG_OID(0);
5624 bruce 318 GIC 1105 : Oid b = PG_GETARG_OID(1);
319 :
4550 tgl 320 1105 : PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
5851 tgl 321 ECB : }
322 :
323 : Datum
5851 tgl 324 CBC 4317 : enum_eq(PG_FUNCTION_ARGS)
325 : {
5624 bruce 326 4317 : Oid a = PG_GETARG_OID(0);
5624 bruce 327 GIC 4317 : Oid b = PG_GETARG_OID(1);
328 :
5851 tgl 329 4317 : PG_RETURN_BOOL(a == b);
5851 tgl 330 ECB : }
331 :
332 : Datum
5851 tgl 333 CBC 36 : enum_ne(PG_FUNCTION_ARGS)
334 : {
5624 bruce 335 36 : Oid a = PG_GETARG_OID(0);
5624 bruce 336 GIC 36 : Oid b = PG_GETARG_OID(1);
337 :
5851 tgl 338 36 : PG_RETURN_BOOL(a != b);
5851 tgl 339 ECB : }
340 :
341 : Datum
5851 tgl 342 CBC 1098 : enum_ge(PG_FUNCTION_ARGS)
343 : {
5624 bruce 344 1098 : Oid a = PG_GETARG_OID(0);
5624 bruce 345 GIC 1098 : Oid b = PG_GETARG_OID(1);
346 :
4550 tgl 347 1098 : PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
5851 tgl 348 ECB : }
349 :
350 : Datum
5851 tgl 351 CBC 1989 : enum_gt(PG_FUNCTION_ARGS)
352 : {
5624 bruce 353 1989 : Oid a = PG_GETARG_OID(0);
5624 bruce 354 GIC 1989 : Oid b = PG_GETARG_OID(1);
355 :
4550 tgl 356 1989 : PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
5851 tgl 357 ECB : }
358 :
359 : Datum
5851 tgl 360 CBC 15 : enum_smaller(PG_FUNCTION_ARGS)
361 : {
5624 bruce 362 15 : Oid a = PG_GETARG_OID(0);
5624 bruce 363 GIC 15 : Oid b = PG_GETARG_OID(1);
364 :
4550 tgl 365 15 : PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
5851 tgl 366 ECB : }
367 :
368 : Datum
5851 tgl 369 CBC 48 : enum_larger(PG_FUNCTION_ARGS)
370 : {
5624 bruce 371 48 : Oid a = PG_GETARG_OID(0);
5624 bruce 372 GIC 48 : Oid b = PG_GETARG_OID(1);
373 :
4550 tgl 374 48 : PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
5851 tgl 375 ECB : }
376 :
377 : Datum
5851 tgl 378 CBC 1231315 : enum_cmp(PG_FUNCTION_ARGS)
379 : {
5624 bruce 380 1231315 : Oid a = PG_GETARG_OID(0);
5624 bruce 381 GIC 1231315 : Oid b = PG_GETARG_OID(1);
382 :
2236 tgl 383 1231315 : PG_RETURN_INT32(enum_cmp_internal(a, b, fcinfo));
5851 tgl 384 ECB : }
385 :
386 : /* Enum programming support functions */
387 :
388 : /*
4550 389 : * enum_endpoint: common code for enum_first/enum_last
390 : */
391 : static Oid
4550 tgl 392 GIC 18 : enum_endpoint(Oid enumtypoid, ScanDirection direction)
393 : {
394 : Relation enum_rel;
395 : Relation enum_idx;
396 : SysScanDesc enum_scan;
397 : HeapTuple enum_tuple;
4550 tgl 398 ECB : 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 : */
4550 tgl 406 GIC 18 : ScanKeyInit(&skey,
407 : Anum_pg_enum_enumtypid,
408 : BTEqualStrategyNumber, F_OIDEQ,
409 : ObjectIdGetDatum(enumtypoid));
410 :
1539 andres 411 18 : enum_rel = table_open(EnumRelationId, AccessShareLock);
4550 tgl 412 CBC 18 : enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
2902 rhaas 413 GIC 18 : enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
414 : 1, &skey);
415 :
4550 tgl 416 18 : enum_tuple = systable_getnext_ordered(enum_scan, direction);
4550 tgl 417 CBC 18 : if (HeapTupleIsValid(enum_tuple))
1643 tmunro 418 ECB : {
419 : /* check it's safe to use in SQL */
1643 tmunro 420 GIC 18 : check_safe_enum_use(enum_tuple);
1601 andres 421 15 : minmax = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
1643 tmunro 422 ECB : }
4550 tgl 423 : else
424 : {
425 : /* should only happen with an empty enum */
4550 tgl 426 LBC 0 : minmax = InvalidOid;
1643 tmunro 427 ECB : }
428 :
4550 tgl 429 GIC 15 : systable_endscan_ordered(enum_scan);
430 15 : index_close(enum_idx, AccessShareLock);
1539 andres 431 15 : table_close(enum_rel, AccessShareLock);
4550 tgl 432 EUB :
4550 tgl 433 GIC 15 : return minmax;
434 : }
4550 tgl 435 ECB :
5851 436 : Datum
5851 tgl 437 CBC 6 : enum_first(PG_FUNCTION_ARGS)
438 : {
5624 bruce 439 ECB : Oid enumtypoid;
440 : Oid min;
441 :
442 : /*
5851 tgl 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 : */
5851 tgl 447 GIC 6 : enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
448 6 : if (enumtypoid == InvalidOid)
5851 tgl 449 UIC 0 : ereport(ERROR,
450 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
451 : errmsg("could not determine actual enum type")));
452 :
4550 tgl 453 ECB : /* Get the OID using the index */
4550 tgl 454 CBC 6 : min = enum_endpoint(enumtypoid, ForwardScanDirection);
5851 tgl 455 EUB :
4550 tgl 456 GIC 6 : if (!OidIsValid(min))
4550 tgl 457 UIC 0 : ereport(ERROR,
458 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
459 : errmsg("enum %s contains no values",
4550 tgl 460 ECB : format_type_be(enumtypoid))));
461 :
5851 tgl 462 CBC 6 : PG_RETURN_OID(min);
5851 tgl 463 EUB : }
464 :
465 : Datum
5851 tgl 466 GIC 12 : enum_last(PG_FUNCTION_ARGS)
467 : {
5624 bruce 468 ECB : Oid enumtypoid;
469 : Oid max;
470 :
471 : /*
5851 tgl 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 : */
5851 tgl 476 GIC 12 : enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
477 12 : if (enumtypoid == InvalidOid)
5851 tgl 478 UIC 0 : ereport(ERROR,
479 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
480 : errmsg("could not determine actual enum type")));
481 :
4550 tgl 482 ECB : /* Get the OID using the index */
4550 tgl 483 CBC 12 : max = enum_endpoint(enumtypoid, BackwardScanDirection);
5851 tgl 484 EUB :
4550 tgl 485 GIC 9 : if (!OidIsValid(max))
4550 tgl 486 UIC 0 : ereport(ERROR,
487 : (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
488 : errmsg("enum %s contains no values",
4550 tgl 489 ECB : format_type_be(enumtypoid))));
490 :
5787 tgl 491 CBC 9 : PG_RETURN_OID(max);
5851 tgl 492 EUB : }
493 :
494 : /* 2-argument variant of enum_range */
495 : Datum
5851 tgl 496 GIC 12 : enum_range_bounds(PG_FUNCTION_ARGS)
5851 tgl 497 ECB : {
498 : Oid lower;
499 : Oid upper;
500 : Oid enumtypoid;
501 :
5851 tgl 502 CBC 12 : if (PG_ARGISNULL(0))
5851 tgl 503 GIC 6 : lower = InvalidOid;
504 : else
505 6 : lower = PG_GETARG_OID(0);
506 12 : if (PG_ARGISNULL(1))
507 6 : upper = InvalidOid;
5851 tgl 508 ECB : else
5851 tgl 509 CBC 6 : upper = PG_GETARG_OID(1);
510 :
5851 tgl 511 ECB : /*
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 : */
5851 tgl 516 GIC 12 : enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
517 12 : if (enumtypoid == InvalidOid)
5851 tgl 518 UIC 0 : ereport(ERROR,
519 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
520 : errmsg("could not determine actual enum type")));
521 :
5851 tgl 522 CBC 12 : PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
5851 tgl 523 ECB : }
5851 tgl 524 EUB :
525 : /* 1-argument variant of enum_range */
526 : Datum
5851 tgl 527 GIC 15 : enum_range_all(PG_FUNCTION_ARGS)
5851 tgl 528 ECB : {
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 : */
5851 tgl 536 GIC 15 : enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
537 15 : if (enumtypoid == InvalidOid)
5851 tgl 538 UIC 0 : ereport(ERROR,
539 : (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
540 : errmsg("could not determine actual enum type")));
541 :
5851 tgl 542 CBC 15 : PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
5851 tgl 543 ECB : InvalidOid, InvalidOid));
5851 tgl 544 EUB : }
545 :
546 : static ArrayType *
5851 tgl 547 GIC 27 : enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
5851 tgl 548 ECB : {
549 : ArrayType *result;
550 : Relation enum_rel;
551 : Relation enum_idx;
552 : SysScanDesc enum_scan;
4550 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 : */
4550 tgl 565 GIC 27 : ScanKeyInit(&skey,
566 : Anum_pg_enum_enumtypid,
567 : BTEqualStrategyNumber, F_OIDEQ,
568 : ObjectIdGetDatum(enumtypoid));
569 :
1539 andres 570 27 : enum_rel = table_open(EnumRelationId, AccessShareLock);
4550 tgl 571 CBC 27 : enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
2902 rhaas 572 GIC 27 : enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);
573 :
4550 tgl 574 27 : max = 64;
575 27 : elems = (Datum *) palloc(max * sizeof(Datum));
4550 tgl 576 CBC 27 : cnt = 0;
577 27 : left_found = !OidIsValid(lower);
4550 tgl 578 ECB :
4550 tgl 579 GIC 123 : while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
4550 tgl 580 ECB : {
1601 andres 581 CBC 108 : Oid enum_oid = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
5851 tgl 582 ECB :
4550 tgl 583 CBC 108 : if (!left_found && lower == enum_oid)
4550 tgl 584 GIC 6 : left_found = true;
5851 tgl 585 ECB :
4550 tgl 586 GIC 108 : if (left_found)
4550 tgl 587 ECB : {
588 : /* check it's safe to use in SQL */
1643 tmunro 589 CBC 102 : check_safe_enum_use(enum_tuple);
1643 tmunro 590 ECB :
4550 tgl 591 GIC 96 : if (cnt >= max)
4550 tgl 592 ECB : {
4550 tgl 593 UIC 0 : max *= 2;
594 0 : elems = (Datum *) repalloc(elems, max * sizeof(Datum));
4550 tgl 595 ECB : }
596 :
4550 tgl 597 CBC 96 : elems[cnt++] = ObjectIdGetDatum(enum_oid);
598 : }
5851 tgl 599 EUB :
4550 tgl 600 GBC 102 : if (OidIsValid(upper) && upper == enum_oid)
4550 tgl 601 GIC 6 : break;
602 : }
5851 tgl 603 ECB :
4550 tgl 604 GIC 21 : systable_endscan_ordered(enum_scan);
605 21 : index_close(enum_idx, AccessShareLock);
1539 andres 606 CBC 21 : table_close(enum_rel, AccessShareLock);
5851 tgl 607 ECB :
608 : /* and build the result array */
609 : /* note this hardwires some details about the representation of Oid */
1131 tgl 610 CBC 21 : result = construct_array(elems, cnt, enumtypoid,
1131 tgl 611 ECB : sizeof(Oid), true, TYPALIGN_INT);
5851 612 :
5851 tgl 613 GIC 21 : pfree(elems);
614 :
615 21 : return result;
5851 tgl 616 ECB : }
|