Merge 'translation: rewrite expressions and properly handle quoted identifiers in UPSERT' from Preston Thorpe

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
This commit is contained in:
Preston Thorpe
2025-09-17 11:25:13 -04:00
committed by GitHub
3 changed files with 87 additions and 56 deletions

View File

@@ -305,7 +305,30 @@ do_execsql_test_on_specific_db {:memory:} upsert-omitted-target-updates-unique {
SELECT * FROM ou;
} {3|y}
# Target qualified with database.table.column should match too (assuming single db).
do_execsql_test_on_specific_db {:memory:} upsert-current-qualified.1 {
CREATE TABLE dq (a INTEGER UNIQUE, b TEXT);
INSERT INTO dq VALUES (1,'old');
INSERT INTO dq VALUES (1,'new')
ON CONFLICT(dq.a) DO UPDATE SET b = dq.b || '-' || excluded.b;
SELECT * FROM dq;
} {1|old-new}
do_execsql_test_on_specific_db {:memory:} upsert-multicol-set.1 {
CREATE TABLE dq (a INTEGER UNIQUE, b TEXT);
INSERT INTO dq VALUES (1,'old');
INSERT INTO dq VALUES (1,'new')
ON CONFLICT(a) DO UPDATE SET (`a`,`b`) = (`excluded`.`a`, `excluded`.`b`);
SELECT * FROM dq;
} {1|new}
do_execsql_test_on_specific_db {:memory:} upsert-where-predicate.1 {
CREATE TABLE dq (a INTEGER UNIQUE, b TEXT);
INSERT INTO dq VALUES (1,'old');
INSERT INTO dq VALUES (1,'old')
ON CONFLICT(a) DO UPDATE SET b = excluded.b WHERE dq.b != excluded.b;
SELECT * FROM dq;
} {1|old}
do_execsql_test_on_specific_db {:memory:} upsert-doubly-qualified-target {
CREATE TABLE dq (a UNIQUE, b);
INSERT INTO dq VALUES (1,'old');