Commit Graph

304 Commits

Author SHA1 Message Date
Jussi Saurio
22a8992b6b mvcc: un-ignore mvcc fuzz test 2025-09-19 09:18:20 +03:00
Jussi Saurio
7410200a9f mvcc: add higher weight for BEGIN CONCURRENT in fuzz test 2025-09-19 09:18:20 +03:00
Nikita Sivukhin
c1176356f7 small fixes 2025-09-17 19:20:42 +04:00
Nikita Sivukhin
f1764d9f76 fix test 2025-09-17 19:09:55 +04:00
Jussi Saurio
4cda0d5d99 mvcc fuzz: fix tx startpoint as BEGIN CONCURRENT can fail 2025-09-17 11:43:20 +03:00
rajajisai
e605aff31b Merge branch 'main' into enc-page-1 2025-09-16 10:06:00 -04:00
rajajisai
89caa868f9 Encryption support for database header page 2025-09-16 10:04:30 -04:00
Jussi Saurio
ea6373b8ae Switch to BTreeMap for deterministic iteration 2025-09-16 12:56:17 +03:00
Jussi Saurio
61764bf415 clippy 2025-09-15 11:37:17 +03:00
Jussi Saurio
1fa57b2dec add test demonstrating that issue 3085 can be closed 2025-09-15 11:36:19 +03:00
Jussi Saurio
88856de48e fmt 2025-09-15 11:33:15 +03:00
Jussi Saurio
f2dbf1eeb0 add test demonstrating that issue 3084 can be closed 2025-09-15 11:32:39 +03:00
Jussi Saurio
d643bb2092 add test that demonstrates issue 3083 can be closed 2025-09-15 11:30:56 +03:00
Jussi Saurio
59f18e2dc8 fix mvcc update
simple reason why mvcc update didn't work: it didn't try to update.
2025-09-15 11:27:56 +03:00
Jussi Saurio
f2079d8f07 test/fuzz: improve error handling in tx isolation fuzz test
- extract out common behavior for checking acceptable errors
- add functionality to check which errors require rolling back
  a transaction
2025-09-15 08:03:08 +03:00
Jussi Saurio
1c5febf047 test/fuzz: introduce fuzzoptions to tx isolation test
this makes it significantly easier to tweak the tx isolation test parameters,
and also makes it much easier to run the MVCC version of the test without
manually tweaking code inline to make it work.

introduces default options for the non-mvcc and mvcc test variants.
2025-09-15 07:44:16 +03:00
Avinash Sajjanshetty
5256f29a9c Add checksums behind a feature flag 2025-09-13 11:00:39 +05:30
Avinash Sajjanshetty
06a824ec68 Add checksum tests 2025-09-13 11:00:39 +05:30
Avinash Sajjanshetty
1536f65f07 move test helper run_query to common module 2025-09-13 11:00:39 +05:30
Pekka Enberg
1803d0bb5d test: Enable some MVCC test cases
Suggested by Jussi
2025-09-12 23:11:45 +03:00
Jussi Saurio
305b2f55ae MVCC: remove reliance on BTreeCursor::has_record() 2025-09-12 16:03:55 +03:00
Pekka Enberg
6a992e551c Merge 'core: Fix reprepare to properly reset statement cursors and registers' from Pedro Muniz
Before we were not updating the number of registers and cursors, which
meant that on a schema change the Program could now open an additional
cursor and we would not have space for it in the ProgramState, which
lead to the panic.
Closes #3002

Closes #3034
2025-09-12 12:29:53 +03:00
Pekka Enberg
06371d8894 Merge 'Add BEGIN CONCURRENT support for MVCC mode' from Pekka Enberg
Currently, when MVCC is enabled, every transaction mode supports
concurrent reads and writes, which makes it hard to adopt for existing
applications that use `BEGIN DEFERRED` or `BEGIN IMMEDIATE`.
Therefore, add support for `BEGIN CONCURRENT` transactions when MVCC is
enabled. The transaction mode allows multiple concurrent read/write
transactions that don't block each other, with conflicts resolved at
commit time. Furthermore, implement the correct semantics for `BEGIN
DEFERRED` and `BEGIN IMMEDIATE` by taking advantage of the pager level
write lock when transaction upgrades to write. This means that now
concurrent MVCC transactions are serialized against the legacy ones when
needed.
The implementation includes:
- Parser support for CONCURRENT keyword in BEGIN statements
- New Concurrent variant in TransactionMode to distinguish from regular
read/write transactions
- MVCC store tracking of exclusive transactions to support IMMEDIATE and
EXCLUSIVE modes alongside CONCURRENT
- Proper transaction state management for all transaction types in MVCC
This enables better concurrency for applications that can handle
optimistic concurrency control, while still supporting traditional
SQLite transaction semantics via IMMEDIATE and EXCLUSIVE modes.

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

Closes #3021
2025-09-12 07:38:53 +03:00
Preston Thorpe
e9944f5d1f Merge 'Fix automatic indexes' from Jussi Saurio
Closes #2993
## Background
When a `CREATE TABLE` statement specifies constraints like `col UNIQUE`,
`col PRIMARY KEY`, `UNIQUE (col1, col2)`, `PRIMARY KEY(col3, col4)`,
SQLite creates indexes for these constraints automatically with the
naming scheme `sqlite_autoindex_<table_name>_<increasing_number>`.
## Problem
SQLite expects these indexes to be created in table definition order.
For example:
```sql
CREATE TABLE t(x UNIQUE, y PRIMARY KEY, c, d, UNIQUE(c,d));
```
Should result in:
```sql
sqlite_autoindex_t_1 -- x UNIQUE
sqlite_autoindex_t_2 -- y PRIMARY KEY
sqlite_autoindex_t_3-- UNIQUE(c,d)
```
However, `tursodb` currently doesn't uphold this invariant -- for
example: the PRIMARY KEY index is always constructed first. SQLite flags
this as a corruption error (see #2993).
## Solution
- Process "unique sets" in table definition order. "Unique sets" are
groups of 1-n columns that are part of either a UNIQUE or a PRIMARY KEY
constraint.
- Deduplicate unique sets properly: a PRIMARY KEY of a rowid alias
(INTEGER PRIMARY KEY) is not a unique set. `UNIQUE (a desc, b)` and
`PRIMARY KEY(a, b)` are a single unique set, not two.
- Unify logic for creating automatic indexes and parsing them - remove
separate logic in `check_automatic_pk_index_required()` and use the
existing `create_table()` utility in both index creation and
deserialization.
- Deserialize a single automatic index per unique set, and assert that
`unique_sets.len() == autoindexes.len()`.
- Verify consistent behavior by adding a fuzz tests that creates 1000
databases with 1 table each and runs `PRAGMA integrity_check` on all of
them with SQLite.
## Trivia
Apart from fixing the exact issue #2993, this PR also fixes other bugs
related to autoindex construction - namely cases where too many indexes
were created due to improper deduplication of unique sets.

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

Closes #3018
2025-09-11 17:04:53 -04:00
pedrocarlo
c04cf535b0 flip is_done to is_busy 2025-09-11 14:58:51 -03:00
pedrocarlo
6264d694d5 on reprepare create new state with updated number of cursors and
registers, so that the Program insns are in sync with ProgramState
2025-09-11 12:50:22 -03:00
Jussi Saurio
6a7bead482 Fix tx isolation test semantics after #3023
The test now incorrectly takes a shadow snapshot of the DB state
before it is determined that the connection successfully started
a read transaction.

Fix: take the snapshot after we've verified that the read TX started.
2025-09-11 16:44:28 +03:00
Pekka Enberg
433b60555f Add BEGIN CONCURRENT support for MVCC mode
Currently, when MVCC is enabled, every transaction mode supports
concurrent reads and writes, which makes it hard to adopt for existing
applications that use `BEGIN DEFERRED` or `BEGIN IMMEDIATE`.

Therefore, add support for `BEGIN CONCURRENT` transactions when MVCC is
enabled. The transaction mode allows multiple concurrent read/write
transactions that don't block each other, with conflicts resolved at
commit time. Furthermore, implement the correct semantics for `BEGIN
DEFERRED` and `BEGIN IMMEDIATE` by taking advantage of the pager level
write lock when transaction upgrades to write. This means that now
concurrent MVCC transactions are serialized against the legacy ones when
needed.

The implementation includes:

- Parser support for CONCURRENT keyword in BEGIN statements

- New Concurrent variant in TransactionMode to distinguish from regular
  read/write transactions

- MVCC store tracking of exclusive transactions to support IMMEDIATE and
  EXCLUSIVE modes alongside CONCURRENT

- Proper transaction state management for all transaction types in MVCC

This enables better concurrency for applications that can handle
optimistic concurrency control, while still supporting traditional
SQLite transaction semantics via IMMEDIATE and EXCLUSIVE modes.
2025-09-11 16:05:52 +03:00
Jussi Saurio
c30d320cab Fix: read transaction cannot be allowed to start with a stale max frame
If both of the following are true:

1. All read locks are already held
2. The highest readmark of any read lock is less than the committed max frame

Then we must return Busy to the reader, because otherwise they would begin a
transaction with a stale local max frame, and thus not see some committed
changes.
2025-09-11 15:58:13 +03:00
Jussi Saurio
b9e2879f74 Add fuzz test for CREATE TABLE
This fuzz test verifies that various CREATE TABLE definitions with
UNIQUE and PRIMARY KEY definitions pass sqlite integrity_check.
2025-09-11 14:11:30 +03:00
Jussi Saurio
5f410fd568 Add missing maybe_update_schema() calls
Connection::query() was not properly checking whether it needs to
refresh its schema.
2025-09-10 22:44:26 +03:00
Jussi Saurio
d7ce781a2a Merge 'Enable the use of indexes in DELETE statements' from Jussi Saurio
Closes #1714
This PR enables the use of an index as the iteration cursor for a point
or range deletion operation. Main changes:
- Use `Delete` opcode for the index that is iterating the rows - avoids
unnecessary seeking on that index, since it's already positioned
correctly
- Fix delete balancing; details below:
### current state
- a deletion may cause a btree rebalancing operation
- to get the cursor back to the right place after a rebalancing, we must
remember what the deleted key was and seek to it
- right now we are using `SeekOp::LT` to move to one slot BEFORE the
deleted key, so that if we delete rows in a loop, the following `Next()`
call will put us back into the right place
### problem
- When we delete multiple rows, we always iterate forwards. Using
`SeekOp::LT` implies backwards iteration, but it works OK for table
btrees since the cursor never remains on an internal node, because table
internal cells do not have payloads. However: this behavior is
problematic for indexes because we can effectively end up skipping
visiting a page entirely. Honestly: despite spending some debugging the
_old_ code, I still don't remember what exactly causes this to happen.
:) It's one of the `iter_dir` specific behaviors in `indexbtree_move_to`
or `get_prev_record()`, but I'm too tired to spend more time figuring it
out. I had the reason in my head before going on vacation, but it was
evicted from the cache it seems...
### solution
use `SeekOp::GE { eq_only: true }` instead and make the next call to
`Next()` a no-op instead. This has the same effect as SeekOp::LT +
next(), but without introducing bugs due to `LT` being implied backwards
iteration.

Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #2981
2025-09-10 16:00:54 +03:00
Jussi Saurio
4eb61a9527 Add gaps to update/delete fuzz test where clauses 2025-09-10 15:38:57 +03:00
Jussi Saurio
53eaf56a63 let's apply clippy's suggestion that makes the code less readable 2025-09-10 14:54:51 +03:00
Jussi Saurio
813bdc088b Adjust fuzz test to do a WHERE-less update or delete sometimes 2025-09-10 14:54:51 +03:00
Pekka Enberg
2131a04b7d core: Rename IO::run_once() to IO::step()
The `run_once()` name is just a historical accident. Furthermore, it now
started to appear elsewhere as well, so let's just call it IO::step() as we
should have from the beginning.
2025-09-10 14:36:02 +03:00
Jussi Saurio
a874530db8 Fix semantics of transaction isolation test
- Busy errors do not rollback the transaction
- Transaction takes snapshot of DB state after its first successful
  access of the DB, not before its first attempt to access the DB
2025-09-10 11:13:50 +03:00
Pekka Enberg
8161badbf4 core/vdbe: Don't rollback transaction when write upgrade fails
If upgrade from read to write transaction fails, don't roll back the
transaction. Instead restore the transaction into its original state,
which allows deferred transactions that have not read anything to
restart automatically.

Fixes #2984
2025-09-10 10:51:52 +03:00
Jussi Saurio
7fe494e888 test/fuzz: add UPDATE/DELETE fuzz test
Our current UPDATE/DELETE fuzz tests are coupled to the btree and do
not exercise the VDBE code paths at all, so a separate one makes sense.

This test repeats the following:

- Creates one table with n columns
- Creates (0..=n) indexes
- Executes UPDATE/DELETE statements
- Asserts that both sqlite and tursodb have the same DB state after each stmt
2025-09-09 13:18:57 +03:00
Pekka Enberg
86baa06600 tests/integration: Add affinity differential fuzz test 2025-09-08 18:59:20 +03:00
Pekka Enberg
7f002548fd tests/integration: Enable min_max_agg_fuzz() test case 2025-09-08 18:49:13 +03:00
Nikita Sivukhin
47808e9da8 enable tracing subscriber in integration tests 2025-09-07 19:56:06 +04:00
Pekka Enberg
12cf4d2e72 core: Make strict schema support experimental
It's not tested properly so let's mark it as experimental for now.

Fixes #2775
2025-09-02 16:40:02 +03:00
Pekka Enberg
d959319b42 Merge 'Use u64 for file offsets in I/O and calculate such offsets in u64' from Preston Thorpe
Using `usize` to compute file offsets caps us at ~16GB on 32-bit
systems. For example, with 4 KiB pages we can only address up to 1048576
pages; attempting the next page overflows a 32-bit usize and can wrap
the write offset, corrupting data. Switching our I/O APIs and offset
math to u64 avoids this overflow on 32-bit targets

Closes #2791
2025-09-02 09:06:49 +03:00
Pekka Enberg
9d06e0bf8e Merge 'Support encryption for non-4k page size' from
Closes #2734.

Reviewed-by: Avinash Sajjanshetty (@avinassh)

Closes #2860
2025-09-01 10:05:52 +03:00
rajajisai
975a83c719 Include test 2025-08-30 16:13:44 -04:00
William Souza
0a6c3872a7 add test for encryption URI options 2025-08-30 15:56:43 -03:00
PThorpe92
a0e5536360 Fix clippy warnings and remove self casts 2025-08-28 09:45:19 -04:00
Pekka Enberg
8f11311473 Merge 'Improve encryption API' from Avinash Sajjanshetty
This patch brings a bunch of quality of life improvements to encryption:
1. Previously, we just let any string to be used as a key. I have
updated the `PRAGMA hexkey=''` to get the key in hex. I have also
renamed from `key`, because that will be used to get passphrase
2. Added `PRAGMA cipher` so that now users can select which cipher they
want to use (for now, either `aegis256` or `aes256gcm`)
3. We now set the encryption context when both cipher and key are set
I also updated tests to reflect this.

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

Closes #2779
2025-08-26 08:32:29 +03:00
Avinash Sajjanshetty
cf7418663c update encryption tests to work with diff ciphers 2025-08-25 16:40:09 +05:30