mirror of https://github.com/postgres/postgres
pg_type.typtype whereever practical. Tom Dunstan, with some kibitzing from Tom Lane.REL8_3_STABLE
parent
a482a3e58b
commit
57690c6803
@ -0,0 +1,146 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* pg_enum.c |
||||
* routines to support manipulation of the pg_enum relation |
||||
* |
||||
* Copyright (c) 2006-2007, PostgreSQL Global Development Group |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/backend/catalog/pg_enum.c,v 1.1 2007/04/02 03:49:37 tgl Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/genam.h" |
||||
#include "access/heapam.h" |
||||
#include "catalog/catalog.h" |
||||
#include "catalog/indexing.h" |
||||
#include "catalog/pg_enum.h" |
||||
#include "utils/builtins.h" |
||||
#include "utils/fmgroids.h" |
||||
|
||||
static int oid_cmp(const void *p1, const void *p2); |
||||
|
||||
|
||||
/*
|
||||
* EnumValuesCreate |
||||
* Create an entry in pg_enum for each of the supplied enum values. |
||||
* |
||||
* vals is a list of Value strings. |
||||
*/ |
||||
void |
||||
EnumValuesCreate(Oid enumTypeOid, List *vals) |
||||
{ |
||||
Relation pg_enum; |
||||
TupleDesc tupDesc; |
||||
NameData enumlabel; |
||||
Oid *oids; |
||||
int i, n; |
||||
Datum values[Natts_pg_enum]; |
||||
char nulls[Natts_pg_enum]; |
||||
ListCell *lc; |
||||
HeapTuple tup; |
||||
|
||||
n = list_length(vals); |
||||
|
||||
/*
|
||||
* XXX we do not bother to check the list of values for duplicates --- |
||||
* if you have any, you'll get a less-than-friendly unique-index |
||||
* violation. Is it worth trying harder? |
||||
*/ |
||||
|
||||
pg_enum = heap_open(EnumRelationId, RowExclusiveLock); |
||||
tupDesc = pg_enum->rd_att; |
||||
|
||||
/*
|
||||
* Allocate oids. While this method does not absolutely guarantee |
||||
* that we generate no duplicate oids (since we haven't entered each |
||||
* oid into the table before allocating the next), trouble could only |
||||
* occur if the oid counter wraps all the way around before we finish. |
||||
* Which seems unlikely. |
||||
*/ |
||||
oids = (Oid *) palloc(n * sizeof(Oid)); |
||||
for(i = 0; i < n; i++) |
||||
{ |
||||
oids[i] = GetNewOid(pg_enum); |
||||
} |
||||
|
||||
/* sort them, just in case counter wrapped from high to low */ |
||||
qsort(oids, n, sizeof(Oid), oid_cmp); |
||||
|
||||
/* and make the entries */ |
||||
memset(nulls, ' ', sizeof(nulls)); |
||||
|
||||
i = 0; |
||||
foreach(lc, vals) |
||||
{ |
||||
char *lab = strVal(lfirst(lc)); |
||||
|
||||
values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); |
||||
namestrcpy(&enumlabel, lab); |
||||
values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); |
||||
|
||||
tup = heap_formtuple(tupDesc, values, nulls); |
||||
HeapTupleSetOid(tup, oids[i]); |
||||
|
||||
simple_heap_insert(pg_enum, tup); |
||||
CatalogUpdateIndexes(pg_enum, tup); |
||||
heap_freetuple(tup); |
||||
|
||||
i++; |
||||
} |
||||
|
||||
/* clean up */ |
||||
pfree(oids); |
||||
heap_close(pg_enum, RowExclusiveLock); |
||||
} |
||||
|
||||
|
||||
/*
|
||||
* EnumValuesDelete |
||||
* Remove all the pg_enum entries for the specified enum type. |
||||
*/ |
||||
void |
||||
EnumValuesDelete(Oid enumTypeOid) |
||||
{ |
||||
Relation pg_enum; |
||||
ScanKeyData key[1]; |
||||
SysScanDesc scan; |
||||
HeapTuple tup; |
||||
|
||||
pg_enum = heap_open(EnumRelationId, RowExclusiveLock); |
||||
|
||||
ScanKeyInit(&key[0], |
||||
Anum_pg_enum_enumtypid, |
||||
BTEqualStrategyNumber, F_OIDEQ, |
||||
ObjectIdGetDatum(enumTypeOid)); |
||||
|
||||
scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true, |
||||
SnapshotNow, 1, key); |
||||
|
||||
while (HeapTupleIsValid(tup = systable_getnext(scan))) |
||||
{ |
||||
simple_heap_delete(pg_enum, &tup->t_self); |
||||
} |
||||
|
||||
systable_endscan(scan); |
||||
|
||||
heap_close(pg_enum, RowExclusiveLock); |
||||
} |
||||
|
||||
|
||||
/* qsort comparison function */ |
||||
static int |
||||
oid_cmp(const void *p1, const void *p2) |
||||
{ |
||||
Oid v1 = *((const Oid *) p1); |
||||
Oid v2 = *((const Oid *) p2); |
||||
|
||||
if (v1 < v2) |
||||
return -1; |
||||
if (v1 > v2) |
||||
return 1; |
||||
return 0; |
||||
} |
||||
@ -0,0 +1,409 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* enum.c |
||||
* I/O functions, operators, aggregates etc for enum types |
||||
* |
||||
* Copyright (c) 2006-2007, PostgreSQL Global Development Group |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* $PostgreSQL: pgsql/src/backend/utils/adt/enum.c,v 1.1 2007/04/02 03:49:39 tgl Exp $ |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "catalog/pg_enum.h" |
||||
#include "fmgr.h" |
||||
#include "utils/array.h" |
||||
#include "utils/builtins.h" |
||||
#include "utils/lsyscache.h" |
||||
#include "utils/syscache.h" |
||||
|
||||
|
||||
static Oid cstring_enum(char *name, Oid enumtypoid); |
||||
static char *enum_cstring(Oid enumval); |
||||
static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper); |
||||
static int enum_elem_cmp(const void *left, const void *right); |
||||
|
||||
|
||||
/* Basic I/O support */ |
||||
|
||||
Datum |
||||
enum_in(PG_FUNCTION_ARGS) |
||||
{ |
||||
char *name = PG_GETARG_CSTRING(0); |
||||
Oid enumtypoid = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_OID(cstring_enum(name, enumtypoid)); |
||||
} |
||||
|
||||
/* guts of enum_in and text-to-enum */ |
||||
static Oid |
||||
cstring_enum(char *name, Oid enumtypoid) |
||||
{ |
||||
HeapTuple tup; |
||||
Oid enumoid; |
||||
|
||||
tup = SearchSysCache(ENUMTYPOIDNAME, |
||||
ObjectIdGetDatum(enumtypoid), |
||||
CStringGetDatum(name), |
||||
0, 0); |
||||
if (tup == NULL) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), |
||||
errmsg("invalid input value for enum %s: \"%s\"", |
||||
format_type_be(enumtypoid), |
||||
name))); |
||||
|
||||
enumoid = HeapTupleGetOid(tup); |
||||
|
||||
ReleaseSysCache(tup); |
||||
return enumoid; |
||||
} |
||||
|
||||
Datum |
||||
enum_out(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid enumoid = PG_GETARG_OID(0); |
||||
|
||||
PG_RETURN_CSTRING(enum_cstring(enumoid)); |
||||
} |
||||
|
||||
/* guts of enum_out and enum-to-text */ |
||||
static char * |
||||
enum_cstring(Oid enumval) |
||||
{ |
||||
HeapTuple tup; |
||||
Form_pg_enum en; |
||||
char *label; |
||||
|
||||
tup = SearchSysCache(ENUMOID, |
||||
ObjectIdGetDatum(enumval), |
||||
0, 0, 0); |
||||
if (tup == NULL) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), |
||||
errmsg("invalid internal value for enum: %u", |
||||
enumval))); |
||||
en = (Form_pg_enum) GETSTRUCT(tup); |
||||
|
||||
label = pstrdup(NameStr(en->enumlabel)); |
||||
|
||||
ReleaseSysCache(tup); |
||||
return label; |
||||
} |
||||
|
||||
/* Comparison functions and related */ |
||||
|
||||
Datum |
||||
enum_lt(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_BOOL(a < b); |
||||
} |
||||
|
||||
Datum |
||||
enum_le(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_BOOL(a <= b); |
||||
} |
||||
|
||||
Datum |
||||
enum_eq(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_BOOL(a == b); |
||||
} |
||||
|
||||
Datum |
||||
enum_ne(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_BOOL(a != b); |
||||
} |
||||
|
||||
Datum |
||||
enum_ge(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_BOOL(a >= b); |
||||
} |
||||
|
||||
Datum |
||||
enum_gt(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_BOOL(a > b); |
||||
} |
||||
|
||||
Datum |
||||
enum_smaller(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_OID(a <= b ? a : b); |
||||
} |
||||
|
||||
Datum |
||||
enum_larger(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
PG_RETURN_OID(a >= b ? a : b); |
||||
} |
||||
|
||||
Datum |
||||
enum_cmp(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid a = PG_GETARG_OID(0); |
||||
Oid b = PG_GETARG_OID(1); |
||||
|
||||
if (a > b) |
||||
PG_RETURN_INT32(1); |
||||
else if (a == b) |
||||
PG_RETURN_INT32(0); |
||||
else |
||||
PG_RETURN_INT32(-1); |
||||
} |
||||
|
||||
/* Casts between text and enum */ |
||||
|
||||
Datum |
||||
enum_text(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid enumval = PG_GETARG_OID(0); |
||||
text *result; |
||||
char *cstr; |
||||
int len; |
||||
|
||||
cstr = enum_cstring(enumval); |
||||
len = strlen(cstr); |
||||
result = (text *) palloc(VARHDRSZ + len); |
||||
SET_VARSIZE(result, VARHDRSZ + len); |
||||
memcpy(VARDATA(result), cstr, len); |
||||
pfree(cstr); |
||||
PG_RETURN_TEXT_P(result); |
||||
} |
||||
|
||||
Datum |
||||
text_enum(PG_FUNCTION_ARGS) |
||||
{ |
||||
text *textval = PG_GETARG_TEXT_P(0); |
||||
Oid enumtypoid; |
||||
char *str; |
||||
|
||||
/*
|
||||
* We rely on being able to get the specific enum type from the calling |
||||
* expression tree. |
||||
*/ |
||||
enumtypoid = get_fn_expr_rettype(fcinfo->flinfo); |
||||
if (enumtypoid == InvalidOid) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
||||
errmsg("could not determine actual enum type"))); |
||||
|
||||
str = DatumGetCString(DirectFunctionCall1(textout, |
||||
PointerGetDatum(textval))); |
||||
PG_RETURN_OID(cstring_enum(str, enumtypoid)); |
||||
} |
||||
|
||||
/* Enum programming support functions */ |
||||
|
||||
Datum |
||||
enum_first(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid enumtypoid; |
||||
Oid min = InvalidOid; |
||||
CatCList *list; |
||||
int num, i; |
||||
|
||||
/*
|
||||
* We rely on being able to get the specific enum type from the calling |
||||
* expression tree. Notice that the actual value of the argument isn't |
||||
* examined at all; in particular it might be NULL. |
||||
*/ |
||||
enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); |
||||
if (enumtypoid == InvalidOid) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
||||
errmsg("could not determine actual enum type"))); |
||||
|
||||
list = SearchSysCacheList(ENUMTYPOIDNAME, 1, |
||||
ObjectIdGetDatum(enumtypoid), |
||||
0, 0, 0); |
||||
num = list->n_members; |
||||
for (i = 0; i < num; i++) |
||||
{ |
||||
Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data); |
||||
if (!OidIsValid(min) || valoid < min) |
||||
min = valoid; |
||||
} |
||||
|
||||
ReleaseCatCacheList(list); |
||||
|
||||
if (!OidIsValid(min)) /* should not happen */ |
||||
elog(ERROR, "no values found for enum %s", |
||||
format_type_be(enumtypoid)); |
||||
|
||||
PG_RETURN_OID(min); |
||||
} |
||||
|
||||
Datum |
||||
enum_last(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid enumtypoid; |
||||
Oid max = InvalidOid; |
||||
CatCList *list; |
||||
int num, i; |
||||
|
||||
/*
|
||||
* We rely on being able to get the specific enum type from the calling |
||||
* expression tree. Notice that the actual value of the argument isn't |
||||
* examined at all; in particular it might be NULL. |
||||
*/ |
||||
enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); |
||||
if (enumtypoid == InvalidOid) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
||||
errmsg("could not determine actual enum type"))); |
||||
|
||||
list = SearchSysCacheList(ENUMTYPOIDNAME, 1, |
||||
ObjectIdGetDatum(enumtypoid), |
||||
0, 0, 0); |
||||
num = list->n_members; |
||||
for (i = 0; i < num; i++) |
||||
{ |
||||
Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data); |
||||
if(!OidIsValid(max) || valoid > max) |
||||
max = valoid; |
||||
} |
||||
|
||||
ReleaseCatCacheList(list); |
||||
|
||||
if (!OidIsValid(max)) /* should not happen */ |
||||
elog(ERROR, "no values found for enum %s", |
||||
format_type_be(enumtypoid)); |
||||
|
||||
PG_RETURN_OID(max); |
||||
} |
||||
|
||||
/* 2-argument variant of enum_range */ |
||||
Datum |
||||
enum_range_bounds(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid lower; |
||||
Oid upper; |
||||
Oid enumtypoid; |
||||
|
||||
if (PG_ARGISNULL(0)) |
||||
lower = InvalidOid; |
||||
else |
||||
lower = PG_GETARG_OID(0); |
||||
if (PG_ARGISNULL(1)) |
||||
upper = InvalidOid; |
||||
else |
||||
upper = PG_GETARG_OID(1); |
||||
|
||||
/*
|
||||
* We rely on being able to get the specific enum type from the calling |
||||
* expression tree. The generic type mechanism should have ensured that |
||||
* both are of the same type. |
||||
*/ |
||||
enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); |
||||
if (enumtypoid == InvalidOid) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
||||
errmsg("could not determine actual enum type"))); |
||||
|
||||
PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper)); |
||||
} |
||||
|
||||
/* 1-argument variant of enum_range */ |
||||
Datum |
||||
enum_range_all(PG_FUNCTION_ARGS) |
||||
{ |
||||
Oid enumtypoid; |
||||
|
||||
/*
|
||||
* We rely on being able to get the specific enum type from the calling |
||||
* expression tree. Notice that the actual value of the argument isn't |
||||
* examined at all; in particular it might be NULL. |
||||
*/ |
||||
enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); |
||||
if (enumtypoid == InvalidOid) |
||||
ereport(ERROR, |
||||
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), |
||||
errmsg("could not determine actual enum type"))); |
||||
|
||||
PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, |
||||
InvalidOid, InvalidOid)); |
||||
} |
||||
|
||||
static ArrayType * |
||||
enum_range_internal(Oid enumtypoid, Oid lower, Oid upper) |
||||
{ |
||||
ArrayType *result; |
||||
CatCList *list; |
||||
int total, i, j; |
||||
Datum *elems; |
||||
|
||||
list = SearchSysCacheList(ENUMTYPOIDNAME, 1, |
||||
ObjectIdGetDatum(enumtypoid), |
||||
0, 0, 0); |
||||
total = list->n_members; |
||||
|
||||
elems = (Datum *) palloc(total * sizeof(Datum)); |
||||
|
||||
j = 0; |
||||
for (i = 0; i < total; i++) |
||||
{ |
||||
Oid val = HeapTupleGetOid(&(list->members[i]->tuple)); |
||||
|
||||
if ((!OidIsValid(lower) || lower <= val) && |
||||
(!OidIsValid(upper) || val <= upper)) |
||||
elems[j++] = ObjectIdGetDatum(val); |
||||
} |
||||
|
||||
/* shouldn't need the cache anymore */ |
||||
ReleaseCatCacheList(list); |
||||
|
||||
/* sort results into OID order */ |
||||
qsort(elems, j, sizeof(Datum), enum_elem_cmp); |
||||
|
||||
/* note this hardwires some details about the representation of Oid */ |
||||
result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i'); |
||||
|
||||
pfree(elems); |
||||
|
||||
return result; |
||||
} |
||||
|
||||
/* qsort comparison function for Datums that are OIDs */ |
||||
static int |
||||
enum_elem_cmp(const void *left, const void *right) |
||||
{ |
||||
Oid l = DatumGetObjectId(*((const Datum *) left)); |
||||
Oid r = DatumGetObjectId(*((const Datum *) right)); |
||||
|
||||
if (l < r) |
||||
return -1; |
||||
if (l > r) |
||||
return 1; |
||||
return 0; |
||||
} |
||||
@ -0,0 +1,72 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* pg_enum.h |
||||
* definition of the system "enum" relation (pg_enum) |
||||
* along with the relation's initial contents. |
||||
* |
||||
* |
||||
* Copyright (c) 2006-2007, PostgreSQL Global Development Group |
||||
* |
||||
* $PostgreSQL: pgsql/src/include/catalog/pg_enum.h,v 1.1 2007/04/02 03:49:40 tgl Exp $ |
||||
* |
||||
* NOTES |
||||
* the genbki.sh script reads this file and generates .bki |
||||
* information from the DATA() statements. |
||||
* |
||||
* XXX do NOT break up DATA() statements into multiple lines! |
||||
* the scripts are not as smart as you might think... |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef PG_ENUM_H |
||||
#define PG_ENUM_H |
||||
|
||||
#include "nodes/pg_list.h" |
||||
|
||||
/* ----------------
|
||||
* postgres.h contains the system type definitions and the |
||||
* CATALOG(), BKI_BOOTSTRAP and DATA() sugar words so this file |
||||
* can be read by both genbki.sh and the C compiler. |
||||
* ---------------- |
||||
*/ |
||||
|
||||
/* ----------------
|
||||
* pg_enum definition. cpp turns this into |
||||
* typedef struct FormData_pg_enum |
||||
* ---------------- |
||||
*/ |
||||
#define EnumRelationId 3501 |
||||
|
||||
CATALOG(pg_enum,3501) |
||||
{ |
||||
Oid enumtypid; /* OID of owning enum type */ |
||||
NameData enumlabel; /* text representation of enum value */ |
||||
} FormData_pg_enum; |
||||
|
||||
/* ----------------
|
||||
* Form_pg_enum corresponds to a pointer to a tuple with |
||||
* the format of pg_enum relation. |
||||
* ---------------- |
||||
*/ |
||||
typedef FormData_pg_enum *Form_pg_enum; |
||||
|
||||
/* ----------------
|
||||
* compiler constants for pg_enum |
||||
* ---------------- |
||||
*/ |
||||
#define Natts_pg_enum 2 |
||||
#define Anum_pg_enum_enumtypid 1 |
||||
#define Anum_pg_enum_enumlabel 2 |
||||
|
||||
/* ----------------
|
||||
* pg_enum has no initial contents |
||||
* ---------------- |
||||
*/ |
||||
|
||||
/*
|
||||
* prototypes for functions in pg_enum.c |
||||
*/ |
||||
extern void EnumValuesCreate(Oid enumTypeOid, List *vals); |
||||
extern void EnumValuesDelete(Oid enumTypeOid); |
||||
|
||||
#endif /* PG_ENUM_H */ |
||||
@ -0,0 +1,407 @@ |
||||
-- |
||||
-- Enum tests |
||||
-- |
||||
CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); |
||||
-- |
||||
-- Did it create the right number of rows? |
||||
-- |
||||
SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype; |
||||
count |
||||
------- |
||||
6 |
||||
(1 row) |
||||
|
||||
-- |
||||
-- I/O functions |
||||
-- |
||||
SELECT 'red'::rainbow; |
||||
rainbow |
||||
--------- |
||||
red |
||||
(1 row) |
||||
|
||||
SELECT 'mauve'::rainbow; |
||||
ERROR: invalid input value for enum rainbow: "mauve" |
||||
-- |
||||
-- Basic table creation, row selection |
||||
-- |
||||
CREATE TABLE enumtest (col rainbow); |
||||
INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green'); |
||||
COPY enumtest FROM stdin; |
||||
SELECT * FROM enumtest; |
||||
col |
||||
-------- |
||||
red |
||||
orange |
||||
yellow |
||||
green |
||||
blue |
||||
purple |
||||
(6 rows) |
||||
|
||||
-- |
||||
-- Operators, no index |
||||
-- |
||||
SELECT * FROM enumtest WHERE col = 'orange'; |
||||
col |
||||
-------- |
||||
orange |
||||
(1 row) |
||||
|
||||
SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; |
||||
col |
||||
-------- |
||||
red |
||||
yellow |
||||
green |
||||
blue |
||||
purple |
||||
(5 rows) |
||||
|
||||
SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; |
||||
col |
||||
-------- |
||||
green |
||||
blue |
||||
purple |
||||
(3 rows) |
||||
|
||||
SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; |
||||
col |
||||
-------- |
||||
yellow |
||||
green |
||||
blue |
||||
purple |
||||
(4 rows) |
||||
|
||||
SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; |
||||
col |
||||
-------- |
||||
red |
||||
orange |
||||
yellow |
||||
(3 rows) |
||||
|
||||
SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; |
||||
col |
||||
-------- |
||||
red |
||||
orange |
||||
yellow |
||||
green |
||||
(4 rows) |
||||
|
||||
-- |
||||
-- Cast to/from text |
||||
-- |
||||
SELECT 'red'::rainbow::text || 'hithere'; |
||||
?column? |
||||
------------ |
||||
redhithere |
||||
(1 row) |
||||
|
||||
SELECT 'red'::text::rainbow = 'red'::rainbow; |
||||
?column? |
||||
---------- |
||||
t |
||||
(1 row) |
||||
|
||||
-- |
||||
-- Aggregates |
||||
-- |
||||
SELECT min(col) FROM enumtest; |
||||
min |
||||
----- |
||||
red |
||||
(1 row) |
||||
|
||||
SELECT max(col) FROM enumtest; |
||||
max |
||||
-------- |
||||
purple |
||||
(1 row) |
||||
|
||||
SELECT max(col) FROM enumtest WHERE col < 'green'; |
||||
max |
||||
-------- |
||||
yellow |
||||
(1 row) |
||||
|
||||
-- |
||||
-- Index tests, force use of index |
||||
-- |
||||
SET enable_seqscan = off; |
||||
SET enable_bitmapscan = off; |
||||
-- |
||||
-- Btree index / opclass with the various operators |
||||
-- |
||||
CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col); |
||||
SELECT * FROM enumtest WHERE col = 'orange'; |
||||
col |
||||
-------- |
||||
orange |
||||
(1 row) |
||||
|
||||
SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; |
||||
col |
||||
-------- |
||||
red |
||||
yellow |
||||
green |
||||
blue |
||||
purple |
||||
(5 rows) |
||||
|
||||
SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; |
||||
col |
||||
-------- |
||||
green |
||||
blue |
||||
purple |
||||
(3 rows) |
||||
|
||||
SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; |
||||
col |
||||
-------- |
||||
yellow |
||||
green |
||||
blue |
||||
purple |
||||
(4 rows) |
||||
|
||||
SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; |
||||
col |
||||
-------- |
||||
red |
||||
orange |
||||
yellow |
||||
(3 rows) |
||||
|
||||
SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; |
||||
col |
||||
-------- |
||||
red |
||||
orange |
||||
yellow |
||||
green |
||||
(4 rows) |
||||
|
||||
SELECT min(col) FROM enumtest; |
||||
min |
||||
----- |
||||
red |
||||
(1 row) |
||||
|
||||
SELECT max(col) FROM enumtest; |
||||
max |
||||
-------- |
||||
purple |
||||
(1 row) |
||||
|
||||
SELECT max(col) FROM enumtest WHERE col < 'green'; |
||||
max |
||||
-------- |
||||
yellow |
||||
(1 row) |
||||
|
||||
DROP INDEX enumtest_btree; |
||||
-- |
||||
-- Hash index / opclass with the = operator |
||||
-- |
||||
CREATE INDEX enumtest_hash ON enumtest USING hash (col); |
||||
SELECT * FROM enumtest WHERE col = 'orange'; |
||||
col |
||||
-------- |
||||
orange |
||||
(1 row) |
||||
|
||||
DROP INDEX enumtest_hash; |
||||
-- |
||||
-- End index tests |
||||
-- |
||||
RESET enable_seqscan; |
||||
RESET enable_bitmapscan; |
||||
-- |
||||
-- Domains over enums |
||||
-- |
||||
CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue')); |
||||
SELECT 'red'::rgb; |
||||
rgb |
||||
----- |
||||
red |
||||
(1 row) |
||||
|
||||
SELECT 'purple'::rgb; |
||||
ERROR: value for domain rgb violates check constraint "rgb_check" |
||||
SELECT 'purple'::rainbow::rgb; |
||||
ERROR: value for domain rgb violates check constraint "rgb_check" |
||||
DROP DOMAIN rgb; |
||||
-- |
||||
-- Arrays |
||||
-- |
||||
SELECT '{red,green,blue}'::rainbow[]; |
||||
rainbow |
||||
------------------ |
||||
{red,green,blue} |
||||
(1 row) |
||||
|
||||
SELECT ('{red,green,blue}'::rainbow[])[2]; |
||||
rainbow |
||||
--------- |
||||
green |
||||
(1 row) |
||||
|
||||
SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]); |
||||
?column? |
||||
---------- |
||||
t |
||||
(1 row) |
||||
|
||||
SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]); |
||||
?column? |
||||
---------- |
||||
f |
||||
(1 row) |
||||
|
||||
SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]); |
||||
?column? |
||||
---------- |
||||
f |
||||
(1 row) |
||||
|
||||
SELECT 'red' = ALL ('{red,red}'::rainbow[]); |
||||
?column? |
||||
---------- |
||||
t |
||||
(1 row) |
||||
|
||||
-- |
||||
-- Support functions |
||||
-- |
||||
SELECT enum_first(NULL::rainbow); |
||||
enum_first |
||||
------------ |
||||
red |
||||
(1 row) |
||||
|
||||
SELECT enum_last('green'::rainbow); |
||||
enum_last |
||||
----------- |
||||
purple |
||||
(1 row) |
||||
|
||||
SELECT enum_range(NULL::rainbow); |
||||
enum_range |
||||
--------------------------------------- |
||||
{red,orange,yellow,green,blue,purple} |
||||
(1 row) |
||||
|
||||
SELECT enum_range('orange'::rainbow, 'green'::rainbow); |
||||
enum_range |
||||
----------------------- |
||||
{orange,yellow,green} |
||||
(1 row) |
||||
|
||||
SELECT enum_range(NULL, 'green'::rainbow); |
||||
enum_range |
||||
--------------------------- |
||||
{red,orange,yellow,green} |
||||
(1 row) |
||||
|
||||
SELECT enum_range('orange'::rainbow, NULL); |
||||
enum_range |
||||
----------------------------------- |
||||
{orange,yellow,green,blue,purple} |
||||
(1 row) |
||||
|
||||
SELECT enum_range(NULL::rainbow, NULL); |
||||
enum_range |
||||
--------------------------------------- |
||||
{red,orange,yellow,green,blue,purple} |
||||
(1 row) |
||||
|
||||
-- |
||||
-- User functions, can't test perl/python etc here since may not be compiled. |
||||
-- |
||||
CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$ |
||||
BEGIN |
||||
RETURN $1::text || 'omg'; |
||||
END |
||||
$$ LANGUAGE plpgsql; |
||||
SELECT echo_me('red'::rainbow); |
||||
echo_me |
||||
--------- |
||||
redomg |
||||
(1 row) |
||||
|
||||
-- |
||||
-- Concrete function should override generic one |
||||
-- |
||||
CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$ |
||||
BEGIN |
||||
RETURN $1::text || 'wtf'; |
||||
END |
||||
$$ LANGUAGE plpgsql; |
||||
SELECT echo_me('red'::rainbow); |
||||
echo_me |
||||
--------- |
||||
redwtf |
||||
(1 row) |
||||
|
||||
-- |
||||
-- If we drop the original generic one, we don't have to qualify the type |
||||
-- anymore, since there's only one match |
||||
-- |
||||
DROP FUNCTION echo_me(anyenum); |
||||
SELECT echo_me('red'); |
||||
echo_me |
||||
--------- |
||||
redwtf |
||||
(1 row) |
||||
|
||||
DROP FUNCTION echo_me(rainbow); |
||||
-- |
||||
-- RI triggers on enum types |
||||
-- |
||||
CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY); |
||||
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "enumtest_parent_pkey" for table "enumtest_parent" |
||||
CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent); |
||||
INSERT INTO enumtest_parent VALUES ('red'); |
||||
INSERT INTO enumtest_child VALUES ('red'); |
||||
INSERT INTO enumtest_child VALUES ('blue'); -- fail |
||||
ERROR: insert or update on table "enumtest_child" violates foreign key constraint "enumtest_child_parent_fkey" |
||||
DETAIL: Key (parent)=(blue) is not present in table "enumtest_parent". |
||||
DELETE FROM enumtest_parent; -- fail |
||||
ERROR: update or delete on table "enumtest_parent" violates foreign key constraint "enumtest_child_parent_fkey" on table "enumtest_child" |
||||
DETAIL: Key (id)=(red) is still referenced from table "enumtest_child". |
||||
-- |
||||
-- cross-type RI should fail |
||||
-- |
||||
CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly'); |
||||
CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent); |
||||
ERROR: foreign key constraint "enumtest_bogus_child_parent_fkey" cannot be implemented |
||||
DETAIL: Key columns "parent" and "id" are of incompatible types: bogus and rainbow. |
||||
DROP TYPE bogus; |
||||
-- |
||||
-- Cleanup |
||||
-- |
||||
DROP TABLE enumtest_child; |
||||
DROP TABLE enumtest_parent; |
||||
DROP TABLE enumtest; |
||||
DROP TYPE rainbow; |
||||
-- |
||||
-- Verify properly cleaned up |
||||
-- |
||||
SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow'; |
||||
count |
||||
------- |
||||
0 |
||||
(1 row) |
||||
|
||||
SELECT * FROM pg_enum WHERE NOT EXISTS |
||||
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid); |
||||
enumtypid | enumlabel |
||||
-----------+----------- |
||||
(0 rows) |
||||
|
||||
@ -0,0 +1,171 @@ |
||||
-- |
||||
-- Enum tests |
||||
-- |
||||
|
||||
CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); |
||||
|
||||
-- |
||||
-- Did it create the right number of rows? |
||||
-- |
||||
SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype; |
||||
|
||||
-- |
||||
-- I/O functions |
||||
-- |
||||
SELECT 'red'::rainbow; |
||||
SELECT 'mauve'::rainbow; |
||||
|
||||
-- |
||||
-- Basic table creation, row selection |
||||
-- |
||||
CREATE TABLE enumtest (col rainbow); |
||||
INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green'); |
||||
COPY enumtest FROM stdin; |
||||
blue |
||||
purple |
||||
\. |
||||
SELECT * FROM enumtest; |
||||
|
||||
-- |
||||
-- Operators, no index |
||||
-- |
||||
SELECT * FROM enumtest WHERE col = 'orange'; |
||||
SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; |
||||
SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; |
||||
SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; |
||||
SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; |
||||
SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; |
||||
|
||||
-- |
||||
-- Cast to/from text |
||||
-- |
||||
SELECT 'red'::rainbow::text || 'hithere'; |
||||
SELECT 'red'::text::rainbow = 'red'::rainbow; |
||||
|
||||
-- |
||||
-- Aggregates |
||||
-- |
||||
SELECT min(col) FROM enumtest; |
||||
SELECT max(col) FROM enumtest; |
||||
SELECT max(col) FROM enumtest WHERE col < 'green'; |
||||
|
||||
-- |
||||
-- Index tests, force use of index |
||||
-- |
||||
SET enable_seqscan = off; |
||||
SET enable_bitmapscan = off; |
||||
|
||||
-- |
||||
-- Btree index / opclass with the various operators |
||||
-- |
||||
CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col); |
||||
SELECT * FROM enumtest WHERE col = 'orange'; |
||||
SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; |
||||
SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; |
||||
SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; |
||||
SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; |
||||
SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; |
||||
SELECT min(col) FROM enumtest; |
||||
SELECT max(col) FROM enumtest; |
||||
SELECT max(col) FROM enumtest WHERE col < 'green'; |
||||
DROP INDEX enumtest_btree; |
||||
|
||||
-- |
||||
-- Hash index / opclass with the = operator |
||||
-- |
||||
CREATE INDEX enumtest_hash ON enumtest USING hash (col); |
||||
SELECT * FROM enumtest WHERE col = 'orange'; |
||||
DROP INDEX enumtest_hash; |
||||
|
||||
-- |
||||
-- End index tests |
||||
-- |
||||
RESET enable_seqscan; |
||||
RESET enable_bitmapscan; |
||||
|
||||
-- |
||||
-- Domains over enums |
||||
-- |
||||
CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue')); |
||||
SELECT 'red'::rgb; |
||||
SELECT 'purple'::rgb; |
||||
SELECT 'purple'::rainbow::rgb; |
||||
DROP DOMAIN rgb; |
||||
|
||||
-- |
||||
-- Arrays |
||||
-- |
||||
SELECT '{red,green,blue}'::rainbow[]; |
||||
SELECT ('{red,green,blue}'::rainbow[])[2]; |
||||
SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]); |
||||
SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]); |
||||
SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]); |
||||
SELECT 'red' = ALL ('{red,red}'::rainbow[]); |
||||
|
||||
-- |
||||
-- Support functions |
||||
-- |
||||
SELECT enum_first(NULL::rainbow); |
||||
SELECT enum_last('green'::rainbow); |
||||
SELECT enum_range(NULL::rainbow); |
||||
SELECT enum_range('orange'::rainbow, 'green'::rainbow); |
||||
SELECT enum_range(NULL, 'green'::rainbow); |
||||
SELECT enum_range('orange'::rainbow, NULL); |
||||
SELECT enum_range(NULL::rainbow, NULL); |
||||
|
||||
-- |
||||
-- User functions, can't test perl/python etc here since may not be compiled. |
||||
-- |
||||
CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$ |
||||
BEGIN |
||||
RETURN $1::text || 'omg'; |
||||
END |
||||
$$ LANGUAGE plpgsql; |
||||
SELECT echo_me('red'::rainbow); |
||||
-- |
||||
-- Concrete function should override generic one |
||||
-- |
||||
CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$ |
||||
BEGIN |
||||
RETURN $1::text || 'wtf'; |
||||
END |
||||
$$ LANGUAGE plpgsql; |
||||
SELECT echo_me('red'::rainbow); |
||||
-- |
||||
-- If we drop the original generic one, we don't have to qualify the type |
||||
-- anymore, since there's only one match |
||||
-- |
||||
DROP FUNCTION echo_me(anyenum); |
||||
SELECT echo_me('red'); |
||||
DROP FUNCTION echo_me(rainbow); |
||||
|
||||
-- |
||||
-- RI triggers on enum types |
||||
-- |
||||
CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY); |
||||
CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent); |
||||
INSERT INTO enumtest_parent VALUES ('red'); |
||||
INSERT INTO enumtest_child VALUES ('red'); |
||||
INSERT INTO enumtest_child VALUES ('blue'); -- fail |
||||
DELETE FROM enumtest_parent; -- fail |
||||
-- |
||||
-- cross-type RI should fail |
||||
-- |
||||
CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly'); |
||||
CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent); |
||||
DROP TYPE bogus; |
||||
|
||||
-- |
||||
-- Cleanup |
||||
-- |
||||
DROP TABLE enumtest_child; |
||||
DROP TABLE enumtest_parent; |
||||
DROP TABLE enumtest; |
||||
DROP TYPE rainbow; |
||||
|
||||
-- |
||||
-- Verify properly cleaned up |
||||
-- |
||||
SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow'; |
||||
SELECT * FROM pg_enum WHERE NOT EXISTS |
||||
(SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid); |
||||
Loading…
Reference in new issue