sqlite: check sqlite3_step() and sqlite3_reset() results - #63319
sqlite: check sqlite3_step() and sqlite3_reset() results#63319semimikoh wants to merge 4 commits into
Conversation
|
Review requested:
|
557bcde to
4740589
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #63319 +/- ##
==========================================
- Coverage 90.32% 90.31% -0.01%
==========================================
Files 759 759
Lines 248328 248352 +24
Branches 46856 46868 +12
==========================================
- Hits 224296 224293 -3
+ Misses 15466 15459 -7
- Partials 8566 8600 +34
🚀 New features to boost your workflow:
|
|
I tried to reproduce a situation where the current code wouldn't catch the error but I couldn't. Please, get such a case into a test so it's clear which situation we must cover. |
TrevorBurnham
left a comment
There was a problem hiding this comment.
Reviewed the reset-error handling. The direction looks right; sqlite3_reset() returning a deferred error from the prior sqlite3_step() is real and worth surfacing. Two things I'd want addressed:
-
In
StatementSyncIterator,RESET_OR_THROWexpands to areturn, so the new throwing paths skipiter->done_ = trueeven though the statement has already been reset. A caught error then leaves the iterator resumable, and it replays the result set from the top. Details inline. -
No tests.
get()/all()can now throw where they previously returned an already-built row/array, which is a user-visible change on a success path. Worth coverage pinning the new behavior, plus a note on whether it needs semver-major.
Things I checked that look correct: needs_reset = false is sequenced before sqlite3_reset(), so there's no double reset on the throwing path; no function can call THROW_ERR_SQLITE_ERROR twice, so the ShouldIgnoreSQLiteError() one-shot isn't consumed twice; void() threads through both macro layers; and every RESET_AND_CHECK caller keeps its OnScopeLeave safety net for the earlier failure paths.
4740589 to
4443a70
Compare
|
Rebased onto
|
This comment was marked as outdated.
This comment was marked as outdated.
4443a70 to
ba4e195
Compare
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
This comment was marked as outdated.
ba4e195 to
ea9f2df
Compare
This comment was marked as outdated.
This comment was marked as outdated.
ea9f2df to
52592e6
Compare
|
@trivikr CI is green now (conflicts resolved, lint fixed). Ready for another look whenever you have time. |
|
Fixed — swapped the order so On the test request: I don't think a failing-reset test is constructible for this specific branch. This code path is only reached after /* If the VM did not run to completion or if it encountered an
** error, then it might not have been halted properly. So halt
** it now.
*/
if( p->eVdbeState==VDBE_RUN_STATE ) sqlite3VdbeHalt(p);
The |
Signed-off-by: semimikoh <ejffjeosms@gmail.com>
- StatementSyncIterator::Return() no longer throws on a deferred reset error, matching the OnScopeLeave guards used elsewhere: it is invoked during abrupt iterator completion (e.g. a throw inside a for...of body), and throwing there would discard the caller's already-pending exception. - Add a short comment on the accepted SQLITE_ROW result in Run(). - Add tests covering get()/all() surfacing a deferred SQLite error from reset() after already building a row/array, the iterator not replaying results after natural exhaustion, and a pending exception propagating correctly when the loop body throws mid-iteration. Signed-off-by: semimikoh <ejffjeosms@gmail.com>
RESET_OR_THROW returns early on a deferred error, so the previous ordering skipped iter->done_ = true when the reset in the natural exhaustion path failed. The statement was reset either way, so setting done_ first is safe and avoids leaving a caught error in a resumable state. Signed-off-by: semimikoh <ejffjeosms@gmail.com>
Rebasing onto main picked up "sqlite: refactor error helpers and user function pointers", which moved SQLTagStore::All()'s reset and bind logic into ResetAndBindStatement() and removed the local Isolate* isolate declaration that this PR's trailing RESET_AND_CHECK call still depends on. Re-add it. Signed-off-by: semimikoh <ejffjeosms@gmail.com>
6f667c2 to
90fe5e2
Compare
Summary
Per the SQLite docs,
sqlite3_reset(S)may return a deferred error codefrom the prior
sqlite3_step(S)call. Several statement execution paths insrc/node_sqlite.ccdropped that return value, which could silently ignoreSQLite errors.
This also checks the previously ignored
sqlite3_step()result inStatementExecutionHelper::Run().Fixes: #63311
Approach
Successful execution paths now explicitly check
sqlite3_reset().Functions with early-return or V8-exception paths keep an
OnScopeLeavereset guard so prepared statements are left reusable. The guard intentionally
drops the reset result to avoid replacing an already-pending SQLite or V8
exception.
StatementSyncIterator::Next()andStatementSyncIterator::Return()use adirect checked reset because their control flow is linear.