@@ -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); |
|