diff --git a/src/box/sql.c b/src/box/sql.c index d48c3cfe50add9c6626abeb56d5f06c5d08e7667..2b93c3d4502588d823fb5311bd5dd2cc84a0d7fb 100644 --- a/src/box/sql.c +++ b/src/box/sql.c @@ -1118,12 +1118,21 @@ cursor_seek(BtCursor *pCur, int *pRes) return SQL_TARANTOOL_ITERATOR_FAIL; } - struct iterator *it = index_create_iterator(pCur->index, pCur->iter_type, - key, part_count); + struct space *space = pCur->space; + struct txn *txn = NULL; + if (space->def->id != 0 && txn_begin_ro_stmt(space, &txn) != 0) + return SQL_TARANTOOL_ERROR; + struct iterator *it = + index_create_iterator(pCur->index, pCur->iter_type, key, + part_count); if (it == NULL) { + if (txn != NULL) + txn_rollback_stmt(); pCur->eState = CURSOR_INVALID; return SQL_TARANTOOL_ITERATOR_FAIL; } + if (txn != NULL) + txn_commit_ro_stmt(txn); pCur->iter = it; pCur->eState = CURSOR_VALID; diff --git a/test/sql/triggers.result b/test/sql/triggers.result index dc0a2e57d60d8f4a79487b93d475c2d326392fe1..658571b50844beed66cea12ab2e739b896c7faaf 100644 --- a/test/sql/triggers.result +++ b/test/sql/triggers.result @@ -246,3 +246,113 @@ box.sql.execute("DROP VIEW V1;") box.sql.execute("DROP TABLE T1;") --- ... +-- +-- gh-3531: Assertion with trigger and two storage engines +-- +-- Case 1: Src 'vinyl' table; Dst 'memtx' table +box.sql.execute("PRAGMA sql_default_engine ('vinyl');") +--- +... +box.sql.execute("CREATE TABLE m (s1 SCALAR PRIMARY KEY);") +--- +... +box.sql.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = DATETIME('now'); END;") +--- +... +box.sql.execute("PRAGMA sql_default_engine('memtx');") +--- +... +box.sql.execute("CREATE TABLE n (s1 CHAR PRIMARY KEY, s2 char);") +--- +... +box.sql.execute("INSERT INTO m VALUES ('');") +--- +... +box.sql.execute("INSERT INTO n VALUES ('',null);") +--- +... +box.sql.execute("UPDATE m SET s1 = 'The Rain In Spain';") +--- +- error: A multi-statement transaction can not use multiple storage engines +... +-- ANALYZE operates with _sql_stat{1,4} tables should work +box.sql.execute("ANALYZE m;") +--- +... +box.sql.execute("DROP TABLE m;") +--- +... +box.sql.execute("DROP TABLE n;") +--- +... +-- Case 2: Src 'memtx' table; Dst 'vinyl' table +box.sql.execute("PRAGMA sql_default_engine ('memtx');") +--- +... +box.sql.execute("CREATE TABLE m (s1 SCALAR PRIMARY KEY);") +--- +... +box.sql.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = DATETIME('now'); END;") +--- +... +box.sql.execute("PRAGMA sql_default_engine('vinyl');") +--- +... +box.sql.execute("CREATE TABLE n (s1 CHAR PRIMARY KEY, s2 char);") +--- +... +box.sql.execute("INSERT INTO m VALUES ('');") +--- +... +box.sql.execute("INSERT INTO n VALUES ('',null);") +--- +... +box.sql.execute("UPDATE m SET s1 = 'The Rain In Spain';") +--- +- error: A multi-statement transaction can not use multiple storage engines +... +-- ANALYZE operates with _sql_stat{1,4} tables should work +box.sql.execute("ANALYZE n;") +--- +... +box.sql.execute("DROP TABLE m;") +--- +... +box.sql.execute("DROP TABLE n;") +--- +... +-- Test SQL Transaction with LUA +box.sql.execute("PRAGMA sql_default_engine ('memtx');") +--- +... +box.sql.execute("CREATE TABLE test (id INT PRIMARY KEY)") +--- +... +box.sql.execute("PRAGMA sql_default_engine='vinyl'") +--- +... +box.sql.execute("CREATE TABLE test2 (id INT PRIMARY KEY)") +--- +... +box.sql.execute("INSERT INTO test2 VALUES (2)") +--- +... +box.sql.execute("START TRANSACTION") +--- +... +box.sql.execute("INSERT INTO test VALUES (1)") +--- +... +box.sql.execute("SELECT * FROM test2") +--- +- error: A multi-statement transaction can not use multiple storage engines +... +box.sql.execute("ROLLBACK;") +--- +... +box.sql.execute("DROP TABLE test;") +--- +... +box.sql.execute("DROP TABLE test2;") +--- +... diff --git a/test/sql/triggers.test.lua b/test/sql/triggers.test.lua index e019c008ca00725383b6ba350c9a1a4852c46c9e..8fd385cd38b6ce9fdbe534b0631b0e9da0298fd5 100644 --- a/test/sql/triggers.test.lua +++ b/test/sql/triggers.test.lua @@ -95,3 +95,50 @@ box.space._trigger:insert(tuple) box.sql.execute("DROP VIEW V1;") box.sql.execute("DROP TABLE T1;") + +-- +-- gh-3531: Assertion with trigger and two storage engines +-- +-- Case 1: Src 'vinyl' table; Dst 'memtx' table +box.sql.execute("PRAGMA sql_default_engine ('vinyl');") +box.sql.execute("CREATE TABLE m (s1 SCALAR PRIMARY KEY);") +box.sql.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = DATETIME('now'); END;") +box.sql.execute("PRAGMA sql_default_engine('memtx');") +box.sql.execute("CREATE TABLE n (s1 CHAR PRIMARY KEY, s2 char);") +box.sql.execute("INSERT INTO m VALUES ('');") +box.sql.execute("INSERT INTO n VALUES ('',null);") +box.sql.execute("UPDATE m SET s1 = 'The Rain In Spain';") + +-- ANALYZE operates with _sql_stat{1,4} tables should work +box.sql.execute("ANALYZE m;") +box.sql.execute("DROP TABLE m;") +box.sql.execute("DROP TABLE n;") + + +-- Case 2: Src 'memtx' table; Dst 'vinyl' table +box.sql.execute("PRAGMA sql_default_engine ('memtx');") +box.sql.execute("CREATE TABLE m (s1 SCALAR PRIMARY KEY);") +box.sql.execute("CREATE TRIGGER m1 BEFORE UPDATE ON m FOR EACH ROW BEGIN UPDATE n SET s2 = DATETIME('now'); END;") +box.sql.execute("PRAGMA sql_default_engine('vinyl');") +box.sql.execute("CREATE TABLE n (s1 CHAR PRIMARY KEY, s2 char);") +box.sql.execute("INSERT INTO m VALUES ('');") +box.sql.execute("INSERT INTO n VALUES ('',null);") +box.sql.execute("UPDATE m SET s1 = 'The Rain In Spain';") + +-- ANALYZE operates with _sql_stat{1,4} tables should work +box.sql.execute("ANALYZE n;") +box.sql.execute("DROP TABLE m;") +box.sql.execute("DROP TABLE n;") + +-- Test SQL Transaction with LUA +box.sql.execute("PRAGMA sql_default_engine ('memtx');") +box.sql.execute("CREATE TABLE test (id INT PRIMARY KEY)") +box.sql.execute("PRAGMA sql_default_engine='vinyl'") +box.sql.execute("CREATE TABLE test2 (id INT PRIMARY KEY)") +box.sql.execute("INSERT INTO test2 VALUES (2)") +box.sql.execute("START TRANSACTION") +box.sql.execute("INSERT INTO test VALUES (1)") +box.sql.execute("SELECT * FROM test2") +box.sql.execute("ROLLBACK;") +box.sql.execute("DROP TABLE test;") +box.sql.execute("DROP TABLE test2;")