chore: import upstream snapshot with attribution
This commit is contained in:
Executable
+747
@@ -0,0 +1,747 @@
|
||||
// Copyright 2025 Dolthub, Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package _go
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
)
|
||||
|
||||
func TestCreateTrigger(t *testing.T) {
|
||||
RunScripts(t, []ScriptTest{
|
||||
{
|
||||
Name: "BEFORE INSERT",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_' || NEW.pk::text;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi_1"},
|
||||
{2, "there_2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BEFORE UPDATE",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_' || NEW.pk::text;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE UPDATE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "UPDATE test SET v1 = v1 || '|' WHERE pk IN (1, 2);",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi|_1"},
|
||||
{2, "there|_2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BEFORE DELETE",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO test2 VALUES (OLD.pk, OLD.v1);
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE DELETE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "DELETE FROM test WHERE pk = 1;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BEFORE INSERT returning NULL",
|
||||
Skip: true, // TODO: returning a NULL-filled row isn't quite valid for this
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_' || NEW.pk::text;
|
||||
INSERT INTO test2 VALUES (NEW.pk, NEW.v1);
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi_1"},
|
||||
{2, "there_2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BEFORE UPDATE returning NULL",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_' || NEW.pk::text;
|
||||
INSERT INTO test2 VALUES (NEW.pk, NEW.v1);
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE UPDATE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "UPDATE test SET v1 = v1 || '|' WHERE pk IN (1, 2);",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi|_1"},
|
||||
{2, "there|_2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BEFORE DELETE returning NULL",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO test2 VALUES (OLD.pk, OLD.v1);
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE DELETE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "DELETE FROM test WHERE pk = 1;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "BEFORE UPDATE with DELETE DML",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO test2 VALUES (OLD.pk, OLD.v1);
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE UPDATE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "DELETE FROM test WHERE pk = 1;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "AFTER INSERT",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_' || NEW.pk::text;
|
||||
INSERT INTO test2 VALUES (NEW.pk, NEW.v1);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger AFTER INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi_1"},
|
||||
{2, "there_2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "AFTER UPDATE",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_' || NEW.pk::text;
|
||||
INSERT INTO test2 VALUES (NEW.pk, NEW.v1);
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger AFTER UPDATE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "UPDATE test SET v1 = v1 || '|' WHERE pk IN (1, 2);",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi|"},
|
||||
{2, "there|"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi|_1"},
|
||||
{2, "there|_2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "AFTER DELETE returning NULL",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO test2 VALUES (OLD.pk, OLD.v1);
|
||||
RETURN NULL;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger AFTER DELETE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "DELETE FROM test WHERE pk = 1;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Cascading DELETE into INSERT, different tables",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO test2 VALUES (OLD.pk, OLD.v1);
|
||||
RETURN OLD;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE FUNCTION trigger_func2() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.pk := NEW.pk + 100;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE DELETE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test2 FOR EACH ROW EXECUTE FUNCTION trigger_func2();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hi"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "DELETE FROM test WHERE pk = 1;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test2;",
|
||||
Expected: []sql.Row{
|
||||
{101, "hi"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Cascading INSERT into UPDATE, same table",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
UPDATE test SET v1 = v1 || NEW.pk::text;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE FUNCTION trigger_func2() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_u';
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
`CREATE TRIGGER test_trigger2 BEFORE UPDATE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func2();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test ORDER BY pk;",
|
||||
Skip: true, // TODO: the UPDATE cannot see the table's contents until the INSERT has completely finished
|
||||
Expected: []sql.Row{
|
||||
{1, "hi2_u"},
|
||||
{2, "there"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Multiple triggers on same table",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func_a() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || 'a';
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE FUNCTION trigger_func_c() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || 'c';
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE FUNCTION trigger_func_b() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || 'b';
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger_b BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func_b();`,
|
||||
`CREATE TRIGGER test_trigger_a BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func_a();`,
|
||||
`CREATE TRIGGER test_trigger_c BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func_c();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test ORDER BY pk;",
|
||||
Expected: []sql.Row{
|
||||
{1, "hiabc"},
|
||||
{2, "thereabc"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Stack depth limit exceeded",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
"CREATE TABLE test2 (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO test2 VALUES (NEW.pk+2, NEW.v1 || '_');
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE FUNCTION trigger_func2() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
INSERT INTO test VALUES (NEW.pk+4, NEW.v1 || '|');
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test2 FOR EACH ROW EXECUTE FUNCTION trigger_func2();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
Skip: true, // TODO: currently we'll just run until we run out of memory, need to abort before that
|
||||
ExpectedErr: "stack depth limit exceeded",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DELETE TABLE deletes attached triggers",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_';
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
`CREATE TRIGGER test_trigger2 BEFORE UPDATE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();",
|
||||
ExpectedErr: "already exists",
|
||||
},
|
||||
{
|
||||
Query: "CREATE TRIGGER test_trigger2 BEFORE UPDATE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();",
|
||||
ExpectedErr: "already exists",
|
||||
},
|
||||
{
|
||||
Query: "DROP TABLE test;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "CREATE TRIGGER test_trigger2 BEFORE UPDATE ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "WHEN on BEFORE INSERT",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func1() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.pk::text || '_' || NEW.v1;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE FUNCTION trigger_func2() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_' || NEW.pk::text;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger1 BEFORE INSERT ON test FOR EACH ROW WHEN (NEW.pk < 1) EXECUTE FUNCTION trigger_func1();`,
|
||||
`CREATE TRIGGER test_trigger2 BEFORE INSERT ON test FOR EACH ROW WHEN (NEW.pk > 1) EXECUTE FUNCTION trigger_func2();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO test VALUES (0, 'hi'), (1, 'there'), (2, 'dude');",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "SELECT * FROM test;",
|
||||
Expected: []sql.Row{
|
||||
{0, "0_hi"},
|
||||
{1, "there"},
|
||||
{2, "dude_2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "WHEN with non-boolean expression",
|
||||
SetUpScript: []string{
|
||||
"CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);",
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.pk::text || '_' || NEW.v1;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW WHEN (NEW.pk + 1) EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "INSERT INTO test VALUES (1, 'hi'), (2, 'there');",
|
||||
ExpectedErr: "argument of WHEN must be type boolean",
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "Table as type",
|
||||
Skip: true, // TODO: figure out why this is not recognizing rec.qty as valid
|
||||
SetUpScript: []string{
|
||||
`CREATE TABLE test (id INT4 PRIMARY KEY, name TEXT NOT NULL, qty INT4 NOT NULL, price REAL NOT NULL);`,
|
||||
`CREATE FUNCTION trigger_func() RETURNS trigger AS $$
|
||||
DECLARE
|
||||
rec test;
|
||||
BEGIN
|
||||
rec := NEW;
|
||||
IF rec.qty < 0 THEN
|
||||
rec.qty := -rec.qty;
|
||||
END IF;
|
||||
NEW := rec;
|
||||
RETURN NEW;
|
||||
END; $$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: `INSERT INTO test VALUES (1, 'apple', 3, 2.5), (2, 'banana', -5, -1.2);`,
|
||||
Expected: []sql.Row{},
|
||||
}, {
|
||||
Query: `SELECT * FROM test;`,
|
||||
Expected: []sql.Row{
|
||||
{1, "apple", 3, 2.5},
|
||||
{2, "banana", 5, -1.2},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "trigger to call procedure that updates another table using dynamic execute",
|
||||
SetUpScript: []string{
|
||||
`create table public."Collections"(
|
||||
id uuid PRIMARY KEY NOT NULL,
|
||||
name text not null,
|
||||
username varchar(28) not null,
|
||||
total_tracks integer DEFAULT 0);`,
|
||||
`INSERT INTO public."Collections" (id, name, username, total_tracks) VALUES ('550e8400-e29b-41d4-a716-446655440000', 'My Custom Playlist', 'user_alpha', 10);`,
|
||||
`create table public."CollectionItems"(
|
||||
collection_id uuid not null,
|
||||
track_id integer not null);`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: `CREATE OR REPLACE FUNCTION update_collections()
|
||||
RETURNS trigger AS $$
|
||||
DECLARE
|
||||
BEGIN
|
||||
IF TG_OP = 'INSERT' THEN
|
||||
EXECUTE 'update public."Collections" set total_tracks=total_tracks+1 where id = $1;'
|
||||
USING NEW.collection_id;
|
||||
END IF;
|
||||
|
||||
IF TG_OP = 'DELETE' THEN
|
||||
EXECUTE 'update public."Collections" set total_tracks=total_tracks-1 where id = $1;'
|
||||
USING OLD.collection_id;
|
||||
END IF;
|
||||
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: `CREATE TRIGGER update_collection
|
||||
AFTER INSERT OR DELETE ON public."CollectionItems"
|
||||
FOR EACH ROW EXECUTE PROCEDURE update_collections();`,
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: `INSERT INTO public."CollectionItems" (collection_id, track_id) VALUES ('550e8400-e29b-41d4-a716-446655440000', 101);`,
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: `SELECT total_tracks FROM public."Collections"`,
|
||||
Expected: []sql.Row{{11}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "DROP TRIGGER",
|
||||
SetUpScript: []string{
|
||||
`CREATE TABLE test (pk INT PRIMARY KEY, v1 TEXT);`,
|
||||
`CREATE FUNCTION trigger_func() RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
NEW.v1 := NEW.v1 || '_' || NEW.pk::text;
|
||||
RETURN NEW;
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;`,
|
||||
`CREATE TRIGGER test_trigger BEFORE INSERT ON test FOR EACH ROW EXECUTE FUNCTION trigger_func();`,
|
||||
},
|
||||
Assertions: []ScriptTestAssertion{
|
||||
{
|
||||
Query: "DROP TRIGGER test_trigger ON test;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
{
|
||||
Query: "DROP TRIGGER IF EXISTS test_trigger ON test;",
|
||||
Expected: []sql.Row{},
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user