-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.148 2007/03/26 16:58:37 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.149 2007/04/02 03:49:36 tgl Exp $ -->
<!--
Documentation of the system catalogs, directed toward PostgreSQL developers
-->
<entry>descriptions or comments on database objects</entry>
</row>
+ <row>
+ <entry><link linkend="catalog-pg-enum"><structname>pg_enum</structname></link></entry>
+ <entry>enum label and value definitions</entry>
+ </row>
+
<row>
<entry><link linkend="catalog-pg-index"><structname>pg_index</structname></link></entry>
<entry>additional index information</entry>
in which the source and target types are the same, if the associated
function takes more than one argument. Such entries represent
<quote>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.
</para>
<para>
</sect1>
+ <sect1 id="catalog-pg-enum">
+ <title><structname>pg_enum</structname></title>
+
+ <indexterm zone="catalog-pg-enum">
+ <primary>pg_enum</primary>
+ </indexterm>
+
+ <para>
+ The <structname>pg_enum</structname> 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 <structname>pg_enum</structname>. 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.
+ </para>
+
+ <table>
+ <title><structname>pg_enum</> Columns</title>
+
+ <tgroup cols=4>
+ <thead>
+ <row>
+ <entry>Name</entry>
+ <entry>Type</entry>
+ <entry>References</entry>
+ <entry>Description</entry>
+ </row>
+ </thead>
+
+ <tbody>
+ <row>
+ <entry><structfield>enumtypid</structfield></entry>
+ <entry><type>oid</type></entry>
+ <entry><literal><link linkend="catalog-pg-type"><structname>pg_type</structname></link>.oid</literal></entry>
+ <entry>The OID of the <structname>pg_type</> entry owning this enum value</entry>
+ </row>
+
+ <row>
+ <entry><structfield>enumlabel</structfield></entry>
+ <entry><type>name</type></entry>
+ <entry></entry>
+ <entry>The textual label for this enum value</entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+ </sect1>
+
<sect1 id="catalog-pg-index">
<title><structname>pg_index</structname></title>
<entry><type>char</type></entry>
<entry></entry>
<entry>
- <structfield>typtype</structfield> is <literal>b</literal> for
- a base type, <literal>c</literal> for a composite type (e.g., a
- table's row type), <literal>d</literal> for a domain, or
- <literal>p</literal> for a pseudo-type. See also
- <structfield>typrelid</structfield> and
+ <structfield>typtype</structfield> is
+ <literal>b</literal> for a base type,
+ <literal>c</literal> for a composite type (e.g., a table's row type),
+ <literal>d</literal> for a domain,
+ <literal>e</literal> for an enum type,
+ or <literal>p</literal> for a pseudo-type.
+ See also <structfield>typrelid</structfield> and
<structfield>typbasetype</structfield>
</entry>
</row>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.191 2007/03/14 17:38:05 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.192 2007/04/02 03:49:36 tgl Exp $ -->
<chapter id="datatype">
<title id="datatype-title">Data Types</title>
</para>
</sect1>
+ <sect1 id="datatype-enum">
+ <title>Enumerated Types</title>
+
+ <indexterm zone="datatype-enum">
+ <primary>data type</primary>
+ <secondary>enumerated (enum)</secondary>
+ </indexterm>
+
+ <para>
+ 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 <type>enum</type>
+ 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.
+ </para>
+
+ <sect2>
+ <title>Declaration of Enumerated Types</title>
+
+ <para>
+ Enum types are created using the <xref
+ linkend="sql-createtype" endterm="sql-createtype-title"> command,
+ for example:
+
+<programlisting>
+CREATE TYPE mood AS ENUM ('sad', 'ok', 'happy');
+</programlisting>
+
+ Once created, the enum type can be used in table and function
+ definitions much like any other type:
+ </para>
+
+ <example>
+ <title>Basic Enum Usage</title>
+<programlisting>
+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)
+</programlisting>
+ </example>
+ </sect2>
+
+ <sect2>
+ <title>Ordering</title>
+
+ <para>
+ 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:
+ </para>
+
+ <example>
+ <title>Enum Ordering</title>
+<programlisting>
+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)
+</programlisting>
+ </example>
+ </sect2>
+
+ <sect2>
+ <title>Type Safety</title>
+
+ <para>
+ Enumerated types are completely separate data types and may not
+ be compared with each other.
+ </para>
+
+ <example>
+ <title>Lack of Casting</title>
+<programlisting>
+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
+</programlisting>
+ </example>
+
+ <para>
+ If you really need to do something like that, you can either
+ write a custom operator or add explicit casts to your query:
+ </para>
+
+ <example>
+ <title>Comparing Different Enums by Casting to Text</title>
+<programlisting>
+SELECT person.name, holidays.num_weeks FROM person, holidays
+ WHERE person.current_mood::text = holidays.happiness::text;
+ name | num_weeks
+------+-----------
+ Moe | 4
+(1 row)
+
+</programlisting>
+ </example>
+ </sect2>
+
+ <sect2>
+ <title>Implementation Details</title>
+
+ <para>
+ An enum value occupies four bytes on disk. The length of an enum
+ value's textual label is limited by the <symbol>NAMEDATALEN</symbol>
+ setting compiled into <productname>PostgreSQL</productname>; in standard
+ builds this means at most 63 bytes.
+ </para>
+
+ <para>
+ Enum labels are case sensitive, so
+ <type>'happy'</type> is not the same as <type>'HAPPY'</type>.
+ Spaces in the labels are significant, too.
+ </para>
+
+ </sect2>
+ </sect1>
+
<sect1 id="datatype-geometric">
<title>Geometric Types</title>
<primary>anyelement</primary>
</indexterm>
+ <indexterm zone="datatype-pseudo">
+ <primary>anyenum</primary>
+ </indexterm>
+
<indexterm zone="datatype-pseudo">
<primary>void</primary>
</indexterm>
(see <xref linkend="extend-types-polymorphic">).</entry>
</row>
+ <row>
+ <entry><type>anyenum</></entry>
+ <entry>Indicates that a function accepts any enum data type
+ (see <xref linkend="extend-types-polymorphic"> and
+ <xref linkend="datatype-enum">).</entry>
+ </row>
+
<row>
<entry><type>cstring</></entry>
<entry>Indicates that a function accepts or returns a null-terminated C string.</entry>
languages all forbid use of a pseudo-type as argument type, and allow
only <type>void</> and <type>record</> as a result type (plus
<type>trigger</> when the function is used as a trigger). Some also
- support polymorphic functions using the types <type>anyarray</> and
- <type>anyelement</>.
+ support polymorphic functions using the types <type>anyarray</>,
+ <type>anyelement</> and <type>anyenum</>.
</para>
<para>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.33 2007/01/31 20:56:17 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/extend.sgml,v 1.34 2007/04/02 03:49:36 tgl Exp $ -->
<chapter id="extend">
<title>Extending <acronym>SQL</acronym></title>
</indexterm>
<para>
- Two pseudo-types of special interest are <type>anyelement</> and
- <type>anyarray</>, which are collectively called <firstterm>polymorphic
- types</>. Any function declared using these types is said to be
+ Three pseudo-types of special interest are <type>anyelement</>,
+ <type>anyarray</>, and <type>anyenum</>,
+ which are collectively called <firstterm>polymorphic types</>.
+ Any function declared using these types is said to be
a <firstterm>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
<type>anyelement</type>, the actual array type in the
<type>anyarray</type> positions must be an array whose elements are
the same type appearing in the <type>anyelement</type> positions.
+ <type>anyenum</> is treated exactly the same as <type>anyelement</>,
+ but adds the additional constraint that the actual type must
+ be an enum type.
</para>
<para>
implements subscripting as <literal>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 <literal>f(anyarray) returns anyenum</>
+ will only accept arrays of enum types.
</para>
</sect2>
</sect1>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.372 2007/04/01 09:00:24 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.373 2007/04/02 03:49:36 tgl Exp $ -->
<chapter id="functions">
<title>Functions and Operators</title>
</sect1>
+ <sect1 id="functions-enum">
+ <title>Enum Support Functions</title>
+
+ <para>
+ For enum types (described in <xref linkend="datatype-enum">),
+ there are several functions that allow cleaner programming without
+ hard-coding particular values of an enum type.
+ These are listed in <xref linkend="functions-enum-table">. The examples
+ assume an enum type created as:
+
+<programlisting>
+CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple');
+</programlisting>
+
+ </para>
+
+ <table id="functions-enum-table">
+ <title>Enum Support Functions</title>
+ <tgroup cols="4">
+ <thead>
+ <row>
+ <entry>Function</entry>
+ <entry>Description</entry>
+ <entry>Example</entry>
+ <entry>Example Result</entry>
+ </row>
+ </thead>
+ <tbody>
+ <row>
+ <entry><literal>enum_first(anyenum)</literal></entry>
+ <entry>Returns the first value of the input enum type</entry>
+ <entry><literal>enum_first(null::rainbow)</literal></entry>
+ <entry><literal>red</literal></entry>
+ </row>
+ <row>
+ <entry><literal>enum_last(anyenum)</literal></entry>
+ <entry>Returns the last value of the input enum type</entry>
+ <entry><literal>enum_last(null::rainbow)</literal></entry>
+ <entry><literal>purple</literal></entry>
+ </row>
+ <row>
+ <entry><literal>enum_range(anyenum)</literal></entry>
+ <entry>Returns all values of the input enum type in an ordered array</entry>
+ <entry><literal>enum_range(null::rainbow)</literal></entry>
+ <entry><literal>{red,orange,yellow,green,blue,purple}</literal></entry>
+ </row>
+ <row>
+ <entry morerows="2"><literal>enum_range(anyenum, anyenum)</literal></entry>
+ <entry morerows="2">
+ 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.
+ </entry>
+ <entry><literal>enum_range('orange'::rainbow, 'green'::rainbow)</literal></entry>
+ <entry><literal>{orange,yellow,green}</literal></entry>
+ </row>
+ <row>
+ <entry><literal>enum_range(NULL, 'green'::rainbow)</literal></entry>
+ <entry><literal>{red,orange,yellow,green}</literal></entry>
+ </row>
+ <row>
+ <entry><literal>enum_range('orange'::rainbow, NULL)</literal></entry>
+ <entry><literal>{orange,yellow,green,blue,purple}</literal></entry>
+ </row>
+ </tbody>
+ </tgroup>
+ </table>
+
+ <para>
+ Notice that except for the two-argument form of <function>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.
+ </para>
+ </sect1>
+
<sect1 id="functions-geometry">
<title>Geometric Functions and Operators</title>
-<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.105 2007/02/01 00:28:17 momjian Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/plpgsql.sgml,v 1.106 2007/04/02 03:49:37 tgl Exp $ -->
<chapter id="plpgsql">
<title><application>PL/pgSQL</application> - <acronym>SQL</acronym> Procedural Language</title>
<para>
<application>PL/pgSQL</> functions can also be declared to accept
and return the polymorphic types
- <type>anyelement</type> and <type>anyarray</type>. The actual
+ <type>anyelement</type>, <type>anyarray</type>, and <type>anyenum</>.
+ The actual
data types handled by a polymorphic function can vary from call to
call, as discussed in <xref linkend="extend-types-polymorphic">.
An example is shown in <xref linkend="plpgsql-declaration-aliases">.
<para>
When the return type of a <application>PL/pgSQL</application>
- function is declared as a polymorphic type (<type>anyelement</type>
- or <type>anyarray</type>), a special parameter <literal>$0</literal>
+ function is declared as a polymorphic type (<type>anyelement</type>,
+ <type>anyarray</type>, or <type>anyenum</>),
+ a special parameter <literal>$0</literal>
is created. Its data type is the actual return type of the function,
as deduced from the actual input types (see <xref
linkend="extend-types-polymorphic">).
<para>
The same effect can be had by declaring one or more output parameters as
- <type>anyelement</type> or <type>anyarray</type>. In this case the
+ polymorphic types. In this case the
special <literal>$0</literal> parameter is not used; the output
parameters themselves serve the same purpose. For example:
<!--
-$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.68 2007/02/01 00:28:18 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.69 2007/04/02 03:49:37 tgl Exp $
PostgreSQL documentation
-->
CREATE TYPE <replaceable class="parameter">name</replaceable> AS
( <replaceable class="PARAMETER">attribute_name</replaceable> <replaceable class="PARAMETER">data_type</replaceable> [, ... ] )
+CREATE TYPE <replaceable class="parameter">name</replaceable> AS ENUM
+ ( '<replaceable class="parameter">label</replaceable>' [, ... ] )
+
CREATE TYPE <replaceable class="parameter">name</replaceable> (
INPUT = <replaceable class="parameter">input_function</replaceable>,
OUTPUT = <replaceable class="parameter">output_function</replaceable>
</para>
</refsect2>
+ <refsect2>
+ <title>Enumerated Types</title>
+
+ <para>
+ The second form of <command>CREATE TYPE</command> creates an enumerated
+ (enum) type, as described in <xref linkend="datatype-enum">.
+ Enum types take a list of one or more quoted labels, each of which
+ must be less than <symbol>NAMEDATALEN</symbol> bytes long (64 in a standard
+ <productname>PostgreSQL</productname> build).
+ </para>
+ </refsect2>
+
<refsect2>
<title>Base Types</title>
<para>
- The second form of <command>CREATE TYPE</command> creates a new base type
+ The third form of <command>CREATE TYPE</command> 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 <command>CREATE FUNCTION</command>) before
<title>Array Types</title>
<para>
- Whenever a user-defined base data type is created,
+ Whenever a user-defined base or enum data type is created,
<productname>PostgreSQL</productname> automatically creates an
associated array type, whose name consists of the base type's
name prepended with an underscore. The parser understands this
</listitem>
</varlistentry>
+ <varlistentry>
+ <term><replaceable class="parameter">label</replaceable></term>
+ <listitem>
+ <para>
+ A string literal representing the textual label associated with
+ one value of an enum type.
+ </para>
+ </listitem>
+ </varlistentry>
+
<varlistentry>
<term><replaceable class="parameter">input_function</replaceable></term>
<listitem>
</programlisting>
</para>
+ <para>
+ This example creates an enumerated type and uses it in
+ a table definition:
+<programlisting>
+CREATE TYPE bug_status AS ENUM ('new', 'open', 'closed');
+
+CREATE TABLE bug (
+ serial id,
+ description text,
+ status bug_status
+);
+</programlisting>
+ </para>
+
<para>
This example creates the base data type <type>box</type> and then uses the
type in a table definition:
-<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.126 2007/02/27 23:48:06 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/xfunc.sgml,v 1.127 2007/04/02 03:49:37 tgl Exp $ -->
<sect1 id="xfunc">
<title>User-Defined Functions</title>
<para>
<acronym>SQL</acronym> functions can be declared to accept and
- return the polymorphic types <type>anyelement</type> and
- <type>anyarray</type>. See <xref
+ return the polymorphic types <type>anyelement</type>,
+ <type>anyarray</type>, and <type>anyenum</type>. See <xref
linkend="extend-types-polymorphic"> for a more detailed
explanation of polymorphic functions. Here is a polymorphic
function <function>make_array</function> that builds up an array
Without the typecast, you will get errors like this:
<screen>
<computeroutput>
-ERROR: could not determine "anyarray"/"anyelement" type because input has type "unknown"
+ERROR: could not determine polymorphic type because input has type "unknown"
</computeroutput>
</screen>
</para>
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.
</screen>
</para>
<para>
C-language functions can be declared to accept and
return the polymorphic types
- <type>anyelement</type> and <type>anyarray</type>.
+ <type>anyelement</type>, <type>anyarray</type>, and <type>anyenum</type>.
See <xref linkend="extend-types-polymorphic"> for a more detailed explanation
of polymorphic functions. When function arguments or return types
are defined as polymorphic types, the function author cannot know
*
*
* 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
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)
{
#
# 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 $
#
#-------------------------------------------------------------------------
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
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 \
*
*
* 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
(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)
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 */
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
hasPolyArg = false;
for (i = 0; i < numArgs; i++)
{
- if (aggArgTypes[i] == ANYARRAYOID ||
- aggArgTypes[i] == ANYELEMENTOID)
+ if (IsPolymorphicType(aggArgTypes[i]))
{
hasPolyArg = true;
break;
* 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;
* 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)
*/
for (i = 0; i < nargs; i++)
{
- if (input_types[i] != ANYARRAYOID &&
- input_types[i] != ANYELEMENTOID)
+ if (!IsPolymorphicType(input_types[i]))
{
allPolyArgs = false;
break;
*/
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),
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
}
/*
- * 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++)
{
{
case ANYARRAYOID:
case ANYELEMENTOID:
+ case ANYENUMOID:
genericInParam = true;
break;
case INTERNALOID:
{
case ANYARRAYOID:
case ANYELEMENTOID:
+ case ANYENUMOID:
genericOutParam = true;
break;
case INTERNALOID:
}
}
- 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,
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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*
* 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;
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 */
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,
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 */
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,
*
*
* 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
* 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",
*
*
* 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
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",
*
*
* 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
#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"
{
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",
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 */
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 */
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);
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",
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 */
/* 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",
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.
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",
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);
* 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),
/* 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",
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",
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
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 */
{
Oid argtype = argOidVect[argnum];
- if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)
+ if (IsPolymorphicType(argtype))
{
argtype = get_fn_expr_argtype(finfo, argnum);
if (argtype == InvalidOid)
* 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
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
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;
/* 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,
* 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 $
*
*-------------------------------------------------------------------------
*/
/*
* 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)
/* 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;
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
case T_CompositeTypeStmt:
retval = _copyCompositeTypeStmt(from);
break;
+ case T_CreateEnumStmt:
+ retval = _copyCreateEnumStmt(from);
+ break;
case T_ViewStmt:
retval = _copyViewStmt(from);
break;
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
{
case T_CompositeTypeStmt:
retval = _equalCompositeTypeStmt(a, b);
break;
+ case T_CreateEnumStmt:
+ retval = _equalCreateEnumStmt(a, b);
+ break;
case T_ViewStmt:
retval = _equalViewStmt(a, b);
break;
*
*
* 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
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;
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));
}
*
*
* 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
TableFuncElementList opt_type_modifiers
prep_type_clause
execute_param_clause using_clause returning_clause
+ enum_val_list
%type <range> OptTempTableName
%type <into> into_clause create_as_target
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
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; }
}
;
+enum_val_list: Sconst
+ { $$ = list_make1(makeString($1)); }
+ | enum_val_list ',' Sconst
+ { $$ = lappend($1, makeString($3)); }
+ ;
+
/*****************************************************************************
*
| ENABLE_P
| ENCODING
| ENCRYPTED
+ | ENUM_P
| ESCAPE
| EXCLUDING
| EXCLUSIVE
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
{"encoding", ENCODING},
{"encrypted", ENCRYPTED},
{"end", END_P},
+ {"enum", ENUM_P},
{"escape", ESCAPE},
{"except", EXCEPT},
{"excluding", EXCLUDING},
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
}
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.
* 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.
*/
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;
* 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.
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
{
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)
}
}
+ if (have_anyenum)
+ {
+ /* require the element type to be an enum */
+ if (!type_is_enum(elem_typeid))
+ return false;
+ }
+
/* Looks valid */
return true;
}
* 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.
* 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,
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
{
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;
}
/*
- * 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;
/* 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))));
}
/*
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)
{
}
}
- /* 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))
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 */
/*
* 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
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);
return array_typeid;
}
}
- else if (declared_type == ANYELEMENTOID)
+ else if (declared_type == ANYELEMENTOID || declared_type == ANYENUMOID)
{
if (context_declared_type == ANYARRAYOID)
{
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;
}
/* 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 */
}
case (INTERNALOID):
case (OPAQUEOID):
case (ANYELEMENTOID):
+ case (ANYENUMOID):
result = GENERIC_TYPE;
break;
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),
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;
/* 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;
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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,
/*
* 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;
}
/*
- * 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,
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
case T_CreateTableSpaceStmt:
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
+ case T_CreateEnumStmt:
case T_ViewStmt:
case T_DropCastStmt:
case T_DropStmt:
}
break;
+ case T_CreateEnumStmt: /* CREATE TYPE (enum) */
+ DefineEnum((CreateEnumStmt *) parsetree);
+ break;
+
case T_ViewStmt: /* CREATE VIEW */
DefineView((ViewStmt *) parsetree, queryString);
break;
tag = "CREATE TYPE";
break;
+ case T_CreateEnumStmt:
+ tag = "CREATE TYPE";
+ break;
+
case T_ViewStmt:
tag = "CREATE VIEW";
break;
lev = LOGSTMT_DDL;
break;
+ case T_CreateEnumStmt:
+ lev = LOGSTMT_DDL;
+ break;
+
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
#
# 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
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 \
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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;
+}
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
}
+/*
+ * 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.
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)));
break;
default:
- if (get_typtype(typeoid) == 'd')
+ if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
{
Oid base_typeoid;
int32 base_typmod = -1;
* 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.
* 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);
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);
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);
}
/*
*
*
* 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
#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"
},
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,
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
if ((flags & TYPECACHE_TUPDESC) &&
typentry->tupDesc == NULL &&
- typentry->typtype == 'c')
+ typentry->typtype == TYPTYPE_COMPOSITE)
{
Relation rel;
* 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 $
*
*-------------------------------------------------------------------------
*/
* 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.
/*
* If scalar polymorphic result, try to resolve it.
*/
- if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+ if (IsPolymorphicType(rettype))
{
Oid newrettype = exprType(call_expr);
/*
* 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
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;
case ANYARRAYOID:
have_anyarray_result = true;
break;
+ case ANYENUMOID:
+ have_anyelement_result = true;
+ have_anyenum = true;
+ break;
default:
break;
}
switch (declared_args->values[i])
{
case ANYELEMENTOID:
+ case ANYENUMOID:
if (!OidIsValid(anyelement_type))
anyelement_type = get_call_expr_argtype(call_expr, i);
break;
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,
/*
* 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.
*
switch (argtypes[i])
{
case ANYELEMENTOID:
+ case ANYENUMOID:
if (argmode == PROARGMODE_OUT)
have_anyelement_result = true;
else
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:
{
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;
* 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
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
*/
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]));
/*
* 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;
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);
}
/*
* 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.
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
*/
/* yyyymmddN */
-#define CATALOG_VERSION_NO 200704011
+#define CATALOG_VERSION_NO 200704012
#endif
* 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 $
*
*-------------------------------------------------------------------------
*/
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
* 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
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_ ));
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" ));
* 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
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 */
* 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
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 */
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 */
*
* 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
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 */
--- /dev/null
+/*-------------------------------------------------------------------------
+ *
+ * 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 */
* 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
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 */
* 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
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
* 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
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 */
* 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
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,
* 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
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.
*/
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
*
#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
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
T_DropOwnedStmt,
T_ReassignOwnedStmt,
T_CompositeTypeStmt,
+ T_CreateEnumStmt,
/*
* TAGS FOR PARSE TREE NODES (parsenodes.h)
* 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 $
*
*-------------------------------------------------------------------------
*/
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
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
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);
* 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 $
*
*-------------------------------------------------------------------------
*/
#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);
/**********************************************************************
* 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 $
*
**********************************************************************/
/* 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 ||
&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",
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)
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);
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype argument */
- if (typeStruct->typtype == 'p')
+ if (typeStruct->typtype == TYPTYPE_PSEUDO)
{
free(prodesc->proname);
free(prodesc);
format_type_be(procStruct->proargtypes.values[i]))));
}
- if (typeStruct->typtype == 'c')
+ if (typeStruct->typtype == TYPTYPE_COMPOSITE)
prodesc->arg_is_rowtype[i] = true;
else
{
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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)
* the info available.
*/
rettypeid = procStruct->prorettype;
- if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)
+ if (IsPolymorphicType(rettypeid))
{
if (forValidator)
{
rettypeid = INT4ARRAYOID;
else
rettypeid = INT4OID;
+ /* XXX what could we use for ANYENUM? */
}
else
{
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)
* 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,
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
switch (argtypes[i])
{
case ANYELEMENTOID:
+ case ANYENUMOID: /* XXX dubious */
argtypes[i] = INT4OID;
break;
case ANYARRAYOID:
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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")));
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")));
*
*
* 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 $
*
*-------------------------------------------------------------------------
*/
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 ||
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",
}
/* 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",
/**********************************************************************
* 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 $
*
*********************************************************************
*/
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)
format_type_be(procStruct->prorettype))));
}
- if (rvTypeStruct->typtype == 'c')
+ if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE)
{
/*
* Tuple: set up later, during first call to
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);
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.");
* 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 $
*
**********************************************************************/
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 */ ;
}
}
- if (typeStruct->typtype == 'c')
+ if (typeStruct->typtype == TYPTYPE_COMPOSITE)
{
free(prodesc->proname);
free(prodesc);
typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
/* Disallow pseudotype argument */
- if (typeStruct->typtype == 'p')
+ if (typeStruct->typtype == TYPTYPE_PSEUDO)
{
free(prodesc->proname);
free(prodesc);
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);
--- /dev/null
+--
+-- 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)
+
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[],
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
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,
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,
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[],
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
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,
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,
(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
-------------------
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.
pg_database | t
pg_depend | t
pg_description | t
+ pg_enum | t
pg_index | t
pg_inherits | t
pg_language | t
timetz_tbl | f
tinterval_tbl | f
varchar_tbl | f
-(130 rows)
+(131 rows)
--
-- another sanity check: every system catalog that has OIDs should have
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'));
-----+---------
(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);
# ----------
# 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
-# $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
test: bit
test: numeric
test: uuid
+test: enum
test: strings
test: numerology
test: point
--- /dev/null
+--
+-- 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);
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'));
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);