From a65a49429f1a6722a6be4a9784b4e08fab8ca380 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Mon, 1 May 2006 23:22:43 +0000
Subject: [PATCH] Provide a namespace.c function for lookup of an operator with
 exact input datatypes given, and use this before trying
 OpernameGetCandidates. This is faster than the old method when there's an
 exact match, and it does not seem materially slower when there's not.  And it
 definitely makes some of the callers cleaner, because they didn't really want
 to know about a list of candidates anyway.  Per discussion with Atsushi
 Ogawa.

---
 src/backend/catalog/namespace.c | 112 ++++++++++++++++---
 src/backend/parser/parse_oper.c | 188 +++++++++++++++-----------------
 src/backend/utils/adt/regproc.c |  25 +----
 src/include/catalog/namespace.h |   3 +-
 4 files changed, 190 insertions(+), 138 deletions(-)

diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 1a1fa310cdb..f20838d12f1 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -13,7 +13,7 @@
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.84 2006/04/25 14:11:53 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/catalog/namespace.c,v 1.85 2006/05/01 23:22:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -682,6 +682,95 @@ FunctionIsVisible(Oid funcid)
 }
 
 
+/*
+ * OpernameGetOprid
+ *		Given a possibly-qualified operator name and exact input datatypes,
+ *		look up the operator.  Returns InvalidOid if not found.
+ *
+ * Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for
+ * a postfix op.
+ *
+ * If the operator name is not schema-qualified, it is sought in the current
+ * namespace search path.
+ */
+Oid
+OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
+{
+	char	   *schemaname;
+	char	   *opername;
+	CatCList   *catlist;
+	ListCell   *l;
+
+	/* deconstruct the name list */
+	DeconstructQualifiedName(names, &schemaname, &opername);
+
+	if (schemaname)
+	{
+		/* search only in exact schema given */
+		Oid			namespaceId;
+		HeapTuple	opertup;
+
+		namespaceId = LookupExplicitNamespace(schemaname);
+		opertup = SearchSysCache(OPERNAMENSP,
+								 CStringGetDatum(opername),
+								 ObjectIdGetDatum(oprleft),
+								 ObjectIdGetDatum(oprright),
+								 ObjectIdGetDatum(namespaceId));
+		if (HeapTupleIsValid(opertup))
+		{
+			Oid		result = HeapTupleGetOid(opertup);
+
+			ReleaseSysCache(opertup);
+			return result;
+		}
+		return InvalidOid;
+	}
+
+	/* Search syscache by name and argument types */
+	catlist = SearchSysCacheList(OPERNAMENSP, 3,
+								 CStringGetDatum(opername),
+								 ObjectIdGetDatum(oprleft),
+								 ObjectIdGetDatum(oprright),
+								 0);
+
+	if (catlist->n_members == 0)
+	{
+		/* no hope, fall out early */
+		ReleaseSysCacheList(catlist);
+		return InvalidOid;
+	}
+
+	/*
+	 * We have to find the list member that is first in the search path,
+	 * if there's more than one.  This doubly-nested loop looks ugly,
+	 * but in practice there should usually be few catlist members.
+	 */
+	recomputeNamespacePath();
+
+	foreach(l, namespaceSearchPath)
+	{
+		Oid			namespaceId = lfirst_oid(l);
+		int			i;
+
+		for (i = 0; i < catlist->n_members; i++)
+		{
+			HeapTuple	opertup = &catlist->members[i]->tuple;
+			Form_pg_operator operform = (Form_pg_operator) GETSTRUCT(opertup);
+
+			if (operform->oprnamespace == namespaceId)
+			{
+				Oid		result = HeapTupleGetOid(opertup);
+
+				ReleaseSysCacheList(catlist);
+				return result;
+			}
+		}
+	}
+
+	ReleaseSysCacheList(catlist);
+	return InvalidOid;
+}
+
 /*
  * OpernameGetCandidates
  *		Given a possibly-qualified operator name and operator kind,
@@ -883,26 +972,13 @@ OperatorIsVisible(Oid oprid)
 		 * If it is in the path, it might still not be visible; it could be
 		 * hidden by another operator of the same name and arguments earlier
 		 * in the path.  So we must do a slow check to see if this is the same
-		 * operator that would be found by OpernameGetCandidates.
+		 * operator that would be found by OpernameGetOprId.
 		 */
 		char	   *oprname = NameStr(oprform->oprname);
-		FuncCandidateList clist;
-
-		visible = false;
 
-		clist = OpernameGetCandidates(list_make1(makeString(oprname)),
-									  oprform->oprkind);
-
-		for (; clist; clist = clist->next)
-		{
-			if (clist->args[0] == oprform->oprleft &&
-				clist->args[1] == oprform->oprright)
-			{
-				/* Found the expected entry; is it the right op? */
-				visible = (clist->oid == oprid);
-				break;
-			}
-		}
+		visible = (OpernameGetOprid(list_make1(makeString(oprname)),
+									oprform->oprleft, oprform->oprright)
+				   == oprid);
 	}
 
 	ReleaseSysCache(oprtup);
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index e483372e852..b96849cfa28 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -8,7 +8,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.86 2006/03/14 22:48:21 tgl Exp $
+ *	  $PostgreSQL: pgsql/src/backend/parser/parse_oper.c,v 1.87 2006/05/01 23:22:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -29,8 +29,7 @@
 #include "utils/typcache.h"
 
 
-static Oid binary_oper_exact(Oid arg1, Oid arg2,
-				  FuncCandidateList candidates);
+static Oid binary_oper_exact(List *opname, Oid arg1, Oid arg2);
 static FuncDetailCode oper_select_candidate(int nargs,
 					  Oid *input_typeids,
 					  FuncCandidateList candidates,
@@ -64,33 +63,31 @@ Oid
 LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright,
 			   bool noError, int location)
 {
-	FuncCandidateList clist;
-	char		oprkind;
-
-	if (!OidIsValid(oprleft))
-		oprkind = 'l';
-	else if (!OidIsValid(oprright))
-		oprkind = 'r';
-	else
-		oprkind = 'b';
-
-	clist = OpernameGetCandidates(opername, oprkind);
+	Oid			result;
 
-	while (clist)
-	{
-		if (clist->args[0] == oprleft && clist->args[1] == oprright)
-			return clist->oid;
-		clist = clist->next;
-	}
+	result = OpernameGetOprid(opername, oprleft, oprright);
+	if (OidIsValid(result))
+		return result;
 
 	/* we don't use op_error here because only an exact match is wanted */
 	if (!noError)
+	{
+		char		oprkind;
+
+		if (!OidIsValid(oprleft))
+			oprkind = 'l';
+		else if (!OidIsValid(oprright))
+			oprkind = 'r';
+		else
+			oprkind = 'b';
+
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
 				 errmsg("operator does not exist: %s",
 						op_signature_string(opername, oprkind,
 											oprleft, oprright)),
 				 parser_errposition(pstate, location)));
+	}
 
 	return InvalidOid;
 }
@@ -387,10 +384,9 @@ oprfuncid(Operator op)
  * be reduced to its base type to find an "exact" match.
  */
 static Oid
-binary_oper_exact(Oid arg1, Oid arg2,
-				  FuncCandidateList candidates)
+binary_oper_exact(List *opname, Oid arg1, Oid arg2)
 {
-	FuncCandidateList cand;
+	Oid			result;
 	bool		was_unknown = false;
 
 	/* Unspecified type for one of the arguments? then use the other */
@@ -405,11 +401,9 @@ binary_oper_exact(Oid arg1, Oid arg2,
 		was_unknown = true;
 	}
 
-	for (cand = candidates; cand != NULL; cand = cand->next)
-	{
-		if (arg1 == cand->args[0] && arg2 == cand->args[1])
-			return cand->oid;
-	}
+	result = OpernameGetOprid(opname, arg1, arg2);
+	if (OidIsValid(result))
+		return result;
 
 	if (was_unknown)
 	{
@@ -418,11 +412,9 @@ binary_oper_exact(Oid arg1, Oid arg2,
 
 		if (basetype != arg1)
 		{
-			for (cand = candidates; cand != NULL; cand = cand->next)
-			{
-				if (basetype == cand->args[0] && basetype == cand->args[1])
-					return cand->oid;
-			}
+			result = OpernameGetOprid(opname, basetype, basetype);
+			if (OidIsValid(result))
+				return result;
 		}
 	}
 
@@ -503,32 +495,33 @@ Operator
 oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
 	 bool noError, int location)
 {
-	FuncCandidateList clist;
-	Oid			inputOids[2];
 	Oid			operOid;
 	FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
 	HeapTuple	tup = NULL;
 
-	/* Get binary operators of given name */
-	clist = OpernameGetCandidates(opname, 'b');
-
-	/* No operators found? Then fail... */
-	if (clist != NULL)
+	/*
+	 * First try for an "exact" match.
+	 */
+	operOid = binary_oper_exact(opname, ltypeId, rtypeId);
+	if (!OidIsValid(operOid))
 	{
 		/*
-		 * Check for an "exact" match.
+		 * Otherwise, search for the most suitable candidate.
 		 */
-		operOid = binary_oper_exact(ltypeId, rtypeId, clist);
-		if (!OidIsValid(operOid))
-		{
-			/*
-			 * Otherwise, search for the most suitable candidate.
-			 */
+		FuncCandidateList clist;
 
+		/* Get binary operators of given name */
+		clist = OpernameGetCandidates(opname, 'b');
+
+		/* No operators found? Then fail... */
+		if (clist != NULL)
+		{
 			/*
 			 * Unspecified type for one of the arguments? then use the other
 			 * (XXX this is probably dead code?)
 			 */
+			Oid			inputOids[2];
+
 			if (rtypeId == InvalidOid)
 				rtypeId = ltypeId;
 			else if (ltypeId == InvalidOid)
@@ -537,12 +530,13 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
 			inputOids[1] = rtypeId;
 			fdresult = oper_select_candidate(2, inputOids, clist, &operOid);
 		}
-		if (OidIsValid(operOid))
-			tup = SearchSysCache(OPEROID,
-								 ObjectIdGetDatum(operOid),
-								 0, 0, 0);
 	}
 
+	if (OidIsValid(operOid))
+		tup = SearchSysCache(OPEROID,
+							 ObjectIdGetDatum(operOid),
+							 0, 0, 0);
+
 	if (!HeapTupleIsValid(tup) && !noError)
 		op_error(pstate, opname, 'b', ltypeId, rtypeId, fdresult, location);
 
@@ -627,32 +621,26 @@ compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError)
 Operator
 right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
 {
-	FuncCandidateList clist;
-	Oid			operOid = InvalidOid;
+	Oid			operOid;
 	FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
 	HeapTuple	tup = NULL;
 
-	/* Find candidates */
-	clist = OpernameGetCandidates(op, 'r');
-
-	if (clist != NULL)
+	/*
+	 * First try for an "exact" match.
+	 */
+	operOid = OpernameGetOprid(op, arg, InvalidOid);
+	if (!OidIsValid(operOid))
 	{
 		/*
-		 * First, quickly check to see if there is an exactly matching
-		 * operator (there can be only one such entry in the list).
+		 * Otherwise, search for the most suitable candidate.
 		 */
-		FuncCandidateList clisti;
+		FuncCandidateList clist;
 
-		for (clisti = clist; clisti != NULL; clisti = clisti->next)
-		{
-			if (arg == clisti->args[0])
-			{
-				operOid = clisti->oid;
-				break;
-			}
-		}
+		/* Get postfix operators of given name */
+		clist = OpernameGetCandidates(op, 'r');
 
-		if (!OidIsValid(operOid))
+		/* No operators found? Then fail... */
+		if (clist != NULL)
 		{
 			/*
 			 * We must run oper_select_candidate even if only one candidate,
@@ -660,12 +648,13 @@ right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
 			 */
 			fdresult = oper_select_candidate(1, &arg, clist, &operOid);
 		}
-		if (OidIsValid(operOid))
-			tup = SearchSysCache(OPEROID,
-								 ObjectIdGetDatum(operOid),
-								 0, 0, 0);
 	}
 
+	if (OidIsValid(operOid))
+		tup = SearchSysCache(OPEROID,
+							 ObjectIdGetDatum(operOid),
+							 0, 0, 0);
+
 	if (!HeapTupleIsValid(tup) && !noError)
 		op_error(pstate, op, 'r', arg, InvalidOid, fdresult, location);
 
@@ -690,50 +679,53 @@ right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
 Operator
 left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
 {
-	FuncCandidateList clist;
-	Oid			operOid = InvalidOid;
+	Oid			operOid;
 	FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
 	HeapTuple	tup = NULL;
 
-	/* Find candidates */
-	clist = OpernameGetCandidates(op, 'l');
-
-	if (clist != NULL)
+	/*
+	 * First try for an "exact" match.
+	 */
+	operOid = OpernameGetOprid(op, InvalidOid, arg);
+	if (!OidIsValid(operOid))
 	{
 		/*
-		 * First, quickly check to see if there is an exactly matching
-		 * operator (there can be only one such entry in the list).
-		 *
-		 * The returned list has args in the form (0, oprright).  Move the
-		 * useful data into args[0] to keep oper_select_candidate simple. XXX
-		 * we are assuming here that we may scribble on the list!
+		 * Otherwise, search for the most suitable candidate.
 		 */
-		FuncCandidateList clisti;
+		FuncCandidateList clist;
+
+		/* Get prefix operators of given name */
+		clist = OpernameGetCandidates(op, 'l');
 
-		for (clisti = clist; clisti != NULL; clisti = clisti->next)
+		/* No operators found? Then fail... */
+		if (clist != NULL)
 		{
-			clisti->args[0] = clisti->args[1];
-			if (arg == clisti->args[0])
+			/*
+			 * The returned list has args in the form (0, oprright).
+			 * Move the useful data into args[0] to keep oper_select_candidate
+			 * simple.  XXX we are assuming here that we may scribble on the
+			 * list!
+			 */
+			FuncCandidateList clisti;
+
+			for (clisti = clist; clisti != NULL; clisti = clisti->next)
 			{
-				operOid = clisti->oid;
-				break;
+				clisti->args[0] = clisti->args[1];
 			}
-		}
 
-		if (!OidIsValid(operOid))
-		{
 			/*
 			 * We must run oper_select_candidate even if only one candidate,
 			 * otherwise we may falsely return a non-type-compatible operator.
 			 */
 			fdresult = oper_select_candidate(1, &arg, clist, &operOid);
 		}
-		if (OidIsValid(operOid))
-			tup = SearchSysCache(OPEROID,
-								 ObjectIdGetDatum(operOid),
-								 0, 0, 0);
 	}
 
+	if (OidIsValid(operOid))
+		tup = SearchSysCache(OPEROID,
+							 ObjectIdGetDatum(operOid),
+							 0, 0, 0);
+
 	if (!HeapTupleIsValid(tup) && !noError)
 		op_error(pstate, op, 'l', InvalidOid, arg, fdresult, location);
 
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index f5b0e2ab0c7..37bb0d929c3 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -13,7 +13,7 @@
  *
  *
  * IDENTIFICATION
- *	  $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.97 2006/03/05 15:58:43 momjian Exp $
+ *	  $PostgreSQL: pgsql/src/backend/utils/adt/regproc.c,v 1.98 2006/05/01 23:22:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -598,12 +598,10 @@ Datum
 regoperatorin(PG_FUNCTION_ARGS)
 {
 	char	   *opr_name_or_oid = PG_GETARG_CSTRING(0);
-	Oid			result = InvalidOid;
+	Oid			result;
 	List	   *names;
 	int			nargs;
 	Oid			argtypes[FUNC_MAX_ARGS];
-	char		oprkind;
-	FuncCandidateList clist;
 
 	/* '0' ? */
 	if (strcmp(opr_name_or_oid, "0") == 0)
@@ -642,28 +640,13 @@ regoperatorin(PG_FUNCTION_ARGS)
 				 errmsg("too many arguments"),
 				 errhint("Provide two argument types for operator.")));
 
-	if (argtypes[0] == InvalidOid)
-		oprkind = 'l';
-	else if (argtypes[1] == InvalidOid)
-		oprkind = 'r';
-	else
-		oprkind = 'b';
+	result = OpernameGetOprid(names, argtypes[0], argtypes[1]);
 
-	clist = OpernameGetCandidates(names, oprkind);
-
-	for (; clist; clist = clist->next)
-	{
-		if (memcmp(clist->args, argtypes, 2 * sizeof(Oid)) == 0)
-			break;
-	}
-
-	if (clist == NULL)
+	if (!OidIsValid(result))
 		ereport(ERROR,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
 				 errmsg("operator does not exist: %s", opr_name_or_oid)));
 
-	result = clist->oid;
-
 	PG_RETURN_OID(result);
 }
 
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index e1389bc2242..ab354f53ac6 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -7,7 +7,7 @@
  * Portions Copyright (c) 1996-2006, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
- * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.41 2006/04/25 14:11:59 momjian Exp $
+ * $PostgreSQL: pgsql/src/include/catalog/namespace.h,v 1.42 2006/05/01 23:22:43 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -44,6 +44,7 @@ extern bool TypeIsVisible(Oid typid);
 extern FuncCandidateList FuncnameGetCandidates(List *names, int nargs);
 extern bool FunctionIsVisible(Oid funcid);
 
+extern Oid	OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
 extern FuncCandidateList OpernameGetCandidates(List *names, char oprkind);
 extern bool OperatorIsVisible(Oid oprid);
 
-- 
GitLab