diff --git a/moor/lib/src/runtime/query_builder/statements/insert.dart b/moor/lib/src/runtime/query_builder/statements/insert.dart index 5ebf6d1b..cb4f3e97 100644 --- a/moor/lib/src/runtime/query_builder/statements/insert.dart +++ b/moor/lib/src/runtime/query_builder/statements/insert.dart @@ -92,7 +92,7 @@ class InsertStatement { /// /// This method is used internally by moor. Consider using [insert] instead. GenerationContext createContext(Insertable entry, InsertMode mode, - {DoUpdate onConflict}) { + {DoUpdate onConflict}) { _validateIntegrity(entry); final rawValues = entry.toColumns(true); @@ -146,7 +146,19 @@ class InsertStatement { } if (onConflict != null) { - final updateSet = onConflict._createInsertable(table).toColumns(true); + final upsertInsertable = onConflict._createInsertable(table.asDslTable); + + if (!identical(entry, upsertInsertable)) { + // We run a ON CONFLICT DO UPDATE, so make sure upsertInsertable is + // valid for updates. + // the identical check is a performance optimization - for the most + // common call (insertOnConflictUpdate) we don't have to check twice. + table + .validateIntegrity(upsertInsertable, isInserting: false) + .throwIfInvalid(upsertInsertable); + } + + final updateSet = upsertInsertable.toColumns(true); ctx.buffer.write(' ON CONFLICT DO UPDATE SET '); diff --git a/moor/test/insert_test.dart b/moor/test/insert_test.dart index 4f190c1c..4b4f3bcf 100644 --- a/moor/test/insert_test.dart +++ b/moor/test/insert_test.dart @@ -68,21 +68,48 @@ void main() { {const TableUpdate('users', kind: UpdateKind.insert)})); }); - test('enforces data integrity', () async { - InvalidDataException exception; - try { - await db.into(db.todosTable).insert( - const TodosTableCompanion( - // not declared as nullable in table definition - content: Value(null), - ), - ); - fail('inserting invalid data did not throw'); - } on InvalidDataException catch (e) { - exception = e; - } + group('enforces integrity', () { + test('for regular inserts', () async { + InvalidDataException exception; + try { + await db.into(db.todosTable).insert( + const TodosTableCompanion( + // not declared as nullable in table definition + content: Value(null), + ), + ); + fail('inserting invalid data did not throw'); + } on InvalidDataException catch (e) { + exception = e; + } - expect(exception.toString(), startsWith('InvalidDataException')); + expect(exception.toString(), startsWith('InvalidDataException')); + }); + + test("for upserts that aren't valid inserts", () { + expect( + () { + return db + .into(db.todosTable) + // content would be required + .insertOnConflictUpdate(const TodosTableCompanion()); + }, + throwsA(isA()), + ); + }); + + test("for upserts that aren't valid updates", () { + expect( + () { + final insert = TodosTableCompanion.insert(content: 'content'); + const update = TodosTableCompanion(content: Value(null)); + return db + .into(db.todosTable) + .insert(insert, onConflict: DoUpdate((_) => update)); + }, + throwsA(isA()), + ); + }); }); test("doesn't allow writing null rows", () {