Skip to content

Commit

Permalink
sql: return "unimplemented" errors for unsupported trigger syntax
Browse files Browse the repository at this point in the history
This commit adds "unimplemented" errors to prevent usage of currently
unsupported `CREATE TRIGGER` syntax. For example, statement-level
triggers are not yet supported.

Informs #126362
Informs #126363
Informs #135655
Informs #135657
Informs #135656
Informs #135658
Fixes #135131

Release note: None
  • Loading branch information
DrewKimball committed Nov 19, 2024
1 parent 128db0a commit f3ff553
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 40 deletions.
41 changes: 29 additions & 12 deletions pkg/ccl/logictestccl/testdata/logic_test/triggers
Original file line number Diff line number Diff line change
Expand Up @@ -580,21 +580,22 @@ subtest when_clause

# The WHEN clause must be of type BOOL.
statement error pgcode 42804 pq: argument of WHEN must be type bool, not type int
CREATE TRIGGER foo AFTER INSERT ON xy WHEN (1) EXECUTE FUNCTION f();
CREATE TRIGGER foo AFTER INSERT ON xy FOR EACH ROW WHEN (1) EXECUTE FUNCTION f();

# The WHEN clause cannot reference table columns.
statement error pgcode 42703 pq: column "x" does not exist\nHINT: column references in a trigger WHEN clause must be prefixed with NEW or OLD
CREATE TRIGGER foo AFTER INSERT ON xy WHEN (x = 1) EXECUTE FUNCTION f();
CREATE TRIGGER foo AFTER INSERT ON xy FOR EACH ROW WHEN (x = 1) EXECUTE FUNCTION f();

# The WHEN clause cannot contain a subquery.
statement error pgcode 0A000 pq: subqueries are not allowed in WHEN
CREATE TRIGGER foo AFTER INSERT ON xy WHEN (SELECT 1) EXECUTE FUNCTION f();
CREATE TRIGGER foo AFTER INSERT ON xy FOR EACH ROW WHEN (SELECT 1) EXECUTE FUNCTION f();

statement error pgcode 42P17 pq: statement trigger's WHEN condition cannot reference column values
CREATE TRIGGER foo AFTER INSERT ON xy WHEN (NEW IS NULL) EXECUTE FUNCTION f();

statement error pgcode 42P17 pq: statement trigger's WHEN condition cannot reference column values
CREATE TRIGGER foo AFTER INSERT ON xy WHEN (OLD IS NULL) EXECUTE FUNCTION f();
# TODO(#126362): uncomment these test cases.
# statement error pgcode 42P17 pq: statement trigger's WHEN condition cannot reference column values
# CREATE TRIGGER foo AFTER INSERT ON xy WHEN (NEW IS NULL) EXECUTE FUNCTION f();
#
# statement error pgcode 42P17 pq: statement trigger's WHEN condition cannot reference column values
# CREATE TRIGGER foo AFTER INSERT ON xy WHEN (OLD IS NULL) EXECUTE FUNCTION f();

statement error pgcode 42P17 pq: DELETE trigger's WHEN condition cannot reference NEW values
CREATE TRIGGER foo AFTER DELETE ON xy FOR EACH ROW WHEN (NEW IS NULL) EXECUTE FUNCTION f();
Expand Down Expand Up @@ -622,7 +623,7 @@ CREATE FUNCTION g() RETURNS TRIGGER AS $$
$$ LANGUAGE PLpgSQL;

statement error pgcode 42P01 pq: relation "nonexistent" does not exist
CREATE TRIGGER foo BEFORE INSERT ON xy EXECUTE FUNCTION g();
CREATE TRIGGER foo BEFORE INSERT ON xy FOR EACH ROW EXECUTE FUNCTION g();

statement ok
DROP FUNCTION g;
Expand Down Expand Up @@ -650,7 +651,7 @@ CREATE FUNCTION g() RETURNS TRIGGER AS $$
$$ LANGUAGE PLpgSQL;

statement error pgcode 42883 pq: unknown function: f_nonexistent()
CREATE TRIGGER foo AFTER DELETE ON xy EXECUTE FUNCTION g();
CREATE TRIGGER foo AFTER DELETE ON xy FOR EACH ROW EXECUTE FUNCTION g();

# Case with a nonexistent type reference.
statement ok
Expand All @@ -662,7 +663,7 @@ CREATE FUNCTION g() RETURNS TRIGGER AS $$
$$ LANGUAGE PLpgSQL;

statement error pgcode 42704 pq: type "typ_nonexistent" does not exist
CREATE TRIGGER foo BEFORE INSERT ON xy EXECUTE FUNCTION g();
CREATE TRIGGER foo BEFORE INSERT ON xy FOR EACH ROW EXECUTE FUNCTION g();

# Incorrect type in a SQL expression.
statement ok
Expand Down Expand Up @@ -691,7 +692,7 @@ CREATE FUNCTION g() RETURNS TRIGGER AS $$
$$ LANGUAGE PLpgSQL;

statement error pgcode 0A000 pq: unimplemented: CREATE TABLE usage inside a function definition
CREATE TRIGGER foo AFTER DELETE ON xy EXECUTE FUNCTION g();
CREATE TRIGGER foo AFTER DELETE ON xy FOR EACH ROW EXECUTE FUNCTION g();

statement ok
DROP FUNCTION g;
Expand Down Expand Up @@ -3477,6 +3478,22 @@ CREATE OR REPLACE TRIGGER foo AFTER INSERT ON xy FOR EACH ROW EXECUTE FUNCTION f
statement error pgcode 0A000 pq: unimplemented: cascade dropping triggers
DROP TRIGGER foo ON xy CASCADE;

statement error pgcode 0A000 pq: unimplemented: statement-level triggers are not yet supported
CREATE TRIGGER foo AFTER INSERT ON xy FOR EACH STATEMENT EXECUTE FUNCTION f();

statement error pgcode 0A000 pq: unimplemented: INSTEAD OF triggers are not yet supported
CREATE TRIGGER foo INSTEAD OF INSERT ON v FOR EACH ROW EXECUTE FUNCTION f();

statement error pgcode 0A000 pq: unimplemented: REFERENCING clause is not yet supported for triggers
CREATE TRIGGER foo AFTER INSERT ON xy REFERENCING NEW TABLE AS nt FOR EACH ROW EXECUTE FUNCTION f();

# TODO(#126362): uncomment this case.
# statement error pgcode 0A000 pq: unimplemented: TRUNCATE triggers are not yet supported
# CREATE TRIGGER foo AFTER TRUNCATE ON xy FOR EACH STATEMENT EXECUTE FUNCTION f();

statement error pgcode 0A000 pq: unimplemented: column lists are not yet supported for triggers
CREATE TRIGGER foo AFTER UPDATE OF y ON xy FOR EACH ROW EXECUTE FUNCTION f();

statement ok
CREATE TRIGGER foo AFTER INSERT ON xy FOR EACH ROW EXECUTE FUNCTION f();

Expand Down
41 changes: 41 additions & 0 deletions pkg/sql/opt/optbuilder/create_trigger.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,9 @@ func (b *Builder) buildCreateTrigger(ct *tree.CreateTrigger, inScope *scope) (ou
panic(err)
}

// Check for unsupported CREATE TRIGGER statements.
checkUnsupportedCreateTrigger(ct, ds)

// Lookup the implicit table type. This must happen after the above checks,
// since virtual/system tables do not have an implicit type.
typeID := typedesc.TableIDToImplicitTypeOID(descpb.ID(ds.ID()))
Expand Down Expand Up @@ -326,3 +329,41 @@ var triggerFuncStaticParams = []routineParam{

const triggerColNew = "new"
const triggerColOld = "old"

func checkUnsupportedCreateTrigger(ct *tree.CreateTrigger, ds cat.DataSource) {
if ct.ForEach == tree.TriggerForEachStatement {
panic(unimplementedStatementLevelErr)
}
if ct.ActionTime == tree.TriggerActionTimeInsteadOf {
panic(unimplementedInsteadOfErr)
}
if len(ct.Transitions) > 0 {
panic(unimplementedReferencingErr)
}
for _, event := range ct.Events {
if event.EventType == tree.TriggerEventTruncate {
panic(unimplementedTruncateErr)
}
if len(event.Columns) > 0 {
panic(unimplementedColumnListErr)
}
}
if _, ok := ds.(cat.View); ok {
panic(unimplementedViewTriggerErr)
}
}

var (
unimplementedStatementLevelErr = unimplemented.NewWithIssue(126362,
"statement-level triggers are not yet supported")
unimplementedInsteadOfErr = unimplemented.NewWithIssue(126363,
"INSTEAD OF triggers are not yet supported")
unimplementedReferencingErr = unimplemented.NewWithIssue(135655,
"REFERENCING clause is not yet supported for triggers")
unimplementedTruncateErr = unimplemented.NewWithIssue(135657,
"TRUNCATE triggers are not yet supported")
unimplementedColumnListErr = unimplemented.NewWithIssue(135656,
"column lists are not yet supported for triggers")
unimplementedViewTriggerErr = unimplemented.NewWithIssue(135658,
"triggers on views are not yet supported")
)
5 changes: 2 additions & 3 deletions pkg/sql/opt/optbuilder/testdata/create_trigger
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,11 @@ create-trigger
├── CREATE TRIGGER tr BEFORE INSERT OR UPDATE ON xy FOR EACH ROW EXECUTE FUNCTION f_basic()
└── no dependencies

# TODO(#126362, #135655): implement this case.
build
CREATE TRIGGER foo AFTER DELETE ON xy REFERENCING OLD TABLE AS foo WHEN (1 = 1) EXECUTE FUNCTION f_basic();
----
create-trigger
├── CREATE TRIGGER foo AFTER DELETE ON xy REFERENCING OLD TABLE AS foo FOR EACH STATEMENT WHEN (1 = 1) EXECUTE FUNCTION f_basic()
└── no dependencies
error (0A000): unimplemented: statement-level triggers are not yet supported

build
CREATE TRIGGER foo AFTER INSERT ON xy FOR EACH ROW EXECUTE FUNCTION f_basic();
Expand Down
29 changes: 4 additions & 25 deletions pkg/sql/schemachanger/scbuild/testdata/create_trigger
Original file line number Diff line number Diff line change
Expand Up @@ -37,28 +37,7 @@ CREATE TRIGGER tr BEFORE INSERT OR UPDATE ON xy FOR EACH ROW EXECUTE FUNCTION f(
- [[TriggerDeps:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{tableId: 104, triggerId: 1, usesRelationIds: [105, 108], usesRoutineIds: [109, 110], usesTypeIds: [106, 107]}

build
CREATE TRIGGER tr AFTER DELETE ON xy REFERENCING OLD TABLE AS foo WHEN (1 = 1) EXECUTE FUNCTION f('a', 'bc');
----
- [[IndexData:{DescID: 104, IndexID: 1}, PUBLIC], PUBLIC]
{indexId: 1, tableId: 104}
- [[TableData:{DescID: 104, ReferencedDescID: 100}, PUBLIC], PUBLIC]
{databaseId: 100, tableId: 104}
- [[Trigger:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{tableId: 104, triggerId: 1}
- [[TriggerName:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{name: tr, tableId: 104, triggerId: 1}
- [[TriggerEnabled:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{enabled: true, tableId: 104, triggerId: 1}
- [[TriggerTiming:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{actionTime: AFTER, tableId: 104, triggerId: 1}
- [[TriggerEvents:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{events: [{columnNames: [], type: DELETE}], tableId: 104, triggerId: 1}
- [[TriggerTransition:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{oldTransitionAlias: foo, tableId: 104, triggerId: 1}
- [[TriggerWhen:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{tableId: 104, triggerId: 1, whenExpr: '(1:::INT8 = 1:::INT8)'}
- [[TriggerFunctionCall:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{funcArgs: [a, bc], funcBody: "DECLARE\nfoo @100106 := 'a';\nBEGIN\nINSERT INTO defaultdb.public.ab VALUES ((new).x, (new).y);\nRAISE NOTICE '% %', public.g(), nextval(108:::REGCLASS);\nRETURN new;\nEND;\n", funcId: 110, tableId: 104, triggerId: 1}
- [[TriggerDeps:{DescID: 104, TriggerID: 1}, PUBLIC], ABSENT]
{tableId: 104, triggerId: 1, usesRelationIds: [105, 108], usesRoutineIds: [109, 110], usesTypeIds: [106, 107]}
# TODO(#126362, #135655): uncomment this test case.
# build
# CREATE TRIGGER tr AFTER DELETE ON xy REFERENCING OLD TABLE AS foo WHEN (1 = 1) EXECUTE FUNCTION f('a', 'bc');
# ----

0 comments on commit f3ff553

Please sign in to comment.