Commit bc2d716a authored by Tom Lane's avatar Tom Lane

Fix ruleutils.c for domain-over-array cases, too.

Further investigation shows that ruleutils isn't quite up to speed either
for cases where we have a domain-over-array: it needs to be prepared to
look past a CoerceToDomain at the top level of field and element
assignments, else it decompiles them incorrectly.  Potentially this would
result in failure to dump/reload a rule, if it looked like the one in the
new test case.  (I also added a test for EXPLAIN; that output isn't broken,
but clearly we need more test coverage here.)

Like commit b1cb32fb, this bug is reachable in cases we already support,
so back-patch all the way.
parent da11977d
...@@ -6206,8 +6206,11 @@ get_update_query_targetlist_def(Query *query, List *targetList, ...@@ -6206,8 +6206,11 @@ get_update_query_targetlist_def(Query *query, List *targetList,
/* /*
* We must dig down into the expr to see if it's a PARAM_MULTIEXPR * We must dig down into the expr to see if it's a PARAM_MULTIEXPR
* Param. That could be buried under FieldStores and ArrayRefs * Param. That could be buried under FieldStores and ArrayRefs
* (cf processIndirection()), and underneath those there could be * and CoerceToDomains (cf processIndirection()), and underneath
* an implicit type coercion. * those there could be an implicit type coercion. Because we
* would ignore implicit type coercions anyway, we don't need to
* be as careful as processIndirection() is about descending past
* implicit CoerceToDomains.
*/ */
expr = (Node *) tle->expr; expr = (Node *) tle->expr;
while (expr) while (expr)
...@@ -6226,6 +6229,14 @@ get_update_query_targetlist_def(Query *query, List *targetList, ...@@ -6226,6 +6229,14 @@ get_update_query_targetlist_def(Query *query, List *targetList,
break; break;
expr = (Node *) aref->refassgnexpr; expr = (Node *) aref->refassgnexpr;
} }
else if (IsA(expr, CoerceToDomain))
{
CoerceToDomain *cdomain = (CoerceToDomain *) expr;
if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
break;
expr = (Node *) cdomain->arg;
}
else else
break; break;
} }
...@@ -10185,13 +10196,17 @@ get_opclass_name(Oid opclass, Oid actual_datatype, ...@@ -10185,13 +10196,17 @@ get_opclass_name(Oid opclass, Oid actual_datatype,
* *
* We strip any top-level FieldStore or assignment ArrayRef nodes that * We strip any top-level FieldStore or assignment ArrayRef nodes that
* appear in the input, printing them as decoration for the base column * appear in the input, printing them as decoration for the base column
* name (which we assume the caller just printed). Return the subexpression * name (which we assume the caller just printed). We might also need to
* that's to be assigned. * strip CoerceToDomain nodes, but only ones that appear above assignment
* nodes.
*
* Returns the subexpression that's to be assigned.
*/ */
static Node * static Node *
processIndirection(Node *node, deparse_context *context) processIndirection(Node *node, deparse_context *context)
{ {
StringInfo buf = context->buf; StringInfo buf = context->buf;
CoerceToDomain *cdomain = NULL;
for (;;) for (;;)
{ {
...@@ -10239,10 +10254,28 @@ processIndirection(Node *node, deparse_context *context) ...@@ -10239,10 +10254,28 @@ processIndirection(Node *node, deparse_context *context)
*/ */
node = (Node *) aref->refassgnexpr; node = (Node *) aref->refassgnexpr;
} }
else if (IsA(node, CoerceToDomain))
{
cdomain = (CoerceToDomain *) node;
/* If it's an explicit domain coercion, we're done */
if (cdomain->coercionformat != COERCE_IMPLICIT_CAST)
break;
/* Tentatively descend past the CoerceToDomain */
node = (Node *) cdomain->arg;
}
else else
break; break;
} }
/*
* If we descended past a CoerceToDomain whose argument turned out not to
* be a FieldStore or array assignment, back up to the CoerceToDomain.
* (This is not enough to be fully correct if there are nested implicit
* CoerceToDomains, but such cases shouldn't ever occur.)
*/
if (cdomain && node == (Node *) cdomain->arg)
node = (Node *) cdomain;
return node; return node;
} }
......
...@@ -266,20 +266,47 @@ insert into dcomptable (d1[1].r, d1[1].i) values(100, 99); -- fail ...@@ -266,20 +266,47 @@ insert into dcomptable (d1[1].r, d1[1].i) values(100, 99); -- fail
ERROR: value for domain dcomptypea violates check constraint "c1" ERROR: value for domain dcomptypea violates check constraint "c1"
update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -- fail update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -- fail
ERROR: value for domain dcomptypea violates check constraint "c1" ERROR: value for domain dcomptypea violates check constraint "c1"
update dcomptable set d1[1].r = d1[1].r - 1 where d1[1].i > 0; update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
select * from dcomptable; select * from dcomptable;
d1 d1
-------------------- --------------------
{"(11,)","(,)"} {"(11,)","(,)"}
{"(99,)"} {"(99,)"}
{"(1,2)","(,)"} {"(1,3)","(,)"}
{"(3,4)","(6,5)"} {"(3,5)","(6,5)"}
{"(7,8)","(10,9)"} {"(7,9)","(10,9)"}
{"(9,10)","(,)"} {"(9,11)","(,)"}
{"(0,2)"} {"(0,3)"}
{"(98,100)"} {"(98,101)"}
(8 rows) (8 rows)
explain (verbose, costs off)
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
QUERY PLAN
----------------------------------------------------------------------------------------------------------------
Update on public.dcomptable
-> Seq Scan on public.dcomptable
Output: (d1[1].r := (d1[1].r - '1'::double precision))[1].i := (d1[1].i + '1'::double precision), ctid
Filter: (dcomptable.d1[1].i > '0'::double precision)
(4 rows)
create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
Table "public.dcomptable"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------+------------+-----------+----------+---------+----------+--------------+-------------
d1 | dcomptypea | | | | extended | |
Indexes:
"dcomptable_d1_key" UNIQUE CONSTRAINT, btree (d1)
Rules:
silly AS
ON DELETE TO dcomptable DO INSTEAD UPDATE dcomptable SET d1[1].r = dcomptable.d1[1].r - 1::double precision, d1[1].i = dcomptable.d1[1].i + 1::double precision
WHERE dcomptable.d1[1].i > 0::double precision
drop table dcomptable; drop table dcomptable;
drop type comptype cascade; drop type comptype cascade;
NOTICE: drop cascades to type dcomptypea NOTICE: drop cascades to type dcomptypea
......
...@@ -150,9 +150,18 @@ insert into dcomptable (d1[1].r) values(99); ...@@ -150,9 +150,18 @@ insert into dcomptable (d1[1].r) values(99);
insert into dcomptable (d1[1].r, d1[1].i) values(99, 100); insert into dcomptable (d1[1].r, d1[1].i) values(99, 100);
insert into dcomptable (d1[1].r, d1[1].i) values(100, 99); -- fail insert into dcomptable (d1[1].r, d1[1].i) values(100, 99); -- fail
update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -- fail update dcomptable set d1[1].r = d1[1].r + 1 where d1[1].i > 0; -- fail
update dcomptable set d1[1].r = d1[1].r - 1 where d1[1].i > 0; update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
select * from dcomptable; select * from dcomptable;
explain (verbose, costs off)
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
create rule silly as on delete to dcomptable do instead
update dcomptable set d1[1].r = d1[1].r - 1, d1[1].i = d1[1].i + 1
where d1[1].i > 0;
\d+ dcomptable
drop table dcomptable; drop table dcomptable;
drop type comptype cascade; drop type comptype cascade;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment