8000
We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
There was an error while loading. Please reload this page.
StatementSync.prototype.iterate
1 parent 0b3ac05 commit b4041e5Copy full SHA for b4041e5
doc/api/sqlite.md
@@ -307,6 +307,25 @@ object. If the prepared statement does not return any results, this method
307
returns `undefined`. The prepared statement [parameters are bound][] using the
308
values in `namedParameters` and `anonymousParameters`.
309
310
+### `statement.iterate([namedParameters][, ...anonymousParameters])`
311
+
312
+<!-- YAML
313
+added: REPLACEME
314
+-->
315
316
+* `namedParameters` {Object} An optional object used to bind named parameters.
317
+ The keys of this object are used to configure the mapping.
318
+* `...anonymousParameters` {null|number|bigint|string|Buffer|Uint8Array} Zero or
319
+ more values to bind to anonymous parameters.
320
+* Returns: {Iterator} An iterable iterator of objects. Each object corresponds to a row
321
+ returned by executing the prepared statement. The keys and values of each
322
+ object correspond to the column names and values of the row.
323
324
+This method executes a prepared statement and returns an iterator of
325
+objects. If the prepared statement does not return any results, this method
326
+returns an empty iterator. The prepared statement [parameters are bound][] using
327
+the values in `namedParameters` and `anonymousParameters`.
328
329
### `statement.run([namedParameters][, ...anonymousParameters])`
330
331
<!-- YAML
src/env_properties.h
@@ -194,8 +194,10 @@
194
V(ipv4_string, "IPv4") \
195
V(ipv6_string, "IPv6") \
196
V(isclosing_string, "isClosing") \
197
+ V(isfinished_string, "isFinished") \
198
V(issuer_string, "issuer") \
199
V(issuercert_string, "issuerCertificate") \
200
+ V(iterator_string, "Iterator") \
201
V(jwk_crv_string, "crv") \
202
V(jwk_d_string, "d") \
203
V(jwk_dp_string, "dp") \
@@ -241,6 +243,7 @@
241
243
V(nistcurve_string, "nistCurve") \
242
244
V(node_string, "node") \
245
V(nsname_string, "nsname") \
246
+ V(num_cols_string, "num_cols") \
247
V(object_string, "Object") \
248
V(ocsp_request_string, "OCSPRequest") \
249
V(oncertcb_string, "oncertcb") \
@@ -288,6 +291,7 @@
288
291
V(priority_string, "priority") \
289
292
V(process_string, "process") \
290
293
V(promise_string, "promise") \
294
+ V(prototype_string, "prototype") \
295
V(psk_string, "psk") \
296
V(pubkey_string, "pubkey") \
297
V(public_exponent_string, "publicExponent") \
@@ -309,6 +313,7 @@
V(require_string, "require") \
V(resource_string, "resource") \
V(retry_string, "retry") \
+ V(return_string, "return") \
V(salt_length_string, "saltLength") \
V(scheme_string, "scheme") \
V(scopeid_string, "scopeid") \
@@ -332,6 +337,7 @@
332
337
V(standard_name_string, "standardName") \
333
338
V(start_time_string, "startTime") \
334
339
V(state_string, "state") \
340
+ V(statement_string, "statement") \
335
341
V(stats_string, "stats") \
336
342
V(status_string, "status") \
343
V(stdio_string, "stdio") \
src/node_sqlite.cc
@@ -22,6 +22,7 @@ using v8::ConstructorBehavior;
22
using v8::Context;
23
using v8::DontDelete;
24
using v8::Exception;
25
+using v8::External;
26
using v8::Function;
27
using v8::FunctionCallback;
28
using v8::FunctionCallbackInfo;
@@ -790,6 +791,180 @@ void StatementSync::All(const FunctionCallbackInfo<Value>& args) {
790
791
args.GetReturnValue().Set(Array::New(isolate, rows.data(), rows.size()));
792
}
793
794
+void StatementSync::IterateReturnCallback(
795
+ const FunctionCallbackInfo<Value>& args) {
796
+ Environment* env = Environment::GetCurrent(args);
797
+ auto isolate = env->isolate();
798
+ auto context = isolate->GetCurrentContext();
799
800
+ auto self = args.This();
801
+ // iterator has fetch all result or break, prevent next func to return result
802
+ self->Set(context, env->isfinished_string(), Boolean::New(isolate, true))
803
+ .ToChecked();
804
805
+ auto external_stmt = Local<External>::Cast(
806
+ self->Get(context, env->statement_string()).ToLocalChecked());
807
+ auto stmt = static_cast<StatementSync*>(external_stmt->Value());
808
+ if (!stmt->IsFinalized()) {
809
+ sqlite3_reset(stmt->statement_);
810
+ }
811
812
+ LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()});
813
+ LocalVector<Value> values(isolate,
814
+ {Boolean::New(isolate, true), Null(isolate)});
815
816
+ DCHECK_EQ(keys.size(), values.size());
817
+ Local<Object> result = Object::New(
818
+ isolate, Null(isolate), keys.data(), values.data(), keys.size());
819
+ args.GetReturnValue().Set(result);
820
+}
821
822
+void StatementSync::IterateNextCallback(
823
824
825
826
827
828
829
830
+ // skip iteration if is_finished
831
+ auto is_finished = Local<Boolean>::Cast(
832
+ self->Get(context, env->isfinished_string()).ToLocalChecked());
833
+ if (is_finished->Value()) {
834
835
836
837
838
839
840
841
842
+ return;
843
844
845
846
847
848
+ auto num_cols =
849
+ Local<Integer>::Cast(
850
+ self->Get(context, env->num_cols_string()).ToLocalChecked())
851
+ ->Value();
852
853
+ THROW_AND_RETURN_ON_BAD_STATE(
854
+ env, stmt->IsFinalized(), "statement has been finalized");
855
856
+ int r = sqlite3_step(stmt->statement_);
857
+ if (r != SQLITE_ROW) {
858
+ CHECK_ERROR_OR_THROW(
859
+ env->isolate(), stmt->db_->Connection(), r, SQLITE_DONE, void());
860
861
+ // cleanup when no more rows to fetch
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
+ LocalVector<Name> row_keys(isolate);
878
+ row_keys.reserve(num_cols);
879
+ LocalVector<Value> row_values(isolate);
880
+ row_values.reserve(num_cols);
881
+ for (int i = 0; i < num_cols; ++i) {
882
+ Local<Name> key;
883
+ if (!stmt->ColumnNameToName(i).ToLocal(&key)) return;
884
+ Local<Value> val;
885
+ if (!stmt->ColumnToValue(i).ToLocal(&val)) return;
886
+ row_keys.emplace_back(key);
887
+ row_values.emplace_back(val);
888
889
890
+ Local<Object> row = Object::New(
891
+ isolate, Null(isolate), row_keys.data(), row_values.data(), num_cols);
892
893
894
+ LocalVector<Value> values(isolate, {Boolean::New(isolate, false), row});
895
896
897
898
899
900
901
902
+void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) {
903
+ StatementSync* stmt;
904
+ ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This());
905
906
907
908
909
+ auto context = env->context();
910
+ int r = sqlite3_reset(stmt->statement_);
911
912
+ env->isolate(), stmt->db_->Connection(), r, SQLITE_OK, void());
913
914
+ if (!stmt->BindParams(args)) {
915
916
917
918
+ Local<Function> next_func =
919
+ Function::New(context, StatementSync::IterateNextCallback)
920
+ .ToLocalChecked();
921
+ Local<Function> return_func =
922
+ Function::New(context, StatementSync::IterateReturnCallback)
923
924
925
+ LocalVector<Name> keys(isolate, {env->next_string(), env->return_string()});
926
+ LocalVector<Value> values(isolate, {next_func, return_func});
927
928
+ Local<Object> global = context->Global();
929
+ Local<Value> js_iterator;
930
+ Local<Value> js_iterator_prototype;
931
+ if (!global->Get(context, env->iterator_string()).ToLocal(&js_iterator))
932
933
+ if (!js_iterator.As<Object>()
934
+ ->Get(context, env->prototype_string())
935
+ .ToLocal(&js_iterator_prototype))
936
937
938
939
+ Local<Object> iterable_iterator = Object::New(
940
+ isolate, js_iterator_prototype, keys.data(), values.data(), keys.size());
941
942
+ auto num_cols_pd = v8::PropertyDescriptor(
943
+ v8::Integer::New(isolate, sqlite3_column_count(stmt->statement_)), false);
944
+ num_cols_pd.set_enumerable(false);
945
+ num_cols_pd.set_configurable(false);
946
+ iterable_iterator
947
+ ->DefineProperty(context, env->num_cols_string(), num_cols_pd)
948
949
950
+ auto stmt_pd =
951
+ v8::PropertyDescriptor(v8::External::New(isolate, stmt), false);
952
+ stmt_pd.set_enumerable(false);
953
+ stmt_pd.set_configurable(false);
954
+ iterable_iterator->DefineProperty(context, env->statement_string(), stmt_pd)
955
956
957
+ auto is_finished_pd =
958
+ v8::PropertyDescriptor(v8::Boolean::New(isolate, false), true);
959
960
961
962
+ ->DefineProperty(context, env->isfinished_string(), is_finished_pd)
963
964
965
+ args.GetReturnValue().Set(iterable_iterator);
966
967
968
void StatementSync::Get(const FunctionCallbackInfo<Value>& args) {
969
StatementSync* stmt;
970
ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This());
@@ -987,6 +1162,7 @@ Local<FunctionTemplate> StatementSync::GetConstructorTemplate(
987
1162
tmpl->SetClassName(FIXED_ONE_BYTE_STRING(isolate, "StatementSync"));
988
1163
tmpl->InstanceTemplate()->SetInternalFieldCount(
989
1164
StatementSync::kInternalFieldCount);
1165
+ SetProtoMethod(isolate, tmpl, "iterate", StatementSync::Iterate);
990
1166
SetProtoMethod(isolate, tmpl, "all", StatementSync::All);
991
1167
SetProtoMethod(isolate, tmpl, "get", StatementSync::Get);
992
1168
SetProtoMethod(isolate, tmpl, "run", StatementSync::Run);
src/node_sqlite.h
@@ -93,6 +93,7 @@ class StatementSync : public BaseObject {
93
DatabaseSync* db,
94
sqlite3_stmt* stmt);
95
static void All(const v8::FunctionCallbackInfo<v8::Value>& args);
96
+ static void Iterate(const v8::FunctionCallbackInfo<v8::Value>& args);
97
static void Get(const v8::FunctionCallbackInfo<v8::Value>& args);
98
static void Run(const v8::FunctionCallbackInfo<v8::Value>& args);
99
static void SourceSQLGetter(const v8::FunctionCallbackInfo<v8::Value>& args);
@@ -118,6 +119,11 @@ class StatementSync : public BaseObject {
118
119
bool BindValue(const v8::Local<v8::Value>& value, const int index);
120
v8::MaybeLocal<v8::Value> ColumnToValue(const int column);
121
v8::MaybeLocal<v8::Name> ColumnNameToName(const int column);
122
123
+ static void IterateNextCallback(
124
+ const v8::FunctionCallbackInfo<v8::Value>& args);
125
+ static void IterateReturnCallback(
126
127
};
128
129
using Sqlite3ChangesetGenFunc = int (*)(sqlite3_session*, int*, void**);
test/parallel/test-sqlite-statement-sync.js
@@ -84,6 +84,42 @@ suite('StatementSync.prototype.all()', () => {
84
});
85
86
87
+suite('StatementSync.prototype.iterate()', () => {
88
+ test('executes a query and returns an empty iterator on no results', (t) => {
89
+ const db = new DatabaseSync(nextDb());
90
+ const stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
91
+ t.assert.deepStrictEqual(stmt.iterate().toArray(), []);
92
+ });
+ test('executes a query and returns all results', (t) => {
+ let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)');
+ t.assert.deepStrictEqual(stmt.run(), { changes: 0, lastInsertRowid: 0 });
+ stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)');
+ t.assert.deepStrictEqual(
100
+ stmt.run('key1', 'val1'),
101
+ { changes: 1, lastInsertRowid: 1 },
102
+ );
103
104
+ stmt.run('key2', 'val2'),
105
+ { changes: 1, lastInsertRowid: 2 },
106
107
108
+ const items = [
109
+ { __proto__: null, key: 'key1', val: 'val1' },
110
+ { __proto__: null, key: 'key2', val: 'val2' },
111
+ ];
112
113
+ stmt = db.prepare('SELECT * FROM storage ORDER BY key');
114
+ t.assert.deepStrictEqual(stmt.iterate().toArray(), items);
115
116
+ const itemsLoop = items.slice();
117
+ for (const item of stmt.iterate()) {
+ t.assert.deepStrictEqual(item, itemsLoop.shift());
+});
suite('StatementSync.prototype.run()', () => {
test('executes a query and returns change metadata', (t) => {
const db = new DatabaseSync(nextDb());