Skip to content

Commit

Permalink
Add consistent methods to delete databases
Browse files Browse the repository at this point in the history
  • Loading branch information
simolus3 committed Nov 29, 2024
1 parent dced60d commit d0b38f3
Show file tree
Hide file tree
Showing 9 changed files with 113 additions and 18 deletions.
5 changes: 5 additions & 0 deletions sqlite3/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,8 @@
## 2.6.0-dev

- Add `SimpleOpfsFileSystem.deleteFromStorage` to delete OPFS-based file
systems.

## 2.5.0

- Allow registering custom virtual file systems on all platforms. Previously,
Expand Down
49 changes: 44 additions & 5 deletions sqlite3/lib/src/wasm/vfs/simple_opfs.dart
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@ import 'package:web/web.dart'
show
FileSystemDirectoryHandle,
FileSystemSyncAccessHandle,
FileSystemReadWriteOptions;
FileSystemReadWriteOptions,
FileSystemRemoveOptions,
DOMException;

import '../../constants.dart';
import '../../vfs.dart';
Expand Down Expand Up @@ -68,6 +70,24 @@ final class SimpleOpfsFileSystem extends BaseVirtualFileSystem {
{String vfsName = 'simple-opfs'})
: super(name: vfsName);

static Future<(FileSystemDirectoryHandle?, FileSystemDirectoryHandle)>
_resolveDir(String path, {bool create = true}) async {
final storage = storageManager;
if (storage == null) {
throw VfsException(SqlError.SQLITE_ERROR);
}

FileSystemDirectoryHandle? parent;
var opfsDirectory = await storage.directory;

for (final segment in p.split(path)) {
parent = opfsDirectory;
opfsDirectory = await opfsDirectory.getDirectory(segment, create: create);
}

return (parent, opfsDirectory);
}

/// Loads an [SimpleOpfsFileSystem] in the desired [path] under the root directory
/// for OPFS as given by `navigator.storage.getDirectory()` in JavaScript.
///
Expand All @@ -81,13 +101,32 @@ final class SimpleOpfsFileSystem extends BaseVirtualFileSystem {
throw VfsException(SqlError.SQLITE_ERROR);
}

var opfsDirectory = await storage.directory;
final (_, directory) = await _resolveDir(path);
return inDirectory(directory, vfsName: vfsName);
}

for (final segment in p.split(path)) {
opfsDirectory = await opfsDirectory.getDirectory(segment, create: true);
/// Deletes the file system directory handle that would store sqlite3
/// databases when using [loadFromStorage] with the same path.
static Future<void> deleteFromStorage(String path) async {
final FileSystemDirectoryHandle? parent;
final FileSystemDirectoryHandle handle;

try {
(parent, handle) = await _resolveDir(path, create: false);
} on DOMException catch (e) {
if (e.name == 'NotFoundError' || e.name == 'TypeMismatchError') {
// Directory doesn't exist, ignore.
return;
} else {
rethrow;
}
}

return inDirectory(opfsDirectory, vfsName: vfsName);
if (parent != null) {
await parent
.removeEntry(handle.name, FileSystemRemoveOptions(recursive: true))
.toDart;
}
}

/// Loads an [SimpleOpfsFileSystem] in the desired [root] directory, which must be
Expand Down
2 changes: 1 addition & 1 deletion sqlite3/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: sqlite3
description: Provides lightweight yet convenient bindings to SQLite by using dart:ffi
version: 2.5.0
version: 2.6.0-dev
homepage: https://github.com/simolus3/sqlite3.dart/tree/main/sqlite3
issue_tracker: https://github.com/simolus3/sqlite3.dart/issues

Expand Down
4 changes: 4 additions & 0 deletions sqlite3_web/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 0.2.1

- Add `WebSqlite.deleteDatabase` to delete databases.

## 0.2.0

- Make `FileSystem` implementation functional, add `FileSystem.flush()`.
Expand Down
15 changes: 14 additions & 1 deletion sqlite3_web/lib/src/client.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import 'dart:js_interop';
import 'dart:js_interop_unsafe';
import 'dart:typed_data';

import 'package:sqlite3/common.dart';
import 'package:sqlite3/wasm.dart' hide WorkerOptions;
import 'package:web/web.dart'
hide Response, Request, FileSystem, Notification, Lock;

Expand All @@ -12,6 +12,7 @@ import 'channel.dart';
import 'database.dart';
import 'protocol.dart';
import 'shared.dart';
import 'worker.dart';

final class RemoteDatabase implements Database {
final WorkerConnection connection;
Expand Down Expand Up @@ -290,6 +291,18 @@ final class DatabaseClient implements WebSqlite {
});
}

@override
Future<void> deleteDatabase(
{required String name, required StorageMode storage}) async {
switch (storage) {
case StorageMode.opfs:
await SimpleOpfsFileSystem.deleteFromStorage(pathForOpfs(name));
case StorageMode.indexedDb:
await IndexedDbFileSystem.deleteDatabase(name);
case StorageMode.inMemory:
}
}

@override
Future<FeatureDetectionResult> runFeatureDetection(
{String? databaseName}) async {
Expand Down
6 changes: 6 additions & 0 deletions sqlite3_web/lib/src/database.dart
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ final class ConnectToRecommendedResult {
/// Please see the readme of the `sqlite3_web` package for an overview on how
/// to set up and use this package.
abstract class WebSqlite {
/// Deletes a database from the [storage] if it exists.
///
/// This method should not be called while the database is still open.
Future<void> deleteDatabase(
{required String name, required StorageMode storage});

/// Tries to find features related to storing and accessing databases.
///
/// The [databaseName] can optionally be used to make
Expand Down
32 changes: 21 additions & 11 deletions sqlite3_web/test/integration_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -128,17 +128,7 @@ void main() {

driver = TestWebDriver(server, rawDriver);
await driver.driver.get('http://localhost:8080/');

while (true) {
try {
// This element is created after main() has completed, make sure
// all the callbacks have been installed.
await driver.driver.findElement(By.id('ready'));
break;
} on NoSuchElementException {
continue;
}
}
await driver.waitReady();
});

tearDown(() => driver.driver.quit());
Expand Down Expand Up @@ -170,6 +160,26 @@ void main() {

expect(await driver.assertFile(true), isPositive);
await driver.flush();

if (storage != StorageMode.inMemory) {
await driver.driver.refresh();
await driver.waitReady();

await driver.openDatabase(
implementation: (storage, access),
onlyOpenVfs: true,
);
await driver.assertFile(true);

await driver.driver.refresh();
await driver.waitReady();
await driver.delete(storage);
await driver.openDatabase(
implementation: (storage, access),
onlyOpenVfs: true,
);
await driver.assertFile(false);
}
});
}
});
Expand Down
12 changes: 12 additions & 0 deletions sqlite3_web/tool/server.dart
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import 'package:shelf/shelf_io.dart';
import 'package:shelf_proxy/shelf_proxy.dart';
import 'package:webdriver/async_io.dart';
import 'package:sqlite3_web/src/types.dart';
import 'package:webdriver/support/async.dart';

void main() async {
await TestAssetServer.start();
Expand Down Expand Up @@ -104,6 +105,12 @@ class TestWebDriver {

TestWebDriver(this.server, this.driver);

/// Wait for the Dart code on the test page to finish its main method, which
/// it signals by creating an element.
Future<void> waitReady() async {
await waitFor(() => driver.findElement(By.id('ready')));
}

Future<
({
Set<(StorageMode, AccessMode)> impls,
Expand Down Expand Up @@ -201,4 +208,9 @@ class TestWebDriver {
throw 'flush() failed: $result';
}
}

Future<void> delete(StorageMode mode) async {
await driver
.executeAsync('delete_db(arguments[0], arguments[1])', [mode.name]);
}
}
6 changes: 6 additions & 0 deletions sqlite3_web/web/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,12 @@ void main() {
await vfs.flush();
return true.toJS;
});
_addCallbackForWebDriver('delete_db', (arg) async {
final storage = StorageMode.values.byName(arg!);
await initializeSqlite()
.deleteDatabase(name: databaseName, storage: storage);
return true.toJS;
});

document.getElementById('selfcheck')?.onClick.listen((event) async {
print('starting');
Expand Down

0 comments on commit d0b38f3

Please sign in to comment.