Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -164,4 +164,11 @@ final class _UnsafeSyncContext extends UnscopedContext {
}
}, sql: sql);
}

@override
Future<void> executeMultiple(String sql) async {
task.timeSync('executeMultiple', () {
db.execute(sql);
}, sql: sql);
}
}
7 changes: 7 additions & 0 deletions packages/sqlite_async/lib/src/impl/context.dart
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import '../sqlite_connection.dart';
abstract base class UnscopedContext implements SqliteReadContext {
Future<ResultSet> execute(String sql, List<Object?> parameters);
Future<void> executeBatch(String sql, List<List<Object?>> parameterSets);
Future<void> executeMultiple(String sql);

/// Returns an [UnscopedContext] useful as the outermost transaction.
///
Expand Down Expand Up @@ -143,6 +144,12 @@ final class ScopedWriteContext extends ScopedReadContext
return await _context.executeBatch(sql, parameterSets);
}

@override
Future<void> executeMultiple(String sql) async {
_checkNotLocked();
return await _context.executeMultiple(sql);
}

@override
Future<T> writeTransaction<T>(
Future<T> Function(SqliteWriteContext tx) callback) async {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -275,6 +275,14 @@ final class _UnsafeContext extends UnscopedContext {
}
});
}

@override
Future<void> executeMultiple(String sql) async {
return computeWithDatabase((db) async {
// execute allows multiple statements, but does not return results.
db.execute(sql);
});
}
}

void _sqliteConnectionIsolate(_SqliteConnectionParams params) async {
Expand Down
3 changes: 3 additions & 0 deletions packages/sqlite_async/lib/src/sqlite_connection.dart
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,9 @@ abstract interface class SqliteWriteContext extends SqliteReadContext {
/// parameter set.
Future<void> executeBatch(String sql, List<List<Object?>> parameterSets);

// Execute a query that potentially contains multiple statements.
Future<void> executeMultiple(String sql);

/// Open a read-write transaction on this write context.
///
/// When called on a [SqliteConnection], this takes a global lock - only one
Expand Down
7 changes: 7 additions & 0 deletions packages/sqlite_async/lib/src/sqlite_queries.dart
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection {
});
}

@override
Future<void> executeMultiple(String sql) {
return writeTransaction((tx) async {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call to use a transaction here. Can we add a test running executeMultiple where one statement fails (e.g. due to a duplicate primary key being inserted twice)? Then we can assert that no intermediate state is visible after executeMultiple completes.

return tx.executeMultiple(sql);
});
}

@override
Future<void> refreshSchema() {
return getAll("PRAGMA table_info('sqlite_master')");
Expand Down
13 changes: 13 additions & 0 deletions packages/sqlite_async/lib/src/web/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,19 @@ final class _UnscopedContext extends UnscopedContext {
});
}

@override
Future<void> executeMultiple(String sql) {
return _task.timeAsync('executeMultiple', sql: sql, () {
return wrapSqliteException(() async {
await _database._database.execute(
sql,
token: _lock,
checkInTransaction: _checkInTransaction,
);
});
});
}

@override
UnscopedContext interceptOutermostTransaction() {
// All execute calls done in the callback will be checked for the
Expand Down
46 changes: 46 additions & 0 deletions packages/sqlite_async/test/basic_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,52 @@ void main() {
)
});

test('execute single statement with RETURNING populates ResultSet',
() async {
final db = await testUtils.setupDatabase(path: path);
await createTables(db);
final result = await db.execute(
'INSERT INTO test_data(description) VALUES(?) RETURNING id, description',
['test returning with params']);

expect(result.columnNames, equals(['id', 'description']));
expect(result.rows.length, equals(1));
expect(result.rows[0][0], isA<int>());
expect(result.rows[0][1], equals('test returning with params'));
});

test(
'execute single statment with RETURNING populates ResultSet without params',
() async {
final db = await testUtils.setupDatabase(path: path);
await createTables(db);
final result = await db.execute(
'INSERT INTO test_data(description) VALUES("test returning without params") RETURNING id, description');

expect(result.columnNames, equals(['id', 'description']));
expect(result.rows.length, equals(1));
expect(result.rows[0][0], isA<int>());
expect(result.rows[0][1], equals('test returning without params'));
});

test('executeMultiple handles multiple statements', () async {
final db = await testUtils.setupDatabase(path: path);
await createTables(db);

await db.executeMultiple('''
INSERT INTO test_data(description) VALUES('row1');
INSERT INTO test_data(description) VALUES('row2');
''');

final results =
await db.getAll('SELECT description FROM test_data ORDER BY id');
expect(results.length, equals(2));
expect(results.rows[0], equals(['row1']));
expect(results.rows[1], equals(['row2']));

await db.close();
});

test('with all connections', () async {
final maxReaders = _isWeb ? 0 : 3;

Expand Down