#include "optimizer/optimizer.h"
 #include "parser/parse_agg.h"
 #include "parser/parse_func.h"
-#include "parser/parse_node.h"
 #include "parser/parse_oper.h"
 #include "parser/parse_relation.h"
 #include "parser/parser.h"
 {
        StringInfo      buf;                    /* output buffer to append to */
        List       *namespaces;         /* List of deparse_namespace nodes */
+       TupleDesc       resultDesc;             /* if top level of a view, the view's tupdesc */
+       List       *targetList;         /* Current query level's SELECT targetlist */
        List       *windowClause;       /* Current query level's WINDOW clause */
-       List       *windowTList;        /* targetlist for resolving WINDOW clause */
        int                     prettyFlags;    /* enabling of pretty-print functions */
        int                     wrapColumn;             /* max line length, or -1 for no limit */
        int                     indentLevel;    /* current indent level for pretty-print */
        bool            varprefix;              /* true to print prefixes on Vars */
-       ParseExprKind special_exprkind; /* set only for exprkinds needing special
-                                                                        * handling */
+       bool            colNamesVisible;        /* do we care about output column names? */
+       bool            inGroupBy;              /* deparsing GROUP BY clause? */
+       bool            varInOrderBy;   /* deparsing simple Var in ORDER BY? */
        Bitmapset  *appendparents;      /* if not null, map child Vars of these relids
                                                                 * back to the parent rel */
 } deparse_context;
                                                  int prettyFlags, int wrapColumn, int startIndent);
 static void get_values_def(List *values_lists, deparse_context *context);
 static void get_with_clause(Query *query, deparse_context *context);
-static void get_select_query_def(Query *query, deparse_context *context,
-                                                                TupleDesc resultDesc, bool colNamesVisible);
-static void get_insert_query_def(Query *query, deparse_context *context,
-                                                                bool colNamesVisible);
-static void get_update_query_def(Query *query, deparse_context *context,
-                                                                bool colNamesVisible);
+static void get_select_query_def(Query *query, deparse_context *context);
+static void get_insert_query_def(Query *query, deparse_context *context);
+static void get_update_query_def(Query *query, deparse_context *context);
 static void get_update_query_targetlist_def(Query *query, List *targetList,
                                                                                        deparse_context *context,
                                                                                        RangeTblEntry *rte);
-static void get_delete_query_def(Query *query, deparse_context *context,
-                                                                bool colNamesVisible);
-static void get_merge_query_def(Query *query, deparse_context *context,
-                                                               bool colNamesVisible);
+static void get_delete_query_def(Query *query, deparse_context *context);
+static void get_merge_query_def(Query *query, deparse_context *context);
 static void get_utility_query_def(Query *query, deparse_context *context);
-static void get_basic_select_query(Query *query, deparse_context *context,
-                                                                  TupleDesc resultDesc, bool colNamesVisible);
-static void get_target_list(List *targetList, deparse_context *context,
-                                                       TupleDesc resultDesc, bool colNamesVisible);
+static void get_basic_select_query(Query *query, deparse_context *context);
+static void get_target_list(List *targetList, deparse_context *context);
 static void get_setop_query(Node *setOp, Query *query,
-                                                       deparse_context *context,
-                                                       TupleDesc resultDesc, bool colNamesVisible);
+                                                       deparse_context *context);
 static Node *get_rule_sortgroupclause(Index ref, List *tlist,
                                                                          bool force_colno,
                                                                          deparse_context *context);
 static char *generate_function_name(Oid funcid, int nargs,
                                                                        List *argnames, Oid *argtypes,
                                                                        bool has_variadic, bool *use_variadic_p,
-                                                                       ParseExprKind special_exprkind);
+                                                                       bool inGroupBy);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static void add_cast_to(StringInfo buf, Oid typid);
 static char *generate_qualified_type_name(Oid typid);
                /* Set up context with one-deep namespace stack */
                context.buf = &buf;
                context.namespaces = list_make1(&dpns);
+               context.resultDesc = NULL;
+               context.targetList = NIL;
                context.windowClause = NIL;
-               context.windowTList = NIL;
                context.varprefix = true;
                context.prettyFlags = GET_PRETTY_FLAGS(pretty);
                context.wrapColumn = WRAP_COLUMN_DEFAULT;
                context.indentLevel = PRETTYINDENT_STD;
-               context.special_exprkind = EXPR_KIND_NONE;
+               context.colNamesVisible = true;
+               context.inGroupBy = false;
+               context.varInOrderBy = false;
                context.appendparents = NULL;
 
                get_rule_expr(qual, &context, false);
        appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
                                         generate_function_name(trigrec->tgfoid, 0,
                                                                                        NIL, NULL,
-                                                                                       false, NULL, EXPR_KIND_NONE));
+                                                                                       false, NULL, false));
 
        if (trigrec->tgnargs > 0)
        {
                appendStringInfo(&buf, " SUPPORT %s",
                                                 generate_function_name(proc->prosupport, 1,
                                                                                                NIL, argtypes,
-                                                                                               false, NULL, EXPR_KIND_NONE));
+                                                                                               false, NULL, false));
        }
 
        if (oldlen != buf.len)
        initStringInfo(&buf);
        context.buf = &buf;
        context.namespaces = dpcontext;
+       context.resultDesc = NULL;
+       context.targetList = NIL;
        context.windowClause = NIL;
-       context.windowTList = NIL;
        context.varprefix = forceprefix;
        context.prettyFlags = prettyFlags;
        context.wrapColumn = WRAP_COLUMN_DEFAULT;
        context.indentLevel = startIndent;
-       context.special_exprkind = EXPR_KIND_NONE;
+       context.colNamesVisible = true;
+       context.inGroupBy = false;
+       context.varInOrderBy = false;
        context.appendparents = NULL;
 
        get_rule_expr(expr, &context, showimplicit);
 
                context.buf = buf;
                context.namespaces = list_make1(&dpns);
+               context.resultDesc = NULL;
+               context.targetList = NIL;
                context.windowClause = NIL;
-               context.windowTList = NIL;
                context.varprefix = (list_length(query->rtable) != 1);
                context.prettyFlags = prettyFlags;
                context.wrapColumn = WRAP_COLUMN_DEFAULT;
                context.indentLevel = PRETTYINDENT_STD;
-               context.special_exprkind = EXPR_KIND_NONE;
+               context.colNamesVisible = true;
+               context.inGroupBy = false;
+               context.varInOrderBy = false;
                context.appendparents = NULL;
 
                set_deparse_for_query(&dpns, query, NIL);
 
        context.buf = buf;
        context.namespaces = lcons(&dpns, list_copy(parentnamespace));
+       context.resultDesc = NULL;
+       context.targetList = NIL;
        context.windowClause = NIL;
-       context.windowTList = NIL;
        context.varprefix = (parentnamespace != NIL ||
                                                 list_length(query->rtable) != 1);
        context.prettyFlags = prettyFlags;
        context.wrapColumn = wrapColumn;
        context.indentLevel = startIndent;
-       context.special_exprkind = EXPR_KIND_NONE;
+       context.colNamesVisible = colNamesVisible;
+       context.inGroupBy = false;
+       context.varInOrderBy = false;
        context.appendparents = NULL;
 
        set_deparse_for_query(&dpns, query, parentnamespace);
        switch (query->commandType)
        {
                case CMD_SELECT:
-                       get_select_query_def(query, &context, resultDesc, colNamesVisible);
+                       /* We set context.resultDesc only if it's a SELECT */
+                       context.resultDesc = resultDesc;
+                       get_select_query_def(query, &context);
                        break;
 
                case CMD_UPDATE:
-                       get_update_query_def(query, &context, colNamesVisible);
+                       get_update_query_def(query, &context);
                        break;
 
                case CMD_INSERT:
-                       get_insert_query_def(query, &context, colNamesVisible);
+                       get_insert_query_def(query, &context);
                        break;
 
                case CMD_DELETE:
-                       get_delete_query_def(query, &context, colNamesVisible);
+                       get_delete_query_def(query, &context);
                        break;
 
                case CMD_MERGE:
-                       get_merge_query_def(query, &context, colNamesVisible);
+                       get_merge_query_def(query, &context);
                        break;
 
                case CMD_NOTHING:
  * ----------
  */
 static void
-get_select_query_def(Query *query, deparse_context *context,
-                                        TupleDesc resultDesc, bool colNamesVisible)
+get_select_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
-       List       *save_windowclause;
-       List       *save_windowtlist;
        bool            force_colno;
        ListCell   *l;
 
        /* Insert the WITH clause if given */
        get_with_clause(query, context);
 
-       /* Set up context for possible window functions */
-       save_windowclause = context->windowClause;
+       /* Subroutines may need to consult the SELECT targetlist and windowClause */
+       context->targetList = query->targetList;
        context->windowClause = query->windowClause;
-       save_windowtlist = context->windowTList;
-       context->windowTList = query->targetList;
 
        /*
         * If the Query node has a setOperations tree, then it's the top level of
         */
        if (query->setOperations)
        {
-               get_setop_query(query->setOperations, query, context, resultDesc,
-                                               colNamesVisible);
+               get_setop_query(query->setOperations, query, context);
                /* ORDER BY clauses must be simple in this case */
                force_colno = true;
        }
        else
        {
-               get_basic_select_query(query, context, resultDesc, colNamesVisible);
+               get_basic_select_query(query, context);
                force_colno = false;
        }
 
                                appendStringInfoString(buf, " SKIP LOCKED");
                }
        }
-
-       context->windowClause = save_windowclause;
-       context->windowTList = save_windowtlist;
 }
 
 /*
 }
 
 static void
-get_basic_select_query(Query *query, deparse_context *context,
-                                          TupleDesc resultDesc, bool colNamesVisible)
+get_basic_select_query(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        RangeTblEntry *values_rte;
         * VALUES part.  This reverses what transformValuesClause() did at parse
         * time.
         */
-       values_rte = get_simple_values_rte(query, resultDesc);
+       values_rte = get_simple_values_rte(query, context->resultDesc);
        if (values_rte)
        {
                get_values_def(values_rte->values_lists, context);
        }
 
        /* Then we tell what to select (the targetlist) */
-       get_target_list(query->targetList, context, resultDesc, colNamesVisible);
+       get_target_list(query->targetList, context);
 
        /* Add the FROM clause if needed */
        get_from_clause(query, " FROM ", context);
        /* Add the GROUP BY clause if given */
        if (query->groupClause != NULL || query->groupingSets != NULL)
        {
-               ParseExprKind save_exprkind;
+               bool            save_ingroupby;
 
                appendContextKeyword(context, " GROUP BY ",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
                if (query->groupDistinct)
                        appendStringInfoString(buf, "DISTINCT ");
 
-               save_exprkind = context->special_exprkind;
-               context->special_exprkind = EXPR_KIND_GROUP_BY;
+               save_ingroupby = context->inGroupBy;
+               context->inGroupBy = true;
 
                if (query->groupingSets == NIL)
                {
                        }
                }
 
-               context->special_exprkind = save_exprkind;
+               context->inGroupBy = save_ingroupby;
        }
 
        /* Add the HAVING clause if given */
  * get_target_list                     - Parse back a SELECT target list
  *
  * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
- *
- * resultDesc and colNamesVisible are as for get_query_def()
  * ----------
  */
 static void
-get_target_list(List *targetList, deparse_context *context,
-                               TupleDesc resultDesc, bool colNamesVisible)
+get_target_list(List *targetList, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        StringInfoData targetbuf;
                         * assigned column name explicitly.  Otherwise, show it only if
                         * it's not FigureColname's fallback.
                         */
-                       attname = colNamesVisible ? NULL : "?column?";
+                       attname = context->colNamesVisible ? NULL : "?column?";
                }
 
                /*
                 * effects of any column RENAME that's been done on the view).
                 * Otherwise, just use what we can find in the TLE.
                 */
-               if (resultDesc && colno <= resultDesc->natts)
-                       colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
+               if (context->resultDesc && colno <= context->resultDesc->natts)
+                       colname = NameStr(TupleDescAttr(context->resultDesc,
+                                                                                       colno - 1)->attname);
                else
                        colname = tle->resname;
 
 }
 
 static void
-get_setop_query(Node *setOp, Query *query, deparse_context *context,
-                               TupleDesc resultDesc, bool colNamesVisible)
+get_setop_query(Node *setOp, Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        bool            need_paren;
                                          subquery->limitCount);
                if (need_paren)
                        appendStringInfoChar(buf, '(');
-               get_query_def(subquery, buf, context->namespaces, resultDesc,
-                                         colNamesVisible,
+               get_query_def(subquery, buf, context->namespaces,
+                                         context->resultDesc, context->colNamesVisible,
                                          context->prettyFlags, context->wrapColumn,
                                          context->indentLevel);
                if (need_paren)
        {
                SetOperationStmt *op = (SetOperationStmt *) setOp;
                int                     subindent;
+               bool            save_colnamesvisible;
 
                /*
                 * We force parens when nesting two SetOperationStmts, except when the
                else
                        subindent = 0;
 
-               get_setop_query(op->larg, query, context, resultDesc, colNamesVisible);
+               get_setop_query(op->larg, query, context);
 
                if (need_paren)
                        appendContextKeyword(context, ") ", -subindent, 0, 0);
                        subindent = 0;
                appendContextKeyword(context, "", subindent, 0, 0);
 
-               get_setop_query(op->rarg, query, context, resultDesc, false);
+               /*
+                * The output column names of the RHS sub-select don't matter.
+                */
+               save_colnamesvisible = context->colNamesVisible;
+               context->colNamesVisible = false;
+
+               get_setop_query(op->rarg, query, context);
+
+               context->colNamesVisible = save_colnamesvisible;
 
                if (PRETTY_INDENT(context))
                        context->indentLevel -= subindent;
         * Use column-number form if requested by caller.  Otherwise, if
         * expression is a constant, force it to be dumped with an explicit cast
         * as decoration --- this is because a simple integer constant is
-        * ambiguous (and will be misinterpreted by findTargetlistEntry()) if we
-        * dump it without any decoration.  If it's anything more complex than a
-        * simple Var, then force extra parens around it, to ensure it can't be
-        * misinterpreted as a cube() or rollup() construct.
+        * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
+        * we dump it without any decoration.  Similarly, if it's just a Var,
+        * there is risk of misinterpretation if the column name is reassigned in
+        * the SELECT list, so we may need to force table qualification.  And, if
+        * it's anything more complex than a simple Var, then force extra parens
+        * around it, to ensure it can't be misinterpreted as a cube() or rollup()
+        * construct.
         */
        if (force_colno)
        {
                Assert(!tle->resjunk);
                appendStringInfo(buf, "%d", tle->resno);
        }
-       else if (expr && IsA(expr, Const))
+       else if (!expr)
+                /* do nothing, probably can't happen */ ;
+       else if (IsA(expr, Const))
                get_const_expr((Const *) expr, context, 1);
-       else if (!expr || IsA(expr, Var))
-               get_rule_expr(expr, context, true);
+       else if (IsA(expr, Var))
+       {
+               /* Tell get_variable to check for name conflict */
+               bool            save_varinorderby = context->varInOrderBy;
+
+               context->varInOrderBy = true;
+               (void) get_variable((Var *) expr, 0, false, context);
+               context->varInOrderBy = save_varinorderby;
+       }
        else
        {
                /*
  * ----------
  */
 static void
-get_insert_query_def(Query *query, deparse_context *context,
-                                        bool colNamesVisible)
+get_insert_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        RangeTblEntry *select_rte = NULL;
        {
                appendContextKeyword(context, " RETURNING",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
-               get_target_list(query->returningList, context, NULL, colNamesVisible);
+               get_target_list(query->returningList, context);
        }
 }
 
  * ----------
  */
 static void
-get_update_query_def(Query *query, deparse_context *context,
-                                        bool colNamesVisible)
+get_update_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        RangeTblEntry *rte;
        {
                appendContextKeyword(context, " RETURNING",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
-               get_target_list(query->returningList, context, NULL, colNamesVisible);
+               get_target_list(query->returningList, context);
        }
 }
 
  * ----------
  */
 static void
-get_delete_query_def(Query *query, deparse_context *context,
-                                        bool colNamesVisible)
+get_delete_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        RangeTblEntry *rte;
        {
                appendContextKeyword(context, " RETURNING",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
-               get_target_list(query->returningList, context, NULL, colNamesVisible);
+               get_target_list(query->returningList, context);
        }
 }
 
  * ----------
  */
 static void
-get_merge_query_def(Query *query, deparse_context *context,
-                                       bool colNamesVisible)
+get_merge_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        RangeTblEntry *rte;
        {
                appendContextKeyword(context, " RETURNING",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
-               get_target_list(query->returningList, context, NULL, colNamesVisible);
+               get_target_list(query->returningList, context);
        }
 }
 
        deparse_columns *colinfo;
        char       *refname;
        char       *attname;
+       bool            need_prefix;
 
        /* Find appropriate nesting depth */
        netlevelsup = var->varlevelsup + levelsup;
                attname = get_rte_attribute_name(rte, attnum);
        }
 
-       if (refname && (context->varprefix || attname == NULL))
+       need_prefix = (context->varprefix || attname == NULL);
+
+       /*
+        * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
+        * clause, we may need to add a table-name prefix to prevent
+        * findTargetlistEntrySQL92 from misinterpreting the name as an
+        * output-column name.  To avoid cluttering the output with unnecessary
+        * prefixes, do so only if there is a name match to a SELECT tlist item
+        * that is different from the Var.
+        */
+       if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
+       {
+               int                     colno = 0;
+
+               foreach_node(TargetEntry, tle, context->targetList)
+               {
+                       char       *colname;
+
+                       if (tle->resjunk)
+                               continue;               /* ignore junk entries */
+                       colno++;
+
+                       /* This must match colname-choosing logic in get_target_list() */
+                       if (context->resultDesc && colno <= context->resultDesc->natts)
+                               colname = NameStr(TupleDescAttr(context->resultDesc,
+                                                                                               colno - 1)->attname);
+                       else
+                               colname = tle->resname;
+
+                       if (colname && strcmp(colname, attname) == 0 &&
+                               !equal(var, tle->expr))
+                       {
+                               need_prefix = true;
+                               break;
+                       }
+               }
+       }
+
+       if (refname && need_prefix)
        {
                appendStringInfoString(buf, quote_identifier(refname));
                appendStringInfoChar(buf, '.');
                                                                                        argnames, argtypes,
                                                                                        expr->funcvariadic,
                                                                                        &use_variadic,
-                                                                                       context->special_exprkind));
+                                                                                       context->inGroupBy));
        nargs = 0;
        foreach(l, expr->args)
        {
                funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
                                                                                  argtypes, aggref->aggvariadic,
                                                                                  &use_variadic,
-                                                                                 context->special_exprkind);
+                                                                                 context->inGroupBy);
 
        /* Print the aggregate name, schema-qualified if needed */
        appendStringInfo(buf, "%s(%s", funcname,
        if (!funcname)
                funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
                                                                                  argtypes, false, NULL,
-                                                                                 context->special_exprkind);
+                                                                                 context->inGroupBy);
 
        appendStringInfo(buf, "%s(", funcname);
 
                        if (wc->name)
                                appendStringInfoString(buf, quote_identifier(wc->name));
                        else
-                               get_rule_windowspec(wc, context->windowTList, context);
+                               get_rule_windowspec(wc, context->targetList, context);
                        break;
                }
        }
        appendStringInfo(buf, " TABLESAMPLE %s (",
                                         generate_function_name(tablesample->tsmhandler, 1,
                                                                                        NIL, argtypes,
-                                                                                       false, NULL, EXPR_KIND_NONE));
+                                                                                       false, NULL, false));
 
        nargs = 0;
        foreach(l, tablesample->args)
  * the output.  For non-FuncExpr cases, has_variadic should be false and
  * use_variadic_p can be NULL.
  *
+ * inGroupBy must be true if we're deparsing a GROUP BY clause.
+ *
  * The result includes all necessary quoting and schema-prefixing.
  */
 static char *
 generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
                                           bool has_variadic, bool *use_variadic_p,
-                                          ParseExprKind special_exprkind)
+                                          bool inGroupBy)
 {
        char       *result;
        HeapTuple       proctup;
 
        /*
         * Due to parser hacks to avoid needing to reserve CUBE, we need to force
-        * qualification in some special cases.
+        * qualification of some function names within GROUP BY.
         */
-       if (special_exprkind == EXPR_KIND_GROUP_BY)
+       if (inGroupBy)
        {
                if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
                        force_qualify = true;