Commit Graph

3178 Commits

Author SHA1 Message Date
Pekka Enberg
3204c889eb Merge 'Antithesis testing' from Pekka Enberg
Closes #1063
2025-03-04 09:34:18 +02:00
Pekka Enberg
be4014a1df Initial pass on Antithesis testing
This adds a "limbo_stress" tool for stress testing Limbo in
non-deterministic way together with support code to run the tests under
Antithesis (which makes them deterministic). The stress tester does not
really do anything useful yet, this is just a step to make sure we can
run tests under Antithesis.
2025-03-04 09:29:57 +02:00
Pere Diaz Bou
c393ce896a Merge 'Add BalanceInfo into WriteInfo' from Pere Diaz Bou
This PR is a cleanup of stuff that I found while eating a burguer:
* `WriteInfo` now includes `BalanceInfo` to hold balance information so
that we can release it at will.
* Remove: `new_pages` and `scratch_cells`
* Once `balance_non_root` completes, it now resets state to
`BalanceStart` so that we can trigger other balance if needed. (we need
to add tests for this).
* Remove extra allocation of divider cells.

Closes #1082
2025-03-03 11:38:13 +01:00
Pere Diaz Bou
1de4861414 fix balance_non_root should trigger balance again 2025-03-02 23:16:32 +01:00
Pere Diaz Bou
10824e3eb3 remove new_pages, remove extra divider_cells and cells capacity 2025-03-02 22:49:20 +01:00
Pere Diaz Bou
0da12df67c Introduce BalanceInfo to hold all balance procedure information 2025-03-02 22:42:48 +01:00
Pere Diaz Bou
482d7b639e Merge 'Minor improvements and cleanups in btree' from Preston Thorpe
This PR does the following:
1. Replaces a couple `RefCell`'s on the cursor and page stack with
`Cell` since the types implement `Copy`
2. Adds a `return_corrupt!` macro to handle the frequent error
3. Removed `Result` return type from cursor `record` method
4. Removed a couple clones and very minor refactoring (a few clippy
suggestions)

Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>

Closes #1075
2025-03-02 22:02:58 +01:00
Pekka Enberg
aa43cad082 Merge 'CODEOWNERS' from Pere Diaz Bou
@PThorpe92 @penberg @jussisaurio please let me know if you want any
change, this is just a simple way for me to know if a pr is made in
core, maybe I should have a `/core/translate` and `/core/storage` too.

Closes #1068
2025-03-02 20:44:41 +02:00
Pere Diaz Bou
b2a22b7f49 comment out codeowners without write access 2025-03-02 19:01:05 +01:00
Pere Diaz Bou
04720051ce remove krishvishal for now until write access granted 2025-03-02 18:53:49 +01:00
Pere Diaz Bou
4e0eaf2401 add bindings/{go,java}, simulator and extensions 2025-03-02 18:51:07 +01:00
PThorpe92
588e43c5aa Minor improvements and cleanups in btree 2025-03-01 15:48:42 -05:00
Pere Diaz Bou
489e66ee02 CODEOWNERS 2025-02-28 19:15:21 +01:00
Pekka Enberg
1de73b389e Merge ' fix usable_space calculation and wrong old pages cell count usage ' from Pere Diaz Bou
Closes #1067
2025-02-28 19:45:07 +02:00
Pere Diaz Bou
e545cc7057 fix btree_insert_fuzz_ex implementation 2025-02-28 18:21:38 +01:00
Pere Diaz Bou
bbb3252ab6 fix usable_space calculation and wrong old pages cell count usage 2025-02-28 18:19:27 +01:00
Pekka Enberg
20d618f35c Disable some failing b-tree tests until we've fixed them 2025-02-28 19:17:29 +02:00
Pekka Enberg
13750e9255 Human Rust programmers exist to keep Clippy happy 2025-02-28 19:12:12 +02:00
Pekka Enberg
b4e8afa3c7 Merge 'Implement SQLite balancing algorithm' from Pere Diaz Bou
Beep boop.
What happened you ask? I removed the dumb balancing algorithm I
implemented in favor of SQLite's implementation based on B*Tree[1] where
a page is 2/3 full instead of 1/2. It also tries to balance a page by
taking a maximum 3 pages and distributing cells evenly between them.
I've made some changes that are somewhat related:
* Moved most operations on pages out of BTreeCursor because those
operations are based on a page, not on a cursor, and it makes it easier
to test.
* Fixed `write_u16` and `read_u16` cases that didn't need a implicit
offset calculation. Added: `write_u16_no_offset` and
`read_u16_no_offset` to counter this.
* Added some tests with fuzz testing too.
* Fixed some important actions like: `compute_free_space`,
`defragment_page` and `drop_cell`.
[1] https://dl.acm.org/doi/10.1145/356770.356776

Closes #968
2025-02-28 19:10:52 +02:00
Pekka Enberg
a446b77de2 Merge 'rust_perf.yml: Remove duplicate lines from merge conflict' from Henrik Ingo
Closes #1061
2025-02-28 16:29:59 +02:00
Pekka Enberg
c5095e82a8 Merge 'Fix wrong count() result if the column specified contains a NULL' from lgualtieri75
Fixes #1064

Closes #1065
2025-02-28 16:28:08 +02:00
l.gualtieri
cf407f639e fix #1064 2025-02-27 19:47:51 +01:00
Pekka Enberg
672fe066c1 Merge 'Improve Rust bindings' from Pekka Enberg
Closes #1062
2025-02-27 11:09:51 +02:00
Pekka Enberg
50f9cc449c bindings/rust: Fix complaints about non-Sync/Send use of Arc
We probably should drop the `Rc` from `Connection` in the core, but
let's paper over it for now.
2025-02-27 10:43:58 +02:00
Pekka Enberg
08c1dce549 bindings/rust: Improve API support
Add support for Statement::query() and others to wire up more of Limbo
core to the Rust bindings.
2025-02-27 10:38:16 +02:00
Pekka Enberg
98e3bc0c0c bindings/rust: Make library thread-safe 2025-02-27 10:22:22 +02:00
Pekka Enberg
f0b29167cd bindings/rust: Fix Database::connect() signature
We don't want to consume `self`...
2025-02-27 10:10:51 +02:00
Henrik Ingo
9c170b6ed3 rust_perf.yml. Re-enable benchmarks (make bench) 2025-02-27 02:00:06 +02:00
Henrik Ingo
e6ca5b60b4 rust_perf.yml: Remove duplicate lines from merge conflict 2025-02-27 01:56:36 +02:00
Pekka Enberg
ddee76e0fd Merge 'Enable Nyrkiö on PRs and clickbench' from Henrik Ingo
- Rename push_only.yml workflow to rust_perf.yml
- Move the 'bench' task from rust.yml to rust_perf.yml
- More Nyrkio configuration options exposed:
  Team support (everyone in gh/tursodatabase can access Nyrkiö)
  Public results
  Alerting options: Comment on PR, if change detected, don't fail task
- Add Nyrkio also to analyse results from the clickbench task

Closes #959
2025-02-26 17:37:39 +02:00
Pekka Enberg
0ecbd399c8 Merge 'Remove uri/dsn parsing in Go bindings' from Preston Thorpe
Extensive uri parsing was added to core and this will be handled there.

Closes #1058
2025-02-26 17:36:48 +02:00
PThorpe92
6a919ac691 Remove uri parsing in go bindings because it has been added in core 2025-02-26 10:17:26 -05:00
Pekka Enberg
8a6d95e257 Merge 'Clean up extension python tests to use convenience CLI-test class' from Preston Thorpe
A nice python class was added in #941 for easily testing limbo's CLI
output. As the tests around extensions continue to grow, we should use
this as much as possible for non-Tcl/compatibility tests so they can
scale more easily without getting messy. I am going to add a whole bunch
of tests that will explicitly test each kind of extension both when
loaded staticly/dynamically and combined with other export types and I
want to get the existing ones migrated over first.

Closes #1048
2025-02-26 13:50:31 +02:00
Pekka Enberg
5779e3dfb2 Merge 'core: Optimize read_record() function' from Pekka Enberg
The SerialType::try_from() was pretty high up in CPU profiles so I asked
my dear friend Claude to optimize it away by using the serial type
integer value directly instead of constructing a fancy enumeration.

Closes #1057
2025-02-26 13:46:06 +02:00
Pekka Enberg
6d44ad22fd core: Optimize read_record() function
The SerialType::try_from() was pretty high up in CPU profiles so I asked
my dear friend Claude to optimize it away by using the serial type
integer value directly instead of constructing a fancy enumeration.
2025-02-26 13:31:38 +02:00
Pekka Enberg
d1fa995ac5 Merge 'core: Kill value type' from Pekka Enberg
We currently have two value types, `Value` and `OwnedValue`. The
original thinking was that `Value` is external type and `OwnedValue` is
internal type. However, this just results in unnecessary transformation
between the types as data crosses the Limbo library boundary.
Let's just follow SQLite here and consolidate on a single value type
(where `sqlite3_value` is just an alias for the internal `Mem` type).
The way this will eventually work is that we can have bunch of pre-
allocated `OwnedValue` objects in `ProgramState` and basically return a
reference to them all the way to the application itself, which extracts
the actual value.
Fixes #906

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

Closes #1056
2025-02-26 12:27:36 +02:00
Pekka Enberg
936ae307b7 core: Kill value type
We currently have two value types, `Value` and `OwnedValue`. The
original thinking was that `Value` is external type and `OwnedValue` is
internal type. However, this just results in unnecessary transformation
between the types as data crosses the Limbo library boundary.

Let's just follow SQLite here and consolidate on a single value type
(where `sqlite3_value` is just an alias for the internal `Mem` type).
The way this will eventually work is that we can have bunch of
pre-allocated `OwnedValue` objects in `ProgramState` and basically
return a reference to them all the way to the application itself, which
extracts the actual value.
2025-02-26 10:57:45 +02:00
PThorpe92
6c98cf4d82 Clean up extension python tests to use convenience CLI-test class 2025-02-25 20:36:35 -05:00
Pekka Enberg
fe440b7b34 Merge 'Fix casting text to integer to match SQLite' from Preston Thorpe
```console
thread 'fuzz::tests::logical_expression_fuzz_run' panicked at tests\integration\fuzz\mod.rs:818:13:
assertion `left == right` failed: query: SELECT  ( ( 3622873 || -8851250 ) * ( ( ( -124 ) + ( -5792536 ) ) ) ) = ( 179434259456392 < 65481085924370 ), limbo: [[Integer(1)]], sqlite: [[Integer(0)]]
  left: [[Integer(1)]]
 right: [[Integer(0)]]
```
This and a few other failing fuzzing tests were due to incorrectly
parsing numerics from strings. Some of our casting was done properly,
but it wasn't being applied to all cases where the behavior was needed.
It was also attempting to parse a string[0..N] N times until
`string[0..N].parse()` would no longer succeed. This searches for the
index of the first illegal character and parses the resulting slice
once.
Tests were added for some of the edgecases that were previously failing.
This PR also adds a macro in vdbe/insn.rs that allows for a bit of
cleanup and reduces some matching.

Closes #1053
2025-02-25 15:44:37 +02:00
Pekka Enberg
7f2525ac27 Merge 'Implement create virtual table using vtab modules, more work on virtual tables' from Preston Thorpe
This PR started out as one to improve the API of extensions but I ended
up building on top of this quite a bit and it just kept going. Sorry
this one is so large but there wasn't really a good stopping point, as
it kept leaving stuff in broken states.
**VCreate**: Support for `CREATE VIRTUAL TABLE t USING vtab_module`
**VUpdate**: Support for `INSERT` and `DELETE` methods on virtual
tables.
Sqlite uses `xUpdate` function with the `VUpdate` opcode to handle all
insert/update/delete functionality in virtual tables..
have to just document that:
```
if args[0] == NULL:  INSERT args[1] the values in args[2..]

if args[1] == NULL: DELETE args[0]

if args[0] != NULL && len(args) > 2: Update values=args[2..]  rowid=args[0]
```
I know I asked @jussisaurio on discord about this already, but it just
sucked so bad that I added some internal translation so we could expose
a [nice API](https://github.com/tursodatabase/limbo/pull/996/files#diff-
3e8f8a660b11786745b48b528222d11671e9f19fa00a032a4eefb5412e8200d1R54) and
handle the logic ourselves while keeping with sqlite's opcodes.
I'll change it back if I have to, I just thought it was genuinely awful
to have to rely on comments to explain all that to extension authors.
The included extension is not meant to be a legitimately useful one, it
is there for testing purposes. I did something similar in #960 using a
test extension, so I figure when they are both merged, I will go back
and combine them into one since you can do many kinds at once, and that
way it will reduce the amount of crates and therefore compile time.
1. Remaining opcodes.
2. `UPDATE` (when we support the syntax)
3. `xConnect` - expose API for a DB connection to a vtab so it can
perform arbitrary queries.

Closes #996
2025-02-25 15:31:12 +02:00
PThorpe92
b31363aecb More improvements/cleanups to vdbe around casting 2025-02-24 21:31:26 -05:00
PThorpe92
6d55cdba3b Remove allocations from numeric text casting, cleanups 2025-02-24 12:30:38 -05:00
PThorpe92
7e94a152a5 Consolidate code to parse numerics from text 2025-02-24 11:21:25 -05:00
PThorpe92
66f0835d51 Add tests for corrected behavior around casting 2025-02-24 11:21:25 -05:00
PThorpe92
8070e51e26 Fix vdbe casting and rounding issues 2025-02-24 11:21:22 -05:00
PThorpe92
8f27a5fc92 Fix (fuzzing tests) casting text to integer to match sqlite behavior 2025-02-24 11:13:25 -05:00
Pekka Enberg
eb6019b453 cargo fmt 2025-02-24 17:39:21 +02:00
Pekka Enberg
92d19e95e7 Attempt to fix stale bot not to close issues 2025-02-24 17:22:20 +02:00
Pekka Enberg
2b002c4875 Merge 'extensions/time: normalize offset_sec' from meteorgan
normalize `offset_sec` in case it's outside its usual range.
It's done in a similar way in `sqlean`:
https://github.com/nalgeon/sqlean/blob/b9ba442909acea14eb0e3cd149d1448f8
4da1f9c/src/time/time.c#L300-L306

Closes #1041
2025-02-24 11:32:44 +02:00
Pekka Enberg
16306ee1f4 Merge 'Modify the LIKE function to work with all types' from Mohamed Hossam
This PR fixes
[#1040](https://github.com/tursodatabase/limbo/issues/1040) and modifies
the `LIKE` function in the VDBE to work on expressions of all types like
SQLite.
Looking at how SQLite handles this, it gets the text value of the
expression regardless of its affinity. I used `exec_cast(exp, "TEXT")`
to achieve the same effect. Since most `LIKE` queries will probably be
done on `TEXT` expressions, I avoid casting the expression if it's
already `TEXT`. If either of the expressions was `NULL`, SQLite returns
nothing i.e. `NULL`. I also changed the unreachable arm message from
`Like on non-text registers` to `Like failed`.
The following queries produced the same results in Limbo:
```
SQLite version 3.46.1 2024-08-13 09:16:08 (UTF-16 console I/O)
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> CREATE TABLE tbl (n NULL, i INTEGER, r REAL, t TEXT, b BLOB);
sqlite> INSERT INTO tbl VALUES(NULL,1,2.0,'a',X'0500');
sqlite> SELECT * FROM tbl;
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE n LIKE NULL;
sqlite> SELECT * FROM tbl WHERE n LIKE 'NULL';
sqlite> SELECT * FROM tbl WHERE n LIKE 1;
sqlite> SELECT * FROM tbl WHERE n LIKE 2.0;
sqlite> SELECT * FROM tbl WHERE n LIKE x'0500';
sqlite>
sqlite> SELECT * FROM tbl WHERE i LIKE NULL;
sqlite> SELECT * FROM tbl WHERE i LIKE 1;
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE i LIKE '1';
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE i LIKE 2.0;
sqlite> SELECT * FROM tbl WHERE i LIKE 1.0;
sqlite> SELECT * FROM tbl WHERE i LIKE x'0500';
sqlite>
sqlite> SELECT * FROM tbl WHERE r LIKE NULL;
sqlite> SELECT * FROM tbl WHERE r LIKE 2;
sqlite> SELECT * FROM tbl WHERE r LIKE 2.0;
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE r LIKE '2.0';
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE r LIKE 'a';
sqlite> SELECT * FROM tbl WHERE r LIKE x'0500';
sqlite>
sqlite> SELECT * FROM tbl WHERE t LIKE NULL;
sqlite> SELECT * FROM tbl WHERE t LIKE 1;
sqlite> SELECT * FROM tbl WHERE t LIKE 2.0;
sqlite> SELECT * FROM tbl WHERE t LIKE 'a';
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE t LIKE x'0500';
sqlite>
sqlite> SELECT * FROM tbl WHERE b LIKE NULL;
sqlite> SELECT * FROM tbl WHERE b LIKE 1;
sqlite> SELECT * FROM tbl WHERE b LIKE 2.0;
sqlite> SELECT * FROM tbl WHERE b LIKE 'a';
sqlite> SELECT * FROM tbl WHERE b LIKE x'0500';
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE b LIKE 'x''0500''';
sqlite> SELECT * FROM tbl WHERE b LIKE '♣';
sqlite>
sqlite> SELECT * FROM tbl WHERE 1 LIKE 1;
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE 2.0 LIKE 2.0;
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE 2.0 LIKE '2.0';
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE '2.0' LIKE 2.0;
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE '123.45' LIKE 123.45;
|1|2.0|a|♣
sqlite> SELECT * FROM tbl WHERE NULL LIKE NULL;
sqlite> SELECT * FROM tbl WHERE x'0500' LIKE x'0500';
|1|2.0|a|♣
sqlite> SELECT typeof(n), typeof(i), typeof(r), typeof(t), typeof(b) FROM tbl;
null|integer|real|text|blob
```
Though, these queries are very basic, and more testing could be done.

Closes #1044
2025-02-24 11:27:02 +02:00