]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/ruleutils.c
CREATE INDEX ... INCLUDING (column[, ...])
[postgresql] / src / backend / utils / adt / ruleutils.c
index 88345312336829dfc7a89a4c2f99f9619a11354e..0e1eefd8dac871312239f12a19d22652d254a6b5 100644 (file)
@@ -4,7 +4,7 @@
  *       Functions to convert stored expressions/querytrees back to
  *       source text
  *
- * Portions Copyright (c) 1996-2014, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  */
 #include "postgres.h"
 
+#include <ctype.h>
 #include <unistd.h>
 #include <fcntl.h>
 
+#include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/tablespace.h"
+#include "common/keywords.h"
 #include "executor/spi.h"
 #include "funcapi.h"
+#include "mb/pg_wchar.h"
+#include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/tlist.h"
-#include "parser/keywords.h"
+#include "parser/parse_node.h"
 #include "parser/parse_agg.h"
 #include "parser/parse_func.h"
 #include "parser/parse_oper.h"
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/hsearch.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
+#include "utils/ruleutils.h"
 #include "utils/snapmgr.h"
 #include "utils/syscache.h"
 #include "utils/tqual.h"
@@ -71,6 +79,8 @@
 #define PRETTYINDENT_JOIN              4
 #define PRETTYINDENT_VAR               4
 
+#define PRETTYINDENT_LIMIT             40              /* wrap limit */
+
 /* Pretty flags */
 #define PRETTYFLAG_PAREN               1
 #define PRETTYFLAG_INDENT              2
@@ -99,6 +109,8 @@ typedef struct
        int                     wrapColumn;             /* max line length, or -1 for no limit */
        int                     indentLevel;    /* current indent level for prettyprint */
        bool            varprefix;              /* TRUE to print prefixes on Vars */
+       ParseExprKind special_exprkind;         /* set only for exprkinds needing
+                                                                                * special handling */
 } deparse_context;
 
 /*
@@ -123,8 +135,8 @@ typedef struct
  * varlevelsup > 0).  We store the PlanState node that is the immediate
  * parent of the expression to be deparsed, as well as a list of that
  * PlanState's ancestors.  In addition, we store its outer and inner subplan
- * state nodes, as well as their plan nodes' targetlists, and the indextlist
- * if the current PlanState is an IndexOnlyScanState.  (These fields could
+ * state nodes, as well as their plan nodes' targetlists, and the index tlist
+ * if the current plan node might contain INDEX_VAR Vars.  (These fields could
  * be derived on-the-fly from the current PlanState, but it seems notationally
  * clearer to set them up as separate fields.)
  */
@@ -152,11 +164,11 @@ typedef struct
  *
  * Selecting aliases is unreasonably complicated because of the need to dump
  * rules/views whose underlying tables may have had columns added, deleted, or
- * renamed since the query was parsed. We must nonetheless print the rule/view
+ * renamed since the query was parsed.  We must nonetheless print the rule/view
  * in a form that can be reloaded and will produce the same results as before.
  *
  * For each RTE used in the query, we must assign column aliases that are
- * unique within that RTE.     SQL does not require this of the original query,
+ * unique within that RTE.  SQL does not require this of the original query,
  * but due to factors such as *-expansion we need to be able to uniquely
  * reference every column in a decompiled query.  As long as we qualify all
  * column references, per-RTE uniqueness is sufficient for that.
@@ -171,11 +183,14 @@ typedef struct
  * query to ensure it can be referenced unambiguously.
  *
  * Another problem is that a JOIN USING clause requires the columns to be
- * merged to have the same aliases in both input RTEs. To handle that, we do
- * USING-column alias assignment in a recursive traversal of the query's
- * jointree.  When descending through a JOIN with USING, we preassign the
- * USING column names to the child columns, overriding other rules for column
- * alias assignment.
+ * merged to have the same aliases in both input RTEs, and that no other
+ * columns in those RTEs or their children conflict with the USING names.
+ * To handle that, we do USING-column alias assignment in a recursive
+ * traversal of the query's jointree.  When descending through a JOIN with
+ * USING, we preassign the USING column names to the child columns, overriding
+ * other rules for column alias assignment.  We also mark each RTE with a list
+ * of all USING column names selected for joins containing that RTE, so that
+ * when we assign other columns' aliases later, we can avoid conflicts.
  *
  * Another problem is that if a JOIN's input tables have had columns added or
  * deleted since the query was parsed, we must generate a column alias list
@@ -208,8 +223,8 @@ typedef struct
        /*
         * new_colnames is an array containing column aliases to use for columns
         * that would exist if the query was re-parsed against the current
-        * definitions of its base tables.      This is what to print as the column
-        * alias list for the RTE.      This array does not include dropped columns,
+        * definitions of its base tables.  This is what to print as the column
+        * alias list for the RTE.  This array does not include dropped columns,
         * but it will include columns added since original parsing.  Indexes in
         * it therefore have little to do with current varattno values.  As above,
         * entries are unique unless this is for an unnamed JOIN RTE.  (In such an
@@ -226,6 +241,9 @@ typedef struct
        /* This flag tells whether we should actually print a column alias list */
        bool            printaliases;
 
+       /* This list has all names used as USING names in joins above this RTE */
+       List       *parentUsing;        /* names assigned to parent merged columns */
+
        /*
         * If this struct is for a JOIN RTE, we fill these fields during the
         * set_using_names() pass to describe its relationship to its child RTEs.
@@ -253,6 +271,15 @@ typedef struct
 #define deparse_columns_fetch(rangetable_index, dpns) \
        ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
 
+/*
+ * Entry in set_rtable_names' hash table
+ */
+typedef struct
+{
+       char            name[NAMEDATALEN];              /* Hash key --- must be first */
+       int                     counter;                /* Largest addition used so far for name */
+} NameHashEntry;
+
 
 /* ----------
  * Global data
@@ -295,15 +322,15 @@ static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
 static int print_function_arguments(StringInfo buf, HeapTuple proctup,
                                                 bool print_table_args, bool print_defaults);
 static void print_function_rettype(StringInfo buf, HeapTuple proctup);
+static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
 static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
                                 Bitmapset *rels_used);
-static bool refname_is_unique(char *refname, deparse_namespace *dpns,
-                                 List *parent_namespaces);
 static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
                                          List *parent_namespaces);
 static void set_simple_column_names(deparse_namespace *dpns);
 static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
-static void set_using_names(deparse_namespace *dpns, Node *jtnode);
+static void set_using_names(deparse_namespace *dpns, Node *jtnode,
+                               List *parentUsing);
 static void set_relation_column_names(deparse_namespace *dpns,
                                                  RangeTblEntry *rte,
                                                  deparse_columns *colinfo);
@@ -341,6 +368,9 @@ static void get_select_query_def(Query *query, deparse_context *context,
                                         TupleDesc resultDesc);
 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);
 static void get_utility_query_def(Query *query, deparse_context *context);
 static void get_basic_select_query(Query *query, deparse_context *context,
@@ -350,9 +380,11 @@ static void get_target_list(List *targetList, deparse_context *context,
 static void get_setop_query(Node *setOp, Query *query,
                                deparse_context *context,
                                TupleDesc resultDesc);
-static Node *get_rule_sortgroupclause(SortGroupClause *srt, List *tlist,
+static Node *get_rule_sortgroupclause(Index ref, List *tlist,
                                                 bool force_colno,
                                                 deparse_context *context);
+static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
+                                        bool omit_parens, deparse_context *context);
 static void get_rule_orderby(List *orderList, List *targetList,
                                 bool force_colno, deparse_context *context);
 static void get_rule_windowclause(Query *query, deparse_context *context);
@@ -370,6 +402,8 @@ static void appendContextKeyword(deparse_context *context, const char *str,
 static void removeStringInfoSpaces(StringInfo str);
 static void get_rule_expr(Node *node, deparse_context *context,
                          bool showimplicit);
+static void get_rule_expr_toplevel(Node *node, deparse_context *context,
+                                          bool showimplicit);
 static void get_oper_expr(OpExpr *expr, deparse_context *context);
 static void get_func_expr(FuncExpr *expr, deparse_context *context,
                          bool showimplicit);
@@ -392,6 +426,8 @@ static void get_column_alias_list(deparse_columns *colinfo,
 static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
                                                   deparse_columns *colinfo,
                                                   deparse_context *context);
+static void get_tablesample_def(TableSampleClause *tablesample,
+                                       deparse_context *context);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
                                 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context,
@@ -399,9 +435,11 @@ static Node *processIndirection(Node *node, deparse_context *context,
 static void printSubscripts(ArrayRef *aref, deparse_context *context);
 static char *get_relation_name(Oid relid);
 static char *generate_relation_name(Oid relid, List *namespaces);
+static char *generate_qualified_relation_name(Oid relid);
 static char *generate_function_name(Oid funcid, int nargs,
                                           List *argnames, Oid *argtypes,
-                                          bool has_variadic, bool *use_variadic_p);
+                                          bool has_variadic, bool *use_variadic_p,
+                                          ParseExprKind special_exprkind);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
@@ -693,6 +731,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
        SysScanDesc tgscan;
        int                     findx = 0;
        char       *tgname;
+       Oid                     argtypes[1];    /* dummy */
        Datum           value;
        bool            isnull;
 
@@ -859,6 +898,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
                context.prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : PRETTYFLAG_INDENT;
                context.wrapColumn = WRAP_COLUMN_DEFAULT;
                context.indentLevel = PRETTYINDENT_STD;
+               context.special_exprkind = EXPR_KIND_NONE;
 
                get_rule_expr(qual, &context, false);
 
@@ -867,8 +907,8 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 
        appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
                                         generate_function_name(trigrec->tgfoid, 0,
-                                                                                       NIL, NULL,
-                                                                                       false, NULL));
+                                                                                       NIL, argtypes,
+                                                                                       false, NULL, EXPR_KIND_NONE));
 
        if (trigrec->tgnargs > 0)
        {
@@ -981,6 +1021,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
        Form_pg_index idxrec;
        Form_pg_class idxrelrec;
        Form_pg_am      amrec;
+       IndexAmRoutine *amroutine;
        List       *indexprs;
        ListCell   *indexpr_item;
        List       *context;
@@ -1041,6 +1082,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                         idxrelrec->relam);
        amrec = (Form_pg_am) GETSTRUCT(ht_am);
 
+       /* Fetch the index AM's API struct */
+       amroutine = GetIndexAmRoutine(amrec->amhandler);
+
        /*
         * Get the index expressions, if any.  (NOTE: we do not use the relcache
         * versions of the expressions and predicate, because we want to display
@@ -1067,7 +1111,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
        context = deparse_context_for(get_relation_name(indrelid), indrelid);
 
        /*
-        * Start the index definition.  Note that the index's name should never be
+        * 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);
@@ -1096,6 +1140,21 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                Oid                     keycoltype;
                Oid                     keycolcollation;
 
+               /*
+                * attrsOnly flag is used for building unique-constraint and
+                * exclusion-constraint error messages. Included attrs are
+                * meaningless there, so do not include them into the message.
+                */
+               if (attrsOnly && keyno >= idxrec->indnkeyatts)
+                       break;
+
+               /* Report the INCLUDED attributes, if any. */
+               if ((!attrsOnly) && keyno == idxrec->indnkeyatts)
+               {
+                               appendStringInfoString(&buf, ") INCLUDING (");
+                               sep = "";
+               }
+
                if (!colno)
                        appendStringInfoString(&buf, sep);
                sep = ", ";
@@ -1109,6 +1168,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                        attname = get_relid_attribute_name(indrelid, attnum);
                        if (!colno || colno == keyno + 1)
                                appendStringInfoString(&buf, quote_identifier(attname));
+
                        get_atttypetypmodcoll(indrelid, attnum,
                                                                  &keycoltype, &keycoltypmod,
                                                                  &keycolcollation);
@@ -1148,11 +1208,14 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                                appendStringInfo(&buf, " COLLATE %s",
                                                                 generate_collation_name((indcoll)));
 
+                       if(keyno >= idxrec->indnkeyatts)
+                               continue;
+
                        /* Add the operator class name, if not default */
                        get_opclass_name(indclass->values[keyno], keycoltype, &buf);
 
                        /* Add options if relevant */
-                       if (amrec->amcanorder)
+                       if (amroutine->amcanorder)
                        {
                                /* if it supports sort ordering, report DESC and NULLS opts */
                                if (opt & INDOPTION_DESC)
@@ -1277,9 +1340,11 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
                                                                                                                          prettyFlags)));
 }
 
-/* Internal version that returns a palloc'd C string; no pretty-printing */
+/*
+ * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
+ */
 char *
-pg_get_constraintdef_string(Oid constraintId)
+pg_get_constraintdef_command(Oid constraintId)
 {
        return pg_get_constraintdef_worker(constraintId, true, 0);
 }
@@ -1294,9 +1359,9 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
        HeapTuple       tup;
        Form_pg_constraint conForm;
        StringInfoData buf;
-       SysScanDesc     scandesc;
+       SysScanDesc scandesc;
        ScanKeyData scankey[1];
-       Snapshot    snapshot = RegisterSnapshot(GetTransactionSnapshot());
+       Snapshot        snapshot = RegisterSnapshot(GetTransactionSnapshot());
        Relation        relation = heap_open(ConstraintRelationId, AccessShareLock);
 
        ScanKeyInit(&scankey[0],
@@ -1305,15 +1370,15 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                                ObjectIdGetDatum(constraintId));
 
        scandesc = systable_beginscan(relation,
-                                                                       ConstraintOidIndexId,
-                                                                       true,
-                                                                       snapshot,
-                                                                       1,
-                                                                       scankey);
+                                                                 ConstraintOidIndexId,
+                                                                 true,
+                                                                 snapshot,
+                                                                 1,
+                                                                 scankey);
 
        /*
-        * We later use the tuple with SysCacheGetAttr() as if we
-        * had obtained it via SearchSysCache, which works fine.
+        * We later use the tuple with SysCacheGetAttr() as if we had obtained it
+        * via SearchSysCache, which works fine.
         */
        tup = systable_getnext(scandesc);
 
@@ -1326,10 +1391,16 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
        initStringInfo(&buf);
 
-       if (fullCommand && OidIsValid(conForm->conrelid))
+       if (fullCommand)
        {
-               appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ",
-                                                generate_relation_name(conForm->conrelid, NIL),
+               /*
+                * Currently, callers want ALTER TABLE (without ONLY) for CHECK
+                * constraints, and other types of constraints don't inherit anyway so
+                * it doesn't matter whether we say ONLY or not.  Someday we might
+                * need to let callers specify whether to put ONLY in the command.
+                */
+               appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
+                                                generate_qualified_relation_name(conForm->conrelid),
                                                 quote_identifier(NameStr(conForm->conname)));
        }
 
@@ -1468,6 +1539,19 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
                                appendStringInfoChar(&buf, ')');
 
+                               /* Fetch and build including column list */
+                               isnull = true;
+                               val = SysCacheGetAttr(CONSTROID, tup,
+                                                                         Anum_pg_constraint_conincluding, &isnull);
+                               if (!isnull)
+                               {
+                                       appendStringInfoString(&buf, " INCLUDING (");
+
+                                       decompile_column_index_array(val, conForm->conrelid, &buf);
+
+                                       appendStringInfoChar(&buf, ')');
+                               }
+
                                indexId = get_constraint_index(constraintId);
 
                                /* XXX why do we only print these bits if fullCommand? */
@@ -1796,7 +1880,7 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
        SysScanDesc scan;
        HeapTuple       tup;
 
-       /* Look up table name.  Can't lock it - we might not have privileges. */
+       /* Look up table name.  Can't lock it - we might not have privileges. */
        tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
        tableOid = RangeVarGetRelid(tablerv, NoLock, false);
 
@@ -1853,28 +1937,9 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
 
        if (OidIsValid(sequenceId))
        {
-               HeapTuple       classtup;
-               Form_pg_class classtuple;
-               char       *nspname;
                char       *result;
 
-               /* Get the sequence's pg_class entry */
-               classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(sequenceId));
-               if (!HeapTupleIsValid(classtup))
-                       elog(ERROR, "cache lookup failed for relation %u", sequenceId);
-               classtuple = (Form_pg_class) GETSTRUCT(classtup);
-
-               /* Get the namespace */
-               nspname = get_namespace_name(classtuple->relnamespace);
-               if (!nspname)
-                       elog(ERROR, "cache lookup failed for namespace %u",
-                                classtuple->relnamespace);
-
-               /* And construct the result string */
-               result = quote_qualified_identifier(nspname,
-                                                                                       NameStr(classtuple->relname));
-
-               ReleaseSysCache(classtup);
+               result = generate_qualified_relation_name(sequenceId);
 
                PG_RETURN_TEXT_P(string_to_text(result));
        }
@@ -1900,9 +1965,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        StringInfoData buf;
        StringInfoData dq;
        HeapTuple       proctup;
-       HeapTuple       langtup;
        Form_pg_proc proc;
-       Form_pg_language lang;
        Datum           tmp;
        bool            isnull;
        const char *prosrc;
@@ -1925,12 +1988,6 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
                                (errcode(ERRCODE_WRONG_OBJECT_TYPE),
                                 errmsg("\"%s\" is an aggregate function", name)));
 
-       /* Need its pg_language tuple for the language name */
-       langtup = SearchSysCache1(LANGOID, ObjectIdGetDatum(proc->prolang));
-       if (!HeapTupleIsValid(langtup))
-               elog(ERROR, "cache lookup failed for language %u", proc->prolang);
-       lang = (Form_pg_language) GETSTRUCT(langtup);
-
        /*
         * We always qualify the function name, to ensure the right function gets
         * replaced.
@@ -1941,8 +1998,11 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        (void) print_function_arguments(&buf, proctup, false, true);
        appendStringInfoString(&buf, ")\n RETURNS ");
        print_function_rettype(&buf, proctup);
+
+       print_function_trftypes(&buf, proctup);
+
        appendStringInfo(&buf, "\n LANGUAGE %s\n",
-                                        quote_identifier(NameStr(lang->lanname)));
+                                 quote_identifier(get_language_name(proc->prolang, false)));
 
        /* Emit some miscellaneous options on one line */
        oldlen = buf.len;
@@ -1964,6 +2024,8 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
                appendStringInfoString(&buf, " STRICT");
        if (proc->prosecdef)
                appendStringInfoString(&buf, " SECURITY DEFINER");
+       if (proc->proleakproof)
+               appendStringInfoString(&buf, " LEAKPROOF");
 
        /* This code for the default cost and rows should match functioncmds.c */
        if (proc->prolang == INTERNALlanguageId ||
@@ -2062,7 +2124,6 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
 
        appendStringInfoChar(&buf, '\n');
 
-       ReleaseSysCache(langtup);
        ReleaseSysCache(proctup);
 
        PG_RETURN_TEXT_P(string_to_text(buf.data));
@@ -2164,7 +2225,7 @@ print_function_rettype(StringInfo buf, HeapTuple proctup)
                appendStringInfoString(&rbuf, "TABLE(");
                ntabargs = print_function_arguments(&rbuf, proctup, true, false);
                if (ntabargs > 0)
-                       appendStringInfoString(&rbuf, ")");
+                       appendStringInfoChar(&rbuf, ')');
                else
                        resetStringInfo(&rbuf);
        }
@@ -2338,6 +2399,30 @@ is_input_argument(int nth, const char *argmodes)
                        || argmodes[nth] == PROARGMODE_VARIADIC);
 }
 
+/*
+ * Append used transformated types to specified buffer
+ */
+static void
+print_function_trftypes(StringInfo buf, HeapTuple proctup)
+{
+       Oid                *trftypes;
+       int                     ntypes;
+
+       ntypes = get_func_trftypes(proctup, &trftypes);
+       if (ntypes > 0)
+       {
+               int                     i;
+
+               appendStringInfoString(buf, "\n TRANSFORM ");
+               for (i = 0; i < ntypes; i++)
+               {
+                       if (i != 0)
+                               appendStringInfoString(buf, ", ");
+                       appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
+               }
+       }
+}
+
 /*
  * Get textual representation of a function argument's default value.  The
  * second argument of this function is the argument number among all arguments
@@ -2396,8 +2481,10 @@ pg_get_function_arg_default(PG_FUNCTION_ARGS)
 
        proc = (Form_pg_proc) GETSTRUCT(proctup);
 
-       /* Calculate index into proargdefaults: proargdefaults corresponds to the
-        * last N input arguments, where N = pronargdefaults. */
+       /*
+        * Calculate index into proargdefaults: proargdefaults corresponds to the
+        * last N input arguments, where N = pronargdefaults.
+        */
        nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
 
        if (nth_default < 0 || nth_default >= list_length(argdefaults))
@@ -2434,7 +2521,7 @@ deparse_expression(Node *expr, List *dpcontext,
  * tree (ie, not the raw output of gram.y).
  *
  * dpcontext is a list of deparse_namespace nodes representing the context
- * for interpreting Vars in the node tree.     It can be NIL if no Vars are
+ * for interpreting Vars in the node tree.  It can be NIL if no Vars are
  * expected.
  *
  * forceprefix is TRUE to force all Vars to be prefixed with their table names.
@@ -2463,6 +2550,7 @@ deparse_expression_pretty(Node *expr, List *dpcontext,
        context.prettyFlags = prettyFlags;
        context.wrapColumn = WRAP_COLUMN_DEFAULT;
        context.indentLevel = startIndent;
+       context.special_exprkind = EXPR_KIND_NONE;
 
        get_rule_expr(expr, &context, showimplicit);
 
@@ -2474,7 +2562,7 @@ deparse_expression_pretty(Node *expr, List *dpcontext,
  *
  * Given the reference name (alias) and OID of a relation, build deparsing
  * context for an expression referencing only that relation (as varno 1,
- * varlevelsup 0).     This is sufficient for many uses of deparse_expression.
+ * varlevelsup 0).  This is sufficient for many uses of deparse_expression.
  * ----------
  */
 List *
@@ -2507,33 +2595,20 @@ deparse_context_for(const char *aliasname, Oid relid)
 }
 
 /*
- * deparse_context_for_planstate       - Build deparse context for a plan
- *
- * When deparsing an expression in a Plan tree, we might have to resolve
- * OUTER_VAR, INNER_VAR, or INDEX_VAR references.  To do this, the caller must
- * provide the parent PlanState node.  Then OUTER_VAR and INNER_VAR references
- * can be resolved by drilling down into the left and right child plans.
- * Similarly, INDEX_VAR references can be resolved by reference to the
- * indextlist given in the parent IndexOnlyScan node.  (Note that we don't
- * currently support deparsing of indexquals in regular IndexScan or
- * BitmapIndexScan nodes; for those, we can only deparse the indexqualorig
- * fields, which won't contain INDEX_VAR Vars.)
- *
- * Note: planstate really ought to be declared as "PlanState *", but we use
- * "Node *" to avoid having to include execnodes.h in builtins.h.
+ * deparse_context_for_plan_rtable - Build deparse context for a plan's rtable
  *
- * The ancestors list is a list of the PlanState's parent PlanStates, the
- * most-closely-nested first.  This is needed to resolve PARAM_EXEC Params.
- * Note we assume that all the PlanStates share the same rtable.
+ * When deparsing an expression in a Plan tree, we use the plan's rangetable
+ * to resolve names of simple Vars.  The initialization of column names for
+ * this is rather expensive if the rangetable is large, and it'll be the same
+ * for every expression in the Plan tree; so we do it just once and re-use
+ * the result of this function for each expression.  (Note that the result
+ * is not usable until set_deparse_context_planstate() is applied to it.)
  *
- * The plan's rangetable list must also be passed, along with the per-RTE
- * alias names assigned by a previous call to select_rtable_names_for_explain.
- * (We use the rangetable to resolve simple Vars, but the plan inputs are
- * necessary for Vars with special varnos.)
+ * In addition to the plan's rangetable list, pass the per-RTE alias names
+ * assigned by a previous call to select_rtable_names_for_explain.
  */
 List *
-deparse_context_for_planstate(Node *planstate, List *ancestors,
-                                                         List *rtable, List *rtable_names)
+deparse_context_for_plan_rtable(List *rtable, List *rtable_names)
 {
        deparse_namespace *dpns;
 
@@ -2545,18 +2620,59 @@ deparse_context_for_planstate(Node *planstate, List *ancestors,
        dpns->ctes = NIL;
 
        /*
-        * Set up column name aliases.  We will get rather bogus results for join
+        * Set up column name aliases.  We will get rather bogus results for join
         * RTEs, but that doesn't matter because plan trees don't contain any join
         * alias Vars.
         */
        set_simple_column_names(dpns);
 
+       /* Return a one-deep namespace stack */
+       return list_make1(dpns);
+}
+
+/*
+ * set_deparse_context_planstate       - Specify Plan node containing expression
+ *
+ * When deparsing an expression in a Plan tree, we might have to resolve
+ * OUTER_VAR, INNER_VAR, or INDEX_VAR references.  To do this, the caller must
+ * provide the parent PlanState node.  Then OUTER_VAR and INNER_VAR references
+ * can be resolved by drilling down into the left and right child plans.
+ * Similarly, INDEX_VAR references can be resolved by reference to the
+ * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
+ * ForeignScan and CustomScan nodes.  (Note that we don't currently support
+ * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
+ * for those, we can only deparse the indexqualorig fields, which won't
+ * contain INDEX_VAR Vars.)
+ *
+ * Note: planstate really ought to be declared as "PlanState *", but we use
+ * "Node *" to avoid having to include execnodes.h in ruleutils.h.
+ *
+ * The ancestors list is a list of the PlanState's parent PlanStates, the
+ * most-closely-nested first.  This is needed to resolve PARAM_EXEC Params.
+ * Note we assume that all the PlanStates share the same rtable.
+ *
+ * Once this function has been called, deparse_expression() can be called on
+ * subsidiary expression(s) of the specified PlanState node.  To deparse
+ * expressions of a different Plan node in the same Plan tree, re-call this
+ * function to identify the new parent Plan node.
+ *
+ * The result is the same List passed in; this is a notational convenience.
+ */
+List *
+set_deparse_context_planstate(List *dpcontext,
+                                                         Node *planstate, List *ancestors)
+{
+       deparse_namespace *dpns;
+
+       /* Should always have one-entry namespace list for Plan deparsing */
+       Assert(list_length(dpcontext) == 1);
+       dpns = (deparse_namespace *) linitial(dpcontext);
+
        /* Set our attention on the specific plan node passed in */
        set_deparse_planstate(dpns, (PlanState *) planstate);
        dpns->ancestors = ancestors;
 
-       /* Return a one-deep namespace stack */
-       return list_make1(dpns);
+       return dpcontext;
 }
 
 /*
@@ -2597,15 +2713,61 @@ static void
 set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
                                 Bitmapset *rels_used)
 {
+       HASHCTL         hash_ctl;
+       HTAB       *names_hash;
+       NameHashEntry *hentry;
+       bool            found;
+       int                     rtindex;
        ListCell   *lc;
-       int                     rtindex = 1;
 
        dpns->rtable_names = NIL;
+       /* nothing more to do if empty rtable */
+       if (dpns->rtable == NIL)
+               return;
+
+       /*
+        * We use a hash table to hold known names, so that this process is O(N)
+        * not O(N^2) for N names.
+        */
+       MemSet(&hash_ctl, 0, sizeof(hash_ctl));
+       hash_ctl.keysize = NAMEDATALEN;
+       hash_ctl.entrysize = sizeof(NameHashEntry);
+       hash_ctl.hcxt = CurrentMemoryContext;
+       names_hash = hash_create("set_rtable_names names",
+                                                        list_length(dpns->rtable),
+                                                        &hash_ctl,
+                                                        HASH_ELEM | HASH_CONTEXT);
+       /* Preload the hash table with names appearing in parent_namespaces */
+       foreach(lc, parent_namespaces)
+       {
+               deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
+               ListCell   *lc2;
+
+               foreach(lc2, olddpns->rtable_names)
+               {
+                       char       *oldname = (char *) lfirst(lc2);
+
+                       if (oldname == NULL)
+                               continue;
+                       hentry = (NameHashEntry *) hash_search(names_hash,
+                                                                                                  oldname,
+                                                                                                  HASH_ENTER,
+                                                                                                  &found);
+                       /* we do not complain about duplicate names in parent namespaces */
+                       hentry->counter = 0;
+               }
+       }
+
+       /* Now we can scan the rtable */
+       rtindex = 1;
        foreach(lc, dpns->rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
                char       *refname;
 
+               /* Just in case this takes an unreasonable amount of time ... */
+               CHECK_FOR_INTERRUPTS();
+
                if (rels_used && !bms_is_member(rtindex, rels_used))
                {
                        /* Ignore unreferenced RTE */
@@ -2633,56 +2795,62 @@ set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
                }
 
                /*
-                * If the selected name isn't unique, append digits to make it so
+                * If the selected name isn't unique, append digits to make it so, and
+                * make a new hash entry for it once we've got a unique name.  For a
+                * very long input name, we might have to truncate to stay within
+                * NAMEDATALEN.
                 */
-               if (refname &&
-                       !refname_is_unique(refname, dpns, parent_namespaces))
+               if (refname)
                {
-                       char       *modname = (char *) palloc(strlen(refname) + 32);
-                       int                     i = 0;
+                       hentry = (NameHashEntry *) hash_search(names_hash,
+                                                                                                  refname,
+                                                                                                  HASH_ENTER,
+                                                                                                  &found);
+                       if (found)
+                       {
+                               /* Name already in use, must choose a new one */
+                               int                     refnamelen = strlen(refname);
+                               char       *modname = (char *) palloc(refnamelen + 16);
+                               NameHashEntry *hentry2;
 
-                       do
+                               do
+                               {
+                                       hentry->counter++;
+                                       for (;;)
+                                       {
+                                               /*
+                                                * We avoid using %.*s here because it can misbehave
+                                                * if the data is not valid in what libc thinks is the
+                                                * prevailing encoding.
+                                                */
+                                               memcpy(modname, refname, refnamelen);
+                                               sprintf(modname + refnamelen, "_%d", hentry->counter);
+                                               if (strlen(modname) < NAMEDATALEN)
+                                                       break;
+                                               /* drop chars from refname to keep all the digits */
+                                               refnamelen = pg_mbcliplen(refname, refnamelen,
+                                                                                                 refnamelen - 1);
+                                       }
+                                       hentry2 = (NameHashEntry *) hash_search(names_hash,
+                                                                                                                       modname,
+                                                                                                                       HASH_ENTER,
+                                                                                                                       &found);
+                               } while (found);
+                               hentry2->counter = 0;   /* init new hash entry */
+                               refname = modname;
+                       }
+                       else
                        {
-                               sprintf(modname, "%s_%d", refname, ++i);
-                       } while (!refname_is_unique(modname, dpns, parent_namespaces));
-                       refname = modname;
+                               /* Name not previously used, need only initialize hentry */
+                               hentry->counter = 0;
+                       }
                }
 
                dpns->rtable_names = lappend(dpns->rtable_names, refname);
                rtindex++;
        }
-}
-
-/*
- * refname_is_unique: is refname distinct from all already-chosen RTE names?
- */
-static bool
-refname_is_unique(char *refname, deparse_namespace *dpns,
-                                 List *parent_namespaces)
-{
-       ListCell   *lc;
-
-       foreach(lc, dpns->rtable_names)
-       {
-               char       *oldname = (char *) lfirst(lc);
-
-               if (oldname && strcmp(oldname, refname) == 0)
-                       return false;
-       }
-       foreach(lc, parent_namespaces)
-       {
-               deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
-               ListCell   *lc2;
 
-               foreach(lc2, olddpns->rtable_names)
-               {
-                       char       *oldname = (char *) lfirst(lc2);
-
-                       if (oldname && strcmp(oldname, refname) == 0)
-                               return false;
-               }
-       }
-       return true;
+       hash_destroy(names_hash);
 }
 
 /*
@@ -2723,7 +2891,7 @@ set_deparse_for_query(deparse_namespace *dpns, Query *query,
                 * Select names for columns merged by USING, via a recursive pass over
                 * the query jointree.
                 */
-               set_using_names(dpns, (Node *) query->jointree);
+               set_using_names(dpns, (Node *) query->jointree, NIL);
        }
 
        /*
@@ -2857,9 +3025,12 @@ has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
  *
  * Column alias info is saved in the dpns->rtable_columns list, which is
  * assumed to be filled with pre-zeroed deparse_columns structs.
+ *
+ * parentUsing is a list of all USING aliases assigned in parent joins of
+ * the current jointree node.  (The passed-in list must not be modified.)
  */
 static void
-set_using_names(deparse_namespace *dpns, Node *jtnode)
+set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
 {
        if (IsA(jtnode, RangeTblRef))
        {
@@ -2871,7 +3042,7 @@ set_using_names(deparse_namespace *dpns, Node *jtnode)
                ListCell   *lc;
 
                foreach(lc, f->fromlist)
-                       set_using_names(dpns, (Node *) lfirst(lc));
+                       set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
        }
        else if (IsA(jtnode, JoinExpr))
        {
@@ -2951,6 +3122,9 @@ set_using_names(deparse_namespace *dpns, Node *jtnode)
                 */
                if (j->usingClause)
                {
+                       /* Copy the input parentUsing list so we don't modify it */
+                       parentUsing = list_copy(parentUsing);
+
                        /* USING names must correspond to the first join output columns */
                        expand_colnames_array_to(colinfo, list_length(j->usingClause));
                        i = 0;
@@ -2980,6 +3154,7 @@ set_using_names(deparse_namespace *dpns, Node *jtnode)
 
                                /* Remember selected names for use later */
                                colinfo->usingNames = lappend(colinfo->usingNames, colname);
+                               parentUsing = lappend(parentUsing, colname);
 
                                /* Push down to left column, unless it's a system column */
                                if (leftattnos[i] > 0)
@@ -2999,9 +3174,13 @@ set_using_names(deparse_namespace *dpns, Node *jtnode)
                        }
                }
 
+               /* Mark child deparse_columns structs with correct parentUsing info */
+               leftcolinfo->parentUsing = parentUsing;
+               rightcolinfo->parentUsing = parentUsing;
+
                /* Now recursively assign USING column names in children */
-               set_using_names(dpns, j->larg);
-               set_using_names(dpns, j->rarg);
+               set_using_names(dpns, j->larg, parentUsing);
+               set_using_names(dpns, j->rarg, parentUsing);
        }
        else
                elog(ERROR, "unrecognized node type: %d",
@@ -3063,7 +3242,16 @@ set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
                i = 0;
                foreach(lc, rte->eref->colnames)
                {
-                       real_colnames[i] = strVal(lfirst(lc));
+                       /*
+                        * If the column name shown in eref is an empty string, then it's
+                        * a column that was dropped at the time of parsing the query, so
+                        * treat it as dropped.
+                        */
+                       char       *cname = strVal(lfirst(lc));
+
+                       if (cname[0] == '\0')
+                               cname = NULL;
+                       real_colnames[i] = cname;
                        i++;
                }
        }
@@ -3092,7 +3280,7 @@ set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
        /*
         * Scan the columns, select a unique alias for each one, and store it in
         * colinfo->colnames and colinfo->new_colnames.  The former array has NULL
-        * entries for dropped columns, the latter omits them.  Also mark
+        * entries for dropped columns, the latter omits them.  Also mark
         * new_colnames entries as to whether they are new since parse time; this
         * is the case for entries beyond the length of rte->eref->colnames.
         */
@@ -3147,7 +3335,7 @@ set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
 
        /*
         * For a relation RTE, we need only print the alias column names if any
-        * are different from the underlying "real" names.      For a function RTE,
+        * are different from the underlying "real" names.  For a function RTE,
         * always emit a complete column alias list; this is to protect against
         * possible instability of the default column names (eg, from altering
         * parameter names).  For other RTE types, print if we changed anything OR
@@ -3468,6 +3656,15 @@ colname_is_unique(char *colname, deparse_namespace *dpns,
                        return false;
        }
 
+       /* Also check against names already assigned for parent-join USING cols */
+       foreach(lc, colinfo->parentUsing)
+       {
+               char       *oldname = (char *) lfirst(lc);
+
+               if (strcmp(oldname, colname) == 0)
+                       return false;
+       }
+
        return true;
 }
 
@@ -3481,16 +3678,34 @@ make_colname_unique(char *colname, deparse_namespace *dpns,
                                        deparse_columns *colinfo)
 {
        /*
-        * If the selected name isn't unique, append digits to make it so
+        * If the selected name isn't unique, append digits to make it so.  For a
+        * very long input name, we might have to truncate to stay within
+        * NAMEDATALEN.
         */
        if (!colname_is_unique(colname, dpns, colinfo))
        {
-               char       *modname = (char *) palloc(strlen(colname) + 32);
+               int                     colnamelen = strlen(colname);
+               char       *modname = (char *) palloc(colnamelen + 16);
                int                     i = 0;
 
                do
                {
-                       sprintf(modname, "%s_%d", colname, ++i);
+                       i++;
+                       for (;;)
+                       {
+                               /*
+                                * We avoid using %.*s here because it can misbehave if the
+                                * data is not valid in what libc thinks is the prevailing
+                                * encoding.
+                                */
+                               memcpy(modname, colname, colnamelen);
+                               sprintf(modname + colnamelen, "_%d", i);
+                               if (strlen(modname) < NAMEDATALEN)
+                                       break;
+                               /* drop chars from colname to keep all the digits */
+                               colnamelen = pg_mbcliplen(colname, colnamelen,
+                                                                                 colnamelen - 1);
+                       }
                } while (!colname_is_unique(modname, dpns, colinfo));
                colname = modname;
        }
@@ -3601,7 +3816,7 @@ identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
 
        /*
         * If there's a USING clause, deconstruct the join quals to identify the
-        * merged columns.      This is a tad painful but if we cannot rely on the
+        * merged columns.  This is a tad painful but if we cannot rely on the
         * column names, there is no other representation of which columns were
         * joined by USING.  (Unless the join type is FULL, we can't tell from the
         * joinaliasvars list which columns are merged.)  Note: we assume that the
@@ -3735,7 +3950,7 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
         * We special-case Append and MergeAppend to pretend that the first child
         * plan is the OUTER referent; we have to interpret OUTER Vars in their
         * tlists according to one of the children, and the first one is the most
-        * natural choice.      Likewise special-case ModifyTable to pretend that the
+        * natural choice.  Likewise special-case ModifyTable to pretend that the
         * first child plan is the OUTER referent; this is to support RETURNING
         * lists containing references to non-target relations.
         */
@@ -3757,22 +3972,34 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
         * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
         * use OUTER because that could someday conflict with the normal meaning.)
         * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
+        * For ON CONFLICT .. UPDATE we just need the inner tlist to point to the
+        * excluded expression's tlist. (Similar to the SubqueryScan we don't want
+        * to reuse OUTER, it's used for RETURNING in some modify table cases,
+        * although not INSERT .. CONFLICT).
         */
        if (IsA(ps, SubqueryScanState))
                dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan;
        else if (IsA(ps, CteScanState))
                dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate;
+       else if (IsA(ps, ModifyTableState))
+               dpns->inner_planstate = ps;
        else
                dpns->inner_planstate = innerPlanState(ps);
 
-       if (dpns->inner_planstate)
+       if (IsA(ps, ModifyTableState))
+               dpns->inner_tlist = ((ModifyTableState *) ps)->mt_excludedtlist;
+       else if (dpns->inner_planstate)
                dpns->inner_tlist = dpns->inner_planstate->plan->targetlist;
        else
                dpns->inner_tlist = NIL;
 
-       /* index_tlist is set only if it's an IndexOnlyScan */
+       /* Set up referent for INDEX_VAR Vars, if needed */
        if (IsA(ps->plan, IndexOnlyScan))
                dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
+       else if (IsA(ps->plan, ForeignScan))
+               dpns->index_tlist = ((ForeignScan *) ps->plan)->fdw_scan_tlist;
+       else if (IsA(ps->plan, CustomScan))
+               dpns->index_tlist = ((CustomScan *) ps->plan)->custom_scan_tlist;
        else
                dpns->index_tlist = NIL;
 }
@@ -4004,6 +4231,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                context.prettyFlags = prettyFlags;
                context.wrapColumn = WRAP_COLUMN_DEFAULT;
                context.indentLevel = PRETTYINDENT_STD;
+               context.special_exprkind = EXPR_KIND_NONE;
 
                set_deparse_for_query(&dpns, query, NIL);
 
@@ -4131,10 +4359,14 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
        deparse_context context;
        deparse_namespace dpns;
 
+       /* Guard against excessively long or deeply-nested queries */
+       CHECK_FOR_INTERRUPTS();
+       check_stack_depth();
+
        /*
         * Before we begin to examine the query, acquire locks on referenced
-        * relations, and fix up deleted columns in JOIN RTEs.  This ensures
-        * consistent results.  Note we assume it's OK to scribble on the passed
+        * relations, and fix up deleted columns in JOIN RTEs.  This ensures
+        * consistent results.  Note we assume it's OK to scribble on the passed
         * querytree!
         *
         * We are only deparsing the query (we are not about to execute it), so we
@@ -4151,6 +4383,7 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
        context.prettyFlags = prettyFlags;
        context.wrapColumn = wrapColumn;
        context.indentLevel = startIndent;
+       context.special_exprkind = EXPR_KIND_NONE;
 
        set_deparse_for_query(&dpns, query, parentnamespace);
 
@@ -4223,10 +4456,10 @@ get_values_def(List *values_lists, deparse_context *context)
 
                        /*
                         * Strip any top-level nodes representing indirection assignments,
-                        * then print the result.
+                        * then print the result.  Whole-row Vars need special treatment.
                         */
-                       get_rule_expr(processIndirection(col, context, false),
-                                                 context, false);
+                       get_rule_expr_toplevel(processIndirection(col, context, false),
+                                                                  context, false);
                }
                appendStringInfoChar(buf, ')');
        }
@@ -4380,6 +4613,11 @@ get_select_query_def(Query *query, deparse_context *context,
 
                        switch (rc->strength)
                        {
+                               case LCS_NONE:
+                                       /* we intentionally throw an error for LCS_NONE */
+                                       elog(ERROR, "unrecognized LockClauseStrength %d",
+                                                (int) rc->strength);
+                                       break;
                                case LCS_FORKEYSHARE:
                                        appendContextKeyword(context, " FOR KEY SHARE",
                                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
@@ -4401,8 +4639,10 @@ get_select_query_def(Query *query, deparse_context *context,
                        appendStringInfo(buf, " OF %s",
                                                         quote_identifier(get_rtable_name(rc->rti,
                                                                                                                          context)));
-                       if (rc->noWait)
+                       if (rc->waitPolicy == LockWaitError)
                                appendStringInfoString(buf, " NOWAIT");
+                       else if (rc->waitPolicy == LockWaitSkip)
+                               appendStringInfoString(buf, " SKIP LOCKED");
                }
        }
 
@@ -4423,10 +4663,7 @@ get_simple_values_rte(Query *query)
        /*
         * We want to return TRUE even if the Query also contains OLD or NEW rule
         * RTEs.  So the idea is to scan the rtable and see if there is only one
-        * inFromCl RTE that is a VALUES RTE.  We don't look at the targetlist at
-        * all.  This is okay because parser/analyze.c will never generate a
-        * "bare" VALUES RTE --- they only appear inside auto-generated
-        * sub-queries with very restricted structure.
+        * inFromCl RTE that is a VALUES RTE.
         */
        foreach(lc, query->rtable)
        {
@@ -4443,6 +4680,33 @@ get_simple_values_rte(Query *query)
                else
                        return NULL;            /* something else -> not simple VALUES */
        }
+
+       /*
+        * We don't need to check the targetlist in any great detail, because
+        * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
+        * appear inside auto-generated sub-queries with very restricted
+        * structure.  However, DefineView might have modified the tlist by
+        * injecting new column aliases; so compare tlist resnames against the
+        * RTE's names to detect that.
+        */
+       if (result)
+       {
+               ListCell   *lcn;
+
+               if (list_length(query->targetList) != list_length(result->eref->colnames))
+                       return NULL;            /* this probably cannot happen */
+               forboth(lc, query->targetList, lcn, result->eref->colnames)
+               {
+                       TargetEntry *tle = (TargetEntry *) lfirst(lc);
+                       char       *cname = strVal(lfirst(lcn));
+
+                       if (tle->resjunk)
+                               return NULL;    /* this probably cannot happen */
+                       if (tle->resname == NULL || strcmp(tle->resname, cname) != 0)
+                               return NULL;    /* column name has been changed */
+               }
+       }
+
        return result;
 }
 
@@ -4490,7 +4754,7 @@ get_basic_select_query(Query *query, deparse_context *context,
                                SortGroupClause *srt = (SortGroupClause *) lfirst(l);
 
                                appendStringInfoString(buf, sep);
-                               get_rule_sortgroupclause(srt, query->targetList,
+                               get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
                                                                                 false, context);
                                sep = ", ";
                        }
@@ -4515,20 +4779,43 @@ get_basic_select_query(Query *query, deparse_context *context,
        }
 
        /* Add the GROUP BY clause if given */
-       if (query->groupClause != NULL)
+       if (query->groupClause != NULL || query->groupingSets != NULL)
        {
+               ParseExprKind save_exprkind;
+
                appendContextKeyword(context, " GROUP BY ",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
-               sep = "";
-               foreach(l, query->groupClause)
+
+               save_exprkind = context->special_exprkind;
+               context->special_exprkind = EXPR_KIND_GROUP_BY;
+
+               if (query->groupingSets == NIL)
                {
-                       SortGroupClause *grp = (SortGroupClause *) lfirst(l);
+                       sep = "";
+                       foreach(l, query->groupClause)
+                       {
+                               SortGroupClause *grp = (SortGroupClause *) lfirst(l);
 
-                       appendStringInfoString(buf, sep);
-                       get_rule_sortgroupclause(grp, query->targetList,
-                                                                        false, context);
-                       sep = ", ";
+                               appendStringInfoString(buf, sep);
+                               get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
+                                                                                false, context);
+                               sep = ", ";
+                       }
+               }
+               else
+               {
+                       sep = "";
+                       foreach(l, query->groupingSets)
+                       {
+                               GroupingSet *grp = lfirst(l);
+
+                               appendStringInfoString(buf, sep);
+                               get_rule_groupingset(grp, query->targetList, true, context);
+                               sep = ", ";
+                       }
                }
+
+               context->special_exprkind = save_exprkind;
        }
 
        /* Add the HAVING clause if given */
@@ -4593,9 +4880,10 @@ get_target_list(List *targetList, deparse_context *context,
                 * the top level of a SELECT list it's not right (the parser will
                 * expand that notation into multiple columns, yielding behavior
                 * different from a whole-row Var).  We need to call get_variable
-                * directly so that we can tell it to do the right thing.
+                * directly so that we can tell it to do the right thing, and so that
+                * we can get the attribute name which is the default AS label.
                 */
-               if (tle->expr && IsA(tle->expr, Var))
+               if (tle->expr && (IsA(tle->expr, Var)))
                {
                        attname = get_variable((Var *) tle->expr, 0, true, context);
                }
@@ -4607,7 +4895,7 @@ get_target_list(List *targetList, deparse_context *context,
                }
 
                /*
-                * Figure out what the result column should be called.  In the context
+                * Figure out what the result column should be called.  In the context
                 * of a view, use the view's tuple descriptor (so as to pick up the
                 * effects of any column RENAME that's been done on the view).
                 * Otherwise, just use what we can find in the TLE.
@@ -4687,6 +4975,10 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
        StringInfo      buf = context->buf;
        bool            need_paren;
 
+       /* Guard against excessively long or deeply-nested queries */
+       CHECK_FOR_INTERRUPTS();
+       check_stack_depth();
+
        if (IsA(setOp, RangeTblRef))
        {
                RangeTblRef *rtr = (RangeTblRef *) setOp;
@@ -4712,42 +5004,59 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
        else if (IsA(setOp, SetOperationStmt))
        {
                SetOperationStmt *op = (SetOperationStmt *) setOp;
-
-               if (PRETTY_INDENT(context))
-               {
-                       context->indentLevel += PRETTYINDENT_STD;
-                       appendStringInfoSpaces(buf, PRETTYINDENT_STD);
-               }
+               int                     subindent;
 
                /*
-                * We force parens whenever nesting two SetOperationStmts. There are
-                * some cases in which parens are needed around a leaf query too, but
-                * those are more easily handled at the next level down (see code
-                * above).
+                * We force parens when nesting two SetOperationStmts, except when the
+                * lefthand input is another setop of the same kind.  Syntactically,
+                * we could omit parens in rather more cases, but it seems best to use
+                * parens to flag cases where the setop operator changes.  If we use
+                * parens, we also increase the indentation level for the child query.
+                *
+                * There are some cases in which parens are needed around a leaf query
+                * too, but those are more easily handled at the next level down (see
+                * code above).
                 */
-               need_paren = !IsA(op->larg, RangeTblRef);
+               if (IsA(op->larg, SetOperationStmt))
+               {
+                       SetOperationStmt *lop = (SetOperationStmt *) op->larg;
+
+                       if (op->op == lop->op && op->all == lop->all)
+                               need_paren = false;
+                       else
+                               need_paren = true;
+               }
+               else
+                       need_paren = false;
 
                if (need_paren)
+               {
                        appendStringInfoChar(buf, '(');
+                       subindent = PRETTYINDENT_STD;
+                       appendContextKeyword(context, "", subindent, 0, 0);
+               }
+               else
+                       subindent = 0;
+
                get_setop_query(op->larg, query, context, resultDesc);
-               if (need_paren)
-                       appendStringInfoChar(buf, ')');
 
-               if (!PRETTY_INDENT(context))
+               if (need_paren)
+                       appendContextKeyword(context, ") ", -subindent, 0, 0);
+               else if (PRETTY_INDENT(context))
+                       appendContextKeyword(context, "", -subindent, 0, 0);
+               else
                        appendStringInfoChar(buf, ' ');
+
                switch (op->op)
                {
                        case SETOP_UNION:
-                               appendContextKeyword(context, "UNION ",
-                                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+                               appendStringInfoString(buf, "UNION ");
                                break;
                        case SETOP_INTERSECT:
-                               appendContextKeyword(context, "INTERSECT ",
-                                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+                               appendStringInfoString(buf, "INTERSECT ");
                                break;
                        case SETOP_EXCEPT:
-                               appendContextKeyword(context, "EXCEPT ",
-                                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+                               appendStringInfoString(buf, "EXCEPT ");
                                break;
                        default:
                                elog(ERROR, "unrecognized set op: %d",
@@ -4756,19 +5065,29 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
                if (op->all)
                        appendStringInfoString(buf, "ALL ");
 
-               if (PRETTY_INDENT(context))
-                       appendContextKeyword(context, "", 0, 0, 0);
-
-               need_paren = !IsA(op->rarg, RangeTblRef);
+               /* Always parenthesize if RHS is another setop */
+               need_paren = IsA(op->rarg, SetOperationStmt);
 
+               /*
+                * The indentation code here is deliberately a bit different from that
+                * for the lefthand input, because we want the line breaks in
+                * different places.
+                */
                if (need_paren)
+               {
                        appendStringInfoChar(buf, '(');
+                       subindent = PRETTYINDENT_STD;
+               }
+               else
+                       subindent = 0;
+               appendContextKeyword(context, "", subindent, 0, 0);
+
                get_setop_query(op->rarg, query, context, resultDesc);
-               if (need_paren)
-                       appendStringInfoChar(buf, ')');
 
                if (PRETTY_INDENT(context))
-                       context->indentLevel -= PRETTYINDENT_STD;
+                       context->indentLevel -= subindent;
+               if (need_paren)
+                       appendContextKeyword(context, ")", 0, 0, 0);
        }
        else
        {
@@ -4783,14 +5102,14 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
  * Also returns the expression tree, so caller need not find it again.
  */
 static Node *
-get_rule_sortgroupclause(SortGroupClause *srt, List *tlist, bool force_colno,
+get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
                                                 deparse_context *context)
 {
        StringInfo      buf = context->buf;
        TargetEntry *tle;
        Node       *expr;
 
-       tle = get_sortgroupclause_tle(srt, tlist);
+       tle = get_sortgroupref_tle(ref, tlist);
        expr = (Node *) tle->expr;
 
        /*
@@ -4798,8 +5117,9 @@ get_rule_sortgroupclause(SortGroupClause *srt, List *tlist, bool force_colno,
         * 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.      Otherwise, just dump the expression
-        * normally.
+        * 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.
         */
        if (force_colno)
        {
@@ -4808,33 +5128,113 @@ get_rule_sortgroupclause(SortGroupClause *srt, List *tlist, bool force_colno,
        }
        else if (expr && IsA(expr, Const))
                get_const_expr((Const *) expr, context, 1);
+       else if (!expr || IsA(expr, Var))
+               get_rule_expr(expr, context, true);
        else
+       {
+               /*
+                * We must force parens for function-like expressions even if
+                * PRETTY_PAREN is off, since those are the ones in danger of
+                * misparsing. For other expressions we need to force them only if
+                * PRETTY_PAREN is on, since otherwise the expression will output them
+                * itself. (We can't skip the parens.)
+                */
+               bool            need_paren = (PRETTY_PAREN(context)
+                                                                 || IsA(expr, FuncExpr)
+                                                                 ||IsA(expr, Aggref)
+                                                                 ||IsA(expr, WindowFunc));
+
+               if (need_paren)
+                       appendStringInfoString(context->buf, "(");
                get_rule_expr(expr, context, true);
+               if (need_paren)
+                       appendStringInfoString(context->buf, ")");
+       }
 
        return expr;
 }
 
 /*
- * Display an ORDER BY list.
+ * Display a GroupingSet
  */
 static void
-get_rule_orderby(List *orderList, List *targetList,
-                                bool force_colno, deparse_context *context)
+get_rule_groupingset(GroupingSet *gset, List *targetlist,
+                                        bool omit_parens, deparse_context *context)
 {
-       StringInfo      buf = context->buf;
-       const char *sep;
        ListCell   *l;
+       StringInfo      buf = context->buf;
+       bool            omit_child_parens = true;
+       char       *sep = "";
 
-       sep = "";
-       foreach(l, orderList)
+       switch (gset->kind)
        {
-               SortGroupClause *srt = (SortGroupClause *) lfirst(l);
-               Node       *sortexpr;
-               Oid                     sortcoltype;
-               TypeCacheEntry *typentry;
+               case GROUPING_SET_EMPTY:
+                       appendStringInfoString(buf, "()");
+                       return;
 
-               appendStringInfoString(buf, sep);
-               sortexpr = get_rule_sortgroupclause(srt, targetList,
+               case GROUPING_SET_SIMPLE:
+                       {
+                               if (!omit_parens || list_length(gset->content) != 1)
+                                       appendStringInfoString(buf, "(");
+
+                               foreach(l, gset->content)
+                               {
+                                       Index           ref = lfirst_int(l);
+
+                                       appendStringInfoString(buf, sep);
+                                       get_rule_sortgroupclause(ref, targetlist,
+                                                                                        false, context);
+                                       sep = ", ";
+                               }
+
+                               if (!omit_parens || list_length(gset->content) != 1)
+                                       appendStringInfoString(buf, ")");
+                       }
+                       return;
+
+               case GROUPING_SET_ROLLUP:
+                       appendStringInfoString(buf, "ROLLUP(");
+                       break;
+               case GROUPING_SET_CUBE:
+                       appendStringInfoString(buf, "CUBE(");
+                       break;
+               case GROUPING_SET_SETS:
+                       appendStringInfoString(buf, "GROUPING SETS (");
+                       omit_child_parens = false;
+                       break;
+       }
+
+       foreach(l, gset->content)
+       {
+               appendStringInfoString(buf, sep);
+               get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
+               sep = ", ";
+       }
+
+       appendStringInfoString(buf, ")");
+}
+
+/*
+ * Display an ORDER BY list.
+ */
+static void
+get_rule_orderby(List *orderList, List *targetList,
+                                bool force_colno, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+       const char *sep;
+       ListCell   *l;
+
+       sep = "";
+       foreach(l, orderList)
+       {
+               SortGroupClause *srt = (SortGroupClause *) lfirst(l);
+               Node       *sortexpr;
+               Oid                     sortcoltype;
+               TypeCacheEntry *typentry;
+
+               appendStringInfoString(buf, sep);
+               sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
                                                                                        force_colno, context);
                sortcoltype = exprType(sortexpr);
                /* See whether operator is default < or > for datatype */
@@ -4934,7 +5334,7 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
                        SortGroupClause *grp = (SortGroupClause *) lfirst(l);
 
                        appendStringInfoString(buf, sep);
-                       get_rule_sortgroupclause(grp, targetList,
+                       get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
                                                                         false, context);
                        sep = ", ";
                }
@@ -5061,6 +5461,10 @@ get_insert_query_def(Query *query, deparse_context *context)
        }
        appendStringInfo(buf, "INSERT INTO %s ",
                                         generate_relation_name(rte->relid, NIL));
+       /* INSERT requires AS keyword for target alias */
+       if (rte->alias != NULL)
+               appendStringInfo(buf, "AS %s ",
+                                                quote_identifier(rte->alias->aliasname));
 
        /*
         * Add the insert-column-names list.  To handle indirection properly, we
@@ -5143,6 +5547,69 @@ get_insert_query_def(Query *query, deparse_context *context)
                appendStringInfoString(buf, "DEFAULT VALUES");
        }
 
+       /* Add ON CONFLICT if present */
+       if (query->onConflict)
+       {
+               OnConflictExpr *confl = query->onConflict;
+
+               appendStringInfoString(buf, " ON CONFLICT");
+
+               if (confl->arbiterElems)
+               {
+                       /* Add the single-VALUES expression list */
+                       appendStringInfoChar(buf, '(');
+                       get_rule_expr((Node *) confl->arbiterElems, context, false);
+                       appendStringInfoChar(buf, ')');
+
+                       /* Add a WHERE clause (for partial indexes) if given */
+                       if (confl->arbiterWhere != NULL)
+                       {
+                               bool            save_varprefix;
+
+                               /*
+                                * Force non-prefixing of Vars, since parser assumes that they
+                                * belong to target relation.  WHERE clause does not use
+                                * InferenceElem, so this is separately required.
+                                */
+                               save_varprefix = context->varprefix;
+                               context->varprefix = false;
+
+                               appendContextKeyword(context, " WHERE ",
+                                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+                               get_rule_expr(confl->arbiterWhere, context, false);
+
+                               context->varprefix = save_varprefix;
+                       }
+               }
+               else if (confl->constraint != InvalidOid)
+               {
+                       char       *constraint = get_constraint_name(confl->constraint);
+
+                       appendStringInfo(buf, " ON CONSTRAINT %s",
+                                                        quote_qualified_identifier(NULL, constraint));
+               }
+
+               if (confl->action == ONCONFLICT_NOTHING)
+               {
+                       appendStringInfoString(buf, " DO NOTHING");
+               }
+               else
+               {
+                       appendStringInfoString(buf, " DO UPDATE SET ");
+                       /* Deparse targetlist */
+                       get_update_query_targetlist_def(query, confl->onConflictSet,
+                                                                                       context, rte);
+
+                       /* Add a WHERE clause if given */
+                       if (confl->onConflictWhere != NULL)
+                       {
+                               appendContextKeyword(context, " WHERE ",
+                                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+                               get_rule_expr(confl->onConflictWhere, context, false);
+                       }
+               }
+       }
+
        /* Add RETURNING if present */
        if (query->returningList)
        {
@@ -5161,9 +5628,7 @@ static void
 get_update_query_def(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
-       char       *sep;
        RangeTblEntry *rte;
-       ListCell   *l;
 
        /* Insert the WITH clause if given */
        get_with_clause(query, context);
@@ -5186,9 +5651,77 @@ get_update_query_def(Query *query, deparse_context *context)
                                                 quote_identifier(rte->alias->aliasname));
        appendStringInfoString(buf, " SET ");
 
+       /* Deparse targetlist */
+       get_update_query_targetlist_def(query, query->targetList, context, rte);
+
+       /* Add the FROM clause if needed */
+       get_from_clause(query, " FROM ", context);
+
+       /* Add a WHERE clause if given */
+       if (query->jointree->quals != NULL)
+       {
+               appendContextKeyword(context, " WHERE ",
+                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+               get_rule_expr(query->jointree->quals, context, false);
+       }
+
+       /* Add RETURNING if present */
+       if (query->returningList)
+       {
+               appendContextKeyword(context, " RETURNING",
+                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+               get_target_list(query->returningList, context, NULL);
+       }
+}
+
+
+/* ----------
+ * get_update_query_targetlist_def                     - Parse back an UPDATE targetlist
+ * ----------
+ */
+static void
+get_update_query_targetlist_def(Query *query, List *targetList,
+                                                               deparse_context *context, RangeTblEntry *rte)
+{
+       StringInfo      buf = context->buf;
+       ListCell   *l;
+       ListCell   *next_ma_cell;
+       int                     remaining_ma_columns;
+       const char *sep;
+       SubLink    *cur_ma_sublink;
+       List       *ma_sublinks;
+
+       /*
+        * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
+        * into a list.  We expect them to appear, in ID order, in resjunk tlist
+        * entries.
+        */
+       ma_sublinks = NIL;
+       if (query->hasSubLinks)         /* else there can't be any */
+       {
+               foreach(l, targetList)
+               {
+                       TargetEntry *tle = (TargetEntry *) lfirst(l);
+
+                       if (tle->resjunk && IsA(tle->expr, SubLink))
+                       {
+                               SubLink    *sl = (SubLink *) tle->expr;
+
+                               if (sl->subLinkType == MULTIEXPR_SUBLINK)
+                               {
+                                       ma_sublinks = lappend(ma_sublinks, sl);
+                                       Assert(sl->subLinkId == list_length(ma_sublinks));
+                               }
+                       }
+               }
+       }
+       next_ma_cell = list_head(ma_sublinks);
+       cur_ma_sublink = NULL;
+       remaining_ma_columns = 0;
+
        /* Add the comma separated list of 'attname = value' */
        sep = "";
-       foreach(l, query->targetList)
+       foreach(l, targetList)
        {
                TargetEntry *tle = (TargetEntry *) lfirst(l);
                Node       *expr;
@@ -5196,9 +5729,57 @@ get_update_query_def(Query *query, deparse_context *context)
                if (tle->resjunk)
                        continue;                       /* ignore junk entries */
 
+               /* Emit separator (OK whether we're in multiassignment or not) */
                appendStringInfoString(buf, sep);
                sep = ", ";
 
+               /*
+                * Check to see if we're starting a multiassignment group: if so,
+                * output a left paren.
+                */
+               if (next_ma_cell != NULL && cur_ma_sublink == NULL)
+               {
+                       /*
+                        * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
+                        * Param.  That could be buried under FieldStores and ArrayRefs
+                        * (cf processIndirection()), and underneath those there could be
+                        * an implicit type coercion.
+                        */
+                       expr = (Node *) tle->expr;
+                       while (expr)
+                       {
+                               if (IsA(expr, FieldStore))
+                               {
+                                       FieldStore *fstore = (FieldStore *) expr;
+
+                                       expr = (Node *) linitial(fstore->newvals);
+                               }
+                               else if (IsA(expr, ArrayRef))
+                               {
+                                       ArrayRef   *aref = (ArrayRef *) expr;
+
+                                       if (aref->refassgnexpr == NULL)
+                                               break;
+                                       expr = (Node *) aref->refassgnexpr;
+                               }
+                               else
+                                       break;
+                       }
+                       expr = strip_implicit_coercions(expr);
+
+                       if (expr && IsA(expr, Param) &&
+                               ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
+                       {
+                               cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
+                               next_ma_cell = lnext(next_ma_cell);
+                               remaining_ma_columns = count_nonjunk_tlist_entries(
+                                                 ((Query *) cur_ma_sublink->subselect)->targetList);
+                               Assert(((Param *) expr)->paramid ==
+                                          ((cur_ma_sublink->subLinkId << 16) | 1));
+                               appendStringInfoChar(buf, '(');
+                       }
+               }
+
                /*
                 * Put out name of target column; look in the catalogs, not at
                 * tle->resname, since resname will fail to track RENAME.
@@ -5213,29 +5794,24 @@ get_update_query_def(Query *query, deparse_context *context)
                 */
                expr = processIndirection((Node *) tle->expr, context, true);
 
+               /*
+                * If we're in a multiassignment, skip printing anything more, unless
+                * this is the last column; in which case, what we print should be the
+                * sublink, not the Param.
+                */
+               if (cur_ma_sublink != NULL)
+               {
+                       if (--remaining_ma_columns > 0)
+                               continue;               /* not the last column of multiassignment */
+                       appendStringInfoChar(buf, ')');
+                       expr = (Node *) cur_ma_sublink;
+                       cur_ma_sublink = NULL;
+               }
+
                appendStringInfoString(buf, " = ");
 
                get_rule_expr(expr, context, false);
        }
-
-       /* Add the FROM clause if needed */
-       get_from_clause(query, " FROM ", context);
-
-       /* Add a WHERE clause if given */
-       if (query->jointree->quals != NULL)
-       {
-               appendContextKeyword(context, " WHERE ",
-                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
-               get_rule_expr(query->jointree->quals, context, false);
-       }
-
-       /* Add RETURNING if present */
-       if (query->returningList)
-       {
-               appendContextKeyword(context, " RETURNING",
-                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
-               get_target_list(query->returningList, context, NULL);
-       }
 }
 
 
@@ -5493,8 +6069,8 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
        /*
         * If it's an unnamed join, look at the expansion of the alias variable.
         * If it's a simple reference to one of the input vars, then recursively
-        * print the name of that var instead.  When it's not a simple reference,
-        * we have to just print the unqualified join column name.      (This can only
+        * print the name of that var instead.  When it's not a simple reference,
+        * we have to just print the unqualified join column name.  (This can only
         * happen with "dangerous" merged columns in a JOIN USING; we took pains
         * previously to make the unqualified column name unique in such cases.)
         *
@@ -5522,7 +6098,7 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
                /*
                 * Unnamed join has no refname.  (Note: since it's unnamed, there is
                 * no way the user could have referenced it to create a whole-row Var
-                * for it.      So we don't have to cover that case below.)
+                * for it.  So we don't have to cover that case below.)
                 */
                Assert(refname == NULL);
        }
@@ -5563,7 +6139,7 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
 
 
 /*
- * Get the name of a field of an expression of composite type. The
+ * Get the name of a field of an expression of composite type.  The
  * expression is usually a Var, but we handle other cases too.
  *
  * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
@@ -5573,7 +6149,7 @@ get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
  * could also be RECORD.  Since no actual table or view column is allowed to
  * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
  * or to a subquery output.  We drill down to find the ultimate defining
- * expression and attempt to infer the field name from it.     We ereport if we
+ * expression and attempt to infer the field name from it.  We ereport if we
  * can't determine the name.
  *
  * Similarly, a PARAM of type RECORD has to refer to some expression of
@@ -5938,7 +6514,7 @@ get_name_for_var_field(Var *var, int fieldno,
 
        /*
         * We now have an expression we can't expand any more, so see if
-        * get_expr_result_type() can do anything with it.      If not, pass to
+        * get_expr_result_type() can do anything with it.  If not, pass to
         * lookup_rowtype_tupdesc() which will probably fail, but will give an
         * appropriate error message while failing.
         */
@@ -5956,7 +6532,7 @@ get_name_for_var_field(Var *var, int fieldno,
  * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
  *
  * If successful, return the expression and set *dpns_p and *ancestor_cell_p
- * appropriately for calling push_ancestor_plan().     If no referent can be
+ * appropriately for calling push_ancestor_plan().  If no referent can be
  * found, return NULL.
  */
 static Node *
@@ -6088,7 +6664,7 @@ get_parameter(Param *param, deparse_context *context)
 
        /*
         * If it's a PARAM_EXEC parameter, try to locate the expression from which
-        * the parameter was computed.  Note that failing to find a referent isn't
+        * the parameter was computed.  Note that failing to find a referent isn't
         * an error, since the Param might well be a subplan output rather than an
         * input.
         */
@@ -6391,14 +6967,36 @@ appendContextKeyword(deparse_context *context, const char *str,
 
        if (PRETTY_INDENT(context))
        {
+               int                     indentAmount;
+
                context->indentLevel += indentBefore;
 
                /* remove any trailing spaces currently in the buffer ... */
                removeStringInfoSpaces(buf);
                /* ... then add a newline and some spaces */
                appendStringInfoChar(buf, '\n');
-               appendStringInfoSpaces(buf,
-                                                          Max(context->indentLevel, 0) + indentPlus);
+
+               if (context->indentLevel < PRETTYINDENT_LIMIT)
+                       indentAmount = Max(context->indentLevel, 0) + indentPlus;
+               else
+               {
+                       /*
+                        * If we're indented more than PRETTYINDENT_LIMIT characters, try
+                        * to conserve horizontal space by reducing the per-level
+                        * indentation.  For best results the scale factor here should
+                        * divide all the indent amounts that get added to indentLevel
+                        * (PRETTYINDENT_STD, etc).  It's important that the indentation
+                        * not grow unboundedly, else deeply-nested trees use O(N^2)
+                        * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
+                        */
+                       indentAmount = PRETTYINDENT_LIMIT +
+                               (context->indentLevel - PRETTYINDENT_LIMIT) /
+                               (PRETTYINDENT_STD / 2);
+                       indentAmount %= PRETTYINDENT_LIMIT;
+                       /* scale/wrap logic affects indentLevel, but not indentPlus */
+                       indentAmount += indentPlus;
+               }
+               appendStringInfoSpaces(buf, indentAmount);
 
                appendStringInfoString(buf, str);
 
@@ -6475,6 +7073,10 @@ get_rule_expr(Node *node, deparse_context *context,
        if (node == NULL)
                return;
 
+       /* Guard against excessively long or deeply-nested queries */
+       CHECK_FOR_INTERRUPTS();
+       check_stack_depth();
+
        /*
         * Each level of get_rule_expr must emit an indivisible term
         * (parenthesized if necessary) to ensure result is reparsed into the same
@@ -6500,6 +7102,16 @@ get_rule_expr(Node *node, deparse_context *context,
                        get_agg_expr((Aggref *) node, context);
                        break;
 
+               case T_GroupingFunc:
+                       {
+                               GroupingFunc *gexpr = (GroupingFunc *) node;
+
+                               appendStringInfoString(buf, "GROUPING(");
+                               get_rule_expr((Node *) gexpr->args, context, true);
+                               appendStringInfoChar(buf, ')');
+                       }
+                       break;
+
                case T_WindowFunc:
                        get_windowfunc_expr((WindowFunc *) node, context);
                        break;
@@ -6540,10 +7152,10 @@ get_rule_expr(Node *node, deparse_context *context,
 
                                /*
                                 * If there's a refassgnexpr, we want to print the node in the
-                                * format "array[subscripts] := refassgnexpr".  This is not
+                                * format "array[subscripts] := refassgnexpr".  This is not
                                 * legal SQL, so decompilation of INSERT or UPDATE statements
                                 * should always use processIndirection as part of the
-                                * statement-level syntax.      We should only see this when
+                                * statement-level syntax.  We should only see this when
                                 * EXPLAIN tries to print the targetlist of a plan resulting
                                 * from such a statement.
                                 */
@@ -6577,7 +7189,7 @@ get_rule_expr(Node *node, deparse_context *context,
                        {
                                NamedArgExpr *na = (NamedArgExpr *) node;
 
-                               appendStringInfo(buf, "%s := ", quote_identifier(na->name));
+                               appendStringInfo(buf, "%s => ", quote_identifier(na->name));
                                get_rule_expr((Node *) na->arg, context, showimplicit);
                        }
                        break;
@@ -6702,7 +7314,7 @@ get_rule_expr(Node *node, deparse_context *context,
 
                                /*
                                 * We cannot see an already-planned subplan in rule deparsing,
-                                * only while EXPLAINing a query plan.  We don't try to
+                                * only while EXPLAINing a query plan.  We don't try to
                                 * reconstruct the original SQL, just reference the subplan
                                 * that appears elsewhere in EXPLAIN's result.
                                 */
@@ -6775,14 +7387,14 @@ get_rule_expr(Node *node, deparse_context *context,
                                 * There is no good way to represent a FieldStore as real SQL,
                                 * so decompilation of INSERT or UPDATE statements should
                                 * always use processIndirection as part of the
-                                * statement-level syntax.      We should only get here when
+                                * statement-level syntax.  We should only get here when
                                 * EXPLAIN tries to print the targetlist of a plan resulting
                                 * from such a statement.  The plan case is even harder than
                                 * ordinary rules would be, because the planner tries to
                                 * collapse multiple assignments to the same field or subfield
                                 * into one FieldStore; so we can see a list of target fields
                                 * not just one, and the arguments could be FieldStores
-                                * themselves.  We don't bother to try to print the target
+                                * themselves.  We don't bother to try to print the target
                                 * field names; we just print the source arguments, with a
                                 * ROW() around them if there's more than one.  This isn't
                                 * terribly complete, but it's probably good enough for
@@ -7025,7 +7637,8 @@ get_rule_expr(Node *node, deparse_context *context,
                                                !tupdesc->attrs[i]->attisdropped)
                                        {
                                                appendStringInfoString(buf, sep);
-                                               get_rule_expr(e, context, true);
+                                               /* Whole-row Vars need special treatment here */
+                                               get_rule_expr_toplevel(e, context, true);
                                                sep = ", ";
                                        }
                                        i++;
@@ -7384,6 +7997,53 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
+               case T_InferenceElem:
+                       {
+                               InferenceElem *iexpr = (InferenceElem *) node;
+                               bool            save_varprefix;
+                               bool            need_parens;
+
+                               /*
+                                * InferenceElem can only refer to target relation, so a
+                                * prefix is not useful, and indeed would cause parse errors.
+                                */
+                               save_varprefix = context->varprefix;
+                               context->varprefix = false;
+
+                               /*
+                                * Parenthesize the element unless it's a simple Var or a bare
+                                * function call.  Follows pg_get_indexdef_worker().
+                                */
+                               need_parens = !IsA(iexpr->expr, Var);
+                               if (IsA(iexpr->expr, FuncExpr) &&
+                                       ((FuncExpr *) iexpr->expr)->funcformat ==
+                                       COERCE_EXPLICIT_CALL)
+                                       need_parens = false;
+
+                               if (need_parens)
+                                       appendStringInfoChar(buf, '(');
+                               get_rule_expr((Node *) iexpr->expr,
+                                                         context, false);
+                               if (need_parens)
+                                       appendStringInfoChar(buf, ')');
+
+                               context->varprefix = save_varprefix;
+
+                               if (iexpr->infercollid)
+                                       appendStringInfo(buf, " COLLATE %s",
+                                                               generate_collation_name(iexpr->infercollid));
+
+                               /* Add the operator class name, if not default */
+                               if (iexpr->inferopclass)
+                               {
+                                       Oid                     inferopclass = iexpr->inferopclass;
+                                       Oid                     inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
+
+                                       get_opclass_name(inferopclass, inferopcinputtype, buf);
+                               }
+                       }
+                       break;
+
                case T_List:
                        {
                                char       *sep;
@@ -7405,6 +8065,27 @@ get_rule_expr(Node *node, deparse_context *context,
        }
 }
 
+/*
+ * get_rule_expr_toplevel              - Parse back a toplevel expression
+ *
+ * Same as get_rule_expr(), except that if the expr is just a Var, we pass
+ * istoplevel = true not false to get_variable().  This causes whole-row Vars
+ * to get printed with decoration that will prevent expansion of "*".
+ * We need to use this in contexts such as ROW() and VALUES(), where the
+ * parser would expand "foo.*" appearing at top level.  (In principle we'd
+ * use this in get_target_list() too, but that has additional worries about
+ * whether to print AS, so it needs to invoke get_variable() directly anyway.)
+ */
+static void
+get_rule_expr_toplevel(Node *node, deparse_context *context,
+                                          bool showimplicit)
+{
+       if (node && IsA(node, Var))
+               (void) get_variable((Var *) node, 0, true, context);
+       else
+               get_rule_expr(node, context, showimplicit);
+}
+
 
 /*
  * get_oper_expr                       - Parse back an OpExpr node
@@ -7538,7 +8219,8 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
                                         generate_function_name(funcoid, nargs,
                                                                                        argnames, argtypes,
                                                                                        expr->funcvariadic,
-                                                                                       &use_variadic));
+                                                                                       &use_variadic,
+                                                                                       context->special_exprkind));
        nargs = 0;
        foreach(l, expr->args)
        {
@@ -7570,14 +8252,15 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
                                         generate_function_name(aggref->aggfnoid, nargs,
                                                                                        NIL, argtypes,
                                                                                        aggref->aggvariadic,
-                                                                                       &use_variadic),
+                                                                                       &use_variadic,
+                                                                                       context->special_exprkind),
                                         (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
 
        if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
        {
                /*
                 * Ordered-set aggregates do not use "*" syntax.  Also, we needn't
-                * worry about inserting VARIADIC.      So we can just dump the direct
+                * worry about inserting VARIADIC.  So we can just dump the direct
                 * args as-is.
                 */
                Assert(!aggref->aggvariadic);
@@ -7660,7 +8343,8 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
        appendStringInfo(buf, "%s(",
                                         generate_function_name(wfunc->winfnoid, nargs,
                                                                                        argnames, argtypes,
-                                                                                       false, NULL));
+                                                                                       false, NULL,
+                                                                                       context->special_exprkind));
        /* winstar can be set only in zero-argument aggregates */
        if (wfunc->winstar)
                appendStringInfoChar(buf, '*');
@@ -7719,9 +8403,16 @@ get_coercion_expr(Node *arg, deparse_context *context,
         * Since parse_coerce.c doesn't immediately collapse application of
         * length-coercion functions to constants, what we'll typically see in
         * such cases is a Const with typmod -1 and a length-coercion function
-        * right above it.      Avoid generating redundant output. However, beware of
+        * right above it.  Avoid generating redundant output. However, beware of
         * suppressing casts when the user actually wrote something like
         * 'foo'::text::char(3).
+        *
+        * Note: it might seem that we are missing the possibility of needing to
+        * print a COLLATE clause for such a Const.  However, a Const could only
+        * have nondefault collation in a post-constant-folding tree, in which the
+        * length coercion would have been folded too.  See also the special
+        * handling of CollateExpr in coerce_to_target_type(): any collation
+        * marking will be above the coercion node, not below it.
         */
        if (arg && IsA(arg, Const) &&
                ((Const *) arg)->consttype == resulttype &&
@@ -7752,8 +8443,9 @@ get_coercion_expr(Node *arg, deparse_context *context,
  * the right type by default.
  *
  * If the Const's collation isn't default for its type, show that too.
- * This can only happen in trees that have been through constant-folding.
- * We assume we don't need to do this when showtype is -1.
+ * We mustn't do this when showtype is -1 (since that means the caller will
+ * print "::typename", and we can't put a COLLATE clause in between).  It's
+ * caller's responsibility that collation isn't missed in such cases.
  * ----------
  */
 static void
@@ -7763,8 +8455,7 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
        Oid                     typoutput;
        bool            typIsVarlena;
        char       *extval;
-       bool            isfloat = false;
-       bool            needlabel;
+       bool            needlabel = false;
 
        if (constval->constisnull)
        {
@@ -7790,40 +8481,42 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
 
        switch (constval->consttype)
        {
-               case INT2OID:
                case INT4OID:
-               case INT8OID:
-               case OIDOID:
-               case FLOAT4OID:
-               case FLOAT8OID:
+
+                       /*
+                        * INT4 can be printed without any decoration, unless it is
+                        * negative; in that case print it as '-nnn'::integer to ensure
+                        * that the output will re-parse as a constant, not as a constant
+                        * plus operator.  In most cases we could get away with printing
+                        * (-nnn) instead, because of the way that gram.y handles negative
+                        * literals; but that doesn't work for INT_MIN, and it doesn't
+                        * seem that much prettier anyway.
+                        */
+                       if (extval[0] != '-')
+                               appendStringInfoString(buf, extval);
+                       else
+                       {
+                               appendStringInfo(buf, "'%s'", extval);
+                               needlabel = true;               /* we must attach a cast */
+                       }
+                       break;
+
                case NUMERICOID:
+
+                       /*
+                        * NUMERIC can be printed without quotes if it looks like a float
+                        * constant (not an integer, and not Infinity or NaN) and doesn't
+                        * have a leading sign (for the same reason as for INT4).
+                        */
+                       if (isdigit((unsigned char) extval[0]) &&
+                               strcspn(extval, "eE.") != strlen(extval))
                        {
-                               /*
-                                * These types are printed without quotes unless they contain
-                                * values that aren't accepted by the scanner unquoted (e.g.,
-                                * 'NaN').      Note that strtod() and friends might accept NaN,
-                                * so we can't use that to test.
-                                *
-                                * In reality we only need to defend against infinity and NaN,
-                                * so we need not get too crazy about pattern matching here.
-                                *
-                                * There is a special-case gotcha: if the constant is signed,
-                                * we need to parenthesize it, else the parser might see a
-                                * leading plus/minus as binding less tightly than adjacent
-                                * operators --- particularly, the cast that we might attach
-                                * below.
-                                */
-                               if (strspn(extval, "0123456789+-eE.") == strlen(extval))
-                               {
-                                       if (extval[0] == '+' || extval[0] == '-')
-                                               appendStringInfo(buf, "(%s)", extval);
-                                       else
-                                               appendStringInfoString(buf, extval);
-                                       if (strcspn(extval, "eE.") != strlen(extval))
-                                               isfloat = true; /* it looks like a float */
-                               }
-                               else
-                                       appendStringInfo(buf, "'%s'", extval);
+                               appendStringInfoString(buf, extval);
+                       }
+                       else
+                       {
+                               appendStringInfo(buf, "'%s'", extval);
+                               needlabel = true;               /* we must attach a cast */
                        }
                        break;
 
@@ -7859,18 +8552,21 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
        switch (constval->consttype)
        {
                case BOOLOID:
-               case INT4OID:
                case UNKNOWNOID:
                        /* These types can be left unlabeled */
                        needlabel = false;
                        break;
+               case INT4OID:
+                       /* We determined above whether a label is needed */
+                       break;
                case NUMERICOID:
 
                        /*
-                        * Float-looking constants will be typed as numeric, but if
-                        * there's a specific typmod we need to show it.
+                        * Float-looking constants will be typed as numeric, which we
+                        * checked above; but if there's a nondefault typmod we need to
+                        * show it.
                         */
-                       needlabel = !isfloat || (constval->consttypmod >= 0);
+                       needlabel |= (constval->consttypmod >= 0);
                        break;
                default:
                        needlabel = true;
@@ -8030,6 +8726,7 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
                        break;
 
                case EXPR_SUBLINK:
+               case MULTIEXPR_SUBLINK:
                case ARRAY_SUBLINK:
                        need_paren = false;
                        break;
@@ -8290,7 +8987,9 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                                break;
                        case RTE_VALUES:
                                /* Values list RTE */
+                               appendStringInfoChar(buf, '(');
                                get_values_def(rte->values_lists, context);
+                               appendStringInfoChar(buf, ')');
                                break;
                        case RTE_CTE:
                                appendStringInfoString(buf, quote_identifier(rte->ctename));
@@ -8325,13 +9024,18 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                else if (rte->rtekind == RTE_FUNCTION)
                {
                        /*
-                        * For a function RTE, always print alias.      This covers possible
+                        * For a function RTE, always print alias.  This covers possible
                         * renaming of the function and/or instability of the
                         * FigureColname rules for things that aren't simple functions.
                         * Note we'd need to force it anyway for the columndef list case.
                         */
                        printalias = true;
                }
+               else if (rte->rtekind == RTE_VALUES)
+               {
+                       /* Alias is syntactically required for VALUES */
+                       printalias = true;
+               }
                else if (rte->rtekind == RTE_CTE)
                {
                        /*
@@ -8356,6 +9060,10 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                        /* Else print column aliases as needed */
                        get_column_alias_list(colinfo, context);
                }
+
+               /* Tablesample clause must go after any alias */
+               if (rte->rtekind == RTE_RELATION && rte->tablesample)
+                       get_tablesample_def(rte->tablesample, context);
        }
        else if (IsA(jtnode, JoinExpr))
        {
@@ -8555,6 +9263,44 @@ get_from_clause_coldeflist(RangeTblFunction *rtfunc,
        appendStringInfoChar(buf, ')');
 }
 
+/*
+ * get_tablesample_def                 - print a TableSampleClause
+ */
+static void
+get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+       Oid                     argtypes[1];
+       int                     nargs;
+       ListCell   *l;
+
+       /*
+        * We should qualify the handler's function name if it wouldn't be
+        * resolved by lookup in the current search path.
+        */
+       argtypes[0] = INTERNALOID;
+       appendStringInfo(buf, " TABLESAMPLE %s (",
+                                        generate_function_name(tablesample->tsmhandler, 1,
+                                                                                       NIL, argtypes,
+                                                                                       false, NULL, EXPR_KIND_NONE));
+
+       nargs = 0;
+       foreach(l, tablesample->args)
+       {
+               if (nargs++ > 0)
+                       appendStringInfoString(buf, ", ");
+               get_rule_expr((Node *) lfirst(l), context, false);
+       }
+       appendStringInfoChar(buf, ')');
+
+       if (tablesample->repeatable != NULL)
+       {
+               appendStringInfoString(buf, " REPEATABLE (");
+               get_rule_expr((Node *) tablesample->repeatable, context, false);
+               appendStringInfoChar(buf, ')');
+       }
+}
+
 /*
  * get_opclass_name                    - fetch name of an index operator class
  *
@@ -8581,7 +9327,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
        if (!OidIsValid(actual_datatype) ||
                GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
        {
-               /* Okay, we need the opclass name.      Do we need to qualify it? */
+               /* 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));
@@ -8677,10 +9423,12 @@ printSubscripts(ArrayRef *aref, deparse_context *context)
                appendStringInfoChar(buf, '[');
                if (lowlist_item)
                {
+                       /* If subexpression is NULL, get_rule_expr prints nothing */
                        get_rule_expr((Node *) lfirst(lowlist_item), context, false);
                        appendStringInfoChar(buf, ':');
                        lowlist_item = lnext(lowlist_item);
                }
+               /* If subexpression is NULL, get_rule_expr prints nothing */
                get_rule_expr((Node *) lfirst(uplist_item), context, false);
                appendStringInfoChar(buf, ']');
        }
@@ -8872,24 +9620,58 @@ generate_relation_name(Oid relid, List *namespaces)
        return result;
 }
 
+/*
+ * generate_qualified_relation_name
+ *             Compute the name to display for a relation specified by OID
+ *
+ * As above, but unconditionally schema-qualify the name.
+ */
+static char *
+generate_qualified_relation_name(Oid relid)
+{
+       HeapTuple       tp;
+       Form_pg_class reltup;
+       char       *relname;
+       char       *nspname;
+       char       *result;
+
+       tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for relation %u", relid);
+       reltup = (Form_pg_class) GETSTRUCT(tp);
+       relname = NameStr(reltup->relname);
+
+       nspname = get_namespace_name(reltup->relnamespace);
+       if (!nspname)
+               elog(ERROR, "cache lookup failed for namespace %u",
+                        reltup->relnamespace);
+
+       result = quote_qualified_identifier(nspname, 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 names and
- *             types.  (Those matter because of ambiguous-function resolution rules.)
+ *             types.  (Those matter because of ambiguous-function resolution rules.)
  *
  * If we're dealing with a potentially variadic function (in practice, this
  * means a FuncExpr or Aggref, not some other way of calling a function), then
  * has_variadic must specify whether variadic arguments have been merged,
  * and *use_variadic_p will be set to indicate whether to print VARIADIC in
- * the output. For non-FuncExpr cases, has_variadic should be FALSE and
+ * the output.  For non-FuncExpr cases, has_variadic should be FALSE and
  * use_variadic_p can be NULL.
  *
  * 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)
+                                          bool has_variadic, bool *use_variadic_p,
+                                          ParseExprKind special_exprkind)
 {
        char       *result;
        HeapTuple       proctup;
@@ -8904,6 +9686,7 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
        int                     p_nvargs;
        Oid                     p_vatype;
        Oid                *p_true_typeids;
+       bool            force_qualify = false;
 
        proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(proctup))
@@ -8911,6 +9694,16 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
        procform = (Form_pg_proc) GETSTRUCT(proctup);
        proname = NameStr(procform->proname);
 
+       /*
+        * Due to parser hacks to avoid needing to reserve CUBE, we need to force
+        * qualification in some special cases.
+        */
+       if (special_exprkind == EXPR_KIND_GROUP_BY)
+       {
+               if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
+                       force_qualify = true;
+       }
+
        /*
         * Determine whether VARIADIC should be printed.  We must do this first
         * since it affects the lookup rules in func_get_detail().
@@ -8942,14 +9735,23 @@ generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
        /*
         * 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 and VARIADIC flag.
+        * specified argtypes and VARIADIC flag.  But if we already decided to
+        * force qualification, then we can skip the lookup and pretend we didn't
+        * find it.
         */
-       p_result = func_get_detail(list_make1(makeString(proname)),
-                                                          NIL, argnames, nargs, argtypes,
-                                                          !use_variadic, true,
-                                                          &p_funcid, &p_rettype,
-                                                          &p_retset, &p_nvargs, &p_vatype,
-                                                          &p_true_typeids, NULL);
+       if (!force_qualify)
+               p_result = func_get_detail(list_make1(makeString(proname)),
+                                                                  NIL, argnames, nargs, argtypes,
+                                                                  !use_variadic, true,
+                                                                  &p_funcid, &p_rettype,
+                                                                  &p_retset, &p_nvargs, &p_vatype,
+                                                                  &p_true_typeids, NULL);
+       else
+       {
+               p_result = FUNCDETAIL_NOTFOUND;
+               p_funcid = InvalidOid;
+       }
+
        if ((p_result == FUNCDETAIL_NORMAL ||
                 p_result == FUNCDETAIL_AGGREGATE ||
                 p_result == FUNCDETAIL_WINDOWFUNC) &&
@@ -9107,18 +9909,59 @@ flatten_reloptions(Oid relid)
                                                                 Anum_pg_class_reloptions, &isnull);
        if (!isnull)
        {
-               Datum           sep,
-                                       txt;
+               StringInfoData buf;
+               Datum      *options;
+               int                     noptions;
+               int                     i;
 
-               /*
-                * We want to use array_to_text(reloptions, ', ') --- but
-                * DirectFunctionCall2(array_to_text) does not work, because
-                * array_to_text() relies on flinfo to be valid.  So use
-                * OidFunctionCall2.
-                */
-               sep = CStringGetTextDatum(", ");
-               txt = OidFunctionCall2(F_ARRAY_TO_TEXT, reloptions, sep);
-               result = TextDatumGetCString(txt);
+               initStringInfo(&buf);
+
+               deconstruct_array(DatumGetArrayTypeP(reloptions),
+                                                 TEXTOID, -1, false, 'i',
+                                                 &options, NULL, &noptions);
+
+               for (i = 0; i < noptions; i++)
+               {
+                       char       *option = TextDatumGetCString(options[i]);
+                       char       *name;
+                       char       *separator;
+                       char       *value;
+
+                       /*
+                        * Each array element should have the form name=value.  If the "="
+                        * is missing for some reason, treat it like an empty value.
+                        */
+                       name = option;
+                       separator = strchr(option, '=');
+                       if (separator)
+                       {
+                               *separator = '\0';
+                               value = separator + 1;
+                       }
+                       else
+                               value = "";
+
+                       if (i > 0)
+                               appendStringInfoString(&buf, ", ");
+                       appendStringInfo(&buf, "%s=", quote_identifier(name));
+
+                       /*
+                        * In general we need to quote the value; but to avoid unnecessary
+                        * clutter, do not quote if it is an identifier that would not
+                        * need quoting.  (We could also allow numbers, but that is a bit
+                        * trickier than it looks --- for example, are leading zeroes
+                        * significant?  We don't want to assume very much here about what
+                        * custom reloptions might mean.)
+                        */
+                       if (quote_identifier(value) == value)
+                               appendStringInfoString(&buf, value);
+                       else
+                               simple_quote_literal(&buf, value);
+
+                       pfree(option);
+               }
+
+               result = buf.data;
        }
 
        ReleaseSysCache(tuple);