Commit Graph

6063 Commits

Author SHA1 Message Date
Pekka Enberg
214831a591 build: Update cargo-dist to 0.28.6
Update `cargo-dist` to version 0.28.6. It should make installers more
robust to $HOME not being defined.

Refs #2073
2025-07-14 12:50:19 +03:00
Pekka Enberg
1a0d618a41 Merge 'Assert I/O read and write sizes' from Pere Diaz Bou
Let's assert **for now** that we do not read/write less bytes than
expected. This should be fixed to retrigger several reads/writes if we
couldn't read/write enough but for now let's assert.

Closes #2078
2025-07-14 12:22:18 +03:00
Pekka Enberg
9285d8b83b Merge 'Fix: OP_NewRowId to generate semi random rowid when largest rowid is i64::MAX' from Krishna Vishal
- `OP_NewRowId` now generates new rowid semi randomly when the largest
rowid in the table is `i64::MAX`.
- Introduced new `LimboError` variant `DatabaseFull` to signify that
database might be full (SQLite behaves this way returning
`SQLITE_FULL`).
Now:
```SQL
turso> CREATE TABLE q(x INTEGER PRIMARY KEY, y);
turso> INSERT INTO q VALUES (9223372036854775807, 1);
turso> INSERT INTO q(y) VALUES (2);
turso> INSERT INTO q(y) VALUES (3);
turso> SELECT * FROM q;
┌─────────────────────┬───┐
│ x                   │ y │
├─────────────────────┼───┤
│ 1841427626667347484 │ 2 │
├─────────────────────┼───┤
│ 4000338366725695791 │ 3 │
├─────────────────────┼───┤
│ 9223372036854775807 │ 1 │
└─────────────────────┴───┘
```
Fixes: https://github.com/tursodatabase/turso/issues/1977

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

Closes #1985
2025-07-14 11:56:09 +03:00
Pekka Enberg
0b544717a1 Merge 'do not check rowid alias for null' from Nikita Sivukhin
Simple PR to check minor issue that `INTEGER PRIMARY KEY NOT NULL` (`NOT
NULL` is redundant here obviously) will prevent user to insert anything
to the table as rowid-alias column always set to null by `turso-db`

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

Closes #2063
2025-07-14 11:55:06 +03:00
Pekka Enberg
80f9de133e Merge 'CDC functions' from Nikita Sivukhin
This PR adds few functions to the `turso-db` in order to simplify
exploration of CDC table. Later we will also add API to work with
changes from the code - but SQL support is also useful.
So, this PR adds 2 functions:
1. `table_columns_json_array('<table-name>')` - returns list of current
table column **names** as a single string in JSON array format
2. `bin_record_json_object('<columns-array>', x'<bin-record>')` -
convert record in the SQLite format to the JSON object with keys from
`columns-array`
So, this functions can be used together to extract changes in human-
readable format:
```sql
turso> PRAGMA unstable_capture_data_changes_conn('full');
turso> CREATE TABLE t(a INTEGER PRIMARY KEY, b);
turso> INSERT INTO t VALUES (1, 2), (3, 4);
turso> UPDATE t SET b = 20 WHERE a = 1;
turso> UPDATE t SET a = 30, b = 40 WHERE a = 3;
turso> DELETE FROM t WHERE a = 1;
turso> SELECT
    bin_record_json_object(table_columns_json_array('t'), before) before,
    bin_record_json_object(table_columns_json_array('t'), after) after
    FROM turso_cdc;
┌─────────────────┬────────────────┐
│ before          │ after          │
├─────────────────┼────────────────┤
│                 │ {"a":1,"b":2}  │
├─────────────────┼────────────────┤
│                 │ {"a":3,"b":4}  │
├─────────────────┼────────────────┤
│ {"a":1,"b":2}   │ {"a":1,"b":20} │
├─────────────────┼────────────────┤
│ {"a":3,"b":4}   │                │
├─────────────────┼────────────────┤
│ {"a":30,"b":40} │                │
├─────────────────┼────────────────┤
│ {"a":1,"b":20}  │                │
└─────────────────┴────────────────┘
```
Initially, I thought to implement single function like
`bin_record_json_object('<table-name', x'<bin-record')` but this design
has certain flaws:
1. In case of schema changes this function can return incorrect result
(imagine that you dropped a column and now JSON from CDC mentions some
random subset of columns). While this feature is unstable - `turso-db`
should avoid silent incorrect behavior at all cost
2. Single-function design provide no way to deal with schema changes
3. The API is unsound and user can think that under the hood `turso-db`
will select proper schema for the record (but this is actually
impossible with current CDC implementation)
So, I decided to stop with two-functions design which cover drawbacks
mentioned above to some extent
1. First concern still remains valid
2. Two-functions design provides a way to deal with schema changes. For
example, user can maintain simple `cdc_schema_changes` table and log
result of `table_columns_json_array` before applying breaking schema
changes.
    * Obviously, this is not ideal UX - but it suits my needs: I don't
want to design schema changes capturing, but also I don't want to block
users and provide a way to have a workaround for scenarios which are not
natively supported by CDC
3. Subjectively, I think that API became a bit more clear about the
machinery of these two functions as user see that it extract column list
of the table (without any context) and then feed it to the
`bin_record_json_object` function.

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

Closes #2057
2025-07-14 11:54:17 +03:00
Pere Diaz Bou
3a34f21434 io/windows: pread return bytes read 2025-07-14 10:44:56 +02:00
Pere Diaz Bou
340391538a io: change comment for assert 2025-07-14 10:36:06 +02:00
Pere Diaz Bou
93235bc566 io/wasm: return number read bytes 2025-07-14 10:35:55 +02:00
Pere Diaz Bou
88ff218810 io: assert small I/O
Let's assert **for now** that we do not read/write less bytes than
expected. This should be fixed to retrigger several reads/writes if we
couldn't read/write enough but for now let's assert.
2025-07-14 10:19:41 +02:00
Nikita Sivukhin
0457567714 more clippy fixes 2025-07-14 12:09:39 +04:00
Krishna Vishal
12f9743443 Remove unused imports 2025-07-14 13:13:54 +05:30
Krishna Vishal
ab0cb06755 split seek and getting rowid as two separate states 2025-07-14 13:11:41 +05:30
Krishna Vishal
3e880c34d6 Make op_new_rowid re-entrant
Introduce `OpNewRowidState` state machine

remove `get_new_rowid` from vdbe/mod.rs
2025-07-14 13:11:40 +05:30
Krishna Vishal
7f2a6187fb Add regression test 2025-07-14 13:09:36 +05:30
Krishna Vishal
98ca275b33 Add a way to semi randomly generate rowid when the max rowid reaches
`i64::MAX`. We do this by attempting to generate random values smaller
than `i64::MAX` for 100 times and returns `DatabaseFull` error on
failure

- Introduced `DatabaseFull` error variant

Fixes: https://github.com/tursodatabase/turso/issues/1977
2025-07-14 13:09:34 +05:30
Nikita Sivukhin
b330c6b70e fix clippy 2025-07-14 11:38:08 +04:00
Nikita Sivukhin
e94ebbad04 remove unwanted changes 2025-07-14 11:27:51 +04:00
Nikita Sivukhin
551c353fff fix clippy 2025-07-14 11:27:51 +04:00
Nikita Sivukhin
cc04f11bd6 remove clone 2025-07-14 11:27:51 +04:00
Nikita Sivukhin
f61d733dd3 make new functions dependend on "json" Cargo feature 2025-07-14 11:26:51 +04:00
Nikita Sivukhin
c9e7271eaf properly pass subtype 2025-07-14 11:20:49 +04:00
Nikita Sivukhin
bf25a0e3f1 fix clippy 2025-07-14 11:20:16 +04:00
Nikita Sivukhin
81cd04dd65 add bin_record_json_object and table_columns_json_array functions 2025-07-14 11:19:45 +04:00
Nikita Sivukhin
eed89993f9 fix clippy 2025-07-14 11:17:32 +04:00
Nikita Sivukhin
5409812610 properly implement generation of before/after records for new modes 2025-07-14 11:17:32 +04:00
Nikita Sivukhin
9e04102a94 add basic cdc tests for new modes 2025-07-14 11:17:31 +04:00
Nikita Sivukhin
fabb00f385 fix test 2025-07-14 11:16:06 +04:00
Nikita Sivukhin
b258c10c9a generate before/after row values in modification statements 2025-07-14 11:16:06 +04:00
Nikita Sivukhin
9129991b62 add id,before,after,full modes 2025-07-14 11:16:06 +04:00
Pekka Enberg
8f8d582b4a Merge 'Ignore double quotes around table names' from Zaid Humayun
This PR normalizes the table name identifier by removing quotes to
improve SQLite compatibility. Fixes
https://github.com/tursodatabase/turso/issues/1964
<img width="545" height="175" alt="Screenshot 2025-07-13 at 5 12 23 PM"
src="https://github.com/user-
attachments/assets/10952fc7-9ade-4c97-a427-385ff2dc3b44" />
<img width="384" height="110" alt="Screenshot 2025-07-13 at 5 12 32 PM"
src="https://github.com/user-
attachments/assets/5d87e0fe-72a4-4472-abc3-24c0d4cc8add" />

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

Closes #2069
2025-07-14 10:04:59 +03:00
Pekka Enberg
26ba8c1176 Merge 'Efficient Record Comparison and Incremental Record Parsing ' from Krishna Vishal
Currently we deserialize the entire record to compare them or to get a
particular column. This PR introduces efficient record operations such
as incremental column deserialization and efficient record comparison.
### Incremental Column Deserialization
- Introduced `RecordCursor` to keep track of how much of the header and
the record we have already parsed. Each `BTreeCursor` will have its own
`RecordCursor` similar to an `ImmutableRecord`.
- The `RecordCursor` gets the number of columns from schema when the
BTreeCursor is initialized in VDBE. This helps in cutting down heap
allocs by reserving the correct amount of space for underlying `Vec`s.
- `Immutable` record only carries the serialized `payload` now.
- We parse the header up till we reach the required serial type (denoted
by the column index) and then calculate the offsets and deserialize only
that particular slice of the payload.
- Manually inlined most of the deserialization code into `fn op_column`
code because the compiler is refusing to inline even with
`#[inline(always)]` hint. This is probably due to complicated control
flow.
- Tried to follow SQLite semantics, where it returns `Null` when the
requested column falls outside the number of columns available in the
record or when the payload is empty etc.
### Efficient Record Comparison ops
- Three record comparison function are introduced for Integer, String
and for general case which replaces the `compare_immutable`. These
functions compare a serialized record with deserialized one.
- `compare_records_int`: is used when the first field is integer, header
≤63 bytes, ≤13 total fields. No varint parsing, direct integer
extraction.
- `compare_records_string`: is used when the first field is text with
binary collation, header ≤63 bytes.
-  `compare_records_generic`: is used in complex cases, custom
collations, large headers. Here we parse the record incrementally field
by field and comparing each field with the one from the deserialized
record. We early exit on the first mismatch saving on the
deserialization cost.
- `find_compare`: selects the optimal comparison strategy for a given
case and dispatches the function required.
### Benchmarks `main` vs `incremental_column`
I've used the `testing/testing.db` for this benchmark.
| Query                                                       | Main
| Incremental | % Change (Faster is +ve) |
|-------------------------------------------------------------|---------
-|-------------|------------------------|
| SELECT first_name FROM users                                | 1.3579ms
| 1.1452ms      | 15.66          |
| SELECT age FROM users                                       | 912.33µs
| 897.97µs      | 1.57            |
| SELECT email FROM users                                     | 1.3632ms
| 1.215ms       | 10.87            |
| SELECT id FROM users                                        | 1.4985ms
| 1.1762ms      | 21.50            |
| SELECT first_name, last_name FROM users                     | 1.5736ms
| 1.4616ms      | 7.11            |
| SELECT first_name, last_name, email FROM users              | 1.7965ms
| 1.754ms       | 2.36            |
| SELECT id, first_name, last_name, email, age FROM users     | 2.3545ms
| 2.4059ms      | -2.18           |
| SELECT * FROM users                                         | 3.5731ms
| 3.7587ms      | -5.19           |
| SELECT * FROM users WHERE age = 30                          | 87.947µs
| 85.545µs     | 2.73            |
| SELECT id, first_name FROM users WHERE first_name LIKE 'John%' |
1.8594ms   | 1.6781ms      | 9.75            |
| SELECT age FROM users LIMIT 1000                            | 100.27µs
| 95.418µs      | 4.83            |
| SELECT first_name, age, email FROM users LIMIT 1000         | 176.04µs
| 167.56µs      | 4.81            |
Closes: https://github.com/tursodatabase/turso/issues/1703

Closes #1923
2025-07-14 10:04:19 +03:00
Krishna Vishal
370d437491 Add docs for get_tie_breaker_from_idx_comp_op 2025-07-14 03:28:55 +05:30
Krishna Vishal
4c5383b0b3 chore: clippy 2025-07-14 03:28:55 +05:30
Krishna Vishal
e27b9c7e0f Address review comments 2025-07-14 03:28:55 +05:30
Krishna Vishal
a79fe458db Fix merge conflicts and adapt schema.rs to use RecordCursor 2025-07-14 03:28:55 +05:30
Krishna Vishal
ea4a4708ea - Address some review comments
- Add docs for `RecordCursor`
2025-07-14 03:28:55 +05:30
Krishna Vishal
b1f27cad94 chore: fix clippy 2025-07-14 03:28:55 +05:30
Krishna Vishal
d3368a28bc fix merge conflicts 2025-07-14 03:28:55 +05:30
Krishna Vishal
894bdca05f refactor: change ImmutableRecord payload to Value for compatibility 2025-07-14 03:28:54 +05:30
Krishna Vishal
d78185bd62 Remove mistakenly pushed file 2025-07-14 03:28:54 +05:30
Krishna Vishal
9de3cf0c60 Remove redundant checks 2025-07-14 03:28:54 +05:30
Krishna Vishal
235e798561 Return corrupt errors. 2025-07-14 03:28:54 +05:30
Krishna Vishal
9393aba0bd Add docs for RecordCompare 2025-07-14 03:28:54 +05:30
Krishna Vishal
e7e5f28c0a chore: Clippy chill 2025-07-14 03:28:54 +05:30
Krishna Vishal
f3b169bf30 Fix empty blob test failure. 2025-07-14 03:28:54 +05:30
Krishna Vishal
9b315d1d7e Manually inline the record deserialization code for performance.
This is done because the compiler is refusing to inline even after
adding inline hint.
- Get refvalues from directly from registers without using
`make_record`
2025-07-14 03:28:54 +05:30
Krishna Vishal
35ed279644 Clean up indexbtree_move_to 2025-07-14 03:28:54 +05:30
Krishna Vishal
dca4e669f7 Reduce allocations in compare_records_generic 2025-07-14 03:28:54 +05:30
Krishna Vishal
f0e8e5871b Replace compare_immutable with compare_records_generic 2025-07-14 03:28:54 +05:30
Krishna Vishal
860de412d9 Add num_columns to BTreeCursor so we can initialize
`Vecs` inside `RecordCursor` to their appropriate to reduce
allocations.
2025-07-14 03:28:54 +05:30