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