Skip to content
Closed
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
23 changes: 23 additions & 0 deletions sqlite3_web/lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,29 @@ final class RemoteDatabase implements Database {
return response.asResultWithResultSet();
}

@override
Future<void> executeMultiple(
String sql, {
bool checkInTransaction = false,
LockToken? token,
Future<void>? abortTrigger,
}) async {
await connection.sendRequest(
RunQuery(
requestId: 0,
databaseId: databaseId,
lockId: token == null ? null : lockTokenToId(token),
sql: sql,
parameters: const [],
returnRows: false,
checkInTransaction: checkInTransaction,
multipleStatements: true,
),
MessageType.simpleSuccessResponse,
abortTrigger: abortTrigger,
);
}

@override
Future<T> requestLock<T>(Future<T> Function(LockToken token) body,
{Future<void>? abortTrigger}) async {
Expand Down
18 changes: 18 additions & 0 deletions sqlite3_web/lib/src/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,24 @@ abstract class Database {
Future<void>? abortTrigger,
});

/// Prepares each statement [sql] and executes them each, sequentially. If
/// any sequence fails, the execution is aborted without running the
/// remaining statements.
///
/// If [checkInTransaction] is enabled, the host will verify that the
/// autocommit mode is disabled before running the statements (and report an
/// exception otherwise).
///
/// The [abortTrigger] can be used to abort the request. When that future
/// completes before the lock has been granted, the future may complete
/// with a [AbortException] without running the statements.
Future<void> executeMultiple(
String sql, {
bool checkInTransaction = false,
LockToken? token,
Future<void>? abortTrigger,
});

/// Prepares [sql], executes it with the given [parameters] and returns the
/// [ResultSet].
///
Expand Down
7 changes: 7 additions & 0 deletions sqlite3_web/lib/src/protocol.dart
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ class _UniqueFieldNames {
static const autocommit = 'x';
static const lastInsertRowid = 'y';
static const lockId = 'z';
static const multipleStatements = 'm';
}

sealed class Message {
Expand Down Expand Up @@ -563,6 +564,7 @@ final class RunQuery extends Request {
final int? lockId;
final bool returnRows;
final bool checkInTransaction;
final bool multipleStatements;

RunQuery({
required super.requestId,
Expand All @@ -572,6 +574,7 @@ final class RunQuery extends Request {
required this.lockId,
required this.returnRows,
required this.checkInTransaction,
this.multipleStatements = false,
});

factory RunQuery.deserialize(JSObject object) {
Expand All @@ -587,6 +590,9 @@ final class RunQuery extends Request {
returnRows: (object[_UniqueFieldNames.returnRows] as JSBoolean).toDart,
checkInTransaction:
(object[_UniqueFieldNames.checkInTransaction] as JSBoolean).toDart,
multipleStatements:
(object[_UniqueFieldNames.multipleStatements] as JSBoolean?)?.toDart ??
false,
);
}

Expand All @@ -611,6 +617,7 @@ final class RunQuery extends Request {
}

object[_UniqueFieldNames.checkInTransaction] = checkInTransaction.toJS;
object[_UniqueFieldNames.multipleStatements] = multipleStatements.toJS;
}

@override
Expand Down
16 changes: 16 additions & 0 deletions sqlite3_web/lib/src/worker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -331,6 +331,11 @@ final class _ClientConnection extends ProtocolChannel
final database = _requireDatabase(request);
final openedDatabase = await database.database.opened;

if (request.returnRows && request.multipleStatements) {
throw ArgumentError(
'Cannot return rows when executing multiple statements');
}

return database.useLock(request.lockId, abortSignal, () {
final db = openedDatabase.database;

Expand All @@ -341,6 +346,17 @@ final class _ClientConnection extends ProtocolChannel
ResultSet? resultSet;
if (request.returnRows) {
resultSet = db.select(request.sql, request.parameters);
} else if (request.multipleStatements) {
final statements = db.prepareMultiple(request.sql);
try {
for (var statement in statements) {
statement.execute();
}
} finally {
for (var statement in statements) {
statement.dispose();
}
}
} else {
db.execute(request.sql, request.parameters);
}
Expand Down
24 changes: 24 additions & 0 deletions sqlite3_web/test/protocol_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,30 @@ void main() {
);
});

test('serializes multipleStatements flag', () async {
server.handleRequestFunction = expectAsync1((request) async {
final run = request as RunQuery;
expect(run.sql, 'CREATE TABLE a (x); CREATE TABLE b (y);');
expect(run.multipleStatements, true);
expect(run.returnRows, false);
return SimpleSuccessResponse(requestId: request.requestId, response: null);
});

await client.sendRequest(
RunQuery(
requestId: 0,
databaseId: 0,
sql: 'CREATE TABLE a (x); CREATE TABLE b (y);',
checkInTransaction: false,
lockId: null,
parameters: [],
returnRows: false,
multipleStatements: true,
),
MessageType.simpleSuccessResponse,
);
});

test('can serialize SqliteExceptions', () async {
server.handleRequestFunction = expectAsync1((req) {
throw SqliteException(
Expand Down