PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
enum.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * enum.c
4  * I/O functions, operators, aggregates etc for enum types
5  *
6  * Copyright (c) 2006-2015, 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 "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
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 
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
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
99 {
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 
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
138 {
139  Oid enumval = PG_GETARG_OID(0);
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 
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
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
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
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
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
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
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
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
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
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
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. See comments for RenumberEnumType
316  * in catalog/pg_enum.c for more info.
317  */
318  ScanKeyInit(&skey,
320  BTEqualStrategyNumber, F_OIDEQ,
321  ObjectIdGetDatum(enumtypoid));
322 
325  enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
326  1, &skey);
327 
328  enum_tuple = systable_getnext_ordered(enum_scan, direction);
329  if (HeapTupleIsValid(enum_tuple))
330  minmax = HeapTupleGetOid(enum_tuple);
331  else
332  minmax = InvalidOid;
333 
334  systable_endscan_ordered(enum_scan);
335  index_close(enum_idx, AccessShareLock);
336  heap_close(enum_rel, AccessShareLock);
337 
338  return minmax;
339 }
340 
341 Datum
343 {
344  Oid enumtypoid;
345  Oid min;
346 
347  /*
348  * We rely on being able to get the specific enum type from the calling
349  * expression tree. Notice that the actual value of the argument isn't
350  * examined at all; in particular it might be NULL.
351  */
352  enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
353  if (enumtypoid == InvalidOid)
354  ereport(ERROR,
355  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
356  errmsg("could not determine actual enum type")));
357 
358  /* Get the OID using the index */
359  min = enum_endpoint(enumtypoid, ForwardScanDirection);
360 
361  if (!OidIsValid(min))
362  ereport(ERROR,
363  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
364  errmsg("enum %s contains no values",
365  format_type_be(enumtypoid))));
366 
367  PG_RETURN_OID(min);
368 }
369 
370 Datum
372 {
373  Oid enumtypoid;
374  Oid max;
375 
376  /*
377  * We rely on being able to get the specific enum type from the calling
378  * expression tree. Notice that the actual value of the argument isn't
379  * examined at all; in particular it might be NULL.
380  */
381  enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
382  if (enumtypoid == InvalidOid)
383  ereport(ERROR,
384  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
385  errmsg("could not determine actual enum type")));
386 
387  /* Get the OID using the index */
388  max = enum_endpoint(enumtypoid, BackwardScanDirection);
389 
390  if (!OidIsValid(max))
391  ereport(ERROR,
392  (errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
393  errmsg("enum %s contains no values",
394  format_type_be(enumtypoid))));
395 
396  PG_RETURN_OID(max);
397 }
398 
399 /* 2-argument variant of enum_range */
400 Datum
402 {
403  Oid lower;
404  Oid upper;
405  Oid enumtypoid;
406 
407  if (PG_ARGISNULL(0))
408  lower = InvalidOid;
409  else
410  lower = PG_GETARG_OID(0);
411  if (PG_ARGISNULL(1))
412  upper = InvalidOid;
413  else
414  upper = PG_GETARG_OID(1);
415 
416  /*
417  * We rely on being able to get the specific enum type from the calling
418  * expression tree. The generic type mechanism should have ensured that
419  * both are of the same type.
420  */
421  enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
422  if (enumtypoid == InvalidOid)
423  ereport(ERROR,
424  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
425  errmsg("could not determine actual enum type")));
426 
427  PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
428 }
429 
430 /* 1-argument variant of enum_range */
431 Datum
433 {
434  Oid enumtypoid;
435 
436  /*
437  * We rely on being able to get the specific enum type from the calling
438  * expression tree. Notice that the actual value of the argument isn't
439  * examined at all; in particular it might be NULL.
440  */
441  enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
442  if (enumtypoid == InvalidOid)
443  ereport(ERROR,
444  (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
445  errmsg("could not determine actual enum type")));
446 
449 }
450 
451 static ArrayType *
453 {
454  ArrayType *result;
455  Relation enum_rel;
456  Relation enum_idx;
457  SysScanDesc enum_scan;
458  HeapTuple enum_tuple;
459  ScanKeyData skey;
460  Datum *elems;
461  int max,
462  cnt;
463  bool left_found;
464 
465  /*
466  * Scan the enum members in order using pg_enum_typid_sortorder_index.
467  * Note we must not use the syscache. See comments for RenumberEnumType
468  * in catalog/pg_enum.c for more info.
469  */
470  ScanKeyInit(&skey,
472  BTEqualStrategyNumber, F_OIDEQ,
473  ObjectIdGetDatum(enumtypoid));
474 
477  enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);
478 
479  max = 64;
480  elems = (Datum *) palloc(max * sizeof(Datum));
481  cnt = 0;
482  left_found = !OidIsValid(lower);
483 
484  while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
485  {
486  Oid enum_oid = HeapTupleGetOid(enum_tuple);
487 
488  if (!left_found && lower == enum_oid)
489  left_found = true;
490 
491  if (left_found)
492  {
493  if (cnt >= max)
494  {
495  max *= 2;
496  elems = (Datum *) repalloc(elems, max * sizeof(Datum));
497  }
498 
499  elems[cnt++] = ObjectIdGetDatum(enum_oid);
500  }
501 
502  if (OidIsValid(upper) && upper == enum_oid)
503  break;
504  }
505 
506  systable_endscan_ordered(enum_scan);
507  index_close(enum_idx, AccessShareLock);
508  heap_close(enum_rel, AccessShareLock);
509 
510  /* and build the result array */
511  /* note this hardwires some details about the representation of Oid */
512  result = construct_array(elems, cnt, enumtypoid, sizeof(Oid), true, 'i');
513 
514  pfree(elems);
515 
516  return result;
517 }
int compare_values_of_enum(TypeCacheEntry *tcache, Oid arg1, Oid arg2)
Definition: typcache.c:1486
#define GETSTRUCT(TUP)
Definition: htup_details.h:631
Datum enum_recv(PG_FUNCTION_ARGS)
Definition: enum.c:98
Datum enum_in(PG_FUNCTION_ARGS)
Definition: enum.c:37
Datum enum_last(PG_FUNCTION_ARGS)
Definition: enum.c:371
Datum lower(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:43
void pq_begintypsend(StringInfo buf)
Definition: pqformat.c:358
char * pstrdup(const char *in)
Definition: mcxt.c:1160
StringInfoData * StringInfo
Definition: stringinfo.h:43
#define PG_RETURN_INT32(x)
Definition: fmgr.h:298
ArrayType * construct_array(Datum *elems, int nelems, Oid elmtype, int elmlen, bool elmbyval, char elmalign)
Definition: arrayfuncs.c:3277
#define AccessShareLock
Definition: lockdefs.h:36
int errcode(int sqlerrcode)
Definition: elog.c:573
void pq_sendtext(StringInfo buf, const char *str, int slen)
Definition: pqformat.c:162
char * format_type_be(Oid type_oid)
Definition: format_type.c:94
#define PG_GETARG_POINTER(n)
Definition: fmgr.h:232
#define heap_close(r, l)
Definition: heapam.h:97
#define PG_RETURN_BYTEA_P(x)
Definition: fmgr.h:313
Datum upper(PG_FUNCTION_ARGS)
Definition: oracle_compat.c:74
HeapTuple systable_getnext_ordered(SysScanDesc sysscan, ScanDirection direction)
Definition: genam.c:591
unsigned int Oid
Definition: postgres_ext.h:31
#define OidIsValid(objectId)
Definition: c.h:519
bytea * pq_endtypsend(StringInfo buf)
Definition: pqformat.c:378
Datum enum_eq(PG_FUNCTION_ARGS)
Definition: enum.c:231
#define SearchSysCache1(cacheId, key1)
Definition: syscache.h:141
Datum enum_lt(PG_FUNCTION_ARGS)
Definition: enum.c:213
Datum enum_range_all(PG_FUNCTION_ARGS)
Definition: enum.c:432
#define NAMEDATALEN
Datum enum_out(PG_FUNCTION_ARGS)
Definition: enum.c:74
FmgrInfo * flinfo
Definition: fmgr.h:71
void pfree(void *pointer)
Definition: mcxt.c:993
Datum enum_gt(PG_FUNCTION_ARGS)
Definition: enum.c:258
static ArrayType * enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
Definition: enum.c:452
Datum enum_ne(PG_FUNCTION_ARGS)
Definition: enum.c:240
Datum enum_first(PG_FUNCTION_ARGS)
Definition: enum.c:342
#define ObjectIdGetDatum(X)
Definition: postgres.h:515
#define ERROR
Definition: elog.h:41
#define EnumRelationId
Definition: pg_enum.h:32
Oid get_fn_expr_argtype(FmgrInfo *flinfo, int argnum)
Definition: fmgr.c:2313
Datum enum_le(PG_FUNCTION_ARGS)
Definition: enum.c:222
static char * buf
Definition: pg_test_fsync.c:65
#define PG_GETARG_OID(n)
Definition: fmgr.h:231
static Oid enum_endpoint(Oid enumtypoid, ScanDirection direction)
Definition: enum.c:304
ScanDirection
Definition: sdir.h:22
#define CStringGetDatum(X)
Definition: postgres.h:586
FormData_pg_enum * Form_pg_enum
Definition: pg_enum.h:46
Datum enum_ge(PG_FUNCTION_ARGS)
Definition: enum.c:249
#define PG_RETURN_ARRAYTYPE_P(x)
Definition: array.h:246
#define ereport(elevel, rest)
Definition: elog.h:132
char * pq_getmsgtext(StringInfo msg, int rawbytes, int *nbytes)
Definition: pqformat.c:587
Datum enum_larger(PG_FUNCTION_ARGS)
Definition: enum.c:276
Datum enum_smaller(PG_FUNCTION_ARGS)
Definition: enum.c:267
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:303
uintptr_t Datum
Definition: postgres.h:374
void ReleaseSysCache(HeapTuple tuple)
Definition: syscache.c:989
Datum enum_range_bounds(PG_FUNCTION_ARGS)
Definition: enum.c:401
Relation heap_open(Oid relationId, LOCKMODE lockmode)
Definition: heapam.c:1234
void systable_endscan_ordered(SysScanDesc sysscan)
Definition: genam.c:608
TypeCacheEntry * lookup_type_cache(Oid type_id, int flags)
Definition: typcache.c:182
#define InvalidOid
Definition: postgres_ext.h:36
Datum enum_send(PG_FUNCTION_ARGS)
Definition: enum.c:137
#define PG_ARGISNULL(n)
Definition: fmgr.h:166
#define HeapTupleIsValid(tuple)
Definition: htup.h:77
#define NULL
Definition: c.h:215
Datum enum_cmp(PG_FUNCTION_ARGS)
Definition: enum.c:285
#define PG_RETURN_CSTRING(x)
Definition: fmgr.h:306
void * fn_extra
Definition: fmgr.h:61
void * repalloc(void *pointer, Size size)
Definition: mcxt.c:1022
const char * name
Definition: encode.c:521
void index_close(Relation relation, LOCKMODE lockmode)
Definition: indexam.c:188
#define Anum_pg_enum_enumtypid
Definition: pg_enum.h:53
void * palloc(Size size)
Definition: mcxt.c:892
int errmsg(const char *fmt,...)
Definition: elog.c:795
SysScanDesc systable_beginscan_ordered(Relation heapRelation, Relation indexRelation, Snapshot snapshot, int nkeys, ScanKey key)
Definition: genam.c:527
#define EnumTypIdSortOrderIndexId
Definition: indexing.h:153
static int enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
Definition: enum.c:168
#define NameStr(name)
Definition: c.h:483
void ScanKeyInit(ScanKey entry, AttrNumber attributeNumber, StrategyNumber strategy, RegProcedure procedure, Datum argument)
Definition: scankey.c:76
#define PG_GETARG_CSTRING(n)
Definition: fmgr.h:233
#define PG_FUNCTION_ARGS
Definition: fmgr.h:150
#define HeapTupleGetOid(tuple)
Definition: htup_details.h:670
#define PG_RETURN_OID(x)
Definition: fmgr.h:304
Relation index_open(Oid relationId, LOCKMODE lockmode)
Definition: indexam.c:163
#define BTEqualStrategyNumber
Definition: stratnum.h:31
#define SearchSysCache2(cacheId, key1, key2)
Definition: syscache.h:143