Commit Graph

8473 Commits

Author SHA1 Message Date
Pere Diaz Bou
382a1e14ca Merge 'core: handle edge cases for read_varint' from Sonny
Add handling malformed inputs to function `read_varint` and test cases.
```
# 9 byte truncated to 8
read_varint(&[0x81, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80]) 
    before -> panic index out of bounds: the len is 8 but the index is 8
    after -> LimboError
    
# bits set without end
read_varint(&[0x80; 9]) 
    before -> Ok((128, 9))
    after -> LimboError
```

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

Closes #2904
2025-09-05 16:15:42 +02:00
Pekka Enberg
811c5a7ce0 Merge 'Fix float formatting and comparison + Blob concat' from Levy A.
These changes can be verified with the expression fuzzer. Fixes
https://github.com/tursodatabase/turso/issues/2881.
- Compatible float formatting
- Compatible integer-float comparisons
- Blob concatenation

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

Closes #2929
2025-09-05 17:02:51 +03:00
Pekka Enberg
0a48ea864b Merge 'Fix infinite loop when query starts comment token ("--")' from Lâm Hoàng Phúc
fix #2937 , we dont need to check comment in cli because parser already
handled it (i guest).
How to reproduce:
```sh
turso> --whatCREATE TABLE users (
    id INT PRIMARY KEY,
    first_name VARCHAR(50),
    age INT
);
```

Closes #2938
2025-09-05 16:47:51 +03:00
Pekka Enberg
2c9ad02e91 Merge 'Persistence for DBSP-based materialized views' from Glauber Costa
This fairly long commit implements persistence for materialized view. It
is hard to split because of all the interdependencies between
components, so it is a one big thing. This commit message will at least
try to go into details about the basic architecture.
Materialized Views as tables
============================
Materialized views are now a normal table - whereas before they were a
virtual table.  By making a materialized view a table, we can reuse all
the infrastructure for dealing with tables (cursors, etc).
One of the advantages of doing this is that we can create indexes on
view columns.  Later, we should also be able to write those views to
separate files with ATTACH write.
Materialized Views as Zsets
===========================
The contents of the table are a ZSet: rowid, values, weight. Readers
will notice that because of this, the usage of the ZSet data structure
dwindles throughout the codebase. The main difference between our
materialized ZSet and the standard DBSP ZSet, is that obviously ours is
backed by a BTree, not a Hash (since SQLite tables are BTrees)
Aggregator State
================
In DBSP, the aggregator nodes also have state. To store that state,
there is a second table.  The table holds all aggregators in the view,
and there is one table per view. That is
__turso_internal_dbsp_state_{view_name}. The format of that table is
similar to a ZSet: rowid, serialized_values, weight. We serialize the
values because there will be many aggregators in the table. We can't
rely on a particular format for the values.
The Materialized View Cursor
============================
Reading from a Materialized View essentially means reading from the
persisted ZSet, and enhancing that with data that exists within the
transaction. Transaction data is ephemeral, so we do not materialize
this anywhere: we have a carefully crafted implementation of seek that
takes care of merging weights and stitching the two sets together.

Closes #2921
2025-09-05 16:41:57 +03:00
Pekka Enberg
b2664e12c2 cargo fmt 2025-09-05 16:12:12 +03:00
Pekka Enberg
5dcffadad6 core/vdbe: Remove empty loop
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-05 16:03:25 +03:00
Levy A.
a7b60e6b00 fix: return NULL for negative base or input on exec_math_log 2025-09-05 10:00:59 -03:00
Pekka Enberg
832e0dee81 core/incremental: Fix typos in cursor.rs
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-09-05 15:40:45 +03:00
Glauber Costa
08b2e685d5 Persistence for DBSP-based materialized views
This fairly long commit implements persistence for materialized view.
It is hard to split because of all the interdependencies between components,
so it is a one big thing. This commit message will at least try to go into
details about the basic architecture.

Materialized Views as tables
============================

Materialized views are now a normal table - whereas before they were a virtual
table.  By making a materialized view a table, we can reuse all the
infrastructure for dealing with tables (cursors, etc).

One of the advantages of doing this is that we can create indexes on view
columns.  Later, we should also be able to write those views to separate files
with ATTACH write.

Materialized Views as Zsets
===========================

The contents of the table are a ZSet: rowid, values, weight. Readers will
notice that because of this, the usage of the ZSet data structure dwindles
throughout the codebase. The main difference between our materialized ZSet and
the standard DBSP ZSet, is that obviously ours is backed by a BTree, not a Hash
(since SQLite tables are BTrees)

Aggregator State
================

In DBSP, the aggregator nodes also have state. To store that state, there is a
second table.  The table holds all aggregators in the view, and there is one
table per view. That is __turso_internal_dbsp_state_{view_name}. The format of
that table is similar to a ZSet: rowid, serialized_values, weight. We serialize
the values because there will be many aggregators in the table. We can't rely
on a particular format for the values.

The Materialized View Cursor
============================

Reading from a Materialized View essentially means reading from the persisted
ZSet, and enhancing that with data that exists within the transaction.
Transaction data is ephemeral, so we do not materialize this anywhere: we have
a carefully crafted implementation of seek that takes care of merging weights
and stitching the two sets together.
2025-09-05 07:04:33 -05:00
Pekka Enberg
3341f4657e antithesis: Drop experimental_indexes parameter from commect()
The `experimental_indexes` parameter got dropped from Python bindings so
stop using it to make Antithesis stress-composer drivers to run.
2025-09-05 13:17:39 +03:00
TcMits
f518291522 infinite loop when query starts with '--' 2025-09-05 16:36:14 +07:00
Pekka Enberg
f0fa0aa161 Merge 'Fix sqlite3 test cases' from Pekka Enberg
The CI is sometimes failing with the following error:
```thread '<unnamed>' panicked at sqlite3/src/lib.rs:156:37:
  called `Result::unwrap()` on an `Err` value: InternalError("WAL already enabled")
  note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace

  thread '<unnamed>' panicked at library/core/src/panicking.rs:226:5:
  panic in a function that cannot unwind
  stack backtrace:
     0:     0x7ff612e11502 - std::backtrace_rs::backtrace::libunwind::trace::h74680e970b6e0712
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/../../backtrace/src/backtrace/libunwind.rs:117:9
     1:     0x7ff612e11502 - std::backtrace_rs::backtrace::trace_unsynchronized::ha3bf590e3565a312
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/../../backtrace/src/backtrace/mod.rs:66:14
     2:     0x7ff612e11502 - std::sys::backtrace::_print_fmt::hcf16024cbdd6c458
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/sys/backtrace.rs:66:9
     3:     0x7ff612e11502 - <std::sys::backtrace::BacktraceLock::print::DisplayBacktrace as core::fmt::Display>::fmt::h46a716bba2450163
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/sys/backtrace.rs:39:26
     4:     0x7ff612e338a3 - core::fmt::rt::Argument::fmt::ha695e732309707b7
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/core/src/fmt/rt.rs:181:76
     5:     0x7ff612e338a3 - core::fmt::write::h275e5980d7008551
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/core/src/fmt/mod.rs:1446:25
     6:     0x7ff612e0f003 - std::io::default_write_fmt::hdc4119be3eb77042
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/io/mod.rs:639:11
     7:     0x7ff612e0f003 - std::io::Write::write_fmt::h561a66a0340b6995
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/io/mod.rs:1914:13
     8:     0x7ff612e11352 - std::sys::backtrace::BacktraceLock::print::hafb9d5969adc39a0
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/sys/backtrace.rs:42:9
     9:     0x7ff612e12842 - std::panicking::default_hook::{{closure}}::hae2e97a5c4b2b777
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/panicking.rs:300:22
    10:     0x7ff612e12645 - std::panicking::default_hook::h3db1b505cfc4eb79
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/panicking.rs:327:9
    11:     0x7ff612e131e2 - std::panicking::rust_panic_with_hook::h409da73ddef13937
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/panicking.rs:833:13
    12:     0x7ff612e12f56 - std::panicking::begin_panic_handler::{{closure}}::h159b61b27f96a9c2
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/panicking.rs:699:13
    13:     0x7ff612e119f9 - std::sys::backtrace::__rust_end_short_backtrace::h5b56844d75e766fc
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/sys/backtrace.rs:168:18
    14:     0x7ff612e12c1d - __rustc[4794b31dd7191200]::rust_begin_unwind
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/std/src/panicking.rs:697:5
    15:     0x7ff611f56f9d - core::panicking::panic_nounwind_fmt::runtime::h4c94eb695becba00
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/core/src/panicking.rs:117:22
    16:     0x7ff611f56f9d - core::panicking::panic_nounwind_fmt::hc3cf3432011a3c3f
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/core/src/intrinsics/mod.rs:3196:9
    17:     0x7ff611f57032 - core::panicking::panic_nounwind::h0c59dc9f7f043ead
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/core/src/panicking.rs:226:5
    18:     0x7ff611f57191 - core::panicking::panic_cannot_unwind::hb8732afd89555502
                                 at /rustc/6b00bc3880198600130e1cf62b8f8a93494488cc/library/core/src/panicking.rs:331:5
    19:     0x7ff611f585ee - sqlite3_open
                                 at /home/runner/_work/turso/turso/sqlite3/src/lib.rs:127:1
    20:     0x55f88e591f2c - compat::tests::test_wal_checkpoint_v2::hc70b8ddc1bc8d78d
                                 at /home/runner/_work/turso/turso/sqlite3/tests/compat/mod.rs:188:17
```
As Rust integration tests run in parallel, they cannot use the same
tests files. However, as it turns out, one test does not even need a
file, and the other two are redundant so we can remote them.

Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #2936
2025-09-05 10:57:23 +03:00
Pekka Enberg
55f37398e4 sqlite3: Remove broken sqlite3_checkpoint() test cases
They're both using the same database file which is wrong (tests run in
parallel). But more importantly, they test almost nothing, and we have a
better checkpoint test already.
2025-09-05 10:15:55 +03:00
Pekka Enberg
b0cd184f1e sqlite3: Use in-memory database in test_prepare_misuse()
There's no need for a file.
2025-09-05 10:07:49 +03:00
Levy A.
ef16853bf1 fix: clippy 2025-09-05 03:07:38 -03:00
Levy A.
49eff9a1ca chore: fmt 2025-09-05 03:01:27 -03:00
Levy A.
6d9b57b47e fix: return empty string on NaN 2025-09-05 03:01:27 -03:00
Levy A.
76c8894b1a small tweaks 2025-09-05 03:01:24 -03:00
Levy A.
63cd34ffad feat(fuzz): complete binary operators 2025-09-05 02:35:05 -03:00
Levy A.
73e901010c fix: float formating and float comparison 2025-09-05 02:35:03 -03:00
Pekka Enberg
6d80d862ee Merge 'io_uring: prevent out of order operations that could interfere with durability' from Preston Thorpe
closes #1419
When submitting a `pwritev` for flushing dirty pages, in the case that
it's a commit frame, we use a new completion type which tells io_uring
to add a flag, which ensures the following:
1. If any operation in the chain fails, subsequent operations get
cancelled with -ECANCELED
2. All operations in the chain complete in order
If there is an ongoing chain of `IO_LINK`, it ends at the `fsync`
barrier, and ensures everything submitted before it has completed.
for 99% of the cases, the syscall that immediately proceeds the
`pwritev` is going to be the fsync, but just in case, this
implementation links everything that comes between the final commit
`pwritev` and the next `fsync`
In the event that we get a partial write, if it was linked, then we
submit an additional fsync after the partial write completes, with an
`IO_DRAIN` flag after forcing a `submit`, which will mean durability is
maintained, as that fsync will flush/drain everything in the squeue
before submission.
The other option in the event of partial writes on commit frames/linked
writes is to error.. not sure which is the right move here. I guess it's
possible that since the fsync completion fired, than the commit could be
over without us being durable ondisk. So maybe it's an assertion
instead? Thoughts?

Closes #2909
2025-09-05 08:34:35 +03:00
Pekka Enberg
a711f2f137 Merge 'core: Simplify WalFileShared life cycle' from Pekka Enberg
Create one WalFileShared for a Database and update its state
accordingly. Also support case where the WAL is disabled.

Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>
Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #2918
2025-09-05 08:29:56 +03:00
Pekka Enberg
44684c457b Merge 'Fix non-determinism in simulator ' from Pedro Muniz
Use IndexSet for order preserving iteration instead of HashSet

Closes #2934
2025-09-05 08:29:03 +03:00
pedrocarlo
5f24ff6e45 fix non determinism by using IndexSet for order preserving iteration 2025-09-05 02:00:18 -03:00
Preston Thorpe
2ea2be6f85 Merge 'prevent modification to system tables.' from Glauber Costa
SQLite does not allow us to modify system tables, but we do. Let's fix
it.

Reviewed-by: Preston Thorpe <preston@turso.tech>
Reviewed-by: Avinash Sajjanshetty (@avinassh)

Closes #2855
2025-09-04 19:57:04 -04:00
Preston Thorpe
2d55099854 Merge 'mark completion as done only after callback will be executed' from Nikita Sivukhin
- otherwise, in multi-threading environment, other thread can think that
completion is finished and start execution
- this can lead to violated assertions (for example, page must be
loaded, but as callback is not executed yet assert will be fired)
Failing scenario:
1. main thread wants to execute pread - so it schedule IO and return
control to the caller
2. IO thread read data from the disk
3. IO thread executes complete(result)
4. complete func set result of the completion to Ok
5. main thread enter into the step loop again and check completion
status
6. completion marked as finished/is_completed - so main thread continue
execution
7. main thread check that page is loaded and fails with assertion -
because it's not loaded yet
8. IO thread executed the callback and finished the completion

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

Closes #2922
2025-09-04 19:44:17 -04:00
Glauber Costa
032eabb3a4 prevent modification to system tables.
SQLite does not allow us to modify system tables, but we do.
Let's fix it.
2025-09-04 17:34:47 -05:00
Nikita Sivukhin
4a3d3b3b8c mark completion as done only after callback will be executed
- otherwise, in multi-threading environment, other thread can think that completion is finished
  and start execution
- this can lead to violated assertions (for example, page must be loaded, but as callback is not executed yet
  assert will be fired)
2025-09-04 23:48:08 +04:00
Pekka Enberg
ecbcd1ecd3 Merge ' core/mvcc: make commit_txn return on I/O ' from Pere Diaz Bou
`commit_txn` in MVCC was hacking its way through I/O until now. After
adding this and the test for concurrent writers we now see `busy` errors
returning as expected because there is no `commit` queueing happening
yet until next PR I open.

Closes #2895
2025-09-04 21:24:10 +03:00
Pekka Enberg
5950003eaf core: Simplify WalFileShared life cycle
Create one WalFileShared for a Database and update its state
accordingly. Also support case where the WAL is disabled.
2025-09-04 21:09:12 +03:00
Pekka Enberg
8ba2e1a85c Merge 'Add io_uring support to stress' from Pekka Enberg
Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #2917
2025-09-04 20:24:24 +03:00
Pekka Enberg
d6557db3ac bindings/rust: Return error if io_uring requested on non-Linux
Suggested by @sivukhin
2025-09-04 20:00:22 +03:00
Pekka Enberg
6a6b0cea2b antithesis: Add io_uring stress test 2025-09-04 19:17:46 +03:00
Pekka Enberg
f61e26eeb1 stress: Add --vfs <io-method> command line option
...unlocks io_uring stress runs.
2025-09-04 19:15:01 +03:00
Pekka Enberg
ed6d5fd3d7 bindings/rust: Add with_io() method to Builder 2025-09-04 19:14:45 +03:00
Pekka Enberg
2fcb9dd76f stress: Use CREATE TABLE IF NOT EXISTS to create schema
Different threads might attempt to create the same tables so avoid
spurious errors printed out to the logs with `CREATE TABLE IF NOT
EXISTS`.
2025-09-04 18:56:05 +03:00
Pekka Enberg
adb538e61a stress: Don't die if database is locked during integrity check 2025-09-04 18:48:12 +03:00
Pekka Enberg
efc105d99e Merge 'Fix column count in ImmutableRow' from Glauber Costa
When we create an ImmutableRow::from_value(), we are always adding a
null padding at the end. We didn't notice this before, because a SQLite
file with an extra column is as valid as any. But that column of course
should not be there.
I traced this to column_count(), which is off by one. My understanding
is that we should be returning based on serial_types, not offset.

Closes #2862
2025-09-04 15:04:13 +03:00
Pekka Enberg
98398a9fe6 Merge 'windows iterator returns no values for shorter slice' from Lâm Hoàng Phúc
Closes #2912
2025-09-04 13:10:18 +03:00
Pekka Enberg
7e2bfd8bc1 Merge 'Refactor LIMIT/OFFSET handling to support expressions' from bit-aloo
Add expression support for `LIMIT` and `OFFSET` by storing them as
`Expr` instead of fixed integers. Constant expressions are folded with
`try_fold_to_i64`, while dynamic ones emit runtime checks, including the
new `IfNeg` opcode to clamp negative or `NULL` values to zero. The
current `build_limit_offset_expr` implementation is still naive and will
be refined in future work.
Fixes #2913

Closes #2720
2025-09-04 11:43:50 +03:00
Pekka Enberg
ef0d10bf2f Merge 'Encryption: add support for other AEGIS and AES-GCM cipher variants' from Frank Denis
Now supported:
- AEGIS variants: 256, 256X2, 256X4, 128L, 128X2, 128X4
- AES-GCM variants: AES-128-GCM, AES-256-GCM
With minor changes in order to make it easy to add new ciphers later
regardless of their key size.

Reviewed-by: Avinash Sajjanshetty (@avinassh)

Closes #2899
2025-09-04 11:42:16 +03:00
Pekka Enberg
1511ad354b Turso 0.1.5-pre.3 2025-09-04 11:40:51 +03:00
Pekka Enberg
adca9e4c70 Merge 'introduce package.json for separate *-browser package (both database and sync)' from Nikita Sivukhin
This PR introduces separate `package.browser.json` file for `*-browser`
npm packages (`@tursodatabase/sync-browser` and
`@tursodatabase/database-browser`).
The packages are nearly identical and the only change is `package.json`
content (browser package mentions only WASM optional dependency which
shouldn't confuse NPM and force it to download WASM dep package instead
of native one).
Due to that, innocent "hack" is implemented which swap `package.json`
with `package.browser.json` before publish of `browser` package.

Closes #2906
2025-09-04 11:40:34 +03:00
Pekka Enberg
44357f93a2 Merge branch 'main' into 2025-08-21-make-limit-and-offset-expr 2025-09-04 09:54:45 +03:00
TcMits
ddbfb6cc16 make clippy happy 2025-09-04 13:04:53 +07:00
TcMits
ce6ff74cd6 add test 2025-09-04 13:02:10 +07:00
TcMits
94b1cf9ab5 windows iterator returns no values for shorter slice 2025-09-04 12:09:21 +07:00
Preston Thorpe
caaf60a7ea Merge 'Unify resolution of aggregate functions' from Piotr Rżysko
This PR unifies the logic for resolving aggregate functions. Previously,
bare aggregates (e.g. `SELECT max(a) FROM t1`) and aggregates wrapped in
expressions (e.g. `SELECT max(a) + 1 FROM t1`) were handled differently,
which led to duplicated code. Now both cases are resolved consistently.
The added benchmark shows a small improvement:
```
Prepare `SELECT first_name, last_name, state, city, age + 10, LENGTH(email), UPPER(first_name), LOWE...
                        time:   [59.791 µs 59.898 µs 60.006 µs]
                        change: [-7.7090% -7.2760% -6.8242%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 10 outliers among 100 measurements (10.00%)
  8 (8.00%) high mild
  2 (2.00%) high severe
```
For an existing benchmark, no change:
```
Prepare `SELECT first_name, count(1) FROM users GROUP BY first_name HAVING count(1) > 1 ORDER BY cou...
                        time:   [11.895 µs 11.913 µs 11.931 µs]
                        change: [-0.2545% +0.2426% +0.6960%] (p = 0.34 > 0.05)
                        No change in performance detected.
Found 8 outliers among 100 measurements (8.00%)
  1 (1.00%) low severe
  2 (2.00%) high mild
  5 (5.00%) high severe
```

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

Closes #2884
2025-09-03 19:46:04 -04:00
Preston Thorpe
c55c7d76c3 Merge 'replace some matches with match_ignore_ascii_case macro' from Lâm Hoàng Phúc
Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #2903
2025-09-03 17:03:19 -04:00
PThorpe92
d894a62132 Add plumbing in io_uring to handle linked writes to ensure consistency 2025-09-03 16:01:16 -04:00