diff --git a/doc/api/sqlite.md b/doc/api/sqlite.md index 8a3592113a5724..7693738182cfaf 100644 --- a/doc/api/sqlite.md +++ b/doc/api/sqlite.md @@ -251,6 +251,8 @@ added: `SQLITE_CHANGESET_DATA` or `SQLITE_CHANGESET_CONFLICT` conflicts). * `SQLITE_CHANGESET_ABORT`: Abort on conflict and roll back the database. + When an error is thrown in the conflict handler, applying the changeset is aborted and the database is rolled back. + **Default**: A function that returns `SQLITE_CHANGESET_ABORT`. * Returns: {boolean} Whether the changeset was applied succesfully without being aborted. diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 5f3a8aae3d6b24..bcd90f129786df 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -9,6 +9,7 @@ #include "node_mem-inl.h" #include "sqlite3.h" #include "util-inl.h" +#include "v8-exception.h" #include @@ -42,6 +43,7 @@ using v8::Number; using v8::Object; using v8::SideEffectType; using v8::String; +using v8::TryCatch; using v8::Uint8Array; using v8::Value; @@ -795,9 +797,14 @@ void DatabaseSync::ApplyChangeset(const FunctionCallbackInfo& args) { Local conflictFunc = conflictValue.As(); conflictCallback = [env, conflictFunc](int conflictType) -> int { Local argv[] = {Integer::New(env->isolate(), conflictType)}; + TryCatch try_catch(env->isolate()); Local result = conflictFunc->Call(env->context(), Null(env->isolate()), 1, argv) - .ToLocalChecked(); + .FromMaybe(Local()); + if (try_catch.HasCaught()) { + try_catch.ReThrow(); + return SQLITE_CHANGESET_ABORT; + } return result->Int32Value(env->context()).FromJust(); }; } diff --git a/test/parallel/test-sqlite-session.js b/test/parallel/test-sqlite-session.js index 4289724abe1247..0c9658283b529e 100644 --- a/test/parallel/test-sqlite-session.js +++ b/test/parallel/test-sqlite-session.js @@ -336,6 +336,20 @@ suite('conflict resolution', () => { message: 'bad parameter or other API misuse' }); }); + + test("conflict resolution handler throws", (t) => { + const { database2, changeset } = prepareConflict(); + t.assert.throws(() => { + database2.applyChangeset(changeset, { + onConflict: () => { + throw new Error('some error'); + } + }); + }, { + name: 'Error', + message: 'some error' + }); + }); }); test('database.createSession() - filter changes', (t) => {