Skip to content

Commit c5d88bb

Browse files
committed
sqlite: add StatementSync.prototype.close()
Signed-off-by: Guilherme Araújo <arauujogui@gmail.com>
1 parent da1856b commit c5d88bb

5 files changed

Lines changed: 169 additions & 1 deletion

File tree

doc/api/sqlite.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,15 @@ objects. If the prepared statement does not return any results, this method
997997
returns an empty array. The prepared statement [parameters are bound][] using
998998
the values in `namedParameters` and `anonymousParameters`.
999999

1000+
### `statement.close()`
1001+
1002+
<!-- YAML
1003+
added: REPLACEME
1004+
-->
1005+
1006+
Finalizes the prepared statement. An exception is thrown if the statement is
1007+
already finalized. This method is a wrapper around [`sqlite3_finalize()`][].
1008+
10001009
### `statement.columns()`
10011010

10021011
<!-- YAML
@@ -1691,6 +1700,7 @@ callback function to indicate what type of operation is being authorized.
16911700
[`sqlite3_deserialize()`]: https://sqlite.org/c3ref/deserialize.html
16921701
[`sqlite3_exec()`]: https://www.sqlite.org/c3ref/exec.html
16931702
[`sqlite3_expanded_sql()`]: https://www.sqlite.org/c3ref/expanded_sql.html
1703+
[`sqlite3_finalize()`]: https://www.sqlite.org/c3ref/finalize.html
16941704
[`sqlite3_get_autocommit()`]: https://sqlite.org/c3ref/get_autocommit.html
16951705
[`sqlite3_last_insert_rowid()`]: https://www.sqlite.org/c3ref/last_insert_rowid.html
16961706
[`sqlite3_load_extension()`]: https://www.sqlite.org/c3ref/load_extension.html

src/node_sqlite.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2602,9 +2602,20 @@ inline bool StatementSync::IsFinalized() {
26022602
void StatementSync::Close(const FunctionCallbackInfo<Value>& args) {
26032603
StatementSync* stmt;
26042604
ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This());
2605+
Environment* env = Environment::GetCurrent(args);
2606+
THROW_AND_RETURN_ON_BAD_STATE(
2607+
env, stmt->IsFinalized(), "statement has been finalized");
26052608
stmt->Close();
26062609
}
26072610

2611+
void StatementSync::Dispose(const FunctionCallbackInfo<Value>& args) {
2612+
v8::TryCatch try_catch(args.GetIsolate());
2613+
Close(args);
2614+
if (try_catch.HasCaught()) {
2615+
CHECK(try_catch.CanContinue());
2616+
}
2617+
}
2618+
26082619
inline int StatementSync::ResetStatement() {
26092620
reset_generation_++;
26102621
return sqlite3_reset(statement_);
@@ -3632,7 +3643,8 @@ Local<FunctionTemplate> StatementSync::GetConstructorTemplate(
36323643
isolate, tmpl, "setReadBigInts", StatementSync::SetReadBigInts);
36333644
SetProtoMethod(
36343645
isolate, tmpl, "setReturnArrays", StatementSync::SetReturnArrays);
3635-
SetProtoDispose(isolate, tmpl, StatementSync::Close);
3646+
SetProtoMethod(isolate, tmpl, "close", StatementSync::Close);
3647+
SetProtoDispose(isolate, tmpl, StatementSync::Dispose);
36363648
env->set_sqlite_statement_sync_constructor_template(tmpl);
36373649
}
36383650
return tmpl;

src/node_sqlite.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,7 @@ class StatementSync : public BaseObject {
279279
static void SetReadBigInts(const v8::FunctionCallbackInfo<v8::Value>& args);
280280
static void SetReturnArrays(const v8::FunctionCallbackInfo<v8::Value>& args);
281281
static void Close(const v8::FunctionCallbackInfo<v8::Value>& args);
282+
static void Dispose(const v8::FunctionCallbackInfo<v8::Value>& args);
282283
v8::MaybeLocal<v8::Value> ColumnToValue(const int column);
283284
v8::MaybeLocal<v8::Name> ColumnNameToName(const int column);
284285
bool GetCachedColumnNames(v8::LocalVector<v8::Name>* keys);

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

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,22 @@ suite('StatementSync.prototype.setAllowUnknownNamedParameters()', () => {
118118
message: /The "enabled" argument must be a boolean/,
119119
});
120120
});
121+
122+
test('throws if the statement is already finalized', (t) => {
123+
using db = new DatabaseSync(':memory:');
124+
const setup = db.exec(
125+
'CREATE TABLE data(key INTEGER PRIMARY KEY, val INTEGER) STRICT;'
126+
);
127+
t.assert.strictEqual(setup, undefined);
128+
const stmt = db.prepare('INSERT INTO data (key, val) VALUES ($k, $v)');
129+
stmt.close();
130+
t.assert.throws(() => {
131+
stmt.setAllowUnknownNamedParameters(true);
132+
}, {
133+
code: 'ERR_INVALID_STATE',
134+
message: /statement has been finalized/,
135+
});
136+
});
121137
});
122138

123139
suite('options.allowUnknownNamedParameters', () => {

test/parallel/test-sqlite-statement-sync.js

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,18 @@ suite('StatementSync.prototype.get()', () => {
5353
const stmt = db.prepare('SELECT 1 as __proto__, 2 as constructor, 3 as toString');
5454
t.assert.deepStrictEqual(stmt.get(), { __proto__: null, ['__proto__']: 1, constructor: 2, toString: 3 });
5555
});
56+
57+
test('throws if the statement is already finalized', (t) => {
58+
using db = new DatabaseSync(':memory:');
59+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
60+
stmt.close();
61+
t.assert.throws(() => {
62+
stmt.get();
63+
}, {
64+
code: 'ERR_INVALID_STATE',
65+
message: /statement has been finalized/,
66+
});
67+
});
5668
});
5769

5870
suite('StatementSync.prototype.all()', () => {
@@ -83,6 +95,18 @@ suite('StatementSync.prototype.all()', () => {
8395
{ __proto__: null, key: 'key2', val: 'val2' },
8496
]);
8597
});
98+
99+
test('throws if the statement is already finalized', (t) => {
100+
using db = new DatabaseSync(':memory:');
101+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
102+
stmt.close();
103+
t.assert.throws(() => {
104+
stmt.all();
105+
}, {
106+
code: 'ERR_INVALID_STATE',
107+
message: /statement has been finalized/,
108+
});
109+
});
86110
});
87111

88112
suite('StatementSync.prototype.iterate()', () => {
@@ -226,6 +250,18 @@ suite('StatementSync.prototype.iterate()', () => {
226250
stmt2.get();
227251
it.next();
228252
});
253+
254+
test('throws if the statement is already finalized', (t) => {
255+
using db = new DatabaseSync(':memory:');
256+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
257+
stmt.close();
258+
t.assert.throws(() => {
259+
stmt.iterate();
260+
}, {
261+
code: 'ERR_INVALID_STATE',
262+
message: /statement has been finalized/,
263+
});
264+
});
229265
});
230266

231267
suite('StatementSync.prototype.run()', () => {
@@ -325,6 +361,18 @@ suite('StatementSync.prototype.run()', () => {
325361
const stmt = db.prepare('INSERT INTO data (key, val) VALUES (?1, ?2)');
326362
t.assert.deepStrictEqual(stmt.run(1, 2), { changes: 1, lastInsertRowid: 1 });
327363
});
364+
365+
test('throws if the statement is already finalized', (t) => {
366+
using db = new DatabaseSync(':memory:');
367+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
368+
stmt.close();
369+
t.assert.throws(() => {
370+
stmt.run();
371+
}, {
372+
code: 'ERR_INVALID_STATE',
373+
message: /statement has been finalized/,
374+
});
375+
});
328376
});
329377

330378
suite('StatementSync.prototype.sourceSQL', () => {
@@ -339,6 +387,16 @@ suite('StatementSync.prototype.sourceSQL', () => {
339387
const stmt = db.prepare(sql);
340388
t.assert.strictEqual(stmt.sourceSQL, sql);
341389
});
390+
391+
test('throws if the statement is already finalized', (t) => {
392+
using db = new DatabaseSync(':memory:');
393+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
394+
stmt.close();
395+
t.assert.throws(() => stmt.sourceSQL, {
396+
code: 'ERR_INVALID_STATE',
397+
message: /statement has been finalized/,
398+
});
399+
});
342400
});
343401

344402
suite('StatementSync.prototype.expandedSQL', () => {
@@ -358,6 +416,16 @@ suite('StatementSync.prototype.expandedSQL', () => {
358416
);
359417
t.assert.strictEqual(stmt.expandedSQL, expanded);
360418
});
419+
420+
test('throws if the statement is already finalized', (t) => {
421+
using db = new DatabaseSync(':memory:');
422+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
423+
stmt.close();
424+
t.assert.throws(() => stmt.expandedSQL, {
425+
code: 'ERR_INVALID_STATE',
426+
message: /statement has been finalized/,
427+
});
428+
});
361429
});
362430

363431
suite('StatementSync.prototype.setReadBigInts()', () => {
@@ -427,6 +495,18 @@ suite('StatementSync.prototype.setReadBigInts()', () => {
427495
[`${Number.MAX_SAFE_INTEGER} + 1`]: 2n ** 53n,
428496
});
429497
});
498+
499+
test('throws if the statement is already finalized', (t) => {
500+
using db = new DatabaseSync(':memory:');
501+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
502+
stmt.close();
503+
t.assert.throws(() => {
504+
stmt.setReadBigInts(true);
505+
}, {
506+
code: 'ERR_INVALID_STATE',
507+
message: /statement has been finalized/,
508+
});
509+
});
430510
});
431511

432512
suite('StatementSync.prototype.setReturnArrays()', () => {
@@ -445,6 +525,18 @@ suite('StatementSync.prototype.setReturnArrays()', () => {
445525
message: /The "returnArrays" argument must be a boolean/,
446526
});
447527
});
528+
529+
test('throws if the statement is already finalized', (t) => {
530+
using db = new DatabaseSync(':memory:');
531+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
532+
stmt.close();
533+
t.assert.throws(() => {
534+
stmt.setReturnArrays(true);
535+
}, {
536+
code: 'ERR_INVALID_STATE',
537+
message: /statement has been finalized/,
538+
});
539+
});
448540
});
449541

450542
suite('StatementSync.prototype.get() with array output', () => {
@@ -663,6 +755,18 @@ suite('StatementSync.prototype.setAllowBareNamedParameters()', () => {
663755
message: /The "allowBareNamedParameters" argument must be a boolean/,
664756
});
665757
});
758+
759+
test('throws if the statement is already finalized', (t) => {
760+
using db = new DatabaseSync(':memory:');
761+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
762+
stmt.close();
763+
t.assert.throws(() => {
764+
stmt.setAllowBareNamedParameters(true);
765+
}, {
766+
code: 'ERR_INVALID_STATE',
767+
message: /statement has been finalized/,
768+
});
769+
});
666770
});
667771

668772
suite('options.readBigInts', () => {
@@ -911,6 +1015,31 @@ suite('options.allowBareNamedParameters', () => {
9111015
});
9121016

9131017

1018+
suite('StatementSync.prototype.close()', () => {
1019+
test('finalizes an open statement', (t) => {
1020+
using db = new DatabaseSync(':memory:');
1021+
db.exec('CREATE TABLE storage(key TEXT, val TEXT)');
1022+
const stmt = db.prepare('SELECT * FROM storage');
1023+
t.assert.strictEqual(stmt.close(), undefined);
1024+
t.assert.throws(() => stmt.get(), {
1025+
code: 'ERR_INVALID_STATE',
1026+
message: /statement has been finalized/,
1027+
});
1028+
});
1029+
1030+
test('throws if the statement is already finalized', (t) => {
1031+
using db = new DatabaseSync(':memory:');
1032+
const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
1033+
stmt.close();
1034+
t.assert.throws(() => {
1035+
stmt.close();
1036+
}, {
1037+
code: 'ERR_INVALID_STATE',
1038+
message: /statement has been finalized/,
1039+
});
1040+
});
1041+
});
1042+
9141043
suite('StatementSync.prototype[Symbol.dispose]()', () => {
9151044
test('finalizes an open statement', (t) => {
9161045
using db = new DatabaseSync(':memory:');

0 commit comments

Comments
 (0)