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