mirror of https://github.com/AMT-Cheif/drift.git
Support additional insert modes
This commit is contained in:
parent
82477d9325
commit
ccf208b329
|
@ -24,17 +24,26 @@ class InsertStatement<D extends DataClass> {
|
|||
/// must be set and non-null. Otherwise, an [InvalidDataException] will be
|
||||
/// thrown.
|
||||
///
|
||||
/// If [orReplace] is true and a row with the same primary key already exists,
|
||||
/// the columns of that row will be updated and no new row will be written.
|
||||
/// Otherwise, an exception will be thrown.
|
||||
/// By default, an exception will be thrown if another row with the same
|
||||
/// primary key already exists. This behavior can be overridden with [mode],
|
||||
/// for instance by using [InsertMode.replace] or [InsertMode.insertOrIgnore].
|
||||
///
|
||||
/// If the table contains an auto-increment column, the generated value will
|
||||
/// be returned. If there is no auto-increment column, you can't rely on the
|
||||
/// return value, but the future will resolve to an error when the insert
|
||||
/// fails.
|
||||
Future<int> insert(Insertable<D> entity, {bool orReplace = false}) async {
|
||||
Future<int> insert(
|
||||
Insertable<D> entity, {
|
||||
@Deprecated('Use mode: InsertMode.replace instead') bool orReplace = false,
|
||||
InsertMode mode,
|
||||
}) async {
|
||||
assert(
|
||||
mode == null || (orReplace != true),
|
||||
'If the mode parameter is set on insertAll, orReplace must be null or '
|
||||
'false',
|
||||
);
|
||||
_validateIntegrity(entity);
|
||||
final ctx = _createContext(entity, orReplace);
|
||||
final ctx = _createContext(entity, _resolveMode(mode, orReplace));
|
||||
|
||||
return await database.executor.doWhenOpened((e) async {
|
||||
final id = await database.executor.runInsert(ctx.sql, ctx.boundVariables);
|
||||
|
@ -48,11 +57,19 @@ class InsertStatement<D extends DataClass> {
|
|||
/// All fields in a row that don't have a default value or auto-increment
|
||||
/// must be set and non-null. Otherwise, an [InvalidDataException] will be
|
||||
/// thrown.
|
||||
/// When a row with the same primary or unique key already exists in the
|
||||
/// database, the insert will fail. Use [orReplace] to replace rows that
|
||||
/// already exist.
|
||||
Future<void> insertAll(List<Insertable<D>> rows,
|
||||
{bool orReplace = false}) async {
|
||||
/// By default, an exception will be thrown if another row with the same
|
||||
/// primary key already exists. This behavior can be overridden with [mode],
|
||||
/// for instance by using [InsertMode.replace] or [InsertMode.insertOrIgnore].
|
||||
Future<void> insertAll(
|
||||
List<Insertable<D>> rows, {
|
||||
@Deprecated('Use mode: InsertMode.replace instead') bool orReplace = false,
|
||||
InsertMode mode,
|
||||
}) async {
|
||||
assert(
|
||||
mode == null || (orReplace != true),
|
||||
'If the mode parameter is set on insertAll, orReplace must be null or '
|
||||
'false',
|
||||
);
|
||||
final statements = <String, List<GenerationContext>>{};
|
||||
|
||||
// Not every insert has the same sql, as fields which are set to null are
|
||||
|
@ -61,7 +78,7 @@ class InsertStatement<D extends DataClass> {
|
|||
for (var row in rows) {
|
||||
_validateIntegrity(row);
|
||||
|
||||
final ctx = _createContext(row, orReplace);
|
||||
final ctx = _createContext(row, _resolveMode(mode, orReplace));
|
||||
statements.putIfAbsent(ctx.sql, () => []).add(ctx);
|
||||
}
|
||||
|
||||
|
@ -76,15 +93,14 @@ class InsertStatement<D extends DataClass> {
|
|||
database.markTablesUpdated({table});
|
||||
}
|
||||
|
||||
GenerationContext _createContext(Insertable<D> entry, bool replace) {
|
||||
GenerationContext _createContext(Insertable<D> entry, InsertMode mode) {
|
||||
final map = table.entityToSql(entry.createCompanion(true))
|
||||
..removeWhere((_, value) => value == null);
|
||||
|
||||
final ctx = GenerationContext.fromDb(database);
|
||||
ctx.buffer
|
||||
..write('INSERT ')
|
||||
..write(replace ? 'OR REPLACE ' : '')
|
||||
..write('INTO ')
|
||||
..write(_insertKeywords[mode])
|
||||
..write(' INTO ')
|
||||
..write(table.$tableName)
|
||||
..write(' ');
|
||||
|
||||
|
@ -113,6 +129,11 @@ class InsertStatement<D extends DataClass> {
|
|||
return ctx;
|
||||
}
|
||||
|
||||
InsertMode _resolveMode(InsertMode mode, bool orReplace) {
|
||||
return mode ??
|
||||
(orReplace == true ? InsertMode.insertOrReplace : InsertMode.insert);
|
||||
}
|
||||
|
||||
void _validateIntegrity(Insertable<D> d) {
|
||||
if (d == null) {
|
||||
throw InvalidDataException(
|
||||
|
@ -124,3 +145,46 @@ class InsertStatement<D extends DataClass> {
|
|||
.throwIfInvalid(d);
|
||||
}
|
||||
}
|
||||
|
||||
/// Enumeration of different insert behaviors. See the documentation on the
|
||||
/// individual fields for details.
|
||||
enum InsertMode {
|
||||
/// A regular `INSERT INTO` statement. When a row with the same primary or
|
||||
/// unique key already exists, the insert statement will fail and an exception
|
||||
/// will be thrown. If the exception is caught, previous statements made in
|
||||
/// the same transaction will NOT be reverted.
|
||||
insert,
|
||||
|
||||
/// Identical to [InsertMode.insertOrReplace], included for the sake of
|
||||
/// completeness.
|
||||
replace,
|
||||
|
||||
/// Like [insert], but if a row with the same primary or unique key already
|
||||
/// exists, it will be deleted and re-created with the row being inserted.
|
||||
insertOrReplace,
|
||||
|
||||
/// Similar to [InsertMode.insertOrAbort], but it will revert the surrounding
|
||||
/// transaction if a constraint is violated, even if the thrown exception is
|
||||
/// caught.
|
||||
insertOrRollback,
|
||||
|
||||
/// Identical to [insert], included for the sake of completeness.
|
||||
insertOrAbort,
|
||||
|
||||
/// Like [insert], but if multiple values are inserted with the same insert
|
||||
/// statement and one of them fails, the others will still be completed.
|
||||
insertOrFail,
|
||||
|
||||
/// Like [insert], but failures will be ignored.
|
||||
insertOrIgnore,
|
||||
}
|
||||
|
||||
const _insertKeywords = <InsertMode, String>{
|
||||
InsertMode.insert: 'INSERT',
|
||||
InsertMode.replace: 'REPLACE',
|
||||
InsertMode.insertOrReplace: 'INSERT OR REPLACE',
|
||||
InsertMode.insertOrRollback: 'INSERT OR ROLLBACK',
|
||||
InsertMode.insertOrAbort: 'INSERT OR ABORT',
|
||||
InsertMode.insertOrFail: 'INSERT OR FAIL',
|
||||
InsertMode.insertOrIgnore: 'INSERT OR IGNORE',
|
||||
};
|
||||
|
|
|
@ -37,6 +37,20 @@ void main() {
|
|||
});
|
||||
|
||||
test('generates insert or replace statements', () async {
|
||||
await db.into(db.todosTable).insert(
|
||||
TodoEntry(
|
||||
id: 113,
|
||||
content: 'Done',
|
||||
),
|
||||
mode: InsertMode.insertOrReplace);
|
||||
|
||||
verify(executor.runInsert(
|
||||
'INSERT OR REPLACE INTO todos (id, content) VALUES (?, ?)',
|
||||
[113, 'Done']));
|
||||
});
|
||||
|
||||
test('generates insert or replace statements with legacy parameter',
|
||||
() async {
|
||||
await db.into(db.todosTable).insert(
|
||||
TodoEntry(
|
||||
id: 113,
|
||||
|
|
Loading…
Reference in New Issue