Hello everyone, I've been experimenting with an interesting form of statement that tries to implement an "upsert" operation and came across some unusual behavior. For context, my table has a notion of a "logical key" aside from the primary key, and this is what determines whether to update or insert a row. For simplicity's sake, we can use this table:
CREATE TABLE demo ( id INTEGER PRIMARY KEY AUTOINCREMENT, guid TEXT UNIQUE NOT NULL, someInt INTEGER NOT NULL DEFAULT -1 ); The "upsert" statement looks like this: WITH new (guid, someInt) AS (VALUES('abc', 777)) INSERT OR REPLACE INTO demo (id, guid, someInt) SELECT old.id, new.guid, IFNULL(new.someInt, old.someInt) FROM new LEFT JOIN demo AS old ON new.guid = old.guid; The idea is to maintain ROWID and merge the new and old values if I have a matching guid, otherwise insert a new row. (I realize there are problems with this approach and it's better to just use multiple statements in a transaction. Roll with me.) INSERT INTO demo (guid, someInt) VALUES ('abc', 7); INSERT INTO demo (guid) VALUES ('def'); INSERT INTO demo (guid, someInt) VALUES ('abc', null); Error: NOT NULL constraint failed: demo.someInt select * from demo; id guid someInt ---------- ---------- ---------- 1 abc 7 2 def -1 --So far nothing unexpected. Let's see about the upsert... WITH new (guid, someInt) AS (VALUES('def', 99)) INSERT OR REPLACE INTO demo (id, guid, someInt) SELECT old.id, new.guid, IFNULL(new.someInt, old.someInt) FROM new LEFT JOIN demo AS old ON new.guid = old.guid; --UPDATES OK WITH new (guid, someInt) AS (VALUES('ghi', 1234)) INSERT OR REPLACE INTO demo (id, guid, someInt) SELECT old.id, new.guid, IFNULL(new.someInt, old.someInt) FROM new LEFT JOIN demo AS old ON new.guid = old.guid; --INSERTS OK select * from demo; id guid someInt ---------- ---------- ---------- 1 abc 7 2 def 99 3 ghi 1234 --Now the part that has me baffled WITH new (guid, someInt) AS (VALUES('xyz', null)) INSERT OR REPLACE INTO demo (id, guid, someInt) SELECT old.id, new.guid, IFNULL(new.someInt, old.someInt) FROM new LEFT JOIN demo AS old ON new.guid = old.guid; --No Error?! select * from demo; id guid someInt ---------- ---------- ---------- 1 abc 7 2 def 99 3 ghi 1234 4 xyz -1 --??? --How did it insert a new row with the default value for someInt? --Here's what the select produces for the insert: WITH new (guid, someInt) AS (VALUES('o_O?', null)) SELECT old.id, new.guid, IFNULL(new.someInt, old.someInt) FROM new LEFT JOIN demo AS old ON new.guid = old.guid; id guid IFNULL(new.someInt, old.someInt) ---------- ---------- -------------------------------- <null> o_O? <null> --But wait... INSERT INTO demo (id, guid, someInt) VALUES (null, 'o_O?', null); Error: NOT NULL constraint failed: demo.someInt It seems this statement circumvents the NOT NULL constraint failed error and also inserts the proper default value instead of null. Is this behavior expected? If it is, is it also documented somewhere? If it's not, is this a bug? Thanks for your attention, Jonathan Koren _______________________________________________ sqlite-users mailing list sqlite-users@mailinglists.sqlite.org http://mailinglists.sqlite.org/cgi-bin/mailman/listinfo/sqlite-users