diff --git a/doc/src/sgml/catalogs.sgml b/doc/src/sgml/catalogs.sgml
index 226eef1c8dae43ddc912e470a081ba6d2d95a0f9..84ae3cb205f543b56d909a0125ac3becd92ec7a3 100644
--- a/doc/src/sgml/catalogs.sgml
+++ b/doc/src/sgml/catalogs.sgml
@@ -1,6 +1,6 @@
 <!--
  Documentation of the system catalogs, directed toward PostgreSQL developers
- $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.86 2004/06/07 04:04:47 tgl Exp $
+ $PostgreSQL: pgsql/doc/src/sgml/catalogs.sgml,v 2.87 2004/06/16 01:26:33 tgl Exp $
  -->
 
 <chapter id="catalogs">
@@ -934,7 +934,7 @@
       <entry>
        Indicates what contexts the cast may be invoked in.
        <literal>e</> means only as an explicit cast (using
-       <literal>CAST</>, <literal>::</>, or function-call syntax).
+       <literal>CAST</> or <literal>::</> syntax).
        <literal>a</> means implicitly in assignment
        to a target column, as well as explicitly.
        <literal>i</> means implicitly in expressions, as well as the
@@ -944,6 +944,39 @@
     </tbody>
    </tgroup>
   </table>
+
+  <para>
+   The cast functions listed in <structname>pg_cast</structname> must
+   always take the cast source type as their first argument type, and
+   return the cast destination type as their result type.  A cast
+   function can have up to three arguments.  The second argument,
+   if present, must be type <type>integer</>; it receives the type
+   modifier associated with the destination type, or <literal>-1</>
+   if there is none.  The third argument,
+   if present, must be type <type>boolean</>; it receives <literal>true</>
+   if the cast is an explicit cast, <literal>false</> otherwise.
+  </para>
+
+  <para>
+   It is legitimate to create a <structname>pg_cast</structname> entry
+   in which the source and target types are the same, if the associated
+   function takes more than one argument.  Such entries represent
+   <quote>length coercion functions</> that coerce values of the type
+   to be legal for a particular type modifier value.  Note however that
+   at present there is no support for associating non-default type
+   modifiers with user-created data types, and so this facility is only
+   of use for the small number of built-in types that have type modifier
+   syntax built into the grammar.
+  </para>
+
+  <para>
+   When a <structname>pg_cast</structname> entry has different source and
+   target types and a function that takes more than one argument, it
+   represents converting from one type to another and applying a length
+   coercion in a single step.  When no such entry is available, coercion
+   to a type that uses a type modifier involves two steps, one to
+   convert between datatypes and a second to apply the modifier.
+  </para>
  </sect1>
 
  <sect1 id="catalog-pg-class">
diff --git a/doc/src/sgml/datatype.sgml b/doc/src/sgml/datatype.sgml
index 19feebe0b28eaac9bb72de7d2e7a7c5612bddc46..fdc63d8250ad3dcc1e5c59db2f7ccac798cd159f 100644
--- a/doc/src/sgml/datatype.sgml
+++ b/doc/src/sgml/datatype.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.145 2004/06/07 04:04:47 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/datatype.sgml,v 1.146 2004/06/16 01:26:35 tgl Exp $
 -->
 
  <chapter id="datatype">
@@ -2851,7 +2851,7 @@ SELECT * FROM test1 WHERE a;
     linkend="sql-syntax-bit-strings"> for information about the syntax
     of bit string constants.  Bit-logical operators and string
     manipulation functions are available; see <xref
-    linkend="functions">.
+    linkend="functions-bitstring">.
    </para>
 
    <example>
diff --git a/doc/src/sgml/func.sgml b/doc/src/sgml/func.sgml
index 37cc1411f817ad5e285e7ece405d89978f2cb165..3de1adafc9a786f6429931284a1d2a3a0fa34aa2 100644
--- a/doc/src/sgml/func.sgml
+++ b/doc/src/sgml/func.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.208 2004/06/14 19:01:09 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/func.sgml,v 1.209 2004/06/16 01:26:36 tgl Exp $
 PostgreSQL documentation
 -->
 
@@ -488,55 +488,13 @@ PostgreSQL documentation
    </table>
 
    <para>
-    The bitwise operators are also available for the bit
+    The bitwise operators work only on integral data types, whereas
+    the others are available for all numeric data types.  The bitwise
+    operators are also available for the bit
     string types <type>bit</type> and <type>bit varying</type>, as
-    shown in <xref linkend="functions-math-bit-table">.
-    Bit string operands of <literal>&amp;</literal>, <literal>|</literal>,
-    and <literal>#</literal> must be of equal length.  When bit
-    shifting, the original length of the string is preserved, as shown
-    in the table.
+    shown in <xref linkend="functions-bit-string-op-table">.
    </para>
 
-    <table id="functions-math-bit-table">
-     <title>Bit String Bitwise Operators</title>
-
-     <tgroup cols="2">
-      <thead>
-       <row>
-        <entry>Example</entry>
-        <entry>Result</entry>
-       </row>
-      </thead>
-
-      <tbody>
-       <row>
-        <entry><literal>B'10001' &amp; B'01101'</literal></entry>
-        <entry><literal>00001</literal></entry>
-       </row>
-       <row>
-        <entry><literal>B'10001' | B'01101'</literal></entry>
-        <entry><literal>11101</literal></entry>
-       </row>
-       <row>
-        <entry><literal>B'10001' # B'01101'</literal></entry>
-        <entry><literal>11110</literal></entry>
-       </row>
-       <row>
-        <entry><literal>~ B'10001'</literal></entry>
-        <entry><literal>01110</literal></entry>
-       </row>
-       <row>
-        <entry><literal>B'10001' &lt;&lt; 3</literal></entry>
-        <entry><literal>01000</literal></entry>
-       </row>
-       <row>
-        <entry><literal>B'10001' &gt;&gt; 2</literal></entry>
-        <entry><literal>00100</literal></entry>
-       </row>
-      </tbody>
-     </tgroup>
-    </table>
-
   <para>
    <xref linkend="functions-math-func-table"> shows the available
    mathematical functions.  In the table, <literal>dp</literal>
@@ -2337,6 +2295,130 @@ PostgreSQL documentation
  </sect1>
 
 
+  <sect1 id="functions-bitstring">
+   <title>Bit String Functions and Operators</title>
+
+   <indexterm zone="functions-bitstring">
+    <primary>bit strings</primary>
+    <secondary>functions</secondary>
+   </indexterm>
+
+   <para>
+    This section describes functions and operators for examining and
+    manipulating bit strings, that is values of the types
+    <type>bit</type> and <type>bit varying</type>.  Aside from the
+    usual comparison operators, the operators
+    shown in <xref linkend="functions-bit-string-op-table"> can be used.
+    Bit string operands of <literal>&amp;</literal>, <literal>|</literal>,
+    and <literal>#</literal> must be of equal length.  When bit
+    shifting, the original length of the string is preserved, as shown
+    in the examples.
+   </para>
+
+   <table id="functions-bit-string-op-table">
+    <title>Bit String Operators</title>
+
+    <tgroup cols="4">
+     <thead>
+      <row>
+       <entry>Operator</entry>
+       <entry>Description</entry>
+       <entry>Example</entry>
+       <entry>Result</entry>
+      </row>
+     </thead>
+
+     <tbody>
+      <row>
+       <entry> <literal>||</literal> </entry>
+       <entry>concatenation</entry>
+       <entry><literal>B'10001' || B'011'</literal></entry>
+       <entry><literal>10001011</literal></entry>
+      </row>
+
+      <row>
+       <entry> <literal>&amp;</literal> </entry>
+       <entry>bitwise AND</entry>
+       <entry><literal>B'10001' &amp; B'01101'</literal></entry>
+       <entry><literal>00001</literal></entry>
+      </row>
+
+      <row>
+       <entry> <literal>|</literal> </entry>
+       <entry>bitwise OR</entry>
+       <entry><literal>B'10001' | B'01101'</literal></entry>
+       <entry><literal>11101</literal></entry>
+      </row>
+
+      <row>
+       <entry> <literal>#</literal> </entry>
+       <entry>bitwise XOR</entry>
+       <entry><literal>B'10001' # B'01101'</literal></entry>
+       <entry><literal>11100</literal></entry>
+      </row>
+
+      <row>
+       <entry> <literal>~</literal> </entry>
+       <entry>bitwise NOT</entry>
+       <entry><literal>~ B'10001'</literal></entry>
+       <entry><literal>01110</literal></entry>
+      </row>
+
+      <row>
+       <entry> <literal>&lt;&lt;</literal> </entry>
+       <entry>bitwise shift left</entry>
+       <entry><literal>B'10001' &lt;&lt; 3</literal></entry>
+       <entry><literal>01000</literal></entry>
+      </row>
+
+      <row>
+       <entry> <literal>&gt;&gt;</literal> </entry>
+       <entry>bitwise shift right</entry>
+       <entry><literal>B'10001' &gt;&gt; 2</literal></entry>
+       <entry><literal>00100</literal></entry>
+      </row>
+     </tbody>
+    </tgroup>
+   </table>
+
+   <para>
+    The following <acronym>SQL</acronym>-standard functions work on bit
+    strings as well as character strings:
+    <literal><function>length</function></literal>,
+    <literal><function>bit_length</function></literal>,
+    <literal><function>octet_length</function></literal>,
+    <literal><function>position</function></literal>,
+    <literal><function>substring</function></literal>.
+   </para>
+
+   <para>
+    In addition, it is possible to cast integral values to and from type
+    <type>bit</>.
+    Some examples:
+<programlisting>
+44::bit(10)                    <lineannotation>0000101100</lineannotation>
+44::bit(3)                     <lineannotation>100</lineannotation>
+cast(-44 as bit(12))           <lineannotation>111111010100</lineannotation>
+'1110'::bit(4)::integer        <lineannotation>14</lineannotation>
+</programlisting>
+    Note that casting to just <quote>bit</> means casting to
+    <literal>bit(1)</>, and so it will deliver only the least significant
+    bit of the integer.
+   </para>
+
+    <note>
+     <para>
+      Prior to <productname>PostgreSQL</productname> 7.5, casting an
+      integer to <type>bit(n)</> would copy the leftmost <literal>n</>
+      bits of the integer, whereas now it copies the rightmost <literal>n</>
+      bits.  Also, casting an integer to a bit string width wider than
+      the integer itself will sign-extend on the left.
+     </para>
+    </note>
+
+  </sect1>
+
+
  <sect1 id="functions-matching">
   <title>Pattern Matching</title>
 
@@ -7628,14 +7710,13 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
        <function>bit_and(<replaceable class="parameter">expression</replaceable>)</function>
       </entry>
       <entry>
-       <type>smallint</type>, <type>integer</type>, <type>bigint</type> or
-       <type>bit</type>,
+       <type>smallint</type>, <type>integer</type>, <type>bigint</type>, or
+       <type>bit</type>
       </entry>
       <entry>
-        same as argument data type.
-      </entry>
-      <entry>the bitwise-and of all non-null input values, or null if empty
+        same as argument data type
       </entry>
+      <entry>the bitwise AND of all non-null input values, or null if none</entry>
      </row>
 
      <row>
@@ -7646,14 +7727,13 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
        <function>bit_or(<replaceable class="parameter">expression</replaceable>)</function>
       </entry>
       <entry>
-       <type>smallint</type>, <type>integer</type>, <type>bigint</type> or
-       <type>bit</type>,
+       <type>smallint</type>, <type>integer</type>, <type>bigint</type>, or
+       <type>bit</type>
       </entry>
       <entry>
-        same as argument data type.
-      </entry>
-      <entry>the bitwise-or of all non-null input values, or null if empty.
+        same as argument data type
       </entry>
+      <entry>the bitwise OR of all non-null input values, or null if none</entry>
      </row>
 
      <row>
@@ -7669,9 +7749,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
       <entry>
        <type>bool</type>
       </entry>
-      <entry>true if all input values are true, otherwise false.
-      Also known as <function>bool_and</function>.
-      </entry>
+      <entry>true if all input values are true, otherwise false</entry>
      </row>
 
      <row>
@@ -7720,9 +7798,7 @@ SELECT pg_type_is_visible('myschema.widget'::regtype);
       <entry>
        <type>bool</type>
       </entry>
-      <entry>true if all input values are true, otherwise false.
-      Also known as <function>bool_and</function>.
-      </entry>
+      <entry>equivalent to <function>bool_and</function></entry>
      </row>
 
      <row>
diff --git a/doc/src/sgml/ref/create_cast.sgml b/doc/src/sgml/ref/create_cast.sgml
index a6f5e4aa19a9a782a5e476aa36d1e19b107fdc73..31d2473c3508f6a087bb894a2e762891b1ceea23 100644
--- a/doc/src/sgml/ref/create_cast.sgml
+++ b/doc/src/sgml/ref/create_cast.sgml
@@ -1,4 +1,4 @@
-<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/create_cast.sgml,v 1.16 2004/02/15 06:27:37 neilc Exp $ -->
+<!-- $PostgreSQL: pgsql/doc/src/sgml/ref/create_cast.sgml,v 1.17 2004/06/16 01:26:40 tgl Exp $ -->
 
 <refentry id="SQL-CREATECAST">
  <refmeta>
@@ -18,7 +18,7 @@
  <refsynopsisdiv>
 <synopsis>
 CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>)
-    WITH FUNCTION <replaceable>funcname</replaceable> (<replaceable>argtype</replaceable>)
+    WITH FUNCTION <replaceable>funcname</replaceable> (<replaceable>argtypes</replaceable>)
     [ AS ASSIGNMENT | AS IMPLICIT ]
 
 CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>)
@@ -55,9 +55,9 @@ SELECT CAST(42 AS text);
   <para>
    By default, a cast can be invoked only by an explicit cast request,
    that is an explicit <literal>CAST(<replaceable>x</> AS
-   <replaceable>typename</>)</literal>,
-   <replaceable>x</><literal>::</><replaceable>typename</>, or
-   <replaceable>typename</>(<replaceable>x</>) construct.
+   <replaceable>typename</>)</literal> or
+   <replaceable>x</><literal>::</><replaceable>typename</>
+   construct.
   </para>
 
   <para>
@@ -141,15 +141,14 @@ SELECT 'The time is ' || CAST(now() AS text);
     </varlistentry>
 
     <varlistentry>
-     <term><replaceable>funcname</replaceable>(<replaceable>argtype</replaceable>)</term>
+     <term><replaceable>funcname</replaceable>(<replaceable>argtypes</replaceable>)</term>
 
      <listitem>
       <para>
        The function used to perform the cast.  The function name may
        be schema-qualified.  If it is not, the function will be looked
-       up in the schema search path.  The argument type must be
-       identical to the source type and the result data type must
-       match the target type of the cast.
+       up in the schema search path.  The function's result data type must
+       match the target type of the cast.   Its arguments are discussed below.
       </para>
      </listitem>
     </varlistentry>
@@ -187,6 +186,42 @@ SELECT 'The time is ' || CAST(now() AS text);
     </varlistentry>
    </variablelist>
 
+  <para>
+   Cast implementation functions may have one to three arguments.
+   The first argument type must be identical to the cast's source type.
+   The second argument,
+   if present, must be type <type>integer</>; it receives the type
+   modifier associated with the destination type, or <literal>-1</>
+   if there is none.  The third argument,
+   if present, must be type <type>boolean</>; it receives <literal>true</>
+   if the cast is an explicit cast, <literal>false</> otherwise.
+   (Bizarrely, the SQL spec demands different behaviors for explicit and
+   implicit casts in some cases.  This argument is supplied for functions
+   that must implement such casts.  It is not recommended that you design
+   your own datatypes so that this matters.)
+  </para>
+
+  <para>
+   Ordinarily a cast must have different source and target data types.
+   However, it is allowed to declare a cast with identical source and
+   target types if it has a cast implementation function with more than one
+   argument.  This is used to represent type-specific length coercion
+   functions in the system catalogs.  The named function is used to
+   coerce a value of the type to the type modifier value given by its
+   second argument.  (Since the grammar presently permits only certain
+   built-in data types to have type modifiers, this feature is of no
+   use for user-defined target types, but we mention it for completeness.)
+  </para>
+
+  <para>
+   When a cast has different source and
+   target types and a function that takes more than one argument, it
+   represents converting from one type to another and applying a length
+   coercion in a single step.  When no such entry is available, coercion
+   to a type that uses a type modifier involves two steps, one to
+   convert between datatypes and a second to apply the modifier.
+  </para>
+
  </refsect1>
 
  <refsect1 id="sql-createcast-notes">
@@ -207,10 +242,40 @@ SELECT 'The time is ' || CAST(now() AS text);
    argument of a different type was automatically a cast function.
    This convention has been abandoned in face of the introduction of
    schemas and to be able to represent binary compatible casts in the
-   system catalogs.  (The built-in cast functions still follow this naming
-   scheme, but they have to be shown as casts in the system catalog <literal>pg_cast</>
-   now.)
+   system catalogs.  The built-in cast functions still follow this naming
+   scheme, but they have to be shown as casts in the system catalog
+   <structname>pg_cast</> as well.
+  </para>
+
+  <para>
+   While not required, it is recommended that you continue to follow this old
+   convention of naming cast implementation functions after the target data
+   type.  Many users are used to being able to cast datatypes using a
+   function-style notation, that is
+   <replaceable>typename</>(<replaceable>x</>).  This notation is in fact
+   nothing more nor less than a call of the cast implementation function; it
+   is not specially treated as a cast.  If your conversion functions are not
+   named to support this convention then you will have surprised users.
+   Since <productname>PostgreSQL</> allows overloading of the same function
+   name with different argument types, there is no difficulty in having
+   multiple conversion functions from different types that all use the
+   target type's name.
   </para>
+
+  <note>
+   <para>
+   There is one small lie in the preceding paragraph: there is still one
+   case in which <structname>pg_cast</> will be used to resolve the
+   meaning of an apparent function call.  If a
+   function call <replaceable>name</>(<replaceable>x</>) matches no
+   actual function, but <replaceable>name</> is the name of a data type
+   and <structname>pg_cast</> shows a binary-compatible cast to this
+   type from the type of <replaceable>x</>, then the call will be construed
+   as an explicit cast.  This exception is made so that binary-compatible
+   casts can be invoked using functional syntax, even though they lack
+   any function.
+   </para>
+  </note>
  </refsect1>
 
 
@@ -234,7 +299,8 @@ CREATE CAST (text AS int4) WITH FUNCTION int4(text);
   <para>
    The <command>CREATE CAST</command> command conforms to SQL99,
    except that SQL99 does not make provisions for binary-compatible
-   types.  <literal>AS IMPLICIT</> is a <productname>PostgreSQL</productname>
+   types or extra arguments to implementation functions.
+   <literal>AS IMPLICIT</> is a <productname>PostgreSQL</productname> 
    extension, too.
   </para>
  </refsect1>
diff --git a/doc/src/sgml/syntax.sgml b/doc/src/sgml/syntax.sgml
index 693f6380ef1badf72122f065bc1575d358a9f70c..b1b5aa1324807411cf96ea872e12a15d77bf60ee 100644
--- a/doc/src/sgml/syntax.sgml
+++ b/doc/src/sgml/syntax.sgml
@@ -1,5 +1,5 @@
 <!--
-$PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.93 2004/06/07 04:04:47 tgl Exp $
+$PostgreSQL: pgsql/doc/src/sgml/syntax.sgml,v 1.94 2004/06/16 01:26:38 tgl Exp $
 -->
 
 <chapter id="sql-syntax">
@@ -1319,7 +1319,7 @@ CAST ( <replaceable>expression</replaceable> AS <replaceable>type</replaceable>
    <para>
     When a cast is applied to a value expression of a known type, it
     represents a run-time type conversion.  The cast will succeed only
-    if a suitable type conversion function is available.  Notice that this
+    if a suitable type conversion operation has been defined.  Notice that this
     is subtly different from the use of casts with constants, as shown in
     <xref linkend="sql-syntax-constants-generic">.  A cast applied to an
     unadorned string literal represents the initial assignment of a type
diff --git a/src/backend/commands/copy.c b/src/backend/commands/copy.c
index 77e7d1b18557fe423e83dda5d72a2d242573f795..4a05b40eea8d3f933206d41e4426b9e105be2266 100644
--- a/src/backend/commands/copy.c
+++ b/src/backend/commands/copy.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.226 2004/06/06 00:41:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/copy.c,v 1.227 2004/06/16 01:26:42 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1565,7 +1565,7 @@ CopyFrom(Relation rel, List *attnumlist, bool binary, bool oids,
 			node = coerce_to_domain((Node *) prm,
 									prm->paramtype,
 									attr[attnum - 1]->atttypid,
-									COERCE_IMPLICIT_CAST);
+									COERCE_IMPLICIT_CAST, false);
 
 			constraintexprs[attnum - 1] = ExecPrepareExpr((Expr *) node,
 												 estate);
diff --git a/src/backend/commands/functioncmds.c b/src/backend/commands/functioncmds.c
index 757869a925ae89bb9e7e445ddcc724e227cf792f..7747eb1d7766ed2e3aa34ed3b988b6cccd253c0b 100644
--- a/src/backend/commands/functioncmds.c
+++ b/src/backend/commands/functioncmds.c
@@ -10,7 +10,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.47 2004/05/26 04:41:11 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/commands/functioncmds.c,v 1.48 2004/06/16 01:26:42 tgl Exp $
  *
  * DESCRIPTION
  *	  These routines take the parse tree and pick out the
@@ -809,6 +809,7 @@ CreateCast(CreateCastStmt *stmt)
 	Oid			sourcetypeid;
 	Oid			targettypeid;
 	Oid			funcid;
+	int			nargs;
 	char		castcontext;
 	Relation	relation;
 	HeapTuple	tuple;
@@ -831,11 +832,6 @@ CreateCast(CreateCastStmt *stmt)
 				 errmsg("target data type %s does not exist",
 						TypeNameToString(stmt->targettype))));
 
-	if (sourcetypeid == targettypeid)
-		ereport(ERROR,
-				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-		  errmsg("source data type and target data type are the same")));
-
 	/* No shells, no pseudo-types allowed */
 	if (!get_typisdefined(sourcetypeid))
 		ereport(ERROR,
@@ -885,14 +881,23 @@ CreateCast(CreateCastStmt *stmt)
 			elog(ERROR, "cache lookup failed for function %u", funcid);
 
 		procstruct = (Form_pg_proc) GETSTRUCT(tuple);
-		if (procstruct->pronargs != 1)
+		nargs = procstruct->pronargs;
+		if (nargs < 1 || nargs > 3)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
-					 errmsg("cast function must take one argument")));
+					 errmsg("cast function must take one to three arguments")));
 		if (procstruct->proargtypes[0] != sourcetypeid)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
 					 errmsg("argument of cast function must match source data type")));
+		if (nargs > 1 && procstruct->proargtypes[1] != INT4OID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("second argument of cast function must be type integer")));
+		if (nargs > 2 && procstruct->proargtypes[2] != BOOLOID)
+			ereport(ERROR,
+					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+					 errmsg("third argument of cast function must be type boolean")));
 		if (procstruct->prorettype != targettypeid)
 			ereport(ERROR,
 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
@@ -931,6 +936,7 @@ CreateCast(CreateCastStmt *stmt)
 
 		/* indicates binary coercibility */
 		funcid = InvalidOid;
+		nargs = 0;
 
 		/*
 		 * Must be superuser to create binary-compatible casts, since
@@ -957,6 +963,15 @@ CreateCast(CreateCastStmt *stmt)
 					 errmsg("source and target data types are not physically compatible")));
 	}
 
+	/*
+	 * Allow source and target types to be same only for length coercion
+	 * functions.  We assume a multi-arg function does length coercion.
+	 */
+	if (sourcetypeid == targettypeid && nargs < 2)
+		ereport(ERROR,
+				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
+		  errmsg("source data type and target data type are the same")));
+
 	/* convert CoercionContext enum to char value for castcontext */
 	switch (stmt->context)
 	{
diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c
index a6b48450a49d9b0ed0812b41d9aaae25226c42db..cc0048972bfe73157d1bf87eb490c68d7d768c09 100644
--- a/src/backend/optimizer/prep/preptlist.c
+++ b/src/backend/optimizer/prep/preptlist.c
@@ -15,7 +15,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.68 2004/05/30 23:40:29 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/optimizer/prep/preptlist.c,v 1.69 2004/06/16 01:26:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -199,7 +199,8 @@ expand_targetlist(List *tlist, int command_type,
 						new_expr = coerce_to_domain(new_expr,
 													InvalidOid,
 													atttype,
-													COERCE_IMPLICIT_CAST);
+													COERCE_IMPLICIT_CAST,
+													false);
 					}
 					else
 					{
diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c
index a2cd7dccc15b7cc5de196ecf2c28a65f1e4c7c80..b3a6b67f5c399a0c100744b1bfea15d1aa4f1e4a 100644
--- a/src/backend/parser/parse_clause.c
+++ b/src/backend/parser/parse_clause.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.132 2004/06/09 19:08:17 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_clause.c,v 1.133 2004/06/16 01:26:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -915,11 +915,11 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
 	 * Insert coercion functions if needed.  Note that a difference in
 	 * typmod can only happen if input has typmod but outcoltypmod is -1.
 	 * In that case we insert a RelabelType to clearly mark that result's
-	 * typmod is not same as input.
+	 * typmod is not same as input.  We never need coerce_type_typmod.
 	 */
 	if (l_colvar->vartype != outcoltype)
 		l_node = coerce_type(pstate, (Node *) l_colvar, l_colvar->vartype,
-							 outcoltype,
+							 outcoltype, outcoltypmod,
 							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
 	else if (l_colvar->vartypmod != outcoltypmod)
 		l_node = (Node *) makeRelabelType((Expr *) l_colvar,
@@ -930,7 +930,7 @@ buildMergedJoinVar(ParseState *pstate, JoinType jointype,
 
 	if (r_colvar->vartype != outcoltype)
 		r_node = coerce_type(pstate, (Node *) r_colvar, r_colvar->vartype,
-							 outcoltype,
+							 outcoltype, outcoltypmod,
 							 COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
 	else if (r_colvar->vartypmod != outcoltypmod)
 		r_node = (Node *) makeRelabelType((Expr *) r_colvar,
@@ -1276,7 +1276,7 @@ transformGroupClause(ParseState *pstate, List *grouplist,
 		if (restype == UNKNOWNOID)
 		{
 			tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr,
-											 restype, TEXTOID,
+											 restype, TEXTOID, -1,
 											 COERCION_IMPLICIT,
 											 COERCE_IMPLICIT_CAST);
 			restype = tle->resdom->restype = TEXTOID;
@@ -1528,7 +1528,7 @@ addTargetToSortList(ParseState *pstate, TargetEntry *tle,
 		if (restype == UNKNOWNOID && resolveUnknown)
 		{
 			tle->expr = (Expr *) coerce_type(pstate, (Node *) tle->expr,
-											 restype, TEXTOID,
+											 restype, TEXTOID, -1,
 											 COERCION_IMPLICIT,
 											 COERCE_IMPLICIT_CAST);
 			restype = tle->resdom->restype = TEXTOID;
diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index b12d1854aa8236dffa8503a45b7eb507da5a8c68..3878d07d5a189f8be32b41bb0892b06676359e45 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.118 2004/06/06 00:41:26 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.119 2004/06/16 01:26:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -33,8 +33,13 @@
 
 
 static Node *coerce_type_typmod(Node *node,
-				   Oid targetTypeId, int32 targetTypMod,
-				   CoercionForm cformat, bool isExplicit);
+								Oid targetTypeId, int32 targetTypMod,
+								CoercionForm cformat, bool isExplicit,
+								bool hideInputCoercion);
+static void hide_coercion_node(Node *node);
+static Node *build_coercion_expression(Node *node, Oid funcId,
+									   Oid targetTypeId, int32 targetTypMod,
+									   CoercionForm cformat, bool isExplicit);
 static Node *coerce_record_to_complex(ParseState *pstate, Node *node,
 									  Oid targetTypeId,
 									  CoercionContext ccontext,
@@ -67,22 +72,27 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
 					  CoercionContext ccontext,
 					  CoercionForm cformat)
 {
-	if (can_coerce_type(1, &exprtype, &targettype, ccontext))
-		expr = coerce_type(pstate, expr, exprtype, targettype,
-						   ccontext, cformat);
-	else
-		expr = NULL;
+	Node	*result;
+
+	if (!can_coerce_type(1, &exprtype, &targettype, ccontext))
+		return NULL;
+
+	result = coerce_type(pstate, expr, exprtype,
+						 targettype, targettypmod,
+						 ccontext, cformat);
 
 	/*
 	 * If the target is a fixed-length type, it may need a length coercion
-	 * as well as a type coercion.
+	 * as well as a type coercion.  If we find ourselves adding both,
+	 * force the inner coercion node to implicit display form.
 	 */
-	if (expr != NULL)
-		expr = coerce_type_typmod(expr, targettype, targettypmod,
-								  cformat,
-								  (cformat != COERCE_IMPLICIT_CAST));
+	result = coerce_type_typmod(result,
+								targettype, targettypmod,
+								cformat,
+								(cformat != COERCE_IMPLICIT_CAST),
+								(result != expr && !IsA(result, Const)));
 
-	return expr;
+	return result;
 }
 
 
@@ -93,10 +103,13 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
  * The caller should already have determined that the coercion is possible;
  * see can_coerce_type.
  *
- * No coercion to a typmod (length) is performed here.	The caller must
- * call coerce_type_typmod as well, if a typmod constraint is wanted.
+ * Normally, no coercion to a typmod (length) is performed here.  The caller
+ * must call coerce_type_typmod as well, if a typmod constraint is wanted.
  * (But if the target type is a domain, it may internally contain a
  * typmod constraint, which will be applied inside coerce_to_domain.)
+ * In some cases pg_cast specifies a type coercion function that also
+ * applies length conversion, and in those cases only, the result will
+ * already be properly coerced to the specified typmod.
  *
  * pstate is only used in the case that we are able to resolve the type of
  * a previously UNKNOWN Param.	It is okay to pass pstate = NULL if the
@@ -104,7 +117,7 @@ coerce_to_target_type(ParseState *pstate, Node *expr, Oid exprtype,
  */
 Node *
 coerce_type(ParseState *pstate, Node *node,
-			Oid inputTypeId, Oid targetTypeId,
+			Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
 			CoercionContext ccontext, CoercionForm cformat)
 {
 	Node	   *result;
@@ -178,7 +191,7 @@ coerce_type(ParseState *pstate, Node *node,
 		/* If target is a domain, apply constraints. */
 		if (targetTyptype == 'd')
 			result = coerce_to_domain(result, InvalidOid, targetTypeId,
-									  cformat);
+									  cformat, false);
 
 		ReleaseSysCache(targetType);
 
@@ -240,13 +253,14 @@ coerce_type(ParseState *pstate, Node *node,
 			 * Generate an expression tree representing run-time
 			 * application of the conversion function.	If we are dealing
 			 * with a domain target type, the conversion function will
-			 * yield the base type.
+			 * yield the base type (and we assume targetTypeMod must be -1).
 			 */
 			Oid			baseTypeId = getBaseType(targetTypeId);
 
-			result = (Node *) makeFuncExpr(funcId, baseTypeId,
-										   list_make1(node),
-										   cformat);
+			result = build_coercion_expression(node, funcId,
+											   baseTypeId, targetTypeMod,
+											   cformat,
+											   (cformat != COERCE_IMPLICIT_CAST));
 
 			/*
 			 * If domain, coerce to the domain type and relabel with
@@ -254,7 +268,7 @@ coerce_type(ParseState *pstate, Node *node,
 			 */
 			if (targetTypeId != baseTypeId)
 				result = coerce_to_domain(result, baseTypeId, targetTypeId,
-										  cformat);
+										  cformat, true);
 		}
 		else
 		{
@@ -269,7 +283,7 @@ coerce_type(ParseState *pstate, Node *node,
 			 * then we won't need a RelabelType node.
 			 */
 			result = coerce_to_domain(node, InvalidOid, targetTypeId,
-									  cformat);
+									  cformat, false);
 			if (result == node)
 			{
 				/*
@@ -409,11 +423,13 @@ can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
  *		has not bothered to look this up)
  * 'typeId': target type to coerce to
  * 'cformat': coercion format
+ * 'hideInputCoercion': if true, hide the input coercion under this one.
  *
  * If the target type isn't a domain, the given 'arg' is returned as-is.
  */
 Node *
-coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
+coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
+				 CoercionForm cformat, bool hideInputCoercion)
 {
 	CoerceToDomain *result;
 	int32		typmod;
@@ -426,6 +442,10 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
 	if (baseTypeId == typeId)
 		return arg;
 
+	/* Suppress display of nested coercion steps */
+	if (hideInputCoercion)
+		hide_coercion_node(arg);
+
 	/*
 	 * If the domain applies a typmod to its base type, build the
 	 * appropriate coercion step.  Mark it implicit for display purposes,
@@ -444,7 +464,8 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
 	if (typmod >= 0)
 		arg = coerce_type_typmod(arg, baseTypeId, typmod,
 								 COERCE_IMPLICIT_CAST,
-								 (cformat != COERCE_IMPLICIT_CAST));
+								 (cformat != COERCE_IMPLICIT_CAST),
+								 false);
 
 	/*
 	 * Now build the domain coercion node.	This represents run-time
@@ -473,57 +494,142 @@ coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId, CoercionForm cformat)
  * The caller must have already ensured that the value is of the correct
  * type, typically by applying coerce_type.
  *
+ * cformat determines the display properties of the generated node (if any),
+ * while isExplicit may affect semantics.  If hideInputCoercion is true
+ * *and* we generate a node, the input node is forced to IMPLICIT display
+ * form, so that only the typmod coercion node will be visible when
+ * displaying the expression.
+ *
  * NOTE: this does not need to work on domain types, because any typmod
  * coercion for a domain is considered to be part of the type coercion
  * needed to produce the domain value in the first place.  So, no getBaseType.
  */
 static Node *
 coerce_type_typmod(Node *node, Oid targetTypeId, int32 targetTypMod,
-				   CoercionForm cformat, bool isExplicit)
+				   CoercionForm cformat, bool isExplicit,
+				   bool hideInputCoercion)
 {
 	Oid			funcId;
-	int			nargs;
 
 	/*
 	 * A negative typmod is assumed to mean that no coercion is wanted.
+	 * Also, skip coercion if already done.
 	 */
 	if (targetTypMod < 0 || targetTypMod == exprTypmod(node))
 		return node;
 
-	funcId = find_typmod_coercion_function(targetTypeId, &nargs);
+	funcId = find_typmod_coercion_function(targetTypeId);
 
 	if (OidIsValid(funcId))
 	{
-		List	   *args;
-		Const	   *cons;
+		/* Suppress display of nested coercion steps */
+		if (hideInputCoercion)
+			hide_coercion_node(node);
+
+		node = build_coercion_expression(node, funcId,
+										 targetTypeId, targetTypMod,
+										 cformat, isExplicit);
+	}
 
-		/* Pass given value, plus target typmod as an int4 constant */
+	return node;
+}
+
+/*
+ * Mark a coercion node as IMPLICIT so it will never be displayed by
+ * ruleutils.c.  We use this when we generate a nest of coercion nodes
+ * to implement what is logically one conversion; the inner nodes are
+ * forced to IMPLICIT_CAST format.  This does not change their semantics,
+ * only display behavior.
+ *
+ * It is caller error to call this on something that doesn't have a
+ * CoercionForm field.
+ */
+static void
+hide_coercion_node(Node *node)
+{
+	if (IsA(node, FuncExpr))
+		((FuncExpr *) node)->funcformat = COERCE_IMPLICIT_CAST;
+	else if (IsA(node, RelabelType))
+		((RelabelType *) node)->relabelformat = COERCE_IMPLICIT_CAST;
+	else if (IsA(node, RowExpr))
+		((RowExpr *) node)->row_format = COERCE_IMPLICIT_CAST;
+	else if (IsA(node, CoerceToDomain))
+		((CoerceToDomain *) node)->coercionformat = COERCE_IMPLICIT_CAST;
+	else
+		elog(ERROR, "unsupported node type: %d", (int) nodeTag(node));
+}
+
+/*
+ * build_coercion_expression()
+ *		Construct a function-call expression for applying a pg_cast entry.
+ *
+ * This is used for both type-coercion and length-coercion functions,
+ * since there is no difference in terms of the calling convention.
+ */
+static Node *
+build_coercion_expression(Node *node, Oid funcId,
+						  Oid targetTypeId, int32 targetTypMod,
+						  CoercionForm cformat, bool isExplicit)
+{
+	HeapTuple	tp;
+	Form_pg_proc procstruct;
+	int			nargs;
+	List	   *args;
+	Const	   *cons;
+
+	tp = SearchSysCache(PROCOID,
+						ObjectIdGetDatum(funcId),
+						0, 0, 0);
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup failed for function %u", funcId);
+	procstruct = (Form_pg_proc) GETSTRUCT(tp);
+
+	/*
+	 * Asserts essentially check that function is a legal coercion function.
+	 * We can't make the seemingly obvious tests on prorettype and
+	 * proargtypes[0], because of various binary-compatibility cases.
+	 */
+	/* Assert(targetTypeId == procstruct->prorettype); */
+	Assert(!procstruct->proretset);
+	Assert(!procstruct->proisagg);
+	nargs = procstruct->pronargs;
+	Assert(nargs >= 1 && nargs <= 3);
+	/* Assert(procstruct->proargtypes[0] == exprType(node)); */
+	Assert(nargs < 2 || procstruct->proargtypes[1] == INT4OID);
+	Assert(nargs < 3 || procstruct->proargtypes[2] == BOOLOID);
+
+	ReleaseSysCache(tp);
+
+	args = list_make1(node);
+
+	if (nargs >= 2)
+	{
+		/* Pass target typmod as an int4 constant */
 		cons = makeConst(INT4OID,
 						 sizeof(int32),
 						 Int32GetDatum(targetTypMod),
 						 false,
 						 true);
 
-		args = list_make2(node, cons);
+		args = lappend(args, cons);
+	}
 
-		if (nargs == 3)
-		{
-			/* Pass it a boolean isExplicit parameter, too */
-			cons = makeConst(BOOLOID,
-							 sizeof(bool),
-							 BoolGetDatum(isExplicit),
-							 false,
-							 true);
-
-			args = lappend(args, cons);
-		}
+	if (nargs == 3)
+	{
+		/* Pass it a boolean isExplicit parameter, too */
+		cons = makeConst(BOOLOID,
+						 sizeof(bool),
+						 BoolGetDatum(isExplicit),
+						 false,
+						 true);
 
-		node = (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);
+		args = lappend(args, cons);
 	}
 
-	return node;
+	return (Node *) makeFuncExpr(funcId, targetTypeId, args, cformat);
 }
 
+
 /*
  * coerce_record_to_complex
  *		Coerce a RECORD to a specific composite type.
@@ -803,7 +909,7 @@ coerce_to_common_type(ParseState *pstate, Node *node,
 	if (inputTypeId == targetTypeId)
 		return node;			/* no work */
 	if (can_coerce_type(1, &inputTypeId, &targetTypeId, COERCION_IMPLICIT))
-		node = coerce_type(pstate, node, inputTypeId, targetTypeId,
+		node = coerce_type(pstate, node, inputTypeId, targetTypeId, -1,
 						   COERCION_IMPLICIT, COERCE_IMPLICIT_CAST);
 	else
 		ereport(ERROR,
@@ -1528,8 +1634,8 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
 	{
 		/*
 		 * If there's no pg_cast entry, perhaps we are dealing with a pair
-		 * of array types.	If so, and if the element types have a
-		 * suitable cast, use array_type_coerce().
+		 * of array types.	If so, and if the element types have a suitable
+		 * cast, use array_type_coerce() or array_type_length_coerce().
 		 */
 		Oid			targetElemType;
 		Oid			sourceElemType;
@@ -1541,7 +1647,23 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
 			if (find_coercion_pathway(targetElemType, sourceElemType,
 									  ccontext, &elemfuncid))
 			{
-				*funcid = F_ARRAY_TYPE_COERCE;
+				if (!OidIsValid(elemfuncid))
+				{
+					/* binary-compatible element type conversion */
+					*funcid = F_ARRAY_TYPE_COERCE;
+				}
+				else
+				{
+					/* does the function take a typmod arg? */
+					Oid		argtypes[FUNC_MAX_ARGS];
+					int		nargs;
+
+					(void) get_func_signature(elemfuncid, argtypes, &nargs);
+					if (nargs > 1)
+						*funcid = F_ARRAY_TYPE_LENGTH_COERCE;
+					else
+						*funcid = F_ARRAY_TYPE_COERCE;
+				}
 				result = true;
 			}
 		}
@@ -1554,14 +1676,8 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
 /*
  * find_typmod_coercion_function -- does the given type need length coercion?
  *
- * If the target type possesses a function named for the type
- * and having parameter signature (targettype, int4), we assume that
- * the type requires coercion to its own length and that the said
- * function should be invoked to do that.
- *
- * Alternatively, the length-coercing function may have the signature
- * (targettype, int4, bool).  On success, *nargs is set to report which
- * signature we found.
+ * If the target type possesses a pg_cast function from itself to itself,
+ * it must need length coercion.
  *
  * "bpchar" (ie, char(N)) and "numeric" are examples of such types.
  *
@@ -1569,23 +1685,15 @@ find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
  * function associated directly with the array type, but instead look for
  * one associated with the element type.  If one exists, we report
  * array_length_coerce() as the coercion function to use.
- *
- * This mechanism may seem pretty grotty and in need of replacement by
- * something in pg_cast, but since typmod is only interesting for datatypes
- * that have special handling in the grammar, there's not really much
- * percentage in making it any easier to apply such coercions ...
  */
 Oid
-find_typmod_coercion_function(Oid typeId, int *nargs)
+find_typmod_coercion_function(Oid typeId)
 {
 	Oid			funcid = InvalidOid;
 	bool		isArray = false;
 	Type		targetType;
 	Form_pg_type typeForm;
-	char	   *typname;
-	Oid			typnamespace;
-	Oid			oid_array[FUNC_MAX_ARGS];
-	HeapTuple	ftup;
+	HeapTuple	tuple;
 
 	targetType = typeidType(typeId);
 	typeForm = (Form_pg_type) GETSTRUCT(targetType);
@@ -1597,79 +1705,30 @@ find_typmod_coercion_function(Oid typeId, int *nargs)
 	{
 		/* Yes, switch our attention to the element type */
 		typeId = typeForm->typelem;
-		ReleaseSysCache(targetType);
-		targetType = typeidType(typeId);
-		typeForm = (Form_pg_type) GETSTRUCT(targetType);
 		isArray = true;
 	}
+	ReleaseSysCache(targetType);
 
-	/* Function name is same as type internal name, and in same namespace */
-	typname = NameStr(typeForm->typname);
-	typnamespace = typeForm->typnamespace;
-
-	/* First look for parameters (type, int4) */
-	MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
-	oid_array[0] = typeId;
-	oid_array[1] = INT4OID;
-	*nargs = 2;
-
-	ftup = SearchSysCache(PROCNAMENSP,
-						  CStringGetDatum(typname),
-						  Int16GetDatum(2),
-						  PointerGetDatum(oid_array),
-						  ObjectIdGetDatum(typnamespace));
-	if (HeapTupleIsValid(ftup))
-	{
-		Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
-
-		/* Make sure the function's result type is as expected */
-		if (pform->prorettype == typeId && !pform->proretset &&
-			!pform->proisagg)
-		{
-			/* Okay to use it */
-			funcid = HeapTupleGetOid(ftup);
-		}
-		ReleaseSysCache(ftup);
-	}
+	/* Look in pg_cast */
+	tuple = SearchSysCache(CASTSOURCETARGET,
+						   ObjectIdGetDatum(typeId),
+						   ObjectIdGetDatum(typeId),
+						   0, 0);
 
-	if (!OidIsValid(funcid))
+	if (HeapTupleIsValid(tuple))
 	{
-		/* Didn't find a function, so now try (type, int4, bool) */
-		oid_array[2] = BOOLOID;
-		*nargs = 3;
-
-		ftup = SearchSysCache(PROCNAMENSP,
-							  CStringGetDatum(typname),
-							  Int16GetDatum(3),
-							  PointerGetDatum(oid_array),
-							  ObjectIdGetDatum(typnamespace));
-		if (HeapTupleIsValid(ftup))
-		{
-			Form_pg_proc pform = (Form_pg_proc) GETSTRUCT(ftup);
+		Form_pg_cast castForm = (Form_pg_cast) GETSTRUCT(tuple);
 
-			/* Make sure the function's result type is as expected */
-			if (pform->prorettype == typeId && !pform->proretset &&
-				!pform->proisagg)
-			{
-				/* Okay to use it */
-				funcid = HeapTupleGetOid(ftup);
-			}
-			ReleaseSysCache(ftup);
-		}
+		funcid = castForm->castfunc;
+		ReleaseSysCache(tuple);
 	}
 
-	ReleaseSysCache(targetType);
-
 	/*
 	 * Now, if we did find a coercion function for an array element type,
-	 * report array_length_coerce() as the function to use.  We know it
-	 * takes three arguments always.
+	 * report array_length_coerce() as the function to use.
 	 */
 	if (isArray && OidIsValid(funcid))
-	{
 		funcid = F_ARRAY_LENGTH_COERCE;
-		*nargs = 3;
-	}
 
 	return funcid;
 }
diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c
index 3b4ad7cf8a06ce0636b24ed6c81e478d09550569..6df4547ba2d8932c4ae90c69b0be7b76cfdd6cfe 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.173 2004/06/09 19:08:17 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_expr.c,v 1.174 2004/06/16 01:26:44 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1578,6 +1578,9 @@ exprTypmod(Node *expr)
  *
  * If coercedTypmod is not NULL, the typmod is stored there if the expression
  * is a length-coercion function, else -1 is stored there.
+ *
+ * Note that a combined type-and-length coercion will be treated as a
+ * length coercion by this routine.
  */
 bool
 exprIsLengthCoercion(Node *expr, int32 *coercedTypmod)
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 781c94f8c2f8d6a8ef91015420432050f50682b3..d5d71b67afdbe320db81c7d87ecab5154c702afb 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.170 2004/05/30 23:40:35 neilc Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_func.c,v 1.171 2004/06/16 01:26:45 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -150,7 +150,7 @@ ParseFuncOrColumn(ParseState *pstate, List *funcname, List *fargs,
 		 * these cases, so why duplicate code...
 		 */
 		return coerce_type(pstate, linitial(fargs),
-						   actual_arg_types[0], rettype,
+						   actual_arg_types[0], rettype, -1,
 						   COERCION_EXPLICIT, COERCE_EXPLICIT_CALL);
 	}
 	else if (fdresult == FUNCDETAIL_NORMAL)
@@ -726,11 +726,12 @@ func_get_detail(List *funcname,
 			{
 				Oid			sourceType = argtypes[0];
 				Node	   *arg1 = linitial(fargs);
+				Oid			cfuncid;
 
 				if ((sourceType == UNKNOWNOID && IsA(arg1, Const)) ||
 					(find_coercion_pathway(targetType, sourceType,
-										   COERCION_EXPLICIT, funcid) &&
-					 *funcid == InvalidOid))
+										   COERCION_EXPLICIT, &cfuncid) &&
+					 cfuncid == InvalidOid))
 				{
 					/* Yup, it's a type coercion */
 					*funcid = InvalidOid;
@@ -1122,7 +1123,7 @@ make_fn_arguments(ParseState *pstate,
 			lfirst(current_fargs) = coerce_type(pstate,
 												lfirst(current_fargs),
 												actual_arg_types[i],
-												declared_arg_types[i],
+												declared_arg_types[i], -1,
 												COERCION_IMPLICIT,
 												COERCE_IMPLICIT_CAST);
 		}
diff --git a/src/backend/rewrite/rewriteHandler.c b/src/backend/rewrite/rewriteHandler.c
index 768f8788b7ec6a629ea27d8401186808a8e93431..bccc369dc094fa4147543abdb9fbf41f89494110 100644
--- a/src/backend/rewrite/rewriteHandler.c
+++ b/src/backend/rewrite/rewriteHandler.c
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.139 2004/06/09 19:08:17 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/rewrite/rewriteHandler.c,v 1.140 2004/06/16 01:26:46 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -367,7 +367,8 @@ rewriteTargetList(Query *parsetree, Relation target_relation)
 					new_expr = coerce_to_domain(new_expr,
 												InvalidOid,
 												att_tup->atttypid,
-												COERCE_IMPLICIT_CAST);
+												COERCE_IMPLICIT_CAST,
+												false);
 				}
 			}
 
diff --git a/src/backend/utils/adt/arrayfuncs.c b/src/backend/utils/adt/arrayfuncs.c
index 62361932f985c0eee461cfc2e28d88779ca20cd1..e9951d839a3768a07fbf5ae135aedddd4a962de7 100644
--- a/src/backend/utils/adt/arrayfuncs.c
+++ b/src/backend/utils/adt/arrayfuncs.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.104 2004/06/08 20:28:21 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/arrayfuncs.c,v 1.105 2004/06/16 01:26:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -109,6 +109,11 @@ static void array_insert_slice(int ndim, int *dim, int *lb,
 				   int *st, int *endp, char *srcPtr,
 				   int typlen, bool typbyval, char typalign);
 static int	array_cmp(FunctionCallInfo fcinfo);
+static Datum array_type_length_coerce_internal(ArrayType *src,
+											   int32 desttypmod,
+											   bool isExplicit,
+											   FmgrInfo *fmgr_info);
+
 
 /*---------------------------------------------------------------------
  * array_in :
@@ -1174,82 +1179,6 @@ array_send(PG_FUNCTION_ARGS)
 	PG_RETURN_BYTEA_P(pq_endtypsend(&buf));
 }
 
-/*-------------------------------------------------------------------------
- * array_length_coerce :
- *		  Apply the element type's length-coercion routine to each element
- *		  of the given array.
- *-------------------------------------------------------------------------
- */
-Datum
-array_length_coerce(PG_FUNCTION_ARGS)
-{
-	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
-	int32		len = PG_GETARG_INT32(1);
-	bool		isExplicit = PG_GETARG_BOOL(2);
-	FmgrInfo   *fmgr_info = fcinfo->flinfo;
-	typedef struct
-	{
-		Oid			elemtype;
-		FmgrInfo	coerce_finfo;
-	} alc_extra;
-	alc_extra  *my_extra;
-	FunctionCallInfoData locfcinfo;
-
-	/* If no typmod is provided, shortcircuit the whole thing */
-	if (len < 0)
-		PG_RETURN_ARRAYTYPE_P(v);
-
-	/*
-	 * We arrange to look up the element type's coercion function only
-	 * once per series of calls, assuming the element type doesn't change
-	 * underneath us.
-	 */
-	my_extra = (alc_extra *) fmgr_info->fn_extra;
-	if (my_extra == NULL)
-	{
-		fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
-												 sizeof(alc_extra));
-		my_extra = (alc_extra *) fmgr_info->fn_extra;
-		my_extra->elemtype = InvalidOid;
-	}
-
-	if (my_extra->elemtype != ARR_ELEMTYPE(v))
-	{
-		Oid			funcId;
-		int			nargs;
-
-		funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v), &nargs);
-
-		if (OidIsValid(funcId))
-			fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
-		else
-			my_extra->coerce_finfo.fn_oid = InvalidOid;
-		my_extra->elemtype = ARR_ELEMTYPE(v);
-	}
-
-	/*
-	 * If we didn't find a coercion function, return the array unmodified
-	 * (this should not happen in the normal course of things, but might
-	 * happen if this function is called manually).
-	 */
-	if (my_extra->coerce_finfo.fn_oid == InvalidOid)
-		PG_RETURN_ARRAYTYPE_P(v);
-
-	/*
-	 * Use array_map to apply the function to each array element.
-	 *
-	 * Note: we pass isExplicit whether or not the function wants it ...
-	 */
-	MemSet(&locfcinfo, 0, sizeof(locfcinfo));
-	locfcinfo.flinfo = &my_extra->coerce_finfo;
-	locfcinfo.nargs = 3;
-	locfcinfo.arg[0] = PointerGetDatum(v);
-	locfcinfo.arg[1] = Int32GetDatum(len);
-	locfcinfo.arg[2] = BoolGetDatum(isExplicit);
-
-	return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v));
-}
-
 /*-----------------------------------------------------------------------------
  * array_dims :
  *		  returns the dimensions of the array pointed to by "v", as a "text"
@@ -2879,6 +2808,9 @@ array_insert_slice(int ndim,
  * array_type_coerce -- allow explicit or assignment coercion from
  * one array type to another.
  *
+ * array_type_length_coerce -- the same, for cases where both type and length
+ * coercion are done by a single function on the element type.
+ *
  * Caller should have already verified that the source element type can be
  * coerced into the target element type.
  */
@@ -2886,8 +2818,30 @@ Datum
 array_type_coerce(PG_FUNCTION_ARGS)
 {
 	ArrayType  *src = PG_GETARG_ARRAYTYPE_P(0);
-	Oid			src_elem_type = ARR_ELEMTYPE(src);
 	FmgrInfo   *fmgr_info = fcinfo->flinfo;
+
+	return array_type_length_coerce_internal(src, -1, false, fmgr_info);
+}
+
+Datum
+array_type_length_coerce(PG_FUNCTION_ARGS)
+{
+	ArrayType  *src = PG_GETARG_ARRAYTYPE_P(0);
+	int32		desttypmod = PG_GETARG_INT32(1);
+	bool		isExplicit = PG_GETARG_BOOL(2);
+	FmgrInfo   *fmgr_info = fcinfo->flinfo;
+
+	return array_type_length_coerce_internal(src, desttypmod,
+											 isExplicit, fmgr_info);
+}
+
+static Datum
+array_type_length_coerce_internal(ArrayType *src,
+								  int32 desttypmod,
+								  bool isExplicit,
+								  FmgrInfo *fmgr_info)
+{
+	Oid			src_elem_type = ARR_ELEMTYPE(src);
 	typedef struct
 	{
 		Oid			srctype;
@@ -2946,7 +2900,8 @@ array_type_coerce(PG_FUNCTION_ARGS)
 		{
 			/* should never happen, but check anyway */
 			elog(ERROR, "no conversion function from %s to %s",
-			format_type_be(src_elem_type), format_type_be(tgt_elem_type));
+				 format_type_be(src_elem_type),
+				 format_type_be(tgt_elem_type));
 		}
 		if (OidIsValid(funcId))
 			fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
@@ -2962,23 +2917,103 @@ array_type_coerce(PG_FUNCTION_ARGS)
 	 */
 	if (my_extra->coerce_finfo.fn_oid == InvalidOid)
 	{
-		ArrayType  *result = DatumGetArrayTypePCopy(PG_GETARG_DATUM(0));
+		ArrayType  *result;
 
+		result = (ArrayType *) DatumGetPointer(datumCopy(PointerGetDatum(src),
+														 false, -1));
 		ARR_ELEMTYPE(result) = my_extra->desttype;
 		PG_RETURN_ARRAYTYPE_P(result);
 	}
 
 	/*
 	 * Use array_map to apply the function to each array element.
+	 *
+	 * We pass on the desttypmod and isExplicit flags whether or not the
+	 * function wants them.
 	 */
 	MemSet(&locfcinfo, 0, sizeof(locfcinfo));
 	locfcinfo.flinfo = &my_extra->coerce_finfo;
-	locfcinfo.nargs = 1;
+	locfcinfo.nargs = 3;
 	locfcinfo.arg[0] = PointerGetDatum(src);
+	locfcinfo.arg[1] = Int32GetDatum(desttypmod);
+	locfcinfo.arg[2] = BoolGetDatum(isExplicit);
 
 	return array_map(&locfcinfo, my_extra->srctype, my_extra->desttype);
 }
 
+/*
+ * array_length_coerce -- apply the element type's length-coercion routine
+ *		to each element of the given array.
+ */
+Datum
+array_length_coerce(PG_FUNCTION_ARGS)
+{
+	ArrayType  *v = PG_GETARG_ARRAYTYPE_P(0);
+	int32		desttypmod = PG_GETARG_INT32(1);
+	bool		isExplicit = PG_GETARG_BOOL(2);
+	FmgrInfo   *fmgr_info = fcinfo->flinfo;
+	typedef struct
+	{
+		Oid			elemtype;
+		FmgrInfo	coerce_finfo;
+	} alc_extra;
+	alc_extra  *my_extra;
+	FunctionCallInfoData locfcinfo;
+
+	/* If no typmod is provided, shortcircuit the whole thing */
+	if (desttypmod < 0)
+		PG_RETURN_ARRAYTYPE_P(v);
+
+	/*
+	 * We arrange to look up the element type's coercion function only
+	 * once per series of calls, assuming the element type doesn't change
+	 * underneath us.
+	 */
+	my_extra = (alc_extra *) fmgr_info->fn_extra;
+	if (my_extra == NULL)
+	{
+		fmgr_info->fn_extra = MemoryContextAlloc(fmgr_info->fn_mcxt,
+												 sizeof(alc_extra));
+		my_extra = (alc_extra *) fmgr_info->fn_extra;
+		my_extra->elemtype = InvalidOid;
+	}
+
+	if (my_extra->elemtype != ARR_ELEMTYPE(v))
+	{
+		Oid			funcId;
+
+		funcId = find_typmod_coercion_function(ARR_ELEMTYPE(v));
+
+		if (OidIsValid(funcId))
+			fmgr_info_cxt(funcId, &my_extra->coerce_finfo, fmgr_info->fn_mcxt);
+		else
+			my_extra->coerce_finfo.fn_oid = InvalidOid;
+		my_extra->elemtype = ARR_ELEMTYPE(v);
+	}
+
+	/*
+	 * If we didn't find a coercion function, return the array unmodified
+	 * (this should not happen in the normal course of things, but might
+	 * happen if this function is called manually).
+	 */
+	if (my_extra->coerce_finfo.fn_oid == InvalidOid)
+		PG_RETURN_ARRAYTYPE_P(v);
+
+	/*
+	 * Use array_map to apply the function to each array element.
+	 *
+	 * Note: we pass isExplicit whether or not the function wants it ...
+	 */
+	MemSet(&locfcinfo, 0, sizeof(locfcinfo));
+	locfcinfo.flinfo = &my_extra->coerce_finfo;
+	locfcinfo.nargs = 3;
+	locfcinfo.arg[0] = PointerGetDatum(v);
+	locfcinfo.arg[1] = Int32GetDatum(desttypmod);
+	locfcinfo.arg[2] = BoolGetDatum(isExplicit);
+
+	return array_map(&locfcinfo, ARR_ELEMTYPE(v), ARR_ELEMTYPE(v));
+}
+
 /*
  * accumArrayResult - accumulate one (more) Datum for an array result
  *
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 589db025d29942c85ea39b625f2a5c54500b6593..4caaabd62cdd53c1e250a8238d8dc6e35830a58f 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.171 2004/06/09 19:08:18 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.172 2004/06/16 01:26:47 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -194,7 +194,6 @@ static void get_oper_expr(OpExpr *expr, deparse_context *context);
 static void get_func_expr(FuncExpr *expr, deparse_context *context,
 			  bool showimplicit);
 static void get_agg_expr(Aggref *aggref, deparse_context *context);
-static Node *strip_type_coercion(Node *expr, Oid resultType);
 static void get_const_expr(Const *constval, deparse_context *context);
 static void get_sublink_expr(SubLink *sublink, deparse_context *context);
 static void get_from_clause(Query *query, deparse_context *context);
@@ -2983,22 +2982,13 @@ get_rule_expr(Node *node, deparse_context *context,
 					!showimplicit)
 				{
 					/* don't show the implicit cast */
-					get_rule_expr_paren(arg, context, showimplicit, node);
+					get_rule_expr_paren(arg, context, false, node);
 				}
 				else
 				{
-					/*
-					 * Strip off any type coercions on the input, so we
-					 * don't print redundancies like
-					 * x::bpchar::character(8).
-					 *
-					 * XXX Are there any cases where this is a bad idea?
-					 */
-					arg = strip_type_coercion(arg, relabel->resulttype);
-
 					if (!PRETTY_PAREN(context))
 						appendStringInfoChar(buf, '(');
-					get_rule_expr_paren(arg, context, showimplicit, node);
+					get_rule_expr_paren(arg, context, false, node);
 					if (!PRETTY_PAREN(context))
 						appendStringInfoChar(buf, ')');
 					appendStringInfo(buf, "::%s",
@@ -3206,11 +3196,6 @@ get_rule_expr(Node *node, deparse_context *context,
 				CoerceToDomain *ctest = (CoerceToDomain *) node;
 				Node	   *arg = (Node *) ctest->arg;
 
-				/*
-				 * Any implicit coercion at the top level of the argument
-				 * is presumably due to the domain's own internal typmod
-				 * coercion, so do not force it to be shown.
-				 */
 				if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
 					!showimplicit)
 				{
@@ -3331,7 +3316,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 	if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
 	{
 		get_rule_expr_paren((Node *) linitial(expr->args), context,
-							showimplicit, (Node *) expr);
+							false, (Node *) expr);
 		return;
 	}
 
@@ -3349,17 +3334,9 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
 		/* Get the typmod if this is a length-coercion function */
 		(void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
 
-		/*
-		 * Strip off any type coercions on the input, so we don't print
-		 * redundancies like x::bpchar::character(8).
-		 *
-		 * XXX Are there any cases where this is a bad idea?
-		 */
-		arg = strip_type_coercion(arg, rettype);
-
 		if (!PRETTY_PAREN(context))
 			appendStringInfoChar(buf, '(');
-		get_rule_expr_paren(arg, context, showimplicit, (Node *) expr);
+		get_rule_expr_paren(arg, context, false, (Node *) expr);
 		if (!PRETTY_PAREN(context))
 			appendStringInfoChar(buf, ')');
 		appendStringInfo(buf, "::%s",
@@ -3413,46 +3390,6 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
 }
 
 
-/*
- * strip_type_coercion
- *		Strip any type coercion at the top of the given expression tree,
- *		if it is a coercion to the given datatype.
- *
- * We use this to avoid printing two levels of coercion in situations where
- * the expression tree has a length-coercion node atop a type-coercion node.
- *
- * Note: avoid stripping a length-coercion node, since two successive
- * coercions to different lengths aren't a no-op.  Also, never strip a
- * CoerceToDomain node, even though it might be effectively just RelabelType.
- */
-static Node *
-strip_type_coercion(Node *expr, Oid resultType)
-{
-	if (expr == NULL || exprType(expr) != resultType)
-		return expr;
-
-	if (IsA(expr, RelabelType) &&
-		((RelabelType *) expr)->resulttypmod == -1)
-		return (Node *) ((RelabelType *) expr)->arg;
-
-	if (IsA(expr, FuncExpr))
-	{
-		FuncExpr   *func = (FuncExpr *) expr;
-
-		if (func->funcformat != COERCE_EXPLICIT_CAST &&
-			func->funcformat != COERCE_IMPLICIT_CAST)
-			return expr;		/* don't absorb into upper coercion */
-
-		if (exprIsLengthCoercion(expr, NULL))
-			return expr;
-
-		return (Node *) linitial(func->args);
-	}
-
-	return expr;
-}
-
-
 /* ----------
  * get_const_expr
  *
diff --git a/src/backend/utils/adt/varbit.c b/src/backend/utils/adt/varbit.c
index 81da4fc17499a694f8a47320292197162b8cdc0a..7fd7727e421b3091c98ba7d48ce0f88af33cba4e 100644
--- a/src/backend/utils/adt/varbit.c
+++ b/src/backend/utils/adt/varbit.c
@@ -9,7 +9,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.38 2003/11/29 19:51:59 pgsql Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/varbit.c,v 1.39 2004/06/16 01:26:47 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -1166,32 +1166,58 @@ bitshiftright(PG_FUNCTION_ARGS)
 	PG_RETURN_VARBIT_P(result);
 }
 
-/* This is not defined in any standard. We retain the natural ordering of
+/*
+ * This is not defined in any standard. We retain the natural ordering of
  * bits here, as it just seems more intuitive.
  */
 Datum
 bitfromint4(PG_FUNCTION_ARGS)
 {
 	int32		a = PG_GETARG_INT32(0);
+	int32		typmod = PG_GETARG_INT32(1);
 	VarBit	   *result;
 	bits8	   *r;
-	int			len;
+	int			rlen;
+	int			destbitsleft,
+				srcbitsleft;
 
-	/* allocate enough space for the bits in an int4 */
-	len = VARBITTOTALLEN(sizeof(int4) * BITS_PER_BYTE);
-	result = (VarBit *) palloc(len);
-	VARATT_SIZEP(result) = len;
-	VARBITLEN(result) = sizeof(int4) * BITS_PER_BYTE;
+	if (typmod <= 0)
+		typmod = 1;				/* default bit length */
+
+	rlen = VARBITTOTALLEN(typmod);
+	result = (VarBit *) palloc(rlen);
+	VARATT_SIZEP(result) = rlen;
+	VARBITLEN(result) = typmod;
 
-	/*
-	 * masks and shifts here are just too painful and we know that an int4
-	 * has got 4 bytes
-	 */
 	r = VARBITS(result);
-	r[0] = (bits8) ((a >> (3 * BITS_PER_BYTE)) & BITMASK);
-	r[1] = (bits8) ((a >> (2 * BITS_PER_BYTE)) & BITMASK);
-	r[2] = (bits8) ((a >> (1 * BITS_PER_BYTE)) & BITMASK);
-	r[3] = (bits8) (a & BITMASK);
+	destbitsleft = typmod;
+	srcbitsleft = 32;
+	/* drop any input bits that don't fit */
+	srcbitsleft = Min(srcbitsleft, destbitsleft);
+	/* sign-fill any excess bytes in output */
+	while (destbitsleft >= srcbitsleft + 8)
+	{
+		*r++ = (bits8) ((a < 0) ? BITMASK : 0);
+		destbitsleft -= 8;
+	}
+	/* store first fractional byte */
+	if (destbitsleft > srcbitsleft)
+	{
+		*r++ = (bits8) ((a >> (srcbitsleft - 8)) & BITMASK);
+		destbitsleft -= 8;
+	}
+	/* Now srcbitsleft and destbitsleft are the same, need not track both */
+	/* store whole bytes */
+	while (destbitsleft >= 8)
+	{
+		*r++ = (bits8) ((a >> (destbitsleft - 8)) & BITMASK);
+		destbitsleft -= 8;
+	}
+	/* store last fractional byte */
+	if (destbitsleft > 0)
+	{
+		*r = (bits8) ((a << (8 - destbitsleft)) & BITMASK);
+	}
 
 	PG_RETURN_VARBIT_P(result);
 }
@@ -1204,7 +1230,7 @@ bittoint4(PG_FUNCTION_ARGS)
 	bits8	   *r;
 
 	/* Check that the bit string is not too long */
-	if (VARBITLEN(arg) > sizeof(int4) * BITS_PER_BYTE)
+	if (VARBITLEN(arg) > sizeof(result) * BITS_PER_BYTE)
 		ereport(ERROR,
 				(errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),
 				 errmsg("integer out of range")));
@@ -1224,46 +1250,62 @@ bittoint4(PG_FUNCTION_ARGS)
 Datum
 bitfromint8(PG_FUNCTION_ARGS)
 {
-#ifndef INT64_IS_BUSTED
 	int64		a = PG_GETARG_INT64(0);
+	int32		typmod = PG_GETARG_INT32(1);
 	VarBit	   *result;
 	bits8	   *r;
-	int			len;
+	int			rlen;
+	int			destbitsleft,
+				srcbitsleft;
 
-	/* allocate enough space for the bits in an int64 */
-	len = VARBITTOTALLEN(sizeof(a) * BITS_PER_BYTE);
-	result = (VarBit *) palloc(len);
-	VARATT_SIZEP(result) = len;
-	VARBITLEN(result) = sizeof(a) * BITS_PER_BYTE;
+	if (typmod <= 0)
+		typmod = 1;				/* default bit length */
 
-	/*
-	 * masks and shifts here are just too painful and we know that an
-	 * int64 has got 8 bytes
-	 */
-	r = VARBITS(result);
-	r[0] = (bits8) ((a >> (7 * BITS_PER_BYTE)) & BITMASK);
-	r[1] = (bits8) ((a >> (6 * BITS_PER_BYTE)) & BITMASK);
-	r[2] = (bits8) ((a >> (5 * BITS_PER_BYTE)) & BITMASK);
-	r[3] = (bits8) ((a >> (4 * BITS_PER_BYTE)) & BITMASK);
-	r[4] = (bits8) ((a >> (3 * BITS_PER_BYTE)) & BITMASK);
-	r[5] = (bits8) ((a >> (2 * BITS_PER_BYTE)) & BITMASK);
-	r[6] = (bits8) ((a >> (1 * BITS_PER_BYTE)) & BITMASK);
-	r[7] = (bits8) (a & BITMASK);
+	rlen = VARBITTOTALLEN(typmod);
+	result = (VarBit *) palloc(rlen);
+	VARATT_SIZEP(result) = rlen;
+	VARBITLEN(result) = typmod;
 
-	PG_RETURN_VARBIT_P(result);
+	r = VARBITS(result);
+	destbitsleft = typmod;
+#ifndef INT64_IS_BUSTED
+	srcbitsleft = 64;
 #else
-	ereport(ERROR,
-			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("64-bit integers not supported on this platform")));
-
-	PG_RETURN_NULL();
+	srcbitsleft = 32;			/* don't try to shift more than 32 */
 #endif
+	/* drop any input bits that don't fit */
+	srcbitsleft = Min(srcbitsleft, destbitsleft);
+	/* sign-fill any excess bytes in output */
+	while (destbitsleft >= srcbitsleft + 8)
+	{
+		*r++ = (bits8) ((a < 0) ? BITMASK : 0);
+		destbitsleft -= 8;
+	}
+	/* store first fractional byte */
+	if (destbitsleft > srcbitsleft)
+	{
+		*r++ = (bits8) ((a >> (srcbitsleft - 8)) & BITMASK);
+		destbitsleft -= 8;
+	}
+	/* Now srcbitsleft and destbitsleft are the same, need not track both */
+	/* store whole bytes */
+	while (destbitsleft >= 8)
+	{
+		*r++ = (bits8) ((a >> (destbitsleft - 8)) & BITMASK);
+		destbitsleft -= 8;
+	}
+	/* store last fractional byte */
+	if (destbitsleft > 0)
+	{
+		*r = (bits8) ((a << (8 - destbitsleft)) & BITMASK);
+	}
+
+	PG_RETURN_VARBIT_P(result);
 }
 
 Datum
 bittoint8(PG_FUNCTION_ARGS)
 {
-#ifndef INT64_IS_BUSTED
 	VarBit	   *arg = PG_GETARG_VARBIT_P(0);
 	uint64		result;
 	bits8	   *r;
@@ -1284,13 +1326,6 @@ bittoint8(PG_FUNCTION_ARGS)
 	result >>= VARBITPAD(arg);
 
 	PG_RETURN_INT64(result);
-#else
-	ereport(ERROR,
-			(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
-			 errmsg("64-bit integers not supported on this platform")));
-
-	PG_RETURN_NULL();
-#endif
 }
 
 
diff --git a/src/include/catalog/catversion.h b/src/include/catalog/catversion.h
index 2bebe32b61501c55e53e6371313a39189cbf482b..6fd25b54f7a16b58c60ce7b4dde2cada442abc73 100644
--- a/src/include/catalog/catversion.h
+++ b/src/include/catalog/catversion.h
@@ -37,7 +37,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.235 2004/06/13 21:57:25 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/catversion.h,v 1.236 2004/06/16 01:26:49 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -53,6 +53,6 @@
  */
 
 /*							yyyymmddN */
-#define CATALOG_VERSION_NO	200406131
+#define CATALOG_VERSION_NO	200406151
 
 #endif
diff --git a/src/include/catalog/pg_cast.h b/src/include/catalog/pg_cast.h
index 549fa5f80db9c7ede4f6c761e7a9af1a1aeeae85..069f2e8e60cd8c5553f754cf7b2113fb9c40501f 100644
--- a/src/include/catalog/pg_cast.h
+++ b/src/include/catalog/pg_cast.h
@@ -4,10 +4,13 @@
  *	  definition of the system "type casts" relation (pg_cast)
  *	  along with the relation's initial contents.
  *
+ * As of Postgres 7.5, pg_cast describes not only type coercion functions
+ * but also length coercion functions.
+ *
  *
  * Copyright (c) 2002-2003, PostgreSQL Global Development Group
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.11 2004/03/15 01:13:41 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_cast.h,v 1.12 2004/06/16 01:26:49 tgl Exp $
  *
  * NOTES
  *	  the genbki.sh script reads this file and generates .bki
@@ -43,7 +46,7 @@ typedef enum CoercionCodes
 										 * expression */
 	COERCION_CODE_ASSIGNMENT = 'a',		/* coercion in context of
 										 * assignment */
-	COERCION_CODE_EXPLICIT = 'e'	/* explicit cast operation */
+	COERCION_CODE_EXPLICIT = 'e'		/* explicit cast operation */
 } CoercionCodes;
 
 
@@ -361,4 +364,18 @@ DATA(insert ( 1042 1266  938 e ));
 DATA(insert ( 1700 1042 1688 a ));
 DATA(insert ( 1042 1700 1686 e ));
 
+/*
+ * Length-coercion functions
+ */
+DATA(insert ( 1042 1042  668 i ));
+DATA(insert ( 1043 1043  669 i ));
+DATA(insert ( 1083 1083 1968 i ));
+DATA(insert ( 1114 1114 1961 i ));
+DATA(insert ( 1184 1184 1967 i ));
+DATA(insert ( 1186 1186 1200 i ));
+DATA(insert ( 1266 1266 1969 i ));
+DATA(insert ( 1560 1560 1685 i ));
+DATA(insert ( 1562 1562 1687 i ));
+DATA(insert ( 1700 1700 1703 i ));
+
 #endif   /* PG_CAST_H */
diff --git a/src/include/catalog/pg_proc.h b/src/include/catalog/pg_proc.h
index b7fad1fca529e88167ccedbb83a7b4b4078e8cfe..ecaf82efab1743d6b1436060368f82f134394b65 100644
--- a/src/include/catalog/pg_proc.h
+++ b/src/include/catalog/pg_proc.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.337 2004/06/13 21:57:26 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/pg_proc.h,v 1.338 2004/06/16 01:26:49 tgl Exp $
  *
  * NOTES
  *	  The script catalog/genbki.sh reads this file and generates .bki
@@ -1028,8 +1028,8 @@ DATA(insert OID = 379 (  array_prepend	   PGNSP PGUID 12 f f t f i 2 2277 "2283
 DESCR("prepend element onto front of array");
 DATA(insert OID = 383 (  array_cat		   PGNSP PGUID 12 f f t f i 2 2277 "2277 2277" _null_ array_cat - _null_ ));
 DESCR("concatenate two arrays");
-DATA(insert OID = 384  (  array_coerce	   PGNSP PGUID 12 f f t f i 1 2277 "2277" _null_ array_type_coerce - _null_ ));
-DESCR("coerce array type to another array type");
+DATA(insert OID = 384  (  array_coerce	   PGNSP PGUID 12 f f t f s 1 2277 "2277" _null_ array_type_coerce - _null_ ));
+DESCR("coerce array to another array type");
 DATA(insert OID = 394 (  string_to_array   PGNSP PGUID 12 f f t f i 2 1009 "25 25" _null_ text_to_array - _null_ ));
 DESCR("split delimited text into text[]");
 DATA(insert OID = 395 (  array_to_string   PGNSP PGUID 12 f f t f i 2 25 "2277 25" _null_ array_to_text - _null_ ));
@@ -1587,8 +1587,8 @@ DESCR("convert int8 to text");
 DATA(insert OID = 1290 (  int8			   PGNSP PGUID 12 f f t f i 1 20 "25" _null_  text_int8 - _null_ ));
 DESCR("convert text to int8");
 
-DATA(insert OID = 1291 (  array_length_coerce	PGNSP PGUID 12 f f t f i 3 2277 "2277 23 16" _null_	array_length_coerce - _null_ ));
-DESCR("adjust any array to element typmod length");
+DATA(insert OID = 1291 (  array_length_coerce	PGNSP PGUID 12 f f t f s 3 2277 "2277 23 16" _null_	array_length_coerce - _null_ ));
+DESCR("adjust any array to new element typmod");
 
 DATA(insert OID = 1292 ( tideq			   PGNSP PGUID 12 f f t f i 2 16 "27 27" _null_	tideq - _null_ ));
 DESCR("equal");
@@ -1722,6 +1722,9 @@ DESCR("convert time to interval");
 DATA(insert OID = 1372 (  char_length		 PGNSP PGUID 12 f f t f i 1 23	 "1042" _null_  bpcharlen - _null_ ));
 DESCR("character length");
 
+DATA(insert OID = 1373 (  array_type_length_coerce	PGNSP PGUID 12 f f t f s 3 2277 "2277 23 16" _null_	array_type_length_coerce - _null_ ));
+DESCR("coerce array to another type and adjust element typmod");
+
 DATA(insert OID = 1374 (  octet_length			 PGNSP PGUID 12 f f t f i 1 23	 "25" _null_  textoctetlen - _null_ ));
 DESCR("octet length");
 DATA(insert OID = 1375 (  octet_length			 PGNSP PGUID 12 f f t f i 1 23	 "1042" _null_  bpcharoctetlen - _null_ ));
@@ -2298,7 +2301,7 @@ DATA(insert OID = 1681 (  length			PGNSP PGUID 12 f f t f i 1 23 "1560" _null_	b
 DESCR("bitstring length");
 DATA(insert OID = 1682 (  octet_length		PGNSP PGUID 12 f f t f i 1 23 "1560" _null_	bitoctetlength - _null_ ));
 DESCR("octet length");
-DATA(insert OID = 1683 (  bit				PGNSP PGUID 12 f f t f i 1 1560 "23" _null_	bitfromint4 - _null_ ));
+DATA(insert OID = 1683 (  bit				PGNSP PGUID 12 f f t f i 2 1560 "23 23" _null_	bitfromint4 - _null_ ));
 DESCR("int4 to bitstring");
 DATA(insert OID = 1684 (  int4				PGNSP PGUID 12 f f t f i 1 23 "1560" _null_	bittoint4 - _null_ ));
 DESCR("bitstring to int4");
@@ -2968,7 +2971,7 @@ DESCR("extracts text matching regular expression");
 DATA(insert OID = 2074 (  substring			PGNSP PGUID 14 f f t f i 3 25 "25 25 25" _null_	"select pg_catalog.substring($1, pg_catalog.similar_escape($2, $3))" - _null_ ));
 DESCR("extracts text matching SQL99 regular expression");
 
-DATA(insert OID = 2075 (  bit				PGNSP PGUID 12 f f t f i 1 1560 "20" _null_	bitfromint8 - _null_ ));
+DATA(insert OID = 2075 (  bit				PGNSP PGUID 12 f f t f i 2 1560 "20 23" _null_	bitfromint8 - _null_ ));
 DESCR("int8 to bitstring");
 DATA(insert OID = 2076 (  int8				PGNSP PGUID 12 f f t f i 1 20 "1560" _null_	bittoint8 - _null_ ));
 DESCR("bitstring to int8");
diff --git a/src/include/parser/parse_coerce.h b/src/include/parser/parse_coerce.h
index 8700200b51bf69b05310185284e0b7e478bb1183..c1c83d514d959bd3a61f152b21e89c43da99dc64 100644
--- a/src/include/parser/parse_coerce.h
+++ b/src/include/parser/parse_coerce.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.56 2003/11/29 22:41:09 pgsql Exp $
+ * $PostgreSQL: pgsql/src/include/parser/parse_coerce.h,v 1.57 2004/06/16 01:26:53 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,10 +47,10 @@ extern Node *coerce_to_target_type(ParseState *pstate,
 extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *target_typeids,
 				CoercionContext ccontext);
 extern Node *coerce_type(ParseState *pstate, Node *node,
-			Oid inputTypeId, Oid targetTypeId,
+			Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,
 			CoercionContext ccontext, CoercionForm cformat);
 extern Node *coerce_to_domain(Node *arg, Oid baseTypeId, Oid typeId,
-				 CoercionForm cformat);
+				 CoercionForm cformat, bool hideInputCoercion);
 
 extern Node *coerce_to_boolean(ParseState *pstate, Node *node,
 				  const char *constructName);
@@ -76,6 +76,6 @@ extern Oid resolve_generic_type(Oid declared_type,
 extern bool find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
 					  CoercionContext ccontext,
 					  Oid *funcid);
-extern Oid	find_typmod_coercion_function(Oid typeId, int *nargs);
+extern Oid	find_typmod_coercion_function(Oid typeId);
 
 #endif   /* PARSE_COERCE_H */
diff --git a/src/include/utils/array.h b/src/include/utils/array.h
index 596cf79d4d3849080e39052436db474889751b1c..3114aefe10b0465a3c82a17a4069d7a2338800c1 100644
--- a/src/include/utils/array.h
+++ b/src/include/utils/array.h
@@ -10,7 +10,7 @@
  * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.47 2004/06/06 00:41:28 tgl Exp $
+ * $PostgreSQL: pgsql/src/include/utils/array.h,v 1.48 2004/06/16 01:26:55 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -116,7 +116,6 @@ extern Datum array_in(PG_FUNCTION_ARGS);
 extern Datum array_out(PG_FUNCTION_ARGS);
 extern Datum array_recv(PG_FUNCTION_ARGS);
 extern Datum array_send(PG_FUNCTION_ARGS);
-extern Datum array_length_coerce(PG_FUNCTION_ARGS);
 extern Datum array_eq(PG_FUNCTION_ARGS);
 extern Datum array_ne(PG_FUNCTION_ARGS);
 extern Datum array_lt(PG_FUNCTION_ARGS);
@@ -128,6 +127,8 @@ extern Datum array_dims(PG_FUNCTION_ARGS);
 extern Datum array_lower(PG_FUNCTION_ARGS);
 extern Datum array_upper(PG_FUNCTION_ARGS);
 extern Datum array_type_coerce(PG_FUNCTION_ARGS);
+extern Datum array_type_length_coerce(PG_FUNCTION_ARGS);
+extern Datum array_length_coerce(PG_FUNCTION_ARGS);
 
 extern Datum array_ref(ArrayType *array, int nSubscripts, int *indx,
 		  int arraylen, int elmlen, bool elmbyval, char elmalign,
diff --git a/src/test/regress/expected/opr_sanity.out b/src/test/regress/expected/opr_sanity.out
index 95b3a2b2ebb0e14cb711d02a224e4a97a3dcf528..f6cebd9a57a6eaa4c4d96b8c2d4573cd4ab82873 100644
--- a/src/test/regress/expected/opr_sanity.out
+++ b/src/test/regress/expected/opr_sanity.out
@@ -225,13 +225,28 @@ WHERE p1.prorettype = 'internal'::regtype AND NOT
 (1 row)
 
 -- **************** pg_cast ****************
--- Look for casts from and to the same type.  This is not harmful, but
--- useless.  Also catch bogus values in pg_cast columns (other than
--- cases detected by oidjoins test).
+-- Catch bogus values in pg_cast columns (other than cases detected by
+-- oidjoins test).
 SELECT *
 FROM pg_cast c
-WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
-    OR castcontext NOT IN ('e', 'a', 'i');
+WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i');
+ castsource | casttarget | castfunc | castcontext 
+------------+------------+----------+-------------
+(0 rows)
+
+-- Look for casts to/from the same type that aren't length coercion functions.
+-- (We assume they are length coercions if they take multiple arguments.)
+-- Such entries are not necessarily harmful, but they are useless.
+SELECT *
+FROM pg_cast c
+WHERE castsource = casttarget AND castfunc = 0;
+ castsource | casttarget | castfunc | castcontext 
+------------+------------+----------+-------------
+(0 rows)
+
+SELECT c.*
+FROM pg_cast c, pg_proc p
+WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget;
  castsource | casttarget | castfunc | castcontext 
 ------------+------------+----------+-------------
 (0 rows)
@@ -246,7 +261,7 @@ WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
 SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
-    (p.pronargs <> 1
+    (p.pronargs < 1 OR p.pronargs > 3
      OR NOT (binary_coercible(c.castsource, p.proargtypes[0])
              OR (c.castsource = 'character'::regtype AND
                  p.proargtypes[0] = 'text'::regtype))
@@ -255,6 +270,15 @@ WHERE c.castfunc = p.oid AND
 ------------+------------+----------+-------------
 (0 rows)
 
+SELECT c.*
+FROM pg_cast c, pg_proc p
+WHERE c.castfunc = p.oid AND
+    ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR
+     (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype));
+ castsource | casttarget | castfunc | castcontext 
+------------+------------+----------+-------------
+(0 rows)
+
 -- Look for binary compatible casts that do not have the reverse
 -- direction registered as well, or where the reverse direction is not
 -- also binary compatible.  This is legal, but usually not intended.
diff --git a/src/test/regress/sql/opr_sanity.sql b/src/test/regress/sql/opr_sanity.sql
index 9c17a0cd3e11a409d238ec423813f6c81b827185..78ea8dc0cbf54a68c05079ff44bcac4c96293eac 100644
--- a/src/test/regress/sql/opr_sanity.sql
+++ b/src/test/regress/sql/opr_sanity.sql
@@ -184,14 +184,24 @@ WHERE p1.prorettype = 'internal'::regtype AND NOT
 
 -- **************** pg_cast ****************
 
--- Look for casts from and to the same type.  This is not harmful, but
--- useless.  Also catch bogus values in pg_cast columns (other than
--- cases detected by oidjoins test).
+-- Catch bogus values in pg_cast columns (other than cases detected by
+-- oidjoins test).
 
 SELECT *
 FROM pg_cast c
-WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
-    OR castcontext NOT IN ('e', 'a', 'i');
+WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i');
+
+-- Look for casts to/from the same type that aren't length coercion functions.
+-- (We assume they are length coercions if they take multiple arguments.)
+-- Such entries are not necessarily harmful, but they are useless.
+
+SELECT *
+FROM pg_cast c
+WHERE castsource = casttarget AND castfunc = 0;
+
+SELECT c.*
+FROM pg_cast c, pg_proc p
+WHERE c.castfunc = p.oid AND p.pronargs < 2 AND castsource = casttarget;
 
 -- Look for cast functions that don't have the right signature.  The
 -- argument and result types in pg_proc must be the same as, or binary
@@ -204,12 +214,18 @@ WHERE castsource = casttarget OR castsource = 0 OR casttarget = 0
 SELECT c.*
 FROM pg_cast c, pg_proc p
 WHERE c.castfunc = p.oid AND
-    (p.pronargs <> 1
+    (p.pronargs < 1 OR p.pronargs > 3
      OR NOT (binary_coercible(c.castsource, p.proargtypes[0])
              OR (c.castsource = 'character'::regtype AND
                  p.proargtypes[0] = 'text'::regtype))
      OR NOT binary_coercible(p.prorettype, c.casttarget));
 
+SELECT c.*
+FROM pg_cast c, pg_proc p
+WHERE c.castfunc = p.oid AND
+    ((p.pronargs > 1 AND p.proargtypes[1] != 'int4'::regtype) OR
+     (p.pronargs > 2 AND p.proargtypes[2] != 'bool'::regtype));
+
 -- Look for binary compatible casts that do not have the reverse
 -- direction registered as well, or where the reverse direction is not
 -- also binary compatible.  This is legal, but usually not intended.