1 /*-------------------------------------------------------------------------
4 * I/O functions, operators, aggregates etc for enum types
6 * Copyright (c) 2006-2013, PostgreSQL Global Development Group
10 * src/backend/utils/adt/enum.c
12 *-------------------------------------------------------------------------
16 #include "access/genam.h"
17 #include "access/heapam.h"
18 #include "access/htup_details.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_enum.h"
21 #include "catalog/pg_type.h"
22 #include "libpq/pqformat.h"
23 #include "utils/array.h"
24 #include "utils/builtins.h"
25 #include "utils/fmgroids.h"
26 #include "utils/snapmgr.h"
27 #include "utils/syscache.h"
28 #include "utils/typcache.h"
31 static Oid enum_endpoint(Oid enumtypoid, ScanDirection direction);
32 static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
35 /* Basic I/O support */
38 enum_in(PG_FUNCTION_ARGS)
40 char *name = PG_GETARG_CSTRING(0);
41 Oid enumtypoid = PG_GETARG_OID(1);
45 /* must check length to prevent Assert failure within SearchSysCache */
46 if (strlen(name) >= NAMEDATALEN)
48 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
49 errmsg("invalid input value for enum %s: \"%s\"",
50 format_type_be(enumtypoid),
53 tup = SearchSysCache2(ENUMTYPOIDNAME,
54 ObjectIdGetDatum(enumtypoid),
55 CStringGetDatum(name));
56 if (!HeapTupleIsValid(tup))
58 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
59 errmsg("invalid input value for enum %s: \"%s\"",
60 format_type_be(enumtypoid),
64 * This comes from pg_enum.oid and stores system oids in user tables. This
65 * oid must be preserved by binary upgrades.
67 enumoid = HeapTupleGetOid(tup);
71 PG_RETURN_OID(enumoid);
75 enum_out(PG_FUNCTION_ARGS)
77 Oid enumval = PG_GETARG_OID(0);
82 tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
83 if (!HeapTupleIsValid(tup))
85 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
86 errmsg("invalid internal value for enum: %u",
88 en = (Form_pg_enum) GETSTRUCT(tup);
90 result = pstrdup(NameStr(en->enumlabel));
94 PG_RETURN_CSTRING(result);
97 /* Binary I/O support */
99 enum_recv(PG_FUNCTION_ARGS)
101 StringInfo buf = (StringInfo) PG_GETARG_POINTER(0);
102 Oid enumtypoid = PG_GETARG_OID(1);
108 /* guard against pre-9.3 misdeclaration of enum_recv */
109 if (get_fn_expr_argtype(fcinfo->flinfo, 0) == CSTRINGOID)
110 elog(ERROR, "invalid argument for enum_recv");
112 name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
114 /* must check length to prevent Assert failure within SearchSysCache */
115 if (strlen(name) >= NAMEDATALEN)
117 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
118 errmsg("invalid input value for enum %s: \"%s\"",
119 format_type_be(enumtypoid),
122 tup = SearchSysCache2(ENUMTYPOIDNAME,
123 ObjectIdGetDatum(enumtypoid),
124 CStringGetDatum(name));
125 if (!HeapTupleIsValid(tup))
127 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
128 errmsg("invalid input value for enum %s: \"%s\"",
129 format_type_be(enumtypoid),
132 enumoid = HeapTupleGetOid(tup);
134 ReleaseSysCache(tup);
138 PG_RETURN_OID(enumoid);
142 enum_send(PG_FUNCTION_ARGS)
144 Oid enumval = PG_GETARG_OID(0);
149 tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
150 if (!HeapTupleIsValid(tup))
152 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
153 errmsg("invalid internal value for enum: %u",
155 en = (Form_pg_enum) GETSTRUCT(tup);
157 pq_begintypsend(&buf);
158 pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
160 ReleaseSysCache(tup);
162 PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
165 /* Comparison functions and related */
168 * enum_cmp_internal is the common engine for all the visible comparison
169 * functions, except for enum_eq and enum_ne which can just check for OID
173 enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
175 TypeCacheEntry *tcache;
177 /* Equal OIDs are equal no matter what */
181 /* Fast path: even-numbered Oids are known to compare correctly */
182 if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
190 /* Locate the typcache entry for the enum type */
191 tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
198 /* Get the OID of the enum type containing arg1 */
199 enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
200 if (!HeapTupleIsValid(enum_tup))
202 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
203 errmsg("invalid internal value for enum: %u",
205 en = (Form_pg_enum) GETSTRUCT(enum_tup);
206 typeoid = en->enumtypid;
207 ReleaseSysCache(enum_tup);
208 /* Now locate and remember the typcache entry */
209 tcache = lookup_type_cache(typeoid, 0);
210 fcinfo->flinfo->fn_extra = (void *) tcache;
213 /* The remaining comparison logic is in typcache.c */
214 return compare_values_of_enum(tcache, arg1, arg2);
218 enum_lt(PG_FUNCTION_ARGS)
220 Oid a = PG_GETARG_OID(0);
221 Oid b = PG_GETARG_OID(1);
223 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
227 enum_le(PG_FUNCTION_ARGS)
229 Oid a = PG_GETARG_OID(0);
230 Oid b = PG_GETARG_OID(1);
232 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
236 enum_eq(PG_FUNCTION_ARGS)
238 Oid a = PG_GETARG_OID(0);
239 Oid b = PG_GETARG_OID(1);
241 PG_RETURN_BOOL(a == b);
245 enum_ne(PG_FUNCTION_ARGS)
247 Oid a = PG_GETARG_OID(0);
248 Oid b = PG_GETARG_OID(1);
250 PG_RETURN_BOOL(a != b);
254 enum_ge(PG_FUNCTION_ARGS)
256 Oid a = PG_GETARG_OID(0);
257 Oid b = PG_GETARG_OID(1);
259 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
263 enum_gt(PG_FUNCTION_ARGS)
265 Oid a = PG_GETARG_OID(0);
266 Oid b = PG_GETARG_OID(1);
268 PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
272 enum_smaller(PG_FUNCTION_ARGS)
274 Oid a = PG_GETARG_OID(0);
275 Oid b = PG_GETARG_OID(1);
277 PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
281 enum_larger(PG_FUNCTION_ARGS)
283 Oid a = PG_GETARG_OID(0);
284 Oid b = PG_GETARG_OID(1);
286 PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
290 enum_cmp(PG_FUNCTION_ARGS)
292 Oid a = PG_GETARG_OID(0);
293 Oid b = PG_GETARG_OID(1);
297 else if (enum_cmp_internal(a, b, fcinfo) > 0)
303 /* Enum programming support functions */
306 * enum_endpoint: common code for enum_first/enum_last
309 enum_endpoint(Oid enumtypoid, ScanDirection direction)
313 SysScanDesc enum_scan;
314 HeapTuple enum_tuple;
319 * Find the first/last enum member using pg_enum_typid_sortorder_index.
320 * Note we must not use the syscache, and must use an MVCC snapshot here.
321 * See comments for RenumberEnumType in catalog/pg_enum.c for more info.
324 Anum_pg_enum_enumtypid,
325 BTEqualStrategyNumber, F_OIDEQ,
326 ObjectIdGetDatum(enumtypoid));
328 enum_rel = heap_open(EnumRelationId, AccessShareLock);
329 enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
330 enum_scan = systable_beginscan_ordered(enum_rel, enum_idx,
331 GetTransactionSnapshot(),
334 enum_tuple = systable_getnext_ordered(enum_scan, direction);
335 if (HeapTupleIsValid(enum_tuple))
336 minmax = HeapTupleGetOid(enum_tuple);
340 systable_endscan_ordered(enum_scan);
341 index_close(enum_idx, AccessShareLock);
342 heap_close(enum_rel, AccessShareLock);
348 enum_first(PG_FUNCTION_ARGS)
354 * We rely on being able to get the specific enum type from the calling
355 * expression tree. Notice that the actual value of the argument isn't
356 * examined at all; in particular it might be NULL.
358 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
359 if (enumtypoid == InvalidOid)
361 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
362 errmsg("could not determine actual enum type")));
364 /* Get the OID using the index */
365 min = enum_endpoint(enumtypoid, ForwardScanDirection);
367 if (!OidIsValid(min))
369 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
370 errmsg("enum %s contains no values",
371 format_type_be(enumtypoid))));
377 enum_last(PG_FUNCTION_ARGS)
383 * We rely on being able to get the specific enum type from the calling
384 * expression tree. Notice that the actual value of the argument isn't
385 * examined at all; in particular it might be NULL.
387 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
388 if (enumtypoid == InvalidOid)
390 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
391 errmsg("could not determine actual enum type")));
393 /* Get the OID using the index */
394 max = enum_endpoint(enumtypoid, BackwardScanDirection);
396 if (!OidIsValid(max))
398 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
399 errmsg("enum %s contains no values",
400 format_type_be(enumtypoid))));
405 /* 2-argument variant of enum_range */
407 enum_range_bounds(PG_FUNCTION_ARGS)
416 lower = PG_GETARG_OID(0);
420 upper = PG_GETARG_OID(1);
423 * We rely on being able to get the specific enum type from the calling
424 * expression tree. The generic type mechanism should have ensured that
425 * both are of the same type.
427 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
428 if (enumtypoid == InvalidOid)
430 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
431 errmsg("could not determine actual enum type")));
433 PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
436 /* 1-argument variant of enum_range */
438 enum_range_all(PG_FUNCTION_ARGS)
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.
447 enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
448 if (enumtypoid == InvalidOid)
450 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
451 errmsg("could not determine actual enum type")));
453 PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
454 InvalidOid, InvalidOid));
458 enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
463 SysScanDesc enum_scan;
464 HeapTuple enum_tuple;
472 * Scan the enum members in order using pg_enum_typid_sortorder_index.
473 * Note we must not use the syscache, and must use an MVCC snapshot here.
474 * See comments for RenumberEnumType in catalog/pg_enum.c for more info.
477 Anum_pg_enum_enumtypid,
478 BTEqualStrategyNumber, F_OIDEQ,
479 ObjectIdGetDatum(enumtypoid));
481 enum_rel = heap_open(EnumRelationId, AccessShareLock);
482 enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
483 enum_scan = systable_beginscan_ordered(enum_rel, enum_idx,
484 GetTransactionSnapshot(),
488 elems = (Datum *) palloc(max * sizeof(Datum));
490 left_found = !OidIsValid(lower);
492 while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
494 Oid enum_oid = HeapTupleGetOid(enum_tuple);
496 if (!left_found && lower == enum_oid)
504 elems = (Datum *) repalloc(elems, max * sizeof(Datum));
507 elems[cnt++] = ObjectIdGetDatum(enum_oid);
510 if (OidIsValid(upper) && upper == enum_oid)
514 systable_endscan_ordered(enum_scan);
515 index_close(enum_idx, AccessShareLock);
516 heap_close(enum_rel, AccessShareLock);
518 /* and build the result array */
519 /* note this hardwires some details about the representation of Oid */
520 result = construct_array(elems, cnt, enumtypoid, sizeof(Oid), true, 'i');