Skip to content

Commit 14345f3

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 864f80f commit 14345f3

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
@@ -470,6 +470,8 @@ static void get_from_clause(Query *query, const char *prefix,
470470
deparse_context *context);
471471
static void get_from_clause_item(Node *jtnode, Query *query,
472472
deparse_context *context);
473+
static void get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
474+
deparse_context *context);
473475
static void get_column_alias_list(deparse_columns *colinfo,
474476
deparse_context *context);
475477
static void get_from_clause_coldeflist(RangeTblFunction *rtfunc,
@@ -6557,12 +6559,14 @@ get_insert_query_def(Query *query, deparse_context *context,
65576559
context->indentLevel += PRETTYINDENT_STD;
65586560
appendStringInfoChar(buf, ' ');
65596561
}
6560-
appendStringInfo(buf, "INSERT INTO %s ",
6562+
appendStringInfo(buf, "INSERT INTO %s",
65616563
generate_relation_name(rte->relid, NIL));
6562-
/* INSERT requires AS keyword for target alias */
6563-
if (rte->alias != NULL)
6564-
appendStringInfo(buf, "AS %s ",
6565-
quote_identifier(rte->alias->aliasname));
6564+
6565+
/* Print the relation alias, if needed; INSERT requires explicit AS */
6566+
get_rte_alias(rte, query->resultRelation, true, context);
6567+
6568+
/* always want a space here */
6569+
appendStringInfoChar(buf, ' ');
65666570

65676571
/*
65686572
* Add the insert-column-names list. Any indirection decoration needed on
@@ -6744,9 +6748,10 @@ get_update_query_def(Query *query, deparse_context *context,
67446748
appendStringInfo(buf, "UPDATE %s%s",
67456749
only_marker(rte),
67466750
generate_relation_name(rte->relid, NIL));
6747-
if (rte->alias != NULL)
6748-
appendStringInfo(buf, " %s",
6749-
quote_identifier(rte->alias->aliasname));
6751+
6752+
/* Print the relation alias, if needed */
6753+
get_rte_alias(rte, query->resultRelation, false, context);
6754+
67506755
appendStringInfoString(buf, " SET ");
67516756

67526757
/* Deparse targetlist */
@@ -6952,9 +6957,9 @@ get_delete_query_def(Query *query, deparse_context *context,
69526957
appendStringInfo(buf, "DELETE FROM %s%s",
69536958
only_marker(rte),
69546959
generate_relation_name(rte->relid, NIL));
6955-
if (rte->alias != NULL)
6956-
appendStringInfo(buf, " %s",
6957-
quote_identifier(rte->alias->aliasname));
6960+
6961+
/* Print the relation alias, if needed */
6962+
get_rte_alias(rte, query->resultRelation, false, context);
69586963

69596964
/* Add the USING clause if given */
69606965
get_from_clause(query, " USING ", context);
@@ -10816,10 +10821,8 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
1081610821
{
1081710822
int varno = ((RangeTblRef *) jtnode)->rtindex;
1081810823
RangeTblEntry *rte = rt_fetch(varno, query->rtable);
10819-
char *refname = get_rtable_name(varno, context);
1082010824
deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
1082110825
RangeTblFunction *rtfunc1 = NULL;
10822-
bool printalias;
1082310826

1082410827
if (rte->lateral)
1082510828
appendStringInfoString(buf, "LATERAL ");
@@ -10956,54 +10959,7 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
1095610959
}
1095710960

1095810961
/* Print the relation alias, if needed */
10959-
printalias = false;
10960-
if (rte->alias != NULL)
10961-
{
10962-
/* Always print alias if user provided one */
10963-
printalias = true;
10964-
}
10965-
else if (colinfo->printaliases)
10966-
{
10967-
/* Always print alias if we need to print column aliases */
10968-
printalias = true;
10969-
}
10970-
else if (rte->rtekind == RTE_RELATION)
10971-
{
10972-
/*
10973-
* No need to print alias if it's same as relation name (this
10974-
* would normally be the case, but not if set_rtable_names had to
10975-
* resolve a conflict).
10976-
*/
10977-
if (strcmp(refname, get_relation_name(rte->relid)) != 0)
10978-
printalias = true;
10979-
}
10980-
else if (rte->rtekind == RTE_FUNCTION)
10981-
{
10982-
/*
10983-
* For a function RTE, always print alias. This covers possible
10984-
* renaming of the function and/or instability of the
10985-
* FigureColname rules for things that aren't simple functions.
10986-
* Note we'd need to force it anyway for the columndef list case.
10987-
*/
10988-
printalias = true;
10989-
}
10990-
else if (rte->rtekind == RTE_VALUES)
10991-
{
10992-
/* Alias is syntactically required for VALUES */
10993-
printalias = true;
10994-
}
10995-
else if (rte->rtekind == RTE_CTE)
10996-
{
10997-
/*
10998-
* No need to print alias if it's same as CTE name (this would
10999-
* normally be the case, but not if set_rtable_names had to
11000-
* resolve a conflict).
11001-
*/
11002-
if (strcmp(refname, rte->ctename) != 0)
11003-
printalias = true;
11004-
}
11005-
if (printalias)
11006-
appendStringInfo(buf, " %s", quote_identifier(refname));
10962+
get_rte_alias(rte, varno, false, context);
1100710963

1100810964
/* Print the column definitions or aliases, if needed */
1100910965
if (rtfunc1 && rtfunc1->funccolnames != NIL)
@@ -11141,6 +11097,73 @@ get_from_clause_item(Node *jtnode, Query *query, deparse_context *context)
1114111097
(int) nodeTag(jtnode));
1114211098
}
1114311099

11100+
/*
11101+
* get_rte_alias - print the relation's alias, if needed
11102+
*
11103+
* If printed, the alias is preceded by a space, or by " AS " if use_as is true.
11104+
*/
11105+
static void
11106+
get_rte_alias(RangeTblEntry *rte, int varno, bool use_as,
11107+
deparse_context *context)
11108+
{
11109+
deparse_namespace *dpns = (deparse_namespace *) linitial(context->namespaces);
11110+
char *refname = get_rtable_name(varno, context);
11111+
deparse_columns *colinfo = deparse_columns_fetch(varno, dpns);
11112+
bool printalias = false;
11113+
11114+
if (rte->alias != NULL)
11115+
{
11116+
/* Always print alias if user provided one */
11117+
printalias = true;
11118+
}
11119+
else if (colinfo->printaliases)
11120+
{
11121+
/* Always print alias if we need to print column aliases */
11122+
printalias = true;
11123+
}
11124+
else if (rte->rtekind == RTE_RELATION)
11125+
{
11126+
/*
11127+
* No need to print alias if it's same as relation name (this would
11128+
* normally be the case, but not if set_rtable_names had to resolve a
11129+
* conflict).
11130+
*/
11131+
if (strcmp(refname, get_relation_name(rte->relid)) != 0)
11132+
printalias = true;
11133+
}
11134+
else if (rte->rtekind == RTE_FUNCTION)
11135+
{
11136+
/*
11137+
* For a function RTE, always print alias. This covers possible
11138+
* renaming of the function and/or instability of the FigureColname
11139+
* rules for things that aren't simple functions. Note we'd need to
11140+
* force it anyway for the columndef list case.
11141+
*/
11142+
printalias = true;
11143+
}
11144+
else if (rte->rtekind == RTE_SUBQUERY ||
11145+
rte->rtekind == RTE_VALUES)
11146+
{
11147+
/* Alias is syntactically required for SUBQUERY and VALUES */
11148+
printalias = true;
11149+
}
11150+
else if (rte->rtekind == RTE_CTE)
11151+
{
11152+
/*
11153+
* No need to print alias if it's same as CTE name (this would
11154+
* normally be the case, but not if set_rtable_names had to resolve a
11155+
* conflict).
11156+
*/
11157+
if (strcmp(refname, rte->ctename) != 0)
11158+
printalias = true;
11159+
}
11160+
11161+
if (printalias)
11162+
appendStringInfo(context->buf, "%s%s",
11163+
use_as ? " AS " : " ",
11164+
quote_identifier(refname));
11165+
}
11166+
1114411167
/*
1114511168
* get_column_alias_list - print column alias list for an RTE
1114611169
*

src/test/regress/expected/rules.out

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

31773177
create rule r4 as on delete to rules_src do notify rules_src_deletion;
3178-
\d+ rules_src
3179-
Table "public.rules_src"
3180-
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
3181-
--------+---------+-----------+----------+---------+---------+--------------+-------------
3182-
f1 | integer | | | | plain | |
3183-
f2 | integer | | | 0 | plain | |
3184-
Rules:
3185-
r1 AS
3186-
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)
3187-
r2 AS
3188-
ON UPDATE TO rules_src DO VALUES (old.f1,old.f2,'old'::text), (new.f1,new.f2,'new'::text)
3189-
r3 AS
3190-
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)
3191-
r4 AS
3192-
ON DELETE TO rules_src DO
3193-
NOTIFY rules_src_deletion
3194-
31953178
--
31963179
-- Ensure an aliased target relation for insert is correctly deparsed.
31973180
--
31983181
create rule r5 as on insert to rules_src do instead insert into rules_log AS trgt SELECT NEW.* RETURNING trgt.f1, trgt.f2;
31993182
create rule r6 as on update to rules_src do instead UPDATE rules_log AS trgt SET tag = 'updated' WHERE trgt.f1 = new.f1;
3183+
--
3184+
-- Check deparse disambiguation of INSERT/UPDATE/DELETE targets.
3185+
--
3186+
create rule r7 as on delete to rules_src do instead
3187+
with wins as (insert into int4_tbl as trgt values (0) returning *),
3188+
wupd as (update int4_tbl trgt set f1 = f1+1 returning *),
3189+
wdel as (delete from int4_tbl trgt where f1 = 0 returning *)
3190+
insert into rules_log AS trgt select old.* from wins, wupd, wdel
3191+
returning trgt.f1, trgt.f2;
3192+
-- check display of all rules added above
32003193
\d+ rules_src
32013194
Table "public.rules_src"
32023195
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
@@ -3221,6 +3214,26 @@ Rules:
32213214
r6 AS
32223215
ON UPDATE TO rules_src DO INSTEAD UPDATE rules_log trgt SET tag = 'updated'::text
32233216
WHERE trgt.f1 = new.f1
3217+
r7 AS
3218+
ON DELETE TO rules_src DO INSTEAD WITH wins AS (
3219+
INSERT INTO int4_tbl AS trgt_1 (f1)
3220+
VALUES (0)
3221+
RETURNING trgt_1.f1
3222+
), wupd AS (
3223+
UPDATE int4_tbl trgt_1 SET f1 = trgt_1.f1 + 1
3224+
RETURNING trgt_1.f1
3225+
), wdel AS (
3226+
DELETE FROM int4_tbl trgt_1
3227+
WHERE trgt_1.f1 = 0
3228+
RETURNING trgt_1.f1
3229+
)
3230+
INSERT INTO rules_log AS trgt (f1, f2) SELECT old.f1,
3231+
old.f2
3232+
FROM wins,
3233+
wupd,
3234+
wdel
3235+
RETURNING trgt.f1,
3236+
trgt.f2
32243237

32253238
--
32263239
-- Also check multiassignment deparsing.

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)