WAL insert API introduced in the #2231 works incorrectly as it never
mark inserted pages as dirty.
This PR fixes this issue and also add simple fuzz test which fails
without fixes.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#2245
Make `add_dirty` helper to set flag and add page to the dirt list. This
makes API safer as now its harder to do one thing and forget about
another (which can lead to DB corruption).
Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#2244
The SetCookie opcode is used, among other things, to notify the
transaction of schema changes. We are not issuing it on DropTable.
Without it, the transaction thinks the schema hasn't changed, and does
not update the schema of the connection back to the database.
SQLite will, of course, issue it:
35 DropTable 0 0 0 foo 0
36 SetCookie 0 1 2 0
Unfortunately I don't have a unit test that breaks with this, because
the one that is supposed to break is having, let's put it this way,
bigger problems.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#2249
This PR adds a const associated value on the VTabModule trait,
`READONLY` defaulted to `true`, so we can bail early when a write
operation is done on an invalid vtable.
This prevents extensions from having to implement `insert`,`update`,
`delete` just to return `Error::ReadOnly`, and prevents us from having
to step through `VUpdate` just to error out.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#2247
The current code includes creating the database object, which is slow.
Unfortunately the same cannot be done on the standard SQLite.
Reviewed-by: Preston Thorpe (@PThorpe92)
Closes#2242
This PR adds support for `INSERT` queries with explicit value for
`rowid` column (not thought rowid alias):
```
turso> create table t(x, y, z);
turso> insert into t(rowid, x, y, z) values (10, 1, 2, 3);
turso> select rowid, * from t;
┌───────┬───┬───┬───┐
│ rowid │ x │ y │ z │
├───────┼───┼───┼───┤
│ 10 │ 1 │ 2 │ 3 │
└───────┴───┴───┴───┘
```
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#2239
Before this update, the entire immutable record was **fully**
deserialized **every** time it was compared in the sorter.
This PR extends the sorter with incremental deserialization of record
keys, only when needed and only if they weren’t already deserialized in
a previous iteration.
I hate that we panic on failed deserialization in `cmp`, but
unfortunately, I can’t return `Result` as part of this interface.
Looking for feedback around a better way to handle this.
Alternatively, I could store the deserialization error as part of
`SortableImmutableRecord` and check it before returning the record in
`next`, thereby deferring the error handling. The downside of this
approach is that it complicates debugging, since the error will be
completely decoupled from the place where it occurs.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#2207
# Fix SUM aggregate function for mixed types
Fixes#2133
The SUM aggregate function was returning incorrect results when
processing tables with mixed numeric and non-numeric values. According
to SQLite documentation:
> "If any input to sum() is neither an integer nor a NULL, then sum()
returns a floating point value"
[*](https://sqlite.org/lang_aggfunc.html)
Now both SQLite and Turso yield the same output of 44.0.
--
I modified `Sum` to increment only for numeric values, skipping non-
numeric values. However, if we have mixed numeric values or non-numeric
values, we return a float output. Added a flag to keep track of it.
as pointed out by @FHaggs , If there are no non-NULL input rows then
sum() returns NULL but total() returns 0.0. I decided to include it in
this PR as well. Empty was such a natural test case.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#2182
The SetCookie opcode is used, among other things, to notify the
transaction of schema changes. We are not issuing it on DropTable.
Without it, the transaction thinks the schema hasn't changed, and does
not update the schema of the connection back to the database.
SQLite will, of course, issue it:
35 DropTable 0 0 0 foo 0
36 SetCookie 0 1 2 0
Unfortunately I don't have a unit test that breaks with this, because
the one that is supposed to break is having, let's put it this way,
bigger problems.
This PR implements missing raw WAL API from LibSQL for future use for
offline-sync feature:
1. `wal_insert_begin` - begin WAL session by opening WAL read/write
transaction
2. `wal_insert_end` - finish WAL session by closing WAL transaction
opened by `wal_insert_begin` call
3. `wal_insert_frame` - insert frame `frame_no` with raw content `frame`
(WAL frame included)
For now any schema changes will not be reflected after
`wal_insert_frame` because `turso-db` do not re-parse schema without
need. I will fix this in follow up PR.
Reviewed-by: Pekka Enberg <penberg@iki.fi>
Closes#2231
## Background
The `balance_non_root` procedure can end up freeing a page if the pages
to be balanced can fit the required combined number of cells in less
pages, even if the page that triggered balancing is overfull. This can
then free the originally overfull pages, leaving a non-zero
`overflow_cells` on the in-mem representation of the page.
```rust
balance_non_root: page=305, overflow_cells=0
balance_non_root: page=304, overflow_cells=0
balance_non_root: page=302, overflow_cells=1
pre_edit_page(page=304, page_idx=0, new_cells=4, old_cells=1, cells_per_page_old=[1, 3, 9, 0, 0], cells_per_page_new=[4, 9, 9, 0, 0], cell_array_count=9)
edit_page start_old_cells=0 start_new_cells=0 number_new_cells=4 cell_array=9 end_old_cells=1 end_new_cells=4
pre_edit_page(page=305, page_idx=1, new_cells=4, old_cells=1, cells_per_page_old=[1, 3, 9, 0, 0], cells_per_page_new=[4, 9, 9, 0, 0], cell_array_count=9)
edit_page start_old_cells=2 start_new_cells=5 number_new_cells=4 cell_array=9 end_old_cells=3 end_new_cells=9
balance_non_root: sibling_count_new=2, sibling_count=3
// Custom assertion to demonstrate this:
thread 'main' panicked at core/storage/pager.rs:1127:29:
Pager::free_page: In memory page with id 302 has overflow cells
```
## Why is this a problem
Right now this is not an immediate problem, because we always allocate
brand new pages. However, in #2233 we begin to reuse pages from the
freelist for page allocation to improve performance and reduce database
size bloat. In that PR, the `balance_non_root` procedure will calculate
cell counts incorrectly in `edit_page()` and panic if: 1. a new
allocated page is taken from the freelist, 2. the page is still in
memory, and 3. and it still contains `overflow_cells`.
## Solution
Clear `page_contents.overflow_cells` when an in-memory page is freed.
Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>
Closes#2238
This PR truncates the Debug output of ImmutableRecord so that blobs and
texts only show the first 20 bytes or chars respectively. Debugging gets
much better when we don't print a huge blob or text a million times.
Closes#2237
When compiling with features disabled, there are lots of clippy
warnings. This PR silences them.
For the utils file, I am using a bit of a hammer and just allowing
unused stuff in the whole file. Due to the box of utilities nature of
this file, it'll always be the case that things will be unused depending
on the feature-set.
Reviewed-by: Preston Thorpe (@PThorpe92)
Closes#2236
When compiling with features disabled, there are lots of clippy
warnings. This PR silences them.
For the utils file, I am using a bit of a hammer and just allowing
unused stuff in the whole file. Due to the box of utilities nature of
this file, it'll always be the case that things will be unused depending
on the feature-set.