diff options
author | Chenhao Qu <chenhao.qu@mongodb.com> | 2022-08-03 12:55:53 +1000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-08-03 03:24:43 +0000 |
commit | ccddad0fd0ce788c9926416c61e518fb0e34086b (patch) | |
tree | ef136e4e67e5f0f469c275adaf3add19631eddad | |
parent | 9ce3d41ad2eebe251e6423aecb01508234bc9b95 (diff) | |
download | mongo-ccddad0fd0ce788c9926416c61e518fb0e34086b.tar.gz |
Import wiredtiger: 6cec5920fd9e9df2dc8bfe7a5c06e3820d4956b8 from branch mongodb-master
ref: 39377c27ac..6cec5920fd
for: 6.1.0-rc0
WT-9475 Write tests for cursor next/prev implementation (#8140)
11 files changed, 804 insertions, 156 deletions
diff --git a/src/third_party/wiredtiger/import.data b/src/third_party/wiredtiger/import.data index 358a9f46fab..a66984909fa 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": "39377c27ac42ab748258d53b3b1a455a054cc469" + "commit": "6cec5920fd9e9df2dc8bfe7a5c06e3820d4956b8" } 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 5d32cea503d..459c9aabd85 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound02.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound02.py @@ -43,7 +43,7 @@ class test_cursor_bound02(bound_base): ('colgroup', dict(uri='table:', use_colgroup=True)) ] - key_format_values = [ + key_formats = [ ('string', dict(key_format='S')), ('var', dict(key_format='r')), ('int', dict(key_format='i')), @@ -53,23 +53,30 @@ class test_cursor_bound02(bound_base): ('composite_complex', dict(key_format='iSru')), ] + value_formats = [ + ('string', dict(value_format='S')), + ('complex-string', dict(value_format='SS')), + ] + inclusive = [ ('inclusive', dict(inclusive=True)), ('no-inclusive', dict(inclusive=False)) ] - scenarios = make_scenarios(types, key_format_values, inclusive) + scenarios = make_scenarios(types, key_formats, value_formats, inclusive) def test_bound_api(self): uri = self.uri + self.file_name - create_params = 'value_format=S,key_format={}'.format(self.key_format) + create_params = 'value_format={},key_format={}'.format(self.value_format, self.key_format) if self.use_colgroup: create_params += self.gen_colgroup_create_param() self.session.create(uri, create_params) - # Add in column group. + + # Add in column groups. if self.use_colgroup: - create_params = 'columns=(v),' - suburi = 'colgroup:{0}:g0'.format(self.file_name) - self.session.create(suburi, create_params) + for i in range(0, len(self.value_format)): + create_params = 'columns=(v{0}),'.format(i) + suburi = 'colgroup:{0}:g{1}'.format(self.file_name, i) + self.session.create(suburi, create_params) cursor = self.session.open_cursor(uri) @@ -100,12 +107,12 @@ class test_cursor_bound02(bound_base): self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: cursor.bound("bound=upper"), '/Invalid argument/') # Test bound API: Test that the key persists after lower bound call. - cursor.set_value("30") + cursor.set_value(self.gen_val("30")) self.assertEqual(self.set_bounds(cursor, 30, "lower"), 0) cursor.insert() # Test bound API: Test that the key persists after upper bound call. - cursor.set_value("90") + cursor.set_value(self.gen_val("90")) self.assertEqual(self.set_bounds(cursor, 90, "upper"), 0) cursor.insert() @@ -132,15 +139,16 @@ class test_cursor_bound02(bound_base): def test_bound_api_reset(self): uri = self.uri + self.file_name - create_params = 'value_format=S,key_format={}'.format(self.key_format) + create_params = 'value_format={},key_format={}'.format(self.value_format, self.key_format) if self.use_colgroup: create_params += self.gen_colgroup_create_param() self.session.create(uri, create_params) - # Add in column group. + # Add in column groups. if self.use_colgroup: - create_params = 'columns=(v),' - suburi = 'colgroup:{0}:g0'.format(self.file_name) - self.session.create(suburi, create_params) + for i in range(0, len(self.value_format)): + create_params = 'columns=(v{0}),'.format(i) + suburi = 'colgroup:{0}:g{1}'.format(self.file_name, i) + self.session.create(suburi, create_params) cursor = self.session.open_cursor(uri) self.assertEqual(self.set_bounds(cursor, 30, "lower"), 0) @@ -184,15 +192,16 @@ class test_cursor_bound02(bound_base): def test_bound_api_clear(self): uri = self.uri + self.file_name - create_params = 'value_format=S,key_format={}'.format(self.key_format) + create_params = 'value_format={},key_format={}'.format(self.value_format, self.key_format) if self.use_colgroup: create_params += self.gen_colgroup_create_param() self.session.create(uri, create_params) - # Add in column group. + # Add in column groups. if self.use_colgroup: - create_params = 'columns=(v),' - suburi = 'colgroup:{0}:g0'.format(self.file_name) - self.session.create(suburi, create_params) + for i in range(0, len(self.value_format)): + create_params = 'columns=(v{0}),'.format(i) + suburi = 'colgroup:{0}:g{1}'.format(self.file_name, i) + self.session.create(suburi, create_params) cursor = self.session.open_cursor(uri) self.assertEqual(self.set_bounds(cursor, 30, "lower"), 0) diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound03.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound03.py index 2ae55ec3576..fcbbb7e4b78 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound03.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound03.py @@ -42,7 +42,7 @@ class test_cursor_bound03(bound_base): ('colgroup', dict(uri='table:', use_colgroup=True)) ] - key_format_values = [ + key_formats = [ ('string', dict(key_format='S')), ('var', dict(key_format='r')), ('int', dict(key_format='i')), @@ -52,6 +52,12 @@ class test_cursor_bound03(bound_base): ('composite_complex', dict(key_format='iSru')), ] + value_formats = [ + ('string', dict(value_format='S')), + # FIX-ME-WT-9589: Fix bug complex colgroups not returning records within bounds. + # ('complex-string', dict(value_format='SS')), + ] + config = [ ('inclusive-evict', dict(lower_inclusive=True,upper_inclusive=True,evict=True)), ('no-inclusive-evict', dict(lower_inclusive=False,upper_inclusive=False,evict=True)), @@ -68,33 +74,7 @@ class test_cursor_bound03(bound_base): ('next', dict(next=True)), ] - scenarios = make_scenarios(types, key_format_values, config, direction) - - def create_session_and_cursor(self): - uri = self.uri + self.file_name - create_params = 'value_format=S,key_format={}'.format(self.key_format) - if self.use_colgroup: - create_params += self.gen_colgroup_create_param() - self.session.create(uri, create_params) - # Add in column group. - if self.use_colgroup: - create_params = 'columns=(v),' - suburi = 'colgroup:{0}:g0'.format(self.file_name) - self.session.create(suburi, create_params) - - cursor = self.session.open_cursor(uri) - self.session.begin_transaction() - for i in range(self.start_key, self.end_key + 1): - cursor[self.gen_key(i)] = "value" + str(i) - self.session.commit_transaction() - - if (self.evict): - evict_cursor = self.session.open_cursor(uri, None, "debug=(release_evict)") - for i in range(self.start_key, self.end_key): - evict_cursor.set_key(self.gen_key(i)) - evict_cursor.search() - evict_cursor.reset() - return cursor + scenarios = make_scenarios(types, key_formats, value_formats, config, direction) def test_bound_general_scenario(self): cursor = self.create_session_and_cursor() @@ -148,7 +128,7 @@ class test_cursor_bound03(bound_base): # Test bound api: Test upper bound clearing with only lower bounds. self.set_bounds(cursor, 50, "lower") cursor.bound("action=clear,bound=upper") - self.cursor_traversal_bound(cursor, None, None, self.direction, self.end_key - 50) + self.cursor_traversal_bound(cursor, None, None, self.direction, self.end_key - 49) cursor.bound("action=clear,bound=lower") self.cursor_traversal_bound(cursor, None, None) diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound04.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound04.py index f8c4c5cc900..b3e6fe0bc27 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound04.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound04.py @@ -44,7 +44,7 @@ class test_cursor_bound04(bound_base): ('colgroup', dict(uri='table:', use_colgroup=True)) ] - key_format_values = [ + key_formats = [ ('string', dict(key_format='S')), ('var', dict(key_format='r')), ('int', dict(key_format='i')), @@ -54,37 +54,17 @@ class test_cursor_bound04(bound_base): ('composite_complex', dict(key_format='iSru')), ] + value_formats = [ + ('string', dict(value_format='S')), + # FIX-ME-WT-9589: Fix bug complex colgroups not returning records within bounds. + # ('complex-string', dict(value_format='SS')), + ] + config = [ ('evict', dict(evict=True)), ('no-evict', dict(evict=False)) ] - scenarios = make_scenarios(types, key_format_values, config) - - def create_session_and_cursor(self): - uri = self.uri + self.file_name - create_params = 'value_format=S,key_format={}'.format(self.key_format) - if self.use_colgroup: - create_params += self.gen_colgroup_create_param() - self.session.create(uri, create_params) - # Add in column group. - if self.use_colgroup: - create_params = 'columns=(v),' - suburi = 'colgroup:{0}:g0'.format(self.file_name) - self.session.create(suburi, create_params) - - cursor = self.session.open_cursor(uri) - self.session.begin_transaction() - for i in range(self.start_key, self.end_key + 1): - cursor[self.gen_key(i)] = "value" + str(i) - self.session.commit_transaction() - - if (self.evict): - evict_cursor = self.session.open_cursor(uri, None, "debug=(release_evict)") - for i in range(self.start_key, self.end_key): - evict_cursor.set_key(self.gen_key(i)) - evict_cursor.search() - evict_cursor.reset() - return cursor + scenarios = make_scenarios(types, key_formats, value_formats, config) def test_bound_special_scenario(self): cursor = self.create_session_and_cursor() @@ -181,7 +161,7 @@ class test_cursor_bound04(bound_base): key = cursor.get_key() self.assertEqual(key, self.check_key(55)) self.assertEqual(cursor.bound("action=clear"), 0) - self.cursor_traversal_bound(cursor, None, None, True, self.end_key - 55 - 1) + self.cursor_traversal_bound(cursor, None, None, True, self.end_key - 55) cursor.reset() self.set_bounds(cursor, 55, "upper") @@ -189,7 +169,7 @@ class test_cursor_bound04(bound_base): key = cursor.get_key() self.assertEqual(key, self.check_key(self.start_key)) self.assertEqual(cursor.bound("action=clear"), 0) - self.cursor_traversal_bound(cursor, None, None, True, self.end_key - self.start_key - 1) + self.cursor_traversal_bound(cursor, None, None, True, self.end_key - self.start_key) cursor.reset() cursor.close() @@ -249,7 +229,7 @@ class test_cursor_bound04(bound_base): key = cursor.get_key() self.assertEqual(key, self.check_key(45)) self.assertEqual(cursor.bound("action=clear"), 0) - self.cursor_traversal_bound(cursor, None, None, False, 45 - self.start_key - 1) + self.cursor_traversal_bound(cursor, None, None, False, 45 - self.start_key) cursor.reset() self.set_bounds(cursor, 45, "upper") @@ -257,7 +237,7 @@ class test_cursor_bound04(bound_base): key = cursor.get_key() self.assertEqual(key, self.check_key(45)) self.assertEqual(cursor.bound("action=clear"), 0) - self.cursor_traversal_bound(cursor, None, None, True, self.end_key - 45 - 1) + self.cursor_traversal_bound(cursor, None, None, True, self.end_key - 45) cursor.reset() cursor.close() diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound05.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound05.py index 4ac39c186bd..3942059e9ad 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound05.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound05.py @@ -36,8 +36,12 @@ from wtbound import bound_base class test_cursor_bound05(bound_base): file_name = 'test_cursor_bound05' key_format = 'S' + value_format = 'S' + + # The start and end key denotes the first and last key in the table. Since 1000 is a key itself, + # there are 1000 entries between the start and end key. start_key = 1000 - end_key = 2000 + end_key = 1999 types = [ ('file', dict(uri='file:', use_colgroup=False)), @@ -50,25 +54,6 @@ class test_cursor_bound05(bound_base): ] scenarios = make_scenarios(types, config) - def create_session_and_cursor(self): - uri = self.uri + self.file_name - create_params = 'value_format=S,key_format={}'.format(self.key_format) - self.session.create(uri, create_params) - - cursor = self.session.open_cursor(uri) - self.session.begin_transaction() - for i in range(self.start_key, self.end_key + 1): - cursor[self.gen_key(i)] = "value" + str(i) - self.session.commit_transaction() - - if (self.evict): - evict_cursor = self.session.open_cursor(uri, None, "debug=(release_evict)") - for i in range(self.start_key, self.end_key): - evict_cursor.set_key(self.gen_key(i)) - evict_cursor.search() - evict_cursor.reset() - return cursor - def test_bound_special_scenario(self): cursor = self.create_session_and_cursor() @@ -80,21 +65,21 @@ class test_cursor_bound05(bound_base): # Test bound api: Test prefix key with upper bound. self.set_bounds(cursor, 20, "upper", False) - self.cursor_traversal_bound(cursor, None, 20, True, 999) - self.cursor_traversal_bound(cursor, None, 20, False, 999) + self.cursor_traversal_bound(cursor, None, 20, True, 1000) + self.cursor_traversal_bound(cursor, None, 20, False, 1000) self.assertEqual(cursor.bound("action=clear"), 0) # Test bound api: Test prefix key works with both bounds. self.set_bounds(cursor, 10, "lower", True) self.set_bounds(cursor, 20, "upper", False) - self.cursor_traversal_bound(cursor, 10, 20, True, 999) - self.cursor_traversal_bound(cursor, 10, 20, False, 999) + self.cursor_traversal_bound(cursor, 10, 20, True, 1000) + self.cursor_traversal_bound(cursor, 10, 20, False, 1000) self.assertEqual(cursor.bound("action=clear"), 0) self.set_bounds(cursor, 10, "lower", True) self.set_bounds(cursor, 11, "upper", False) - self.cursor_traversal_bound(cursor, 10, 11, True, 99) - self.cursor_traversal_bound(cursor, 10, 11, False, 99) + self.cursor_traversal_bound(cursor, 10, 11, True, 100) + self.cursor_traversal_bound(cursor, 10, 11, False, 100) self.assertEqual(cursor.bound("action=clear"), 0) # Test bound api: Test early exit works with lower bound (Greater than data range). 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 0a6a47c355e..29632233d38 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound06.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound06.py @@ -31,7 +31,7 @@ from wtscenario import make_scenarios from wtbound import bound_base # test_cursor_bound06.py -# Test the search() call in the cursor bound API. +# Test the search() call in the cursor bound API. class test_cursor_bound06(bound_base): file_name = 'test_cursor_bound06' @@ -41,10 +41,10 @@ class test_cursor_bound06(bound_base): ('colgroup', dict(uri='table:', use_colgroup=True)) ] - key_format_values = [ + key_formats = [ ('string', dict(key_format='S')), - # FIXME-WT-9474: Uncomment once column store is implemented. - #('var', dict(key_format='r')), + # FIXME-WT-9487: Uncomment once column store search is implemented. + # ('var', dict(key_format='r')), ('int', dict(key_format='i')), ('bytes', dict(key_format='u')), ('composite_string', dict(key_format='SSS')), @@ -52,33 +52,20 @@ class test_cursor_bound06(bound_base): ('composite_complex', dict(key_format='iSru')), ] + value_formats = [ + ('string', dict(value_format='S')), + ('complex-string', dict(value_format='SS')), + ] + inclusive = [ ('inclusive', dict(inclusive=True)), ('no-inclusive', dict(inclusive=False)) ] - scenarios = make_scenarios(types, key_format_values, inclusive) - - def create_session_and_cursor(self): - uri = self.uri + self.file_name - create_params = 'value_format=S,key_format={}'.format(self.key_format) - if self.use_colgroup: - create_params += self.gen_colgroup_create_param() - self.session.create(uri, create_params) - - # Add in column group. - if self.use_colgroup: - create_params = 'columns=(v),' - suburi = 'colgroup:{0}:g0'.format(self.file_name) - self.session.create(suburi, create_params) - cursor = self.session.open_cursor(uri) - self.session.begin_transaction() - - for i in range(self.start_key, self.end_key + 1): - cursor[self.gen_key(i)] = "value" + str(i) - self.session.commit_transaction() - - return cursor + config = [ + ('no-evict', dict(evict=False)) + ] + scenarios = make_scenarios(types, key_formats, value_formats, inclusive, config) def test_bound_search_scenario(self): cursor = self.create_session_and_cursor() diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound07.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound07.py index d87a97e2552..804e1c04686 100644 --- a/src/third_party/wiredtiger/test/suite/test_cursor_bound07.py +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound07.py @@ -34,8 +34,11 @@ from wtbound import bound_base # Test column store related scenarios with the bounds API. class test_cursor_bound07(bound_base): file_name = 'test_cursor_bound07' + + # The start and end key denotes the first and last key in the table. Since 10 is a key itself, + # there are 100 entries between the start and end key. start_key = 10 - end_key = 100 + end_key = 99 key_format = 'r' lower_inclusive = True upper_inclusive = True @@ -70,19 +73,19 @@ class test_cursor_bound07(bound_base): cursor = self.session.open_cursor(uri) self.session.begin_transaction() - for i in range(10, 31): + for i in range(10, 30): value = "value" + str(i) if not self.records_rle else "value" cursor[self.gen_key(i)] = value self.session.commit_transaction() self.session.begin_transaction() - for i in range(71, 101): + for i in range(70, 100): value = "value" + str(i) if not self.records_rle else "value" cursor[self.gen_key(i)] = value self.session.commit_transaction() self.session.begin_transaction() - for i in range(31, 71): + for i in range(30, 70): value = "value" + str(i) if not self.deleted_rle else "value" cursor[self.gen_key(i)] = value cursor.set_key(self.gen_key(i)) @@ -119,7 +122,7 @@ class test_cursor_bound07(bound_base): # Test bound api: Test traversal with lower over deleted records. self.set_bounds(cursor, 50, "lower", self.lower_inclusive) - self.cursor_traversal_bound(cursor, 50, None, self.next, 29) + self.cursor_traversal_bound(cursor, 50, None, self.next, 30) self.assertEqual(cursor.bound("action=clear"), 0) self.set_bounds(cursor, 50, "upper", self.upper_inclusive) @@ -128,12 +131,12 @@ class test_cursor_bound07(bound_base): # Test bound api: Test column store insert list. self.session.begin_transaction() - for i in range(51, 61): + for i in range(50, 60): cursor[self.gen_key(i)] = "value" + str(i) self.session.commit_transaction() self.set_bounds(cursor, 55, "upper", self.upper_inclusive) - self.cursor_traversal_bound(cursor, None, 55, self.next, 25) + self.cursor_traversal_bound(cursor, None, 55, self.next, 26) self.assertEqual(cursor.bound("action=clear"), 0) self.set_bounds(cursor, 55, "lower", self.lower_inclusive) @@ -145,10 +148,9 @@ class test_cursor_bound07(bound_base): cursor[101] = "value_normal" + str(i) self.session.commit_transaction() - # FIX-ME-WT-9475: cursor_traversal_bound has a bug, therefore e-enable this check when the function is fixed. - # self.set_bounds(cursor, 100, "lower", False) - # self.cursor_traversal_bound(cursor, 100, None, self.next, 1) - # self.assertEqual(cursor.bound("action=clear"), 0) + self.set_bounds(cursor, 100, "lower", False) + self.cursor_traversal_bound(cursor, 100, None, self.next, 1) + self.assertEqual(cursor.bound("action=clear"), 0) self.set_bounds(cursor, 101, "upper", False) self.cursor_traversal_bound(cursor, None, 101, self.next, 60) @@ -158,10 +160,9 @@ class test_cursor_bound07(bound_base): cursor[102] = "value_normal" + str(i) self.session.commit_transaction() - # FIX-ME-WT-9475: cursor_traversal_bound has a bug, therefore e-enable this check when the function is fixed. - # self.set_bounds(cursor, 100, "lower", False) - # self.cursor_traversal_bound(cursor, 100, None, self.next, 2) - # self.assertEqual(cursor.bound("action=clear"), 0) + self.set_bounds(cursor, 100, "lower", False) + self.cursor_traversal_bound(cursor, 100, None, self.next, 2) + self.assertEqual(cursor.bound("action=clear"), 0) self.set_bounds(cursor, 101, "upper", False) self.cursor_traversal_bound(cursor, None, 101, self.next, 60) diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound08.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound08.py new file mode 100644 index 00000000000..712384d698f --- /dev/null +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound08.py @@ -0,0 +1,252 @@ +#!/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 +from wiredtiger import stat + +# test_cursor_bound08.py +# Test that the statistics added for cursor bound API are appropiately incremented for all different cursor +# operations and edge cases. +class test_cursor_bound08(bound_base): + conn_config = 'statistics=(all)' + file_name = 'test_cursor_bound08' + value_format = 'S' + lower_inclusive = True + upper_inclusive = True + + types = [ + ('file', dict(uri='file:', use_colgroup=False)), + ('table', dict(uri='table:', use_colgroup=False)), + ] + + key_format_values = [ + ('string', dict(key_format='S')), + # FIXME-WT-9489: Uncomment once column store search near is implemented. + # ('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')), + ] + + evict = [ + ('inclusive-evict', dict(evict=True)), + ('inclusive-no-evict', dict(evict=False)), + ] + + scenarios = make_scenarios(types, key_format_values, evict) + + def create_session_and_cursor_timestamp(self): + uri = self.uri + self.file_name + create_params = 'value_format=S,key_format={}'.format(self.key_format) + self.session.create(uri, create_params) + + cursor = self.session.open_cursor(uri) + self.session.begin_transaction() + for i in range(100, 301): + cursor[self.gen_key(i)] = "value" + str(i) + self.session.commit_transaction('commit_timestamp=' + self.timestamp_str(100)) + + self.session.begin_transaction() + for i in range(301, 601): + cursor[self.gen_key(i)] = "value" + str(i) + self.session.commit_transaction('commit_timestamp=' + self.timestamp_str(200)) + + self.session.begin_transaction() + for i in range(601, 1000): + cursor[self.gen_key(i)] = "value" + str(i) + self.session.commit_transaction('commit_timestamp=' + self.timestamp_str(100)) + + if (self.evict): + evict_cursor = self.session.open_cursor(uri, None, "debug=(release_evict)") + for i in range(0, 1000): + evict_cursor.set_key(self.gen_key(i)) + evict_cursor.search() + evict_cursor.reset() + evict_cursor.close() + return cursor + + def get_stat(self, stat): + stat_cursor = self.session.open_cursor('statistics:') + val = stat_cursor[stat][2] + stat_cursor.close() + return val + + def test_bound_basic_stat_scenario(self): + cursor = self.create_session_and_cursor() + + # Test bound api: Test that early exit stat gets incremented with a upper bound. + self.set_bounds(cursor, 50, "upper") + self.cursor_traversal_bound(cursor, None, 50, True) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_next_early_exit), 1) + + # Test bound api: Test that early exit stat gets incremented with a lower bound. + self.set_bounds(cursor, 45, "lower") + self.cursor_traversal_bound(cursor, 45, None, False) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_prev_early_exit), 1) + + # Test bound api: Test that cursor next unpositioned stat gets incremented with a lower bound. + self.set_bounds(cursor, 45, "lower") + self.cursor_traversal_bound(cursor, 45, None, True) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_next_unpositioned), 1) + + # Test bound api: Test that cursor prev unpositioned stat gets incremented with an upper bound. + self.set_bounds(cursor, 50, "upper") + self.cursor_traversal_bound(cursor, None, 50, False) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_prev_unpositioned), 1) + + # Test bound api: Test that both stats get incremented with both bounds set. + self.set_bounds(cursor, 45, "lower") + self.set_bounds(cursor, 50, "upper") + self.cursor_traversal_bound(cursor, 45, 50, True) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_next_early_exit), 2) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_next_unpositioned), 2) + + self.set_bounds(cursor, 45, "lower") + self.set_bounds(cursor, 50, "upper") + self.cursor_traversal_bound(cursor, 45, 50, False) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_prev_early_exit), 2) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_prev_unpositioned), 2) + + # Test bound api: Test that cursor bound reset stats get incremented when clearing bounds. + self.set_bounds(cursor, 45, "lower") + self.set_bounds(cursor, 50, "upper") + self.assertEqual(cursor.reset(), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_reset), 1) + + # Test bound api: Test that cursor bound search early exit stats get incremented. + self.set_bounds(cursor, 40, "upper") + cursor.set_key(self.gen_key(60)) + ret = cursor.search() + self.assertEqual(ret, wiredtiger.WT_NOTFOUND) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_search_early_exit), 1) + + self.set_bounds(cursor, 30, "lower") + cursor.set_key(self.gen_key(20)) + ret = cursor.search() + self.assertEqual(ret, wiredtiger.WT_NOTFOUND) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertEqual(self.get_stat(stat.conn.cursor_bounds_search_early_exit), 2) + + self.set_bounds(cursor, 30, "lower") + cursor.set_key(self.gen_key(20)) + self.assertEqual(cursor.search_near(), 0) + 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) + + # Test bound api: Test that cursor bound search near reposition exit stats get incremented. + # 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.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.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) + + cursor.close() + + def test_bound_perf_stat_scenario(self): + cursor = self.create_session_and_cursor_timestamp() + + key_count = 900 + # Make sure to run the test with no records visible. + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(50)) + + # Test bound api: Test that cursor bound search near traverses less entries perf on upper bounds. + cursor.set_key(self.gen_key(200)) + self.assertEqual(cursor.search_near(), wiredtiger.WT_NOTFOUND) + skip_count = self.get_stat(stat.conn.cursor_next_skip_total) + self.get_stat(stat.conn.cursor_prev_skip_total) + # This should be equal to roughly key_count * 2 as we're going to traverse the whole + # range forward, and then the whole range backwards. + self.assertGreater(skip_count, 2 * key_count - 200) + + prev_skip_count = skip_count + self.set_bounds(cursor, 300, "upper") + cursor.set_key(self.gen_key(200)) + self.assertEqual(cursor.search_near(), wiredtiger.WT_NOTFOUND) + skip_count = self.get_stat(stat.conn.cursor_next_skip_total) + self.get_stat(stat.conn.cursor_prev_skip_total) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertGreater(skip_count - prev_skip_count, 50 * 2) + + # Test bound api: Test that cursor bound search near traverses less entries perf on lower bounds. + prev_skip_count = skip_count + cursor.set_key(self.gen_key(900)) + self.assertEqual(cursor.search_near(), wiredtiger.WT_NOTFOUND) + skip_count = self.get_stat(stat.conn.cursor_next_skip_total) + self.get_stat(stat.conn.cursor_prev_skip_total) + # This should be equal to roughly key_count * 2 as we're going to traverse the whole + # range forward, and then the whole range backwards. + self.assertGreater(skip_count - prev_skip_count, 2 * key_count - 900) + + prev_skip_count = skip_count + self.set_bounds(cursor, 900, "lower") + cursor.set_key(self.gen_key(900)) + self.assertEqual(cursor.search_near(), wiredtiger.WT_NOTFOUND) + skip_count = self.get_stat(stat.conn.cursor_next_skip_total) + self.get_stat(stat.conn.cursor_prev_skip_total) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertGreater(skip_count - prev_skip_count, 100) + + # Test bound api: Test that cursor bound search near traverses less entries perf on both bounds. + prev_skip_count = skip_count + cursor.set_key(self.gen_key(750)) + self.assertEqual(cursor.search_near(), wiredtiger.WT_NOTFOUND) + skip_count = self.get_stat(stat.conn.cursor_next_skip_total) + self.get_stat(stat.conn.cursor_prev_skip_total) + # This should be equal to roughly key_count * 2 as we're going to traverse the whole + # range forward, and then the whole range backwards. + self.assertGreater(skip_count - prev_skip_count, 2 * key_count - 750) + + prev_skip_count = skip_count + self.set_bounds(cursor, 600, "lower") + self.set_bounds(cursor, 900, "upper") + cursor.set_key(self.gen_key(750)) + self.assertEqual(cursor.search_near(), wiredtiger.WT_NOTFOUND) + skip_count = self.get_stat(stat.conn.cursor_next_skip_total) + self.get_stat(stat.conn.cursor_prev_skip_total) + self.assertEqual(cursor.bound("action=clear"), 0) + self.assertGreater(skip_count - prev_skip_count, 150 * 2) + + cursor.close() + +if __name__ == '__main__': + wttest.run() diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound09.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound09.py new file mode 100644 index 00000000000..2ffc013c814 --- /dev/null +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound09.py @@ -0,0 +1,217 @@ +#!/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 wiredtiger import wiredtiger_strerror, WT_PREPARE_CONFLICT, WiredTigerError +from wtscenario import make_scenarios +from wtbound import bound_base + +# test_cursor_bound09.py +# Test that cursor API usage generates expected error in prepared state with bounded cursors. +class test_cursor_bound09(bound_base): + file_name = 'test_cursor_bound09' + value_format= 'S' + + types = [ + ('file', dict(uri='file:', use_colgroup=False)), + ('table', dict(uri='table:', use_colgroup=False)), + ('colgroup', dict(uri='table:', use_colgroup=True)) + ] + + key_format_values = [ + ('string', dict(key_format='S')), + # FIXME-WT-9489: Uncomment once column store search near is implemented. + # ('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')), + ] + + config = [ + ('inclusive-evict', dict(lower_inclusive=True,upper_inclusive=True,evict=True)), + ('no-inclusive-evict', dict(lower_inclusive=False,upper_inclusive=False,evict=True)), + ('lower-inclusive-evict', dict(lower_inclusive=True,upper_inclusive=False,evict=True)), + ('upper-inclusive-evict', dict(lower_inclusive=False,upper_inclusive=True,evict=True)), + ('inclusive-no-evict', dict(lower_inclusive=True,upper_inclusive=True,evict=False)), + ('lower-inclusive-no-evict', dict(lower_inclusive=True,upper_inclusive=False,evict=False)), + ('no-inclusive-no-evict', dict(lower_inclusive=False,upper_inclusive=False,evict=False)) + ] + + ignore_prepare = [ + ('ignore_prepare',dict(ignore_prepare=True)), + ('no_ignore_prepare',dict(ignore_prepare=False)) + ] + + scenarios = make_scenarios(types, key_format_values, config, ignore_prepare) + + # Test ignore prepare. + def test_cursor_bound_prepared(self): + + # Scenario 1: Prepare conflict on search, search_near and next with key set in middle of bounds. + cursor = self.create_session_and_cursor() + session2 = self.conn.open_session() + + # Prepare keys 30-35 + self.session.begin_transaction() + for i in range (30,36): + cursor[self.gen_key(i)] = "updated value" + str(i) + self.session.prepare_transaction('prepare_timestamp=2') + + if (self.ignore_prepare): + session2.begin_transaction('ignore_prepare=true') + else: + session2.begin_transaction() + + cursor2 = session2.open_cursor(self.uri + self.file_name) + cursor_ops = [cursor2.search, cursor2.search_near, cursor2.next] + + for op in cursor_ops: + cursor2.reset() + self.set_bounds(cursor2, 20, "lower", self.lower_inclusive) + self.set_bounds(cursor2, 40, "upper", self.upper_inclusive) + cursor2.set_key(self.gen_key(30)) + + try: + if(self.ignore_prepare): + self.assertEqual(op(),0) + else: + op() + except WiredTigerError as e: + if wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e): + pass + else: + raise e + session2.rollback_transaction() + + # Scenario 2: Prepare conflict on search, search_near and next with key set on the bounds. + if (self.ignore_prepare): + session2.begin_transaction('ignore_prepare=true') + else: + session2.begin_transaction() + cursor_ops = [cursor2.search_near, cursor2.search, cursor2.next] + + for op in cursor_ops: + cursor2.reset() + self.set_bounds(cursor2, 30, "lower", self.lower_inclusive) + self.set_bounds(cursor2, 40, "upper", self.upper_inclusive) + cursor2.set_key(self.gen_key(30)) + try: + if (self.ignore_prepare): + # Search on the non-inclusive lower bound is not expected to suceed. + if (not self.lower_inclusive and op == cursor2.search): + self.assertEqual(op(), wiredtiger.WT_NOTFOUND) + else: + self.assertGreaterEqual(op(),0) + else: + op() + except WiredTigerError as e: + if wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e): + pass + else: + raise e + session2.rollback_transaction() + + # Scenario 3: Prepare conflict with prev. Validate keys that aren't in prepared range. + if (self.ignore_prepare): + session2.begin_transaction('ignore_prepare=true') + else: + session2.begin_transaction() + + cursor2.reset() + self.set_bounds(cursor2, 30, "lower", self.lower_inclusive) + self.set_bounds(cursor2, 40, "upper", self.upper_inclusive) + + while True: + try: + if (self.ignore_prepare): + ret = cursor2.prev() + if(ret == wiredtiger.WT_NOTFOUND): + break + else: + cursor2.prev() + self.assertGreaterEqual(cursor2.get_key(),self.check_key(35)) + except WiredTigerError as e: + if wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e): + break + else: + raise (e) + session2.rollback_transaction() + + # Scenario 4: Prepare conflict with search_near out of the bounds. + self.session.rollback_transaction() + + # Prepare keys 29-30 + self.session.begin_transaction() + for i in range (29,31): + cursor[self.gen_key(i)] = "updated value" + str(i) + self.session.prepare_transaction('prepare_timestamp=2') + + if (self.ignore_prepare): + session2.begin_transaction('ignore_prepare=true') + else: + session2.begin_transaction() + cursor2.reset() + self.set_bounds(cursor2, 30, "lower", self.lower_inclusive) + self.set_bounds(cursor2, 40, "upper", self.upper_inclusive) + cursor2.set_key(self.gen_key(20)) + + try: + if (not(self.ignore_prepare)): + self.assertGreaterEqual(cursor2.search_near(), 0) + self.assertEqual(cursor2.get_key(), self.check_key(31)) + except WiredTigerError as e: + if wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e): + self.assertEqual(cursor2.get_key(),self.check_key(20)) + pass + else: + raise e + session2.rollback_transaction() + + # Scenario 4: Prepare conflict with next with non-inclusive bounds. + if (self.ignore_prepare): + session2.begin_transaction('ignore_prepare=true') + else: + session2.begin_transaction() + cursor2.reset() + self.set_bounds(cursor2, 30, "lower", inclusive = False) + self.set_bounds(cursor2, 40, "upper", inclusive = False) + + try: + cursor2.next() + except WiredTigerError as e: + if wiredtiger_strerror(WT_PREPARE_CONFLICT) in str(e): + self.assertEqual(cursor2.get_key(),self.check_key(30)) + pass + else: + raise e + session2.rollback_transaction() + +if __name__ == '__main__': + wttest.run() diff --git a/src/third_party/wiredtiger/test/suite/test_cursor_bound10.py b/src/third_party/wiredtiger/test/suite/test_cursor_bound10.py new file mode 100644 index 00000000000..f45e25606a4 --- /dev/null +++ b/src/third_party/wiredtiger/test/suite/test_cursor_bound10.py @@ -0,0 +1,178 @@ +#!/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_bound10.py +# Test next/prev history store scenarios with cursor bound API. +class test_cursor_bound10(bound_base): + file_name = 'test_cursor_bound10' + lower_inclusive = True + upper_inclusive = True + + types = [ + ('file', dict(uri='file:', use_colgroup=False)), + ('table', dict(uri='table:', use_colgroup=False)), + ('colgroup', dict(uri='table:', use_colgroup=True)) + ] + + key_format_values = [ + ('var', dict(key_format='r')), + ('int', dict(key_format='i')), + ('composite_int_string', dict(key_format='iS')), + ('composite_complex', dict(key_format='iSru')), + ] + + config = [ + ('evict', dict(evict=True)), + ('no-evict', dict(evict=False)), + ] + + direction = [ + ('prev', dict(next=False)), + ('next', dict(next=True)), + ] + + scenarios = make_scenarios(types, key_format_values, config, direction) + + def create_session_and_cursor(self): + uri = self.uri + self.file_name + create_params = 'value_format=S,key_format={}'.format(self.key_format) + self.session.create(uri, create_params) + + cursor = self.session.open_cursor(uri) + self.session.begin_transaction() + for i in range(1, 101): + cursor[self.gen_key(i)] = "value" + str(i) + self.session.commit_transaction('commit_timestamp=' + self.timestamp_str(50)) + + self.session.begin_transaction() + for i in range(101, 601): + cursor[self.gen_key(i)] = "value" + str(i) + self.session.commit_transaction('commit_timestamp=' + self.timestamp_str(200)) + + self.session.begin_transaction() + for i in range(601, 1001): + cursor[self.gen_key(i)] = "value" + str(i) + self.session.commit_transaction('commit_timestamp=' + self.timestamp_str(100)) + + if (self.evict): + evict_cursor = self.session.open_cursor(uri, None, "debug=(release_evict)") + for i in range(1, 1001): + evict_cursor.set_key(self.gen_key(i)) + evict_cursor.search() + evict_cursor.reset() + return cursor + + def test_bound_general_scenario(self): + cursor = self.create_session_and_cursor() + + # Test bound api: Test early exit works with upper bound. + self.set_bounds(cursor, 900, "upper") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(10)) + self.cursor_traversal_bound(cursor, None, 900, self.next, 0) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 900, "upper") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(75)) + self.cursor_traversal_bound(cursor, None, 900, self.next, 100) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 900, "upper") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(150)) + self.cursor_traversal_bound(cursor, None, 900, self.next, 400) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 900, "upper") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(250)) + self.cursor_traversal_bound(cursor, None, 900, self.next, 900) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + # Test bound api: Test traversal works with lower bound. + self.set_bounds(cursor, 50, "lower") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(10)) + self.cursor_traversal_bound(cursor, 50, None, self.next, 0) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 50, "lower") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(75)) + self.cursor_traversal_bound(cursor, 50, None, self.next, 51) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 50, "lower") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(150)) + self.cursor_traversal_bound(cursor, 50, None, self.next, 451) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 50, "lower") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(250)) + self.cursor_traversal_bound(cursor, 50, None, self.next, 951) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + # Test bound api: Test traversal with both bounds. + self.set_bounds(cursor, 50, "lower") + self.set_bounds(cursor, 900, "upper") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(10)) + self.cursor_traversal_bound(cursor, 50, 900, self.next, 0) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 50, "lower") + self.set_bounds(cursor, 900, "upper") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(75)) + self.cursor_traversal_bound(cursor, 50, 900, self.next, 51) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 50, "lower") + self.set_bounds(cursor, 900, "upper") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(150)) + self.cursor_traversal_bound(cursor, 50, 900, self.next, 351) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + + self.set_bounds(cursor, 50, "lower") + self.set_bounds(cursor, 900, "upper") + self.session.begin_transaction('read_timestamp=' + self.timestamp_str(250)) + self.cursor_traversal_bound(cursor, 50, 900, self.next, 851) + self.session.commit_transaction() + self.assertEqual(cursor.bound("action=clear"), 0) + cursor.close() + +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 8782c06640f..610972a767d 100644 --- a/src/third_party/wiredtiger/test/suite/wtbound.py +++ b/src/third_party/wiredtiger/test/suite/wtbound.py @@ -46,6 +46,8 @@ class bound(): self.key = key self.inclusive = inclusive self.enabled = enabled + self.key_format = 'S' + self.value_format = 'S' def to_string(self): return "Enabled: " + str(self.enabled) + ", Key: " + str(self.key) + ", incl: " + self.inclusive_str() @@ -100,18 +102,60 @@ class bounds(): # Shared base class used by cursor bound tests. class bound_base(wttest.WiredTigerTestCase): + # The start and end key denotes the first and last key in the table. Since 20 is a key itself, + # there are 60 entries between the start and end key. start_key = 20 - end_key = 80 - lower_inclusve = True + end_key = 79 + lower_inclusive = True upper_inclusive = True + def create_session_and_cursor(self): + 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() + self.session.create(uri, create_params) + + # Add in column group. + if self.use_colgroup: + for i in range(0, len(self.value_format)): + create_params = 'columns=(v{0}),'.format(i) + suburi = 'colgroup:{0}:g{1}'.format(self.file_name, i) + self.session.create(suburi, create_params) + + cursor = self.session.open_cursor(uri) + 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)) + self.session.commit_transaction() + + if (self.evict): + evict_cursor = self.session.open_cursor(uri, None, "debug=(release_evict)") + for i in range(self.start_key, self.end_key): + evict_cursor.set_key(self.gen_key(i)) + evict_cursor.search() + evict_cursor.reset() + return cursor + def gen_colgroup_create_param(self): create_params = ",columns=(" start = 0 for _ in self.key_format: create_params += "k{0},".format(str(start)) start += 1 - create_params += "v),colgroups=(g0)" + + start = 0 + for _ in self.value_format: + create_params += "v{0},".format(str(start)) + start += 1 + 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): @@ -129,6 +173,21 @@ class bound_base(wttest.WiredTigerTestCase): else: return tuple(tuple_key) + def gen_val(self, i): + tuple_val = [] + for key in self.value_format: + if key == 'S' or key == 'u': + tuple_val.append(str(i)) + elif key == "r": + tuple_val.append(self.recno(i)) + elif key == "i": + tuple_val.append(i) + + if (len(self.value_format) == 1): + return tuple_val[0] + else: + return tuple(tuple_val) + def check_key(self, i): list_key = [] for key in self.key_format: @@ -175,15 +234,16 @@ class bound_base(wttest.WiredTigerTestCase): if (upper_key): if (upper_key < end_range): end_range = upper_key - if (self.upper_inclusive == False): + if (not self.upper_inclusive): end_range -= 1 - + if (lower_key): if (lower_key > start_range): start_range = lower_key - if (self.lower_inclusive == False): + if (not self.lower_inclusive): start_range += 1 + count = ret = 0 while True: if (next): @@ -206,8 +266,7 @@ class bound_base(wttest.WiredTigerTestCase): elif (upper_key): self.assertTrue(key < self.check_key(upper_key)) - count = max(count - 1, 0) if (expected_count != None): self.assertEqual(expected_count, count) else: - self.assertEqual(end_range - start_range, count) + self.assertEqual(end_range - start_range + 1, count) |