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..6d0c744 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,11 @@ final class _UnsafeSyncContext extends UnscopedContext { } }, sql: sql); } + + @override + Future executeMultiple(String sql) async { + task.timeSync('executeMultiple', () { + db.execute(sql); + }, sql: sql); + } } diff --git a/packages/sqlite_async/lib/src/impl/context.dart b/packages/sqlite_async/lib/src/impl/context.dart index 0e3eef6..f0d1bd5 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); /// Returns an [UnscopedContext] useful as the outermost transaction. /// @@ -143,6 +144,12 @@ final class ScopedWriteContext extends ScopedReadContext return await _context.executeBatch(sql, parameterSets); } + @override + Future executeMultiple(String sql) async { + _checkNotLocked(); + return await _context.executeMultiple(sql); + } + @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..619c182 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,14 @@ final class _UnsafeContext extends UnscopedContext { } }); } + + @override + Future 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 { diff --git a/packages/sqlite_async/lib/src/sqlite_connection.dart b/packages/sqlite_async/lib/src/sqlite_connection.dart index 0e360fc..50f9a1c 100644 --- a/packages/sqlite_async/lib/src/sqlite_connection.dart +++ b/packages/sqlite_async/lib/src/sqlite_connection.dart @@ -76,6 +76,9 @@ 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); + /// 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..c5e4e54 100644 --- a/packages/sqlite_async/lib/src/sqlite_queries.dart +++ b/packages/sqlite_async/lib/src/sqlite_queries.dart @@ -136,6 +136,13 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection { }); } + @override + Future executeMultiple(String sql) { + return writeTransaction((tx) async { + return tx.executeMultiple(sql); + }); + } + @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..a0f19a2 100644 --- a/packages/sqlite_async/lib/src/web/database.dart +++ b/packages/sqlite_async/lib/src/web/database.dart @@ -293,6 +293,19 @@ final class _UnscopedContext extends UnscopedContext { }); } + @override + Future 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 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;