Test for integrity with upserts

This commit is contained in:
Simon Binder 2020-04-19 21:16:01 +02:00
parent c3b0c95389
commit 398577ceab
No known key found for this signature in database
GPG Key ID: 7891917E4147B8C0
2 changed files with 55 additions and 16 deletions

View File

@ -92,7 +92,7 @@ class InsertStatement<T extends Table, D extends DataClass> {
/// ///
/// This method is used internally by moor. Consider using [insert] instead. /// This method is used internally by moor. Consider using [insert] instead.
GenerationContext createContext(Insertable<D> entry, InsertMode mode, GenerationContext createContext(Insertable<D> entry, InsertMode mode,
{DoUpdate onConflict}) { {DoUpdate<T, D> onConflict}) {
_validateIntegrity(entry); _validateIntegrity(entry);
final rawValues = entry.toColumns(true); final rawValues = entry.toColumns(true);
@ -146,7 +146,19 @@ class InsertStatement<T extends Table, D extends DataClass> {
} }
if (onConflict != null) { 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 '); ctx.buffer.write(' ON CONFLICT DO UPDATE SET ');

View File

@ -68,21 +68,48 @@ void main() {
{const TableUpdate('users', kind: UpdateKind.insert)})); {const TableUpdate('users', kind: UpdateKind.insert)}));
}); });
test('enforces data integrity', () async { group('enforces integrity', () {
InvalidDataException exception; test('for regular inserts', () async {
try { InvalidDataException exception;
await db.into(db.todosTable).insert( try {
const TodosTableCompanion( await db.into(db.todosTable).insert(
// not declared as nullable in table definition const TodosTableCompanion(
content: Value(null), // not declared as nullable in table definition
), content: Value(null),
); ),
fail('inserting invalid data did not throw'); );
} on InvalidDataException catch (e) { fail('inserting invalid data did not throw');
exception = e; } 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<InvalidDataException>()),
);
});
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<InvalidDataException>()),
);
});
}); });
test("doesn't allow writing null rows", () { test("doesn't allow writing null rows", () {