Commit Graph

815 Commits

Author SHA1 Message Date
Preston Thorpe
97526ee4d1 Merge 'Add framework for testing extensions in TCL' from Piotr Rżysko
There is a distinction between tests that verify extension-specific
behavior and those that verify interactions between the database engine
and extensions. Previously, both types of tests were kept in
`extensions.py`. With this new framework, we can extract the latter type
of tests from `extensions.py` into TCL. This cleans up `extensions.py`
and enables compatibility testing with SQLite at no extra cost.
I’m currently working on supporting outer joins involving TVFs and
planning to add more tests that exercise the database’s handling of
virtual tables, so I decided to do this refactoring first.
In the future, we may consider moving extension-specific tests to TCL as
well, especially those that have counterparts in SQLite or sqlean.

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

Closes #2556
2025-08-15 17:58:21 -04:00
Piotr Rzysko
d1a91a63e6 Extract TVF-related tests to TCL
These tests verify interactions between the database engine and TVFs.
They happen to use generate_series, but they are not intended to test
the behavior of any specific extension. Tests that verify generate_series
specific behavior remain in extensions.py.
2025-08-15 21:06:30 +02:00
Piotr Rzysko
20ea079679 Add framework for testing extensions in TCL
There is a distinction between tests that verify extension-specific
behavior and tests that verify interactions between the database engine
and extensions. Previously, both types of tests were kept in extensions.py.
With this new framework, we can extract the latter type of tests from
extensions.py into TCL. This cleans up extensions.py and provides
compatibility testing with SQLite at no extra cost.

To demonstrate the framework’s usage, tests verifying the handling of
virtual tables were extracted to TCL.

In the future, we may consider moving extension-specific tests to TCL as
well, especially those that have counterparts in SQLite or sqlean.
2025-08-15 21:06:27 +02:00
Piotr Rzysko
116673c2e5 Unify how SQL is executed in TCL tests 2025-08-15 21:06:17 +02:00
Jussi Saurio
d2cfe06aa5 Fix DISTINCT with ORDER BY
We had a bug where we were checking for duplicates in the DISTINCT
index based on both the result column count plus any ORDER BY columns
not present in the DISTINCT clause.

This is wrong, so fix it by only using the result columns for the
dedupe check.
2025-08-15 15:49:55 +03:00
PThorpe92
6d7b660dd4 Adjust test for .clone method 2025-08-14 21:31:14 -04:00
Jussi Saurio
0b17957f4e Merge 'Implement normal views' from Glauber Costa
Now that we actually implemented the statement parsing around views,
implementing normal SQLite views is relatively trivial, as they are just
an alias to a query.
We'll implement them now to get them out of the way, and then I'll go
back to DBSP

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

Closes #2591
2025-08-14 10:54:46 +03:00
Preston Thorpe
78abe72762 Merge 'fix: Handle fresh INSERTs in materialized view incremental maintenance' from Glauber Costa
The op_insert function was incorrectly trying to capture an "old record"
for fresh INSERT operations when a table had dependent materialized
views. This caused a "Cannot delete: no current row" error because the
cursor wasn't positioned on any row for new inserts.
The issue was introduced in commit f38333b3 which refactored the state
machine for incremental view handling but didn't properly distinguish
between:
- Fresh INSERT operations (no old record exists)
- UPDATE operations without rowid change (old record should be captured)
- UPDATE operations with rowid change (already handled by DELETE)
This fix checks if cursor.rowid() returns a value before attempting to
capture the old record. If no row exists (fresh INSERT), we correctly
set old_record to None instead of erroring out.
I am also including tests to make sure this doesn't break. The reason I
didn't include tests earlier is that I didn't know it was possible to
run the tests under a flag. But in here, I am just adding the flag to
the execution script.

Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #2579
2025-08-13 21:36:19 -04:00
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
7e76970035 fix: Handle fresh INSERTs in materialized view incremental maintenance
The op_insert function was incorrectly trying to capture an "old record"
for fresh INSERT operations when a table had dependent materialized views.
This caused a "Cannot delete: no current row" error because the cursor
wasn't positioned on any row for new inserts.

The issue was introduced in commit f38333b3 which refactored the state
machine for incremental view handling but didn't properly distinguish
between:
- Fresh INSERT operations (no old record exists)
- UPDATE operations without rowid change (old record should be captured)
- UPDATE operations with rowid change (already handled by DELETE)

This fix checks if cursor.rowid() returns a value before attempting to
capture the old record. If no row exists (fresh INSERT), we correctly
set old_record to None instead of erroring out.

I am also including tests to make sure this doesn't break. The reason I
didn't include tests earlier is that I didn't know it was possible to
run the tests under a flag. But in here, I am just adding the flag to
the execution script.
2025-08-13 06:41:14 -05:00
Levy A.
45d959635c fix: check if index exists with the same name 2025-08-13 08:55:17 +03:00
Pekka Enberg
96673a54a8 bindings/javascript: Add async connect() function
Let's make the API symmetric with libSQL and serverless drivers.
2025-08-12 11:39:59 +03:00
Pekka Enberg
1c6a3bacb9 testing/javascript: Fix libSQL connection setup
The API changed in libSQL to be async.
2025-08-12 11:33:54 +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
PThorpe92
ae22af29cd Fix pragma module list tests 2025-08-11 12:13:47 -04:00
Lucas Forato
673cfdbc0f test: include tests in extensions.py 2025-08-11 08:42:08 -03:00
Lucas Forato
c2a5b43c68 fix: revert changes on testing dbs 2025-08-11 08:42:08 -03:00
Lucas Forato
804df8dd7a feat: changed to non-empty test 2025-08-11 08:42:03 -03:00
Lucas Forato
c37ccd49e1 feat: included tests 2025-08-11 08:42:03 -03:00
Mikaël Francoeur
2cf4e4fe96 handle single, double and unquoted strings in values clause 2025-08-08 09:03:38 -04: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
Pekka Enberg
f2b2e4d4d8 Rename JavaScript package to @tursodatabase/database 2025-08-08 13:22:10 +03:00
Pekka Enberg
7a09eb0d4c Merge 'Fix JavaScript bindings packaging' from Nikita Sivukhin
This PR configure `#entry-point` import alias for javascript bindings in
order to use `browser.js` napi-rs generated file in browser context.
Also, this PR forces napi-rs to emit `index.js` entrypoint using ESM and
also use typescript for writing our wrapper code around napi-rs
bindings.
In order to make behaviour consistent when lib is imported through ESM
or CommonJS this PR also replace default export of `Database` by named
on. The problem is that `export default Database` will be logically
equivalent to `modules.export.default = Database` which is not the same
thing as `modules.export = Database` and this will need to access
additional `.default` field with CommonJs style imports (e.g. `new
require('@tursodatabase/turso').default(...)`). In order to remove this
difference - I just replaced default export with named one.

Closes #2488
2025-08-08 10:42:21 +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
Preston Thorpe
88d49e402f Merge 'javascript: Organize test cases better' from Pekka Enberg
Reviewed-by: Preston Thorpe <preston@turso.tech>

Closes #2490
2025-08-07 21:28:27 -04:00
Nikita Sivukhin
cbe0a7708e update tests 2025-08-08 01:21:37 +04:00
PThorpe92
b131331673 Add shell .py tests for .clone cli command 2025-08-07 16:27:08 -04:00
Pekka Enberg
b033333c8a javascript: Organize test cases better 2025-08-07 15:10:52 +03:00
Pekka Enberg
bae4406e32 testing/javascript: Enable iterate() test cases 2025-08-07 14:28:34 +03:00
Pekka Enberg
b603ee7062 Merge 'JavaScript improvements' from Pekka Enberg
Closes #2467
2025-08-07 14:01:07 +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
Preston Thorpe
f55d34a3db Merge 'Fix panic on loading extension on brand new connection' from Preston Thorpe
Closes #2476

Closes #2477
2025-08-06 23:44:38 -04:00
rajajisai
44ba3caaaa Remove miscellaneous characters generated due varying CLI size 2025-08-06 16:02:15 -07:00
PThorpe92
ae99edf4a6 Adjust test to test for behavior of loading extension on brand new connection 2025-08-06 16:55:54 -04: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
Jussi Saurio
aaa9ed1d9f Merge 'Add regexp capture' from bit-aloo
This PR adds RegExp Capture to regexp module

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

Closes #2465
2025-08-06 12:13:19 +03:00
bit-aloo
84316d9206 fix python lint 2025-08-06 12:02:20 +05:30
Pekka Enberg
ba37e1dc9a testing/javascript: Enable some passing sync test cases 2025-08-06 09:25:22 +03:00
Pekka Enberg
185b7016dd testing/javascript: Enable Statement.all() raw mode test case 2025-08-06 09:23:16 +03:00
Pekka Enberg
3a5e7f8fb6 testing/javascript: Enable Statement.all() test case 2025-08-06 09:22:48 +03:00
Pekka Enberg
53ac67a2be testing/javascript: Add pragma() after close() test case 2025-08-06 09:10:03 +03:00
Pekka Enberg
66b4de1ad9 testing/javascript: Enable exec() after close() test case 2025-08-06 08:08:35 +03:00
Pekka Enberg
cda3375061 testing/javascript: Enable prepare() after close() test case 2025-08-06 07:50:23 +03:00
rajajisai
9ebfed294e Including tests 2025-08-05 21:18:52 -07: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
bit-aloo
b26a58f652 update extension.py with regexp_replace test 2025-08-05 20:36:09 +05:30
Piotr Rzysko
99f87c07c1 Support column references in table-valued function arguments
This change extends table-valued function support by allowing arguments
to be column references, not only literals.

Virtual tables can now reject a plan by returning an error from
best_index (e.g., when a TVF argument references a table that appears
later in the join order). The planner using this information excludes
invalid plans during join order search.
2025-08-05 05:48:28 +02:00
Piotr Rzysko
82491ceb6a Integrate virtual tables with optimizer
This change connects virtual tables with the query optimizer.
The optimizer now considers virtual tables during join order search
and invokes their best_index callbacks to determine feasible access
paths.

Currently, this is not a visible change, since none of the existing
extensions return information indicating that a plan is invalid.
2025-08-05 05:48:28 +02:00
Piotr Rzysko
6a4cf02a90 Fix computation of argv_index in best_index
The `filter` methods for extensions affected by this fix expect arguments
to be passed in a specific order. For example, `generate_series` assumes
that if the `start` argument exists, it is always passed to `filter`
first. If `start` does not exist, then `stop` is passed first — but
`stop` must never come before `start`.

Previously, this was not guaranteed: `best_index` relied on constraints
being passed in the order matching `filter`'s expectations.
2025-08-04 19:38:45 +02:00
Piotr Rzysko
79e166d722 Implement xBestIndex for kvstore.c
This is to match Rust kv_store implementation.
2025-08-04 19:25:11 +02:00