Hi Folks,
I have a virtual table implementation currently based upon 3.6.18 sources. I
have found a bug in the way left outer joins are implemented in SQLITE3 that
causes bad results to occur. The short description of the problem is that the
temporary registers setup to fetch data from the virtual tables overwrite the
register used to store a value as part of the loop. By that I mean an inner
loop overwrites a value that is expected to be there when used by the outer
loop. As such, the result set is not as expected.
The virtual table setup can be emulated with the following SQL:
create table atab(id int, mask text) ;
create table btab(mask text, bid int) ;
create table ctab(bid int, desc text) ;
insert into atab values (1, '1234') ;
insert into atab values (2, '2345') ;
insert into btab values ('1234', 1) ;
insert into btab values ('1234', 2) ;
insert into btab values ('1234', 3) ;
insert into btab values ('1234', 7) ;
insert into ctab values ( 1, 'Text for 1') ;
insert into ctab values ( 2, 'Text for 2') ;
insert into ctab values ( 3, 'Text for 3') ;
select a.id, a.mask, b.mask, b.bid, c.bid, c.desc
from atab as a
left outer join btab as b
on a.mask = b.mask
left outer join ctab as c
on b.bid = c.bid
where
a.id = 1 ;
The following is the "explain" of the query as a virtual table implementation.
The names have been "corrected" to emulate the SQL above, and the column
numbers are obviously a bit different than those found above, but the problem
is demonstrated:
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- --------------------- -- -------------
0 Trace 0 0 0 00 (null)
1 Integer 3 1 0 00 (null)
2 Goto 0 50 0 00 (null)
3 VOpen 0 0 0 vtab:8224638:8101800 00 atab
4 VOpen 1 0 0 vtab:81F90E0:8101800 00 btab
5 VOpen 2 0 0 vtab:81E71C0:8101800 00 ctab
6 SCopy 1 4 0 00 (null)
7 Integer 1 2 0 00 (null)
8 Integer 1 3 0 00 (null)
9 VFilter 0 46 2 00 (null)
10 VColumn 0 0 6 00 atab.id
11 Ne 1 45 6 collseq(BINARY) 6c (null)
12 Integer 0 8 0 00 init LEFT
JOIN no-match flag
13 VColumn 0 3 4 00 atab.mask
14 Integer 1 2 0 00 (null)
15 Integer 1 3 0 00 (null)
16 VFilter 1 42 2 00 (null)
17 VColumn 1 0 7 00 btab.mask
18 Ne 7 41 4 collseq(BINARY) 6a (null)
19 Integer 1 8 0 00 record LEFT
JOIN hit
20 Integer 0 9 0 00 init LEFT
JOIN no-match flag
21 Integer 17 4 0 00 (null)
22 VColumn 1 1 5 00 btab.bid
23 Integer 2 2 0 00 (null)
24 Integer 2 3 0 00 (null)
25 VFilter 2 38 2 00 (null)
26 VColumn 2 0 7 00 ctab.bid
27 Integer 17 6 0 00 (null)
28 Ne 6 37 7 collseq(BINARY) 6c (null)
29 VColumn 2 1 6 00 ctab.bid
30 Ne 6 37 5 collseq(BINARY) 6b (null)
31 Integer 1 9 0 00 record LEFT
JOIN hit
32 VColumn 0 3 10 00 atab.mask
33 VColumn 1 1 11 00 btab.bid
34 VColumn 2 1 12 00 ctab.bid
35 VColumn 2 2 13 00 ctab.desc
36 ResultRow 10 4 0 00 (null)
37 VNext 2 26 0 00 (null)
38 IfPos 9 41 0 00 (null)
39 NullRow 2 0 0 00 (null)
40 Goto 0 31 0 00 (null)
41 VNext 1 17 0 00 (null)
42 IfPos 8 45 0 00 (null)
43 NullRow 1 0 0 00 (null)
44 Goto 0 19 0 00 (null)
45 VNext 0 10 0 00 (null)
46 Close 0 0 0 00 (null)
47 Close 1 0 0 00 (null)
48 Close 2 0 0 00 (null)
49 Halt 0 0 0 00 (null)
50 Transaction 0 0 0 00 (null)
51 VerifyCookie 0 151 0 00 (null)
52 Goto 0 3 0 00 (null)
Specifically, please note the VNext instruction at position 41. It jumps to
position 17 which fetches data into register 7 and (at position 18) performs a
comparison with register 4 which should be atab.mask loaded at position 13. The
first time through this condition is true. However, in processing the inner
loops (specifically, positions 22-25) register 4 is overwritten with a value
used to process the left outer join and is, from that point on, invalid when
the VNext instruction occurs at position 41.
This problem occurs using the 3.6.18 source tree as well as trunk (as of this
morning). I build from source (not amalgamation) and use various "OMIT"
symbols, so if that is important to debugging this I can supply more
information.
I can "fix" the problem by not releasing the temporary registers allocated in
the codeOneLoopStart() routine that handles the virtual table implementation.
That obviously uses more registers, but the expected results are then generated.
When comparing the above code to an explain plan using the above SQL I get:
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Trace 0 0 0 00
1 Integer 1 1 0 00
2 Goto 0 41 0 00
3 OpenRead 0 2 0 2 00 atab
4 OpenRead 1 3 0 2 00 btab
5 OpenRead 2 4 0 2 00 ctab
6 Rewind 0 37 0 00
7 Column 0 0 2 00 atab.id
8 Ne 1 36 2 collseq(BINARY) 6c
9 Integer 0 4 0 00 init LEFT JOIN
no-match flag
10 Rewind 1 33 0 00
11 Column 0 1 3 00 atab.mask
12 Column 1 0 5 00 btab.mask
13 Ne 5 32 3 collseq(BINARY) 6a
14 Integer 1 4 0 00 record LEFT JOIN hit
15 Integer 0 6 0 00 init LEFT JOIN
no-match flag
16 Rewind 2 29 0 00
17 Column 1 1 5 00 btab.bid
18 Column 2 0 3 00 ctab.bid
19 Ne 3 28 5 collseq(BINARY) 6b
20 Integer 1 6 0 00 record LEFT JOIN hit
21 Column 0 0 7 00 atab.id
22 Column 0 1 8 00 atab.mask
23 Column 1 0 9 00 btab.mask
24 Column 1 1 10 00 btab.bid
25 Column 2 0 11 00 ctab.bid
26 Column 2 1 12 00 ctab.desc
27 ResultRow 7 6 0 00
28 Next 2 17 0 01
29 IfPos 6 32 0 00
30 NullRow 2 0 0 00
31 Goto 0 20 0 00
32 Next 1 11 0 01
33 IfPos 4 36 0 00
34 NullRow 1 0 0 00
35 Goto 0 14 0 00
36 Next 0 7 0 01
37 Close 0 0 0 00
38 Close 1 0 0 00
39 Close 2 0 0 00
40 Halt 0 0 0 00
41 Transaction 0 0 0 00
42 VerifyCookie 0 3 0 00
43 Goto 0 3 0 00
The difference to note is that the Next instruction at position 32 which jumps
to position 11, jumps to a location where it re-fetches the data it requires to
perform the comparison. I suspect a comparable solution is desired, or that the
register used to store atab.mask is marked as cached so that result is
preserved over the temporary register allocation process. However, the caching
seems to be on table columns and not expressions, and the fetch of atab.mask is
the result of processing the right-hand side of the join expression. Is there a
caching mechanism for non-column values?
I'm not sure what is the better solution. Any ideas or pointers or solutions is
appreciated.
Regards,
-Allan
_______________________________________________
sqlite-users mailing list
[email protected]
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users