summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChenhao Qu <chenhao.qu@mongodb.com>2022-08-03 12:55:53 +1000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2022-08-03 03:24:43 +0000
commitccddad0fd0ce788c9926416c61e518fb0e34086b (patch)
treeef136e4e67e5f0f469c275adaf3add19631eddad
parent9ce3d41ad2eebe251e6423aecb01508234bc9b95 (diff)
downloadmongo-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)
-rw-r--r--src/third_party/wiredtiger/import.data2
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound02.py47
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound03.py38
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound04.py44
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound05.py37
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound06.py39
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound07.py31
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound08.py252
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound09.py217
-rw-r--r--src/third_party/wiredtiger/test/suite/test_cursor_bound10.py178
-rw-r--r--src/third_party/wiredtiger/test/suite/wtbound.py75
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)