~ubuntu-branches/ubuntu/oneiric/postgresql-9.1/oneiric-security

« back to all changes in this revision

Viewing changes to src/backend/utils/adt/enum.c

  • Committer: Bazaar Package Importer
  • Author(s): Martin Pitt
  • Date: 2011-05-11 10:41:53 UTC
  • Revision ID: james.westby@ubuntu.com-20110511104153-psbh2o58553fv1m0
Tags: upstream-9.1~beta1
ImportĀ upstreamĀ versionĀ 9.1~beta1

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*-------------------------------------------------------------------------
 
2
 *
 
3
 * enum.c
 
4
 *        I/O functions, operators, aggregates etc for enum types
 
5
 *
 
6
 * Copyright (c) 2006-2011, 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 "catalog/indexing.h"
 
19
#include "catalog/pg_enum.h"
 
20
#include "fmgr.h"
 
21
#include "libpq/pqformat.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
/* Basic I/O support */
 
35
 
 
36
Datum
 
37
enum_in(PG_FUNCTION_ARGS)
 
38
{
 
39
        char       *name = PG_GETARG_CSTRING(0);
 
40
        Oid                     enumtypoid = PG_GETARG_OID(1);
 
41
        Oid                     enumoid;
 
42
        HeapTuple       tup;
 
43
 
 
44
        /* must check length to prevent Assert failure within SearchSysCache */
 
45
        if (strlen(name) >= NAMEDATALEN)
 
46
                ereport(ERROR,
 
47
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
48
                                 errmsg("invalid input value for enum %s: \"%s\"",
 
49
                                                format_type_be(enumtypoid),
 
50
                                                name)));
 
51
 
 
52
        tup = SearchSysCache2(ENUMTYPOIDNAME,
 
53
                                                  ObjectIdGetDatum(enumtypoid),
 
54
                                                  CStringGetDatum(name));
 
55
        if (!HeapTupleIsValid(tup))
 
56
                ereport(ERROR,
 
57
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
58
                                 errmsg("invalid input value for enum %s: \"%s\"",
 
59
                                                format_type_be(enumtypoid),
 
60
                                                name)));
 
61
 
 
62
        /*
 
63
         * This comes from pg_enum.oid and stores system oids in user tables. This
 
64
         * oid must be preserved by binary upgrades.
 
65
         */
 
66
        enumoid = HeapTupleGetOid(tup);
 
67
 
 
68
        ReleaseSysCache(tup);
 
69
 
 
70
        PG_RETURN_OID(enumoid);
 
71
}
 
72
 
 
73
Datum
 
74
enum_out(PG_FUNCTION_ARGS)
 
75
{
 
76
        Oid                     enumval = PG_GETARG_OID(0);
 
77
        char       *result;
 
78
        HeapTuple       tup;
 
79
        Form_pg_enum en;
 
80
 
 
81
        tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
 
82
        if (!HeapTupleIsValid(tup))
 
83
                ereport(ERROR,
 
84
                                (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
85
                                 errmsg("invalid internal value for enum: %u",
 
86
                                                enumval)));
 
87
        en = (Form_pg_enum) GETSTRUCT(tup);
 
88
 
 
89
        result = pstrdup(NameStr(en->enumlabel));
 
90
 
 
91
        ReleaseSysCache(tup);
 
92
 
 
93
        PG_RETURN_CSTRING(result);
 
94
}
 
95
 
 
96
/* Binary I/O support */
 
97
Datum
 
98
enum_recv(PG_FUNCTION_ARGS)
 
99
{
 
100
        StringInfo      buf = (StringInfo) PG_GETARG_POINTER(0);
 
101
        Oid                     enumtypoid = PG_GETARG_OID(1);
 
102
        Oid                     enumoid;
 
103
        HeapTuple       tup;
 
104
        char       *name;
 
105
        int                     nbytes;
 
106
 
 
107
        name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
 
108
 
 
109
        /* must check length to prevent Assert failure within SearchSysCache */
 
110
        if (strlen(name) >= NAMEDATALEN)
 
111
                ereport(ERROR,
 
112
                                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
 
113
                                 errmsg("invalid input value for enum %s: \"%s\"",
 
114
                                                format_type_be(enumtypoid),
 
115
                                                name)));
 
116
 
 
117
        tup = SearchSysCache2(ENUMTYPOIDNAME,
 
118
                                                  ObjectIdGetDatum(enumtypoid),
 
119
                                                  CStringGetDatum(name));
 
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 = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
 
145
        if (!HeapTupleIsValid(tup))
 
146
                ereport(ERROR,
 
147
                                (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
148
                                 errmsg("invalid internal value for enum: %u",
 
149
                                                enumval)));
 
150
        en = (Form_pg_enum) GETSTRUCT(tup);
 
151
 
 
152
        pq_begintypsend(&buf);
 
153
        pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
 
154
 
 
155
        ReleaseSysCache(tup);
 
156
 
 
157
        PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 
158
}
 
159
 
 
160
/* Comparison functions and related */
 
161
 
 
162
/*
 
163
 * enum_cmp_internal is the common engine for all the visible comparison
 
164
 * functions, except for enum_eq and enum_ne which can just check for OID
 
165
 * equality directly.
 
166
 */
 
167
static int
 
168
enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
 
169
{
 
170
        TypeCacheEntry *tcache;
 
171
 
 
172
        /* Equal OIDs are equal no matter what */
 
173
        if (arg1 == arg2)
 
174
                return 0;
 
175
 
 
176
        /* Fast path: even-numbered Oids are known to compare correctly */
 
177
        if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
 
178
        {
 
179
                if (arg1 < arg2)
 
180
                        return -1;
 
181
                else
 
182
                        return 1;
 
183
        }
 
184
 
 
185
        /* Locate the typcache entry for the enum type */
 
186
        tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
 
187
        if (tcache == NULL)
 
188
        {
 
189
                HeapTuple       enum_tup;
 
190
                Form_pg_enum en;
 
191
                Oid                     typeoid;
 
192
 
 
193
                /* Get the OID of the enum type containing arg1 */
 
194
                enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
 
195
                if (!HeapTupleIsValid(enum_tup))
 
196
                        ereport(ERROR,
 
197
                                        (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
 
198
                                         errmsg("invalid internal value for enum: %u",
 
199
                                                        arg1)));
 
200
                en = (Form_pg_enum) GETSTRUCT(enum_tup);
 
201
                typeoid = en->enumtypid;
 
202
                ReleaseSysCache(enum_tup);
 
203
                /* Now locate and remember the typcache entry */
 
204
                tcache = lookup_type_cache(typeoid, 0);
 
205
                fcinfo->flinfo->fn_extra = (void *) tcache;
 
206
        }
 
207
 
 
208
        /* The remaining comparison logic is in typcache.c */
 
209
        return compare_values_of_enum(tcache, arg1, arg2);
 
210
}
 
211
 
 
212
Datum
 
213
enum_lt(PG_FUNCTION_ARGS)
 
214
{
 
215
        Oid                     a = PG_GETARG_OID(0);
 
216
        Oid                     b = PG_GETARG_OID(1);
 
217
 
 
218
        PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
 
219
}
 
220
 
 
221
Datum
 
222
enum_le(PG_FUNCTION_ARGS)
 
223
{
 
224
        Oid                     a = PG_GETARG_OID(0);
 
225
        Oid                     b = PG_GETARG_OID(1);
 
226
 
 
227
        PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
 
228
}
 
229
 
 
230
Datum
 
231
enum_eq(PG_FUNCTION_ARGS)
 
232
{
 
233
        Oid                     a = PG_GETARG_OID(0);
 
234
        Oid                     b = PG_GETARG_OID(1);
 
235
 
 
236
        PG_RETURN_BOOL(a == b);
 
237
}
 
238
 
 
239
Datum
 
240
enum_ne(PG_FUNCTION_ARGS)
 
241
{
 
242
        Oid                     a = PG_GETARG_OID(0);
 
243
        Oid                     b = PG_GETARG_OID(1);
 
244
 
 
245
        PG_RETURN_BOOL(a != b);
 
246
}
 
247
 
 
248
Datum
 
249
enum_ge(PG_FUNCTION_ARGS)
 
250
{
 
251
        Oid                     a = PG_GETARG_OID(0);
 
252
        Oid                     b = PG_GETARG_OID(1);
 
253
 
 
254
        PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
 
255
}
 
256
 
 
257
Datum
 
258
enum_gt(PG_FUNCTION_ARGS)
 
259
{
 
260
        Oid                     a = PG_GETARG_OID(0);
 
261
        Oid                     b = PG_GETARG_OID(1);
 
262
 
 
263
        PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
 
264
}
 
265
 
 
266
Datum
 
267
enum_smaller(PG_FUNCTION_ARGS)
 
268
{
 
269
        Oid                     a = PG_GETARG_OID(0);
 
270
        Oid                     b = PG_GETARG_OID(1);
 
271
 
 
272
        PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
 
273
}
 
274
 
 
275
Datum
 
276
enum_larger(PG_FUNCTION_ARGS)
 
277
{
 
278
        Oid                     a = PG_GETARG_OID(0);
 
279
        Oid                     b = PG_GETARG_OID(1);
 
280
 
 
281
        PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
 
282
}
 
283
 
 
284
Datum
 
285
enum_cmp(PG_FUNCTION_ARGS)
 
286
{
 
287
        Oid                     a = PG_GETARG_OID(0);
 
288
        Oid                     b = PG_GETARG_OID(1);
 
289
 
 
290
        if (a == b)
 
291
                PG_RETURN_INT32(0);
 
292
        else if (enum_cmp_internal(a, b, fcinfo) > 0)
 
293
                PG_RETURN_INT32(1);
 
294
        else
 
295
                PG_RETURN_INT32(-1);
 
296
}
 
297
 
 
298
/* Enum programming support functions */
 
299
 
 
300
/*
 
301
 * enum_endpoint: common code for enum_first/enum_last
 
302
 */
 
303
static Oid
 
304
enum_endpoint(Oid enumtypoid, ScanDirection direction)
 
305
{
 
306
        Relation        enum_rel;
 
307
        Relation        enum_idx;
 
308
        SysScanDesc enum_scan;
 
309
        HeapTuple       enum_tuple;
 
310
        ScanKeyData skey;
 
311
        Oid                     minmax;
 
312
 
 
313
        /*
 
314
         * Find the first/last enum member using pg_enum_typid_sortorder_index.
 
315
         * Note we must not use the syscache, and must use an MVCC snapshot here.
 
316
         * See comments for RenumberEnumType in catalog/pg_enum.c for more info.
 
317
         */
 
318
        ScanKeyInit(&skey,
 
319
                                Anum_pg_enum_enumtypid,
 
320
                                BTEqualStrategyNumber, F_OIDEQ,
 
321
                                ObjectIdGetDatum(enumtypoid));
 
322
 
 
323
        enum_rel = heap_open(EnumRelationId, AccessShareLock);
 
324
        enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
 
325
        enum_scan = systable_beginscan_ordered(enum_rel, enum_idx,
 
326
                                                                                   GetTransactionSnapshot(),
 
327
                                                                                   1, &skey);
 
328
 
 
329
        enum_tuple = systable_getnext_ordered(enum_scan, direction);
 
330
        if (HeapTupleIsValid(enum_tuple))
 
331
                minmax = HeapTupleGetOid(enum_tuple);
 
332
        else
 
333
                minmax = InvalidOid;
 
334
 
 
335
        systable_endscan_ordered(enum_scan);
 
336
        index_close(enum_idx, AccessShareLock);
 
337
        heap_close(enum_rel, AccessShareLock);
 
338
 
 
339
        return minmax;
 
340
}
 
341
 
 
342
Datum
 
343
enum_first(PG_FUNCTION_ARGS)
 
344
{
 
345
        Oid                     enumtypoid;
 
346
        Oid                     min;
 
347
 
 
348
        /*
 
349
         * We rely on being able to get the specific enum type from the calling
 
350
         * expression tree.  Notice that the actual value of the argument isn't
 
351
         * examined at all; in particular it might be NULL.
 
352
         */
 
353
        enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
354
        if (enumtypoid == InvalidOid)
 
355
                ereport(ERROR,
 
356
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
357
                                 errmsg("could not determine actual enum type")));
 
358
 
 
359
        /* Get the OID using the index */
 
360
        min = enum_endpoint(enumtypoid, ForwardScanDirection);
 
361
 
 
362
        if (!OidIsValid(min))
 
363
                ereport(ERROR,
 
364
                                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 
365
                                 errmsg("enum %s contains no values",
 
366
                                                format_type_be(enumtypoid))));
 
367
 
 
368
        PG_RETURN_OID(min);
 
369
}
 
370
 
 
371
Datum
 
372
enum_last(PG_FUNCTION_ARGS)
 
373
{
 
374
        Oid                     enumtypoid;
 
375
        Oid                     max;
 
376
 
 
377
        /*
 
378
         * We rely on being able to get the specific enum type from the calling
 
379
         * expression tree.  Notice that the actual value of the argument isn't
 
380
         * examined at all; in particular it might be NULL.
 
381
         */
 
382
        enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
383
        if (enumtypoid == InvalidOid)
 
384
                ereport(ERROR,
 
385
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
386
                                 errmsg("could not determine actual enum type")));
 
387
 
 
388
        /* Get the OID using the index */
 
389
        max = enum_endpoint(enumtypoid, BackwardScanDirection);
 
390
 
 
391
        if (!OidIsValid(max))
 
392
                ereport(ERROR,
 
393
                                (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
 
394
                                 errmsg("enum %s contains no values",
 
395
                                                format_type_be(enumtypoid))));
 
396
 
 
397
        PG_RETURN_OID(max);
 
398
}
 
399
 
 
400
/* 2-argument variant of enum_range */
 
401
Datum
 
402
enum_range_bounds(PG_FUNCTION_ARGS)
 
403
{
 
404
        Oid                     lower;
 
405
        Oid                     upper;
 
406
        Oid                     enumtypoid;
 
407
 
 
408
        if (PG_ARGISNULL(0))
 
409
                lower = InvalidOid;
 
410
        else
 
411
                lower = PG_GETARG_OID(0);
 
412
        if (PG_ARGISNULL(1))
 
413
                upper = InvalidOid;
 
414
        else
 
415
                upper = PG_GETARG_OID(1);
 
416
 
 
417
        /*
 
418
         * We rely on being able to get the specific enum type from the calling
 
419
         * expression tree.  The generic type mechanism should have ensured that
 
420
         * both are of the same type.
 
421
         */
 
422
        enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
423
        if (enumtypoid == InvalidOid)
 
424
                ereport(ERROR,
 
425
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
426
                                 errmsg("could not determine actual enum type")));
 
427
 
 
428
        PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
 
429
}
 
430
 
 
431
/* 1-argument variant of enum_range */
 
432
Datum
 
433
enum_range_all(PG_FUNCTION_ARGS)
 
434
{
 
435
        Oid                     enumtypoid;
 
436
 
 
437
        /*
 
438
         * We rely on being able to get the specific enum type from the calling
 
439
         * expression tree.  Notice that the actual value of the argument isn't
 
440
         * examined at all; in particular it might be NULL.
 
441
         */
 
442
        enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
 
443
        if (enumtypoid == InvalidOid)
 
444
                ereport(ERROR,
 
445
                                (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
 
446
                                 errmsg("could not determine actual enum type")));
 
447
 
 
448
        PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
 
449
                                                                                          InvalidOid, InvalidOid));
 
450
}
 
451
 
 
452
static ArrayType *
 
453
enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
 
454
{
 
455
        ArrayType  *result;
 
456
        Relation        enum_rel;
 
457
        Relation        enum_idx;
 
458
        SysScanDesc enum_scan;
 
459
        HeapTuple       enum_tuple;
 
460
        ScanKeyData skey;
 
461
        Datum      *elems;
 
462
        int                     max,
 
463
                                cnt;
 
464
        bool            left_found;
 
465
 
 
466
        /*
 
467
         * Scan the enum members in order using pg_enum_typid_sortorder_index.
 
468
         * Note we must not use the syscache, and must use an MVCC snapshot here.
 
469
         * See comments for RenumberEnumType in catalog/pg_enum.c for more info.
 
470
         */
 
471
        ScanKeyInit(&skey,
 
472
                                Anum_pg_enum_enumtypid,
 
473
                                BTEqualStrategyNumber, F_OIDEQ,
 
474
                                ObjectIdGetDatum(enumtypoid));
 
475
 
 
476
        enum_rel = heap_open(EnumRelationId, AccessShareLock);
 
477
        enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
 
478
        enum_scan = systable_beginscan_ordered(enum_rel, enum_idx,
 
479
                                                                                   GetTransactionSnapshot(),
 
480
                                                                                   1, &skey);
 
481
 
 
482
        max = 64;
 
483
        elems = (Datum *) palloc(max * sizeof(Datum));
 
484
        cnt = 0;
 
485
        left_found = !OidIsValid(lower);
 
486
 
 
487
        while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
 
488
        {
 
489
                Oid                     enum_oid = HeapTupleGetOid(enum_tuple);
 
490
 
 
491
                if (!left_found && lower == enum_oid)
 
492
                        left_found = true;
 
493
 
 
494
                if (left_found)
 
495
                {
 
496
                        if (cnt >= max)
 
497
                        {
 
498
                                max *= 2;
 
499
                                elems = (Datum *) repalloc(elems, max * sizeof(Datum));
 
500
                        }
 
501
 
 
502
                        elems[cnt++] = ObjectIdGetDatum(enum_oid);
 
503
                }
 
504
 
 
505
                if (OidIsValid(upper) && upper == enum_oid)
 
506
                        break;
 
507
        }
 
508
 
 
509
        systable_endscan_ordered(enum_scan);
 
510
        index_close(enum_idx, AccessShareLock);
 
511
        heap_close(enum_rel, AccessShareLock);
 
512
 
 
513
        /* and build the result array */
 
514
        /* note this hardwires some details about the representation of Oid */
 
515
        result = construct_array(elems, cnt, enumtypoid, sizeof(Oid), true, 'i');
 
516
 
 
517
        pfree(elems);
 
518
 
 
519
        return result;
 
520
}