mirror of
https://github.com/postgres/postgres.git
synced 2025-07-26 01:22:12 +03:00
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 b1cb32fb6
, this bug is reachable in cases we already support,
so back-patch all the way.
This commit is contained in:
@ -8634,12 +8634,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, and return the subexpression that's to be assigned.
|
* appear in the input, and return the subexpression that's to be assigned.
|
||||||
* If printit is true, we also print out the appropriate decoration for the
|
* If printit is true, we also print out the appropriate decoration for the
|
||||||
* base column name (that the caller just printed).
|
* base column name (that the caller just printed). We might also need to
|
||||||
|
* 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, bool printit)
|
processIndirection(Node *node, deparse_context *context, bool printit)
|
||||||
{
|
{
|
||||||
StringInfo buf = context->buf;
|
StringInfo buf = context->buf;
|
||||||
|
CoerceToDomain *cdomain = NULL;
|
||||||
|
|
||||||
for (;;)
|
for (;;)
|
||||||
{
|
{
|
||||||
@ -8689,10 +8694,28 @@ processIndirection(Node *node, deparse_context *context, bool printit)
|
|||||||
*/
|
*/
|
||||||
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,48 @@ 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 | Modifiers | 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
|
||||||
|
Has OIDs: no
|
||||||
|
|
||||||
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);
|
|||||||
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;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user