diff -r -N -c --exclude=CVS pgsql-head/doc/src/sgml/catalogs.sgml pgsql-enums/doc/src/sgml/catalogs.sgml
*** pgsql-head/doc/src/sgml/catalogs.sgml 2006-12-18 23:48:10.000000000 +0000
--- pgsql-enums/doc/src/sgml/catalogs.sgml 2006-12-19 01:24:59.000000000 +0000
***************
*** 129,134 ****
--- 129,139 ----
+ pg_enum
+ enum label and value definitions
+
+
+ pg_indexadditional index information
***************
*** 2345,2350 ****
--- 2350,2401 ----
+
+ pg_enum
+
+
+ pg_index
+
+
+
+ The pg_enum catalog contains entries
+ matching enum types to their associated values and labels. The
+ value used internally for a given enum value is actually the OID
+ of its associated row in pg_enum.
+
+
+
+ pg_enum> Columns
+
+
+
+
+ Name
+ Type
+ References
+ Description
+
+
+
+
+
+ enumtypid
+ oid
+ pg_type.oid
+ The OID of the pg_type> entry for this enum value
+
+
+
+ enumlabel
+ name
+
+ The label for this enum value.
+
+
+
+
+
+
pg_index
***************
*** 4196,4203 ****
typtype is b for
a base type, c for a composite type (e.g., a
! table's row type), d for a domain, or
! p for a pseudo-type. See also
typrelid and
typbasetype
--- 4247,4255 ----
typtype is b for
a base type, c for a composite type (e.g., a
! table's row type), e for an enum type,
! d for a domain, or p for
! a pseudo-type. See also
typrelid and
typbasetype
diff -r -N -c --exclude=CVS pgsql-head/doc/src/sgml/datatype.sgml pgsql-enums/doc/src/sgml/datatype.sgml
*** pgsql-head/doc/src/sgml/datatype.sgml 2006-11-23 04:27:33.000000000 +0000
--- pgsql-enums/doc/src/sgml/datatype.sgml 2006-12-19 01:25:14.000000000 +0000
***************
*** 2368,2373 ****
--- 2368,2526 ----
+
+ Enumerated Types
+
+
+ Enumerated (enum) types are user-creatable, distinct types that
+ are comprised of a static, predefined set of values with a
+ specific order. They are an equivalent to the enum
+ types in a number or programming languages. An example of an enum
+ type might be the days of the week, or a set of status values for
+ a piece of data.
+
+
+
+ Declaration of Enumerated Types
+
+
+ Enum types must be declared using a CREATE TYPE
+ statement before use:
+
+
+ CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
+
+
+ Once created, the enum type can be used in table and function
+ declarations as any other type can:
+
+
+
+ Basing Enum Usage
+
+ CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
+ CREATE TABLE person (
+ name text,
+ current_mood mood
+ );
+ INSERT INTO person VALUES ('Moe', 'happy');
+ SELECT * FROM person WHERE current_mood = 'happy';
+ name | current_mood
+ ------+--------------
+ Moe | happy
+ (1 row)
+
+ SELECT name FROM person WHERE current_mood = 'sad';
+ name
+ ------
+ (0 rows)
+
+
+
+
+
+
+ Ordering
+
+
+ The ordering of the values in an enum type is defined by the
+ order that was used when the type was declared, in ascending
+ order. The full range of comparison operators and related
+ aggregate functions is allowed:
+
+
+
+ Enum Ordering
+
+ INSERT INTO person VALUES ('Larry', 'sad');
+ INSERT INTO person VALUES ('Curly', 'ok');
+ SELECT * FROM person WHERE current_mood > 'sad';
+ name | current_mood
+ -------+--------------
+ Moe | happy
+ Curly | ok
+ (2 rows)
+
+ SELECT * FROM person WHERE current_mood > 'sad' ORDER BY current_mood;
+ name | current_mood
+ -------+--------------
+ Curly | ok
+ Moe | happy
+ (2 rows)
+
+ SELECT name FROM person where current_mood = (SELECT MIN(current_mood) FROM person);
+ name
+ -------
+ Larry
+ (1 row)
+
+
+
+
+
+
+ Type Safety
+
+
+ Enumerated types are completely separate data types and may not
+ be compared with each other.
+
+
+
+ Lack of Casting
+
+ CREATE TYPE happiness AS ENUM ('happy', 'very happy', 'ecstatic');
+ CREATE TABLE holidays (
+ num_weeks int,
+ happiness happiness
+ );
+ INSERT INTO holidays(num_weeks,happiness) VALUES (4, 'happy');
+ INSERT INTO holidays(num_weeks,happiness) VALUES (6, 'very happy');
+ INSERT INTO holidays(num_weeks,happiness) VALUES (8, 'ecstatic');
+ INSERT INTO holidays(num_weeks,happiness) VALUES (2, 'depressed');
+ ERROR: invalid input value for enum: "depressed"
+ SELECT person.name, holidays.num_weeks FROM person, holidays WHERE person.current_mood = holidays.happiness;
+ ERROR: operator does not exist: mood = happiness
+ LINE 1: ...s from person, holidays where person.current_mood = holidays...
+ ^
+ HINT: No operator matches the given name and argument type(s). You may need to add explicit type casts.
+
+
+
+
+ If you really need to do something like that, you can either
+ write a custom operator or add explicit casts to your query:
+
+
+
+ Comparing Different Enums by Casting to Text
+
+ SELECT person.name, holidays.num_weeks FROM person, holidays WHERE person.current_mood::text = holidays.happiness::text;
+ name | num_weeks
+ ------+-----------
+ Moe | 4
+ (1 row)
+
+
+
+
+
+
+ Implementation Specifics
+
+
+ Although individual enum labels may be up to 63 bytes long, each value
+ is stored in four bytes on disk.
+
+
+
+ Textual enum labels are case sensitive, so
+ 'happy' is not the same as 'HAPPY'.
+
+
+
+
+
Geometric Types
***************
*** 3223,3228 ****
--- 3376,3385 ----
+ anyenum
+
+
+ void
***************
*** 3288,3293 ****
--- 3445,3457 ----
+ anyenum>
+ Indicates that a function accepts any enum data type
+ (see and
+ ).
+
+
+ cstring>Indicates that a function accepts or returns a null-terminated C string.
***************
*** 3339,3346 ****
languages all forbid use of a pseudo-type as argument type, and allow
only void> and record> as a result type (plus
trigger> when the function is used as a trigger). Some also
! support polymorphic functions using the types anyarray> and
! anyelement>.
--- 3503,3510 ----
languages all forbid use of a pseudo-type as argument type, and allow
only void> and record> as a result type (plus
trigger> when the function is used as a trigger). Some also
! support polymorphic functions using the types anyarray>,
! anyelement> and anyenum>.
diff -r -N -c --exclude=CVS pgsql-head/doc/src/sgml/extend.sgml pgsql-enums/doc/src/sgml/extend.sgml
*** pgsql-head/doc/src/sgml/extend.sgml 2006-09-16 01:30:13.000000000 +0100
--- pgsql-enums/doc/src/sgml/extend.sgml 2006-12-19 01:23:51.000000000 +0000
***************
*** 236,241 ****
--- 236,247 ----
argument to be an array type, and allows the parser to infer the correct
result type from the actual first argument's type.
+
+
+ A third, similar pseudo-type is anyenum>. anyenum>
+ is treated exactly the same as anyelement>, except that the
+ actual type found must be an enum type.
+
diff -r -N -c --exclude=CVS pgsql-head/doc/src/sgml/func.sgml pgsql-enums/doc/src/sgml/func.sgml
*** pgsql-head/doc/src/sgml/func.sgml 2006-11-25 00:38:53.000000000 +0000
--- pgsql-enums/doc/src/sgml/func.sgml 2006-12-19 01:23:51.000000000 +0000
***************
*** 6334,6339 ****
--- 6334,6410 ----
+
+ Enum Support Functions
+
+
+ For enum types described in , there are several
+ support functions to allow nicer programming without hard-coding values.
+ These are listed in . The results
+ listed for the examples are for an enum type created as:
+
+
+ CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple');
+
+
+
+
+
+ Geometric Operators
+
+
+
+ Function
+ Description
+ Example
+ Example Result
+
+
+
+
+ enum_first(regtype)
+ Returns the first value for the given enum
+ SELECT enum_first('rainbow');
+ red
+
+
+ enum_last(regtype)
+ Returns the lsst value for the given enum
+ SELECT enum_last('rainbow');
+ purple
+
+
+ enum_range(regtype)
+ Returns all values for the given enum in an ordered array
+ SELECT enum_range('rainbow');
+ {red,orange,yellow,green,blue,purple}
+
+
+ enum_range(anyenum, anyenum)
+
+ Returns the range between the two given enum values, as an ordered
+ array. The values must be from the same enum type. If the first
+ parameter is null, the array will start from the start of the enum.
+ If the second parameter is null, the array will finish at the end
+ of the enum. At least one parameter must be specified.
+
+ SELECT enum_range('orange'::rainbow, 'green'::rainbow);
+ {orange,yellow,green}
+
+
+ SELECT enum_range(NULL, 'green'::rainbow);
+ {red,orange,yellow,green}
+
+
+ SELECT enum_range('orange'::rainbow, NULL);
+ {orange,yellow,green,blue,purple}
+
+
+
+
+
+
+
Geometric Functions and Operators
diff -r -N -c --exclude=CVS pgsql-head/doc/src/sgml/ref/create_type.sgml pgsql-enums/doc/src/sgml/ref/create_type.sgml
*** pgsql-head/doc/src/sgml/ref/create_type.sgml 2006-09-16 01:30:17.000000000 +0100
--- pgsql-enums/doc/src/sgml/ref/create_type.sgml 2006-12-19 01:25:30.000000000 +0000
***************
*** 23,28 ****
--- 23,31 ----
CREATE TYPE name AS
( attribute_namedata_type [, ... ] )
+ CREATE TYPE name AS ENUM
+ ( 'label' [, ... ] )
+
CREATE TYPE name (
INPUT = input_function,
OUTPUT = output_function
***************
*** 76,85 ****
Base Types
! The second form of CREATE TYPE creates a new base type
(scalar type). The parameters may appear in any order, not only that
illustrated above, and most are optional. You must register
two or more functions (using CREATE FUNCTION) before
--- 79,98 ----
+ Enumerated Types
+
+
+ The second form of CREATE TYPE creates an enumerated
+ (enum) type, as described in .
+ Enum types take a list of one or more quoted labels, which may be up to 63 bytes long.
+
+
+
+ Base Types
! The third form of CREATE TYPE creates a new base type
(scalar type). The parameters may appear in any order, not only that
illustrated above, and most are optional. You must register
two or more functions (using CREATE FUNCTION) before
***************
*** 516,521 ****
--- 529,548 ----
+ This example creates an enumerated type and uses it in
+ a table definition:
+
+ CREATE TYPE bug_status AS ENUM ('new', 'open', 'closed');
+
+ CREATE TABLE bug (
+ serial id,
+ description text,
+ status bug_status
+ );
+
+
+
+
This example creates the base data type box and then uses the
type in a table definition:
diff -r -N -c --exclude=CVS pgsql-head/src/backend/access/hash/hashfunc.c pgsql-enums/src/backend/access/hash/hashfunc.c
*** pgsql-head/src/backend/access/hash/hashfunc.c 2006-10-04 01:29:48.000000000 +0100
--- pgsql-enums/src/backend/access/hash/hashfunc.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 270,272 ****
--- 270,279 ----
/* report the result */
return UInt32GetDatum(c);
}
+
+ Datum
+ hashenum(PG_FUNCTION_ARGS)
+ {
+ PG_RETURN_UINT32(~((uint32) PG_GETARG_OID(0)));
+ }
+
diff -r -N -c --exclude=CVS pgsql-head/src/backend/catalog/Makefile pgsql-enums/src/backend/catalog/Makefile
*** pgsql-head/src/backend/catalog/Makefile 2006-07-31 02:16:36.000000000 +0100
--- pgsql-enums/src/backend/catalog/Makefile 2006-12-19 01:23:51.000000000 +0000
***************
*** 13,19 ****
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \
pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_shdepend.o \
! pg_type.o toasting.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
--- 13,19 ----
OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \
pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_shdepend.o \
! pg_type.o toasting.o pg_enum.o
BKIFILES = postgres.bki postgres.description postgres.shdescription
***************
*** 35,40 ****
--- 35,41 ----
pg_namespace.h pg_conversion.h pg_depend.h \
pg_database.h pg_tablespace.h pg_pltemplate.h \
pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
+ pg_enum.h \
toasting.h indexing.h \
)
diff -r -N -c --exclude=CVS pgsql-head/src/backend/catalog/pg_aggregate.c pgsql-enums/src/backend/catalog/pg_aggregate.c
*** pgsql-head/src/backend/catalog/pg_aggregate.c 2006-10-04 01:29:50.000000000 +0100
--- pgsql-enums/src/backend/catalog/pg_aggregate.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 81,87 ****
for (i = 0; i < numArgs; i++)
{
if (aggArgTypes[i] == ANYARRAYOID ||
! aggArgTypes[i] == ANYELEMENTOID)
{
hasPolyArg = true;
break;
--- 81,88 ----
for (i = 0; i < numArgs; i++)
{
if (aggArgTypes[i] == ANYARRAYOID ||
! aggArgTypes[i] == ANYELEMENTOID ||
! aggArgTypes[i] == ANYENUMOID)
{
hasPolyArg = true;
break;
***************
*** 93,99 ****
* we will have no way to deduce the actual transtype.
*/
if (!hasPolyArg &&
! (aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine transition data type"),
--- 94,101 ----
* we will have no way to deduce the actual transtype.
*/
if (!hasPolyArg &&
! (aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID ||
! aggTransType == ANYENUMOID))
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine transition data type"),
***************
*** 171,177 ****
* polymorphic input.)
*/
if (!hasPolyArg &&
! (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot determine result data type"),
--- 173,180 ----
* polymorphic input.)
*/
if (!hasPolyArg &&
! (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID ||
! finaltype == ANYENUMOID))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("cannot determine result data type"),
***************
*** 328,334 ****
for (i = 0; i < nargs; i++)
{
if (input_types[i] != ANYARRAYOID &&
! input_types[i] != ANYELEMENTOID)
{
allPolyArgs = false;
break;
--- 331,338 ----
for (i = 0; i < nargs; i++)
{
if (input_types[i] != ANYARRAYOID &&
! input_types[i] != ANYELEMENTOID &&
! input_types[i] != ANYENUMOID)
{
allPolyArgs = false;
break;
***************
*** 351,356 ****
--- 355,361 ----
{
if (true_oid_array[i] != ANYARRAYOID &&
true_oid_array[i] != ANYELEMENTOID &&
+ true_oid_array[i] != ANYENUMOID &&
!IsBinaryCoercible(input_types[i], true_oid_array[i]))
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
diff -r -N -c --exclude=CVS pgsql-head/src/backend/catalog/pg_enum.c pgsql-enums/src/backend/catalog/pg_enum.c
*** pgsql-head/src/backend/catalog/pg_enum.c 1970-01-01 01:00:00.000000000 +0100
--- pgsql-enums/src/backend/catalog/pg_enum.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 0 ****
--- 1,81 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_enum.c
+ * routines to support manipulation of the pg_enum relation
+ *
+ * Copyright (c) 2006, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+ #include "postgres.h"
+
+ #include "access/heapam.h"
+ #include "catalog/catalog.h"
+ #include "catalog/indexing.h"
+ #include "catalog/pg_enum.h"
+ #include "nodes/pg_list.h"
+ #include "utils/builtins.h"
+
+ int oid_cmp(const void *left, const void *right);
+
+ /* -------------------------------------------------------------------------
+ * EnumValuesCreate
+ *
+ * This creates a value in pg_enum for each of the supplied value
+ * strings. Allocates an Oid for each enum value, and ensures that
+ * they are in increasing order.
+ *
+ * -------------------------------------------------------------------------
+ */
+ void
+ EnumValuesCreate(Oid enumTypeOid, List *vals)
+ {
+ Relation pg_enum;
+ TupleDesc tupDesc;
+ NameData val;
+ Oid *oids;
+ int i, n;
+ Datum values[Natts_pg_enum];
+ char nulls[] = { ' ', ' ' };
+ ListCell *lc;
+ HeapTuple tup;
+
+ pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+ tupDesc = pg_enum->rd_att;
+
+ n = list_length(vals);
+
+ /* allocate oids */
+ oids = (Oid *) palloc(sizeof(Oid) * n);
+ for(i = 0; i < n; i++)
+ {
+ oids[i] = GetNewOid(pg_enum);
+ }
+ /* wraparound is unlikely, but just to be safe...*/
+ qsort(oids, n, sizeof(Oid), oid_cmp);
+
+ lc = list_head(vals);
+ for(i = 0, lc = list_head(vals); i < n; i++, lc = lnext(lc))
+ {
+ values[0] = ObjectIdGetDatum(enumTypeOid);
+ namestrcpy(&val, lfirst(lc));
+ values[1] = NameGetDatum(&val);
+ tup = heap_formtuple(tupDesc, values, nulls);
+ HeapTupleSetOid(tup, oids[i]);
+ simple_heap_insert(pg_enum, tup);
+ CatalogUpdateIndexes(pg_enum, tup);
+ heap_freetuple(tup);
+ }
+ pfree(oids);
+ heap_close(pg_enum, RowExclusiveLock);
+ }
+
+ int
+ oid_cmp(const void *left, const void *right)
+ {
+ return (int) (*((Oid *) left)) - (*((Oid *) right));
+ }
diff -r -N -c --exclude=CVS pgsql-head/src/backend/catalog/pg_proc.c pgsql-enums/src/backend/catalog/pg_proc.c
*** pgsql-head/src/backend/catalog/pg_proc.c 2006-10-19 19:32:46.000000000 +0100
--- pgsql-enums/src/backend/catalog/pg_proc.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 135,143 ****
}
/*
! * Do not allow return type ANYARRAY or ANYELEMENT unless at least one
! * input argument is ANYARRAY or ANYELEMENT. Also, do not allow return
! * type INTERNAL unless at least one input argument is INTERNAL.
*/
for (i = 0; i < parameterCount; i++)
{
--- 135,144 ----
}
/*
! * Do not allow return type ANYARRAY, ANYELEMENT or ANYENUM unless at
! * least one input argument is ANYARRAY, ANYELEMENT or ANYENUM. Also,
! * do not allow return type INTERNAL unless at least one input argument
! * is INTERNAL.
*/
for (i = 0; i < parameterCount; i++)
{
***************
*** 145,150 ****
--- 146,152 ----
{
case ANYARRAYOID:
case ANYELEMENTOID:
+ case ANYENUMOID:
genericInParam = true;
break;
case INTERNALOID:
***************
*** 167,172 ****
--- 169,175 ----
{
case ANYARRAYOID:
case ANYELEMENTOID:
+ case ANYENUMOID:
genericOutParam = true;
break;
case INTERNALOID:
***************
*** 177,183 ****
}
if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID ||
! genericOutParam) && !genericInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
--- 180,187 ----
}
if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID ||
! returnType == ANYENUMOID || genericOutParam)
! && !genericInParam)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("cannot determine result data type"),
***************
*** 534,554 ****
proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID &&
proc->prorettype != ANYARRAYOID &&
! proc->prorettype != ANYELEMENTOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("SQL functions cannot return type %s",
format_type_be(proc->prorettype))));
/* Disallow pseudotypes in arguments */
! /* except for ANYARRAY or ANYELEMENT */
haspolyarg = false;
for (i = 0; i < proc->pronargs; i++)
{
if (get_typtype(proc->proargtypes.values[i]) == 'p')
{
if (proc->proargtypes.values[i] == ANYARRAYOID ||
! proc->proargtypes.values[i] == ANYELEMENTOID)
haspolyarg = true;
else
ereport(ERROR,
--- 538,560 ----
proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID &&
proc->prorettype != ANYARRAYOID &&
! proc->prorettype != ANYELEMENTOID &&
! proc->prorettype != ANYENUMOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("SQL functions cannot return type %s",
format_type_be(proc->prorettype))));
/* Disallow pseudotypes in arguments */
! /* except for ANYARRAY, ANYELEMENT or ANYENUM */
haspolyarg = false;
for (i = 0; i < proc->pronargs; i++)
{
if (get_typtype(proc->proargtypes.values[i]) == 'p')
{
if (proc->proargtypes.values[i] == ANYARRAYOID ||
! proc->proargtypes.values[i] == ANYELEMENTOID ||
! proc->proargtypes.values[i] == ANYENUMOID)
haspolyarg = true;
else
ereport(ERROR,
diff -r -N -c --exclude=CVS pgsql-head/src/backend/commands/aggregatecmds.c pgsql-enums/src/backend/commands/aggregatecmds.c
*** pgsql-head/src/backend/commands/aggregatecmds.c 2006-10-04 01:29:50.000000000 +0100
--- pgsql-enums/src/backend/commands/aggregatecmds.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 178,184 ****
transTypeId = typenameTypeId(NULL, transType);
if (get_typtype(transTypeId) == 'p' &&
transTypeId != ANYARRAYOID &&
! transTypeId != ANYELEMENTOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("aggregate transition data type cannot be %s",
--- 178,185 ----
transTypeId = typenameTypeId(NULL, transType);
if (get_typtype(transTypeId) == 'p' &&
transTypeId != ANYARRAYOID &&
! transTypeId != ANYELEMENTOID &&
! transTypeId != ANYENUMOID)
ereport(ERROR,
(errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
errmsg("aggregate transition data type cannot be %s",
diff -r -N -c --exclude=CVS pgsql-head/src/backend/commands/typecmds.c pgsql-enums/src/backend/commands/typecmds.c
*** pgsql-head/src/backend/commands/typecmds.c 2006-10-04 01:29:51.000000000 +0100
--- pgsql-enums/src/backend/commands/typecmds.c 2006-12-19 01:41:22.000000000 +0000
***************
*** 39,44 ****
--- 39,45 ----
#include "catalog/indexing.h"
#include "catalog/pg_constraint.h"
#include "catalog/pg_depend.h"
+ #include "catalog/pg_enum.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_type.h"
#include "commands/defrem.h"
***************
*** 82,87 ****
--- 83,89 ----
Oid baseTypeOid,
int typMod, Constraint *constr,
char *domainName);
+ static void RemoveEnumValues(Oid enumtypoid);
/*
***************
*** 430,435 ****
--- 432,475 ----
/*
+ * RemoveEnumValues
+ * Remove values from pg_enum when dropping a type.
+ */
+ static void
+ RemoveEnumValues(Oid enumtypoid)
+ {
+ CatCList *list;
+ int num, i;
+ Oid *valoids;
+ Relation relation;
+
+ list = SearchSysCacheList(ENUMTYPOIDNAME, 1,
+ ObjectIdGetDatum(enumtypoid), 0, 0, 0);
+ num = list->n_members;
+ valoids = (Oid *) palloc(num * sizeof(Oid));
+ for(i = 0; i < num; i++)
+ valoids[i] = HeapTupleGetOid(&(list->members[i]->tuple));
+
+ ReleaseCatCacheList(list);
+
+
+ relation = heap_open(EnumRelationId, RowExclusiveLock);
+
+ for(i = 0; i < num; i++)
+ {
+ HeapTuple tup = SearchSysCache(ENUMOID,
+ ObjectIdGetDatum(valoids[i]),
+ 0, 0, 0);
+ if (!HeapTupleIsValid(tup))
+ elog(ERROR, "cache lookup failed for enum oid %u", valoids[i]);
+ simple_heap_delete(relation, &tup->t_self);
+ ReleaseSysCache(tup);
+ }
+
+ heap_close(relation, RowExclusiveLock);
+ }
+
+ /*
* RemoveType
* Removes a datatype.
*/
***************
*** 440,445 ****
--- 480,486 ----
Oid typeoid;
HeapTuple tup;
ObjectAddress object;
+ char typtype;
/* Make a TypeName so we can use standard type lookup machinery */
typename = makeTypeNameFromNameList(names);
***************
*** 478,483 ****
--- 519,525 ----
aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE,
TypeNameToString(typename));
+ typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
ReleaseSysCache(tup);
/*
***************
*** 488,493 ****
--- 530,541 ----
object.objectSubId = 0;
performDeletion(&object, behavior);
+
+ /*
+ * If it was an enum, delete the enum vals.
+ */
+ if (typtype == 'e')
+ RemoveEnumValues(typeoid);
}
***************
*** 586,597 ****
basetypeoid = HeapTupleGetOid(typeTup);
/*
! * Base type must be a plain base type or another domain. Domains over
! * pseudotypes would create a security hole. Domains over composite types
! * might be made to work in the future, but not today.
*/
typtype = baseType->typtype;
! if (typtype != 'b' && typtype != 'd')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
--- 634,646 ----
basetypeoid = HeapTupleGetOid(typeTup);
/*
! * Base type must be a plain base type, another domain or an enum.
! * Domains over pseudotypes would create a security hole. Domains
! * over composite types might be made to work in the future, but not
! * today.
*/
typtype = baseType->typtype;
! if (typtype != 'b' && typtype != 'd' && typtype != 'e')
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("\"%s\" is not a valid base type for a domain",
***************
*** 887,892 ****
--- 936,1012 ----
performDeletion(&object, behavior);
}
+ /*
+ * DefineEnum
+ * Registers a new enum.
+ */
+ void
+ DefineEnum(CreateEnumStmt *stmt)
+ {
+ char *enumName;
+ char *enumArrayName;
+ Oid enumNamespace;
+ Oid enumTypeOid;
+
+ /* Convert list of names to a name and namespace */
+ enumNamespace = QualifiedNameGetCreationNamespace(stmt->defnames,
+ &enumName);
+ enumTypeOid =
+ TypeCreate(enumName, /* type name */
+ enumNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ 4, /* internal size */
+ 'e', /* type-type (base type) */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ENUM_IN, /* input procedure */
+ F_ENUM_OUT, /* output procedure */
+ InvalidOid, /* receive procedure */
+ InvalidOid, /* send procedure */
+ InvalidOid, /* analyze procedure - default */
+ InvalidOid, /* element type ID */
+ InvalidOid, /* base type ID */
+
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ true, /* never passed by value */
+ 'i', /* int alignment */
+ 'p', /* always plain */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false); /* Type NOT NULL */
+
+ EnumValuesCreate(enumTypeOid, stmt->vals);
+
+ /* create enum array type */
+ enumArrayName = makeArrayTypeName(enumName);
+
+ TypeCreate(enumArrayName, /* type name */
+ enumNamespace, /* namespace */
+ InvalidOid, /* relation oid (n/a here) */
+ 0, /* relation kind (ditto) */
+ -1, /* internal size */
+ 'b', /* type-type (base type) */
+ DEFAULT_TYPDELIM, /* array element delimiter */
+ F_ARRAY_IN, /* input procedure */
+ F_ARRAY_OUT, /* output procedure */
+ F_ARRAY_RECV, /* receive procedure */
+ F_ARRAY_SEND, /* send procedure */
+ InvalidOid, /* analyze procedure - default */
+ enumTypeOid, /* element type ID */
+ InvalidOid, /* base type ID */
+ NULL, /* never a default type value */
+ NULL, /* binary default isn't sent either */
+ false, /* never passed by value */
+ 'i', /* enums have i alignment, so do arrays */
+ 'x', /* ARRAY is always toastable */
+ -1, /* typMod (Domains only) */
+ 0, /* Array dimensions of typbasetype */
+ false); /* Type NOT NULL */
+
+ pfree(enumArrayName);
+ pfree(enumName);
+ }
/*
* Find suitable I/O functions for a type.
diff -r -N -c --exclude=CVS pgsql-head/src/backend/executor/functions.c pgsql-enums/src/backend/executor/functions.c
*** pgsql-head/src/backend/executor/functions.c 2006-10-12 18:02:24.000000000 +0100
--- pgsql-enums/src/backend/executor/functions.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 181,187 ****
*/
rettype = procedureStruct->prorettype;
! if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
{
rettype = get_fn_expr_rettype(finfo);
if (rettype == InvalidOid) /* this probably should not happen */
--- 181,188 ----
*/
rettype = procedureStruct->prorettype;
! if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID ||
! rettype == ANYENUMOID)
{
rettype = get_fn_expr_rettype(finfo);
if (rettype == InvalidOid) /* this probably should not happen */
***************
*** 235,241 ****
{
Oid argtype = argOidVect[argnum];
! if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)
{
argtype = get_fn_expr_argtype(finfo, argnum);
if (argtype == InvalidOid)
--- 236,243 ----
{
Oid argtype = argOidVect[argnum];
! if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID ||
! argtype == ANYENUMOID)
{
argtype = get_fn_expr_argtype(finfo, argnum);
if (argtype == InvalidOid)
***************
*** 862,868 ****
* of a function with polymorphic arguments, we instead apply it during
* function execution startup. The rettype is then the actual resolved
* output type of the function, rather than the declared type. (Therefore,
! * we should never see ANYARRAY or ANYELEMENT as rettype.)
*
* The return value is true if the function returns the entire tuple result
* of its final SELECT, and false otherwise. Note that because we allow
--- 864,870 ----
* of a function with polymorphic arguments, we instead apply it during
* function execution startup. The rettype is then the actual resolved
* output type of the function, rather than the declared type. (Therefore,
! * we should never see ANYARRAY, ANYENUM or ANYELEMENT as rettype.)
*
* The return value is true if the function returns the entire tuple result
* of its final SELECT, and false otherwise. Note that because we allow
***************
*** 940,946 ****
fn_typtype = get_typtype(rettype);
! if (fn_typtype == 'b' || fn_typtype == 'd')
{
/*
* For base-type returns, the target list should have exactly one
--- 942,948 ----
fn_typtype = get_typtype(rettype);
! if (fn_typtype == 'b' || fn_typtype == 'd' || fn_typtype == 'e')
{
/*
* For base-type returns, the target list should have exactly one
***************
*** 1068,1074 ****
/* Report that we are returning entire tuple result */
return true;
}
! else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
{
/* This should already have been caught ... */
ereport(ERROR,
--- 1070,1077 ----
/* Report that we are returning entire tuple result */
return true;
}
! else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID ||
! rettype == ANYENUMOID)
{
/* This should already have been caught ... */
ereport(ERROR,
diff -r -N -c --exclude=CVS pgsql-head/src/backend/executor/nodeAgg.c pgsql-enums/src/backend/executor/nodeAgg.c
*** pgsql-head/src/backend/executor/nodeAgg.c 2006-10-04 01:29:52.000000000 +0100
--- pgsql-enums/src/backend/executor/nodeAgg.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 1366,1372 ****
/*
* Get actual datatypes of the inputs. These could be different from
* the agg's declared input types, when the agg accepts ANY, ANYARRAY
! * or ANYELEMENT.
*/
i = 0;
foreach(lc, aggref->args)
--- 1366,1372 ----
/*
* Get actual datatypes of the inputs. These could be different from
* the agg's declared input types, when the agg accepts ANY, ANYARRAY
! * ANYELEMENT or ANYENUM.
*/
i = 0;
foreach(lc, aggref->args)
***************
*** 1423,1429 ****
/* resolve actual type of transition state, if polymorphic */
aggtranstype = aggform->aggtranstype;
! if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
{
/* have to fetch the agg's declared input types... */
Oid *declaredArgTypes;
--- 1423,1430 ----
/* resolve actual type of transition state, if polymorphic */
aggtranstype = aggform->aggtranstype;
! if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID ||
! aggtranstype == ANYENUMOID)
{
/* have to fetch the agg's declared input types... */
Oid *declaredArgTypes;
diff -r -N -c --exclude=CVS pgsql-head/src/backend/optimizer/util/clauses.c pgsql-enums/src/backend/optimizer/util/clauses.c
*** pgsql-head/src/backend/optimizer/util/clauses.c 2006-10-25 23:11:32.000000000 +0100
--- pgsql-enums/src/backend/optimizer/util/clauses.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 431,437 ****
ReleaseSysCache(aggTuple);
/* resolve actual type of transition state, if polymorphic */
! if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
{
/* have to fetch the agg's declared input types... */
Oid *declaredArgTypes;
--- 431,438 ----
ReleaseSysCache(aggTuple);
/* resolve actual type of transition state, if polymorphic */
! if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID ||
! aggtranstype == ANYENUMOID)
{
/* have to fetch the agg's declared input types... */
Oid *declaredArgTypes;
***************
*** 2733,2739 ****
for (i = 0; i < funcform->pronargs; i++)
{
if (argtypes[i] == ANYARRAYOID ||
! argtypes[i] == ANYELEMENTOID)
{
polymorphic = true;
argtypes[i] = exprType((Node *) list_nth(args, i));
--- 2734,2741 ----
for (i = 0; i < funcform->pronargs; i++)
{
if (argtypes[i] == ANYARRAYOID ||
! argtypes[i] == ANYELEMENTOID ||
! argtypes[i] == ANYENUMOID)
{
polymorphic = true;
argtypes[i] = exprType((Node *) list_nth(args, i));
***************
*** 2741,2747 ****
}
if (funcform->prorettype == ANYARRAYOID ||
! funcform->prorettype == ANYELEMENTOID)
polymorphic = true;
/* Fetch and parse the function body */
--- 2743,2750 ----
}
if (funcform->prorettype == ANYARRAYOID ||
! funcform->prorettype == ANYELEMENTOID ||
! funcform->prorettype == ANYENUMOID)
polymorphic = true;
/* Fetch and parse the function body */
diff -r -N -c --exclude=CVS pgsql-head/src/backend/parser/gram.y pgsql-enums/src/backend/parser/gram.y
*** pgsql-head/src/backend/parser/gram.y 2006-11-05 22:42:09.000000000 +0000
--- pgsql-enums/src/backend/parser/gram.y 2006-12-19 01:23:51.000000000 +0000
***************
*** 244,249 ****
--- 244,250 ----
TableFuncElementList
prep_type_clause prep_type_list
execute_param_clause using_clause returning_clause
+ enum_val_list
%type into_clause OptTempTableName
***************
*** 373,379 ****
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
DESC DISABLE_P DISTINCT DO DOMAIN_P DOUBLE_P DROP
! EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
--- 374,380 ----
DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
DESC DISABLE_P DISTINCT DO DOMAIN_P DOUBLE_P DROP
! EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM ESCAPE EXCEPT EXCLUDING
EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
FALSE_P FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
***************
*** 2823,2828 ****
--- 2824,2836 ----
n->definition = NIL;
$$ = (Node *)n;
}
+ | CREATE TYPE_P any_name AS ENUM '(' enum_val_list ')'
+ {
+ CreateEnumStmt *n = makeNode(CreateEnumStmt);
+ n->defnames = $3;
+ n->vals = $7;
+ $$ = (Node *)n;
+ }
| CREATE TYPE_P any_name AS '(' TableFuncElementList ')'
{
CompositeTypeStmt *n = makeNode(CompositeTypeStmt);
***************
*** 2907,2912 ****
--- 2915,2923 ----
}
;
+ enum_val_list: Sconst { $$ = list_make1($1); }
+ | enum_val_list ',' Sconst { $$ = lappend($1, $3); }
+ ;
/*****************************************************************************
*
***************
*** 8588,8593 ****
--- 8599,8605 ----
| ENABLE_P
| ENCODING
| ENCRYPTED
+ | ENUM
| ESCAPE
| EXCLUDING
| EXCLUSIVE
diff -r -N -c --exclude=CVS pgsql-head/src/backend/parser/keywords.c pgsql-enums/src/backend/parser/keywords.c
*** pgsql-head/src/backend/parser/keywords.c 2006-10-07 22:51:02.000000000 +0100
--- pgsql-enums/src/backend/parser/keywords.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 132,137 ****
--- 132,138 ----
{"encoding", ENCODING},
{"encrypted", ENCRYPTED},
{"end", END_P},
+ {"enum", ENUM},
{"escape", ESCAPE},
{"except", EXCEPT},
{"excluding", EXCLUDING},
diff -r -N -c --exclude=CVS pgsql-head/src/backend/parser/parse_coerce.c pgsql-enums/src/backend/parser/parse_coerce.c
*** pgsql-head/src/backend/parser/parse_coerce.c 2006-12-10 22:13:26.000000000 +0000
--- pgsql-enums/src/backend/parser/parse_coerce.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 130,135 ****
--- 130,136 ----
}
if (targetTypeId == ANYOID ||
targetTypeId == ANYELEMENTOID ||
+ targetTypeId == ANYENUMOID ||
(targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID))
{
/*
***************
*** 402,410 ****
if (targetTypeId == ANYOID)
continue;
! /* accept if target is ANYARRAY or ANYELEMENT, for now */
if (targetTypeId == ANYARRAYOID ||
! targetTypeId == ANYELEMENTOID)
{
have_generics = true; /* do more checking later */
continue;
--- 403,412 ----
if (targetTypeId == ANYOID)
continue;
! /* accept if target is ANYARRAY, ANYELEMENT or ANYENUM, for now */
if (targetTypeId == ANYARRAYOID ||
! targetTypeId == ANYELEMENTOID ||
! targetTypeId == ANYENUMOID)
{
have_generics = true; /* do more checking later */
continue;
***************
*** 447,452 ****
--- 449,462 ----
continue;
/*
+ * If input is an enum, can ANYENUM be cast to target?
+ */
+ if (is_type_enum(inputTypeId) &&
+ find_coercion_pathway(targetTypeId, ANYENUMOID,
+ ccontext, &funcId))
+ continue;
+
+ /*
* Else, cannot coerce at this argument position
*/
return false;
***************
*** 1067,1073 ****
Oid array_typeid = InvalidOid;
Oid array_typelem;
bool have_anyelement = false;
!
/*
* Loop through the arguments to see if we have any that are ANYARRAY or
* ANYELEMENT. If so, require the actual types to be self-consistent
--- 1077,1083 ----
Oid array_typeid = InvalidOid;
Oid array_typelem;
bool have_anyelement = false;
! bool have_enum = false;
/*
* Loop through the arguments to see if we have any that are ANYARRAY or
* ANYELEMENT. If so, require the actual types to be self-consistent
***************
*** 1076,1082 ****
{
Oid actual_type = actual_arg_types[j];
! if (declared_arg_types[j] == ANYELEMENTOID)
{
have_anyelement = true;
if (actual_type == UNKNOWNOID)
--- 1086,1093 ----
{
Oid actual_type = actual_arg_types[j];
! if (declared_arg_types[j] == ANYELEMENTOID ||
! declared_arg_types[j] == ANYENUMOID)
{
have_anyelement = true;
if (actual_type == UNKNOWNOID)
***************
*** 1084,1089 ****
--- 1095,1102 ----
if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
return false;
elem_typeid = actual_type;
+ if (declared_arg_types[j] == ANYENUMOID)
+ have_enum = true;
}
else if (declared_arg_types[j] == ANYARRAYOID)
{
***************
*** 1124,1129 ****
--- 1137,1148 ----
}
}
+ if (have_enum)
+ {
+ /* is the given type as enum type? */
+ return is_type_enum(elem_typeid);
+ }
+
/* Looks valid */
return true;
}
***************
*** 1177,1183 ****
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid array_typelem;
! bool have_anyelement = (rettype == ANYELEMENTOID);
/*
* Loop through the arguments to see if we have any that are ANYARRAY or
--- 1196,1203 ----
Oid elem_typeid = InvalidOid;
Oid array_typeid = InvalidOid;
Oid array_typelem;
! bool have_anyelement = (rettype == ANYELEMENTOID ||
! rettype == ANYENUMOID);
/*
* Loop through the arguments to see if we have any that are ANYARRAY or
***************
*** 1187,1193 ****
{
Oid actual_type = actual_arg_types[j];
! if (declared_arg_types[j] == ANYELEMENTOID)
{
have_generics = have_anyelement = true;
if (actual_type == UNKNOWNOID)
--- 1207,1214 ----
{
Oid actual_type = actual_arg_types[j];
! if (declared_arg_types[j] == ANYELEMENTOID ||
! declared_arg_types[j] == ANYENUMOID)
{
have_generics = have_anyelement = true;
if (actual_type == UNKNOWNOID)
***************
*** 1286,1292 ****
if (actual_type != UNKNOWNOID)
continue;
! if (declared_arg_types[j] == ANYELEMENTOID)
declared_arg_types[j] = elem_typeid;
else if (declared_arg_types[j] == ANYARRAYOID)
{
--- 1307,1314 ----
if (actual_type != UNKNOWNOID)
continue;
! if (declared_arg_types[j] == ANYELEMENTOID ||
! declared_arg_types[j] == ANYENUMOID)
declared_arg_types[j] = elem_typeid;
else if (declared_arg_types[j] == ANYARRAYOID)
{
***************
*** 1320,1326 ****
}
/* if we return ANYELEMENTOID use the appropriate argument type */
! if (rettype == ANYELEMENTOID)
return elem_typeid;
/* we don't return a generic type; send back the original return type */
--- 1342,1348 ----
}
/* if we return ANYELEMENTOID use the appropriate argument type */
! if (rettype == ANYELEMENTOID || rettype == ANYENUMOID)
return elem_typeid;
/* we don't return a generic type; send back the original return type */
***************
*** 1359,1365 ****
format_type_be(context_actual_type))));
return context_actual_type;
}
! else if (context_declared_type == ANYELEMENTOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
--- 1381,1388 ----
format_type_be(context_actual_type))));
return context_actual_type;
}
! else if (context_declared_type == ANYELEMENTOID ||
! context_declared_type == ANYENUMOID)
{
/* Use the array type corresponding to actual type */
Oid array_typeid = get_array_type(context_actual_type);
***************
*** 1372,1378 ****
return array_typeid;
}
}
! else if (declared_type == ANYELEMENTOID)
{
if (context_declared_type == ANYARRAYOID)
{
--- 1395,1401 ----
return array_typeid;
}
}
! else if (declared_type == ANYELEMENTOID || declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
***************
*** 1386,1392 ****
format_type_be(context_actual_type))));
return array_typelem;
}
! else if (context_declared_type == ANYELEMENTOID)
{
/* Use the actual type; it doesn't matter if array or not */
return context_actual_type;
--- 1409,1416 ----
format_type_be(context_actual_type))));
return array_typelem;
}
! else if (context_declared_type == ANYELEMENTOID ||
! context_declared_type == ANYENUMOID)
{
/* Use the actual type; it doesn't matter if array or not */
return context_actual_type;
***************
*** 1499,1504 ****
--- 1523,1529 ----
case (INTERNALOID):
case (OPAQUEOID):
case (ANYELEMENTOID):
+ case (ANYENUMOID):
result = GENERIC_TYPE;
break;
***************
*** 1631,1636 ****
--- 1656,1665 ----
if (srctype == targettype)
return true;
+ /* Check for enums */
+ if (targettype == ANYENUMOID)
+ return is_type_enum(srctype);
+
/* If srctype is a domain, reduce to its base type */
if (OidIsValid(srctype))
srctype = getBaseType(srctype);
***************
*** 1779,1784 ****
--- 1808,1829 ----
result = true;
}
}
+
+ /*
+ * Deal with enums. If the input type is an enum, and we haven't found
+ * an explicit pg_cast entry above, retry with ANYENUM.
+ */
+ else if (is_type_enum(sourceTypeId))
+ {
+ result = find_coercion_pathway(targetTypeId, ANYENUMOID,
+ ccontext, funcid);
+ }
+ else if (is_type_enum(targetTypeId))
+ {
+ result = find_coercion_pathway(ANYENUMOID, sourceTypeId,
+ ccontext, funcid);
+ }
+
}
return result;
diff -r -N -c --exclude=CVS pgsql-head/src/backend/parser/parse_oper.c pgsql-enums/src/backend/parser/parse_oper.c
*** pgsql-head/src/backend/parser/parse_oper.c 2006-10-04 01:29:56.000000000 +0100
--- pgsql-enums/src/backend/parser/parse_oper.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 913,919 ****
* Now switch back to the array type on the right, arranging for any
* needed cast to be applied.
*/
! res_atypeId = get_array_type(declared_arg_types[1]);
if (!OidIsValid(res_atypeId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
--- 913,928 ----
* Now switch back to the array type on the right, arranging for any
* needed cast to be applied.
*/
! if(declared_arg_types[1] == ANYENUMOID)
! {
! /*
! * anyenum[] doesn't exist, but we're guaranteed that
! * real_type[] will
! */
! res_atypeId = get_array_type(actual_arg_types[1]);
! }
! else
! res_atypeId = get_array_type(declared_arg_types[1]);
if (!OidIsValid(res_atypeId))
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
diff -r -N -c --exclude=CVS pgsql-head/src/backend/tcop/utility.c pgsql-enums/src/backend/tcop/utility.c
*** pgsql-head/src/backend/tcop/utility.c 2006-10-04 01:29:58.000000000 +0100
--- pgsql-enums/src/backend/tcop/utility.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 344,349 ****
--- 344,350 ----
case T_TruncateStmt:
case T_DropOwnedStmt:
case T_ReassignOwnedStmt:
+ case T_CreateEnumStmt:
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("transaction is read-only")));
***************
*** 1016,1021 ****
--- 1017,1029 ----
break;
/*
+ * ******************************** ENUM statements ****
+ */
+ case T_CreateEnumStmt:
+ DefineEnum((CreateEnumStmt *) parsetree);
+ break;
+
+ /*
* ******************************** ROLE statements ****
*/
case T_CreateRoleStmt:
***************
*** 1595,1600 ****
--- 1603,1612 ----
tag = "CREATE TYPE";
break;
+ case T_CreateEnumStmt:
+ tag = "CREATE TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
diff -r -N -c --exclude=CVS pgsql-head/src/backend/utils/adt/enum.c pgsql-enums/src/backend/utils/adt/enum.c
*** pgsql-head/src/backend/utils/adt/enum.c 1970-01-01 01:00:00.000000000 +0100
--- pgsql-enums/src/backend/utils/adt/enum.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 0 ****
--- 1,375 ----
+ /*-------------------------------------------------------------------------
+ *
+ * enum.c
+ * i/o functions, operators, aggregates etc for enum types
+ *
+ * Copyright (c) 2006, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ * $PostgreSQL$
+ *
+ *-------------------------------------------------------------------------
+ */
+
+ #include "postgres.h"
+ #include "fmgr.h"
+ #include "utils/array.h"
+ #include "utils/builtins.h"
+ #include "utils/lsyscache.h"
+ #include "utils/syscache.h"
+ #include "catalog/pg_enum.h"
+ #include "catalog/pg_type.h"
+
+ char* enum_cstring(Oid enumval);
+ Oid cstring_enum(char *name, Oid enumtypoid);
+
+ PG_FUNCTION_INFO_V1(enum_in);
+ PG_FUNCTION_INFO_V1(enum_out);
+ PG_FUNCTION_INFO_V1(enum_lt);
+ PG_FUNCTION_INFO_V1(enum_le);
+ PG_FUNCTION_INFO_V1(enum_eq);
+ PG_FUNCTION_INFO_V1(enum_ne);
+ PG_FUNCTION_INFO_V1(enum_ge);
+ PG_FUNCTION_INFO_V1(enum_gt);
+ PG_FUNCTION_INFO_V1(enum_cmp);
+ PG_FUNCTION_INFO_V1(enum_text);
+ PG_FUNCTION_INFO_V1(text_enum);
+ PG_FUNCTION_INFO_V1(enum_smaller);
+ PG_FUNCTION_INFO_V1(enum_larger);
+ PG_FUNCTION_INFO_V1(enum_first);
+ PG_FUNCTION_INFO_V1(enum_last);
+ PG_FUNCTION_INFO_V1(enum_range_bounds);
+ PG_FUNCTION_INFO_V1(enum_range_all);
+
+ Datum enum_in(PG_FUNCTION_ARGS);
+ Datum enum_out(PG_FUNCTION_ARGS);
+ Datum enum_lt(PG_FUNCTION_ARGS);
+ Datum enum_le(PG_FUNCTION_ARGS);
+ Datum enum_eq(PG_FUNCTION_ARGS);
+ Datum enum_ne(PG_FUNCTION_ARGS);
+ Datum enum_ge(PG_FUNCTION_ARGS);
+ Datum enum_gt(PG_FUNCTION_ARGS);
+ Datum enum_cmp(PG_FUNCTION_ARGS);
+ Datum enum_text(PG_FUNCTION_ARGS);
+ Datum text_enum(PG_FUNCTION_ARGS);
+ Datum enum_smaller(PG_FUNCTION_ARGS);
+ Datum enum_larger(PG_FUNCTION_ARGS);
+ Datum enum_first(PG_FUNCTION_ARGS);
+ Datum enum_last(PG_FUNCTION_ARGS);
+ Datum enum_range_bounds(PG_FUNCTION_ARGS);
+ Datum enum_range_all(PG_FUNCTION_ARGS);
+
+
+ Datum
+ enum_in(PG_FUNCTION_ARGS)
+ {
+ char *name;
+ Oid enumtypoid;
+
+ name = PG_GETARG_CSTRING(0);
+ enumtypoid = PG_GETARG_OID(1);
+
+ PG_RETURN_OID(cstring_enum(name, enumtypoid));
+ }
+
+ Oid
+ cstring_enum(char *name, Oid enumtypoid)
+ {
+ HeapTuple tup;
+ Oid enumoid;
+
+ tup = SearchSysCache(ENUMTYPOIDNAME, enumtypoid, PointerGetDatum(name), 0, 0);
+ if (tup == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid input value for enum: \"%s\"",
+ 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));
+ }
+
+ char* enum_cstring(Oid enumval)
+ {
+ HeapTuple tup;
+ Form_pg_enum en;
+ char *name;
+
+ tup = SearchSysCache(ENUMOID, enumval, 0, 0, 0);
+ if (tup == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("invalid internal value for enum: \"%d\"",
+ enumval)));
+
+ en = (Form_pg_enum) GETSTRUCT(tup);
+ name = (char *) pstrdup(en->enumname.data);
+ ReleaseSysCache(tup);
+ return name;
+ }
+
+ Datum
+ enum_lt(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_BOOL(left < right);
+ }
+
+ Datum
+ enum_le(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_BOOL(left <= right);
+ }
+
+ Datum
+ enum_eq(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_BOOL(left == right);
+ }
+
+ Datum
+ enum_ne(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_BOOL(left != right);
+ }
+
+ Datum
+ enum_ge(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_BOOL(left >= right);
+ }
+
+ Datum
+ enum_gt(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_BOOL(left > right);
+ }
+
+ Datum
+ enum_smaller(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_OID(left <= right ? left : right);
+ }
+
+ Datum
+ enum_larger(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_OID(left >= right ? left : right);
+ }
+
+ Datum
+ enum_cmp(PG_FUNCTION_ARGS)
+ {
+ Oid left = PG_GETARG_OID(0);
+ Oid right = PG_GETARG_OID(1);
+ PG_RETURN_INT32((int)left - (int)right);
+ }
+
+ Datum
+ enum_text(PG_FUNCTION_ARGS)
+ {
+ char *cstr;
+ int len;
+ text *result;
+ Oid enumval = PG_GETARG_OID(0);
+
+ cstr = enum_cstring(enumval);
+ len = strlen(cstr);
+ result = (text *) palloc(VARHDRSZ + len);
+ VARATT_SIZEP(result) = VARHDRSZ + len;
+ memcpy(VARATT_DATA(result), cstr, len);
+ pfree(cstr);
+ PG_RETURN_TEXT_P(result);
+ }
+
+ #define GET_STR(textp) DatumGetCString(DirectFunctionCall1(textout, PointerGetDatum(textp)))
+
+ Datum
+ text_enum(PG_FUNCTION_ARGS)
+ {
+ text *the_text = PG_GETARG_TEXT_P(0);
+ Oid enumtypoid = PG_GETARG_OID(1);
+ char *str = GET_STR(the_text);
+
+ PG_RETURN_OID(cstring_enum(str, enumtypoid));
+ }
+
+ Datum
+ enum_first(PG_FUNCTION_ARGS)
+ {
+ CatCList *list;
+ int num, i;
+ Oid enumtypoid = PG_GETARG_OID(0);
+ Oid min = InvalidOid;
+
+ 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))
+ ereport(ERROR,
+ /* TOM FIXME what code? */
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("Can't find values for enum: \"%d\"",
+ enumtypoid)));
+
+ PG_RETURN_OID(min);
+ }
+
+ Datum
+ enum_last(PG_FUNCTION_ARGS)
+ {
+ CatCList *list;
+ int num, i;
+ Oid enumtypoid = PG_GETARG_OID(0);
+ Oid max = InvalidOid;
+
+ 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))
+ ereport(ERROR,
+ /* TOM FIXME what code? */
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("Can't find values for enum: \"%d\"",
+ enumtypoid)));
+
+ PG_RETURN_OID(max);
+ }
+
+ Oid
+ get_enum_type_oid(Oid val);
+
+
+ #define HEADERGETSTRUCT(HEADER) ((char *) (HEADER) + (HEADER)->t_hoff)
+ int
+ enum_elem_cmp(const void *left, const void *right);
+
+ ArrayType*
+ enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
+
+ Datum
+ enum_range_bounds(PG_FUNCTION_ARGS)
+ {
+ Oid enumtypoid;
+
+ /* these should be null or guaranteed to be from the same enum,
+ * enforced by the generic type mechanism */
+ Oid lower = PG_GETARG_OID(0);
+ Oid upper = PG_GETARG_OID(1);
+
+ /* must specify at least one */
+ if (!OidIsValid(lower) && !OidIsValid(upper))
+ ereport(ERROR,
+ /* TOM FIXME what code? */
+ (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+ errmsg("Must specify at least one of upper and lower range")));
+
+ enumtypoid = get_enum_type_oid(OidIsValid(lower) ? lower : upper);
+ PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
+ }
+
+ Datum
+ enum_range_all(PG_FUNCTION_ARGS)
+ {
+ Oid enumtypoid = PG_GETARG_OID(0);
+ PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, InvalidOid, InvalidOid));
+ }
+
+ ArrayType*
+ enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
+ {
+ CatCList *list;
+ int total, num, i, j;
+ Datum *elems;
+
+ list = SearchSysCacheList(ENUMTYPOIDNAME, 1,
+ ObjectIdGetDatum(enumtypoid), 0, 0, 0);
+ total = list->n_members;
+
+ /* count matches */
+ num = 0;
+ if (OidIsValid(lower) || OidIsValid(upper))
+ {
+ for (i = 0; i < total; i++)
+ {
+ Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
+ if ((!OidIsValid(lower) || lower <= val) &&
+ (!OidIsValid(upper) || val <= upper))
+ num++;
+ }
+ }
+ else
+ {
+ /* fast path for entire-range version */
+ num = total;
+ }
+
+ /* allocate array, copy data */
+ elems = palloc(sizeof(Datum) * num);
+ 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 */
+ qsort(elems, num, sizeof(Datum), enum_elem_cmp);
+
+ return construct_array(elems, num, enumtypoid, sizeof(Oid), true, 'i');
+ }
+
+ int
+ enum_elem_cmp(const void *left, const void *right)
+ {
+ Oid l = DatumGetObjectId(*((Datum *) left));
+ Oid r = DatumGetObjectId(*((Datum *) right));
+ return l - r;
+ }
+
diff -r -N -c --exclude=CVS pgsql-head/src/backend/utils/adt/Makefile pgsql-enums/src/backend/utils/adt/Makefile
*** pgsql-head/src/backend/utils/adt/Makefile 2006-04-05 23:11:55.000000000 +0100
--- pgsql-enums/src/backend/utils/adt/Makefile 2006-12-19 01:23:51.000000000 +0000
***************
*** 17,23 ****
OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
! float.o format_type.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
--- 17,23 ----
OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
cash.o char.o date.o datetime.o datum.o domains.o \
! enum.o float.o format_type.o \
geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
oid.o oracle_compat.o pseudotypes.o rowtypes.o \
diff -r -N -c --exclude=CVS pgsql-head/src/backend/utils/cache/lsyscache.c pgsql-enums/src/backend/utils/cache/lsyscache.c
*** pgsql-head/src/backend/utils/cache/lsyscache.c 2006-10-04 01:30:00.000000000 +0100
--- pgsql-enums/src/backend/utils/cache/lsyscache.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 19,24 ****
--- 19,25 ----
#include "bootstrap/bootstrap.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
+ #include "catalog/pg_enum.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
***************
*** 1624,1629 ****
--- 1625,1633 ----
{
/* Not a domain, so done */
ReleaseSysCache(tup);
+ /* pass enum type to builtin enum functions as typmod */
+ if (typTup->typtype == 'e')
+ *typmod = DatumGetInt32(ObjectIdGetDatum(typid));
break;
}
***************
*** 2225,2227 ****
--- 2229,2273 ----
errmsg("role \"%s\" does not exist", rolname)));
return roleid;
}
+
+ /*
+ * get_enum_type_oid
+ * Return the type oid for the given enum value oid.
+ */
+ Oid
+ get_enum_type_oid(Oid val)
+ {
+ HeapTuple tuple;
+ Oid result;
+
+ tuple = SearchSysCache(ENUMOID, val, 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ result = InvalidOid;
+ else
+ result = ((FormData_pg_enum *) GETSTRUCT(tuple))->enumtypid;
+
+ ReleaseSysCache(tuple);
+ return result;
+ }
+
+ /*
+ * is_type_enum
+ * Returns true if the given type is an enum type.
+ */
+
+ bool
+ is_type_enum(Oid typeOid)
+ {
+ HeapTuple tuple;
+ bool result;
+
+ tuple = SearchSysCache(TYPEOID, typeOid, 0, 0, 0);
+ if (!HeapTupleIsValid(tuple))
+ result = false;
+ else
+ result = ((FormData_pg_type *) GETSTRUCT(tuple))->typtype == 'e';
+
+ ReleaseSysCache(tuple);
+ return result;
+ }
+
diff -r -N -c --exclude=CVS pgsql-head/src/backend/utils/cache/syscache.c pgsql-enums/src/backend/utils/cache/syscache.c
*** pgsql-head/src/backend/utils/cache/syscache.c 2006-10-06 19:23:35.000000000 +0100
--- pgsql-enums/src/backend/utils/cache/syscache.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 30,35 ****
--- 30,36 ----
#include "catalog/pg_cast.h"
#include "catalog/pg_conversion.h"
#include "catalog/pg_database.h"
+ #include "catalog/pg_enum.h"
#include "catalog/pg_inherits.h"
#include "catalog/pg_language.h"
#include "catalog/pg_namespace.h"
***************
*** 322,327 ****
--- 323,352 ----
},
4
},
+ {EnumRelationId, /* ENUMOID */
+ EnumOidIndexId,
+ 0,
+ 1,
+ {
+ ObjectIdAttributeNumber,
+ 0,
+ 0,
+ 0
+ },
+ 256
+ },
+ {EnumRelationId, /* ENUMTYPOIDNAME */
+ EnumTypIdNameIndexId,
+ 0,
+ 2,
+ {
+ Anum_pg_enum_enumtypid,
+ Anum_pg_enum_enumname,
+ 0,
+ 0
+ },
+ 256
+ },
{IndexRelationId, /* INDEXRELID */
IndexRelidIndexId,
Anum_pg_index_indrelid,
diff -r -N -c --exclude=CVS pgsql-head/src/backend/utils/fmgr/funcapi.c pgsql-enums/src/backend/utils/fmgr/funcapi.c
*** pgsql-head/src/backend/utils/fmgr/funcapi.c 2006-07-11 17:35:33.000000000 +0100
--- pgsql-enums/src/backend/utils/fmgr/funcapi.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 193,200 ****
* only when we couldn't resolve the actual rowtype for lack of information.
*
* The other hard case that this handles is resolution of polymorphism.
! * We will never return ANYELEMENT or ANYARRAY, either as a scalar result
! * type or as a component of a rowtype.
*
* This function is relatively expensive --- in a function returning set,
* try to call it only the first time through.
--- 193,200 ----
* only when we couldn't resolve the actual rowtype for lack of information.
*
* The other hard case that this handles is resolution of polymorphism.
! * We will never return ANYELEMENT, ANYARRAY or ANYENUM, either as a scalar
! * result type or as a component of a rowtype.
*
* This function is relatively expensive --- in a function returning set,
* try to call it only the first time through.
***************
*** 338,344 ****
/*
* If scalar polymorphic result, try to resolve it.
*/
! if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
{
Oid newrettype = exprType(call_expr);
--- 338,345 ----
/*
* If scalar polymorphic result, try to resolve it.
*/
! if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID ||
! rettype == ANYENUMOID)
{
Oid newrettype = exprType(call_expr);
***************
*** 348,353 ****
--- 349,355 ----
errmsg("could not determine actual result type for function \"%s\" declared to return type %s",
NameStr(procform->proname),
format_type_be(rettype))));
+ /* should we validate that newrettype is an enum type for anyenum? */
rettype = newrettype;
}
***************
*** 389,396 ****
/*
* Given the result tuple descriptor for a function with OUT parameters,
! * replace any polymorphic columns (ANYELEMENT/ANYARRAY) with correct data
! * types deduced from the input arguments. Returns TRUE if able to deduce
* all types, FALSE if not.
*/
static bool
--- 391,398 ----
/*
* Given the result tuple descriptor for a function with OUT parameters,
! * replace any polymorphic columns (ANYELEMENT/ANYARRAY/ANYENUM) with correct
! * data types deduced from the input arguments. Returns TRUE if able to deduce
* all types, FALSE if not.
*/
static bool
***************
*** 411,416 ****
--- 413,419 ----
switch (tupdesc->attrs[i]->atttypid)
{
case ANYELEMENTOID:
+ case ANYENUMOID:
have_anyelement_result = true;
break;
case ANYARRAYOID:
***************
*** 435,440 ****
--- 438,444 ----
switch (declared_args->values[i])
{
case ANYELEMENTOID:
+ case ANYENUMOID:
if (!OidIsValid(anyelement_type))
anyelement_type = get_call_expr_argtype(call_expr, i);
break;
***************
*** 467,472 ****
--- 471,477 ----
switch (tupdesc->attrs[i]->atttypid)
{
case ANYELEMENTOID:
+ case ANYENUMOID:
TupleDescInitEntry(tupdesc, i + 1,
NameStr(tupdesc->attrs[i]->attname),
anyelement_type,
***************
*** 490,497 ****
/*
* Given the declared argument types and modes for a function,
! * replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data
! * types deduced from the input arguments. Returns TRUE if able to deduce
* all types, FALSE if not. This is the same logic as
* resolve_polymorphic_tupdesc, but with a different argument representation.
*
--- 495,502 ----
/*
* Given the declared argument types and modes for a function,
! * replace any polymorphic types (ANYELEMENT/ANYARRAY/ANYENUM) with correct
! * data types deduced from the input arguments. Returns TRUE if able to deduce
* all types, FALSE if not. This is the same logic as
* resolve_polymorphic_tupdesc, but with a different argument representation.
*
***************
*** 517,522 ****
--- 522,528 ----
switch (argtypes[i])
{
case ANYELEMENTOID:
+ case ANYENUMOID:
if (argmode == PROARGMODE_OUT)
have_anyelement_result = true;
else
***************
*** 577,582 ****
--- 583,589 ----
switch (argtypes[i])
{
case ANYELEMENTOID:
+ case ANYENUMOID:
argtypes[i] = anyelement_type;
break;
case ANYARRAYOID:
***************
*** 607,612 ****
--- 614,620 ----
return TYPEFUNC_COMPOSITE;
case 'b':
case 'd':
+ case 'e':
return TYPEFUNC_SCALAR;
case 'p':
if (typid == RECORDOID)
***************
*** 840,847 ****
* Given a pg_proc row for a function, return a tuple descriptor for the
* result rowtype, or NULL if the function does not have OUT parameters.
*
! * Note that this does not handle resolution of ANYELEMENT/ANYARRAY types;
! * that is deliberate.
*/
TupleDesc
build_function_result_tupdesc_t(HeapTuple procTuple)
--- 848,855 ----
* Given a pg_proc row for a function, return a tuple descriptor for the
* result rowtype, or NULL if the function does not have OUT parameters.
*
! * Note that this does not handle resolution of ANYELEMENT/ANYARRAY/ANYENUM
! * types; that is deliberate.
*/
TupleDesc
build_function_result_tupdesc_t(HeapTuple procTuple)
diff -r -N -c --exclude=CVS pgsql-head/src/bin/pg_dump/pg_dump.c pgsql-enums/src/bin/pg_dump/pg_dump.c
*** pgsql-head/src/bin/pg_dump/pg_dump.c 2006-10-10 00:36:59.000000000 +0100
--- pgsql-enums/src/bin/pg_dump/pg_dump.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 137,142 ****
--- 137,143 ----
static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
static void dumpType(Archive *fout, TypeInfo *tinfo);
static void dumpBaseType(Archive *fout, TypeInfo *tinfo);
+ static void dumpEnumType(Archive *fout, TypeInfo *tinfo);
static void dumpDomain(Archive *fout, TypeInfo *tinfo);
static void dumpCompositeType(Archive *fout, TypeInfo *tinfo);
static void dumpShellType(Archive *fout, ShellTypeInfo *stinfo);
***************
*** 4988,4993 ****
--- 4989,5072 ----
dumpDomain(fout, tinfo);
else if (tinfo->typtype == 'c')
dumpCompositeType(fout, tinfo);
+ else if (tinfo->typtype == 'e')
+ dumpEnumType(fout, tinfo);
+ }
+
+ /*
+ * dumpEnumType
+ * writes out to fout the queries to recreate a user-defined enum type
+ */
+ static void
+ dumpEnumType(Archive *fout, TypeInfo *tinfo)
+ {
+ PQExpBuffer q = createPQExpBuffer();
+ PQExpBuffer delq = createPQExpBuffer();
+ PQExpBuffer query = createPQExpBuffer();
+ PGresult *res;
+ int num, i;
+ char *name;
+
+ /* Set proper schema search path so regproc references list correctly */
+ selectSourceSchema(tinfo->dobj.namespace->dobj.name);
+
+ appendPQExpBuffer(query, "SELECT enumname FROM pg_catalog.pg_enum "
+ "WHERE enumtypid = '%u'"
+ "ORDER BY oid",
+ tinfo->dobj.catId.oid);
+
+ res = PQexec(g_conn, query->data);
+ check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+ num = PQntuples(res);
+ /* should be at least 1 value */
+ if (num == 0)
+ {
+ write_msg(NULL, "No rows found for enum");
+ exit_nicely();
+ }
+
+ /*
+ * DROP must be fully qualified in case same name appears in pg_catalog.
+ * CASCADE shouldn't be required here as for normal types since the
+ * I/O functions are generic and do not get dropped.
+ */
+ appendPQExpBuffer(delq, "DROP TYPE %s.",
+ fmtId(tinfo->dobj.namespace->dobj.name));
+ appendPQExpBuffer(delq, "%s;\n",
+ fmtId(tinfo->dobj.name));
+ appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (\n",
+ fmtId(tinfo->dobj.name));
+ for (i = 0; i < num; i++)
+ {
+ name = PQgetvalue(res, i, 0);
+ appendPQExpBuffer(q, " ");
+ appendStringLiteralAH(q, name, fout);
+ appendPQExpBuffer(q, "\n");
+ }
+ appendPQExpBuffer(q, ");\n");
+
+ ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
+ tinfo->dobj.name,
+ tinfo->dobj.namespace->dobj.name,
+ NULL,
+ tinfo->rolname, false,
+ "TYPE", q->data, delq->data, NULL,
+ tinfo->dobj.dependencies, tinfo->dobj.nDeps,
+ NULL, NULL);
+
+ /* Dump Type Comments */
+ resetPQExpBuffer(q);
+
+ appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
+ dumpComment(fout, q->data,
+ tinfo->dobj.namespace->dobj.name, tinfo->rolname,
+ tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
+
+ PQclear(res);
+ destroyPQExpBuffer(q);
+ destroyPQExpBuffer(delq);
+ destroyPQExpBuffer(query);
}
/*
diff -r -N -c --exclude=CVS pgsql-head/src/include/access/hash.h pgsql-enums/src/include/access/hash.h
*** pgsql-head/src/include/access/hash.h 2006-07-13 17:49:19.000000000 +0100
--- pgsql-enums/src/include/access/hash.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 262,267 ****
--- 262,268 ----
extern Datum hashtext(PG_FUNCTION_ARGS);
extern Datum hashvarlena(PG_FUNCTION_ARGS);
extern Datum hash_any(register const unsigned char *k, register int keylen);
+ extern Datum hashenum(PG_FUNCTION_ARGS);
/* private routines */
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/indexing.h pgsql-enums/src/include/catalog/indexing.h
*** pgsql-head/src/include/catalog/indexing.h 2006-07-13 18:47:01.000000000 +0100
--- pgsql-enums/src/include/catalog/indexing.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 142,147 ****
--- 142,152 ----
DECLARE_UNIQUE_INDEX(pg_shdescription_o_c_index, 2397, on pg_shdescription using btree(objoid oid_ops, classoid oid_ops));
#define SharedDescriptionObjIndexId 2397
+ DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 2878, on pg_enum using btree(oid oid_ops));
+ #define EnumOidIndexId 2878
+ DECLARE_UNIQUE_INDEX(pg_enum_typid_name_index, 2879, on pg_enum using btree(enumtypid oid_ops, enumname name_ops));
+ #define EnumTypIdNameIndexId 2879
+
/* This following index is not used for a cache and is not unique */
DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
#define IndexIndrelidIndexId 2678
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_aggregate.h pgsql-enums/src/include/catalog/pg_aggregate.h
*** pgsql-head/src/include/catalog/pg_aggregate.h 2006-10-04 01:30:07.000000000 +0100
--- pgsql-enums/src/include/catalog/pg_aggregate.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 221,226 ****
--- 221,231 ----
DATA(insert ( 2242 bitand - 0 1560 _null_ ));
DATA(insert ( 2243 bitor - 0 1560 _null_ ));
+
+ /* enums */
+ DATA(insert ( 2913 enum_larger - 2906 2858 _null_ ));
+ DATA(insert ( 2914 enum_smaller - 2905 2858 _null_ ));
+
/*
* prototypes for functions in pg_aggregate.c
*/
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_amop.h pgsql-enums/src/include/catalog/pg_amop.h
*** pgsql-head/src/include/catalog/pg_amop.h 2006-10-04 01:30:07.000000000 +0100
--- pgsql-enums/src/include/catalog/pg_amop.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 892,895 ****
--- 892,909 ----
DATA(insert ( 2780 0 3 t 2752 ));
DATA(insert ( 2780 0 4 t 1070 ));
+ /*
+ * enum_btree_ops
+ */
+ DATA(insert ( 2909 0 1 f 2905 ));
+ DATA(insert ( 2909 0 2 f 2907 ));
+ DATA(insert ( 2909 0 3 f 2903 ));
+ DATA(insert ( 2909 0 4 f 2908 ));
+ DATA(insert ( 2909 0 5 f 2906 ));
+
+ /*
+ * enum_hash_ops
+ */
+ DATA(insert ( 2910 0 1 f 2903 ));
+
#endif /* PG_AMOP_H */
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_amproc.h pgsql-enums/src/include/catalog/pg_amproc.h
*** pgsql-head/src/include/catalog/pg_amproc.h 2006-10-04 01:30:07.000000000 +0100
--- pgsql-enums/src/include/catalog/pg_amproc.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 125,130 ****
--- 125,131 ----
DATA(insert ( 2233 0 1 380 ));
DATA(insert ( 2234 0 1 381 ));
DATA(insert ( 2789 0 1 2794 ));
+ DATA(insert ( 2909 0 1 2901 ));
/* hash */
***************
*** 161,166 ****
--- 162,168 ----
DATA(insert ( 2231 0 1 456 ));
DATA(insert ( 2232 0 1 455 ));
DATA(insert ( 2235 0 1 329 ));
+ DATA(insert ( 2910 0 1 2902 ));
/* gist */
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_cast.h pgsql-enums/src/include/catalog/pg_cast.h
*** pgsql-head/src/include/catalog/pg_cast.h 2006-03-05 15:58:54.000000000 +0000
--- pgsql-enums/src/include/catalog/pg_cast.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 392,395 ****
--- 392,402 ----
DATA(insert ( 1562 1562 1687 i ));
DATA(insert ( 1700 1700 1703 i ));
+ /*
+ * enums
+ */
+ DATA(insert ( 2858 25 2911 e ));
+ DATA(insert ( 25 2858 2912 e ));
+
+
#endif /* PG_CAST_H */
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_enum.h pgsql-enums/src/include/catalog/pg_enum.h
*** pgsql-head/src/include/catalog/pg_enum.h 1970-01-01 01:00:00.000000000 +0100
--- pgsql-enums/src/include/catalog/pg_enum.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 0 ****
--- 1,71 ----
+ /*-------------------------------------------------------------------------
+ *
+ * pg_enum.h
+ * definition of the system enum relation, pg_enum.
+ * it has no initial content
+ *
+ * Copyright (c) 2006, PostgreSQL Global Development Group
+ *
+ * $PostgreSQL$
+ *
+ * NOTES
+ * The script catalog/genbki.sh reads this file and generates .bki
+ * information from the DATA() statements. utils/Gen_fmgrtab.sh
+ * generates fmgroids.h and fmgrtab.c the same way.
+ *
+ * XXX do NOT break up DATA() statements into multiple lines!
+ * the scripts are not as smart as you might think...
+ * XXX (eg. #if 0 #endif won't do what you think)
+ *
+ *-------------------------------------------------------------------------
+ */
+ #ifndef PG_ENUM_H
+ #define PG_ENUM_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_proc definition. cpp turns this into
+ * typedef struct FormData_pg_proc
+ * ----------------
+ */
+ #define EnumRelationId 2859
+
+ CATALOG(pg_enum,2859)
+ {
+ Oid enumtypid; /* OID of owning enum type */
+ NameData enumname; /* 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_enumname 2
+
+ /* ----------------
+ * no initial contents of pg_enum
+ * ----------------
+ */
+
+ /*
+ * prototypes for functions in pg_enum.c
+ */
+ extern void EnumValuesCreate(Oid enumTypeOid,
+ List *vals);
+
+ #endif /* PG_ENUM_H */
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_opclass.h pgsql-enums/src/include/catalog/pg_opclass.h
*** pgsql-head/src/include/catalog/pg_opclass.h 2006-07-21 21:51:33.000000000 +0100
--- pgsql-enums/src/include/catalog/pg_opclass.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 207,211 ****
--- 207,213 ----
DATA(insert OID = 2778 ( 2742 _money_ops PGNSP PGUID 791 t 790 ));
DATA(insert OID = 2779 ( 2742 _reltime_ops PGNSP PGUID 1024 t 703 ));
DATA(insert OID = 2780 ( 2742 _tinterval_ops PGNSP PGUID 1025 t 704 ));
+ DATA(insert OID = 2909 ( 403 enum_btree_ops PGNSP PGUID 2858 t 0 ));
+ DATA(insert OID = 2910 ( 405 enum_hash_ops PGNSP PGUID 2858 t 0 ));
#endif /* PG_OPCLASS_H */
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_operator.h pgsql-enums/src/include/catalog/pg_operator.h
*** pgsql-head/src/include/catalog/pg_operator.h 2006-10-04 01:30:07.000000000 +0100
--- pgsql-enums/src/include/catalog/pg_operator.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 903,908 ****
--- 903,915 ----
DATA(insert OID = 2876 ( "@" PGNSP PGUID b f 601 603 16 0 0 0 0 0 0 on_sb - - ));
DATA(insert OID = 2877 ( "~" PGNSP PGUID b f 1034 1033 16 0 0 0 0 0 0 aclcontains - - ));
+ /* enum operators */
+ DATA(insert OID = 2903 ( "=" PGNSP PGUID b t 2858 2858 16 2903 2904 2905 2905 2905 2906 enum_eq eqsel eqjoinsel ));
+ DATA(insert OID = 2904 ( "<>" PGNSP PGUID b f 2858 2858 16 2904 2903 0 0 0 0 enum_ne neqsel neqjoinsel ));
+ DATA(insert OID = 2905 ( "<" PGNSP PGUID b f 2858 2858 16 2906 2908 0 0 0 0 enum_lt scalarltsel scalarltjoinsel ));
+ DATA(insert OID = 2906 ( ">" PGNSP PGUID b f 2858 2858 16 2905 2907 0 0 0 0 enum_gt scalargtsel scalargtjoinsel ));
+ DATA(insert OID = 2907 ( "<=" PGNSP PGUID b f 2858 2858 16 2908 2906 0 0 0 0 enum_le scalarltsel scalarltjoinsel ));
+ DATA(insert OID = 2908 ( ">=" PGNSP PGUID b f 2858 2858 16 2907 2905 0 0 0 0 enum_ge scalargtsel scalargtjoinsel ));
/*
* function prototypes
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_proc.h pgsql-enums/src/include/catalog/pg_proc.h
*** pgsql-head/src/include/catalog/pg_proc.h 2006-12-06 18:06:47.000000000 +0000
--- pgsql-enums/src/include/catalog/pg_proc.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 3976,3981 ****
--- 3976,4017 ----
DATA(insert OID = 2892 ( pg_advisory_unlock_all PGNSP PGUID 12 f f t f v 0 2278 "" _null_ _null_ _null_ pg_advisory_unlock_all - _null_ ));
DESCR("release all advisory locks");
+ /* enum related procs */
+ DATA(insert OID = 2893 ( enum_in PGNSP PGUID 12 f f t f s 2 2858 "2275 26" _null_ _null_ _null_ enum_in - _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 2894 ( enum_out PGNSP PGUID 12 f f t f s 1 2275 "2858" _null_ _null_ _null_ enum_out - _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 2895 ( enum_eq PGNSP PGUID 12 f f t f i 2 16 "2858 2858" _null_ _null_ _null_ enum_eq - _null_ ));
+ DESCR("equal");
+ DATA(insert OID = 2896 ( enum_ne PGNSP PGUID 12 f f t f i 2 16 "2858 2858" _null_ _null_ _null_ enum_ne - _null_ ));
+ DESCR("not equal");
+ DATA(insert OID = 2897 ( enum_lt PGNSP PGUID 12 f f t f i 2 16 "2858 2858" _null_ _null_ _null_ enum_lt - _null_ ));
+ DESCR("less-than");
+ DATA(insert OID = 2898 ( enum_gt PGNSP PGUID 12 f f t f i 2 16 "2858 2858" _null_ _null_ _null_ enum_gt - _null_ ));
+ DESCR("greater-than");
+ DATA(insert OID = 2899 ( enum_le PGNSP PGUID 12 f f t f i 2 16 "2858 2858" _null_ _null_ _null_ enum_le - _null_ ));
+ DESCR("less-than-or-equal");
+ DATA(insert OID = 2900 ( enum_ge PGNSP PGUID 12 f f t f i 2 16 "2858 2858" _null_ _null_ _null_ enum_ge - _null_ ));
+ DESCR("greater-than-or-equal");
+ DATA(insert OID = 2901 ( enum_cmp PGNSP PGUID 12 f f t f i 2 23 "2858 2858" _null_ _null_ _null_ enum_cmp - _null_ ));
+ DESCR("btree-less-equal-greater");
+ DATA(insert OID = 2902 ( hashenum PGNSP PGUID 12 f f t f i 1 23 "2858" _null_ _null_ _null_ hashenum - _null_ ));
+ DESCR("hash");
+ DATA(insert OID = 2911 ( enum_text PGNSP PGUID 12 f f t f i 1 25 "2858" _null_ _null_ _null_ enum_text - _null_ ));
+ DESCR("convert enum to text");
+ DATA(insert OID = 2912 ( text_enum PGNSP PGUID 12 f f t f i 2 2858 "25 23" _null_ _null_ _null_ text_enum - _null_ ));
+ DESCR("convert text to enum");
+ DATA(insert OID = 2913 ( max PGNSP PGUID 12 t f f f i 1 2858 "2858" _null_ _null_ _null_ aggregate_dummy - _null_ ));
+ DATA(insert OID = 2914 ( min PGNSP PGUID 12 t f f f i 1 2858 "2858" _null_ _null_ _null_ aggregate_dummy - _null_ ));
+ DATA(insert OID = 2915 ( enum_smaller PGNSP PGUID 12 f f t f i 2 2858 "2858 2858" _null_ _null_ _null_ enum_smaller - _null_ ));
+ DESCR("smaller of two");
+ DATA(insert OID = 2916 ( enum_larger PGNSP PGUID 12 f f t f i 2 2858 "2858 2858" _null_ _null_ _null_ enum_larger - _null_ ));
+ DESCR("larger of two");
+ DATA(insert OID = 2917 ( enum_first PGNSP PGUID 12 f f t f i 1 2858 "2206" _null_ _null_ _null_ enum_first - _null_ ));
+ DATA(insert OID = 2918 ( enum_last PGNSP PGUID 12 f f t f i 1 2858 "2206" _null_ _null_ _null_ enum_last - _null_ ));
+ DATA(insert OID = 2919 ( enum_range PGNSP PGUID 12 f f f f i 2 2277 "2858 2858" _null_ _null_ _null_ enum_range_bounds - _null_ ));
+ DATA(insert OID = 2920 ( enum_range PGNSP PGUID 12 f f t f i 1 2277 "2206" _null_ _null_ _null_ enum_range_all - _null_ ));
+
/*
* Symbolic values for provolatile column: these indicate whether the result
* of a function is dependent *only* on the values of its explicit arguments,
diff -r -N -c --exclude=CVS pgsql-head/src/include/catalog/pg_type.h pgsql-enums/src/include/catalog/pg_type.h
*** pgsql-head/src/include/catalog/pg_type.h 2006-10-04 01:30:08.000000000 +0100
--- pgsql-enums/src/include/catalog/pg_type.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 67,73 ****
/*
* typtype is 'b' for a basic type, 'c' for a complex type (ie a table's
! * rowtype), 'd' for a domain type, or 'p' for a pseudo type.
*
* If typtype is 'c', typrelid is the OID of the class' entry in pg_class.
*/
--- 67,74 ----
/*
* typtype is 'b' for a basic type, 'c' for a complex type (ie a table's
! * rowtype), 'd' for a domain type, 'p' for a pseudo type, or 'e' for an
! * enum type.
*
* If typtype is 'c', typrelid is the OID of the class' entry in pg_class.
*/
***************
*** 547,552 ****
--- 548,555 ----
#define OPAQUEOID 2282
DATA(insert OID = 2283 ( anyelement PGNSP PGUID 4 t p t \054 0 0 anyelement_in anyelement_out - - - i p f 0 -1 0 _null_ _null_ ));
#define ANYELEMENTOID 2283
+ DATA(insert OID = 2858 ( anyenum PGNSP PGUID 4 t p t \054 0 0 enum_in enum_out - - - i p f 0 -1 0 _null_ _null_ ));
+ #define ANYENUMOID 2858
/*
* prototypes for functions in pg_type.c
diff -r -N -c --exclude=CVS pgsql-head/src/include/commands/typecmds.h pgsql-enums/src/include/commands/typecmds.h
*** pgsql-head/src/include/commands/typecmds.h 2006-03-05 15:58:55.000000000 +0000
--- pgsql-enums/src/include/commands/typecmds.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 24,29 ****
--- 24,30 ----
extern void RemoveTypeById(Oid typeOid);
extern void DefineDomain(CreateDomainStmt *stmt);
extern void RemoveDomain(List *names, DropBehavior behavior, bool missing_ok);
+ extern void DefineEnum(CreateEnumStmt *stmt);
extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
extern void AlterDomainDefault(List *names, Node *defaultRaw);
diff -r -N -c --exclude=CVS pgsql-head/src/include/nodes/nodes.h pgsql-enums/src/include/nodes/nodes.h
*** pgsql-head/src/include/nodes/nodes.h 2006-09-28 21:51:42.000000000 +0100
--- pgsql-enums/src/include/nodes/nodes.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 293,298 ****
--- 293,299 ----
T_AlterOwnerStmt,
T_DropOwnedStmt,
T_ReassignOwnedStmt,
+ T_CreateEnumStmt,
T_A_Expr = 800,
T_ColumnRef,
diff -r -N -c --exclude=CVS pgsql-head/src/include/nodes/parsenodes.h pgsql-enums/src/include/nodes/parsenodes.h
*** pgsql-head/src/include/nodes/parsenodes.h 2006-11-05 22:42:10.000000000 +0000
--- pgsql-enums/src/include/nodes/parsenodes.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 1311,1316 ****
--- 1311,1327 ----
} CreateDomainStmt;
/* ----------------------
+ * Create Enum Statement
+ * ----------------------
+ */
+ typedef struct CreateEnumStmt
+ {
+ NodeTag type;
+ List *defnames; /* qualified name (list of Value strings) */
+ List *vals; /* enum values (list of Value strings) */
+ } CreateEnumStmt;
+
+ /* ----------------------
* Create Operator Class Statement
* ----------------------
*/
diff -r -N -c --exclude=CVS pgsql-head/src/include/utils/lsyscache.h pgsql-enums/src/include/utils/lsyscache.h
*** pgsql-head/src/include/utils/lsyscache.h 2006-10-04 01:30:10.000000000 +0100
--- pgsql-enums/src/include/utils/lsyscache.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 113,118 ****
--- 113,121 ----
extern char *get_namespace_name(Oid nspid);
extern Oid get_roleid(const char *rolname);
extern Oid get_roleid_checked(const char *rolname);
+ extern Oid get_enum_type_oid(Oid val);
+ extern bool is_type_enum(Oid typeOid);
+
#define is_array_type(typid) (get_element_type(typid) != InvalidOid)
diff -r -N -c --exclude=CVS pgsql-head/src/include/utils/syscache.h pgsql-enums/src/include/utils/syscache.h
*** pgsql-head/src/include/utils/syscache.h 2006-07-13 19:01:02.000000000 +0100
--- pgsql-enums/src/include/utils/syscache.h 2006-12-19 01:23:51.000000000 +0000
***************
*** 47,68 ****
#define CONNAMENSP 16
#define CONOID 17
#define DATABASEOID 18
! #define INDEXRELID 19
! #define INHRELID 20
! #define LANGNAME 21
! #define LANGOID 22
! #define NAMESPACENAME 23
! #define NAMESPACEOID 24
! #define OPERNAMENSP 25
! #define OPEROID 26
! #define PROCNAMEARGSNSP 27
! #define PROCOID 28
! #define RELNAMENSP 29
! #define RELOID 30
! #define RULERELNAME 31
! #define STATRELATT 32
! #define TYPENAMENSP 33
! #define TYPEOID 34
extern void InitCatalogCache(void);
extern void InitCatalogCachePhase2(void);
--- 47,70 ----
#define CONNAMENSP 16
#define CONOID 17
#define DATABASEOID 18
! #define ENUMOID 19
! #define ENUMTYPOIDNAME 20
! #define INDEXRELID 21
! #define INHRELID 22
! #define LANGNAME 23
! #define LANGOID 24
! #define NAMESPACENAME 25
! #define NAMESPACEOID 26
! #define OPERNAMENSP 27
! #define OPEROID 28
! #define PROCNAMEARGSNSP 29
! #define PROCOID 30
! #define RELNAMENSP 31
! #define RELOID 32
! #define RULERELNAME 33
! #define STATRELATT 34
! #define TYPENAMENSP 35
! #define TYPEOID 36
extern void InitCatalogCache(void);
extern void InitCatalogCachePhase2(void);
diff -r -N -c --exclude=CVS pgsql-head/src/pl/plpgsql/src/pl_comp.c pgsql-enums/src/pl/plpgsql/src/pl_comp.c
*** pgsql-head/src/pl/plpgsql/src/pl_comp.c 2006-10-04 01:30:13.000000000 +0100
--- pgsql-enums/src/pl/plpgsql/src/pl_comp.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 359,365 ****
argdtype = plpgsql_build_datatype(argtypeid, -1);
/* Disallow pseudotype argument */
! /* (note we already replaced ANYARRAY/ANYELEMENT) */
/* (build_variable would do this, but wrong message) */
if (argdtype->ttype != PLPGSQL_TTYPE_SCALAR &&
argdtype->ttype != PLPGSQL_TTYPE_ROW)
--- 359,365 ----
argdtype = plpgsql_build_datatype(argtypeid, -1);
/* Disallow pseudotype argument */
! /* (note we already replaced ANYARRAY/ANYELEMENT/ANYENUM) */
/* (build_variable would do this, but wrong message) */
if (argdtype->ttype != PLPGSQL_TTYPE_SCALAR &&
argdtype->ttype != PLPGSQL_TTYPE_ROW)
***************
*** 427,433 ****
* the info available.
*/
rettypeid = procStruct->prorettype;
! if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)
{
if (forValidator)
{
--- 427,434 ----
* the info available.
*/
rettypeid = procStruct->prorettype;
! if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID ||
! rettypeid == ANYENUMOID)
{
if (forValidator)
{
***************
*** 465,471 ****
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype result, except VOID or RECORD */
! /* (note we already replaced ANYARRAY/ANYELEMENT) */
if (typeStruct->typtype == 'p')
{
if (rettypeid == VOIDOID ||
--- 466,472 ----
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype result, except VOID or RECORD */
! /* (note we already replaced ANYARRAY/ANYELEMENT/ANYENUM) */
if (typeStruct->typtype == 'p')
{
if (rettypeid == VOIDOID ||
***************
*** 498,504 ****
* output parameter.
*/
if ((procStruct->prorettype == ANYARRAYOID ||
! procStruct->prorettype == ANYELEMENTOID) &&
num_out_args == 0)
{
(void) plpgsql_build_variable("$0", 0,
--- 499,506 ----
* output parameter.
*/
if ((procStruct->prorettype == ANYARRAYOID ||
! procStruct->prorettype == ANYELEMENTOID ||
! procStruct->prorettype == ANYENUMOID) &&
num_out_args == 0)
{
(void) plpgsql_build_variable("$0", 0,
***************
*** 1745,1750 ****
--- 1747,1753 ----
{
case 'b': /* base type */
case 'd': /* domain */
+ case 'e': /* domain */
typ->ttype = PLPGSQL_TTYPE_SCALAR;
break;
case 'c': /* composite, ie, rowtype */
***************
*** 1978,1983 ****
--- 1981,1987 ----
switch (argtypes[i])
{
case ANYELEMENTOID:
+ case ANYENUMOID:
argtypes[i] = INT4OID;
break;
case ANYARRAYOID:
diff -r -N -c --exclude=CVS pgsql-head/src/pl/plpgsql/src/pl_handler.c pgsql-enums/src/pl/plpgsql/src/pl_handler.c
*** pgsql-head/src/pl/plpgsql/src/pl_handler.c 2006-10-19 19:32:48.000000000 +0100
--- pgsql-enums/src/pl/plpgsql/src/pl_handler.c 2006-12-19 01:23:51.000000000 +0000
***************
*** 131,137 ****
functyptype = get_typtype(proc->prorettype);
/* Disallow pseudotype result */
! /* except for TRIGGER, RECORD, VOID, ANYARRAY, or ANYELEMENT */
if (functyptype == 'p')
{
/* we assume OPAQUE with no arguments means a trigger */
--- 131,137 ----
functyptype = get_typtype(proc->prorettype);
/* Disallow pseudotype result */
! /* except for TRIGGER, RECORD, VOID, ANYARRAY, ANYELEMENT, or ANYENUM */
if (functyptype == 'p')
{
/* we assume OPAQUE with no arguments means a trigger */
***************
*** 141,147 ****
else if (proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID &&
proc->prorettype != ANYARRAYOID &&
! proc->prorettype != ANYELEMENTOID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("plpgsql functions cannot return type %s",
--- 141,148 ----
else if (proc->prorettype != RECORDOID &&
proc->prorettype != VOIDOID &&
proc->prorettype != ANYARRAYOID &&
! proc->prorettype != ANYELEMENTOID &&
! proc->prorettype != ANYENUMOID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("plpgsql functions cannot return type %s",
***************
*** 149,155 ****
}
/* Disallow pseudotypes in arguments (either IN or OUT) */
! /* except for ANYARRAY or ANYELEMENT */
numargs = get_func_arg_info(tuple,
&argtypes, &argnames, &argmodes);
for (i = 0; i < numargs; i++)
--- 150,156 ----
}
/* Disallow pseudotypes in arguments (either IN or OUT) */
! /* except for ANYARRAY, ANYELEMENT or ANYENUM */
numargs = get_func_arg_info(tuple,
&argtypes, &argnames, &argmodes);
for (i = 0; i < numargs; i++)
***************
*** 157,163 ****
if (get_typtype(argtypes[i]) == 'p')
{
if (argtypes[i] != ANYARRAYOID &&
! argtypes[i] != ANYELEMENTOID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("plpgsql functions cannot take type %s",
--- 158,165 ----
if (get_typtype(argtypes[i]) == 'p')
{
if (argtypes[i] != ANYARRAYOID &&
! argtypes[i] != ANYELEMENTOID &&
! argtypes[i] != ANYENUMOID)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("plpgsql functions cannot take type %s",
diff -r -N -c --exclude=CVS pgsql-head/src/test/regress/expected/enum.out pgsql-enums/src/test/regress/expected/enum.out
*** pgsql-head/src/test/regress/expected/enum.out 1970-01-01 01:00:00.000000000 +0100
--- pgsql-enums/src/test/regress/expected/enum.out 2006-12-19 01:23:51.000000000 +0000
***************
*** 0 ****
--- 1,357 ----
+ --
+ -- 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_type, pg_enum WHERE pg_type.oid = pg_enum.enumtypid
+ AND pg_type.typname = 'rainbow';
+ count
+ -------
+ 6
+ (1 row)
+
+ --
+ -- I/O functions
+ --
+ SELECT 'red'::rainbow;
+ rainbow
+ ---------
+ red
+ (1 row)
+
+ SELECT 'mauve'::rainbow;
+ ERROR: invalid input value for enum: "mauve"
+ --
+ -- Basic table creation, row selection
+ --
+ CREATE TABLE enumtest (col rainbow);
+ INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green'), ('blue'), ('purple');
+ SELECT * FROM enumtest ORDER BY col;
+ 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)
+
+ --
+ -- Index tests, force use of index
+ --
+ SET enable_seqscan = 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)
+
+ 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
+ --
+ SET enable_seqscan = on;
+ --
+ -- 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)
+
+ --
+ -- Domains of enums
+ --
+ CREATE DOMAIN rgb AS rainbow CHECK(VALUE IN ('red', 'green', 'blue'));
+ SELECT 'red'::rgb;
+ rgb
+ -----
+ red
+ (1 row)
+
+ 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('rainbow');
+ enum_first
+ ------------
+ red
+ (1 row)
+
+ SELECT enum_last('rainbow');
+ enum_last
+ -----------
+ purple
+ (1 row)
+
+ SELECT enum_range('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, NULL);
+ ERROR: could not determine anyarray/anyelement type because input has type "unknown"
+ --
+ -- 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);
+ --
+ -- Cleanup
+ --
+ 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 * FROM pg_type WHERE pg_type.oid = enumtypid);
+ enumtypid | enumname
+ -----------+----------
+ (0 rows)
+
diff -r -N -c --exclude=CVS pgsql-head/src/test/regress/expected/sanity_check.out pgsql-enums/src/test/regress/expected/sanity_check.out
*** pgsql-head/src/test/regress/expected/sanity_check.out 2006-08-06 05:35:21.000000000 +0100
--- pgsql-enums/src/test/regress/expected/sanity_check.out 2006-12-19 01:23:51.000000000 +0000
***************
*** 97,102 ****
--- 97,103 ----
pg_database | t
pg_depend | t
pg_description | t
+ pg_enum | t
pg_index | t
pg_inherits | t
pg_language | t
***************
*** 140,146 ****
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (129 rows)
--
-- another sanity check: every system catalog that has OIDs should have
--- 141,147 ----
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
! (130 rows)
--
-- another sanity check: every system catalog that has OIDs should have
diff -r -N -c --exclude=CVS pgsql-head/src/test/regress/expected/type_sanity.out pgsql-enums/src/test/regress/expected/type_sanity.out
*** pgsql-head/src/test/regress/expected/type_sanity.out 2005-07-10 22:14:00.000000000 +0100
--- pgsql-enums/src/test/regress/expected/type_sanity.out 2006-12-19 01:23:51.000000000 +0000
***************
*** 17,23 ****
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
! (p1.typtype not in ('b', 'c', 'd', 'p')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
--- 17,23 ----
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
! (p1.typtype not in ('b', 'c', 'd', 'p', 'e')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
***************
*** 84,90 ****
((p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring'::regtype) OR
(p2.pronargs = 3 AND p2.proargtypes[0] = 'cstring'::regtype AND
p2.proargtypes[1] = 'oid'::regtype AND
! p2.proargtypes[2] = 'int4'::regtype));
oid | typname | oid | proname
-----+---------+-----+---------
(0 rows)
--- 84,92 ----
((p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring'::regtype) OR
(p2.pronargs = 3 AND p2.proargtypes[0] = 'cstring'::regtype AND
p2.proargtypes[1] = 'oid'::regtype AND
! p2.proargtypes[2] = 'int4'::regtype) OR
! (p2.pronargs = 2 AND p2.proargtypes[0] = 'cstring'::regtype AND
! p2.proargtypes[1] = 'oid'::regtype));
oid | typname | oid | proname
-----+---------+-----+---------
(0 rows)
diff -r -N -c --exclude=CVS pgsql-head/src/test/regress/parallel_schedule pgsql-enums/src/test/regress/parallel_schedule
*** pgsql-head/src/test/regress/parallel_schedule 2006-08-31 00:34:22.000000000 +0100
--- pgsql-enums/src/test/regress/parallel_schedule 2006-12-19 01:23:51.000000000 +0000
***************
*** 12,18 ****
# ----------
# The second group of parallel test
# ----------
! test: point lseg box path polygon circle date time timetz timestamp timestamptz interval abstime reltime tinterval inet comments oidjoins type_sanity opr_sanity
# Depends on point, lseg, box, path, polygon and circle
test: geometry
--- 12,18 ----
# ----------
# The second group of parallel test
# ----------
! test: point lseg box path polygon circle date time timetz timestamp timestamptz interval abstime reltime tinterval inet comments oidjoins type_sanity opr_sanity enum
# Depends on point, lseg, box, path, polygon and circle
test: geometry
diff -r -N -c --exclude=CVS pgsql-head/src/test/regress/serial_schedule pgsql-enums/src/test/regress/serial_schedule
*** pgsql-head/src/test/regress/serial_schedule 2006-08-31 00:34:22.000000000 +0100
--- pgsql-enums/src/test/regress/serial_schedule 2006-12-19 01:23:51.000000000 +0000
***************
*** 35,40 ****
--- 35,41 ----
test: oidjoins
test: type_sanity
test: opr_sanity
+ test: enum
test: geometry
test: horology
test: insert
diff -r -N -c --exclude=CVS pgsql-head/src/test/regress/sql/enum.sql pgsql-enums/src/test/regress/sql/enum.sql
*** pgsql-head/src/test/regress/sql/enum.sql 1970-01-01 01:00:00.000000000 +0100
--- pgsql-enums/src/test/regress/sql/enum.sql 2006-12-19 01:23:51.000000000 +0000
***************
*** 0 ****
--- 1,143 ----
+ --
+ -- 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_type, pg_enum WHERE pg_type.oid = pg_enum.enumtypid
+ AND pg_type.typname = 'rainbow';
+
+ --
+ -- 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'), ('blue'), ('purple');
+ SELECT * FROM enumtest ORDER BY col;
+
+ --
+ -- 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;
+
+ --
+ -- Index tests, force use of index
+ --
+ SET enable_seqscan = 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;
+ 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
+ --
+ SET enable_seqscan = on;
+
+ --
+ -- Aggregates
+ --
+ SELECT min(col) FROM enumtest;
+ SELECT max(col) FROM enumtest;
+ SELECT max(col) FROM enumtest WHERE col < 'green';
+
+ --
+ -- Domains of enums
+ --
+ CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue'));
+ SELECT 'red'::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('rainbow');
+ SELECT enum_last('rainbow');
+ SELECT enum_range('rainbow');
+ SELECT enum_range('orange'::rainbow, 'green'::rainbow);
+ SELECT enum_range(NULL, 'green'::rainbow);
+ SELECT enum_range('orange'::rainbow, NULL);
+ SELECT enum_range(NULL, 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);
+
+ --
+ -- Cleanup
+ --
+ 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 * FROM pg_type WHERE pg_type.oid = enumtypid);
diff -r -N -c --exclude=CVS pgsql-head/src/test/regress/sql/type_sanity.sql pgsql-enums/src/test/regress/sql/type_sanity.sql
*** pgsql-head/src/test/regress/sql/type_sanity.sql 2005-07-10 22:14:00.000000000 +0100
--- pgsql-enums/src/test/regress/sql/type_sanity.sql 2006-12-19 01:23:51.000000000 +0000
***************
*** 20,26 ****
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
! (p1.typtype not in ('b', 'c', 'd', 'p')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
--- 20,26 ----
FROM pg_type as p1
WHERE p1.typnamespace = 0 OR
(p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
! (p1.typtype not in ('b', 'c', 'd', 'p', 'e')) OR
NOT p1.typisdefined OR
(p1.typalign not in ('c', 's', 'i', 'd')) OR
(p1.typstorage not in ('p', 'x', 'e', 'm'));
***************
*** 73,79 ****
((p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring'::regtype) OR
(p2.pronargs = 3 AND p2.proargtypes[0] = 'cstring'::regtype AND
p2.proargtypes[1] = 'oid'::regtype AND
! p2.proargtypes[2] = 'int4'::regtype));
-- As of 8.0, this check finds refcursor, which is borrowing
-- other types' I/O routines
--- 73,81 ----
((p2.pronargs = 1 AND p2.proargtypes[0] = 'cstring'::regtype) OR
(p2.pronargs = 3 AND p2.proargtypes[0] = 'cstring'::regtype AND
p2.proargtypes[1] = 'oid'::regtype AND
! p2.proargtypes[2] = 'int4'::regtype) OR
! (p2.pronargs = 2 AND p2.proargtypes[0] = 'cstring'::regtype AND
! p2.proargtypes[1] = 'oid'::regtype));
-- As of 8.0, this check finds refcursor, which is borrowing
-- other types' I/O routines