From: Tom Lane Date: Mon, 2 Apr 2007 03:49:42 +0000 (+0000) Subject: Support enum data types. Along the way, use macros for the values of X-Git-Tag: REL8_3_BETA1~917 X-Git-Url: http://git.postgresql.org/gitweb/?a=commitdiff_plain;h=57690c6803525f879fe96920a05e979ece073e71;p=postgresql.git Support enum data types. Along the way, use macros for the values of pg_type.typtype whereever practical. Tom Dunstan, with some kibitzing from Tom Lane. --- diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml index 7d325d1dde5..492b06de0a2 100644 --- a/doc/src/sgml/catalogs.sgml +++ b/doc/src/sgml/catalogs.sgml @@ -1,4 +1,4 @@ - + @@ -128,6 +128,11 @@ descriptions or comments on database objects + + pg_enum + enum label and value definitions + + pg_index additional index information @@ -1425,11 +1430,7 @@ in which the source and target types are the same, if the associated function takes more than one argument. Such entries represent length coercion functions that coerce values of the type - to be legal for a particular type modifier value. Note however that - at present there is no support for associating non-default type - modifiers with user-created data types, and so this facility is only - of use for the small number of built-in types that have type modifier - syntax built into the grammar. + to be legal for a particular type modifier value. @@ -2413,6 +2414,55 @@ + + <structname>pg_enum</structname> + + + pg_enum + + + + The pg_enum catalog contains entries + matching enum types to their associated values and labels. The + internal representation of a given enum value is actually the OID + of its associated row in pg_enum. The + OIDs for a particular enum type are guaranteed to be ordered in + the way the type should sort, but there is no guarantee about the + ordering of OIDs of unrelated enum types. + + + + <structname>pg_enum</> Columns + + + + + Name + Type + References + Description + + + + + + enumtypid + oid + pg_type.oid + The OID of the pg_type entry owning this enum value + + + + enumlabel + name + + The textual label for this enum value + + + +
+
+ <structname>pg_index</structname> @@ -4395,11 +4445,13 @@ char - 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 + typtype is + b for a base type, + c for a composite type (e.g., a table's row type), + d for a domain, + e for an enum type, + or p for a pseudo-type. + See also typrelid and typbasetype
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml index 2d67885fd65..1bf103c8780 100644 --- a/doc/src/sgml/datatype.sgml +++ b/doc/src/sgml/datatype.sgml @@ -1,4 +1,4 @@ - + Data Types @@ -2424,6 +2424,161 @@ SELECT * FROM test1 WHERE a; + + Enumerated Types + + + data type + enumerated (enum) + + + + Enumerated (enum) types are data types that + are comprised of a static, predefined set of values with a + specific order. They are equivalent to the enum + types in a number of 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 are created using the command, + for example: + + +CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy'); + + + Once created, the enum type can be used in table and function + definitions much like any other type: + + + + Basic 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) + + + + + + Ordering + + + The ordering of the values in an enum type is the + order in which the values were listed when the type was declared. + All standard comparison operators and related + aggregate functions are supported for enums. For example: + + + + 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, 'sad'); +ERROR: invalid input value for enum happiness: "sad" +SELECT person.name, holidays.num_weeks FROM person, holidays + WHERE person.current_mood = holidays.happiness; +ERROR: operator does not exist: mood = happiness + + + + + 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 Details + + + An enum value occupies four bytes on disk. The length of an enum + value's textual label is limited by the NAMEDATALEN + setting compiled into PostgreSQL; in standard + builds this means at most 63 bytes. + + + + Enum labels are case sensitive, so + 'happy' is not the same as 'HAPPY'. + Spaces in the labels are significant, too. + + + + + Geometric Types @@ -3278,6 +3433,10 @@ SELECT * FROM pg_attribute anyelement + + anyenum + + void @@ -3343,6 +3502,13 @@ SELECT * FROM pg_attribute (see ). + + 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. @@ -3395,8 +3561,8 @@ SELECT * FROM pg_attribute 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. + support polymorphic functions using the types anyarray, + anyelement and anyenum. diff --git a/doc/src/sgml/extend.sgml b/doc/src/sgml/extend.sgml index ad22a5dcddd..bb5834e74a9 100644 --- a/doc/src/sgml/extend.sgml +++ b/doc/src/sgml/extend.sgml @@ -1,4 +1,4 @@ - + Extending <acronym>SQL</acronym> @@ -193,9 +193,10 @@ - Two pseudo-types of special interest are anyelement and - anyarray, which are collectively called polymorphic - types. Any function declared using these types is said to be + Three pseudo-types of special interest are anyelement, + anyarray, and anyenum, + which are collectively called polymorphic types. + Any function declared using these types is said to be a polymorphic function. A polymorphic function can operate on many different data types, with the specific data type(s) being determined by the data types actually passed to it in a particular @@ -215,6 +216,9 @@ anyelement, the actual array type in the anyarray positions must be an array whose elements are the same type appearing in the anyelement positions. + anyenum is treated exactly the same as anyelement, + but adds the additional constraint that the actual type must + be an enum type. @@ -234,7 +238,9 @@ implements subscripting as subscript(anyarray, integer) returns anyelement. This declaration constrains the actual first argument to be an array type, and allows the parser to infer the correct - result type from the actual first argument's type. + result type from the actual first argument's type. Another example + is that a function declared as f(anyarray) returns anyenum + will only accept arrays of enum types. diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml index db7cd1d1f3b..0baf1525944 100644 --- a/doc/src/sgml/func.sgml +++ b/doc/src/sgml/func.sgml @@ -1,4 +1,4 @@ - + Functions and Operators @@ -6646,6 +6646,87 @@ SELECT pg_sleep(1.5); + + Enum Support Functions + + + For enum types (described in ), + there are several functions that allow cleaner programming without + hard-coding particular values of an enum type. + These are listed in . The examples + assume an enum type created as: + + +CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); + + + + + + Enum Support Functions + + + + Function + Description + Example + Example Result + + + + + enum_first(anyenum) + Returns the first value of the input enum type + enum_first(null::rainbow) + red + + + enum_last(anyenum) + Returns the last value of the input enum type + enum_last(null::rainbow) + purple + + + enum_range(anyenum) + Returns all values of the input enum type in an ordered array + enum_range(null::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 result will start with the first value of + the enum type. + If the second parameter is null, the result will end with the last + value of the enum type. + + enum_range('orange'::rainbow, 'green'::rainbow) + {orange,yellow,green} + + + enum_range(NULL, 'green'::rainbow) + {red,orange,yellow,green} + + + enum_range('orange'::rainbow, NULL) + {orange,yellow,green,blue,purple} + + + +
+ + + Notice that except for the two-argument form of enum_range, + these functions disregard the specific value passed to them; they care + only about its declared datatype. Either NULL or a specific value of + the type can be passed, with the same result. It is more common to + apply these functions to a table column or function argument than to + a hardwired type name as suggested by the examples. + +
+ Geometric Functions and Operators diff --git a/doc/src/sgml/plpgsql.sgml b/doc/src/sgml/plpgsql.sgml index 55904d6638a..e6f7309c650 100644 --- a/doc/src/sgml/plpgsql.sgml +++ b/doc/src/sgml/plpgsql.sgml @@ -1,4 +1,4 @@ - + <application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language @@ -210,7 +210,8 @@ $$ LANGUAGE plpgsql; PL/pgSQL functions can also be declared to accept and return the polymorphic types - anyelement and anyarray. The actual + anyelement, anyarray, and anyenum. + The actual data types handled by a polymorphic function can vary from call to call, as discussed in . An example is shown in . @@ -698,8 +699,9 @@ $$ LANGUAGE plpgsql; When the return type of a PL/pgSQL - function is declared as a polymorphic type (anyelement - or anyarray), a special parameter $0 + function is declared as a polymorphic type (anyelement, + anyarray, or anyenum), + a special parameter $0 is created. Its data type is the actual return type of the function, as deduced from the actual input types (see ). @@ -726,7 +728,7 @@ $$ LANGUAGE plpgsql; The same effect can be had by declaring one or more output parameters as - anyelement or anyarray. In this case the + polymorphic types. In this case the special $0 parameter is not used; the output parameters themselves serve the same purpose. For example: diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml index 29f35417079..9be57d7fcde 100644 --- a/doc/src/sgml/ref/create_type.sgml +++ b/doc/src/sgml/ref/create_type.sgml @@ -1,5 +1,5 @@ @@ -23,6 +23,9 @@ PostgreSQL documentation CREATE TYPE name AS ( attribute_name data_type [, ... ] ) +CREATE TYPE name AS ENUM + ( 'label' [, ... ] ) + CREATE TYPE name ( INPUT = input_function, OUTPUT = output_function @@ -77,11 +80,23 @@ CREATE TYPE name + + 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, each of which + must be less than NAMEDATALEN bytes long (64 in a standard + PostgreSQL build). + + + Base Types - The second form of CREATE TYPE creates a new base type + The third form of CREATE TYPE creates a new base type (scalar type). The parameters can appear in any order, not only that illustrated above, and most are optional. You must register two or more functions (using CREATE FUNCTION) before @@ -297,7 +312,7 @@ CREATE TYPE name Array Types - Whenever a user-defined base data type is created, + Whenever a user-defined base or enum data type is created, PostgreSQL automatically creates an associated array type, whose name consists of the base type's name prepended with an underscore. The parser understands this @@ -363,6 +378,16 @@ CREATE TYPE name + + label + + + A string literal representing the textual label associated with + one value of an enum type. + + + + input_function @@ -567,6 +592,20 @@ $$ LANGUAGE SQL; + + 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 --git a/doc/src/sgml/xfunc.sgml b/doc/src/sgml/xfunc.sgml index 65d636b5e4c..553b33e1732 100644 --- a/doc/src/sgml/xfunc.sgml +++ b/doc/src/sgml/xfunc.sgml @@ -1,4 +1,4 @@ - + User-Defined Functions @@ -717,8 +717,8 @@ SELECT name, listchildren(name) FROM nodes; SQL functions can be declared to accept and - return the polymorphic types anyelement and - anyarray. See anyelement, + anyarray, and anyenum. See for a more detailed explanation of polymorphic functions. Here is a polymorphic function make_array that builds up an array @@ -746,7 +746,7 @@ SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray; Without the typecast, you will get errors like this: -ERROR: could not determine "anyarray"/"anyelement" type because input has type "unknown" +ERROR: could not determine polymorphic type because input has type "unknown" @@ -769,7 +769,7 @@ CREATE FUNCTION invalid_func() RETURNS anyelement AS $$ SELECT 1; $$ LANGUAGE SQL; ERROR: cannot determine result data type -DETAIL: A function returning "anyarray" or "anyelement" must have at least one argument of either type. +DETAIL: A function returning a polymorphic type must have at least one polymorphic argument. @@ -2831,7 +2831,7 @@ CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer, C-language functions can be declared to accept and return the polymorphic types - anyelement and anyarray. + anyelement, anyarray, and anyenum. See for a more detailed explanation of polymorphic functions. When function arguments or return types are defined as polymorphic types, the function author cannot know diff --git a/src/backend/access/hash/hashfunc.c b/src/backend/access/hash/hashfunc.c index 1a1f21ef2aa..d9b5524b0ab 100644 --- a/src/backend/access/hash/hashfunc.c +++ b/src/backend/access/hash/hashfunc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/access/hash/hashfunc.c,v 1.50 2007/01/05 22:19:22 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/access/hash/hashfunc.c,v 1.51 2007/04/02 03:49:37 tgl Exp $ * * NOTES * These functions are stored in pg_amproc. For each operator class @@ -72,6 +72,12 @@ hashoid(PG_FUNCTION_ARGS) PG_RETURN_UINT32(~((uint32) PG_GETARG_OID(0))); } +Datum +hashenum(PG_FUNCTION_ARGS) +{ + PG_RETURN_UINT32(~((uint32) PG_GETARG_OID(0))); +} + Datum hashfloat4(PG_FUNCTION_ARGS) { diff --git a/src/backend/catalog/Makefile b/src/backend/catalog/Makefile index 026d9053a0a..3503385c2ac 100644 --- a/src/backend/catalog/Makefile +++ b/src/backend/catalog/Makefile @@ -2,7 +2,7 @@ # # Makefile for backend/catalog # -# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.63 2007/02/09 15:55:58 petere Exp $ +# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.64 2007/04/02 03:49:37 tgl Exp $ # #------------------------------------------------------------------------- @@ -11,7 +11,7 @@ top_builddir = ../../.. include $(top_builddir)/src/Makefile.global 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_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \ pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_shdepend.o \ pg_type.o toasting.o @@ -32,7 +32,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\ pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \ pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \ pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \ - pg_namespace.h pg_conversion.h pg_depend.h \ + pg_enum.h 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 \ toasting.h indexing.h \ diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index d29a6df21d3..e6404ecd0b5 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.317 2007/02/14 01:58:56 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.318 2007/04/02 03:49:37 tgl Exp $ * * * INTERFACE ROUTINES @@ -412,7 +412,7 @@ CheckAttributeType(const char *attname, Oid atttypid) (errcode(ERRCODE_INVALID_TABLE_DEFINITION), errmsg("column \"%s\" has type \"unknown\"", attname), errdetail("Proceeding with relation creation anyway."))); - else if (att_typtype == 'p') + else if (att_typtype == TYPTYPE_PSEUDO) { /* Special hack for pg_statistic: allow ANYARRAY during initdb */ if (atttypid != ANYARRAYOID || IsUnderPostmaster) @@ -718,7 +718,7 @@ AddNewRelationType(const char *typeName, new_rel_oid, /* relation oid */ new_rel_kind, /* relation kind */ -1, /* internal size (varlena) */ - 'c', /* type-type (complex) */ + TYPTYPE_COMPOSITE, /* type-type (composite) */ ',', /* default array delimiter */ F_RECORD_IN, /* input procedure */ F_RECORD_OUT, /* output procedure */ diff --git a/src/backend/catalog/pg_aggregate.c b/src/backend/catalog/pg_aggregate.c index 1d448cf5620..b60cd7714f5 100644 --- a/src/backend/catalog/pg_aggregate.c +++ b/src/backend/catalog/pg_aggregate.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.85 2007/01/22 01:35:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.86 2007/04/02 03:49:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -80,8 +80,7 @@ AggregateCreate(const char *aggName, hasPolyArg = false; for (i = 0; i < numArgs; i++) { - if (aggArgTypes[i] == ANYARRAYOID || - aggArgTypes[i] == ANYELEMENTOID) + if (IsPolymorphicType(aggArgTypes[i])) { hasPolyArg = true; break; @@ -92,12 +91,11 @@ AggregateCreate(const char *aggName, * If transtype is polymorphic, must have polymorphic argument also; else * we will have no way to deduce the actual transtype. */ - if (!hasPolyArg && - (aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID)) + if (IsPolymorphicType(aggTransType) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine transition data type"), - errdetail("An aggregate using \"anyarray\" or \"anyelement\" as transition type must have at least one argument of either type."))); + errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument."))); /* find the transfn */ nargs_transfn = numArgs + 1; @@ -170,13 +168,12 @@ AggregateCreate(const char *aggName, * that itself violates the rule against polymorphic result with no * polymorphic input.) */ - if (!hasPolyArg && - (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID)) + if (IsPolymorphicType(finaltype) && !hasPolyArg) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot determine result data type"), - errdetail("An aggregate returning \"anyarray\" or \"anyelement\" " - "must have at least one argument of either type."))); + errdetail("An aggregate returning a polymorphic type " + "must have at least one polymorphic argument."))); /* handle sortop, if supplied */ if (aggsortopName) @@ -329,8 +326,7 @@ lookup_agg_function(List *fnName, */ for (i = 0; i < nargs; i++) { - if (input_types[i] != ANYARRAYOID && - input_types[i] != ANYELEMENTOID) + if (!IsPolymorphicType(input_types[i])) { allPolyArgs = false; break; @@ -351,8 +347,7 @@ lookup_agg_function(List *fnName, */ for (i = 0; i < nargs; i++) { - if (true_oid_array[i] != ANYARRAYOID && - true_oid_array[i] != ANYELEMENTOID && + if (!IsPolymorphicType(true_oid_array[i]) && !IsBinaryCoercible(input_types[i], true_oid_array[i])) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c new file mode 100644 index 00000000000..696c1f06d44 --- /dev/null +++ b/src/backend/catalog/pg_enum.c @@ -0,0 +1,146 @@ +/*------------------------------------------------------------------------- + * + * pg_enum.c + * routines to support manipulation of the pg_enum relation + * + * Copyright (c) 2006-2007, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/catalog/pg_enum.c,v 1.1 2007/04/02 03:49:37 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "access/genam.h" +#include "access/heapam.h" +#include "catalog/catalog.h" +#include "catalog/indexing.h" +#include "catalog/pg_enum.h" +#include "utils/builtins.h" +#include "utils/fmgroids.h" + +static int oid_cmp(const void *p1, const void *p2); + + +/* + * EnumValuesCreate + * Create an entry in pg_enum for each of the supplied enum values. + * + * vals is a list of Value strings. + */ +void +EnumValuesCreate(Oid enumTypeOid, List *vals) +{ + Relation pg_enum; + TupleDesc tupDesc; + NameData enumlabel; + Oid *oids; + int i, n; + Datum values[Natts_pg_enum]; + char nulls[Natts_pg_enum]; + ListCell *lc; + HeapTuple tup; + + n = list_length(vals); + + /* + * XXX we do not bother to check the list of values for duplicates --- + * if you have any, you'll get a less-than-friendly unique-index + * violation. Is it worth trying harder? + */ + + pg_enum = heap_open(EnumRelationId, RowExclusiveLock); + tupDesc = pg_enum->rd_att; + + /* + * Allocate oids. While this method does not absolutely guarantee + * that we generate no duplicate oids (since we haven't entered each + * oid into the table before allocating the next), trouble could only + * occur if the oid counter wraps all the way around before we finish. + * Which seems unlikely. + */ + oids = (Oid *) palloc(n * sizeof(Oid)); + for(i = 0; i < n; i++) + { + oids[i] = GetNewOid(pg_enum); + } + + /* sort them, just in case counter wrapped from high to low */ + qsort(oids, n, sizeof(Oid), oid_cmp); + + /* and make the entries */ + memset(nulls, ' ', sizeof(nulls)); + + i = 0; + foreach(lc, vals) + { + char *lab = strVal(lfirst(lc)); + + values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid); + namestrcpy(&enumlabel, lab); + values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel); + + tup = heap_formtuple(tupDesc, values, nulls); + HeapTupleSetOid(tup, oids[i]); + + simple_heap_insert(pg_enum, tup); + CatalogUpdateIndexes(pg_enum, tup); + heap_freetuple(tup); + + i++; + } + + /* clean up */ + pfree(oids); + heap_close(pg_enum, RowExclusiveLock); +} + + +/* + * EnumValuesDelete + * Remove all the pg_enum entries for the specified enum type. + */ +void +EnumValuesDelete(Oid enumTypeOid) +{ + Relation pg_enum; + ScanKeyData key[1]; + SysScanDesc scan; + HeapTuple tup; + + pg_enum = heap_open(EnumRelationId, RowExclusiveLock); + + ScanKeyInit(&key[0], + Anum_pg_enum_enumtypid, + BTEqualStrategyNumber, F_OIDEQ, + ObjectIdGetDatum(enumTypeOid)); + + scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true, + SnapshotNow, 1, key); + + while (HeapTupleIsValid(tup = systable_getnext(scan))) + { + simple_heap_delete(pg_enum, &tup->t_self); + } + + systable_endscan(scan); + + heap_close(pg_enum, RowExclusiveLock); +} + + +/* qsort comparison function */ +static int +oid_cmp(const void *p1, const void *p2) +{ + Oid v1 = *((const Oid *) p1); + Oid v2 = *((const Oid *) p2); + + if (v1 < v2) + return -1; + if (v1 > v2) + return 1; + return 0; +} diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index d4ed9b69f52..26c5eb75e3e 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.143 2007/01/22 01:35:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.144 2007/04/02 03:49:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -137,9 +137,9 @@ ProcedureCreate(const char *procedureName, } /* - * 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. + * Do not allow polymorphic return type unless at least one input argument + * is polymorphic. Also, do not allow return type INTERNAL unless at + * least one input argument is INTERNAL. */ for (i = 0; i < parameterCount; i++) { @@ -147,6 +147,7 @@ ProcedureCreate(const char *procedureName, { case ANYARRAYOID: case ANYELEMENTOID: + case ANYENUMOID: genericInParam = true; break; case INTERNALOID: @@ -169,6 +170,7 @@ ProcedureCreate(const char *procedureName, { case ANYARRAYOID: case ANYELEMENTOID: + case ANYENUMOID: genericOutParam = true; break; case INTERNALOID: @@ -178,12 +180,12 @@ ProcedureCreate(const char *procedureName, } } - if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID || - genericOutParam) && !genericInParam) + if ((IsPolymorphicType(returnType) || genericOutParam) + && !genericInParam) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), - errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type."))); + errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); if ((returnType == INTERNALOID || internalOutParam) && !internalInParam) ereport(ERROR, @@ -533,26 +535,24 @@ fmgr_sql_validator(PG_FUNCTION_ARGS) proc = (Form_pg_proc) GETSTRUCT(tuple); /* Disallow pseudotype result */ - /* except for RECORD, VOID, ANYARRAY, or ANYELEMENT */ - if (get_typtype(proc->prorettype) == 'p' && + /* except for RECORD, VOID, or polymorphic */ + if (get_typtype(proc->prorettype) == TYPTYPE_PSEUDO && proc->prorettype != RECORDOID && proc->prorettype != VOIDOID && - proc->prorettype != ANYARRAYOID && - proc->prorettype != ANYELEMENTOID) + !IsPolymorphicType(proc->prorettype)) 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 */ + /* except for polymorphic */ haspolyarg = false; for (i = 0; i < proc->pronargs; i++) { - if (get_typtype(proc->proargtypes.values[i]) == 'p') + if (get_typtype(proc->proargtypes.values[i]) == TYPTYPE_PSEUDO) { - if (proc->proargtypes.values[i] == ANYARRAYOID || - proc->proargtypes.values[i] == ANYELEMENTOID) + if (IsPolymorphicType(proc->proargtypes.values[i])) haspolyarg = true; else ereport(ERROR, diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index df5e996a780..efc3b8de099 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.110 2007/01/05 22:19:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.111 2007/04/02 03:49:37 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -75,7 +75,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace) * * The representational details are the same as int4 ... it doesn't really * matter what they are so long as they are consistent. Also note that we - * give it typtype = 'p' (pseudotype) as extra insurance that it won't be + * give it typtype = TYPTYPE_PSEUDO as extra insurance that it won't be * mistaken for a usable type. */ i = 0; @@ -85,7 +85,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace) values[i++] = ObjectIdGetDatum(GetUserId()); /* typowner */ values[i++] = Int16GetDatum(sizeof(int4)); /* typlen */ values[i++] = BoolGetDatum(true); /* typbyval */ - values[i++] = CharGetDatum('p'); /* typtype */ + values[i++] = CharGetDatum(TYPTYPE_PSEUDO); /* typtype */ values[i++] = BoolGetDatum(false); /* typisdefined */ values[i++] = CharGetDatum(DEFAULT_TYPDELIM); /* typdelim */ values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */ @@ -159,7 +159,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace) Oid TypeCreate(const char *typeName, Oid typeNamespace, - Oid relationOid, /* only for 'c'atalog types */ + Oid relationOid, /* only for composite types */ char relationKind, /* ditto */ int16 internalSize, char typeType, @@ -243,7 +243,8 @@ TypeCreate(const char *typeName, values[i++] = CharGetDatum(typeType); /* typtype */ values[i++] = BoolGetDatum(true); /* typisdefined */ values[i++] = CharGetDatum(typDelim); /* typdelim */ - values[i++] = ObjectIdGetDatum(typeType == 'c' ? relationOid : InvalidOid); /* typrelid */ + values[i++] = ObjectIdGetDatum(typeType == TYPTYPE_COMPOSITE ? + relationOid : InvalidOid); /* typrelid */ values[i++] = ObjectIdGetDatum(elementType); /* typelem */ values[i++] = ObjectIdGetDatum(inputProcedure); /* typinput */ values[i++] = ObjectIdGetDatum(outputProcedure); /* typoutput */ @@ -377,7 +378,7 @@ TypeCreate(const char *typeName, void GenerateTypeDependencies(Oid typeNamespace, Oid typeObjectId, - Oid relationOid, /* only for 'c'atalog types */ + Oid relationOid, /* only for composite types */ char relationKind, /* ditto */ Oid owner, Oid inputProcedure, diff --git a/src/backend/commands/aggregatecmds.c b/src/backend/commands/aggregatecmds.c index 27fc681bea3..89ea00d89a6 100644 --- a/src/backend/commands/aggregatecmds.c +++ b/src/backend/commands/aggregatecmds.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.42 2007/01/05 22:19:25 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.43 2007/04/02 03:49:37 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -176,9 +176,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters) * in some cases (AggregateCreate will check). */ transTypeId = typenameTypeId(NULL, transType); - if (get_typtype(transTypeId) == 'p' && - transTypeId != ANYARRAYOID && - transTypeId != ANYELEMENTOID) + if (get_typtype(transTypeId) == TYPTYPE_PSEUDO && + !IsPolymorphicType(transTypeId)) ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("aggregate transition data type cannot be %s", diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c index 2971add22a6..0949feab1f2 100644 --- a/src/backend/commands/functioncmds.c +++ b/src/backend/commands/functioncmds.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.82 2007/01/22 01:35:20 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.83 2007/04/02 03:49:37 tgl Exp $ * * DESCRIPTION * These routines take the parse tree and pick out the @@ -1259,13 +1259,13 @@ CreateCast(CreateCastStmt *stmt) targettypeid = typenameTypeId(NULL, stmt->targettype); /* No pseudo-types allowed */ - if (get_typtype(sourcetypeid) == 'p') + if (get_typtype(sourcetypeid) == TYPTYPE_PSEUDO) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("source data type %s is a pseudo-type", TypeNameToString(stmt->sourcetype)))); - if (get_typtype(targettypeid) == 'p') + if (get_typtype(targettypeid) == TYPTYPE_PSEUDO) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("target data type %s is a pseudo-type", diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 99f964db74d..5c1b9f6f0e6 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.100 2007/02/14 01:58:57 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.101 2007/04/02 03:49:38 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -39,6 +39,7 @@ #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" @@ -205,7 +206,7 @@ DefineType(List *names, List *parameters) { elemType = typenameTypeId(NULL, defGetTypeName(defel)); /* disallow arrays of pseudotypes */ - if (get_typtype(elemType) == 'p') + if (get_typtype(elemType) == TYPTYPE_PSEUDO) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("array element type cannot be %s", @@ -404,7 +405,7 @@ DefineType(List *names, List *parameters) InvalidOid, /* relation oid (n/a here) */ 0, /* relation kind (ditto) */ internalLength, /* internal size */ - 'b', /* type-type (base type) */ + TYPTYPE_BASE, /* type-type (base type) */ delimiter, /* array element delimiter */ inputOid, /* input procedure */ outputOid, /* output procedure */ @@ -438,7 +439,7 @@ DefineType(List *names, List *parameters) InvalidOid, /* relation oid (n/a here) */ 0, /* relation kind (ditto) */ -1, /* internal size */ - 'b', /* type-type (base type) */ + TYPTYPE_BASE, /* type-type (base type) */ DEFAULT_TYPDELIM, /* array element delimiter */ F_ARRAY_IN, /* input procedure */ F_ARRAY_OUT, /* output procedure */ @@ -543,6 +544,14 @@ RemoveTypeById(Oid typeOid) simple_heap_delete(relation, &tup->t_self); + /* + * If it is an enum, delete the pg_enum entries too; we don't bother + * with making dependency entries for those, so it has to be done + * "by hand" here. + */ + if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM) + EnumValuesDelete(typeOid); + ReleaseSysCache(tup); heap_close(relation, RowExclusiveLock); @@ -620,12 +629,15 @@ DefineDomain(CreateDomainStmt *stmt) basetypeMod = typenameTypeMod(NULL, stmt->typename, basetypeoid); /* - * 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. + * 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') + if (typtype != TYPTYPE_BASE && + typtype != TYPTYPE_DOMAIN && + typtype != TYPTYPE_ENUM) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("\"%s\" is not a valid base type for a domain", @@ -798,7 +810,7 @@ DefineDomain(CreateDomainStmt *stmt) InvalidOid, /* relation oid (n/a here) */ 0, /* relation kind (ditto) */ internalLength, /* internal size */ - 'd', /* type-type (domain type) */ + TYPTYPE_DOMAIN, /* type-type (domain type) */ delimiter, /* array element delimiter */ inputProcedure, /* input procedure */ outputProcedure, /* output procedure */ @@ -907,7 +919,7 @@ RemoveDomain(List *names, DropBehavior behavior, bool missing_ok) /* Check that this is actually a domain */ typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype; - if (typtype != 'd') + if (typtype != TYPTYPE_DOMAIN) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a domain", @@ -925,6 +937,100 @@ RemoveDomain(List *names, DropBehavior behavior, bool missing_ok) performDeletion(&object, behavior); } +/* + * DefineEnum + * Registers a new enum. + */ +void +DefineEnum(CreateEnumStmt *stmt) +{ + char *enumName; + char *enumArrayName; + Oid enumNamespace; + Oid enumTypeOid; + AclResult aclresult; + + /* Convert list of names to a name and namespace */ + enumNamespace = QualifiedNameGetCreationNamespace(stmt->typename, + &enumName); + + /* Check we have creation rights in target namespace */ + aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE); + if (aclresult != ACLCHECK_OK) + aclcheck_error(aclresult, ACL_KIND_NAMESPACE, + get_namespace_name(enumNamespace)); + + /* + * Type names must be one character shorter than other names, allowing + * room to create the corresponding array type name with prepended "_". + */ + if (strlen(enumName) > (NAMEDATALEN - 2)) + ereport(ERROR, + (errcode(ERRCODE_INVALID_NAME), + errmsg("type names must be %d characters or less", + NAMEDATALEN - 2))); + + /* Create the pg_type entry */ + enumTypeOid = + TypeCreate(enumName, /* type name */ + enumNamespace, /* namespace */ + InvalidOid, /* relation oid (n/a here) */ + 0, /* relation kind (ditto) */ + sizeof(Oid), /* internal size */ + TYPTYPE_ENUM, /* type-type (enum type) */ + DEFAULT_TYPDELIM, /* array element delimiter */ + F_ENUM_IN, /* input procedure */ + F_ENUM_OUT, /* output procedure */ + InvalidOid, /* receive procedure - none */ + InvalidOid, /* send procedure - none */ + InvalidOid, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + InvalidOid, /* analyze procedure - default */ + InvalidOid, /* element type ID */ + InvalidOid, /* base type ID (only for domains) */ + NULL, /* never a default type value */ + NULL, /* binary default isn't sent either */ + true, /* always passed by value */ + 'i', /* int alignment */ + 'p', /* TOAST strategy always plain */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false); /* Type NOT NULL */ + + /* Enter the enum's values into pg_enum */ + EnumValuesCreate(enumTypeOid, stmt->vals); + + /* Create array type for enum */ + enumArrayName = makeArrayTypeName(enumName); + + TypeCreate(enumArrayName, /* type name */ + enumNamespace, /* namespace */ + InvalidOid, /* relation oid (n/a here) */ + 0, /* relation kind (ditto) */ + -1, /* internal size */ + TYPTYPE_BASE, /* 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, /* typmodin procedure - none */ + InvalidOid, /* typmodout procedure - none */ + 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 align i, so do their arrays */ + 'x', /* ARRAY is always toastable */ + -1, /* typMod (Domains only) */ + 0, /* Array dimensions of typbasetype */ + false); /* Type NOT NULL */ + + pfree(enumArrayName); +} + /* * Find suitable I/O functions for a type. @@ -1835,7 +1941,7 @@ checkDomainOwner(HeapTuple tup, TypeName *typename) Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup); /* Check that this is actually a domain */ - if (typTup->typtype != 'd') + if (typTup->typtype != TYPTYPE_DOMAIN) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("\"%s\" is not a domain", @@ -2021,7 +2127,7 @@ GetDomainConstraints(Oid typeOid) elog(ERROR, "cache lookup failed for type %u", typeOid); typTup = (Form_pg_type) GETSTRUCT(tup); - if (typTup->typtype != 'd') + if (typTup->typtype != TYPTYPE_DOMAIN) { /* Not a domain, so done */ ReleaseSysCache(tup); @@ -2148,7 +2254,7 @@ AlterTypeOwner(List *names, Oid newOwnerId) * free-standing composite type, and not a table's underlying type. We * want people to use ALTER TABLE not ALTER TYPE for that case. */ - if (typTup->typtype == 'c' && + if (typTup->typtype == TYPTYPE_COMPOSITE && get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), @@ -2325,11 +2431,12 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, /* Detect whether type is a composite type (but not a table rowtype) */ isCompositeType = - (typform->typtype == 'c' && + (typform->typtype == TYPTYPE_COMPOSITE && get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE); /* Enforce not-table-type if requested */ - if (typform->typtype == 'c' && !isCompositeType && errorOnTableType) + if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType && + errorOnTableType) ereport(ERROR, (errcode(ERRCODE_WRONG_OBJECT_TYPE), errmsg("%s is a table's row type", @@ -2376,14 +2483,14 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid, else { /* If it's a domain, it might have constraints */ - if (typform->typtype == 'd') + if (typform->typtype == TYPTYPE_DOMAIN) AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true); /* * Update dependency on schema, if any --- a table rowtype has not got * one. */ - if (typform->typtype != 'c') + if (typform->typtype != TYPTYPE_COMPOSITE) if (changeDependencyFor(TypeRelationId, typeOid, NamespaceRelationId, oldNspOid, nspOid) != 1) elog(ERROR, "failed to change schema dependency for type %s", diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index 7e648f437b6..4781bd903ab 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.112 2007/03/13 00:33:40 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.113 2007/04/02 03:49:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -182,7 +182,7 @@ init_sql_fcache(FmgrInfo *finfo) */ rettype = procedureStruct->prorettype; - if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID) + if (IsPolymorphicType(rettype)) { rettype = get_fn_expr_rettype(finfo); if (rettype == InvalidOid) /* this probably should not happen */ @@ -218,7 +218,7 @@ init_sql_fcache(FmgrInfo *finfo) { Oid argtype = argOidVect[argnum]; - if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID) + if (IsPolymorphicType(argtype)) { argtype = get_fn_expr_argtype(finfo, argnum); if (argtype == InvalidOid) @@ -845,9 +845,9 @@ ShutdownSQLFunction(Datum arg) * to be sure that the user is returning the type he claims. * * For a polymorphic function the passed rettype must be the actual resolved - * output type of the function; we should never see ANYARRAY or ANYELEMENT - * as rettype. (This means we can't check the type during function definition - * of a polymorphic function.) + * output type of the function; we should never see ANYARRAY, ANYENUM or + * ANYELEMENT as rettype. (This means we can't check the type during function + * definition of a polymorphic function.) * * 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 @@ -925,7 +925,9 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, fn_typtype = get_typtype(rettype); - if (fn_typtype == 'b' || fn_typtype == 'd') + if (fn_typtype == TYPTYPE_BASE || + fn_typtype == TYPTYPE_DOMAIN || + fn_typtype == TYPTYPE_ENUM) { /* * For base-type returns, the target list should have exactly one @@ -948,7 +950,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, errdetail("Actual return type is %s.", format_type_be(restype)))); } - else if (fn_typtype == 'c' || rettype == RECORDOID) + else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID) { /* Returns a rowtype */ TupleDesc tupdesc; @@ -1053,13 +1055,13 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList, /* Report that we are returning entire tuple result */ return true; } - else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID) + else if (IsPolymorphicType(rettype)) { /* This should already have been caught ... */ ereport(ERROR, (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION), errmsg("cannot determine result data type"), - errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type."))); + errdetail("A function returning a polymorphic type must have at least one polymorphic argument."))); } else ereport(ERROR, diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index 58e59c60c5a..d9e26d1d196 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -61,7 +61,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.151 2007/02/22 23:44:24 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.152 2007/04/02 03:49:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1363,8 +1363,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) /* * 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. + * the agg's declared input types, when the agg accepts ANY or + * a polymorphic type. */ i = 0; foreach(lc, aggref->args) @@ -1421,7 +1421,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags) /* resolve actual type of transition state, if polymorphic */ aggtranstype = aggform->aggtranstype; - if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID) + if (IsPolymorphicType(aggtranstype)) { /* have to fetch the agg's declared input types... */ Oid *declaredArgTypes; diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 198a583f88f..9bf57fb87f2 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -15,7 +15,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.372 2007/03/27 23:21:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.373 2007/04/02 03:49:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2361,6 +2361,17 @@ _copyCompositeTypeStmt(CompositeTypeStmt *from) return newnode; } +static CreateEnumStmt * +_copyCreateEnumStmt(CreateEnumStmt *from) +{ + CreateEnumStmt *newnode = makeNode(CreateEnumStmt); + + COPY_NODE_FIELD(typename); + COPY_NODE_FIELD(vals); + + return newnode; +} + static ViewStmt * _copyViewStmt(ViewStmt *from) { @@ -3312,6 +3323,9 @@ copyObject(void *from) case T_CompositeTypeStmt: retval = _copyCompositeTypeStmt(from); break; + case T_CreateEnumStmt: + retval = _copyCreateEnumStmt(from); + break; case T_ViewStmt: retval = _copyViewStmt(from); break; diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c index 977f121bc42..1cc0c343dca 100644 --- a/src/backend/nodes/equalfuncs.c +++ b/src/backend/nodes/equalfuncs.c @@ -18,7 +18,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.303 2007/03/27 23:21:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.304 2007/04/02 03:49:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1186,6 +1186,15 @@ _equalCompositeTypeStmt(CompositeTypeStmt *a, CompositeTypeStmt *b) return true; } +static bool +_equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b) +{ + COMPARE_NODE_FIELD(typename); + COMPARE_NODE_FIELD(vals); + + return true; +} + static bool _equalViewStmt(ViewStmt *a, ViewStmt *b) { @@ -2247,6 +2256,9 @@ equal(void *a, void *b) case T_CompositeTypeStmt: retval = _equalCompositeTypeStmt(a, b); break; + case T_CreateEnumStmt: + retval = _equalCreateEnumStmt(a, b); + break; case T_ViewStmt: retval = _equalViewStmt(a, b); break; diff --git a/src/backend/optimizer/util/clauses.c b/src/backend/optimizer/util/clauses.c index 67652fbfdec..d4cb3f06b05 100644 --- a/src/backend/optimizer/util/clauses.c +++ b/src/backend/optimizer/util/clauses.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.240 2007/03/27 23:21:09 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.241 2007/04/02 03:49:38 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -435,7 +435,7 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts) ReleaseSysCache(aggTuple); /* resolve actual type of transition state, if polymorphic */ - if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID) + if (IsPolymorphicType(aggtranstype)) { /* have to fetch the agg's declared input types... */ Oid *declaredArgTypes; @@ -2907,8 +2907,7 @@ inline_function(Oid funcid, Oid result_type, List *args, funcform->pronargs * sizeof(Oid)); for (i = 0; i < funcform->pronargs; i++) { - if (argtypes[i] == ANYARRAYOID || - argtypes[i] == ANYELEMENTOID) + if (IsPolymorphicType(argtypes[i])) { argtypes[i] = exprType((Node *) list_nth(args, i)); } diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 8cad738a357..767129fb924 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.584 2007/03/26 16:58:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.585 2007/04/02 03:49:38 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -248,6 +248,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) TableFuncElementList opt_type_modifiers prep_type_clause execute_param_clause using_clause returning_clause + enum_val_list %type OptTempTableName %type into_clause create_as_target @@ -383,7 +384,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args) DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS DESC DISABLE_P DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP - EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING + EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT EXCLUDING EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD @@ -2922,6 +2923,13 @@ DefineStmt: n->coldeflist = $6; $$ = (Node *)n; } + | CREATE TYPE_P any_name AS ENUM_P '(' enum_val_list ')' + { + CreateEnumStmt *n = makeNode(CreateEnumStmt); + n->typename = $3; + n->vals = $7; + $$ = (Node *)n; + } ; definition: '(' def_list ')' { $$ = $2; } @@ -2966,6 +2974,12 @@ old_aggr_elem: IDENT '=' def_arg } ; +enum_val_list: Sconst + { $$ = list_make1(makeString($1)); } + | enum_val_list ',' Sconst + { $$ = lappend($1, makeString($3)); } + ; + /***************************************************************************** * @@ -8760,6 +8774,7 @@ unreserved_keyword: | ENABLE_P | ENCODING | ENCRYPTED + | ENUM_P | ESCAPE | EXCLUDING | EXCLUSIVE diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 9be91e7e6e6..9f6cf1e20e4 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.185 2007/03/19 23:38:29 wieck Exp $ + * $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.186 2007/04/02 03:49:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -136,6 +136,7 @@ static const ScanKeyword ScanKeywords[] = { {"encoding", ENCODING}, {"encrypted", ENCRYPTED}, {"end", END_P}, + {"enum", ENUM_P}, {"escape", ESCAPE}, {"except", EXCEPT}, {"excluding", EXCLUDING}, diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c index fbc83870a5c..c232dee4a58 100644 --- a/src/backend/parser/parse_coerce.c +++ b/src/backend/parser/parse_coerce.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.152 2007/03/27 23:21:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.153 2007/04/02 03:49:38 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -132,7 +132,8 @@ coerce_type(ParseState *pstate, Node *node, } if (targetTypeId == ANYOID || targetTypeId == ANYELEMENTOID || - (targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID)) + (targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) || + (targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID)) { /* * Assume can_coerce_type verified that implicit coercion is okay. @@ -143,7 +144,8 @@ coerce_type(ParseState *pstate, Node *node, * since an UNKNOWN value is still a perfectly valid Datum. However * an UNKNOWN value is definitely *not* an array, and so we mustn't * accept it for ANYARRAY. (Instead, we will call anyarray_in below, - * which will produce an error.) + * which will produce an error.) Likewise, UNKNOWN input is no good + * for ANYENUM. * * NB: we do NOT want a RelabelType here. */ @@ -406,9 +408,8 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids, if (targetTypeId == ANYOID) continue; - /* accept if target is ANYARRAY or ANYELEMENT, for now */ - if (targetTypeId == ANYARRAYOID || - targetTypeId == ANYELEMENTOID) + /* accept if target is polymorphic, for now */ + if (IsPolymorphicType(targetTypeId)) { have_generics = true; /* do more checking later */ continue; @@ -1048,6 +1049,9 @@ coerce_to_common_type(ParseState *pstate, Node *node, * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure * the actual ANYELEMENT datatype is in fact the element type for * the actual ANYARRAY datatype. + * 4) ANYENUM is treated the same as ANYELEMENT except that if it is used + * (alone or in combination with plain ANYELEMENT), we add the extra + * condition that the ANYELEMENT type must be an enum. * * If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT * or ANYARRAY argument, assume it is okay. @@ -1070,6 +1074,7 @@ check_generic_type_consistency(Oid *actual_arg_types, Oid array_typeid = InvalidOid; Oid array_typelem; bool have_anyelement = false; + bool have_anyenum = false; /* * Loop through the arguments to see if we have any that are ANYARRAY or @@ -1079,9 +1084,12 @@ check_generic_type_consistency(Oid *actual_arg_types, { Oid actual_type = actual_arg_types[j]; - if (declared_arg_types[j] == ANYELEMENTOID) + if (declared_arg_types[j] == ANYELEMENTOID || + declared_arg_types[j] == ANYENUMOID) { have_anyelement = true; + if (declared_arg_types[j] == ANYENUMOID) + have_anyenum = true; if (actual_type == UNKNOWNOID) continue; if (OidIsValid(elem_typeid) && actual_type != elem_typeid) @@ -1127,6 +1135,13 @@ check_generic_type_consistency(Oid *actual_arg_types, } } + if (have_anyenum) + { + /* require the element type to be an enum */ + if (!type_is_enum(elem_typeid)) + return false; + } + /* Looks valid */ return true; } @@ -1136,18 +1151,18 @@ check_generic_type_consistency(Oid *actual_arg_types, * Make sure a polymorphic function is legally callable, and * deduce actual argument and result types. * - * If ANYARRAY or ANYELEMENT is used for a function's arguments or + * If ANYARRAY, ANYELEMENT, or ANYENUM is used for a function's arguments or * return type, we make sure the actual data types are consistent with * each other. The argument consistency rules are shown above for * check_generic_type_consistency(). * - * If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT - * or ANYARRAY argument, we attempt to deduce the actual type it should - * have. If successful, we alter that position of declared_arg_types[] - * so that make_fn_arguments will coerce the literal to the right thing. + * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic + * argument, we attempt to deduce the actual type it should have. If + * successful, we alter that position of declared_arg_types[] so that + * make_fn_arguments will coerce the literal to the right thing. * * Rules are applied to the function's return type (possibly altering it) - * if it is declared ANYARRAY or ANYELEMENT: + * if it is declared as a polymorphic type: * * 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the * argument's actual type as the function's return type. @@ -1167,6 +1182,9 @@ check_generic_type_consistency(Oid *actual_arg_types, * 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT, * generate an ERROR. This condition is prevented by CREATE FUNCTION * and is therefore not expected here. + * 7) ANYENUM is treated the same as ANYELEMENT except that if it is used + * (alone or in combination with plain ANYELEMENT), we add the extra + * condition that the ANYELEMENT type must be an enum. */ Oid enforce_generic_type_consistency(Oid *actual_arg_types, @@ -1180,7 +1198,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types, Oid elem_typeid = InvalidOid; Oid array_typeid = InvalidOid; Oid array_typelem; - bool have_anyelement = (rettype == ANYELEMENTOID); + bool have_anyelement = (rettype == ANYELEMENTOID || + rettype == ANYENUMOID); + bool have_anyenum = (rettype == ANYENUMOID); /* * Loop through the arguments to see if we have any that are ANYARRAY or @@ -1190,9 +1210,12 @@ enforce_generic_type_consistency(Oid *actual_arg_types, { Oid actual_type = actual_arg_types[j]; - if (declared_arg_types[j] == ANYELEMENTOID) + if (declared_arg_types[j] == ANYELEMENTOID || + declared_arg_types[j] == ANYENUMOID) { have_generics = have_anyelement = true; + if (declared_arg_types[j] == ANYENUMOID) + have_anyenum = true; if (actual_type == UNKNOWNOID) { have_unknowns = true; @@ -1227,8 +1250,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, } /* - * Fast Track: if none of the arguments are ANYARRAY or ANYELEMENT, return - * the unmodified rettype. + * Fast Track: if none of the arguments are polymorphic, return the + * unmodified rettype. We assume it can't be polymorphic either. */ if (!have_generics) return rettype; @@ -1274,7 +1297,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* Only way to get here is if all the generic args are UNKNOWN */ ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), - errmsg("could not determine anyarray/anyelement type because input has type \"unknown\""))); + errmsg("could not determine polymorphic type because input has type \"unknown\""))); + } + + if (have_anyenum) + { + /* require the element type to be an enum */ + if (!type_is_enum(elem_typeid)) + ereport(ERROR, + (errcode(ERRCODE_DATATYPE_MISMATCH), + errmsg("type matched to anyenum is not an enum type: %s", + format_type_be(elem_typeid)))); } /* @@ -1289,7 +1322,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, if (actual_type != UNKNOWNOID) continue; - if (declared_arg_types[j] == ANYELEMENTOID) + if (declared_arg_types[j] == ANYELEMENTOID || + declared_arg_types[j] == ANYENUMOID) declared_arg_types[j] = elem_typeid; else if (declared_arg_types[j] == ANYARRAYOID) { @@ -1307,7 +1341,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, } } - /* if we return ANYARRAYOID use the appropriate argument type */ + /* if we return ANYARRAY use the appropriate argument type */ if (rettype == ANYARRAYOID) { if (!OidIsValid(array_typeid)) @@ -1322,8 +1356,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types, return array_typeid; } - /* if we return ANYELEMENTOID use the appropriate argument type */ - if (rettype == ANYELEMENTOID) + /* if we return ANYELEMENT 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 */ @@ -1333,7 +1367,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types, /* * resolve_generic_type() * Deduce an individual actual datatype on the assumption that - * the rules for ANYARRAY/ANYELEMENT are being followed. + * the rules for polymorphic types are being followed. * * declared_type is the declared datatype we want to resolve. * context_actual_type is the actual input datatype to some argument @@ -1362,7 +1396,8 @@ resolve_generic_type(Oid declared_type, format_type_be(context_actual_type)))); return context_actual_type; } - else if (context_declared_type == ANYELEMENTOID) + 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); @@ -1375,7 +1410,7 @@ resolve_generic_type(Oid declared_type, return array_typeid; } } - else if (declared_type == ANYELEMENTOID) + else if (declared_type == ANYELEMENTOID || declared_type == ANYENUMOID) { if (context_declared_type == ANYARRAYOID) { @@ -1389,7 +1424,8 @@ resolve_generic_type(Oid declared_type, format_type_be(context_actual_type)))); return array_typelem; } - else if (context_declared_type == ANYELEMENTOID) + 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; @@ -1402,7 +1438,7 @@ resolve_generic_type(Oid declared_type, } /* If we get here, declared_type is polymorphic and context isn't */ /* NB: this is a calling-code logic error, not a user error */ - elog(ERROR, "could not determine ANYARRAY/ANYELEMENT type because context isn't polymorphic"); + elog(ERROR, "could not determine polymorphic type because context isn't polymorphic"); return InvalidOid; /* keep compiler quiet */ } @@ -1502,6 +1538,7 @@ TypeCategory(Oid inType) case (INTERNALOID): case (OPAQUEOID): case (ANYELEMENTOID): + case (ANYENUMOID): result = GENERIC_TYPE; break; @@ -1647,6 +1684,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype) if (get_element_type(srctype) != InvalidOid) return true; + /* Also accept any enum type as coercible to ANYENUM */ + if (targettype == ANYENUMOID) + if (type_is_enum(srctype)) + return true; + /* Else look in pg_cast */ tuple = SearchSysCache(CASTSOURCETARGET, ObjectIdGetDatum(srctype), @@ -1777,6 +1819,22 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId, result = true; } } + + /* + * If we still haven't found a possibility, check for enums, + * and retry looking for a cast to or from ANYENUM. But don't + * mistakenly conclude that ANYENUM-to-some-enum-type is a + * trivial cast. + */ + if (!result) + { + if (type_is_enum(sourceTypeId)) + result = find_coercion_pathway(targetTypeId, ANYENUMOID, + ccontext, funcid, arrayCoerce); + else if (sourceTypeId != ANYENUMOID && type_is_enum(targetTypeId)) + result = find_coercion_pathway(ANYENUMOID, sourceTypeId, + ccontext, funcid, arrayCoerce); + } } return result; @@ -1813,7 +1871,7 @@ find_typmod_coercion_function(Oid typeId, /* Check for a varlena array type (and not a domain) */ if (typeForm->typelem != InvalidOid && typeForm->typlen == -1 && - typeForm->typtype != 'd') + typeForm->typtype != TYPTYPE_DOMAIN) { /* Yes, switch our attention to the element type */ typeId = typeForm->typelem; diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c index 45e488e33f5..d9a6af82962 100644 --- a/src/backend/parser/parse_expr.c +++ b/src/backend/parser/parse_expr.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.215 2007/03/27 23:21:10 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.216 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1627,7 +1627,7 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname, break; case RTE_FUNCTION: toid = exprType(rte->funcexpr); - if (toid == RECORDOID || get_typtype(toid) == 'c') + if (type_is_rowtype(toid)) { /* func returns composite; same as relation case */ result = (Node *) makeVar(vnum, diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c index c8d295047b3..42bd04fa01e 100644 --- a/src/backend/parser/parse_oper.c +++ b/src/backend/parser/parse_oper.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.94 2007/02/01 19:10:27 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.95 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -886,8 +886,8 @@ make_scalar_array_op(ParseState *pstate, List *opname, declared_arg_types[1] = opform->oprright; /* - * enforce consistency with ANYARRAY and ANYELEMENT argument and return - * types, possibly adjusting return type or declared_arg_types (which will + * enforce consistency with polymorphic argument and return types, + * possibly adjusting return type or declared_arg_types (which will * be used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, @@ -911,15 +911,25 @@ make_scalar_array_op(ParseState *pstate, List *opname, /* * Now switch back to the array type on the right, arranging for any - * needed cast to be applied. + * needed cast to be applied. Beware of polymorphic operators here; + * enforce_generic_type_consistency may or may not have replaced a + * polymorphic type with a real one. */ - res_atypeId = get_array_type(declared_arg_types[1]); - if (!OidIsValid(res_atypeId)) - ereport(ERROR, - (errcode(ERRCODE_UNDEFINED_OBJECT), - errmsg("could not find array type for data type %s", - format_type_be(declared_arg_types[1])), - parser_errposition(pstate, location))); + if (IsPolymorphicType(declared_arg_types[1])) + { + /* assume the actual array type is OK */ + res_atypeId = atypeId; + } + else + { + res_atypeId = get_array_type(declared_arg_types[1]); + if (!OidIsValid(res_atypeId)) + ereport(ERROR, + (errcode(ERRCODE_UNDEFINED_OBJECT), + errmsg("could not find array type for data type %s", + format_type_be(declared_arg_types[1])), + parser_errposition(pstate, location))); + } actual_arg_types[1] = atypeId; declared_arg_types[1] = res_atypeId; @@ -986,8 +996,8 @@ make_op_expr(ParseState *pstate, Operator op, } /* - * enforce consistency with ANYARRAY and ANYELEMENT argument and return - * types, possibly adjusting return type or declared_arg_types (which will + * enforce consistency with polymorphic argument and return types, + * possibly adjusting return type or declared_arg_types (which will * be used as the cast destination by make_fn_arguments) */ rettype = enforce_generic_type_consistency(actual_arg_types, diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c index c0280f5ea2c..ebb86f8bba9 100644 --- a/src/backend/parser/parse_type.c +++ b/src/backend/parser/parse_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.87 2007/01/05 22:19:34 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.88 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -398,16 +398,6 @@ typeByVal(Type t) return typ->typbyval; } -/* given type (as type struct), return the value of its 'typtype' attribute.*/ -char -typeTypType(Type t) -{ - Form_pg_type typ; - - typ = (Form_pg_type) GETSTRUCT(t); - return typ->typtype; -} - /* given type (as type struct), return the name of type */ char * typeTypeName(Type t) diff --git a/src/backend/tcop/utility.c b/src/backend/tcop/utility.c index daf23fd0fe5..864b6d1dd63 100644 --- a/src/backend/tcop/utility.c +++ b/src/backend/tcop/utility.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.275 2007/03/26 16:58:39 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.276 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -337,6 +337,7 @@ check_xact_readonly(Node *parsetree) case T_CreateTableSpaceStmt: case T_CreateTrigStmt: case T_CompositeTypeStmt: + case T_CreateEnumStmt: case T_ViewStmt: case T_DropCastStmt: case T_DropStmt: @@ -779,6 +780,10 @@ ProcessUtility(Node *parsetree, } break; + case T_CreateEnumStmt: /* CREATE TYPE (enum) */ + DefineEnum((CreateEnumStmt *) parsetree); + break; + case T_ViewStmt: /* CREATE VIEW */ DefineView((ViewStmt *) parsetree, queryString); break; @@ -1640,6 +1645,10 @@ CreateCommandTag(Node *parsetree) tag = "CREATE TYPE"; break; + case T_CreateEnumStmt: + tag = "CREATE TYPE"; + break; + case T_ViewStmt: tag = "CREATE VIEW"; break; @@ -2075,6 +2084,10 @@ GetCommandLogLevel(Node *parsetree) lev = LOGSTMT_DDL; break; + case T_CreateEnumStmt: + lev = LOGSTMT_DDL; + break; + case T_ViewStmt: lev = LOGSTMT_DDL; break; diff --git a/src/backend/utils/adt/Makefile b/src/backend/utils/adt/Makefile index ad3b848607b..12d158a49bd 100644 --- a/src/backend/utils/adt/Makefile +++ b/src/backend/utils/adt/Makefile @@ -1,7 +1,7 @@ # # Makefile for utils/adt # -# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.63 2007/01/28 16:16:52 neilc Exp $ +# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.64 2007/04/02 03:49:39 tgl Exp $ # subdir = src/backend/utils/adt @@ -17,7 +17,7 @@ endif 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 \ + 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 --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c new file mode 100644 index 00000000000..288894ec3fb --- /dev/null +++ b/src/backend/utils/adt/enum.c @@ -0,0 +1,409 @@ +/*------------------------------------------------------------------------- + * + * enum.c + * I/O functions, operators, aggregates etc for enum types + * + * Copyright (c) 2006-2007, PostgreSQL Global Development Group + * + * + * IDENTIFICATION + * $PostgreSQL: pgsql/src/backend/utils/adt/enum.c,v 1.1 2007/04/02 03:49:39 tgl Exp $ + * + *------------------------------------------------------------------------- + */ +#include "postgres.h" + +#include "catalog/pg_enum.h" +#include "fmgr.h" +#include "utils/array.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + + +static Oid cstring_enum(char *name, Oid enumtypoid); +static char *enum_cstring(Oid enumval); +static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper); +static int enum_elem_cmp(const void *left, const void *right); + + +/* Basic I/O support */ + +Datum +enum_in(PG_FUNCTION_ARGS) +{ + char *name = PG_GETARG_CSTRING(0); + Oid enumtypoid = PG_GETARG_OID(1); + + PG_RETURN_OID(cstring_enum(name, enumtypoid)); +} + +/* guts of enum_in and text-to-enum */ +static Oid +cstring_enum(char *name, Oid enumtypoid) +{ + HeapTuple tup; + Oid enumoid; + + tup = SearchSysCache(ENUMTYPOIDNAME, + ObjectIdGetDatum(enumtypoid), + CStringGetDatum(name), + 0, 0); + if (tup == NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION), + errmsg("invalid input value for enum %s: \"%s\"", + format_type_be(enumtypoid), + name))); + + enumoid = HeapTupleGetOid(tup); + + ReleaseSysCache(tup); + return enumoid; +} + +Datum +enum_out(PG_FUNCTION_ARGS) +{ + Oid enumoid = PG_GETARG_OID(0); + + PG_RETURN_CSTRING(enum_cstring(enumoid)); +} + +/* guts of enum_out and enum-to-text */ +static char * +enum_cstring(Oid enumval) +{ + HeapTuple tup; + Form_pg_enum en; + char *label; + + tup = SearchSysCache(ENUMOID, + ObjectIdGetDatum(enumval), + 0, 0, 0); + if (tup == NULL) + ereport(ERROR, + (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), + errmsg("invalid internal value for enum: %u", + enumval))); + en = (Form_pg_enum) GETSTRUCT(tup); + + label = pstrdup(NameStr(en->enumlabel)); + + ReleaseSysCache(tup); + return label; +} + +/* Comparison functions and related */ + +Datum +enum_lt(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + PG_RETURN_BOOL(a < b); +} + +Datum +enum_le(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + PG_RETURN_BOOL(a <= b); +} + +Datum +enum_eq(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + PG_RETURN_BOOL(a == b); +} + +Datum +enum_ne(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + PG_RETURN_BOOL(a != b); +} + +Datum +enum_ge(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + PG_RETURN_BOOL(a >= b); +} + +Datum +enum_gt(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + PG_RETURN_BOOL(a > b); +} + +Datum +enum_smaller(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + PG_RETURN_OID(a <= b ? a : b); +} + +Datum +enum_larger(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + PG_RETURN_OID(a >= b ? a : b); +} + +Datum +enum_cmp(PG_FUNCTION_ARGS) +{ + Oid a = PG_GETARG_OID(0); + Oid b = PG_GETARG_OID(1); + + if (a > b) + PG_RETURN_INT32(1); + else if (a == b) + PG_RETURN_INT32(0); + else + PG_RETURN_INT32(-1); +} + +/* Casts between text and enum */ + +Datum +enum_text(PG_FUNCTION_ARGS) +{ + Oid enumval = PG_GETARG_OID(0); + text *result; + char *cstr; + int len; + + cstr = enum_cstring(enumval); + len = strlen(cstr); + result = (text *) palloc(VARHDRSZ + len); + SET_VARSIZE(result, VARHDRSZ + len); + memcpy(VARDATA(result), cstr, len); + pfree(cstr); + PG_RETURN_TEXT_P(result); +} + +Datum +text_enum(PG_FUNCTION_ARGS) +{ + text *textval = PG_GETARG_TEXT_P(0); + Oid enumtypoid; + char *str; + + /* + * We rely on being able to get the specific enum type from the calling + * expression tree. + */ + enumtypoid = get_fn_expr_rettype(fcinfo->flinfo); + if (enumtypoid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("could not determine actual enum type"))); + + str = DatumGetCString(DirectFunctionCall1(textout, + PointerGetDatum(textval))); + PG_RETURN_OID(cstring_enum(str, enumtypoid)); +} + +/* Enum programming support functions */ + +Datum +enum_first(PG_FUNCTION_ARGS) +{ + Oid enumtypoid; + Oid min = InvalidOid; + CatCList *list; + int num, i; + + /* + * We rely on being able to get the specific enum type from the calling + * expression tree. Notice that the actual value of the argument isn't + * examined at all; in particular it might be NULL. + */ + enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); + if (enumtypoid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("could not determine actual enum type"))); + + list = SearchSysCacheList(ENUMTYPOIDNAME, 1, + ObjectIdGetDatum(enumtypoid), + 0, 0, 0); + num = list->n_members; + for (i = 0; i < num; i++) + { + Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data); + if (!OidIsValid(min) || valoid < min) + min = valoid; + } + + ReleaseCatCacheList(list); + + if (!OidIsValid(min)) /* should not happen */ + elog(ERROR, "no values found for enum %s", + format_type_be(enumtypoid)); + + PG_RETURN_OID(min); +} + +Datum +enum_last(PG_FUNCTION_ARGS) +{ + Oid enumtypoid; + Oid max = InvalidOid; + CatCList *list; + int num, i; + + /* + * We rely on being able to get the specific enum type from the calling + * expression tree. Notice that the actual value of the argument isn't + * examined at all; in particular it might be NULL. + */ + enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); + if (enumtypoid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("could not determine actual enum type"))); + + list = SearchSysCacheList(ENUMTYPOIDNAME, 1, + ObjectIdGetDatum(enumtypoid), + 0, 0, 0); + num = list->n_members; + for (i = 0; i < num; i++) + { + Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data); + if(!OidIsValid(max) || valoid > max) + max = valoid; + } + + ReleaseCatCacheList(list); + + if (!OidIsValid(max)) /* should not happen */ + elog(ERROR, "no values found for enum %s", + format_type_be(enumtypoid)); + + PG_RETURN_OID(max); +} + +/* 2-argument variant of enum_range */ +Datum +enum_range_bounds(PG_FUNCTION_ARGS) +{ + Oid lower; + Oid upper; + Oid enumtypoid; + + if (PG_ARGISNULL(0)) + lower = InvalidOid; + else + lower = PG_GETARG_OID(0); + if (PG_ARGISNULL(1)) + upper = InvalidOid; + else + upper = PG_GETARG_OID(1); + + /* + * We rely on being able to get the specific enum type from the calling + * expression tree. The generic type mechanism should have ensured that + * both are of the same type. + */ + enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); + if (enumtypoid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("could not determine actual enum type"))); + + PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper)); +} + +/* 1-argument variant of enum_range */ +Datum +enum_range_all(PG_FUNCTION_ARGS) +{ + Oid enumtypoid; + + /* + * We rely on being able to get the specific enum type from the calling + * expression tree. Notice that the actual value of the argument isn't + * examined at all; in particular it might be NULL. + */ + enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0); + if (enumtypoid == InvalidOid) + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("could not determine actual enum type"))); + + PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, + InvalidOid, InvalidOid)); +} + +static ArrayType * +enum_range_internal(Oid enumtypoid, Oid lower, Oid upper) +{ + ArrayType *result; + CatCList *list; + int total, i, j; + Datum *elems; + + list = SearchSysCacheList(ENUMTYPOIDNAME, 1, + ObjectIdGetDatum(enumtypoid), + 0, 0, 0); + total = list->n_members; + + elems = (Datum *) palloc(total * sizeof(Datum)); + + j = 0; + for (i = 0; i < total; i++) + { + Oid val = HeapTupleGetOid(&(list->members[i]->tuple)); + + if ((!OidIsValid(lower) || lower <= val) && + (!OidIsValid(upper) || val <= upper)) + elems[j++] = ObjectIdGetDatum(val); + } + + /* shouldn't need the cache anymore */ + ReleaseCatCacheList(list); + + /* sort results into OID order */ + qsort(elems, j, sizeof(Datum), enum_elem_cmp); + + /* note this hardwires some details about the representation of Oid */ + result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i'); + + pfree(elems); + + return result; +} + +/* qsort comparison function for Datums that are OIDs */ +static int +enum_elem_cmp(const void *left, const void *right) +{ + Oid l = DatumGetObjectId(*((const Datum *) left)); + Oid r = DatumGetObjectId(*((const Datum *) right)); + + if (l < r) + return -1; + if (l > r) + return 1; + return 0; +} diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c index 898534477b2..f7879bafc50 100644 --- a/src/backend/utils/adt/format_type.c +++ b/src/backend/utils/adt/format_type.c @@ -8,7 +8,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.46 2007/01/05 22:19:40 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.47 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -148,7 +148,7 @@ format_type_internal(Oid type_oid, int32 typemod, if (array_base_type != InvalidOid && typeform->typstorage != 'p' && - typeform->typtype != 'd') + typeform->typtype != TYPTYPE_DOMAIN) { /* Switch our attention to the array element type */ ReleaseSysCache(tuple); diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index 3bd5ff66d38..fc9f3324827 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.18 2007/01/05 22:19:41 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.19 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -163,6 +163,31 @@ anyarray_send(PG_FUNCTION_ARGS) } +/* + * anyenum_in - input routine for pseudo-type ANYENUM. + */ +Datum +anyenum_in(PG_FUNCTION_ARGS) +{ + ereport(ERROR, + (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), + errmsg("cannot accept a value of type anyenum"))); + + PG_RETURN_VOID(); /* keep compiler quiet */ +} + +/* + * anyenum_out - output routine for pseudo-type ANYENUM. + * + * We may as well allow this, since enum_out will in fact work. + */ +Datum +anyenum_out(PG_FUNCTION_ARGS) +{ + return enum_out(fcinfo); +} + + /* * void_in - input routine for pseudo-type VOID. * diff --git a/src/backend/utils/adt/xml.c b/src/backend/utils/adt/xml.c index 55be759c9e3..4fea9912834 100644 --- a/src/backend/utils/adt/xml.c +++ b/src/backend/utils/adt/xml.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.38 2007/04/01 09:00:25 petere Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.39 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -2648,7 +2648,7 @@ map_sql_type_to_xml_name(Oid typeoid, int typmod) Form_pg_type typtuple = (Form_pg_type) GETSTRUCT(tuple); appendStringInfoString(&result, - map_multipart_sql_identifier_to_xml_name((typtuple->typtype == 'd') ? "Domain" : "UDT", + map_multipart_sql_identifier_to_xml_name((typtuple->typtype == TYPTYPE_DOMAIN) ? "Domain" : "UDT", get_database_name(MyDatabaseId), get_namespace_name(typtuple->typnamespace), NameStr(typtuple->typname))); @@ -2877,7 +2877,7 @@ map_sql_type_to_xmlschema_type(Oid typeoid, int typmod) break; default: - if (get_typtype(typeoid) == 'd') + if (get_typtype(typeoid) == TYPTYPE_DOMAIN) { Oid base_typeoid; int32 base_typmod = -1; diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c index 69f1e0c2422..391870c3a69 100644 --- a/src/backend/utils/cache/lsyscache.c +++ b/src/backend/utils/cache/lsyscache.c @@ -7,7 +7,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.150 2007/03/19 16:30:31 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.151 2007/04/02 03:49:39 tgl Exp $ * * NOTES * Eventually, the index information should go through here, too. @@ -1770,7 +1770,7 @@ getTypeIOParam(HeapTuple typeTuple) * own type OID as parameter. (As of 8.2, domains must get their own OID * even if their base type is an array.) */ - if (typeStruct->typtype == 'b' && OidIsValid(typeStruct->typelem)) + if (typeStruct->typtype == TYPTYPE_BASE && OidIsValid(typeStruct->typelem)) return typeStruct->typelem; else return HeapTupleGetOid(typeTuple); @@ -2022,7 +2022,7 @@ getBaseTypeAndTypmod(Oid typid, int32 *typmod) if (!HeapTupleIsValid(tup)) elog(ERROR, "cache lookup failed for type %u", typid); typTup = (Form_pg_type) GETSTRUCT(tup); - if (typTup->typtype != 'd') + if (typTup->typtype != TYPTYPE_DOMAIN) { /* Not a domain, so done */ ReleaseSysCache(tup); @@ -2128,7 +2128,17 @@ get_typtype(Oid typid) bool type_is_rowtype(Oid typid) { - return (typid == RECORDOID || get_typtype(typid) == 'c'); + return (typid == RECORDOID || get_typtype(typid) == TYPTYPE_COMPOSITE); +} + +/* + * type_is_enum + * Returns true if the given type is an enum type. + */ +bool +type_is_enum(Oid typid) +{ + return (get_typtype(typid) == TYPTYPE_ENUM); } /* diff --git a/src/backend/utils/cache/syscache.c b/src/backend/utils/cache/syscache.c index 75f290dda61..2d52b4299b7 100644 --- a/src/backend/utils/cache/syscache.c +++ b/src/backend/utils/cache/syscache.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.111 2007/02/14 01:58:57 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.112 2007/04/02 03:49:39 tgl Exp $ * * NOTES * These routines allow the parser/planner/executor to perform @@ -31,6 +31,7 @@ #include "catalog/pg_constraint.h" #include "catalog/pg_conversion.h" #include "catalog/pg_database.h" +#include "catalog/pg_enum.h" #include "catalog/pg_language.h" #include "catalog/pg_namespace.h" #include "catalog/pg_opclass.h" @@ -335,6 +336,30 @@ static const struct cachedesc cacheinfo[] = { }, 4 }, + {EnumRelationId, /* ENUMOID */ + EnumOidIndexId, + 0, + 1, + { + ObjectIdAttributeNumber, + 0, + 0, + 0 + }, + 256 + }, + {EnumRelationId, /* ENUMTYPOIDNAME */ + EnumTypIdLabelIndexId, + 0, + 2, + { + Anum_pg_enum_enumtypid, + Anum_pg_enum_enumlabel, + 0, + 0 + }, + 256 + }, {IndexRelationId, /* INDEXRELID */ IndexRelidIndexId, Anum_pg_index_indrelid, diff --git a/src/backend/utils/cache/typcache.c b/src/backend/utils/cache/typcache.c index 523e8ca35ab..a8a24099116 100644 --- a/src/backend/utils/cache/typcache.c +++ b/src/backend/utils/cache/typcache.c @@ -36,7 +36,7 @@ * Portions Copyright (c) 1994, Regents of the University of California * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.24 2007/01/05 22:19:43 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.25 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -275,7 +275,7 @@ lookup_type_cache(Oid type_id, int flags) */ if ((flags & TYPECACHE_TUPDESC) && typentry->tupDesc == NULL && - typentry->typtype == 'c') + typentry->typtype == TYPTYPE_COMPOSITE) { Relation rel; diff --git a/src/backend/utils/fmgr/funcapi.c b/src/backend/utils/fmgr/funcapi.c index 218b7646b75..c32130c3ec1 100644 --- a/src/backend/utils/fmgr/funcapi.c +++ b/src/backend/utils/fmgr/funcapi.c @@ -7,7 +7,7 @@ * Copyright (c) 2002-2007, PostgreSQL Global Development Group * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.33 2007/02/01 19:10:28 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.34 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -193,8 +193,8 @@ shutdown_MultiFuncCall(Datum arg) * 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. + * 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,7 +338,7 @@ internal_get_result_type(Oid funcid, /* * If scalar polymorphic result, try to resolve it. */ - if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID) + if (IsPolymorphicType(rettype)) { Oid newrettype = exprType(call_expr); @@ -389,8 +389,8 @@ internal_get_result_type(Oid funcid, /* * 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 + * 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 @@ -401,6 +401,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, int nargs = declared_args->dim1; bool have_anyelement_result = false; bool have_anyarray_result = false; + bool have_anyenum = false; Oid anyelement_type = InvalidOid; Oid anyarray_type = InvalidOid; int i; @@ -416,6 +417,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, case ANYARRAYOID: have_anyarray_result = true; break; + case ANYENUMOID: + have_anyelement_result = true; + have_anyenum = true; + break; default: break; } @@ -435,6 +440,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, switch (declared_args->values[i]) { case ANYELEMENTOID: + case ANYENUMOID: if (!OidIsValid(anyelement_type)) anyelement_type = get_call_expr_argtype(call_expr, i); break; @@ -461,12 +467,17 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, anyelement_type, ANYELEMENTOID); + /* Check for enum if needed */ + if (have_anyenum && !type_is_enum(anyelement_type)) + return false; + /* And finally replace the tuple column types as needed */ for (i = 0; i < natts; i++) { switch (tupdesc->attrs[i]->atttypid) { case ANYELEMENTOID: + case ANYENUMOID: TupleDescInitEntry(tupdesc, i + 1, NameStr(tupdesc->attrs[i]->attname), anyelement_type, @@ -490,8 +501,8 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args, /* * 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 + * 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,6 +528,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, switch (argtypes[i]) { case ANYELEMENTOID: + case ANYENUMOID: if (argmode == PROARGMODE_OUT) have_anyelement_result = true; else @@ -571,12 +583,15 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes, anyelement_type, ANYELEMENTOID); + /* XXX do we need to enforce ANYENUM here? I think not */ + /* And finally replace the output column types as needed */ for (i = 0; i < numargs; i++) { switch (argtypes[i]) { case ANYELEMENTOID: + case ANYENUMOID: argtypes[i] = anyelement_type; break; case ANYARRAYOID: @@ -603,12 +618,13 @@ get_type_func_class(Oid typid) { switch (get_typtype(typid)) { - case 'c': + case TYPTYPE_COMPOSITE: return TYPEFUNC_COMPOSITE; - case 'b': - case 'd': + case TYPTYPE_BASE: + case TYPTYPE_DOMAIN: + case TYPTYPE_ENUM: return TYPEFUNC_SCALAR; - case 'p': + case TYPTYPE_PSEUDO: if (typid == RECORDOID) return TYPEFUNC_RECORD; @@ -840,7 +856,7 @@ get_func_result_name(Oid functionId) * 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; + * Note that this does not handle resolution of polymorphic types; * that is deliberate. */ TupleDesc diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c index b0c9e70a46f..9f9790baed1 100644 --- a/src/bin/pg_dump/pg_dump.c +++ b/src/bin/pg_dump/pg_dump.c @@ -12,7 +12,7 @@ * by PostgreSQL * * IDENTIFICATION - * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.465 2007/03/26 16:58:39 tgl Exp $ + * $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.466 2007/04/02 03:49:39 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -137,6 +137,7 @@ static void dumpDumpableObject(Archive *fout, DumpableObject *dobj); 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); @@ -2085,7 +2086,7 @@ getTypes(int *numTypes) */ tinfo[i].nDomChecks = 0; tinfo[i].domChecks = NULL; - if (tinfo[i].dobj.dump && tinfo[i].typtype == 'd') + if (tinfo[i].dobj.dump && tinfo[i].typtype == TYPTYPE_DOMAIN) getDomainConstraints(&(tinfo[i])); /* @@ -2097,7 +2098,7 @@ getTypes(int *numTypes) * should copy the base type's catId, but then it might capture the * pg_depend entries for the type, which we don't want. */ - if (tinfo[i].dobj.dump && tinfo[i].typtype == 'b') + if (tinfo[i].dobj.dump && tinfo[i].typtype == TYPTYPE_BASE) { stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo)); stinfo->dobj.objType = DO_SHELL_TYPE; @@ -5119,12 +5120,91 @@ dumpType(Archive *fout, TypeInfo *tinfo) return; /* Dump out in proper style */ - if (tinfo->typtype == 'b') + if (tinfo->typtype == TYPTYPE_BASE) dumpBaseType(fout, tinfo); - else if (tinfo->typtype == 'd') + else if (tinfo->typtype == TYPTYPE_DOMAIN) dumpDomain(fout, tinfo); - else if (tinfo->typtype == 'c') + else if (tinfo->typtype == TYPTYPE_COMPOSITE) dumpCompositeType(fout, tinfo); + else if (tinfo->typtype == TYPTYPE_ENUM) + 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 *label; + + /* Set proper schema search path so regproc references list correctly */ + selectSourceSchema(tinfo->dobj.namespace->dobj.name); + + appendPQExpBuffer(query, "SELECT enumlabel 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++) + { + label = PQgetvalue(res, i, 0); + if (i > 0) + appendPQExpBuffer(q, ",\n"); + appendPQExpBuffer(q, " "); + appendStringLiteralAH(q, label, fout); + } + appendPQExpBuffer(q, "\n);\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 --git a/src/include/access/hash.h b/src/include/access/hash.h index baed9fdb4b7..7ba8fbfc2f2 100644 --- a/src/include/access/hash.h +++ b/src/include/access/hash.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/access/hash.h,v 1.76 2007/01/30 01:33:36 tgl Exp $ + * $PostgreSQL: pgsql/src/include/access/hash.h,v 1.77 2007/04/02 03:49:40 tgl Exp $ * * NOTES * modeled after Margo Seltzer's hash implementation for unix. @@ -258,6 +258,7 @@ extern Datum hashint2(PG_FUNCTION_ARGS); extern Datum hashint4(PG_FUNCTION_ARGS); extern Datum hashint8(PG_FUNCTION_ARGS); extern Datum hashoid(PG_FUNCTION_ARGS); +extern Datum hashenum(PG_FUNCTION_ARGS); extern Datum hashfloat4(PG_FUNCTION_ARGS); extern Datum hashfloat8(PG_FUNCTION_ARGS); extern Datum hashoidvector(PG_FUNCTION_ARGS); diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index d31dce2a0cf..9525a198fee 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.398 2007/04/01 09:56:02 petere Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.399 2007/04/02 03:49:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200704011 +#define CATALOG_VERSION_NO 200704012 #endif diff --git a/src/include/catalog/indexing.h b/src/include/catalog/indexing.h index baa650a8edb..71970d48f74 100644 --- a/src/include/catalog/indexing.h +++ b/src/include/catalog/indexing.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.98 2007/02/14 01:58:58 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.99 2007/04/02 03:49:40 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -146,6 +146,11 @@ DECLARE_UNIQUE_INDEX(pg_description_o_c_o_index, 2675, on pg_description using b 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, 3502, on pg_enum using btree(oid oid_ops)); +#define EnumOidIndexId 3502 +DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops)); +#define EnumTypIdLabelIndexId 3503 + /* 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 --git a/src/include/catalog/pg_aggregate.h b/src/include/catalog/pg_aggregate.h index 9036b1d4a6c..0ee798b356e 100644 --- a/src/include/catalog/pg_aggregate.h +++ b/src/include/catalog/pg_aggregate.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.61 2007/02/17 00:55:57 momjian Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.62 2007/04/02 03:49:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -118,6 +118,7 @@ DATA(insert ( 2130 numeric_larger - 1756 1700 _null_ )); DATA(insert ( 2050 array_larger - 1073 2277 _null_ )); DATA(insert ( 2244 bpchar_larger - 1060 1042 _null_ )); DATA(insert ( 2797 tidlarger - 2800 27 _null_ )); +DATA(insert ( 3526 enum_larger - 3519 3500 _null_ )); /* min */ DATA(insert ( 2131 int8smaller - 412 20 _null_ )); @@ -139,6 +140,7 @@ DATA(insert ( 2146 numeric_smaller - 1754 1700 _null_ )); DATA(insert ( 2051 array_smaller - 1072 2277 _null_ )); DATA(insert ( 2245 bpchar_smaller - 1058 1042 _null_ )); DATA(insert ( 2798 tidsmaller - 2799 27 _null_ )); +DATA(insert ( 3527 enum_smaller - 3518 3500 _null_ )); /* count */ DATA(insert ( 2147 int8inc_any - 0 20 "0" )); diff --git a/src/include/catalog/pg_amop.h b/src/include/catalog/pg_amop.h index c1023df6ea9..2b852645998 100644 --- a/src/include/catalog/pg_amop.h +++ b/src/include/catalog/pg_amop.h @@ -29,7 +29,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_amop.h,v 1.79 2007/02/06 02:59:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_amop.h,v 1.80 2007/04/02 03:49:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -636,4 +636,18 @@ DATA(insert ( 2745 2277 2277 2 f 2751 2742 )); DATA(insert ( 2745 2277 2277 3 t 2752 2742 )); DATA(insert ( 2745 2277 2277 4 t 1070 2742 )); +/* + * btree enum_ops + */ +DATA(insert ( 3522 3500 3500 1 f 3518 403 )); +DATA(insert ( 3522 3500 3500 2 f 3520 403 )); +DATA(insert ( 3522 3500 3500 3 f 3516 403 )); +DATA(insert ( 3522 3500 3500 4 f 3521 403 )); +DATA(insert ( 3522 3500 3500 5 f 3519 403 )); + +/* + * hash enum_ops + */ +DATA(insert ( 3523 3500 3500 1 f 3516 405 )); + #endif /* PG_AMOP_H */ diff --git a/src/include/catalog/pg_amproc.h b/src/include/catalog/pg_amproc.h index 64db6483417..d535e615c81 100644 --- a/src/include/catalog/pg_amproc.h +++ b/src/include/catalog/pg_amproc.h @@ -22,7 +22,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_amproc.h,v 1.63 2007/01/28 16:16:52 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_amproc.h,v 1.64 2007/04/02 03:49:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -128,6 +128,7 @@ DATA(insert ( 2233 703 703 1 380 )); DATA(insert ( 2234 704 704 1 381 )); DATA(insert ( 2789 27 27 1 2794 )); DATA(insert ( 2968 2950 2950 1 2960 )); +DATA(insert ( 3522 3500 3500 1 3514 )); /* hash */ @@ -162,6 +163,7 @@ DATA(insert ( 2231 1042 1042 1 456 )); DATA(insert ( 2232 19 19 1 455 )); DATA(insert ( 2235 1033 1033 1 329 )); DATA(insert ( 2969 2950 2950 1 2963 )); +DATA(insert ( 3523 3500 3500 1 3515 )); /* gist */ diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h index 0047f865af2..bc558b82e9e 100644 --- a/src/include/catalog/pg_cast.h +++ b/src/include/catalog/pg_cast.h @@ -10,7 +10,7 @@ * * Copyright (c) 2002-2007, PostgreSQL Global Development Group * - * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.31 2007/02/03 14:06:55 petere Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.32 2007/04/02 03:49:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -403,4 +403,10 @@ DATA(insert ( 2950 25 2965 a )); DATA(insert ( 1043 2950 2964 a )); DATA(insert ( 2950 1043 2965 a )); +/* + * enums + */ +DATA(insert ( 3500 25 3532 e )); +DATA(insert ( 25 3500 3533 e )); + #endif /* PG_CAST_H */ diff --git a/src/include/catalog/pg_enum.h b/src/include/catalog/pg_enum.h new file mode 100644 index 00000000000..1196e435a40 --- /dev/null +++ b/src/include/catalog/pg_enum.h @@ -0,0 +1,72 @@ +/*------------------------------------------------------------------------- + * + * pg_enum.h + * definition of the system "enum" relation (pg_enum) + * along with the relation's initial contents. + * + * + * Copyright (c) 2006-2007, PostgreSQL Global Development Group + * + * $PostgreSQL: pgsql/src/include/catalog/pg_enum.h,v 1.1 2007/04/02 03:49:40 tgl Exp $ + * + * NOTES + * the genbki.sh script reads this file and generates .bki + * information from the DATA() statements. + * + * XXX do NOT break up DATA() statements into multiple lines! + * the scripts are not as smart as you might think... + * + *------------------------------------------------------------------------- + */ +#ifndef PG_ENUM_H +#define PG_ENUM_H + +#include "nodes/pg_list.h" + +/* ---------------- + * postgres.h contains the system type definitions and the + * CATALOG(), BKI_BOOTSTRAP and DATA() sugar words so this file + * can be read by both genbki.sh and the C compiler. + * ---------------- + */ + +/* ---------------- + * pg_enum definition. cpp turns this into + * typedef struct FormData_pg_enum + * ---------------- + */ +#define EnumRelationId 3501 + +CATALOG(pg_enum,3501) +{ + Oid enumtypid; /* OID of owning enum type */ + NameData enumlabel; /* text representation of enum value */ +} FormData_pg_enum; + +/* ---------------- + * Form_pg_enum corresponds to a pointer to a tuple with + * the format of pg_enum relation. + * ---------------- + */ +typedef FormData_pg_enum *Form_pg_enum; + +/* ---------------- + * compiler constants for pg_enum + * ---------------- + */ +#define Natts_pg_enum 2 +#define Anum_pg_enum_enumtypid 1 +#define Anum_pg_enum_enumlabel 2 + +/* ---------------- + * pg_enum has no initial contents + * ---------------- + */ + +/* + * prototypes for functions in pg_enum.c + */ +extern void EnumValuesCreate(Oid enumTypeOid, List *vals); +extern void EnumValuesDelete(Oid enumTypeOid); + +#endif /* PG_ENUM_H */ diff --git a/src/include/catalog/pg_opclass.h b/src/include/catalog/pg_opclass.h index 148f02db2a9..2df9b8241a8 100644 --- a/src/include/catalog/pg_opclass.h +++ b/src/include/catalog/pg_opclass.h @@ -28,7 +28,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_opclass.h,v 1.74 2007/01/28 16:16:52 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_opclass.h,v 1.75 2007/04/02 03:49:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -200,7 +200,9 @@ DATA(insert ( 2742 _timestamp_ops PGNSP PGUID 2745 1115 t 1114 )); DATA(insert ( 2742 _money_ops PGNSP PGUID 2745 791 t 790 )); DATA(insert ( 2742 _reltime_ops PGNSP PGUID 2745 1024 t 703 )); DATA(insert ( 2742 _tinterval_ops PGNSP PGUID 2745 1025 t 704 )); -DATA(insert ( 403 uuid_ops PGNSP PGUID 2968 2950 t 0 )); -DATA(insert ( 405 uuid_ops PGNSP PGUID 2969 2950 t 0 )); +DATA(insert ( 403 uuid_ops PGNSP PGUID 2968 2950 t 0 )); +DATA(insert ( 405 uuid_ops PGNSP PGUID 2969 2950 t 0 )); +DATA(insert ( 403 enum_ops PGNSP PGUID 3522 3500 t 0 )); +DATA(insert ( 405 enum_ops PGNSP PGUID 3523 3500 t 0 )); #endif /* PG_OPCLASS_H */ diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index e53f74b5193..2591b1ff7b1 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.150 2007/02/06 02:59:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.151 2007/04/02 03:49:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -903,6 +903,14 @@ DATA(insert OID = 2975 ( ">" PGNSP PGUID b f f 2950 2950 16 2974 2976 uuid_g DATA(insert OID = 2976 ( "<=" PGNSP PGUID b f f 2950 2950 16 2977 2975 uuid_le scalarltsel scalarltjoinsel )); DATA(insert OID = 2977 ( ">=" PGNSP PGUID b f f 2950 2950 16 2976 2974 uuid_ge scalargtsel scalargtjoinsel )); +/* enum operators */ +DATA(insert OID = 3516 ( "=" PGNSP PGUID b t t 3500 3500 16 3516 3517 enum_eq eqsel eqjoinsel )); +DATA(insert OID = 3517 ( "<>" PGNSP PGUID b f f 3500 3500 16 3517 3516 enum_ne neqsel neqjoinsel )); +DATA(insert OID = 3518 ( "<" PGNSP PGUID b f f 3500 3500 16 3519 3521 enum_lt scalarltsel scalarltjoinsel )); +DATA(insert OID = 3519 ( ">" PGNSP PGUID b f f 3500 3500 16 3518 3520 enum_gt scalargtsel scalargtjoinsel )); +DATA(insert OID = 3520 ( "<=" PGNSP PGUID b f f 3500 3500 16 3521 3519 enum_le scalarltsel scalarltjoinsel )); +DATA(insert OID = 3521 ( ">=" PGNSP PGUID b f f 3500 3500 16 3520 3518 enum_ge scalargtsel scalargtjoinsel )); + /* * function prototypes diff --git a/src/include/catalog/pg_opfamily.h b/src/include/catalog/pg_opfamily.h index 63d1b277643..3d4c0a41d0a 100644 --- a/src/include/catalog/pg_opfamily.h +++ b/src/include/catalog/pg_opfamily.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_opfamily.h,v 1.3 2007/01/28 16:16:52 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_opfamily.h,v 1.4 2007/04/02 03:49:40 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -136,6 +136,8 @@ DATA(insert OID = 2595 ( 783 circle_ops PGNSP PGUID )); DATA(insert OID = 2745 ( 2742 array_ops PGNSP PGUID )); DATA(insert OID = 2968 ( 403 uuid_ops PGNSP PGUID )); DATA(insert OID = 2969 ( 405 uuid_ops PGNSP PGUID )); +DATA(insert OID = 3522 ( 403 enum_ops PGNSP PGUID )); +DATA(insert OID = 3523 ( 405 enum_ops PGNSP PGUID )); #endif /* PG_OPFAMILY_H */ diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index 252d7460ac7..d7b048b07f7 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.453 2007/04/01 09:00:25 petere Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.454 2007/04/02 03:49:40 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -4149,6 +4149,46 @@ DESCR("convert text to uuid"); DATA(insert OID = 2965 ( text PGNSP PGUID 12 1 0 f f t f i 1 25 "2950" _null_ _null_ _null_ uuid_text - _null_ )); DESCR("convert uuid to text"); +/* enum related procs */ +DATA(insert OID = 3504 ( anyenum_in PGNSP PGUID 12 1 0 f f t f i 1 3500 "2275" _null_ _null_ _null_ anyenum_in - _null_ )); +DESCR("I/O"); +DATA(insert OID = 3505 ( anyenum_out PGNSP PGUID 12 1 0 f f t f s 1 2275 "3500" _null_ _null_ _null_ anyenum_out - _null_ )); +DESCR("I/O"); +DATA(insert OID = 3506 ( enum_in PGNSP PGUID 12 1 0 f f t f s 2 3500 "2275 26" _null_ _null_ _null_ enum_in - _null_ )); +DESCR("I/O"); +DATA(insert OID = 3507 ( enum_out PGNSP PGUID 12 1 0 f f t f s 1 2275 "3500" _null_ _null_ _null_ enum_out - _null_ )); +DESCR("I/O"); +DATA(insert OID = 3508 ( enum_eq PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_eq - _null_ )); +DESCR("equal"); +DATA(insert OID = 3509 ( enum_ne PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_ne - _null_ )); +DESCR("not equal"); +DATA(insert OID = 3510 ( enum_lt PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_lt - _null_ )); +DESCR("less-than"); +DATA(insert OID = 3511 ( enum_gt PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_gt - _null_ )); +DESCR("greater-than"); +DATA(insert OID = 3512 ( enum_le PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_le - _null_ )); +DESCR("less-than-or-equal"); +DATA(insert OID = 3513 ( enum_ge PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_ge - _null_ )); +DESCR("greater-than-or-equal"); +DATA(insert OID = 3514 ( enum_cmp PGNSP PGUID 12 1 0 f f t f i 2 23 "3500 3500" _null_ _null_ _null_ enum_cmp - _null_ )); +DESCR("btree-less-equal-greater"); +DATA(insert OID = 3515 ( hashenum PGNSP PGUID 12 1 0 f f t f i 1 23 "3500" _null_ _null_ _null_ hashenum - _null_ )); +DESCR("hash"); +DATA(insert OID = 3524 ( enum_smaller PGNSP PGUID 12 1 0 f f t f i 2 3500 "3500 3500" _null_ _null_ _null_ enum_smaller - _null_ )); +DESCR("smaller of two"); +DATA(insert OID = 3525 ( enum_larger PGNSP PGUID 12 1 0 f f t f i 2 3500 "3500 3500" _null_ _null_ _null_ enum_larger - _null_ )); +DESCR("larger of two"); +DATA(insert OID = 3526 ( max PGNSP PGUID 12 1 0 t f f f i 1 3500 "3500" _null_ _null_ _null_ aggregate_dummy - _null_ )); +DATA(insert OID = 3527 ( min PGNSP PGUID 12 1 0 t f f f i 1 3500 "3500" _null_ _null_ _null_ aggregate_dummy - _null_ )); +DATA(insert OID = 3528 ( enum_first PGNSP PGUID 12 1 0 f f f f s 1 3500 "3500" _null_ _null_ _null_ enum_first - _null_ )); +DATA(insert OID = 3529 ( enum_last PGNSP PGUID 12 1 0 f f f f s 1 3500 "3500" _null_ _null_ _null_ enum_last - _null_ )); +DATA(insert OID = 3530 ( enum_range PGNSP PGUID 12 1 0 f f f f s 2 2277 "3500 3500" _null_ _null_ _null_ enum_range_bounds - _null_ )); +DATA(insert OID = 3531 ( enum_range PGNSP PGUID 12 1 0 f f f f s 1 2277 "3500" _null_ _null_ _null_ enum_range_all - _null_ )); +DATA(insert OID = 3532 ( text PGNSP PGUID 12 1 0 f f t f s 1 25 "3500" _null_ _null_ _null_ enum_text - _null_ )); +DESCR("convert enum to text"); +DATA(insert OID = 3533 ( enum PGNSP PGUID 12 1 0 f f t f s 1 3500 "25" _null_ _null_ _null_ text_enum - _null_ )); +DESCR("convert text to enum"); + /* * Symbolic values for provolatile column: these indicate whether the result * of a function is dependent *only* on the values of its explicit arguments, diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h index 89849c25fcb..69713196000 100644 --- a/src/include/catalog/pg_type.h +++ b/src/include/catalog/pg_type.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.180 2007/01/28 16:16:54 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.181 2007/04/02 03:49:41 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -66,8 +66,9 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP bool typbyval; /* - * 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. + * typtype is 'b' for a base type, 'c' for a composite type (e.g., + * a table's rowtype), 'd' for a domain type, 'e' for an enum type, + * or 'p' for a pseudo-type. (Use the TYPTYPE macros below.) * * If typtype is 'c', typrelid is the OID of the class' entry in pg_class. */ @@ -531,6 +532,11 @@ DATA(insert OID = 2210 ( _regclass PGNSP PGUID -1 f b t \054 0 2205 array_in DATA(insert OID = 2211 ( _regtype PGNSP PGUID -1 f b t \054 0 2206 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); #define REGTYPEARRAYOID 2211 +/* uuid */ +DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b t \054 0 0 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ )); +DESCR("UUID datatype"); +DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b t \054 0 2950 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); + /* * pseudo-types * @@ -560,11 +566,24 @@ DATA(insert OID = 2282 ( opaque PGNSP PGUID 4 t p t \054 0 0 opaque_in opaque #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 = 3500 ( anyenum PGNSP PGUID 4 t p t \054 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ )); +#define ANYENUMOID 3500 -/* uuid */ -DATA(insert OID = 2950 ( uuid PGNSP PGUID 16 f b t \054 0 0 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ )); -DESCR("UUID datatype"); -DATA(insert OID = 2951 ( _uuid PGNSP PGUID -1 f b t \054 0 2950 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ )); + +/* + * macros + */ +#define TYPTYPE_BASE 'b' /* base type (ordinary scalar type) */ +#define TYPTYPE_COMPOSITE 'c' /* composite (e.g., table's rowtype) */ +#define TYPTYPE_DOMAIN 'd' /* domain over another type */ +#define TYPTYPE_ENUM 'e' /* enumerated type */ +#define TYPTYPE_PSEUDO 'p' /* pseudo-type */ + +/* Is a type OID a polymorphic pseudotype? (Beware of multiple evaluation) */ +#define IsPolymorphicType(typid) \ + ((typid) == ANYELEMENTOID || \ + (typid) == ANYARRAYOID || \ + (typid) == ANYENUMOID) /* * prototypes for functions in pg_type.c diff --git a/src/include/commands/typecmds.h b/src/include/commands/typecmds.h index 97196a062e2..91e56e12cb0 100644 --- a/src/include/commands/typecmds.h +++ b/src/include/commands/typecmds.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.17 2007/01/05 22:19:54 momjian Exp $ + * $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.18 2007/04/02 03:49:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -24,6 +24,7 @@ extern void RemoveType(List *names, DropBehavior behavior, bool missing_ok); 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 --git a/src/include/nodes/nodes.h b/src/include/nodes/nodes.h index f344e3f5d10..305be462e10 100644 --- a/src/include/nodes/nodes.h +++ b/src/include/nodes/nodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.197 2007/03/27 23:21:12 tgl Exp $ + * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.198 2007/04/02 03:49:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -307,6 +307,7 @@ typedef enum NodeTag T_DropOwnedStmt, T_ReassignOwnedStmt, T_CompositeTypeStmt, + T_CreateEnumStmt, /* * TAGS FOR PARSE TREE NODES (parsenodes.h) diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h index c3a2bebca68..8552620be3c 100644 --- a/src/include/nodes/parsenodes.h +++ b/src/include/nodes/parsenodes.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.343 2007/03/19 23:38:32 wieck Exp $ + * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.344 2007/04/02 03:49:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -1706,6 +1706,17 @@ typedef struct CompositeTypeStmt List *coldeflist; /* list of ColumnDef nodes */ } CompositeTypeStmt; +/* ---------------------- + * Create Type Statement, enum types + * ---------------------- + */ +typedef struct CreateEnumStmt +{ + NodeTag type; + List *typename; /* qualified name (list of Value strings) */ + List *vals; /* enum values (list of Value strings) */ +} CreateEnumStmt; + /* ---------------------- * Create View Statement diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h index 3fff9ba33d2..3d07b085793 100644 --- a/src/include/parser/parse_type.h +++ b/src/include/parser/parse_type.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/parser/parse_type.h,v 1.35 2007/01/05 22:19:57 momjian Exp $ + * $PostgreSQL: pgsql/src/include/parser/parse_type.h,v 1.36 2007/04/02 03:49:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -33,7 +33,6 @@ extern Type typeidType(Oid id); extern Oid typeTypeId(Type tp); extern int16 typeLen(Type t); extern bool typeByVal(Type t); -extern char typeTypType(Type t); extern char *typeTypeName(Type t); extern Oid typeTypeRelid(Type typ); extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index b2a55bbbb98..d93599a9ae5 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.290 2007/03/20 05:45:00 neilc Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.291 2007/04/02 03:49:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -103,6 +103,25 @@ extern Datum char_text(PG_FUNCTION_ARGS); extern Datum domain_in(PG_FUNCTION_ARGS); extern Datum domain_recv(PG_FUNCTION_ARGS); +/* enum.c */ +extern Datum enum_in(PG_FUNCTION_ARGS); +extern Datum enum_out(PG_FUNCTION_ARGS); +extern Datum enum_lt(PG_FUNCTION_ARGS); +extern Datum enum_le(PG_FUNCTION_ARGS); +extern Datum enum_eq(PG_FUNCTION_ARGS); +extern Datum enum_ne(PG_FUNCTION_ARGS); +extern Datum enum_ge(PG_FUNCTION_ARGS); +extern Datum enum_gt(PG_FUNCTION_ARGS); +extern Datum enum_cmp(PG_FUNCTION_ARGS); +extern Datum enum_text(PG_FUNCTION_ARGS); +extern Datum text_enum(PG_FUNCTION_ARGS); +extern Datum enum_smaller(PG_FUNCTION_ARGS); +extern Datum enum_larger(PG_FUNCTION_ARGS); +extern Datum enum_first(PG_FUNCTION_ARGS); +extern Datum enum_last(PG_FUNCTION_ARGS); +extern Datum enum_range_bounds(PG_FUNCTION_ARGS); +extern Datum enum_range_all(PG_FUNCTION_ARGS); + /* int.c */ extern Datum int2in(PG_FUNCTION_ARGS); extern Datum int2out(PG_FUNCTION_ARGS); @@ -450,6 +469,8 @@ extern Datum anyarray_in(PG_FUNCTION_ARGS); extern Datum anyarray_out(PG_FUNCTION_ARGS); extern Datum anyarray_recv(PG_FUNCTION_ARGS); extern Datum anyarray_send(PG_FUNCTION_ARGS); +extern Datum anyenum_in(PG_FUNCTION_ARGS); +extern Datum anyenum_out(PG_FUNCTION_ARGS); extern Datum void_in(PG_FUNCTION_ARGS); extern Datum void_out(PG_FUNCTION_ARGS); extern Datum trigger_in(PG_FUNCTION_ARGS); diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h index 78c45891fa4..25782e322e9 100644 --- a/src/include/utils/lsyscache.h +++ b/src/include/utils/lsyscache.h @@ -6,7 +6,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.117 2007/02/14 01:58:58 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.118 2007/04/02 03:49:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -105,6 +105,7 @@ extern char get_typstorage(Oid typid); extern Node *get_typdefault(Oid typid); extern char get_typtype(Oid typid); extern bool type_is_rowtype(Oid typid); +extern bool type_is_enum(Oid typid); extern Oid get_typ_typrelid(Oid typid); extern Oid get_element_type(Oid typid); extern Oid get_array_type(Oid typid); diff --git a/src/include/utils/syscache.h b/src/include/utils/syscache.h index c6967251ce1..12cd9a72f22 100644 --- a/src/include/utils/syscache.h +++ b/src/include/utils/syscache.h @@ -9,7 +9,7 @@ * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.68 2007/02/14 01:58:58 tgl Exp $ + * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.69 2007/04/02 03:49:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -48,23 +48,25 @@ #define CONSTROID 17 #define CONVOID 18 #define DATABASEOID 19 -#define INDEXRELID 20 -#define LANGNAME 21 -#define LANGOID 22 -#define NAMESPACENAME 23 -#define NAMESPACEOID 24 -#define OPERNAMENSP 25 -#define OPEROID 26 -#define OPFAMILYAMNAMENSP 27 -#define OPFAMILYOID 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 +#define ENUMOID 20 +#define ENUMTYPOIDNAME 21 +#define INDEXRELID 22 +#define LANGNAME 23 +#define LANGOID 24 +#define NAMESPACENAME 25 +#define NAMESPACEOID 26 +#define OPERNAMENSP 27 +#define OPEROID 28 +#define OPFAMILYAMNAMENSP 29 +#define OPFAMILYOID 30 +#define PROCNAMEARGSNSP 31 +#define PROCOID 32 +#define RELNAMENSP 33 +#define RELOID 34 +#define RULERELNAME 35 +#define STATRELATT 36 +#define TYPENAMENSP 37 +#define TYPEOID 38 extern void InitCatalogCache(void); extern void InitCatalogCachePhase2(void); diff --git a/src/pl/plperl/plperl.c b/src/pl/plperl/plperl.c index 33027c53bde..ff484f4dbe0 100644 --- a/src/pl/plperl/plperl.c +++ b/src/pl/plperl/plperl.c @@ -1,7 +1,7 @@ /********************************************************************** * plperl.c - perl as a procedural language for PostgreSQL * - * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.127 2007/02/09 03:35:34 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.128 2007/04/02 03:49:41 tgl Exp $ * **********************************************************************/ @@ -843,7 +843,7 @@ plperl_validator(PG_FUNCTION_ARGS) /* Disallow pseudotype result */ /* except for TRIGGER, RECORD, or VOID */ - if (functyptype == 'p') + if (functyptype == TYPTYPE_PSEUDO) { /* we assume OPAQUE with no arguments means a trigger */ if (proc->prorettype == TRIGGEROID || @@ -862,7 +862,7 @@ plperl_validator(PG_FUNCTION_ARGS) &argtypes, &argnames, &argmodes); for (i = 0; i < numargs; i++) { - if (get_typtype(argtypes[i]) == 'p') + if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plperl functions cannot take type %s", @@ -1525,7 +1525,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype result, except VOID or RECORD */ - if (typeStruct->typtype == 'p') + if (typeStruct->typtype == TYPTYPE_PSEUDO) { if (procStruct->prorettype == VOIDOID || procStruct->prorettype == RECORDOID) @@ -1552,8 +1552,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) prodesc->result_oid = procStruct->prorettype; prodesc->fn_retisset = procStruct->proretset; - prodesc->fn_retistuple = (typeStruct->typtype == 'c' || - procStruct->prorettype == RECORDOID); + prodesc->fn_retistuple = (procStruct->prorettype == RECORDOID || + typeStruct->typtype == TYPTYPE_COMPOSITE); prodesc->fn_retisarray = (typeStruct->typlen == -1 && typeStruct->typelem); @@ -1586,7 +1586,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype argument */ - if (typeStruct->typtype == 'p') + if (typeStruct->typtype == TYPTYPE_PSEUDO) { free(prodesc->proname); free(prodesc); @@ -1596,7 +1596,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger) format_type_be(procStruct->proargtypes.values[i])))); } - if (typeStruct->typtype == 'c') + if (typeStruct->typtype == TYPTYPE_COMPOSITE) prodesc->arg_is_rowtype[i] = true; else { diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c index 44fae0f1b28..74918c890ca 100644 --- a/src/pl/plpgsql/src/pl_comp.c +++ b/src/pl/plpgsql/src/pl_comp.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.113 2007/02/09 03:35:34 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.114 2007/04/02 03:49:41 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -406,7 +406,7 @@ do_compile(FunctionCallInfo fcinfo, argdtype = plpgsql_build_datatype(argtypeid, -1); /* Disallow pseudotype argument */ - /* (note we already replaced ANYARRAY/ANYELEMENT) */ + /* (note we already replaced polymorphic types) */ /* (build_variable would do this, but wrong message) */ if (argdtype->ttype != PLPGSQL_TTYPE_SCALAR && argdtype->ttype != PLPGSQL_TTYPE_ROW) @@ -474,7 +474,7 @@ do_compile(FunctionCallInfo fcinfo, * the info available. */ rettypeid = procStruct->prorettype; - if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID) + if (IsPolymorphicType(rettypeid)) { if (forValidator) { @@ -482,6 +482,7 @@ do_compile(FunctionCallInfo fcinfo, rettypeid = INT4ARRAYOID; else rettypeid = INT4OID; + /* XXX what could we use for ANYENUM? */ } else { @@ -512,8 +513,8 @@ do_compile(FunctionCallInfo fcinfo, typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype result, except VOID or RECORD */ - /* (note we already replaced ANYARRAY/ANYELEMENT) */ - if (typeStruct->typtype == 'p') + /* (note we already replaced polymorphic types) */ + if (typeStruct->typtype == TYPTYPE_PSEUDO) { if (rettypeid == VOIDOID || rettypeid == RECORDOID) @@ -544,8 +545,7 @@ do_compile(FunctionCallInfo fcinfo, * types, and not when the return is specified through an * output parameter. */ - if ((procStruct->prorettype == ANYARRAYOID || - procStruct->prorettype == ANYELEMENTOID) && + if (IsPolymorphicType(procStruct->prorettype) && num_out_args == 0) { (void) plpgsql_build_variable("$0", 0, @@ -1785,15 +1785,16 @@ build_datatype(HeapTuple typeTup, int32 typmod) typ->typoid = HeapTupleGetOid(typeTup); switch (typeStruct->typtype) { - case 'b': /* base type */ - case 'd': /* domain */ + case TYPTYPE_BASE: + case TYPTYPE_DOMAIN: + case TYPTYPE_ENUM: typ->ttype = PLPGSQL_TTYPE_SCALAR; break; - case 'c': /* composite, ie, rowtype */ + case TYPTYPE_COMPOSITE: Assert(OidIsValid(typeStruct->typrelid)); typ->ttype = PLPGSQL_TTYPE_ROW; break; - case 'p': /* pseudo */ + case TYPTYPE_PSEUDO: if (typ->typoid == RECORDOID) typ->ttype = PLPGSQL_TTYPE_REC; else @@ -2026,6 +2027,7 @@ plpgsql_resolve_polymorphic_argtypes(int numargs, switch (argtypes[i]) { case ANYELEMENTOID: + case ANYENUMOID: /* XXX dubious */ argtypes[i] = INT4OID; break; case ANYARRAYOID: diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c index 41d2b3e5448..0654a044b3e 100644 --- a/src/pl/plpgsql/src/pl_exec.c +++ b/src/pl/plpgsql/src/pl_exec.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.192 2007/03/27 23:21:12 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.193 2007/04/02 03:49:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -3294,8 +3294,7 @@ exec_assign_value(PLpgSQL_execstate *estate, PLpgSQL_row *row = (PLpgSQL_row *) target; /* Source must be of RECORD or composite type */ - if (!(valtype == RECORDOID || - get_typtype(valtype) == 'c')) + if (!type_is_rowtype(valtype)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot assign non-composite value to a row variable"))); @@ -3337,8 +3336,7 @@ exec_assign_value(PLpgSQL_execstate *estate, PLpgSQL_rec *rec = (PLpgSQL_rec *) target; /* Source must be of RECORD or composite type */ - if (!(valtype == RECORDOID || - get_typtype(valtype) == 'c')) + if (!type_is_rowtype(valtype)) ereport(ERROR, (errcode(ERRCODE_DATATYPE_MISMATCH), errmsg("cannot assign non-composite value to a record variable"))); diff --git a/src/pl/plpgsql/src/pl_handler.c b/src/pl/plpgsql/src/pl_handler.c index 9b02160da4d..f4d27dc8d8f 100644 --- a/src/pl/plpgsql/src/pl_handler.c +++ b/src/pl/plpgsql/src/pl_handler.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.36 2007/01/30 22:05:13 tgl Exp $ + * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.37 2007/04/02 03:49:42 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -147,8 +147,8 @@ plpgsql_validator(PG_FUNCTION_ARGS) functyptype = get_typtype(proc->prorettype); /* Disallow pseudotype result */ - /* except for TRIGGER, RECORD, VOID, ANYARRAY, or ANYELEMENT */ - if (functyptype == 'p') + /* except for TRIGGER, RECORD, VOID, or polymorphic */ + if (functyptype == TYPTYPE_PSEUDO) { /* we assume OPAQUE with no arguments means a trigger */ if (proc->prorettype == TRIGGEROID || @@ -156,8 +156,7 @@ plpgsql_validator(PG_FUNCTION_ARGS) istrigger = true; else if (proc->prorettype != RECORDOID && proc->prorettype != VOIDOID && - proc->prorettype != ANYARRAYOID && - proc->prorettype != ANYELEMENTOID) + !IsPolymorphicType(proc->prorettype)) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpgsql functions cannot return type %s", @@ -165,15 +164,14 @@ plpgsql_validator(PG_FUNCTION_ARGS) } /* Disallow pseudotypes in arguments (either IN or OUT) */ - /* except for ANYARRAY or ANYELEMENT */ + /* except for polymorphic */ numargs = get_func_arg_info(tuple, &argtypes, &argnames, &argmodes); for (i = 0; i < numargs; i++) { - if (get_typtype(argtypes[i]) == 'p') + if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO) { - if (argtypes[i] != ANYARRAYOID && - argtypes[i] != ANYELEMENTOID) + if (!IsPolymorphicType(argtypes[i])) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpgsql functions cannot take type %s", diff --git a/src/pl/plpython/plpython.c b/src/pl/plpython/plpython.c index 78c9f6c66e0..31ce60373f6 100644 --- a/src/pl/plpython/plpython.c +++ b/src/pl/plpython/plpython.c @@ -1,7 +1,7 @@ /********************************************************************** * plpython.c - python as a procedural language for PostgreSQL * - * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.96 2007/02/21 03:27:32 adunstan Exp $ + * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.97 2007/04/02 03:49:42 tgl Exp $ * ********************************************************************* */ @@ -1185,7 +1185,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid, rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup); /* Disallow pseudotype result, except for void */ - if (rvTypeStruct->typtype == 'p' && + if (rvTypeStruct->typtype == TYPTYPE_PSEUDO && procStruct->prorettype != VOIDOID) { if (procStruct->prorettype == TRIGGEROID) @@ -1199,7 +1199,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid, format_type_be(procStruct->prorettype)))); } - if (rvTypeStruct->typtype == 'c') + if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE) { /* * Tuple: set up later, during first call to @@ -1258,13 +1258,13 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid, argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup); /* Disallow pseudotype argument */ - if (argTypeStruct->typtype == 'p') + if (argTypeStruct->typtype == TYPTYPE_PSEUDO) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("plpython functions cannot take type %s", format_type_be(procStruct->proargtypes.values[i])))); - if (argTypeStruct->typtype != 'c') + if (argTypeStruct->typtype != TYPTYPE_COMPOSITE) PLy_input_datum_func(&(proc->args[i]), procStruct->proargtypes.values[i], argTypeTup); @@ -2338,7 +2338,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args) plan->types[i] = typeId; typeStruct = (Form_pg_type) GETSTRUCT(typeTup); - if (typeStruct->typtype != 'c') + if (typeStruct->typtype != TYPTYPE_COMPOSITE) PLy_output_datum_func(&plan->args[i], typeTup); else elog(ERROR, "tuples not handled in plpy.prepare, yet."); diff --git a/src/pl/tcl/pltcl.c b/src/pl/tcl/pltcl.c index 2cd26d06412..093d990af33 100644 --- a/src/pl/tcl/pltcl.c +++ b/src/pl/tcl/pltcl.c @@ -2,7 +2,7 @@ * pltcl.c - PostgreSQL support for Tcl as * procedural language (PL) * - * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.111 2007/02/21 03:27:32 adunstan Exp $ + * $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.112 2007/04/02 03:49:42 tgl Exp $ * **********************************************************************/ @@ -1051,7 +1051,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid) typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype result, except VOID */ - if (typeStruct->typtype == 'p') + if (typeStruct->typtype == TYPTYPE_PSEUDO) { if (procStruct->prorettype == VOIDOID) /* okay */ ; @@ -1074,7 +1074,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid) } } - if (typeStruct->typtype == 'c') + if (typeStruct->typtype == TYPTYPE_COMPOSITE) { free(prodesc->proname); free(prodesc); @@ -1112,7 +1112,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid) typeStruct = (Form_pg_type) GETSTRUCT(typeTup); /* Disallow pseudotype argument */ - if (typeStruct->typtype == 'p') + if (typeStruct->typtype == TYPTYPE_PSEUDO) { free(prodesc->proname); free(prodesc); @@ -1122,7 +1122,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid) format_type_be(procStruct->proargtypes.values[i])))); } - if (typeStruct->typtype == 'c') + if (typeStruct->typtype == TYPTYPE_COMPOSITE) { prodesc->arg_is_rowtype[i] = true; snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1); diff --git a/src/test/regress/expected/enum.out b/src/test/regress/expected/enum.out new file mode 100644 index 00000000000..4fa2d07adaa --- /dev/null +++ b/src/test/regress/expected/enum.out @@ -0,0 +1,407 @@ +-- +-- Enum tests +-- +CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); +-- +-- Did it create the right number of rows? +-- +SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype; + count +------- + 6 +(1 row) + +-- +-- I/O functions +-- +SELECT 'red'::rainbow; + rainbow +--------- + red +(1 row) + +SELECT 'mauve'::rainbow; +ERROR: invalid input value for enum rainbow: "mauve" +-- +-- Basic table creation, row selection +-- +CREATE TABLE enumtest (col rainbow); +INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green'); +COPY enumtest FROM stdin; +SELECT * FROM enumtest; + col +-------- + red + orange + yellow + green + blue + purple +(6 rows) + +-- +-- Operators, no index +-- +SELECT * FROM enumtest WHERE col = 'orange'; + col +-------- + orange +(1 row) + +SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; + col +-------- + red + yellow + green + blue + purple +(5 rows) + +SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; + col +-------- + green + blue + purple +(3 rows) + +SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; + col +-------- + yellow + green + blue + purple +(4 rows) + +SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; + col +-------- + red + orange + yellow +(3 rows) + +SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; + col +-------- + red + orange + yellow + green +(4 rows) + +-- +-- Cast to/from text +-- +SELECT 'red'::rainbow::text || 'hithere'; + ?column? +------------ + redhithere +(1 row) + +SELECT 'red'::text::rainbow = 'red'::rainbow; + ?column? +---------- + t +(1 row) + +-- +-- Aggregates +-- +SELECT min(col) FROM enumtest; + min +----- + red +(1 row) + +SELECT max(col) FROM enumtest; + max +-------- + purple +(1 row) + +SELECT max(col) FROM enumtest WHERE col < 'green'; + max +-------- + yellow +(1 row) + +-- +-- Index tests, force use of index +-- +SET enable_seqscan = off; +SET enable_bitmapscan = off; +-- +-- Btree index / opclass with the various operators +-- +CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col); +SELECT * FROM enumtest WHERE col = 'orange'; + col +-------- + orange +(1 row) + +SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; + col +-------- + red + yellow + green + blue + purple +(5 rows) + +SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; + col +-------- + green + blue + purple +(3 rows) + +SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; + col +-------- + yellow + green + blue + purple +(4 rows) + +SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; + col +-------- + red + orange + yellow +(3 rows) + +SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; + col +-------- + red + orange + yellow + green +(4 rows) + +SELECT min(col) FROM enumtest; + min +----- + red +(1 row) + +SELECT max(col) FROM enumtest; + max +-------- + purple +(1 row) + +SELECT max(col) FROM enumtest WHERE col < 'green'; + max +-------- + yellow +(1 row) + +DROP INDEX enumtest_btree; +-- +-- Hash index / opclass with the = operator +-- +CREATE INDEX enumtest_hash ON enumtest USING hash (col); +SELECT * FROM enumtest WHERE col = 'orange'; + col +-------- + orange +(1 row) + +DROP INDEX enumtest_hash; +-- +-- End index tests +-- +RESET enable_seqscan; +RESET enable_bitmapscan; +-- +-- Domains over enums +-- +CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue')); +SELECT 'red'::rgb; + rgb +----- + red +(1 row) + +SELECT 'purple'::rgb; +ERROR: value for domain rgb violates check constraint "rgb_check" +SELECT 'purple'::rainbow::rgb; +ERROR: value for domain rgb violates check constraint "rgb_check" +DROP DOMAIN rgb; +-- +-- Arrays +-- +SELECT '{red,green,blue}'::rainbow[]; + rainbow +------------------ + {red,green,blue} +(1 row) + +SELECT ('{red,green,blue}'::rainbow[])[2]; + rainbow +--------- + green +(1 row) + +SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]); + ?column? +---------- + t +(1 row) + +SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]); + ?column? +---------- + f +(1 row) + +SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]); + ?column? +---------- + f +(1 row) + +SELECT 'red' = ALL ('{red,red}'::rainbow[]); + ?column? +---------- + t +(1 row) + +-- +-- Support functions +-- +SELECT enum_first(NULL::rainbow); + enum_first +------------ + red +(1 row) + +SELECT enum_last('green'::rainbow); + enum_last +----------- + purple +(1 row) + +SELECT enum_range(NULL::rainbow); + enum_range +--------------------------------------- + {red,orange,yellow,green,blue,purple} +(1 row) + +SELECT enum_range('orange'::rainbow, 'green'::rainbow); + enum_range +----------------------- + {orange,yellow,green} +(1 row) + +SELECT enum_range(NULL, 'green'::rainbow); + enum_range +--------------------------- + {red,orange,yellow,green} +(1 row) + +SELECT enum_range('orange'::rainbow, NULL); + enum_range +----------------------------------- + {orange,yellow,green,blue,purple} +(1 row) + +SELECT enum_range(NULL::rainbow, NULL); + enum_range +--------------------------------------- + {red,orange,yellow,green,blue,purple} +(1 row) + +-- +-- User functions, can't test perl/python etc here since may not be compiled. +-- +CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$ +BEGIN +RETURN $1::text || 'omg'; +END +$$ LANGUAGE plpgsql; +SELECT echo_me('red'::rainbow); + echo_me +--------- + redomg +(1 row) + +-- +-- Concrete function should override generic one +-- +CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$ +BEGIN +RETURN $1::text || 'wtf'; +END +$$ LANGUAGE plpgsql; +SELECT echo_me('red'::rainbow); + echo_me +--------- + redwtf +(1 row) + +-- +-- If we drop the original generic one, we don't have to qualify the type +-- anymore, since there's only one match +-- +DROP FUNCTION echo_me(anyenum); +SELECT echo_me('red'); + echo_me +--------- + redwtf +(1 row) + +DROP FUNCTION echo_me(rainbow); +-- +-- RI triggers on enum types +-- +CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY); +NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "enumtest_parent_pkey" for table "enumtest_parent" +CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent); +INSERT INTO enumtest_parent VALUES ('red'); +INSERT INTO enumtest_child VALUES ('red'); +INSERT INTO enumtest_child VALUES ('blue'); -- fail +ERROR: insert or update on table "enumtest_child" violates foreign key constraint "enumtest_child_parent_fkey" +DETAIL: Key (parent)=(blue) is not present in table "enumtest_parent". +DELETE FROM enumtest_parent; -- fail +ERROR: update or delete on table "enumtest_parent" violates foreign key constraint "enumtest_child_parent_fkey" on table "enumtest_child" +DETAIL: Key (id)=(red) is still referenced from table "enumtest_child". +-- +-- cross-type RI should fail +-- +CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly'); +CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent); +ERROR: foreign key constraint "enumtest_bogus_child_parent_fkey" cannot be implemented +DETAIL: Key columns "parent" and "id" are of incompatible types: bogus and rainbow. +DROP TYPE bogus; +-- +-- Cleanup +-- +DROP TABLE enumtest_child; +DROP TABLE enumtest_parent; +DROP TABLE enumtest; +DROP TYPE rainbow; +-- +-- Verify properly cleaned up +-- +SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow'; + count +------- + 0 +(1 row) + +SELECT * FROM pg_enum WHERE NOT EXISTS + (SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid); + enumtypid | enumlabel +-----------+----------- +(0 rows) + diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out index 841d77c78e5..29a0a190d61 100644 --- a/src/test/regress/expected/polymorphism.out +++ b/src/test/regress/expected/polymorphism.out @@ -80,7 +80,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[], CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- N P -- should CREATE CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[], @@ -92,11 +92,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[], CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- Case2 (R = P) && ((B = P) || (B = N)) -- ------------------------------------- -- S tf1 B tf2 @@ -151,13 +151,13 @@ ERROR: function tfp(integer[], anyelement) does not exist CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- P N N P -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- P N P N -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int) CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp, @@ -173,21 +173,21 @@ ERROR: function tf2p(anyarray, anyelement) does not exist CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- P P N P -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement) CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- P P P N -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int) CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p, @@ -217,11 +217,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[], CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- N P -- should CREATE CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[], @@ -231,7 +231,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[], CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- Case4 (R = N) && ((B = P) || (B = N)) -- ------------------------------------- -- S tf1 B tf2 @@ -285,21 +285,21 @@ ERROR: function tfp(integer[], anyelement) does not exist CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- P N N P -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement) CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- P N P N -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int) CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp, @@ -321,13 +321,13 @@ ERROR: function tf2p(anyarray, anyelement) does not exist CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- P P N P -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement) CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray, FINALFUNC = ffnp, INITCOND = '{}'); ERROR: cannot determine transition data type -DETAIL: An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type. +DETAIL: An aggregate using a polymorphic transition type must have at least one polymorphic argument. -- P P P N -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int) CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p, diff --git a/src/test/regress/expected/rangefuncs.out b/src/test/regress/expected/rangefuncs.out index 8abc323a70e..957bd49f246 100644 --- a/src/test/regress/expected/rangefuncs.out +++ b/src/test/regress/expected/rangefuncs.out @@ -500,7 +500,7 @@ SELECT dup(22); (1 row) SELECT dup('xyz'); -- fails -ERROR: could not determine anyarray/anyelement type because input has type "unknown" +ERROR: could not determine polymorphic type because input has type "unknown" SELECT dup('xyz'::text); dup ------------------- @@ -527,4 +527,4 @@ DROP FUNCTION dup(anyelement); CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray) AS 'select $1, array[$1,$1]' LANGUAGE sql; ERROR: cannot determine result data type -DETAIL: A function returning "anyarray" or "anyelement" must have at least one argument of either type. +DETAIL: A function returning a polymorphic type must have at least one polymorphic argument. diff --git a/src/test/regress/expected/sanity_check.out b/src/test/regress/expected/sanity_check.out index 5e6a570e244..6a31d52bcfc 100644 --- a/src/test/regress/expected/sanity_check.out +++ b/src/test/regress/expected/sanity_check.out @@ -97,6 +97,7 @@ SELECT relname, relhasindex pg_database | t pg_depend | t pg_description | t + pg_enum | t pg_index | t pg_inherits | t pg_language | t @@ -141,7 +142,7 @@ SELECT relname, relhasindex timetz_tbl | f tinterval_tbl | f varchar_tbl | f -(130 rows) +(131 rows) -- -- another sanity check: every system catalog that has OIDs should have diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out index 11c9298e9ae..b2db870da4f 100644 --- a/src/test/regress/expected/type_sanity.out +++ b/src/test/regress/expected/type_sanity.out @@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname 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 + (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm')); @@ -55,11 +55,11 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR -----+--------- (0 rows) --- Look for basic types that don't have an array type. +-- Look for basic or enum types that don't have an array type. -- NOTE: as of 8.0, this check finds smgr and unknown. SELECT p1.oid, p1.typname FROM pg_type as p1 -WHERE p1.typtype in ('b') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS +WHERE p1.typtype in ('b','e') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid); diff --git a/src/test/regress/parallel_schedule b/src/test/regress/parallel_schedule index d25ed4c8cad..b7cc56c6213 100644 --- a/src/test/regress/parallel_schedule +++ b/src/test/regress/parallel_schedule @@ -1,8 +1,8 @@ # ---------- # The first group of parallel test -# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.41 2007/03/19 16:44:41 tgl Exp $ +# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.42 2007/04/02 03:49:42 tgl Exp $ # ---------- -test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid +test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid enum # Depends on things setup during char, varchar and text test: strings diff --git a/src/test/regress/serial_schedule b/src/test/regress/serial_schedule index 31bac612607..85c03f33c9e 100644 --- a/src/test/regress/serial_schedule +++ b/src/test/regress/serial_schedule @@ -1,4 +1,4 @@ -# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.38 2007/03/13 00:33:44 tgl Exp $ +# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.39 2007/04/02 03:49:42 tgl Exp $ # This should probably be in an order similar to parallel_schedule. test: boolean test: char @@ -14,6 +14,7 @@ test: float8 test: bit test: numeric test: uuid +test: enum test: strings test: numerology test: point diff --git a/src/test/regress/sql/enum.sql b/src/test/regress/sql/enum.sql new file mode 100644 index 00000000000..387e8e72ed8 --- /dev/null +++ b/src/test/regress/sql/enum.sql @@ -0,0 +1,171 @@ +-- +-- Enum tests +-- + +CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple'); + +-- +-- Did it create the right number of rows? +-- +SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype; + +-- +-- I/O functions +-- +SELECT 'red'::rainbow; +SELECT 'mauve'::rainbow; + +-- +-- Basic table creation, row selection +-- +CREATE TABLE enumtest (col rainbow); +INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green'); +COPY enumtest FROM stdin; +blue +purple +\. +SELECT * FROM enumtest; + +-- +-- Operators, no index +-- +SELECT * FROM enumtest WHERE col = 'orange'; +SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; +SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; +SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; +SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; +SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; + +-- +-- Cast to/from text +-- +SELECT 'red'::rainbow::text || 'hithere'; +SELECT 'red'::text::rainbow = 'red'::rainbow; + +-- +-- Aggregates +-- +SELECT min(col) FROM enumtest; +SELECT max(col) FROM enumtest; +SELECT max(col) FROM enumtest WHERE col < 'green'; + +-- +-- Index tests, force use of index +-- +SET enable_seqscan = off; +SET enable_bitmapscan = off; + +-- +-- Btree index / opclass with the various operators +-- +CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col); +SELECT * FROM enumtest WHERE col = 'orange'; +SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col; +SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col; +SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col; +SELECT * FROM enumtest WHERE col < 'green' ORDER BY col; +SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col; +SELECT min(col) FROM enumtest; +SELECT max(col) FROM enumtest; +SELECT max(col) FROM enumtest WHERE col < 'green'; +DROP INDEX enumtest_btree; + +-- +-- Hash index / opclass with the = operator +-- +CREATE INDEX enumtest_hash ON enumtest USING hash (col); +SELECT * FROM enumtest WHERE col = 'orange'; +DROP INDEX enumtest_hash; + +-- +-- End index tests +-- +RESET enable_seqscan; +RESET enable_bitmapscan; + +-- +-- Domains over enums +-- +CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue')); +SELECT 'red'::rgb; +SELECT 'purple'::rgb; +SELECT 'purple'::rainbow::rgb; +DROP DOMAIN rgb; + +-- +-- Arrays +-- +SELECT '{red,green,blue}'::rainbow[]; +SELECT ('{red,green,blue}'::rainbow[])[2]; +SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]); +SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]); +SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]); +SELECT 'red' = ALL ('{red,red}'::rainbow[]); + +-- +-- Support functions +-- +SELECT enum_first(NULL::rainbow); +SELECT enum_last('green'::rainbow); +SELECT enum_range(NULL::rainbow); +SELECT enum_range('orange'::rainbow, 'green'::rainbow); +SELECT enum_range(NULL, 'green'::rainbow); +SELECT enum_range('orange'::rainbow, NULL); +SELECT enum_range(NULL::rainbow, NULL); + +-- +-- User functions, can't test perl/python etc here since may not be compiled. +-- +CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$ +BEGIN +RETURN $1::text || 'omg'; +END +$$ LANGUAGE plpgsql; +SELECT echo_me('red'::rainbow); +-- +-- Concrete function should override generic one +-- +CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$ +BEGIN +RETURN $1::text || 'wtf'; +END +$$ LANGUAGE plpgsql; +SELECT echo_me('red'::rainbow); +-- +-- If we drop the original generic one, we don't have to qualify the type +-- anymore, since there's only one match +-- +DROP FUNCTION echo_me(anyenum); +SELECT echo_me('red'); +DROP FUNCTION echo_me(rainbow); + +-- +-- RI triggers on enum types +-- +CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY); +CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent); +INSERT INTO enumtest_parent VALUES ('red'); +INSERT INTO enumtest_child VALUES ('red'); +INSERT INTO enumtest_child VALUES ('blue'); -- fail +DELETE FROM enumtest_parent; -- fail +-- +-- cross-type RI should fail +-- +CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly'); +CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent); +DROP TYPE bogus; + +-- +-- Cleanup +-- +DROP TABLE enumtest_child; +DROP TABLE enumtest_parent; +DROP TABLE enumtest; +DROP TYPE rainbow; + +-- +-- Verify properly cleaned up +-- +SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow'; +SELECT * FROM pg_enum WHERE NOT EXISTS + (SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid); diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql index 3677b90ceb9..b1caa33efbd 100644 --- a/src/test/regress/sql/type_sanity.sql +++ b/src/test/regress/sql/type_sanity.sql @@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname 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 + (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR NOT p1.typisdefined OR (p1.typalign not in ('c', 's', 'i', 'd')) OR (p1.typstorage not in ('p', 'x', 'e', 'm')); @@ -49,12 +49,12 @@ FROM pg_type as p1 WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR (p1.typtype != 'c' AND p1.typrelid != 0); --- Look for basic types that don't have an array type. +-- Look for basic or enum types that don't have an array type. -- NOTE: as of 8.0, this check finds smgr and unknown. SELECT p1.oid, p1.typname FROM pg_type as p1 -WHERE p1.typtype in ('b') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS +WHERE p1.typtype in ('b','e') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS (SELECT 1 FROM pg_type as p2 WHERE p2.typname = ('_' || p1.typname)::name AND p2.typelem = p1.oid);