mirror of
https://github.com/aljazceru/turso.git
synced 2026-01-07 10:14:21 +01:00
Closes #2924 Unsure if this fix is that great, but it does fix the issue described in #2924 -- added minimal regression test to illustrate the behavior This crash requires a pretty specific set of circumstances: - 3-way join with two innermost being left joins - nullable seek key on the innermost table: * middle table gets nulled out because no matches with the outermost table * hence when we seek the innermost table using middle table values, the seek key is null, so `Insn::IsNull` entirely skips the innermost table Perhaps a bytecode plan illustrates this better: ```sql turso> explain select a.x, b.x, c.x from a left join b on a.y=b.x left join c on b.y=c.x; addr opcode p1 p2 p3 p4 p5 comment ---- ----------------- ---- ---- ---- ------------- -- ------- 0 Init 0 34 0 0 Start at 34 1 OpenRead 0 2 0 0 table=a, root=2, iDb=0 2 OpenRead 1 4 0 0 table=b, root=4, iDb=0 3 OpenRead 2 5 0 0 index=sqlite_autoindex_b_1, root=5, iDb=0 4 OpenRead 3 7 0 0 index=sqlite_autoindex_c_1, root=7, iDb=0 5 Rewind 0 33 0 0 Rewind table a 6 Integer 0 4 0 0 r[4]=0 7 Column 0 1 6 0 r[6]=a.y 8 IsNull 6 28 0 0 if (r[6]==NULL) goto 28 9 SeekGE 2 28 6 0 key=[6..6] 10 IdxGT 2 28 6 0 key=[6..6] 11 DeferredSeek 2 1 0 0 12 Integer 1 4 0 0 r[4]=1 13 Integer 0 5 0 0 r[5]=0 14 Column 1 1 7 0 r[7]=b.y -- if b.y is NULL, we skip the entire table loop between insns 16-23 -- except when we call NullRow and then Goto to re-enter that loop in order to -- return NULL values for the table 15 IsNull 7 24 0 0 if (r[7]==NULL) goto 24 16 SeekGE 3 24 7 0 key=[7..7] 17 IdxGT 3 24 7 0 key=[7..7] 18 Integer 1 5 0 0 r[5]=1 19 Column 0 0 1 0 r[1]=a.x 20 Column 1 0 2 0 r[2]=b.x 21 Column 3 0 3 0 r[3]=sqlite_autoindex_c_1.x 22 ResultRow 1 3 0 0 output=r[1..3] 23 Next 3 17 0 0 24 IfPos 5 27 0 0 r[5]>0 -> r[5]-=0, goto 27 25 NullRow 3 0 0 0 Set cursor 3 to a (pseudo) NULL row 26 Goto 0 18 0 0 27 Next 2 10 0 0 28 IfPos 4 32 0 0 r[4]>0 -> r[4]-=0, goto 32 29 NullRow 1 0 0 0 Set cursor 1 to a (pseudo) NULL row 30 NullRow 2 0 0 0 Set cursor 2 to a (pseudo) NULL row 31 Goto 0 12 0 0 32 Next 0 6 0 0 33 Halt 0 0 0 0 34 Transaction 0 0 3 0 iDb=0 write=false 35 Goto 0 1 0 0 ``` Reviewed-by: Preston Thorpe <preston@turso.tech> Closes #2967