diff --git a/eslint.config.mjs b/eslint.config.mjs index 2beadb519c4954..3586af4ebf3a8e 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -238,7 +238,7 @@ export default [ 'no-throw-literal': 'error', 'no-undef': ['error', { typeof: true }], 'no-undef-init': 'error', - 'no-unused-expressions': ['error', { allowShortCircuit: true }], + 'no-unused-expressions': ['error', { allowShortCircuit: true, allowTaggedTemplates: true }], 'no-unused-vars': ['error', { args: 'none', caughtErrors: 'all' }], 'no-use-before-define': ['error', { classes: true, diff --git a/src/env_properties.h b/src/env_properties.h index 5c5271e344da2b..014e78702d86b9 100644 --- a/src/env_properties.h +++ b/src/env_properties.h @@ -162,6 +162,7 @@ V(errno_string, "errno") \ V(error_string, "error") \ V(errstr_string, "errstr") \ + V(errmsg_string, "errmsg") \ V(events_waiting, "eventsWaiting") \ V(events, "events") \ V(exclusive_string, "exclusive") \ diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index cf9ce6686cffca..d447ebdf741336 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -188,6 +188,35 @@ inline MaybeLocal CreateSQLiteError(Isolate* isolate, sqlite3* db) { return e; } +inline MaybeLocal CreateSQLiteError(Isolate* isolate, + sqlite3* db, + const char* message) { + int errcode = sqlite3_extended_errcode(db); + const char* errstr = sqlite3_errstr(errcode); + const char* errmsg = sqlite3_errmsg(db); + Local js_errstr; + Local js_errmsg; + Local e; + if (!String::NewFromUtf8(isolate, errstr).ToLocal(&js_errstr) || + !String::NewFromUtf8(isolate, errmsg).ToLocal(&js_errmsg) || + !CreateSQLiteError(isolate, message).ToLocal(&e) || + e->Set(isolate->GetCurrentContext(), + Environment::GetCurrent(isolate)->errcode_string(), + Integer::New(isolate, errcode)) + .IsNothing() || + e->Set(isolate->GetCurrentContext(), + Environment::GetCurrent(isolate)->errstr_string(), + js_errstr) + .IsNothing() || + e->Set(isolate->GetCurrentContext(), + Environment::GetCurrent(isolate)->errmsg_string(), + js_errmsg) + .IsNothing()) { + return MaybeLocal(); + } + return e; +} + void JSValueToSQLiteResult(Isolate* isolate, sqlite3_context* ctx, Local value) { @@ -241,6 +270,20 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, const char* message) { } } +inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, + DatabaseSync* db, + const char* message) { + if (db->ShouldIgnoreSQLiteError()) { + db->SetIgnoreNextSQLiteError(false); + return; + } + + Local e; + if (CreateSQLiteError(isolate, db->Connection(), message).ToLocal(&e)) { + isolate->ThrowException(e); + } +} + inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, int errcode) { const char* errstr = sqlite3_errstr(errcode); @@ -2906,7 +2949,8 @@ BaseObjectPtr SQLTagStore::PrepareStatement( session->database_->connection_, sql.data(), sql.size(), &s, 0); if (r != SQLITE_OK) { - THROW_ERR_SQLITE_ERROR(isolate, "Failed to prepare statement"); + THROW_ERR_SQLITE_ERROR( + isolate, session->database_.get(), "Failed to prepare statement"); sqlite3_finalize(s); return BaseObjectPtr(); } diff --git a/test/parallel/test-sqlite-template-tag.js b/test/parallel/test-sqlite-template-tag.js index 8628200cf8930f..ff68ed459629b3 100644 --- a/test/parallel/test-sqlite-template-tag.js +++ b/test/parallel/test-sqlite-template-tag.js @@ -100,3 +100,16 @@ test('TagStore capacity, size, and clear', () => { test('sql.db returns the associated DatabaseSync instance', () => { assert.strictEqual(sql.db, db); }); + +test('failed prepares throw', () => { + assert.throws(() => { + sql.all`SELECT * FROM does_not_exist`; + }, { + name: 'Error', + message: 'Failed to prepare statement', + code: 'ERR_SQLITE_ERROR', + errcode: 1, + errstr: 'SQL logic error', + errmsg: 'no such table: does_not_exist' + }); +});