Commit Graph

6618 Commits

Author SHA1 Message Date
Pekka Enberg
551df4ce0d bindings/rust: Fix test_database_persistence_many_frames()
We need to make sure database connection is closed to avoid reusing he
same `Database` object.
2025-07-28 13:04:22 +03:00
Pekka Enberg
b2a5a7077c sqlite3: Fix WAL tests by closing connection
Commit ac33ae90 ("core: Enforce single, shared database object per
database file") changes the semantics of the WAL because unless we close
all the connections, the WAL remains open due to `Database` being kept
open in memory. Fix test failures by properly closing the connection
between different test cases.
2025-07-28 13:04:22 +03:00
Pekka Enberg
aca6ffa042 Merge 'io/unix: wrap file with Mutex' from Pere Diaz Bou
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2301
2025-07-28 12:53:38 +03:00
Pekka Enberg
5b8efda5da Merge 'core: Clone everything in schema' from Pere Diaz Bou
Closes #2300
2025-07-28 12:53:22 +03:00
Pere Diaz Bou
f458f622a5 io/unix: wrap file with Mutex 2025-07-28 11:33:57 +02:00
Pere Diaz Bou
752a876f9a change every Rc to Arc in schema internals 2025-07-28 10:51:17 +02:00
Pere Diaz Bou
d273de483f comment clone for schema 2025-07-28 10:50:50 +02:00
Pere Diaz Bou
6ec80b3364 clone everything in schema 2025-07-28 10:27:45 +02:00
Pekka Enberg
e4a1856ef0 UTF-8 is the only supported character encoding
We have no plans to support UTF-16 unless there's a major application
that needs it.
2025-07-28 09:55:18 +03:00
Pekka Enberg
fd2a7f9098 core: Switch to unreachable for invalid enum variants
The parser unfortunately outputs Stmt, which has some enum variants that
we never actually encounter in some parts of the core. Switch to
unreachable instead of todo.
2025-07-28 09:52:20 +03:00
Pekka Enberg
a02a590f88 Merge 'core/translate: Handle Expr::Id in CREATE INDEX' from Kristofer
I am running into issues when creating indexes and made this PR with a
possible fix.
`Error: cannot use expressions in CREATE INDEX`
In my setup, running on `wasm32-unknown-unknown` (not in the browser), I
can reproduce the issue like this. First, creating a table:
```rust
conn.execute(
    r#"
    CREATE TABLE IF NOT EXISTS users (
        name TEXT,
        created DATETIME DEFAULT CURRENT_TIMESTAMP
    )
    "#,
    (),
)
.await
.unwrap();
```
Here, creating an index for that table:
```rust
conn.execute(
    "CREATE INDEX IF NOT EXISTS idx_users_name ON users(name)",
    (),
)
.await
.unwrap();
```
## Findings
I had a closer look at `resolve_sorted_columns`. In this bit, it checks
the expression of the sorted column.
https://github.com/tursodatabase/turso/blob/a2a31a520ff6e228a00e785026da
e19b5b2cced7/core/translate/index.rs#L252-L257
```rust
let ident = normalize_ident(match &sc.expr {
    // SQLite supports indexes on arbitrary expressions, but we don't (yet).
    // See "How to use indexes on expressions" in https://www.sqlite.org/expridx.html
    Expr::Name(ast::Name::Ident(col_name)) | Expr::Name(ast::Name::Quoted(col_name)) => {
        col_name
    }
    _ => crate::bail_parse_error!("Error: cannot use expressions in CREATE INDEX"),
});
```
If it is not an `Expr::Name`, function fails.
But, the `sc.expr` I am getting is not `Expr::Name` but `Expr::Id`.
Which doesn't seem unexpected but rather expected. Reading up on the
`sqlite3_parser` AST, it seems that both `Name` and `Id` can   be
expected.
Adding `Expr::Id` to the check fixes the issue.
```rust
let ident = normalize_ident(match &sc.expr {
    // SQLite supports indexes on arbitrary expressions, but we don't (yet).
    // See "How to use indexes on expressions" in https://www.sqlite.org/expridx.html
    Expr::Id(ast::Name::Ident(col_name))
    | Expr::Id(ast::Name::Quoted(col_name))
    | Expr::Name(ast::Name::Ident(col_name))
    | Expr::Name(ast::Name::Quoted(col_name)) => col_name,
    _ => crate::bail_parse_error!("Error: cannot use expressions in CREATE INDEX"),
});
```

Closes #2294
2025-07-28 08:54:45 +03:00
Pekka Enberg
d92ebd6d37 Merge 'Fix writing wal header for async IO' from Preston Thorpe
We previously were making another inline completion inside io_uring.rs,
I thought this wouldn't be needed anymore because of the Arc that is now
wrapping the RefCell<Buffer>, but in the case of the WAL header, where
it's not pinned to a page in the cache, there is nothing to keep it
alive and we will write a corrupt wal header.
```rust
        #[allow(clippy::arc_with_non_send_sync)]
        Arc::new(RefCell::new(buffer))
    };

    let write_complete = move |bytes_written: i32| {
     turso_assert!(
            bytes_written == WAL_HEADER_SIZE as i32,
            "wal header wrote({bytes_written}) != expected({WAL_HEADER_SIZE})"
        );
    };
// buffer is never referenced again, this works for sync IO but io_uring writes junk bytes
```
<img width="881" height="134" alt="image" src="https://github.com/user-
attachments/assets/0ff06ad5-411a-43d2-abac-caf9e23ceaeb" />

Closes #2297
2025-07-28 08:47:12 +03:00
Pekka Enberg
04204a7f85 Merge 'Thread-safe WindowsFile' from Levy A.
Prevents `BorrowMut` errors seen in #2217.

Closes #2295
2025-07-28 08:46:30 +03:00
Pekka Enberg
9c66363623 Merge 'bindings/javascript: Fix tracing and SqliteError message not populated' from Levy A.
Useful for debugging.
Also fixes  `SqliteError`'s `message` not being populated, seen in #2217
CI runs.

Closes #2296
2025-07-28 08:43:26 +03:00
PThorpe92
b08c465450 Fix writing wal header for async IO 2025-07-27 21:52:13 -04:00
Levy A.
cbb618f0ad feat(javascript): add tracing_subscriber 2025-07-27 21:24:39 -03:00
Levy A.
dda5fc22ee refactor(javascript): simplify SqliteError 2025-07-27 21:24:39 -03:00
Levy A.
1f57ab02cf feat: instrument WindowsIO functions 2025-07-27 20:39:49 -03:00
Levy A.
c95c6b67ee fix: thread-safe WindowsFile 2025-07-27 20:39:49 -03:00
Kristofer Lund
cbd5a26cf7 Adding Expr::Id as an allowed Expr when
creating an index.
2025-07-27 22:54:20 +02:00
Pekka Enberg
a2a31a520f Remove CreateTable from COMPAT.md
There is no such opcode.
2025-07-27 09:12:31 +03:00
Pekka Enberg
6bf6cc28e4 Merge 'Implement the Returning statement for inserts and updates' from Glauber Costa
They are very similar. DELETE is very different, so that one we'll do it
later.

Closes #2276
2025-07-27 09:11:16 +03:00
Pekka Enberg
86c97fca6d Merge 'Fix sum() to follow the SQLite semantics' from FamHaggs
### Follow SUM [spec](https://sqlite.org/lang_aggfunc.html)
This PR updates the `SUM` aggregation logic to follow the
[Kahan–Babushka–Neumaier summation
algorithm](https://en.wikipedia.org/wiki/Kahan_summation_algorithm),
consistent with SQLite’s implementation. It improves the numerical
stability of floating-point summation.This fixes issue #2252 . I added a
fuzz test to ensure the compatibility of the implementations
I also fixed the return types for `SUM` to match SQLite’s documented
behavior. This was previously discussed in
[#2182](https://github.com/tursodatabase/turso/pull/2182), but part of
the logic was later unintentionally overwritten by
[#2265](https://github.com/tursodatabase/turso/pull/2265).
I introduced two helper functions, `apply_kbn_step` and
`apply_kbn_step_int`, in `vbde/execute.rs` to handle floating-point and
integer accumulation respectively. However, I’m new to this codebase and
would welcome constructive feedback on whether there’s a better place
for these helpers.

Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #2270
2025-07-27 09:08:34 +03:00
Pekka Enberg
ab39ea54c7 Merge 'Fix error handling when binding column references while translating the UPDATE statement' from Iaroslav Zeigerman
Closes #1968

Reviewed-by: bit-aloo (@Shourya742)

Closes #2273
2025-07-27 09:05:17 +03:00
Pekka Enberg
6d88c6851b Merge 'io_uring: use Arc pointer for user data of entries' from Preston Thorpe
trying to pull bite sized adjustments out of other open PR's

Closes #2281
2025-07-27 09:04:35 +03:00
Pekka Enberg
b41f337616 Merge 'Stop checkpointing the entire WAL after every write when wal frame size > threshold' from Preston Thorpe
I know I have #2179 and now #2278 open both working on checkpointing,
but I discovered during async IO benchmarks that once we hit
`should_checkpoint` (there are greater than 1000 frames in the WAL), we
will trigger checkpoint on cache flush every single write, and since we
don't update `nbackfills` in the case that everything was backfilled,
every checkpoint will backfill every single frame to the db file 💀
At least this can be reviewed+merged faster and easier.
This also fixes the return value of `pragma wal_checkpoint;` to match
sqlite.
Sqlite returns the # of possible frames (`shared.max_frame -
shared.nbackfills`, and then the number of frames we successfully
checkpointed `ongoing_checkpoint.max_frame -
ongoing_checkpoint.min_frame + 1`)
If `pragma wal_checkpoint;` is called again when there are no pages to
backfill, then it returns the value from the previous checkpoint.
<img width="781" height="293" alt="image" src="https://github.com/user-
attachments/assets/9aa78a6c-aa18-444d-82bd-398d684fed75" />

Closes #2280
2025-07-27 09:04:11 +03:00
PThorpe92
e6737d923d Return correct value for pragma checkpoint 2025-07-26 23:09:40 -04:00
PThorpe92
fb611390c0 Update test to use realistic expectations for should_checkpoint in cacheflush 2025-07-26 23:03:51 -04:00
PThorpe92
7c027fed8c Keep should_checkpoint logic for now until greater checkpointing is fixed 2025-07-26 23:03:51 -04:00
PThorpe92
6644036be4 Stop checkpointing after every write when wal frame size > threshold 2025-07-26 23:03:47 -04:00
PThorpe92
60c37eb036 Merge 'implement the pragma encoding' from Glauber Costa
Do not allow setting it. That ship has sailed around 2005.

Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #2282
2025-07-26 21:31:27 -04:00
PThorpe92
22df76d37d Merge 'compat police' from Glauber Costa
All those opcodes seem implemented.

Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #2284
2025-07-26 21:28:28 -04:00
Glauber Costa
421ab44cf2 compat police
All those opcodes seem implemented.
2025-07-26 20:06:21 -05:00
PThorpe92
3f2414aceb Merge 'remove upsert statement from COMPAT.md' from Glauber Costa
There's no such statement in SQLite.

Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #2283
2025-07-26 20:44:43 -04:00
Glauber Costa
f0b1abd567 remove upsert statement from COMPAT.md
There's no such statement in SQLite.
2025-07-26 19:39:32 -05:00
Glauber Costa
b8ee38868d implement the pragma encoding
Do not allow setting it. That ship has sailed around 2005.
2025-07-26 19:37:39 -05:00
PThorpe92
735026b502 Use Arc pointer for user data and save indirection when processing sqe/cqes 2025-07-26 16:35:40 -04:00
Pekka Enberg
d19bd0f24a Merge 'Update limbo -> turso in manual.md' from stano
Closes #2279
2025-07-26 18:56:34 +03:00
Glauber Costa
5d8d08d1b6 Implement the Returning statement for inserts and updates
They are very similar. DELETE is very different, so that one we'll
do it later.
2025-07-26 09:01:09 -05:00
stano
4ceea1a473 Update limbo -> turso in manual.md 2025-07-26 15:58:22 +03:00
Iaroslav Zeigerman
6f63327320 fix overlooked tests 2025-07-26 04:51:44 -07:00
Iaroslav Zeigerman
f13b9105b9 Fix error handling when binding column references while translating the UPDATE statement 2025-07-26 04:51:42 -07:00
Pekka Enberg
cc5d4dc3ba Merge 'support doubly qualified identifiers' from Glauber Costa
Closes #2271
2025-07-26 11:31:42 +03:00
Pekka Enberg
2ae5e7bd76 Merge 'mark detach as supported' from Glauber Costa
Honestly, I didn't even notice we had a compat entry for it because it
seems so trivial. But it was done as part of ATTACH.

Closes #2272
2025-07-26 11:31:07 +03:00
Glauber Costa
02e57c81f7 mark detach as supported
Honestly, I didn't even notice we had a compat entry for it because it
seems so trivial. But it was done as part of ATTACH.
2025-07-25 15:35:13 -05:00
Glauber Costa
b5927dcfd5 support doubly qualified identifiers 2025-07-25 14:52:45 -05:00
FHaggs
ef88b9914a Fix clippy warnings 2025-07-25 15:41:49 -03:00
Pekka Enberg
273c2923f0 Merge 'Fix get_column_name() when a column name doesn't exist' from meteorgan
we would encounter a panic in `display.rs` when a column name does not
exist.
```
turso> select * from (select 1);

thread 'main' panicked at core/translate/display.rs:252:50:
called `Option::unwrap()` on a `None` value
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
```
after this PR:
```
turso> select * from (select 1);
2025-07-25T15:52:24.241294Z DEBUG ThreadId(01) run_cmd:translate:optimize_plan: turso_core::translate::optimizer: 51: plan_sql="SELECT subquery_0.0 FROM (SELECT 1 FROM) AS subquery_0"
2025-07-25T15:52:24.242036Z DEBUG ThreadId(01) step:begin_read_tx:begin_read_tx: turso_core::storage::wal: 552: begin_read_tx(min_frame=1, max_frame=0, lock=0, max_frame_in_wal=0)
2025-07-25T15:52:24.242349Z DEBUG ThreadId(01) step:commit_txn:end_read_tx:end_read_tx: turso_core::storage::wal: 566: end_read_tx(lock=0)
┌───┐
│ . │
├───┤
│ 1 │
└───┘
```

Closes #2269
2025-07-25 21:38:09 +03:00
FHaggs
ab8040aa89 Add fuzz test for float sums 2025-07-25 15:26:43 -03:00
FHaggs
54edfa09d5 Replicate the sqlite Kahan-Babaska-Neumaier algorithm 2025-07-25 15:25:29 -03:00