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 **** ! To define the complex type, we need to create the ! user-defined I/O functions before creating the type: CREATE FUNCTION complex_in(cstring) --- 168,180 ---- ! To define the complex type, we first declare it as a shell type: ! ! ! CREATE TYPE complex; ! ! ! Then we create the user-defined I/O functions needed to create the type: CREATE FUNCTION complex_in(cstring) *************** *** 193,206 **** LANGUAGE C IMMUTABLE STRICT; - 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. ! Finally, we can declare the data type: CREATE TYPE complex ( internallength = 16, --- 198,207 ---- LANGUAGE C IMMUTABLE STRICT; ! Finally, we can declare the data type properly: 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 name AS ( attribute_name data_type [, ... ] ) ! CREATE TYPE name ( INPUT = input_function, OUTPUT = output_function [ , RECEIVE = receive_function ] --- 23,29 ---- CREATE TYPE name AS ( attribute_name data_type [, ... ] ) ! CREATE TYPE name [ ( INPUT = input_function, OUTPUT = output_function [ , RECEIVE = receive_function ] *************** *** 36,42 **** [ , DEFAULT = default ] [ , ELEMENT = element ] [ , DELIMITER = delimiter ] ! ) --- 36,42 ---- [ , DEFAULT = default ] [ , ELEMENT = element ] [ , DELIMITER = delimiter ] ! ) ] *************** *** 142,158 **** 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. ! PostgreSQL will first see the name of the new ! data type as the return type of the input function. It will create a ! 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, CREATE TYPE replaces the ! shell entry with a complete type definition, and the new type can be used. --- 142,157 ---- 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 CREATE TYPE foo, it will create a ! 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, 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. *************** *** 468,473 **** --- 467,485 ---- a notice and change the function's declaration to use the correct types. + + + 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. + PostgreSQL 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 C or internal. + + + 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;