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
|
/// must be set and non-null. Otherwise, an [InvalidDataException] will be
|
||||||
/// thrown.
|
/// thrown.
|
||||||
///
|
///
|
||||||
/// If [orReplace] is true and a row with the same primary key already exists,
|
/// By default, an exception will be thrown if another row with the same
|
||||||
/// the columns of that row will be updated and no new row will be written.
|
/// primary key already exists. This behavior can be overridden with [mode],
|
||||||
/// Otherwise, an exception will be thrown.
|
/// for instance by using [InsertMode.replace] or [InsertMode.insertOrIgnore].
|
||||||
///
|
///
|
||||||
/// If the table contains an auto-increment column, the generated value will
|
/// 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
|
/// 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
|
/// return value, but the future will resolve to an error when the insert
|
||||||
/// fails.
|
/// 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);
|
_validateIntegrity(entity);
|
||||||
final ctx = _createContext(entity, orReplace);
|
final ctx = _createContext(entity, _resolveMode(mode, orReplace));
|
||||||
|
|
||||||
return await database.executor.doWhenOpened((e) async {
|
return await database.executor.doWhenOpened((e) async {
|
||||||
final id = await database.executor.runInsert(ctx.sql, ctx.boundVariables);
|
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
|
/// 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
|
/// must be set and non-null. Otherwise, an [InvalidDataException] will be
|
||||||
/// thrown.
|
/// thrown.
|
||||||
/// When a row with the same primary or unique key already exists in the
|
/// By default, an exception will be thrown if another row with the same
|
||||||
/// database, the insert will fail. Use [orReplace] to replace rows that
|
/// primary key already exists. This behavior can be overridden with [mode],
|
||||||
/// already exist.
|
/// for instance by using [InsertMode.replace] or [InsertMode.insertOrIgnore].
|
||||||
Future<void> insertAll(List<Insertable<D>> rows,
|
Future<void> insertAll(
|
||||||
{bool orReplace = false}) async {
|
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>>{};
|
final statements = <String, List<GenerationContext>>{};
|
||||||
|
|
||||||
// Not every insert has the same sql, as fields which are set to null are
|
// 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) {
|
for (var row in rows) {
|
||||||
_validateIntegrity(row);
|
_validateIntegrity(row);
|
||||||
|
|
||||||
final ctx = _createContext(row, orReplace);
|
final ctx = _createContext(row, _resolveMode(mode, orReplace));
|
||||||
statements.putIfAbsent(ctx.sql, () => []).add(ctx);
|
statements.putIfAbsent(ctx.sql, () => []).add(ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,15 +93,14 @@ class InsertStatement<D extends DataClass> {
|
||||||
database.markTablesUpdated({table});
|
database.markTablesUpdated({table});
|
||||||
}
|
}
|
||||||
|
|
||||||
GenerationContext _createContext(Insertable<D> entry, bool replace) {
|
GenerationContext _createContext(Insertable<D> entry, InsertMode mode) {
|
||||||
final map = table.entityToSql(entry.createCompanion(true))
|
final map = table.entityToSql(entry.createCompanion(true))
|
||||||
..removeWhere((_, value) => value == null);
|
..removeWhere((_, value) => value == null);
|
||||||
|
|
||||||
final ctx = GenerationContext.fromDb(database);
|
final ctx = GenerationContext.fromDb(database);
|
||||||
ctx.buffer
|
ctx.buffer
|
||||||
..write('INSERT ')
|
..write(_insertKeywords[mode])
|
||||||
..write(replace ? 'OR REPLACE ' : '')
|
..write(' INTO ')
|
||||||
..write('INTO ')
|
|
||||||
..write(table.$tableName)
|
..write(table.$tableName)
|
||||||
..write(' ');
|
..write(' ');
|
||||||
|
|
||||||
|
@ -113,6 +129,11 @@ class InsertStatement<D extends DataClass> {
|
||||||
return ctx;
|
return ctx;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InsertMode _resolveMode(InsertMode mode, bool orReplace) {
|
||||||
|
return mode ??
|
||||||
|
(orReplace == true ? InsertMode.insertOrReplace : InsertMode.insert);
|
||||||
|
}
|
||||||
|
|
||||||
void _validateIntegrity(Insertable<D> d) {
|
void _validateIntegrity(Insertable<D> d) {
|
||||||
if (d == null) {
|
if (d == null) {
|
||||||
throw InvalidDataException(
|
throw InvalidDataException(
|
||||||
|
@ -124,3 +145,46 @@ class InsertStatement<D extends DataClass> {
|
||||||
.throwIfInvalid(d);
|
.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 {
|
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(
|
await db.into(db.todosTable).insert(
|
||||||
TodoEntry(
|
TodoEntry(
|
||||||
id: 113,
|
id: 113,
|
||||||
|
|
Loading…
Reference in New Issue