From 5725b9d9afc8c3ba24e94cbc7020889fe8ad7ef9 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Sat, 30 Dec 2006 21:21:56 +0000
Subject: [PATCH] Support type modifiers for user-defined types, and pull most
 knowledge about typmod representation for standard types out into
 type-specific typmod I/O functions.  Teodor Sigaev, with some
 editorialization by Tom Lane.

---
 doc/src/sgml/catalogs.sgml                |  16 +-
 doc/src/sgml/keywords.sgml                |  55 +--
 doc/src/sgml/ref/create_type.sgml         |  64 +++-
 src/backend/access/common/tupdesc.c       |   9 +-
 src/backend/catalog/heap.c                |   4 +-
 src/backend/catalog/pg_type.c             |  30 +-
 src/backend/commands/tablecmds.c          |  44 ++-
 src/backend/commands/typecmds.c           | 104 ++++-
 src/backend/nodes/copyfuncs.c             |   5 +-
 src/backend/nodes/equalfuncs.c            |   5 +-
 src/backend/nodes/makefuncs.c             |  16 +-
 src/backend/nodes/outfuncs.c              |   5 +-
 src/backend/parser/gram.y                 | 447 +++++++---------------
 src/backend/parser/parse_expr.c           |  29 +-
 src/backend/parser/parse_relation.c       |   6 +-
 src/backend/parser/parse_type.c           |  77 +++-
 src/backend/utils/adt/arrayutils.c        |  30 +-
 src/backend/utils/adt/date.c              |  89 ++++-
 src/backend/utils/adt/format_type.c       | 145 +++----
 src/backend/utils/adt/numeric.c           |  65 +++-
 src/backend/utils/adt/timestamp.c         | 244 +++++++++++-
 src/backend/utils/adt/varbit.c            |  87 ++++-
 src/backend/utils/adt/varchar.c           |  89 ++++-
 src/backend/utils/cache/lsyscache.c       |  56 ++-
 src/backend/utils/misc/guc.c              |   8 +-
 src/bin/pg_dump/pg_dump.c                 |  48 ++-
 src/include/catalog/catversion.h          |   4 +-
 src/include/catalog/pg_attribute.h        |  42 +-
 src/include/catalog/pg_class.h            |   4 +-
 src/include/catalog/pg_proc.h             |  42 +-
 src/include/catalog/pg_type.h             | 266 +++++++------
 src/include/nodes/parsenodes.h            |   7 +-
 src/include/parser/parse_type.h           |   4 +-
 src/include/utils/array.h                 |   3 +-
 src/include/utils/builtins.h              |  10 +-
 src/include/utils/date.h                  |   6 +-
 src/include/utils/lsyscache.h             |   3 +-
 src/include/utils/timestamp.h             |   8 +-
 src/include/utils/varbit.h                |   6 +-
 src/test/regress/expected/create_type.out |  14 +
 src/test/regress/expected/horology.out    |   4 +
 src/test/regress/expected/oidjoins.out    |  16 +
 src/test/regress/expected/type_sanity.out |  42 ++
 src/test/regress/sql/create_type.sql      |  11 +
 src/test/regress/sql/oidjoins.sql         |   8 +
 src/test/regress/sql/type_sanity.sql      |  34 ++
 src/tools/findoidjoins/README             |   2 +
 47 files changed, 1628 insertions(+), 685 deletions(-)

diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index a906623ac9a..2d42280f50e 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.139 2006/12/23 00:43:08 tgl Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.140 2006/12/30 21:21:52 tgl Exp $ -->
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
  -->
@@ -4393,6 +4393,20 @@
       <entry>Output conversion function (binary format), or 0 if none</entry>
      </row>
 
+     <row>
+      <entry><structfield>typmodin</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Type modifier input function, or 0 if type does not support modifiers</entry>
+     </row>
+
+     <row>
+      <entry><structfield>typmodout</structfield></entry>
+      <entry><type>regproc</type></entry>
+      <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
+      <entry>Type modifier output function, or 0 to use the standard format</entry>
+     </row>
+
      <row>
       <entry><structfield>typanalyze</structfield></entry>
       <entry><type>regproc</type></entry>
diff --git a/doc/src/sgml/keywords.sgml b/doc/src/sgml/keywords.sgml
index c82b3b6f1b9..b9565b3283c 100644
--- a/doc/src/sgml/keywords.sgml
+++ b/doc/src/sgml/keywords.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/keywords.sgml,v 2.18 2006/10/08 20:51:52 petere Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/keywords.sgml,v 2.19 2006/12/30 21:21:52 tgl Exp $ -->
 
 <appendix id="sql-keywords-appendix">
  <title><acronym>SQL</acronym> Key Words</title>
@@ -45,16 +45,17 @@
   In <xref linkend="keywords-table"> in the column for
   <productname>PostgreSQL</productname> we classify as
   <quote>non-reserved</quote> those key words that are explicitly
-  known to the parser but are allowed in most or all contexts where an
-  identifier is expected.  Some key words that are otherwise
+  known to the parser but are allowed as column or table names.
+  Some key words that are otherwise
   non-reserved cannot be used as function or data type names and are
   marked accordingly.  (Most of these words represent built-in
   functions or data types with special syntax.  The function or type
   is still available but it cannot be redefined by the user.)  Labeled
-  <quote>reserved</quote> are those tokens that are only allowed as
-  <quote>AS</quote> column label names (and perhaps in very few other
-  contexts).  Some reserved key words are allowable as names for
-  functions; this is also shown in the table.
+  <quote>reserved</quote> are those tokens that are not allowed as
+  column or table names.  Some reserved key words are
+  allowable as names for functions or data types; this is also shown in the
+  table.  If not so marked, a reserved key word is only allowed as an
+  <quote>AS</quote> column label name.
  </para>
 
  <para>
@@ -326,7 +327,7 @@
    </row>
    <row>
     <entry><token>AUTHORIZATION</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -368,7 +369,7 @@
    </row>
    <row>
     <entry><token>BETWEEN</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>non-reserved</entry>
     <entry>reserved</entry>
@@ -382,7 +383,7 @@
    </row>
    <row>
     <entry><token>BINARY</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry></entry>
@@ -956,7 +957,7 @@
    </row>
    <row>
     <entry><token>CROSS</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -1642,7 +1643,7 @@
    </row>
    <row>
     <entry><token>FREEZE</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry></entry>
     <entry></entry>
     <entry></entry>
@@ -1656,7 +1657,7 @@
    </row>
    <row>
     <entry><token>FULL</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -1831,7 +1832,7 @@
    </row>
    <row>
     <entry><token>ILIKE</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry></entry>
     <entry></entry>
     <entry></entry>
@@ -1943,7 +1944,7 @@
    </row>
    <row>
     <entry><token>INNER</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -2048,14 +2049,14 @@
    </row>
    <row>
     <entry><token>IS</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
    </row>
    <row>
     <entry><token>ISNULL</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry></entry>
     <entry></entry>
     <entry></entry>
@@ -2076,7 +2077,7 @@
    </row>
    <row>
     <entry><token>JOIN</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -2160,7 +2161,7 @@
    </row>
    <row>
     <entry><token>LEFT</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -2188,7 +2189,7 @@
    </row>
    <row>
     <entry><token>LIKE</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -2475,7 +2476,7 @@
    </row>
    <row>
     <entry><token>NATURAL</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -2608,7 +2609,7 @@
    </row>
    <row>
     <entry><token>NOTNULL</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry></entry>
     <entry></entry>
     <entry></entry>
@@ -2811,7 +2812,7 @@
    </row>
    <row>
     <entry><token>OUTER</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -2832,7 +2833,7 @@
    </row>
    <row>
     <entry><token>OVERLAPS</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>non-reserved</entry>
     <entry>reserved</entry>
@@ -3385,7 +3386,7 @@
    </row>
    <row>
     <entry><token>RIGHT</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
     <entry>reserved</entry>
@@ -3658,7 +3659,7 @@
    </row>
    <row>
     <entry><token>SIMILAR</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry>reserved</entry>
     <entry>non-reserved</entry>
     <entry></entry>
@@ -4414,7 +4415,7 @@
    </row>
    <row>
     <entry><token>VERBOSE</token></entry>
-    <entry>reserved (can be function)</entry>
+    <entry>reserved (can be function or type)</entry>
     <entry></entry>
     <entry></entry>
     <entry></entry>
diff --git a/doc/src/sgml/ref/create_type.sgml b/doc/src/sgml/ref/create_type.sgml
index e85c94dbe74..2f1f6eab3c4 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.65 2006/12/23 01:28:09 momjian Exp $
+$PostgreSQL: pgsql/doc/src/sgml/ref/create_type.sgml,v 1.66 2006/12/30 21:21:52 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -28,6 +28,8 @@ CREATE TYPE <replaceable class="parameter">name</replaceable> (
     OUTPUT = <replaceable class="parameter">output_function</replaceable>
     [ , RECEIVE = <replaceable class="parameter">receive_function</replaceable> ]
     [ , SEND = <replaceable class="parameter">send_function</replaceable> ]
+    [ , TYPMOD_IN = <replaceable class="parameter">type_modifier_input_function</replaceable> ]
+    [ , TYPMOD_OUT = <replaceable class="parameter">type_modifier_output_function</replaceable> ]
     [ , ANALYZE = <replaceable class="parameter">analyze_function</replaceable> ]
     [ , INTERNALLENGTH = { <replaceable class="parameter">internallength</replaceable> | VARIABLE } ]
     [ , PASSEDBYVALUE ]
@@ -83,12 +85,14 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    (scalar type).  The parameters may appear in any order, not only that
    illustrated above, and most are optional.  You must register
    two or more functions (using <command>CREATE FUNCTION</command>) before
-   defining the type.  The support functions 
+   defining the type.  The support functions
    <replaceable class="parameter">input_function</replaceable> and
    <replaceable class="parameter">output_function</replaceable>
    are required, while the functions
    <replaceable class="parameter">receive_function</replaceable>,
-   <replaceable class="parameter">send_function</replaceable> and
+   <replaceable class="parameter">send_function</replaceable>,
+   <replaceable class="parameter">type_modifier_input_function</replaceable>,
+   <replaceable class="parameter">type_modifier_output_function</replaceable> and
    <replaceable class="parameter">analyze_function</replaceable>
    are optional.  Generally these functions have to be coded in C
    or another low-level language.
@@ -169,6 +173,34 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    used normally.
   </para>
 
+  <para>
+   The optional
+   <replaceable class="parameter">type_modifier_input_function</replaceable>
+   and <replaceable class="parameter">type_modifier_output_function</replaceable>
+   are needed if the type supports modifiers, that is optional constraints
+   attached to a type declaration, such as <literal>char(5)</> or
+   <literal>numeric(30,2)</>.  <productname>PostgreSQL</productname> allows
+   user-defined types to take one or more integer constants as modifiers;
+   however, this information must be capable of being packed into a single
+   non-negative integer value for storage in the system catalogs.  The
+   <replaceable class="parameter">type_modifier_input_function</replaceable>
+   is passed the declared modifier(s) in the form of an <type>integer</>
+   array.  It must check the values for validity (throwing an error if they
+   are wrong), and if they are correct, return a single non-negative
+   <type>integer</> value that will be stored as the column <quote>typmod</>.
+   Type modifiers will be rejected if the type does not have a
+   <replaceable class="parameter">type_modifier_input_function</replaceable>.
+   The <replaceable class="parameter">type_modifier_output_function</replaceable>
+   converts the internal integer typmod value back to the correct form for
+   user display.  It must return a <type>cstring</> value that is the exact
+   string to append to the type name; for example <type>numeric</>'s
+   function might return <literal>(30,2)</>.
+   It is allowed to omit the
+   <replaceable class="parameter">type_modifier_output_function</replaceable>,
+   in which case the default display format is just the stored typmod value
+   enclosed in parentheses.
+  </para>
+
   <para>
    The optional <replaceable class="parameter">analyze_function</replaceable>
    performs type-specific statistics collection for columns of the data type.
@@ -265,7 +297,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    <title>Array Types</title>
 
    <para>
-    Whenever a user-defined base data type is created, 
+    Whenever a user-defined base data type is created,
     <productname>PostgreSQL</productname> automatically creates an
     associated array type, whose name consists of the base type's
     name prepended with an underscore.  The parser understands this
@@ -298,7 +330,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
    </para>
   </refsect2>
  </refsect1>
-  
+
  <refsect1>
   <title>Parameters</title>
 
@@ -371,6 +403,26 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
     </listitem>
    </varlistentry>
 
+   <varlistentry>
+    <term><replaceable class="parameter">type_modifier_input_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of a function that converts numeric modifier(s) for the type
+      into internal form.
+     </para>
+    </listitem>
+   </varlistentry>
+
+   <varlistentry>
+    <term><replaceable class="parameter">type_modifier_output_function</replaceable></term>
+    <listitem>
+     <para>
+      The name of a function that converts the internal form of the type's
+      modifier(s) to external textual form.
+     </para>
+    </listitem>
+   </varlistentry>
+
    <varlistentry>
     <term><replaceable class="parameter">analyze_function</replaceable></term>
     <listitem>
@@ -499,7 +551,7 @@ CREATE TYPE <replaceable class="parameter">name</replaceable>
   </para>
 
  </refsect1>
- 
+
  <refsect1>
   <title>Examples</title>
 
diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c
index 6242afa20f3..be5665db79c 100644
--- a/src/backend/access/common/tupdesc.c
+++ b/src/backend/access/common/tupdesc.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.118 2006/07/14 14:52:16 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/access/common/tupdesc.c,v 1.119 2006/12/30 21:21:52 tgl Exp $
  *
  * NOTES
  *	  some of the executor utility code such as "ExecTypeFromTL" should be
@@ -508,6 +508,7 @@ BuildDescForRelation(List *schema)
 	AttrDefault *attrdef = NULL;
 	TupleConstr *constr = (TupleConstr *) palloc0(sizeof(TupleConstr));
 	char	   *attname;
+	Oid			atttypid;
 	int32		atttypmod;
 	int			attdim;
 	int			ndef = 0;
@@ -533,7 +534,8 @@ BuildDescForRelation(List *schema)
 		attnum++;
 
 		attname = entry->colname;
-		atttypmod = entry->typename->typmod;
+		atttypid = typenameTypeId(NULL, entry->typename);
+		atttypmod = typenameTypeMod(NULL, entry->typename, atttypid);
 		attdim = list_length(entry->typename->arrayBounds);
 
 		if (entry->typename->setof)
@@ -543,8 +545,7 @@ BuildDescForRelation(List *schema)
 							attname)));
 
 		TupleDescInitEntry(desc, attnum, attname,
-						   typenameTypeId(NULL, entry->typename),
-						   atttypmod, attdim);
+						   atttypid, atttypmod, attdim);
 
 		/* Fill in additional stuff not handled by TupleDescInitEntry */
 		if (entry->is_not_null)
diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c
index d6822c73c69..41ef41c7c17 100644
--- a/src/backend/catalog/heap.c
+++ b/src/backend/catalog/heap.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.314 2006/11/05 22:42:08 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/heap.c,v 1.315 2006/12/30 21:21:52 tgl Exp $
  *
  *
  * INTERFACE ROUTINES
@@ -724,6 +724,8 @@ AddNewRelationType(const char *typeName,
 				   F_RECORD_OUT,	/* output procedure */
 				   F_RECORD_RECV,		/* receive procedure */
 				   F_RECORD_SEND,		/* send procedure */
+				   InvalidOid,	/* typmodin procedure - none */
+				   InvalidOid,	/* typmodout procedure - none */
 				   InvalidOid,	/* analyze procedure - default */
 				   InvalidOid,	/* array element type - irrelevant */
 				   InvalidOid,	/* domain base type - irrelevant */
diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c
index 32de0b90dc1..74517124fbd 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.108 2006/10/04 00:29:50 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/pg_type.c,v 1.109 2006/12/30 21:21:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -94,6 +94,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
 	values[i++] = ObjectIdGetDatum(F_SHELL_OUT);		/* typoutput */
 	values[i++] = ObjectIdGetDatum(InvalidOid); /* typreceive */
 	values[i++] = ObjectIdGetDatum(InvalidOid); /* typsend */
+	values[i++] = ObjectIdGetDatum(InvalidOid); /* typmodin */
+	values[i++] = ObjectIdGetDatum(InvalidOid); /* typmodout */
 	values[i++] = ObjectIdGetDatum(InvalidOid); /* typanalyze */
 	values[i++] = CharGetDatum('i');	/* typalign */
 	values[i++] = CharGetDatum('p');	/* typstorage */
@@ -132,6 +134,8 @@ TypeShellMake(const char *typeName, Oid typeNamespace)
 								 InvalidOid,
 								 InvalidOid,
 								 InvalidOid,
+								 InvalidOid,
+								 InvalidOid,
 								 NULL,
 								 false);
 
@@ -164,6 +168,8 @@ TypeCreate(const char *typeName,
 		   Oid outputProcedure,
 		   Oid receiveProcedure,
 		   Oid sendProcedure,
+		   Oid typmodinProcedure,
+		   Oid typmodoutProcedure,
 		   Oid analyzeProcedure,
 		   Oid elementType,
 		   Oid baseType,
@@ -243,6 +249,8 @@ TypeCreate(const char *typeName,
 	values[i++] = ObjectIdGetDatum(outputProcedure);	/* typoutput */
 	values[i++] = ObjectIdGetDatum(receiveProcedure);	/* typreceive */
 	values[i++] = ObjectIdGetDatum(sendProcedure);		/* typsend */
+	values[i++] = ObjectIdGetDatum(typmodinProcedure);	/* typmodin */
+	values[i++] = ObjectIdGetDatum(typmodoutProcedure);	/* typmodout */
 	values[i++] = ObjectIdGetDatum(analyzeProcedure);	/* typanalyze */
 	values[i++] = CharGetDatum(alignment);		/* typalign */
 	values[i++] = CharGetDatum(storage);		/* typstorage */
@@ -341,6 +349,8 @@ TypeCreate(const char *typeName,
 								 outputProcedure,
 								 receiveProcedure,
 								 sendProcedure,
+								 typmodinProcedure,
+								 typmodoutProcedure,
 								 analyzeProcedure,
 								 elementType,
 								 baseType,
@@ -374,6 +384,8 @@ GenerateTypeDependencies(Oid typeNamespace,
 						 Oid outputProcedure,
 						 Oid receiveProcedure,
 						 Oid sendProcedure,
+						 Oid typmodinProcedure,
+						 Oid typmodoutProcedure,
 						 Oid analyzeProcedure,
 						 Oid elementType,
 						 Oid baseType,
@@ -436,6 +448,22 @@ GenerateTypeDependencies(Oid typeNamespace,
 		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
 	}
 
+	if (OidIsValid(typmodinProcedure))
+	{
+		referenced.classId = ProcedureRelationId;
+		referenced.objectId = typmodinProcedure;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
+	if (OidIsValid(typmodoutProcedure))
+	{
+		referenced.classId = ProcedureRelationId;
+		referenced.objectId = typmodoutProcedure;
+		referenced.objectSubId = 0;
+		recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
+	}
+
 	if (OidIsValid(analyzeProcedure))
 	{
 		referenced.classId = ProcedureRelationId;
diff --git a/src/backend/commands/tablecmds.c b/src/backend/commands/tablecmds.c
index 2a1116f7580..c30aa69c555 100644
--- a/src/backend/commands/tablecmds.c
+++ b/src/backend/commands/tablecmds.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.207 2006/12/23 00:43:09 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/tablecmds.c,v 1.208 2006/12/30 21:21:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -889,6 +889,9 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 			exist_attno = findAttrByName(attributeName, inhSchema);
 			if (exist_attno > 0)
 			{
+				Oid		defTypeId;
+				int32	deftypmod;
+
 				/*
 				 * Yes, try to merge the two column definitions. They must
 				 * have the same type and typmod.
@@ -897,8 +900,10 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 						(errmsg("merging multiple inherited definitions of column \"%s\"",
 								attributeName)));
 				def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
-				if (typenameTypeId(NULL, def->typename) != attribute->atttypid ||
-					def->typename->typmod != attribute->atttypmod)
+				defTypeId = typenameTypeId(NULL, def->typename);
+				deftypmod = typenameTypeMod(NULL, def->typename, defTypeId);
+				if (defTypeId != attribute->atttypid ||
+					deftypmod != attribute->atttypmod)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATATYPE_MISMATCH),
 						errmsg("inherited column \"%s\" has a type conflict",
@@ -1029,6 +1034,8 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 			if (exist_attno > 0)
 			{
 				ColumnDef  *def;
+				Oid	defTypeId, newTypeId;
+				int32 deftypmod, newtypmod;
 
 				/*
 				 * Yes, try to merge the two column definitions. They must
@@ -1038,8 +1045,11 @@ MergeAttributes(List *schema, List *supers, bool istemp,
 				   (errmsg("merging column \"%s\" with inherited definition",
 						   attributeName)));
 				def = (ColumnDef *) list_nth(inhSchema, exist_attno - 1);
-				if (typenameTypeId(NULL, def->typename) != typenameTypeId(NULL, newdef->typename) ||
-					def->typename->typmod != newdef->typename->typmod)
+				defTypeId = typenameTypeId(NULL, def->typename);
+				deftypmod = typenameTypeMod(NULL, def->typename, defTypeId);
+				newTypeId = typenameTypeId(NULL, newdef->typename);
+				newtypmod = typenameTypeMod(NULL, newdef->typename, newTypeId);
+				if (defTypeId != newTypeId || deftypmod != newtypmod)
 					ereport(ERROR,
 							(errcode(ERRCODE_DATATYPE_MISMATCH),
 							 errmsg("column \"%s\" has a type conflict",
@@ -3092,6 +3102,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 				maxatts;
 	HeapTuple	typeTuple;
 	Oid			typeOid;
+	int32		typmod;
 	Form_pg_type tform;
 	Expr	   *defval;
 
@@ -3110,10 +3121,14 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 		if (HeapTupleIsValid(tuple))
 		{
 			Form_pg_attribute childatt = (Form_pg_attribute) GETSTRUCT(tuple);
+			Oid		ctypeId;
+			int32 	ctypmod;
 
 			/* Okay if child matches by type */
-			if (typenameTypeId(NULL, colDef->typename) != childatt->atttypid ||
-				colDef->typename->typmod != childatt->atttypmod)
+			ctypeId = typenameTypeId(NULL, colDef->typename);
+			ctypmod = typenameTypeMod(NULL, colDef->typename, ctypeId);
+			if (ctypeId != childatt->atttypid ||
+				ctypmod != childatt->atttypmod)
 				ereport(ERROR,
 						(errcode(ERRCODE_DATATYPE_MISMATCH),
 						 errmsg("child table \"%s\" has different type for column \"%s\"",
@@ -3169,6 +3184,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	typeTuple = typenameType(NULL, colDef->typename);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	typeOid = HeapTupleGetOid(typeTuple);
+	typmod = typenameTypeMod(NULL, colDef->typename, typeOid);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colDef->colname, typeOid);
@@ -3186,7 +3202,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 	attribute->attstattarget = -1;
 	attribute->attlen = tform->typlen;
 	attribute->attcacheoff = -1;
-	attribute->atttypmod = colDef->typename->typmod;
+	attribute->atttypmod = typmod;
 	attribute->attnum = i;
 	attribute->attbyval = tform->typbyval;
 	attribute->attndims = list_length(colDef->typename->arrayBounds);
@@ -3278,7 +3294,7 @@ ATExecAddColumn(AlteredTableInfo *tab, Relation rel,
 												(Node *) defval,
 												basetype,
 												typeOid,
-												colDef->typename->typmod,
+												typmod,
 												COERCION_ASSIGNMENT,
 												COERCE_IMPLICIT_CAST);
 		if (defval == NULL)		/* should not happen */
@@ -4877,6 +4893,7 @@ ATPrepAlterColumnType(List **wqueue,
 	Form_pg_attribute attTup;
 	AttrNumber	attnum;
 	Oid			targettype;
+	int32		targettypmod;
 	Node	   *transform;
 	NewColumnValue *newval;
 	ParseState *pstate = make_parsestate(NULL);
@@ -4907,6 +4924,7 @@ ATPrepAlterColumnType(List **wqueue,
 
 	/* Look up the target type */
 	targettype = typenameTypeId(NULL, typename);
+	targettypmod = typenameTypeMod(NULL, typename, targettype);
 
 	/* make sure datatype is legal for a column */
 	CheckAttributeType(colName, targettype);
@@ -4958,7 +4976,7 @@ ATPrepAlterColumnType(List **wqueue,
 
 	transform = coerce_to_target_type(pstate,
 									  transform, exprType(transform),
-									  targettype, typename->typmod,
+									  targettype, targettypmod,
 									  COERCION_ASSIGNMENT,
 									  COERCE_IMPLICIT_CAST);
 	if (transform == NULL)
@@ -5004,6 +5022,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	HeapTuple	typeTuple;
 	Form_pg_type tform;
 	Oid			targettype;
+	int32		targettypmod;
 	Node	   *defaultexpr;
 	Relation	attrelation;
 	Relation	depRel;
@@ -5035,6 +5054,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	typeTuple = typenameType(NULL, typename);
 	tform = (Form_pg_type) GETSTRUCT(typeTuple);
 	targettype = HeapTupleGetOid(typeTuple);
+	targettypmod = typenameTypeMod(NULL, typename, targettype);
 
 	/*
 	 * If there is a default expression for the column, get it and ensure we
@@ -5055,7 +5075,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 		defaultexpr = strip_implicit_coercions(defaultexpr);
 		defaultexpr = coerce_to_target_type(NULL,		/* no UNKNOWN params */
 										  defaultexpr, exprType(defaultexpr),
-											targettype, typename->typmod,
+											targettype, targettypmod,
 											COERCION_ASSIGNMENT,
 											COERCE_IMPLICIT_CAST);
 		if (defaultexpr == NULL)
@@ -5272,7 +5292,7 @@ ATExecAlterColumnType(AlteredTableInfo *tab, Relation rel,
 	 * copy of the syscache entry, so okay to scribble on.)
 	 */
 	attTup->atttypid = targettype;
-	attTup->atttypmod = typename->typmod;
+	attTup->atttypmod = targettypmod;
 	attTup->attndims = list_length(typename->arrayBounds);
 	attTup->attlen = tform->typlen;
 	attTup->attbyval = tform->typbyval;
diff --git a/src/backend/commands/typecmds.c b/src/backend/commands/typecmds.c
index 53cca73a9dd..2b26f2dfa14 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.97 2006/10/04 00:29:51 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/typecmds.c,v 1.98 2006/12/30 21:21:53 tgl Exp $
  *
  * DESCRIPTION
  *	  The "DefineFoo" routines take the parse tree and pick out the
@@ -75,6 +75,8 @@ static Oid	findTypeInputFunction(List *procname, Oid typeOid);
 static Oid	findTypeOutputFunction(List *procname, Oid typeOid);
 static Oid	findTypeReceiveFunction(List *procname, Oid typeOid);
 static Oid	findTypeSendFunction(List *procname, Oid typeOid);
+static Oid	findTypeTypmodinFunction(List *procname);
+static Oid	findTypeTypmodoutFunction(List *procname);
 static Oid	findTypeAnalyzeFunction(List *procname, Oid typeOid);
 static List *get_rels_with_domain(Oid domainOid, LOCKMODE lockmode);
 static void checkDomainOwner(HeapTuple tup, TypeName *typename);
@@ -100,6 +102,8 @@ DefineType(List *names, List *parameters)
 	List	   *outputName = NIL;
 	List	   *receiveName = NIL;
 	List	   *sendName = NIL;
+	List	   *typmodinName = NIL;
+	List	   *typmodoutName = NIL;
 	List	   *analyzeName = NIL;
 	char	   *defaultValue = NULL;
 	bool		byValue = false;
@@ -110,6 +114,8 @@ DefineType(List *names, List *parameters)
 	Oid			outputOid;
 	Oid			receiveOid = InvalidOid;
 	Oid			sendOid = InvalidOid;
+	Oid			typmodinOid = InvalidOid;
+	Oid			typmodoutOid = InvalidOid;
 	Oid			analyzeOid = InvalidOid;
 	char	   *shadow_type;
 	ListCell   *pl;
@@ -182,6 +188,10 @@ DefineType(List *names, List *parameters)
 			receiveName = defGetQualifiedName(defel);
 		else if (pg_strcasecmp(defel->defname, "send") == 0)
 			sendName = defGetQualifiedName(defel);
+		else if (pg_strcasecmp(defel->defname, "typmod_in") == 0)
+			typmodinName = defGetQualifiedName(defel);
+		else if (pg_strcasecmp(defel->defname, "typmod_out") == 0)
+			typmodoutName = defGetQualifiedName(defel);
 		else if (pg_strcasecmp(defel->defname, "analyze") == 0 ||
 				 pg_strcasecmp(defel->defname, "analyse") == 0)
 			analyzeName = defGetQualifiedName(defel);
@@ -268,6 +278,11 @@ DefineType(List *names, List *parameters)
 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 				 errmsg("type output function must be specified")));
 
+	if (typmodinName == NIL && typmodoutName != NIL)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("type modifier output function is useless without a type modifier input function")));
+
 	/*
 	 * Convert I/O proc names to OIDs
 	 */
@@ -335,6 +350,14 @@ DefineType(List *names, List *parameters)
 						  NameListToString(sendName))));
 	}
 
+	/*
+	 * Convert typmodin/out function proc names to OIDs.
+	 */
+	if (typmodinName)
+		typmodinOid = findTypeTypmodinFunction(typmodinName);
+	if (typmodoutName)
+		typmodoutOid = findTypeTypmodoutFunction(typmodoutName);
+
 	/*
 	 * Convert analysis function proc name to an OID. If no analysis function
 	 * is specified, we'll use zero to select the built-in default algorithm.
@@ -362,6 +385,12 @@ DefineType(List *names, List *parameters)
 	if (sendOid && !pg_proc_ownercheck(sendOid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
 					   NameListToString(sendName));
+	if (typmodinOid && !pg_proc_ownercheck(typmodinOid, GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+					   NameListToString(typmodinName));
+	if (typmodoutOid && !pg_proc_ownercheck(typmodoutOid, GetUserId()))
+		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
+					   NameListToString(typmodoutName));
 	if (analyzeOid && !pg_proc_ownercheck(analyzeOid, GetUserId()))
 		aclcheck_error(ACLCHECK_NOT_OWNER, ACL_KIND_PROC,
 					   NameListToString(analyzeName));
@@ -381,6 +410,8 @@ DefineType(List *names, List *parameters)
 				   outputOid,	/* output procedure */
 				   receiveOid,	/* receive procedure */
 				   sendOid,		/* send procedure */
+				   typmodinOid, /* typmodin procedure */
+				   typmodoutOid,/* typmodout procedure */
 				   analyzeOid,	/* analyze procedure */
 				   elemType,	/* element type ID */
 				   InvalidOid,	/* base type ID (only for domains) */
@@ -413,6 +444,8 @@ DefineType(List *names, List *parameters)
 			   F_ARRAY_OUT,		/* output procedure */
 			   F_ARRAY_RECV,	/* receive procedure */
 			   F_ARRAY_SEND,	/* send procedure */
+			   typmodinOid,		/* typmodin procedure */
+			   typmodoutOid,	/* typmodout procedure */
 			   InvalidOid,		/* analyze procedure - default */
 			   typoid,			/* element type ID */
 			   InvalidOid,		/* base type ID */
@@ -552,6 +585,7 @@ DefineDomain(CreateDomainStmt *stmt)
 	Oid			basetypeoid;
 	Oid			domainoid;
 	Form_pg_type baseType;
+	int32		basetypeMod;
 
 	/* Convert list of names to a name and namespace */
 	domainNamespace = QualifiedNameGetCreationNamespace(stmt->domainname,
@@ -581,9 +615,9 @@ DefineDomain(CreateDomainStmt *stmt)
 	 * Look up the base type.
 	 */
 	typeTup = typenameType(NULL, stmt->typename);
-
 	baseType = (Form_pg_type) GETSTRUCT(typeTup);
 	basetypeoid = HeapTupleGetOid(typeTup);
+	basetypeMod = typenameTypeMod(NULL, stmt->typename, basetypeoid);
 
 	/*
 	 * Base type must be a plain base type or another domain.  Domains over
@@ -621,6 +655,8 @@ DefineDomain(CreateDomainStmt *stmt)
 	receiveProcedure = F_DOMAIN_RECV;
 	sendProcedure = baseType->typsend;
 
+	/* Domains never accept typmods, so no typmodin/typmodout needed */
+
 	/* Analysis function */
 	analyzeProcedure = baseType->typanalyze;
 
@@ -681,7 +717,7 @@ DefineDomain(CreateDomainStmt *stmt)
 				 */
 				defaultExpr = cookDefault(pstate, constr->raw_expr,
 										  basetypeoid,
-										  stmt->typename->typmod,
+										  basetypeMod,
 										  domainName);
 
 				/*
@@ -768,6 +804,8 @@ DefineDomain(CreateDomainStmt *stmt)
 				   outputProcedure,		/* output procedure */
 				   receiveProcedure,	/* receive procedure */
 				   sendProcedure,		/* send procedure */
+				   InvalidOid,			/* typmodin procedure - none */
+				   InvalidOid,			/* typmodout procedure - none */
 				   analyzeProcedure,	/* analyze procedure */
 				   typelem,		/* element type ID */
 				   basetypeoid, /* base type ID */
@@ -776,7 +814,7 @@ DefineDomain(CreateDomainStmt *stmt)
 				   byValue,		/* passed by value */
 				   alignment,	/* required alignment */
 				   storage,		/* TOAST strategy */
-				   stmt->typename->typmod,		/* typeMod value */
+				   basetypeMod, /* typeMod value */
 				   typNDims,	/* Array dimensions for base type */
 				   typNotNull); /* Type NOT NULL */
 
@@ -793,7 +831,7 @@ DefineDomain(CreateDomainStmt *stmt)
 		{
 			case CONSTR_CHECK:
 				domainAddConstraint(domainoid, domainNamespace,
-									basetypeoid, stmt->typename->typmod,
+									basetypeoid, basetypeMod,
 									constr, domainName);
 				break;
 
@@ -1067,6 +1105,60 @@ findTypeSendFunction(List *procname, Oid typeOid)
 	return InvalidOid;			/* keep compiler quiet */
 }
 
+static Oid
+findTypeTypmodinFunction(List *procname)
+{
+	Oid			argList[1];
+	Oid			procOid;
+
+	/*
+	 * typmodin functions always take one int4[] argument and return int4.
+	 */
+	argList[0] = INT4ARRAYOID;
+
+	procOid = LookupFuncName(procname, 1, argList, true);
+	if (!OidIsValid(procOid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("function %s does not exist",
+						func_signature_string(procname, 1, argList))));
+
+	if (get_func_rettype(procOid) != INT4OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("typmod_in function %s must return type \"integer\"",
+						NameListToString(procname))));
+
+	return procOid;
+}
+
+static Oid
+findTypeTypmodoutFunction(List *procname)
+{
+	Oid			argList[1];
+	Oid			procOid;
+
+	/*
+	 * typmodout functions always take one int4 argument and return cstring.
+	 */
+	argList[0] = INT4OID;
+
+	procOid = LookupFuncName(procname, 1, argList, true);
+	if (!OidIsValid(procOid))
+		ereport(ERROR,
+				(errcode(ERRCODE_UNDEFINED_FUNCTION),
+				 errmsg("function %s does not exist",
+						func_signature_string(procname, 1, argList))));
+
+	if (get_func_rettype(procOid) != CSTRINGOID)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+				 errmsg("typmod_out function %s must return type \"cstring\"",
+						NameListToString(procname))));
+
+	return procOid;
+}
+
 static Oid
 findTypeAnalyzeFunction(List *procname, Oid typeOid)
 {
@@ -1244,6 +1336,8 @@ AlterDomainDefault(List *names, Node *defaultRaw)
 							 typTup->typoutput,
 							 typTup->typreceive,
 							 typTup->typsend,
+							 typTup->typmodin,
+							 typTup->typmodout,
 							 typTup->typanalyze,
 							 typTup->typelem,
 							 typTup->typbasetype,
diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c
index d46ed57d830..c8c9b907c9c 100644
--- a/src/backend/nodes/copyfuncs.c
+++ b/src/backend/nodes/copyfuncs.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.357 2006/12/24 00:29:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/copyfuncs.c,v 1.358 2006/12/30 21:21:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1584,7 +1584,8 @@ _copyTypeName(TypeName *from)
 	COPY_SCALAR_FIELD(timezone);
 	COPY_SCALAR_FIELD(setof);
 	COPY_SCALAR_FIELD(pct_type);
-	COPY_SCALAR_FIELD(typmod);
+	COPY_NODE_FIELD(typmods);
+	COPY_SCALAR_FIELD(typemod);
 	COPY_NODE_FIELD(arrayBounds);
 	COPY_SCALAR_FIELD(location);
 
diff --git a/src/backend/nodes/equalfuncs.c b/src/backend/nodes/equalfuncs.c
index 29bff448c7f..57e61f0e2a2 100644
--- a/src/backend/nodes/equalfuncs.c
+++ b/src/backend/nodes/equalfuncs.c
@@ -18,7 +18,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.291 2006/12/24 00:29:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/equalfuncs.c,v 1.292 2006/12/30 21:21:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1614,7 +1614,8 @@ _equalTypeName(TypeName *a, TypeName *b)
 	COMPARE_SCALAR_FIELD(timezone);
 	COMPARE_SCALAR_FIELD(setof);
 	COMPARE_SCALAR_FIELD(pct_type);
-	COMPARE_SCALAR_FIELD(typmod);
+	COMPARE_NODE_FIELD(typmods);
+	COMPARE_SCALAR_FIELD(typemod);
 	COMPARE_NODE_FIELD(arrayBounds);
 	COMPARE_SCALAR_FIELD(location);
 
diff --git a/src/backend/nodes/makefuncs.c b/src/backend/nodes/makefuncs.c
index 9f6aa22707b..277103e4a18 100644
--- a/src/backend/nodes/makefuncs.c
+++ b/src/backend/nodes/makefuncs.c
@@ -9,7 +9,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.52 2006/10/04 00:29:53 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/makefuncs.c,v 1.53 2006/12/30 21:21:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -262,12 +262,7 @@ makeRangeVar(char *schemaname, char *relname)
 TypeName *
 makeTypeName(char *typnam)
 {
-	TypeName   *n = makeNode(TypeName);
-
-	n->names = list_make1(makeString(typnam));
-	n->typmod = -1;
-	n->location = -1;
-	return n;
+	return makeTypeNameFromNameList(list_make1(makeString(typnam)));
 }
 
 /*
@@ -282,14 +277,15 @@ makeTypeNameFromNameList(List *names)
 	TypeName   *n = makeNode(TypeName);
 
 	n->names = names;
-	n->typmod = -1;
+	n->typmods = NIL;
+	n->typemod = -1;
 	n->location = -1;
 	return n;
 }
 
 /*
  * makeTypeNameFromOid -
- *	build a TypeName node to represent a type already known by OID.
+ *	build a TypeName node to represent a type already known by OID/typmod.
  */
 TypeName *
 makeTypeNameFromOid(Oid typeid, int32 typmod)
@@ -297,7 +293,7 @@ makeTypeNameFromOid(Oid typeid, int32 typmod)
 	TypeName   *n = makeNode(TypeName);
 
 	n->typeid = typeid;
-	n->typmod = typmod;
+	n->typemod = typmod;
 	n->location = -1;
 	return n;
 }
diff --git a/src/backend/nodes/outfuncs.c b/src/backend/nodes/outfuncs.c
index 4911d6ed404..2b21eae6a10 100644
--- a/src/backend/nodes/outfuncs.c
+++ b/src/backend/nodes/outfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.289 2006/12/24 00:29:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/nodes/outfuncs.c,v 1.290 2006/12/30 21:21:53 tgl Exp $
  *
  * NOTES
  *	  Every node type that can appear in stored rules' parsetrees *must*
@@ -1476,7 +1476,8 @@ _outTypeName(StringInfo str, TypeName *node)
 	WRITE_BOOL_FIELD(timezone);
 	WRITE_BOOL_FIELD(setof);
 	WRITE_BOOL_FIELD(pct_type);
-	WRITE_INT_FIELD(typmod);
+	WRITE_NODE_FIELD(typmods);
+	WRITE_INT_FIELD(typemod);
 	WRITE_NODE_FIELD(arrayBounds);
 	WRITE_INT_FIELD(location);
 }
diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y
index a1511870f28..c4820190f07 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.570 2006/12/24 00:29:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/gram.y,v 2.571 2006/12/30 21:21:53 tgl Exp $
  *
  * HISTORY
  *	  AUTHOR			DATE			MAJOR EVENT
@@ -206,7 +206,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 
 %type <str>		relation_name copy_file_name
 				database_name access_method_clause access_method attr_name
-				index_name name function_name file_name
+				index_name name file_name
 
 %type <list>	func_name handler_name qual_Op qual_all_Op subquery_Op
 				opt_class opt_validator
@@ -242,7 +242,7 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 				group_clause TriggerFuncArgs select_limit
 				opt_select_limit opclass_item_list
 				transaction_mode_list_or_empty
-				TableFuncElementList
+				TableFuncElementList opt_type_modifiers
 				prep_type_clause prep_type_list
 				execute_param_clause using_clause returning_clause
 
@@ -319,20 +319,19 @@ static Node *makeXmlExpr(XmlExprOp op, char *name, List *named_args, List *args)
 %type <str>		character
 %type <str>		extract_arg
 %type <str>		opt_charset
-%type <ival>	opt_numeric opt_decimal
 %type <boolean> opt_varying opt_timezone
 
 %type <ival>	Iconst SignedIconst
 %type <str>		Sconst comment_text
 %type <str>		RoleId opt_granted_by opt_boolean ColId_or_Sconst
 %type <list>	var_list var_list_or_default
-%type <str>		ColId ColLabel var_name type_name param_name
+%type <str>		ColId ColLabel var_name type_function_name param_name
 %type <node>	var_value zone_value
 
-%type <keyword> unreserved_keyword func_name_keyword
+%type <keyword> unreserved_keyword type_func_name_keyword
 %type <keyword> col_name_keyword reserved_keyword
 
-%type <node>	TableConstraint TableLikeClause 
+%type <node>	TableConstraint TableLikeClause
 %type <list>	TableLikeOptionList
 %type <ival>	TableLikeOption
 %type <list>	ColQualList
@@ -1180,35 +1179,20 @@ zone_value:
 							ereport(ERROR,
 									(errcode(ERRCODE_SYNTAX_ERROR),
 									 errmsg("time zone interval must be HOUR or HOUR TO MINUTE")));
-						n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3);
+						n->typename->typmods = list_make1(makeIntConst($3));
 					}
 					$$ = (Node *)n;
 				}
 			| ConstInterval '(' Iconst ')' Sconst opt_interval
 				{
 					A_Const *n = (A_Const *) makeStringConst($5, $1);
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("INTERVAL(%d) precision must not be negative",
-										$3)));
-					if ($3 > MAX_INTERVAL_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
-										$3, MAX_INTERVAL_PRECISION)));
-						$3 = MAX_INTERVAL_PRECISION;
-					}
-
 					if (($6 != INTERVAL_FULL_RANGE)
 						&& (($6 & ~(INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE))) != 0))
 						ereport(ERROR,
 								(errcode(ERRCODE_SYNTAX_ERROR),
 								 errmsg("time zone interval must be HOUR or HOUR TO MINUTE")));
-
-					n->typename->typmod = INTERVAL_TYPMOD($3, $6);
-
+					n->typename->typmods = list_make2(makeIntConst($6),
+													  makeIntConst($3));
 					$$ = (Node *)n;
 				}
 			| NumericOnly							{ $$ = makeAConst($1); }
@@ -2823,7 +2807,7 @@ DefineStmt:
 					n->definition = $4;
 					$$ = (Node *)n;
 				}
-			| CREATE TYPE_P any_name 
+			| CREATE TYPE_P any_name
 				{
 					/* Shell type (identified by lack of definition) */
 					DefineStmt *n = makeNode(DefineStmt);
@@ -2889,7 +2873,6 @@ def_elem:  ColLabel '=' def_arg
 
 /* Note: any simple identifier will be returned as a type name! */
 def_arg:	func_type						{ $$ = (Node *)$1; }
-			| func_name_keyword				{ $$ = (Node *)makeString(pstrdup($1)); }
 			| reserved_keyword				{ $$ = (Node *)makeString(pstrdup($1)); }
 			| qual_all_Op					{ $$ = (Node *)$1; }
 			| NumericOnly					{ $$ = (Node *)$1; }
@@ -3047,7 +3030,7 @@ ReassignOwnedStmt:
  *
  *		QUERY:
  *
- *		DROP itemtype [ IF EXISTS ] itemname [, itemname ...] 
+ *		DROP itemtype [ IF EXISTS ] itemname [, itemname ...]
  *           [ RESTRICT | CASCADE ]
  *
  *****************************************************************************/
@@ -3872,7 +3855,7 @@ arg_class:	IN_P									{ $$ = FUNC_PARAM_IN; }
 /*
  * Ideally param_name should be ColId, but that causes too many conflicts.
  */
-param_name:	function_name
+param_name:	type_function_name
 		;
 
 func_return:
@@ -3888,23 +3871,20 @@ func_return:
 
 /*
  * We would like to make the %TYPE productions here be ColId attrs etc,
- * but that causes reduce/reduce conflicts.  type_name is next best choice.
+ * but that causes reduce/reduce conflicts.  type_function_name
+ * is next best choice.
  */
 func_type:	Typename								{ $$ = $1; }
-			| type_name attrs '%' TYPE_P
+			| type_function_name attrs '%' TYPE_P
 				{
-					$$ = makeNode(TypeName);
-					$$->names = lcons(makeString($1), $2);
+					$$ = makeTypeNameFromNameList(lcons(makeString($1), $2));
 					$$->pct_type = true;
-					$$->typmod = -1;
 					$$->location = @1;
 				}
-			| SETOF type_name attrs '%' TYPE_P
+			| SETOF type_function_name attrs '%' TYPE_P
 				{
-					$$ = makeNode(TypeName);
-					$$->names = lcons(makeString($2), $3);
+					$$ = makeTypeNameFromNameList(lcons(makeString($2), $3));
 					$$->pct_type = true;
-					$$->typmod = -1;
 					$$->setof = TRUE;
 					$$->location = @2;
 				}
@@ -5552,7 +5532,7 @@ multiple_set_clause:
 
 						res_col->val = res_val;
 					}
-				    
+
 					$$ = $2;
 				}
 		;
@@ -6363,14 +6343,6 @@ opt_array_bounds:
 					{  $$ = NIL; }
 		;
 
-/*
- * XXX ideally, the production for a qualified typename should be ColId attrs
- * (there's no obvious reason why the first name should need to be restricted)
- * and should be an alternative of GenericType (so that it can be used to
- * specify a type for a literal in AExprConst).  However doing either causes
- * reduce/reduce conflicts that I haven't been able to find a workaround
- * for.  FIXME later.
- */
 SimpleTypename:
 			GenericType								{ $$ = $1; }
 			| Numeric								{ $$ = $1; }
@@ -6381,32 +6353,13 @@ SimpleTypename:
 				{
 					$$ = $1;
 					if ($2 != INTERVAL_FULL_RANGE)
-						$$->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $2);
+						$$->typmods = list_make1(makeIntConst($2));
 				}
 			| ConstInterval '(' Iconst ')' opt_interval
 				{
 					$$ = $1;
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("INTERVAL(%d) precision must not be negative",
-										$3)));
-					if ($3 > MAX_INTERVAL_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
-										$3, MAX_INTERVAL_PRECISION)));
-						$3 = MAX_INTERVAL_PRECISION;
-					}
-					$$->typmod = INTERVAL_TYPMOD($3, $5);
-				}
-			| type_name attrs
-				{
-					$$ = makeNode(TypeName);
-					$$->names = lcons(makeString($1), $2);
-					$$->typmod = -1;
-					$$->location = @1;
+					$$->typmods = list_make2(makeIntConst($5),
+											 makeIntConst($3));
 				}
 		;
 
@@ -6417,80 +6370,112 @@ SimpleTypename:
  * where there is an obvious better choice to make.
  * Note that ConstInterval is not included here since it must
  * be pushed up higher in the rules to accomodate the postfix
- * options (e.g. INTERVAL '1' YEAR).
+ * options (e.g. INTERVAL '1' YEAR). Likewise, we have to handle
+ * the generic-type-name case in AExprConst to avoid premature
+ * reduce/reduce conflicts against function names.
  */
 ConstTypename:
-			GenericType								{ $$ = $1; }
-			| Numeric								{ $$ = $1; }
+			Numeric									{ $$ = $1; }
 			| ConstBit								{ $$ = $1; }
 			| ConstCharacter						{ $$ = $1; }
 			| ConstDatetime							{ $$ = $1; }
 		;
 
+/*
+ * GenericType covers all type names that don't have special syntax mandated
+ * by the standard, including qualified names.  We also allow type modifiers.
+ * To avoid parsing conflicts against function invocations, the modifiers
+ * have to be shown as expr_list here, but parse analysis will only accept
+ * integer constants for them.
+ */
 GenericType:
-			type_name
+			type_function_name opt_type_modifiers
 				{
 					$$ = makeTypeName($1);
+					$$->typmods = $2;
+					$$->location = @1;
+				}
+			| type_function_name attrs opt_type_modifiers
+				{
+					$$ = makeTypeNameFromNameList(lcons(makeString($1), $2));
+					$$->typmods = $3;
 					$$->location = @1;
 				}
 		;
 
-/* SQL92 numeric data types
- * Check FLOAT() precision limits assuming IEEE floating types.
- * - thomas 1997-09-18
- * Provide real DECIMAL() and NUMERIC() implementations now - Jan 1998-12-30
+opt_type_modifiers: '(' expr_list ')'				{ $$ = $2; }
+					| /* EMPTY */					{ $$ = NIL; }
+		;
+
+/*
+ * SQL92 numeric data types
  */
 Numeric:	INT_P
 				{
 					$$ = SystemTypeName("int4");
+					$$->location = @1;
 				}
 			| INTEGER
 				{
 					$$ = SystemTypeName("int4");
+					$$->location = @1;
 				}
 			| SMALLINT
 				{
 					$$ = SystemTypeName("int2");
+					$$->location = @1;
 				}
 			| BIGINT
 				{
 					$$ = SystemTypeName("int8");
+					$$->location = @1;
 				}
 			| REAL
 				{
 					$$ = SystemTypeName("float4");
+					$$->location = @1;
 				}
 			| FLOAT_P opt_float
 				{
 					$$ = $2;
+					$$->location = @1;
 				}
 			| DOUBLE_P PRECISION
 				{
 					$$ = SystemTypeName("float8");
+					$$->location = @1;
 				}
-			| DECIMAL_P opt_decimal
+			| DECIMAL_P opt_type_modifiers
 				{
 					$$ = SystemTypeName("numeric");
-					$$->typmod = $2;
+					$$->typmods = $2;
+					$$->location = @1;
 				}
-			| DEC opt_decimal
+			| DEC opt_type_modifiers
 				{
 					$$ = SystemTypeName("numeric");
-					$$->typmod = $2;
+					$$->typmods = $2;
+					$$->location = @1;
 				}
-			| NUMERIC opt_numeric
+			| NUMERIC opt_type_modifiers
 				{
 					$$ = SystemTypeName("numeric");
-					$$->typmod = $2;
+					$$->typmods = $2;
+					$$->location = @1;
 				}
 			| BOOLEAN_P
 				{
 					$$ = SystemTypeName("bool");
+					$$->location = @1;
 				}
 		;
 
 opt_float:	'(' Iconst ')'
 				{
+					/*
+					 * Check FLOAT() precision limits assuming IEEE floating
+					 * types - thomas 1997-09-18
+					 */
 					if ($2 < 1)
 						ereport(ERROR,
 								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -6510,73 +6495,6 @@ opt_float:	'(' Iconst ')'
 				}
 		;
 
-opt_numeric:
-			'(' Iconst ',' Iconst ')'
-				{
-					if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("NUMERIC precision %d must be between 1 and %d",
-										$2, NUMERIC_MAX_PRECISION)));
-					if ($4 < 0 || $4 > $2)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("NUMERIC scale %d must be between 0 and precision %d",
-										$4, $2)));
-
-					$$ = (($2 << 16) | $4) + VARHDRSZ;
-				}
-			| '(' Iconst ')'
-				{
-					if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("NUMERIC precision %d must be between 1 and %d",
-										$2, NUMERIC_MAX_PRECISION)));
-
-					$$ = ($2 << 16) + VARHDRSZ;
-				}
-			| /*EMPTY*/
-				{
-					/* Insert "-1" meaning "no limit" */
-					$$ = -1;
-				}
-		;
-
-opt_decimal:
-			'(' Iconst ',' Iconst ')'
-				{
-					if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("DECIMAL precision %d must be between 1 and %d",
-										$2, NUMERIC_MAX_PRECISION)));
-					if ($4 < 0 || $4 > $2)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("DECIMAL scale %d must be between 0 and precision %d",
-										$4, $2)));
-
-					$$ = (($2 << 16) | $4) + VARHDRSZ;
-				}
-			| '(' Iconst ')'
-				{
-					if ($2 < 1 || $2 > NUMERIC_MAX_PRECISION)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("DECIMAL precision %d must be between 1 and %d",
-										$2, NUMERIC_MAX_PRECISION)));
-
-					$$ = ($2 << 16) + VARHDRSZ;
-				}
-			| /*EMPTY*/
-				{
-					/* Insert "-1" meaning "no limit" */
-					$$ = -1;
-				}
-		;
-
-
 /*
  * SQL92 bit-field data types
  * The following implements BIT() and BIT VARYING().
@@ -6600,28 +6518,19 @@ ConstBit:	BitWithLength
 			| BitWithoutLength
 				{
 					$$ = $1;
-					$$->typmod = -1;
+					$$->typmods = NIL;
 				}
 		;
 
 BitWithLength:
-			BIT opt_varying '(' Iconst ')'
+			BIT opt_varying '(' expr_list ')'
 				{
 					char *typname;
 
 					typname = $2 ? "varbit" : "bit";
 					$$ = SystemTypeName(typname);
-					if ($4 < 1)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("length for type %s must be at least 1",
-										typname)));
-					else if ($4 > (MaxAttrSize * BITS_PER_BYTE))
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("length for type %s cannot exceed %d",
-										typname, MaxAttrSize * BITS_PER_BYTE)));
-					$$->typmod = $4;
+					$$->typmods = $4;
+					$$->location = @1;
 				}
 		;
 
@@ -6632,13 +6541,13 @@ BitWithoutLength:
 					if ($2)
 					{
 						$$ = SystemTypeName("varbit");
-						$$->typmod = -1;
 					}
 					else
 					{
 						$$ = SystemTypeName("bit");
-						$$->typmod = 1;
+						$$->typmods = list_make1(makeIntConst(1));
 					}
+					$$->location = @1;
 				}
 		;
 
@@ -6670,7 +6579,7 @@ ConstCharacter:  CharacterWithLength
 					 * was not specified.
 					 */
 					$$ = $1;
-					$$->typmod = -1;
+					$$->typmods = NIL;
 				}
 		;
 
@@ -6688,24 +6597,8 @@ CharacterWithLength:  character '(' Iconst ')' opt_charset
 					}
 
 					$$ = SystemTypeName($1);
-
-					if ($3 < 1)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("length for type %s must be at least 1",
-										$1)));
-					else if ($3 > MaxAttrSize)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("length for type %s cannot exceed %d",
-										$1, MaxAttrSize)));
-
-					/* we actually implement these like a varlen, so
-					 * the first 4 bytes is the length. (the difference
-					 * between these and "text" is that we blank-pad and
-					 * truncate where necessary)
-					 */
-					$$->typmod = VARHDRSZ + $3;
+					$$->typmods = list_make1(makeIntConst($3));
+					$$->location = @1;
 				}
 		;
 
@@ -6726,9 +6619,9 @@ CharacterWithoutLength:	 character opt_charset
 
 					/* char defaults to char(1), varchar to no limit */
 					if (strcmp($1, "bpchar") == 0)
-						$$->typmod = VARHDRSZ + 1;
-					else
-						$$->typmod = -1;
+						$$->typmods = list_make1(makeIntConst(1));
+
+					$$->location = @1;
 				}
 		;
 
@@ -6756,6 +6649,9 @@ opt_charset:
 			| /*EMPTY*/								{ $$ = NULL; }
 		;
 
+/*
+ * SQL92 date/time types
+ */
 ConstDatetime:
 			TIMESTAMP '(' Iconst ')' opt_timezone
 				{
@@ -6767,21 +6663,8 @@ ConstDatetime:
 					 * - thomas 2001-09-06
 					 */
 					$$->timezone = $5;
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("TIMESTAMP(%d)%s precision must not be negative",
-										$3, ($5 ? " WITH TIME ZONE": ""))));
-					if ($3 > MAX_TIMESTAMP_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d",
-										$3, ($5 ? " WITH TIME ZONE": ""),
-										MAX_TIMESTAMP_PRECISION)));
-						$3 = MAX_TIMESTAMP_PRECISION;
-					}
-					$$->typmod = $3;
+					$$->typmods = list_make1(makeIntConst($3));
+					$$->location = @1;
 				}
 			| TIMESTAMP opt_timezone
 				{
@@ -6793,6 +6676,7 @@ ConstDatetime:
 					 * - thomas 2001-09-06
 					 */
 					$$->timezone = $2;
+					$$->location = @1;
 				}
 			| TIME '(' Iconst ')' opt_timezone
 				{
@@ -6800,21 +6684,8 @@ ConstDatetime:
 						$$ = SystemTypeName("timetz");
 					else
 						$$ = SystemTypeName("time");
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("TIME(%d)%s precision must not be negative",
-										$3, ($5 ? " WITH TIME ZONE": ""))));
-					if ($3 > MAX_TIME_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
-										$3, ($5 ? " WITH TIME ZONE": ""),
-										MAX_TIME_PRECISION)));
-						$3 = MAX_TIME_PRECISION;
-					}
-					$$->typmod = $3;
+					$$->typmods = list_make1(makeIntConst($3));
+					$$->location = @1;
 				}
 			| TIME opt_timezone
 				{
@@ -6822,11 +6693,16 @@ ConstDatetime:
 						$$ = SystemTypeName("timetz");
 					else
 						$$ = SystemTypeName("time");
+					$$->location = @1;
 				}
 		;
 
 ConstInterval:
-			INTERVAL								{ $$ = SystemTypeName("interval"); }
+			INTERVAL
+				{
+					$$ = SystemTypeName("interval");
+					$$->location = @1;
+				}
 		;
 
 opt_timezone:
@@ -7520,20 +7396,7 @@ func_expr:	func_name '(' ')'
 					s->val.val.str = "now";
 					s->typename = SystemTypeName("text");
 					d = SystemTypeName("timetz");
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("CURRENT_TIME(%d) precision must not be negative",
-										$3)));
-					if ($3 > MAX_TIME_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("CURRENT_TIME(%d) precision reduced to maximum allowed, %d",
-										$3, MAX_TIME_PRECISION)));
-						$3 = MAX_TIME_PRECISION;
-					}
-					d->typmod = $3;
+					d->typmods = list_make1(makeIntConst($3));
 
 					$$ = (Node *)makeTypeCast((Node *)s, d);
 				}
@@ -7565,20 +7428,7 @@ func_expr:	func_name '(' ')'
 					s->typename = SystemTypeName("text");
 
 					d = SystemTypeName("timestamptz");
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("CURRENT_TIMESTAMP(%d) precision must not be negative",
-										$3)));
-					if ($3 > MAX_TIMESTAMP_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("CURRENT_TIMESTAMP(%d) precision reduced to maximum allowed, %d",
-										$3, MAX_TIMESTAMP_PRECISION)));
-						$3 = MAX_TIMESTAMP_PRECISION;
-					}
-					d->typmod = $3;
+					d->typmods = list_make1(makeIntConst($3));
 
 					$$ = (Node *)makeTypeCast((Node *)s, d);
 				}
@@ -7612,20 +7462,7 @@ func_expr:	func_name '(' ')'
 					s->val.val.str = "now";
 					s->typename = SystemTypeName("text");
 					d = SystemTypeName("time");
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("LOCALTIME(%d) precision must not be negative",
-										$3)));
-					if ($3 > MAX_TIME_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("LOCALTIME(%d) precision reduced to maximum allowed, %d",
-										$3, MAX_TIME_PRECISION)));
-						$3 = MAX_TIME_PRECISION;
-					}
-					d->typmod = $3;
+					d->typmods = list_make1(makeIntConst($3));
 
 					$$ = (Node *)makeTypeCast((Node *)s, d);
 				}
@@ -7660,20 +7497,7 @@ func_expr:	func_name '(' ')'
 					s->typename = SystemTypeName("text");
 
 					d = SystemTypeName("timestamp");
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("LOCALTIMESTAMP(%d) precision must not be negative",
-										$3)));
-					if ($3 > MAX_TIMESTAMP_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("LOCALTIMESTAMP(%d) precision reduced to maximum allowed, %d",
-										$3, MAX_TIMESTAMP_PRECISION)));
-						$3 = MAX_TIMESTAMP_PRECISION;
-					}
-					d->typmod = $3;
+					d->typmods = list_make1(makeIntConst($3));
 
 					$$ = (Node *)makeTypeCast((Node *)s, d);
 				}
@@ -7880,7 +7704,7 @@ func_expr:	func_name '(' ')'
 					$$ = (Node *)v;
 				}
 			| XMLCONCAT '(' expr_list ')'
-				{		
+				{
 					$$ = makeXmlExpr(IS_XMLCONCAT, NULL, NIL, $3);
 				}
 			| XMLELEMENT '(' NAME_P ColLabel ')'
@@ -7985,7 +7809,7 @@ xml_attribute_el: a_expr AS ColLabel
 					$$ = makeNode(ResTarget);
 					$$->name = NULL;
 					$$->indirection = NULL;
-					$$->val = (Node *) $1;				
+					$$->val = (Node *) $1;
 					$$->location = @1;
 				}
 		;
@@ -8486,12 +8310,12 @@ file_name:	Sconst									{ $$ = $1; };
 /*
  * The production for a qualified func_name has to exactly match the
  * production for a qualified columnref, because we cannot tell which we
- * are parsing until we see what comes after it ('(' for a func_name,
+ * are parsing until we see what comes after it ('(' or Sconst for a func_name,
  * anything else for a columnref).  Therefore we allow 'indirection' which
  * may contain subscripts, and reject that case in the C code.  (If we
  * ever implement SQL99-like methods, such syntax may actually become legal!)
  */
-func_name:	function_name
+func_name:	type_function_name
 					{ $$ = list_make1(makeString($1)); }
 			| relation_name indirection
 					{ $$ = check_func_name(lcons(makeString($1), $2)); }
@@ -8541,6 +8365,27 @@ AexprConst: Iconst
 					n->val.val.str = $1;
 					$$ = (Node *)n;
 				}
+			| func_name Sconst
+				{
+					/* generic type 'literal' syntax */
+					A_Const *n = makeNode(A_Const);
+					n->typename = makeTypeNameFromNameList($1);
+					n->typename->location = @1;
+					n->val.type = T_String;
+					n->val.val.str = $2;
+					$$ = (Node *)n;
+				}
+			| func_name '(' expr_list ')' Sconst
+				{
+					/* generic syntax with a type modifier */
+					A_Const *n = makeNode(A_Const);
+					n->typename = makeTypeNameFromNameList($1);
+					n->typename->typmods = $3;
+					n->typename->location = @1;
+					n->val.type = T_String;
+					n->val.val.str = $5;
+					$$ = (Node *)n;
+				}
 			| ConstTypename Sconst
 				{
 					A_Const *n = makeNode(A_Const);
@@ -8557,7 +8402,7 @@ AexprConst: Iconst
 					n->val.val.str = $2;
 					/* precision is not specified, but fields may be... */
 					if ($3 != INTERVAL_FULL_RANGE)
-						n->typename->typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, $3);
+						n->typename->typmods = list_make1(makeIntConst($3));
 					$$ = (Node *)n;
 				}
 			| ConstInterval '(' Iconst ')' Sconst opt_interval
@@ -8566,21 +8411,8 @@ AexprConst: Iconst
 					n->typename = $1;
 					n->val.type = T_String;
 					n->val.val.str = $5;
-					/* precision specified, and fields may be... */
-					if ($3 < 0)
-						ereport(ERROR,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("INTERVAL(%d) precision must not be negative",
-										$3)));
-					if ($3 > MAX_INTERVAL_PRECISION)
-					{
-						ereport(WARNING,
-								(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
-								 errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
-										$3, MAX_INTERVAL_PRECISION)));
-						$3 = MAX_INTERVAL_PRECISION;
-					}
-					n->typename->typmod = INTERVAL_TYPMOD($3, $6);
+					n->typename->typmods = list_make2(makeIntConst($6),
+													  makeIntConst($3));
 					$$ = (Node *)n;
 				}
 			| TRUE_P
@@ -8625,18 +8457,11 @@ ColId:		IDENT									{ $$ = $1; }
 			| col_name_keyword						{ $$ = pstrdup($1); }
 		;
 
-/* Type identifier --- names that can be type names.
- */
-type_name:	IDENT									{ $$ = $1; }
-			| unreserved_keyword					{ $$ = pstrdup($1); }
-		;
-
-/* Function identifier --- names that can be function names.
+/* Type/function identifier --- names that can be type or function names.
  */
-function_name:
-			IDENT									{ $$ = $1; }
+type_function_name:	IDENT							{ $$ = $1; }
 			| unreserved_keyword					{ $$ = pstrdup($1); }
-			| func_name_keyword						{ $$ = pstrdup($1); }
+			| type_func_name_keyword				{ $$ = pstrdup($1); }
 		;
 
 /* Column label --- allowed labels in "AS" clauses.
@@ -8645,7 +8470,7 @@ function_name:
 ColLabel:	IDENT									{ $$ = $1; }
 			| unreserved_keyword					{ $$ = pstrdup($1); }
 			| col_name_keyword						{ $$ = pstrdup($1); }
-			| func_name_keyword						{ $$ = pstrdup($1); }
+			| type_func_name_keyword				{ $$ = pstrdup($1); }
 			| reserved_keyword						{ $$ = pstrdup($1); }
 		;
 
@@ -8940,7 +8765,7 @@ col_name_keyword:
 			| XMLSERIALIZE
 		;
 
-/* Function identifier --- keywords that can be function names.
+/* Type/function identifier --- keywords that can be type or function names.
  *
  * Most of these are keywords that are used as operators in expressions;
  * in general such keywords can't be column names because they would be
@@ -8950,7 +8775,7 @@ col_name_keyword:
  * productions in a_expr to support the goofy SQL9x argument syntax.
  * - thomas 2000-11-28
  */
-func_name_keyword:
+type_func_name_keyword:
 			  AUTHORIZATION
 			| BETWEEN
 			| BINARY
@@ -9383,12 +9208,8 @@ SystemFuncName(char *name)
 TypeName *
 SystemTypeName(char *name)
 {
-	TypeName   *n = makeNode(TypeName);
-
-	n->names = list_make2(makeString("pg_catalog"), makeString(name));
-	n->typmod = -1;
-	n->location = -1;
-	return n;
+	return makeTypeNameFromNameList(list_make2(makeString("pg_catalog"),
+											   makeString(name)));
 }
 
 /* parser_init()
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 7dbbb9a33a8..383013c9a10 100644
--- a/src/backend/parser/parse_expr.c
+++ b/src/backend/parser/parse_expr.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.202 2006/12/24 00:29:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.203 2006/12/30 21:21:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1810,29 +1810,6 @@ exprTypmod(Node *expr)
 	{
 		case T_Var:
 			return ((Var *) expr)->vartypmod;
-		case T_Const:
-			{
-				/* Be smart about string constants... */
-				Const	   *con = (Const *) expr;
-
-				switch (con->consttype)
-				{
-					case BPCHAROID:
-						if (!con->constisnull)
-						{
-							int32		len = VARSIZE(DatumGetPointer(con->constvalue)) - VARHDRSZ;
-
-							/* if multi-byte, take len and find # characters */
-							if (pg_database_encoding_max_length() > 1)
-								len = pg_mbstrlen_with_len(VARDATA(DatumGetPointer(con->constvalue)), len);
-							return len + VARHDRSZ;
-						}
-						break;
-					default:
-						break;
-				}
-			}
-			break;
 		case T_Param:
 			return ((Param *) expr)->paramtypmod;
 		case T_FuncExpr:
@@ -2024,14 +2001,16 @@ typecast_expression(ParseState *pstate, Node *expr, TypeName *typename)
 {
 	Oid			inputType = exprType(expr);
 	Oid			targetType;
+	int32		targetTypmod;
 
 	targetType = typenameTypeId(pstate, typename);
+	targetTypmod = typenameTypeMod(pstate, typename, targetType);
 
 	if (inputType == InvalidOid)
 		return expr;			/* do nothing if NULL input */
 
 	expr = coerce_to_target_type(pstate, expr, inputType,
-								 targetType, typename->typmod,
+								 targetType, targetTypmod,
 								 COERCION_EXPLICIT,
 								 COERCE_EXPLICIT_CAST);
 	if (expr == NULL)
diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c
index 75d5a50702a..f6d4fcae4aa 100644
--- a/src/backend/parser/parse_relation.c
+++ b/src/backend/parser/parse_relation.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.125 2006/10/04 00:29:56 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.126 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -901,9 +901,9 @@ addRangeTableEntryForFunction(ParseState *pstate,
 						(errcode(ERRCODE_INVALID_TABLE_DEFINITION),
 						 errmsg("column \"%s\" cannot be declared SETOF",
 								attrname)));
-			eref->colnames = lappend(eref->colnames, makeString(attrname));
 			attrtype = typenameTypeId(pstate, n->typename);
-			attrtypmod = n->typename->typmod;
+			attrtypmod = typenameTypeMod(pstate, n->typename, attrtype);
+			eref->colnames = lappend(eref->colnames, makeString(attrname));
 			rte->funccoltypes = lappend_oid(rte->funccoltypes, attrtype);
 			rte->funccoltypmods = lappend_int(rte->funccoltypmods, attrtypmod);
 		}
diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c
index 93c7db6b52a..6aeabb57498 100644
--- a/src/backend/parser/parse_type.c
+++ b/src/backend/parser/parse_type.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.85 2006/10/04 00:29:56 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_type.c,v 1.86 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,6 +20,7 @@
 #include "nodes/makefuncs.h"
 #include "parser/parser.h"
 #include "parser/parse_type.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/lsyscache.h"
 #include "utils/syscache.h"
@@ -245,9 +246,81 @@ typenameTypeId(ParseState *pstate, const TypeName *typename)
 				 errmsg("type \"%s\" is only a shell",
 						TypeNameToString(typename)),
 				 parser_errposition(pstate, typename->location)));
+
 	return typoid;
 }
 
+/*
+ * typenameTypeMod - given a TypeName, return the internal typmod value
+ *
+ * This will throw an error if the TypeName includes type modifiers that are
+ * illegal for the data type.
+ *
+ * The actual type OID represented by the TypeName must already have been
+ * determined (usually by typenameTypeId()), and is passed as typeId.
+ *
+ * pstate is only used for error location info, and may be NULL.
+ */
+int32
+typenameTypeMod(ParseState *pstate, const TypeName *typename,
+				Oid typeId)
+{
+	int32		result;
+	Oid			typmodin;
+	Datum	   *datums;
+	int			n;
+	ListCell   *l;
+	ArrayType  *arrtypmod;
+
+	Assert(OidIsValid(typeId));
+
+	/* Return prespecified typmod if no typmod expressions */
+	if (typename->typmods == NIL)
+		return typename->typemod;
+
+	/* Else, type had better accept typmods */
+	typmodin = get_typmodin(typeId);
+
+	if (typmodin == InvalidOid)
+		ereport(ERROR,
+				(errcode(ERRCODE_SYNTAX_ERROR),
+				 errmsg("type modifier is not allowed for type \"%s\"",
+						TypeNameToString(typename)),
+				 parser_errposition(pstate, typename->location)));
+
+	/*
+	 * Convert the list of (raw grammar output) expressions to an integer
+	 * array.  Currently, we only allow simple integer constants, though
+	 * possibly this could be extended.
+	 */
+	datums = (Datum *) palloc(list_length(typename->typmods) * sizeof(Datum));
+	n = 0;
+	foreach(l, typename->typmods)
+	{
+		A_Const	*ac = (A_Const *) lfirst(l);
+
+		if (!IsA(ac, A_Const) ||
+			!IsA(&ac->val, Integer))
+			ereport(ERROR,
+					(errcode(ERRCODE_SYNTAX_ERROR),
+					 errmsg("type modifiers must be integer constants"),
+					 parser_errposition(pstate, typename->location)));
+		datums[n++] = Int32GetDatum(ac->val.val.ival);
+	}
+
+	/* hardwired knowledge about int4's representation details here */
+	arrtypmod = construct_array(datums, n, INT4OID,
+								sizeof(int4), true, 'i');
+
+	result = DatumGetInt32(OidFunctionCall1(typmodin,
+											PointerGetDatum(arrtypmod)));
+
+	pfree(datums);
+	pfree(arrtypmod);
+
+	return result;
+}
+
 /*
  * typenameType - given a TypeName, return a Type structure
  *
@@ -490,7 +563,7 @@ parseTypeString(const char *str, Oid *type_id, int32 *typmod)
 		goto fail;
 
 	*type_id = typenameTypeId(NULL, typename);
-	*typmod = typename->typmod;
+	*typmod = typenameTypeMod(NULL, typename, *type_id);
 
 	pfree(buf.data);
 
diff --git a/src/backend/utils/adt/arrayutils.c b/src/backend/utils/adt/arrayutils.c
index 2a732ad63bb..2913d0a1d8b 100644
--- a/src/backend/utils/adt/arrayutils.c
+++ b/src/backend/utils/adt/arrayutils.c
@@ -8,13 +8,14 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayutils.c,v 1.21 2006/03/05 15:58:41 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayutils.c,v 1.22 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "catalog/pg_type.h"
 #include "utils/array.h"
 #include "utils/memutils.h"
 
@@ -188,3 +189,30 @@ mda_next_tuple(int n, int *curr, const int *span)
 
 	return -1;
 }
+
+/*
+ * ArrayGetTypmods: verify that argument is a 1-D integer array,
+ * return its length and a pointer to the first contained integer.
+ */
+int32 *
+ArrayGetTypmods(ArrayType *arr, int *n)
+{
+	if (ARR_ELEMTYPE(arr) != INT4OID)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_ELEMENT_ERROR),
+				 errmsg("typmod array must be type integer[]")));
+
+	if (ARR_NDIM(arr) != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_ARRAY_SUBSCRIPT_ERROR),
+				 errmsg("typmod array must be one-dimensional")));
+
+	if (ARR_HASNULL(arr))
+		ereport(ERROR,
+				(errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+				 errmsg("typmod array must not contain nulls")));
+
+	*n = ArrayGetNItems(ARR_NDIM(arr), ARR_DIMS(arr));
+
+	return (int32 *) ARR_DATA_PTR(arr);
+}
diff --git a/src/backend/utils/adt/date.c b/src/backend/utils/adt/date.c
index 9efc7125b19..e2781dec485 100644
--- a/src/backend/utils/adt/date.c
+++ b/src/backend/utils/adt/date.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.125 2006/07/14 14:52:23 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/date.c,v 1.126 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,7 @@
 #include "libpq/pqformat.h"
 #include "miscadmin.h"
 #include "parser/scansup.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/date.h"
 #include "utils/nabstime.h"
@@ -43,6 +44,60 @@ static int	tm2time(struct pg_tm * tm, fsec_t fsec, TimeADT *result);
 static int	tm2timetz(struct pg_tm * tm, fsec_t fsec, int tz, TimeTzADT *result);
 static void AdjustTimeForTypmod(TimeADT *time, int32 typmod);
 
+
+/* common code for timetypmodin and timetztypmodin */
+static int32
+anytime_typmodin(bool istz, ArrayType *ta)
+{
+	int32	typmod;
+	int32	*tl;
+	int		n;
+
+	tl = ArrayGetTypmods(ta, &n);
+
+	/*
+	 * we're not too tense about good error message here because grammar
+	 * shouldn't allow wrong number of modifiers for TIME
+	 */
+	if (n != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid type modifier")));
+
+	if (*tl < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("TIME(%d)%s precision must not be negative",
+						*tl, (istz ? " WITH TIME ZONE" : ""))));
+	if (*tl > MAX_TIME_PRECISION)
+	{
+		ereport(WARNING,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("TIME(%d)%s precision reduced to maximum allowed, %d",
+						*tl, (istz ? " WITH TIME ZONE" : "" ),
+						MAX_TIME_PRECISION)));
+		typmod = MAX_TIME_PRECISION;
+	} else
+		typmod = *tl;
+
+	return typmod;
+}
+
+/* common code for timetypmodout and timetztypmodout */
+static char *
+anytime_typmodout(bool istz, int32 typmod)
+{
+	char    *res = (char *) palloc(64);
+	const char *tz = istz ? " with time zone" : " without time zone";
+
+	if (typmod >= 0)
+		snprintf(res, 64, "(%d)%s", (int) typmod, tz);
+	else
+		snprintf(res, 64, "%s", tz);
+	return res;
+}
+
+
 /*****************************************************************************
  *	 Date ADT
  *****************************************************************************/
@@ -1029,6 +1084,22 @@ time_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+Datum
+timetypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+	PG_RETURN_INT32(anytime_typmodin(false, ta));
+}
+
+Datum
+timetypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+
+	PG_RETURN_CSTRING(anytime_typmodout(false, typmod));
+}
+
 
 /* time_scale()
  * Adjust time type for specified scale factor.
@@ -1830,6 +1901,22 @@ timetz_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+Datum
+timetztypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+	PG_RETURN_INT32(anytime_typmodin(true, ta));
+}
+
+Datum
+timetztypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+
+	PG_RETURN_CSTRING(anytime_typmodout(true, typmod));
+}
+
 
 /* timetz2tm()
  * Convert TIME WITH TIME ZONE data type to POSIX time structure.
diff --git a/src/backend/utils/adt/format_type.c b/src/backend/utils/adt/format_type.c
index 22d9bc156f6..d2b6323f0f2 100644
--- a/src/backend/utils/adt/format_type.c
+++ b/src/backend/utils/adt/format_type.c
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.44 2006/07/14 14:52:24 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/format_type.c,v 1.45 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -20,7 +20,6 @@
 #include "catalog/namespace.h"
 #include "catalog/pg_type.h"
 #include "utils/builtins.h"
-#include "utils/datetime.h"
 #include "utils/lsyscache.h"
 #include "utils/numeric.h"
 #include "utils/syscache.h"
@@ -31,6 +30,7 @@
 
 static char *format_type_internal(Oid type_oid, int32 typemod,
 					 bool typemod_given, bool allow_invalid);
+static char *printTypmod(const char *typname, int32 typmod, Oid typmodout);
 static char *
 psnprintf(size_t len, const char *fmt,...)
 /* This lets gcc check the format string for consistency. */
@@ -186,8 +186,7 @@ format_type_internal(Oid type_oid, int32 typemod,
 	{
 		case BITOID:
 			if (with_typemod)
-				buf = psnprintf(5 + MAX_INT32_LEN + 1, "bit(%d)",
-								(int) typemod);
+				buf = printTypmod("bit", typemod, typeform->typmodout);
 			else if (typemod_given)
 			{
 				/*
@@ -206,8 +205,7 @@ format_type_internal(Oid type_oid, int32 typemod,
 
 		case BPCHAROID:
 			if (with_typemod)
-				buf = psnprintf(11 + MAX_INT32_LEN + 1, "character(%d)",
-								(int) (typemod - VARHDRSZ));
+				buf = printTypmod("character", typemod, typeform->typmodout);
 			else if (typemod_given)
 			{
 				/*
@@ -242,136 +240,56 @@ format_type_internal(Oid type_oid, int32 typemod,
 
 		case NUMERICOID:
 			if (with_typemod)
-				buf = psnprintf(10 + 2 * MAX_INT32_LEN + 1, "numeric(%d,%d)",
-								((typemod - VARHDRSZ) >> 16) & 0xffff,
-								(typemod - VARHDRSZ) & 0xffff);
+				buf = printTypmod("numeric", typemod, typeform->typmodout);
 			else
 				buf = pstrdup("numeric");
 			break;
 
 		case INTERVALOID:
 			if (with_typemod)
-			{
-				int			fields = INTERVAL_RANGE(typemod);
-				int			precision = INTERVAL_PRECISION(typemod);
-				const char *fieldstr;
-
-				switch (fields)
-				{
-					case INTERVAL_MASK(YEAR):
-						fieldstr = " year";
-						break;
-					case INTERVAL_MASK(MONTH):
-						fieldstr = " month";
-						break;
-					case INTERVAL_MASK(DAY):
-						fieldstr = " day";
-						break;
-					case INTERVAL_MASK(HOUR):
-						fieldstr = " hour";
-						break;
-					case INTERVAL_MASK(MINUTE):
-						fieldstr = " minute";
-						break;
-					case INTERVAL_MASK(SECOND):
-						fieldstr = " second";
-						break;
-						case INTERVAL_MASK(YEAR)
-					| INTERVAL_MASK(MONTH):
-						fieldstr = " year to month";
-						break;
-						case INTERVAL_MASK(DAY)
-					| INTERVAL_MASK(HOUR):
-						fieldstr = " day to hour";
-						break;
-						case INTERVAL_MASK(DAY)
-							| INTERVAL_MASK(HOUR)
-					| INTERVAL_MASK(MINUTE):
-						fieldstr = " day to minute";
-						break;
-						case INTERVAL_MASK(DAY)
-							| INTERVAL_MASK(HOUR)
-							| INTERVAL_MASK(MINUTE)
-					| INTERVAL_MASK(SECOND):
-						fieldstr = " day to second";
-						break;
-						case INTERVAL_MASK(HOUR)
-					| INTERVAL_MASK(MINUTE):
-						fieldstr = " hour to minute";
-						break;
-						case INTERVAL_MASK(HOUR)
-							| INTERVAL_MASK(MINUTE)
-					| INTERVAL_MASK(SECOND):
-						fieldstr = " hour to second";
-						break;
-						case INTERVAL_MASK(MINUTE)
-					| INTERVAL_MASK(SECOND):
-						fieldstr = " minute to second";
-						break;
-					case INTERVAL_FULL_RANGE:
-						fieldstr = "";
-						break;
-					default:
-						elog(ERROR, "invalid INTERVAL typmod: 0x%x", typemod);
-						fieldstr = "";
-						break;
-				}
-				if (precision != INTERVAL_FULL_PRECISION)
-					buf = psnprintf(100, "interval(%d)%s",
-									precision, fieldstr);
-				else
-					buf = psnprintf(100, "interval%s",
-									fieldstr);
-			}
+				buf = printTypmod("interval", typemod, typeform->typmodout);
 			else
 				buf = pstrdup("interval");
 			break;
 
 		case TIMEOID:
 			if (with_typemod)
-				buf = psnprintf(50, "time(%d) without time zone",
-								typemod);
+				buf = printTypmod("time", typemod, typeform->typmodout);
 			else
 				buf = pstrdup("time without time zone");
 			break;
 
 		case TIMETZOID:
 			if (with_typemod)
-				buf = psnprintf(50, "time(%d) with time zone",
-								typemod);
+				buf = printTypmod("time", typemod, typeform->typmodout);
 			else
 				buf = pstrdup("time with time zone");
 			break;
 
 		case TIMESTAMPOID:
 			if (with_typemod)
-				buf = psnprintf(50, "timestamp(%d) without time zone",
-								typemod);
+				buf = printTypmod("timestamp", typemod, typeform->typmodout);
 			else
 				buf = pstrdup("timestamp without time zone");
 			break;
 
 		case TIMESTAMPTZOID:
 			if (with_typemod)
-				buf = psnprintf(50, "timestamp(%d) with time zone",
-								typemod);
+				buf = printTypmod("timestamp", typemod, typeform->typmodout);
 			else
 				buf = pstrdup("timestamp with time zone");
 			break;
 
 		case VARBITOID:
 			if (with_typemod)
-				buf = psnprintf(13 + MAX_INT32_LEN + 1, "bit varying(%d)",
-								(int) typemod);
+				buf = printTypmod("bit varying", typemod, typeform->typmodout);
 			else
 				buf = pstrdup("bit varying");
 			break;
 
 		case VARCHAROID:
 			if (with_typemod)
-				buf = psnprintf(19 + MAX_INT32_LEN + 1,
-								"character varying(%d)",
-								(int) (typemod - VARHDRSZ));
+				buf = printTypmod("character varying", typemod, typeform->typmodout);
 			else
 				buf = pstrdup("character varying");
 			break;
@@ -396,6 +314,9 @@ format_type_internal(Oid type_oid, int32 typemod,
 		typname = NameStr(typeform->typname);
 
 		buf = quote_qualified_identifier(nspname, typname);
+
+		if (with_typemod)
+			buf = printTypmod(buf, typemod, typeform->typmodout);
 	}
 
 	if (is_array)
@@ -407,6 +328,38 @@ format_type_internal(Oid type_oid, int32 typemod,
 }
 
 
+/*
+ * Add typmod decoration to the basic type name
+ */
+static char *
+printTypmod(const char *typname, int32 typmod, Oid typmodout)
+{
+	char	*res;
+
+	/* Shouldn't be called if typmod is -1 */
+	Assert(typmod >= 0);
+
+	if (typmodout == InvalidOid)
+	{
+		/* Default behavior: just print the integer typmod with parens */
+		res = psnprintf(strlen(typname) + MAX_INT32_LEN + 3, "%s(%d)",
+						typname, (int) typmod);
+	}
+	else
+	{
+		/* Use the type-specific typmodout procedure */
+		char *tmstr;
+
+		tmstr = DatumGetCString(OidFunctionCall1(typmodout,
+												 Int32GetDatum(typmod)));
+		res = psnprintf(strlen(typname) + strlen(tmstr) + 1, "%s%s",
+						typname, tmstr);
+	}
+
+	return res;
+}
+
+
 /*
  * type_maximum_size --- determine maximum width of a variable-width column
  *
@@ -417,7 +370,9 @@ format_type_internal(Oid type_oid, int32 typemod,
  *
  * This may appear unrelated to format_type(), but in fact the two routines
  * share knowledge of the encoding of typmod for different types, so it's
- * convenient to keep them together.
+ * convenient to keep them together.  (XXX now that most of this knowledge
+ * has been pushed out of format_type into the typmodout functions, it's
+ * interesting to wonder if it's worth trying to factor this code too...)
  */
 int32
 type_maximum_size(Oid type_oid, int32 typemod)
diff --git a/src/backend/utils/adt/numeric.c b/src/backend/utils/adt/numeric.c
index 35b0221b85d..11dd881011f 100644
--- a/src/backend/utils/adt/numeric.c
+++ b/src/backend/utils/adt/numeric.c
@@ -14,7 +14,7 @@
  * Copyright (c) 1998-2006, PostgreSQL Global Development Group
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.96 2006/10/04 00:29:59 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/numeric.c,v 1.97 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -470,7 +470,7 @@ numeric_send(PG_FUNCTION_ARGS)
  *	scale of the attribute have to be applied on the value.
  */
 Datum
-numeric		(PG_FUNCTION_ARGS)
+numeric(PG_FUNCTION_ARGS)
 {
 	Numeric		num = PG_GETARG_NUMERIC(0);
 	int32		typmod = PG_GETARG_INT32(1);
@@ -537,6 +537,67 @@ numeric		(PG_FUNCTION_ARGS)
 	PG_RETURN_NUMERIC(new);
 }
 
+Datum
+numerictypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType   *ta = PG_GETARG_ARRAYTYPE_P(0);
+	int32    	*tl;
+	int			n;
+	int32		typmod;
+
+	tl = ArrayGetTypmods(ta, &n);
+
+	if (n == 2)
+	{
+		if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("NUMERIC precision %d must be between 1 and %d",
+							tl[0], NUMERIC_MAX_PRECISION)));
+		if (tl[1] < 0 || tl[1] > tl[0])
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("NUMERIC scale %d must be between 0 and precision %d",
+							tl[1], tl[0])));
+		typmod = ((tl[0] << 16) | tl[1]) + VARHDRSZ;
+	}
+	else if (n == 1)
+	{
+		if (tl[0] < 1 || tl[0] > NUMERIC_MAX_PRECISION)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("NUMERIC precision %d must be between 1 and %d",
+							tl[0], NUMERIC_MAX_PRECISION)));
+		/* scale defaults to zero */
+		typmod = (tl[0] << 16) + VARHDRSZ;
+	}
+	else
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("invalid NUMERIC type modifier")));
+		typmod = 0;				/* keep compiler quiet */
+	}
+
+	PG_RETURN_INT32(typmod);
+}
+
+Datum
+numerictypmodout(PG_FUNCTION_ARGS)
+{
+	int32	typmod = PG_GETARG_INT32(0);
+	char   *res = (char *) palloc(64);
+
+	if (typmod >= 0)
+		snprintf(res, 64, "(%d,%d)",
+				 ((typmod - VARHDRSZ) >> 16) & 0xffff,
+				 (typmod - VARHDRSZ) & 0xffff);
+	else
+		*res = '\0';
+
+	PG_RETURN_CSTRING(res);
+}
+
 
 /* ----------------------------------------------------------------------
  *
diff --git a/src/backend/utils/adt/timestamp.c b/src/backend/utils/adt/timestamp.c
index f94413e3f32..f9b0bb2c992 100644
--- a/src/backend/utils/adt/timestamp.c
+++ b/src/backend/utils/adt/timestamp.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.169 2006/11/11 01:14:19 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/timestamp.c,v 1.170 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -56,6 +56,60 @@ static void AdjustIntervalForTypmod(Interval *interval, int32 typmod);
 static TimestampTz timestamp2timestamptz(Timestamp timestamp);
 
 
+/* common code for timestamptypmodin and timestamptztypmodin */
+static int32
+anytimestamp_typmodin(bool istz, ArrayType *ta)
+{
+    int32    typmod;
+	int32    *tl;
+	int		n;
+
+	tl = ArrayGetTypmods(ta, &n);
+
+	/*
+	 * we're not too tense about good error message here because grammar
+	 * shouldn't allow wrong number of modifiers for TIMESTAMP
+	 */
+	if (n != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid type modifier")));
+
+	if (*tl < 0)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("TIMESTAMP(%d)%s precision must not be negative",
+						*tl, (istz ? " WITH TIME ZONE" : ""))));
+	if (*tl > MAX_TIMESTAMP_PRECISION)
+	{
+		ereport(WARNING,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("TIMESTAMP(%d)%s precision reduced to maximum allowed, %d",
+						*tl, (istz ? " WITH TIME ZONE" : ""),
+						MAX_TIMESTAMP_PRECISION)));
+		typmod = MAX_TIMESTAMP_PRECISION;
+	} else
+		typmod = *tl;
+
+	return typmod;
+}
+
+/* common code for timestamptypmodout and timestamptztypmodout */
+static char *
+anytimestamp_typmodout(bool istz, int32 typmod)
+{
+	char    *res = (char *) palloc(64);
+	const char *tz = istz ? " with time zone" : " without time zone";
+
+	if (typmod >= 0)
+		snprintf(res, 64, "(%d)%s", (int) typmod, tz);
+	else
+		snprintf(res, 64, "%s", tz);
+
+	return res;
+}
+
+
 /*****************************************************************************
  *	 USER I/O ROUTINES														 *
  *****************************************************************************/
@@ -215,6 +269,22 @@ timestamp_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+Datum
+timestamptypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType    *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+	PG_RETURN_INT32(anytimestamp_typmodin(false, ta));
+}
+
+Datum
+timestamptypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+
+	PG_RETURN_CSTRING(anytimestamp_typmodout(false, typmod));
+}
+
 
 /* timestamp_scale()
  * Adjust time type for specified scale factor.
@@ -461,6 +531,22 @@ timestamptz_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+Datum
+timestamptztypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType    *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+	PG_RETURN_INT32(anytimestamp_typmodin(true, ta));
+}
+
+Datum
+timestamptztypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+
+	PG_RETURN_CSTRING(anytimestamp_typmodout(true, typmod));
+}
+
 
 /* timestamptz_scale()
  * Adjust time type for specified scale factor.
@@ -625,6 +711,162 @@ interval_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
+Datum
+intervaltypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType	*ta = PG_GETARG_ARRAYTYPE_P(0);
+	int32    	*tl;
+    int    		n;
+	int32		typmod;
+
+	tl = ArrayGetTypmods(ta, &n);
+
+	/*
+	 * tl[0] - opt_interval
+	 * tl[1] - Iconst (optional)
+	 *
+	 * Note we must validate tl[0] even though it's normally guaranteed
+	 * correct by the grammar --- consider SELECT 'foo'::"interval"(1000).
+	 */
+	if (n > 0)
+	{
+		switch (tl[0])
+		{
+			case INTERVAL_MASK(YEAR):
+			case INTERVAL_MASK(MONTH):
+			case INTERVAL_MASK(DAY):
+			case INTERVAL_MASK(HOUR):
+			case INTERVAL_MASK(MINUTE):
+			case INTERVAL_MASK(SECOND):
+			case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
+			case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
+			case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
+			case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+			case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
+			case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+			case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+			case INTERVAL_FULL_RANGE:
+				/* all OK */
+				break;
+			default:
+				ereport(ERROR,
+						(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						 errmsg("invalid INTERVAL type modifier")));
+		}
+	}
+
+	if (n == 1)
+	{
+		if (tl[0] != INTERVAL_FULL_RANGE)
+			typmod = INTERVAL_TYPMOD(INTERVAL_FULL_PRECISION, tl[0]);
+		else
+			typmod = -1;
+	}
+	else if (n == 2)
+	{
+		if (tl[1] < 0)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					 errmsg("INTERVAL(%d) precision must not be negative",
+							 tl[1])));
+		if (tl[1] > MAX_INTERVAL_PRECISION)
+		{
+			ereport(WARNING,
+					(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+					errmsg("INTERVAL(%d) precision reduced to maximum allowed, %d",
+					tl[1], MAX_INTERVAL_PRECISION)));
+			typmod = INTERVAL_TYPMOD(MAX_INTERVAL_PRECISION, tl[0]);
+		}
+		else
+			typmod = INTERVAL_TYPMOD(tl[1], tl[0]);
+	}
+	else
+	{
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+						errmsg("invalid INTERVAL type modifier")));
+		typmod = 0;				/* keep compiler quiet */
+	}
+
+	PG_RETURN_INT32(typmod);
+}
+
+Datum
+intervaltypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+	char	   *res = (char *) palloc(64);
+	int         fields;
+	int         precision;
+	const char *fieldstr;
+
+	if (typmod < 0)
+	{
+		*res = '\0';
+		PG_RETURN_CSTRING(res);
+	}
+
+	fields = INTERVAL_RANGE(typmod);
+	precision = INTERVAL_PRECISION(typmod);
+
+	switch (fields)
+	{
+		case INTERVAL_MASK(YEAR):
+			fieldstr = " year";
+			break;
+		case INTERVAL_MASK(MONTH):
+			fieldstr = " month";
+			break;
+		case INTERVAL_MASK(DAY):
+			fieldstr = " day";
+			break;
+		case INTERVAL_MASK(HOUR):
+			fieldstr = " hour";
+			break;
+		case INTERVAL_MASK(MINUTE):
+			fieldstr = " minute";
+			break;
+		case INTERVAL_MASK(SECOND):
+			fieldstr = " second";
+			break;
+		case INTERVAL_MASK(YEAR) | INTERVAL_MASK(MONTH):
+			fieldstr = " year to month";
+			break;
+		case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR):
+			fieldstr = " day to hour";
+			break;
+		case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
+			fieldstr = " day to minute";
+			break;
+		case INTERVAL_MASK(DAY) | INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+			fieldstr = " day to second";
+			break;
+		case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE):
+			fieldstr = " hour to minute";
+			break;
+		case INTERVAL_MASK(HOUR) | INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+			fieldstr = " hour to second";
+			break;
+		case INTERVAL_MASK(MINUTE) | INTERVAL_MASK(SECOND):
+			fieldstr = " minute to second";
+			break;
+		case INTERVAL_FULL_RANGE:
+			fieldstr = "";
+			break;
+		default:
+			elog(ERROR, "invalid INTERVAL typmod: 0x%x", typmod);
+			fieldstr = "";
+			break;
+	}
+
+	if (precision != INTERVAL_FULL_PRECISION)
+		snprintf(res, 64, "(%d)%s", precision, fieldstr);
+	else
+		snprintf(res, 64, "%s", fieldstr);
+
+	PG_RETURN_CSTRING(res);
+}
+
 
 /* interval_scale()
  * Adjust interval type for specified fields.
diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index e0a67d340ef..4a810b955da 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -9,19 +9,71 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.50 2006/07/14 14:52:24 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.51 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
 
 #include "postgres.h"
 
+#include "access/htup.h"
 #include "libpq/pqformat.h"
+#include "utils/array.h"
 #include "utils/varbit.h"
 
 #define HEXDIG(z)	 ((z)<10 ? ((z)+'0') : ((z)-10+'A'))
 
 
+/* common code for bittypmodin and varbittypmodin */
+static int32
+anybit_typmodin(ArrayType *ta, const char *typename)
+{
+	int32    typmod;
+	int32	*tl;
+	int		n;
+
+	tl = ArrayGetTypmods(ta, &n);
+
+	/*
+	 * we're not too tense about good error message here because grammar
+	 * shouldn't allow wrong number of modifiers for BIT
+	 */
+	if (n != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid type modifier")));
+
+	if (*tl < 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("length for type %s must be at least 1",
+						typename)));
+	if (*tl > (MaxAttrSize * BITS_PER_BYTE))
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("length for type %s cannot exceed %d",
+						typename, MaxAttrSize * BITS_PER_BYTE)));
+
+	typmod = *tl;
+
+	return typmod;
+}
+
+/* common code for bittypmodout and varbittypmodout */
+static char *
+anybit_typmodout(int32 typmod)
+{
+	char    *res = (char *) palloc(64);
+
+	if (typmod >= 0)
+		snprintf(res, 64, "(%d)", typmod);
+	else
+		*res = '\0';
+
+	return res;
+}
+
+
 /*----------
  *	attypmod -- contains the length of the bit string in bits, or for
  *			   varying bits the maximum length.
@@ -325,6 +377,23 @@ bit(PG_FUNCTION_ARGS)
 	PG_RETURN_VARBIT_P(result);
 }
 
+Datum
+bittypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType    *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+	PG_RETURN_INT32(anybit_typmodin(ta, "bit"));
+}
+
+Datum
+bittypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+
+	PG_RETURN_CSTRING(anybit_typmodout(typmod));
+}
+
+
 /*
  * varbit_in -
  *	  converts a string to the internal representation of a bitstring.
@@ -603,6 +672,22 @@ varbit(PG_FUNCTION_ARGS)
 	PG_RETURN_VARBIT_P(result);
 }
 
+Datum
+varbittypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType    *ta = PG_GETARG_ARRAYTYPE_P(0);
+
+	PG_RETURN_INT32(anybit_typmodin(ta, "varbit"));
+}
+
+Datum
+varbittypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+
+	PG_RETURN_CSTRING(anybit_typmodout(typmod));
+}
+
 
 /*
  * Comparison operators
diff --git a/src/backend/utils/adt/varchar.c b/src/backend/utils/adt/varchar.c
index 937cf96ebef..9cc2f5e34e3 100644
--- a/src/backend/utils/adt/varchar.c
+++ b/src/backend/utils/adt/varchar.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.119 2006/10/04 00:30:00 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/varchar.c,v 1.120 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -17,10 +17,65 @@
 
 #include "access/hash.h"
 #include "libpq/pqformat.h"
+#include "utils/array.h"
 #include "utils/builtins.h"
 #include "mb/pg_wchar.h"
 
 
+/* common code for bpchartypmodin and varchartypmodin */
+static int32
+anychar_typmodin(ArrayType *ta, const char *typename)
+{
+	int32 	typmod;
+	int32	*tl;
+	int		n;
+
+	tl = ArrayGetTypmods(ta, &n);
+
+	/*
+	 * we're not too tense about good error message here because grammar
+	 * shouldn't allow wrong number of modifiers for CHAR
+	 */
+	if (n != 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("invalid type modifier")));
+
+	if (*tl < 1)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("length for type %s must be at least 1", typename)));
+	if (*tl > MaxAttrSize)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
+				 errmsg("length for type %s cannot exceed %d",
+						typename, MaxAttrSize)));
+
+	/*
+	 * For largely historical reasons, the typmod is VARHDRSZ plus the
+	 * number of characters; there is enough client-side code that knows
+	 * about that that we'd better not change it.
+	 */
+	typmod = VARHDRSZ + *tl;
+
+	return typmod;
+}
+
+/* common code for bpchartypmodout and varchartypmodout */
+static char *
+anychar_typmodout(int32 typmod)
+{
+	char	*res = (char *) palloc(64);
+
+	if (typmod > VARHDRSZ)
+		snprintf(res, 64, "(%d)", (int) (typmod - VARHDRSZ));
+	else
+		*res = '\0';
+
+	return res;
+}
+
+
 /*
  * CHAR() and VARCHAR() types are part of the ANSI SQL standard. CHAR()
  * is for blank-padded string whose length is specified in CREATE TABLE.
@@ -359,6 +414,22 @@ name_bpchar(PG_FUNCTION_ARGS)
 	PG_RETURN_BPCHAR_P(result);
 }
 
+Datum
+bpchartypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType	*ta = PG_GETARG_ARRAYTYPE_P(0);
+
+	PG_RETURN_INT32(anychar_typmodin(ta, "char"));
+}
+
+Datum
+bpchartypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+
+	PG_RETURN_CSTRING(anychar_typmodout(typmod));
+}
+
 
 /*****************************************************************************
  *	 varchar - varchar(n)
@@ -536,6 +607,22 @@ varchar(PG_FUNCTION_ARGS)
 	PG_RETURN_VARCHAR_P(result);
 }
 
+Datum
+varchartypmodin(PG_FUNCTION_ARGS)
+{
+	ArrayType	*ta = PG_GETARG_ARRAYTYPE_P(0);
+
+	PG_RETURN_INT32(anychar_typmodin(ta, "varchar"));
+}
+
+Datum
+varchartypmodout(PG_FUNCTION_ARGS)
+{
+	int32 typmod = PG_GETARG_INT32(0);
+
+	PG_RETURN_CSTRING(anychar_typmodout(typmod));
+}
+
 
 /*****************************************************************************
  * Exported functions
diff --git a/src/backend/utils/cache/lsyscache.c b/src/backend/utils/cache/lsyscache.c
index 824ef4a1efe..8c4cbef66cb 100644
--- a/src/backend/utils/cache/lsyscache.c
+++ b/src/backend/utils/cache/lsyscache.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.139 2006/12/23 00:43:11 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/cache/lsyscache.c,v 1.140 2006/12/30 21:21:54 tgl Exp $
  *
  * NOTES
  *	  Eventually, the index information should go through here, too.
@@ -2016,6 +2016,60 @@ getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena)
 	ReleaseSysCache(typeTuple);
 }
 
+/*
+ * get_typmodin
+ *
+ *		Given the type OID, return the type's typmodin procedure, if any.
+ */
+Oid
+get_typmodin(Oid typid)
+{
+	HeapTuple	tp;
+
+	tp = SearchSysCache(TYPEOID,
+						ObjectIdGetDatum(typid),
+						0, 0, 0);
+	if (HeapTupleIsValid(tp))
+	{
+		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+		Oid			result;
+
+		result = typtup->typmodin;
+		ReleaseSysCache(tp);
+		return result;
+	}
+	else
+		return InvalidOid;
+}
+
+#ifdef NOT_USED
+/*
+ * get_typmodout
+ *
+ *		Given the type OID, return the type's typmodout procedure, if any.
+ */
+Oid
+get_typmodout(Oid typid)
+{
+	HeapTuple	tp;
+
+	tp = SearchSysCache(TYPEOID,
+						ObjectIdGetDatum(typid),
+						0, 0, 0);
+	if (HeapTupleIsValid(tp))
+	{
+		Form_pg_type typtup = (Form_pg_type) GETSTRUCT(tp);
+		Oid			result;
+
+		result = typtup->typmodout;
+		ReleaseSysCache(tp);
+		return result;
+	}
+	else
+		return InvalidOid;
+}
+#endif /* NOT_USED */
+
 
 /*				---------- STATISTICS CACHE ----------					 */
 
diff --git a/src/backend/utils/misc/guc.c b/src/backend/utils/misc/guc.c
index 6c6ff1fb5a3..377c6d87c3a 100644
--- a/src/backend/utils/misc/guc.c
+++ b/src/backend/utils/misc/guc.c
@@ -10,7 +10,7 @@
  * Written by Peter Eisentraut <peter_e@gmx.net>.
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.363 2006/12/23 00:52:40 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/misc/guc.c,v 1.364 2006/12/30 21:21:54 tgl Exp $
  *
  *--------------------------------------------------------------------
  */
@@ -45,6 +45,7 @@
 #include "parser/gramparse.h"
 #include "parser/parse_expr.h"
 #include "parser/parse_relation.h"
+#include "parser/parse_type.h"
 #include "parser/scansup.h"
 #include "pgstat.h"
 #include "postmaster/autovacuum.h"
@@ -4523,14 +4524,17 @@ flatten_set_variable_args(const char *name, List *args)
 					 * to interval and back to normalize the value and account
 					 * for any typmod.
 					 */
+					int32		typmod;
 					Datum		interval;
 					char	   *intervalout;
 
+					typmod = typenameTypeMod(NULL, arg->typename, INTERVALOID);
+
 					interval =
 						DirectFunctionCall3(interval_in,
 											CStringGetDatum(val),
 											ObjectIdGetDatum(InvalidOid),
-									   Int32GetDatum(arg->typename->typmod));
+											Int32GetDatum(typmod));
 
 					intervalout =
 						DatumGetCString(DirectFunctionCall1(interval_out,
diff --git a/src/bin/pg_dump/pg_dump.c b/src/bin/pg_dump/pg_dump.c
index ae8d54936f1..637af90d91c 100644
--- a/src/bin/pg_dump/pg_dump.c
+++ b/src/bin/pg_dump/pg_dump.c
@@ -12,7 +12,7 @@
  *	by PostgreSQL
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.454 2006/12/23 00:43:12 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/bin/pg_dump/pg_dump.c,v 1.455 2006/12/30 21:21:54 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -5007,11 +5007,15 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	char	   *typoutput;
 	char	   *typreceive;
 	char	   *typsend;
+	char	   *typmodin;
+	char	   *typmodout;
 	char	   *typanalyze;
 	Oid			typinputoid;
 	Oid			typoutputoid;
 	Oid			typreceiveoid;
 	Oid			typsendoid;
+	Oid			typmodinoid;
+	Oid			typmodoutoid;
 	Oid			typanalyzeoid;
 	char	   *typdelim;
 	char	   *typbyval;
@@ -5024,15 +5028,35 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	selectSourceSchema(tinfo->dobj.namespace->dobj.name);
 
 	/* Fetch type-specific details */
-	if (fout->remoteVersion >= 80000)
+	if (fout->remoteVersion >= 80300)
 	{
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, typreceive, typsend, "
+						  "typmodin, typmodout, typanalyze, "
+						  "typinput::pg_catalog.oid as typinputoid, "
+						  "typoutput::pg_catalog.oid as typoutputoid, "
+						  "typreceive::pg_catalog.oid as typreceiveoid, "
+						  "typsend::pg_catalog.oid as typsendoid, "
+						  "typmodin::pg_catalog.oid as typmodinoid, "
+						  "typmodout::pg_catalog.oid as typmodoutoid, "
+						  "typanalyze::pg_catalog.oid as typanalyzeoid, "
+						  "typdelim, typbyval, typalign, typstorage, "
+						  "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) as typdefaultbin, typdefault "
+						  "FROM pg_catalog.pg_type "
+						  "WHERE oid = '%u'::pg_catalog.oid",
+						  tinfo->dobj.catId.oid);
+	}
+	else if (fout->remoteVersion >= 80000)
+	{
+		appendPQExpBuffer(query, "SELECT typlen, "
+						  "typinput, typoutput, typreceive, typsend, "
+						  "'-' as typmodin, '-' as typmodout, "
 						  "typanalyze, "
 						  "typinput::pg_catalog.oid as typinputoid, "
 						  "typoutput::pg_catalog.oid as typoutputoid, "
 						  "typreceive::pg_catalog.oid as typreceiveoid, "
 						  "typsend::pg_catalog.oid as typsendoid, "
+						  "0 as typmodinoid, 0 as typmodoutoid, "
 						  "typanalyze::pg_catalog.oid as typanalyzeoid, "
 						  "typdelim, typbyval, typalign, typstorage, "
 						  "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) as typdefaultbin, typdefault "
@@ -5044,11 +5068,13 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	{
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, typreceive, typsend, "
+						  "'-' as typmodin, '-' as typmodout, "
 						  "'-' as typanalyze, "
 						  "typinput::pg_catalog.oid as typinputoid, "
 						  "typoutput::pg_catalog.oid as typoutputoid, "
 						  "typreceive::pg_catalog.oid as typreceiveoid, "
 						  "typsend::pg_catalog.oid as typsendoid, "
+						  "0 as typmodinoid, 0 as typmodoutoid, "
 						  "0 as typanalyzeoid, "
 						  "typdelim, typbyval, typalign, typstorage, "
 						  "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) as typdefaultbin, typdefault "
@@ -5061,10 +5087,12 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, "
 						  "'-' as typreceive, '-' as typsend, "
+						  "'-' as typmodin, '-' as typmodout, "
 						  "'-' as typanalyze, "
 						  "typinput::pg_catalog.oid as typinputoid, "
 						  "typoutput::pg_catalog.oid as typoutputoid, "
 						  "0 as typreceiveoid, 0 as typsendoid, "
+						  "0 as typmodinoid, 0 as typmodoutoid, "
 						  "0 as typanalyzeoid, "
 						  "typdelim, typbyval, typalign, typstorage, "
 						  "pg_catalog.pg_get_expr(typdefaultbin, 'pg_catalog.pg_type'::pg_catalog.regclass) as typdefaultbin, typdefault "
@@ -5081,10 +5109,12 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, "
 						  "'-' as typreceive, '-' as typsend, "
+						  "'-' as typmodin, '-' as typmodout, "
 						  "'-' as typanalyze, "
 						  "typinput::oid as typinputoid, "
 						  "typoutput::oid as typoutputoid, "
 						  "0 as typreceiveoid, 0 as typsendoid, "
+						  "0 as typmodinoid, 0 as typmodoutoid, "
 						  "0 as typanalyzeoid, "
 						  "typdelim, typbyval, typalign, typstorage, "
 						  "NULL as typdefaultbin, typdefault "
@@ -5101,10 +5131,12 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, "
 						  "'-' as typreceive, '-' as typsend, "
+						  "'-' as typmodin, '-' as typmodout, "
 						  "'-' as typanalyze, "
 						  "typinput::oid as typinputoid, "
 						  "typoutput::oid as typoutputoid, "
 						  "0 as typreceiveoid, 0 as typsendoid, "
+						  "0 as typmodinoid, 0 as typmodoutoid, "
 						  "0 as typanalyzeoid, "
 						  "typdelim, typbyval, typalign, typstorage, "
 						  "NULL as typdefaultbin, NULL as typdefault "
@@ -5117,10 +5149,12 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		appendPQExpBuffer(query, "SELECT typlen, "
 						  "typinput, typoutput, "
 						  "'-' as typreceive, '-' as typsend, "
+						  "'-' as typmodin, '-' as typmodout, "
 						  "'-' as typanalyze, "
 						  "typinput::oid as typinputoid, "
 						  "typoutput::oid as typoutputoid, "
 						  "0 as typreceiveoid, 0 as typsendoid, "
+						  "0 as typmodinoid, 0 as typmodoutoid, "
 						  "0 as typanalyzeoid, "
 						  "typdelim, typbyval, typalign, "
 						  "'p'::char as typstorage, "
@@ -5147,11 +5181,15 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 	typoutput = PQgetvalue(res, 0, PQfnumber(res, "typoutput"));
 	typreceive = PQgetvalue(res, 0, PQfnumber(res, "typreceive"));
 	typsend = PQgetvalue(res, 0, PQfnumber(res, "typsend"));
+	typmodin = PQgetvalue(res, 0, PQfnumber(res, "typmodin"));
+	typmodout = PQgetvalue(res, 0, PQfnumber(res, "typmodout"));
 	typanalyze = PQgetvalue(res, 0, PQfnumber(res, "typanalyze"));
 	typinputoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typinputoid")));
 	typoutputoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typoutputoid")));
 	typreceiveoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typreceiveoid")));
 	typsendoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typsendoid")));
+	typmodinoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodinoid")));
+	typmodoutoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typmodoutoid")));
 	typanalyzeoid = atooid(PQgetvalue(res, 0, PQfnumber(res, "typanalyzeoid")));
 	typdelim = PQgetvalue(res, 0, PQfnumber(res, "typdelim"));
 	typbyval = PQgetvalue(res, 0, PQfnumber(res, "typbyval"));
@@ -5193,6 +5231,10 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 			appendPQExpBuffer(q, ",\n    RECEIVE = %s", typreceive);
 		if (OidIsValid(typsendoid))
 			appendPQExpBuffer(q, ",\n    SEND = %s", typsend);
+		if (OidIsValid(typmodinoid))
+			appendPQExpBuffer(q, ",\n    TYPMOD_IN = %s", typmodin);
+		if (OidIsValid(typmodoutoid))
+			appendPQExpBuffer(q, ",\n    TYPMOD_OUT = %s", typmodout);
 		if (OidIsValid(typanalyzeoid))
 			appendPQExpBuffer(q, ",\n    ANALYZE = %s", typanalyze);
 	}
@@ -5202,7 +5244,7 @@ dumpBaseType(Archive *fout, TypeInfo *tinfo)
 		/* cannot combine these because fmtId uses static result area */
 		appendPQExpBuffer(q, ",\n    INPUT = %s", fmtId(typinput));
 		appendPQExpBuffer(q, ",\n    OUTPUT = %s", fmtId(typoutput));
-		/* no chance that receive/send/analyze need be printed */
+		/* receive/send/typmodin/typmodout/analyze need not be printed */
 	}
 
 	if (typdefault != NULL)
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 1b1e07bd719..1200af0c6e6 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.367 2006/12/28 14:28:36 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.368 2006/12/30 21:21:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200612281
+#define CATALOG_VERSION_NO	200612291
 
 #endif
diff --git a/src/include/catalog/pg_attribute.h b/src/include/catalog/pg_attribute.h
index 3fcc5ed6746..2c38c2b88f8 100644
--- a/src/include/catalog/pg_attribute.h
+++ b/src/include/catalog/pg_attribute.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.126 2006/11/05 22:42:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_attribute.h,v 1.127 2006/12/30 21:21:55 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -236,15 +236,17 @@ typedef FormData_pg_attribute *Form_pg_attribute;
 { 1247, {"typoutput"},	   24, -1,	4, 12, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
 { 1247, {"typreceive"},    24, -1,	4, 13, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
 { 1247, {"typsend"},	   24, -1,	4, 14, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typanalyze"},    24, -1,	4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typalign"},	   18, -1,	1, 16, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typstorage"},    18, -1,	1, 17, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typnotnull"},    16, -1,	1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
-{ 1247, {"typbasetype"},   26, -1,	4, 19, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typtypmod"},	   23, -1,	4, 20, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typndims"},	   23, -1,	4, 21, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
-{ 1247, {"typdefaultbin"}, 25, -1, -1, 22, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
-{ 1247, {"typdefault"},    25, -1, -1, 23, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
+{ 1247, {"typmodin"},	   24, -1,	4, 15, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typmodout"},	   24, -1,	4, 16, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typanalyze"},    24, -1,	4, 17, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typalign"},	   18, -1,	1, 18, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typstorage"},    18, -1,	1, 19, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typnotnull"},    16, -1,	1, 20, 0, -1, -1, true, 'p', 'c', true, false, false, true, 0 }, \
+{ 1247, {"typbasetype"},   26, -1,	4, 21, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typtypmod"},	   23, -1,	4, 22, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typndims"},	   23, -1,	4, 23, 0, -1, -1, true, 'p', 'i', true, false, false, true, 0 }, \
+{ 1247, {"typdefaultbin"}, 25, -1, -1, 24, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }, \
+{ 1247, {"typdefault"},    25, -1, -1, 25, 0, -1, -1, false, 'x', 'i', false, false, false, true, 0 }
 
 DATA(insert ( 1247 typname			19 -1 NAMEDATALEN	1 0 -1 -1 f p i t f f t 0));
 DATA(insert ( 1247 typnamespace		26 -1 4   2 0 -1 -1 t p i t f f t 0));
@@ -260,15 +262,17 @@ DATA(insert ( 1247 typinput			24 -1 4  11 0 -1 -1 t p i t f f t 0));
 DATA(insert ( 1247 typoutput		24 -1 4  12 0 -1 -1 t p i t f f t 0));
 DATA(insert ( 1247 typreceive		24 -1 4  13 0 -1 -1 t p i t f f t 0));
 DATA(insert ( 1247 typsend			24 -1 4  14 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typanalyze		24 -1 4  15 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typalign			18 -1 1  16 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typstorage		18 -1 1  17 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typnotnull		16 -1 1  18 0 -1 -1 t p c t f f t 0));
-DATA(insert ( 1247 typbasetype		26 -1 4  19 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typtypmod		23 -1 4  20 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typndims			23 -1 4  21 0 -1 -1 t p i t f f t 0));
-DATA(insert ( 1247 typdefaultbin	25 -1 -1 22 0 -1 -1 f x i f f f t 0));
-DATA(insert ( 1247 typdefault		25 -1 -1 23 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1247 typmodin			24 -1 4  15 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typmodout		24 -1 4  16 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typanalyze		24 -1 4  17 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typalign			18 -1 1  18 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typstorage		18 -1 1  19 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typnotnull		16 -1 1  20 0 -1 -1 t p c t f f t 0));
+DATA(insert ( 1247 typbasetype		26 -1 4  21 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typtypmod		23 -1 4  22 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typndims			23 -1 4  23 0 -1 -1 t p i t f f t 0));
+DATA(insert ( 1247 typdefaultbin	25 -1 -1 24 0 -1 -1 f x i f f f t 0));
+DATA(insert ( 1247 typdefault		25 -1 -1 25 0 -1 -1 f x i f f f t 0));
 DATA(insert ( 1247 ctid				27 0  6  -1 0 -1 -1 f p s t f f t 0));
 DATA(insert ( 1247 oid				26 0  4  -2 0 -1 -1 t p i t f f t 0));
 DATA(insert ( 1247 xmin				28 0  4  -3 0 -1 -1 t p i t f f t 0));
diff --git a/src/include/catalog/pg_class.h b/src/include/catalog/pg_class.h
index 75aee92e512..155b4c7bc32 100644
--- a/src/include/catalog/pg_class.h
+++ b/src/include/catalog/pg_class.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.97 2006/11/05 22:42:10 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_class.h,v 1.98 2006/12/30 21:21:55 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -132,7 +132,7 @@ typedef FormData_pg_class *Form_pg_class;
  */
 
 /* Note: "3" in the relfrozenxid column stands for FirstNormalTransactionId */
-DATA(insert OID = 1247 (  pg_type		PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 23 0 0 0 0 0 t f f f 3 _null_ _null_ ));
+DATA(insert OID = 1247 (  pg_type		PGNSP 71 PGUID 0 1247 0 0 0 0 0 f f r 25 0 0 0 0 0 t f f f 3   _null_ _null_ ));
 DESCR("");
 DATA(insert OID = 1249 (  pg_attribute	PGNSP 75 PGUID 0 1249 0 0 0 0 0 f f r 17 0 0 0 0 0 f f f f 3 _null_ _null_ ));
 DESCR("");
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b5913007095..f98717a978f 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.433 2006/12/28 14:28:36 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.434 2006/12/30 21:21:55 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -1335,10 +1335,18 @@ DATA(insert OID = 1044 (  bpcharin		   PGNSP PGUID 12 f f t f i 3 1042 "2275 26
 DESCR("I/O");
 DATA(insert OID = 1045 (  bpcharout		   PGNSP PGUID 12 f f t f i 1 2275 "1042" _null_ _null_ _null_	bpcharout - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2913 (  bpchartypmodin   PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	bpchartypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2914 (  bpchartypmodout  PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	bpchartypmodout - _null_ ));
+DESCR("I/O typmod");
 DATA(insert OID = 1046 (  varcharin		   PGNSP PGUID 12 f f t f i 3 1043 "2275 26 23" _null_ _null_ _null_ varcharin - _null_ ));
 DESCR("I/O");
 DATA(insert OID = 1047 (  varcharout	   PGNSP PGUID 12 f f t f i 1 2275 "1043" _null_ _null_ _null_	varcharout - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2915 (  varchartypmodin  PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	varchartypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2916 (  varchartypmodout PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	varchartypmodout - _null_ ));
+DESCR("I/O typmod");
 DATA(insert OID = 1048 (  bpchareq		   PGNSP PGUID 12 f f t f i 2 16 "1042 1042" _null_ _null_ _null_ bpchareq - _null_ ));
 DESCR("equal");
 DATA(insert OID = 1049 (  bpcharlt		   PGNSP PGUID 12 f f t f i 2 16 "1042 1042" _null_ _null_ _null_ bpcharlt - _null_ ));
@@ -1408,6 +1416,10 @@ DATA(insert OID = 1143 (  time_in		   PGNSP PGUID 12 f f t f s 3 1083 "2275 26 2
 DESCR("I/O");
 DATA(insert OID = 1144 (  time_out		   PGNSP PGUID 12 f f t f i 1 2275 "1083" _null_ _null_ _null_	time_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2909 (  timetypmodin   	PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	timetypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2910 (  timetypmodout  	PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	timetypmodout - _null_ ));
+DESCR("I/O typmod");
 DATA(insert OID = 1145 (  time_eq		   PGNSP PGUID 12 f f t f i 2 16 "1083 1083" _null_ _null_ _null_ time_eq - _null_ ));
 DESCR("equal");
 
@@ -1424,6 +1436,10 @@ DATA(insert OID = 1150 (  timestamptz_in   PGNSP PGUID 12 f f t f s 3 1184 "2275
 DESCR("I/O");
 DATA(insert OID = 1151 (  timestamptz_out  PGNSP PGUID 12 f f t f s 1 2275 "1184" _null_ _null_ _null_	timestamptz_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2907 (  timestamptztypmodin   	PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	timestamptztypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2908 (  timestamptztypmodout  	PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	timestamptztypmodout - _null_ ));
+DESCR("I/O typmod");
 DATA(insert OID = 1152 (  timestamptz_eq   PGNSP PGUID 12 f f t f i 2 16 "1184 1184" _null_ _null_ _null_ timestamp_eq - _null_ ));
 DESCR("equal");
 DATA(insert OID = 1153 (  timestamptz_ne   PGNSP PGUID 12 f f t f i 2 16 "1184 1184" _null_ _null_ _null_ timestamp_ne - _null_ ));
@@ -1445,6 +1461,10 @@ DATA(insert OID = 1160 (  interval_in	   PGNSP PGUID 12 f f t f s 3 1186 "2275 2
 DESCR("I/O");
 DATA(insert OID = 1161 (  interval_out	   PGNSP PGUID 12 f f t f i 1 2275 "1186" _null_ _null_ _null_	interval_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2903 (  intervaltypmodin   	PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	intervaltypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2904 (  intervaltypmodout  	PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	intervaltypmodout - _null_ ));
+DESCR("I/O typmod");
 DATA(insert OID = 1162 (  interval_eq	   PGNSP PGUID 12 f f t f i 2 16 "1186 1186" _null_ _null_ _null_ interval_eq - _null_ ));
 DESCR("equal");
 DATA(insert OID = 1163 (  interval_ne	   PGNSP PGUID 12 f f t f i 2 16 "1186 1186" _null_ _null_ _null_ interval_ne - _null_ ));
@@ -1668,6 +1688,10 @@ DATA(insert OID = 1312 (  timestamp_in		 PGNSP PGUID 12 f f t f s 3 1114 "2275 2
 DESCR("I/O");
 DATA(insert OID = 1313 (  timestamp_out		 PGNSP PGUID 12 f f t f s 1 2275 "1114" _null_ _null_ _null_ timestamp_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2905 (  timestamptypmodin   	PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	timestamptypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2906 (  timestamptypmodout  	PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	timestamptypmodout - _null_ ));
+DESCR("I/O typmod");
 DATA(insert OID = 1314 (  timestamptz_cmp	 PGNSP PGUID 12 f f t f i 2 23 "1184 1184" _null_ _null_ _null_ timestamp_cmp - _null_ ));
 DESCR("less-equal-greater");
 DATA(insert OID = 1315 (  interval_cmp		 PGNSP PGUID 12 f f t f i 2 23 "1186 1186" _null_ _null_ _null_ interval_cmp - _null_ ));
@@ -1721,6 +1745,10 @@ DATA(insert OID = 1350 (  timetz_in		   PGNSP PGUID 12 f f t f s 3 1266 "2275 26
 DESCR("I/O");
 DATA(insert OID = 1351 (  timetz_out	   PGNSP PGUID 12 f f t f i 1 2275 "1266" _null_ _null_ _null_	timetz_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2911 (  timetztypmodin   	PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	timetztypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2912 (  timetztypmodout  	PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	timetztypmodout - _null_ ));
+DESCR("I/O typmod");
 DATA(insert OID = 1352 (  timetz_eq		   PGNSP PGUID 12 f f t f i 2 16 "1266 1266" _null_ _null_ _null_ timetz_eq - _null_ ));
 DESCR("equal");
 DATA(insert OID = 1353 (  timetz_ne		   PGNSP PGUID 12 f f t f i 2 16 "1266 1266" _null_ _null_ _null_ timetz_ne - _null_ ));
@@ -2053,6 +2081,10 @@ DATA(insert OID = 1564 (  bit_in			PGNSP PGUID 12 f f t f i 3 1560 "2275 26 23"
 DESCR("I/O");
 DATA(insert OID = 1565 (  bit_out			PGNSP PGUID 12 f f t f i 1 2275 "1560" _null_ _null_ _null_ bit_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2919 (  bittypmodin   	PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	bittypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2920 (  bittypmodout  	PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	bittypmodout - _null_ ));
+DESCR("I/O typmod");
 
 DATA(insert OID = 1569 (  like				PGNSP PGUID 12 f f t f i 2 16 "25 25" _null_ _null_ _null_	textlike - _null_ ));
 DESCR("matches LIKE expression");
@@ -2078,6 +2110,10 @@ DATA(insert OID = 1579 (  varbit_in			PGNSP PGUID 12 f f t f i 3 1562 "2275 26 2
 DESCR("I/O");
 DATA(insert OID = 1580 (  varbit_out		PGNSP PGUID 12 f f t f i 1 2275 "1562" _null_ _null_ _null_ varbit_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2902 (  varbittypmodin   	PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	varbittypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2921 (  varbittypmodout  	PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	varbittypmodout - _null_ ));
+DESCR("I/O typmod");
 
 DATA(insert OID = 1581 (  biteq				PGNSP PGUID 12 f f t f i 2 16 "1560 1560" _null_ _null_ _null_	biteq - _null_ ));
 DESCR("equal");
@@ -2498,6 +2534,10 @@ DATA(insert OID = 1701 ( numeric_in				PGNSP PGUID 12 f f t f i 3 1700 "2275 26
 DESCR("I/O");
 DATA(insert OID = 1702 ( numeric_out			PGNSP PGUID 12 f f t f i 1 2275 "1700" _null_ _null_ _null_ numeric_out - _null_ ));
 DESCR("I/O");
+DATA(insert OID = 2917 (  numerictypmodin   	PGNSP PGUID 12 f f t f i 1 23 "1007" _null_ _null_ _null_	numerictypmodin - _null_ ));
+DESCR("I/O typmod");
+DATA(insert OID = 2918 (  numerictypmodout  	PGNSP PGUID 12 f f t f i 1 2275 "23" _null_ _null_ _null_	numerictypmodout - _null_ ));
+DESCR("I/O typmod");
 DATA(insert OID = 1703 ( numeric				PGNSP PGUID 12 f f t f i 2 1700 "1700 23" _null_ _null_ _null_	numeric - _null_ ));
 DESCR("adjust numeric to typmod precision/scale");
 DATA(insert OID = 1704 ( numeric_abs			PGNSP PGUID 12 f f t f i 1 1700 "1700" _null_ _null_ _null_ numeric_abs - _null_ ));
diff --git a/src/include/catalog/pg_type.h b/src/include/catalog/pg_type.h
index 9a95ef305ba..65f8b9deea8 100644
--- a/src/include/catalog/pg_type.h
+++ b/src/include/catalog/pg_type.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.175 2006/12/28 14:28:36 petere Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_type.h,v 1.176 2006/12/30 21:21:55 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -104,6 +104,12 @@ CATALOG(pg_type,1247) BKI_BOOTSTRAP
 	regproc		typreceive;		/* binary format (optional) */
 	regproc		typsend;
 
+	/*
+	 * I/O functions for optional type modifiers.
+	 */
+	 regproc	typmodin;
+	 regproc	typmodout;
+
 	/*
 	 * Custom ANALYZE procedure for the datatype (0 selects the default).
 	 */
@@ -205,7 +211,7 @@ typedef FormData_pg_type *Form_pg_type;
  *		compiler constants for pg_type
  * ----------------
  */
-#define Natts_pg_type					23
+#define Natts_pg_type					25
 #define Anum_pg_type_typname			1
 #define Anum_pg_type_typnamespace		2
 #define Anum_pg_type_typowner			3
@@ -220,15 +226,17 @@ typedef FormData_pg_type *Form_pg_type;
 #define Anum_pg_type_typoutput			12
 #define Anum_pg_type_typreceive			13
 #define Anum_pg_type_typsend			14
-#define Anum_pg_type_typanalyze			15
-#define Anum_pg_type_typalign			16
-#define Anum_pg_type_typstorage			17
-#define Anum_pg_type_typnotnull			18
-#define Anum_pg_type_typbasetype		19
-#define Anum_pg_type_typtypmod			20
-#define Anum_pg_type_typndims			21
-#define Anum_pg_type_typdefaultbin		22
-#define Anum_pg_type_typdefault			23
+#define Anum_pg_type_typmodin			15
+#define Anum_pg_type_typmodout			16
+#define Anum_pg_type_typanalyze			17
+#define Anum_pg_type_typalign			18
+#define Anum_pg_type_typstorage			19
+#define Anum_pg_type_typnotnull			20
+#define Anum_pg_type_typbasetype		21
+#define Anum_pg_type_typtypmod			22
+#define Anum_pg_type_typndims			23
+#define Anum_pg_type_typdefaultbin		24
+#define Anum_pg_type_typdefault			25
 
 
 /* ----------------
@@ -244,86 +252,86 @@ typedef FormData_pg_type *Form_pg_type;
 */
 
 /* OIDS 1 - 99 */
-DATA(insert OID = 16 (	bool	   PGNSP PGUID	1 t b t \054 0	 0 boolin boolout boolrecv boolsend - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 16 (	bool	   PGNSP PGUID	1 t b t \054 0	 0 boolin boolout boolrecv boolsend - - - c p f 0 -1 0 _null_ _null_ ));
 DESCR("boolean, 'true'/'false'");
 #define BOOLOID			16
 
-DATA(insert OID = 17 (	bytea	   PGNSP PGUID -1 f b t \054 0	0 byteain byteaout bytearecv byteasend - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 17 (	bytea	   PGNSP PGUID -1 f b t \054 0	0 byteain byteaout bytearecv byteasend - - - i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length string, binary values escaped");
 #define BYTEAOID		17
 
-DATA(insert OID = 18 (	char	   PGNSP PGUID	1 t b t \054 0	 0 charin charout charrecv charsend - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 18 (	char	   PGNSP PGUID	1 t b t \054 0	 0 charin charout charrecv charsend - - - c p f 0 -1 0 _null_ _null_ ));
 DESCR("single character");
 #define CHAROID			18
 
-DATA(insert OID = 19 (	name	   PGNSP PGUID NAMEDATALEN f b t \054 0 18 namein nameout namerecv namesend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 19 (	name	   PGNSP PGUID NAMEDATALEN f b t \054 0 18 namein nameout namerecv namesend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("63-character type for storing system identifiers");
 #define NAMEOID			19
 
-DATA(insert OID = 20 (	int8	   PGNSP PGUID	8 f b t \054 0	 0 int8in int8out int8recv int8send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 20 (	int8	   PGNSP PGUID	8 f b t \054 0	 0 int8in int8out int8recv int8send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("~18 digit integer, 8-byte storage");
 #define INT8OID			20
 
-DATA(insert OID = 21 (	int2	   PGNSP PGUID	2 t b t \054 0	 0 int2in int2out int2recv int2send - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 21 (	int2	   PGNSP PGUID	2 t b t \054 0	 0 int2in int2out int2recv int2send - - - s p f 0 -1 0 _null_ _null_ ));
 DESCR("-32 thousand to 32 thousand, 2-byte storage");
 #define INT2OID			21
 
-DATA(insert OID = 22 (	int2vector PGNSP PGUID -1 f b t \054 0	21 int2vectorin int2vectorout int2vectorrecv int2vectorsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 22 (	int2vector PGNSP PGUID -1 f b t \054 0	21 int2vectorin int2vectorout int2vectorrecv int2vectorsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("array of int2, used in system tables");
 #define INT2VECTOROID	22
 
-DATA(insert OID = 23 (	int4	   PGNSP PGUID	4 t b t \054 0	 0 int4in int4out int4recv int4send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 23 (	int4	   PGNSP PGUID	4 t b t \054 0	 0 int4in int4out int4recv int4send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("-2 billion to 2 billion integer, 4-byte storage");
 #define INT4OID			23
 
-DATA(insert OID = 24 (	regproc    PGNSP PGUID	4 t b t \054 0	 0 regprocin regprocout regprocrecv regprocsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 24 (	regproc    PGNSP PGUID	4 t b t \054 0	 0 regprocin regprocout regprocrecv regprocsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered procedure");
 #define REGPROCOID		24
 
-DATA(insert OID = 25 (	text	   PGNSP PGUID -1 f b t \054 0	0 textin textout textrecv textsend - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 25 (	text	   PGNSP PGUID -1 f b t \054 0	0 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length string, no limit specified");
 #define TEXTOID			25
 
-DATA(insert OID = 26 (	oid		   PGNSP PGUID	4 t b t \054 0	 0 oidin oidout oidrecv oidsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 26 (	oid		   PGNSP PGUID	4 t b t \054 0	 0 oidin oidout oidrecv oidsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("object identifier(oid), maximum 4 billion");
 #define OIDOID			26
 
-DATA(insert OID = 27 (	tid		   PGNSP PGUID	6 f b t \054 0	 0 tidin tidout tidrecv tidsend - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 27 (	tid		   PGNSP PGUID	6 f b t \054 0	 0 tidin tidout tidrecv tidsend - - - s p f 0 -1 0 _null_ _null_ ));
 DESCR("(Block, offset), physical location of tuple");
 #define TIDOID		27
 
-DATA(insert OID = 28 (	xid		   PGNSP PGUID	4 t b t \054 0	 0 xidin xidout xidrecv xidsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 28 (	xid		   PGNSP PGUID	4 t b t \054 0	 0 xidin xidout xidrecv xidsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("transaction id");
 #define XIDOID 28
 
-DATA(insert OID = 29 (	cid		   PGNSP PGUID	4 t b t \054 0	 0 cidin cidout cidrecv cidsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 29 (	cid		   PGNSP PGUID	4 t b t \054 0	 0 cidin cidout cidrecv cidsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("command identifier type, sequence in transaction id");
 #define CIDOID 29
 
-DATA(insert OID = 30 (	oidvector  PGNSP PGUID -1 f b t \054 0	26 oidvectorin oidvectorout oidvectorrecv oidvectorsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 30 (	oidvector  PGNSP PGUID -1 f b t \054 0	26 oidvectorin oidvectorout oidvectorrecv oidvectorsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("array of oids, used in system tables");
 #define OIDVECTOROID	30
 
 /* hand-built rowtype entries for bootstrapped catalogs: */
 
-DATA(insert OID = 71 (	pg_type			PGNSP PGUID -1 f c t \054 1247 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 71 (	pg_type			PGNSP PGUID -1 f c t \054 1247 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define PG_TYPE_RELTYPE_OID 71
-DATA(insert OID = 75 (	pg_attribute	PGNSP PGUID -1 f c t \054 1249 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 75 (	pg_attribute	PGNSP PGUID -1 f c t \054 1249 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define PG_ATTRIBUTE_RELTYPE_OID 75
-DATA(insert OID = 81 (	pg_proc			PGNSP PGUID -1 f c t \054 1255 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 81 (	pg_proc			PGNSP PGUID -1 f c t \054 1255 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define PG_PROC_RELTYPE_OID 81
-DATA(insert OID = 83 (	pg_class		PGNSP PGUID -1 f c t \054 1259 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 83 (	pg_class		PGNSP PGUID -1 f c t \054 1259 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define PG_CLASS_RELTYPE_OID 83
 
 /* OIDS 100 - 199 */
-DATA(insert OID = 142 ( xml		   PGNSP PGUID -1 f b t \054 0 0 xml_in xml_out xml_recv xml_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 142 ( xml		   PGNSP PGUID -1 f b t \054 0 0 xml_in xml_out xml_recv xml_send - - - i x f 0 -1 0 _null_ _null_ ));
 DESCR("XML content");
 #define XMLOID 142
-DATA(insert OID = 143 ( _xml	   PGNSP PGUID -1 f b t \054 0 142 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 143 ( _xml	   PGNSP PGUID -1 f b t \054 0 142 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 200 - 299 */
 
-DATA(insert OID = 210 (  smgr	   PGNSP PGUID 2 t b t \054 0 0 smgrin smgrout - - - s p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 210 (  smgr	   PGNSP PGUID 2 t b t \054 0 0 smgrin smgrout - - - - - s p f 0 -1 0 _null_ _null_ ));
 DESCR("storage manager");
 
 /* OIDS 300 - 399 */
@@ -333,194 +341,194 @@ DESCR("storage manager");
 /* OIDS 500 - 599 */
 
 /* OIDS 600 - 699 */
-DATA(insert OID = 600 (  point	   PGNSP PGUID 16 f b t \054 0 701 point_in point_out point_recv point_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 600 (  point	   PGNSP PGUID 16 f b t \054 0 701 point_in point_out point_recv point_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric point '(x, y)'");
 #define POINTOID		600
-DATA(insert OID = 601 (  lseg	   PGNSP PGUID 32 f b t \054 0 600 lseg_in lseg_out lseg_recv lseg_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 601 (  lseg	   PGNSP PGUID 32 f b t \054 0 600 lseg_in lseg_out lseg_recv lseg_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric line segment '(pt1,pt2)'");
 #define LSEGOID			601
-DATA(insert OID = 602 (  path	   PGNSP PGUID -1 f b t \054 0 0 path_in path_out path_recv path_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 602 (  path	   PGNSP PGUID -1 f b t \054 0 0 path_in path_out path_recv path_send - - - d x f 0 -1 0 _null_ _null_ ));
 DESCR("geometric path '(pt1,...)'");
 #define PATHOID			602
-DATA(insert OID = 603 (  box	   PGNSP PGUID 32 f b t \073 0 600 box_in box_out box_recv box_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 603 (  box	   PGNSP PGUID 32 f b t \073 0 600 box_in box_out box_recv box_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric box '(lower left,upper right)'");
 #define BOXOID			603
-DATA(insert OID = 604 (  polygon   PGNSP PGUID -1 f b t \054 0	 0 poly_in poly_out poly_recv poly_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 604 (  polygon   PGNSP PGUID -1 f b t \054 0	 0 poly_in poly_out poly_recv poly_send - - - d x f 0 -1 0 _null_ _null_ ));
 DESCR("geometric polygon '(pt1,...)'");
 #define POLYGONOID		604
 
-DATA(insert OID = 628 (  line	   PGNSP PGUID 32 f b t \054 0 701 line_in line_out line_recv line_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 628 (  line	   PGNSP PGUID 32 f b t \054 0 701 line_in line_out line_recv line_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric line (not implemented)'");
 #define LINEOID			628
-DATA(insert OID = 629 (  _line	   PGNSP PGUID	-1 f b t \054 0 628 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 629 (  _line	   PGNSP PGUID	-1 f b t \054 0 628 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
 DESCR("");
 
 /* OIDS 700 - 799 */
 
-DATA(insert OID = 700 (  float4    PGNSP PGUID	4 f b t \054 0	 0 float4in float4out float4recv float4send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 700 (  float4    PGNSP PGUID	4 f b t \054 0	 0 float4in float4out float4recv float4send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("single-precision floating point number, 4-byte storage");
 #define FLOAT4OID 700
-DATA(insert OID = 701 (  float8    PGNSP PGUID	8 f b t \054 0	 0 float8in float8out float8recv float8send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 701 (  float8    PGNSP PGUID	8 f b t \054 0	 0 float8in float8out float8recv float8send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("double-precision floating point number, 8-byte storage");
 #define FLOAT8OID 701
-DATA(insert OID = 702 (  abstime   PGNSP PGUID	4 t b t \054 0	 0 abstimein abstimeout abstimerecv abstimesend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 702 (  abstime   PGNSP PGUID	4 t b t \054 0	 0 abstimein abstimeout abstimerecv abstimesend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("absolute, limited-range date and time (Unix system time)");
 #define ABSTIMEOID		702
-DATA(insert OID = 703 (  reltime   PGNSP PGUID	4 t b t \054 0	 0 reltimein reltimeout reltimerecv reltimesend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 703 (  reltime   PGNSP PGUID	4 t b t \054 0	 0 reltimein reltimeout reltimerecv reltimesend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("relative, limited-range time interval (Unix delta time)");
 #define RELTIMEOID		703
-DATA(insert OID = 704 (  tinterval PGNSP PGUID 12 f b t \054 0	 0 tintervalin tintervalout tintervalrecv tintervalsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 704 (  tinterval PGNSP PGUID 12 f b t \054 0	 0 tintervalin tintervalout tintervalrecv tintervalsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("(abstime,abstime), time interval");
 #define TINTERVALOID	704
-DATA(insert OID = 705 (  unknown   PGNSP PGUID -2 f b t \054 0	 0 unknownin unknownout unknownrecv unknownsend - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 705 (  unknown   PGNSP PGUID -2 f b t \054 0	 0 unknownin unknownout unknownrecv unknownsend - - - c p f 0 -1 0 _null_ _null_ ));
 DESCR("");
 #define UNKNOWNOID		705
 
-DATA(insert OID = 718 (  circle    PGNSP PGUID	24 f b t \054 0 0 circle_in circle_out circle_recv circle_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 718 (  circle    PGNSP PGUID	24 f b t \054 0 0 circle_in circle_out circle_recv circle_send - - - d p f 0 -1 0 _null_ _null_ ));
 DESCR("geometric circle '(center,radius)'");
 #define CIRCLEOID		718
-DATA(insert OID = 719 (  _circle   PGNSP PGUID	-1 f b t \054 0  718 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 790 (  money	   PGNSP PGUID	 4 f b t \054 0 0 cash_in cash_out cash_recv cash_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 719 (  _circle   PGNSP PGUID	-1 f b t \054 0  718 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 790 (  money	   PGNSP PGUID	 4 f b t \054 0 0 cash_in cash_out cash_recv cash_send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("monetary amounts, $d,ddd.cc");
 #define CASHOID 790
-DATA(insert OID = 791 (  _money    PGNSP PGUID	-1 f b t \054 0  790 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 791 (  _money    PGNSP PGUID	-1 f b t \054 0  790 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 800 - 899 */
-DATA(insert OID = 829 ( macaddr    PGNSP PGUID	6 f b t \054 0 0 macaddr_in macaddr_out macaddr_recv macaddr_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 829 ( macaddr    PGNSP PGUID	6 f b t \054 0 0 macaddr_in macaddr_out macaddr_recv macaddr_send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("XX:XX:XX:XX:XX:XX, MAC address");
 #define MACADDROID 829
-DATA(insert OID = 869 ( inet	   PGNSP PGUID	-1 f b t \054 0 0 inet_in inet_out inet_recv inet_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 869 ( inet	   PGNSP PGUID	-1 f b t \054 0 0 inet_in inet_out inet_recv inet_send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("IP address/netmask, host address, netmask optional");
 #define INETOID 869
-DATA(insert OID = 650 ( cidr	   PGNSP PGUID	-1 f b t \054 0 0 cidr_in cidr_out cidr_recv cidr_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 650 ( cidr	   PGNSP PGUID	-1 f b t \054 0 0 cidr_in cidr_out cidr_recv cidr_send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("network IP address/netmask, network address");
 #define CIDROID 650
 
 /* OIDS 900 - 999 */
 
 /* OIDS 1000 - 1099 */
-DATA(insert OID = 1000 (  _bool		 PGNSP PGUID -1 f b t \054 0	16 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1001 (  _bytea	 PGNSP PGUID -1 f b t \054 0	17 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1002 (  _char		 PGNSP PGUID -1 f b t \054 0	18 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1003 (  _name		 PGNSP PGUID -1 f b t \054 0	19 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1005 (  _int2		 PGNSP PGUID -1 f b t \054 0	21 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1006 (  _int2vector PGNSP PGUID -1 f b t \054 0	22 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1007 (  _int4		 PGNSP PGUID -1 f b t \054 0	23 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1000 (  _bool		 PGNSP PGUID -1 f b t \054 0	16 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1001 (  _bytea	 PGNSP PGUID -1 f b t \054 0	17 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1002 (  _char		 PGNSP PGUID -1 f b t \054 0	18 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1003 (  _name		 PGNSP PGUID -1 f b t \054 0	19 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1005 (  _int2		 PGNSP PGUID -1 f b t \054 0	21 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1006 (  _int2vector PGNSP PGUID -1 f b t \054 0	22 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1007 (  _int4		 PGNSP PGUID -1 f b t \054 0	23 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 #define INT4ARRAYOID		1007
-DATA(insert OID = 1008 (  _regproc	 PGNSP PGUID -1 f b t \054 0	24 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1009 (  _text		 PGNSP PGUID -1 f b t \054 0	25 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1028 (  _oid		 PGNSP PGUID -1 f b t \054 0	26 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1010 (  _tid		 PGNSP PGUID -1 f b t \054 0	27 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1011 (  _xid		 PGNSP PGUID -1 f b t \054 0	28 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1012 (  _cid		 PGNSP PGUID -1 f b t \054 0	29 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1013 (  _oidvector PGNSP PGUID -1 f b t \054 0	30 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1014 (  _bpchar	 PGNSP PGUID -1 f b t \054 0 1042 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1015 (  _varchar	 PGNSP PGUID -1 f b t \054 0 1043 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1016 (  _int8		 PGNSP PGUID -1 f b t \054 0	20 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1017 (  _point	 PGNSP PGUID -1 f b t \054 0 600 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1018 (  _lseg		 PGNSP PGUID -1 f b t \054 0 601 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1019 (  _path		 PGNSP PGUID -1 f b t \054 0 602 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1020 (  _box		 PGNSP PGUID -1 f b t \073 0 603 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1021 (  _float4	 PGNSP PGUID -1 f b t \054 0 700 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1008 (  _regproc	 PGNSP PGUID -1 f b t \054 0	24 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1009 (  _text		 PGNSP PGUID -1 f b t \054 0	25 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1028 (  _oid		 PGNSP PGUID -1 f b t \054 0	26 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1010 (  _tid		 PGNSP PGUID -1 f b t \054 0	27 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1011 (  _xid		 PGNSP PGUID -1 f b t \054 0	28 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1012 (  _cid		 PGNSP PGUID -1 f b t \054 0	29 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1013 (  _oidvector PGNSP PGUID -1 f b t \054 0	30 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1014 (  _bpchar	 PGNSP PGUID -1 f b t \054 0 1042 array_in array_out array_recv array_send bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1015 (  _varchar	 PGNSP PGUID -1 f b t \054 0 1043 array_in array_out array_recv array_send varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1016 (  _int8		 PGNSP PGUID -1 f b t \054 0	20 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1017 (  _point	 PGNSP PGUID -1 f b t \054 0 600 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1018 (  _lseg		 PGNSP PGUID -1 f b t \054 0 601 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1019 (  _path		 PGNSP PGUID -1 f b t \054 0 602 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1020 (  _box		 PGNSP PGUID -1 f b t \073 0 603 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1021 (  _float4	 PGNSP PGUID -1 f b t \054 0 700 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 #define FLOAT4ARRAYOID 1021
-DATA(insert OID = 1022 (  _float8	 PGNSP PGUID -1 f b t \054 0 701 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1023 (  _abstime	 PGNSP PGUID -1 f b t \054 0 702 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1024 (  _reltime	 PGNSP PGUID -1 f b t \054 0 703 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1025 (  _tinterval PGNSP PGUID -1 f b t \054 0 704 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1027 (  _polygon	 PGNSP PGUID -1 f b t \054 0 604 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1033 (  aclitem	 PGNSP PGUID 12 f b t \054 0 0 aclitemin aclitemout - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1022 (  _float8	 PGNSP PGUID -1 f b t \054 0 701 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1023 (  _abstime	 PGNSP PGUID -1 f b t \054 0 702 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1024 (  _reltime	 PGNSP PGUID -1 f b t \054 0 703 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1025 (  _tinterval PGNSP PGUID -1 f b t \054 0 704 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1027 (  _polygon	 PGNSP PGUID -1 f b t \054 0 604 array_in array_out array_recv array_send - - - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1033 (  aclitem	 PGNSP PGUID 12 f b t \054 0 0 aclitemin aclitemout - - - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("access control list");
 #define ACLITEMOID		1033
-DATA(insert OID = 1034 (  _aclitem	 PGNSP PGUID -1 f b t \054 0 1033 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1040 (  _macaddr	 PGNSP PGUID -1 f b t \054 0  829 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1041 (  _inet    PGNSP PGUID -1 f b t \054 0	869 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 651  (  _cidr    PGNSP PGUID -1 f b t \054 0	650 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1042 ( bpchar		 PGNSP PGUID -1 f b t \054 0	0 bpcharin bpcharout bpcharrecv bpcharsend - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1034 (  _aclitem	 PGNSP PGUID -1 f b t \054 0 1033 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1040 (  _macaddr	 PGNSP PGUID -1 f b t \054 0  829 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1041 (  _inet    PGNSP PGUID -1 f b t \054 0	869 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 651  (  _cidr    PGNSP PGUID -1 f b t \054 0	650 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1042 ( bpchar		 PGNSP PGUID -1 f b t \054 0	0 bpcharin bpcharout bpcharrecv bpcharsend bpchartypmodin bpchartypmodout - i x f 0 -1 0 _null_ _null_ ));
 DESCR("char(length), blank-padded string, fixed storage length");
 #define BPCHAROID		1042
-DATA(insert OID = 1043 ( varchar	 PGNSP PGUID -1 f b t \054 0	0 varcharin varcharout varcharrecv varcharsend - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1043 ( varchar	 PGNSP PGUID -1 f b t \054 0	0 varcharin varcharout varcharrecv varcharsend varchartypmodin varchartypmodout - i x f 0 -1 0 _null_ _null_ ));
 DESCR("varchar(length), non-blank-padded string, variable storage length");
 #define VARCHAROID		1043
 
-DATA(insert OID = 1082 ( date		 PGNSP PGUID	4 t b t \054 0	0 date_in date_out date_recv date_send - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1082 ( date		 PGNSP PGUID	4 t b t \054 0	0 date_in date_out date_recv date_send - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("ANSI SQL date");
 #define DATEOID			1082
-DATA(insert OID = 1083 ( time		 PGNSP PGUID	8 f b t \054 0	0 time_in time_out time_recv time_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1083 ( time		 PGNSP PGUID	8 f b t \054 0	0 time_in time_out time_recv time_send timetypmodin timetypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("hh:mm:ss, ANSI SQL time");
 #define TIMEOID			1083
 
 /* OIDS 1100 - 1199 */
-DATA(insert OID = 1114 ( timestamp	 PGNSP PGUID	8 f b t \054 0	0 timestamp_in timestamp_out timestamp_recv timestamp_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1114 ( timestamp	 PGNSP PGUID	8 f b t \054 0	0 timestamp_in timestamp_out timestamp_recv timestamp_send timestamptypmodin timestamptypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("date and time");
 #define TIMESTAMPOID	1114
-DATA(insert OID = 1115 ( _timestamp  PGNSP PGUID	-1 f b t \054 0 1114 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1182 ( _date		 PGNSP PGUID	-1 f b t \054 0 1082 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1183 ( _time		 PGNSP PGUID	-1 f b t \054 0 1083 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1184 ( timestamptz PGNSP PGUID	8 f b t \054 0	0 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1115 ( _timestamp  PGNSP PGUID	-1 f b t \054 0 1114 array_in array_out array_recv array_send timestamptypmodin timestamptypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1182 ( _date		 PGNSP PGUID	-1 f b t \054 0 1082 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1183 ( _time		 PGNSP PGUID	-1 f b t \054 0 1083 array_in array_out array_recv array_send timetypmodin timetypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1184 ( timestamptz PGNSP PGUID	8 f b t \054 0	0 timestamptz_in timestamptz_out timestamptz_recv timestamptz_send timestamptztypmodin timestamptztypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("date and time with time zone");
 #define TIMESTAMPTZOID	1184
-DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b t \054 0	1184 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1186 ( interval	 PGNSP PGUID 16 f b t \054 0	0 interval_in interval_out interval_recv interval_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1185 ( _timestamptz PGNSP PGUID -1 f b t \054 0	1184 array_in array_out array_recv array_send timestamptztypmodin timestamptztypmodout - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1186 ( interval	 PGNSP PGUID 16 f b t \054 0	0 interval_in interval_out interval_recv interval_send intervaltypmodin intervaltypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("@ <number> <units>, time interval");
 #define INTERVALOID		1186
-DATA(insert OID = 1187 ( _interval	 PGNSP PGUID	-1 f b t \054 0 1186 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1187 ( _interval	 PGNSP PGUID	-1 f b t \054 0 1186 array_in array_out array_recv array_send intervaltypmodin intervaltypmodout - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1200 - 1299 */
-DATA(insert OID = 1231 (  _numeric	 PGNSP PGUID -1 f b t \054 0	1700 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1266 ( timetz		 PGNSP PGUID 12 f b t \054 0	0 timetz_in timetz_out timetz_recv timetz_send - d p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1231 (  _numeric	 PGNSP PGUID -1 f b t \054 0	1700 array_in array_out array_recv array_send numerictypmodin numerictypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1266 ( timetz		 PGNSP PGUID 12 f b t \054 0	0 timetz_in timetz_out timetz_recv timetz_send timetztypmodin timetztypmodout - d p f 0 -1 0 _null_ _null_ ));
 DESCR("hh:mm:ss, ANSI SQL time");
 #define TIMETZOID		1266
-DATA(insert OID = 1270 ( _timetz	 PGNSP PGUID -1 f b t \054 0	1266 array_in array_out array_recv array_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1270 ( _timetz	 PGNSP PGUID -1 f b t \054 0	1266 array_in array_out array_recv array_send timetztypmodin timetztypmodout - d x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1500 - 1599 */
-DATA(insert OID = 1560 ( bit		 PGNSP PGUID -1 f b t \054 0	0 bit_in bit_out bit_recv bit_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1560 ( bit		 PGNSP PGUID -1 f b t \054 0	0 bit_in bit_out bit_recv bit_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
 DESCR("fixed-length bit string");
 #define BITOID	 1560
-DATA(insert OID = 1561 ( _bit		 PGNSP PGUID -1 f b t \054 0	1560 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 1562 ( varbit		 PGNSP PGUID -1 f b t \054 0	0 varbit_in varbit_out varbit_recv varbit_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1561 ( _bit		 PGNSP PGUID -1 f b t \054 0	1560 array_in array_out array_recv array_send bittypmodin bittypmodout - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1562 ( varbit		 PGNSP PGUID -1 f b t \054 0	0 varbit_in varbit_out varbit_recv varbit_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
 DESCR("variable-length bit string");
 #define VARBITOID	  1562
-DATA(insert OID = 1563 ( _varbit	 PGNSP PGUID -1 f b t \054 0	1562 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1563 ( _varbit	 PGNSP PGUID -1 f b t \054 0	1562 array_in array_out array_recv array_send varbittypmodin varbittypmodout - i x f 0 -1 0 _null_ _null_ ));
 
 /* OIDS 1600 - 1699 */
 
 /* OIDS 1700 - 1799 */
-DATA(insert OID = 1700 ( numeric	   PGNSP PGUID -1 f b t \054 0	0 numeric_in numeric_out numeric_recv numeric_send - i m f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1700 ( numeric	   PGNSP PGUID -1 f b t \054 0	0 numeric_in numeric_out numeric_recv numeric_send numerictypmodin numerictypmodout - i m f 0 -1 0 _null_ _null_ ));
 DESCR("numeric(precision, decimal), arbitrary precision number");
 #define NUMERICOID		1700
 
-DATA(insert OID = 1790 ( refcursor	   PGNSP PGUID -1 f b t \054 0	0 textin textout textrecv textsend - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 1790 ( refcursor	   PGNSP PGUID -1 f b t \054 0	0 textin textout textrecv textsend - - - i x f 0 -1 0 _null_ _null_ ));
 DESCR("reference cursor (portal name)");
 #define REFCURSOROID	1790
 
 /* OIDS 2200 - 2299 */
-DATA(insert OID = 2201 ( _refcursor    PGNSP PGUID -1 f b t \054 0 1790 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2201 ( _refcursor    PGNSP PGUID -1 f b t \054 0 1790 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 
-DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID	4 t b t \054 0	 0 regprocedurein regprocedureout regprocedurerecv regproceduresend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2202 ( regprocedure  PGNSP PGUID	4 t b t \054 0	 0 regprocedurein regprocedureout regprocedurerecv regproceduresend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered procedure (with args)");
 #define REGPROCEDUREOID 2202
 
-DATA(insert OID = 2203 ( regoper	   PGNSP PGUID	4 t b t \054 0	 0 regoperin regoperout regoperrecv regopersend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2203 ( regoper	   PGNSP PGUID	4 t b t \054 0	 0 regoperin regoperout regoperrecv regopersend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered operator");
 #define REGOPEROID		2203
 
-DATA(insert OID = 2204 ( regoperator   PGNSP PGUID	4 t b t \054 0	 0 regoperatorin regoperatorout regoperatorrecv regoperatorsend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2204 ( regoperator   PGNSP PGUID	4 t b t \054 0	 0 regoperatorin regoperatorout regoperatorrecv regoperatorsend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered operator (with args)");
 #define REGOPERATOROID	2204
 
-DATA(insert OID = 2205 ( regclass	   PGNSP PGUID	4 t b t \054 0	 0 regclassin regclassout regclassrecv regclasssend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2205 ( regclass	   PGNSP PGUID	4 t b t \054 0	 0 regclassin regclassout regclassrecv regclasssend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered class");
 #define REGCLASSOID		2205
 
-DATA(insert OID = 2206 ( regtype	   PGNSP PGUID	4 t b t \054 0	 0 regtypein regtypeout regtyperecv regtypesend - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2206 ( regtype	   PGNSP PGUID	4 t b t \054 0	 0 regtypein regtypeout regtyperecv regtypesend - - - i p f 0 -1 0 _null_ _null_ ));
 DESCR("registered type");
 #define REGTYPEOID		2206
 
-DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b t \054 0 2202 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2208 ( _regoper	   PGNSP PGUID -1 f b t \054 0 2203 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1 f b t \054 0 2204 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2210 ( _regclass	   PGNSP PGUID -1 f b t \054 0 2205 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
-DATA(insert OID = 2211 ( _regtype	   PGNSP PGUID -1 f b t \054 0 2206 array_in array_out array_recv array_send - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2207 ( _regprocedure PGNSP PGUID -1 f b t \054 0 2202 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2208 ( _regoper	   PGNSP PGUID -1 f b t \054 0 2203 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2209 ( _regoperator  PGNSP PGUID -1 f b t \054 0 2204 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2210 ( _regclass	   PGNSP PGUID -1 f b t \054 0 2205 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2211 ( _regtype	   PGNSP PGUID -1 f b t \054 0 2206 array_in array_out array_recv array_send - - - i x f 0 -1 0 _null_ _null_ ));
 #define REGTYPEARRAYOID 2211
 
 /*
@@ -532,25 +540,25 @@ DATA(insert OID = 2211 ( _regtype	   PGNSP PGUID -1 f b t \054 0 2206 array_in a
  * argument and result types (if supported by the function's implementation
  * language).
  */
-DATA(insert OID = 2249 ( record			PGNSP PGUID -1 f p t \054 0 0 record_in record_out record_recv record_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2249 ( record			PGNSP PGUID -1 f p t \054 0 0 record_in record_out record_recv record_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define RECORDOID		2249
-DATA(insert OID = 2275 ( cstring		PGNSP PGUID -2 f p t \054 0 0 cstring_in cstring_out cstring_recv cstring_send - c p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2275 ( cstring		PGNSP PGUID -2 f p t \054 0 0 cstring_in cstring_out cstring_recv cstring_send - - - c p f 0 -1 0 _null_ _null_ ));
 #define CSTRINGOID		2275
-DATA(insert OID = 2276 ( any			PGNSP PGUID  4 t p t \054 0 0 any_in any_out - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2276 ( any			PGNSP PGUID  4 t p t \054 0 0 any_in any_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYOID			2276
-DATA(insert OID = 2277 ( anyarray		PGNSP PGUID -1 f p t \054 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - d x f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2277 ( anyarray		PGNSP PGUID -1 f p t \054 0 0 anyarray_in anyarray_out anyarray_recv anyarray_send - - - d x f 0 -1 0 _null_ _null_ ));
 #define ANYARRAYOID		2277
-DATA(insert OID = 2278 ( void			PGNSP PGUID  4 t p t \054 0 0 void_in void_out - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2278 ( void			PGNSP PGUID  4 t p t \054 0 0 void_in void_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define VOIDOID			2278
-DATA(insert OID = 2279 ( trigger		PGNSP PGUID  4 t p t \054 0 0 trigger_in trigger_out - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2279 ( trigger		PGNSP PGUID  4 t p t \054 0 0 trigger_in trigger_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define TRIGGEROID		2279
-DATA(insert OID = 2280 ( language_handler	PGNSP PGUID  4 t p t \054 0 0 language_handler_in language_handler_out - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2280 ( language_handler	PGNSP PGUID  4 t p t \054 0 0 language_handler_in language_handler_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define LANGUAGE_HANDLEROID		2280
-DATA(insert OID = 2281 ( internal		PGNSP PGUID  4 t p t \054 0 0 internal_in internal_out - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2281 ( internal		PGNSP PGUID  4 t p t \054 0 0 internal_in internal_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define INTERNALOID		2281
-DATA(insert OID = 2282 ( opaque			PGNSP PGUID  4 t p t \054 0 0 opaque_in opaque_out - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2282 ( opaque			PGNSP PGUID  4 t p t \054 0 0 opaque_in opaque_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define OPAQUEOID		2282
-DATA(insert OID = 2283 ( anyelement		PGNSP PGUID  4 t p t \054 0 0 anyelement_in anyelement_out - - - i p f 0 -1 0 _null_ _null_ ));
+DATA(insert OID = 2283 ( anyelement		PGNSP PGUID  4 t p t \054 0 0 anyelement_in anyelement_out - - - - - i p f 0 -1 0 _null_ _null_ ));
 #define ANYELEMENTOID	2283
 
 /*
@@ -569,6 +577,8 @@ extern Oid TypeCreate(const char *typeName,
 		   Oid outputProcedure,
 		   Oid receiveProcedure,
 		   Oid sendProcedure,
+		   Oid typmodinProcedure,
+		   Oid typmodoutProcedure,
 		   Oid analyzeProcedure,
 		   Oid elementType,
 		   Oid baseType,
@@ -590,6 +600,8 @@ extern void GenerateTypeDependencies(Oid typeNamespace,
 						 Oid outputProcedure,
 						 Oid receiveProcedure,
 						 Oid sendProcedure,
+		   				 Oid typmodinProcedure,
+		   				 Oid typmodoutProcedure,
 						 Oid analyzeProcedure,
 						 Oid elementType,
 						 Oid baseType,
diff --git a/src/include/nodes/parsenodes.h b/src/include/nodes/parsenodes.h
index bc62c90f4dc..5d7fa50673c 100644
--- a/src/include/nodes/parsenodes.h
+++ b/src/include/nodes/parsenodes.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.335 2006/12/23 00:43:12 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/nodes/parsenodes.h,v 1.336 2006/12/30 21:21:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -167,6 +167,8 @@ typedef struct Query
  * For TypeName structures generated internally, it is often easier to
  * specify the type by OID than by name.  If "names" is NIL then the
  * actual type OID is given by typeid, otherwise typeid is unused.
+ * Similarly, if "typmods" is NIL then the actual typmod is expected to
+ * be prespecified in typemod, otherwise typemod is unused.
  *
  * If pct_type is TRUE, then names is actually a field name and we look up
  * the type of that field.	Otherwise (the normal case), names is a type
@@ -180,7 +182,8 @@ typedef struct TypeName
 	bool		timezone;		/* timezone specified? */
 	bool		setof;			/* is a set? */
 	bool		pct_type;		/* %TYPE specified? */
-	int32		typmod;			/* type modifier */
+	List	   *typmods;		/* type modifier expression(s) */
+	int32		typemod;		/* prespecified type modifier */
 	List	   *arrayBounds;	/* array bounds */
 	int			location;		/* token location, or -1 if unknown */
 } TypeName;
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index 0d2cf087d15..1e9e616d09c 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_type.h,v 1.33 2006/09/25 15:17:34 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_type.h,v 1.34 2006/12/30 21:21:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -24,6 +24,8 @@ extern Oid	LookupTypeName(ParseState *pstate, const TypeName *typename);
 extern char *TypeNameToString(const TypeName *typename);
 extern char *TypeNameListToString(List *typenames);
 extern Oid	typenameTypeId(ParseState *pstate, const TypeName *typename);
+extern int32 typenameTypeMod(ParseState *pstate, const TypeName *typename,
+							 Oid typeId);
 extern Type typenameType(ParseState *pstate, const TypeName *typename);
 
 extern Type typeidType(Oid id);
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 791f6ebd999..9c9ff140873 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -49,7 +49,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.60 2006/11/08 19:24:38 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.61 2006/12/30 21:21:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -255,6 +255,7 @@ extern void mda_get_range(int n, int *span, const int *st, const int *endp);
 extern void mda_get_prod(int n, const int *range, int *prod);
 extern void mda_get_offset_values(int n, int *dist, const int *prod, const int *span);
 extern int	mda_next_tuple(int n, int *curr, const int *span);
+extern int32 *ArrayGetTypmods(ArrayType *arr, int *n);
 
 /*
  * prototypes for functions defined in array_userfuncs.c
diff --git a/src/include/utils/builtins.h b/src/include/utils/builtins.h
index e1ed7b862b3..56e63d7f73c 100644
--- a/src/include/utils/builtins.h
+++ b/src/include/utils/builtins.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.282 2006/09/18 22:40:40 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/builtins.h,v 1.283 2006/12/30 21:21:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -565,6 +565,8 @@ extern Datum bpcharin(PG_FUNCTION_ARGS);
 extern Datum bpcharout(PG_FUNCTION_ARGS);
 extern Datum bpcharrecv(PG_FUNCTION_ARGS);
 extern Datum bpcharsend(PG_FUNCTION_ARGS);
+extern Datum bpchartypmodin(PG_FUNCTION_ARGS);
+extern Datum bpchartypmodout(PG_FUNCTION_ARGS);
 extern Datum bpchar(PG_FUNCTION_ARGS);
 extern Datum char_bpchar(PG_FUNCTION_ARGS);
 extern Datum name_bpchar(PG_FUNCTION_ARGS);
@@ -586,6 +588,8 @@ extern Datum varcharin(PG_FUNCTION_ARGS);
 extern Datum varcharout(PG_FUNCTION_ARGS);
 extern Datum varcharrecv(PG_FUNCTION_ARGS);
 extern Datum varcharsend(PG_FUNCTION_ARGS);
+extern Datum varchartypmodin(PG_FUNCTION_ARGS);
+extern Datum varchartypmodout(PG_FUNCTION_ARGS);
 extern Datum varchar(PG_FUNCTION_ARGS);
 
 /* varlena.c */
@@ -789,7 +793,9 @@ extern Datum numeric_in(PG_FUNCTION_ARGS);
 extern Datum numeric_out(PG_FUNCTION_ARGS);
 extern Datum numeric_recv(PG_FUNCTION_ARGS);
 extern Datum numeric_send(PG_FUNCTION_ARGS);
-extern Datum numeric (PG_FUNCTION_ARGS);
+extern Datum numerictypmodin(PG_FUNCTION_ARGS);
+extern Datum numerictypmodout(PG_FUNCTION_ARGS);
+extern Datum numeric(PG_FUNCTION_ARGS);
 extern Datum numeric_abs(PG_FUNCTION_ARGS);
 extern Datum numeric_uminus(PG_FUNCTION_ARGS);
 extern Datum numeric_uplus(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/date.h b/src/include/utils/date.h
index 44eb8e81143..48a6ae1cee1 100644
--- a/src/include/utils/date.h
+++ b/src/include/utils/date.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/date.h,v 1.34 2006/07/13 16:49:20 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/date.h,v 1.35 2006/12/30 21:21:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -140,6 +140,8 @@ extern Datum time_in(PG_FUNCTION_ARGS);
 extern Datum time_out(PG_FUNCTION_ARGS);
 extern Datum time_recv(PG_FUNCTION_ARGS);
 extern Datum time_send(PG_FUNCTION_ARGS);
+extern Datum timetypmodin(PG_FUNCTION_ARGS);
+extern Datum timetypmodout(PG_FUNCTION_ARGS);
 extern Datum time_scale(PG_FUNCTION_ARGS);
 extern Datum time_eq(PG_FUNCTION_ARGS);
 extern Datum time_ne(PG_FUNCTION_ARGS);
@@ -166,6 +168,8 @@ extern Datum timetz_in(PG_FUNCTION_ARGS);
 extern Datum timetz_out(PG_FUNCTION_ARGS);
 extern Datum timetz_recv(PG_FUNCTION_ARGS);
 extern Datum timetz_send(PG_FUNCTION_ARGS);
+extern Datum timetztypmodin(PG_FUNCTION_ARGS);
+extern Datum timetztypmodout(PG_FUNCTION_ARGS);
 extern Datum timetz_scale(PG_FUNCTION_ARGS);
 extern Datum timetz_eq(PG_FUNCTION_ARGS);
 extern Datum timetz_ne(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/lsyscache.h b/src/include/utils/lsyscache.h
index 272b321e642..e78236b2185 100644
--- a/src/include/utils/lsyscache.h
+++ b/src/include/utils/lsyscache.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.108 2006/12/23 00:43:13 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/lsyscache.h,v 1.109 2006/12/30 21:21:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -100,6 +100,7 @@ extern void getTypeInputInfo(Oid type, Oid *typInput, Oid *typIOParam);
 extern void getTypeOutputInfo(Oid type, Oid *typOutput, bool *typIsVarlena);
 extern void getTypeBinaryInputInfo(Oid type, Oid *typReceive, Oid *typIOParam);
 extern void getTypeBinaryOutputInfo(Oid type, Oid *typSend, bool *typIsVarlena);
+extern Oid	get_typmodin(Oid typid);
 extern Oid	getBaseType(Oid typid);
 extern Oid	getBaseTypeAndTypmod(Oid typid, int32 *typmod);
 extern int32 get_typavgwidth(Oid typid, int32 typmod);
diff --git a/src/include/utils/timestamp.h b/src/include/utils/timestamp.h
index 0c6e59cc92f..3bbd63ba456 100644
--- a/src/include/utils/timestamp.h
+++ b/src/include/utils/timestamp.h
@@ -6,7 +6,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.64 2006/10/04 00:30:11 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/timestamp.h,v 1.65 2006/12/30 21:21:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -200,6 +200,8 @@ extern Datum timestamp_in(PG_FUNCTION_ARGS);
 extern Datum timestamp_out(PG_FUNCTION_ARGS);
 extern Datum timestamp_recv(PG_FUNCTION_ARGS);
 extern Datum timestamp_send(PG_FUNCTION_ARGS);
+extern Datum timestamptypmodin(PG_FUNCTION_ARGS);
+extern Datum timestamptypmodout(PG_FUNCTION_ARGS);
 extern Datum timestamp_scale(PG_FUNCTION_ARGS);
 extern Datum timestamp_eq(PG_FUNCTION_ARGS);
 extern Datum timestamp_ne(PG_FUNCTION_ARGS);
@@ -232,6 +234,8 @@ extern Datum interval_in(PG_FUNCTION_ARGS);
 extern Datum interval_out(PG_FUNCTION_ARGS);
 extern Datum interval_recv(PG_FUNCTION_ARGS);
 extern Datum interval_send(PG_FUNCTION_ARGS);
+extern Datum intervaltypmodin(PG_FUNCTION_ARGS);
+extern Datum intervaltypmodout(PG_FUNCTION_ARGS);
 extern Datum interval_scale(PG_FUNCTION_ARGS);
 extern Datum interval_eq(PG_FUNCTION_ARGS);
 extern Datum interval_ne(PG_FUNCTION_ARGS);
@@ -264,6 +268,8 @@ extern Datum timestamptz_in(PG_FUNCTION_ARGS);
 extern Datum timestamptz_out(PG_FUNCTION_ARGS);
 extern Datum timestamptz_recv(PG_FUNCTION_ARGS);
 extern Datum timestamptz_send(PG_FUNCTION_ARGS);
+extern Datum timestamptztypmodin(PG_FUNCTION_ARGS);
+extern Datum timestamptztypmodout(PG_FUNCTION_ARGS);
 extern Datum timestamptz_scale(PG_FUNCTION_ARGS);
 extern Datum timestamptz_timestamp(PG_FUNCTION_ARGS);
 extern Datum timestamptz_zone(PG_FUNCTION_ARGS);
diff --git a/src/include/utils/varbit.h b/src/include/utils/varbit.h
index 793d28d3835..f0fb87e71e7 100644
--- a/src/include/utils/varbit.h
+++ b/src/include/utils/varbit.h
@@ -8,7 +8,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/varbit.h,v 1.23 2006/03/05 15:59:08 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/utils/varbit.h,v 1.24 2006/12/30 21:21:56 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -64,10 +64,14 @@ extern Datum bit_in(PG_FUNCTION_ARGS);
 extern Datum bit_out(PG_FUNCTION_ARGS);
 extern Datum bit_recv(PG_FUNCTION_ARGS);
 extern Datum bit_send(PG_FUNCTION_ARGS);
+extern Datum bittypmodin(PG_FUNCTION_ARGS);
+extern Datum bittypmodout(PG_FUNCTION_ARGS);
 extern Datum varbit_in(PG_FUNCTION_ARGS);
 extern Datum varbit_out(PG_FUNCTION_ARGS);
 extern Datum varbit_recv(PG_FUNCTION_ARGS);
 extern Datum varbit_send(PG_FUNCTION_ARGS);
+extern Datum varbittypmodin(PG_FUNCTION_ARGS);
+extern Datum varbittypmodout(PG_FUNCTION_ARGS);
 extern Datum bit(PG_FUNCTION_ARGS);
 extern Datum varbit(PG_FUNCTION_ARGS);
 extern Datum biteq(PG_FUNCTION_ARGS);
diff --git a/src/test/regress/expected/create_type.out b/src/test/regress/expected/create_type.out
index 3e2edeb1e0b..133ad857ac1 100644
--- a/src/test/regress/expected/create_type.out
+++ b/src/test/regress/expected/create_type.out
@@ -10,6 +10,8 @@ CREATE TYPE widget (
    internallength = 24, 
    input = widget_in,
    output = widget_out,
+   typmod_in = numerictypmodin,
+   typmod_out = numerictypmodout,
    alignment = double
 );
 CREATE TYPE city_budget ( 
@@ -99,3 +101,15 @@ 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;
+-- Check usage of typmod with a user-defined type
+-- (we have borrowed numeric's typmod functions)
+CREATE TEMP TABLE mytab (foo widget(42,13,7));     -- should fail
+ERROR:  invalid NUMERIC type modifier
+CREATE TEMP TABLE mytab (foo widget(42,13));
+SELECT format_type(atttypid,atttypmod) FROM pg_attribute
+WHERE attrelid = 'mytab'::regclass AND attnum > 0;
+  format_type  
+---------------
+ widget(42,13)
+(1 row)
+
diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out
index 3aa8e3714bf..f8cc66492ce 100644
--- a/src/test/regress/expected/horology.out
+++ b/src/test/regress/expected/horology.out
@@ -840,8 +840,12 @@ SELECT time '03:30' + interval '1 month 04:01' AS "07:31:00";
 
 SELECT CAST(time with time zone '01:02-08' AS interval) AS "+00:01";
 ERROR:  cannot cast type time with time zone to interval
+LINE 1: SELECT CAST(time with time zone '01:02-08' AS interval) AS "...
+                                                      ^
 SELECT CAST(interval '02:03' AS time with time zone) AS "02:03:00-08";
 ERROR:  cannot cast type interval to time with time zone
+LINE 1: SELECT CAST(interval '02:03' AS time with time zone) AS "02:...
+                                        ^
 SELECT time with time zone '01:30-08' - interval '02:01' AS "23:29:00-08";
  23:29:00-08 
 -------------
diff --git a/src/test/regress/expected/oidjoins.out b/src/test/regress/expected/oidjoins.out
index cc82b12e027..5945753a1b3 100644
--- a/src/test/regress/expected/oidjoins.out
+++ b/src/test/regress/expected/oidjoins.out
@@ -753,6 +753,22 @@ WHERE	typsend != 0 AND
 ------+---------
 (0 rows)
 
+SELECT	ctid, typmodin 
+FROM	pg_catalog.pg_type fk 
+WHERE	typmodin != 0 AND 
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typmodin);
+ ctid | typmodin 
+------+----------
+(0 rows)
+
+SELECT	ctid, typmodout 
+FROM	pg_catalog.pg_type fk 
+WHERE	typmodout != 0 AND 
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typmodout);
+ ctid | typmodout 
+------+-----------
+(0 rows)
+
 SELECT	ctid, typbasetype 
 FROM	pg_catalog.pg_type fk 
 WHERE	typbasetype != 0 AND 
diff --git a/src/test/regress/expected/type_sanity.out b/src/test/regress/expected/type_sanity.out
index 73d51925c2f..11c9298e9ae 100644
--- a/src/test/regress/expected/type_sanity.out
+++ b/src/test/regress/expected/type_sanity.out
@@ -212,6 +212,48 @@ WHERE p1.typsend = p2.oid AND p1.typtype in ('b', 'p') AND NOT
 -----+---------+-----+---------
 (0 rows)
 
+-- Check for bogus typmodin routines
+SELECT p1.oid, p1.typname, p2.oid, p2.proname
+FROM pg_type AS p1, pg_proc AS p2
+WHERE p1.typmodin = p2.oid AND p1.typtype in ('b', 'p') AND NOT
+    (p2.pronargs = 1 AND
+     p2.proargtypes[0] = 'int4[]'::regtype AND
+     p2.prorettype = 'int4'::regtype AND NOT p2.proretset);
+ oid | typname | oid | proname 
+-----+---------+-----+---------
+(0 rows)
+
+-- Check for bogus typmodout routines
+SELECT p1.oid, p1.typname, p2.oid, p2.proname
+FROM pg_type AS p1, pg_proc AS p2
+WHERE p1.typmodout = p2.oid AND p1.typtype in ('b', 'p') AND NOT
+    (p2.pronargs = 1 AND
+     p2.proargtypes[0] = 'int4'::regtype AND
+     p2.prorettype = 'cstring'::regtype AND NOT p2.proretset);
+ oid | typname | oid | proname 
+-----+---------+-----+---------
+(0 rows)
+
+-- Array types should have same typmodin/out as their element types
+SELECT p1.oid, p1.typname, p2.oid, p2.typname
+FROM pg_type AS p1, pg_type AS p2
+WHERE p1.typelem = p2.oid AND NOT
+    (p1.typmodin = p2.typmodin AND p1.typmodout = p2.typmodout);
+ oid | typname | oid | typname 
+-----+---------+-----+---------
+(0 rows)
+
+-- Check for bogus typanalyze routines
+SELECT p1.oid, p1.typname, p2.oid, p2.proname
+FROM pg_type AS p1, pg_proc AS p2
+WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT
+    (p2.pronargs = 1 AND
+     p2.proargtypes[0] = 'internal'::regtype AND
+     p2.prorettype = 'bool'::regtype AND NOT p2.proretset);
+ oid | typname | oid | proname 
+-----+---------+-----+---------
+(0 rows)
+
 -- **************** pg_class ****************
 -- Look for illegal values in pg_class fields
 SELECT p1.oid, p1.relname
diff --git a/src/test/regress/sql/create_type.sql b/src/test/regress/sql/create_type.sql
index 097d51fc925..c6e391d8104 100644
--- a/src/test/regress/sql/create_type.sql
+++ b/src/test/regress/sql/create_type.sql
@@ -11,6 +11,8 @@ CREATE TYPE widget (
    internallength = 24, 
    input = widget_in,
    output = widget_out,
+   typmod_in = numerictypmodin,
+   typmod_out = numerictypmodout,
    alignment = double
 );
 
@@ -98,3 +100,12 @@ CREATE TYPE text_w_default;		-- should fail
 DROP TYPE default_test_row CASCADE;
 
 DROP TABLE default_test;
+
+-- Check usage of typmod with a user-defined type
+-- (we have borrowed numeric's typmod functions)
+
+CREATE TEMP TABLE mytab (foo widget(42,13,7));     -- should fail
+CREATE TEMP TABLE mytab (foo widget(42,13));
+
+SELECT format_type(atttypid,atttypmod) FROM pg_attribute
+WHERE attrelid = 'mytab'::regclass AND attnum > 0;
diff --git a/src/test/regress/sql/oidjoins.sql b/src/test/regress/sql/oidjoins.sql
index bf713e9fa7c..5eb440f0f54 100644
--- a/src/test/regress/sql/oidjoins.sql
+++ b/src/test/regress/sql/oidjoins.sql
@@ -377,6 +377,14 @@ SELECT	ctid, typsend
 FROM	pg_catalog.pg_type fk 
 WHERE	typsend != 0 AND 
 	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typsend);
+SELECT	ctid, typmodin 
+FROM	pg_catalog.pg_type fk 
+WHERE	typmodin != 0 AND 
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typmodin);
+SELECT	ctid, typmodout 
+FROM	pg_catalog.pg_type fk 
+WHERE	typmodout != 0 AND 
+	NOT EXISTS(SELECT 1 FROM pg_catalog.pg_proc pk WHERE pk.oid = fk.typmodout);
 SELECT	ctid, typbasetype 
 FROM	pg_catalog.pg_type fk 
 WHERE	typbasetype != 0 AND 
diff --git a/src/test/regress/sql/type_sanity.sql b/src/test/regress/sql/type_sanity.sql
index 3969b4cce2e..3677b90ceb9 100644
--- a/src/test/regress/sql/type_sanity.sql
+++ b/src/test/regress/sql/type_sanity.sql
@@ -163,6 +163,40 @@ FROM pg_type AS p1, pg_proc AS p2
 WHERE p1.typsend = p2.oid AND p1.typtype in ('b', 'p') AND NOT
     (p2.prorettype = 'bytea'::regtype AND NOT p2.proretset);
 
+-- Check for bogus typmodin routines
+
+SELECT p1.oid, p1.typname, p2.oid, p2.proname
+FROM pg_type AS p1, pg_proc AS p2
+WHERE p1.typmodin = p2.oid AND p1.typtype in ('b', 'p') AND NOT
+    (p2.pronargs = 1 AND
+     p2.proargtypes[0] = 'int4[]'::regtype AND
+     p2.prorettype = 'int4'::regtype AND NOT p2.proretset);
+
+-- Check for bogus typmodout routines
+
+SELECT p1.oid, p1.typname, p2.oid, p2.proname
+FROM pg_type AS p1, pg_proc AS p2
+WHERE p1.typmodout = p2.oid AND p1.typtype in ('b', 'p') AND NOT
+    (p2.pronargs = 1 AND
+     p2.proargtypes[0] = 'int4'::regtype AND
+     p2.prorettype = 'cstring'::regtype AND NOT p2.proretset);
+
+-- Array types should have same typmodin/out as their element types
+
+SELECT p1.oid, p1.typname, p2.oid, p2.typname
+FROM pg_type AS p1, pg_type AS p2
+WHERE p1.typelem = p2.oid AND NOT
+    (p1.typmodin = p2.typmodin AND p1.typmodout = p2.typmodout);
+
+-- Check for bogus typanalyze routines
+
+SELECT p1.oid, p1.typname, p2.oid, p2.proname
+FROM pg_type AS p1, pg_proc AS p2
+WHERE p1.typanalyze = p2.oid AND p1.typtype in ('b', 'p') AND NOT
+    (p2.pronargs = 1 AND
+     p2.proargtypes[0] = 'internal'::regtype AND
+     p2.prorettype = 'bool'::regtype AND NOT p2.proretset);
+
 -- **************** pg_class ****************
 
 -- Look for illegal values in pg_class fields
diff --git a/src/tools/findoidjoins/README b/src/tools/findoidjoins/README
index aec8f1bd8de..598aab2bbd1 100644
--- a/src/tools/findoidjoins/README
+++ b/src/tools/findoidjoins/README
@@ -130,6 +130,8 @@ Join pg_catalog.pg_type.typinput => pg_catalog.pg_proc.oid
 Join pg_catalog.pg_type.typoutput => pg_catalog.pg_proc.oid
 Join pg_catalog.pg_type.typreceive => pg_catalog.pg_proc.oid
 Join pg_catalog.pg_type.typsend => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_type.typmodin => pg_catalog.pg_proc.oid
+Join pg_catalog.pg_type.typmodout => pg_catalog.pg_proc.oid
 Join pg_catalog.pg_type.typbasetype => pg_catalog.pg_type.oid
 
 ---------------------------------------------------------------------------
-- 
GitLab