diff --git a/src/node_sqlite.cc b/src/node_sqlite.cc index 3a7c39e72cf421..14bf396e76cb4c 100644 --- a/src/node_sqlite.cc +++ b/src/node_sqlite.cc @@ -1942,7 +1942,9 @@ bool StatementSync::BindParams(const FunctionCallbackInfo& args) { } for (int i = anon_start; i < args.Length(); ++i) { - while (sqlite3_bind_parameter_name(statement_, anon_idx) != nullptr) { + while (1) { + const char* param = sqlite3_bind_parameter_name(statement_, anon_idx); + if (param == nullptr || param[0] == '?') break; anon_idx++; } diff --git a/test/parallel/test-sqlite-statement-sync.js b/test/parallel/test-sqlite-statement-sync.js index 858a1486601763..04494a02c692a8 100644 --- a/test/parallel/test-sqlite-statement-sync.js +++ b/test/parallel/test-sqlite-statement-sync.js @@ -240,6 +240,36 @@ suite('StatementSync.prototype.run()', () => { stmt.run({ k: 3, v: 30 }), { changes: 1, lastInsertRowid: 3 } ); }); + + test('SQLite defaults unbound ?NNN parameters', (t) => { + const db = new DatabaseSync(nextDb()); + t.after(() => { db.close(); }); + const setup = db.exec( + 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;' + ); + t.assert.strictEqual(setup, undefined); + const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?1, ?3)'); + + t.assert.throws(() => { + stmt.run(1); + }, { + code: 'ERR_SQLITE_ERROR', + message: 'NOT NULL constraint failed: data.val', + errcode: 1299, + errstr: 'constraint failed', + }); + }); + + test('binds ?NNN params by position', (t) => { + const db = new DatabaseSync(nextDb()); + t.after(() => { db.close(); }); + const setup = db.exec( + 'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER NOT NULL) STRICT;' + ); + t.assert.strictEqual(setup, undefined); + const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?1, ?2)'); + t.assert.deepStrictEqual(stmt.run(1, 2), { changes: 1, lastInsertRowid: 1 }); + }); }); suite('StatementSync.prototype.sourceSQL', () => {