Title: sqlite: statements with JOINs are re-prepared on every run since SQLite 3.54 (sqlite3_clear_bindings) · Issue #66800 · nodejs/node · GitHub
Open Graph Title: sqlite: statements with JOINs are re-prepared on every run since SQLite 3.54 (sqlite3_clear_bindings) · Issue #66800 · nodejs/node
X Title: sqlite: statements with JOINs are re-prepared on every run since SQLite 3.54 (sqlite3_clear_bindings) · Issue #66800 · nodejs/node
Description: Version v26.12.0 Platform Linux 6.17 aarch64 (also expected on other platforms; the cause is not platform specific) Subsystem sqlite What steps will reproduce the bug? import { Database } from 'node:sqlite'; const db = new Database(':mem...
Open Graph Description: Version v26.12.0 Platform Linux 6.17 aarch64 (also expected on other platforms; the cause is not platform specific) Subsystem sqlite What steps will reproduce the bug? import { Database } from 'nod...
X Description: Version v26.12.0 Platform Linux 6.17 aarch64 (also expected on other platforms; the cause is not platform specific) Subsystem sqlite What steps will reproduce the bug? import { Database } from '...
Opengraph URL: https://github.com/nodejs/node/issues/66800
X: @github
Domain: github.com
{"@context":"https://schema.org","@type":"DiscussionForumPosting","headline":"sqlite: statements with JOINs are re-prepared on every run since SQLite 3.54 (sqlite3_clear_bindings)","articleBody":"### Version\n\nv26.12.0\n\n### Platform\n\nLinux 6.17 aarch64 (also expected on other platforms; the cause is not platform specific)\n\n### Subsystem\n\nsqlite\n\n### What steps will reproduce the bug?\n\n```js\nimport { Database } from 'node:sqlite';\nconst db = new Database(':memory:');\ndb.exec('create table t (id integer primary key, h text); create table p (id integer primary key, a integer)');\nconst insT = db.prepare('insert into t values (?, ?)');\nconst insP = db.prepare('insert into p values (?, ?)');\nfor (let i = 1; i \u003c= 1000; i++) { insT.run(i, 'x'); insP.run(i, i); }\nfor (const sql of ['select h from t where id = ?', 'select t.h from p join t on t.id = p.a where p.id = ?']) {\n const st = db.prepare(sql);\n const t0 = performance.now();\n for (let i = 0; i \u003c 100_000; i++) st.get(1 + (i % 1000));\n console.log(sql, ((performance.now() - t0) * 1000 / 100_000).toFixed(2), 'us/op, reprepare =', st.stat('reprepare'));\n}\n```\n\n### How often does it reproduce? Is there a required condition?\n\nAlways, for statements where SQLite 3.54's planner marks a bound parameter for\nre-optimization (e.g. a JOIN filtered by `rowid = ?`).\n\n### What is the expected behavior? Why is that the expected behavior?\n\nThe statement is prepared once and reused, as on v24.21.0 (SQLite 3.53.4).\n\n### What do you see instead?\n\n```\nv24.21.0 (SQLite 3.53.4) join: 2.14 us/op (same script with DatabaseSync; no stat() on v24)\nv26.12.0 (SQLite 3.54.0) join: 8.44 us/op, reprepare = 100000\n single table: 1.53 us/op, reprepare = 0 (unchanged)\n```\n\n### Additional information\n\n`Statement::BindParams()` and `SQLTagStore::ResetAndBindStatement()` call `sqlite3_clear_bindings()`\nbefore every execution. In SQLite 3.54, `sqlite3_clear_bindings()` expires any\nstatement whose `expmask` is non-zero, without honouring the new `smimask`\n\"only re-prepare on change to/from 0 or 1\" rule that `sqlite3_bind_*()` honours.\n3.54 sets `expmask` for ordinary JOIN statements, so they are re-prepared on every\n`sqlite3_step()`. A plain C repro (no Node involved) shows the same thing:\n3.54.1 with `clear_bindings` re-prepares 200000/200000 runs; without it, 8.\n\nThis is also being reported on the SQLite forum; I'll link the thread in a comment.\n\nIndependently of an upstream fix, node:sqlite could avoid the unconditional\n`sqlite3_clear_bindings()` call, e.g. only clear when fewer parameters are bound\nthan `sqlite3_bind_parameter_count()`, or bind NULL explicitly to the remaining\nones. This also matters for LTS lines once SQLite 3.54 is backported (deps update\nwas #66663).\n","author":{"url":"https://github.com/Glasden","@type":"Person","name":"Glasden"},"datePublished":"2026-10-16T19:01:02Z","url":"https://github.com/nodejs/node/issues/66800"}
| react-profiling | 0 |
| react-import-map | react,react-dom,react-dom/client,react-dom/profiling,react-is,react-reconciler,react/compiler-runtime,react/jsx-dev-runtime,react/jsx-runtime,scheduler@d5750093facf |
| route-pattern | /_view_fragments/issues/show/:user_id/:repository/:id/issue_layout(.:format) |
| route-controller | voltron_issues_fragments |
| route-action | issue_layout |
| fetch-nonce | v2:0823d1ee-66f0-1585-8bf5-2404c6d134c1 |
| current-catalog-service-hash | 81bb79d38c15960b92d99bca9288a9108c7a47b18f2423d0f6438c5b7bcd2114 |
| request-id | A1D6:61676:A337E:DF088:6AED3357 |
| html-safe-nonce | d0cf090bac0e3646d504d15febabe93e4bd60c2099e8335395192478d3db99e0 |
| visitor-payload | eyJyZWZlcnJlciI6IiIsInJlcXVlc3RfaWQiOiJBMUQ2OjYxNjc2OkEzMzdFOkRGMDg4OjZBRUQzMzU3IiwidmlzaXRvcl9pZCI6IjQ4NzMyNzExODgwNjc3OTE3MDMiLCJyZWdpb25fZWRnZSI6ImlhZCIsInJlZ2lvbl9yZW5kZXIiOiJpYWQifQ== |
| visitor-hmac | e43f3d3ba9dc0e63f0203e14c35c04c3fa4b2e87fa37d470c2315c2660caec0d |
| hovercard-subject-tag | issue:5889654550 |
| github-keyboard-shortcuts | repository,issues,copilot |
| google-site-verification | Apib7-x98H0j5cPqHWwSMm6dNU4GmODRoqxLiDzdx9I |
| octolytics-url | https://collector.github.com/github/collect |
| analytics-location | / |
| fb:app_id | 1401488693436528 |
| apple-itunes-app | app-id=1477376905, app-argument=https://github.com/_view_fragments/issues/show/nodejs/node/66800/issue_layout |
| twitter:image | https://opengraph.githubassets.com/e5464a773212048c3f591a80f15ceabd2accfe06acdc23a81c3477406548c92b/nodejs/node/issues/66800 |
| twitter:card | summary_large_image |
| og:image | https://opengraph.githubassets.com/e5464a773212048c3f591a80f15ceabd2accfe06acdc23a81c3477406548c92b/nodejs/node/issues/66800 |
| og:image:alt | Version v26.12.0 Platform Linux 6.17 aarch64 (also expected on other platforms; the cause is not platform specific) Subsystem sqlite What steps will reproduce the bug? import { Database } from 'nod... |
| og:image:width | 1200 |
| og:image:height | 600 |
| og:site_name | GitHub |
| og:type | object |
| og:author:username | Glasden |
| hostname | github.com |
| expected-hostname | github.com |
| None | e51d0b7b5ab34b1688c05b54c3ed17b8658b2fa9bca973a9c740683ccb12cf4a |
| turbo-cache-control | no-preview |
| go-import | github.com/nodejs/node git https://github.com/nodejs/node.git |
| octolytics-dimension-user_id | 9950313 |
| octolytics-dimension-user_login | nodejs |
| octolytics-dimension-repository_id | 27193779 |
| octolytics-dimension-repository_nwo | nodejs/node |
| octolytics-dimension-repository_public | true |
| octolytics-dimension-repository_is_fork | false |
| octolytics-dimension-repository_network_root_id | 27193779 |
| octolytics-dimension-repository_network_root_nwo | nodejs/node |
| turbo-body-classes | logged-out env-production page-responsive |
| disable-turbo | false |
| browser-stats-url | https://api.github.com/_private/browser/stats |
| browser-errors-url | https://api.github.com/_private/browser/errors |
| release | 59ebb7b901cc154ecc179a44f266fcf69f343b30 |
| ui-target | full |
| theme-color | #1e2327 |
| color-scheme | light dark |
| react-app-name | issues |
Links:
Viewport: width=device-width