Skip to content

Commit 3dd287c

Browse files
committed
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
1 parent a40e7b7 commit 3dd287c

File tree

3 files changed

+126
-79
lines changed

3 files changed

+126
-79
lines changed

src/backend/utils/adt/ruleutils.c

Lines changed: 84 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,8 @@ static void get_from_clause(Query *query, const char *prefix,
452452
deparse_context *context);
453453
static void get_from_clause_item(Node *jtnode, Query *query,
454454
deparse_context *context);
455+
static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
456+
deparse_context *context);
455457
static void get_column_alias_list(deparse_columns *colinfo,
456458
deparse_context *context);
457459
static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
@@ -6255,12 +6257,14 @@ get_insert_query_def(Query *query, deparse_context *context,
62556257
context->indentLevel += PRETTYINDENT_STD;
62566258
appendStringInfoChar(buf, ' ');
62576259
}
6258-
appendStringInfo(buf, "INSERT INTO %s ",
6260+
appendStringInfo(buf, "INSERT INTO %s",
62596261
generate_relation_name(rte->relid, NIL));
6260-
/* INSERT requires AS keyword for target alias */
6261-
if (rte->alias != NULL)
6262-
appendStringInfo(buf, "AS %s ",
6263-
quote_identifier(rte->alias->aliasname));
6262+
6263+
/* Print the relation alias, if needed; INSERT requires explicit AS */
6264+
get_rte_alias(rte, query->resultRelation, true, context);
6265+
6266+
/* always want a space here */
6267+
appendStringInfoChar(buf, ' ');
62646268

62656269
/*
62666270
* Add the insert-column-names list. Any indirection decoration needed on
@@ -6442,9 +6446,10 @@ get_update_query_def(Query *query, deparse_context *context,
64426446
appendStringInfo(buf, "UPDATE %s%s",
64436447
only_marker(rte),
64446448
generate_relation_name(rte->relid, NIL));
6445-
if (rte->alias != NULL)
6446-
appendStringInfo(buf, " %s",
6447-
quote_identifier(rte->alias->aliasname));
6449+
6450+
/* Print the relation alias, if needed */
6451+
get_rte_alias(rte, query->resultRelation, false, context);
6452+
64486453
appendStringInfoString(buf, " SET ");
64496454

64506455
/* Deparse targetlist */
@@ -6651,9 +6656,9 @@ get_delete_query_def(Query *query, deparse_context *context,
66516656
appendStringInfo(buf, "DELETE FROM %s%s",
66526657
only_marker(rte),
66536658
generate_relation_name(rte->relid, NIL));
6654-
if (rte->alias != NULL)
6655-
appendStringInfo(buf, " %s",
6656-
quote_identifier(rte->alias->aliasname));
6659+
6660+
/* Print the relation alias, if needed */
6661+
get_rte_alias(rte, query->resultRelation, false, context);
66576662

66586663
/* Add the USING clause if given */
66596664
get_from_clause(query, " USING ", context);
@@ -10105,10 +10110,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
1010510110
{
1010610111
int varno = ((RangeTblRef *) jtnode)->rtindex;
1010710112
RangeTblEntry *rte = rt_fetch(varno, query->rtable);
10108-
char *refname = get_rtable_name(varno, context);
1010910113
deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
1011010114
RangeTblFunction *rtfunc1 = NULL;
10111-
bool printalias;
1011210115

1011310116
if (rte->lateral)
1011410117
appendStringInfoString(buf, "LATERAL ");
@@ -10245,54 +10248,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
1024510248
}
1024610249

1024710250
/* Print the relation alias, if needed */
10248-
printalias = false;
10249-
if (rte->alias != NULL)
10250-
{
10251-
/* Always print alias if user provided one */
10252-
printalias = true;
10253-
}
10254-
else if (colinfo->printaliases)
10255-
{
10256-
/* Always print alias if we need to print column aliases */
10257-
printalias = true;
10258-
}
10259-
else if (rte->rtekind == RTE_RELATION)
10260-
{
10261-
/*
10262-
* No need to print alias if it's same as relation name (this
10263-
* would normally be the case, but not if set_rtable_names had to
10264-
* resolve a conflict).
10265-
*/
10266-
if (strcmp(refname, get_relation_name(rte->relid)) != 0)
10267-
printalias = true;
10268-
}
10269-
else if (rte->rtekind == RTE_FUNCTION)
10270-
{
10271-
/*
10272-
* For a function RTE, always print alias. This covers possible
10273-
* renaming of the function and/or instability of the
10274-
* FigureColname rules for things that aren't simple functions.
10275-
* Note we'd need to force it anyway for the columndef list case.
10276-
*/
10277-
printalias = true;
10278-
}
10279-
else if (rte->rtekind == RTE_VALUES)
10280-
{
10281-
/* Alias is syntactically required for VALUES */
10282-
printalias = true;
10283-
}
10284-
else if (rte->rtekind == RTE_CTE)
10285-
{
10286-
/*
10287-
* No need to print alias if it's same as CTE name (this would
10288-
* normally be the case, but not if set_rtable_names had to
10289-
* resolve a conflict).
10290-
*/
10291-
if (strcmp(refname, rte->ctename) != 0)
10292-
printalias = true;
10293-
}
10294-
if (printalias)
10295-
appendStringInfo(buf, " %s", quote_identifier(refname));
10251+
get_rte_alias(rte, varno, false, context);
1029610252

1029710253
/* Print the column definitions or aliases, if needed */
1029810254
if (rtfunc1 && rtfunc1->funccolnames != NIL)
@@ -10426,6 +10382,73 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
1042610382
(int) nodeTag(jtnode));
1042710383
}
1042810384

10385+
/*
10386+
* get_rte_alias - print the relation's alias, if needed
10387+
*
10388+
* If printed, the alias is preceded by a space, or by " AS " if use_as is true.
10389+
*/
10390+
static void
10391+
get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
10392+
deparse_context *context)
10393+
{
10394+
deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
10395+
char *refname = get_rtable_name(varno, context);
10396+
deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
10397+
bool printalias = false;
10398+
10399+
if (rte->alias != NULL)
10400+
{
10401+
/* Always print alias if user provided one */
10402+
printalias = true;
10403+
}
10404+
else if (colinfo->printaliases)
10405+
{
10406+
/* Always print alias if we need to print column aliases */
10407+
printalias = true;
10408+
}
10409+
else if (rte->rtekind == RTE_RELATION)
10410+
{
10411+
/*
10412+
* No need to print alias if it's same as relation name (this would
10413+
* normally be the case, but not if set_rtable_names had to resolve a
10414+
* conflict).
10415+
*/
10416+
if (strcmp(refname, get_relation_name(rte->relid)) != 0)
10417+
printalias = true;
10418+
}
10419+
else if (rte->rtekind == RTE_FUNCTION)
10420+
{
10421+
/*
10422+
* For a function RTE, always print alias. This covers possible
10423+
* renaming of the function and/or instability of the FigureColname
10424+
* rules for things that aren't simple functions. Note we'd need to
10425+
* force it anyway for the columndef list case.
10426+
*/
10427+
printalias = true;
10428+
}
10429+
else if (rte->rtekind == RTE_SUBQUERY ||
10430+
rte->rtekind == RTE_VALUES)
10431+
{
10432+
/* Alias is syntactically required for SUBQUERY and VALUES */
10433+
printalias = true;
10434+
}
10435+
else if (rte->rtekind == RTE_CTE)
10436+
{
10437+
/*
10438+
* No need to print alias if it's same as CTE name (this would
10439+
* normally be the case, but not if set_rtable_names had to resolve a
10440+
* conflict).
10441+
*/
10442+
if (strcmp(refname, rte->ctename) != 0)
10443+
printalias = true;
10444+
}
10445+
10446+
if (printalias)
10447+
appendStringInfo(context->buf, "%s%s",
10448+
use_as ? " AS " : " ",
10449+
quote_identifier(refname));
10450+
}
10451+
1042910452
/*
1043010453
* get_column_alias_list - print column alias list for an RTE
1043110454
*

src/test/regress/expected/rules.out

Lines changed: 30 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -2971,28 +2971,21 @@ select * from rules_log;
29712971
(16 rows)
29722972

29732973
create rule r4 as on delete to rules_src do notify rules_src_deletion;
2974-
\d+ rules_src
2975-
Table "public.rules_src"
2976-
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
2977-
--------+---------+-----------+----------+---------+---------+--------------+-------------
2978-
f1 | integer | | | | plain | |
2979-
f2 | integer | | | 0 | plain | |
2980-
Rules:
2981-
r1 AS
2982-
ON UPDATE TO rules_src DO INSERT INTO rules_log (f1, f2, tag, id) VALUES (old.f1,old.f2,'old'::text,DEFAULT), (new.f1,new.f2,'new'::text,DEFAULT)
2983-
r2 AS
2984-
ON UPDATE TO rules_src DO VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
2985-
r3 AS
2986-
ON INSERT TO rules_src DO INSERT INTO rules_log (f1, f2, tag, id) VALUES (NULL::integer,NULL::integer,'-'::text,DEFAULT), (new.f1,new.f2,'new'::text,DEFAULT)
2987-
r4 AS
2988-
ON DELETE TO rules_src DO
2989-
NOTIFY rules_src_deletion
2990-
29912974
--
29922975
-- Ensure an aliased target relation for insert is correctly deparsed.
29932976
--
29942977
create rule r5 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
29952978
create rule r6 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
2979+
--
2980+
-- Check deparse disambiguation of INSERT/UPDATE/DELETE targets.
2981+
--
2982+
create rule r7 as on delete to rules_src do instead
2983+
with wins as (insert into int4_tbl as trgt values (0) returning *),
2984+
wupd as (update int4_tbl trgt set f1 = f1+1 returning *),
2985+
wdel as (delete from int4_tbl trgt where f1 = 0 returning *)
2986+
insert into rules_log AS trgt select old.* from wins, wupd, wdel
2987+
returning trgt.f1, trgt.f2;
2988+
-- check display of all rules added above
29962989
\d+ rules_src
29972990
Table "public.rules_src"
29982991
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
@@ -3017,6 +3010,26 @@ Rules:
30173010
r6 AS
30183011
ON UPDATE TO rules_src DO INSTEAD UPDATE rules_log trgt SET tag = 'updated'::text
30193012
WHERE trgt.f1 = new.f1
3013+
r7 AS
3014+
ON DELETE TO rules_src DO INSTEAD WITH wins AS (
3015+
INSERT INTO int4_tbl AS trgt_1 (f1)
3016+
VALUES (0)
3017+
RETURNING trgt_1.f1
3018+
), wupd AS (
3019+
UPDATE int4_tbl trgt_1 SET f1 = trgt_1.f1 + 1
3020+
RETURNING trgt_1.f1
3021+
), wdel AS (
3022+
DELETE FROM int4_tbl trgt_1
3023+
WHERE trgt_1.f1 = 0
3024+
RETURNING trgt_1.f1
3025+
)
3026+
INSERT INTO rules_log AS trgt (f1, f2) SELECT old.f1,
3027+
old.f2
3028+
FROM wins,
3029+
wupd,
3030+
wdel
3031+
RETURNING trgt.f1,
3032+
trgt.f2
30203033

30213034
--
30223035
-- check alter rename rule

src/test/regress/sql/rules.sql

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1034,13 +1034,24 @@ insert into rules_src values(22,23), (33,default);
10341034
select * from rules_src;
10351035
select * from rules_log;
10361036
create rule r4 as on delete to rules_src do notify rules_src_deletion;
1037-
\d+ rules_src
10381037

10391038
--
10401039
-- Ensure an aliased target relation for insert is correctly deparsed.
10411040
--
10421041
create rule r5 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
10431042
create rule r6 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
1043+
1044+
--
1045+
-- Check deparse disambiguation of INSERT/UPDATE/DELETE targets.
1046+
--
1047+
create rule r7 as on delete to rules_src do instead
1048+
with wins as (insert into int4_tbl as trgt values (0) returning *),
1049+
wupd as (update int4_tbl trgt set f1 = f1+1 returning *),
1050+
wdel as (delete from int4_tbl trgt where f1 = 0 returning *)
1051+
insert into rules_log AS trgt select old.* from wins, wupd, wdel
1052+
returning trgt.f1, trgt.f2;
1053+
1054+
-- check display of all rules added above
10441055
\d+ rules_src
10451056

10461057
--

0 commit comments

Comments
 (0)