]> granicus.if.org Git - postgresql/blob - src/backend/utils/adt/enum.c
binary migration: pg_migrator
[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-2009, PostgreSQL Global Development Group
7  *
8  *
9  * IDENTIFICATION
10  *        $PostgreSQL: pgsql/src/backend/utils/adt/enum.c,v 1.8 2009/12/19 00:47:57 momjian Exp $
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15
16 #include "catalog/pg_enum.h"
17 #include "fmgr.h"
18 #include "utils/array.h"
19 #include "utils/builtins.h"
20 #include "utils/lsyscache.h"
21 #include "utils/syscache.h"
22 #include "libpq/pqformat.h"
23 #include "miscadmin.h"
24
25
26 static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
27 static int      enum_elem_cmp(const void *left, const void *right);
28
29
30 /* Basic I/O support */
31
32 Datum
33 enum_in(PG_FUNCTION_ARGS)
34 {
35         char       *name = PG_GETARG_CSTRING(0);
36         Oid                     enumtypoid = PG_GETARG_OID(1);
37         Oid                     enumoid;
38         HeapTuple       tup;
39
40         /* must check length to prevent Assert failure within SearchSysCache */
41         if (strlen(name) >= NAMEDATALEN)
42                 ereport(ERROR,
43                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
44                                  errmsg("invalid input value for enum %s: \"%s\"",
45                                                 format_type_be(enumtypoid),
46                                                 name)));
47
48         tup = SearchSysCache(ENUMTYPOIDNAME,
49                                                  ObjectIdGetDatum(enumtypoid),
50                                                  CStringGetDatum(name),
51                                                  0, 0);
52         if (!HeapTupleIsValid(tup))
53                 ereport(ERROR,
54                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
55                                  errmsg("invalid input value for enum %s: \"%s\"",
56                                                 format_type_be(enumtypoid),
57                                                 name)));
58
59         /*
60          *      This comes from pg_enum.oid and stores system oids in user tables.
61          *      This oid must be preserved by binary upgrades.
62          */
63         enumoid = HeapTupleGetOid(tup);
64
65         ReleaseSysCache(tup);
66
67         PG_RETURN_OID(enumoid);
68 }
69
70 Datum
71 enum_out(PG_FUNCTION_ARGS)
72 {
73         Oid                     enumval = PG_GETARG_OID(0);
74         char       *result;
75         HeapTuple       tup;
76         Form_pg_enum en;
77
78         tup = SearchSysCache(ENUMOID,
79                                                  ObjectIdGetDatum(enumval),
80                                                  0, 0, 0);
81         if (!HeapTupleIsValid(tup))
82                 ereport(ERROR,
83                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
84                                  errmsg("invalid internal value for enum: %u",
85                                                 enumval)));
86         en = (Form_pg_enum) GETSTRUCT(tup);
87
88         result = pstrdup(NameStr(en->enumlabel));
89
90         ReleaseSysCache(tup);
91
92         PG_RETURN_CSTRING(result);
93 }
94
95 /* Binary I/O support */
96 Datum
97 enum_recv(PG_FUNCTION_ARGS)
98 {
99         StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
100         Oid                     enumtypoid = PG_GETARG_OID(1);
101         Oid                     enumoid;
102         HeapTuple       tup;
103         char       *name;
104         int                     nbytes;
105
106         name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
107
108         /* must check length to prevent Assert failure within SearchSysCache */
109         if (strlen(name) >= NAMEDATALEN)
110                 ereport(ERROR,
111                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
112                                  errmsg("invalid input value for enum %s: \"%s\"",
113                                                 format_type_be(enumtypoid),
114                                                 name)));
115
116         tup = SearchSysCache(ENUMTYPOIDNAME,
117                                                  ObjectIdGetDatum(enumtypoid),
118                                                  CStringGetDatum(name),
119                                                  0, 0);
120         if (!HeapTupleIsValid(tup))
121                 ereport(ERROR,
122                                 (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
123                                  errmsg("invalid input value for enum %s: \"%s\"",
124                                                 format_type_be(enumtypoid),
125                                                 name)));
126
127         enumoid = HeapTupleGetOid(tup);
128
129         ReleaseSysCache(tup);
130
131         pfree(name);
132
133         PG_RETURN_OID(enumoid);
134 }
135
136 Datum
137 enum_send(PG_FUNCTION_ARGS)
138 {
139         Oid                     enumval = PG_GETARG_OID(0);
140         StringInfoData buf;
141         HeapTuple       tup;
142         Form_pg_enum en;
143
144         tup = SearchSysCache(ENUMOID,
145                                                  ObjectIdGetDatum(enumval),
146                                                  0, 0, 0);
147         if (!HeapTupleIsValid(tup))
148                 ereport(ERROR,
149                                 (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
150                                  errmsg("invalid internal value for enum: %u",
151                                                 enumval)));
152         en = (Form_pg_enum) GETSTRUCT(tup);
153
154         pq_begintypsend(&buf);
155         pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
156
157         ReleaseSysCache(tup);
158
159         PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
160 }
161
162 /* Comparison functions and related */
163
164 Datum
165 enum_lt(PG_FUNCTION_ARGS)
166 {
167         Oid                     a = PG_GETARG_OID(0);
168         Oid                     b = PG_GETARG_OID(1);
169
170         PG_RETURN_BOOL(a < b);
171 }
172
173 Datum
174 enum_le(PG_FUNCTION_ARGS)
175 {
176         Oid                     a = PG_GETARG_OID(0);
177         Oid                     b = PG_GETARG_OID(1);
178
179         PG_RETURN_BOOL(a <= b);
180 }
181
182 Datum
183 enum_eq(PG_FUNCTION_ARGS)
184 {
185         Oid                     a = PG_GETARG_OID(0);
186         Oid                     b = PG_GETARG_OID(1);
187
188         PG_RETURN_BOOL(a == b);
189 }
190
191 Datum
192 enum_ne(PG_FUNCTION_ARGS)
193 {
194         Oid                     a = PG_GETARG_OID(0);
195         Oid                     b = PG_GETARG_OID(1);
196
197         PG_RETURN_BOOL(a != b);
198 }
199
200 Datum
201 enum_ge(PG_FUNCTION_ARGS)
202 {
203         Oid                     a = PG_GETARG_OID(0);
204         Oid                     b = PG_GETARG_OID(1);
205
206         PG_RETURN_BOOL(a >= b);
207 }
208
209 Datum
210 enum_gt(PG_FUNCTION_ARGS)
211 {
212         Oid                     a = PG_GETARG_OID(0);
213         Oid                     b = PG_GETARG_OID(1);
214
215         PG_RETURN_BOOL(a > b);
216 }
217
218 Datum
219 enum_smaller(PG_FUNCTION_ARGS)
220 {
221         Oid                     a = PG_GETARG_OID(0);
222         Oid                     b = PG_GETARG_OID(1);
223
224         PG_RETURN_OID(a <= b ? a : b);
225 }
226
227 Datum
228 enum_larger(PG_FUNCTION_ARGS)
229 {
230         Oid                     a = PG_GETARG_OID(0);
231         Oid                     b = PG_GETARG_OID(1);
232
233         PG_RETURN_OID(a >= b ? a : b);
234 }
235
236 Datum
237 enum_cmp(PG_FUNCTION_ARGS)
238 {
239         Oid                     a = PG_GETARG_OID(0);
240         Oid                     b = PG_GETARG_OID(1);
241
242         if (a > b)
243                 PG_RETURN_INT32(1);
244         else if (a == b)
245                 PG_RETURN_INT32(0);
246         else
247                 PG_RETURN_INT32(-1);
248 }
249
250 /* Enum programming support functions */
251
252 Datum
253 enum_first(PG_FUNCTION_ARGS)
254 {
255         Oid                     enumtypoid;
256         Oid                     min = InvalidOid;
257         CatCList   *list;
258         int                     num,
259                                 i;
260
261         /*
262          * We rely on being able to get the specific enum type from the calling
263          * expression tree.  Notice that the actual value of the argument isn't
264          * examined at all; in particular it might be NULL.
265          */
266         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
267         if (enumtypoid == InvalidOid)
268                 ereport(ERROR,
269                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
270                                  errmsg("could not determine actual enum type")));
271
272         list = SearchSysCacheList(ENUMTYPOIDNAME, 1,
273                                                           ObjectIdGetDatum(enumtypoid),
274                                                           0, 0, 0);
275         num = list->n_members;
276         for (i = 0; i < num; i++)
277         {
278                 Oid                     valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
279
280                 if (!OidIsValid(min) || valoid < min)
281                         min = valoid;
282         }
283
284         ReleaseCatCacheList(list);
285
286         if (!OidIsValid(min))           /* should not happen */
287                 elog(ERROR, "no values found for enum %s",
288                          format_type_be(enumtypoid));
289
290         PG_RETURN_OID(min);
291 }
292
293 Datum
294 enum_last(PG_FUNCTION_ARGS)
295 {
296         Oid                     enumtypoid;
297         Oid                     max = InvalidOid;
298         CatCList   *list;
299         int                     num,
300                                 i;
301
302         /*
303          * We rely on being able to get the specific enum type from the calling
304          * expression tree.  Notice that the actual value of the argument isn't
305          * examined at all; in particular it might be NULL.
306          */
307         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
308         if (enumtypoid == InvalidOid)
309                 ereport(ERROR,
310                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
311                                  errmsg("could not determine actual enum type")));
312
313         list = SearchSysCacheList(ENUMTYPOIDNAME, 1,
314                                                           ObjectIdGetDatum(enumtypoid),
315                                                           0, 0, 0);
316         num = list->n_members;
317         for (i = 0; i < num; i++)
318         {
319                 Oid                     valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
320
321                 if (!OidIsValid(max) || valoid > max)
322                         max = valoid;
323         }
324
325         ReleaseCatCacheList(list);
326
327         if (!OidIsValid(max))           /* should not happen */
328                 elog(ERROR, "no values found for enum %s",
329                          format_type_be(enumtypoid));
330
331         PG_RETURN_OID(max);
332 }
333
334 /* 2-argument variant of enum_range */
335 Datum
336 enum_range_bounds(PG_FUNCTION_ARGS)
337 {
338         Oid                     lower;
339         Oid                     upper;
340         Oid                     enumtypoid;
341
342         if (PG_ARGISNULL(0))
343                 lower = InvalidOid;
344         else
345                 lower = PG_GETARG_OID(0);
346         if (PG_ARGISNULL(1))
347                 upper = InvalidOid;
348         else
349                 upper = PG_GETARG_OID(1);
350
351         /*
352          * We rely on being able to get the specific enum type from the calling
353          * expression tree.  The generic type mechanism should have ensured that
354          * both are of the same type.
355          */
356         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
357         if (enumtypoid == InvalidOid)
358                 ereport(ERROR,
359                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
360                                  errmsg("could not determine actual enum type")));
361
362         PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
363 }
364
365 /* 1-argument variant of enum_range */
366 Datum
367 enum_range_all(PG_FUNCTION_ARGS)
368 {
369         Oid                     enumtypoid;
370
371         /*
372          * We rely on being able to get the specific enum type from the calling
373          * expression tree.  Notice that the actual value of the argument isn't
374          * examined at all; in particular it might be NULL.
375          */
376         enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
377         if (enumtypoid == InvalidOid)
378                 ereport(ERROR,
379                                 (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
380                                  errmsg("could not determine actual enum type")));
381
382         PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
383                                                                                           InvalidOid, InvalidOid));
384 }
385
386 static ArrayType *
387 enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
388 {
389         ArrayType  *result;
390         CatCList   *list;
391         int                     total,
392                                 i,
393                                 j;
394         Datum      *elems;
395
396         list = SearchSysCacheList(ENUMTYPOIDNAME, 1,
397                                                           ObjectIdGetDatum(enumtypoid),
398                                                           0, 0, 0);
399         total = list->n_members;
400
401         elems = (Datum *) palloc(total * sizeof(Datum));
402
403         j = 0;
404         for (i = 0; i < total; i++)
405         {
406                 Oid                     val = HeapTupleGetOid(&(list->members[i]->tuple));
407
408                 if ((!OidIsValid(lower) || lower <= val) &&
409                         (!OidIsValid(upper) || val <= upper))
410                         elems[j++] = ObjectIdGetDatum(val);
411         }
412
413         /* shouldn't need the cache anymore */
414         ReleaseCatCacheList(list);
415
416         /* sort results into OID order */
417         qsort(elems, j, sizeof(Datum), enum_elem_cmp);
418
419         /* note this hardwires some details about the representation of Oid */
420         result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
421
422         pfree(elems);
423
424         return result;
425 }
426
427 /* qsort comparison function for Datums that are OIDs */
428 static int
429 enum_elem_cmp(const void *left, const void *right)
430 {
431         Oid                     l = DatumGetObjectId(*((const Datum *) left));
432         Oid                     r = DatumGetObjectId(*((const Datum *) right));
433
434         if (l < r)
435                 return -1;
436         if (l > r)
437                 return 1;
438         return 0;
439 }