sqlite: add support for custom functions · nodejs/node@4745798
@@ -28,6 +28,8 @@ using v8::Function;
2828using v8::FunctionCallback;
2929using v8::FunctionCallbackInfo;
3030using v8::FunctionTemplate;
31+using v8::Global;
32+using v8::Int32;
3133using v8::Integer;
3234using v8::Isolate;
3335using v8::Local;
@@ -112,6 +114,123 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, const char* message) {
112114 }
113115}
114116117+class UserDefinedFunction {
118+public:
119+explicit UserDefinedFunction(Environment* env,
120+ Local<Function> fn,
121+bool use_bigint_args)
122+ : env_(env), fn_(env->isolate(), fn), use_bigint_args_(use_bigint_args) {}
123+virtual ~UserDefinedFunction() {}
124+125+static void xFunc(sqlite3_context* ctx, int argc, sqlite3_value** argv) {
126+ UserDefinedFunction* self =
127+static_cast<UserDefinedFunction*>(sqlite3_user_data(ctx));
128+ Environment* env = self->env_;
129+ Isolate* isolate = env->isolate();
130+auto recv = Undefined(isolate);
131+auto fn = self->fn_.Get(isolate);
132+ LocalVector<Value> js_argv(isolate);
133+134+for (int i = 0; i < argc; ++i) {
135+ sqlite3_value* value = argv[i];
136+ MaybeLocal<Value> js_val;
137+138+switch (sqlite3_value_type(value)) {
139+case SQLITE_INTEGER: {
140+ sqlite3_int64 val = sqlite3_value_int64(value);
141+if (self->use_bigint_args_) {
142+ js_val = BigInt::New(isolate, val);
143+ } else if (std::abs(val) <= kMaxSafeJsInteger) {
144+ js_val = Number::New(isolate, val);
145+ } else {
146+THROW_ERR_OUT_OF_RANGE(isolate,
147+"Value is too large to be represented as a "
148+"JavaScript number: %" PRId64,
149+ val);
150+return;
151+ }
152+break;
153+ }
154+case SQLITE_FLOAT:
155+ js_val = Number::New(isolate, sqlite3_value_double(value));
156+break;
157+case SQLITE_TEXT: {
158+const char* v =
159+reinterpret_cast<const char*>(sqlite3_value_text(value));
160+ js_val = String::NewFromUtf8(isolate, v).As<Value>();
161+break;
162+ }
163+case SQLITE_NULL:
164+ js_val = Null(isolate);
165+break;
166+case SQLITE_BLOB: {
167+size_t size = static_cast<size_t>(sqlite3_value_bytes(value));
168+auto data =
169+reinterpret_cast<const uint8_t*>(sqlite3_value_blob(value));
170+auto store = ArrayBuffer::NewBackingStore(isolate, size);
171+memcpy(store->Data(), data, size);
172+auto ab = ArrayBuffer::New(isolate, std::move(store));
173+ js_val = Uint8Array::New(ab, 0, size);
174+break;
175+ }
176+default:
177+UNREACHABLE("Bad SQLite value");
178+ }
179+180+ Local<Value> local;
181+if (!js_val.ToLocal(&local)) {
182+return;
183+ }
184+185+ js_argv.emplace_back(local);
186+ }
187+188+ MaybeLocal<Value> retval =
189+ fn->Call(env->context(), recv, argc, js_argv.data());
190+ Local<Value> result;
191+if (!retval.ToLocal(&result)) {
192+return;
193+ }
194+195+if (result->IsUndefined() || result->IsNull()) {
196+sqlite3_result_null(ctx);
197+ } else if (result->IsNumber()) {
198+sqlite3_result_double(ctx, result.As<Number>()->Value());
199+ } else if (result->IsString()) {
200+ Utf8Value val(isolate, result.As<String>());
201+sqlite3_result_text(ctx, *val, val.length(), SQLITE_TRANSIENT);
202+ } else if (result->IsUint8Array()) {
203+ ArrayBufferViewContents<uint8_t> buf(result);
204+sqlite3_result_blob(ctx, buf.data(), buf.length(), SQLITE_TRANSIENT);
205+ } else if (result->IsBigInt()) {
206+bool lossless;
207+int64_t as_int = result.As<BigInt>()->Int64Value(&lossless);
208+if (!lossless) {
209+sqlite3_result_error(ctx, "BigInt value is too large for SQLite", -1);
210+return;
211+ }
212+sqlite3_result_int64(ctx, as_int);
213+ } else if (result->IsPromise()) {
214+sqlite3_result_error(
215+ ctx, "Asynchronous user-defined functions are not supported", -1);
216+ } else {
217+sqlite3_result_error(
218+ ctx,
219+"Returned JavaScript value cannot be converted to a SQLite value",
220+ -1);
221+ }
222+ }
223+224+static void xDestroy(void* self) {
225+delete static_cast<UserDefinedFunction*>(self);
226+ }
227+228+private:
229+ Environment* env_;
230+ Global<Function> fn_;
231+bool use_bigint_args_;
232+};
233+115234DatabaseSync::DatabaseSync(Environment* env,
116235 Local<Object> object,
117236 DatabaseOpenConfiguration&& open_config,
@@ -400,6 +519,151 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
400519CHECK_ERROR_OR_THROW(env->isolate(), db->connection_, r, SQLITE_OK, void());
401520}
402521522+void DatabaseSync::CustomFunction(const FunctionCallbackInfo<Value>& args) {
523+ DatabaseSync* db;
524+ASSIGN_OR_RETURN_UNWRAP(&db, args.This());
525+ Environment* env = Environment::GetCurrent(args);
526+THROW_AND_RETURN_ON_BAD_STATE(env, !db->IsOpen(), "database is not open");
527+528+if (!args[0]->IsString()) {
529+THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
530+"The \"name\" argument must be a string.");
531+return;
532+ }
533+534+int fn_index = args.Length() < 3 ? 1 : 2;
535+bool use_bigint_args = false;
536+bool varargs = false;
537+bool deterministic = false;
538+bool direct_only = false;
539+540+if (fn_index > 1) {
541+if (!args[1]->IsObject()) {
542+THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
543+"The \"options\" argument must be an object.");
544+return;
545+ }
546+547+ Local<Object> options = args[1].As<Object>();
548+ Local<Value> use_bigint_args_v;
549+if (!options
550+ ->Get(env->context(),
551+FIXED_ONE_BYTE_STRING(env->isolate(), "useBigIntArguments"))
552+ .ToLocal(&use_bigint_args_v)) {
553+return;
554+ }
555+556+if (!use_bigint_args_v->IsUndefined()) {
557+if (!use_bigint_args_v->IsBoolean()) {
558+THROW_ERR_INVALID_ARG_TYPE(
559+ env->isolate(),
560+"The \"options.useBigIntArguments\" argument must be a boolean.");
561+return;
562+ }
563+ use_bigint_args = use_bigint_args_v.As<Boolean>()->Value();
564+ }
565+566+ Local<Value> varargs_v;
567+if (!options
568+ ->Get(env->context(),
569+FIXED_ONE_BYTE_STRING(env->isolate(), "varargs"))
570+ .ToLocal(&varargs_v)) {
571+return;
572+ }
573+574+if (!varargs_v->IsUndefined()) {
575+if (!varargs_v->IsBoolean()) {
576+THROW_ERR_INVALID_ARG_TYPE(
577+ env->isolate(),
578+"The \"options.varargs\" argument must be a boolean.");
579+return;
580+ }
581+ varargs = varargs_v.As<Boolean>()->Value();
582+ }
583+584+ Local<Value> deterministic_v;
585+if (!options
586+ ->Get(env->context(),
587+FIXED_ONE_BYTE_STRING(env->isolate(), "deterministic"))
588+ .ToLocal(&deterministic_v)) {
589+return;
590+ }
591+592+if (!deterministic_v->IsUndefined()) {
593+if (!deterministic_v->IsBoolean()) {
594+THROW_ERR_INVALID_ARG_TYPE(
595+ env->isolate(),
596+"The \"options.deterministic\" argument must be a boolean.");
597+return;
598+ }
599+ deterministic = deterministic_v.As<Boolean>()->Value();
600+ }
601+602+ Local<Value> direct_only_v;
603+if (!options
604+ ->Get(env->context(),
605+FIXED_ONE_BYTE_STRING(env->isolate(), "directOnly"))
606+ .ToLocal(&direct_only_v)) {
607+return;
608+ }
609+610+if (!direct_only_v->IsUndefined()) {
611+if (!direct_only_v->IsBoolean()) {
612+THROW_ERR_INVALID_ARG_TYPE(
613+ env->isolate(),
614+"The \"options.directOnly\" argument must be a boolean.");
615+return;
616+ }
617+ direct_only = direct_only_v.As<Boolean>()->Value();
618+ }
619+ }
620+621+if (!args[fn_index]->IsFunction()) {
622+THROW_ERR_INVALID_ARG_TYPE(env->isolate(),
623+"The \"function\" argument must be a function.");
624+return;
625+ }
626+627+ Utf8Value name(env->isolate(), args[0].As<String>());
628+ Local<Function> fn = args[fn_index].As<Function>();
629+630+int argc = 0;
631+if (varargs) {
632+ argc = -1;
633+ } else {
634+ Local<Value> js_len;
635+if (!fn->Get(env->context(),
636+FIXED_ONE_BYTE_STRING(env->isolate(), "length"))
637+ .ToLocal(&js_len)) {
638+return;
639+ }
640+ argc = js_len.As<Int32>()->Value();
641+ }
642+643+ UserDefinedFunction* user_data =
644+new UserDefinedFunction(env, fn, use_bigint_args);
645+int text_rep = SQLITE_UTF8;
646+647+if (deterministic) {
648+ text_rep |= SQLITE_DETERMINISTIC;
649+ }
650+651+if (direct_only) {
652+ text_rep |= SQLITE_DIRECTONLY;
653+ }
654+655+int r = sqlite3_create_function_v2(db->connection_,
656+ *name,
657+ argc,
658+ text_rep,
659+ user_data,
660+ UserDefinedFunction::xFunc,
661+nullptr,
662+nullptr,
663+ UserDefinedFunction::xDestroy);
664+CHECK_ERROR_OR_THROW(env->isolate(), db->connection_, r, SQLITE_OK, void());
665+}
666+403667void DatabaseSync::CreateSession(const FunctionCallbackInfo<Value>& args) {
404668 std::string table;
405669 std::string db_name = "main";
@@ -1409,6 +1673,7 @@ static void Initialize(Local<Object> target,
14091673SetProtoMethod(isolate, db_tmpl, "close", DatabaseSync::Close);
14101674SetProtoMethod(isolate, db_tmpl, "prepare", DatabaseSync::Prepare);
14111675SetProtoMethod(isolate, db_tmpl, "exec", DatabaseSync::Exec);
1676+SetProtoMethod(isolate, db_tmpl, "function", DatabaseSync::CustomFunction);
14121677SetProtoMethod(
14131678 isolate, db_tmpl, "createSession", DatabaseSync::CreateSession);
14141679SetProtoMethod(