@@ -28,6 +28,8 @@ using v8::Function;
28
28
using v8::FunctionCallback;
29
29
using v8::FunctionCallbackInfo;
30
30
using v8::FunctionTemplate;
31
+ using v8::Global;
32
+ using v8::Int32;
31
33
using v8::Integer;
32
34
using v8::Isolate;
33
35
using v8::Local;
@@ -112,6 +114,123 @@ inline void THROW_ERR_SQLITE_ERROR(Isolate* isolate, const char* message) {
112
114
}
113
115
}
114
116
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
+
115
234
DatabaseSync::DatabaseSync (Environment* env,
116
235
Local<Object> object,
117
236
DatabaseOpenConfiguration&& open_config,
@@ -400,6 +519,151 @@ void DatabaseSync::Exec(const FunctionCallbackInfo<Value>& args) {
400
519
CHECK_ERROR_OR_THROW (env->isolate (), db->connection_ , r, SQLITE_OK, void ());
401
520
}
402
521
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
+
403
667
void DatabaseSync::CreateSession (const FunctionCallbackInfo<Value>& args) {
404
668
std::string table;
405
669
std::string db_name = " main" ;
@@ -1409,6 +1673,7 @@ static void Initialize(Local<Object> target,
1409
1673
SetProtoMethod (isolate, db_tmpl, " close" , DatabaseSync::Close);
1410
1674
SetProtoMethod (isolate, db_tmpl, " prepare" , DatabaseSync::Prepare);
1411
1675
SetProtoMethod (isolate, db_tmpl, " exec" , DatabaseSync::Exec);
1676
+ SetProtoMethod (isolate, db_tmpl, " function" , DatabaseSync::CustomFunction);
1412
1677
SetProtoMethod (
1413
1678
isolate, db_tmpl, " createSession" , DatabaseSync::CreateSession);
1414
1679
SetProtoMethod (
0 commit comments