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