1#!/usr/bin/env python
2#
3# Public Domain 2014-2018 MongoDB, Inc.
4# Public Domain 2008-2014 WiredTiger, Inc.
5#
6# This is free and unencumbered software released into the public domain.
7#
8# Anyone is free to copy, modify, publish, use, compile, sell, or
9# distribute this software, either in source code form or as a compiled
10# binary, for any purpose, commercial or non-commercial, and by any
11# means.
12#
13# In jurisdictions that recognize copyright laws, the author or authors
14# of this software dedicate any and all copyright interest in the
15# software to the public domain. We make this dedication for the benefit
16# of the public at large and to the detriment of our heirs and
17# successors. We intend this dedication to be an overt act of
18# relinquishment in perpetuity of all present and future rights to this
19# software under copyright law.
20#
21# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
24# IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
25# OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
26# ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27# OTHER DEALINGS IN THE SOFTWARE.
28
29import wiredtiger, wttest
30from wtscenario import make_scenarios
31
32# test_base04.py
33#     Cursor operations
34class test_cursor04(wttest.WiredTigerTestCase):
35    """
36    Test cursor search and search_near
37    """
38    table_name1 = 'test_cursor04'
39    nentries = 20
40
41    scenarios = make_scenarios([
42        ('row', dict(tablekind='row', uri='table')),
43        ('lsm-row', dict(tablekind='row', uri='lsm')),
44        ('col', dict(tablekind='col', uri='table')),
45        ('fix', dict(tablekind='fix', uri='table'))
46    ])
47
48    def config_string(self):
49        """
50        Return any additional configuration.
51        This method may be overridden.
52        """
53        return ''
54
55    def session_create(self, name, args):
56        """
57        session.create, but report errors more completely
58        """
59        try:
60            self.session.create(name, args)
61        except:
62            print('**** ERROR in session.create("' + name + '","' + args + '") ***** ')
63            raise
64
65    def create_session_and_cursor(self):
66        tablearg = self.uri + ":" + self.table_name1
67        if self.tablekind == 'row':
68            keyformat = 'key_format=S'
69        else:
70            keyformat = 'key_format=r'  # record format
71        if self.tablekind == 'fix':
72            valformat = 'value_format=8t'
73        else:
74            valformat = 'value_format=S'
75        create_args = keyformat + ',' + valformat + self.config_string()
76        self.pr('creating session: ' + create_args)
77        self.session_create(tablearg, create_args)
78        self.pr('creating cursor')
79        return self.session.open_cursor(tablearg, None, None)
80
81    def genkey(self, i):
82        if self.tablekind == 'row':
83            return 'key' + str(i).zfill(5)  # return key00001, key00002, etc.
84        else:
85            return long(i+1)
86
87    def genvalue(self, i):
88        if self.tablekind == 'fix':
89            return int(i & 0xff)
90        else:
91            return 'value' + str(i)
92
93    def expect_either(self, cursor, lt, gt):
94        origkey = cursor.get_key()
95        direction = cursor.search_near()
96        self.assertNotEqual(direction, wiredtiger.WT_NOTFOUND)
97
98        # Deletions for 'fix' clear the value, they
99        # do not remove the key, so we expect '0' direction
100        # (that is key found) for fix.
101        if self.tablekind != 'fix':
102            self.assertTrue(direction == 1 or direction == -1)
103        else:
104            self.assertEqual(direction, 0)
105
106        if direction == 1:
107            self.assertEqual(cursor.get_key(), self.genkey(gt))
108            self.assertEqual(cursor.get_value(), self.genvalue(gt))
109        elif direction == -1:
110            self.assertEqual(cursor.get_key(), self.genkey(lt))
111            self.assertEqual(cursor.get_value(), self.genvalue(lt))
112        else:
113            self.assertEqual(direction, 0)
114            self.assertEqual(cursor.get_key(), origkey)
115            self.assertEqual(cursor.get_value(), 0)
116
117    def test_searches(self):
118        """
119        Create entries, and read back in a cursor: key=string, value=string
120        """
121        cursor = self.create_session_and_cursor()
122
123        # Some tests below expect keys between 0-10 to be available
124        self.assertTrue(self.nentries > 10)
125
126        # 0. Populate the key space
127        for i in range(0, self.nentries):
128            cursor[self.genkey(i)] = self.genvalue(i)
129
130        # 1. Calling search for a value that exists
131        self.assertEqual(cursor[self.genkey(5)], self.genvalue(5))
132
133        # 2. Calling search for a value that does not exist
134        self.assertRaises(KeyError, lambda: cursor[self.genkey(self.nentries)])
135
136        # 2. Calling search_near for a value beyond the end
137        cursor.set_key(self.genkey(self.nentries))
138        cmp = cursor.search_near()
139        self.assertEqual(cmp, -1)
140        self.assertEqual(cursor.get_key(), self.genkey(self.nentries-1))
141        self.assertEqual(cursor.get_value(), self.genvalue(self.nentries-1))
142
143        # 2.a calling search_near for an existing value
144        cursor.set_key(self.genkey(7))
145        cmp = cursor.search_near()
146        self.assertEqual(cmp, 0)
147        self.assertEqual(cursor.get_key(), self.genkey(7))
148        self.assertEqual(cursor.get_value(), self.genvalue(7))
149
150        # 3. Delete some keys
151        # Deletions for 'fix' clear the value, they
152        # do not remove the key
153        cursor.set_key(self.genkey(0))
154        cursor.remove()
155        cursor.set_key(self.genkey(5))
156        cursor.remove()
157        cursor.set_key(self.genkey(9))
158        cursor.remove()
159        cursor.set_key(self.genkey(10))
160        cursor.remove()
161
162        #cursor.reset()
163        #for key, value in cursor:
164        #    print('key: ' + str(key))
165        #    print('value: ' + str(value))
166
167        cursor.set_key(self.genkey(0))
168        cmp = cursor.search_near()
169        if self.tablekind != 'fix':
170            self.assertEqual(cmp, 1)
171            self.assertEqual(cursor.get_key(), self.genkey(1))
172            self.assertEqual(cursor.get_value(), self.genvalue(1))
173        else:
174            self.assertEqual(cmp, 0)
175            self.assertEqual(cursor.get_key(), self.genkey(0))
176            self.assertEqual(cursor.get_value(), 0)
177
178        cursor.set_key(self.genkey(5))
179        self.expect_either(cursor, 4, 6)
180
181        cursor.set_key(self.genkey(9))
182        self.expect_either(cursor, 8, 11)
183
184        cursor.set_key(self.genkey(10))
185        self.expect_either(cursor, 8, 11)
186
187        cursor.close()
188
189if __name__ == '__main__':
190    wttest.run()
191