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