Commit Graph

134 Commits

Author SHA1 Message Date
Jussi Saurio
691dce6b8a Make decision about UpdatePlan::ephemeral_plan _after_ optimizer
An ephemeral table is required if the b-tree key of the table (rowid)
or the index (index key) is affected by the UPDATE.
2025-10-14 14:17:28 +03:00
Jussi Saurio
bc80ac1754 require &mut ProgramBuilder argument in optimize_plan()
this will be used for ephemeral plan construction for UPDATE in
a later commit.
2025-10-14 12:18:13 +03:00
Jussi Saurio
59a1c2ae2e Disallow joining more than 63 tables
Returns an error instead of panicing
2025-10-13 13:30:03 +03:00
Jussi Saurio
74e04634aa Fix incorrectly using an equality constraint twice for index seek
Prevents something like `WHERE x = 5 AND x = 5` from becoming a two
component index key.

Closes #3656
2025-10-10 13:19:19 +03:00
Nikita Sivukhin
4313f57ecb Optimize range scans 2025-10-09 11:47:41 +03:00
PThorpe92
ae975afe49 Remove unnecessary FK resolution on schema parsing 2025-10-07 16:45:16 -04:00
Jussi Saurio
58ea9e4c3c clippy 2025-10-02 21:49:33 +03:00
Jussi Saurio
edfe0cb4fe Collate: prevent using an index if collation sequences don't match 2025-10-02 21:49:33 +03:00
Jussi Saurio
63f9913dbb Clear WhereTerm 'from_outer_join' state when LEFT JOIN is optimized to INNER JOIN
Closes #2470

In a query like `SELECT * FROM t LEFT JOIN s ON t.a=s.a WHERE s.a = 'foo'` we can
remove the LEFT JOIN because NULL values will be equal to 'foo'. In fact, we have
this optimization already.

However, there was a dumb bug where `WhereTerm`s involving this join still retained
their `from_outer_join` state, resulting in forcing the evaluation of those terms
at the original join index, which results in completely wrong bytecode if the join
optimizer decides to reorder the join as `s JOIN t` instead. Effectively it will
evaluate `t.a=s.a` after table `s` is open but table `t` is not open yet.

This PR fixes that issue by clearing `from_outer_join` properly from the relevant
`WhereTerm`s.
2025-10-01 00:33:22 +03:00
Nikita Sivukhin
a32ed53bd8 remove optimization
- even if index search will return only 1 row - it will call next in the loop - and we incorrecty can process same row values multiple times
- the following query failed with this optimization:

turso> CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT, k TEXT, c0 INT);
turso> CREATE UNIQUE INDEX idx_p1_0 ON t(c0);
turso> insert into t values (null, 'uu', -1);
turso> insert into t values (null, 'uu', -2);
turso> UPDATE t SET c0 = NULL WHERE c0 = -1;
turso> SELECT * FROM t
┌────┬────┬────┐
│ id │ k  │ c0 │
├────┼────┼────┤
│  1 │ uu │    │
├────┼────┼────┤
│  2 │ uu │    │
└────┴────┴────┘
2025-09-30 16:37:41 +04:00
Diego Reis
90f4d69774 fix(3301): Remove identifier assert assumption
Not every identifier should be double-quoted
2025-09-29 22:33:21 -03:00
Nikita Sivukhin
f3f9219795 completely remove usage of enum variants 2025-09-26 13:01:49 +04:00
Pavan Nambi
f1ac855441 Merge branch 'main' into cdc_fail_autoincrement 2025-09-22 21:11:26 +05:30
Jussi Saurio
c0fc2ad234 fix optimizer tests 2025-09-22 10:18:03 +03:00
Jussi Saurio
eada24b508 Store in-memory index definitions most-recently-seen-first
This solves an issue where an INSERT statement conflicts with
multiple indices. In that case, sqlite iterates the linked list
`pTab->pIndex` in order and handles the first conflict encountered.
The newest parsed index is always added to the head of the list.

To be compatible with this behavior, we also need to put the most
recently parsed index definition first in our indexes list for a given
table.
2025-09-22 10:11:50 +03:00
Pavan Nambi
47194d7658 Merge branch 'tursodatabase:main' into cdc_fail_autoincrement 2025-09-21 16:03:38 +05:30
PThorpe92
21f6455190 Fix clippy warnings and tests 2025-09-20 14:38:50 -04:00
PThorpe92
635273f782 Prevent using a partial index as a scan driver 2025-09-20 14:38:48 -04:00
PThorpe92
51f970a263 Support partial indexes in INSERT/UPDATE/DELETE 2025-09-20 14:38:48 -04:00
PThorpe92
ffd1f87682 Centralize most of the AST traversal by binding columns and rewriting exprs together 2025-09-18 18:38:03 -04:00
Pavan-Nambi
020921f803 Merge remote-tracking branch 'upstream/main' into cdc_fail_autoincrement 2025-09-18 19:27:19 +05:30
Preston Thorpe
bcafb288ad Merge 'Fix is_nonnull returns true on 1 / 0' from Lâm Hoàng Phúc
turso:
```sh
turso> CREATE TABLE t (x PRIMARY KEY, y, z);
turso> INSERT INTO t VALUES (37, -70, -196792117);
turso> SELECT * FROM t WHERE  (1 / 0) >= -3289742039 < t.x;
┌────┬─────┬────────────┐
│ x  │ y   │ z          │
├────┼─────┼────────────┤
│ 37 │ -70 │ -196792117 │
└────┴─────┴────────────┘
turso>
```
sqlite:
```sh
sqlite> CREATE TABLE t (x PRIMARY KEY, y, z);
sqlite> INSERT INTO t VALUES (37, -70, -196792117);
sqlite> SELECT * FROM t WHERE  (1 / 0) >= -3289742039 < t.x;
sqlite>
```
related: https://github.com/tursodatabase/turso/actions/runs/17765571409
/job/50488042583?pr=3147#step:8:855

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #3167
2025-09-17 06:55:55 -04:00
TcMits
6606bf12d3 is_nonnull returns true on 1 / 0 2025-09-17 14:50:15 +07:00
Piotr Rzysko
9ff2133ff2 Rewrite window function expressions in the optimizer
Currently, this is effectively a no-op because, at the optimization
stage, window function expressions are in the form
win_func(subquery_column1, subquery_column2, ...).

Nevertheless, expressions are rewritten to maintain consistency with
aggregates, which also hold cloned expressions from sources like result
columns. This ensures future changes in the optimizer won’t break window
function handling.
2025-09-13 11:12:44 +02:00
Piotr Rzysko
6224cdbbd3 Support WalkControl in walk_expr_mut
Now walk_expr_mut can use WalkControl to skip parts of the expression
tree. This makes it consistent with walk_expr.
2025-09-13 10:49:14 +02:00
Pavan-Nambi
7191f1cc1c Merge remote-tracking branch 'upstream/main' into cdc_fail_autoincrement 2025-09-12 15:17:12 +05:30
Jussi Saurio
e3bd00883b Fix creation of automatic indexes
indexes with the naming scheme "sqlite_autoindex_<tblname>_<number>"
are automatically created when a table is created with UNIQUE or
PRIMARY KEY definitions.

these indexes must map to the table definition SQL in definition order,
i.e. sqlite_autoindex_foo_1 must be the first instance of UNIQUE or
PRIMARY KEY and so on.

this commit fixes our autoindex creation / parsing so that this invariant
is upheld.
2025-09-11 14:11:30 +03:00
Jussi Saurio
e0ca0cf8af Enable access path optimizer for DELETE 2025-09-10 14:54:51 +03:00
Pavan-Nambi
6728384b47 Merge remote-tracking branch 'origin/main' into cdc_fail_autoincrement 2025-09-10 07:30:22 +05:30
Pavan-Nambi
b833e71c20 inserting ain't working
hell yeah

concurrency tests passing now woosh

finally write tests passed

Most of the cdc tests are passing yay

autoincremeent draft

remove shared schema code that broke transactions

sequnce table should reset if table is drop

fmt

fmt

fmt
2025-09-09 20:07:52 +05:30
Jussi Saurio
2d1d284279 optimizer: convert outer join to inner join if possible 2025-09-08 23:21:34 +03:00
Jussi Saurio
f6ede79de7 Do not consume non-join WHERE terms in left joins 2025-09-08 09:35:11 +03:00
TcMits
b0f4dd49d5 use match_ignore_ascii_case macro 2025-09-03 12:01:52 +07:00
TcMits
4ddfdb2a62 finish 2025-08-27 14:58:35 +07:00
TcMits
50bdaec6d0 merge main 2025-08-27 13:36:54 +07:00
Pekka Enberg
26ba09c45f Revert "Merge 'Remove double indirection in the Parser' from Pedro Muniz"
This reverts commit 71c1b357e4, reversing
changes made to 6bc568ff69 because it
actually makes things slower.
2025-08-26 14:58:21 +03:00
pedrocarlo
d3240844ec refactor Core to remove the double indirection 2025-08-25 22:59:31 -03:00
Glauber Costa
38def26704 Add expr_compiler
To be used in DBSP-based projections. This will compile an expression
to VDBE bytecode and execute it.

To do that we need to add a new type of Expression, which we call a
Register.

This is a way for us to pass parameters to a DBSP program which will be
not columns or literals, but inputs from the DBSP deltas.
2025-08-25 17:48:17 +03:00
TcMits
399f10fe9a refactor parser fmt 2025-08-23 19:16:26 +07:00
Levy A.
4ba1304fb9 complete parser integration 2025-08-21 15:23:59 -03:00
Levy A.
186e2f5d8e switch to new parser 2025-08-21 15:19:16 -03:00
Jussi Saurio
5da76c9125 Allow index in UPDATE for point queries (i.e. max 1 row affected) 2025-08-14 15:58:01 +03:00
Jussi Saurio
cd3b4bccd3 Fix UPDATE: Do not use an index for iteration if that index is going to be updated
Closes #2598
2025-08-14 15:35:00 +03:00
Mikaël Francoeur
2cf4e4fe96 handle single, double and unquoted strings in values clause 2025-08-08 09:03:38 -04:00
Piotr Rzysko
59ec2d3949 Replace ConstraintInfo::plan_info with ConstraintInfo::index
The side of the binary expression no longer needs to be stored in
`ConstraintInfo`, since the optimizer now guarantees that it is always
on the right. As a result, only the index of the corresponding constraint
needs to be preserved.
2025-08-05 05:48:29 +02:00
Piotr Rzysko
8fb4fbf8af Make WhereTerm::consumed a plain bool
Now that virtual tables are integrated into the optimizer, this field no
longer needs to be wrapped in Cell<bool>.
2025-08-05 05:48:28 +02:00
Piotr Rzysko
99f87c07c1 Support column references in table-valued function arguments
This change extends table-valued function support by allowing arguments
to be column references, not only literals.

Virtual tables can now reject a plan by returning an error from
best_index (e.g., when a TVF argument references a table that appears
later in the join order). The planner using this information excludes
invalid plans during join order search.
2025-08-05 05:48:28 +02:00
Piotr Rzysko
82491ceb6a Integrate virtual tables with optimizer
This change connects virtual tables with the query optimizer.
The optimizer now considers virtual tables during join order search
and invokes their best_index callbacks to determine feasible access
paths.

Currently, this is not a visible change, since none of the existing
extensions return information indicating that a plan is invalid.
2025-08-05 05:48:28 +02:00
Piotr Rzysko
521eb2368e Return error when no valid plan exists
Replace panics with proper errors when a valid plan does not exist.
Currently, this never happens because a naive plan is always available.
However, once virtual tables are integrated into the planner, it may
occur—for example, when table-valued function arguments are column
references, and the function cannot be placed in the join order so that
its arguments can be evaluated.

Although this change is effectively a no-op for now, it is extracted
into a separate commit to avoid polluting the one that introduces
virtual table integration with the planner.
2025-08-04 20:27:23 +02:00
Piotr Rzysko
c80cd370cb Remove cost_upper_bound_ordered
It was redundant, as it was always equal to cost_upper_bound.
2025-08-04 20:27:23 +02:00