]> 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 9767b9f1f508db3c68efe1ee1268ccdac766096e..9ca3b9d0c44c71522309c2da61071e096825cdf0 100644 (file)
@@ -4,12 +4,12 @@
  *       Functions to convert stored expressions/querytrees back to
  *       source text
  *
- * Portions Copyright (c) 1996-2009, 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.304 2009/07/24 21:08:42 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"
@@ -51,8 +51,9 @@
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
 #include "utils/lsyscache.h"
-#include "utils/tqual.h"
+#include "utils/rel.h"
 #include "utils/syscache.h"
+#include "utils/tqual.h"
 #include "utils/typcache.h"
 #include "utils/xml.h"
 
@@ -72,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)
@@ -102,18 +105,28 @@ typedef struct
  * 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.
- * Also, in the plan-tree case we don't have access to the parse-time CTE
- * list, so we need a list of subplans instead.
+ * 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 */
        List       *ctes;                       /* List of CommonTableExpr nodes */
-       List       *subplans;           /* List of subplans, in plan-tree case */
-       Plan       *outer_plan;         /* OUTER subplan, or NULL if none */
-       Plan       *inner_plan;         /* INNER subplan, or NULL if none */
+       /* 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;
 
 
@@ -125,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;
 
 
 /* ----------
@@ -139,10 +156,13 @@ 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);
@@ -151,6 +171,15 @@ static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
 static int print_function_arguments(StringInfo buf, HeapTuple proctup,
                                                 bool print_table_args, bool print_defaults);
 static void print_function_rettype(StringInfo buf, HeapTuple proctup);
+static void 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,
@@ -180,11 +209,13 @@ static void get_rule_orderby(List *orderList, List *targetList,
 static void get_rule_windowclause(Query *query, deparse_context *context);
 static void get_rule_windowspec(WindowClause *wc, List *targetList,
                                        deparse_context *context);
-static void push_plan(deparse_namespace *dpns, Plan *subplan);
-static char *get_variable(Var *var, int levelsup, bool showstar,
+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 appendContextKeyword(deparse_context *context, const char *str,
@@ -201,6 +232,7 @@ static void get_coercion_expr(Node *arg, deparse_context *context,
                                  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,
@@ -209,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 *get_relation_name(Oid relid);
 static char *generate_relation_name(Oid relid, List *namespaces);
-static char *generate_function_name(Oid funcid, int nargs, Oid *argtypes,
-                                          bool *is_variadic);
+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);
@@ -288,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;
        }
 
        /*
@@ -348,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)
 {
@@ -356,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)));
 }
@@ -374,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)));
 }
@@ -419,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;
        }
 
        /*
@@ -461,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;
@@ -469,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
@@ -498,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");
@@ -524,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))
        {
@@ -531,16 +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, 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,
-                                                                                                       NIL));
+                                               generate_relation_name(trigrec->tgconstrrelid, NIL));
                if (!trigrec->tgdeferrable)
                        appendStringInfo(&buf, "NOT ");
                appendStringInfo(&buf, "DEFERRABLE INITIALLY ");
@@ -548,7 +645,6 @@ pg_get_triggerdef(PG_FUNCTION_ARGS)
                        appendStringInfo(&buf, "DEFERRED ");
                else
                        appendStringInfo(&buf, "IMMEDIATE ");
-
        }
 
        if (TRIGGER_FOR_ROW(trigrec->tgtype))
@@ -556,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)
@@ -592,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;
 }
 
 /* ----------
@@ -613,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
@@ -626,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;
@@ -651,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;
@@ -664,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);
@@ -674,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);
@@ -687,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);
@@ -697,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);
@@ -728,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
@@ -736,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, NIL),
-                                                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
@@ -751,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);
@@ -760,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
                {
@@ -788,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);
 
@@ -813,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, ')');
 
@@ -839,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)));
+                       }
                }
 
                /*
@@ -864,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);
                }
        }
 
@@ -919,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);
@@ -1044,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:
@@ -1120,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
@@ -1133,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:
@@ -1150,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);
 
@@ -1305,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);
@@ -1343,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);
@@ -1406,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);
@@ -1436,6 +1690,11 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
  * pg_get_functiondef
  *             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)
@@ -1458,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);
@@ -1472,9 +1729,7 @@ 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);
@@ -1631,9 +1886,7 @@ 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);
 
@@ -1659,9 +1912,7 @@ pg_get_function_identity_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);
 
@@ -1686,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);
 
@@ -1918,12 +2167,13 @@ 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;
@@ -1931,63 +2181,207 @@ deparse_context_for(const char *aliasname, Oid relid)
        /* Build one-element rtable */
        dpns->rtable = list_make1(rte);
        dpns->ctes = NIL;
-       dpns->subplans = NIL;
-       dpns->outer_plan = dpns->inner_plan = NULL;
 
        /* 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.  To do this, the caller must provide the
- * parent Plan node.  In the normal case of a join plan node, OUTER and
- * INNER references can be resolved by drilling down into the left and
- * right child plans.  A special case is that a nestloop inner indexscan
- * might have OUTER Vars, but the outer side of the join is not a child
- * plan node.  To handle such cases the outer plan node must be passed
- * separately. (Pass NULL for outer_plan otherwise.)
+ * OUTER_VAR, INNER_VAR, or INDEX_VAR references.  To do this, the caller must
+ * provide the parent PlanState node.  Then OUTER_VAR and INNER_VAR references
+ * can be resolved by drilling down into the left and right child plans.
+ * Similarly, INDEX_VAR references can be resolved by reference to the
+ * indextlist given in the parent IndexOnlyScan node.  (Note that we don't
+ * currently support deparsing of indexquals in regular IndexScan or
+ * BitmapIndexScan nodes; for those, we can only deparse the indexqualorig
+ * fields, which won't contain INDEX_VAR Vars.)
+ *
+ * Note: planstate really ought to be declared as "PlanState *", but we use
+ * "Node *" to avoid having to include execnodes.h in builtins.h.
  *
- * Note: plan and outer_plan really ought to be declared as "Plan *", but
- * we use "Node *" to avoid having to include plannodes.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 plan inputs are necessary
- * for Vars that reference expressions computed in subplan target lists.
- *
- * We also need the list of subplans associated with the Plan tree; this
- * is for resolving references to CTE subplans.
+ * for Vars with special varnos.
  */
 List *
-deparse_context_for_plan(Node *plan, Node *outer_plan,
-                                                List *rtable, List *subplans)
+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->ctes = NIL;
-       dpns->subplans = subplans;
+
+       /* 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;
 
        /*
-        * Set up outer_plan and inner_plan from the Plan node (this includes
-        * various special cases for particular Plan types).
+        * 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.
         */
-       push_plan(dpns, (Plan *) plan);
+       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;
 
        /*
-        * If outer_plan is given, that overrides whatever we got from the plan.
+        * 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 (outer_plan)
-               dpns->outer_plan = (Plan *) outer_plan;
+       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);
 
-       /* Return a one-deep namespace stack */
-       return list_make1(dpns);
+       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
@@ -2122,7 +2516,7 @@ 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);
@@ -2131,10 +2525,10 @@ make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                context.varprefix = (list_length(query->rtable) != 1);
                context.prettyFlags = prettyFlags;
                context.indentLevel = PRETTYINDENT_STD;
+
+               memset(&dpns, 0, sizeof(dpns));
                dpns.rtable = query->rtable;
                dpns.ctes = query->cteList;
-               dpns.subplans = NIL;
-               dpns.outer_plan = dpns.inner_plan = NULL;
 
                get_rule_expr(qual, &context, false);
        }
@@ -2267,7 +2661,7 @@ 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));
@@ -2278,10 +2672,9 @@ get_query_def(Query *query, StringInfo buf, List *parentnamespace,
        context.prettyFlags = prettyFlags;
        context.indentLevel = startIndent;
 
+       memset(&dpns, 0, sizeof(dpns));
        dpns.rtable = query->rtable;
        dpns.ctes = query->cteList;
-       dpns.subplans = NIL;
-       dpns.outer_plan = dpns.inner_plan = NULL;
 
        switch (query->commandType)
        {
@@ -2496,21 +2889,28 @@ 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;
@@ -2638,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;
@@ -2646,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 */
@@ -2654,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
                {
@@ -2688,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);
        }
 }
 
@@ -2978,6 +3449,16 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
                        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)
@@ -2987,6 +3468,16 @@ get_rule_windowspec(WindowClause *wc, List *targetList,
                                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);
                }
@@ -3012,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.
@@ -3111,15 +3605,11 @@ get_insert_query_def(Query *query, deparse_context *context)
        }
        else if (values_rte)
        {
-               /* A WITH clause is possible here */
-               get_with_clause(query, context);
                /* Add the multi-VALUES expression lists */
                get_values_def(values_rte->values_lists, context);
        }
        else
        {
-               /* A WITH clause is possible here */
-               get_with_clause(query, context);
                /* Add the single-VALUES expression list */
                appendContextKeyword(context, "VALUES (",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
@@ -3149,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
         */
@@ -3230,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
         */
@@ -3285,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
        {
@@ -3294,51 +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.
- *
- * We also use this to initialize the fields during deparse_context_for_plan.
- */
-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.)
-        * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
-        */
-       if (IsA(subplan, SubqueryScan))
-               dpns->inner_plan = ((SubqueryScan *) subplan)->subplan;
-       else if (IsA(subplan, CteScan))
-       {
-               int                     ctePlanId = ((CteScan *) subplan)->ctePlanId;
-
-               if (ctePlanId > 0 && ctePlanId <= list_length(dpns->subplans))
-                       dpns->inner_plan = list_nth(dpns->subplans, ctePlanId - 1);
-               else
-                       dpns->inner_plan = NULL;
-       }
-       else
-               dpns->inner_plan = innerPlan(subplan);
-}
-
-
 /*
  * Display a Var appropriately.
  *
@@ -3346,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;
@@ -3373,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
@@ -3406,24 +3866,20 @@ 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);
-               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);
 
                /*
                 * Force parentheses because our caller probably assumed a Var is a
@@ -3435,22 +3891,82 @@ 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
+       else if (var->varno == INDEX_VAR && dpns->index_tlist)
        {
-               elog(ERROR, "bogus varno: %d", var->varno);
-               return NULL;                    /* keep compiler quiet */
-       }
+               TargetEntry *tle;
 
-       /* Identify names to use */
-       schemaname = NULL;                      /* default assumptions */
-       refname = rte->eref->aliasname;
+               tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
+               if (!tle)
+                       elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
 
-       /* Exceptions occur only if the RTE is alias-less */
-       if (rte->alias == NULL)
+               Assert(netlevelsup == 0);
+
+               /*
+                * 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, ')');
+
+               return NULL;
+       }
+       else
+       {
+               elog(ERROR, "bogus varno: %d", var->varno);
+               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;
+
+       /* Exceptions occur only if the RTE is alias-less */
+       if (rte->alias == NULL)
        {
                if (rte->rtekind == RTE_RELATION)
                {
@@ -3488,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;
                }
        }
@@ -3506,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,
@@ -3560,6 +4086,29 @@ get_name_for_var_field(Var *var, int fieldno,
                        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
@@ -3588,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;
+               deparse_namespace save_dpns;
+               const char *result;
+
+               tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
+               if (!tle)
+                       elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
+
+               Assert(netlevelsup == 0);
+               push_child_plan(dpns, dpns->outer_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 == 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->outer_plan->targetlist, var->varattno);
+               tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
                if (!tle)
-                       elog(ERROR, "bogus varattno for OUTER var: %d", var->varattno);
+                       elog(ERROR, "bogus varattno for INNER_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->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;
        }
-       else if (var->varno == INNER && dpns->inner_plan)
+       else if (var->varno == INDEX_VAR && dpns->index_tlist)
        {
                TargetEntry *tle;
-               Plan       *save_outer;
-               Plan       *save_inner;
                const char *result;
 
-               tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
+               tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
                if (!tle)
-                       elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
+                       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
@@ -3666,7 +4223,6 @@ get_name_for_var_field(Var *var, int fieldno,
        switch (rte->rtekind)
        {
                case RTE_RELATION:
-               case RTE_SPECIAL:
                case RTE_VALUES:
 
                        /*
@@ -3698,10 +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.ctes = rte->subquery->cteList;
-                                               mydpns.subplans = NIL;
-                                               mydpns.outer_plan = mydpns.inner_plan = NULL;
 
                                                context->namespaces = lcons(&mydpns,
                                                                                                        context->namespaces);
@@ -3726,28 +4281,23 @@ get_name_for_var_field(Var *var, int fieldno,
                                         * 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;
                                }
                        }
@@ -3800,7 +4350,7 @@ get_name_for_var_field(Var *var, int fieldno,
                                if (lc != NULL)
                                {
                                        Query      *ctequery = (Query *) cte->ctequery;
-                                       TargetEntry *ste = get_tle_by_resno(ctequery->targetList,
+                                       TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
                                                                                                                attnum);
 
                                        if (ste == NULL || ste->resjunk)
@@ -3821,10 +4371,9 @@ get_name_for_var_field(Var *var, int fieldno,
                                                deparse_namespace mydpns;
                                                const char *result;
 
+                                               memset(&mydpns, 0, sizeof(mydpns));
                                                mydpns.rtable = ctequery->rtable;
                                                mydpns.ctes = ctequery->cteList;
-                                               mydpns.subplans = NIL;
-                                               mydpns.outer_plan = mydpns.inner_plan = NULL;
 
                                                new_nslist = list_copy_tail(context->namespaces,
                                                                                                        ctelevelsup);
@@ -3848,28 +4397,23 @@ get_name_for_var_field(Var *var, int fieldno,
                                         * can look into the subplan'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 CTE %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;
                                }
                        }
@@ -3932,6 +4476,193 @@ find_rte_by_refname(const char *refname, deparse_context *context)
        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;
+
+               pop_ancestor_plan(dpns, &save_dpns);
+
+               return;
+       }
+
+       /*
+        * Not PARAM_EXEC, or couldn't find referent: just print $N.
+        */
+       appendStringInfo(context->buf, "$%d", param->paramid);
+}
 
 /*
  * get_simple_binary_op_name
@@ -4260,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:
@@ -4268,7 +4999,7 @@ 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:
@@ -4284,6 +5015,22 @@ get_rule_expr(Node *node, deparse_context *context,
                                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
@@ -4296,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;
 
@@ -4311,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;
@@ -4332,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;
@@ -4345,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, ')');
@@ -4486,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:
@@ -4577,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;
@@ -4594,54 +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").  We have also to consider the
-                                                * possibility that an implicit coercion was inserted
-                                                * between the CaseTestExpr and the operator.
+                                                * 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))
                                                {
                                                        List       *args = ((OpExpr *) w)->args;
-                                                       Node       *lhs;
-                                                       Node       *rhs;
-
-                                                       Assert(list_length(args) == 2);
-                                                       lhs = strip_implicit_coercions(linitial(args));
-                                                       Assert(IsA(lhs, CaseTestExpr) ||
-                                                                  IsA(lhs, Const));
-                                                       rhs = (Node *) lsecond(args);
-                                                       get_rule_expr(rhs, context, false);
-                                               }
-                                               else if (IsA(strip_implicit_coercions(w),
-                                                                        CaseTestExpr))
-                                                       appendStringInfo(buf, "TRUE");
-                                               else if (not_clause(w))
-                                               {
-                                                       Assert(IsA(strip_implicit_coercions((Node *) get_notclausearg((Expr *) w)),
-                                                                          CaseTestExpr));
-                                                       appendStringInfo(buf, "FALSE");
+
+                                                       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);
                                }
@@ -4657,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;
@@ -4957,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
@@ -4966,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;
@@ -5132,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);
@@ -5174,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;
 
@@ -5218,14 +6029,20 @@ get_func_expr(FuncExpr *expr, deparse_context *context,
                                (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
                                 errmsg("too many arguments")));
        nargs = 0;
+       argnames = NIL;
        foreach(l, expr->args)
        {
-               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)
@@ -5247,29 +6064,44 @@ get_agg_expr(Aggref *aggref, deparse_context *context)
 {
        StringInfo      buf = context->buf;
        Oid                     argtypes[FUNC_MAX_ARGS];
+       List       *arglist;
        int                     nargs;
        ListCell   *l;
 
-       if (list_length(aggref->args) > FUNC_MAX_ARGS)
-               ereport(ERROR,
-                               (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
-                                errmsg("too many arguments")));
+       /* Extract the regular arguments, ignoring resjunk stuff for the moment */
+       arglist = NIL;
        nargs = 0;
        foreach(l, aggref->args)
        {
-               argtypes[nargs] = exprType((Node *) lfirst(l));
+               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(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, ')');
 }
 
@@ -5291,13 +6123,16 @@ get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
        nargs = 0;
        foreach(l, wfunc->args)
        {
-               argtypes[nargs] = exprType((Node *) lfirst(l));
+               Node       *arg = (Node *) lfirst(l);
+
+               Assert(!IsA(arg, NamedArgExpr));
+               argtypes[nargs] = exprType(arg);
                nargs++;
        }
 
-       appendStringInfo(buf, "%s(%s",
-                                        generate_function_name(wfunc->winfnoid,
-                                                                                       nargs, argtypes, NULL), "");
+       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, '*');
@@ -5380,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
@@ -5400,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;
        }
 
@@ -5503,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));
+               }
+       }
 }
 
 /*
@@ -5695,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);
+               }
+
        }
 }
 
@@ -5753,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.
@@ -5787,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
@@ -5985,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++;
        }
 
@@ -6038,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);
@@ -6093,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)
@@ -6198,6 +7111,9 @@ quote_identifier(const char *ident)
                }
        }
 
+       if (quote_all_identifiers)
+               safe = false;
+
        if (safe)
        {
                /*
@@ -6256,6 +7172,23 @@ quote_qualified_identifier(const char *qualifier,
        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
@@ -6277,9 +7210,7 @@ generate_relation_name(Oid relid, List *namespaces)
        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);
@@ -6325,15 +7256,15 @@ generate_relation_name(Oid relid, List *namespaces)
 /*
  * 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
  * 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;
@@ -6347,9 +7278,7 @@ 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);
@@ -6358,10 +7287,12 @@ generate_function_name(Oid funcid, int nargs, Oid *argtypes,
        /*
         * The idea here is to schema-qualify only if the parser would fail to
         * resolve the correct function given the unqualified func name with the
-        * specified argtypes.
+        * 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, true,
+                                                          NIL, argnames, nargs, argtypes,
+                                                          !OidIsValid(procform->provariadic), true,
                                                           &p_funcid, &p_rettype,
                                                           &p_retset, &p_nvargs, &p_true_typeids, NULL);
        if ((p_result == FUNCDETAIL_NORMAL ||
@@ -6413,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);
@@ -6467,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.
  *
@@ -6493,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);