]> granicus.if.org Git - postgresql/blobdiff - src/backend/utils/adt/ruleutils.c
CREATE INDEX ... INCLUDING (column[, ...])
[postgresql] / src / backend / utils / adt / ruleutils.c
index 0585251d8fed95adad2a91b3e94f269beb39ff3f..0e1eefd8dac871312239f12a19d22652d254a6b5 100644 (file)
@@ -4,7 +4,7 @@
  *       Functions to convert stored expressions/querytrees back to
  *       source text
  *
- * Portions Copyright (c) 1996-2015, PostgreSQL Global Development Group
+ * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
  * Portions Copyright (c) 1994, Regents of the University of California
  *
  *
 #include <unistd.h>
 #include <fcntl.h>
 
+#include "access/amapi.h"
 #include "access/htup_details.h"
 #include "access/sysattr.h"
 #include "catalog/dependency.h"
 #include "catalog/indexing.h"
 #include "catalog/pg_aggregate.h"
+#include "catalog/pg_am.h"
 #include "catalog/pg_authid.h"
 #include "catalog/pg_collation.h"
 #include "catalog/pg_constraint.h"
 #include "catalog/pg_opclass.h"
 #include "catalog/pg_operator.h"
 #include "catalog/pg_proc.h"
-#include "catalog/pg_tablesample_method.h"
 #include "catalog/pg_trigger.h"
 #include "catalog/pg_type.h"
 #include "commands/defrem.h"
 #include "commands/tablespace.h"
+#include "common/keywords.h"
 #include "executor/spi.h"
 #include "funcapi.h"
+#include "mb/pg_wchar.h"
 #include "miscadmin.h"
 #include "nodes/makefuncs.h"
 #include "nodes/nodeFuncs.h"
 #include "optimizer/tlist.h"
-#include "parser/keywords.h"
 #include "parser/parse_node.h"
 #include "parser/parse_agg.h"
 #include "parser/parse_func.h"
@@ -56,6 +58,7 @@
 #include "utils/array.h"
 #include "utils/builtins.h"
 #include "utils/fmgroids.h"
+#include "utils/hsearch.h"
 #include "utils/lsyscache.h"
 #include "utils/rel.h"
 #include "utils/ruleutils.h"
@@ -106,8 +109,8 @@ typedef struct
        int                     wrapColumn;             /* max line length, or -1 for no limit */
        int                     indentLevel;    /* current indent level for prettyprint */
        bool            varprefix;              /* TRUE to print prefixes on Vars */
-       ParseExprKind special_exprkind; /* set only for exprkinds needing */
-                                                                       /* special handling */
+       ParseExprKind special_exprkind;         /* set only for exprkinds needing
+                                                                                * special handling */
 } deparse_context;
 
 /*
@@ -268,6 +271,15 @@ typedef struct
 #define deparse_columns_fetch(rangetable_index, dpns) \
        ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
 
+/*
+ * Entry in set_rtable_names' hash table
+ */
+typedef struct
+{
+       char            name[NAMEDATALEN];              /* Hash key --- must be first */
+       int                     counter;                /* Largest addition used so far for name */
+} NameHashEntry;
+
 
 /* ----------
  * Global data
@@ -313,8 +325,6 @@ static void print_function_rettype(StringInfo buf, HeapTuple proctup);
 static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
 static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
                                 Bitmapset *rels_used);
-static bool refname_is_unique(char *refname, deparse_namespace *dpns,
-                                 List *parent_namespaces);
 static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
                                          List *parent_namespaces);
 static void set_simple_column_names(deparse_namespace *dpns);
@@ -349,8 +359,6 @@ static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                         int prettyFlags);
 static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
                         int prettyFlags, int wrapColumn);
-static void get_tablesample_def(TableSampleClause *tablesample,
-                                                               deparse_context *context);
 static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
                          TupleDesc resultDesc,
                          int prettyFlags, int wrapColumn, int startIndent);
@@ -361,8 +369,8 @@ static void get_select_query_def(Query *query, deparse_context *context,
 static void get_insert_query_def(Query *query, deparse_context *context);
 static void get_update_query_def(Query *query, deparse_context *context);
 static void get_update_query_targetlist_def(Query *query, List *targetList,
-                                                                       deparse_context *context,
-                                                                       RangeTblEntry *rte);
+                                                               deparse_context *context,
+                                                               RangeTblEntry *rte);
 static void get_delete_query_def(Query *query, deparse_context *context);
 static void get_utility_query_def(Query *query, deparse_context *context);
 static void get_basic_select_query(Query *query, deparse_context *context,
@@ -376,7 +384,7 @@ static Node *get_rule_sortgroupclause(Index ref, List *tlist,
                                                 bool force_colno,
                                                 deparse_context *context);
 static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
-                                                                bool omit_parens, deparse_context *context);
+                                        bool omit_parens, deparse_context *context);
 static void get_rule_orderby(List *orderList, List *targetList,
                                 bool force_colno, deparse_context *context);
 static void get_rule_windowclause(Query *query, deparse_context *context);
@@ -394,6 +402,8 @@ static void appendContextKeyword(deparse_context *context, const char *str,
 static void removeStringInfoSpaces(StringInfo str);
 static void get_rule_expr(Node *node, deparse_context *context,
                          bool showimplicit);
+static void get_rule_expr_toplevel(Node *node, deparse_context *context,
+                                          bool showimplicit);
 static void get_oper_expr(OpExpr *expr, deparse_context *context);
 static void get_func_expr(FuncExpr *expr, deparse_context *context,
                          bool showimplicit);
@@ -416,6 +426,8 @@ static void get_column_alias_list(deparse_columns *colinfo,
 static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
                                                   deparse_columns *colinfo,
                                                   deparse_context *context);
+static void get_tablesample_def(TableSampleClause *tablesample,
+                                       deparse_context *context);
 static void get_opclass_name(Oid opclass, Oid actual_datatype,
                                 StringInfo buf);
 static Node *processIndirection(Node *node, deparse_context *context,
@@ -423,10 +435,11 @@ static Node *processIndirection(Node *node, deparse_context *context,
 static void printSubscripts(ArrayRef *aref, deparse_context *context);
 static char *get_relation_name(Oid relid);
 static char *generate_relation_name(Oid relid, List *namespaces);
+static char *generate_qualified_relation_name(Oid relid);
 static char *generate_function_name(Oid funcid, int nargs,
-                                                       List *argnames, Oid *argtypes,
-                                                       bool has_variadic, bool *use_variadic_p,
-                                                       ParseExprKind special_exprkind);
+                                          List *argnames, Oid *argtypes,
+                                          bool has_variadic, bool *use_variadic_p,
+                                          ParseExprKind special_exprkind);
 static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
 static text *string_to_text(char *str);
 static char *flatten_reloptions(Oid relid);
@@ -718,6 +731,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
        SysScanDesc tgscan;
        int                     findx = 0;
        char       *tgname;
+       Oid                     argtypes[1];    /* dummy */
        Datum           value;
        bool            isnull;
 
@@ -893,7 +907,7 @@ pg_get_triggerdef_worker(Oid trigid, bool pretty)
 
        appendStringInfo(&buf, "EXECUTE PROCEDURE %s(",
                                         generate_function_name(trigrec->tgfoid, 0,
-                                                                                       NIL, NULL,
+                                                                                       NIL, argtypes,
                                                                                        false, NULL, EXPR_KIND_NONE));
 
        if (trigrec->tgnargs > 0)
@@ -1007,6 +1021,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
        Form_pg_index idxrec;
        Form_pg_class idxrelrec;
        Form_pg_am      amrec;
+       IndexAmRoutine *amroutine;
        List       *indexprs;
        ListCell   *indexpr_item;
        List       *context;
@@ -1067,6 +1082,9 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                         idxrelrec->relam);
        amrec = (Form_pg_am) GETSTRUCT(ht_am);
 
+       /* Fetch the index AM's API struct */
+       amroutine = GetIndexAmRoutine(amrec->amhandler);
+
        /*
         * Get the index expressions, if any.  (NOTE: we do not use the relcache
         * versions of the expressions and predicate, because we want to display
@@ -1122,6 +1140,21 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                Oid                     keycoltype;
                Oid                     keycolcollation;
 
+               /*
+                * attrsOnly flag is used for building unique-constraint and
+                * exclusion-constraint error messages. Included attrs are
+                * meaningless there, so do not include them into the message.
+                */
+               if (attrsOnly && keyno >= idxrec->indnkeyatts)
+                       break;
+
+               /* Report the INCLUDED attributes, if any. */
+               if ((!attrsOnly) && keyno == idxrec->indnkeyatts)
+               {
+                               appendStringInfoString(&buf, ") INCLUDING (");
+                               sep = "";
+               }
+
                if (!colno)
                        appendStringInfoString(&buf, sep);
                sep = ", ";
@@ -1135,6 +1168,7 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                        attname = get_relid_attribute_name(indrelid, attnum);
                        if (!colno || colno == keyno + 1)
                                appendStringInfoString(&buf, quote_identifier(attname));
+
                        get_atttypetypmodcoll(indrelid, attnum,
                                                                  &keycoltype, &keycoltypmod,
                                                                  &keycolcollation);
@@ -1174,11 +1208,14 @@ pg_get_indexdef_worker(Oid indexrelid, int colno,
                                appendStringInfo(&buf, " COLLATE %s",
                                                                 generate_collation_name((indcoll)));
 
+                       if(keyno >= idxrec->indnkeyatts)
+                               continue;
+
                        /* Add the operator class name, if not default */
                        get_opclass_name(indclass->values[keyno], keycoltype, &buf);
 
                        /* Add options if relevant */
-                       if (amrec->amcanorder)
+                       if (amroutine->amcanorder)
                        {
                                /* if it supports sort ordering, report DESC and NULLS opts */
                                if (opt & INDOPTION_DESC)
@@ -1303,9 +1340,11 @@ pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
                                                                                                                          prettyFlags)));
 }
 
-/* Internal version that returns a palloc'd C string; no pretty-printing */
+/*
+ * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
+ */
 char *
-pg_get_constraintdef_string(Oid constraintId)
+pg_get_constraintdef_command(Oid constraintId)
 {
        return pg_get_constraintdef_worker(constraintId, true, 0);
 }
@@ -1352,10 +1391,16 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
        initStringInfo(&buf);
 
-       if (fullCommand && OidIsValid(conForm->conrelid))
+       if (fullCommand)
        {
-               appendStringInfo(&buf, "ALTER TABLE ONLY %s ADD CONSTRAINT %s ",
-                                                generate_relation_name(conForm->conrelid, NIL),
+               /*
+                * Currently, callers want ALTER TABLE (without ONLY) for CHECK
+                * constraints, and other types of constraints don't inherit anyway so
+                * it doesn't matter whether we say ONLY or not.  Someday we might
+                * need to let callers specify whether to put ONLY in the command.
+                */
+               appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
+                                                generate_qualified_relation_name(conForm->conrelid),
                                                 quote_identifier(NameStr(conForm->conname)));
        }
 
@@ -1494,6 +1539,19 @@ pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
 
                                appendStringInfoChar(&buf, ')');
 
+                               /* Fetch and build including column list */
+                               isnull = true;
+                               val = SysCacheGetAttr(CONSTROID, tup,
+                                                                         Anum_pg_constraint_conincluding, &isnull);
+                               if (!isnull)
+                               {
+                                       appendStringInfoString(&buf, " INCLUDING (");
+
+                                       decompile_column_index_array(val, conForm->conrelid, &buf);
+
+                                       appendStringInfoChar(&buf, ')');
+                               }
+
                                indexId = get_constraint_index(constraintId);
 
                                /* XXX why do we only print these bits if fullCommand? */
@@ -1879,28 +1937,9 @@ pg_get_serial_sequence(PG_FUNCTION_ARGS)
 
        if (OidIsValid(sequenceId))
        {
-               HeapTuple       classtup;
-               Form_pg_class classtuple;
-               char       *nspname;
                char       *result;
 
-               /* Get the sequence's pg_class entry */
-               classtup = SearchSysCache1(RELOID, ObjectIdGetDatum(sequenceId));
-               if (!HeapTupleIsValid(classtup))
-                       elog(ERROR, "cache lookup failed for relation %u", sequenceId);
-               classtuple = (Form_pg_class) GETSTRUCT(classtup);
-
-               /* Get the namespace */
-               nspname = get_namespace_name(classtuple->relnamespace);
-               if (!nspname)
-                       elog(ERROR, "cache lookup failed for namespace %u",
-                                classtuple->relnamespace);
-
-               /* And construct the result string */
-               result = quote_qualified_identifier(nspname,
-                                                                                       NameStr(classtuple->relname));
-
-               ReleaseSysCache(classtup);
+               result = generate_qualified_relation_name(sequenceId);
 
                PG_RETURN_TEXT_P(string_to_text(result));
        }
@@ -1963,7 +2002,7 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
        print_function_trftypes(&buf, proctup);
 
        appendStringInfo(&buf, "\n LANGUAGE %s\n",
-                                        quote_identifier(get_language_name(proc->prolang, false)));
+                                 quote_identifier(get_language_name(proc->prolang, false)));
 
        /* Emit some miscellaneous options on one line */
        oldlen = buf.len;
@@ -1985,6 +2024,8 @@ pg_get_functiondef(PG_FUNCTION_ARGS)
                appendStringInfoString(&buf, " STRICT");
        if (proc->prosecdef)
                appendStringInfoString(&buf, " SECURITY DEFINER");
+       if (proc->proleakproof)
+               appendStringInfoString(&buf, " LEAKPROOF");
 
        /* This code for the default cost and rows should match functioncmds.c */
        if (proc->prolang == INTERNALlanguageId ||
@@ -2364,13 +2405,13 @@ is_input_argument(int nth, const char *argmodes)
 static void
 print_function_trftypes(StringInfo buf, HeapTuple proctup)
 {
-       Oid     *trftypes;
-       int     ntypes;
+       Oid                *trftypes;
+       int                     ntypes;
 
        ntypes = get_func_trftypes(proctup, &trftypes);
        if (ntypes > 0)
        {
-               int     i;
+               int                     i;
 
                appendStringInfoString(buf, "\n TRANSFORM ");
                for (i = 0; i < ntypes; i++)
@@ -2672,15 +2713,61 @@ static void
 set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
                                 Bitmapset *rels_used)
 {
+       HASHCTL         hash_ctl;
+       HTAB       *names_hash;
+       NameHashEntry *hentry;
+       bool            found;
+       int                     rtindex;
        ListCell   *lc;
-       int                     rtindex = 1;
 
        dpns->rtable_names = NIL;
+       /* nothing more to do if empty rtable */
+       if (dpns->rtable == NIL)
+               return;
+
+       /*
+        * We use a hash table to hold known names, so that this process is O(N)
+        * not O(N^2) for N names.
+        */
+       MemSet(&hash_ctl, 0, sizeof(hash_ctl));
+       hash_ctl.keysize = NAMEDATALEN;
+       hash_ctl.entrysize = sizeof(NameHashEntry);
+       hash_ctl.hcxt = CurrentMemoryContext;
+       names_hash = hash_create("set_rtable_names names",
+                                                        list_length(dpns->rtable),
+                                                        &hash_ctl,
+                                                        HASH_ELEM | HASH_CONTEXT);
+       /* Preload the hash table with names appearing in parent_namespaces */
+       foreach(lc, parent_namespaces)
+       {
+               deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
+               ListCell   *lc2;
+
+               foreach(lc2, olddpns->rtable_names)
+               {
+                       char       *oldname = (char *) lfirst(lc2);
+
+                       if (oldname == NULL)
+                               continue;
+                       hentry = (NameHashEntry *) hash_search(names_hash,
+                                                                                                  oldname,
+                                                                                                  HASH_ENTER,
+                                                                                                  &found);
+                       /* we do not complain about duplicate names in parent namespaces */
+                       hentry->counter = 0;
+               }
+       }
+
+       /* Now we can scan the rtable */
+       rtindex = 1;
        foreach(lc, dpns->rtable)
        {
                RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
                char       *refname;
 
+               /* Just in case this takes an unreasonable amount of time ... */
+               CHECK_FOR_INTERRUPTS();
+
                if (rels_used && !bms_is_member(rtindex, rels_used))
                {
                        /* Ignore unreferenced RTE */
@@ -2708,56 +2795,62 @@ set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
                }
 
                /*
-                * If the selected name isn't unique, append digits to make it so
+                * If the selected name isn't unique, append digits to make it so, and
+                * make a new hash entry for it once we've got a unique name.  For a
+                * very long input name, we might have to truncate to stay within
+                * NAMEDATALEN.
                 */
-               if (refname &&
-                       !refname_is_unique(refname, dpns, parent_namespaces))
+               if (refname)
                {
-                       char       *modname = (char *) palloc(strlen(refname) + 32);
-                       int                     i = 0;
+                       hentry = (NameHashEntry *) hash_search(names_hash,
+                                                                                                  refname,
+                                                                                                  HASH_ENTER,
+                                                                                                  &found);
+                       if (found)
+                       {
+                               /* Name already in use, must choose a new one */
+                               int                     refnamelen = strlen(refname);
+                               char       *modname = (char *) palloc(refnamelen + 16);
+                               NameHashEntry *hentry2;
 
-                       do
+                               do
+                               {
+                                       hentry->counter++;
+                                       for (;;)
+                                       {
+                                               /*
+                                                * We avoid using %.*s here because it can misbehave
+                                                * if the data is not valid in what libc thinks is the
+                                                * prevailing encoding.
+                                                */
+                                               memcpy(modname, refname, refnamelen);
+                                               sprintf(modname + refnamelen, "_%d", hentry->counter);
+                                               if (strlen(modname) < NAMEDATALEN)
+                                                       break;
+                                               /* drop chars from refname to keep all the digits */
+                                               refnamelen = pg_mbcliplen(refname, refnamelen,
+                                                                                                 refnamelen - 1);
+                                       }
+                                       hentry2 = (NameHashEntry *) hash_search(names_hash,
+                                                                                                                       modname,
+                                                                                                                       HASH_ENTER,
+                                                                                                                       &found);
+                               } while (found);
+                               hentry2->counter = 0;   /* init new hash entry */
+                               refname = modname;
+                       }
+                       else
                        {
-                               sprintf(modname, "%s_%d", refname, ++i);
-                       } while (!refname_is_unique(modname, dpns, parent_namespaces));
-                       refname = modname;
+                               /* Name not previously used, need only initialize hentry */
+                               hentry->counter = 0;
+                       }
                }
 
                dpns->rtable_names = lappend(dpns->rtable_names, refname);
                rtindex++;
        }
-}
-
-/*
- * refname_is_unique: is refname distinct from all already-chosen RTE names?
- */
-static bool
-refname_is_unique(char *refname, deparse_namespace *dpns,
-                                 List *parent_namespaces)
-{
-       ListCell   *lc;
-
-       foreach(lc, dpns->rtable_names)
-       {
-               char       *oldname = (char *) lfirst(lc);
-
-               if (oldname && strcmp(oldname, refname) == 0)
-                       return false;
-       }
-       foreach(lc, parent_namespaces)
-       {
-               deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
-               ListCell   *lc2;
 
-               foreach(lc2, olddpns->rtable_names)
-               {
-                       char       *oldname = (char *) lfirst(lc2);
-
-                       if (oldname && strcmp(oldname, refname) == 0)
-                               return false;
-               }
-       }
-       return true;
+       hash_destroy(names_hash);
 }
 
 /*
@@ -3585,16 +3678,34 @@ make_colname_unique(char *colname, deparse_namespace *dpns,
                                        deparse_columns *colinfo)
 {
        /*
-        * If the selected name isn't unique, append digits to make it so
+        * If the selected name isn't unique, append digits to make it so.  For a
+        * very long input name, we might have to truncate to stay within
+        * NAMEDATALEN.
         */
        if (!colname_is_unique(colname, dpns, colinfo))
        {
-               char       *modname = (char *) palloc(strlen(colname) + 32);
+               int                     colnamelen = strlen(colname);
+               char       *modname = (char *) palloc(colnamelen + 16);
                int                     i = 0;
 
                do
                {
-                       sprintf(modname, "%s_%d", colname, ++i);
+                       i++;
+                       for (;;)
+                       {
+                               /*
+                                * We avoid using %.*s here because it can misbehave if the
+                                * data is not valid in what libc thinks is the prevailing
+                                * encoding.
+                                */
+                               memcpy(modname, colname, colnamelen);
+                               sprintf(modname + colnamelen, "_%d", i);
+                               if (strlen(modname) < NAMEDATALEN)
+                                       break;
+                               /* drop chars from colname to keep all the digits */
+                               colnamelen = pg_mbcliplen(colname, colnamelen,
+                                                                                 colnamelen - 1);
+                       }
                } while (!colname_is_unique(modname, dpns, colinfo));
                colname = modname;
        }
@@ -4232,50 +4343,6 @@ make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
        heap_close(ev_relation, AccessShareLock);
 }
 
-/* ----------
- * get_tablesample_def                 - Convert TableSampleClause back to SQL
- * ----------
- */
-static void
-get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
-{
-       StringInfo      buf = context->buf;
-       HeapTuple       tuple;
-       Form_pg_tablesample_method tsm;
-       char       *tsmname;
-       int                     nargs;
-       ListCell   *l;
-
-       /* Load the tablesample method */
-       tuple = SearchSysCache1(TABLESAMPLEMETHODOID, ObjectIdGetDatum(tablesample->tsmid));
-       if (!HeapTupleIsValid(tuple))
-               ereport(ERROR,
-                               (errcode(ERRCODE_UNDEFINED_OBJECT),
-                                errmsg("cache lookup failed for tablesample method %u",
-                                               tablesample->tsmid)));
-
-       tsm = (Form_pg_tablesample_method) GETSTRUCT(tuple);
-       tsmname = NameStr(tsm->tsmname);
-       appendStringInfo(buf, " TABLESAMPLE %s (", quote_identifier(tsmname));
-
-       ReleaseSysCache(tuple);
-
-       nargs = 0;
-       foreach(l, tablesample->args)
-       {
-               if (nargs++ > 0)
-                       appendStringInfoString(buf, ", ");
-               get_rule_expr((Node *) lfirst(l), context, true);
-       }
-       appendStringInfoChar(buf, ')');
-
-       if (tablesample->repeatable != NULL)
-       {
-               appendStringInfoString(buf, " REPEATABLE (");
-               get_rule_expr(tablesample->repeatable, context, true);
-               appendStringInfoChar(buf, ')');
-       }
-}
 
 /* ----------
  * get_query_def                       - Parse back one query parsetree
@@ -4389,10 +4456,10 @@ get_values_def(List *values_lists, deparse_context *context)
 
                        /*
                         * Strip any top-level nodes representing indirection assignments,
-                        * then print the result.
+                        * then print the result.  Whole-row Vars need special treatment.
                         */
-                       get_rule_expr(processIndirection(col, context, false),
-                                                 context, false);
+                       get_rule_expr_toplevel(processIndirection(col, context, false),
+                                                                  context, false);
                }
                appendStringInfoChar(buf, ')');
        }
@@ -4714,7 +4781,7 @@ get_basic_select_query(Query *query, deparse_context *context,
        /* Add the GROUP BY clause if given */
        if (query->groupClause != NULL || query->groupingSets != NULL)
        {
-               ParseExprKind   save_exprkind;
+               ParseExprKind save_exprkind;
 
                appendContextKeyword(context, " GROUP BY ",
                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
@@ -4813,7 +4880,8 @@ get_target_list(List *targetList, deparse_context *context,
                 * the top level of a SELECT list it's not right (the parser will
                 * expand that notation into multiple columns, yielding behavior
                 * different from a whole-row Var).  We need to call get_variable
-                * directly so that we can tell it to do the right thing.
+                * directly so that we can tell it to do the right thing, and so that
+                * we can get the attribute name which is the default AS label.
                 */
                if (tle->expr && (IsA(tle->expr, Var)))
                {
@@ -5045,13 +5113,13 @@ get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
        expr = (Node *) tle->expr;
 
        /*
-        * Use column-number form if requested by caller.  Otherwise, if expression
-        * is a constant, force it to be dumped with an explicit cast as decoration
-        * --- this is because a simple integer constant is ambiguous (and will be
-        * misinterpreted by findTargetlistEntry()) if we dump it without any
-        * decoration.  If it's anything more complex than a simple Var, then force
-        * extra parens around it, to ensure it can't be misinterpreted as a cube()
-        * or rollup() construct.
+        * Use column-number form if requested by caller.  Otherwise, if
+        * expression is a constant, force it to be dumped with an explicit cast
+        * as decoration --- this is because a simple integer constant is
+        * ambiguous (and will be misinterpreted by findTargetlistEntry()) if we
+        * dump it without any decoration.  If it's anything more complex than a
+        * simple Var, then force extra parens around it, to ensure it can't be
+        * misinterpreted as a cube() or rollup() construct.
         */
        if (force_colno)
        {
@@ -5067,14 +5135,15 @@ get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
                /*
                 * We must force parens for function-like expressions even if
                 * PRETTY_PAREN is off, since those are the ones in danger of
-                * misparsing. For other expressions we need to force them
-                * only if PRETTY_PAREN is on, since otherwise the expression
-                * will output them itself. (We can't skip the parens.)
+                * misparsing. For other expressions we need to force them only if
+                * PRETTY_PAREN is on, since otherwise the expression will output them
+                * itself. (We can't skip the parens.)
                 */
-               bool    need_paren = (PRETTY_PAREN(context)
-                                                         || IsA(expr, FuncExpr)
-                                                         || IsA(expr, Aggref)
-                                                         || IsA(expr, WindowFunc));
+               bool            need_paren = (PRETTY_PAREN(context)
+                                                                 || IsA(expr, FuncExpr)
+                                                                 ||IsA(expr, Aggref)
+                                                                 ||IsA(expr, WindowFunc));
+
                if (need_paren)
                        appendStringInfoString(context->buf, "(");
                get_rule_expr(expr, context, true);
@@ -5110,7 +5179,7 @@ get_rule_groupingset(GroupingSet *gset, List *targetlist,
 
                                foreach(l, gset->content)
                                {
-                                       Index ref = lfirst_int(l);
+                                       Index           ref = lfirst_int(l);
 
                                        appendStringInfoString(buf, sep);
                                        get_rule_sortgroupclause(ref, targetlist,
@@ -5483,7 +5552,7 @@ get_insert_query_def(Query *query, deparse_context *context)
        {
                OnConflictExpr *confl = query->onConflict;
 
-               appendStringInfo(buf, " ON CONFLICT");
+               appendStringInfoString(buf, " ON CONFLICT");
 
                if (confl->arbiterElems)
                {
@@ -5495,14 +5564,26 @@ get_insert_query_def(Query *query, deparse_context *context)
                        /* Add a WHERE clause (for partial indexes) if given */
                        if (confl->arbiterWhere != NULL)
                        {
+                               bool            save_varprefix;
+
+                               /*
+                                * Force non-prefixing of Vars, since parser assumes that they
+                                * belong to target relation.  WHERE clause does not use
+                                * InferenceElem, so this is separately required.
+                                */
+                               save_varprefix = context->varprefix;
+                               context->varprefix = false;
+
                                appendContextKeyword(context, " WHERE ",
                                                                         -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
                                get_rule_expr(confl->arbiterWhere, context, false);
+
+                               context->varprefix = save_varprefix;
                        }
                }
                else if (confl->constraint != InvalidOid)
                {
-                       char   *constraint = get_constraint_name(confl->constraint);
+                       char       *constraint = get_constraint_name(confl->constraint);
 
                        appendStringInfo(buf, " ON CONSTRAINT %s",
                                                         quote_qualified_identifier(NULL, constraint));
@@ -7556,7 +7637,8 @@ get_rule_expr(Node *node, deparse_context *context,
                                                !tupdesc->attrs[i]->attisdropped)
                                        {
                                                appendStringInfoString(buf, sep);
-                                               get_rule_expr(e, context, true);
+                                               /* Whole-row Vars need special treatment here */
+                                               get_rule_expr_toplevel(e, context, true);
                                                sep = ", ";
                                        }
                                        i++;
@@ -7917,14 +7999,15 @@ get_rule_expr(Node *node, deparse_context *context,
 
                case T_InferenceElem:
                        {
-                               InferenceElem  *iexpr = (InferenceElem *) node;
-                               bool                    varprefix = context->varprefix;
-                               bool                    need_parens;
+                               InferenceElem *iexpr = (InferenceElem *) node;
+                               bool            save_varprefix;
+                               bool            need_parens;
 
                                /*
                                 * InferenceElem can only refer to target relation, so a
-                                * prefix is never useful.
+                                * prefix is not useful, and indeed would cause parse errors.
                                 */
+                               save_varprefix = context->varprefix;
                                context->varprefix = false;
 
                                /*
@@ -7944,17 +8027,17 @@ get_rule_expr(Node *node, deparse_context *context,
                                if (need_parens)
                                        appendStringInfoChar(buf, ')');
 
-                               context->varprefix = varprefix;
+                               context->varprefix = save_varprefix;
 
                                if (iexpr->infercollid)
                                        appendStringInfo(buf, " COLLATE %s",
-                                                                        generate_collation_name(iexpr->infercollid));
+                                                               generate_collation_name(iexpr->infercollid));
 
                                /* Add the operator class name, if not default */
                                if (iexpr->inferopclass)
                                {
-                                       Oid             inferopclass = iexpr->inferopclass;
-                                       Oid             inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
+                                       Oid                     inferopclass = iexpr->inferopclass;
+                                       Oid                     inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
 
                                        get_opclass_name(inferopclass, inferopcinputtype, buf);
                                }
@@ -7982,6 +8065,27 @@ get_rule_expr(Node *node, deparse_context *context,
        }
 }
 
+/*
+ * get_rule_expr_toplevel              - Parse back a toplevel expression
+ *
+ * Same as get_rule_expr(), except that if the expr is just a Var, we pass
+ * istoplevel = true not false to get_variable().  This causes whole-row Vars
+ * to get printed with decoration that will prevent expansion of "*".
+ * We need to use this in contexts such as ROW() and VALUES(), where the
+ * parser would expand "foo.*" appearing at top level.  (In principle we'd
+ * use this in get_target_list() too, but that has additional worries about
+ * whether to print AS, so it needs to invoke get_variable() directly anyway.)
+ */
+static void
+get_rule_expr_toplevel(Node *node, deparse_context *context,
+                                          bool showimplicit)
+{
+       if (node && IsA(node, Var))
+               (void) get_variable((Var *) node, 0, true, context);
+       else
+               get_rule_expr(node, context, showimplicit);
+}
+
 
 /*
  * get_oper_expr                       - Parse back an OpExpr node
@@ -8777,9 +8881,6 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                                                                 only_marker(rte),
                                                                 generate_relation_name(rte->relid,
                                                                                                                context->namespaces));
-
-                               if (rte->tablesample)
-                                       get_tablesample_def(rte->tablesample, context);
                                break;
                        case RTE_SUBQUERY:
                                /* Subquery RTE */
@@ -8959,6 +9060,10 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
                        /* Else print column aliases as needed */
                        get_column_alias_list(colinfo, context);
                }
+
+               /* Tablesample clause must go after any alias */
+               if (rte->rtekind == RTE_RELATION && rte->tablesample)
+                       get_tablesample_def(rte->tablesample, context);
        }
        else if (IsA(jtnode, JoinExpr))
        {
@@ -9158,6 +9263,44 @@ get_from_clause_coldeflist(RangeTblFunction *rtfunc,
        appendStringInfoChar(buf, ')');
 }
 
+/*
+ * get_tablesample_def                 - print a TableSampleClause
+ */
+static void
+get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
+{
+       StringInfo      buf = context->buf;
+       Oid                     argtypes[1];
+       int                     nargs;
+       ListCell   *l;
+
+       /*
+        * We should qualify the handler's function name if it wouldn't be
+        * resolved by lookup in the current search path.
+        */
+       argtypes[0] = INTERNALOID;
+       appendStringInfo(buf, " TABLESAMPLE %s (",
+                                        generate_function_name(tablesample->tsmhandler, 1,
+                                                                                       NIL, argtypes,
+                                                                                       false, NULL, EXPR_KIND_NONE));
+
+       nargs = 0;
+       foreach(l, tablesample->args)
+       {
+               if (nargs++ > 0)
+                       appendStringInfoString(buf, ", ");
+               get_rule_expr((Node *) lfirst(l), context, false);
+       }
+       appendStringInfoChar(buf, ')');
+
+       if (tablesample->repeatable != NULL)
+       {
+               appendStringInfoString(buf, " REPEATABLE (");
+               get_rule_expr((Node *) tablesample->repeatable, context, false);
+               appendStringInfoChar(buf, ')');
+       }
+}
+
 /*
  * get_opclass_name                    - fetch name of an index operator class
  *
@@ -9280,10 +9423,12 @@ printSubscripts(ArrayRef *aref, deparse_context *context)
                appendStringInfoChar(buf, '[');
                if (lowlist_item)
                {
+                       /* If subexpression is NULL, get_rule_expr prints nothing */
                        get_rule_expr((Node *) lfirst(lowlist_item), context, false);
                        appendStringInfoChar(buf, ':');
                        lowlist_item = lnext(lowlist_item);
                }
+               /* If subexpression is NULL, get_rule_expr prints nothing */
                get_rule_expr((Node *) lfirst(uplist_item), context, false);
                appendStringInfoChar(buf, ']');
        }
@@ -9475,6 +9620,39 @@ generate_relation_name(Oid relid, List *namespaces)
        return result;
 }
 
+/*
+ * generate_qualified_relation_name
+ *             Compute the name to display for a relation specified by OID
+ *
+ * As above, but unconditionally schema-qualify the name.
+ */
+static char *
+generate_qualified_relation_name(Oid relid)
+{
+       HeapTuple       tp;
+       Form_pg_class reltup;
+       char       *relname;
+       char       *nspname;
+       char       *result;
+
+       tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
+       if (!HeapTupleIsValid(tp))
+               elog(ERROR, "cache lookup failed for relation %u", relid);
+       reltup = (Form_pg_class) GETSTRUCT(tp);
+       relname = NameStr(reltup->relname);
+
+       nspname = get_namespace_name(reltup->relnamespace);
+       if (!nspname)
+               elog(ERROR, "cache lookup failed for namespace %u",
+                        reltup->relnamespace);
+
+       result = quote_qualified_identifier(nspname, relname);
+
+       ReleaseSysCache(tp);
+
+       return result;
+}
+
 /*
  * generate_function_name
  *             Compute the name to display for a function specified by OID,
@@ -9731,18 +9909,59 @@ flatten_reloptions(Oid relid)
                                                                 Anum_pg_class_reloptions, &isnull);
        if (!isnull)
        {
-               Datum           sep,
-                                       txt;
+               StringInfoData buf;
+               Datum      *options;
+               int                     noptions;
+               int                     i;
 
-               /*
-                * We want to use array_to_text(reloptions, ', ') --- but
-                * DirectFunctionCall2(array_to_text) does not work, because
-                * array_to_text() relies on flinfo to be valid.  So use
-                * OidFunctionCall2.
-                */
-               sep = CStringGetTextDatum(", ");
-               txt = OidFunctionCall2(F_ARRAY_TO_TEXT, reloptions, sep);
-               result = TextDatumGetCString(txt);
+               initStringInfo(&buf);
+
+               deconstruct_array(DatumGetArrayTypeP(reloptions),
+                                                 TEXTOID, -1, false, 'i',
+                                                 &options, NULL, &noptions);
+
+               for (i = 0; i < noptions; i++)
+               {
+                       char       *option = TextDatumGetCString(options[i]);
+                       char       *name;
+                       char       *separator;
+                       char       *value;
+
+                       /*
+                        * Each array element should have the form name=value.  If the "="
+                        * is missing for some reason, treat it like an empty value.
+                        */
+                       name = option;
+                       separator = strchr(option, '=');
+                       if (separator)
+                       {
+                               *separator = '\0';
+                               value = separator + 1;
+                       }
+                       else
+                               value = "";
+
+                       if (i > 0)
+                               appendStringInfoString(&buf, ", ");
+                       appendStringInfo(&buf, "%s=", quote_identifier(name));
+
+                       /*
+                        * In general we need to quote the value; but to avoid unnecessary
+                        * clutter, do not quote if it is an identifier that would not
+                        * need quoting.  (We could also allow numbers, but that is a bit
+                        * trickier than it looks --- for example, are leading zeroes
+                        * significant?  We don't want to assume very much here about what
+                        * custom reloptions might mean.)
+                        */
+                       if (quote_identifier(value) == value)
+                               appendStringInfoString(&buf, value);
+                       else
+                               simple_quote_literal(&buf, value);
+
+                       pfree(option);
+               }
+
+               result = buf.data;
        }
 
        ReleaseSysCache(tuple);