| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -307,6 +307,25 @@ object. If the prepared statement does not return any results, this method | |||
| 307 | 307 | returns `undefined`. The prepared statement [parameters are bound][] using the | |
| 308 | 308 | values in `namedParameters` and `anonymousParameters`. | |
| 309 | 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 | + | ||
| 310 | 329 | ### `statement.run([namedParameters][, ...anonymousParameters])` | |
| 311 | 330 | ||
| 312 | 331 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -194,8 +194,10 @@ | |||
| 194 | 194 | V(ipv4_string, "IPv4") \ | |
| 195 | 195 | V(ipv6_string, "IPv6") \ | |
| 196 | 196 | V(isclosing_string, "isClosing") \ | |
| 197 | + V(isfinished_string, "isFinished") \ | ||
| 197 | 198 | V(issuer_string, "issuer") \ | |
| 198 | 199 | V(issuercert_string, "issuerCertificate") \ | |
| 200 | + V(iterator_string, "Iterator") \ | ||
| 199 | 201 | V(jwk_crv_string, "crv") \ | |
| 200 | 202 | V(jwk_d_string, "d") \ | |
| 201 | 203 | V(jwk_dp_string, "dp") \ | |
@@ -241,6 +243,7 @@ | |||
| 241 | 243 | V(nistcurve_string, "nistCurve") \ | |
| 242 | 244 | V(node_string, "node") \ | |
| 243 | 245 | V(nsname_string, "nsname") \ | |
| 246 | + V(num_cols_string, "num_cols") \ | ||
| 244 | 247 | V(object_string, "Object") \ | |
| 245 | 248 | V(ocsp_request_string, "OCSPRequest") \ | |
| 246 | 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") \ | ||
| 291 | 295 | V(psk_string, "psk") \ | |
| 292 | 296 | V(pubkey_string, "pubkey") \ | |
| 293 | 297 | V(public_exponent_string, "publicExponent") \ | |
@@ -309,6 +313,7 @@ | |||
| 309 | 313 | V(require_string, "require") \ | |
| 310 | 314 | V(resource_string, "resource") \ | |
| 311 | 315 | V(retry_string, "retry") \ | |
| 316 | + V(return_string, "return") \ | ||
| 312 | 317 | V(salt_length_string, "saltLength") \ | |
| 313 | 318 | V(scheme_string, "scheme") \ | |
| 314 | 319 | 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") \ | |
| 337 | 343 | V(stdio_string, "stdio") \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,6 +22,7 @@ using v8::ConstructorBehavior; | |||
| 22 | 22 | using v8::Context; | |
| 23 | 23 | using v8::DontDelete; | |
| 24 | 24 | using v8::Exception; | |
| 25 | + using v8::External; | ||
| 25 | 26 | using v8::Function; | |
| 26 | 27 | using v8::FunctionCallback; | |
| 27 | 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())); | |
| 791 | 792 | } | |
| 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 | + const FunctionCallbackInfo<Value>& args) { | ||
| 824 | + Environment* env = Environment::GetCurrent(args); | ||
| 825 | + auto isolate = env->isolate(); | ||
| 826 | + auto context = isolate->GetCurrentContext(); | ||
| 827 | + | ||
| 828 | + auto self = args.This(); | ||
| 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 | + LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()}); | ||
| 835 | + LocalVector<Value> values(isolate, | ||
| 836 | + {Boolean::New(isolate, true), Null(isolate)}); | ||
| 837 | + | ||
| 838 | + DCHECK_EQ(keys.size(), values.size()); | ||
| 839 | + Local<Object> result = Object::New( | ||
| 840 | + isolate, Null(isolate), keys.data(), values.data(), keys.size()); | ||
| 841 | + args.GetReturnValue().Set(result); | ||
| 842 | + return; | ||
| 843 | + } | ||
| 844 | + | ||
| 845 | + auto external_stmt = Local<External>::Cast( | ||
| 846 | + self->Get(context, env->statement_string()).ToLocalChecked()); | ||
| 847 | + auto stmt = static_cast<StatementSync*>(external_stmt->Value()); | ||
| 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 | + sqlite3_reset(stmt->statement_); | ||
| 863 | + self->Set(context, env->isfinished_string(), Boolean::New(isolate, true)) | ||
| 864 | + .ToChecked(); | ||
| 865 | + | ||
| 866 | + LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()}); | ||
| 867 | + LocalVector<Value> values(isolate, | ||
| 868 | + {Boolean::New(isolate, true), Null(isolate)}); | ||
| 869 | + | ||
| 870 | + DCHECK_EQ(keys.size(), values.size()); | ||
| 871 | + Local<Object> result = Object::New( | ||
| 872 | + isolate, Null(isolate), keys.data(), values.data(), keys.size()); | ||
| 873 | + args.GetReturnValue().Set(result); | ||
| 874 | + return; | ||
| 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 | + LocalVector<Name> keys(isolate, {env->done_string(), env->value_string()}); | ||
| 894 | + LocalVector<Value> values(isolate, {Boolean::New(isolate, false), row}); | ||
| 895 | + | ||
| 896 | + DCHECK_EQ(keys.size(), values.size()); | ||
| 897 | + Local<Object> result = Object::New( | ||
| 898 | + isolate, Null(isolate), keys.data(), values.data(), keys.size()); | ||
| 899 | + args.GetReturnValue().Set(result); | ||
| 900 | + } | ||
| 901 | + | ||
| 902 | + void StatementSync::Iterate(const FunctionCallbackInfo<Value>& args) { | ||
| 903 | + StatementSync* stmt; | ||
| 904 | + ASSIGN_OR_RETURN_UNWRAP(&stmt, args.This()); | ||
| 905 | + Environment* env = Environment::GetCurrent(args); | ||
| 906 | + THROW_AND_RETURN_ON_BAD_STATE( | ||
| 907 | + env, stmt->IsFinalized(), "statement has been finalized"); | ||
| 908 | + auto isolate = env->isolate(); | ||
| 909 | + auto context = env->context(); | ||
| 910 | + int r = sqlite3_reset(stmt->statement_); | ||
| 911 | + CHECK_ERROR_OR_THROW( | ||
| 912 | + env->isolate(), stmt->db_->Connection(), r, SQLITE_OK, void()); | ||
| 913 | + | ||
| 914 | + if (!stmt->BindParams(args)) { | ||
| 915 | + return; | ||
| 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 | + .ToLocalChecked(); | ||
| 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 | + return; | ||
| 933 | + if (!js_iterator.As<Object>() | ||
| 934 | + ->Get(context, env->prototype_string()) | ||
| 935 | + .ToLocal(&js_iterator_prototype)) | ||
| 936 | + return; | ||
| 937 | + | ||
| 938 | + DCHECK_EQ(keys.size(), values.size()); | ||
| 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 | + .ToChecked(); | ||
| 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 | + .ToChecked(); | ||
| 956 | + | ||
| 957 | + auto is_finished_pd = | ||
| 958 | + v8::PropertyDescriptor(v8::Boolean::New(isolate, false), true); | ||
| 959 | + stmt_pd.set_enumerable(false); | ||
| 960 | + stmt_pd.set_configurable(false); | ||
| 961 | + iterable_iterator | ||
| 962 | + ->DefineProperty(context, env->isfinished_string(), is_finished_pd) | ||
| 963 | + .ToChecked(); | ||
| 964 | + | ||
| 965 | + args.GetReturnValue().Set(iterable_iterator); | ||
| 966 | + } | ||
| 967 | + | ||
| 793 | 968 | void StatementSync::Get(const FunctionCallbackInfo<Value>& args) { | |
| 794 | 969 | StatementSync* stmt; | |
| 795 | 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); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -93,6 +93,7 @@ class StatementSync : public BaseObject { | |||
| 93 | 93 | DatabaseSync* db, | |
| 94 | 94 | sqlite3_stmt* stmt); | |
| 95 | 95 | static void All(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 96 | + static void Iterate(const v8::FunctionCallbackInfo<v8::Value>& args); | ||
| 96 | 97 | static void Get(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 97 | 98 | static void Run(const v8::FunctionCallbackInfo<v8::Value>& args); | |
| 98 | 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); | |
| 119 | 120 | v8::MaybeLocal<v8::Value> ColumnToValue(const int column); | |
| 120 | 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 | + const v8::FunctionCallbackInfo<v8::Value>& args); | ||
| 121 | 127 | }; | |
| 122 | 128 | ||
| 123 | 129 | using Sqlite3ChangesetGenFunc = int (*)(sqlite3_session*, int*, void**); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -84,6 +84,42 @@ suite('StatementSync.prototype.all()', () => { | |||
| 84 | 84 | }); | |
| 85 | 85 | }); | |
| 86 | 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 | + }); | ||
| 93 | + | ||
| 94 | + test('executes a query and returns all results', (t) => { | ||
| 95 | + const db = new DatabaseSync(nextDb()); | ||
| 96 | + let stmt = db.prepare('CREATE TABLE storage(key TEXT, val TEXT)'); | ||
| 97 | + t.assert.deepStrictEqual(stmt.run(), { changes: 0, lastInsertRowid: 0 }); | ||
| 98 | + stmt = db.prepare('INSERT INTO storage (key, val) VALUES (?, ?)'); | ||
| 99 | + t.assert.deepStrictEqual( | ||
| 100 | + stmt.run('key1', 'val1'), | ||
| 101 | + { changes: 1, lastInsertRowid: 1 }, | ||
| 102 | + ); | ||
| 103 | + t.assert.deepStrictEqual( | ||
| 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()) { | ||
| 118 | + t.assert.deepStrictEqual(item, itemsLoop.shift()); | ||
| 119 | + } | ||
| 120 | + }); | ||
| 121 | + }); | ||
| 122 | + | ||
| 87 | 123 | suite('StatementSync.prototype.run()', () => { | |
| 88 | 124 | test('executes a query and returns change metadata', (t) => { | |
| 89 | 125 | const db = new DatabaseSync(nextDb()); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments