Commit Graph

8505 Commits

Author SHA1 Message Date
Jussi Saurio
457f1980ca Merge 'Small fixes' from Nikita Sivukhin
Attempt to improve performance of `SELECT COUNT(*) FROM t WHERE 1 = 1`
(redundant filter added to force sqlite and tursodb to iterate over rows
instead of applying count optimization).
Before that PR turso with hot cache runs for ~440ms.
After that PR turso with hot cache runs for ~200ms.
This PR eliminates small things on a hot path and also introduce
separate flow for `get_next_record` which just increment cell idx if its
safe to do that - without much additional overhead.
Also, this PR eliminates RefCell/Cell from PageStack because there were
completely unnecessary.

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

Closes #2887
2025-09-08 09:45:35 +03:00
Nikita Sivukhin
87d49cd039 cargo fmt after rebase 2025-09-07 20:08:10 +04:00
Nikita Sivukhin
cd627c2368 remove unnecessary changes 2025-09-07 19:56:06 +04:00
Nikita Sivukhin
47808e9da8 enable tracing subscriber in integration tests 2025-09-07 19:56:06 +04:00
Nikita Sivukhin
5b9fe0cdf3 fix 2025-09-07 19:56:06 +04:00
Nikita Sivukhin
0b6a6e7713 remove comma 2025-09-07 19:56:06 +04:00
Nikita Sivukhin
9aed831f2f format 2025-09-07 19:56:05 +04:00
Nikita Sivukhin
db7c6b3370 try to speed up count(*) where 1 = 1 2025-09-07 19:55:42 +04:00
Nikita Sivukhin
c374cf0c93 remove Cell/RefCell from PageStack 2025-09-07 19:54:50 +04:00
Preston Thorpe
b7c43cf293 Merge 'Add json_each table-valued function (1-arg only)' from Mikaël Francoeur
This adds the [`json_each`](https://sqlite.org/json1.html#the_json_each_
and_json_tree_table_valued_functions) TVF. Only the 1-arg version is
supported for now.
As suggested in the comments on this PR, I've also extended the virtual
table system to support internal TVF's, as opposed to extensions and
pragma TVF's.

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

Closes #2691
2025-09-06 19:46:20 -04:00
Pekka Enberg
9c24b8d088 Merge 'Remove RefCell from Cursor' from Pedro Muniz
Closes #2944
2025-09-06 15:03:23 +03:00
pedrocarlo
e6344db5b1 remove Refcell from Cursor 2025-09-06 01:46:21 -03:00
Preston Thorpe
ec40ee6d3f Merge 'Page Cache: optimize and use sieve/Gclock hybird algorithm in place of LRU' from Preston Thorpe
This PR replaces the existing LRU cache implementation with the sieve
algo, providing better performance and simpler code.
Same intrusive linked-list without pointer chasing and no per-
page/insert heap allocations.
Benchmarks look really good so far, although some did show some
regression (I think it's more likely that they were flakey and its not
representative on either end, but I do believe this to be considerably
more performant)
Best numbers I have seen yet on Mobibench locally, with 1473 tx/s.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #2885
2025-09-05 23:14:32 -04:00
PThorpe92
01d64977d7 Use more efficient circular list and rely on clock hand for pagecache 2025-09-05 22:40:27 -04:00
PThorpe92
644d0f270b Add evict slot method in page cache 2025-09-05 16:13:30 -04:00
PThorpe92
b89513f031 remove useless saturating sub 2025-09-05 16:13:30 -04:00
PThorpe92
39a47d67e6 Apply PR suggestions 2025-09-05 16:13:29 -04:00
PThorpe92
f45a7538fe Use true sieve/gclock algo instead of lru,dont link pages circilarly 2025-09-05 16:13:29 -04:00
PThorpe92
e418a902e5 Fix scoping issues now that refcells are gone to prevent extra destructors 2025-09-05 16:13:28 -04:00
PThorpe92
c85a61442f Remove type alias in page cache 2025-09-05 16:13:28 -04:00
PThorpe92
5ba273eea5 remove unused impl for refbit 2025-09-05 16:13:28 -04:00
PThorpe92
246b62d513 Remove unnecessary refcells, as PageCacheEntry has interior mutability 2025-09-05 16:13:27 -04:00
PThorpe92
582e25241e Implement GClock algorithm to distinguish between hot pages and scan touches 2025-09-05 16:13:27 -04:00
PThorpe92
254a0a9342 Apply fix and rename ignore_existing to upsert 2025-09-05 16:13:27 -04:00
PThorpe92
3a0b9b360a Fix clippy warnings 2025-09-05 16:13:26 -04:00
PThorpe92
03d5598cfb Use sieve algorithm in page cache in place of full LRU 2025-09-05 16:13:26 -04:00
Mikaël Francoeur
bc08548dc3 put json_each behind feature 2025-09-05 14:56:09 -04:00
Mikaël Francoeur
cf034dbabd fix rebase 2025-09-05 14:56:09 -04:00
Mikaël Francoeur
54719f4eac use new internal virtual table type 2025-09-05 14:56:05 -04:00
Mikaël Francoeur
e6d3d6ea54 1-arg json_each implementation 2025-09-05 14:47:40 -04:00
Preston Thorpe
d60628dc8e Merge 'Avoid allocating and then immediately fallbacking errors in affinity' from Jussi Saurio
On the syscall IO backend, on TPC-H query 12, the _dominating_ part of
the stack trace is trying to construct affinities from a character,
failing, allocating an error&string, and then immediately falling back
to Blob affinity and dropping the error&string.
Since I'm on vacation I won't spend cycles on figuring out why we are
passing an incorrect affinity in `flags.get_affinity()` and instead make
this lazy PR just to improve performance and stop doing silly things :]

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

Closes #2941
2025-09-05 14:26:03 -04:00
Jussi Saurio
a0613ef781 Avoid allocating and then immediately fallbacking errors in affinity
On the syscall IO backend, on TPC-H query 12, the _dominating_ part
of the stack trace is trying to construct affinities from a character,
failing, allocating an error&string, and then immediately falling back to
Blob affinity and dropping the error&string.

Since I'm on vacation I won't spend cycles on figuring out why we are passing
an incorrect affinity in `flags.get_affinity()` and instead make this lazy PR
just to improve performance and stop doing silly things :]
2025-09-05 18:34:23 +03:00
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