This PR adds the beginnings of
[AUTOVACUUM](https://www.sqlite.org/lang_vacuum.html) to Limbo. It adds
a feature flag called `omit_autovacuum` which is analogous to
`SQLITE_OMIT_AUTOVACUUM`. It is off by default, same as SQLite.
It introduces the concept of [pointer map pages](https://www.sqlite.org/
fileformat.html#pointer_map_or_ptrmap_pages) which are reverse index
pages used to map pages to their parents. This is used to swap pages
(when a table is deleted for instance) to keep root pages clustered at
the beginning of the file. It's also used while creating a table to
ensure that root pages are clustered at the beginning (although, this
isn't completely implemented yet)
Finally, it also adds a couple of missing instructions like `Int64` that
are required for `PRAGMA` commands related to `auto_vacuum` settings
<img width="1512" alt="Screenshot 2025-05-28 at 8 47 51 PM"
src="https://github.com/user-
attachments/assets/d52eb74f-5b79-4d52-9401-1bdc2dcc304d" />
Closes#1600
This PR implements the `ToSqlString` trait to most of the `ast` structs
and to the `SelectPlan`, `UpdatePlan`, `DeletePlan`,
`CompoundSelectPlan`.
Inside the files in the `to_sql_string` folder, I annotated many `TODOs`
with things that seem to diverge from SQLite syntax. The most egregious
by far was that Create Trigger statements do not use the standard
`delete`, `select`, `update`, and `insert` statements. The parser uses
different structs for those statements only in Create Trigger. E.g
`ast::TriggerCmdUpdate` instead of `ast::Update` and so on.
Also, as this iteration of reverse parsing is not particularly efficient
in the number of string allocations it does. I tested different methods
of achieving this by using `format!`, pushing directly to a `String`, or
just pushing to `Vec<String>` and joining all the string with a space
separator. I focused mainly on trying to get the syntax to print
correctly without major hurdles in understanding the code.
Lastly, I intend in the future to use this code in the simulator to
expand the its available syntax.
Closes#1619
This commit ensures that the metadata in the database header and the pointer map pages allocated are correctly persisted to the page cache. This was not being done earlier.
This commit introduces AUTOVACUUM to Limbo. It introduces the concept of ptrmap pages and also adds some additional instructions that are required to make AUTOVACUUM PRAGMA work
Closes#1622
I did an A/B test between SQLite and Limbo and they can restart the db
from each other, indicating that there isn't something very wrong with
our file format. Turns out it was with our reset logic without
truncating the file. I assumed it's safe to don't reset if we're in
`PASSIVE` mode, given the
[docs](https://www.sqlite.org/c3ref/wal_checkpoint_v2.html) and [source
code](https://github.com/sqlite/sqlite/blob/2bd9f69d40dd240c4122c6d02f1f
f447e7b5c098/src/wal.c#L2193).
It also does some small clean ups and fixes.
Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>
Closes#1647
This PR adds support for table-valued functions for PRAGMAs (see the
[PRAGMA functions section](https://www.sqlite.org/pragma.html)).
Additionally, it introduces built-in table-valued functions. I
considered using extensions for this, but there are several reasons in
favor of a dedicated mechanism:
* It simplifies the use of internal functions, structs, etc. For
example, when implementing `json_each` and `json_tree`, direct access to
internals was necessary:
https://github.com/tursodatabase/limbo/pull/1088
* It avoids FFI overhead. [Benchmarks](https://github.com/piotrrzysko/li
mbo/blob/pragma_vtabs_bench/core/benches/pragma_benchmarks.rs) on my
hardware show that `pragma_table_info()` implemented as an extension is
2.5× slower than the built-in version.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#1642
Closes#1628. Every function that calls `process_overflow_read` needs
to be reentrant. I did not change it here, but it would include
`get_prev_record` and `get_next_record`. Maybe `tablebtree_move_to` did
not need to use the state machine, but I included it as a safeguard.
Edit: Closes#1625 . When I implemented `restore_context`, I forgot to
add a `return_if_io` after calling it in `next` 🤦♂️
Edit: Closes#1617 . Just tested it and it also solves this bug.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#1636
The salts values in the WAL header are (re)generated in every checkpoint (but in PASSIVE mode), so if we find a frame with mismatch it means it's a leftover from a previous checkpoint.
In preparation for `CREATE VIEW`, we need to have the original sql query
that was used to create the view. I'm using the scanner's offset to
slice into the original input, trimming the newlines, and passing it to
the translate function.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#1621
Instrument trace_insn to debug print its the stack pc and instruction.
Also, disable rustyline logs for the CLI as it is too noisy to work
with.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#1635