1 /*-------------------------------------------------------------------------
2  *
3  * enum.c
4  *	  I/O functions, operators, aggregates etc for enum types
5  *
6  * Copyright (c) 2006-2020, 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/htup_details.h"
18 #include "access/table.h"
19 #include "catalog/indexing.h"
20 #include "catalog/pg_enum.h"
21 #include "libpq/pqformat.h"
22 #include "storage/procarray.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 /*
36  * Disallow use of an uncommitted pg_enum tuple.
37  *
38  * We need to make sure that uncommitted enum values don't get into indexes.
39  * If they did, and if we then rolled back the pg_enum addition, we'd have
40  * broken the index because value comparisons will not work reliably without
41  * an underlying pg_enum entry.  (Note that removal of the heap entry
42  * containing an enum value is not sufficient to ensure that it doesn't appear
43  * in upper levels of indexes.)  To do this we prevent an uncommitted row from
44  * being used for any SQL-level purpose.  This is stronger than necessary,
45  * since the value might not be getting inserted into a table or there might
46  * be no index on its column, but it's easy to enforce centrally.
47  *
48  * However, it's okay to allow use of uncommitted values belonging to enum
49  * types that were themselves created in the same transaction, because then
50  * any such index would also be new and would go away altogether on rollback.
51  * We don't implement that fully right now, but we do allow free use of enum
52  * values created during CREATE TYPE AS ENUM, which are surely of the same
53  * lifespan as the enum type.  (This case is required by "pg_restore -1".)
54  * Values added by ALTER TYPE ADD VALUE are currently restricted, but could
55  * be allowed if the enum type could be proven to have been created earlier
56  * in the same transaction.  (Note that comparing tuple xmins would not work
57  * for that, because the type tuple might have been updated in the current
58  * transaction.  Subtransactions also create hazards to be accounted for.)
59  *
60  * This function needs to be called (directly or indirectly) in any of the
61  * functions below that could return an enum value to SQL operations.
62  */
63 static void
check_safe_enum_use(HeapTuple enumval_tup)64 check_safe_enum_use(HeapTuple enumval_tup)
65 {
66 	TransactionId xmin;
67 	Form_pg_enum en = (Form_pg_enum) GETSTRUCT(enumval_tup);
68 
69 	/*
70 	 * If the row is hinted as committed, it's surely safe.  This provides a
71 	 * fast path for all normal use-cases.
72 	 */
73 	if (HeapTupleHeaderXminCommitted(enumval_tup->t_data))
74 		return;
75 
76 	/*
77 	 * Usually, a row would get hinted as committed when it's read or loaded
78 	 * into syscache; but just in case not, let's check the xmin directly.
79 	 */
80 	xmin = HeapTupleHeaderGetXmin(enumval_tup->t_data);
81 	if (!TransactionIdIsInProgress(xmin) &&
82 		TransactionIdDidCommit(xmin))
83 		return;
84 
85 	/*
86 	 * Check if the enum value is blacklisted.  If not, it's safe, because it
87 	 * was made during CREATE TYPE AS ENUM and can't be shorter-lived than its
88 	 * owning type.  (This'd also be false for values made by other
89 	 * transactions; but the previous tests should have handled all of those.)
90 	 */
91 	if (!EnumBlacklisted(en->oid))
92 		return;
93 
94 	/*
95 	 * There might well be other tests we could do here to narrow down the
96 	 * unsafe conditions, but for now just raise an exception.
97 	 */
98 	ereport(ERROR,
99 			(errcode(ERRCODE_UNSAFE_NEW_ENUM_VALUE_USAGE),
100 			 errmsg("unsafe use of new value \"%s\" of enum type %s",
101 					NameStr(en->enumlabel),
102 					format_type_be(en->enumtypid)),
103 			 errhint("New enum values must be committed before they can be used.")));
104 }
105 
106 
107 /* Basic I/O support */
108 
109 Datum
enum_in(PG_FUNCTION_ARGS)110 enum_in(PG_FUNCTION_ARGS)
111 {
112 	char	   *name = PG_GETARG_CSTRING(0);
113 	Oid			enumtypoid = PG_GETARG_OID(1);
114 	Oid			enumoid;
115 	HeapTuple	tup;
116 
117 	/* must check length to prevent Assert failure within SearchSysCache */
118 	if (strlen(name) >= NAMEDATALEN)
119 		ereport(ERROR,
120 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
121 				 errmsg("invalid input value for enum %s: \"%s\"",
122 						format_type_be(enumtypoid),
123 						name)));
124 
125 	tup = SearchSysCache2(ENUMTYPOIDNAME,
126 						  ObjectIdGetDatum(enumtypoid),
127 						  CStringGetDatum(name));
128 	if (!HeapTupleIsValid(tup))
129 		ereport(ERROR,
130 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
131 				 errmsg("invalid input value for enum %s: \"%s\"",
132 						format_type_be(enumtypoid),
133 						name)));
134 
135 	/* check it's safe to use in SQL */
136 	check_safe_enum_use(tup);
137 
138 	/*
139 	 * This comes from pg_enum.oid and stores system oids in user tables. This
140 	 * oid must be preserved by binary upgrades.
141 	 */
142 	enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
143 
144 	ReleaseSysCache(tup);
145 
146 	PG_RETURN_OID(enumoid);
147 }
148 
149 Datum
enum_out(PG_FUNCTION_ARGS)150 enum_out(PG_FUNCTION_ARGS)
151 {
152 	Oid			enumval = PG_GETARG_OID(0);
153 	char	   *result;
154 	HeapTuple	tup;
155 	Form_pg_enum en;
156 
157 	tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
158 	if (!HeapTupleIsValid(tup))
159 		ereport(ERROR,
160 				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
161 				 errmsg("invalid internal value for enum: %u",
162 						enumval)));
163 	en = (Form_pg_enum) GETSTRUCT(tup);
164 
165 	result = pstrdup(NameStr(en->enumlabel));
166 
167 	ReleaseSysCache(tup);
168 
169 	PG_RETURN_CSTRING(result);
170 }
171 
172 /* Binary I/O support */
173 Datum
enum_recv(PG_FUNCTION_ARGS)174 enum_recv(PG_FUNCTION_ARGS)
175 {
176 	StringInfo	buf = (StringInfo) PG_GETARG_POINTER(0);
177 	Oid			enumtypoid = PG_GETARG_OID(1);
178 	Oid			enumoid;
179 	HeapTuple	tup;
180 	char	   *name;
181 	int			nbytes;
182 
183 	name = pq_getmsgtext(buf, buf->len - buf->cursor, &nbytes);
184 
185 	/* must check length to prevent Assert failure within SearchSysCache */
186 	if (strlen(name) >= NAMEDATALEN)
187 		ereport(ERROR,
188 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
189 				 errmsg("invalid input value for enum %s: \"%s\"",
190 						format_type_be(enumtypoid),
191 						name)));
192 
193 	tup = SearchSysCache2(ENUMTYPOIDNAME,
194 						  ObjectIdGetDatum(enumtypoid),
195 						  CStringGetDatum(name));
196 	if (!HeapTupleIsValid(tup))
197 		ereport(ERROR,
198 				(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
199 				 errmsg("invalid input value for enum %s: \"%s\"",
200 						format_type_be(enumtypoid),
201 						name)));
202 
203 	/* check it's safe to use in SQL */
204 	check_safe_enum_use(tup);
205 
206 	enumoid = ((Form_pg_enum) GETSTRUCT(tup))->oid;
207 
208 	ReleaseSysCache(tup);
209 
210 	pfree(name);
211 
212 	PG_RETURN_OID(enumoid);
213 }
214 
215 Datum
enum_send(PG_FUNCTION_ARGS)216 enum_send(PG_FUNCTION_ARGS)
217 {
218 	Oid			enumval = PG_GETARG_OID(0);
219 	StringInfoData buf;
220 	HeapTuple	tup;
221 	Form_pg_enum en;
222 
223 	tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(enumval));
224 	if (!HeapTupleIsValid(tup))
225 		ereport(ERROR,
226 				(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
227 				 errmsg("invalid internal value for enum: %u",
228 						enumval)));
229 	en = (Form_pg_enum) GETSTRUCT(tup);
230 
231 	pq_begintypsend(&buf);
232 	pq_sendtext(&buf, NameStr(en->enumlabel), strlen(NameStr(en->enumlabel)));
233 
234 	ReleaseSysCache(tup);
235 
236 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
237 }
238 
239 /* Comparison functions and related */
240 
241 /*
242  * enum_cmp_internal is the common engine for all the visible comparison
243  * functions, except for enum_eq and enum_ne which can just check for OID
244  * equality directly.
245  */
246 static int
enum_cmp_internal(Oid arg1,Oid arg2,FunctionCallInfo fcinfo)247 enum_cmp_internal(Oid arg1, Oid arg2, FunctionCallInfo fcinfo)
248 {
249 	TypeCacheEntry *tcache;
250 
251 	/*
252 	 * We don't need the typcache except in the hopefully-uncommon case that
253 	 * one or both Oids are odd.  This means that cursory testing of code that
254 	 * fails to pass flinfo to an enum comparison function might not disclose
255 	 * the oversight.  To make such errors more obvious, Assert that we have a
256 	 * place to cache even when we take a fast-path exit.
257 	 */
258 	Assert(fcinfo->flinfo != NULL);
259 
260 	/* Equal OIDs are equal no matter what */
261 	if (arg1 == arg2)
262 		return 0;
263 
264 	/* Fast path: even-numbered Oids are known to compare correctly */
265 	if ((arg1 & 1) == 0 && (arg2 & 1) == 0)
266 	{
267 		if (arg1 < arg2)
268 			return -1;
269 		else
270 			return 1;
271 	}
272 
273 	/* Locate the typcache entry for the enum type */
274 	tcache = (TypeCacheEntry *) fcinfo->flinfo->fn_extra;
275 	if (tcache == NULL)
276 	{
277 		HeapTuple	enum_tup;
278 		Form_pg_enum en;
279 		Oid			typeoid;
280 
281 		/* Get the OID of the enum type containing arg1 */
282 		enum_tup = SearchSysCache1(ENUMOID, ObjectIdGetDatum(arg1));
283 		if (!HeapTupleIsValid(enum_tup))
284 			ereport(ERROR,
285 					(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
286 					 errmsg("invalid internal value for enum: %u",
287 							arg1)));
288 		en = (Form_pg_enum) GETSTRUCT(enum_tup);
289 		typeoid = en->enumtypid;
290 		ReleaseSysCache(enum_tup);
291 		/* Now locate and remember the typcache entry */
292 		tcache = lookup_type_cache(typeoid, 0);
293 		fcinfo->flinfo->fn_extra = (void *) tcache;
294 	}
295 
296 	/* The remaining comparison logic is in typcache.c */
297 	return compare_values_of_enum(tcache, arg1, arg2);
298 }
299 
300 Datum
enum_lt(PG_FUNCTION_ARGS)301 enum_lt(PG_FUNCTION_ARGS)
302 {
303 	Oid			a = PG_GETARG_OID(0);
304 	Oid			b = PG_GETARG_OID(1);
305 
306 	PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) < 0);
307 }
308 
309 Datum
enum_le(PG_FUNCTION_ARGS)310 enum_le(PG_FUNCTION_ARGS)
311 {
312 	Oid			a = PG_GETARG_OID(0);
313 	Oid			b = PG_GETARG_OID(1);
314 
315 	PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) <= 0);
316 }
317 
318 Datum
enum_eq(PG_FUNCTION_ARGS)319 enum_eq(PG_FUNCTION_ARGS)
320 {
321 	Oid			a = PG_GETARG_OID(0);
322 	Oid			b = PG_GETARG_OID(1);
323 
324 	PG_RETURN_BOOL(a == b);
325 }
326 
327 Datum
enum_ne(PG_FUNCTION_ARGS)328 enum_ne(PG_FUNCTION_ARGS)
329 {
330 	Oid			a = PG_GETARG_OID(0);
331 	Oid			b = PG_GETARG_OID(1);
332 
333 	PG_RETURN_BOOL(a != b);
334 }
335 
336 Datum
enum_ge(PG_FUNCTION_ARGS)337 enum_ge(PG_FUNCTION_ARGS)
338 {
339 	Oid			a = PG_GETARG_OID(0);
340 	Oid			b = PG_GETARG_OID(1);
341 
342 	PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) >= 0);
343 }
344 
345 Datum
enum_gt(PG_FUNCTION_ARGS)346 enum_gt(PG_FUNCTION_ARGS)
347 {
348 	Oid			a = PG_GETARG_OID(0);
349 	Oid			b = PG_GETARG_OID(1);
350 
351 	PG_RETURN_BOOL(enum_cmp_internal(a, b, fcinfo) > 0);
352 }
353 
354 Datum
enum_smaller(PG_FUNCTION_ARGS)355 enum_smaller(PG_FUNCTION_ARGS)
356 {
357 	Oid			a = PG_GETARG_OID(0);
358 	Oid			b = PG_GETARG_OID(1);
359 
360 	PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) < 0 ? a : b);
361 }
362 
363 Datum
enum_larger(PG_FUNCTION_ARGS)364 enum_larger(PG_FUNCTION_ARGS)
365 {
366 	Oid			a = PG_GETARG_OID(0);
367 	Oid			b = PG_GETARG_OID(1);
368 
369 	PG_RETURN_OID(enum_cmp_internal(a, b, fcinfo) > 0 ? a : b);
370 }
371 
372 Datum
enum_cmp(PG_FUNCTION_ARGS)373 enum_cmp(PG_FUNCTION_ARGS)
374 {
375 	Oid			a = PG_GETARG_OID(0);
376 	Oid			b = PG_GETARG_OID(1);
377 
378 	PG_RETURN_INT32(enum_cmp_internal(a, b, fcinfo));
379 }
380 
381 /* Enum programming support functions */
382 
383 /*
384  * enum_endpoint: common code for enum_first/enum_last
385  */
386 static Oid
enum_endpoint(Oid enumtypoid,ScanDirection direction)387 enum_endpoint(Oid enumtypoid, ScanDirection direction)
388 {
389 	Relation	enum_rel;
390 	Relation	enum_idx;
391 	SysScanDesc enum_scan;
392 	HeapTuple	enum_tuple;
393 	ScanKeyData skey;
394 	Oid			minmax;
395 
396 	/*
397 	 * Find the first/last enum member using pg_enum_typid_sortorder_index.
398 	 * Note we must not use the syscache.  See comments for RenumberEnumType
399 	 * in catalog/pg_enum.c for more info.
400 	 */
401 	ScanKeyInit(&skey,
402 				Anum_pg_enum_enumtypid,
403 				BTEqualStrategyNumber, F_OIDEQ,
404 				ObjectIdGetDatum(enumtypoid));
405 
406 	enum_rel = table_open(EnumRelationId, AccessShareLock);
407 	enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
408 	enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL,
409 										   1, &skey);
410 
411 	enum_tuple = systable_getnext_ordered(enum_scan, direction);
412 	if (HeapTupleIsValid(enum_tuple))
413 	{
414 		/* check it's safe to use in SQL */
415 		check_safe_enum_use(enum_tuple);
416 		minmax = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
417 	}
418 	else
419 	{
420 		/* should only happen with an empty enum */
421 		minmax = InvalidOid;
422 	}
423 
424 	systable_endscan_ordered(enum_scan);
425 	index_close(enum_idx, AccessShareLock);
426 	table_close(enum_rel, AccessShareLock);
427 
428 	return minmax;
429 }
430 
431 Datum
enum_first(PG_FUNCTION_ARGS)432 enum_first(PG_FUNCTION_ARGS)
433 {
434 	Oid			enumtypoid;
435 	Oid			min;
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 	/* Get the OID using the index */
449 	min = enum_endpoint(enumtypoid, ForwardScanDirection);
450 
451 	if (!OidIsValid(min))
452 		ereport(ERROR,
453 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
454 				 errmsg("enum %s contains no values",
455 						format_type_be(enumtypoid))));
456 
457 	PG_RETURN_OID(min);
458 }
459 
460 Datum
enum_last(PG_FUNCTION_ARGS)461 enum_last(PG_FUNCTION_ARGS)
462 {
463 	Oid			enumtypoid;
464 	Oid			max;
465 
466 	/*
467 	 * We rely on being able to get the specific enum type from the calling
468 	 * expression tree.  Notice that the actual value of the argument isn't
469 	 * examined at all; in particular it might be NULL.
470 	 */
471 	enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
472 	if (enumtypoid == InvalidOid)
473 		ereport(ERROR,
474 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
475 				 errmsg("could not determine actual enum type")));
476 
477 	/* Get the OID using the index */
478 	max = enum_endpoint(enumtypoid, BackwardScanDirection);
479 
480 	if (!OidIsValid(max))
481 		ereport(ERROR,
482 				(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
483 				 errmsg("enum %s contains no values",
484 						format_type_be(enumtypoid))));
485 
486 	PG_RETURN_OID(max);
487 }
488 
489 /* 2-argument variant of enum_range */
490 Datum
enum_range_bounds(PG_FUNCTION_ARGS)491 enum_range_bounds(PG_FUNCTION_ARGS)
492 {
493 	Oid			lower;
494 	Oid			upper;
495 	Oid			enumtypoid;
496 
497 	if (PG_ARGISNULL(0))
498 		lower = InvalidOid;
499 	else
500 		lower = PG_GETARG_OID(0);
501 	if (PG_ARGISNULL(1))
502 		upper = InvalidOid;
503 	else
504 		upper = PG_GETARG_OID(1);
505 
506 	/*
507 	 * We rely on being able to get the specific enum type from the calling
508 	 * expression tree.  The generic type mechanism should have ensured that
509 	 * both are of the same type.
510 	 */
511 	enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
512 	if (enumtypoid == InvalidOid)
513 		ereport(ERROR,
514 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
515 				 errmsg("could not determine actual enum type")));
516 
517 	PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
518 }
519 
520 /* 1-argument variant of enum_range */
521 Datum
enum_range_all(PG_FUNCTION_ARGS)522 enum_range_all(PG_FUNCTION_ARGS)
523 {
524 	Oid			enumtypoid;
525 
526 	/*
527 	 * We rely on being able to get the specific enum type from the calling
528 	 * expression tree.  Notice that the actual value of the argument isn't
529 	 * examined at all; in particular it might be NULL.
530 	 */
531 	enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
532 	if (enumtypoid == InvalidOid)
533 		ereport(ERROR,
534 				(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
535 				 errmsg("could not determine actual enum type")));
536 
537 	PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
538 											  InvalidOid, InvalidOid));
539 }
540 
541 static ArrayType *
enum_range_internal(Oid enumtypoid,Oid lower,Oid upper)542 enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
543 {
544 	ArrayType  *result;
545 	Relation	enum_rel;
546 	Relation	enum_idx;
547 	SysScanDesc enum_scan;
548 	HeapTuple	enum_tuple;
549 	ScanKeyData skey;
550 	Datum	   *elems;
551 	int			max,
552 				cnt;
553 	bool		left_found;
554 
555 	/*
556 	 * Scan the enum members in order using pg_enum_typid_sortorder_index.
557 	 * Note we must not use the syscache.  See comments for RenumberEnumType
558 	 * in catalog/pg_enum.c for more info.
559 	 */
560 	ScanKeyInit(&skey,
561 				Anum_pg_enum_enumtypid,
562 				BTEqualStrategyNumber, F_OIDEQ,
563 				ObjectIdGetDatum(enumtypoid));
564 
565 	enum_rel = table_open(EnumRelationId, AccessShareLock);
566 	enum_idx = index_open(EnumTypIdSortOrderIndexId, AccessShareLock);
567 	enum_scan = systable_beginscan_ordered(enum_rel, enum_idx, NULL, 1, &skey);
568 
569 	max = 64;
570 	elems = (Datum *) palloc(max * sizeof(Datum));
571 	cnt = 0;
572 	left_found = !OidIsValid(lower);
573 
574 	while (HeapTupleIsValid(enum_tuple = systable_getnext_ordered(enum_scan, ForwardScanDirection)))
575 	{
576 		Oid			enum_oid = ((Form_pg_enum) GETSTRUCT(enum_tuple))->oid;
577 
578 		if (!left_found && lower == enum_oid)
579 			left_found = true;
580 
581 		if (left_found)
582 		{
583 			/* check it's safe to use in SQL */
584 			check_safe_enum_use(enum_tuple);
585 
586 			if (cnt >= max)
587 			{
588 				max *= 2;
589 				elems = (Datum *) repalloc(elems, max * sizeof(Datum));
590 			}
591 
592 			elems[cnt++] = ObjectIdGetDatum(enum_oid);
593 		}
594 
595 		if (OidIsValid(upper) && upper == enum_oid)
596 			break;
597 	}
598 
599 	systable_endscan_ordered(enum_scan);
600 	index_close(enum_idx, AccessShareLock);
601 	table_close(enum_rel, AccessShareLock);
602 
603 	/* and build the result array */
604 	/* note this hardwires some details about the representation of Oid */
605 	result = construct_array(elems, cnt, enumtypoid,
606 							 sizeof(Oid), true, TYPALIGN_INT);
607 
608 	pfree(elems);
609 
610 	return result;
611 }
612