]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/ruleutils.c
Run pgindent on 9.2 source tree in preparation for first 9.3
[postgresql] / src / backend / utils / adt / ruleutils.c
index 71fea45ddd0c58ea138e6eed7ef3466626f9e3e9..9ca3b9d0c44c71522309c2da61071e096825cdf0 100644 (file)
@@ -4,12 +4,12 @@
  *       Functions to convert stored expressions/querytrees back to
  *       source text
  *
- * Portions Copyright (c) 1996-2008, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
  * IDENTIFICATION
- *       $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.284 2008/09/06 20:18:08 tgl Exp $
+ *       src/backend/utils/adt/ruleutils.c
  *
  *-------------------------------------------------------------------------
  */
 #include <unistd.h>
 #include <fcntl.h>
 
-#include "access/genam.h"
 #include "access/sysattr.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_authid.h"
+#include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_depend.h"
+#include "catalog/pg_language.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
+#include "catalog/pg_proc.h"
 #include "catalog/pg_trigger.h"
+#include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/tablespace.h"
 #include "executor/spi.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/clauses.h"
 #include "optimizer/tlist.h"
-#include "parser/gramparse.h"
 #include "parser/keywords.h"
 #include "parser/parse_func.h"
 #include "parser/parse_oper.h"
+#include "parser/parser.h"
 #include "parser/parsetree.h"
 #include "rewrite/rewriteHandler.h"
 #include "rewrite/rewriteManip.h"
 #include "rewrite/rewriteSupport.h"
+#include "utils/array.h"
+#include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
+#include "utils/rel.h"
+#include "utils/syscache.h"
 #include "utils/tqual.h"
 #include "utils/typcache.h"
 #include "utils/xml.h"
@@ -66,6 +73,8 @@
 #define PRETTYFLAG_PAREN               1
 #define PRETTYFLAG_INDENT              2
 
+#define PRETTY_WRAP_DEFAULT            79
+
 /* macro to test if pretty action needed */
 #define PRETTY_PAREN(context)  ((context)->prettyFlags & PRETTYFLAG_PAREN)
 #define PRETTY_INDENT(context) ((context)->prettyFlags & PRETTYFLAG_INDENT)
@@ -81,6 +90,8 @@ typedef struct
 {
        StringInfo      buf;                    /* output buffer to append to */
        List       *namespaces;         /* List of deparse_namespace nodes */
+       List       *windowClause;       /* Current query level's WINDOW clause */
+       List       *windowTList;        /* targetlist for resolving WINDOW clause */
        int                     prettyFlags;    /* enabling of pretty-print functions */
        int                     indentLevel;    /* current indent level for prettyprint */
        bool            varprefix;              /* TRUE to print prefixes on Vars */
@@ -91,16 +102,31 @@ typedef struct
  * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
  * the current context's namespaces list.
  *
- * The rangetable is the list of actual RTEs from the query tree.
+ * The rangetable is the list of actual RTEs from the query tree, and the
+ * cte list is the list of actual CTEs.
  *
- * For deparsing plan trees, we provide for outer and inner subplan nodes.
- * The tlists of these nodes are used to resolve OUTER and INNER varnos.
+ * When deparsing plan trees, there is always just a single item in the
+ * deparse_namespace list (since a plan tree never contains Vars with
+ * 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
+ * be derived on-the-fly from the current PlanState, but it seems notationally
+ * clearer to set them up as separate fields.)
  */
 typedef struct
 {
        List       *rtable;                     /* List of RangeTblEntry nodes */
-       Plan       *outer_plan;         /* OUTER subplan, or NULL if none */
-       Plan       *inner_plan;         /* INNER subplan, or NULL if none */
+       List       *ctes;                       /* List of CommonTableExpr nodes */
+       /* Remaining fields are used only when deparsing a Plan tree: */
+       PlanState  *planstate;          /* immediate parent of current expression */
+       List       *ancestors;          /* ancestors of planstate */
+       PlanState  *outer_planstate;    /* outer subplan state, or NULL if none */
+       PlanState  *inner_planstate;    /* inner subplan state, or NULL if none */
+       List       *outer_tlist;        /* referent for OUTER_VAR Vars */
+       List       *inner_tlist;        /* referent for INNER_VAR Vars */
+       List       *index_tlist;        /* referent for INDEX_VAR Vars */
 } deparse_namespace;
 
 
@@ -112,6 +138,10 @@ static SPIPlanPtr plan_getrulebyoid = NULL;
 static const char *query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
 static SPIPlanPtr plan_getviewrule = NULL;
 static const char *query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
+static int     pretty_wrap = PRETTY_WRAP_DEFAULT;
+
+/* GUC parameters */
+bool           quote_all_identifiers = false;
 
 
 /* ----------
@@ -126,18 +156,30 @@ static char *deparse_expression_pretty(Node *expr, List *dpcontext,
                                                  bool forceprefix, bool showimplicit,
                                                  int prettyFlags, int startIndent);
 static char *pg_get_viewdef_worker(Oid viewoid, int prettyFlags);
+static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
 static void decompile_column_index_array(Datum column_index_array, Oid relId,
                                                         StringInfo buf);
 static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
-static char *pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
+static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
+                                          const Oid *excludeOps,
+                                          bool attrsOnly, bool showTblSpc,
                                           int prettyFlags);
 static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                                                        int prettyFlags);
-static char *pg_get_expr_worker(text *expr, Oid relid, char *relname,
+static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
                                   int prettyFlags);
 static int print_function_arguments(StringInfo buf, HeapTuple proctup,
-                                                bool print_table_args);
+                                                bool print_table_args, bool print_defaults);
 static void print_function_rettype(StringInfo buf, HeapTuple proctup);
+static void set_deparse_planstate(deparse_namespace *dpns, PlanState *ps);
+static void push_child_plan(deparse_namespace *dpns, PlanState *ps,
+                               deparse_namespace *save_dpns);
+static void pop_child_plan(deparse_namespace *dpns,
+                          deparse_namespace *save_dpns);
+static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
+                                  deparse_namespace *save_dpns);
+static void pop_ancestor_plan(deparse_namespace *dpns,
+                                 deparse_namespace *save_dpns);
 static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                         int prettyFlags);
 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
@@ -145,6 +187,7 @@ static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
 static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
                          TupleDesc resultDesc, int prettyFlags, int startIndent);
 static void get_values_def(List *values_lists, deparse_context *context);
+static void get_with_clause(Query *query, deparse_context *context);
 static void get_select_query_def(Query *query, deparse_context *context,
                                         TupleDesc resultDesc);
 static void get_insert_query_def(Query *query, deparse_context *context);
@@ -161,13 +204,20 @@ static void get_setop_query(Node *setOp, Query *query,
 static Node *get_rule_sortgroupclause(SortGroupClause *srt, List *tlist,
                                                 bool force_colno,
                                                 deparse_context *context);
-static char *get_variable(Var *var, int levelsup, bool showstar,
+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);
+static void get_rule_windowspec(WindowClause *wc, List *targetList,
+                                       deparse_context *context);
+static char *get_variable(Var *var, int levelsup, bool istoplevel,
                         deparse_context *context);
 static RangeTblEntry *find_rte_by_refname(const char *refname,
                                        deparse_context *context);
+static Node *find_param_referent(Param *param, deparse_context *context,
+                                       deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
+static void get_parameter(Param *param, deparse_context *context);
 static const char *get_simple_binary_op_name(OpExpr *expr);
 static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
-static void appendStringInfoSpaces(StringInfo buf, int count);
 static void appendContextKeyword(deparse_context *context, const char *str,
                                         int indentBefore, int indentAfter, int indentPlus);
 static void get_rule_expr(Node *node, deparse_context *context,
@@ -176,11 +226,13 @@ static void get_oper_expr(OpExpr *expr, deparse_context *context);
 static void get_func_expr(FuncExpr *expr, deparse_context *context,
                          bool showimplicit);
 static void get_agg_expr(Aggref *aggref, deparse_context *context);
+static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
 static void get_coercion_expr(Node *arg, deparse_context *context,
                                  Oid resulttype, int32 resulttypmod,
                                  Node *parentNode);
 static void get_const_expr(Const *constval, deparse_context *context,
                           int showtype);
+static void get_const_collation(Const *constval, deparse_context *context);
 static void simple_quote_literal(StringInfo buf, const char *val);
 static void get_sublink_expr(SubLink *sublink, deparse_context *context);
 static void get_from_clause(Query *query, const char *prefix,
@@ -189,16 +241,18 @@ static void get_from_clause_item(Node *jtnode, Query *query,
                                         deparse_context *context);
 static void get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
                                          deparse_context *context);
-static void get_from_clause_coldeflist(List *names, List *types, List *typmods,
+static void get_from_clause_coldeflist(List *names,
+                                                  List *types, List *typmods, List *collations,
                                                   deparse_context *context);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
                                 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context,
                                   bool printit);
 static void printSubscripts(ArrayRef *aref, deparse_context *context);
-static char *generate_relation_name(Oid relid);
-static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes,
-                                                                       bool *is_variadic);
+static char *get_relation_name(Oid relid);
+static char *generate_relation_name(Oid relid, List *namespaces);
+static char *generate_function_name(Oid funcid, int nargs, List *argnames,
+                                          Oid *argtypes, bool *is_variadic);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
@@ -268,7 +322,8 @@ pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
                plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
                if (plan == NULL)
                        elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
-               plan_getrulebyoid = SPI_saveplan(plan);
+               SPI_keepplan(plan);
+               plan_getrulebyoid = plan;
        }
 
        /*
@@ -328,6 +383,23 @@ pg_get_viewdef_ext(PG_FUNCTION_ARGS)
        PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags)));
 }
 
+Datum
+pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
+{
+       /* By OID */
+       Oid                     viewoid = PG_GETARG_OID(0);
+       int                     wrap = PG_GETARG_INT32(1);
+       int                     prettyFlags;
+       char       *result;
+
+       /* calling this implies we want pretty printing */
+       prettyFlags = PRETTYFLAG_PAREN | PRETTYFLAG_INDENT;
+       pretty_wrap = wrap;
+       result = pg_get_viewdef_worker(viewoid, prettyFlags);
+       pretty_wrap = PRETTY_WRAP_DEFAULT;
+       PG_RETURN_TEXT_P(string_to_text(result));
+}
+
 Datum
 pg_get_viewdef_name(PG_FUNCTION_ARGS)
 {
@@ -336,8 +408,9 @@ pg_get_viewdef_name(PG_FUNCTION_ARGS)
        RangeVar   *viewrel;
        Oid                     viewoid;
 
+       /* Look up view name.  Can't lock it - we might not have privileges. */
        viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
-       viewoid = RangeVarGetRelid(viewrel, false);
+       viewoid = RangeVarGetRelid(viewrel, NoLock, false);
 
        PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, 0)));
 }
@@ -354,8 +427,10 @@ pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
        Oid                     viewoid;
 
        prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
+
+       /* Look up view name.  Can't lock it - we might not have privileges. */
        viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
-       viewoid = RangeVarGetRelid(viewrel, false);
+       viewoid = RangeVarGetRelid(viewrel, NoLock, false);
 
        PG_RETURN_TEXT_P(string_to_text(pg_get_viewdef_worker(viewoid, prettyFlags)));
 }
@@ -399,7 +474,8 @@ pg_get_viewdef_worker(Oid viewoid, int prettyFlags)
                plan = SPI_prepare(query_getviewrule, 2, argtypes);
                if (plan == NULL)
                        elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
-               plan_getviewrule = SPI_saveplan(plan);
+               SPI_keepplan(plan);
+               plan_getviewrule = plan;
        }
 
        /*
@@ -441,6 +517,22 @@ Datum
 pg_get_triggerdef(PG_FUNCTION_ARGS)
 {
        Oid                     trigid = PG_GETARG_OID(0);
+
+       PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, false)));
+}
+
+Datum
+pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
+{
+       Oid                     trigid = PG_GETARG_OID(0);
+       bool            pretty = PG_GETARG_BOOL(1);
+
+       PG_RETURN_TEXT_P(string_to_text(pg_get_triggerdef_worker(trigid, pretty)));
+}
+
+static char *
+pg_get_triggerdef_worker(Oid trigid, bool pretty)
+{
        HeapTuple       ht_trig;
        Form_pg_trigger trigrec;
        StringInfoData buf;
@@ -449,6 +541,8 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
        SysScanDesc tgscan;
        int                     findx = 0;
        char       *tgname;
+       Datum           value;
+       bool            isnull;
 
        /*
         * Fetch the pg_trigger tuple by the Oid of the trigger
@@ -478,13 +572,18 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
 
        tgname = NameStr(trigrec->tgname);
        appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
-                                        trigrec->tgisconstraint ? "CONSTRAINT " : "",
+                                        OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
                                         quote_identifier(tgname));
 
        if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
                appendStringInfo(&buf, "BEFORE");
-       else
+       else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
                appendStringInfo(&buf, "AFTER");
+       else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
+               appendStringInfo(&buf, "INSTEAD OF");
+       else
+               elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
+
        if (TRIGGER_FOR_INSERT(trigrec->tgtype))
        {
                appendStringInfo(&buf, " INSERT");
@@ -504,6 +603,24 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
                        appendStringInfo(&buf, " OR UPDATE");
                else
                        appendStringInfo(&buf, " UPDATE");
+               findx++;
+               /* tgattr is first var-width field, so OK to access directly */
+               if (trigrec->tgattr.dim1 > 0)
+               {
+                       int                     i;
+
+                       appendStringInfoString(&buf, " OF ");
+                       for (i = 0; i < trigrec->tgattr.dim1; i++)
+                       {
+                               char       *attname;
+
+                               if (i > 0)
+                                       appendStringInfoString(&buf, ", ");
+                               attname = get_relid_attribute_name(trigrec->tgrelid,
+                                                                                                  trigrec->tgattr.values[i]);
+                               appendStringInfoString(&buf, quote_identifier(attname));
+                       }
+               }
        }
        if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
        {
@@ -511,15 +628,16 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
                        appendStringInfo(&buf, " OR TRUNCATE");
                else
                        appendStringInfo(&buf, " TRUNCATE");
+               findx++;
        }
        appendStringInfo(&buf, " ON %s ",
-                                        generate_relation_name(trigrec->tgrelid));
+                                        generate_relation_name(trigrec->tgrelid, NIL));
 
-       if (trigrec->tgisconstraint)
+       if (OidIsValid(trigrec->tgconstraint))
        {
-               if (trigrec->tgconstrrelid != InvalidOid)
+               if (OidIsValid(trigrec->tgconstrrelid))
                        appendStringInfo(&buf, "FROM %s ",
-                                                        generate_relation_name(trigrec->tgconstrrelid));
+                                               generate_relation_name(trigrec->tgconstrrelid, NIL));
                if (!trigrec->tgdeferrable)
                        appendStringInfo(&buf, "NOT ");
                appendStringInfo(&buf, "DEFERRABLE INITIALLY ");
@@ -527,7 +645,6 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
                        appendStringInfo(&buf, "DEFERRED ");
                else
                        appendStringInfo(&buf, "IMMEDIATE ");
-
        }
 
        if (TRIGGER_FOR_ROW(trigrec->tgtype))
@@ -535,22 +652,74 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
        else
                appendStringInfo(&buf, "FOR EACH STATEMENT ");
 
+       /* If the trigger has a WHEN qualification, add that */
+       value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
+                                               tgrel->rd_att, &isnull);
+       if (!isnull)
+       {
+               Node       *qual;
+               char            relkind;
+               deparse_context context;
+               deparse_namespace dpns;
+               RangeTblEntry *oldrte;
+               RangeTblEntry *newrte;
+
+               appendStringInfoString(&buf, "WHEN (");
+
+               qual = stringToNode(TextDatumGetCString(value));
+
+               relkind = get_rel_relkind(trigrec->tgrelid);
+
+               /* Build minimal OLD and NEW RTEs for the rel */
+               oldrte = makeNode(RangeTblEntry);
+               oldrte->rtekind = RTE_RELATION;
+               oldrte->relid = trigrec->tgrelid;
+               oldrte->relkind = relkind;
+               oldrte->eref = makeAlias("old", NIL);
+               oldrte->inh = false;
+               oldrte->inFromCl = true;
+
+               newrte = makeNode(RangeTblEntry);
+               newrte->rtekind = RTE_RELATION;
+               newrte->relid = trigrec->tgrelid;
+               newrte->relkind = relkind;
+               newrte->eref = makeAlias("new", NIL);
+               newrte->inh = false;
+               newrte->inFromCl = true;
+
+               /* Build two-element rtable */
+               memset(&dpns, 0, sizeof(dpns));
+               dpns.rtable = list_make2(oldrte, newrte);
+               dpns.ctes = NIL;
+
+               /* Set up context with one-deep namespace stack */
+               context.buf = &buf;
+               context.namespaces = list_make1(&dpns);
+               context.windowClause = NIL;
+               context.windowTList = NIL;
+               context.varprefix = true;
+               context.prettyFlags = pretty ? PRETTYFLAG_PAREN : 0;
+               context.indentLevel = PRETTYINDENT_STD;
+
+               get_rule_expr(qual, &context, false);
+
+               appendStringInfo(&buf, ") ");
+       }
+
        appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
-                                        generate_function_name(trigrec->tgfoid, 0, NULL, NULL));
+                                        generate_function_name(trigrec->tgfoid, 0,
+                                                                                       NIL, NULL, NULL));
 
        if (trigrec->tgnargs > 0)
        {
-               bytea      *val;
-               bool            isnull;
                char       *p;
                int                     i;
 
-               val = DatumGetByteaP(fastgetattr(ht_trig,
-                                                                                Anum_pg_trigger_tgargs,
-                                                                                tgrel->rd_att, &isnull));
+               value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
+                                                       tgrel->rd_att, &isnull);
                if (isnull)
                        elog(ERROR, "tgargs is null for trigger %u", trigid);
-               p = (char *) VARDATA(val);
+               p = (char *) VARDATA(DatumGetByteaP(value));
                for (i = 0; i < trigrec->tgnargs; i++)
                {
                        if (i > 0)
@@ -571,7 +740,7 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
 
        heap_close(tgrel, AccessShareLock);
 
-       PG_RETURN_TEXT_P(string_to_text(buf.data));
+       return buf.data;
 }
 
 /* ----------
@@ -592,7 +761,8 @@ pg_get_indexdef(PG_FUNCTION_ARGS)
        Oid                     indexrelid = PG_GETARG_OID(0);
 
        PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, 0,
-                                                                                                                  false, 0)));
+                                                                                                                  NULL,
+                                                                                                                  false, false, 0)));
 }
 
 Datum
@@ -605,20 +775,43 @@ pg_get_indexdef_ext(PG_FUNCTION_ARGS)
 
        prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
        PG_RETURN_TEXT_P(string_to_text(pg_get_indexdef_worker(indexrelid, colno,
-                                                                                                          false, prettyFlags)));
+                                                                                                                  NULL,
+                                                                                                                  colno != 0,
+                                                                                                                  false,
+                                                                                                                  prettyFlags)));
 }
 
 /* Internal version that returns a palloc'd C string */
 char *
 pg_get_indexdef_string(Oid indexrelid)
 {
-       return pg_get_indexdef_worker(indexrelid, 0, true, 0);
+       return pg_get_indexdef_worker(indexrelid, 0, NULL, false, true, 0);
+}
+
+/* Internal version that just reports the column definitions */
+char *
+pg_get_indexdef_columns(Oid indexrelid, bool pretty)
+{
+       int                     prettyFlags;
+
+       prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
+       return pg_get_indexdef_worker(indexrelid, 0, NULL, true, false, prettyFlags);
 }
 
+/*
+ * Internal workhorse to decompile an index definition.
+ *
+ * This is now used for exclusion constraints as well: if excludeOps is not
+ * NULL then it points to an array of exclusion operator OIDs.
+ */
 static char *
-pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
+pg_get_indexdef_worker(Oid indexrelid, int colno,
+                                          const Oid *excludeOps,
+                                          bool attrsOnly, bool showTblSpc,
                                           int prettyFlags)
 {
+       /* might want a separate isConstraint parameter later */
+       bool            isConstraint = (excludeOps != NULL);
        HeapTuple       ht_idx;
        HeapTuple       ht_idxrel;
        HeapTuple       ht_am;
@@ -630,10 +823,11 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
        List       *context;
        Oid                     indrelid;
        int                     keyno;
-       Oid                     keycoltype;
+       Datum           indcollDatum;
        Datum           indclassDatum;
        Datum           indoptionDatum;
        bool            isnull;
+       oidvector  *indcollation;
        oidvector  *indclass;
        int2vector *indoption;
        StringInfoData buf;
@@ -643,9 +837,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
        /*
         * Fetch the pg_index tuple by the Oid of the index
         */
-       ht_idx = SearchSysCache(INDEXRELID,
-                                                       ObjectIdGetDatum(indexrelid),
-                                                       0, 0, 0);
+       ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
        if (!HeapTupleIsValid(ht_idx))
                elog(ERROR, "cache lookup failed for index %u", indexrelid);
        idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
@@ -653,11 +845,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
        indrelid = idxrec->indrelid;
        Assert(indexrelid == idxrec->indexrelid);
 
-       /* Must get indclass and indoption the hard way */
+       /* Must get indcollation, indclass, and indoption the hard way */
+       indcollDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
+                                                                  Anum_pg_index_indcollation, &isnull);
+       Assert(!isnull);
+       indcollation = (oidvector *) DatumGetPointer(indcollDatum);
+
        indclassDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
                                                                        Anum_pg_index_indclass, &isnull);
        Assert(!isnull);
        indclass = (oidvector *) DatumGetPointer(indclassDatum);
+
        indoptionDatum = SysCacheGetAttr(INDEXRELID, ht_idx,
                                                                         Anum_pg_index_indoption, &isnull);
        Assert(!isnull);
@@ -666,9 +864,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
        /*
         * Fetch the pg_class tuple of the index relation
         */
-       ht_idxrel = SearchSysCache(RELOID,
-                                                          ObjectIdGetDatum(indexrelid),
-                                                          0, 0, 0);
+       ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
        if (!HeapTupleIsValid(ht_idxrel))
                elog(ERROR, "cache lookup failed for relation %u", indexrelid);
        idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
@@ -676,9 +872,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
        /*
         * Fetch the pg_am tuple of the index' access method
         */
-       ht_am = SearchSysCache(AMOID,
-                                                  ObjectIdGetDatum(idxrelrec->relam),
-                                                  0, 0, 0);
+       ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
        if (!HeapTupleIsValid(ht_am))
                elog(ERROR, "cache lookup failed for access method %u",
                         idxrelrec->relam);
@@ -707,7 +901,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
 
        indexpr_item = list_head(indexprs);
 
-       context = deparse_context_for(get_rel_name(indrelid), indrelid);
+       context = deparse_context_for(get_relation_name(indrelid), indrelid);
 
        /*
         * Start the index definition.  Note that the index's name should never be
@@ -715,12 +909,18 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
         */
        initStringInfo(&buf);
 
-       if (!colno)
-               appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
-                                                idxrec->indisunique ? "UNIQUE " : "",
-                                                quote_identifier(NameStr(idxrelrec->relname)),
-                                                generate_relation_name(indrelid),
-                                                quote_identifier(NameStr(amrec->amname)));
+       if (!attrsOnly)
+       {
+               if (!isConstraint)
+                       appendStringInfo(&buf, "CREATE %sINDEX %s ON %s USING %s (",
+                                                        idxrec->indisunique ? "UNIQUE " : "",
+                                                        quote_identifier(NameStr(idxrelrec->relname)),
+                                                        generate_relation_name(indrelid, NIL),
+                                                        quote_identifier(NameStr(amrec->amname)));
+               else    /* currently, must be EXCLUDE constraint */
+                       appendStringInfo(&buf, "EXCLUDE USING %s (",
+                                                        quote_identifier(NameStr(amrec->amname)));
+       }
 
        /*
         * Report the indexed attributes
@@ -730,6 +930,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
        {
                AttrNumber      attnum = idxrec->indkey.values[keyno];
                int16           opt = indoption->values[keyno];
+               Oid                     keycoltype;
+               Oid                     keycolcollation;
 
                if (!colno)
                        appendStringInfoString(&buf, sep);
@@ -739,11 +941,14 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
                {
                        /* Simple index column */
                        char       *attname;
+                       int32           keycoltypmod;
 
                        attname = get_relid_attribute_name(indrelid, attnum);
                        if (!colno || colno == keyno + 1)
                                appendStringInfoString(&buf, quote_identifier(attname));
-                       keycoltype = get_atttype(indrelid, attnum);
+                       get_atttypetypmodcoll(indrelid, attnum,
+                                                                 &keycoltype, &keycoltypmod,
+                                                                 &keycolcollation);
                }
                else
                {
@@ -767,11 +972,19 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
                                        appendStringInfo(&buf, "(%s)", str);
                        }
                        keycoltype = exprType(indexkey);
+                       keycolcollation = exprCollation(indexkey);
                }
 
-               /* Provide decoration only in the colno=0 case */
-               if (!colno)
+               if (!attrsOnly && (!colno || colno == keyno + 1))
                {
+                       Oid                     indcoll;
+
+                       /* Add collation, if not default for column */
+                       indcoll = indcollation->values[keyno];
+                       if (OidIsValid(indcoll) && indcoll != keycolcollation)
+                               appendStringInfo(&buf, " COLLATE %s",
+                                                                generate_collation_name((indcoll)));
+
                        /* Add the operator class name, if not default */
                        get_opclass_name(indclass->values[keyno], keycoltype, &buf);
 
@@ -792,10 +1005,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
                                                appendStringInfo(&buf, " NULLS FIRST");
                                }
                        }
+
+                       /* Add the exclusion operator if relevant */
+                       if (excludeOps != NULL)
+                               appendStringInfo(&buf, " WITH %s",
+                                                                generate_operator_name(excludeOps[keyno],
+                                                                                                               keycoltype,
+                                                                                                               keycoltype));
                }
        }
 
-       if (!colno)
+       if (!attrsOnly)
        {
                appendStringInfoChar(&buf, ')');
 
@@ -818,8 +1038,12 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
 
                        tblspc = get_rel_tablespace(indexrelid);
                        if (OidIsValid(tblspc))
+                       {
+                               if (isConstraint)
+                                       appendStringInfoString(&buf, " USING INDEX");
                                appendStringInfo(&buf, " TABLESPACE %s",
                                                          quote_identifier(get_tablespace_name(tblspc)));
+                       }
                }
 
                /*
@@ -843,7 +1067,10 @@ pg_get_indexdef_worker(Oid indexrelid, int colno, bool showTblSpc,
                        /* Deparse */
                        str = deparse_expression_pretty(node, context, false, false,
                                                                                        prettyFlags, 0);
-                       appendStringInfo(&buf, " WHERE %s", str);
+                       if (isConstraint)
+                               appendStringInfo(&buf, " WHERE (%s)", str);
+                       else
+                               appendStringInfo(&buf, " WHERE %s", str);
                }
        }
 
@@ -898,9 +1125,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
        Form_pg_constraint conForm;
        StringInfoData buf;
 
-       tup = SearchSysCache(CONSTROID,
-                                                ObjectIdGetDatum(constraintId),
-                                                0, 0, 0);
+       tup = SearchSysCache1(CONSTROID, ObjectIdGetDatum(constraintId));
        if (!HeapTupleIsValid(tup)) /* should not happen */
                elog(ERROR, "cache lookup failed for constraint %u", constraintId);
        conForm = (Form_pg_constraint) GETSTRUCT(tup);
@@ -910,7 +1135,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
        if (fullCommand && OidIsValid(conForm->conrelid))
        {
                appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ",
-                                                generate_relation_name(conForm->conrelid),
+                                                generate_relation_name(conForm->conrelid, NIL),
                                                 quote_identifier(NameStr(conForm->conname)));
        }
 
@@ -936,7 +1161,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
                                /* add foreign relation name */
                                appendStringInfo(&buf, ") REFERENCES %s(",
-                                                                generate_relation_name(conForm->confrelid));
+                                                                generate_relation_name(conForm->confrelid,
+                                                                                                               NIL));
 
                                /* Fetch and build referenced-column list */
                                val = SysCacheGetAttr(CONSTROID, tup,
@@ -1022,11 +1248,6 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                                if (string)
                                        appendStringInfo(&buf, " ON DELETE %s", string);
 
-                               if (conForm->condeferrable)
-                                       appendStringInfo(&buf, " DEFERRABLE");
-                               if (conForm->condeferred)
-                                       appendStringInfo(&buf, " INITIALLY DEFERRED");
-
                                break;
                        }
                case CONSTRAINT_PRIMARY:
@@ -1098,7 +1319,7 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                                if (conForm->conrelid != InvalidOid)
                                {
                                        /* relation constraint */
-                                       context = deparse_context_for(get_rel_name(conForm->conrelid),
+                                       context = deparse_context_for(get_relation_name(conForm->conrelid),
                                                                                                  conForm->conrelid);
                                }
                                else
@@ -1111,16 +1332,68 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                                                                                                   prettyFlags, 0);
 
                                /*
-                                * Now emit the constraint definition.  There are cases where
-                                * the constraint expression will be fully parenthesized and
-                                * we don't need the outer parens ... but there are other
-                                * cases where we do need 'em.  Be conservative for now.
+                                * Now emit the constraint definition, adding NO INHERIT if
+                                * necessary.
+                                *
+                                * There are cases where the constraint expression will be
+                                * fully parenthesized and we don't need the outer parens ...
+                                * but there are other cases where we do need 'em.  Be
+                                * conservative for now.
                                 *
                                 * Note that simply checking for leading '(' and trailing ')'
                                 * would NOT be good enough, consider "(x > 0) AND (y > 0)".
                                 */
-                               appendStringInfo(&buf, "CHECK (%s)", consrc);
+                               appendStringInfo(&buf, "CHECK %s(%s)",
+                                                                conForm->connoinherit ? "NO INHERIT " : "",
+                                                                consrc);
+
+                               break;
+                       }
+               case CONSTRAINT_TRIGGER:
+
+                       /*
+                        * There isn't an ALTER TABLE syntax for creating a user-defined
+                        * constraint trigger, but it seems better to print something than
+                        * throw an error; if we throw error then this function couldn't
+                        * safely be applied to all rows of pg_constraint.
+                        */
+                       appendStringInfo(&buf, "TRIGGER");
+                       break;
+               case CONSTRAINT_EXCLUSION:
+                       {
+                               Oid                     indexOid = conForm->conindid;
+                               Datum           val;
+                               bool            isnull;
+                               Datum      *elems;
+                               int                     nElems;
+                               int                     i;
+                               Oid                *operators;
+
+                               /* Extract operator OIDs from the pg_constraint tuple */
+                               val = SysCacheGetAttr(CONSTROID, tup,
+                                                                         Anum_pg_constraint_conexclop,
+                                                                         &isnull);
+                               if (isnull)
+                                       elog(ERROR, "null conexclop for constraint %u",
+                                                constraintId);
 
+                               deconstruct_array(DatumGetArrayTypeP(val),
+                                                                 OIDOID, sizeof(Oid), true, 'i',
+                                                                 &elems, NULL, &nElems);
+
+                               operators = (Oid *) palloc(nElems * sizeof(Oid));
+                               for (i = 0; i < nElems; i++)
+                                       operators[i] = DatumGetObjectId(elems[i]);
+
+                               /* pg_get_indexdef_worker does the rest */
+                               /* suppress tablespace because pg_dump wants it that way */
+                               appendStringInfoString(&buf,
+                                                                          pg_get_indexdef_worker(indexOid,
+                                                                                                                         0,
+                                                                                                                         operators,
+                                                                                                                         false,
+                                                                                                                         false,
+                                                                                                                         prettyFlags));
                                break;
                        }
                default:
@@ -1128,6 +1401,13 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                        break;
        }
 
+       if (conForm->condeferrable)
+               appendStringInfo(&buf, " DEFERRABLE");
+       if (conForm->condeferred)
+               appendStringInfo(&buf, " INITIALLY DEFERRED");
+       if (!conForm->convalidated)
+               appendStringInfoString(&buf, " NOT VALID");
+
        /* Cleanup */
        ReleaseSysCache(tup);
 
@@ -1175,7 +1455,8 @@ decompile_column_index_array(Datum column_index_array, Oid relId,
  *
  * Currently, the expression can only refer to a single relation, namely
  * the one specified by the second parameter.  This is sufficient for
- * partial indexes, column default expressions, etc.
+ * partial indexes, column default expressions, etc.  We also support
+ * Var-free expressions, for which the OID can be InvalidOid.
  * ----------
  */
 Datum
@@ -1185,12 +1466,24 @@ pg_get_expr(PG_FUNCTION_ARGS)
        Oid                     relid = PG_GETARG_OID(1);
        char       *relname;
 
-       /* Get the name for the relation */
-       relname = get_rel_name(relid);
-       if (relname == NULL)
-               PG_RETURN_NULL();               /* should we raise an error? */
+       if (OidIsValid(relid))
+       {
+               /* Get the name for the relation */
+               relname = get_rel_name(relid);
+
+               /*
+                * If the OID isn't actually valid, don't throw an error, just return
+                * NULL.  This is a bit questionable, but it's what we've done
+                * historically, and it can help avoid unwanted failures when
+                * examining catalog entries for just-deleted relations.
+                */
+               if (relname == NULL)
+                       PG_RETURN_NULL();
+       }
+       else
+               relname = NULL;
 
-       PG_RETURN_TEXT_P(string_to_text(pg_get_expr_worker(expr, relid, relname, 0)));
+       PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, 0));
 }
 
 Datum
@@ -1204,16 +1497,22 @@ pg_get_expr_ext(PG_FUNCTION_ARGS)
 
        prettyFlags = pretty ? PRETTYFLAG_PAREN | PRETTYFLAG_INDENT : 0;
 
-       /* Get the name for the relation */
-       relname = get_rel_name(relid);
-       if (relname == NULL)
-               PG_RETURN_NULL();               /* should we raise an error? */
+       if (OidIsValid(relid))
+       {
+               /* Get the name for the relation */
+               relname = get_rel_name(relid);
+               /* See notes above */
+               if (relname == NULL)
+                       PG_RETURN_NULL();
+       }
+       else
+               relname = NULL;
 
-       PG_RETURN_TEXT_P(string_to_text(pg_get_expr_worker(expr, relid, relname, prettyFlags)));
+       PG_RETURN_TEXT_P(pg_get_expr_worker(expr, relid, relname, prettyFlags));
 }
 
-static char *
-pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags)
+static text *
+pg_get_expr_worker(text *expr, Oid relid, const char *relname, int prettyFlags)
 {
        Node       *node;
        List       *context;
@@ -1226,14 +1525,19 @@ pg_get_expr_worker(text *expr, Oid relid, char *relname, int prettyFlags)
        /* Convert expression to node tree */
        node = (Node *) stringToNode(exprstr);
 
+       pfree(exprstr);
+
+       /* Prepare deparse context if needed */
+       if (OidIsValid(relid))
+               context = deparse_context_for(relname, relid);
+       else
+               context = NIL;
+
        /* Deparse */
-       context = deparse_context_for(relname, relid);
        str = deparse_expression_pretty(node, context, false, false,
                                                                        prettyFlags, 0);
 
-       pfree(exprstr);
-
-       return str;
+       return string_to_text(str);
 }
 
 
@@ -1259,9 +1563,7 @@ pg_get_userbyid(PG_FUNCTION_ARGS)
        /*
         * Get the pg_authid entry and print the result
         */
-       roletup = SearchSysCache(AUTHOID,
-                                                        ObjectIdGetDatum(roleid),
-                                                        0, 0, 0);
+       roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
        if (HeapTupleIsValid(roletup))
        {
                role_rec = (Form_pg_authid) GETSTRUCT(roletup);
@@ -1297,9 +1599,9 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
        SysScanDesc scan;
        HeapTuple       tup;
 
-       /* Get the OID of the table */
+       /* Look up table name.  Can't lock it - we might not have privileges. */
        tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
-       tableOid = RangeVarGetRelid(tablerv, false);
+       tableOid = RangeVarGetRelid(tablerv, NoLock, false);
 
        /* Get the number of the column */
        column = text_to_cstring(columnname);
@@ -1360,9 +1662,7 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
                char       *result;
 
                /* Get the sequence's pg_class entry */
-               classtup = SearchSysCache(RELOID,
-                                                                 ObjectIdGetDatum(sequenceId),
-                                                                 0, 0, 0);
+               classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(sequenceId));
                if (!HeapTupleIsValid(classtup))
                        elog(ERROR, "cache lookup failed for relation %u", sequenceId);
                classtuple = (Form_pg_class) GETSTRUCT(classtup);
@@ -1388,8 +1688,13 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
 
 /*
  * pg_get_functiondef
- *             Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
- *             the specified function.
+ *             Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
+ *             the specified function.
+ *
+ * Note: if you change the output format of this function, be careful not
+ * to break psql's rules (in \ef and \sf) for identifying the start of the
+ * function body.  To wit: the function body starts on a line that begins
+ * with "AS ", and no preceding line will look like that.
  */
 Datum
 pg_get_functiondef(PG_FUNCTION_ARGS)
@@ -1412,9 +1717,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        initStringInfo(&buf);
 
        /* Look up the function */
-       proctup = SearchSysCache(PROCOID,
-                                                        ObjectIdGetDatum(funcid),
-                                                        0, 0, 0);
+       proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(proctup))
                elog(ERROR, "cache lookup failed for function %u", funcid);
        proc = (Form_pg_proc) GETSTRUCT(proctup);
@@ -1426,21 +1729,19 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
                                 errmsg("\"%s\" is an aggregate function", name)));
 
        /* Need its pg_language tuple for the language name */
-       langtup = SearchSysCache(LANGOID,
-                                                        ObjectIdGetDatum(proc->prolang),
-                                                        0, 0, 0);
+       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.
+        * We always qualify the function name, to ensure the right function gets
+        * replaced.
         */
        nsp = get_namespace_name(proc->pronamespace);
        appendStringInfo(&buf, "CREATE OR REPLACE FUNCTION %s(",
                                         quote_qualified_identifier(nsp, name));
-       (void) print_function_arguments(&buf, proctup, false);
+       (void) print_function_arguments(&buf, proctup, false, true);
        appendStringInfoString(&buf, ")\n RETURNS ");
        print_function_rettype(&buf, proctup);
        appendStringInfo(&buf, "\n LANGUAGE %s\n",
@@ -1449,6 +1750,8 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        /* Emit some miscellaneous options on one line */
        oldlen = buf.len;
 
+       if (proc->proiswindow)
+               appendStringInfoString(&buf, " WINDOW");
        switch (proc->provolatile)
        {
                case PROVOLATILE_IMMUTABLE:
@@ -1484,7 +1787,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
        if (!isnull)
        {
-               ArrayType       *a = DatumGetArrayTypeP(tmp);
+               ArrayType  *a = DatumGetArrayTypeP(tmp);
                int                     i;
 
                Assert(ARR_ELEMTYPE(a) == TEXTOID);
@@ -1493,7 +1796,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
 
                for (i = 1; i <= ARR_DIMS(a)[0]; i++)
                {
-                       Datum   d;
+                       Datum           d;
 
                        d = array_ref(a, 1, &i,
                                                  -1 /* varlenarray */ ,
@@ -1546,9 +1849,9 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        /*
         * We always use dollar quoting.  Figure out a suitable delimiter.
         *
-        * Since the user is likely to be editing the function body string,
-        * we shouldn't use a short delimiter that he might easily create a
-        * conflict with.  Hence prefer "$function$", but extend if needed.
+        * Since the user is likely to be editing the function body string, we
+        * shouldn't use a short delimiter that he might easily create a conflict
+        * with.  Hence prefer "$function$", but extend if needed.
         */
        initStringInfo(&dq);
        appendStringInfoString(&dq, "$function");
@@ -1583,13 +1886,37 @@ pg_get_function_arguments(PG_FUNCTION_ARGS)
 
        initStringInfo(&buf);
 
-       proctup = SearchSysCache(PROCOID,
-                                                        ObjectIdGetDatum(funcid),
-                                                        0, 0, 0);
+       proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
+       if (!HeapTupleIsValid(proctup))
+               elog(ERROR, "cache lookup failed for function %u", funcid);
+
+       (void) print_function_arguments(&buf, proctup, false, true);
+
+       ReleaseSysCache(proctup);
+
+       PG_RETURN_TEXT_P(string_to_text(buf.data));
+}
+
+/*
+ * pg_get_function_identity_arguments
+ *             Get a formatted list of arguments for a function.
+ *             This is everything that would go between the parentheses in
+ *             ALTER FUNCTION, etc.  In particular, don't print defaults.
+ */
+Datum
+pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
+{
+       Oid                     funcid = PG_GETARG_OID(0);
+       StringInfoData buf;
+       HeapTuple       proctup;
+
+       initStringInfo(&buf);
+
+       proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(proctup))
                elog(ERROR, "cache lookup failed for function %u", funcid);
 
-       (void) print_function_arguments(&buf, proctup, false);
+       (void) print_function_arguments(&buf, proctup, false, false);
 
        ReleaseSysCache(proctup);
 
@@ -1610,9 +1937,7 @@ pg_get_function_result(PG_FUNCTION_ARGS)
 
        initStringInfo(&buf);
 
-       proctup = SearchSysCache(PROCOID,
-                                                        ObjectIdGetDatum(funcid),
-                                                        0, 0, 0);
+       proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(proctup))
                elog(ERROR, "cache lookup failed for function %u", funcid);
 
@@ -1640,7 +1965,7 @@ print_function_rettype(StringInfo buf, HeapTuple proctup)
        {
                /* It might be a table function; try to print the arguments */
                appendStringInfoString(&rbuf, "TABLE(");
-               ntabargs = print_function_arguments(&rbuf, proctup, true);
+               ntabargs = print_function_arguments(&rbuf, proctup, true, false);
                if (ntabargs > 0)
                        appendStringInfoString(&rbuf, ")");
                else
@@ -1662,61 +1987,112 @@ print_function_rettype(StringInfo buf, HeapTuple proctup)
  * Common code for pg_get_function_arguments and pg_get_function_result:
  * append the desired subset of arguments to buf.  We print only TABLE
  * arguments when print_table_args is true, and all the others when it's false.
+ * We print argument defaults only if print_defaults is true.
  * Function return value is the number of arguments printed.
  */
 static int
 print_function_arguments(StringInfo buf, HeapTuple proctup,
-                                                bool print_table_args)
+                                                bool print_table_args, bool print_defaults)
 {
+       Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
        int                     numargs;
        Oid                *argtypes;
        char      **argnames;
        char       *argmodes;
        int                     argsprinted;
+       int                     inputargno;
+       int                     nlackdefaults;
+       ListCell   *nextargdefault = NULL;
        int                     i;
 
        numargs = get_func_arg_info(proctup,
                                                                &argtypes, &argnames, &argmodes);
 
+       nlackdefaults = numargs;
+       if (print_defaults && proc->pronargdefaults > 0)
+       {
+               Datum           proargdefaults;
+               bool            isnull;
+
+               proargdefaults = SysCacheGetAttr(PROCOID, proctup,
+                                                                                Anum_pg_proc_proargdefaults,
+                                                                                &isnull);
+               if (!isnull)
+               {
+                       char       *str;
+                       List       *argdefaults;
+
+                       str = TextDatumGetCString(proargdefaults);
+                       argdefaults = (List *) stringToNode(str);
+                       Assert(IsA(argdefaults, List));
+                       pfree(str);
+                       nextargdefault = list_head(argdefaults);
+                       /* nlackdefaults counts only *input* arguments lacking defaults */
+                       nlackdefaults = proc->pronargs - list_length(argdefaults);
+               }
+       }
+
        argsprinted = 0;
+       inputargno = 0;
        for (i = 0; i < numargs; i++)
        {
-               Oid             argtype = argtypes[i];
-               char   *argname = argnames ? argnames[i] : NULL;
-               char    argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
+               Oid                     argtype = argtypes[i];
+               char       *argname = argnames ? argnames[i] : NULL;
+               char            argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
                const char *modename;
-
-               if (print_table_args != (argmode == PROARGMODE_TABLE))
-                       continue;
+               bool            isinput;
 
                switch (argmode)
                {
                        case PROARGMODE_IN:
                                modename = "";
+                               isinput = true;
                                break;
                        case PROARGMODE_INOUT:
                                modename = "INOUT ";
+                               isinput = true;
                                break;
                        case PROARGMODE_OUT:
                                modename = "OUT ";
+                               isinput = false;
                                break;
                        case PROARGMODE_VARIADIC:
                                modename = "VARIADIC ";
+                               isinput = true;
                                break;
                        case PROARGMODE_TABLE:
                                modename = "";
+                               isinput = false;
                                break;
                        default:
                                elog(ERROR, "invalid parameter mode '%c'", argmode);
-                               modename = NULL; /* keep compiler quiet */
+                               modename = NULL;        /* keep compiler quiet */
+                               isinput = false;
                                break;
                }
+               if (isinput)
+                       inputargno++;           /* this is a 1-based counter */
+
+               if (print_table_args != (argmode == PROARGMODE_TABLE))
+                       continue;
+
                if (argsprinted)
                        appendStringInfoString(buf, ", ");
                appendStringInfoString(buf, modename);
                if (argname && argname[0])
-                       appendStringInfo(buf, "%s ", argname);
+                       appendStringInfo(buf, "%s ", quote_identifier(argname));
                appendStringInfoString(buf, format_type_be(argtype));
+               if (print_defaults && isinput && inputargno > nlackdefaults)
+               {
+                       Node       *expr;
+
+                       Assert(nextargdefault != NULL);
+                       expr = (Node *) lfirst(nextargdefault);
+                       nextargdefault = lnext(nextargdefault);
+
+                       appendStringInfo(buf, " DEFAULT %s",
+                                                        deparse_expression(expr, NIL, false, false));
+               }
                argsprinted++;
        }
 
@@ -1766,6 +2142,8 @@ deparse_expression_pretty(Node *expr, List *dpcontext,
        initStringInfo(&buf);
        context.buf = &buf;
        context.namespaces = dpcontext;
+       context.windowClause = NIL;
+       context.windowTList = NIL;
        context.varprefix = forceprefix;
        context.prettyFlags = prettyFlags;
        context.indentLevel = startIndent;
@@ -1789,56 +2167,221 @@ deparse_context_for(const char *aliasname, Oid relid)
        deparse_namespace *dpns;
        RangeTblEntry *rte;
 
-       dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+       dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
 
        /* Build a minimal RTE for the rel */
        rte = makeNode(RangeTblEntry);
        rte->rtekind = RTE_RELATION;
        rte->relid = relid;
+       rte->relkind = RELKIND_RELATION;        /* no need for exactness here */
        rte->eref = makeAlias(aliasname, NIL);
        rte->inh = false;
        rte->inFromCl = true;
 
        /* Build one-element rtable */
        dpns->rtable = list_make1(rte);
-       dpns->outer_plan = dpns->inner_plan = NULL;
+       dpns->ctes = NIL;
 
        /* Return a one-deep namespace stack */
        return list_make1(dpns);
 }
 
 /*
- * deparse_context_for_plan            - Build deparse context for a plan node
+ * deparse_context_for_planstate       - Build deparse context for a plan
  *
  * When deparsing an expression in a Plan tree, we might have to resolve
- * OUTER or INNER references.  Pass the plan nodes whose targetlists define
- * such references, or NULL when none are expected.  (outer_plan and
- * inner_plan really ought to be declared as "Plan *", but we use "Node *"
- * to avoid having to include plannodes.h in builtins.h.)
+ * 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.)
  *
- * As a special case, when deparsing a SubqueryScan plan, pass the subplan
- * as inner_plan (there won't be any regular innerPlan() in this case).
+ * Note: planstate really ought to be declared as "PlanState *", but we use
+ * "Node *" to avoid having to include execnodes.h in builtins.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.
  *
  * The plan's rangetable list must also be passed.  We actually prefer to use
- * the rangetable to resolve simple Vars, but the subplan inputs are needed
- * for Vars that reference expressions computed in subplan target lists.
+ * the rangetable to resolve simple Vars, but the plan inputs are necessary
+ * for Vars with special varnos.
  */
 List *
-deparse_context_for_plan(Node *outer_plan, Node *inner_plan,
-                                                List *rtable)
+deparse_context_for_planstate(Node *planstate, List *ancestors,
+                                                         List *rtable)
 {
        deparse_namespace *dpns;
 
-       dpns = (deparse_namespace *) palloc(sizeof(deparse_namespace));
+       dpns = (deparse_namespace *) palloc0(sizeof(deparse_namespace));
 
+       /* Initialize fields that stay the same across the whole plan tree */
        dpns->rtable = rtable;
-       dpns->outer_plan = (Plan *) outer_plan;
-       dpns->inner_plan = (Plan *) inner_plan;
+       dpns->ctes = NIL;
+
+       /* 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);
 }
 
+/*
+ * set_deparse_planstate: set up deparse_namespace to parse subexpressions
+ * of a given PlanState node
+ *
+ * This sets the planstate, outer_planstate, inner_planstate, outer_tlist,
+ * inner_tlist, and index_tlist fields.  Caller is responsible for adjusting
+ * the ancestors list if necessary.  Note that the rtable and ctes fields do
+ * not need to change when shifting attention to different plan nodes in a
+ * single plan tree.
+ */
+static void
+set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
+{
+       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
+        * first child plan is the OUTER referent; this is to support RETURNING
+        * lists containing references to non-target relations.
+        */
+       if (IsA(ps, AppendState))
+               dpns->outer_planstate = ((AppendState *) ps)->appendplans[0];
+       else if (IsA(ps, MergeAppendState))
+               dpns->outer_planstate = ((MergeAppendState *) ps)->mergeplans[0];
+       else if (IsA(ps, ModifyTableState))
+               dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0];
+       else
+               dpns->outer_planstate = outerPlanState(ps);
+
+       if (dpns->outer_planstate)
+               dpns->outer_tlist = dpns->outer_planstate->plan->targetlist;
+       else
+               dpns->outer_tlist = NIL;
+
+       /*
+        * 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.
+        */
+       if (IsA(ps, SubqueryScanState))
+               dpns->inner_planstate = ((SubqueryScanState *) ps)->subplan;
+       else if (IsA(ps, CteScanState))
+               dpns->inner_planstate = ((CteScanState *) ps)->cteplanstate;
+       else
+               dpns->inner_planstate = innerPlanState(ps);
+
+       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 */
+       if (IsA(ps->plan, IndexOnlyScan))
+               dpns->index_tlist = ((IndexOnlyScan *) ps->plan)->indextlist;
+       else
+               dpns->index_tlist = NIL;
+}
+
+/*
+ * push_child_plan: temporarily transfer deparsing attention to a child plan
+ *
+ * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
+ * deparse context in case the referenced expression itself uses
+ * OUTER_VAR/INNER_VAR.  We modify the top stack entry in-place to avoid
+ * affecting levelsup issues (although in a Plan tree there really shouldn't
+ * be any).
+ *
+ * Caller must provide a local deparse_namespace variable to save the
+ * previous state for pop_child_plan.
+ */
+static void
+push_child_plan(deparse_namespace *dpns, PlanState *ps,
+                               deparse_namespace *save_dpns)
+{
+       /* Save state for restoration later */
+       *save_dpns = *dpns;
+
+       /*
+        * Currently we don't bother to adjust the ancestors list, because an
+        * OUTER_VAR or INNER_VAR reference really shouldn't contain any Params
+        * that would be set by the parent node itself.  If we did want to adjust
+        * the list, lcons'ing dpns->planstate onto dpns->ancestors would be the
+        * appropriate thing --- and pop_child_plan would need to undo the change
+        * to the list.
+        */
+
+       /* Set attention on selected child */
+       set_deparse_planstate(dpns, ps);
+}
+
+/*
+ * pop_child_plan: undo the effects of push_child_plan
+ */
+static void
+pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
+{
+       /* Restore fields changed by push_child_plan */
+       *dpns = *save_dpns;
+}
+
+/*
+ * push_ancestor_plan: temporarily transfer deparsing attention to an
+ * ancestor plan
+ *
+ * When expanding a Param reference, we must adjust the deparse context
+ * to match the plan node that contains the expression being printed;
+ * otherwise we'd fail if that expression itself contains a Param or
+ * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
+ *
+ * The target ancestor is conveniently identified by the ListCell holding it
+ * in dpns->ancestors.
+ *
+ * Caller must provide a local deparse_namespace variable to save the
+ * previous state for pop_ancestor_plan.
+ */
+static void
+push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
+                                  deparse_namespace *save_dpns)
+{
+       PlanState  *ps = (PlanState *) lfirst(ancestor_cell);
+       List       *ancestors;
+
+       /* Save state for restoration later */
+       *save_dpns = *dpns;
+
+       /* Build a new ancestor list with just this node's ancestors */
+       ancestors = NIL;
+       while ((ancestor_cell = lnext(ancestor_cell)) != NULL)
+               ancestors = lappend(ancestors, lfirst(ancestor_cell));
+       dpns->ancestors = ancestors;
+
+       /* Set attention on selected ancestor */
+       set_deparse_planstate(dpns, ps);
+}
+
+/*
+ * pop_ancestor_plan: undo the effects of push_ancestor_plan
+ */
+static void
+pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
+{
+       /* Free the ancestor list made in push_ancestor_plan */
+       list_free(dpns->ancestors);
+
+       /* Restore fields changed by push_ancestor_plan */
+       *dpns = *save_dpns;
+}
+
+
 /* ----------
  * make_ruledef                        - reconstruct the CREATE RULE command
  *                               for a given pg_rewrite tuple
@@ -1936,7 +2479,7 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
        }
 
        /* The relation the rule is fired on */
-       appendStringInfo(buf, " TO %s", generate_relation_name(ev_class));
+       appendStringInfo(buf, " TO %s", generate_relation_name(ev_class, NIL));
        if (ev_attr > 0)
                appendStringInfo(buf, ".%s",
                                                 quote_identifier(get_relid_attribute_name(ev_class,
@@ -1973,15 +2516,19 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                query = getInsertSelectQuery(query, NULL);
 
                /* Must acquire locks right away; see notes in get_query_def() */
-               AcquireRewriteLocks(query);
+               AcquireRewriteLocks(query, false);
 
                context.buf = buf;
                context.namespaces = list_make1(&dpns);
+               context.windowClause = NIL;
+               context.windowTList = NIL;
                context.varprefix = (list_length(query->rtable) != 1);
                context.prettyFlags = prettyFlags;
                context.indentLevel = PRETTYINDENT_STD;
+
+               memset(&dpns, 0, sizeof(dpns));
                dpns.rtable = query->rtable;
-               dpns.outer_plan = dpns.inner_plan = NULL;
+               dpns.ctes = query->cteList;
 
                get_rule_expr(qual, &context, false);
        }
@@ -2114,17 +2661,20 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
         * consistent results.  Note we assume it's OK to scribble on the passed
         * querytree!
         */
-       AcquireRewriteLocks(query);
+       AcquireRewriteLocks(query, false);
 
        context.buf = buf;
        context.namespaces = lcons(&dpns, list_copy(parentnamespace));
+       context.windowClause = NIL;
+       context.windowTList = NIL;
        context.varprefix = (parentnamespace != NIL ||
                                                 list_length(query->rtable) != 1);
        context.prettyFlags = prettyFlags;
        context.indentLevel = startIndent;
 
+       memset(&dpns, 0, sizeof(dpns));
        dpns.rtable = query->rtable;
-       dpns.outer_plan = dpns.inner_plan = NULL;
+       dpns.ctes = query->cteList;
 
        switch (query->commandType)
        {
@@ -2205,83 +2755,120 @@ get_values_def(List *values_lists, deparse_context *context)
 }
 
 /* ----------
- * get_select_query_def                        - Parse back a SELECT parsetree
+ * get_with_clause                     - Parse back a WITH clause
  * ----------
  */
 static void
-get_select_query_def(Query *query, deparse_context *context,
-                                        TupleDesc resultDesc)
+get_with_clause(Query *query, deparse_context *context)
 {
        StringInfo      buf = context->buf;
-       bool            force_colno;
-       char       *sep;
+       const char *sep;
        ListCell   *l;
 
-       /*
-        * If the Query node has a setOperations tree, then it's the top level of
-        * a UNION/INTERSECT/EXCEPT query; only the ORDER BY and LIMIT fields are
-        * interesting in the top query itself.
-        */
-       if (query->setOperations)
+       if (query->cteList == NIL)
+               return;
+
+       if (PRETTY_INDENT(context))
        {
-               get_setop_query(query->setOperations, query, context, resultDesc);
-               /* ORDER BY clauses must be simple in this case */
-               force_colno = true;
+               context->indentLevel += PRETTYINDENT_STD;
+               appendStringInfoChar(buf, ' ');
        }
+
+       if (query->hasRecursive)
+               sep = "WITH RECURSIVE ";
        else
+               sep = "WITH ";
+       foreach(l, query->cteList)
        {
-               get_basic_select_query(query, context, resultDesc);
-               force_colno = false;
-       }
+               CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
 
-       /* Add the ORDER BY clause if given */
-       if (query->sortClause != NIL)
-       {
-               appendContextKeyword(context, " ORDER BY ",
-                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
-               sep = "";
-               foreach(l, query->sortClause)
+               appendStringInfoString(buf, sep);
+               appendStringInfoString(buf, quote_identifier(cte->ctename));
+               if (cte->aliascolnames)
                {
-                       SortGroupClause *srt = (SortGroupClause *) lfirst(l);
-                       Node       *sortexpr;
-                       Oid                     sortcoltype;
-                       TypeCacheEntry *typentry;
+                       bool            first = true;
+                       ListCell   *col;
 
-                       appendStringInfoString(buf, sep);
-                       sortexpr = get_rule_sortgroupclause(srt, query->targetList,
-                                                                                               force_colno, context);
-                       sortcoltype = exprType(sortexpr);
-                       /* See whether operator is default < or > for datatype */
-                       typentry = lookup_type_cache(sortcoltype,
-                                                                                TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
-                       if (srt->sortop == typentry->lt_opr)
-                       {
-                               /* ASC is default, so emit nothing for it */
-                               if (srt->nulls_first)
-                                       appendStringInfo(buf, " NULLS FIRST");
-                       }
-                       else if (srt->sortop == typentry->gt_opr)
-                       {
-                               appendStringInfo(buf, " DESC");
-                               /* DESC defaults to NULLS FIRST */
-                               if (!srt->nulls_first)
-                                       appendStringInfo(buf, " NULLS LAST");
-                       }
-                       else
+                       appendStringInfoChar(buf, '(');
+                       foreach(col, cte->aliascolnames)
                        {
-                               appendStringInfo(buf, " USING %s",
-                                                                generate_operator_name(srt->sortop,
-                                                                                                               sortcoltype,
-                                                                                                               sortcoltype));
-                               /* be specific to eliminate ambiguity */
-                               if (srt->nulls_first)
-                                       appendStringInfo(buf, " NULLS FIRST");
+                               if (first)
+                                       first = false;
                                else
-                                       appendStringInfo(buf, " NULLS LAST");
+                                       appendStringInfoString(buf, ", ");
+                               appendStringInfoString(buf,
+                                                                          quote_identifier(strVal(lfirst(col))));
                        }
-                       sep = ", ";
+                       appendStringInfoChar(buf, ')');
                }
-       }
+               appendStringInfoString(buf, " AS (");
+               if (PRETTY_INDENT(context))
+                       appendContextKeyword(context, "", 0, 0, 0);
+               get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
+                                         context->prettyFlags, context->indentLevel);
+               if (PRETTY_INDENT(context))
+                       appendContextKeyword(context, "", 0, 0, 0);
+               appendStringInfoChar(buf, ')');
+               sep = ", ";
+       }
+
+       if (PRETTY_INDENT(context))
+       {
+               context->indentLevel -= PRETTYINDENT_STD;
+               appendContextKeyword(context, "", 0, 0, 0);
+       }
+       else
+               appendStringInfoChar(buf, ' ');
+}
+
+/* ----------
+ * get_select_query_def                        - Parse back a SELECT parsetree
+ * ----------
+ */
+static void
+get_select_query_def(Query *query, deparse_context *context,
+                                        TupleDesc resultDesc)
+{
+       StringInfo      buf = context->buf;
+       List       *save_windowclause;
+       List       *save_windowtlist;
+       bool            force_colno;
+       ListCell   *l;
+
+       /* Insert the WITH clause if given */
+       get_with_clause(query, context);
+
+       /* Set up context for possible window functions */
+       save_windowclause = context->windowClause;
+       context->windowClause = query->windowClause;
+       save_windowtlist = context->windowTList;
+       context->windowTList = query->targetList;
+
+       /*
+        * If the Query node has a setOperations tree, then it's the top level of
+        * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
+        * fields are interesting in the top query itself.
+        */
+       if (query->setOperations)
+       {
+               get_setop_query(query->setOperations, query, context, resultDesc);
+               /* ORDER BY clauses must be simple in this case */
+               force_colno = true;
+       }
+       else
+       {
+               get_basic_select_query(query, context, resultDesc);
+               force_colno = false;
+       }
+
+       /* Add the ORDER BY clause if given */
+       if (query->sortClause != NIL)
+       {
+               appendContextKeyword(context, " ORDER BY ",
+                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+               get_rule_orderby(query->sortClause, query->targetList,
+                                                force_colno, context);
+       }
 
        /* Add the LIMIT clause if given */
        if (query->limitOffset != NULL)
@@ -2302,22 +2889,32 @@ get_select_query_def(Query *query, deparse_context *context,
        }
 
        /* Add FOR UPDATE/SHARE clauses if present */
-       foreach(l, query->rowMarks)
+       if (query->hasForUpdate)
        {
-               RowMarkClause *rc = (RowMarkClause *) lfirst(l);
-               RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable);
+               foreach(l, query->rowMarks)
+               {
+                       RowMarkClause *rc = (RowMarkClause *) lfirst(l);
+                       RangeTblEntry *rte = rt_fetch(rc->rti, query->rtable);
 
-               if (rc->forUpdate)
-                       appendContextKeyword(context, " FOR UPDATE",
-                                                                -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
-               else
-                       appendContextKeyword(context, " FOR SHARE",
-                                                                -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
-               appendStringInfo(buf, " OF %s",
-                                                quote_identifier(rte->eref->aliasname));
-               if (rc->noWait)
-                       appendStringInfo(buf, " NOWAIT");
+                       /* don't print implicit clauses */
+                       if (rc->pushedDown)
+                               continue;
+
+                       if (rc->forUpdate)
+                               appendContextKeyword(context, " FOR UPDATE",
+                                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+                       else
+                               appendContextKeyword(context, " FOR SHARE",
+                                                                        -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
+                       appendStringInfo(buf, " OF %s",
+                                                        quote_identifier(rte->eref->aliasname));
+                       if (rc->noWait)
+                               appendStringInfo(buf, " NOWAIT");
+               }
        }
+
+       context->windowClause = save_windowclause;
+       context->windowTList = save_windowtlist;
 }
 
 static void
@@ -2421,6 +3018,10 @@ get_basic_select_query(Query *query, deparse_context *context,
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
                get_rule_expr(query->havingQual, context, false);
        }
+
+       /* Add the WINDOW clause if needed */
+       if (query->windowClause != NIL)
+               get_rule_windowclause(query, context);
 }
 
 /* ----------
@@ -2437,6 +3038,7 @@ get_target_list(List *targetList, deparse_context *context,
        char       *sep;
        int                     colno;
        ListCell   *l;
+       bool            last_was_multiline = false;
 
        sep = " ";
        colno = 0;
@@ -2445,6 +3047,10 @@ get_target_list(List *targetList, deparse_context *context,
                TargetEntry *tle = (TargetEntry *) lfirst(l);
                char       *colname;
                char       *attname;
+               StringInfoData targetbuf;
+               int                     leading_nl_pos = -1;
+               char       *trailing_nl;
+               int                     pos;
 
                if (tle->resjunk)
                        continue;                       /* ignore junk entries */
@@ -2453,17 +3059,26 @@ get_target_list(List *targetList, deparse_context *context,
                sep = ", ";
                colno++;
 
+               /*
+                * Put the new field spec into targetbuf so we can decide after we've
+                * got it whether or not it needs to go on a new line.
+                */
+
+               initStringInfo(&targetbuf);
+               context->buf = &targetbuf;
+
                /*
                 * We special-case Var nodes rather than using get_rule_expr. This is
                 * needed because get_rule_expr will display a whole-row Var as
                 * "foo.*", which is the preferred notation in most contexts, but at
                 * 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 want just "foo", instead.
+                * different from a whole-row Var).  We need to call get_variable
+                * directly so that we can tell it to do the right thing.
                 */
                if (tle->expr && IsA(tle->expr, Var))
                {
-                       attname = get_variable((Var *) tle->expr, 0, false, context);
+                       attname = get_variable((Var *) tle->expr, 0, true, context);
                }
                else
                {
@@ -2487,8 +3102,65 @@ get_target_list(List *targetList, deparse_context *context,
                if (colname)                    /* resname could be NULL */
                {
                        if (attname == NULL || strcmp(attname, colname) != 0)
-                               appendStringInfo(buf, " AS %s", quote_identifier(colname));
+                               appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
+               }
+
+               /* Restore context buffer */
+
+               context->buf = buf;
+
+               /* Does the new field start with whitespace plus a new line? */
+
+               for (pos = 0; pos < targetbuf.len; pos++)
+               {
+                       if (targetbuf.data[pos] == '\n')
+                       {
+                               leading_nl_pos = pos;
+                               break;
+                       }
+                       if (targetbuf.data[pos] > ' ')
+                               break;
                }
+
+               /* Locate the start of the current      line in the buffer */
+
+               trailing_nl = (strrchr(buf->data, '\n'));
+               if (trailing_nl == NULL)
+                       trailing_nl = buf->data;
+               else
+                       trailing_nl++;
+
+               /*
+                * If the field we're adding is the first in the list, or it already
+                * has a leading newline, or wrap mode is disabled (pretty_wrap < 0),
+                * don't add anything. Otherwise, add a newline, plus some
+                * indentation, if either the new field would cause an overflow or the
+                * last field used more than one line.
+                */
+
+               if (colno > 1 &&
+                       leading_nl_pos == -1 &&
+                       pretty_wrap >= 0 &&
+                       ((strlen(trailing_nl) + strlen(targetbuf.data) > pretty_wrap) ||
+                        last_was_multiline))
+               {
+                       appendContextKeyword(context, "", -PRETTYINDENT_STD,
+                                                                PRETTYINDENT_STD, PRETTYINDENT_VAR);
+               }
+
+               /* Add the new field */
+
+               appendStringInfoString(buf, targetbuf.data);
+
+
+               /* Keep track of this field's status for next iteration */
+
+               last_was_multiline =
+                       (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
+
+               /* cleanup */
+
+               pfree(targetbuf.data);
        }
 }
 
@@ -2507,8 +3179,9 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
 
                Assert(subquery != NULL);
                Assert(subquery->setOperations == NULL);
-               /* Need parens if ORDER BY, FOR UPDATE, or LIMIT; see gram.y */
-               need_paren = (subquery->sortClause ||
+               /* Need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y */
+               need_paren = (subquery->cteList ||
+                                         subquery->sortClause ||
                                          subquery->rowMarks ||
                                          subquery->limitOffset ||
                                          subquery->limitCount);
@@ -2523,6 +3196,12 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
        {
                SetOperationStmt *op = (SetOperationStmt *) setOp;
 
+               if (PRETTY_INDENT(context))
+               {
+                       context->indentLevel += PRETTYINDENT_STD;
+                       appendStringInfoSpaces(buf, PRETTYINDENT_STD);
+               }
+
                /*
                 * We force parens whenever nesting two SetOperationStmts. There are
                 * some cases in which parens are needed around a leaf query too, but
@@ -2570,6 +3249,9 @@ get_setop_query(Node *setOp, Query *query, deparse_context *context,
                get_setop_query(op->rarg, query, context, resultDesc);
                if (need_paren)
                        appendStringInfoChar(buf, ')');
+
+               if (PRETTY_INDENT(context))
+                       context->indentLevel -= PRETTYINDENT_STD;
        }
        else
        {
@@ -2596,11 +3278,11 @@ get_rule_sortgroupclause(SortGroupClause *srt, List *tlist, bool force_colno,
 
        /*
         * Use column-number form if requested by caller.  Otherwise, if
-        * expression is a constant, force it to be dumped with an explicit
-        * cast as decoration --- this is because a simple integer constant
-        * is ambiguous (and will be misinterpreted by findTargetlistEntry())
-        * if we dump it without any decoration.  Otherwise, just dump the
-        * expression normally.
+        * 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.
         */
        if (force_colno)
        {
@@ -2615,6 +3297,196 @@ get_rule_sortgroupclause(SortGroupClause *srt, List *tlist, bool force_colno,
        return expr;
 }
 
+/*
+ * 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, targetList,
+                                                                                       force_colno, context);
+               sortcoltype = exprType(sortexpr);
+               /* See whether operator is default < or > for datatype */
+               typentry = lookup_type_cache(sortcoltype,
+                                                                        TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
+               if (srt->sortop == typentry->lt_opr)
+               {
+                       /* ASC is default, so emit nothing for it */
+                       if (srt->nulls_first)
+                               appendStringInfo(buf, " NULLS FIRST");
+               }
+               else if (srt->sortop == typentry->gt_opr)
+               {
+                       appendStringInfo(buf, " DESC");
+                       /* DESC defaults to NULLS FIRST */
+                       if (!srt->nulls_first)
+                               appendStringInfo(buf, " NULLS LAST");
+               }
+               else
+               {
+                       appendStringInfo(buf, " USING %s",
+                                                        generate_operator_name(srt->sortop,
+                                                                                                       sortcoltype,
+                                                                                                       sortcoltype));
+                       /* be specific to eliminate ambiguity */
+                       if (srt->nulls_first)
+                               appendStringInfo(buf, " NULLS FIRST");
+                       else
+                               appendStringInfo(buf, " NULLS LAST");
+               }
+               sep = ", ";
+       }
+}
+
+/*
+ * Display a WINDOW clause.
+ *
+ * Note that the windowClause list might contain only anonymous window
+ * specifications, in which case we should print nothing here.
+ */
+static void
+get_rule_windowclause(Query *query, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+       const char *sep;
+       ListCell   *l;
+
+       sep = NULL;
+       foreach(l, query->windowClause)
+       {
+               WindowClause *wc = (WindowClause *) lfirst(l);
+
+               if (wc->name == NULL)
+                       continue;                       /* ignore anonymous windows */
+
+               if (sep == NULL)
+                       appendContextKeyword(context, " WINDOW ",
+                                                                -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
+               else
+                       appendStringInfoString(buf, sep);
+
+               appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
+
+               get_rule_windowspec(wc, query->targetList, context);
+
+               sep = ", ";
+       }
+}
+
+/*
+ * Display a window definition
+ */
+static void
+get_rule_windowspec(WindowClause *wc, List *targetList,
+                                       deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+       bool            needspace = false;
+       const char *sep;
+       ListCell   *l;
+
+       appendStringInfoChar(buf, '(');
+       if (wc->refname)
+       {
+               appendStringInfoString(buf, quote_identifier(wc->refname));
+               needspace = true;
+       }
+       /* partition clauses are always inherited, so only print if no refname */
+       if (wc->partitionClause && !wc->refname)
+       {
+               if (needspace)
+                       appendStringInfoChar(buf, ' ');
+               appendStringInfoString(buf, "PARTITION BY ");
+               sep = "";
+               foreach(l, wc->partitionClause)
+               {
+                       SortGroupClause *grp = (SortGroupClause *) lfirst(l);
+
+                       appendStringInfoString(buf, sep);
+                       get_rule_sortgroupclause(grp, targetList,
+                                                                        false, context);
+                       sep = ", ";
+               }
+               needspace = true;
+       }
+       /* print ordering clause only if not inherited */
+       if (wc->orderClause && !wc->copiedOrder)
+       {
+               if (needspace)
+                       appendStringInfoChar(buf, ' ');
+               appendStringInfoString(buf, "ORDER BY ");
+               get_rule_orderby(wc->orderClause, targetList, false, context);
+               needspace = true;
+       }
+       /* framing clause is never inherited, so print unless it's default */
+       if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
+       {
+               if (needspace)
+                       appendStringInfoChar(buf, ' ');
+               if (wc->frameOptions & FRAMEOPTION_RANGE)
+                       appendStringInfoString(buf, "RANGE ");
+               else if (wc->frameOptions & FRAMEOPTION_ROWS)
+                       appendStringInfoString(buf, "ROWS ");
+               else
+                       Assert(false);
+               if (wc->frameOptions & FRAMEOPTION_BETWEEN)
+                       appendStringInfoString(buf, "BETWEEN ");
+               if (wc->frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
+                       appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
+               else if (wc->frameOptions & FRAMEOPTION_START_CURRENT_ROW)
+                       appendStringInfoString(buf, "CURRENT ROW ");
+               else if (wc->frameOptions & FRAMEOPTION_START_VALUE)
+               {
+                       get_rule_expr(wc->startOffset, context, false);
+                       if (wc->frameOptions & FRAMEOPTION_START_VALUE_PRECEDING)
+                               appendStringInfoString(buf, " PRECEDING ");
+                       else if (wc->frameOptions & FRAMEOPTION_START_VALUE_FOLLOWING)
+                               appendStringInfoString(buf, " FOLLOWING ");
+                       else
+                               Assert(false);
+               }
+               else
+                       Assert(false);
+               if (wc->frameOptions & FRAMEOPTION_BETWEEN)
+               {
+                       appendStringInfoString(buf, "AND ");
+                       if (wc->frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
+                               appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
+                       else if (wc->frameOptions & FRAMEOPTION_END_CURRENT_ROW)
+                               appendStringInfoString(buf, "CURRENT ROW ");
+                       else if (wc->frameOptions & FRAMEOPTION_END_VALUE)
+                       {
+                               get_rule_expr(wc->endOffset, context, false);
+                               if (wc->frameOptions & FRAMEOPTION_END_VALUE_PRECEDING)
+                                       appendStringInfoString(buf, " PRECEDING ");
+                               else if (wc->frameOptions & FRAMEOPTION_END_VALUE_FOLLOWING)
+                                       appendStringInfoString(buf, " FOLLOWING ");
+                               else
+                                       Assert(false);
+                       }
+                       else
+                               Assert(false);
+               }
+               /* we will now have a trailing space; remove it */
+               buf->len--;
+       }
+       appendStringInfoChar(buf, ')');
+}
+
 /* ----------
  * get_insert_query_def                        - Parse back an INSERT parsetree
  * ----------
@@ -2631,6 +3503,9 @@ get_insert_query_def(Query *query, deparse_context *context)
        ListCell   *l;
        List       *strippedexprs;
 
+       /* Insert the WITH clause if given */
+       get_with_clause(query, context);
+
        /*
         * If it's an INSERT ... SELECT or VALUES (...), (...), ... there will be
         * a single RTE for the SELECT or VALUES.
@@ -2668,7 +3543,7 @@ get_insert_query_def(Query *query, deparse_context *context)
                appendStringInfoChar(buf, ' ');
        }
        appendStringInfo(buf, "INSERT INTO %s (",
-                                        generate_relation_name(rte->relid));
+                                        generate_relation_name(rte->relid, NIL));
 
        /*
         * Add the insert-column-names list.  To handle indirection properly, we
@@ -2764,6 +3639,9 @@ get_update_query_def(Query *query, deparse_context *context)
        RangeTblEntry *rte;
        ListCell   *l;
 
+       /* Insert the WITH clause if given */
+       get_with_clause(query, context);
+
        /*
         * Start the query with UPDATE relname SET
         */
@@ -2776,7 +3654,7 @@ get_update_query_def(Query *query, deparse_context *context)
        }
        appendStringInfo(buf, "UPDATE %s%s",
                                         only_marker(rte),
-                                        generate_relation_name(rte->relid));
+                                        generate_relation_name(rte->relid, NIL));
        if (rte->alias != NULL)
                appendStringInfo(buf, " %s",
                                                 quote_identifier(rte->alias->aliasname));
@@ -2845,6 +3723,9 @@ get_delete_query_def(Query *query, deparse_context *context)
        StringInfo      buf = context->buf;
        RangeTblEntry *rte;
 
+       /* Insert the WITH clause if given */
+       get_with_clause(query, context);
+
        /*
         * Start the query with DELETE FROM relname
         */
@@ -2857,7 +3738,7 @@ get_delete_query_def(Query *query, deparse_context *context)
        }
        appendStringInfo(buf, "DELETE FROM %s%s",
                                         only_marker(rte),
-                                        generate_relation_name(rte->relid));
+                                        generate_relation_name(rte->relid, NIL));
        if (rte->alias != NULL)
                appendStringInfo(buf, " %s",
                                                 quote_identifier(rte->alias->aliasname));
@@ -2900,6 +3781,11 @@ get_utility_query_def(Query *query, deparse_context *context)
                                                         0, PRETTYINDENT_STD, 1);
                appendStringInfo(buf, "NOTIFY %s",
                                                 quote_identifier(stmt->conditionname));
+               if (stmt->payload)
+               {
+                       appendStringInfoString(buf, ", ");
+                       simple_quote_literal(buf, stmt->payload);
+               }
        }
        else
        {
@@ -2909,39 +3795,6 @@ get_utility_query_def(Query *query, deparse_context *context)
 }
 
 
-/*
- * push_plan: set up deparse_namespace to recurse into the tlist of a subplan
- *
- * When expanding an OUTER or INNER reference, we must push new outer/inner
- * subplans in case the referenced expression itself uses OUTER/INNER. We
- * modify the top stack entry in-place to avoid affecting levelsup issues
- * (although in a Plan tree there really shouldn't be any).
- *
- * Caller must save and restore outer_plan and inner_plan around this.
- */
-static void
-push_plan(deparse_namespace *dpns, Plan *subplan)
-{
-       /*
-        * We special-case Append to pretend that the first child plan is the
-        * OUTER referent; otherwise normal.
-        */
-       if (IsA(subplan, Append))
-               dpns->outer_plan = (Plan *) linitial(((Append *) subplan)->appendplans);
-       else
-               dpns->outer_plan = outerPlan(subplan);
-
-       /*
-        * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
-        * use OUTER because that could someday conflict with the normal meaning.)
-        */
-       if (IsA(subplan, SubqueryScan))
-               dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
-       else
-               dpns->inner_plan = innerPlan(subplan);
-}
-
-
 /*
  * Display a Var appropriately.
  *
@@ -2949,13 +3802,20 @@ push_plan(deparse_namespace *dpns, Plan *subplan)
  * the Var's varlevelsup has to be interpreted with respect to a context
  * above the current one; levelsup indicates the offset.
  *
- * If showstar is TRUE, whole-row Vars are displayed as "foo.*";
- * if FALSE, merely as "foo".
+ * If istoplevel is TRUE, the Var is at the top level of a SELECT's
+ * targetlist, which means we need special treatment of whole-row Vars.
+ * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
+ * dirty hack to prevent "tab.*" from being expanded into multiple columns.
+ * (The parser will strip the useless coercion, so no inefficiency is added in
+ * dump and reload.)  We used to print just "tab" in such cases, but that is
+ * ambiguous and will yield the wrong result if "tab" is also a plain column
+ * name in the query.
  *
- * Returns the attname of the Var, or NULL if not determinable.
+ * Returns the attname of the Var, or NULL if the Var has no attname (because
+ * it is a whole-row Var).
  */
 static char *
-get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
+get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        RangeTblEntry *rte;
@@ -2976,28 +3836,25 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
 
        /*
         * Try to find the relevant RTE in this rtable.  In a plan tree, it's
-        * likely that varno is OUTER or INNER, in which case we must dig down
-        * into the subplans.
+        * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
+        * down into the subplans, or INDEX_VAR, which is resolved similarly.
         */
        if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
        {
                rte = rt_fetch(var->varno, dpns->rtable);
                attnum = var->varattno;
        }
-       else if (var->varno == OUTER && dpns->outer_plan)
+       else if (var->varno == OUTER_VAR && dpns->outer_tlist)
        {
                TargetEntry *tle;
-               Plan       *save_outer;
-               Plan       *save_inner;
+               deparse_namespace save_dpns;
 
-               tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
+               tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
                if (!tle)
-                       elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
+                       elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
 
                Assert(netlevelsup == 0);
-               save_outer = dpns->outer_plan;
-               save_inner = dpns->inner_plan;
-               push_plan(dpns, dpns->outer_plan);
+               push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
 
                /*
                 * Force parentheses because our caller probably assumed a Var is a
@@ -3009,24 +3866,43 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
                if (!IsA(tle->expr, Var))
                        appendStringInfoChar(buf, ')');
 
-               dpns->outer_plan = save_outer;
-               dpns->inner_plan = save_inner;
+               pop_child_plan(dpns, &save_dpns);
                return NULL;
        }
-       else if (var->varno == INNER && dpns->inner_plan)
+       else if (var->varno == INNER_VAR && dpns->inner_tlist)
        {
                TargetEntry *tle;
-               Plan       *save_outer;
-               Plan       *save_inner;
+               deparse_namespace save_dpns;
 
-               tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
+               tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
                if (!tle)
-                       elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
+                       elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
+
+               Assert(netlevelsup == 0);
+               push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+
+               /*
+                * Force parentheses because our caller probably assumed a Var is a
+                * simple expression.
+                */
+               if (!IsA(tle->expr, Var))
+                       appendStringInfoChar(buf, '(');
+               get_rule_expr((Node *) tle->expr, context, true);
+               if (!IsA(tle->expr, Var))
+                       appendStringInfoChar(buf, ')');
+
+               pop_child_plan(dpns, &save_dpns);
+               return NULL;
+       }
+       else if (var->varno == INDEX_VAR && dpns->index_tlist)
+       {
+               TargetEntry *tle;
+
+               tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
+               if (!tle)
+                       elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
 
                Assert(netlevelsup == 0);
-               save_outer = dpns->outer_plan;
-               save_inner = dpns->inner_plan;
-               push_plan(dpns, dpns->inner_plan);
 
                /*
                 * Force parentheses because our caller probably assumed a Var is a
@@ -3038,8 +3914,6 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
                if (!IsA(tle->expr, Var))
                        appendStringInfoChar(buf, ')');
 
-               dpns->outer_plan = save_outer;
-               dpns->inner_plan = save_inner;
                return NULL;
        }
        else
@@ -3048,6 +3922,45 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
                return NULL;                    /* keep compiler quiet */
        }
 
+       /*
+        * The planner will sometimes emit Vars referencing resjunk elements of a
+        * subquery's target list (this is currently only possible if it chooses
+        * to generate a "physical tlist" for a SubqueryScan or CteScan node).
+        * Although we prefer to print subquery-referencing Vars using the
+        * subquery's alias, that's not possible for resjunk items since they have
+        * no alias.  So in that case, drill down to the subplan and print the
+        * contents of the referenced tlist item.  This works because in a plan
+        * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
+        * we'll have set dpns->inner_planstate to reference the child plan node.
+        */
+       if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
+               attnum > list_length(rte->eref->colnames) &&
+               dpns->inner_planstate)
+       {
+               TargetEntry *tle;
+               deparse_namespace save_dpns;
+
+               tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
+               if (!tle)
+                       elog(ERROR, "bogus varattno for subquery var: %d", var->varattno);
+
+               Assert(netlevelsup == 0);
+               push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+
+               /*
+                * Force parentheses because our caller probably assumed a Var is a
+                * simple expression.
+                */
+               if (!IsA(tle->expr, Var))
+                       appendStringInfoChar(buf, '(');
+               get_rule_expr((Node *) tle->expr, context, true);
+               if (!IsA(tle->expr, Var))
+                       appendStringInfoChar(buf, ')');
+
+               pop_child_plan(dpns, &save_dpns);
+               return NULL;
+       }
+
        /* Identify names to use */
        schemaname = NULL;                      /* default assumptions */
        refname = rte->eref->aliasname;
@@ -3091,10 +4004,16 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
                                if (IsA(aliasvar, Var))
                                {
                                        return get_variable(aliasvar, var->varlevelsup + levelsup,
-                                                                               showstar, context);
+                                                                               istoplevel, context);
                                }
                        }
-                       /* Unnamed join has neither schemaname nor refname */
+
+                       /*
+                        * Unnamed join has neither schemaname nor 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.)
+                        */
                        refname = NULL;
                }
        }
@@ -3109,36 +4028,40 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
                if (schemaname)
                        appendStringInfo(buf, "%s.",
                                                         quote_identifier(schemaname));
-
-               if (strcmp(refname, "*NEW*") == 0)
-                       appendStringInfoString(buf, "new");
-               else if (strcmp(refname, "*OLD*") == 0)
-                       appendStringInfoString(buf, "old");
-               else
-                       appendStringInfoString(buf, quote_identifier(refname));
-
-               if (attname || showstar)
-                       appendStringInfoChar(buf, '.');
+               appendStringInfoString(buf, quote_identifier(refname));
+               appendStringInfoChar(buf, '.');
        }
        if (attname)
                appendStringInfoString(buf, quote_identifier(attname));
-       else if (showstar)
+       else
+       {
                appendStringInfoChar(buf, '*');
+               if (istoplevel)
+                       appendStringInfo(buf, "::%s",
+                                                        format_type_with_typemod(var->vartype,
+                                                                                                         var->vartypmod));
+       }
 
        return attname;
 }
 
 
 /*
- * Get the name of a field of an expression of composite type.
- *
- * This is fairly straightforward except for the case of a Var of type RECORD.
- * Since no actual table or view column is allowed to have type RECORD, such
- * a Var 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 can't determine the name.
+ * 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.
+ *
+ * This is fairly straightforward when the expression has a named composite
+ * type; we need only look up the type in the catalogs.  However, the type
+ * 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
+ * can't determine the name.
+ *
+ * Similarly, a PARAM of type RECORD has to refer to some expression of
+ * a determinable composite type.
  */
 static const char *
 get_name_for_var_field(Var *var, int fieldno,
@@ -3151,6 +4074,41 @@ get_name_for_var_field(Var *var, int fieldno,
        TupleDesc       tupleDesc;
        Node       *expr;
 
+       /*
+        * If it's a RowExpr that was expanded from a whole-row Var, use the
+        * column names attached to it.
+        */
+       if (IsA(var, RowExpr))
+       {
+               RowExpr    *r = (RowExpr *) var;
+
+               if (fieldno > 0 && fieldno <= list_length(r->colnames))
+                       return strVal(list_nth(r->colnames, fieldno - 1));
+       }
+
+       /*
+        * If it's a Param of type RECORD, try to find what the Param refers to.
+        */
+       if (IsA(var, Param))
+       {
+               Param      *param = (Param *) var;
+               ListCell   *ancestor_cell;
+
+               expr = find_param_referent(param, context, &dpns, &ancestor_cell);
+               if (expr)
+               {
+                       /* Found a match, so recurse to decipher the field name */
+                       deparse_namespace save_dpns;
+                       const char *result;
+
+                       push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+                       result = get_name_for_var_field((Var *) expr, fieldno,
+                                                                                       0, context);
+                       pop_ancestor_plan(dpns, &save_dpns);
+                       return result;
+               }
+       }
+
        /*
         * If it's a Var of type RECORD, we have to find what the Var refers to;
         * if not, we can use get_expr_result_type. If that fails, we try
@@ -3179,58 +4137,66 @@ get_name_for_var_field(Var *var, int fieldno,
 
        /*
         * Try to find the relevant RTE in this rtable.  In a plan tree, it's
-        * likely that varno is OUTER or INNER, in which case we must dig down
-        * into the subplans.
+        * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
+        * down into the subplans, or INDEX_VAR, which is resolved similarly.
         */
        if (var->varno >= 1 && var->varno <= list_length(dpns->rtable))
        {
                rte = rt_fetch(var->varno, dpns->rtable);
                attnum = var->varattno;
        }
-       else if (var->varno == OUTER && dpns->outer_plan)
+       else if (var->varno == OUTER_VAR && dpns->outer_tlist)
        {
                TargetEntry *tle;
-               Plan       *save_outer;
-               Plan       *save_inner;
+               deparse_namespace save_dpns;
                const char *result;
 
-               tle = get_tle_by_resno(dpns->outer_plan->targetlist, var->varattno);
+               tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
                if (!tle)
-                       elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
+                       elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
 
                Assert(netlevelsup == 0);
-               save_outer = dpns->outer_plan;
-               save_inner = dpns->inner_plan;
-               push_plan(dpns, dpns->outer_plan);
+               push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
 
                result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                                                                levelsup, context);
 
-               dpns->outer_plan = save_outer;
-               dpns->inner_plan = save_inner;
+               pop_child_plan(dpns, &save_dpns);
                return result;
        }
-       else if (var->varno == INNER && dpns->inner_plan)
+       else if (var->varno == INNER_VAR && dpns->inner_tlist)
        {
                TargetEntry *tle;
-               Plan       *save_outer;
-               Plan       *save_inner;
+               deparse_namespace save_dpns;
                const char *result;
 
-               tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
+               tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
                if (!tle)
-                       elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
+                       elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
+
+               Assert(netlevelsup == 0);
+               push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+
+               result = get_name_for_var_field((Var *) tle->expr, fieldno,
+                                                                               levelsup, context);
+
+               pop_child_plan(dpns, &save_dpns);
+               return result;
+       }
+       else if (var->varno == INDEX_VAR && dpns->index_tlist)
+       {
+               TargetEntry *tle;
+               const char *result;
+
+               tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
+               if (!tle)
+                       elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
 
                Assert(netlevelsup == 0);
-               save_outer = dpns->outer_plan;
-               save_inner = dpns->inner_plan;
-               push_plan(dpns, dpns->inner_plan);
 
                result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                                                                levelsup, context);
 
-               dpns->outer_plan = save_outer;
-               dpns->inner_plan = save_inner;
                return result;
        }
        else
@@ -3249,15 +4215,14 @@ get_name_for_var_field(Var *var, int fieldno,
         * This part has essentially the same logic as the parser's
         * expandRecordVariable() function, but we are dealing with a different
         * representation of the input context, and we only need one field name
-        * not a TupleDesc.  Also, we need a special case for deparsing Plan
-        * trees, because the subquery field has been removed from SUBQUERY RTEs.
+        * not a TupleDesc.  Also, we need special cases for finding subquery and
+        * CTE subplans when deparsing Plan trees.
         */
        expr = (Node *) var;            /* default if we can't drill down */
 
        switch (rte->rtekind)
        {
                case RTE_RELATION:
-               case RTE_SPECIAL:
                case RTE_VALUES:
 
                        /*
@@ -3267,10 +4232,10 @@ get_name_for_var_field(Var *var, int fieldno,
                         */
                        break;
                case RTE_SUBQUERY:
+                       /* Subselect-in-FROM: examine sub-select's output expr */
                        {
                                if (rte->subquery)
                                {
-                                       /* Subselect-in-FROM: examine sub-select's output expr */
                                        TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
                                                                                                                attnum);
 
@@ -3289,8 +4254,9 @@ get_name_for_var_field(Var *var, int fieldno,
                                                deparse_namespace mydpns;
                                                const char *result;
 
+                                               memset(&mydpns, 0, sizeof(mydpns));
                                                mydpns.rtable = rte->subquery->rtable;
-                                               mydpns.outer_plan = mydpns.inner_plan = NULL;
+                                               mydpns.ctes = rte->subquery->cteList;
 
                                                context->namespaces = lcons(&mydpns,
                                                                                                        context->namespaces);
@@ -3309,34 +4275,29 @@ get_name_for_var_field(Var *var, int fieldno,
                                {
                                        /*
                                         * We're deparsing a Plan tree so we don't have complete
-                                        * RTE entries.  But the only place we'd see a Var
-                                        * directly referencing a SUBQUERY RTE is in a
-                                        * SubqueryScan plan node, and we can look into the child
-                                        * plan's tlist instead.
+                                        * RTE entries (in particular, rte->subquery is NULL). But
+                                        * the only place we'd see a Var directly referencing a
+                                        * SUBQUERY RTE is in a SubqueryScan plan node, and we can
+                                        * look into the child plan's tlist instead.
                                         */
                                        TargetEntry *tle;
-                                       Plan       *save_outer;
-                                       Plan       *save_inner;
+                                       deparse_namespace save_dpns;
                                        const char *result;
 
-                                       if (!dpns->inner_plan)
+                                       if (!dpns->inner_planstate)
                                                elog(ERROR, "failed to find plan for subquery %s",
                                                         rte->eref->aliasname);
-                                       tle = get_tle_by_resno(dpns->inner_plan->targetlist,
-                                                                                  attnum);
+                                       tle = get_tle_by_resno(dpns->inner_tlist, attnum);
                                        if (!tle)
                                                elog(ERROR, "bogus varattno for subquery var: %d",
                                                         attnum);
                                        Assert(netlevelsup == 0);
-                                       save_outer = dpns->outer_plan;
-                                       save_inner = dpns->inner_plan;
-                                       push_plan(dpns, dpns->inner_plan);
+                                       push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
 
                                        result = get_name_for_var_field((Var *) tle->expr, fieldno,
                                                                                                        levelsup, context);
 
-                                       dpns->outer_plan = save_outer;
-                                       dpns->inner_plan = save_inner;
+                                       pop_child_plan(dpns, &save_dpns);
                                        return result;
                                }
                        }
@@ -3360,6 +4321,103 @@ get_name_for_var_field(Var *var, int fieldno,
                         * its result columns as RECORD, which is not allowed.
                         */
                        break;
+               case RTE_CTE:
+                       /* CTE reference: examine subquery's output expr */
+                       {
+                               CommonTableExpr *cte = NULL;
+                               Index           ctelevelsup;
+                               ListCell   *lc;
+
+                               /*
+                                * Try to find the referenced CTE using the namespace stack.
+                                */
+                               ctelevelsup = rte->ctelevelsup + netlevelsup;
+                               if (ctelevelsup >= list_length(context->namespaces))
+                                       lc = NULL;
+                               else
+                               {
+                                       deparse_namespace *ctedpns;
+
+                                       ctedpns = (deparse_namespace *)
+                                               list_nth(context->namespaces, ctelevelsup);
+                                       foreach(lc, ctedpns->ctes)
+                                       {
+                                               cte = (CommonTableExpr *) lfirst(lc);
+                                               if (strcmp(cte->ctename, rte->ctename) == 0)
+                                                       break;
+                                       }
+                               }
+                               if (lc != NULL)
+                               {
+                                       Query      *ctequery = (Query *) cte->ctequery;
+                                       TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
+                                                                                                               attnum);
+
+                                       if (ste == NULL || ste->resjunk)
+                                               elog(ERROR, "subquery %s does not have attribute %d",
+                                                        rte->eref->aliasname, attnum);
+                                       expr = (Node *) ste->expr;
+                                       if (IsA(expr, Var))
+                                       {
+                                               /*
+                                                * Recurse into the CTE to see what its Var refers to.
+                                                * We have to build an additional level of namespace
+                                                * to keep in step with varlevelsup in the CTE.
+                                                * Furthermore it could be an outer CTE, so we may
+                                                * have to delete some levels of namespace.
+                                                */
+                                               List       *save_nslist = context->namespaces;
+                                               List       *new_nslist;
+                                               deparse_namespace mydpns;
+                                               const char *result;
+
+                                               memset(&mydpns, 0, sizeof(mydpns));
+                                               mydpns.rtable = ctequery->rtable;
+                                               mydpns.ctes = ctequery->cteList;
+
+                                               new_nslist = list_copy_tail(context->namespaces,
+                                                                                                       ctelevelsup);
+                                               context->namespaces = lcons(&mydpns, new_nslist);
+
+                                               result = get_name_for_var_field((Var *) expr, fieldno,
+                                                                                                               0, context);
+
+                                               context->namespaces = save_nslist;
+
+                                               return result;
+                                       }
+                                       /* else fall through to inspect the expression */
+                               }
+                               else
+                               {
+                                       /*
+                                        * We're deparsing a Plan tree so we don't have a CTE
+                                        * list.  But the only place we'd see a Var directly
+                                        * referencing a CTE RTE is in a CteScan plan node, and we
+                                        * can look into the subplan's tlist instead.
+                                        */
+                                       TargetEntry *tle;
+                                       deparse_namespace save_dpns;
+                                       const char *result;
+
+                                       if (!dpns->inner_planstate)
+                                               elog(ERROR, "failed to find plan for CTE %s",
+                                                        rte->eref->aliasname);
+                                       tle = get_tle_by_resno(dpns->inner_tlist, attnum);
+                                       if (!tle)
+                                               elog(ERROR, "bogus varattno for subquery var: %d",
+                                                        attnum);
+                                       Assert(netlevelsup == 0);
+                                       push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
+
+                                       result = get_name_for_var_field((Var *) tle->expr, fieldno,
+                                                                                                       levelsup, context);
+
+                                       pop_child_plan(dpns, &save_dpns);
+                                       return result;
+                               }
+                       }
+                       break;
        }
 
        /*
@@ -3401,23 +4459,210 @@ find_rte_by_refname(const char *refname, deparse_context *context)
                deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
                ListCell   *rtlist;
 
-               foreach(rtlist, dpns->rtable)
-               {
-                       RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtlist);
+               foreach(rtlist, dpns->rtable)
+               {
+                       RangeTblEntry *rte = (RangeTblEntry *) lfirst(rtlist);
+
+                       if (strcmp(rte->eref->aliasname, refname) == 0)
+                       {
+                               if (result)
+                                       return NULL;    /* it's ambiguous */
+                               result = rte;
+                       }
+               }
+               if (result)
+                       break;
+       }
+       return result;
+}
+
+/*
+ * Try to find the referenced expression for a PARAM_EXEC Param that might
+ * 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
+ * found, return NULL.
+ */
+static Node *
+find_param_referent(Param *param, deparse_context *context,
+                                       deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
+{
+       /* Initialize output parameters to prevent compiler warnings */
+       *dpns_p = NULL;
+       *ancestor_cell_p = NULL;
+
+       /*
+        * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
+        * SubPlan argument.  This will necessarily be in some ancestor of the
+        * current expression's PlanState.
+        */
+       if (param->paramkind == PARAM_EXEC)
+       {
+               deparse_namespace *dpns;
+               PlanState  *child_ps;
+               bool            in_same_plan_level;
+               ListCell   *lc;
+
+               dpns = (deparse_namespace *) linitial(context->namespaces);
+               child_ps = dpns->planstate;
+               in_same_plan_level = true;
+
+               foreach(lc, dpns->ancestors)
+               {
+                       PlanState  *ps = (PlanState *) lfirst(lc);
+                       ListCell   *lc2;
+
+                       /*
+                        * NestLoops transmit params to their inner child only; also, once
+                        * we've crawled up out of a subplan, this couldn't possibly be
+                        * the right match.
+                        */
+                       if (IsA(ps, NestLoopState) &&
+                               child_ps == innerPlanState(ps) &&
+                               in_same_plan_level)
+                       {
+                               NestLoop   *nl = (NestLoop *) ps->plan;
+
+                               foreach(lc2, nl->nestParams)
+                               {
+                                       NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
+
+                                       if (nlp->paramno == param->paramid)
+                                       {
+                                               /* Found a match, so return it */
+                                               *dpns_p = dpns;
+                                               *ancestor_cell_p = lc;
+                                               return (Node *) nlp->paramval;
+                                       }
+                               }
+                       }
+
+                       /*
+                        * Check to see if we're crawling up from a subplan.
+                        */
+                       foreach(lc2, ps->subPlan)
+                       {
+                               SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+                               SubPlan    *subplan = (SubPlan *) sstate->xprstate.expr;
+                               ListCell   *lc3;
+                               ListCell   *lc4;
+
+                               if (child_ps != sstate->planstate)
+                                       continue;
+
+                               /* Matched subplan, so check its arguments */
+                               forboth(lc3, subplan->parParam, lc4, subplan->args)
+                               {
+                                       int                     paramid = lfirst_int(lc3);
+                                       Node       *arg = (Node *) lfirst(lc4);
+
+                                       if (paramid == param->paramid)
+                                       {
+                                               /* Found a match, so return it */
+                                               *dpns_p = dpns;
+                                               *ancestor_cell_p = lc;
+                                               return arg;
+                                       }
+                               }
+
+                               /* Keep looking, but we are emerging from a subplan. */
+                               in_same_plan_level = false;
+                               break;
+                       }
+
+                       /*
+                        * Likewise check to see if we're emerging from an initplan.
+                        * Initplans never have any parParams, so no need to search that
+                        * list, but we need to know if we should reset
+                        * in_same_plan_level.
+                        */
+                       foreach(lc2, ps->initPlan)
+                       {
+                               SubPlanState *sstate = (SubPlanState *) lfirst(lc2);
+
+                               if (child_ps != sstate->planstate)
+                                       continue;
+
+                               /* No parameters to be had here. */
+                               Assert(((SubPlan *) sstate->xprstate.expr)->parParam == NIL);
+
+                               /* Keep looking, but we are emerging from an initplan. */
+                               in_same_plan_level = false;
+                               break;
+                       }
+
+                       /* No luck, crawl up to next ancestor */
+                       child_ps = ps;
+               }
+       }
+
+       /* No referent found */
+       return NULL;
+}
+
+/*
+ * Display a Param appropriately.
+ */
+static void
+get_parameter(Param *param, deparse_context *context)
+{
+       Node       *expr;
+       deparse_namespace *dpns;
+       ListCell   *ancestor_cell;
+
+       /*
+        * 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
+        * an error, since the Param might well be a subplan output rather than an
+        * input.
+        */
+       expr = find_param_referent(param, context, &dpns, &ancestor_cell);
+       if (expr)
+       {
+               /* Found a match, so print it */
+               deparse_namespace save_dpns;
+               bool            save_varprefix;
+               bool            need_paren;
+
+               /* Switch attention to the ancestor plan node */
+               push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+
+               /*
+                * Force prefixing of Vars, since they won't belong to the relation
+                * being scanned in the original plan node.
+                */
+               save_varprefix = context->varprefix;
+               context->varprefix = true;
+
+               /*
+                * A Param's expansion is typically a Var, Aggref, or upper-level
+                * Param, which wouldn't need extra parentheses.  Otherwise, insert
+                * parens to ensure the expression looks atomic.
+                */
+               need_paren = !(IsA(expr, Var) ||
+                                          IsA(expr, Aggref) ||
+                                          IsA(expr, Param));
+               if (need_paren)
+                       appendStringInfoChar(context->buf, '(');
+
+               get_rule_expr(expr, context, false);
+
+               if (need_paren)
+                       appendStringInfoChar(context->buf, ')');
+
+               context->varprefix = save_varprefix;
 
-                       if (strcmp(rte->eref->aliasname, refname) == 0)
-                       {
-                               if (result)
-                                       return NULL;    /* it's ambiguous */
-                               result = rte;
-                       }
-               }
-               if (result)
-                       break;
+               pop_ancestor_plan(dpns, &save_dpns);
+
+               return;
        }
-       return result;
-}
 
+       /*
+        * Not PARAM_EXEC, or couldn't find referent: just print $N.
+        */
+       appendStringInfo(context->buf, "$%d", param->paramid);
+}
 
 /*
  * get_simple_binary_op_name
@@ -3476,6 +4721,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                case T_XmlExpr:
                case T_NullIfExpr:
                case T_Aggref:
+               case T_WindowFunc:
                case T_FuncExpr:
                        /* function-like: name(..) or name[..] */
                        return true;
@@ -3591,6 +4837,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                                case T_XmlExpr: /* own parentheses */
                                case T_NullIfExpr:              /* other separators */
                                case T_Aggref:  /* own parentheses */
+                               case T_WindowFunc:              /* own parentheses */
                                case T_CaseExpr:                /* other separators */
                                        return true;
                                default:
@@ -3640,6 +4887,7 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
                                case T_XmlExpr: /* own parentheses */
                                case T_NullIfExpr:              /* other separators */
                                case T_Aggref:  /* own parentheses */
+                               case T_WindowFunc:              /* own parentheses */
                                case T_CaseExpr:                /* other separators */
                                        return true;
                                default:
@@ -3654,16 +4902,6 @@ isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
 }
 
 
-/*
- * appendStringInfoSpaces - append spaces to buffer
- */
-static void
-appendStringInfoSpaces(StringInfo buf, int count)
-{
-       while (count-- > 0)
-               appendStringInfoChar(buf, ' ');
-}
-
 /*
  * appendContextKeyword - append a keyword to buffer
  *
@@ -3753,7 +4991,7 @@ get_rule_expr(Node *node, deparse_context *context,
        switch (nodeTag(node))
        {
                case T_Var:
-                       (void) get_variable((Var *) node, 0, true, context);
+                       (void) get_variable((Var *) node, 0, false, context);
                        break;
 
                case T_Const:
@@ -3761,18 +4999,38 @@ get_rule_expr(Node *node, deparse_context *context,
                        break;
 
                case T_Param:
-                       appendStringInfo(buf, "$%d", ((Param *) node)->paramid);
+                       get_parameter((Param *) node, context);
                        break;
 
                case T_Aggref:
                        get_agg_expr((Aggref *) node, context);
                        break;
 
+               case T_WindowFunc:
+                       get_windowfunc_expr((WindowFunc *) node, context);
+                       break;
+
                case T_ArrayRef:
                        {
                                ArrayRef   *aref = (ArrayRef *) node;
                                bool            need_parens;
 
+                               /*
+                                * If the argument is a CaseTestExpr, we must be inside a
+                                * FieldStore, ie, we are assigning to an element of an array
+                                * within a composite column.  Since we already punted on
+                                * displaying the FieldStore's target information, just punt
+                                * here too, and display only the assignment source
+                                * expression.
+                                */
+                               if (IsA(aref->refexpr, CaseTestExpr))
+                               {
+                                       Assert(aref->refassgnexpr);
+                                       get_rule_expr((Node *) aref->refassgnexpr,
+                                                                 context, showimplicit);
+                                       break;
+                               }
+
                                /*
                                 * Parenthesize the argument unless it's a simple Var or a
                                 * FieldSelect.  (In particular, if it's another ArrayRef, we
@@ -3785,14 +5043,35 @@ get_rule_expr(Node *node, deparse_context *context,
                                get_rule_expr((Node *) aref->refexpr, context, showimplicit);
                                if (need_parens)
                                        appendStringInfoChar(buf, ')');
-                               printSubscripts(aref, context);
 
                                /*
-                                * Array assignment nodes should have been handled in
-                                * processIndirection().
+                                * If there's a refassgnexpr, we want to print the node in the
+                                * 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
+                                * EXPLAIN tries to print the targetlist of a plan resulting
+                                * from such a statement.
                                 */
                                if (aref->refassgnexpr)
-                                       elog(ERROR, "unexpected refassgnexpr");
+                               {
+                                       Node       *refassgnexpr;
+
+                                       /*
+                                        * Use processIndirection to print this node's subscripts
+                                        * as well as any additional field selections or
+                                        * subscripting in immediate descendants.  It returns the
+                                        * RHS expr that is actually being "assigned".
+                                        */
+                                       refassgnexpr = processIndirection(node, context, true);
+                                       appendStringInfoString(buf, " := ");
+                                       get_rule_expr(refassgnexpr, context, showimplicit);
+                               }
+                               else
+                               {
+                                       /* Just an ordinary array fetch, so print subscripts */
+                                       printSubscripts(aref, context);
+                               }
                        }
                        break;
 
@@ -3800,6 +5079,15 @@ get_rule_expr(Node *node, deparse_context *context,
                        get_func_expr((FuncExpr *) node, context, showimplicit);
                        break;
 
+               case T_NamedArgExpr:
+                       {
+                               NamedArgExpr *na = (NamedArgExpr *) node;
+
+                               appendStringInfo(buf, "%s := ", quote_identifier(na->name));
+                               get_rule_expr((Node *) na->arg, context, showimplicit);
+                       }
+                       break;
+
                case T_OpExpr:
                        get_oper_expr((OpExpr *) node, context);
                        break;
@@ -3821,6 +5109,16 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
+               case T_NullIfExpr:
+                       {
+                               NullIfExpr *nullifexpr = (NullIfExpr *) node;
+
+                               appendStringInfo(buf, "NULLIF(");
+                               get_rule_expr((Node *) nullifexpr->args, context, true);
+                               appendStringInfoChar(buf, ')');
+                       }
+                       break;
+
                case T_ScalarArrayOpExpr:
                        {
                                ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
@@ -3834,7 +5132,7 @@ get_rule_expr(Node *node, deparse_context *context,
                                appendStringInfo(buf, " %s %s (",
                                                                 generate_operator_name(expr->opno,
                                                                                                                exprType(arg1),
-                                                                                  get_element_type(exprType(arg2))),
+                                                                         get_base_element_type(exprType(arg2))),
                                                                 expr->useOr ? "ANY" : "ALL");
                                get_rule_expr_paren(arg2, context, true, node);
                                appendStringInfoChar(buf, ')');
@@ -3906,20 +5204,42 @@ get_rule_expr(Node *node, deparse_context *context,
 
                case T_SubPlan:
                        {
+                               SubPlan    *subplan = (SubPlan *) node;
+
                                /*
                                 * We cannot see an already-planned subplan in rule deparsing,
-                                * only while EXPLAINing a query plan. For now, just punt.
+                                * 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.
                                 */
-                               if (((SubPlan *) node)->useHashTable)
-                                       appendStringInfo(buf, "(hashed subplan)");
+                               if (subplan->useHashTable)
+                                       appendStringInfo(buf, "(hashed %s)", subplan->plan_name);
                                else
-                                       appendStringInfo(buf, "(subplan)");
+                                       appendStringInfo(buf, "(%s)", subplan->plan_name);
                        }
                        break;
 
                case T_AlternativeSubPlan:
-                       /* As above, just punt */
-                       appendStringInfo(buf, "(alternative subplans)");
+                       {
+                               AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
+                               ListCell   *lc;
+
+                               /* As above, this can only happen during EXPLAIN */
+                               appendStringInfo(buf, "(alternatives: ");
+                               foreach(lc, asplan->subplans)
+                               {
+                                       SubPlan    *splan = (SubPlan *) lfirst(lc);
+
+                                       Assert(IsA(splan, SubPlan));
+                                       if (splan->useHashTable)
+                                               appendStringInfo(buf, "hashed %s", splan->plan_name);
+                                       else
+                                               appendStringInfo(buf, "%s", splan->plan_name);
+                                       if (lnext(lc))
+                                               appendStringInfo(buf, " or ");
+                               }
+                               appendStringInfo(buf, ")");
+                       }
                        break;
 
                case T_FieldSelect:
@@ -3953,12 +5273,36 @@ get_rule_expr(Node *node, deparse_context *context,
                        break;
 
                case T_FieldStore:
+                       {
+                               FieldStore *fstore = (FieldStore *) node;
+                               bool            need_parens;
 
-                       /*
-                        * We shouldn't see FieldStore here; it should have been stripped
-                        * off by processIndirection().
-                        */
-                       elog(ERROR, "unexpected FieldStore");
+                               /*
+                                * 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
+                                * 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
+                                * 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
+                                * EXPLAIN's purposes; especially since anything more would be
+                                * either hopelessly confusing or an even poorer
+                                * representation of what the plan is actually doing.
+                                */
+                               need_parens = (list_length(fstore->newvals) != 1);
+                               if (need_parens)
+                                       appendStringInfoString(buf, "ROW(");
+                               get_rule_expr((Node *) fstore->newvals, context, showimplicit);
+                               if (need_parens)
+                                       appendStringInfoChar(buf, ')');
+                       }
                        break;
 
                case T_RelabelType:
@@ -4044,6 +5388,21 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
+               case T_CollateExpr:
+                       {
+                               CollateExpr *collate = (CollateExpr *) node;
+                               Node       *arg = (Node *) collate->arg;
+
+                               if (!PRETTY_PAREN(context))
+                                       appendStringInfoChar(buf, '(');
+                               get_rule_expr_paren(arg, context, showimplicit, node);
+                               appendStringInfo(buf, " COLLATE %s",
+                                                                generate_collation_name(collate->collOid));
+                               if (!PRETTY_PAREN(context))
+                                       appendStringInfoChar(buf, ')');
+                       }
+                       break;
+
                case T_CaseExpr:
                        {
                                CaseExpr   *caseexpr = (CaseExpr *) node;
@@ -4061,49 +5420,36 @@ get_rule_expr(Node *node, deparse_context *context,
                                        CaseWhen   *when = (CaseWhen *) lfirst(temp);
                                        Node       *w = (Node *) when->expr;
 
-                                       if (!PRETTY_INDENT(context))
-                                               appendStringInfoChar(buf, ' ');
-                                       appendContextKeyword(context, "WHEN ",
-                                                                                0, 0, 0);
                                        if (caseexpr->arg)
                                        {
                                                /*
                                                 * The parser should have produced WHEN clauses of the
-                                                * form "CaseTestExpr = RHS"; we want to show just the
-                                                * RHS.  If the user wrote something silly like "CASE
-                                                * boolexpr WHEN TRUE THEN ...", then the optimizer's
-                                                * simplify_boolean_equality() may have reduced this
-                                                * to just "CaseTestExpr" or "NOT CaseTestExpr", for
-                                                * which we have to show "TRUE" or "FALSE".  Also,
-                                                * depending on context the original CaseTestExpr
-                                                * might have been reduced to a Const (but we won't
-                                                * see "WHEN Const").
+                                                * form "CaseTestExpr = RHS", possibly with an
+                                                * implicit coercion inserted above the CaseTestExpr.
+                                                * For accurate decompilation of rules it's essential
+                                                * that we show just the RHS.  However in an
+                                                * expression that's been through the optimizer, the
+                                                * WHEN clause could be almost anything (since the
+                                                * equality operator could have been expanded into an
+                                                * inline function).  If we don't recognize the form
+                                                * of the WHEN clause, just punt and display it as-is.
                                                 */
                                                if (IsA(w, OpExpr))
                                                {
-                                                       Node       *rhs;
-
-                                                       Assert(IsA(linitial(((OpExpr *) w)->args),
-                                                                          CaseTestExpr) ||
-                                                                  IsA(linitial(((OpExpr *) w)->args),
-                                                                          Const));
-                                                       rhs = (Node *) lsecond(((OpExpr *) w)->args);
-                                                       get_rule_expr(rhs, context, false);
-                                               }
-                                               else if (IsA(w, CaseTestExpr))
-                                                       appendStringInfo(buf, "TRUE");
-                                               else if (not_clause(w))
-                                               {
-                                                       Assert(IsA(get_notclausearg((Expr *) w),
-                                                                          CaseTestExpr));
-                                                       appendStringInfo(buf, "FALSE");
+                                                       List       *args = ((OpExpr *) w)->args;
+
+                                                       if (list_length(args) == 2 &&
+                                                               IsA(strip_implicit_coercions(linitial(args)),
+                                                                       CaseTestExpr))
+                                                               w = (Node *) lsecond(args);
                                                }
-                                               else
-                                                       elog(ERROR, "unexpected CASE WHEN clause: %d",
-                                                                (int) nodeTag(w));
                                        }
-                                       else
-                                               get_rule_expr(w, context, false);
+
+                                       if (!PRETTY_INDENT(context))
+                                               appendStringInfoChar(buf, ' ');
+                                       appendContextKeyword(context, "WHEN ",
+                                                                                0, 0, 0);
+                                       get_rule_expr(w, context, false);
                                        appendStringInfo(buf, " THEN ");
                                        get_rule_expr((Node *) when->result, context, true);
                                }
@@ -4119,6 +5465,19 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
+               case T_CaseTestExpr:
+                       {
+                               /*
+                                * Normally we should never get here, since for expressions
+                                * that can contain this node type we attempt to avoid
+                                * recursing to it.  But in an optimized expression we might
+                                * be unable to avoid that (see comments for CaseExpr).  If we
+                                * do see one, print it as CASE_TEST_EXPR.
+                                */
+                               appendStringInfo(buf, "CASE_TEST_EXPR");
+                       }
+                       break;
+
                case T_ArrayExpr:
                        {
                                ArrayExpr  *arrayexpr = (ArrayExpr *) node;
@@ -4126,6 +5485,15 @@ get_rule_expr(Node *node, deparse_context *context,
                                appendStringInfo(buf, "ARRAY[");
                                get_rule_expr((Node *) arrayexpr->elements, context, true);
                                appendStringInfoChar(buf, ']');
+
+                               /*
+                                * If the array isn't empty, we assume its elements are
+                                * coerced to the desired type.  If it's empty, though, we
+                                * need an explicit coercion to the array type.
+                                */
+                               if (arrayexpr->elements == NIL)
+                                       appendStringInfo(buf, "::%s",
+                                         format_type_with_typemod(arrayexpr->array_typeid, -1));
                        }
                        break;
 
@@ -4410,8 +5778,9 @@ get_rule_expr(Node *node, deparse_context *context,
 
                                }
                                if (xexpr->op == IS_XMLSERIALIZE)
-                                       appendStringInfo(buf, " AS %s", format_type_with_typemod(xexpr->type,
-                                                                                                                        xexpr->typmod));
+                                       appendStringInfo(buf, " AS %s",
+                                                                        format_type_with_typemod(xexpr->type,
+                                                                                                                         xexpr->typmod));
                                if (xexpr->op == IS_DOCUMENT)
                                        appendStringInfoString(buf, " IS DOCUMENT");
                                else
@@ -4419,16 +5788,6 @@ get_rule_expr(Node *node, deparse_context *context,
                        }
                        break;
 
-               case T_NullIfExpr:
-                       {
-                               NullIfExpr *nullifexpr = (NullIfExpr *) node;
-
-                               appendStringInfo(buf, "NULLIF(");
-                               get_rule_expr((Node *) nullifexpr->args, context, true);
-                               appendStringInfoChar(buf, ')');
-                       }
-                       break;
-
                case T_NullTest:
                        {
                                NullTest   *ntest = (NullTest *) node;
@@ -4585,9 +5944,7 @@ get_oper_expr(OpExpr *expr, deparse_context *context)
                HeapTuple       tp;
                Form_pg_operator optup;
 
-               tp = SearchSysCache(OPEROID,
-                                                       ObjectIdGetDatum(opno),
-                                                       0, 0, 0);
+               tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno));
                if (!HeapTupleIsValid(tp))
                        elog(ERROR, "cache lookup failed for operator %u", opno);
                optup = (Form_pg_operator) GETSTRUCT(tp);
@@ -4627,6 +5984,7 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
        Oid                     funcoid = expr->funcid;
        Oid                     argtypes[FUNC_MAX_ARGS];
        int                     nargs;
+       List       *argnames;
        bool            is_variadic;
        ListCell   *l;
 
@@ -4666,19 +6024,25 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
         * Normal function: display as proname(args).  First we need to extract
         * the argument datatypes.
         */
+       if (list_length(expr->args) > FUNC_MAX_ARGS)
+               ereport(ERROR,
+                               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                errmsg("too many arguments")));
        nargs = 0;
+       argnames = NIL;
        foreach(l, expr->args)
        {
-               if (nargs >= FUNC_MAX_ARGS)
-                       ereport(ERROR,
-                                       (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                        errmsg("too many arguments")));
-               argtypes[nargs] = exprType((Node *) lfirst(l));
+               Node       *arg = (Node *) lfirst(l);
+
+               if (IsA(arg, NamedArgExpr))
+                       argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
+               argtypes[nargs] = exprType(arg);
                nargs++;
        }
 
        appendStringInfo(buf, "%s(",
-                                        generate_function_name(funcoid, nargs, argtypes,
+                                        generate_function_name(funcoid, nargs,
+                                                                                       argnames, argtypes,
                                                                                        &is_variadic));
        nargs = 0;
        foreach(l, expr->args)
@@ -4700,32 +6064,109 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        Oid                     argtypes[FUNC_MAX_ARGS];
+       List       *arglist;
        int                     nargs;
        ListCell   *l;
 
+       /* Extract the regular arguments, ignoring resjunk stuff for the moment */
+       arglist = NIL;
        nargs = 0;
        foreach(l, aggref->args)
        {
-               if (nargs >= FUNC_MAX_ARGS)
+               TargetEntry *tle = (TargetEntry *) lfirst(l);
+               Node       *arg = (Node *) tle->expr;
+
+               Assert(!IsA(arg, NamedArgExpr));
+               if (tle->resjunk)
+                       continue;
+               if (nargs >= FUNC_MAX_ARGS)             /* paranoia */
                        ereport(ERROR,
                                        (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
                                         errmsg("too many arguments")));
-               argtypes[nargs] = exprType((Node *) lfirst(l));
+               argtypes[nargs] = exprType(arg);
+               arglist = lappend(arglist, arg);
                nargs++;
        }
 
        appendStringInfo(buf, "%s(%s",
-                                        generate_function_name(aggref->aggfnoid,
-                                                                                       nargs, argtypes, NULL),
-                                        aggref->aggdistinct ? "DISTINCT " : "");
+                                        generate_function_name(aggref->aggfnoid, nargs,
+                                                                                       NIL, argtypes, NULL),
+                                        (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
        /* aggstar can be set only in zero-argument aggregates */
        if (aggref->aggstar)
                appendStringInfoChar(buf, '*');
        else
-               get_rule_expr((Node *) aggref->args, context, true);
+               get_rule_expr((Node *) arglist, context, true);
+       if (aggref->aggorder != NIL)
+       {
+               appendStringInfoString(buf, " ORDER BY ");
+               get_rule_orderby(aggref->aggorder, aggref->args, false, context);
+       }
        appendStringInfoChar(buf, ')');
 }
 
+/*
+ * get_windowfunc_expr - Parse back a WindowFunc node
+ */
+static void
+get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+       Oid                     argtypes[FUNC_MAX_ARGS];
+       int                     nargs;
+       ListCell   *l;
+
+       if (list_length(wfunc->args) > FUNC_MAX_ARGS)
+               ereport(ERROR,
+                               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
+                                errmsg("too many arguments")));
+       nargs = 0;
+       foreach(l, wfunc->args)
+       {
+               Node       *arg = (Node *) lfirst(l);
+
+               Assert(!IsA(arg, NamedArgExpr));
+               argtypes[nargs] = exprType(arg);
+               nargs++;
+       }
+
+       appendStringInfo(buf, "%s(",
+                                        generate_function_name(wfunc->winfnoid, nargs,
+                                                                                       NIL, argtypes, NULL));
+       /* winstar can be set only in zero-argument aggregates */
+       if (wfunc->winstar)
+               appendStringInfoChar(buf, '*');
+       else
+               get_rule_expr((Node *) wfunc->args, context, true);
+       appendStringInfoString(buf, ") OVER ");
+
+       foreach(l, context->windowClause)
+       {
+               WindowClause *wc = (WindowClause *) lfirst(l);
+
+               if (wc->winref == wfunc->winref)
+               {
+                       if (wc->name)
+                               appendStringInfoString(buf, quote_identifier(wc->name));
+                       else
+                               get_rule_windowspec(wc, context->windowTList, context);
+                       break;
+               }
+       }
+       if (l == NULL)
+       {
+               if (context->windowClause)
+                       elog(ERROR, "could not find window clause for winref %u",
+                                wfunc->winref);
+
+               /*
+                * In EXPLAIN, we don't have window context information available, so
+                * we have to settle for this:
+                */
+               appendStringInfoString(buf, "(?)");
+       }
+}
+
 /* ----------
  * get_coercion_expr
  *
@@ -4774,6 +6215,10 @@ get_coercion_expr(Node *arg, deparse_context *context,
  * showtype can be -1 to never show "::typename" decoration, or +1 to always
  * show it, or 0 to show it only if the constant wouldn't be assumed to be
  * 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.
  * ----------
  */
 static void
@@ -4794,9 +6239,12 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
                 */
                appendStringInfo(buf, "NULL");
                if (showtype >= 0)
+               {
                        appendStringInfo(buf, "::%s",
                                                         format_type_with_typemod(constval->consttype,
                                                                                                          constval->consttypmod));
+                       get_const_collation(constval, context);
+               }
                return;
        }
 
@@ -4897,6 +6345,28 @@ get_const_expr(Const *constval, deparse_context *context, int showtype)
                appendStringInfo(buf, "::%s",
                                                 format_type_with_typemod(constval->consttype,
                                                                                                  constval->consttypmod));
+
+       get_const_collation(constval, context);
+}
+
+/*
+ * helper for get_const_expr: append COLLATE if needed
+ */
+static void
+get_const_collation(Const *constval, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+
+       if (OidIsValid(constval->constcollid))
+       {
+               Oid                     typcollation = get_typcollation(constval->consttype);
+
+               if (constval->constcollid != typcollation)
+               {
+                       appendStringInfo(buf, " COLLATE %s",
+                                                        generate_collation_name(constval->constcollid));
+               }
+       }
 }
 
 /*
@@ -4908,9 +6378,9 @@ simple_quote_literal(StringInfo buf, const char *val)
        const char *valptr;
 
        /*
-        * We form the string literal according to the prevailing setting
-        * of standard_conforming_strings; we never use E''. User is
-        * responsible for making sure result is used correctly.
+        * We form the string literal according to the prevailing setting of
+        * standard_conforming_strings; we never use E''. User is responsible for
+        * making sure result is used correctly.
         */
        appendStringInfoChar(buf, '\'');
        for (valptr = val; *valptr; valptr++)
@@ -5029,6 +6499,7 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
                        need_paren = false;
                        break;
 
+               case CTE_SUBLINK:               /* shouldn't occur in a SubLink */
                default:
                        elog(ERROR, "unrecognized sublink type: %d",
                                 (int) sublink->subLinkType);
@@ -5088,11 +6559,52 @@ get_from_clause(Query *query, const char *prefix, deparse_context *context)
                        appendContextKeyword(context, prefix,
                                                                 -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
                        first = false;
+
+                       get_from_clause_item(jtnode, query, context);
                }
                else
+               {
+                       StringInfoData targetbuf;
+                       char       *trailing_nl;
+
                        appendStringInfoString(buf, ", ");
 
-               get_from_clause_item(jtnode, query, context);
+                       initStringInfo(&targetbuf);
+                       context->buf = &targetbuf;
+
+                       get_from_clause_item(jtnode, query, context);
+
+                       context->buf = buf;
+
+                       /* Locate the start of the current      line in the buffer */
+
+                       trailing_nl = (strrchr(buf->data, '\n'));
+                       if (trailing_nl == NULL)
+                               trailing_nl = buf->data;
+                       else
+                               trailing_nl++;
+
+                       /*
+                        * Add a newline, plus some  indentation, if pretty_wrap is on and
+                        * the new from-clause item would cause an overflow.
+                        */
+
+                       if (pretty_wrap >= 0 &&
+                               (strlen(trailing_nl) + strlen(targetbuf.data) > pretty_wrap))
+                       {
+                               appendContextKeyword(context, "", -PRETTYINDENT_STD,
+                                                                        PRETTYINDENT_STD, PRETTYINDENT_VAR);
+                       }
+
+                       /* Add the new item */
+
+                       appendStringInfoString(buf, targetbuf.data);
+
+                       /* cleanup */
+
+                       pfree(targetbuf.data);
+               }
+
        }
 }
 
@@ -5113,7 +6625,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                                /* Normal relation RTE */
                                appendStringInfo(buf, "%s%s",
                                                                 only_marker(rte),
-                                                                generate_relation_name(rte->relid));
+                                                                generate_relation_name(rte->relid,
+                                                                                                               context->namespaces));
                                break;
                        case RTE_SUBQUERY:
                                /* Subquery RTE */
@@ -5130,6 +6643,9 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                                /* Values list RTE */
                                get_values_def(rte->values_lists, context);
                                break;
+                       case RTE_CTE:
+                               appendStringInfoString(buf, quote_identifier(rte->ctename));
+                               break;
                        default:
                                elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
                                break;
@@ -5142,7 +6658,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                        gavealias = true;
                }
                else if (rte->rtekind == RTE_RELATION &&
-                                strcmp(rte->eref->aliasname, get_rel_name(rte->relid)) != 0)
+                       strcmp(rte->eref->aliasname, get_relation_name(rte->relid)) != 0)
                {
                        /*
                         * Apparently the rel has been renamed since the rule was made.
@@ -5176,6 +6692,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                                get_from_clause_coldeflist(rte->eref->colnames,
                                                                                   rte->funccoltypes,
                                                                                   rte->funccoltypmods,
+                                                                                  rte->funccolcollations,
                                                                                   context);
                        }
                        else
@@ -5288,14 +6805,14 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
 
                if (!j->isNatural)
                {
-                       if (j->using)
+                       if (j->usingClause)
                        {
                                ListCell   *col;
 
                                appendStringInfo(buf, " USING (");
-                               foreach(col, j->using)
+                               foreach(col, j->usingClause)
                                {
-                                       if (col != list_head(j->using))
+                                       if (col != list_head(j->usingClause))
                                                appendStringInfo(buf, ", ");
                                        appendStringInfoString(buf,
                                                                          quote_identifier(strVal(lfirst(col))));
@@ -5374,35 +6891,45 @@ get_from_clause_alias(Alias *alias, RangeTblEntry *rte,
  * responsible for ensuring that an alias or AS is present before it.
  */
 static void
-get_from_clause_coldeflist(List *names, List *types, List *typmods,
+get_from_clause_coldeflist(List *names,
+                                                  List *types, List *typmods, List *collations,
                                                   deparse_context *context)
 {
        StringInfo      buf = context->buf;
        ListCell   *l1;
        ListCell   *l2;
        ListCell   *l3;
+       ListCell   *l4;
        int                     i = 0;
 
        appendStringInfoChar(buf, '(');
 
        l2 = list_head(types);
        l3 = list_head(typmods);
+       l4 = list_head(collations);
        foreach(l1, names)
        {
                char       *attname = strVal(lfirst(l1));
                Oid                     atttypid;
                int32           atttypmod;
+               Oid                     attcollation;
 
                atttypid = lfirst_oid(l2);
                l2 = lnext(l2);
                atttypmod = lfirst_int(l3);
                l3 = lnext(l3);
+               attcollation = lfirst_oid(l4);
+               l4 = lnext(l4);
 
                if (i > 0)
                        appendStringInfo(buf, ", ");
                appendStringInfo(buf, "%s %s",
                                                 quote_identifier(attname),
                                                 format_type_with_typemod(atttypid, atttypmod));
+               if (OidIsValid(attcollation) &&
+                       attcollation != get_typcollation(atttypid))
+                       appendStringInfo(buf, " COLLATE %s",
+                                                        generate_collation_name(attcollation));
                i++;
        }
 
@@ -5427,9 +6954,7 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
        char       *opcname;
        char       *nspname;
 
-       ht_opc = SearchSysCache(CLAOID,
-                                                       ObjectIdGetDatum(opclass),
-                                                       0, 0, 0);
+       ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
        if (!HeapTupleIsValid(ht_opc))
                elog(ERROR, "cache lookup failed for opclass %u", opclass);
        opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
@@ -5482,12 +7007,11 @@ processIndirection(Node *node, deparse_context *context, bool printit)
                                         format_type_be(fstore->resulttype));
 
                        /*
-                        * Print the field name.  Note we assume here that there's only
-                        * one field being assigned to.  This is okay in stored rules but
-                        * could be wrong in executable target lists.  Presently no
-                        * problem since explain.c doesn't print plan targetlists, but
-                        * someday may have to think of something ...
+                        * Print the field name.  There should only be one target field in
+                        * stored rules.  There could be more than that in executable
+                        * target lists, but this function cannot be used for that case.
                         */
+                       Assert(list_length(fstore->fieldnums) == 1);
                        fieldname = get_relid_attribute_name(typrelid,
                                                                                        linitial_int(fstore->fieldnums));
                        if (printit)
@@ -5587,6 +7111,9 @@ quote_identifier(const char *ident)
                }
        }
 
+       if (quote_all_identifiers)
+               safe = false;
+
        if (safe)
        {
                /*
@@ -5597,7 +7124,9 @@ quote_identifier(const char *ident)
                 * Note: ScanKeywordLookup() does case-insensitive comparison, but
                 * that's fine, since we already know we have all-lower-case.
                 */
-               const ScanKeyword *keyword = ScanKeywordLookup(ident);
+               const ScanKeyword *keyword = ScanKeywordLookup(ident,
+                                                                                                          ScanKeywords,
+                                                                                                          NumScanKeywords);
 
                if (keyword != NULL && keyword->category != UNRESERVED_KEYWORD)
                        safe = false;
@@ -5627,50 +7156,97 @@ quote_identifier(const char *ident)
 /*
  * quote_qualified_identifier  - Quote a possibly-qualified identifier
  *
- * Return a name of the form namespace.ident, or just ident if namespace
+ * Return a name of the form qualifier.ident, or just ident if qualifier
  * is NULL, quoting each component if necessary.  The result is palloc'd.
  */
 char *
-quote_qualified_identifier(const char *namespace,
+quote_qualified_identifier(const char *qualifier,
                                                   const char *ident)
 {
        StringInfoData buf;
 
        initStringInfo(&buf);
-       if (namespace)
-               appendStringInfo(&buf, "%s.", quote_identifier(namespace));
+       if (qualifier)
+               appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
        appendStringInfoString(&buf, quote_identifier(ident));
        return buf.data;
 }
 
+/*
+ * get_relation_name
+ *             Get the unqualified name of a relation specified by OID
+ *
+ * This differs from the underlying get_rel_name() function in that it will
+ * throw error instead of silently returning NULL if the OID is bad.
+ */
+static char *
+get_relation_name(Oid relid)
+{
+       char       *relname = get_rel_name(relid);
+
+       if (!relname)
+               elog(ERROR, "cache lookup failed for relation %u", relid);
+       return relname;
+}
+
 /*
  * generate_relation_name
  *             Compute the name to display for a relation specified by OID
  *
  * The result includes all necessary quoting and schema-prefixing.
+ *
+ * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
+ * We will forcibly qualify the relation name if it equals any CTE name
+ * visible in the namespace list.
  */
 static char *
-generate_relation_name(Oid relid)
+generate_relation_name(Oid relid, List *namespaces)
 {
        HeapTuple       tp;
        Form_pg_class reltup;
+       bool            need_qual;
+       ListCell   *nslist;
+       char       *relname;
        char       *nspname;
        char       *result;
 
-       tp = SearchSysCache(RELOID,
-                                               ObjectIdGetDatum(relid),
-                                               0, 0, 0);
+       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);
 
-       /* Qualify the name if not visible in search path */
-       if (RelationIsVisible(relid))
-               nspname = NULL;
-       else
+       /* Check for conflicting CTE name */
+       need_qual = false;
+       foreach(nslist, namespaces)
+       {
+               deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
+               ListCell   *ctlist;
+
+               foreach(ctlist, dpns->ctes)
+               {
+                       CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
+
+                       if (strcmp(cte->ctename, relname) == 0)
+                       {
+                               need_qual = true;
+                               break;
+                       }
+               }
+               if (need_qual)
+                       break;
+       }
+
+       /* Otherwise, qualify the name if not visible in search path */
+       if (!need_qual)
+               need_qual = !RelationIsVisible(relid);
+
+       if (need_qual)
                nspname = get_namespace_name(reltup->relnamespace);
+       else
+               nspname = NULL;
 
-       result = quote_qualified_identifier(nspname, NameStr(reltup->relname));
+       result = quote_qualified_identifier(nspname, relname);
 
        ReleaseSysCache(tp);
 
@@ -5680,15 +7256,15 @@ generate_relation_name(Oid relid)
 /*
  * generate_function_name
  *             Compute the name to display for a function specified by OID,
- *             given that it is being called with the specified actual arg types.
- *             (Arg types matter because of ambiguous-function resolution rules.)
+ *             given that it is being called with the specified actual arg names and
+ *             types.  (Those matter because of ambiguous-function resolution rules.)
  *
- * The result includes all necessary quoting and schema-prefixing.  We can
+ * The result includes all necessary quoting and schema-prefixing.     We can
  * also pass back an indication of whether the function is variadic.
  */
 static char *
-generate_function_name(Oid funcid, int nargs, Oid *argtypes,
-                                          bool *is_variadic)
+generate_function_name(Oid funcid, int nargs, List *argnames,
+                                          Oid *argtypes, bool *is_variadic)
 {
        HeapTuple       proctup;
        Form_pg_proc procform;
@@ -5702,25 +7278,26 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes,
        int                     p_nvargs;
        Oid                *p_true_typeids;
 
-       proctup = SearchSysCache(PROCOID,
-                                                        ObjectIdGetDatum(funcid),
-                                                        0, 0, 0);
+       proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
        if (!HeapTupleIsValid(proctup))
                elog(ERROR, "cache lookup failed for function %u", funcid);
        procform = (Form_pg_proc) GETSTRUCT(proctup);
        proname = NameStr(procform->proname);
-       Assert(nargs >= procform->pronargs);
 
        /*
         * The idea here is to schema-qualify only if the parser would fail to
         * resolve the correct function given the unqualified func name with the
-        * specified argtypes.
+        * specified argtypes.  If the function is variadic, we should presume
+        * that VARIADIC will be included in the call.
         */
        p_result = func_get_detail(list_make1(makeString(proname)),
-                                                          NIL, nargs, argtypes, false,
+                                                          NIL, argnames, nargs, argtypes,
+                                                          !OidIsValid(procform->provariadic), true,
                                                           &p_funcid, &p_rettype,
-                                                          &p_retset, &p_nvargs, &p_true_typeids);
-       if ((p_result == FUNCDETAIL_NORMAL || p_result == FUNCDETAIL_AGGREGATE) &&
+                                                          &p_retset, &p_nvargs, &p_true_typeids, NULL);
+       if ((p_result == FUNCDETAIL_NORMAL ||
+                p_result == FUNCDETAIL_AGGREGATE ||
+                p_result == FUNCDETAIL_WINDOWFUNC) &&
                p_funcid == funcid)
                nspname = NULL;
        else
@@ -5767,9 +7344,7 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
 
        initStringInfo(&buf);
 
-       opertup = SearchSysCache(OPEROID,
-                                                        ObjectIdGetDatum(operid),
-                                                        0, 0, 0);
+       opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
        if (!HeapTupleIsValid(opertup))
                elog(ERROR, "cache lookup failed for operator %u", operid);
        operform = (Form_pg_operator) GETSTRUCT(opertup);
@@ -5821,6 +7396,39 @@ generate_operator_name(Oid operid, Oid arg1, Oid arg2)
        return buf.data;
 }
 
+/*
+ * generate_collation_name
+ *             Compute the name to display for a collation specified by OID
+ *
+ * The result includes all necessary quoting and schema-prefixing.
+ */
+char *
+generate_collation_name(Oid collid)
+{
+       HeapTuple       tp;
+       Form_pg_collation colltup;
+       char       *collname;
+       char       *nspname;
+       char       *result;
+
+       tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for collation %u", collid);
+       colltup = (Form_pg_collation) GETSTRUCT(tp);
+       collname = NameStr(colltup->collname);
+
+       if (!CollationIsVisible(collid))
+               nspname = get_namespace_name(colltup->collnamespace);
+       else
+               nspname = NULL;
+
+       result = quote_qualified_identifier(nspname, collname);
+
+       ReleaseSysCache(tp);
+
+       return result;
+}
+
 /*
  * Given a C string, produce a TEXT datum.
  *
@@ -5847,9 +7455,7 @@ flatten_reloptions(Oid relid)
        Datum           reloptions;
        bool            isnull;
 
-       tuple = SearchSysCache(RELOID,
-                                                  ObjectIdGetDatum(relid),
-                                                  0, 0, 0);
+       tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
        if (!HeapTupleIsValid(tuple))
                elog(ERROR, "cache lookup failed for relation %u", relid);