Commit Graph

7648 Commits

Author SHA1 Message Date
PThorpe92
285dcdd2c1 Prevent potential corruption from copying db file without holding proper locks 2025-08-14 21:31:13 -04:00
PThorpe92
3c088dda59 Update callsites of copy_to Database impl 2025-08-14 21:31:13 -04:00
PThorpe92
55f09a01c4 Update copy_to method in file trait to separate source and destination IO 2025-08-14 21:31:13 -04:00
Preston Thorpe
5cea0f572e Merge 'Revive async io extension PR' from Preston Thorpe
bringing #1127 back to life, except better because this doesn't add
Tokio as a dependency for extension lib just for tests.

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

Closes #2418
2025-08-14 16:10:09 -04:00
Preston Thorpe
64c83237b9 apply copilot review doc comment
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-14 16:09:25 -04:00
Preston Thorpe
e9d23e7fea apply copilot suggestion
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
2025-08-14 16:08:55 -04:00
Preston Thorpe
c9888375fd Merge 'sim: reduce frequency of compound selects and complex joins' from Jussi Saurio
we spend a shitload of time doing very complex selects in the simulator,
and i'm not sure there has been a lot of gain from them. using up a lot
of the runtime of a simulator run on these can mask other issues.

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

Closes #2605
2025-08-14 16:03:55 -04:00
Preston Thorpe
a425c72ddc Merge 'Add assertion for expected write amount in writev callback' from Preston Thorpe
as @avinassh pointed out, if we have an error in a `writev` call, it
could go unnoticed and the checkpoint could proceed as expected after
not writing the expected total amt.

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

Closes #2601
2025-08-14 14:05:08 -04:00
Jussi Saurio
165f2aed23 make big joins even less likely 2025-08-14 19:35:16 +03:00
Jussi Saurio
f5438d14de make big compound selects even less likely 2025-08-14 19:35:16 +03:00
Jussi Saurio
aed48bb665 sim: reduce frequency of compound selects and complex joins 2025-08-14 19:35:16 +03:00
Jussi Saurio
e85af59eaf Merge 'sim: add Property::ReadYourUpdatesBack' from Jussi Saurio
## Problem
We have been running the simulator for over a month without ever doing a
check like "did the rows we updated actually get updated" (#2602 ),
because the only properties that check those are - for whatever reason -
`FsyncNoWait` and `FaultyQuery`, and both of those have been disabled
for a long time.
Before we enable `FaultyQuery` again, let's make sure we don't have any
active bugs on `main` that don't depend on IO faults to happen. One of
these was an index-related corruption bug #2599 - already fixed - which
would've been caught by this test (or any similar assertion that just
checks what we updated in the DB, but we didn't have any)
## Solution
Add `Property::ReadYourUpdatesBack`, which essentially just does the
following:
```
UPDATE foo SET <arbitrary columns = arbitrary values> WHERE <arbitrary condition>
-- followed by
SELECT <the same columns> WHERE <the same condition>
```
And asserts that the values are the ones we just set

Reviewed-by: Pedro Muniz (@pedrocarlo)

Closes #2604
2025-08-14 19:18:09 +03:00
Jussi Saurio
1a11648974 sim: add Property::ReadYourUpdatesBack 2025-08-14 19:04:27 +03:00
Jussi Saurio
0b9d07db4c sim: restrict Update::arbitrary_from() to SET each column max once 2025-08-14 18:55:34 +03:00
PThorpe92
5a7c9325e2 Add assertion for expected write amount in writev callback 2025-08-14 10:53:16 -04:00
Jussi Saurio
649fcf9603 Merge 'Fix UPDATE: Do not use an index for iteration if that index is going to be updated' from Jussi Saurio
Closes #2598
Forbids using an index as the iteration cursor for an `UPDATE` statement
unless either of these is true:
1. The index is not affected by the `UPDATE` statement
2. The search condition on the index is guaranteed to return a maximum
of 1 row, so no iteration will happen
### Note
I have created a follow-up issue about handling this a bit better:
https://github.com/tursodatabase/turso/issues/2600

Closes #2599
2025-08-14 17:21:14 +03:00
Jussi Saurio
5da76c9125 Allow index in UPDATE for point queries (i.e. max 1 row affected) 2025-08-14 15:58:01 +03:00
Jussi Saurio
cd3b4bccd3 Fix UPDATE: Do not use an index for iteration if that index is going to be updated
Closes #2598
2025-08-14 15:35:00 +03:00
Jussi Saurio
f9bd047e4d Merge 'Fix non-4096 page sizes' from Jussi Saurio
Closes #2555
## Problem
The main problem we had with the current implementation of
`init_pager()` was that the WAL header was eagerly allocated and written
to disk with a page size, and a potential already-set page size on an
initialized database was not checked. Given this scenario:
- Initialized database with e.g. page size 512 but no WAL
- Tursodb connects to DB
It would not check the database file for the page size and instead would
initialize the WAL header with the default 4096 page size, plus
initialize the `BufferPool` similarly with the wrong size, and then
panic when reading pages from the DB, expecting to read `4096` instead
of `512`, as demonstrated in the reproduction of #2555.
## Fix
1. Add `Database::read_page_size_from_db_header()` method that can be
used in the above cases
2. Initialize the WAL header lazily during the first frame append, using
the existing `WalFile::ensure_header_if_needed()` method, removing the
need to eagerly pass `page_size` when constructing the in-memory
`WalFileShared` structure.
## Reader notes
This PR follows a fairly logical commit-by-commit structure so you'll
preferably want to read it that way.

Reviewed-by: Nikita Sivukhin (@sivukhin)

Closes #2569
2025-08-14 13:02:33 +03:00
Jussi Saurio
bd8c6f3c7c make PageSize more robust: only accept literal '1' value if it comes directly from db header 2025-08-14 12:40:58 +03:00
Jussi Saurio
c2e89f94f8 Change more page size panics to corrupt errors 2025-08-14 12:40:58 +03:00
Jussi Saurio
0c6d548402 integration test tweak 2025-08-14 12:40:58 +03:00
Jussi Saurio
f94fa2bbbe salt tweak 2025-08-14 12:40:58 +03:00
Jussi Saurio
38bb0719cc read from disk tweak 2025-08-14 12:40:58 +03:00
Jussi Saurio
8a1c3390e6 Add integration test for page_size=512 2025-08-14 12:40:58 +03:00
Jussi Saurio
a2a88e2c69 Make exception for page size literal value 1 2025-08-14 12:40:58 +03:00
Jussi Saurio
c75e4c1092 Fix non-4096 page sizes by making WAL header lazy 2025-08-14 12:40:58 +03:00
Jussi Saurio
f8620a9869 Use non-hardcoded size for BTreeCursor immutablerecord 2025-08-14 12:40:58 +03:00
Jussi Saurio
f5e27f23ad Use type-safe PageSize newtype for connection.page_size 2025-08-14 12:40:58 +03:00
Jussi Saurio
bb21bd93da Use type-safe PageSize newtype for pager.page_size 2025-08-14 12:40:58 +03:00
Jussi Saurio
ee58b7bd86 Add fn read_header() to DatabaseStorage trait 2025-08-14 12:40:58 +03:00
Jussi Saurio
a2a6feb193 Merge 'Use BufferPool owned by Database instead of a static global' from Jussi Saurio
## Problem
There are several problems with our current statically allocated
`BufferPool`.
1. You cannot open two databases in the same process with different page
sizes, because the `BufferPool`'s `Arena`s will be locked forever into
the page size of the first database. This is the case regardless of
whether the two `Database`s are open at the same time, or if the first
is closed before the second is opened.
2. It is impossible to even write Rust tests for different page sizes
because of this, assuming the test uses a single process.
## Solution
Make `Database` own `BufferPool` instead of it being statically
allocated, so this problem goes away.
Note that I didn't touch the still statically-allocated
`TEMP_BUFFER_CACHE`, because it should continue to work regardless of
this change. It should only be a problem if the user has two or more
databases with different page sizes open simultaneously, because
`TEMP_BUFFER_CACHE` will only support one pool of a given page size at a
time, so the rest of the allocations will go through the global
allocator instead.
## Notes
I extracted this change out from #2569, because I didn't want it to be
smuggled in without being reviewed as an individual piece.

Reviewed-by: Avinash Sajjanshetty (@avinassh)

Closes #2596
2025-08-14 12:40:32 +03:00
Jussi Saurio
6a33d4e792 Merge 'sync-engine: avoid unnecessary WAL push' from Nikita Sivukhin
This PR ignores changes from `TURSO_SYNC_TABLE_NAME` meta table in order
to not generate unnecessary push commands when nothing actually changed
on the client side.

Closes #2597
2025-08-14 12:26:05 +03:00
Nikita Sivukhin
34a7b2ffd4 ignore changes in the turso_sync_last_change_id 2025-08-14 12:39:44 +04:00
Nikita Sivukhin
8c9d648852 add test which check that we don't push without the need 2025-08-14 12:38:15 +04:00
Nikita Sivukhin
f603a0dfc8 change log level to INFO in order to simplify debugging (DEBUG logs in the db are pretty spammy) 2025-08-14 12:37:49 +04:00
Jussi Saurio
69d8a73028 Merge 'use virtual root page for sqlite_schema' from Mikaël Francoeur
This PR fixes a problem where `sqlite_schema` could be read before page
1 was allocated.
The fix is similar to that in SQLite. In SQLite, if `btreeCursor()` sees
that the root page is 1 and that the b-tree is empty, it sets the page
to 0 ([here](https://github.com/sqlite/sqlite/blob/master/src/btree.c#L4
691-L4696)). SQLite's `moveToRoot()` then uses this special value to
return `CURSOR_INVALID` with no rows ([here](https://github.com/sqlite/s
qlite/blob/master/src/btree.c#L5538-L5540)).
Fixes https://github.com/tursodatabase/turso/issues/2449

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

Closes #2551
2025-08-14 11:08:11 +03:00
Jussi Saurio
d7186c7d7b Merge 'Add support for unlikely(X)' from bit-aloo
Implements the unlikely(X) function. Removes runtime implementations of
likely(), unlikely() and likelihood(), replacing them with panics if
they reach the VDBE.

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

Closes #2559
2025-08-14 10:56:27 +03:00
Jussi Saurio
78f1ed979e Merge 'io_uring: Gracefully handle submission queue overflow' from Preston Thorpe
Current handling is not ideal, this adds proper squeue overflow handling
by ensuring everything is still submitted in-order

Closes #2586
2025-08-14 10:55:17 +03:00
Jussi Saurio
0b17957f4e Merge 'Implement normal views' from Glauber Costa
Now that we actually implemented the statement parsing around views,
implementing normal SQLite views is relatively trivial, as they are just
an alias to a query.
We'll implement them now to get them out of the way, and then I'll go
back to DBSP

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

Closes #2591
2025-08-14 10:54:46 +03:00
Jussi Saurio
fed70b49be Merge 'Disable unused variables in cargo clippy for CI' from Pedro Muniz
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #2584
2025-08-14 10:54:18 +03:00
Jussi Saurio
359cba0474 Use BufferPool owned by Database instead of a static global
Problem

There are several problems with our current statically allocated
`BufferPool`.

1. You cannot open two databases in the same process with different
page sizes, because the `BufferPool`'s `Arena`s will be locked forever
into the page size of the first database. This is the case regardless
of whether the two `Database`s are open at the same time, or if the first
is closed before the second is opened.

2. It is impossible to even write Rust tests for different page sizes because
of this, assuming the test uses a single process.

Solution

Make `Database` own `BufferPool` instead of it being statically allocated, so this
problem goes away.

Note that I didn't touch the still statically-allocated `TEMP_BUFFER_CACHE`, because
it should continue to work regardless of this change. It should only be a problem if
the user has two or more databases with different page sizes open simultaneously, because
`TEMP_BUFFER_CACHE` will only support one pool of a given page size at a time, so the rest
of the allocations will go through the global allocator instead.

Notes

I extracted this change out from #2569, because I didn't want it to be smuggled in without
being reviewed as an individual piece.
2025-08-14 10:29:52 +03:00
bit-aloo
32e59614c7 remove unnecessary copy instr in likelihood, likely and unlikely 2025-08-14 09:08:32 +05:30
Preston Thorpe
be2c0ec6ab Merge 'Refactor: atomic ordering' from Preston Thorpe
Sequential is very rarely actually needed, we can very safely use
Acquire / Release for loads/stores, and some of these aren't guarding
anything and can use Relaxed.

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

Closes #2548
2025-08-13 22:39:50 -04:00
PThorpe92
3840708970 Add safety comment 2025-08-13 21:42:19 -04:00
PThorpe92
95d448d9af Actually defer callbacks till run_once in test extension 2025-08-13 21:42:19 -04:00
PThorpe92
ec95c3f8fb Add callback api to proc macros 2025-08-13 21:42:19 -04:00
PThorpe92
1bf74970bc Update test extension to new callback api 2025-08-13 21:42:19 -04:00
PThorpe92
0e3750b85b Integrate changes to vfs IO impl for callbacks 2025-08-13 21:42:18 -04:00
PThorpe92
20bf9ee2be Update vfs extension to support IO callbacks 2025-08-13 21:38:21 -04:00