From 8e68d783902b0b47f377efa4a23a04ddeef272a8 Mon Sep 17 00:00:00 2001 From: Tom Lane <tgl@sss.pgh.pa.us> Date: Tue, 28 Feb 2006 22:37:27 +0000 Subject: [PATCH] Allow the syntax CREATE TYPE foo, with no parameters, to permit explicit creation of a shell type. This allows a less hacky way of dealing with the mutual dependency between a datatype and its I/O functions: make a shell type, then make the functions, then define the datatype fully. We should fix pg_dump to handle things this way, but this commit just deals with the backend. Martijn van Oosterhout, with some corrections by Tom Lane. --- doc/src/sgml/ref/create_type.sgml | 52 +++++++++++----- doc/src/sgml/xtypes.sgml | 21 ++++--- src/backend/catalog/pg_type.c | 74 +++++++++++++---------- src/backend/commands/typecmds.c | 49 +++++++++------ src/backend/parser/gram.y | 11 +++- src/backend/utils/adt/pseudotypes.c | 28 ++++++++- src/include/catalog/catversion.h | 4 +- src/include/catalog/pg_operator.h | 6 +- src/include/catalog/pg_proc.h | 8 ++- src/include/utils/builtins.h | 4 +- src/test/regress/expected/create_type.out | 28 +++++++-- src/test/regress/sql/create_type.sql | 21 +++++++ 12 files changed, 220 insertions(+), 86 deletions(-) diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml index a39c244c6c7..9382395182a 100644 --- a/doc/src/sgml/ref/create_type.sgml +++ b/doc/src/sgml/ref/create_type.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.60 2006/01/13 18:06:45 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.61 2006/02/28 22:37:25 tgl Exp $ PostgreSQL documentation --> @@ -37,6 +37,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> ( [ , ELEMENT = <replaceable class="parameter">element</replaceable> ] [ , DELIMITER = <replaceable class="parameter">delimiter</replaceable> ] ) + +CREATE TYPE <replaceable class="parameter">name</replaceable> </synopsis> </refsynopsisdiv> @@ -142,17 +144,16 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> ( <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. + 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 type should first be defined as a <firstterm>shell type</>, which is a + placeholder type that has no properties except a name and an owner. This + is done by issuing the command <literal>CREATE TYPE + <replaceable>name</></literal>, with no additional parameters. Then the + I/O functions can be defined referencing the shell type. Finally, + <command>CREATE TYPE</> with a full definition replaces the shell entry + with a complete, valid type definition, after which the new type can be + used normally. </para> <para> @@ -457,17 +458,33 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> ( while converting it to or from external form. </para> + <para> + Before <productname>PostgreSQL</productname> version 8.2, the syntax + <literal>CREATE TYPE <replaceable>name</></literal> did not exist. + The way to create a new base type was to create its input function first. + In this approach, <productname>PostgreSQL</productname> will first see + the name of the new data type as the return type of the input function. + The shell type is implicitly created in this situation, and then it + can be referenced in the definitions of the remaining I/O functions. + This approach still works, but is deprecated and may be disallowed in + some future release. Also, to avoid accidentally cluttering + the catalogs with shell types as a result of simple typos in function + definitions, a shell type will only be made this way when the input + function is written in C. + </para> + <para> In <productname>PostgreSQL</productname> versions before 7.3, it - was customary to avoid creating a shell type by replacing the + was customary to avoid creating a shell type at all, by replacing the functions' forward references to the type name with the placeholder pseudotype <type>opaque</>. The <type>cstring</> arguments and results also had to be declared as <type>opaque</> before 7.3. To support loading of old dump files, <command>CREATE TYPE</> will - accept functions declared using <type>opaque</>, but it will issue - a notice and change the function's declaration to use the correct + accept I/O functions declared using <type>opaque</>, but it will issue + a notice and change the function declarations to use the correct types. </para> + </refsect1> <refsect1> @@ -489,6 +506,11 @@ $$ LANGUAGE SQL; This example creates the base data type <type>box</type> and then uses the type in a table definition: <programlisting> +CREATE TYPE box; + +CREATE FUNCTION my_box_in_function(cstring) RETURNS box AS ... ; +CREATE FUNCTION my_box_out_function(box) RETURNS cstring AS ... ; + CREATE TYPE box ( INTERNALLENGTH = 16, INPUT = my_box_in_function, diff --git a/doc/src/sgml/xtypes.sgml b/doc/src/sgml/xtypes.sgml index 22d11a6300b..95723360e70 100644 --- a/doc/src/sgml/xtypes.sgml +++ b/doc/src/sgml/xtypes.sgml @@ -1,5 +1,5 @@ <!-- -$PostgreSQL: pgsql/doc/src/sgml/xtypes.sgml,v 1.25 2005/01/10 00:04:38 tgl Exp $ +$PostgreSQL: pgsql/doc/src/sgml/xtypes.sgml,v 1.26 2006/02/28 22:37:25 tgl Exp $ --> <sect1 id="xtypes"> @@ -168,8 +168,16 @@ complex_send(PG_FUNCTION_ARGS) </para> <para> - To define the <type>complex</type> type, we need to create the - user-defined I/O functions before creating the type: + Once we have written the I/O functions and compiled them into a shared + library, we can define the <type>complex</type> type in SQL. + First we declare it as a shell type: + +<programlisting> +CREATE TYPE complex; +</programlisting> + + This serves as a placeholder that allows us to reference the type while + defining its I/O functions. Now we can define the I/O functions: <programlisting> CREATE FUNCTION complex_in(cstring) @@ -192,15 +200,10 @@ CREATE FUNCTION complex_send(complex) AS '<replaceable>filename</replaceable>' 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: + Finally, we can provide the full definition of the data type: <programlisting> CREATE TYPE complex ( internallength = 16, diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index ab250b02ea9..f6fbbef005e 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.104 2005/10/15 02:49:14 momjian Exp $ + * $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.105 2006/02/28 22:37:25 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -20,8 +20,11 @@ #include "catalog/pg_namespace.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" +#include "commands/typecmds.h" #include "miscadmin.h" +#include "utils/acl.h" #include "utils/builtins.h" +#include "utils/fmgroids.h" #include "utils/lsyscache.h" #include "utils/syscache.h" @@ -29,14 +32,14 @@ /* ---------------------------------------------------------------- * TypeShellMake * - * This procedure inserts a "shell" tuple into the type - * relation. The type tuple inserted has invalid values - * and in particular, the "typisdefined" field is false. + * This procedure inserts a "shell" tuple into the pg_type relation. + * The type tuple inserted has valid but dummy values, and its + * "typisdefined" field is false indicating it's not really defined. * - * This is used so that a tuple exists in the catalogs. - * The invalid fields should be fixed up sometime after - * this routine is called, and then the "typeisdefined" - * field is set to true. -cim 6/15/90 + * This is used so that a tuple exists in the catalogs. The I/O + * functions for the type will link to this tuple. When the full + * CREATE TYPE command is issued, the bogus values will be replaced + * with correct ones, and "typisdefined" will be set to true. * ---------------------------------------------------------------- */ Oid @@ -70,30 +73,35 @@ TypeShellMake(const char *typeName, Oid typeNamespace) /* * initialize *values with the type name and dummy values + * + * 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 mistaken for a usable type. */ i = 0; namestrcpy(&name, typeName); 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 */ - values[i++] = CharGetDatum('i'); /* typalign */ - values[i++] = CharGetDatum('p'); /* typstorage */ - values[i++] = BoolGetDatum(false); /* typnotnull */ - values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */ - values[i++] = Int32GetDatum(-1); /* typtypmod */ - values[i++] = Int32GetDatum(0); /* typndims */ + values[i++] = Int16GetDatum(sizeof(int4)); /* typlen */ + values[i++] = BoolGetDatum(true); /* typbyval */ + values[i++] = CharGetDatum('p'); /* typtype */ + values[i++] = BoolGetDatum(false); /* typisdefined */ + values[i++] = CharGetDatum(DEFAULT_TYPDELIM); /* typdelim */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typrelid */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typelem */ + values[i++] = ObjectIdGetDatum(F_SHELL_IN); /* typinput */ + values[i++] = ObjectIdGetDatum(F_SHELL_OUT); /* typoutput */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */ + values[i++] = CharGetDatum('i'); /* typalign */ + values[i++] = CharGetDatum('p'); /* typstorage */ + values[i++] = BoolGetDatum(false); /* typnotnull */ + values[i++] = ObjectIdGetDatum(InvalidOid); /* typbasetype */ + values[i++] = Int32GetDatum(-1); /* typtypmod */ + values[i++] = Int32GetDatum(0); /* typndims */ nulls[i++] = 'n'; /* typdefaultbin */ nulls[i++] = 'n'; /* typdefault */ @@ -118,8 +126,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace) InvalidOid, 0, GetUserId(), - InvalidOid, - InvalidOid, + F_SHELL_IN, + F_SHELL_OUT, InvalidOid, InvalidOid, InvalidOid, @@ -289,7 +297,13 @@ TypeCreate(const char *typeName, errmsg("type \"%s\" already exists", typeName))); /* - * Okay to update existing "shell" type tuple + * shell type must have been created by same owner + */ + if (((Form_pg_type) GETSTRUCT(tup))->typowner != GetUserId()) + aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_TYPE, typeName); + + /* + * Okay to update existing shell type tuple */ tup = heap_modifytuple(tup, RelationGetDescr(pg_type_desc), @@ -350,8 +364,6 @@ TypeCreate(const char *typeName, * If rebuild is true, we remove existing dependencies and rebuild them * from scratch. This is needed for ALTER TYPE, and also when replacing * a shell type. - * - * NOTE: a shell type will have a dependency to its namespace, and no others. */ void GenerateTypeDependencies(Oid typeNamespace, diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c index 143695252f4..f8e1a2665cf 100644 --- a/src/backend/commands/typecmds.c +++ b/src/backend/commands/typecmds.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.86 2006/01/13 18:06:45 tgl Exp $ + * $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.87 2006/02/28 22:37:26 tgl Exp $ * * DESCRIPTION * The "DefineFoo" routines take the parse tree and pick out the @@ -138,6 +138,37 @@ DefineType(List *names, List *parameters) errmsg("type names must be %d characters or less", NAMEDATALEN - 2))); + /* + * 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. + */ + typoid = GetSysCacheOid(TYPENAMENSP, + CStringGetDatum(typeName), + ObjectIdGetDatum(typeNamespace), + 0, 0); + if (!OidIsValid(typoid)) + { + typoid = TypeShellMake(typeName, typeNamespace); + /* Make new shell type visible for modification below */ + CommandCounterIncrement(); + + /* + * If the command was a parameterless CREATE TYPE, we're done --- + * creating the shell type was all we're supposed to do. + */ + if (parameters == NIL) + return; + } + else + { + /* Complain if dummy CREATE TYPE and entry already exists */ + if (parameters == NIL) + ereport(ERROR, + (errcode(ERRCODE_DUPLICATE_OBJECT), + errmsg("type \"%s\" already exists", typeName))); + } + foreach(pl, parameters) { DefElem *defel = (DefElem *) lfirst(pl); @@ -240,22 +271,6 @@ DefineType(List *names, List *parameters) (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. - */ - typoid = GetSysCacheOid(TYPENAMENSP, - CStringGetDatum(typeName), - ObjectIdGetDatum(typeNamespace), - 0, 0); - if (!OidIsValid(typoid)) - { - typoid = TypeShellMake(typeName, typeNamespace); - /* Make new shell type visible for modification below */ - CommandCounterIncrement(); - } - /* * Convert I/O proc names to OIDs */ diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index 6cb6f96fa4f..a60d4157b2e 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -11,7 +11,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.530 2006/02/19 00:04:27 neilc Exp $ + * $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.531 2006/02/28 22:37:26 tgl Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -2690,6 +2690,15 @@ DefineStmt: 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 = NIL; + $$ = (Node *)n; + } | CREATE TYPE_P any_name AS '(' TableFuncElementList ')' { CompositeTypeStmt *n = makeNode(CompositeTypeStmt); diff --git a/src/backend/utils/adt/pseudotypes.c b/src/backend/utils/adt/pseudotypes.c index 105598afcce..98934328271 100644 --- a/src/backend/utils/adt/pseudotypes.c +++ b/src/backend/utils/adt/pseudotypes.c @@ -16,7 +16,7 @@ * * * IDENTIFICATION - * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.15 2004/12/31 22:01:22 pgsql Exp $ + * $PostgreSQL: pgsql/src/backend/utils/adt/pseudotypes.c,v 1.16 2006/02/28 22:37:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -321,3 +321,29 @@ anyelement_out(PG_FUNCTION_ARGS) PG_RETURN_VOID(); /* keep compiler quiet */ } + +/* + * shell_in - input routine for "shell" types (those not yet filled in). + */ +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" types. + */ +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 */ +} diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h index 1a391bad3f3..dbca22ea761 100644 --- a/src/include/catalog/catversion.h +++ b/src/include/catalog/catversion.h @@ -37,7 +37,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.316 2006/02/26 18:36:21 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.317 2006/02/28 22:37:26 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -53,6 +53,6 @@ */ /* yyyymmddN */ -#define CATALOG_VERSION_NO 200602251 +#define CATALOG_VERSION_NO 200602281 #endif diff --git a/src/include/catalog/pg_operator.h b/src/include/catalog/pg_operator.h index a746bff2711..84fb254f97b 100644 --- a/src/include/catalog/pg_operator.h +++ b/src/include/catalog/pg_operator.h @@ -8,7 +8,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.140 2006/02/26 18:36:21 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_operator.h,v 1.141 2006/02/28 22:37:26 tgl Exp $ * * NOTES * the genbki.sh script reads this file and generates .bki @@ -128,9 +128,9 @@ DATA(insert OID = 388 ( "!" PGNSP PGUID r f 20 0 1700 0 0 0 0 0 DATA(insert OID = 389 ( "!!" PGNSP PGUID l f 0 20 1700 0 0 0 0 0 0 numeric_fac - - )); DATA(insert OID = 385 ( "=" PGNSP PGUID b t 29 29 16 385 0 0 0 0 0 cideq eqsel eqjoinsel )); DATA(insert OID = 386 ( "=" PGNSP PGUID b t 22 22 16 386 0 0 0 0 0 int2vectoreq eqsel eqjoinsel )); -DATA(insert OID = 387 ( "=" PGNSP PGUID b f 27 27 16 387 0 0 0 0 0 tideq eqsel eqjoinsel )); +DATA(insert OID = 387 ( "=" PGNSP PGUID b f 27 27 16 387 402 0 0 0 0 tideq eqsel eqjoinsel )); #define TIDEqualOperator 387 -DATA(insert OID = 402 ( "<>" PGNSP PGUID b f 27 27 16 402 0 0 0 0 0 tidne neqsel neqjoinsel )); +DATA(insert OID = 402 ( "<>" PGNSP PGUID b f 27 27 16 402 387 0 0 0 0 tidne neqsel neqjoinsel )); DATA(insert OID = 410 ( "=" PGNSP PGUID b t 20 20 16 410 411 412 412 412 413 int8eq eqsel eqjoinsel )); DATA(insert OID = 411 ( "<>" PGNSP PGUID b f 20 20 16 411 410 0 0 0 0 int8ne neqsel neqjoinsel )); diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h index ec43235eef3..4a9da19366e 100644 --- a/src/include/catalog/pg_proc.h +++ b/src/include/catalog/pg_proc.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.398 2006/02/26 18:36:21 neilc Exp $ + * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.399 2006/02/28 22:37:26 tgl Exp $ * * NOTES * The script catalog/genbki.sh reads this file and generates .bki @@ -1598,7 +1598,7 @@ DATA(insert OID = 1293 ( currtid PGNSP PGUID 12 f f t f v 2 27 "26 27" _null DESCR("latest tid of a tuple"); DATA(insert OID = 1294 ( currtid2 PGNSP PGUID 12 f f t f v 2 27 "25 27" _null_ _null_ _null_ currtid_byrelname - _null_ )); DESCR("latest tid of a tuple"); -DATA(insert OID = 2398 ( tidne PGNSP PGUID 12 f f t f i 2 16 "27 27" _null_ _null_ _null_ tidne - _null_ )); +DATA(insert OID = 1265 ( tidne PGNSP PGUID 12 f f t f i 2 16 "27 27" _null_ _null_ _null_ tidne - _null_ )); DESCR("not equal"); DATA(insert OID = 2168 ( pg_database_size PGNSP PGUID 12 f f t f v 1 20 "19" _null_ _null_ _null_ pg_database_size_name - _null_ )); @@ -3321,6 +3321,10 @@ DATA(insert OID = 2312 ( anyelement_in PGNSP PGUID 12 f f t f i 1 2283 "2275" 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_ )); diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h index 28a70d11ce7..50f349abdfd 100644 --- a/src/include/utils/builtins.h +++ b/src/include/utils/builtins.h @@ -7,7 +7,7 @@ * Portions Copyright (c) 1996-2005, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * - * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.274 2006/02/26 18:36:22 neilc Exp $ + * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.275 2006/02/28 22:37:27 tgl Exp $ * *------------------------------------------------------------------------- */ @@ -443,6 +443,8 @@ extern Datum opaque_in(PG_FUNCTION_ARGS); extern Datum opaque_out(PG_FUNCTION_ARGS); extern Datum anyelement_in(PG_FUNCTION_ARGS); extern Datum anyelement_out(PG_FUNCTION_ARGS); +extern Datum shell_in(PG_FUNCTION_ARGS); +extern Datum shell_out(PG_FUNCTION_ARGS); /* regexp.c */ extern Datum nameregexeq(PG_FUNCTION_ARGS); diff --git a/src/test/regress/expected/create_type.out b/src/test/regress/expected/create_type.out index 4e2d44d5c41..3e2edeb1e0b 100644 --- a/src/test/regress/expected/create_type.out +++ b/src/test/regress/expected/create_type.out @@ -1,6 +1,11 @@ -- -- CREATE_TYPE -- +-- +-- Note: widget_in/out were created in create_function_1, without any +-- prior shell-type creation. These commands therefore complete a test +-- of the "old style" approach of making the functions first. +-- CREATE TYPE widget ( internallength = 24, input = widget_in, @@ -13,14 +18,27 @@ CREATE TYPE city_budget ( output = int44out, element = int4 ); +-- Test creation and destruction of shell types +CREATE TYPE shell; +CREATE TYPE shell; -- fail, type already present +ERROR: type "shell" already exists +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) +-- +-- This part of the test also exercises the "new style" approach of making +-- a shell type and then filling it in. +-- +CREATE TYPE int42; +CREATE TYPE text_w_default; -- Make dummy I/O routines using the existing internal support for int4, text CREATE FUNCTION int42_in(cstring) RETURNS int42 AS 'int4in' LANGUAGE internal STRICT; -NOTICE: type "int42" is not yet defined -DETAIL: Creating a shell type definition. +NOTICE: return type int42 is only a shell CREATE FUNCTION int42_out(int42) RETURNS cstring AS 'int4out' @@ -30,8 +48,7 @@ CREATE FUNCTION text_w_default_in(cstring) RETURNS text_w_default AS 'textin' LANGUAGE internal STRICT; -NOTICE: type "text_w_default" is not yet defined -DETAIL: Creating a shell type definition. +NOTICE: return type text_w_default is only a shell CREATE FUNCTION text_w_default_out(text_w_default) RETURNS cstring AS 'textout' @@ -76,6 +93,9 @@ COMMENT ON TYPE bad IS 'bad comment'; 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; -- should fail +ERROR: type "text_w_default" already exists DROP TYPE default_test_row CASCADE; NOTICE: drop cascades to function get_default_test() DROP TABLE default_test; diff --git a/src/test/regress/sql/create_type.sql b/src/test/regress/sql/create_type.sql index 66d78c9216b..097d51fc925 100644 --- a/src/test/regress/sql/create_type.sql +++ b/src/test/regress/sql/create_type.sql @@ -2,6 +2,11 @@ -- CREATE_TYPE -- +-- +-- Note: widget_in/out were created in create_function_1, without any +-- prior shell-type creation. These commands therefore complete a test +-- of the "old style" approach of making the functions first. +-- CREATE TYPE widget ( internallength = 24, input = widget_in, @@ -16,7 +21,20 @@ CREATE TYPE city_budget ( element = int4 ); +-- Test creation and destruction of shell types +CREATE TYPE shell; +CREATE TYPE shell; -- fail, type already present +DROP TYPE shell; +DROP TYPE shell; -- fail, type not exist + +-- -- Test type-related default values (broken in releases before PG 7.2) +-- +-- This part of the test also exercises the "new style" approach of making +-- a shell type and then filling it in. +-- +CREATE TYPE int42; +CREATE TYPE text_w_default; -- Make dummy I/O routines using the existing internal support for int4, text CREATE FUNCTION int42_in(cstring) @@ -74,6 +92,9 @@ COMMENT ON TYPE bad IS 'bad comment'; 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; -- should fail + DROP TYPE default_test_row CASCADE; DROP TABLE default_test; -- GitLab