LCOV - code coverage report
Current view: top level - src/backend/utils/adt - ruleutils.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 90.7 % 5272 4780
Test Date: 2026-03-12 06:14:44 Functions: 99.4 % 171 170
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * ruleutils.c
       4              :  *    Functions to convert stored expressions/querytrees back to
       5              :  *    source text
       6              :  *
       7              :  * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
       8              :  * Portions Copyright (c) 1994, Regents of the University of California
       9              :  *
      10              :  *
      11              :  * IDENTIFICATION
      12              :  *    src/backend/utils/adt/ruleutils.c
      13              :  *
      14              :  *-------------------------------------------------------------------------
      15              :  */
      16              : #include "postgres.h"
      17              : 
      18              : #include <ctype.h>
      19              : #include <unistd.h>
      20              : #include <fcntl.h>
      21              : 
      22              : #include "access/amapi.h"
      23              : #include "access/htup_details.h"
      24              : #include "access/relation.h"
      25              : #include "access/table.h"
      26              : #include "catalog/pg_aggregate.h"
      27              : #include "catalog/pg_am.h"
      28              : #include "catalog/pg_authid.h"
      29              : #include "catalog/pg_collation.h"
      30              : #include "catalog/pg_constraint.h"
      31              : #include "catalog/pg_depend.h"
      32              : #include "catalog/pg_language.h"
      33              : #include "catalog/pg_opclass.h"
      34              : #include "catalog/pg_operator.h"
      35              : #include "catalog/pg_partitioned_table.h"
      36              : #include "catalog/pg_proc.h"
      37              : #include "catalog/pg_statistic_ext.h"
      38              : #include "catalog/pg_trigger.h"
      39              : #include "catalog/pg_type.h"
      40              : #include "commands/defrem.h"
      41              : #include "commands/tablespace.h"
      42              : #include "common/keywords.h"
      43              : #include "executor/spi.h"
      44              : #include "funcapi.h"
      45              : #include "mb/pg_wchar.h"
      46              : #include "miscadmin.h"
      47              : #include "nodes/makefuncs.h"
      48              : #include "nodes/nodeFuncs.h"
      49              : #include "nodes/pathnodes.h"
      50              : #include "optimizer/optimizer.h"
      51              : #include "parser/parse_agg.h"
      52              : #include "parser/parse_func.h"
      53              : #include "parser/parse_oper.h"
      54              : #include "parser/parse_relation.h"
      55              : #include "parser/parser.h"
      56              : #include "parser/parsetree.h"
      57              : #include "rewrite/rewriteHandler.h"
      58              : #include "rewrite/rewriteManip.h"
      59              : #include "rewrite/rewriteSupport.h"
      60              : #include "utils/array.h"
      61              : #include "utils/builtins.h"
      62              : #include "utils/fmgroids.h"
      63              : #include "utils/guc.h"
      64              : #include "utils/hsearch.h"
      65              : #include "utils/lsyscache.h"
      66              : #include "utils/partcache.h"
      67              : #include "utils/rel.h"
      68              : #include "utils/ruleutils.h"
      69              : #include "utils/snapmgr.h"
      70              : #include "utils/syscache.h"
      71              : #include "utils/typcache.h"
      72              : #include "utils/varlena.h"
      73              : #include "utils/xml.h"
      74              : 
      75              : /* ----------
      76              :  * Pretty formatting constants
      77              :  * ----------
      78              :  */
      79              : 
      80              : /* Indent counts */
      81              : #define PRETTYINDENT_STD        8
      82              : #define PRETTYINDENT_JOIN       4
      83              : #define PRETTYINDENT_VAR        4
      84              : 
      85              : #define PRETTYINDENT_LIMIT      40  /* wrap limit */
      86              : 
      87              : /* Pretty flags */
      88              : #define PRETTYFLAG_PAREN        0x0001
      89              : #define PRETTYFLAG_INDENT       0x0002
      90              : #define PRETTYFLAG_SCHEMA       0x0004
      91              : 
      92              : /* Standard conversion of a "bool pretty" option to detailed flags */
      93              : #define GET_PRETTY_FLAGS(pretty) \
      94              :     ((pretty) ? (PRETTYFLAG_PAREN | PRETTYFLAG_INDENT | PRETTYFLAG_SCHEMA) \
      95              :      : PRETTYFLAG_INDENT)
      96              : 
      97              : /* Default line length for pretty-print wrapping: 0 means wrap always */
      98              : #define WRAP_COLUMN_DEFAULT     0
      99              : 
     100              : /* macros to test if pretty action needed */
     101              : #define PRETTY_PAREN(context)   ((context)->prettyFlags & PRETTYFLAG_PAREN)
     102              : #define PRETTY_INDENT(context)  ((context)->prettyFlags & PRETTYFLAG_INDENT)
     103              : #define PRETTY_SCHEMA(context)  ((context)->prettyFlags & PRETTYFLAG_SCHEMA)
     104              : 
     105              : 
     106              : /* ----------
     107              :  * Local data types
     108              :  * ----------
     109              :  */
     110              : 
     111              : /* Context info needed for invoking a recursive querytree display routine */
     112              : typedef struct
     113              : {
     114              :     StringInfo  buf;            /* output buffer to append to */
     115              :     List       *namespaces;     /* List of deparse_namespace nodes */
     116              :     TupleDesc   resultDesc;     /* if top level of a view, the view's tupdesc */
     117              :     List       *targetList;     /* Current query level's SELECT targetlist */
     118              :     List       *windowClause;   /* Current query level's WINDOW clause */
     119              :     int         prettyFlags;    /* enabling of pretty-print functions */
     120              :     int         wrapColumn;     /* max line length, or -1 for no limit */
     121              :     int         indentLevel;    /* current indent level for pretty-print */
     122              :     bool        varprefix;      /* true to print prefixes on Vars */
     123              :     bool        colNamesVisible;    /* do we care about output column names? */
     124              :     bool        inGroupBy;      /* deparsing GROUP BY clause? */
     125              :     bool        varInOrderBy;   /* deparsing simple Var in ORDER BY? */
     126              :     Bitmapset  *appendparents;  /* if not null, map child Vars of these relids
     127              :                                  * back to the parent rel */
     128              : } deparse_context;
     129              : 
     130              : /*
     131              :  * Each level of query context around a subtree needs a level of Var namespace.
     132              :  * A Var having varlevelsup=N refers to the N'th item (counting from 0) in
     133              :  * the current context's namespaces list.
     134              :  *
     135              :  * rtable is the list of actual RTEs from the Query or PlannedStmt.
     136              :  * rtable_names holds the alias name to be used for each RTE (either a C
     137              :  * string, or NULL for nameless RTEs such as unnamed joins).
     138              :  * rtable_columns holds the column alias names to be used for each RTE.
     139              :  *
     140              :  * subplans is a list of Plan trees for SubPlans and CTEs (it's only used
     141              :  * in the PlannedStmt case).
     142              :  * ctes is a list of CommonTableExpr nodes (only used in the Query case).
     143              :  * appendrels, if not null (it's only used in the PlannedStmt case), is an
     144              :  * array of AppendRelInfo nodes, indexed by child relid.  We use that to map
     145              :  * child-table Vars to their inheritance parents.
     146              :  *
     147              :  * In some cases we need to make names of merged JOIN USING columns unique
     148              :  * across the whole query, not only per-RTE.  If so, unique_using is true
     149              :  * and using_names is a list of C strings representing names already assigned
     150              :  * to USING columns.
     151              :  *
     152              :  * When deparsing plan trees, there is always just a single item in the
     153              :  * deparse_namespace list (since a plan tree never contains Vars with
     154              :  * varlevelsup > 0).  We store the Plan node that is the immediate
     155              :  * parent of the expression to be deparsed, as well as a list of that
     156              :  * Plan's ancestors.  In addition, we store its outer and inner subplan nodes,
     157              :  * as well as their targetlists, and the index tlist if the current plan node
     158              :  * might contain INDEX_VAR Vars.  (These fields could be derived on-the-fly
     159              :  * from the current Plan node, but it seems notationally clearer to set them
     160              :  * up as separate fields.)
     161              :  */
     162              : typedef struct
     163              : {
     164              :     List       *rtable;         /* List of RangeTblEntry nodes */
     165              :     List       *rtable_names;   /* Parallel list of names for RTEs */
     166              :     List       *rtable_columns; /* Parallel list of deparse_columns structs */
     167              :     List       *subplans;       /* List of Plan trees for SubPlans */
     168              :     List       *ctes;           /* List of CommonTableExpr nodes */
     169              :     AppendRelInfo **appendrels; /* Array of AppendRelInfo nodes, or NULL */
     170              :     char       *ret_old_alias;  /* alias for OLD in RETURNING list */
     171              :     char       *ret_new_alias;  /* alias for NEW in RETURNING list */
     172              :     /* Workspace for column alias assignment: */
     173              :     bool        unique_using;   /* Are we making USING names globally unique */
     174              :     List       *using_names;    /* List of assigned names for USING columns */
     175              :     /* Remaining fields are used only when deparsing a Plan tree: */
     176              :     Plan       *plan;           /* immediate parent of current expression */
     177              :     List       *ancestors;      /* ancestors of plan */
     178              :     Plan       *outer_plan;     /* outer subnode, or NULL if none */
     179              :     Plan       *inner_plan;     /* inner subnode, or NULL if none */
     180              :     List       *outer_tlist;    /* referent for OUTER_VAR Vars */
     181              :     List       *inner_tlist;    /* referent for INNER_VAR Vars */
     182              :     List       *index_tlist;    /* referent for INDEX_VAR Vars */
     183              :     /* Special namespace representing a function signature: */
     184              :     char       *funcname;
     185              :     int         numargs;
     186              :     char      **argnames;
     187              : } deparse_namespace;
     188              : 
     189              : /*
     190              :  * Per-relation data about column alias names.
     191              :  *
     192              :  * Selecting aliases is unreasonably complicated because of the need to dump
     193              :  * rules/views whose underlying tables may have had columns added, deleted, or
     194              :  * renamed since the query was parsed.  We must nonetheless print the rule/view
     195              :  * in a form that can be reloaded and will produce the same results as before.
     196              :  *
     197              :  * For each RTE used in the query, we must assign column aliases that are
     198              :  * unique within that RTE.  SQL does not require this of the original query,
     199              :  * but due to factors such as *-expansion we need to be able to uniquely
     200              :  * reference every column in a decompiled query.  As long as we qualify all
     201              :  * column references, per-RTE uniqueness is sufficient for that.
     202              :  *
     203              :  * However, we can't ensure per-column name uniqueness for unnamed join RTEs,
     204              :  * since they just inherit column names from their input RTEs, and we can't
     205              :  * rename the columns at the join level.  Most of the time this isn't an issue
     206              :  * because we don't need to reference the join's output columns as such; we
     207              :  * can reference the input columns instead.  That approach can fail for merged
     208              :  * JOIN USING columns, however, so when we have one of those in an unnamed
     209              :  * join, we have to make that column's alias globally unique across the whole
     210              :  * query to ensure it can be referenced unambiguously.
     211              :  *
     212              :  * Another problem is that a JOIN USING clause requires the columns to be
     213              :  * merged to have the same aliases in both input RTEs, and that no other
     214              :  * columns in those RTEs or their children conflict with the USING names.
     215              :  * To handle that, we do USING-column alias assignment in a recursive
     216              :  * traversal of the query's jointree.  When descending through a JOIN with
     217              :  * USING, we preassign the USING column names to the child columns, overriding
     218              :  * other rules for column alias assignment.  We also mark each RTE with a list
     219              :  * of all USING column names selected for joins containing that RTE, so that
     220              :  * when we assign other columns' aliases later, we can avoid conflicts.
     221              :  *
     222              :  * Another problem is that if a JOIN's input tables have had columns added or
     223              :  * deleted since the query was parsed, we must generate a column alias list
     224              :  * for the join that matches the current set of input columns --- otherwise, a
     225              :  * change in the number of columns in the left input would throw off matching
     226              :  * of aliases to columns of the right input.  Thus, positions in the printable
     227              :  * column alias list are not necessarily one-for-one with varattnos of the
     228              :  * JOIN, so we need a separate new_colnames[] array for printing purposes.
     229              :  *
     230              :  * Finally, when dealing with wide tables we risk O(N^2) costs in assigning
     231              :  * non-duplicate column names.  We ameliorate that by using a hash table that
     232              :  * holds all the strings appearing in colnames, new_colnames, and parentUsing.
     233              :  */
     234              : typedef struct
     235              : {
     236              :     /*
     237              :      * colnames is an array containing column aliases to use for columns that
     238              :      * existed when the query was parsed.  Dropped columns have NULL entries.
     239              :      * This array can be directly indexed by varattno to get a Var's name.
     240              :      *
     241              :      * Non-NULL entries are guaranteed unique within the RTE, *except* when
     242              :      * this is for an unnamed JOIN RTE.  In that case we merely copy up names
     243              :      * from the two input RTEs.
     244              :      *
     245              :      * During the recursive descent in set_using_names(), forcible assignment
     246              :      * of a child RTE's column name is represented by pre-setting that element
     247              :      * of the child's colnames array.  So at that stage, NULL entries in this
     248              :      * array just mean that no name has been preassigned, not necessarily that
     249              :      * the column is dropped.
     250              :      */
     251              :     int         num_cols;       /* length of colnames[] array */
     252              :     char      **colnames;       /* array of C strings and NULLs */
     253              : 
     254              :     /*
     255              :      * new_colnames is an array containing column aliases to use for columns
     256              :      * that would exist if the query was re-parsed against the current
     257              :      * definitions of its base tables.  This is what to print as the column
     258              :      * alias list for the RTE.  This array does not include dropped columns,
     259              :      * but it will include columns added since original parsing.  Indexes in
     260              :      * it therefore have little to do with current varattno values.  As above,
     261              :      * entries are unique unless this is for an unnamed JOIN RTE.  (In such an
     262              :      * RTE, we never actually print this array, but we must compute it anyway
     263              :      * for possible use in computing column names of upper joins.) The
     264              :      * parallel array is_new_col marks which of these columns are new since
     265              :      * original parsing.  Entries with is_new_col false must match the
     266              :      * non-NULL colnames entries one-for-one.
     267              :      */
     268              :     int         num_new_cols;   /* length of new_colnames[] array */
     269              :     char      **new_colnames;   /* array of C strings */
     270              :     bool       *is_new_col;     /* array of bool flags */
     271              : 
     272              :     /* This flag tells whether we should actually print a column alias list */
     273              :     bool        printaliases;
     274              : 
     275              :     /* This list has all names used as USING names in joins above this RTE */
     276              :     List       *parentUsing;    /* names assigned to parent merged columns */
     277              : 
     278              :     /*
     279              :      * If this struct is for a JOIN RTE, we fill these fields during the
     280              :      * set_using_names() pass to describe its relationship to its child RTEs.
     281              :      *
     282              :      * leftattnos and rightattnos are arrays with one entry per existing
     283              :      * output column of the join (hence, indexable by join varattno).  For a
     284              :      * simple reference to a column of the left child, leftattnos[i] is the
     285              :      * child RTE's attno and rightattnos[i] is zero; and conversely for a
     286              :      * column of the right child.  But for merged columns produced by JOIN
     287              :      * USING/NATURAL JOIN, both leftattnos[i] and rightattnos[i] are nonzero.
     288              :      * Note that a simple reference might be to a child RTE column that's been
     289              :      * dropped; but that's OK since the column could not be used in the query.
     290              :      *
     291              :      * If it's a JOIN USING, usingNames holds the alias names selected for the
     292              :      * merged columns (these might be different from the original USING list,
     293              :      * if we had to modify names to achieve uniqueness).
     294              :      */
     295              :     int         leftrti;        /* rangetable index of left child */
     296              :     int         rightrti;       /* rangetable index of right child */
     297              :     int        *leftattnos;     /* left-child varattnos of join cols, or 0 */
     298              :     int        *rightattnos;    /* right-child varattnos of join cols, or 0 */
     299              :     List       *usingNames;     /* names assigned to merged columns */
     300              : 
     301              :     /*
     302              :      * Hash table holding copies of all the strings appearing in this struct's
     303              :      * colnames, new_colnames, and parentUsing.  We use a hash table only for
     304              :      * sufficiently wide relations, and only during the colname-assignment
     305              :      * functions set_relation_column_names and set_join_column_names;
     306              :      * otherwise, names_hash is NULL.
     307              :      */
     308              :     HTAB       *names_hash;     /* entries are just strings */
     309              : } deparse_columns;
     310              : 
     311              : /* This macro is analogous to rt_fetch(), but for deparse_columns structs */
     312              : #define deparse_columns_fetch(rangetable_index, dpns) \
     313              :     ((deparse_columns *) list_nth((dpns)->rtable_columns, (rangetable_index)-1))
     314              : 
     315              : /*
     316              :  * Entry in set_rtable_names' hash table
     317              :  */
     318              : typedef struct
     319              : {
     320              :     char        name[NAMEDATALEN];  /* Hash key --- must be first */
     321              :     int         counter;        /* Largest addition used so far for name */
     322              : } NameHashEntry;
     323              : 
     324              : /* Callback signature for resolve_special_varno() */
     325              : typedef void (*rsv_callback) (Node *node, deparse_context *context,
     326              :                               void *callback_arg);
     327              : 
     328              : 
     329              : /* ----------
     330              :  * Global data
     331              :  * ----------
     332              :  */
     333              : static SPIPlanPtr plan_getrulebyoid = NULL;
     334              : static const char *const query_getrulebyoid = "SELECT * FROM pg_catalog.pg_rewrite WHERE oid = $1";
     335              : static SPIPlanPtr plan_getviewrule = NULL;
     336              : static const char *const query_getviewrule = "SELECT * FROM pg_catalog.pg_rewrite WHERE ev_class = $1 AND rulename = $2";
     337              : 
     338              : /* GUC parameters */
     339              : bool        quote_all_identifiers = false;
     340              : 
     341              : 
     342              : /* ----------
     343              :  * Local functions
     344              :  *
     345              :  * Most of these functions used to use fixed-size buffers to build their
     346              :  * results.  Now, they take an (already initialized) StringInfo object
     347              :  * as a parameter, and append their text output to its contents.
     348              :  * ----------
     349              :  */
     350              : static char *deparse_expression_pretty(Node *expr, List *dpcontext,
     351              :                                        bool forceprefix, bool showimplicit,
     352              :                                        int prettyFlags, int startIndent);
     353              : static char *pg_get_viewdef_worker(Oid viewoid,
     354              :                                    int prettyFlags, int wrapColumn);
     355              : static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
     356              : static int  decompile_column_index_array(Datum column_index_array, Oid relId,
     357              :                                          bool withPeriod, StringInfo buf);
     358              : static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
     359              : static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
     360              :                                     const Oid *excludeOps,
     361              :                                     bool attrsOnly, bool keysOnly,
     362              :                                     bool showTblSpc, bool inherits,
     363              :                                     int prettyFlags, bool missing_ok);
     364              : static char *pg_get_statisticsobj_worker(Oid statextid, bool columns_only,
     365              :                                          bool missing_ok);
     366              : static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
     367              :                                       bool attrsOnly, bool missing_ok);
     368              : static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
     369              :                                          int prettyFlags, bool missing_ok);
     370              : static text *pg_get_expr_worker(text *expr, Oid relid, int prettyFlags);
     371              : static int  print_function_arguments(StringInfo buf, HeapTuple proctup,
     372              :                                      bool print_table_args, bool print_defaults);
     373              : static void print_function_rettype(StringInfo buf, HeapTuple proctup);
     374              : static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
     375              : static void print_function_sqlbody(StringInfo buf, HeapTuple proctup);
     376              : static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
     377              :                              Bitmapset *rels_used);
     378              : static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
     379              :                                   List *parent_namespaces);
     380              : static void set_simple_column_names(deparse_namespace *dpns);
     381              : static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
     382              : static void set_using_names(deparse_namespace *dpns, Node *jtnode,
     383              :                             List *parentUsing);
     384              : static void set_relation_column_names(deparse_namespace *dpns,
     385              :                                       RangeTblEntry *rte,
     386              :                                       deparse_columns *colinfo);
     387              : static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
     388              :                                   deparse_columns *colinfo);
     389              : static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
     390              :                               deparse_columns *colinfo);
     391              : static char *make_colname_unique(char *colname, deparse_namespace *dpns,
     392              :                                  deparse_columns *colinfo);
     393              : static void expand_colnames_array_to(deparse_columns *colinfo, int n);
     394              : static void build_colinfo_names_hash(deparse_columns *colinfo);
     395              : static void add_to_names_hash(deparse_columns *colinfo, const char *name);
     396              : static void destroy_colinfo_names_hash(deparse_columns *colinfo);
     397              : static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
     398              :                                   deparse_columns *colinfo);
     399              : static char *get_rtable_name(int rtindex, deparse_context *context);
     400              : static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
     401              : static Plan *find_recursive_union(deparse_namespace *dpns,
     402              :                                   WorkTableScan *wtscan);
     403              : static void push_child_plan(deparse_namespace *dpns, Plan *plan,
     404              :                             deparse_namespace *save_dpns);
     405              : static void pop_child_plan(deparse_namespace *dpns,
     406              :                            deparse_namespace *save_dpns);
     407              : static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
     408              :                                deparse_namespace *save_dpns);
     409              : static void pop_ancestor_plan(deparse_namespace *dpns,
     410              :                               deparse_namespace *save_dpns);
     411              : static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
     412              :                          int prettyFlags);
     413              : static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
     414              :                          int prettyFlags, int wrapColumn);
     415              : static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
     416              :                           TupleDesc resultDesc, bool colNamesVisible,
     417              :                           int prettyFlags, int wrapColumn, int startIndent);
     418              : static void get_values_def(List *values_lists, deparse_context *context);
     419              : static void get_with_clause(Query *query, deparse_context *context);
     420              : static void get_select_query_def(Query *query, deparse_context *context);
     421              : static void get_insert_query_def(Query *query, deparse_context *context);
     422              : static void get_update_query_def(Query *query, deparse_context *context);
     423              : static void get_update_query_targetlist_def(Query *query, List *targetList,
     424              :                                             deparse_context *context,
     425              :                                             RangeTblEntry *rte);
     426              : static void get_delete_query_def(Query *query, deparse_context *context);
     427              : static void get_merge_query_def(Query *query, deparse_context *context);
     428              : static void get_utility_query_def(Query *query, deparse_context *context);
     429              : static char *get_lock_clause_strength(LockClauseStrength strength);
     430              : static void get_basic_select_query(Query *query, deparse_context *context);
     431              : static void get_target_list(List *targetList, deparse_context *context);
     432              : static void get_returning_clause(Query *query, deparse_context *context);
     433              : static void get_setop_query(Node *setOp, Query *query,
     434              :                             deparse_context *context);
     435              : static Node *get_rule_sortgroupclause(Index ref, List *tlist,
     436              :                                       bool force_colno,
     437              :                                       deparse_context *context);
     438              : static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
     439              :                                  bool omit_parens, deparse_context *context);
     440              : static void get_rule_orderby(List *orderList, List *targetList,
     441              :                              bool force_colno, deparse_context *context);
     442              : static void get_rule_windowclause(Query *query, deparse_context *context);
     443              : static void get_rule_windowspec(WindowClause *wc, List *targetList,
     444              :                                 deparse_context *context);
     445              : static void get_window_frame_options(int frameOptions,
     446              :                                      Node *startOffset, Node *endOffset,
     447              :                                      deparse_context *context);
     448              : static char *get_variable(Var *var, int levelsup, bool istoplevel,
     449              :                           deparse_context *context);
     450              : static void get_special_variable(Node *node, deparse_context *context,
     451              :                                  void *callback_arg);
     452              : static void resolve_special_varno(Node *node, deparse_context *context,
     453              :                                   rsv_callback callback, void *callback_arg);
     454              : static Node *find_param_referent(Param *param, deparse_context *context,
     455              :                                  deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
     456              : static SubPlan *find_param_generator(Param *param, deparse_context *context,
     457              :                                      int *column_p);
     458              : static SubPlan *find_param_generator_initplan(Param *param, Plan *plan,
     459              :                                               int *column_p);
     460              : static void get_parameter(Param *param, deparse_context *context);
     461              : static const char *get_simple_binary_op_name(OpExpr *expr);
     462              : static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
     463              : static void appendContextKeyword(deparse_context *context, const char *str,
     464              :                                  int indentBefore, int indentAfter, int indentPlus);
     465              : static void removeStringInfoSpaces(StringInfo str);
     466              : static void get_rule_expr(Node *node, deparse_context *context,
     467              :                           bool showimplicit);
     468              : static void get_rule_expr_toplevel(Node *node, deparse_context *context,
     469              :                                    bool showimplicit);
     470              : static void get_rule_list_toplevel(List *lst, deparse_context *context,
     471              :                                    bool showimplicit);
     472              : static void get_rule_expr_funccall(Node *node, deparse_context *context,
     473              :                                    bool showimplicit);
     474              : static bool looks_like_function(Node *node);
     475              : static void get_oper_expr(OpExpr *expr, deparse_context *context);
     476              : static void get_func_expr(FuncExpr *expr, deparse_context *context,
     477              :                           bool showimplicit);
     478              : static void get_agg_expr(Aggref *aggref, deparse_context *context,
     479              :                          Aggref *original_aggref);
     480              : static void get_agg_expr_helper(Aggref *aggref, deparse_context *context,
     481              :                                 Aggref *original_aggref, const char *funcname,
     482              :                                 const char *options, bool is_json_objectagg);
     483              : static void get_agg_combine_expr(Node *node, deparse_context *context,
     484              :                                  void *callback_arg);
     485              : static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
     486              : static void get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
     487              :                                        const char *funcname, const char *options,
     488              :                                        bool is_json_objectagg);
     489              : static bool get_func_sql_syntax(FuncExpr *expr, deparse_context *context);
     490              : static void get_coercion_expr(Node *arg, deparse_context *context,
     491              :                               Oid resulttype, int32 resulttypmod,
     492              :                               Node *parentNode);
     493              : static void get_const_expr(Const *constval, deparse_context *context,
     494              :                            int showtype);
     495              : static void get_const_collation(Const *constval, deparse_context *context);
     496              : static void get_json_format(JsonFormat *format, StringInfo buf);
     497              : static void get_json_returning(JsonReturning *returning, StringInfo buf,
     498              :                                bool json_format_by_default);
     499              : static void get_json_constructor(JsonConstructorExpr *ctor,
     500              :                                  deparse_context *context, bool showimplicit);
     501              : static void get_json_constructor_options(JsonConstructorExpr *ctor,
     502              :                                          StringInfo buf);
     503              : static void get_json_agg_constructor(JsonConstructorExpr *ctor,
     504              :                                      deparse_context *context,
     505              :                                      const char *funcname,
     506              :                                      bool is_json_objectagg);
     507              : static void simple_quote_literal(StringInfo buf, const char *val);
     508              : static void get_sublink_expr(SubLink *sublink, deparse_context *context);
     509              : static void get_tablefunc(TableFunc *tf, deparse_context *context,
     510              :                           bool showimplicit);
     511              : static void get_from_clause(Query *query, const char *prefix,
     512              :                             deparse_context *context);
     513              : static void get_from_clause_item(Node *jtnode, Query *query,
     514              :                                  deparse_context *context);
     515              : static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
     516              :                           deparse_context *context);
     517              : static void get_column_alias_list(deparse_columns *colinfo,
     518              :                                   deparse_context *context);
     519              : static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
     520              :                                        deparse_columns *colinfo,
     521              :                                        deparse_context *context);
     522              : static void get_tablesample_def(TableSampleClause *tablesample,
     523              :                                 deparse_context *context);
     524              : static void get_opclass_name(Oid opclass, Oid actual_datatype,
     525              :                              StringInfo buf);
     526              : static Node *processIndirection(Node *node, deparse_context *context);
     527              : static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
     528              : static char *get_relation_name(Oid relid);
     529              : static char *generate_relation_name(Oid relid, List *namespaces);
     530              : static char *generate_qualified_relation_name(Oid relid);
     531              : static char *generate_function_name(Oid funcid, int nargs,
     532              :                                     List *argnames, Oid *argtypes,
     533              :                                     bool has_variadic, bool *use_variadic_p,
     534              :                                     bool inGroupBy);
     535              : static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
     536              : static void add_cast_to(StringInfo buf, Oid typid);
     537              : static char *generate_qualified_type_name(Oid typid);
     538              : static text *string_to_text(char *str);
     539              : static char *flatten_reloptions(Oid relid);
     540              : static void get_reloptions(StringInfo buf, Datum reloptions);
     541              : static void get_json_path_spec(Node *path_spec, deparse_context *context,
     542              :                                bool showimplicit);
     543              : static void get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
     544              :                                    deparse_context *context,
     545              :                                    bool showimplicit);
     546              : static void get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
     547              :                                           deparse_context *context,
     548              :                                           bool showimplicit,
     549              :                                           bool needcomma);
     550              : 
     551              : #define only_marker(rte)  ((rte)->inh ? "" : "ONLY ")
     552              : 
     553              : 
     554              : /* ----------
     555              :  * pg_get_ruledef       - Do it all and return a text
     556              :  *                that could be used as a statement
     557              :  *                to recreate the rule
     558              :  * ----------
     559              :  */
     560              : Datum
     561          228 : pg_get_ruledef(PG_FUNCTION_ARGS)
     562              : {
     563          228 :     Oid         ruleoid = PG_GETARG_OID(0);
     564              :     int         prettyFlags;
     565              :     char       *res;
     566              : 
     567          228 :     prettyFlags = PRETTYFLAG_INDENT;
     568              : 
     569          228 :     res = pg_get_ruledef_worker(ruleoid, prettyFlags);
     570              : 
     571          228 :     if (res == NULL)
     572            3 :         PG_RETURN_NULL();
     573              : 
     574          225 :     PG_RETURN_TEXT_P(string_to_text(res));
     575              : }
     576              : 
     577              : 
     578              : Datum
     579           57 : pg_get_ruledef_ext(PG_FUNCTION_ARGS)
     580              : {
     581           57 :     Oid         ruleoid = PG_GETARG_OID(0);
     582           57 :     bool        pretty = PG_GETARG_BOOL(1);
     583              :     int         prettyFlags;
     584              :     char       *res;
     585              : 
     586           57 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
     587              : 
     588           57 :     res = pg_get_ruledef_worker(ruleoid, prettyFlags);
     589              : 
     590           57 :     if (res == NULL)
     591            0 :         PG_RETURN_NULL();
     592              : 
     593           57 :     PG_RETURN_TEXT_P(string_to_text(res));
     594              : }
     595              : 
     596              : 
     597              : static char *
     598          285 : pg_get_ruledef_worker(Oid ruleoid, int prettyFlags)
     599              : {
     600              :     Datum       args[1];
     601              :     char        nulls[1];
     602              :     int         spirc;
     603              :     HeapTuple   ruletup;
     604              :     TupleDesc   rulettc;
     605              :     StringInfoData buf;
     606              : 
     607              :     /*
     608              :      * Do this first so that string is alloc'd in outer context not SPI's.
     609              :      */
     610          285 :     initStringInfo(&buf);
     611              : 
     612              :     /*
     613              :      * Connect to SPI manager
     614              :      */
     615          285 :     SPI_connect();
     616              : 
     617              :     /*
     618              :      * On the first call prepare the plan to lookup pg_rewrite. We read
     619              :      * pg_rewrite over the SPI manager instead of using the syscache to be
     620              :      * checked for read access on pg_rewrite.
     621              :      */
     622          285 :     if (plan_getrulebyoid == NULL)
     623              :     {
     624              :         Oid         argtypes[1];
     625              :         SPIPlanPtr  plan;
     626              : 
     627           20 :         argtypes[0] = OIDOID;
     628           20 :         plan = SPI_prepare(query_getrulebyoid, 1, argtypes);
     629           20 :         if (plan == NULL)
     630            0 :             elog(ERROR, "SPI_prepare failed for \"%s\"", query_getrulebyoid);
     631           20 :         SPI_keepplan(plan);
     632           20 :         plan_getrulebyoid = plan;
     633              :     }
     634              : 
     635              :     /*
     636              :      * Get the pg_rewrite tuple for this rule
     637              :      */
     638          285 :     args[0] = ObjectIdGetDatum(ruleoid);
     639          285 :     nulls[0] = ' ';
     640          285 :     spirc = SPI_execute_plan(plan_getrulebyoid, args, nulls, true, 0);
     641          285 :     if (spirc != SPI_OK_SELECT)
     642            0 :         elog(ERROR, "failed to get pg_rewrite tuple for rule %u", ruleoid);
     643          285 :     if (SPI_processed != 1)
     644              :     {
     645              :         /*
     646              :          * There is no tuple data available here, just keep the output buffer
     647              :          * empty.
     648              :          */
     649              :     }
     650              :     else
     651              :     {
     652              :         /*
     653              :          * Get the rule's definition and put it into executor's memory
     654              :          */
     655          282 :         ruletup = SPI_tuptable->vals[0];
     656          282 :         rulettc = SPI_tuptable->tupdesc;
     657          282 :         make_ruledef(&buf, ruletup, rulettc, prettyFlags);
     658              :     }
     659              : 
     660              :     /*
     661              :      * Disconnect from SPI manager
     662              :      */
     663          285 :     if (SPI_finish() != SPI_OK_FINISH)
     664            0 :         elog(ERROR, "SPI_finish failed");
     665              : 
     666          285 :     if (buf.len == 0)
     667            3 :         return NULL;
     668              : 
     669          282 :     return buf.data;
     670              : }
     671              : 
     672              : 
     673              : /* ----------
     674              :  * pg_get_viewdef       - Mainly the same thing, but we
     675              :  *                only return the SELECT part of a view
     676              :  * ----------
     677              :  */
     678              : Datum
     679         1249 : pg_get_viewdef(PG_FUNCTION_ARGS)
     680              : {
     681              :     /* By OID */
     682         1249 :     Oid         viewoid = PG_GETARG_OID(0);
     683              :     int         prettyFlags;
     684              :     char       *res;
     685              : 
     686         1249 :     prettyFlags = PRETTYFLAG_INDENT;
     687              : 
     688         1249 :     res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
     689              : 
     690         1249 :     if (res == NULL)
     691            3 :         PG_RETURN_NULL();
     692              : 
     693         1246 :     PG_RETURN_TEXT_P(string_to_text(res));
     694              : }
     695              : 
     696              : 
     697              : Datum
     698          282 : pg_get_viewdef_ext(PG_FUNCTION_ARGS)
     699              : {
     700              :     /* By OID */
     701          282 :     Oid         viewoid = PG_GETARG_OID(0);
     702          282 :     bool        pretty = PG_GETARG_BOOL(1);
     703              :     int         prettyFlags;
     704              :     char       *res;
     705              : 
     706          282 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
     707              : 
     708          282 :     res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
     709              : 
     710          282 :     if (res == NULL)
     711            0 :         PG_RETURN_NULL();
     712              : 
     713          282 :     PG_RETURN_TEXT_P(string_to_text(res));
     714              : }
     715              : 
     716              : Datum
     717            3 : pg_get_viewdef_wrap(PG_FUNCTION_ARGS)
     718              : {
     719              :     /* By OID */
     720            3 :     Oid         viewoid = PG_GETARG_OID(0);
     721            3 :     int         wrap = PG_GETARG_INT32(1);
     722              :     int         prettyFlags;
     723              :     char       *res;
     724              : 
     725              :     /* calling this implies we want pretty printing */
     726            3 :     prettyFlags = GET_PRETTY_FLAGS(true);
     727              : 
     728            3 :     res = pg_get_viewdef_worker(viewoid, prettyFlags, wrap);
     729              : 
     730            3 :     if (res == NULL)
     731            0 :         PG_RETURN_NULL();
     732              : 
     733            3 :     PG_RETURN_TEXT_P(string_to_text(res));
     734              : }
     735              : 
     736              : Datum
     737           39 : pg_get_viewdef_name(PG_FUNCTION_ARGS)
     738              : {
     739              :     /* By qualified name */
     740           39 :     text       *viewname = PG_GETARG_TEXT_PP(0);
     741              :     int         prettyFlags;
     742              :     RangeVar   *viewrel;
     743              :     Oid         viewoid;
     744              :     char       *res;
     745              : 
     746           39 :     prettyFlags = PRETTYFLAG_INDENT;
     747              : 
     748              :     /* Look up view name.  Can't lock it - we might not have privileges. */
     749           39 :     viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
     750           39 :     viewoid = RangeVarGetRelid(viewrel, NoLock, false);
     751              : 
     752           39 :     res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
     753              : 
     754           39 :     if (res == NULL)
     755            0 :         PG_RETURN_NULL();
     756              : 
     757           39 :     PG_RETURN_TEXT_P(string_to_text(res));
     758              : }
     759              : 
     760              : 
     761              : Datum
     762          201 : pg_get_viewdef_name_ext(PG_FUNCTION_ARGS)
     763              : {
     764              :     /* By qualified name */
     765          201 :     text       *viewname = PG_GETARG_TEXT_PP(0);
     766          201 :     bool        pretty = PG_GETARG_BOOL(1);
     767              :     int         prettyFlags;
     768              :     RangeVar   *viewrel;
     769              :     Oid         viewoid;
     770              :     char       *res;
     771              : 
     772          201 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
     773              : 
     774              :     /* Look up view name.  Can't lock it - we might not have privileges. */
     775          201 :     viewrel = makeRangeVarFromNameList(textToQualifiedNameList(viewname));
     776          201 :     viewoid = RangeVarGetRelid(viewrel, NoLock, false);
     777              : 
     778          201 :     res = pg_get_viewdef_worker(viewoid, prettyFlags, WRAP_COLUMN_DEFAULT);
     779              : 
     780          201 :     if (res == NULL)
     781            0 :         PG_RETURN_NULL();
     782              : 
     783          201 :     PG_RETURN_TEXT_P(string_to_text(res));
     784              : }
     785              : 
     786              : /*
     787              :  * Common code for by-OID and by-name variants of pg_get_viewdef
     788              :  */
     789              : static char *
     790         1774 : pg_get_viewdef_worker(Oid viewoid, int prettyFlags, int wrapColumn)
     791              : {
     792              :     Datum       args[2];
     793              :     char        nulls[2];
     794              :     int         spirc;
     795              :     HeapTuple   ruletup;
     796              :     TupleDesc   rulettc;
     797              :     StringInfoData buf;
     798              : 
     799              :     /*
     800              :      * Do this first so that string is alloc'd in outer context not SPI's.
     801              :      */
     802         1774 :     initStringInfo(&buf);
     803              : 
     804              :     /*
     805              :      * Connect to SPI manager
     806              :      */
     807         1774 :     SPI_connect();
     808              : 
     809              :     /*
     810              :      * On the first call prepare the plan to lookup pg_rewrite. We read
     811              :      * pg_rewrite over the SPI manager instead of using the syscache to be
     812              :      * checked for read access on pg_rewrite.
     813              :      */
     814         1774 :     if (plan_getviewrule == NULL)
     815              :     {
     816              :         Oid         argtypes[2];
     817              :         SPIPlanPtr  plan;
     818              : 
     819          124 :         argtypes[0] = OIDOID;
     820          124 :         argtypes[1] = NAMEOID;
     821          124 :         plan = SPI_prepare(query_getviewrule, 2, argtypes);
     822          124 :         if (plan == NULL)
     823            0 :             elog(ERROR, "SPI_prepare failed for \"%s\"", query_getviewrule);
     824          124 :         SPI_keepplan(plan);
     825          124 :         plan_getviewrule = plan;
     826              :     }
     827              : 
     828              :     /*
     829              :      * Get the pg_rewrite tuple for the view's SELECT rule
     830              :      */
     831         1774 :     args[0] = ObjectIdGetDatum(viewoid);
     832         1774 :     args[1] = DirectFunctionCall1(namein, CStringGetDatum(ViewSelectRuleName));
     833         1774 :     nulls[0] = ' ';
     834         1774 :     nulls[1] = ' ';
     835         1774 :     spirc = SPI_execute_plan(plan_getviewrule, args, nulls, true, 0);
     836         1774 :     if (spirc != SPI_OK_SELECT)
     837            0 :         elog(ERROR, "failed to get pg_rewrite tuple for view %u", viewoid);
     838         1774 :     if (SPI_processed != 1)
     839              :     {
     840              :         /*
     841              :          * There is no tuple data available here, just keep the output buffer
     842              :          * empty.
     843              :          */
     844              :     }
     845              :     else
     846              :     {
     847              :         /*
     848              :          * Get the rule's definition and put it into executor's memory
     849              :          */
     850         1771 :         ruletup = SPI_tuptable->vals[0];
     851         1771 :         rulettc = SPI_tuptable->tupdesc;
     852         1771 :         make_viewdef(&buf, ruletup, rulettc, prettyFlags, wrapColumn);
     853              :     }
     854              : 
     855              :     /*
     856              :      * Disconnect from SPI manager
     857              :      */
     858         1774 :     if (SPI_finish() != SPI_OK_FINISH)
     859            0 :         elog(ERROR, "SPI_finish failed");
     860              : 
     861         1774 :     if (buf.len == 0)
     862            3 :         return NULL;
     863              : 
     864         1771 :     return buf.data;
     865              : }
     866              : 
     867              : /* ----------
     868              :  * pg_get_triggerdef        - Get the definition of a trigger
     869              :  * ----------
     870              :  */
     871              : Datum
     872           82 : pg_get_triggerdef(PG_FUNCTION_ARGS)
     873              : {
     874           82 :     Oid         trigid = PG_GETARG_OID(0);
     875              :     char       *res;
     876              : 
     877           82 :     res = pg_get_triggerdef_worker(trigid, false);
     878              : 
     879           82 :     if (res == NULL)
     880            3 :         PG_RETURN_NULL();
     881              : 
     882           79 :     PG_RETURN_TEXT_P(string_to_text(res));
     883              : }
     884              : 
     885              : Datum
     886          613 : pg_get_triggerdef_ext(PG_FUNCTION_ARGS)
     887              : {
     888          613 :     Oid         trigid = PG_GETARG_OID(0);
     889          613 :     bool        pretty = PG_GETARG_BOOL(1);
     890              :     char       *res;
     891              : 
     892          613 :     res = pg_get_triggerdef_worker(trigid, pretty);
     893              : 
     894          613 :     if (res == NULL)
     895            0 :         PG_RETURN_NULL();
     896              : 
     897          613 :     PG_RETURN_TEXT_P(string_to_text(res));
     898              : }
     899              : 
     900              : static char *
     901          695 : pg_get_triggerdef_worker(Oid trigid, bool pretty)
     902              : {
     903              :     HeapTuple   ht_trig;
     904              :     Form_pg_trigger trigrec;
     905              :     StringInfoData buf;
     906              :     Relation    tgrel;
     907              :     ScanKeyData skey[1];
     908              :     SysScanDesc tgscan;
     909          695 :     int         findx = 0;
     910              :     char       *tgname;
     911              :     char       *tgoldtable;
     912              :     char       *tgnewtable;
     913              :     Datum       value;
     914              :     bool        isnull;
     915              : 
     916              :     /*
     917              :      * Fetch the pg_trigger tuple by the Oid of the trigger
     918              :      */
     919          695 :     tgrel = table_open(TriggerRelationId, AccessShareLock);
     920              : 
     921          695 :     ScanKeyInit(&skey[0],
     922              :                 Anum_pg_trigger_oid,
     923              :                 BTEqualStrategyNumber, F_OIDEQ,
     924              :                 ObjectIdGetDatum(trigid));
     925              : 
     926          695 :     tgscan = systable_beginscan(tgrel, TriggerOidIndexId, true,
     927              :                                 NULL, 1, skey);
     928              : 
     929          695 :     ht_trig = systable_getnext(tgscan);
     930              : 
     931          695 :     if (!HeapTupleIsValid(ht_trig))
     932              :     {
     933            3 :         systable_endscan(tgscan);
     934            3 :         table_close(tgrel, AccessShareLock);
     935            3 :         return NULL;
     936              :     }
     937              : 
     938          692 :     trigrec = (Form_pg_trigger) GETSTRUCT(ht_trig);
     939              : 
     940              :     /*
     941              :      * Start the trigger definition. Note that the trigger's name should never
     942              :      * be schema-qualified, but the trigger rel's name may be.
     943              :      */
     944          692 :     initStringInfo(&buf);
     945              : 
     946          692 :     tgname = NameStr(trigrec->tgname);
     947         1384 :     appendStringInfo(&buf, "CREATE %sTRIGGER %s ",
     948          692 :                      OidIsValid(trigrec->tgconstraint) ? "CONSTRAINT " : "",
     949              :                      quote_identifier(tgname));
     950              : 
     951          692 :     if (TRIGGER_FOR_BEFORE(trigrec->tgtype))
     952          265 :         appendStringInfoString(&buf, "BEFORE");
     953          427 :     else if (TRIGGER_FOR_AFTER(trigrec->tgtype))
     954          415 :         appendStringInfoString(&buf, "AFTER");
     955           12 :     else if (TRIGGER_FOR_INSTEAD(trigrec->tgtype))
     956           12 :         appendStringInfoString(&buf, "INSTEAD OF");
     957              :     else
     958            0 :         elog(ERROR, "unexpected tgtype value: %d", trigrec->tgtype);
     959              : 
     960          692 :     if (TRIGGER_FOR_INSERT(trigrec->tgtype))
     961              :     {
     962          483 :         appendStringInfoString(&buf, " INSERT");
     963          483 :         findx++;
     964              :     }
     965          692 :     if (TRIGGER_FOR_DELETE(trigrec->tgtype))
     966              :     {
     967          105 :         if (findx > 0)
     968           45 :             appendStringInfoString(&buf, " OR DELETE");
     969              :         else
     970           60 :             appendStringInfoString(&buf, " DELETE");
     971          105 :         findx++;
     972              :     }
     973          692 :     if (TRIGGER_FOR_UPDATE(trigrec->tgtype))
     974              :     {
     975          324 :         if (findx > 0)
     976          175 :             appendStringInfoString(&buf, " OR UPDATE");
     977              :         else
     978          149 :             appendStringInfoString(&buf, " UPDATE");
     979          324 :         findx++;
     980              :         /* tgattr is first var-width field, so OK to access directly */
     981          324 :         if (trigrec->tgattr.dim1 > 0)
     982              :         {
     983              :             int         i;
     984              : 
     985           38 :             appendStringInfoString(&buf, " OF ");
     986           84 :             for (i = 0; i < trigrec->tgattr.dim1; i++)
     987              :             {
     988              :                 char       *attname;
     989              : 
     990           46 :                 if (i > 0)
     991            8 :                     appendStringInfoString(&buf, ", ");
     992           46 :                 attname = get_attname(trigrec->tgrelid,
     993           46 :                                       trigrec->tgattr.values[i], false);
     994           46 :                 appendStringInfoString(&buf, quote_identifier(attname));
     995              :             }
     996              :         }
     997              :     }
     998          692 :     if (TRIGGER_FOR_TRUNCATE(trigrec->tgtype))
     999              :     {
    1000            0 :         if (findx > 0)
    1001            0 :             appendStringInfoString(&buf, " OR TRUNCATE");
    1002              :         else
    1003            0 :             appendStringInfoString(&buf, " TRUNCATE");
    1004            0 :         findx++;
    1005              :     }
    1006              : 
    1007              :     /*
    1008              :      * In non-pretty mode, always schema-qualify the target table name for
    1009              :      * safety.  In pretty mode, schema-qualify only if not visible.
    1010              :      */
    1011         1384 :     appendStringInfo(&buf, " ON %s ",
    1012              :                      pretty ?
    1013           87 :                      generate_relation_name(trigrec->tgrelid, NIL) :
    1014          605 :                      generate_qualified_relation_name(trigrec->tgrelid));
    1015              : 
    1016          692 :     if (OidIsValid(trigrec->tgconstraint))
    1017              :     {
    1018            0 :         if (OidIsValid(trigrec->tgconstrrelid))
    1019            0 :             appendStringInfo(&buf, "FROM %s ",
    1020              :                              generate_relation_name(trigrec->tgconstrrelid, NIL));
    1021            0 :         if (!trigrec->tgdeferrable)
    1022            0 :             appendStringInfoString(&buf, "NOT ");
    1023            0 :         appendStringInfoString(&buf, "DEFERRABLE INITIALLY ");
    1024            0 :         if (trigrec->tginitdeferred)
    1025            0 :             appendStringInfoString(&buf, "DEFERRED ");
    1026              :         else
    1027            0 :             appendStringInfoString(&buf, "IMMEDIATE ");
    1028              :     }
    1029              : 
    1030          692 :     value = fastgetattr(ht_trig, Anum_pg_trigger_tgoldtable,
    1031              :                         tgrel->rd_att, &isnull);
    1032          692 :     if (!isnull)
    1033           49 :         tgoldtable = NameStr(*DatumGetName(value));
    1034              :     else
    1035          643 :         tgoldtable = NULL;
    1036          692 :     value = fastgetattr(ht_trig, Anum_pg_trigger_tgnewtable,
    1037              :                         tgrel->rd_att, &isnull);
    1038          692 :     if (!isnull)
    1039           54 :         tgnewtable = NameStr(*DatumGetName(value));
    1040              :     else
    1041          638 :         tgnewtable = NULL;
    1042          692 :     if (tgoldtable != NULL || tgnewtable != NULL)
    1043              :     {
    1044           76 :         appendStringInfoString(&buf, "REFERENCING ");
    1045           76 :         if (tgoldtable != NULL)
    1046           49 :             appendStringInfo(&buf, "OLD TABLE AS %s ",
    1047              :                              quote_identifier(tgoldtable));
    1048           76 :         if (tgnewtable != NULL)
    1049           54 :             appendStringInfo(&buf, "NEW TABLE AS %s ",
    1050              :                              quote_identifier(tgnewtable));
    1051              :     }
    1052              : 
    1053          692 :     if (TRIGGER_FOR_ROW(trigrec->tgtype))
    1054          533 :         appendStringInfoString(&buf, "FOR EACH ROW ");
    1055              :     else
    1056          159 :         appendStringInfoString(&buf, "FOR EACH STATEMENT ");
    1057              : 
    1058              :     /* If the trigger has a WHEN qualification, add that */
    1059          692 :     value = fastgetattr(ht_trig, Anum_pg_trigger_tgqual,
    1060              :                         tgrel->rd_att, &isnull);
    1061          692 :     if (!isnull)
    1062              :     {
    1063              :         Node       *qual;
    1064              :         char        relkind;
    1065              :         deparse_context context;
    1066              :         deparse_namespace dpns;
    1067              :         RangeTblEntry *oldrte;
    1068              :         RangeTblEntry *newrte;
    1069              : 
    1070           76 :         appendStringInfoString(&buf, "WHEN (");
    1071              : 
    1072           76 :         qual = stringToNode(TextDatumGetCString(value));
    1073              : 
    1074           76 :         relkind = get_rel_relkind(trigrec->tgrelid);
    1075              : 
    1076              :         /* Build minimal OLD and NEW RTEs for the rel */
    1077           76 :         oldrte = makeNode(RangeTblEntry);
    1078           76 :         oldrte->rtekind = RTE_RELATION;
    1079           76 :         oldrte->relid = trigrec->tgrelid;
    1080           76 :         oldrte->relkind = relkind;
    1081           76 :         oldrte->rellockmode = AccessShareLock;
    1082           76 :         oldrte->alias = makeAlias("old", NIL);
    1083           76 :         oldrte->eref = oldrte->alias;
    1084           76 :         oldrte->lateral = false;
    1085           76 :         oldrte->inh = false;
    1086           76 :         oldrte->inFromCl = true;
    1087              : 
    1088           76 :         newrte = makeNode(RangeTblEntry);
    1089           76 :         newrte->rtekind = RTE_RELATION;
    1090           76 :         newrte->relid = trigrec->tgrelid;
    1091           76 :         newrte->relkind = relkind;
    1092           76 :         newrte->rellockmode = AccessShareLock;
    1093           76 :         newrte->alias = makeAlias("new", NIL);
    1094           76 :         newrte->eref = newrte->alias;
    1095           76 :         newrte->lateral = false;
    1096           76 :         newrte->inh = false;
    1097           76 :         newrte->inFromCl = true;
    1098              : 
    1099              :         /* Build two-element rtable */
    1100           76 :         memset(&dpns, 0, sizeof(dpns));
    1101           76 :         dpns.rtable = list_make2(oldrte, newrte);
    1102           76 :         dpns.subplans = NIL;
    1103           76 :         dpns.ctes = NIL;
    1104           76 :         dpns.appendrels = NULL;
    1105           76 :         set_rtable_names(&dpns, NIL, NULL);
    1106           76 :         set_simple_column_names(&dpns);
    1107              : 
    1108              :         /* Set up context with one-deep namespace stack */
    1109           76 :         context.buf = &buf;
    1110           76 :         context.namespaces = list_make1(&dpns);
    1111           76 :         context.resultDesc = NULL;
    1112           76 :         context.targetList = NIL;
    1113           76 :         context.windowClause = NIL;
    1114           76 :         context.varprefix = true;
    1115           76 :         context.prettyFlags = GET_PRETTY_FLAGS(pretty);
    1116           76 :         context.wrapColumn = WRAP_COLUMN_DEFAULT;
    1117           76 :         context.indentLevel = PRETTYINDENT_STD;
    1118           76 :         context.colNamesVisible = true;
    1119           76 :         context.inGroupBy = false;
    1120           76 :         context.varInOrderBy = false;
    1121           76 :         context.appendparents = NULL;
    1122              : 
    1123           76 :         get_rule_expr(qual, &context, false);
    1124              : 
    1125           76 :         appendStringInfoString(&buf, ") ");
    1126              :     }
    1127              : 
    1128          692 :     appendStringInfo(&buf, "EXECUTE FUNCTION %s(",
    1129              :                      generate_function_name(trigrec->tgfoid, 0,
    1130              :                                             NIL, NULL,
    1131              :                                             false, NULL, false));
    1132              : 
    1133          692 :     if (trigrec->tgnargs > 0)
    1134              :     {
    1135              :         char       *p;
    1136              :         int         i;
    1137              : 
    1138          223 :         value = fastgetattr(ht_trig, Anum_pg_trigger_tgargs,
    1139              :                             tgrel->rd_att, &isnull);
    1140          223 :         if (isnull)
    1141            0 :             elog(ERROR, "tgargs is null for trigger %u", trigid);
    1142          223 :         p = (char *) VARDATA_ANY(DatumGetByteaPP(value));
    1143          502 :         for (i = 0; i < trigrec->tgnargs; i++)
    1144              :         {
    1145          279 :             if (i > 0)
    1146           56 :                 appendStringInfoString(&buf, ", ");
    1147          279 :             simple_quote_literal(&buf, p);
    1148              :             /* advance p to next string embedded in tgargs */
    1149         2746 :             while (*p)
    1150         2467 :                 p++;
    1151          279 :             p++;
    1152              :         }
    1153              :     }
    1154              : 
    1155              :     /* We deliberately do not put semi-colon at end */
    1156          692 :     appendStringInfoChar(&buf, ')');
    1157              : 
    1158              :     /* Clean up */
    1159          692 :     systable_endscan(tgscan);
    1160              : 
    1161          692 :     table_close(tgrel, AccessShareLock);
    1162              : 
    1163          692 :     return buf.data;
    1164              : }
    1165              : 
    1166              : /* ----------
    1167              :  * pg_get_indexdef          - Get the definition of an index
    1168              :  *
    1169              :  * In the extended version, there is a colno argument as well as pretty bool.
    1170              :  *  if colno == 0, we want a complete index definition.
    1171              :  *  if colno > 0, we only want the Nth index key's variable or expression.
    1172              :  *
    1173              :  * Note that the SQL-function versions of this omit any info about the
    1174              :  * index tablespace; this is intentional because pg_dump wants it that way.
    1175              :  * However pg_get_indexdef_string() includes the index tablespace.
    1176              :  * ----------
    1177              :  */
    1178              : Datum
    1179         2855 : pg_get_indexdef(PG_FUNCTION_ARGS)
    1180              : {
    1181         2855 :     Oid         indexrelid = PG_GETARG_OID(0);
    1182              :     int         prettyFlags;
    1183              :     char       *res;
    1184              : 
    1185         2855 :     prettyFlags = PRETTYFLAG_INDENT;
    1186              : 
    1187         2855 :     res = pg_get_indexdef_worker(indexrelid, 0, NULL,
    1188              :                                  false, false,
    1189              :                                  false, false,
    1190              :                                  prettyFlags, true);
    1191              : 
    1192         2855 :     if (res == NULL)
    1193            3 :         PG_RETURN_NULL();
    1194              : 
    1195         2852 :     PG_RETURN_TEXT_P(string_to_text(res));
    1196              : }
    1197              : 
    1198              : Datum
    1199         1015 : pg_get_indexdef_ext(PG_FUNCTION_ARGS)
    1200              : {
    1201         1015 :     Oid         indexrelid = PG_GETARG_OID(0);
    1202         1015 :     int32       colno = PG_GETARG_INT32(1);
    1203         1015 :     bool        pretty = PG_GETARG_BOOL(2);
    1204              :     int         prettyFlags;
    1205              :     char       *res;
    1206              : 
    1207         1015 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
    1208              : 
    1209         1015 :     res = pg_get_indexdef_worker(indexrelid, colno, NULL,
    1210              :                                  colno != 0, false,
    1211              :                                  false, false,
    1212              :                                  prettyFlags, true);
    1213              : 
    1214         1015 :     if (res == NULL)
    1215            0 :         PG_RETURN_NULL();
    1216              : 
    1217         1015 :     PG_RETURN_TEXT_P(string_to_text(res));
    1218              : }
    1219              : 
    1220              : /*
    1221              :  * Internal version for use by ALTER TABLE.
    1222              :  * Includes a tablespace clause in the result.
    1223              :  * Returns a palloc'd C string; no pretty-printing.
    1224              :  */
    1225              : char *
    1226          117 : pg_get_indexdef_string(Oid indexrelid)
    1227              : {
    1228          117 :     return pg_get_indexdef_worker(indexrelid, 0, NULL,
    1229              :                                   false, false,
    1230              :                                   true, true,
    1231              :                                   0, false);
    1232              : }
    1233              : 
    1234              : /* Internal version that just reports the key-column definitions */
    1235              : char *
    1236          559 : pg_get_indexdef_columns(Oid indexrelid, bool pretty)
    1237              : {
    1238              :     int         prettyFlags;
    1239              : 
    1240          559 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
    1241              : 
    1242          559 :     return pg_get_indexdef_worker(indexrelid, 0, NULL,
    1243              :                                   true, true,
    1244              :                                   false, false,
    1245              :                                   prettyFlags, false);
    1246              : }
    1247              : 
    1248              : /* Internal version, extensible with flags to control its behavior */
    1249              : char *
    1250            4 : pg_get_indexdef_columns_extended(Oid indexrelid, bits16 flags)
    1251              : {
    1252            4 :     bool        pretty = ((flags & RULE_INDEXDEF_PRETTY) != 0);
    1253            4 :     bool        keys_only = ((flags & RULE_INDEXDEF_KEYS_ONLY) != 0);
    1254              :     int         prettyFlags;
    1255              : 
    1256            4 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
    1257              : 
    1258            4 :     return pg_get_indexdef_worker(indexrelid, 0, NULL,
    1259              :                                   true, keys_only,
    1260              :                                   false, false,
    1261              :                                   prettyFlags, false);
    1262              : }
    1263              : 
    1264              : /*
    1265              :  * Internal workhorse to decompile an index definition.
    1266              :  *
    1267              :  * This is now used for exclusion constraints as well: if excludeOps is not
    1268              :  * NULL then it points to an array of exclusion operator OIDs.
    1269              :  */
    1270              : static char *
    1271         4602 : pg_get_indexdef_worker(Oid indexrelid, int colno,
    1272              :                        const Oid *excludeOps,
    1273              :                        bool attrsOnly, bool keysOnly,
    1274              :                        bool showTblSpc, bool inherits,
    1275              :                        int prettyFlags, bool missing_ok)
    1276              : {
    1277              :     /* might want a separate isConstraint parameter later */
    1278         4602 :     bool        isConstraint = (excludeOps != NULL);
    1279              :     HeapTuple   ht_idx;
    1280              :     HeapTuple   ht_idxrel;
    1281              :     HeapTuple   ht_am;
    1282              :     Form_pg_index idxrec;
    1283              :     Form_pg_class idxrelrec;
    1284              :     Form_pg_am  amrec;
    1285              :     const IndexAmRoutine *amroutine;
    1286              :     List       *indexprs;
    1287              :     ListCell   *indexpr_item;
    1288              :     List       *context;
    1289              :     Oid         indrelid;
    1290              :     int         keyno;
    1291              :     Datum       indcollDatum;
    1292              :     Datum       indclassDatum;
    1293              :     Datum       indoptionDatum;
    1294              :     oidvector  *indcollation;
    1295              :     oidvector  *indclass;
    1296              :     int2vector *indoption;
    1297              :     StringInfoData buf;
    1298              :     char       *str;
    1299              :     char       *sep;
    1300              : 
    1301              :     /*
    1302              :      * Fetch the pg_index tuple by the Oid of the index
    1303              :      */
    1304         4602 :     ht_idx = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexrelid));
    1305         4602 :     if (!HeapTupleIsValid(ht_idx))
    1306              :     {
    1307            3 :         if (missing_ok)
    1308            3 :             return NULL;
    1309            0 :         elog(ERROR, "cache lookup failed for index %u", indexrelid);
    1310              :     }
    1311         4599 :     idxrec = (Form_pg_index) GETSTRUCT(ht_idx);
    1312              : 
    1313         4599 :     indrelid = idxrec->indrelid;
    1314              :     Assert(indexrelid == idxrec->indexrelid);
    1315              : 
    1316              :     /* Must get indcollation, indclass, and indoption the hard way */
    1317         4599 :     indcollDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
    1318              :                                           Anum_pg_index_indcollation);
    1319         4599 :     indcollation = (oidvector *) DatumGetPointer(indcollDatum);
    1320              : 
    1321         4599 :     indclassDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
    1322              :                                            Anum_pg_index_indclass);
    1323         4599 :     indclass = (oidvector *) DatumGetPointer(indclassDatum);
    1324              : 
    1325         4599 :     indoptionDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
    1326              :                                             Anum_pg_index_indoption);
    1327         4599 :     indoption = (int2vector *) DatumGetPointer(indoptionDatum);
    1328              : 
    1329              :     /*
    1330              :      * Fetch the pg_class tuple of the index relation
    1331              :      */
    1332         4599 :     ht_idxrel = SearchSysCache1(RELOID, ObjectIdGetDatum(indexrelid));
    1333         4599 :     if (!HeapTupleIsValid(ht_idxrel))
    1334            0 :         elog(ERROR, "cache lookup failed for relation %u", indexrelid);
    1335         4599 :     idxrelrec = (Form_pg_class) GETSTRUCT(ht_idxrel);
    1336              : 
    1337              :     /*
    1338              :      * Fetch the pg_am tuple of the index' access method
    1339              :      */
    1340         4599 :     ht_am = SearchSysCache1(AMOID, ObjectIdGetDatum(idxrelrec->relam));
    1341         4599 :     if (!HeapTupleIsValid(ht_am))
    1342            0 :         elog(ERROR, "cache lookup failed for access method %u",
    1343              :              idxrelrec->relam);
    1344         4599 :     amrec = (Form_pg_am) GETSTRUCT(ht_am);
    1345              : 
    1346              :     /* Fetch the index AM's API struct */
    1347         4599 :     amroutine = GetIndexAmRoutine(amrec->amhandler);
    1348              : 
    1349              :     /*
    1350              :      * Get the index expressions, if any.  (NOTE: we do not use the relcache
    1351              :      * versions of the expressions and predicate, because we want to display
    1352              :      * non-const-folded expressions.)
    1353              :      */
    1354         4599 :     if (!heap_attisnull(ht_idx, Anum_pg_index_indexprs, NULL))
    1355              :     {
    1356              :         Datum       exprsDatum;
    1357              :         char       *exprsString;
    1358              : 
    1359          329 :         exprsDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
    1360              :                                             Anum_pg_index_indexprs);
    1361          329 :         exprsString = TextDatumGetCString(exprsDatum);
    1362          329 :         indexprs = (List *) stringToNode(exprsString);
    1363          329 :         pfree(exprsString);
    1364              :     }
    1365              :     else
    1366         4270 :         indexprs = NIL;
    1367              : 
    1368         4599 :     indexpr_item = list_head(indexprs);
    1369              : 
    1370         4599 :     context = deparse_context_for(get_relation_name(indrelid), indrelid);
    1371              : 
    1372              :     /*
    1373              :      * Start the index definition.  Note that the index's name should never be
    1374              :      * schema-qualified, but the indexed rel's name may be.
    1375              :      */
    1376         4599 :     initStringInfo(&buf);
    1377              : 
    1378         4599 :     if (!attrsOnly)
    1379              :     {
    1380         3805 :         if (!isConstraint)
    1381         7506 :             appendStringInfo(&buf, "CREATE %sINDEX %s ON %s%s USING %s (",
    1382         3753 :                              idxrec->indisunique ? "UNIQUE " : "",
    1383         3753 :                              quote_identifier(NameStr(idxrelrec->relname)),
    1384         3753 :                              idxrelrec->relkind == RELKIND_PARTITIONED_INDEX
    1385          350 :                              && !inherits ? "ONLY " : "",
    1386         3753 :                              (prettyFlags & PRETTYFLAG_SCHEMA) ?
    1387          784 :                              generate_relation_name(indrelid, NIL) :
    1388         2969 :                              generate_qualified_relation_name(indrelid),
    1389         3753 :                              quote_identifier(NameStr(amrec->amname)));
    1390              :         else                    /* currently, must be EXCLUDE constraint */
    1391           52 :             appendStringInfo(&buf, "EXCLUDE USING %s (",
    1392           52 :                              quote_identifier(NameStr(amrec->amname)));
    1393              :     }
    1394              : 
    1395              :     /*
    1396              :      * Report the indexed attributes
    1397              :      */
    1398         4599 :     sep = "";
    1399        11508 :     for (keyno = 0; keyno < idxrec->indnatts; keyno++)
    1400              :     {
    1401         6958 :         AttrNumber  attnum = idxrec->indkey.values[keyno];
    1402              :         Oid         keycoltype;
    1403              :         Oid         keycolcollation;
    1404              : 
    1405              :         /*
    1406              :          * Ignore non-key attributes if told to.
    1407              :          */
    1408         6958 :         if (keysOnly && keyno >= idxrec->indnkeyatts)
    1409           49 :             break;
    1410              : 
    1411              :         /* Otherwise, print INCLUDE to divide key and non-key attrs. */
    1412         6909 :         if (!colno && keyno == idxrec->indnkeyatts)
    1413              :         {
    1414          125 :             appendStringInfoString(&buf, ") INCLUDE (");
    1415          125 :             sep = "";
    1416              :         }
    1417              : 
    1418         6909 :         if (!colno)
    1419         6588 :             appendStringInfoString(&buf, sep);
    1420         6909 :         sep = ", ";
    1421              : 
    1422         6909 :         if (attnum != 0)
    1423              :         {
    1424              :             /* Simple index column */
    1425              :             char       *attname;
    1426              :             int32       keycoltypmod;
    1427              : 
    1428         6503 :             attname = get_attname(indrelid, attnum, false);
    1429         6503 :             if (!colno || colno == keyno + 1)
    1430         6419 :                 appendStringInfoString(&buf, quote_identifier(attname));
    1431         6503 :             get_atttypetypmodcoll(indrelid, attnum,
    1432              :                                   &keycoltype, &keycoltypmod,
    1433              :                                   &keycolcollation);
    1434              :         }
    1435              :         else
    1436              :         {
    1437              :             /* expressional index */
    1438              :             Node       *indexkey;
    1439              : 
    1440          406 :             if (indexpr_item == NULL)
    1441            0 :                 elog(ERROR, "too few entries in indexprs list");
    1442          406 :             indexkey = (Node *) lfirst(indexpr_item);
    1443          406 :             indexpr_item = lnext(indexprs, indexpr_item);
    1444              :             /* Deparse */
    1445          406 :             str = deparse_expression_pretty(indexkey, context, false, false,
    1446              :                                             prettyFlags, 0);
    1447          406 :             if (!colno || colno == keyno + 1)
    1448              :             {
    1449              :                 /* Need parens if it's not a bare function call */
    1450          400 :                 if (looks_like_function(indexkey))
    1451           26 :                     appendStringInfoString(&buf, str);
    1452              :                 else
    1453          374 :                     appendStringInfo(&buf, "(%s)", str);
    1454              :             }
    1455          406 :             keycoltype = exprType(indexkey);
    1456          406 :             keycolcollation = exprCollation(indexkey);
    1457              :         }
    1458              : 
    1459              :         /* Print additional decoration for (selected) key columns */
    1460         6909 :         if (!attrsOnly && keyno < idxrec->indnkeyatts &&
    1461            0 :             (!colno || colno == keyno + 1))
    1462              :         {
    1463         5605 :             int16       opt = indoption->values[keyno];
    1464         5605 :             Oid         indcoll = indcollation->values[keyno];
    1465         5605 :             Datum       attoptions = get_attoptions(indexrelid, keyno + 1);
    1466         5605 :             bool        has_options = attoptions != (Datum) 0;
    1467              : 
    1468              :             /* Add collation, if not default for column */
    1469         5605 :             if (OidIsValid(indcoll) && indcoll != keycolcollation)
    1470           47 :                 appendStringInfo(&buf, " COLLATE %s",
    1471              :                                  generate_collation_name((indcoll)));
    1472              : 
    1473              :             /* Add the operator class name, if not default */
    1474         5605 :             get_opclass_name(indclass->values[keyno],
    1475              :                              has_options ? InvalidOid : keycoltype, &buf);
    1476              : 
    1477         5605 :             if (has_options)
    1478              :             {
    1479           17 :                 appendStringInfoString(&buf, " (");
    1480           17 :                 get_reloptions(&buf, attoptions);
    1481           17 :                 appendStringInfoChar(&buf, ')');
    1482              :             }
    1483              : 
    1484              :             /* Add options if relevant */
    1485         5605 :             if (amroutine->amcanorder)
    1486              :             {
    1487              :                 /* if it supports sort ordering, report DESC and NULLS opts */
    1488         4552 :                 if (opt & INDOPTION_DESC)
    1489              :                 {
    1490            0 :                     appendStringInfoString(&buf, " DESC");
    1491              :                     /* NULLS FIRST is the default in this case */
    1492            0 :                     if (!(opt & INDOPTION_NULLS_FIRST))
    1493            0 :                         appendStringInfoString(&buf, " NULLS LAST");
    1494              :                 }
    1495              :                 else
    1496              :                 {
    1497         4552 :                     if (opt & INDOPTION_NULLS_FIRST)
    1498            0 :                         appendStringInfoString(&buf, " NULLS FIRST");
    1499              :                 }
    1500              :             }
    1501              : 
    1502              :             /* Add the exclusion operator if relevant */
    1503         5605 :             if (excludeOps != NULL)
    1504           62 :                 appendStringInfo(&buf, " WITH %s",
    1505           62 :                                  generate_operator_name(excludeOps[keyno],
    1506              :                                                         keycoltype,
    1507              :                                                         keycoltype));
    1508              :         }
    1509              :     }
    1510              : 
    1511         4599 :     if (!attrsOnly)
    1512              :     {
    1513         3805 :         appendStringInfoChar(&buf, ')');
    1514              : 
    1515         3805 :         if (idxrec->indnullsnotdistinct)
    1516            6 :             appendStringInfoString(&buf, " NULLS NOT DISTINCT");
    1517              : 
    1518              :         /*
    1519              :          * If it has options, append "WITH (options)"
    1520              :          */
    1521         3805 :         str = flatten_reloptions(indexrelid);
    1522         3805 :         if (str)
    1523              :         {
    1524          105 :             appendStringInfo(&buf, " WITH (%s)", str);
    1525          105 :             pfree(str);
    1526              :         }
    1527              : 
    1528              :         /*
    1529              :          * Print tablespace, but only if requested
    1530              :          */
    1531         3805 :         if (showTblSpc)
    1532              :         {
    1533              :             Oid         tblspc;
    1534              : 
    1535          117 :             tblspc = get_rel_tablespace(indexrelid);
    1536          117 :             if (OidIsValid(tblspc))
    1537              :             {
    1538           27 :                 if (isConstraint)
    1539            0 :                     appendStringInfoString(&buf, " USING INDEX");
    1540           27 :                 appendStringInfo(&buf, " TABLESPACE %s",
    1541           27 :                                  quote_identifier(get_tablespace_name(tblspc)));
    1542              :             }
    1543              :         }
    1544              : 
    1545              :         /*
    1546              :          * If it's a partial index, decompile and append the predicate
    1547              :          */
    1548         3805 :         if (!heap_attisnull(ht_idx, Anum_pg_index_indpred, NULL))
    1549              :         {
    1550              :             Node       *node;
    1551              :             Datum       predDatum;
    1552              :             char       *predString;
    1553              : 
    1554              :             /* Convert text string to node tree */
    1555          162 :             predDatum = SysCacheGetAttrNotNull(INDEXRELID, ht_idx,
    1556              :                                                Anum_pg_index_indpred);
    1557          162 :             predString = TextDatumGetCString(predDatum);
    1558          162 :             node = (Node *) stringToNode(predString);
    1559          162 :             pfree(predString);
    1560              : 
    1561              :             /* Deparse */
    1562          162 :             str = deparse_expression_pretty(node, context, false, false,
    1563              :                                             prettyFlags, 0);
    1564          162 :             if (isConstraint)
    1565           21 :                 appendStringInfo(&buf, " WHERE (%s)", str);
    1566              :             else
    1567          141 :                 appendStringInfo(&buf, " WHERE %s", str);
    1568              :         }
    1569              :     }
    1570              : 
    1571              :     /* Clean up */
    1572         4599 :     ReleaseSysCache(ht_idx);
    1573         4599 :     ReleaseSysCache(ht_idxrel);
    1574         4599 :     ReleaseSysCache(ht_am);
    1575              : 
    1576         4599 :     return buf.data;
    1577              : }
    1578              : 
    1579              : /* ----------
    1580              :  * pg_get_querydef
    1581              :  *
    1582              :  * Public entry point to deparse one query parsetree.
    1583              :  * The pretty flags are determined by GET_PRETTY_FLAGS(pretty).
    1584              :  *
    1585              :  * The result is a palloc'd C string.
    1586              :  * ----------
    1587              :  */
    1588              : char *
    1589            0 : pg_get_querydef(Query *query, bool pretty)
    1590              : {
    1591              :     StringInfoData buf;
    1592              :     int         prettyFlags;
    1593              : 
    1594            0 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
    1595              : 
    1596            0 :     initStringInfo(&buf);
    1597              : 
    1598            0 :     get_query_def(query, &buf, NIL, NULL, true,
    1599              :                   prettyFlags, WRAP_COLUMN_DEFAULT, 0);
    1600              : 
    1601            0 :     return buf.data;
    1602              : }
    1603              : 
    1604              : /*
    1605              :  * pg_get_statisticsobjdef
    1606              :  *      Get the definition of an extended statistics object
    1607              :  */
    1608              : Datum
    1609          153 : pg_get_statisticsobjdef(PG_FUNCTION_ARGS)
    1610              : {
    1611          153 :     Oid         statextid = PG_GETARG_OID(0);
    1612              :     char       *res;
    1613              : 
    1614          153 :     res = pg_get_statisticsobj_worker(statextid, false, true);
    1615              : 
    1616          153 :     if (res == NULL)
    1617            3 :         PG_RETURN_NULL();
    1618              : 
    1619          150 :     PG_RETURN_TEXT_P(string_to_text(res));
    1620              : }
    1621              : 
    1622              : /*
    1623              :  * Internal version for use by ALTER TABLE.
    1624              :  * Returns a palloc'd C string; no pretty-printing.
    1625              :  */
    1626              : char *
    1627           40 : pg_get_statisticsobjdef_string(Oid statextid)
    1628              : {
    1629           40 :     return pg_get_statisticsobj_worker(statextid, false, false);
    1630              : }
    1631              : 
    1632              : /*
    1633              :  * pg_get_statisticsobjdef_columns
    1634              :  *      Get columns and expressions for an extended statistics object
    1635              :  */
    1636              : Datum
    1637          213 : pg_get_statisticsobjdef_columns(PG_FUNCTION_ARGS)
    1638              : {
    1639          213 :     Oid         statextid = PG_GETARG_OID(0);
    1640              :     char       *res;
    1641              : 
    1642          213 :     res = pg_get_statisticsobj_worker(statextid, true, true);
    1643              : 
    1644          213 :     if (res == NULL)
    1645            0 :         PG_RETURN_NULL();
    1646              : 
    1647          213 :     PG_RETURN_TEXT_P(string_to_text(res));
    1648              : }
    1649              : 
    1650              : /*
    1651              :  * Internal workhorse to decompile an extended statistics object.
    1652              :  */
    1653              : static char *
    1654          406 : pg_get_statisticsobj_worker(Oid statextid, bool columns_only, bool missing_ok)
    1655              : {
    1656              :     Form_pg_statistic_ext statextrec;
    1657              :     HeapTuple   statexttup;
    1658              :     StringInfoData buf;
    1659              :     int         colno;
    1660              :     char       *nsp;
    1661              :     ArrayType  *arr;
    1662              :     char       *enabled;
    1663              :     Datum       datum;
    1664              :     bool        ndistinct_enabled;
    1665              :     bool        dependencies_enabled;
    1666              :     bool        mcv_enabled;
    1667              :     int         i;
    1668              :     List       *context;
    1669              :     ListCell   *lc;
    1670          406 :     List       *exprs = NIL;
    1671              :     bool        has_exprs;
    1672              :     int         ncolumns;
    1673              : 
    1674          406 :     statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
    1675              : 
    1676          406 :     if (!HeapTupleIsValid(statexttup))
    1677              :     {
    1678            3 :         if (missing_ok)
    1679            3 :             return NULL;
    1680            0 :         elog(ERROR, "cache lookup failed for statistics object %u", statextid);
    1681              :     }
    1682              : 
    1683              :     /* has the statistics expressions? */
    1684          403 :     has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
    1685              : 
    1686          403 :     statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
    1687              : 
    1688              :     /*
    1689              :      * Get the statistics expressions, if any.  (NOTE: we do not use the
    1690              :      * relcache versions of the expressions, because we want to display
    1691              :      * non-const-folded expressions.)
    1692              :      */
    1693          403 :     if (has_exprs)
    1694              :     {
    1695              :         Datum       exprsDatum;
    1696              :         char       *exprsString;
    1697              : 
    1698          111 :         exprsDatum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
    1699              :                                             Anum_pg_statistic_ext_stxexprs);
    1700          111 :         exprsString = TextDatumGetCString(exprsDatum);
    1701          111 :         exprs = (List *) stringToNode(exprsString);
    1702          111 :         pfree(exprsString);
    1703              :     }
    1704              :     else
    1705          292 :         exprs = NIL;
    1706              : 
    1707              :     /* count the number of columns (attributes and expressions) */
    1708          403 :     ncolumns = statextrec->stxkeys.dim1 + list_length(exprs);
    1709              : 
    1710          403 :     initStringInfo(&buf);
    1711              : 
    1712          403 :     if (!columns_only)
    1713              :     {
    1714          190 :         nsp = get_namespace_name_or_temp(statextrec->stxnamespace);
    1715          190 :         appendStringInfo(&buf, "CREATE STATISTICS %s",
    1716              :                          quote_qualified_identifier(nsp,
    1717          190 :                                                     NameStr(statextrec->stxname)));
    1718              : 
    1719              :         /*
    1720              :          * Decode the stxkind column so that we know which stats types to
    1721              :          * print.
    1722              :          */
    1723          190 :         datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
    1724              :                                        Anum_pg_statistic_ext_stxkind);
    1725          190 :         arr = DatumGetArrayTypeP(datum);
    1726          190 :         if (ARR_NDIM(arr) != 1 ||
    1727          190 :             ARR_HASNULL(arr) ||
    1728          190 :             ARR_ELEMTYPE(arr) != CHAROID)
    1729            0 :             elog(ERROR, "stxkind is not a 1-D char array");
    1730          190 :         enabled = (char *) ARR_DATA_PTR(arr);
    1731              : 
    1732          190 :         ndistinct_enabled = false;
    1733          190 :         dependencies_enabled = false;
    1734          190 :         mcv_enabled = false;
    1735              : 
    1736          597 :         for (i = 0; i < ARR_DIMS(arr)[0]; i++)
    1737              :         {
    1738          407 :             if (enabled[i] == STATS_EXT_NDISTINCT)
    1739          122 :                 ndistinct_enabled = true;
    1740          285 :             else if (enabled[i] == STATS_EXT_DEPENDENCIES)
    1741           99 :                 dependencies_enabled = true;
    1742          186 :             else if (enabled[i] == STATS_EXT_MCV)
    1743          108 :                 mcv_enabled = true;
    1744              : 
    1745              :             /* ignore STATS_EXT_EXPRESSIONS (it's built automatically) */
    1746              :         }
    1747              : 
    1748              :         /*
    1749              :          * If any option is disabled, then we'll need to append the types
    1750              :          * clause to show which options are enabled.  We omit the types clause
    1751              :          * on purpose when all options are enabled, so a pg_dump/pg_restore
    1752              :          * will create all statistics types on a newer postgres version, if
    1753              :          * the statistics had all options enabled on the original version.
    1754              :          *
    1755              :          * But if the statistics is defined on just a single column, it has to
    1756              :          * be an expression statistics. In that case we don't need to specify
    1757              :          * kinds.
    1758              :          */
    1759          190 :         if ((!ndistinct_enabled || !dependencies_enabled || !mcv_enabled) &&
    1760              :             (ncolumns > 1))
    1761              :         {
    1762           59 :             bool        gotone = false;
    1763              : 
    1764           59 :             appendStringInfoString(&buf, " (");
    1765              : 
    1766           59 :             if (ndistinct_enabled)
    1767              :             {
    1768           32 :                 appendStringInfoString(&buf, "ndistinct");
    1769           32 :                 gotone = true;
    1770              :             }
    1771              : 
    1772           59 :             if (dependencies_enabled)
    1773              :             {
    1774            9 :                 appendStringInfo(&buf, "%sdependencies", gotone ? ", " : "");
    1775            9 :                 gotone = true;
    1776              :             }
    1777              : 
    1778           59 :             if (mcv_enabled)
    1779           18 :                 appendStringInfo(&buf, "%smcv", gotone ? ", " : "");
    1780              : 
    1781           59 :             appendStringInfoChar(&buf, ')');
    1782              :         }
    1783              : 
    1784          190 :         appendStringInfoString(&buf, " ON ");
    1785              :     }
    1786              : 
    1787              :     /* decode simple column references */
    1788         1133 :     for (colno = 0; colno < statextrec->stxkeys.dim1; colno++)
    1789              :     {
    1790          730 :         AttrNumber  attnum = statextrec->stxkeys.values[colno];
    1791              :         char       *attname;
    1792              : 
    1793          730 :         if (colno > 0)
    1794          400 :             appendStringInfoString(&buf, ", ");
    1795              : 
    1796          730 :         attname = get_attname(statextrec->stxrelid, attnum, false);
    1797              : 
    1798          730 :         appendStringInfoString(&buf, quote_identifier(attname));
    1799              :     }
    1800              : 
    1801          403 :     context = deparse_context_for(get_relation_name(statextrec->stxrelid),
    1802              :                                   statextrec->stxrelid);
    1803              : 
    1804          557 :     foreach(lc, exprs)
    1805              :     {
    1806          154 :         Node       *expr = (Node *) lfirst(lc);
    1807              :         char       *str;
    1808          154 :         int         prettyFlags = PRETTYFLAG_PAREN;
    1809              : 
    1810          154 :         str = deparse_expression_pretty(expr, context, false, false,
    1811              :                                         prettyFlags, 0);
    1812              : 
    1813          154 :         if (colno > 0)
    1814           81 :             appendStringInfoString(&buf, ", ");
    1815              : 
    1816              :         /* Need parens if it's not a bare function call */
    1817          154 :         if (looks_like_function(expr))
    1818           17 :             appendStringInfoString(&buf, str);
    1819              :         else
    1820          137 :             appendStringInfo(&buf, "(%s)", str);
    1821              : 
    1822          154 :         colno++;
    1823              :     }
    1824              : 
    1825          403 :     if (!columns_only)
    1826          190 :         appendStringInfo(&buf, " FROM %s",
    1827              :                          generate_relation_name(statextrec->stxrelid, NIL));
    1828              : 
    1829          403 :     ReleaseSysCache(statexttup);
    1830              : 
    1831          403 :     return buf.data;
    1832              : }
    1833              : 
    1834              : /*
    1835              :  * Generate text array of expressions for statistics object.
    1836              :  */
    1837              : Datum
    1838          103 : pg_get_statisticsobjdef_expressions(PG_FUNCTION_ARGS)
    1839              : {
    1840          103 :     Oid         statextid = PG_GETARG_OID(0);
    1841              :     Form_pg_statistic_ext statextrec;
    1842              :     HeapTuple   statexttup;
    1843              :     Datum       datum;
    1844              :     List       *context;
    1845              :     ListCell   *lc;
    1846          103 :     List       *exprs = NIL;
    1847              :     bool        has_exprs;
    1848              :     char       *tmp;
    1849          103 :     ArrayBuildState *astate = NULL;
    1850              : 
    1851          103 :     statexttup = SearchSysCache1(STATEXTOID, ObjectIdGetDatum(statextid));
    1852              : 
    1853          103 :     if (!HeapTupleIsValid(statexttup))
    1854            0 :         PG_RETURN_NULL();
    1855              : 
    1856              :     /* Does the stats object have expressions? */
    1857          103 :     has_exprs = !heap_attisnull(statexttup, Anum_pg_statistic_ext_stxexprs, NULL);
    1858              : 
    1859              :     /* no expressions? we're done */
    1860          103 :     if (!has_exprs)
    1861              :     {
    1862            9 :         ReleaseSysCache(statexttup);
    1863            9 :         PG_RETURN_NULL();
    1864              :     }
    1865              : 
    1866           94 :     statextrec = (Form_pg_statistic_ext) GETSTRUCT(statexttup);
    1867              : 
    1868              :     /*
    1869              :      * Get the statistics expressions, and deparse them into text values.
    1870              :      */
    1871           94 :     datum = SysCacheGetAttrNotNull(STATEXTOID, statexttup,
    1872              :                                    Anum_pg_statistic_ext_stxexprs);
    1873           94 :     tmp = TextDatumGetCString(datum);
    1874           94 :     exprs = (List *) stringToNode(tmp);
    1875           94 :     pfree(tmp);
    1876              : 
    1877           94 :     context = deparse_context_for(get_relation_name(statextrec->stxrelid),
    1878              :                                   statextrec->stxrelid);
    1879              : 
    1880          223 :     foreach(lc, exprs)
    1881              :     {
    1882          129 :         Node       *expr = (Node *) lfirst(lc);
    1883              :         char       *str;
    1884          129 :         int         prettyFlags = PRETTYFLAG_INDENT;
    1885              : 
    1886          129 :         str = deparse_expression_pretty(expr, context, false, false,
    1887              :                                         prettyFlags, 0);
    1888              : 
    1889          129 :         astate = accumArrayResult(astate,
    1890          129 :                                   PointerGetDatum(cstring_to_text(str)),
    1891              :                                   false,
    1892              :                                   TEXTOID,
    1893              :                                   CurrentMemoryContext);
    1894              :     }
    1895              : 
    1896           94 :     ReleaseSysCache(statexttup);
    1897              : 
    1898           94 :     PG_RETURN_DATUM(makeArrayResult(astate, CurrentMemoryContext));
    1899              : }
    1900              : 
    1901              : /*
    1902              :  * pg_get_partkeydef
    1903              :  *
    1904              :  * Returns the partition key specification, ie, the following:
    1905              :  *
    1906              :  * { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
    1907              :  */
    1908              : Datum
    1909          739 : pg_get_partkeydef(PG_FUNCTION_ARGS)
    1910              : {
    1911          739 :     Oid         relid = PG_GETARG_OID(0);
    1912              :     char       *res;
    1913              : 
    1914          739 :     res = pg_get_partkeydef_worker(relid, PRETTYFLAG_INDENT, false, true);
    1915              : 
    1916          739 :     if (res == NULL)
    1917            3 :         PG_RETURN_NULL();
    1918              : 
    1919          736 :     PG_RETURN_TEXT_P(string_to_text(res));
    1920              : }
    1921              : 
    1922              : /* Internal version that just reports the column definitions */
    1923              : char *
    1924           71 : pg_get_partkeydef_columns(Oid relid, bool pretty)
    1925              : {
    1926              :     int         prettyFlags;
    1927              : 
    1928           71 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
    1929              : 
    1930           71 :     return pg_get_partkeydef_worker(relid, prettyFlags, true, false);
    1931              : }
    1932              : 
    1933              : /*
    1934              :  * Internal workhorse to decompile a partition key definition.
    1935              :  */
    1936              : static char *
    1937          810 : pg_get_partkeydef_worker(Oid relid, int prettyFlags,
    1938              :                          bool attrsOnly, bool missing_ok)
    1939              : {
    1940              :     Form_pg_partitioned_table form;
    1941              :     HeapTuple   tuple;
    1942              :     oidvector  *partclass;
    1943              :     oidvector  *partcollation;
    1944              :     List       *partexprs;
    1945              :     ListCell   *partexpr_item;
    1946              :     List       *context;
    1947              :     Datum       datum;
    1948              :     StringInfoData buf;
    1949              :     int         keyno;
    1950              :     char       *str;
    1951              :     char       *sep;
    1952              : 
    1953          810 :     tuple = SearchSysCache1(PARTRELID, ObjectIdGetDatum(relid));
    1954          810 :     if (!HeapTupleIsValid(tuple))
    1955              :     {
    1956            3 :         if (missing_ok)
    1957            3 :             return NULL;
    1958            0 :         elog(ERROR, "cache lookup failed for partition key of %u", relid);
    1959              :     }
    1960              : 
    1961          807 :     form = (Form_pg_partitioned_table) GETSTRUCT(tuple);
    1962              : 
    1963              :     Assert(form->partrelid == relid);
    1964              : 
    1965              :     /* Must get partclass and partcollation the hard way */
    1966          807 :     datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
    1967              :                                    Anum_pg_partitioned_table_partclass);
    1968          807 :     partclass = (oidvector *) DatumGetPointer(datum);
    1969              : 
    1970          807 :     datum = SysCacheGetAttrNotNull(PARTRELID, tuple,
    1971              :                                    Anum_pg_partitioned_table_partcollation);
    1972          807 :     partcollation = (oidvector *) DatumGetPointer(datum);
    1973              : 
    1974              : 
    1975              :     /*
    1976              :      * Get the expressions, if any.  (NOTE: we do not use the relcache
    1977              :      * versions of the expressions, because we want to display
    1978              :      * non-const-folded expressions.)
    1979              :      */
    1980          807 :     if (!heap_attisnull(tuple, Anum_pg_partitioned_table_partexprs, NULL))
    1981              :     {
    1982              :         Datum       exprsDatum;
    1983              :         char       *exprsString;
    1984              : 
    1985           73 :         exprsDatum = SysCacheGetAttrNotNull(PARTRELID, tuple,
    1986              :                                             Anum_pg_partitioned_table_partexprs);
    1987           73 :         exprsString = TextDatumGetCString(exprsDatum);
    1988           73 :         partexprs = (List *) stringToNode(exprsString);
    1989              : 
    1990           73 :         if (!IsA(partexprs, List))
    1991            0 :             elog(ERROR, "unexpected node type found in partexprs: %d",
    1992              :                  (int) nodeTag(partexprs));
    1993              : 
    1994           73 :         pfree(exprsString);
    1995              :     }
    1996              :     else
    1997          734 :         partexprs = NIL;
    1998              : 
    1999          807 :     partexpr_item = list_head(partexprs);
    2000          807 :     context = deparse_context_for(get_relation_name(relid), relid);
    2001              : 
    2002          807 :     initStringInfo(&buf);
    2003              : 
    2004          807 :     switch (form->partstrat)
    2005              :     {
    2006           58 :         case PARTITION_STRATEGY_HASH:
    2007           58 :             if (!attrsOnly)
    2008           58 :                 appendStringInfoString(&buf, "HASH");
    2009           58 :             break;
    2010          293 :         case PARTITION_STRATEGY_LIST:
    2011          293 :             if (!attrsOnly)
    2012          273 :                 appendStringInfoString(&buf, "LIST");
    2013          293 :             break;
    2014          456 :         case PARTITION_STRATEGY_RANGE:
    2015          456 :             if (!attrsOnly)
    2016          405 :                 appendStringInfoString(&buf, "RANGE");
    2017          456 :             break;
    2018            0 :         default:
    2019            0 :             elog(ERROR, "unexpected partition strategy: %d",
    2020              :                  (int) form->partstrat);
    2021              :     }
    2022              : 
    2023          807 :     if (!attrsOnly)
    2024          736 :         appendStringInfoString(&buf, " (");
    2025          807 :     sep = "";
    2026         1690 :     for (keyno = 0; keyno < form->partnatts; keyno++)
    2027              :     {
    2028          883 :         AttrNumber  attnum = form->partattrs.values[keyno];
    2029              :         Oid         keycoltype;
    2030              :         Oid         keycolcollation;
    2031              :         Oid         partcoll;
    2032              : 
    2033          883 :         appendStringInfoString(&buf, sep);
    2034          883 :         sep = ", ";
    2035          883 :         if (attnum != 0)
    2036              :         {
    2037              :             /* Simple attribute reference */
    2038              :             char       *attname;
    2039              :             int32       keycoltypmod;
    2040              : 
    2041          804 :             attname = get_attname(relid, attnum, false);
    2042          804 :             appendStringInfoString(&buf, quote_identifier(attname));
    2043          804 :             get_atttypetypmodcoll(relid, attnum,
    2044              :                                   &keycoltype, &keycoltypmod,
    2045              :                                   &keycolcollation);
    2046              :         }
    2047              :         else
    2048              :         {
    2049              :             /* Expression */
    2050              :             Node       *partkey;
    2051              : 
    2052           79 :             if (partexpr_item == NULL)
    2053            0 :                 elog(ERROR, "too few entries in partexprs list");
    2054           79 :             partkey = (Node *) lfirst(partexpr_item);
    2055           79 :             partexpr_item = lnext(partexprs, partexpr_item);
    2056              : 
    2057              :             /* Deparse */
    2058           79 :             str = deparse_expression_pretty(partkey, context, false, false,
    2059              :                                             prettyFlags, 0);
    2060              :             /* Need parens if it's not a bare function call */
    2061           79 :             if (looks_like_function(partkey))
    2062           28 :                 appendStringInfoString(&buf, str);
    2063              :             else
    2064           51 :                 appendStringInfo(&buf, "(%s)", str);
    2065              : 
    2066           79 :             keycoltype = exprType(partkey);
    2067           79 :             keycolcollation = exprCollation(partkey);
    2068              :         }
    2069              : 
    2070              :         /* Add collation, if not default for column */
    2071          883 :         partcoll = partcollation->values[keyno];
    2072          883 :         if (!attrsOnly && OidIsValid(partcoll) && partcoll != keycolcollation)
    2073            3 :             appendStringInfo(&buf, " COLLATE %s",
    2074              :                              generate_collation_name((partcoll)));
    2075              : 
    2076              :         /* Add the operator class name, if not default */
    2077          883 :         if (!attrsOnly)
    2078          785 :             get_opclass_name(partclass->values[keyno], keycoltype, &buf);
    2079              :     }
    2080              : 
    2081          807 :     if (!attrsOnly)
    2082          736 :         appendStringInfoChar(&buf, ')');
    2083              : 
    2084              :     /* Clean up */
    2085          807 :     ReleaseSysCache(tuple);
    2086              : 
    2087          807 :     return buf.data;
    2088              : }
    2089              : 
    2090              : /*
    2091              :  * pg_get_partition_constraintdef
    2092              :  *
    2093              :  * Returns partition constraint expression as a string for the input relation
    2094              :  */
    2095              : Datum
    2096          115 : pg_get_partition_constraintdef(PG_FUNCTION_ARGS)
    2097              : {
    2098          115 :     Oid         relationId = PG_GETARG_OID(0);
    2099              :     Expr       *constr_expr;
    2100              :     int         prettyFlags;
    2101              :     List       *context;
    2102              :     char       *consrc;
    2103              : 
    2104          115 :     constr_expr = get_partition_qual_relid(relationId);
    2105              : 
    2106              :     /* Quick exit if no partition constraint */
    2107          115 :     if (constr_expr == NULL)
    2108           12 :         PG_RETURN_NULL();
    2109              : 
    2110              :     /*
    2111              :      * Deparse and return the constraint expression.
    2112              :      */
    2113          103 :     prettyFlags = PRETTYFLAG_INDENT;
    2114          103 :     context = deparse_context_for(get_relation_name(relationId), relationId);
    2115          103 :     consrc = deparse_expression_pretty((Node *) constr_expr, context, false,
    2116              :                                        false, prettyFlags, 0);
    2117              : 
    2118          103 :     PG_RETURN_TEXT_P(string_to_text(consrc));
    2119              : }
    2120              : 
    2121              : /*
    2122              :  * pg_get_partconstrdef_string
    2123              :  *
    2124              :  * Returns the partition constraint as a C-string for the input relation, with
    2125              :  * the given alias.  No pretty-printing.
    2126              :  */
    2127              : char *
    2128           55 : pg_get_partconstrdef_string(Oid partitionId, char *aliasname)
    2129              : {
    2130              :     Expr       *constr_expr;
    2131              :     List       *context;
    2132              : 
    2133           55 :     constr_expr = get_partition_qual_relid(partitionId);
    2134           55 :     context = deparse_context_for(aliasname, partitionId);
    2135              : 
    2136           55 :     return deparse_expression((Node *) constr_expr, context, true, false);
    2137              : }
    2138              : 
    2139              : /*
    2140              :  * pg_get_constraintdef
    2141              :  *
    2142              :  * Returns the definition for the constraint, ie, everything that needs to
    2143              :  * appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
    2144              :  */
    2145              : Datum
    2146         1093 : pg_get_constraintdef(PG_FUNCTION_ARGS)
    2147              : {
    2148         1093 :     Oid         constraintId = PG_GETARG_OID(0);
    2149              :     int         prettyFlags;
    2150              :     char       *res;
    2151              : 
    2152         1093 :     prettyFlags = PRETTYFLAG_INDENT;
    2153              : 
    2154         1093 :     res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
    2155              : 
    2156         1093 :     if (res == NULL)
    2157            3 :         PG_RETURN_NULL();
    2158              : 
    2159         1090 :     PG_RETURN_TEXT_P(string_to_text(res));
    2160              : }
    2161              : 
    2162              : Datum
    2163         2427 : pg_get_constraintdef_ext(PG_FUNCTION_ARGS)
    2164              : {
    2165         2427 :     Oid         constraintId = PG_GETARG_OID(0);
    2166         2427 :     bool        pretty = PG_GETARG_BOOL(1);
    2167              :     int         prettyFlags;
    2168              :     char       *res;
    2169              : 
    2170         2427 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
    2171              : 
    2172         2427 :     res = pg_get_constraintdef_worker(constraintId, false, prettyFlags, true);
    2173              : 
    2174         2427 :     if (res == NULL)
    2175            0 :         PG_RETURN_NULL();
    2176              : 
    2177         2427 :     PG_RETURN_TEXT_P(string_to_text(res));
    2178              : }
    2179              : 
    2180              : /*
    2181              :  * Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
    2182              :  */
    2183              : char *
    2184          328 : pg_get_constraintdef_command(Oid constraintId)
    2185              : {
    2186          328 :     return pg_get_constraintdef_worker(constraintId, true, 0, false);
    2187              : }
    2188              : 
    2189              : /*
    2190              :  * As of 9.4, we now use an MVCC snapshot for this.
    2191              :  */
    2192              : static char *
    2193         3848 : pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
    2194              :                             int prettyFlags, bool missing_ok)
    2195              : {
    2196              :     HeapTuple   tup;
    2197              :     Form_pg_constraint conForm;
    2198              :     StringInfoData buf;
    2199              :     SysScanDesc scandesc;
    2200              :     ScanKeyData scankey[1];
    2201         3848 :     Snapshot    snapshot = RegisterSnapshot(GetTransactionSnapshot());
    2202         3848 :     Relation    relation = table_open(ConstraintRelationId, AccessShareLock);
    2203              : 
    2204         3848 :     ScanKeyInit(&scankey[0],
    2205              :                 Anum_pg_constraint_oid,
    2206              :                 BTEqualStrategyNumber, F_OIDEQ,
    2207              :                 ObjectIdGetDatum(constraintId));
    2208              : 
    2209         3848 :     scandesc = systable_beginscan(relation,
    2210              :                                   ConstraintOidIndexId,
    2211              :                                   true,
    2212              :                                   snapshot,
    2213              :                                   1,
    2214              :                                   scankey);
    2215              : 
    2216              :     /*
    2217              :      * We later use the tuple with SysCacheGetAttr() as if we had obtained it
    2218              :      * via SearchSysCache, which works fine.
    2219              :      */
    2220         3848 :     tup = systable_getnext(scandesc);
    2221              : 
    2222         3848 :     UnregisterSnapshot(snapshot);
    2223              : 
    2224         3848 :     if (!HeapTupleIsValid(tup))
    2225              :     {
    2226            3 :         if (missing_ok)
    2227              :         {
    2228            3 :             systable_endscan(scandesc);
    2229            3 :             table_close(relation, AccessShareLock);
    2230            3 :             return NULL;
    2231              :         }
    2232            0 :         elog(ERROR, "could not find tuple for constraint %u", constraintId);
    2233              :     }
    2234              : 
    2235         3845 :     conForm = (Form_pg_constraint) GETSTRUCT(tup);
    2236              : 
    2237         3845 :     initStringInfo(&buf);
    2238              : 
    2239         3845 :     if (fullCommand)
    2240              :     {
    2241          328 :         if (OidIsValid(conForm->conrelid))
    2242              :         {
    2243              :             /*
    2244              :              * Currently, callers want ALTER TABLE (without ONLY) for CHECK
    2245              :              * constraints, and other types of constraints don't inherit
    2246              :              * anyway so it doesn't matter whether we say ONLY or not. Someday
    2247              :              * we might need to let callers specify whether to put ONLY in the
    2248              :              * command.
    2249              :              */
    2250          321 :             appendStringInfo(&buf, "ALTER TABLE %s ADD CONSTRAINT %s ",
    2251              :                              generate_qualified_relation_name(conForm->conrelid),
    2252          321 :                              quote_identifier(NameStr(conForm->conname)));
    2253              :         }
    2254              :         else
    2255              :         {
    2256              :             /* Must be a domain constraint */
    2257              :             Assert(OidIsValid(conForm->contypid));
    2258            7 :             appendStringInfo(&buf, "ALTER DOMAIN %s ADD CONSTRAINT %s ",
    2259              :                              generate_qualified_type_name(conForm->contypid),
    2260            7 :                              quote_identifier(NameStr(conForm->conname)));
    2261              :         }
    2262              :     }
    2263              : 
    2264         3845 :     switch (conForm->contype)
    2265              :     {
    2266          386 :         case CONSTRAINT_FOREIGN:
    2267              :             {
    2268              :                 Datum       val;
    2269              :                 bool        isnull;
    2270              :                 const char *string;
    2271              : 
    2272              :                 /* Start off the constraint definition */
    2273          386 :                 appendStringInfoString(&buf, "FOREIGN KEY (");
    2274              : 
    2275              :                 /* Fetch and build referencing-column list */
    2276          386 :                 val = SysCacheGetAttrNotNull(CONSTROID, tup,
    2277              :                                              Anum_pg_constraint_conkey);
    2278              : 
    2279              :                 /* If it is a temporal foreign key then it uses PERIOD. */
    2280          386 :                 decompile_column_index_array(val, conForm->conrelid, conForm->conperiod, &buf);
    2281              : 
    2282              :                 /* add foreign relation name */
    2283          386 :                 appendStringInfo(&buf, ") REFERENCES %s(",
    2284              :                                  generate_relation_name(conForm->confrelid,
    2285              :                                                         NIL));
    2286              : 
    2287              :                 /* Fetch and build referenced-column list */
    2288          386 :                 val = SysCacheGetAttrNotNull(CONSTROID, tup,
    2289              :                                              Anum_pg_constraint_confkey);
    2290              : 
    2291          386 :                 decompile_column_index_array(val, conForm->confrelid, conForm->conperiod, &buf);
    2292              : 
    2293          386 :                 appendStringInfoChar(&buf, ')');
    2294              : 
    2295              :                 /* Add match type */
    2296          386 :                 switch (conForm->confmatchtype)
    2297              :                 {
    2298           17 :                     case FKCONSTR_MATCH_FULL:
    2299           17 :                         string = " MATCH FULL";
    2300           17 :                         break;
    2301            0 :                     case FKCONSTR_MATCH_PARTIAL:
    2302            0 :                         string = " MATCH PARTIAL";
    2303            0 :                         break;
    2304          369 :                     case FKCONSTR_MATCH_SIMPLE:
    2305          369 :                         string = "";
    2306          369 :                         break;
    2307            0 :                     default:
    2308            0 :                         elog(ERROR, "unrecognized confmatchtype: %d",
    2309              :                              conForm->confmatchtype);
    2310              :                         string = "";  /* keep compiler quiet */
    2311              :                         break;
    2312              :                 }
    2313          386 :                 appendStringInfoString(&buf, string);
    2314              : 
    2315              :                 /* Add ON UPDATE and ON DELETE clauses, if needed */
    2316          386 :                 switch (conForm->confupdtype)
    2317              :                 {
    2318          318 :                     case FKCONSTR_ACTION_NOACTION:
    2319          318 :                         string = NULL;  /* suppress default */
    2320          318 :                         break;
    2321            0 :                     case FKCONSTR_ACTION_RESTRICT:
    2322            0 :                         string = "RESTRICT";
    2323            0 :                         break;
    2324           54 :                     case FKCONSTR_ACTION_CASCADE:
    2325           54 :                         string = "CASCADE";
    2326           54 :                         break;
    2327           14 :                     case FKCONSTR_ACTION_SETNULL:
    2328           14 :                         string = "SET NULL";
    2329           14 :                         break;
    2330            0 :                     case FKCONSTR_ACTION_SETDEFAULT:
    2331            0 :                         string = "SET DEFAULT";
    2332            0 :                         break;
    2333            0 :                     default:
    2334            0 :                         elog(ERROR, "unrecognized confupdtype: %d",
    2335              :                              conForm->confupdtype);
    2336              :                         string = NULL;  /* keep compiler quiet */
    2337              :                         break;
    2338              :                 }
    2339          386 :                 if (string)
    2340           68 :                     appendStringInfo(&buf, " ON UPDATE %s", string);
    2341              : 
    2342          386 :                 switch (conForm->confdeltype)
    2343              :                 {
    2344          320 :                     case FKCONSTR_ACTION_NOACTION:
    2345          320 :                         string = NULL;  /* suppress default */
    2346          320 :                         break;
    2347            0 :                     case FKCONSTR_ACTION_RESTRICT:
    2348            0 :                         string = "RESTRICT";
    2349            0 :                         break;
    2350           54 :                     case FKCONSTR_ACTION_CASCADE:
    2351           54 :                         string = "CASCADE";
    2352           54 :                         break;
    2353            9 :                     case FKCONSTR_ACTION_SETNULL:
    2354            9 :                         string = "SET NULL";
    2355            9 :                         break;
    2356            3 :                     case FKCONSTR_ACTION_SETDEFAULT:
    2357            3 :                         string = "SET DEFAULT";
    2358            3 :                         break;
    2359            0 :                     default:
    2360            0 :                         elog(ERROR, "unrecognized confdeltype: %d",
    2361              :                              conForm->confdeltype);
    2362              :                         string = NULL;  /* keep compiler quiet */
    2363              :                         break;
    2364              :                 }
    2365          386 :                 if (string)
    2366           66 :                     appendStringInfo(&buf, " ON DELETE %s", string);
    2367              : 
    2368              :                 /*
    2369              :                  * Add columns specified to SET NULL or SET DEFAULT if
    2370              :                  * provided.
    2371              :                  */
    2372          386 :                 val = SysCacheGetAttr(CONSTROID, tup,
    2373              :                                       Anum_pg_constraint_confdelsetcols, &isnull);
    2374          386 :                 if (!isnull)
    2375              :                 {
    2376            6 :                     appendStringInfoString(&buf, " (");
    2377            6 :                     decompile_column_index_array(val, conForm->conrelid, false, &buf);
    2378            6 :                     appendStringInfoChar(&buf, ')');
    2379              :                 }
    2380              : 
    2381          386 :                 break;
    2382              :             }
    2383         1991 :         case CONSTRAINT_PRIMARY:
    2384              :         case CONSTRAINT_UNIQUE:
    2385              :             {
    2386              :                 Datum       val;
    2387              :                 Oid         indexId;
    2388              :                 int         keyatts;
    2389              :                 HeapTuple   indtup;
    2390              : 
    2391              :                 /* Start off the constraint definition */
    2392         1991 :                 if (conForm->contype == CONSTRAINT_PRIMARY)
    2393         1624 :                     appendStringInfoString(&buf, "PRIMARY KEY ");
    2394              :                 else
    2395          367 :                     appendStringInfoString(&buf, "UNIQUE ");
    2396              : 
    2397         1991 :                 indexId = conForm->conindid;
    2398              : 
    2399         1991 :                 indtup = SearchSysCache1(INDEXRELID, ObjectIdGetDatum(indexId));
    2400         1991 :                 if (!HeapTupleIsValid(indtup))
    2401            0 :                     elog(ERROR, "cache lookup failed for index %u", indexId);
    2402         1991 :                 if (conForm->contype == CONSTRAINT_UNIQUE &&
    2403          367 :                     ((Form_pg_index) GETSTRUCT(indtup))->indnullsnotdistinct)
    2404            0 :                     appendStringInfoString(&buf, "NULLS NOT DISTINCT ");
    2405              : 
    2406         1991 :                 appendStringInfoChar(&buf, '(');
    2407              : 
    2408              :                 /* Fetch and build target column list */
    2409         1991 :                 val = SysCacheGetAttrNotNull(CONSTROID, tup,
    2410              :                                              Anum_pg_constraint_conkey);
    2411              : 
    2412         1991 :                 keyatts = decompile_column_index_array(val, conForm->conrelid, false, &buf);
    2413         1991 :                 if (conForm->conperiod)
    2414          191 :                     appendStringInfoString(&buf, " WITHOUT OVERLAPS");
    2415              : 
    2416         1991 :                 appendStringInfoChar(&buf, ')');
    2417              : 
    2418              :                 /* Build including column list (from pg_index.indkeys) */
    2419         1991 :                 val = SysCacheGetAttrNotNull(INDEXRELID, indtup,
    2420              :                                              Anum_pg_index_indnatts);
    2421         1991 :                 if (DatumGetInt32(val) > keyatts)
    2422              :                 {
    2423              :                     Datum       cols;
    2424              :                     Datum      *keys;
    2425              :                     int         nKeys;
    2426              :                     int         j;
    2427              : 
    2428           41 :                     appendStringInfoString(&buf, " INCLUDE (");
    2429              : 
    2430           41 :                     cols = SysCacheGetAttrNotNull(INDEXRELID, indtup,
    2431              :                                                   Anum_pg_index_indkey);
    2432              : 
    2433           41 :                     deconstruct_array_builtin(DatumGetArrayTypeP(cols), INT2OID,
    2434              :                                               &keys, NULL, &nKeys);
    2435              : 
    2436          123 :                     for (j = keyatts; j < nKeys; j++)
    2437              :                     {
    2438              :                         char       *colName;
    2439              : 
    2440           82 :                         colName = get_attname(conForm->conrelid,
    2441           82 :                                               DatumGetInt16(keys[j]), false);
    2442           82 :                         if (j > keyatts)
    2443           41 :                             appendStringInfoString(&buf, ", ");
    2444           82 :                         appendStringInfoString(&buf, quote_identifier(colName));
    2445              :                     }
    2446              : 
    2447           41 :                     appendStringInfoChar(&buf, ')');
    2448              :                 }
    2449         1991 :                 ReleaseSysCache(indtup);
    2450              : 
    2451              :                 /* XXX why do we only print these bits if fullCommand? */
    2452         1991 :                 if (fullCommand && OidIsValid(indexId))
    2453              :                 {
    2454          102 :                     char       *options = flatten_reloptions(indexId);
    2455              :                     Oid         tblspc;
    2456              : 
    2457          102 :                     if (options)
    2458              :                     {
    2459            0 :                         appendStringInfo(&buf, " WITH (%s)", options);
    2460            0 :                         pfree(options);
    2461              :                     }
    2462              : 
    2463              :                     /*
    2464              :                      * Print the tablespace, unless it's the database default.
    2465              :                      * This is to help ALTER TABLE usage of this facility,
    2466              :                      * which needs this behavior to recreate exact catalog
    2467              :                      * state.
    2468              :                      */
    2469          102 :                     tblspc = get_rel_tablespace(indexId);
    2470          102 :                     if (OidIsValid(tblspc))
    2471           12 :                         appendStringInfo(&buf, " USING INDEX TABLESPACE %s",
    2472           12 :                                          quote_identifier(get_tablespace_name(tblspc)));
    2473              :                 }
    2474              : 
    2475         1991 :                 break;
    2476              :             }
    2477         1182 :         case CONSTRAINT_CHECK:
    2478              :             {
    2479              :                 Datum       val;
    2480              :                 char       *conbin;
    2481              :                 char       *consrc;
    2482              :                 Node       *expr;
    2483              :                 List       *context;
    2484              : 
    2485              :                 /* Fetch constraint expression in parsetree form */
    2486         1182 :                 val = SysCacheGetAttrNotNull(CONSTROID, tup,
    2487              :                                              Anum_pg_constraint_conbin);
    2488              : 
    2489         1182 :                 conbin = TextDatumGetCString(val);
    2490         1182 :                 expr = stringToNode(conbin);
    2491              : 
    2492              :                 /* Set up deparsing context for Var nodes in constraint */
    2493         1182 :                 if (conForm->conrelid != InvalidOid)
    2494              :                 {
    2495              :                     /* relation constraint */
    2496         1056 :                     context = deparse_context_for(get_relation_name(conForm->conrelid),
    2497              :                                                   conForm->conrelid);
    2498              :                 }
    2499              :                 else
    2500              :                 {
    2501              :                     /* domain constraint --- can't have Vars */
    2502          126 :                     context = NIL;
    2503              :                 }
    2504              : 
    2505         1182 :                 consrc = deparse_expression_pretty(expr, context, false, false,
    2506              :                                                    prettyFlags, 0);
    2507              : 
    2508              :                 /*
    2509              :                  * Now emit the constraint definition, adding NO INHERIT if
    2510              :                  * necessary.
    2511              :                  *
    2512              :                  * There are cases where the constraint expression will be
    2513              :                  * fully parenthesized and we don't need the outer parens ...
    2514              :                  * but there are other cases where we do need 'em.  Be
    2515              :                  * conservative for now.
    2516              :                  *
    2517              :                  * Note that simply checking for leading '(' and trailing ')'
    2518              :                  * would NOT be good enough, consider "(x > 0) AND (y > 0)".
    2519              :                  */
    2520         1182 :                 appendStringInfo(&buf, "CHECK (%s)%s",
    2521              :                                  consrc,
    2522         1182 :                                  conForm->connoinherit ? " NO INHERIT" : "");
    2523         1182 :                 break;
    2524              :             }
    2525          234 :         case CONSTRAINT_NOTNULL:
    2526              :             {
    2527          234 :                 if (conForm->conrelid)
    2528              :                 {
    2529              :                     AttrNumber  attnum;
    2530              : 
    2531          178 :                     attnum = extractNotNullColumn(tup);
    2532              : 
    2533          178 :                     appendStringInfo(&buf, "NOT NULL %s",
    2534          178 :                                      quote_identifier(get_attname(conForm->conrelid,
    2535              :                                                                   attnum, false)));
    2536          178 :                     if (((Form_pg_constraint) GETSTRUCT(tup))->connoinherit)
    2537            0 :                         appendStringInfoString(&buf, " NO INHERIT");
    2538              :                 }
    2539           56 :                 else if (conForm->contypid)
    2540              :                 {
    2541              :                     /* conkey is null for domain not-null constraints */
    2542           56 :                     appendStringInfoString(&buf, "NOT NULL");
    2543              :                 }
    2544          234 :                 break;
    2545              :             }
    2546              : 
    2547            0 :         case CONSTRAINT_TRIGGER:
    2548              : 
    2549              :             /*
    2550              :              * There isn't an ALTER TABLE syntax for creating a user-defined
    2551              :              * constraint trigger, but it seems better to print something than
    2552              :              * throw an error; if we throw error then this function couldn't
    2553              :              * safely be applied to all rows of pg_constraint.
    2554              :              */
    2555            0 :             appendStringInfoString(&buf, "TRIGGER");
    2556            0 :             break;
    2557           52 :         case CONSTRAINT_EXCLUSION:
    2558              :             {
    2559           52 :                 Oid         indexOid = conForm->conindid;
    2560              :                 Datum       val;
    2561              :                 Datum      *elems;
    2562              :                 int         nElems;
    2563              :                 int         i;
    2564              :                 Oid        *operators;
    2565              : 
    2566              :                 /* Extract operator OIDs from the pg_constraint tuple */
    2567           52 :                 val = SysCacheGetAttrNotNull(CONSTROID, tup,
    2568              :                                              Anum_pg_constraint_conexclop);
    2569              : 
    2570           52 :                 deconstruct_array_builtin(DatumGetArrayTypeP(val), OIDOID,
    2571              :                                           &elems, NULL, &nElems);
    2572              : 
    2573           52 :                 operators = (Oid *) palloc(nElems * sizeof(Oid));
    2574          114 :                 for (i = 0; i < nElems; i++)
    2575           62 :                     operators[i] = DatumGetObjectId(elems[i]);
    2576              : 
    2577              :                 /* pg_get_indexdef_worker does the rest */
    2578              :                 /* suppress tablespace because pg_dump wants it that way */
    2579           52 :                 appendStringInfoString(&buf,
    2580           52 :                                        pg_get_indexdef_worker(indexOid,
    2581              :                                                               0,
    2582              :                                                               operators,
    2583              :                                                               false,
    2584              :                                                               false,
    2585              :                                                               false,
    2586              :                                                               false,
    2587              :                                                               prettyFlags,
    2588              :                                                               false));
    2589           52 :                 break;
    2590              :             }
    2591            0 :         default:
    2592            0 :             elog(ERROR, "invalid constraint type \"%c\"", conForm->contype);
    2593              :             break;
    2594              :     }
    2595              : 
    2596         3845 :     if (conForm->condeferrable)
    2597           60 :         appendStringInfoString(&buf, " DEFERRABLE");
    2598         3845 :     if (conForm->condeferred)
    2599           24 :         appendStringInfoString(&buf, " INITIALLY DEFERRED");
    2600              : 
    2601              :     /* Validated status is irrelevant when the constraint is NOT ENFORCED. */
    2602         3845 :     if (!conForm->conenforced)
    2603           61 :         appendStringInfoString(&buf, " NOT ENFORCED");
    2604         3784 :     else if (!conForm->convalidated)
    2605          139 :         appendStringInfoString(&buf, " NOT VALID");
    2606              : 
    2607              :     /* Cleanup */
    2608         3845 :     systable_endscan(scandesc);
    2609         3845 :     table_close(relation, AccessShareLock);
    2610              : 
    2611         3845 :     return buf.data;
    2612              : }
    2613              : 
    2614              : 
    2615              : /*
    2616              :  * Convert an int16[] Datum into a comma-separated list of column names
    2617              :  * for the indicated relation; append the list to buf.  Returns the number
    2618              :  * of keys.
    2619              :  */
    2620              : static int
    2621         2769 : decompile_column_index_array(Datum column_index_array, Oid relId,
    2622              :                              bool withPeriod, StringInfo buf)
    2623              : {
    2624              :     Datum      *keys;
    2625              :     int         nKeys;
    2626              :     int         j;
    2627              : 
    2628              :     /* Extract data from array of int16 */
    2629         2769 :     deconstruct_array_builtin(DatumGetArrayTypeP(column_index_array), INT2OID,
    2630              :                               &keys, NULL, &nKeys);
    2631              : 
    2632         6676 :     for (j = 0; j < nKeys; j++)
    2633              :     {
    2634              :         char       *colName;
    2635              : 
    2636         3907 :         colName = get_attname(relId, DatumGetInt16(keys[j]), false);
    2637              : 
    2638         3907 :         if (j == 0)
    2639         2769 :             appendStringInfoString(buf, quote_identifier(colName));
    2640              :         else
    2641         1252 :             appendStringInfo(buf, ", %s%s",
    2642          114 :                              (withPeriod && j == nKeys - 1) ? "PERIOD " : "",
    2643              :                              quote_identifier(colName));
    2644              :     }
    2645              : 
    2646         2769 :     return nKeys;
    2647              : }
    2648              : 
    2649              : 
    2650              : /* ----------
    2651              :  * pg_get_expr          - Decompile an expression tree
    2652              :  *
    2653              :  * Input: an expression tree in nodeToString form, and a relation OID
    2654              :  *
    2655              :  * Output: reverse-listed expression
    2656              :  *
    2657              :  * Currently, the expression can only refer to a single relation, namely
    2658              :  * the one specified by the second parameter.  This is sufficient for
    2659              :  * partial indexes, column default expressions, etc.  We also support
    2660              :  * Var-free expressions, for which the OID can be InvalidOid.
    2661              :  *
    2662              :  * If the OID is nonzero but not actually valid, don't throw an error,
    2663              :  * just return NULL.  This is a bit questionable, but it's what we've
    2664              :  * done historically, and it can help avoid unwanted failures when
    2665              :  * examining catalog entries for just-deleted relations.
    2666              :  *
    2667              :  * We expect this function to work, or throw a reasonably clean error,
    2668              :  * for any node tree that can appear in a catalog pg_node_tree column.
    2669              :  * Query trees, such as those appearing in pg_rewrite.ev_action, are
    2670              :  * not supported.  Nor are expressions in more than one relation, which
    2671              :  * can appear in places like pg_rewrite.ev_qual.
    2672              :  * ----------
    2673              :  */
    2674              : Datum
    2675         4647 : pg_get_expr(PG_FUNCTION_ARGS)
    2676              : {
    2677         4647 :     text       *expr = PG_GETARG_TEXT_PP(0);
    2678         4647 :     Oid         relid = PG_GETARG_OID(1);
    2679              :     text       *result;
    2680              :     int         prettyFlags;
    2681              : 
    2682         4647 :     prettyFlags = PRETTYFLAG_INDENT;
    2683              : 
    2684         4647 :     result = pg_get_expr_worker(expr, relid, prettyFlags);
    2685         4647 :     if (result)
    2686         4647 :         PG_RETURN_TEXT_P(result);
    2687              :     else
    2688            0 :         PG_RETURN_NULL();
    2689              : }
    2690              : 
    2691              : Datum
    2692          431 : pg_get_expr_ext(PG_FUNCTION_ARGS)
    2693              : {
    2694          431 :     text       *expr = PG_GETARG_TEXT_PP(0);
    2695          431 :     Oid         relid = PG_GETARG_OID(1);
    2696          431 :     bool        pretty = PG_GETARG_BOOL(2);
    2697              :     text       *result;
    2698              :     int         prettyFlags;
    2699              : 
    2700          431 :     prettyFlags = GET_PRETTY_FLAGS(pretty);
    2701              : 
    2702          431 :     result = pg_get_expr_worker(expr, relid, prettyFlags);
    2703          431 :     if (result)
    2704          431 :         PG_RETURN_TEXT_P(result);
    2705              :     else
    2706            0 :         PG_RETURN_NULL();
    2707              : }
    2708              : 
    2709              : static text *
    2710         5078 : pg_get_expr_worker(text *expr, Oid relid, int prettyFlags)
    2711              : {
    2712              :     Node       *node;
    2713              :     Node       *tst;
    2714              :     Relids      relids;
    2715              :     List       *context;
    2716              :     char       *exprstr;
    2717         5078 :     Relation    rel = NULL;
    2718              :     char       *str;
    2719              : 
    2720              :     /* Convert input pg_node_tree (really TEXT) object to C string */
    2721         5078 :     exprstr = text_to_cstring(expr);
    2722              : 
    2723              :     /* Convert expression to node tree */
    2724         5078 :     node = (Node *) stringToNode(exprstr);
    2725              : 
    2726         5078 :     pfree(exprstr);
    2727              : 
    2728              :     /*
    2729              :      * Throw error if the input is a querytree rather than an expression tree.
    2730              :      * While we could support queries here, there seems no very good reason
    2731              :      * to.  In most such catalog columns, we'll see a List of Query nodes, or
    2732              :      * even nested Lists, so drill down to a non-List node before checking.
    2733              :      */
    2734         5078 :     tst = node;
    2735         5078 :     while (tst && IsA(tst, List))
    2736            0 :         tst = linitial((List *) tst);
    2737         5078 :     if (tst && IsA(tst, Query))
    2738            0 :         ereport(ERROR,
    2739              :                 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    2740              :                  errmsg("input is a query, not an expression")));
    2741              : 
    2742              :     /*
    2743              :      * Throw error if the expression contains Vars we won't be able to
    2744              :      * deparse.
    2745              :      */
    2746         5078 :     relids = pull_varnos(NULL, node);
    2747         5078 :     if (OidIsValid(relid))
    2748              :     {
    2749         5036 :         if (!bms_is_subset(relids, bms_make_singleton(1)))
    2750            0 :             ereport(ERROR,
    2751              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    2752              :                      errmsg("expression contains variables of more than one relation")));
    2753              :     }
    2754              :     else
    2755              :     {
    2756           42 :         if (!bms_is_empty(relids))
    2757            0 :             ereport(ERROR,
    2758              :                     (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
    2759              :                      errmsg("expression contains variables")));
    2760              :     }
    2761              : 
    2762              :     /*
    2763              :      * Prepare deparse context if needed.  If we are deparsing with a relid,
    2764              :      * we need to transiently open and lock the rel, to make sure it won't go
    2765              :      * away underneath us.  (set_relation_column_names would lock it anyway,
    2766              :      * so this isn't really introducing any new behavior.)
    2767              :      */
    2768         5078 :     if (OidIsValid(relid))
    2769              :     {
    2770         5036 :         rel = try_relation_open(relid, AccessShareLock);
    2771         5036 :         if (rel == NULL)
    2772            0 :             return NULL;
    2773         5036 :         context = deparse_context_for(RelationGetRelationName(rel), relid);
    2774              :     }
    2775              :     else
    2776           42 :         context = NIL;
    2777              : 
    2778              :     /* Deparse */
    2779         5078 :     str = deparse_expression_pretty(node, context, false, false,
    2780              :                                     prettyFlags, 0);
    2781              : 
    2782         5078 :     if (rel != NULL)
    2783         5036 :         relation_close(rel, AccessShareLock);
    2784              : 
    2785         5078 :     return string_to_text(str);
    2786              : }
    2787              : 
    2788              : 
    2789              : /* ----------
    2790              :  * pg_get_userbyid      - Get a user name by roleid and
    2791              :  *                fallback to 'unknown (OID=n)'
    2792              :  * ----------
    2793              :  */
    2794              : Datum
    2795          926 : pg_get_userbyid(PG_FUNCTION_ARGS)
    2796              : {
    2797          926 :     Oid         roleid = PG_GETARG_OID(0);
    2798              :     Name        result;
    2799              :     HeapTuple   roletup;
    2800              :     Form_pg_authid role_rec;
    2801              : 
    2802              :     /*
    2803              :      * Allocate space for the result
    2804              :      */
    2805          926 :     result = (Name) palloc(NAMEDATALEN);
    2806          926 :     memset(NameStr(*result), 0, NAMEDATALEN);
    2807              : 
    2808              :     /*
    2809              :      * Get the pg_authid entry and print the result
    2810              :      */
    2811          926 :     roletup = SearchSysCache1(AUTHOID, ObjectIdGetDatum(roleid));
    2812          926 :     if (HeapTupleIsValid(roletup))
    2813              :     {
    2814          926 :         role_rec = (Form_pg_authid) GETSTRUCT(roletup);
    2815          926 :         *result = role_rec->rolname;
    2816          926 :         ReleaseSysCache(roletup);
    2817              :     }
    2818              :     else
    2819            0 :         sprintf(NameStr(*result), "unknown (OID=%u)", roleid);
    2820              : 
    2821          926 :     PG_RETURN_NAME(result);
    2822              : }
    2823              : 
    2824              : 
    2825              : /*
    2826              :  * pg_get_serial_sequence
    2827              :  *      Get the name of the sequence used by an identity or serial column,
    2828              :  *      formatted suitably for passing to setval, nextval or currval.
    2829              :  *      First parameter is not treated as double-quoted, second parameter
    2830              :  *      is --- see documentation for reason.
    2831              :  */
    2832              : Datum
    2833            6 : pg_get_serial_sequence(PG_FUNCTION_ARGS)
    2834              : {
    2835            6 :     text       *tablename = PG_GETARG_TEXT_PP(0);
    2836            6 :     text       *columnname = PG_GETARG_TEXT_PP(1);
    2837              :     RangeVar   *tablerv;
    2838              :     Oid         tableOid;
    2839              :     char       *column;
    2840              :     AttrNumber  attnum;
    2841            6 :     Oid         sequenceId = InvalidOid;
    2842              :     Relation    depRel;
    2843              :     ScanKeyData key[3];
    2844              :     SysScanDesc scan;
    2845              :     HeapTuple   tup;
    2846              : 
    2847              :     /* Look up table name.  Can't lock it - we might not have privileges. */
    2848            6 :     tablerv = makeRangeVarFromNameList(textToQualifiedNameList(tablename));
    2849            6 :     tableOid = RangeVarGetRelid(tablerv, NoLock, false);
    2850              : 
    2851              :     /* Get the number of the column */
    2852            6 :     column = text_to_cstring(columnname);
    2853              : 
    2854            6 :     attnum = get_attnum(tableOid, column);
    2855            6 :     if (attnum == InvalidAttrNumber)
    2856            0 :         ereport(ERROR,
    2857              :                 (errcode(ERRCODE_UNDEFINED_COLUMN),
    2858              :                  errmsg("column \"%s\" of relation \"%s\" does not exist",
    2859              :                         column, tablerv->relname)));
    2860              : 
    2861              :     /* Search the dependency table for the dependent sequence */
    2862            6 :     depRel = table_open(DependRelationId, AccessShareLock);
    2863              : 
    2864            6 :     ScanKeyInit(&key[0],
    2865              :                 Anum_pg_depend_refclassid,
    2866              :                 BTEqualStrategyNumber, F_OIDEQ,
    2867              :                 ObjectIdGetDatum(RelationRelationId));
    2868            6 :     ScanKeyInit(&key[1],
    2869              :                 Anum_pg_depend_refobjid,
    2870              :                 BTEqualStrategyNumber, F_OIDEQ,
    2871              :                 ObjectIdGetDatum(tableOid));
    2872            6 :     ScanKeyInit(&key[2],
    2873              :                 Anum_pg_depend_refobjsubid,
    2874              :                 BTEqualStrategyNumber, F_INT4EQ,
    2875              :                 Int32GetDatum(attnum));
    2876              : 
    2877            6 :     scan = systable_beginscan(depRel, DependReferenceIndexId, true,
    2878              :                               NULL, 3, key);
    2879              : 
    2880           15 :     while (HeapTupleIsValid(tup = systable_getnext(scan)))
    2881              :     {
    2882           15 :         Form_pg_depend deprec = (Form_pg_depend) GETSTRUCT(tup);
    2883              : 
    2884              :         /*
    2885              :          * Look for an auto dependency (serial column) or internal dependency
    2886              :          * (identity column) of a sequence on a column.  (We need the relkind
    2887              :          * test because indexes can also have auto dependencies on columns.)
    2888              :          */
    2889           15 :         if (deprec->classid == RelationRelationId &&
    2890            6 :             deprec->objsubid == 0 &&
    2891            6 :             (deprec->deptype == DEPENDENCY_AUTO ||
    2892            9 :              deprec->deptype == DEPENDENCY_INTERNAL) &&
    2893            6 :             get_rel_relkind(deprec->objid) == RELKIND_SEQUENCE)
    2894              :         {
    2895            6 :             sequenceId = deprec->objid;
    2896            6 :             break;
    2897              :         }
    2898              :     }
    2899              : 
    2900            6 :     systable_endscan(scan);
    2901            6 :     table_close(depRel, AccessShareLock);
    2902              : 
    2903            6 :     if (OidIsValid(sequenceId))
    2904              :     {
    2905              :         char       *result;
    2906              : 
    2907            6 :         result = generate_qualified_relation_name(sequenceId);
    2908              : 
    2909            6 :         PG_RETURN_TEXT_P(string_to_text(result));
    2910              :     }
    2911              : 
    2912            0 :     PG_RETURN_NULL();
    2913              : }
    2914              : 
    2915              : 
    2916              : /*
    2917              :  * pg_get_functiondef
    2918              :  *      Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
    2919              :  *      the specified function.
    2920              :  *
    2921              :  * Note: if you change the output format of this function, be careful not
    2922              :  * to break psql's rules (in \ef and \sf) for identifying the start of the
    2923              :  * function body.  To wit: the function body starts on a line that begins with
    2924              :  * "AS ", "BEGIN ", or "RETURN ", and no preceding line will look like that.
    2925              :  */
    2926              : Datum
    2927           86 : pg_get_functiondef(PG_FUNCTION_ARGS)
    2928              : {
    2929           86 :     Oid         funcid = PG_GETARG_OID(0);
    2930              :     StringInfoData buf;
    2931              :     StringInfoData dq;
    2932              :     HeapTuple   proctup;
    2933              :     Form_pg_proc proc;
    2934              :     bool        isfunction;
    2935              :     Datum       tmp;
    2936              :     bool        isnull;
    2937              :     const char *prosrc;
    2938              :     const char *name;
    2939              :     const char *nsp;
    2940              :     float4      procost;
    2941              :     int         oldlen;
    2942              : 
    2943           86 :     initStringInfo(&buf);
    2944              : 
    2945              :     /* Look up the function */
    2946           86 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    2947           86 :     if (!HeapTupleIsValid(proctup))
    2948            3 :         PG_RETURN_NULL();
    2949              : 
    2950           83 :     proc = (Form_pg_proc) GETSTRUCT(proctup);
    2951           83 :     name = NameStr(proc->proname);
    2952              : 
    2953           83 :     if (proc->prokind == PROKIND_AGGREGATE)
    2954            0 :         ereport(ERROR,
    2955              :                 (errcode(ERRCODE_WRONG_OBJECT_TYPE),
    2956              :                  errmsg("\"%s\" is an aggregate function", name)));
    2957              : 
    2958           83 :     isfunction = (proc->prokind != PROKIND_PROCEDURE);
    2959              : 
    2960              :     /*
    2961              :      * We always qualify the function name, to ensure the right function gets
    2962              :      * replaced.
    2963              :      */
    2964           83 :     nsp = get_namespace_name_or_temp(proc->pronamespace);
    2965           83 :     appendStringInfo(&buf, "CREATE OR REPLACE %s %s(",
    2966              :                      isfunction ? "FUNCTION" : "PROCEDURE",
    2967              :                      quote_qualified_identifier(nsp, name));
    2968           83 :     (void) print_function_arguments(&buf, proctup, false, true);
    2969           83 :     appendStringInfoString(&buf, ")\n");
    2970           83 :     if (isfunction)
    2971              :     {
    2972           73 :         appendStringInfoString(&buf, " RETURNS ");
    2973           73 :         print_function_rettype(&buf, proctup);
    2974           73 :         appendStringInfoChar(&buf, '\n');
    2975              :     }
    2976              : 
    2977           83 :     print_function_trftypes(&buf, proctup);
    2978              : 
    2979           83 :     appendStringInfo(&buf, " LANGUAGE %s\n",
    2980           83 :                      quote_identifier(get_language_name(proc->prolang, false)));
    2981              : 
    2982              :     /* Emit some miscellaneous options on one line */
    2983           83 :     oldlen = buf.len;
    2984              : 
    2985           83 :     if (proc->prokind == PROKIND_WINDOW)
    2986            0 :         appendStringInfoString(&buf, " WINDOW");
    2987           83 :     switch (proc->provolatile)
    2988              :     {
    2989            6 :         case PROVOLATILE_IMMUTABLE:
    2990            6 :             appendStringInfoString(&buf, " IMMUTABLE");
    2991            6 :             break;
    2992           15 :         case PROVOLATILE_STABLE:
    2993           15 :             appendStringInfoString(&buf, " STABLE");
    2994           15 :             break;
    2995           62 :         case PROVOLATILE_VOLATILE:
    2996           62 :             break;
    2997              :     }
    2998              : 
    2999           83 :     switch (proc->proparallel)
    3000              :     {
    3001           14 :         case PROPARALLEL_SAFE:
    3002           14 :             appendStringInfoString(&buf, " PARALLEL SAFE");
    3003           14 :             break;
    3004            0 :         case PROPARALLEL_RESTRICTED:
    3005            0 :             appendStringInfoString(&buf, " PARALLEL RESTRICTED");
    3006            0 :             break;
    3007           69 :         case PROPARALLEL_UNSAFE:
    3008           69 :             break;
    3009              :     }
    3010              : 
    3011           83 :     if (proc->proisstrict)
    3012           25 :         appendStringInfoString(&buf, " STRICT");
    3013           83 :     if (proc->prosecdef)
    3014            3 :         appendStringInfoString(&buf, " SECURITY DEFINER");
    3015           83 :     if (proc->proleakproof)
    3016            0 :         appendStringInfoString(&buf, " LEAKPROOF");
    3017              : 
    3018              :     /* This code for the default cost and rows should match functioncmds.c */
    3019           83 :     if (proc->prolang == INTERNALlanguageId ||
    3020           83 :         proc->prolang == ClanguageId)
    3021            5 :         procost = 1;
    3022              :     else
    3023           78 :         procost = 100;
    3024           83 :     if (proc->procost != procost)
    3025            3 :         appendStringInfo(&buf, " COST %g", proc->procost);
    3026              : 
    3027           83 :     if (proc->prorows > 0 && proc->prorows != 1000)
    3028            0 :         appendStringInfo(&buf, " ROWS %g", proc->prorows);
    3029              : 
    3030           83 :     if (proc->prosupport)
    3031              :     {
    3032              :         Oid         argtypes[1];
    3033              : 
    3034              :         /*
    3035              :          * We should qualify the support function's name if it wouldn't be
    3036              :          * resolved by lookup in the current search path.
    3037              :          */
    3038            0 :         argtypes[0] = INTERNALOID;
    3039            0 :         appendStringInfo(&buf, " SUPPORT %s",
    3040              :                          generate_function_name(proc->prosupport, 1,
    3041              :                                                 NIL, argtypes,
    3042              :                                                 false, NULL, false));
    3043              :     }
    3044              : 
    3045           83 :     if (oldlen != buf.len)
    3046           32 :         appendStringInfoChar(&buf, '\n');
    3047              : 
    3048              :     /* Emit any proconfig options, one per line */
    3049           83 :     tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_proconfig, &isnull);
    3050           83 :     if (!isnull)
    3051              :     {
    3052            3 :         ArrayType  *a = DatumGetArrayTypeP(tmp);
    3053              :         int         i;
    3054              : 
    3055              :         Assert(ARR_ELEMTYPE(a) == TEXTOID);
    3056              :         Assert(ARR_NDIM(a) == 1);
    3057              :         Assert(ARR_LBOUND(a)[0] == 1);
    3058              : 
    3059           21 :         for (i = 1; i <= ARR_DIMS(a)[0]; i++)
    3060              :         {
    3061              :             Datum       d;
    3062              : 
    3063           18 :             d = array_ref(a, 1, &i,
    3064              :                           -1 /* varlenarray */ ,
    3065              :                           -1 /* TEXT's typlen */ ,
    3066              :                           false /* TEXT's typbyval */ ,
    3067              :                           TYPALIGN_INT /* TEXT's typalign */ ,
    3068              :                           &isnull);
    3069           18 :             if (!isnull)
    3070              :             {
    3071           18 :                 char       *configitem = TextDatumGetCString(d);
    3072              :                 char       *pos;
    3073              : 
    3074           18 :                 pos = strchr(configitem, '=');
    3075           18 :                 if (pos == NULL)
    3076            0 :                     continue;
    3077           18 :                 *pos++ = '\0';
    3078              : 
    3079           18 :                 appendStringInfo(&buf, " SET %s TO ",
    3080              :                                  quote_identifier(configitem));
    3081              : 
    3082              :                 /*
    3083              :                  * Variables that are marked GUC_LIST_QUOTE were already fully
    3084              :                  * quoted by flatten_set_variable_args() before they were put
    3085              :                  * into the proconfig array.  However, because the quoting
    3086              :                  * rules used there aren't exactly like SQL's, we have to
    3087              :                  * break the list value apart and then quote the elements as
    3088              :                  * string literals.  (The elements may be double-quoted as-is,
    3089              :                  * but we can't just feed them to the SQL parser; it would do
    3090              :                  * the wrong thing with elements that are zero-length or
    3091              :                  * longer than NAMEDATALEN.)  Also, we need a special case for
    3092              :                  * empty lists.
    3093              :                  *
    3094              :                  * Variables that are not so marked should just be emitted as
    3095              :                  * simple string literals.  If the variable is not known to
    3096              :                  * guc.c, we'll do that; this makes it unsafe to use
    3097              :                  * GUC_LIST_QUOTE for extension variables.
    3098              :                  */
    3099           18 :                 if (GetConfigOptionFlags(configitem, true) & GUC_LIST_QUOTE)
    3100              :                 {
    3101              :                     List       *namelist;
    3102              :                     ListCell   *lc;
    3103              : 
    3104              :                     /* Parse string into list of identifiers */
    3105            9 :                     if (!SplitGUCList(pos, ',', &namelist))
    3106              :                     {
    3107              :                         /* this shouldn't fail really */
    3108            0 :                         elog(ERROR, "invalid list syntax in proconfig item");
    3109              :                     }
    3110              :                     /* Special case: represent an empty list as NULL */
    3111            9 :                     if (namelist == NIL)
    3112            3 :                         appendStringInfoString(&buf, "NULL");
    3113           24 :                     foreach(lc, namelist)
    3114              :                     {
    3115           15 :                         char       *curname = (char *) lfirst(lc);
    3116              : 
    3117           15 :                         simple_quote_literal(&buf, curname);
    3118           15 :                         if (lnext(namelist, lc))
    3119            9 :                             appendStringInfoString(&buf, ", ");
    3120              :                     }
    3121              :                 }
    3122              :                 else
    3123            9 :                     simple_quote_literal(&buf, pos);
    3124           18 :                 appendStringInfoChar(&buf, '\n');
    3125              :             }
    3126              :         }
    3127              :     }
    3128              : 
    3129              :     /* And finally the function definition ... */
    3130           83 :     (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
    3131           83 :     if (proc->prolang == SQLlanguageId && !isnull)
    3132              :     {
    3133           57 :         print_function_sqlbody(&buf, proctup);
    3134              :     }
    3135              :     else
    3136              :     {
    3137           26 :         appendStringInfoString(&buf, "AS ");
    3138              : 
    3139           26 :         tmp = SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_probin, &isnull);
    3140           26 :         if (!isnull)
    3141              :         {
    3142            5 :             simple_quote_literal(&buf, TextDatumGetCString(tmp));
    3143            5 :             appendStringInfoString(&buf, ", "); /* assume prosrc isn't null */
    3144              :         }
    3145              : 
    3146           26 :         tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosrc);
    3147           26 :         prosrc = TextDatumGetCString(tmp);
    3148              : 
    3149              :         /*
    3150              :          * We always use dollar quoting.  Figure out a suitable delimiter.
    3151              :          *
    3152              :          * Since the user is likely to be editing the function body string, we
    3153              :          * shouldn't use a short delimiter that he might easily create a
    3154              :          * conflict with.  Hence prefer "$function$"/"$procedure$", but extend
    3155              :          * if needed.
    3156              :          */
    3157           26 :         initStringInfo(&dq);
    3158           26 :         appendStringInfoChar(&dq, '$');
    3159           26 :         appendStringInfoString(&dq, (isfunction ? "function" : "procedure"));
    3160           26 :         while (strstr(prosrc, dq.data) != NULL)
    3161            0 :             appendStringInfoChar(&dq, 'x');
    3162           26 :         appendStringInfoChar(&dq, '$');
    3163              : 
    3164           26 :         appendBinaryStringInfo(&buf, dq.data, dq.len);
    3165           26 :         appendStringInfoString(&buf, prosrc);
    3166           26 :         appendBinaryStringInfo(&buf, dq.data, dq.len);
    3167              :     }
    3168              : 
    3169           83 :     appendStringInfoChar(&buf, '\n');
    3170              : 
    3171           83 :     ReleaseSysCache(proctup);
    3172              : 
    3173           83 :     PG_RETURN_TEXT_P(string_to_text(buf.data));
    3174              : }
    3175              : 
    3176              : /*
    3177              :  * pg_get_function_arguments
    3178              :  *      Get a nicely-formatted list of arguments for a function.
    3179              :  *      This is everything that would go between the parentheses in
    3180              :  *      CREATE FUNCTION.
    3181              :  */
    3182              : Datum
    3183         2334 : pg_get_function_arguments(PG_FUNCTION_ARGS)
    3184              : {
    3185         2334 :     Oid         funcid = PG_GETARG_OID(0);
    3186              :     StringInfoData buf;
    3187              :     HeapTuple   proctup;
    3188              : 
    3189         2334 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    3190         2334 :     if (!HeapTupleIsValid(proctup))
    3191            3 :         PG_RETURN_NULL();
    3192              : 
    3193         2331 :     initStringInfo(&buf);
    3194              : 
    3195         2331 :     (void) print_function_arguments(&buf, proctup, false, true);
    3196              : 
    3197         2331 :     ReleaseSysCache(proctup);
    3198              : 
    3199         2331 :     PG_RETURN_TEXT_P(string_to_text(buf.data));
    3200              : }
    3201              : 
    3202              : /*
    3203              :  * pg_get_function_identity_arguments
    3204              :  *      Get a formatted list of arguments for a function.
    3205              :  *      This is everything that would go between the parentheses in
    3206              :  *      ALTER FUNCTION, etc.  In particular, don't print defaults.
    3207              :  */
    3208              : Datum
    3209         2068 : pg_get_function_identity_arguments(PG_FUNCTION_ARGS)
    3210              : {
    3211         2068 :     Oid         funcid = PG_GETARG_OID(0);
    3212              :     StringInfoData buf;
    3213              :     HeapTuple   proctup;
    3214              : 
    3215         2068 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    3216         2068 :     if (!HeapTupleIsValid(proctup))
    3217            3 :         PG_RETURN_NULL();
    3218              : 
    3219         2065 :     initStringInfo(&buf);
    3220              : 
    3221         2065 :     (void) print_function_arguments(&buf, proctup, false, false);
    3222              : 
    3223         2065 :     ReleaseSysCache(proctup);
    3224              : 
    3225         2065 :     PG_RETURN_TEXT_P(string_to_text(buf.data));
    3226              : }
    3227              : 
    3228              : /*
    3229              :  * pg_get_function_result
    3230              :  *      Get a nicely-formatted version of the result type of a function.
    3231              :  *      This is what would appear after RETURNS in CREATE FUNCTION.
    3232              :  */
    3233              : Datum
    3234         2042 : pg_get_function_result(PG_FUNCTION_ARGS)
    3235              : {
    3236         2042 :     Oid         funcid = PG_GETARG_OID(0);
    3237              :     StringInfoData buf;
    3238              :     HeapTuple   proctup;
    3239              : 
    3240         2042 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    3241         2042 :     if (!HeapTupleIsValid(proctup))
    3242            3 :         PG_RETURN_NULL();
    3243              : 
    3244         2039 :     if (((Form_pg_proc) GETSTRUCT(proctup))->prokind == PROKIND_PROCEDURE)
    3245              :     {
    3246          119 :         ReleaseSysCache(proctup);
    3247          119 :         PG_RETURN_NULL();
    3248              :     }
    3249              : 
    3250         1920 :     initStringInfo(&buf);
    3251              : 
    3252         1920 :     print_function_rettype(&buf, proctup);
    3253              : 
    3254         1920 :     ReleaseSysCache(proctup);
    3255              : 
    3256         1920 :     PG_RETURN_TEXT_P(string_to_text(buf.data));
    3257              : }
    3258              : 
    3259              : /*
    3260              :  * Guts of pg_get_function_result: append the function's return type
    3261              :  * to the specified buffer.
    3262              :  */
    3263              : static void
    3264         1993 : print_function_rettype(StringInfo buf, HeapTuple proctup)
    3265              : {
    3266         1993 :     Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
    3267         1993 :     int         ntabargs = 0;
    3268              :     StringInfoData rbuf;
    3269              : 
    3270         1993 :     initStringInfo(&rbuf);
    3271              : 
    3272         1993 :     if (proc->proretset)
    3273              :     {
    3274              :         /* It might be a table function; try to print the arguments */
    3275          202 :         appendStringInfoString(&rbuf, "TABLE(");
    3276          202 :         ntabargs = print_function_arguments(&rbuf, proctup, true, false);
    3277          202 :         if (ntabargs > 0)
    3278           38 :             appendStringInfoChar(&rbuf, ')');
    3279              :         else
    3280          164 :             resetStringInfo(&rbuf);
    3281              :     }
    3282              : 
    3283         1993 :     if (ntabargs == 0)
    3284              :     {
    3285              :         /* Not a table function, so do the normal thing */
    3286         1955 :         if (proc->proretset)
    3287          164 :             appendStringInfoString(&rbuf, "SETOF ");
    3288         1955 :         appendStringInfoString(&rbuf, format_type_be(proc->prorettype));
    3289              :     }
    3290              : 
    3291         1993 :     appendBinaryStringInfo(buf, rbuf.data, rbuf.len);
    3292         1993 : }
    3293              : 
    3294              : /*
    3295              :  * Common code for pg_get_function_arguments and pg_get_function_result:
    3296              :  * append the desired subset of arguments to buf.  We print only TABLE
    3297              :  * arguments when print_table_args is true, and all the others when it's false.
    3298              :  * We print argument defaults only if print_defaults is true.
    3299              :  * Function return value is the number of arguments printed.
    3300              :  */
    3301              : static int
    3302         4681 : print_function_arguments(StringInfo buf, HeapTuple proctup,
    3303              :                          bool print_table_args, bool print_defaults)
    3304              : {
    3305         4681 :     Form_pg_proc proc = (Form_pg_proc) GETSTRUCT(proctup);
    3306              :     int         numargs;
    3307              :     Oid        *argtypes;
    3308              :     char      **argnames;
    3309              :     char       *argmodes;
    3310         4681 :     int         insertorderbyat = -1;
    3311              :     int         argsprinted;
    3312              :     int         inputargno;
    3313              :     int         nlackdefaults;
    3314         4681 :     List       *argdefaults = NIL;
    3315         4681 :     ListCell   *nextargdefault = NULL;
    3316              :     int         i;
    3317              : 
    3318         4681 :     numargs = get_func_arg_info(proctup,
    3319              :                                 &argtypes, &argnames, &argmodes);
    3320              : 
    3321         4681 :     nlackdefaults = numargs;
    3322         4681 :     if (print_defaults && proc->pronargdefaults > 0)
    3323              :     {
    3324              :         Datum       proargdefaults;
    3325              :         bool        isnull;
    3326              : 
    3327           19 :         proargdefaults = SysCacheGetAttr(PROCOID, proctup,
    3328              :                                          Anum_pg_proc_proargdefaults,
    3329              :                                          &isnull);
    3330           19 :         if (!isnull)
    3331              :         {
    3332              :             char       *str;
    3333              : 
    3334           19 :             str = TextDatumGetCString(proargdefaults);
    3335           19 :             argdefaults = castNode(List, stringToNode(str));
    3336           19 :             pfree(str);
    3337           19 :             nextargdefault = list_head(argdefaults);
    3338              :             /* nlackdefaults counts only *input* arguments lacking defaults */
    3339           19 :             nlackdefaults = proc->pronargs - list_length(argdefaults);
    3340              :         }
    3341              :     }
    3342              : 
    3343              :     /* Check for special treatment of ordered-set aggregates */
    3344         4681 :     if (proc->prokind == PROKIND_AGGREGATE)
    3345              :     {
    3346              :         HeapTuple   aggtup;
    3347              :         Form_pg_aggregate agg;
    3348              : 
    3349          585 :         aggtup = SearchSysCache1(AGGFNOID, ObjectIdGetDatum(proc->oid));
    3350          585 :         if (!HeapTupleIsValid(aggtup))
    3351            0 :             elog(ERROR, "cache lookup failed for aggregate %u",
    3352              :                  proc->oid);
    3353          585 :         agg = (Form_pg_aggregate) GETSTRUCT(aggtup);
    3354          585 :         if (AGGKIND_IS_ORDERED_SET(agg->aggkind))
    3355           26 :             insertorderbyat = agg->aggnumdirectargs;
    3356          585 :         ReleaseSysCache(aggtup);
    3357              :     }
    3358              : 
    3359         4681 :     argsprinted = 0;
    3360         4681 :     inputargno = 0;
    3361         9426 :     for (i = 0; i < numargs; i++)
    3362              :     {
    3363         4745 :         Oid         argtype = argtypes[i];
    3364         4745 :         char       *argname = argnames ? argnames[i] : NULL;
    3365         4745 :         char        argmode = argmodes ? argmodes[i] : PROARGMODE_IN;
    3366              :         const char *modename;
    3367              :         bool        isinput;
    3368              : 
    3369         4745 :         switch (argmode)
    3370              :         {
    3371         3906 :             case PROARGMODE_IN:
    3372              : 
    3373              :                 /*
    3374              :                  * For procedures, explicitly mark all argument modes, so as
    3375              :                  * to avoid ambiguity with the SQL syntax for DROP PROCEDURE.
    3376              :                  */
    3377         3906 :                 if (proc->prokind == PROKIND_PROCEDURE)
    3378          266 :                     modename = "IN ";
    3379              :                 else
    3380         3640 :                     modename = "";
    3381         3906 :                 isinput = true;
    3382         3906 :                 break;
    3383           50 :             case PROARGMODE_INOUT:
    3384           50 :                 modename = "INOUT ";
    3385           50 :                 isinput = true;
    3386           50 :                 break;
    3387          478 :             case PROARGMODE_OUT:
    3388          478 :                 modename = "OUT ";
    3389          478 :                 isinput = false;
    3390          478 :                 break;
    3391           89 :             case PROARGMODE_VARIADIC:
    3392           89 :                 modename = "VARIADIC ";
    3393           89 :                 isinput = true;
    3394           89 :                 break;
    3395          222 :             case PROARGMODE_TABLE:
    3396          222 :                 modename = "";
    3397          222 :                 isinput = false;
    3398          222 :                 break;
    3399            0 :             default:
    3400            0 :                 elog(ERROR, "invalid parameter mode '%c'", argmode);
    3401              :                 modename = NULL;    /* keep compiler quiet */
    3402              :                 isinput = false;
    3403              :                 break;
    3404              :         }
    3405         4745 :         if (isinput)
    3406         4045 :             inputargno++;       /* this is a 1-based counter */
    3407              : 
    3408         4745 :         if (print_table_args != (argmode == PROARGMODE_TABLE))
    3409          382 :             continue;
    3410              : 
    3411         4363 :         if (argsprinted == insertorderbyat)
    3412              :         {
    3413           26 :             if (argsprinted)
    3414           26 :                 appendStringInfoChar(buf, ' ');
    3415           26 :             appendStringInfoString(buf, "ORDER BY ");
    3416              :         }
    3417         4337 :         else if (argsprinted)
    3418         1408 :             appendStringInfoString(buf, ", ");
    3419              : 
    3420         4363 :         appendStringInfoString(buf, modename);
    3421         4363 :         if (argname && argname[0])
    3422         1553 :             appendStringInfo(buf, "%s ", quote_identifier(argname));
    3423         4363 :         appendStringInfoString(buf, format_type_be(argtype));
    3424         4363 :         if (print_defaults && isinput && inputargno > nlackdefaults)
    3425              :         {
    3426              :             Node       *expr;
    3427              : 
    3428              :             Assert(nextargdefault != NULL);
    3429           29 :             expr = (Node *) lfirst(nextargdefault);
    3430           29 :             nextargdefault = lnext(argdefaults, nextargdefault);
    3431              : 
    3432           29 :             appendStringInfo(buf, " DEFAULT %s",
    3433              :                              deparse_expression(expr, NIL, false, false));
    3434              :         }
    3435         4363 :         argsprinted++;
    3436              : 
    3437              :         /* nasty hack: print the last arg twice for variadic ordered-set agg */
    3438         4363 :         if (argsprinted == insertorderbyat && i == numargs - 1)
    3439              :         {
    3440           13 :             i--;
    3441              :             /* aggs shouldn't have defaults anyway, but just to be sure ... */
    3442           13 :             print_defaults = false;
    3443              :         }
    3444              :     }
    3445              : 
    3446         4681 :     return argsprinted;
    3447              : }
    3448              : 
    3449              : static bool
    3450           48 : is_input_argument(int nth, const char *argmodes)
    3451              : {
    3452              :     return (!argmodes
    3453           21 :             || argmodes[nth] == PROARGMODE_IN
    3454            9 :             || argmodes[nth] == PROARGMODE_INOUT
    3455           69 :             || argmodes[nth] == PROARGMODE_VARIADIC);
    3456              : }
    3457              : 
    3458              : /*
    3459              :  * Append used transformed types to specified buffer
    3460              :  */
    3461              : static void
    3462           83 : print_function_trftypes(StringInfo buf, HeapTuple proctup)
    3463              : {
    3464              :     Oid        *trftypes;
    3465              :     int         ntypes;
    3466              : 
    3467           83 :     ntypes = get_func_trftypes(proctup, &trftypes);
    3468           83 :     if (ntypes > 0)
    3469              :     {
    3470              :         int         i;
    3471              : 
    3472            3 :         appendStringInfoString(buf, " TRANSFORM ");
    3473            8 :         for (i = 0; i < ntypes; i++)
    3474              :         {
    3475            5 :             if (i != 0)
    3476            2 :                 appendStringInfoString(buf, ", ");
    3477            5 :             appendStringInfo(buf, "FOR TYPE %s", format_type_be(trftypes[i]));
    3478              :         }
    3479            3 :         appendStringInfoChar(buf, '\n');
    3480              :     }
    3481           83 : }
    3482              : 
    3483              : /*
    3484              :  * Get textual representation of a function argument's default value.  The
    3485              :  * second argument of this function is the argument number among all arguments
    3486              :  * (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
    3487              :  * how information_schema.sql uses it.
    3488              :  */
    3489              : Datum
    3490           27 : pg_get_function_arg_default(PG_FUNCTION_ARGS)
    3491              : {
    3492           27 :     Oid         funcid = PG_GETARG_OID(0);
    3493           27 :     int32       nth_arg = PG_GETARG_INT32(1);
    3494              :     HeapTuple   proctup;
    3495              :     Form_pg_proc proc;
    3496              :     int         numargs;
    3497              :     Oid        *argtypes;
    3498              :     char      **argnames;
    3499              :     char       *argmodes;
    3500              :     int         i;
    3501              :     List       *argdefaults;
    3502              :     Node       *node;
    3503              :     char       *str;
    3504              :     int         nth_inputarg;
    3505              :     Datum       proargdefaults;
    3506              :     bool        isnull;
    3507              :     int         nth_default;
    3508              : 
    3509           27 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    3510           27 :     if (!HeapTupleIsValid(proctup))
    3511            6 :         PG_RETURN_NULL();
    3512              : 
    3513           21 :     numargs = get_func_arg_info(proctup, &argtypes, &argnames, &argmodes);
    3514           21 :     if (nth_arg < 1 || nth_arg > numargs || !is_input_argument(nth_arg - 1, argmodes))
    3515              :     {
    3516            6 :         ReleaseSysCache(proctup);
    3517            6 :         PG_RETURN_NULL();
    3518              :     }
    3519              : 
    3520           15 :     nth_inputarg = 0;
    3521           42 :     for (i = 0; i < nth_arg; i++)
    3522           27 :         if (is_input_argument(i, argmodes))
    3523           24 :             nth_inputarg++;
    3524              : 
    3525           15 :     proargdefaults = SysCacheGetAttr(PROCOID, proctup,
    3526              :                                      Anum_pg_proc_proargdefaults,
    3527              :                                      &isnull);
    3528           15 :     if (isnull)
    3529              :     {
    3530            0 :         ReleaseSysCache(proctup);
    3531            0 :         PG_RETURN_NULL();
    3532              :     }
    3533              : 
    3534           15 :     str = TextDatumGetCString(proargdefaults);
    3535           15 :     argdefaults = castNode(List, stringToNode(str));
    3536           15 :     pfree(str);
    3537              : 
    3538           15 :     proc = (Form_pg_proc) GETSTRUCT(proctup);
    3539              : 
    3540              :     /*
    3541              :      * Calculate index into proargdefaults: proargdefaults corresponds to the
    3542              :      * last N input arguments, where N = pronargdefaults.
    3543              :      */
    3544           15 :     nth_default = nth_inputarg - 1 - (proc->pronargs - proc->pronargdefaults);
    3545              : 
    3546           15 :     if (nth_default < 0 || nth_default >= list_length(argdefaults))
    3547              :     {
    3548            3 :         ReleaseSysCache(proctup);
    3549            3 :         PG_RETURN_NULL();
    3550              :     }
    3551           12 :     node = list_nth(argdefaults, nth_default);
    3552           12 :     str = deparse_expression(node, NIL, false, false);
    3553              : 
    3554           12 :     ReleaseSysCache(proctup);
    3555              : 
    3556           12 :     PG_RETURN_TEXT_P(string_to_text(str));
    3557              : }
    3558              : 
    3559              : static void
    3560          105 : print_function_sqlbody(StringInfo buf, HeapTuple proctup)
    3561              : {
    3562              :     int         numargs;
    3563              :     Oid        *argtypes;
    3564              :     char      **argnames;
    3565              :     char       *argmodes;
    3566          105 :     deparse_namespace dpns = {0};
    3567              :     Datum       tmp;
    3568              :     Node       *n;
    3569              : 
    3570          105 :     dpns.funcname = pstrdup(NameStr(((Form_pg_proc) GETSTRUCT(proctup))->proname));
    3571          105 :     numargs = get_func_arg_info(proctup,
    3572              :                                 &argtypes, &argnames, &argmodes);
    3573          105 :     dpns.numargs = numargs;
    3574          105 :     dpns.argnames = argnames;
    3575              : 
    3576          105 :     tmp = SysCacheGetAttrNotNull(PROCOID, proctup, Anum_pg_proc_prosqlbody);
    3577          105 :     n = stringToNode(TextDatumGetCString(tmp));
    3578              : 
    3579          105 :     if (IsA(n, List))
    3580              :     {
    3581              :         List       *stmts;
    3582              :         ListCell   *lc;
    3583              : 
    3584           82 :         stmts = linitial(castNode(List, n));
    3585              : 
    3586           82 :         appendStringInfoString(buf, "BEGIN ATOMIC\n");
    3587              : 
    3588          159 :         foreach(lc, stmts)
    3589              :         {
    3590           77 :             Query      *query = lfirst_node(Query, lc);
    3591              : 
    3592              :             /* It seems advisable to get at least AccessShareLock on rels */
    3593           77 :             AcquireRewriteLocks(query, false, false);
    3594           77 :             get_query_def(query, buf, list_make1(&dpns), NULL, false,
    3595              :                           PRETTYFLAG_INDENT, WRAP_COLUMN_DEFAULT, 1);
    3596           77 :             appendStringInfoChar(buf, ';');
    3597           77 :             appendStringInfoChar(buf, '\n');
    3598              :         }
    3599              : 
    3600           82 :         appendStringInfoString(buf, "END");
    3601              :     }
    3602              :     else
    3603              :     {
    3604           23 :         Query      *query = castNode(Query, n);
    3605              : 
    3606              :         /* It seems advisable to get at least AccessShareLock on rels */
    3607           23 :         AcquireRewriteLocks(query, false, false);
    3608           23 :         get_query_def(query, buf, list_make1(&dpns), NULL, false,
    3609              :                       0, WRAP_COLUMN_DEFAULT, 0);
    3610              :     }
    3611          105 : }
    3612              : 
    3613              : Datum
    3614         1780 : pg_get_function_sqlbody(PG_FUNCTION_ARGS)
    3615              : {
    3616         1780 :     Oid         funcid = PG_GETARG_OID(0);
    3617              :     StringInfoData buf;
    3618              :     HeapTuple   proctup;
    3619              :     bool        isnull;
    3620              : 
    3621         1780 :     initStringInfo(&buf);
    3622              : 
    3623              :     /* Look up the function */
    3624         1780 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
    3625         1780 :     if (!HeapTupleIsValid(proctup))
    3626            0 :         PG_RETURN_NULL();
    3627              : 
    3628         1780 :     (void) SysCacheGetAttr(PROCOID, proctup, Anum_pg_proc_prosqlbody, &isnull);
    3629         1780 :     if (isnull)
    3630              :     {
    3631         1732 :         ReleaseSysCache(proctup);
    3632         1732 :         PG_RETURN_NULL();
    3633              :     }
    3634              : 
    3635           48 :     print_function_sqlbody(&buf, proctup);
    3636              : 
    3637           48 :     ReleaseSysCache(proctup);
    3638              : 
    3639           48 :     PG_RETURN_TEXT_P(cstring_to_text_with_len(buf.data, buf.len));
    3640              : }
    3641              : 
    3642              : 
    3643              : /*
    3644              :  * deparse_expression           - General utility for deparsing expressions
    3645              :  *
    3646              :  * calls deparse_expression_pretty with all prettyPrinting disabled
    3647              :  */
    3648              : char *
    3649        41415 : deparse_expression(Node *expr, List *dpcontext,
    3650              :                    bool forceprefix, bool showimplicit)
    3651              : {
    3652        41415 :     return deparse_expression_pretty(expr, dpcontext, forceprefix,
    3653              :                                      showimplicit, 0, 0);
    3654              : }
    3655              : 
    3656              : /* ----------
    3657              :  * deparse_expression_pretty    - General utility for deparsing expressions
    3658              :  *
    3659              :  * expr is the node tree to be deparsed.  It must be a transformed expression
    3660              :  * tree (ie, not the raw output of gram.y).
    3661              :  *
    3662              :  * dpcontext is a list of deparse_namespace nodes representing the context
    3663              :  * for interpreting Vars in the node tree.  It can be NIL if no Vars are
    3664              :  * expected.
    3665              :  *
    3666              :  * forceprefix is true to force all Vars to be prefixed with their table names.
    3667              :  *
    3668              :  * showimplicit is true to force all implicit casts to be shown explicitly.
    3669              :  *
    3670              :  * Tries to pretty up the output according to prettyFlags and startIndent.
    3671              :  *
    3672              :  * The result is a palloc'd string.
    3673              :  * ----------
    3674              :  */
    3675              : static char *
    3676        48708 : deparse_expression_pretty(Node *expr, List *dpcontext,
    3677              :                           bool forceprefix, bool showimplicit,
    3678              :                           int prettyFlags, int startIndent)
    3679              : {
    3680              :     StringInfoData buf;
    3681              :     deparse_context context;
    3682              : 
    3683        48708 :     initStringInfo(&buf);
    3684        48708 :     context.buf = &buf;
    3685        48708 :     context.namespaces = dpcontext;
    3686        48708 :     context.resultDesc = NULL;
    3687        48708 :     context.targetList = NIL;
    3688        48708 :     context.windowClause = NIL;
    3689        48708 :     context.varprefix = forceprefix;
    3690        48708 :     context.prettyFlags = prettyFlags;
    3691        48708 :     context.wrapColumn = WRAP_COLUMN_DEFAULT;
    3692        48708 :     context.indentLevel = startIndent;
    3693        48708 :     context.colNamesVisible = true;
    3694        48708 :     context.inGroupBy = false;
    3695        48708 :     context.varInOrderBy = false;
    3696        48708 :     context.appendparents = NULL;
    3697              : 
    3698        48708 :     get_rule_expr(expr, &context, showimplicit);
    3699              : 
    3700        48708 :     return buf.data;
    3701              : }
    3702              : 
    3703              : /* ----------
    3704              :  * deparse_context_for          - Build deparse context for a single relation
    3705              :  *
    3706              :  * Given the reference name (alias) and OID of a relation, build deparsing
    3707              :  * context for an expression referencing only that relation (as varno 1,
    3708              :  * varlevelsup 0).  This is sufficient for many uses of deparse_expression.
    3709              :  * ----------
    3710              :  */
    3711              : List *
    3712        12655 : deparse_context_for(const char *aliasname, Oid relid)
    3713              : {
    3714              :     deparse_namespace *dpns;
    3715              :     RangeTblEntry *rte;
    3716              : 
    3717        12655 :     dpns = palloc0_object(deparse_namespace);
    3718              : 
    3719              :     /* Build a minimal RTE for the rel */
    3720        12655 :     rte = makeNode(RangeTblEntry);
    3721        12655 :     rte->rtekind = RTE_RELATION;
    3722        12655 :     rte->relid = relid;
    3723        12655 :     rte->relkind = RELKIND_RELATION; /* no need for exactness here */
    3724        12655 :     rte->rellockmode = AccessShareLock;
    3725        12655 :     rte->alias = makeAlias(aliasname, NIL);
    3726        12655 :     rte->eref = rte->alias;
    3727        12655 :     rte->lateral = false;
    3728        12655 :     rte->inh = false;
    3729        12655 :     rte->inFromCl = true;
    3730              : 
    3731              :     /* Build one-element rtable */
    3732        12655 :     dpns->rtable = list_make1(rte);
    3733        12655 :     dpns->subplans = NIL;
    3734        12655 :     dpns->ctes = NIL;
    3735        12655 :     dpns->appendrels = NULL;
    3736        12655 :     set_rtable_names(dpns, NIL, NULL);
    3737        12655 :     set_simple_column_names(dpns);
    3738              : 
    3739              :     /* Return a one-deep namespace stack */
    3740        12655 :     return list_make1(dpns);
    3741              : }
    3742              : 
    3743              : /*
    3744              :  * deparse_context_for_plan_tree - Build deparse context for a Plan tree
    3745              :  *
    3746              :  * When deparsing an expression in a Plan tree, we use the plan's rangetable
    3747              :  * to resolve names of simple Vars.  The initialization of column names for
    3748              :  * this is rather expensive if the rangetable is large, and it'll be the same
    3749              :  * for every expression in the Plan tree; so we do it just once and re-use
    3750              :  * the result of this function for each expression.  (Note that the result
    3751              :  * is not usable until set_deparse_context_plan() is applied to it.)
    3752              :  *
    3753              :  * In addition to the PlannedStmt, pass the per-RTE alias names
    3754              :  * assigned by a previous call to select_rtable_names_for_explain.
    3755              :  */
    3756              : List *
    3757        12502 : deparse_context_for_plan_tree(PlannedStmt *pstmt, List *rtable_names)
    3758              : {
    3759              :     deparse_namespace *dpns;
    3760              : 
    3761        12502 :     dpns = palloc0_object(deparse_namespace);
    3762              : 
    3763              :     /* Initialize fields that stay the same across the whole plan tree */
    3764        12502 :     dpns->rtable = pstmt->rtable;
    3765        12502 :     dpns->rtable_names = rtable_names;
    3766        12502 :     dpns->subplans = pstmt->subplans;
    3767        12502 :     dpns->ctes = NIL;
    3768        12502 :     if (pstmt->appendRelations)
    3769              :     {
    3770              :         /* Set up the array, indexed by child relid */
    3771         1974 :         int         ntables = list_length(dpns->rtable);
    3772              :         ListCell   *lc;
    3773              : 
    3774         1974 :         dpns->appendrels = (AppendRelInfo **)
    3775         1974 :             palloc0((ntables + 1) * sizeof(AppendRelInfo *));
    3776        10909 :         foreach(lc, pstmt->appendRelations)
    3777              :         {
    3778         8935 :             AppendRelInfo *appinfo = lfirst_node(AppendRelInfo, lc);
    3779         8935 :             Index       crelid = appinfo->child_relid;
    3780              : 
    3781              :             Assert(crelid > 0 && crelid <= ntables);
    3782              :             Assert(dpns->appendrels[crelid] == NULL);
    3783         8935 :             dpns->appendrels[crelid] = appinfo;
    3784              :         }
    3785              :     }
    3786              :     else
    3787        10528 :         dpns->appendrels = NULL; /* don't need it */
    3788              : 
    3789              :     /*
    3790              :      * Set up column name aliases, ignoring any join RTEs; they don't matter
    3791              :      * because plan trees don't contain any join alias Vars.
    3792              :      */
    3793        12502 :     set_simple_column_names(dpns);
    3794              : 
    3795              :     /* Return a one-deep namespace stack */
    3796        12502 :     return list_make1(dpns);
    3797              : }
    3798              : 
    3799              : /*
    3800              :  * set_deparse_context_plan - Specify Plan node containing expression
    3801              :  *
    3802              :  * When deparsing an expression in a Plan tree, we might have to resolve
    3803              :  * OUTER_VAR, INNER_VAR, or INDEX_VAR references.  To do this, the caller must
    3804              :  * provide the parent Plan node.  Then OUTER_VAR and INNER_VAR references
    3805              :  * can be resolved by drilling down into the left and right child plans.
    3806              :  * Similarly, INDEX_VAR references can be resolved by reference to the
    3807              :  * indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
    3808              :  * ForeignScan and CustomScan nodes.  (Note that we don't currently support
    3809              :  * deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
    3810              :  * for those, we can only deparse the indexqualorig fields, which won't
    3811              :  * contain INDEX_VAR Vars.)
    3812              :  *
    3813              :  * The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
    3814              :  * the most-closely-nested first.  This is needed to resolve PARAM_EXEC
    3815              :  * Params.  Note we assume that all the Plan nodes share the same rtable.
    3816              :  *
    3817              :  * For a ModifyTable plan, we might also need to resolve references to OLD/NEW
    3818              :  * variables in the RETURNING list, so we copy the alias names of the OLD and
    3819              :  * NEW rows from the ModifyTable plan node.
    3820              :  *
    3821              :  * Once this function has been called, deparse_expression() can be called on
    3822              :  * subsidiary expression(s) of the specified Plan node.  To deparse
    3823              :  * expressions of a different Plan node in the same Plan tree, re-call this
    3824              :  * function to identify the new parent Plan node.
    3825              :  *
    3826              :  * The result is the same List passed in; this is a notational convenience.
    3827              :  */
    3828              : List *
    3829        29563 : set_deparse_context_plan(List *dpcontext, Plan *plan, List *ancestors)
    3830              : {
    3831              :     deparse_namespace *dpns;
    3832              : 
    3833              :     /* Should always have one-entry namespace list for Plan deparsing */
    3834              :     Assert(list_length(dpcontext) == 1);
    3835        29563 :     dpns = (deparse_namespace *) linitial(dpcontext);
    3836              : 
    3837              :     /* Set our attention on the specific plan node passed in */
    3838        29563 :     dpns->ancestors = ancestors;
    3839        29563 :     set_deparse_plan(dpns, plan);
    3840              : 
    3841              :     /* For ModifyTable, set aliases for OLD and NEW in RETURNING */
    3842        29563 :     if (IsA(plan, ModifyTable))
    3843              :     {
    3844          111 :         dpns->ret_old_alias = ((ModifyTable *) plan)->returningOldAlias;
    3845          111 :         dpns->ret_new_alias = ((ModifyTable *) plan)->returningNewAlias;
    3846              :     }
    3847              : 
    3848        29563 :     return dpcontext;
    3849              : }
    3850              : 
    3851              : /*
    3852              :  * select_rtable_names_for_explain  - Select RTE aliases for EXPLAIN
    3853              :  *
    3854              :  * Determine the relation aliases we'll use during an EXPLAIN operation.
    3855              :  * This is just a frontend to set_rtable_names.  We have to expose the aliases
    3856              :  * to EXPLAIN because EXPLAIN needs to know the right alias names to print.
    3857              :  */
    3858              : List *
    3859        12502 : select_rtable_names_for_explain(List *rtable, Bitmapset *rels_used)
    3860              : {
    3861              :     deparse_namespace dpns;
    3862              : 
    3863        12502 :     memset(&dpns, 0, sizeof(dpns));
    3864        12502 :     dpns.rtable = rtable;
    3865        12502 :     dpns.subplans = NIL;
    3866        12502 :     dpns.ctes = NIL;
    3867        12502 :     dpns.appendrels = NULL;
    3868        12502 :     set_rtable_names(&dpns, NIL, rels_used);
    3869              :     /* We needn't bother computing column aliases yet */
    3870              : 
    3871        12502 :     return dpns.rtable_names;
    3872              : }
    3873              : 
    3874              : /*
    3875              :  * set_rtable_names: select RTE aliases to be used in printing a query
    3876              :  *
    3877              :  * We fill in dpns->rtable_names with a list of names that is one-for-one with
    3878              :  * the already-filled dpns->rtable list.  Each RTE name is unique among those
    3879              :  * in the new namespace plus any ancestor namespaces listed in
    3880              :  * parent_namespaces.
    3881              :  *
    3882              :  * If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
    3883              :  *
    3884              :  * Note that this function is only concerned with relation names, not column
    3885              :  * names.
    3886              :  */
    3887              : static void
    3888        28179 : set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
    3889              :                  Bitmapset *rels_used)
    3890              : {
    3891              :     HASHCTL     hash_ctl;
    3892              :     HTAB       *names_hash;
    3893              :     NameHashEntry *hentry;
    3894              :     bool        found;
    3895              :     int         rtindex;
    3896              :     ListCell   *lc;
    3897              : 
    3898        28179 :     dpns->rtable_names = NIL;
    3899              :     /* nothing more to do if empty rtable */
    3900        28179 :     if (dpns->rtable == NIL)
    3901          284 :         return;
    3902              : 
    3903              :     /*
    3904              :      * We use a hash table to hold known names, so that this process is O(N)
    3905              :      * not O(N^2) for N names.
    3906              :      */
    3907        27895 :     hash_ctl.keysize = NAMEDATALEN;
    3908        27895 :     hash_ctl.entrysize = sizeof(NameHashEntry);
    3909        27895 :     hash_ctl.hcxt = CurrentMemoryContext;
    3910        27895 :     names_hash = hash_create("set_rtable_names names",
    3911        27895 :                              list_length(dpns->rtable),
    3912              :                              &hash_ctl,
    3913              :                              HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
    3914              : 
    3915              :     /* Preload the hash table with names appearing in parent_namespaces */
    3916        28763 :     foreach(lc, parent_namespaces)
    3917              :     {
    3918          868 :         deparse_namespace *olddpns = (deparse_namespace *) lfirst(lc);
    3919              :         ListCell   *lc2;
    3920              : 
    3921         3149 :         foreach(lc2, olddpns->rtable_names)
    3922              :         {
    3923         2281 :             char       *oldname = (char *) lfirst(lc2);
    3924              : 
    3925         2281 :             if (oldname == NULL)
    3926          168 :                 continue;
    3927         2113 :             hentry = (NameHashEntry *) hash_search(names_hash,
    3928              :                                                    oldname,
    3929              :                                                    HASH_ENTER,
    3930              :                                                    &found);
    3931              :             /* we do not complain about duplicate names in parent namespaces */
    3932         2113 :             hentry->counter = 0;
    3933              :         }
    3934              :     }
    3935              : 
    3936              :     /* Now we can scan the rtable */
    3937        27895 :     rtindex = 1;
    3938        80421 :     foreach(lc, dpns->rtable)
    3939              :     {
    3940        52526 :         RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
    3941              :         char       *refname;
    3942              : 
    3943              :         /* Just in case this takes an unreasonable amount of time ... */
    3944        52526 :         CHECK_FOR_INTERRUPTS();
    3945              : 
    3946        52526 :         if (rels_used && !bms_is_member(rtindex, rels_used))
    3947              :         {
    3948              :             /* Ignore unreferenced RTE */
    3949         9147 :             refname = NULL;
    3950              :         }
    3951        43379 :         else if (rte->alias)
    3952              :         {
    3953              :             /* If RTE has a user-defined alias, prefer that */
    3954        28364 :             refname = rte->alias->aliasname;
    3955              :         }
    3956        15015 :         else if (rte->rtekind == RTE_RELATION)
    3957              :         {
    3958              :             /* Use the current actual name of the relation */
    3959        11455 :             refname = get_rel_name(rte->relid);
    3960              :         }
    3961         3560 :         else if (rte->rtekind == RTE_JOIN)
    3962              :         {
    3963              :             /* Unnamed join has no refname */
    3964          900 :             refname = NULL;
    3965              :         }
    3966              :         else
    3967              :         {
    3968              :             /* Otherwise use whatever the parser assigned */
    3969         2660 :             refname = rte->eref->aliasname;
    3970              :         }
    3971              : 
    3972              :         /*
    3973              :          * If the selected name isn't unique, append digits to make it so, and
    3974              :          * make a new hash entry for it once we've got a unique name.  For a
    3975              :          * very long input name, we might have to truncate to stay within
    3976              :          * NAMEDATALEN.
    3977              :          */
    3978        52526 :         if (refname)
    3979              :         {
    3980        42479 :             hentry = (NameHashEntry *) hash_search(names_hash,
    3981              :                                                    refname,
    3982              :                                                    HASH_ENTER,
    3983              :                                                    &found);
    3984        42479 :             if (found)
    3985              :             {
    3986              :                 /* Name already in use, must choose a new one */
    3987         7653 :                 int         refnamelen = strlen(refname);
    3988         7653 :                 char       *modname = (char *) palloc(refnamelen + 16);
    3989              :                 NameHashEntry *hentry2;
    3990              : 
    3991              :                 do
    3992              :                 {
    3993         7656 :                     hentry->counter++;
    3994              :                     for (;;)
    3995              :                     {
    3996         7662 :                         memcpy(modname, refname, refnamelen);
    3997         7662 :                         sprintf(modname + refnamelen, "_%d", hentry->counter);
    3998         7662 :                         if (strlen(modname) < NAMEDATALEN)
    3999         7656 :                             break;
    4000              :                         /* drop chars from refname to keep all the digits */
    4001            6 :                         refnamelen = pg_mbcliplen(refname, refnamelen,
    4002              :                                                   refnamelen - 1);
    4003              :                     }
    4004         7656 :                     hentry2 = (NameHashEntry *) hash_search(names_hash,
    4005              :                                                             modname,
    4006              :                                                             HASH_ENTER,
    4007              :                                                             &found);
    4008         7656 :                 } while (found);
    4009         7653 :                 hentry2->counter = 0;    /* init new hash entry */
    4010         7653 :                 refname = modname;
    4011              :             }
    4012              :             else
    4013              :             {
    4014              :                 /* Name not previously used, need only initialize hentry */
    4015        34826 :                 hentry->counter = 0;
    4016              :             }
    4017              :         }
    4018              : 
    4019        52526 :         dpns->rtable_names = lappend(dpns->rtable_names, refname);
    4020        52526 :         rtindex++;
    4021              :     }
    4022              : 
    4023        27895 :     hash_destroy(names_hash);
    4024              : }
    4025              : 
    4026              : /*
    4027              :  * set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
    4028              :  *
    4029              :  * For convenience, this is defined to initialize the deparse_namespace struct
    4030              :  * from scratch.
    4031              :  */
    4032              : static void
    4033         2946 : set_deparse_for_query(deparse_namespace *dpns, Query *query,
    4034              :                       List *parent_namespaces)
    4035              : {
    4036              :     ListCell   *lc;
    4037              :     ListCell   *lc2;
    4038              : 
    4039              :     /* Initialize *dpns and fill rtable/ctes links */
    4040         2946 :     memset(dpns, 0, sizeof(deparse_namespace));
    4041         2946 :     dpns->rtable = query->rtable;
    4042         2946 :     dpns->subplans = NIL;
    4043         2946 :     dpns->ctes = query->cteList;
    4044         2946 :     dpns->appendrels = NULL;
    4045         2946 :     dpns->ret_old_alias = query->returningOldAlias;
    4046         2946 :     dpns->ret_new_alias = query->returningNewAlias;
    4047              : 
    4048              :     /* Assign a unique relation alias to each RTE */
    4049         2946 :     set_rtable_names(dpns, parent_namespaces, NULL);
    4050              : 
    4051              :     /* Initialize dpns->rtable_columns to contain zeroed structs */
    4052         2946 :     dpns->rtable_columns = NIL;
    4053         8259 :     while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
    4054         5313 :         dpns->rtable_columns = lappend(dpns->rtable_columns,
    4055              :                                        palloc0(sizeof(deparse_columns)));
    4056              : 
    4057              :     /* If it's a utility query, it won't have a jointree */
    4058         2946 :     if (query->jointree)
    4059              :     {
    4060              :         /* Detect whether global uniqueness of USING names is needed */
    4061         2938 :         dpns->unique_using =
    4062         2938 :             has_dangerous_join_using(dpns, (Node *) query->jointree);
    4063              : 
    4064              :         /*
    4065              :          * Select names for columns merged by USING, via a recursive pass over
    4066              :          * the query jointree.
    4067              :          */
    4068         2938 :         set_using_names(dpns, (Node *) query->jointree, NIL);
    4069              :     }
    4070              : 
    4071              :     /*
    4072              :      * Now assign remaining column aliases for each RTE.  We do this in a
    4073              :      * linear scan of the rtable, so as to process RTEs whether or not they
    4074              :      * are in the jointree (we mustn't miss NEW.*, INSERT target relations,
    4075              :      * etc).  JOIN RTEs must be processed after their children, but this is
    4076              :      * okay because they appear later in the rtable list than their children
    4077              :      * (cf Asserts in identify_join_columns()).
    4078              :      */
    4079         8259 :     forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
    4080              :     {
    4081         5313 :         RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
    4082         5313 :         deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
    4083              : 
    4084         5313 :         if (rte->rtekind == RTE_JOIN)
    4085          758 :             set_join_column_names(dpns, rte, colinfo);
    4086              :         else
    4087         4555 :             set_relation_column_names(dpns, rte, colinfo);
    4088              :     }
    4089         2946 : }
    4090              : 
    4091              : /*
    4092              :  * set_simple_column_names: fill in column aliases for non-query situations
    4093              :  *
    4094              :  * This handles EXPLAIN and cases where we only have relation RTEs.  Without
    4095              :  * a join tree, we can't do anything smart about join RTEs, but we don't
    4096              :  * need to, because EXPLAIN should never see join alias Vars anyway.
    4097              :  * If we find a join RTE we'll just skip it, leaving its deparse_columns
    4098              :  * struct all-zero.  If somehow we try to deparse a join alias Var, we'll
    4099              :  * error out cleanly because the struct's num_cols will be zero.
    4100              :  */
    4101              : static void
    4102        25233 : set_simple_column_names(deparse_namespace *dpns)
    4103              : {
    4104              :     ListCell   *lc;
    4105              :     ListCell   *lc2;
    4106              : 
    4107              :     /* Initialize dpns->rtable_columns to contain zeroed structs */
    4108        25233 :     dpns->rtable_columns = NIL;
    4109        72446 :     while (list_length(dpns->rtable_columns) < list_length(dpns->rtable))
    4110        47213 :         dpns->rtable_columns = lappend(dpns->rtable_columns,
    4111              :                                        palloc0(sizeof(deparse_columns)));
    4112              : 
    4113              :     /* Assign unique column aliases within each non-join RTE */
    4114        72446 :     forboth(lc, dpns->rtable, lc2, dpns->rtable_columns)
    4115              :     {
    4116        47213 :         RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
    4117        47213 :         deparse_columns *colinfo = (deparse_columns *) lfirst(lc2);
    4118              : 
    4119        47213 :         if (rte->rtekind != RTE_JOIN)
    4120        44155 :             set_relation_column_names(dpns, rte, colinfo);
    4121              :     }
    4122        25233 : }
    4123              : 
    4124              : /*
    4125              :  * has_dangerous_join_using: search jointree for unnamed JOIN USING
    4126              :  *
    4127              :  * Merged columns of a JOIN USING may act differently from either of the input
    4128              :  * columns, either because they are merged with COALESCE (in a FULL JOIN) or
    4129              :  * because an implicit coercion of the underlying input column is required.
    4130              :  * In such a case the column must be referenced as a column of the JOIN not as
    4131              :  * a column of either input.  And this is problematic if the join is unnamed
    4132              :  * (alias-less): we cannot qualify the column's name with an RTE name, since
    4133              :  * there is none.  (Forcibly assigning an alias to the join is not a solution,
    4134              :  * since that will prevent legal references to tables below the join.)
    4135              :  * To ensure that every column in the query is unambiguously referenceable,
    4136              :  * we must assign such merged columns names that are globally unique across
    4137              :  * the whole query, aliasing other columns out of the way as necessary.
    4138              :  *
    4139              :  * Because the ensuing re-aliasing is fairly damaging to the readability of
    4140              :  * the query, we don't do this unless we have to.  So, we must pre-scan
    4141              :  * the join tree to see if we have to, before starting set_using_names().
    4142              :  */
    4143              : static bool
    4144         6966 : has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode)
    4145              : {
    4146         6966 :     if (IsA(jtnode, RangeTblRef))
    4147              :     {
    4148              :         /* nothing to do here */
    4149              :     }
    4150         3663 :     else if (IsA(jtnode, FromExpr))
    4151              :     {
    4152         2938 :         FromExpr   *f = (FromExpr *) jtnode;
    4153              :         ListCell   *lc;
    4154              : 
    4155         5555 :         foreach(lc, f->fromlist)
    4156              :         {
    4157         2656 :             if (has_dangerous_join_using(dpns, (Node *) lfirst(lc)))
    4158           39 :                 return true;
    4159              :         }
    4160              :     }
    4161          725 :     else if (IsA(jtnode, JoinExpr))
    4162              :     {
    4163          725 :         JoinExpr   *j = (JoinExpr *) jtnode;
    4164              : 
    4165              :         /* Is it an unnamed JOIN with USING? */
    4166          725 :         if (j->alias == NULL && j->usingClause)
    4167              :         {
    4168              :             /*
    4169              :              * Yes, so check each join alias var to see if any of them are not
    4170              :              * simple references to underlying columns.  If so, we have a
    4171              :              * dangerous situation and must pick unique aliases.
    4172              :              */
    4173          146 :             RangeTblEntry *jrte = rt_fetch(j->rtindex, dpns->rtable);
    4174              : 
    4175              :             /* We need only examine the merged columns */
    4176          301 :             for (int i = 0; i < jrte->joinmergedcols; i++)
    4177              :             {
    4178          194 :                 Node       *aliasvar = list_nth(jrte->joinaliasvars, i);
    4179              : 
    4180          194 :                 if (!IsA(aliasvar, Var))
    4181           39 :                     return true;
    4182              :             }
    4183              :         }
    4184              : 
    4185              :         /* Nope, but inspect children */
    4186          686 :         if (has_dangerous_join_using(dpns, j->larg))
    4187            0 :             return true;
    4188          686 :         if (has_dangerous_join_using(dpns, j->rarg))
    4189            0 :             return true;
    4190              :     }
    4191              :     else
    4192            0 :         elog(ERROR, "unrecognized node type: %d",
    4193              :              (int) nodeTag(jtnode));
    4194         6888 :     return false;
    4195              : }
    4196              : 
    4197              : /*
    4198              :  * set_using_names: select column aliases to be used for merged USING columns
    4199              :  *
    4200              :  * We do this during a recursive descent of the query jointree.
    4201              :  * dpns->unique_using must already be set to determine the global strategy.
    4202              :  *
    4203              :  * Column alias info is saved in the dpns->rtable_columns list, which is
    4204              :  * assumed to be filled with pre-zeroed deparse_columns structs.
    4205              :  *
    4206              :  * parentUsing is a list of all USING aliases assigned in parent joins of
    4207              :  * the current jointree node.  (The passed-in list must not be modified.)
    4208              :  *
    4209              :  * Note that we do not use per-deparse_columns hash tables in this function.
    4210              :  * The number of names that need to be assigned should be small enough that
    4211              :  * we don't need to trouble with that.
    4212              :  */
    4213              : static void
    4214         7131 : set_using_names(deparse_namespace *dpns, Node *jtnode, List *parentUsing)
    4215              : {
    4216         7131 :     if (IsA(jtnode, RangeTblRef))
    4217              :     {
    4218              :         /* nothing to do now */
    4219              :     }
    4220         3696 :     else if (IsA(jtnode, FromExpr))
    4221              :     {
    4222         2938 :         FromExpr   *f = (FromExpr *) jtnode;
    4223              :         ListCell   *lc;
    4224              : 
    4225         5615 :         foreach(lc, f->fromlist)
    4226         2677 :             set_using_names(dpns, (Node *) lfirst(lc), parentUsing);
    4227              :     }
    4228          758 :     else if (IsA(jtnode, JoinExpr))
    4229              :     {
    4230          758 :         JoinExpr   *j = (JoinExpr *) jtnode;
    4231          758 :         RangeTblEntry *rte = rt_fetch(j->rtindex, dpns->rtable);
    4232          758 :         deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
    4233              :         int        *leftattnos;
    4234              :         int        *rightattnos;
    4235              :         deparse_columns *leftcolinfo;
    4236              :         deparse_columns *rightcolinfo;
    4237              :         int         i;
    4238              :         ListCell   *lc;
    4239              : 
    4240              :         /* Get info about the shape of the join */
    4241          758 :         identify_join_columns(j, rte, colinfo);
    4242          758 :         leftattnos = colinfo->leftattnos;
    4243          758 :         rightattnos = colinfo->rightattnos;
    4244              : 
    4245              :         /* Look up the not-yet-filled-in child deparse_columns structs */
    4246          758 :         leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
    4247          758 :         rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
    4248              : 
    4249              :         /*
    4250              :          * If this join is unnamed, then we cannot substitute new aliases at
    4251              :          * this level, so any name requirements pushed down to here must be
    4252              :          * pushed down again to the children.
    4253              :          */
    4254          758 :         if (rte->alias == NULL)
    4255              :         {
    4256          773 :             for (i = 0; i < colinfo->num_cols; i++)
    4257              :             {
    4258           69 :                 char       *colname = colinfo->colnames[i];
    4259              : 
    4260           69 :                 if (colname == NULL)
    4261           12 :                     continue;
    4262              : 
    4263              :                 /* Push down to left column, unless it's a system column */
    4264           57 :                 if (leftattnos[i] > 0)
    4265              :                 {
    4266           51 :                     expand_colnames_array_to(leftcolinfo, leftattnos[i]);
    4267           51 :                     leftcolinfo->colnames[leftattnos[i] - 1] = colname;
    4268              :                 }
    4269              : 
    4270              :                 /* Same on the righthand side */
    4271           57 :                 if (rightattnos[i] > 0)
    4272              :                 {
    4273           57 :                     expand_colnames_array_to(rightcolinfo, rightattnos[i]);
    4274           57 :                     rightcolinfo->colnames[rightattnos[i] - 1] = colname;
    4275              :                 }
    4276              :             }
    4277              :         }
    4278              : 
    4279              :         /*
    4280              :          * If there's a USING clause, select the USING column names and push
    4281              :          * those names down to the children.  We have two strategies:
    4282              :          *
    4283              :          * If dpns->unique_using is true, we force all USING names to be
    4284              :          * unique across the whole query level.  In principle we'd only need
    4285              :          * the names of dangerous USING columns to be globally unique, but to
    4286              :          * safely assign all USING names in a single pass, we have to enforce
    4287              :          * the same uniqueness rule for all of them.  However, if a USING
    4288              :          * column's name has been pushed down from the parent, we should use
    4289              :          * it as-is rather than making a uniqueness adjustment.  This is
    4290              :          * necessary when we're at an unnamed join, and it creates no risk of
    4291              :          * ambiguity.  Also, if there's a user-written output alias for a
    4292              :          * merged column, we prefer to use that rather than the input name;
    4293              :          * this simplifies the logic and seems likely to lead to less aliasing
    4294              :          * overall.
    4295              :          *
    4296              :          * If dpns->unique_using is false, we only need USING names to be
    4297              :          * unique within their own join RTE.  We still need to honor
    4298              :          * pushed-down names, though.
    4299              :          *
    4300              :          * Though significantly different in results, these two strategies are
    4301              :          * implemented by the same code, with only the difference of whether
    4302              :          * to put assigned names into dpns->using_names.
    4303              :          */
    4304          758 :         if (j->usingClause)
    4305              :         {
    4306              :             /* Copy the input parentUsing list so we don't modify it */
    4307          215 :             parentUsing = list_copy(parentUsing);
    4308              : 
    4309              :             /* USING names must correspond to the first join output columns */
    4310          215 :             expand_colnames_array_to(colinfo, list_length(j->usingClause));
    4311          215 :             i = 0;
    4312          508 :             foreach(lc, j->usingClause)
    4313              :             {
    4314          293 :                 char       *colname = strVal(lfirst(lc));
    4315              : 
    4316              :                 /* Assert it's a merged column */
    4317              :                 Assert(leftattnos[i] != 0 && rightattnos[i] != 0);
    4318              : 
    4319              :                 /* Adopt passed-down name if any, else select unique name */
    4320          293 :                 if (colinfo->colnames[i] != NULL)
    4321           51 :                     colname = colinfo->colnames[i];
    4322              :                 else
    4323              :                 {
    4324              :                     /* Prefer user-written output alias if any */
    4325          242 :                     if (rte->alias && i < list_length(rte->alias->colnames))
    4326            0 :                         colname = strVal(list_nth(rte->alias->colnames, i));
    4327              :                     /* Make it appropriately unique */
    4328          242 :                     colname = make_colname_unique(colname, dpns, colinfo);
    4329          242 :                     if (dpns->unique_using)
    4330           66 :                         dpns->using_names = lappend(dpns->using_names,
    4331              :                                                     colname);
    4332              :                     /* Save it as output column name, too */
    4333          242 :                     colinfo->colnames[i] = colname;
    4334              :                 }
    4335              : 
    4336              :                 /* Remember selected names for use later */
    4337          293 :                 colinfo->usingNames = lappend(colinfo->usingNames, colname);
    4338          293 :                 parentUsing = lappend(parentUsing, colname);
    4339              : 
    4340              :                 /* Push down to left column, unless it's a system column */
    4341          293 :                 if (leftattnos[i] > 0)
    4342              :                 {
    4343          293 :                     expand_colnames_array_to(leftcolinfo, leftattnos[i]);
    4344          293 :                     leftcolinfo->colnames[leftattnos[i] - 1] = colname;
    4345              :                 }
    4346              : 
    4347              :                 /* Same on the righthand side */
    4348          293 :                 if (rightattnos[i] > 0)
    4349              :                 {
    4350          293 :                     expand_colnames_array_to(rightcolinfo, rightattnos[i]);
    4351          293 :                     rightcolinfo->colnames[rightattnos[i] - 1] = colname;
    4352              :                 }
    4353              : 
    4354          293 :                 i++;
    4355              :             }
    4356              :         }
    4357              : 
    4358              :         /* Mark child deparse_columns structs with correct parentUsing info */
    4359          758 :         leftcolinfo->parentUsing = parentUsing;
    4360          758 :         rightcolinfo->parentUsing = parentUsing;
    4361              : 
    4362              :         /* Now recursively assign USING column names in children */
    4363          758 :         set_using_names(dpns, j->larg, parentUsing);
    4364          758 :         set_using_names(dpns, j->rarg, parentUsing);
    4365              :     }
    4366              :     else
    4367            0 :         elog(ERROR, "unrecognized node type: %d",
    4368              :              (int) nodeTag(jtnode));
    4369         7131 : }
    4370              : 
    4371              : /*
    4372              :  * set_relation_column_names: select column aliases for a non-join RTE
    4373              :  *
    4374              :  * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
    4375              :  * If any colnames entries are already filled in, those override local
    4376              :  * choices.
    4377              :  */
    4378              : static void
    4379        48710 : set_relation_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
    4380              :                           deparse_columns *colinfo)
    4381              : {
    4382              :     int         ncolumns;
    4383              :     char      **real_colnames;
    4384              :     bool        changed_any;
    4385              :     int         noldcolumns;
    4386              :     int         i;
    4387              :     int         j;
    4388              : 
    4389              :     /*
    4390              :      * Construct an array of the current "real" column names of the RTE.
    4391              :      * real_colnames[] will be indexed by physical column number, with NULL
    4392              :      * entries for dropped columns.
    4393              :      */
    4394        48710 :     if (rte->rtekind == RTE_RELATION)
    4395              :     {
    4396              :         /* Relation --- look to the system catalogs for up-to-date info */
    4397              :         Relation    rel;
    4398              :         TupleDesc   tupdesc;
    4399              : 
    4400        41401 :         rel = relation_open(rte->relid, AccessShareLock);
    4401        41401 :         tupdesc = RelationGetDescr(rel);
    4402              : 
    4403        41401 :         ncolumns = tupdesc->natts;
    4404        41401 :         real_colnames = (char **) palloc(ncolumns * sizeof(char *));
    4405              : 
    4406       260919 :         for (i = 0; i < ncolumns; i++)
    4407              :         {
    4408       219518 :             Form_pg_attribute attr = TupleDescAttr(tupdesc, i);
    4409              : 
    4410       219518 :             if (attr->attisdropped)
    4411         1617 :                 real_colnames[i] = NULL;
    4412              :             else
    4413       217901 :                 real_colnames[i] = pstrdup(NameStr(attr->attname));
    4414              :         }
    4415        41401 :         relation_close(rel, AccessShareLock);
    4416              :     }
    4417              :     else
    4418              :     {
    4419              :         /* Otherwise get the column names from eref or expandRTE() */
    4420              :         List       *colnames;
    4421              :         ListCell   *lc;
    4422              : 
    4423              :         /*
    4424              :          * Functions returning composites have the annoying property that some
    4425              :          * of the composite type's columns might have been dropped since the
    4426              :          * query was parsed.  If possible, use expandRTE() to handle that
    4427              :          * case, since it has the tedious logic needed to find out about
    4428              :          * dropped columns.  However, if we're explaining a plan, then we
    4429              :          * don't have rte->functions because the planner thinks that won't be
    4430              :          * needed later, and that breaks expandRTE().  So in that case we have
    4431              :          * to rely on rte->eref, which may lead us to report a dropped
    4432              :          * column's old name; that seems close enough for EXPLAIN's purposes.
    4433              :          *
    4434              :          * For non-RELATION, non-FUNCTION RTEs, we can just look at rte->eref,
    4435              :          * which should be sufficiently up-to-date: no other RTE types can
    4436              :          * have columns get dropped from under them after parsing.
    4437              :          */
    4438         7309 :         if (rte->rtekind == RTE_FUNCTION && rte->functions != NIL)
    4439              :         {
    4440              :             /* Since we're not creating Vars, rtindex etc. don't matter */
    4441          435 :             expandRTE(rte, 1, 0, VAR_RETURNING_DEFAULT, -1,
    4442              :                       true /* include dropped */ , &colnames, NULL);
    4443              :         }
    4444              :         else
    4445         6874 :             colnames = rte->eref->colnames;
    4446              : 
    4447         7309 :         ncolumns = list_length(colnames);
    4448         7309 :         real_colnames = (char **) palloc(ncolumns * sizeof(char *));
    4449              : 
    4450         7309 :         i = 0;
    4451        23852 :         foreach(lc, colnames)
    4452              :         {
    4453              :             /*
    4454              :              * If the column name we find here is an empty string, then it's a
    4455              :              * dropped column, so change to NULL.
    4456              :              */
    4457        16543 :             char       *cname = strVal(lfirst(lc));
    4458              : 
    4459        16543 :             if (cname[0] == '\0')
    4460           27 :                 cname = NULL;
    4461        16543 :             real_colnames[i] = cname;
    4462        16543 :             i++;
    4463              :         }
    4464              :     }
    4465              : 
    4466              :     /*
    4467              :      * Ensure colinfo->colnames has a slot for each column.  (It could be long
    4468              :      * enough already, if we pushed down a name for the last column.)  Note:
    4469              :      * it's possible that there are now more columns than there were when the
    4470              :      * query was parsed, ie colnames could be longer than rte->eref->colnames.
    4471              :      * We must assign unique aliases to the new columns too, else there could
    4472              :      * be unresolved conflicts when the view/rule is reloaded.
    4473              :      */
    4474        48710 :     expand_colnames_array_to(colinfo, ncolumns);
    4475              :     Assert(colinfo->num_cols == ncolumns);
    4476              : 
    4477              :     /*
    4478              :      * Make sufficiently large new_colnames and is_new_col arrays, too.
    4479              :      *
    4480              :      * Note: because we leave colinfo->num_new_cols zero until after the loop,
    4481              :      * colname_is_unique will not consult that array, which is fine because it
    4482              :      * would only be duplicate effort.
    4483              :      */
    4484        48710 :     colinfo->new_colnames = (char **) palloc(ncolumns * sizeof(char *));
    4485        48710 :     colinfo->is_new_col = (bool *) palloc(ncolumns * sizeof(bool));
    4486              : 
    4487              :     /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
    4488        48710 :     build_colinfo_names_hash(colinfo);
    4489              : 
    4490              :     /*
    4491              :      * Scan the columns, select a unique alias for each one, and store it in
    4492              :      * colinfo->colnames and colinfo->new_colnames.  The former array has NULL
    4493              :      * entries for dropped columns, the latter omits them.  Also mark
    4494              :      * new_colnames entries as to whether they are new since parse time; this
    4495              :      * is the case for entries beyond the length of rte->eref->colnames.
    4496              :      */
    4497        48710 :     noldcolumns = list_length(rte->eref->colnames);
    4498        48710 :     changed_any = false;
    4499        48710 :     j = 0;
    4500       284771 :     for (i = 0; i < ncolumns; i++)
    4501              :     {
    4502       236061 :         char       *real_colname = real_colnames[i];
    4503       236061 :         char       *colname = colinfo->colnames[i];
    4504              : 
    4505              :         /* Skip dropped columns */
    4506       236061 :         if (real_colname == NULL)
    4507              :         {
    4508              :             Assert(colname == NULL);    /* colnames[i] is already NULL */
    4509         1644 :             continue;
    4510              :         }
    4511              : 
    4512              :         /* If alias already assigned, that's what to use */
    4513       234417 :         if (colname == NULL)
    4514              :         {
    4515              :             /* If user wrote an alias, prefer that over real column name */
    4516       233882 :             if (rte->alias && i < list_length(rte->alias->colnames))
    4517        22436 :                 colname = strVal(list_nth(rte->alias->colnames, i));
    4518              :             else
    4519       211446 :                 colname = real_colname;
    4520              : 
    4521              :             /* Unique-ify and insert into colinfo */
    4522       233882 :             colname = make_colname_unique(colname, dpns, colinfo);
    4523              : 
    4524       233882 :             colinfo->colnames[i] = colname;
    4525       233882 :             add_to_names_hash(colinfo, colname);
    4526              :         }
    4527              : 
    4528              :         /* Put names of non-dropped columns in new_colnames[] too */
    4529       234417 :         colinfo->new_colnames[j] = colname;
    4530              :         /* And mark them as new or not */
    4531       234417 :         colinfo->is_new_col[j] = (i >= noldcolumns);
    4532       234417 :         j++;
    4533              : 
    4534              :         /* Remember if any assigned aliases differ from "real" name */
    4535       234417 :         if (!changed_any && strcmp(colname, real_colname) != 0)
    4536          599 :             changed_any = true;
    4537              :     }
    4538              : 
    4539              :     /* We're now done needing the colinfo's names_hash */
    4540        48710 :     destroy_colinfo_names_hash(colinfo);
    4541              : 
    4542              :     /*
    4543              :      * Set correct length for new_colnames[] array.  (Note: if columns have
    4544              :      * been added, colinfo->num_cols includes them, which is not really quite
    4545              :      * right but is harmless, since any new columns must be at the end where
    4546              :      * they won't affect varattnos of pre-existing columns.)
    4547              :      */
    4548        48710 :     colinfo->num_new_cols = j;
    4549              : 
    4550              :     /*
    4551              :      * For a relation RTE, we need only print the alias column names if any
    4552              :      * are different from the underlying "real" names.  For a function RTE,
    4553              :      * always emit a complete column alias list; this is to protect against
    4554              :      * possible instability of the default column names (eg, from altering
    4555              :      * parameter names).  For tablefunc RTEs, we never print aliases, because
    4556              :      * the column names are part of the clause itself.  For other RTE types,
    4557              :      * print if we changed anything OR if there were user-written column
    4558              :      * aliases (since the latter would be part of the underlying "reality").
    4559              :      */
    4560        48710 :     if (rte->rtekind == RTE_RELATION)
    4561        41401 :         colinfo->printaliases = changed_any;
    4562         7309 :     else if (rte->rtekind == RTE_FUNCTION)
    4563          733 :         colinfo->printaliases = true;
    4564         6576 :     else if (rte->rtekind == RTE_TABLEFUNC)
    4565           88 :         colinfo->printaliases = false;
    4566         6488 :     else if (rte->alias && rte->alias->colnames != NIL)
    4567          375 :         colinfo->printaliases = true;
    4568              :     else
    4569         6113 :         colinfo->printaliases = changed_any;
    4570        48710 : }
    4571              : 
    4572              : /*
    4573              :  * set_join_column_names: select column aliases for a join RTE
    4574              :  *
    4575              :  * Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
    4576              :  * If any colnames entries are already filled in, those override local
    4577              :  * choices.  Also, names for USING columns were already chosen by
    4578              :  * set_using_names().  We further expect that column alias selection has been
    4579              :  * completed for both input RTEs.
    4580              :  */
    4581              : static void
    4582          758 : set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
    4583              :                       deparse_columns *colinfo)
    4584              : {
    4585              :     deparse_columns *leftcolinfo;
    4586              :     deparse_columns *rightcolinfo;
    4587              :     bool        changed_any;
    4588              :     int         noldcolumns;
    4589              :     int         nnewcolumns;
    4590          758 :     Bitmapset  *leftmerged = NULL;
    4591          758 :     Bitmapset  *rightmerged = NULL;
    4592              :     int         i;
    4593              :     int         j;
    4594              :     int         ic;
    4595              :     int         jc;
    4596              : 
    4597              :     /* Look up the previously-filled-in child deparse_columns structs */
    4598          758 :     leftcolinfo = deparse_columns_fetch(colinfo->leftrti, dpns);
    4599          758 :     rightcolinfo = deparse_columns_fetch(colinfo->rightrti, dpns);
    4600              : 
    4601              :     /*
    4602              :      * Ensure colinfo->colnames has a slot for each column.  (It could be long
    4603              :      * enough already, if we pushed down a name for the last column.)  Note:
    4604              :      * it's possible that one or both inputs now have more columns than there
    4605              :      * were when the query was parsed, but we'll deal with that below.  We
    4606              :      * only need entries in colnames for pre-existing columns.
    4607              :      */
    4608          758 :     noldcolumns = list_length(rte->eref->colnames);
    4609          758 :     expand_colnames_array_to(colinfo, noldcolumns);
    4610              :     Assert(colinfo->num_cols == noldcolumns);
    4611              : 
    4612              :     /* If the RTE is wide enough, use a hash table to avoid O(N^2) costs */
    4613          758 :     build_colinfo_names_hash(colinfo);
    4614              : 
    4615              :     /*
    4616              :      * Scan the join output columns, select an alias for each one, and store
    4617              :      * it in colinfo->colnames.  If there are USING columns, set_using_names()
    4618              :      * already selected their names, so we can start the loop at the first
    4619              :      * non-merged column.
    4620              :      */
    4621          758 :     changed_any = false;
    4622        25211 :     for (i = list_length(colinfo->usingNames); i < noldcolumns; i++)
    4623              :     {
    4624        24453 :         char       *colname = colinfo->colnames[i];
    4625              :         char       *real_colname;
    4626              : 
    4627              :         /* Join column must refer to at least one input column */
    4628              :         Assert(colinfo->leftattnos[i] != 0 || colinfo->rightattnos[i] != 0);
    4629              : 
    4630              :         /* Get the child column name */
    4631        24453 :         if (colinfo->leftattnos[i] > 0)
    4632        17258 :             real_colname = leftcolinfo->colnames[colinfo->leftattnos[i] - 1];
    4633         7195 :         else if (colinfo->rightattnos[i] > 0)
    4634         7195 :             real_colname = rightcolinfo->colnames[colinfo->rightattnos[i] - 1];
    4635              :         else
    4636              :         {
    4637              :             /* We're joining system columns --- use eref name */
    4638            0 :             real_colname = strVal(list_nth(rte->eref->colnames, i));
    4639              :         }
    4640              : 
    4641              :         /* If child col has been dropped, no need to assign a join colname */
    4642        24453 :         if (real_colname == NULL)
    4643              :         {
    4644            3 :             colinfo->colnames[i] = NULL;
    4645            3 :             continue;
    4646              :         }
    4647              : 
    4648              :         /* In an unnamed join, just report child column names as-is */
    4649        24450 :         if (rte->alias == NULL)
    4650              :         {
    4651        24261 :             colinfo->colnames[i] = real_colname;
    4652        24261 :             add_to_names_hash(colinfo, real_colname);
    4653        24261 :             continue;
    4654              :         }
    4655              : 
    4656              :         /* If alias already assigned, that's what to use */
    4657          189 :         if (colname == NULL)
    4658              :         {
    4659              :             /* If user wrote an alias, prefer that over real column name */
    4660          189 :             if (rte->alias && i < list_length(rte->alias->colnames))
    4661           48 :                 colname = strVal(list_nth(rte->alias->colnames, i));
    4662              :             else
    4663          141 :                 colname = real_colname;
    4664              : 
    4665              :             /* Unique-ify and insert into colinfo */
    4666          189 :             colname = make_colname_unique(colname, dpns, colinfo);
    4667              : 
    4668          189 :             colinfo->colnames[i] = colname;
    4669          189 :             add_to_names_hash(colinfo, colname);
    4670              :         }
    4671              : 
    4672              :         /* Remember if any assigned aliases differ from "real" name */
    4673          189 :         if (!changed_any && strcmp(colname, real_colname) != 0)
    4674           12 :             changed_any = true;
    4675              :     }
    4676              : 
    4677              :     /*
    4678              :      * Calculate number of columns the join would have if it were re-parsed
    4679              :      * now, and create storage for the new_colnames and is_new_col arrays.
    4680              :      *
    4681              :      * Note: colname_is_unique will be consulting new_colnames[] during the
    4682              :      * loops below, so its not-yet-filled entries must be zeroes.
    4683              :      */
    4684         1516 :     nnewcolumns = leftcolinfo->num_new_cols + rightcolinfo->num_new_cols -
    4685          758 :         list_length(colinfo->usingNames);
    4686          758 :     colinfo->num_new_cols = nnewcolumns;
    4687          758 :     colinfo->new_colnames = (char **) palloc0(nnewcolumns * sizeof(char *));
    4688          758 :     colinfo->is_new_col = (bool *) palloc0(nnewcolumns * sizeof(bool));
    4689              : 
    4690              :     /*
    4691              :      * Generating the new_colnames array is a bit tricky since any new columns
    4692              :      * added since parse time must be inserted in the right places.  This code
    4693              :      * must match the parser, which will order a join's columns as merged
    4694              :      * columns first (in USING-clause order), then non-merged columns from the
    4695              :      * left input (in attnum order), then non-merged columns from the right
    4696              :      * input (ditto).  If one of the inputs is itself a join, its columns will
    4697              :      * be ordered according to the same rule, which means newly-added columns
    4698              :      * might not be at the end.  We can figure out what's what by consulting
    4699              :      * the leftattnos and rightattnos arrays plus the input is_new_col arrays.
    4700              :      *
    4701              :      * In these loops, i indexes leftattnos/rightattnos (so it's join varattno
    4702              :      * less one), j indexes new_colnames/is_new_col, and ic/jc have similar
    4703              :      * meanings for the current child RTE.
    4704              :      */
    4705              : 
    4706              :     /* Handle merged columns; they are first and can't be new */
    4707          758 :     i = j = 0;
    4708          758 :     while (i < noldcolumns &&
    4709         1051 :            colinfo->leftattnos[i] != 0 &&
    4710         1051 :            colinfo->rightattnos[i] != 0)
    4711              :     {
    4712              :         /* column name is already determined and known unique */
    4713          293 :         colinfo->new_colnames[j] = colinfo->colnames[i];
    4714          293 :         colinfo->is_new_col[j] = false;
    4715              : 
    4716              :         /* build bitmapsets of child attnums of merged columns */
    4717          293 :         if (colinfo->leftattnos[i] > 0)
    4718          293 :             leftmerged = bms_add_member(leftmerged, colinfo->leftattnos[i]);
    4719          293 :         if (colinfo->rightattnos[i] > 0)
    4720          293 :             rightmerged = bms_add_member(rightmerged, colinfo->rightattnos[i]);
    4721              : 
    4722          293 :         i++, j++;
    4723              :     }
    4724              : 
    4725              :     /* Handle non-merged left-child columns */
    4726          758 :     ic = 0;
    4727        18552 :     for (jc = 0; jc < leftcolinfo->num_new_cols; jc++)
    4728              :     {
    4729        17794 :         char       *child_colname = leftcolinfo->new_colnames[jc];
    4730              : 
    4731        17794 :         if (!leftcolinfo->is_new_col[jc])
    4732              :         {
    4733              :             /* Advance ic to next non-dropped old column of left child */
    4734        17590 :             while (ic < leftcolinfo->num_cols &&
    4735        17590 :                    leftcolinfo->colnames[ic] == NULL)
    4736           42 :                 ic++;
    4737              :             Assert(ic < leftcolinfo->num_cols);
    4738        17548 :             ic++;
    4739              :             /* If it is a merged column, we already processed it */
    4740        17548 :             if (bms_is_member(ic, leftmerged))
    4741          293 :                 continue;
    4742              :             /* Else, advance i to the corresponding existing join column */
    4743        17258 :             while (i < colinfo->num_cols &&
    4744        17258 :                    colinfo->colnames[i] == NULL)
    4745            3 :                 i++;
    4746              :             Assert(i < colinfo->num_cols);
    4747              :             Assert(ic == colinfo->leftattnos[i]);
    4748              :             /* Use the already-assigned name of this column */
    4749        17255 :             colinfo->new_colnames[j] = colinfo->colnames[i];
    4750        17255 :             i++;
    4751              :         }
    4752              :         else
    4753              :         {
    4754              :             /*
    4755              :              * Unique-ify the new child column name and assign, unless we're
    4756              :              * in an unnamed join, in which case just copy
    4757              :              */
    4758          246 :             if (rte->alias != NULL)
    4759              :             {
    4760          132 :                 colinfo->new_colnames[j] =
    4761           66 :                     make_colname_unique(child_colname, dpns, colinfo);
    4762           66 :                 if (!changed_any &&
    4763           54 :                     strcmp(colinfo->new_colnames[j], child_colname) != 0)
    4764            6 :                     changed_any = true;
    4765              :             }
    4766              :             else
    4767          180 :                 colinfo->new_colnames[j] = child_colname;
    4768          246 :             add_to_names_hash(colinfo, colinfo->new_colnames[j]);
    4769              :         }
    4770              : 
    4771        17501 :         colinfo->is_new_col[j] = leftcolinfo->is_new_col[jc];
    4772        17501 :         j++;
    4773              :     }
    4774              : 
    4775              :     /* Handle non-merged right-child columns in exactly the same way */
    4776          758 :     ic = 0;
    4777         8330 :     for (jc = 0; jc < rightcolinfo->num_new_cols; jc++)
    4778              :     {
    4779         7572 :         char       *child_colname = rightcolinfo->new_colnames[jc];
    4780              : 
    4781         7572 :         if (!rightcolinfo->is_new_col[jc])
    4782              :         {
    4783              :             /* Advance ic to next non-dropped old column of right child */
    4784         7488 :             while (ic < rightcolinfo->num_cols &&
    4785         7488 :                    rightcolinfo->colnames[ic] == NULL)
    4786            0 :                 ic++;
    4787              :             Assert(ic < rightcolinfo->num_cols);
    4788         7488 :             ic++;
    4789              :             /* If it is a merged column, we already processed it */
    4790         7488 :             if (bms_is_member(ic, rightmerged))
    4791          293 :                 continue;
    4792              :             /* Else, advance i to the corresponding existing join column */
    4793         7195 :             while (i < colinfo->num_cols &&
    4794         7195 :                    colinfo->colnames[i] == NULL)
    4795            0 :                 i++;
    4796              :             Assert(i < colinfo->num_cols);
    4797              :             Assert(ic == colinfo->rightattnos[i]);
    4798              :             /* Use the already-assigned name of this column */
    4799         7195 :             colinfo->new_colnames[j] = colinfo->colnames[i];
    4800         7195 :             i++;
    4801              :         }
    4802              :         else
    4803              :         {
    4804              :             /*
    4805              :              * Unique-ify the new child column name and assign, unless we're
    4806              :              * in an unnamed join, in which case just copy
    4807              :              */
    4808           84 :             if (rte->alias != NULL)
    4809              :             {
    4810           24 :                 colinfo->new_colnames[j] =
    4811           12 :                     make_colname_unique(child_colname, dpns, colinfo);
    4812           12 :                 if (!changed_any &&
    4813           12 :                     strcmp(colinfo->new_colnames[j], child_colname) != 0)
    4814            6 :                     changed_any = true;
    4815              :             }
    4816              :             else
    4817           72 :                 colinfo->new_colnames[j] = child_colname;
    4818           84 :             add_to_names_hash(colinfo, colinfo->new_colnames[j]);
    4819              :         }
    4820              : 
    4821         7279 :         colinfo->is_new_col[j] = rightcolinfo->is_new_col[jc];
    4822         7279 :         j++;
    4823              :     }
    4824              : 
    4825              :     /* Assert we processed the right number of columns */
    4826              : #ifdef USE_ASSERT_CHECKING
    4827              :     while (i < colinfo->num_cols && colinfo->colnames[i] == NULL)
    4828              :         i++;
    4829              :     Assert(i == colinfo->num_cols);
    4830              :     Assert(j == nnewcolumns);
    4831              : #endif
    4832              : 
    4833              :     /* We're now done needing the colinfo's names_hash */
    4834          758 :     destroy_colinfo_names_hash(colinfo);
    4835              : 
    4836              :     /*
    4837              :      * For a named join, print column aliases if we changed any from the child
    4838              :      * names.  Unnamed joins cannot print aliases.
    4839              :      */
    4840          758 :     if (rte->alias != NULL)
    4841           54 :         colinfo->printaliases = changed_any;
    4842              :     else
    4843          704 :         colinfo->printaliases = false;
    4844          758 : }
    4845              : 
    4846              : /*
    4847              :  * colname_is_unique: is colname distinct from already-chosen column names?
    4848              :  *
    4849              :  * dpns is query-wide info, colinfo is for the column's RTE
    4850              :  */
    4851              : static bool
    4852       235575 : colname_is_unique(const char *colname, deparse_namespace *dpns,
    4853              :                   deparse_columns *colinfo)
    4854              : {
    4855              :     int         i;
    4856              :     ListCell   *lc;
    4857              : 
    4858              :     /*
    4859              :      * If we have a hash table, consult that instead of linearly scanning the
    4860              :      * colinfo's strings.
    4861              :      */
    4862       235575 :     if (colinfo->names_hash)
    4863              :     {
    4864         9001 :         if (hash_search(colinfo->names_hash,
    4865              :                         colname,
    4866              :                         HASH_FIND,
    4867              :                         NULL) != NULL)
    4868            0 :             return false;
    4869              :     }
    4870              :     else
    4871              :     {
    4872              :         /* Check against already-assigned column aliases within RTE */
    4873      3093350 :         for (i = 0; i < colinfo->num_cols; i++)
    4874              :         {
    4875      2867924 :             char       *oldname = colinfo->colnames[i];
    4876              : 
    4877      2867924 :             if (oldname && strcmp(oldname, colname) == 0)
    4878         1148 :                 return false;
    4879              :         }
    4880              : 
    4881              :         /*
    4882              :          * If we're building a new_colnames array, check that too (this will
    4883              :          * be partially but not completely redundant with the previous checks)
    4884              :          */
    4885       226062 :         for (i = 0; i < colinfo->num_new_cols; i++)
    4886              :         {
    4887          648 :             char       *oldname = colinfo->new_colnames[i];
    4888              : 
    4889          648 :             if (oldname && strcmp(oldname, colname) == 0)
    4890           12 :                 return false;
    4891              :         }
    4892              : 
    4893              :         /*
    4894              :          * Also check against names already assigned for parent-join USING
    4895              :          * cols
    4896              :          */
    4897       226716 :         foreach(lc, colinfo->parentUsing)
    4898              :         {
    4899         1305 :             char       *oldname = (char *) lfirst(lc);
    4900              : 
    4901         1305 :             if (strcmp(oldname, colname) == 0)
    4902            3 :                 return false;
    4903              :         }
    4904              :     }
    4905              : 
    4906              :     /*
    4907              :      * Also check against USING-column names that must be globally unique.
    4908              :      * These are not hashed, but there should be few of them.
    4909              :      */
    4910       234841 :     foreach(lc, dpns->using_names)
    4911              :     {
    4912          450 :         char       *oldname = (char *) lfirst(lc);
    4913              : 
    4914          450 :         if (strcmp(oldname, colname) == 0)
    4915           21 :             return false;
    4916              :     }
    4917              : 
    4918       234391 :     return true;
    4919              : }
    4920              : 
    4921              : /*
    4922              :  * make_colname_unique: modify colname if necessary to make it unique
    4923              :  *
    4924              :  * dpns is query-wide info, colinfo is for the column's RTE
    4925              :  */
    4926              : static char *
    4927       234391 : make_colname_unique(char *colname, deparse_namespace *dpns,
    4928              :                     deparse_columns *colinfo)
    4929              : {
    4930              :     /*
    4931              :      * If the selected name isn't unique, append digits to make it so.  For a
    4932              :      * very long input name, we might have to truncate to stay within
    4933              :      * NAMEDATALEN.
    4934              :      */
    4935       234391 :     if (!colname_is_unique(colname, dpns, colinfo))
    4936              :     {
    4937          822 :         int         colnamelen = strlen(colname);
    4938          822 :         char       *modname = (char *) palloc(colnamelen + 16);
    4939          822 :         int         i = 0;
    4940              : 
    4941              :         do
    4942              :         {
    4943         1184 :             i++;
    4944              :             for (;;)
    4945              :             {
    4946         1184 :                 memcpy(modname, colname, colnamelen);
    4947         1184 :                 sprintf(modname + colnamelen, "_%d", i);
    4948         1184 :                 if (strlen(modname) < NAMEDATALEN)
    4949         1184 :                     break;
    4950              :                 /* drop chars from colname to keep all the digits */
    4951            0 :                 colnamelen = pg_mbcliplen(colname, colnamelen,
    4952              :                                           colnamelen - 1);
    4953              :             }
    4954         1184 :         } while (!colname_is_unique(modname, dpns, colinfo));
    4955          822 :         colname = modname;
    4956              :     }
    4957       234391 :     return colname;
    4958              : }
    4959              : 
    4960              : /*
    4961              :  * expand_colnames_array_to: make colinfo->colnames at least n items long
    4962              :  *
    4963              :  * Any added array entries are initialized to zero.
    4964              :  */
    4965              : static void
    4966        50377 : expand_colnames_array_to(deparse_columns *colinfo, int n)
    4967              : {
    4968        50377 :     if (n > colinfo->num_cols)
    4969              :     {
    4970        49015 :         if (colinfo->colnames == NULL)
    4971        48298 :             colinfo->colnames = palloc0_array(char *, n);
    4972              :         else
    4973          717 :             colinfo->colnames = repalloc0_array(colinfo->colnames, char *, colinfo->num_cols, n);
    4974        49015 :         colinfo->num_cols = n;
    4975              :     }
    4976        50377 : }
    4977              : 
    4978              : /*
    4979              :  * build_colinfo_names_hash: optionally construct a hash table for colinfo
    4980              :  */
    4981              : static void
    4982        49468 : build_colinfo_names_hash(deparse_columns *colinfo)
    4983              : {
    4984              :     HASHCTL     hash_ctl;
    4985              :     int         i;
    4986              :     ListCell   *lc;
    4987              : 
    4988              :     /*
    4989              :      * Use a hash table only for RTEs with at least 32 columns.  (The cutoff
    4990              :      * is somewhat arbitrary, but let's choose it so that this code does get
    4991              :      * exercised in the regression tests.)
    4992              :      */
    4993        49468 :     if (colinfo->num_cols < 32)
    4994        48792 :         return;
    4995              : 
    4996              :     /*
    4997              :      * Set up the hash table.  The entries are just strings with no other
    4998              :      * payload.
    4999              :      */
    5000          676 :     hash_ctl.keysize = NAMEDATALEN;
    5001          676 :     hash_ctl.entrysize = NAMEDATALEN;
    5002          676 :     hash_ctl.hcxt = CurrentMemoryContext;
    5003         1352 :     colinfo->names_hash = hash_create("deparse_columns names",
    5004          676 :                                       colinfo->num_cols + colinfo->num_new_cols,
    5005              :                                       &hash_ctl,
    5006              :                                       HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
    5007              : 
    5008              :     /*
    5009              :      * Preload the hash table with any names already present (these would have
    5010              :      * come from set_using_names).
    5011              :      */
    5012        31703 :     for (i = 0; i < colinfo->num_cols; i++)
    5013              :     {
    5014        31027 :         char       *oldname = colinfo->colnames[i];
    5015              : 
    5016        31027 :         if (oldname)
    5017            0 :             add_to_names_hash(colinfo, oldname);
    5018              :     }
    5019              : 
    5020          676 :     for (i = 0; i < colinfo->num_new_cols; i++)
    5021              :     {
    5022            0 :         char       *oldname = colinfo->new_colnames[i];
    5023              : 
    5024            0 :         if (oldname)
    5025            0 :             add_to_names_hash(colinfo, oldname);
    5026              :     }
    5027              : 
    5028          676 :     foreach(lc, colinfo->parentUsing)
    5029              :     {
    5030            0 :         char       *oldname = (char *) lfirst(lc);
    5031              : 
    5032            0 :         add_to_names_hash(colinfo, oldname);
    5033              :     }
    5034              : }
    5035              : 
    5036              : /*
    5037              :  * add_to_names_hash: add a string to the names_hash, if we're using one
    5038              :  */
    5039              : static void
    5040       258662 : add_to_names_hash(deparse_columns *colinfo, const char *name)
    5041              : {
    5042       258662 :     if (colinfo->names_hash)
    5043        31027 :         (void) hash_search(colinfo->names_hash,
    5044              :                            name,
    5045              :                            HASH_ENTER,
    5046              :                            NULL);
    5047       258662 : }
    5048              : 
    5049              : /*
    5050              :  * destroy_colinfo_names_hash: destroy hash table when done with it
    5051              :  */
    5052              : static void
    5053        49468 : destroy_colinfo_names_hash(deparse_columns *colinfo)
    5054              : {
    5055        49468 :     if (colinfo->names_hash)
    5056              :     {
    5057          676 :         hash_destroy(colinfo->names_hash);
    5058          676 :         colinfo->names_hash = NULL;
    5059              :     }
    5060        49468 : }
    5061              : 
    5062              : /*
    5063              :  * identify_join_columns: figure out where columns of a join come from
    5064              :  *
    5065              :  * Fills the join-specific fields of the colinfo struct, except for
    5066              :  * usingNames which is filled later.
    5067              :  */
    5068              : static void
    5069          758 : identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
    5070              :                       deparse_columns *colinfo)
    5071              : {
    5072              :     int         numjoincols;
    5073              :     int         jcolno;
    5074              :     int         rcolno;
    5075              :     ListCell   *lc;
    5076              : 
    5077              :     /* Extract left/right child RT indexes */
    5078          758 :     if (IsA(j->larg, RangeTblRef))
    5079          485 :         colinfo->leftrti = ((RangeTblRef *) j->larg)->rtindex;
    5080          273 :     else if (IsA(j->larg, JoinExpr))
    5081          273 :         colinfo->leftrti = ((JoinExpr *) j->larg)->rtindex;
    5082              :     else
    5083            0 :         elog(ERROR, "unrecognized node type in jointree: %d",
    5084              :              (int) nodeTag(j->larg));
    5085          758 :     if (IsA(j->rarg, RangeTblRef))
    5086          758 :         colinfo->rightrti = ((RangeTblRef *) j->rarg)->rtindex;
    5087            0 :     else if (IsA(j->rarg, JoinExpr))
    5088            0 :         colinfo->rightrti = ((JoinExpr *) j->rarg)->rtindex;
    5089              :     else
    5090            0 :         elog(ERROR, "unrecognized node type in jointree: %d",
    5091              :              (int) nodeTag(j->rarg));
    5092              : 
    5093              :     /* Assert children will be processed earlier than join in second pass */
    5094              :     Assert(colinfo->leftrti < j->rtindex);
    5095              :     Assert(colinfo->rightrti < j->rtindex);
    5096              : 
    5097              :     /* Initialize result arrays with zeroes */
    5098          758 :     numjoincols = list_length(jrte->joinaliasvars);
    5099              :     Assert(numjoincols == list_length(jrte->eref->colnames));
    5100          758 :     colinfo->leftattnos = (int *) palloc0(numjoincols * sizeof(int));
    5101          758 :     colinfo->rightattnos = (int *) palloc0(numjoincols * sizeof(int));
    5102              : 
    5103              :     /*
    5104              :      * Deconstruct RTE's joinleftcols/joinrightcols into desired format.
    5105              :      * Recall that the column(s) merged due to USING are the first column(s)
    5106              :      * of the join output.  We need not do anything special while scanning
    5107              :      * joinleftcols, but while scanning joinrightcols we must distinguish
    5108              :      * merged from unmerged columns.
    5109              :      */
    5110          758 :     jcolno = 0;
    5111        18309 :     foreach(lc, jrte->joinleftcols)
    5112              :     {
    5113        17551 :         int         leftattno = lfirst_int(lc);
    5114              : 
    5115        17551 :         colinfo->leftattnos[jcolno++] = leftattno;
    5116              :     }
    5117          758 :     rcolno = 0;
    5118         8246 :     foreach(lc, jrte->joinrightcols)
    5119              :     {
    5120         7488 :         int         rightattno = lfirst_int(lc);
    5121              : 
    5122         7488 :         if (rcolno < jrte->joinmergedcols)    /* merged column? */
    5123          293 :             colinfo->rightattnos[rcolno] = rightattno;
    5124              :         else
    5125         7195 :             colinfo->rightattnos[jcolno++] = rightattno;
    5126         7488 :         rcolno++;
    5127              :     }
    5128              :     Assert(jcolno == numjoincols);
    5129          758 : }
    5130              : 
    5131              : /*
    5132              :  * get_rtable_name: convenience function to get a previously assigned RTE alias
    5133              :  *
    5134              :  * The RTE must belong to the topmost namespace level in "context".
    5135              :  */
    5136              : static char *
    5137         3361 : get_rtable_name(int rtindex, deparse_context *context)
    5138              : {
    5139         3361 :     deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
    5140              : 
    5141              :     Assert(rtindex > 0 && rtindex <= list_length(dpns->rtable_names));
    5142         3361 :     return (char *) list_nth(dpns->rtable_names, rtindex - 1);
    5143              : }
    5144              : 
    5145              : /*
    5146              :  * set_deparse_plan: set up deparse_namespace to parse subexpressions
    5147              :  * of a given Plan node
    5148              :  *
    5149              :  * This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
    5150              :  * and index_tlist fields.  Caller must already have adjusted the ancestors
    5151              :  * list if necessary.  Note that the rtable, subplans, and ctes fields do
    5152              :  * not need to change when shifting attention to different plan nodes in a
    5153              :  * single plan tree.
    5154              :  */
    5155              : static void
    5156        76153 : set_deparse_plan(deparse_namespace *dpns, Plan *plan)
    5157              : {
    5158        76153 :     dpns->plan = plan;
    5159              : 
    5160              :     /*
    5161              :      * We special-case Append and MergeAppend to pretend that the first child
    5162              :      * plan is the OUTER referent; we have to interpret OUTER Vars in their
    5163              :      * tlists according to one of the children, and the first one is the most
    5164              :      * natural choice.
    5165              :      */
    5166        76153 :     if (IsA(plan, Append))
    5167         2250 :         dpns->outer_plan = linitial(((Append *) plan)->appendplans);
    5168        73903 :     else if (IsA(plan, MergeAppend))
    5169          276 :         dpns->outer_plan = linitial(((MergeAppend *) plan)->mergeplans);
    5170              :     else
    5171        73627 :         dpns->outer_plan = outerPlan(plan);
    5172              : 
    5173        76153 :     if (dpns->outer_plan)
    5174        36813 :         dpns->outer_tlist = dpns->outer_plan->targetlist;
    5175              :     else
    5176        39340 :         dpns->outer_tlist = NIL;
    5177              : 
    5178              :     /*
    5179              :      * For a SubqueryScan, pretend the subplan is INNER referent.  (We don't
    5180              :      * use OUTER because that could someday conflict with the normal meaning.)
    5181              :      * Likewise, for a CteScan, pretend the subquery's plan is INNER referent.
    5182              :      * For a WorkTableScan, locate the parent RecursiveUnion plan node and use
    5183              :      * that as INNER referent.
    5184              :      *
    5185              :      * For MERGE, pretend the ModifyTable's source plan (its outer plan) is
    5186              :      * INNER referent.  This is the join from the target relation to the data
    5187              :      * source, and all INNER_VAR Vars in other parts of the query refer to its
    5188              :      * targetlist.
    5189              :      *
    5190              :      * For ON CONFLICT DO SELECT/UPDATE we just need the inner tlist to point
    5191              :      * to the excluded expression's tlist. (Similar to the SubqueryScan we
    5192              :      * don't want to reuse OUTER, it's used for RETURNING in some modify table
    5193              :      * cases, although not INSERT .. CONFLICT).
    5194              :      */
    5195        76153 :     if (IsA(plan, SubqueryScan))
    5196          343 :         dpns->inner_plan = ((SubqueryScan *) plan)->subplan;
    5197        75810 :     else if (IsA(plan, CteScan))
    5198          286 :         dpns->inner_plan = list_nth(dpns->subplans,
    5199          286 :                                     ((CteScan *) plan)->ctePlanId - 1);
    5200        75524 :     else if (IsA(plan, WorkTableScan))
    5201           87 :         dpns->inner_plan = find_recursive_union(dpns,
    5202              :                                                 (WorkTableScan *) plan);
    5203        75437 :     else if (IsA(plan, ModifyTable))
    5204              :     {
    5205          222 :         if (((ModifyTable *) plan)->operation == CMD_MERGE)
    5206           30 :             dpns->inner_plan = outerPlan(plan);
    5207              :         else
    5208          192 :             dpns->inner_plan = plan;
    5209              :     }
    5210              :     else
    5211        75215 :         dpns->inner_plan = innerPlan(plan);
    5212              : 
    5213        76153 :     if (IsA(plan, ModifyTable) && ((ModifyTable *) plan)->operation == CMD_INSERT)
    5214          106 :         dpns->inner_tlist = ((ModifyTable *) plan)->exclRelTlist;
    5215        76047 :     else if (dpns->inner_plan)
    5216        13229 :         dpns->inner_tlist = dpns->inner_plan->targetlist;
    5217              :     else
    5218        62818 :         dpns->inner_tlist = NIL;
    5219              : 
    5220              :     /* Set up referent for INDEX_VAR Vars, if needed */
    5221        76153 :     if (IsA(plan, IndexOnlyScan))
    5222         1742 :         dpns->index_tlist = ((IndexOnlyScan *) plan)->indextlist;
    5223        74411 :     else if (IsA(plan, ForeignScan))
    5224         1558 :         dpns->index_tlist = ((ForeignScan *) plan)->fdw_scan_tlist;
    5225        72853 :     else if (IsA(plan, CustomScan))
    5226            0 :         dpns->index_tlist = ((CustomScan *) plan)->custom_scan_tlist;
    5227              :     else
    5228        72853 :         dpns->index_tlist = NIL;
    5229        76153 : }
    5230              : 
    5231              : /*
    5232              :  * Locate the ancestor plan node that is the RecursiveUnion generating
    5233              :  * the WorkTableScan's work table.  We can match on wtParam, since that
    5234              :  * should be unique within the plan tree.
    5235              :  */
    5236              : static Plan *
    5237           87 : find_recursive_union(deparse_namespace *dpns, WorkTableScan *wtscan)
    5238              : {
    5239              :     ListCell   *lc;
    5240              : 
    5241          219 :     foreach(lc, dpns->ancestors)
    5242              :     {
    5243          219 :         Plan       *ancestor = (Plan *) lfirst(lc);
    5244              : 
    5245          219 :         if (IsA(ancestor, RecursiveUnion) &&
    5246           87 :             ((RecursiveUnion *) ancestor)->wtParam == wtscan->wtParam)
    5247           87 :             return ancestor;
    5248              :     }
    5249            0 :     elog(ERROR, "could not find RecursiveUnion for WorkTableScan with wtParam %d",
    5250              :          wtscan->wtParam);
    5251              :     return NULL;
    5252              : }
    5253              : 
    5254              : /*
    5255              :  * push_child_plan: temporarily transfer deparsing attention to a child plan
    5256              :  *
    5257              :  * When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
    5258              :  * deparse context in case the referenced expression itself uses
    5259              :  * OUTER_VAR/INNER_VAR.  We modify the top stack entry in-place to avoid
    5260              :  * affecting levelsup issues (although in a Plan tree there really shouldn't
    5261              :  * be any).
    5262              :  *
    5263              :  * Caller must provide a local deparse_namespace variable to save the
    5264              :  * previous state for pop_child_plan.
    5265              :  */
    5266              : static void
    5267        44264 : push_child_plan(deparse_namespace *dpns, Plan *plan,
    5268              :                 deparse_namespace *save_dpns)
    5269              : {
    5270              :     /* Save state for restoration later */
    5271        44264 :     *save_dpns = *dpns;
    5272              : 
    5273              :     /* Link current plan node into ancestors list */
    5274        44264 :     dpns->ancestors = lcons(dpns->plan, dpns->ancestors);
    5275              : 
    5276              :     /* Set attention on selected child */
    5277        44264 :     set_deparse_plan(dpns, plan);
    5278        44264 : }
    5279              : 
    5280              : /*
    5281              :  * pop_child_plan: undo the effects of push_child_plan
    5282              :  */
    5283              : static void
    5284        44264 : pop_child_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
    5285              : {
    5286              :     List       *ancestors;
    5287              : 
    5288              :     /* Get rid of ancestors list cell added by push_child_plan */
    5289        44264 :     ancestors = list_delete_first(dpns->ancestors);
    5290              : 
    5291              :     /* Restore fields changed by push_child_plan */
    5292        44264 :     *dpns = *save_dpns;
    5293              : 
    5294              :     /* Make sure dpns->ancestors is right (may be unnecessary) */
    5295        44264 :     dpns->ancestors = ancestors;
    5296        44264 : }
    5297              : 
    5298              : /*
    5299              :  * push_ancestor_plan: temporarily transfer deparsing attention to an
    5300              :  * ancestor plan
    5301              :  *
    5302              :  * When expanding a Param reference, we must adjust the deparse context
    5303              :  * to match the plan node that contains the expression being printed;
    5304              :  * otherwise we'd fail if that expression itself contains a Param or
    5305              :  * OUTER_VAR/INNER_VAR/INDEX_VAR variable.
    5306              :  *
    5307              :  * The target ancestor is conveniently identified by the ListCell holding it
    5308              :  * in dpns->ancestors.
    5309              :  *
    5310              :  * Caller must provide a local deparse_namespace variable to save the
    5311              :  * previous state for pop_ancestor_plan.
    5312              :  */
    5313              : static void
    5314         2326 : push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
    5315              :                    deparse_namespace *save_dpns)
    5316              : {
    5317         2326 :     Plan       *plan = (Plan *) lfirst(ancestor_cell);
    5318              : 
    5319              :     /* Save state for restoration later */
    5320         2326 :     *save_dpns = *dpns;
    5321              : 
    5322              :     /* Build a new ancestor list with just this node's ancestors */
    5323         2326 :     dpns->ancestors =
    5324         2326 :         list_copy_tail(dpns->ancestors,
    5325         2326 :                        list_cell_number(dpns->ancestors, ancestor_cell) + 1);
    5326              : 
    5327              :     /* Set attention on selected ancestor */
    5328         2326 :     set_deparse_plan(dpns, plan);
    5329         2326 : }
    5330              : 
    5331              : /*
    5332              :  * pop_ancestor_plan: undo the effects of push_ancestor_plan
    5333              :  */
    5334              : static void
    5335         2326 : pop_ancestor_plan(deparse_namespace *dpns, deparse_namespace *save_dpns)
    5336              : {
    5337              :     /* Free the ancestor list made in push_ancestor_plan */
    5338         2326 :     list_free(dpns->ancestors);
    5339              : 
    5340              :     /* Restore fields changed by push_ancestor_plan */
    5341         2326 :     *dpns = *save_dpns;
    5342         2326 : }
    5343              : 
    5344              : 
    5345              : /* ----------
    5346              :  * make_ruledef         - reconstruct the CREATE RULE command
    5347              :  *                for a given pg_rewrite tuple
    5348              :  * ----------
    5349              :  */
    5350              : static void
    5351          282 : make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
    5352              :              int prettyFlags)
    5353              : {
    5354              :     char       *rulename;
    5355              :     char        ev_type;
    5356              :     Oid         ev_class;
    5357              :     bool        is_instead;
    5358              :     char       *ev_qual;
    5359              :     char       *ev_action;
    5360              :     List       *actions;
    5361              :     Relation    ev_relation;
    5362          282 :     TupleDesc   viewResultDesc = NULL;
    5363              :     int         fno;
    5364              :     Datum       dat;
    5365              :     bool        isnull;
    5366              : 
    5367              :     /*
    5368              :      * Get the attribute values from the rules tuple
    5369              :      */
    5370          282 :     fno = SPI_fnumber(rulettc, "rulename");
    5371          282 :     dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
    5372              :     Assert(!isnull);
    5373          282 :     rulename = NameStr(*(DatumGetName(dat)));
    5374              : 
    5375          282 :     fno = SPI_fnumber(rulettc, "ev_type");
    5376          282 :     dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
    5377              :     Assert(!isnull);
    5378          282 :     ev_type = DatumGetChar(dat);
    5379              : 
    5380          282 :     fno = SPI_fnumber(rulettc, "ev_class");
    5381          282 :     dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
    5382              :     Assert(!isnull);
    5383          282 :     ev_class = DatumGetObjectId(dat);
    5384              : 
    5385          282 :     fno = SPI_fnumber(rulettc, "is_instead");
    5386          282 :     dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
    5387              :     Assert(!isnull);
    5388          282 :     is_instead = DatumGetBool(dat);
    5389              : 
    5390          282 :     fno = SPI_fnumber(rulettc, "ev_qual");
    5391          282 :     ev_qual = SPI_getvalue(ruletup, rulettc, fno);
    5392              :     Assert(ev_qual != NULL);
    5393              : 
    5394          282 :     fno = SPI_fnumber(rulettc, "ev_action");
    5395          282 :     ev_action = SPI_getvalue(ruletup, rulettc, fno);
    5396              :     Assert(ev_action != NULL);
    5397          282 :     actions = (List *) stringToNode(ev_action);
    5398          282 :     if (actions == NIL)
    5399            0 :         elog(ERROR, "invalid empty ev_action list");
    5400              : 
    5401          282 :     ev_relation = table_open(ev_class, AccessShareLock);
    5402              : 
    5403              :     /*
    5404              :      * Build the rules definition text
    5405              :      */
    5406          282 :     appendStringInfo(buf, "CREATE RULE %s AS",
    5407              :                      quote_identifier(rulename));
    5408              : 
    5409          282 :     if (prettyFlags & PRETTYFLAG_INDENT)
    5410          282 :         appendStringInfoString(buf, "\n    ON ");
    5411              :     else
    5412            0 :         appendStringInfoString(buf, " ON ");
    5413              : 
    5414              :     /* The event the rule is fired for */
    5415          282 :     switch (ev_type)
    5416              :     {
    5417            3 :         case '1':
    5418            3 :             appendStringInfoString(buf, "SELECT");
    5419            3 :             viewResultDesc = RelationGetDescr(ev_relation);
    5420            3 :             break;
    5421              : 
    5422           77 :         case '2':
    5423           77 :             appendStringInfoString(buf, "UPDATE");
    5424           77 :             break;
    5425              : 
    5426          150 :         case '3':
    5427          150 :             appendStringInfoString(buf, "INSERT");
    5428          150 :             break;
    5429              : 
    5430           52 :         case '4':
    5431           52 :             appendStringInfoString(buf, "DELETE");
    5432           52 :             break;
    5433              : 
    5434            0 :         default:
    5435            0 :             ereport(ERROR,
    5436              :                     (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
    5437              :                      errmsg("rule \"%s\" has unsupported event type %d",
    5438              :                             rulename, ev_type)));
    5439              :             break;
    5440              :     }
    5441              : 
    5442              :     /* The relation the rule is fired on */
    5443          282 :     appendStringInfo(buf, " TO %s",
    5444          282 :                      (prettyFlags & PRETTYFLAG_SCHEMA) ?
    5445           57 :                      generate_relation_name(ev_class, NIL) :
    5446          225 :                      generate_qualified_relation_name(ev_class));
    5447              : 
    5448              :     /* If the rule has an event qualification, add it */
    5449          282 :     if (strcmp(ev_qual, "<>") != 0)
    5450              :     {
    5451              :         Node       *qual;
    5452              :         Query      *query;
    5453              :         deparse_context context;
    5454              :         deparse_namespace dpns;
    5455              : 
    5456           61 :         if (prettyFlags & PRETTYFLAG_INDENT)
    5457           61 :             appendStringInfoString(buf, "\n  ");
    5458           61 :         appendStringInfoString(buf, " WHERE ");
    5459              : 
    5460           61 :         qual = stringToNode(ev_qual);
    5461              : 
    5462              :         /*
    5463              :          * We need to make a context for recognizing any Vars in the qual
    5464              :          * (which can only be references to OLD and NEW).  Use the rtable of
    5465              :          * the first query in the action list for this purpose.
    5466              :          */
    5467           61 :         query = (Query *) linitial(actions);
    5468              : 
    5469              :         /*
    5470              :          * If the action is INSERT...SELECT, OLD/NEW have been pushed down
    5471              :          * into the SELECT, and that's what we need to look at. (Ugly kluge
    5472              :          * ... try to fix this when we redesign querytrees.)
    5473              :          */
    5474           61 :         query = getInsertSelectQuery(query, NULL);
    5475              : 
    5476              :         /* Must acquire locks right away; see notes in get_query_def() */
    5477           61 :         AcquireRewriteLocks(query, false, false);
    5478              : 
    5479           61 :         context.buf = buf;
    5480           61 :         context.namespaces = list_make1(&dpns);
    5481           61 :         context.resultDesc = NULL;
    5482           61 :         context.targetList = NIL;
    5483           61 :         context.windowClause = NIL;
    5484           61 :         context.varprefix = (list_length(query->rtable) != 1);
    5485           61 :         context.prettyFlags = prettyFlags;
    5486           61 :         context.wrapColumn = WRAP_COLUMN_DEFAULT;
    5487           61 :         context.indentLevel = PRETTYINDENT_STD;
    5488           61 :         context.colNamesVisible = true;
    5489           61 :         context.inGroupBy = false;
    5490           61 :         context.varInOrderBy = false;
    5491           61 :         context.appendparents = NULL;
    5492              : 
    5493           61 :         set_deparse_for_query(&dpns, query, NIL);
    5494              : 
    5495           61 :         get_rule_expr(qual, &context, false);
    5496              :     }
    5497              : 
    5498          282 :     appendStringInfoString(buf, " DO ");
    5499              : 
    5500              :     /* The INSTEAD keyword (if so) */
    5501          282 :     if (is_instead)
    5502          168 :         appendStringInfoString(buf, "INSTEAD ");
    5503              : 
    5504              :     /* Finally the rules actions */
    5505          282 :     if (list_length(actions) > 1)
    5506              :     {
    5507              :         ListCell   *action;
    5508              :         Query      *query;
    5509              : 
    5510           10 :         appendStringInfoChar(buf, '(');
    5511           30 :         foreach(action, actions)
    5512              :         {
    5513           20 :             query = (Query *) lfirst(action);
    5514           20 :             get_query_def(query, buf, NIL, viewResultDesc, true,
    5515              :                           prettyFlags, WRAP_COLUMN_DEFAULT, 0);
    5516           20 :             if (prettyFlags)
    5517           20 :                 appendStringInfoString(buf, ";\n");
    5518              :             else
    5519            0 :                 appendStringInfoString(buf, "; ");
    5520              :         }
    5521           10 :         appendStringInfoString(buf, ");");
    5522              :     }
    5523              :     else
    5524              :     {
    5525              :         Query      *query;
    5526              : 
    5527          272 :         query = (Query *) linitial(actions);
    5528          272 :         get_query_def(query, buf, NIL, viewResultDesc, true,
    5529              :                       prettyFlags, WRAP_COLUMN_DEFAULT, 0);
    5530          272 :         appendStringInfoChar(buf, ';');
    5531              :     }
    5532              : 
    5533          282 :     table_close(ev_relation, AccessShareLock);
    5534          282 : }
    5535              : 
    5536              : 
    5537              : /* ----------
    5538              :  * make_viewdef         - reconstruct the SELECT part of a
    5539              :  *                view rewrite rule
    5540              :  * ----------
    5541              :  */
    5542              : static void
    5543         1771 : make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
    5544              :              int prettyFlags, int wrapColumn)
    5545              : {
    5546              :     Query      *query;
    5547              :     char        ev_type;
    5548              :     Oid         ev_class;
    5549              :     bool        is_instead;
    5550              :     char       *ev_qual;
    5551              :     char       *ev_action;
    5552              :     List       *actions;
    5553              :     Relation    ev_relation;
    5554              :     int         fno;
    5555              :     Datum       dat;
    5556              :     bool        isnull;
    5557              : 
    5558              :     /*
    5559              :      * Get the attribute values from the rules tuple
    5560              :      */
    5561         1771 :     fno = SPI_fnumber(rulettc, "ev_type");
    5562         1771 :     dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
    5563              :     Assert(!isnull);
    5564         1771 :     ev_type = DatumGetChar(dat);
    5565              : 
    5566         1771 :     fno = SPI_fnumber(rulettc, "ev_class");
    5567         1771 :     dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
    5568              :     Assert(!isnull);
    5569         1771 :     ev_class = DatumGetObjectId(dat);
    5570              : 
    5571         1771 :     fno = SPI_fnumber(rulettc, "is_instead");
    5572         1771 :     dat = SPI_getbinval(ruletup, rulettc, fno, &isnull);
    5573              :     Assert(!isnull);
    5574         1771 :     is_instead = DatumGetBool(dat);
    5575              : 
    5576         1771 :     fno = SPI_fnumber(rulettc, "ev_qual");
    5577         1771 :     ev_qual = SPI_getvalue(ruletup, rulettc, fno);
    5578              :     Assert(ev_qual != NULL);
    5579              : 
    5580         1771 :     fno = SPI_fnumber(rulettc, "ev_action");
    5581         1771 :     ev_action = SPI_getvalue(ruletup, rulettc, fno);
    5582              :     Assert(ev_action != NULL);
    5583         1771 :     actions = (List *) stringToNode(ev_action);
    5584              : 
    5585         1771 :     if (list_length(actions) != 1)
    5586              :     {
    5587              :         /* keep output buffer empty and leave */
    5588            0 :         return;
    5589              :     }
    5590              : 
    5591         1771 :     query = (Query *) linitial(actions);
    5592              : 
    5593         1771 :     if (ev_type != '1' || !is_instead ||
    5594         1771 :         strcmp(ev_qual, "<>") != 0 || query->commandType != CMD_SELECT)
    5595              :     {
    5596              :         /* keep output buffer empty and leave */
    5597            0 :         return;
    5598              :     }
    5599              : 
    5600         1771 :     ev_relation = table_open(ev_class, AccessShareLock);
    5601              : 
    5602         1771 :     get_query_def(query, buf, NIL, RelationGetDescr(ev_relation), true,
    5603              :                   prettyFlags, wrapColumn, 0);
    5604         1771 :     appendStringInfoChar(buf, ';');
    5605              : 
    5606         1771 :     table_close(ev_relation, AccessShareLock);
    5607              : }
    5608              : 
    5609              : 
    5610              : /* ----------
    5611              :  * get_query_def            - Parse back one query parsetree
    5612              :  *
    5613              :  * query: parsetree to be displayed
    5614              :  * buf: output text is appended to buf
    5615              :  * parentnamespace: list (initially empty) of outer-level deparse_namespace's
    5616              :  * resultDesc: if not NULL, the output tuple descriptor for the view
    5617              :  *      represented by a SELECT query.  We use the column names from it
    5618              :  *      to label SELECT output columns, in preference to names in the query
    5619              :  * colNamesVisible: true if the surrounding context cares about the output
    5620              :  *      column names at all (as, for example, an EXISTS() context does not);
    5621              :  *      when false, we can suppress dummy column labels such as "?column?"
    5622              :  * prettyFlags: bitmask of PRETTYFLAG_XXX options
    5623              :  * wrapColumn: maximum line length, or -1 to disable wrapping
    5624              :  * startIndent: initial indentation amount
    5625              :  * ----------
    5626              :  */
    5627              : static void
    5628         2867 : get_query_def(Query *query, StringInfo buf, List *parentnamespace,
    5629              :               TupleDesc resultDesc, bool colNamesVisible,
    5630              :               int prettyFlags, int wrapColumn, int startIndent)
    5631              : {
    5632              :     deparse_context context;
    5633              :     deparse_namespace dpns;
    5634              :     int         rtable_size;
    5635              : 
    5636              :     /* Guard against excessively long or deeply-nested queries */
    5637         2867 :     CHECK_FOR_INTERRUPTS();
    5638         2867 :     check_stack_depth();
    5639              : 
    5640         5734 :     rtable_size = query->hasGroupRTE ?
    5641         2867 :         list_length(query->rtable) - 1 :
    5642         2754 :         list_length(query->rtable);
    5643              : 
    5644              :     /*
    5645              :      * Replace any Vars in the query's targetlist and havingQual that
    5646              :      * reference GROUP outputs with the underlying grouping expressions.
    5647              :      *
    5648              :      * We can safely pass NULL for the root here.  Preserving varnullingrels
    5649              :      * makes no difference to the deparsed source text.
    5650              :      */
    5651         2867 :     if (query->hasGroupRTE)
    5652              :     {
    5653          113 :         query->targetList = (List *)
    5654          113 :             flatten_group_exprs(NULL, query, (Node *) query->targetList);
    5655          113 :         query->havingQual =
    5656          113 :             flatten_group_exprs(NULL, query, query->havingQual);
    5657              :     }
    5658              : 
    5659              :     /*
    5660              :      * Before we begin to examine the query, acquire locks on referenced
    5661              :      * relations, and fix up deleted columns in JOIN RTEs.  This ensures
    5662              :      * consistent results.  Note we assume it's OK to scribble on the passed
    5663              :      * querytree!
    5664              :      *
    5665              :      * We are only deparsing the query (we are not about to execute it), so we
    5666              :      * only need AccessShareLock on the relations it mentions.
    5667              :      */
    5668         2867 :     AcquireRewriteLocks(query, false, false);
    5669              : 
    5670         2867 :     context.buf = buf;
    5671         2867 :     context.namespaces = lcons(&dpns, list_copy(parentnamespace));
    5672         2867 :     context.resultDesc = NULL;
    5673         2867 :     context.targetList = NIL;
    5674         2867 :     context.windowClause = NIL;
    5675         2867 :     context.varprefix = (parentnamespace != NIL ||
    5676         2867 :                          rtable_size != 1);
    5677         2867 :     context.prettyFlags = prettyFlags;
    5678         2867 :     context.wrapColumn = wrapColumn;
    5679         2867 :     context.indentLevel = startIndent;
    5680         2867 :     context.colNamesVisible = colNamesVisible;
    5681         2867 :     context.inGroupBy = false;
    5682         2867 :     context.varInOrderBy = false;
    5683         2867 :     context.appendparents = NULL;
    5684              : 
    5685         2867 :     set_deparse_for_query(&dpns, query, parentnamespace);
    5686              : 
    5687         2867 :     switch (query->commandType)
    5688              :     {
    5689         2544 :         case CMD_SELECT:
    5690              :             /* We set context.resultDesc only if it's a SELECT */
    5691         2544 :             context.resultDesc = resultDesc;
    5692         2544 :             get_select_query_def(query, &context);
    5693         2544 :             break;
    5694              : 
    5695           77 :         case CMD_UPDATE:
    5696           77 :             get_update_query_def(query, &context);
    5697           77 :             break;
    5698              : 
    5699          173 :         case CMD_INSERT:
    5700          173 :             get_insert_query_def(query, &context);
    5701          173 :             break;
    5702              : 
    5703           38 :         case CMD_DELETE:
    5704           38 :             get_delete_query_def(query, &context);
    5705           38 :             break;
    5706              : 
    5707            6 :         case CMD_MERGE:
    5708            6 :             get_merge_query_def(query, &context);
    5709            6 :             break;
    5710              : 
    5711           21 :         case CMD_NOTHING:
    5712           21 :             appendStringInfoString(buf, "NOTHING");
    5713           21 :             break;
    5714              : 
    5715            8 :         case CMD_UTILITY:
    5716            8 :             get_utility_query_def(query, &context);
    5717            8 :             break;
    5718              : 
    5719            0 :         default:
    5720            0 :             elog(ERROR, "unrecognized query command type: %d",
    5721              :                  query->commandType);
    5722              :             break;
    5723              :     }
    5724         2867 : }
    5725              : 
    5726              : /* ----------
    5727              :  * get_values_def           - Parse back a VALUES list
    5728              :  * ----------
    5729              :  */
    5730              : static void
    5731          136 : get_values_def(List *values_lists, deparse_context *context)
    5732              : {
    5733          136 :     StringInfo  buf = context->buf;
    5734          136 :     bool        first_list = true;
    5735              :     ListCell   *vtl;
    5736              : 
    5737          136 :     appendStringInfoString(buf, "VALUES ");
    5738              : 
    5739          389 :     foreach(vtl, values_lists)
    5740              :     {
    5741          253 :         List       *sublist = (List *) lfirst(vtl);
    5742          253 :         bool        first_col = true;
    5743              :         ListCell   *lc;
    5744              : 
    5745          253 :         if (first_list)
    5746          136 :             first_list = false;
    5747              :         else
    5748          117 :             appendStringInfoString(buf, ", ");
    5749              : 
    5750          253 :         appendStringInfoChar(buf, '(');
    5751          979 :         foreach(lc, sublist)
    5752              :         {
    5753          726 :             Node       *col = (Node *) lfirst(lc);
    5754              : 
    5755          726 :             if (first_col)
    5756          253 :                 first_col = false;
    5757              :             else
    5758          473 :                 appendStringInfoChar(buf, ',');
    5759              : 
    5760              :             /*
    5761              :              * Print the value.  Whole-row Vars need special treatment.
    5762              :              */
    5763          726 :             get_rule_expr_toplevel(col, context, false);
    5764              :         }
    5765          253 :         appendStringInfoChar(buf, ')');
    5766              :     }
    5767          136 : }
    5768              : 
    5769              : /* ----------
    5770              :  * get_with_clause          - Parse back a WITH clause
    5771              :  * ----------
    5772              :  */
    5773              : static void
    5774         2838 : get_with_clause(Query *query, deparse_context *context)
    5775              : {
    5776         2838 :     StringInfo  buf = context->buf;
    5777              :     const char *sep;
    5778              :     ListCell   *l;
    5779              : 
    5780         2838 :     if (query->cteList == NIL)
    5781         2790 :         return;
    5782              : 
    5783           48 :     if (PRETTY_INDENT(context))
    5784              :     {
    5785           48 :         context->indentLevel += PRETTYINDENT_STD;
    5786           48 :         appendStringInfoChar(buf, ' ');
    5787              :     }
    5788              : 
    5789           48 :     if (query->hasRecursive)
    5790           28 :         sep = "WITH RECURSIVE ";
    5791              :     else
    5792           20 :         sep = "WITH ";
    5793          121 :     foreach(l, query->cteList)
    5794              :     {
    5795           73 :         CommonTableExpr *cte = (CommonTableExpr *) lfirst(l);
    5796              : 
    5797           73 :         appendStringInfoString(buf, sep);
    5798           73 :         appendStringInfoString(buf, quote_identifier(cte->ctename));
    5799           73 :         if (cte->aliascolnames)
    5800              :         {
    5801           28 :             bool        first = true;
    5802              :             ListCell   *col;
    5803              : 
    5804           28 :             appendStringInfoChar(buf, '(');
    5805           74 :             foreach(col, cte->aliascolnames)
    5806              :             {
    5807           46 :                 if (first)
    5808           28 :                     first = false;
    5809              :                 else
    5810           18 :                     appendStringInfoString(buf, ", ");
    5811           46 :                 appendStringInfoString(buf,
    5812           46 :                                        quote_identifier(strVal(lfirst(col))));
    5813              :             }
    5814           28 :             appendStringInfoChar(buf, ')');
    5815              :         }
    5816           73 :         appendStringInfoString(buf, " AS ");
    5817           73 :         switch (cte->ctematerialized)
    5818              :         {
    5819           64 :             case CTEMaterializeDefault:
    5820           64 :                 break;
    5821            9 :             case CTEMaterializeAlways:
    5822            9 :                 appendStringInfoString(buf, "MATERIALIZED ");
    5823            9 :                 break;
    5824            0 :             case CTEMaterializeNever:
    5825            0 :                 appendStringInfoString(buf, "NOT MATERIALIZED ");
    5826            0 :                 break;
    5827              :         }
    5828           73 :         appendStringInfoChar(buf, '(');
    5829           73 :         if (PRETTY_INDENT(context))
    5830           73 :             appendContextKeyword(context, "", 0, 0, 0);
    5831           73 :         get_query_def((Query *) cte->ctequery, buf, context->namespaces, NULL,
    5832              :                       true,
    5833              :                       context->prettyFlags, context->wrapColumn,
    5834              :                       context->indentLevel);
    5835           73 :         if (PRETTY_INDENT(context))
    5836           73 :             appendContextKeyword(context, "", 0, 0, 0);
    5837           73 :         appendStringInfoChar(buf, ')');
    5838              : 
    5839           73 :         if (cte->search_clause)
    5840              :         {
    5841            3 :             bool        first = true;
    5842              :             ListCell   *lc;
    5843              : 
    5844            3 :             appendStringInfo(buf, " SEARCH %s FIRST BY ",
    5845            3 :                              cte->search_clause->search_breadth_first ? "BREADTH" : "DEPTH");
    5846              : 
    5847            9 :             foreach(lc, cte->search_clause->search_col_list)
    5848              :             {
    5849            6 :                 if (first)
    5850            3 :                     first = false;
    5851              :                 else
    5852            3 :                     appendStringInfoString(buf, ", ");
    5853            6 :                 appendStringInfoString(buf,
    5854            6 :                                        quote_identifier(strVal(lfirst(lc))));
    5855              :             }
    5856              : 
    5857            3 :             appendStringInfo(buf, " SET %s", quote_identifier(cte->search_clause->search_seq_column));
    5858              :         }
    5859              : 
    5860           73 :         if (cte->cycle_clause)
    5861              :         {
    5862            6 :             bool        first = true;
    5863              :             ListCell   *lc;
    5864              : 
    5865            6 :             appendStringInfoString(buf, " CYCLE ");
    5866              : 
    5867           18 :             foreach(lc, cte->cycle_clause->cycle_col_list)
    5868              :             {
    5869           12 :                 if (first)
    5870            6 :                     first = false;
    5871              :                 else
    5872            6 :                     appendStringInfoString(buf, ", ");
    5873           12 :                 appendStringInfoString(buf,
    5874           12 :                                        quote_identifier(strVal(lfirst(lc))));
    5875              :             }
    5876              : 
    5877            6 :             appendStringInfo(buf, " SET %s", quote_identifier(cte->cycle_clause->cycle_mark_column));
    5878              : 
    5879              :             {
    5880            6 :                 Const      *cmv = castNode(Const, cte->cycle_clause->cycle_mark_value);
    5881            6 :                 Const      *cmd = castNode(Const, cte->cycle_clause->cycle_mark_default);
    5882              : 
    5883            9 :                 if (!(cmv->consttype == BOOLOID && !cmv->constisnull && DatumGetBool(cmv->constvalue) == true &&
    5884            3 :                       cmd->consttype == BOOLOID && !cmd->constisnull && DatumGetBool(cmd->constvalue) == false))
    5885              :                 {
    5886            3 :                     appendStringInfoString(buf, " TO ");
    5887            3 :                     get_rule_expr(cte->cycle_clause->cycle_mark_value, context, false);
    5888            3 :                     appendStringInfoString(buf, " DEFAULT ");
    5889            3 :                     get_rule_expr(cte->cycle_clause->cycle_mark_default, context, false);
    5890              :                 }
    5891              :             }
    5892              : 
    5893            6 :             appendStringInfo(buf, " USING %s", quote_identifier(cte->cycle_clause->cycle_path_column));
    5894              :         }
    5895              : 
    5896           73 :         sep = ", ";
    5897              :     }
    5898              : 
    5899           48 :     if (PRETTY_INDENT(context))
    5900              :     {
    5901           48 :         context->indentLevel -= PRETTYINDENT_STD;
    5902           48 :         appendContextKeyword(context, "", 0, 0, 0);
    5903              :     }
    5904              :     else
    5905            0 :         appendStringInfoChar(buf, ' ');
    5906              : }
    5907              : 
    5908              : /* ----------
    5909              :  * get_select_query_def         - Parse back a SELECT parsetree
    5910              :  * ----------
    5911              :  */
    5912              : static void
    5913         2544 : get_select_query_def(Query *query, deparse_context *context)
    5914              : {
    5915         2544 :     StringInfo  buf = context->buf;
    5916              :     bool        force_colno;
    5917              :     ListCell   *l;
    5918              : 
    5919              :     /* Insert the WITH clause if given */
    5920         2544 :     get_with_clause(query, context);
    5921              : 
    5922              :     /* Subroutines may need to consult the SELECT targetlist and windowClause */
    5923         2544 :     context->targetList = query->targetList;
    5924         2544 :     context->windowClause = query->windowClause;
    5925              : 
    5926              :     /*
    5927              :      * If the Query node has a setOperations tree, then it's the top level of
    5928              :      * a UNION/INTERSECT/EXCEPT query; only the WITH, ORDER BY and LIMIT
    5929              :      * fields are interesting in the top query itself.
    5930              :      */
    5931         2544 :     if (query->setOperations)
    5932              :     {
    5933           82 :         get_setop_query(query->setOperations, query, context);
    5934              :         /* ORDER BY clauses must be simple in this case */
    5935           82 :         force_colno = true;
    5936              :     }
    5937              :     else
    5938              :     {
    5939         2462 :         get_basic_select_query(query, context);
    5940         2462 :         force_colno = false;
    5941              :     }
    5942              : 
    5943              :     /* Add the ORDER BY clause if given */
    5944         2544 :     if (query->sortClause != NIL)
    5945              :     {
    5946           90 :         appendContextKeyword(context, " ORDER BY ",
    5947              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    5948           90 :         get_rule_orderby(query->sortClause, query->targetList,
    5949              :                          force_colno, context);
    5950              :     }
    5951              : 
    5952              :     /*
    5953              :      * Add the LIMIT/OFFSET clauses if given. If non-default options, use the
    5954              :      * standard spelling of LIMIT.
    5955              :      */
    5956         2544 :     if (query->limitOffset != NULL)
    5957              :     {
    5958           16 :         appendContextKeyword(context, " OFFSET ",
    5959              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
    5960           16 :         get_rule_expr(query->limitOffset, context, false);
    5961              :     }
    5962         2544 :     if (query->limitCount != NULL)
    5963              :     {
    5964           43 :         if (query->limitOption == LIMIT_OPTION_WITH_TIES)
    5965              :         {
    5966              :             /*
    5967              :              * The limitCount arg is a c_expr, so it needs parens. Simple
    5968              :              * literals and function expressions would not need parens, but
    5969              :              * unfortunately it's hard to tell if the expression will be
    5970              :              * printed as a simple literal like 123 or as a typecast
    5971              :              * expression, like '-123'::int4. The grammar accepts the former
    5972              :              * without quoting, but not the latter.
    5973              :              */
    5974           24 :             appendContextKeyword(context, " FETCH FIRST ",
    5975              :                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
    5976           24 :             appendStringInfoChar(buf, '(');
    5977           24 :             get_rule_expr(query->limitCount, context, false);
    5978           24 :             appendStringInfoChar(buf, ')');
    5979           24 :             appendStringInfoString(buf, " ROWS WITH TIES");
    5980              :         }
    5981              :         else
    5982              :         {
    5983           19 :             appendContextKeyword(context, " LIMIT ",
    5984              :                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
    5985           19 :             if (IsA(query->limitCount, Const) &&
    5986            8 :                 ((Const *) query->limitCount)->constisnull)
    5987            8 :                 appendStringInfoString(buf, "ALL");
    5988              :             else
    5989           11 :                 get_rule_expr(query->limitCount, context, false);
    5990              :         }
    5991              :     }
    5992              : 
    5993              :     /* Add FOR [KEY] UPDATE/SHARE clauses if present */
    5994         2544 :     if (query->hasForUpdate)
    5995              :     {
    5996            6 :         foreach(l, query->rowMarks)
    5997              :         {
    5998            3 :             RowMarkClause *rc = (RowMarkClause *) lfirst(l);
    5999              : 
    6000              :             /* don't print implicit clauses */
    6001            3 :             if (rc->pushedDown)
    6002            0 :                 continue;
    6003              : 
    6004            3 :             appendContextKeyword(context,
    6005            3 :                                  get_lock_clause_strength(rc->strength),
    6006              :                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
    6007              : 
    6008            3 :             appendStringInfo(buf, " OF %s",
    6009            3 :                              quote_identifier(get_rtable_name(rc->rti,
    6010              :                                                               context)));
    6011            3 :             if (rc->waitPolicy == LockWaitError)
    6012            0 :                 appendStringInfoString(buf, " NOWAIT");
    6013            3 :             else if (rc->waitPolicy == LockWaitSkip)
    6014            0 :                 appendStringInfoString(buf, " SKIP LOCKED");
    6015              :         }
    6016              :     }
    6017         2544 : }
    6018              : 
    6019              : static char *
    6020            6 : get_lock_clause_strength(LockClauseStrength strength)
    6021              : {
    6022            6 :     switch (strength)
    6023              :     {
    6024            0 :         case LCS_NONE:
    6025              :             /* we intentionally throw an error for LCS_NONE */
    6026            0 :             elog(ERROR, "unrecognized LockClauseStrength %d",
    6027              :                  (int) strength);
    6028              :             break;
    6029            0 :         case LCS_FORKEYSHARE:
    6030            0 :             return " FOR KEY SHARE";
    6031            0 :         case LCS_FORSHARE:
    6032            0 :             return " FOR SHARE";
    6033            0 :         case LCS_FORNOKEYUPDATE:
    6034            0 :             return " FOR NO KEY UPDATE";
    6035            6 :         case LCS_FORUPDATE:
    6036            6 :             return " FOR UPDATE";
    6037              :     }
    6038            0 :     return NULL;                /* keep compiler quiet */
    6039              : }
    6040              : 
    6041              : /*
    6042              :  * Detect whether query looks like SELECT ... FROM VALUES(),
    6043              :  * with no need to rename the output columns of the VALUES RTE.
    6044              :  * If so, return the VALUES RTE.  Otherwise return NULL.
    6045              :  */
    6046              : static RangeTblEntry *
    6047         2462 : get_simple_values_rte(Query *query, TupleDesc resultDesc)
    6048              : {
    6049         2462 :     RangeTblEntry *result = NULL;
    6050              :     ListCell   *lc;
    6051              : 
    6052              :     /*
    6053              :      * We want to detect a match even if the Query also contains OLD or NEW
    6054              :      * rule RTEs.  So the idea is to scan the rtable and see if there is only
    6055              :      * one inFromCl RTE that is a VALUES RTE.
    6056              :      */
    6057         2648 :     foreach(lc, query->rtable)
    6058              :     {
    6059         2241 :         RangeTblEntry *rte = (RangeTblEntry *) lfirst(lc);
    6060              : 
    6061         2241 :         if (rte->rtekind == RTE_VALUES && rte->inFromCl)
    6062              :         {
    6063          114 :             if (result)
    6064         2055 :                 return NULL;    /* multiple VALUES (probably not possible) */
    6065          114 :             result = rte;
    6066              :         }
    6067         2127 :         else if (rte->rtekind == RTE_RELATION && !rte->inFromCl)
    6068           72 :             continue;           /* ignore rule entries */
    6069              :         else
    6070         2055 :             return NULL;        /* something else -> not simple VALUES */
    6071              :     }
    6072              : 
    6073              :     /*
    6074              :      * We don't need to check the targetlist in any great detail, because
    6075              :      * parser/analyze.c will never generate a "bare" VALUES RTE --- they only
    6076              :      * appear inside auto-generated sub-queries with very restricted
    6077              :      * structure.  However, DefineView might have modified the tlist by
    6078              :      * injecting new column aliases, or we might have some other column
    6079              :      * aliases forced by a resultDesc.  We can only simplify if the RTE's
    6080              :      * column names match the names that get_target_list() would select.
    6081              :      */
    6082          407 :     if (result)
    6083              :     {
    6084              :         ListCell   *lcn;
    6085              :         int         colno;
    6086              : 
    6087          114 :         if (list_length(query->targetList) != list_length(result->eref->colnames))
    6088            0 :             return NULL;        /* this probably cannot happen */
    6089          114 :         colno = 0;
    6090          421 :         forboth(lc, query->targetList, lcn, result->eref->colnames)
    6091              :         {
    6092          313 :             TargetEntry *tle = (TargetEntry *) lfirst(lc);
    6093          313 :             char       *cname = strVal(lfirst(lcn));
    6094              :             char       *colname;
    6095              : 
    6096          313 :             if (tle->resjunk)
    6097            6 :                 return NULL;    /* this probably cannot happen */
    6098              : 
    6099              :             /* compute name that get_target_list would use for column */
    6100          313 :             colno++;
    6101          313 :             if (resultDesc && colno <= resultDesc->natts)
    6102           15 :                 colname = NameStr(TupleDescAttr(resultDesc, colno - 1)->attname);
    6103              :             else
    6104          298 :                 colname = tle->resname;
    6105              : 
    6106              :             /* does it match the VALUES RTE? */
    6107          313 :             if (colname == NULL || strcmp(colname, cname) != 0)
    6108            6 :                 return NULL;    /* column name has been changed */
    6109              :         }
    6110              :     }
    6111              : 
    6112          401 :     return result;
    6113              : }
    6114              : 
    6115              : static void
    6116         2462 : get_basic_select_query(Query *query, deparse_context *context)
    6117              : {
    6118         2462 :     StringInfo  buf = context->buf;
    6119              :     RangeTblEntry *values_rte;
    6120              :     char       *sep;
    6121              :     ListCell   *l;
    6122              : 
    6123         2462 :     if (PRETTY_INDENT(context))
    6124              :     {
    6125         2439 :         context->indentLevel += PRETTYINDENT_STD;
    6126         2439 :         appendStringInfoChar(buf, ' ');
    6127              :     }
    6128              : 
    6129              :     /*
    6130              :      * If the query looks like SELECT * FROM (VALUES ...), then print just the
    6131              :      * VALUES part.  This reverses what transformValuesClause() did at parse
    6132              :      * time.
    6133              :      */
    6134         2462 :     values_rte = get_simple_values_rte(query, context->resultDesc);
    6135         2462 :     if (values_rte)
    6136              :     {
    6137          108 :         get_values_def(values_rte->values_lists, context);
    6138          108 :         return;
    6139              :     }
    6140              : 
    6141              :     /*
    6142              :      * Build up the query string - first we say SELECT
    6143              :      */
    6144         2354 :     if (query->isReturn)
    6145           26 :         appendStringInfoString(buf, "RETURN");
    6146              :     else
    6147         2328 :         appendStringInfoString(buf, "SELECT");
    6148              : 
    6149              :     /* Add the DISTINCT clause if given */
    6150         2354 :     if (query->distinctClause != NIL)
    6151              :     {
    6152            0 :         if (query->hasDistinctOn)
    6153              :         {
    6154            0 :             appendStringInfoString(buf, " DISTINCT ON (");
    6155            0 :             sep = "";
    6156            0 :             foreach(l, query->distinctClause)
    6157              :             {
    6158            0 :                 SortGroupClause *srt = (SortGroupClause *) lfirst(l);
    6159              : 
    6160            0 :                 appendStringInfoString(buf, sep);
    6161            0 :                 get_rule_sortgroupclause(srt->tleSortGroupRef, query->targetList,
    6162              :                                          false, context);
    6163            0 :                 sep = ", ";
    6164              :             }
    6165            0 :             appendStringInfoChar(buf, ')');
    6166              :         }
    6167              :         else
    6168            0 :             appendStringInfoString(buf, " DISTINCT");
    6169              :     }
    6170              : 
    6171              :     /* Then we tell what to select (the targetlist) */
    6172         2354 :     get_target_list(query->targetList, context);
    6173              : 
    6174              :     /* Add the FROM clause if needed */
    6175         2354 :     get_from_clause(query, " FROM ", context);
    6176              : 
    6177              :     /* Add the WHERE clause if given */
    6178         2354 :     if (query->jointree->quals != NULL)
    6179              :     {
    6180          747 :         appendContextKeyword(context, " WHERE ",
    6181              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    6182          747 :         get_rule_expr(query->jointree->quals, context, false);
    6183              :     }
    6184              : 
    6185              :     /* Add the GROUP BY clause if given */
    6186         2354 :     if (query->groupClause != NULL || query->groupingSets != NULL)
    6187              :     {
    6188              :         bool        save_ingroupby;
    6189              : 
    6190          113 :         appendContextKeyword(context, " GROUP BY ",
    6191              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    6192          113 :         if (query->groupDistinct)
    6193            0 :             appendStringInfoString(buf, "DISTINCT ");
    6194              : 
    6195          113 :         save_ingroupby = context->inGroupBy;
    6196          113 :         context->inGroupBy = true;
    6197              : 
    6198          113 :         if (query->groupByAll)
    6199            3 :             appendStringInfoString(buf, "ALL");
    6200          110 :         else if (query->groupingSets == NIL)
    6201              :         {
    6202          107 :             sep = "";
    6203          243 :             foreach(l, query->groupClause)
    6204              :             {
    6205          136 :                 SortGroupClause *grp = (SortGroupClause *) lfirst(l);
    6206              : 
    6207          136 :                 appendStringInfoString(buf, sep);
    6208          136 :                 get_rule_sortgroupclause(grp->tleSortGroupRef, query->targetList,
    6209              :                                          false, context);
    6210          136 :                 sep = ", ";
    6211              :             }
    6212              :         }
    6213              :         else
    6214              :         {
    6215            3 :             sep = "";
    6216            6 :             foreach(l, query->groupingSets)
    6217              :             {
    6218            3 :                 GroupingSet *grp = lfirst(l);
    6219              : 
    6220            3 :                 appendStringInfoString(buf, sep);
    6221            3 :                 get_rule_groupingset(grp, query->targetList, true, context);
    6222            3 :                 sep = ", ";
    6223              :             }
    6224              :         }
    6225              : 
    6226          113 :         context->inGroupBy = save_ingroupby;
    6227              :     }
    6228              : 
    6229              :     /* Add the HAVING clause if given */
    6230         2354 :     if (query->havingQual != NULL)
    6231              :     {
    6232            5 :         appendContextKeyword(context, " HAVING ",
    6233              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 0);
    6234            5 :         get_rule_expr(query->havingQual, context, false);
    6235              :     }
    6236              : 
    6237              :     /* Add the WINDOW clause if needed */
    6238         2354 :     if (query->windowClause != NIL)
    6239           24 :         get_rule_windowclause(query, context);
    6240              : }
    6241              : 
    6242              : /* ----------
    6243              :  * get_target_list          - Parse back a SELECT target list
    6244              :  *
    6245              :  * This is also used for RETURNING lists in INSERT/UPDATE/DELETE/MERGE.
    6246              :  * ----------
    6247              :  */
    6248              : static void
    6249         2430 : get_target_list(List *targetList, deparse_context *context)
    6250              : {
    6251         2430 :     StringInfo  buf = context->buf;
    6252              :     StringInfoData targetbuf;
    6253         2430 :     bool        last_was_multiline = false;
    6254              :     char       *sep;
    6255              :     int         colno;
    6256              :     ListCell   *l;
    6257              : 
    6258              :     /* we use targetbuf to hold each TLE's text temporarily */
    6259         2430 :     initStringInfo(&targetbuf);
    6260              : 
    6261         2430 :     sep = " ";
    6262         2430 :     colno = 0;
    6263        12810 :     foreach(l, targetList)
    6264              :     {
    6265        10380 :         TargetEntry *tle = (TargetEntry *) lfirst(l);
    6266              :         char       *colname;
    6267              :         char       *attname;
    6268              : 
    6269        10380 :         if (tle->resjunk)
    6270           20 :             continue;           /* ignore junk entries */
    6271              : 
    6272        10360 :         appendStringInfoString(buf, sep);
    6273        10360 :         sep = ", ";
    6274        10360 :         colno++;
    6275              : 
    6276              :         /*
    6277              :          * Put the new field text into targetbuf so we can decide after we've
    6278              :          * got it whether or not it needs to go on a new line.
    6279              :          */
    6280        10360 :         resetStringInfo(&targetbuf);
    6281        10360 :         context->buf = &targetbuf;
    6282              : 
    6283              :         /*
    6284              :          * We special-case Var nodes rather than using get_rule_expr. This is
    6285              :          * needed because get_rule_expr will display a whole-row Var as
    6286              :          * "foo.*", which is the preferred notation in most contexts, but at
    6287              :          * the top level of a SELECT list it's not right (the parser will
    6288              :          * expand that notation into multiple columns, yielding behavior
    6289              :          * different from a whole-row Var).  We need to call get_variable
    6290              :          * directly so that we can tell it to do the right thing, and so that
    6291              :          * we can get the attribute name which is the default AS label.
    6292              :          */
    6293        10360 :         if (tle->expr && (IsA(tle->expr, Var)))
    6294              :         {
    6295         7984 :             attname = get_variable((Var *) tle->expr, 0, true, context);
    6296              :         }
    6297              :         else
    6298              :         {
    6299         2376 :             get_rule_expr((Node *) tle->expr, context, true);
    6300              : 
    6301              :             /*
    6302              :              * When colNamesVisible is true, we should always show the
    6303              :              * assigned column name explicitly.  Otherwise, show it only if
    6304              :              * it's not FigureColname's fallback.
    6305              :              */
    6306         2376 :             attname = context->colNamesVisible ? NULL : "?column?";
    6307              :         }
    6308              : 
    6309              :         /*
    6310              :          * Figure out what the result column should be called.  In the context
    6311              :          * of a view, use the view's tuple descriptor (so as to pick up the
    6312              :          * effects of any column RENAME that's been done on the view).
    6313              :          * Otherwise, just use what we can find in the TLE.
    6314              :          */
    6315        10360 :         if (context->resultDesc && colno <= context->resultDesc->natts)
    6316         9448 :             colname = NameStr(TupleDescAttr(context->resultDesc,
    6317              :                                             colno - 1)->attname);
    6318              :         else
    6319          912 :             colname = tle->resname;
    6320              : 
    6321              :         /* Show AS unless the column's name is correct as-is */
    6322        10360 :         if (colname)            /* resname could be NULL */
    6323              :         {
    6324        10334 :             if (attname == NULL || strcmp(attname, colname) != 0)
    6325         3355 :                 appendStringInfo(&targetbuf, " AS %s", quote_identifier(colname));
    6326              :         }
    6327              : 
    6328              :         /* Restore context's output buffer */
    6329        10360 :         context->buf = buf;
    6330              : 
    6331              :         /* Consider line-wrapping if enabled */
    6332        10360 :         if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
    6333              :         {
    6334              :             int         leading_nl_pos;
    6335              : 
    6336              :             /* Does the new field start with a new line? */
    6337        10337 :             if (targetbuf.len > 0 && targetbuf.data[0] == '\n')
    6338          279 :                 leading_nl_pos = 0;
    6339              :             else
    6340        10058 :                 leading_nl_pos = -1;
    6341              : 
    6342              :             /* If so, we shouldn't add anything */
    6343        10337 :             if (leading_nl_pos >= 0)
    6344              :             {
    6345              :                 /* instead, remove any trailing spaces currently in buf */
    6346          279 :                 removeStringInfoSpaces(buf);
    6347              :             }
    6348              :             else
    6349              :             {
    6350              :                 char       *trailing_nl;
    6351              : 
    6352              :                 /* Locate the start of the current line in the output buffer */
    6353        10058 :                 trailing_nl = strrchr(buf->data, '\n');
    6354        10058 :                 if (trailing_nl == NULL)
    6355         2989 :                     trailing_nl = buf->data;
    6356              :                 else
    6357         7069 :                     trailing_nl++;
    6358              : 
    6359              :                 /*
    6360              :                  * Add a newline, plus some indentation, if the new field is
    6361              :                  * not the first and either the new field would cause an
    6362              :                  * overflow or the last field used more than one line.
    6363              :                  */
    6364        10058 :                 if (colno > 1 &&
    6365         7659 :                     ((strlen(trailing_nl) + targetbuf.len > context->wrapColumn) ||
    6366              :                      last_was_multiline))
    6367         7659 :                     appendContextKeyword(context, "", -PRETTYINDENT_STD,
    6368              :                                          PRETTYINDENT_STD, PRETTYINDENT_VAR);
    6369              :             }
    6370              : 
    6371              :             /* Remember this field's multiline status for next iteration */
    6372        10337 :             last_was_multiline =
    6373        10337 :                 (strchr(targetbuf.data + leading_nl_pos + 1, '\n') != NULL);
    6374              :         }
    6375              : 
    6376              :         /* Add the new field */
    6377        10360 :         appendBinaryStringInfo(buf, targetbuf.data, targetbuf.len);
    6378              :     }
    6379              : 
    6380              :     /* clean up */
    6381         2430 :     pfree(targetbuf.data);
    6382         2430 : }
    6383              : 
    6384              : static void
    6385           76 : get_returning_clause(Query *query, deparse_context *context)
    6386              : {
    6387           76 :     StringInfo  buf = context->buf;
    6388              : 
    6389           76 :     if (query->returningList)
    6390              :     {
    6391           76 :         bool        have_with = false;
    6392              : 
    6393           76 :         appendContextKeyword(context, " RETURNING",
    6394              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    6395              : 
    6396              :         /* Add WITH (OLD/NEW) options, if they're not the defaults */
    6397           76 :         if (query->returningOldAlias && strcmp(query->returningOldAlias, "old") != 0)
    6398              :         {
    6399            9 :             appendStringInfo(buf, " WITH (OLD AS %s",
    6400            9 :                              quote_identifier(query->returningOldAlias));
    6401            9 :             have_with = true;
    6402              :         }
    6403           76 :         if (query->returningNewAlias && strcmp(query->returningNewAlias, "new") != 0)
    6404              :         {
    6405            9 :             if (have_with)
    6406            6 :                 appendStringInfo(buf, ", NEW AS %s",
    6407            6 :                                  quote_identifier(query->returningNewAlias));
    6408              :             else
    6409              :             {
    6410            3 :                 appendStringInfo(buf, " WITH (NEW AS %s",
    6411            3 :                                  quote_identifier(query->returningNewAlias));
    6412            3 :                 have_with = true;
    6413              :             }
    6414              :         }
    6415           76 :         if (have_with)
    6416           12 :             appendStringInfoChar(buf, ')');
    6417              : 
    6418              :         /* Add the returning expressions themselves */
    6419           76 :         get_target_list(query->returningList, context);
    6420              :     }
    6421           76 : }
    6422              : 
    6423              : static void
    6424          378 : get_setop_query(Node *setOp, Query *query, deparse_context *context)
    6425              : {
    6426          378 :     StringInfo  buf = context->buf;
    6427              :     bool        need_paren;
    6428              : 
    6429              :     /* Guard against excessively long or deeply-nested queries */
    6430          378 :     CHECK_FOR_INTERRUPTS();
    6431          378 :     check_stack_depth();
    6432              : 
    6433          378 :     if (IsA(setOp, RangeTblRef))
    6434              :     {
    6435          230 :         RangeTblRef *rtr = (RangeTblRef *) setOp;
    6436          230 :         RangeTblEntry *rte = rt_fetch(rtr->rtindex, query->rtable);
    6437          230 :         Query      *subquery = rte->subquery;
    6438              : 
    6439              :         Assert(subquery != NULL);
    6440              : 
    6441              :         /*
    6442              :          * We need parens if WITH, ORDER BY, FOR UPDATE, or LIMIT; see gram.y.
    6443              :          * Also add parens if the leaf query contains its own set operations.
    6444              :          * (That shouldn't happen unless one of the other clauses is also
    6445              :          * present, see transformSetOperationTree; but let's be safe.)
    6446              :          */
    6447          690 :         need_paren = (subquery->cteList ||
    6448          230 :                       subquery->sortClause ||
    6449          230 :                       subquery->rowMarks ||
    6450          230 :                       subquery->limitOffset ||
    6451          690 :                       subquery->limitCount ||
    6452          230 :                       subquery->setOperations);
    6453          230 :         if (need_paren)
    6454            0 :             appendStringInfoChar(buf, '(');
    6455          230 :         get_query_def(subquery, buf, context->namespaces,
    6456          230 :                       context->resultDesc, context->colNamesVisible,
    6457              :                       context->prettyFlags, context->wrapColumn,
    6458              :                       context->indentLevel);
    6459          230 :         if (need_paren)
    6460            0 :             appendStringInfoChar(buf, ')');
    6461              :     }
    6462          148 :     else if (IsA(setOp, SetOperationStmt))
    6463              :     {
    6464          148 :         SetOperationStmt *op = (SetOperationStmt *) setOp;
    6465              :         int         subindent;
    6466              :         bool        save_colnamesvisible;
    6467              : 
    6468              :         /*
    6469              :          * We force parens when nesting two SetOperationStmts, except when the
    6470              :          * lefthand input is another setop of the same kind.  Syntactically,
    6471              :          * we could omit parens in rather more cases, but it seems best to use
    6472              :          * parens to flag cases where the setop operator changes.  If we use
    6473              :          * parens, we also increase the indentation level for the child query.
    6474              :          *
    6475              :          * There are some cases in which parens are needed around a leaf query
    6476              :          * too, but those are more easily handled at the next level down (see
    6477              :          * code above).
    6478              :          */
    6479          148 :         if (IsA(op->larg, SetOperationStmt))
    6480              :         {
    6481           66 :             SetOperationStmt *lop = (SetOperationStmt *) op->larg;
    6482              : 
    6483           66 :             if (op->op == lop->op && op->all == lop->all)
    6484           66 :                 need_paren = false;
    6485              :             else
    6486            0 :                 need_paren = true;
    6487              :         }
    6488              :         else
    6489           82 :             need_paren = false;
    6490              : 
    6491          148 :         if (need_paren)
    6492              :         {
    6493            0 :             appendStringInfoChar(buf, '(');
    6494            0 :             subindent = PRETTYINDENT_STD;
    6495            0 :             appendContextKeyword(context, "", subindent, 0, 0);
    6496              :         }
    6497              :         else
    6498          148 :             subindent = 0;
    6499              : 
    6500          148 :         get_setop_query(op->larg, query, context);
    6501              : 
    6502          148 :         if (need_paren)
    6503            0 :             appendContextKeyword(context, ") ", -subindent, 0, 0);
    6504          148 :         else if (PRETTY_INDENT(context))
    6505          148 :             appendContextKeyword(context, "", -subindent, 0, 0);
    6506              :         else
    6507            0 :             appendStringInfoChar(buf, ' ');
    6508              : 
    6509          148 :         switch (op->op)
    6510              :         {
    6511          148 :             case SETOP_UNION:
    6512          148 :                 appendStringInfoString(buf, "UNION ");
    6513          148 :                 break;
    6514            0 :             case SETOP_INTERSECT:
    6515            0 :                 appendStringInfoString(buf, "INTERSECT ");
    6516            0 :                 break;
    6517            0 :             case SETOP_EXCEPT:
    6518            0 :                 appendStringInfoString(buf, "EXCEPT ");
    6519            0 :                 break;
    6520            0 :             default:
    6521            0 :                 elog(ERROR, "unrecognized set op: %d",
    6522              :                      (int) op->op);
    6523              :         }
    6524          148 :         if (op->all)
    6525          142 :             appendStringInfoString(buf, "ALL ");
    6526              : 
    6527              :         /* Always parenthesize if RHS is another setop */
    6528          148 :         need_paren = IsA(op->rarg, SetOperationStmt);
    6529              : 
    6530              :         /*
    6531              :          * The indentation code here is deliberately a bit different from that
    6532              :          * for the lefthand input, because we want the line breaks in
    6533              :          * different places.
    6534              :          */
    6535          148 :         if (need_paren)
    6536              :         {
    6537            0 :             appendStringInfoChar(buf, '(');
    6538            0 :             subindent = PRETTYINDENT_STD;
    6539              :         }
    6540              :         else
    6541          148 :             subindent = 0;
    6542          148 :         appendContextKeyword(context, "", subindent, 0, 0);
    6543              : 
    6544              :         /*
    6545              :          * The output column names of the RHS sub-select don't matter.
    6546              :          */
    6547          148 :         save_colnamesvisible = context->colNamesVisible;
    6548          148 :         context->colNamesVisible = false;
    6549              : 
    6550          148 :         get_setop_query(op->rarg, query, context);
    6551              : 
    6552          148 :         context->colNamesVisible = save_colnamesvisible;
    6553              : 
    6554          148 :         if (PRETTY_INDENT(context))
    6555          148 :             context->indentLevel -= subindent;
    6556          148 :         if (need_paren)
    6557            0 :             appendContextKeyword(context, ")", 0, 0, 0);
    6558              :     }
    6559              :     else
    6560              :     {
    6561            0 :         elog(ERROR, "unrecognized node type: %d",
    6562              :              (int) nodeTag(setOp));
    6563              :     }
    6564          378 : }
    6565              : 
    6566              : /*
    6567              :  * Display a sort/group clause.
    6568              :  *
    6569              :  * Also returns the expression tree, so caller need not find it again.
    6570              :  */
    6571              : static Node *
    6572          339 : get_rule_sortgroupclause(Index ref, List *tlist, bool force_colno,
    6573              :                          deparse_context *context)
    6574              : {
    6575          339 :     StringInfo  buf = context->buf;
    6576              :     TargetEntry *tle;
    6577              :     Node       *expr;
    6578              : 
    6579          339 :     tle = get_sortgroupref_tle(ref, tlist);
    6580          339 :     expr = (Node *) tle->expr;
    6581              : 
    6582              :     /*
    6583              :      * Use column-number form if requested by caller.  Otherwise, if
    6584              :      * expression is a constant, force it to be dumped with an explicit cast
    6585              :      * as decoration --- this is because a simple integer constant is
    6586              :      * ambiguous (and will be misinterpreted by findTargetlistEntrySQL92()) if
    6587              :      * we dump it without any decoration.  Similarly, if it's just a Var,
    6588              :      * there is risk of misinterpretation if the column name is reassigned in
    6589              :      * the SELECT list, so we may need to force table qualification.  And, if
    6590              :      * it's anything more complex than a simple Var, then force extra parens
    6591              :      * around it, to ensure it can't be misinterpreted as a cube() or rollup()
    6592              :      * construct.
    6593              :      */
    6594          339 :     if (force_colno)
    6595              :     {
    6596              :         Assert(!tle->resjunk);
    6597            6 :         appendStringInfo(buf, "%d", tle->resno);
    6598              :     }
    6599          333 :     else if (!expr)
    6600              :          /* do nothing, probably can't happen */ ;
    6601          333 :     else if (IsA(expr, Const))
    6602            0 :         get_const_expr((Const *) expr, context, 1);
    6603          333 :     else if (IsA(expr, Var))
    6604              :     {
    6605              :         /* Tell get_variable to check for name conflict */
    6606          319 :         bool        save_varinorderby = context->varInOrderBy;
    6607              : 
    6608          319 :         context->varInOrderBy = true;
    6609          319 :         (void) get_variable((Var *) expr, 0, false, context);
    6610          319 :         context->varInOrderBy = save_varinorderby;
    6611              :     }
    6612              :     else
    6613              :     {
    6614              :         /*
    6615              :          * We must force parens for function-like expressions even if
    6616              :          * PRETTY_PAREN is off, since those are the ones in danger of
    6617              :          * misparsing. For other expressions we need to force them only if
    6618              :          * PRETTY_PAREN is on, since otherwise the expression will output them
    6619              :          * itself. (We can't skip the parens.)
    6620              :          */
    6621           28 :         bool        need_paren = (PRETTY_PAREN(context)
    6622           14 :                                   || IsA(expr, FuncExpr)
    6623           12 :                                   || IsA(expr, Aggref)
    6624           12 :                                   || IsA(expr, WindowFunc)
    6625           28 :                                   || IsA(expr, JsonConstructorExpr));
    6626              : 
    6627           14 :         if (need_paren)
    6628            2 :             appendStringInfoChar(context->buf, '(');
    6629           14 :         get_rule_expr(expr, context, true);
    6630           14 :         if (need_paren)
    6631            2 :             appendStringInfoChar(context->buf, ')');
    6632              :     }
    6633              : 
    6634          339 :     return expr;
    6635              : }
    6636              : 
    6637              : /*
    6638              :  * Display a GroupingSet
    6639              :  */
    6640              : static void
    6641            9 : get_rule_groupingset(GroupingSet *gset, List *targetlist,
    6642              :                      bool omit_parens, deparse_context *context)
    6643              : {
    6644              :     ListCell   *l;
    6645            9 :     StringInfo  buf = context->buf;
    6646            9 :     bool        omit_child_parens = true;
    6647            9 :     char       *sep = "";
    6648              : 
    6649            9 :     switch (gset->kind)
    6650              :     {
    6651            0 :         case GROUPING_SET_EMPTY:
    6652            0 :             appendStringInfoString(buf, "()");
    6653            0 :             return;
    6654              : 
    6655            6 :         case GROUPING_SET_SIMPLE:
    6656              :             {
    6657            6 :                 if (!omit_parens || list_length(gset->content) != 1)
    6658            6 :                     appendStringInfoChar(buf, '(');
    6659              : 
    6660           21 :                 foreach(l, gset->content)
    6661              :                 {
    6662           15 :                     Index       ref = lfirst_int(l);
    6663              : 
    6664           15 :                     appendStringInfoString(buf, sep);
    6665           15 :                     get_rule_sortgroupclause(ref, targetlist,
    6666              :                                              false, context);
    6667           15 :                     sep = ", ";
    6668              :                 }
    6669              : 
    6670            6 :                 if (!omit_parens || list_length(gset->content) != 1)
    6671            6 :                     appendStringInfoChar(buf, ')');
    6672              :             }
    6673            6 :             return;
    6674              : 
    6675            3 :         case GROUPING_SET_ROLLUP:
    6676            3 :             appendStringInfoString(buf, "ROLLUP(");
    6677            3 :             break;
    6678            0 :         case GROUPING_SET_CUBE:
    6679            0 :             appendStringInfoString(buf, "CUBE(");
    6680            0 :             break;
    6681            0 :         case GROUPING_SET_SETS:
    6682            0 :             appendStringInfoString(buf, "GROUPING SETS (");
    6683            0 :             omit_child_parens = false;
    6684            0 :             break;
    6685              :     }
    6686              : 
    6687            9 :     foreach(l, gset->content)
    6688              :     {
    6689            6 :         appendStringInfoString(buf, sep);
    6690            6 :         get_rule_groupingset(lfirst(l), targetlist, omit_child_parens, context);
    6691            6 :         sep = ", ";
    6692              :     }
    6693              : 
    6694            3 :     appendStringInfoChar(buf, ')');
    6695              : }
    6696              : 
    6697              : /*
    6698              :  * Display an ORDER BY list.
    6699              :  */
    6700              : static void
    6701          172 : get_rule_orderby(List *orderList, List *targetList,
    6702              :                  bool force_colno, deparse_context *context)
    6703              : {
    6704          172 :     StringInfo  buf = context->buf;
    6705              :     const char *sep;
    6706              :     ListCell   *l;
    6707              : 
    6708          172 :     sep = "";
    6709          360 :     foreach(l, orderList)
    6710              :     {
    6711          188 :         SortGroupClause *srt = (SortGroupClause *) lfirst(l);
    6712              :         Node       *sortexpr;
    6713              :         Oid         sortcoltype;
    6714              :         TypeCacheEntry *typentry;
    6715              : 
    6716          188 :         appendStringInfoString(buf, sep);
    6717          188 :         sortexpr = get_rule_sortgroupclause(srt->tleSortGroupRef, targetList,
    6718              :                                             force_colno, context);
    6719          188 :         sortcoltype = exprType(sortexpr);
    6720              :         /* See whether operator is default < or > for datatype */
    6721          188 :         typentry = lookup_type_cache(sortcoltype,
    6722              :                                      TYPECACHE_LT_OPR | TYPECACHE_GT_OPR);
    6723          188 :         if (srt->sortop == typentry->lt_opr)
    6724              :         {
    6725              :             /* ASC is default, so emit nothing for it */
    6726          174 :             if (srt->nulls_first)
    6727            0 :                 appendStringInfoString(buf, " NULLS FIRST");
    6728              :         }
    6729           14 :         else if (srt->sortop == typentry->gt_opr)
    6730              :         {
    6731            5 :             appendStringInfoString(buf, " DESC");
    6732              :             /* DESC defaults to NULLS FIRST */
    6733            5 :             if (!srt->nulls_first)
    6734            1 :                 appendStringInfoString(buf, " NULLS LAST");
    6735              :         }
    6736              :         else
    6737              :         {
    6738            9 :             appendStringInfo(buf, " USING %s",
    6739              :                              generate_operator_name(srt->sortop,
    6740              :                                                     sortcoltype,
    6741              :                                                     sortcoltype));
    6742              :             /* be specific to eliminate ambiguity */
    6743            9 :             if (srt->nulls_first)
    6744            0 :                 appendStringInfoString(buf, " NULLS FIRST");
    6745              :             else
    6746            9 :                 appendStringInfoString(buf, " NULLS LAST");
    6747              :         }
    6748          188 :         sep = ", ";
    6749              :     }
    6750          172 : }
    6751              : 
    6752              : /*
    6753              :  * Display a WINDOW clause.
    6754              :  *
    6755              :  * Note that the windowClause list might contain only anonymous window
    6756              :  * specifications, in which case we should print nothing here.
    6757              :  */
    6758              : static void
    6759           24 : get_rule_windowclause(Query *query, deparse_context *context)
    6760              : {
    6761           24 :     StringInfo  buf = context->buf;
    6762              :     const char *sep;
    6763              :     ListCell   *l;
    6764              : 
    6765           24 :     sep = NULL;
    6766           48 :     foreach(l, query->windowClause)
    6767              :     {
    6768           24 :         WindowClause *wc = (WindowClause *) lfirst(l);
    6769              : 
    6770           24 :         if (wc->name == NULL)
    6771           21 :             continue;           /* ignore anonymous windows */
    6772              : 
    6773            3 :         if (sep == NULL)
    6774            3 :             appendContextKeyword(context, " WINDOW ",
    6775              :                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    6776              :         else
    6777            0 :             appendStringInfoString(buf, sep);
    6778              : 
    6779            3 :         appendStringInfo(buf, "%s AS ", quote_identifier(wc->name));
    6780              : 
    6781            3 :         get_rule_windowspec(wc, query->targetList, context);
    6782              : 
    6783            3 :         sep = ", ";
    6784              :     }
    6785           24 : }
    6786              : 
    6787              : /*
    6788              :  * Display a window definition
    6789              :  */
    6790              : static void
    6791           24 : get_rule_windowspec(WindowClause *wc, List *targetList,
    6792              :                     deparse_context *context)
    6793              : {
    6794           24 :     StringInfo  buf = context->buf;
    6795           24 :     bool        needspace = false;
    6796              :     const char *sep;
    6797              :     ListCell   *l;
    6798              : 
    6799           24 :     appendStringInfoChar(buf, '(');
    6800           24 :     if (wc->refname)
    6801              :     {
    6802            0 :         appendStringInfoString(buf, quote_identifier(wc->refname));
    6803            0 :         needspace = true;
    6804              :     }
    6805              :     /* partition clauses are always inherited, so only print if no refname */
    6806           24 :     if (wc->partitionClause && !wc->refname)
    6807              :     {
    6808            0 :         if (needspace)
    6809            0 :             appendStringInfoChar(buf, ' ');
    6810            0 :         appendStringInfoString(buf, "PARTITION BY ");
    6811            0 :         sep = "";
    6812            0 :         foreach(l, wc->partitionClause)
    6813              :         {
    6814            0 :             SortGroupClause *grp = (SortGroupClause *) lfirst(l);
    6815              : 
    6816            0 :             appendStringInfoString(buf, sep);
    6817            0 :             get_rule_sortgroupclause(grp->tleSortGroupRef, targetList,
    6818              :                                      false, context);
    6819            0 :             sep = ", ";
    6820              :         }
    6821            0 :         needspace = true;
    6822              :     }
    6823              :     /* print ordering clause only if not inherited */
    6824           24 :     if (wc->orderClause && !wc->copiedOrder)
    6825              :     {
    6826           24 :         if (needspace)
    6827            0 :             appendStringInfoChar(buf, ' ');
    6828           24 :         appendStringInfoString(buf, "ORDER BY ");
    6829           24 :         get_rule_orderby(wc->orderClause, targetList, false, context);
    6830           24 :         needspace = true;
    6831              :     }
    6832              :     /* framing clause is never inherited, so print unless it's default */
    6833           24 :     if (wc->frameOptions & FRAMEOPTION_NONDEFAULT)
    6834              :     {
    6835           21 :         if (needspace)
    6836           21 :             appendStringInfoChar(buf, ' ');
    6837           21 :         get_window_frame_options(wc->frameOptions,
    6838              :                                  wc->startOffset, wc->endOffset,
    6839              :                                  context);
    6840              :     }
    6841           24 :     appendStringInfoChar(buf, ')');
    6842           24 : }
    6843              : 
    6844              : /*
    6845              :  * Append the description of a window's framing options to context->buf
    6846              :  */
    6847              : static void
    6848          119 : get_window_frame_options(int frameOptions,
    6849              :                          Node *startOffset, Node *endOffset,
    6850              :                          deparse_context *context)
    6851              : {
    6852          119 :     StringInfo  buf = context->buf;
    6853              : 
    6854          119 :     if (frameOptions & FRAMEOPTION_NONDEFAULT)
    6855              :     {
    6856          119 :         if (frameOptions & FRAMEOPTION_RANGE)
    6857           10 :             appendStringInfoString(buf, "RANGE ");
    6858          109 :         else if (frameOptions & FRAMEOPTION_ROWS)
    6859          103 :             appendStringInfoString(buf, "ROWS ");
    6860            6 :         else if (frameOptions & FRAMEOPTION_GROUPS)
    6861            6 :             appendStringInfoString(buf, "GROUPS ");
    6862              :         else
    6863              :             Assert(false);
    6864          119 :         if (frameOptions & FRAMEOPTION_BETWEEN)
    6865           46 :             appendStringInfoString(buf, "BETWEEN ");
    6866          119 :         if (frameOptions & FRAMEOPTION_START_UNBOUNDED_PRECEDING)
    6867           76 :             appendStringInfoString(buf, "UNBOUNDED PRECEDING ");
    6868           43 :         else if (frameOptions & FRAMEOPTION_START_CURRENT_ROW)
    6869           13 :             appendStringInfoString(buf, "CURRENT ROW ");
    6870           30 :         else if (frameOptions & FRAMEOPTION_START_OFFSET)
    6871              :         {
    6872           30 :             get_rule_expr(startOffset, context, false);
    6873           30 :             if (frameOptions & FRAMEOPTION_START_OFFSET_PRECEDING)
    6874           30 :                 appendStringInfoString(buf, " PRECEDING ");
    6875            0 :             else if (frameOptions & FRAMEOPTION_START_OFFSET_FOLLOWING)
    6876            0 :                 appendStringInfoString(buf, " FOLLOWING ");
    6877              :             else
    6878              :                 Assert(false);
    6879              :         }
    6880              :         else
    6881              :             Assert(false);
    6882          119 :         if (frameOptions & FRAMEOPTION_BETWEEN)
    6883              :         {
    6884           46 :             appendStringInfoString(buf, "AND ");
    6885           46 :             if (frameOptions & FRAMEOPTION_END_UNBOUNDED_FOLLOWING)
    6886           10 :                 appendStringInfoString(buf, "UNBOUNDED FOLLOWING ");
    6887           36 :             else if (frameOptions & FRAMEOPTION_END_CURRENT_ROW)
    6888            3 :                 appendStringInfoString(buf, "CURRENT ROW ");
    6889           33 :             else if (frameOptions & FRAMEOPTION_END_OFFSET)
    6890              :             {
    6891           33 :                 get_rule_expr(endOffset, context, false);
    6892           33 :                 if (frameOptions & FRAMEOPTION_END_OFFSET_PRECEDING)
    6893            0 :                     appendStringInfoString(buf, " PRECEDING ");
    6894           33 :                 else if (frameOptions & FRAMEOPTION_END_OFFSET_FOLLOWING)
    6895           33 :                     appendStringInfoString(buf, " FOLLOWING ");
    6896              :                 else
    6897              :                     Assert(false);
    6898              :             }
    6899              :             else
    6900              :                 Assert(false);
    6901              :         }
    6902          119 :         if (frameOptions & FRAMEOPTION_EXCLUDE_CURRENT_ROW)
    6903            3 :             appendStringInfoString(buf, "EXCLUDE CURRENT ROW ");
    6904          116 :         else if (frameOptions & FRAMEOPTION_EXCLUDE_GROUP)
    6905            3 :             appendStringInfoString(buf, "EXCLUDE GROUP ");
    6906          113 :         else if (frameOptions & FRAMEOPTION_EXCLUDE_TIES)
    6907            3 :             appendStringInfoString(buf, "EXCLUDE TIES ");
    6908              :         /* we will now have a trailing space; remove it */
    6909          119 :         buf->data[--(buf->len)] = '\0';
    6910              :     }
    6911          119 : }
    6912              : 
    6913              : /*
    6914              :  * Return the description of a window's framing options as a palloc'd string
    6915              :  */
    6916              : char *
    6917           98 : get_window_frame_options_for_explain(int frameOptions,
    6918              :                                      Node *startOffset, Node *endOffset,
    6919              :                                      List *dpcontext, bool forceprefix)
    6920              : {
    6921              :     StringInfoData buf;
    6922              :     deparse_context context;
    6923              : 
    6924           98 :     initStringInfo(&buf);
    6925           98 :     context.buf = &buf;
    6926           98 :     context.namespaces = dpcontext;
    6927           98 :     context.resultDesc = NULL;
    6928           98 :     context.targetList = NIL;
    6929           98 :     context.windowClause = NIL;
    6930           98 :     context.varprefix = forceprefix;
    6931           98 :     context.prettyFlags = 0;
    6932           98 :     context.wrapColumn = WRAP_COLUMN_DEFAULT;
    6933           98 :     context.indentLevel = 0;
    6934           98 :     context.colNamesVisible = true;
    6935           98 :     context.inGroupBy = false;
    6936           98 :     context.varInOrderBy = false;
    6937           98 :     context.appendparents = NULL;
    6938              : 
    6939           98 :     get_window_frame_options(frameOptions, startOffset, endOffset, &context);
    6940              : 
    6941           98 :     return buf.data;
    6942              : }
    6943              : 
    6944              : /* ----------
    6945              :  * get_insert_query_def         - Parse back an INSERT parsetree
    6946              :  * ----------
    6947              :  */
    6948              : static void
    6949          173 : get_insert_query_def(Query *query, deparse_context *context)
    6950              : {
    6951          173 :     StringInfo  buf = context->buf;
    6952          173 :     RangeTblEntry *select_rte = NULL;
    6953          173 :     RangeTblEntry *values_rte = NULL;
    6954              :     RangeTblEntry *rte;
    6955              :     char       *sep;
    6956              :     ListCell   *l;
    6957              :     List       *strippedexprs;
    6958              : 
    6959              :     /* Insert the WITH clause if given */
    6960          173 :     get_with_clause(query, context);
    6961              : 
    6962              :     /*
    6963              :      * If it's an INSERT ... SELECT or multi-row VALUES, there will be a
    6964              :      * single RTE for the SELECT or VALUES.  Plain VALUES has neither.
    6965              :      */
    6966          676 :     foreach(l, query->rtable)
    6967              :     {
    6968          503 :         rte = (RangeTblEntry *) lfirst(l);
    6969              : 
    6970          503 :         if (rte->rtekind == RTE_SUBQUERY)
    6971              :         {
    6972           25 :             if (select_rte)
    6973            0 :                 elog(ERROR, "too many subquery RTEs in INSERT");
    6974           25 :             select_rte = rte;
    6975              :         }
    6976              : 
    6977          503 :         if (rte->rtekind == RTE_VALUES)
    6978              :         {
    6979           22 :             if (values_rte)
    6980            0 :                 elog(ERROR, "too many values RTEs in INSERT");
    6981           22 :             values_rte = rte;
    6982              :         }
    6983              :     }
    6984          173 :     if (select_rte && values_rte)
    6985            0 :         elog(ERROR, "both subquery and values RTEs in INSERT");
    6986              : 
    6987              :     /*
    6988              :      * Start the query with INSERT INTO relname
    6989              :      */
    6990          173 :     rte = rt_fetch(query->resultRelation, query->rtable);
    6991              :     Assert(rte->rtekind == RTE_RELATION);
    6992              : 
    6993          173 :     if (PRETTY_INDENT(context))
    6994              :     {
    6995          173 :         context->indentLevel += PRETTYINDENT_STD;
    6996          173 :         appendStringInfoChar(buf, ' ');
    6997              :     }
    6998          173 :     appendStringInfo(buf, "INSERT INTO %s",
    6999              :                      generate_relation_name(rte->relid, NIL));
    7000              : 
    7001              :     /* Print the relation alias, if needed; INSERT requires explicit AS */
    7002          173 :     get_rte_alias(rte, query->resultRelation, true, context);
    7003              : 
    7004              :     /* always want a space here */
    7005          173 :     appendStringInfoChar(buf, ' ');
    7006              : 
    7007              :     /*
    7008              :      * Add the insert-column-names list.  Any indirection decoration needed on
    7009              :      * the column names can be inferred from the top targetlist.
    7010              :      */
    7011          173 :     strippedexprs = NIL;
    7012          173 :     sep = "";
    7013          173 :     if (query->targetList)
    7014          173 :         appendStringInfoChar(buf, '(');
    7015          630 :     foreach(l, query->targetList)
    7016              :     {
    7017          457 :         TargetEntry *tle = (TargetEntry *) lfirst(l);
    7018              : 
    7019          457 :         if (tle->resjunk)
    7020            0 :             continue;           /* ignore junk entries */
    7021              : 
    7022          457 :         appendStringInfoString(buf, sep);
    7023          457 :         sep = ", ";
    7024              : 
    7025              :         /*
    7026              :          * Put out name of target column; look in the catalogs, not at
    7027              :          * tle->resname, since resname will fail to track RENAME.
    7028              :          */
    7029          457 :         appendStringInfoString(buf,
    7030          457 :                                quote_identifier(get_attname(rte->relid,
    7031          457 :                                                             tle->resno,
    7032              :                                                             false)));
    7033              : 
    7034              :         /*
    7035              :          * Print any indirection needed (subfields or subscripts), and strip
    7036              :          * off the top-level nodes representing the indirection assignments.
    7037              :          * Add the stripped expressions to strippedexprs.  (If it's a
    7038              :          * single-VALUES statement, the stripped expressions are the VALUES to
    7039              :          * print below.  Otherwise they're just Vars and not really
    7040              :          * interesting.)
    7041              :          */
    7042          457 :         strippedexprs = lappend(strippedexprs,
    7043          457 :                                 processIndirection((Node *) tle->expr,
    7044              :                                                    context));
    7045              :     }
    7046          173 :     if (query->targetList)
    7047          173 :         appendStringInfoString(buf, ") ");
    7048              : 
    7049          173 :     if (query->override)
    7050              :     {
    7051            0 :         if (query->override == OVERRIDING_SYSTEM_VALUE)
    7052            0 :             appendStringInfoString(buf, "OVERRIDING SYSTEM VALUE ");
    7053            0 :         else if (query->override == OVERRIDING_USER_VALUE)
    7054            0 :             appendStringInfoString(buf, "OVERRIDING USER VALUE ");
    7055              :     }
    7056              : 
    7057          173 :     if (select_rte)
    7058              :     {
    7059              :         /* Add the SELECT */
    7060           25 :         get_query_def(select_rte->subquery, buf, context->namespaces, NULL,
    7061              :                       false,
    7062              :                       context->prettyFlags, context->wrapColumn,
    7063              :                       context->indentLevel);
    7064              :     }
    7065          148 :     else if (values_rte)
    7066              :     {
    7067              :         /* Add the multi-VALUES expression lists */
    7068           22 :         get_values_def(values_rte->values_lists, context);
    7069              :     }
    7070          126 :     else if (strippedexprs)
    7071              :     {
    7072              :         /* Add the single-VALUES expression list */
    7073          126 :         appendContextKeyword(context, "VALUES (",
    7074              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
    7075          126 :         get_rule_list_toplevel(strippedexprs, context, false);
    7076          126 :         appendStringInfoChar(buf, ')');
    7077              :     }
    7078              :     else
    7079              :     {
    7080              :         /* No expressions, so it must be DEFAULT VALUES */
    7081            0 :         appendStringInfoString(buf, "DEFAULT VALUES");
    7082              :     }
    7083              : 
    7084              :     /* Add ON CONFLICT if present */
    7085          173 :     if (query->onConflict)
    7086              :     {
    7087           18 :         OnConflictExpr *confl = query->onConflict;
    7088              : 
    7089           18 :         appendStringInfoString(buf, " ON CONFLICT");
    7090              : 
    7091           18 :         if (confl->arbiterElems)
    7092              :         {
    7093              :             /* Add the single-VALUES expression list */
    7094           15 :             appendStringInfoChar(buf, '(');
    7095           15 :             get_rule_expr((Node *) confl->arbiterElems, context, false);
    7096           15 :             appendStringInfoChar(buf, ')');
    7097              : 
    7098              :             /* Add a WHERE clause (for partial indexes) if given */
    7099           15 :             if (confl->arbiterWhere != NULL)
    7100              :             {
    7101              :                 bool        save_varprefix;
    7102              : 
    7103              :                 /*
    7104              :                  * Force non-prefixing of Vars, since parser assumes that they
    7105              :                  * belong to target relation.  WHERE clause does not use
    7106              :                  * InferenceElem, so this is separately required.
    7107              :                  */
    7108            6 :                 save_varprefix = context->varprefix;
    7109            6 :                 context->varprefix = false;
    7110              : 
    7111            6 :                 appendContextKeyword(context, " WHERE ",
    7112              :                                      -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    7113            6 :                 get_rule_expr(confl->arbiterWhere, context, false);
    7114              : 
    7115            6 :                 context->varprefix = save_varprefix;
    7116              :             }
    7117              :         }
    7118            3 :         else if (OidIsValid(confl->constraint))
    7119              :         {
    7120            0 :             char       *constraint = get_constraint_name(confl->constraint);
    7121              : 
    7122            0 :             if (!constraint)
    7123            0 :                 elog(ERROR, "cache lookup failed for constraint %u",
    7124              :                      confl->constraint);
    7125            0 :             appendStringInfo(buf, " ON CONSTRAINT %s",
    7126              :                              quote_identifier(constraint));
    7127              :         }
    7128              : 
    7129           18 :         if (confl->action == ONCONFLICT_NOTHING)
    7130              :         {
    7131            9 :             appendStringInfoString(buf, " DO NOTHING");
    7132              :         }
    7133            9 :         else if (confl->action == ONCONFLICT_UPDATE)
    7134              :         {
    7135            6 :             appendStringInfoString(buf, " DO UPDATE SET ");
    7136              :             /* Deparse targetlist */
    7137            6 :             get_update_query_targetlist_def(query, confl->onConflictSet,
    7138              :                                             context, rte);
    7139              : 
    7140              :             /* Add a WHERE clause if given */
    7141            6 :             if (confl->onConflictWhere != NULL)
    7142              :             {
    7143            6 :                 appendContextKeyword(context, " WHERE ",
    7144              :                                      -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    7145            6 :                 get_rule_expr(confl->onConflictWhere, context, false);
    7146              :             }
    7147              :         }
    7148              :         else
    7149              :         {
    7150              :             Assert(confl->action == ONCONFLICT_SELECT);
    7151            3 :             appendStringInfoString(buf, " DO SELECT");
    7152              : 
    7153              :             /* Add FOR [KEY] UPDATE/SHARE clause if present */
    7154            3 :             if (confl->lockStrength != LCS_NONE)
    7155            3 :                 appendStringInfoString(buf, get_lock_clause_strength(confl->lockStrength));
    7156              : 
    7157              :             /* Add a WHERE clause if given */
    7158            3 :             if (confl->onConflictWhere != NULL)
    7159              :             {
    7160            3 :                 appendContextKeyword(context, " WHERE ",
    7161              :                                      -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    7162            3 :                 get_rule_expr(confl->onConflictWhere, context, false);
    7163              :             }
    7164              :         }
    7165              :     }
    7166              : 
    7167              :     /* Add RETURNING if present */
    7168          173 :     if (query->returningList)
    7169           42 :         get_returning_clause(query, context);
    7170          173 : }
    7171              : 
    7172              : 
    7173              : /* ----------
    7174              :  * get_update_query_def         - Parse back an UPDATE parsetree
    7175              :  * ----------
    7176              :  */
    7177              : static void
    7178           77 : get_update_query_def(Query *query, deparse_context *context)
    7179              : {
    7180           77 :     StringInfo  buf = context->buf;
    7181              :     RangeTblEntry *rte;
    7182              : 
    7183              :     /* Insert the WITH clause if given */
    7184           77 :     get_with_clause(query, context);
    7185              : 
    7186              :     /*
    7187              :      * Start the query with UPDATE relname SET
    7188              :      */
    7189           77 :     rte = rt_fetch(query->resultRelation, query->rtable);
    7190              :     Assert(rte->rtekind == RTE_RELATION);
    7191           77 :     if (PRETTY_INDENT(context))
    7192              :     {
    7193           77 :         appendStringInfoChar(buf, ' ');
    7194           77 :         context->indentLevel += PRETTYINDENT_STD;
    7195              :     }
    7196          154 :     appendStringInfo(buf, "UPDATE %s%s",
    7197           77 :                      only_marker(rte),
    7198              :                      generate_relation_name(rte->relid, NIL));
    7199              : 
    7200              :     /* Print the relation alias, if needed */
    7201           77 :     get_rte_alias(rte, query->resultRelation, false, context);
    7202              : 
    7203           77 :     appendStringInfoString(buf, " SET ");
    7204              : 
    7205              :     /* Deparse targetlist */
    7206           77 :     get_update_query_targetlist_def(query, query->targetList, context, rte);
    7207              : 
    7208              :     /* Add the FROM clause if needed */
    7209           77 :     get_from_clause(query, " FROM ", context);
    7210              : 
    7211              :     /* Add a WHERE clause if given */
    7212           77 :     if (query->jointree->quals != NULL)
    7213              :     {
    7214           57 :         appendContextKeyword(context, " WHERE ",
    7215              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    7216           57 :         get_rule_expr(query->jointree->quals, context, false);
    7217              :     }
    7218              : 
    7219              :     /* Add RETURNING if present */
    7220           77 :     if (query->returningList)
    7221           23 :         get_returning_clause(query, context);
    7222           77 : }
    7223              : 
    7224              : 
    7225              : /* ----------
    7226              :  * get_update_query_targetlist_def          - Parse back an UPDATE targetlist
    7227              :  * ----------
    7228              :  */
    7229              : static void
    7230           95 : get_update_query_targetlist_def(Query *query, List *targetList,
    7231              :                                 deparse_context *context, RangeTblEntry *rte)
    7232              : {
    7233           95 :     StringInfo  buf = context->buf;
    7234              :     ListCell   *l;
    7235              :     ListCell   *next_ma_cell;
    7236              :     int         remaining_ma_columns;
    7237              :     const char *sep;
    7238              :     SubLink    *cur_ma_sublink;
    7239              :     List       *ma_sublinks;
    7240              : 
    7241              :     /*
    7242              :      * Prepare to deal with MULTIEXPR assignments: collect the source SubLinks
    7243              :      * into a list.  We expect them to appear, in ID order, in resjunk tlist
    7244              :      * entries.
    7245              :      */
    7246           95 :     ma_sublinks = NIL;
    7247           95 :     if (query->hasSubLinks)      /* else there can't be any */
    7248              :     {
    7249           21 :         foreach(l, targetList)
    7250              :         {
    7251           15 :             TargetEntry *tle = (TargetEntry *) lfirst(l);
    7252              : 
    7253           15 :             if (tle->resjunk && IsA(tle->expr, SubLink))
    7254              :             {
    7255            3 :                 SubLink    *sl = (SubLink *) tle->expr;
    7256              : 
    7257            3 :                 if (sl->subLinkType == MULTIEXPR_SUBLINK)
    7258              :                 {
    7259            3 :                     ma_sublinks = lappend(ma_sublinks, sl);
    7260              :                     Assert(sl->subLinkId == list_length(ma_sublinks));
    7261              :                 }
    7262              :             }
    7263              :         }
    7264              :     }
    7265           95 :     next_ma_cell = list_head(ma_sublinks);
    7266           95 :     cur_ma_sublink = NULL;
    7267           95 :     remaining_ma_columns = 0;
    7268              : 
    7269              :     /* Add the comma separated list of 'attname = value' */
    7270           95 :     sep = "";
    7271          244 :     foreach(l, targetList)
    7272              :     {
    7273          149 :         TargetEntry *tle = (TargetEntry *) lfirst(l);
    7274              :         Node       *expr;
    7275              : 
    7276          149 :         if (tle->resjunk)
    7277            3 :             continue;           /* ignore junk entries */
    7278              : 
    7279              :         /* Emit separator (OK whether we're in multiassignment or not) */
    7280          146 :         appendStringInfoString(buf, sep);
    7281          146 :         sep = ", ";
    7282              : 
    7283              :         /*
    7284              :          * Check to see if we're starting a multiassignment group: if so,
    7285              :          * output a left paren.
    7286              :          */
    7287          146 :         if (next_ma_cell != NULL && cur_ma_sublink == NULL)
    7288              :         {
    7289              :             /*
    7290              :              * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
    7291              :              * Param.  That could be buried under FieldStores and
    7292              :              * SubscriptingRefs and CoerceToDomains (cf processIndirection()),
    7293              :              * and underneath those there could be an implicit type coercion.
    7294              :              * Because we would ignore implicit type coercions anyway, we
    7295              :              * don't need to be as careful as processIndirection() is about
    7296              :              * descending past implicit CoerceToDomains.
    7297              :              */
    7298            3 :             expr = (Node *) tle->expr;
    7299            6 :             while (expr)
    7300              :             {
    7301            6 :                 if (IsA(expr, FieldStore))
    7302              :                 {
    7303            0 :                     FieldStore *fstore = (FieldStore *) expr;
    7304              : 
    7305            0 :                     expr = (Node *) linitial(fstore->newvals);
    7306              :                 }
    7307            6 :                 else if (IsA(expr, SubscriptingRef))
    7308              :                 {
    7309            3 :                     SubscriptingRef *sbsref = (SubscriptingRef *) expr;
    7310              : 
    7311            3 :                     if (sbsref->refassgnexpr == NULL)
    7312            0 :                         break;
    7313              : 
    7314            3 :                     expr = (Node *) sbsref->refassgnexpr;
    7315              :                 }
    7316            3 :                 else if (IsA(expr, CoerceToDomain))
    7317              :                 {
    7318            0 :                     CoerceToDomain *cdomain = (CoerceToDomain *) expr;
    7319              : 
    7320            0 :                     if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
    7321            0 :                         break;
    7322            0 :                     expr = (Node *) cdomain->arg;
    7323              :                 }
    7324              :                 else
    7325            3 :                     break;
    7326              :             }
    7327            3 :             expr = strip_implicit_coercions(expr);
    7328              : 
    7329            3 :             if (expr && IsA(expr, Param) &&
    7330            3 :                 ((Param *) expr)->paramkind == PARAM_MULTIEXPR)
    7331              :             {
    7332            3 :                 cur_ma_sublink = (SubLink *) lfirst(next_ma_cell);
    7333            3 :                 next_ma_cell = lnext(ma_sublinks, next_ma_cell);
    7334            3 :                 remaining_ma_columns = count_nonjunk_tlist_entries(((Query *) cur_ma_sublink->subselect)->targetList);
    7335              :                 Assert(((Param *) expr)->paramid ==
    7336              :                        ((cur_ma_sublink->subLinkId << 16) | 1));
    7337            3 :                 appendStringInfoChar(buf, '(');
    7338              :             }
    7339              :         }
    7340              : 
    7341              :         /*
    7342              :          * Put out name of target column; look in the catalogs, not at
    7343              :          * tle->resname, since resname will fail to track RENAME.
    7344              :          */
    7345          146 :         appendStringInfoString(buf,
    7346          146 :                                quote_identifier(get_attname(rte->relid,
    7347          146 :                                                             tle->resno,
    7348              :                                                             false)));
    7349              : 
    7350              :         /*
    7351              :          * Print any indirection needed (subfields or subscripts), and strip
    7352              :          * off the top-level nodes representing the indirection assignments.
    7353              :          */
    7354          146 :         expr = processIndirection((Node *) tle->expr, context);
    7355              : 
    7356              :         /*
    7357              :          * If we're in a multiassignment, skip printing anything more, unless
    7358              :          * this is the last column; in which case, what we print should be the
    7359              :          * sublink, not the Param.
    7360              :          */
    7361          146 :         if (cur_ma_sublink != NULL)
    7362              :         {
    7363            9 :             if (--remaining_ma_columns > 0)
    7364            6 :                 continue;       /* not the last column of multiassignment */
    7365            3 :             appendStringInfoChar(buf, ')');
    7366            3 :             expr = (Node *) cur_ma_sublink;
    7367            3 :             cur_ma_sublink = NULL;
    7368              :         }
    7369              : 
    7370          140 :         appendStringInfoString(buf, " = ");
    7371              : 
    7372          140 :         get_rule_expr(expr, context, false);
    7373              :     }
    7374           95 : }
    7375              : 
    7376              : 
    7377              : /* ----------
    7378              :  * get_delete_query_def         - Parse back a DELETE parsetree
    7379              :  * ----------
    7380              :  */
    7381              : static void
    7382           38 : get_delete_query_def(Query *query, deparse_context *context)
    7383              : {
    7384           38 :     StringInfo  buf = context->buf;
    7385              :     RangeTblEntry *rte;
    7386              : 
    7387              :     /* Insert the WITH clause if given */
    7388           38 :     get_with_clause(query, context);
    7389              : 
    7390              :     /*
    7391              :      * Start the query with DELETE FROM relname
    7392              :      */
    7393           38 :     rte = rt_fetch(query->resultRelation, query->rtable);
    7394              :     Assert(rte->rtekind == RTE_RELATION);
    7395           38 :     if (PRETTY_INDENT(context))
    7396              :     {
    7397           38 :         appendStringInfoChar(buf, ' ');
    7398           38 :         context->indentLevel += PRETTYINDENT_STD;
    7399              :     }
    7400           76 :     appendStringInfo(buf, "DELETE FROM %s%s",
    7401           38 :                      only_marker(rte),
    7402              :                      generate_relation_name(rte->relid, NIL));
    7403              : 
    7404              :     /* Print the relation alias, if needed */
    7405           38 :     get_rte_alias(rte, query->resultRelation, false, context);
    7406              : 
    7407              :     /* Add the USING clause if given */
    7408           38 :     get_from_clause(query, " USING ", context);
    7409              : 
    7410              :     /* Add a WHERE clause if given */
    7411           38 :     if (query->jointree->quals != NULL)
    7412              :     {
    7413           38 :         appendContextKeyword(context, " WHERE ",
    7414              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 1);
    7415           38 :         get_rule_expr(query->jointree->quals, context, false);
    7416              :     }
    7417              : 
    7418              :     /* Add RETURNING if present */
    7419           38 :     if (query->returningList)
    7420            8 :         get_returning_clause(query, context);
    7421           38 : }
    7422              : 
    7423              : 
    7424              : /* ----------
    7425              :  * get_merge_query_def              - Parse back a MERGE parsetree
    7426              :  * ----------
    7427              :  */
    7428              : static void
    7429            6 : get_merge_query_def(Query *query, deparse_context *context)
    7430              : {
    7431            6 :     StringInfo  buf = context->buf;
    7432              :     RangeTblEntry *rte;
    7433              :     ListCell   *lc;
    7434              :     bool        haveNotMatchedBySource;
    7435              : 
    7436              :     /* Insert the WITH clause if given */
    7437            6 :     get_with_clause(query, context);
    7438              : 
    7439              :     /*
    7440              :      * Start the query with MERGE INTO relname
    7441              :      */
    7442            6 :     rte = rt_fetch(query->resultRelation, query->rtable);
    7443              :     Assert(rte->rtekind == RTE_RELATION);
    7444            6 :     if (PRETTY_INDENT(context))
    7445              :     {
    7446            6 :         appendStringInfoChar(buf, ' ');
    7447            6 :         context->indentLevel += PRETTYINDENT_STD;
    7448              :     }
    7449           12 :     appendStringInfo(buf, "MERGE INTO %s%s",
    7450            6 :                      only_marker(rte),
    7451              :                      generate_relation_name(rte->relid, NIL));
    7452              : 
    7453              :     /* Print the relation alias, if needed */
    7454            6 :     get_rte_alias(rte, query->resultRelation, false, context);
    7455              : 
    7456              :     /* Print the source relation and join clause */
    7457            6 :     get_from_clause(query, " USING ", context);
    7458            6 :     appendContextKeyword(context, " ON ",
    7459              :                          -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
    7460            6 :     get_rule_expr(query->mergeJoinCondition, context, false);
    7461              : 
    7462              :     /*
    7463              :      * Test for any NOT MATCHED BY SOURCE actions.  If there are none, then
    7464              :      * any NOT MATCHED BY TARGET actions are output as "WHEN NOT MATCHED", per
    7465              :      * SQL standard.  Otherwise, we have a non-SQL-standard query, so output
    7466              :      * "BY SOURCE" / "BY TARGET" qualifiers for all NOT MATCHED actions, to be
    7467              :      * more explicit.
    7468              :      */
    7469            6 :     haveNotMatchedBySource = false;
    7470           42 :     foreach(lc, query->mergeActionList)
    7471              :     {
    7472           39 :         MergeAction *action = lfirst_node(MergeAction, lc);
    7473              : 
    7474           39 :         if (action->matchKind == MERGE_WHEN_NOT_MATCHED_BY_SOURCE)
    7475              :         {
    7476            3 :             haveNotMatchedBySource = true;
    7477            3 :             break;
    7478              :         }
    7479              :     }
    7480              : 
    7481              :     /* Print each merge action */
    7482           45 :     foreach(lc, query->mergeActionList)
    7483              :     {
    7484           39 :         MergeAction *action = lfirst_node(MergeAction, lc);
    7485              : 
    7486           39 :         appendContextKeyword(context, " WHEN ",
    7487              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
    7488           39 :         switch (action->matchKind)
    7489              :         {
    7490           18 :             case MERGE_WHEN_MATCHED:
    7491           18 :                 appendStringInfoString(buf, "MATCHED");
    7492           18 :                 break;
    7493            3 :             case MERGE_WHEN_NOT_MATCHED_BY_SOURCE:
    7494            3 :                 appendStringInfoString(buf, "NOT MATCHED BY SOURCE");
    7495            3 :                 break;
    7496           18 :             case MERGE_WHEN_NOT_MATCHED_BY_TARGET:
    7497           18 :                 if (haveNotMatchedBySource)
    7498            3 :                     appendStringInfoString(buf, "NOT MATCHED BY TARGET");
    7499              :                 else
    7500           15 :                     appendStringInfoString(buf, "NOT MATCHED");
    7501           18 :                 break;
    7502            0 :             default:
    7503            0 :                 elog(ERROR, "unrecognized matchKind: %d",
    7504              :                      (int) action->matchKind);
    7505              :         }
    7506              : 
    7507           39 :         if (action->qual)
    7508              :         {
    7509           24 :             appendContextKeyword(context, " AND ",
    7510              :                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
    7511           24 :             get_rule_expr(action->qual, context, false);
    7512              :         }
    7513           39 :         appendContextKeyword(context, " THEN ",
    7514              :                              -PRETTYINDENT_STD, PRETTYINDENT_STD, 3);
    7515              : 
    7516           39 :         if (action->commandType == CMD_INSERT)
    7517              :         {
    7518              :             /* This generally matches get_insert_query_def() */
    7519           18 :             List       *strippedexprs = NIL;
    7520           18 :             const char *sep = "";
    7521              :             ListCell   *lc2;
    7522              : 
    7523           18 :             appendStringInfoString(buf, "INSERT");
    7524              : 
    7525           18 :             if (action->targetList)
    7526           15 :                 appendStringInfoString(buf, " (");
    7527           51 :             foreach(lc2, action->targetList)
    7528              :             {
    7529           33 :                 TargetEntry *tle = (TargetEntry *) lfirst(lc2);
    7530              : 
    7531              :                 Assert(!tle->resjunk);
    7532              : 
    7533           33 :                 appendStringInfoString(buf, sep);
    7534           33 :                 sep = ", ";
    7535              : 
    7536           33 :                 appendStringInfoString(buf,
    7537           33 :                                        quote_identifier(get_attname(rte->relid,
    7538           33 :                                                                     tle->resno,
    7539              :                                                                     false)));
    7540           33 :                 strippedexprs = lappend(strippedexprs,
    7541           33 :                                         processIndirection((Node *) tle->expr,
    7542              :                                                            context));
    7543              :             }
    7544           18 :             if (action->targetList)
    7545           15 :                 appendStringInfoChar(buf, ')');
    7546              : 
    7547           18 :             if (action->override)
    7548              :             {
    7549            3 :                 if (action->override == OVERRIDING_SYSTEM_VALUE)
    7550            0 :                     appendStringInfoString(buf, " OVERRIDING SYSTEM VALUE");
    7551            3 :                 else if (action->override == OVERRIDING_USER_VALUE)
    7552            3 :                     appendStringInfoString(buf, " OVERRIDING USER VALUE");
    7553              :             }
    7554              : 
    7555           18 :             if (strippedexprs)
    7556              :             {
    7557           15 :                 appendContextKeyword(context, " VALUES (",
    7558              :                                      -PRETTYINDENT_STD, PRETTYINDENT_STD, 4);
    7559           15 :                 get_rule_list_toplevel(strippedexprs, context, false);
    7560           15 :                 appendStringInfoChar(buf, ')');
    7561              :             }
    7562              :             else
    7563            3 :                 appendStringInfoString(buf, " DEFAULT VALUES");
    7564              :         }
    7565           21 :         else if (action->commandType == CMD_UPDATE)
    7566              :         {
    7567           12 :             appendStringInfoString(buf, "UPDATE SET ");
    7568           12 :             get_update_query_targetlist_def(query, action->targetList,
    7569              :                                             context, rte);
    7570              :         }
    7571            9 :         else if (action->commandType == CMD_DELETE)
    7572            6 :             appendStringInfoString(buf, "DELETE");
    7573            3 :         else if (action->commandType == CMD_NOTHING)
    7574            3 :             appendStringInfoString(buf, "DO NOTHING");
    7575              :     }
    7576              : 
    7577              :     /* Add RETURNING if present */
    7578            6 :     if (query->returningList)
    7579            3 :         get_returning_clause(query, context);
    7580            6 : }
    7581              : 
    7582              : 
    7583              : /* ----------
    7584              :  * get_utility_query_def            - Parse back a UTILITY parsetree
    7585              :  * ----------
    7586              :  */
    7587              : static void
    7588            8 : get_utility_query_def(Query *query, deparse_context *context)
    7589              : {
    7590            8 :     StringInfo  buf = context->buf;
    7591              : 
    7592            8 :     if (query->utilityStmt && IsA(query->utilityStmt, NotifyStmt))
    7593            8 :     {
    7594            8 :         NotifyStmt *stmt = (NotifyStmt *) query->utilityStmt;
    7595              : 
    7596            8 :         appendContextKeyword(context, "",
    7597              :                              0, PRETTYINDENT_STD, 1);
    7598            8 :         appendStringInfo(buf, "NOTIFY %s",
    7599            8 :                          quote_identifier(stmt->conditionname));
    7600            8 :         if (stmt->payload)
    7601              :         {
    7602            0 :             appendStringInfoString(buf, ", ");
    7603            0 :             simple_quote_literal(buf, stmt->payload);
    7604              :         }
    7605              :     }
    7606              :     else
    7607              :     {
    7608              :         /* Currently only NOTIFY utility commands can appear in rules */
    7609            0 :         elog(ERROR, "unexpected utility statement type");
    7610              :     }
    7611            8 : }
    7612              : 
    7613              : /*
    7614              :  * Display a Var appropriately.
    7615              :  *
    7616              :  * In some cases (currently only when recursing into an unnamed join)
    7617              :  * the Var's varlevelsup has to be interpreted with respect to a context
    7618              :  * above the current one; levelsup indicates the offset.
    7619              :  *
    7620              :  * If istoplevel is true, the Var is at the top level of a SELECT's
    7621              :  * targetlist, which means we need special treatment of whole-row Vars.
    7622              :  * Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
    7623              :  * dirty hack to prevent "tab.*" from being expanded into multiple columns.
    7624              :  * (The parser will strip the useless coercion, so no inefficiency is added in
    7625              :  * dump and reload.)  We used to print just "tab" in such cases, but that is
    7626              :  * ambiguous and will yield the wrong result if "tab" is also a plain column
    7627              :  * name in the query.
    7628              :  *
    7629              :  * Returns the attname of the Var, or NULL if the Var has no attname (because
    7630              :  * it is a whole-row Var or a subplan output reference).
    7631              :  */
    7632              : static char *
    7633        96607 : get_variable(Var *var, int levelsup, bool istoplevel, deparse_context *context)
    7634              : {
    7635        96607 :     StringInfo  buf = context->buf;
    7636              :     RangeTblEntry *rte;
    7637              :     AttrNumber  attnum;
    7638              :     int         netlevelsup;
    7639              :     deparse_namespace *dpns;
    7640              :     int         varno;
    7641              :     AttrNumber  varattno;
    7642              :     deparse_columns *colinfo;
    7643              :     char       *refname;
    7644              :     char       *attname;
    7645              :     bool        need_prefix;
    7646              : 
    7647              :     /* Find appropriate nesting depth */
    7648        96607 :     netlevelsup = var->varlevelsup + levelsup;
    7649        96607 :     if (netlevelsup >= list_length(context->namespaces))
    7650            0 :         elog(ERROR, "bogus varlevelsup: %d offset %d",
    7651              :              var->varlevelsup, levelsup);
    7652        96607 :     dpns = (deparse_namespace *) list_nth(context->namespaces,
    7653              :                                           netlevelsup);
    7654              : 
    7655              :     /*
    7656              :      * If we have a syntactic referent for the Var, and we're working from a
    7657              :      * parse tree, prefer to use the syntactic referent.  Otherwise, fall back
    7658              :      * on the semantic referent.  (Forcing use of the semantic referent when
    7659              :      * printing plan trees is a design choice that's perhaps more motivated by
    7660              :      * backwards compatibility than anything else.  But it does have the
    7661              :      * advantage of making plans more explicit.)
    7662              :      */
    7663        96607 :     if (var->varnosyn > 0 && dpns->plan == NULL)
    7664              :     {
    7665        19881 :         varno = var->varnosyn;
    7666        19881 :         varattno = var->varattnosyn;
    7667              :     }
    7668              :     else
    7669              :     {
    7670        76726 :         varno = var->varno;
    7671        76726 :         varattno = var->varattno;
    7672              :     }
    7673              : 
    7674              :     /*
    7675              :      * Try to find the relevant RTE in this rtable.  In a plan tree, it's
    7676              :      * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
    7677              :      * down into the subplans, or INDEX_VAR, which is resolved similarly. Also
    7678              :      * find the aliases previously assigned for this RTE.
    7679              :      */
    7680        96607 :     if (varno >= 1 && varno <= list_length(dpns->rtable))
    7681              :     {
    7682              :         /*
    7683              :          * We might have been asked to map child Vars to some parent relation.
    7684              :          */
    7685        70533 :         if (context->appendparents && dpns->appendrels)
    7686              :         {
    7687         1942 :             int         pvarno = varno;
    7688         1942 :             AttrNumber  pvarattno = varattno;
    7689         1942 :             AppendRelInfo *appinfo = dpns->appendrels[pvarno];
    7690         1942 :             bool        found = false;
    7691              : 
    7692              :             /* Only map up to inheritance parents, not UNION ALL appendrels */
    7693         3918 :             while (appinfo &&
    7694         2151 :                    rt_fetch(appinfo->parent_relid,
    7695         2151 :                             dpns->rtable)->rtekind == RTE_RELATION)
    7696              :             {
    7697         1976 :                 found = false;
    7698         1976 :                 if (pvarattno > 0)   /* system columns stay as-is */
    7699              :                 {
    7700         1837 :                     if (pvarattno > appinfo->num_child_cols)
    7701            0 :                         break;  /* safety check */
    7702         1837 :                     pvarattno = appinfo->parent_colnos[pvarattno - 1];
    7703         1837 :                     if (pvarattno == 0)
    7704            0 :                         break;  /* Var is local to child */
    7705              :                 }
    7706              : 
    7707         1976 :                 pvarno = appinfo->parent_relid;
    7708         1976 :                 found = true;
    7709              : 
    7710              :                 /* If the parent is itself a child, continue up. */
    7711              :                 Assert(pvarno > 0 && pvarno <= list_length(dpns->rtable));
    7712         1976 :                 appinfo = dpns->appendrels[pvarno];
    7713              :             }
    7714              : 
    7715              :             /*
    7716              :              * If we found an ancestral rel, and that rel is included in
    7717              :              * appendparents, print that column not the original one.
    7718              :              */
    7719         1942 :             if (found && bms_is_member(pvarno, context->appendparents))
    7720              :             {
    7721         1581 :                 varno = pvarno;
    7722         1581 :                 varattno = pvarattno;
    7723              :             }
    7724              :         }
    7725              : 
    7726        70533 :         rte = rt_fetch(varno, dpns->rtable);
    7727              : 
    7728              :         /* might be returning old/new column value */
    7729        70533 :         if (var->varreturningtype == VAR_RETURNING_OLD)
    7730          208 :             refname = dpns->ret_old_alias;
    7731        70325 :         else if (var->varreturningtype == VAR_RETURNING_NEW)
    7732          207 :             refname = dpns->ret_new_alias;
    7733              :         else
    7734        70118 :             refname = (char *) list_nth(dpns->rtable_names, varno - 1);
    7735              : 
    7736        70533 :         colinfo = deparse_columns_fetch(varno, dpns);
    7737        70533 :         attnum = varattno;
    7738              :     }
    7739              :     else
    7740              :     {
    7741        26074 :         resolve_special_varno((Node *) var, context,
    7742              :                               get_special_variable, NULL);
    7743        26074 :         return NULL;
    7744              :     }
    7745              : 
    7746              :     /*
    7747              :      * The planner will sometimes emit Vars referencing resjunk elements of a
    7748              :      * subquery's target list (this is currently only possible if it chooses
    7749              :      * to generate a "physical tlist" for a SubqueryScan or CteScan node).
    7750              :      * Although we prefer to print subquery-referencing Vars using the
    7751              :      * subquery's alias, that's not possible for resjunk items since they have
    7752              :      * no alias.  So in that case, drill down to the subplan and print the
    7753              :      * contents of the referenced tlist item.  This works because in a plan
    7754              :      * tree, such Vars can only occur in a SubqueryScan or CteScan node, and
    7755              :      * we'll have set dpns->inner_plan to reference the child plan node.
    7756              :      */
    7757        72904 :     if ((rte->rtekind == RTE_SUBQUERY || rte->rtekind == RTE_CTE) &&
    7758         2371 :         attnum > list_length(rte->eref->colnames) &&
    7759            1 :         dpns->inner_plan)
    7760              :     {
    7761              :         TargetEntry *tle;
    7762              :         deparse_namespace save_dpns;
    7763              : 
    7764            1 :         tle = get_tle_by_resno(dpns->inner_tlist, attnum);
    7765            1 :         if (!tle)
    7766            0 :             elog(ERROR, "invalid attnum %d for relation \"%s\"",
    7767              :                  attnum, rte->eref->aliasname);
    7768              : 
    7769              :         Assert(netlevelsup == 0);
    7770            1 :         push_child_plan(dpns, dpns->inner_plan, &save_dpns);
    7771              : 
    7772              :         /*
    7773              :          * Force parentheses because our caller probably assumed a Var is a
    7774              :          * simple expression.
    7775              :          */
    7776            1 :         if (!IsA(tle->expr, Var))
    7777            0 :             appendStringInfoChar(buf, '(');
    7778            1 :         get_rule_expr((Node *) tle->expr, context, true);
    7779            1 :         if (!IsA(tle->expr, Var))
    7780            0 :             appendStringInfoChar(buf, ')');
    7781              : 
    7782            1 :         pop_child_plan(dpns, &save_dpns);
    7783            1 :         return NULL;
    7784              :     }
    7785              : 
    7786              :     /*
    7787              :      * If it's an unnamed join, look at the expansion of the alias variable.
    7788              :      * If it's a simple reference to one of the input vars, then recursively
    7789              :      * print the name of that var instead.  When it's not a simple reference,
    7790              :      * we have to just print the unqualified join column name.  (This can only
    7791              :      * happen with "dangerous" merged columns in a JOIN USING; we took pains
    7792              :      * previously to make the unqualified column name unique in such cases.)
    7793              :      *
    7794              :      * This wouldn't work in decompiling plan trees, because we don't store
    7795              :      * joinaliasvars lists after planning; but a plan tree should never
    7796              :      * contain a join alias variable.
    7797              :      */
    7798        70532 :     if (rte->rtekind == RTE_JOIN && rte->alias == NULL)
    7799              :     {
    7800           54 :         if (rte->joinaliasvars == NIL)
    7801            0 :             elog(ERROR, "cannot decompile join alias var in plan tree");
    7802           54 :         if (attnum > 0)
    7803              :         {
    7804              :             Var        *aliasvar;
    7805              : 
    7806           54 :             aliasvar = (Var *) list_nth(rte->joinaliasvars, attnum - 1);
    7807              :             /* we intentionally don't strip implicit coercions here */
    7808           54 :             if (aliasvar && IsA(aliasvar, Var))
    7809              :             {
    7810            0 :                 return get_variable(aliasvar, var->varlevelsup + levelsup,
    7811              :                                     istoplevel, context);
    7812              :             }
    7813              :         }
    7814              : 
    7815              :         /*
    7816              :          * Unnamed join has no refname.  (Note: since it's unnamed, there is
    7817              :          * no way the user could have referenced it to create a whole-row Var
    7818              :          * for it.  So we don't have to cover that case below.)
    7819              :          */
    7820              :         Assert(refname == NULL);
    7821              :     }
    7822              : 
    7823        70532 :     if (attnum == InvalidAttrNumber)
    7824          541 :         attname = NULL;
    7825        69991 :     else if (attnum > 0)
    7826              :     {
    7827              :         /* Get column name to use from the colinfo struct */
    7828        69004 :         if (attnum > colinfo->num_cols)
    7829            0 :             elog(ERROR, "invalid attnum %d for relation \"%s\"",
    7830              :                  attnum, rte->eref->aliasname);
    7831        69004 :         attname = colinfo->colnames[attnum - 1];
    7832              : 
    7833              :         /*
    7834              :          * If we find a Var referencing a dropped column, it seems better to
    7835              :          * print something (anything) than to fail.  In general this should
    7836              :          * not happen, but it used to be possible for some cases involving
    7837              :          * functions returning named composite types, and perhaps there are
    7838              :          * still bugs out there.
    7839              :          */
    7840        69004 :         if (attname == NULL)
    7841            3 :             attname = "?dropped?column?";
    7842              :     }
    7843              :     else
    7844              :     {
    7845              :         /* System column - name is fixed, get it from the catalog */
    7846          987 :         attname = get_rte_attribute_name(rte, attnum);
    7847              :     }
    7848              : 
    7849       103735 :     need_prefix = (context->varprefix || attname == NULL ||
    7850        33203 :                    var->varreturningtype != VAR_RETURNING_DEFAULT);
    7851              : 
    7852              :     /*
    7853              :      * If we're considering a plain Var in an ORDER BY (but not GROUP BY)
    7854              :      * clause, we may need to add a table-name prefix to prevent
    7855              :      * findTargetlistEntrySQL92 from misinterpreting the name as an
    7856              :      * output-column name.  To avoid cluttering the output with unnecessary
    7857              :      * prefixes, do so only if there is a name match to a SELECT tlist item
    7858              :      * that is different from the Var.
    7859              :      */
    7860        70532 :     if (context->varInOrderBy && !context->inGroupBy && !need_prefix)
    7861              :     {
    7862          126 :         int         colno = 0;
    7863              : 
    7864          488 :         foreach_node(TargetEntry, tle, context->targetList)
    7865              :         {
    7866              :             char       *colname;
    7867              : 
    7868          242 :             if (tle->resjunk)
    7869            0 :                 continue;       /* ignore junk entries */
    7870          242 :             colno++;
    7871              : 
    7872              :             /* This must match colname-choosing logic in get_target_list() */
    7873          242 :             if (context->resultDesc && colno <= context->resultDesc->natts)
    7874          242 :                 colname = NameStr(TupleDescAttr(context->resultDesc,
    7875              :                                                 colno - 1)->attname);
    7876              :             else
    7877            0 :                 colname = tle->resname;
    7878              : 
    7879          242 :             if (colname && strcmp(colname, attname) == 0 &&
    7880           87 :                 !equal(var, tle->expr))
    7881              :             {
    7882            6 :                 need_prefix = true;
    7883            6 :                 break;
    7884              :             }
    7885              :         }
    7886              :     }
    7887              : 
    7888        70532 :     if (refname && need_prefix)
    7889              :     {
    7890        37295 :         appendStringInfoString(buf, quote_identifier(refname));
    7891        37295 :         appendStringInfoChar(buf, '.');
    7892              :     }
    7893        70532 :     if (attname)
    7894        69991 :         appendStringInfoString(buf, quote_identifier(attname));
    7895              :     else
    7896              :     {
    7897          541 :         appendStringInfoChar(buf, '*');
    7898          541 :         if (istoplevel)
    7899           42 :             appendStringInfo(buf, "::%s",
    7900              :                              format_type_with_typemod(var->vartype,
    7901              :                                                       var->vartypmod));
    7902              :     }
    7903              : 
    7904        70532 :     return attname;
    7905              : }
    7906              : 
    7907              : /*
    7908              :  * Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR.  This
    7909              :  * routine is actually a callback for resolve_special_varno, which handles
    7910              :  * finding the correct TargetEntry.  We get the expression contained in that
    7911              :  * TargetEntry and just need to deparse it, a job we can throw back on
    7912              :  * get_rule_expr.
    7913              :  */
    7914              : static void
    7915        26074 : get_special_variable(Node *node, deparse_context *context, void *callback_arg)
    7916              : {
    7917        26074 :     StringInfo  buf = context->buf;
    7918              : 
    7919              :     /*
    7920              :      * For a non-Var referent, force parentheses because our caller probably
    7921              :      * assumed a Var is a simple expression.
    7922              :      */
    7923        26074 :     if (!IsA(node, Var))
    7924         2627 :         appendStringInfoChar(buf, '(');
    7925        26074 :     get_rule_expr(node, context, true);
    7926        26074 :     if (!IsA(node, Var))
    7927         2627 :         appendStringInfoChar(buf, ')');
    7928        26074 : }
    7929              : 
    7930              : /*
    7931              :  * Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
    7932              :  * INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
    7933              :  * invoke the callback provided.
    7934              :  */
    7935              : static void
    7936        73437 : resolve_special_varno(Node *node, deparse_context *context,
    7937              :                       rsv_callback callback, void *callback_arg)
    7938              : {
    7939              :     Var        *var;
    7940              :     deparse_namespace *dpns;
    7941              : 
    7942              :     /* This function is recursive, so let's be paranoid. */
    7943        73437 :     check_stack_depth();
    7944              : 
    7945              :     /* If it's not a Var, invoke the callback. */
    7946        73437 :     if (!IsA(node, Var))
    7947              :     {
    7948         3018 :         (*callback) (node, context, callback_arg);
    7949         3018 :         return;
    7950              :     }
    7951              : 
    7952              :     /* Find appropriate nesting depth */
    7953        70419 :     var = (Var *) node;
    7954        70419 :     dpns = (deparse_namespace *) list_nth(context->namespaces,
    7955        70419 :                                           var->varlevelsup);
    7956              : 
    7957              :     /*
    7958              :      * If varno is special, recurse.  (Don't worry about varnosyn; if we're
    7959              :      * here, we already decided not to use that.)
    7960              :      */
    7961        70419 :     if (var->varno == OUTER_VAR && dpns->outer_tlist)
    7962              :     {
    7963              :         TargetEntry *tle;
    7964              :         deparse_namespace save_dpns;
    7965              :         Bitmapset  *save_appendparents;
    7966              : 
    7967        35564 :         tle = get_tle_by_resno(dpns->outer_tlist, var->varattno);
    7968        35564 :         if (!tle)
    7969            0 :             elog(ERROR, "bogus varattno for OUTER_VAR var: %d", var->varattno);
    7970              : 
    7971              :         /*
    7972              :          * If we're descending to the first child of an Append or MergeAppend,
    7973              :          * update appendparents.  This will affect deparsing of all Vars
    7974              :          * appearing within the eventually-resolved subexpression.
    7975              :          */
    7976        35564 :         save_appendparents = context->appendparents;
    7977              : 
    7978        35564 :         if (IsA(dpns->plan, Append))
    7979         2265 :             context->appendparents = bms_union(context->appendparents,
    7980         2265 :                                                ((Append *) dpns->plan)->apprelids);
    7981        33299 :         else if (IsA(dpns->plan, MergeAppend))
    7982          316 :             context->appendparents = bms_union(context->appendparents,
    7983          316 :                                                ((MergeAppend *) dpns->plan)->apprelids);
    7984              : 
    7985        35564 :         push_child_plan(dpns, dpns->outer_plan, &save_dpns);
    7986        35564 :         resolve_special_varno((Node *) tle->expr, context,
    7987              :                               callback, callback_arg);
    7988        35564 :         pop_child_plan(dpns, &save_dpns);
    7989        35564 :         context->appendparents = save_appendparents;
    7990        35564 :         return;
    7991              :     }
    7992        34855 :     else if (var->varno == INNER_VAR && dpns->inner_tlist)
    7993              :     {
    7994              :         TargetEntry *tle;
    7995              :         deparse_namespace save_dpns;
    7996              : 
    7997         8633 :         tle = get_tle_by_resno(dpns->inner_tlist, var->varattno);
    7998         8633 :         if (!tle)
    7999            0 :             elog(ERROR, "bogus varattno for INNER_VAR var: %d", var->varattno);
    8000              : 
    8001         8633 :         push_child_plan(dpns, dpns->inner_plan, &save_dpns);
    8002         8633 :         resolve_special_varno((Node *) tle->expr, context,
    8003              :                               callback, callback_arg);
    8004         8633 :         pop_child_plan(dpns, &save_dpns);
    8005         8633 :         return;
    8006              :     }
    8007        26222 :     else if (var->varno == INDEX_VAR && dpns->index_tlist)
    8008              :     {
    8009              :         TargetEntry *tle;
    8010              : 
    8011         2775 :         tle = get_tle_by_resno(dpns->index_tlist, var->varattno);
    8012         2775 :         if (!tle)
    8013            0 :             elog(ERROR, "bogus varattno for INDEX_VAR var: %d", var->varattno);
    8014              : 
    8015         2775 :         resolve_special_varno((Node *) tle->expr, context,
    8016              :                               callback, callback_arg);
    8017         2775 :         return;
    8018              :     }
    8019        23447 :     else if (var->varno < 1 || var->varno > list_length(dpns->rtable))
    8020            0 :         elog(ERROR, "bogus varno: %d", var->varno);
    8021              : 
    8022              :     /* Not special.  Just invoke the callback. */
    8023        23447 :     (*callback) (node, context, callback_arg);
    8024              : }
    8025              : 
    8026              : /*
    8027              :  * Get the name of a field of an expression of composite type.  The
    8028              :  * expression is usually a Var, but we handle other cases too.
    8029              :  *
    8030              :  * levelsup is an extra offset to interpret the Var's varlevelsup correctly.
    8031              :  *
    8032              :  * This is fairly straightforward when the expression has a named composite
    8033              :  * type; we need only look up the type in the catalogs.  However, the type
    8034              :  * could also be RECORD.  Since no actual table or view column is allowed to
    8035              :  * have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
    8036              :  * or to a subquery output.  We drill down to find the ultimate defining
    8037              :  * expression and attempt to infer the field name from it.  We ereport if we
    8038              :  * can't determine the name.
    8039              :  *
    8040              :  * Similarly, a PARAM of type RECORD has to refer to some expression of
    8041              :  * a determinable composite type.
    8042              :  */
    8043              : static const char *
    8044          865 : get_name_for_var_field(Var *var, int fieldno,
    8045              :                        int levelsup, deparse_context *context)
    8046              : {
    8047              :     RangeTblEntry *rte;
    8048              :     AttrNumber  attnum;
    8049              :     int         netlevelsup;
    8050              :     deparse_namespace *dpns;
    8051              :     int         varno;
    8052              :     AttrNumber  varattno;
    8053              :     TupleDesc   tupleDesc;
    8054              :     Node       *expr;
    8055              : 
    8056              :     /*
    8057              :      * If it's a RowExpr that was expanded from a whole-row Var, use the
    8058              :      * column names attached to it.  (We could let get_expr_result_tupdesc()
    8059              :      * handle this, but it's much cheaper to just pull out the name we need.)
    8060              :      */
    8061          865 :     if (IsA(var, RowExpr))
    8062              :     {
    8063           18 :         RowExpr    *r = (RowExpr *) var;
    8064              : 
    8065           18 :         if (fieldno > 0 && fieldno <= list_length(r->colnames))
    8066           18 :             return strVal(list_nth(r->colnames, fieldno - 1));
    8067              :     }
    8068              : 
    8069              :     /*
    8070              :      * If it's a Param of type RECORD, try to find what the Param refers to.
    8071              :      */
    8072          847 :     if (IsA(var, Param))
    8073              :     {
    8074            9 :         Param      *param = (Param *) var;
    8075              :         ListCell   *ancestor_cell;
    8076              : 
    8077            9 :         expr = find_param_referent(param, context, &dpns, &ancestor_cell);
    8078            9 :         if (expr)
    8079              :         {
    8080              :             /* Found a match, so recurse to decipher the field name */
    8081              :             deparse_namespace save_dpns;
    8082              :             const char *result;
    8083              : 
    8084            9 :             push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
    8085            9 :             result = get_name_for_var_field((Var *) expr, fieldno,
    8086              :                                             0, context);
    8087            9 :             pop_ancestor_plan(dpns, &save_dpns);
    8088            9 :             return result;
    8089              :         }
    8090              :     }
    8091              : 
    8092              :     /*
    8093              :      * If it's a Var of type RECORD, we have to find what the Var refers to;
    8094              :      * if not, we can use get_expr_result_tupdesc().
    8095              :      */
    8096          838 :     if (!IsA(var, Var) ||
    8097          798 :         var->vartype != RECORDOID)
    8098              :     {
    8099          715 :         tupleDesc = get_expr_result_tupdesc((Node *) var, false);
    8100              :         /* Got the tupdesc, so we can extract the field name */
    8101              :         Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
    8102          715 :         return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
    8103              :     }
    8104              : 
    8105              :     /* Find appropriate nesting depth */
    8106          123 :     netlevelsup = var->varlevelsup + levelsup;
    8107          123 :     if (netlevelsup >= list_length(context->namespaces))
    8108            0 :         elog(ERROR, "bogus varlevelsup: %d offset %d",
    8109              :              var->varlevelsup, levelsup);
    8110          123 :     dpns = (deparse_namespace *) list_nth(context->namespaces,
    8111              :                                           netlevelsup);
    8112              : 
    8113              :     /*
    8114              :      * If we have a syntactic referent for the Var, and we're working from a
    8115              :      * parse tree, prefer to use the syntactic referent.  Otherwise, fall back
    8116              :      * on the semantic referent.  (See comments in get_variable().)
    8117              :      */
    8118          123 :     if (var->varnosyn > 0 && dpns->plan == NULL)
    8119              :     {
    8120           48 :         varno = var->varnosyn;
    8121           48 :         varattno = var->varattnosyn;
    8122              :     }
    8123              :     else
    8124              :     {
    8125           75 :         varno = var->varno;
    8126           75 :         varattno = var->varattno;
    8127              :     }
    8128              : 
    8129              :     /*
    8130              :      * Try to find the relevant RTE in this rtable.  In a plan tree, it's
    8131              :      * likely that varno is OUTER_VAR or INNER_VAR, in which case we must dig
    8132              :      * down into the subplans, or INDEX_VAR, which is resolved similarly.
    8133              :      *
    8134              :      * Note: unlike get_variable and resolve_special_varno, we need not worry
    8135              :      * about inheritance mapping: a child Var should have the same datatype as
    8136              :      * its parent, and here we're really only interested in the Var's type.
    8137              :      */
    8138          123 :     if (varno >= 1 && varno <= list_length(dpns->rtable))
    8139              :     {
    8140           84 :         rte = rt_fetch(varno, dpns->rtable);
    8141           84 :         attnum = varattno;
    8142              :     }
    8143           39 :     else if (varno == OUTER_VAR && dpns->outer_tlist)
    8144              :     {
    8145              :         TargetEntry *tle;
    8146              :         deparse_namespace save_dpns;
    8147              :         const char *result;
    8148              : 
    8149           30 :         tle = get_tle_by_resno(dpns->outer_tlist, varattno);
    8150           30 :         if (!tle)
    8151            0 :             elog(ERROR, "bogus varattno for OUTER_VAR var: %d", varattno);
    8152              : 
    8153              :         Assert(netlevelsup == 0);
    8154           30 :         push_child_plan(dpns, dpns->outer_plan, &save_dpns);
    8155              : 
    8156           30 :         result = get_name_for_var_field((Var *) tle->expr, fieldno,
    8157              :                                         levelsup, context);
    8158              : 
    8159           30 :         pop_child_plan(dpns, &save_dpns);
    8160           30 :         return result;
    8161              :     }
    8162            9 :     else if (varno == INNER_VAR && dpns->inner_tlist)
    8163              :     {
    8164              :         TargetEntry *tle;
    8165              :         deparse_namespace save_dpns;
    8166              :         const char *result;
    8167              : 
    8168            9 :         tle = get_tle_by_resno(dpns->inner_tlist, varattno);
    8169            9 :         if (!tle)
    8170            0 :             elog(ERROR, "bogus varattno for INNER_VAR var: %d", varattno);
    8171              : 
    8172              :         Assert(netlevelsup == 0);
    8173            9 :         push_child_plan(dpns, dpns->inner_plan, &save_dpns);
    8174              : 
    8175            9 :         result = get_name_for_var_field((Var *) tle->expr, fieldno,
    8176              :                                         levelsup, context);
    8177              : 
    8178            9 :         pop_child_plan(dpns, &save_dpns);
    8179            9 :         return result;
    8180              :     }
    8181            0 :     else if (varno == INDEX_VAR && dpns->index_tlist)
    8182              :     {
    8183              :         TargetEntry *tle;
    8184              :         const char *result;
    8185              : 
    8186            0 :         tle = get_tle_by_resno(dpns->index_tlist, varattno);
    8187            0 :         if (!tle)
    8188            0 :             elog(ERROR, "bogus varattno for INDEX_VAR var: %d", varattno);
    8189              : 
    8190              :         Assert(netlevelsup == 0);
    8191              : 
    8192            0 :         result = get_name_for_var_field((Var *) tle->expr, fieldno,
    8193              :                                         levelsup, context);
    8194              : 
    8195            0 :         return result;
    8196              :     }
    8197              :     else
    8198              :     {
    8199            0 :         elog(ERROR, "bogus varno: %d", varno);
    8200              :         return NULL;            /* keep compiler quiet */
    8201              :     }
    8202              : 
    8203           84 :     if (attnum == InvalidAttrNumber)
    8204              :     {
    8205              :         /* Var is whole-row reference to RTE, so select the right field */
    8206           12 :         return get_rte_attribute_name(rte, fieldno);
    8207              :     }
    8208              : 
    8209              :     /*
    8210              :      * This part has essentially the same logic as the parser's
    8211              :      * expandRecordVariable() function, but we are dealing with a different
    8212              :      * representation of the input context, and we only need one field name
    8213              :      * not a TupleDesc.  Also, we need special cases for finding subquery and
    8214              :      * CTE subplans when deparsing Plan trees.
    8215              :      */
    8216           72 :     expr = (Node *) var;        /* default if we can't drill down */
    8217              : 
    8218           72 :     switch (rte->rtekind)
    8219              :     {
    8220            0 :         case RTE_RELATION:
    8221              :         case RTE_VALUES:
    8222              :         case RTE_NAMEDTUPLESTORE:
    8223              :         case RTE_RESULT:
    8224              : 
    8225              :             /*
    8226              :              * This case should not occur: a column of a table, values list,
    8227              :              * or ENR shouldn't have type RECORD.  Fall through and fail (most
    8228              :              * likely) at the bottom.
    8229              :              */
    8230            0 :             break;
    8231           36 :         case RTE_SUBQUERY:
    8232              :             /* Subselect-in-FROM: examine sub-select's output expr */
    8233              :             {
    8234           36 :                 if (rte->subquery)
    8235              :                 {
    8236           21 :                     TargetEntry *ste = get_tle_by_resno(rte->subquery->targetList,
    8237              :                                                         attnum);
    8238              : 
    8239           21 :                     if (ste == NULL || ste->resjunk)
    8240            0 :                         elog(ERROR, "subquery %s does not have attribute %d",
    8241              :                              rte->eref->aliasname, attnum);
    8242           21 :                     expr = (Node *) ste->expr;
    8243           21 :                     if (IsA(expr, Var))
    8244              :                     {
    8245              :                         /*
    8246              :                          * Recurse into the sub-select to see what its Var
    8247              :                          * refers to. We have to build an additional level of
    8248              :                          * namespace to keep in step with varlevelsup in the
    8249              :                          * subselect; furthermore, the subquery RTE might be
    8250              :                          * from an outer query level, in which case the
    8251              :                          * namespace for the subselect must have that outer
    8252              :                          * level as parent namespace.
    8253              :                          */
    8254            9 :                         List       *save_nslist = context->namespaces;
    8255              :                         List       *parent_namespaces;
    8256              :                         deparse_namespace mydpns;
    8257              :                         const char *result;
    8258              : 
    8259            9 :                         parent_namespaces = list_copy_tail(context->namespaces,
    8260              :                                                            netlevelsup);
    8261              : 
    8262            9 :                         set_deparse_for_query(&mydpns, rte->subquery,
    8263              :                                               parent_namespaces);
    8264              : 
    8265            9 :                         context->namespaces = lcons(&mydpns, parent_namespaces);
    8266              : 
    8267            9 :                         result = get_name_for_var_field((Var *) expr, fieldno,
    8268              :                                                         0, context);
    8269              : 
    8270            9 :                         context->namespaces = save_nslist;
    8271              : 
    8272            9 :                         return result;
    8273              :                     }
    8274              :                     /* else fall through to inspect the expression */
    8275              :                 }
    8276              :                 else
    8277              :                 {
    8278              :                     /*
    8279              :                      * We're deparsing a Plan tree so we don't have complete
    8280              :                      * RTE entries (in particular, rte->subquery is NULL). But
    8281              :                      * the only place we'd normally see a Var directly
    8282              :                      * referencing a SUBQUERY RTE is in a SubqueryScan plan
    8283              :                      * node, and we can look into the child plan's tlist
    8284              :                      * instead.  An exception occurs if the subquery was
    8285              :                      * proven empty and optimized away: then we'd find such a
    8286              :                      * Var in a childless Result node, and there's nothing in
    8287              :                      * the plan tree that would let us figure out what it had
    8288              :                      * originally referenced.  In that case, fall back on
    8289              :                      * printing "fN", analogously to the default column names
    8290              :                      * for RowExprs.
    8291              :                      */
    8292              :                     TargetEntry *tle;
    8293              :                     deparse_namespace save_dpns;
    8294              :                     const char *result;
    8295              : 
    8296           15 :                     if (!dpns->inner_plan)
    8297              :                     {
    8298            6 :                         char       *dummy_name = palloc(32);
    8299              : 
    8300              :                         Assert(dpns->plan && IsA(dpns->plan, Result));
    8301            6 :                         snprintf(dummy_name, 32, "f%d", fieldno);
    8302            6 :                         return dummy_name;
    8303              :                     }
    8304              :                     Assert(dpns->plan && IsA(dpns->plan, SubqueryScan));
    8305              : 
    8306            9 :                     tle = get_tle_by_resno(dpns->inner_tlist, attnum);
    8307            9 :                     if (!tle)
    8308            0 :                         elog(ERROR, "bogus varattno for subquery var: %d",
    8309              :                              attnum);
    8310              :                     Assert(netlevelsup == 0);
    8311            9 :                     push_child_plan(dpns, dpns->inner_plan, &save_dpns);
    8312              : 
    8313            9 :                     result = get_name_for_var_field((Var *) tle->expr, fieldno,
    8314              :                                                     levelsup, context);
    8315              : 
    8316            9 :                     pop_child_plan(dpns, &save_dpns);
    8317            9 :                     return result;
    8318              :                 }
    8319              :             }
    8320           12 :             break;
    8321            0 :         case RTE_JOIN:
    8322              :             /* Join RTE --- recursively inspect the alias variable */
    8323            0 :             if (rte->joinaliasvars == NIL)
    8324            0 :                 elog(ERROR, "cannot decompile join alias var in plan tree");
    8325              :             Assert(attnum > 0 && attnum <= list_length(rte->joinaliasvars));
    8326            0 :             expr = (Node *) list_nth(rte->joinaliasvars, attnum - 1);
    8327              :             Assert(expr != NULL);
    8328              :             /* we intentionally don't strip implicit coercions here */
    8329            0 :             if (IsA(expr, Var))
    8330            0 :                 return get_name_for_var_field((Var *) expr, fieldno,
    8331            0 :                                               var->varlevelsup + levelsup,
    8332              :                                               context);
    8333              :             /* else fall through to inspect the expression */
    8334            0 :             break;
    8335            0 :         case RTE_FUNCTION:
    8336              :         case RTE_TABLEFUNC:
    8337              : 
    8338              :             /*
    8339              :              * We couldn't get here unless a function is declared with one of
    8340              :              * its result columns as RECORD, which is not allowed.
    8341              :              */
    8342            0 :             break;
    8343           36 :         case RTE_CTE:
    8344              :             /* CTE reference: examine subquery's output expr */
    8345              :             {
    8346           36 :                 CommonTableExpr *cte = NULL;
    8347              :                 Index       ctelevelsup;
    8348              :                 ListCell   *lc;
    8349              : 
    8350              :                 /*
    8351              :                  * Try to find the referenced CTE using the namespace stack.
    8352              :                  */
    8353           36 :                 ctelevelsup = rte->ctelevelsup + netlevelsup;
    8354           36 :                 if (ctelevelsup >= list_length(context->namespaces))
    8355            6 :                     lc = NULL;
    8356              :                 else
    8357              :                 {
    8358              :                     deparse_namespace *ctedpns;
    8359              : 
    8360              :                     ctedpns = (deparse_namespace *)
    8361           30 :                         list_nth(context->namespaces, ctelevelsup);
    8362           33 :                     foreach(lc, ctedpns->ctes)
    8363              :                     {
    8364           18 :                         cte = (CommonTableExpr *) lfirst(lc);
    8365           18 :                         if (strcmp(cte->ctename, rte->ctename) == 0)
    8366           15 :                             break;
    8367              :                     }
    8368              :                 }
    8369           36 :                 if (lc != NULL)
    8370              :                 {
    8371           15 :                     Query      *ctequery = (Query *) cte->ctequery;
    8372           15 :                     TargetEntry *ste = get_tle_by_resno(GetCTETargetList(cte),
    8373              :                                                         attnum);
    8374              : 
    8375           15 :                     if (ste == NULL || ste->resjunk)
    8376            0 :                         elog(ERROR, "CTE %s does not have attribute %d",
    8377              :                              rte->eref->aliasname, attnum);
    8378           15 :                     expr = (Node *) ste->expr;
    8379           15 :                     if (IsA(expr, Var))
    8380              :                     {
    8381              :                         /*
    8382              :                          * Recurse into the CTE to see what its Var refers to.
    8383              :                          * We have to build an additional level of namespace
    8384              :                          * to keep in step with varlevelsup in the CTE;
    8385              :                          * furthermore it could be an outer CTE (compare
    8386              :                          * SUBQUERY case above).
    8387              :                          */
    8388            9 :                         List       *save_nslist = context->namespaces;
    8389              :                         List       *parent_namespaces;
    8390              :                         deparse_namespace mydpns;
    8391              :                         const char *result;
    8392              : 
    8393            9 :                         parent_namespaces = list_copy_tail(context->namespaces,
    8394              :                                                            ctelevelsup);
    8395              : 
    8396            9 :                         set_deparse_for_query(&mydpns, ctequery,
    8397              :                                               parent_namespaces);
    8398              : 
    8399            9 :                         context->namespaces = lcons(&mydpns, parent_namespaces);
    8400              : 
    8401            9 :                         result = get_name_for_var_field((Var *) expr, fieldno,
    8402              :                                                         0, context);
    8403              : 
    8404            9 :                         context->namespaces = save_nslist;
    8405              : 
    8406            9 :                         return result;
    8407              :                     }
    8408              :                     /* else fall through to inspect the expression */
    8409              :                 }
    8410              :                 else
    8411              :                 {
    8412              :                     /*
    8413              :                      * We're deparsing a Plan tree so we don't have a CTE
    8414              :                      * list.  But the only places we'd normally see a Var
    8415              :                      * directly referencing a CTE RTE are in CteScan or
    8416              :                      * WorkTableScan plan nodes.  For those cases,
    8417              :                      * set_deparse_plan arranged for dpns->inner_plan to be
    8418              :                      * the plan node that emits the CTE or RecursiveUnion
    8419              :                      * result, and we can look at its tlist instead.  As
    8420              :                      * above, this can fail if the CTE has been proven empty,
    8421              :                      * in which case fall back to "fN".
    8422              :                      */
    8423              :                     TargetEntry *tle;
    8424              :                     deparse_namespace save_dpns;
    8425              :                     const char *result;
    8426              : 
    8427           21 :                     if (!dpns->inner_plan)
    8428              :                     {
    8429            3 :                         char       *dummy_name = palloc(32);
    8430              : 
    8431              :                         Assert(dpns->plan && IsA(dpns->plan, Result));
    8432            3 :                         snprintf(dummy_name, 32, "f%d", fieldno);
    8433            3 :                         return dummy_name;
    8434              :                     }
    8435              :                     Assert(dpns->plan && (IsA(dpns->plan, CteScan) ||
    8436              :                                           IsA(dpns->plan, WorkTableScan)));
    8437              : 
    8438           18 :                     tle = get_tle_by_resno(dpns->inner_tlist, attnum);
    8439           18 :                     if (!tle)
    8440            0 :                         elog(ERROR, "bogus varattno for subquery var: %d",
    8441              :                              attnum);
    8442              :                     Assert(netlevelsup == 0);
    8443           18 :                     push_child_plan(dpns, dpns->inner_plan, &save_dpns);
    8444              : 
    8445           18 :                     result = get_name_for_var_field((Var *) tle->expr, fieldno,
    8446              :                                                     levelsup, context);
    8447              : 
    8448           18 :                     pop_child_plan(dpns, &save_dpns);
    8449           18 :                     return result;
    8450              :                 }
    8451              :             }
    8452            6 :             break;
    8453            0 :         case RTE_GROUP:
    8454              : 
    8455              :             /*
    8456              :              * We couldn't get here: any Vars that reference the RTE_GROUP RTE
    8457              :              * should have been replaced with the underlying grouping
    8458              :              * expressions.
    8459              :              */
    8460            0 :             break;
    8461              :     }
    8462              : 
    8463              :     /*
    8464              :      * We now have an expression we can't expand any more, so see if
    8465              :      * get_expr_result_tupdesc() can do anything with it.
    8466              :      */
    8467           18 :     tupleDesc = get_expr_result_tupdesc(expr, false);
    8468              :     /* Got the tupdesc, so we can extract the field name */
    8469              :     Assert(fieldno >= 1 && fieldno <= tupleDesc->natts);
    8470           18 :     return NameStr(TupleDescAttr(tupleDesc, fieldno - 1)->attname);
    8471              : }
    8472              : 
    8473              : /*
    8474              :  * Try to find the referenced expression for a PARAM_EXEC Param that might
    8475              :  * reference a parameter supplied by an upper NestLoop or SubPlan plan node.
    8476              :  *
    8477              :  * If successful, return the expression and set *dpns_p and *ancestor_cell_p
    8478              :  * appropriately for calling push_ancestor_plan().  If no referent can be
    8479              :  * found, return NULL.
    8480              :  */
    8481              : static Node *
    8482         3659 : find_param_referent(Param *param, deparse_context *context,
    8483              :                     deparse_namespace **dpns_p, ListCell **ancestor_cell_p)
    8484              : {
    8485              :     /* Initialize output parameters to prevent compiler warnings */
    8486         3659 :     *dpns_p = NULL;
    8487         3659 :     *ancestor_cell_p = NULL;
    8488              : 
    8489              :     /*
    8490              :      * If it's a PARAM_EXEC parameter, look for a matching NestLoopParam or
    8491              :      * SubPlan argument.  This will necessarily be in some ancestor of the
    8492              :      * current expression's Plan node.
    8493              :      */
    8494         3659 :     if (param->paramkind == PARAM_EXEC)
    8495              :     {
    8496              :         deparse_namespace *dpns;
    8497              :         Plan       *child_plan;
    8498              :         ListCell   *lc;
    8499              : 
    8500         3213 :         dpns = (deparse_namespace *) linitial(context->namespaces);
    8501         3213 :         child_plan = dpns->plan;
    8502              : 
    8503         5684 :         foreach(lc, dpns->ancestors)
    8504              :         {
    8505         4797 :             Node       *ancestor = (Node *) lfirst(lc);
    8506              :             ListCell   *lc2;
    8507              : 
    8508              :             /*
    8509              :              * NestLoops transmit params to their inner child only.
    8510              :              */
    8511         4797 :             if (IsA(ancestor, NestLoop) &&
    8512         2176 :                 child_plan == innerPlan(ancestor))
    8513              :             {
    8514         2084 :                 NestLoop   *nl = (NestLoop *) ancestor;
    8515              : 
    8516         2583 :                 foreach(lc2, nl->nestParams)
    8517              :                 {
    8518         2494 :                     NestLoopParam *nlp = (NestLoopParam *) lfirst(lc2);
    8519              : 
    8520         2494 :                     if (nlp->paramno == param->paramid)
    8521              :                     {
    8522              :                         /* Found a match, so return it */
    8523         1995 :                         *dpns_p = dpns;
    8524         1995 :                         *ancestor_cell_p = lc;
    8525         1995 :                         return (Node *) nlp->paramval;
    8526              :                     }
    8527              :                 }
    8528              :             }
    8529              : 
    8530              :             /*
    8531              :              * If ancestor is a SubPlan, check the arguments it provides.
    8532              :              */
    8533         2802 :             if (IsA(ancestor, SubPlan))
    8534          216 :             {
    8535          547 :                 SubPlan    *subplan = (SubPlan *) ancestor;
    8536              :                 ListCell   *lc3;
    8537              :                 ListCell   *lc4;
    8538              : 
    8539          715 :                 forboth(lc3, subplan->parParam, lc4, subplan->args)
    8540              :                 {
    8541          499 :                     int         paramid = lfirst_int(lc3);
    8542          499 :                     Node       *arg = (Node *) lfirst(lc4);
    8543              : 
    8544          499 :                     if (paramid == param->paramid)
    8545              :                     {
    8546              :                         /*
    8547              :                          * Found a match, so return it.  But, since Vars in
    8548              :                          * the arg are to be evaluated in the surrounding
    8549              :                          * context, we have to point to the next ancestor item
    8550              :                          * that is *not* a SubPlan.
    8551              :                          */
    8552              :                         ListCell   *rest;
    8553              : 
    8554          331 :                         for_each_cell(rest, dpns->ancestors,
    8555              :                                       lnext(dpns->ancestors, lc))
    8556              :                         {
    8557          331 :                             Node       *ancestor2 = (Node *) lfirst(rest);
    8558              : 
    8559          331 :                             if (!IsA(ancestor2, SubPlan))
    8560              :                             {
    8561          331 :                                 *dpns_p = dpns;
    8562          331 :                                 *ancestor_cell_p = rest;
    8563          331 :                                 return arg;
    8564              :                             }
    8565              :                         }
    8566            0 :                         elog(ERROR, "SubPlan cannot be outermost ancestor");
    8567              :                     }
    8568              :                 }
    8569              : 
    8570              :                 /* SubPlan isn't a kind of Plan, so skip the rest */
    8571          216 :                 continue;
    8572              :             }
    8573              : 
    8574              :             /*
    8575              :              * We need not consider the ancestor's initPlan list, since
    8576              :              * initplans never have any parParams.
    8577              :              */
    8578              : 
    8579              :             /* No luck, crawl up to next ancestor */
    8580         2255 :             child_plan = (Plan *) ancestor;
    8581              :         }
    8582              :     }
    8583              : 
    8584              :     /* No referent found */
    8585         1333 :     return NULL;
    8586              : }
    8587              : 
    8588              : /*
    8589              :  * Try to find a subplan/initplan that emits the value for a PARAM_EXEC Param.
    8590              :  *
    8591              :  * If successful, return the generating subplan/initplan and set *column_p
    8592              :  * to the subplan's 0-based output column number.
    8593              :  * Otherwise, return NULL.
    8594              :  */
    8595              : static SubPlan *
    8596         1333 : find_param_generator(Param *param, deparse_context *context, int *column_p)
    8597              : {
    8598              :     /* Initialize output parameter to prevent compiler warnings */
    8599         1333 :     *column_p = 0;
    8600              : 
    8601              :     /*
    8602              :      * If it's a PARAM_EXEC parameter, search the current plan node as well as
    8603              :      * ancestor nodes looking for a subplan or initplan that emits the value
    8604              :      * for the Param.  It could appear in the setParams of an initplan or
    8605              :      * MULTIEXPR_SUBLINK subplan, or in the paramIds of an ancestral SubPlan.
    8606              :      */
    8607         1333 :     if (param->paramkind == PARAM_EXEC)
    8608              :     {
    8609              :         SubPlan    *result;
    8610              :         deparse_namespace *dpns;
    8611              :         ListCell   *lc;
    8612              : 
    8613          887 :         dpns = (deparse_namespace *) linitial(context->namespaces);
    8614              : 
    8615              :         /* First check the innermost plan node's initplans */
    8616          887 :         result = find_param_generator_initplan(param, dpns->plan, column_p);
    8617          887 :         if (result)
    8618          264 :             return result;
    8619              : 
    8620              :         /*
    8621              :          * The plan's targetlist might contain MULTIEXPR_SUBLINK SubPlans,
    8622              :          * which can be referenced by Params elsewhere in the targetlist.
    8623              :          * (Such Params should always be in the same targetlist, so there's no
    8624              :          * need to do this work at upper plan nodes.)
    8625              :          */
    8626         3164 :         foreach_node(TargetEntry, tle, dpns->plan->targetlist)
    8627              :         {
    8628         1970 :             if (tle->expr && IsA(tle->expr, SubPlan))
    8629              :             {
    8630           50 :                 SubPlan    *subplan = (SubPlan *) tle->expr;
    8631              : 
    8632           50 :                 if (subplan->subLinkType == MULTIEXPR_SUBLINK)
    8633              :                 {
    8634           39 :                     foreach_int(paramid, subplan->setParam)
    8635              :                     {
    8636           39 :                         if (paramid == param->paramid)
    8637              :                         {
    8638              :                             /* Found a match, so return it. */
    8639           26 :                             *column_p = foreach_current_index(paramid);
    8640           26 :                             return subplan;
    8641              :                         }
    8642              :                     }
    8643              :                 }
    8644              :             }
    8645              :         }
    8646              : 
    8647              :         /* No luck, so check the ancestor nodes */
    8648          774 :         foreach(lc, dpns->ancestors)
    8649              :         {
    8650          774 :             Node       *ancestor = (Node *) lfirst(lc);
    8651              : 
    8652              :             /*
    8653              :              * If ancestor is a SubPlan, check the paramIds it provides.
    8654              :              */
    8655          774 :             if (IsA(ancestor, SubPlan))
    8656            0 :             {
    8657          144 :                 SubPlan    *subplan = (SubPlan *) ancestor;
    8658              : 
    8659          163 :                 foreach_int(paramid, subplan->paramIds)
    8660              :                 {
    8661          163 :                     if (paramid == param->paramid)
    8662              :                     {
    8663              :                         /* Found a match, so return it. */
    8664          144 :                         *column_p = foreach_current_index(paramid);
    8665          144 :                         return subplan;
    8666              :                     }
    8667              :                 }
    8668              : 
    8669              :                 /* SubPlan isn't a kind of Plan, so skip the rest */
    8670            0 :                 continue;
    8671              :             }
    8672              : 
    8673              :             /*
    8674              :              * Otherwise, it's some kind of Plan node, so check its initplans.
    8675              :              */
    8676          630 :             result = find_param_generator_initplan(param, (Plan *) ancestor,
    8677              :                                                    column_p);
    8678          630 :             if (result)
    8679          453 :                 return result;
    8680              : 
    8681              :             /* No luck, crawl up to next ancestor */
    8682              :         }
    8683              :     }
    8684              : 
    8685              :     /* No generator found */
    8686          446 :     return NULL;
    8687              : }
    8688              : 
    8689              : /*
    8690              :  * Subroutine for find_param_generator: search one Plan node's initplans
    8691              :  */
    8692              : static SubPlan *
    8693         1517 : find_param_generator_initplan(Param *param, Plan *plan, int *column_p)
    8694              : {
    8695         2389 :     foreach_node(SubPlan, subplan, plan->initPlan)
    8696              :     {
    8697          936 :         foreach_int(paramid, subplan->setParam)
    8698              :         {
    8699          792 :             if (paramid == param->paramid)
    8700              :             {
    8701              :                 /* Found a match, so return it. */
    8702          717 :                 *column_p = foreach_current_index(paramid);
    8703          717 :                 return subplan;
    8704              :             }
    8705              :         }
    8706              :     }
    8707          800 :     return NULL;
    8708              : }
    8709              : 
    8710              : /*
    8711              :  * Display a Param appropriately.
    8712              :  */
    8713              : static void
    8714         3650 : get_parameter(Param *param, deparse_context *context)
    8715              : {
    8716              :     Node       *expr;
    8717              :     deparse_namespace *dpns;
    8718              :     ListCell   *ancestor_cell;
    8719              :     SubPlan    *subplan;
    8720              :     int         column;
    8721              : 
    8722              :     /*
    8723              :      * If it's a PARAM_EXEC parameter, try to locate the expression from which
    8724              :      * the parameter was computed.  This stanza handles only cases in which
    8725              :      * the Param represents an input to the subplan we are currently in.
    8726              :      */
    8727         3650 :     expr = find_param_referent(param, context, &dpns, &ancestor_cell);
    8728         3650 :     if (expr)
    8729              :     {
    8730              :         /* Found a match, so print it */
    8731              :         deparse_namespace save_dpns;
    8732              :         bool        save_varprefix;
    8733              :         bool        need_paren;
    8734              : 
    8735              :         /* Switch attention to the ancestor plan node */
    8736         2317 :         push_ancestor_plan(dpns, ancestor_cell, &save_dpns);
    8737              : 
    8738              :         /*
    8739              :          * Force prefixing of Vars, since they won't belong to the relation
    8740              :          * being scanned in the original plan node.
    8741              :          */
    8742         2317 :         save_varprefix = context->varprefix;
    8743         2317 :         context->varprefix = true;
    8744              : 
    8745              :         /*
    8746              :          * A Param's expansion is typically a Var, Aggref, GroupingFunc, or
    8747              :          * upper-level Param, which wouldn't need extra parentheses.
    8748              :          * Otherwise, insert parens to ensure the expression looks atomic.
    8749              :          */
    8750         2329 :         need_paren = !(IsA(expr, Var) ||
    8751           12 :                        IsA(expr, Aggref) ||
    8752            9 :                        IsA(expr, GroupingFunc) ||
    8753            6 :                        IsA(expr, Param));
    8754         2317 :         if (need_paren)
    8755            0 :             appendStringInfoChar(context->buf, '(');
    8756              : 
    8757         2317 :         get_rule_expr(expr, context, false);
    8758              : 
    8759         2317 :         if (need_paren)
    8760            0 :             appendStringInfoChar(context->buf, ')');
    8761              : 
    8762         2317 :         context->varprefix = save_varprefix;
    8763              : 
    8764         2317 :         pop_ancestor_plan(dpns, &save_dpns);
    8765              : 
    8766         2317 :         return;
    8767              :     }
    8768              : 
    8769              :     /*
    8770              :      * Alternatively, maybe it's a subplan output, which we print as a
    8771              :      * reference to the subplan.  (We could drill down into the subplan and
    8772              :      * print the relevant targetlist expression, but that has been deemed too
    8773              :      * confusing since it would violate normal SQL scope rules.  Also, we're
    8774              :      * relying on this reference to show that the testexpr containing the
    8775              :      * Param has anything to do with that subplan at all.)
    8776              :      */
    8777         1333 :     subplan = find_param_generator(param, context, &column);
    8778         1333 :     if (subplan)
    8779              :     {
    8780              :         const char *nameprefix;
    8781              : 
    8782          887 :         if (subplan->isInitPlan)
    8783          717 :             nameprefix = "InitPlan ";
    8784              :         else
    8785          170 :             nameprefix = "SubPlan ";
    8786              : 
    8787          887 :         appendStringInfo(context->buf, "(%s%s%s).col%d",
    8788          887 :                          subplan->useHashTable ? "hashed " : "",
    8789              :                          nameprefix,
    8790              :                          subplan->plan_name, column + 1);
    8791              : 
    8792          887 :         return;
    8793              :     }
    8794              : 
    8795              :     /*
    8796              :      * If it's an external parameter, see if the outermost namespace provides
    8797              :      * function argument names.
    8798              :      */
    8799          446 :     if (param->paramkind == PARAM_EXTERN && context->namespaces != NIL)
    8800              :     {
    8801          446 :         dpns = llast(context->namespaces);
    8802          446 :         if (dpns->argnames &&
    8803           34 :             param->paramid > 0 &&
    8804           34 :             param->paramid <= dpns->numargs)
    8805              :         {
    8806           34 :             char       *argname = dpns->argnames[param->paramid - 1];
    8807              : 
    8808           34 :             if (argname)
    8809              :             {
    8810           34 :                 bool        should_qualify = false;
    8811              :                 ListCell   *lc;
    8812              : 
    8813              :                 /*
    8814              :                  * Qualify the parameter name if there are any other deparse
    8815              :                  * namespaces with range tables.  This avoids qualifying in
    8816              :                  * trivial cases like "RETURN a + b", but makes it safe in all
    8817              :                  * other cases.
    8818              :                  */
    8819           78 :                 foreach(lc, context->namespaces)
    8820              :                 {
    8821           59 :                     deparse_namespace *depns = lfirst(lc);
    8822              : 
    8823           59 :                     if (depns->rtable_names != NIL)
    8824              :                     {
    8825           15 :                         should_qualify = true;
    8826           15 :                         break;
    8827              :                     }
    8828              :                 }
    8829           34 :                 if (should_qualify)
    8830              :                 {
    8831           15 :                     appendStringInfoString(context->buf, quote_identifier(dpns->funcname));
    8832           15 :                     appendStringInfoChar(context->buf, '.');
    8833              :                 }
    8834              : 
    8835           34 :                 appendStringInfoString(context->buf, quote_identifier(argname));
    8836           34 :                 return;
    8837              :             }
    8838              :         }
    8839              :     }
    8840              : 
    8841              :     /*
    8842              :      * Not PARAM_EXEC, or couldn't find referent: just print $N.
    8843              :      *
    8844              :      * It's a bug if we get here for anything except PARAM_EXTERN Params, but
    8845              :      * in production builds printing $N seems more useful than failing.
    8846              :      */
    8847              :     Assert(param->paramkind == PARAM_EXTERN);
    8848              : 
    8849          412 :     appendStringInfo(context->buf, "$%d", param->paramid);
    8850              : }
    8851              : 
    8852              : /*
    8853              :  * get_simple_binary_op_name
    8854              :  *
    8855              :  * helper function for isSimpleNode
    8856              :  * will return single char binary operator name, or NULL if it's not
    8857              :  */
    8858              : static const char *
    8859           75 : get_simple_binary_op_name(OpExpr *expr)
    8860              : {
    8861           75 :     List       *args = expr->args;
    8862              : 
    8863           75 :     if (list_length(args) == 2)
    8864              :     {
    8865              :         /* binary operator */
    8866           75 :         Node       *arg1 = (Node *) linitial(args);
    8867           75 :         Node       *arg2 = (Node *) lsecond(args);
    8868              :         const char *op;
    8869              : 
    8870           75 :         op = generate_operator_name(expr->opno, exprType(arg1), exprType(arg2));
    8871           75 :         if (strlen(op) == 1)
    8872           75 :             return op;
    8873              :     }
    8874            0 :     return NULL;
    8875              : }
    8876              : 
    8877              : 
    8878              : /*
    8879              :  * isSimpleNode - check if given node is simple (doesn't need parenthesizing)
    8880              :  *
    8881              :  *  true   : simple in the context of parent node's type
    8882              :  *  false  : not simple
    8883              :  */
    8884              : static bool
    8885         2953 : isSimpleNode(Node *node, Node *parentNode, int prettyFlags)
    8886              : {
    8887         2953 :     if (!node)
    8888            0 :         return false;
    8889              : 
    8890         2953 :     switch (nodeTag(node))
    8891              :     {
    8892         2501 :         case T_Var:
    8893              :         case T_Const:
    8894              :         case T_Param:
    8895              :         case T_CoerceToDomainValue:
    8896              :         case T_SetToDefault:
    8897              :         case T_CurrentOfExpr:
    8898              :             /* single words: always simple */
    8899         2501 :             return true;
    8900              : 
    8901          248 :         case T_SubscriptingRef:
    8902              :         case T_ArrayExpr:
    8903              :         case T_RowExpr:
    8904              :         case T_CoalesceExpr:
    8905              :         case T_MinMaxExpr:
    8906              :         case T_SQLValueFunction:
    8907              :         case T_XmlExpr:
    8908              :         case T_NextValueExpr:
    8909              :         case T_NullIfExpr:
    8910              :         case T_Aggref:
    8911              :         case T_GroupingFunc:
    8912              :         case T_WindowFunc:
    8913              :         case T_MergeSupportFunc:
    8914              :         case T_FuncExpr:
    8915              :         case T_JsonConstructorExpr:
    8916              :         case T_JsonExpr:
    8917              :             /* function-like: name(..) or name[..] */
    8918          248 :             return true;
    8919              : 
    8920              :             /* CASE keywords act as parentheses */
    8921            0 :         case T_CaseExpr:
    8922            0 :             return true;
    8923              : 
    8924           36 :         case T_FieldSelect:
    8925              : 
    8926              :             /*
    8927              :              * appears simple since . has top precedence, unless parent is
    8928              :              * T_FieldSelect itself!
    8929              :              */
    8930           36 :             return !IsA(parentNode, FieldSelect);
    8931              : 
    8932            0 :         case T_FieldStore:
    8933              : 
    8934              :             /*
    8935              :              * treat like FieldSelect (probably doesn't matter)
    8936              :              */
    8937            0 :             return !IsA(parentNode, FieldStore);
    8938              : 
    8939            0 :         case T_CoerceToDomain:
    8940              :             /* maybe simple, check args */
    8941            0 :             return isSimpleNode((Node *) ((CoerceToDomain *) node)->arg,
    8942              :                                 node, prettyFlags);
    8943            9 :         case T_RelabelType:
    8944            9 :             return isSimpleNode((Node *) ((RelabelType *) node)->arg,
    8945              :                                 node, prettyFlags);
    8946            0 :         case T_CoerceViaIO:
    8947            0 :             return isSimpleNode((Node *) ((CoerceViaIO *) node)->arg,
    8948              :                                 node, prettyFlags);
    8949            0 :         case T_ArrayCoerceExpr:
    8950            0 :             return isSimpleNode((Node *) ((ArrayCoerceExpr *) node)->arg,
    8951              :                                 node, prettyFlags);
    8952            0 :         case T_ConvertRowtypeExpr:
    8953            0 :             return isSimpleNode((Node *) ((ConvertRowtypeExpr *) node)->arg,
    8954              :                                 node, prettyFlags);
    8955            0 :         case T_ReturningExpr:
    8956            0 :             return isSimpleNode((Node *) ((ReturningExpr *) node)->retexpr,
    8957              :                                 node, prettyFlags);
    8958              : 
    8959          138 :         case T_OpExpr:
    8960              :             {
    8961              :                 /* depends on parent node type; needs further checking */
    8962          138 :                 if (prettyFlags & PRETTYFLAG_PAREN && IsA(parentNode, OpExpr))
    8963              :                 {
    8964              :                     const char *op;
    8965              :                     const char *parentOp;
    8966              :                     bool        is_lopriop;
    8967              :                     bool        is_hipriop;
    8968              :                     bool        is_lopriparent;
    8969              :                     bool        is_hipriparent;
    8970              : 
    8971           39 :                     op = get_simple_binary_op_name((OpExpr *) node);
    8972           39 :                     if (!op)
    8973            0 :                         return false;
    8974              : 
    8975              :                     /* We know only the basic operators + - and * / % */
    8976           39 :                     is_lopriop = (strchr("+-", *op) != NULL);
    8977           39 :                     is_hipriop = (strchr("*/%", *op) != NULL);
    8978           39 :                     if (!(is_lopriop || is_hipriop))
    8979            3 :                         return false;
    8980              : 
    8981           36 :                     parentOp = get_simple_binary_op_name((OpExpr *) parentNode);
    8982           36 :                     if (!parentOp)
    8983            0 :                         return false;
    8984              : 
    8985           36 :                     is_lopriparent = (strchr("+-", *parentOp) != NULL);
    8986           36 :                     is_hipriparent = (strchr("*/%", *parentOp) != NULL);
    8987           36 :                     if (!(is_lopriparent || is_hipriparent))
    8988            0 :                         return false;
    8989              : 
    8990           36 :                     if (is_hipriop && is_lopriparent)
    8991            6 :                         return true;    /* op binds tighter than parent */
    8992              : 
    8993           30 :                     if (is_lopriop && is_hipriparent)
    8994           24 :                         return false;
    8995              : 
    8996              :                     /*
    8997              :                      * Operators are same priority --- can skip parens only if
    8998              :                      * we have (a - b) - c, not a - (b - c).
    8999              :                      */
    9000            6 :                     if (node == (Node *) linitial(((OpExpr *) parentNode)->args))
    9001            3 :                         return true;
    9002              : 
    9003            3 :                     return false;
    9004              :                 }
    9005              :                 /* else do the same stuff as for T_SubLink et al. */
    9006              :             }
    9007              :             pg_fallthrough;
    9008              : 
    9009              :         case T_SubLink:
    9010              :         case T_NullTest:
    9011              :         case T_BooleanTest:
    9012              :         case T_DistinctExpr:
    9013              :         case T_JsonIsPredicate:
    9014          108 :             switch (nodeTag(parentNode))
    9015              :             {
    9016           30 :                 case T_FuncExpr:
    9017              :                     {
    9018              :                         /* special handling for casts and COERCE_SQL_SYNTAX */
    9019           30 :                         CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
    9020              : 
    9021           30 :                         if (type == COERCE_EXPLICIT_CAST ||
    9022            3 :                             type == COERCE_IMPLICIT_CAST ||
    9023              :                             type == COERCE_SQL_SYNTAX)
    9024           30 :                             return false;
    9025            0 :                         return true;    /* own parentheses */
    9026              :                     }
    9027           63 :                 case T_BoolExpr:    /* lower precedence */
    9028              :                 case T_SubscriptingRef: /* other separators */
    9029              :                 case T_ArrayExpr:   /* other separators */
    9030              :                 case T_RowExpr: /* other separators */
    9031              :                 case T_CoalesceExpr:    /* own parentheses */
    9032              :                 case T_MinMaxExpr:  /* own parentheses */
    9033              :                 case T_XmlExpr: /* own parentheses */
    9034              :                 case T_NullIfExpr:  /* other separators */
    9035              :                 case T_Aggref:  /* own parentheses */
    9036              :                 case T_GroupingFunc:    /* own parentheses */
    9037              :                 case T_WindowFunc:  /* own parentheses */
    9038              :                 case T_CaseExpr:    /* other separators */
    9039           63 :                     return true;
    9040           15 :                 default:
    9041           15 :                     return false;
    9042              :             }
    9043              : 
    9044            9 :         case T_BoolExpr:
    9045            9 :             switch (nodeTag(parentNode))
    9046              :             {
    9047            9 :                 case T_BoolExpr:
    9048            9 :                     if (prettyFlags & PRETTYFLAG_PAREN)
    9049              :                     {
    9050              :                         BoolExprType type;
    9051              :                         BoolExprType parentType;
    9052              : 
    9053            9 :                         type = ((BoolExpr *) node)->boolop;
    9054            9 :                         parentType = ((BoolExpr *) parentNode)->boolop;
    9055            9 :                         switch (type)
    9056              :                         {
    9057            6 :                             case NOT_EXPR:
    9058              :                             case AND_EXPR:
    9059            6 :                                 if (parentType == AND_EXPR || parentType == OR_EXPR)
    9060            6 :                                     return true;
    9061            0 :                                 break;
    9062            3 :                             case OR_EXPR:
    9063            3 :                                 if (parentType == OR_EXPR)
    9064            0 :                                     return true;
    9065            3 :                                 break;
    9066              :                         }
    9067              :                     }
    9068            3 :                     return false;
    9069            0 :                 case T_FuncExpr:
    9070              :                     {
    9071              :                         /* special handling for casts and COERCE_SQL_SYNTAX */
    9072            0 :                         CoercionForm type = ((FuncExpr *) parentNode)->funcformat;
    9073              : 
    9074            0 :                         if (type == COERCE_EXPLICIT_CAST ||
    9075            0 :                             type == COERCE_IMPLICIT_CAST ||
    9076              :                             type == COERCE_SQL_SYNTAX)
    9077            0 :                             return false;
    9078            0 :                         return true;    /* own parentheses */
    9079              :                     }
    9080            0 :                 case T_SubscriptingRef: /* other separators */
    9081              :                 case T_ArrayExpr:   /* other separators */
    9082              :                 case T_RowExpr: /* other separators */
    9083              :                 case T_CoalesceExpr:    /* own parentheses */
    9084              :                 case T_MinMaxExpr:  /* own parentheses */
    9085              :                 case T_XmlExpr: /* own parentheses */
    9086              :                 case T_NullIfExpr:  /* other separators */
    9087              :                 case T_Aggref:  /* own parentheses */
    9088              :                 case T_GroupingFunc:    /* own parentheses */
    9089              :                 case T_WindowFunc:  /* own parentheses */
    9090              :                 case T_CaseExpr:    /* other separators */
    9091              :                 case T_JsonExpr:    /* own parentheses */
    9092            0 :                     return true;
    9093            0 :                 default:
    9094            0 :                     return false;
    9095              :             }
    9096              : 
    9097            0 :         case T_JsonValueExpr:
    9098              :             /* maybe simple, check args */
    9099            0 :             return isSimpleNode((Node *) ((JsonValueExpr *) node)->raw_expr,
    9100              :                                 node, prettyFlags);
    9101              : 
    9102            3 :         default:
    9103            3 :             break;
    9104              :     }
    9105              :     /* those we don't know: in dubio complexo */
    9106            3 :     return false;
    9107              : }
    9108              : 
    9109              : 
    9110              : /*
    9111              :  * appendContextKeyword - append a keyword to buffer
    9112              :  *
    9113              :  * If prettyPrint is enabled, perform a line break, and adjust indentation.
    9114              :  * Otherwise, just append the keyword.
    9115              :  */
    9116              : static void
    9117        15522 : appendContextKeyword(deparse_context *context, const char *str,
    9118              :                      int indentBefore, int indentAfter, int indentPlus)
    9119              : {
    9120        15522 :     StringInfo  buf = context->buf;
    9121              : 
    9122        15522 :     if (PRETTY_INDENT(context))
    9123              :     {
    9124              :         int         indentAmount;
    9125              : 
    9126        15040 :         context->indentLevel += indentBefore;
    9127              : 
    9128              :         /* remove any trailing spaces currently in the buffer ... */
    9129        15040 :         removeStringInfoSpaces(buf);
    9130              :         /* ... then add a newline and some spaces */
    9131        15040 :         appendStringInfoChar(buf, '\n');
    9132              : 
    9133        15040 :         if (context->indentLevel < PRETTYINDENT_LIMIT)
    9134        15040 :             indentAmount = Max(context->indentLevel, 0) + indentPlus;
    9135              :         else
    9136              :         {
    9137              :             /*
    9138              :              * If we're indented more than PRETTYINDENT_LIMIT characters, try
    9139              :              * to conserve horizontal space by reducing the per-level
    9140              :              * indentation.  For best results the scale factor here should
    9141              :              * divide all the indent amounts that get added to indentLevel
    9142              :              * (PRETTYINDENT_STD, etc).  It's important that the indentation
    9143              :              * not grow unboundedly, else deeply-nested trees use O(N^2)
    9144              :              * whitespace; so we also wrap modulo PRETTYINDENT_LIMIT.
    9145              :              */
    9146            0 :             indentAmount = PRETTYINDENT_LIMIT +
    9147            0 :                 (context->indentLevel - PRETTYINDENT_LIMIT) /
    9148              :                 (PRETTYINDENT_STD / 2);
    9149            0 :             indentAmount %= PRETTYINDENT_LIMIT;
    9150              :             /* scale/wrap logic affects indentLevel, but not indentPlus */
    9151            0 :             indentAmount += indentPlus;
    9152              :         }
    9153        15040 :         appendStringInfoSpaces(buf, indentAmount);
    9154              : 
    9155        15040 :         appendStringInfoString(buf, str);
    9156              : 
    9157        15040 :         context->indentLevel += indentAfter;
    9158        15040 :         if (context->indentLevel < 0)
    9159            0 :             context->indentLevel = 0;
    9160              :     }
    9161              :     else
    9162          482 :         appendStringInfoString(buf, str);
    9163        15522 : }
    9164              : 
    9165              : /*
    9166              :  * removeStringInfoSpaces - delete trailing spaces from a buffer.
    9167              :  *
    9168              :  * Possibly this should move to stringinfo.c at some point.
    9169              :  */
    9170              : static void
    9171        15319 : removeStringInfoSpaces(StringInfo str)
    9172              : {
    9173        23952 :     while (str->len > 0 && str->data[str->len - 1] == ' ')
    9174         8633 :         str->data[--(str->len)] = '\0';
    9175        15319 : }
    9176              : 
    9177              : 
    9178              : /*
    9179              :  * get_rule_expr_paren  - deparse expr using get_rule_expr,
    9180              :  * embracing the string with parentheses if necessary for prettyPrint.
    9181              :  *
    9182              :  * Never embrace if prettyFlags=0, because it's done in the calling node.
    9183              :  *
    9184              :  * Any node that does *not* embrace its argument node by sql syntax (with
    9185              :  * parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
    9186              :  * use get_rule_expr_paren instead of get_rule_expr so parentheses can be
    9187              :  * added.
    9188              :  */
    9189              : static void
    9190        83577 : get_rule_expr_paren(Node *node, deparse_context *context,
    9191              :                     bool showimplicit, Node *parentNode)
    9192              : {
    9193              :     bool        need_paren;
    9194              : 
    9195        86521 :     need_paren = PRETTY_PAREN(context) &&
    9196         2944 :         !isSimpleNode(node, parentNode, context->prettyFlags);
    9197              : 
    9198        83577 :     if (need_paren)
    9199           81 :         appendStringInfoChar(context->buf, '(');
    9200              : 
    9201        83577 :     get_rule_expr(node, context, showimplicit);
    9202              : 
    9203        83577 :     if (need_paren)
    9204           81 :         appendStringInfoChar(context->buf, ')');
    9205        83577 : }
    9206              : 
    9207              : static void
    9208           42 : get_json_behavior(JsonBehavior *behavior, deparse_context *context,
    9209              :                   const char *on)
    9210              : {
    9211              :     /*
    9212              :      * The order of array elements must correspond to the order of
    9213              :      * JsonBehaviorType members.
    9214              :      */
    9215           42 :     const char *behavior_names[] =
    9216              :     {
    9217              :         " NULL",
    9218              :         " ERROR",
    9219              :         " EMPTY",
    9220              :         " TRUE",
    9221              :         " FALSE",
    9222              :         " UNKNOWN",
    9223              :         " EMPTY ARRAY",
    9224              :         " EMPTY OBJECT",
    9225              :         " DEFAULT "
    9226              :     };
    9227              : 
    9228           42 :     if ((int) behavior->btype < 0 || behavior->btype >= lengthof(behavior_names))
    9229            0 :         elog(ERROR, "invalid json behavior type: %d", behavior->btype);
    9230              : 
    9231           42 :     appendStringInfoString(context->buf, behavior_names[behavior->btype]);
    9232              : 
    9233           42 :     if (behavior->btype == JSON_BEHAVIOR_DEFAULT)
    9234            9 :         get_rule_expr(behavior->expr, context, false);
    9235              : 
    9236           42 :     appendStringInfo(context->buf, " ON %s", on);
    9237           42 : }
    9238              : 
    9239              : /*
    9240              :  * get_json_expr_options
    9241              :  *
    9242              :  * Parse back common options for JSON_QUERY, JSON_VALUE, JSON_EXISTS and
    9243              :  * JSON_TABLE columns.
    9244              :  */
    9245              : static void
    9246          228 : get_json_expr_options(JsonExpr *jsexpr, deparse_context *context,
    9247              :                       JsonBehaviorType default_behavior)
    9248              : {
    9249          228 :     if (jsexpr->op == JSON_QUERY_OP)
    9250              :     {
    9251          105 :         if (jsexpr->wrapper == JSW_CONDITIONAL)
    9252            6 :             appendStringInfoString(context->buf, " WITH CONDITIONAL WRAPPER");
    9253           99 :         else if (jsexpr->wrapper == JSW_UNCONDITIONAL)
    9254           15 :             appendStringInfoString(context->buf, " WITH UNCONDITIONAL WRAPPER");
    9255              :         /* The default */
    9256           84 :         else if (jsexpr->wrapper == JSW_NONE || jsexpr->wrapper == JSW_UNSPEC)
    9257           84 :             appendStringInfoString(context->buf, " WITHOUT WRAPPER");
    9258              : 
    9259          105 :         if (jsexpr->omit_quotes)
    9260           21 :             appendStringInfoString(context->buf, " OMIT QUOTES");
    9261              :         /* The default */
    9262              :         else
    9263           84 :             appendStringInfoString(context->buf, " KEEP QUOTES");
    9264              :     }
    9265              : 
    9266          228 :     if (jsexpr->on_empty && jsexpr->on_empty->btype != default_behavior)
    9267           15 :         get_json_behavior(jsexpr->on_empty, context, "EMPTY");
    9268              : 
    9269          228 :     if (jsexpr->on_error && jsexpr->on_error->btype != default_behavior)
    9270           24 :         get_json_behavior(jsexpr->on_error, context, "ERROR");
    9271          228 : }
    9272              : 
    9273              : /* ----------
    9274              :  * get_rule_expr            - Parse back an expression
    9275              :  *
    9276              :  * Note: showimplicit determines whether we display any implicit cast that
    9277              :  * is present at the top of the expression tree.  It is a passed argument,
    9278              :  * not a field of the context struct, because we change the value as we
    9279              :  * recurse down into the expression.  In general we suppress implicit casts
    9280              :  * when the result type is known with certainty (eg, the arguments of an
    9281              :  * OR must be boolean).  We display implicit casts for arguments of functions
    9282              :  * and operators, since this is needed to be certain that the same function
    9283              :  * or operator will be chosen when the expression is re-parsed.
    9284              :  * ----------
    9285              :  */
    9286              : static void
    9287       181865 : get_rule_expr(Node *node, deparse_context *context,
    9288              :               bool showimplicit)
    9289              : {
    9290       181865 :     StringInfo  buf = context->buf;
    9291              : 
    9292       181865 :     if (node == NULL)
    9293           45 :         return;
    9294              : 
    9295              :     /* Guard against excessively long or deeply-nested queries */
    9296       181820 :     CHECK_FOR_INTERRUPTS();
    9297       181820 :     check_stack_depth();
    9298              : 
    9299              :     /*
    9300              :      * Each level of get_rule_expr must emit an indivisible term
    9301              :      * (parenthesized if necessary) to ensure result is reparsed into the same
    9302              :      * expression tree.  The only exception is that when the input is a List,
    9303              :      * we emit the component items comma-separated with no surrounding
    9304              :      * decoration; this is convenient for most callers.
    9305              :      */
    9306       181820 :     switch (nodeTag(node))
    9307              :     {
    9308        87655 :         case T_Var:
    9309        87655 :             (void) get_variable((Var *) node, 0, false, context);
    9310        87655 :             break;
    9311              : 
    9312        31895 :         case T_Const:
    9313        31895 :             get_const_expr((Const *) node, context, 0);
    9314        31895 :             break;
    9315              : 
    9316         3650 :         case T_Param:
    9317         3650 :             get_parameter((Param *) node, context);
    9318         3650 :             break;
    9319              : 
    9320         1994 :         case T_Aggref:
    9321         1994 :             get_agg_expr((Aggref *) node, context, (Aggref *) node);
    9322         1994 :             break;
    9323              : 
    9324           56 :         case T_GroupingFunc:
    9325              :             {
    9326           56 :                 GroupingFunc *gexpr = (GroupingFunc *) node;
    9327              : 
    9328           56 :                 appendStringInfoString(buf, "GROUPING(");
    9329           56 :                 get_rule_expr((Node *) gexpr->args, context, true);
    9330           56 :                 appendStringInfoChar(buf, ')');
    9331              :             }
    9332           56 :             break;
    9333              : 
    9334          162 :         case T_WindowFunc:
    9335          162 :             get_windowfunc_expr((WindowFunc *) node, context);
    9336          162 :             break;
    9337              : 
    9338            3 :         case T_MergeSupportFunc:
    9339            3 :             appendStringInfoString(buf, "MERGE_ACTION()");
    9340            3 :             break;
    9341              : 
    9342          194 :         case T_SubscriptingRef:
    9343              :             {
    9344          194 :                 SubscriptingRef *sbsref = (SubscriptingRef *) node;
    9345              :                 bool        need_parens;
    9346              : 
    9347              :                 /*
    9348              :                  * If the argument is a CaseTestExpr, we must be inside a
    9349              :                  * FieldStore, ie, we are assigning to an element of an array
    9350              :                  * within a composite column.  Since we already punted on
    9351              :                  * displaying the FieldStore's target information, just punt
    9352              :                  * here too, and display only the assignment source
    9353              :                  * expression.
    9354              :                  */
    9355          194 :                 if (IsA(sbsref->refexpr, CaseTestExpr))
    9356              :                 {
    9357              :                     Assert(sbsref->refassgnexpr);
    9358            0 :                     get_rule_expr((Node *) sbsref->refassgnexpr,
    9359              :                                   context, showimplicit);
    9360            0 :                     break;
    9361              :                 }
    9362              : 
    9363              :                 /*
    9364              :                  * Parenthesize the argument unless it's a simple Var or a
    9365              :                  * FieldSelect.  (In particular, if it's another
    9366              :                  * SubscriptingRef, we *must* parenthesize to avoid
    9367              :                  * confusion.)
    9368              :                  */
    9369          301 :                 need_parens = !IsA(sbsref->refexpr, Var) &&
    9370          107 :                     !IsA(sbsref->refexpr, FieldSelect);
    9371          194 :                 if (need_parens)
    9372           47 :                     appendStringInfoChar(buf, '(');
    9373          194 :                 get_rule_expr((Node *) sbsref->refexpr, context, showimplicit);
    9374          194 :                 if (need_parens)
    9375           47 :                     appendStringInfoChar(buf, ')');
    9376              : 
    9377              :                 /*
    9378              :                  * If there's a refassgnexpr, we want to print the node in the
    9379              :                  * format "container[subscripts] := refassgnexpr".  This is
    9380              :                  * not legal SQL, so decompilation of INSERT or UPDATE
    9381              :                  * statements should always use processIndirection as part of
    9382              :                  * the statement-level syntax.  We should only see this when
    9383              :                  * EXPLAIN tries to print the targetlist of a plan resulting
    9384              :                  * from such a statement.
    9385              :                  */
    9386          194 :                 if (sbsref->refassgnexpr)
    9387              :                 {
    9388              :                     Node       *refassgnexpr;
    9389              : 
    9390              :                     /*
    9391              :                      * Use processIndirection to print this node's subscripts
    9392              :                      * as well as any additional field selections or
    9393              :                      * subscripting in immediate descendants.  It returns the
    9394              :                      * RHS expr that is actually being "assigned".
    9395              :                      */
    9396            6 :                     refassgnexpr = processIndirection(node, context);
    9397            6 :                     appendStringInfoString(buf, " := ");
    9398            6 :                     get_rule_expr(refassgnexpr, context, showimplicit);
    9399              :                 }
    9400              :                 else
    9401              :                 {
    9402              :                     /* Just an ordinary container fetch, so print subscripts */
    9403          188 :                     printSubscripts(sbsref, context);
    9404              :                 }
    9405              :             }
    9406          194 :             break;
    9407              : 
    9408         6501 :         case T_FuncExpr:
    9409         6501 :             get_func_expr((FuncExpr *) node, context, showimplicit);
    9410         6501 :             break;
    9411              : 
    9412           15 :         case T_NamedArgExpr:
    9413              :             {
    9414           15 :                 NamedArgExpr *na = (NamedArgExpr *) node;
    9415              : 
    9416           15 :                 appendStringInfo(buf, "%s => ", quote_identifier(na->name));
    9417           15 :                 get_rule_expr((Node *) na->arg, context, showimplicit);
    9418              :             }
    9419           15 :             break;
    9420              : 
    9421        31363 :         case T_OpExpr:
    9422        31363 :             get_oper_expr((OpExpr *) node, context);
    9423        31363 :             break;
    9424              : 
    9425           12 :         case T_DistinctExpr:
    9426              :             {
    9427           12 :                 DistinctExpr *expr = (DistinctExpr *) node;
    9428           12 :                 List       *args = expr->args;
    9429           12 :                 Node       *arg1 = (Node *) linitial(args);
    9430           12 :                 Node       *arg2 = (Node *) lsecond(args);
    9431              : 
    9432           12 :                 if (!PRETTY_PAREN(context))
    9433            9 :                     appendStringInfoChar(buf, '(');
    9434           12 :                 get_rule_expr_paren(arg1, context, true, node);
    9435           12 :                 appendStringInfoString(buf, " IS DISTINCT FROM ");
    9436           12 :                 get_rule_expr_paren(arg2, context, true, node);
    9437           12 :                 if (!PRETTY_PAREN(context))
    9438            9 :                     appendStringInfoChar(buf, ')');
    9439              :             }
    9440           12 :             break;
    9441              : 
    9442           80 :         case T_NullIfExpr:
    9443              :             {
    9444           80 :                 NullIfExpr *nullifexpr = (NullIfExpr *) node;
    9445              : 
    9446           80 :                 appendStringInfoString(buf, "NULLIF(");
    9447           80 :                 get_rule_expr((Node *) nullifexpr->args, context, true);
    9448           80 :                 appendStringInfoChar(buf, ')');
    9449              :             }
    9450           80 :             break;
    9451              : 
    9452         1521 :         case T_ScalarArrayOpExpr:
    9453              :             {
    9454         1521 :                 ScalarArrayOpExpr *expr = (ScalarArrayOpExpr *) node;
    9455         1521 :                 List       *args = expr->args;
    9456         1521 :                 Node       *arg1 = (Node *) linitial(args);
    9457         1521 :                 Node       *arg2 = (Node *) lsecond(args);
    9458              : 
    9459         1521 :                 if (!PRETTY_PAREN(context))
    9460         1515 :                     appendStringInfoChar(buf, '(');
    9461         1521 :                 get_rule_expr_paren(arg1, context, true, node);
    9462         1521 :                 appendStringInfo(buf, " %s %s (",
    9463              :                                  generate_operator_name(expr->opno,
    9464              :                                                         exprType(arg1),
    9465              :                                                         get_base_element_type(exprType(arg2))),
    9466         1521 :                                  expr->useOr ? "ANY" : "ALL");
    9467         1521 :                 get_rule_expr_paren(arg2, context, true, node);
    9468              : 
    9469              :                 /*
    9470              :                  * There's inherent ambiguity in "x op ANY/ALL (y)" when y is
    9471              :                  * a bare sub-SELECT.  Since we're here, the sub-SELECT must
    9472              :                  * be meant as a scalar sub-SELECT yielding an array value to
    9473              :                  * be used in ScalarArrayOpExpr; but the grammar will
    9474              :                  * preferentially interpret such a construct as an ANY/ALL
    9475              :                  * SubLink.  To prevent misparsing the output that way, insert
    9476              :                  * a dummy coercion (which will be stripped by parse analysis,
    9477              :                  * so no inefficiency is added in dump and reload).  This is
    9478              :                  * indeed most likely what the user wrote to get the construct
    9479              :                  * accepted in the first place.
    9480              :                  */
    9481         1521 :                 if (IsA(arg2, SubLink) &&
    9482            3 :                     ((SubLink *) arg2)->subLinkType == EXPR_SUBLINK)
    9483            3 :                     appendStringInfo(buf, "::%s",
    9484              :                                      format_type_with_typemod(exprType(arg2),
    9485              :                                                               exprTypmod(arg2)));
    9486         1521 :                 appendStringInfoChar(buf, ')');
    9487         1521 :                 if (!PRETTY_PAREN(context))
    9488         1515 :                     appendStringInfoChar(buf, ')');
    9489              :             }
    9490         1521 :             break;
    9491              : 
    9492         5675 :         case T_BoolExpr:
    9493              :             {
    9494         5675 :                 BoolExpr   *expr = (BoolExpr *) node;
    9495         5675 :                 Node       *first_arg = linitial(expr->args);
    9496              :                 ListCell   *arg;
    9497              : 
    9498         5675 :                 switch (expr->boolop)
    9499              :                 {
    9500         4503 :                     case AND_EXPR:
    9501         4503 :                         if (!PRETTY_PAREN(context))
    9502         4476 :                             appendStringInfoChar(buf, '(');
    9503         4503 :                         get_rule_expr_paren(first_arg, context,
    9504              :                                             false, node);
    9505        10260 :                         for_each_from(arg, expr->args, 1)
    9506              :                         {
    9507         5757 :                             appendStringInfoString(buf, " AND ");
    9508         5757 :                             get_rule_expr_paren((Node *) lfirst(arg), context,
    9509              :                                                 false, node);
    9510              :                         }
    9511         4503 :                         if (!PRETTY_PAREN(context))
    9512         4476 :                             appendStringInfoChar(buf, ')');
    9513         4503 :                         break;
    9514              : 
    9515          950 :                     case OR_EXPR:
    9516          950 :                         if (!PRETTY_PAREN(context))
    9517          944 :                             appendStringInfoChar(buf, '(');
    9518          950 :                         get_rule_expr_paren(first_arg, context,
    9519              :                                             false, node);
    9520         2257 :                         for_each_from(arg, expr->args, 1)
    9521              :                         {
    9522         1307 :                             appendStringInfoString(buf, " OR ");
    9523         1307 :                             get_rule_expr_paren((Node *) lfirst(arg), context,
    9524              :                                                 false, node);
    9525              :                         }
    9526          950 :                         if (!PRETTY_PAREN(context))
    9527          944 :                             appendStringInfoChar(buf, ')');
    9528          950 :                         break;
    9529              : 
    9530          222 :                     case NOT_EXPR:
    9531          222 :                         if (!PRETTY_PAREN(context))
    9532          216 :                             appendStringInfoChar(buf, '(');
    9533          222 :                         appendStringInfoString(buf, "NOT ");
    9534          222 :                         get_rule_expr_paren(first_arg, context,
    9535              :                                             false, node);
    9536          222 :                         if (!PRETTY_PAREN(context))
    9537          216 :                             appendStringInfoChar(buf, ')');
    9538          222 :                         break;
    9539              : 
    9540            0 :                     default:
    9541            0 :                         elog(ERROR, "unrecognized boolop: %d",
    9542              :                              (int) expr->boolop);
    9543              :                 }
    9544              :             }
    9545         5675 :             break;
    9546              : 
    9547          230 :         case T_SubLink:
    9548          230 :             get_sublink_expr((SubLink *) node, context);
    9549          230 :             break;
    9550              : 
    9551          397 :         case T_SubPlan:
    9552              :             {
    9553          397 :                 SubPlan    *subplan = (SubPlan *) node;
    9554              : 
    9555              :                 /*
    9556              :                  * We cannot see an already-planned subplan in rule deparsing,
    9557              :                  * only while EXPLAINing a query plan.  We don't try to
    9558              :                  * reconstruct the original SQL, just reference the subplan
    9559              :                  * that appears elsewhere in EXPLAIN's result.  It does seem
    9560              :                  * useful to show the subLinkType and testexpr (if any), and
    9561              :                  * we also note whether the subplan will be hashed.
    9562              :                  */
    9563          397 :                 switch (subplan->subLinkType)
    9564              :                 {
    9565           51 :                     case EXISTS_SUBLINK:
    9566           51 :                         appendStringInfoString(buf, "EXISTS(");
    9567              :                         Assert(subplan->testexpr == NULL);
    9568           51 :                         break;
    9569            3 :                     case ALL_SUBLINK:
    9570            3 :                         appendStringInfoString(buf, "(ALL ");
    9571              :                         Assert(subplan->testexpr != NULL);
    9572            3 :                         break;
    9573          116 :                     case ANY_SUBLINK:
    9574          116 :                         appendStringInfoString(buf, "(ANY ");
    9575              :                         Assert(subplan->testexpr != NULL);
    9576          116 :                         break;
    9577            3 :                     case ROWCOMPARE_SUBLINK:
    9578              :                         /* Parenthesizing the testexpr seems sufficient */
    9579            3 :                         appendStringInfoChar(buf, '(');
    9580              :                         Assert(subplan->testexpr != NULL);
    9581            3 :                         break;
    9582          205 :                     case EXPR_SUBLINK:
    9583              :                         /* No need to decorate these subplan references */
    9584          205 :                         appendStringInfoChar(buf, '(');
    9585              :                         Assert(subplan->testexpr == NULL);
    9586          205 :                         break;
    9587           13 :                     case MULTIEXPR_SUBLINK:
    9588              :                         /* MULTIEXPR isn't executed in the normal way */
    9589           13 :                         appendStringInfoString(buf, "(rescan ");
    9590              :                         Assert(subplan->testexpr == NULL);
    9591           13 :                         break;
    9592            6 :                     case ARRAY_SUBLINK:
    9593            6 :                         appendStringInfoString(buf, "ARRAY(");
    9594              :                         Assert(subplan->testexpr == NULL);
    9595            6 :                         break;
    9596            0 :                     case CTE_SUBLINK:
    9597              :                         /* This case is unreachable within expressions */
    9598            0 :                         appendStringInfoString(buf, "CTE(");
    9599              :                         Assert(subplan->testexpr == NULL);
    9600            0 :                         break;
    9601              :                 }
    9602              : 
    9603          397 :                 if (subplan->testexpr != NULL)
    9604              :                 {
    9605              :                     deparse_namespace *dpns;
    9606              : 
    9607              :                     /*
    9608              :                      * Push SubPlan into ancestors list while deparsing
    9609              :                      * testexpr, so that we can handle PARAM_EXEC references
    9610              :                      * to the SubPlan's paramIds.  (This makes it look like
    9611              :                      * the SubPlan is an "ancestor" of the current plan node,
    9612              :                      * which is a little weird, but it does no harm.)  In this
    9613              :                      * path, we don't need to mention the SubPlan explicitly,
    9614              :                      * because the referencing Params will show its existence.
    9615              :                      */
    9616          122 :                     dpns = (deparse_namespace *) linitial(context->namespaces);
    9617          122 :                     dpns->ancestors = lcons(subplan, dpns->ancestors);
    9618              : 
    9619          122 :                     get_rule_expr(subplan->testexpr, context, showimplicit);
    9620          122 :                     appendStringInfoChar(buf, ')');
    9621              : 
    9622          122 :                     dpns->ancestors = list_delete_first(dpns->ancestors);
    9623              :                 }
    9624              :                 else
    9625              :                 {
    9626              :                     const char *nameprefix;
    9627              : 
    9628              :                     /* No referencing Params, so show the SubPlan's name */
    9629          275 :                     if (subplan->isInitPlan)
    9630            0 :                         nameprefix = "InitPlan ";
    9631              :                     else
    9632          275 :                         nameprefix = "SubPlan ";
    9633          275 :                     if (subplan->useHashTable)
    9634            0 :                         appendStringInfo(buf, "hashed %s%s)",
    9635              :                                          nameprefix, subplan->plan_name);
    9636              :                     else
    9637          275 :                         appendStringInfo(buf, "%s%s)",
    9638              :                                          nameprefix, subplan->plan_name);
    9639              :                 }
    9640              :             }
    9641          397 :             break;
    9642              : 
    9643            0 :         case T_AlternativeSubPlan:
    9644              :             {
    9645            0 :                 AlternativeSubPlan *asplan = (AlternativeSubPlan *) node;
    9646              :                 ListCell   *lc;
    9647              : 
    9648              :                 /*
    9649              :                  * This case cannot be reached in normal usage, since no
    9650              :                  * AlternativeSubPlan can appear either in parsetrees or
    9651              :                  * finished plan trees.  We keep it just in case somebody
    9652              :                  * wants to use this code to print planner data structures.
    9653              :                  */
    9654            0 :                 appendStringInfoString(buf, "(alternatives: ");
    9655            0 :                 foreach(lc, asplan->subplans)
    9656              :                 {
    9657            0 :                     SubPlan    *splan = lfirst_node(SubPlan, lc);
    9658              :                     const char *nameprefix;
    9659              : 
    9660            0 :                     if (splan->isInitPlan)
    9661            0 :                         nameprefix = "InitPlan ";
    9662              :                     else
    9663            0 :                         nameprefix = "SubPlan ";
    9664            0 :                     if (splan->useHashTable)
    9665            0 :                         appendStringInfo(buf, "hashed %s%s", nameprefix,
    9666              :                                          splan->plan_name);
    9667              :                     else
    9668            0 :                         appendStringInfo(buf, "%s%s", nameprefix,
    9669              :                                          splan->plan_name);
    9670            0 :                     if (lnext(asplan->subplans, lc))
    9671            0 :                         appendStringInfoString(buf, " or ");
    9672              :                 }
    9673            0 :                 appendStringInfoChar(buf, ')');
    9674              :             }
    9675            0 :             break;
    9676              : 
    9677          772 :         case T_FieldSelect:
    9678              :             {
    9679          772 :                 FieldSelect *fselect = (FieldSelect *) node;
    9680          772 :                 Node       *arg = (Node *) fselect->arg;
    9681          772 :                 int         fno = fselect->fieldnum;
    9682              :                 const char *fieldname;
    9683              :                 bool        need_parens;
    9684              : 
    9685              :                 /*
    9686              :                  * Parenthesize the argument unless it's a SubscriptingRef or
    9687              :                  * another FieldSelect.  Note in particular that it would be
    9688              :                  * WRONG to not parenthesize a Var argument; simplicity is not
    9689              :                  * the issue here, having the right number of names is.
    9690              :                  */
    9691         1526 :                 need_parens = !IsA(arg, SubscriptingRef) &&
    9692          754 :                     !IsA(arg, FieldSelect);
    9693          772 :                 if (need_parens)
    9694          754 :                     appendStringInfoChar(buf, '(');
    9695          772 :                 get_rule_expr(arg, context, true);
    9696          772 :                 if (need_parens)
    9697          754 :                     appendStringInfoChar(buf, ')');
    9698              : 
    9699              :                 /*
    9700              :                  * Get and print the field name.
    9701              :                  */
    9702          772 :                 fieldname = get_name_for_var_field((Var *) arg, fno,
    9703              :                                                    0, context);
    9704          772 :                 appendStringInfo(buf, ".%s", quote_identifier(fieldname));
    9705              :             }
    9706          772 :             break;
    9707              : 
    9708            3 :         case T_FieldStore:
    9709              :             {
    9710            3 :                 FieldStore *fstore = (FieldStore *) node;
    9711              :                 bool        need_parens;
    9712              : 
    9713              :                 /*
    9714              :                  * There is no good way to represent a FieldStore as real SQL,
    9715              :                  * so decompilation of INSERT or UPDATE statements should
    9716              :                  * always use processIndirection as part of the
    9717              :                  * statement-level syntax.  We should only get here when
    9718              :                  * EXPLAIN tries to print the targetlist of a plan resulting
    9719              :                  * from such a statement.  The plan case is even harder than
    9720              :                  * ordinary rules would be, because the planner tries to
    9721              :                  * collapse multiple assignments to the same field or subfield
    9722              :                  * into one FieldStore; so we can see a list of target fields
    9723              :                  * not just one, and the arguments could be FieldStores
    9724              :                  * themselves.  We don't bother to try to print the target
    9725              :                  * field names; we just print the source arguments, with a
    9726              :                  * ROW() around them if there's more than one.  This isn't
    9727              :                  * terribly complete, but it's probably good enough for
    9728              :                  * EXPLAIN's purposes; especially since anything more would be
    9729              :                  * either hopelessly confusing or an even poorer
    9730              :                  * representation of what the plan is actually doing.
    9731              :                  */
    9732            3 :                 need_parens = (list_length(fstore->newvals) != 1);
    9733            3 :                 if (need_parens)
    9734            3 :                     appendStringInfoString(buf, "ROW(");
    9735            3 :                 get_rule_expr((Node *) fstore->newvals, context, showimplicit);
    9736            3 :                 if (need_parens)
    9737            3 :                     appendStringInfoChar(buf, ')');
    9738              :             }
    9739            3 :             break;
    9740              : 
    9741         1395 :         case T_RelabelType:
    9742              :             {
    9743         1395 :                 RelabelType *relabel = (RelabelType *) node;
    9744         1395 :                 Node       *arg = (Node *) relabel->arg;
    9745              : 
    9746         1395 :                 if (relabel->relabelformat == COERCE_IMPLICIT_CAST &&
    9747         1280 :                     !showimplicit)
    9748              :                 {
    9749              :                     /* don't show the implicit cast */
    9750           37 :                     get_rule_expr_paren(arg, context, false, node);
    9751              :                 }
    9752              :                 else
    9753              :                 {
    9754         1358 :                     get_coercion_expr(arg, context,
    9755              :                                       relabel->resulttype,
    9756              :                                       relabel->resulttypmod,
    9757              :                                       node);
    9758              :                 }
    9759              :             }
    9760         1395 :             break;
    9761              : 
    9762          358 :         case T_CoerceViaIO:
    9763              :             {
    9764          358 :                 CoerceViaIO *iocoerce = (CoerceViaIO *) node;
    9765          358 :                 Node       *arg = (Node *) iocoerce->arg;
    9766              : 
    9767          358 :                 if (iocoerce->coerceformat == COERCE_IMPLICIT_CAST &&
    9768           12 :                     !showimplicit)
    9769              :                 {
    9770              :                     /* don't show the implicit cast */
    9771           12 :                     get_rule_expr_paren(arg, context, false, node);
    9772              :                 }
    9773              :                 else
    9774              :                 {
    9775          346 :                     get_coercion_expr(arg, context,
    9776              :                                       iocoerce->resulttype,
    9777              :                                       -1,
    9778              :                                       node);
    9779              :                 }
    9780              :             }
    9781          358 :             break;
    9782              : 
    9783           26 :         case T_ArrayCoerceExpr:
    9784              :             {
    9785           26 :                 ArrayCoerceExpr *acoerce = (ArrayCoerceExpr *) node;
    9786           26 :                 Node       *arg = (Node *) acoerce->arg;
    9787              : 
    9788           26 :                 if (acoerce->coerceformat == COERCE_IMPLICIT_CAST &&
    9789           26 :                     !showimplicit)
    9790              :                 {
    9791              :                     /* don't show the implicit cast */
    9792            0 :                     get_rule_expr_paren(arg, context, false, node);
    9793              :                 }
    9794              :                 else
    9795              :                 {
    9796           26 :                     get_coercion_expr(arg, context,
    9797              :                                       acoerce->resulttype,
    9798              :                                       acoerce->resulttypmod,
    9799              :                                       node);
    9800              :                 }
    9801              :             }
    9802           26 :             break;
    9803              : 
    9804           44 :         case T_ConvertRowtypeExpr:
    9805              :             {
    9806           44 :                 ConvertRowtypeExpr *convert = (ConvertRowtypeExpr *) node;
    9807           44 :                 Node       *arg = (Node *) convert->arg;
    9808              : 
    9809           44 :                 if (convert->convertformat == COERCE_IMPLICIT_CAST &&
    9810           41 :                     !showimplicit)
    9811              :                 {
    9812              :                     /* don't show the implicit cast */
    9813           12 :                     get_rule_expr_paren(arg, context, false, node);
    9814              :                 }
    9815              :                 else
    9816              :                 {
    9817           32 :                     get_coercion_expr(arg, context,
    9818              :                                       convert->resulttype, -1,
    9819              :                                       node);
    9820              :                 }
    9821              :             }
    9822           44 :             break;
    9823              : 
    9824           45 :         case T_CollateExpr:
    9825              :             {
    9826           45 :                 CollateExpr *collate = (CollateExpr *) node;
    9827           45 :                 Node       *arg = (Node *) collate->arg;
    9828              : 
    9829           45 :                 if (!PRETTY_PAREN(context))
    9830           42 :                     appendStringInfoChar(buf, '(');
    9831           45 :                 get_rule_expr_paren(arg, context, showimplicit, node);
    9832           45 :                 appendStringInfo(buf, " COLLATE %s",
    9833              :                                  generate_collation_name(collate->collOid));
    9834           45 :                 if (!PRETTY_PAREN(context))
    9835           42 :                     appendStringInfoChar(buf, ')');
    9836              :             }
    9837           45 :             break;
    9838              : 
    9839          351 :         case T_CaseExpr:
    9840              :             {
    9841          351 :                 CaseExpr   *caseexpr = (CaseExpr *) node;
    9842              :                 ListCell   *temp;
    9843              : 
    9844          351 :                 appendContextKeyword(context, "CASE",
    9845              :                                      0, PRETTYINDENT_VAR, 0);
    9846          351 :                 if (caseexpr->arg)
    9847              :                 {
    9848          111 :                     appendStringInfoChar(buf, ' ');
    9849          111 :                     get_rule_expr((Node *) caseexpr->arg, context, true);
    9850              :                 }
    9851         1541 :                 foreach(temp, caseexpr->args)
    9852              :                 {
    9853         1190 :                     CaseWhen   *when = (CaseWhen *) lfirst(temp);
    9854         1190 :                     Node       *w = (Node *) when->expr;
    9855              : 
    9856         1190 :                     if (caseexpr->arg)
    9857              :                     {
    9858              :                         /*
    9859              :                          * The parser should have produced WHEN clauses of the
    9860              :                          * form "CaseTestExpr = RHS", possibly with an
    9861              :                          * implicit coercion inserted above the CaseTestExpr.
    9862              :                          * For accurate decompilation of rules it's essential
    9863              :                          * that we show just the RHS.  However in an
    9864              :                          * expression that's been through the optimizer, the
    9865              :                          * WHEN clause could be almost anything (since the
    9866              :                          * equality operator could have been expanded into an
    9867              :                          * inline function).  If we don't recognize the form
    9868              :                          * of the WHEN clause, just punt and display it as-is.
    9869              :                          */
    9870          440 :                         if (IsA(w, OpExpr))
    9871              :                         {
    9872          440 :                             List       *args = ((OpExpr *) w)->args;
    9873              : 
    9874          440 :                             if (list_length(args) == 2 &&
    9875          440 :                                 IsA(strip_implicit_coercions(linitial(args)),
    9876              :                                     CaseTestExpr))
    9877          440 :                                 w = (Node *) lsecond(args);
    9878              :                         }
    9879              :                     }
    9880              : 
    9881         1190 :                     if (!PRETTY_INDENT(context))
    9882           65 :                         appendStringInfoChar(buf, ' ');
    9883         1190 :                     appendContextKeyword(context, "WHEN ",
    9884              :                                          0, 0, 0);
    9885         1190 :                     get_rule_expr(w, context, false);
    9886         1190 :                     appendStringInfoString(buf, " THEN ");
    9887         1190 :                     get_rule_expr((Node *) when->result, context, true);
    9888              :                 }
    9889          351 :                 if (!PRETTY_INDENT(context))
    9890           60 :                     appendStringInfoChar(buf, ' ');
    9891          351 :                 appendContextKeyword(context, "ELSE ",
    9892              :                                      0, 0, 0);
    9893          351 :                 get_rule_expr((Node *) caseexpr->defresult, context, true);
    9894          351 :                 if (!PRETTY_INDENT(context))
    9895           60 :                     appendStringInfoChar(buf, ' ');
    9896          351 :                 appendContextKeyword(context, "END",
    9897              :                                      -PRETTYINDENT_VAR, 0, 0);
    9898              :             }
    9899          351 :             break;
    9900              : 
    9901            0 :         case T_CaseTestExpr:
    9902              :             {
    9903              :                 /*
    9904              :                  * Normally we should never get here, since for expressions
    9905              :                  * that can contain this node type we attempt to avoid
    9906              :                  * recursing to it.  But in an optimized expression we might
    9907              :                  * be unable to avoid that (see comments for CaseExpr).  If we
    9908              :                  * do see one, print it as CASE_TEST_EXPR.
    9909              :                  */
    9910            0 :                 appendStringInfoString(buf, "CASE_TEST_EXPR");
    9911              :             }
    9912            0 :             break;
    9913              : 
    9914          292 :         case T_ArrayExpr:
    9915              :             {
    9916          292 :                 ArrayExpr  *arrayexpr = (ArrayExpr *) node;
    9917              : 
    9918          292 :                 appendStringInfoString(buf, "ARRAY[");
    9919          292 :                 get_rule_expr((Node *) arrayexpr->elements, context, true);
    9920          292 :                 appendStringInfoChar(buf, ']');
    9921              : 
    9922              :                 /*
    9923              :                  * If the array isn't empty, we assume its elements are
    9924              :                  * coerced to the desired type.  If it's empty, though, we
    9925              :                  * need an explicit coercion to the array type.
    9926              :                  */
    9927          292 :                 if (arrayexpr->elements == NIL)
    9928            3 :                     appendStringInfo(buf, "::%s",
    9929              :                                      format_type_with_typemod(arrayexpr->array_typeid, -1));
    9930              :             }
    9931          292 :             break;
    9932              : 
    9933          108 :         case T_RowExpr:
    9934              :             {
    9935          108 :                 RowExpr    *rowexpr = (RowExpr *) node;
    9936          108 :                 TupleDesc   tupdesc = NULL;
    9937              :                 ListCell   *arg;
    9938              :                 int         i;
    9939              :                 char       *sep;
    9940              : 
    9941              :                 /*
    9942              :                  * If it's a named type and not RECORD, we may have to skip
    9943              :                  * dropped columns and/or claim there are NULLs for added
    9944              :                  * columns.
    9945              :                  */
    9946          108 :                 if (rowexpr->row_typeid != RECORDOID)
    9947              :                 {
    9948           33 :                     tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
    9949              :                     Assert(list_length(rowexpr->args) <= tupdesc->natts);
    9950              :                 }
    9951              : 
    9952              :                 /*
    9953              :                  * SQL99 allows "ROW" to be omitted when there is more than
    9954              :                  * one column, but for simplicity we always print it.
    9955              :                  */
    9956          108 :                 appendStringInfoString(buf, "ROW(");
    9957          108 :                 sep = "";
    9958          108 :                 i = 0;
    9959          327 :                 foreach(arg, rowexpr->args)
    9960              :                 {
    9961          219 :                     Node       *e = (Node *) lfirst(arg);
    9962              : 
    9963          219 :                     if (tupdesc == NULL ||
    9964           78 :                         !TupleDescCompactAttr(tupdesc, i)->attisdropped)
    9965              :                     {
    9966          219 :                         appendStringInfoString(buf, sep);
    9967              :                         /* Whole-row Vars need special treatment here */
    9968          219 :                         get_rule_expr_toplevel(e, context, true);
    9969          219 :                         sep = ", ";
    9970              :                     }
    9971          219 :                     i++;
    9972              :                 }
    9973          108 :                 if (tupdesc != NULL)
    9974              :                 {
    9975           33 :                     while (i < tupdesc->natts)
    9976              :                     {
    9977            0 :                         if (!TupleDescCompactAttr(tupdesc, i)->attisdropped)
    9978              :                         {
    9979            0 :                             appendStringInfoString(buf, sep);
    9980            0 :                             appendStringInfoString(buf, "NULL");
    9981            0 :                             sep = ", ";
    9982              :                         }
    9983            0 :                         i++;
    9984              :                     }
    9985              : 
    9986           33 :                     ReleaseTupleDesc(tupdesc);
    9987              :                 }
    9988          108 :                 appendStringInfoChar(buf, ')');
    9989          108 :                 if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
    9990           18 :                     appendStringInfo(buf, "::%s",
    9991              :                                      format_type_with_typemod(rowexpr->row_typeid, -1));
    9992              :             }
    9993          108 :             break;
    9994              : 
    9995           60 :         case T_RowCompareExpr:
    9996              :             {
    9997           60 :                 RowCompareExpr *rcexpr = (RowCompareExpr *) node;
    9998              : 
    9999              :                 /*
   10000              :                  * SQL99 allows "ROW" to be omitted when there is more than
   10001              :                  * one column, but for simplicity we always print it.  Within
   10002              :                  * a ROW expression, whole-row Vars need special treatment, so
   10003              :                  * use get_rule_list_toplevel.
   10004              :                  */
   10005           60 :                 appendStringInfoString(buf, "(ROW(");
   10006           60 :                 get_rule_list_toplevel(rcexpr->largs, context, true);
   10007              : 
   10008              :                 /*
   10009              :                  * We assume that the name of the first-column operator will
   10010              :                  * do for all the rest too.  This is definitely open to
   10011              :                  * failure, eg if some but not all operators were renamed
   10012              :                  * since the construct was parsed, but there seems no way to
   10013              :                  * be perfect.
   10014              :                  */
   10015           60 :                 appendStringInfo(buf, ") %s ROW(",
   10016           60 :                                  generate_operator_name(linitial_oid(rcexpr->opnos),
   10017           60 :                                                         exprType(linitial(rcexpr->largs)),
   10018           60 :                                                         exprType(linitial(rcexpr->rargs))));
   10019           60 :                 get_rule_list_toplevel(rcexpr->rargs, context, true);
   10020           60 :                 appendStringInfoString(buf, "))");
   10021              :             }
   10022           60 :             break;
   10023              : 
   10024          615 :         case T_CoalesceExpr:
   10025              :             {
   10026          615 :                 CoalesceExpr *coalesceexpr = (CoalesceExpr *) node;
   10027              : 
   10028          615 :                 appendStringInfoString(buf, "COALESCE(");
   10029          615 :                 get_rule_expr((Node *) coalesceexpr->args, context, true);
   10030          615 :                 appendStringInfoChar(buf, ')');
   10031              :             }
   10032          615 :             break;
   10033              : 
   10034           21 :         case T_MinMaxExpr:
   10035              :             {
   10036           21 :                 MinMaxExpr *minmaxexpr = (MinMaxExpr *) node;
   10037              : 
   10038           21 :                 switch (minmaxexpr->op)
   10039              :                 {
   10040            6 :                     case IS_GREATEST:
   10041            6 :                         appendStringInfoString(buf, "GREATEST(");
   10042            6 :                         break;
   10043           15 :                     case IS_LEAST:
   10044           15 :                         appendStringInfoString(buf, "LEAST(");
   10045           15 :                         break;
   10046              :                 }
   10047           21 :                 get_rule_expr((Node *) minmaxexpr->args, context, true);
   10048           21 :                 appendStringInfoChar(buf, ')');
   10049              :             }
   10050           21 :             break;
   10051              : 
   10052          359 :         case T_SQLValueFunction:
   10053              :             {
   10054          359 :                 SQLValueFunction *svf = (SQLValueFunction *) node;
   10055              : 
   10056              :                 /*
   10057              :                  * Note: this code knows that typmod for time, timestamp, and
   10058              :                  * timestamptz just prints as integer.
   10059              :                  */
   10060          359 :                 switch (svf->op)
   10061              :                 {
   10062           52 :                     case SVFOP_CURRENT_DATE:
   10063           52 :                         appendStringInfoString(buf, "CURRENT_DATE");
   10064           52 :                         break;
   10065            6 :                     case SVFOP_CURRENT_TIME:
   10066            6 :                         appendStringInfoString(buf, "CURRENT_TIME");
   10067            6 :                         break;
   10068            6 :                     case SVFOP_CURRENT_TIME_N:
   10069            6 :                         appendStringInfo(buf, "CURRENT_TIME(%d)", svf->typmod);
   10070            6 :                         break;
   10071            6 :                     case SVFOP_CURRENT_TIMESTAMP:
   10072            6 :                         appendStringInfoString(buf, "CURRENT_TIMESTAMP");
   10073            6 :                         break;
   10074           64 :                     case SVFOP_CURRENT_TIMESTAMP_N:
   10075           64 :                         appendStringInfo(buf, "CURRENT_TIMESTAMP(%d)",
   10076              :                                          svf->typmod);
   10077           64 :                         break;
   10078            6 :                     case SVFOP_LOCALTIME:
   10079            6 :                         appendStringInfoString(buf, "LOCALTIME");
   10080            6 :                         break;
   10081            6 :                     case SVFOP_LOCALTIME_N:
   10082            6 :                         appendStringInfo(buf, "LOCALTIME(%d)", svf->typmod);
   10083            6 :                         break;
   10084           15 :                     case SVFOP_LOCALTIMESTAMP:
   10085           15 :                         appendStringInfoString(buf, "LOCALTIMESTAMP");
   10086           15 :                         break;
   10087            9 :                     case SVFOP_LOCALTIMESTAMP_N:
   10088            9 :                         appendStringInfo(buf, "LOCALTIMESTAMP(%d)",
   10089              :                                          svf->typmod);
   10090            9 :                         break;
   10091            6 :                     case SVFOP_CURRENT_ROLE:
   10092            6 :                         appendStringInfoString(buf, "CURRENT_ROLE");
   10093            6 :                         break;
   10094          148 :                     case SVFOP_CURRENT_USER:
   10095          148 :                         appendStringInfoString(buf, "CURRENT_USER");
   10096          148 :                         break;
   10097            6 :                     case SVFOP_USER:
   10098            6 :                         appendStringInfoString(buf, "USER");
   10099            6 :                         break;
   10100           17 :                     case SVFOP_SESSION_USER:
   10101           17 :                         appendStringInfoString(buf, "SESSION_USER");
   10102           17 :                         break;
   10103            6 :                     case SVFOP_CURRENT_CATALOG:
   10104            6 :                         appendStringInfoString(buf, "CURRENT_CATALOG");
   10105            6 :                         break;
   10106            6 :                     case SVFOP_CURRENT_SCHEMA:
   10107            6 :                         appendStringInfoString(buf, "CURRENT_SCHEMA");
   10108            6 :                         break;
   10109              :                 }
   10110              :             }
   10111          359 :             break;
   10112              : 
   10113           88 :         case T_XmlExpr:
   10114              :             {
   10115           88 :                 XmlExpr    *xexpr = (XmlExpr *) node;
   10116           88 :                 bool        needcomma = false;
   10117              :                 ListCell   *arg;
   10118              :                 ListCell   *narg;
   10119              :                 Const      *con;
   10120              : 
   10121           88 :                 switch (xexpr->op)
   10122              :                 {
   10123            8 :                     case IS_XMLCONCAT:
   10124            8 :                         appendStringInfoString(buf, "XMLCONCAT(");
   10125            8 :                         break;
   10126           16 :                     case IS_XMLELEMENT:
   10127           16 :                         appendStringInfoString(buf, "XMLELEMENT(");
   10128           16 :                         break;
   10129            8 :                     case IS_XMLFOREST:
   10130            8 :                         appendStringInfoString(buf, "XMLFOREST(");
   10131            8 :                         break;
   10132            8 :                     case IS_XMLPARSE:
   10133            8 :                         appendStringInfoString(buf, "XMLPARSE(");
   10134            8 :                         break;
   10135            8 :                     case IS_XMLPI:
   10136            8 :                         appendStringInfoString(buf, "XMLPI(");
   10137            8 :                         break;
   10138            8 :                     case IS_XMLROOT:
   10139            8 :                         appendStringInfoString(buf, "XMLROOT(");
   10140            8 :                         break;
   10141           32 :                     case IS_XMLSERIALIZE:
   10142           32 :                         appendStringInfoString(buf, "XMLSERIALIZE(");
   10143           32 :                         break;
   10144            0 :                     case IS_DOCUMENT:
   10145            0 :                         break;
   10146              :                 }
   10147           88 :                 if (xexpr->op == IS_XMLPARSE || xexpr->op == IS_XMLSERIALIZE)
   10148              :                 {
   10149           40 :                     if (xexpr->xmloption == XMLOPTION_DOCUMENT)
   10150           16 :                         appendStringInfoString(buf, "DOCUMENT ");
   10151              :                     else
   10152           24 :                         appendStringInfoString(buf, "CONTENT ");
   10153              :                 }
   10154           88 :                 if (xexpr->name)
   10155              :                 {
   10156           24 :                     appendStringInfo(buf, "NAME %s",
   10157           24 :                                      quote_identifier(map_xml_name_to_sql_identifier(xexpr->name)));
   10158           24 :                     needcomma = true;
   10159              :                 }
   10160           88 :                 if (xexpr->named_args)
   10161              :                 {
   10162           16 :                     if (xexpr->op != IS_XMLFOREST)
   10163              :                     {
   10164            8 :                         if (needcomma)
   10165            8 :                             appendStringInfoString(buf, ", ");
   10166            8 :                         appendStringInfoString(buf, "XMLATTRIBUTES(");
   10167            8 :                         needcomma = false;
   10168              :                     }
   10169           56 :                     forboth(arg, xexpr->named_args, narg, xexpr->arg_names)
   10170              :                     {
   10171           40 :                         Node       *e = (Node *) lfirst(arg);
   10172           40 :                         char       *argname = strVal(lfirst(narg));
   10173              : 
   10174           40 :                         if (needcomma)
   10175           24 :                             appendStringInfoString(buf, ", ");
   10176           40 :                         get_rule_expr(e, context, true);
   10177           40 :                         appendStringInfo(buf, " AS %s",
   10178           40 :                                          quote_identifier(map_xml_name_to_sql_identifier(argname)));
   10179           40 :                         needcomma = true;
   10180              :                     }
   10181           16 :                     if (xexpr->op != IS_XMLFOREST)
   10182            8 :                         appendStringInfoChar(buf, ')');
   10183              :                 }
   10184           88 :                 if (xexpr->args)
   10185              :                 {
   10186           80 :                     if (needcomma)
   10187           24 :                         appendStringInfoString(buf, ", ");
   10188           80 :                     switch (xexpr->op)
   10189              :                     {
   10190           64 :                         case IS_XMLCONCAT:
   10191              :                         case IS_XMLELEMENT:
   10192              :                         case IS_XMLFOREST:
   10193              :                         case IS_XMLPI:
   10194              :                         case IS_XMLSERIALIZE:
   10195              :                             /* no extra decoration needed */
   10196           64 :                             get_rule_expr((Node *) xexpr->args, context, true);
   10197           64 :                             break;
   10198            8 :                         case IS_XMLPARSE:
   10199              :                             Assert(list_length(xexpr->args) == 2);
   10200              : 
   10201            8 :                             get_rule_expr((Node *) linitial(xexpr->args),
   10202              :                                           context, true);
   10203              : 
   10204            8 :                             con = lsecond_node(Const, xexpr->args);
   10205              :                             Assert(!con->constisnull);
   10206            8 :                             if (DatumGetBool(con->constvalue))
   10207            0 :                                 appendStringInfoString(buf,
   10208              :                                                        " PRESERVE WHITESPACE");
   10209              :                             else
   10210            8 :                                 appendStringInfoString(buf,
   10211              :                                                        " STRIP WHITESPACE");
   10212            8 :                             break;
   10213            8 :                         case IS_XMLROOT:
   10214              :                             Assert(list_length(xexpr->args) == 3);
   10215              : 
   10216            8 :                             get_rule_expr((Node *) linitial(xexpr->args),
   10217              :                                           context, true);
   10218              : 
   10219            8 :                             appendStringInfoString(buf, ", VERSION ");
   10220            8 :                             con = (Const *) lsecond(xexpr->args);
   10221            8 :                             if (IsA(con, Const) &&
   10222            8 :                                 con->constisnull)
   10223            8 :                                 appendStringInfoString(buf, "NO VALUE");
   10224              :                             else
   10225            0 :                                 get_rule_expr((Node *) con, context, false);
   10226              : 
   10227            8 :                             con = lthird_node(Const, xexpr->args);
   10228            8 :                             if (con->constisnull)
   10229              :                                  /* suppress STANDALONE NO VALUE */ ;
   10230              :                             else
   10231              :                             {
   10232            8 :                                 switch (DatumGetInt32(con->constvalue))
   10233              :                                 {
   10234            8 :                                     case XML_STANDALONE_YES:
   10235            8 :                                         appendStringInfoString(buf,
   10236              :                                                                ", STANDALONE YES");
   10237            8 :                                         break;
   10238            0 :                                     case XML_STANDALONE_NO:
   10239            0 :                                         appendStringInfoString(buf,
   10240              :                                                                ", STANDALONE NO");
   10241            0 :                                         break;
   10242            0 :                                     case XML_STANDALONE_NO_VALUE:
   10243            0 :                                         appendStringInfoString(buf,
   10244              :                                                                ", STANDALONE NO VALUE");
   10245            0 :                                         break;
   10246            0 :                                     default:
   10247            0 :                                         break;
   10248              :                                 }
   10249              :                             }
   10250            8 :                             break;
   10251            0 :                         case IS_DOCUMENT:
   10252            0 :                             get_rule_expr_paren((Node *) xexpr->args, context, false, node);
   10253            0 :                             break;
   10254              :                     }
   10255              :                 }
   10256           88 :                 if (xexpr->op == IS_XMLSERIALIZE)
   10257              :                 {
   10258           32 :                     appendStringInfo(buf, " AS %s",
   10259              :                                      format_type_with_typemod(xexpr->type,
   10260              :                                                               xexpr->typmod));
   10261           32 :                     if (xexpr->indent)
   10262            8 :                         appendStringInfoString(buf, " INDENT");
   10263              :                     else
   10264           24 :                         appendStringInfoString(buf, " NO INDENT");
   10265              :                 }
   10266              : 
   10267           88 :                 if (xexpr->op == IS_DOCUMENT)
   10268            0 :                     appendStringInfoString(buf, " IS DOCUMENT");
   10269              :                 else
   10270           88 :                     appendStringInfoChar(buf, ')');
   10271              :             }
   10272           88 :             break;
   10273              : 
   10274         1379 :         case T_NullTest:
   10275              :             {
   10276         1379 :                 NullTest   *ntest = (NullTest *) node;
   10277              : 
   10278         1379 :                 if (!PRETTY_PAREN(context))
   10279         1349 :                     appendStringInfoChar(buf, '(');
   10280         1379 :                 get_rule_expr_paren((Node *) ntest->arg, context, true, node);
   10281              : 
   10282              :                 /*
   10283              :                  * For scalar inputs, we prefer to print as IS [NOT] NULL,
   10284              :                  * which is shorter and traditional.  If it's a rowtype input
   10285              :                  * but we're applying a scalar test, must print IS [NOT]
   10286              :                  * DISTINCT FROM NULL to be semantically correct.
   10287              :                  */
   10288         1379 :                 if (ntest->argisrow ||
   10289         1348 :                     !type_is_rowtype(exprType((Node *) ntest->arg)))
   10290              :                 {
   10291         1364 :                     switch (ntest->nulltesttype)
   10292              :                     {
   10293          429 :                         case IS_NULL:
   10294          429 :                             appendStringInfoString(buf, " IS NULL");
   10295          429 :                             break;
   10296          935 :                         case IS_NOT_NULL:
   10297          935 :                             appendStringInfoString(buf, " IS NOT NULL");
   10298          935 :                             break;
   10299            0 :                         default:
   10300            0 :                             elog(ERROR, "unrecognized nulltesttype: %d",
   10301              :                                  (int) ntest->nulltesttype);
   10302              :                     }
   10303              :                 }
   10304              :                 else
   10305              :                 {
   10306           15 :                     switch (ntest->nulltesttype)
   10307              :                     {
   10308            6 :                         case IS_NULL:
   10309            6 :                             appendStringInfoString(buf, " IS NOT DISTINCT FROM NULL");
   10310            6 :                             break;
   10311            9 :                         case IS_NOT_NULL:
   10312            9 :                             appendStringInfoString(buf, " IS DISTINCT FROM NULL");
   10313            9 :                             break;
   10314            0 :                         default:
   10315            0 :                             elog(ERROR, "unrecognized nulltesttype: %d",
   10316              :                                  (int) ntest->nulltesttype);
   10317              :                     }
   10318              :                 }
   10319         1379 :                 if (!PRETTY_PAREN(context))
   10320         1349 :                     appendStringInfoChar(buf, ')');
   10321              :             }
   10322         1379 :             break;
   10323              : 
   10324          156 :         case T_BooleanTest:
   10325              :             {
   10326          156 :                 BooleanTest *btest = (BooleanTest *) node;
   10327              : 
   10328          156 :                 if (!PRETTY_PAREN(context))
   10329          156 :                     appendStringInfoChar(buf, '(');
   10330          156 :                 get_rule_expr_paren((Node *) btest->arg, context, false, node);
   10331          156 :                 switch (btest->booltesttype)
   10332              :                 {
   10333           18 :                     case IS_TRUE:
   10334           18 :                         appendStringInfoString(buf, " IS TRUE");
   10335           18 :                         break;
   10336           69 :                     case IS_NOT_TRUE:
   10337           69 :                         appendStringInfoString(buf, " IS NOT TRUE");
   10338           69 :                         break;
   10339            0 :                     case IS_FALSE:
   10340            0 :                         appendStringInfoString(buf, " IS FALSE");
   10341            0 :                         break;
   10342           27 :                     case IS_NOT_FALSE:
   10343           27 :                         appendStringInfoString(buf, " IS NOT FALSE");
   10344           27 :                         break;
   10345           15 :                     case IS_UNKNOWN:
   10346           15 :                         appendStringInfoString(buf, " IS UNKNOWN");
   10347           15 :                         break;
   10348           27 :                     case IS_NOT_UNKNOWN:
   10349           27 :                         appendStringInfoString(buf, " IS NOT UNKNOWN");
   10350           27 :                         break;
   10351            0 :                     default:
   10352            0 :                         elog(ERROR, "unrecognized booltesttype: %d",
   10353              :                              (int) btest->booltesttype);
   10354              :                 }
   10355          156 :                 if (!PRETTY_PAREN(context))
   10356          156 :                     appendStringInfoChar(buf, ')');
   10357              :             }
   10358          156 :             break;
   10359              : 
   10360           49 :         case T_CoerceToDomain:
   10361              :             {
   10362           49 :                 CoerceToDomain *ctest = (CoerceToDomain *) node;
   10363           49 :                 Node       *arg = (Node *) ctest->arg;
   10364              : 
   10365           49 :                 if (ctest->coercionformat == COERCE_IMPLICIT_CAST &&
   10366           24 :                     !showimplicit)
   10367              :                 {
   10368              :                     /* don't show the implicit cast */
   10369           16 :                     get_rule_expr(arg, context, false);
   10370              :                 }
   10371              :                 else
   10372              :                 {
   10373           33 :                     get_coercion_expr(arg, context,
   10374              :                                       ctest->resulttype,
   10375              :                                       ctest->resulttypmod,
   10376              :                                       node);
   10377              :                 }
   10378              :             }
   10379           49 :             break;
   10380              : 
   10381          219 :         case T_CoerceToDomainValue:
   10382          219 :             appendStringInfoString(buf, "VALUE");
   10383          219 :             break;
   10384              : 
   10385           38 :         case T_SetToDefault:
   10386           38 :             appendStringInfoString(buf, "DEFAULT");
   10387           38 :             break;
   10388              : 
   10389           12 :         case T_CurrentOfExpr:
   10390              :             {
   10391           12 :                 CurrentOfExpr *cexpr = (CurrentOfExpr *) node;
   10392              : 
   10393           12 :                 if (cexpr->cursor_name)
   10394           12 :                     appendStringInfo(buf, "CURRENT OF %s",
   10395           12 :                                      quote_identifier(cexpr->cursor_name));
   10396              :                 else
   10397            0 :                     appendStringInfo(buf, "CURRENT OF $%d",
   10398              :                                      cexpr->cursor_param);
   10399              :             }
   10400           12 :             break;
   10401              : 
   10402            0 :         case T_NextValueExpr:
   10403              :             {
   10404            0 :                 NextValueExpr *nvexpr = (NextValueExpr *) node;
   10405              : 
   10406              :                 /*
   10407              :                  * This isn't exactly nextval(), but that seems close enough
   10408              :                  * for EXPLAIN's purposes.
   10409              :                  */
   10410            0 :                 appendStringInfoString(buf, "nextval(");
   10411            0 :                 simple_quote_literal(buf,
   10412            0 :                                      generate_relation_name(nvexpr->seqid,
   10413              :                                                             NIL));
   10414            0 :                 appendStringInfoChar(buf, ')');
   10415              :             }
   10416            0 :             break;
   10417              : 
   10418           15 :         case T_InferenceElem:
   10419              :             {
   10420           15 :                 InferenceElem *iexpr = (InferenceElem *) node;
   10421              :                 bool        save_varprefix;
   10422              :                 bool        need_parens;
   10423              : 
   10424              :                 /*
   10425              :                  * InferenceElem can only refer to target relation, so a
   10426              :                  * prefix is not useful, and indeed would cause parse errors.
   10427              :                  */
   10428           15 :                 save_varprefix = context->varprefix;
   10429           15 :                 context->varprefix = false;
   10430              : 
   10431              :                 /*
   10432              :                  * Parenthesize the element unless it's a simple Var or a bare
   10433              :                  * function call.  Follows pg_get_indexdef_worker().
   10434              :                  */
   10435           15 :                 need_parens = !IsA(iexpr->expr, Var);
   10436           15 :                 if (IsA(iexpr->expr, FuncExpr) &&
   10437            0 :                     ((FuncExpr *) iexpr->expr)->funcformat ==
   10438              :                     COERCE_EXPLICIT_CALL)
   10439            0 :                     need_parens = false;
   10440              : 
   10441           15 :                 if (need_parens)
   10442            0 :                     appendStringInfoChar(buf, '(');
   10443           15 :                 get_rule_expr((Node *) iexpr->expr,
   10444              :                               context, false);
   10445           15 :                 if (need_parens)
   10446            0 :                     appendStringInfoChar(buf, ')');
   10447              : 
   10448           15 :                 context->varprefix = save_varprefix;
   10449              : 
   10450           15 :                 if (iexpr->infercollid)
   10451            6 :                     appendStringInfo(buf, " COLLATE %s",
   10452              :                                      generate_collation_name(iexpr->infercollid));
   10453              : 
   10454              :                 /* Add the operator class name, if not default */
   10455           15 :                 if (iexpr->inferopclass)
   10456              :                 {
   10457            6 :                     Oid         inferopclass = iexpr->inferopclass;
   10458            6 :                     Oid         inferopcinputtype = get_opclass_input_type(iexpr->inferopclass);
   10459              : 
   10460            6 :                     get_opclass_name(inferopclass, inferopcinputtype, buf);
   10461              :                 }
   10462              :             }
   10463           15 :             break;
   10464              : 
   10465            6 :         case T_ReturningExpr:
   10466              :             {
   10467            6 :                 ReturningExpr *retExpr = (ReturningExpr *) node;
   10468              : 
   10469              :                 /*
   10470              :                  * We cannot see a ReturningExpr in rule deparsing, only while
   10471              :                  * EXPLAINing a query plan (ReturningExpr nodes are only ever
   10472              :                  * adding during query rewriting). Just display the expression
   10473              :                  * returned (an expanded view column).
   10474              :                  */
   10475            6 :                 get_rule_expr((Node *) retExpr->retexpr, context, showimplicit);
   10476              :             }
   10477            6 :             break;
   10478              : 
   10479         2365 :         case T_PartitionBoundSpec:
   10480              :             {
   10481         2365 :                 PartitionBoundSpec *spec = (PartitionBoundSpec *) node;
   10482              :                 ListCell   *cell;
   10483              :                 char       *sep;
   10484              : 
   10485         2365 :                 if (spec->is_default)
   10486              :                 {
   10487          132 :                     appendStringInfoString(buf, "DEFAULT");
   10488          132 :                     break;
   10489              :                 }
   10490              : 
   10491         2233 :                 switch (spec->strategy)
   10492              :                 {
   10493          153 :                     case PARTITION_STRATEGY_HASH:
   10494              :                         Assert(spec->modulus > 0 && spec->remainder >= 0);
   10495              :                         Assert(spec->modulus > spec->remainder);
   10496              : 
   10497          153 :                         appendStringInfoString(buf, "FOR VALUES");
   10498          153 :                         appendStringInfo(buf, " WITH (modulus %d, remainder %d)",
   10499              :                                          spec->modulus, spec->remainder);
   10500          153 :                         break;
   10501              : 
   10502          726 :                     case PARTITION_STRATEGY_LIST:
   10503              :                         Assert(spec->listdatums != NIL);
   10504              : 
   10505          726 :                         appendStringInfoString(buf, "FOR VALUES IN (");
   10506          726 :                         sep = "";
   10507         2013 :                         foreach(cell, spec->listdatums)
   10508              :                         {
   10509         1287 :                             Const      *val = lfirst_node(Const, cell);
   10510              : 
   10511         1287 :                             appendStringInfoString(buf, sep);
   10512         1287 :                             get_const_expr(val, context, -1);
   10513         1287 :                             sep = ", ";
   10514              :                         }
   10515              : 
   10516          726 :                         appendStringInfoChar(buf, ')');
   10517          726 :                         break;
   10518              : 
   10519         1354 :                     case PARTITION_STRATEGY_RANGE:
   10520              :                         Assert(spec->lowerdatums != NIL &&
   10521              :                                spec->upperdatums != NIL &&
   10522              :                                list_length(spec->lowerdatums) ==
   10523              :                                list_length(spec->upperdatums));
   10524              : 
   10525         1354 :                         appendStringInfo(buf, "FOR VALUES FROM %s TO %s",
   10526              :                                          get_range_partbound_string(spec->lowerdatums),
   10527              :                                          get_range_partbound_string(spec->upperdatums));
   10528         1354 :                         break;
   10529              : 
   10530            0 :                     default:
   10531            0 :                         elog(ERROR, "unrecognized partition strategy: %d",
   10532              :                              (int) spec->strategy);
   10533              :                         break;
   10534              :                 }
   10535              :             }
   10536         2233 :             break;
   10537              : 
   10538           75 :         case T_JsonValueExpr:
   10539              :             {
   10540           75 :                 JsonValueExpr *jve = (JsonValueExpr *) node;
   10541              : 
   10542           75 :                 get_rule_expr((Node *) jve->raw_expr, context, false);
   10543           75 :                 get_json_format(jve->format, context->buf);
   10544              :             }
   10545           75 :             break;
   10546              : 
   10547           93 :         case T_JsonConstructorExpr:
   10548           93 :             get_json_constructor((JsonConstructorExpr *) node, context, false);
   10549           93 :             break;
   10550              : 
   10551           30 :         case T_JsonIsPredicate:
   10552              :             {
   10553           30 :                 JsonIsPredicate *pred = (JsonIsPredicate *) node;
   10554              : 
   10555           30 :                 if (!PRETTY_PAREN(context))
   10556           15 :                     appendStringInfoChar(context->buf, '(');
   10557              : 
   10558           30 :                 get_rule_expr_paren(pred->expr, context, true, node);
   10559              : 
   10560           30 :                 appendStringInfoString(context->buf, " IS JSON");
   10561              : 
   10562              :                 /* TODO: handle FORMAT clause */
   10563              : 
   10564           30 :                 switch (pred->item_type)
   10565              :                 {
   10566            6 :                     case JS_TYPE_SCALAR:
   10567            6 :                         appendStringInfoString(context->buf, " SCALAR");
   10568            6 :                         break;
   10569            6 :                     case JS_TYPE_ARRAY:
   10570            6 :                         appendStringInfoString(context->buf, " ARRAY");
   10571            6 :                         break;
   10572            6 :                     case JS_TYPE_OBJECT:
   10573            6 :                         appendStringInfoString(context->buf, " OBJECT");
   10574            6 :                         break;
   10575           12 :                     default:
   10576           12 :                         break;
   10577              :                 }
   10578              : 
   10579           30 :                 if (pred->unique_keys)
   10580            6 :                     appendStringInfoString(context->buf, " WITH UNIQUE KEYS");
   10581              : 
   10582           30 :                 if (!PRETTY_PAREN(context))
   10583           15 :                     appendStringInfoChar(context->buf, ')');
   10584              :             }
   10585           30 :             break;
   10586              : 
   10587           30 :         case T_JsonExpr:
   10588              :             {
   10589           30 :                 JsonExpr   *jexpr = (JsonExpr *) node;
   10590              : 
   10591           30 :                 switch (jexpr->op)
   10592              :                 {
   10593            6 :                     case JSON_EXISTS_OP:
   10594            6 :                         appendStringInfoString(buf, "JSON_EXISTS(");
   10595            6 :                         break;
   10596           18 :                     case JSON_QUERY_OP:
   10597           18 :                         appendStringInfoString(buf, "JSON_QUERY(");
   10598           18 :                         break;
   10599            6 :                     case JSON_VALUE_OP:
   10600            6 :                         appendStringInfoString(buf, "JSON_VALUE(");
   10601            6 :                         break;
   10602            0 :                     default:
   10603            0 :                         elog(ERROR, "unrecognized JsonExpr op: %d",
   10604              :                              (int) jexpr->op);
   10605              :                 }
   10606              : 
   10607           30 :                 get_rule_expr(jexpr->formatted_expr, context, showimplicit);
   10608              : 
   10609           30 :                 appendStringInfoString(buf, ", ");
   10610              : 
   10611           30 :                 get_json_path_spec(jexpr->path_spec, context, showimplicit);
   10612              : 
   10613           30 :                 if (jexpr->passing_values)
   10614              :                 {
   10615              :                     ListCell   *lc1,
   10616              :                                *lc2;
   10617            6 :                     bool        needcomma = false;
   10618              : 
   10619            6 :                     appendStringInfoString(buf, " PASSING ");
   10620              : 
   10621           24 :                     forboth(lc1, jexpr->passing_names,
   10622              :                             lc2, jexpr->passing_values)
   10623              :                     {
   10624           18 :                         if (needcomma)
   10625           12 :                             appendStringInfoString(buf, ", ");
   10626           18 :                         needcomma = true;
   10627              : 
   10628           18 :                         get_rule_expr((Node *) lfirst(lc2), context, showimplicit);
   10629           18 :                         appendStringInfo(buf, " AS %s",
   10630           18 :                                          quote_identifier(lfirst_node(String, lc1)->sval));
   10631              :                     }
   10632              :                 }
   10633              : 
   10634           30 :                 if (jexpr->op != JSON_EXISTS_OP ||
   10635            6 :                     jexpr->returning->typid != BOOLOID)
   10636           24 :                     get_json_returning(jexpr->returning, context->buf,
   10637           24 :                                        jexpr->op == JSON_QUERY_OP);
   10638              : 
   10639           30 :                 get_json_expr_options(jexpr, context,
   10640           30 :                                       jexpr->op != JSON_EXISTS_OP ?
   10641              :                                       JSON_BEHAVIOR_NULL :
   10642              :                                       JSON_BEHAVIOR_FALSE);
   10643              : 
   10644           30 :                 appendStringInfoChar(buf, ')');
   10645              :             }
   10646           30 :             break;
   10647              : 
   10648         1377 :         case T_List:
   10649              :             {
   10650              :                 char       *sep;
   10651              :                 ListCell   *l;
   10652              : 
   10653         1377 :                 sep = "";
   10654         3892 :                 foreach(l, (List *) node)
   10655              :                 {
   10656         2515 :                     appendStringInfoString(buf, sep);
   10657         2515 :                     get_rule_expr((Node *) lfirst(l), context, showimplicit);
   10658         2515 :                     sep = ", ";
   10659              :                 }
   10660              :             }
   10661         1377 :             break;
   10662              : 
   10663           36 :         case T_TableFunc:
   10664           36 :             get_tablefunc((TableFunc *) node, context, showimplicit);
   10665           36 :             break;
   10666              : 
   10667            0 :         default:
   10668            0 :             elog(ERROR, "unrecognized node type: %d", (int) nodeTag(node));
   10669              :             break;
   10670              :     }
   10671              : }
   10672              : 
   10673              : /*
   10674              :  * get_rule_expr_toplevel       - Parse back a toplevel expression
   10675              :  *
   10676              :  * Same as get_rule_expr(), except that if the expr is just a Var, we pass
   10677              :  * istoplevel = true not false to get_variable().  This causes whole-row Vars
   10678              :  * to get printed with decoration that will prevent expansion of "*".
   10679              :  * We need to use this in contexts such as ROW() and VALUES(), where the
   10680              :  * parser would expand "foo.*" appearing at top level.  (In principle we'd
   10681              :  * use this in get_target_list() too, but that has additional worries about
   10682              :  * whether to print AS, so it needs to invoke get_variable() directly anyway.)
   10683              :  */
   10684              : static void
   10685         1570 : get_rule_expr_toplevel(Node *node, deparse_context *context,
   10686              :                        bool showimplicit)
   10687              : {
   10688         1570 :     if (node && IsA(node, Var))
   10689          649 :         (void) get_variable((Var *) node, 0, true, context);
   10690              :     else
   10691          921 :         get_rule_expr(node, context, showimplicit);
   10692         1570 : }
   10693              : 
   10694              : /*
   10695              :  * get_rule_list_toplevel       - Parse back a list of toplevel expressions
   10696              :  *
   10697              :  * Apply get_rule_expr_toplevel() to each element of a List.
   10698              :  *
   10699              :  * This adds commas between the expressions, but caller is responsible
   10700              :  * for printing surrounding decoration.
   10701              :  */
   10702              : static void
   10703          261 : get_rule_list_toplevel(List *lst, deparse_context *context,
   10704              :                        bool showimplicit)
   10705              : {
   10706              :     const char *sep;
   10707              :     ListCell   *lc;
   10708              : 
   10709          261 :     sep = "";
   10710          886 :     foreach(lc, lst)
   10711              :     {
   10712          625 :         Node       *e = (Node *) lfirst(lc);
   10713              : 
   10714          625 :         appendStringInfoString(context->buf, sep);
   10715          625 :         get_rule_expr_toplevel(e, context, showimplicit);
   10716          625 :         sep = ", ";
   10717              :     }
   10718          261 : }
   10719              : 
   10720              : /*
   10721              :  * get_rule_expr_funccall       - Parse back a function-call expression
   10722              :  *
   10723              :  * Same as get_rule_expr(), except that we guarantee that the output will
   10724              :  * look like a function call, or like one of the things the grammar treats as
   10725              :  * equivalent to a function call (see the func_expr_windowless production).
   10726              :  * This is needed in places where the grammar uses func_expr_windowless and
   10727              :  * you can't substitute a parenthesized a_expr.  If what we have isn't going
   10728              :  * to look like a function call, wrap it in a dummy CAST() expression, which
   10729              :  * will satisfy the grammar --- and, indeed, is likely what the user wrote to
   10730              :  * produce such a thing.
   10731              :  */
   10732              : static void
   10733          444 : get_rule_expr_funccall(Node *node, deparse_context *context,
   10734              :                        bool showimplicit)
   10735              : {
   10736          444 :     if (looks_like_function(node))
   10737          438 :         get_rule_expr(node, context, showimplicit);
   10738              :     else
   10739              :     {
   10740            6 :         StringInfo  buf = context->buf;
   10741              : 
   10742            6 :         appendStringInfoString(buf, "CAST(");
   10743              :         /* no point in showing any top-level implicit cast */
   10744            6 :         get_rule_expr(node, context, false);
   10745            6 :         appendStringInfo(buf, " AS %s)",
   10746              :                          format_type_with_typemod(exprType(node),
   10747              :                                                   exprTypmod(node)));
   10748              :     }
   10749          444 : }
   10750              : 
   10751              : /*
   10752              :  * Helper function to identify node types that satisfy func_expr_windowless.
   10753              :  * If in doubt, "false" is always a safe answer.
   10754              :  */
   10755              : static bool
   10756         1077 : looks_like_function(Node *node)
   10757              : {
   10758         1077 :     if (node == NULL)
   10759            0 :         return false;           /* probably shouldn't happen */
   10760         1077 :     switch (nodeTag(node))
   10761              :     {
   10762          458 :         case T_FuncExpr:
   10763              :             /* OK, unless it's going to deparse as a cast */
   10764          467 :             return (((FuncExpr *) node)->funcformat == COERCE_EXPLICIT_CALL ||
   10765            9 :                     ((FuncExpr *) node)->funcformat == COERCE_SQL_SYNTAX);
   10766           54 :         case T_NullIfExpr:
   10767              :         case T_CoalesceExpr:
   10768              :         case T_MinMaxExpr:
   10769              :         case T_SQLValueFunction:
   10770              :         case T_XmlExpr:
   10771              :         case T_JsonExpr:
   10772              :             /* these are all accepted by func_expr_common_subexpr */
   10773           54 :             return true;
   10774          565 :         default:
   10775          565 :             break;
   10776              :     }
   10777          565 :     return false;
   10778              : }
   10779              : 
   10780              : 
   10781              : /*
   10782              :  * get_oper_expr            - Parse back an OpExpr node
   10783              :  */
   10784              : static void
   10785        31363 : get_oper_expr(OpExpr *expr, deparse_context *context)
   10786              : {
   10787        31363 :     StringInfo  buf = context->buf;
   10788        31363 :     Oid         opno = expr->opno;
   10789        31363 :     List       *args = expr->args;
   10790              : 
   10791        31363 :     if (!PRETTY_PAREN(context))
   10792        30134 :         appendStringInfoChar(buf, '(');
   10793        31363 :     if (list_length(args) == 2)
   10794              :     {
   10795              :         /* binary operator */
   10796        31348 :         Node       *arg1 = (Node *) linitial(args);
   10797        31348 :         Node       *arg2 = (Node *) lsecond(args);
   10798              : 
   10799        31348 :         get_rule_expr_paren(arg1, context, true, (Node *) expr);
   10800        31348 :         appendStringInfo(buf, " %s ",
   10801              :                          generate_operator_name(opno,
   10802              :                                                 exprType(arg1),
   10803              :                                                 exprType(arg2)));
   10804        31348 :         get_rule_expr_paren(arg2, context, true, (Node *) expr);
   10805              :     }
   10806              :     else
   10807              :     {
   10808              :         /* prefix operator */
   10809           15 :         Node       *arg = (Node *) linitial(args);
   10810              : 
   10811           15 :         appendStringInfo(buf, "%s ",
   10812              :                          generate_operator_name(opno,
   10813              :                                                 InvalidOid,
   10814              :                                                 exprType(arg)));
   10815           15 :         get_rule_expr_paren(arg, context, true, (Node *) expr);
   10816              :     }
   10817        31363 :     if (!PRETTY_PAREN(context))
   10818        30134 :         appendStringInfoChar(buf, ')');
   10819        31363 : }
   10820              : 
   10821              : /*
   10822              :  * get_func_expr            - Parse back a FuncExpr node
   10823              :  */
   10824              : static void
   10825         6501 : get_func_expr(FuncExpr *expr, deparse_context *context,
   10826              :               bool showimplicit)
   10827              : {
   10828         6501 :     StringInfo  buf = context->buf;
   10829         6501 :     Oid         funcoid = expr->funcid;
   10830              :     Oid         argtypes[FUNC_MAX_ARGS];
   10831              :     int         nargs;
   10832              :     List       *argnames;
   10833              :     bool        use_variadic;
   10834              :     ListCell   *l;
   10835              : 
   10836              :     /*
   10837              :      * If the function call came from an implicit coercion, then just show the
   10838              :      * first argument --- unless caller wants to see implicit coercions.
   10839              :      */
   10840         6501 :     if (expr->funcformat == COERCE_IMPLICIT_CAST && !showimplicit)
   10841              :     {
   10842          672 :         get_rule_expr_paren((Node *) linitial(expr->args), context,
   10843              :                             false, (Node *) expr);
   10844         1655 :         return;
   10845              :     }
   10846              : 
   10847              :     /*
   10848              :      * If the function call came from a cast, then show the first argument
   10849              :      * plus an explicit cast operation.
   10850              :      */
   10851         5829 :     if (expr->funcformat == COERCE_EXPLICIT_CAST ||
   10852         5480 :         expr->funcformat == COERCE_IMPLICIT_CAST)
   10853              :     {
   10854          896 :         Node       *arg = linitial(expr->args);
   10855          896 :         Oid         rettype = expr->funcresulttype;
   10856              :         int32       coercedTypmod;
   10857              : 
   10858              :         /* Get the typmod if this is a length-coercion function */
   10859          896 :         (void) exprIsLengthCoercion((Node *) expr, &coercedTypmod);
   10860              : 
   10861          896 :         get_coercion_expr(arg, context,
   10862              :                           rettype, coercedTypmod,
   10863              :                           (Node *) expr);
   10864              : 
   10865          896 :         return;
   10866              :     }
   10867              : 
   10868              :     /*
   10869              :      * If the function was called using one of the SQL spec's random special
   10870              :      * syntaxes, try to reproduce that.  If we don't recognize the function,
   10871              :      * fall through.
   10872              :      */
   10873         4933 :     if (expr->funcformat == COERCE_SQL_SYNTAX)
   10874              :     {
   10875           90 :         if (get_func_sql_syntax(expr, context))
   10876           87 :             return;
   10877              :     }
   10878              : 
   10879              :     /*
   10880              :      * Normal function: display as proname(args).  First we need to extract
   10881              :      * the argument datatypes.
   10882              :      */
   10883         4846 :     if (list_length(expr->args) > FUNC_MAX_ARGS)
   10884            0 :         ereport(ERROR,
   10885              :                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
   10886              :                  errmsg("too many arguments")));
   10887         4846 :     nargs = 0;
   10888         4846 :     argnames = NIL;
   10889        10101 :     foreach(l, expr->args)
   10890              :     {
   10891         5255 :         Node       *arg = (Node *) lfirst(l);
   10892              : 
   10893         5255 :         if (IsA(arg, NamedArgExpr))
   10894           15 :             argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
   10895         5255 :         argtypes[nargs] = exprType(arg);
   10896         5255 :         nargs++;
   10897              :     }
   10898              : 
   10899         4846 :     appendStringInfo(buf, "%s(",
   10900              :                      generate_function_name(funcoid, nargs,
   10901              :                                             argnames, argtypes,
   10902         4846 :                                             expr->funcvariadic,
   10903              :                                             &use_variadic,
   10904         4846 :                                             context->inGroupBy));
   10905         4846 :     nargs = 0;
   10906        10101 :     foreach(l, expr->args)
   10907              :     {
   10908         5255 :         if (nargs++ > 0)
   10909         1008 :             appendStringInfoString(buf, ", ");
   10910         5255 :         if (use_variadic && lnext(expr->args, l) == NULL)
   10911            6 :             appendStringInfoString(buf, "VARIADIC ");
   10912         5255 :         get_rule_expr((Node *) lfirst(l), context, true);
   10913              :     }
   10914         4846 :     appendStringInfoChar(buf, ')');
   10915              : }
   10916              : 
   10917              : /*
   10918              :  * get_agg_expr         - Parse back an Aggref node
   10919              :  */
   10920              : static void
   10921         2385 : get_agg_expr(Aggref *aggref, deparse_context *context,
   10922              :              Aggref *original_aggref)
   10923              : {
   10924         2385 :     get_agg_expr_helper(aggref, context, original_aggref, NULL, NULL,
   10925              :                         false);
   10926         2385 : }
   10927              : 
   10928              : /*
   10929              :  * get_agg_expr_helper      - subroutine for get_agg_expr and
   10930              :  *                          get_json_agg_constructor
   10931              :  */
   10932              : static void
   10933         2412 : get_agg_expr_helper(Aggref *aggref, deparse_context *context,
   10934              :                     Aggref *original_aggref, const char *funcname,
   10935              :                     const char *options, bool is_json_objectagg)
   10936              : {
   10937         2412 :     StringInfo  buf = context->buf;
   10938              :     Oid         argtypes[FUNC_MAX_ARGS];
   10939              :     int         nargs;
   10940         2412 :     bool        use_variadic = false;
   10941              : 
   10942              :     /*
   10943              :      * For a combining aggregate, we look up and deparse the corresponding
   10944              :      * partial aggregate instead.  This is necessary because our input
   10945              :      * argument list has been replaced; the new argument list always has just
   10946              :      * one element, which will point to a partial Aggref that supplies us with
   10947              :      * transition states to combine.
   10948              :      */
   10949         2412 :     if (DO_AGGSPLIT_COMBINE(aggref->aggsplit))
   10950              :     {
   10951              :         TargetEntry *tle;
   10952              : 
   10953              :         Assert(list_length(aggref->args) == 1);
   10954          391 :         tle = linitial_node(TargetEntry, aggref->args);
   10955          391 :         resolve_special_varno((Node *) tle->expr, context,
   10956              :                               get_agg_combine_expr, original_aggref);
   10957          391 :         return;
   10958              :     }
   10959              : 
   10960              :     /*
   10961              :      * Mark as PARTIAL, if appropriate.  We look to the original aggref so as
   10962              :      * to avoid printing this when recursing from the code just above.
   10963              :      */
   10964         2021 :     if (DO_AGGSPLIT_SKIPFINAL(original_aggref->aggsplit))
   10965          862 :         appendStringInfoString(buf, "PARTIAL ");
   10966              : 
   10967              :     /* Extract the argument types as seen by the parser */
   10968         2021 :     nargs = get_aggregate_argtypes(aggref, argtypes);
   10969              : 
   10970         2021 :     if (!funcname)
   10971         1994 :         funcname = generate_function_name(aggref->aggfnoid, nargs, NIL,
   10972         1994 :                                           argtypes, aggref->aggvariadic,
   10973              :                                           &use_variadic,
   10974         1994 :                                           context->inGroupBy);
   10975              : 
   10976              :     /* Print the aggregate name, schema-qualified if needed */
   10977         2021 :     appendStringInfo(buf, "%s(%s", funcname,
   10978         2021 :                      (aggref->aggdistinct != NIL) ? "DISTINCT " : "");
   10979              : 
   10980         2021 :     if (AGGKIND_IS_ORDERED_SET(aggref->aggkind))
   10981              :     {
   10982              :         /*
   10983              :          * Ordered-set aggregates do not use "*" syntax.  Also, we needn't
   10984              :          * worry about inserting VARIADIC.  So we can just dump the direct
   10985              :          * args as-is.
   10986              :          */
   10987              :         Assert(!aggref->aggvariadic);
   10988           14 :         get_rule_expr((Node *) aggref->aggdirectargs, context, true);
   10989              :         Assert(aggref->aggorder != NIL);
   10990           14 :         appendStringInfoString(buf, ") WITHIN GROUP (ORDER BY ");
   10991           14 :         get_rule_orderby(aggref->aggorder, aggref->args, false, context);
   10992              :     }
   10993              :     else
   10994              :     {
   10995              :         /* aggstar can be set only in zero-argument aggregates */
   10996         2007 :         if (aggref->aggstar)
   10997          597 :             appendStringInfoChar(buf, '*');
   10998              :         else
   10999              :         {
   11000              :             ListCell   *l;
   11001              :             int         i;
   11002              : 
   11003         1410 :             i = 0;
   11004         2914 :             foreach(l, aggref->args)
   11005              :             {
   11006         1504 :                 TargetEntry *tle = (TargetEntry *) lfirst(l);
   11007         1504 :                 Node       *arg = (Node *) tle->expr;
   11008              : 
   11009              :                 Assert(!IsA(arg, NamedArgExpr));
   11010         1504 :                 if (tle->resjunk)
   11011           25 :                     continue;
   11012         1479 :                 if (i++ > 0)
   11013              :                 {
   11014           69 :                     if (is_json_objectagg)
   11015              :                     {
   11016              :                         /*
   11017              :                          * the ABSENT ON NULL and WITH UNIQUE args are printed
   11018              :                          * separately, so ignore them here
   11019              :                          */
   11020           15 :                         if (i > 2)
   11021            0 :                             break;
   11022              : 
   11023           15 :                         appendStringInfoString(buf, " : ");
   11024              :                     }
   11025              :                     else
   11026           54 :                         appendStringInfoString(buf, ", ");
   11027              :                 }
   11028         1479 :                 if (use_variadic && i == nargs)
   11029            4 :                     appendStringInfoString(buf, "VARIADIC ");
   11030         1479 :                 get_rule_expr(arg, context, true);
   11031              :             }
   11032              :         }
   11033              : 
   11034         2007 :         if (aggref->aggorder != NIL)
   11035              :         {
   11036           44 :             appendStringInfoString(buf, " ORDER BY ");
   11037           44 :             get_rule_orderby(aggref->aggorder, aggref->args, false, context);
   11038              :         }
   11039              :     }
   11040              : 
   11041         2021 :     if (options)
   11042           27 :         appendStringInfoString(buf, options);
   11043              : 
   11044         2021 :     if (aggref->aggfilter != NULL)
   11045              :     {
   11046           23 :         appendStringInfoString(buf, ") FILTER (WHERE ");
   11047           23 :         get_rule_expr((Node *) aggref->aggfilter, context, false);
   11048              :     }
   11049              : 
   11050         2021 :     appendStringInfoChar(buf, ')');
   11051              : }
   11052              : 
   11053              : /*
   11054              :  * This is a helper function for get_agg_expr().  It's used when we deparse
   11055              :  * a combining Aggref; resolve_special_varno locates the corresponding partial
   11056              :  * Aggref and then calls this.
   11057              :  */
   11058              : static void
   11059          391 : get_agg_combine_expr(Node *node, deparse_context *context, void *callback_arg)
   11060              : {
   11061              :     Aggref     *aggref;
   11062          391 :     Aggref     *original_aggref = callback_arg;
   11063              : 
   11064          391 :     if (!IsA(node, Aggref))
   11065            0 :         elog(ERROR, "combining Aggref does not point to an Aggref");
   11066              : 
   11067          391 :     aggref = (Aggref *) node;
   11068          391 :     get_agg_expr(aggref, context, original_aggref);
   11069          391 : }
   11070              : 
   11071              : /*
   11072              :  * get_windowfunc_expr  - Parse back a WindowFunc node
   11073              :  */
   11074              : static void
   11075          162 : get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context)
   11076              : {
   11077          162 :     get_windowfunc_expr_helper(wfunc, context, NULL, NULL, false);
   11078          162 : }
   11079              : 
   11080              : 
   11081              : /*
   11082              :  * get_windowfunc_expr_helper   - subroutine for get_windowfunc_expr and
   11083              :  *                              get_json_agg_constructor
   11084              :  */
   11085              : static void
   11086          168 : get_windowfunc_expr_helper(WindowFunc *wfunc, deparse_context *context,
   11087              :                            const char *funcname, const char *options,
   11088              :                            bool is_json_objectagg)
   11089              : {
   11090          168 :     StringInfo  buf = context->buf;
   11091              :     Oid         argtypes[FUNC_MAX_ARGS];
   11092              :     int         nargs;
   11093              :     List       *argnames;
   11094              :     ListCell   *l;
   11095              : 
   11096          168 :     if (list_length(wfunc->args) > FUNC_MAX_ARGS)
   11097            0 :         ereport(ERROR,
   11098              :                 (errcode(ERRCODE_TOO_MANY_ARGUMENTS),
   11099              :                  errmsg("too many arguments")));
   11100          168 :     nargs = 0;
   11101          168 :     argnames = NIL;
   11102          285 :     foreach(l, wfunc->args)
   11103              :     {
   11104          117 :         Node       *arg = (Node *) lfirst(l);
   11105              : 
   11106          117 :         if (IsA(arg, NamedArgExpr))
   11107            0 :             argnames = lappend(argnames, ((NamedArgExpr *) arg)->name);
   11108          117 :         argtypes[nargs] = exprType(arg);
   11109          117 :         nargs++;
   11110              :     }
   11111              : 
   11112          168 :     if (!funcname)
   11113          162 :         funcname = generate_function_name(wfunc->winfnoid, nargs, argnames,
   11114              :                                           argtypes, false, NULL,
   11115          162 :                                           context->inGroupBy);
   11116              : 
   11117          168 :     appendStringInfo(buf, "%s(", funcname);
   11118              : 
   11119              :     /* winstar can be set only in zero-argument aggregates */
   11120          168 :     if (wfunc->winstar)
   11121           12 :         appendStringInfoChar(buf, '*');
   11122              :     else
   11123              :     {
   11124          156 :         if (is_json_objectagg)
   11125              :         {
   11126            3 :             get_rule_expr((Node *) linitial(wfunc->args), context, false);
   11127            3 :             appendStringInfoString(buf, " : ");
   11128            3 :             get_rule_expr((Node *) lsecond(wfunc->args), context, false);
   11129              :         }
   11130              :         else
   11131          153 :             get_rule_expr((Node *) wfunc->args, context, true);
   11132              :     }
   11133              : 
   11134          168 :     if (options)
   11135            6 :         appendStringInfoString(buf, options);
   11136              : 
   11137          168 :     if (wfunc->aggfilter != NULL)
   11138              :     {
   11139            0 :         appendStringInfoString(buf, ") FILTER (WHERE ");
   11140            0 :         get_rule_expr((Node *) wfunc->aggfilter, context, false);
   11141              :     }
   11142              : 
   11143          168 :     appendStringInfoString(buf, ") ");
   11144              : 
   11145          168 :     if (wfunc->ignore_nulls == PARSER_IGNORE_NULLS)
   11146            3 :         appendStringInfoString(buf, "IGNORE NULLS ");
   11147              : 
   11148          168 :     appendStringInfoString(buf, "OVER ");
   11149              : 
   11150          168 :     if (context->windowClause)
   11151              :     {
   11152              :         /* Query-decompilation case: search the windowClause list */
   11153           30 :         foreach(l, context->windowClause)
   11154              :         {
   11155           30 :             WindowClause *wc = (WindowClause *) lfirst(l);
   11156              : 
   11157           30 :             if (wc->winref == wfunc->winref)
   11158              :             {
   11159           30 :                 if (wc->name)
   11160            9 :                     appendStringInfoString(buf, quote_identifier(wc->name));
   11161              :                 else
   11162           21 :                     get_rule_windowspec(wc, context->targetList, context);
   11163           30 :                 break;
   11164              :             }
   11165              :         }
   11166           30 :         if (l == NULL)
   11167            0 :             elog(ERROR, "could not find window clause for winref %u",
   11168              :                  wfunc->winref);
   11169              :     }
   11170              :     else
   11171              :     {
   11172              :         /*
   11173              :          * In EXPLAIN, search the namespace stack for a matching WindowAgg
   11174              :          * node (probably it's always the first entry), and print winname.
   11175              :          */
   11176          138 :         foreach(l, context->namespaces)
   11177              :         {
   11178          138 :             deparse_namespace *dpns = (deparse_namespace *) lfirst(l);
   11179              : 
   11180          138 :             if (dpns->plan && IsA(dpns->plan, WindowAgg))
   11181              :             {
   11182          138 :                 WindowAgg  *wagg = (WindowAgg *) dpns->plan;
   11183              : 
   11184          138 :                 if (wagg->winref == wfunc->winref)
   11185              :                 {
   11186          138 :                     appendStringInfoString(buf, quote_identifier(wagg->winname));
   11187          138 :                     break;
   11188              :                 }
   11189              :             }
   11190              :         }
   11191          138 :         if (l == NULL)
   11192            0 :             elog(ERROR, "could not find window clause for winref %u",
   11193              :                  wfunc->winref);
   11194              :     }
   11195          168 : }
   11196              : 
   11197              : /*
   11198              :  * get_func_sql_syntax      - Parse back a SQL-syntax function call
   11199              :  *
   11200              :  * Returns true if we successfully deparsed, false if we did not
   11201              :  * recognize the function.
   11202              :  */
   11203              : static bool
   11204           90 : get_func_sql_syntax(FuncExpr *expr, deparse_context *context)
   11205              : {
   11206           90 :     StringInfo  buf = context->buf;
   11207           90 :     Oid         funcoid = expr->funcid;
   11208              : 
   11209           90 :     switch (funcoid)
   11210              :     {
   11211           12 :         case F_TIMEZONE_INTERVAL_TIMESTAMP:
   11212              :         case F_TIMEZONE_INTERVAL_TIMESTAMPTZ:
   11213              :         case F_TIMEZONE_INTERVAL_TIMETZ:
   11214              :         case F_TIMEZONE_TEXT_TIMESTAMP:
   11215              :         case F_TIMEZONE_TEXT_TIMESTAMPTZ:
   11216              :         case F_TIMEZONE_TEXT_TIMETZ:
   11217              :             /* AT TIME ZONE ... note reversed argument order */
   11218           12 :             appendStringInfoChar(buf, '(');
   11219           12 :             get_rule_expr_paren((Node *) lsecond(expr->args), context, false,
   11220              :                                 (Node *) expr);
   11221           12 :             appendStringInfoString(buf, " AT TIME ZONE ");
   11222           12 :             get_rule_expr_paren((Node *) linitial(expr->args), context, false,
   11223              :                                 (Node *) expr);
   11224           12 :             appendStringInfoChar(buf, ')');
   11225           12 :             return true;
   11226              : 
   11227            9 :         case F_TIMEZONE_TIMESTAMP:
   11228              :         case F_TIMEZONE_TIMESTAMPTZ:
   11229              :         case F_TIMEZONE_TIMETZ:
   11230              :             /* AT LOCAL */
   11231            9 :             appendStringInfoChar(buf, '(');
   11232            9 :             get_rule_expr_paren((Node *) linitial(expr->args), context, false,
   11233              :                                 (Node *) expr);
   11234            9 :             appendStringInfoString(buf, " AT LOCAL)");
   11235            9 :             return true;
   11236              : 
   11237            3 :         case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_INTERVAL:
   11238              :         case F_OVERLAPS_TIMESTAMPTZ_INTERVAL_TIMESTAMPTZ_TIMESTAMPTZ:
   11239              :         case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_INTERVAL:
   11240              :         case F_OVERLAPS_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ_TIMESTAMPTZ:
   11241              :         case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_INTERVAL:
   11242              :         case F_OVERLAPS_TIMESTAMP_INTERVAL_TIMESTAMP_TIMESTAMP:
   11243              :         case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_INTERVAL:
   11244              :         case F_OVERLAPS_TIMESTAMP_TIMESTAMP_TIMESTAMP_TIMESTAMP:
   11245              :         case F_OVERLAPS_TIMETZ_TIMETZ_TIMETZ_TIMETZ:
   11246              :         case F_OVERLAPS_TIME_INTERVAL_TIME_INTERVAL:
   11247              :         case F_OVERLAPS_TIME_INTERVAL_TIME_TIME:
   11248              :         case F_OVERLAPS_TIME_TIME_TIME_INTERVAL:
   11249              :         case F_OVERLAPS_TIME_TIME_TIME_TIME:
   11250              :             /* (x1, x2) OVERLAPS (y1, y2) */
   11251            3 :             appendStringInfoString(buf, "((");
   11252            3 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11253            3 :             appendStringInfoString(buf, ", ");
   11254            3 :             get_rule_expr((Node *) lsecond(expr->args), context, false);
   11255            3 :             appendStringInfoString(buf, ") OVERLAPS (");
   11256            3 :             get_rule_expr((Node *) lthird(expr->args), context, false);
   11257            3 :             appendStringInfoString(buf, ", ");
   11258            3 :             get_rule_expr((Node *) lfourth(expr->args), context, false);
   11259            3 :             appendStringInfoString(buf, "))");
   11260            3 :             return true;
   11261              : 
   11262            9 :         case F_EXTRACT_TEXT_DATE:
   11263              :         case F_EXTRACT_TEXT_TIME:
   11264              :         case F_EXTRACT_TEXT_TIMETZ:
   11265              :         case F_EXTRACT_TEXT_TIMESTAMP:
   11266              :         case F_EXTRACT_TEXT_TIMESTAMPTZ:
   11267              :         case F_EXTRACT_TEXT_INTERVAL:
   11268              :             /* EXTRACT (x FROM y) */
   11269            9 :             appendStringInfoString(buf, "EXTRACT(");
   11270              :             {
   11271            9 :                 Const      *con = (Const *) linitial(expr->args);
   11272              : 
   11273              :                 Assert(IsA(con, Const) &&
   11274              :                        con->consttype == TEXTOID &&
   11275              :                        !con->constisnull);
   11276            9 :                 appendStringInfoString(buf, TextDatumGetCString(con->constvalue));
   11277              :             }
   11278            9 :             appendStringInfoString(buf, " FROM ");
   11279            9 :             get_rule_expr((Node *) lsecond(expr->args), context, false);
   11280            9 :             appendStringInfoChar(buf, ')');
   11281            9 :             return true;
   11282              : 
   11283            6 :         case F_IS_NORMALIZED:
   11284              :             /* IS xxx NORMALIZED */
   11285            6 :             appendStringInfoChar(buf, '(');
   11286            6 :             get_rule_expr_paren((Node *) linitial(expr->args), context, false,
   11287              :                                 (Node *) expr);
   11288            6 :             appendStringInfoString(buf, " IS");
   11289            6 :             if (list_length(expr->args) == 2)
   11290              :             {
   11291            3 :                 Const      *con = (Const *) lsecond(expr->args);
   11292              : 
   11293              :                 Assert(IsA(con, Const) &&
   11294              :                        con->consttype == TEXTOID &&
   11295              :                        !con->constisnull);
   11296            3 :                 appendStringInfo(buf, " %s",
   11297            3 :                                  TextDatumGetCString(con->constvalue));
   11298              :             }
   11299            6 :             appendStringInfoString(buf, " NORMALIZED)");
   11300            6 :             return true;
   11301              : 
   11302            3 :         case F_PG_COLLATION_FOR:
   11303              :             /* COLLATION FOR */
   11304            3 :             appendStringInfoString(buf, "COLLATION FOR (");
   11305            3 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11306            3 :             appendStringInfoChar(buf, ')');
   11307            3 :             return true;
   11308              : 
   11309            6 :         case F_NORMALIZE:
   11310              :             /* NORMALIZE() */
   11311            6 :             appendStringInfoString(buf, "NORMALIZE(");
   11312            6 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11313            6 :             if (list_length(expr->args) == 2)
   11314              :             {
   11315            3 :                 Const      *con = (Const *) lsecond(expr->args);
   11316              : 
   11317              :                 Assert(IsA(con, Const) &&
   11318              :                        con->consttype == TEXTOID &&
   11319              :                        !con->constisnull);
   11320            3 :                 appendStringInfo(buf, ", %s",
   11321            3 :                                  TextDatumGetCString(con->constvalue));
   11322              :             }
   11323            6 :             appendStringInfoChar(buf, ')');
   11324            6 :             return true;
   11325              : 
   11326            6 :         case F_OVERLAY_BIT_BIT_INT4:
   11327              :         case F_OVERLAY_BIT_BIT_INT4_INT4:
   11328              :         case F_OVERLAY_BYTEA_BYTEA_INT4:
   11329              :         case F_OVERLAY_BYTEA_BYTEA_INT4_INT4:
   11330              :         case F_OVERLAY_TEXT_TEXT_INT4:
   11331              :         case F_OVERLAY_TEXT_TEXT_INT4_INT4:
   11332              :             /* OVERLAY() */
   11333            6 :             appendStringInfoString(buf, "OVERLAY(");
   11334            6 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11335            6 :             appendStringInfoString(buf, " PLACING ");
   11336            6 :             get_rule_expr((Node *) lsecond(expr->args), context, false);
   11337            6 :             appendStringInfoString(buf, " FROM ");
   11338            6 :             get_rule_expr((Node *) lthird(expr->args), context, false);
   11339            6 :             if (list_length(expr->args) == 4)
   11340              :             {
   11341            3 :                 appendStringInfoString(buf, " FOR ");
   11342            3 :                 get_rule_expr((Node *) lfourth(expr->args), context, false);
   11343              :             }
   11344            6 :             appendStringInfoChar(buf, ')');
   11345            6 :             return true;
   11346              : 
   11347            3 :         case F_POSITION_BIT_BIT:
   11348              :         case F_POSITION_BYTEA_BYTEA:
   11349              :         case F_POSITION_TEXT_TEXT:
   11350              :             /* POSITION() ... extra parens since args are b_expr not a_expr */
   11351            3 :             appendStringInfoString(buf, "POSITION((");
   11352            3 :             get_rule_expr((Node *) lsecond(expr->args), context, false);
   11353            3 :             appendStringInfoString(buf, ") IN (");
   11354            3 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11355            3 :             appendStringInfoString(buf, "))");
   11356            3 :             return true;
   11357              : 
   11358            3 :         case F_SUBSTRING_BIT_INT4:
   11359              :         case F_SUBSTRING_BIT_INT4_INT4:
   11360              :         case F_SUBSTRING_BYTEA_INT4:
   11361              :         case F_SUBSTRING_BYTEA_INT4_INT4:
   11362              :         case F_SUBSTRING_TEXT_INT4:
   11363              :         case F_SUBSTRING_TEXT_INT4_INT4:
   11364              :             /* SUBSTRING FROM/FOR (i.e., integer-position variants) */
   11365            3 :             appendStringInfoString(buf, "SUBSTRING(");
   11366            3 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11367            3 :             appendStringInfoString(buf, " FROM ");
   11368            3 :             get_rule_expr((Node *) lsecond(expr->args), context, false);
   11369            3 :             if (list_length(expr->args) == 3)
   11370              :             {
   11371            3 :                 appendStringInfoString(buf, " FOR ");
   11372            3 :                 get_rule_expr((Node *) lthird(expr->args), context, false);
   11373              :             }
   11374            3 :             appendStringInfoChar(buf, ')');
   11375            3 :             return true;
   11376              : 
   11377            3 :         case F_SUBSTRING_TEXT_TEXT_TEXT:
   11378              :             /* SUBSTRING SIMILAR/ESCAPE */
   11379            3 :             appendStringInfoString(buf, "SUBSTRING(");
   11380            3 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11381            3 :             appendStringInfoString(buf, " SIMILAR ");
   11382            3 :             get_rule_expr((Node *) lsecond(expr->args), context, false);
   11383            3 :             appendStringInfoString(buf, " ESCAPE ");
   11384            3 :             get_rule_expr((Node *) lthird(expr->args), context, false);
   11385            3 :             appendStringInfoChar(buf, ')');
   11386            3 :             return true;
   11387              : 
   11388            6 :         case F_BTRIM_BYTEA_BYTEA:
   11389              :         case F_BTRIM_TEXT:
   11390              :         case F_BTRIM_TEXT_TEXT:
   11391              :             /* TRIM() */
   11392            6 :             appendStringInfoString(buf, "TRIM(BOTH");
   11393            6 :             if (list_length(expr->args) == 2)
   11394              :             {
   11395            6 :                 appendStringInfoChar(buf, ' ');
   11396            6 :                 get_rule_expr((Node *) lsecond(expr->args), context, false);
   11397              :             }
   11398            6 :             appendStringInfoString(buf, " FROM ");
   11399            6 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11400            6 :             appendStringInfoChar(buf, ')');
   11401            6 :             return true;
   11402              : 
   11403            6 :         case F_LTRIM_BYTEA_BYTEA:
   11404              :         case F_LTRIM_TEXT:
   11405              :         case F_LTRIM_TEXT_TEXT:
   11406              :             /* TRIM() */
   11407            6 :             appendStringInfoString(buf, "TRIM(LEADING");
   11408            6 :             if (list_length(expr->args) == 2)
   11409              :             {
   11410            6 :                 appendStringInfoChar(buf, ' ');
   11411            6 :                 get_rule_expr((Node *) lsecond(expr->args), context, false);
   11412              :             }
   11413            6 :             appendStringInfoString(buf, " FROM ");
   11414            6 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11415            6 :             appendStringInfoChar(buf, ')');
   11416            6 :             return true;
   11417              : 
   11418            6 :         case F_RTRIM_BYTEA_BYTEA:
   11419              :         case F_RTRIM_TEXT:
   11420              :         case F_RTRIM_TEXT_TEXT:
   11421              :             /* TRIM() */
   11422            6 :             appendStringInfoString(buf, "TRIM(TRAILING");
   11423            6 :             if (list_length(expr->args) == 2)
   11424              :             {
   11425            3 :                 appendStringInfoChar(buf, ' ');
   11426            3 :                 get_rule_expr((Node *) lsecond(expr->args), context, false);
   11427              :             }
   11428            6 :             appendStringInfoString(buf, " FROM ");
   11429            6 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11430            6 :             appendStringInfoChar(buf, ')');
   11431            6 :             return true;
   11432              : 
   11433            6 :         case F_SYSTEM_USER:
   11434            6 :             appendStringInfoString(buf, "SYSTEM_USER");
   11435            6 :             return true;
   11436              : 
   11437            0 :         case F_XMLEXISTS:
   11438              :             /* XMLEXISTS ... extra parens because args are c_expr */
   11439            0 :             appendStringInfoString(buf, "XMLEXISTS((");
   11440            0 :             get_rule_expr((Node *) linitial(expr->args), context, false);
   11441            0 :             appendStringInfoString(buf, ") PASSING (");
   11442            0 :             get_rule_expr((Node *) lsecond(expr->args), context, false);
   11443            0 :             appendStringInfoString(buf, "))");
   11444            0 :             return true;
   11445              :     }
   11446            3 :     return false;
   11447              : }
   11448              : 
   11449              : /* ----------
   11450              :  * get_coercion_expr
   11451              :  *
   11452              :  *  Make a string representation of a value coerced to a specific type
   11453              :  * ----------
   11454              :  */
   11455              : static void
   11456         2691 : get_coercion_expr(Node *arg, deparse_context *context,
   11457              :                   Oid resulttype, int32 resulttypmod,
   11458              :                   Node *parentNode)
   11459              : {
   11460         2691 :     StringInfo  buf = context->buf;
   11461              : 
   11462              :     /*
   11463              :      * Since parse_coerce.c doesn't immediately collapse application of
   11464              :      * length-coercion functions to constants, what we'll typically see in
   11465              :      * such cases is a Const with typmod -1 and a length-coercion function
   11466              :      * right above it.  Avoid generating redundant output. However, beware of
   11467              :      * suppressing casts when the user actually wrote something like
   11468              :      * 'foo'::text::char(3).
   11469              :      *
   11470              :      * Note: it might seem that we are missing the possibility of needing to
   11471              :      * print a COLLATE clause for such a Const.  However, a Const could only
   11472              :      * have nondefault collation in a post-constant-folding tree, in which the
   11473              :      * length coercion would have been folded too.  See also the special
   11474              :      * handling of CollateExpr in coerce_to_target_type(): any collation
   11475              :      * marking will be above the coercion node, not below it.
   11476              :      */
   11477         2691 :     if (arg && IsA(arg, Const) &&
   11478          328 :         ((Const *) arg)->consttype == resulttype &&
   11479           12 :         ((Const *) arg)->consttypmod == -1)
   11480              :     {
   11481              :         /* Show the constant without normal ::typename decoration */
   11482           12 :         get_const_expr((Const *) arg, context, -1);
   11483              :     }
   11484              :     else
   11485              :     {
   11486         2679 :         if (!PRETTY_PAREN(context))
   11487         2488 :             appendStringInfoChar(buf, '(');
   11488         2679 :         get_rule_expr_paren(arg, context, false, parentNode);
   11489         2679 :         if (!PRETTY_PAREN(context))
   11490         2488 :             appendStringInfoChar(buf, ')');
   11491              :     }
   11492              : 
   11493              :     /*
   11494              :      * Never emit resulttype(arg) functional notation. A pg_proc entry could
   11495              :      * take precedence, and a resulttype in pg_temp would require schema
   11496              :      * qualification that format_type_with_typemod() would usually omit. We've
   11497              :      * standardized on arg::resulttype, but CAST(arg AS resulttype) notation
   11498              :      * would work fine.
   11499              :      */
   11500         2691 :     appendStringInfo(buf, "::%s",
   11501              :                      format_type_with_typemod(resulttype, resulttypmod));
   11502         2691 : }
   11503              : 
   11504              : /* ----------
   11505              :  * get_const_expr
   11506              :  *
   11507              :  *  Make a string representation of a Const
   11508              :  *
   11509              :  * showtype can be -1 to never show "::typename" decoration, or +1 to always
   11510              :  * show it, or 0 to show it only if the constant wouldn't be assumed to be
   11511              :  * the right type by default.
   11512              :  *
   11513              :  * If the Const's collation isn't default for its type, show that too.
   11514              :  * We mustn't do this when showtype is -1 (since that means the caller will
   11515              :  * print "::typename", and we can't put a COLLATE clause in between).  It's
   11516              :  * caller's responsibility that collation isn't missed in such cases.
   11517              :  * ----------
   11518              :  */
   11519              : static void
   11520        36463 : get_const_expr(Const *constval, deparse_context *context, int showtype)
   11521              : {
   11522        36463 :     StringInfo  buf = context->buf;
   11523              :     Oid         typoutput;
   11524              :     bool        typIsVarlena;
   11525              :     char       *extval;
   11526        36463 :     bool        needlabel = false;
   11527              : 
   11528        36463 :     if (constval->constisnull)
   11529              :     {
   11530              :         /*
   11531              :          * Always label the type of a NULL constant to prevent misdecisions
   11532              :          * about type when reparsing.
   11533              :          */
   11534          626 :         appendStringInfoString(buf, "NULL");
   11535          626 :         if (showtype >= 0)
   11536              :         {
   11537          599 :             appendStringInfo(buf, "::%s",
   11538              :                              format_type_with_typemod(constval->consttype,
   11539              :                                                       constval->consttypmod));
   11540          599 :             get_const_collation(constval, context);
   11541              :         }
   11542         5167 :         return;
   11543              :     }
   11544              : 
   11545        35837 :     getTypeOutputInfo(constval->consttype,
   11546              :                       &typoutput, &typIsVarlena);
   11547              : 
   11548        35837 :     extval = OidOutputFunctionCall(typoutput, constval->constvalue);
   11549              : 
   11550        35837 :     switch (constval->consttype)
   11551              :     {
   11552        20642 :         case INT4OID:
   11553              : 
   11554              :             /*
   11555              :              * INT4 can be printed without any decoration, unless it is
   11556              :              * negative; in that case print it as '-nnn'::integer to ensure
   11557              :              * that the output will re-parse as a constant, not as a constant
   11558              :              * plus operator.  In most cases we could get away with printing
   11559              :              * (-nnn) instead, because of the way that gram.y handles negative
   11560              :              * literals; but that doesn't work for INT_MIN, and it doesn't
   11561              :              * seem that much prettier anyway.
   11562              :              */
   11563        20642 :             if (extval[0] != '-')
   11564        20384 :                 appendStringInfoString(buf, extval);
   11565              :             else
   11566              :             {
   11567          258 :                 appendStringInfo(buf, "'%s'", extval);
   11568          258 :                 needlabel = true;   /* we must attach a cast */
   11569              :             }
   11570        20642 :             break;
   11571              : 
   11572          551 :         case NUMERICOID:
   11573              : 
   11574              :             /*
   11575              :              * NUMERIC can be printed without quotes if it looks like a float
   11576              :              * constant (not an integer, and not Infinity or NaN) and doesn't
   11577              :              * have a leading sign (for the same reason as for INT4).
   11578              :              */
   11579          551 :             if (isdigit((unsigned char) extval[0]) &&
   11580          551 :                 strcspn(extval, "eE.") != strlen(extval))
   11581              :             {
   11582          196 :                 appendStringInfoString(buf, extval);
   11583              :             }
   11584              :             else
   11585              :             {
   11586          355 :                 appendStringInfo(buf, "'%s'", extval);
   11587          355 :                 needlabel = true;   /* we must attach a cast */
   11588              :             }
   11589          551 :             break;
   11590              : 
   11591          873 :         case BOOLOID:
   11592          873 :             if (strcmp(extval, "t") == 0)
   11593          376 :                 appendStringInfoString(buf, "true");
   11594              :             else
   11595          497 :                 appendStringInfoString(buf, "false");
   11596          873 :             break;
   11597              : 
   11598        13771 :         default:
   11599        13771 :             simple_quote_literal(buf, extval);
   11600        13771 :             break;
   11601              :     }
   11602              : 
   11603        35837 :     pfree(extval);
   11604              : 
   11605        35837 :     if (showtype < 0)
   11606         4541 :         return;
   11607              : 
   11608              :     /*
   11609              :      * For showtype == 0, append ::typename unless the constant will be
   11610              :      * implicitly typed as the right type when it is read in.
   11611              :      *
   11612              :      * XXX this code has to be kept in sync with the behavior of the parser,
   11613              :      * especially make_const.
   11614              :      */
   11615        31296 :     switch (constval->consttype)
   11616              :     {
   11617          907 :         case BOOLOID:
   11618              :         case UNKNOWNOID:
   11619              :             /* These types can be left unlabeled */
   11620          907 :             needlabel = false;
   11621          907 :             break;
   11622        18227 :         case INT4OID:
   11623              :             /* We determined above whether a label is needed */
   11624        18227 :             break;
   11625          551 :         case NUMERICOID:
   11626              : 
   11627              :             /*
   11628              :              * Float-looking constants will be typed as numeric, which we
   11629              :              * checked above; but if there's a nondefault typmod we need to
   11630              :              * show it.
   11631              :              */
   11632          551 :             needlabel |= (constval->consttypmod >= 0);
   11633          551 :             break;
   11634        11611 :         default:
   11635        11611 :             needlabel = true;
   11636        11611 :             break;
   11637              :     }
   11638        31296 :     if (needlabel || showtype > 0)
   11639        12217 :         appendStringInfo(buf, "::%s",
   11640              :                          format_type_with_typemod(constval->consttype,
   11641              :                                                   constval->consttypmod));
   11642              : 
   11643        31296 :     get_const_collation(constval, context);
   11644              : }
   11645              : 
   11646              : /*
   11647              :  * helper for get_const_expr: append COLLATE if needed
   11648              :  */
   11649              : static void
   11650        31895 : get_const_collation(Const *constval, deparse_context *context)
   11651              : {
   11652        31895 :     StringInfo  buf = context->buf;
   11653              : 
   11654        31895 :     if (OidIsValid(constval->constcollid))
   11655              :     {
   11656         4602 :         Oid         typcollation = get_typcollation(constval->consttype);
   11657              : 
   11658         4602 :         if (constval->constcollid != typcollation)
   11659              :         {
   11660           37 :             appendStringInfo(buf, " COLLATE %s",
   11661              :                              generate_collation_name(constval->constcollid));
   11662              :         }
   11663              :     }
   11664        31895 : }
   11665              : 
   11666              : /*
   11667              :  * get_json_path_spec       - Parse back a JSON path specification
   11668              :  */
   11669              : static void
   11670          228 : get_json_path_spec(Node *path_spec, deparse_context *context, bool showimplicit)
   11671              : {
   11672          228 :     if (IsA(path_spec, Const))
   11673          228 :         get_const_expr((Const *) path_spec, context, -1);
   11674              :     else
   11675            0 :         get_rule_expr(path_spec, context, showimplicit);
   11676          228 : }
   11677              : 
   11678              : /*
   11679              :  * get_json_format          - Parse back a JsonFormat node
   11680              :  */
   11681              : static void
   11682           93 : get_json_format(JsonFormat *format, StringInfo buf)
   11683              : {
   11684           93 :     if (format->format_type == JS_FORMAT_DEFAULT)
   11685           54 :         return;
   11686              : 
   11687           39 :     appendStringInfoString(buf,
   11688           39 :                            format->format_type == JS_FORMAT_JSONB ?
   11689              :                            " FORMAT JSONB" : " FORMAT JSON");
   11690              : 
   11691           39 :     if (format->encoding != JS_ENC_DEFAULT)
   11692              :     {
   11693              :         const char *encoding;
   11694              : 
   11695            3 :         encoding =
   11696            6 :             format->encoding == JS_ENC_UTF16 ? "UTF16" :
   11697            3 :             format->encoding == JS_ENC_UTF32 ? "UTF32" : "UTF8";
   11698              : 
   11699            3 :         appendStringInfo(buf, " ENCODING %s", encoding);
   11700              :     }
   11701              : }
   11702              : 
   11703              : /*
   11704              :  * get_json_returning       - Parse back a JsonReturning structure
   11705              :  */
   11706              : static void
   11707           90 : get_json_returning(JsonReturning *returning, StringInfo buf,
   11708              :                    bool json_format_by_default)
   11709              : {
   11710           90 :     if (!OidIsValid(returning->typid))
   11711            0 :         return;
   11712              : 
   11713           90 :     appendStringInfo(buf, " RETURNING %s",
   11714              :                      format_type_with_typemod(returning->typid,
   11715              :                                               returning->typmod));
   11716              : 
   11717          174 :     if (!json_format_by_default ||
   11718           84 :         returning->format->format_type !=
   11719           84 :         (returning->typid == JSONBOID ? JS_FORMAT_JSONB : JS_FORMAT_JSON))
   11720           18 :         get_json_format(returning->format, buf);
   11721              : }
   11722              : 
   11723              : /*
   11724              :  * get_json_constructor     - Parse back a JsonConstructorExpr node
   11725              :  */
   11726              : static void
   11727           93 : get_json_constructor(JsonConstructorExpr *ctor, deparse_context *context,
   11728              :                      bool showimplicit)
   11729              : {
   11730           93 :     StringInfo  buf = context->buf;
   11731              :     const char *funcname;
   11732              :     bool        is_json_object;
   11733              :     int         curridx;
   11734              :     ListCell   *lc;
   11735              : 
   11736           93 :     if (ctor->type == JSCTOR_JSON_OBJECTAGG)
   11737              :     {
   11738           18 :         get_json_agg_constructor(ctor, context, "JSON_OBJECTAGG", true);
   11739           18 :         return;
   11740              :     }
   11741           75 :     else if (ctor->type == JSCTOR_JSON_ARRAYAGG)
   11742              :     {
   11743           15 :         get_json_agg_constructor(ctor, context, "JSON_ARRAYAGG", false);
   11744           15 :         return;
   11745              :     }
   11746              : 
   11747           60 :     switch (ctor->type)
   11748              :     {
   11749           15 :         case JSCTOR_JSON_OBJECT:
   11750           15 :             funcname = "JSON_OBJECT";
   11751           15 :             break;
   11752           12 :         case JSCTOR_JSON_ARRAY:
   11753           12 :             funcname = "JSON_ARRAY";
   11754           12 :             break;
   11755           21 :         case JSCTOR_JSON_PARSE:
   11756           21 :             funcname = "JSON";
   11757           21 :             break;
   11758            6 :         case JSCTOR_JSON_SCALAR:
   11759            6 :             funcname = "JSON_SCALAR";
   11760            6 :             break;
   11761            6 :         case JSCTOR_JSON_SERIALIZE:
   11762            6 :             funcname = "JSON_SERIALIZE";
   11763            6 :             break;
   11764            0 :         default:
   11765            0 :             elog(ERROR, "invalid JsonConstructorType %d", ctor->type);
   11766              :     }
   11767              : 
   11768           60 :     appendStringInfo(buf, "%s(", funcname);
   11769              : 
   11770           60 :     is_json_object = ctor->type == JSCTOR_JSON_OBJECT;
   11771          159 :     foreach(lc, ctor->args)
   11772              :     {
   11773           99 :         curridx = foreach_current_index(lc);
   11774           99 :         if (curridx > 0)
   11775              :         {
   11776              :             const char *sep;
   11777              : 
   11778           39 :             sep = (is_json_object && (curridx % 2) != 0) ? " : " : ", ";
   11779           39 :             appendStringInfoString(buf, sep);
   11780              :         }
   11781              : 
   11782           99 :         get_rule_expr((Node *) lfirst(lc), context, true);
   11783              :     }
   11784              : 
   11785           60 :     get_json_constructor_options(ctor, buf);
   11786           60 :     appendStringInfoChar(buf, ')');
   11787              : }
   11788              : 
   11789              : /*
   11790              :  * Append options, if any, to the JSON constructor being deparsed
   11791              :  */
   11792              : static void
   11793           93 : get_json_constructor_options(JsonConstructorExpr *ctor, StringInfo buf)
   11794              : {
   11795           93 :     if (ctor->absent_on_null)
   11796              :     {
   11797           18 :         if (ctor->type == JSCTOR_JSON_OBJECT ||
   11798           18 :             ctor->type == JSCTOR_JSON_OBJECTAGG)
   11799            0 :             appendStringInfoString(buf, " ABSENT ON NULL");
   11800              :     }
   11801              :     else
   11802              :     {
   11803           75 :         if (ctor->type == JSCTOR_JSON_ARRAY ||
   11804           75 :             ctor->type == JSCTOR_JSON_ARRAYAGG)
   11805            9 :             appendStringInfoString(buf, " NULL ON NULL");
   11806              :     }
   11807              : 
   11808           93 :     if (ctor->unique)
   11809           12 :         appendStringInfoString(buf, " WITH UNIQUE KEYS");
   11810              : 
   11811              :     /*
   11812              :      * Append RETURNING clause if needed; JSON() and JSON_SCALAR() don't
   11813              :      * support one.
   11814              :      */
   11815           93 :     if (ctor->type != JSCTOR_JSON_PARSE && ctor->type != JSCTOR_JSON_SCALAR)
   11816           66 :         get_json_returning(ctor->returning, buf, true);
   11817           93 : }
   11818              : 
   11819              : /*
   11820              :  * get_json_agg_constructor - Parse back an aggregate JsonConstructorExpr node
   11821              :  */
   11822              : static void
   11823           33 : get_json_agg_constructor(JsonConstructorExpr *ctor, deparse_context *context,
   11824              :                          const char *funcname, bool is_json_objectagg)
   11825              : {
   11826              :     StringInfoData options;
   11827              : 
   11828           33 :     initStringInfo(&options);
   11829           33 :     get_json_constructor_options(ctor, &options);
   11830              : 
   11831           33 :     if (IsA(ctor->func, Aggref))
   11832           27 :         get_agg_expr_helper((Aggref *) ctor->func, context,
   11833           27 :                             (Aggref *) ctor->func,
   11834           27 :                             funcname, options.data, is_json_objectagg);
   11835            6 :     else if (IsA(ctor->func, WindowFunc))
   11836            6 :         get_windowfunc_expr_helper((WindowFunc *) ctor->func, context,
   11837            6 :                                    funcname, options.data,
   11838              :                                    is_json_objectagg);
   11839              :     else
   11840            0 :         elog(ERROR, "invalid JsonConstructorExpr underlying node type: %d",
   11841              :              nodeTag(ctor->func));
   11842           33 : }
   11843              : 
   11844              : /*
   11845              :  * simple_quote_literal - Format a string as a SQL literal, append to buf
   11846              :  */
   11847              : static void
   11848        14207 : simple_quote_literal(StringInfo buf, const char *val)
   11849              : {
   11850              :     const char *valptr;
   11851              : 
   11852              :     /*
   11853              :      * We always form the string literal according to standard SQL rules.
   11854              :      */
   11855        14207 :     appendStringInfoChar(buf, '\'');
   11856       144884 :     for (valptr = val; *valptr; valptr++)
   11857              :     {
   11858       130677 :         char        ch = *valptr;
   11859              : 
   11860       130677 :         if (SQL_STR_DOUBLE(ch, false))
   11861          153 :             appendStringInfoChar(buf, ch);
   11862       130677 :         appendStringInfoChar(buf, ch);
   11863              :     }
   11864        14207 :     appendStringInfoChar(buf, '\'');
   11865        14207 : }
   11866              : 
   11867              : 
   11868              : /* ----------
   11869              :  * get_sublink_expr         - Parse back a sublink
   11870              :  * ----------
   11871              :  */
   11872              : static void
   11873          230 : get_sublink_expr(SubLink *sublink, deparse_context *context)
   11874              : {
   11875          230 :     StringInfo  buf = context->buf;
   11876          230 :     Query      *query = (Query *) (sublink->subselect);
   11877          230 :     char       *opname = NULL;
   11878              :     bool        need_paren;
   11879              : 
   11880          230 :     if (sublink->subLinkType == ARRAY_SUBLINK)
   11881           12 :         appendStringInfoString(buf, "ARRAY(");
   11882              :     else
   11883          218 :         appendStringInfoChar(buf, '(');
   11884              : 
   11885              :     /*
   11886              :      * Note that we print the name of only the first operator, when there are
   11887              :      * multiple combining operators.  This is an approximation that could go
   11888              :      * wrong in various scenarios (operators in different schemas, renamed
   11889              :      * operators, etc) but there is not a whole lot we can do about it, since
   11890              :      * the syntax allows only one operator to be shown.
   11891              :      */
   11892          230 :     if (sublink->testexpr)
   11893              :     {
   11894            9 :         if (IsA(sublink->testexpr, OpExpr))
   11895              :         {
   11896              :             /* single combining operator */
   11897            3 :             OpExpr     *opexpr = (OpExpr *) sublink->testexpr;
   11898              : 
   11899            3 :             get_rule_expr(linitial(opexpr->args), context, true);
   11900            3 :             opname = generate_operator_name(opexpr->opno,
   11901            3 :                                             exprType(linitial(opexpr->args)),
   11902            3 :                                             exprType(lsecond(opexpr->args)));
   11903              :         }
   11904            6 :         else if (IsA(sublink->testexpr, BoolExpr))
   11905              :         {
   11906              :             /* multiple combining operators, = or <> cases */
   11907              :             char       *sep;
   11908              :             ListCell   *l;
   11909              : 
   11910            3 :             appendStringInfoChar(buf, '(');
   11911            3 :             sep = "";
   11912            9 :             foreach(l, ((BoolExpr *) sublink->testexpr)->args)
   11913              :             {
   11914            6 :                 OpExpr     *opexpr = lfirst_node(OpExpr, l);
   11915              : 
   11916            6 :                 appendStringInfoString(buf, sep);
   11917            6 :                 get_rule_expr(linitial(opexpr->args), context, true);
   11918            6 :                 if (!opname)
   11919            3 :                     opname = generate_operator_name(opexpr->opno,
   11920            3 :                                                     exprType(linitial(opexpr->args)),
   11921            3 :                                                     exprType(lsecond(opexpr->args)));
   11922            6 :                 sep = ", ";
   11923              :             }
   11924            3 :             appendStringInfoChar(buf, ')');
   11925              :         }
   11926            3 :         else if (IsA(sublink->testexpr, RowCompareExpr))
   11927              :         {
   11928              :             /* multiple combining operators, < <= > >= cases */
   11929            3 :             RowCompareExpr *rcexpr = (RowCompareExpr *) sublink->testexpr;
   11930              : 
   11931            3 :             appendStringInfoChar(buf, '(');
   11932            3 :             get_rule_expr((Node *) rcexpr->largs, context, true);
   11933            3 :             opname = generate_operator_name(linitial_oid(rcexpr->opnos),
   11934            3 :                                             exprType(linitial(rcexpr->largs)),
   11935            3 :                                             exprType(linitial(rcexpr->rargs)));
   11936            3 :             appendStringInfoChar(buf, ')');
   11937              :         }
   11938              :         else
   11939            0 :             elog(ERROR, "unrecognized testexpr type: %d",
   11940              :                  (int) nodeTag(sublink->testexpr));
   11941              :     }
   11942              : 
   11943          230 :     need_paren = true;
   11944              : 
   11945          230 :     switch (sublink->subLinkType)
   11946              :     {
   11947           88 :         case EXISTS_SUBLINK:
   11948           88 :             appendStringInfoString(buf, "EXISTS ");
   11949           88 :             break;
   11950              : 
   11951            6 :         case ANY_SUBLINK:
   11952            6 :             if (strcmp(opname, "=") == 0) /* Represent = ANY as IN */
   11953            3 :                 appendStringInfoString(buf, " IN ");
   11954              :             else
   11955            3 :                 appendStringInfo(buf, " %s ANY ", opname);
   11956            6 :             break;
   11957              : 
   11958            3 :         case ALL_SUBLINK:
   11959            3 :             appendStringInfo(buf, " %s ALL ", opname);
   11960            3 :             break;
   11961              : 
   11962            0 :         case ROWCOMPARE_SUBLINK:
   11963            0 :             appendStringInfo(buf, " %s ", opname);
   11964            0 :             break;
   11965              : 
   11966          133 :         case EXPR_SUBLINK:
   11967              :         case MULTIEXPR_SUBLINK:
   11968              :         case ARRAY_SUBLINK:
   11969          133 :             need_paren = false;
   11970          133 :             break;
   11971              : 
   11972            0 :         case CTE_SUBLINK:       /* shouldn't occur in a SubLink */
   11973              :         default:
   11974            0 :             elog(ERROR, "unrecognized sublink type: %d",
   11975              :                  (int) sublink->subLinkType);
   11976              :             break;
   11977              :     }
   11978              : 
   11979          230 :     if (need_paren)
   11980           97 :         appendStringInfoChar(buf, '(');
   11981              : 
   11982          230 :     get_query_def(query, buf, context->namespaces, NULL, false,
   11983              :                   context->prettyFlags, context->wrapColumn,
   11984              :                   context->indentLevel);
   11985              : 
   11986          230 :     if (need_paren)
   11987           97 :         appendStringInfoString(buf, "))");
   11988              :     else
   11989          133 :         appendStringInfoChar(buf, ')');
   11990          230 : }
   11991              : 
   11992              : 
   11993              : /* ----------
   11994              :  * get_xmltable         - Parse back a XMLTABLE function
   11995              :  * ----------
   11996              :  */
   11997              : static void
   11998           31 : get_xmltable(TableFunc *tf, deparse_context *context, bool showimplicit)
   11999              : {
   12000           31 :     StringInfo  buf = context->buf;
   12001              : 
   12002           31 :     appendStringInfoString(buf, "XMLTABLE(");
   12003              : 
   12004           31 :     if (tf->ns_uris != NIL)
   12005              :     {
   12006              :         ListCell   *lc1,
   12007              :                    *lc2;
   12008            8 :         bool        first = true;
   12009              : 
   12010            8 :         appendStringInfoString(buf, "XMLNAMESPACES (");
   12011           16 :         forboth(lc1, tf->ns_uris, lc2, tf->ns_names)
   12012              :         {
   12013            8 :             Node       *expr = (Node *) lfirst(lc1);
   12014            8 :             String     *ns_node = lfirst_node(String, lc2);
   12015              : 
   12016            8 :             if (!first)
   12017            0 :                 appendStringInfoString(buf, ", ");
   12018              :             else
   12019            8 :                 first = false;
   12020              : 
   12021            8 :             if (ns_node != NULL)
   12022              :             {
   12023            8 :                 get_rule_expr(expr, context, showimplicit);
   12024            8 :                 appendStringInfo(buf, " AS %s",
   12025            8 :                                  quote_identifier(strVal(ns_node)));
   12026              :             }
   12027              :             else
   12028              :             {
   12029            0 :                 appendStringInfoString(buf, "DEFAULT ");
   12030            0 :                 get_rule_expr(expr, context, showimplicit);
   12031              :             }
   12032              :         }
   12033            8 :         appendStringInfoString(buf, "), ");
   12034              :     }
   12035              : 
   12036           31 :     appendStringInfoChar(buf, '(');
   12037           31 :     get_rule_expr((Node *) tf->rowexpr, context, showimplicit);
   12038           31 :     appendStringInfoString(buf, ") PASSING (");
   12039           31 :     get_rule_expr((Node *) tf->docexpr, context, showimplicit);
   12040           31 :     appendStringInfoChar(buf, ')');
   12041              : 
   12042           31 :     if (tf->colexprs != NIL)
   12043              :     {
   12044              :         ListCell   *l1;
   12045              :         ListCell   *l2;
   12046              :         ListCell   *l3;
   12047              :         ListCell   *l4;
   12048              :         ListCell   *l5;
   12049           31 :         int         colnum = 0;
   12050              : 
   12051           31 :         appendStringInfoString(buf, " COLUMNS ");
   12052          187 :         forfive(l1, tf->colnames, l2, tf->coltypes, l3, tf->coltypmods,
   12053              :                 l4, tf->colexprs, l5, tf->coldefexprs)
   12054              :         {
   12055          156 :             char       *colname = strVal(lfirst(l1));
   12056          156 :             Oid         typid = lfirst_oid(l2);
   12057          156 :             int32       typmod = lfirst_int(l3);
   12058          156 :             Node       *colexpr = (Node *) lfirst(l4);
   12059          156 :             Node       *coldefexpr = (Node *) lfirst(l5);
   12060          156 :             bool        ordinality = (tf->ordinalitycol == colnum);
   12061          156 :             bool        notnull = bms_is_member(colnum, tf->notnulls);
   12062              : 
   12063          156 :             if (colnum > 0)
   12064          125 :                 appendStringInfoString(buf, ", ");
   12065          156 :             colnum++;
   12066              : 
   12067          295 :             appendStringInfo(buf, "%s %s", quote_identifier(colname),
   12068              :                              ordinality ? "FOR ORDINALITY" :
   12069          139 :                              format_type_with_typemod(typid, typmod));
   12070          156 :             if (ordinality)
   12071           17 :                 continue;
   12072              : 
   12073          139 :             if (coldefexpr != NULL)
   12074              :             {
   12075           17 :                 appendStringInfoString(buf, " DEFAULT (");
   12076           17 :                 get_rule_expr((Node *) coldefexpr, context, showimplicit);
   12077           17 :                 appendStringInfoChar(buf, ')');
   12078              :             }
   12079          139 :             if (colexpr != NULL)
   12080              :             {
   12081          127 :                 appendStringInfoString(buf, " PATH (");
   12082          127 :                 get_rule_expr((Node *) colexpr, context, showimplicit);
   12083          127 :                 appendStringInfoChar(buf, ')');
   12084              :             }
   12085          139 :             if (notnull)
   12086           17 :                 appendStringInfoString(buf, " NOT NULL");
   12087              :         }
   12088              :     }
   12089              : 
   12090           31 :     appendStringInfoChar(buf, ')');
   12091           31 : }
   12092              : 
   12093              : /*
   12094              :  * get_json_table_nested_columns - Parse back nested JSON_TABLE columns
   12095              :  */
   12096              : static void
   12097           51 : get_json_table_nested_columns(TableFunc *tf, JsonTablePlan *plan,
   12098              :                               deparse_context *context, bool showimplicit,
   12099              :                               bool needcomma)
   12100              : {
   12101           51 :     if (IsA(plan, JsonTablePathScan))
   12102              :     {
   12103           36 :         JsonTablePathScan *scan = castNode(JsonTablePathScan, plan);
   12104              : 
   12105           36 :         if (needcomma)
   12106           24 :             appendStringInfoChar(context->buf, ',');
   12107              : 
   12108           36 :         appendStringInfoChar(context->buf, ' ');
   12109           36 :         appendContextKeyword(context, "NESTED PATH ", 0, 0, 0);
   12110           36 :         get_const_expr(scan->path->value, context, -1);
   12111           36 :         appendStringInfo(context->buf, " AS %s", quote_identifier(scan->path->name));
   12112           36 :         get_json_table_columns(tf, scan, context, showimplicit);
   12113              :     }
   12114           15 :     else if (IsA(plan, JsonTableSiblingJoin))
   12115              :     {
   12116           15 :         JsonTableSiblingJoin *join = (JsonTableSiblingJoin *) plan;
   12117              : 
   12118           15 :         get_json_table_nested_columns(tf, join->lplan, context, showimplicit,
   12119              :                                       needcomma);
   12120           15 :         get_json_table_nested_columns(tf, join->rplan, context, showimplicit,
   12121              :                                       true);
   12122              :     }
   12123           51 : }
   12124              : 
   12125              : /*
   12126              :  * get_json_table_columns - Parse back JSON_TABLE columns
   12127              :  */
   12128              : static void
   12129           90 : get_json_table_columns(TableFunc *tf, JsonTablePathScan *scan,
   12130              :                        deparse_context *context,
   12131              :                        bool showimplicit)
   12132              : {
   12133           90 :     StringInfo  buf = context->buf;
   12134              :     ListCell   *lc_colname;
   12135              :     ListCell   *lc_coltype;
   12136              :     ListCell   *lc_coltypmod;
   12137              :     ListCell   *lc_colvalexpr;
   12138           90 :     int         colnum = 0;
   12139              : 
   12140           90 :     appendStringInfoChar(buf, ' ');
   12141           90 :     appendContextKeyword(context, "COLUMNS (", 0, 0, 0);
   12142              : 
   12143           90 :     if (PRETTY_INDENT(context))
   12144           69 :         context->indentLevel += PRETTYINDENT_VAR;
   12145              : 
   12146          429 :     forfour(lc_colname, tf->colnames,
   12147              :             lc_coltype, tf->coltypes,
   12148              :             lc_coltypmod, tf->coltypmods,
   12149              :             lc_colvalexpr, tf->colvalexprs)
   12150              :     {
   12151          363 :         char       *colname = strVal(lfirst(lc_colname));
   12152              :         JsonExpr   *colexpr;
   12153              :         Oid         typid;
   12154              :         int32       typmod;
   12155              :         bool        ordinality;
   12156              :         JsonBehaviorType default_behavior;
   12157              : 
   12158          363 :         typid = lfirst_oid(lc_coltype);
   12159          363 :         typmod = lfirst_int(lc_coltypmod);
   12160          363 :         colexpr = castNode(JsonExpr, lfirst(lc_colvalexpr));
   12161              : 
   12162              :         /* Skip columns that don't belong to this scan. */
   12163          363 :         if (scan->colMin < 0 || colnum < scan->colMin)
   12164              :         {
   12165          132 :             colnum++;
   12166          132 :             continue;
   12167              :         }
   12168          231 :         if (colnum > scan->colMax)
   12169           24 :             break;
   12170              : 
   12171          207 :         if (colnum > scan->colMin)
   12172          129 :             appendStringInfoString(buf, ", ");
   12173              : 
   12174          207 :         colnum++;
   12175              : 
   12176          207 :         ordinality = !colexpr;
   12177              : 
   12178          207 :         appendContextKeyword(context, "", 0, 0, 0);
   12179              : 
   12180          405 :         appendStringInfo(buf, "%s %s", quote_identifier(colname),
   12181              :                          ordinality ? "FOR ORDINALITY" :
   12182          198 :                          format_type_with_typemod(typid, typmod));
   12183          207 :         if (ordinality)
   12184            9 :             continue;
   12185              : 
   12186              :         /*
   12187              :          * Set default_behavior to guide get_json_expr_options() on whether to
   12188              :          * emit the ON ERROR / EMPTY clauses.
   12189              :          */
   12190          198 :         if (colexpr->op == JSON_EXISTS_OP)
   12191              :         {
   12192           18 :             appendStringInfoString(buf, " EXISTS");
   12193           18 :             default_behavior = JSON_BEHAVIOR_FALSE;
   12194              :         }
   12195              :         else
   12196              :         {
   12197          180 :             if (colexpr->op == JSON_QUERY_OP)
   12198              :             {
   12199              :                 char        typcategory;
   12200              :                 bool        typispreferred;
   12201              : 
   12202           87 :                 get_type_category_preferred(typid, &typcategory, &typispreferred);
   12203              : 
   12204           87 :                 if (typcategory == TYPCATEGORY_STRING)
   12205           18 :                     appendStringInfoString(buf,
   12206           18 :                                            colexpr->format->format_type == JS_FORMAT_JSONB ?
   12207              :                                            " FORMAT JSONB" : " FORMAT JSON");
   12208              :             }
   12209              : 
   12210          180 :             default_behavior = JSON_BEHAVIOR_NULL;
   12211              :         }
   12212              : 
   12213          198 :         appendStringInfoString(buf, " PATH ");
   12214              : 
   12215          198 :         get_json_path_spec(colexpr->path_spec, context, showimplicit);
   12216              : 
   12217          198 :         get_json_expr_options(colexpr, context, default_behavior);
   12218              :     }
   12219              : 
   12220           90 :     if (scan->child)
   12221           21 :         get_json_table_nested_columns(tf, scan->child, context, showimplicit,
   12222           21 :                                       scan->colMin >= 0);
   12223              : 
   12224           90 :     if (PRETTY_INDENT(context))
   12225           69 :         context->indentLevel -= PRETTYINDENT_VAR;
   12226              : 
   12227           90 :     appendContextKeyword(context, ")", 0, 0, 0);
   12228           90 : }
   12229              : 
   12230              : /* ----------
   12231              :  * get_json_table           - Parse back a JSON_TABLE function
   12232              :  * ----------
   12233              :  */
   12234              : static void
   12235           54 : get_json_table(TableFunc *tf, deparse_context *context, bool showimplicit)
   12236              : {
   12237           54 :     StringInfo  buf = context->buf;
   12238           54 :     JsonExpr   *jexpr = castNode(JsonExpr, tf->docexpr);
   12239           54 :     JsonTablePathScan *root = castNode(JsonTablePathScan, tf->plan);
   12240              : 
   12241           54 :     appendStringInfoString(buf, "JSON_TABLE(");
   12242              : 
   12243           54 :     if (PRETTY_INDENT(context))
   12244           33 :         context->indentLevel += PRETTYINDENT_VAR;
   12245              : 
   12246           54 :     appendContextKeyword(context, "", 0, 0, 0);
   12247              : 
   12248           54 :     get_rule_expr(jexpr->formatted_expr, context, showimplicit);
   12249              : 
   12250           54 :     appendStringInfoString(buf, ", ");
   12251              : 
   12252           54 :     get_const_expr(root->path->value, context, -1);
   12253              : 
   12254           54 :     appendStringInfo(buf, " AS %s", quote_identifier(root->path->name));
   12255              : 
   12256           54 :     if (jexpr->passing_values)
   12257              :     {
   12258              :         ListCell   *lc1,
   12259              :                    *lc2;
   12260           42 :         bool        needcomma = false;
   12261              : 
   12262           42 :         appendStringInfoChar(buf, ' ');
   12263           42 :         appendContextKeyword(context, "PASSING ", 0, 0, 0);
   12264              : 
   12265           42 :         if (PRETTY_INDENT(context))
   12266           21 :             context->indentLevel += PRETTYINDENT_VAR;
   12267              : 
   12268          126 :         forboth(lc1, jexpr->passing_names,
   12269              :                 lc2, jexpr->passing_values)
   12270              :         {
   12271           84 :             if (needcomma)
   12272           42 :                 appendStringInfoString(buf, ", ");
   12273           84 :             needcomma = true;
   12274              : 
   12275           84 :             appendContextKeyword(context, "", 0, 0, 0);
   12276              : 
   12277           84 :             get_rule_expr((Node *) lfirst(lc2), context, false);
   12278           84 :             appendStringInfo(buf, " AS %s",
   12279           84 :                              quote_identifier((lfirst_node(String, lc1))->sval)
   12280              :                 );
   12281              :         }
   12282              : 
   12283           42 :         if (PRETTY_INDENT(context))
   12284           21 :             context->indentLevel -= PRETTYINDENT_VAR;
   12285              :     }
   12286              : 
   12287           54 :     get_json_table_columns(tf, castNode(JsonTablePathScan, tf->plan), context,
   12288              :                            showimplicit);
   12289              : 
   12290           54 :     if (jexpr->on_error->btype != JSON_BEHAVIOR_EMPTY_ARRAY)
   12291            3 :         get_json_behavior(jexpr->on_error, context, "ERROR");
   12292              : 
   12293           54 :     if (PRETTY_INDENT(context))
   12294           33 :         context->indentLevel -= PRETTYINDENT_VAR;
   12295              : 
   12296           54 :     appendContextKeyword(context, ")", 0, 0, 0);
   12297           54 : }
   12298              : 
   12299              : /* ----------
   12300              :  * get_tablefunc            - Parse back a table function
   12301              :  * ----------
   12302              :  */
   12303              : static void
   12304           85 : get_tablefunc(TableFunc *tf, deparse_context *context, bool showimplicit)
   12305              : {
   12306              :     /* XMLTABLE and JSON_TABLE are the only existing implementations.  */
   12307              : 
   12308           85 :     if (tf->functype == TFT_XMLTABLE)
   12309           31 :         get_xmltable(tf, context, showimplicit);
   12310           54 :     else if (tf->functype == TFT_JSON_TABLE)
   12311           54 :         get_json_table(tf, context, showimplicit);
   12312           85 : }
   12313              : 
   12314              : /* ----------
   12315              :  * get_from_clause          - Parse back a FROM clause
   12316              :  *
   12317              :  * "prefix" is the keyword that denotes the start of the list of FROM
   12318              :  * elements. It is FROM when used to parse back SELECT and UPDATE, but
   12319              :  * is USING when parsing back DELETE.
   12320              :  * ----------
   12321              :  */
   12322              : static void
   12323         2475 : get_from_clause(Query *query, const char *prefix, deparse_context *context)
   12324              : {
   12325         2475 :     StringInfo  buf = context->buf;
   12326         2475 :     bool        first = true;
   12327              :     ListCell   *l;
   12328              : 
   12329              :     /*
   12330              :      * We use the query's jointree as a guide to what to print.  However, we
   12331              :      * must ignore auto-added RTEs that are marked not inFromCl. (These can
   12332              :      * only appear at the top level of the jointree, so it's sufficient to
   12333              :      * check here.)  This check also ensures we ignore the rule pseudo-RTEs
   12334              :      * for NEW and OLD.
   12335              :      */
   12336         4927 :     foreach(l, query->jointree->fromlist)
   12337              :     {
   12338         2452 :         Node       *jtnode = (Node *) lfirst(l);
   12339              : 
   12340         2452 :         if (IsA(jtnode, RangeTblRef))
   12341              :         {
   12342         1967 :             int         varno = ((RangeTblRef *) jtnode)->rtindex;
   12343         1967 :             RangeTblEntry *rte = rt_fetch(varno, query->rtable);
   12344              : 
   12345         1967 :             if (!rte->inFromCl)
   12346          200 :                 continue;
   12347              :         }
   12348              : 
   12349         2252 :         if (first)
   12350              :         {
   12351         2067 :             appendContextKeyword(context, prefix,
   12352              :                                  -PRETTYINDENT_STD, PRETTYINDENT_STD, 2);
   12353         2067 :             first = false;
   12354              : 
   12355         2067 :             get_from_clause_item(jtnode, query, context);
   12356              :         }
   12357              :         else
   12358              :         {
   12359              :             StringInfoData itembuf;
   12360              : 
   12361          185 :             appendStringInfoString(buf, ", ");
   12362              : 
   12363              :             /*
   12364              :              * Put the new FROM item's text into itembuf so we can decide
   12365              :              * after we've got it whether or not it needs to go on a new line.
   12366              :              */
   12367          185 :             initStringInfo(&itembuf);
   12368          185 :             context->buf = &itembuf;
   12369              : 
   12370          185 :             get_from_clause_item(jtnode, query, context);
   12371              : 
   12372              :             /* Restore context's output buffer */
   12373          185 :             context->buf = buf;
   12374              : 
   12375              :             /* Consider line-wrapping if enabled */
   12376          185 :             if (PRETTY_INDENT(context) && context->wrapColumn >= 0)
   12377              :             {
   12378              :                 /* Does the new item start with a new line? */
   12379          185 :                 if (itembuf.len > 0 && itembuf.data[0] == '\n')
   12380              :                 {
   12381              :                     /* If so, we shouldn't add anything */
   12382              :                     /* instead, remove any trailing spaces currently in buf */
   12383            0 :                     removeStringInfoSpaces(buf);
   12384              :                 }
   12385              :                 else
   12386              :                 {
   12387              :                     char       *trailing_nl;
   12388              : 
   12389              :                     /* Locate the start of the current line in the buffer */
   12390          185 :                     trailing_nl = strrchr(buf->data, '\n');
   12391          185 :                     if (trailing_nl == NULL)
   12392            0 :                         trailing_nl = buf->data;
   12393              :                     else
   12394          185 :                         trailing_nl++;
   12395              : 
   12396              :                     /*
   12397              :                      * Add a newline, plus some indentation, if the new item
   12398              :                      * would cause an overflow.
   12399              :                      */
   12400          185 :                     if (strlen(trailing_nl) + itembuf.len > context->wrapColumn)
   12401          185 :                         appendContextKeyword(context, "", -PRETTYINDENT_STD,
   12402              :                                              PRETTYINDENT_STD,
   12403              :                                              PRETTYINDENT_VAR);
   12404              :                 }
   12405              :             }
   12406              : 
   12407              :             /* Add the new item */
   12408          185 :             appendBinaryStringInfo(buf, itembuf.data, itembuf.len);
   12409              : 
   12410              :             /* clean up */
   12411          185 :             pfree(itembuf.data);
   12412              :         }
   12413              :     }
   12414         2475 : }
   12415              : 
   12416              : static void
   12417         3768 : get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
   12418              : {
   12419         3768 :     StringInfo  buf = context->buf;
   12420         3768 :     deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
   12421              : 
   12422         3768 :     if (IsA(jtnode, RangeTblRef))
   12423              :     {
   12424         3010 :         int         varno = ((RangeTblRef *) jtnode)->rtindex;
   12425         3010 :         RangeTblEntry *rte = rt_fetch(varno, query->rtable);
   12426         3010 :         deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
   12427         3010 :         RangeTblFunction *rtfunc1 = NULL;
   12428              : 
   12429         3010 :         if (rte->lateral)
   12430           62 :             appendStringInfoString(buf, "LATERAL ");
   12431              : 
   12432              :         /* Print the FROM item proper */
   12433         3010 :         switch (rte->rtekind)
   12434              :         {
   12435         2282 :             case RTE_RELATION:
   12436              :                 /* Normal relation RTE */
   12437         4564 :                 appendStringInfo(buf, "%s%s",
   12438         2282 :                                  only_marker(rte),
   12439              :                                  generate_relation_name(rte->relid,
   12440              :                                                         context->namespaces));
   12441         2282 :                 break;
   12442          146 :             case RTE_SUBQUERY:
   12443              :                 /* Subquery RTE */
   12444          146 :                 appendStringInfoChar(buf, '(');
   12445          146 :                 get_query_def(rte->subquery, buf, context->namespaces, NULL,
   12446              :                               true,
   12447              :                               context->prettyFlags, context->wrapColumn,
   12448              :                               context->indentLevel);
   12449          146 :                 appendStringInfoChar(buf, ')');
   12450          146 :                 break;
   12451          435 :             case RTE_FUNCTION:
   12452              :                 /* Function RTE */
   12453          435 :                 rtfunc1 = (RangeTblFunction *) linitial(rte->functions);
   12454              : 
   12455              :                 /*
   12456              :                  * Omit ROWS FROM() syntax for just one function, unless it
   12457              :                  * has both a coldeflist and WITH ORDINALITY. If it has both,
   12458              :                  * we must use ROWS FROM() syntax to avoid ambiguity about
   12459              :                  * whether the coldeflist includes the ordinality column.
   12460              :                  */
   12461          435 :                 if (list_length(rte->functions) == 1 &&
   12462          420 :                     (rtfunc1->funccolnames == NIL || !rte->funcordinality))
   12463              :                 {
   12464          420 :                     get_rule_expr_funccall(rtfunc1->funcexpr, context, true);
   12465              :                     /* we'll print the coldeflist below, if it has one */
   12466              :                 }
   12467              :                 else
   12468              :                 {
   12469              :                     bool        all_unnest;
   12470              :                     ListCell   *lc;
   12471              : 
   12472              :                     /*
   12473              :                      * If all the function calls in the list are to unnest,
   12474              :                      * and none need a coldeflist, then collapse the list back
   12475              :                      * down to UNNEST(args).  (If we had more than one
   12476              :                      * built-in unnest function, this would get more
   12477              :                      * difficult.)
   12478              :                      *
   12479              :                      * XXX This is pretty ugly, since it makes not-terribly-
   12480              :                      * future-proof assumptions about what the parser would do
   12481              :                      * with the output; but the alternative is to emit our
   12482              :                      * nonstandard ROWS FROM() notation for what might have
   12483              :                      * been a perfectly spec-compliant multi-argument
   12484              :                      * UNNEST().
   12485              :                      */
   12486           15 :                     all_unnest = true;
   12487           39 :                     foreach(lc, rte->functions)
   12488              :                     {
   12489           33 :                         RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
   12490              : 
   12491           33 :                         if (!IsA(rtfunc->funcexpr, FuncExpr) ||
   12492           33 :                             ((FuncExpr *) rtfunc->funcexpr)->funcid != F_UNNEST_ANYARRAY ||
   12493           24 :                             rtfunc->funccolnames != NIL)
   12494              :                         {
   12495            9 :                             all_unnest = false;
   12496            9 :                             break;
   12497              :                         }
   12498              :                     }
   12499              : 
   12500           15 :                     if (all_unnest)
   12501              :                     {
   12502            6 :                         List       *allargs = NIL;
   12503              : 
   12504           24 :                         foreach(lc, rte->functions)
   12505              :                         {
   12506           18 :                             RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
   12507           18 :                             List       *args = ((FuncExpr *) rtfunc->funcexpr)->args;
   12508              : 
   12509           18 :                             allargs = list_concat(allargs, args);
   12510              :                         }
   12511              : 
   12512            6 :                         appendStringInfoString(buf, "UNNEST(");
   12513            6 :                         get_rule_expr((Node *) allargs, context, true);
   12514            6 :                         appendStringInfoChar(buf, ')');
   12515              :                     }
   12516              :                     else
   12517              :                     {
   12518            9 :                         int         funcno = 0;
   12519              : 
   12520            9 :                         appendStringInfoString(buf, "ROWS FROM(");
   12521           33 :                         foreach(lc, rte->functions)
   12522              :                         {
   12523           24 :                             RangeTblFunction *rtfunc = (RangeTblFunction *) lfirst(lc);
   12524              : 
   12525           24 :                             if (funcno > 0)
   12526           15 :                                 appendStringInfoString(buf, ", ");
   12527           24 :                             get_rule_expr_funccall(rtfunc->funcexpr, context, true);
   12528           24 :                             if (rtfunc->funccolnames != NIL)
   12529              :                             {
   12530              :                                 /* Reconstruct the column definition list */
   12531            3 :                                 appendStringInfoString(buf, " AS ");
   12532            3 :                                 get_from_clause_coldeflist(rtfunc,
   12533              :                                                            NULL,
   12534              :                                                            context);
   12535              :                             }
   12536           24 :                             funcno++;
   12537              :                         }
   12538            9 :                         appendStringInfoChar(buf, ')');
   12539              :                     }
   12540              :                     /* prevent printing duplicate coldeflist below */
   12541           15 :                     rtfunc1 = NULL;
   12542              :                 }
   12543          435 :                 if (rte->funcordinality)
   12544            9 :                     appendStringInfoString(buf, " WITH ORDINALITY");
   12545          435 :                 break;
   12546           49 :             case RTE_TABLEFUNC:
   12547           49 :                 get_tablefunc(rte->tablefunc, context, true);
   12548           49 :                 break;
   12549            6 :             case RTE_VALUES:
   12550              :                 /* Values list RTE */
   12551            6 :                 appendStringInfoChar(buf, '(');
   12552            6 :                 get_values_def(rte->values_lists, context);
   12553            6 :                 appendStringInfoChar(buf, ')');
   12554            6 :                 break;
   12555           92 :             case RTE_CTE:
   12556           92 :                 appendStringInfoString(buf, quote_identifier(rte->ctename));
   12557           92 :                 break;
   12558            0 :             default:
   12559            0 :                 elog(ERROR, "unrecognized RTE kind: %d", (int) rte->rtekind);
   12560              :                 break;
   12561              :         }
   12562              : 
   12563              :         /* Print the relation alias, if needed */
   12564         3010 :         get_rte_alias(rte, varno, false, context);
   12565              : 
   12566              :         /* Print the column definitions or aliases, if needed */
   12567         3010 :         if (rtfunc1 && rtfunc1->funccolnames != NIL)
   12568              :         {
   12569              :             /* Reconstruct the columndef list, which is also the aliases */
   12570            0 :             get_from_clause_coldeflist(rtfunc1, colinfo, context);
   12571              :         }
   12572              :         else
   12573              :         {
   12574              :             /* Else print column aliases as needed */
   12575         3010 :             get_column_alias_list(colinfo, context);
   12576              :         }
   12577              : 
   12578              :         /* Tablesample clause must go after any alias */
   12579         3010 :         if (rte->rtekind == RTE_RELATION && rte->tablesample)
   12580           16 :             get_tablesample_def(rte->tablesample, context);
   12581              :     }
   12582          758 :     else if (IsA(jtnode, JoinExpr))
   12583              :     {
   12584          758 :         JoinExpr   *j = (JoinExpr *) jtnode;
   12585          758 :         deparse_columns *colinfo = deparse_columns_fetch(j->rtindex, dpns);
   12586              :         bool        need_paren_on_right;
   12587              : 
   12588         1735 :         need_paren_on_right = PRETTY_PAREN(context) &&
   12589          758 :             !IsA(j->rarg, RangeTblRef) &&
   12590            0 :             !(IsA(j->rarg, JoinExpr) && ((JoinExpr *) j->rarg)->alias != NULL);
   12591              : 
   12592          758 :         if (!PRETTY_PAREN(context) || j->alias != NULL)
   12593          593 :             appendStringInfoChar(buf, '(');
   12594              : 
   12595          758 :         get_from_clause_item(j->larg, query, context);
   12596              : 
   12597          758 :         switch (j->jointype)
   12598              :         {
   12599          416 :             case JOIN_INNER:
   12600          416 :                 if (j->quals)
   12601          395 :                     appendContextKeyword(context, " JOIN ",
   12602              :                                          -PRETTYINDENT_STD,
   12603              :                                          PRETTYINDENT_STD,
   12604              :                                          PRETTYINDENT_JOIN);
   12605              :                 else
   12606           21 :                     appendContextKeyword(context, " CROSS JOIN ",
   12607              :                                          -PRETTYINDENT_STD,
   12608              :                                          PRETTYINDENT_STD,
   12609              :                                          PRETTYINDENT_JOIN);
   12610          416 :                 break;
   12611          291 :             case JOIN_LEFT:
   12612          291 :                 appendContextKeyword(context, " LEFT JOIN ",
   12613              :                                      -PRETTYINDENT_STD,
   12614              :                                      PRETTYINDENT_STD,
   12615              :                                      PRETTYINDENT_JOIN);
   12616          291 :                 break;
   12617           51 :             case JOIN_FULL:
   12618           51 :                 appendContextKeyword(context, " FULL JOIN ",
   12619              :                                      -PRETTYINDENT_STD,
   12620              :                                      PRETTYINDENT_STD,
   12621              :                                      PRETTYINDENT_JOIN);
   12622           51 :                 break;
   12623            0 :             case JOIN_RIGHT:
   12624            0 :                 appendContextKeyword(context, " RIGHT JOIN ",
   12625              :                                      -PRETTYINDENT_STD,
   12626              :                                      PRETTYINDENT_STD,
   12627              :                                      PRETTYINDENT_JOIN);
   12628            0 :                 break;
   12629            0 :             default:
   12630            0 :                 elog(ERROR, "unrecognized join type: %d",
   12631              :                      (int) j->jointype);
   12632              :         }
   12633              : 
   12634          758 :         if (need_paren_on_right)
   12635            0 :             appendStringInfoChar(buf, '(');
   12636          758 :         get_from_clause_item(j->rarg, query, context);
   12637          758 :         if (need_paren_on_right)
   12638            0 :             appendStringInfoChar(buf, ')');
   12639              : 
   12640          758 :         if (j->usingClause)
   12641              :         {
   12642              :             ListCell   *lc;
   12643          215 :             bool        first = true;
   12644              : 
   12645          215 :             appendStringInfoString(buf, " USING (");
   12646              :             /* Use the assigned names, not what's in usingClause */
   12647          508 :             foreach(lc, colinfo->usingNames)
   12648              :             {
   12649          293 :                 char       *colname = (char *) lfirst(lc);
   12650              : 
   12651          293 :                 if (first)
   12652          215 :                     first = false;
   12653              :                 else
   12654           78 :                     appendStringInfoString(buf, ", ");
   12655          293 :                 appendStringInfoString(buf, quote_identifier(colname));
   12656              :             }
   12657          215 :             appendStringInfoChar(buf, ')');
   12658              : 
   12659          215 :             if (j->join_using_alias)
   12660            6 :                 appendStringInfo(buf, " AS %s",
   12661            6 :                                  quote_identifier(j->join_using_alias->aliasname));
   12662              :         }
   12663          543 :         else if (j->quals)
   12664              :         {
   12665          519 :             appendStringInfoString(buf, " ON ");
   12666          519 :             if (!PRETTY_PAREN(context))
   12667          516 :                 appendStringInfoChar(buf, '(');
   12668          519 :             get_rule_expr(j->quals, context, false);
   12669          519 :             if (!PRETTY_PAREN(context))
   12670          516 :                 appendStringInfoChar(buf, ')');
   12671              :         }
   12672           24 :         else if (j->jointype != JOIN_INNER)
   12673              :         {
   12674              :             /* If we didn't say CROSS JOIN above, we must provide an ON */
   12675            3 :             appendStringInfoString(buf, " ON TRUE");
   12676              :         }
   12677              : 
   12678          758 :         if (!PRETTY_PAREN(context) || j->alias != NULL)
   12679          593 :             appendStringInfoChar(buf, ')');
   12680              : 
   12681              :         /* Yes, it's correct to put alias after the right paren ... */
   12682          758 :         if (j->alias != NULL)
   12683              :         {
   12684              :             /*
   12685              :              * Note that it's correct to emit an alias clause if and only if
   12686              :              * there was one originally.  Otherwise we'd be converting a named
   12687              :              * join to unnamed or vice versa, which creates semantic
   12688              :              * subtleties we don't want.  However, we might print a different
   12689              :              * alias name than was there originally.
   12690              :              */
   12691           54 :             appendStringInfo(buf, " %s",
   12692           54 :                              quote_identifier(get_rtable_name(j->rtindex,
   12693              :                                                               context)));
   12694           54 :             get_column_alias_list(colinfo, context);
   12695              :         }
   12696              :     }
   12697              :     else
   12698            0 :         elog(ERROR, "unrecognized node type: %d",
   12699              :              (int) nodeTag(jtnode));
   12700         3768 : }
   12701              : 
   12702              : /*
   12703              :  * get_rte_alias - print the relation's alias, if needed
   12704              :  *
   12705              :  * If printed, the alias is preceded by a space, or by " AS " if use_as is true.
   12706              :  */
   12707              : static void
   12708         3304 : get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
   12709              :               deparse_context *context)
   12710              : {
   12711         3304 :     deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
   12712         3304 :     char       *refname = get_rtable_name(varno, context);
   12713         3304 :     deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
   12714         3304 :     bool        printalias = false;
   12715              : 
   12716         3304 :     if (rte->alias != NULL)
   12717              :     {
   12718              :         /* Always print alias if user provided one */
   12719         1547 :         printalias = true;
   12720              :     }
   12721         1757 :     else if (colinfo->printaliases)
   12722              :     {
   12723              :         /* Always print alias if we need to print column aliases */
   12724          165 :         printalias = true;
   12725              :     }
   12726         1592 :     else if (rte->rtekind == RTE_RELATION)
   12727              :     {
   12728              :         /*
   12729              :          * No need to print alias if it's same as relation name (this would
   12730              :          * normally be the case, but not if set_rtable_names had to resolve a
   12731              :          * conflict).
   12732              :          */
   12733         1456 :         if (strcmp(refname, get_relation_name(rte->relid)) != 0)
   12734           40 :             printalias = true;
   12735              :     }
   12736          136 :     else if (rte->rtekind == RTE_FUNCTION)
   12737              :     {
   12738              :         /*
   12739              :          * For a function RTE, always print alias.  This covers possible
   12740              :          * renaming of the function and/or instability of the FigureColname
   12741              :          * rules for things that aren't simple functions.  Note we'd need to
   12742              :          * force it anyway for the columndef list case.
   12743              :          */
   12744            0 :         printalias = true;
   12745              :     }
   12746          136 :     else if (rte->rtekind == RTE_SUBQUERY ||
   12747          124 :              rte->rtekind == RTE_VALUES)
   12748              :     {
   12749              :         /*
   12750              :          * For a subquery, always print alias.  This makes the output
   12751              :          * SQL-spec-compliant, even though we allow such aliases to be omitted
   12752              :          * on input.
   12753              :          */
   12754           18 :         printalias = true;
   12755              :     }
   12756          118 :     else if (rte->rtekind == RTE_CTE)
   12757              :     {
   12758              :         /*
   12759              :          * No need to print alias if it's same as CTE name (this would
   12760              :          * normally be the case, but not if set_rtable_names had to resolve a
   12761              :          * conflict).
   12762              :          */
   12763           72 :         if (strcmp(refname, rte->ctename) != 0)
   12764           11 :             printalias = true;
   12765              :     }
   12766              : 
   12767         3304 :     if (printalias)
   12768         1781 :         appendStringInfo(context->buf, "%s%s",
   12769              :                          use_as ? " AS " : " ",
   12770              :                          quote_identifier(refname));
   12771         3304 : }
   12772              : 
   12773              : /*
   12774              :  * get_column_alias_list - print column alias list for an RTE
   12775              :  *
   12776              :  * Caller must already have printed the relation's alias name.
   12777              :  */
   12778              : static void
   12779         3064 : get_column_alias_list(deparse_columns *colinfo, deparse_context *context)
   12780              : {
   12781         3064 :     StringInfo  buf = context->buf;
   12782              :     int         i;
   12783         3064 :     bool        first = true;
   12784              : 
   12785              :     /* Don't print aliases if not needed */
   12786         3064 :     if (!colinfo->printaliases)
   12787         2449 :         return;
   12788              : 
   12789         4968 :     for (i = 0; i < colinfo->num_new_cols; i++)
   12790              :     {
   12791         4353 :         char       *colname = colinfo->new_colnames[i];
   12792              : 
   12793         4353 :         if (first)
   12794              :         {
   12795          615 :             appendStringInfoChar(buf, '(');
   12796          615 :             first = false;
   12797              :         }
   12798              :         else
   12799         3738 :             appendStringInfoString(buf, ", ");
   12800         4353 :         appendStringInfoString(buf, quote_identifier(colname));
   12801              :     }
   12802          615 :     if (!first)
   12803          615 :         appendStringInfoChar(buf, ')');
   12804              : }
   12805              : 
   12806              : /*
   12807              :  * get_from_clause_coldeflist - reproduce FROM clause coldeflist
   12808              :  *
   12809              :  * When printing a top-level coldeflist (which is syntactically also the
   12810              :  * relation's column alias list), use column names from colinfo.  But when
   12811              :  * printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
   12812              :  * original coldeflist's names, which are available in rtfunc->funccolnames.
   12813              :  * Pass NULL for colinfo to select the latter behavior.
   12814              :  *
   12815              :  * The coldeflist is appended immediately (no space) to buf.  Caller is
   12816              :  * responsible for ensuring that an alias or AS is present before it.
   12817              :  */
   12818              : static void
   12819            3 : get_from_clause_coldeflist(RangeTblFunction *rtfunc,
   12820              :                            deparse_columns *colinfo,
   12821              :                            deparse_context *context)
   12822              : {
   12823            3 :     StringInfo  buf = context->buf;
   12824              :     ListCell   *l1;
   12825              :     ListCell   *l2;
   12826              :     ListCell   *l3;
   12827              :     ListCell   *l4;
   12828              :     int         i;
   12829              : 
   12830            3 :     appendStringInfoChar(buf, '(');
   12831              : 
   12832            3 :     i = 0;
   12833           12 :     forfour(l1, rtfunc->funccoltypes,
   12834              :             l2, rtfunc->funccoltypmods,
   12835              :             l3, rtfunc->funccolcollations,
   12836              :             l4, rtfunc->funccolnames)
   12837              :     {
   12838            9 :         Oid         atttypid = lfirst_oid(l1);
   12839            9 :         int32       atttypmod = lfirst_int(l2);
   12840            9 :         Oid         attcollation = lfirst_oid(l3);
   12841              :         char       *attname;
   12842              : 
   12843            9 :         if (colinfo)
   12844            0 :             attname = colinfo->colnames[i];
   12845              :         else
   12846            9 :             attname = strVal(lfirst(l4));
   12847              : 
   12848              :         Assert(attname);        /* shouldn't be any dropped columns here */
   12849              : 
   12850            9 :         if (i > 0)
   12851            6 :             appendStringInfoString(buf, ", ");
   12852            9 :         appendStringInfo(buf, "%s %s",
   12853              :                          quote_identifier(attname),
   12854              :                          format_type_with_typemod(atttypid, atttypmod));
   12855           12 :         if (OidIsValid(attcollation) &&
   12856            3 :             attcollation != get_typcollation(atttypid))
   12857            0 :             appendStringInfo(buf, " COLLATE %s",
   12858              :                              generate_collation_name(attcollation));
   12859              : 
   12860            9 :         i++;
   12861              :     }
   12862              : 
   12863            3 :     appendStringInfoChar(buf, ')');
   12864            3 : }
   12865              : 
   12866              : /*
   12867              :  * get_tablesample_def          - print a TableSampleClause
   12868              :  */
   12869              : static void
   12870           16 : get_tablesample_def(TableSampleClause *tablesample, deparse_context *context)
   12871              : {
   12872           16 :     StringInfo  buf = context->buf;
   12873              :     Oid         argtypes[1];
   12874              :     int         nargs;
   12875              :     ListCell   *l;
   12876              : 
   12877              :     /*
   12878              :      * We should qualify the handler's function name if it wouldn't be
   12879              :      * resolved by lookup in the current search path.
   12880              :      */
   12881           16 :     argtypes[0] = INTERNALOID;
   12882           16 :     appendStringInfo(buf, " TABLESAMPLE %s (",
   12883              :                      generate_function_name(tablesample->tsmhandler, 1,
   12884              :                                             NIL, argtypes,
   12885              :                                             false, NULL, false));
   12886              : 
   12887           16 :     nargs = 0;
   12888           32 :     foreach(l, tablesample->args)
   12889              :     {
   12890           16 :         if (nargs++ > 0)
   12891            0 :             appendStringInfoString(buf, ", ");
   12892           16 :         get_rule_expr((Node *) lfirst(l), context, false);
   12893              :     }
   12894           16 :     appendStringInfoChar(buf, ')');
   12895              : 
   12896           16 :     if (tablesample->repeatable != NULL)
   12897              :     {
   12898            8 :         appendStringInfoString(buf, " REPEATABLE (");
   12899            8 :         get_rule_expr((Node *) tablesample->repeatable, context, false);
   12900            8 :         appendStringInfoChar(buf, ')');
   12901              :     }
   12902           16 : }
   12903              : 
   12904              : /*
   12905              :  * get_opclass_name         - fetch name of an index operator class
   12906              :  *
   12907              :  * The opclass name is appended (after a space) to buf.
   12908              :  *
   12909              :  * Output is suppressed if the opclass is the default for the given
   12910              :  * actual_datatype.  (If you don't want this behavior, just pass
   12911              :  * InvalidOid for actual_datatype.)
   12912              :  */
   12913              : static void
   12914         6399 : get_opclass_name(Oid opclass, Oid actual_datatype,
   12915              :                  StringInfo buf)
   12916              : {
   12917              :     HeapTuple   ht_opc;
   12918              :     Form_pg_opclass opcrec;
   12919              :     char       *opcname;
   12920              :     char       *nspname;
   12921              : 
   12922         6399 :     ht_opc = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclass));
   12923         6399 :     if (!HeapTupleIsValid(ht_opc))
   12924            0 :         elog(ERROR, "cache lookup failed for opclass %u", opclass);
   12925         6399 :     opcrec = (Form_pg_opclass) GETSTRUCT(ht_opc);
   12926              : 
   12927        12778 :     if (!OidIsValid(actual_datatype) ||
   12928         6379 :         GetDefaultOpClass(actual_datatype, opcrec->opcmethod) != opclass)
   12929              :     {
   12930              :         /* Okay, we need the opclass name.  Do we need to qualify it? */
   12931          279 :         opcname = NameStr(opcrec->opcname);
   12932          279 :         if (OpclassIsVisible(opclass))
   12933          279 :             appendStringInfo(buf, " %s", quote_identifier(opcname));
   12934              :         else
   12935              :         {
   12936            0 :             nspname = get_namespace_name_or_temp(opcrec->opcnamespace);
   12937            0 :             appendStringInfo(buf, " %s.%s",
   12938              :                              quote_identifier(nspname),
   12939              :                              quote_identifier(opcname));
   12940              :         }
   12941              :     }
   12942         6399 :     ReleaseSysCache(ht_opc);
   12943         6399 : }
   12944              : 
   12945              : /*
   12946              :  * generate_opclass_name
   12947              :  *      Compute the name to display for an opclass specified by OID
   12948              :  *
   12949              :  * The result includes all necessary quoting and schema-prefixing.
   12950              :  */
   12951              : char *
   12952            3 : generate_opclass_name(Oid opclass)
   12953              : {
   12954              :     StringInfoData buf;
   12955              : 
   12956            3 :     initStringInfo(&buf);
   12957            3 :     get_opclass_name(opclass, InvalidOid, &buf);
   12958              : 
   12959            3 :     return &buf.data[1];        /* get_opclass_name() prepends space */
   12960              : }
   12961              : 
   12962              : /*
   12963              :  * processIndirection - take care of array and subfield assignment
   12964              :  *
   12965              :  * We strip any top-level FieldStore or assignment SubscriptingRef nodes that
   12966              :  * appear in the input, printing them as decoration for the base column
   12967              :  * name (which we assume the caller just printed).  We might also need to
   12968              :  * strip CoerceToDomain nodes, but only ones that appear above assignment
   12969              :  * nodes.
   12970              :  *
   12971              :  * Returns the subexpression that's to be assigned.
   12972              :  */
   12973              : static Node *
   12974          642 : processIndirection(Node *node, deparse_context *context)
   12975              : {
   12976          642 :     StringInfo  buf = context->buf;
   12977          642 :     CoerceToDomain *cdomain = NULL;
   12978              : 
   12979              :     for (;;)
   12980              :     {
   12981          795 :         if (node == NULL)
   12982            0 :             break;
   12983          795 :         if (IsA(node, FieldStore))
   12984              :         {
   12985           54 :             FieldStore *fstore = (FieldStore *) node;
   12986              :             Oid         typrelid;
   12987              :             char       *fieldname;
   12988              : 
   12989              :             /* lookup tuple type */
   12990           54 :             typrelid = get_typ_typrelid(fstore->resulttype);
   12991           54 :             if (!OidIsValid(typrelid))
   12992            0 :                 elog(ERROR, "argument type %s of FieldStore is not a tuple type",
   12993              :                      format_type_be(fstore->resulttype));
   12994              : 
   12995              :             /*
   12996              :              * Print the field name.  There should only be one target field in
   12997              :              * stored rules.  There could be more than that in executable
   12998              :              * target lists, but this function cannot be used for that case.
   12999              :              */
   13000              :             Assert(list_length(fstore->fieldnums) == 1);
   13001           54 :             fieldname = get_attname(typrelid,
   13002           54 :                                     linitial_int(fstore->fieldnums), false);
   13003           54 :             appendStringInfo(buf, ".%s", quote_identifier(fieldname));
   13004              : 
   13005              :             /*
   13006              :              * We ignore arg since it should be an uninteresting reference to
   13007              :              * the target column or subcolumn.
   13008              :              */
   13009           54 :             node = (Node *) linitial(fstore->newvals);
   13010              :         }
   13011          741 :         else if (IsA(node, SubscriptingRef))
   13012              :         {
   13013           69 :             SubscriptingRef *sbsref = (SubscriptingRef *) node;
   13014              : 
   13015           69 :             if (sbsref->refassgnexpr == NULL)
   13016            0 :                 break;
   13017              : 
   13018           69 :             printSubscripts(sbsref, context);
   13019              : 
   13020              :             /*
   13021              :              * We ignore refexpr since it should be an uninteresting reference
   13022              :              * to the target column or subcolumn.
   13023              :              */
   13024           69 :             node = (Node *) sbsref->refassgnexpr;
   13025              :         }
   13026          672 :         else if (IsA(node, CoerceToDomain))
   13027              :         {
   13028           30 :             cdomain = (CoerceToDomain *) node;
   13029              :             /* If it's an explicit domain coercion, we're done */
   13030           30 :             if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
   13031            0 :                 break;
   13032              :             /* Tentatively descend past the CoerceToDomain */
   13033           30 :             node = (Node *) cdomain->arg;
   13034              :         }
   13035              :         else
   13036          642 :             break;
   13037              :     }
   13038              : 
   13039              :     /*
   13040              :      * If we descended past a CoerceToDomain whose argument turned out not to
   13041              :      * be a FieldStore or array assignment, back up to the CoerceToDomain.
   13042              :      * (This is not enough to be fully correct if there are nested implicit
   13043              :      * CoerceToDomains, but such cases shouldn't ever occur.)
   13044              :      */
   13045          642 :     if (cdomain && node == (Node *) cdomain->arg)
   13046            0 :         node = (Node *) cdomain;
   13047              : 
   13048          642 :     return node;
   13049              : }
   13050              : 
   13051              : static void
   13052          257 : printSubscripts(SubscriptingRef *sbsref, deparse_context *context)
   13053              : {
   13054          257 :     StringInfo  buf = context->buf;
   13055              :     ListCell   *lowlist_item;
   13056              :     ListCell   *uplist_item;
   13057              : 
   13058          257 :     lowlist_item = list_head(sbsref->reflowerindexpr);   /* could be NULL */
   13059          514 :     foreach(uplist_item, sbsref->refupperindexpr)
   13060              :     {
   13061          257 :         appendStringInfoChar(buf, '[');
   13062          257 :         if (lowlist_item)
   13063              :         {
   13064              :             /* If subexpression is NULL, get_rule_expr prints nothing */
   13065            0 :             get_rule_expr((Node *) lfirst(lowlist_item), context, false);
   13066            0 :             appendStringInfoChar(buf, ':');
   13067            0 :             lowlist_item = lnext(sbsref->reflowerindexpr, lowlist_item);
   13068              :         }
   13069              :         /* If subexpression is NULL, get_rule_expr prints nothing */
   13070          257 :         get_rule_expr((Node *) lfirst(uplist_item), context, false);
   13071          257 :         appendStringInfoChar(buf, ']');
   13072              :     }
   13073          257 : }
   13074              : 
   13075              : /*
   13076              :  * quote_identifier         - Quote an identifier only if needed
   13077              :  *
   13078              :  * When quotes are needed, we palloc the required space; slightly
   13079              :  * space-wasteful but well worth it for notational simplicity.
   13080              :  */
   13081              : const char *
   13082      1303747 : quote_identifier(const char *ident)
   13083              : {
   13084              :     /*
   13085              :      * Can avoid quoting if ident starts with a lowercase letter or underscore
   13086              :      * and contains only lowercase letters, digits, and underscores, *and* is
   13087              :      * not any SQL keyword.  Otherwise, supply quotes.
   13088              :      */
   13089      1303747 :     int         nquotes = 0;
   13090              :     bool        safe;
   13091              :     const char *ptr;
   13092              :     char       *result;
   13093              :     char       *optr;
   13094              : 
   13095              :     /*
   13096              :      * would like to use <ctype.h> macros here, but they might yield unwanted
   13097              :      * locale-specific results...
   13098              :      */
   13099      1303747 :     safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
   13100              : 
   13101     11306768 :     for (ptr = ident; *ptr; ptr++)
   13102              :     {
   13103     10003021 :         char        ch = *ptr;
   13104              : 
   13105     10003021 :         if ((ch >= 'a' && ch <= 'z') ||
   13106      1221326 :             (ch >= '0' && ch <= '9') ||
   13107              :             (ch == '_'))
   13108              :         {
   13109              :             /* okay */
   13110              :         }
   13111              :         else
   13112              :         {
   13113        33880 :             safe = false;
   13114        33880 :             if (ch == '"')
   13115           82 :                 nquotes++;
   13116              :         }
   13117              :     }
   13118              : 
   13119      1303747 :     if (quote_all_identifiers)
   13120         6634 :         safe = false;
   13121              : 
   13122      1303747 :     if (safe)
   13123              :     {
   13124              :         /*
   13125              :          * Check for keyword.  We quote keywords except for unreserved ones.
   13126              :          * (In some cases we could avoid quoting a col_name or type_func_name
   13127              :          * keyword, but it seems much harder than it's worth to tell that.)
   13128              :          *
   13129              :          * Note: ScanKeywordLookup() does case-insensitive comparison, but
   13130              :          * that's fine, since we already know we have all-lower-case.
   13131              :          */
   13132      1283684 :         int         kwnum = ScanKeywordLookup(ident, &ScanKeywords);
   13133              : 
   13134      1283684 :         if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
   13135         1735 :             safe = false;
   13136              :     }
   13137              : 
   13138      1303747 :     if (safe)
   13139      1281949 :         return ident;           /* no change needed */
   13140              : 
   13141        21798 :     result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
   13142              : 
   13143        21798 :     optr = result;
   13144        21798 :     *optr++ = '"';
   13145       131479 :     for (ptr = ident; *ptr; ptr++)
   13146              :     {
   13147       109681 :         char        ch = *ptr;
   13148              : 
   13149       109681 :         if (ch == '"')
   13150           82 :             *optr++ = '"';
   13151       109681 :         *optr++ = ch;
   13152              :     }
   13153        21798 :     *optr++ = '"';
   13154        21798 :     *optr = '\0';
   13155              : 
   13156        21798 :     return result;
   13157              : }
   13158              : 
   13159              : /*
   13160              :  * quote_qualified_identifier   - Quote a possibly-qualified identifier
   13161              :  *
   13162              :  * Return a name of the form qualifier.ident, or just ident if qualifier
   13163              :  * is NULL, quoting each component if necessary.  The result is palloc'd.
   13164              :  */
   13165              : char *
   13166       647334 : quote_qualified_identifier(const char *qualifier,
   13167              :                            const char *ident)
   13168              : {
   13169              :     StringInfoData buf;
   13170              : 
   13171       647334 :     initStringInfo(&buf);
   13172       647334 :     if (qualifier)
   13173       229824 :         appendStringInfo(&buf, "%s.", quote_identifier(qualifier));
   13174       647334 :     appendStringInfoString(&buf, quote_identifier(ident));
   13175       647334 :     return buf.data;
   13176              : }
   13177              : 
   13178              : /*
   13179              :  * get_relation_name
   13180              :  *      Get the unqualified name of a relation specified by OID
   13181              :  *
   13182              :  * This differs from the underlying get_rel_name() function in that it will
   13183              :  * throw error instead of silently returning NULL if the OID is bad.
   13184              :  */
   13185              : static char *
   13186         8518 : get_relation_name(Oid relid)
   13187              : {
   13188         8518 :     char       *relname = get_rel_name(relid);
   13189              : 
   13190         8518 :     if (!relname)
   13191            0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   13192         8518 :     return relname;
   13193              : }
   13194              : 
   13195              : /*
   13196              :  * generate_relation_name
   13197              :  *      Compute the name to display for a relation specified by OID
   13198              :  *
   13199              :  * The result includes all necessary quoting and schema-prefixing.
   13200              :  *
   13201              :  * If namespaces isn't NIL, it must be a list of deparse_namespace nodes.
   13202              :  * We will forcibly qualify the relation name if it equals any CTE name
   13203              :  * visible in the namespace list.
   13204              :  */
   13205              : static char *
   13206         4080 : generate_relation_name(Oid relid, List *namespaces)
   13207              : {
   13208              :     HeapTuple   tp;
   13209              :     Form_pg_class reltup;
   13210              :     bool        need_qual;
   13211              :     ListCell   *nslist;
   13212              :     char       *relname;
   13213              :     char       *nspname;
   13214              :     char       *result;
   13215              : 
   13216         4080 :     tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   13217         4080 :     if (!HeapTupleIsValid(tp))
   13218            0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   13219         4080 :     reltup = (Form_pg_class) GETSTRUCT(tp);
   13220         4080 :     relname = NameStr(reltup->relname);
   13221              : 
   13222              :     /* Check for conflicting CTE name */
   13223         4080 :     need_qual = false;
   13224         6975 :     foreach(nslist, namespaces)
   13225              :     {
   13226         2895 :         deparse_namespace *dpns = (deparse_namespace *) lfirst(nslist);
   13227              :         ListCell   *ctlist;
   13228              : 
   13229         2961 :         foreach(ctlist, dpns->ctes)
   13230              :         {
   13231           66 :             CommonTableExpr *cte = (CommonTableExpr *) lfirst(ctlist);
   13232              : 
   13233           66 :             if (strcmp(cte->ctename, relname) == 0)
   13234              :             {
   13235            0 :                 need_qual = true;
   13236            0 :                 break;
   13237              :             }
   13238              :         }
   13239         2895 :         if (need_qual)
   13240            0 :             break;
   13241              :     }
   13242              : 
   13243              :     /* Otherwise, qualify the name if not visible in search path */
   13244         4080 :     if (!need_qual)
   13245         4080 :         need_qual = !RelationIsVisible(relid);
   13246              : 
   13247         4080 :     if (need_qual)
   13248         1181 :         nspname = get_namespace_name_or_temp(reltup->relnamespace);
   13249              :     else
   13250         2899 :         nspname = NULL;
   13251              : 
   13252         4080 :     result = quote_qualified_identifier(nspname, relname);
   13253              : 
   13254         4080 :     ReleaseSysCache(tp);
   13255              : 
   13256         4080 :     return result;
   13257              : }
   13258              : 
   13259              : /*
   13260              :  * generate_qualified_relation_name
   13261              :  *      Compute the name to display for a relation specified by OID
   13262              :  *
   13263              :  * As above, but unconditionally schema-qualify the name.
   13264              :  */
   13265              : static char *
   13266         4126 : generate_qualified_relation_name(Oid relid)
   13267              : {
   13268              :     HeapTuple   tp;
   13269              :     Form_pg_class reltup;
   13270              :     char       *relname;
   13271              :     char       *nspname;
   13272              :     char       *result;
   13273              : 
   13274         4126 :     tp = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   13275         4126 :     if (!HeapTupleIsValid(tp))
   13276            0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   13277         4126 :     reltup = (Form_pg_class) GETSTRUCT(tp);
   13278         4126 :     relname = NameStr(reltup->relname);
   13279              : 
   13280         4126 :     nspname = get_namespace_name_or_temp(reltup->relnamespace);
   13281         4126 :     if (!nspname)
   13282            0 :         elog(ERROR, "cache lookup failed for namespace %u",
   13283              :              reltup->relnamespace);
   13284              : 
   13285         4126 :     result = quote_qualified_identifier(nspname, relname);
   13286              : 
   13287         4126 :     ReleaseSysCache(tp);
   13288              : 
   13289         4126 :     return result;
   13290              : }
   13291              : 
   13292              : /*
   13293              :  * generate_function_name
   13294              :  *      Compute the name to display for a function specified by OID,
   13295              :  *      given that it is being called with the specified actual arg names and
   13296              :  *      types.  (Those matter because of ambiguous-function resolution rules.)
   13297              :  *
   13298              :  * If we're dealing with a potentially variadic function (in practice, this
   13299              :  * means a FuncExpr or Aggref, not some other way of calling a function), then
   13300              :  * has_variadic must specify whether variadic arguments have been merged,
   13301              :  * and *use_variadic_p will be set to indicate whether to print VARIADIC in
   13302              :  * the output.  For non-FuncExpr cases, has_variadic should be false and
   13303              :  * use_variadic_p can be NULL.
   13304              :  *
   13305              :  * inGroupBy must be true if we're deparsing a GROUP BY clause.
   13306              :  *
   13307              :  * The result includes all necessary quoting and schema-prefixing.
   13308              :  */
   13309              : static char *
   13310         7710 : generate_function_name(Oid funcid, int nargs, List *argnames, Oid *argtypes,
   13311              :                        bool has_variadic, bool *use_variadic_p,
   13312              :                        bool inGroupBy)
   13313              : {
   13314              :     char       *result;
   13315              :     HeapTuple   proctup;
   13316              :     Form_pg_proc procform;
   13317              :     char       *proname;
   13318              :     bool        use_variadic;
   13319              :     char       *nspname;
   13320              :     FuncDetailCode p_result;
   13321              :     int         fgc_flags;
   13322              :     Oid         p_funcid;
   13323              :     Oid         p_rettype;
   13324              :     bool        p_retset;
   13325              :     int         p_nvargs;
   13326              :     Oid         p_vatype;
   13327              :     Oid        *p_true_typeids;
   13328         7710 :     bool        force_qualify = false;
   13329              : 
   13330         7710 :     proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid));
   13331         7710 :     if (!HeapTupleIsValid(proctup))
   13332            0 :         elog(ERROR, "cache lookup failed for function %u", funcid);
   13333         7710 :     procform = (Form_pg_proc) GETSTRUCT(proctup);
   13334         7710 :     proname = NameStr(procform->proname);
   13335              : 
   13336              :     /*
   13337              :      * Due to parser hacks to avoid needing to reserve CUBE, we need to force
   13338              :      * qualification of some function names within GROUP BY.
   13339              :      */
   13340         7710 :     if (inGroupBy)
   13341              :     {
   13342            0 :         if (strcmp(proname, "cube") == 0 || strcmp(proname, "rollup") == 0)
   13343            0 :             force_qualify = true;
   13344              :     }
   13345              : 
   13346              :     /*
   13347              :      * Determine whether VARIADIC should be printed.  We must do this first
   13348              :      * since it affects the lookup rules in func_get_detail().
   13349              :      *
   13350              :      * We always print VARIADIC if the function has a merged variadic-array
   13351              :      * argument.  Note that this is always the case for functions taking a
   13352              :      * VARIADIC argument type other than VARIADIC ANY.  If we omitted VARIADIC
   13353              :      * and printed the array elements as separate arguments, the call could
   13354              :      * match a newer non-VARIADIC function.
   13355              :      */
   13356         7710 :     if (use_variadic_p)
   13357              :     {
   13358              :         /* Parser should not have set funcvariadic unless fn is variadic */
   13359              :         Assert(!has_variadic || OidIsValid(procform->provariadic));
   13360         6840 :         use_variadic = has_variadic;
   13361         6840 :         *use_variadic_p = use_variadic;
   13362              :     }
   13363              :     else
   13364              :     {
   13365              :         Assert(!has_variadic);
   13366          870 :         use_variadic = false;
   13367              :     }
   13368              : 
   13369              :     /*
   13370              :      * The idea here is to schema-qualify only if the parser would fail to
   13371              :      * resolve the correct function given the unqualified func name with the
   13372              :      * specified argtypes and VARIADIC flag.  But if we already decided to
   13373              :      * force qualification, then we can skip the lookup and pretend we didn't
   13374              :      * find it.
   13375              :      */
   13376         7710 :     if (!force_qualify)
   13377         7710 :         p_result = func_get_detail(list_make1(makeString(proname)),
   13378              :                                    NIL, argnames, nargs, argtypes,
   13379              :                                    !use_variadic, true, false,
   13380              :                                    &fgc_flags,
   13381              :                                    &p_funcid, &p_rettype,
   13382              :                                    &p_retset, &p_nvargs, &p_vatype,
   13383         7710 :                                    &p_true_typeids, NULL);
   13384              :     else
   13385              :     {
   13386            0 :         p_result = FUNCDETAIL_NOTFOUND;
   13387            0 :         p_funcid = InvalidOid;
   13388              :     }
   13389              : 
   13390         7710 :     if ((p_result == FUNCDETAIL_NORMAL ||
   13391          634 :          p_result == FUNCDETAIL_AGGREGATE ||
   13392         7139 :          p_result == FUNCDETAIL_WINDOWFUNC) &&
   13393         7139 :         p_funcid == funcid)
   13394         7139 :         nspname = NULL;
   13395              :     else
   13396          571 :         nspname = get_namespace_name_or_temp(procform->pronamespace);
   13397              : 
   13398         7710 :     result = quote_qualified_identifier(nspname, proname);
   13399              : 
   13400         7710 :     ReleaseSysCache(proctup);
   13401              : 
   13402         7710 :     return result;
   13403              : }
   13404              : 
   13405              : /*
   13406              :  * generate_operator_name
   13407              :  *      Compute the name to display for an operator specified by OID,
   13408              :  *      given that it is being called with the specified actual arg types.
   13409              :  *      (Arg types matter because of ambiguous-operator resolution rules.
   13410              :  *      Pass InvalidOid for unused arg of a unary operator.)
   13411              :  *
   13412              :  * The result includes all necessary quoting and schema-prefixing,
   13413              :  * plus the OPERATOR() decoration needed to use a qualified operator name
   13414              :  * in an expression.
   13415              :  */
   13416              : static char *
   13417        33099 : generate_operator_name(Oid operid, Oid arg1, Oid arg2)
   13418              : {
   13419              :     StringInfoData buf;
   13420              :     HeapTuple   opertup;
   13421              :     Form_pg_operator operform;
   13422              :     char       *oprname;
   13423              :     char       *nspname;
   13424              :     Operator    p_result;
   13425              : 
   13426        33099 :     initStringInfo(&buf);
   13427              : 
   13428        33099 :     opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operid));
   13429        33099 :     if (!HeapTupleIsValid(opertup))
   13430            0 :         elog(ERROR, "cache lookup failed for operator %u", operid);
   13431        33099 :     operform = (Form_pg_operator) GETSTRUCT(opertup);
   13432        33099 :     oprname = NameStr(operform->oprname);
   13433              : 
   13434              :     /*
   13435              :      * The idea here is to schema-qualify only if the parser would fail to
   13436              :      * resolve the correct operator given the unqualified op name with the
   13437              :      * specified argtypes.
   13438              :      */
   13439        33099 :     switch (operform->oprkind)
   13440              :     {
   13441        33084 :         case 'b':
   13442        33084 :             p_result = oper(NULL, list_make1(makeString(oprname)), arg1, arg2,
   13443              :                             true, -1);
   13444        33084 :             break;
   13445           15 :         case 'l':
   13446           15 :             p_result = left_oper(NULL, list_make1(makeString(oprname)), arg2,
   13447              :                                  true, -1);
   13448           15 :             break;
   13449            0 :         default:
   13450            0 :             elog(ERROR, "unrecognized oprkind: %d", operform->oprkind);
   13451              :             p_result = NULL;    /* keep compiler quiet */
   13452              :             break;
   13453              :     }
   13454              : 
   13455        33099 :     if (p_result != NULL && oprid(p_result) == operid)
   13456        33094 :         nspname = NULL;
   13457              :     else
   13458              :     {
   13459            5 :         nspname = get_namespace_name_or_temp(operform->oprnamespace);
   13460            5 :         appendStringInfo(&buf, "OPERATOR(%s.", quote_identifier(nspname));
   13461              :     }
   13462              : 
   13463        33099 :     appendStringInfoString(&buf, oprname);
   13464              : 
   13465        33099 :     if (nspname)
   13466            5 :         appendStringInfoChar(&buf, ')');
   13467              : 
   13468        33099 :     if (p_result != NULL)
   13469        33094 :         ReleaseSysCache(p_result);
   13470              : 
   13471        33099 :     ReleaseSysCache(opertup);
   13472              : 
   13473        33099 :     return buf.data;
   13474              : }
   13475              : 
   13476              : /*
   13477              :  * generate_operator_clause --- generate a binary-operator WHERE clause
   13478              :  *
   13479              :  * This is used for internally-generated-and-executed SQL queries, where
   13480              :  * precision is essential and readability is secondary.  The basic
   13481              :  * requirement is to append "leftop op rightop" to buf, where leftop and
   13482              :  * rightop are given as strings and are assumed to yield types leftoptype
   13483              :  * and rightoptype; the operator is identified by OID.  The complexity
   13484              :  * comes from needing to be sure that the parser will select the desired
   13485              :  * operator when the query is parsed.  We always name the operator using
   13486              :  * OPERATOR(schema.op) syntax, so as to avoid search-path uncertainties.
   13487              :  * We have to emit casts too, if either input isn't already the input type
   13488              :  * of the operator; else we are at the mercy of the parser's heuristics for
   13489              :  * ambiguous-operator resolution.  The caller must ensure that leftop and
   13490              :  * rightop are suitable arguments for a cast operation; it's best to insert
   13491              :  * parentheses if they aren't just variables or parameters.
   13492              :  */
   13493              : void
   13494         3361 : generate_operator_clause(StringInfo buf,
   13495              :                          const char *leftop, Oid leftoptype,
   13496              :                          Oid opoid,
   13497              :                          const char *rightop, Oid rightoptype)
   13498              : {
   13499              :     HeapTuple   opertup;
   13500              :     Form_pg_operator operform;
   13501              :     char       *oprname;
   13502              :     char       *nspname;
   13503              : 
   13504         3361 :     opertup = SearchSysCache1(OPEROID, ObjectIdGetDatum(opoid));
   13505         3361 :     if (!HeapTupleIsValid(opertup))
   13506            0 :         elog(ERROR, "cache lookup failed for operator %u", opoid);
   13507         3361 :     operform = (Form_pg_operator) GETSTRUCT(opertup);
   13508              :     Assert(operform->oprkind == 'b');
   13509         3361 :     oprname = NameStr(operform->oprname);
   13510              : 
   13511         3361 :     nspname = get_namespace_name(operform->oprnamespace);
   13512              : 
   13513         3361 :     appendStringInfoString(buf, leftop);
   13514         3361 :     if (leftoptype != operform->oprleft)
   13515          599 :         add_cast_to(buf, operform->oprleft);
   13516         3361 :     appendStringInfo(buf, " OPERATOR(%s.", quote_identifier(nspname));
   13517         3361 :     appendStringInfoString(buf, oprname);
   13518         3361 :     appendStringInfo(buf, ") %s", rightop);
   13519         3361 :     if (rightoptype != operform->oprright)
   13520          484 :         add_cast_to(buf, operform->oprright);
   13521              : 
   13522         3361 :     ReleaseSysCache(opertup);
   13523         3361 : }
   13524              : 
   13525              : /*
   13526              :  * Add a cast specification to buf.  We spell out the type name the hard way,
   13527              :  * intentionally not using format_type_be().  This is to avoid corner cases
   13528              :  * for CHARACTER, BIT, and perhaps other types, where specifying the type
   13529              :  * using SQL-standard syntax results in undesirable data truncation.  By
   13530              :  * doing it this way we can be certain that the cast will have default (-1)
   13531              :  * target typmod.
   13532              :  */
   13533              : static void
   13534         1083 : add_cast_to(StringInfo buf, Oid typid)
   13535              : {
   13536              :     HeapTuple   typetup;
   13537              :     Form_pg_type typform;
   13538              :     char       *typname;
   13539              :     char       *nspname;
   13540              : 
   13541         1083 :     typetup = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
   13542         1083 :     if (!HeapTupleIsValid(typetup))
   13543            0 :         elog(ERROR, "cache lookup failed for type %u", typid);
   13544         1083 :     typform = (Form_pg_type) GETSTRUCT(typetup);
   13545              : 
   13546         1083 :     typname = NameStr(typform->typname);
   13547         1083 :     nspname = get_namespace_name_or_temp(typform->typnamespace);
   13548              : 
   13549         1083 :     appendStringInfo(buf, "::%s.%s",
   13550              :                      quote_identifier(nspname), quote_identifier(typname));
   13551              : 
   13552         1083 :     ReleaseSysCache(typetup);
   13553         1083 : }
   13554              : 
   13555              : /*
   13556              :  * generate_qualified_type_name
   13557              :  *      Compute the name to display for a type specified by OID
   13558              :  *
   13559              :  * This is different from format_type_be() in that we unconditionally
   13560              :  * schema-qualify the name.  That also means no special syntax for
   13561              :  * SQL-standard type names ... although in current usage, this should
   13562              :  * only get used for domains, so such cases wouldn't occur anyway.
   13563              :  */
   13564              : static char *
   13565            7 : generate_qualified_type_name(Oid typid)
   13566              : {
   13567              :     HeapTuple   tp;
   13568              :     Form_pg_type typtup;
   13569              :     char       *typname;
   13570              :     char       *nspname;
   13571              :     char       *result;
   13572              : 
   13573            7 :     tp = SearchSysCache1(TYPEOID, ObjectIdGetDatum(typid));
   13574            7 :     if (!HeapTupleIsValid(tp))
   13575            0 :         elog(ERROR, "cache lookup failed for type %u", typid);
   13576            7 :     typtup = (Form_pg_type) GETSTRUCT(tp);
   13577            7 :     typname = NameStr(typtup->typname);
   13578              : 
   13579            7 :     nspname = get_namespace_name_or_temp(typtup->typnamespace);
   13580            7 :     if (!nspname)
   13581            0 :         elog(ERROR, "cache lookup failed for namespace %u",
   13582              :              typtup->typnamespace);
   13583              : 
   13584            7 :     result = quote_qualified_identifier(nspname, typname);
   13585              : 
   13586            7 :     ReleaseSysCache(tp);
   13587              : 
   13588            7 :     return result;
   13589              : }
   13590              : 
   13591              : /*
   13592              :  * generate_collation_name
   13593              :  *      Compute the name to display for a collation specified by OID
   13594              :  *
   13595              :  * The result includes all necessary quoting and schema-prefixing.
   13596              :  */
   13597              : char *
   13598          147 : generate_collation_name(Oid collid)
   13599              : {
   13600              :     HeapTuple   tp;
   13601              :     Form_pg_collation colltup;
   13602              :     char       *collname;
   13603              :     char       *nspname;
   13604              :     char       *result;
   13605              : 
   13606          147 :     tp = SearchSysCache1(COLLOID, ObjectIdGetDatum(collid));
   13607          147 :     if (!HeapTupleIsValid(tp))
   13608            0 :         elog(ERROR, "cache lookup failed for collation %u", collid);
   13609          147 :     colltup = (Form_pg_collation) GETSTRUCT(tp);
   13610          147 :     collname = NameStr(colltup->collname);
   13611              : 
   13612          147 :     if (!CollationIsVisible(collid))
   13613            0 :         nspname = get_namespace_name_or_temp(colltup->collnamespace);
   13614              :     else
   13615          147 :         nspname = NULL;
   13616              : 
   13617          147 :     result = quote_qualified_identifier(nspname, collname);
   13618              : 
   13619          147 :     ReleaseSysCache(tp);
   13620              : 
   13621          147 :     return result;
   13622              : }
   13623              : 
   13624              : /*
   13625              :  * Given a C string, produce a TEXT datum.
   13626              :  *
   13627              :  * We assume that the input was palloc'd and may be freed.
   13628              :  */
   13629              : static text *
   13630        22826 : string_to_text(char *str)
   13631              : {
   13632              :     text       *result;
   13633              : 
   13634        22826 :     result = cstring_to_text(str);
   13635        22826 :     pfree(str);
   13636        22826 :     return result;
   13637              : }
   13638              : 
   13639              : /*
   13640              :  * Generate a C string representing a relation options from text[] datum.
   13641              :  */
   13642              : static void
   13643          122 : get_reloptions(StringInfo buf, Datum reloptions)
   13644              : {
   13645              :     Datum      *options;
   13646              :     int         noptions;
   13647              :     int         i;
   13648              : 
   13649          122 :     deconstruct_array_builtin(DatumGetArrayTypeP(reloptions), TEXTOID,
   13650              :                               &options, NULL, &noptions);
   13651              : 
   13652          254 :     for (i = 0; i < noptions; i++)
   13653              :     {
   13654          132 :         char       *option = TextDatumGetCString(options[i]);
   13655              :         char       *name;
   13656              :         char       *separator;
   13657              :         char       *value;
   13658              : 
   13659              :         /*
   13660              :          * Each array element should have the form name=value.  If the "=" is
   13661              :          * missing for some reason, treat it like an empty value.
   13662              :          */
   13663          132 :         name = option;
   13664          132 :         separator = strchr(option, '=');
   13665          132 :         if (separator)
   13666              :         {
   13667          132 :             *separator = '\0';
   13668          132 :             value = separator + 1;
   13669              :         }
   13670              :         else
   13671            0 :             value = "";
   13672              : 
   13673          132 :         if (i > 0)
   13674           10 :             appendStringInfoString(buf, ", ");
   13675          132 :         appendStringInfo(buf, "%s=", quote_identifier(name));
   13676              : 
   13677              :         /*
   13678              :          * In general we need to quote the value; but to avoid unnecessary
   13679              :          * clutter, do not quote if it is an identifier that would not need
   13680              :          * quoting.  (We could also allow numbers, but that is a bit trickier
   13681              :          * than it looks --- for example, are leading zeroes significant?  We
   13682              :          * don't want to assume very much here about what custom reloptions
   13683              :          * might mean.)
   13684              :          */
   13685          132 :         if (quote_identifier(value) == value)
   13686            4 :             appendStringInfoString(buf, value);
   13687              :         else
   13688          128 :             simple_quote_literal(buf, value);
   13689              : 
   13690          132 :         pfree(option);
   13691              :     }
   13692          122 : }
   13693              : 
   13694              : /*
   13695              :  * Generate a C string representing a relation's reloptions, or NULL if none.
   13696              :  */
   13697              : static char *
   13698         3907 : flatten_reloptions(Oid relid)
   13699              : {
   13700         3907 :     char       *result = NULL;
   13701              :     HeapTuple   tuple;
   13702              :     Datum       reloptions;
   13703              :     bool        isnull;
   13704              : 
   13705         3907 :     tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
   13706         3907 :     if (!HeapTupleIsValid(tuple))
   13707            0 :         elog(ERROR, "cache lookup failed for relation %u", relid);
   13708              : 
   13709         3907 :     reloptions = SysCacheGetAttr(RELOID, tuple,
   13710              :                                  Anum_pg_class_reloptions, &isnull);
   13711         3907 :     if (!isnull)
   13712              :     {
   13713              :         StringInfoData buf;
   13714              : 
   13715          105 :         initStringInfo(&buf);
   13716          105 :         get_reloptions(&buf, reloptions);
   13717              : 
   13718          105 :         result = buf.data;
   13719              :     }
   13720              : 
   13721         3907 :     ReleaseSysCache(tuple);
   13722              : 
   13723         3907 :     return result;
   13724              : }
   13725              : 
   13726              : /*
   13727              :  * get_range_partbound_string
   13728              :  *      A C string representation of one range partition bound
   13729              :  */
   13730              : char *
   13731         2726 : get_range_partbound_string(List *bound_datums)
   13732              : {
   13733              :     deparse_context context;
   13734              :     StringInfoData buf;
   13735              :     ListCell   *cell;
   13736              :     char       *sep;
   13737              : 
   13738         2726 :     initStringInfo(&buf);
   13739         2726 :     memset(&context, 0, sizeof(deparse_context));
   13740         2726 :     context.buf = &buf;
   13741              : 
   13742         2726 :     appendStringInfoChar(&buf, '(');
   13743         2726 :     sep = "";
   13744         5848 :     foreach(cell, bound_datums)
   13745              :     {
   13746         3122 :         PartitionRangeDatum *datum =
   13747              :             lfirst_node(PartitionRangeDatum, cell);
   13748              : 
   13749         3122 :         appendStringInfoString(&buf, sep);
   13750         3122 :         if (datum->kind == PARTITION_RANGE_DATUM_MINVALUE)
   13751          111 :             appendStringInfoString(&buf, "MINVALUE");
   13752         3011 :         else if (datum->kind == PARTITION_RANGE_DATUM_MAXVALUE)
   13753           60 :             appendStringInfoString(&buf, "MAXVALUE");
   13754              :         else
   13755              :         {
   13756         2951 :             Const      *val = castNode(Const, datum->value);
   13757              : 
   13758         2951 :             get_const_expr(val, &context, -1);
   13759              :         }
   13760         3122 :         sep = ", ";
   13761              :     }
   13762         2726 :     appendStringInfoChar(&buf, ')');
   13763              : 
   13764         2726 :     return buf.data;
   13765              : }
        

Generated by: LCOV version 2.0-1