1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-30 21:42:05 +03:00

Print the correct aliases for DML target tables in ruleutils.

ruleutils.c blindly printed the user-given alias (or nothing if there
hadn't been one) for the target table of INSERT/UPDATE/DELETE queries.
That works a large percentage of the time, but not always: for queries
appearing in WITH, it's possible that we chose a different alias to
avoid conflict with outer-scope names.  Since the chosen alias would
be used in any Var references to the target table, this'd lead to an
inconsistent printout with consequences such as dump/restore failures.

The correct logic for printing (or not) a relation alias was embedded
in get_from_clause_item.  Factor it out to a separate function so that
we don't need a jointree node to use it.  (Only a limited part of that
function can be reached from these new call sites, but this seems like
the cleanest non-duplicative factorization.)

In passing, I got rid of a redundant "\d+ rules_src" step in rules.sql.

Initial report from Jonathan Katz; thanks to Vignesh C for analysis.
This has been broken for a long time, so back-patch to all supported
branches.

Discussion: https://postgr.es/m/e947fa21-24b2-f922-375a-d4f763ef3e4b@postgresql.org
Discussion: https://postgr.es/m/CALDaNm1MMntjmT_NJGp-Z=xbF02qHGAyuSHfYHias3TqQbPF2w@mail.gmail.com
This commit is contained in:
Tom Lane
2023-02-17 16:40:34 -05:00
parent 5d8ec1b9f6
commit c8a5f1685f
3 changed files with 126 additions and 80 deletions

View File

@ -475,6 +475,8 @@ 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_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
deparse_context *context);
static void get_column_alias_list(deparse_columns *colinfo,
deparse_context *context);
static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
@ -6658,12 +6660,14 @@ get_insert_query_def(Query *query, deparse_context *context,
context->indentLevel += PRETTYINDENT_STD;
appendStringInfoChar(buf, ' ');
}
appendStringInfo(buf, "INSERT INTO %s ",
appendStringInfo(buf, "INSERT INTO %s",
generate_relation_name(rte->relid, NIL));
/* INSERT requires AS keyword for target alias */
if (rte->alias != NULL)
appendStringInfo(buf, "AS %s ",
quote_identifier(rte->alias->aliasname));
/* Print the relation alias, if needed; INSERT requires explicit AS */
get_rte_alias(rte, query->resultRelation, true, context);
/* always want a space here */
appendStringInfoChar(buf, ' ');
/*
* Add the insert-column-names list. Any indirection decoration needed on
@ -6845,9 +6849,10 @@ get_update_query_def(Query *query, deparse_context *context,
appendStringInfo(buf, "UPDATE %s%s",
only_marker(rte),
generate_relation_name(rte->relid, NIL));
if (rte->alias != NULL)
appendStringInfo(buf, " %s",
quote_identifier(rte->alias->aliasname));
/* Print the relation alias, if needed */
get_rte_alias(rte, query->resultRelation, false, context);
appendStringInfoString(buf, " SET ");
/* Deparse targetlist */
@ -7053,9 +7058,9 @@ get_delete_query_def(Query *query, deparse_context *context,
appendStringInfo(buf, "DELETE FROM %s%s",
only_marker(rte),
generate_relation_name(rte->relid, NIL));
if (rte->alias != NULL)
appendStringInfo(buf, " %s",
quote_identifier(rte->alias->aliasname));
/* Print the relation alias, if needed */
get_rte_alias(rte, query->resultRelation, false, context);
/* Add the USING clause if given */
get_from_clause(query, " USING ", context);
@ -10916,10 +10921,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
{
int varno = ((RangeTblRef *) jtnode)->rtindex;
RangeTblEntry *rte = rt_fetch(varno, query->rtable);
char *refname = get_rtable_name(varno, context);
deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
RangeTblFunction *rtfunc1 = NULL;
bool printalias;
if (rte->lateral)
appendStringInfoString(buf, "LATERAL ");
@ -11056,55 +11059,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
}
/* Print the relation alias, if needed */
printalias = false;
if (rte->alias != NULL)
{
/* Always print alias if user provided one */
printalias = true;
}
else if (colinfo->printaliases)
{
/* Always print alias if we need to print column aliases */
printalias = true;
}
else if (rte->rtekind == RTE_RELATION)
{
/*
* No need to print alias if it's same as relation name (this
* would normally be the case, but not if set_rtable_names had to
* resolve a conflict).
*/
if (strcmp(refname, get_relation_name(rte->relid)) != 0)
printalias = true;
}
else if (rte->rtekind == RTE_FUNCTION)
{
/*
* For a function RTE, always print alias. This covers possible
* renaming of the function and/or instability of the
* FigureColname rules for things that aren't simple functions.
* Note we'd need to force it anyway for the columndef list case.
*/
printalias = true;
}
else if (rte->rtekind == RTE_SUBQUERY ||
rte->rtekind == RTE_VALUES)
{
/* Alias is syntactically required for SUBQUERY and VALUES */
printalias = true;
}
else if (rte->rtekind == RTE_CTE)
{
/*
* No need to print alias if it's same as CTE name (this would
* normally be the case, but not if set_rtable_names had to
* resolve a conflict).
*/
if (strcmp(refname, rte->ctename) != 0)
printalias = true;
}
if (printalias)
appendStringInfo(buf, " %s", quote_identifier(refname));
get_rte_alias(rte, varno, false, context);
/* Print the column definitions or aliases, if needed */
if (rtfunc1 && rtfunc1->funccolnames != NIL)
@ -11242,6 +11197,73 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
(int) nodeTag(jtnode));
}
/*
* get_rte_alias - print the relation's alias, if needed
*
* If printed, the alias is preceded by a space, or by " AS " if use_as is true.
*/
static void
get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
deparse_context *context)
{
deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
char *refname = get_rtable_name(varno, context);
deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
bool printalias = false;
if (rte->alias != NULL)
{
/* Always print alias if user provided one */
printalias = true;
}
else if (colinfo->printaliases)
{
/* Always print alias if we need to print column aliases */
printalias = true;
}
else if (rte->rtekind == RTE_RELATION)
{
/*
* No need to print alias if it's same as relation name (this would
* normally be the case, but not if set_rtable_names had to resolve a
* conflict).
*/
if (strcmp(refname, get_relation_name(rte->relid)) != 0)
printalias = true;
}
else if (rte->rtekind == RTE_FUNCTION)
{
/*
* For a function RTE, always print alias. This covers possible
* renaming of the function and/or instability of the FigureColname
* rules for things that aren't simple functions. Note we'd need to
* force it anyway for the columndef list case.
*/
printalias = true;
}
else if (rte->rtekind == RTE_SUBQUERY ||
rte->rtekind == RTE_VALUES)
{
/* Alias is syntactically required for SUBQUERY and VALUES */
printalias = true;
}
else if (rte->rtekind == RTE_CTE)
{
/*
* No need to print alias if it's same as CTE name (this would
* normally be the case, but not if set_rtable_names had to resolve a
* conflict).
*/
if (strcmp(refname, rte->ctename) != 0)
printalias = true;
}
if (printalias)
appendStringInfo(context->buf, "%s%s",
use_as ? " AS " : " ",
quote_identifier(refname));
}
/*
* get_column_alias_list - print column alias list for an RTE
*