mirror of https://github.com/AMT-Cheif/drift.git
Automatically migrate from local storage to IndexedDb
This commit is contained in:
parent
29eec7f784
commit
cdd57f340d
|
@ -80,3 +80,17 @@ object via `window.db`. If you need to quickly run a query to check the state of
|
||||||
If you need to delete your databases, there stored using local storage. You can clear all your data with `localStorage.clear()`.
|
If you need to delete your databases, there stored using local storage. You can clear all your data with `localStorage.clear()`.
|
||||||
|
|
||||||
Web support is experimental at the moment, so please [report all issues](https://github.com/simolus3/moor/issues/new) you find.
|
Web support is experimental at the moment, so please [report all issues](https://github.com/simolus3/moor/issues/new) you find.
|
||||||
|
|
||||||
|
## Using IndexedDb
|
||||||
|
|
||||||
|
The default `WebDatabase` uses local storage to store the raw sqlite database file. On browsers that support it, you can also
|
||||||
|
use `IndexedDb` to store that blob. In general, browsers allow a larger size for `IndexedDb`. The implementation is also more
|
||||||
|
performant, since we don't have to encode binary blobs as strings.
|
||||||
|
|
||||||
|
To use this implementation on browsers that support it, replace `WebDatabase(name)` with:
|
||||||
|
|
||||||
|
```dart
|
||||||
|
WebDatabase.withStorage(MoorWebStorage.indexedDbIfSupported(name))
|
||||||
|
```
|
||||||
|
|
||||||
|
Moor will automatically migrate data from local storage to `IndexeDb` when it is available.
|
|
@ -8,6 +8,7 @@ library moor_web;
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:html';
|
import 'dart:html';
|
||||||
import 'dart:indexed_db';
|
import 'dart:indexed_db';
|
||||||
|
import 'dart:js';
|
||||||
|
|
||||||
import 'package:meta/meta.dart';
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
|
|
@ -29,9 +29,30 @@ abstract class MoorWebStorage {
|
||||||
/// This implementation is significantly faster than the default
|
/// This implementation is significantly faster than the default
|
||||||
/// implementation in local storage. Browsers also tend to allow more data
|
/// implementation in local storage. Browsers also tend to allow more data
|
||||||
/// to be saved in IndexedDB.
|
/// to be saved in IndexedDB.
|
||||||
|
///
|
||||||
|
/// When the [migrateFromLocalStorage] parameter (defaults to `true`) is set,
|
||||||
|
/// old data saved using the default [MoorWebStorage] will be migrated to the
|
||||||
|
/// IndexedDB based implementation. This parameter can be turned off for
|
||||||
|
/// applications that never used the local storage implementation as a small
|
||||||
|
/// performance improvement.
|
||||||
|
///
|
||||||
/// However, older browsers might not support IndexedDB.
|
/// However, older browsers might not support IndexedDB.
|
||||||
@experimental
|
@experimental
|
||||||
factory MoorWebStorage.indexedDb(String name) = _IndexedDbStorage;
|
factory MoorWebStorage.indexedDb(String name,
|
||||||
|
{bool migrateFromLocalStorage}) = _IndexedDbStorage;
|
||||||
|
|
||||||
|
/// Uses [MoorWebStorage.indexedDb] if the current browser supports it.
|
||||||
|
/// Otherwise, falls back to the local storage based implementation.
|
||||||
|
factory MoorWebStorage.indexedDbIfSupported(String name) {
|
||||||
|
return supportsIndexedDb
|
||||||
|
? MoorWebStorage.indexedDb(name)
|
||||||
|
: MoorWebStorage(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Attempts to check whether the current browser supports the
|
||||||
|
/// [MoorWebStorage.indexedDb] storage implementation.
|
||||||
|
static bool get supportsIndexedDb =>
|
||||||
|
IdbFactory.supported && context.hasProperty('FileReader');
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class _CustomSchemaVersionSave implements MoorWebStorage {
|
abstract class _CustomSchemaVersionSave implements MoorWebStorage {
|
||||||
|
@ -39,11 +60,27 @@ abstract class _CustomSchemaVersionSave implements MoorWebStorage {
|
||||||
set schemaVersion(int value);
|
set schemaVersion(int value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
String _persistenceKeyForLocalStorage(String name) {
|
||||||
|
return 'moor_db_str_$name';
|
||||||
|
}
|
||||||
|
|
||||||
|
String _legacyVersionKeyForLocalStorage(String name) {
|
||||||
|
return 'moor_db_version_$name';
|
||||||
|
}
|
||||||
|
|
||||||
|
Uint8List /*?*/ _restoreLocalStorage(String name) {
|
||||||
|
final raw = window.localStorage[_persistenceKeyForLocalStorage(name)];
|
||||||
|
if (raw != null) {
|
||||||
|
return bin2str.decode(raw);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
class _LocalStorageImpl implements MoorWebStorage, _CustomSchemaVersionSave {
|
class _LocalStorageImpl implements MoorWebStorage, _CustomSchemaVersionSave {
|
||||||
final String name;
|
final String name;
|
||||||
|
|
||||||
String get _persistenceKey => 'moor_db_str_$name';
|
String get _persistenceKey => _persistenceKeyForLocalStorage(name);
|
||||||
String get _versionKey => 'moor_db_version_$name';
|
String get _versionKey => _legacyVersionKeyForLocalStorage(name);
|
||||||
|
|
||||||
const _LocalStorageImpl(this.name);
|
const _LocalStorageImpl(this.name);
|
||||||
|
|
||||||
|
@ -69,11 +106,7 @@ class _LocalStorageImpl implements MoorWebStorage, _CustomSchemaVersionSave {
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<Uint8List> restore() async {
|
Future<Uint8List> restore() async {
|
||||||
final raw = window.localStorage[_persistenceKey];
|
return _restoreLocalStorage(name);
|
||||||
if (raw != null) {
|
|
||||||
return bin2str.decode(raw);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
@ -89,21 +122,33 @@ class _IndexedDbStorage implements MoorWebStorage {
|
||||||
static const _objectStoreName = 'moor_databases';
|
static const _objectStoreName = 'moor_databases';
|
||||||
|
|
||||||
final String name;
|
final String name;
|
||||||
|
final bool migrateFromLocalStorage;
|
||||||
|
|
||||||
Database _database;
|
Database _database;
|
||||||
|
|
||||||
_IndexedDbStorage(this.name);
|
_IndexedDbStorage(this.name, {this.migrateFromLocalStorage = true});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Future<void> open() async {
|
Future<void> open() async {
|
||||||
|
var wasCreated = false;
|
||||||
|
|
||||||
_database = await window.indexedDB.open(
|
_database = await window.indexedDB.open(
|
||||||
_objectStoreName,
|
_objectStoreName,
|
||||||
version: 1,
|
version: 1,
|
||||||
onUpgradeNeeded: (event) {
|
onUpgradeNeeded: (event) {
|
||||||
final database = event.target.result as Database;
|
final database = event.target.result as Database;
|
||||||
|
|
||||||
database.createObjectStore(_objectStoreName);
|
database.createObjectStore(_objectStoreName);
|
||||||
|
wasCreated = true;
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (migrateFromLocalStorage && wasCreated) {
|
||||||
|
final fromLocalStorage = _restoreLocalStorage(name);
|
||||||
|
if (fromLocalStorage != null) {
|
||||||
|
await store(fromLocalStorage);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
@TestOn('browser')
|
||||||
|
import 'dart:convert';
|
||||||
|
|
||||||
|
import 'package:moor/moor.dart';
|
||||||
|
import 'package:moor/moor_web.dart';
|
||||||
|
import 'package:test/test.dart';
|
||||||
|
|
||||||
|
final _allBytes = Uint8List.fromList(List.generate(256, (index) => index));
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
test('can migrate from local storage to IndexedDb', () async {
|
||||||
|
const local = MoorWebStorage('name');
|
||||||
|
final idb = MoorWebStorage.indexedDb('name');
|
||||||
|
|
||||||
|
await local.open();
|
||||||
|
await local.store(_allBytes);
|
||||||
|
await local.close();
|
||||||
|
|
||||||
|
await idb.open();
|
||||||
|
final restored = await idb.restore();
|
||||||
|
await idb.close();
|
||||||
|
|
||||||
|
expect(restored, _allBytes);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not migrate when idb database already exists', () async {
|
||||||
|
final otherPayload = Uint8List.fromList(utf8.encode('hello world'));
|
||||||
|
|
||||||
|
const local = MoorWebStorage('name');
|
||||||
|
final idb = MoorWebStorage.indexedDb('name');
|
||||||
|
|
||||||
|
await idb.open();
|
||||||
|
await idb.store(otherPayload);
|
||||||
|
await idb.close();
|
||||||
|
|
||||||
|
await local.open();
|
||||||
|
await local.store(_allBytes);
|
||||||
|
await local.close();
|
||||||
|
|
||||||
|
await idb.open();
|
||||||
|
final restored = await idb.restore();
|
||||||
|
await idb.close();
|
||||||
|
|
||||||
|
expect(restored, otherPayload);
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in New Issue