Make ruleutils.c schema-aware. Displayed names are schema-qualified
authorTom Lane
Fri, 3 May 2002 20:15:02 +0000 (20:15 +0000)
committerTom Lane
Fri, 3 May 2002 20:15:02 +0000 (20:15 +0000)
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
src/backend/utils/adt/ruleutils.c

index 2ab9c9a689868cd8dbc9756514d6e30768f9d176..f74f5be2f7bec4637c3a209bbfb37546e4325cdf 100644 (file)
@@ -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);
index 1e4b5713514f2ad5c7ca8217f451bd76be9080f2..b1f012d4d9d85434a58d52e949b0c397ca28b67a 100644 (file)
@@ -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)