Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/node_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2702,13 +2702,28 @@ bool StatementSync::BindParams(const FunctionCallbackInfo<Value>& args) {
anon_start++;
}

int param_count = sqlite3_bind_parameter_count(statement_);

for (int i = anon_start; i < args.Length(); ++i) {
while (1) {
const char* param = sqlite3_bind_parameter_name(statement_, anon_idx);
if (param == nullptr || param[0] == '?') break;
anon_idx++;
}

// More values were supplied than the statement has parameters to bind
// them to. Report that directly instead of letting sqlite3_bind_*()
// surface its "column index out of range" error, which reads as a
// problem with the table rather than with the call.
if (anon_idx > param_count) {
THROW_ERR_INVALID_ARG_VALUE(
env(),
"Too many parameter values were provided. The statement accepts %d "
"parameter(s), which are already bound.",
param_count);
return false;
}

if (!BindValue(args[i], anon_idx)) {
return false;
}
Expand Down
35 changes: 35 additions & 0 deletions test/parallel/test-sqlite-data-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,41 @@ suite('data binding and mapping', () => {
});
});

test('throws when more values are provided than the statement accepts', (t) => {
const db = new DatabaseSync(nextDb());
t.after(() => { db.close(); });
db.exec('CREATE TABLE data(key INTEGER PRIMARY KEY) STRICT;');

t.assert.throws(() => {
db.prepare('INSERT INTO data (key) VALUES (?)').run(1, 2);
}, {
code: 'ERR_INVALID_ARG_VALUE',
message: /Too many parameter values were provided.+accepts 1 parameter/,
});

// A statement with no parameters at all.
t.assert.throws(() => {
db.prepare('SELECT 1').get(5);
}, {
code: 'ERR_INVALID_ARG_VALUE',
message: /Too many parameter values were provided.+accepts 0 parameter/,
});

// Excess anonymous values alongside named parameters.
t.assert.throws(() => {
db.prepare('INSERT INTO data (key) VALUES ($k)').run({ $k: 1 }, 2);
}, {
code: 'ERR_INVALID_ARG_VALUE',
message: /Too many parameter values were provided.+accepts 1 parameter/,
});

// The correct number of values still binds.
t.assert.deepStrictEqual(
db.prepare('INSERT INTO data (key) VALUES (?)').run(10),
{ changes: 1, lastInsertRowid: 10 },
);
});

test('throws when binding a BigInt that is too large', (t) => {
const max = 9223372036854775807n; // Largest 64-bit signed integer value.
const db = new DatabaseSync(nextDb());
Expand Down
8 changes: 3 additions & 5 deletions test/parallel/test-sqlite-statement-sync.js
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ suite('StatementSync.prototype.run()', () => {
t.assert.deepStrictEqual(stmt.run(), { changes: 1, lastInsertRowid: 1 });
});

test('SQLite throws when trying to bind too many parameters', (t) => {
test('throws when trying to bind too many parameters', (t) => {
const db = new DatabaseSync(nextDb());
t.after(() => { db.close(); });
const setup = db.exec(
Expand All @@ -321,10 +321,8 @@ suite('StatementSync.prototype.run()', () => {
t.assert.throws(() => {
stmt.run(1, 2, 3);
}, {
code: 'ERR_SQLITE_ERROR',
message: 'column index out of range',
errcode: 25,
errstr: 'column index out of range',
code: 'ERR_INVALID_ARG_VALUE',
message: /Too many parameter values were provided.+accepts 2 parameter/,
});
});

Expand Down
Loading