Commit Graph

6115 Commits

Author SHA1 Message Date
Jussi Saurio
9aa09d5ccf Add EXPLAIN support for trigger subprograms
They get printed after the parent program.
2025-11-18 15:19:01 +02:00
Jussi Saurio
423a1444d1 Don't crash if table cursor is already opened 2025-11-18 15:19:01 +02:00
Jussi Saurio
7f536506c3 Clear deferred_seeks for cursor when it is closed
Sometimes the deferred seek never happens, so we don't want it to
dangle if the same cursor is reused for another seek
2025-11-18 15:19:01 +02:00
Jussi Saurio
d398f12471 triggers: subprograms shouldnt commit or use the transaction opcode 2025-11-18 15:19:01 +02:00
Jussi Saurio
be6f8ab8b3 state.end_statement() should not be called separately in cases where abort() already does it 2025-11-18 15:19:01 +02:00
Jussi Saurio
7a12e184a8 Only reset FK violation counter if stmt was rolled back
In the case of trigger subprograms the statement didn't roll back,
since the parent program will roll it back.
2025-11-18 15:19:01 +02:00
Jussi Saurio
770c6eef9f triggers: subprograms dont use transactions 2025-11-18 15:19:01 +02:00
Jussi Saurio
70267f8710 triggers: add translation logic for INSERT triggers 2025-11-18 15:19:01 +02:00
Jussi Saurio
e28301dc2e triggers: add translation logic for UPDATE triggers 2025-11-18 15:19:01 +02:00
Jussi Saurio
516dae5b6a triggers: add translation logic for DELETE triggers 2025-11-18 15:19:01 +02:00
Jussi Saurio
5b037b0f75 resolve labels for RowSetRead insn 2025-11-18 15:19:01 +02:00
Jussi Saurio
7d1543fcc5 triggers: take triggers into account in optimizer decision
- optimize the select plan used for the RowSet in DELETE
- require ephemeral table when UPDATE involves triggers
2025-11-18 15:19:01 +02:00
Jussi Saurio
78ce3c8658 triggers: add capability for DeletePlan to write the write set into a RowSet first
This is needed for safe DELETE when there are DELETE triggers on the affected
table.
2025-11-18 15:19:01 +02:00
Jussi Saurio
e60e37da7d triggers: add execution plumbing to translation and vdbe layers 2025-11-18 15:19:01 +02:00
Jussi Saurio
3d00686f48 triggers: translation functions for DDL 2025-11-18 12:18:07 +02:00
Jussi Saurio
d4b487eebc triggers: add in-memory schema entries 2025-11-18 12:14:27 +02:00
PThorpe92
56f35ad4cd cargo fmt 2025-11-17 12:22:55 -05:00
PThorpe92
c3185d0b8c Properly handle foreign keys for INSERT OR REPLACE 2025-11-17 12:19:33 -05:00
PThorpe92
8cd33f3ec9 Add comment for or replace behavior require seek in translate/insert 2025-11-17 08:41:22 -05:00
PThorpe92
0ce5f81008 Cleanup translate/insert fix clippy warnings 2025-11-17 08:23:16 -05:00
PThorpe92
f8e78b73a8 Fix handling of partial indexes when deleting rows in ON REPLACE for insert 2025-11-17 08:23:16 -05:00
PThorpe92
634af4d6f6 Handle NOT NULL behavior for INSERT OR REPLACE 2025-11-17 08:23:15 -05:00
PThorpe92
5bff10c56e Implement INSERT OR REPLACE translation/emission 2025-11-17 08:23:10 -05:00
Jussi Saurio
693eaeb851 Merge 'Add ColDef struct to make schema::Column creation more ergonomic' from Preston Thorpe
RE: #3970
That Column::new having 14 boolean arguments was not great.
Also this removes the unneeded `parent_cols: Vec<String>` from
`ResolvedFkRef`

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

Closes #3973
2025-11-17 09:17:56 +02:00
Preston Thorpe
29f30c5c35 Merge 'allow null for unique columns and don't validate fkeys on parent' from Pavan Nambi
closes #3966
closes #3965
check constraint pr depends on this.

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

Closes #3969
2025-11-16 19:59:59 -05:00
PThorpe92
ced5aec2c7 Add ColDef arguments to Column::new in collate.rs 2025-11-16 19:36:11 -05:00
PThorpe92
ca783ccdff Add ColDef struct to make schema::Column creation more ergonomic 2025-11-16 19:10:42 -05:00
Pavan-Nambi
134f898952 correct order in column creation tests 2025-11-16 18:36:10 +05:30
Pavan-Nambi
3de37d3f64 dont validate fkey on parent add tests
correct msitake and add null test issue

add fkey test
2025-11-16 09:41:57 +05:30
Pavan-Nambi
8edea305f6 allow null for unique columns 2025-11-16 08:37:57 +05:30
Jussi Saurio
ec149865f1 Merge 'Support DELETE ... RETURNING' from Jussi Saurio
Closes #3948
2025-11-14 13:53:57 +02:00
Jussi Saurio
3f970d5d95 Merge 'Return parse error if NULLS LAST used in ORDER BY' from Jussi Saurio
we don't support this yet, so let's not silently ignore it either

Closes #3945
2025-11-14 13:36:34 +02:00
Jussi Saurio
46440ad58d Merge 'don't overwrite col mappings' from Pavan Nambi
closes https://github.com/tursodatabase/turso/issues/3951

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

Closes #3956
2025-11-14 13:36:07 +02:00
Jussi Saurio
272dadc4bc Support DELETE ... RETURNING
I didn't end up having to use the RowSet instructions for this after
all. Maybe there's some edge cases where it's required -- sqlite uses
ephemeral tables or rowsets in all RETURNING handling, but I haven't
found a need to do that yet.
2025-11-14 13:35:24 +02:00
Jussi Saurio
fca32737e7 Merge 'Refactor RETURNING to support arbitrary expressions' from Jussi Saurio
Note: contains 1000 lines of TCL tests generated by cursor :] runtime
changes are smaller and this actually deletes code in aggregate.
---
Main change is to support arbitrary expressions in RETURNING instead of
a specialcased subset, but also e.g. disallow returning TABLE.* which is
illegal syntax in SQLite.
Main idea is we add the columns of the target table (the table affected
by INSERT/UPDATE/DELETE) into `expr_to_reg_cache`) and then just
translate the RETURNING expressions as normal

Closes #3942
2025-11-14 13:34:53 +02:00
Pavan-Nambi
8d2b06e6bf remove stupid files,clippy and tcl-syntax 2025-11-14 08:24:01 +05:30
Pavan-Nambi
eaa8edb6f7 don't overwrite col mappings 2025-11-14 07:41:41 +05:30
Pekka Enberg
1f79fbc22c Merge 'Partial sync basic' from Nikita Sivukhin
This PR implements basic support for partial sync. Right now the scope
is limited to only `:memory:` IO and later will be properly expanded to
the file based IO later.
The main addition is `PartialDatabaseStorage` which make request to the
remote server for missing local pages on demand.
The main change is that now tursodatabase JS bindings accept optional
"external" IO event loop which in case of sync will drive `ProtocolIo`
internal work associated with remote page fetching tasks.

Closes #3931
2025-11-13 16:38:04 +02:00
Jussi Saurio
950184a2c6 Return parse error if NULLS LAST used in ORDER BY
we don't support this yet, so let's not silently ignore it either
2025-11-13 10:47:17 +02:00
Jussi Saurio
eee7fa5f95 Refactor RETURNING to support arbitrary expressions
Currently RETURNING was a bit of a hack since it had a special
translate_expr_for_returning() function that only supported a subset
of expressions.

Instead, we can store the columns of the target table of the INSERT/UPDATE/DELETE
we are RETURNING from in `Resolver::expr_to_reg_cache` and make those columns point
to the registers that hold the OLD/NEW column values (depending on the operation).
2025-11-13 10:32:38 +02:00
Jussi Saurio
50fbd9a3a2 Store owned strings in InsertEmitCtx for borrow-checker reasons 2025-11-13 09:35:09 +02:00
Jussi Saurio
34978d0fde Store Cow<&Expr> in expr_to_reg_cache
We will be storing owned expressions in it for RETURNING in a later commit.
2025-11-13 09:32:37 +02:00
Pere Diaz Bou
7c96b6d9f9 Merge 'Fix: Drop internal DBSP table when dropping materialized view' from Martin Mauch
# Fix: Clean up DBSP state table when dropping materialized views
## Problem
When dropping a materialized view, the internal DBSP state table (e.g.,
`__turso_internal_dbsp_state_v1_view_name`) and its automatic primary
key index were not being properly cleaned up. This caused two issues:
1. **Persistent schema entries**: The DBSP table and index entries
remained in `sqlite_schema` after dropping the view
2. **In-memory schema inconsistency**: The DBSP table remained in the
in-memory schema's `tables` HashMap, causing "table already exists"
errors when trying to recreate a materialized view with the same name
## Root Cause
The issue had two parts:
1. **Missing sqlite_schema cleanup**: The `translate_drop_view` function
deleted the view entry from `sqlite_schema` but didn't delete the
associated DBSP state table and index entries
2. **Missing in-memory schema cleanup**: The `remove_view` function
removed the materialized view from the in-memory schema but didn't
remove the DBSP state table and its indexes
## Solution
### Changes in `core/translate/view.rs`
- Added a second pass loop in `translate_drop_view` to scan
`sqlite_schema` and delete DBSP table and index entries
- The loop checks for entries matching the DBSP table name pattern
(`__turso_internal_dbsp_state_v{version}_{view_name}`) and the automatic
index name pattern (`sqlite_autoindex___turso_internal_dbsp_state_v{vers
ion}_{view_name}_1`)
- Registers for comparison values are allocated outside the loop for
efficiency
- Column registers are reused across loop iterations
### Changes in `core/schema.rs`
- Updated `remove_view` to also remove the DBSP state table and its
indexes from the in-memory schema's `tables` HashMap and `indexes`
collection
- This ensures consistency between the persistent schema
(`sqlite_schema`) and the in-memory schema
### Tests Added
Added two new test cases in `testing/materialized_views.test`:
1. **`matview-drop-cleans-up-dbsp-table`**: Explicitly verifies that
after dropping a materialized view:
   - The view entry is removed from `sqlite_schema`
   - The DBSP state table entry is removed from `sqlite_schema`
   - The DBSP state index entry is removed from `sqlite_schema`
2. **`matview-recreate-after-drop`**: Verifies that a materialized view
can be successfully recreated after being dropped, which implicitly
tests that all underlying resources (including DBSP tables) are properly
cleaned up
## Testing
- All existing materialized view tests pass
- New tests specifically verify the cleanup behavior
- Manual testing confirms that materialized views can be dropped and
recreated without errors
## Related
This fix ensures that materialized views can be safely dropped and
recreated, resolving issues where the DBSP state table would persist and
cause conflicts.

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

Closes #3928
2025-11-12 17:16:04 +01:00
Nikita Sivukhin
3d14092679 fix 2025-11-12 16:38:04 +04:00
Nikita Sivukhin
54cb7758ef fix formatting 2025-11-12 16:14:26 +04:00
Jussi Saurio
16097e7355 Merge 'Add RowSet<Add/Read/Test> instructions and rowset implementation' from Jussi Saurio
## What
Rowsets are used in SQLite for two purposes:
1. for membership tests on a set of `i64`s,
2. for in-order iteration of a set of `i64`s,
Both in cases where we can just use rowids (which are `i64`) instead of
building an entire ephemeral btree from a table's contents.
For example, in cases where a `DELETE FROM tbl WHERE ...` is performed
on a table that has any `BEFORE DELETE` triggers, SQLite collects the
table's rowids into a RowSet before actually performing the deletion.
This is similar to how an UPDATE that modifies rowids (or the index used
to iterate the UPDATE loop) will first collect the rows into an
ephemeral index, and same with `INSERT INTO ... SELECT`.
## Details
RowSet uses a "batch" concept where insertions of a given batch must be
guaranteed by caller to contain no duplicates and will be pushed into a
vector for O(1). When a new batch is started, the previous batch is
folded into a `BTreeSet` so that membership tests can be performed in
O(logn). As far as I can tell, the "in-order iteration" use case doesn't
use this batch logic at all.
## AI disclosure
This entire PR description was written by me - no AIs were harmed in the
production of it. However, the code itself was mostly vibecoded using
two agents in Cursor:
- Composer 1: given the SQLite opcode documentation and rowset.c source
code, and asked to implement the VDBE instructions and the RowSet
module.
- GPT-5: given the same SQLite docs and source code, and asked to review
Composer 1's work and write feedback into a separate markdown file.
This loop was run for roughly 4-5 iterations, where each time GPT-5's
feedback was given to Composer 1, until GPT-5 found nothing to comment
anymore.
After this, I instructed Composer 1 to improve the documentation to be
less stupid.
After that, I made a manual editing pass over the runtime code to e.g.
change boolean flags to a `RowSetMode` enum to make clearer that the
rowset has two distinct mutually exclusive purposes (membership tests
and in-order iteration), plus cleaned up some other dumb shit and added
comments.
I am still not sure if this saved time or not.

Closes #3938
2025-11-12 13:02:00 +02:00
Jussi Saurio
933c3112f9 Merge 'Use AsValueRef in more functions' from Pedro Muniz
Depends on #3932
Converting more functions to use `AsValueRef`

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

Closes #3934
2025-11-12 12:54:39 +02:00
Jussi Saurio
65a7dd40b3 Merge 'Change Value::Text and ValueRef::Text to use Cow<'static, str> and &str to avoid allocations' from Pedro Muniz
When building text values, we could not pass ownership of newly created
strings, which meant a lot of the times we were double cloning strings,
one to transform, and one to build the Value

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

Closes #3932
2025-11-12 12:54:16 +02:00
Jussi Saurio
a63e12f793 Merge 'treat parameters as "constant" within a query' from Nikita Sivukhin
Right now tursodb treat parameters/variable as non-constant. But
actually they are constant in a sense that parameters/variables has
fixed value during query execution which never changes.
This PR makes tursodb to treat parameters as constant and evaluate
expressions related to them only once.
One real-world scenario where this can be helpful is vector search
query:
```sql
    SELECT id, vector_distance_jaccard(embedding, vector32_sparse(?)) as distance
    FROM vectors
    ORDER BY distance ASC
    LIMIT ?
```
Without constant optimization, `vector32_sparse` function will be
executed for every row - which is very inefficient and query can be 100x
slower due to that (but there is no need to evaluate this function for
every query as we can transform text representation to binary only once)

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

Closes #3936
2025-11-12 11:46:10 +02:00
Jussi Saurio
cdf2f0d3c5 Fix comment about DELETE ... RETURNING 2025-11-12 11:43:06 +02:00