Commit Graph

88 Commits

Author SHA1 Message Date
pedrocarlo
9f726dbe62 simplify simple count detection 2025-05-10 22:36:43 -03:00
pedrocarlo
e9b1631d3c fix is_simple_count detection 2025-05-10 22:23:01 -03:00
Pekka Enberg
97ad25c506 Merge 'Initial implementation of ALTER TABLE RENAME' from Levy A.
- [x] `ALTER TABLE _ RENAME TO _`

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

Closes #1456
2025-05-10 07:57:42 +03:00
Levy A.
023a116b0d feat: initial implementation of ALTER TABLE
only supporting renaming tables
2025-05-08 09:24:56 -03:00
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
306e097950 Merge 'Fix bug: we cant remove order by terms from the head of the list' from Jussi Saurio
we had an incorrect optimization in `eliminate_orderby_like_groupby()`
where it could remove e.g. the first term of the ORDER BY if it matched
the first GROUP BY term and the result set was naturally ordered by that
term. this is invalid. see e.g.:
```sql
main branch - BAD: removes the `ORDER BY id` term because the results are naturally ordered by id.
However, this results in sorting the entire thing by last name only!

limbo> select id, last_name, count(1) from users GROUP BY 1,2 order by id, last_name desc limit 3;
┌──────┬───────────┬───────────┐
│ id   │ last_name │ count (1) │
├──────┼───────────┼───────────┤
│ 6235 │ Zuniga    │         1 │
├──────┼───────────┼───────────┤
│ 8043 │ Zuniga    │         1 │
├──────┼───────────┼───────────┤
│  944 │ Zimmerman │         1 │
└──────┴───────────┴───────────┘

after fix - GOOD:

limbo> select id, last_name, count(1) from users GROUP BY 1,2 order by id, last_name desc limit 3;
┌────┬───────────┬───────────┐
│ id │ last_name │ count (1) │
├────┼───────────┼───────────┤
│  1 │ Foster    │         1 │
├────┼───────────┼───────────┤
│  2 │ Salazar   │         1 │
├────┼───────────┼───────────┤
│  3 │ Perry     │         1 │
└────┴───────────┴───────────┘

I also refactored sorters to always use the ast `SortOrder` instead of boolean vectors, and use the `compare_immutable()` utility we use inside btrees too.

Closes #1365
2025-05-03 12:48:08 +03:00
Pere Diaz Bou
64a12ed887 update index on indexed columns
Previously columns that were indexed were updated only in the
BtreeTable, but not on Index table. This commit basically enables
updates on indexes too if they are needed.
2025-05-01 11:16:29 +03:00
Pere Diaz Bou
63a94e7c62 Merge 'Emit IdxDelete instruction and some fixes on seek after deletion' from Pere Diaz Bou
Previously `DELETE FROM ...` only emitted deletes for main table, but
this is incorrect as we want to remove entries from index tables as
well.

Closes #1383
2025-04-28 09:13:54 +03:00
Pere Diaz Bou
b7970a286d implement IdxDelete
clippy

revert op_idx_ge changes

fmt

fmt again

rever op_idx_gt changes
2025-04-24 16:23:34 +02:00
Jussi Saurio
3798b4aa8b use SortOrder in sorters always 2025-04-24 10:34:06 +03:00
Pekka Enberg
beaccae664 Merge 'Create an automatic ephemeral index when a nested table scan would otherwise be selected' from Jussi Saurio
Closes #747
- Creates an automatic ephemeral (in-memory) index on the right-side
table of a join if otherwise a nested table scan would be selected.
- This behavior is not hardcoded; instead this PR introduces a (quite
dumb) cost estimator that naturally deincentivizes building ephemeral
indexes where they don't make sense (e.g. the outermost table). I will
probably build this estimator to be smarter in the future when working
on join reordering optimizations
### Example bytecode plans and runtimes (note that this is debug mode)
Example query with no persistent indexes to choose from. Without
ephemeral index it's a nested scan:
```sql
limbo> explain select * from t1 natural join t2;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     13    0                    0   Start at 13
1     OpenRead           0     2     0                    0   table=t1, root=2
2     OpenRead           1     3     0                    0   table=t2, root=3
3     Rewind             0     12    0                    0   Rewind t1
4       Rewind           1     11    0                    0   Rewind t2
5         Column         0     0     2                    0   r[2]=t1.a
6         Column         1     0     3                    0   r[3]=t2.a
7         Ne             2     3     10                   0   if r[2]!=r[3] goto 10
8         Column         0     0     1                    0   r[1]=t1.a
9         ResultRow      1     1     0                    0   output=r[1]
10      Next             1     5     0                    0
11    Next               0     4     0                    0
12    Halt               0     0     0                    0
13    Transaction        0     0     0                    0   write=false
14    Goto               0     1     0                    0

limbo> .timer on
limbo> select * from t1 natural join t2;
┌───┐
│ a │
├───┤
└───┘
Command stats:
----------------------------
total: 953 ms (this includes parsing/coloring of cli app)
```
Same query with autoindexing enabled:
```sql
limbo> explain select * from t1 natural join t2;
addr  opcode             p1    p2    p3    p4             p5  comment
----  -----------------  ----  ----  ----  -------------  --  -------
0     Init               0     22    0                    0   Start at 22
1     OpenRead           0     2     0                    0   table=t1, root=2
2     OpenRead           1     3     0                    0   table=t2, root=3
3     Rewind             0     21    0                    0   Rewind t1
4       Once             12    0     0                    0   goto 12 # execute block 5-11 only once, on subsequent iters jump straight to 12
5       OpenAutoindex    3     0     0                    0   cursor=3
6       Rewind           1     12    0                    0   Rewind t2 # open source table for ephemeral index
7         Column         1     0     2                    0   r[2]=t2.a
8         RowId          1     3     0                    0   r[3]=t2.rowid
9         MakeRecord     2     2     4                    0   r[4]=mkrec(r[2..3])
10        IdxInsert      3     4     2                    0   key=r[4] # insert stuff to ephemeral index
11      Next             1     7     0                    0
12      Column           0     0     5                    0   r[5]=t1.a
13      IsNull           5     20    0                    0   if (r[5]==NULL) goto 20
14      SeekGE           3     20    5                    0   key=[5..5] # perform seek on ephemeral index
15        IdxGT          3     20    5                    0   key=[5..5]
16        DeferredSeek   3     1     0                    0
17        Column         0     0     1                    0   r[1]=t1.a
18        ResultRow      1     1     0                    0   output=r[1]
19      Next             2     15    0                    0
20    Next               0     4     0                    0
21    Halt               0     0     0                    0
22    Transaction        0     0     0                    0   write=false
23    Goto               0     1     0                    0

limbo> .timer on
limbo> select * from t1 natural join t2;
┌───┐
│ a │
├───┤
└───┘
Command stats:
----------------------------
total: 220 ms (this includes parsing/coloring of cli app)
```

Closes #1356
2025-04-22 13:00:06 +03:00
Timo Kösters
68d8b86bb7 fix: get name of rowid column 2025-04-22 08:46:37 +02:00
Jussi Saurio
af21f60887 translate/main_loop: create autoindex when index.ephemeral=true 2025-04-21 14:59:13 +03:00
Jussi Saurio
c1b2dfc32b TableReference: add method column_is_used() 2025-04-21 14:59:13 +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
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
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
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
457bded14d optimizer: refactor optimizer to support multicolumn index scans 2025-04-10 15:53:02 +03:00
Jussi Saurio
a706b7160a planner: support index backwards seeks and iteration 2025-04-09 10:14:29 +03:00
PThorpe92
f6a64a7b15 Support OFFSET clause for LIMIT in UPDATE queries 2025-04-04 12:35:30 -04:00
PThorpe92
211c9a0212 Remove From impl on iteration direction for sort order 2025-03-30 12:18:12 -04:00
PThorpe92
516e443a2b Fix use index cursor id in emitter and prevent reinsert pk on update 2025-03-30 12:15:25 -04:00
PThorpe92
a88ce2a4b7 Correct comment on update plan 2025-03-30 12:15:25 -04:00
PThorpe92
b7fca31ef6 Add comments and impl Copy on iterdir type 2025-03-30 12:15:24 -04:00
PThorpe92
3fe14f37a5 Create plan for Update queries 2025-03-30 12:15:24 -04:00
Pere Diaz Bou
e4a8ee5402 move load extensions to Connection
Extensions are loaded per connection and not per database as per SQLite
behaviour. This also helps with removing locks.
2025-03-05 14:07:48 +01:00
Pere Diaz Bou
8daf7666d1 Make database Sync + Send 2025-03-05 14:07:48 +01: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
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
795576b2ec dont eagerly allocate result column name strings 2025-02-05 17:53:23 +02:00
Jussi Saurio
40f536fabb Dont store available_indexes on plan; only used in optimize_plan() 2025-02-03 12:52:14 +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
e63256f657 Change Display implementation of Plan to work with new data structures 2025-02-02 10:18:13 +02:00
Jussi Saurio
390d0e673f plan.rs: refactor data structures
- Get rid of SourceOperator tree
- Make plan have a Vec of TableReference, and TableReference now
  contains the information from the old SourceOperator.
- Remove `predicates` (conditions) from Table References -- put
  everything in the WHERE clause like SQLite, and attach metadata
  to the where clause expressions with JoinAwareConditionExpr struct.
- Refactor select_star() to be simpler now that we use a vec, not a tree
2025-02-02 10:18:13 +02:00
Glauber Costa
249a8cf8d2 keep type information as a string in column metadata
SQLite holds on to it deeply, for example:

sqlite> create table a(a int);
sqlite> create table b(b integer);
sqlite> create table c(c glauber);

sqlite> pragma table_info=a;
0|a|INT|0||0
sqlite> pragma table_info=b;
0|b|INTEGER|0||0
sqlite> pragma table_info=c;
0|c|glauber|0||0

So we'll keep it as well so we can produce the same responses.
2025-01-30 19:53:36 -05:00
Glauber Costa
69d3fbc797 keep track of notnull constraint on column creation 2025-01-30 17:04:12 -05:00
Glauber Costa
42f93e9bea add default type to Column definition 2025-01-30 16:45:57 -05:00
ben594
54a6505b33 Handle negative limits and offsets 2025-01-26 16:40:30 -05:00
ben594
d03a0dbd39 Added parsing of offset clause 2025-01-26 16:40:30 -05:00
Jorge López
86a4714711 syntactic changes: remove unneeded paths when the type is already imported 2025-01-18 18:29:12 +01:00