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