diff options
author | Luke Chen <luke.chen@mongodb.com> | 2022-09-09 16:04:55 +1000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-09-09 06:34:13 +0000 |
commit | dd101c28231cda92c52047f7b61475625513c522 (patch) | |
tree | 8ad7a3fc058d18a537f3ffdccb820d11cced80fe | |
parent | 1446eda8a18e60ee43275d3ede20ca6e5ea8070a (diff) | |
download | mongo-dd101c28231cda92c52047f7b61475625513c522.tar.gz |
Import wiredtiger: cfb48c05ebcc12aa122d245430dbda07c5f0513a from branch mongodb-6.1
ref: e217c5727a..cfb48c05eb
for: 6.1.0-rc2
WT-9781 Fix search near exact edge case with bounded cursor (#8231)
4 files changed, 206 insertions, 4 deletions
diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data index d8e96d5419c..4c48c144ef5 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-6.1", - "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() |