Commit Graph

58 Commits

Author SHA1 Message Date
Jussi Saurio
37097e01ae GROUP BY: refactor logic to support cases where no sorting is needed 2025-05-08 12:39:26 +03:00
Jussi Saurio
330fedbc2f Add notion of join ordering to plan + make determining where to eval expr dynamic always 2025-05-03 15:32:06 +03:00
Jussi Saurio
029e5eddde Fix existing resolve_label() calls to work with new system 2025-04-24 11:05:21 +03:00
Jussi Saurio
a7488496d5 expr.is_nonnull(): return true if col.primary_key || col.notnull 2025-04-23 18:10:33 +03:00
Jussi Saurio
af21f60887 translate/main_loop: create autoindex when index.ephemeral=true 2025-04-21 14:59:13 +03:00
Jussi Saurio
83c509a613 Fix bug: left join null flag not being cleared
In left joins, even if the join condition is not matched, the system
must emit a row for every row of the outer table:

-- this must return t1.count() rows, with NULLs for all columns of t2
SELECT * FROM t1 LEFT JOIN t2 ON FALSE;

Our logic for clearing the null flag was to do it in Next/Prev. However,
this is problematic for a few reasons:

- If the inner table of the left join is using SeekRowid, then Next/Prev
  is never called on its cursor, so the null flag doesn't get cleared.
- If the inner table of the left join is using a non-covering index seek,
  i.e. it iterates its rows using an index, but seeks to the main table
  to fetch data, then Next/Prev is never called on the main table, and the
  main table's null flag doesn't get cleared.

What this results in is NULL values incorrectly being emitted for the
inner table after the first correct NULL row, since the null flag is
correctly set to true, but never cleared.

This PR fixes the issue by clearing the null flag whenever seek() is
invoked on the cursor. Hence, the null flag is now cleared on:

- next()
- prev()
- seek()
2025-04-19 13:56:52 +03:00
Jussi Saurio
6c73db6fd3 feat: use covering indexes whenever possible 2025-04-18 15:13:09 +03:00
PThorpe92
d02900294e Remove 2nd shell in vtab tests, fix expr translation in main loop 2025-04-17 14:01:45 -04:00
PThorpe92
a25a02efe1 Improve xBestIndex call site and allow for proper handling of join and where constraints 2025-04-17 14:01:45 -04:00
PThorpe92
e17fd7edc4 Add comments and address PR review 2025-04-17 14:01:44 -04:00
PThorpe92
528a9b6c7e Clean up allocations in main loop and fix ext tests 2025-04-17 14:01:44 -04:00
PThorpe92
6f2c6c6a61 Actually skip omitted predicates in open loop 2025-04-17 14:01:44 -04:00
PThorpe92
de27c2fe4c Properly handle pushing predicates for query optimization from xBestIndex 2025-04-17 14:01:37 -04:00
PThorpe92
853af16946 Implement xBestIndex for virtual table api to improve query planning 2025-04-17 13:53:27 -04:00
Jussi Saurio
1189b7a288 codegen: add support for descending indexes 2025-04-16 13:58:12 +03:00
Jussi Saurio
d286a56e15 refactor: fold Async/Await insns into a single instruction 2025-04-14 09:40:20 +03:00
Jussi Saurio
6bea4de30f Check that index seek key members are not null 2025-04-11 17:22:46 +03:00
Jussi Saurio
457bded14d optimizer: refactor optimizer to support multicolumn index scans 2025-04-10 15:53:02 +03:00
PThorpe92
f223e66c82 Remove unused mut and fix merge conflict issues 2025-04-09 11:15:04 -04:00
PThorpe92
b685086cad Support UPDATE for virtual tables 2025-04-09 11:06:41 -04:00
Jussi Saurio
a706b7160a planner: support index backwards seeks and iteration 2025-04-09 10:14:29 +03:00
Jussi Saurio
c19e4fc69c Merge 'Aggregation without group by produces incorrect results for scalars' from Ihor Andrianov
Closes #954
Before:
<img width="669" alt="Знімок екрана 2025-03-27 о 21 49 19"
src="https://github.com/user-
attachments/assets/d005e690-7dab-41e5-bc03-b574cade3965" />
After:
<img width="676" alt="Знімок екрана 2025-03-27 о 21 49 44"
src="https://github.com/user-
attachments/assets/1f4eb6bf-a238-496e-9fa4-32382799ef86" />
SQLite:
<img width="656" alt="Знімок екрана 2025-03-27 о 21 50 04"
src="https://github.com/user-
attachments/assets/3eca184e-6ea5-47c1-824f-51d11256a7af" />

Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #1198
2025-04-06 20:00:30 +03:00
Preston Thorpe
62e0a137dd Merge branch 'main' into update_offset 2025-04-06 12:34:23 -04:00
PThorpe92
e020ba3dfe Add enum for interpreting a value as a register or literal for insns 2025-04-05 11:19:07 -04:00
PThorpe92
b0016a0ee2 Support create index with SeekEnd and IdxCreate opcode functionality 2025-04-05 11:15:36 -04:00
Ihor Andrianov
0c9464e3fc reduce vec allocations, add comments for magic ifs 2025-04-05 15:15:10 +03:00
PThorpe92
f6a64a7b15 Support OFFSET clause for LIMIT in UPDATE queries 2025-04-04 12:35:30 -04:00
Ihor Andrianov
34a132fcd3 fix output when group by is not part of resulting set 2025-04-03 22:28:13 +03:00
Ihor Andrianov
91ceab1626 improve naming and add comments for context 2025-04-03 22:28:13 +03:00
Ihor Andrianov
2bcdd4e404 non group by cols are displayed in group by agg statements 2025-04-03 22:28:12 +03:00
Ihor Andrianov
4fd1dcdc73 small refine 2025-04-03 22:28:11 +03:00
Ihor Andrianov
36fe859d7d create if only if non aggregate columns present 2025-04-03 22:28:11 +03:00
Ihor Andrianov
352fa6fd34 cargo fmt 2025-04-03 22:28:11 +03:00
Ihor Andrianov
b47c214a5e fix aggregation functions without group by 2025-04-03 22:28:10 +03:00
PThorpe92
3fe14f37a5 Create plan for Update queries 2025-03-30 12:15:24 -04: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
8b5772fe1c Implement VUpdate (insert/delete for virtual tables 2025-02-17 20:44:44 -05:00
PThorpe92
9c8083231c Implement create virtual table and VUpdate opcode 2025-02-17 20:44:44 -05:00
Jussi Saurio
8e5499e5ed Fix not evaling constant conditions when no tables in query
We were not evaluating constant conditions (e.g '1 IS NULL')
when there were no tables referenced in the query, because
our WHERE term evaluation was based on "during which loop"
to evaluate them. However, when there are no tables, there are
no loops, so they were never evaluated.
2025-02-17 13:10:27 +02:00
Pekka Enberg
ac54c35f92 Switch to workspace dependencies
...makes it easier to specify a version, which is needed for `cargo publish`.
2025-02-12 17:28:04 +02:00
Aarni Koskela
eaea02c567 Fix a handful of typos 2025-02-09 18:08:29 +02:00
Jussi Saurio
d5f58f5fea Add quickcheck tests for generate_series() and refine implementation 2025-02-06 18:36:21 +02:00
PThorpe92
ae88d51e6f Remove TableReferenceType enum to clean up planner 2025-02-06 09:15:39 -05:00
Jussi Saurio
f5f77c0bd1 Initial virtual table implementation 2025-02-06 07:51:50 -05:00
Jussi Saurio
750a9c6463 assertions and small cleanups 2025-02-03 13:08:13 +02:00
Jussi Saurio
f2dab8499d preallocate loop metadata according to table/column count and prefer vec over hashmap 2025-02-03 12:51:24 +02:00
Pekka Enberg
662d629666 Rename JoinAwareConditionExpr to WhereTerm
We transform all JOIN conditions into WHERE clause terms in the query
planner. The JoinAwareConditionExpr name tries to make that point, but I
think it makes things more confusing. Let's call it WhereTerm (suggested
by Jussi).
2025-02-03 07:46:51 +02:00
Jussi Saurio
89fba9305a main_loop.rs: use iteration instead of recursion
Now that we do not have a tree of SourceOperators but rather
a Vec of TableReferences, we can just use loops instead of
recursion for handling the main query loop.
2025-02-02 10:18:13 +02:00
Glauber Costa
c04260ab54 rename Flags to a less ambiguous name
Those Flags in SQLite are global, but it doesn't mean it has to be
the case for us as well.
2025-02-01 08:09:06 -05:00
Glauber Costa
96987db6ca implement is and is not where constraints
The main difference between = and != is how null values are handled.
SQLite passes a flag "NULLEQ" to Eq and Ne to disambiguate that.
In the presence of that flag, NULL = NULL.

Some prep work is done to make sure we can pass a flag instead of a
boolean to Eq and Ne. I looked into the bitflags crate but got a bit
scared with the list of dependencies.
2025-01-31 23:01:49 -05:00