From 90739d4621c654719dd8d214f9a8c160d407ac4c Mon Sep 17 00:00:00 2001
From: Tom Lane <tgl@sss.pgh.pa.us>
Date: Fri, 3 May 2002 20:15:02 +0000
Subject: [PATCH] Make ruleutils.c schema-aware.  Displayed names are
 schema-qualified only if they would not be found without qualification given
 the current search path, as per idea from Peter Eisentraut.

---
 src/backend/parser/parse_func.c   |   9 +-
 src/backend/utils/adt/ruleutils.c | 496 ++++++++++++++++++++----------
 2 files changed, 339 insertions(+), 166 deletions(-)

diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c
index 2ab9c9a6898..f74f5be2f7b 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.126 2002/04/11 20:00:01 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.127 2002/05/03 20:15:02 tgl Exp $
  *
  *-------------------------------------------------------------------------
  */
@@ -686,6 +686,11 @@ func_select_candidate(int nargs,
  *	 b) if the answer is one, we have our function
  *	 c) if the answer is more than one, attempt to resolve the conflict
  *	 d) if the answer is zero, try the next array from vector #1
+ *
+ * Note: we rely primarily on nargs/argtypes as the argument description.
+ * The actual expression node list is passed in fargs so that we can check
+ * for type coercion of a constant.  Some callers pass fargs == NIL
+ * indicating they don't want that check made.
  */
 FuncDetailCode
 func_get_detail(List *funcname,
@@ -740,7 +745,7 @@ func_get_detail(List *funcname,
 		 * that result for something coerce_type can't handle, we'll cause
 		 * infinite recursion between this module and coerce_type!
 		 */
-		if (nargs == 1)
+		if (nargs == 1 && fargs != NIL)
 		{
 			Oid			targetType;
 			TypeName   *tn = makeNode(TypeName);
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 1e4b5713514..b1f012d4d9d 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -3,7 +3,7 @@
  *				back to source text
  *
  * IDENTIFICATION
- *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.101 2002/05/02 18:44:11 tgl Exp $
+ *	  $Header: /cvsroot/pgsql/src/backend/utils/adt/ruleutils.c,v 1.102 2002/05/03 20:15:02 tgl Exp $
  *
  *	  This software is copyrighted by Jan Wieck - Hamburg.
  *
@@ -54,6 +54,8 @@
 #include "optimizer/tlist.h"
 #include "parser/keywords.h"
 #include "parser/parse_expr.h"
+#include "parser/parse_func.h"
+#include "parser/parse_oper.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rewriteSupport.h"
@@ -124,12 +126,13 @@ static void get_utility_query_def(Query *query, deparse_context *context);
 static void get_basic_select_query(Query *query, deparse_context *context);
 static void get_setop_query(Node *setOp, Query *query,
 							deparse_context *context);
-static void get_rule_sortgroupclause(SortClause *srt, List *tlist,
+static Node *get_rule_sortgroupclause(SortClause *srt, List *tlist,
 						 bool force_colno,
 						 deparse_context *context);
 static void get_names_for_var(Var *var, deparse_context *context,
 				  char **refname, char **attname);
 static void get_rule_expr(Node *node, deparse_context *context);
+static void get_oper_expr(Expr *expr, deparse_context *context);
 static void get_func_expr(Expr *expr, deparse_context *context);
 static void get_agg_expr(Aggref *aggref, deparse_context *context);
 static Node *strip_type_coercion(Node *expr, Oid resultType);
@@ -142,6 +145,9 @@ static void get_from_clause_item(Node *jtnode, Query *query,
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
 				 StringInfo buf);
 static bool tleIsArrayAssign(TargetEntry *tle);
+static char *generate_relation_name(Oid relid);
+static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes);
+static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static char *get_relid_attribute_name(Oid relid, AttrNumber attnum);
 
 #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
@@ -281,7 +287,6 @@ pg_do_getviewdef(Oid viewoid)
 	TupleDesc	rulettc;
 	StringInfoData buf;
 	int			len;
-	char	   *viewname;
 
 	/*
 	 * Connect to SPI manager
@@ -310,14 +315,13 @@ pg_do_getviewdef(Oid viewoid)
 	/*
 	 * Get the pg_rewrite tuple for the view's SELECT rule
 	 */
-	viewname = get_rel_name(viewoid);
 	args[0] = ObjectIdGetDatum(viewoid);
 	args[1] = PointerGetDatum(ViewSelectRuleName);
 	nulls[0] = ' ';
 	nulls[1] = ' ';
 	spirc = SPI_execp(plan_getviewrule, args, nulls, 2);
 	if (spirc != SPI_OK_SELECT)
-		elog(ERROR, "failed to get pg_rewrite tuple for view %s", viewname);
+		elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
 	initStringInfo(&buf);
 	if (SPI_processed != 1)
 		appendStringInfo(&buf, "Not a view");
@@ -357,14 +361,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 	text	   *indexdef;
 	HeapTuple	ht_idx;
 	HeapTuple	ht_idxrel;
-	HeapTuple	ht_indrel;
 	HeapTuple	ht_am;
 	Form_pg_index idxrec;
 	Form_pg_class idxrelrec;
-	Form_pg_class indrelrec;
 	Form_pg_am	amrec;
+	Oid			indrelid;
 	int			len;
 	int			keyno;
+	Oid			keycoltypes[INDEX_MAX_KEYS];
 	StringInfoData buf;
 	StringInfoData keybuf;
 	char	   *sep;
@@ -379,26 +383,19 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 		elog(ERROR, "syscache lookup for index %u failed", indexrelid);
 	idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
 
+	indrelid = idxrec->indrelid;
+	Assert(indexrelid == idxrec->indexrelid);
+
 	/*
 	 * Fetch the pg_class tuple of the index relation
 	 */
 	ht_idxrel = SearchSysCache(RELOID,
-							   ObjectIdGetDatum(idxrec->indexrelid),
+							   ObjectIdGetDatum(indexrelid),
 							   0, 0, 0);
 	if (!HeapTupleIsValid(ht_idxrel))
-		elog(ERROR, "syscache lookup for relid %u failed", idxrec->indexrelid);
+		elog(ERROR, "syscache lookup for relid %u failed", indexrelid);
 	idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
 
-	/*
-	 * Fetch the pg_class tuple of the indexed relation
-	 */
-	ht_indrel = SearchSysCache(RELOID,
-							   ObjectIdGetDatum(idxrec->indrelid),
-							   0, 0, 0);
-	if (!HeapTupleIsValid(ht_indrel))
-		elog(ERROR, "syscache lookup for relid %u failed", idxrec->indrelid);
-	indrelrec = (Form_pg_class) GETSTRUCT(ht_indrel);
-
 	/*
 	 * Fetch the pg_am tuple of the index' access method
 	 */
@@ -410,13 +407,14 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 	amrec = (Form_pg_am) GETSTRUCT(ht_am);
 
 	/*
-	 * Start the index definition
+	 * Start the index definition.  Note that the index's name should never
+	 * be schema-qualified, but the indexed rel's name may be.
 	 */
 	initStringInfo(&buf);
 	appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
 					 idxrec->indisunique ? "UNIQUE " : "",
 					 quote_identifier(NameStr(idxrelrec->relname)),
-					 quote_identifier(NameStr(indrelrec->relname)),
+					 generate_relation_name(indrelid),
 					 quote_identifier(NameStr(amrec->amname)));
 
 	/*
@@ -427,26 +425,28 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 	for (keyno = 0; keyno < INDEX_MAX_KEYS; keyno++)
 	{
 		AttrNumber	attnum = idxrec->indkey[keyno];
+		char	   *attname;
 
 		if (attnum == InvalidAttrNumber)
 			break;
 
+		attname = get_relid_attribute_name(indrelid, attnum);
+		keycoltypes[keyno] = get_atttype(indrelid, attnum);
+
 		appendStringInfo(&keybuf, sep);
 		sep = ", ";
 
 		/*
 		 * Add the indexed field name
 		 */
-		appendStringInfo(&keybuf, "%s",
-			  quote_identifier(get_relid_attribute_name(idxrec->indrelid,
-														attnum)));
+		appendStringInfo(&keybuf, "%s", quote_identifier(attname));
 
 		/*
 		 * If not a functional index, add the operator class name
 		 */
 		if (idxrec->indproc == InvalidOid)
 			get_opclass_name(idxrec->indclass[keyno],
-							 get_atttype(idxrec->indrelid, attnum),
+							 keycoltypes[keyno],
 							 &keybuf);
 	}
 
@@ -455,22 +455,13 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 		/*
 		 * For functional index say 'func (attrs) opclass'
 		 */
-		HeapTuple	proctup;
-		Form_pg_proc procStruct;
-
-		proctup = SearchSysCache(PROCOID,
-								 ObjectIdGetDatum(idxrec->indproc),
-								 0, 0, 0);
-		if (!HeapTupleIsValid(proctup))
-			elog(ERROR, "cache lookup for proc %u failed", idxrec->indproc);
-		procStruct = (Form_pg_proc) GETSTRUCT(proctup);
-
 		appendStringInfo(&buf, "%s(%s)",
-						 quote_identifier(NameStr(procStruct->proname)),
+						 generate_function_name(idxrec->indproc,
+												keyno, keycoltypes),
 						 keybuf.data);
-		get_opclass_name(idxrec->indclass[0], procStruct->prorettype, &buf);
-
-		ReleaseSysCache(proctup);
+		get_opclass_name(idxrec->indclass[0],
+						 get_func_rettype(idxrec->indproc),
+						 &buf);
 	}
 	else
 	{
@@ -480,7 +471,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 		appendStringInfo(&buf, "%s", keybuf.data);
 	}
 
-	appendStringInfo(&buf, ")");
+	appendStringInfoChar(&buf, ')');
 
 	/*
 	 * If it's a partial index, decompile and append the predicate
@@ -506,8 +497,7 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 		if (node && IsA(node, List))
 			node = (Node *) make_ands_explicit((List *) node);
 		/* Deparse */
-		context = deparse_context_for(NameStr(indrelrec->relname),
-									  idxrec->indrelid);
+		context = deparse_context_for(get_rel_name(indrelid), indrelid);
 		str = deparse_expression(node, context, false);
 		appendStringInfo(&buf, " WHERE %s", str);
 	}
@@ -525,7 +515,6 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
 
 	ReleaseSysCache(ht_idx);
 	ReleaseSysCache(ht_idxrel);
-	ReleaseSysCache(ht_indrel);
 	ReleaseSysCache(ht_am);
 
 	PG_RETURN_TEXT_P(indexdef);
@@ -856,7 +845,6 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
 	if (ev_action != NULL)
 		actions = (List *) stringToNode(ev_action);
 
-
 	/*
 	 * Build the rules definition text
 	 */
@@ -889,8 +877,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc)
 	}
 
 	/* The relation the rule is fired on */
-	appendStringInfo(buf, " TO %s",
-					 quote_identifier(get_rel_name(ev_class)));
+	appendStringInfo(buf, " TO %s", generate_relation_name(ev_class));
 	if (ev_attr > 0)
 		appendStringInfo(buf, ".%s",
 					  quote_identifier(get_relid_attribute_name(ev_class,
@@ -1126,12 +1113,16 @@ get_select_query_def(Query *query, deparse_context *context)
 		foreach(l, query->sortClause)
 		{
 			SortClause *srt = (SortClause *) lfirst(l);
+			Node	   *sortexpr;
+			Oid			sortcoltype;
 			char	   *opname;
 
 			appendStringInfo(buf, sep);
-			get_rule_sortgroupclause(srt, query->targetList,
-									 force_colno, context);
-			opname = get_opname(srt->sortop);
+			sortexpr = get_rule_sortgroupclause(srt, query->targetList,
+												force_colno, context);
+			sortcoltype = exprType(sortexpr);
+			opname = generate_operator_name(srt->sortop,
+											sortcoltype, sortcoltype);
 			if (strcmp(opname, "<") != 0)
 			{
 				if (strcmp(opname, ">") == 0)
@@ -1315,8 +1306,10 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context)
 
 /*
  * Display a sort/group clause.
+ *
+ * Also returns the expression tree, so caller need not find it again.
  */
-static void
+static Node *
 get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno,
 						 deparse_context *context)
 {
@@ -1339,6 +1332,8 @@ get_rule_sortgroupclause(SortClause *srt, List *tlist, bool force_colno,
 	}
 	else
 		get_rule_expr(expr, context);
+
+	return expr;
 }
 
 /* ----------
@@ -1361,7 +1356,7 @@ get_insert_query_def(Query *query, deparse_context *context)
 	foreach(l, query->rtable)
 	{
 		rte = (RangeTblEntry *) lfirst(l);
-		if (rte->subquery == NULL)
+		if (rte->rtekind != RTE_SUBQUERY)
 			continue;
 		if (select_rte)
 			elog(ERROR, "get_insert_query_def: too many RTEs in INSERT!");
@@ -1372,8 +1367,9 @@ get_insert_query_def(Query *query, deparse_context *context)
 	 * Start the query with INSERT INTO relname
 	 */
 	rte = rt_fetch(query->resultRelation, query->rtable);
+	Assert(rte->rtekind == RTE_RELATION);
 	appendStringInfo(buf, "INSERT INTO %s",
-					 quote_identifier(rte->eref->aliasname));
+					 generate_relation_name(rte->relid));
 
 	/* Add the insert-column-names list */
 	sep = " (";
@@ -1429,9 +1425,10 @@ get_update_query_def(Query *query, deparse_context *context)
 	 * Start the query with UPDATE relname SET
 	 */
 	rte = rt_fetch(query->resultRelation, query->rtable);
+	Assert(rte->rtekind == RTE_RELATION);
 	appendStringInfo(buf, "UPDATE %s%s SET ",
 					 only_marker(rte),
-					 quote_identifier(rte->eref->aliasname));
+					 generate_relation_name(rte->relid));
 
 	/* Add the comma separated list of 'attname = value' */
 	sep = "";
@@ -1482,9 +1479,10 @@ get_delete_query_def(Query *query, deparse_context *context)
 	 * Start the query with DELETE FROM relname
 	 */
 	rte = rt_fetch(query->resultRelation, query->rtable);
+	Assert(rte->rtekind == RTE_RELATION);
 	appendStringInfo(buf, "DELETE FROM %s%s",
 					 only_marker(rte),
-					 quote_identifier(rte->eref->aliasname));
+					 generate_relation_name(rte->relid));
 
 	/* Add a WHERE clause if given */
 	if (query->jointree->quals != NULL)
@@ -1509,7 +1507,8 @@ get_utility_query_def(Query *query, deparse_context *context)
 		NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
 
 		appendStringInfo(buf, "NOTIFY %s",
-						 quote_identifier(stmt->relation->relname));
+						 quote_qualified_identifier(stmt->relation->schemaname,
+													stmt->relation->relname));
 	}
 	else
 		elog(ERROR, "get_utility_query_def: unexpected statement type");
@@ -1628,52 +1627,11 @@ get_rule_expr(Node *node, deparse_context *context)
 				switch (expr->opType)
 				{
 					case OP_EXPR:
-						appendStringInfoChar(buf, '(');
-						if (length(args) == 2)
-						{
-							/* binary operator */
-							get_rule_expr((Node *) lfirst(args), context);
-							appendStringInfo(buf, " %s ",
-								get_opname(((Oper *) expr->oper)->opno));
-							get_rule_expr((Node *) lsecond(args), context);
-						}
-						else
-						{
-							/* unary operator --- but which side? */
-							Oid			opno = ((Oper *) expr->oper)->opno;
-							HeapTuple	tp;
-							Form_pg_operator optup;
-
-							tp = SearchSysCache(OPEROID,
-												ObjectIdGetDatum(opno),
-												0, 0, 0);
-							if (!HeapTupleIsValid(tp))
-								elog(ERROR, "cache lookup for operator %u failed", opno);
-							optup = (Form_pg_operator) GETSTRUCT(tp);
-							switch (optup->oprkind)
-							{
-								case 'l':
-									appendStringInfo(buf, "%s ",
-													 get_opname(opno));
-									get_rule_expr((Node *) lfirst(args),
-												  context);
-									break;
-								case 'r':
-									get_rule_expr((Node *) lfirst(args),
-												  context);
-									appendStringInfo(buf, " %s",
-													 get_opname(opno));
-									break;
-								default:
-									elog(ERROR, "get_rule_expr: bogus oprkind");
-							}
-							ReleaseSysCache(tp);
-						}
-						appendStringInfoChar(buf, ')');
+						get_oper_expr(expr, context);
 						break;
 
 					case FUNC_EXPR:
-						get_func_expr((Expr *) node, context);
+						get_func_expr(expr, context);
 						break;
 
 					case OR_EXPR:
@@ -1922,9 +1880,69 @@ get_rule_expr(Node *node, deparse_context *context)
 }
 
 
-/* ----------
+/*
+ * get_oper_expr			- Parse back an Oper node
+ */
+static void
+get_oper_expr(Expr *expr, deparse_context *context)
+{
+	StringInfo	buf = context->buf;
+	Oid			opno = ((Oper *) expr->oper)->opno;
+	List	   *args = expr->args;
+
+	appendStringInfoChar(buf, '(');
+	if (length(args) == 2)
+	{
+		/* binary operator */
+		Node   *arg1 = (Node *) lfirst(args);
+		Node   *arg2 = (Node *) lsecond(args);
+
+		get_rule_expr(arg1, context);
+		appendStringInfo(buf, " %s ",
+						 generate_operator_name(opno,
+												exprType(arg1),
+												exprType(arg2)));
+		get_rule_expr(arg2, context);
+	}
+	else
+	{
+		/* unary operator --- but which side? */
+		Node	   *arg = (Node *) lfirst(args);
+		HeapTuple	tp;
+		Form_pg_operator optup;
+
+		tp = SearchSysCache(OPEROID,
+							ObjectIdGetDatum(opno),
+							0, 0, 0);
+		if (!HeapTupleIsValid(tp))
+			elog(ERROR, "cache lookup for operator %u failed", opno);
+		optup = (Form_pg_operator) GETSTRUCT(tp);
+		switch (optup->oprkind)
+		{
+			case 'l':
+				appendStringInfo(buf, "%s ",
+								 generate_operator_name(opno,
+														InvalidOid,
+														exprType(arg)));
+				get_rule_expr(arg, context);
+				break;
+			case 'r':
+				get_rule_expr(arg, context);
+				appendStringInfo(buf, " %s",
+								 generate_operator_name(opno,
+														exprType(arg),
+														InvalidOid));
+				break;
+			default:
+				elog(ERROR, "get_rule_expr: bogus oprkind");
+		}
+		ReleaseSysCache(tp);
+	}
+	appendStringInfoChar(buf, ')');
+}
+
+/*
  * get_func_expr			- Parse back a Func node
- * ----------
  */
 static void
 get_func_expr(Expr *expr, deparse_context *context)
@@ -1932,25 +1950,12 @@ get_func_expr(Expr *expr, deparse_context *context)
 	StringInfo	buf = context->buf;
 	Func	   *func = (Func *) (expr->oper);
 	Oid			funcoid = func->funcid;
-	HeapTuple	proctup;
-	Form_pg_proc procStruct;
-	char	   *proname;
 	int32		coercedTypmod;
+	Oid			argtypes[FUNC_MAX_ARGS];
+	int			nargs;
 	List	   *l;
 	char	   *sep;
 
-	/*
-	 * Get the functions pg_proc tuple
-	 */
-	proctup = SearchSysCache(PROCOID,
-							 ObjectIdGetDatum(funcoid),
-							 0, 0, 0);
-	if (!HeapTupleIsValid(proctup))
-		elog(ERROR, "cache lookup for proc %u failed", funcoid);
-
-	procStruct = (Form_pg_proc) GETSTRUCT(proctup);
-	proname = NameStr(procStruct->proname);
-
 	/*
 	 * Check to see if function is a length-coercion function for some
 	 * datatype.  If so, display the operation as a type cast.
@@ -1958,6 +1963,7 @@ get_func_expr(Expr *expr, deparse_context *context)
 	if (exprIsLengthCoercion((Node *) expr, &coercedTypmod))
 	{
 		Node	   *arg = lfirst(expr->args);
+		Oid			rettype = get_func_rettype(funcoid);
 		char	   *typdesc;
 
 		/*
@@ -1966,7 +1972,7 @@ get_func_expr(Expr *expr, deparse_context *context)
 		 *
 		 * XXX Are there any cases where this is a bad idea?
 		 */
-		arg = strip_type_coercion(arg, procStruct->prorettype);
+		arg = strip_type_coercion(arg, rettype);
 
 		appendStringInfoChar(buf, '(');
 		get_rule_expr(arg, context);
@@ -1978,19 +1984,28 @@ get_func_expr(Expr *expr, deparse_context *context)
 		 * to quote the result of format_type_with_typemod: it takes
 		 * care of double-quoting any identifier that needs it.
 		 */
-		typdesc = format_type_with_typemod(procStruct->prorettype,
-										   coercedTypmod);
+		typdesc = format_type_with_typemod(rettype, coercedTypmod);
 		appendStringInfo(buf, ")::%s", typdesc);
 		pfree(typdesc);
 
-		ReleaseSysCache(proctup);
 		return;
 	}
 
 	/*
-	 * Normal function: display as proname(args)
+	 * Normal function: display as proname(args).  First we need to extract
+	 * the argument datatypes.
 	 */
-	appendStringInfo(buf, "%s(", quote_identifier(proname));
+	nargs = 0;
+	foreach(l, expr->args)
+	{
+		Assert(nargs < FUNC_MAX_ARGS);
+		argtypes[nargs] = exprType((Node *) lfirst(l));
+		nargs++;
+	}
+	
+	appendStringInfo(buf, "%s(",
+					 generate_function_name(funcoid, nargs, argtypes));
+
 	sep = "";
 	foreach(l, expr->args)
 	{
@@ -1999,47 +2014,25 @@ get_func_expr(Expr *expr, deparse_context *context)
 		get_rule_expr((Node *) lfirst(l), context);
 	}
 	appendStringInfoChar(buf, ')');
-
-	ReleaseSysCache(proctup);
 }
 
-/* ----------
+/*
  * get_agg_expr			- Parse back an Aggref node
- * ----------
  */
 static void
 get_agg_expr(Aggref *aggref, deparse_context *context)
 {
 	StringInfo	buf = context->buf;
-	HeapTuple	proctup;
-	Form_pg_proc procStruct;
-	char	   *proname;
+	Oid			argtype = exprType(aggref->target);
 
-	/*
-	 * Get the aggregate's pg_proc tuple
-	 */
-	proctup = SearchSysCache(PROCOID,
-							 ObjectIdGetDatum(aggref->aggfnoid),
-							 0, 0, 0);
-	if (!HeapTupleIsValid(proctup))
-		elog(ERROR, "cache lookup for proc %u failed", aggref->aggfnoid);
-
-	procStruct = (Form_pg_proc) GETSTRUCT(proctup);
-	proname = NameStr(procStruct->proname);
-
-	/*
-	 * Display it
-	 */
 	appendStringInfo(buf, "%s(%s",
-					 quote_identifier(proname),
+					 generate_function_name(aggref->aggfnoid, 1, &argtype),
 					 aggref->aggdistinct ? "DISTINCT " : "");
 	if (aggref->aggstar)
 		appendStringInfo(buf, "*");
 	else
 		get_rule_expr(aggref->target, context);
 	appendStringInfoChar(buf, ')');
-
-	ReleaseSysCache(proctup);
 }
 
 
@@ -2064,7 +2057,8 @@ strip_type_coercion(Node *expr, Oid resultType)
 	if (IsA(expr, RelabelType))
 		return strip_type_coercion(((RelabelType *) expr)->arg, resultType);
 
-	if (IsA(expr, Expr) &&((Expr *) expr)->opType == FUNC_EXPR)
+	if (IsA(expr, Expr) &&
+		((Expr *) expr)->opType == FUNC_EXPR)
 	{
 		Func	   *func;
 		HeapTuple	procTuple;
@@ -2173,9 +2167,8 @@ get_const_expr(Const *constval, deparse_context *context)
 	if (constval->constisnull)
 	{
 		/*
-		 * Always label the type of a NULL constant.  This not only
-		 * prevents misdecisions about the type, but it ensures that our
-		 * output is a valid b_expr.
+		 * Always label the type of a NULL constant to prevent misdecisions
+		 * about type when reparsing.
 		 */
 		appendStringInfo(buf, "NULL::%s",
 						 format_type_with_typemod(constval->consttype, -1));
@@ -2201,7 +2194,7 @@ get_const_expr(Const *constval, deparse_context *context)
 		case INT4OID:
 		case OIDOID:			/* int types */
 		case FLOAT4OID:
-		case FLOAT8OID: /* float types */
+		case FLOAT8OID:			/* float types */
 			/* These types are printed without quotes */
 			appendStringInfo(buf, extval);
 			break;
@@ -2289,6 +2282,12 @@ get_sublink_expr(Node *node, deparse_context *context)
 
 	need_paren = true;
 
+	/*
+	 * XXX we assume here that we can get away without qualifying the
+	 * operator name.  Since the name may imply multiple physical operators
+	 * it's rather difficult to do otherwise --- in fact, if the operators
+	 * are in different namespaces any attempt to qualify would surely fail.
+	 */
 	switch (sublink->subLinkType)
 	{
 		case EXISTS_SUBLINK:
@@ -2391,7 +2390,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 				/* Normal relation RTE */
 				appendStringInfo(buf, "%s%s",
 								 only_marker(rte),
-								 quote_identifier(get_rel_name(rte->relid)));
+								 generate_relation_name(rte->relid));
 				break;
 			case RTE_SUBQUERY:
 				/* Subquery RTE */
@@ -2506,7 +2505,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 			 nodeTag(jtnode));
 }
 
-/* ----------
+/*
  * get_opclass_name			- fetch name of an index operator class
  *
  * The opclass name is appended (after a space) to buf.
@@ -2514,7 +2513,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
  * Output is suppressed if the opclass is the default for the given
  * actual_datatype.  (If you don't want this behavior, just pass
  * InvalidOid for actual_datatype.)
- * ----------
  */
 static void
 get_opclass_name(Oid opclass, Oid actual_datatype,
@@ -2522,6 +2520,8 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
 {
 	HeapTuple	ht_opc;
 	Form_pg_opclass opcrec;
+	char	   *opcname;
+	char	   *nspname;
 
 	ht_opc = SearchSysCache(CLAOID,
 							ObjectIdGetDatum(opclass),
@@ -2530,14 +2530,24 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
 		elog(ERROR, "cache lookup failed for opclass %u", opclass);
 	opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
 	if (actual_datatype != opcrec->opcintype || !opcrec->opcdefault)
-		appendStringInfo(buf, " %s",
-						 quote_identifier(NameStr(opcrec->opcname)));
+	{
+		/* Okay, we need the opclass name.  Do we need to qualify it? */
+		opcname = NameStr(opcrec->opcname);
+		if (OpclassIsVisible(opclass))
+			appendStringInfo(buf, " %s", quote_identifier(opcname));
+		else
+		{
+			nspname = get_namespace_name(opcrec->opcnamespace);
+			appendStringInfo(buf, " %s.%s",
+							 quote_identifier(nspname),
+							 quote_identifier(opcname));
+		}
+	}
 	ReleaseSysCache(ht_opc);
 }
 
-/* ----------
+/*
  * tleIsArrayAssign			- check for array assignment
- * ----------
  */
 static bool
 tleIsArrayAssign(TargetEntry *tle)
@@ -2561,12 +2571,11 @@ tleIsArrayAssign(TargetEntry *tle)
 	return true;
 }
 
-/* ----------
+/*
  * quote_identifier			- Quote an identifier only if needed
  *
  * When quotes are needed, we palloc the required space; slightly
  * space-wasteful but well worth it for notational simplicity.
- * ----------
  */
 const char *
 quote_identifier(const char *ident)
@@ -2623,12 +2632,11 @@ quote_identifier(const char *ident)
 	return result;
 }
 
-/* ----------
+/*
  * quote_qualified_identifier	- Quote a possibly-qualified identifier
  *
  * Return a name of the form namespace.ident, or just ident if namespace
  * is NULL, quoting each component if necessary.  The result is palloc'd.
- * ----------
  */
 char *
 quote_qualified_identifier(const char *namespace,
@@ -2643,13 +2651,173 @@ quote_qualified_identifier(const char *namespace,
 	return buf.data;
 }
 
-/* ----------
+/*
+ * generate_relation_name
+ *		Compute the name to display for a relation specified by OID
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+static char *
+generate_relation_name(Oid relid)
+{
+	HeapTuple	tp;
+	Form_pg_class reltup;
+	char	   *nspname;
+	char	   *result;
+
+	tp = SearchSysCache(RELOID,
+						ObjectIdGetDatum(relid),
+						0, 0, 0);
+	if (!HeapTupleIsValid(tp))
+		elog(ERROR, "cache lookup of relation %u failed", relid);
+	reltup = (Form_pg_class) GETSTRUCT(tp);
+
+	/* Qualify the name if not visible in search path */
+	if (RelationIsVisible(relid))
+		nspname = NULL;
+	else
+		nspname = get_namespace_name(reltup->relnamespace);
+
+	result = quote_qualified_identifier(nspname, NameStr(reltup->relname));
+
+	ReleaseSysCache(tp);
+
+	return result;
+}
+
+/*
+ * generate_function_name
+ *		Compute the name to display for a function specified by OID,
+ *		given that it is being called with the specified actual arg types.
+ *		(Arg types matter because of ambiguous-function resolution rules.)
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+static char *
+generate_function_name(Oid funcid, int nargs, Oid *argtypes)
+{
+	HeapTuple	proctup;
+	Form_pg_proc procform;
+	char	   *proname;
+	char	   *nspname;
+	char	   *result;
+	FuncDetailCode p_result;
+	Oid			p_funcid;
+	Oid			p_rettype;
+	bool		p_retset;
+	Oid		   *p_true_typeids;
+
+	proctup = SearchSysCache(PROCOID,
+							 ObjectIdGetDatum(funcid),
+							 0, 0, 0);
+	if (!HeapTupleIsValid(proctup))
+		elog(ERROR, "cache lookup of function %u failed", funcid);
+	procform = (Form_pg_proc) GETSTRUCT(proctup);
+	proname = NameStr(procform->proname);
+	Assert(nargs == procform->pronargs);
+
+	/*
+	 * The idea here is to schema-qualify only if the parser would fail to
+	 * resolve the correct function given the unqualified func name
+	 * with the specified argtypes.
+	 */
+	p_result = func_get_detail(makeList1(makeString(proname)),
+							   NIL, nargs, argtypes,
+							   &p_funcid, &p_rettype,
+							   &p_retset, &p_true_typeids);
+	if (p_result != FUNCDETAIL_NOTFOUND && p_funcid == funcid)
+		nspname = NULL;
+	else
+		nspname = get_namespace_name(procform->pronamespace);
+
+	result = quote_qualified_identifier(nspname, proname);
+
+	ReleaseSysCache(proctup);
+
+	return result;
+}
+
+/*
+ * generate_operator_name
+ *		Compute the name to display for an operator specified by OID,
+ *		given that it is being called with the specified actual arg types.
+ *		(Arg types matter because of ambiguous-operator resolution rules.
+ *		Pass InvalidOid for unused arg of a unary operator.)
+ *
+ * The result includes all necessary quoting and schema-prefixing,
+ * plus the OPERATOR() decoration needed to use a qualified operator name
+ * in an expression.
+ */
+static char *
+generate_operator_name(Oid operid, Oid arg1, Oid arg2)
+{
+	StringInfoData buf;
+	HeapTuple	opertup;
+	Form_pg_operator operform;
+	char	   *oprname;
+	char	   *nspname;
+	Operator	p_result;
+
+	initStringInfo(&buf);
+
+	opertup = SearchSysCache(OPEROID,
+							 ObjectIdGetDatum(operid),
+							 0, 0, 0);
+	if (!HeapTupleIsValid(opertup))
+		elog(ERROR, "cache lookup of operator %u failed", operid);
+	operform = (Form_pg_operator) GETSTRUCT(opertup);
+	oprname = NameStr(operform->oprname);
+
+	/*
+	 * The idea here is to schema-qualify only if the parser would fail to
+	 * resolve the correct operator given the unqualified op name
+	 * with the specified argtypes.
+	 */
+	switch (operform->oprkind)
+	{
+		case 'b':
+			p_result = oper(makeList1(makeString(oprname)), arg1, arg2, true);
+			break;
+		case 'l':
+			p_result = left_oper(makeList1(makeString(oprname)), arg2, true);
+			break;
+		case 'r':
+			p_result = right_oper(makeList1(makeString(oprname)), arg1, true);
+			break;
+		default:
+			elog(ERROR, "unexpected oprkind %c for operator %u",
+				 operform->oprkind, operid);
+			p_result = NULL;	/* keep compiler quiet */
+			break;
+	}
+
+	if (p_result != NULL && oprid(p_result) == operid)
+		nspname = NULL;
+	else
+	{
+		nspname = get_namespace_name(operform->oprnamespace);
+		appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
+	}
+
+	appendStringInfo(&buf, "%s", oprname);
+
+	if (nspname)
+		appendStringInfoChar(&buf, ')');
+
+	if (p_result != NULL)
+		ReleaseSysCache(p_result);
+
+	ReleaseSysCache(opertup);
+
+	return buf.data;
+}
+
+/*
  * get_relid_attribute_name
  *		Get an attribute name by its relations Oid and its attnum
  *
  * Same as underlying syscache routine get_attname(), except that error
  * is handled by elog() instead of returning NULL.
- * ----------
  */
 static char *
 get_relid_attribute_name(Oid relid, AttrNumber attnum)
-- 
GitLab