From 839deadf749d1e483789b882e3f5cc404a7bb6c9 Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Thu, 21 Aug 2025 18:20:42 -0400
Subject: [PATCH v3 3/4] Improve the messages for operator-not-found, too.

Extend the same return-a-bitmask approach to OpernameGetCandidates.
The issues around argument names don't apply to operator syntax,
but it still seems worth distinguishing between "there is no
operator of that name" and "we couldn't match the argument types".

Also follow the previous patch's improvement of style by
separating errdetail from errhint.

Reported-by: Dominique Devienne <ddevienne@gmail.com>
Author: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/1756041.1754616558@sss.pgh.pa.us
---
 .../postgres_fdw/expected/postgres_fdw.out    |  6 +-
 src/backend/catalog/namespace.c               | 22 ++++++-
 src/backend/parser/parse_oper.c               | 63 +++++++++++++++----
 src/backend/utils/adt/regproc.c               |  6 +-
 src/include/catalog/namespace.h               |  3 +-
 src/test/regress/expected/alter_table.out     |  3 +-
 .../regress/expected/create_function_sql.out  |  3 +-
 src/test/regress/expected/create_view.out     |  3 +-
 src/test/regress/expected/domain.out          |  3 +-
 src/test/regress/expected/expressions.out     |  3 +-
 src/test/regress/expected/geometry.out        |  3 +-
 src/test/regress/expected/horology.out        |  3 +-
 src/test/regress/expected/plpgsql.out         |  3 +-
 src/test/regress/expected/polymorphism.out    |  3 +-
 src/test/regress/expected/subselect.out       |  3 +-
 src/test/regress/expected/text.out            |  3 +-
 src/test/regress/expected/timetz.out          |  3 +-
 src/test/regress/expected/with.out            |  3 +-
 src/test/regress/expected/xid.out             | 12 ++--
 19 files changed, 116 insertions(+), 35 deletions(-)

diff --git a/contrib/postgres_fdw/expected/postgres_fdw.out b/contrib/postgres_fdw/expected/postgres_fdw.out
index d3323b04676..35c619226a6 100644
--- a/contrib/postgres_fdw/expected/postgres_fdw.out
+++ b/contrib/postgres_fdw/expected/postgres_fdw.out
@@ -4621,11 +4621,13 @@ SELECT * FROM ft1 WHERE 'foo' = c8 LIMIT 1;
 -- with that remote type
 SELECT * FROM ft1 WHERE c8 LIKE 'foo' LIMIT 1; -- ERROR
 ERROR:  operator does not exist: public.user_enum ~~ unknown
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 CONTEXT:  remote SQL command: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((c8 ~~ 'foo')) LIMIT 1::bigint
 SELECT * FROM ft1 WHERE c8::text LIKE 'foo' LIMIT 1; -- ERROR; cast not pushed down
 ERROR:  operator does not exist: public.user_enum ~~ unknown
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 CONTEXT:  remote SQL command: SELECT "C 1", c2, c3, c4, c5, c6, c7, c8 FROM "S 1"."T 1" WHERE ((c8 ~~ 'foo')) LIMIT 1::bigint
 ALTER FOREIGN TABLE ft1 ALTER COLUMN c8 TYPE user_enum;
 -- ===================================================================
diff --git a/src/backend/catalog/namespace.c b/src/backend/catalog/namespace.c
index 524f325be03..0c18a8466d2 100644
--- a/src/backend/catalog/namespace.c
+++ b/src/backend/catalog/namespace.c
@@ -1929,9 +1929,20 @@ OpernameGetOprid(List *names, Oid oprleft, Oid oprright)
  *
  * The returned items always have two args[] entries --- the first will be
  * InvalidOid for a prefix oprkind.  nargs is always 2, too.
+ *
+ * We return an empty list (NULL) if no suitable matches can be found.  If the
+ * operator name was schema-qualified with a schema that does not exist, then
+ * we return an empty list if missing_schema_ok is true and otherwise throw an
+ * error.  (missing_schema_ok does not affect the behavior otherwise.)
+ *
+ * The output argument *fgc_flags is filled with a bitmask indicating how
+ * far we were able to match the supplied information.  This is not of much
+ * interest if any candidates were found, but if not, it can help callers
+ * produce an on-point error message.
  */
 FuncCandidateList
-OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
+OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok,
+					  int *fgc_flags)
 {
 	FuncCandidateList resultList = NULL;
 	char	   *resultSpace = NULL;
@@ -1942,15 +1953,20 @@ OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
 	CatCList   *catlist;
 	int			i;
 
+	/* initialize output fgc_flags to empty */
+	*fgc_flags = 0;
+
 	/* deconstruct the name list */
 	DeconstructQualifiedName(names, &schemaname, &opername);
 
 	if (schemaname)
 	{
 		/* use exact schema given */
+		*fgc_flags |= FGC_SCHEMA_GIVEN; /* report that a schema is given */
 		namespaceId = LookupExplicitNamespace(schemaname, missing_schema_ok);
-		if (missing_schema_ok && !OidIsValid(namespaceId))
+		if (!OidIsValid(namespaceId))
 			return NULL;
+		*fgc_flags |= FGC_SCHEMA_MATCH; /* report that the schema is valid */
 	}
 	else
 	{
@@ -2061,6 +2077,8 @@ OpernameGetCandidates(List *names, char oprkind, bool missing_schema_ok)
 			}
 		}
 
+		*fgc_flags |= FGC_NAME_MATCH;	/* we found a matching operator name */
+
 		/*
 		 * Okay to add it to result list
 		 */
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c
index 0c4337563cf..f9e18270489 100644
--- a/src/backend/parser/parse_oper.c
+++ b/src/backend/parser/parse_oper.c
@@ -72,7 +72,8 @@ static FuncDetailCode oper_select_candidate(int nargs,
 											Oid *operOid);
 static void op_error(ParseState *pstate, List *op,
 					 Oid arg1, Oid arg2,
-					 FuncDetailCode fdresult, int location);
+					 FuncDetailCode fdresult, int fgc_flags, int location);
+static int	oper_lookup_failure_details(int fgc_flags, bool is_unary_op);
 static bool make_oper_cache_key(ParseState *pstate, OprCacheKey *key,
 								List *opname, Oid ltypeId, Oid rtypeId,
 								int location);
@@ -373,6 +374,7 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
 	Oid			operOid;
 	OprCacheKey key;
 	bool		key_ok;
+	int			fgc_flags = 0;
 	FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
 	HeapTuple	tup = NULL;
 
@@ -404,7 +406,7 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
 		FuncCandidateList clist;
 
 		/* Get binary operators of given name */
-		clist = OpernameGetCandidates(opname, 'b', false);
+		clist = OpernameGetCandidates(opname, 'b', false, &fgc_flags);
 
 		/* No operators found? Then fail... */
 		if (clist != NULL)
@@ -434,7 +436,8 @@ oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
 			make_oper_cache_entry(&key, operOid);
 	}
 	else if (!noError)
-		op_error(pstate, opname, ltypeId, rtypeId, fdresult, location);
+		op_error(pstate, opname, ltypeId, rtypeId,
+				 fdresult, fgc_flags, location);
 
 	return (Operator) tup;
 }
@@ -520,6 +523,7 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
 	Oid			operOid;
 	OprCacheKey key;
 	bool		key_ok;
+	int			fgc_flags = 0;
 	FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
 	HeapTuple	tup = NULL;
 
@@ -551,7 +555,7 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
 		FuncCandidateList clist;
 
 		/* Get prefix operators of given name */
-		clist = OpernameGetCandidates(op, 'l', false);
+		clist = OpernameGetCandidates(op, 'l', false, &fgc_flags);
 
 		/* No operators found? Then fail... */
 		if (clist != NULL)
@@ -585,7 +589,8 @@ left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
 			make_oper_cache_entry(&key, operOid);
 	}
 	else if (!noError)
-		op_error(pstate, op, InvalidOid, arg, fdresult, location);
+		op_error(pstate, op, InvalidOid, arg,
+				 fdresult, fgc_flags, location);
 
 	return (Operator) tup;
 }
@@ -621,7 +626,7 @@ op_signature_string(List *op, Oid arg1, Oid arg2)
 static void
 op_error(ParseState *pstate, List *op,
 		 Oid arg1, Oid arg2,
-		 FuncDetailCode fdresult, int location)
+		 FuncDetailCode fdresult, int fgc_flags, int location)
 {
 	if (fdresult == FUNCDETAIL_MULTIPLE)
 		ereport(ERROR,
@@ -636,14 +641,50 @@ op_error(ParseState *pstate, List *op,
 				(errcode(ERRCODE_UNDEFINED_FUNCTION),
 				 errmsg("operator does not exist: %s",
 						op_signature_string(op, arg1, arg2)),
-				 (!arg1 || !arg2) ?
-				 errhint("No operator matches the given name and argument type. "
-						 "You might need to add an explicit type cast.") :
-				 errhint("No operator matches the given name and argument types. "
-						 "You might need to add explicit type casts."),
+				 oper_lookup_failure_details(fgc_flags, (!arg1 || !arg2)),
 				 parser_errposition(pstate, location)));
 }
 
+/*
+ * Interpret the fgc_flags and issue a suitable detail or hint message.
+ */
+static int
+oper_lookup_failure_details(int fgc_flags, bool is_unary_op)
+{
+	/*
+	 * If not FGC_NAME_MATCH, we shouldn't raise the question of whether the
+	 * arguments are wrong.  It does seem worth calling the search_path to the
+	 * user's mind if the operator name was not schema-qualified; but if it
+	 * was, there's really nothing to add to the basic "operator does not
+	 * exist" message.
+	 *
+	 * Note: we passed missing_ok = false to OpernameGetCandidates, so there's
+	 * no need to consider FGC_SCHEMA_MATCH here: we'd have already thrown an
+	 * error if an explicitly-given schema doesn't exist.
+	 */
+	if (!(fgc_flags & FGC_NAME_MATCH))
+	{
+		if (fgc_flags & FGC_SCHEMA_GIVEN)
+			return 0;			/* schema-qualified name */
+		else
+			return errdetail("There is no operator of that name in the search_path.");
+	}
+
+	/*
+	 * Otherwise, the problem must be incorrect argument type(s).
+	 */
+	if (is_unary_op)
+	{
+		(void) errdetail("No operator of that name accepts the given argument type.");
+		return errhint("You might need to add an explicit type cast.");
+	}
+	else
+	{
+		(void) errdetail("No operator of that name accepts the given argument types.");
+		return errhint("You might need to add explicit type casts.");
+	}
+}
+
 /*
  * make_op()
  *		Operator expression construction.
diff --git a/src/backend/utils/adt/regproc.c b/src/backend/utils/adt/regproc.c
index 0c5dec025d7..2709024a4ad 100644
--- a/src/backend/utils/adt/regproc.c
+++ b/src/backend/utils/adt/regproc.c
@@ -488,6 +488,7 @@ regoperin(PG_FUNCTION_ARGS)
 	Oid			result;
 	List	   *names;
 	FuncCandidateList clist;
+	int			fgc_flags;
 
 	/* Handle "0" or numeric OID */
 	if (parseNumericOid(opr_name_or_oid, &result, escontext))
@@ -507,7 +508,7 @@ regoperin(PG_FUNCTION_ARGS)
 	if (names == NIL)
 		PG_RETURN_NULL();
 
-	clist = OpernameGetCandidates(names, '\0', true);
+	clist = OpernameGetCandidates(names, '\0', true, &fgc_flags);
 
 	if (clist == NULL)
 		ereturn(escontext, (Datum) 0,
@@ -577,13 +578,14 @@ regoperout(PG_FUNCTION_ARGS)
 		else
 		{
 			FuncCandidateList clist;
+			int			fgc_flags;
 
 			/*
 			 * Would this oper be found (uniquely!) by regoperin? If not,
 			 * qualify it.
 			 */
 			clist = OpernameGetCandidates(list_make1(makeString(oprname)),
-										  '\0', false);
+										  '\0', false, &fgc_flags);
 			if (clist != NULL && clist->next == NULL &&
 				clist->oid == oprid)
 				result = pstrdup(oprname);
diff --git a/src/include/catalog/namespace.h b/src/include/catalog/namespace.h
index a7bc24ead86..a3a194620f4 100644
--- a/src/include/catalog/namespace.h
+++ b/src/include/catalog/namespace.h
@@ -125,7 +125,8 @@ extern bool FunctionIsVisible(Oid funcid);
 
 extern Oid	OpernameGetOprid(List *names, Oid oprleft, Oid oprright);
 extern FuncCandidateList OpernameGetCandidates(List *names, char oprkind,
-											   bool missing_schema_ok);
+											   bool missing_schema_ok,
+											   int *fgc_flags);
 extern bool OperatorIsVisible(Oid oprid);
 
 extern Oid	OpclassnameGetOpcid(Oid amid, const char *opcname);
diff --git a/src/test/regress/expected/alter_table.out b/src/test/regress/expected/alter_table.out
index 08984dd98f1..18dd093ca79 100644
--- a/src/test/regress/expected/alter_table.out
+++ b/src/test/regress/expected/alter_table.out
@@ -2041,7 +2041,8 @@ alter table anothertab alter column atcol1 drop default;
 alter table anothertab alter column atcol1 type boolean
         using case when atcol1 % 2 = 0 then true else false end; -- fails
 ERROR:  operator does not exist: boolean <= integer
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 alter table anothertab drop constraint anothertab_chk;
 alter table anothertab drop constraint anothertab_chk; -- fails
 ERROR:  constraint "anothertab_chk" of relation "anothertab" does not exist
diff --git a/src/test/regress/expected/create_function_sql.out b/src/test/regress/expected/create_function_sql.out
index da112608d66..73c6730d459 100644
--- a/src/test/regress/expected/create_function_sql.out
+++ b/src/test/regress/expected/create_function_sql.out
@@ -304,7 +304,8 @@ CREATE FUNCTION functest_S_xx(x date) RETURNS boolean
 ERROR:  operator does not exist: date > integer
 LINE 3:     RETURN x > 1;
                      ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 -- tricky parsing
 CREATE FUNCTION functest_S_15(x int) RETURNS boolean
 LANGUAGE SQL
diff --git a/src/test/regress/expected/create_view.out b/src/test/regress/expected/create_view.out
index f551624afb3..49dd13c345c 100644
--- a/src/test/regress/expected/create_view.out
+++ b/src/test/regress/expected/create_view.out
@@ -1924,7 +1924,8 @@ select 'foo'::text = any((select array['abc','def','foo']::text[]));  -- fail
 ERROR:  operator does not exist: text = text[]
 LINE 1: select 'foo'::text = any((select array['abc','def','foo']::t...
                            ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 select 'foo'::text = any((select array['abc','def','foo']::text[])::text[]);
  ?column? 
 ----------
diff --git a/src/test/regress/expected/domain.out b/src/test/regress/expected/domain.out
index b5ea707df31..62a48a523a2 100644
--- a/src/test/regress/expected/domain.out
+++ b/src/test/regress/expected/domain.out
@@ -415,7 +415,8 @@ select row(0,1)::dcomptype;  -- fail
 ERROR:  value for domain dcomptype violates check constraint "c1"
 alter type comptype alter attribute r type varchar;  -- fail
 ERROR:  operator does not exist: character varying > double precision
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 alter type comptype alter attribute r type bigint;
 alter type comptype drop attribute r;  -- fail
 ERROR:  cannot drop column r of composite type comptype because other objects depend on it
diff --git a/src/test/regress/expected/expressions.out b/src/test/regress/expected/expressions.out
index 21c54fc1989..9a3c97b15a3 100644
--- a/src/test/regress/expected/expressions.out
+++ b/src/test/regress/expected/expressions.out
@@ -218,7 +218,8 @@ select '(0,0)'::point in ('(0,0,0,0)'::box, point(0,0));
 ERROR:  operator does not exist: point = box
 LINE 1: select '(0,0)'::point in ('(0,0,0,0)'::box, point(0,0));
                               ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 --
 -- Tests for ScalarArrayOpExpr with a hashfn
 --
diff --git a/src/test/regress/expected/geometry.out b/src/test/regress/expected/geometry.out
index 8be694f46be..1d168b21cbc 100644
--- a/src/test/regress/expected/geometry.out
+++ b/src/test/regress/expected/geometry.out
@@ -1777,7 +1777,8 @@ SELECT p.f1, l.s, l.s # p.f1 AS intersection
 ERROR:  operator does not exist: lseg # point
 LINE 1: SELECT p.f1, l.s, l.s # p.f1 AS intersection
                               ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 -- Length
 SELECT s, @-@ s FROM LSEG_TBL;
                s               |   ?column?    
diff --git a/src/test/regress/expected/horology.out b/src/test/regress/expected/horology.out
index 5ae93d8e8a5..32cf62b6741 100644
--- a/src/test/regress/expected/horology.out
+++ b/src/test/regress/expected/horology.out
@@ -605,7 +605,8 @@ SELECT date '1991-02-03' - time with time zone '04:05:06 UTC' AS "Subtract Time
 ERROR:  operator does not exist: date - time with time zone
 LINE 1: SELECT date '1991-02-03' - time with time zone '04:05:06 UTC...
                                  ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 --
 -- timestamp, interval arithmetic
 --
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index b2aaaf10b9c..b7b009295ff 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -1763,7 +1763,8 @@ select f1(point(3,4));  -- fail for lack of + operator
 ERROR:  operator does not exist: point + integer
 LINE 1: x + 1
           ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 QUERY:  x + 1
 CONTEXT:  PL/pgSQL function f1(anyelement) line 3 at RETURN
 drop function f1(x anyelement);
diff --git a/src/test/regress/expected/polymorphism.out b/src/test/regress/expected/polymorphism.out
index 05b2300a46e..6ac00488103 100644
--- a/src/test/regress/expected/polymorphism.out
+++ b/src/test/regress/expected/polymorphism.out
@@ -15,7 +15,8 @@ select polyf(point(3,4));  -- fail for lack of + operator
 ERROR:  operator does not exist: point + integer
 LINE 2:   select x + 1
                    ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 QUERY:  
   select x + 1
 
diff --git a/src/test/regress/expected/subselect.out b/src/test/regress/expected/subselect.out
index 0563d0cd5a1..637b55f1414 100644
--- a/src/test/regress/expected/subselect.out
+++ b/src/test/regress/expected/subselect.out
@@ -1156,7 +1156,8 @@ select * from int8_tbl where q1 in (select c1 from inner_text);
 ERROR:  operator does not exist: bigint = text
 LINE 1: select * from int8_tbl where q1 in (select c1 from inner_tex...
                                         ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 begin;
 -- make an operator to allow it to succeed
 create function bogus_int8_text_eq(int8, text) returns boolean
diff --git a/src/test/regress/expected/text.out b/src/test/regress/expected/text.out
index ced71e903c6..3f9982388ba 100644
--- a/src/test/regress/expected/text.out
+++ b/src/test/regress/expected/text.out
@@ -49,7 +49,8 @@ select 3 || 4.0;
 ERROR:  operator does not exist: integer || numeric
 LINE 1: select 3 || 4.0;
                  ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 /*
  * various string functions
  */
diff --git a/src/test/regress/expected/timetz.out b/src/test/regress/expected/timetz.out
index cbab6cfe5d7..324b1a740e8 100644
--- a/src/test/regress/expected/timetz.out
+++ b/src/test/regress/expected/timetz.out
@@ -174,7 +174,8 @@ SELECT f1 + time with time zone '00:01' AS "Illegal" FROM TIMETZ_TBL;
 ERROR:  operator does not exist: time with time zone + time with time zone
 LINE 1: SELECT f1 + time with time zone '00:01' AS "Illegal" FROM TI...
                   ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 --
 -- test EXTRACT
 --
diff --git a/src/test/regress/expected/with.out b/src/test/regress/expected/with.out
index 26c88505140..f015e997276 100644
--- a/src/test/regress/expected/with.out
+++ b/src/test/regress/expected/with.out
@@ -175,7 +175,8 @@ SELECT n, pg_typeof(n) FROM t;
 ERROR:  operator does not exist: text + integer
 LINE 4:     SELECT n+1 FROM t WHERE n < 10
                     ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 -- Deeply nested WITH caused a list-munging problem in v13
 -- Detection of cross-references and self-references
 WITH RECURSIVE w1(c1) AS
diff --git a/src/test/regress/expected/xid.out b/src/test/regress/expected/xid.out
index 835077e9d57..1ce7826cf90 100644
--- a/src/test/regress/expected/xid.out
+++ b/src/test/regress/expected/xid.out
@@ -110,22 +110,26 @@ select '1'::xid < '2'::xid;
 ERROR:  operator does not exist: xid < xid
 LINE 1: select '1'::xid < '2'::xid;
                         ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 select '1'::xid <= '2'::xid;
 ERROR:  operator does not exist: xid <= xid
 LINE 1: select '1'::xid <= '2'::xid;
                         ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 select '1'::xid > '2'::xid;
 ERROR:  operator does not exist: xid > xid
 LINE 1: select '1'::xid > '2'::xid;
                         ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 select '1'::xid >= '2'::xid;
 ERROR:  operator does not exist: xid >= xid
 LINE 1: select '1'::xid >= '2'::xid;
                         ^
-HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.
+DETAIL:  No operator of that name accepts the given argument types.
+HINT:  You might need to add explicit type casts.
 -- we want them for xid8 though
 select '1'::xid8 < '2'::xid8, '2'::xid8 < '2'::xid8, '2'::xid8 < '1'::xid8;
  ?column? | ?column? | ?column? 
-- 
2.43.7

