@@ -27,6 +27,8 @@ using v8::Function;
27
27
using v8::FunctionCallback;
28
28
using v8::FunctionCallbackInfo;
29
29
using v8::FunctionTemplate;
30
+ using v8::Global;
31
+ using v8::Int32;
30
32
using v8::Integer;
31
33
using v8::Isolate;
32
34
using v8::Local;
@@ -111,6 +113,123 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, const char* message) {
111
113
}
112
114
}
113
115
116
+ class UserDefinedFunction {
117
+ public:
118
+ explicit UserDefinedFunction (Environment* env,
119
+ Local<Function> fn,
120
+ bool use_bigint_args)
121
+ : env_(env), fn_(env->isolate (), fn), use_bigint_args_(use_bigint_args) {}
122
+ virtual ~UserDefinedFunction () {}
123
+
124
+ static void xFunc (sqlite3_context* ctx, int argc, sqlite3_value** argv) {
125
+ UserDefinedFunction* self =
126
+ static_cast <UserDefinedFunction*>(sqlite3_user_data (ctx));
127
+ Environment* env = self->env_ ;
128
+ Isolate* isolate = env->isolate ();
129
+ auto recv = Undefined (isolate);
130
+ auto fn = self->fn_ .Get (isolate);
131
+ std::vector<Local<Value>> js_argv;
132
+
133
+ for (int i = 0 ; i < argc; ++i) {
134
+ sqlite3_value* value = argv[i];
135
+ MaybeLocal<Value> js_val;
136
+
137
+ switch (sqlite3_value_type (value)) {
138
+ case SQLITE_INTEGER: {
139
+ sqlite3_int64 val = sqlite3_value_int64 (value);
140
+ if (self->use_bigint_args_ ) {
141
+ js_val = BigInt::New (isolate, val);
142
+ } else if (std::abs (val) <= kMaxSafeJsInteger ) {
143
+ js_val = Number::New (isolate, val);
144
+ } else {
145
+ THROW_ERR_OUT_OF_RANGE (isolate,
146
+ " Value is too large to be represented as a "
147
+ " JavaScript number: %" PRId64,
148
+ val);
149
+ return ;
150
+ }
151
+ break ;
152
+ }
153
+ case SQLITE_FLOAT:
154
+ js_val = Number::New (isolate, sqlite3_value_double (value));
155
+ break ;
156
+ case SQLITE_TEXT: {
157
+ const char * v =
158
+ reinterpret_cast <const char *>(sqlite3_value_text (value));
159
+ js_val = String::NewFromUtf8 (isolate, v).As <Value>();
160
+ break ;
161
+ }
162
+ case SQLITE_NULL:
163
+ js_val = Null (isolate);
164
+ break ;
165
+ case SQLITE_BLOB: {
166
+ size_t size = static_cast <size_t >(sqlite3_value_bytes (value));
167
+ auto data =
168
+ reinterpret_cast <const uint8_t *>(sqlite3_value_blob (value));
169
+ auto store = ArrayBuffer::NewBackingStore (isolate, size);
170
+ memcpy (store->Data (), data, size);
171
+ auto ab = ArrayBuffer::New (isolate, std::move (store));
172
+ js_val = Uint8Array::New (ab, 0 , size);
173
+ break ;
174
+ }
175
+ default :
176
+ UNREACHABLE (" Bad SQLite value" );
177
+ }
178
+
179
+ Local<Value> local;
180
+ if (!js_val.ToLocal (&local)) {
181
+ return ;
182
+ }
183
+
184
+ js_argv.emplace_back (local);
185
+ }
186
+
187
+ MaybeLocal<Value> retval =
188
+ fn->Call (env->context (), recv, argc, js_argv.data ());
189
+ Local<Value> result;
190
+ if (!retval.ToLocal (&result)) {
191
+ return ;
192
+ }
193
+
194
+ if (result->IsUndefined () || result->IsNull ()) {
195
+ sqlite3_result_null (ctx);
196
+ } else if (result->IsNumber ()) {
197
+ sqlite3_result_double (ctx, result.As <Number>()->Value ());
198
+ } else if (result->IsString ()) {
199
+ Utf8Value val (isolate, result.As <String>());
200
+ sqlite3_result_text (ctx, *val, val.length (), SQLITE_TRANSIENT);
201
+ } else if (result->IsUint8Array ()) {
202
+ ArrayBufferViewContents<uint8_t > buf (result);
203
+ sqlite3_result_blob (ctx, buf.data (), buf.length (), SQLITE_TRANSIENT);
204
+ } else if (result->IsBigInt ()) {
205
+ bool lossless;
206
+ int64_t as_int = result.As <BigInt>()->Int64Value (&lossless);
207
+ if (!lossless) {
208
+ sqlite3_result_error (ctx, " BigInt value is too large for SQLite" , -1 );
209
+ return ;
210
+ }
211
+ sqlite3_result_int64 (ctx, as_int);
212
+ } else if (result->IsPromise ()) {
213
+ sqlite3_result_error (
214
+ ctx, " Asynchronous user-defined functions are not supported" , -1 );
215
+ } else {
216
+ sqlite3_result_error (
217
+ ctx,
218
+ " Returned JavaScript value cannot be converted to a SQLite value" ,
219
+ -1 );
220
+ }
221
+ }
222
+
223
+ static void xDestroy (void * self) {
224
+ delete static_cast <UserDefinedFunction*>(self);
225
+ }
226
+
227
+ private:
228
+ Environment* env_;
229
+ Global<Function> fn_;
230
+ bool use_bigint_args_;
231
+ };
232
+
114
233
DatabaseSync::DatabaseSync (Environment* env,
115
234
Local<Object> object,
116
235
DatabaseOpenConfiguration&& open_config,
@@ -363,6 +482,151 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
363
482
CHECK_ERROR_OR_THROW (env->isolate (), db->connection_ , r, SQLITE_OK, void ());
364
483
}
365
484
485
+ void DatabaseSync::CustomFunction (const FunctionCallbackInfo<Value>& args) {
486
+ DatabaseSync* db;
487
+ ASSIGN_OR_RETURN_UNWRAP (&db, args.This ());
488
+ Environment* env = Environment::GetCurrent (args);
489
+ THROW_AND_RETURN_ON_BAD_STATE (env, !db->IsOpen (), " database is not open" );
490
+
491
+ if (!args[0 ]->IsString ()) {
492
+ THROW_ERR_INVALID_ARG_TYPE (env->isolate (),
493
+ " The \" name\" argument must be a string." );
494
+ return ;
495
+ }
496
+
497
+ int fn_index = args.Length () < 3 ? 1 : 2 ;
498
+ bool use_bigint_args = false ;
499
+ bool varargs = false ;
500
+ bool deterministic = false ;
501
+ bool direct_only = false ;
502
+
503
+ if (fn_index > 1 ) {
504
+ if (!args[1 ]->IsObject ()) {
505
+ THROW_ERR_INVALID_ARG_TYPE (env->isolate (),
506
+ " The \" options\" argument must be an object." );
507
+ return ;
508
+ }
509
+
510
+ Local<Object> options = args[1 ].As <Object>();
511
+ Local<Value> use_bigint_args_v;
512
+ if (!options
513
+ ->Get (env->context (),
514
+ FIXED_ONE_BYTE_STRING (env->isolate (), " useBigIntArguments" ))
515
+ .ToLocal (&use_bigint_args_v)) {
516
+ return ;
517
+ }
518
+
519
+ if (!use_bigint_args_v->IsUndefined ()) {
520
+ if (!use_bigint_args_v->IsBoolean ()) {
521
+ THROW_ERR_INVALID_ARG_TYPE (
522
+ env->isolate (),
523
+ " The \" options.useBigIntArguments\" argument must be a boolean." );
524
+ return ;
525
+ }
526
+ use_bigint_args = use_bigint_args_v.As <Boolean>()->Value ();
527
+ }
528
+
529
+ Local<Value> varargs_v;
530
+ if (!options
531
+ ->Get (env->context (),
532
+ FIXED_ONE_BYTE_STRING (env->isolate (), " varargs" ))
533
+ .ToLocal (&varargs_v)) {
534
+ return ;
535
+ }
536
+
537
+ if (!varargs_v->IsUndefined ()) {
538
+ if (!varargs_v->IsBoolean ()) {
539
+ THROW_ERR_INVALID_ARG_TYPE (
540
+ env->isolate (),
541
+ " The \" options.varargs\" argument must be a boolean." );
542
+ return ;
543
+ }
544
+ varargs = varargs_v.As <Boolean>()->Value ();
545
+ }
546
+
547
+ Local<Value> deterministic_v;
548
+ if (!options
549
+ ->Get (env->context (),
550
+ FIXED_ONE_BYTE_STRING (env->isolate (), " deterministic" ))
551
+ .ToLocal (&deterministic_v)) {
552
+ return ;
553
+ }
554
+
555
+ if (!deterministic_v->IsUndefined ()) {
556
+ if (!deterministic_v->IsBoolean ()) {
557
+ THROW_ERR_INVALID_ARG_TYPE (
558
+ env->isolate (),
559
+ " The \" options.deterministic\" argument must be a boolean." );
560
+ return ;
561
+ }
562
+ deterministic = deterministic_v.As <Boolean>()->Value ();
563
+ }
564
+
565
+ Local<Value> direct_only_v;
566
+ if (!options
567
+ ->Get (env->context (),
568
+ FIXED_ONE_BYTE_STRING (env->isolate (), " directOnly" ))
569
+ .ToLocal (&direct_only_v)) {
570
+ return ;
571
+ }
572
+
573
+ if (!direct_only_v->IsUndefined ()) {
574
+ if (!direct_only_v->IsBoolean ()) {
575
+ THROW_ERR_INVALID_ARG_TYPE (
576
+ env->isolate (),
577
+ " The \" options.directOnly\" argument must be a boolean." );
578
+ return ;
579
+ }
580
+ direct_only = direct_only_v.As <Boolean>()->Value ();
581
+ }
582
+ }
583
+
584
+ if (!args[fn_index]->IsFunction ()) {
585
+ THROW_ERR_INVALID_ARG_TYPE (env->isolate (),
586
+ " The \" function\" argument must be a function." );
587
+ return ;
588
+ }
589
+
590
+ Utf8Value name (env->isolate (), args[0 ].As <String>());
591
+ Local<Function> fn = args[fn_index].As <Function>();
592
+
593
+ int argc = 0 ;
594
+ if (varargs) {
595
+ argc = -1 ;
596
+ } else {
597
+ Local<Value> js_len;
598
+ if (!fn->Get (env->context (),
599
+ FIXED_ONE_BYTE_STRING (env->isolate (), " length" ))
600
+ .ToLocal (&js_len)) {
601
+ return ;
602
+ }
603
+ argc = js_len.As <Int32>()->Value ();
604
+ }
605
+
606
+ UserDefinedFunction* user_data =
607
+ new UserDefinedFunction (env, fn, use_bigint_args);
608
+ int text_rep = SQLITE_UTF8;
609
+
610
+ if (deterministic) {
611
+ text_rep |= SQLITE_DETERMINISTIC;
612
+ }
613
+
614
+ if (direct_only) {
615
+ text_rep |= SQLITE_DIRECTONLY;
616
+ }
617
+
618
+ int r = sqlite3_create_function_v2 (db->connection_ ,
619
+ *name,
620
+ argc,
621
+ text_rep,
622
+ user_data,
623
+ UserDefinedFunction::xFunc,
624
+ nullptr ,
625
+ nullptr ,
626
+ UserDefinedFunction::xDestroy);
627
+ CHECK_ERROR_OR_THROW (env->isolate (), db->connection_ , r, SQLITE_OK, void ());
628
+ }
629
+
366
630
void DatabaseSync::CreateSession (const FunctionCallbackInfo<Value>& args) {
367
631
std::string table;
368
632
std::string db_name = " main" ;
@@ -1308,6 +1572,7 @@ static void Initialize(Local<Object> target,
1308
1572
SetProtoMethod (isolate, db_tmpl, " close" , DatabaseSync::Close);
1309
1573
SetProtoMethod (isolate, db_tmpl, " prepare" , DatabaseSync::Prepare);
1310
1574
SetProtoMethod (isolate, db_tmpl, " exec" , DatabaseSync::Exec);
1575
+ SetProtoMethod (isolate, db_tmpl, " function" , DatabaseSync::CustomFunction);
1311
1576
SetProtoMethod (
1312
1577
isolate, db_tmpl, " createSession" , DatabaseSync::CreateSession);
1313
1578
SetProtoMethod (
0 commit comments