duckdb 0.4.1-dev98.0 → 0.5.1-dev5.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 +423 -80
- package/package.json +3 -1
- package/src/connection.cpp +137 -136
- package/src/data_chunk.cpp +185 -0
- package/src/database.cpp +73 -23
- package/src/duckdb.cpp +73720 -30454
- package/src/duckdb.hpp +10403 -6269
- package/src/duckdb_node.cpp +1 -0
- package/src/duckdb_node.hpp +57 -18
- package/src/parquet-amalgamation.cpp +37729 -37065
- package/src/parquet-amalgamation.hpp +112 -114
- package/src/statement.cpp +163 -34
- package/test/data_type_support.test.js +110 -13
- package/test/extension.test.js +5 -1
- package/test/jsdoc.test.js +60 -0
- package/test/query_result.test.js +23 -0
- package/test/syntax_error.test.js +16 -0
- package/test/udf.test.js +172 -107
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,13 +26,34 @@ 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 {
|
|
30
33
|
auto &connection = Get<Connection>();
|
|
34
|
+
if (!connection.database_ref || !connection.database_ref->database) {
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
31
37
|
connection.connection = duckdb::make_unique<duckdb::Connection>(*connection.database_ref->database);
|
|
38
|
+
success = true;
|
|
39
|
+
}
|
|
40
|
+
void Callback() override {
|
|
41
|
+
auto &connection = Get<Connection>();
|
|
42
|
+
Napi::Env env = connection.Env();
|
|
43
|
+
|
|
44
|
+
std::vector<napi_value> args;
|
|
45
|
+
if (!success) {
|
|
46
|
+
args.push_back(Utils::CreateError(env, "Invalid database object"));
|
|
47
|
+
} else {
|
|
48
|
+
args.push_back(env.Null());
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
Napi::HandleScope scope(env);
|
|
52
|
+
|
|
53
|
+
callback.Value().MakeCallback(connection.Value(), args);
|
|
32
54
|
}
|
|
55
|
+
|
|
56
|
+
bool success = false;
|
|
33
57
|
};
|
|
34
58
|
|
|
35
59
|
Connection::Connection(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Connection>(info) {
|
|
@@ -75,94 +99,28 @@ struct JSArgs {
|
|
|
75
99
|
duckdb::DataChunk *args;
|
|
76
100
|
duckdb::Vector *result;
|
|
77
101
|
bool done;
|
|
78
|
-
|
|
102
|
+
duckdb::PreservedError error;
|
|
79
103
|
};
|
|
80
104
|
|
|
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) {
|
|
105
|
+
void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, std::nullptr_t *, JSArgs *jsargs) {
|
|
151
106
|
try { // if we dont catch exceptions here we terminate node if one happens ^^
|
|
107
|
+
Napi::EscapableHandleScope scope(env);
|
|
152
108
|
|
|
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));
|
|
109
|
+
// Set up descriptor and data arrays
|
|
110
|
+
auto descr = Napi::Object::New(env);
|
|
111
|
+
auto chunk = EncodeDataChunk(env, *jsargs->args, true, true);
|
|
112
|
+
descr.Set("args", scope.Escape(chunk));
|
|
163
113
|
descr.Set("rows", jsargs->rows);
|
|
164
|
-
|
|
165
|
-
|
|
114
|
+
auto ret = Napi::Object::New(env);
|
|
115
|
+
ret.Set("sqlType", jsargs->result->GetType().ToString());
|
|
116
|
+
auto ret_type = jsargs->result->GetType().InternalType();
|
|
117
|
+
#if NAPI_VERSION <= 5
|
|
118
|
+
if (ret_type == duckdb::PhysicalType::INT64 || ret_type == duckdb::PhysicalType::UINT64) {
|
|
119
|
+
ret_type = duckdb::PhysicalType::DOUBLE;
|
|
120
|
+
}
|
|
121
|
+
#endif
|
|
122
|
+
ret.Set("physicalType", TypeIdToString(ret_type));
|
|
123
|
+
descr.Set("ret", ret);
|
|
166
124
|
|
|
167
125
|
// actually call the UDF, or rather its vectorized wrapper from duckdb.js/Connection.prototype.register wrapper
|
|
168
126
|
jsudf({descr});
|
|
@@ -181,49 +139,87 @@ void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, nullptr_t *, JSA
|
|
|
181
139
|
}
|
|
182
140
|
|
|
183
141
|
// 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>>();
|
|
142
|
+
auto return_validity = ret.ToObject().Get("validity").As<Napi::Uint8Array>();
|
|
189
143
|
for (duckdb::idx_t row_idx = 0; row_idx < jsargs->rows; row_idx++) {
|
|
190
144
|
duckdb::FlatVector::SetNull(*jsargs->result, row_idx, !return_validity[row_idx]);
|
|
191
145
|
}
|
|
192
146
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
147
|
+
switch (jsargs->result->GetType().id()) {
|
|
148
|
+
case duckdb::LogicalTypeId::TINYINT: {
|
|
149
|
+
auto data = ret.Get("data").As<Napi::Int8Array>();
|
|
150
|
+
auto out = duckdb::FlatVector::GetData<int8_t>(*jsargs->result);
|
|
151
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
case duckdb::LogicalTypeId::SMALLINT: {
|
|
155
|
+
auto data = ret.Get("data").As<Napi::Int16Array>();
|
|
156
|
+
auto out = duckdb::FlatVector::GetData<int16_t>(*jsargs->result);
|
|
157
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case duckdb::LogicalTypeId::INTEGER: {
|
|
161
|
+
auto data = ret.Get("data").As<Napi::Int32Array>();
|
|
162
|
+
auto out = duckdb::FlatVector::GetData<int32_t>(*jsargs->result);
|
|
163
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
164
|
+
break;
|
|
165
|
+
}
|
|
166
|
+
case duckdb::LogicalTypeId::DOUBLE: {
|
|
167
|
+
auto data = ret.Get("data").As<Napi::Float64Array>();
|
|
168
|
+
auto out = duckdb::FlatVector::GetData<double>(*jsargs->result);
|
|
169
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
170
|
+
break;
|
|
171
|
+
}
|
|
172
|
+
case duckdb::LogicalTypeId::TIME:
|
|
173
|
+
case duckdb::LogicalTypeId::TIMESTAMP:
|
|
174
|
+
case duckdb::LogicalTypeId::TIMESTAMP_MS:
|
|
175
|
+
case duckdb::LogicalTypeId::TIMESTAMP_SEC:
|
|
176
|
+
case duckdb::LogicalTypeId::TIMESTAMP_NS:
|
|
177
|
+
case duckdb::LogicalTypeId::BIGINT: {
|
|
178
|
+
#if NAPI_VERSION > 5
|
|
179
|
+
auto data = ret.Get("data").As<Napi::BigInt64Array>();
|
|
180
|
+
#else
|
|
181
|
+
auto data = ret.Get("data").As<Napi::Float64Array>();
|
|
182
|
+
#endif
|
|
183
|
+
auto out = duckdb::FlatVector::GetData<int64_t>(*jsargs->result);
|
|
184
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
185
|
+
break;
|
|
186
|
+
}
|
|
187
|
+
case duckdb::LogicalTypeId::UBIGINT: {
|
|
188
|
+
#if NAPI_VERSION > 5
|
|
189
|
+
auto data = ret.Get("data").As<Napi::BigUint64Array>();
|
|
190
|
+
#else
|
|
191
|
+
auto data = ret.Get("data").As<Napi::Float64Array>();
|
|
192
|
+
#endif
|
|
193
|
+
auto out = duckdb::FlatVector::GetData<uint64_t>(*jsargs->result);
|
|
194
|
+
memcpy(out, data.Data(), jsargs->rows * duckdb::GetTypeIdSize(ret_type));
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
case duckdb::LogicalTypeId::BLOB:
|
|
198
|
+
case duckdb::LogicalTypeId::VARCHAR: {
|
|
199
|
+
auto data = ret.Get("data").As<Napi::Array>();
|
|
200
|
+
auto out = duckdb::FlatVector::GetData<duckdb::string_t>(*jsargs->result);
|
|
201
|
+
for (size_t i = 0; i < data.Length(); ++i) {
|
|
202
|
+
out[i] = duckdb::string_t(data.Get(i).ToString());
|
|
203
|
+
}
|
|
204
|
+
break;
|
|
205
|
+
}
|
|
206
|
+
default: {
|
|
201
207
|
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
|
-
}
|
|
208
|
+
duckdb::FlatVector::SetNull(*jsargs->result, row_idx, false);
|
|
208
209
|
}
|
|
209
|
-
} else {
|
|
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()));
|
|
217
210
|
}
|
|
211
|
+
}
|
|
212
|
+
} catch (const duckdb::Exception &e) {
|
|
213
|
+
jsargs->error = duckdb::PreservedError(e);
|
|
218
214
|
} catch (const std::exception &e) {
|
|
219
|
-
jsargs->error = e
|
|
215
|
+
jsargs->error = duckdb::PreservedError(e);
|
|
220
216
|
}
|
|
221
217
|
jsargs->done = true;
|
|
222
218
|
}
|
|
223
219
|
|
|
224
220
|
struct RegisterTask : public Task {
|
|
225
|
-
RegisterTask(Connection &
|
|
226
|
-
: Task(
|
|
221
|
+
RegisterTask(Connection &connection, std::string name, std::string return_type_name, Napi::Function callback)
|
|
222
|
+
: Task(connection, callback), name(std::move(name)), return_type_name(std::move(return_type_name)) {
|
|
227
223
|
}
|
|
228
224
|
|
|
229
225
|
void DoWork() override {
|
|
@@ -234,7 +230,7 @@ struct RegisterTask : public Task {
|
|
|
234
230
|
// here we can do only DuckDB stuff because we do not have a functioning env
|
|
235
231
|
|
|
236
232
|
// Flatten all args to simplify udfs
|
|
237
|
-
args.
|
|
233
|
+
args.Flatten();
|
|
238
234
|
|
|
239
235
|
JSArgs jsargs;
|
|
240
236
|
jsargs.rows = args.size();
|
|
@@ -246,8 +242,8 @@ struct RegisterTask : public Task {
|
|
|
246
242
|
while (!jsargs.done) {
|
|
247
243
|
std::this_thread::yield();
|
|
248
244
|
}
|
|
249
|
-
if (
|
|
250
|
-
|
|
245
|
+
if (jsargs.error) {
|
|
246
|
+
jsargs.error.Throw();
|
|
251
247
|
}
|
|
252
248
|
};
|
|
253
249
|
|
|
@@ -282,8 +278,8 @@ Napi::Value Connection::Register(const Napi::CallbackInfo &info) {
|
|
|
282
278
|
return env.Null();
|
|
283
279
|
}
|
|
284
280
|
|
|
285
|
-
auto udf =
|
|
286
|
-
|
|
281
|
+
auto udf = duckdb_node_udf_function_t::New(env, udf_callback, "duckdb_node_udf" + name, 0, 1, nullptr,
|
|
282
|
+
[](Napi::Env, void *, std::nullptr_t *ctx) {});
|
|
287
283
|
|
|
288
284
|
// we have to unref the udf because otherwise there is a circular ref with the connection somehow(?)
|
|
289
285
|
// this took far too long to figure out
|
|
@@ -297,8 +293,8 @@ Napi::Value Connection::Register(const Napi::CallbackInfo &info) {
|
|
|
297
293
|
}
|
|
298
294
|
|
|
299
295
|
struct UnregisterTask : public Task {
|
|
300
|
-
UnregisterTask(Connection &
|
|
301
|
-
: Task(
|
|
296
|
+
UnregisterTask(Connection &connection, std::string name, Napi::Function callback)
|
|
297
|
+
: Task(connection, callback), name(std::move(name)) {
|
|
302
298
|
}
|
|
303
299
|
|
|
304
300
|
void DoWork() override {
|
|
@@ -340,39 +336,44 @@ Napi::Value Connection::Unregister(const Napi::CallbackInfo &info) {
|
|
|
340
336
|
}
|
|
341
337
|
|
|
342
338
|
struct ExecTask : public Task {
|
|
343
|
-
ExecTask(Connection &
|
|
344
|
-
: Task(
|
|
339
|
+
ExecTask(Connection &connection, std::string sql, Napi::Function callback)
|
|
340
|
+
: Task(connection, callback), sql(std::move(sql)) {
|
|
345
341
|
}
|
|
346
342
|
|
|
347
343
|
void DoWork() override {
|
|
348
344
|
auto &connection = Get<Connection>();
|
|
349
345
|
|
|
350
346
|
success = true;
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
347
|
+
try {
|
|
348
|
+
auto statements = connection.connection->ExtractStatements(sql);
|
|
349
|
+
if (statements.empty()) {
|
|
350
|
+
return;
|
|
351
|
+
}
|
|
355
352
|
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
353
|
+
for (duckdb::idx_t i = 0; i < statements.size(); i++) {
|
|
354
|
+
auto res = connection.connection->Query(move(statements[i]));
|
|
355
|
+
if (res->HasError()) {
|
|
356
|
+
success = false;
|
|
357
|
+
error = res->GetErrorObject();
|
|
358
|
+
break;
|
|
359
|
+
}
|
|
363
360
|
}
|
|
361
|
+
} catch (duckdb::ParserException &e) {
|
|
362
|
+
success = false;
|
|
363
|
+
error = duckdb::PreservedError(e);
|
|
364
|
+
return;
|
|
364
365
|
}
|
|
365
366
|
}
|
|
366
367
|
|
|
367
368
|
void Callback() override {
|
|
368
369
|
auto env = object.Env();
|
|
369
370
|
Napi::HandleScope scope(env);
|
|
370
|
-
callback.Value().MakeCallback(object.Value(), {success ? env.Null() : Napi::String::New(env, error)});
|
|
371
|
+
callback.Value().MakeCallback(object.Value(), {success ? env.Null() : Napi::String::New(env, error.Message())});
|
|
371
372
|
};
|
|
372
373
|
|
|
373
374
|
std::string sql;
|
|
374
375
|
bool success;
|
|
375
|
-
|
|
376
|
+
duckdb::PreservedError error;
|
|
376
377
|
};
|
|
377
378
|
|
|
378
379
|
Napi::Value Connection::Exec(const Napi::CallbackInfo &info) {
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#include "duckdb.hpp"
|
|
2
|
+
#include "duckdb_node.hpp"
|
|
3
|
+
#include "napi.h"
|
|
4
|
+
|
|
5
|
+
#include <thread>
|
|
6
|
+
|
|
7
|
+
namespace node_duckdb {
|
|
8
|
+
|
|
9
|
+
Napi::Array EncodeDataChunk(Napi::Env env, duckdb::DataChunk &chunk, bool with_types, bool with_data) {
|
|
10
|
+
Napi::Array col_descs(Napi::Array::New(env, chunk.ColumnCount()));
|
|
11
|
+
for (idx_t col_idx = 0; col_idx < chunk.ColumnCount(); col_idx++) {
|
|
12
|
+
auto col_desc = Napi::Object::New(env);
|
|
13
|
+
|
|
14
|
+
// Make sure we only have flat vectors hereafter (for now)
|
|
15
|
+
auto &chunk_vec = chunk.data[col_idx];
|
|
16
|
+
if (with_data) {
|
|
17
|
+
chunk_vec.Flatten(chunk.size());
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// Do a post-order DFS traversal
|
|
21
|
+
std::vector<std::tuple<bool, duckdb::Vector *, Napi::Object, size_t, size_t>> pending;
|
|
22
|
+
pending.emplace_back(false, &chunk_vec, Napi::Object::New(env), 0, 0);
|
|
23
|
+
|
|
24
|
+
while (!pending.empty()) {
|
|
25
|
+
// Unpack DFS node
|
|
26
|
+
auto &back = pending.back();
|
|
27
|
+
auto &visited = std::get<0>(back);
|
|
28
|
+
auto &vec = std::get<1>(back);
|
|
29
|
+
auto &desc = std::get<2>(back);
|
|
30
|
+
auto &parent_idx = std::get<3>(back);
|
|
31
|
+
auto &idx_in_parent = std::get<4>(back);
|
|
32
|
+
|
|
33
|
+
// Already visited?
|
|
34
|
+
if (visited) {
|
|
35
|
+
if (pending.size() == 1) {
|
|
36
|
+
col_desc = desc;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
std::get<2>(pending[parent_idx]).Get("children").As<Napi::Array>().Set(idx_in_parent, desc);
|
|
40
|
+
pending.pop_back();
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
visited = true;
|
|
44
|
+
auto current_idx = pending.size() - 1;
|
|
45
|
+
|
|
46
|
+
// Store types
|
|
47
|
+
auto &vec_type = vec->GetType();
|
|
48
|
+
if (with_types) {
|
|
49
|
+
desc.Set("sqlType", vec_type.ToString());
|
|
50
|
+
desc.Set("physicalType", TypeIdToString(vec_type.InternalType()));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Create validity vector
|
|
54
|
+
if (with_data) {
|
|
55
|
+
vec->Flatten(chunk.size());
|
|
56
|
+
auto &validity = duckdb::FlatVector::Validity(*vec);
|
|
57
|
+
auto validity_buffer = Napi::Uint8Array::New(env, chunk.size());
|
|
58
|
+
for (idx_t row_idx = 0; row_idx < chunk.size(); row_idx++) {
|
|
59
|
+
validity_buffer[row_idx] = validity.RowIsValid(row_idx);
|
|
60
|
+
}
|
|
61
|
+
desc.Set("validity", validity_buffer);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Create data buffer
|
|
65
|
+
switch (vec_type.id()) {
|
|
66
|
+
case duckdb::LogicalTypeId::TINYINT: {
|
|
67
|
+
if (with_data) {
|
|
68
|
+
auto array = Napi::Int8Array::New(env, chunk.size());
|
|
69
|
+
auto data = duckdb::FlatVector::GetData<int8_t>(*vec);
|
|
70
|
+
for (size_t i = 0; i < chunk.size(); ++i) {
|
|
71
|
+
array[i] = data[i];
|
|
72
|
+
}
|
|
73
|
+
desc.Set("data", array);
|
|
74
|
+
}
|
|
75
|
+
break;
|
|
76
|
+
}
|
|
77
|
+
case duckdb::LogicalTypeId::SMALLINT: {
|
|
78
|
+
if (with_data) {
|
|
79
|
+
auto array = Napi::Int16Array::New(env, chunk.size());
|
|
80
|
+
auto data = duckdb::FlatVector::GetData<int16_t>(*vec);
|
|
81
|
+
for (size_t i = 0; i < chunk.size(); ++i) {
|
|
82
|
+
array[i] = data[i];
|
|
83
|
+
}
|
|
84
|
+
desc.Set("data", array);
|
|
85
|
+
}
|
|
86
|
+
break;
|
|
87
|
+
}
|
|
88
|
+
case duckdb::LogicalTypeId::INTEGER: {
|
|
89
|
+
if (with_data) {
|
|
90
|
+
auto array = Napi::Int32Array::New(env, chunk.size());
|
|
91
|
+
auto data = duckdb::FlatVector::GetData<int32_t>(*vec);
|
|
92
|
+
for (size_t i = 0; i < chunk.size(); ++i) {
|
|
93
|
+
array[i] = data[i];
|
|
94
|
+
}
|
|
95
|
+
desc.Set("data", array);
|
|
96
|
+
}
|
|
97
|
+
break;
|
|
98
|
+
}
|
|
99
|
+
case duckdb::LogicalTypeId::DOUBLE: {
|
|
100
|
+
if (with_data) {
|
|
101
|
+
auto array = Napi::Float64Array::New(env, chunk.size());
|
|
102
|
+
auto data = duckdb::FlatVector::GetData<double>(*vec);
|
|
103
|
+
for (size_t i = 0; i < chunk.size(); ++i) {
|
|
104
|
+
array[i] = data[i];
|
|
105
|
+
}
|
|
106
|
+
desc.Set("data", array);
|
|
107
|
+
}
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
case duckdb::LogicalTypeId::BIGINT:
|
|
111
|
+
case duckdb::LogicalTypeId::TIME:
|
|
112
|
+
case duckdb::LogicalTypeId::TIME_TZ:
|
|
113
|
+
case duckdb::LogicalTypeId::TIMESTAMP_MS:
|
|
114
|
+
case duckdb::LogicalTypeId::TIMESTAMP_NS:
|
|
115
|
+
case duckdb::LogicalTypeId::TIMESTAMP_SEC:
|
|
116
|
+
case duckdb::LogicalTypeId::TIMESTAMP: {
|
|
117
|
+
if (with_data) {
|
|
118
|
+
#if NAPI_VERSION > 5
|
|
119
|
+
auto array = Napi::BigInt64Array::New(env, chunk.size());
|
|
120
|
+
auto data = duckdb::FlatVector::GetData<int64_t>(*vec);
|
|
121
|
+
#else
|
|
122
|
+
auto array = Napi::Float64Array::New(env, chunk.size());
|
|
123
|
+
auto data = duckdb::FlatVector::GetData<int64_t>(*vec);
|
|
124
|
+
#endif
|
|
125
|
+
for (size_t i = 0; i < chunk.size(); ++i) {
|
|
126
|
+
array[i] = data[i];
|
|
127
|
+
}
|
|
128
|
+
desc.Set("data", array);
|
|
129
|
+
}
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
case duckdb::LogicalTypeId::UBIGINT: {
|
|
133
|
+
if (with_data) {
|
|
134
|
+
#if NAPI_VERSION > 5
|
|
135
|
+
auto array = Napi::BigUint64Array::New(env, chunk.size());
|
|
136
|
+
auto data = duckdb::FlatVector::GetData<uint64_t>(*vec);
|
|
137
|
+
#else
|
|
138
|
+
auto array = Napi::Float64Array::New(env, chunk.size());
|
|
139
|
+
auto data = duckdb::FlatVector::GetData<int64_t>(*vec);
|
|
140
|
+
#endif
|
|
141
|
+
for (size_t i = 0; i < chunk.size(); ++i) {
|
|
142
|
+
array[i] = data[i];
|
|
143
|
+
}
|
|
144
|
+
desc.Set("data", array);
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
case duckdb::LogicalTypeId::BLOB:
|
|
149
|
+
case duckdb::LogicalTypeId::VARCHAR: {
|
|
150
|
+
if (with_data) {
|
|
151
|
+
auto array = Napi::Array::New(env, chunk.size());
|
|
152
|
+
auto data = duckdb::FlatVector::GetData<duckdb::string_t>(*vec);
|
|
153
|
+
for (size_t i = 0; i < chunk.size(); ++i) {
|
|
154
|
+
array.Set(i, data[i].GetString());
|
|
155
|
+
}
|
|
156
|
+
desc.Set("data", array);
|
|
157
|
+
}
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
case duckdb::LogicalTypeId::STRUCT: {
|
|
161
|
+
auto child_count = duckdb::StructType::GetChildCount(vec_type);
|
|
162
|
+
auto &entries = duckdb::StructVector::GetEntries(*vec);
|
|
163
|
+
desc.Set("children", Napi::Array::New(env, child_count));
|
|
164
|
+
for (size_t i = 0; i < child_count; ++i) {
|
|
165
|
+
auto c = child_count - 1 - i;
|
|
166
|
+
auto &entry = entries[c];
|
|
167
|
+
auto desc = Napi::Object::New(env);
|
|
168
|
+
auto name = duckdb::StructType::GetChildName(vec_type, c);
|
|
169
|
+
desc.Set("name", name);
|
|
170
|
+
pending.emplace_back(false, entry.get(), desc, current_idx, i);
|
|
171
|
+
}
|
|
172
|
+
break;
|
|
173
|
+
}
|
|
174
|
+
default:
|
|
175
|
+
Napi::TypeError::New(env, "Unsupported UDF argument type " + vec->GetType().ToString())
|
|
176
|
+
.ThrowAsJavaScriptException();
|
|
177
|
+
break;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
col_descs.Set(col_idx, col_desc);
|
|
181
|
+
}
|
|
182
|
+
return col_descs;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
} // namespace node_duckdb
|