aboutsummaryrefslogtreecommitdiff
path: root/src/backend/utils/adt/ruleutils.c
diff options
context:
space:
mode:
authorTom Lane <tgl@sss.pgh.pa.us>2023-02-17 16:40:34 -0500
committerTom Lane <tgl@sss.pgh.pa.us>2023-02-17 16:40:34 -0500
commit393430f57544dfd550135e0191cc91139926b682 (patch)
tree7ab03d4fa7ee463c00003501f5b6558b789b214f /src/backend/utils/adt/ruleutils.c
parent881a91781f49b6116ee6ce49b5bcc03da7200423 (diff)
downloadpostgresql-393430f57544dfd550135e0191cc91139926b682.tar.gz
postgresql-393430f57544dfd550135e0191cc91139926b682.zip
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
Diffstat (limited to 'src/backend/utils/adt/ruleutils.c')
-rw-r--r--src/backend/utils/adt/ruleutils.c154
1 files changed, 88 insertions, 66 deletions
diff --git a/src/backend/utils/adt/ruleutils.c b/src/backend/utils/adt/ruleutils.c
index 9ac42efdbc3..6dc117dea8e 100644
--- a/src/backend/utils/adt/ruleutils.c
+++ b/src/backend/utils/adt/ruleutils.c
@@ -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,
@@ -6648,12 +6650,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
@@ -6835,9 +6839,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 */
@@ -7043,9 +7048,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);
@@ -10887,10 +10892,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 ");
@@ -11027,59 +11030,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)
- {
- /*
- * For a subquery, always print alias. This makes the output SQL
- * spec-compliant, even though we allow the alias to be omitted on
- * input.
- */
- 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)
@@ -11218,6 +11169,77 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
}
/*
+ * 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)
+ {
+ /*
+ * For a subquery, always print alias. This makes the output
+ * SQL-spec-compliant, even though we allow such aliases to be omitted
+ * on input.
+ */
+ 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
*
* Caller must already have printed the relation's alias name.