Skip to content
Open
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
22 changes: 15 additions & 7 deletions src/node_sqlite.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3545,8 +3545,10 @@ inline int Statement::ResetStatement() {
}

bool Statement::BindParams(const FunctionCallbackInfo<Value>& args) {
int r = sqlite3_clear_bindings(statement_.get());
CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
int param_count = sqlite3_bind_parameter_count(statement_.get());
// Parameter indexing starts at one.
MaybeStackBuffer<bool, 32> bound(param_count + 1);
std::fill_n(bound.out(), param_count + 1, false);

int anon_idx = 1;
int anon_start = 0;
Expand All @@ -3562,8 +3564,6 @@ bool Statement::BindParams(const FunctionCallbackInfo<Value>& args) {

if (allow_bare_named_params_ && !bare_named_params_.has_value()) {
bare_named_params_.emplace();
int param_count = sqlite3_bind_parameter_count(statement_.get());
// Parameter indexing starts at one.
for (int i = 1; i <= param_count; ++i) {
const char* name = sqlite3_bind_parameter_name(statement_.get(), i);
if (name == nullptr) {
Expand Down Expand Up @@ -3626,6 +3626,7 @@ bool Statement::BindParams(const FunctionCallbackInfo<Value>& args) {
if (!BindValue(value, r)) {
return false;
}
bound[r] = true;
}
anon_start++;
}
Expand All @@ -3641,10 +3642,20 @@ bool Statement::BindParams(const FunctionCallbackInfo<Value>& args) {
if (!BindValue(args[i], anon_idx)) {
return false;
}
bound[anon_idx] = true;

anon_idx++;
}

// sqlite3_clear_bindings() would expire statements whose query plan depends
// on a bound value, so only reset the parameters that were not bound.
for (int i = 1; i <= param_count; ++i) {
if (!bound[i]) {
int r = sqlite3_bind_null(statement_.get(), i);
CHECK_ERROR_OR_THROW(env()->isolate(), db_.get(), r, SQLITE_OK, false);
}
}

return true;
}

Expand Down Expand Up @@ -4406,9 +4417,6 @@ bool SQLTagStore::ResetAndBindStatement(
int r = stmt->ResetStatement();
CHECK_ERROR_OR_THROW(isolate, stmt->db_.get(), r, SQLITE_OK, false);

r = sqlite3_clear_bindings(stmt->statement_.get());
CHECK_ERROR_OR_THROW(isolate, stmt->db_.get(), r, SQLITE_OK, false);

uint32_t n_params = args.Length() - 1;
int param_count = sqlite3_bind_parameter_count(stmt->statement_.get());
if (param_count != static_cast<int>(n_params)) {
Expand Down
28 changes: 28 additions & 0 deletions test/parallel/test-sqlite-data-types.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,4 +256,32 @@ suite('data binding and mapping', () => {
[{ __proto__: null, key: 1, val: 5 }, { __proto__: null, key: 2, val: null }],
);
});

test('omitted named parameters are unbound on each call', (t) => {
const db = new Database(':memory:');
t.after(() => { db.close(); });
const setup = db.exec(
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
);
t.assert.strictEqual(setup, undefined);
const named = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
const mixed = db.prepare('INSERT INTO data (key, val) VALUES ($k, ?)');
named.run({ k: 1, v: 5 });
named.run({ k: 2 });
named.run({ k: 3, v: 6 });
named.run({ k: 4, $k: 4 });
mixed.run({ k: 5 }, 7);
mixed.run({ k: 6 });
t.assert.deepStrictEqual(
db.prepare('SELECT * FROM data ORDER BY key').all(),
[
{ __proto__: null, key: 1, val: 5 },
{ __proto__: null, key: 2, val: null },
{ __proto__: null, key: 3, val: 6 },
{ __proto__: null, key: 4, val: null },
{ __proto__: null, key: 5, val: 7 },
{ __proto__: null, key: 6, val: null },
],
);
});
});
Loading