Bump. One bump limit met :-) Didn't know if this got lost in the holiday rush, but the bug remains. I have a work-around (see below) that does work, but didn't know if there was a better solution.
Regards, -Allan > -----Original Message----- > From: sqlite-users-boun...@sqlite.org [mailto:sqlite-users- > boun...@sqlite.org] On Behalf Of Schrum, Allan > Sent: Monday, November 23, 2009 11:29 AM > To: General Discussion of SQLite Database > Subject: [sqlite] Bug: Problem with left-outer join and virtual tables. > Version 3.6.18 and trunk > > 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 > sqlite-users@sqlite.org > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users _______________________________________________ sqlite-users mailing list sqlite-users@sqlite.org http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users