[Please CC any replies, thanks]

Persuant to the recent discussion, here is a patch to allow users to
create shell types by using the symtax:

CREATE TYPE foo;

It was actually much easier than I thought given that normal type
creation creates a shell type just before creating the real type. This
means it works just by stopping after creating the shell type before
creating the real type. This also means that issuing a "CREATE TYPE
foo;" on an already existing type succeeds without doing anything, just
like structure declarations in C.

Unfortuntly there are some minor issues not quite considered when this
was first brought up. Primarily this:

postgres=# create type text;
CREATE TYPE
postgres=# select * from pg_type where typname = 'text';
 typname | typnamespace | typowner | typlen | typbyval | typtype | typisdefined 
| typdelim | typrelid | typelem | typinput | typoutput | typreceive | typsend  
| typanalyze | typalign | typstorage | typnotnull | typbasetype | typtypmod | 
typndims | typdefaultbin | typdefault 
---------+--------------+----------+--------+----------+---------+--------------+----------+----------+---------+----------+-----------+------------+----------+------------+----------+------------+------------+-------------+-----------+----------+---------------+------------
 text    |           11 |       10 |     -1 | f        | b       | t            
| ,        |        0 |       0 | textin   | textout   | textrecv   | textsend 
| -          | i        | x          | f          |           0 |        -1 |   
     0 |               | 
 text    |         2200 |       10 |      4 | t        | p       | f            
| ,        |        0 |       0 | shell_in | shell_out | -          | -        
| -          | i        | p          | f          |           0 |        -1 |   
     0 |               | 
(2 rows)

postgres=# drop type text;
ERROR:  cannot drop type text because it is required by the database system
postgres=# drop type public.text;
DROP TYPE

The first line creates public.text, but the drop tries to delete
pg_catalog.text. I'm not sure which we should make smarter, the create
or the drop, or whether just the error messages need to be made much
clearer as to what's going on.

Other points:
- Changed the shell create function to create a type with the same
parameters as a pseudotype. This should address Tom's issue with code
not paying attention to the fact the type is not complete yet.

- Created two functions shell_in and shell_out persuant to making shell
types look like pseudo types. I however didn't actually create a
pseudotype "shell" so shell_in actually returns "opaque". Do people
have a problem with this?

- I still think it would be useful to require people to create the
shell type and the complete type within the same transaction, if only
to prevent people filling up catalog with useless entries. Shell types
can be dropped as normal, but still...

- Includes documentation updates. Does not include regression tests,
yet.

Comments?

http://svana.org/kleptog/pgsql/shell.diff
-- 
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    20 Feb 2006 11:50:06 -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   20 Feb 2006 11:50:07 -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       20 Feb 2006 11:50:07 -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     20 Feb 2006 11:50:08 -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   20 Feb 2006 11:50:14 -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 20 Feb 2006 11:50:14 -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       20 Feb 2006 11:50:20 -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_ ));

Attachment: signature.asc
Description: Digital signature

Reply via email to