33#define NODE_API_NO_EXTERNAL_BUFFERS_ALLOWED
44#include " napi.h"
55
6+ #include < condition_variable>
7+ #include < cstddef>
8+ #include < mutex>
69#include < optional>
710#include < string>
811#include < vector>
@@ -462,6 +465,10 @@ Napi::External<_duckdb_data_chunk> CreateExternalForDataChunk(Napi::Env env, duc
462465 return CreateExternal<_duckdb_data_chunk>(env, DataChunkTypeTag, chunk, FinalizeDataChunk);
463466}
464467
468+ Napi::External<_duckdb_data_chunk> CreateExternalForDataChunkWithoutFinalizer (Napi::Env env, duckdb_data_chunk chunk) {
469+ return CreateExternalWithoutFinalizer<_duckdb_data_chunk>(env, DataChunkTypeTag, chunk);
470+ }
471+
465472duckdb_data_chunk GetDataChunkFromExternal (Napi::Env env, Napi::Value value) {
466473 return GetDataFromExternal<_duckdb_data_chunk>(env, DataChunkTypeTag, value, " Invalid data chunk argument" );
467474}
@@ -485,6 +492,21 @@ duckdb_extracted_statements GetExtractedStatementsFromExternal(Napi::Env env, Na
485492 return GetDataFromExternal<_duckdb_extracted_statements>(env, ExtractedStatementsTypeTag, value, " Invalid extracted statements argument" );
486493}
487494
495+ static const napi_type_tag FunctionInfoTypeTag = {
496+ 0xB0E6739D698048EA , 0x9E79734E3E137AC3
497+ };
498+
499+ Napi::External<_duckdb_function_info> CreateExternalForFunctionInfoWithoutFinalizer (Napi::Env env, duckdb_function_info function_info) {
500+ // FunctionInfo objects are never explicitly created; they are passed in to function callbacks.
501+ return CreateExternalWithoutFinalizer<_duckdb_function_info>(env, FunctionInfoTypeTag, function_info);
502+ }
503+
504+ duckdb_function_info GetFunctionInfoFromExternal (Napi::Env env, Napi::Value value) {
505+ return GetDataFromExternal<_duckdb_function_info>(env, FunctionInfoTypeTag, value, " Invalid function info argument" );
506+ }
507+
508+
509+
488510static const napi_type_tag InstanceCacheTypeTag = {
489511 0x2F3346E30FB5457C , 0xB9201EE5112EEF9F
490512};
@@ -592,6 +614,38 @@ duckdb_result *GetResultFromExternal(Napi::Env env, Napi::Value value) {
592614 return GetDataFromExternal<duckdb_result>(env, ResultTypeTag, value, " Invalid result argument" );
593615}
594616
617+ static const napi_type_tag ScalarFunctionTypeTag = {
618+ 0x95D48B7051D14994 , 0x9F883D7DF5DEA86D
619+ };
620+
621+ typedef struct {
622+ duckdb_scalar_function scalar_function;
623+ } duckdb_scalar_function_holder;
624+
625+ duckdb_scalar_function_holder *CreateScalarFunctionHolder (duckdb_scalar_function scalar_function) {
626+ auto scalar_function_holder_ptr = reinterpret_cast <duckdb_scalar_function_holder*>(duckdb_malloc (sizeof (duckdb_scalar_function_holder)));
627+ scalar_function_holder_ptr->scalar_function = scalar_function;
628+ return scalar_function_holder_ptr;
629+ }
630+
631+ void FinalizeScalarFunctionHolder (Napi::BasicEnv, duckdb_scalar_function_holder *scalar_function_holder_ptr) {
632+ // duckdb_destroy_scalar_function is a no-op if already closed
633+ duckdb_destroy_scalar_function (&scalar_function_holder_ptr->scalar_function );
634+ duckdb_free (scalar_function_holder_ptr);
635+ }
636+
637+ Napi::External<duckdb_scalar_function_holder> CreateExternalForScalarFunction (Napi::Env env, duckdb_scalar_function scalar_function) {
638+ return CreateExternal<duckdb_scalar_function_holder>(env, ScalarFunctionTypeTag, CreateScalarFunctionHolder (scalar_function), FinalizeScalarFunctionHolder);
639+ }
640+
641+ duckdb_scalar_function_holder *GetScalarFunctionHolderFromExternal (Napi::Env env, Napi::Value value) {
642+ return GetDataFromExternal<duckdb_scalar_function_holder>(env, ScalarFunctionTypeTag, value, " Invalid scalar function argument" );
643+ }
644+
645+ duckdb_scalar_function GetScalarFunctionFromExternal (Napi::Env env, Napi::Value value) {
646+ return GetScalarFunctionHolderFromExternal (env, value)->scalar_function ;
647+ }
648+
595649static const napi_type_tag ValueTypeTag = {
596650 0xC60F36613BF14E93 , 0xBAA92848936FAA25
597651};
@@ -615,7 +669,7 @@ static const napi_type_tag VectorTypeTag = {
615669 0x9FE56DE8E3124D07 , 0x9ABF31145EDE1C9E
616670};
617671
618- Napi::External<_duckdb_vector> CreateExternalForVector (Napi::Env env, duckdb_vector vector) {
672+ Napi::External<_duckdb_vector> CreateExternalForVectorWithoutFinalizer (Napi::Env env, duckdb_vector vector) {
619673 // Vectors live as long as their containing data chunk; they cannot be explicitly destroyed.
620674 return CreateExternalWithoutFinalizer<_duckdb_vector>(env, VectorTypeTag, vector);
621675}
@@ -624,6 +678,78 @@ duckdb_vector GetVectorFromExternal(Napi::Env env, Napi::Value value) {
624678 return GetDataFromExternal<_duckdb_vector>(env, VectorTypeTag, value, " Invalid vector argument" );
625679}
626680
681+ // Scalar functions
682+
683+ using ScalarFunctionMainContext = std::nullptr_t ;
684+ struct ScalarFunctionMainData {
685+ duckdb_function_info info;
686+ duckdb_data_chunk input;
687+ duckdb_vector output;
688+ std::condition_variable *cv;
689+ std::mutex *cv_mutex;
690+ bool done;
691+ };
692+ void ScalarFunctionMainCallback (Napi::Env env, Napi::Function callback, ScalarFunctionMainContext *context, ScalarFunctionMainData *data) {
693+ if (env != nullptr ) {
694+ if (callback != nullptr ) {
695+ callback.Call (
696+ env.Undefined (),
697+ {
698+ CreateExternalForFunctionInfoWithoutFinalizer (env, data->info ),
699+ CreateExternalForDataChunkWithoutFinalizer (env, data->input ),
700+ CreateExternalForVectorWithoutFinalizer (env, data->output )
701+ }
702+ );
703+ }
704+ }
705+ {
706+ std::lock_guard lk (*data->cv_mutex );
707+ data->done = true ;
708+ }
709+ data->cv ->notify_one ();
710+ }
711+ using ScalarFunctionMainTSFN = Napi::TypedThreadSafeFunction<ScalarFunctionMainContext, ScalarFunctionMainData, ScalarFunctionMainCallback>;
712+
713+ struct ScalarFunctionMainExtraInfo {
714+ ScalarFunctionMainTSFN tsfn;
715+ // TODO: user extra info
716+ };
717+
718+ ScalarFunctionMainExtraInfo *CreateScalarFunctionMainExtraInfo (Napi::Env env, Napi::Function func) {
719+ auto extra_info = reinterpret_cast <ScalarFunctionMainExtraInfo*>(duckdb_malloc (sizeof (ScalarFunctionMainExtraInfo)));
720+ extra_info->tsfn = ScalarFunctionMainTSFN::New (env, func, " ScalarFunctionMain" , 0 , 1 );
721+ return extra_info;
722+ }
723+
724+ void DeleteScalarFunctionMainExtraInfo (ScalarFunctionMainExtraInfo *extra_info) {
725+ extra_info->tsfn .Release ();
726+ duckdb_free (extra_info);
727+ }
728+
729+ void ScalarFunctionMainFunction (duckdb_function_info info, duckdb_data_chunk input, duckdb_vector output) {
730+ auto extra_info = reinterpret_cast <ScalarFunctionMainExtraInfo*>(duckdb_scalar_function_get_extra_info (info));
731+ auto data = reinterpret_cast <ScalarFunctionMainData*>(duckdb_malloc (sizeof (ScalarFunctionMainData)));
732+ data->info = info;
733+ data->input = input;
734+ data->output = output;
735+ data->cv = new std::condition_variable;
736+ data->cv_mutex = new std::mutex;
737+ data->done = false ;
738+ // The "blocking" part of this call only waits for queue space, not for the JS function call to complete.
739+ // Since we specify no limit to the queue space, it in fact never blocks.
740+ auto status = extra_info->tsfn .BlockingCall (data);
741+ if (status == napi_ok) {
742+ // Wait for the JS function call to complete.
743+ std::unique_lock<std::mutex> lk (*data->cv_mutex );
744+ data->cv ->wait (lk, [&]{ return data->done ; });
745+ } else {
746+ duckdb_scalar_function_set_error (info, " BlockingCall returned not ok" );
747+ }
748+ delete data->cv ;
749+ delete data->cv_mutex ;
750+ duckdb_free (data);
751+ }
752+
627753// Promise workers
628754
629755class PromiseWorker : public Napi ::AsyncWorker {
@@ -1421,6 +1547,14 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
14211547 InstanceMethod (" validity_set_row_invalid" , &DuckDBNodeAddon::validity_set_row_invalid),
14221548 InstanceMethod (" validity_set_row_valid" , &DuckDBNodeAddon::validity_set_row_valid),
14231549
1550+ InstanceMethod (" create_scalar_function" , &DuckDBNodeAddon::create_scalar_function),
1551+ InstanceMethod (" destroy_scalar_function_sync" , &DuckDBNodeAddon::destroy_scalar_function_sync),
1552+ InstanceMethod (" scalar_function_set_name" , &DuckDBNodeAddon::scalar_function_set_name),
1553+ InstanceMethod (" scalar_function_set_return_type" , &DuckDBNodeAddon::scalar_function_set_return_type),
1554+ InstanceMethod (" scalar_function_set_function" , &DuckDBNodeAddon::scalar_function_set_function),
1555+ InstanceMethod (" register_scalar_function" , &DuckDBNodeAddon::register_scalar_function),
1556+ InstanceMethod (" scalar_function_set_error" , &DuckDBNodeAddon::scalar_function_set_error),
1557+
14241558 InstanceMethod (" appender_create" , &DuckDBNodeAddon::appender_create),
14251559 InstanceMethod (" appender_create_ext" , &DuckDBNodeAddon::appender_create_ext),
14261560 InstanceMethod (" appender_column_count" , &DuckDBNodeAddon::appender_column_count),
@@ -3680,7 +3814,7 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
36803814 auto chunk = GetDataChunkFromExternal (env, info[0 ]);
36813815 auto column_index = info[1 ].As <Napi::Number>().Uint32Value ();
36823816 auto vector = duckdb_data_chunk_get_vector (chunk, column_index);
3683- return CreateExternalForVector (env, vector);
3817+ return CreateExternalForVectorWithoutFinalizer (env, vector);
36843818 }
36853819
36863820 // DUCKDB_C_API idx_t duckdb_data_chunk_get_size(duckdb_data_chunk chunk);
@@ -3779,7 +3913,7 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
37793913 auto env = info.Env ();
37803914 auto vector = GetVectorFromExternal (env, info[0 ]);
37813915 auto child = duckdb_list_vector_get_child (vector);
3782- return CreateExternalForVector (env, child);
3916+ return CreateExternalForVectorWithoutFinalizer (env, child);
37833917 }
37843918
37853919 // DUCKDB_C_API idx_t duckdb_list_vector_get_size(duckdb_vector vector);
@@ -3818,7 +3952,7 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
38183952 auto vector = GetVectorFromExternal (env, info[0 ]);
38193953 auto index = info[1 ].As <Napi::Number>().Uint32Value ();
38203954 auto child = duckdb_struct_vector_get_child (vector, index);
3821- return CreateExternalForVector (env, child);
3955+ return CreateExternalForVectorWithoutFinalizer (env, child);
38223956 }
38233957
38243958 // DUCKDB_C_API duckdb_vector duckdb_array_vector_get_child(duckdb_vector vector);
@@ -3827,7 +3961,7 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
38273961 auto env = info.Env ();
38283962 auto vector = GetVectorFromExternal (env, info[0 ]);
38293963 auto child = duckdb_array_vector_get_child (vector);
3830- return CreateExternalForVector (env, child);
3964+ return CreateExternalForVectorWithoutFinalizer (env, child);
38313965 }
38323966
38333967 // DUCKDB_C_API void duckdb_slice_vector(duckdb_vector vector, duckdb_selection_vector selection, idx_t len);
@@ -3878,23 +4012,90 @@ class DuckDBNodeAddon : public Napi::Addon<DuckDBNodeAddon> {
38784012 }
38794013
38804014 // DUCKDB_C_API duckdb_scalar_function duckdb_create_scalar_function();
4015+ // function create_scalar_function(): ScalarFunction
4016+ Napi::Value create_scalar_function (const Napi::CallbackInfo& info) {
4017+ auto env = info.Env ();
4018+ auto scalar_function = duckdb_create_scalar_function ();
4019+ return CreateExternalForScalarFunction (env, scalar_function);
4020+ }
4021+
38814022 // DUCKDB_C_API void duckdb_destroy_scalar_function(duckdb_scalar_function *scalar_function);
4023+ // function destroy_scalar_function_sync(scalar_function: ScalarFunction): void
4024+ Napi::Value destroy_scalar_function_sync (const Napi::CallbackInfo& info) {
4025+ auto env = info.Env ();
4026+ auto scalar_function_holder_ptr = GetScalarFunctionHolderFromExternal (env, info[0 ]);
4027+ // duckdb_destroy_scalar_function is a no-op if already closed
4028+ duckdb_destroy_scalar_function (&scalar_function_holder_ptr->scalar_function );
4029+ return env.Undefined ();
4030+ }
4031+
38824032 // DUCKDB_C_API void duckdb_scalar_function_set_name(duckdb_scalar_function scalar_function, const char *name);
4033+ // function scalar_function_set_name(scalar_function: ScalarFunction, name: string): void
4034+ Napi::Value scalar_function_set_name (const Napi::CallbackInfo& info) {
4035+ auto env = info.Env ();
4036+ auto scalar_function = GetScalarFunctionFromExternal (env, info[0 ]);
4037+ std::string name = info[1 ].As <Napi::String>();
4038+ duckdb_scalar_function_set_name (scalar_function, name.c_str ());
4039+ return env.Undefined ();
4040+ }
4041+
38834042 // DUCKDB_C_API void duckdb_scalar_function_set_varargs(duckdb_scalar_function scalar_function, duckdb_logical_type type);
38844043 // DUCKDB_C_API void duckdb_scalar_function_set_special_handling(duckdb_scalar_function scalar_function);
38854044 // DUCKDB_C_API void duckdb_scalar_function_set_volatile(duckdb_scalar_function scalar_function);
38864045 // DUCKDB_C_API void duckdb_scalar_function_add_parameter(duckdb_scalar_function scalar_function, duckdb_logical_type type);
4046+
38874047 // DUCKDB_C_API void duckdb_scalar_function_set_return_type(duckdb_scalar_function scalar_function, duckdb_logical_type type);
4048+ // function scalar_function_set_return_type(scalar_function: ScalarFunction, logical_type: LogicalType): void
4049+ Napi::Value scalar_function_set_return_type (const Napi::CallbackInfo& info) {
4050+ auto env = info.Env ();
4051+ auto scalar_function = GetScalarFunctionFromExternal (env, info[0 ]);
4052+ auto logical_type = GetLogicalTypeFromExternal (env, info[1 ]);
4053+ duckdb_scalar_function_set_return_type (scalar_function, logical_type);
4054+ return env.Undefined ();
4055+ }
4056+
38884057 // DUCKDB_C_API void duckdb_scalar_function_set_extra_info(duckdb_scalar_function scalar_function, void *extra_info, duckdb_delete_callback_t destroy);
38894058 // DUCKDB_C_API void duckdb_scalar_function_set_bind(duckdb_scalar_function scalar_function, duckdb_scalar_function_bind_t bind);
38904059 // DUCKDB_C_API void duckdb_scalar_function_set_bind_data(duckdb_bind_info info, void *bind_data, duckdb_delete_callback_t destroy);
38914060 // DUCKDB_C_API void duckdb_scalar_function_bind_set_error(duckdb_bind_info info, const char *error);
4061+
38924062 // DUCKDB_C_API void duckdb_scalar_function_set_function(duckdb_scalar_function scalar_function, duckdb_scalar_function_t function);
4063+ // function scalar_function_set_function(scalar_function: ScalarFunction, func: ScalarFunctionMainFunction): void
4064+ Napi::Value scalar_function_set_function (const Napi::CallbackInfo& info) {
4065+ auto env = info.Env ();
4066+ auto scalar_function = GetScalarFunctionFromExternal (env, info[0 ]);
4067+ auto func = info[1 ].As <Napi::Function>();
4068+ auto extra_info = CreateScalarFunctionMainExtraInfo (env, func);
4069+ duckdb_scalar_function_set_extra_info (scalar_function, extra_info, reinterpret_cast <duckdb_delete_callback_t >(DeleteScalarFunctionMainExtraInfo));
4070+ duckdb_scalar_function_set_function (scalar_function, &ScalarFunctionMainFunction);
4071+ return env.Undefined ();
4072+ }
4073+
38934074 // DUCKDB_C_API duckdb_state duckdb_register_scalar_function(duckdb_connection con, duckdb_scalar_function scalar_function);
4075+ // function register_scalar_function(connection: Connection, scalar_function: ScalarFunction): void
4076+ Napi::Value register_scalar_function (const Napi::CallbackInfo& info) {
4077+ auto env = info.Env ();
4078+ auto connection = GetConnectionFromExternal (env, info[0 ]);
4079+ auto scalar_function = GetScalarFunctionFromExternal (env, info[1 ]);
4080+ if (duckdb_register_scalar_function (connection, scalar_function)) {
4081+ throw Napi::Error::New (env, " Failed to register scalar function" );
4082+ }
4083+ return env.Undefined ();
4084+ }
4085+
38944086 // DUCKDB_C_API void *duckdb_scalar_function_get_extra_info(duckdb_function_info info);
38954087 // DUCKDB_C_API void *duckdb_scalar_function_get_bind_data(duckdb_function_info info);
38964088 // DUCKDB_C_API void duckdb_scalar_function_get_client_context(duckdb_bind_info info, duckdb_client_context *out_context);
4089+
38974090 // DUCKDB_C_API void duckdb_scalar_function_set_error(duckdb_function_info info, const char *error);
4091+ // function scalar_function_set_error(function_info: FunctionInfo, error: string): void
4092+ Napi::Value scalar_function_set_error (const Napi::CallbackInfo& info) {
4093+ auto env = info.Env ();
4094+ auto function_info = GetFunctionInfoFromExternal (env, info[0 ]);
4095+ std::string error = info[1 ].As <Napi::String>();
4096+ duckdb_scalar_function_set_error (function_info, error.c_str ());
4097+ return env.Undefined ();
4098+ }
38984099
38994100 // DUCKDB_C_API duckdb_scalar_function_set duckdb_create_scalar_function_set(const char *name);
39004101 // DUCKDB_C_API void duckdb_destroy_scalar_function_set(duckdb_scalar_function_set *scalar_function_set);
@@ -4490,14 +4691,14 @@ NODE_API_ADDON(DuckDBNodeAddon)
44904691 ---
44914692 431 total functions
44924693
4493- 245 instance methods
4694+ 252 instance methods
44944695 3 unimplemented client context functions
44954696 1 unimplemented table names function
44964697 1 unimplemented value to string function
44974698 1 unimplemented logical type function
44984699 2 unimplemented vector creation functions
44994700 3 unimplemented vector manipulation functions
4500- 18 unimplemented scalar function functions
4701+ 11 unimplemented scalar function functions
45014702 4 unimplemented scalar function set functions
45024703 3 unimplemented selection vector functions
45034704 12 unimplemented aggregate function functions
0 commit comments