1
0
mirror of https://github.com/postgres/postgres.git synced 2026-10-23 13:29:09 +03:00

Fix two issues in deparsing.

If the target relation of UPDATE, DELETE, or MERGE has an alias,
we printed that "bare" (without AS or forced quoting), the same
as we do in FROM.  However, if that alias is "set", there's an
ambiguity with the SET keyword that must follow the relation
specification.  The parser would choose the wrong interpretation,
so that the dumped query would fail to reload.  The least surprising
and most future-proof fix seems to be to always print AS in these
contexts, as we already did in INSERT.

Also, we were insufficiently careful about parenthesizing the
left-hand argument of a SubLink (IN, op ANY/ALL, or row comparison),
so that the printed expression might not parse back with the intended
meaning depending on operator precedence.  This is only a problem in
"pretty-print" mode, since non-pretty mode fully parenthesizes every
operator.  So it isn't a hazard for pg_dump, but it still seems like
something to fix.  To do so, switch to using get_rule_expr_paren.
Presently that will add parens in some cases where it wouldn't really
be necessary, but that's better than the reverse, and it arguably
is helpful for readability anyway.

Author: Henson Choi <assam258@gmail.com>
Reviewed-by: Tom Lane <tgl@sss.pgh.pa.us>
Discussion: https://postgr.es/m/CAAAe_zDqXin5NeB5atRCbZqUdcMOeyXRqhan+tVp0F30OCURnw@mail.gmail.com
Discussion: https://postgr.es/m/CAAAe_zCy+D45Qd_br484F+V34y0BVA=hp_7cb=u6HysyMkE3UQ@mail.gmail.com
Backpatch-through: 14
This commit is contained in:
Tom Lane
2026-10-19 17:51:09 -04:00
parent ccb355d681
commit 0da632e084
5 changed files with 39 additions and 19 deletions

View File

@@ -7197,8 +7197,11 @@ get_update_query_def(Query *query, deparse_context *context)
only_marker(rte),
generate_relation_name(rte->relid, NIL));
/* Print the relation alias, if needed */
get_rte_alias(rte, query->resultRelation, false, context);
/*
* Print the relation alias, if needed. Always write AS, since a bare
* alias named "set" would be read as the SET keyword.
*/
get_rte_alias(rte, query->resultRelation, true, context);
appendStringInfoString(buf, " SET ");
@@ -7401,8 +7404,11 @@ get_delete_query_def(Query *query, deparse_context *context)
only_marker(rte),
generate_relation_name(rte->relid, NIL));
/* Print the relation alias, if needed */
get_rte_alias(rte, query->resultRelation, false, context);
/*
* Print the relation alias, if needed. Always write AS, since a bare
* alias named "set" would be read as the SET keyword.
*/
get_rte_alias(rte, query->resultRelation, true, context);
/* Add the USING clause if given */
get_from_clause(query, " USING ", context);
@@ -7450,8 +7456,11 @@ get_merge_query_def(Query *query, deparse_context *context)
only_marker(rte),
generate_relation_name(rte->relid, NIL));
/* Print the relation alias, if needed */
get_rte_alias(rte, query->resultRelation, false, context);
/*
* Print the relation alias, if needed. Always write AS, since a bare
* alias named "set" would be read as the SET keyword.
*/
get_rte_alias(rte, query->resultRelation, true, context);
/* Print the source relation and join clause */
get_from_clause(query, " USING ", context);
@@ -11943,7 +11952,9 @@ get_sublink_expr(SubLink *sublink, deparse_context *context)
/* single combining operator */
OpExpr *opexpr = (OpExpr *) sublink->testexpr;
get_rule_expr(linitial(opexpr->args), context, true);
/* Use get_rule_expr_paren to add parens if needed */
get_rule_expr_paren(linitial(opexpr->args), context, true,
(Node *) sublink);
opname = generate_operator_name(opexpr->opno,
exprType(linitial(opexpr->args)),
exprType(lsecond(opexpr->args)));

View File

@@ -2251,7 +2251,10 @@ select x + y + z as c1,
(x > y) AND (y > z OR x > z) as c8,
(x > y) OR (y > z AND NOT (x > z)) as c9,
(x,y) <> ALL (values(1,2),(3,4)) as c10,
(x,y) <= ANY (values(1,2),(3,4)) as c11
(x,y) <= ANY (values(1,2),(3,4)) as c11,
(x>0 OR y>0) IN (SELECT false) AS c12,
(x < y) <> ALL (SELECT false) AS c13,
true = (y = ANY (SELECT 1)) AS c14
from (values(1,2,3)) v(x,y,z);
select pg_get_viewdef('tt26v', true);
pg_get_viewdef
@@ -2266,7 +2269,10 @@ select pg_get_viewdef('tt26v', true);
x > y AND (y > z OR x > z) AS c8, +
x > y OR y > z AND NOT x > z AS c9, +
((x, y) <> ALL ( VALUES (1,2), (3,4))) AS c10,+
((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11 +
((x, y) <= ANY ( VALUES (1,2), (3,4))) AS c11,+
((x > 0 OR y > 0) IN ( SELECT false)) AS c12, +
((x < y) <> ALL ( SELECT false)) AS c13, +
true = ((y IN ( SELECT 1))) AS c14 +
FROM ( VALUES (1,2,3)) v(x, y, z);
(1 row)

View File

@@ -965,7 +965,7 @@ CREATE OR REPLACE FUNCTION public.foo_update()
LANGUAGE sql
BEGIN ATOMIC
WITH u1 AS (
UPDATE foo foo_1 SET f1 = (foo_1.f1 + 1)
UPDATE foo AS foo_1 SET f1 = (foo_1.f1 + 1)
RETURNING old.f1,
old.f2,
old.f4,
@@ -973,7 +973,7 @@ BEGIN ATOMIC
new.f2,
new.f4
), u2 AS (
UPDATE foo foo_1 SET f1 = (foo_1.f1 + 1)
UPDATE foo AS foo_1 SET f1 = (foo_1.f1 + 1)
RETURNING WITH (OLD AS "old foo") "old foo".f1,
"old foo".f2,
"old foo".f4,
@@ -981,7 +981,7 @@ BEGIN ATOMIC
new.f2,
new.f4
), u3 AS (
UPDATE foo foo_1 SET f1 = (foo_1.f1 + 1)
UPDATE foo AS foo_1 SET f1 = (foo_1.f1 + 1)
RETURNING WITH (NEW AS "new foo") old.f1,
old.f2,
old.f4,

View File

@@ -3283,7 +3283,7 @@ Rules:
RETURNING trgt.f1,
trgt.f2
r6 AS
ON UPDATE TO rules_src DO INSTEAD UPDATE rules_log trgt SET tag = 'updated'::text
ON UPDATE TO rules_src DO INSTEAD UPDATE rules_log AS trgt SET tag = 'updated'::text
WHERE trgt.f1 = new.f1
r7 AS
ON DELETE TO rules_src DO INSTEAD WITH wins AS (
@@ -3291,10 +3291,10 @@ Rules:
VALUES (0)
RETURNING trgt_1.f1
), wupd AS (
UPDATE int4_tbl trgt_1 SET f1 = trgt_1.f1 + 1
UPDATE int4_tbl AS trgt_1 SET f1 = trgt_1.f1 + 1
RETURNING trgt_1.f1
), wdel AS (
DELETE FROM int4_tbl trgt_1
DELETE FROM int4_tbl AS trgt_1
WHERE trgt_1.f1 = 0
RETURNING trgt_1.f1
)
@@ -3322,7 +3322,7 @@ create rule rr as on update to rule_t1 do instead UPDATE rule_dest trgt
f2 | integer | | | | plain | |
Rules:
rr AS
ON UPDATE TO rule_t1 DO INSTEAD UPDATE rule_dest trgt SET (f2[1], f1, tag) = ( SELECT new.f2,
ON UPDATE TO rule_t1 DO INSTEAD UPDATE rule_dest AS trgt SET (f2[1], f1, tag) = ( SELECT new.f2,
new.f1,
'updated'::character varying AS "varchar")
WHERE trgt.f1 = new.f1
@@ -3943,7 +3943,7 @@ CREATE OR REPLACE FUNCTION public.merge_sf_test()
RETURNS TABLE(action text, a integer, b text, id integer, data text, filling integer[], old_id integer, old_data text, old_filling integer[], new_id integer, new_data text, new_filling integer[])
LANGUAGE sql
BEGIN ATOMIC
MERGE INTO sf_target t
MERGE INTO sf_target AS t
USING rule_merge1 s
ON (s.a = t.id)
WHEN MATCHED
@@ -4011,7 +4011,7 @@ CREATE OR REPLACE FUNCTION public.merge_sf_test2()
RETURNS void
LANGUAGE sql
BEGIN ATOMIC
MERGE INTO sf_target t
MERGE INTO sf_target AS t
USING rule_merge1 s
ON (s.a = t.id)
WHEN NOT MATCHED BY TARGET

View File

@@ -825,7 +825,10 @@ select x + y + z as c1,
(x > y) AND (y > z OR x > z) as c8,
(x > y) OR (y > z AND NOT (x > z)) as c9,
(x,y) <> ALL (values(1,2),(3,4)) as c10,
(x,y) <= ANY (values(1,2),(3,4)) as c11
(x,y) <= ANY (values(1,2),(3,4)) as c11,
(x>0 OR y>0) IN (SELECT false) AS c12,
(x < y) <> ALL (SELECT false) AS c13,
true = (y = ANY (SELECT 1)) AS c14
from (values(1,2,3)) v(x,y,z);
select pg_get_viewdef('tt26v', true);