Commit Graph

7151 Commits

Author SHA1 Message Date
Jussi Saurio
9cf8a9b533 Merge 'refactor/btree: don't clone WriteState during balancing' from Jussi Saurio
## Beef
Removes cloning of `WriteState` in `balance_non_root()` and removes the
`Clone` implementation from `WriteState`.  Not only is it unnecessary to
clone the state, but even having the `Clone` implementation available is
a real regression risk in `insert_into_page()` as it will deep clone
vectors that we require stable raw pointers to in `fill_cell_payload()`
## Details
In removing the cloning of `WriteState` in `balance_non_root()`, this
replaces the `next_write_state` logic with a plain loop, which ends up
looking like a lot of changes, but it's really not. Main things that
were changed:
- Loop and mutate state instead of create new state and return
- Make `WriteInfo` an `Option` in `WriteState` for ergonomics so it can
be `.take()`:n instead of `.clone()`:d
- Clone a `Rc<Pager>` in `balance_non_root()` so that we can call
`pager.do_allocate_page()` directly instead of `self.allocate_page()`
which is just a facade for the pager method, and would violate borrowing
rules.
- assign `let usable_space = self.usable_space()` at the beginning of
the balance loop for similar reasons.
- Make the balance debug validation methods static so they don't require
a reference to self, for similar reasons

Reviewed-by: Nikita Sivukhin (@sivukhin)
Reviewed-by: Avinash Sajjanshetty (@avinassh)

Closes #2466
2025-08-06 12:03:23 +03:00
Jussi Saurio
5f3cfaac60 refactor/btree: don't clone WriteState in balance_non_root() 2025-08-06 11:30:09 +03:00
Jussi Saurio
a15d7dd2e7 refactor/btree: don't clone WriteState in balance() 2025-08-06 11:30:09 +03:00
Jussi Saurio
3d635ecd67 Merge 'refactor/btree: don't clone WriteState in insert_into_page()' from Jussi Saurio
## Problem
We currently clone `WriteState` in every loop iteration of
`insert_into_page()`, which was probably done for borrow checker
reasons, but since `WriteState` has expanded to include buffers that
must not be moved in memory or dropped, it has necessitated a really
annoying workaround of wrapping the buffers in `Arc<Mutex>>` which is
just completely wasteful.
## Fix
Do not clone `WriteState` in `insert_into_page()`, and instead work with
the borrow checker a bit more. Note that `WriteState` still _implements_
`Clone` because it's also cloned in `balance_non_root()` - that can be a
separate refactor.

Reviewed-by: Avinash Sajjanshetty (@avinassh)
Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #2464
2025-08-06 11:29:55 +03:00
Jussi Saurio
406cbb9e78 Merge 'Coll seq' from Glauber Costa
Implement the CollSeq vdbe opcode.

Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #2454
2025-08-06 11:28:27 +03:00
Pekka Enberg
2913dc4dd4 Turso 0.1.4-pre.2 2025-08-06 10:52:32 +03:00
Jussi Saurio
1c1f55fdfb refactor/btree: remove cloning of WriteState in insert_into_page() 2025-08-06 08:50:56 +03:00
Jussi Saurio
c3a32b63bf refactor/btree: remove unnecessary ref of self in overwrite_content() 2025-08-06 08:45:34 +03:00
Jussi Saurio
6dd08c21e4 refactor/btree: remove unnecessary mut ref of self in rowid() 2025-08-06 08:44:52 +03:00
Jussi Saurio
fc95282c57 Merge 'core/btree: fix re-entrancy bug in insert_into_page()' from Jussi Saurio
## Background
We currently clone `WriteState` on every iteration of
`insert_into_page()`,
presumably for _Borrow Checker Reasons (tm)_.
## Problem
There was a bug in `WriteState::Insert` handling where if
`fill_cell_payload()`
returned IO, the `fill_cell_payload_state` was not updated in
`write_info.state`, leading to an infinite loop of allocating new pages.
Further, the `new_payload` vector was also always deep-cloned.
## Fix
Update the state if `fill_cell_payload()` returns IO. Because of
`WriteState` cloning we also need to `Arc<Mutex>` wrap the vector so
that the underlying data buffer doesn't get cloned the next time
`WriteState` gets cloned, since `fill_cell_payload` relies on raw
pointers to work. Left a couple of prominent FIXMEs about this.
## Notes
This bug was surfaced by, but not caused by,
https://github.com/tursodatabase/turso/pull/2400.

Closes #2463
2025-08-06 08:23:49 +03:00
Jussi Saurio
839d428e36 core/btree: fix re-entrancy bug in insert_into_page()
We currently clone WriteState on every iteration of `insert_into_page()`,
presumably for Borrow Checker Reasons (tm).

There was a bug in `WriteState::Insert` handling where if `fill_cell_payload()`
returned IO, the `fill_cell_payload_state` was not updated in
`write_info.state`, leading to an infinite loop of allocating new pages.

This bug was surfaced by, but not caused by, #2400.
2025-08-06 08:01:49 +03:00
Jussi Saurio
cd3fe523a3 core/types: add IOResult::is_io() helper 2025-08-06 07:46:51 +03:00
PThorpe92
f6fb786cc9 Fix borrow method on WindowsIO 2025-08-05 22:26:19 -04:00
Preston Thorpe
cb9a74ccc6 Merge 'Remove RefCell from Buffer in IO trait methods and PageContents' from Preston Thorpe
We are doing unsafe mut borrowing in `PageContent` currently, and when
new BufferPool is merged, it will be all pointers into an arena anyway.
We just need to be sure to clone the Arc and reference the buffers in
the completion callbacks so they are ensured to live for async IO.
naturally in true spirit of all my previous PR's, I needed to introduce
one while another is open that causes an absurd amount of conflicts.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2456
2025-08-05 21:30:27 -04:00
PThorpe92
4010c7bf0b Make clippy happy 2025-08-05 21:24:54 -04:00
PThorpe92
53a0524050 Fix clippy warning 2025-08-05 16:24:50 -04:00
PThorpe92
f6a68cffc2 Remove RefCell from IO and Page apis 2025-08-05 16:24:49 -04:00
Preston Thorpe
01531be9f4 Merge 'Remove Clone impl for Buffer and PageContent' from Preston Thorpe
After #2258, we no longer need to clone/deep copy `PageContent` or
`Buffer`.
We can remove the implementation to make any copying of page data
explicit.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2453
2025-08-05 16:17:13 -04:00
Glauber Costa
d1be7ad0bb implement the collseq bytecode instruction
SQLite generates those in aggregations like min / max with collation
information either in the table definition or in the column expression.

We currently generate the wrong result here, and properly generating the
bytecode instruction fixes it.
2025-08-05 13:49:04 -05:00
Glauber Costa
6a66053ca8 make sure value comparisons for min and max are collation aware
They currently aren't, which isn't right.
2025-08-05 13:39:38 -05:00
PThorpe92
914c10e095 Remove Clone impl for Buffer and PageContent 2025-08-05 14:26:53 -04:00
Pekka Enberg
9492a29d47 Merge 'Fix performance regression' from Jussi Saurio
Closes #2440
## Fix 1
Do not start a read transaction when a SELECT is not going to access the
database, which means we can avoid checking whether the schema has
changed.
## Fix 2
Add a field `accesses_db` to `Program` and `Statement` so we can avoid
even checking for `SchemaUpdated` errors when it's not possible to get
one.
## Fix 3
Avoid doing any work in `commit_txn` when not in a transaction. This
optimization is only enabled when `mv_store.is_none()`, because MVCC has
its own logic and this doesn't work with MVCC enabled, and honestly I'm
too tired to find out why. Left an inline comment about it, though.
```sql
Execute `SELECT 1`/limbo_execute_select_1
                        time:   [21.440 ns 21.513 ns 21.586 ns]
                        change: [-60.766% -60.616% -60.453%] (p = 0.00 < 0.05)
                        Performance has improved.
```
Effect is even more dramatic in CI where the latency is down over 80%

Closes #2441
2025-08-05 16:30:18 +03:00
Jussi Saurio
cde8567b1d Merge 'More state machine + Return IO in places where completions are created' from Pedro Muniz
In preparation for tracking IO Completions, we need to start to return
IO in places where completions are created. Doing some more plumbing now
to avoid bigger PRs for the future

Closes #2438
2025-08-05 15:47:51 +03:00
Jussi Saurio
d13a1a0eec Merge 'test/fuzz: add ALTER TABLE column ops to tx isolation fuzz test' from Jussi Saurio
## Beef
Adds `AddColumn`, `DropColumn`, `RenameColumn`
## Details
- Previously the test was hardcoded to assume there's always 2 named
columns, so changed a bunch of things for this reason
- Still assumes the primary key column is always `id` and is never
renamed or dropped etc.

Closes #2434
2025-08-05 15:42:31 +03:00
Pekka Enberg
49123db6e8 Merge 'core/mvcc: implement exists' from Pere Diaz Bou
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2446
2025-08-05 15:34:23 +03:00
Jussi Saurio
9f820ae4c7 Merge 'cleanup: remove unused page uptodate flag' from Jussi Saurio
Closes #2445
2025-08-05 15:26:41 +03:00
Jussi Saurio
1feb5ba2d3 perf/vdbe: avoid doing work in commit_txn if not in txn 2025-08-05 15:25:28 +03:00
Jussi Saurio
3f633247f7 perf/stmt: avoid checking for SchemaUpdated errors if it's impossible 2025-08-05 15:10:55 +03:00
Jussi Saurio
c498196c7b fix/perf: fix regression in SELECT 1 benchmark
Do not start a read transaction when a SELECT is not going to access
the database, which means we can avoid checking whether the schema has
changed.
2025-08-05 15:10:55 +03:00
Pere Diaz Bou
474f0d8bbc core/mvcc: implement exists 2025-08-05 13:34:51 +02:00
Jussi Saurio
a28e64bfdd cleanup: remove unused page uptodate flag 2025-08-05 14:25:42 +03:00
Pere Diaz Bou
a3cf3051d7 Merge 'tests/fuzz_transactions: add tests for fuzzing transactions with MVCC' from Pere Diaz Bou
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2444
2025-08-05 13:18:57 +02:00
Pekka Enberg
d2fea25fef Merge 'perf/btree: implement fast algorithm for defragment_page' from Jussi Saurio
Implement sqlite's fast path defragment algorithm. This path is taken
when:
1. There are 1-2 freeblocks
2. There are at most `max_frag_bytes` fragmented free bytes (-1..=4)
Instead of reconstructing the entire page, it merges the two freeblocks
and then moves the merged freeblock to the left, effectively turning it
into free space in the unallocated region, instead of a freeblock.
`max_frag_bytes` is particularly important when jnserting a new cell,
because if the page contains (in total) ~just enough space for the new
cell, then there can be hardly any fragmented free space because
otherwise, merging the 1-2 freeblocks won't produce enough contiguous
free space to fit the cell.
## Benchmark
```sql
Insert rows in batches/limbo_insert_1_rows
                        time:   [26.692 µs 27.153 µs 27.695 µs]
                        change: [-9.9033% -2.9097% +1.6336%] (p = 0.55 > 0.05)
                        No change in performance detected.
Insert rows in batches/limbo_insert_10_rows
                        time:   [38.618 µs 40.022 µs 42.201 µs]
                        change: [-8.9137% -6.6405% -4.2299%] (p = 0.00 < 0.05)
                        Performance has improved.
Insert rows in batches/limbo_insert_100_rows
                        time:   [168.94 µs 169.58 µs 170.31 µs]
                        change: [-22.520% -17.669% -12.790%] (p = 0.00 < 0.05)
                        Performance has improved.
```

Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>

Closes #2411
2025-08-05 12:44:48 +03:00
Pekka Enberg
aa20c2f1ba Merge 'Relax I/O configuration attribute to cover all Unixes' from Pedro Muniz
hopefully fixes #2268.

Closes #2435
2025-08-05 12:44:34 +03:00
Pere Diaz Bou
2a3e2349ca tests/fuzz_transactions: add tests for fuzzing transactions with MVCC 2025-08-05 11:43:26 +02:00
Pekka Enberg
a972b81a39 Merge 'bindings/rust: add with_mvcc option, open with path too!' from Pere Diaz Bou
Closes #2443
2025-08-05 12:42:30 +03:00
Pekka Enberg
e355fc4c65 Merge 'core/mvcc: implement seeking operations with rowid' from Pere Diaz Bou
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2429
2025-08-05 12:40:48 +03:00
Pekka Enberg
d9fd56c31f Merge 'bindings/rust: add with_mvcc option' from Pere Diaz Bou
Closes #2442
2025-08-05 12:40:34 +03:00
Pere Diaz Bou
2392ea1b55 bindings/rust: add with_mvcc option 2025-08-05 11:40:23 +02:00
Pere Diaz Bou
e5fc08317a bindings/rust: add with_mvcc option 2025-08-05 11:28:36 +02:00
Jussi Saurio
ad35cf07eb Add extra illustrative doodle for pere 2025-08-05 11:24:15 +03:00
Jussi Saurio
a5330aa6fb perf/btree: implement fast algorithm for defragment_page 2025-08-05 11:24:14 +03:00
Jussi Saurio
5b84ad6b0f Merge 'Update defragment page to defragment in-place' from João Severo
Change original code from doing a full copy of the original buffer to
modify the buffer in-place using a temporary vector with offsets.

Closes #2258
2025-08-05 11:22:22 +03:00
Jussi Saurio
cd79d2dce5 test/fuzz: add ALTER TABLE column ops to tx isolation fuzz test 2025-08-05 10:05:56 +03:00
Jussi Saurio
685615dc98 test/fuzz/txn: remove assumption about hardcoded column count 2025-08-05 10:04:25 +03:00
Jussi Saurio
c9c5565867 Merge 'Integrate virtual tables with optimizer' from Piotr Rżysko
This PR integrates virtual tables into the query optimizer. It is a
follow-up to https://github.com/tursodatabase/turso/pull/1727.
The most immediate improvement is better support for inner joins
involving TVFs, particularly when TVF arguments are column references.
### Example
The following two queries are semantically equivalent, but require
different join orders to be valid:
```sql
-- TVF depends on `t.id`, so `t` must be evaluated in outer loop
SELECT t.id, series.value
FROM target t, generate_series(t.id, 3) series;

-- Equivalent query, but with reversed table order in the FROM clause
SELECT t.id, series.value
FROM generate_series(t.id, 3) series, target t;
```
Without optimizer integration, the second query would fail because the
planner would attempt to evaluate `generate_series` before `t`. With
this change, the optimizer detects column dependencies and produces the
correct join order in both cases.
### TODO
Support for outer joins with TVFs is still missing and will be addressed
in a follow-up PR.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2439
2025-08-05 09:22:08 +03:00
pedrocarlo
aa8d17cbf1 state machine for ptrmap_get 2025-08-05 01:38:42 -03: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