summaryrefslogtreecommitdiff
path: root/src/third_party/wiredtiger
diff options
context:
space:
mode:
authorChenhao Qu <chenhao.qu@mongodb.com>2022-11-09 12:26:26 +1100
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-11-09 02:47:22 +0000
commit04be3d51c64f804421f5bca6dd05c8fe64d898d2 (patch)
tree773867f9e2830347f19fb6fe21851e66cf848834 /src/third_party/wiredtiger
parenta197f7350ddaaac6bb30309469c03593e97c3835 (diff)
downloadmongo-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')
-rw-r--r--src/third_party/wiredtiger/import.data2
-rw-r--r--src/third_party/wiredtiger/src/cursor/cur_index.c108
-rw-r--r--src/third_party/wiredtiger/src/docs/cursor-ops.dox2
-rw-r--r--src/third_party/wiredtiger/src/include/api.h4
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound01.py22
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound02.py6
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound06.py3
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound19.py144
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound20.py186
-rw-r--r--src/third_party/wiredtiger/test/suite/wtbound.py63
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