Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
133220: opt: plan row-level BEFORE triggers for cascading mutations r=DrewKimball a=DrewKimball

#### opt: mechanical changes for BEFORE triggers on cascades

This commit consists of the following changes to prepare for adding
support for row-level BEFORE triggers to cascading mutations:
* Added `unsafe_allow_triggers_modifying_cascades` which will determine
  whether a trigger can modify the rows mutated by a cascade, default off.
* Added a "cascade" argument to `buildRowLevelBeforeTriggers` to provide
  the context when building a row-level BEFORE trigger.
* Refactoring the logic for invoking `crdb_internal.plpgsql_raise` to be
  re-used for triggers.

Informs #132971

Release note: None

#### opt: plan row-level BEFORE triggers for cascading mutations

This commit adds logic to invoke the functions for row-level BEFORE
triggers that are fired by a cascade's mutation. This is mostly
straightforward, except for the fact that a row-level BEFORE trigger
can actually modify or filter rows-to-be-mutated. In Postgres, it's
possible to cause constraint violations via this behavior. This commit
adds a runtime check to ensure that trigger functions do not modify
cascading updates/deletes. The check is gated behind
`unsafe_allow_triggers_modifying_cascades`, default off.

Fixes #132971

Release note (sql change): Cascades can now fire row-level BEFORE triggers.
By default, attempting to modify or eliminate the cascading update/delete
will result in a `Triggered Data Change Violation` error. Users that wish
to do this anyway can set `unsafe_allow_triggers_modifying_cascades`.
Note that doing so could result in constraint violations, similar to Postgres.

#### sql: fix queuing behavior for checks and triggers

This commit fixes a bug in `PlanAndRunPostQueries` that could cause
check queries to be run more than once. This would cause problems with
unclosed resources after query completion. This commit also fixes a
minor bug that could cause triggers queued by triggers to be run
before newly-queued checks and cascades, instead of after. The following
commit includes a regression test.

Fixes #133792

Release note: None

#### opt: don't add spurious check queries after BEFORE trigger

This commit fixes a bug that could cause spurious constraint violation
errors when a BEFORE trigger was planned on a cascaded mutation. Since we
don't allow triggers to modify the rows of a cascaded mutation, it is safe
to avoid the extra check entirely. This commit also adds a diamond-pattern
cascade test, with triggers on each table.

Fixes #133784

Release note: None

134005: ui: delete old db pages r=xinhaoz a=xinhaoz

Delete legacy db pages and related functionality.
These pages are now replaced by the v2 db pages.
Deleted
- Database overview page and related apis
- Database details page and related apis
- Database table page and related apis

Epic: none

Release note (ui change): As of 25.1 the legacy db page which was previously available via Advanced Debug is no longer available.

134313: opt: make `max-stack` opt tester option more reliable r=mgartner a=mgartner

The `max-stack` opt tester option now runs the test command in a
separate goroutine. A fresh stack makes tests using this setting more
reliable.

It also decreases the `max-stack` of the original test that motivated
the `max-stack` option (see #132701) to 100KB, between 65KB in which the
test fails after the fix in #132701 and 135KB in which the test fails
before the fix.

Finally, the test has been disabled under `race` builds which increase
the size of stack frames and would cause this test to fail.

Epic: None

Release note: None


134353: mixedversion: update skip upgrades logic r=RaduBerinde a=RaduBerinde

This commit updates the logic that determines which versions support
skipping the previous major release during upgrade.

Epic: REL-1292
Release note: None

Co-authored-by: Drew Kimball <[email protected]>
Co-authored-by: Xin Hao Zhang <[email protected]>
Co-authored-by: Marcus Gartner <[email protected]>
Co-authored-by: Radu Berinde <[email protected]>
  • Loading branch information
5 people committed Nov 6, 2024
5 parents 1118bec + ffa99f9 + 96e4d83 + e6bdabb + e456cc3 commit 03e522a
Show file tree
Hide file tree
Showing 90 changed files with 1,111 additions and 10,336 deletions.
266 changes: 260 additions & 6 deletions pkg/ccl/logictestccl/testdata/logic_test/triggers
Original file line number Diff line number Diff line change
Expand Up @@ -2230,24 +2230,28 @@ statement ok
INSERT INTO parent VALUES (1), (2), (3);
INSERT INTO child VALUES (1, 1), (2, 2), (3, 2), (4, 3);

# TODO(#132971): We should fire the BEFORE trigger on child.
query T noticetrace
UPDATE parent SET k = k * 10 WHERE k = 2 OR k = 3;
----
NOTICE: BEFORE UPDATE ON parent: (2) -> (20)
NOTICE: BEFORE UPDATE ON parent: (3) -> (30)
NOTICE: BEFORE UPDATE ON child: (2,2) -> (2,20)
NOTICE: BEFORE UPDATE ON child: (3,2) -> (3,20)
NOTICE: BEFORE UPDATE ON child: (4,3) -> (4,30)
NOTICE: AFTER UPDATE ON parent: (2) -> (20)
NOTICE: AFTER UPDATE ON parent: (3) -> (30)
NOTICE: AFTER UPDATE ON child: (2,2) -> (2,20)
NOTICE: AFTER UPDATE ON child: (3,2) -> (3,20)
NOTICE: AFTER UPDATE ON child: (4,3) -> (4,30)

# TODO(#132971): We should fire the BEFORE trigger on child.
query T noticetrace
DELETE FROM parent WHERE k = 20 OR k = 30;
----
NOTICE: BEFORE DELETE ON parent: (20) -> <NULL>
NOTICE: BEFORE DELETE ON parent: (30) -> <NULL>
NOTICE: BEFORE DELETE ON child: (2,20) -> <NULL>
NOTICE: BEFORE DELETE ON child: (3,20) -> <NULL>
NOTICE: BEFORE DELETE ON child: (4,30) -> <NULL>
NOTICE: AFTER DELETE ON parent: (20) -> <NULL>
NOTICE: AFTER DELETE ON parent: (30) -> <NULL>
NOTICE: AFTER DELETE ON child: (2,20) -> <NULL>
Expand Down Expand Up @@ -2286,31 +2290,35 @@ $$;
statement ok
CREATE TRIGGER foo BEFORE INSERT ON ab FOR EACH ROW EXECUTE FUNCTION h();

# TODO(#132971): We should fire the BEFORE trigger on child.
query T noticetrace
INSERT INTO ab VALUES (2, 20), (3, 30);
----
NOTICE: update parent.k to 20 in trigger where k = 2
NOTICE: BEFORE UPDATE ON parent: (2) -> (20)
NOTICE: BEFORE UPDATE ON child: (2,2) -> (2,20)
NOTICE: BEFORE UPDATE ON child: (3,2) -> (3,20)
NOTICE: AFTER UPDATE ON parent: (2) -> (20)
NOTICE: AFTER UPDATE ON child: (2,2) -> (2,20)
NOTICE: AFTER UPDATE ON child: (3,2) -> (3,20)
NOTICE: update parent.k to 30 in trigger where k = 3
NOTICE: BEFORE UPDATE ON parent: (3) -> (30)
NOTICE: BEFORE UPDATE ON child: (4,3) -> (4,30)
NOTICE: AFTER UPDATE ON parent: (3) -> (30)
NOTICE: AFTER UPDATE ON child: (4,3) -> (4,30)

# TODO(#132971): We should fire the BEFORE trigger on child.
query T noticetrace
INSERT INTO ab VALUES (20, NULL), (30, NULL);
----
NOTICE: delete from parent in trigger where k = 20
NOTICE: BEFORE DELETE ON parent: (20) -> <NULL>
NOTICE: BEFORE DELETE ON child: (2,20) -> <NULL>
NOTICE: BEFORE DELETE ON child: (3,20) -> <NULL>
NOTICE: AFTER DELETE ON parent: (20) -> <NULL>
NOTICE: AFTER DELETE ON child: (2,20) -> <NULL>
NOTICE: AFTER DELETE ON child: (3,20) -> <NULL>
NOTICE: delete from parent in trigger where k = 30
NOTICE: BEFORE DELETE ON parent: (30) -> <NULL>
NOTICE: BEFORE DELETE ON child: (4,30) -> <NULL>
NOTICE: AFTER DELETE ON parent: (30) -> <NULL>
NOTICE: AFTER DELETE ON child: (4,30) -> <NULL>

Expand All @@ -2330,31 +2338,35 @@ subtest after_trigger_fires_cascades
statement ok
CREATE TRIGGER foo AFTER INSERT OR DELETE ON ab FOR EACH ROW EXECUTE FUNCTION h();

# TODO(#132971): We should fire the BEFORE trigger on child.
query T noticetrace
INSERT INTO ab VALUES (2, 20), (3, 30);
----
NOTICE: update parent.k to 20 in trigger where k = 2
NOTICE: BEFORE UPDATE ON parent: (2) -> (20)
NOTICE: BEFORE UPDATE ON child: (2,2) -> (2,20)
NOTICE: BEFORE UPDATE ON child: (3,2) -> (3,20)
NOTICE: AFTER UPDATE ON parent: (2) -> (20)
NOTICE: AFTER UPDATE ON child: (2,2) -> (2,20)
NOTICE: AFTER UPDATE ON child: (3,2) -> (3,20)
NOTICE: update parent.k to 30 in trigger where k = 3
NOTICE: BEFORE UPDATE ON parent: (3) -> (30)
NOTICE: BEFORE UPDATE ON child: (4,3) -> (4,30)
NOTICE: AFTER UPDATE ON parent: (3) -> (30)
NOTICE: AFTER UPDATE ON child: (4,3) -> (4,30)

# TODO(#132971): We should fire the BEFORE trigger on child.
query T noticetrace
INSERT INTO ab VALUES (20, NULL), (30, NULL);
----
NOTICE: delete from parent in trigger where k = 20
NOTICE: BEFORE DELETE ON parent: (20) -> <NULL>
NOTICE: BEFORE DELETE ON child: (2,20) -> <NULL>
NOTICE: BEFORE DELETE ON child: (3,20) -> <NULL>
NOTICE: AFTER DELETE ON parent: (20) -> <NULL>
NOTICE: AFTER DELETE ON child: (2,20) -> <NULL>
NOTICE: AFTER DELETE ON child: (3,20) -> <NULL>
NOTICE: delete from parent in trigger where k = 30
NOTICE: BEFORE DELETE ON parent: (30) -> <NULL>
NOTICE: BEFORE DELETE ON child: (4,30) -> <NULL>
NOTICE: AFTER DELETE ON parent: (30) -> <NULL>
NOTICE: AFTER DELETE ON child: (4,30) -> <NULL>

Expand Down Expand Up @@ -2415,13 +2427,17 @@ query T noticetrace
CALL wrap_parent_update(2, NULL);
----
NOTICE: BEFORE DELETE ON parent: (2) -> <NULL>
NOTICE: BEFORE DELETE ON child: (2,2) -> <NULL>
NOTICE: BEFORE DELETE ON child: (3,2) -> <NULL>
NOTICE: FK violation updating parent from 2 to <NULL>

# Try updating a referenced row in the parent table.
query T noticetrace
CALL wrap_parent_update(2, 10);
----
NOTICE: BEFORE UPDATE ON parent: (2) -> (10)
NOTICE: BEFORE UPDATE ON child: (2,2) -> (2,10)
NOTICE: BEFORE UPDATE ON child: (3,2) -> (3,10)
NOTICE: FK violation updating parent from 2 to 10

# Try inserting a row with no reference into the child2 table.
Expand All @@ -2434,10 +2450,248 @@ NOTICE: FK violation inserting (10, 10) into child2
statement ok
DROP PROCEDURE wrap_parent_update;
DROP PROCEDURE wrap_child2_insert;
DROP TABLE child2;

# Unless "unsafe_allow_triggers_modifying_cascades" is set, triggers are not
# allowed to modify or filter rows the mutation for a cascade.
subtest triggers_modify_fk_cascades

statement ok
CREATE FUNCTION h() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
BEGIN
NEW.k := (NEW).k + 100;
OLD.k := (OLD).k + 100;
RETURN COALESCE(NEW, OLD);
END
$$;

statement ok
CREATE FUNCTION filter() RETURNS TRIGGER LANGUAGE PLpgSQL AS $$
BEGIN
RETURN NULL;
END
$$;

statement ok
DELETE FROM child WHERE True;
DELETE FROM parent WHERE True;
INSERT INTO parent VALUES (1), (2), (3);
INSERT INTO child VALUES (1, 1), (2, 2), (3, 2), (4, 3);

statement ok
CREATE TRIGGER mod BEFORE INSERT OR UPDATE OR DELETE ON child FOR EACH ROW EXECUTE FUNCTION h();

statement error pgcode 27000 pq: trigger mod attempted to modify or filter a row in a cascade operation: \(1,11\)
UPDATE parent SET k = k + 10 WHERE k < 3;

statement error pgcode 27000 pq: trigger mod attempted to modify or filter a row in a cascade operation: \(1,1\)
DELETE FROM parent WHERE k < 3;

statement ok
SET unsafe_allow_triggers_modifying_cascades = true;

statement ok
UPDATE parent SET k = k + 10 WHERE k < 3;

statement ok
DELETE FROM parent WHERE k < 3;

statement ok
RESET unsafe_allow_triggers_modifying_cascades;

statement ok
DROP TRIGGER mod ON child;

statement ok
DELETE FROM child WHERE True;
DELETE FROM parent WHERE True;
INSERT INTO parent VALUES (1), (2), (3);
INSERT INTO child VALUES (1, 1), (2, 2), (3, 2), (4, 3);

statement ok
CREATE TRIGGER filter BEFORE INSERT OR UPDATE OR DELETE ON child FOR EACH ROW EXECUTE FUNCTION filter();

statement error pgcode 27000 pq: trigger filter attempted to modify or filter a row in a cascade operation: \(1,11\)
UPDATE parent SET k = k + 10 WHERE k < 3;

statement error pgcode 27000 pq: trigger filter attempted to modify or filter a row in a cascade operation: \(1,1\)
DELETE FROM parent WHERE k < 3;

statement ok
SET unsafe_allow_triggers_modifying_cascades = true;

statement ok
UPDATE parent SET k = k + 10 WHERE k < 3;

statement ok
DELETE FROM parent WHERE k < 3;

statement ok
RESET unsafe_allow_triggers_modifying_cascades;

statement ok
DROP TRIGGER filter ON child;

statement ok
DELETE FROM child WHERE True;
DELETE FROM parent WHERE True;
INSERT INTO parent VALUES (1), (2), (3);
INSERT INTO child VALUES (1, 1), (2, 2), (3, 2), (4, 3);

# Modifications to mutated rows made by BEFORE triggers are visible to cascades.
subtest before_trigger_modifies_fk_cascades

statement ok
CREATE TRIGGER mod BEFORE INSERT OR UPDATE OR DELETE ON parent FOR EACH ROW EXECUTE FUNCTION h();

query T noticetrace
UPDATE parent SET k = k + 10 WHERE k < 3;
----
NOTICE: BEFORE UPDATE ON parent: (1) -> (11)
NOTICE: BEFORE UPDATE ON parent: (2) -> (12)
NOTICE: BEFORE UPDATE ON child: (1,1) -> (1,111)
NOTICE: BEFORE UPDATE ON child: (2,2) -> (2,112)
NOTICE: BEFORE UPDATE ON child: (3,2) -> (3,112)
NOTICE: AFTER UPDATE ON parent: (1) -> (111)
NOTICE: AFTER UPDATE ON parent: (2) -> (112)
NOTICE: AFTER UPDATE ON child: (1,1) -> (1,111)
NOTICE: AFTER UPDATE ON child: (2,2) -> (2,112)
NOTICE: AFTER UPDATE ON child: (3,2) -> (3,112)

query II rowsort
SELECT * FROM child;
----
1 111
2 112
3 112
4 3

query T noticetrace
DELETE FROM parent WHERE k > 3;
----
NOTICE: BEFORE DELETE ON parent: (111) -> <NULL>
NOTICE: BEFORE DELETE ON parent: (112) -> <NULL>
NOTICE: BEFORE DELETE ON child: (1,111) -> <NULL>
NOTICE: BEFORE DELETE ON child: (2,112) -> <NULL>
NOTICE: BEFORE DELETE ON child: (3,112) -> <NULL>
NOTICE: AFTER DELETE ON parent: (111) -> <NULL>
NOTICE: AFTER DELETE ON parent: (112) -> <NULL>
NOTICE: AFTER DELETE ON child: (1,111) -> <NULL>
NOTICE: AFTER DELETE ON child: (2,112) -> <NULL>
NOTICE: AFTER DELETE ON child: (3,112) -> <NULL>

query II rowsort
SELECT * FROM child;
----
4 3

statement ok
DROP TRIGGER mod ON parent;

subtest cascade_diamond

# Create a diamond cascade structure.
statement ok
DROP TABLE child;
DELETE FROM parent WHERE True;

statement ok
CREATE TABLE child (k INT PRIMARY KEY, v INT UNIQUE NOT NULL REFERENCES parent(k) ON UPDATE CASCADE ON DELETE CASCADE);
CREATE TABLE child2 (k INT PRIMARY KEY, v INT UNIQUE NOT NULL REFERENCES parent(k) ON UPDATE CASCADE ON DELETE CASCADE);

statement ok
CREATE TRIGGER foo BEFORE INSERT OR UPDATE OR DELETE ON child FOR EACH ROW EXECUTE FUNCTION g();

statement ok
CREATE TRIGGER bar AFTER INSERT OR UPDATE OR DELETE ON child FOR EACH ROW EXECUTE FUNCTION g();

statement ok
CREATE TRIGGER foo BEFORE INSERT OR UPDATE OR DELETE ON child2 FOR EACH ROW EXECUTE FUNCTION g();

statement ok
CREATE TRIGGER bar AFTER INSERT OR UPDATE OR DELETE ON child2 FOR EACH ROW EXECUTE FUNCTION g();

statement ok
CREATE TABLE grandchild (
k INT PRIMARY KEY,
v INT REFERENCES child(v) ON UPDATE CASCADE ON DELETE CASCADE,
v2 INT REFERENCES child2(v) ON UPDATE CASCADE ON DELETE CASCADE
);

statement ok
CREATE TRIGGER foo BEFORE INSERT OR UPDATE OR DELETE ON grandchild FOR EACH ROW EXECUTE FUNCTION g();

statement ok
CREATE TRIGGER bar AFTER INSERT OR UPDATE OR DELETE ON grandchild FOR EACH ROW EXECUTE FUNCTION g();

statement ok
INSERT INTO parent VALUES (1), (2), (3);
INSERT INTO child VALUES (1, 1), (2, 2), (3, 3);
INSERT INTO child2 VALUES (1, 1), (2, 2), (3, 3);
INSERT INTO grandchild VALUES (1, 1, 1), (2, 2, 2), (3, 2, 2), (4, 3, 3);

# Update the parent table, which should cascade to the children and grandchild.
# Note that both child tables cascade to the grandchild.
#
# Regression test for #133784 and #133792.
query T noticetrace
UPDATE parent SET k = k + 10 WHERE k < 3;
----
NOTICE: BEFORE UPDATE ON parent: (1) -> (11)
NOTICE: BEFORE UPDATE ON parent: (2) -> (12)
NOTICE: BEFORE UPDATE ON child: (1,1) -> (1,11)
NOTICE: BEFORE UPDATE ON child: (2,2) -> (2,12)
NOTICE: BEFORE UPDATE ON child2: (1,1) -> (1,11)
NOTICE: BEFORE UPDATE ON child2: (2,2) -> (2,12)
NOTICE: AFTER UPDATE ON parent: (1) -> (11)
NOTICE: AFTER UPDATE ON parent: (2) -> (12)
NOTICE: AFTER UPDATE ON child: (1,1) -> (1,11)
NOTICE: AFTER UPDATE ON child: (2,2) -> (2,12)
NOTICE: AFTER UPDATE ON child2: (1,1) -> (1,11)
NOTICE: AFTER UPDATE ON child2: (2,2) -> (2,12)
NOTICE: BEFORE UPDATE ON grandchild: (1,1,1) -> (1,11,1)
NOTICE: BEFORE UPDATE ON grandchild: (2,2,2) -> (2,12,2)
NOTICE: BEFORE UPDATE ON grandchild: (3,2,2) -> (3,12,2)
NOTICE: BEFORE UPDATE ON grandchild: (1,11,1) -> (1,11,11)
NOTICE: BEFORE UPDATE ON grandchild: (2,12,2) -> (2,12,12)
NOTICE: BEFORE UPDATE ON grandchild: (3,12,2) -> (3,12,12)
NOTICE: AFTER UPDATE ON grandchild: (1,1,1) -> (1,11,1)
NOTICE: AFTER UPDATE ON grandchild: (2,2,2) -> (2,12,2)
NOTICE: AFTER UPDATE ON grandchild: (3,2,2) -> (3,12,2)
NOTICE: AFTER UPDATE ON grandchild: (1,11,1) -> (1,11,11)
NOTICE: AFTER UPDATE ON grandchild: (2,12,2) -> (2,12,12)
NOTICE: AFTER UPDATE ON grandchild: (3,12,2) -> (3,12,12)

query II rowsort
SELECT * FROM child;
----
1 11
2 12
3 3

query II rowsort
SELECT * FROM child2;
----
1 11
2 12
3 3

query III rowsort
SELECT * FROM grandchild;
----
1 11 11
2 12 12
3 12 12
4 3 3

statement ok
DROP TABLE grandchild;
DROP TABLE child;
DROP TABLE child2;
DROP TABLE parent;
DROP FUNCTION g;
DROP FUNCTION h;
DROP FUNCTION filter;

# ==============================================================================
# Test order of execution.
Expand Down
2 changes: 2 additions & 0 deletions pkg/cmd/roachtest/roachtestutil/mixedversion/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ go_library(
importpath = "github.com/cockroachdb/cockroach/pkg/cmd/roachtest/roachtestutil/mixedversion",
visibility = ["//visibility:public"],
deps = [
"//pkg/clusterversion",
"//pkg/cmd/roachprod/grafana",
"//pkg/cmd/roachtest/cluster",
"//pkg/cmd/roachtest/option",
Expand Down Expand Up @@ -54,6 +55,7 @@ go_test(
embed = [":mixedversion"],
embedsrcs = ["testdata/test_releases.yaml"],
deps = [
"//pkg/clusterversion",
"//pkg/cmd/roachtest/option",
"//pkg/cmd/roachtest/registry",
"//pkg/cmd/roachtest/roachtestutil",
Expand Down
Loading

0 comments on commit 03e522a

Please sign in to comment.