diff --git a/src/box/sql/delete.c b/src/box/sql/delete.c index 87d4ed4f41d548e58fef73f3754647ba81fcba0d..f4d0334f445f6c59d759fd4535dfd091c607a21d 100644 --- a/src/box/sql/delete.c +++ b/src/box/sql/delete.c @@ -53,6 +53,12 @@ sql_lookup_space(struct Parse *parse, struct SrcList_item *space_name) parse->is_aborted = true; return NULL; } + if (space->index_count == 0 && !space->def->opts.is_view) { + diag_set(ClientError, ER_UNSUPPORTED, "SQL", + "spaces without primary key"); + parse->is_aborted = true; + return NULL; + } space_name->space = space; if (sqlIndexedByLookup(parse, space_name) != 0) space = NULL; diff --git a/test/sql/errinj.result b/test/sql/errinj.result index 49e71ff065f240f5cba723d4d67636df51b14d0d..a1e7cc4a38ae193c89dd8d9c1dfebb7806cf17ce 100644 --- a/test/sql/errinj.result +++ b/test/sql/errinj.result @@ -340,3 +340,51 @@ errinj.set("ERRINJ_WAL_IO", false) box.sql.execute("DROP TABLE t3;") --- ... +-- gh-3780: space without PK raises error if +-- it is used in SQL queries. +-- +errinj = box.error.injection +--- +... +fiber = require('fiber') +--- +... +box.sql.execute("CREATE TABLE t (id INT PRIMARY KEY);") +--- +... +box.sql.execute("INSERT INTO t VALUES (1);") +--- +... +errinj.set("ERRINJ_WAL_DELAY", true) +--- +- ok +... +-- DROP TABLE consists of several steps: firstly indexes +-- are deleted, then space itself. Lets make sure that if +-- first part of drop is successfully finished, but resulted +-- in yield, all operations on space will be blocked due to +-- absence of primary key. +-- +function drop_table_yield() box.sql.execute("DROP TABLE t;") end +--- +... +f = fiber.create(drop_table_yield) +--- +... +box.sql.execute("SELECT * FROM t;") +--- +- error: SQL does not support spaces without primary key +... +box.sql.execute("INSERT INTO t VALUES (2);") +--- +- error: SQL does not support spaces without primary key +... +box.sql.execute("UPDATE t SET id = 2;") +--- +- error: SQL does not support spaces without primary key +... +-- Finish drop space. +errinj.set("ERRINJ_WAL_DELAY", false) +--- +- ok +... diff --git a/test/sql/errinj.test.lua b/test/sql/errinj.test.lua index 80c8fde26690c5da406ecb1d85d14e55e9dfb34a..d8833feb447259eddb9e089fc77cf434f6c53ca5 100644 --- a/test/sql/errinj.test.lua +++ b/test/sql/errinj.test.lua @@ -117,3 +117,25 @@ box.sql.execute("ALTER TABLE t3 DROP CONSTRAINT fk1;") box.sql.execute("INSERT INTO t3 VALUES(1, 1, 3);") errinj.set("ERRINJ_WAL_IO", false) box.sql.execute("DROP TABLE t3;") + +-- gh-3780: space without PK raises error if +-- it is used in SQL queries. +-- +errinj = box.error.injection +fiber = require('fiber') +box.sql.execute("CREATE TABLE t (id INT PRIMARY KEY);") +box.sql.execute("INSERT INTO t VALUES (1);") +errinj.set("ERRINJ_WAL_DELAY", true) +-- DROP TABLE consists of several steps: firstly indexes +-- are deleted, then space itself. Lets make sure that if +-- first part of drop is successfully finished, but resulted +-- in yield, all operations on space will be blocked due to +-- absence of primary key. +-- +function drop_table_yield() box.sql.execute("DROP TABLE t;") end +f = fiber.create(drop_table_yield) +box.sql.execute("SELECT * FROM t;") +box.sql.execute("INSERT INTO t VALUES (2);") +box.sql.execute("UPDATE t SET id = 2;") +-- Finish drop space. +errinj.set("ERRINJ_WAL_DELAY", false) diff --git a/test/sql/no-pk-space.result b/test/sql/no-pk-space.result new file mode 100644 index 0000000000000000000000000000000000000000..1d57d16879158b16a5b09c3afc336e40936db752 --- /dev/null +++ b/test/sql/no-pk-space.result @@ -0,0 +1,61 @@ +test_run = require('test_run').new() +--- +... +engine = test_run:get_cfg('engine') +--- +... +box.sql.execute('pragma sql_default_engine=\''..engine..'\'') +--- +... +format = {} +--- +... +format[1] = {'id', 'integer'} +--- +... +s = box.schema.create_space('test', {format = format}) +--- +... +box.sql.execute("SELECT * FROM \"test\";") +--- +- error: SQL does not support spaces without primary key +... +box.sql.execute("INSERT INTO \"test\" VALUES (1);") +--- +- error: SQL does not support spaces without primary key +... +box.sql.execute("DELETE FROM \"test\";") +--- +- error: SQL does not support spaces without primary key +... +box.sql.execute("UPDATE \"test\" SET id = 3;") +--- +- error: SQL does not support spaces without primary key +... +s:drop() +--- +... +-- Notorious artefact: check of view referencing counter occurs +-- after drop of indexes. So, if space:drop() fails due to being +-- referenced by a view, space becomes unusable in SQL terms. +-- +box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY);") +--- +... +box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;") +--- +... +box.space.T1:drop() +--- +- error: 'Can''t drop space ''T1'': other views depend on this space' +... +box.sql.execute("SELECT * FROM v1;") +--- +- error: SQL does not support spaces without primary key +... +box.space.V1:drop() +--- +... +box.space.T1:drop() +--- +... diff --git a/test/sql/no-pk-space.test.lua b/test/sql/no-pk-space.test.lua new file mode 100644 index 0000000000000000000000000000000000000000..2828777f7d3dcf9eec34e3318390d6a78e102eaf --- /dev/null +++ b/test/sql/no-pk-space.test.lua @@ -0,0 +1,24 @@ +test_run = require('test_run').new() +engine = test_run:get_cfg('engine') +box.sql.execute('pragma sql_default_engine=\''..engine..'\'') + +format = {} +format[1] = {'id', 'integer'} +s = box.schema.create_space('test', {format = format}) +box.sql.execute("SELECT * FROM \"test\";") +box.sql.execute("INSERT INTO \"test\" VALUES (1);") +box.sql.execute("DELETE FROM \"test\";") +box.sql.execute("UPDATE \"test\" SET id = 3;") + +s:drop() + +-- Notorious artefact: check of view referencing counter occurs +-- after drop of indexes. So, if space:drop() fails due to being +-- referenced by a view, space becomes unusable in SQL terms. +-- +box.sql.execute("CREATE TABLE t1 (id INT PRIMARY KEY);") +box.sql.execute("CREATE VIEW v1 AS SELECT * FROM t1;") +box.space.T1:drop() +box.sql.execute("SELECT * FROM v1;") +box.space.V1:drop() +box.space.T1:drop()