Commit Graph

4414 Commits

Author SHA1 Message Date
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
Pekka Enberg
bef665b7f3 Limbo 0.0.20-pre.2 2025-05-14 09:17:07 +03:00
Pekka Enberg
d912f14528 Update CHANGELOG 2025-05-14 09:16:53 +03:00
Pekka Enberg
67775fbc1d Merge 'github: Ensure rustmft is installed' from Pekka Enberg
Closes #1478
2025-05-14 09:16:10 +03:00
Pekka Enberg
da3815e1cb github: Ensure rustmft is installed 2025-05-14 09:12:35 +03:00
Pekka Enberg
3be9807e4f Update CHANGELOG 2025-05-14 08:58:08 +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
e91d17f06e Add tests for parameter binding for update, select and delete queries 2025-05-13 12:50:10 -04:00
PThorpe92
0593a99f0e Remove insertCtx from parameters and replace fix with expr rewriting 2025-05-13 12:49:16 -04:00
Jussi Saurio
3cc9147f6c Merge 'testing/py: rename debug_print() to run_debug()' from Jussi Saurio
I wasted a few minutes staring at #1471 because I thought
`limbo.debug_print()` just prints the sql, but it actually executes it
too

Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #1472
2025-05-13 10:12:27 +03: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
44e282f630 Add multi-row insert regression test 2025-05-13 09:03:01 +03:00
Jussi Saurio
957fe1b446 Fix infinite loop when inserting multiple rows 2025-05-13 08:54:25 +03:00