Skip to content

Commit bd5c906

Browse files
cjihrigRafaelGSS
authored andcommitted
sqlite: add support for unknown named parameters
This commit adds a method for toggling support for unknown named parameters in prepared statements. Fixes: #55533 PR-URL: #57552 Reviewed-By: Edy Silva <[email protected]> Reviewed-By: James M Snell <[email protected]>
1 parent 2ce7978 commit bd5c906

File tree

4 files changed

+84
-3
lines changed

4 files changed

+84
-3
lines changed

doc/api/sqlite.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,17 @@ are several caveats to be aware of when enabling bare named parameters:
488488
statement will result in an exception as it cannot be determined how to bind
489489
a bare name.
490490

491+
### `statement.setAllowUnknownNamedParameters(enabled)`
492+
493+
<!-- YAML
494+
added: REPLACEME
495+
-->
496+
497+
* `enabled` {boolean} Enables or disables support for unknown named parameters.
498+
499+
By default, if an unknown name is encountered while binding parameters, an
500+
exception is thrown. This method allows unknown named parameters to be ignored.
501+
491502
### `statement.setReadBigInts(enabled)`
492503

493504
<!-- YAML

src/node_sqlite.cc

Lines changed: 29 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1012,6 +1012,7 @@ StatementSync::StatementSync(Environment* env,
10121012
// connection level and inherited by statements to reduce boilerplate.
10131013
use_big_ints_ = false;
10141014
allow_bare_named_params_ = true;
1015+
allow_unknown_named_params_ = false;
10151016
bare_named_params_ = std::nullopt;
10161017
}
10171018

@@ -1095,9 +1096,13 @@ bool StatementSync::BindParams(const FunctionCallbackInfo<Value>& args) {
10951096
}
10961097

10971098
if (r == 0) {
1098-
THROW_ERR_INVALID_STATE(
1099-
env(), "Unknown named parameter '%s'", *utf8_key);
1100-
return false;
1099+
if (allow_unknown_named_params_) {
1100+
continue;
1101+
} else {
1102+
THROW_ERR_INVALID_STATE(
1103+
env(), "Unknown named parameter '%s'", *utf8_key);
1104+
return false;
1105+
}
11011106
}
11021107
}
11031108

@@ -1627,6 +1632,23 @@ void StatementSync::SetAllowBareNamedParameters(
16271632
stmt->allow_bare_named_params_ = args[0]->IsTrue();
16281633
}
16291634

1635+
void StatementSync::SetAllowUnknownNamedParameters(
1636+
const FunctionCallbackInfo<Value>& args) {
1637+
StatementSync* stmt;
1638+
ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This());
1639+
Environment* env = Environment::GetCurrent(args);
1640+
THROW_AND_RETURN_ON_BAD_STATE(
1641+
env, stmt->IsFinalized(), "statement has been finalized");
1642+
1643+
if (!args[0]->IsBoolean()) {
1644+
THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
1645+
"The \"enabled\" argument must be a boolean.");
1646+
return;
1647+
}
1648+
1649+
stmt->allow_unknown_named_params_ = args[0]->IsTrue();
1650+
}
1651+
16301652
void StatementSync::SetReadBigInts(const FunctionCallbackInfo<Value>& args) {
16311653
StatementSync* stmt;
16321654
ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This());
@@ -1690,6 +1712,10 @@ Local<FunctionTemplate> StatementSync::GetConstructorTemplate(
16901712
tmpl,
16911713
"setAllowBareNamedParameters",
16921714
StatementSync::SetAllowBareNamedParameters);
1715+
SetProtoMethod(isolate,
1716+
tmpl,
1717+
"setAllowUnknownNamedParameters",
1718+
StatementSync::SetAllowUnknownNamedParameters);
16931719
SetProtoMethod(
16941720
isolate, tmpl, "setReadBigInts", StatementSync::SetReadBigInts);
16951721
env->set_sqlite_statement_sync_constructor_template(tmpl);

src/node_sqlite.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,8 @@ class StatementSync : public BaseObject {
109109
const v8::FunctionCallbackInfo<v8::Value>& args);
110110
static void SetAllowBareNamedParameters(
111111
const v8::FunctionCallbackInfo<v8::Value>& args);
112+
static void SetAllowUnknownNamedParameters(
113+
const v8::FunctionCallbackInfo<v8::Value>& args);
112114
static void SetReadBigInts(const v8::FunctionCallbackInfo<v8::Value>& args);
113115
void Finalize();
114116
bool IsFinalized();
@@ -122,6 +124,7 @@ class StatementSync : public BaseObject {
122124
sqlite3_stmt* statement_;
123125
bool use_big_ints_;
124126
bool allow_bare_named_params_;
127+
bool allow_unknown_named_params_;
125128
std::optional<std::map<std::string, std::string>> bare_named_params_;
126129
bool BindParams(const v8::FunctionCallbackInfo<v8::Value>& args);
127130
bool BindValue(const v8::Local<v8::Value>& value, const int index);

test/parallel/test-sqlite-named-parameters.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,3 +77,44 @@ suite('named parameters', () => {
7777
});
7878
});
7979
});
80+
81+
suite('StatementSync.prototype.setAllowUnknownNamedParameters()', () => {
82+
test('unknown named parameter support can be toggled', (t) => {
83+
const db = new DatabaseSync(':memory:');
84+
t.after(() => { db.close(); });
85+
const setup = db.exec(
86+
'CREATE TABLE data(key INTEGER, val INTEGER) STRICT;'
87+
);
88+
t.assert.strictEqual(setup, undefined);
89+
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
90+
t.assert.strictEqual(stmt.setAllowUnknownNamedParameters(true), undefined);
91+
const params = { $a: 1, $b: 2, $k: 42, $y: 25, $v: 84, $z: 99 };
92+
t.assert.deepStrictEqual(
93+
stmt.run(params),
94+
{ changes: 1, lastInsertRowid: 1 },
95+
);
96+
t.assert.strictEqual(stmt.setAllowUnknownNamedParameters(false), undefined);
97+
t.assert.throws(() => {
98+
stmt.run(params);
99+
}, {
100+
code: 'ERR_INVALID_STATE',
101+
message: /Unknown named parameter '\$a'/,
102+
});
103+
});
104+
105+
test('throws when input is not a boolean', (t) => {
106+
const db = new DatabaseSync(':memory:');
107+
t.after(() => { db.close(); });
108+
const setup = db.exec(
109+
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
110+
);
111+
t.assert.strictEqual(setup, undefined);
112+
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
113+
t.assert.throws(() => {
114+
stmt.setAllowUnknownNamedParameters();
115+
}, {
116+
code: 'ERR_INVALID_ARG_TYPE',
117+
message: /The "enabled" argument must be a boolean/,
118+
});
119+
});
120+
});

0 commit comments

Comments
 (0)