penberg / limbo

Limbo is a work-in-progress, in-process OLTP database management system, compatible with SQLite.
MIT License
960 stars 53 forks source link

Optimize constant conditions #237

Closed jussisaurio closed 1 month ago

jussisaurio commented 1 month ago

Example 1: jump immediately to halt when WHERE is always false

limbo> explain select first_name from users where 1 and 0;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     11    0                    0   Start at 11
1     OpenReadAsync      0     2     0                    0   root=2
2     OpenReadAwait      0     0     0                    0   
3     RewindAsync        0     0     0                    0   
4     RewindAwait        0     -5    0                    0   
5       Goto             0     10    0                    0   
6       Column           0     1     1                    0   r[1]=users.first_name
7       ResultRow        1     1     0                    0   output=r[1]
8     NextAsync          0     0     0                    0   
9     NextAwait          0     4     0                    0   
10    Halt               0     0     0                    0   
11    Transaction        0     0     0                    0   
12    Goto               0     1     0                    0

Example 2: don't emit a conditional when WHERE is always true

limbo> explain select first_name from users where 1 or 0;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     10    0                    0   Start at 10
1     OpenReadAsync      0     2     0                    0   root=2
2     OpenReadAwait      0     0     0                    0   
3     RewindAsync        0     0     0                    0   
4     RewindAwait        0     -5    0                    0   
5       Column           0     1     1                    0   r[1]=users.first_name
6       ResultRow        1     1     0                    0   output=r[1]
7     NextAsync          0     0     0                    0   
8     NextAwait          0     4     0                    0   
9     Halt               0     0     0                    0   
10    Transaction        0     0     0                    0   
11    Goto               0     1     0                    0

Example 3: in LEFT JOIN inner loop, jump immediately to setting the right cursor to null when the join condition is always false

limbo> explain select u.first_name, p.name from users u left join products p on 0;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     23    0                    0   Start at 23
1     OpenReadAsync      0     2     0                    0   root=2
2     OpenReadAwait      0     0     0                    0   
3     OpenReadAsync      1     3     0                    0   root=3
4     OpenReadAwait      0     0     0                    0   
5     RewindAsync        0     0     0                    0   
6     RewindAwait        0     -5    0                    0   
7       Integer          0     1     0                    0   r[1]=0
8       RewindAsync      1     0     0                    0   
9       RewindAwait      1     -11   0                    0   
10        Goto           0     17    0                    0   
11        Integer        1     1     0                    0   r[1]=1
12        Column         0     1     2                    0   r[2]=users.first_name
13        Column         1     1     3                    0   r[3]=products.name
14        ResultRow      2     2     0                    0   output=r[2..3]
15      NextAsync        1     0     0                    0   
16      NextAwait        1     9     0                    0   
17      IfPos            1     20    0                    0   r[1]>0 -> r[1]-=0, goto 20
18      NullRow          1     0     0                    0   Set cursor 1 to a (pseudo) NULL row
19      Goto             0     11    0                    0   
20    NextAsync          0     0     0                    0   
21    NextAwait          0     6     0                    0   
22    Halt               0     0     0                    0   
23    Transaction        0     0     0                    0   
24    Goto               0     1     0                    0   
limbo> explain select u.first_name, p.name from users u left join products p on 0 limit 5;

Example 4: three-way-join (a LEFT JOIN b ON TRUE JOIN c on FALSE), the left join condition is completely erased, but the code still jump immediately to halt because of the always-false inner join condition:

limbo> explain select u.first_name, p.name from users u left join products p on 1 join users u2 on 0;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     29    0                    0   Start at 29
1     OpenReadAsync      0     2     0                    0   root=2
2     OpenReadAwait      0     0     0                    0   
3     OpenReadAsync      1     3     0                    0   root=3
4     OpenReadAwait      0     0     0                    0   
5     OpenReadAsync      2     2     0                    0   root=2
6     OpenReadAwait      0     0     0                    0   
7     RewindAsync        0     0     0                    0   
8     RewindAwait        0     -5    0                    0   
9       Goto             0     28    0                    0   
10      Integer          0     1     0                    0   r[1]=0
11      RewindAsync      1     0     0                    0   
12      RewindAwait      1     -11   0                    0   
13        Integer        1     1     0                    0   r[1]=1
14        RewindAsync    2     0     0                    0   
15        RewindAwait    2     -14   0                    0   
16          Column       0     1     2                    0   r[2]=users.first_name
17          Column       1     1     3                    0   r[3]=products.name
18          ResultRow    2     2     0                    0   output=r[2..3]
19        NextAsync      2     0     0                    0   
20        NextAwait      2     15    0                    0   
21      NextAsync        1     0     0                    0   
22      NextAwait        1     12    0                    0   
23      IfPos            1     26    0                    0   r[1]>0 -> r[1]-=0, goto 26
24      NullRow          1     0     0                    0   Set cursor 1 to a (pseudo) NULL row
25      Goto             0     13    0                    0   
26    NextAsync          0     0     0                    0   
27    NextAwait          0     8     0                    0   
28    Halt               0     0     0                    0   
29    Transaction        0     0     0                    0   
30    Goto               0     1     0                    0 

This is OK I guess, but I noticed that SQLite immediately jumps to Halt even before OpenRead if it can, maybe we should also write it that way... the only consideration that you can only do that on constant-false conditions that are in a WHERE or an INNER JOIN.