From d2a6f64e825712145a28e566502f9b0d1d13c399 Mon Sep 17 00:00:00 2001
From: Alexandr Lyapunov <aleks@tarantool.org>
Date: Fri, 25 Nov 2016 18:19:59 +0300
Subject: [PATCH] bitset: fix crash and harmonise s:count() with #s:select{}

Fixes #1946
---
 src/box/memtx_bitset.cc   |  5 ++---
 src/lib/bitset/iterator.c |  2 +-
 test/box/bitset.result    | 46 ++++++++++++++++++++++++++++++++++++++-
 test/box/bitset.test.lua  | 16 ++++++++++++++
 4 files changed, 64 insertions(+), 5 deletions(-)

diff --git a/src/box/memtx_bitset.cc b/src/box/memtx_bitset.cc
index 89e7cbe01e..41d4a61963 100644
--- a/src/box/memtx_bitset.cc
+++ b/src/box/memtx_bitset.cc
@@ -419,13 +419,12 @@ MemtxBitset::count(enum iterator_type type, const char *key,
 			return bitset_index_count(&m_index, bit);
 	} else if (type == ITER_BITS_ALL_SET) {
 		/**
-		 * Optimization: for an empty key return the number of items
-		 * in the index.
+		 * Optimization: for an empty key return 0.
 		 */
 		bit_iterator_init(&bit_it, bitset_key, bitset_key_size, true);
 		bit = bit_iterator_next(&bit_it);
 		if (bit == SIZE_MAX)
-			return bitset_index_size(&m_index);
+			return 0;
 		/**
 		 * Optimiation: for a single bit key use
 		 * bitset_index_count().
diff --git a/src/lib/bitset/iterator.c b/src/lib/bitset/iterator.c
index b9f7e41652..6c0a830458 100644
--- a/src/lib/bitset/iterator.c
+++ b/src/lib/bitset/iterator.c
@@ -208,13 +208,13 @@ bitset_iterator_init(struct bitset_iterator *it, struct bitset_expr *expr,
 	for (size_t c = 0; c < expr->size; c++) {
 		struct bitset_expr_conj *exconj = &expr->conjs[c];
 		struct bitset_iterator_conj *itconj = &it->conjs[c];
+		itconj->page_first_pos = 0;
 
 		if (bitset_iterator_conj_reserve(it, itconj, exconj->size) != 0)
 			return -1;
 
 		for (size_t b = 0; b < exconj->size; b++) {
 			assert(exconj->bitset_ids[b] < bitsets_size);
-			itconj->page_first_pos = 0;
 			assert(p_bitsets[exconj->bitset_ids[b]] != NULL);
 			itconj->bitsets[b] = p_bitsets[exconj->bitset_ids[b]];
 			itconj->pre_nots[b] = exconj->pre_nots[b];
diff --git a/test/box/bitset.result b/test/box/bitset.result
index 444190314b..7d2f1feb43 100644
--- a/test/box/bitset.result
+++ b/test/box/bitset.result
@@ -167,7 +167,7 @@ dump(box.index.BITS_ALL_SET, 0)
 ...
 box.space.tweedledum.index.bitset:count(0, { iterator = box.index.BITS_ALL_SET})
 ---
-- 128
+- 0
 ...
 dump(box.index.BITS_ALL_SET, 1)
 ---
@@ -1940,3 +1940,47 @@ s:drop()
 s = nil
 ---
 ...
+-- https://github.com/tarantool/tarantool/issues/1946 BITS_ALL_SET crashes
+s = box.schema.space.create('test')
+---
+...
+_ = s:create_index('primary', { type = 'hash', parts = {1, 'num'}, unique = true })
+---
+...
+i = s:create_index('bitset', { type = 'bitset', parts = {2, 'num'}, unique = false })
+---
+...
+for i=1,10 do s:insert{i, math.random(8)} end
+---
+...
+good = true
+---
+...
+function is_good(key, opts) return #i:select({key}, opts) == i:count({key}, opts) end
+---
+...
+function check(key, opts) good = good and is_good(key, opts) end
+---
+...
+for j=1,100 do check(math.random(9) - 1) end
+---
+...
+for j=1,100 do check(math.random(9) - 1, {iterator = box.index.BITS_ANY_SET}) end
+---
+...
+for j=1,100 do check(math.random(9) - 1, {iterator = box.index.BITS_ALL_SET}) end
+---
+...
+for j=1,100 do check(math.random(9) - 1, {iterator = box.index.BITS_ALL_NOT_SET}) end
+---
+...
+good
+---
+- true
+...
+s:drop()
+---
+...
+s = nil
+---
+...
diff --git a/test/box/bitset.test.lua b/test/box/bitset.test.lua
index 656f311c92..850f056d29 100644
--- a/test/box/bitset.test.lua
+++ b/test/box/bitset.test.lua
@@ -132,3 +132,19 @@ s:insert{9, 8}
 i:count(7, {iterator = box.index.BITS_ANY_SET})
 s:drop()
 s = nil
+
+-- https://github.com/tarantool/tarantool/issues/1946 BITS_ALL_SET crashes
+s = box.schema.space.create('test')
+_ = s:create_index('primary', { type = 'hash', parts = {1, 'num'}, unique = true })
+i = s:create_index('bitset', { type = 'bitset', parts = {2, 'num'}, unique = false })
+for i=1,10 do s:insert{i, math.random(8)} end
+good = true
+function is_good(key, opts) return #i:select({key}, opts) == i:count({key}, opts) end
+function check(key, opts) good = good and is_good(key, opts) end
+for j=1,100 do check(math.random(9) - 1) end
+for j=1,100 do check(math.random(9) - 1, {iterator = box.index.BITS_ANY_SET}) end
+for j=1,100 do check(math.random(9) - 1, {iterator = box.index.BITS_ALL_SET}) end
+for j=1,100 do check(math.random(9) - 1, {iterator = box.index.BITS_ALL_NOT_SET}) end
+good
+s:drop()
+s = nil
-- 
GitLab