diff --git a/packages/sqlite_async/lib/src/common/connection/sync_sqlite_connection.dart b/packages/sqlite_async/lib/src/common/connection/sync_sqlite_connection.dart index f63e0df..b0e2158 100644 --- a/packages/sqlite_async/lib/src/common/connection/sync_sqlite_connection.dart +++ b/packages/sqlite_async/lib/src/common/connection/sync_sqlite_connection.dart @@ -164,4 +164,12 @@ final class _UnsafeSyncContext extends UnscopedContext { } }, sql: sql); } + + @override + Future executeMultiple(String sql, + [List parameters = const []]) async { + task.timeSync('executeMultiple', () { + db.execute(sql, parameters); + }, sql: sql); + } } diff --git a/packages/sqlite_async/lib/src/impl/context.dart b/packages/sqlite_async/lib/src/impl/context.dart index 0e3eef6..a8a754a 100644 --- a/packages/sqlite_async/lib/src/impl/context.dart +++ b/packages/sqlite_async/lib/src/impl/context.dart @@ -12,6 +12,7 @@ import '../sqlite_connection.dart'; abstract base class UnscopedContext implements SqliteReadContext { Future execute(String sql, List parameters); Future executeBatch(String sql, List> parameterSets); + Future executeMultiple(String sql, List parameters); /// Returns an [UnscopedContext] useful as the outermost transaction. /// @@ -143,6 +144,13 @@ final class ScopedWriteContext extends ScopedReadContext return await _context.executeBatch(sql, parameterSets); } + @override + Future executeMultiple(String sql, + [List parameters = const []]) async { + _checkNotLocked(); + return await _context.executeMultiple(sql, parameters); + } + @override Future writeTransaction( Future Function(SqliteWriteContext tx) callback) async { diff --git a/packages/sqlite_async/lib/src/native/database/native_sqlite_connection_impl.dart b/packages/sqlite_async/lib/src/native/database/native_sqlite_connection_impl.dart index e90739e..8181e28 100644 --- a/packages/sqlite_async/lib/src/native/database/native_sqlite_connection_impl.dart +++ b/packages/sqlite_async/lib/src/native/database/native_sqlite_connection_impl.dart @@ -275,6 +275,15 @@ final class _UnsafeContext extends UnscopedContext { } }); } + + @override + Future executeMultiple(String sql, + [List parameters = const []]) async { + return computeWithDatabase((db) async { + // execute allows multiple statements, but does not return results. + db.execute(sql, parameters); + }); + } } void _sqliteConnectionIsolate(_SqliteConnectionParams params) async { diff --git a/packages/sqlite_async/lib/src/sqlite_connection.dart b/packages/sqlite_async/lib/src/sqlite_connection.dart index 0e360fc..2369585 100644 --- a/packages/sqlite_async/lib/src/sqlite_connection.dart +++ b/packages/sqlite_async/lib/src/sqlite_connection.dart @@ -76,6 +76,10 @@ abstract interface class SqliteWriteContext extends SqliteReadContext { /// parameter set. Future executeBatch(String sql, List> parameterSets); + // Execute a query that potentially contains multiple statements. + Future executeMultiple(String sql, + [List parameters = const []]); + /// Open a read-write transaction on this write context. /// /// When called on a [SqliteConnection], this takes a global lock - only one diff --git a/packages/sqlite_async/lib/src/sqlite_queries.dart b/packages/sqlite_async/lib/src/sqlite_queries.dart index 367d23f..073bde2 100644 --- a/packages/sqlite_async/lib/src/sqlite_queries.dart +++ b/packages/sqlite_async/lib/src/sqlite_queries.dart @@ -136,6 +136,14 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection { }); } + @override + Future executeMultiple(String sql, + [List parameters = const []]) { + return writeTransaction((tx) async { + return tx.executeMultiple(sql, parameters); + }); + } + @override Future refreshSchema() { return getAll("PRAGMA table_info('sqlite_master')"); diff --git a/packages/sqlite_async/lib/src/web/database.dart b/packages/sqlite_async/lib/src/web/database.dart index 23424fc..7567eef 100644 --- a/packages/sqlite_async/lib/src/web/database.dart +++ b/packages/sqlite_async/lib/src/web/database.dart @@ -293,6 +293,21 @@ final class _UnscopedContext extends UnscopedContext { }); } + @override + Future executeMultiple(String sql, + [List parameters = const []]) { + return _task.timeAsync('executeMultiple', sql: sql, () { + return wrapSqliteException(() async { + await _database._database.execute( + sql, + parameters: parameters, + token: _lock, + checkInTransaction: _checkInTransaction, + ); + }); + }); + } + @override UnscopedContext interceptOutermostTransaction() { // All execute calls done in the callback will be checked for the diff --git a/packages/sqlite_async/test/basic_test.dart b/packages/sqlite_async/test/basic_test.dart index e2914b3..38f5775 100644 --- a/packages/sqlite_async/test/basic_test.dart +++ b/packages/sqlite_async/test/basic_test.dart @@ -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()); + 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()); + 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;