summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEtienne Petrel <etienne.petrel@mongodb.com>2022-09-01 14:23:46 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-09-01 15:05:20 +0000
commite99ff96fda815d20d7e6cbbd5c67f1c6860a697f (patch)
treef31bce58dcd631323c745fd81a229617e1b04440
parentf9fd4ff7a04219df87edd9d6084524eaf5616f10 (diff)
downloadmongo-e99ff96fda815d20d7e6cbbd5c67f1c6860a697f.tar.gz
Import wiredtiger: cfb48c05ebcc12aa122d245430dbda07c5f0513a from branch mongodb-master
ref: e217c5727a..cfb48c05eb for: 6.2.0-rc0 WT-9781 Fix search near exact edge case with bounded cursor (#8231)
-rw-r--r--src/third_party/wiredtiger/import.data2
-rw-r--r--src/third_party/wiredtiger/src/btree/bt_cursor.c13
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound08.py6
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound15.py189
4 files changed, 206 insertions, 4 deletions
diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data
index 2c0aa44d8aa..7b02856bc93 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": "e217c5727a0922118d3d294729864605c2e8bacc"
+ "commit": "cfb48c05ebcc12aa122d245430dbda07c5f0513a"
}
diff --git a/src/third_party/wiredtiger/src/btree/bt_cursor.c b/src/third_party/wiredtiger/src/btree/bt_cursor.c
index 6b2d5bb60ca..0d61b0350f3 100644
--- a/src/third_party/wiredtiger/src/btree/bt_cursor.c
+++ b/src/third_party/wiredtiger/src/btree/bt_cursor.c
@@ -1006,6 +1006,19 @@ __wt_btcur_search_near(WT_CURSOR_BTREE *cbt, int *exactp)
__cursor_state_restore(cursor, &state);
else {
__wt_value_return(cbt, cbt->upd_value);
+ /*
+ * This compare is needed for bounded cursors in the event that a valid key is found.
+ * The returned value of exact must reflect the comparison between the found key and the
+ * original search key, not the repositioned bounds key. This comparison ensures that is
+ * the case.
+ */
+ if (WT_CURSOR_BOUNDS_SET(cursor)) {
+ if (btree->type == BTREE_ROW)
+ WT_ERR(
+ __wt_compare(session, btree->collator, &cursor->key, &state.key, &exact));
+ else
+ exact = cbt->recno < state.recno ? -1 : cbt->recno == state.recno ? 0 : 1;
+ }
goto done;
}
}
diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound08.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound08.py
index 805ad68eb90..75097c0a472 100644
--- a/src/third_party/wiredtiger/test/suite/test_cursor_bound08.py
+++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound08.py
@@ -165,7 +165,7 @@ class test_cursor_bound08(bound_base):
self.set_bounds(cursor, 30, "lower")
cursor.set_key(self.gen_key(20))
- self.assertEqual(cursor.search_near(), 0)
+ self.assertEqual(cursor.search_near(), 1)
self.assertEqual(cursor.get_key(), self.check_key(30))
self.assertEqual(cursor.reset(), 0)
self.assertEqual(self.get_stat(stat.conn.cursor_bounds_search_near_repositioned_cursor), 1)
@@ -174,14 +174,14 @@ class test_cursor_bound08(bound_base):
# This can only happen when the search key is out of the bound range.
self.set_bounds(cursor, 30, "lower")
cursor.set_key(self.gen_key(20))
- self.assertEqual(cursor.search_near(), 0)
+ self.assertEqual(cursor.search_near(), 1)
self.assertEqual(cursor.get_key(), self.check_key(30))
self.assertEqual(cursor.reset(), 0)
self.assertEqual(self.get_stat(stat.conn.cursor_bounds_search_near_repositioned_cursor), 2)
self.set_bounds(cursor, 40, "upper")
cursor.set_key(self.gen_key(60))
- self.assertEqual(cursor.search_near(), 0)
+ self.assertEqual(cursor.search_near(), -1)
self.assertEqual(cursor.get_key(), self.check_key(40))
self.assertEqual(cursor.reset(), 0)
self.assertEqual(self.get_stat(stat.conn.cursor_bounds_search_near_repositioned_cursor), 3)
diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound15.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound15.py
new file mode 100644
index 00000000000..378e62b6bd3
--- /dev/null
+++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound15.py
@@ -0,0 +1,189 @@
+#!/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 wttest
+from wtscenario import make_scenarios
+from wtbound import set_prefix_bound, bound_base
+
+# test_cursor_bound15.py
+# This test checks the edge case that a search_near call with bounds and prefix key
+# will return the right value of exact.
+class test_cursor_bound15(bound_base):
+ key_format_values = [
+ ('var_string', dict(key_format='S')),
+ ('byte_array', dict(key_format='u')),
+ ]
+
+ eviction = [
+ ('eviction', dict(eviction=True)),
+ ('no eviction', dict(eviction=False)),
+ ]
+
+ scenarios = make_scenarios(key_format_values)
+
+ def check_key(self, key):
+ if self.key_format == 'u':
+ return key.encode()
+ elif self.key_format == '10s':
+ return key.ljust(10, "\x00")
+ else:
+ return key
+
+ def test_cursor_bound(self):
+ uri = 'table:test_cursor_bound'
+ self.session.create(uri, 'key_format={},value_format=S'.format(self.key_format))
+ cursor = self.session.open_cursor(uri)
+ cursor2 = self.session.open_cursor(uri, None, "debug=(release_evict=true)")
+ # Basic character array.
+ l = "abcdefghijklmnopqrstuvwxyz"
+
+ # Insert keys aaa -> aaz with timestamp 200.
+ prefix = "aa"
+ self.session.begin_transaction()
+ for k in range (0, 25):
+ key = prefix + l[k]
+ cursor[key] = key
+ self.session.commit_transaction('commit_timestamp=' + self.timestamp_str(200))
+
+ # Insert key aaz with timestamp 50.
+ self.session.begin_transaction()
+ cursor[prefix + "z"] = prefix + "z"
+ self.session.commit_transaction('commit_timestamp=' + self.timestamp_str(50))
+
+ if self.eviction:
+ # Evict the whole range.
+ for k in range (0, 26):
+ cursor2.set_key(prefix + l[k])
+ self.assertEqual(cursor2.search(), 0)
+ self.assertEqual(cursor2.reset(), 0)
+
+ # Begin transaction at timestamp 250, all keys should be visible.
+ self.session.begin_transaction('read_timestamp=' + self.timestamp_str(250))
+ cursor3 = self.session.open_cursor(uri)
+ cursor3.reset()
+
+ # Test with only lower bound set.
+ self.assertEqual(self.set_bounds(cursor3, "aab", "lower"), 0)
+ cursor3.set_key("ab")
+ self.assertEqual(cursor3.search_near(), -1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aaz"))
+ cursor3.reset()
+
+ self.assertEqual(self.set_bounds(cursor3, "aac", "lower"), 0)
+ cursor3.set_key("ab")
+ self.assertEqual(cursor3.search_near(), -1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aaz"))
+ cursor3.reset()
+
+ self.assertEqual(self.set_bounds(cursor3, "aaz", "lower", True), 0)
+ cursor3.set_key("aaz")
+ self.assertEqual(cursor3.search_near(), 0)
+ self.assertEqual(cursor3.get_key(), self.check_key("aaz"))
+ cursor3.reset()
+
+ self.assertEqual(self.set_bounds(cursor3, "a", "lower"), 0)
+ cursor3.set_key("aa")
+ self.assertEqual(cursor3.search_near(), 1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aaa"))
+ cursor3.reset()
+
+ # Test with only upper bound set.
+ self.assertEqual(self.set_bounds(cursor3, "aac", "upper", True), 0)
+ cursor3.set_key("aad")
+ self.assertEqual(cursor3.search_near(), -1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aac"))
+ cursor3.reset()
+
+ self.assertEqual(self.set_bounds(cursor3, "aaz", "upper"), 0)
+ cursor3.set_key("aac")
+ self.assertEqual(cursor3.search_near(), 0)
+ self.assertEqual(cursor3.get_key(), self.check_key("aac"))
+ cursor3.reset()
+
+ self.assertEqual(self.set_bounds(cursor3, "ac", "upper"), 0)
+ cursor3.set_key("aa")
+ self.assertEqual(cursor3.search_near(), 1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aaa"))
+ cursor3.reset()
+
+ # Test with both bounds set.
+ self.assertEqual(self.set_bounds(cursor3, "aaa", "lower"), 0)
+ self.assertEqual(self.set_bounds(cursor3, "aad", "upper"), 0)
+ cursor3.set_key("aae")
+ self.assertEqual(cursor3.search_near(), -1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aad"))
+ cursor3.reset()
+
+ self.assertEqual(self.set_bounds(cursor3, "aaa", "lower"), 0)
+ self.assertEqual(self.set_bounds(cursor3, "aae", "upper"), 0)
+ cursor3.set_key("aad")
+ self.assertEqual(cursor3.search_near(), 0)
+ self.assertEqual(cursor3.get_key(), self.check_key("aad"))
+ cursor3.reset()
+
+ self.assertEqual(self.set_bounds(cursor3, "aac", "lower", True), 0)
+ self.assertEqual(self.set_bounds(cursor3, "aaz", "upper", True), 0)
+ cursor3.set_key("aab")
+ self.assertEqual(cursor3.search_near(), 1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aac"))
+ cursor3.reset()
+
+ # Test with prefix bounds set.
+ # Search near for aaza, with prefix bounds aaa should return the closest visible key: aaz.
+ set_prefix_bound(self, cursor3, "aaz")
+ self.session.breakpoint()
+ cursor3.set_key("aaza")
+ self.assertEqual(cursor3.search_near(), -1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aaz"))
+ cursor3.reset()
+
+ # Search near for ab, with prefix bounds "aaa" should return the closest visible key: aaa.
+ set_prefix_bound(self, cursor3, "aaa")
+ self.session.breakpoint()
+ cursor3.set_key("ab")
+ self.assertEqual(cursor3.search_near(), -1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aaa"))
+ cursor3.reset()
+
+ # Search near for aac, should return the closest visible key: aac.
+ set_prefix_bound(self, cursor3, "a")
+ self.session.breakpoint()
+ cursor3.set_key("aac")
+ self.assertEqual(cursor3.search_near(), 0)
+ self.assertEqual(cursor3.get_key(), self.check_key("aac"))
+ cursor3.reset()
+
+ # Search near for aa, should return the closest visible key: aaa.
+ set_prefix_bound(self, cursor3, "aaa")
+ self.session.breakpoint()
+ cursor3.set_key("aa")
+ self.assertEqual(cursor3.search_near(), 1)
+ self.assertEqual(cursor3.get_key(), self.check_key("aaa"))
+ cursor3.reset()
+
+ cursor3.close()
+ self.session.commit_transaction()