Previously, the `jump_if_condition_is_true` flag was not respected. As a
result, for expressions like <`ISNULL`/`NOTNULL`> `OR` <rhs>, the <rhs>
expression was evaluated even when the left-hand side was true, and its
value was incorrectly used as the final result.
Page 1 must be initialized and written as soon as possible without
marking page as dirty.
OpenEphemeral now requires a state machine to accomodate new
begin_write_tx semantics.
Closes#1839
Makes it easier to test the feature:
```
$ cargo run -- --experimental-indexes
Limbo v0.0.22
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database
limbo> CREATE TABLE t(x);
limbo> CREATE INDEX t_idx ON t(x);
limbo> DROP INDEX t_idx;
```
Closes#1831
Makes it easier to test the feature:
```
$ cargo run -- --experimental-indexes
Limbo v0.0.22
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database
limbo> CREATE TABLE t(x);
limbo> CREATE INDEX t_idx ON t(x);
limbo> DROP INDEX t_idx;
```
Support for simple interactive rollback like:
```sql
create table t (x);
insert into t values (1);
begin;
insert into t values (2);
rollback;
select * from t;
```
This PR also fixes some other issues I found while debugging:
* Checkpoint would never `clear_dirty` on pages in page cache.
* Auto commit for interactive transactions was not respected so any
`insert` after `begin` would flush frames regardless of `auto_commit`
state.
* `max_frame` on wal shared state was being updated after every
`append_frame` which was incorrect, as another transaction would be able
to use that new `max_frame` even tho the transaction could've rolled
back. Instead we update the private copy of `max_frame` and only update
it at the end.
Follow up for later are savepoints which require implementing a
subjournal to track savepoints and their modified pages.
Closes#1825
The example is wrong (imports wrong package), but also duplicates text from
top-level README.md that will change. Let's drop this for now to avoid
confusion.
When `struct Database` is constructed, store `is_empty` as an
`Arc<AtomicBool>` - the value is true if:
1. DB size is zero
2. WAL has no frames
When `struct Pager` is constructed, this `Arc` is simply cloned. When
any connection runs a transaction it will first check `Pager::is_empty`,
and if the DB is empty, it will lock `init_lock` and then check
`is_empty` again, and if it's still true, it allocates page1 and stores
`false` in the `is_empty` `AtomicBool` and drops the lock.
---
Note that Limbo can currently have a zero DB and a WAL with frames, as
we have no special logic for folding page1 to the main DB file during
initialization.
Page 1 allocation currently happens on the first transaction (read or
write, due to having to support `select * from sqlite_schema` on an
empty DB; we should really check how SQLite actually does this.).
Closes#1830
When `struct Database` is constructed, store `is_empty` as an
`Arc<AtomicBool>` - the value is true if:
1. DB size is zero
2. WAL has no frames
When `struct Pager` is constructed, this `Arc` is simply cloned.
When any connection runs a transaction it will first check `is_empty`,
and if the DB is empty, it will lock `init_lock` and then check `is_empty`
again, and if it's still true, it allocates page1 and stores `false` in
the `is_empty` `AtomicBool` and drops the lock.
---
Note that Limbo can currently have a zero DB and a WAL with frames,
as we have no special logic for folding page1 to the main DB file
during initialization.
Page 1 allocation currently happens on the first transaction (read or
write, due to having to support `select * from sqlite_schema` on an
empty DB; we should really check how SQLite actually does this.).
Closes#1821
Two caveats:
- I wouldn't merge this before #1761 since it fails constantly against
`main` code
- Not sure if this works as intended with multiple threads, but we don't
support that in `stress` yet anyway
Reviewed-by: Pekka Enberg <penberg@iki.fi>
Closes#1822
Add fault type that reopens the DB and reconnects the connections. It
purposefully does not call conn.close(), as the default behavior of that
method is to checkpoint the database.
This easily exposes multiple manifestations of DB/WAL corruption caused
by this issue: https://github.com/tursodatabase/limbo/issues/1725
in fact, in my testing, every run fails when this fault is enabled.
Hence I've added the --disable-reopen-database flag if you want to try
to find some other bugs.
Closes#1762
This PR makes limbo read the database header and schema via the regular
pager `read_page()` route, instead of always reading it from the
database file. This is very important for correctness as modifications
to page 1 (containing header and schema) may exist in WAL frames, not
just the main database file. Without this, practically any real use of
the database would very quickly corrupt the DB as limbo would read the
incorrect page count from the stale page 1 copy in the database file,
and e.g. allocate the same rootpage for multiple tables and so on.
This issue has been so far hidden due to lack of tests and the fact that
`Connection::close()` checkpoints the DB by default, which has delayed
discovering this incredibly basic issue since most manual testing has
happened via CLI, and if everything is always checkpointed, reopening
the DB doesn't cause any problems.
This closes#1725
See also: #1762 (commit from this PR is bundled into this branch as
well)
EDIT: Also closes#1818Closes#1761