User defined I/O conversion casts

Started by Heikki Linnakangasover 17 years ago9 messages
#1Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
1 attachment(s)

Since 8.3, the system provides automatic I/O conversion casts between
the built-in string types and other types, but there's currently no way
for a user to declare additional casts using the I/O functions. You can
always create a simple SQL function to do that, but it seems like we
should provide a direct interface for that. I propose the syntax:

CREATE CAST (<source> AS <target>) WITH INOUT [AS IMPLICIT | AS ASSIGNMENT]

Conveniently, INOUT is already a col_name_keyword, so this works without
any conflicts or new keywords.

This would be very useful for those who want relax some built-in
automatic assignment casts to implicit casts, 8.2 style, as well as
anyone creating a user-defined data type with casts that can be
implemented with the I/O functions.

Patch attached. I'm using a magic OID "1" in pg_cast.castfunc field to
mark these extra I/O conversion casts. Perhaps we want to have a
separate field for that or something, but that was a quick way to get
started.

Anyone else think this is a good idea? Thoughts on the implementation?

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

Attachments:

user-defined-io-casts-1.patchtext/x-diff; name=user-defined-io-casts-1.patchDownload
Index: src/backend/commands/functioncmds.c
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/backend/commands/functioncmds.c,v
retrieving revision 1.98
diff -c -r1.98 functioncmds.c
*** src/backend/commands/functioncmds.c	18 Jul 2008 03:32:52 -0000	1.98
--- src/backend/commands/functioncmds.c	29 Aug 2008 10:56:49 -0000
***************
*** 1474,1479 ****
--- 1474,1481 ----
  
  		ReleaseSysCache(tuple);
  	}
+ 	else if (stmt->coerceviaio)
+ 		funcid = IOCOERCE_FUNCOID;
  	else
  	{
  		int16		typ1len;
***************
*** 1589,1595 ****
  	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  
  	/* dependency on function */
! 	if (OidIsValid(funcid))
  	{
  		referenced.classId = ProcedureRelationId;
  		referenced.objectId = funcid;
--- 1591,1597 ----
  	recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL);
  
  	/* dependency on function */
! 	if (OidIsValid(funcid) && funcid != IOCOERCE_FUNCOID)
  	{
  		referenced.classId = ProcedureRelationId;
  		referenced.objectId = funcid;
Index: src/backend/parser/gram.y
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/backend/parser/gram.y,v
retrieving revision 2.618
diff -c -r2.618 gram.y
*** src/backend/parser/gram.y	18 Jul 2008 03:32:52 -0000	2.618
--- src/backend/parser/gram.y	29 Aug 2008 09:29:22 -0000
***************
*** 4534,4539 ****
--- 4534,4550 ----
  					n->context = (CoercionContext) $10;
  					$$ = (Node *)n;
  				}
+ 			| CREATE CAST '(' Typename AS Typename ')'
+ 					WITH INOUT cast_context
+ 				{
+ 					CreateCastStmt *n = makeNode(CreateCastStmt);
+ 					n->sourcetype = $4;
+ 					n->targettype = $6;
+ 					n->func = NULL;
+ 					n->coerceviaio = true;
+ 					n->context = (CoercionContext) $10;
+ 					$$ = (Node *)n;
+ 				}
  		;
  
  cast_context:  AS IMPLICIT_P					{ $$ = COERCION_IMPLICIT; }
Index: src/backend/parser/parse_coerce.c
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/backend/parser/parse_coerce.c,v
retrieving revision 2.163
diff -c -r2.163 parse_coerce.c
*** src/backend/parser/parse_coerce.c	30 Jul 2008 21:23:17 -0000	2.163
--- src/backend/parser/parse_coerce.c	29 Aug 2008 09:38:51 -0000
***************
*** 1745,1751 ****
  		if (ccontext >= castcontext)
  		{
  			*funcid = castForm->castfunc;
! 			if (OidIsValid(*funcid))
  				result = COERCION_PATH_FUNC;
  			else
  				result = COERCION_PATH_RELABELTYPE;
--- 1745,1756 ----
  		if (ccontext >= castcontext)
  		{
  			*funcid = castForm->castfunc;
! 			if (*funcid == IOCOERCE_FUNCOID)
! 			{
! 				result = COERCION_PATH_COERCEVIAIO;
! 				*funcid = InvalidOid;
! 			}
! 			else if (OidIsValid(*funcid))
  				result = COERCION_PATH_FUNC;
  			else
  				result = COERCION_PATH_RELABELTYPE;
Index: src/include/catalog/pg_cast.h
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/include/catalog/pg_cast.h,v
retrieving revision 1.39
diff -c -r1.39 pg_cast.h
*** src/include/catalog/pg_cast.h	27 Mar 2008 03:57:34 -0000	1.39
--- src/include/catalog/pg_cast.h	29 Aug 2008 09:14:15 -0000
***************
*** 56,61 ****
--- 56,63 ----
  	COERCION_CODE_EXPLICIT = 'e'	/* explicit cast operation */
  } CoercionCodes;
  
+ #define IOCOERCE_FUNCOID 1
+ 
  
  /* ----------------
   *		compiler constants for pg_cast
Index: src/include/nodes/parsenodes.h
===================================================================
RCS file: /home/hlinnaka/pgcvsrepository/pgsql/src/include/nodes/parsenodes.h,v
retrieving revision 1.371
diff -c -r1.371 parsenodes.h
*** src/include/nodes/parsenodes.h	7 Aug 2008 01:11:51 -0000	1.371
--- src/include/nodes/parsenodes.h	29 Aug 2008 09:21:08 -0000
***************
*** 1989,1994 ****
--- 1989,1995 ----
  	TypeName   *targettype;
  	FuncWithArgs *func;
  	CoercionContext context;
+ 	bool		coerceviaio;
  } CreateCastStmt;
  
  /* ----------------------
#2Teodor Sigaev
teodor@sigaev.ru
In reply to: Heikki Linnakangas (#1)
Re: User defined I/O conversion casts

Since 8.3, the system provides automatic I/O conversion casts between
the built-in string types and other types, but there's currently no way
for a user to declare additional casts using the I/O functions. You can
always create a simple SQL function to do that, but it seems like we
should provide a direct interface for that. I propose the syntax:

CREATE CAST (<source> AS <target>) WITH INOUT [AS IMPLICIT | AS ASSIGNMENT]

Anyone else think this is a good idea? Thoughts on the implementation?

Agree, that task causes too often for me. Although using separate boolean column
looks more preferable than special value for OID.

--
Teodor Sigaev E-mail: teodor@sigaev.ru
WWW: http://www.sigaev.ru/

#3Tom Lane
tgl@sss.pgh.pa.us
In reply to: Heikki Linnakangas (#1)
Re: User defined I/O conversion casts

Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:

Patch attached. I'm using a magic OID "1" in pg_cast.castfunc field to
mark these extra I/O conversion casts.

Ugh. That's really unacceptable (doesn't it make the oidjoins
regression test fail?),

I think that as things stand at the moment, you can get I/O conversion
casts to work for a user-defined type by making it be of string
category. Maybe that would be an easier way to get the effect.

regards, tom lane

#4Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Tom Lane (#3)
Re: User defined I/O conversion casts

Tom Lane wrote:

Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:

Patch attached. I'm using a magic OID "1" in pg_cast.castfunc field to
mark these extra I/O conversion casts.

Ugh. That's really unacceptable (doesn't it make the oidjoins
regression test fail?),

Yeah, it does if you create a cast like that. And pg_dump/restore will
need to be taught about it as well.

I think that as things stand at the moment, you can get I/O conversion
casts to work for a user-defined type by making it be of string
category. Maybe that would be an easier way to get the effect.

Hmm. That would be sensible only for types that are, well, strings. Not
if you wanted to make the cast from, say, int4 to text implicit.

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

#5Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Tom Lane (#3)
Re: User defined I/O conversion casts

(Resurrecting an old thread.)

Tom Lane wrote:

Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:

Patch attached. I'm using a magic OID "1" in pg_cast.castfunc field to
mark these extra I/O conversion casts.

Ugh. That's really unacceptable (doesn't it make the oidjoins
regression test fail?),

Yeah, a magical OID clearly has some issues. A new field in pg_cast is
the obvious alternative. How about adding a "castmethod" char field,
with values:
b = binary-compatible cast (CREATE CAST ... WITHOUT FUNCTION)
i = I/O coercion cast (the new beast, CREATE CAST ... WITH INOUT)
f = use cast function specified in castfunc field.

castfunc is 0 for methods b and i.

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

#6Tom Lane
tgl@sss.pgh.pa.us
In reply to: Heikki Linnakangas (#5)
Re: User defined I/O conversion casts

Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:

Yeah, a magical OID clearly has some issues. A new field in pg_cast is
the obvious alternative. How about adding a "castmethod" char field,
with values:
b = binary-compatible cast (CREATE CAST ... WITHOUT FUNCTION)
i = I/O coercion cast (the new beast, CREATE CAST ... WITH INOUT)
f = use cast function specified in castfunc field.
castfunc is 0 for methods b and i.

Seems sane to me. If you do that, please add a check in the opr_sanity
regression test that castfunc is nonzero if and only if castmethod is f.

regards, tom lane

#7Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Tom Lane (#6)
Re: User defined I/O conversion casts

Tom Lane wrote:

Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:

Yeah, a magical OID clearly has some issues. A new field in pg_cast is
the obvious alternative. How about adding a "castmethod" char field,
with values:
b = binary-compatible cast (CREATE CAST ... WITHOUT FUNCTION)
i = I/O coercion cast (the new beast, CREATE CAST ... WITH INOUT)
f = use cast function specified in castfunc field.
castfunc is 0 for methods b and i.

Seems sane to me. If you do that, please add a check in the opr_sanity
regression test that castfunc is nonzero if and only if castmethod is f.

Ok, will do.

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

#8Heikki Linnakangas
heikki.linnakangas@enterprisedb.com
In reply to: Tom Lane (#6)
1 attachment(s)
Re: User defined I/O conversion casts

Tom Lane wrote:

Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:

Yeah, a magical OID clearly has some issues. A new field in pg_cast is
the obvious alternative. How about adding a "castmethod" char field,
with values:
b = binary-compatible cast (CREATE CAST ... WITHOUT FUNCTION)
i = I/O coercion cast (the new beast, CREATE CAST ... WITH INOUT)
f = use cast function specified in castfunc field.
castfunc is 0 for methods b and i.

Seems sane to me. If you do that, please add a check in the opr_sanity
regression test that castfunc is nonzero if and only if castmethod is f.

Here's a patch. It seems pretty straightforward, I'll apply this
tomorrow, barring objections. Note to self: bump the catalog version.

BTW, it looks like we don't have any test cases for the CREATE CAST
command. I think I'll add one.

--
Heikki Linnakangas
EnterpriseDB http://www.enterprisedb.com

Attachments:

user-defined-io-casts-2.patchtext/x-diff; name=user-defined-io-casts-2.patchDownload
*** doc/src/sgml/catalogs.sgml
--- doc/src/sgml/catalogs.sgml
***************
*** 1415,1423 ****
     cannot be deduced from some generic rule.  For example, casting between a
     domain and its base type is not explicitly represented in
     <structname>pg_cast</structname>.  Another important exception is that
!    <quote>I/O conversion casts</>, those performed using a data type's own
!    I/O functions to convert to or from <type>text</> or other string types,
!    are not explicitly represented in <structname>pg_cast</structname>.
    </para>
  
    <table>
--- 1415,1424 ----
     cannot be deduced from some generic rule.  For example, casting between a
     domain and its base type is not explicitly represented in
     <structname>pg_cast</structname>.  Another important exception is that
!    <quote>automatic I/O conversion casts</>, those performed using a data
!    type's own I/O functions to convert to or from <type>text</> or other
!    string types, are not explicitly represented in
!    <structname>pg_cast</structname>.
    </para>
  
    <table>
***************
*** 1454,1461 ****
        <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
        <entry>
         The OID of the function to use to perform this cast.  Zero is
!        stored if the data types are binary coercible (that is, no
!        run-time operation is needed to perform the cast)
        </entry>
       </row>
  
--- 1455,1461 ----
        <entry><literal><link linkend="catalog-pg-proc"><structname>pg_proc</structname></link>.oid</literal></entry>
        <entry>
         The OID of the function to use to perform this cast.  Zero is
!        stored if the cast method doesn't require a function.
        </entry>
       </row>
  
***************
*** 1473,1478 ****
--- 1473,1489 ----
         other cases
        </entry>
       </row>
+      <row>
+       <entry><structfield>castmethod</structfield></entry>
+       <entry><type>char</type></entry>
+       <entry></entry>
+       <entry>
+        Indicates how the cast is performed.
+        <literal>f</> means that the function specified in the <structfield>castfunc</> field is used.
+        <literal>i</> means that the input/output functions are used.
+        <literal>b</> means that the types are binary-coercible, thus no conversion is required
+       </entry>
+      </row>
      </tbody>
     </tgroup>
    </table>
*** doc/src/sgml/ref/create_cast.sgml
--- doc/src/sgml/ref/create_cast.sgml
***************
*** 24,29 **** CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</r
--- 24,33 ----
  CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>)
      WITHOUT FUNCTION
      [ AS ASSIGNMENT | AS IMPLICIT ]
+ 
+ CREATE CAST (<replaceable>sourcetype</replaceable> AS <replaceable>targettype</replaceable>)
+     WITH INOUT
+     [ AS ASSIGNMENT | AS IMPLICIT ]
  </synopsis>
   </refsynopsisdiv>
  
***************
*** 59,64 **** SELECT CAST(42 AS float8);
--- 63,75 ----
    </para>
  
    <para>
+    You can define a cast as an <firstterm>I/O conversion cast</> using
+    the <literal>WITH INOUT</literal> syntax. An I/O conversion cast is
+    performed by invoking the output function of the source data type, and
+    passing the result to the input function of the target data type.
+   </para>
+ 
+   <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> or
***************
*** 200,205 **** SELECT CAST ( 2 AS numeric ) + 4.0;
--- 211,228 ----
      </varlistentry>
  
      <varlistentry>
+      <term><literal>WITH INOUT</literal></term>
+ 
+      <listitem>
+       <para>
+        Indicates that the cast is an I/O conversion cast, performed by
+        invoking the output function of the source data type, and passing the
+        result to the input function of the target data type.
+       </para>
+      </listitem>
+     </varlistentry>
+ 
+     <varlistentry>
       <term><literal>AS ASSIGNMENT</literal></term>
  
       <listitem>
***************
*** 284,296 **** SELECT CAST ( 2 AS numeric ) + 4.0;
     It is normally not necessary to create casts between user-defined types
     and the standard string types (<type>text</>, <type>varchar</>, and
     <type>char(<replaceable>n</>)</type>, as well as user-defined types that
!    are defined to be in the string category).  <productname>PostgreSQL</> will
!    automatically handle a cast to a string type by invoking the other
!    type's output function, or conversely handle a cast from a string type
!    by invoking the other type's input function.  These
!    automatically-provided casts are known as <firstterm>I/O conversion
!    casts</>.  I/O conversion casts to string types are treated as
!    assignment casts, while I/O conversion casts from string types are
     explicit-only.  You can override this behavior by declaring your own
     cast to replace an I/O conversion cast, but usually the only reason to
     do so is if you want the conversion to be more easily invokable than the
--- 307,316 ----
     It is normally not necessary to create casts between user-defined types
     and the standard string types (<type>text</>, <type>varchar</>, and
     <type>char(<replaceable>n</>)</type>, as well as user-defined types that
!    are defined to be in the string category).  <productname>PostgreSQL</>
!    provides automatic I/O conversion casts for that. The automatic casts to
!    string types are treated as assignment casts, while the automatic casts
!    from string types are
     explicit-only.  You can override this behavior by declaring your own
     cast to replace an I/O conversion cast, but usually the only reason to
     do so is if you want the conversion to be more easily invokable than the
*** src/backend/commands/functioncmds.c
--- src/backend/commands/functioncmds.c
***************
*** 1383,1388 **** CreateCast(CreateCastStmt *stmt)
--- 1383,1389 ----
  	Oid			funcid;
  	int			nargs;
  	char		castcontext;
+ 	char		castmethod;
  	Relation	relation;
  	HeapTuple	tuple;
  	Datum		values[Natts_pg_cast];
***************
*** 1415,1421 **** CreateCast(CreateCastStmt *stmt)
--- 1416,1430 ----
  						format_type_be(sourcetypeid),
  						format_type_be(targettypeid))));
  
+ 	/* Detemine the cast method */
  	if (stmt->func != NULL)
+ 		castmethod = COERCION_METHOD_FUNCTION;
+ 	else if(stmt->inout)
+ 		castmethod = COERCION_METHOD_INOUT;
+ 	else
+ 		castmethod = COERCION_METHOD_BINARY;
+ 
+ 	if (castmethod == COERCION_METHOD_FUNCTION)
  	{
  		Form_pg_proc procstruct;
  
***************
*** 1476,1481 **** CreateCast(CreateCastStmt *stmt)
--- 1485,1496 ----
  	}
  	else
  	{
+ 		funcid = InvalidOid;
+ 		nargs = 0;
+ 	}
+ 
+ 	if (castmethod == COERCION_METHOD_BINARY)
+ 	{
  		int16		typ1len;
  		int16		typ2len;
  		bool		typ1byval;
***************
*** 1483,1492 **** CreateCast(CreateCastStmt *stmt)
  		char		typ1align;
  		char		typ2align;
  
- 		/* indicates binary coercibility */
- 		funcid = InvalidOid;
- 		nargs = 0;
- 
  		/*
  		 * Must be superuser to create binary-compatible casts, since
  		 * erroneous casts can easily crash the backend.
--- 1498,1503 ----
***************
*** 1562,1567 **** CreateCast(CreateCastStmt *stmt)
--- 1573,1579 ----
  	values[Anum_pg_cast_casttarget - 1] = ObjectIdGetDatum(targettypeid);
  	values[Anum_pg_cast_castfunc - 1] = ObjectIdGetDatum(funcid);
  	values[Anum_pg_cast_castcontext - 1] = CharGetDatum(castcontext);
+ 	values[Anum_pg_cast_castmethod - 1] = CharGetDatum(castmethod);
  
  	MemSet(nulls, ' ', Natts_pg_cast);
  
*** src/backend/nodes/copyfuncs.c
--- src/backend/nodes/copyfuncs.c
***************
*** 3042,3047 **** _copyCreateCastStmt(CreateCastStmt *from)
--- 3042,3048 ----
  	COPY_NODE_FIELD(targettype);
  	COPY_NODE_FIELD(func);
  	COPY_SCALAR_FIELD(context);
+ 	COPY_SCALAR_FIELD(inout);
  
  	return newnode;
  }
*** src/backend/nodes/equalfuncs.c
--- src/backend/nodes/equalfuncs.c
***************
*** 1666,1671 **** _equalCreateCastStmt(CreateCastStmt *a, CreateCastStmt *b)
--- 1666,1672 ----
  	COMPARE_NODE_FIELD(targettype);
  	COMPARE_NODE_FIELD(func);
  	COMPARE_SCALAR_FIELD(context);
+ 	COMPARE_SCALAR_FIELD(inout);
  
  	return true;
  }
*** src/backend/parser/gram.y
--- src/backend/parser/gram.y
***************
*** 4590,4595 **** CreateCastStmt: CREATE CAST '(' Typename AS Typename ')'
--- 4590,4596 ----
  					n->targettype = $6;
  					n->func = $10;
  					n->context = (CoercionContext) $11;
+ 					n->inout = false;
  					$$ = (Node *)n;
  				}
  			| CREATE CAST '(' Typename AS Typename ')'
***************
*** 4600,4605 **** CreateCastStmt: CREATE CAST '(' Typename AS Typename ')'
--- 4601,4618 ----
  					n->targettype = $6;
  					n->func = NULL;
  					n->context = (CoercionContext) $10;
+ 					n->inout = false;
+ 					$$ = (Node *)n;
+ 				}
+ 			| CREATE CAST '(' Typename AS Typename ')'
+ 					WITH INOUT cast_context
+ 				{
+ 					CreateCastStmt *n = makeNode(CreateCastStmt);
+ 					n->sourcetype = $4;
+ 					n->targettype = $6;
+ 					n->func = NULL;
+ 					n->context = (CoercionContext) $10;
+ 					n->inout = true;
  					$$ = (Node *)n;
  				}
  		;
*** src/backend/parser/parse_coerce.c
--- src/backend/parser/parse_coerce.c
***************
*** 1909,1919 **** find_coercion_pathway(Oid targetTypeId, Oid sourceTypeId,
  		/* Rely on ordering of enum for correct behavior here */
  		if (ccontext >= castcontext)
  		{
! 			*funcid = castForm->castfunc;
! 			if (OidIsValid(*funcid))
! 				result = COERCION_PATH_FUNC;
! 			else
! 				result = COERCION_PATH_RELABELTYPE;
  		}
  
  		ReleaseSysCache(tuple);
--- 1909,1931 ----
  		/* Rely on ordering of enum for correct behavior here */
  		if (ccontext >= castcontext)
  		{
! 			switch (castForm->castmethod)
! 			{
! 				case COERCION_METHOD_FUNCTION:
! 					result = COERCION_PATH_FUNC;
! 					*funcid = castForm->castfunc;
! 					break;
! 				case COERCION_METHOD_INOUT:
! 					result = COERCION_PATH_COERCEVIAIO;
! 					break;
! 				case COERCION_METHOD_BINARY:
! 					result = COERCION_PATH_RELABELTYPE;
! 					break;
! 				default:
! 					elog(ERROR, "unrecognized castmethod: %d",
! 						 (int) castForm->castmethod);
! 					break;
! 			}
  		}
  
  		ReleaseSysCache(tuple);
*** src/bin/pg_dump/pg_dump.c
--- src/bin/pg_dump/pg_dump.c
***************
*** 36,41 **** int			optreset;
--- 36,42 ----
  
  #include "access/attnum.h"
  #include "access/sysattr.h"
+ #include "catalog/pg_cast.h"
  #include "catalog/pg_class.h"
  #include "catalog/pg_proc.h"
  #include "catalog/pg_trigger.h"
***************
*** 4410,4430 **** getCasts(int *numCasts)
  	int			i_casttarget;
  	int			i_castfunc;
  	int			i_castcontext;
  
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, "
! 						  "castsource, casttarget, castfunc, castcontext "
  						  "FROM pg_cast ORDER BY 3,4");
  	}
  	else
  	{
  		appendPQExpBuffer(query, "SELECT 0 as tableoid, p.oid, "
  						  "t1.oid as castsource, t2.oid as casttarget, "
! 						  "p.oid as castfunc, 'e' as castcontext "
  						  "FROM pg_type t1, pg_type t2, pg_proc p "
  						  "WHERE p.pronargs = 1 AND "
  						  "p.proargtypes[0] = t1.oid AND "
--- 4411,4441 ----
  	int			i_casttarget;
  	int			i_castfunc;
  	int			i_castcontext;
+ 	int			i_castmethod;
  
  	/* Make sure we are in proper schema */
  	selectSourceSchema("pg_catalog");
  
! 	if (g_fout->remoteVersion >= 80400)
! 	{
! 		appendPQExpBuffer(query, "SELECT tableoid, oid, "
! 						  "castsource, casttarget, castfunc, castcontext, "
! 						  "castmethod "
! 						  "FROM pg_cast ORDER BY 3,4");
! 	}
! 	else if (g_fout->remoteVersion >= 70300)
  	{
  		appendPQExpBuffer(query, "SELECT tableoid, oid, "
! 						  "castsource, casttarget, castfunc, castcontext, "
! 						  "CASE WHEN castfunc = 0 THEN 'b' ELSE 'f' END "
  						  "FROM pg_cast ORDER BY 3,4");
  	}
  	else
  	{
  		appendPQExpBuffer(query, "SELECT 0 as tableoid, p.oid, "
  						  "t1.oid as castsource, t2.oid as casttarget, "
! 						  "p.oid as castfunc, 'e' as castcontext, "
! 						  "'f' as castmethod "
  						  "FROM pg_type t1, pg_type t2, pg_proc p "
  						  "WHERE p.pronargs = 1 AND "
  						  "p.proargtypes[0] = t1.oid AND "
***************
*** 4447,4452 **** getCasts(int *numCasts)
--- 4458,4464 ----
  	i_casttarget = PQfnumber(res, "casttarget");
  	i_castfunc = PQfnumber(res, "castfunc");
  	i_castcontext = PQfnumber(res, "castcontext");
+ 	i_castmethod = PQfnumber(res, "castmethod");
  
  	for (i = 0; i < ntups; i++)
  	{
***************
*** 4462,4467 **** getCasts(int *numCasts)
--- 4474,4480 ----
  		castinfo[i].casttarget = atooid(PQgetvalue(res, i, i_casttarget));
  		castinfo[i].castfunc = atooid(PQgetvalue(res, i, i_castfunc));
  		castinfo[i].castcontext = *(PQgetvalue(res, i, i_castcontext));
+ 		castinfo[i].castmethod = *(PQgetvalue(res, i, i_castmethod));
  
  		/*
  		 * Try to name cast as concatenation of typnames.  This is only used
***************
*** 7188,7205 **** dumpCast(Archive *fout, CastInfo *cast)
  					  getFormattedTypeName(cast->castsource, zeroAsNone),
  					  getFormattedTypeName(cast->casttarget, zeroAsNone));
  
! 	if (!OidIsValid(cast->castfunc))
! 		appendPQExpBuffer(defqry, "WITHOUT FUNCTION");
! 	else
  	{
! 		/*
! 		 * Always qualify the function name, in case it is not in pg_catalog
! 		 * schema (format_function_signature won't qualify it).
! 		 */
! 		appendPQExpBuffer(defqry, "WITH FUNCTION %s.",
! 						  fmtId(funcInfo->dobj.namespace->dobj.name));
! 		appendPQExpBuffer(defqry, "%s",
! 						  format_function_signature(funcInfo, true));
  	}
  
  	if (cast->castcontext == 'a')
--- 7201,7226 ----
  					  getFormattedTypeName(cast->castsource, zeroAsNone),
  					  getFormattedTypeName(cast->casttarget, zeroAsNone));
  
! 	switch(cast->castmethod)
  	{
! 		case COERCION_METHOD_BINARY:
! 			appendPQExpBuffer(defqry, "WITHOUT FUNCTION");
! 			break;
! 		case COERCION_METHOD_INOUT:
! 			appendPQExpBuffer(defqry, "WITH INOUT");
! 			break;
! 		case COERCION_METHOD_FUNCTION:
! 			/*
! 			 * Always qualify the function name, in case it is not in
! 			 * pg_catalog schema (format_function_signature won't qualify it).
! 			 */
! 			appendPQExpBuffer(defqry, "WITH FUNCTION %s.",
! 							  fmtId(funcInfo->dobj.namespace->dobj.name));
! 			appendPQExpBuffer(defqry, "%s",
! 							  format_function_signature(funcInfo, true));
! 			break;
! 		default:
! 			write_msg(NULL, "WARNING: bogus value in pg_cast.castmethod field\n");
  	}
  
  	if (cast->castcontext == 'a')
*** src/bin/pg_dump/pg_dump.h
--- src/bin/pg_dump/pg_dump.h
***************
*** 376,381 **** typedef struct _castInfo
--- 376,382 ----
  	Oid			casttarget;
  	Oid			castfunc;
  	char		castcontext;
+ 	char		castmethod;
  } CastInfo;
  
  /* InhInfo isn't a DumpableObject, just temporary state */
*** src/include/catalog/pg_cast.h
--- src/include/catalog/pg_cast.h
***************
*** 36,41 **** CATALOG(pg_cast,2605)
--- 36,42 ----
  	Oid			casttarget;		/* destination datatype for cast */
  	Oid			castfunc;		/* cast function; 0 = binary coercible */
  	char		castcontext;	/* contexts in which cast can be used */
+ 	char		castmethod;		/* cast method */
  } FormData_pg_cast;
  
  typedef FormData_pg_cast *Form_pg_cast;
***************
*** 56,71 **** typedef enum CoercionCodes
  	COERCION_CODE_EXPLICIT = 'e'	/* explicit cast operation */
  } CoercionCodes;
  
  
  /* ----------------
   *		compiler constants for pg_cast
   * ----------------
   */
! #define Natts_pg_cast				4
  #define Anum_pg_cast_castsource		1
  #define Anum_pg_cast_casttarget		2
  #define Anum_pg_cast_castfunc		3
  #define Anum_pg_cast_castcontext	4
  
  /* ----------------
   *		initial contents of pg_cast
--- 57,85 ----
  	COERCION_CODE_EXPLICIT = 'e'	/* explicit cast operation */
  } CoercionCodes;
  
+ /*
+  * The allowable values for pg_cast.castmethod are specified by this enum.
+  * Since castcontext is stored as a "char", we use ASCII codes for human
+  * convenience in reading the table.
+  */
+ typedef enum CoercionMethod
+ {
+ 	COERCION_METHOD_FUNCTION = 'f',		/* use a function */
+ 	COERCION_METHOD_BINARY = 'b',		/* types are binary-compatible */
+ 	COERCION_METHOD_INOUT = 'i'			/* use input/output functions */
+ } CoercionMethod;
+ 
  
  /* ----------------
   *		compiler constants for pg_cast
   * ----------------
   */
! #define Natts_pg_cast				5
  #define Anum_pg_cast_castsource		1
  #define Anum_pg_cast_casttarget		2
  #define Anum_pg_cast_castfunc		3
  #define Anum_pg_cast_castcontext	4
+ #define Anum_pg_cast_castmethod		5
  
  /* ----------------
   *		initial contents of pg_cast
***************
*** 80,119 **** typedef enum CoercionCodes
   * int2->int4->int8->numeric->float4->float8, while casts in the
   * reverse direction are assignment-only.
   */
! DATA(insert (	20	 21  714 a ));
! DATA(insert (	20	 23  480 a ));
! DATA(insert (	20	700  652 i ));
! DATA(insert (	20	701  482 i ));
! DATA(insert (	20 1700 1781 i ));
! DATA(insert (	21	 20  754 i ));
! DATA(insert (	21	 23  313 i ));
! DATA(insert (	21	700  236 i ));
! DATA(insert (	21	701  235 i ));
! DATA(insert (	21 1700 1782 i ));
! DATA(insert (	23	 20  481 i ));
! DATA(insert (	23	 21  314 a ));
! DATA(insert (	23	700  318 i ));
! DATA(insert (	23	701  316 i ));
! DATA(insert (	23 1700 1740 i ));
! DATA(insert (  700	 20  653 a ));
! DATA(insert (  700	 21  238 a ));
! DATA(insert (  700	 23  319 a ));
! DATA(insert (  700	701  311 i ));
! DATA(insert (  700 1700 1742 a ));
! DATA(insert (  701	 20  483 a ));
! DATA(insert (  701	 21  237 a ));
! DATA(insert (  701	 23  317 a ));
! DATA(insert (  701	700  312 a ));
! DATA(insert (  701 1700 1743 a ));
! DATA(insert ( 1700	 20 1779 a ));
! DATA(insert ( 1700	 21 1783 a ));
! DATA(insert ( 1700	 23 1744 a ));
! DATA(insert ( 1700	700 1745 i ));
! DATA(insert ( 1700	701 1746 i ));
  
  /* Allow explicit coercions between int4 and bool */
! DATA(insert (	23	16	2557 e ));
! DATA(insert (	16	23	2558 e ));
  
  /*
   * OID category: allow implicit conversion from any integral type (including
--- 94,133 ----
   * int2->int4->int8->numeric->float4->float8, while casts in the
   * reverse direction are assignment-only.
   */
! DATA(insert (	20	 21  714 a f ));
! DATA(insert (	20	 23  480 a f ));
! DATA(insert (	20	700  652 i f ));
! DATA(insert (	20	701  482 i f ));
! DATA(insert (	20 1700 1781 i f ));
! DATA(insert (	21	 20  754 i f ));
! DATA(insert (	21	 23  313 i f ));
! DATA(insert (	21	700  236 i f ));
! DATA(insert (	21	701  235 i f ));
! DATA(insert (	21 1700 1782 i f ));
! DATA(insert (	23	 20  481 i f ));
! DATA(insert (	23	 21  314 a f ));
! DATA(insert (	23	700  318 i f ));
! DATA(insert (	23	701  316 i f ));
! DATA(insert (	23 1700 1740 i f ));
! DATA(insert (  700	 20  653 a f ));
! DATA(insert (  700	 21  238 a f ));
! DATA(insert (  700	 23  319 a f ));
! DATA(insert (  700	701  311 i f ));
! DATA(insert (  700 1700 1742 a f ));
! DATA(insert (  701	 20  483 a f ));
! DATA(insert (  701	 21  237 a f ));
! DATA(insert (  701	 23  317 a f ));
! DATA(insert (  701	700  312 a f ));
! DATA(insert (  701 1700 1743 a f ));
! DATA(insert ( 1700	 20 1779 a f ));
! DATA(insert ( 1700	 21 1783 a f ));
! DATA(insert ( 1700	 23 1744 a f ));
! DATA(insert ( 1700	700 1745 i f ));
! DATA(insert ( 1700	701 1746 i f ));
  
  /* Allow explicit coercions between int4 and bool */
! DATA(insert (	23	16	2557 e f ));
! DATA(insert (	16	23	2558 e f ));
  
  /*
   * OID category: allow implicit conversion from any integral type (including
***************
*** 125,288 **** DATA(insert (	16	23	2558 e ));
   * casts from text and varchar to regclass, which exist mainly to support
   * legacy forms of nextval() and related functions.
   */
! DATA(insert (	20	 26 1287 i ));
! DATA(insert (	21	 26  313 i ));
! DATA(insert (	23	 26    0 i ));
! DATA(insert (	26	 20 1288 a ));
! DATA(insert (	26	 23    0 a ));
! DATA(insert (	26	 24    0 i ));
! DATA(insert (	24	 26    0 i ));
! DATA(insert (	20	 24 1287 i ));
! DATA(insert (	21	 24  313 i ));
! DATA(insert (	23	 24    0 i ));
! DATA(insert (	24	 20 1288 a ));
! DATA(insert (	24	 23    0 a ));
! DATA(insert (	24 2202    0 i ));
! DATA(insert ( 2202	 24    0 i ));
! DATA(insert (	26 2202    0 i ));
! DATA(insert ( 2202	 26    0 i ));
! DATA(insert (	20 2202 1287 i ));
! DATA(insert (	21 2202  313 i ));
! DATA(insert (	23 2202    0 i ));
! DATA(insert ( 2202	 20 1288 a ));
! DATA(insert ( 2202	 23    0 a ));
! DATA(insert (	26 2203    0 i ));
! DATA(insert ( 2203	 26    0 i ));
! DATA(insert (	20 2203 1287 i ));
! DATA(insert (	21 2203  313 i ));
! DATA(insert (	23 2203    0 i ));
! DATA(insert ( 2203	 20 1288 a ));
! DATA(insert ( 2203	 23    0 a ));
! DATA(insert ( 2203 2204    0 i ));
! DATA(insert ( 2204 2203    0 i ));
! DATA(insert (	26 2204    0 i ));
! DATA(insert ( 2204	 26    0 i ));
! DATA(insert (	20 2204 1287 i ));
! DATA(insert (	21 2204  313 i ));
! DATA(insert (	23 2204    0 i ));
! DATA(insert ( 2204	 20 1288 a ));
! DATA(insert ( 2204	 23    0 a ));
! DATA(insert (	26 2205    0 i ));
! DATA(insert ( 2205	 26    0 i ));
! DATA(insert (	20 2205 1287 i ));
! DATA(insert (	21 2205  313 i ));
! DATA(insert (	23 2205    0 i ));
! DATA(insert ( 2205	 20 1288 a ));
! DATA(insert ( 2205	 23    0 a ));
! DATA(insert (	26 2206    0 i ));
! DATA(insert ( 2206	 26    0 i ));
! DATA(insert (	20 2206 1287 i ));
! DATA(insert (	21 2206  313 i ));
! DATA(insert (	23 2206    0 i ));
! DATA(insert ( 2206	 20 1288 a ));
! DATA(insert ( 2206	 23    0 a ));
! DATA(insert (	26 3734    0 i ));
! DATA(insert ( 3734	 26    0 i ));
! DATA(insert (	20 3734 1287 i ));
! DATA(insert (	21 3734  313 i ));
! DATA(insert (	23 3734    0 i ));
! DATA(insert ( 3734	 20 1288 a ));
! DATA(insert ( 3734	 23    0 a ));
! DATA(insert (	26 3769    0 i ));
! DATA(insert ( 3769	 26    0 i ));
! DATA(insert (	20 3769 1287 i ));
! DATA(insert (	21 3769  313 i ));
! DATA(insert (	23 3769    0 i ));
! DATA(insert ( 3769	 20 1288 a ));
! DATA(insert ( 3769	 23    0 a ));
! DATA(insert (	25 2205 1079 i ));
! DATA(insert ( 1043 2205 1079 i ));
  
  /*
   * String category
   */
! DATA(insert (	25 1042    0 i ));
! DATA(insert (	25 1043    0 i ));
! DATA(insert ( 1042	 25  401 i ));
! DATA(insert ( 1042 1043  401 i ));
! DATA(insert ( 1043	 25    0 i ));
! DATA(insert ( 1043 1042    0 i ));
! DATA(insert (	18	 25  946 i ));
! DATA(insert (	18 1042  860 a ));
! DATA(insert (	18 1043  946 a ));
! DATA(insert (	19	 25  406 i ));
! DATA(insert (	19 1042  408 a ));
! DATA(insert (	19 1043 1401 a ));
! DATA(insert (	25	 18  944 a ));
! DATA(insert ( 1042	 18  944 a ));
! DATA(insert ( 1043	 18  944 a ));
! DATA(insert (	25	 19  407 i ));
! DATA(insert ( 1042	 19  409 i ));
! DATA(insert ( 1043	 19 1400 i ));
  
  /* Allow explicit coercions between int4 and "char" */
! DATA(insert (	18	 23   77 e ));
! DATA(insert (	23	 18   78 e ));
  
  /*
   * Datetime category
   */
! DATA(insert (  702 1082 1179 a ));
! DATA(insert (  702 1083 1364 a ));
! DATA(insert (  702 1114 2023 i ));
! DATA(insert (  702 1184 1173 i ));
! DATA(insert (  703 1186 1177 i ));
! DATA(insert ( 1082 1114 2024 i ));
! DATA(insert ( 1082 1184 1174 i ));
! DATA(insert ( 1083 1186 1370 i ));
! DATA(insert ( 1083 1266 2047 i ));
! DATA(insert ( 1114	702 2030 a ));
! DATA(insert ( 1114 1082 2029 a ));
! DATA(insert ( 1114 1083 1316 a ));
! DATA(insert ( 1114 1184 2028 i ));
! DATA(insert ( 1184	702 1180 a ));
! DATA(insert ( 1184 1082 1178 a ));
! DATA(insert ( 1184 1083 2019 a ));
! DATA(insert ( 1184 1114 2027 a ));
! DATA(insert ( 1184 1266 1388 a ));
! DATA(insert ( 1186	703 1194 a ));
! DATA(insert ( 1186 1083 1419 a ));
! DATA(insert ( 1266 1083 2046 a ));
  /* Cross-category casts between int4 and abstime, reltime */
! DATA(insert (	23	702    0 e ));
! DATA(insert (  702	 23    0 e ));
! DATA(insert (	23	703    0 e ));
! DATA(insert (  703	 23    0 e ));
  
  /*
   * Geometric category
   */
! DATA(insert (  601	600 1532 e ));
! DATA(insert (  602	600 1533 e ));
! DATA(insert (  602	604 1449 a ));
! DATA(insert (  603	600 1534 e ));
! DATA(insert (  603	601 1541 e ));
! DATA(insert (  603	604 1448 a ));
! DATA(insert (  603	718 1479 e ));
! DATA(insert (  604	600 1540 e ));
! DATA(insert (  604	602 1447 a ));
! DATA(insert (  604	603 1446 e ));
! DATA(insert (  604	718 1474 e ));
! DATA(insert (  718	600 1416 e ));
! DATA(insert (  718	603 1480 e ));
! DATA(insert (  718	604 1544 e ));
  
  /*
   * INET category
   */
! DATA(insert (  650	869    0 i ));
! DATA(insert (  869	650 1715 a ));
  
  /*
   * BitString category
   */
! DATA(insert ( 1560 1562    0 i ));
! DATA(insert ( 1562 1560    0 i ));
  /* Cross-category casts between bit and int4, int8 */
! DATA(insert (	20 1560 2075 e ));
! DATA(insert (	23 1560 1683 e ));
! DATA(insert ( 1560	 20 2076 e ));
! DATA(insert ( 1560	 23 1684 e ));
  
  /*
   * Cross-category casts to and from TEXT
--- 139,302 ----
   * casts from text and varchar to regclass, which exist mainly to support
   * legacy forms of nextval() and related functions.
   */
! DATA(insert (	20	 26 1287 i f ));
! DATA(insert (	21	 26  313 i f ));
! DATA(insert (	23	 26    0 i b ));
! DATA(insert (	26	 20 1288 a f ));
! DATA(insert (	26	 23    0 a b ));
! DATA(insert (	26	 24    0 i b ));
! DATA(insert (	24	 26    0 i b ));
! DATA(insert (	20	 24 1287 i f ));
! DATA(insert (	21	 24  313 i f ));
! DATA(insert (	23	 24    0 i b ));
! DATA(insert (	24	 20 1288 a f ));
! DATA(insert (	24	 23    0 a b ));
! DATA(insert (	24 2202    0 i b ));
! DATA(insert ( 2202	 24    0 i b ));
! DATA(insert (	26 2202    0 i b ));
! DATA(insert ( 2202	 26    0 i b ));
! DATA(insert (	20 2202 1287 i f ));
! DATA(insert (	21 2202  313 i f ));
! DATA(insert (	23 2202    0 i b ));
! DATA(insert ( 2202	 20 1288 a f ));
! DATA(insert ( 2202	 23    0 a b ));
! DATA(insert (	26 2203    0 i b ));
! DATA(insert ( 2203	 26    0 i b ));
! DATA(insert (	20 2203 1287 i f ));
! DATA(insert (	21 2203  313 i f ));
! DATA(insert (	23 2203    0 i b ));
! DATA(insert ( 2203	 20 1288 a f ));
! DATA(insert ( 2203	 23    0 a b ));
! DATA(insert ( 2203 2204    0 i b ));
! DATA(insert ( 2204 2203    0 i b ));
! DATA(insert (	26 2204    0 i b ));
! DATA(insert ( 2204	 26    0 i b ));
! DATA(insert (	20 2204 1287 i f ));
! DATA(insert (	21 2204  313 i f ));
! DATA(insert (	23 2204    0 i b ));
! DATA(insert ( 2204	 20 1288 a f ));
! DATA(insert ( 2204	 23    0 a b ));
! DATA(insert (	26 2205    0 i b ));
! DATA(insert ( 2205	 26    0 i b ));
! DATA(insert (	20 2205 1287 i f ));
! DATA(insert (	21 2205  313 i f ));
! DATA(insert (	23 2205    0 i b ));
! DATA(insert ( 2205	 20 1288 a f ));
! DATA(insert ( 2205	 23    0 a b ));
! DATA(insert (	26 2206    0 i b ));
! DATA(insert ( 2206	 26    0 i b ));
! DATA(insert (	20 2206 1287 i f ));
! DATA(insert (	21 2206  313 i f ));
! DATA(insert (	23 2206    0 i b ));
! DATA(insert ( 2206	 20 1288 a f ));
! DATA(insert ( 2206	 23    0 a b ));
! DATA(insert (	26 3734    0 i b ));
! DATA(insert ( 3734	 26    0 i b ));
! DATA(insert (	20 3734 1287 i f ));
! DATA(insert (	21 3734  313 i f ));
! DATA(insert (	23 3734    0 i b ));
! DATA(insert ( 3734	 20 1288 a f ));
! DATA(insert ( 3734	 23    0 a b ));
! DATA(insert (	26 3769    0 i b ));
! DATA(insert ( 3769	 26    0 i b ));
! DATA(insert (	20 3769 1287 i f ));
! DATA(insert (	21 3769  313 i f ));
! DATA(insert (	23 3769    0 i b ));
! DATA(insert ( 3769	 20 1288 a f ));
! DATA(insert ( 3769	 23    0 a b ));
! DATA(insert (	25 2205 1079 i f ));
! DATA(insert ( 1043 2205 1079 i f ));
  
  /*
   * String category
   */
! DATA(insert (	25 1042    0 i b ));
! DATA(insert (	25 1043    0 i b ));
! DATA(insert ( 1042	 25  401 i f ));
! DATA(insert ( 1042 1043  401 i f ));
! DATA(insert ( 1043	 25    0 i b ));
! DATA(insert ( 1043 1042    0 i b ));
! DATA(insert (	18	 25  946 i f ));
! DATA(insert (	18 1042  860 a f ));
! DATA(insert (	18 1043  946 a f ));
! DATA(insert (	19	 25  406 i f ));
! DATA(insert (	19 1042  408 a f ));
! DATA(insert (	19 1043 1401 a f ));
! DATA(insert (	25	 18  944 a f ));
! DATA(insert ( 1042	 18  944 a f ));
! DATA(insert ( 1043	 18  944 a f ));
! DATA(insert (	25	 19  407 i f ));
! DATA(insert ( 1042	 19  409 i f ));
! DATA(insert ( 1043	 19 1400 i f ));
  
  /* Allow explicit coercions between int4 and "char" */
! DATA(insert (	18	 23   77 e f ));
! DATA(insert (	23	 18   78 e f ));
  
  /*
   * Datetime category
   */
! DATA(insert (  702 1082 1179 a f ));
! DATA(insert (  702 1083 1364 a f ));
! DATA(insert (  702 1114 2023 i f ));
! DATA(insert (  702 1184 1173 i f ));
! DATA(insert (  703 1186 1177 i f ));
! DATA(insert ( 1082 1114 2024 i f ));
! DATA(insert ( 1082 1184 1174 i f ));
! DATA(insert ( 1083 1186 1370 i f ));
! DATA(insert ( 1083 1266 2047 i f ));
! DATA(insert ( 1114	702 2030 a f ));
! DATA(insert ( 1114 1082 2029 a f ));
! DATA(insert ( 1114 1083 1316 a f ));
! DATA(insert ( 1114 1184 2028 i f ));
! DATA(insert ( 1184	702 1180 a f ));
! DATA(insert ( 1184 1082 1178 a f ));
! DATA(insert ( 1184 1083 2019 a f ));
! DATA(insert ( 1184 1114 2027 a f ));
! DATA(insert ( 1184 1266 1388 a f ));
! DATA(insert ( 1186	703 1194 a f ));
! DATA(insert ( 1186 1083 1419 a f ));
! DATA(insert ( 1266 1083 2046 a f ));
  /* Cross-category casts between int4 and abstime, reltime */
! DATA(insert (	23	702    0 e b ));
! DATA(insert (  702	 23    0 e b ));
! DATA(insert (	23	703    0 e b ));
! DATA(insert (  703	 23    0 e b ));
  
  /*
   * Geometric category
   */
! DATA(insert (  601	600 1532 e f ));
! DATA(insert (  602	600 1533 e f ));
! DATA(insert (  602	604 1449 a f ));
! DATA(insert (  603	600 1534 e f ));
! DATA(insert (  603	601 1541 e f ));
! DATA(insert (  603	604 1448 a f ));
! DATA(insert (  603	718 1479 e f ));
! DATA(insert (  604	600 1540 e f ));
! DATA(insert (  604	602 1447 a f ));
! DATA(insert (  604	603 1446 e f ));
! DATA(insert (  604	718 1474 e f ));
! DATA(insert (  718	600 1416 e f ));
! DATA(insert (  718	603 1480 e f ));
! DATA(insert (  718	604 1544 e f ));
  
  /*
   * INET category
   */
! DATA(insert (  650	869    0 i b ));
! DATA(insert (  869	650 1715 a f ));
  
  /*
   * BitString category
   */
! DATA(insert ( 1560 1562    0 i b ));
! DATA(insert ( 1562 1560    0 i b ));
  /* Cross-category casts between bit and int4, int8 */
! DATA(insert (	20 1560 2075 e f ));
! DATA(insert (	23 1560 1683 e f ));
! DATA(insert ( 1560	 20 2076 e f ));
! DATA(insert ( 1560	 23 1684 e f ));
  
  /*
   * Cross-category casts to and from TEXT
***************
*** 296,341 **** DATA(insert ( 1560	 23 1684 e ));
   * behavior will ensue when the automatic cast is applied instead of the
   * pg_cast entry!
   */
! DATA(insert (  650	 25  730 a ));
! DATA(insert (  869	 25  730 a ));
! DATA(insert (	16	 25 2971 a ));
! DATA(insert (  142	 25    0 a ));
! DATA(insert (	25	142 2896 e ));
  
  /*
   * Cross-category casts to and from VARCHAR
   *
   * We support all the same casts as for TEXT.
   */
! DATA(insert (  650 1043  730 a ));
! DATA(insert (  869 1043  730 a ));
! DATA(insert (	16 1043 2971 a ));
! DATA(insert (  142 1043    0 a ));
! DATA(insert ( 1043	142 2896 e ));
  
  /*
   * Cross-category casts to and from BPCHAR
   *
   * We support all the same casts as for TEXT.
   */
! DATA(insert (  650 1042  730 a ));
! DATA(insert (  869 1042  730 a ));
! DATA(insert (	16 1042 2971 a ));
! DATA(insert (  142 1042    0 a ));
! DATA(insert ( 1042	142 2896 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 */
--- 310,355 ----
   * behavior will ensue when the automatic cast is applied instead of the
   * pg_cast entry!
   */
! DATA(insert (  650	 25  730 a f ));
! DATA(insert (  869	 25  730 a f ));
! DATA(insert (	16	 25 2971 a f ));
! DATA(insert (  142	 25    0 a b ));
! DATA(insert (	25	142 2896 e f ));
  
  /*
   * Cross-category casts to and from VARCHAR
   *
   * We support all the same casts as for TEXT.
   */
! DATA(insert (  650 1043  730 a f ));
! DATA(insert (  869 1043  730 a f ));
! DATA(insert (	16 1043 2971 a f ));
! DATA(insert (  142 1043    0 a b ));
! DATA(insert ( 1043	142 2896 e f ));
  
  /*
   * Cross-category casts to and from BPCHAR
   *
   * We support all the same casts as for TEXT.
   */
! DATA(insert (  650 1042  730 a f ));
! DATA(insert (  869 1042  730 a f ));
! DATA(insert (	16 1042 2971 a f ));
! DATA(insert (  142 1042    0 a b ));
! DATA(insert ( 1042	142 2896 e f ));
  
  /*
   * Length-coercion functions
   */
! DATA(insert ( 1042 1042  668 i f ));
! DATA(insert ( 1043 1043  669 i f ));
! DATA(insert ( 1083 1083 1968 i f ));
! DATA(insert ( 1114 1114 1961 i f ));
! DATA(insert ( 1184 1184 1967 i f ));
! DATA(insert ( 1186 1186 1200 i f ));
! DATA(insert ( 1266 1266 1969 i f ));
! DATA(insert ( 1560 1560 1685 i f ));
! DATA(insert ( 1562 1562 1687 i f ));
! DATA(insert ( 1700 1700 1703 i f ));
  
  #endif   /* PG_CAST_H */
*** src/include/nodes/parsenodes.h
--- src/include/nodes/parsenodes.h
***************
*** 2062,2067 **** typedef struct CreateCastStmt
--- 2062,2068 ----
  	TypeName   *targettype;
  	FuncWithArgs *func;
  	CoercionContext context;
+ 	bool		inout;
  } CreateCastStmt;
  
  /* ----------------------
*** src/test/regress/expected/opr_sanity.out
--- src/test/regress/expected/opr_sanity.out
***************
*** 22,28 **** create function binary_coercible(oid, oid) returns bool as $$
  SELECT ($1 = $2) OR
   EXISTS(select 1 from pg_catalog.pg_cast where
          castsource = $1 and casttarget = $2 and
!         castfunc = 0 and castcontext = 'i') OR
   ($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
    EXISTS(select 1 from pg_catalog.pg_type where
           oid = $1 and typelem != 0 and typlen = -1))
--- 22,28 ----
  SELECT ($1 = $2) OR
   EXISTS(select 1 from pg_catalog.pg_cast where
          castsource = $1 and casttarget = $2 and
!         castmethod = 'b' and castcontext = 'i') OR
   ($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
    EXISTS(select 1 from pg_catalog.pg_type where
           oid = $1 and typelem != 0 and typlen = -1))
***************
*** 33,39 **** create function physically_coercible(oid, oid) returns bool as $$
  SELECT ($1 = $2) OR
   EXISTS(select 1 from pg_catalog.pg_cast where
          castsource = $1 and casttarget = $2 and
!         castfunc = 0) OR
   ($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
    EXISTS(select 1 from pg_catalog.pg_type where
           oid = $1 and typelem != 0 and typlen = -1))
--- 33,39 ----
  SELECT ($1 = $2) OR
   EXISTS(select 1 from pg_catalog.pg_cast where
          castsource = $1 and casttarget = $2 and
!         castmethod = 'b') OR
   ($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
    EXISTS(select 1 from pg_catalog.pg_type where
           oid = $1 and typelem != 0 and typlen = -1))
***************
*** 262,270 **** WHERE p1.prorettype = 'internal'::regtype AND NOT
  -- oidjoins test).
  SELECT *
  FROM pg_cast c
! 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.
--- 262,281 ----
  -- oidjoins test).
  SELECT *
  FROM pg_cast c
! WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i')
!     OR castmethod NOT IN ('f', 'b' ,'i');
!  castsource | casttarget | castfunc | castcontext | castmethod 
! ------------+------------+----------+-------------+------------
! (0 rows)
! 
! -- Check that castfunc is nonzero only for cast methods that need a function,
! -- and zero otherwise
! SELECT *
! FROM pg_cast c
! WHERE (castmethod = 'f' AND castfunc = 0)
!    OR (castmethod IN ('b', 'i') AND castfunc <> 0);
!  castsource | casttarget | castfunc | castcontext | castmethod 
! ------------+------------+----------+-------------+------------
  (0 rows)
  
  -- Look for casts to/from the same type that aren't length coercion functions.
***************
*** 273,287 **** WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i');
  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)
  
  -- Look for cast functions that don't have the right signature.  The
--- 284,298 ----
  SELECT *
  FROM pg_cast c
  WHERE castsource = casttarget AND castfunc = 0;
!  castsource | casttarget | castfunc | castcontext | castmethod 
! ------------+------------+----------+-------------+------------
  (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 | castmethod 
! ------------+------------+----------+-------------+------------
  (0 rows)
  
  -- Look for cast functions that don't have the right signature.  The
***************
*** 299,306 **** WHERE c.castfunc = p.oid AND
               OR (c.castsource = 'character'::regtype AND
                   p.proargtypes[0] = 'text'::regtype))
       OR NOT binary_coercible(p.prorettype, c.casttarget));
!  castsource | casttarget | castfunc | castcontext 
! ------------+------------+----------+-------------
  (0 rows)
  
  SELECT c.*
--- 310,317 ----
               OR (c.castsource = 'character'::regtype AND
                   p.proargtypes[0] = 'text'::regtype))
       OR NOT binary_coercible(p.prorettype, c.casttarget));
!  castsource | casttarget | castfunc | castcontext | castmethod 
! ------------+------------+----------+-------------+------------
  (0 rows)
  
  SELECT c.*
***************
*** 308,315 **** 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
--- 319,326 ----
  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 | castmethod 
! ------------+------------+----------+-------------+------------
  (0 rows)
  
  -- Look for binary compatible casts that do not have the reverse
***************
*** 324,342 **** WHERE c.castfunc = p.oid AND
  -- texttoxml(), which does an XML syntax check.
  SELECT *
  FROM pg_cast c
! WHERE c.castfunc = 0 AND
      NOT EXISTS (SELECT 1 FROM pg_cast k
!                 WHERE k.castfunc = 0 AND
                      k.castsource = c.casttarget AND
                      k.casttarget = c.castsource);
!  castsource | casttarget | castfunc | castcontext 
! ------------+------------+----------+-------------
!          25 |       1042 |        0 | i
!        1043 |       1042 |        0 | i
!         650 |        869 |        0 | i
!         142 |         25 |        0 | a
!         142 |       1043 |        0 | a
!         142 |       1042 |        0 | a
  (6 rows)
  
  -- **************** pg_operator ****************
--- 335,353 ----
  -- texttoxml(), which does an XML syntax check.
  SELECT *
  FROM pg_cast c
! WHERE c.castmethod = 'b' AND
      NOT EXISTS (SELECT 1 FROM pg_cast k
!                 WHERE k.castmethod = 'b' AND
                      k.castsource = c.casttarget AND
                      k.casttarget = c.castsource);
!  castsource | casttarget | castfunc | castcontext | castmethod 
! ------------+------------+----------+-------------+------------
!          25 |       1042 |        0 | i           | b
!        1043 |       1042 |        0 | i           | b
!         650 |        869 |        0 | i           | b
!         142 |         25 |        0 | a           | b
!         142 |       1043 |        0 | a           | b
!         142 |       1042 |        0 | a           | b
  (6 rows)
  
  -- **************** pg_operator ****************
*** src/test/regress/sql/opr_sanity.sql
--- src/test/regress/sql/opr_sanity.sql
***************
*** 25,31 **** create function binary_coercible(oid, oid) returns bool as $$
  SELECT ($1 = $2) OR
   EXISTS(select 1 from pg_catalog.pg_cast where
          castsource = $1 and casttarget = $2 and
!         castfunc = 0 and castcontext = 'i') OR
   ($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
    EXISTS(select 1 from pg_catalog.pg_type where
           oid = $1 and typelem != 0 and typlen = -1))
--- 25,31 ----
  SELECT ($1 = $2) OR
   EXISTS(select 1 from pg_catalog.pg_cast where
          castsource = $1 and casttarget = $2 and
!         castmethod = 'b' and castcontext = 'i') OR
   ($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
    EXISTS(select 1 from pg_catalog.pg_type where
           oid = $1 and typelem != 0 and typlen = -1))
***************
*** 37,43 **** create function physically_coercible(oid, oid) returns bool as $$
  SELECT ($1 = $2) OR
   EXISTS(select 1 from pg_catalog.pg_cast where
          castsource = $1 and casttarget = $2 and
!         castfunc = 0) OR
   ($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
    EXISTS(select 1 from pg_catalog.pg_type where
           oid = $1 and typelem != 0 and typlen = -1))
--- 37,43 ----
  SELECT ($1 = $2) OR
   EXISTS(select 1 from pg_catalog.pg_cast where
          castsource = $1 and casttarget = $2 and
!         castmethod = 'b') OR
   ($2 = 'pg_catalog.anyarray'::pg_catalog.regtype AND
    EXISTS(select 1 from pg_catalog.pg_type where
           oid = $1 and typelem != 0 and typlen = -1))
***************
*** 214,220 **** WHERE p1.prorettype = 'internal'::regtype AND NOT
  
  SELECT *
  FROM pg_cast c
! 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.)
--- 214,229 ----
  
  SELECT *
  FROM pg_cast c
! WHERE castsource = 0 OR casttarget = 0 OR castcontext NOT IN ('e', 'a', 'i')
!     OR castmethod NOT IN ('f', 'b' ,'i');
! 
! -- Check that castfunc is nonzero only for cast methods that need a function,
! -- and zero otherwise
! 
! SELECT *
! FROM pg_cast c
! WHERE (castmethod = 'f' AND castfunc = 0)
!    OR (castmethod IN ('b', 'i') AND castfunc <> 0);
  
  -- 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.)
***************
*** 267,275 **** WHERE c.castfunc = p.oid AND
  
  SELECT *
  FROM pg_cast c
! WHERE c.castfunc = 0 AND
      NOT EXISTS (SELECT 1 FROM pg_cast k
!                 WHERE k.castfunc = 0 AND
                      k.castsource = c.casttarget AND
                      k.casttarget = c.castsource);
  
--- 276,284 ----
  
  SELECT *
  FROM pg_cast c
! WHERE c.castmethod = 'b' AND
      NOT EXISTS (SELECT 1 FROM pg_cast k
!                 WHERE k.castmethod = 'b' AND
                      k.castsource = c.casttarget AND
                      k.casttarget = c.castsource);
  
#9Tom Lane
tgl@sss.pgh.pa.us
In reply to: Heikki Linnakangas (#8)
Re: User defined I/O conversion casts

Heikki Linnakangas <heikki.linnakangas@enterprisedb.com> writes:

Here's a patch. It seems pretty straightforward, I'll apply this
tomorrow, barring objections. Note to self: bump the catalog version.

Looks sane in a fast once-over. I didn't cross-check the pg_cast.h
data changes, but that's what the regression test check is for ;-)

regards, tom lane