Commit Graph

3549 Commits

Author SHA1 Message Date
bit-aloo
3cb2db933d remove Id 2025-07-24 14:40:24 +05:30
bit-aloo
9a54ef214e parser: Distinguish quoted identifiers and unify Id into Name enum
This commit replaces the `Name(pub String)` struct with a `Name` enum that
explicitly models how the name appeared in the source either as an
unquoted identifier (`Ident`) or a quoted string (`Quoted`).

In the process, the separate `Id` wrapper type has been coalesced into the
`Name` enum, simplifying the AST and reducing duplication in identifier
handling logic.

While this increases the size of some AST nodes (notably `yyStackEntry`),
it improves correctness and makes source structure more explicit for
later phases.
2025-07-24 14:40:19 +05:30
Jussi Saurio
2d3c9001ee Merge 'emit SetCookie after DropTable' from Glauber Costa
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
2025-07-24 10:12:16 +03:00
Jussi Saurio
92a10f94d8 Merge 'Bail early for read-only virtual tables' from Preston Thorpe
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
2025-07-24 10:12:07 +03:00
Jussi Saurio
dd30729f5e Merge 'Explicit rowid insert' from Nikita Sivukhin
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
2025-07-24 10:08:42 +03:00
Jussi Saurio
49b2bf4fdb Merge 'Deserialize keys only once when sorting immutable records' from Iaroslav Zeigerman
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
2025-07-24 10:08:16 +03:00
Jussi Saurio
52b4c22be9 Merge 'fix: SUM returns correct float for mixed numeric/non-numeric types & return value on empty set' from Axel Tobieson Rova
# 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
2025-07-24 10:08:01 +03:00
Glauber Costa
2a2468026c emit SetCookie after DropTable
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.
2025-07-23 19:34:41 -05:00
PThorpe92
b68539fc45 Make the ReadOnly error more generic 2025-07-23 16:58:22 -04:00
PThorpe92
0871a8c7f3 Bail early when we detect a readonly virtual table 2025-07-23 16:57:30 -04:00
PThorpe92
9c3f9426c3 Add readonly method for VirtualTable to bail early 2025-07-23 16:49:42 -04:00
Iaroslav Zeigerman
1e51d23bd6 store the key deserialization error instead of panicking 2025-07-23 11:22:01 -07:00
Nikita Sivukhin
001670c069 fix clippy 2025-07-23 16:00:24 +04:00
Nikita Sivukhin
a017baced7 small refactoring 2025-07-23 15:56:38 +04:00
Nikita Sivukhin
d3f3807ede fix cdc emit 2025-07-23 15:50:34 +04:00
Nikita Sivukhin
fd63128227 adjust behaviour in case when both rowid alias and rowid provided 2025-07-23 15:42:05 +04:00
Nikita Sivukhin
a4d114460a adjust vtable insert 2025-07-23 15:22:56 +04:00
Jussi Saurio
1e38202084 Merge 'WAL insert API' from Nikita Sivukhin
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
2025-07-23 14:08:15 +03:00
Nikita Sivukhin
0178b41b28 accept explicit "rowid" column name in the INSERT statement 2025-07-23 15:03:38 +04:00
Jussi Saurio
63f488a1cc Merge 'Pager: clear overflow cells when freeing page' from Jussi Saurio
## 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
2025-07-23 13:38:31 +03:00
Jussi Saurio
f98a9e8939 Pager: don't assume page is necessarily in memory anymore 2025-07-23 11:08:34 +03:00
Jussi Saurio
ecb5fce1bd Pager: clear overflow cells when freeing page 2025-07-23 10:58:10 +03:00
Jussi Saurio
ffd2299aa1 types: less noisy Debug implementation for ImmutableRecord 2025-07-23 10:56:41 +03:00
Nikita Sivukhin
60eaa11add hide new methods behind fs feature 2025-07-23 11:51:39 +04:00
Nikita Sivukhin
a85283a84f add trailing comma 2025-07-23 11:31:00 +04:00
Nikita Sivukhin
3c0af3e389 small adjustments 2025-07-23 11:31:00 +04:00
Nikita Sivukhin
73761a8983 rollback non-commited changes 2025-07-23 11:31:00 +04:00
Nikita Sivukhin
bf2bfbe978 fix clippy 2025-07-23 11:31:00 +04:00
Nikita Sivukhin
16763e1500 implement raw WAL write api 2025-07-23 11:30:59 +04:00
Nikita Sivukhin
bc09ea6e98 make end_write_txn/end_read_txn function non-failing 2025-07-23 11:30:29 +04:00
PThorpe92
a13fc3515e Fix cargo fmt warning 2025-07-22 21:47:15 -04:00
Glauber Costa
a10d8d7f94 silence clippy errors with features disabled
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.
2025-07-22 20:37:45 -05:00
PThorpe92
9c785ea0ea Merge 'make readonly a property of the database' from Glauber Costa
There's no such thing as a read-only connection.
In a normal connection, you can have many attached databases. Some r/o,
some r/w.
To properly fix that, we also need to fix the OpenWrite opcode. Right
now we are passing a name, which is the name of the table. That
parameter is not used anywhere. That is also not what the SQLite opcode
specifies. Same as OpenRead, the p3 register should be the database
index.
With that change, we can - for now - pass the index 0, which is all we
support anyway, and then use that to test if we are r/o.

Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #2232
2025-07-22 21:12:08 -04:00
Levy A.
e6ad88cc18 refactor: constified enum -> regular enum 2025-07-22 17:20:30 -03:00
Levy A.
203239ff30 refactor: safer db_state 2025-07-22 17:20:29 -03:00
Axel
9d05344258 Fix Sum() return value if there are no non-NULL input rows
Add simple fuzz test for total and sum.
2025-07-22 17:38:09 +02:00
Glauber Costa
57a1113460 make readonly a property of the database
There's no such thing as a read-only connection.
In a normal connection, you can have many attached databases. Some
r/o, some r/w.

To properly fix that, we also need to fix the OpenWrite opcode. Right
now we are passing a name, which is the name of the table. That
parameter is not used anywhere. That is also not what the SQLite opcode
specifies. Same as OpenRead, the p3 register should be the database
index.

With that change, we can - for now - pass the index 0, which is all
we support anyway, and then use that to test if we are r/o.
2025-07-22 09:41:32 -05:00
Jussi Saurio
72b4318fa1 Merge 'fix raw read frame WAL API' from Nikita Sivukhin
This PR fixes `wal_read_frame_raw` API
Before, implementation of raw read API read only page content - which is
not enough as we also need page_no and size_after fields from the
header. This PR fixes that and also make few adjustments in the
signatures.

Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #2229
2025-07-22 16:10:55 +03:00
Nikita Sivukhin
b34d081d35 cargo fmt 2025-07-22 16:23:04 +04:00
Nikita Sivukhin
d617d1d21e fix raw read frame WAL API 2025-07-22 16:21:04 +04:00
Nikita Sivukhin
a730136564 use default hasher for the sake of determinism 2025-07-22 16:18:42 +04:00
Jussi Saurio
022f679fab chore: make every CREATE TABLE stmt in entire repo have 1 space after tbl name
`BTreeTable::to_sql` makes us incompatible with SQLite by losing e.g. the original whitespace provided during the CREATE TABLE command.

For now let's fix our tests by regex-replacing every CREATE TABLE in
the entire repo to have exactly 1 space after the table name in the
CREATE TABLE statement.
2025-07-22 11:35:21 +03:00
Jussi Saurio
13d40c6a73 schema: fix extra whitespace in BTreeTable::from_sql 2025-07-22 11:11:08 +03:00
Pekka Enberg
f979903633 Merge 'Load static extensions once and store on Database instead of once per connection' from Preston Thorpe
To help make the connection speed faster, we don't need to register
these every time as they are compiled in.

Closes #2213
2025-07-22 09:16:22 +03:00
Pekka Enberg
afc22da07c Merge 'Fix duplicate SET statement compatibility with SQLite' from Ihor Andrianov
For duplicate SET statements, SQLite uses last one

Closes #2117
2025-07-22 09:02:16 +03:00
Pekka Enberg
b03fe4669a Merge 'wal: write txn fail in case max_frame change midway' from Pere Diaz Bou
A write txn can only start if the current snapshot held by writer is
consistent with the one in shared state

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

Closes #2204
2025-07-22 09:01:44 +03:00
Iaroslav Zeigerman
d847c88df6 cosmetic 2025-07-22 06:37:50 +02:00
PThorpe92
a92126961d Remove duplicate case and just send Mutex along with schema for extension registrations 2025-07-21 20:06:14 -04:00
PThorpe92
d514304ac2 Remove unneeded changes 2025-07-21 19:24:24 -04:00
PThorpe92
f7ba8efdbd Switch back to std::mutex because it was an unnecessary change 2025-07-21 19:20:17 -04:00