Skip to content

Commit 62df868

Browse files
committed
Address CI issues in DatabaseTracker
1 parent 3345223 commit 62df868

File tree

2 files changed

+123
-4
lines changed

2 files changed

+123
-4
lines changed

drift/lib/src/sqlite3/database_tracker.dart

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ final DatabaseTracker tracker = DatabaseTracker();
2424
class DatabaseTracker {
2525
final Database _db;
2626

27+
/// Whether this [DatabaseTracker] has been disposed.
28+
bool _isDisposed = false;
29+
30+
/// Public getter to check if this tracker has been disposed.
31+
bool get isDisposed => _isDisposed;
32+
2733
/// Creates a new tracker with necessary tables.
2834
DatabaseTracker()
2935
: _db = sqlite3.open(
@@ -38,24 +44,36 @@ CREATE TABLE IF NOT EXISTS open_connections(
3844
''');
3945
}
4046

41-
/// Tracks the [openedDb]. The [path] argument can be used to track the path
42-
/// of that database, if it's bound to a file.
47+
/// Tracks the [openedDb]. The [path] argument can be used to track
48+
/// the path of that database, if it's bound to a file.
49+
///
50+
/// Throws a [StateError] if this tracker has already been disposed.
4351
void markOpened(String path, Database openedDb) {
52+
_checkIfDisposed();
53+
4454
final stmt = _db.prepare('INSERT INTO open_connections VALUES (?, ?)');
4555
stmt.execute([openedDb.handle.address, path]);
4656
stmt.dispose();
4757
}
4858

4959
/// Marks the database [db] as closed.
60+
///
61+
/// Throws a [StateError] if this tracker has already been disposed.
5062
void markClosed(Database db) {
63+
_checkIfDisposed();
64+
5165
final ptr = db.handle.address;
5266
_db.execute('DELETE FROM open_connections WHERE database_pointer = $ptr');
5367
}
5468

55-
/// Closes tracked database connections.
69+
/// Closes all tracked database connections that are still open.
70+
///
71+
/// This operation is wrapped in a transaction (`BEGIN` ... `COMMIT`).
72+
/// Throws a [StateError] if this tracker has already been disposed.
5673
void closeExisting() {
57-
_db.execute('BEGIN;');
74+
_checkIfDisposed();
5875

76+
_db.execute('BEGIN;');
5977
try {
6078
final results =
6179
_db.select('SELECT database_pointer FROM open_connections');
@@ -70,4 +88,33 @@ CREATE TABLE IF NOT EXISTS open_connections(
7088
_db.execute('COMMIT;');
7189
}
7290
}
91+
92+
/// Releases all open database handles managed by this tracker and
93+
/// closes the internal in-memory database.
94+
///
95+
/// After calling this method, this instance should no longer be used.
96+
/// Any further calls to public methods will throw a [StateError].
97+
void dispose() {
98+
if (_isDisposed) {
99+
return;
100+
}
101+
102+
// Safely close any tracked connections
103+
closeExisting();
104+
105+
// Dispose of the in-memory tracker database
106+
_db.dispose();
107+
108+
// Mark this tracker as disposed
109+
_isDisposed = true;
110+
}
111+
112+
/// Checks if this tracker has been disposed and throws a [StateError] if so.
113+
void _checkIfDisposed() {
114+
if (_isDisposed) {
115+
throw StateError(
116+
'DatabaseTracker has already been disposed and can no longer be used.',
117+
);
118+
}
119+
}
73120
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
import 'package:test/test.dart';
2+
import 'package:sqlite3/sqlite3.dart';
3+
import 'package:drift/src/sqlite3/database_tracker.dart';
4+
5+
void main() {
6+
group('DatabaseTracker', () {
7+
late DatabaseTracker tracker;
8+
9+
setUp(() {
10+
// Create a fresh DatabaseTracker instance before each test.
11+
tracker = DatabaseTracker();
12+
});
13+
14+
tearDown(() {
15+
// Clean up resources by disposing of the tracker.
16+
// Multiple dispose calls should be safe.
17+
tracker.dispose();
18+
});
19+
20+
test('tracks and closes existing database connections', () {
21+
// Open two in-memory SQLite databases.
22+
final db1 = sqlite3.openInMemory();
23+
final db2 = sqlite3.openInMemory();
24+
25+
// Register each database with the tracker.
26+
tracker.markOpened('db1_path', db1);
27+
tracker.markOpened('db2_path', db2);
28+
29+
// Optionally perform some queries to confirm the databases are working.
30+
db1.execute('CREATE TABLE test1 (id INTEGER NOT NULL PRIMARY KEY)');
31+
db2.execute('CREATE TABLE test2 (id INTEGER NOT NULL PRIMARY KEY)');
32+
33+
// Use the tracker to close all tracked connections.
34+
tracker.closeExisting();
35+
36+
// After closing, further queries should throw an error.
37+
expect(
38+
() => db1.execute('INSERT INTO test1 (id) VALUES (1)'),
39+
throwsA(anything),
40+
);
41+
expect(
42+
() => db2.execute('INSERT INTO test2 (id) VALUES (2)'),
43+
throwsA(anything),
44+
);
45+
});
46+
47+
test('throws StateError after disposal', () {
48+
// Dispose immediately
49+
tracker.dispose();
50+
51+
// Any usage of the tracker after dispose should throw a StateError.
52+
expect(
53+
() => tracker.markOpened('test', sqlite3.openInMemory()),
54+
throwsStateError,
55+
);
56+
expect(() => tracker.closeExisting(), throwsStateError);
57+
});
58+
59+
test('multiple calls to dispose are safe', () {
60+
// Dispose can be called multiple times without throwing errors.
61+
expect(() => tracker.dispose(), returnsNormally);
62+
expect(() => tracker.dispose(), returnsNormally);
63+
});
64+
65+
test('isDisposed reflects tracker state', () {
66+
expect(tracker.isDisposed, isFalse);
67+
68+
tracker.dispose();
69+
expect(tracker.isDisposed, isTrue);
70+
});
71+
});
72+
}

0 commit comments

Comments
 (0)