]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/enum.c
1eb8ccfaee56d88d5be9de4d3dfeb3d2c4ed880d
[postgresql] / src / backend / utils / adt / enum.c
1 /*-------------------------------------------------------------------------
2  *
3  * enum.c
4  *        I/O functions, operators, aggregates etc for enum types
5  *
6  * Copyright (c) 2006-2013, 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/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"
29
30
31 static Oid      enum_endpoint(Oid enumtypoid, ScanDirection direction);
32 static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
33
34
35 /* Basic I/O support */
36
37 Datum
38 enum_in(PG_FUNCTION_ARGS)
39 {
40         char       *name = PG_GETARG_CSTRING(0);
41         Oid                     enumtypoid = PG_GETARG_OID(1);
42         Oid                     enumoid;
43         HeapTuple       tup;
44
45         /* must check length to prevent Assert failure within SearchSysCache */
46         if (strlen(name) >= NAMEDATALEN)
47                 ereport(ERROR,
48                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
49                                  errmsg("invalid input value for enum %s: \"%s\"",
50                                                 format_type_be(enumtypoid),
51                                                 name)));
52
53         tup = SearchSysCache2(ENUMTYPOIDNAME,
54                                                   ObjectIdGetDatum(enumtypoid),
55                                                   CStringGetDatum(name));
56         if (!HeapTupleIsValid(tup))
57                 ereport(ERROR,
58                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
59                                  errmsg("invalid input value for enum %s: \"%s\"",
60                                                 format_type_be(enumtypoid),
61                                                 name)));
62
63         /*
64          * This comes from pg_enum.oid and stores system oids in user tables. This
65          * oid must be preserved by binary upgrades.
66          */
67         enumoid = HeapTupleGetOid(tup);
68
69         ReleaseSysCache(tup);
70
71         PG_RETURN_OID(enumoid);
72 }
73
74 Datum
75 enum_out(PG_FUNCTION_ARGS)
76 {
77         Oid                     enumval = PG_GETARG_OID(0);
78         char       *result;
79         HeapTuple       tup;
80         Form_pg_enum en;
81
82         tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
83         if (!HeapTupleIsValid(tup))
84                 ereport(ERROR,
85                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
86                                  errmsg("invalid internal value for enum: %u",
87                                                 enumval)));
88         en = (Form_pg_enum) GETSTRUCT(tup);
89
90         result = pstrdup(NameStr(en->enumlabel));
91
92         ReleaseSysCache(tup);
93
94         PG_RETURN_CSTRING(result);
95 }
96
97 /* Binary I/O support */
98 Datum
99 enum_recv(PG_FUNCTION_ARGS)
100 {
101         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
102         Oid                     enumtypoid = PG_GETARG_OID(1);
103         Oid                     enumoid;
104         HeapTuple       tup;
105         char       *name;
106         int                     nbytes;
107
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");
111
112         name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
113
114         /* must check length to prevent Assert failure within SearchSysCache */
115         if (strlen(name) >= NAMEDATALEN)
116                 ereport(ERROR,
117                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
118                                  errmsg("invalid input value for enum %s: \"%s\"",
119                                                 format_type_be(enumtypoid),
120                                                 name)));
121
122         tup = SearchSysCache2(ENUMTYPOIDNAME,
123                                                   ObjectIdGetDatum(enumtypoid),
124                                                   CStringGetDatum(name));
125         if (!HeapTupleIsValid(tup))
126                 ereport(ERROR,
127                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
128                                  errmsg("invalid input value for enum %s: \"%s\"",
129                                                 format_type_be(enumtypoid),
130                                                 name)));
131
132         enumoid = HeapTupleGetOid(tup);
133
134         ReleaseSysCache(tup);
135
136         pfree(name);
137
138         PG_RETURN_OID(enumoid);
139 }
140
141 Datum
142 enum_send(PG_FUNCTION_ARGS)
143 {
144         Oid                     enumval = PG_GETARG_OID(0);
145         StringInfoData buf;
146         HeapTuple       tup;
147         Form_pg_enum en;
148
149         tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
150         if (!HeapTupleIsValid(tup))
151                 ereport(ERROR,
152                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
153                                  errmsg("invalid internal value for enum: %u",
154                                                 enumval)));
155         en = (Form_pg_enum) GETSTRUCT(tup);
156
157         pq_begintypsend(&buf);
158         pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
159
160         ReleaseSysCache(tup);
161
162         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
163 }
164
165 /* Comparison functions and related */
166
167 /*
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
170  * equality directly.
171  */
172 static int
173 enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
174 {
175         TypeCacheEntry *tcache;
176
177         /* Equal OIDs are equal no matter what */
178         if (arg1 == arg2)
179                 return 0;
180
181         /* Fast path: even-numbered Oids are known to compare correctly */
182         if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
183         {
184                 if (arg1 < arg2)
185                         return -1;
186                 else
187                         return 1;
188         }
189
190         /* Locate the typcache entry for the enum type */
191         tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
192         if (tcache == NULL)
193         {
194                 HeapTuple       enum_tup;
195                 Form_pg_enum en;
196                 Oid                     typeoid;
197
198                 /* Get the OID of the enum type containing arg1 */
199                 enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
200                 if (!HeapTupleIsValid(enum_tup))
201                         ereport(ERROR,
202                                         (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
203                                          errmsg("invalid internal value for enum: %u",
204                                                         arg1)));
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;
211         }
212
213         /* The remaining comparison logic is in typcache.c */
214         return compare_values_of_enum(tcache, arg1, arg2);
215 }
216
217 Datum
218 enum_lt(PG_FUNCTION_ARGS)
219 {
220         Oid                     a = PG_GETARG_OID(0);
221         Oid                     b = PG_GETARG_OID(1);
222
223         PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
224 }
225
226 Datum
227 enum_le(PG_FUNCTION_ARGS)
228 {
229         Oid                     a = PG_GETARG_OID(0);
230         Oid                     b = PG_GETARG_OID(1);
231
232         PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
233 }
234
235 Datum
236 enum_eq(PG_FUNCTION_ARGS)
237 {
238         Oid                     a = PG_GETARG_OID(0);
239         Oid                     b = PG_GETARG_OID(1);
240
241         PG_RETURN_BOOL(a == b);
242 }
243
244 Datum
245 enum_ne(PG_FUNCTION_ARGS)
246 {
247         Oid                     a = PG_GETARG_OID(0);
248         Oid                     b = PG_GETARG_OID(1);
249
250         PG_RETURN_BOOL(a != b);
251 }
252
253 Datum
254 enum_ge(PG_FUNCTION_ARGS)
255 {
256         Oid                     a = PG_GETARG_OID(0);
257         Oid                     b = PG_GETARG_OID(1);
258
259         PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
260 }
261
262 Datum
263 enum_gt(PG_FUNCTION_ARGS)
264 {
265         Oid                     a = PG_GETARG_OID(0);
266         Oid                     b = PG_GETARG_OID(1);
267
268         PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
269 }
270
271 Datum
272 enum_smaller(PG_FUNCTION_ARGS)
273 {
274         Oid                     a = PG_GETARG_OID(0);
275         Oid                     b = PG_GETARG_OID(1);
276
277         PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
278 }
279
280 Datum
281 enum_larger(PG_FUNCTION_ARGS)
282 {
283         Oid                     a = PG_GETARG_OID(0);
284         Oid                     b = PG_GETARG_OID(1);
285
286         PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
287 }
288
289 Datum
290 enum_cmp(PG_FUNCTION_ARGS)
291 {
292         Oid                     a = PG_GETARG_OID(0);
293         Oid                     b = PG_GETARG_OID(1);
294
295         if (a == b)
296                 PG_RETURN_INT32(0);
297         else if (enum_cmp_internal(a, b, fcinfo) > 0)
298                 PG_RETURN_INT32(1);
299         else
300                 PG_RETURN_INT32(-1);
301 }
302
303 /* Enum programming support functions */
304
305 /*
306  * enum_endpoint: common code for enum_first/enum_last
307  */
308 static Oid
309 enum_endpoint(Oid enumtypoid, ScanDirection direction)
310 {
311         Relation        enum_rel;
312         Relation        enum_idx;
313         SysScanDesc enum_scan;
314         HeapTuple       enum_tuple;
315         ScanKeyData skey;
316         Oid                     minmax;
317
318         /*
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.
322          */
323         ScanKeyInit(&skey,
324                                 Anum_pg_enum_enumtypid,
325                                 BTEqualStrategyNumber, F_OIDEQ,
326                                 ObjectIdGetDatum(enumtypoid));
327
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(),
332                                                                                    1, &skey);
333
334         enum_tuple = systable_getnext_ordered(enum_scan, direction);
335         if (HeapTupleIsValid(enum_tuple))
336                 minmax = HeapTupleGetOid(enum_tuple);
337         else
338                 minmax = InvalidOid;
339
340         systable_endscan_ordered(enum_scan);
341         index_close(enum_idx, AccessShareLock);
342         heap_close(enum_rel, AccessShareLock);
343
344         return minmax;
345 }
346
347 Datum
348 enum_first(PG_FUNCTION_ARGS)
349 {
350         Oid                     enumtypoid;
351         Oid                     min;
352
353         /*
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.
357          */
358         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
359         if (enumtypoid == InvalidOid)
360                 ereport(ERROR,
361                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
362                                  errmsg("could not determine actual enum type")));
363
364         /* Get the OID using the index */
365         min = enum_endpoint(enumtypoid, ForwardScanDirection);
366
367         if (!OidIsValid(min))
368                 ereport(ERROR,
369                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
370                                  errmsg("enum %s contains no values",
371                                                 format_type_be(enumtypoid))));
372
373         PG_RETURN_OID(min);
374 }
375
376 Datum
377 enum_last(PG_FUNCTION_ARGS)
378 {
379         Oid                     enumtypoid;
380         Oid                     max;
381
382         /*
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.
386          */
387         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
388         if (enumtypoid == InvalidOid)
389                 ereport(ERROR,
390                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
391                                  errmsg("could not determine actual enum type")));
392
393         /* Get the OID using the index */
394         max = enum_endpoint(enumtypoid, BackwardScanDirection);
395
396         if (!OidIsValid(max))
397                 ereport(ERROR,
398                                 (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
399                                  errmsg("enum %s contains no values",
400                                                 format_type_be(enumtypoid))));
401
402         PG_RETURN_OID(max);
403 }
404
405 /* 2-argument variant of enum_range */
406 Datum
407 enum_range_bounds(PG_FUNCTION_ARGS)
408 {
409         Oid                     lower;
410         Oid                     upper;
411         Oid                     enumtypoid;
412
413         if (PG_ARGISNULL(0))
414                 lower = InvalidOid;
415         else
416                 lower = PG_GETARG_OID(0);
417         if (PG_ARGISNULL(1))
418                 upper = InvalidOid;
419         else
420                 upper = PG_GETARG_OID(1);
421
422         /*
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.
426          */
427         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
428         if (enumtypoid == InvalidOid)
429                 ereport(ERROR,
430                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
431                                  errmsg("could not determine actual enum type")));
432
433         PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
434 }
435
436 /* 1-argument variant of enum_range */
437 Datum
438 enum_range_all(PG_FUNCTION_ARGS)
439 {
440         Oid                     enumtypoid;
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         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
448         if (enumtypoid == InvalidOid)
449                 ereport(ERROR,
450                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
451                                  errmsg("could not determine actual enum type")));
452
453         PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
454                                                                                           InvalidOid, InvalidOid));
455 }
456
457 static ArrayType *
458 enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
459 {
460         ArrayType  *result;
461         Relation        enum_rel;
462         Relation        enum_idx;
463         SysScanDesc enum_scan;
464         HeapTuple       enum_tuple;
465         ScanKeyData skey;
466         Datum      *elems;
467         int                     max,
468                                 cnt;
469         bool            left_found;
470
471         /*
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.
475          */
476         ScanKeyInit(&skey,
477                                 Anum_pg_enum_enumtypid,
478                                 BTEqualStrategyNumber, F_OIDEQ,
479                                 ObjectIdGetDatum(enumtypoid));
480
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(),
485                                                                                    1, &skey);
486
487         max = 64;
488         elems = (Datum *) palloc(max * sizeof(Datum));
489         cnt = 0;
490         left_found = !OidIsValid(lower);
491
492         while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
493         {
494                 Oid                     enum_oid = HeapTupleGetOid(enum_tuple);
495
496                 if (!left_found && lower == enum_oid)
497                         left_found = true;
498
499                 if (left_found)
500                 {
501                         if (cnt >= max)
502                         {
503                                 max *= 2;
504                                 elems = (Datum *) repalloc(elems, max * sizeof(Datum));
505                         }
506
507                         elems[cnt++] = ObjectIdGetDatum(enum_oid);
508                 }
509
510                 if (OidIsValid(upper) && upper == enum_oid)
511                         break;
512         }
513
514         systable_endscan_ordered(enum_scan);
515         index_close(enum_idx, AccessShareLock);
516         heap_close(enum_rel, AccessShareLock);
517
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');
521
522         pfree(elems);
523
524         return result;
525 }