Hi, An UPDATE/DELETE ... FOR PORTION OF against a view that has an unqualified DO INSTEAD rule silently ignores the FOR PORTION OF clause and modifies (or deletes) the whole temporal row instead of just the requested portion -- no error, no warning:
CREATE TABLE t (id int, valid_at daterange, name text);
INSERT INTO t VALUES (1, '[2020-01-01,2021-01-01)', 'a');
CREATE VIEW v AS SELECT * FROM t;
CREATE RULE v_upd AS ON UPDATE TO v DO INSTEAD
UPDATE t SET name = NEW.name WHERE id = OLD.id;
UPDATE v FOR PORTION OF valid_at FROM '2020-06-01' TO '2020-07-01'
SET name = 'b';
SELECT * FROM t;
id | valid_at | name
----+-------------------------+------
1 | [2020-01-01,2021-01-01) | b -- whole row changed
The same statement on the base table (or a plain auto-updatable view)
correctly splits the row three ways. DELETE is worse: DELETE ... FOR
PORTION OF through such a view removes the entire row.
The cause is that an unqualified INSTEAD rule replaces the original
query with the rule's action during rewriting, so rewriteTargetView()
-- the path that carries the query's forPortionOf through a view -- is
never reached, and the rule action has forPortionOf = NULL. This is
the exact analog of the INSTEAD OF trigger case that dfce19c2300
("Forbid FOR PORTION OF on views with INSTEAD OF triggers") already
rejects with a feature-not-supported error (5b5e99047ab on
REL_19_STABLE); the DO INSTEAD rule sibling was left unguarded.
The attached patch rejects FOR PORTION OF when an unqualified INSTEAD
rule fires on a view, using the same error as the trigger case. A
DO ALSO rule does not replace the query, so FOR PORTION OF keeps working
on the automatically-updatable path; the tests cover that as well as the
UPDATE and DELETE INSTEAD-rule cases. make check passes. The patch
is against master; the same code (and the same behavior) is present on
REL_19_STABLE.
--
Regards,
Ewan Young
v1-0001-Reject-FOR-PORTION-OF-on-views-with-instead-rules.patch
Description: Binary data
