@@ -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}
114116
117+ 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) {
400519 CHECK_ERROR_OR_THROW (env->isolate (), db->connection_ , r, SQLITE_OK, void ());
401520}
402521
522+ 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,
14091673 SetProtoMethod (isolate, db_tmpl, " close" , DatabaseSync::Close);
14101674 SetProtoMethod (isolate, db_tmpl, " prepare" , DatabaseSync::Prepare);
14111675 SetProtoMethod (isolate, db_tmpl, " exec" , DatabaseSync::Exec);
1676+ SetProtoMethod (isolate, db_tmpl, " function" , DatabaseSync::CustomFunction);
14121677 SetProtoMethod (
14131678 isolate, db_tmpl, " createSession" , DatabaseSync::CreateSession);
14141679 SetProtoMethod (
0 commit comments