duckdb 0.4.1-dev9.0 → 0.4.1-dev953.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Makefile +2 -2
- package/binding.gyp +8 -5
- package/lib/duckdb.js +92 -64
- package/package.json +1 -1
- package/src/connection.cpp +109 -130
- package/src/data_chunk.cpp +185 -0
- package/src/database.cpp +49 -15
- package/src/duckdb.cpp +56578 -24996
- package/src/duckdb.hpp +4203 -2416
- package/src/duckdb_node.hpp +20 -17
- package/src/parquet-amalgamation.cpp +37359 -36870
- package/src/parquet-amalgamation.hpp +95 -104
- package/src/statement.cpp +17 -12
- package/test/extension.test.js +1 -1
- package/test/syntax_error.test.js +16 -0
- package/test/udf.test.js +172 -107
package/Makefile
CHANGED
|
@@ -7,10 +7,10 @@ src/duckdb.cpp:
|
|
|
7
7
|
npm install --build-from-source
|
|
8
8
|
|
|
9
9
|
build: ./node_modules src/duckdb.cpp
|
|
10
|
-
./node_modules/.bin/node-pre-gyp build --loglevel=silent
|
|
10
|
+
./node_modules/.bin/node-pre-gyp build -j max --loglevel=silent
|
|
11
11
|
|
|
12
12
|
debug: ./node_modules src/duckdb.cpp
|
|
13
|
-
./node_modules/.bin/node-pre-gyp build --debug --verbose
|
|
13
|
+
./node_modules/.bin/node-pre-gyp build -j max --debug --verbose
|
|
14
14
|
|
|
15
15
|
clean:
|
|
16
16
|
@rm -rf ./build
|
package/binding.gyp
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
"sources": [
|
|
6
6
|
"src/duckdb_node.cpp",
|
|
7
7
|
"src/database.cpp",
|
|
8
|
+
"src/data_chunk.cpp",
|
|
8
9
|
"src/connection.cpp",
|
|
9
10
|
"src/statement.cpp",
|
|
10
11
|
"src/utils.cpp",
|
|
@@ -20,18 +21,20 @@
|
|
|
20
21
|
],
|
|
21
22
|
"cflags_cc": [
|
|
22
23
|
"-frtti",
|
|
23
|
-
"-fexceptions"
|
|
24
|
+
"-fexceptions",
|
|
25
|
+
"-Wno-redundant-move",
|
|
24
26
|
],
|
|
25
27
|
"cflags_cc!": [
|
|
26
|
-
"-fno-rrti"
|
|
28
|
+
"-fno-rrti",
|
|
27
29
|
"-fno-exceptions",
|
|
28
30
|
],
|
|
29
31
|
"cflags": [
|
|
30
32
|
"-frtti",
|
|
31
|
-
"-fexceptions"
|
|
33
|
+
"-fexceptions",
|
|
34
|
+
"-Wno-redundant-move",
|
|
32
35
|
],
|
|
33
36
|
"cflags!": [
|
|
34
|
-
"-fno-rrti"
|
|
37
|
+
"-fno-rrti",
|
|
35
38
|
"-fno-exceptions",
|
|
36
39
|
],
|
|
37
40
|
"xcode_settings": {
|
|
@@ -40,7 +43,7 @@
|
|
|
40
43
|
"CLANG_CXX_LIBRARY": "libc++",
|
|
41
44
|
"MACOSX_DEPLOYMENT_TARGET": "10.15",
|
|
42
45
|
'CLANG_CXX_LANGUAGE_STANDARD':'c++11',
|
|
43
|
-
'OTHER_CFLAGS' : ['-fexceptions', '-frtti']
|
|
46
|
+
'OTHER_CFLAGS' : ['-fexceptions', '-frtti', '-Wno-redundant-move']
|
|
44
47
|
|
|
45
48
|
},
|
|
46
49
|
"msvs_settings": {
|
package/lib/duckdb.js
CHANGED
|
@@ -23,83 +23,111 @@ Connection.prototype.each = function(sql) {
|
|
|
23
23
|
return statement.each.apply(statement, arguments);
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
function ptr_to_arr(buffer, ptype, n) {
|
|
27
|
-
// TODO can we create those on the C++ side of things already?
|
|
28
|
-
switch(ptype) {
|
|
29
|
-
case 'BOOL':
|
|
30
|
-
case 'UINT8':
|
|
31
|
-
return new Uint8Array(buffer.buffer, 0, n);
|
|
32
|
-
case 'INT8':
|
|
33
|
-
return new Int8Array(buffer.buffer, 0, n);
|
|
34
|
-
case 'INT16':
|
|
35
|
-
return new Int16Array(buffer.buffer, 0, n);
|
|
36
|
-
case 'UINT16':
|
|
37
|
-
return new UInt16Array(buffer.buffer, 0, n);
|
|
38
|
-
case 'INT32':
|
|
39
|
-
return new Int32Array(buffer.buffer, 0, n);
|
|
40
|
-
case 'UINT32':
|
|
41
|
-
return new UInt32Array(buffer.buffer, 0, n);
|
|
42
|
-
case 'FLOAT':
|
|
43
|
-
return new Float32Array(buffer.buffer, 0, n);
|
|
44
|
-
case 'DOUBLE':
|
|
45
|
-
return new Float64Array(buffer.buffer, 0, n);
|
|
46
|
-
case 'VARCHAR': // we already have created a string array on the C++ side for this
|
|
47
|
-
return buffer;
|
|
48
|
-
default:
|
|
49
|
-
return new Array<string>(0); // cough
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
26
|
// this follows the wasm udfs somewhat but is simpler because we can pass data much more cleanly
|
|
54
27
|
Connection.prototype.register = function(name, return_type, fun) {
|
|
55
28
|
// TODO what if this throws an error somewhere? do we need a try/catch?
|
|
56
|
-
return this.register_bulk(name, return_type, function(
|
|
29
|
+
return this.register_bulk(name, return_type, function(desc) {
|
|
57
30
|
try {
|
|
58
|
-
|
|
59
|
-
const
|
|
31
|
+
// Build an argument resolver
|
|
32
|
+
const buildResolver = (arg) => {
|
|
33
|
+
let validity = arg.validity || null;
|
|
34
|
+
switch (arg.physicalType) {
|
|
35
|
+
case 'STRUCT': {
|
|
36
|
+
const tmp = {};
|
|
37
|
+
const children = [];
|
|
38
|
+
for (let j = 0; j < (arg.children.length || 0); ++j) {
|
|
39
|
+
const attr = arg.children[j];
|
|
40
|
+
const child = buildResolver(attr);
|
|
41
|
+
children.push((row) => {
|
|
42
|
+
tmp[attr.name] = child(row);
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (validity != null) {
|
|
46
|
+
return (row) => {
|
|
47
|
+
if (!validity[row]) {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
for (const resolver of children) {
|
|
51
|
+
resolver(row);
|
|
52
|
+
}
|
|
53
|
+
return tmp;
|
|
54
|
+
};
|
|
55
|
+
} else {
|
|
56
|
+
return (row) => {
|
|
57
|
+
for (const resolver of children) {
|
|
58
|
+
resolver(row);
|
|
59
|
+
}
|
|
60
|
+
return tmp;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
default: {
|
|
65
|
+
if (arg.data === undefined) {
|
|
66
|
+
throw new Error(
|
|
67
|
+
'malformed data view, expected data buffer for argument of type: ' + arg.physicalType,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
70
|
+
const data = arg.data;
|
|
71
|
+
if (validity != null) {
|
|
72
|
+
return (row) => (!validity[row] ? null : data[row]);
|
|
73
|
+
} else {
|
|
74
|
+
return (row) => data[row];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
};
|
|
60
79
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
80
|
+
// Translate argument data
|
|
81
|
+
const argResolvers = [];
|
|
82
|
+
for (let i = 0; i < desc.args.length; ++i) {
|
|
83
|
+
argResolvers.push(buildResolver(desc.args[i]));
|
|
84
|
+
}
|
|
85
|
+
const args = [];
|
|
86
|
+
for (let i = 0; i < desc.args.length; ++i) {
|
|
87
|
+
args.push(null);
|
|
65
88
|
}
|
|
66
89
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
for (let i = 0; i < descr.rows; ++i) {
|
|
73
|
-
const res = fun();
|
|
74
|
-
out_data[i] = res;
|
|
75
|
-
out_validity[i] = res == undefined || res == null ? 0 : 1;
|
|
76
|
-
}
|
|
90
|
+
// Return type
|
|
91
|
+
desc.ret.validity = new Uint8Array(desc.rows);
|
|
92
|
+
switch (desc.ret.physicalType) {
|
|
93
|
+
case 'INT8':
|
|
94
|
+
desc.ret.data = new Int8Array(desc.rows);
|
|
77
95
|
break;
|
|
78
|
-
case
|
|
79
|
-
|
|
80
|
-
const res = fun(validity_arr[0][i] ? data_arr[0][i] : undefined);
|
|
81
|
-
out_data[i] = res;
|
|
82
|
-
out_validity[i] = res == undefined || res == null ? 0 : 1;
|
|
83
|
-
}
|
|
96
|
+
case 'INT16':
|
|
97
|
+
desc.ret.data = new Int16Array(desc.rows);
|
|
84
98
|
break;
|
|
85
|
-
case
|
|
86
|
-
|
|
87
|
-
const res = fun(validity_arr[0][i] ? data_arr[0][i] : undefined, validity_arr[1][i] ? data_arr[1][i] : undefined);
|
|
88
|
-
out_data[i] = res;
|
|
89
|
-
out_validity[i] = res == undefined || res == null ? 0 : 1;
|
|
90
|
-
}
|
|
99
|
+
case 'INT32':
|
|
100
|
+
desc.ret.data = new Int32Array(desc.rows);
|
|
91
101
|
break;
|
|
92
|
-
case
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
102
|
+
case 'DOUBLE':
|
|
103
|
+
desc.ret.data = new Float64Array(desc.rows);
|
|
104
|
+
break;
|
|
105
|
+
case 'DATE64':
|
|
106
|
+
case 'TIME64':
|
|
107
|
+
case 'TIMESTAMP':
|
|
108
|
+
case 'INT64':
|
|
109
|
+
desc.ret.data = new BigInt64Array(desc.rows);
|
|
98
110
|
break;
|
|
99
|
-
|
|
100
|
-
|
|
111
|
+
case 'UINT64':
|
|
112
|
+
desc.ret.data = new BigUint64Array(desc.rows);
|
|
113
|
+
break;
|
|
114
|
+
case 'BLOB':
|
|
115
|
+
case 'VARCHAR':
|
|
116
|
+
desc.ret.data = new Array(desc.rows);
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Call the function
|
|
121
|
+
for (let i = 0; i < desc.rows; ++i) {
|
|
122
|
+
for (let j = 0; j < desc.args.length; ++j) {
|
|
123
|
+
args[j] = argResolvers[j](i);
|
|
124
|
+
}
|
|
125
|
+
const res = fun(...args);
|
|
126
|
+
desc.ret.data[i] = res;
|
|
127
|
+
desc.ret.validity[i] = res === undefined || res === null ? 0 : 1;
|
|
101
128
|
}
|
|
102
129
|
} catch(error) { // work around recently fixed napi bug https://github.com/nodejs/node-addon-api/issues/912
|
|
130
|
+
console.log(desc.ret);
|
|
103
131
|
msg = error;
|
|
104
132
|
if (typeof error == 'object' && 'message' in error) {
|
|
105
133
|
msg = error.message
|
package/package.json
CHANGED
package/src/connection.cpp
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
#include "duckdb.hpp"
|
|
1
2
|
#include "duckdb_node.hpp"
|
|
3
|
+
#include "napi.h"
|
|
2
4
|
|
|
5
|
+
#include <iostream>
|
|
3
6
|
#include <thread>
|
|
4
7
|
|
|
5
8
|
namespace node_duckdb {
|
|
@@ -23,7 +26,7 @@ Napi::Object Connection::Init(Napi::Env env, Napi::Object exports) {
|
|
|
23
26
|
}
|
|
24
27
|
|
|
25
28
|
struct ConnectTask : public Task {
|
|
26
|
-
ConnectTask(Connection &
|
|
29
|
+
ConnectTask(Connection &connection, Napi::Function callback) : Task(connection, callback) {
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
void DoWork() override {
|
|
@@ -78,91 +81,25 @@ struct JSArgs {
|
|
|
78
81
|
std::string error;
|
|
79
82
|
};
|
|
80
83
|
|
|
81
|
-
|
|
82
|
-
Napi::EscapableHandleScope scope(env);
|
|
83
|
-
|
|
84
|
-
Napi::Array data_buffers(Napi::Array::New(env));
|
|
85
|
-
|
|
86
|
-
auto data_buffer_index = -1;
|
|
87
|
-
auto length_buffer_index = -1;
|
|
88
|
-
auto validity_buffer_index = -1;
|
|
89
|
-
|
|
90
|
-
auto validity_buffer = Napi::Buffer<uint8_t>::New(env, rows);
|
|
91
|
-
auto validity = duckdb::FlatVector::Validity(vec);
|
|
92
|
-
auto validity_ptr = validity_buffer.Data();
|
|
93
|
-
for (duckdb::idx_t row_idx = 0; row_idx < rows; row_idx++) {
|
|
94
|
-
validity_ptr[row_idx] = validity.RowIsValid(row_idx);
|
|
95
|
-
}
|
|
96
|
-
data_buffers.Set(data_buffers.Length(), validity_buffer);
|
|
97
|
-
validity_buffer_index = data_buffers.Length() - 1;
|
|
98
|
-
|
|
99
|
-
auto &vec_type = vec.GetType();
|
|
100
|
-
|
|
101
|
-
switch (vec_type.id()) {
|
|
102
|
-
case duckdb::LogicalTypeId::BOOLEAN:
|
|
103
|
-
case duckdb::LogicalTypeId::UTINYINT:
|
|
104
|
-
case duckdb::LogicalTypeId::TINYINT:
|
|
105
|
-
case duckdb::LogicalTypeId::USMALLINT:
|
|
106
|
-
case duckdb::LogicalTypeId::SMALLINT:
|
|
107
|
-
case duckdb::LogicalTypeId::INTEGER:
|
|
108
|
-
case duckdb::LogicalTypeId::UINTEGER:
|
|
109
|
-
case duckdb::LogicalTypeId::FLOAT:
|
|
110
|
-
case duckdb::LogicalTypeId::DOUBLE: {
|
|
111
|
-
auto data_buffer = Napi::Buffer<uint8_t>::New(env, rows * duckdb::GetTypeIdSize(vec_type.InternalType()));
|
|
112
|
-
// TODO this is technically not neccessary but it fixes a crash in Node 14. Some weird data ownership issue.
|
|
113
|
-
memcpy(data_buffer.Data(), duckdb::FlatVector::GetData<uint8_t>(vec),
|
|
114
|
-
rows * duckdb::GetTypeIdSize(vec_type.InternalType()));
|
|
115
|
-
|
|
116
|
-
data_buffers.Set(data_buffers.Length(), data_buffer);
|
|
117
|
-
data_buffer_index = data_buffers.Length() - 1;
|
|
118
|
-
break;
|
|
119
|
-
}
|
|
120
|
-
case duckdb::LogicalTypeId::VARCHAR: {
|
|
121
|
-
Napi::Array string_buffers(Napi::Array::New(env, rows));
|
|
122
|
-
if (copy) {
|
|
123
|
-
auto string_vec_ptr = duckdb::FlatVector::GetData<duckdb::string_t>(vec);
|
|
124
|
-
for (duckdb::idx_t row_idx = 0; row_idx < rows; row_idx++) {
|
|
125
|
-
string_buffers.Set(row_idx, validity_ptr[row_idx]
|
|
126
|
-
? Napi::String::New(env, string_vec_ptr[row_idx].GetDataUnsafe(),
|
|
127
|
-
string_vec_ptr[row_idx].GetSize())
|
|
128
|
-
: Napi::Value());
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
data_buffers.Set(data_buffers.Length(), string_buffers);
|
|
132
|
-
data_buffer_index = data_buffers.Length() - 1;
|
|
133
|
-
break;
|
|
134
|
-
}
|
|
135
|
-
default:
|
|
136
|
-
throw duckdb::NotImplementedException(vec_type.ToString());
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
Napi::Object desc(Napi::Object::New(env));
|
|
140
|
-
desc.Set("logical_type", vec_type.ToString());
|
|
141
|
-
desc.Set("physical_type", TypeIdToString(vec_type.InternalType()));
|
|
142
|
-
desc.Set("validity_buffer", validity_buffer_index);
|
|
143
|
-
desc.Set("data_buffer", data_buffer_index);
|
|
144
|
-
desc.Set("length_buffer", length_buffer_index);
|
|
145
|
-
desc.Set("data_buffers", data_buffers);
|
|
146
|
-
|
|
147
|
-
return scope.Escape(desc);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, nullptr_t *, JSArgs *jsargs) {
|
|
84
|
+
void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, std::nullptr_t *, JSArgs *jsargs) {
|
|
151
85
|
try { // if we dont catch exceptions here we terminate node if one happens ^^
|
|
86
|
+
Napi::EscapableHandleScope scope(env);
|
|
152
87
|
|
|
153
|
-
//
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
auto arg_descr = transform_vector(env, vec, jsargs->rows, true);
|
|
158
|
-
args_descr.Set(col_idx, arg_descr);
|
|
159
|
-
}
|
|
160
|
-
auto ret_descr = transform_vector(env, *jsargs->result, jsargs->rows, false);
|
|
161
|
-
|
|
162
|
-
Napi::Object descr(Napi::Object::New(env));
|
|
88
|
+
// Set up descriptor and data arrays
|
|
89
|
+
auto descr = Napi::Object::New(env);
|
|
90
|
+
auto chunk = EncodeDataChunk(env, *jsargs->args, true, true);
|
|
91
|
+
descr.Set("args", scope.Escape(chunk));
|
|
163
92
|
descr.Set("rows", jsargs->rows);
|
|
164
|
-
|
|
165
|
-
|
|
93
|
+
auto ret = Napi::Object::New(env);
|
|
94
|
+
ret.Set("sqlType", jsargs->result->GetType().ToString());
|
|
95
|
+
auto ret_type = jsargs->result->GetType().InternalType();
|
|
96
|
+
#if NAPI_VERSION <= 5
|
|
97
|
+
if (ret_type == duckdb::PhysicalType::INT64 || ret_type == duckdb::PhysicalType::UINT64) {
|
|
98
|
+
ret_type = duckdb::PhysicalType::DOUBLE;
|
|
99
|
+
}
|
|
100
|
+
#endif
|
|
101
|
+
ret.Set("physicalType", TypeIdToString(ret_type));
|
|
102
|
+
descr.Set("ret", ret);
|
|
166
103
|
|
|
167
104
|
// actually call the UDF, or rather its vectorized wrapper from duckdb.js/Connection.prototype.register wrapper
|
|
168
105
|
jsudf({descr});
|
|
@@ -181,39 +118,75 @@ void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, nullptr_t *, JSA
|
|
|
181
118
|
}
|
|
182
119
|
|
|
183
120
|
// transform the result back to a vector
|
|
184
|
-
auto return_validity =
|
|
185
|
-
.Get("data_buffers")
|
|
186
|
-
.ToObject()
|
|
187
|
-
.Get(ret_descr.ToObject().Get("validity_buffer"))
|
|
188
|
-
.As<Napi::Buffer<uint8_t>>();
|
|
121
|
+
auto return_validity = ret.ToObject().Get("validity").As<Napi::Uint8Array>();
|
|
189
122
|
for (duckdb::idx_t row_idx = 0; row_idx < jsargs->rows; row_idx++) {
|
|
190
123
|
duckdb::FlatVector::SetNull(*jsargs->result, row_idx, !return_validity[row_idx]);
|
|
191
124
|
}
|
|
192
125
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
126
|
+
switch (jsargs->result->GetType().id()) {
|
|
127
|
+
case duckdb::LogicalTypeId::TINYINT: {
|
|
128
|
+
auto data = ret.Get("data").As<Napi::Int8Array>();
|
|
129
|
+
auto out = duckdb::FlatVector::GetData<int8_t>(*jsargs->result);
|
|
130
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
131
|
+
break;
|
|
132
|
+
}
|
|
133
|
+
case duckdb::LogicalTypeId::SMALLINT: {
|
|
134
|
+
auto data = ret.Get("data").As<Napi::Int16Array>();
|
|
135
|
+
auto out = duckdb::FlatVector::GetData<int16_t>(*jsargs->result);
|
|
136
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
case duckdb::LogicalTypeId::INTEGER: {
|
|
140
|
+
auto data = ret.Get("data").As<Napi::Int32Array>();
|
|
141
|
+
auto out = duckdb::FlatVector::GetData<int32_t>(*jsargs->result);
|
|
142
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
case duckdb::LogicalTypeId::DOUBLE: {
|
|
146
|
+
auto data = ret.Get("data").As<Napi::Float64Array>();
|
|
147
|
+
auto out = duckdb::FlatVector::GetData<double>(*jsargs->result);
|
|
148
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
149
|
+
break;
|
|
150
|
+
}
|
|
151
|
+
case duckdb::LogicalTypeId::TIME:
|
|
152
|
+
case duckdb::LogicalTypeId::TIMESTAMP:
|
|
153
|
+
case duckdb::LogicalTypeId::TIMESTAMP_MS:
|
|
154
|
+
case duckdb::LogicalTypeId::TIMESTAMP_SEC:
|
|
155
|
+
case duckdb::LogicalTypeId::TIMESTAMP_NS:
|
|
156
|
+
case duckdb::LogicalTypeId::BIGINT: {
|
|
157
|
+
#if NAPI_VERSION > 5
|
|
158
|
+
auto data = ret.Get("data").As<Napi::BigInt64Array>();
|
|
159
|
+
#else
|
|
160
|
+
auto data = ret.Get("data").As<Napi::Float64Array>();
|
|
161
|
+
#endif
|
|
162
|
+
auto out = duckdb::FlatVector::GetData<int64_t>(*jsargs->result);
|
|
163
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case duckdb::LogicalTypeId::UBIGINT: {
|
|
167
|
+
#if NAPI_VERSION > 5
|
|
168
|
+
auto data = ret.Get("data").As<Napi::BigUint64Array>();
|
|
169
|
+
#else
|
|
170
|
+
auto data = ret.Get("data").As<Napi::Float64Array>();
|
|
171
|
+
#endif
|
|
172
|
+
auto out = duckdb::FlatVector::GetData<uint64_t>(*jsargs->result);
|
|
173
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
174
|
+
break;
|
|
175
|
+
}
|
|
176
|
+
case duckdb::LogicalTypeId::BLOB:
|
|
177
|
+
case duckdb::LogicalTypeId::VARCHAR: {
|
|
178
|
+
auto data = ret.Get("data").As<Napi::Array>();
|
|
179
|
+
auto out = duckdb::FlatVector::GetData<duckdb::string_t>(*jsargs->result);
|
|
180
|
+
for (size_t i = 0; i < data.Length(); ++i) {
|
|
181
|
+
out[i] = duckdb::string_t(data.Get(i).ToString());
|
|
182
|
+
}
|
|
183
|
+
break;
|
|
184
|
+
}
|
|
185
|
+
default: {
|
|
201
186
|
for (duckdb::idx_t row_idx = 0; row_idx < jsargs->rows; row_idx++) {
|
|
202
|
-
|
|
203
|
-
duckdb::FlatVector::SetNull(*jsargs->result, row_idx, true);
|
|
204
|
-
} else {
|
|
205
|
-
auto str = return_string_array.Get(row_idx).As<Napi::String>();
|
|
206
|
-
return_string_vec_ptr[row_idx] = duckdb::StringVector::AddString(*jsargs->result, str);
|
|
207
|
-
}
|
|
187
|
+
duckdb::FlatVector::SetNull(*jsargs->result, row_idx, false);
|
|
208
188
|
}
|
|
209
|
-
}
|
|
210
|
-
auto return_data = ret_descr.ToObject()
|
|
211
|
-
.Get("data_buffers")
|
|
212
|
-
.ToObject()
|
|
213
|
-
.Get(ret_descr.ToObject().Get("data_buffer"))
|
|
214
|
-
.As<Napi::Buffer<uint8_t>>();
|
|
215
|
-
memcpy(duckdb::FlatVector::GetData<uint8_t>(*jsargs->result), return_data.Data(),
|
|
216
|
-
jsargs->rows * duckdb::GetTypeIdSize(jsargs->result->GetType().InternalType()));
|
|
189
|
+
}
|
|
217
190
|
}
|
|
218
191
|
} catch (const std::exception &e) {
|
|
219
192
|
jsargs->error = e.what();
|
|
@@ -222,8 +195,8 @@ void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, nullptr_t *, JSA
|
|
|
222
195
|
}
|
|
223
196
|
|
|
224
197
|
struct RegisterTask : public Task {
|
|
225
|
-
RegisterTask(Connection &
|
|
226
|
-
: Task(
|
|
198
|
+
RegisterTask(Connection &connection, std::string name, std::string return_type_name, Napi::Function callback)
|
|
199
|
+
: Task(connection, callback), name(std::move(name)), return_type_name(std::move(return_type_name)) {
|
|
227
200
|
}
|
|
228
201
|
|
|
229
202
|
void DoWork() override {
|
|
@@ -234,7 +207,7 @@ struct RegisterTask : public Task {
|
|
|
234
207
|
// here we can do only DuckDB stuff because we do not have a functioning env
|
|
235
208
|
|
|
236
209
|
// Flatten all args to simplify udfs
|
|
237
|
-
args.
|
|
210
|
+
args.Flatten();
|
|
238
211
|
|
|
239
212
|
JSArgs jsargs;
|
|
240
213
|
jsargs.rows = args.size();
|
|
@@ -282,8 +255,8 @@ Napi::Value Connection::Register(const Napi::CallbackInfo &info) {
|
|
|
282
255
|
return env.Null();
|
|
283
256
|
}
|
|
284
257
|
|
|
285
|
-
auto udf =
|
|
286
|
-
|
|
258
|
+
auto udf = duckdb_node_udf_function_t::New(env, udf_callback, "duckdb_node_udf" + name, 0, 1, nullptr,
|
|
259
|
+
[](Napi::Env, void *, std::nullptr_t *ctx) {});
|
|
287
260
|
|
|
288
261
|
// we have to unref the udf because otherwise there is a circular ref with the connection somehow(?)
|
|
289
262
|
// this took far too long to figure out
|
|
@@ -297,8 +270,8 @@ Napi::Value Connection::Register(const Napi::CallbackInfo &info) {
|
|
|
297
270
|
}
|
|
298
271
|
|
|
299
272
|
struct UnregisterTask : public Task {
|
|
300
|
-
UnregisterTask(Connection &
|
|
301
|
-
: Task(
|
|
273
|
+
UnregisterTask(Connection &connection, std::string name, Napi::Function callback)
|
|
274
|
+
: Task(connection, callback), name(std::move(name)) {
|
|
302
275
|
}
|
|
303
276
|
|
|
304
277
|
void DoWork() override {
|
|
@@ -340,27 +313,33 @@ Napi::Value Connection::Unregister(const Napi::CallbackInfo &info) {
|
|
|
340
313
|
}
|
|
341
314
|
|
|
342
315
|
struct ExecTask : public Task {
|
|
343
|
-
ExecTask(Connection &
|
|
344
|
-
: Task(
|
|
316
|
+
ExecTask(Connection &connection, std::string sql, Napi::Function callback)
|
|
317
|
+
: Task(connection, callback), sql(std::move(sql)) {
|
|
345
318
|
}
|
|
346
319
|
|
|
347
320
|
void DoWork() override {
|
|
348
321
|
auto &connection = Get<Connection>();
|
|
349
322
|
|
|
350
323
|
success = true;
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
324
|
+
try {
|
|
325
|
+
auto statements = connection.connection->ExtractStatements(sql);
|
|
326
|
+
if (statements.empty()) {
|
|
327
|
+
return;
|
|
328
|
+
}
|
|
355
329
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
330
|
+
// thanks Mark
|
|
331
|
+
for (duckdb::idx_t i = 0; i < statements.size(); i++) {
|
|
332
|
+
auto res = connection.connection->Query(move(statements[i]));
|
|
333
|
+
if (!res->success) {
|
|
334
|
+
success = false;
|
|
335
|
+
error = res->error;
|
|
336
|
+
break;
|
|
337
|
+
}
|
|
363
338
|
}
|
|
339
|
+
} catch (duckdb::ParserException &e) {
|
|
340
|
+
success = false;
|
|
341
|
+
error = e.what();
|
|
342
|
+
return;
|
|
364
343
|
}
|
|
365
344
|
}
|
|
366
345
|
|