Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

sqlite: custom function callback errors should be reported to sqlite API #56772

Open
Renegade334 opened this issue Jan 26, 2025 · 2 comments · May be fixed by #56787
Open

sqlite: custom function callback errors should be reported to sqlite API #56772

Renegade334 opened this issue Jan 26, 2025 · 2 comments · May be fixed by #56787

Comments

@Renegade334
Copy link
Contributor

In the callback wrapper for user-provided functions (ie. those added with DatabaseSync#function()), error conditions in the JS callback result in an early return to the sqlite API:

node/src/node_sqlite.cc

Lines 202 to 207 in 1c7c32f

MaybeLocal<Value> retval =
fn->Call(env->context(), recv, argc, js_argv.data());
Local<Value> result;
if (!retval.ToLocal(&result)) {
return;
}

In sqlite, this results in the return value of the user-defined function being implicitly set to NULL. It does not signal an error condition from the callback.

This raises the potential for user function callbacks to encounter error conditions, but still cause side-effects in sqlite. As a basic example:

const sqlite = require('node:sqlite');
const db = new sqlite.DatabaseSync(':memory:');
db.exec('CREATE TABLE test (id NUMBER NOT NULL PRIMARY KEY, data TEXT)');

// create a user-defined function that throws
db.function('MYFUNC', function () { throw new Error('MYFUNC callback threw!') });

// fill table with arbitrary data
{
  const stmt = db.prepare('INSERT INTO test (id, data) VALUES (?, ?)');
  for (let i = 0; i < 10; i++) {
    stmt.run(i, Math.random().toString());
  }
}

db.prepare('SELECT * FROM test').all();
// [
//   [Object: null prototype] { id: 0, data: '0.5786694350792139' },
//   [Object: null prototype] { id: 1, data: '0.9442753197210123' },
//   ...
// ]

db.exec('UPDATE test SET data = MYFUNC()');
// Uncaught Error: MYFUNC callback threw!

db.prepare('SELECT * FROM test').all();
// [
//   [Object: null prototype] { id: 0, data: null },
//   [Object: null prototype] { id: 1, data: null },
//   ...
// ]

Error conditions in the user function callback should call sqlite3_result_error() in order to signal the sqlite API that an error has occurred and that the query should be aborted.

Charlesnorris509 added a commit to Charlesnorris509/node that referenced this issue Jan 26, 2025
Fixes nodejs#56772

Add error reporting for custom function callback errors to the sqlite API.

* **src/node_sqlite.cc**
  - Call `sqlite3_result_error()` in `UserDefinedFunction::xFunc` when the JS callback throws an error.
  - Update the `retval` check to handle the error condition and call `sqlite3_result_error()`.

* **test/parallel/test-sqlite-custom-functions.js**
  - Add a new test to verify that custom function callback errors are reported to the sqlite API.
  - Write tests to ensure that error conditions in the JS callback result in `sqlite3_result_error()` being called.

---

For more details, open the [Copilot Workspace session](https://copilot-workspace.githubnext.com/nodejs/node/issues/56772?shareId=XXXX-XXXX-XXXX-XXXX).
cjihrig added a commit to cjihrig/node that referenced this issue Jan 26, 2025
This commit adds support for the situation where SQLite is
trying to report an error while JavaScript already has an
exception pending.

Fixes: nodejs#56772
@cjihrig
Copy link
Contributor

cjihrig commented Jan 26, 2025

Nice find. I've put together a patch in cjihrig@f113c39. There is already another PR though (#56773), so I'm not sure how we want to proceed. Even though node:sqlite is experimental, I think we should aim to get this fixed in the next release possible since it risks data corruption.

@Renegade334
Copy link
Contributor Author

Renegade334 commented Jan 26, 2025

My $0.02 would be that there are considerations beyond simply adding a one-liner (I was going to comment about the other silent returns, but the point about deduplicating exceptions is a very good one) that make the best course of action to open a new PR and supersede the first. With what would likely be a lot of iterations of recommend-modify-review, I doubt the existing one would be committable in time for the current release cycle.

@cjihrig cjihrig linked a pull request Jan 27, 2025 that will close this issue
cjihrig added a commit to cjihrig/node that referenced this issue Jan 29, 2025
This commit adds support for the situation where SQLite is
trying to report an error while JavaScript already has an
exception pending.

Fixes: nodejs#56772
cjihrig added a commit to cjihrig/node that referenced this issue Jan 29, 2025
This commit adds support for the situation where SQLite is
trying to report an error while JavaScript already has an
exception pending.

Fixes: nodejs#56772
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
2 participants