Commit Graph

8400 Commits

Author SHA1 Message Date
Pere Diaz Bou
662da34e7d core/mvcc: implement seeking operations with rowid 2025-08-04 13:52:54 +02:00
Pekka Enberg
e4accdc29d Merge 'hide dangerous methods behind conn_raw_api feature' from Nikita Sivukhin
WAL API shouldn't be exposed by default because this is relatively
dangerous API which we use internally and ordinary users shouldn't not
be interested in it.

Reviewed-by: Pekka Enberg <penberg@iki.fi>

Closes #2424
2025-08-04 14:52:40 +03:00
Pekka Enberg
1572285ee6 Merge 'preserve files in IO memory backend' from Nikita Sivukhin
Simple PR to preserve and reuse files in memory IO

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

Closes #2428
2025-08-04 14:52:24 +03:00
Pekka Enberg
5b5bc441b7 Merge ' core/mvcc: fix new rowid on restart' from Pere Diaz Bou
Next rowid was being tracked globally for all tables and restarted to 0
every time database was opened

Closes #2425
2025-08-04 14:37:13 +03:00
Nikita Sivukhin
129895f0b2 preserve files in IO memory backend 2025-08-04 15:22:04 +04:00
Pere Diaz Bou
56240ddac9 core/mvcc: add restart tests 2025-08-04 12:31:17 +02:00
Pere Diaz Bou
f26e442597 core/mvcc: fix new rowid
next rowid was being tracked globally for all tables and restarted to 0
every time database was opened
2025-08-04 12:31:17 +02:00
Pere Diaz Bou
83a658d3d6 core/mvcc: add option to test with a random file and restart it 2025-08-04 12:31:17 +02:00
Nikita Sivukhin
83b1e99a61 fix compilation 2025-08-04 12:53:07 +04:00
Nikita Sivukhin
0adb40534c hind dangerous methods behind conn_raw_api feature 2025-08-04 12:40:28 +04:00
Jussi Saurio
4f3f66d55e fix/wal: remove start_pages_in_frames_hack to prevent checkpoint data loss
We have some kind of transaction-local hack (`start_pages_in_frames`) for bookkeeping
how many pages are currently in the in-memory WAL frame cache,
I assume for performance reasons or whatever.

`wal.rollback()` clears all the frames from `shared.frame_cache` that the rollbacking tx is
allowed to clear, and then truncates `shared.pages_in_frames` to however much its local
`start_pages_in_frames` value was.

In `complete_append_frame`, we check if `frame_cache` has that key (page) already, and if not,
we add it to `pages_in_frames`.

However, `wal.rollback()` never _removes_ the key (page) if its value is empty, so we can end
up in a scenario where the `frame_cache` key for `page P` exists but has no frames, and so `page P`
does not get added to `pages_in_frames` in `complete_append_frame`.

This leads to a checkpoint data loss scenario:

- transaction rolls back, has start_pages_in_frames=0, so truncates
  shared pages_in_frames to an empty vec. let's say `page P` key in `frame_cache` still remains
  but it has no frames.
- The next time someone commits a frame for `page P`, it does NOT get added to `pages_in_frames`
  because `frame_cache` has that key
- At some point, a PASSIVE checkpoint checkpoints `n` frames, but since `pages_in_frames` does not have
  `page P`, it doesn't actually checkpoint it and all the "checkpointed" frames are simply thrown away
- very similar to the scenario in #2366

Remove the `start_pages_in_frames` hack entirely and just make `pages_in_frames` effectively
the same as `frame_cache.keys`. I think we could also just get rid of `pages_in_frames` and just use
`frame_cache.contains_key(p)` but maybe Pere can chime in here
2025-08-04 10:35:12 +03:00
Pekka Enberg
deec70e541 Merge 'Improve SQLite3 TCL test suite' from Pekka Enberg
Add more stubs in tester.tcl so that the test suite does not bail out
early.

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

Closes #2405
2025-08-04 08:43:14 +03:00
Pekka Enberg
ca14799da5 Merge 'Make completions idempotent' from Preston Thorpe
Closes #2417
2025-08-04 08:42:42 +03:00
Pekka Enberg
3691b51039 Merge 'perf/btree: skip seek in move_to_rightmost() if we are already on rightmost page' from Jussi Saurio
## Background
When we get a new rowid using `op_new_rowid()`, we move to the end of
the btree to look at what the maximum rowid currently is, and then
increment it by one.
This requires a btree seek.
## Problem
If we were already on the rightmost page, this is a lot of unnecessary
work, including potentially a few page reads from disk (although to be
fair the ancestor pages are very likely to be in cache at this point.)
## Fix
Cache the rightmost page id whenever we enter it in
`move_to_rightmost()`, and invalidate it whenever we do a balancing
operation.
## Local benchmark results
```sql
Insert rows in batches/limbo_insert_1_rows
                        time:   [23.333 µs 27.718 µs 35.801 µs]
                        change: [-7.7924% +0.8805% +12.841%] (p = 0.91 > 0.05)
                        No change in performance detected.
Insert rows in batches/limbo_insert_10_rows
                        time:   [38.204 µs 38.381 µs 38.568 µs]
                        change: [-8.7188% -7.4786% -6.1955%] (p = 0.00 < 0.05)
                        Performance has improved.
Insert rows in batches/limbo_insert_100_rows
                        time:   [158.39 µs 165.06 µs 178.37 µs]
                        change: [-21.000% -18.789% -15.666%] (p = 0.00 < 0.05)
                        Performance has improved.

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

Closes #2409
2025-08-04 08:41:51 +03:00
TcMits
5b0d4b1731 fix error 2025-08-03 20:00:48 +07:00
TcMits
70c08558b4 doing select 2025-08-03 19:57:21 +07:00
PThorpe92
79629daff4 Make completions idempotent 2025-08-02 21:48:39 -04:00
Levy A.
b9a3a93ef0 fix: clippy 2025-08-02 20:06:05 -03:00
PThorpe92
b5117ac5c7 Use pwrite for single buffer in unix IO 2025-08-02 18:34:16 -04:00
Levy A.
b14a11a2fd fix: change name for schema btree + fix benchmark 2025-08-02 17:17:36 -03:00
Jussi Saurio
130e1f80ea fix/vdbe: call seek_to_last() only once in op_new_rowid 2025-08-02 14:18:58 +03:00
Jussi Saurio
63a5ef596b perf/btree: skip seek in move_to_rightmost() if we are already on rightmost page 2025-08-02 13:56:59 +03:00
Pekka Enberg
a4dcbeb392 Update PERF.md 2025-08-02 13:37:42 +03:00
Jussi Saurio
3b0c8b08fe Merge 'perf/pager: dont clear page cache on commit' from Jussi Saurio
This should be safe to do as:
1. page cache is private per connection
2. since this connection wrote the flushed pages/frames, they are up to
date from its perspective
3. multiple concurrent statements inside one connection are not
snapshot-transactional even in sqlite

Reviewed-by: Pekka Enberg <penberg@iki.fi>

Closes #2407
2025-08-02 13:35:57 +03:00
Jussi Saurio
66f1ff9ad0 btree/defragment_page: fix corruption check assertion 2025-08-02 13:28:41 +03:00
Joao Severo
1f21d92f6d use turbo_assert! 2025-08-02 13:24:12 +03:00
Joao Severo
71b09727d9 add comment clarifying the cell ordering 2025-08-02 13:24:12 +03:00
Joao Severo
3adae17483 Remove redundant cast 2025-08-02 13:24:12 +03:00
Joao Severo
785c3fdb03 Split into two methods 2025-08-02 13:24:12 +03:00
Joao Severo
8f4ea280ae Update defragment page to run in-place 2025-08-02 13:24:11 +03:00
Jussi Saurio
4497d22d3f perf/pager: dont clear page cache on commit 2025-08-02 13:09:36 +03:00
Pekka Enberg
5037e2dc0d testing/sqlite3: Update all.test 2025-08-02 13:00:18 +03:00
Pekka Enberg
779b8e0149 testing/sqlite3: Import more join test cases 2025-08-02 12:59:11 +03:00
Pekka Enberg
c7497d55b6 testing/sqlite3: Disable select9.test
...it seems to run forever.
2025-08-02 12:59:11 +03:00
Pekka Enberg
a380e0775d testing/sqlite3: Improve tester.tcl
Improve tester.tcl by adding stubs so that we can run more of the test
suite without the test harness giving up.
2025-08-02 12:59:11 +03:00
Pekka Enberg
bca9994cf6 Merge 'Rename liblimbo_sqlite3 to libturso_sqlite3' from Pekka Enberg
Closes #2403
2025-08-02 12:34:19 +03:00
Pekka Enberg
12455c6531 Merge 'core: Fold HeaderRef to pager module' from Pekka Enberg
Closes #2401
2025-08-02 12:34:13 +03:00
Pekka Enberg
4aea9372bd Rename liblimbo_sqlite3 to libturso_sqlite3 2025-08-02 11:24:40 +03:00
TcMits
2e7de4ccd6 parse SAVEPOINT, RELEASE 2025-08-02 15:10:41 +07:00
TcMits
6baa5abb56 parse ROLLBACK 2025-08-02 14:47:49 +07:00
TcMits
62886ee8a1 parse COMMIT|END 2025-08-02 14:32:01 +07:00
TcMits
a23500274f fix parse nm 2025-08-02 14:19:32 +07:00
Pekka Enberg
2c05a3e787 Merge 'perf/vdbe: remove eager cloning in op_comparison' from Jussi Saurio
Shaves off about 100-200ms of runtime from TPC-H `19.sql`

Closes #2385
2025-08-02 10:01:47 +03:00
Pekka Enberg
598fdade3e core: Fold HeaderRef to pager module 2025-08-02 09:50:25 +03:00
Pekka Enberg
9a82e269cd Merge 'Update cargo-dist to the latest official version' from Hiroaki Yutani
https://github.com/tursodatabase/turso/pull/1256 switched cargo-dist to
Astral's forked version, but, recently, the official repository got a
new maintainer and started to be maintained again.
Their latest release, [v0.29.0](https://github.com/axodotdev/cargo-
dist/releases/tag/v0.29.0), now includes the features originally added
to Astral's version. So, probably it's a good time to switch back to the
official cargo-dist. That said, as there's no significant changes from
Astral's version, it's also fine to hold the current one.

Closes #2398
2025-08-02 09:46:26 +03:00
Jussi Saurio
43c1afe4b6 Merge 'bindings/rust: Enhance API by removing verbosity' from Diego Reis
While working on #2151 I saw myself forced to do things like:
```rust
assert_eq!(
                6,
                *result
                    .next()
                    .await?
                    .unwrap()
                    .get_value(0)?
                    .as_integer()
                    .unwrap()
            );
```
Just to get a simple value from a row, now with this PR users can just
do:
```rust
assert_eq!(6, result.get::<i32>(0)?);
```
(Thanks libsql devs, this is so much better!)

Closes #2377
2025-08-02 09:39:27 +03:00
Jussi Saurio
c6b178483b Merge 'io_uring: setup plumbing for Fixed opcodes' from Preston Thorpe
This PR by itself is uninteresting and doesn't do anything. But I am
heavily trying to avoid massive PR's, and this is very merge-able
😄

Closes #2396
2025-08-02 09:37:48 +03:00
Jussi Saurio
be1456f7cb Merge 'use state machine for NoConflict opcode' from Mikaël Francoeur
This will save some work when yielding to IO. Previously, on every
invocation, if the record was a packed record, we parsed it and iterated
through the values to check for nulls. Now, the pre-seeking work is done
only once.

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

Closes #2394
2025-08-02 09:37:00 +03:00
Jussi Saurio
37a565021e Merge 'state_machine: remove State associated type' from Pere Diaz Bou
Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #2395
2025-08-02 09:36:43 +03:00
Preston Thorpe
97b236f217 Merge 'Fix numeric overflow in numeric string parsing in logical comparision' from
Closes #2077-
This PR fixes an integer overflow bug that causes results from Turso to
differ from SQLite.
-Commit 1: Fixes incorrect logic that failed to detect integer overflow
when parsing long numeric strings.
-Commit 2: Handles the case where a parsed numeric string is stored as a
float and classified as PureInteger, but lies outside the integer range.
Previously, `parsed_value.as_integer()` would return None, causing Turso
to fall back to text comparison against numeric values. This caused
**another** erroneous result, as shown below.
`$> SELECT (-104614899632619 || 45597) > CAST(0 AS NUMERIC); -- tursodb
= 1(wrong), sqlite = 0`
 Now, if Turso fails to convert a very long numeric string to an
integer, it tries to convert it to a float. This is in line with the
`static void applyNumericAffinity` function in sqlite3
**Before**
<img width="623" height="238" alt="Screenshot 2025-08-01 at 12 11 49 PM"
src="https://github.com/user-attachments/assets/796d6ff6-768b-40ef-
ac83-e0c55fff6bd9" />
**After**
`SELECT (104614899632619 || 45597) > CAST(0 AS NUMERIC); -- tursodb = 1,
sqlite = 1`
`SELECT (-104614899632619 || 45597) > CAST(0 AS NUMERIC); -- tursodb =
0, sqlite = 0`

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

Closes #2397
2025-08-01 21:39:30 -04:00