Read transactions from logical log and load mvcc store based on the
contents onto transaction 0, which is the special transaction where we
will load all row versions that can be read by all new transactions.
- [ ] Testing for multiple transactions
- [ ] Lock multiple loading of same logical-log at the same time
- [ ] Add column_count to format because if not it is hard to judge from
record alone.
This format is based on previous discussions:
1. Log header
```rust
/// Log's Header, this will be the 64 bytes in any logical log file.
/// Log header is 64 bytes at maximum, fields added must not exceed that size. If it doesn't exceed
/// it, any bytes missing will be padded with zeroes.
struct LogHeader {
version: u8,
salt: u64,
encrypted: u8, // 0 is no
}
```
2. Transaction format:
* Transaction id
* Checksum u64
* Byte size of all rows combined
* Rows
* End marker (offset position after appending buffer)
3. Row format:
```rust
/// Serialize a row_version into on disk format.
/// Format of a "row" (maybe we could change the name because row is not general enough for
/// future type of values):
///
/// * table_id (root page) -> u64
/// * row type -> u8
///
/// (by row type)
/// Delete:
/// * Payload length -> u64
/// * Rowid -> varint
///
/// Insert:
/// * Payload length -> u64
/// * Data size -> varint
/// * Rowid -> varint
/// * Data -> [u8] (data size length)
fn serialize(&self, buffer: &mut Vec<u8>, row_version: &RowVersion) {
```
Closes#3245
fixes#3231
```zsh
❯ sqlite3
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> CREATE TABLE t1 (a);
ALTER TABLE t1 ADD COLUMN a;
Parse error: duplicate column name: a
sqlite> ALTER TABLE t1 ADD COLUMN name varchar(255);
SELECT sql FROM sqlite_schema WHERE name = 't1';
CREATE TABLE t1 (a, name varchar(255))
sqlite>
```
```zsh
turso>
turso> CREATE TABLE t1 (a);
ALTER TABLE t1 ADD COLUMN a;
x Parse error: duplicate column name: a
turso> ALTER TABLE t1 ADD COLUMN name varchar(255);
SELECT sql FROM sqlite_schema WHERE name = 't1';
┌─────────────────────────────────────────┐
│ sql │
├─────────────────────────────────────────┤
│ CREATE TABLE t1 (a, name varchar (255)) │
└─────────────────────────────────────────┘
turso>
```
Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>
Closes#3249
Closes#3253
Had to do some slight changes in how we shadow interactions and changed
some of the execution code to accommodate both runtime generation and
pre-run generation (pre-run generation is needed for instance when
running the shrunk plans). This behavior is abstracted by the
`InteractionPlanIterator` trait
Closes#3272
Use SQL over HTTP batch statement in the push logic of sync-engine with
additional condition for `!auto_commit` for every step except from
initial `BEGIN` of txn.
This is needed to avoid non-transactional update if SQLite will decide
to close transaction due to some error and reset state to autocommit
mode.
Closes#3271
This PR implements the `Sequence` and `SequenceTest` opcodes, although
does not yet add plumbing to emit the latter.
SQLite has two distinct mechanisms that determine the final row order
with aggregates:
Traversal order of GROUP BY, and ORDER BY tiebreaking. When ORDER BY
contains only aggregate expressions and/or constants, SQLite has no
extra tiebreak key, but when ORDER BY mixes aggregate and non-aggregate
terms, SQLite adds an implicit, stable row `sequence` so “ties” respect
the input order.
This PR also fixes an issue with a query like the following:
```sql
SELECT u.first_name, COUNT(*) AS c
FROM users u
JOIN orders o ON o.user_id = u.id
GROUP BY u.first_name
ORDER BY c DESC;
```
Because ORDER BY has only an aggregate (COUNT(*) DESC) and no non-
aggregate terms, SQLite traverses the group key (u.first_name) in DESC
order in this case, so ties on c naturally appear with group keys in
descending order.
Previously tursodb would return the group key sorted in ASC order,
because it was used in all cases as the default
Closes#3287