66
77#include " strings.h"
88
9+ #include < workerd/jsg/exception.h>
910#include < workerd/util/autogate.h>
1011#include < workerd/util/sentry.h>
1112
@@ -196,7 +197,19 @@ void tagSentry(kj::Exception& e, kj::StringPtr tag) {
196197 }
197198}
198199
199- [[noreturn]] void throwSentryException (kj::Exception&& e, kj::StringPtr tag) {
200+ // Applies the context given to `SqliteDatabase::setErrorContext()`, if any. Tunneled exceptions
201+ // are left untouched because their description is shown to the application; a VFS callback may
202+ // throw one (e.g. when a write exceeds a storage limit).
203+ void appendErrorContext (kj::Exception& e, kj::Maybe<kj::StringPtr> errorContext) {
204+ if (jsg::isTunneledException (e.getDescription ())) return ;
205+ KJ_IF_SOME (context, errorContext) {
206+ e.setDescription (kj::str (e.getDescription (), " ; " , context));
207+ }
208+ }
209+
210+ [[noreturn]] void throwSentryException (
211+ kj::Exception&& e, kj::StringPtr tag, kj::Maybe<kj::StringPtr> errorContext) {
212+ appendErrorContext (e, errorContext);
200213 tagSentry (e, tag);
201214 kj::throwFatalException (kj::mv (e));
202215}
@@ -231,8 +244,9 @@ class SqliteCallScope {
231244 vfsErrorListener = nullptr ;
232245 }
233246
234- void rethrowVfsError () {
247+ void rethrowVfsError (kj::Maybe<kj::StringPtr> errorContext ) {
235248 KJ_IF_SOME (e, error) {
249+ appendErrorContext (e, errorContext);
236250 // Slight hack: The exception already has a stack trace attached which should include the
237251 // current stack, but `kj::throwFatalException()` would re-append the current stack trace
238252 // to the exception. We can avoid that by calling
@@ -257,13 +271,14 @@ class SqliteCallScope {
257271
258272// Like KJ_REQUIRE() but give the Regulator a chance to report the error. `errorMessage` is either
259273// the return value of sqlite3_errmsg() or a string literal containing a similarly
260- // application-approriate error message. A reference called `regulator` must be in-scope.
274+ // application-approriate error message. A reference called `regulator` must be in-scope, as must
275+ // a `getErrorContext()` method (i.e. this must be used within SqliteDatabase or Query).
261276// sqliteErrorCode is a kj::Maybe<int> and represents the error code from sqlite.
262277#define SQLITE_REQUIRE_WITH_TAG (condition, sqliteErrorCode, sentryTag, errorMessage, ...) \
263278 if (!(condition)) { \
264279 regulator->onError (sqliteErrorCode, errorMessage); \
265- throwSentryException ( \
266- KJ_EXCEPTION ( FAILED , " SQLite failed " , errorMessage, ##__VA_ARGS__), sentryTag); \
280+ throwSentryException (KJ_EXCEPTION ( FAILED , " SQLite failed " , errorMessage, ##__VA_ARGS__), \
281+ sentryTag, getErrorContext ()); \
267282 }
268283
269284#define SQLITE_REQUIRE (condition, sqliteErrorCode, errorMessage, ...) \
@@ -275,12 +290,12 @@ class SqliteCallScope {
275290 do { \
276291 SqliteCallScope sqliteCallScope; \
277292 int _ec = code; \
278- if (_ec != SQLITE_OK ) sqliteCallScope.rethrowVfsError (); \
293+ if (_ec != SQLITE_OK ) sqliteCallScope.rethrowVfsError (kj::none); \
279294 if (_ec != SQLITE_OK ) { \
280295 throwSentryException ( \
281296 KJ_EXCEPTION ( \
282297 FAILED , kj::str (sqlite3_errstr (_ec), " : " , namedErrorCode (_ec)), ##__VA_ARGS__), \
283- " SENTRY_DO" _kj); \
298+ " SENTRY_DO" _kj, kj::none); \
284299 } \
285300 } while (false )
286301
@@ -293,7 +308,7 @@ class SqliteCallScope {
293308 /* SQLITE_MISUSE doesn't put error info on the database object, so check it separately */ \
294309 KJ_ASSERT (_ec != SQLITE_MISUSE , " SQLite misused: " #code, ##__VA_ARGS__); \
295310 handleCriticalError (_ec, dbErrorMessage (_ec, db), sqliteCallScope.getException ()); \
296- if (_ec == SQLITE_IOERR ) sqliteCallScope.rethrowVfsError (); \
311+ if (_ec == SQLITE_IOERR ) sqliteCallScope.rethrowVfsError (getErrorContext ()); \
297312 SQLITE_REQUIRE (_ec == SQLITE_OK , _ec, dbErrorMessage (_ec, db), ##__VA_ARGS__); \
298313 } while (false )
299314
@@ -306,7 +321,7 @@ class SqliteCallScope {
306321 do { \
307322 KJ_ASSERT (error != SQLITE_MISUSE , " SQLite misused: " code, ##__VA_ARGS__); \
308323 handleCriticalError (error, dbErrorMessage (error, db), sqliteCallScope.getException ()); \
309- if (error == SQLITE_IOERR ) sqliteCallScope.rethrowVfsError (); \
324+ if (error == SQLITE_IOERR ) sqliteCallScope.rethrowVfsError (getErrorContext ()); \
310325 SQLITE_REQUIRE_WITH_TAG ( \
311326 error != SQLITE_BUSY , error, " NOSENTRY" _kj, dbErrorMessage (error, db), ##__VA_ARGS__); \
312327 SQLITE_REQUIRE (error == SQLITE_OK , error, dbErrorMessage (error, db), ##__VA_ARGS__); \
0 commit comments