Commit Graph

1336 Commits

Author SHA1 Message Date
Jussi Saurio
f5e27f23ad Use type-safe PageSize newtype for connection.page_size 2025-08-14 12:40:58 +03:00
Jussi Saurio
d7186c7d7b Merge 'Add support for unlikely(X)' from bit-aloo
Implements the unlikely(X) function. Removes runtime implementations of
likely(), unlikely() and likelihood(), replacing them with panics if
they reach the VDBE.

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

Closes #2559
2025-08-14 10:56:27 +03:00
bit-aloo
32e59614c7 remove unnecessary copy instr in likelihood, likely and unlikely 2025-08-14 09:08:32 +05:30
Glauber Costa
5ab6f78f6b Implement views
Views (non materialized) are relatively simple, since they are just
query aliases.

We can expand them as if they were subqueries.
2025-08-13 14:14:03 -05:00
Glauber Costa
337f27a433 rename some structures to mention materialized views
A lot of the structures we have - like the ones under Schema, are
specific for materialized views. In preparation to adding normal views,
rename them, so things are less confusing.
2025-08-13 14:13:16 -05:00
bit-aloo
eda3a82306 strip unylikely and just translate the inner value 2025-08-13 22:46:31 +05:30
bit-aloo
e72097e2b7 strip likely and just translate the inner value 2025-08-13 22:46:22 +05:30
Levy A.
45d959635c fix: check if index exists with the same name 2025-08-13 08:55:17 +03:00
Glauber Costa
770f86e490 move our dbsp-based views to materialized views
We will implement normal SQLite-style view-as-an-alias for
compatibility, and will call our incremental views materialized views.
2025-08-12 14:19:17 -05:00
bit-aloo
3581895f18 add unlikely scalar method 2025-08-12 16:27:23 +05:30
Jussi Saurio
0c9180426f Merge 'Direct schema mutation – add RenameColumn instruction' from Levy A.
<img width="934" height="511" alt="image" src="https://github.com/user-
attachments/assets/992e6c7e-5b4a-4cd2-9040-5d1f44ca55d0" />
<img width="973" height="261" alt="image" src="https://github.com/user-
attachments/assets/e179143a-92f3-427a-be9a-280ce6a739f6" />

Closes #2546
2025-08-12 10:19:31 +03:00
Pekka Enberg
2fa501158c Merge 'turso-cdc: add updates column for cdc table' from Nikita Sivukhin
This PR adds new `updates` column to the CDC table. This column holds
updated fields of the row in the following format:
```
[C boolean values where true set for changed columns]
[C values with updates where NULL is set for not-changed columns]
```
For example:
```
turso> UPDATE t SET y = 'turso', q = 'db' WHERE rowid = 1;
turso> SELECT bin_record_json_object('["x","y","z","q","x","y","z","q"]', updates) as updates FROM turso_cdc;
┌──────────────────────────────────────────────────────────────────┐
│ updates                                                          │
├──────────────────────────────────────────────────────────────────┤
│ {"x":0,"y":1,"z":0,"q":1,"x":null,"y":"turso","z":null,"q":"db"} │
└──────────────────────────────────────────────────────────────────┘
```
Also, this column works differently for `ALTER TABLE` statements where
update value for `sql` will be equal to the original `ALTER TABLE`:
```
turso> ALTER TABLE t ADD COLUMN t;
turso> SELECT bin_record_json_object('["type","name","tbl_name","rootpage","sql","type","name","tbl_name","rootpage","sql"]', updates) as updates FROM turso_cdc WHERE rowid = 2;
┌───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ updates                                                                                                                                           │
├───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ {"type":0,"name":0,"tbl_name":0,"rootpage":0,"sql":1,"type":null,"name":null,"tbl_name":null,"rootpage":null,"sql":"ALTER TABLE t ADD COLUMN t;"} │
└───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘
```
This will help turso-db to implement logical replication which supports
both column-level updates and schema changes

Closes #2538
2025-08-12 09:50:16 +03:00
Pekka Enberg
1349c051f8 Merge 'Fix view processing in the VDBE' from Jussi Saurio
1. Introduce state machines to insert and delete to make sure IO is
handled properly
2. When `rowid` is changed in `UPDATE`, it is handled as a combination
of delete and insert, so `op_insert` doesn't need to update the
incremental view with the deleted old column since the preceding
`op_delete` instruction should already do it.

Closes #2542
2025-08-12 09:49:11 +03:00
Levy A.
665f10d71b feat: add RenameColumn instruction 2025-08-11 17:08:06 -03:00
Jussi Saurio
f598c86fa4 Merge 'Handle single, double and unquoted strings in values clause' from Mikaël Francoeur
I'm not sure how much this will clash with @TcMits's parser rewrite,
hopefully not too much. If it does and we eventually have to remove it,
at least we'll have two new regression tests.
Closes https://github.com/tursodatabase/turso/issues/2484

Closes #2499
2025-08-11 21:08:15 +03:00
Preston Thorpe
cb59877821 Merge 'Feat/pragma module list' from Lucas Forato
Implementation of the module_list pragma

Reviewed-by: Diego Reis (@el-yawd)
Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #2430
2025-08-11 13:54:24 -04:00
Jussi Saurio
f38333b373 fix/vdbe: fix state handling for incremental views
- When the rowid is changed in UPDATE, it is handled as a combination of DELETE + INSERT,
so we dont need to delete the old values in that case
- We should only update the views after the operation on the btree is done
- A proper state machine is needed to handle IO yielding points
2025-08-11 19:02:15 +03:00
Lucas Forato
e07ab423e6 feat: removed module_name from VirtualTable, instead looking only at symbol tables 2025-08-11 11:43:32 -03:00
Pekka Enberg
4a53c44466 Merge 'Add support for PRAGMA freelist_count' from bit-aloo
Implements `PRAGMA freelist_count` to return the current number of free
pages in the database.

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

Closes #2531
2025-08-11 15:07:58 +03:00
Lucas Forato
19c445f8b1 feat: listing both used and non-used modules 2025-08-11 08:42:08 -03:00
Lucas Forato
5aa99f6743 feat: listing all modules 2025-08-11 08:42:03 -03:00
Nikita Sivukhin
5d0ada9fb9 add "updates" column for cdc table 2025-08-11 12:46:15 +04:00
Glauber Costa
145d6eede7 Implement very basic views using DBSP
This is just the bare minimum that I needed to convince myself that this
approach will work. The only views that we support are slices of the
main table: no aggregations, no joins, no projections.

drop view is implemented.
view population is implemented.
deletes, inserts and updates are implemented.

much like indexes before, a flag must be passed to enable views.
2025-08-10 23:34:04 -05:00
bit-aloo
d2171e24a5 add pragma freelist_count 2025-08-11 10:03:46 +05:30
Glauber Costa
e255fc9a81 Add table name to the delete bytecode
When building views (soon), it will be important to know which table
is being deleted. Getting from the cursor id is very cumbersome.

What we are doing here is symmetrical to op_insert, and sqlite also
passes table information in one of the registers (p4)
2025-08-10 22:50:25 -05:00
Glauber Costa
e9b8f6fba9 Add table name to the delete bytecode
When building views (soon), it will be important to know which table
is being deleted. Getting from the cursor id is very cumbersome.

What we are doing here is symmetrical to op_insert, and sqlite also
passes table information in one of the registers (p4)
2025-08-10 16:10:45 -05:00
Mikaël Francoeur
2cf4e4fe96 handle single, double and unquoted strings in values clause 2025-08-08 09:03:38 -04:00
Jussi Saurio
2fbe33d200 Merge 'translate: return parse errors for unsupported features instead of silently ignoring' from Jussi Saurio
Closes #1510

Closes #2505
2025-08-08 15:56:13 +03:00
Jussi Saurio
cca2f6c947 Merge 'Evaluate WHERE conditions after LEFT JOIN' from Piotr Rżysko
This fix ensures that `WHERE` conditions are emitted after the `LEFT
JOIN` match flag is set, so rows from the right table are properly
filtered, even when they are `NULL` due to the outer join.
Previously, the query below would return rows where `products.price` was
`NULL`:
```sql
SELECT users.id, price
FROM users
LEFT JOIN products ON users.id = products.id
WHERE products.price IS NOT NULL;
```

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

Closes #2501
2025-08-08 15:54:17 +03:00
Jussi Saurio
21dc2d0161 translate: return parse errors for unsupported features instead of silently ignoring 2025-08-08 11:39:30 +03:00
Pekka Enberg
0f9d0cf519 Merge branch 'main' into 2025-08-07-add-query-only-pragma 2025-08-08 07:41:38 +03:00
Piotr Rzysko
375b9047e2 Evaluate WHERE conditions after LEFT JOIN
Previously, the query from the added test would not filter out rows
where `products.price` was NULL.
2025-08-08 06:26:30 +02:00
Piotr Rzysko
92ba25e44d Extract loop emitting conditions into a method
No functional changes — this is just preparation for reusing this code
and avoiding polluting future commits with trivial refactoring.
2025-08-08 06:21:08 +02:00
bit-aloo
c7f7ae32e3 review fixes 2025-08-08 08:43:15 +05:30
Preston Thorpe
6b266e7e84 Merge 'Direct schema mutation – add instruction' from Levy A.
<img width="960" height="205" alt="image" src="https://github.com/user-
attachments/assets/f60a2133-dfe4-4411-9a7c-7283eb073875" />
<img width="944" height="504" alt="image" src="https://github.com/user-
attachments/assets/9383c8e2-4d8d-40b9-8ace-825ca3cf8682" />
```
`ALTER TABLE _ ADD COLUMN _`/limbo_add_column/
                        time:   [2.1199 ms 2.1921 ms 2.2756 ms]
                        change: [-85.983% -85.416% -84.716%] (p = 0.00 < 0.05)
                        Performance has improved.
Found 13 outliers among 100 measurements (13.00%)
  6 (6.00%) high mild
  7 (7.00%) high severe
`ALTER TABLE _ ADD COLUMN _`/sqlite_add_column/
                        time:   [10.358 ms 10.404 ms 10.469 ms]
                        change: [-6.2566% -2.3515% +0.2046%] (p = 0.21 > 0.05)
                        No change in performance detected.
Found 14 outliers among 100 measurements (14.00%)
  2 (2.00%) high mild
  12 (12.00%) high severe
  ```

Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #2482
2025-08-07 21:31:49 -04:00
bit-aloo
aaeec4d4f3 Implement PRAGMA query_only logic 2025-08-07 23:49:07 +05:30
bit-aloo
2cf7f66a02 Enforce query_only in write operations 2025-08-07 23:46:00 +05:30
Piotr Rzysko
8986266394 Emit conditions in open_loop in one place
The loop emitting conditions is independent of the operation type.
2025-08-07 19:26:32 +02:00
Levy A.
658405d6b3 feat: add AddColumn instruction 2025-08-07 11:43:16 -03:00
Jussi Saurio
eb7fa9693d Merge 'Return error on attempting to drop index associated with PRIMARY KEY and UNIQUE constraints' from
Closes issue #2455. Also includes tests.

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

Closes #2461
2025-08-07 09:00:02 +03:00
Glauber Costa
071330a739 implement the JournalMode vdbe instruction
We do this already, but not through any opcode.
Move it to an opcode for compatibility reasons.
2025-08-06 19:30:19 -05:00
Glauber Costa
f36974f086 implement the MaxPgCount opcode
It is used by the pragma max_page_count, which is also implemented.
2025-08-06 13:20:15 -05:00
Levy A.
c9e1eca8dc feat: add DropColumn instruction 2025-08-06 13:39:30 -03:00
Pekka Enberg
0c9216d1cc Merge 'cdc: emit entries for schema changes' from Nikita Sivukhin
This PR emit CDC entries as changes in `sqlite_schema` table for DDL
statements: `CREATE TABLE` / `CREATE INDEX` / etc.
The logic is a bit tricky as under the hood `turso` can do some implicit
DDL operations like:
1. Creating auto-indexes in case of `CREATE TABLE`
2. Deletion of all attached indices in case of `DROP TABLE`
```
turso> PRAGMA unstable_capture_data_changes_conn('full');
turso> CREATE TABLE t(x, y, z UNIQUE, q, PRIMARY KEY (x, y));
turso> CREATE INDEX t_xy ON t(x, y);
turso> CREATE TABLE q(a, b, c);
turso> ALTER TABLE q DROP COLUMN b;

turso> SELECT
change_id, 
id,
change_type, 
table_name,
bin_record_json_object(table_columns_json_array(table_name), before) AS before,
bin_record_json_object(table_columns_json_array(table_name), after) AS after
FROM turso_cdc;
┌───────────┬────┬─────────────┬───────────────┬─────────────────────────────────────────────────────────────────────┬─────────────────────────────────────────────────────────────────────┐
│ change_id │ id │ change_type │ table_name    │ before                                                              │ after                                                               │
├───────────┼────┼─────────────┼───────────────┼─────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│         1 │  2 │           1 │ sqlite_schema │                                                                     │ {"type":"table","name":"t","tbl_name":"t","rootpage":3,"sql":"CREA… │
├───────────┼────┼─────────────┼───────────────┼─────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│         2 │  5 │           1 │ sqlite_schema │                                                                     │ {"type":"index","name":"t_xy","tbl_name":"t","rootpage":6,"sql":"C… │
├───────────┼────┼─────────────┼───────────────┼─────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│         3 │  6 │           1 │ sqlite_schema │                                                                     │ {"type":"table","name":"q","tbl_name":"q","rootpage":7,"sql":"CREA… │
├───────────┼────┼─────────────┼───────────────┼─────────────────────────────────────────────────────────────────────┼─────────────────────────────────────────────────────────────────────┤
│         4 │  6 │           0 │ sqlite_schema │ {"type":"table","name":"q","tbl_name":"q","rootpage":7,"sql":"CREA… │ {"type":"table","name":"q","tbl_name":"q","rootpage":7,"sql":"CREA… │
└───────────┴────┴─────────────┴───────────────┴─────────────────────────────────────────────────────────────────────┴─────────────────────────────────────────────────────────────────────┘
```
For now, CDC capture only all explicit operations and ignore all
implicit operations. The reasoning for that is that one use case for CDC
is to apply logical changes as is with simple SQL statements - but if
implicit operations will be logged to the CDC table too - we can have
hard times using simple SQL statement (for example, creation of
`autoindices` will always work; implicit deletion of indices for `DROP
TABLE` also can lead to some troubles and force us to is `DROP INDEX IF
EXISTS ...` statements + we will need to filter out autoindices in this
case too).
Also, to simplify PR, for now `DatabaseTape` from `turso-sync` package
just ignore all schema changes from CDC table.

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

Closes #2426
2025-08-06 14:48:27 +03:00
rajajisai
3dd08864f9 Edit comment 2025-08-05 21:27:47 -07:00
rajajisai
f49575d547 Return error when trying to drop an index associated with UNIQUE or PRIMARY KEY constraints 2025-08-05 21:17:25 -07:00
Nikita Sivukhin
c0d5c55d5c fix tests and clippy 2025-08-06 01:03:49 +04:00
Nikita Sivukhin
c6a87d61c7 emit CDC entries if necessary for schema changes 2025-08-06 01:03:49 +04:00
Nikita Sivukhin
0b4c1ac802 refactor code a little bit 2025-08-06 01:03:48 +04:00
Glauber Costa
d1be7ad0bb implement the collseq bytecode instruction
SQLite generates those in aggregations like min / max with collation
information either in the table definition or in the column expression.

We currently generate the wrong result here, and properly generating the
bytecode instruction fixes it.
2025-08-05 13:49:04 -05:00