Discovered this one while working on #3322
It was a bit more elusive because the original error was essentially a
red herring:
```
turso> CREATE INDEX idx ON "t t" (`a a`);
× unexpected token at SourceSpan { offset: SourceOffset(22), length: 1 }
╭────
1 │ CREATE INDEX idx ON "t t" (`a a`);
· ┬
· ╰── here
╰────
help: expected [TK_LP] but found TK_ID
```
Reviewed-by: Preston Thorpe <preston@turso.tech>
Closes#3345
This PR implements the `json_tree` table-valued function.
It's not 100% compatible with SQLite, because SQLite does some iffy
things with the `key` and `path` columns. I started a
[thread](https://www.sqlite.org/forum/forumpost/48f5763d8c) on their
forum and I linked it to the disabled tests in `json.test`.
Reviewed-by: Preston Thorpe <preston@turso.tech>
Closes#3256
fixes#1976
and #1605
```zsh
turso> DROP TABLE IF EXISTS t;
CREATE TABLE t (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
);
turso> INSERT INTO t (name) VALUES ('A'); SELECT * FROM sqlite_sequence;
┌──────┬─────┐
│ name │ seq │
├──────┼─────┤
│ t │ 1 │
└──────┴─────┘
turso> DROP TABLE IF EXISTS t;
CREATE TABLE t (
id INTEGER PRIMARY KEY AUTOINCREMENT,
name TEXT
);
turso> INSERT INTO t (name) VALUES ('A'); SELECT * FROM sqlite_sequence;
┌──────┬─────┐
│ name │ seq │
├──────┼─────┤
│ t │ 1 │
└──────┴─────┘
turso> INSERT INTO t (name) VALUES ('A'); SELECT * FROM sqlite_sequence;
┌──────┬─────┐
│ name │ seq │
├──────┼─────┤
│ t │ 2 │
└──────┴─────┘
turso>
```
Reviewed-by: Preston Thorpe <preston@turso.tech>
Closes#2983
This PR makes all JS db packages to have uniform interface: `new
Database(...)` constructor with explicit `connect()` and `close()`
methods.
Also, this PR adds docstrings in the code and properly support few
better-sqlite options (readonly, fileMustExist, timeout)
Closes#3334
Closes#3320Closes#3286
In addition to the problem reported in the ticket, 2 more issues were
identified:
1. Renaming a column for a table with a special character in its name
failed with
```
turso> CREATE TABLE `t t`(a);
turso> ALTER TABLE `t t` RENAME COLUMN a TO `a a`;
thread 'main' panicked at core/vdbe/execute.rs:7870:14:
table being renamed should be in schema
```
2. The renamed table in the `sql` column of `sqlite_schema` was not
reflected correctly after renaming:
```
turso> select * from sqlite_schema;
┌───────┬──────┬──────────┬──────────┬──────────────────────┐
│ type │ name │ tbl_name │ rootpage │ sql │
├───────┼──────┼──────────┼──────────┼──────────────────────┤
│ table │ t t │ t t │ 2 │ CREATE TABLE t t (a) │
└───────┴──────┴──────────┴──────────┴──────────────────────┘
```
3. `sql` for indexes was not reflected correctly after renaming a column
that contains special characters:
```
turso> ALTER TABLE `t t` RENAME COLUMN `a a` TO `b b`;
turso> SELECT sql FROM sqlite_schema;
┌───────────────────────────────────┐
│ sql │
├───────────────────────────────────┤
│ CREATE TABLE `t t` (`b b`) │
├───────────────────────────────────┤
│ CREATE INDEX idx ON `t t` (`a a`) │
├───────────────────────────────────┤
```
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#3322
In e.g. `SELECT x AS y, y AS x FROM t ORDER BY x;`, the `x` in the
`ORDER BY` should reference t.y, which has been aliased as `x` for this
query. The same goes for GROUP BY, JOIN ON etc. but NOT for WHERE.
Previously we had wrong precedence in `bind_and_rewrite_expr`.
fixes#3231
```zsh
❯ sqlite3
SQLite version 3.50.4 2025-07-30 19:33:53
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> CREATE TABLE t1 (a);
ALTER TABLE t1 ADD COLUMN a;
Parse error: duplicate column name: a
sqlite> ALTER TABLE t1 ADD COLUMN name varchar(255);
SELECT sql FROM sqlite_schema WHERE name = 't1';
CREATE TABLE t1 (a, name varchar(255))
sqlite>
```
```zsh
turso>
turso> CREATE TABLE t1 (a);
ALTER TABLE t1 ADD COLUMN a;
x Parse error: duplicate column name: a
turso> ALTER TABLE t1 ADD COLUMN name varchar(255);
SELECT sql FROM sqlite_schema WHERE name = 't1';
┌─────────────────────────────────────────┐
│ sql │
├─────────────────────────────────────────┤
│ CREATE TABLE t1 (a, name varchar (255)) │
└─────────────────────────────────────────┘
turso>
```
Reviewed-by: Pere Diaz Bou <pere-altea@homail.com>
Closes#3249
This PR implements the `Sequence` and `SequenceTest` opcodes, although
does not yet add plumbing to emit the latter.
SQLite has two distinct mechanisms that determine the final row order
with aggregates:
Traversal order of GROUP BY, and ORDER BY tiebreaking. When ORDER BY
contains only aggregate expressions and/or constants, SQLite has no
extra tiebreak key, but when ORDER BY mixes aggregate and non-aggregate
terms, SQLite adds an implicit, stable row `sequence` so “ties” respect
the input order.
This PR also fixes an issue with a query like the following:
```sql
SELECT u.first_name, COUNT(*) AS c
FROM users u
JOIN orders o ON o.user_id = u.id
GROUP BY u.first_name
ORDER BY c DESC;
```
Because ORDER BY has only an aggregate (COUNT(*) DESC) and no non-
aggregate terms, SQLite traverses the group key (u.first_name) in DESC
order in this case, so ties on c naturally appear with group keys in
descending order.
Previously tursodb would return the group key sorted in ASC order,
because it was used in all cases as the default
Closes#3287
This PR adds support for partial indexes, e.g. `CREATE INDEX` with a
provided predicate
```sql
CREATE UNIQUE INDEX idx_expensive ON products(sku) where price > 100;
```
The PR does not yet implement support for using the partial indexes in
the optimizer.
Reviewed-by: Jussi Saurio <jussi.saurio@gmail.com>
Closes#3228
This PR fixes bugs found in the [turso-
go](https://github.com/tursodatabase/turso-go) driver with UPSERT clause
earlier, where `Gorm` will (obviously) use Expr::Variable's as well as
use quotes for `Expr::Qualified` in the tail end of an UPSERT statement.
Example:
```sql
INSERT INTO users (a,b,c) VALUES (?,?,?) ON CONFLICT (`users`.`a`) DO UPDATE SET b = `excluded`.`b`, a = ?;
```
and previously we were not properly calling `rewrite_expr`, which was
not properly setting the anonymous `Expr::Variable` to `__param_N` named
parameter, so it would ignore it completely, then return the wrong # of
parameters.
Also, we didn't handle quoted "`excluded`.`x`", so it would panic in the
optimizer that Qualified should have been rewritten earlier.
Closes#3157