Support enum data types. Along the way, use macros for the values of
authorTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Apr 2007 03:49:42 +0000 (03:49 +0000)
committerTom Lane <tgl@sss.pgh.pa.us>
Mon, 2 Apr 2007 03:49:42 +0000 (03:49 +0000)
pg_type.typtype whereever practical.  Tom Dunstan, with some kibitzing
from Tom Lane.

74 files changed:
doc/src/sgml/catalogs.sgml
doc/src/sgml/datatype.sgml
doc/src/sgml/extend.sgml
doc/src/sgml/func.sgml
doc/src/sgml/plpgsql.sgml
doc/src/sgml/ref/create_type.sgml
doc/src/sgml/xfunc.sgml
src/backend/access/hash/hashfunc.c
src/backend/catalog/Makefile
src/backend/catalog/heap.c
src/backend/catalog/pg_aggregate.c
src/backend/catalog/pg_enum.c [new file with mode: 0644]
src/backend/catalog/pg_proc.c
src/backend/catalog/pg_type.c
src/backend/commands/aggregatecmds.c
src/backend/commands/functioncmds.c
src/backend/commands/typecmds.c
src/backend/executor/functions.c
src/backend/executor/nodeAgg.c
src/backend/nodes/copyfuncs.c
src/backend/nodes/equalfuncs.c
src/backend/optimizer/util/clauses.c
src/backend/parser/gram.y
src/backend/parser/keywords.c
src/backend/parser/parse_coerce.c
src/backend/parser/parse_expr.c
src/backend/parser/parse_oper.c
src/backend/parser/parse_type.c
src/backend/tcop/utility.c
src/backend/utils/adt/Makefile
src/backend/utils/adt/enum.c [new file with mode: 0644]
src/backend/utils/adt/format_type.c
src/backend/utils/adt/pseudotypes.c
src/backend/utils/adt/xml.c
src/backend/utils/cache/lsyscache.c
src/backend/utils/cache/syscache.c
src/backend/utils/cache/typcache.c
src/backend/utils/fmgr/funcapi.c
src/bin/pg_dump/pg_dump.c
src/include/access/hash.h
src/include/catalog/catversion.h
src/include/catalog/indexing.h
src/include/catalog/pg_aggregate.h
src/include/catalog/pg_amop.h
src/include/catalog/pg_amproc.h
src/include/catalog/pg_cast.h
src/include/catalog/pg_enum.h [new file with mode: 0644]
src/include/catalog/pg_opclass.h
src/include/catalog/pg_operator.h
src/include/catalog/pg_opfamily.h
src/include/catalog/pg_proc.h
src/include/catalog/pg_type.h
src/include/commands/typecmds.h
src/include/nodes/nodes.h
src/include/nodes/parsenodes.h
src/include/parser/parse_type.h
src/include/utils/builtins.h
src/include/utils/lsyscache.h
src/include/utils/syscache.h
src/pl/plperl/plperl.c
src/pl/plpgsql/src/pl_comp.c
src/pl/plpgsql/src/pl_exec.c
src/pl/plpgsql/src/pl_handler.c
src/pl/plpython/plpython.c
src/pl/tcl/pltcl.c
src/test/regress/expected/enum.out [new file with mode: 0644]
src/test/regress/expected/polymorphism.out
src/test/regress/expected/rangefuncs.out
src/test/regress/expected/sanity_check.out
src/test/regress/expected/type_sanity.out
src/test/regress/parallel_schedule
src/test/regress/serial_schedule
src/test/regress/sql/enum.sql [new file with mode: 0644]
src/test/regress/sql/type_sanity.sql

index 7d325d1dde58f736ebaefab76bb3c6ac386e8f75..492b06de0a2df7dd2f0bfe46694de19bb1eddb4b 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
index 2d67885fd65c39edb76cefb8403f270da22c3bc2..1bf103c8780e21949bbe5c3b9fc96e61c322a10a 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
@@ -2424,6 +2424,161 @@ SELECT * FROM test1 WHERE a;
    </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>
 
@@ -3278,6 +3433,10 @@ SELECT * FROM pg_attribute
     <primary>anyelement</primary>
    </indexterm>
 
+   <indexterm zone="datatype-pseudo">
+    <primary>anyenum</primary>
+   </indexterm>
+
    <indexterm zone="datatype-pseudo">
     <primary>void</primary>
    </indexterm>
@@ -3343,6 +3502,13 @@ SELECT * FROM pg_attribute
         (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>
@@ -3395,8 +3561,8 @@ SELECT * FROM pg_attribute
     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>
index ad22a5dcdddb1eebcb70388fa0aa337ffe00aa7c..bb5834e74a9a570df3343cb85ca0f1497584bbeb 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
index db7cd1d1f3b3a1d6c3de246f5f5cc5128ed9870d..0baf1525944cce736ca069d13beb9f1c62bfddca 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
@@ -6646,6 +6646,87 @@ SELECT pg_sleep(1.5);
  </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>
 
index 55904d6638a779a5287cf675cfbba3b35b558036..e6f7309c6505b712f1801dc78a72b0decd444ba3 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
@@ -210,7 +210,8 @@ $$ LANGUAGE plpgsql;
     <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">.
@@ -698,8 +699,9 @@ $$ LANGUAGE plpgsql;
 
      <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">).
@@ -726,7 +728,7 @@ $$ LANGUAGE plpgsql;
 
      <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:
 
index 29f35417079e4787bcab91dbf537d9929e70eced..9be57d7fcdee11e8284b28ba9fc68ea31abe1f57 100644 (file)
@@ -1,5 +1,5 @@
 <!--
-$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
 -->
 
@@ -23,6 +23,9 @@ 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>
@@ -77,11 +80,23 @@ CREATE TYPE <replaceable class="parameter">name</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
@@ -297,7 +312,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    <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
@@ -363,6 +378,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
     </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>
@@ -567,6 +592,20 @@ $$ LANGUAGE SQL;
 </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:
index 65d636b5e4c695fcbbd113829f3b1f59992e1246..553b33e17326c14089539996d071ccc89ca9b5fb 100644 (file)
@@ -1,4 +1,4 @@
-<!-- $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>
@@ -717,8 +717,8 @@ SELECT name, listchildren(name) FROM nodes;
 
     <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
@@ -746,7 +746,7 @@ SELECT make_array(1, 2) AS intarray, make_array('a'::text, 'b') AS textarray;
      Without the typecast, you will get errors like this:
 <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>
@@ -769,7 +769,7 @@ CREATE FUNCTION invalid_func() RETURNS anyelement AS $$
     SELECT 1;
 $$ LANGUAGE SQL;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyarray" or "anyelement" must have at least one argument of either type.
+DETAIL:  A function returning a polymorphic type must have at least one polymorphic argument.
 </screen>
     </para>
 
@@ -2831,7 +2831,7 @@ CREATE OR REPLACE FUNCTION retcomposite(IN integer, IN integer,
     <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
index 1a1f21ef2aa79dd0e875a5ebb9c6814c3d51a80a..d9b5524b0ab6d3dad9330be92db30f7d1e3d4f5b 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/access/hash/hashfunc.c,v 1.50 2007/01/05 22:19:22 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/access/hash/hashfunc.c,v 1.51 2007/04/02 03:49:37 tgl Exp $
  *
  * NOTES
  *   These functions are stored in pg_amproc.  For each operator class
@@ -72,6 +72,12 @@ hashoid(PG_FUNCTION_ARGS)
    PG_RETURN_UINT32(~((uint32) PG_GETARG_OID(0)));
 }
 
+Datum
+hashenum(PG_FUNCTION_ARGS)
+{
+    PG_RETURN_UINT32(~((uint32) PG_GETARG_OID(0)));
+}
+
 Datum
 hashfloat4(PG_FUNCTION_ARGS)
 {
index 026d9053a0aa639444cb74940ed36ee8538f9c5d..3503385c2ac63e5f92ea523cc2c4d1980b24c6c8 100644 (file)
@@ -2,7 +2,7 @@
 #
 # Makefile for backend/catalog
 #
-# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.63 2007/02/09 15:55:58 petere Exp $
+# $PostgreSQL: pgsql/src/backend/catalog/Makefile,v 1.64 2007/04/02 03:49:37 tgl Exp $
 #
 #-------------------------------------------------------------------------
 
@@ -11,7 +11,7 @@ top_builddir = ../../..
 include $(top_builddir)/src/Makefile.global
 
 OBJS = catalog.o dependency.o heap.o index.o indexing.o namespace.o aclchk.o \
-       pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o \
+       pg_aggregate.o pg_constraint.o pg_conversion.o pg_depend.o pg_enum.o \
        pg_largeobject.o pg_namespace.o pg_operator.o pg_proc.o pg_shdepend.o \
        pg_type.o toasting.o
 
@@ -32,7 +32,7 @@ POSTGRES_BKI_SRCS = $(addprefix $(top_srcdir)/src/include/catalog/,\
    pg_opfamily.h pg_opclass.h pg_am.h pg_amop.h pg_amproc.h \
    pg_language.h pg_largeobject.h pg_aggregate.h pg_statistic.h \
    pg_rewrite.h pg_trigger.h pg_listener.h pg_description.h pg_cast.h \
-   pg_namespace.h pg_conversion.h pg_depend.h \
+   pg_enum.h pg_namespace.h pg_conversion.h pg_depend.h \
    pg_database.h pg_tablespace.h pg_pltemplate.h \
    pg_authid.h pg_auth_members.h pg_shdepend.h pg_shdescription.h \
    toasting.h indexing.h \
index d29a6df21d3aab050614c43a3d60bfc767a4115c..e6404ecd0b56b4fccecb57338f961d7bb9c81f12 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.317 2007/02/14 01:58:56 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.318 2007/04/02 03:49:37 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -412,7 +412,7 @@ CheckAttributeType(const char *attname, Oid atttypid)
                (errcode(ERRCODE_INVALID_TABLE_DEFINITION),
                 errmsg("column \"%s\" has type \"unknown\"", attname),
                 errdetail("Proceeding with relation creation anyway.")));
-   else if (att_typtype == 'p')
+   else if (att_typtype == TYPTYPE_PSEUDO)
    {
        /* Special hack for pg_statistic: allow ANYARRAY during initdb */
        if (atttypid != ANYARRAYOID || IsUnderPostmaster)
@@ -718,7 +718,7 @@ AddNewRelationType(const char *typeName,
                   new_rel_oid, /* relation oid */
                   new_rel_kind,    /* relation kind */
                   -1,          /* internal size (varlena) */
-                  'c',         /* type-type (complex) */
+                  TYPTYPE_COMPOSITE,   /* type-type (composite) */
                   ',',         /* default array delimiter */
                   F_RECORD_IN, /* input procedure */
                   F_RECORD_OUT,    /* output procedure */
index 1d448cf5620830973964a2580f9afabd84fc7222..b60cd7714f5116ceaa54e218396ed6a091b00433 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.85 2007/01/22 01:35:20 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/pg_aggregate.c,v 1.86 2007/04/02 03:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -80,8 +80,7 @@ AggregateCreate(const char *aggName,
    hasPolyArg = false;
    for (i = 0; i < numArgs; i++)
    {
-       if (aggArgTypes[i] == ANYARRAYOID ||
-           aggArgTypes[i] == ANYELEMENTOID)
+       if (IsPolymorphicType(aggArgTypes[i]))
        {
            hasPolyArg = true;
            break;
@@ -92,12 +91,11 @@ AggregateCreate(const char *aggName,
     * If transtype is polymorphic, must have polymorphic argument also; else
     * we will have no way to deduce the actual transtype.
     */
-   if (!hasPolyArg &&
-       (aggTransType == ANYARRAYOID || aggTransType == ANYELEMENTOID))
+   if (IsPolymorphicType(aggTransType) && !hasPolyArg)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                 errmsg("cannot determine transition data type"),
-                errdetail("An aggregate using \"anyarray\" or \"anyelement\" as transition type must have at least one argument of either type.")));
+                errdetail("An aggregate using a polymorphic transition type must have at least one polymorphic argument.")));
 
    /* find the transfn */
    nargs_transfn = numArgs + 1;
@@ -170,13 +168,12 @@ AggregateCreate(const char *aggName,
     * that itself violates the rule against polymorphic result with no
     * polymorphic input.)
     */
-   if (!hasPolyArg &&
-       (finaltype == ANYARRAYOID || finaltype == ANYELEMENTOID))
+   if (IsPolymorphicType(finaltype) && !hasPolyArg)
        ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                 errmsg("cannot determine result data type"),
-          errdetail("An aggregate returning \"anyarray\" or \"anyelement\" "
-                    "must have at least one argument of either type.")));
+          errdetail("An aggregate returning a polymorphic type "
+                    "must have at least one polymorphic argument.")));
 
    /* handle sortop, if supplied */
    if (aggsortopName)
@@ -329,8 +326,7 @@ lookup_agg_function(List *fnName,
     */
    for (i = 0; i < nargs; i++)
    {
-       if (input_types[i] != ANYARRAYOID &&
-           input_types[i] != ANYELEMENTOID)
+       if (!IsPolymorphicType(input_types[i]))
        {
            allPolyArgs = false;
            break;
@@ -351,8 +347,7 @@ lookup_agg_function(List *fnName,
     */
    for (i = 0; i < nargs; i++)
    {
-       if (true_oid_array[i] != ANYARRAYOID &&
-           true_oid_array[i] != ANYELEMENTOID &&
+       if (!IsPolymorphicType(true_oid_array[i]) &&
            !IsBinaryCoercible(input_types[i], true_oid_array[i]))
            ereport(ERROR,
                    (errcode(ERRCODE_DATATYPE_MISMATCH),
diff --git a/src/backend/catalog/pg_enum.c b/src/backend/catalog/pg_enum.c
new file mode 100644 (file)
index 0000000..696c1f0
--- /dev/null
@@ -0,0 +1,146 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_enum.c
+ *   routines to support manipulation of the pg_enum relation
+ *
+ * Copyright (c) 2006-2007, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *   $PostgreSQL: pgsql/src/backend/catalog/pg_enum.c,v 1.1 2007/04/02 03:49:37 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "access/genam.h"
+#include "access/heapam.h"
+#include "catalog/catalog.h"
+#include "catalog/indexing.h"
+#include "catalog/pg_enum.h"
+#include "utils/builtins.h"
+#include "utils/fmgroids.h"
+
+static int oid_cmp(const void *p1, const void *p2);
+
+
+/*
+ * EnumValuesCreate
+ *     Create an entry in pg_enum for each of the supplied enum values.
+ *
+ * vals is a list of Value strings.
+ */
+void
+EnumValuesCreate(Oid enumTypeOid, List *vals)
+{
+   Relation    pg_enum;
+   TupleDesc   tupDesc;
+   NameData    enumlabel;
+   Oid        *oids;
+   int         i, n;
+   Datum       values[Natts_pg_enum];
+   char        nulls[Natts_pg_enum];
+   ListCell   *lc;
+   HeapTuple tup;
+
+   n = list_length(vals);
+
+   /*
+    * XXX we do not bother to check the list of values for duplicates ---
+    * if you have any, you'll get a less-than-friendly unique-index
+    * violation.  Is it worth trying harder?
+    */
+
+   pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+   tupDesc = pg_enum->rd_att;
+
+   /*
+    * Allocate oids.  While this method does not absolutely guarantee
+    * that we generate no duplicate oids (since we haven't entered each
+    * oid into the table before allocating the next), trouble could only
+    * occur if the oid counter wraps all the way around before we finish.
+    * Which seems unlikely.
+    */
+   oids = (Oid *) palloc(n * sizeof(Oid));
+   for(i = 0; i < n; i++)
+   {
+       oids[i] = GetNewOid(pg_enum);
+   }
+
+   /* sort them, just in case counter wrapped from high to low */
+   qsort(oids, n, sizeof(Oid), oid_cmp);
+
+   /* and make the entries */
+   memset(nulls, ' ', sizeof(nulls));
+
+   i = 0;
+   foreach(lc, vals)
+   {
+       char *lab = strVal(lfirst(lc));
+
+       values[Anum_pg_enum_enumtypid - 1] = ObjectIdGetDatum(enumTypeOid);
+       namestrcpy(&enumlabel, lab);
+       values[Anum_pg_enum_enumlabel - 1] = NameGetDatum(&enumlabel);
+
+       tup = heap_formtuple(tupDesc, values, nulls);
+       HeapTupleSetOid(tup, oids[i]);
+
+       simple_heap_insert(pg_enum, tup);
+       CatalogUpdateIndexes(pg_enum, tup);
+       heap_freetuple(tup);
+
+       i++;
+   }
+
+   /* clean up */
+   pfree(oids);
+   heap_close(pg_enum, RowExclusiveLock);
+}
+
+
+/*
+ * EnumValuesDelete
+ *     Remove all the pg_enum entries for the specified enum type.
+ */
+void
+EnumValuesDelete(Oid enumTypeOid)
+{
+   Relation    pg_enum;
+   ScanKeyData key[1];
+   SysScanDesc scan;
+   HeapTuple   tup;
+
+   pg_enum = heap_open(EnumRelationId, RowExclusiveLock);
+
+   ScanKeyInit(&key[0],
+               Anum_pg_enum_enumtypid,
+               BTEqualStrategyNumber, F_OIDEQ,
+               ObjectIdGetDatum(enumTypeOid));
+
+   scan = systable_beginscan(pg_enum, EnumTypIdLabelIndexId, true,
+                             SnapshotNow, 1, key);
+
+   while (HeapTupleIsValid(tup = systable_getnext(scan)))
+   {
+       simple_heap_delete(pg_enum, &tup->t_self);
+   }
+
+   systable_endscan(scan);
+
+   heap_close(pg_enum, RowExclusiveLock);
+}
+
+
+/* qsort comparison function */
+static int
+oid_cmp(const void *p1, const void *p2)
+{
+   Oid     v1 = *((const Oid *) p1);
+   Oid     v2 = *((const Oid *) p2);
+
+   if (v1 < v2)
+       return -1;
+   if (v1 > v2)
+       return 1;
+   return 0;
+}
index d4ed9b69f52f80f0fe73318d90ffb20f82c85a8f..26c5eb75e3e751cdd8dd44a40c4b752717468152 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.143 2007/01/22 01:35:20 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/pg_proc.c,v 1.144 2007/04/02 03:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,9 +137,9 @@ ProcedureCreate(const char *procedureName,
    }
 
    /*
-    * Do not allow return type ANYARRAY or ANYELEMENT unless at least one
-    * input argument is ANYARRAY or ANYELEMENT.  Also, do not allow return
-    * type INTERNAL unless at least one input argument is INTERNAL.
+    * Do not allow polymorphic return type unless at least one input argument
+    * is polymorphic.  Also, do not allow return type INTERNAL unless at
+    * least one input argument is INTERNAL.
     */
    for (i = 0; i < parameterCount; i++)
    {
@@ -147,6 +147,7 @@ ProcedureCreate(const char *procedureName,
        {
            case ANYARRAYOID:
            case ANYELEMENTOID:
+           case ANYENUMOID:
                genericInParam = true;
                break;
            case INTERNALOID:
@@ -169,6 +170,7 @@ ProcedureCreate(const char *procedureName,
            {
                case ANYARRAYOID:
                case ANYELEMENTOID:
+               case ANYENUMOID:
                    genericOutParam = true;
                    break;
                case INTERNALOID:
@@ -178,12 +180,12 @@ ProcedureCreate(const char *procedureName,
        }
    }
 
-   if ((returnType == ANYARRAYOID || returnType == ANYELEMENTOID ||
-        genericOutParam) && !genericInParam)
+   if ((IsPolymorphicType(returnType) || genericOutParam)
+       && !genericInParam)
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                 errmsg("cannot determine result data type"),
-                errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type.")));
+                errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
 
    if ((returnType == INTERNALOID || internalOutParam) && !internalInParam)
        ereport(ERROR,
@@ -533,26 +535,24 @@ fmgr_sql_validator(PG_FUNCTION_ARGS)
    proc = (Form_pg_proc) GETSTRUCT(tuple);
 
    /* Disallow pseudotype result */
-   /* except for RECORD, VOID, ANYARRAY, or ANYELEMENT */
-   if (get_typtype(proc->prorettype) == 'p' &&
+   /* except for RECORD, VOID, or polymorphic */
+   if (get_typtype(proc->prorettype) == TYPTYPE_PSEUDO &&
        proc->prorettype != RECORDOID &&
        proc->prorettype != VOIDOID &&
-       proc->prorettype != ANYARRAYOID &&
-       proc->prorettype != ANYELEMENTOID)
+       !IsPolymorphicType(proc->prorettype))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                 errmsg("SQL functions cannot return type %s",
                        format_type_be(proc->prorettype))));
 
    /* Disallow pseudotypes in arguments */
-   /* except for ANYARRAY or ANYELEMENT */
+   /* except for polymorphic */
    haspolyarg = false;
    for (i = 0; i < proc->pronargs; i++)
    {
-       if (get_typtype(proc->proargtypes.values[i]) == 'p')
+       if (get_typtype(proc->proargtypes.values[i]) == TYPTYPE_PSEUDO)
        {
-           if (proc->proargtypes.values[i] == ANYARRAYOID ||
-               proc->proargtypes.values[i] == ANYELEMENTOID)
+           if (IsPolymorphicType(proc->proargtypes.values[i]))
                haspolyarg = true;
            else
                ereport(ERROR,
index df5e996a7802b7f7fdeef6fa7af4e9d6c437c2e9..efc3b8de0993659f90f0fb8ec7e9571989e90675 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.110 2007/01/05 22:19:25 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.111 2007/04/02 03:49:37 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -75,7 +75,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
     *
     * The representational details are the same as int4 ... it doesn't really
     * matter what they are so long as they are consistent.  Also note that we
-    * give it typtype = 'p' (pseudotype) as extra insurance that it won't be
+    * give it typtype = TYPTYPE_PSEUDO as extra insurance that it won't be
     * mistaken for a usable type.
     */
    i = 0;
@@ -85,7 +85,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
    values[i++] = ObjectIdGetDatum(GetUserId());        /* typowner */
    values[i++] = Int16GetDatum(sizeof(int4));  /* typlen */
    values[i++] = BoolGetDatum(true);   /* typbyval */
-   values[i++] = CharGetDatum('p');    /* typtype */
+   values[i++] = CharGetDatum(TYPTYPE_PSEUDO); /* typtype */
    values[i++] = BoolGetDatum(false);  /* typisdefined */
    values[i++] = CharGetDatum(DEFAULT_TYPDELIM);       /* typdelim */
    values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */
@@ -159,7 +159,7 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
 Oid
 TypeCreate(const char *typeName,
           Oid typeNamespace,
-          Oid relationOid,     /* only for 'c'atalog types */
+          Oid relationOid,     /* only for composite types */
           char relationKind,   /* ditto */
           int16 internalSize,
           char typeType,
@@ -243,7 +243,8 @@ TypeCreate(const char *typeName,
    values[i++] = CharGetDatum(typeType);       /* typtype */
    values[i++] = BoolGetDatum(true);   /* typisdefined */
    values[i++] = CharGetDatum(typDelim);       /* typdelim */
-   values[i++] = ObjectIdGetDatum(typeType == 'c' ? relationOid : InvalidOid); /* typrelid */
+   values[i++] = ObjectIdGetDatum(typeType == TYPTYPE_COMPOSITE ?
+                                  relationOid : InvalidOid); /* typrelid */
    values[i++] = ObjectIdGetDatum(elementType);        /* typelem */
    values[i++] = ObjectIdGetDatum(inputProcedure);     /* typinput */
    values[i++] = ObjectIdGetDatum(outputProcedure);    /* typoutput */
@@ -377,7 +378,7 @@ TypeCreate(const char *typeName,
 void
 GenerateTypeDependencies(Oid typeNamespace,
                         Oid typeObjectId,
-                        Oid relationOid,       /* only for 'c'atalog types */
+                        Oid relationOid,       /* only for composite types */
                         char relationKind,     /* ditto */
                         Oid owner,
                         Oid inputProcedure,
index 27fc681bea399b6369a9d623cb301fd93bda80f5..89ea00d89a603c9bcf6bd35186d54497d8896eb7 100644 (file)
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.42 2007/01/05 22:19:25 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/aggregatecmds.c,v 1.43 2007/04/02 03:49:37 tgl Exp $
  *
  * DESCRIPTION
  *   The "DefineFoo" routines take the parse tree and pick out the
@@ -176,9 +176,8 @@ DefineAggregate(List *name, List *args, bool oldstyle, List *parameters)
     * in some cases (AggregateCreate will check).
     */
    transTypeId = typenameTypeId(NULL, transType);
-   if (get_typtype(transTypeId) == 'p' &&
-       transTypeId != ANYARRAYOID &&
-       transTypeId != ANYELEMENTOID)
+   if (get_typtype(transTypeId) == TYPTYPE_PSEUDO &&
+       !IsPolymorphicType(transTypeId))
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                 errmsg("aggregate transition data type cannot be %s",
index 2971add22a6e03f4ddba3b8d93dc55853f1f7f32..0949feab1f293cf1ae6a61292419f58b50715cb2 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.82 2007/01/22 01:35:20 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.83 2007/04/02 03:49:37 tgl Exp $
  *
  * DESCRIPTION
  *   These routines take the parse tree and pick out the
@@ -1259,13 +1259,13 @@ CreateCast(CreateCastStmt *stmt)
    targettypeid = typenameTypeId(NULL, stmt->targettype);
 
    /* No pseudo-types allowed */
-   if (get_typtype(sourcetypeid) == 'p')
+   if (get_typtype(sourcetypeid) == TYPTYPE_PSEUDO)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("source data type %s is a pseudo-type",
                        TypeNameToString(stmt->sourcetype))));
 
-   if (get_typtype(targettypeid) == 'p')
+   if (get_typtype(targettypeid) == TYPTYPE_PSEUDO)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("target data type %s is a pseudo-type",
index 99f964db74df0ac9fc8a7988171f2035436151b1..5c1b9f6f0e6868616a3e138de545d79af101eecb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.100 2007/02/14 01:58:57 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.101 2007/04/02 03:49:38 tgl Exp $
  *
  * DESCRIPTION
  *   The "DefineFoo" routines take the parse tree and pick out the
@@ -39,6 +39,7 @@
 #include "catalog/indexing.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_enum.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
@@ -205,7 +206,7 @@ DefineType(List *names, List *parameters)
        {
            elemType = typenameTypeId(NULL, defGetTypeName(defel));
            /* disallow arrays of pseudotypes */
-           if (get_typtype(elemType) == 'p')
+           if (get_typtype(elemType) == TYPTYPE_PSEUDO)
                ereport(ERROR,
                        (errcode(ERRCODE_DATATYPE_MISMATCH),
                         errmsg("array element type cannot be %s",
@@ -404,7 +405,7 @@ DefineType(List *names, List *parameters)
                   InvalidOid,  /* relation oid (n/a here) */
                   0,           /* relation kind (ditto) */
                   internalLength,      /* internal size */
-                  'b',         /* type-type (base type) */
+                  TYPTYPE_BASE,        /* type-type (base type) */
                   delimiter,   /* array element delimiter */
                   inputOid,    /* input procedure */
                   outputOid,   /* output procedure */
@@ -438,7 +439,7 @@ DefineType(List *names, List *parameters)
               InvalidOid,      /* relation oid (n/a here) */
               0,               /* relation kind (ditto) */
               -1,              /* internal size */
-              'b',             /* type-type (base type) */
+              TYPTYPE_BASE,    /* type-type (base type) */
               DEFAULT_TYPDELIM,    /* array element delimiter */
               F_ARRAY_IN,      /* input procedure */
               F_ARRAY_OUT,     /* output procedure */
@@ -543,6 +544,14 @@ RemoveTypeById(Oid typeOid)
 
    simple_heap_delete(relation, &tup->t_self);
 
+   /*
+    * If it is an enum, delete the pg_enum entries too; we don't bother
+    * with making dependency entries for those, so it has to be done
+    * "by hand" here.
+    */
+   if (((Form_pg_type) GETSTRUCT(tup))->typtype == TYPTYPE_ENUM)
+       EnumValuesDelete(typeOid);
+
    ReleaseSysCache(tup);
 
    heap_close(relation, RowExclusiveLock);
@@ -620,12 +629,15 @@ DefineDomain(CreateDomainStmt *stmt)
    basetypeMod = typenameTypeMod(NULL, stmt->typename, basetypeoid);
 
    /*
-    * Base type must be a plain base type or another domain.  Domains over
-    * pseudotypes would create a security hole.  Domains over composite types
-    * might be made to work in the future, but not today.
+    * Base type must be a plain base type, another domain or an enum.
+    * Domains over pseudotypes would create a security hole.  Domains
+    * over composite types might be made to work in the future, but not
+    * today.
     */
    typtype = baseType->typtype;
-   if (typtype != 'b' && typtype != 'd')
+   if (typtype != TYPTYPE_BASE &&
+       typtype != TYPTYPE_DOMAIN &&
+       typtype != TYPTYPE_ENUM)
        ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
                 errmsg("\"%s\" is not a valid base type for a domain",
@@ -798,7 +810,7 @@ DefineDomain(CreateDomainStmt *stmt)
                   InvalidOid,  /* relation oid (n/a here) */
                   0,           /* relation kind (ditto) */
                   internalLength,      /* internal size */
-                  'd',         /* type-type (domain type) */
+                  TYPTYPE_DOMAIN,      /* type-type (domain type) */
                   delimiter,   /* array element delimiter */
                   inputProcedure,      /* input procedure */
                   outputProcedure,     /* output procedure */
@@ -907,7 +919,7 @@ RemoveDomain(List *names, DropBehavior behavior, bool missing_ok)
    /* Check that this is actually a domain */
    typtype = ((Form_pg_type) GETSTRUCT(tup))->typtype;
 
-   if (typtype != 'd')
+   if (typtype != TYPTYPE_DOMAIN)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("\"%s\" is not a domain",
@@ -925,6 +937,100 @@ RemoveDomain(List *names, DropBehavior behavior, bool missing_ok)
    performDeletion(&object, behavior);
 }
 
+/*
+ * DefineEnum
+ *     Registers a new enum.
+ */
+void
+DefineEnum(CreateEnumStmt *stmt)
+{
+   char   *enumName;
+   char   *enumArrayName;
+   Oid     enumNamespace;
+   Oid     enumTypeOid;
+   AclResult   aclresult;
+
+   /* Convert list of names to a name and namespace */
+   enumNamespace = QualifiedNameGetCreationNamespace(stmt->typename,
+                                                     &enumName);
+
+   /* Check we have creation rights in target namespace */
+   aclresult = pg_namespace_aclcheck(enumNamespace, GetUserId(), ACL_CREATE);
+   if (aclresult != ACLCHECK_OK)
+       aclcheck_error(aclresult, ACL_KIND_NAMESPACE,
+                      get_namespace_name(enumNamespace));
+
+   /*
+    * Type names must be one character shorter than other names, allowing
+    * room to create the corresponding array type name with prepended "_".
+    */
+   if (strlen(enumName) > (NAMEDATALEN - 2))
+       ereport(ERROR,
+               (errcode(ERRCODE_INVALID_NAME),
+                errmsg("type names must be %d characters or less",
+                       NAMEDATALEN - 2)));
+
+   /* Create the pg_type entry */
+   enumTypeOid = 
+       TypeCreate(enumName,        /* type name */
+                  enumNamespace,   /* namespace */
+                  InvalidOid,      /* relation oid (n/a here) */
+                  0,               /* relation kind (ditto) */
+                  sizeof(Oid),     /* internal size */
+                  TYPTYPE_ENUM,    /* type-type (enum type) */
+                  DEFAULT_TYPDELIM,    /* array element delimiter */
+                  F_ENUM_IN,       /* input procedure */
+                  F_ENUM_OUT,      /* output procedure */
+                  InvalidOid,      /* receive procedure - none */
+                  InvalidOid,      /* send procedure - none */
+                  InvalidOid,      /* typmodin procedure - none */
+                  InvalidOid,      /* typmodout procedure - none */
+                  InvalidOid,      /* analyze procedure - default */
+                  InvalidOid,      /* element type ID */
+                  InvalidOid,      /* base type ID (only for domains) */
+                  NULL,            /* never a default type value */
+                  NULL,            /* binary default isn't sent either */
+                  true,            /* always passed by value */
+                  'i',             /* int alignment */
+                  'p',             /* TOAST strategy always plain */
+                  -1,              /* typMod (Domains only) */
+                  0,               /* Array dimensions of typbasetype */
+                  false);          /* Type NOT NULL */
+
+   /* Enter the enum's values into pg_enum */
+   EnumValuesCreate(enumTypeOid, stmt->vals);
+
+   /* Create array type for enum */
+   enumArrayName = makeArrayTypeName(enumName);
+
+   TypeCreate(enumArrayName,   /* type name */
+              enumNamespace,   /* namespace */
+              InvalidOid,      /* relation oid (n/a here) */
+              0,               /* relation kind (ditto) */
+              -1,              /* internal size */
+              TYPTYPE_BASE,    /* type-type (base type) */
+              DEFAULT_TYPDELIM,    /* array element delimiter */
+              F_ARRAY_IN,      /* input procedure */
+              F_ARRAY_OUT,     /* output procedure */
+              F_ARRAY_RECV,    /* receive procedure */
+              F_ARRAY_SEND,    /* send procedure */
+              InvalidOid,      /* typmodin procedure - none */
+              InvalidOid,      /* typmodout procedure - none */
+              InvalidOid,      /* analyze procedure - default */
+              enumTypeOid,     /* element type ID */
+              InvalidOid,      /* base type ID */
+              NULL,            /* never a default type value */
+              NULL,            /* binary default isn't sent either */
+              false,           /* never passed by value */
+              'i',             /* enums have align i, so do their arrays */
+              'x',             /* ARRAY is always toastable */
+              -1,              /* typMod (Domains only) */
+               0,               /* Array dimensions of typbasetype */
+              false);          /* Type NOT NULL */
+
+   pfree(enumArrayName);
+}
+
 
 /*
  * Find suitable I/O functions for a type.
@@ -1835,7 +1941,7 @@ checkDomainOwner(HeapTuple tup, TypeName *typename)
    Form_pg_type typTup = (Form_pg_type) GETSTRUCT(tup);
 
    /* Check that this is actually a domain */
-   if (typTup->typtype != 'd')
+   if (typTup->typtype != TYPTYPE_DOMAIN)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("\"%s\" is not a domain",
@@ -2021,7 +2127,7 @@ GetDomainConstraints(Oid typeOid)
            elog(ERROR, "cache lookup failed for type %u", typeOid);
        typTup = (Form_pg_type) GETSTRUCT(tup);
 
-       if (typTup->typtype != 'd')
+       if (typTup->typtype != TYPTYPE_DOMAIN)
        {
            /* Not a domain, so done */
            ReleaseSysCache(tup);
@@ -2148,7 +2254,7 @@ AlterTypeOwner(List *names, Oid newOwnerId)
     * free-standing composite type, and not a table's underlying type. We
     * want people to use ALTER TABLE not ALTER TYPE for that case.
     */
-   if (typTup->typtype == 'c' &&
+   if (typTup->typtype == TYPTYPE_COMPOSITE &&
        get_rel_relkind(typTup->typrelid) != RELKIND_COMPOSITE_TYPE)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
@@ -2325,11 +2431,12 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
 
    /* Detect whether type is a composite type (but not a table rowtype) */
    isCompositeType =
-       (typform->typtype == 'c' &&
+       (typform->typtype == TYPTYPE_COMPOSITE &&
         get_rel_relkind(typform->typrelid) == RELKIND_COMPOSITE_TYPE);
 
    /* Enforce not-table-type if requested */
-   if (typform->typtype == 'c' && !isCompositeType && errorOnTableType)
+   if (typform->typtype == TYPTYPE_COMPOSITE && !isCompositeType &&
+       errorOnTableType)
        ereport(ERROR,
                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                 errmsg("%s is a table's row type",
@@ -2376,14 +2483,14 @@ AlterTypeNamespaceInternal(Oid typeOid, Oid nspOid,
    else
    {
        /* If it's a domain, it might have constraints */
-       if (typform->typtype == 'd')
+       if (typform->typtype == TYPTYPE_DOMAIN)
            AlterConstraintNamespaces(typeOid, oldNspOid, nspOid, true);
 
        /*
         * Update dependency on schema, if any --- a table rowtype has not got
         * one.
         */
-       if (typform->typtype != 'c')
+       if (typform->typtype != TYPTYPE_COMPOSITE)
            if (changeDependencyFor(TypeRelationId, typeOid,
                                NamespaceRelationId, oldNspOid, nspOid) != 1)
                elog(ERROR, "failed to change schema dependency for type %s",
index 7e648f437b67435eff63ba2965bde2ece771b206..4781bd903ab0b5b2e1f112c653847cf22e5602cb 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.112 2007/03/13 00:33:40 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/functions.c,v 1.113 2007/04/02 03:49:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -182,7 +182,7 @@ init_sql_fcache(FmgrInfo *finfo)
     */
    rettype = procedureStruct->prorettype;
 
-   if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+   if (IsPolymorphicType(rettype))
    {
        rettype = get_fn_expr_rettype(finfo);
        if (rettype == InvalidOid)      /* this probably should not happen */
@@ -218,7 +218,7 @@ init_sql_fcache(FmgrInfo *finfo)
        {
            Oid         argtype = argOidVect[argnum];
 
-           if (argtype == ANYARRAYOID || argtype == ANYELEMENTOID)
+           if (IsPolymorphicType(argtype))
            {
                argtype = get_fn_expr_argtype(finfo, argnum);
                if (argtype == InvalidOid)
@@ -845,9 +845,9 @@ ShutdownSQLFunction(Datum arg)
  * to be sure that the user is returning the type he claims.
  *
  * For a polymorphic function the passed rettype must be the actual resolved
- * output type of the function; we should never see ANYARRAY or ANYELEMENT
- * as rettype.  (This means we can't check the type during function definition
- * of a polymorphic function.)
+ * output type of the function; we should never see ANYARRAY, ANYENUM or
+ * ANYELEMENT as rettype.  (This means we can't check the type during function
+ * definition of a polymorphic function.)
  *
  * The return value is true if the function returns the entire tuple result
  * of its final SELECT, and false otherwise.  Note that because we allow
@@ -925,7 +925,9 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
 
    fn_typtype = get_typtype(rettype);
 
-   if (fn_typtype == 'b' || fn_typtype == 'd')
+   if (fn_typtype == TYPTYPE_BASE ||
+       fn_typtype == TYPTYPE_DOMAIN ||
+       fn_typtype == TYPTYPE_ENUM)
    {
        /*
         * For base-type returns, the target list should have exactly one
@@ -948,7 +950,7 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
                     errdetail("Actual return type is %s.",
                               format_type_be(restype))));
    }
-   else if (fn_typtype == 'c' || rettype == RECORDOID)
+   else if (fn_typtype == TYPTYPE_COMPOSITE || rettype == RECORDOID)
    {
        /* Returns a rowtype */
        TupleDesc   tupdesc;
@@ -1053,13 +1055,13 @@ check_sql_fn_retval(Oid func_id, Oid rettype, List *queryTreeList,
        /* Report that we are returning entire tuple result */
        return true;
    }
-   else if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+   else if (IsPolymorphicType(rettype))
    {
        /* This should already have been caught ... */
        ereport(ERROR,
                (errcode(ERRCODE_INVALID_FUNCTION_DEFINITION),
                 errmsg("cannot determine result data type"),
-                errdetail("A function returning \"anyarray\" or \"anyelement\" must have at least one argument of either type.")));
+                errdetail("A function returning a polymorphic type must have at least one polymorphic argument.")));
    }
    else
        ereport(ERROR,
index 58e59c60c5a7aeef5851ec8b5475e41c03ceede2..d9e26d1d1960f5e31ad7de6425a252c0c9aca0fe 100644 (file)
@@ -61,7 +61,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.151 2007/02/22 23:44:24 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/executor/nodeAgg.c,v 1.152 2007/04/02 03:49:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1363,8 +1363,8 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 
        /*
         * Get actual datatypes of the inputs.  These could be different from
-        * the agg's declared input types, when the agg accepts ANY, ANYARRAY
-        * or ANYELEMENT.
+        * the agg's declared input types, when the agg accepts ANY or
+        * a polymorphic type.
         */
        i = 0;
        foreach(lc, aggref->args)
@@ -1421,7 +1421,7 @@ ExecInitAgg(Agg *node, EState *estate, int eflags)
 
        /* resolve actual type of transition state, if polymorphic */
        aggtranstype = aggform->aggtranstype;
-       if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
+       if (IsPolymorphicType(aggtranstype))
        {
            /* have to fetch the agg's declared input types... */
            Oid        *declaredArgTypes;
index 198a583f88f4570fd7a0f0be4b2c7b21495f9ef6..9bf57fb87f2b2033d50b6d0292ff4e64aa1fd29c 100644 (file)
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.372 2007/03/27 23:21:09 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.373 2007/04/02 03:49:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2361,6 +2361,17 @@ _copyCompositeTypeStmt(CompositeTypeStmt *from)
    return newnode;
 }
 
+static CreateEnumStmt *
+_copyCreateEnumStmt(CreateEnumStmt *from)
+{
+   CreateEnumStmt *newnode = makeNode(CreateEnumStmt);
+
+   COPY_NODE_FIELD(typename);
+   COPY_NODE_FIELD(vals);
+
+   return newnode;
+}
+
 static ViewStmt *
 _copyViewStmt(ViewStmt *from)
 {
@@ -3312,6 +3323,9 @@ copyObject(void *from)
        case T_CompositeTypeStmt:
            retval = _copyCompositeTypeStmt(from);
            break;
+       case T_CreateEnumStmt:
+           retval = _copyCreateEnumStmt(from);
+           break;
        case T_ViewStmt:
            retval = _copyViewStmt(from);
            break;
index 977f121bc42ecf5f1e3c42c7b99943c851133bcc..1cc0c343dcac94aea81a80b0a22d46fa8348279d 100644 (file)
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.303 2007/03/27 23:21:09 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.304 2007/04/02 03:49:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1186,6 +1186,15 @@ _equalCompositeTypeStmt(CompositeTypeStmt *a, CompositeTypeStmt *b)
    return true;
 }
 
+static bool
+_equalCreateEnumStmt(CreateEnumStmt *a, CreateEnumStmt *b)
+{
+   COMPARE_NODE_FIELD(typename);
+   COMPARE_NODE_FIELD(vals);
+
+   return true;
+}
+
 static bool
 _equalViewStmt(ViewStmt *a, ViewStmt *b)
 {
@@ -2247,6 +2256,9 @@ equal(void *a, void *b)
        case T_CompositeTypeStmt:
            retval = _equalCompositeTypeStmt(a, b);
            break;
+       case T_CreateEnumStmt:
+           retval = _equalCreateEnumStmt(a, b);
+           break;
        case T_ViewStmt:
            retval = _equalViewStmt(a, b);
            break;
index 67652fbfdecb5bbbb476fbd7454778aee58109c5..d4cb3f06b05a71963ab4953142c9c7a76e1d376c 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.240 2007/03/27 23:21:09 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.241 2007/04/02 03:49:38 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -435,7 +435,7 @@ count_agg_clauses_walker(Node *node, AggClauseCounts *counts)
        ReleaseSysCache(aggTuple);
 
        /* resolve actual type of transition state, if polymorphic */
-       if (aggtranstype == ANYARRAYOID || aggtranstype == ANYELEMENTOID)
+       if (IsPolymorphicType(aggtranstype))
        {
            /* have to fetch the agg's declared input types... */
            Oid        *declaredArgTypes;
@@ -2907,8 +2907,7 @@ inline_function(Oid funcid, Oid result_type, List *args,
           funcform->pronargs * sizeof(Oid));
    for (i = 0; i < funcform->pronargs; i++)
    {
-       if (argtypes[i] == ANYARRAYOID ||
-           argtypes[i] == ANYELEMENTOID)
+       if (IsPolymorphicType(argtypes[i]))
        {
            argtypes[i] = exprType((Node *) list_nth(args, i));
        }
index 8cad738a357e7c1c28b436ff33084f77a39ed27c..767129fb924c5c626b02b52430af3cf50e23a5eb 100644 (file)
@@ -11,7 +11,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.584 2007/03/26 16:58:39 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.585 2007/04/02 03:49:38 tgl Exp $
  *
  * HISTORY
  *   AUTHOR            DATE            MAJOR EVENT
@@ -248,6 +248,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
                TableFuncElementList opt_type_modifiers
                prep_type_clause
                execute_param_clause using_clause returning_clause
+               enum_val_list
 
 %type <range>  OptTempTableName
 %type <into>   into_clause create_as_target
@@ -383,7 +384,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
    DEFERRABLE DEFERRED DEFINER DELETE_P DELIMITER DELIMITERS
    DESC DISABLE_P DISTINCT DO DOCUMENT_P DOMAIN_P DOUBLE_P DROP
 
-   EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ESCAPE EXCEPT EXCLUDING
+   EACH ELSE ENABLE_P ENCODING ENCRYPTED END_P ENUM_P ESCAPE EXCEPT EXCLUDING
    EXCLUSIVE EXECUTE EXISTS EXPLAIN EXTERNAL EXTRACT
 
    FALSE_P FAMILY FETCH FIRST_P FLOAT_P FOR FORCE FOREIGN FORWARD
@@ -2922,6 +2923,13 @@ DefineStmt:
                    n->coldeflist = $6;
                    $$ = (Node *)n;
                }
+           | CREATE TYPE_P any_name AS ENUM_P '(' enum_val_list ')'
+               {
+                   CreateEnumStmt *n = makeNode(CreateEnumStmt);
+                   n->typename = $3;
+                   n->vals = $7;
+                   $$ = (Node *)n;
+               }
        ;
 
 definition: '(' def_list ')'                       { $$ = $2; }
@@ -2966,6 +2974,12 @@ old_aggr_elem:  IDENT '=' def_arg
                }
        ;
 
+enum_val_list: Sconst
+               { $$ = list_make1(makeString($1)); }
+           | enum_val_list ',' Sconst
+               { $$ = lappend($1, makeString($3)); }
+       ;
+
 
 /*****************************************************************************
  *
@@ -8760,6 +8774,7 @@ unreserved_keyword:
            | ENABLE_P
            | ENCODING
            | ENCRYPTED
+           | ENUM_P
            | ESCAPE
            | EXCLUDING
            | EXCLUSIVE
index 9be91e7e6e62a43cb0b176d491669a56c93568c8..9f6cf1e20e4d7bfd35bc3f851f22c465a8c55a98 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.185 2007/03/19 23:38:29 wieck Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/keywords.c,v 1.186 2007/04/02 03:49:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -136,6 +136,7 @@ static const ScanKeyword ScanKeywords[] = {
    {"encoding", ENCODING},
    {"encrypted", ENCRYPTED},
    {"end", END_P},
+   {"enum", ENUM_P},
    {"escape", ESCAPE},
    {"except", EXCEPT},
    {"excluding", EXCLUDING},
index fbc83870a5c43815731c6ffa68c06e1a0e8320da..c232dee4a58dc49af008dcaab5f5afc24e655c70 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.152 2007/03/27 23:21:10 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.153 2007/04/02 03:49:38 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -132,7 +132,8 @@ coerce_type(ParseState *pstate, Node *node,
    }
    if (targetTypeId == ANYOID ||
        targetTypeId == ANYELEMENTOID ||
-       (targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID))
+       (targetTypeId == ANYARRAYOID && inputTypeId != UNKNOWNOID) ||
+       (targetTypeId == ANYENUMOID && inputTypeId != UNKNOWNOID))
    {
        /*
         * Assume can_coerce_type verified that implicit coercion is okay.
@@ -143,7 +144,8 @@ coerce_type(ParseState *pstate, Node *node,
         * since an UNKNOWN value is still a perfectly valid Datum.  However
         * an UNKNOWN value is definitely *not* an array, and so we mustn't
         * accept it for ANYARRAY.  (Instead, we will call anyarray_in below,
-        * which will produce an error.)
+        * which will produce an error.)  Likewise, UNKNOWN input is no good
+        * for ANYENUM.
         *
         * NB: we do NOT want a RelabelType here.
         */
@@ -406,9 +408,8 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
        if (targetTypeId == ANYOID)
            continue;
 
-       /* accept if target is ANYARRAY or ANYELEMENT, for now */
-       if (targetTypeId == ANYARRAYOID ||
-           targetTypeId == ANYELEMENTOID)
+       /* accept if target is polymorphic, for now */
+       if (IsPolymorphicType(targetTypeId))
        {
            have_generics = true;       /* do more checking later */
            continue;
@@ -1048,6 +1049,9 @@ coerce_to_common_type(ParseState *pstate, Node *node,
  * 3) If there are arguments of both ANYELEMENT and ANYARRAY, make sure
  *   the actual ANYELEMENT datatype is in fact the element type for
  *   the actual ANYARRAY datatype.
+ * 4) ANYENUM is treated the same as ANYELEMENT except that if it is used
+ *   (alone or in combination with plain ANYELEMENT), we add the extra
+ *   condition that the ANYELEMENT type must be an enum.
  *
  * If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT
  * or ANYARRAY argument, assume it is okay.
@@ -1070,6 +1074,7 @@ check_generic_type_consistency(Oid *actual_arg_types,
    Oid         array_typeid = InvalidOid;
    Oid         array_typelem;
    bool        have_anyelement = false;
+   bool        have_anyenum = false;
 
    /*
     * Loop through the arguments to see if we have any that are ANYARRAY or
@@ -1079,9 +1084,12 @@ check_generic_type_consistency(Oid *actual_arg_types,
    {
        Oid         actual_type = actual_arg_types[j];
 
-       if (declared_arg_types[j] == ANYELEMENTOID)
+       if (declared_arg_types[j] == ANYELEMENTOID ||
+           declared_arg_types[j] == ANYENUMOID)
        {
            have_anyelement = true;
+           if (declared_arg_types[j] == ANYENUMOID)
+               have_anyenum = true;
            if (actual_type == UNKNOWNOID)
                continue;
            if (OidIsValid(elem_typeid) && actual_type != elem_typeid)
@@ -1127,6 +1135,13 @@ check_generic_type_consistency(Oid *actual_arg_types,
        }
    }
 
+   if (have_anyenum)
+   {
+       /* require the element type to be an enum */
+       if (!type_is_enum(elem_typeid))
+           return false;
+   }
+
    /* Looks valid */
    return true;
 }
@@ -1136,18 +1151,18 @@ check_generic_type_consistency(Oid *actual_arg_types,
  *     Make sure a polymorphic function is legally callable, and
  *     deduce actual argument and result types.
  *
- * If ANYARRAY or ANYELEMENT is used for a function's arguments or
+ * If ANYARRAY, ANYELEMENT, or ANYENUM is used for a function's arguments or
  * return type, we make sure the actual data types are consistent with
  * each other. The argument consistency rules are shown above for
  * check_generic_type_consistency().
  *
- * If we have UNKNOWN input (ie, an untyped literal) for any ANYELEMENT
- * or ANYARRAY argument, we attempt to deduce the actual type it should
- * have.  If successful, we alter that position of declared_arg_types[]
- * so that make_fn_arguments will coerce the literal to the right thing.
+ * If we have UNKNOWN input (ie, an untyped literal) for any polymorphic
+ * argument, we attempt to deduce the actual type it should have.  If
+ * successful, we alter that position of declared_arg_types[] so that
+ * make_fn_arguments will coerce the literal to the right thing.
  *
  * Rules are applied to the function's return type (possibly altering it)
- * if it is declared ANYARRAY or ANYELEMENT:
+ * if it is declared as a polymorphic type:
  *
  * 1) If return type is ANYARRAY, and any argument is ANYARRAY, use the
  *   argument's actual type as the function's return type.
@@ -1167,6 +1182,9 @@ check_generic_type_consistency(Oid *actual_arg_types,
  * 6) If return type is ANYELEMENT, no argument is ANYARRAY or ANYELEMENT,
  *   generate an ERROR. This condition is prevented by CREATE FUNCTION
  *   and is therefore not expected here.
+ * 7) ANYENUM is treated the same as ANYELEMENT except that if it is used
+ *   (alone or in combination with plain ANYELEMENT), we add the extra
+ *   condition that the ANYELEMENT type must be an enum.
  */
 Oid
 enforce_generic_type_consistency(Oid *actual_arg_types,
@@ -1180,7 +1198,9 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
    Oid         elem_typeid = InvalidOid;
    Oid         array_typeid = InvalidOid;
    Oid         array_typelem;
-   bool        have_anyelement = (rettype == ANYELEMENTOID);
+   bool        have_anyelement = (rettype == ANYELEMENTOID ||
+                                  rettype == ANYENUMOID);
+   bool        have_anyenum = (rettype == ANYENUMOID);
 
    /*
     * Loop through the arguments to see if we have any that are ANYARRAY or
@@ -1190,9 +1210,12 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
    {
        Oid         actual_type = actual_arg_types[j];
 
-       if (declared_arg_types[j] == ANYELEMENTOID)
+       if (declared_arg_types[j] == ANYELEMENTOID ||
+           declared_arg_types[j] == ANYENUMOID)
        {
            have_generics = have_anyelement = true;
+           if (declared_arg_types[j] == ANYENUMOID)
+               have_anyenum = true;
            if (actual_type == UNKNOWNOID)
            {
                have_unknowns = true;
@@ -1227,8 +1250,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
    }
 
    /*
-    * Fast Track: if none of the arguments are ANYARRAY or ANYELEMENT, return
-    * the unmodified rettype.
+    * Fast Track: if none of the arguments are polymorphic, return the
+    * unmodified rettype.  We assume it can't be polymorphic either.
     */
    if (!have_generics)
        return rettype;
@@ -1274,7 +1297,17 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
        /* Only way to get here is if all the generic args are UNKNOWN */
        ereport(ERROR,
                (errcode(ERRCODE_DATATYPE_MISMATCH),
-                errmsg("could not determine anyarray/anyelement type because input has type \"unknown\"")));
+                errmsg("could not determine polymorphic type because input has type \"unknown\"")));
+   }
+
+   if (have_anyenum)
+   {
+       /* require the element type to be an enum */
+       if (!type_is_enum(elem_typeid))
+           ereport(ERROR,
+                   (errcode(ERRCODE_DATATYPE_MISMATCH),
+                    errmsg("type matched to anyenum is not an enum type: %s",
+                           format_type_be(elem_typeid))));
    }
 
    /*
@@ -1289,7 +1322,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
            if (actual_type != UNKNOWNOID)
                continue;
 
-           if (declared_arg_types[j] == ANYELEMENTOID)
+           if (declared_arg_types[j] == ANYELEMENTOID ||
+               declared_arg_types[j] == ANYENUMOID)
                declared_arg_types[j] = elem_typeid;
            else if (declared_arg_types[j] == ANYARRAYOID)
            {
@@ -1307,7 +1341,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
        }
    }
 
-   /* if we return ANYARRAYOID use the appropriate argument type */
+   /* if we return ANYARRAY use the appropriate argument type */
    if (rettype == ANYARRAYOID)
    {
        if (!OidIsValid(array_typeid))
@@ -1322,8 +1356,8 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
        return array_typeid;
    }
 
-   /* if we return ANYELEMENTOID use the appropriate argument type */
-   if (rettype == ANYELEMENTOID)
+   /* if we return ANYELEMENT use the appropriate argument type */
+   if (rettype == ANYELEMENTOID || rettype == ANYENUMOID)
        return elem_typeid;
 
    /* we don't return a generic type; send back the original return type */
@@ -1333,7 +1367,7 @@ enforce_generic_type_consistency(Oid *actual_arg_types,
 /*
  * resolve_generic_type()
  *     Deduce an individual actual datatype on the assumption that
- *     the rules for ANYARRAY/ANYELEMENT are being followed.
+ *     the rules for polymorphic types are being followed.
  *
  * declared_type is the declared datatype we want to resolve.
  * context_actual_type is the actual input datatype to some argument
@@ -1362,7 +1396,8 @@ resolve_generic_type(Oid declared_type,
                                format_type_be(context_actual_type))));
            return context_actual_type;
        }
-       else if (context_declared_type == ANYELEMENTOID)
+       else if (context_declared_type == ANYELEMENTOID ||
+                context_declared_type == ANYENUMOID)
        {
            /* Use the array type corresponding to actual type */
            Oid         array_typeid = get_array_type(context_actual_type);
@@ -1375,7 +1410,7 @@ resolve_generic_type(Oid declared_type,
            return array_typeid;
        }
    }
-   else if (declared_type == ANYELEMENTOID)
+   else if (declared_type == ANYELEMENTOID || declared_type == ANYENUMOID)
    {
        if (context_declared_type == ANYARRAYOID)
        {
@@ -1389,7 +1424,8 @@ resolve_generic_type(Oid declared_type,
                                format_type_be(context_actual_type))));
            return array_typelem;
        }
-       else if (context_declared_type == ANYELEMENTOID)
+       else if (context_declared_type == ANYELEMENTOID ||
+                context_declared_type == ANYENUMOID)
        {
            /* Use the actual type; it doesn't matter if array or not */
            return context_actual_type;
@@ -1402,7 +1438,7 @@ resolve_generic_type(Oid declared_type,
    }
    /* If we get here, declared_type is polymorphic and context isn't */
    /* NB: this is a calling-code logic error, not a user error */
-   elog(ERROR, "could not determine ANYARRAY/ANYELEMENT type because context isn't polymorphic");
+   elog(ERROR, "could not determine polymorphic type because context isn't polymorphic");
    return InvalidOid;          /* keep compiler quiet */
 }
 
@@ -1502,6 +1538,7 @@ TypeCategory(Oid inType)
        case (INTERNALOID):
        case (OPAQUEOID):
        case (ANYELEMENTOID):
+       case (ANYENUMOID):
            result = GENERIC_TYPE;
            break;
 
@@ -1647,6 +1684,11 @@ IsBinaryCoercible(Oid srctype, Oid targettype)
        if (get_element_type(srctype) != InvalidOid)
            return true;
 
+   /* Also accept any enum type as coercible to ANYENUM */
+   if (targettype == ANYENUMOID)
+       if (type_is_enum(srctype))
+           return true;
+
    /* Else look in pg_cast */
    tuple = SearchSysCache(CASTSOURCETARGET,
                           ObjectIdGetDatum(srctype),
@@ -1777,6 +1819,22 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
                result = true;
            }
        }
+
+       /*
+        * If we still haven't found a possibility, check for enums,
+        * and retry looking for a cast to or from ANYENUM.  But don't
+        * mistakenly conclude that ANYENUM-to-some-enum-type is a
+        * trivial cast.
+        */
+       if (!result)
+       {
+           if (type_is_enum(sourceTypeId))
+               result = find_coercion_pathway(targetTypeId, ANYENUMOID,
+                                              ccontext, funcid, arrayCoerce);
+           else if (sourceTypeId != ANYENUMOID && type_is_enum(targetTypeId))
+               result = find_coercion_pathway(ANYENUMOID, sourceTypeId,
+                                              ccontext, funcid, arrayCoerce);
+       }
    }
 
    return result;
@@ -1813,7 +1871,7 @@ find_typmod_coercion_function(Oid typeId,
    /* Check for a varlena array type (and not a domain) */
    if (typeForm->typelem != InvalidOid &&
        typeForm->typlen == -1 &&
-       typeForm->typtype != 'd')
+       typeForm->typtype != TYPTYPE_DOMAIN)
    {
        /* Yes, switch our attention to the element type */
        typeId = typeForm->typelem;
index 45e488e33f5822cad75fc4a2296807c673919e0d..d9a6af829621322ae4169f534a2f26fbb735b48f 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.215 2007/03/27 23:21:10 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.216 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1627,7 +1627,7 @@ transformWholeRowRef(ParseState *pstate, char *schemaname, char *relname,
            break;
        case RTE_FUNCTION:
            toid = exprType(rte->funcexpr);
-           if (toid == RECORDOID || get_typtype(toid) == 'c')
+           if (type_is_rowtype(toid))
            {
                /* func returns composite; same as relation case */
                result = (Node *) makeVar(vnum,
index c8d295047b39587436253533ecc5a93dd4c28826..42bd04fa01ebee35ccd4671acd4ff6138d05a2a5 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.94 2007/02/01 19:10:27 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.95 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -886,8 +886,8 @@ make_scalar_array_op(ParseState *pstate, List *opname,
    declared_arg_types[1] = opform->oprright;
 
    /*
-    * enforce consistency with ANYARRAY and ANYELEMENT argument and return
-    * types, possibly adjusting return type or declared_arg_types (which will
+    * enforce consistency with polymorphic argument and return types,
+    * possibly adjusting return type or declared_arg_types (which will
     * be used as the cast destination by make_fn_arguments)
     */
    rettype = enforce_generic_type_consistency(actual_arg_types,
@@ -911,15 +911,25 @@ make_scalar_array_op(ParseState *pstate, List *opname,
 
    /*
     * Now switch back to the array type on the right, arranging for any
-    * needed cast to be applied.
+    * needed cast to be applied.  Beware of polymorphic operators here;
+    * enforce_generic_type_consistency may or may not have replaced a
+    * polymorphic type with a real one.
     */
-   res_atypeId = get_array_type(declared_arg_types[1]);
-   if (!OidIsValid(res_atypeId))
-       ereport(ERROR,
-               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                errmsg("could not find array type for data type %s",
-                       format_type_be(declared_arg_types[1])),
-                parser_errposition(pstate, location)));
+   if (IsPolymorphicType(declared_arg_types[1]))
+   {
+       /* assume the actual array type is OK */
+       res_atypeId = atypeId;
+   }
+   else
+   {
+       res_atypeId = get_array_type(declared_arg_types[1]);
+       if (!OidIsValid(res_atypeId))
+           ereport(ERROR,
+                   (errcode(ERRCODE_UNDEFINED_OBJECT),
+                    errmsg("could not find array type for data type %s",
+                           format_type_be(declared_arg_types[1])),
+                    parser_errposition(pstate, location)));
+   }
    actual_arg_types[1] = atypeId;
    declared_arg_types[1] = res_atypeId;
 
@@ -986,8 +996,8 @@ make_op_expr(ParseState *pstate, Operator op,
    }
 
    /*
-    * enforce consistency with ANYARRAY and ANYELEMENT argument and return
-    * types, possibly adjusting return type or declared_arg_types (which will
+    * enforce consistency with polymorphic argument and return types,
+    * possibly adjusting return type or declared_arg_types (which will
     * be used as the cast destination by make_fn_arguments)
     */
    rettype = enforce_generic_type_consistency(actual_arg_types,
index c0280f5ea2c0e8cdcf1adc63e52e873657e5ee2a..ebb86f8bba914db11f07d915cbd9dd97933a2bb3 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.87 2007/01/05 22:19:34 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.88 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -398,16 +398,6 @@ typeByVal(Type t)
    return typ->typbyval;
 }
 
-/* given type (as type struct), return the value of its 'typtype' attribute.*/
-char
-typeTypType(Type t)
-{
-   Form_pg_type typ;
-
-   typ = (Form_pg_type) GETSTRUCT(t);
-   return typ->typtype;
-}
-
 /* given type (as type struct), return the name of type */
 char *
 typeTypeName(Type t)
index daf23fd0fe5586917067aa77a2e3d5f0dec38e88..864b6d1dd63b42244274fb48aecb88b4eccc814e 100644 (file)
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.275 2007/03/26 16:58:39 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/tcop/utility.c,v 1.276 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -337,6 +337,7 @@ check_xact_readonly(Node *parsetree)
        case T_CreateTableSpaceStmt:
        case T_CreateTrigStmt:
        case T_CompositeTypeStmt:
+       case T_CreateEnumStmt:
        case T_ViewStmt:
        case T_DropCastStmt:
        case T_DropStmt:
@@ -779,6 +780,10 @@ ProcessUtility(Node *parsetree,
            }
            break;
 
+       case T_CreateEnumStmt:          /* CREATE TYPE (enum) */
+           DefineEnum((CreateEnumStmt *) parsetree);
+           break;
+
        case T_ViewStmt:                /* CREATE VIEW */
            DefineView((ViewStmt *) parsetree, queryString);
            break;
@@ -1640,6 +1645,10 @@ CreateCommandTag(Node *parsetree)
            tag = "CREATE TYPE";
            break;
 
+       case T_CreateEnumStmt:
+           tag = "CREATE TYPE";
+           break;
+
        case T_ViewStmt:
            tag = "CREATE VIEW";
            break;
@@ -2075,6 +2084,10 @@ GetCommandLogLevel(Node *parsetree)
            lev = LOGSTMT_DDL;
            break;
 
+       case T_CreateEnumStmt:
+           lev = LOGSTMT_DDL;
+           break;
+
        case T_ViewStmt:
            lev = LOGSTMT_DDL;
            break;
index ad3b848607b9a768e1e209e5baaa67fa34e4e6f4..12d158a49bd7a69fe43ab2b307558985d8e2709e 100644 (file)
@@ -1,7 +1,7 @@
 #
 # Makefile for utils/adt
 #
-# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.63 2007/01/28 16:16:52 neilc Exp $
+# $PostgreSQL: pgsql/src/backend/utils/adt/Makefile,v 1.64 2007/04/02 03:49:39 tgl Exp $
 #
 
 subdir = src/backend/utils/adt
@@ -17,7 +17,7 @@ endif
 
 OBJS = acl.o arrayfuncs.o array_userfuncs.o arrayutils.o bool.o \
    cash.o char.o date.o datetime.o datum.o domains.o \
-   float.o format_type.o \
+   enum.o float.o format_type.o \
    geo_ops.o geo_selfuncs.o int.o int8.o like.o lockfuncs.o \
    misc.o nabstime.o name.o not_in.o numeric.o numutils.o \
    oid.o oracle_compat.o pseudotypes.o rowtypes.o \
diff --git a/src/backend/utils/adt/enum.c b/src/backend/utils/adt/enum.c
new file mode 100644 (file)
index 0000000..288894e
--- /dev/null
@@ -0,0 +1,409 @@
+/*-------------------------------------------------------------------------
+ *
+ * enum.c
+ *    I/O functions, operators, aggregates etc for enum types
+ *
+ * Copyright (c) 2006-2007, PostgreSQL Global Development Group
+ *
+ *
+ * IDENTIFICATION
+ *    $PostgreSQL: pgsql/src/backend/utils/adt/enum.c,v 1.1 2007/04/02 03:49:39 tgl Exp $
+ *
+ *-------------------------------------------------------------------------
+ */
+#include "postgres.h"
+
+#include "catalog/pg_enum.h"
+#include "fmgr.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
+#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+
+static Oid cstring_enum(char *name, Oid enumtypoid);
+static char *enum_cstring(Oid enumval);
+static ArrayType *enum_range_internal(Oid enumtypoid, Oid lower, Oid upper);
+static int enum_elem_cmp(const void *left, const void *right);
+
+
+/* Basic I/O support */
+
+Datum
+enum_in(PG_FUNCTION_ARGS)
+{
+    char *name = PG_GETARG_CSTRING(0);
+    Oid enumtypoid = PG_GETARG_OID(1);
+
+    PG_RETURN_OID(cstring_enum(name, enumtypoid));
+}
+
+/* guts of enum_in and text-to-enum */
+static Oid
+cstring_enum(char *name, Oid enumtypoid)
+{
+   HeapTuple tup;
+   Oid enumoid;
+
+   tup = SearchSysCache(ENUMTYPOIDNAME,
+                        ObjectIdGetDatum(enumtypoid),
+                        CStringGetDatum(name),
+                        0, 0);
+    if (tup == NULL)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),
+                 errmsg("invalid input value for enum %s: \"%s\"",
+                       format_type_be(enumtypoid),
+                        name)));
+
+   enumoid = HeapTupleGetOid(tup);
+
+   ReleaseSysCache(tup);
+   return enumoid;
+}
+
+Datum
+enum_out(PG_FUNCTION_ARGS)
+{
+    Oid enumoid = PG_GETARG_OID(0);
+
+    PG_RETURN_CSTRING(enum_cstring(enumoid));
+}
+
+/* guts of enum_out and enum-to-text */
+static char *
+enum_cstring(Oid enumval)
+{
+   HeapTuple tup;
+   Form_pg_enum en;
+   char *label;
+
+   tup = SearchSysCache(ENUMOID,
+                        ObjectIdGetDatum(enumval),
+                        0, 0, 0);
+    if (tup == NULL)
+        ereport(ERROR,
+                (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION),
+                 errmsg("invalid internal value for enum: %u",
+                        enumval)));
+   en = (Form_pg_enum) GETSTRUCT(tup);
+
+   label = pstrdup(NameStr(en->enumlabel));
+
+   ReleaseSysCache(tup);
+   return label;
+}
+
+/* Comparison functions and related */
+
+Datum
+enum_lt(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   PG_RETURN_BOOL(a < b);
+}
+
+Datum
+enum_le(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   PG_RETURN_BOOL(a <= b);
+}
+
+Datum
+enum_eq(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   PG_RETURN_BOOL(a == b);
+}
+
+Datum
+enum_ne(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   PG_RETURN_BOOL(a != b);
+}
+
+Datum
+enum_ge(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   PG_RETURN_BOOL(a >= b);
+}
+
+Datum
+enum_gt(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   PG_RETURN_BOOL(a > b);
+}
+
+Datum
+enum_smaller(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   PG_RETURN_OID(a <= b ? a : b);
+}
+
+Datum
+enum_larger(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   PG_RETURN_OID(a >= b ? a : b);
+}
+
+Datum
+enum_cmp(PG_FUNCTION_ARGS)
+{
+   Oid a = PG_GETARG_OID(0);
+   Oid b = PG_GETARG_OID(1);
+
+   if (a > b)
+       PG_RETURN_INT32(1);
+   else if (a == b)
+       PG_RETURN_INT32(0);
+   else
+       PG_RETURN_INT32(-1);
+}
+
+/* Casts between text and enum */
+
+Datum
+enum_text(PG_FUNCTION_ARGS)
+{
+   Oid enumval = PG_GETARG_OID(0);
+   text *result;
+   char *cstr;
+   int len;
+
+   cstr = enum_cstring(enumval);
+   len = strlen(cstr);
+   result = (text *) palloc(VARHDRSZ + len);
+   SET_VARSIZE(result, VARHDRSZ + len);
+   memcpy(VARDATA(result), cstr, len);
+   pfree(cstr);
+   PG_RETURN_TEXT_P(result);
+}
+
+Datum
+text_enum(PG_FUNCTION_ARGS)
+{
+   text *textval = PG_GETARG_TEXT_P(0);
+   Oid enumtypoid;
+   char *str;
+
+   /*
+    * We rely on being able to get the specific enum type from the calling
+    * expression tree.
+    */
+   enumtypoid = get_fn_expr_rettype(fcinfo->flinfo);
+   if (enumtypoid == InvalidOid)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("could not determine actual enum type")));
+
+   str = DatumGetCString(DirectFunctionCall1(textout,
+                                             PointerGetDatum(textval)));
+    PG_RETURN_OID(cstring_enum(str, enumtypoid));
+}
+
+/* Enum programming support functions */
+
+Datum
+enum_first(PG_FUNCTION_ARGS)
+{
+   Oid enumtypoid;
+   Oid min = InvalidOid;
+   CatCList *list;
+   int num, i;
+
+   /*
+    * We rely on being able to get the specific enum type from the calling
+    * expression tree.  Notice that the actual value of the argument isn't
+    * examined at all; in particular it might be NULL.
+    */
+   enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
+   if (enumtypoid == InvalidOid)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("could not determine actual enum type")));
+
+   list = SearchSysCacheList(ENUMTYPOIDNAME, 1,
+                             ObjectIdGetDatum(enumtypoid),
+                             0, 0, 0);
+   num = list->n_members;
+   for (i = 0; i < num; i++)
+   {
+       Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
+       if (!OidIsValid(min) || valoid < min)
+           min = valoid;
+   }
+
+   ReleaseCatCacheList(list);
+
+    if (!OidIsValid(min))      /* should not happen */
+       elog(ERROR, "no values found for enum %s",
+            format_type_be(enumtypoid));
+
+   PG_RETURN_OID(min);
+}
+
+Datum
+enum_last(PG_FUNCTION_ARGS)
+{
+    Oid enumtypoid;
+    Oid max = InvalidOid;
+    CatCList *list;
+    int num, i;
+
+   /*
+    * We rely on being able to get the specific enum type from the calling
+    * expression tree.  Notice that the actual value of the argument isn't
+    * examined at all; in particular it might be NULL.
+    */
+   enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
+   if (enumtypoid == InvalidOid)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("could not determine actual enum type")));
+
+    list = SearchSysCacheList(ENUMTYPOIDNAME, 1,
+                              ObjectIdGetDatum(enumtypoid),
+                             0, 0, 0);
+    num = list->n_members;
+    for (i = 0; i < num; i++)
+    {
+       Oid valoid = HeapTupleHeaderGetOid(list->members[i]->tuple.t_data);
+        if(!OidIsValid(max) || valoid > max)
+            max = valoid;
+    }
+
+   ReleaseCatCacheList(list);
+
+    if (!OidIsValid(max))      /* should not happen */
+       elog(ERROR, "no values found for enum %s",
+            format_type_be(enumtypoid));
+
+    PG_RETURN_OID(max);
+}
+
+/* 2-argument variant of enum_range */
+Datum
+enum_range_bounds(PG_FUNCTION_ARGS)
+{
+   Oid lower;
+   Oid upper;
+   Oid enumtypoid;
+
+   if (PG_ARGISNULL(0))
+       lower = InvalidOid;
+   else
+       lower = PG_GETARG_OID(0);
+   if (PG_ARGISNULL(1))
+       upper = InvalidOid;
+   else
+       upper = PG_GETARG_OID(1);
+
+   /*
+    * We rely on being able to get the specific enum type from the calling
+    * expression tree.  The generic type mechanism should have ensured that
+    * both are of the same type.
+    */
+   enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
+   if (enumtypoid == InvalidOid)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("could not determine actual enum type")));
+
+   PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid, lower, upper));
+}
+
+/* 1-argument variant of enum_range */
+Datum
+enum_range_all(PG_FUNCTION_ARGS)
+{
+   Oid enumtypoid;
+
+   /*
+    * We rely on being able to get the specific enum type from the calling
+    * expression tree.  Notice that the actual value of the argument isn't
+    * examined at all; in particular it might be NULL.
+    */
+   enumtypoid = get_fn_expr_argtype(fcinfo->flinfo, 0);
+   if (enumtypoid == InvalidOid)
+       ereport(ERROR,
+               (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                errmsg("could not determine actual enum type")));
+
+   PG_RETURN_ARRAYTYPE_P(enum_range_internal(enumtypoid,
+                                             InvalidOid, InvalidOid));
+}
+
+static ArrayType *
+enum_range_internal(Oid enumtypoid, Oid lower, Oid upper)
+{
+   ArrayType *result;
+    CatCList *list;
+    int total, i, j;
+    Datum *elems;
+
+   list = SearchSysCacheList(ENUMTYPOIDNAME, 1,
+                              ObjectIdGetDatum(enumtypoid),
+                             0, 0, 0);
+   total = list->n_members;
+
+   elems = (Datum *) palloc(total * sizeof(Datum));
+
+   j = 0;
+    for (i = 0; i < total; i++)
+    {
+       Oid val = HeapTupleGetOid(&(list->members[i]->tuple));
+
+       if ((!OidIsValid(lower) || lower <= val) &&
+           (!OidIsValid(upper) || val <= upper))
+            elems[j++] = ObjectIdGetDatum(val);
+    }
+
+   /* shouldn't need the cache anymore */
+   ReleaseCatCacheList(list);
+
+   /* sort results into OID order */
+   qsort(elems, j, sizeof(Datum), enum_elem_cmp);
+
+   /* note this hardwires some details about the representation of Oid */
+   result = construct_array(elems, j, enumtypoid, sizeof(Oid), true, 'i');
+
+   pfree(elems);
+
+   return result;
+}
+
+/* qsort comparison function for Datums that are OIDs */
+static int
+enum_elem_cmp(const void *left, const void *right)
+{
+   Oid l = DatumGetObjectId(*((const Datum *) left));
+   Oid r = DatumGetObjectId(*((const Datum *) right));
+
+   if (l < r)
+       return -1;
+   if (l > r)
+       return 1;
+   return 0;
+}
index 898534477b238f35c1791fe79efc6c2c959d292d..f7879bafc504c56665fcdcdf3b4f5f754d51e1a1 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.46 2007/01/05 22:19:40 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.47 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -148,7 +148,7 @@ format_type_internal(Oid type_oid, int32 typemod,
 
    if (array_base_type != InvalidOid &&
        typeform->typstorage != 'p' &&
-       typeform->typtype != 'd')
+       typeform->typtype != TYPTYPE_DOMAIN)
    {
        /* Switch our attention to the array element type */
        ReleaseSysCache(tuple);
index 3bd5ff66d38b91a549d3130c9a550ba2c0e8964b..fc9f3324827111127dc43fe9f03fd034c6aa1b30 100644 (file)
@@ -16,7 +16,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.18 2007/01/05 22:19:41 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.19 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -163,6 +163,31 @@ anyarray_send(PG_FUNCTION_ARGS)
 }
 
 
+/*
+ * anyenum_in      - input routine for pseudo-type ANYENUM.
+ */
+Datum
+anyenum_in(PG_FUNCTION_ARGS)
+{
+   ereport(ERROR,
+           (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+            errmsg("cannot accept a value of type anyenum")));
+
+   PG_RETURN_VOID();           /* keep compiler quiet */
+}
+
+/*
+ * anyenum_out     - output routine for pseudo-type ANYENUM.
+ *
+ * We may as well allow this, since enum_out will in fact work.
+ */
+Datum
+anyenum_out(PG_FUNCTION_ARGS)
+{
+   return enum_out(fcinfo);
+}
+
+
 /*
  * void_in     - input routine for pseudo-type VOID.
  *
index 55be759c9e331bdfd2a8cdfb58639d90b2f209c8..4fea99128343cc0be9f0b0d5a40b34c5f96357eb 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.38 2007/04/01 09:00:25 petere Exp $
+ * $PostgreSQL: pgsql/src/backend/utils/adt/xml.c,v 1.39 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -2648,7 +2648,7 @@ map_sql_type_to_xml_name(Oid typeoid, int typmod)
            Form_pg_type typtuple = (Form_pg_type) GETSTRUCT(tuple);
 
            appendStringInfoString(&result,
-                                  map_multipart_sql_identifier_to_xml_name((typtuple->typtype == 'd') ? "Domain" : "UDT",
+                                  map_multipart_sql_identifier_to_xml_name((typtuple->typtype == TYPTYPE_DOMAIN) ? "Domain" : "UDT",
                                                                            get_database_name(MyDatabaseId),
                                                                            get_namespace_name(typtuple->typnamespace),
                                                                            NameStr(typtuple->typname)));
@@ -2877,7 +2877,7 @@ map_sql_type_to_xmlschema_type(Oid typeoid, int typmod)
                                 break;
 
            default:
-               if (get_typtype(typeoid) == 'd')
+               if (get_typtype(typeoid) == TYPTYPE_DOMAIN)
                {
                    Oid base_typeoid;
                    int32 base_typmod = -1;
index 69f1e0c2422bc8ee49507dbde034b87e64cec234..391870c3a6909f89ee79c9fad47e95a0afa3195a 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.150 2007/03/19 16:30:31 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.151 2007/04/02 03:49:39 tgl Exp $
  *
  * NOTES
  *   Eventually, the index information should go through here, too.
@@ -1770,7 +1770,7 @@ getTypeIOParam(HeapTuple typeTuple)
     * own type OID as parameter.  (As of 8.2, domains must get their own OID
     * even if their base type is an array.)
     */
-   if (typeStruct->typtype == 'b' && OidIsValid(typeStruct->typelem))
+   if (typeStruct->typtype == TYPTYPE_BASE && OidIsValid(typeStruct->typelem))
        return typeStruct->typelem;
    else
        return HeapTupleGetOid(typeTuple);
@@ -2022,7 +2022,7 @@ getBaseTypeAndTypmod(Oid typid, int32 *typmod)
        if (!HeapTupleIsValid(tup))
            elog(ERROR, "cache lookup failed for type %u", typid);
        typTup = (Form_pg_type) GETSTRUCT(tup);
-       if (typTup->typtype != 'd')
+       if (typTup->typtype != TYPTYPE_DOMAIN)
        {
            /* Not a domain, so done */
            ReleaseSysCache(tup);
@@ -2128,7 +2128,17 @@ get_typtype(Oid typid)
 bool
 type_is_rowtype(Oid typid)
 {
-   return (typid == RECORDOID || get_typtype(typid) == 'c');
+   return (typid == RECORDOID || get_typtype(typid) == TYPTYPE_COMPOSITE);
+}
+
+/*
+ * type_is_enum
+ *    Returns true if the given type is an enum type.
+ */
+bool
+type_is_enum(Oid typid)
+{
+   return (get_typtype(typid) == TYPTYPE_ENUM);
 }
 
 /*
index 75f290dda614f7a2e6e889de491734c8bf50fd77..2d52b4299b75fbd704a4b308cebdea20355a8a21 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.111 2007/02/14 01:58:57 tgl Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/cache/syscache.c,v 1.112 2007/04/02 03:49:39 tgl Exp $
  *
  * NOTES
  *   These routines allow the parser/planner/executor to perform
@@ -31,6 +31,7 @@
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_conversion.h"
 #include "catalog/pg_database.h"
+#include "catalog/pg_enum.h"
 #include "catalog/pg_language.h"
 #include "catalog/pg_namespace.h"
 #include "catalog/pg_opclass.h"
@@ -335,6 +336,30 @@ static const struct cachedesc cacheinfo[] = {
        },
        4
    },
+   {EnumRelationId,            /* ENUMOID */
+       EnumOidIndexId,
+       0,
+       1,
+       {
+           ObjectIdAttributeNumber,
+           0,
+           0,
+           0
+       },
+       256
+   },
+   {EnumRelationId,            /* ENUMTYPOIDNAME */
+       EnumTypIdLabelIndexId,
+       0,
+       2,
+       {
+           Anum_pg_enum_enumtypid,
+           Anum_pg_enum_enumlabel,
+           0,
+           0
+       },
+       256
+   },
    {IndexRelationId,           /* INDEXRELID */
        IndexRelidIndexId,
        Anum_pg_index_indrelid,
index 523e8ca35ab31cd4a88403fcfd565d960509d3bc..a8a240991167defcc025fab7cb0df220bc8bc69c 100644 (file)
@@ -36,7 +36,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.24 2007/01/05 22:19:43 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/cache/typcache.c,v 1.25 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -275,7 +275,7 @@ lookup_type_cache(Oid type_id, int flags)
     */
    if ((flags & TYPECACHE_TUPDESC) &&
        typentry->tupDesc == NULL &&
-       typentry->typtype == 'c')
+       typentry->typtype == TYPTYPE_COMPOSITE)
    {
        Relation    rel;
 
index 218b7646b7594cdd556ffe7c031ddd80d1fae216..c32130c3ec132de04714436cf0d69f7a327c4b43 100644 (file)
@@ -7,7 +7,7 @@
  * Copyright (c) 2002-2007, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.33 2007/02/01 19:10:28 momjian Exp $
+ *   $PostgreSQL: pgsql/src/backend/utils/fmgr/funcapi.c,v 1.34 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -193,8 +193,8 @@ shutdown_MultiFuncCall(Datum arg)
  * only when we couldn't resolve the actual rowtype for lack of information.
  *
  * The other hard case that this handles is resolution of polymorphism.
- * We will never return ANYELEMENT or ANYARRAY, either as a scalar result
- * type or as a component of a rowtype.
+ * We will never return ANYELEMENT, ANYARRAY or ANYENUM, either as a scalar
+ * result type or as a component of a rowtype.
  *
  * This function is relatively expensive --- in a function returning set,
  * try to call it only the first time through.
@@ -338,7 +338,7 @@ internal_get_result_type(Oid funcid,
    /*
     * If scalar polymorphic result, try to resolve it.
     */
-   if (rettype == ANYARRAYOID || rettype == ANYELEMENTOID)
+   if (IsPolymorphicType(rettype))
    {
        Oid         newrettype = exprType(call_expr);
 
@@ -389,8 +389,8 @@ internal_get_result_type(Oid funcid,
 
 /*
  * Given the result tuple descriptor for a function with OUT parameters,
- * replace any polymorphic columns (ANYELEMENT/ANYARRAY) with correct data
- * types deduced from the input arguments. Returns TRUE if able to deduce
+ * replace any polymorphic columns (ANYELEMENT/ANYARRAY/ANYENUM) with correct
+ * data types deduced from the input arguments.    Returns TRUE if able to deduce
  * all types, FALSE if not.
  */
 static bool
@@ -401,6 +401,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
    int         nargs = declared_args->dim1;
    bool        have_anyelement_result = false;
    bool        have_anyarray_result = false;
+   bool        have_anyenum = false;
    Oid         anyelement_type = InvalidOid;
    Oid         anyarray_type = InvalidOid;
    int         i;
@@ -416,6 +417,10 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
            case ANYARRAYOID:
                have_anyarray_result = true;
                break;
+           case ANYENUMOID:
+               have_anyelement_result = true;
+               have_anyenum = true;
+               break;
            default:
                break;
        }
@@ -435,6 +440,7 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
        switch (declared_args->values[i])
        {
            case ANYELEMENTOID:
+           case ANYENUMOID:
                if (!OidIsValid(anyelement_type))
                    anyelement_type = get_call_expr_argtype(call_expr, i);
                break;
@@ -461,12 +467,17 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
                                             anyelement_type,
                                             ANYELEMENTOID);
 
+   /* Check for enum if needed */
+   if (have_anyenum && !type_is_enum(anyelement_type))
+       return false;
+
    /* And finally replace the tuple column types as needed */
    for (i = 0; i < natts; i++)
    {
        switch (tupdesc->attrs[i]->atttypid)
        {
            case ANYELEMENTOID:
+           case ANYENUMOID:
                TupleDescInitEntry(tupdesc, i + 1,
                                   NameStr(tupdesc->attrs[i]->attname),
                                   anyelement_type,
@@ -490,8 +501,8 @@ resolve_polymorphic_tupdesc(TupleDesc tupdesc, oidvector *declared_args,
 
 /*
  * Given the declared argument types and modes for a function,
- * replace any polymorphic types (ANYELEMENT/ANYARRAY) with correct data
- * types deduced from the input arguments. Returns TRUE if able to deduce
+ * replace any polymorphic types (ANYELEMENT/ANYARRAY/ANYENUM) with correct
+ * data types deduced from the input arguments.    Returns TRUE if able to deduce
  * all types, FALSE if not.  This is the same logic as
  * resolve_polymorphic_tupdesc, but with a different argument representation.
  *
@@ -517,6 +528,7 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
        switch (argtypes[i])
        {
            case ANYELEMENTOID:
+           case ANYENUMOID:
                if (argmode == PROARGMODE_OUT)
                    have_anyelement_result = true;
                else
@@ -571,12 +583,15 @@ resolve_polymorphic_argtypes(int numargs, Oid *argtypes, char *argmodes,
                                             anyelement_type,
                                             ANYELEMENTOID);
 
+   /* XXX do we need to enforce ANYENUM here?  I think not */
+
    /* And finally replace the output column types as needed */
    for (i = 0; i < numargs; i++)
    {
        switch (argtypes[i])
        {
            case ANYELEMENTOID:
+           case ANYENUMOID:
                argtypes[i] = anyelement_type;
                break;
            case ANYARRAYOID:
@@ -603,12 +618,13 @@ get_type_func_class(Oid typid)
 {
    switch (get_typtype(typid))
    {
-       case 'c':
+       case TYPTYPE_COMPOSITE:
            return TYPEFUNC_COMPOSITE;
-       case 'b':
-       case 'd':
+       case TYPTYPE_BASE:
+       case TYPTYPE_DOMAIN:
+       case TYPTYPE_ENUM:
            return TYPEFUNC_SCALAR;
-       case 'p':
+       case TYPTYPE_PSEUDO:
            if (typid == RECORDOID)
                return TYPEFUNC_RECORD;
 
@@ -840,7 +856,7 @@ get_func_result_name(Oid functionId)
  * Given a pg_proc row for a function, return a tuple descriptor for the
  * result rowtype, or NULL if the function does not have OUT parameters.
  *
- * Note that this does not handle resolution of ANYELEMENT/ANYARRAY types;
+ * Note that this does not handle resolution of polymorphic types;
  * that is deliberate.
  */
 TupleDesc
index b0c9e70a46f2423084c1e509b74a797a90899fb8..9f9790baed112d6ec9ecd5399efb044bc496bfaa 100644 (file)
@@ -12,7 +12,7 @@
  * by PostgreSQL
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.465 2007/03/26 16:58:39 tgl Exp $
+ *   $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.466 2007/04/02 03:49:39 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -137,6 +137,7 @@ static void dumpDumpableObject(Archive *fout, DumpableObject *dobj);
 static void dumpNamespace(Archive *fout, NamespaceInfo *nspinfo);
 static void dumpType(Archive *fout, TypeInfo *tinfo);
 static void dumpBaseType(Archive *fout, TypeInfo *tinfo);
+static void dumpEnumType(Archive *fout, TypeInfo *tinfo);
 static void dumpDomain(Archive *fout, TypeInfo *tinfo);
 static void dumpCompositeType(Archive *fout, TypeInfo *tinfo);
 static void dumpShellType(Archive *fout, ShellTypeInfo *stinfo);
@@ -2085,7 +2086,7 @@ getTypes(int *numTypes)
         */
        tinfo[i].nDomChecks = 0;
        tinfo[i].domChecks = NULL;
-       if (tinfo[i].dobj.dump && tinfo[i].typtype == 'd')
+       if (tinfo[i].dobj.dump && tinfo[i].typtype == TYPTYPE_DOMAIN)
            getDomainConstraints(&(tinfo[i]));
 
        /*
@@ -2097,7 +2098,7 @@ getTypes(int *numTypes)
         * should copy the base type's catId, but then it might capture the
         * pg_depend entries for the type, which we don't want.
         */
-       if (tinfo[i].dobj.dump && tinfo[i].typtype == 'b')
+       if (tinfo[i].dobj.dump && tinfo[i].typtype == TYPTYPE_BASE)
        {
            stinfo = (ShellTypeInfo *) malloc(sizeof(ShellTypeInfo));
            stinfo->dobj.objType = DO_SHELL_TYPE;
@@ -5119,12 +5120,91 @@ dumpType(Archive *fout, TypeInfo *tinfo)
        return;
 
    /* Dump out in proper style */
-   if (tinfo->typtype == 'b')
+   if (tinfo->typtype == TYPTYPE_BASE)
        dumpBaseType(fout, tinfo);
-   else if (tinfo->typtype == 'd')
+   else if (tinfo->typtype == TYPTYPE_DOMAIN)
        dumpDomain(fout, tinfo);
-   else if (tinfo->typtype == 'c')
+   else if (tinfo->typtype == TYPTYPE_COMPOSITE)
        dumpCompositeType(fout, tinfo);
+   else if (tinfo->typtype == TYPTYPE_ENUM)
+       dumpEnumType(fout, tinfo);
+}
+
+/*
+ * dumpEnumType
+ *   writes out to fout the queries to recreate a user-defined enum type
+ */
+static void
+dumpEnumType(Archive *fout, TypeInfo *tinfo)
+{
+   PQExpBuffer q = createPQExpBuffer();
+   PQExpBuffer delq = createPQExpBuffer();
+   PQExpBuffer query = createPQExpBuffer();
+   PGresult   *res;
+   int num, i;
+   char *label;
+
+   /* Set proper schema search path so regproc references list correctly */
+   selectSourceSchema(tinfo->dobj.namespace->dobj.name);
+
+   appendPQExpBuffer(query, "SELECT enumlabel FROM pg_catalog.pg_enum "
+                     "WHERE enumtypid = '%u'"
+                     "ORDER BY oid",
+                     tinfo->dobj.catId.oid);
+
+   res = PQexec(g_conn, query->data);
+   check_sql_result(res, g_conn, query->data, PGRES_TUPLES_OK);
+
+   num = PQntuples(res);
+   /* should be at least 1 value */
+   if (num == 0)
+   {
+       write_msg(NULL, "No rows found for enum");
+       exit_nicely();
+   }
+
+   /*
+    * DROP must be fully qualified in case same name appears in pg_catalog.
+    * CASCADE shouldn't be required here as for normal types since the
+    * I/O functions are generic and do not get dropped.
+    */
+   appendPQExpBuffer(delq, "DROP TYPE %s.",
+                     fmtId(tinfo->dobj.namespace->dobj.name));
+   appendPQExpBuffer(delq, "%s;\n",
+                     fmtId(tinfo->dobj.name));
+   appendPQExpBuffer(q, "CREATE TYPE %s AS ENUM (\n",
+                     fmtId(tinfo->dobj.name));
+   for (i = 0; i < num; i++)
+   {
+       label = PQgetvalue(res, i, 0);
+       if (i > 0)
+           appendPQExpBuffer(q, ",\n"); 
+       appendPQExpBuffer(q, "    "); 
+       appendStringLiteralAH(q, label, fout);
+   }
+   appendPQExpBuffer(q, "\n);\n");
+
+   ArchiveEntry(fout, tinfo->dobj.catId, tinfo->dobj.dumpId,
+                tinfo->dobj.name,
+                tinfo->dobj.namespace->dobj.name,
+                NULL,
+                tinfo->rolname, false,
+                "TYPE", q->data, delq->data, NULL,
+                tinfo->dobj.dependencies, tinfo->dobj.nDeps,
+                NULL, NULL);
+
+   /* Dump Type Comments */
+   resetPQExpBuffer(q);
+
+   appendPQExpBuffer(q, "TYPE %s", fmtId(tinfo->dobj.name));
+   dumpComment(fout, q->data,
+               tinfo->dobj.namespace->dobj.name, tinfo->rolname,
+               tinfo->dobj.catId, 0, tinfo->dobj.dumpId);
+
+   PQclear(res);
+   destroyPQExpBuffer(q);
+   destroyPQExpBuffer(delq);
+   destroyPQExpBuffer(query);
 }
 
 /*
index baed9fdb4b74bb270a9f7cb8a8acfbe76bd00fe8..7ba8fbfc2f292d34f9e99e470e5f860f19346c3b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/access/hash.h,v 1.76 2007/01/30 01:33:36 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/access/hash.h,v 1.77 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *     modeled after Margo Seltzer's hash implementation for unix.
@@ -258,6 +258,7 @@ extern Datum hashint2(PG_FUNCTION_ARGS);
 extern Datum hashint4(PG_FUNCTION_ARGS);
 extern Datum hashint8(PG_FUNCTION_ARGS);
 extern Datum hashoid(PG_FUNCTION_ARGS);
+extern Datum hashenum(PG_FUNCTION_ARGS);
 extern Datum hashfloat4(PG_FUNCTION_ARGS);
 extern Datum hashfloat8(PG_FUNCTION_ARGS);
 extern Datum hashoidvector(PG_FUNCTION_ARGS);
index d31dce2a0cf661beb9ee7135fb2d55ac7c7c1a47..9525a198feeb912f387ec268d7f4fa6db9d7dbbb 100644 (file)
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.398 2007/04/01 09:56:02 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.399 2007/04/02 03:49:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*                         yyyymmddN */
-#define CATALOG_VERSION_NO 200704011
+#define CATALOG_VERSION_NO 200704012
 
 #endif
index baa650a8edb260b3f58ae9d7c217899717ce8945..71970d48f74e3283a0b9a4d519394d5e53ca8599 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.98 2007/02/14 01:58:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/indexing.h,v 1.99 2007/04/02 03:49:40 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -146,6 +146,11 @@ DECLARE_UNIQUE_INDEX(pg_description_o_c_o_index, 2675, on pg_description using b
 DECLARE_UNIQUE_INDEX(pg_shdescription_o_c_index, 2397, on pg_shdescription using btree(objoid oid_ops, classoid oid_ops));
 #define SharedDescriptionObjIndexId 2397
 
+DECLARE_UNIQUE_INDEX(pg_enum_oid_index, 3502, on pg_enum using btree(oid oid_ops));
+#define EnumOidIndexId  3502
+DECLARE_UNIQUE_INDEX(pg_enum_typid_label_index, 3503, on pg_enum using btree(enumtypid oid_ops, enumlabel name_ops));
+#define EnumTypIdLabelIndexId 3503
+
 /* This following index is not used for a cache and is not unique */
 DECLARE_INDEX(pg_index_indrelid_index, 2678, on pg_index using btree(indrelid oid_ops));
 #define IndexIndrelidIndexId  2678
index 9036b1d4a6c0992dfa8f6a4bbdfc460d4ab5e360..0ee798b356ed00639e043cb714d314fada655251 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.61 2007/02/17 00:55:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_aggregate.h,v 1.62 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -118,6 +118,7 @@ DATA(insert ( 2130  numeric_larger  -               1756    1700    _null_ ));
 DATA(insert ( 2050 array_larger    -               1073    2277    _null_ ));
 DATA(insert ( 2244 bpchar_larger   -               1060    1042    _null_ ));
 DATA(insert ( 2797 tidlarger       -               2800    27      _null_ ));
+DATA(insert ( 3526 enum_larger     -               3519    3500    _null_ ));
 
 /* min */
 DATA(insert ( 2131 int8smaller     -               412     20      _null_ ));
@@ -139,6 +140,7 @@ DATA(insert ( 2146  numeric_smaller -               1754    1700    _null_ ));
 DATA(insert ( 2051 array_smaller   -               1072    2277    _null_ ));
 DATA(insert ( 2245 bpchar_smaller  -               1058    1042    _null_ ));
 DATA(insert ( 2798 tidsmaller      -               2799    27      _null_ ));
+DATA(insert ( 3527 enum_smaller    -               3518    3500    _null_ ));
 
 /* count */
 DATA(insert ( 2147 int8inc_any     -               0       20      "0" ));
index c1023df6ea922bd45a7ef8277bdf03b9eeb9d758..2b852645998336bebb05b0f11c86354372047c4c 100644 (file)
@@ -29,7 +29,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_amop.h,v 1.79 2007/02/06 02:59:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_amop.h,v 1.80 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *  the genbki.sh script reads this file and generates .bki
@@ -636,4 +636,18 @@ DATA(insert (  2745   2277 2277 2  f   2751    2742 ));
 DATA(insert (  2745   2277 2277 3  t   2752    2742 ));
 DATA(insert (  2745   2277 2277 4  t   1070    2742 ));
 
+/*
+ * btree enum_ops
+ */
+DATA(insert (   3522   3500 3500 1  f  3518    403 ));
+DATA(insert (   3522   3500 3500 2  f  3520    403 ));
+DATA(insert (   3522   3500 3500 3  f  3516    403 ));
+DATA(insert (   3522   3500 3500 4  f  3521    403 ));
+DATA(insert (   3522   3500 3500 5  f  3519    403 ));
+
+/*
+ * hash enum_ops
+ */
+DATA(insert (   3523   3500 3500 1  f  3516    405 ));
+
 #endif   /* PG_AMOP_H */
index 64db6483417048af085a32c644f64aeb7705725b..d535e615c811bdd2fba7ed2cb2e238dd6892924b 100644 (file)
@@ -22,7 +22,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_amproc.h,v 1.63 2007/01/28 16:16:52 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_amproc.h,v 1.64 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -128,6 +128,7 @@ DATA(insert (   2233   703 703 1  380 ));
 DATA(insert (  2234   704 704 1  381 ));
 DATA(insert (  2789   27 27 1 2794 ));
 DATA(insert (  2968   2950 2950 1 2960 ));
+DATA(insert (  3522   3500 3500 1 3514 ));
 
 
 /* hash */
@@ -162,6 +163,7 @@ DATA(insert (   2231   1042 1042 1 456 ));
 DATA(insert (  2232   19 19 1 455 ));
 DATA(insert (  2235   1033 1033 1 329 ));
 DATA(insert (  2969   2950 2950 1 2963 ));
+DATA(insert (  3523   3500 3500 1 3515 ));
 
 
 /* gist */
index 0047f865af2328e07f19c27f21a3dbab2a2f75c1..bc558b82e9eb1354b87a68fabb8eeb8edae4e1ac 100644 (file)
@@ -10,7 +10,7 @@
  *
  * Copyright (c) 2002-2007, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.31 2007/02/03 14:06:55 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.32 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -403,4 +403,10 @@ DATA(insert ( 2950   25 2965 a ));
 DATA(insert ( 1043 2950 2964 a ));
 DATA(insert ( 2950 1043 2965 a ));
 
+/*
+ * enums
+ */
+DATA(insert ( 3500   25 3532 e ));
+DATA(insert (   25 3500 3533 e ));
+
 #endif   /* PG_CAST_H */
diff --git a/src/include/catalog/pg_enum.h b/src/include/catalog/pg_enum.h
new file mode 100644 (file)
index 0000000..1196e43
--- /dev/null
@@ -0,0 +1,72 @@
+/*-------------------------------------------------------------------------
+ *
+ * pg_enum.h
+ *   definition of the system "enum" relation (pg_enum)
+ *   along with the relation's initial contents.
+ *
+ *
+ * Copyright (c) 2006-2007, PostgreSQL Global Development Group
+ *
+ * $PostgreSQL: pgsql/src/include/catalog/pg_enum.h,v 1.1 2007/04/02 03:49:40 tgl Exp $
+ *
+ * NOTES
+ *   the genbki.sh script reads this file and generates .bki
+ *   information from the DATA() statements.
+ *
+ *   XXX do NOT break up DATA() statements into multiple lines!
+ *       the scripts are not as smart as you might think...
+ *
+ *-------------------------------------------------------------------------
+ */
+#ifndef PG_ENUM_H
+#define PG_ENUM_H
+
+#include "nodes/pg_list.h"
+
+/* ----------------
+ *     postgres.h contains the system type definitions and the
+ *     CATALOG(), BKI_BOOTSTRAP and DATA() sugar words so this file
+ *     can be read by both genbki.sh and the C compiler.
+ * ----------------
+ */
+
+/* ----------------
+ *     pg_enum definition.  cpp turns this into
+ *     typedef struct FormData_pg_enum
+ * ----------------
+ */
+#define EnumRelationId  3501
+
+CATALOG(pg_enum,3501)
+{
+   Oid         enumtypid;      /* OID of owning enum type */
+   NameData    enumlabel;      /* text representation of enum value */
+} FormData_pg_enum;
+
+/* ----------------
+ *     Form_pg_enum corresponds to a pointer to a tuple with
+ *     the format of pg_enum relation.
+ * ----------------
+ */
+typedef FormData_pg_enum *Form_pg_enum;
+
+/* ----------------
+ *     compiler constants for pg_enum
+ * ----------------
+ */
+#define Natts_pg_enum                  2
+#define Anum_pg_enum_enumtypid         1
+#define Anum_pg_enum_enumlabel         2
+
+/* ----------------
+ *     pg_enum has no initial contents
+ * ----------------
+ */
+
+/*
+ * prototypes for functions in pg_enum.c
+ */
+extern void EnumValuesCreate(Oid enumTypeOid, List *vals);
+extern void EnumValuesDelete(Oid enumTypeOid);
+
+#endif   /* PG_ENUM_H */
index 148f02db2a99d32d8febdd0d02dbf669e152eaba..2df9b8241a8b5fa86c71ad00905ca6088d5b9f91 100644 (file)
@@ -28,7 +28,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_opclass.h,v 1.74 2007/01/28 16:16:52 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_opclass.h,v 1.75 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -200,7 +200,9 @@ DATA(insert (   2742    _timestamp_ops      PGNSP PGUID 2745  1115 t 1114 ));
 DATA(insert (  2742    _money_ops          PGNSP PGUID 2745  791 t 790 ));
 DATA(insert (  2742    _reltime_ops        PGNSP PGUID 2745  1024 t 703 ));
 DATA(insert (  2742    _tinterval_ops      PGNSP PGUID 2745  1025 t 704 ));
-DATA(insert (   403     uuid_ops           PGNSP PGUID 2968  2950 t 0 ));
-DATA(insert (   405     uuid_ops           PGNSP PGUID 2969  2950 t 0 ));
+DATA(insert (  403     uuid_ops            PGNSP PGUID 2968  2950 t 0 ));
+DATA(insert (  405     uuid_ops            PGNSP PGUID 2969  2950 t 0 ));
+DATA(insert (  403     enum_ops            PGNSP PGUID 3522  3500 t 0 ));
+DATA(insert (  405     enum_ops            PGNSP PGUID 3523  3500 t 0 ));
 
 #endif   /* PG_OPCLASS_H */
index e53f74b5193a774105bc7c936c8ff38c3e4c80a9..2591b1ff7b192896c5b578d8e04f2060907f1127 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.150 2007/02/06 02:59:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.151 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -903,6 +903,14 @@ DATA(insert OID = 2975 (  ">"     PGNSP PGUID b f f 2950 2950 16 2974 2976 uuid_g
 DATA(insert OID = 2976 (  "<="    PGNSP PGUID b f f 2950 2950 16 2977 2975 uuid_le scalarltsel scalarltjoinsel ));
 DATA(insert OID = 2977 (  ">="    PGNSP PGUID b f f 2950 2950 16 2976 2974 uuid_ge scalargtsel scalargtjoinsel ));
 
+/* enum operators */
+DATA(insert OID = 3516 (  "="     PGNSP PGUID b t t 3500 3500 16 3516 3517 enum_eq eqsel eqjoinsel ));
+DATA(insert OID = 3517 (  "<>"    PGNSP PGUID b f f 3500 3500 16 3517 3516 enum_ne neqsel neqjoinsel ));
+DATA(insert OID = 3518 (  "<"     PGNSP PGUID b f f 3500 3500 16 3519 3521 enum_lt scalarltsel scalarltjoinsel ));
+DATA(insert OID = 3519 (  ">"     PGNSP PGUID b f f 3500 3500 16 3518 3520 enum_gt scalargtsel scalargtjoinsel ));
+DATA(insert OID = 3520 (  "<="    PGNSP PGUID b f f 3500 3500 16 3521 3519 enum_le scalarltsel scalarltjoinsel ));
+DATA(insert OID = 3521 (  ">="    PGNSP PGUID b f f 3500 3500 16 3520 3518 enum_ge scalargtsel scalargtjoinsel ));
+
 
 /*
  * function prototypes
index 63d1b2776431edec4a8aa656dd402a3e6dae8fdf..3d4c0a41d0a568a1aec54d4ca34fcb866c62c59d 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_opfamily.h,v 1.3 2007/01/28 16:16:52 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_opfamily.h,v 1.4 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -136,6 +136,8 @@ DATA(insert OID = 2595 (    783     circle_ops      PGNSP PGUID ));
 DATA(insert OID = 2745 (   2742    array_ops       PGNSP PGUID ));
 DATA(insert OID = 2968 (   403     uuid_ops        PGNSP PGUID ));
 DATA(insert OID = 2969 (   405     uuid_ops        PGNSP PGUID ));
+DATA(insert OID = 3522 (   403     enum_ops        PGNSP PGUID ));
+DATA(insert OID = 3523 (   405     enum_ops        PGNSP PGUID ));
 
 
 #endif   /* PG_OPFAMILY_H */
index 252d7460ac7658d18839f84deea29480f6f9b45c..d7b048b07f7f4c67313f3ea613d0e757f2abcf9d 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.453 2007/04/01 09:00:25 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.454 2007/04/02 03:49:40 tgl Exp $
  *
  * NOTES
  *   The script catalog/genbki.sh reads this file and generates .bki
@@ -4149,6 +4149,46 @@ DESCR("convert text to uuid");
 DATA(insert OID = 2965 (  text PGNSP      PGUID 12 1 0 f f t f i 1 25 "2950" _null_ _null_ _null_ uuid_text - _null_ ));
 DESCR("convert uuid to text");
 
+/* enum related procs */
+DATA(insert OID = 3504 (  anyenum_in   PGNSP PGUID 12 1 0 f f t f i 1 3500 "2275" _null_ _null_ _null_ anyenum_in - _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3505 (  anyenum_out  PGNSP PGUID 12 1 0 f f t f s 1 2275 "3500" _null_ _null_ _null_ anyenum_out - _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3506 (  enum_in      PGNSP PGUID 12 1 0 f f t f s 2 3500 "2275 26" _null_ _null_ _null_ enum_in - _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3507 (  enum_out     PGNSP PGUID 12 1 0 f f t f s 1 2275 "3500" _null_ _null_ _null_ enum_out - _null_ ));
+DESCR("I/O");
+DATA(insert OID = 3508 (  enum_eq      PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_eq - _null_ ));
+DESCR("equal");
+DATA(insert OID = 3509 (  enum_ne      PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_ne - _null_ ));
+DESCR("not equal");
+DATA(insert OID = 3510 (  enum_lt      PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_lt - _null_ ));
+DESCR("less-than");
+DATA(insert OID = 3511 (  enum_gt      PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_gt - _null_ ));
+DESCR("greater-than");
+DATA(insert OID = 3512 (  enum_le      PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_le - _null_ ));
+DESCR("less-than-or-equal");
+DATA(insert OID = 3513 (  enum_ge      PGNSP PGUID 12 1 0 f f t f i 2 16 "3500 3500" _null_ _null_ _null_ enum_ge - _null_ ));
+DESCR("greater-than-or-equal");
+DATA(insert OID = 3514 (  enum_cmp     PGNSP PGUID 12 1 0 f f t f i 2 23 "3500 3500" _null_ _null_ _null_ enum_cmp - _null_ ));
+DESCR("btree-less-equal-greater");
+DATA(insert OID = 3515 (  hashenum     PGNSP PGUID 12 1 0 f f t f i 1 23 "3500" _null_ _null_ _null_ hashenum - _null_ ));
+DESCR("hash");
+DATA(insert OID = 3524 (  enum_smaller PGNSP PGUID 12 1 0 f f t f i 2 3500 "3500 3500" _null_ _null_ _null_ enum_smaller - _null_ ));
+DESCR("smaller of two");
+DATA(insert OID = 3525 (  enum_larger  PGNSP PGUID 12 1 0 f f t f i 2 3500 "3500 3500" _null_ _null_ _null_ enum_larger - _null_ ));
+DESCR("larger of two");
+DATA(insert OID = 3526 (  max          PGNSP PGUID 12 1 0 t f f f i 1 3500 "3500" _null_ _null_ _null_ aggregate_dummy - _null_ ));
+DATA(insert OID = 3527 (  min          PGNSP PGUID 12 1 0 t f f f i 1 3500 "3500" _null_ _null_ _null_ aggregate_dummy - _null_ ));
+DATA(insert OID = 3528 (  enum_first   PGNSP PGUID 12 1 0 f f f f s 1 3500 "3500" _null_ _null_ _null_ enum_first - _null_ ));
+DATA(insert OID = 3529 (  enum_last        PGNSP PGUID 12 1 0 f f f f s 1 3500 "3500" _null_ _null_ _null_ enum_last - _null_ ));
+DATA(insert OID = 3530 (  enum_range   PGNSP PGUID 12 1 0 f f f f s 2 2277 "3500 3500" _null_ _null_ _null_ enum_range_bounds - _null_ ));
+DATA(insert OID = 3531 (  enum_range   PGNSP PGUID 12 1 0 f f f f s 1 2277 "3500" _null_ _null_ _null_ enum_range_all - _null_ ));
+DATA(insert OID = 3532 (  text         PGNSP PGUID 12 1 0 f f t f s 1 25 "3500" _null_ _null_ _null_ enum_text - _null_ ));
+DESCR("convert enum to text");
+DATA(insert OID = 3533 (  enum         PGNSP PGUID 12 1 0 f f t f s 1 3500 "25" _null_ _null_ _null_ text_enum - _null_ ));
+DESCR("convert text to enum");
+
 /*
  * Symbolic values for provolatile column: these indicate whether the result
  * of a function is dependent *only* on the values of its explicit arguments,
index 89849c25fcbf10355b0650af89b1e7ac03a5a9ac..69713196000b78b9e437e235a1ab12dddfe42992 100644 (file)
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.180 2007/01/28 16:16:54 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.181 2007/04/02 03:49:41 tgl Exp $
  *
  * NOTES
  *   the genbki.sh script reads this file and generates .bki
@@ -66,8 +66,9 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP
    bool        typbyval;
 
    /*
-    * typtype is 'b' for a basic type, 'c' for a complex type (ie a table's
-    * rowtype), 'd' for a domain type, or 'p' for a pseudo type.
+    * typtype is 'b' for a base type, 'c' for a composite type (e.g.,
+    * a table's rowtype), 'd' for a domain type, 'e' for an enum type,
+    * or 'p' for a pseudo-type.  (Use the TYPTYPE macros below.)
     *
     * If typtype is 'c', typrelid is the OID of the class' entry in pg_class.
     */
@@ -531,6 +532,11 @@ DATA(insert OID = 2210 ( _regclass    PGNSP PGUID -1 f b t \054 0 2205 array_in
 DATA(insert OID = 2211 ( _regtype     PGNSP PGUID -1 f b t \054 0 2206 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 #define REGTYPEARRAYOID 2211
 
+/* uuid */ 
+DATA(insert OID = 2950 ( uuid          PGNSP PGUID 16 f b t \054 0 0 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
+DESCR("UUID datatype");
+DATA(insert OID = 2951 ( _uuid         PGNSP PGUID -1 f b t \054 0 2950 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+
 /*
  * pseudo-types
  *
@@ -560,11 +566,24 @@ DATA(insert OID = 2282 ( opaque           PGNSP PGUID  4 t p t \054 0 0 opaque_in opaque
 #define OPAQUEOID      2282
 DATA(insert OID = 2283 ( anyelement        PGNSP PGUID  4 t p t \054 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYELEMENTOID  2283
+DATA(insert OID = 3500 ( anyenum       PGNSP PGUID  4 t p t \054 0 0 anyenum_in anyenum_out - - - - - i p f 0 -1 0 _null_ _null_ ));
+#define ANYENUMOID     3500
 
-/* uuid */ 
-DATA(insert OID = 2950 ( uuid          PGNSP PGUID 16 f b t \054 0 0 uuid_in uuid_out uuid_recv uuid_send - - - c p f 0 -1 0 _null_ _null_ ));
-DESCR("UUID datatype");
-DATA(insert OID = 2951 ( _uuid         PGNSP PGUID -1 f b t \054 0 2950 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+
+/*
+ * macros
+ */
+#define  TYPTYPE_BASE      'b'     /* base type (ordinary scalar type) */
+#define  TYPTYPE_COMPOSITE 'c'     /* composite (e.g., table's rowtype) */
+#define  TYPTYPE_DOMAIN        'd'     /* domain over another type */
+#define  TYPTYPE_ENUM      'e'     /* enumerated type */
+#define  TYPTYPE_PSEUDO        'p'     /* pseudo-type */
+
+/* Is a type OID a polymorphic pseudotype?  (Beware of multiple evaluation) */
+#define IsPolymorphicType(typid)  \
+   ((typid) == ANYELEMENTOID || \
+    (typid) == ANYARRAYOID || \
+    (typid) == ANYENUMOID)
 
 /*
  * prototypes for functions in pg_type.c
index 97196a062e24aee97d12d58d60771fd133f04edb..91e56e12cb0cf0e0eb68a61cb1577b64f998b384 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.17 2007/01/05 22:19:54 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/commands/typecmds.h,v 1.18 2007/04/02 03:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@ extern void RemoveType(List *names, DropBehavior behavior, bool missing_ok);
 extern void RemoveTypeById(Oid typeOid);
 extern void DefineDomain(CreateDomainStmt *stmt);
 extern void RemoveDomain(List *names, DropBehavior behavior, bool missing_ok);
+extern void DefineEnum(CreateEnumStmt *stmt);
 extern Oid DefineCompositeType(const RangeVar *typevar, List *coldeflist);
 
 extern void AlterDomainDefault(List *names, Node *defaultRaw);
index f344e3f5d1064d0a458b6c6ded89dda1ed95dd01..305be462e10343f3052cc57a3fae47bbbf583a3f 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.197 2007/03/27 23:21:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/nodes.h,v 1.198 2007/04/02 03:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -307,6 +307,7 @@ typedef enum NodeTag
    T_DropOwnedStmt,
    T_ReassignOwnedStmt,
    T_CompositeTypeStmt,
+   T_CreateEnumStmt,
 
    /*
     * TAGS FOR PARSE TREE NODES (parsenodes.h)
index c3a2bebca68b54e3e3d333fe83ea1dca21d98674..8552620be3c65083b15eccd1e847df39c45e851b 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.343 2007/03/19 23:38:32 wieck Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.344 2007/04/02 03:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1706,6 +1706,17 @@ typedef struct CompositeTypeStmt
    List       *coldeflist;     /* list of ColumnDef nodes */
 } CompositeTypeStmt;
 
+/* ----------------------
+ *     Create Type Statement, enum types
+ * ----------------------
+ */
+typedef struct CreateEnumStmt
+{
+   NodeTag     type;
+   List       *typename;       /* qualified name (list of Value strings) */
+   List       *vals;           /* enum values (list of Value strings) */
+} CreateEnumStmt;
+
 
 /* ----------------------
  *     Create View Statement
index 3fff9ba33d2b159655b72a40af03107c6c54de12..3d07b085793561547d5c9937333f4a4677871acd 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_type.h,v 1.35 2007/01/05 22:19:57 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_type.h,v 1.36 2007/04/02 03:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,7 +33,6 @@ extern Type typeidType(Oid id);
 extern Oid typeTypeId(Type tp);
 extern int16 typeLen(Type t);
 extern bool typeByVal(Type t);
-extern char typeTypType(Type t);
 extern char *typeTypeName(Type t);
 extern Oid typeTypeRelid(Type typ);
 extern Datum stringTypeDatum(Type tp, char *string, int32 atttypmod);
index b2a55bbbb9839cbafc3e3d9c05a56d6d1123fb60..d93599a9ae5761f58a8d7949b5c1493731beb20e 100644 (file)
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.290 2007/03/20 05:45:00 neilc Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.291 2007/04/02 03:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -103,6 +103,25 @@ extern Datum char_text(PG_FUNCTION_ARGS);
 extern Datum domain_in(PG_FUNCTION_ARGS);
 extern Datum domain_recv(PG_FUNCTION_ARGS);
 
+/* enum.c */
+extern Datum enum_in(PG_FUNCTION_ARGS);
+extern Datum enum_out(PG_FUNCTION_ARGS);
+extern Datum enum_lt(PG_FUNCTION_ARGS);
+extern Datum enum_le(PG_FUNCTION_ARGS);
+extern Datum enum_eq(PG_FUNCTION_ARGS);
+extern Datum enum_ne(PG_FUNCTION_ARGS);
+extern Datum enum_ge(PG_FUNCTION_ARGS);
+extern Datum enum_gt(PG_FUNCTION_ARGS);
+extern Datum enum_cmp(PG_FUNCTION_ARGS);
+extern Datum enum_text(PG_FUNCTION_ARGS);
+extern Datum text_enum(PG_FUNCTION_ARGS);
+extern Datum enum_smaller(PG_FUNCTION_ARGS);
+extern Datum enum_larger(PG_FUNCTION_ARGS);
+extern Datum enum_first(PG_FUNCTION_ARGS);
+extern Datum enum_last(PG_FUNCTION_ARGS);
+extern Datum enum_range_bounds(PG_FUNCTION_ARGS);
+extern Datum enum_range_all(PG_FUNCTION_ARGS);
+
 /* int.c */
 extern Datum int2in(PG_FUNCTION_ARGS);
 extern Datum int2out(PG_FUNCTION_ARGS);
@@ -450,6 +469,8 @@ extern Datum anyarray_in(PG_FUNCTION_ARGS);
 extern Datum anyarray_out(PG_FUNCTION_ARGS);
 extern Datum anyarray_recv(PG_FUNCTION_ARGS);
 extern Datum anyarray_send(PG_FUNCTION_ARGS);
+extern Datum anyenum_in(PG_FUNCTION_ARGS);
+extern Datum anyenum_out(PG_FUNCTION_ARGS);
 extern Datum void_in(PG_FUNCTION_ARGS);
 extern Datum void_out(PG_FUNCTION_ARGS);
 extern Datum trigger_in(PG_FUNCTION_ARGS);
index 78c45891fa49cb7c15a68ae8409b9cab09d1651f..25782e322e9845a9703c9f09c07353f2d2edffb0 100644 (file)
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.117 2007/02/14 01:58:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.118 2007/04/02 03:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -105,6 +105,7 @@ extern char get_typstorage(Oid typid);
 extern Node *get_typdefault(Oid typid);
 extern char get_typtype(Oid typid);
 extern bool type_is_rowtype(Oid typid);
+extern bool type_is_enum(Oid typid);
 extern Oid get_typ_typrelid(Oid typid);
 extern Oid get_element_type(Oid typid);
 extern Oid get_array_type(Oid typid);
index c6967251ce1f4132dd9cad90c9fcaf994e2e4621..12cd9a72f225a774aab0294c983d9ac5a6b6472d 100644 (file)
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1996-2007, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.68 2007/02/14 01:58:58 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/syscache.h,v 1.69 2007/04/02 03:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 #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);
index 33027c53bde1dfdd20da2eaf3e8da6617f2020d3..ff484f4dbe015deaaba209b524dd7d403ce35e67 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plperl.c - perl as a procedural language for PostgreSQL
  *
- *   $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.127 2007/02/09 03:35:34 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plperl/plperl.c,v 1.128 2007/04/02 03:49:41 tgl Exp $
  *
  **********************************************************************/
 
@@ -843,7 +843,7 @@ plperl_validator(PG_FUNCTION_ARGS)
 
    /* Disallow pseudotype result */
    /* except for TRIGGER, RECORD, or VOID */
-   if (functyptype == 'p')
+   if (functyptype == TYPTYPE_PSEUDO)
    {
        /* we assume OPAQUE with no arguments means a trigger */
        if (proc->prorettype == TRIGGEROID ||
@@ -862,7 +862,7 @@ plperl_validator(PG_FUNCTION_ARGS)
                                &argtypes, &argnames, &argmodes);
    for (i = 0; i < numargs; i++)
    {
-       if (get_typtype(argtypes[i]) == 'p')
+       if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("plperl functions cannot take type %s",
@@ -1525,7 +1525,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
            typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
            /* Disallow pseudotype result, except VOID or RECORD */
-           if (typeStruct->typtype == 'p')
+           if (typeStruct->typtype == TYPTYPE_PSEUDO)
            {
                if (procStruct->prorettype == VOIDOID ||
                    procStruct->prorettype == RECORDOID)
@@ -1552,8 +1552,8 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
 
            prodesc->result_oid = procStruct->prorettype;
            prodesc->fn_retisset = procStruct->proretset;
-           prodesc->fn_retistuple = (typeStruct->typtype == 'c' ||
-                                     procStruct->prorettype == RECORDOID);
+           prodesc->fn_retistuple = (procStruct->prorettype == RECORDOID ||
+                                     typeStruct->typtype == TYPTYPE_COMPOSITE);
 
            prodesc->fn_retisarray =
                (typeStruct->typlen == -1 && typeStruct->typelem);
@@ -1586,7 +1586,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
                typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
                /* Disallow pseudotype argument */
-               if (typeStruct->typtype == 'p')
+               if (typeStruct->typtype == TYPTYPE_PSEUDO)
                {
                    free(prodesc->proname);
                    free(prodesc);
@@ -1596,7 +1596,7 @@ compile_plperl_function(Oid fn_oid, bool is_trigger)
                        format_type_be(procStruct->proargtypes.values[i]))));
                }
 
-               if (typeStruct->typtype == 'c')
+               if (typeStruct->typtype == TYPTYPE_COMPOSITE)
                    prodesc->arg_is_rowtype[i] = true;
                else
                {
index 44fae0f1b2831f2358d2d7d042038bde6d5bb0fb..74918c890ca528b368ddd1a3779da256870e58d9 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.113 2007/02/09 03:35:34 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.114 2007/04/02 03:49:41 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -406,7 +406,7 @@ do_compile(FunctionCallInfo fcinfo,
                argdtype = plpgsql_build_datatype(argtypeid, -1);
 
                /* Disallow pseudotype argument */
-               /* (note we already replaced ANYARRAY/ANYELEMENT) */
+               /* (note we already replaced polymorphic types) */
                /* (build_variable would do this, but wrong message) */
                if (argdtype->ttype != PLPGSQL_TTYPE_SCALAR &&
                    argdtype->ttype != PLPGSQL_TTYPE_ROW)
@@ -474,7 +474,7 @@ do_compile(FunctionCallInfo fcinfo,
             * the info available.
             */
            rettypeid = procStruct->prorettype;
-           if (rettypeid == ANYARRAYOID || rettypeid == ANYELEMENTOID)
+           if (IsPolymorphicType(rettypeid))
            {
                if (forValidator)
                {
@@ -482,6 +482,7 @@ do_compile(FunctionCallInfo fcinfo,
                        rettypeid = INT4ARRAYOID;
                    else
                        rettypeid = INT4OID;
+                   /* XXX what could we use for ANYENUM? */
                }
                else
                {
@@ -512,8 +513,8 @@ do_compile(FunctionCallInfo fcinfo,
            typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
            /* Disallow pseudotype result, except VOID or RECORD */
-           /* (note we already replaced ANYARRAY/ANYELEMENT) */
-           if (typeStruct->typtype == 'p')
+           /* (note we already replaced polymorphic types) */
+           if (typeStruct->typtype == TYPTYPE_PSEUDO)
            {
                if (rettypeid == VOIDOID ||
                    rettypeid == RECORDOID)
@@ -544,8 +545,7 @@ do_compile(FunctionCallInfo fcinfo,
                 * types, and not when the return is specified through an
                 * output parameter.
                 */
-               if ((procStruct->prorettype == ANYARRAYOID ||
-                    procStruct->prorettype == ANYELEMENTOID) &&
+               if (IsPolymorphicType(procStruct->prorettype) &&
                    num_out_args == 0)
                {
                    (void) plpgsql_build_variable("$0", 0,
@@ -1785,15 +1785,16 @@ build_datatype(HeapTuple typeTup, int32 typmod)
    typ->typoid = HeapTupleGetOid(typeTup);
    switch (typeStruct->typtype)
    {
-       case 'b':               /* base type */
-       case 'd':               /* domain */
+       case TYPTYPE_BASE:
+       case TYPTYPE_DOMAIN:
+       case TYPTYPE_ENUM:
            typ->ttype = PLPGSQL_TTYPE_SCALAR;
            break;
-       case 'c':               /* composite, ie, rowtype */
+       case TYPTYPE_COMPOSITE:
            Assert(OidIsValid(typeStruct->typrelid));
            typ->ttype = PLPGSQL_TTYPE_ROW;
            break;
-       case 'p':               /* pseudo */
+       case TYPTYPE_PSEUDO:
            if (typ->typoid == RECORDOID)
                typ->ttype = PLPGSQL_TTYPE_REC;
            else
@@ -2026,6 +2027,7 @@ plpgsql_resolve_polymorphic_argtypes(int numargs,
            switch (argtypes[i])
            {
                case ANYELEMENTOID:
+               case ANYENUMOID:                /* XXX dubious */
                    argtypes[i] = INT4OID;
                    break;
                case ANYARRAYOID:
index 41d2b3e54481eb2f0d0f957c67e6d650f3557a00..0654a044b3ea628ba95ab043faeeae38c93b4955 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.192 2007/03/27 23:21:12 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.193 2007/04/02 03:49:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -3294,8 +3294,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                PLpgSQL_row *row = (PLpgSQL_row *) target;
 
                /* Source must be of RECORD or composite type */
-               if (!(valtype == RECORDOID ||
-                     get_typtype(valtype) == 'c'))
+               if (!type_is_rowtype(valtype))
                    ereport(ERROR,
                            (errcode(ERRCODE_DATATYPE_MISMATCH),
                             errmsg("cannot assign non-composite value to a row variable")));
@@ -3337,8 +3336,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
                PLpgSQL_rec *rec = (PLpgSQL_rec *) target;
 
                /* Source must be of RECORD or composite type */
-               if (!(valtype == RECORDOID ||
-                     get_typtype(valtype) == 'c'))
+               if (!type_is_rowtype(valtype))
                    ereport(ERROR,
                            (errcode(ERRCODE_DATATYPE_MISMATCH),
                             errmsg("cannot assign non-composite value to a record variable")));
index 9b02160da4ddf89dcf30ef2f73a433a9945ea824..f4d27dc8d8f23d4a6bf9d35c64245a24c4dec824 100644 (file)
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.36 2007/01/30 22:05:13 tgl Exp $
+ *   $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_handler.c,v 1.37 2007/04/02 03:49:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -147,8 +147,8 @@ plpgsql_validator(PG_FUNCTION_ARGS)
    functyptype = get_typtype(proc->prorettype);
 
    /* Disallow pseudotype result */
-   /* except for TRIGGER, RECORD, VOID, ANYARRAY, or ANYELEMENT */
-   if (functyptype == 'p')
+   /* except for TRIGGER, RECORD, VOID, or polymorphic */
+   if (functyptype == TYPTYPE_PSEUDO)
    {
        /* we assume OPAQUE with no arguments means a trigger */
        if (proc->prorettype == TRIGGEROID ||
@@ -156,8 +156,7 @@ plpgsql_validator(PG_FUNCTION_ARGS)
            istrigger = true;
        else if (proc->prorettype != RECORDOID &&
                 proc->prorettype != VOIDOID &&
-                proc->prorettype != ANYARRAYOID &&
-                proc->prorettype != ANYELEMENTOID)
+                !IsPolymorphicType(proc->prorettype))
            ereport(ERROR,
                    (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                     errmsg("plpgsql functions cannot return type %s",
@@ -165,15 +164,14 @@ plpgsql_validator(PG_FUNCTION_ARGS)
    }
 
    /* Disallow pseudotypes in arguments (either IN or OUT) */
-   /* except for ANYARRAY or ANYELEMENT */
+   /* except for polymorphic */
    numargs = get_func_arg_info(tuple,
                                &argtypes, &argnames, &argmodes);
    for (i = 0; i < numargs; i++)
    {
-       if (get_typtype(argtypes[i]) == 'p')
+       if (get_typtype(argtypes[i]) == TYPTYPE_PSEUDO)
        {
-           if (argtypes[i] != ANYARRAYOID &&
-               argtypes[i] != ANYELEMENTOID)
+           if (!IsPolymorphicType(argtypes[i]))
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("plpgsql functions cannot take type %s",
index 78c9f6c66e0e0057b3e8987620b9e10ec5d51186..31ce60373f61555bde119b9e270b4c35a151dbed 100644 (file)
@@ -1,7 +1,7 @@
 /**********************************************************************
  * plpython.c - python as a procedural language for PostgreSQL
  *
- * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.96 2007/02/21 03:27:32 adunstan Exp $
+ * $PostgreSQL: pgsql/src/pl/plpython/plpython.c,v 1.97 2007/04/02 03:49:42 tgl Exp $
  *
  *********************************************************************
  */
@@ -1185,7 +1185,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
            rvTypeStruct = (Form_pg_type) GETSTRUCT(rvTypeTup);
 
            /* Disallow pseudotype result, except for void */
-           if (rvTypeStruct->typtype == 'p' &&
+           if (rvTypeStruct->typtype == TYPTYPE_PSEUDO &&
                procStruct->prorettype != VOIDOID)
            {
                if (procStruct->prorettype == TRIGGEROID)
@@ -1199,7 +1199,7 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
                                  format_type_be(procStruct->prorettype))));
            }
 
-           if (rvTypeStruct->typtype == 'c')
+           if (rvTypeStruct->typtype == TYPTYPE_COMPOSITE)
            {
                /*
                 * Tuple: set up later, during first call to
@@ -1258,13 +1258,13 @@ PLy_procedure_create(FunctionCallInfo fcinfo, Oid tgreloid,
            argTypeStruct = (Form_pg_type) GETSTRUCT(argTypeTup);
 
            /* Disallow pseudotype argument */
-           if (argTypeStruct->typtype == 'p')
+           if (argTypeStruct->typtype == TYPTYPE_PSEUDO)
                ereport(ERROR,
                        (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
                         errmsg("plpython functions cannot take type %s",
                        format_type_be(procStruct->proargtypes.values[i]))));
 
-           if (argTypeStruct->typtype != 'c')
+           if (argTypeStruct->typtype != TYPTYPE_COMPOSITE)
                PLy_input_datum_func(&(proc->args[i]),
                                     procStruct->proargtypes.values[i],
                                     argTypeTup);
@@ -2338,7 +2338,7 @@ PLy_spi_prepare(PyObject * self, PyObject * args)
 
                    plan->types[i] = typeId;
                    typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
-                   if (typeStruct->typtype != 'c')
+                   if (typeStruct->typtype != TYPTYPE_COMPOSITE)
                        PLy_output_datum_func(&plan->args[i], typeTup);
                    else
                        elog(ERROR, "tuples not handled in plpy.prepare, yet.");
index 2cd26d06412cd94f76112e2056224030ad4c3c01..093d990af33dad8353e98d35988902daf858c3cd 100644 (file)
@@ -2,7 +2,7 @@
  * pltcl.c     - PostgreSQL support for Tcl as
  *               procedural language (PL)
  *
- *   $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.111 2007/02/21 03:27:32 adunstan Exp $
+ *   $PostgreSQL: pgsql/src/pl/tcl/pltcl.c,v 1.112 2007/04/02 03:49:42 tgl Exp $
  *
  **********************************************************************/
 
@@ -1051,7 +1051,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
            typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
            /* Disallow pseudotype result, except VOID */
-           if (typeStruct->typtype == 'p')
+           if (typeStruct->typtype == TYPTYPE_PSEUDO)
            {
                if (procStruct->prorettype == VOIDOID)
                     /* okay */ ;
@@ -1074,7 +1074,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
                }
            }
 
-           if (typeStruct->typtype == 'c')
+           if (typeStruct->typtype == TYPTYPE_COMPOSITE)
            {
                free(prodesc->proname);
                free(prodesc);
@@ -1112,7 +1112,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
                typeStruct = (Form_pg_type) GETSTRUCT(typeTup);
 
                /* Disallow pseudotype argument */
-               if (typeStruct->typtype == 'p')
+               if (typeStruct->typtype == TYPTYPE_PSEUDO)
                {
                    free(prodesc->proname);
                    free(prodesc);
@@ -1122,7 +1122,7 @@ compile_pltcl_function(Oid fn_oid, Oid tgreloid)
                        format_type_be(procStruct->proargtypes.values[i]))));
                }
 
-               if (typeStruct->typtype == 'c')
+               if (typeStruct->typtype == TYPTYPE_COMPOSITE)
                {
                    prodesc->arg_is_rowtype[i] = true;
                    snprintf(buf, sizeof(buf), "__PLTcl_Tup_%d", i + 1);
diff --git a/src/test/regress/expected/enum.out b/src/test/regress/expected/enum.out
new file mode 100644 (file)
index 0000000..4fa2d07
--- /dev/null
@@ -0,0 +1,407 @@
+--
+-- Enum tests
+--
+CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple');
+--
+-- Did it create the right number of rows?
+--
+SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype;
+ count 
+-------
+     6
+(1 row)
+
+--
+-- I/O functions
+--
+SELECT 'red'::rainbow;
+ rainbow 
+---------
+ red
+(1 row)
+
+SELECT 'mauve'::rainbow;
+ERROR:  invalid input value for enum rainbow: "mauve"
+--
+-- Basic table creation, row selection
+--
+CREATE TABLE enumtest (col rainbow);
+INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green');
+COPY enumtest FROM stdin;
+SELECT * FROM enumtest;
+  col   
+--------
+ red
+ orange
+ yellow
+ green
+ blue
+ purple
+(6 rows)
+
+--
+-- Operators, no index
+--
+SELECT * FROM enumtest WHERE col = 'orange';
+  col   
+--------
+ orange
+(1 row)
+
+SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col;
+  col   
+--------
+ red
+ yellow
+ green
+ blue
+ purple
+(5 rows)
+
+SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col;
+  col   
+--------
+ green
+ blue
+ purple
+(3 rows)
+
+SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col;
+  col   
+--------
+ yellow
+ green
+ blue
+ purple
+(4 rows)
+
+SELECT * FROM enumtest WHERE col < 'green' ORDER BY col;
+  col   
+--------
+ red
+ orange
+ yellow
+(3 rows)
+
+SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col;
+  col   
+--------
+ red
+ orange
+ yellow
+ green
+(4 rows)
+
+--
+-- Cast to/from text
+--
+SELECT 'red'::rainbow::text || 'hithere';
+  ?column?  
+------------
+ redhithere
+(1 row)
+
+SELECT 'red'::text::rainbow = 'red'::rainbow;
+ ?column? 
+----------
+ t
+(1 row)
+
+--
+-- Aggregates
+--
+SELECT min(col) FROM enumtest;
+ min 
+-----
+ red
+(1 row)
+
+SELECT max(col) FROM enumtest;
+  max   
+--------
+ purple
+(1 row)
+
+SELECT max(col) FROM enumtest WHERE col < 'green';
+  max   
+--------
+ yellow
+(1 row)
+
+--
+-- Index tests, force use of index
+--
+SET enable_seqscan = off;
+SET enable_bitmapscan = off;
+--
+-- Btree index / opclass with the various operators
+--
+CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col);
+SELECT * FROM enumtest WHERE col = 'orange';
+  col   
+--------
+ orange
+(1 row)
+
+SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col;
+  col   
+--------
+ red
+ yellow
+ green
+ blue
+ purple
+(5 rows)
+
+SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col;
+  col   
+--------
+ green
+ blue
+ purple
+(3 rows)
+
+SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col;
+  col   
+--------
+ yellow
+ green
+ blue
+ purple
+(4 rows)
+
+SELECT * FROM enumtest WHERE col < 'green' ORDER BY col;
+  col   
+--------
+ red
+ orange
+ yellow
+(3 rows)
+
+SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col;
+  col   
+--------
+ red
+ orange
+ yellow
+ green
+(4 rows)
+
+SELECT min(col) FROM enumtest;
+ min 
+-----
+ red
+(1 row)
+
+SELECT max(col) FROM enumtest;
+  max   
+--------
+ purple
+(1 row)
+
+SELECT max(col) FROM enumtest WHERE col < 'green';
+  max   
+--------
+ yellow
+(1 row)
+
+DROP INDEX enumtest_btree;
+--
+-- Hash index / opclass with the = operator
+--
+CREATE INDEX enumtest_hash ON enumtest USING hash (col);
+SELECT * FROM enumtest WHERE col = 'orange';
+  col   
+--------
+ orange
+(1 row)
+
+DROP INDEX enumtest_hash;
+--
+-- End index tests
+--
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+--
+-- Domains over enums
+--
+CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue'));
+SELECT 'red'::rgb;
+ rgb 
+-----
+ red
+(1 row)
+
+SELECT 'purple'::rgb;
+ERROR:  value for domain rgb violates check constraint "rgb_check"
+SELECT 'purple'::rainbow::rgb;
+ERROR:  value for domain rgb violates check constraint "rgb_check"
+DROP DOMAIN rgb;
+--
+-- Arrays
+--
+SELECT '{red,green,blue}'::rainbow[];
+     rainbow      
+------------------
+ {red,green,blue}
+(1 row)
+
+SELECT ('{red,green,blue}'::rainbow[])[2];
+ rainbow 
+---------
+ green
+(1 row)
+
+SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]);
+ ?column? 
+----------
+ t
+(1 row)
+
+SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]);
+ ?column? 
+----------
+ f
+(1 row)
+
+SELECT 'red' = ALL ('{red,red}'::rainbow[]);
+ ?column? 
+----------
+ t
+(1 row)
+
+--
+-- Support functions
+--
+SELECT enum_first(NULL::rainbow);
+ enum_first 
+------------
+ red
+(1 row)
+
+SELECT enum_last('green'::rainbow);
+ enum_last 
+-----------
+ purple
+(1 row)
+
+SELECT enum_range(NULL::rainbow);
+              enum_range               
+---------------------------------------
+ {red,orange,yellow,green,blue,purple}
+(1 row)
+
+SELECT enum_range('orange'::rainbow, 'green'::rainbow);
+      enum_range       
+-----------------------
+ {orange,yellow,green}
+(1 row)
+
+SELECT enum_range(NULL, 'green'::rainbow);
+        enum_range         
+---------------------------
+ {red,orange,yellow,green}
+(1 row)
+
+SELECT enum_range('orange'::rainbow, NULL);
+            enum_range             
+-----------------------------------
+ {orange,yellow,green,blue,purple}
+(1 row)
+
+SELECT enum_range(NULL::rainbow, NULL);
+              enum_range               
+---------------------------------------
+ {red,orange,yellow,green,blue,purple}
+(1 row)
+
+--
+-- User functions, can't test perl/python etc here since may not be compiled.
+--
+CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$
+BEGIN
+RETURN $1::text || 'omg';
+END
+$$ LANGUAGE plpgsql;
+SELECT echo_me('red'::rainbow);
+ echo_me 
+---------
+ redomg
+(1 row)
+
+--
+-- Concrete function should override generic one
+--
+CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$
+BEGIN
+RETURN $1::text || 'wtf';
+END
+$$ LANGUAGE plpgsql;
+SELECT echo_me('red'::rainbow);
+ echo_me 
+---------
+ redwtf
+(1 row)
+
+--
+-- If we drop the original generic one, we don't have to qualify the type
+-- anymore, since there's only one match
+--
+DROP FUNCTION echo_me(anyenum);
+SELECT echo_me('red');
+ echo_me 
+---------
+ redwtf
+(1 row)
+
+DROP FUNCTION echo_me(rainbow);
+--
+-- RI triggers on enum types
+--
+CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY);
+NOTICE:  CREATE TABLE / PRIMARY KEY will create implicit index "enumtest_parent_pkey" for table "enumtest_parent"
+CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent);
+INSERT INTO enumtest_parent VALUES ('red');
+INSERT INTO enumtest_child VALUES ('red');
+INSERT INTO enumtest_child VALUES ('blue');  -- fail
+ERROR:  insert or update on table "enumtest_child" violates foreign key constraint "enumtest_child_parent_fkey"
+DETAIL:  Key (parent)=(blue) is not present in table "enumtest_parent".
+DELETE FROM enumtest_parent;  -- fail
+ERROR:  update or delete on table "enumtest_parent" violates foreign key constraint "enumtest_child_parent_fkey" on table "enumtest_child"
+DETAIL:  Key (id)=(red) is still referenced from table "enumtest_child".
+--
+-- cross-type RI should fail
+--
+CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly');
+CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent);
+ERROR:  foreign key constraint "enumtest_bogus_child_parent_fkey" cannot be implemented
+DETAIL:  Key columns "parent" and "id" are of incompatible types: bogus and rainbow.
+DROP TYPE bogus;
+--
+-- Cleanup
+--
+DROP TABLE enumtest_child;
+DROP TABLE enumtest_parent;
+DROP TABLE enumtest;
+DROP TYPE rainbow;
+--
+-- Verify properly cleaned up
+--
+SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
+ count 
+-------
+     0
+(1 row)
+
+SELECT * FROM pg_enum WHERE NOT EXISTS
+  (SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
+ enumtypid | enumlabel 
+-----------+-----------
+(0 rows)
+
index 841d77c78e55033e359588e707bc376ea05d057b..29a0a190d6195c7e11421314623e70b2f18365f6 100644 (file)
@@ -80,7 +80,7 @@ CREATE AGGREGATE myaggp01a(*) (SFUNC = stfnp, STYPE = int4[],
 CREATE AGGREGATE myaggp02a(*) (SFUNC = stfnp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --     N    P
 -- should CREATE
 CREATE AGGREGATE myaggp03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -92,11 +92,11 @@ CREATE AGGREGATE myaggp03b(*) (SFUNC = stfp, STYPE = int4[],
 CREATE AGGREGATE myaggp04a(*) (SFUNC = stfp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 CREATE AGGREGATE myaggp04b(*) (SFUNC = stfp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    Case2 (R = P) && ((B = P) || (B = N))
 --    -------------------------------------
 --    S    tf1      B    tf2
@@ -151,13 +151,13 @@ ERROR:  function tfp(integer[], anyelement) does not exist
 CREATE AGGREGATE myaggp13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    P    N        N    P
 -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
 CREATE AGGREGATE myaggp14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    P    N        P    N
 -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
 CREATE AGGREGATE myaggp15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -173,21 +173,21 @@ ERROR:  function tf2p(anyarray, anyelement) does not exist
 CREATE AGGREGATE myaggp17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 CREATE AGGREGATE myaggp17b(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    P    P        N    P
 -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
 CREATE AGGREGATE myaggp18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   FINALFUNC = ffp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 CREATE AGGREGATE myaggp18b(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    P    P        P    N
 -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
 CREATE AGGREGATE myaggp19a(BASETYPE = anyelement, SFUNC = tf1p,
@@ -217,11 +217,11 @@ CREATE AGGREGATE myaggn01b(*) (SFUNC = stfnp, STYPE = int4[],
 CREATE AGGREGATE myaggn02a(*) (SFUNC = stfnp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 CREATE AGGREGATE myaggn02b(*) (SFUNC = stfnp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --     N    P
 -- should CREATE
 CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
@@ -231,7 +231,7 @@ CREATE AGGREGATE myaggn03a(*) (SFUNC = stfp, STYPE = int4[],
 CREATE AGGREGATE myaggn04a(*) (SFUNC = stfp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    Case4 (R = N) && ((B = P) || (B = N))
 --    -------------------------------------
 --    S    tf1      B    tf2
@@ -285,21 +285,21 @@ ERROR:  function tfp(integer[], anyelement) does not exist
 CREATE AGGREGATE myaggn13a(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 CREATE AGGREGATE myaggn13b(BASETYPE = int, SFUNC = tfnp, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    P    N        N    P
 -- should ERROR: tf2p(anyarray, int) not matched by tf2p(int[],anyelement)
 CREATE AGGREGATE myaggn14a(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 CREATE AGGREGATE myaggn14b(BASETYPE = int, SFUNC = tf2p, STYPE = anyarray,
   INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    P    N        P    N
 -- should ERROR: tfnp(anyarray, anyelement) not matched by tfnp(int[],int)
 CREATE AGGREGATE myaggn15a(BASETYPE = anyelement, SFUNC = tfnp,
@@ -321,13 +321,13 @@ ERROR:  function tf2p(anyarray, anyelement) does not exist
 CREATE AGGREGATE myaggn17a(BASETYPE = int, SFUNC = tf1p, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    P    P        N    P
 -- should ERROR: tfp(anyarray, int) not matched by tfp(anyarray, anyelement)
 CREATE AGGREGATE myaggn18a(BASETYPE = int, SFUNC = tfp, STYPE = anyarray,
   FINALFUNC = ffnp, INITCOND = '{}');
 ERROR:  cannot determine transition data type
-DETAIL:  An aggregate using "anyarray" or "anyelement" as transition type must have at least one argument of either type.
+DETAIL:  An aggregate using a polymorphic transition type must have at least one polymorphic argument.
 --    P    P        P    N
 -- should ERROR: tf1p(anyarray, anyelement) not matched by tf1p(anyarray, int)
 CREATE AGGREGATE myaggn19a(BASETYPE = anyelement, SFUNC = tf1p,
index 8abc323a70e451c7fdf03cb9b42b4497677794f3..957bd49f24615baa09c545c49c665d16982287e0 100644 (file)
@@ -500,7 +500,7 @@ SELECT dup(22);
 (1 row)
 
 SELECT dup('xyz'); -- fails
-ERROR:  could not determine anyarray/anyelement type because input has type "unknown"
+ERROR:  could not determine polymorphic type because input has type "unknown"
 SELECT dup('xyz'::text);
         dup        
 -------------------
@@ -527,4 +527,4 @@ DROP FUNCTION dup(anyelement);
 CREATE FUNCTION bad (f1 int, out f2 anyelement, out f3 anyarray)
 AS 'select $1, array[$1,$1]' LANGUAGE sql;
 ERROR:  cannot determine result data type
-DETAIL:  A function returning "anyarray" or "anyelement" must have at least one argument of either type.
+DETAIL:  A function returning a polymorphic type must have at least one polymorphic argument.
index 5e6a570e24464f61f07ec03e09940fc20c0d15c2..6a31d52bcfc291f671ef15fca15b2f2c0941a2d5 100644 (file)
@@ -97,6 +97,7 @@ SELECT relname, relhasindex
  pg_database             | t
  pg_depend               | t
  pg_description          | t
+ pg_enum                 | t
  pg_index                | t
  pg_inherits             | t
  pg_language             | t
@@ -141,7 +142,7 @@ SELECT relname, relhasindex
  timetz_tbl              | f
  tinterval_tbl           | f
  varchar_tbl             | f
-(130 rows)
+(131 rows)
 
 --
 -- another sanity check: every system catalog that has OIDs should have
index 11c9298e9aed472b2c60fb1c1f5e40997047c2e7..b2db870da4f1fb2108777a9b6f2267e5e9017a48 100644 (file)
@@ -17,7 +17,7 @@ SELECT p1.oid, p1.typname
 FROM pg_type as p1
 WHERE p1.typnamespace = 0 OR
     (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
-    (p1.typtype not in ('b', 'c', 'd', 'p')) OR
+    (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR
     NOT p1.typisdefined OR
     (p1.typalign not in ('c', 's', 'i', 'd')) OR
     (p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -55,11 +55,11 @@ WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
 -----+---------
 (0 rows)
 
--- Look for basic types that don't have an array type.
+-- Look for basic or enum types that don't have an array type.
 -- NOTE: as of 8.0, this check finds smgr and unknown.
 SELECT p1.oid, p1.typname
 FROM pg_type as p1
-WHERE p1.typtype in ('b') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS
+WHERE p1.typtype in ('b','e') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS
     (SELECT 1 FROM pg_type as p2
      WHERE p2.typname = ('_' || p1.typname)::name AND
            p2.typelem = p1.oid);
index d25ed4c8cadbe9cf9414e0e810815fd405ab4149..b7cc56c62130c78876d8245a9d35dd15fa233419 100644 (file)
@@ -1,8 +1,8 @@
 # ----------
 # The first group of parallel test
-# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.41 2007/03/19 16:44:41 tgl Exp $
+# $PostgreSQL: pgsql/src/test/regress/parallel_schedule,v 1.42 2007/04/02 03:49:42 tgl Exp $
 # ----------
-test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid
+test: boolean char name varchar text int2 int4 int8 oid float4 float8 bit numeric uuid enum
 
 # Depends on things setup during char, varchar and text
 test: strings
index 31bac6126075918ee9de02d7f804ee6ae89364f3..85c03f33c9e397ceb5d526bbeca6316a5b2ab7f3 100644 (file)
@@ -1,4 +1,4 @@
-# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.38 2007/03/13 00:33:44 tgl Exp $
+# $PostgreSQL: pgsql/src/test/regress/serial_schedule,v 1.39 2007/04/02 03:49:42 tgl Exp $
 # This should probably be in an order similar to parallel_schedule.
 test: boolean
 test: char
@@ -14,6 +14,7 @@ test: float8
 test: bit
 test: numeric
 test: uuid
+test: enum
 test: strings
 test: numerology
 test: point
diff --git a/src/test/regress/sql/enum.sql b/src/test/regress/sql/enum.sql
new file mode 100644 (file)
index 0000000..387e8e7
--- /dev/null
@@ -0,0 +1,171 @@
+--
+-- Enum tests
+--
+
+CREATE TYPE rainbow AS ENUM ('red', 'orange', 'yellow', 'green', 'blue', 'purple');
+
+--
+-- Did it create the right number of rows?
+--
+SELECT COUNT(*) FROM pg_enum WHERE enumtypid = 'rainbow'::regtype;
+
+--
+-- I/O functions
+--
+SELECT 'red'::rainbow;
+SELECT 'mauve'::rainbow;
+
+--
+-- Basic table creation, row selection
+--
+CREATE TABLE enumtest (col rainbow);
+INSERT INTO enumtest values ('red'), ('orange'), ('yellow'), ('green');
+COPY enumtest FROM stdin;
+blue
+purple
+\.
+SELECT * FROM enumtest;
+
+--
+-- Operators, no index
+--
+SELECT * FROM enumtest WHERE col = 'orange';
+SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col;
+SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col;
+SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col;
+SELECT * FROM enumtest WHERE col < 'green' ORDER BY col;
+SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col;
+
+--
+-- Cast to/from text
+--
+SELECT 'red'::rainbow::text || 'hithere';
+SELECT 'red'::text::rainbow = 'red'::rainbow;
+
+--
+-- Aggregates
+--
+SELECT min(col) FROM enumtest;
+SELECT max(col) FROM enumtest;
+SELECT max(col) FROM enumtest WHERE col < 'green';
+
+--
+-- Index tests, force use of index
+--
+SET enable_seqscan = off;
+SET enable_bitmapscan = off;
+
+--
+-- Btree index / opclass with the various operators
+--
+CREATE UNIQUE INDEX enumtest_btree ON enumtest USING btree (col);
+SELECT * FROM enumtest WHERE col = 'orange';
+SELECT * FROM enumtest WHERE col <> 'orange' ORDER BY col;
+SELECT * FROM enumtest WHERE col > 'yellow' ORDER BY col;
+SELECT * FROM enumtest WHERE col >= 'yellow' ORDER BY col;
+SELECT * FROM enumtest WHERE col < 'green' ORDER BY col;
+SELECT * FROM enumtest WHERE col <= 'green' ORDER BY col;
+SELECT min(col) FROM enumtest;
+SELECT max(col) FROM enumtest;
+SELECT max(col) FROM enumtest WHERE col < 'green';
+DROP INDEX enumtest_btree;
+
+--
+-- Hash index / opclass with the = operator
+--
+CREATE INDEX enumtest_hash ON enumtest USING hash (col);
+SELECT * FROM enumtest WHERE col = 'orange';
+DROP INDEX enumtest_hash;
+
+--
+-- End index tests
+--
+RESET enable_seqscan;
+RESET enable_bitmapscan;
+
+--
+-- Domains over enums
+--
+CREATE DOMAIN rgb AS rainbow CHECK (VALUE IN ('red', 'green', 'blue'));
+SELECT 'red'::rgb;
+SELECT 'purple'::rgb;
+SELECT 'purple'::rainbow::rgb;
+DROP DOMAIN rgb;
+
+--
+-- Arrays
+--
+SELECT '{red,green,blue}'::rainbow[];
+SELECT ('{red,green,blue}'::rainbow[])[2];
+SELECT 'red' = ANY ('{red,green,blue}'::rainbow[]);
+SELECT 'yellow' = ANY ('{red,green,blue}'::rainbow[]);
+SELECT 'red' = ALL ('{red,green,blue}'::rainbow[]);
+SELECT 'red' = ALL ('{red,red}'::rainbow[]);
+
+--
+-- Support functions
+--
+SELECT enum_first(NULL::rainbow);
+SELECT enum_last('green'::rainbow);
+SELECT enum_range(NULL::rainbow);
+SELECT enum_range('orange'::rainbow, 'green'::rainbow);
+SELECT enum_range(NULL, 'green'::rainbow);
+SELECT enum_range('orange'::rainbow, NULL);
+SELECT enum_range(NULL::rainbow, NULL);
+
+--
+-- User functions, can't test perl/python etc here since may not be compiled.
+--
+CREATE FUNCTION echo_me(anyenum) RETURNS text AS $$
+BEGIN
+RETURN $1::text || 'omg';
+END
+$$ LANGUAGE plpgsql;
+SELECT echo_me('red'::rainbow);
+--
+-- Concrete function should override generic one
+--
+CREATE FUNCTION echo_me(rainbow) RETURNS text AS $$
+BEGIN
+RETURN $1::text || 'wtf';
+END
+$$ LANGUAGE plpgsql;
+SELECT echo_me('red'::rainbow);
+--
+-- If we drop the original generic one, we don't have to qualify the type
+-- anymore, since there's only one match
+--
+DROP FUNCTION echo_me(anyenum);
+SELECT echo_me('red');
+DROP FUNCTION echo_me(rainbow);
+
+--
+-- RI triggers on enum types
+--
+CREATE TABLE enumtest_parent (id rainbow PRIMARY KEY);
+CREATE TABLE enumtest_child (parent rainbow REFERENCES enumtest_parent);
+INSERT INTO enumtest_parent VALUES ('red');
+INSERT INTO enumtest_child VALUES ('red');
+INSERT INTO enumtest_child VALUES ('blue');  -- fail
+DELETE FROM enumtest_parent;  -- fail
+--
+-- cross-type RI should fail
+--
+CREATE TYPE bogus AS ENUM('good', 'bad', 'ugly');
+CREATE TABLE enumtest_bogus_child(parent bogus REFERENCES enumtest_parent);
+DROP TYPE bogus;
+
+--
+-- Cleanup
+--
+DROP TABLE enumtest_child;
+DROP TABLE enumtest_parent;
+DROP TABLE enumtest;
+DROP TYPE rainbow;
+
+--
+-- Verify properly cleaned up
+--
+SELECT COUNT(*) FROM pg_type WHERE typname = 'rainbow';
+SELECT * FROM pg_enum WHERE NOT EXISTS
+  (SELECT 1 FROM pg_type WHERE pg_type.oid = enumtypid);
index 3677b90ceb9835c230f0c0d1a4d91cb5864899c6..b1caa33efbda6d32d56ecc7d396f08a5abae551e 100644 (file)
@@ -20,7 +20,7 @@ SELECT p1.oid, p1.typname
 FROM pg_type as p1
 WHERE p1.typnamespace = 0 OR
     (p1.typlen <= 0 AND p1.typlen != -1 AND p1.typlen != -2) OR
-    (p1.typtype not in ('b', 'c', 'd', 'p')) OR
+    (p1.typtype not in ('b', 'c', 'd', 'e', 'p')) OR
     NOT p1.typisdefined OR
     (p1.typalign not in ('c', 's', 'i', 'd')) OR
     (p1.typstorage not in ('p', 'x', 'e', 'm'));
@@ -49,12 +49,12 @@ FROM pg_type as p1
 WHERE (p1.typtype = 'c' AND p1.typrelid = 0) OR
     (p1.typtype != 'c' AND p1.typrelid != 0);
 
--- Look for basic types that don't have an array type.
+-- Look for basic or enum types that don't have an array type.
 -- NOTE: as of 8.0, this check finds smgr and unknown.
 
 SELECT p1.oid, p1.typname
 FROM pg_type as p1
-WHERE p1.typtype in ('b') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS
+WHERE p1.typtype in ('b','e') AND p1.typname NOT LIKE E'\\_%' AND NOT EXISTS
     (SELECT 1 FROM pg_type as p2
      WHERE p2.typname = ('_' || p1.typname)::name AND
            p2.typelem = p1.oid);