It seems this is intended behavior. From sqlite.org/lang_conflict.html under REPLACE:
> If a NOT NULL constraint violation occurs, the REPLACE conflict resolution replaces > the NULL value with the default value for that column, or if the column has no default > value, then the ABORT algorithm is used. Sorry for the trouble. Thanks, Jonathan Koren On Fri, Jan 20, 2017 at 9:59 PM, Jonathan Koren <jdko...@gmail.com> wrote: > 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