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 : }
|