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#
29# test_reserve.py
30#       Reserve update tests.
31
32import wiredtiger, wttest
33from wtdataset import SimpleDataSet, SimpleIndexDataSet
34from wtdataset import SimpleLSMDataSet, ComplexDataSet, ComplexLSMDataSet
35from wtscenario import make_scenarios
36
37# Test WT_CURSOR.reserve.
38class test_reserve(wttest.WiredTigerTestCase):
39
40    keyfmt = [
41        ('integer', dict(keyfmt='i')),
42        ('recno', dict(keyfmt='r')),
43        ('string', dict(keyfmt='S')),
44    ]
45    types = [
46        ('file', dict(uri='file', ds=SimpleDataSet)),
47        ('lsm', dict(uri='lsm', ds=SimpleDataSet)),
48        ('table-complex', dict(uri='table', ds=ComplexDataSet)),
49        ('table-complex-lsm', dict(uri='table', ds=ComplexLSMDataSet)),
50        ('table-index', dict(uri='table', ds=SimpleIndexDataSet)),
51        ('table-simple', dict(uri='table', ds=SimpleDataSet)),
52        ('table-simple-lsm', dict(uri='table', ds=SimpleLSMDataSet)),
53    ]
54    scenarios = make_scenarios(types, keyfmt)
55
56    def skip(self):
57        return self.keyfmt == 'r' and \
58            (self.ds.is_lsm() or self.uri == 'lsm')
59
60    def test_reserve(self):
61        if self.skip():
62            return
63
64        uri = self.uri + ':test_reserve'
65
66        ds = self.ds(self, uri, 500, key_format=self.keyfmt)
67        ds.populate()
68        s = self.conn.open_session()
69        c = s.open_cursor(uri, None)
70
71        # Repeatedly update a record.
72        for i in range(1, 5):
73            s.begin_transaction('isolation=snapshot')
74            c.set_key(ds.key(100))
75            c.set_value(ds.value(100))
76            self.assertEquals(c.update(), 0)
77            s.commit_transaction()
78
79        # Confirm reserve fails if the record doesn't exist.
80        s.begin_transaction('isolation=snapshot')
81        c.set_key(ds.key(600))
82        self.assertRaises(wiredtiger.WiredTigerError, lambda:c.reserve())
83        s.rollback_transaction()
84
85        # Repeatedly reserve a record and commit.
86        for i in range(1, 5):
87            s.begin_transaction('isolation=snapshot')
88            c.set_key(ds.key(100))
89            self.assertEquals(c.reserve(), 0)
90            s.commit_transaction()
91
92        # Repeatedly reserve a record and rollback.
93        for i in range(1, 5):
94            s.begin_transaction('isolation=snapshot')
95            c.set_key(ds.key(100))
96            self.assertEquals(c.reserve(), 0)
97            s.rollback_transaction()
98
99        # Repeatedly reserve, then update, a record, and commit.
100        for i in range(1, 5):
101            s.begin_transaction('isolation=snapshot')
102            c.set_key(ds.key(100))
103            self.assertEquals(c.reserve(), 0)
104            c.set_value(ds.value(100))
105            self.assertEquals(c.update(), 0)
106            s.commit_transaction()
107
108        # Repeatedly reserve, then update, a record, and rollback.
109        for i in range(1, 5):
110            s.begin_transaction('isolation=snapshot')
111            c.set_key(ds.key(100))
112            self.assertEquals(c.reserve(), 0)
113            c.set_value(ds.value(100))
114            self.assertEquals(c.update(), 0)
115            s.commit_transaction()
116
117        # Reserve a slot, repeatedly try and update a record from another
118        # transaction (which should fail), repeatedly update a record and
119        # commit.
120        s2 = self.conn.open_session()
121        c2 = s2.open_cursor(uri, None)
122        for i in range(1, 2):
123            s.begin_transaction('isolation=snapshot')
124            c.set_key(ds.key(100))
125            self.assertEquals(c.reserve(), 0)
126
127            s2.begin_transaction('isolation=snapshot')
128            c2.set_key(ds.key(100))
129            c2.set_value(ds.value(100))
130            self.assertRaises(wiredtiger.WiredTigerError, lambda:c2.update())
131            s2.rollback_transaction()
132
133            c.set_key(ds.key(100))
134            c.set_value(ds.value(100))
135            self.assertEquals(c.update(), 0)
136            s.commit_transaction()
137
138    # Test cursor.reserve will fail if a key has not yet been set.
139    def test_reserve_without_key(self):
140        if self.skip():
141            return
142
143        uri = self.uri + ':test_reserve_without_key'
144
145        ds = self.ds(self, uri, 10, key_format=self.keyfmt)
146        ds.populate()
147        s = self.conn.open_session()
148        c = s.open_cursor(uri, None)
149        s.begin_transaction('isolation=snapshot')
150        msg = "/requires key be set/"
151        self.assertRaisesWithMessage(
152            wiredtiger.WiredTigerError, lambda:c.reserve(), msg)
153
154    # Test cursor.reserve will fail if there's no running transaction.
155    def test_reserve_without_txn(self):
156        if self.skip():
157            return
158
159        uri = self.uri + ':test_reserve_without_txn'
160
161        ds = self.ds(self, uri, 10, key_format=self.keyfmt)
162        ds.populate()
163        s = self.conn.open_session()
164        c = s.open_cursor(uri, None)
165        c.set_key(ds.key(5))
166        msg = "/only permitted in a running transaction/"
167        self.assertRaisesWithMessage(
168            wiredtiger.WiredTigerError, lambda:c.reserve(), msg)
169
170    # Test cursor.reserve returns a value on success.
171    def test_reserve_returns_value(self):
172        if self.skip():
173            return
174
175        uri = self.uri + ':test_reserve_returns_value'
176
177        ds = self.ds(self, uri, 10, key_format=self.keyfmt)
178        ds.populate()
179        s = self.conn.open_session()
180        c = s.open_cursor(uri, None)
181        s.begin_transaction('isolation=snapshot')
182        c.set_key(ds.key(5))
183        self.assertEquals(c.reserve(), 0)
184        self.assertEqual(c.get_value(), ds.comparable_value(5))
185
186    # Test cursor.reserve fails on non-standard cursors.
187    def test_reserve_not_supported(self):
188        if self.skip():
189            return
190
191        uri = self.uri + ':test_reserve_not_supported'
192        s = self.conn.open_session()
193        s.create(uri, 'key_format=' + self.keyfmt + ",value_format=S")
194
195        list = [ "bulk", "dump=json" ]
196        for l in list:
197                c = s.open_cursor(uri, None, l)
198                msg = "/Operation not supported/"
199                self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
200                    lambda:self.assertEquals(c.reserve(), 0), msg)
201                c.close()
202
203        list = [ "backup:", "config:" "log:" "metadata:" "statistics:" ]
204        for l in list:
205                c = s.open_cursor(l, None, None)
206                msg = "/Operation not supported/"
207                self.assertRaisesWithMessage(wiredtiger.WiredTigerError,
208                    lambda:self.assertEquals(c.reserve(), 0), msg)
209
210if __name__ == '__main__':
211    wttest.run()
212