From d14c8aab99577d7dd06e9d50f3ac9c09d0334131 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 16 Mar 2000 06:35:07 +0000
Subject: [PATCH] Turns out that Mazurkiewicz's gripe about 'function
 inheritance' is actually a type-coercion problem.  If you have a function
 defined on class A, and class B inherits from A, then the function ought to
 work on class B as well --- but coerce_type didn't know that.  Now it does.

---
 src/backend/parser/parse_coerce.c | 102 +++++++++++++++++-------------
 src/backend/parser/parse_func.c   |  37 ++++++++++-
 src/include/parser/parse_func.h   |   4 +-
 src/include/parser/parse_type.h   |   4 +-
 4 files changed, 98 insertions(+), 49 deletions(-)

diff --git a/src/backend/parser/parse_coerce.c b/src/backend/parser/parse_coerce.c
index ce06da2669e..f37082a87f5 100644
--- a/src/backend/parser/parse_coerce.c
+++ b/src/backend/parser/parse_coerce.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.35 2000/03/14 23:06:32 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.36 2000/03/16 06:35:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -103,6 +103,11 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 
 		result = (Node *) relabel;
 	}
+	else if (typeInheritsFrom(inputTypeId, targetTypeId))
+	{
+		/* Input class type is a subclass of target, so nothing to do */
+		result = node;
+	}
 	else
 	{
 		/*
@@ -156,62 +161,69 @@ coerce_type(ParseState *pstate, Node *node, Oid inputTypeId,
 bool
 can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
 {
-	HeapTuple	ftup;
 	int			i;
-	Type		tp;
+	HeapTuple	ftup;
 	Oid			oid_array[FUNC_MAX_ARGS];
 
 	/* run through argument list... */
 	for (i = 0; i < nargs; i++)
 	{
-		if (input_typeids[i] != func_typeids[i])
-		{
+		Oid		inputTypeId = input_typeids[i];
+		Oid		targetTypeId = func_typeids[i];
 
-			/*
-			 * one of the known-good transparent conversions? then drop
-			 * through...
-			 */
-			if (IS_BINARY_COMPATIBLE(input_typeids[i], func_typeids[i]))
-				;
+		/* no problem if same type */
+		if (inputTypeId == targetTypeId)
+			continue;
 
-			/* don't know what to do for the output type? then quit... */
-			else if (func_typeids[i] == InvalidOid)
-				return false;
-			/* don't know what to do for the input type? then quit... */
-			else if (input_typeids[i] == InvalidOid)
-				return false;
+		/*
+		 * one of the known-good transparent conversions? then drop
+		 * through...
+		 */
+		if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
+			continue;
 
-			/*
-			 * if not unknown input type, try for explicit conversion
-			 * using functions...
-			 */
-			else if (input_typeids[i] != UNKNOWNOID)
-			{
-				MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
-				oid_array[0] = input_typeids[i];
-
-				/*
-				 * look for a single-argument function named with the
-				 * target type name
-				 */
-				ftup = SearchSysCacheTuple(PROCNAME,
-						PointerGetDatum(typeidTypeName(func_typeids[i])),
-										   Int32GetDatum(1),
-										   PointerGetDatum(oid_array),
-										   0);
-
-				/*
-				 * should also check the function return type just to be
-				 * safe...
-				 */
-				if (!HeapTupleIsValid(ftup))
-					return false;
-			}
+		/* don't know what to do for the output type? then quit... */
+		if (targetTypeId == InvalidOid)
+			return false;
+		/* don't know what to do for the input type? then quit... */
+		if (inputTypeId == InvalidOid)
+			return false;
 
-			tp = typeidType(input_typeids[i]);
-			if (typeTypeFlag(tp) == 'c')
+		/*
+		 * If input is an untyped string constant, assume we can
+		 * convert it to anything except a class type.
+		 */
+		if (inputTypeId == UNKNOWNOID)
+		{
+			if (ISCOMPLEX(targetTypeId))
 				return false;
+			continue;
 		}
+
+		/*
+		 * If input is a class type that inherits from target, no problem
+		 */
+		if (typeInheritsFrom(inputTypeId, targetTypeId))
+			continue;
+
+		/*
+		 * Else, try for explicit conversion using functions:
+		 * look for a single-argument function named with the
+		 * target type name and accepting the source type.
+		 */
+		MemSet(oid_array, 0, FUNC_MAX_ARGS * sizeof(Oid));
+		oid_array[0] = inputTypeId;
+
+		ftup = SearchSysCacheTuple(PROCNAME,
+							PointerGetDatum(typeidTypeName(targetTypeId)),
+								   Int32GetDatum(1),
+								   PointerGetDatum(oid_array),
+								   0);
+		if (!HeapTupleIsValid(ftup))
+			return false;
+		/*
+		 * should also check the function return type just to be safe...
+		 */
 	}
 
 	return true;
diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index eb4884accbb..e24007a4393 100644
--- a/src/backend/parser/parse_func.c
+++ b/src/backend/parser/parse_func.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.74 2000/03/14 23:06:32 thomas Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.75 2000/03/16 06:35:07 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -68,7 +68,6 @@ static Oid *func_select_candidate(int nargs, Oid *input_typeids,
 static int	agg_get_candidates(char *aggname, Oid typeId, CandidateList *candidates);
 static Oid	agg_select_candidate(Oid typeid, CandidateList candidates);
 
-#define ISCOMPLEX(type) (typeidTypeRelid(type) ? true : false)
 
 /*
  ** ParseNestedFuncOrColumn
@@ -1360,6 +1359,40 @@ gen_cross_product(InhPaths *arginh, int nargs)
 }
 
 
+/*
+ * Given two type OIDs, determine whether the first is a complex type
+ * (class type) that inherits from the second.
+ */
+bool
+typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId)
+{
+	Oid			relid;
+	Oid		   *supervec;
+	int			nsupers,
+				i;
+	bool		result;
+
+	if (!ISCOMPLEX(subclassTypeId) || !ISCOMPLEX(superclassTypeId))
+		return false;
+	relid = typeidTypeRelid(subclassTypeId);
+	if (relid == InvalidOid)
+		return false;
+	nsupers = find_inheritors(relid, &supervec);
+	result = false;
+	for (i = 0; i < nsupers; i++)
+	{
+		if (supervec[i] == superclassTypeId)
+		{
+			result = true;
+			break;
+		}
+	}
+	if (supervec)
+		pfree(supervec);
+	return result;
+}
+
+
 /* make_arguments()
  * Given the number and types of arguments to a function, and the
  *	actual arguments and argument types, do the necessary typecasting.
diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h
index 9ee17203708..b191944ee2f 100644
--- a/src/include/parser/parse_func.h
+++ b/src/include/parser/parse_func.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_func.h,v 1.22 2000/01/26 05:58:27 momjian Exp $
+ * $Id: parse_func.h,v 1.23 2000/03/16 06:35:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -47,6 +47,8 @@ extern Node *ParseFuncOrColumn(ParseState *pstate,
 
 extern List *setup_base_tlist(Oid typeid);
 
+extern bool typeInheritsFrom(Oid subclassTypeId, Oid superclassTypeId);
+
 extern void func_error(char *caller, char *funcname,
 					   int nargs, Oid *argtypes, char *msg);
 
diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h
index 034c47b8b0b..190d65db4d8 100644
--- a/src/include/parser/parse_type.h
+++ b/src/include/parser/parse_type.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2000, PostgreSQL, Inc
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $Id: parse_type.h,v 1.12 2000/01/26 05:58:27 momjian Exp $
+ * $Id: parse_type.h,v 1.13 2000/03/16 06:35:06 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -35,4 +35,6 @@ extern Oid	GetArrayElementType(Oid typearray);
 extern Oid	typeInfunc(Type typ);
 extern Oid	typeOutfunc(Type typ);
 
+#define ISCOMPLEX(typeid) (typeidTypeRelid(typeid) != InvalidOid)
+
 #endif	 /* PARSE_TYPE_H */
-- 
GitLab