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,12 @@ final class _UnsafeSyncContext extends UnscopedContext {
}
}, sql: sql);
}

@override
Future<void> executeMultiple(String sql,
[List<Object?> parameters = const []]) async {
task.timeSync('executeMultiple', () {
db.execute(sql, parameters);
}, sql: sql);
}
}
8 changes: 8 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, List<Object?> parameters);

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

@override
Future<void> executeMultiple(String sql,
[List<Object?> parameters = const []]) async {
_checkNotLocked();
return await _context.executeMultiple(sql, parameters);
}

@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,15 @@ final class _UnsafeContext extends UnscopedContext {
}
});
}

@override
Future<void> executeMultiple(String sql,
[List<Object?> 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 {
Expand Down
4 changes: 4 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,10 @@ 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,
[List<Object?> parameters = const []]);
Copy link
Contributor

Choose a reason for hiding this comment

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

For executeMultiple, we should probably remove the parameters argument like you mentioned in the issue.

Copy link
Author

Choose a reason for hiding this comment

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

Unless I'm misreading the sqlite3 package, I should be possible to do something like

executeMultiple(
  "INSERT INTO books VALUES (?, ?); INSERT INTO authors (?);",
   ["Sqlite for Dummies", "simolus3", "simolus3"],
);

Copy link
Contributor

Choose a reason for hiding this comment

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

I don't think that would work, if you call execute with parameters it will only prepare a single statement.

Now, we could potentially make that work by tracking how many parameters each statement has and then skipping those, but it gets trickier if parameters have explicit indexes (INSERT INTO books VALUES (?, ?2); INSERT INTO authors (?2);) or names (VALUES (:title, :author); INSERT INTO authors (:author)).


/// Open a read-write transaction on this write context.
///
/// When called on a [SqliteConnection], this takes a global lock - only one
Expand Down
8 changes: 8 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,14 @@ mixin SqliteQueries implements SqliteWriteContext, SqliteConnection {
});
}

@override
Future<void> executeMultiple(String sql,
[List<Object?> parameters = const []]) {
return writeTransaction((tx) async {
return tx.executeMultiple(sql, parameters);
});
}

@override
Future<void> refreshSchema() {
return getAll("PRAGMA table_info('sqlite_master')");
Expand Down
15 changes: 15 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,21 @@ final class _UnscopedContext extends UnscopedContext {
});
}

@override
Future<void> executeMultiple(String sql,
[List<Object?> 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
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