mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-18 00:54:19 +01:00
1395 lines
55 KiB
Tcl
Executable File
1395 lines
55 KiB
Tcl
Executable File
#!/usr/bin/env tclsh
|
|
set testdir [file dirname $argv0]
|
|
source $testdir/tester.tcl
|
|
source $testdir/sqlite3/tester.tcl
|
|
|
|
# ============================================================================
|
|
# INSERT RETURNING tests
|
|
# ============================================================================
|
|
|
|
# Basic column references
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-single-column {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5) RETURNING id;
|
|
} {1}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-multiple-columns {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5) RETURNING id, name;
|
|
} {1|test}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-all-columns {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5) RETURNING *;
|
|
} {1|test|10.5}
|
|
|
|
# Table-qualified column references
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-table-qualified {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING t.id, t.name;
|
|
} {1|test}
|
|
|
|
# Arbitrary expressions not referencing columns
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-literal {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING 42;
|
|
} {42}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-constant-expression {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING 2 + 3 * 4;
|
|
} {14}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-string-literal {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING 'hello world';
|
|
} {"hello world"}
|
|
|
|
# Expressions referencing result columns
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-column-arithmetic {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10) RETURNING 2 * value;
|
|
} {20}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-complex-expression {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 3) RETURNING x + y * 2;
|
|
} {11}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-multiple-column-expression {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER, c INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3, 4) RETURNING a * b + c;
|
|
} {10}
|
|
|
|
# Function calls
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-function-call {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello') RETURNING upper(name);
|
|
} {HELLO}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-function-multiple-columns {
|
|
CREATE TABLE t (id INTEGER, first TEXT, last TEXT);
|
|
INSERT INTO t VALUES (1, 'john', 'doe') RETURNING upper(first) || ' ' || upper(last);
|
|
} {"JOHN DOE"}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-function-with-expression {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 3) RETURNING abs(x - y);
|
|
} {2}
|
|
|
|
# Mixed expressions
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-mixed-expressions {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value INTEGER);
|
|
INSERT INTO t VALUES (1, 'test', 10) RETURNING id, upper(name), value * 3, 42;
|
|
} {1|TEST|30|42}
|
|
|
|
# Multiple rows
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-multiple-rows {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'first'), (2, 'second'), (3, 'third') RETURNING id, name;
|
|
} {1|first
|
|
2|second
|
|
3|third}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-multiple-rows-expressions {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10), (2, 20), (3, 30) RETURNING id, value * 2;
|
|
} {1|20
|
|
2|40
|
|
3|60}
|
|
|
|
# NULL handling
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-null-values {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value INTEGER);
|
|
INSERT INTO t VALUES (1, NULL, NULL) RETURNING id, name, value;
|
|
} {1||}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-null-expression {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, NULL) RETURNING coalesce(name, 'default');
|
|
} {default}
|
|
|
|
# Rowid
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-rowid {
|
|
CREATE TABLE t (name TEXT);
|
|
INSERT INTO t VALUES ('test') RETURNING rowid, name;
|
|
} {1|test}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-rowid-expression {
|
|
CREATE TABLE t (name TEXT);
|
|
INSERT INTO t VALUES ('test') RETURNING rowid * 2;
|
|
} {2}
|
|
|
|
# Auto-increment
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-autoincrement {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);
|
|
INSERT INTO t (name) VALUES ('test') RETURNING id, name;
|
|
} {1|test}
|
|
|
|
# Complex nested expressions
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-nested-expressions {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER, z INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3, 4) RETURNING (x + y) * (z - 1);
|
|
} {15}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-case-expression {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5) RETURNING CASE WHEN value > 10 THEN 'high' WHEN value > 0 THEN 'low' ELSE 'zero' END;
|
|
} {low}
|
|
|
|
# String operations
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-string-concat {
|
|
CREATE TABLE t (id INTEGER, first TEXT, last TEXT);
|
|
INSERT INTO t VALUES (1, 'John', 'Doe') RETURNING first || ' ' || last;
|
|
} {"John Doe"}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-substring {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello world') RETURNING substr(name, 1, 5);
|
|
} {hello}
|
|
|
|
# ============================================================================
|
|
# INSERT ON CONFLICT RETURNING tests
|
|
# ============================================================================
|
|
|
|
# RETURNING on INSERT path (no conflict)
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-insert-path {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'new')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name
|
|
RETURNING id, name;
|
|
} {1|new}
|
|
|
|
# RETURNING on UPDATE path (conflict occurs)
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-update-path {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'old');
|
|
INSERT INTO t VALUES (1, 'new')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name
|
|
RETURNING id, name;
|
|
} {1|new}
|
|
|
|
# RETURNING on DO NOTHING (should return empty)
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-do-nothing {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'old');
|
|
INSERT INTO t VALUES (1, 'ignored')
|
|
ON CONFLICT DO NOTHING
|
|
RETURNING id, name;
|
|
} {}
|
|
|
|
# RETURNING with table-qualified columns
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-table-qualified {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'old');
|
|
INSERT INTO t VALUES (1, 'new')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name
|
|
RETURNING t.id, t.name;
|
|
} {1|new}
|
|
|
|
# RETURNING with expressions on INSERT path
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-expression-insert {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10)
|
|
ON CONFLICT DO UPDATE SET value = excluded.value
|
|
RETURNING id, value * 2;
|
|
} {1|20}
|
|
|
|
# RETURNING with expressions on UPDATE path
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-expression-update {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5);
|
|
INSERT INTO t VALUES (1, 10)
|
|
ON CONFLICT DO UPDATE SET value = excluded.value
|
|
RETURNING id, value * 3;
|
|
} {1|30}
|
|
|
|
# RETURNING with complex expressions
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-complex-expression {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3);
|
|
INSERT INTO t VALUES (1, 5, 7)
|
|
ON CONFLICT DO UPDATE SET x = excluded.x, y = excluded.y
|
|
RETURNING id, x + y * 2;
|
|
} {1|19}
|
|
|
|
# RETURNING with function calls
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-function {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello');
|
|
INSERT INTO t VALUES (1, 'world')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name
|
|
RETURNING id, upper(name);
|
|
} {1|WORLD}
|
|
|
|
# RETURNING with multiple rows (mixed insert/update)
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-multiple-rows {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'old1'), (2, 'old2');
|
|
INSERT INTO t VALUES (1, 'new1'), (2, 'new2'), (3, 'new3')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name
|
|
RETURNING id, name;
|
|
} {1|new1
|
|
2|new2
|
|
3|new3}
|
|
|
|
# RETURNING with WHERE clause in DO UPDATE
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-where-update {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5);
|
|
INSERT INTO t VALUES (1, 10)
|
|
ON CONFLICT DO UPDATE SET value = excluded.value WHERE excluded.value > t.value
|
|
RETURNING id, value;
|
|
} {1|10}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-where-no-update {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10);
|
|
INSERT INTO t VALUES (1, 5)
|
|
ON CONFLICT DO UPDATE SET value = excluded.value WHERE excluded.value > t.value
|
|
RETURNING id, value;
|
|
} {}
|
|
|
|
# RETURNING with arbitrary expressions not referencing columns
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-literal {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name
|
|
RETURNING 42;
|
|
} {42}
|
|
|
|
# RETURNING with mixed insert/update in single statement (first one doesn't conflict, second one does)
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-mixed-operations {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5);
|
|
INSERT INTO t VALUES (1, 10), (2, 20)
|
|
ON CONFLICT DO UPDATE SET value = excluded.value + t.value
|
|
RETURNING id, value;
|
|
} {1|15
|
|
2|20}
|
|
|
|
# ============================================================================
|
|
# INSERT ... SELECT ... RETURNING tests
|
|
# ============================================================================
|
|
|
|
# Basic INSERT ... SELECT with RETURNING
|
|
do_execsql_test_on_specific_db {:memory:} insert-select-returning-basic {
|
|
CREATE TABLE u (a INTEGER, b INTEGER PRIMARY KEY, c TEXT);
|
|
CREATE TABLE t (a TEXT, b INTEGER, c INTEGER PRIMARY KEY);
|
|
INSERT INTO u VALUES (1, 2, 'test'), (2, 4, 'data'), (3, 6, 'more');
|
|
INSERT INTO t SELECT c, b, b * 2 FROM u RETURNING *;
|
|
} {test|2|4
|
|
data|4|8
|
|
more|6|12}
|
|
|
|
# INSERT ... SELECT with WHERE clause and RETURNING
|
|
do_execsql_test_on_specific_db {:memory:} insert-select-returning-where {
|
|
CREATE TABLE u (a INTEGER, b INTEGER PRIMARY KEY, c TEXT);
|
|
CREATE TABLE t (a TEXT, b INTEGER, c INTEGER PRIMARY KEY);
|
|
INSERT INTO u SELECT value, value * 2, 'kekkers' FROM generate_series(1, 10);
|
|
INSERT INTO t SELECT concat(c, '-lollers') as lul, b, b * 2 FROM u WHERE a > 5 RETURNING *;
|
|
} {kekkers-lollers|12|24
|
|
kekkers-lollers|14|28
|
|
kekkers-lollers|16|32
|
|
kekkers-lollers|18|36
|
|
kekkers-lollers|20|40}
|
|
|
|
# INSERT ... SELECT with RETURNING specific columns
|
|
do_execsql_test_on_specific_db {:memory:} insert-select-returning-columns {
|
|
CREATE TABLE u (a INTEGER, b INTEGER, c TEXT);
|
|
CREATE TABLE t (a TEXT, b INTEGER, c INTEGER);
|
|
INSERT INTO u VALUES (1, 10, 'x'), (2, 20, 'y'), (3, 30, 'z');
|
|
INSERT INTO t SELECT c, b, a FROM u RETURNING a, c;
|
|
} {x|1
|
|
y|2
|
|
z|3}
|
|
|
|
# INSERT ... SELECT with RETURNING expressions
|
|
do_execsql_test_on_specific_db {:memory:} insert-select-returning-expressions {
|
|
CREATE TABLE u (a INTEGER, b INTEGER);
|
|
CREATE TABLE t (x INTEGER, y INTEGER);
|
|
INSERT INTO u VALUES (5, 10), (15, 20);
|
|
INSERT INTO t SELECT a, b FROM u RETURNING x + y, x * y;
|
|
} {15|50
|
|
35|300}
|
|
|
|
# INSERT ... SELECT with RETURNING and function calls
|
|
do_execsql_test_on_specific_db {:memory:} insert-select-returning-functions {
|
|
CREATE TABLE u (name TEXT, value INTEGER);
|
|
CREATE TABLE t (name TEXT, value INTEGER);
|
|
INSERT INTO u VALUES ('hello', 100), ('world', 200);
|
|
INSERT INTO t SELECT name, value FROM u RETURNING upper(name), value / 10;
|
|
} {HELLO|10
|
|
WORLD|20}
|
|
|
|
# INSERT ... SELECT with RETURNING and aggregates in source
|
|
do_execsql_test_on_specific_db {:memory:} insert-select-returning-aggregate-source {
|
|
CREATE TABLE u (category TEXT, amount INTEGER);
|
|
CREATE TABLE t (category TEXT, total INTEGER);
|
|
INSERT INTO u VALUES ('A', 10), ('A', 20), ('B', 30), ('B', 40);
|
|
INSERT INTO t SELECT category, SUM(amount) FROM u GROUP BY category RETURNING *;
|
|
} {A|30
|
|
B|70}
|
|
|
|
# INSERT ... SELECT with RETURNING literal values
|
|
do_execsql_test_on_specific_db {:memory:} insert-select-returning-literals {
|
|
CREATE TABLE u (id INTEGER);
|
|
CREATE TABLE t (id INTEGER, constant INTEGER);
|
|
INSERT INTO u VALUES (1), (2), (3);
|
|
INSERT INTO t SELECT id, 42 FROM u RETURNING constant;
|
|
} {42
|
|
42
|
|
42}
|
|
|
|
# INSERT ... SELECT with RETURNING and ORDER BY in source
|
|
do_execsql_test_on_specific_db {:memory:} insert-select-returning-ordered {
|
|
CREATE TABLE u (value INTEGER);
|
|
CREATE TABLE t (value INTEGER);
|
|
INSERT INTO u VALUES (30), (10), (20);
|
|
INSERT INTO t SELECT value FROM u ORDER BY value RETURNING value;
|
|
} {10
|
|
20
|
|
30}
|
|
|
|
# ============================================================================
|
|
# UPDATE RETURNING tests
|
|
# ============================================================================
|
|
|
|
# Basic column references
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-single-column {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5);
|
|
UPDATE t SET value = 20.5 WHERE id = 1 RETURNING id;
|
|
} {1}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-multiple-columns {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5);
|
|
UPDATE t SET value = 20.5 WHERE id = 1 RETURNING id, name, value;
|
|
} {1|test|20.5}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-all-columns {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5);
|
|
UPDATE t SET value = 20.5 WHERE id = 1 RETURNING *;
|
|
} {1|test|20.5}
|
|
|
|
# Table-qualified column references
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-table-qualified {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'old');
|
|
UPDATE t SET name = 'new' WHERE id = 1 RETURNING t.id, t.name;
|
|
} {1|new}
|
|
|
|
# Arbitrary expressions not referencing columns
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-literal {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10);
|
|
UPDATE t SET value = 20 WHERE id = 1 RETURNING 42;
|
|
} {42}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-constant-expression {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1);
|
|
UPDATE t SET id = 2 RETURNING 2 + 3 * 4;
|
|
} {14}
|
|
|
|
# Expressions referencing updated columns
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-column-arithmetic {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10);
|
|
UPDATE t SET value = 20 WHERE id = 1 RETURNING 2 * value;
|
|
} {40}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-complex-expression {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 3);
|
|
UPDATE t SET x = 8 WHERE id = 1 RETURNING x + y * 2;
|
|
} {14}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-multiple-column-expression {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER, c INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3, 4);
|
|
UPDATE t SET a = 5, b = 6 WHERE id = 1 RETURNING a * b + c;
|
|
} {34}
|
|
|
|
# Function calls
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-function-call {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello');
|
|
UPDATE t SET name = 'world' WHERE id = 1 RETURNING upper(name);
|
|
} {WORLD}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-function-multiple-columns {
|
|
CREATE TABLE t (id INTEGER, first TEXT, last TEXT);
|
|
INSERT INTO t VALUES (1, 'john', 'doe');
|
|
UPDATE t SET first = 'jane', last = 'smith' WHERE id = 1 RETURNING upper(first) || ' ' || upper(last);
|
|
} {"JANE SMITH"}
|
|
|
|
# Mixed expressions
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-mixed-expressions {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value INTEGER);
|
|
INSERT INTO t VALUES (1, 'test', 10);
|
|
UPDATE t SET name = 'updated', value = 30 WHERE id = 1 RETURNING id, upper(name), value * 2, 42;
|
|
} {1|UPDATED|60|42}
|
|
|
|
# Multiple rows
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-multiple-rows {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'first'), (2, 'second'), (3, 'third');
|
|
UPDATE t SET name = 'updated' RETURNING id, name;
|
|
} {1|updated
|
|
2|updated
|
|
3|updated}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-multiple-rows-expressions {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10), (2, 20), (3, 30);
|
|
UPDATE t SET value = value * 2 RETURNING id, value;
|
|
} {1|20
|
|
2|40
|
|
3|60}
|
|
|
|
# WHERE clause filtering
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-with-where {
|
|
CREATE TABLE t (id INTEGER, name TEXT, active INTEGER);
|
|
INSERT INTO t VALUES (1, 'first', 1), (2, 'second', 0), (3, 'third', 1);
|
|
UPDATE t SET name = 'updated' WHERE active = 1 RETURNING id, name;
|
|
} {1|updated
|
|
3|updated}
|
|
|
|
# Old vs new values
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-old-vs-new-values {
|
|
CREATE TABLE t (id INTEGER, counter INTEGER);
|
|
INSERT INTO t VALUES (1, 5);
|
|
UPDATE t SET counter = counter + 10 WHERE id = 1 RETURNING id, counter;
|
|
} {1|15}
|
|
|
|
# NULL handling
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-null-values {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value INTEGER);
|
|
INSERT INTO t VALUES (1, 'test', 10);
|
|
UPDATE t SET name = NULL, value = NULL WHERE id = 1 RETURNING id, name, value;
|
|
} {1||}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-null-expression {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test');
|
|
UPDATE t SET name = NULL WHERE id = 1 RETURNING coalesce(name, 'default');
|
|
} {default}
|
|
|
|
# Rowid
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-rowid {
|
|
CREATE TABLE t (name TEXT);
|
|
INSERT INTO t VALUES ('test');
|
|
UPDATE t SET name = 'updated' RETURNING rowid, name;
|
|
} {1|updated}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-rowid-expression {
|
|
CREATE TABLE t (name TEXT);
|
|
INSERT INTO t VALUES ('test');
|
|
UPDATE t SET name = 'updated' RETURNING rowid * 2;
|
|
} {2}
|
|
|
|
# Complex nested expressions
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-nested-expressions {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER, z INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3, 4);
|
|
UPDATE t SET x = 5, y = 6 WHERE id = 1 RETURNING (x + y) * (z - 1);
|
|
} {33}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-case-expression {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5);
|
|
UPDATE t SET value = 15 WHERE id = 1 RETURNING CASE WHEN value > 10 THEN 'high' WHEN value > 0 THEN 'low' ELSE 'zero' END;
|
|
} {high}
|
|
|
|
# String operations
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-string-concat {
|
|
CREATE TABLE t (id INTEGER, first TEXT, last TEXT);
|
|
INSERT INTO t VALUES (1, 'John', 'Doe');
|
|
UPDATE t SET first = 'Jane', last = 'Smith' WHERE id = 1 RETURNING first || ' ' || last;
|
|
} {"Jane Smith"}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-substring {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello world');
|
|
UPDATE t SET name = 'goodbye world' WHERE id = 1 RETURNING substr(name, 1, 7);
|
|
} {goodbye}
|
|
|
|
# Row value updates
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-row-values {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test');
|
|
UPDATE t SET (id, name) = (2, 'mordor') RETURNING id, name;
|
|
} {2|mordor}
|
|
|
|
# ============================================================================
|
|
# DELETE RETURNING tests
|
|
# ============================================================================
|
|
|
|
# Basic column references
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-single-column {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5);
|
|
DELETE FROM t WHERE id = 1 RETURNING id;
|
|
} {1}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-multiple-columns {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5);
|
|
DELETE FROM t WHERE id = 1 RETURNING id, name;
|
|
} {1|test}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-all-columns {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5);
|
|
DELETE FROM t WHERE id = 1 RETURNING *;
|
|
} {1|test|10.5}
|
|
|
|
# Table-qualified column references
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-table-qualified {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test');
|
|
DELETE FROM t WHERE id = 1 RETURNING t.id, t.name;
|
|
} {1|test}
|
|
|
|
# Arbitrary expressions not referencing columns
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-literal {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1);
|
|
DELETE FROM t WHERE id = 1 RETURNING 42;
|
|
} {42}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-constant-expression {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1);
|
|
DELETE FROM t WHERE id = 1 RETURNING 2 + 3 * 4;
|
|
} {14}
|
|
|
|
# Expressions referencing deleted columns
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-column-arithmetic {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10);
|
|
DELETE FROM t WHERE id = 1 RETURNING 2 * value;
|
|
} {20}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-complex-expression {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 3);
|
|
DELETE FROM t WHERE id = 1 RETURNING x + y * 2;
|
|
} {11}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-function-call {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello');
|
|
DELETE FROM t WHERE id = 1 RETURNING upper(name);
|
|
} {HELLO}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-mixed-expressions {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value INTEGER);
|
|
INSERT INTO t VALUES (1, 'test', 10);
|
|
DELETE FROM t WHERE id = 1 RETURNING id, upper(name), value * 3;
|
|
} {1|TEST|30}
|
|
|
|
# Multiple rows deleted
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-multiple-rows {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'first'), (2, 'second'), (3, 'third');
|
|
DELETE FROM t RETURNING id, name;
|
|
} {1|first
|
|
2|second
|
|
3|third}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-multiple-rows-expressions {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10), (2, 20), (3, 30);
|
|
DELETE FROM t RETURNING id, value * 2;
|
|
} {1|20
|
|
2|40
|
|
3|60}
|
|
|
|
# NULL handling
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-null-values {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value INTEGER);
|
|
INSERT INTO t VALUES (1, NULL, NULL);
|
|
DELETE FROM t WHERE id = 1 RETURNING id, name, value;
|
|
} {1||}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-null-expression {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, NULL);
|
|
DELETE FROM t WHERE id = 1 RETURNING coalesce(name, 'default');
|
|
} {default}
|
|
|
|
# Rowid
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-rowid {
|
|
CREATE TABLE t (name TEXT);
|
|
INSERT INTO t VALUES ('test');
|
|
DELETE FROM t RETURNING rowid, name;
|
|
} {1|test}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-rowid-expression {
|
|
CREATE TABLE t (name TEXT);
|
|
INSERT INTO t VALUES ('test');
|
|
DELETE FROM t RETURNING rowid * 2;
|
|
} {2}
|
|
|
|
# WHERE clause filtering
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-with-where {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'a'), (2, 'b'), (3, 'c');
|
|
DELETE FROM t WHERE id > 1 RETURNING id, name;
|
|
} {2|b
|
|
3|c}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-no-matches {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test');
|
|
DELETE FROM t WHERE id = 999 RETURNING *;
|
|
} {}
|
|
|
|
# Expressions with string operations
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-string-concat {
|
|
CREATE TABLE t (id INTEGER, first TEXT, last TEXT);
|
|
INSERT INTO t VALUES (1, 'John', 'Doe');
|
|
DELETE FROM t WHERE id = 1 RETURNING first || ' ' || last;
|
|
} {"John Doe"}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-substring {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello world');
|
|
DELETE FROM t WHERE id = 1 RETURNING substr(name, 1, 5);
|
|
} {hello}
|
|
|
|
# Case expressions
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-case-expression {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5);
|
|
DELETE FROM t WHERE id = 1 RETURNING CASE WHEN value > 10 THEN 'high' WHEN value > 0 THEN 'low' ELSE 'zero' END;
|
|
} {low}
|
|
|
|
# Nested expressions
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-nested-expressions {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER, z INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3, 4);
|
|
DELETE FROM t WHERE id = 1 RETURNING (x + y) * (z - 1);
|
|
} {15}
|
|
|
|
# ============================================================================
|
|
# DELETE RETURNING NASTY EDGE CASES
|
|
# ============================================================================
|
|
|
|
# Multiple references to same column in RETURNING
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-multiple-column-references {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER);
|
|
INSERT INTO t VALUES (1, 10), (2, 20), (3, 30);
|
|
DELETE FROM t WHERE id = 2 RETURNING x, x * 2, x + x;
|
|
} {20|40|40}
|
|
|
|
# Column aliases in RETURNING
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-column-aliases {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 1, 2), (2, 3, 4);
|
|
DELETE FROM t WHERE x = 1 RETURNING x AS deleted_x, y AS deleted_y, x + y AS sum;
|
|
} {1|2|3}
|
|
|
|
# Complex WHERE clauses with IN
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-where-in {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);
|
|
INSERT INTO t VALUES (1, 1), (2, 2), (3, 3);
|
|
DELETE FROM t WHERE val IN (1, 3) RETURNING *;
|
|
} {1|1
|
|
3|3}
|
|
|
|
# Complex WHERE clauses with BETWEEN
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-where-between {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 1, 10), (2, 2, 20), (3, 3, 30);
|
|
DELETE FROM t WHERE x BETWEEN 1 AND 2 RETURNING id, x, y, x + y;
|
|
} {1|1|10|11
|
|
2|2|20|22}
|
|
|
|
# Complex WHERE clauses with LIKE
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-where-like {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');
|
|
DELETE FROM t WHERE name LIKE 'B%' RETURNING *;
|
|
} {2|Bob}
|
|
|
|
# Complex WHERE clauses with AND/OR
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-where-complex {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER);
|
|
INSERT INTO t VALUES (1, 5), (2, 10), (3, 15);
|
|
DELETE FROM t WHERE x > 5 AND x < 15 RETURNING id, x, x * x, x + x + x;
|
|
} {2|10|100|30}
|
|
|
|
# WHERE clause with modulo operator
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-where-modulo {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, val INTEGER);
|
|
INSERT INTO t VALUES (1, 'a', 1), (2, 'b', 2), (3, 'c', 3);
|
|
DELETE FROM t WHERE val % 2 = 0 RETURNING id, name, val, length(name), abs(val);
|
|
} {2|b|2|1|2}
|
|
|
|
# WHERE clause with subquery (correlated) - NOT SUPPORTED YET
|
|
# do_execsql_test_on_specific_db {:memory:} delete-returning-where-subquery {
|
|
# CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER);
|
|
# INSERT INTO t VALUES (1, 1), (2, 2), (3, 3);
|
|
# DELETE FROM t WHERE x = (SELECT MAX(x) FROM t) RETURNING *;
|
|
# } {3|3}
|
|
|
|
# WHERE clause with IN subquery - NOT SUPPORTED YET
|
|
# do_execsql_test_on_specific_db {:memory:} delete-returning-where-in-subquery {
|
|
# CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER);
|
|
# INSERT INTO t VALUES (1, 1, 2), (2, 3, 4), (3, 5, 6);
|
|
# DELETE FROM t WHERE a IN (SELECT a FROM t WHERE a > 3) RETURNING *;
|
|
# } {3|5|6}
|
|
|
|
# WHERE clause that matches nothing
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-where-false {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test');
|
|
DELETE FROM t WHERE 1=0 RETURNING *;
|
|
} {}
|
|
|
|
# Rowid and INTEGER PRIMARY KEY together
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-rowid-and-pk {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'a'), (2, 'b');
|
|
DELETE FROM t RETURNING rowid, id, name;
|
|
} {1|1|a
|
|
2|2|b}
|
|
|
|
# AUTOINCREMENT with RETURNING
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-autoincrement {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);
|
|
INSERT INTO t VALUES (NULL, 'a'), (NULL, 'b');
|
|
DELETE FROM t RETURNING id, name;
|
|
} {1|a
|
|
2|b}
|
|
|
|
# Type functions in RETURNING
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-typeof {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x REAL, y TEXT);
|
|
INSERT INTO t VALUES (1, 1.5, 'test'), (2, 2.5, 'foo');
|
|
DELETE FROM t RETURNING id, x, y, typeof(x), typeof(y);
|
|
} {1|1.5|test|real|text
|
|
2|2.5|foo|real|text}
|
|
|
|
# NULL handling with coalesce
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-null-coalesce {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);
|
|
INSERT INTO t VALUES (1, NULL), (2, 42), (3, NULL);
|
|
DELETE FROM t WHERE val IS NULL RETURNING id, val, coalesce(val, 999);
|
|
} {1||999
|
|
3||999}
|
|
|
|
# BLOB handling
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-blob {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, val BLOB);
|
|
INSERT INTO t VALUES (1, zeroblob(10)), (2, x'0102');
|
|
DELETE FROM t RETURNING id, length(val), typeof(val);
|
|
} {1|10|blob
|
|
2|2|blob}
|
|
|
|
# printf function in RETURNING
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-printf {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello'), (2, 'world');
|
|
DELETE FROM t WHERE name = 'hello' RETURNING printf('Deleted: %s', name);
|
|
} {"Deleted: hello"}
|
|
|
|
# Additional edge cases
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-empty-string-null {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello'), (2, ''), (3, NULL);
|
|
DELETE FROM t RETURNING id, name, length(name), name IS NULL;
|
|
} {1|hello|5|0
|
|
2||0|0
|
|
3|||1}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-string-concat-numbers {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 1, 2), (2, 3, 4);
|
|
DELETE FROM t WHERE x = 1 RETURNING x, y, x + y, x * y, x || y;
|
|
} {1|2|3|2|12}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-round-precision {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, val REAL);
|
|
INSERT INTO t VALUES (1, 1.23456789), (2, 9.999999);
|
|
DELETE FROM t RETURNING id, val, round(val, 2);
|
|
} {1|1.23456789|1.23
|
|
2|9.999999|10.0}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-glob-pattern {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'Alice'), (2, 'Bob'), (3, 'Charlie');
|
|
DELETE FROM t WHERE name GLOB '[BC]*' RETURNING *;
|
|
} {2|Bob
|
|
3|Charlie}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-operator-precedence {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, a INTEGER, b INTEGER, c INTEGER);
|
|
INSERT INTO t VALUES (1, 1, 2, 3), (2, 4, 5, 6);
|
|
DELETE FROM t RETURNING a, b, c, a + b * c, (a + b) * c;
|
|
} {1|2|3|7|9
|
|
4|5|6|34|54}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-large-numbers {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);
|
|
INSERT INTO t VALUES (1, 999999999), (2, -999999999), (3, 0);
|
|
DELETE FROM t RETURNING id, val, val + 1, val * 2;
|
|
} {1|999999999|1000000000|1999999998
|
|
2|-999999999|-999999998|-1999999998
|
|
3|0|1|0}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-nullif-coalesce {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test'), (2, 'test'), (3, 'other');
|
|
DELETE FROM t WHERE name = 'test' RETURNING id, name, NULLIF(name, 'test'), COALESCE(NULLIF(name, 'test'), 'was_test');
|
|
} {1|test||was_test
|
|
2|test||was_test}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-iif-null-handling {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 10), (2, NULL, 20), (3, 15, NULL);
|
|
DELETE FROM t RETURNING id, x, y, IIF(x IS NULL OR y IS NULL, 'has_null', x + y);
|
|
} {1|5|10|15
|
|
2||20|has_null
|
|
3|15||has_null}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-string-functions {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello'), (2, 'world');
|
|
DELETE FROM t RETURNING id, name, upper(name), lower(name), length(name);
|
|
} {1|hello|HELLO|hello|5
|
|
2|world|WORLD|world|5}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-replace-function {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello world'), (2, 'foo bar');
|
|
DELETE FROM t RETURNING id, name, replace(name, ' ', '_') AS replaced;
|
|
} {"1|hello world|hello_world
|
|
2|foo bar|foo_bar"}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-abs-sign {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, val INTEGER);
|
|
INSERT INTO t VALUES (1, 42), (2, -10), (3, 0);
|
|
DELETE FROM t RETURNING id, val, abs(val), sign(val);
|
|
} {1|42|42|1
|
|
2|-10|10|-1
|
|
3|0|0|0}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-coalesce-arithmetic {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 5, NULL), (2, NULL, 10), (3, NULL, NULL);
|
|
DELETE FROM t RETURNING id, x, y, coalesce(x, 0) + coalesce(y, 0);
|
|
} {1|5||5
|
|
2||10|10
|
|
3|||0}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-case-expression {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT, val INTEGER);
|
|
INSERT INTO t VALUES (1, 'test', 10), (2, 'test', 20);
|
|
DELETE FROM t WHERE val = 10 RETURNING id, name, val, CASE WHEN val > 15 THEN 'high' ELSE 'low' END;
|
|
} {1|test|10|low}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-type-cast {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, x REAL, y INTEGER);
|
|
INSERT INTO t VALUES (1, 1.5, 2), (2, 3.7, 4);
|
|
DELETE FROM t RETURNING id, x, y, round(x), cast(x AS INTEGER);
|
|
} {1|1.5|2|2.0|1
|
|
2|3.7|4|4.0|3}
|
|
|
|
# ============================================================================
|
|
# Edge cases and complex scenarios
|
|
# ============================================================================
|
|
|
|
# RETURNING expressions that don't reference any columns
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-no-column-reference {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING 1 + 1, 'constant', NULL;
|
|
} {2|constant|}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-no-column-reference {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1);
|
|
UPDATE t SET id = 2 RETURNING 1 + 1, 'constant', NULL;
|
|
} {2|constant|}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} delete-returning-no-column-reference {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1);
|
|
DELETE FROM t WHERE id = 1 RETURNING 1 + 1, 'constant', NULL;
|
|
} {2|constant|}
|
|
|
|
# RETURNING with aggregate-like expressions (should work per-row)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-sum-expression {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3) RETURNING a + b;
|
|
} {5}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-sum-expression {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3);
|
|
UPDATE t SET a = 5, b = 7 WHERE id = 1 RETURNING a + b;
|
|
} {12}
|
|
|
|
# ============================================================================
|
|
# NASTY EDGE CASES - Things that should work but might break
|
|
# ============================================================================
|
|
|
|
# Column name same as table name - ambiguity resolution
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-column-same-as-table-name {
|
|
CREATE TABLE t (t INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING t.t, t.name;
|
|
} {1|test}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-column-same-as-table-name-unqualified {
|
|
CREATE TABLE t (t INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING t, name;
|
|
} {1|test}
|
|
|
|
# RETURNING with rowid when INTEGER PRIMARY KEY exists - can reference both
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-rowid-and-pk-alias {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING rowid, id, name;
|
|
} {1|1|test}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-rowid-and-pk-alias {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name TEXT);
|
|
INSERT INTO t VALUES (1, 'old');
|
|
UPDATE t SET name = 'new' WHERE id = 1 RETURNING rowid, id, name;
|
|
} {1|1|new}
|
|
|
|
# RETURNING with expressions referencing updated columns in UPDATE
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-reference-updated-column {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 10);
|
|
UPDATE t SET x = 20 WHERE id = 1 RETURNING x, x * 2, x + y;
|
|
} {20|40|30}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-reference-multiple-updated-columns {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER, c INTEGER);
|
|
INSERT INTO t VALUES (1, 1, 2, 3);
|
|
UPDATE t SET a = 10, b = 20 WHERE id = 1 RETURNING a, b, a + b, a * b + c;
|
|
} {10|20|30|203}
|
|
|
|
# RETURNING with NULLIF edge cases
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-nullif {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5) RETURNING NULLIF(value, 5);
|
|
} {}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-nullif-no-match {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5) RETURNING NULLIF(value, 10);
|
|
} {5}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-nullif {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5);
|
|
UPDATE t SET value = 10 WHERE id = 1 RETURNING NULLIF(value, 5);
|
|
} {10}
|
|
|
|
# RETURNING with COALESCE multiple arguments
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-coalesce-multiple {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER, c INTEGER);
|
|
INSERT INTO t VALUES (1, NULL, NULL, 42) RETURNING COALESCE(a, b, c);
|
|
} {42}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-coalesce-all-null {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER);
|
|
INSERT INTO t VALUES (1, NULL, NULL) RETURNING COALESCE(a, b, 'default');
|
|
} {default}
|
|
|
|
# RETURNING with arithmetic on NULL
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-null-arithmetic {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, NULL, 5) RETURNING x + y, x * y, x - y;
|
|
} {||}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-null-arithmetic {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 10, 5);
|
|
UPDATE t SET x = NULL WHERE id = 1 RETURNING x + y, x * y;
|
|
} {|}
|
|
|
|
# RETURNING with string functions on NULL
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-string-func-null {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, NULL) RETURNING upper(name), length(name), substr(name, 1, 5);
|
|
} {||}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-string-func-null {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test');
|
|
UPDATE t SET name = NULL WHERE id = 1 RETURNING upper(name), length(name);
|
|
} {|}
|
|
|
|
# RETURNING with CASE expressions having NULL branches
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-case-with-null {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5) RETURNING CASE WHEN value > 10 THEN 'high' WHEN value > 0 THEN NULL ELSE 'zero' END;
|
|
} {}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-case-nested {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 10) RETURNING CASE WHEN a > b THEN a WHEN a < b THEN b ELSE NULL END;
|
|
} {10}
|
|
|
|
# RETURNING with type conversions
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-type-conversion {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 42) RETURNING CAST(value AS TEXT), CAST(value AS REAL);
|
|
} {42|42.0}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-type-conversion {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 42);
|
|
UPDATE t SET value = 100 WHERE id = 1 RETURNING CAST(value AS TEXT);
|
|
} {100}
|
|
|
|
# RETURNING when updating rowid directly (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} update-rowid-returning {
|
|
CREATE TABLE t (name TEXT);
|
|
INSERT INTO t VALUES ('test');
|
|
UPDATE t SET rowid = 999 WHERE rowid = 1 RETURNING rowid, name;
|
|
} {999|test}
|
|
|
|
# RETURNING with expressions referencing columns not in result set
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-reference-hidden-column {
|
|
CREATE TABLE t (id INTEGER, secret INTEGER, public INTEGER);
|
|
INSERT INTO t VALUES (1, 100, 50) RETURNING public, secret * 2;
|
|
} {50|200}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-reference-hidden-column {
|
|
CREATE TABLE t (id INTEGER, secret INTEGER, public INTEGER);
|
|
INSERT INTO t VALUES (1, 100, 50);
|
|
UPDATE t SET public = 75 WHERE id = 1 RETURNING public, secret + public;
|
|
} {75|175}
|
|
|
|
# RETURNING with duplicate column names in result (should work)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-duplicate-column-names {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING id, id, name, name;
|
|
} {1|1|test|test}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-duplicate-column-names {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10);
|
|
UPDATE t SET value = 20 WHERE id = 1 RETURNING value, value * 2, value;
|
|
} {20|40|20}
|
|
|
|
# RETURNING with expressions using column names as strings (should not work, but test anyway)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-column-name-as-string {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING 'id', 'name';
|
|
} {id|name}
|
|
|
|
# RETURNING with excluded in regular INSERT (should error)
|
|
do_execsql_test_in_memory_any_error insert-returning-excluded-not-upsert {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING excluded.name;
|
|
}
|
|
|
|
# RETURNING with expressions referencing columns that were updated to NULL
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-updated-to-null {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 42);
|
|
UPDATE t SET value = NULL WHERE id = 1 RETURNING value, COALESCE(value, 0);
|
|
} {|0}
|
|
|
|
# RETURNING with expressions referencing columns that were updated from NULL
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-updated-from-null {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, NULL);
|
|
UPDATE t SET value = 42 WHERE id = 1 RETURNING value, COALESCE(value, 0);
|
|
} {42|42}
|
|
|
|
# RETURNING with expressions that use backticks
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-backtick-columns {
|
|
CREATE TABLE t (`id` INTEGER, `name` TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING `id`, `name`;
|
|
} {1|test}
|
|
|
|
# RETURNING with expressions that use double quotes (if DQS enabled)
|
|
do_execsql_test_skip_lines_on_specific_db 1 {:memory:} insert-returning-double-quote-columns {
|
|
.dbconfig dqs_dml on
|
|
CREATE TABLE t ("id" INTEGER, "name" TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING "id", "name";
|
|
} {1|test}
|
|
|
|
# RETURNING with expressions referencing columns in different cases (case sensitivity)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-case-insensitive {
|
|
CREATE TABLE t (ID INTEGER, Name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING id, name, ID, Name;
|
|
} {1|test|1|test}
|
|
|
|
# RETURNING with expressions that reference columns that don't exist (should error)
|
|
do_execsql_test_in_memory_any_error insert-returning-nonexistent-column {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING nonexistent;
|
|
}
|
|
|
|
do_execsql_test_in_memory_any_error update-returning-nonexistent-column {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1);
|
|
UPDATE t SET id = 2 RETURNING nonexistent;
|
|
}
|
|
|
|
# RETURNING with expressions that reference wrong table (should error)
|
|
do_execsql_test_in_memory_any_error insert-returning-wrong-table {
|
|
CREATE TABLE t1 (id INTEGER);
|
|
CREATE TABLE t2 (id INTEGER);
|
|
INSERT INTO t1 VALUES (1) RETURNING t2.id;
|
|
}
|
|
|
|
# RETURNING with expressions that mix table-qualified and unqualified
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-mixed-qualification {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test') RETURNING t.id, name, t.name, id;
|
|
} {1|test|test|1}
|
|
|
|
# RETURNING with expressions that reference columns updated in SET clause
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-set-clause-reference {
|
|
CREATE TABLE t (id INTEGER, x INTEGER, y INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 10);
|
|
UPDATE t SET x = 20, y = x + 10 WHERE id = 1 RETURNING x, y;
|
|
} {20|15}
|
|
|
|
# RETURNING with expressions that reference columns in order of update
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-order-dependent {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER);
|
|
INSERT INTO t VALUES (1, 1, 2);
|
|
UPDATE t SET a = b, b = a WHERE id = 1 RETURNING a, b;
|
|
} {2|1}
|
|
|
|
# RETURNING with empty result set (no rows affected)
|
|
do_execsql_test_on_specific_db {:memory:} update-returning-no-rows {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'test');
|
|
UPDATE t SET name = 'updated' WHERE id = 999 RETURNING id, name;
|
|
} {}
|
|
|
|
# RETURNING with expressions using || operator on NULL
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-concat-null {
|
|
CREATE TABLE t (id INTEGER, first TEXT, last TEXT);
|
|
INSERT INTO t VALUES (1, NULL, 'Doe') RETURNING first || ' ' || last;
|
|
} {}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-concat-both-null {
|
|
CREATE TABLE t (id INTEGER, first TEXT, last TEXT);
|
|
INSERT INTO t VALUES (1, NULL, NULL) RETURNING first || ' ' || last;
|
|
} {}
|
|
|
|
# RETURNING with expressions using IN operator
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-in-operator {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5) RETURNING value IN (1, 2, 3, 4, 5);
|
|
} {1}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-in-operator-false {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 10) RETURNING value IN (1, 2, 3, 4, 5);
|
|
} {0}
|
|
|
|
# RETURNING with expressions using LIKE operator
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-like-operator {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello world') RETURNING name LIKE 'hello%';
|
|
} {1}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-like-operator-false {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello world') RETURNING name LIKE 'goodbye%';
|
|
} {0}
|
|
|
|
# RETURNING with expressions using IS NULL
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-is-null {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, NULL) RETURNING value IS NULL;
|
|
} {1}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-is-not-null {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 42) RETURNING value IS NOT NULL;
|
|
} {1}
|
|
|
|
# RETURNING with expressions using BETWEEN
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-between {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5) RETURNING value BETWEEN 1 AND 10;
|
|
} {1}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-between-false {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 15) RETURNING value BETWEEN 1 AND 10;
|
|
} {0}
|
|
|
|
# RETURNING with expressions using GLOB (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-glob {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello.txt') RETURNING name GLOB '*.txt';
|
|
} {1}
|
|
|
|
# RETURNING with expressions that overflow (large numbers)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-large-number {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 9223372036854775807) RETURNING value;
|
|
} {9223372036854775807}
|
|
|
|
# RETURNING with expressions using ABS on negative
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-abs-negative {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, -42) RETURNING ABS(value);
|
|
} {42}
|
|
|
|
# RETURNING with expressions using MAX/MIN (should work per-row, not aggregate)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-max-min-per-row {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER);
|
|
INSERT INTO t VALUES (1, 5, 10) RETURNING MAX(a, b), MIN(a, b);
|
|
} {10|5}
|
|
|
|
# RETURNING with expressions using ROUND
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-round {
|
|
CREATE TABLE t (id INTEGER, value REAL);
|
|
INSERT INTO t VALUES (1, 3.14159) RETURNING ROUND(value, 2);
|
|
} {3.14}
|
|
|
|
# RETURNING with expressions using LENGTH on empty string
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-length-empty {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, '') RETURNING LENGTH(name);
|
|
} {0}
|
|
|
|
# RETURNING with expressions using TRIM
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-trim {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, ' hello ') RETURNING TRIM(name);
|
|
} {hello}
|
|
|
|
# RETURNING with expressions using REPLACE
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-replace {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello world') RETURNING REPLACE(name, 'world', 'universe');
|
|
} {"hello universe"}
|
|
|
|
# RETURNING with expressions using LOWER/UPPER on mixed case
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-lower-upper {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'HeLLo WoRLd') RETURNING LOWER(name), UPPER(name);
|
|
} {"hello world|HELLO WORLD"}
|
|
|
|
# RETURNING with expressions using SUBSTR with negative start
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-substr-negative {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'hello') RETURNING SUBSTR(name, -3);
|
|
} {llo}
|
|
|
|
# RETURNING with expressions using CHAR (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-char {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 65) RETURNING CHAR(value);
|
|
} {A}
|
|
|
|
# RETURNING with expressions using UNICODE (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-unicode {
|
|
CREATE TABLE t (id INTEGER, name TEXT);
|
|
INSERT INTO t VALUES (1, 'A') RETURNING UNICODE(name);
|
|
} {65}
|
|
|
|
# RETURNING with expressions using DATE functions (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-date {
|
|
CREATE TABLE t (id INTEGER, date_text TEXT);
|
|
INSERT INTO t VALUES (1, '2023-01-01') RETURNING DATE(date_text);
|
|
} {2023-01-01}
|
|
|
|
# RETURNING with expressions using JULIANDAY (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-julianday {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING JULIANDAY('2023-01-01');
|
|
} {2459945.5}
|
|
|
|
# RETURNING with expressions using STRFTIME (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-strftime {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING STRFTIME('%Y-%m-%d', '2023-01-01');
|
|
} {2023-01-01}
|
|
|
|
# RETURNING with expressions using JSON functions (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-json {
|
|
CREATE TABLE t (id INTEGER, data TEXT);
|
|
INSERT INTO t VALUES (1, '{"key": "value"}') RETURNING JSON_EXTRACT(data, '$.key');
|
|
} {"value"}
|
|
|
|
# RETURNING with expressions using typeof
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-typeof {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value REAL);
|
|
INSERT INTO t VALUES (1, 'test', 10.5) RETURNING typeof(id), typeof(name), typeof(value);
|
|
} {integer|text|real}
|
|
|
|
# RETURNING with expressions using typeof on NULL
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-typeof-null {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, NULL) RETURNING typeof(value);
|
|
} {null}
|
|
|
|
# RETURNING with expressions using printf (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-printf {
|
|
CREATE TABLE t (id INTEGER, name TEXT, value INTEGER);
|
|
INSERT INTO t VALUES (1, 'test', 42) RETURNING printf('id=%d name=%s value=%d', id, name, value);
|
|
} {"id=1 name=test value=42"}
|
|
|
|
# RETURNING with expressions using randomblob/zeroblob (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-zeroblob {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING LENGTH(zeroblob(100));
|
|
} {100}
|
|
|
|
# RETURNING with expressions using changes (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-changes {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING changes();
|
|
} {0}
|
|
|
|
# RETURNING with expressions using last_insert_rowid (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-last-insert-rowid {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY AUTOINCREMENT, name TEXT);
|
|
INSERT INTO t (name) VALUES ('test') RETURNING last_insert_rowid();
|
|
} {1}
|
|
|
|
# RETURNING with expressions using total_changes (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-total-changes {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING total_changes();
|
|
} {0}
|
|
|
|
# RETURNING with expressions using likely/unlikely (optimization hints)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-likely {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5) RETURNING likely(value > 0);
|
|
} {1}
|
|
|
|
# RETURNING with expressions using iif (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-iif {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 5) RETURNING IIF(value > 0, 'positive', 'negative');
|
|
} {positive}
|
|
|
|
# RETURNING with expressions using json_array/json_object (if supported)
|
|
do_execsql_test_on_specific_db {:memory:} insert-returning-json-array {
|
|
CREATE TABLE t (id INTEGER, a INTEGER, b INTEGER);
|
|
INSERT INTO t VALUES (1, 2, 3) RETURNING JSON_ARRAY(a, b);
|
|
} {[2,3]}
|
|
|
|
# RETURNING with expressions using aggregate functions (should error)
|
|
do_execsql_test_in_memory_any_error insert-returning-aggregate {
|
|
CREATE TABLE t (id INTEGER, value INTEGER);
|
|
INSERT INTO t VALUES (1, 42) RETURNING SUM(value);
|
|
}
|
|
|
|
do_execsql_test_in_memory_any_error insert-returning-aggregate-count {
|
|
CREATE TABLE t (id INTEGER);
|
|
INSERT INTO t VALUES (1) RETURNING COUNT(*);
|
|
} |