in ext/pg_query/src_backend_utils_adt_ruleutils.c [309:1545]
typedef void (*rsv_callback) (Node *node, deparse_context *context,
void *callback_arg);
/* ----------
* Global data
* ----------
*/
/* GUC parameters */
__thread bool quote_all_identifiers = false;
/* ----------
* Local functions
*
* Most of these functions used to use fixed-size buffers to build their
* results. Now, they take an (already initialized) StringInfo object
* as a parameter, and append their text output to its contents.
* ----------
*/
static char *deparse_expression_pretty(Node *expr, List *dpcontext,
bool forceprefix, bool showimplicit,
int prettyFlags, int startIndent);
static char *pg_get_viewdef_worker(Oid viewoid,
int prettyFlags, int wrapColumn);
static char *pg_get_triggerdef_worker(Oid trigid, bool pretty);
static int decompile_column_index_array(Datum column_index_array, Oid relId,
StringInfo buf);
static char *pg_get_ruledef_worker(Oid ruleoid, int prettyFlags);
static char *pg_get_indexdef_worker(Oid indexrelid, int colno,
const Oid *excludeOps,
bool attrsOnly, bool keysOnly,
bool showTblSpc, bool inherits,
int prettyFlags, bool missing_ok);
static char *pg_get_statisticsobj_worker(Oid statextid, bool missing_ok);
static char *pg_get_partkeydef_worker(Oid relid, int prettyFlags,
bool attrsOnly, bool missing_ok);
static char *pg_get_constraintdef_worker(Oid constraintId, bool fullCommand,
int prettyFlags, bool missing_ok);
static text *pg_get_expr_worker(text *expr, Oid relid, const char *relname,
int prettyFlags);
static int print_function_arguments(StringInfo buf, HeapTuple proctup,
bool print_table_args, bool print_defaults);
static void print_function_rettype(StringInfo buf, HeapTuple proctup);
static void print_function_trftypes(StringInfo buf, HeapTuple proctup);
static void set_rtable_names(deparse_namespace *dpns, List *parent_namespaces,
Bitmapset *rels_used);
static void set_deparse_for_query(deparse_namespace *dpns, Query *query,
List *parent_namespaces);
static void set_simple_column_names(deparse_namespace *dpns);
static bool has_dangerous_join_using(deparse_namespace *dpns, Node *jtnode);
static void set_using_names(deparse_namespace *dpns, Node *jtnode,
List *parentUsing);
static void set_relation_column_names(deparse_namespace *dpns,
RangeTblEntry *rte,
deparse_columns *colinfo);
static void set_join_column_names(deparse_namespace *dpns, RangeTblEntry *rte,
deparse_columns *colinfo);
static bool colname_is_unique(const char *colname, deparse_namespace *dpns,
deparse_columns *colinfo);
static char *make_colname_unique(char *colname, deparse_namespace *dpns,
deparse_columns *colinfo);
static void expand_colnames_array_to(deparse_columns *colinfo, int n);
static void identify_join_columns(JoinExpr *j, RangeTblEntry *jrte,
deparse_columns *colinfo);
static char *get_rtable_name(int rtindex, deparse_context *context);
static void set_deparse_plan(deparse_namespace *dpns, Plan *plan);
static void push_child_plan(deparse_namespace *dpns, Plan *plan,
deparse_namespace *save_dpns);
static void pop_child_plan(deparse_namespace *dpns,
deparse_namespace *save_dpns);
static void push_ancestor_plan(deparse_namespace *dpns, ListCell *ancestor_cell,
deparse_namespace *save_dpns);
static void pop_ancestor_plan(deparse_namespace *dpns,
deparse_namespace *save_dpns);
static void make_ruledef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
int prettyFlags);
static void make_viewdef(StringInfo buf, HeapTuple ruletup, TupleDesc rulettc,
int prettyFlags, int wrapColumn);
static void get_query_def(Query *query, StringInfo buf, List *parentnamespace,
TupleDesc resultDesc,
int prettyFlags, int wrapColumn, int startIndent);
static void get_values_def(List *values_lists, deparse_context *context);
static void get_with_clause(Query *query, deparse_context *context);
static void get_select_query_def(Query *query, deparse_context *context,
TupleDesc resultDesc);
static void get_insert_query_def(Query *query, deparse_context *context);
static void get_update_query_def(Query *query, deparse_context *context);
static void get_update_query_targetlist_def(Query *query, List *targetList,
deparse_context *context,
RangeTblEntry *rte);
static void get_delete_query_def(Query *query, deparse_context *context);
static void get_utility_query_def(Query *query, deparse_context *context);
static void get_basic_select_query(Query *query, deparse_context *context,
TupleDesc resultDesc);
static void get_target_list(List *targetList, deparse_context *context,
TupleDesc resultDesc);
static void get_setop_query(Node *setOp, Query *query,
deparse_context *context,
TupleDesc resultDesc);
static Node *get_rule_sortgroupclause(Index ref, List *tlist,
bool force_colno,
deparse_context *context);
static void get_rule_groupingset(GroupingSet *gset, List *targetlist,
bool omit_parens, deparse_context *context);
static void get_rule_orderby(List *orderList, List *targetList,
bool force_colno, deparse_context *context);
static void get_rule_windowclause(Query *query, deparse_context *context);
static void get_rule_windowspec(WindowClause *wc, List *targetList,
deparse_context *context);
static char *get_variable(Var *var, int levelsup, bool istoplevel,
deparse_context *context);
static void get_special_variable(Node *node, deparse_context *context,
void *callback_arg);
static void resolve_special_varno(Node *node, deparse_context *context,
rsv_callback callback, void *callback_arg);
static Node *find_param_referent(Param *param, deparse_context *context,
deparse_namespace **dpns_p, ListCell **ancestor_cell_p);
static void get_parameter(Param *param, deparse_context *context);
static const char *get_simple_binary_op_name(OpExpr *expr);
static bool isSimpleNode(Node *node, Node *parentNode, int prettyFlags);
static void appendContextKeyword(deparse_context *context, const char *str,
int indentBefore, int indentAfter, int indentPlus);
static void removeStringInfoSpaces(StringInfo str);
static void get_rule_expr(Node *node, deparse_context *context,
bool showimplicit);
static void get_rule_expr_toplevel(Node *node, deparse_context *context,
bool showimplicit);
static void get_rule_expr_funccall(Node *node, deparse_context *context,
bool showimplicit);
static bool looks_like_function(Node *node);
static void get_oper_expr(OpExpr *expr, deparse_context *context);
static void get_func_expr(FuncExpr *expr, deparse_context *context,
bool showimplicit);
static void get_agg_expr(Aggref *aggref, deparse_context *context,
Aggref *original_aggref);
static void get_agg_combine_expr(Node *node, deparse_context *context,
void *callback_arg);
static void get_windowfunc_expr(WindowFunc *wfunc, deparse_context *context);
static void get_coercion_expr(Node *arg, deparse_context *context,
Oid resulttype, int32 resulttypmod,
Node *parentNode);
static void get_const_expr(Const *constval, deparse_context *context,
int showtype);
static void get_const_collation(Const *constval, deparse_context *context);
static void simple_quote_literal(StringInfo buf, const char *val);
static void get_sublink_expr(SubLink *sublink, deparse_context *context);
static void get_tablefunc(TableFunc *tf, deparse_context *context,
bool showimplicit);
static void get_from_clause(Query *query, const char *prefix,
deparse_context *context);
static void get_from_clause_item(Node *jtnode, Query *query,
deparse_context *context);
static void get_column_alias_list(deparse_columns *colinfo,
deparse_context *context);
static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
deparse_columns *colinfo,
deparse_context *context);
static void get_tablesample_def(TableSampleClause *tablesample,
deparse_context *context);
static void get_opclass_name(Oid opclass, Oid actual_datatype,
StringInfo buf);
static Node *processIndirection(Node *node, deparse_context *context);
static void printSubscripts(SubscriptingRef *sbsref, deparse_context *context);
static char *get_relation_name(Oid relid);
static char *generate_relation_name(Oid relid, List *namespaces);
static char *generate_qualified_relation_name(Oid relid);
static char *generate_function_name(Oid funcid, int nargs,
List *argnames, Oid *argtypes,
bool has_variadic, bool *use_variadic_p,
ParseExprKind special_exprkind);
static char *generate_operator_name(Oid operid, Oid arg1, Oid arg2);
static void add_cast_to(StringInfo buf, Oid typid);
static char *generate_qualified_type_name(Oid typid);
static text *string_to_text(char *str);
static char *flatten_reloptions(Oid relid);
static void get_reloptions(StringInfo buf, Datum reloptions);
#define only_marker(rte) ((rte)->inh ? "" : "ONLY ")
/* ----------
* pg_get_ruledef - Do it all and return a text
* that could be used as a statement
* to recreate the rule
* ----------
*/
/* ----------
* pg_get_viewdef - Mainly the same thing, but we
* only return the SELECT part of a view
* ----------
*/
/*
* Common code for by-OID and by-name variants of pg_get_viewdef
*/
/* ----------
* pg_get_triggerdef - Get the definition of a trigger
* ----------
*/
/* ----------
* pg_get_indexdef - Get the definition of an index
*
* In the extended version, there is a colno argument as well as pretty bool.
* if colno == 0, we want a complete index definition.
* if colno > 0, we only want the Nth index key's variable or expression.
*
* Note that the SQL-function versions of this omit any info about the
* index tablespace; this is intentional because pg_dump wants it that way.
* However pg_get_indexdef_string() includes the index tablespace.
* ----------
*/
/*
* Internal version for use by ALTER TABLE.
* Includes a tablespace clause in the result.
* Returns a palloc'd C string; no pretty-printing.
*/
/* Internal version that just reports the key-column definitions */
/*
* Internal workhorse to decompile an index definition.
*
* This is now used for exclusion constraints as well: if excludeOps is not
* NULL then it points to an array of exclusion operator OIDs.
*/
/*
* pg_get_statisticsobjdef
* Get the definition of an extended statistics object
*/
/*
* Internal workhorse to decompile an extended statistics object.
*/
/*
* pg_get_partkeydef
*
* Returns the partition key specification, ie, the following:
*
* PARTITION BY { RANGE | LIST | HASH } (column opt_collation opt_opclass [, ...])
*/
/* Internal version that just reports the column definitions */
/*
* Internal workhorse to decompile a partition key definition.
*/
/*
* pg_get_partition_constraintdef
*
* Returns partition constraint expression as a string for the input relation
*/
/*
* pg_get_partconstrdef_string
*
* Returns the partition constraint as a C-string for the input relation, with
* the given alias. No pretty-printing.
*/
/*
* pg_get_constraintdef
*
* Returns the definition for the constraint, ie, everything that needs to
* appear after "ALTER TABLE ... ADD CONSTRAINT <constraintname>".
*/
/*
* Internal version that returns a full ALTER TABLE ... ADD CONSTRAINT command
*/
/*
* As of 9.4, we now use an MVCC snapshot for this.
*/
/*
* Convert an int16[] Datum into a comma-separated list of column names
* for the indicated relation; append the list to buf. Returns the number
* of keys.
*/
/* ----------
* pg_get_expr - Decompile an expression tree
*
* Input: an expression tree in nodeToString form, and a relation OID
*
* Output: reverse-listed expression
*
* Currently, the expression can only refer to a single relation, namely
* the one specified by the second parameter. This is sufficient for
* partial indexes, column default expressions, etc. We also support
* Var-free expressions, for which the OID can be InvalidOid.
* ----------
*/
/* ----------
* pg_get_userbyid - Get a user name by roleid and
* fallback to 'unknown (OID=n)'
* ----------
*/
/*
* pg_get_serial_sequence
* Get the name of the sequence used by an identity or serial column,
* formatted suitably for passing to setval, nextval or currval.
* First parameter is not treated as double-quoted, second parameter
* is --- see documentation for reason.
*/
/*
* pg_get_functiondef
* Returns the complete "CREATE OR REPLACE FUNCTION ..." statement for
* the specified function.
*
* Note: if you change the output format of this function, be careful not
* to break psql's rules (in \ef and \sf) for identifying the start of the
* function body. To wit: the function body starts on a line that begins
* with "AS ", and no preceding line will look like that.
*/
/*
* pg_get_function_arguments
* Get a nicely-formatted list of arguments for a function.
* This is everything that would go between the parentheses in
* CREATE FUNCTION.
*/
/*
* pg_get_function_identity_arguments
* Get a formatted list of arguments for a function.
* This is everything that would go between the parentheses in
* ALTER FUNCTION, etc. In particular, don't print defaults.
*/
/*
* pg_get_function_result
* Get a nicely-formatted version of the result type of a function.
* This is what would appear after RETURNS in CREATE FUNCTION.
*/
/*
* Guts of pg_get_function_result: append the function's return type
* to the specified buffer.
*/
/*
* Common code for pg_get_function_arguments and pg_get_function_result:
* append the desired subset of arguments to buf. We print only TABLE
* arguments when print_table_args is true, and all the others when it's false.
* We print argument defaults only if print_defaults is true.
* Function return value is the number of arguments printed.
*/
/*
* Append used transformed types to specified buffer
*/
/*
* Get textual representation of a function argument's default value. The
* second argument of this function is the argument number among all arguments
* (i.e. proallargtypes, *not* proargtypes), starting with 1, because that's
* how information_schema.sql uses it.
*/
/*
* deparse_expression - General utility for deparsing expressions
*
* calls deparse_expression_pretty with all prettyPrinting disabled
*/
/* ----------
* deparse_expression_pretty - General utility for deparsing expressions
*
* expr is the node tree to be deparsed. It must be a transformed expression
* tree (ie, not the raw output of gram.y).
*
* dpcontext is a list of deparse_namespace nodes representing the context
* for interpreting Vars in the node tree. It can be NIL if no Vars are
* expected.
*
* forceprefix is true to force all Vars to be prefixed with their table names.
*
* showimplicit is true to force all implicit casts to be shown explicitly.
*
* Tries to pretty up the output according to prettyFlags and startIndent.
*
* The result is a palloc'd string.
* ----------
*/
/* ----------
* deparse_context_for - Build deparse context for a single relation
*
* Given the reference name (alias) and OID of a relation, build deparsing
* context for an expression referencing only that relation (as varno 1,
* varlevelsup 0). This is sufficient for many uses of deparse_expression.
* ----------
*/
/*
* deparse_context_for_plan_tree - Build deparse context for a Plan tree
*
* When deparsing an expression in a Plan tree, we use the plan's rangetable
* to resolve names of simple Vars. The initialization of column names for
* this is rather expensive if the rangetable is large, and it'll be the same
* for every expression in the Plan tree; so we do it just once and re-use
* the result of this function for each expression. (Note that the result
* is not usable until set_deparse_context_plan() is applied to it.)
*
* In addition to the PlannedStmt, pass the per-RTE alias names
* assigned by a previous call to select_rtable_names_for_explain.
*/
/*
* set_deparse_context_plan - Specify Plan node containing expression
*
* When deparsing an expression in a Plan tree, we might have to resolve
* OUTER_VAR, INNER_VAR, or INDEX_VAR references. To do this, the caller must
* provide the parent Plan node. Then OUTER_VAR and INNER_VAR references
* can be resolved by drilling down into the left and right child plans.
* Similarly, INDEX_VAR references can be resolved by reference to the
* indextlist given in a parent IndexOnlyScan node, or to the scan tlist in
* ForeignScan and CustomScan nodes. (Note that we don't currently support
* deparsing of indexquals in regular IndexScan or BitmapIndexScan nodes;
* for those, we can only deparse the indexqualorig fields, which won't
* contain INDEX_VAR Vars.)
*
* The ancestors list is a list of the Plan's parent Plan and SubPlan nodes,
* the most-closely-nested first. This is needed to resolve PARAM_EXEC
* Params. Note we assume that all the Plan nodes share the same rtable.
*
* Once this function has been called, deparse_expression() can be called on
* subsidiary expression(s) of the specified Plan node. To deparse
* expressions of a different Plan node in the same Plan tree, re-call this
* function to identify the new parent Plan node.
*
* The result is the same List passed in; this is a notational convenience.
*/
/*
* select_rtable_names_for_explain - Select RTE aliases for EXPLAIN
*
* Determine the relation aliases we'll use during an EXPLAIN operation.
* This is just a frontend to set_rtable_names. We have to expose the aliases
* to EXPLAIN because EXPLAIN needs to know the right alias names to print.
*/
/*
* set_rtable_names: select RTE aliases to be used in printing a query
*
* We fill in dpns->rtable_names with a list of names that is one-for-one with
* the already-filled dpns->rtable list. Each RTE name is unique among those
* in the new namespace plus any ancestor namespaces listed in
* parent_namespaces.
*
* If rels_used isn't NULL, only RTE indexes listed in it are given aliases.
*
* Note that this function is only concerned with relation names, not column
* names.
*/
/*
* set_deparse_for_query: set up deparse_namespace for deparsing a Query tree
*
* For convenience, this is defined to initialize the deparse_namespace struct
* from scratch.
*/
/*
* set_simple_column_names: fill in column aliases for non-query situations
*
* This handles EXPLAIN and cases where we only have relation RTEs. Without
* a join tree, we can't do anything smart about join RTEs, but we don't
* need to (note that EXPLAIN should never see join alias Vars anyway).
* If we do hit a join RTE we'll just process it like a non-table base RTE.
*/
/*
* has_dangerous_join_using: search jointree for unnamed JOIN USING
*
* Merged columns of a JOIN USING may act differently from either of the input
* columns, either because they are merged with COALESCE (in a FULL JOIN) or
* because an implicit coercion of the underlying input column is required.
* In such a case the column must be referenced as a column of the JOIN not as
* a column of either input. And this is problematic if the join is unnamed
* (alias-less): we cannot qualify the column's name with an RTE name, since
* there is none. (Forcibly assigning an alias to the join is not a solution,
* since that will prevent legal references to tables below the join.)
* To ensure that every column in the query is unambiguously referenceable,
* we must assign such merged columns names that are globally unique across
* the whole query, aliasing other columns out of the way as necessary.
*
* Because the ensuing re-aliasing is fairly damaging to the readability of
* the query, we don't do this unless we have to. So, we must pre-scan
* the join tree to see if we have to, before starting set_using_names().
*/
/*
* set_using_names: select column aliases to be used for merged USING columns
*
* We do this during a recursive descent of the query jointree.
* dpns->unique_using must already be set to determine the global strategy.
*
* Column alias info is saved in the dpns->rtable_columns list, which is
* assumed to be filled with pre-zeroed deparse_columns structs.
*
* parentUsing is a list of all USING aliases assigned in parent joins of
* the current jointree node. (The passed-in list must not be modified.)
*/
/*
* set_relation_column_names: select column aliases for a non-join RTE
*
* Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
* If any colnames entries are already filled in, those override local
* choices.
*/
/*
* set_join_column_names: select column aliases for a join RTE
*
* Column alias info is saved in *colinfo, which is assumed to be pre-zeroed.
* If any colnames entries are already filled in, those override local
* choices. Also, names for USING columns were already chosen by
* set_using_names(). We further expect that column alias selection has been
* completed for both input RTEs.
*/
#ifdef USE_ASSERT_CHECKING
#endif
/*
* colname_is_unique: is colname distinct from already-chosen column names?
*
* dpns is query-wide info, colinfo is for the column's RTE
*/
/*
* make_colname_unique: modify colname if necessary to make it unique
*
* dpns is query-wide info, colinfo is for the column's RTE
*/
/*
* expand_colnames_array_to: make colinfo->colnames at least n items long
*
* Any added array entries are initialized to zero.
*/
/*
* identify_join_columns: figure out where columns of a join come from
*
* Fills the join-specific fields of the colinfo struct, except for
* usingNames which is filled later.
*/
/*
* get_rtable_name: convenience function to get a previously assigned RTE alias
*
* The RTE must belong to the topmost namespace level in "context".
*/
/*
* set_deparse_plan: set up deparse_namespace to parse subexpressions
* of a given Plan node
*
* This sets the plan, outer_plan, inner_plan, outer_tlist, inner_tlist,
* and index_tlist fields. Caller is responsible for adjusting the ancestors
* list if necessary. Note that the rtable, subplans, and ctes fields do
* not need to change when shifting attention to different plan nodes in a
* single plan tree.
*/
/*
* push_child_plan: temporarily transfer deparsing attention to a child plan
*
* When expanding an OUTER_VAR or INNER_VAR reference, we must adjust the
* deparse context in case the referenced expression itself uses
* OUTER_VAR/INNER_VAR. We modify the top stack entry in-place to avoid
* affecting levelsup issues (although in a Plan tree there really shouldn't
* be any).
*
* Caller must provide a local deparse_namespace variable to save the
* previous state for pop_child_plan.
*/
/*
* pop_child_plan: undo the effects of push_child_plan
*/
/*
* push_ancestor_plan: temporarily transfer deparsing attention to an
* ancestor plan
*
* When expanding a Param reference, we must adjust the deparse context
* to match the plan node that contains the expression being printed;
* otherwise we'd fail if that expression itself contains a Param or
* OUTER_VAR/INNER_VAR/INDEX_VAR variable.
*
* The target ancestor is conveniently identified by the ListCell holding it
* in dpns->ancestors.
*
* Caller must provide a local deparse_namespace variable to save the
* previous state for pop_ancestor_plan.
*/
/*
* pop_ancestor_plan: undo the effects of push_ancestor_plan
*/
/* ----------
* make_ruledef - reconstruct the CREATE RULE command
* for a given pg_rewrite tuple
* ----------
*/
/* ----------
* make_viewdef - reconstruct the SELECT part of a
* view rewrite rule
* ----------
*/
/* ----------
* get_query_def - Parse back one query parsetree
*
* If resultDesc is not NULL, then it is the output tuple descriptor for
* the view represented by a SELECT query.
* ----------
*/
/* ----------
* get_values_def - Parse back a VALUES list
* ----------
*/
/* ----------
* get_with_clause - Parse back a WITH clause
* ----------
*/
/* ----------
* get_select_query_def - Parse back a SELECT parsetree
* ----------
*/
/*
* Detect whether query looks like SELECT ... FROM VALUES(),
* with no need to rename the output columns of the VALUES RTE.
* If so, return the VALUES RTE. Otherwise return NULL.
*/
/* ----------
* get_target_list - Parse back a SELECT target list
*
* This is also used for RETURNING lists in INSERT/UPDATE/DELETE.
* ----------
*/
/*
* Display a sort/group clause.
*
* Also returns the expression tree, so caller need not find it again.
*/
/*
* Display a GroupingSet
*/
/*
* Display an ORDER BY list.
*/
/*
* Display a WINDOW clause.
*
* Note that the windowClause list might contain only anonymous window
* specifications, in which case we should print nothing here.
*/
/*
* Display a window definition
*/
/* ----------
* get_insert_query_def - Parse back an INSERT parsetree
* ----------
*/
/* ----------
* get_update_query_def - Parse back an UPDATE parsetree
* ----------
*/
/* ----------
* get_update_query_targetlist_def - Parse back an UPDATE targetlist
* ----------
*/
/* ----------
* get_delete_query_def - Parse back a DELETE parsetree
* ----------
*/
/* ----------
* get_utility_query_def - Parse back a UTILITY parsetree
* ----------
*/
/*
* Display a Var appropriately.
*
* In some cases (currently only when recursing into an unnamed join)
* the Var's varlevelsup has to be interpreted with respect to a context
* above the current one; levelsup indicates the offset.
*
* If istoplevel is true, the Var is at the top level of a SELECT's
* targetlist, which means we need special treatment of whole-row Vars.
* Instead of the normal "tab.*", we'll print "tab.*::typename", which is a
* dirty hack to prevent "tab.*" from being expanded into multiple columns.
* (The parser will strip the useless coercion, so no inefficiency is added in
* dump and reload.) We used to print just "tab" in such cases, but that is
* ambiguous and will yield the wrong result if "tab" is also a plain column
* name in the query.
*
* Returns the attname of the Var, or NULL if the Var has no attname (because
* it is a whole-row Var or a subplan output reference).
*/
/*
* Deparse a Var which references OUTER_VAR, INNER_VAR, or INDEX_VAR. This
* routine is actually a callback for resolve_special_varno, which handles
* finding the correct TargetEntry. We get the expression contained in that
* TargetEntry and just need to deparse it, a job we can throw back on
* get_rule_expr.
*/
/*
* Chase through plan references to special varnos (OUTER_VAR, INNER_VAR,
* INDEX_VAR) until we find a real Var or some kind of non-Var node; then,
* invoke the callback provided.
*/
/*
* Get the name of a field of an expression of composite type. The
* expression is usually a Var, but we handle other cases too.
*
* levelsup is an extra offset to interpret the Var's varlevelsup correctly.
*
* This is fairly straightforward when the expression has a named composite
* type; we need only look up the type in the catalogs. However, the type
* could also be RECORD. Since no actual table or view column is allowed to
* have type RECORD, a Var of type RECORD must refer to a JOIN or FUNCTION RTE
* or to a subquery output. We drill down to find the ultimate defining
* expression and attempt to infer the field name from it. We ereport if we
* can't determine the name.
*
* Similarly, a PARAM of type RECORD has to refer to some expression of
* a determinable composite type.
*/
/*
* Try to find the referenced expression for a PARAM_EXEC Param that might
* reference a parameter supplied by an upper NestLoop or SubPlan plan node.
*
* If successful, return the expression and set *dpns_p and *ancestor_cell_p
* appropriately for calling push_ancestor_plan(). If no referent can be
* found, return NULL.
*/
/*
* Display a Param appropriately.
*/
/*
* get_simple_binary_op_name
*
* helper function for isSimpleNode
* will return single char binary operator name, or NULL if it's not
*/
/*
* isSimpleNode - check if given node is simple (doesn't need parenthesizing)
*
* true : simple in the context of parent node's type
* false : not simple
*/
/*
* appendContextKeyword - append a keyword to buffer
*
* If prettyPrint is enabled, perform a line break, and adjust indentation.
* Otherwise, just append the keyword.
*/
/*
* removeStringInfoSpaces - delete trailing spaces from a buffer.
*
* Possibly this should move to stringinfo.c at some point.
*/
/*
* get_rule_expr_paren - deparse expr using get_rule_expr,
* embracing the string with parentheses if necessary for prettyPrint.
*
* Never embrace if prettyFlags=0, because it's done in the calling node.
*
* Any node that does *not* embrace its argument node by sql syntax (with
* parentheses, non-operator keywords like CASE/WHEN/ON, or comma etc) should
* use get_rule_expr_paren instead of get_rule_expr so parentheses can be
* added.
*/
/* ----------
* get_rule_expr - Parse back an expression
*
* Note: showimplicit determines whether we display any implicit cast that
* is present at the top of the expression tree. It is a passed argument,
* not a field of the context struct, because we change the value as we
* recurse down into the expression. In general we suppress implicit casts
* when the result type is known with certainty (eg, the arguments of an
* OR must be boolean). We display implicit casts for arguments of functions
* and operators, since this is needed to be certain that the same function
* or operator will be chosen when the expression is re-parsed.
* ----------
*/
/*
* get_rule_expr_toplevel - Parse back a toplevel expression
*
* Same as get_rule_expr(), except that if the expr is just a Var, we pass
* istoplevel = true not false to get_variable(). This causes whole-row Vars
* to get printed with decoration that will prevent expansion of "*".
* We need to use this in contexts such as ROW() and VALUES(), where the
* parser would expand "foo.*" appearing at top level. (In principle we'd
* use this in get_target_list() too, but that has additional worries about
* whether to print AS, so it needs to invoke get_variable() directly anyway.)
*/
/*
* get_rule_expr_funccall - Parse back a function-call expression
*
* Same as get_rule_expr(), except that we guarantee that the output will
* look like a function call, or like one of the things the grammar treats as
* equivalent to a function call (see the func_expr_windowless production).
* This is needed in places where the grammar uses func_expr_windowless and
* you can't substitute a parenthesized a_expr. If what we have isn't going
* to look like a function call, wrap it in a dummy CAST() expression, which
* will satisfy the grammar --- and, indeed, is likely what the user wrote to
* produce such a thing.
*/
/*
* Helper function to identify node types that satisfy func_expr_windowless.
* If in doubt, "false" is always a safe answer.
*/
/*
* get_oper_expr - Parse back an OpExpr node
*/
/*
* get_func_expr - Parse back a FuncExpr node
*/
/*
* get_agg_expr - Parse back an Aggref node
*/
/*
* This is a helper function for get_agg_expr(). It's used when we deparse
* a combining Aggref; resolve_special_varno locates the corresponding partial
* Aggref and then calls this.
*/
/*
* get_windowfunc_expr - Parse back a WindowFunc node
*/
/* ----------
* get_coercion_expr
*
* Make a string representation of a value coerced to a specific type
* ----------
*/
/* ----------
* get_const_expr
*
* Make a string representation of a Const
*
* showtype can be -1 to never show "::typename" decoration, or +1 to always
* show it, or 0 to show it only if the constant wouldn't be assumed to be
* the right type by default.
*
* If the Const's collation isn't default for its type, show that too.
* We mustn't do this when showtype is -1 (since that means the caller will
* print "::typename", and we can't put a COLLATE clause in between). It's
* caller's responsibility that collation isn't missed in such cases.
* ----------
*/
/*
* helper for get_const_expr: append COLLATE if needed
*/
/*
* simple_quote_literal - Format a string as a SQL literal, append to buf
*/
/* ----------
* get_sublink_expr - Parse back a sublink
* ----------
*/
/* ----------
* get_tablefunc - Parse back a table function
* ----------
*/
/* ----------
* get_from_clause - Parse back a FROM clause
*
* "prefix" is the keyword that denotes the start of the list of FROM
* elements. It is FROM when used to parse back SELECT and UPDATE, but
* is USING when parsing back DELETE.
* ----------
*/
/*
* get_column_alias_list - print column alias list for an RTE
*
* Caller must already have printed the relation's alias name.
*/
/*
* get_from_clause_coldeflist - reproduce FROM clause coldeflist
*
* When printing a top-level coldeflist (which is syntactically also the
* relation's column alias list), use column names from colinfo. But when
* printing a coldeflist embedded inside ROWS FROM(), we prefer to use the
* original coldeflist's names, which are available in rtfunc->funccolnames.
* Pass NULL for colinfo to select the latter behavior.
*
* The coldeflist is appended immediately (no space) to buf. Caller is
* responsible for ensuring that an alias or AS is present before it.
*/
/*
* get_tablesample_def - print a TableSampleClause
*/
/*
* get_opclass_name - fetch name of an index operator class
*
* The opclass name is appended (after a space) to buf.
*
* Output is suppressed if the opclass is the default for the given
* actual_datatype. (If you don't want this behavior, just pass
* InvalidOid for actual_datatype.)
*/
/*
* generate_opclass_name
* Compute the name to display for a opclass specified by OID
*
* The result includes all necessary quoting and schema-prefixing.
*/
/*
* processIndirection - take care of array and subfield assignment
*
* We strip any top-level FieldStore or assignment SubscriptingRef nodes that
* appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). We might also need to
* strip CoerceToDomain nodes, but only ones that appear above assignment
* nodes.
*
* Returns the subexpression that's to be assigned.
*/
/*
* quote_identifier - Quote an identifier only if needed
*
* When quotes are needed, we palloc the required space; slightly
* space-wasteful but well worth it for notational simplicity.
*/
const char *
quote_identifier(const char *ident)
{
/*
* Can avoid quoting if ident starts with a lowercase letter or underscore
* and contains only lowercase letters, digits, and underscores, *and* is
* not any SQL keyword. Otherwise, supply quotes.
*/
int nquotes = 0;
bool safe;
const char *ptr;
char *result;
char *optr;
/*
* would like to use <ctype.h> macros here, but they might yield unwanted
* locale-specific results...
*/
safe = ((ident[0] >= 'a' && ident[0] <= 'z') || ident[0] == '_');
for (ptr = ident; *ptr; ptr++)
{
char ch = *ptr;
if ((ch >= 'a' && ch <= 'z') ||
(ch >= '0' && ch <= '9') ||
(ch == '_'))
{
/* okay */
}
else
{
safe = false;
if (ch == '"')
nquotes++;
}
}
if (quote_all_identifiers)
safe = false;
if (safe)
{
/*
* Check for keyword. We quote keywords except for unreserved ones.
* (In some cases we could avoid quoting a col_name or type_func_name
* keyword, but it seems much harder than it's worth to tell that.)
*
* Note: ScanKeywordLookup() does case-insensitive comparison, but
* that's fine, since we already know we have all-lower-case.
*/
int kwnum = ScanKeywordLookup(ident, &ScanKeywords);
if (kwnum >= 0 && ScanKeywordCategories[kwnum] != UNRESERVED_KEYWORD)
safe = false;
}
if (safe)
return ident; /* no change needed */
result = (char *) palloc(strlen(ident) + nquotes + 2 + 1);
optr = result;
*optr++ = '"';
for (ptr = ident; *ptr; ptr++)
{
char ch = *ptr;
if (ch == '"')
*optr++ = '"';
*optr++ = ch;
}
*optr++ = '"';
*optr = '\0';
return result;
}