Commit Graph

689 Commits

Author SHA1 Message Date
Pekka Enberg
0affdada2a core: Move datetime.rs to vdbe/
The file contains SQL functions invoked by the VDBE so let's move the
file there.
2024-08-02 17:34:10 +03:00
Pekka Enberg
9c479734be core: Rename to exec_time()
Follow the same naming convention as other SQL functions.
2024-08-02 17:34:10 +03:00
Pekka Enberg
465dfa3cb5 core: Rename to exec_date()
Follow the same naming convention as other SQL functions.
2024-08-02 17:34:10 +03:00
Pekka Enberg
e926ade455 Merge 'Date and time code cleanups' from Pekka Enberg
Closes #261
2024-08-02 16:21:28 +03:00
Pekka Enberg
69eb851120 core/datetime: Move get_max_datetime_exclusive() to bottom
Move the function so that it's callers are above the function definition
for smoother flow when reading the code.
2024-08-02 16:15:22 +03:00
Pekka Enberg
6ffb03216f core/datetime: Simplify error handling 2024-08-02 16:15:22 +03:00
Pekka Enberg
3412b65b8a core/datetime: Remove TimeUnit
...it's not used for anything much.
2024-08-02 16:04:09 +03:00
Pekka Enberg
763bf17c9e core/datetime: Use "cfg(test)" annotation for tests 2024-08-02 16:03:53 +03:00
Pekka Enberg
f8492c85ae core/datetime: Remove trace calls
We should trace in high-level code like VDBE interpreter loop, but not
in error handling path of specific SQL functions.
2024-08-02 16:03:46 +03:00
Pekka Enberg
707d1a0705 Merge 'Update Flake and add Darwin link flags' from Ethan Niser
This pull request:

 - updates the nix flake to include tcl 8.6 addressing #243

 - updates the nix flake to add the darwin "core foundation" lib

 - updates the make flake to link the core foundation lib on darwin

I was previously unable to run `make test-sqlite3` with this error:
https://gist.github.com/ethanniser/5b96888637da6ea9e9d56dcc3af9b573

But now it is able to run.

Closes #259
2024-08-02 08:12:14 +03:00
Ethan Niser
d47d494865 update flake to include tcl latest (addresses #243) and add core foundation lib and link flags for darwin 2024-08-01 18:00:46 -07:00
Pekka Enberg
e18fc511bb scripts/merge-pr.py: Use temporal file for commit message
...fixes problems like eating perfectly valid Markdown and stuff from
pull request descriptions.
2024-08-01 21:07:39 +03:00
Pekka Enberg
64738d6348 Merge 'Add ability to annotate instructions with comments' from Jussi Saurio
Similar to what SQLite does.

```
limbo> EXPLAIN SELECT u.age, p.name FROM users u LEFT JOIN products p ON u.first_name = p.name;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     25    0                    0   Start at 25
1     OpenReadAsync      0     2     0                    0   table=u, root=2
2     OpenReadAwait      0     0     0                    0
3     OpenReadAsync      1     3     0                    0   table=p, root=3
4     OpenReadAwait      0     0     0                    0
5     RewindAsync        0     0     0                    0
6     RewindAwait        0     -5    0                    0   Rewind table u
7       Integer          0     1     0                    0   r[1]=0; init LEFT JOIN match flag
8       RewindAsync      1     0     0                    0
9       RewindAwait      1     -11   0                    0   Rewind table p
10        Column         0     1     2                    0   r[2]=u.first_name
11        Column         1     1     3                    0   r[3]=p.name
12        Ne             2     3     17                   0   if r[2]!=r[3] goto 17
13        Integer        1     1     0                    0   r[1]=1; record LEFT JOIN hit
14        Column         0     9     4                    0   r[4]=u.age
15        Column         1     1     5                    0   r[5]=p.name
16        ResultRow      4     2     0                    0   output=r[4..5]
17      NextAsync        1     0     0                    0
18      NextAwait        1     9     0                    0
19      IfPos            1     22    0                    0   r[1]>0 -> r[1]-=0, goto 22
20      NullRow          1     0     0                    0   Set cursor 1 to a (pseudo) NULL row
21      Goto             0     13    0                    0
22    NextAsync          0     0     0                    0
23    NextAwait          0     6     0                    0
24    Halt               0     0     0                    0
25    Transaction        0     0     0                    0
26    Goto               0     1     0                    0
```

Closes #258
2024-08-01 21:07:31 +03:00
jussisaurio
20c085614f Add ability to annotate instructions with comments 2024-08-01 20:34:45 +03:00
Pekka Enberg
fedb558346 Update COMPAT.md 2024-08-01 20:24:44 +03:00
Pekka Enberg
5d29e04854 Update COMPAT.md 2024-08-01 19:24:10 +03:00
Pekka Enberg
cb34deef09 Limbo 0.0.3 2024-08-01 18:57:13 +03:00
Pekka Enberg
6af9aafddc Update CHANGELOG.md 2024-08-01 18:57:13 +03:00
Pekka Enberg
d97a434e29 Merge 'Use SeekRowId to avoid nested scans' from Jussi Saurio
The SQLite `SeekRowid` instruction tries to find a record in a B-tree table with a particular `rowid`  (or INTEGER PRIMARY KEY, if defined), and jumps if it is not found. In this PR, constraints like `tbl.id = 5` or `tbl.id = tbl2.id` are transformed into special `SeekRowid` expressions that emit `SeekRowid` VM instructions. This avoids a table scan and instead completes in `log N` time (for a table of `N` rows), because of how B-Trees work.


`LoopInfo` now contains a `Plan` which is either `Scan` or `SeekRowid` -- in the case of `SeekRowid` no `Rewind`/`Next` instructions are emitted - i.e. no looping is done.

`BTreeCursor` now implements a `btree_seek_rowid()` method that tries to find a row by `rowid`.

Our loop order is currently static, i.e. `SELECT * from a join b join c` always results in "loop a, loop b, loop c", so `SeekRowId` is only supported for equality expressions where the non-PK side of the expression only refers to outer loops or constants. Examples:

**Because `u.id` refers to an outer loop compared to the primary key `p.id`, `p` is selected for SeekRowid optimization:**
```
limbo> explain SELECT u.age FROM users u JOIN products p ON u.id = p.id

addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     14    0                    0   Start at 14
1     OpenReadAsync      0     2     0                    0   table=u, root=2
2     OpenReadAwait      0     0     0                    0   
3     OpenReadAsync      1     3     0                    0   table=p, root=3
4     OpenReadAwait      0     0     0                    0   
5     RewindAsync        0     0     0                    0   
6     RewindAwait        0     -5    0                    0   Rewind table u
7       RowId            0     1     0                    0   r[1]=u.rowid
8       SeekRowid        1     1     11                   0   if (r[1]!=p.rowid) goto 11
9       Column           0     9     2                    0   r[2]=u.age
10      ResultRow        2     1     0                    0   output=r[2]
11    NextAsync          0     0     0                    0   
12    NextAwait          0     6     0                    0   
13    Halt               0     0     0                    0   
14    Transaction        0     0     0                    0   
15    Goto               0     1     0                    0

limbo> SELECT u.age FROM users u JOIN products p ON u.id = p.id
94
37
18
33
15
89
24
63
77
13
22
```

**Because `5` refers to a constant and `u.id` is a primary key, `u` is selected for SeekRowid optimization:**
```
limbo> explain SELECT u.first_name, p.name FROM users u JOIN products p ON u.id = 5;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     15    0                    0   Start at 15
1     OpenReadAsync      0     2     0                    0   table=u, root=2
2     OpenReadAwait      0     0     0                    0   
3     OpenReadAsync      1     3     0                    0   table=p, root=3
4     OpenReadAwait      0     0     0                    0   
5     Integer            5     1     0                    0   r[1]=5
6     SeekRowid          0     1     14                   0   if (r[1]!=u.rowid) goto 14
7     RewindAsync        1     0     0                    0   
8     RewindAwait        1     -8    0                    0   Rewind table p
9       Column           0     1     2                    0   r[2]=u.first_name
10      Column           1     1     3                    0   r[3]=p.name
11      ResultRow        2     2     0                    0   output=r[2..3]
12    NextAsync          1     0     0                    0   
13    NextAwait          1     8     0                    0   
14    Halt               0     0     0                    0   
15    Transaction        0     0     0                    0   
16    Goto               0     1     0                    0   
limbo> SELECT u.first_name, p.name FROM users u JOIN products p ON u.id = 5;
Edward|hat
Edward|cap
Edward|shirt
Edward|sweater
Edward|sweatshirt
Edward|shorts
Edward|jeans
Edward|sneakers
Edward|boots
Edward|coat
Edward|accessories
```

**Same, but LEFT JOIN:**
```
limbo> EXPLAIN SELECT u.first_name, p.name FROM users u LEFT JOIN products p ON u.id = 5;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     20    0                    0   Start at 20
1     OpenReadAsync      0     2     0                    0   table=u, root=2
2     OpenReadAwait      0     0     0                    0   
3     OpenReadAsync      1     3     0                    0   table=p, root=3
4     OpenReadAwait      0     0     0                    0   
5     Integer            0     1     0                    0   r[1]=0
6     RewindAsync        1     0     0                    0   
7     RewindAwait        1     -11   0                    0   Rewind table p
8       Integer          5     2     0                    0   r[2]=5
9       SeekRowid        0     2     14                   0   if (r[2]!=u.rowid) goto 14
10      Integer          1     1     0                    0   r[1]=1
11      Column           0     1     3                    0   r[3]=u.first_name
12      Column           1     1     4                    0   r[4]=p.name
13      ResultRow        3     2     0                    0   output=r[3..4]
14    NextAsync          1     0     0                    0   
15    NextAwait          1     7     0                    0   
16    IfPos              1     19    0                    0   r[1]>0 -> r[1]-=0, goto 19
17    NullRow            1     0     0                    0   Set cursor 1 to a (pseudo) NULL row
18    Goto               0     10    0                    0   
19    Halt               0     0     0                    0   
20    Transaction        0     0     0                    0   
21    Goto               0     1     0                    0   
limbo> SELECT u.first_name, p.name FROM users u LEFT JOIN products p ON u.id = 5;
Edward|hat
Edward|cap
Edward|shirt
Edward|sweater
Edward|sweatshirt
Edward|shorts
Edward|jeans
Edward|sneakers
Edward|boots
Edward|coat
Edward|accessories
```

**Both `p` and `u` selected for optimization:**
```
limbo> EXPLAIN SELECT u.first_name, p.name FROM users u JOIN products p ON u.id = p.id and u.id = 5;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     13    0                    0   Start at 13
1     OpenReadAsync      0     2     0                    0   table=u, root=2
2     OpenReadAwait      0     0     0                    0   
3     OpenReadAsync      1     3     0                    0   table=p, root=3
4     OpenReadAwait      0     0     0                    0   
5     Integer            5     1     0                    0   r[1]=5
6     SeekRowid          0     1     12                   0   if (r[1]!=u.rowid) goto 12
7     RowId              0     2     0                    0   r[2]=u.rowid
8     SeekRowid          1     2     12                   0   if (r[2]!=p.rowid) goto 12
9     Column             0     1     3                    0   r[3]=u.first_name
10    Column             1     1     4                    0   r[4]=p.name
11    ResultRow          3     2     0                    0   output=r[3..4]
12    Halt               0     0     0                    0   
13    Transaction        0     0     0                    0   
14    Goto               0     1     0                    0   

limbo> SELECT u.first_name, p.name FROM users u JOIN products p ON u.id = p.id and u.id = 5;
Edward|sweatshirt
```

**`p.id + 1` refers to an INNER loop compared to the primary key `u.id`, so optimization is skipped:**
```
limbo> EXPLAIN SELECT u.age FROM users u JOIN products p ON u.id = p.id + 1;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     21    0                    0   Start at 21
1     OpenReadAsync      0     2     0                    0   table=u, root=2
2     OpenReadAwait      0     0     0                    0   
3     OpenReadAsync      1     3     0                    0   table=p, root=3
4     OpenReadAwait      0     0     0                    0   
5     RewindAsync        0     0     0                    0   
6     RewindAwait        0     -5    0                    0   Rewind table u
7       RewindAsync      1     0     0                    0   
8       RewindAwait      1     -8    0                    0   Rewind table p
9         RowId          0     1     0                    0   r[1]=u.rowid
10        RowId          1     3     0                    0   r[3]=p.rowid
11        Integer        1     4     0                    0   r[4]=1
12        Add            3     4     2                    0   r[2]=r[3]+r[4]
13        Ne             1     2     16                   0   if r[1]!=r[2] goto 16
14        Column         0     9     5                    0   r[5]=u.age
15        ResultRow      5     1     0                    0   output=r[5]
16      NextAsync        1     0     0                    0   
17      NextAwait        1     8     0                    0   
18    NextAsync          0     0     0                    0   
19    NextAwait          0     6     0                    0   
20    Halt               0     0     0                    0   
21    Transaction        0     0     0                    0   
22    Goto               0     1     0                    0   
limbo> SELECT u.age FROM users u JOIN products p ON u.id = p.id + 1;
37
18
33
15
89
24
63
77
13
22
18
```

This whole thing is a bit ad-hoc / "something that works" kind of thing, probably me and @benclmnt need to put our noses down into some books and start to actually build some sort of graph-based query planner after this...

Closes #247
2024-08-01 18:54:33 +03:00
jussisaurio
02cae50324 use move_to() in btree_seek_rowid() 2024-08-01 18:32:01 +03:00
jussisaurio
d5ade427c2 add test for seekrowid order independence 2024-08-01 17:58:38 +03:00
jussisaurio
f344e07868 extract method 2024-08-01 17:54:29 +03:00
jussisaurio
7e88ad64da Rename and comment 2024-08-01 17:53:21 +03:00
jussisaurio
f46b13690f augment comment 2024-08-01 17:47:13 +03:00
jussisaurio
6860329940 rename and add comments 2024-08-01 17:45:40 +03:00
jussisaurio
81c2f2eca6 Add comment 2024-08-01 17:40:39 +03:00
jussisaurio
583fe31667 rebase fix 2024-08-01 17:26:00 +03:00
jussisaurio
551b11303f Broaden the type of expr that qualifies as seekrowid candidate 2024-08-01 17:23:59 +03:00
jussisaurio
64f7e48f1b Cleanup 2024-08-01 17:23:59 +03:00
jussisaurio
8feb443048 Use SeekRowid instruction on expr1 = expr2 constraints if they contain primary keys 2024-08-01 17:23:59 +03:00
jussisaurio
97dfae437c SeekRowid VM instruction 2024-08-01 17:23:59 +03:00
jussisaurio
d965998cdf btree_seek_rowid() implementation 2024-08-01 17:23:59 +03:00
Pekka Enberg
bcd4139a07 Merge 'Storage module cleanup' from Pekka Enberg
This pull request moves bunch of code under a new  module. The
idea is that we have three major subsystems:

- translate -- that performs code generation for SQL statements
- vdbe -- the runtime that executes SQL statements
- storage -- the storage layer that runtime uses

Closes #254
2024-08-01 12:10:59 +03:00
Pekka Enberg
0bf12ec1b3 core: Move buffer_pool.rs to storage module 2024-08-01 11:53:14 +03:00
Pekka Enberg
ed1c23bfe6 core: Move wal.rs to storage module 2024-08-01 11:52:50 +03:00
Pekka Enberg
f8a43361db core: Move pager.rs to storage module 2024-08-01 11:52:50 +03:00
Pekka Enberg
7abc48303f core: Move btree.rs to storage module 2024-08-01 11:52:50 +03:00
Pekka Enberg
307cd71b11 core: Rename storage.rs to storage/mod.rs
Prepare for moving more code under storage module.
2024-08-01 11:52:50 +03:00
Pekka Enberg
957cc383c8 core: Fix module documentation format in sqlite3_ondisk.rs 2024-08-01 11:52:18 +03:00
Pekka Enberg
5a7db98efb Merge 'Initial pass on WAL reader' from Pekka Enberg
Closes #249
2024-08-01 11:35:55 +03:00
Pekka Enberg
73bdf1671f Initial pass on WAL reader
These are mostly just stubs for now, but at least we have some code in
place as reminder what we need.
2024-08-01 11:31:17 +03:00
Pekka Enberg
d75817998d Add merge-pr.py helper script
This adds a helper script to generate merge commits that are nicer than
the default Github one.
2024-08-01 10:23:06 +03:00
Pekka Enberg
e88e57f9ba Merge 'Random clippy cleanups' from Pekka Enberg
Closes #253
2024-08-01 10:22:25 +03:00
Pekka Enberg
8c474870c1 core: Eliminate redundant casts 2024-08-01 09:25:25 +03:00
Pekka Enberg
531bf9f96f Merge pull request #252 from sonhmai/feat/158-partial-support-function-time
feat: add time() scalar function partial support without modifier #158
2024-08-01 09:22:08 +03:00
Pekka Enberg
82ff5b9c9b core: Remove useless use of format!() 2024-08-01 09:16:02 +03:00
Pekka Enberg
6a62e03a7d simulator: Silence unused variable warning 2024-08-01 09:12:53 +03:00
sonhmai
789ae4becf feat: add time() scalar function partial support without modifier #158 2024-08-01 13:06:07 +07:00
Pekka Enberg
ce7f373add Update README.md and CHANGELOG.md 2024-07-31 20:08:46 +03:00
Pekka Enberg
83a14fb6db Merge pull request #229 from pereman2/ww
core: write path
2024-07-31 20:04:06 +03:00