[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_ ));
signature.asc
Description: Digital signature