mirror of
https://github.com/aljazceru/turso.git
synced 2025-12-17 08:34:19 +01:00
415 lines
16 KiB
Tcl
Executable File
415 lines
16 KiB
Tcl
Executable File
#!/usr/bin/env tclsh
|
|
set testdir [file dirname $argv0]
|
|
source $testdir/tester.tcl
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-pk-update {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name);
|
|
INSERT INTO t VALUES (1,'old');
|
|
INSERT INTO t VALUES (1,'new') ON CONFLICT DO UPDATE SET name = excluded.name;
|
|
SELECT * FROM t;
|
|
} {1|new}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-pk-do-nothing {
|
|
CREATE TABLE t (id INTEGER PRIMARY KEY, name);
|
|
INSERT INTO t VALUES (1,'new');
|
|
INSERT INTO t VALUES (1,'ignored') ON CONFLICT DO NOTHING;
|
|
SELECT * FROM t;
|
|
} {1|new}
|
|
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-unique-update {
|
|
CREATE TABLE u (a, b, c);
|
|
CREATE UNIQUE INDEX u_a ON u(a);
|
|
INSERT INTO u VALUES (1,10,100);
|
|
INSERT INTO u VALUES (1,20,200) ON CONFLICT(a) DO UPDATE SET b = excluded.b, c = excluded.c;
|
|
SELECT * FROM u;
|
|
} {1|20|200}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-unique-do-nothing {
|
|
CREATE TABLE u (a, b, c);
|
|
CREATE UNIQUE INDEX u_a ON u(a);
|
|
INSERT INTO u VALUES (1,10,100);
|
|
INSERT INTO u VALUES (2,30,300) ON CONFLICT(a) DO NOTHING;
|
|
SELECT * FROM u ORDER BY a;
|
|
} {1|10|100
|
|
2|30|300}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-where-guard-no-change {
|
|
CREATE TABLE g (a UNIQUE, b);
|
|
INSERT INTO g VALUES (1,'x');
|
|
INSERT INTO g VALUES (1,'y') ON CONFLICT(a) DO UPDATE SET b = excluded.b WHERE b IS NULL;
|
|
SELECT * FROM g;
|
|
} {1|x}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-where-guard-apply {
|
|
CREATE TABLE g (a UNIQUE, b);
|
|
INSERT INTO g VALUES (1,NULL);
|
|
INSERT INTO g VALUES (1,'y') ON CONFLICT(a) DO UPDATE SET b = excluded.b WHERE b IS NULL;
|
|
SELECT * FROM g;
|
|
} {1|y}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-selfref-and-excluded {
|
|
CREATE TABLE s (a UNIQUE, b, c);
|
|
INSERT INTO s VALUES (1,10,'old');
|
|
INSERT INTO s VALUES (1,99,'new')
|
|
ON CONFLICT(a) DO UPDATE SET b = b + 1, c = excluded.c;
|
|
SELECT * FROM s;
|
|
} {1|11|new}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-values-mixed-insert-update {
|
|
CREATE TABLE m (a UNIQUE, b);
|
|
INSERT INTO m VALUES (1,'one');
|
|
INSERT INTO m VALUES (1,'uno'), (2,'dos')
|
|
ON CONFLICT(a) DO UPDATE SET b = excluded.b;
|
|
SELECT * FROM m ORDER BY a;
|
|
} {1|uno
|
|
2|dos}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-select-single {
|
|
CREATE TABLE s1 (a UNIQUE, b);
|
|
INSERT INTO s1 VALUES (1,'old');
|
|
INSERT INTO s1 SELECT 1,'NEW' ON CONFLICT(a) DO UPDATE SET b = excluded.b;
|
|
SELECT * FROM s1;
|
|
} {1|NEW}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-composite-target-orderless {
|
|
CREATE TABLE c (a, b, val);
|
|
CREATE UNIQUE INDEX c_ab ON c(a,b);
|
|
INSERT INTO c VALUES (1,1,'x');
|
|
INSERT INTO c VALUES (1,1,'y') ON CONFLICT(b,a) DO UPDATE SET val = excluded.val;
|
|
SELECT val FROM c WHERE a=1 AND b=1;
|
|
} {y}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-collate-nocase {
|
|
CREATE TABLE nc (name TEXT COLLATE NOCASE UNIQUE, v);
|
|
INSERT INTO nc VALUES ('Alice', 1);
|
|
INSERT INTO nc VALUES ('aLiCe', 2)
|
|
ON CONFLICT(name COLLATE NOCASE) DO UPDATE SET v = excluded.v;
|
|
SELECT * FROM nc;
|
|
} {Alice|2}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-update {
|
|
CREATE TABLE r (id INTEGER PRIMARY KEY, name);
|
|
INSERT INTO r VALUES (1,'a');
|
|
INSERT INTO r VALUES (1,'b')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name
|
|
RETURNING id, name;
|
|
} {1|b}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-insert {
|
|
CREATE TABLE r2 (id INTEGER PRIMARY KEY, name);
|
|
INSERT INTO r2 VALUES (2,'c')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name
|
|
RETURNING id, name;
|
|
} {2|c}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-returning-do-nothing-empty {
|
|
CREATE TABLE r3 (id INTEGER PRIMARY KEY, name);
|
|
INSERT INTO r3 VALUES (2,'orig');
|
|
INSERT INTO r3 VALUES (2,'ignored')
|
|
ON CONFLICT DO NOTHING
|
|
RETURNING id, name;
|
|
} {}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-rowid-in-set {
|
|
CREATE TABLE rid (id INTEGER PRIMARY KEY, name);
|
|
INSERT INTO rid VALUES (5,'foo');
|
|
INSERT INTO rid VALUES (5,'bar')
|
|
ON CONFLICT DO UPDATE SET name = printf('id=%d', rowid);
|
|
SELECT * FROM rid;
|
|
} {5|id=5}
|
|
|
|
do_execsql_test_in_memory_any_error upsert-notnull-violation {
|
|
CREATE TABLE nn (a UNIQUE, b NOT NULL);
|
|
INSERT INTO nn VALUES (1,'x');
|
|
INSERT INTO nn VALUES (1,'y') ON CONFLICT(a) DO UPDATE SET b = NULL;
|
|
}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-updates-other-unique-key {
|
|
CREATE TABLE idx (a UNIQUE, b UNIQUE);
|
|
INSERT INTO idx VALUES (1,1);
|
|
INSERT INTO idx VALUES (1,2) ON CONFLICT(a) DO UPDATE SET b = excluded.b;
|
|
SELECT * FROM idx;
|
|
} {1|2}
|
|
|
|
|
|
do_execsql_test_in_memory_any_error upsert-target-mismatch-errors {
|
|
CREATE TABLE tm (a, b UNIQUE);
|
|
INSERT INTO tm VALUES (1,1);
|
|
INSERT INTO tm VALUES (2,1)
|
|
ON CONFLICT(a) DO UPDATE SET a = excluded.a; -- conflict is on b, target is a → error
|
|
}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-omitted-target-matches-pk {
|
|
CREATE TABLE pkalias (a INTEGER PRIMARY KEY, b);
|
|
INSERT INTO pkalias VALUES (42,'old');
|
|
INSERT INTO pkalias (a,b) VALUES (42,'new') ON CONFLICT DO UPDATE SET b = excluded.b;
|
|
SELECT * FROM pkalias;
|
|
} {42|new}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-rowvalue-set {
|
|
CREATE TABLE rv (a INTEGER PRIMARY KEY, b, c);
|
|
INSERT INTO rv VALUES (1,'x','y');
|
|
INSERT INTO rv VALUES (1,'B','C')
|
|
ON CONFLICT DO UPDATE SET (b,c) = (excluded.b, excluded.c);
|
|
SELECT * FROM rv;
|
|
} {1|B|C}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-where-excluded-vs-target {
|
|
CREATE TABLE wh (a UNIQUE, b);
|
|
INSERT INTO wh VALUES (1,5);
|
|
INSERT INTO wh VALUES (1,3)
|
|
ON CONFLICT(a) DO UPDATE SET b = excluded.b WHERE excluded.b > b; -- 3 > 5 → no
|
|
INSERT INTO wh VALUES (1,10)
|
|
ON CONFLICT(a) DO UPDATE SET b = excluded.b WHERE excluded.b > b; -- 10 > 5 → yes
|
|
SELECT * FROM wh;
|
|
} {1|10}
|
|
|
|
do_execsql_test_in_memory_any_error upsert-invalid-qualified-lhs {
|
|
CREATE TABLE bad (a UNIQUE, b);
|
|
INSERT INTO bad VALUES (1,'x');
|
|
INSERT INTO bad VALUES (1,'y')
|
|
ON CONFLICT(a) DO UPDATE SET excluded.b = 'z';
|
|
}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-values-returning-mixed {
|
|
CREATE TABLE mix (k UNIQUE, v);
|
|
INSERT INTO mix VALUES (1,'one');
|
|
INSERT INTO mix VALUES (1,'uno'), (2,'dos')
|
|
ON CONFLICT(k) DO UPDATE SET v = excluded.v
|
|
RETURNING k, v
|
|
;
|
|
} {1|uno
|
|
2|dos}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-collate-implicit-match {
|
|
CREATE TABLE ci (name TEXT COLLATE NOCASE, v);
|
|
-- no explicit collate on index
|
|
CREATE UNIQUE INDEX ci_name ON ci(name);
|
|
INSERT INTO ci VALUES ('Alice', 1);
|
|
INSERT INTO ci VALUES ('aLiCe', 2)
|
|
ON CONFLICT(name COLLATE NOCASE) DO UPDATE SET v = excluded.v;
|
|
SELECT * FROM ci;
|
|
} {Alice|2}
|
|
|
|
# Composite index requires exact coverage, targeting too few columns must not match.
|
|
do_execsql_test_in_memory_any_error upsert-composite-target-too-few {
|
|
CREATE TABLE ct (a, b, val);
|
|
CREATE UNIQUE INDEX ct_ab ON ct(a,b);
|
|
INSERT INTO ct VALUES (1,1,'x');
|
|
INSERT INTO ct VALUES (1,1,'y')
|
|
ON CONFLICT(a) DO UPDATE SET val = excluded.val; -- only "a" given → no match → error
|
|
}
|
|
|
|
# Composite index requires exact coverage, targeting too few columns must not match.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-multilple-conflict-targets {
|
|
CREATE TABLE ct (id INTEGER PRIMARY KEY, x UNIQUE, y UNIQUE, z DEFAULT NULL);
|
|
INSERT INTO ct(id, x, y) VALUES (1, 'x', 'y');
|
|
INSERT INTO ct(id, x, y) VALUES (2, 'a', 'b');
|
|
INSERT INTO ct(id, x, y) VALUES (3, '!', '@');
|
|
INSERT INTO ct(id, x, y) VALUES (4, 'x', 'y1'), (5, 'a1', 'b'), (3, '_', '_')
|
|
ON CONFLICT(x) DO UPDATE SET x = excluded.x || '-' || x, y = excluded.y || '@' || y, z = 'x'
|
|
ON CONFLICT(y) DO UPDATE SET x = excluded.x || '+' || x, y = excluded.y || '!' || y, z = 'y'
|
|
ON CONFLICT DO UPDATE SET x = excluded.x || '#' || x, y = excluded.y || '%' || y, z = 'fallback';
|
|
SELECT * FROM ct;
|
|
} {1|x-x|y1@y|x
|
|
2|a1+a|b!b|y
|
|
3|_#!|_%@|fallback
|
|
}
|
|
|
|
# Qualified target (t.a) should match unique index on a.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-qualified-target {
|
|
CREATE TABLE qt (a UNIQUE, b);
|
|
INSERT INTO qt VALUES (1,'old');
|
|
INSERT INTO qt VALUES (1,'new')
|
|
ON CONFLICT(qt.a) DO UPDATE SET b = excluded.b;
|
|
SELECT * FROM qt;
|
|
} {1|new}
|
|
|
|
# Non-simple target expression is not allowed (e.g., lower(name)) → no match → error.
|
|
do_execsql_test_in_memory_any_error upsert-invalid-target-expression {
|
|
CREATE TABLE it (name, v);
|
|
CREATE UNIQUE INDEX it_name ON it(name);
|
|
INSERT INTO it VALUES ('x',1);
|
|
INSERT INTO it VALUES ('x',2)
|
|
ON CONFLICT(lower(name)) DO UPDATE SET v = excluded.v;
|
|
}
|
|
|
|
|
|
# WHERE with three-valued logic: b < excluded.b is NULL if b IS NULL, should NOT update.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-where-null-3vl-no-update {
|
|
CREATE TABLE w3 (a UNIQUE, b);
|
|
INSERT INTO w3 VALUES (1, NULL);
|
|
INSERT INTO w3 VALUES (1, 5)
|
|
ON CONFLICT(a) DO UPDATE SET b = excluded.b WHERE b < excluded.b;
|
|
SELECT * FROM w3;
|
|
} {1|}
|
|
|
|
# WHERE false on PK conflict → behaves like DO NOTHING.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-pk-where-false {
|
|
CREATE TABLE pw (id INTEGER PRIMARY KEY, name);
|
|
INSERT INTO pw VALUES (7,'keep');
|
|
INSERT INTO pw VALUES (7,'drop')
|
|
ON CONFLICT DO UPDATE SET name = excluded.name WHERE 0;
|
|
SELECT * FROM pw;
|
|
} {7|keep}
|
|
|
|
# WHERE referencing both target and EXCLUDED with arithmetic.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-where-combo {
|
|
CREATE TABLE wc (a UNIQUE, b);
|
|
INSERT INTO wc VALUES (1, 10);
|
|
INSERT INTO wc VALUES (1, 12)
|
|
ON CONFLICT(a) DO UPDATE SET b = excluded.b
|
|
WHERE excluded.b >= b + 2; -- 12 >= 10 + 2 → yes
|
|
SELECT * FROM wc;
|
|
} {1|12}
|
|
|
|
# Invalid EXCLUDED reference should error.
|
|
do_execsql_test_in_memory_error_content upsert-invalid-excluded-column {
|
|
CREATE TABLE xe (a UNIQUE, v);
|
|
INSERT INTO xe VALUES (1, 'ok');
|
|
INSERT INTO xe VALUES (1, 'nope')
|
|
ON CONFLICT(a) DO UPDATE SET v = excluded.not_a_column;
|
|
} {".*no such column.*"}
|
|
|
|
|
|
# DO UPDATE changes the *conflicting key column* to a different unique value.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-update-conflicting-key {
|
|
CREATE TABLE uk (a UNIQUE, b);
|
|
INSERT INTO uk VALUES (1,'old');
|
|
INSERT INTO uk VALUES (1,'new')
|
|
ON CONFLICT(a) DO UPDATE SET a = 2, b = excluded.b;
|
|
SELECT * FROM uk;
|
|
} {2|new}
|
|
|
|
# DO UPDATE that writes the same values (no-op) should still succeed.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-noop-update-ok {
|
|
CREATE TABLE nu (a UNIQUE, b);
|
|
INSERT INTO nu VALUES (5,'same');
|
|
INSERT INTO nu VALUES (5,'irrelevant')
|
|
ON CONFLICT(a) DO UPDATE SET b = b; -- no change
|
|
SELECT * FROM nu;
|
|
} {5|same}
|
|
|
|
# DO UPDATE that would violate a different UNIQUE constraint should error.
|
|
do_execsql_test_in_memory_any_error upsert-update-causes-second-unique-violation {
|
|
CREATE TABLE uv (a UNIQUE, b UNIQUE);
|
|
INSERT INTO uv VALUES (1, 10);
|
|
INSERT INTO uv VALUES (2, 20);
|
|
INSERT INTO uv VALUES (1, 20)
|
|
ON CONFLICT(a) DO UPDATE SET b = excluded.b; # would duplicate b=20 from row a=2
|
|
}
|
|
|
|
# Multi-row VALUES with mixed conflict/non-conflict and WHERE filter in the DO UPDATE.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-multirow-mixed-where {
|
|
CREATE TABLE mm (k UNIQUE, v);
|
|
INSERT INTO mm VALUES (1,'one');
|
|
INSERT INTO mm VALUES (1,'two'), (2,'dos'), (1,'zzz')
|
|
ON CONFLICT(k) DO UPDATE SET v = excluded.v
|
|
WHERE excluded.v != 'zzz'; -- skip the 'zzz' update
|
|
SELECT * FROM mm ORDER BY k;
|
|
} {1|two
|
|
2|dos}
|
|
|
|
# Omitted target with UNIQUE index: confirm it updates.
|
|
do_execsql_test_on_specific_db {:memory:} upsert-omitted-target-updates-unique {
|
|
CREATE TABLE ou (a, b);
|
|
CREATE UNIQUE INDEX ou_a ON ou(a);
|
|
INSERT INTO ou VALUES (3,'x');
|
|
INSERT INTO ou VALUES (3,'y')
|
|
ON CONFLICT DO UPDATE SET b = excluded.b;
|
|
SELECT * FROM ou;
|
|
} {3|y}
|
|
|
|
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');
|
|
INSERT INTO dq VALUES (1,'new')
|
|
ON CONFLICT(main.dq.a) DO UPDATE SET b = excluded.b;
|
|
SELECT * FROM dq;
|
|
} {1|new}
|
|
|
|
do_execsql_test_on_specific_db {:memory:} upsert-targets-chain {
|
|
CREATE TABLE dq (a UNIQUE, b UNIQUE, c UNIQUE, value TEXT);
|
|
CREATE UNIQUE INDEX dq_ab ON dq(a, b);
|
|
INSERT INTO dq VALUES ('a1', 'a2', 'a3', 'aaa');
|
|
INSERT INTO dq VALUES ('b1', 'b2', 'b3', 'bbb');
|
|
INSERT INTO dq VALUES ('c1', 'c2', 'c3', 'ccc');
|
|
INSERT INTO dq VALUES ('d1', 'd2', 'd3', 'ddd');
|
|
INSERT INTO dq VALUES
|
|
('a1', 'a2', 'a3', 'upd1'), ('b1', 'b1', 'b1', 'upd2'), ('c2', 'c2', 'c2', 'upd3'), ('d3', 'd3', 'd3', 'upd4')
|
|
ON CONFLICT (a, b) DO UPDATE SET value = excluded.value || '-a'
|
|
ON CONFLICT (a) DO UPDATE SET value = excluded.value || '-b'
|
|
ON CONFLICT (b) DO UPDATE SET value = excluded.value || '-c'
|
|
ON CONFLICT DO UPDATE SET value = excluded.value || '-d';
|
|
SELECT * FROM dq;
|
|
} {a1|a2|a3|upd1-a
|
|
b1|b2|b3|upd2-b
|
|
c1|c2|c3|upd3-c
|
|
d1|d2|d3|upd4-d}
|
|
|
|
# https://github.com/tursodatabase/turso/issues/3384
|
|
do_execsql_test_on_specific_db {:memory:} upsert-non-rowid-pk-target {
|
|
create table phonebook(name text primary key, phonenumber text, validDate date);
|
|
insert into phonebook values ('Alice','704-545-3333','2018-10-10');
|
|
insert into phonebook values ('Alice','704-111-1111','2018-10-20') on conflict (name) do update set phonenumber=excluded.phonenumber, validDate=excluded.validDate;
|
|
SELECT phonenumber, validDate FROM phonebook;
|
|
} {704-111-1111|2018-10-20}
|
|
|
|
|
|
# TODO: uncomment these when we support collations in indexes
|
|
# (right now it errors on Parse Error: cannot use expressions in CREATE INDEX)
|
|
#
|
|
# Target specifies BINARY but the unique index is NOCASE: target should NOT match, so expect error
|
|
# do_execsql_test_in_memory_any_error upsert-collate-target-mismatch {
|
|
# CREATE TABLE cm (name TEXT, v);
|
|
# CREATE UNIQUE INDEX cm_name_nocase ON cm(name COLLATE NOCASE);
|
|
# INSERT INTO cm VALUES ('Alice', 1);
|
|
# INSERT INTO cm VALUES ('aLiCe', 2)
|
|
# ON CONFLICT(name COLLATE BINARY) DO UPDATE SET v = excluded.v;
|
|
# }
|
|
#
|
|
# do_execsql_test_on_specific_db {:memory:} upsert-collate-omitted-target-matches {
|
|
# CREATE TABLE co (name TEXT, v);
|
|
# CREATE UNIQUE INDEX co_name_nocase ON co(name COLLATE NOCASE);
|
|
# INSERT INTO co VALUES ('Alice', 1);
|
|
# INSERT INTO co VALUES ('aLiCe', 9)
|
|
# ON CONFLICT DO UPDATE SET v = excluded.v;
|
|
# SELECT * FROM co;
|
|
# } {Alice|9}
|
|
#
|
|
#
|
|
# do_execsql_test_on_specific_db {:memory:} upsert-composite-collate-orderless {
|
|
# CREATE TABLE cc (name TEXT, city TEXT, val);
|
|
# CREATE UNIQUE INDEX cc_nc ON cc(name COLLATE NOCASE, city);
|
|
# INSERT INTO cc VALUES ('Alice','SF','old');
|
|
# INSERT INTO cc VALUES ('aLiCe','SF','new')
|
|
# ON CONFLICT(city, name COLLATE NOCASE) DO UPDATE SET val = excluded.val;
|
|
# SELECT * FROM cc;
|
|
# } {Alice|SF|new}
|
|
|