]> 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 f1c1d04ee092f4d80eb2b3fe04712932a0649292..9ca3b9d0c44c71522309c2da61071e096825cdf0 100644 (file)
@@ -4,7 +4,7 @@
  *       Functions to convert stored expressions/querytrees back to
  *       source text
  *
- * Portions Copyright (c) 1996-2010, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
 #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)
@@ -106,9 +109,11 @@ typedef struct
  * 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 the outer and inner
- * subplan nodes, whose targetlists are used to resolve OUTER and INNER Vars.
- * (Note: these could be derived on-the-fly from the planstate instead.)
+ * 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
 {
@@ -117,10 +122,11 @@ typedef struct
        /* 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 */
-       Plan       *outer_plan;         /* OUTER subplan, or NULL if none */
-       Plan       *inner_plan;         /* INNER subplan, or NULL if none */
+       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;
 
 
@@ -132,6 +138,7 @@ 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;
@@ -168,11 +175,11 @@ 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);
+                          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);
+                                 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,
@@ -202,13 +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 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 void print_parameter_expr(Node *expr, ListCell *ancestor_cell,
-                                        deparse_namespace *dpns, 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,
@@ -225,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,
@@ -233,13 +241,15 @@ 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, List *argnames,
                                           Oid *argtypes, bool *is_variadic);
@@ -312,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;
        }
 
        /*
@@ -372,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)
 {
@@ -380,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)));
 }
@@ -398,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)));
 }
@@ -443,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;
        }
 
        /*
@@ -626,6 +658,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
        if (!isnull)
        {
                Node       *qual;
+               char            relkind;
                deparse_context context;
                deparse_namespace dpns;
                RangeTblEntry *oldrte;
@@ -635,10 +668,13 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 
                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;
@@ -646,6 +682,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
                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;
@@ -786,10 +823,11 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
        List       *context;
        Oid                     indrelid;
        int                     keyno;
-       Oid                     keycoltype;
+       Datum           indcollDatum;
        Datum           indclassDatum;
        Datum           indoptionDatum;
        bool            isnull;
+       oidvector  *indcollation;
        oidvector  *indclass;
        int2vector *indoption;
        StringInfoData buf;
@@ -807,11 +845,17 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
        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);
@@ -857,7 +901,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
 
        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
@@ -886,6 +930,8 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
        {
                AttrNumber      attnum = idxrec->indkey.values[keyno];
                int16           opt = indoption->values[keyno];
+               Oid                     keycoltype;
+               Oid                     keycolcollation;
 
                if (!colno)
                        appendStringInfoString(&buf, sep);
@@ -895,11 +941,14 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                {
                        /* 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
                {
@@ -923,10 +972,19 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                                        appendStringInfo(&buf, "(%s)", str);
                        }
                        keycoltype = exprType(indexkey);
+                       keycolcollation = exprCollation(indexkey);
                }
 
                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);
 
@@ -1261,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
@@ -1274,15 +1332,20 @@ 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;
                        }
@@ -1342,6 +1405,8 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
                appendStringInfo(&buf, " DEFERRABLE");
        if (conForm->condeferred)
                appendStringInfo(&buf, " INITIALLY DEFERRED");
+       if (!conForm->convalidated)
+               appendStringInfoString(&buf, " NOT VALID");
 
        /* Cleanup */
        ReleaseSysCache(tup);
@@ -1534,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);
@@ -2108,6 +2173,7 @@ deparse_context_for(const char *aliasname, Oid relid)
        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;
@@ -2124,9 +2190,14 @@ deparse_context_for(const char *aliasname, Oid relid)
  * deparse_context_for_planstate       - Build deparse context for a plan
  *
  * When deparsing an expression in a Plan tree, we might have to resolve
- * OUTER or INNER references.  To do this, the caller must provide the
- * parent PlanState node.  Then OUTER and INNER references can be resolved
- * by drilling down into the left and right child plans.
+ * 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.
@@ -2137,7 +2208,7 @@ deparse_context_for(const char *aliasname, Oid relid)
  *
  * 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.
+ * for Vars with special varnos.
  */
 List *
 deparse_context_for_planstate(Node *planstate, List *ancestors,
@@ -2163,10 +2234,11 @@ deparse_context_for_planstate(Node *planstate, List *ancestors,
  * set_deparse_planstate: set up deparse_namespace to parse subexpressions
  * of a given PlanState node
  *
- * This sets the planstate, outer_planstate, inner_planstate, outer_plan, and
- * inner_plan 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.
+ * 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)
@@ -2174,24 +2246,26 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
        dpns->planstate = ps;
 
        /*
-        * We special-case Append to pretend that the first child plan is the
-        * OUTER referent; we have to interpret OUTER Vars in the Append's tlist
-        * 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.
+        * We special-case Append and MergeAppend to pretend that the first child
+        * plan is the OUTER referent; we have to interpret OUTER Vars in their
+        * tlists according to one of the children, and the first one is the most
+        * natural choice.      Likewise special-case ModifyTable to pretend that the
+        * first child plan is the OUTER referent; this is to support RETURNING
+        * lists containing references to non-target relations.
         */
        if (IsA(ps, AppendState))
                dpns->outer_planstate = ((AppendState *) ps)->appendplans[0];
+       else if (IsA(ps, MergeAppendState))
+               dpns->outer_planstate = ((MergeAppendState *) ps)->mergeplans[0];
        else if (IsA(ps, ModifyTableState))
                dpns->outer_planstate = ((ModifyTableState *) ps)->mt_plans[0];
        else
                dpns->outer_planstate = outerPlanState(ps);
 
        if (dpns->outer_planstate)
-               dpns->outer_plan = dpns->outer_planstate->plan;
+               dpns->outer_tlist = dpns->outer_planstate->plan->targetlist;
        else
-               dpns->outer_plan = NULL;
+               dpns->outer_tlist = NIL;
 
        /*
         * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
@@ -2206,18 +2280,25 @@ set_deparse_planstate(deparse_namespace *dpns, PlanState *ps)
                dpns->inner_planstate = innerPlanState(ps);
 
        if (dpns->inner_planstate)
-               dpns->inner_plan = dpns->inner_planstate->plan;
+               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->inner_plan = NULL;
+               dpns->index_tlist = NIL;
 }
 
 /*
  * push_child_plan: temporarily transfer deparsing attention to a child plan
  *
- * When expanding an OUTER or INNER reference, we must adjust the deparse
- * context 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).
+ * 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.
@@ -2231,10 +2312,11 @@ push_child_plan(deparse_namespace *dpns, PlanState *ps,
 
        /*
         * Currently we don't bother to adjust the ancestors list, because an
-        * OUTER or INNER reference really shouldn't contain any Params that
-        * would be set by the parent node itself.  If we did want to adjust it,
-        * 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.
+        * 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 */
@@ -2258,7 +2340,7 @@ pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
  * 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/INNER variables.
+ * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
  *
  * The target ancestor is conveniently identified by the ListCell holding it
  * in dpns->ancestors.
@@ -2956,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;
@@ -2964,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 */
@@ -2972,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
                {
@@ -3006,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);
        }
 }
 
@@ -3350,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.
@@ -3449,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);
@@ -3487,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
         */
@@ -3568,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
         */
@@ -3644,13 +3802,20 @@ get_utility_query_def(Query *query, deparse_context *context)
  * 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;
@@ -3671,22 +3836,22 @@ 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;
                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);
                push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
@@ -3704,14 +3869,14 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
                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;
                deparse_namespace save_dpns;
 
-               tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
+               tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
                if (!tle)
-                       elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
+                       elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
 
                Assert(netlevelsup == 0);
                push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
@@ -3729,6 +3894,28 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
                pop_child_plan(dpns, &save_dpns);
                return NULL;
        }
+       else if (var->varno == INDEX_VAR && dpns->index_tlist)
+       {
+               TargetEntry *tle;
+
+               tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
+               if (!tle)
+                       elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
+
+               Assert(netlevelsup == 0);
+
+               /*
+                * 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);
@@ -3743,17 +3930,17 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
         * 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_plan to reference the child plan node.
+        * 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_plan)
+               dpns->inner_planstate)
        {
                TargetEntry *tle;
                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 subquery var: %d", var->varattno);
 
@@ -3817,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;
                }
        }
@@ -3836,28 +4029,39 @@ get_variable(Var *var, int levelsup, bool showstar, deparse_context *context)
                        appendStringInfo(buf, "%s.",
                                                         quote_identifier(schemaname));
                appendStringInfoString(buf, quote_identifier(refname));
-               if (attname || showstar)
-                       appendStringInfoChar(buf, '.');
+               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,
@@ -3882,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
@@ -3910,23 +4137,23 @@ 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_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);
                push_child_plan(dpns, dpns->outer_planstate, &save_dpns);
@@ -3937,15 +4164,15 @@ get_name_for_var_field(Var *var, int fieldno,
                pop_child_plan(dpns, &save_dpns);
                return result;
        }
-       else if (var->varno == INNER && dpns->inner_plan)
+       else if (var->varno == INNER_VAR && dpns->inner_tlist)
        {
                TargetEntry *tle;
                deparse_namespace save_dpns;
                const char *result;
 
-               tle = get_tle_by_resno(dpns->inner_plan->targetlist, var->varattno);
+               tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
                if (!tle)
-                       elog(ERROR, "bogus varattno for INNER var: %d", var->varattno);
+                       elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
 
                Assert(netlevelsup == 0);
                push_child_plan(dpns, dpns->inner_planstate, &save_dpns);
@@ -3956,6 +4183,22 @@ get_name_for_var_field(Var *var, int fieldno,
                pop_child_plan(dpns, &save_dpns);
                return result;
        }
+       else if (var->varno == INDEX_VAR && dpns->index_tlist)
+       {
+               TargetEntry *tle;
+               const char *result;
+
+               tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
+               if (!tle)
+                       elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
+
+               Assert(netlevelsup == 0);
+
+               result = get_name_for_var_field((Var *) tle->expr, fieldno,
+                                                                               levelsup, context);
+
+               return result;
+       }
        else
        {
                elog(ERROR, "bogus varno: %d", var->varno);
@@ -3980,7 +4223,6 @@ get_name_for_var_field(Var *var, int fieldno,
        switch (rte->rtekind)
        {
                case RTE_RELATION:
-               case RTE_SPECIAL:
                case RTE_VALUES:
 
                        /*
@@ -4042,11 +4284,10 @@ get_name_for_var_field(Var *var, int fieldno,
                                        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);
@@ -4109,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)
@@ -4159,11 +4400,10 @@ get_name_for_var_field(Var *var, int fieldno,
                                        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);
@@ -4237,17 +4477,25 @@ find_rte_by_refname(const char *refname, deparse_context *context)
 }
 
 /*
- * Display a Param appropriately.
+ * 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 void
-get_parameter(Param *param, deparse_context *context)
+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, try to locate the expression from
-        * which the parameter was computed.  This will necessarily be in some
-        * ancestor of the current expression's PlanState.  Note that failing
-        * to find a referent isn't an error, since the Param might well be a
-        * subplan output rather than an input.
+        * 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)
        {
@@ -4266,9 +4514,9 @@ get_parameter(Param *param, deparse_context *context)
                        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.
+                        * 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) &&
@@ -4278,14 +4526,14 @@ get_parameter(Param *param, deparse_context *context)
 
                                foreach(lc2, nl->nestParams)
                                {
-                                       NestLoopParam  *nlp = (NestLoopParam *) lfirst(lc2);
+                                       NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
 
                                        if (nlp->paramno == param->paramid)
                                        {
-                                               /* Found a match, so print it */
-                                               print_parameter_expr((Node *) nlp->paramval, lc,
-                                                                                        dpns, context);
-                                               return;
+                                               /* Found a match, so return it */
+                                               *dpns_p = dpns;
+                                               *ancestor_cell_p = lc;
+                                               return (Node *) nlp->paramval;
                                        }
                                }
                        }
@@ -4306,14 +4554,15 @@ get_parameter(Param *param, deparse_context *context)
                                /* Matched subplan, so check its arguments */
                                forboth(lc3, subplan->parParam, lc4, subplan->args)
                                {
-                                       int             paramid = lfirst_int(lc3);
-                                       Node   *arg = (Node *) lfirst(lc4);
+                                       int                     paramid = lfirst_int(lc3);
+                                       Node       *arg = (Node *) lfirst(lc4);
 
                                        if (paramid == param->paramid)
                                        {
-                                               /* Found a match, so print it */
-                                               print_parameter_expr(arg, lc, dpns, context);
-                                               return;
+                                               /* Found a match, so return it */
+                                               *dpns_p = dpns;
+                                               *ancestor_cell_p = lc;
+                                               return arg;
                                        }
                                }
 
@@ -4348,41 +4597,71 @@ get_parameter(Param *param, deparse_context *context)
                }
        }
 
-       /*
-        * Not PARAM_EXEC, or couldn't find referent: just print $N.
-        */
-       appendStringInfo(context->buf, "$%d", param->paramid);
+       /* No referent found */
+       return NULL;
 }
 
-/* Print a parameter reference expression found by get_parameter */
+/*
+ * Display a Param appropriately.
+ */
 static void
-print_parameter_expr(Node *expr, ListCell *ancestor_cell,
-                                        deparse_namespace *dpns, deparse_context *context)
+get_parameter(Param *param, deparse_context *context)
 {
-       deparse_namespace save_dpns;
-       bool            save_varprefix;
-
-       /* Switch attention to the ancestor plan node */
-       push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
+       Node       *expr;
+       deparse_namespace *dpns;
+       ListCell   *ancestor_cell;
 
        /*
-        * Force prefixing of Vars, since they won't belong to the relation being
-        * scanned in the original plan node.
+        * 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.
         */
-       save_varprefix = context->varprefix;
-       context->varprefix = true;
+       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;
 
-       /*
-        * We don't need to add parentheses because a Param's expansion is
-        * (currently) always a Var or Aggref.
-        */
-       Assert(IsA(expr, Var) || IsA(expr, Aggref));
+               /* Switch attention to the ancestor plan node */
+               push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
 
-       get_rule_expr(expr, context, false);
+               /*
+                * 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;
 
-       context->varprefix = save_varprefix;
+               /*
+                * 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, ')');
 
-       pop_ancestor_plan(dpns, &save_dpns);
+               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);
 }
 
 /*
@@ -4712,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:
@@ -4830,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;
@@ -4843,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, ')');
@@ -5099,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;
@@ -5116,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);
                                }
@@ -5179,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;
@@ -5479,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
@@ -5488,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;
@@ -5925,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
@@ -5945,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;
        }
 
@@ -6048,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));
+               }
+       }
 }
 
 /*
@@ -6240,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);
+               }
+
        }
 }
 
@@ -6298,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.
@@ -6332,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
@@ -6530,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++;
        }
 
@@ -6801,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
@@ -7008,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.
  *