Commit Graph

6332 Commits

Author SHA1 Message Date
Glauber Costa
024d79fc0d implement write side of pragma schema_version
It is insane that SQLite even allows this.
They actually don't if "defensive mode" is enabled:

"It is always safe to read the schema_version, but changing the
schema_version can cause problems. For this reason, attempts to change
the value of schema_version are a silent no-op when defensive mode is
enabled for a database connection.

Warning: Misuse of this pragma can result in database corruption."

We also update the compat table, which was not updated to reflect
the read version of this pragma being implemented.
2025-07-19 20:39:30 -05:00
Pekka Enberg
d1fdc7dbc8 Merge 'Simplify blocking operations – add io.block(fn) for IO trait implementors' from Levy A.
This PR make it simpler to block async operations instead of creating
sync equivalent functions. eg.: `get_page_size` and
`get_page_size_async`. All functions should be async by default, and
making them block should more explicit.
Also makes it easier to grep for top-level uses of blocking code without
needing to "go to definition".

Closes #2173
2025-07-19 12:20:07 +03:00
Pekka Enberg
16958f1a44 Turso 0.1.3-pre.3 2025-07-19 08:44:22 +03:00
Levy A.
0ea7849dca feat: IOExt utility trait 2025-07-19 01:40:42 -03:00
Jussi Saurio
67f3e54782 Merge 'sorter: fix sorter panic on SortedChunkIOState::WaitingForRead' from Jussi Saurio
Closes #2165
The following sequence of events is possible:
- init_chunk_heap() called
- flush() gets called, and all chunks start writing to disk
- chunk A status is WriteComplete, so chunk.read() gets called on chunk
A
- chunk A sets its status to WaitingForRead
- some other chunk B is still in WaitingForWrite status after flush()
- for this reason, init_chunk_heap() returns IOResult::IO
- init_chunk_heap() is called again
- we panic because chunk A is in WaitingForRead status
So - we just allow WaitingForRead status in init_chunk_heap() instead.
This panic was caught thanks to Pedro's IO latency enhancement to the
sim!

Reviewed-by: Iaroslav Zeigerman (@izeigerman)

Closes #2166
2025-07-18 23:32:46 +03:00
Pekka Enberg
fd6fda07ac Merge 'bindings/rust: Initial support for transactions API' from Diego Reis
Closes #2121
There are two important things to point out:
1. The support is incomplete since we yet don't support savepoints in
core.
2. When a txn drops we should call `_finish()` on it, but since async
drop is [unstable](https://github.com/rust-lang/rust/issues/126482) the
best solution that I came up was just forcing the user to explicitly
call `finish()` before any drops.

Closes #2151
2025-07-18 21:02:18 +03:00
Pekka Enberg
c2a8a6f178 Merge 'improve handling of double quotes' from Glauber Costa
I ended up hitting #1974 today and wanted to fix it. I worked with
Claude to generate a more comprehensive set of queries that could fail
aside from just the insert query described in the issue. He got most of
them right - lots of cases were indeed failing. The ones that were
gibberish, he told me I was absolutely right for pointing out they were
bad.
But alas. With the test cases generated, we can work on fixing it. The
place where the assertion was hit, all we need to do there is return
true (but we assert that this is indeed a string literal, it shouldn't
be anything else at this point).
There are then just a couple of places where we need to make sure we
handle double quotes correctly. We already tested for single quotes in a
couple of places, but never for double quotes.
There is one funny corner case where you can just select "col" from tbl,
and if there is no column "col" on the table, that is treated as a
string literal. We handle that too.
Fixes #1974

Closes #2152
2025-07-18 20:55:37 +03:00
Glauber Costa
cbdd5c5fc7 improve handling of double quotes
I ended up hitting #1974 today and wanted to fix it. I worked with
Claude to generate a more comprehensive set of queries that could fail
aside from just the insert query described in the issue. He got most of
them right - lots of cases were indeed failing. The ones that were
gibberish, he told me I was absolutely right for pointing out they were
bad.

But alas. With the test cases generated, we can work on fixing it. The
place where the assertion was hit, all we need to do there is return
true (but we assert that this is indeed a string literal, it shouldn't
be anything else at this point).

There are then just a couple of places where we need to make sure we
handle double quotes correctly. We already tested for single quotes in a
couple of places, but never for double quotes.

There is one funny corner case where you can just select "col" from tbl,
and if there is no column "col" on the table, that is treated as a
string literal. We handle that too.

Fixes #1974
2025-07-18 10:39:02 -05:00
Glauber Costa
523f8f9c67 add .dbconfig option
Currently ignored. The reason we are adding it is so that we have
an output that can fit in a single line. This is so we can use it in
tests, and have a predictable output pattern for both sqlite and turso.
2025-07-18 10:25:06 -05:00
Diego Reis
b3c8255032 Fix clippy warnings 2025-07-18 11:26:10 -03:00
Diego Reis
f9d024b68a bind/rust: Use Rusqlite compatible consumer API
Methods like commit, rollback and finish should "consume" a transaction,
so it cannot be used after calling any of them (Pretty neat, rust!)
2025-07-18 11:25:24 -03:00
Diego Reis
92cddb6437 bind/rust: Add more tests for Transaction 2025-07-18 11:25:24 -03:00
Diego Reis
0b96c24196 wip: Basic support for Transaction in Rust binding 2025-07-18 11:25:24 -03:00
Jussi Saurio
2aca28a86b Merge 'claude sonnet forgot to run clippy when implementing mcp server' from Jussi Saurio
Closes #2169
2025-07-18 17:19:59 +03:00
Jussi Saurio
1f55726acf claude sonnet forgot to run clippy when implementing mcp server 2025-07-18 17:15:39 +03:00
Pekka Enberg
81d386d6c4 Merge 'bindings/js: support iterator, and more kinds of params' from Mikaël Francoeur
This PR fixes 3 tests that check param binding and iteration.
-----
as part of https://github.com/tursodatabase/turso/issues/1900

Closes #2097
2025-07-18 16:25:01 +03:00
Pekka Enberg
ac157f6dcc Merge 'Add a native MCP server' from Glauber Costa
The SQLite command line has facilities to ingest things like csv, and
other formats. But here we are, in 2025, and I asked Claude if Turso's
CLI should, in the same vein, have a native MCP server.
Claude told me: "You're absolutely right!" "That's a great insight!"
"That's a fantastic idea!" and then proceeded to help me with the
boilerplate for this beautiful server.
Rust has a crate, mcp_server, that implements an mcp_server trait.
However, that depends on Tokio, and I think that would bloat our binary
too much.
I have also considered implementing an MCP server that operates on a
directory and allows to list many SQLite files, but figured that would
be a good job for a more advanced and specialized server, not for the
one that comes by default with the CLI. Let's go for simple.

Closes #2148
2025-07-18 16:05:57 +03:00
Jussi Saurio
4d15725b6f sorter: fix sorter panic on SortedChunkIOState::WaitingForRead
The following sequence of events is possible:

- init_chunk_heap() called
- chunk A status is WriteComplete, so chunk.read() gets called on chunk A
- some other chunk B is in WaitingForWrite status after flush()
- init_chunk_heap() returns IOResult::IO
- init_chunk_heap() is called again
- we panic because chunk A is in WaitingForRead status

So - we just allow WaitingForRead status in init_chunk_heap() instead.

This panic was caught thanks to Pedro's IO latency enhancement to the sim!
2025-07-18 14:18:51 +03:00
Jussi Saurio
bca1e56f49 Merge 'Remove find_cell, allow overwrite of index interior cell' from Jussi Saurio
Currently we are using `find_cell` which does a binary search on every
insert, even if the cursor is already in the correct place.
1. Remove `find_cell` and use `seek` when inserting instead of
`move_to`.
2. After removing `find_cell`, we now need to be more mindful of when we
do actually require a seek before insertion. This is signified by the
addition of `InsertFlags::REQUIRE_SEEK`. Previously some inserts worked
by accident because we always happened to be on the correct page --
`find_cell()´ only binary searches a single page.
3. After removing `find_cell` we also need to call `next()` after
`Insn::NewRowid`, because `NewRowid` positions us at the old maximum
rowid, and we need to be positioned after it.
4. After removing `find_cell` we also need to leave the cursor
positioned correctly during seeks where we don't find a match.
5. Allow overwrite of an index interior cell, which Closes #1975.

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

Closes #1988
2025-07-18 14:09:39 +03:00
Jussi Saurio
40df1725c5 Fix restore_context() not advancing when required 2025-07-18 13:48:23 +03:00
Jussi Saurio
2a2ab16c52 fix moved_before handling in cursor.insert 2025-07-18 13:48:23 +03:00
Jussi Saurio
28c050dd27 seek before insert to ensure correct location in fuzz test 2025-07-18 13:48:23 +03:00
Jussi Saurio
fdeb15bb9d btree/delete: rightmost_cell_was_dropped logic is not needed since a) if we balance, we seek anyway, and b) if we dont balance, we retreat anyway 2025-07-18 13:48:23 +03:00
Jussi Saurio
55151a8061 Fix cases where Insn::Insert needs to seek to ensure correct insertion 2025-07-18 13:48:23 +03:00
Jussi Saurio
9ee6988fc5 VDBE: NewRowid needs to call next() in case op_not_exists() is not called afterwards 2025-07-18 13:48:23 +03:00
Jussi Saurio
4f0ef663e2 btree: add target cell tracking for EQ seeks 2025-07-18 13:48:23 +03:00
Jussi Saurio
2b23495943 btree: allow overwriting index interior cell 2025-07-18 13:48:23 +03:00
Jussi Saurio
e33ff667dc btree: use seek() when inserting -- replaces find_cell() 2025-07-18 13:48:23 +03:00
Jussi Saurio
9201030e67 Ephemeral UNION indexes don't need to be UNIQUE 2025-07-18 13:48:23 +03:00
Jussi Saurio
f7068ce23a Merge 'Fix parent page stack location after interior node replacement' from Jussi Saurio
Another fix extracted from running simulations on the #1988 branch.
## What
When interior cell replacement happens as described in #2108, we use the
`cursor.prev()` method to locate the largest key in the left subtree.
There was an error during backwards traversal in the `get_prev_record()`
method where the parent's cell index was set as `i32::MAX` but not
properly set to `cell_count + 1` (indicating that rightmost pointer has
been visited).
This meant that if the child page we took the replacement cell from
underflowed and needed balancing, the parent page would now have
`i32::MAX` as its cell index and crash at the point where we determine
based on the parent page which page is going to undergo balancing.
## Fix
This PR fixes the issue by setting the cell index of the parent properly
when visiting the rightmost child. This way the balance procedure will
correctly detect that the rightmost child page is going to undergo
balancing.
### Trivia
The reason `i32::MAX` is used is that the cell count of the page is not
necessarily known at the time it is pushed to the stack.

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

Closes #2163
2025-07-18 13:48:06 +03:00
Jussi Saurio
aeab89bd75 Fix parent page stack location after interior node replacement
Another fix extracted from running simulations on the #1988 branch.

When interior cell replacement happens as described in #2108,
we use the `cursor.prev()` method to locate the largest key in the
left subtree.

There was an error during backwards traversal in the `get_prev_record()`
method where the parent's cell index was set as `i32::MAX` but not properly
set to `cell_count + 1` (indicating that rightmost pointer has been visited).

The reason `i32::MAX` is used is that the cell count of the page is not
necessarily known at the time it is pushed to the stack.

This PR fixes the issue by setting the cell index of the parent properly
when visiting the rightmost child.
2025-07-18 13:30:01 +03:00
Jussi Saurio
b1ebc1c82f Merge 'sim: show seed in 'execute_interaction()' trace' from Jussi Saurio
This helps e.g. when you hit some infinite loop

Closes #2161
2025-07-18 12:27:48 +03:00
Jussi Saurio
086a269a65 Merge 'sim: change --disable-create-index flag to --enable-create-index (default false)' from Jussi Saurio
Closes #2160
2025-07-18 12:27:35 +03:00
Jussi Saurio
0af2443061 Merge 'sim: add order by to some queries' from Jussi Saurio
Closes #2159
2025-07-18 12:27:30 +03:00
Pekka Enberg
02f4bc39b3 Merge 'Reanimate MVCC' from Pekka Enberg
Bit-rot happened. Bring MVCC back from the dead.

Closes #2136
2025-07-18 11:22:49 +03:00
Jussi Saurio
687d126e25 sim: change --disable-create-index flag to --enable-create-index (default false) 2025-07-18 11:21:24 +03:00
Jussi Saurio
5b2d7315eb sim: show seed in 'execute_interaction()' trace 2025-07-18 11:21:04 +03:00
Jussi Saurio
9722aab8a1 clappy 2025-07-18 11:01:57 +03:00
Jussi Saurio
347a9152a6 Merge 'Replace verbose IO Completion methods with helpers' from Preston Thorpe
one of the last remnants of some original verbosity

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

Closes #2156
2025-07-18 10:52:17 +03:00
Jussi Saurio
bbd7f32d80 Merge 'Fix rollback for TxErrors' from Diego Reis
Fixes #2153.
Not so sure if SQLite doesn't rollback in more cases, we should
definitively check this out.

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

Closes #2154
2025-07-18 10:49:29 +03:00
Jussi Saurio
7e1755ac6f Merge 'Use pread and pwrite in run_once' from Ihor Andrianov
pread and pwrite is usually less instructions then seek and read. Also
added possibility for io to retry if AGAIN error happens. And made write
to wait for Event::writable

Reviewed-by: Preston Thorpe (@PThorpe92)
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2010
2025-07-18 10:48:26 +03:00
Jussi Saurio
d67a9f03fd sim: add order by to some queries 2025-07-18 10:47:36 +03:00
Jussi Saurio
2be9ead3f7 Merge 'Core: Introduce external sorting' from Iaroslav Zeigerman
Closes #91
Replaces #1771 since the previous PR went stale and had too many
conflicts.
The maximum size of the in-memory buffer is determined by the settings
that configure the page-cache size.
In scope:
- Spill the in-memory buffer to a temporary file on disk if its size
exceeds the threshold
- When iterating over the sorter, the next record is picked using heap
populated from sorted chunk files
- Flushing the in-memory buffer and reading from chunk files occur
asynchronously.

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

Closes #2128
2025-07-18 10:44:56 +03:00
Iaroslav Zeigerman
28ff170e14 improve sorter settings in the fuzz test 2025-07-18 07:41:15 +02:00
Iaroslav Zeigerman
76e748146b rebase 2025-07-18 07:30:08 +02:00
Iaroslav Zeigerman
f6f1d076da verify that records remain unchanged after sorting 2025-07-18 07:28:37 +02:00
Iaroslav Zeigerman
d9751212d7 make a fuzz sorter test 2025-07-18 07:28:37 +02:00
Iaroslav Zeigerman
20bdbd5ca5 address suggestions 2025-07-18 07:28:37 +02:00
Iaroslav Zeigerman
edf2be1432 fix conflicts 2025-07-18 07:28:37 +02:00
Iaroslav Zeigerman
6a609398fe cosmetic fix 2025-07-18 07:28:37 +02:00