Skip to content

Commit c957403

Browse files
quchenhaoEvergreen Agent
authored andcommitted
Import wiredtiger: 4c6b28fb2bdd12681391b1e1eb11070d391aae57 from branch mongodb-4.6
ref: c97151853e..4c6b28fb2b for: 4.5.1 WT-6511 cursor join: explicitly advance the iterator when finished with a clause
1 parent 74ab0cd commit c957403

File tree

3 files changed

+199
-3
lines changed

3 files changed

+199
-3
lines changed

src/third_party/wiredtiger/import.data

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,5 @@
22
"vendor": "wiredtiger",
33
"github": "wiredtiger/wiredtiger.git",
44
"branch": "mongodb-4.6",
5-
"commit": "c97151853eb2e8f66e14c9671e120ba755675dba"
5+
"commit": "4c6b28fb2bdd12681391b1e1eb11070d391aae57"
66
}

src/third_party/wiredtiger/src/cursor/cur_join.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -201,7 +201,8 @@ __curjoin_iter_set_entry(WT_CURSOR_JOIN_ITER *iter, u_int entry_pos)
201201
/*
202202
* __curjoin_iter_bump --
203203
* Called to advance the iterator to the next endpoint, which may in turn advance to the next
204-
* entry.
204+
* entry. We cannot skip a call to this at the end of an iteration because we'll need to advance
205+
* the position to the end.
205206
*/
206207
static int
207208
__curjoin_iter_bump(WT_CURSOR_JOIN_ITER *iter)
@@ -482,8 +483,14 @@ __curjoin_entry_in_range(
482483
}
483484

484485
if (!passed) {
485-
if (iter != NULL && (iter->is_equal || F_ISSET(end, WT_CURJOIN_END_LT)))
486+
if (iter != NULL && (iter->is_equal || F_ISSET(end, WT_CURJOIN_END_LT))) {
487+
/*
488+
* Even though this cursor is done, we still need to bump (advance it), to mark the
489+
* iteration as complete.
490+
*/
491+
WT_RET(__curjoin_iter_bump(iter));
486492
return (WT_NOTFOUND);
493+
}
487494
if (!disjunction)
488495
return (WT_NOTFOUND);
489496
iter = NULL;
Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
#!/usr/bin/env python
2+
#
3+
# Public Domain 2014-2020 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+
import wiredtiger, wttest
30+
from wtscenario import make_scenarios
31+
32+
# test_join10.py
33+
# Test modeled on the C example for join.
34+
class test_join10(wttest.WiredTigerTestCase):
35+
36+
# We need statistics for these tests.
37+
conn_config = 'statistics=(all)'
38+
39+
def check_stats(self, jc, expect):
40+
statcursor = self.session.open_cursor('statistics:join', jc, None)
41+
for id, desc, valstr, val in statcursor:
42+
if desc in expect:
43+
expect_val = expect.pop(desc)
44+
self.assertEqual(val, expect_val)
45+
self.assertTrue(len(expect) == 0,
46+
'missing expected values in stats: ' + str(expect))
47+
statcursor.close()
48+
49+
def test_country(self):
50+
session = self.session
51+
pop_data = [["AU", 1900, 4000000],
52+
["AU", 1950, 8267337],
53+
["AU", 2000, 19053186],
54+
["CAN", 1900, 5500000],
55+
["CAN", 1950, 14011422],
56+
["CAN", 2000, 31099561],
57+
["UK", 1900, 369000000],
58+
["UK", 1950, 50127000],
59+
["UK", 2000, 59522468],
60+
["USA", 1900, 76212168],
61+
["USA", 1950, 150697361],
62+
["USA", 2000, 301279593]]
63+
64+
session.create("table:poptable", \
65+
"key_format=r,value_format=SHQ," +
66+
"columns=(id,country,year,population),colgroups=(main,population)")
67+
68+
session.create("colgroup:poptable:main", "columns=(country,year,population)")
69+
session.create("colgroup:poptable:population", "columns=(population)")
70+
71+
session.create("index:poptable:country", "columns=(country)")
72+
session.create("index:poptable:immutable_year", "columns=(year),immutable")
73+
74+
cursor = session.open_cursor("table:poptable", None, "append")
75+
for p in pop_data:
76+
cursor.set_value(p[0], p[1], p[2])
77+
cursor.insert()
78+
cursor.close()
79+
80+
join_cursor = session.open_cursor("join:table:poptable")
81+
country_cursor = session.open_cursor("index:poptable:country")
82+
year_cursor = session.open_cursor("index:poptable:immutable_year")
83+
84+
# select values WHERE country == "AU" AND year > 1900
85+
country_cursor.set_key("AU")
86+
self.assertEqual(country_cursor.search(), 0)
87+
session.join(join_cursor, country_cursor, "compare=eq,count=10")
88+
year_cursor.set_key(1900)
89+
self.assertEqual(year_cursor.search(), 0)
90+
session.join(join_cursor, year_cursor, "compare=gt,count=10,strategy=bloom")
91+
92+
# Check results
93+
expect = [[c,y,p] for c,y,p in pop_data if c == "AU" and y > 1900]
94+
got = []
95+
for recno, country, year, population in join_cursor:
96+
got.append([country, year, population])
97+
self.assertEqual(expect, got)
98+
99+
# Check statistics
100+
# It may seem odd to encode specific values to check against, but each of these
101+
# statistics represent significant and predictable events in the code, and we
102+
# want to know if anything changes.
103+
expect_stat = dict()
104+
pfxc = 'join: index:poptable:country: '
105+
pfxy = 'join: index:poptable:immutable_year: '
106+
107+
expect_stat[pfxc + 'accesses to the main table'] = 2
108+
expect_stat[pfxc + 'bloom filter false positives'] = 0
109+
expect_stat[pfxc + 'checks that conditions of membership are satisfied'] = 4
110+
expect_stat[pfxc + 'items inserted into a bloom filter'] = 0
111+
expect_stat[pfxc + 'items iterated'] = 4
112+
113+
# We're using a bloom filter on this one, but we don't check for bloom filter
114+
# false positives, it's a bit tied to implementation details.
115+
expect_stat[pfxy + 'accesses to the main table'] = 2
116+
expect_stat[pfxy + 'checks that conditions of membership are satisfied'] = 3
117+
expect_stat[pfxy + 'items inserted into a bloom filter'] = 8
118+
expect_stat[pfxy + 'items iterated'] = 12
119+
120+
self.check_stats(join_cursor, expect_stat)
121+
122+
join_cursor.close()
123+
year_cursor.close()
124+
country_cursor.close()
125+
126+
# Complex join cursors
127+
join_cursor = session.open_cursor("join:table:poptable")
128+
subjoin_cursor = session.open_cursor("join:table:poptable")
129+
130+
country_cursor = session.open_cursor("index:poptable:country")
131+
country_cursor2 = session.open_cursor("index:poptable:country")
132+
year_cursor = session.open_cursor("index:poptable:immutable_year")
133+
134+
# select values WHERE (country == "AU" OR country == "UK")
135+
# AND year > 1900
136+
137+
# First, set up the join representing the country clause.
138+
country_cursor.set_key("AU")
139+
self.assertEqual(country_cursor.search(), 0)
140+
session.join(subjoin_cursor, country_cursor, "operation=or,compare=eq,count=10")
141+
142+
country_cursor2.set_key("UK")
143+
self.assertEqual(country_cursor2.search(), 0)
144+
session.join(subjoin_cursor, country_cursor2, "operation=or,compare=eq,count=10")
145+
146+
# Join that to the top join, and add the year clause
147+
session.join(join_cursor, subjoin_cursor)
148+
year_cursor.set_key(1900)
149+
self.assertEqual(year_cursor.search(), 0)
150+
151+
session.join(join_cursor, year_cursor, "compare=gt,count=10,strategy=bloom")
152+
153+
# Check results
154+
expect = [[c,y,p] for c,y,p in pop_data if (c == "AU" or c == "UK") and y > 1900]
155+
got = []
156+
for recno, country, year, population in join_cursor:
157+
got.append([country, year, population])
158+
self.assertEqual(expect, got)
159+
160+
expect_stat = dict()
161+
162+
# Note: the stats collected for the clause (country == "AU" OR country == "UK")
163+
# are in a join entry that is a "subjoin". Due to a quirk in the implementation
164+
# of join statistics, subjoin statistics are returned using the main table prefix.
165+
pfxm = 'join: table:poptable: '
166+
pfxy = 'join: index:poptable:immutable_year: '
167+
168+
expect_stat[pfxm + 'accesses to the main table'] = 4
169+
expect_stat[pfxm + 'bloom filter false positives'] = 0
170+
expect_stat[pfxm + 'checks that conditions of membership are satisfied'] = 12
171+
expect_stat[pfxm + 'items inserted into a bloom filter'] = 0
172+
expect_stat[pfxm + 'items iterated'] = 0
173+
174+
expect_stat[pfxy + 'accesses to the main table'] = 6
175+
expect_stat[pfxy + 'bloom filter false positives'] = 0
176+
expect_stat[pfxy + 'checks that conditions of membership are satisfied'] = 6
177+
expect_stat[pfxy + 'items inserted into a bloom filter'] = 0
178+
expect_stat[pfxy + 'items iterated'] = 0
179+
180+
self.check_stats(join_cursor, expect_stat)
181+
182+
join_cursor.close()
183+
subjoin_cursor.close()
184+
country_cursor.close()
185+
country_cursor2.close()
186+
year_cursor.close()
187+
188+
if __name__ == '__main__':
189+
wttest.run()

0 commit comments

Comments
 (0)