diff options
author | Chenhao Qu <chenhao.qu@mongodb.com> | 2022-11-09 12:26:26 +1100 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-11-09 02:47:22 +0000 |
commit | 04be3d51c64f804421f5bca6dd05c8fe64d898d2 (patch) | |
tree | 773867f9e2830347f19fb6fe21851e66cf848834 /src/third_party/wiredtiger | |
parent | a197f7350ddaaac6bb30309469c03593e97c3835 (diff) | |
download | mongo-04be3d51c64f804421f5bca6dd05c8fe64d898d2.tar.gz |
Import wiredtiger: 81fd744316f24372692f429a5ac9c2326e74b28f from branch mongodb-master
ref: ae5ba8ffbd..81fd744316
for: 6.2.0-rc0
WT-9365 Implement cursor bound logic for WT indexes
Diffstat (limited to 'src/third_party/wiredtiger')
10 files changed, 508 insertions, 32 deletions
diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data index 7ee04c686fa..e3453ddcc8a 100644 --- a/src/third_party/wiredtiger/import.data +++ b/src/third_party/wiredtiger/import.data @@ -2,5 +2,5 @@ "vendor": "wiredtiger", "github": "wiredtiger/wiredtiger.git", "branch": "mongodb-master", - "commit": "ae5ba8ffbdf64ac06c3bf34137f7a8aee11d3577" + "commit": "81fd744316f24372692f429a5ac9c2326e74b28f" } diff --git a/src/third_party/wiredtiger/src/cursor/cur_index.c b/src/third_party/wiredtiger/src/cursor/cur_index.c index 71e2deec9cf..91c9a423000 100644 --- a/src/third_party/wiredtiger/src/cursor/cur_index.c +++ b/src/third_party/wiredtiger/src/cursor/cur_index.c @@ -203,6 +203,13 @@ __curindex_reset(WT_CURSOR *cursor) WT_TRET((*cp)->reset(*cp)); } + /* + * The bounded cursor API clears bounds on external calls to cursor->reset. We determine this by + * guarding the call to cursor bound reset with the API_USER_ENTRY macro. Doing so prevents + * internal API calls from resetting cursor bounds unintentionally, e.g. cursor->remove. + */ + if (API_USER_ENTRY(session)) + __wt_cursor_bound_reset(cindex->child); err: API_END_RET(session, ret); } @@ -348,6 +355,40 @@ err: } /* + * __increment_bound_array -- + * Increment the given buffer by one bit, return true if we incremented the buffer or not. If + * all of the values inside the buffer are UINT8_MAX value we do not increment the buffer. + */ +static inline bool +__increment_bound_array(WT_ITEM *user_item) +{ + size_t usz, i; + uint8_t *userp; + + usz = user_item->size; + userp = (uint8_t *)user_item->data; + /* + * First loop through all max values on the buffer from the end. This is to find the appropriate + * position to increment add one to the byte. + */ + for (i = usz - 1; i > 0 && userp[i] == UINT8_MAX; --i) + ; + + /* + * If all of the buffer are max values, we don't need to do increment the buffer as the key + * format is a fixed length format. Ideally we double check that the table format has a fixed + * length string. + */ + if (i == 0 && userp[i] == UINT8_MAX) + return (false); + + userp[i++] += 1; + for (; i < usz; ++i) + userp[i] = 0; + return (true); +} + +/* * __curindex_bound -- * WT_CURSOR->bound method for the index cursor type. * @@ -355,19 +396,78 @@ err: static int __curindex_bound(WT_CURSOR *cursor, const char *config) { + WT_CONFIG_ITEM cval; WT_CURSOR *child; + WT_CURSOR_BOUNDS_STATE saved_bounds; WT_CURSOR_INDEX *cindex; WT_DECL_RET; WT_SESSION_IMPL *session; + bool inclusive; cindex = (WT_CURSOR_INDEX *)cursor; - JOINABLE_CURSOR_API_CALL(cursor, session, bound, NULL); - - /* Grab the primary cursor and call bound function. */ - cindex = (WT_CURSOR_INDEX *)cursor; child = cindex->child; + WT_CLEAR(saved_bounds); + inclusive = false; + + JOINABLE_CURSOR_API_CALL_CONF(cursor, session, bound, config, cfg, NULL); + + /* Save the current state of the bounds in case we fail to apply the new state. */ + WT_ERR(__wt_cursor_bounds_save(session, child, &saved_bounds)); + + WT_ERR(__wt_config_gets(session, cfg, "action", &cval)); + + /* When setting bounds, we need to check that the key is set. */ + if (WT_STRING_MATCH("set", cval.str, cval.len)) { + WT_ERR(__cursor_checkkey(cursor)); + + /* Point the public cursor to the key in the child. */ + __wt_cursor_set_raw_key(child, &cursor->key); + + WT_ERR(__wt_config_gets(session, cfg, "inclusive", &cval)); + inclusive = cval.val != 0; + + /* Check if we have set the lower bound or upper bound. */ + WT_ERR(__wt_config_gets(session, cfg, "bound", &cval)); + } + WT_ERR(child->bound(child, config)); + + /* + * Index tables internally combines the user chosen columns with the key format of the table to + * maintain uniqueness between each key. However user's are not aware of the combining the key + * format and cannot set bounds based on the combined index format. Therefore WiredTiger needs + * to internally fix this by incrementing one bit to the array in two cases: + * 1. If the set bound is lower and it is not inclusive. + * 2. If the set bound is upper and it is inclusive. + */ + if (WT_STRING_MATCH("lower", cval.str, cval.len) && !inclusive) { + /* + * In the case that we can't increment the lower bound, it means we have reached the max + * possible key for the lower bound. This is a very tricky case since there isn't a trivial + * way to set the lower bound to a key exclusively not show the max possible key. This is + * due to how index key formats are combined with the main table's key format. In this edge + * case we expect no entries to be returned, thus we return it back to the user with an + * error instead. + */ + if (!__increment_bound_array(&child->lower_bound)) { + WT_ERR(__wt_cursor_bounds_restore(session, child, &saved_bounds)); + WT_ERR_MSG(session, EINVAL, + "Cannot set index cursors with the max possible key as the lower bound"); + } + } + + if (WT_STRING_MATCH("upper", cval.str, cval.len) && inclusive) { + /* + * In the case that we can't increment the upper bound, it means we have reached the max + * possible key for the upper bound. In that case we can just clear upper bound. + */ + if (!__increment_bound_array(&child->upper_bound)) + WT_ERR(child->bound(child, "action=clear,bound=upper")); + } err: + + __wt_scr_free(session, &saved_bounds.lower_bound); + __wt_scr_free(session, &saved_bounds.upper_bound); API_END_RET(session, ret); } diff --git a/src/third_party/wiredtiger/src/docs/cursor-ops.dox b/src/third_party/wiredtiger/src/docs/cursor-ops.dox index 3110eec75e8..70a962c5284 100644 --- a/src/third_party/wiredtiger/src/docs/cursor-ops.dox +++ b/src/third_party/wiredtiger/src/docs/cursor-ops.dox @@ -198,5 +198,5 @@ The bounds API supports these cursor types: - Table cursor - Datastore cursor - Dump cursor - +- Index cursor */ diff --git a/src/third_party/wiredtiger/src/include/api.h b/src/third_party/wiredtiger/src/include/api.h index b34d41a8167..07368912f67 100644 --- a/src/third_party/wiredtiger/src/include/api.h +++ b/src/third_party/wiredtiger/src/include/api.h @@ -261,6 +261,10 @@ CURSOR_API_CALL(cur, s, n, bt); \ JOINABLE_CURSOR_CALL_CHECK(cur) +#define JOINABLE_CURSOR_API_CALL_CONF(cur, s, n, config, cfg, bt) \ + CURSOR_API_CALL_CONF(cur, s, n, config, cfg, bt); \ + JOINABLE_CURSOR_CALL_CHECK(cur) + #define JOINABLE_CURSOR_API_CALL_PREPARE_ALLOWED(cur, s, n, bt) \ CURSOR_API_CALL_PREPARE_ALLOWED(cur, s, n, bt); \ JOINABLE_CURSOR_CALL_CHECK(cur) diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound01.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound01.py index 5a629e5cc3f..f95c64c6ca6 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound01.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound01.py @@ -40,8 +40,7 @@ class test_cursor_bound01(bound_base): ('table', dict(uri='table:', use_index = False, use_colgroup = False)), ('lsm', dict(uri='lsm:', use_index = False, use_colgroup = False)), ('colgroup', dict(uri='table:', use_index = False, use_colgroup = False)), - #FIXME: Turn on once index cursor bound implementation is done. - #('index', dict(uri='table:', use_index = True)), + ('index', dict(uri='table:', use_index = True, use_colgroup = False)), ] format_values = [ @@ -94,14 +93,25 @@ class test_cursor_bound01(bound_base): '/Invalid argument/') # Check that bound configuration works properly. - cursor.set_key(self.gen_key(1)) - cursor.bound("action=set,bound=lower") - cursor.set_key(self.gen_key(10)) - cursor.bound("action=set,bound=upper") + if (self.use_index): + cursor.set_key(self.gen_val(1)) + cursor.bound("bound=lower") + cursor.set_key(self.gen_val(10)) + cursor.bound("bound=upper") + else: + cursor.set_key(self.gen_key(1)) + cursor.bound("bound=lower") + cursor.set_key(self.gen_key(10)) + cursor.bound("bound=upper") # Check that clear works properly. cursor.bound("action=clear") + # Index cursors work slightly differently to other cursors, we can early exit here as the + # below edge cases don't apply for index cursors. + if (self.use_index): + return + # Check that largest key doesn't work with bounded cursors. cursor.set_key(self.gen_key(1)) cursor.bound("action=set,bound=lower") diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound02.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound02.py index 444e0bb86ec..99beda152f4 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound02.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound02.py @@ -68,7 +68,7 @@ class test_cursor_bound02(bound_base): uri = self.uri + self.file_name create_params = 'value_format={},key_format={}'.format(self.value_format, self.key_format) if self.use_colgroup: - create_params += self.gen_colgroup_create_param() + create_params += self.gen_create_param() self.session.create(uri, create_params) # Add in column groups. @@ -141,7 +141,7 @@ class test_cursor_bound02(bound_base): uri = self.uri + self.file_name create_params = 'value_format={},key_format={}'.format(self.value_format, self.key_format) if self.use_colgroup: - create_params += self.gen_colgroup_create_param() + create_params += self.gen_create_param() self.session.create(uri, create_params) # Add in column groups. if self.use_colgroup: @@ -194,7 +194,7 @@ class test_cursor_bound02(bound_base): uri = self.uri + self.file_name create_params = 'value_format={},key_format={}'.format(self.value_format, self.key_format) if self.use_colgroup: - create_params += self.gen_colgroup_create_param() + create_params += self.gen_create_param() self.session.create(uri, create_params) # Add in column groups. if self.use_colgroup: diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound06.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound06.py index f8373182379..4350a15af46 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound06.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound06.py @@ -62,7 +62,8 @@ class test_cursor_bound06(bound_base): ] config = [ - ('no-evict', dict(evict=False)) + ('no-evict', dict(evict=False)), + ('evict', dict(evict=True)) ] scenarios = make_scenarios(types, key_formats, value_formats, inclusive, config) diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound19.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound19.py new file mode 100644 index 00000000000..0121e3adc65 --- /dev/null +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound19.py @@ -0,0 +1,144 @@ +#!/usr/bin/env python +# +# Public Domain 2014-present MongoDB, Inc. +# Public Domain 2008-2014 WiredTiger, Inc. +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +import wiredtiger, wttest +from wtscenario import make_scenarios +from wtbound import bound_base + +# test_cursor_bound19.py +# Test basic cursor index bounds operations. To test index table formats the populate function +# has duplicate pair values for each key. This will construct an index table that needs to seperate +# the duplicate values. +class test_cursor_bound19(bound_base): + file_name = 'test_cursor_bound19' + use_index = True + + types = [ + ('table', dict(uri='table:', use_colgroup=False)), + ('colgroup', dict(uri='table:', use_colgroup=True)) + ] + + key_formats = [ + ('string', dict(key_format='S')), + ('var', dict(key_format='r')), + ('int', dict(key_format='i')), + ('bytes', dict(key_format='u')), + ('composite_string', dict(key_format='SSS')), + ('composite_int_string', dict(key_format='iS')), + ('composite_complex', dict(key_format='iSru')), + ] + + value_formats = [ + ('string', dict(value_format='S')), + ('int', dict(value_format='i')), + ('bytes', dict(value_format='u')), + ('composite_string', dict(value_format='SSS')), + ('composite_int_string', dict(value_format='iS')), + ('composite_complex', dict(value_format='iSru')), + ] + + config = [ + ('no-evict', dict(evict=False)), + ('evict', dict(evict=True)) + ] + + scenarios = make_scenarios(types, key_formats, value_formats, config) + + def test_cursor_index_bounds(self): + cursor = self.create_session_and_cursor() + cursor.close() + + # Test Index index_cursors bound API support. + suburi = "index:" + self.file_name + ":i0" + start = 0 + columns_param = "columns=(" + for _ in self.value_format: + columns_param += "v{0},".format(str(start)) + start += 1 + columns_param += ")" + self.session.create(suburi, columns_param) + + index_cursor = self.session.open_cursor("index:" + self.file_name + ":i0") + + # Set bounds at lower key 30 and upper key at 50. We have 22 entries here since we + # double entries for from 30 up until 41 (upper is set to inclusive). + self.set_bounds(index_cursor, 30, "lower") + self.set_bounds(index_cursor, 40, "upper") + self.cursor_traversal_bound(index_cursor, 30, 40, True, 22) + self.cursor_traversal_bound(index_cursor, 30, 40, False, 22) + + # Test basic search near scenarios. + index_cursor.set_key(self.gen_key(20)) + self.assertEqual(index_cursor.search_near(), 1) + self.assertEqual(index_cursor.get_key(), self.check_key(30)) + + index_cursor.set_key(self.gen_key(35)) + self.assertEqual(index_cursor.search_near(), 0) + self.assertEqual(index_cursor.get_key(), self.check_key(35)) + + index_cursor.set_key(self.gen_key(60)) + self.assertEqual(index_cursor.search_near(), -1) + self.assertEqual(index_cursor.get_key(), self.check_key(40)) + + # Test basic search scnarios. + index_cursor.set_key(self.gen_key(20)) + self.assertEqual(index_cursor.search(), wiredtiger.WT_NOTFOUND) + + index_cursor.set_key(self.gen_key(35)) + self.assertEqual(index_cursor.search(), 0) + + index_cursor.set_key(self.gen_key(50)) + self.assertEqual(index_cursor.search(), wiredtiger.WT_NOTFOUND) + + # Test that cursor resets the bounds. + self.assertEqual(index_cursor.reset(), 0) + self.cursor_traversal_bound(index_cursor, None, None, True, 60) + self.cursor_traversal_bound(index_cursor, None, None, False, 60) + + # Test that cursor action clear works and clears the bounds. + self.set_bounds(index_cursor, 30, "lower") + self.set_bounds(index_cursor, 50, "upper") + self.assertEqual(index_cursor.bound("action=clear"), 0) + self.cursor_traversal_bound(index_cursor, None, None, True, 60) + self.cursor_traversal_bound(index_cursor, None, None, False, 60) + + # Test special index case: Lower bound with exclusive + self.set_bounds(index_cursor, 30, "lower", False) + self.set_bounds(index_cursor, 40, "upper", True) + self.cursor_traversal_bound(index_cursor, 30, 40, True, 20) + self.cursor_traversal_bound(index_cursor, 30, 40, False, 20) + + index_cursor.set_key(self.gen_key(20)) + self.assertEqual(index_cursor.search_near(), 1) + self.assertEqual(index_cursor.get_key(), self.check_key(31)) + + index_cursor.set_key(self.gen_key(30)) + self.assertEqual(index_cursor.search(), wiredtiger.WT_NOTFOUND) + +if __name__ == '__main__': + wttest.run() diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound20.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound20.py new file mode 100644 index 00000000000..347007c5e2f --- /dev/null +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound20.py @@ -0,0 +1,186 @@ +#!/usr/bin/env python +# +# Public Domain 2014-present MongoDB, Inc. +# Public Domain 2008-2014 WiredTiger, Inc. +# +# This is free and unencumbered software released into the public domain. +# +# Anyone is free to copy, modify, publish, use, compile, sell, or +# distribute this software, either in source code form or as a compiled +# binary, for any purpose, commercial or non-commercial, and by any +# means. +# +# In jurisdictions that recognize copyright laws, the author or authors +# of this software dedicate any and all copyright interest in the +# software to the public domain. We make this dedication for the benefit +# of the public at large and to the detriment of our heirs and +# successors. We intend this dedication to be an overt act of +# relinquishment in perpetuity of all present and future rights to this +# software under copyright law. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +# OTHER DEALINGS IN THE SOFTWARE. + +import wiredtiger, wttest +from wtscenario import make_scenarios +from wtbound import bound_base + +# test_cursor_bound20.py +# Test special cursor index bounds case. This python test aims to test the edge cases of +# increment_bounds_array function. When max values are given as a key, it will not increment and +# can turn off the upper bounds setting in some edge cases. +class test_cursor_bound20(bound_base): + file_name = 'test_cursor_bound20' + uri = 'table:' + key_format = 'S' + + def set_bounds(self, cursor, key, bound_config, inclusive = None): + inclusive_config = "" + if (bound_config == "lower"): + if (inclusive == False): + inclusive_config = ",inclusive=false" + elif (bound_config == "upper"): + if (inclusive == False): + inclusive_config = ",inclusive=false" + + # Set key and bounds. + cursor.set_key(key) + return cursor.bound("bound={0}{1}".format(bound_config, inclusive_config)) + + def gen_uval(self, i): + result = bytearray() + while i > 0: + n = i % 4 + i = i // 4 + if n == 0: + pass + elif n == 3: + result.insert(0, 0xff) + else: + result.insert(0, n - 1) # either 00 or 01 + return bytes(result) + + def gen_index_table(self): + # Test Index index_cursors bound API support. + suburi = "index:" + self.file_name + ":i0" + start = 0 + columns_param = "columns=(" + for v in self.value_format: + if v.isdigit(): + continue + + columns_param += "v{0},".format(str(start)) + start += 1 + columns_param += ")" + self.session.create(suburi, columns_param) + + + def test_cursor_index_bounds_fixed(self): + self.value_format = '4s' + MAX_FIXED_STRING = chr(127) + chr(127) + chr(127) + chr(127) + + uri = self.uri + self.file_name + create_params = 'value_format={},key_format={}'.format(self.value_format, self.key_format) + create_params += self.gen_create_param() + self.session.create(uri, create_params) + + cursor = self.session.open_cursor(uri, None, None) + self.session.begin_transaction() + for i in range(self.start_key, self.end_key + 1): + cursor[self.gen_key(i)] = MAX_FIXED_STRING + self.session.commit_transaction() + cursor.close() + + # Create index table. + self.gen_index_table() + index_cursor = self.session.open_cursor("index:" + self.file_name + ":i0") + + # Set bounds at lower key and upper max value. This is to validate the increment bounds + # function for fixed length string. + self.set_bounds(index_cursor, "0000", "lower") + self.set_bounds(index_cursor, MAX_FIXED_STRING, "upper") + self.cursor_traversal_bound(index_cursor, None, None, True) + self.cursor_traversal_bound(index_cursor, None, None, False) + + # Test basic search near scenarios. + index_cursor.set_key(MAX_FIXED_STRING) + self.assertEqual(index_cursor.search_near(), 0) + self.assertEqual(index_cursor.get_key(), self.check_key(MAX_FIXED_STRING)) + + # Test basic search scnarios. + index_cursor.set_key(MAX_FIXED_STRING) + self.assertEqual(index_cursor.search(), 0) + self.assertEqual(index_cursor.get_key(), self.check_key(MAX_FIXED_STRING)) + index_cursor.reset() + + # Test index case: Lower bound with exclusive + self.set_bounds(index_cursor, MAX_FIXED_STRING, "lower", False) + self.cursor_traversal_bound(index_cursor, None, None, True, 0) + self.cursor_traversal_bound(index_cursor, None, None, False, 0) + + index_cursor.set_key(MAX_FIXED_STRING) + self.assertEqual(index_cursor.search_near(), wiredtiger.WT_NOTFOUND) + + index_cursor.set_key(MAX_FIXED_STRING) + self.assertEqual(index_cursor.search(), wiredtiger.WT_NOTFOUND) + + def test_cursor_index_bounds_byte(self): + self.value_format = '2u' + MAX_BYTE_ARRAY = bytearray() + MAX_BYTE_ARRAY.insert(0, 0xFF) + MAX_BYTE_ARRAY.insert(0, 0xFF) + MAX_BYTE_ARRAY = bytes(MAX_BYTE_ARRAY) + + uri = self.uri + self.file_name + create_params = 'value_format=u,key_format={}'.format(self.key_format) + create_params += self.gen_create_param() + self.session.create(uri, create_params) + + cursor = self.session.open_cursor(uri, None, None) + self.session.begin_transaction() + count = 0 + for i in range(self.start_key, self.end_key + 1): + cursor[self.gen_key(i)] = self.gen_uval(count) + count = (count + 1) % 20 + self.session.commit_transaction() + cursor.close() + + # Create index table. + self.gen_index_table() + index_cursor = self.session.open_cursor("index:" + self.file_name + ":i0") + + # Set bounds at lower key and upper max byte value. This is to validate the increment bounds + # function for bytes. + self.set_bounds(index_cursor, bytes(0), "lower") + self.set_bounds(index_cursor, MAX_BYTE_ARRAY, "upper") + self.cursor_traversal_bound(index_cursor, None, None, True) + self.cursor_traversal_bound(index_cursor, None, None, False) + + # Test basic search near scenarios. + index_cursor.set_key(MAX_BYTE_ARRAY) + self.assertEqual(index_cursor.search_near(), 0) + + # Test basic search scnarios. + index_cursor.set_key(MAX_BYTE_ARRAY) + self.assertEqual(index_cursor.search(), 0) + self.assertEqual(index_cursor.get_key(), MAX_BYTE_ARRAY) + index_cursor.reset() + + # Test index case: Lower bound with exclusive + self.set_bounds(index_cursor, MAX_BYTE_ARRAY, "lower", False) + self.cursor_traversal_bound(index_cursor, None, None, True, 0) + self.cursor_traversal_bound(index_cursor, None, None, False, 0) + + index_cursor.set_key(MAX_BYTE_ARRAY) + self.assertEqual(index_cursor.search_near(), wiredtiger.WT_NOTFOUND) + + index_cursor.set_key(MAX_BYTE_ARRAY) + self.assertEqual(index_cursor.search(), wiredtiger.WT_NOTFOUND) + +if __name__ == '__main__': + wttest.run() diff --git a/src/third_party/wiredtiger/test/suite/wtbound.py b/src/third_party/wiredtiger/test/suite/wtbound.py index 3f731e4fe0f..1ce76f86cff 100644 --- a/src/third_party/wiredtiger/test/suite/wtbound.py +++ b/src/third_party/wiredtiger/test/suite/wtbound.py @@ -108,12 +108,15 @@ class bound_base(wttest.WiredTigerTestCase): end_key = 79 lower_inclusive = True upper_inclusive = True + use_index = False + use_colgroup = False def create_session_and_cursor(self, cursor_config=None): + index = self.use_index uri = self.uri + self.file_name create_params = 'value_format={},key_format={}'.format(self.value_format, self.key_format) - if self.use_colgroup: - create_params += self.gen_colgroup_create_param() + if self.use_colgroup or self.use_index: + create_params += self.gen_create_param() self.session.create(uri, create_params) # Add in column group. @@ -126,8 +129,19 @@ class bound_base(wttest.WiredTigerTestCase): cursor = self.session.open_cursor(uri, None, cursor_config) self.session.begin_transaction() - for i in range(self.start_key, self.end_key + 1): - cursor[self.gen_key(i)] = self.gen_val("value" + str(i)) + if (self.use_index): + # Turn use_index off while populating main table, as the variable is used + # to generate the key and value for index tables. + self.use_index = False + count = self.start_key + for i in range(self.start_key, self.end_key + 1): + cursor[self.gen_key(i)] = self.gen_val(count) + # Increase count on every even interval to produce duplicate values. + if (i % 2 == 0): + count = count + 1 + else: + for i in range(self.start_key, self.end_key + 1): + cursor[self.gen_key(i)] = self.gen_val("value" + str(i)) self.session.commit_transaction() if (self.evict): @@ -136,9 +150,13 @@ class bound_base(wttest.WiredTigerTestCase): evict_cursor.set_key(self.gen_key(i)) evict_cursor.search() evict_cursor.reset() + evict_cursor.close() + + if (index): + self.use_index = True return cursor - def gen_colgroup_create_param(self): + def gen_create_param(self): create_params = ",columns=(" start = 0 for _ in self.key_format: @@ -146,21 +164,30 @@ class bound_base(wttest.WiredTigerTestCase): start += 1 start = 0 - for _ in self.value_format: - create_params += "v{0},".format(str(start)) - start += 1 - create_params += "),colgroups=(" + for v in self.value_format: + if v.isdigit(): + continue - start = 0 - for _ in self.value_format: - create_params += "g{0},".format(str(start)) + create_params += "v{0},".format(str(start)) start += 1 create_params += ")" + + if (self.use_colgroup): + create_params += ",colgroups=(" + start = 0 + for _ in self.value_format: + create_params += "g{0},".format(str(start)) + start += 1 + create_params += ")" return create_params def gen_key(self, i): tuple_key = [] - for key in self.key_format: + key_format = self.key_format + if (self.use_index): + key_format = self.value_format + + for key in key_format: if key == 'S' or key == 'u': tuple_key.append(str(i)) elif key == "r": @@ -168,7 +195,7 @@ class bound_base(wttest.WiredTigerTestCase): elif key == "i": tuple_key.append(i) - if (len(self.key_format) == 1): + if (len(key_format) == 1): return tuple_key[0] else: return tuple(tuple_key) @@ -190,7 +217,11 @@ class bound_base(wttest.WiredTigerTestCase): def check_key(self, i): list_key = [] - for key in self.key_format: + key_format = self.key_format + if (self.use_index): + key_format = self.value_format + + for key in key_format: if key == 'S': list_key.append(str(i)) elif key == "r": @@ -200,7 +231,7 @@ class bound_base(wttest.WiredTigerTestCase): elif key == "u": list_key.append(str(i).encode()) - if (len(self.key_format) == 1): + if (len(key_format) == 1): return list_key[0] else: return list_key |