Jussi Saurio
12a2c2b9ad
Add more documentation to OPTIMIZER.MD
2025-05-14 09:42:26 +03:00
Jussi Saurio
fe628e221a
plan_satisfies_order_target(): simplify
2025-05-14 09:42:26 +03:00
Jussi Saurio
4dde356d97
AccessMethod: simplify
2025-05-14 09:42:26 +03:00
Jussi Saurio
a90358f669
TableMask: comments
2025-05-14 09:42:26 +03:00
Jussi Saurio
f12eb25962
cost.rs: simplify cost estimation
2025-05-14 09:42:26 +03:00
Jussi Saurio
4f07c808b2
Fix bug with constraint ordering introduced by refactor
2025-05-14 09:42:26 +03:00
Jussi Saurio
52b28d3099
rename use_indexes to optimize_table_access
2025-05-14 09:42:26 +03:00
Jussi Saurio
d8218483a2
use_indexes: comments
2025-05-14 09:42:26 +03:00
Jussi Saurio
e53ab385d7
order.rs: comments
2025-05-14 09:42:26 +03:00
Jussi Saurio
ff8e187eda
find_best_access_method_for_join_order: comments
2025-05-14 09:42:26 +03:00
Jussi Saurio
3442e4981d
remove some unnecessary parameters
2025-05-14 09:42:26 +03:00
Jussi Saurio
c18bb3cd14
rename
2025-05-14 09:42:26 +03:00
Jussi Saurio
15b32f7e57
constraints.rs: more comments
2025-05-14 09:42:26 +03:00
Jussi Saurio
c782616180
Refactor constraints so that WHERE clause is not needed in join reordering phase
2025-05-14 09:42:26 +03:00
Jussi Saurio
6aa5b01a7b
Add note about optimizer directory structure
2025-05-14 09:42:26 +03:00
Jussi Saurio
bd875e3876
optimizer module split
2025-05-14 09:42:26 +03:00
Jussi Saurio
ec45a92bac
move optimizer to its own directory
2025-05-14 09:42:26 +03:00
Jussi Saurio
c639a43676
fix parenthesized column edge case
2025-05-14 09:42:26 +03:00
Jussi Saurio
90de8791f5
comments
2025-05-14 09:42:26 +03:00
Jussi Saurio
c8f5bd3f4f
rename
2025-05-14 09:42:26 +03:00
Jussi Saurio
630a6093aa
refactor join_lhs_tables_to_rhs_table
2025-05-14 09:42:26 +03:00
Jussi Saurio
62d2ee8eb6
rename
2025-05-14 09:42:26 +03:00
Jussi Saurio
5f9ebe26a0
as_binary_components() helper
2025-05-14 09:42:26 +03:00
Jussi Saurio
a92d94270a
Get rid of useless ScanCost struct
2025-05-14 09:42:26 +03:00
Jussi Saurio
de9e8442e8
fix ephemeral
2025-05-14 09:42:25 +03:00
Jussi Saurio
3b1aef4a9e
Do Less Work (tm) - everything works except ephemeral
2025-05-14 09:42:01 +03:00
Jussi Saurio
87850e5706
simplify
2025-05-14 09:41:14 +03:00
Jussi Saurio
77f11ba004
simplify AccessMethodKind
2025-05-14 09:41:14 +03:00
Jussi Saurio
5f724d6b2e
Add more comments to join ordering logic
2025-05-14 09:41:14 +03:00
Jussi Saurio
c02d3f8bcd
Do groupby/orderby sort elimination based on optimizer decision
2025-05-14 09:41:13 +03:00
Jussi Saurio
1e46f1d9de
Feature: join reordering optimizer
2025-05-14 09:40:48 +03:00
Jussi Saurio
c8c83fc6e6
OPTIMIZER.MD docs
2025-05-14 09:39:47 +03:00
Jussi Saurio
67a080bfa0
dont mutate where clause during individual index selection phase
2025-05-14 09:39:47 +03:00
Jussi Saurio
1b71f58bbf
Merge 'Redesign parameter binding in query translator' from Preston Thorpe
...
closes #1467
## Example:
Previously as explained in #1449 , our parameter binding wasn't working
properly because we would essentially
assign the first index of whatever was translated first
```console
limbo> create table t (id integer primary key, name text, age integer);
limbo> explain select * from t where name = ? and id > ? and age between ? and ?;
addr opcode p1 p2 p3 p4 p5 comment
---- ----------------- ---- ---- ---- ------------- -- -------
0 Init 0 20 0 0 Start at 20
1 OpenRead 0 2 0 0 table=t, root=2
2 Variable 1 4 0 0 r[4]=parameter(1) # always 1
3 IsNull 4 19 0 0 if (r[4]==NULL) goto 19
4 SeekGT 0 19 4 0 key=[4..4]
5 Column 0 1 5 0 r[5]=t.name
6 Variable 2 6 0 0 r[6]=parameter(2) # always 2
7 Ne 5 6 18 0 if r[5]!=r[6] goto 18
8 Variable 3 7 0 0 r[7]=parameter(3) # etc...
9 Column 0 2 8 0 r[8]=t.age
10 Gt 7 8 18 0 if r[7]>r[8] goto 18
11 Column 0 2 9 0 r[9]=t.age
12 Variable 4 10 0 0 r[10]=parameter(4)
13 Gt 9 10 18 0 if r[9]>r[10] goto 18
14 RowId 0 1 0 0 r[1]=t.rowid
15 Column 0 1 2 0 r[2]=t.name
16 Column 0 2 3 0 r[3]=t.age
17 ResultRow 1 3 0 0 output=r[1..3]
18 Next 0 5 0 0
19 Halt 0 0 0 0
20 Transaction 0 0 0 0 write=false
21 Goto 0 1 0 0
```
## Solution:
`rewrite_expr` currently is used to transform `true|false` to `1|0`, so
it has been adapted to transform anonymous `Expr::Variable`s to named
variables, inserting the appropriate index of the parameter by passing
in a counter.
```rust
ast::Expr::Variable(var) => {
if var.is_empty() {
// rewrite anonymous variables only, ensure that the `param_idx` starts at 1 and
// all the expressions are rewritten in the order they come in the statement
*expr = ast::Expr::Variable(format!("{}{param_idx}", PARAM_PREFIX));
*param_idx += 1;
}
Ok(())
}
```
# Corrected output: (notice the seek)
```console
limbo> explain select * from t where name = ? and id > ? and age between ? and ?;
addr opcode p1 p2 p3 p4 p5 comment
---- ----------------- ---- ---- ---- ------------- -- -------
0 Init 0 20 0 0 Start at 20
1 OpenRead 0 2 0 0 table=t, root=2
2 Variable 2 4 0 0 r[4]=parameter(2)
3 IsNull 4 19 0 0 if (r[4]==NULL) goto 19
4 SeekGT 0 19 4 0 key=[4..4]
5 Column 0 1 5 0 r[5]=t.name
6 Variable 1 6 0 0 r[6]=parameter(1)
7 Ne 5 6 18 0 if r[5]!=r[6] goto 18
8 Variable 3 7 0 0 r[7]=parameter(3)
9 Column 0 2 8 0 r[8]=t.age
10 Gt 7 8 18 0 if r[7]>r[8] goto 18
11 Column 0 2 9 0 r[9]=t.age
12 Variable 4 10 0 0 r[10]=parameter(4)
13 Gt 9 10 18 0 if r[9]>r[10] goto 18
14 RowId 0 1 0 0 r[1]=t.rowid
15 Column 0 1 2 0 r[2]=t.name
16 Column 0 2 3 0 r[3]=t.age
17 ResultRow 1 3 0 0 output=r[1..3]
18 Next 0 5 0 0
19 Halt 0 0 0 0
20 Transaction 0 0 0 0 write=false
21 Goto 0 1 0 0
```
## And a `Delete`:
```console
limbo> explain delete from t where name = ? and age > ? and id > ?;
addr opcode p1 p2 p3 p4 p5 comment
---- ----------------- ---- ---- ---- ------------- -- -------
0 Init 0 15 0 0 Start at 15
1 OpenWrite 0 2 0 0
2 Variable 3 1 0 0 r[1]=parameter(3)
3 IsNull 1 14 0 0 if (r[1]==NULL) goto 14
4 SeekGT 0 14 1 0 key=[1..1]
5 Column 0 1 2 0 r[2]=t.name
6 Variable 1 3 0 0 r[3]=parameter(1)
7 Ne 2 3 13 0 if r[2]!=r[3] goto 13
8 Column 0 2 4 0 r[4]=t.age
9 Variable 2 5 0 0 r[5]=parameter(2)
10 Le 4 5 13 0 if r[4]<=r[5] goto 13
11 RowId 0 6 0 0 r[6]=t.rowid
12 Delete 0 0 0 0
13 Next 0 5 0 0
14 Halt 0 0 0 0
15 Transaction 0 1 0 0 write=true
16 Goto 0 1 0 0
```
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com >
Closes #1475
2025-05-14 09:26:06 +03:00
Jussi Saurio
a0f973cb34
Merge 'Fix infinite loop when inserting multiple rows' from Jussi Saurio
...
Due to constant instruction reshuffling introduced in #1359 , it is
advisable not to do either of the following:
1. Use raw offsets as jump targets
2. Use `program.resolve_label(my_label, program.offset())` when it is
uncertain what the next instruction will be
Instead, if you want a label to point to "whatever instruction follows
the last one", you should use
`program.preassign_label_to_next_insn(label)`, which will work correctly
even with instruction rerdering
Reviewed-by: Preston Thorpe (@PThorpe92)
Closes #1474
2025-05-14 09:24:47 +03:00
PThorpe92
a0b2b6e85d
Consolidate match case in parameters push to handle all anonymous params in one case
2025-05-13 14:42:12 -04:00
PThorpe92
2f255524bd
Remove unused import and unnecessary mut annotations in insert.rs
2025-05-13 14:34:22 -04:00
PThorpe92
94aa9cd99d
Add cases to rewrite_expr in the optimizer
2025-05-13 14:33:45 -04:00
PThorpe92
16ac6ab918
Fix parameter push method to re-convert anonymous parameters
2025-05-13 14:33:11 -04:00
PThorpe92
0593a99f0e
Remove insertCtx from parameters and replace fix with expr rewriting
2025-05-13 12:49:16 -04:00
Jussi Saurio
a2e577ad01
Merge 'Fix handling of empty strings in prepared statements' from Diego Reis
...
When `prepare()` is called with an empty string it should throw an error
(e.g `ApiMisuse` in rusqlite). I'm testing only in JS but it should
throw to any bind.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com >
Closes #1473
2025-05-13 10:12:09 +03:00
Jussi Saurio
957fe1b446
Fix infinite loop when inserting multiple rows
2025-05-13 08:54:25 +03:00
Diego Reis
07bfeadd56
core: Simplify error handling of malformed strings for prepared statements
2025-05-12 13:25:11 -03:00
Diego Reis
f7ab8b11d6
cargo fmt
2025-05-12 10:56:53 -03:00
Jussi Saurio
2b6b09d435
Merge 'btree: Coalesce free blocks in page_free_array()' from Mohamed Hossam
...
Coalesce adjacent free blocks during `page_free_array()` in
`core/storage/btree`.
Instead of immediately passing free cells to `free_cell_range()`, buffer
up to 10 free cells and try to merge adjacent free blocks. Break on the
first merge to avoid time complexity, `free_cell_range()` coalesces
blocks afterwards anyways. This follows SQLite's [`pageFreeArray()`](htt
ps://github.com/sqlite/sqlite/blob/d7324103b196c572a98724a5658970b4000b8
c39/src/btree.c#L7729) implementation.
Removed this TODO:
```rust
fn page_free_array( . . )
.
.
// TODO: implement fancy smart free block coalescing procedure instead of dumb free to
// then defragment
```
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com >
Closes #1448
2025-05-12 16:52:11 +03:00
Diego Reis
c4e7be04f8
core: Handles prepared statement with empty SQL
2025-05-12 10:38:58 -03:00
m0hossam
70b9438f80
Add comments
2025-05-12 15:58:36 +03:00
Jussi Saurio
9b96e2bcc3
Merge 'CREATE VIRTUAL TABLE fixes' from Piotr Rżysko
...
This PR fixes two bugs in `CREATE VIRTUAL TABLE` (see individual commits
for details).
I added a test in `extensions.py` instead of the TCL test suite because
it's currently more convenient - there’s no existing framework for
loading extensions in TCL tests. However, I believe the test should
eventually be moved to TCL, as it verifies behavior expected to be
compatible with SQLite and is independent of any specific extension.
I've seen a PR proposing to migrate TCL tests to Rust, so please let me
know if moving this test to TCL would still be valuable.
Reviewed-by: Preston Thorpe (@PThorpe92)
Closes #1471
2025-05-12 10:47:51 +03:00
Jussi Saurio
501e95637a
Merge 'Support isnull and notnull expr' from meteorgan
...
Limbo `isnull` output:
```
limbo> explain select 1 isnull;
addr opcode p1 p2 p3 p4 p5 comment
---- ----------------- ---- ---- ---- ------------- -- -------
0 Init 0 3 0 0 Start at 3
1 ResultRow 1 1 0 0 output=r[1]
2 Halt 0 0 0 0
3 Integer 1 2 0 0 r[2]=1
4 Integer 1 1 0 0 r[1]=1
5 IsNull 2 7 0 0 if (r[2]==NULL) goto 7
6 Integer 0 1 0 0 r[1]=0
7 Goto 0 1 0 0
```
Sqlite `isnull` output:
```
sqlite> explain select 1 isnull;
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 6 0 0 Start at 6
1 Integer 1 1 0 0 r[1]=1
2 IsNull 2 4 0 0 if r[2]==NULL goto 4
3 Integer 0 1 0 0 r[1]=0
4 ResultRow 1 1 0 0 output=r[1]
5 Halt 0 0 0 0
6 Integer 1 2 0 0 r[2]=1
7 Goto 0 1 0 0
```
------------------------------------------------------------------------
-------------------
Limbo `notnull` output:
```
limbo> explain select 1 notnull;
addr opcode p1 p2 p3 p4 p5 comment
---- ----------------- ---- ---- ---- ------------- -- -------
0 Init 0 3 0 0 Start at 3
1 ResultRow 1 1 0 0 output=r[1]
2 Halt 0 0 0 0
3 Integer 1 2 0 0 r[2]=1
4 Integer 1 1 0 0 r[1]=1
5 NotNull 2 7 0 0 r[2]!=NULL -> goto 7
6 Integer 0 1 0 0 r[1]=0
7 Goto 0 1 0 0
```
Sqlite `notnull` output:
```
sqlite> explain select 1 notnull;
addr opcode p1 p2 p3 p4 p5 comment
---- ------------- ---- ---- ---- ------------- -- -------------
0 Init 0 6 0 0 Start at 6
1 Integer 1 1 0 0 r[1]=1
2 NotNull 2 4 0 0 if r[2]!=NULL goto 4
3 Integer 0 1 0 0 r[1]=0
4 ResultRow 1 1 0 0 output=r[1]
5 Halt 0 0 0 0
6 Integer 1 2 0 0 r[2]=1
7 Goto 0 1 0 0
```
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com >
Closes #1468
2025-05-12 10:06:35 +03:00
Jussi Saurio
07415dac07
Merge 'Count optimization' from Pedro Muniz
...
After reading #1123 , I wanted to see what optimizations I could do.
Sqlite optimizes `count` aggregation for the following case: `SELECT
count() FROM <tbl>`. This is so widely used, that they made an
optimization just for it in the form of the `COUNT` opcode.
This PR thus implements this optimization by creating the `COUNT`
opcode, and checking in the select emitter if we the query is a Simple
Count Query. If it is, we just emit the Opcode instead of going through
a Rewind loop, saving on execution time.
The screenshots below show a huge decrease in execution time.
- **Main**
<img width="383" alt="image" src="https://github.com/user-
attachments/assets/99a9dec4-e7c5-41db-ba67-4eafa80dd2e6" />
- **Count Optimization**
<img width="435" alt="image" src="https://github.com/user-
attachments/assets/e93b3233-92e6-4736-aa60-b52b2477179f" />
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com >
Closes #1443
2025-05-12 10:01:50 +03:00