commit - 774b84d7e6f3d61000d64bbf91dd9b0784f30f5c
commit + 280ede938294bba45b4157add8ea8b08a29ab373
blob - /dev/null
blob + 65c81c27a0c2fa023348db25103b81f981f243d4 (mode 644)
--- /dev/null
+++ changelogs/unreleased/gh-10396-memtx-mvcc-exclude-null-count.md
+## bugfix/memtx
+
+* Fixed a bug when `index:count()` could return a wrong number, raise the
+ last error, or fail with the `IllegalParams` error if the index has
+ the `exclude_null` attribute and MVCC is enabled (gh-10396).
blob - e3bce440c5287c1432d1df2c13dc517d8b2e064f
blob + af94b66b67f960dcabbfcaef25180f51de8681d5
--- src/box/memtx_tx.c
+++ src/box/memtx_tx.c
}
assert(link->newer_story == NULL);
+ /*
+ * Excluded tuples have their own chains consisting of the only
+ * excluded story. Such stories must be skipped since they are
+ * not actually inserted to index.
+ */
+ if (tuple_key_is_excluded(story->tuple, index->def->key_def,
+ MULTIKEY_NONE)) {
+ assert(link->older_story == NULL);
+ continue;
+ }
+
struct tuple *visible = NULL;
bool is_prepared_ok = detect_whether_prepared_ok(txn);
bool unused;
blob - cdb98285daf6f6afafee5ef162293db3c9224ce6
blob + 94c8ee66cde9dba2b145218c30b071854fe7d4e2
--- test/box-luatest/gh_9954_mvcc_with_exclude_null_test.lua
+++ test/box-luatest/gh_9954_mvcc_with_exclude_null_test.lua
box.space.test:drop()
end)
end
+
+-- gh-10396
+g.test_mvcc_with_exclude_null_count = function()
+ g.server:exec(function()
+ local space = box.schema.space.create("TEST", {
+ format = {
+ {name = "ID", type = "unsigned", is_nullable = false},
+ {name = "FLAG", type = "boolean", is_nullable = true},
+ },
+ })
+ space:create_index("ID", {
+ unique = true,
+ type = "TREE",
+ parts = {{field = "ID", type = "unsigned"}},
+ })
+ space:create_index("FLAG", {
+ unique = false,
+ type = "TREE",
+ parts = {{
+ field = "FLAG",
+ type = "boolean",
+ exclude_null = true,
+ is_nullable = true,
+ }},
+ })
+
+ -- Wrap into transaction so that stories won't be deleted
+ box.begin()
+ -- Insert excluded tuples and then replace half of them
+ -- with non-excluded ones
+ for i = 1, 100 do
+ space:replace{i, box.NULL}
+ end
+ for i = 1, 100, 2 do
+ space:replace{i, true}
+ end
+
+ -- Insert non-excluded tuples and then replace half of them
+ -- with excluded ones
+ for i = 101, 150 do
+ space:replace{i, false}
+ end
+ for i = 101, 150, 2 do
+ space:replace{i, box.NULL}
+ end
+ -- Check if count works correctly
+ t.assert_equals(space.index.FLAG:count({}, {iterator = 'ALL'}), 75)
+ box.commit()
+ end)
+end