#!/usr/bin/env python # # Public Domain 2014-2016 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 # test_join08.py # Test join error paths class test_join08(wttest.WiredTigerTestCase): nentries = 100 # We need statistics for these tests. conn_config = 'statistics=(all)' def gen_key(self, i): return [ i + 1 ] def gen_values(self, i): s = str(i) rs = s[::-1] sort3 = (self.nentries * (i % 3)) + i # multiples of 3 sort first return [s, rs, sort3] def test_join_errors(self): self.session.create('table:join08', 'key_format=r,value_format=SS' ',columns=(k,v0,v1)') self.session.create('table:join08B', 'key_format=r,value_format=SS' ',columns=(k,v0,v1)') self.session.create('index:join08:index0','columns=(v0)') self.session.create('index:join08:index1','columns=(v1)') self.session.create('index:join08B:index0','columns=(v0)') jc = self.session.open_cursor('join:table:join08', None, None) tc = self.session.open_cursor('table:join08', None, None) fc = self.session.open_cursor('file:join08.wt', None, None) ic0 = self.session.open_cursor('index:join08:index0', None, None) ic0again = self.session.open_cursor('index:join08:index0', None, None) ic1 = self.session.open_cursor('index:join08:index1', None, None) icB = self.session.open_cursor('index:join08B:index0', None, None) tcB = self.session.open_cursor('table:join08B', None, None) tc.set_key(1) tc.set_value('val1', 'val1') tc.insert() tcB.set_key(1) tcB.set_value('val1', 'val1') tcB.insert() fc.next() # Joining using a non join-cursor self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(tc, ic0, 'compare=ge'), '/not a join cursor/') # Joining a table cursor, not index self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, fc, 'compare=ge'), '/must be an index, table or join cursor/') # Joining a non positioned cursor self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0, 'compare=ge'), '/requires reference cursor be positioned/') ic0.set_key('val1') # Joining a non positioned cursor (no search or next has been done) self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0, 'compare=ge'), '/requires reference cursor be positioned/') ic0.set_key('valXX') self.assertEqual(ic0.search(), wiredtiger.WT_NOTFOUND) # Joining a non positioned cursor after failed search self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0, 'compare=ge'), '/requires reference cursor be positioned/') # position the cursors now ic0.set_key('val1') ic0.search() ic0again.next() icB.next() # Joining non matching index self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, icB, 'compare=ge'), '/table for join cursor does not match/') # The cursor must be positioned self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic1, 'compare=ge'), '/requires reference cursor be positioned/') ic1.next() # This succeeds. self.session.join(jc, ic1, 'compare=ge'), # With bloom filters, a count is required self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0, 'compare=ge,strategy=bloom'), '/count must be nonzero/') # This succeeds. self.session.join(jc, ic0, 'compare=ge,strategy=bloom,count=1000'), bloom_config = ',strategy=bloom,count=1000' # Cannot use the same index cursor self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0, 'compare=le' + bloom_config), '/cursor already used in a join/') # When joining with the same index, need compatible compares self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0again, 'compare=ge' + bloom_config), '/join has overlapping ranges/') # Another incompatible compare self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0again, 'compare=gt' + bloom_config), '/join has overlapping ranges/') # Compare is compatible, but bloom args need to match self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0again, 'compare=le'), '/join has incompatible strategy/') # Counts need to match for bloom filters self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: self.session.join(jc, ic0again, 'compare=le,strategy=bloom,' 'count=100'), '/count.* does not match previous count/') # This succeeds self.session.join(jc, ic0again, 'compare=le,strategy=bloom,count=1000') # Need to do initial next() before getting key/values self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: jc.get_keys(), '/join cursor must be advanced with next/') self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: jc.get_values(), '/join cursor must be advanced with next/') # Operations on the joined cursor are frozen until the join is closed. self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: ic0.next(), '/cursor is being used in a join/') # Operations on the joined cursor are frozen until the join is closed. self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: ic0.prev(), '/cursor is being used in a join/') self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: ic0.reset(), '/cursor is being used in a join/') # Only a small number of operations allowed on a join cursor msg = "/Unsupported cursor/" self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: jc.search(), msg) self.assertRaisesWithMessage(wiredtiger.WiredTigerError, lambda: jc.prev(), msg) self.assertEquals(jc.next(), 0) self.assertEquals(jc.next(), wiredtiger.WT_NOTFOUND) # Only after the join cursor is closed can we use the index cursor # normally jc.close() self.assertEquals(ic0.next(), wiredtiger.WT_NOTFOUND) self.assertEquals(ic0.prev(), 0) # common code for making sure that cursors can be # implicitly closed, no matter the order they are created def cursor_close_common(self, joinfirst): self.session.create('table:join08', 'key_format=r' + ',value_format=SS,columns=(k,v0,v1)') self.session.create('index:join08:index0','columns=(v0)') self.session.create('index:join08:index1','columns=(v1)') c = self.session.open_cursor('table:join08', None, None) for i in range(0, self.nentries): c.set_key(*self.gen_key(i)) c.set_value(*self.gen_values(i)) c.insert() c.close() if joinfirst: jc = self.session.open_cursor('join:table:join08', None, None) c0 = self.session.open_cursor('index:join08:index0', None, None) c1 = self.session.open_cursor('index:join08:index1', None, None) c0.next() # index cursors must be positioned c1.next() if not joinfirst: jc = self.session.open_cursor('join:table:join08', None, None) self.session.join(jc, c0, 'compare=ge') self.session.join(jc, c1, 'compare=ge') self.session.close() self.session = None def test_cursor_close1(self): self.cursor_close_common(True) def test_cursor_close2(self): self.cursor_close_common(False) # test statistics with a simple one index join cursor def test_simple_stats(self): self.session.create("table:join01b", "key_format=i,value_format=i,columns=(k,v)") self.session.create("index:join01b:index", "columns=(v)") cursor = self.session.open_cursor("table:join01b", None, None) cursor[1] = 11 cursor[2] = 12 cursor[3] = 13 cursor.close() cursor = self.session.open_cursor("index:join01b:index", None, None) cursor.set_key(11) cursor.search() jcursor = self.session.open_cursor("join:table:join01b", None, None) self.session.join(jcursor, cursor, "compare=gt") while jcursor.next() == 0: [k] = jcursor.get_keys() [v] = jcursor.get_values() statcur = self.session.open_cursor("statistics:join", jcursor, None) found = False while statcur.next() == 0: [desc, pvalue, value] = statcur.get_values() #self.tty(str(desc) + "=" + str(pvalue)) found = True self.assertEquals(found, True) jcursor.close() cursor.close() if __name__ == '__main__': wttest.run()