1
+ import 'dart:async' ;
2
+
1
3
import 'package:meta/meta.dart' ;
2
4
import 'package:sentry/sentry.dart' ;
3
5
import 'package:sqflite/sqflite.dart' ;
@@ -32,7 +34,10 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database {
32
34
// ignore: public_member_api_docs
33
35
static const dbSqlQueryOp = 'db.sql.query' ;
34
36
35
- static const _dbSqlOp = 'db.sql.transaction' ;
37
+ static const _dbSqlTransactionOp = 'db.sql.transaction' ;
38
+
39
+ static const _dbSqlReadTransactionOp = 'db.sql.read_transaction' ;
40
+
36
41
@internal
37
42
// ignore: public_member_api_docs
38
43
static const dbSystemKey = 'db.system' ;
@@ -143,7 +148,7 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database {
143
148
final currentSpan = _hub.getSpan ();
144
149
final description = 'Transaction DB: ${_database .path }' ;
145
150
final span = currentSpan? .startChild (
146
- _dbSqlOp ,
151
+ _dbSqlTransactionOp ,
147
152
description: description,
148
153
);
149
154
// ignore: invalid_use_of_internal_member
@@ -152,7 +157,7 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database {
152
157
153
158
var breadcrumb = Breadcrumb (
154
159
message: description,
155
- category: _dbSqlOp ,
160
+ category: _dbSqlTransactionOp ,
156
161
data: {},
157
162
type: 'query' ,
158
163
);
@@ -196,4 +201,86 @@ class SentryDatabase extends SentryDatabaseExecutor implements Database {
196
201
}
197
202
});
198
203
}
204
+
205
+ @override
206
+ // ignore: override_on_non_overriding_member, public_member_api_docs
207
+ Future <T > readTransaction <T >(Future <T > Function (Transaction txn) action) {
208
+ return Future <T >(() async {
209
+ final currentSpan = _hub.getSpan ();
210
+ final description = 'Transaction DB: ${_database .path }' ;
211
+ final span = currentSpan? .startChild (
212
+ _dbSqlReadTransactionOp,
213
+ description: description,
214
+ );
215
+ // ignore: invalid_use_of_internal_member
216
+ span? .origin = SentryTraceOrigins .autoDbSqfliteDatabase;
217
+ setDatabaseAttributeData (span, dbName);
218
+
219
+ var breadcrumb = Breadcrumb (
220
+ message: description,
221
+ category: _dbSqlReadTransactionOp,
222
+ data: {},
223
+ type: 'query' ,
224
+ );
225
+ setDatabaseAttributeOnBreadcrumb (breadcrumb, dbName);
226
+
227
+ Future <T > newAction (Transaction txn) async {
228
+ final executor = SentryDatabaseExecutor (
229
+ txn,
230
+ parentSpan: span,
231
+ hub: _hub,
232
+ dbName: dbName,
233
+ );
234
+ final sentrySqfliteTransaction =
235
+ SentrySqfliteTransaction (executor, hub: _hub, dbName: dbName);
236
+
237
+ return await action (sentrySqfliteTransaction);
238
+ }
239
+
240
+ try {
241
+ final futureOrResult = _resolvedReadTransaction (newAction);
242
+ T result;
243
+
244
+ if (futureOrResult is Future <T >) {
245
+ result = await futureOrResult;
246
+ } else {
247
+ result = futureOrResult;
248
+ }
249
+
250
+ span? .status = SpanStatus .ok ();
251
+ breadcrumb.data? ['status' ] = 'ok' ;
252
+
253
+ return result;
254
+ } catch (exception) {
255
+ span? .throwable = exception;
256
+ span? .status = SpanStatus .internalError ();
257
+ breadcrumb.data? ['status' ] = 'internal_error' ;
258
+ breadcrumb = breadcrumb.copyWith (
259
+ level: SentryLevel .warning,
260
+ );
261
+
262
+ rethrow ;
263
+ } finally {
264
+ await span? .finish ();
265
+
266
+ // ignore: invalid_use_of_internal_member
267
+ await _hub.scope.addBreadcrumb (breadcrumb);
268
+ }
269
+ });
270
+ }
271
+
272
+ FutureOr <T > _resolvedReadTransaction <T >(
273
+ Future <T > Function (Transaction txn) action,
274
+ ) async {
275
+ try {
276
+ // ignore: return_of_invalid_type
277
+ final result = await (_database as dynamic ).readTransaction (action);
278
+ // Await and cast, as directly returning the future resulted in a runtime error.
279
+ return result as T ;
280
+ } on NoSuchMethodError catch (_) {
281
+ // The `readTransaction` does not exists on sqflite version < 2.5.0+2.
282
+ // Fallback to transaction instead.
283
+ return _database.transaction (action);
284
+ }
285
+ }
199
286
}
0 commit comments