Skip to content

sqlite: improve error for excess bound parameters - #65164

Open
lazerg wants to merge 1 commit into
nodejs:mainfrom
lazerg:fix/issue-65163-excess-bound-params
Open

sqlite: improve error for excess bound parameters#65164
lazerg wants to merge 1 commit into
nodejs:mainfrom
lazerg:fix/issue-65163-excess-bound-params

Conversation

@lazerg

@lazerg lazerg commented Aug 9, 2026

Copy link
Copy Markdown
Contributor

Fixes: #65163

BindParams() walked the anonymous arguments without checking how many parameters the statement actually has, so an extra argument failed inside sqlite3_bind_* and came back as ERR_SQLITE_ERROR: column index out of range. To a JavaScript caller "column" reads as a table column, which points at the schema rather than at the extra argument.

Binding now stops when it runs out of parameter slots and throws ERR_INVALID_STATE with both counts, the same way an unknown named parameter is already reported from that function.

Callers matching on ERR_SQLITE_ERROR or errcode 25 for this case will see the new error instead.

@nodejs-github-bot

Copy link
Copy Markdown
Collaborator

Review requested:

  • @nodejs/sqlite

@nodejs-github-bot nodejs-github-bot added c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. sqlite Issues and PRs related to the SQLite subsystem. labels Aug 9, 2026
@codecov

codecov Bot commented Aug 9, 2026

Copy link
Copy Markdown

Codecov Report

βœ… All modified and coverable lines are covered by tests.
βœ… Project coverage is 90.31%. Comparing base (ee5a070) to head (f4ad7ab).

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #65164      +/-   ##
==========================================
+ Coverage   90.30%   90.31%   +0.01%     
==========================================
  Files         759      759              
  Lines      248328   248336       +8     
  Branches    46853    46858       +5     
==========================================
+ Hits       224243   224283      +40     
+ Misses      15514    15465      -49     
- Partials     8571     8588      +17     
Files with missing lines Coverage Ξ”
src/node_sqlite.cc 81.25% <100.00%> (+0.01%) ⬆️

... and 28 files with indirect coverage changes

πŸš€ New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • πŸ“¦ JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Signed-off-by: Lazizbek Ergashev <lazerg2@gmail.com>
@lazerg
lazerg force-pushed the fix/issue-65163-excess-bound-params branch from 2fc910b to f4ad7ab Compare August 9, 2026 17:07

@pacocartones pacocartones left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Independent verification β€” logic checks out; one doc/test gap to consider

I verified the fix independently, both against the C++ and against live SQLite semantics (Node 24, node:sqlite). The core change is correct and the error message counts are accurate.

Root cause confirmed: the old loop kept binding anonymous values past the statement's parameter count; sqlite3_bind_* then failed with SQLITE_RANGE (errcode 25), which surfaced as the opaque ERR_SQLITE_ERROR: column index out of range. The explicit capacity check before binding fixes exactly that.

Loop verification (PR head):

  • anon_idx++ after BindValue is retained, so each arg binds to a distinct slot.
  • The while (anon_idx <= param_count) skip over named parameters is correct for every interleaving I checked (named-only, anon-only, $a, ?, ?, $a, $a, ?, $b, ?).
  • The counts are right: at failure, i - anon_start equals the number of anonymous values bound, which β€” since the throw only fires once every slot up to param_count is taken β€” is the statement's actual anonymous capacity; args.Length() - anon_start is the number of anonymous values received (the named-params object excluded). I hand-checked all four new tests against the code and the expected messages match.

?NNN semantics (relevant to the new doc sentence): SQLite numbers ?NNN by explicit index and silently allows binding to intermediate "phantom" indices, so SELECT ?2 genuinely accepts up to two values (get('x', 'y') β†’ { a: 'y' }) and SELECT ?2, ? numbers the bare ? at index 3 (bind count 3). The new loop handles these correctly β€” the doc claim "The ?NNN form raises the number accepted to NNN" holds.

One gap (non-blocking): that doc sentence is the only place the ?NNN form is covered, and there is no test for it. The four added tests exercise ?1,?2, no params, mixed named+anonymous, and named-only. Suggest adding a ?NNN case (e.g. db.prepare('SELECT ?2 AS a').run('x', 'y') succeeds; .run('x', 'y', 'z') throws the new ERR_INVALID_STATE with accepts 2, received 3) β€” it locks in the documented behavior, including the surprising phantom-index binding, and guards the new loop against regressions.

LGTM otherwise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Issues and PRs that require attention from people who are familiar with C++. needs-ci PRs that need a full CI run. sqlite Issues and PRs related to the SQLite subsystem.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

sqlite: excess bound parameters produce an opaque "column index out of range" error

3 participants