Hi,

The only thing I considered wrong with the previous patch was it's odd
behaviour w.r.t. creation of types with the same name as one in
pg_catalog. However, Tom said this is not a serious issue given the
same problem exists in many other places in PostgreSQL.

So, I've added some regression tests and here is the patch for
inclusion.

Have a nice day,
-- 
Martijn van Oosterhout   <kleptog@svana.org>   http://svana.org/kleptog/
> Patent. n. Genius is 5% inspiration and 95% perspiration. A patent is a
> tool for doing 5% of the work and then sitting around waiting for someone
> else to do the other 95% so you can sue them.
Index: doc/src/sgml/xtypes.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/xtypes.sgml,v
retrieving revision 1.25
diff -c -r1.25 xtypes.sgml
*** doc/src/sgml/xtypes.sgml    10 Jan 2005 00:04:38 -0000      1.25
--- doc/src/sgml/xtypes.sgml    21 Feb 2006 10:30:54 -0000
***************
*** 168,175 ****
   </para>
  
   <para>
!   To define the <type>complex</type> type, we need to create the
!   user-defined I/O functions before creating the type:
  
  <programlisting>
  CREATE FUNCTION complex_in(cstring)
--- 168,180 ----
   </para>
  
   <para>
!   To define the <type>complex</type> type, we first declare it as a shell 
type:
! 
! <programlisting>
! CREATE TYPE complex;
! </programlisting>
! 
!   Then we create the user-defined I/O functions needed to create the type:
  
  <programlisting>
  CREATE FUNCTION complex_in(cstring)
***************
*** 193,206 ****
     LANGUAGE C IMMUTABLE STRICT;
  </programlisting>
  
-   Notice that the declarations of the input and output functions must
-   reference the not-yet-defined type.  This is allowed, but will draw
-   warning messages that may be ignored.  The input function must
-   appear first.
   </para>
  
   <para>
!   Finally, we can declare the data type:
  <programlisting>
  CREATE TYPE complex (
     internallength = 16, 
--- 198,207 ----
     LANGUAGE C IMMUTABLE STRICT;
  </programlisting>
  
   </para>
  
   <para>
!   Finally, we can declare the data type properly:
  <programlisting>
  CREATE TYPE complex (
     internallength = 16, 
Index: doc/src/sgml/ref/create_type.sgml
===================================================================
RCS file: /projects/cvsroot/pgsql/doc/src/sgml/ref/create_type.sgml,v
retrieving revision 1.60
diff -c -r1.60 create_type.sgml
*** doc/src/sgml/ref/create_type.sgml   13 Jan 2006 18:06:45 -0000      1.60
--- doc/src/sgml/ref/create_type.sgml   21 Feb 2006 10:30:54 -0000
***************
*** 23,29 ****
  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> (
      INPUT = <replaceable class="parameter">input_function</replaceable>,
      OUTPUT = <replaceable class="parameter">output_function</replaceable>
      [ , RECEIVE = <replaceable 
class="parameter">receive_function</replaceable> ]
--- 23,29 ----
  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> [ (
      INPUT = <replaceable class="parameter">input_function</replaceable>,
      OUTPUT = <replaceable class="parameter">output_function</replaceable>
      [ , RECEIVE = <replaceable 
class="parameter">receive_function</replaceable> ]
***************
*** 36,42 ****
      [ , DEFAULT = <replaceable class="parameter">default</replaceable> ]
      [ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
      [ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
! )
  </synopsis>
   </refsynopsisdiv>
  
--- 36,42 ----
      [ , DEFAULT = <replaceable class="parameter">default</replaceable> ]
      [ , ELEMENT = <replaceable class="parameter">element</replaceable> ]
      [ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ]
! ) ]
  </synopsis>
   </refsynopsisdiv>
  
***************
*** 142,158 ****
  
    <para>
     You should at this point be wondering how the input and output functions
!    can be declared to have results or arguments of the new type, when they 
have
!    to be created before the new type can be created.  The answer is that the
!    input function must be created first, then the output function (and
!    the binary I/O functions if wanted), and finally the data type.
!    <productname>PostgreSQL</productname> will first see the name of the new
!    data type as the return type of the input function.  It will create a
!    <quote>shell</> type, which is simply a placeholder entry in
!    the system catalog, and link the input function definition to the shell
!    type.  Similarly the other functions will be linked to the (now already
!    existing) shell type.  Finally, <command>CREATE TYPE</> replaces the
!    shell entry with a complete type definition, and the new type can be used.
    </para>
  
    <para>
--- 142,157 ----
  
    <para>
     You should at this point be wondering how the input and output functions
!    can be declared to have results or arguments of the new type, when they
!    have to be created before the new type can be created.  The answer is
!    that the the entire declaration portion of the type is optional. If you
!    just issue the command <command>CREATE TYPE foo</>, it will create a
!    <quote>shell</> type, which is simply a placeholder entry in the system
!    catalog. With the shell type in place, you can create the necessary
!    functions. Finally, <command>CREATE TYPE</> with a full definition
!    replaces the shell entry with a complete type definition and the new
!    type can be used. Note that many procedural languages do not allow you to
!    create functions that create or return shell types.
    </para>
  
    <para>
***************
*** 468,473 ****
--- 467,485 ----
     a notice and change the function's declaration to use the correct
     types.
    </para>
+ 
+   <para>
+    Prior to version 8.2, you could not explicitly create shell types, but an
+    alternate way to create shell types was provided. For this to work the
+    input function must be created first, then the output function (and the
+    binary I/O functions if wanted), and finally the data type. 
+    <productname>PostgreSQL</productname> will first see the name of the new
+    data type as the return type of the input function and create the shell
+    type. This workaround only works for types where the type input function
+    is of language <literal>C</> or <literal>internal</>.
+   </para>
+ 
+ 
   </refsect1>
   
   <refsect1>
Index: src/backend/catalog/pg_type.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/catalog/pg_type.c,v
retrieving revision 1.104
diff -c -r1.104 pg_type.c
*** src/backend/catalog/pg_type.c       15 Oct 2005 02:49:14 -0000      1.104
--- src/backend/catalog/pg_type.c       21 Feb 2006 10:30:55 -0000
***************
*** 76,90 ****
        values[i++] = NameGetDatum(&name);      /* typname */
        values[i++] = ObjectIdGetDatum(typeNamespace);          /* typnamespace 
*/
        values[i++] = ObjectIdGetDatum(GetUserId());            /* typowner */
!       values[i++] = Int16GetDatum(0);         /* typlen */
!       values[i++] = BoolGetDatum(false);      /* typbyval */
!       values[i++] = CharGetDatum(0);          /* typtype */
        values[i++] = BoolGetDatum(false);      /* typisdefined */
!       values[i++] = CharGetDatum(0);          /* typdelim */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */
!       values[i++] = ObjectIdGetDatum(InvalidOid); /* typinput */
!       values[i++] = ObjectIdGetDatum(InvalidOid); /* typoutput */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */
--- 76,90 ----
        values[i++] = NameGetDatum(&name);      /* typname */
        values[i++] = ObjectIdGetDatum(typeNamespace);          /* typnamespace 
*/
        values[i++] = ObjectIdGetDatum(GetUserId());            /* typowner */
!       values[i++] = Int16GetDatum(4);         /* typlen */
!       values[i++] = BoolGetDatum(true);       /* typbyval */
!       values[i++] = CharGetDatum('p');                /* typtype */
        values[i++] = BoolGetDatum(false);      /* typisdefined */
!       values[i++] = CharGetDatum('\054');             /* typdelim */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */
!       values[i++] = ObjectIdGetDatum(2398); /* typinput */
!       values[i++] = ObjectIdGetDatum(2399); /* typoutput */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */
        values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */
Index: src/backend/commands/typecmds.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/commands/typecmds.c,v
retrieving revision 1.86
diff -c -r1.86 typecmds.c
*** src/backend/commands/typecmds.c     13 Jan 2006 18:06:45 -0000      1.86
--- src/backend/commands/typecmds.c     21 Feb 2006 10:30:56 -0000
***************
*** 229,246 ****
        }
  
        /*
-        * make sure we have our required definitions
-        */
-       if (inputName == NIL)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                errmsg("type input function must be 
specified")));
-       if (outputName == NIL)
-               ereport(ERROR,
-                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-                                errmsg("type output function must be 
specified")));
- 
-       /*
         * Look to see if type already exists (presumably as a shell; if not,
         * TypeCreate will complain).  If it doesn't, create it as a shell, so
         * that the OID is known for use in the I/O function definitions.
--- 229,234 ----
***************
*** 255,260 ****
--- 243,264 ----
                /* Make new shell type visible for modification below */
                CommandCounterIncrement();
        }
+       
+       /* Shell type definition, we're done */
+       if( parameters == NULL )
+               return;
+ 
+       /*
+        * make sure we have our required definitions
+        */
+       if (inputName == NIL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type input function must be 
specified")));
+       if (outputName == NIL)
+               ereport(ERROR,
+                               (errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+                                errmsg("type output function must be 
specified")));
  
        /*
         * Convert I/O proc names to OIDs
Index: src/backend/parser/gram.y
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.530
diff -c -r2.530 gram.y
*** src/backend/parser/gram.y   19 Feb 2006 00:04:27 -0000      2.530
--- src/backend/parser/gram.y   21 Feb 2006 10:31:01 -0000
***************
*** 2690,2695 ****
--- 2690,2703 ----
                                        n->definition = $4;
                                        $$ = (Node *)n;
                                }
+                       | CREATE TYPE_P any_name 
+                               {       /* Shell type identified by lack of 
definition */
+                                       DefineStmt *n = makeNode(DefineStmt);
+                                       n->kind = OBJECT_TYPE;
+                                       n->defnames = $3;
+                                       n->definition = NULL;
+                                       $$ = (Node *)n;
+                               }
                        | CREATE TYPE_P any_name AS '(' TableFuncElementList ')'
                                {
                                        CompositeTypeStmt *n = 
makeNode(CompositeTypeStmt);
Index: src/backend/utils/adt/pseudotypes.c
===================================================================
RCS file: /projects/cvsroot/pgsql/src/backend/utils/adt/pseudotypes.c,v
retrieving revision 1.15
diff -c -r1.15 pseudotypes.c
*** src/backend/utils/adt/pseudotypes.c 31 Dec 2004 22:01:22 -0000      1.15
--- src/backend/utils/adt/pseudotypes.c 21 Feb 2006 10:31:01 -0000
***************
*** 321,323 ****
--- 321,350 ----
  
        PG_RETURN_VOID();                       /* keep compiler quiet */
  }
+ 
+ /*
+  * shell_in           - input routine for shell-type.
+  */
+ Datum
+ shell_in(PG_FUNCTION_ARGS)
+ {
+       ereport(ERROR,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("cannot accept a value of a shell type")));
+ 
+       PG_RETURN_VOID();                       /* keep compiler quiet */
+ }
+ 
+ /*
+  * shell_out          - output routine for shell-type.
+  */
+ Datum
+ shell_out(PG_FUNCTION_ARGS)
+ {
+       ereport(ERROR,
+                       (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
+                        errmsg("cannot display a value of a shell type")));
+ 
+       PG_RETURN_VOID();                       /* keep compiler quiet */
+ }
+ 
Index: src/include/catalog/pg_proc.h
===================================================================
RCS file: /projects/cvsroot/pgsql/src/include/catalog/pg_proc.h,v
retrieving revision 1.397
diff -c -r1.397 pg_proc.h
*** src/include/catalog/pg_proc.h       12 Feb 2006 03:22:19 -0000      1.397
--- src/include/catalog/pg_proc.h       21 Feb 2006 10:31:06 -0000
***************
*** 3319,3324 ****
--- 3319,3328 ----
  DESCR("I/O");
  DATA(insert OID = 2313 (  anyelement_out      PGNSP PGUID 12 f f t f i 1 2275 
"2283" _null_ _null_ _null_ anyelement_out - _null_ ));
  DESCR("I/O");
+ DATA(insert OID = 2398 (  shell_in                    PGNSP PGUID 12 f f t f 
i 1 2282 "2275" _null_ _null_ _null_ shell_in - _null_ ));
+ DESCR("I/O");
+ DATA(insert OID = 2399 (  shell_out                   PGNSP PGUID 12 f f t f 
i 1 2275 "2282" _null_ _null_ _null_ shell_out - _null_ ));
+ DESCR("I/O");
  
  /* cryptographic */
  DATA(insert OID =  2311 (  md5           PGNSP PGUID 12 f f t f i 1 25 "25" 
_null_ _null_ _null_      md5_text - _null_ ));
Index: src/test/regress/expected/create_type.out
===================================================================
RCS file: /projects/cvsroot/pgsql/src/test/regress/expected/create_type.out,v
retrieving revision 1.11
diff -c -r1.11 create_type.out
*** src/test/regress/expected/create_type.out   21 Nov 2003 22:32:49 -0000      
1.11
--- src/test/regress/expected/create_type.out   21 Feb 2006 10:31:07 -0000
***************
*** 13,26 ****
     output = int44out, 
     element = int4
  );
  -- Test type-related default values (broken in releases before PG 7.2)
  -- Make dummy I/O routines using the existing internal support for int4, text
  CREATE FUNCTION int42_in(cstring)
     RETURNS int42
     AS 'int4in'
     LANGUAGE 'internal' WITH (isStrict);
! NOTICE:  type "int42" is not yet defined
! DETAIL:  Creating a shell type definition.
  CREATE FUNCTION int42_out(int42)
     RETURNS cstring
     AS 'int4out'
--- 13,32 ----
     output = int44out, 
     element = int4
  );
+ -- Test creation and destruction of shell types
+ CREATE TYPE shell;
+ CREATE TYPE shell;   -- succeed, no error
+ DROP TYPE shell;
+ DROP TYPE shell;     -- fail, type not exist
+ ERROR:  type "shell" does not exist
  -- Test type-related default values (broken in releases before PG 7.2)
+ CREATE TYPE int42;
  -- Make dummy I/O routines using the existing internal support for int4, text
  CREATE FUNCTION int42_in(cstring)
     RETURNS int42
     AS 'int4in'
     LANGUAGE 'internal' WITH (isStrict);
! NOTICE:  return type int42 is only a shell
  CREATE FUNCTION int42_out(int42)
     RETURNS cstring
     AS 'int4out'
***************
*** 76,81 ****
--- 82,89 ----
  ERROR:  type "bad" does not exist
  COMMENT ON TYPE default_test_row IS 'good comment';
  COMMENT ON TYPE default_test_row IS NULL;
+ -- Check shell type create for existing types
+ CREATE TYPE text_w_default;
  DROP TYPE default_test_row CASCADE;
  NOTICE:  drop cascades to function get_default_test()
  DROP TABLE default_test;
Index: src/test/regress/sql/create_type.sql
===================================================================
RCS file: /projects/cvsroot/pgsql/src/test/regress/sql/create_type.sql,v
retrieving revision 1.7
diff -c -r1.7 create_type.sql
*** src/test/regress/sql/create_type.sql        21 Nov 2003 22:32:49 -0000      
1.7
--- src/test/regress/sql/create_type.sql        21 Feb 2006 10:31:07 -0000
***************
*** 16,22 ****
--- 16,29 ----
     element = int4
  );
  
+ -- Test creation and destruction of shell types
+ CREATE TYPE shell;
+ CREATE TYPE shell;   -- succeed, no error
+ DROP TYPE shell;
+ DROP TYPE shell;     -- fail, type not exist
+ 
  -- Test type-related default values (broken in releases before PG 7.2)
+ CREATE TYPE int42;
  
  -- Make dummy I/O routines using the existing internal support for int4, text
  CREATE FUNCTION int42_in(cstring)
***************
*** 74,79 ****
--- 81,89 ----
  COMMENT ON TYPE default_test_row IS 'good comment';
  COMMENT ON TYPE default_test_row IS NULL;
  
+ -- Check shell type create for existing types
+ CREATE TYPE text_w_default;
+ 
  DROP TYPE default_test_row CASCADE;
  
  DROP TABLE default_test;

Attachment: signature.asc
Description: Digital signature

Reply via email to