sqflite_common_ffi_web 0.2.0 sqflite_common_ffi_web: ^0.2.0 copied to clipboard
Sqflite web implementation using sqlite3 ffi and sqlite3 wasm.
import 'dart:async';
import 'dart:html' as html;
import 'dart:math';
import 'package:sqflite_common/sqlite_api.dart';
import 'package:sqflite_common/utils/utils.dart';
import 'package:sqflite_common_ffi_web/sqflite_ffi_web.dart';
import 'package:sqflite_common_ffi_web/src/sw/constants.dart';
import 'package:sqflite_common_ffi_web/src/web/load_sqlite_web.dart'
show SqfliteFfiWebContextExt;
import 'ui.dart';
var _debugVersion = 1;
var _shc = '/_shc$_debugVersion';
var swOptions = SqfliteFfiWebOptions(sharedWorkerUri: Uri.parse('sw.dart.js'));
Future incrementPrebuilt() async {
await incrementSqfliteValueInDatabaseFactory(databaseFactoryWebPrebuilt,
tag: 'prebuilt');
}
Future incrementWork() async {
await incrementSqfliteValueInDatabaseFactory(databaseFactoryWebLocal,
tag: 'work');
}
Future exceptionWork() async {
try {
var factory = databaseFactoryWebLocal;
var db = await factory.openDatabase('test_missing');
await db.query('Test');
} catch (e) {
write('exception $e');
}
}
class _Data {
late Database db;
}
/// Out internal data.
// ignore: library_private_types_in_public_api
final _Data data = _Data();
/// Get the value field from a given id
Future<dynamic> getValue(int id) async {
return ((await data.db.query('Test', where: 'id = $id')).first)['value'];
}
/// insert the value field and return the id
Future<int> insertValue(dynamic value) async {
return await data.db.insert('Test', {'value': value});
}
/// insert the value field and return the id
Future<int> updateValue(int id, dynamic value) async {
return await data.db.update('Test', {'value': value}, where: 'id = $id');
}
Future bigInt() async {
try {
var factory = databaseFactoryWebLocal;
var db = data.db = await factory.openDatabase('test_big_int');
await db.execute(
'CREATE TABLE IF NOT EXISTS Test (id INTEGER PRIMARY KEY AUTOINCREMENT, value INTEGER)');
await db.query('Test');
} catch (e) {
write('exception $e');
}
var id = await insertValue(-1);
assert(await getValue(id) == -1);
// less than 32 bits
id = await insertValue(pow(2, 31));
assert(await getValue(id) == pow(2, 31));
// more than 32 bits
id = await insertValue(pow(2, 33));
//devPrint('2^33: ${await getValue(id)}');
assert(await getValue(id) == pow(2, 33));
id = await insertValue(pow(2, 62));
//devPrint('2^62: ${pow(2, 62)} ${await getValue(id)}');
assert(await getValue(id) == pow(2, 62));
/*
var value = pow(2, 63).round() - 1;
id = await insertValue(value);
//devPrint('${value} ${await getValue(id)}');
expect(await getValue(id), value, reason: '$value ${await getValue(id)}');
value = -(pow(2, 63)).round();
id = await insertValue(value);
//devPrint('${value} ${await getValue(id)}');
expect(await getValue(id), value, reason: '$value ${await getValue(id)}');
*/
}
Future incrementNoWebWorker() async {
await incrementSqfliteValueInDatabaseFactory(
databaseFactoryWebNoWebWorkerLocal,
tag: 'ui');
}
Future<void> main() async {
initUi();
// sqliteFfiWebDebugWebWorker = devWarning(true);
write('$_shc running $_debugVersion');
// devWarning(incrementVarInServiceWorker());
// await devWarning(bigInt());
// await devWarning(exceptionWork());
// await devWarning(incrementWork());
// await devWarning(incrementPrebuilt());
// await incrementVarInServiceWorker();
// await incrementSqfliteValueInDatabaseFactory(
// databaseFactoryWebNoWebWorkerLocal);
// await incrementSqfliteValueInDatabaseFactory(databaseFactoryWebLocal);
// await devWarning(
// incrementSqfliteValueInDatabaseFactory(databaseFactoryWebPrebuilt));
}
var _sharedWorkerRegisterAndReady = sqfliteFfiWebStartSharedWorker(swOptions);
Future<html.SharedWorker> sharedWorkerRegisterAndReady() async =>
(await _sharedWorkerRegisterAndReady).sharedWorker!;
var databaseFactoryWebPrebuilt = databaseFactoryFfiWeb;
var databaseFactoryWebNoWebWorkerLocal = databaseFactoryFfiWebNoWebWorker;
var databaseFactoryWebLocal = createDatabaseFactoryFfiWeb(options: swOptions);
/// Returns response
Future<Object?> sharedWorkerSendRawMessage(
html.SharedWorker sw, Object message) {
var completer = Completer<Object?>();
// This wraps the message posting/response in a promise, which will resolve if the response doesn't
// contain an error, and reject with the error if it does. If you'd prefer, it's possible to call
// controller.postMessage() and set up the onmessage handler independently of a promise, but this is
// a convenient wrapper.
var messageChannel = html.MessageChannel();
//var receivePort =ReceivePort();
messageChannel.port1.onMessage.listen((event) {
print('Receiving from sw: ${event.data}');
completer.complete(event.data);
});
// This sends the message data as well as transferring messageChannel.port2 to the shared worker.
// The shared worker can then use the transferred port to reply via postMessage(), which
// will in turn trigger the onmessage handler on messageChannel.port1.
// See https://html.spec.whatwg.org/multipage/workers.html#dom-worker-postmessage
(sw.port as html.MessagePort).postMessage(
//
message,
//
[messageChannel.port2]
//((jsify([messageChannel.port2]) as List).cast<Object>())
//
);
return completer.future;
}
var key = 'testValue';
Future<Object?> getTestValue(html.SharedWorker sw) async {
var response = await sharedWorkerSendRawMessage(sw, [
commandVarGet,
{'key': key}
]) as Map;
return (response['result'] as Map)['value'] as Object?;
}
Future<void> setTestValue(html.SharedWorker sw, Object? value) async {
await sharedWorkerSendRawMessage(sw, [
commandVarSet,
{'key': key, 'value': value}
]);
}
Future<void> incrementVarInSharedWorker() async {
var sw = await sharedWorkerRegisterAndReady();
write('shared worker ready');
var value = await getTestValue(sw);
write('var before $value');
if (value is! int) {
value = 0;
}
await setTestValue(sw, value + 1);
value = await getTestValue(sw);
write('var after $value');
}
Future<void> incrementSqfliteValueInDatabaseFactory(DatabaseFactory factory,
{String? tag}) async {
tag ??= 'db';
try {
write('/$tag accessing db...');
// await factory.debugSetLogLevel(sqfliteLogLevelVerbose);
var db = await factory.openDatabase('test.db',
options: OpenDatabaseOptions(
version: 1,
onCreate: (db, version) async {
await db.execute(
'CREATE TABLE Test(id INTEGER PRIMARY KEY, value INTEGER)');
}));
Future<int?> readValue() async {
var value = firstIntValue(
await db.query('Test', columns: ['value'], where: 'id = 1')) ??
0;
return value;
}
var value = await readValue();
write('/$tag read before $value');
await db.insert('Test', {'id': 1, 'value': (value ?? 0) + 1},
conflictAlgorithm: ConflictAlgorithm.replace);
value = await readValue() ?? 0;
write('/$tag read after $value');
} catch (e) {
write('/$tag error: $e');
rethrow;
}
}
void initUi() {
addButton('load sqlite', () async {});
addButton('increment var in shared worker', () async {
await incrementVarInSharedWorker();
});
addButton('increment sqflite value in main thread', () async {
await incrementNoWebWorker();
});
addButton('increment sqflite value in web worker', () async {
await incrementWork();
});
addButton('exception in web worker', () async {
await exceptionWork();
});
addButton('increment sqflite value in pre-built web worker', () async {
await incrementPrebuilt();
});
}