### 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
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
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
Switch to napi [v3](https://napi.rs/blog/announce-v3).
With the exception of `Statement.iterate()`, the behavior is preserved.
I had to temporarily remove it because the trait `Generator` doesn't
supports the new lifetime scoped values, I already brought this issue in
napi's discord server and it should be fixed soon.
Closes#2262
```
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/penberg/src/tursodatabase/turso/packages/turso-serverless/dist/error' imported from /Users/penberg/src/tursodatabase/turso/packages/turso-serverless/dist/protocol.js
Error [ERR_MODULE_NOT_FOUND]: Cannot find module '/Users/penberg/src/tursodatabase/turso/packages/turso-serverless/dist/error' imported from /Users/penberg/src/tursodatabase/turso/packages/turso-serverless/dist/protocol.js
at finalizeResolution (node:internal/modules/esm/resolve:275:11)
at moduleResolve (node:internal/modules/esm/resolve:860:10)
at defaultResolve (node:internal/modules/esm/resolve:984:11)
at ModuleLoader.defaultResolve (node:internal/modules/esm/loader:780:12)
at #cachedDefaultResolve (node:internal/modules/esm/loader:704:25)
at ModuleLoader.resolve (node:internal/modules/esm/loader:687:38)
at ModuleLoader.getModuleJobForImport (node:internal/modules/esm/loader:305:38)
at ModuleJob._link (node:internal/modules/esm/module_job:137:49)
✘ integration-tests/serverless.test.mjs exited with a non-zero exit code: 1
```
`maybe_reparse_schema` function introduced in the #2246 was incorrect as
it didn't update `schema_version` for internal schema representation and
basically updated only schema for connection which called
`maybe_reparse_schema`.
This PR fixes this issue by reading schema and cookie value within a
single transaction and updating both schema content and its version for
internal representation.
Reviewed-by: Pedro Muniz (@pedrocarlo)
Closes#2259
Closes: #1947
This PR replaces the `Name(pub String)` struct with a `Name` enum that
explicitly models how the name appeared in the source either as an
unquoted identifier (`Ident`) or a quoted string (`Quoted`).
In the process, the separate `Id` wrapper type has been coalesced into
the `Name` enum, simplifying the AST and reducing duplication in
identifier handling logic.
While this increases the size of some AST nodes (notably
`yyStackEntry`).
cc: @levydsa
Reviewed-by: Levy A. (@levydsa)
Reviewed-by: Preston Thorpe (@PThorpe92)
Closes#2251
Support for attaching databases. The main difference from SQLite is that
we support an arbitrary number of attached databases, and we are not
bound to just 100ish.
We for now only support read-only databases. We open them as read-only,
but also, to keep things simple, we don't patch any of the insert
machinery to resolve foreign tables. So if an insert is tried on an
attached database, it will just fail with a "no such table" error - this
is perfect for now.
The code in core/translate/attach.rs is written by Claude, who also
played a key part in the boilerplate for stuff like the .databases
command and extending the pragma database_list, and also aided me in the
test cases.
Closes#2235