Commit Graph

2265 Commits

Author SHA1 Message Date
Jussi Saurio
3f9bdbdf14 btree: use binary search in move_to() for table btrees 2025-04-18 16:11:36 +03:00
Jussi Saurio
1ccc321030 Merge 'Feat: Covering indexes' from Jussi Saurio
Closes #364
Covering indexes mean being able to read all the necessary data from an
index instead of using the underlying table at all. This PR adds that
functionality.
This PR can be reviewed commit-by-commit as the first commits are
enablers for the actual covering index usage functionality
Example of a scan where covering index can be used:
```sql
limbo> .schema
CREATE TABLE t(a,b,c,d,e);
CREATE INDEX abc ON t (a,b,c);
limbo> explain select b+1,concat(a, c) from t;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     12    0                    0   Start at 12
1     OpenRead           0     3     0                    0   table=abc, root=3
2     Rewind             0     11    0                    0   Rewind abc
3       Column           0     1     3                    0   r[3]=abc.b
4       Integer          1     4     0                    0   r[4]=1
5       Add              3     4     1                    0   r[1]=r[3]+r[4]
6       Column           0     0     5                    0   r[5]=abc.a
7       Column           0     2     6                    0   r[6]=abc.c
8       Function         0     5     2     concat         0   r[2]=func(r[5..6])
9       ResultRow        1     2     0                    0   output=r[1..2]
10    Next               0     3     0                    0
11    Halt               0     0     0                    0
12    Transaction        0     0     0                    0   write=false
13    Goto               0     1     0                    0
```
Example of a scan where it can't be used:
```sql
limbo> .schema
CREATE TABLE t(a,b,c,d,e);
CREATE INDEX abc ON t (a,b,c);
limbo> explain select a,b,c,d from t limit 5;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     11    0                    0   Start at 11
1     OpenRead           0     2     0                    0   table=t, root=2
2     Rewind             0     10    0                    0   Rewind t
3       Column           0     0     4                    0   r[4]=t.a
4       Column           0     1     5                    0   r[5]=t.b
5       Column           0     2     6                    0   r[6]=t.c
6       Column           0     3     7                    0   r[7]=t.d
7       ResultRow        4     4     0                    0   output=r[4..7]
8       DecrJumpZero     1     10    0                    0   if (--r[1]==0) goto 10
9     Next               0     3     0                    0
10    Halt               0     0     0                    0
11    Transaction        0     0     0                    0   write=false
12    Integer            5     1     0                    0   r[1]=5
13    Integer            0     2     0                    0   r[2]=0
14    OffsetLimit        1     3     2                    0   if r[1]>0 then r[3]=r[1]+max(0,r[2]) else r[3]=(-1)
15    Goto               0     1     0                    0
```

Closes #1351
2025-04-18 15:27:27 +03:00
Jussi Saurio
9d553c50cc Merge 'allow index entry delete' from Pere Diaz Bou
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>

Closes #1341
2025-04-18 15:26:05 +03:00
Jussi Saurio
bf2e198a57 Merge 'Fix out of bounds access on parse_numeric_str' from Levy A.
Fixes #1361.

Closes #1362
2025-04-18 15:24:37 +03:00
Jussi Saurio
6c73db6fd3 feat: use covering indexes whenever possible 2025-04-18 15:13:09 +03:00
Jussi Saurio
5b71d3a3da eliminate_unnecessary_orderby: add edge case handling 2025-04-18 15:12:06 +03:00
Jussi Saurio
40d880c3b0 TableReference: add resolve_cursors() method 2025-04-18 15:12:06 +03:00
Jussi Saurio
d5a6553e63 TableReference: add open_cursors() 2025-04-18 15:12:06 +03:00
Jussi Saurio
4ab4a3f6c3 TableReference: add index_is_covering() and utilizes_covering_index() 2025-04-18 15:12:06 +03:00
Levy A.
5fd2ed0bae fix: handle empty case 2025-04-17 20:20:57 -03:00
Levy A.
32d59b8c78 refactor+fix: using a more robust pattern matching approach 2025-04-17 20:08:05 -03:00
Jussi Saurio
48bee334cf Merge 'Support xBestIndex in vtab API' from Preston Thorpe
closes #1185
## The Problem:
The underlying schema of virtual tables is hidden from the query
planner, and it currently has no way of optimizing select queries with
vtab table refs by using indexes or removing non-constant predicates.
All vtabs are currently rewound completely each time and any conditional
filtering is done in the vdbe layer instead of in the `VFilter`.
## The solution:
Add xBestIndex to the vtab module API to let extensions return some
`IndexInfo` that will allow the query planner to make better
optimizations and possibly omit conditionals
## Examples:
table `t`: vtab: (key, value)
table `t2`: table: (a,b)
### Join where vtab is outer table:
![image](https://github.com/user-
attachments/assets/87f4233f-7d32-4a5e-8f95-4bebd3549304)
Properly pushes predicate to VFilter, which receives the idx_str
`key_eq` arg, telling it that there is a useable where clause on the key
"index"
### Join where vtab is inner table:
![image](https://github.com/user-
attachments/assets/f8fcf6d3-42bc-41a3-ad86-16e497ec6056)
Constraint is not sent because it is marked as unusable
### Where clause on "indexed" column:
![image](https://github.com/user-
attachments/assets/8817cc45-177c-404d-8323-4d33180e280c)
Pushed down and the predicate is omitted from the VDBE layer.
### Where clause on regular column:
![image](https://github.com/user-
attachments/assets/85595c7f-920f-4047-8388-a7dddd01778c)
No idx info received from BestIndex, VDBE handles conditional.
## TODO:
OrderBy info needs to be sent to xBestIndex and its not in a great
position in `open_loop` currently

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

Closes #1264
2025-04-17 23:17:01 +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
d53c60e071 Prevent double allocations for VFilter args in vdbe 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
7d271edf8a Remove unused function in core/util.rs 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
0f34a813ff Add can_pushdown_predicate fn to evaluate ast expressions for constness 2025-04-17 13:53:28 -04:00
PThorpe92
853af16946 Implement xBestIndex for virtual table api to improve query planning 2025-04-17 13:53:27 -04:00
Pere Diaz Bou
262c630c16 fix validation with overflow cells 2025-04-17 18:28:42 +02:00
Jussi Saurio
1189b7a288 codegen: add support for descending indexes 2025-04-16 13:58:12 +03:00
Jussi Saurio
b1073da4a5 btree: add support fo descending indexes 2025-04-16 13:58:12 +03:00
Jussi Saurio
af09025088 schema: keep track of primary key column sort order 2025-04-16 13:58:12 +03:00
Jussi Saurio
1d9c6d6981 Merge 'btree: move some blocks of code to more reasonable places' from Jussi Saurio
Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #1343
2025-04-16 11:13:15 +03:00
Jussi Saurio
913367409e Merge 'Parse hex integers 2' from Anton Harniakou
Continuation of #1329

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

Closes #1347
2025-04-16 11:13:01 +03:00
Anton Harniakou
8c797a9bd1 Use eq_ignore_ascii_case to compare strings 2025-04-15 21:48:53 +03:00
Jussi Saurio
13a703d636 Merge 'Add BeginSubrtn, NotFound and Affinity bytecodes' from Diego Reis
I'm working on an optimization of `WHERE .. IN (..)` statements that
requires these bytecodes.
```sh
sqlite> explain select * from users where first_name in ('alice', 'bob', 'charlie');
addr  opcode         p1    p2    p3    p4             p5  comment
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     35    0                    0   Start at 35
1     OpenRead       0     2     0     10             0   root=2 iDb=0; users
2     Rewind         0     34    0                    0
3       Noop           0     0     0                    0   begin IN expr
4       BeginSubrtn    0     1     0                    0   r[1]=NULL <---- Here
5         Once           0     17    0                    0
6         OpenEphemeral  1     1     0     k(1,B)         0   nColumn=1; RHS of IN operator
7         String8        0     2     0     alice          0   r[2]='alice'
8         MakeRecord     2     1     3     B              0   r[3]=mkrec(r[2])
9         IdxInsert      1     3     2     1              0   key=r[3]
10        String8        0     2     0     bob            0   r[2]='bob'
11        MakeRecord     2     1     3     B              0   r[3]=mkrec(r[2])
12        IdxInsert      1     3     2     1              0   key=r[3]
13        String8        0     2     0     charlie        0   r[2]='charlie'
14        MakeRecord     2     1     3     B              0   r[3]=mkrec(r[2])
15        IdxInsert      1     3     2     1              0   key=r[3]
16        NullRow        1     0     0                    0
17      Return         1     5     1                    0
18      Column         0     1     4                    0   r[4]= cursor 0 column 1
19      IsNull         4     33    0                    0   if r[4]==NULL goto 33
20      Affinity       4     1     0     B              0   affinity(r[4]) <---- Here
21      NotFound       1     33    4     1              0   key=r[4]; end IN expr <---- Here
22      Rowid          0     5     0                    0   r[5]=users.rowid
23      Column         0     1     6                    0   r[6]= cursor 0 column 1
24      Column         0     2     7                    0   r[7]= cursor 0 column 2
25      Column         0     3     8                    0   r[8]= cursor 0 column 3
26      Column         0     4     9                    0   r[9]= cursor 0 column 4
27      Column         0     5     10                   0   r[10]= cursor 0 column 5
28      Column         0     6     11                   0   r[11]= cursor 0 column 6
29      Column         0     7     12                   0   r[12]= cursor 0 column 7
30      Column         0     8     13                   0   r[13]= cursor 0 column 8
31      Column         0     9     14                   0   r[14]= cursor 0 column 9
32      ResultRow      5     10    0                    0   output=r[5..14]
33    Next           0     3     0                    1
34    Halt           0     0     0                    0
35    Transaction    0     0     3     0              1   usesStmtJournal=0
36    Goto           0     1     0                    0
```
EDIT: [Found](https://sqlite.org/opcode.html#Found) and
[NoConflict](https://sqlite.org/opcode.html#NoConflict) can be easily
derived from `NotFound` but I wanted to be concise, I could do it in
another PR :)

Closes #1345
2025-04-15 20:25:55 +03:00
Diego Reis
2cc492844e Improve NotFound's docs clarity 2025-04-15 13:36:45 -03:00
Diego Reis
58efb90467 core: Add Affinity bytecode
Apply affinities to a range of P2 registers starting with P1.

P4 is a string that is P2 characters long. The N-th character of the string indicates the column affinity that should be used for the N-th memory cell in the range.
2025-04-15 09:52:04 -03:00
Diego Reis
c5161311fc core/vdbe: Add NotFound bytecode
If P4==0 then register P3 holds a blob constructed by MakeRecord. If P4>0 then register P3 is the first of P4 registers that form an unpacked record.

Cursor P1 is on an index btree. If the record identified by P3 and P4 is not the prefix of any entry in P1 then a jump is made to P2. If P1 does contain an entry whose prefix matches the P3/P4 record then control falls through to the next instruction and P1 is left pointing at the matching entry.

This operation leaves the cursor in a state where it cannot be advanced in either direction. In other words, the Next and Prev opcodes do not work after this operation.
2025-04-15 09:52:04 -03:00
Diego Reis
825aeb3f83 core/vdbe: Add BeginSubrtn bytecode
Basically it does the very same thing of Null, but has a different name to differentiate its usage.
2025-04-15 09:52:04 -03:00
Jussi Saurio
1fe1f0ebba ProgramBuilder: add resolve_cursor_id_safe() which doesn't unwrap 2025-04-15 15:13:39 +03:00
Jussi Saurio
5a1cfb7d15 Add ColumnUsedMask struct to TableReference to track columns referenced in query 2025-04-15 15:13:31 +03:00
Jussi Saurio
72dac59813 Operation: add method index() to retrieve used index, if any 2025-04-15 15:13:24 +03:00
Jussi Saurio
e299a0e77e vdbe: add Insn::IdxRowId 2025-04-15 15:13:18 +03:00
Jussi Saurio
a467060e1c Index: add method column_table_pos_to_index_pos() 2025-04-15 14:47:56 +03:00
Jussi Saurio
198aedb042 Refactor: add 'pos_in_table' to IndexColumn for easier lookup 2025-04-15 14:47:49 +03:00
Jussi Saurio
cc8f89e8e0 Merge 'Fix Unary Negate Operation on Blobs' from Pedro Muniz
Fixing stuff that appears in Fuzz testing.
# Before
<img width="668" alt="image" src="https://github.com/user-
attachments/assets/f1f59b63-5173-4932-98b2-774803cb8a8e" />
```
limbo> EXPLAIN SELECT -x'';
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     5     0                    0   Start at 5
1     Blob               0     2     0                    0   r[2]= (len=0)
2     Multiply           3     2     1                    0   r[1]=r[3]*r[2]
3     ResultRow          1     1     0                    0   output=r[1]
4     Halt               0     0     0                    0
5     Integer            -1    3     0                    0   r[3]=-1
6     Goto               0     1     0                    0
```
# After
<img width="175" alt="image" src="https://github.com/user-
attachments/assets/9f361dc3-b243-4d69-bdd2-d6a2bbc0bf20" />
```
limbo> EXPLAIN SELECT -x'';
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     5     0                    0   Start at 5
1     Blob               0     2     0                    0   r[2]= (len=0)
2     Subtract           3     2     1                    0   r[1]=r[3]-r[2]
3     ResultRow          1     1     0                    0   output=r[1]
4     Halt               0     0     0                    0
5     Integer            0     3     0                    0   r[3]=0
6     Goto               0     1     0                    0
```
# Sqlite
```
sqlite> SELECT -x'';
0
sqlite> EXPLAIN SELECT -x'';
addr  opcode         p1    p2    p3    p4             p5  comment
----  -------------  ----  ----  ----  -------------  --  -------------
0     Init           0     4     0                    0   Start at 4
1     Subtract       3     2     1                    0   r[1]=r[2]-r[3]
2     ResultRow      1     1     0                    0   output=r[1]
3     Halt           0     0     0                    0
4     Integer        0     2     0                    0   r[2]=0
5     Blob           0     3     0                    0   r[3]= (len=0)
6     Goto           0     1     0                    0
```

Closes #1333
2025-04-15 14:42:54 +03:00
Jussi Saurio
6463448fdc Merge 'Fix incompatibility AND Operation' from Pedro Muniz
Sqlite reference implementation: https://github.com/sqlite/sqlite/blob/8
37dc09bce7de8971c7488b70cf5da93c60fbed0/src/vdbe.c#L2558
We did not support blobs before and our ordering of the match statements
were incorrect when one of the arguments was NULL

Closes #1337
2025-04-15 14:37:29 +03:00
Jussi Saurio
5a38b38e71 Merge 'Feature: VDestroy for Dropping Virtual Tables' from Pedro Muniz
Reviewed-by: Preston Thorpe (@PThorpe92)

Closes #1274
2025-04-15 14:34:30 +03:00
Jussi Saurio
e0e031d8af Merge 'Fix two issues with indexes' from Jussi Saurio
Fixes #1298
- Fixes Limbo trying to use an index using a WHERE clause constraint
that refers to the same table on both sides, e.g. `WHERE t.x = t.x`
- Fixes not using indexes when the relevant expression is paren wrapped,
e.g.
    - `SELECT * FROM t WHERE (indexcol) > 5`
    - `SELECT * FROM t WHERE (indexcol > 5)`
- Changes existing table logical expr fuzz test to have primary keys
(which helped me find both issues above)

Closes #1300
2025-04-15 14:00:58 +03:00
Pere Diaz Bou
0518107443 Merge 'Feat balance shallower' from Lâm Hoàng Phúc
Fixed is_empty assertion in #1203 , but simulator still has error.
```sh
[ERROR] error Internal error: select '(engrossing_berger < X'6566651A3C70278D4E200657551D8071A1' AND competitive_petit > 1236742147.9451914)' should return no values for table 'super_becky'
```

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

Closes #1308
2025-04-15 11:02:31 +02:00
Pere Diaz Bou
aece4e5442 use seek instead of move_to in post balance delete 2025-04-15 10:59:49 +02:00
Anton Harniakou
0cebeef2ff Support hex integers beginning with uppercase notation like 0Xfff or 0XFFF 2025-04-14 21:23:04 +03:00
Anton Harniakou
3c06ddadde Parse hex integers in unary operators
Unary operators ~ and - should work with hex integers
2025-04-14 21:13:39 +03:00
pedrocarlo
e1ddf5ffcc Fix Unary Negate Operation on Blobs 2025-04-14 12:05:00 -03:00
Jussi Saurio
5628cc27a6 btree: move allocate_overflow_page to Pager impl 2025-04-14 15:25:15 +03:00