Files
mongo/test/suite/test_join08.py
Don Anderson a94aa935f0 WT-2571 In join tests, use scenarios instead of lots of nested loops.
Move miscellaneous tests that don't need scenarios to their own test class.
2016-04-22 13:38:50 -04:00

266 lines
11 KiB
Python

#!/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
from wtscenario import check_scenarios, multiply_scenarios, number_scenarios
# 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()