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.
@@ -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 &connection_, Napi::Function callback_) : Task(connection_, callback_) {
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
- std::string error;
102
+ duckdb::PreservedError error;
79
103
  };
80
104
 
81
- static Napi::Value transform_vector(Napi::Env env, duckdb::Vector &vec, duckdb::idx_t rows, bool copy) {
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
- // set up descriptor and data arrays
154
- Napi::Array args_descr(Napi::Array::New(env, jsargs->args->ColumnCount()));
155
- for (duckdb::idx_t col_idx = 0; col_idx < jsargs->args->ColumnCount(); col_idx++) {
156
- auto &vec = jsargs->args->data[col_idx];
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
- descr.Set("args", args_descr);
165
- descr.Set("ret", ret_descr);
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 = ret_descr.ToObject()
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
- if (jsargs->result->GetType().id() == duckdb::LogicalTypeId::VARCHAR) {
194
- auto return_string_array = ret_descr.ToObject()
195
- .Get("data_buffers")
196
- .ToObject()
197
- .Get(ret_descr.ToObject().Get("data_buffer"))
198
- .ToObject();
199
- auto return_string_vec_ptr = duckdb::FlatVector::GetData<duckdb::string_t>(*jsargs->result);
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
- if (!return_validity[row_idx]) {
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.what();
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 &connection_, std::string name_, std::string return_type_name_, Napi::Function callback_)
226
- : Task(connection_, callback_), name(name_), return_type_name(return_type_name_) {
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.Normalify();
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 (!jsargs.error.empty()) {
250
- throw duckdb::IOException(jsargs.error);
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 = DuckDBNodeUDFFUnction::New(env, udf_callback, "duckdb_node_udf" + name, 0, 1, nullptr,
286
- [](Napi::Env, void *, nullptr_t *ctx) {});
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 &connection_, std::string name_, Napi::Function callback_)
301
- : Task(connection_, callback_), name(name_) {
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 &connection_, std::string sql_, Napi::Function callback_)
344
- : Task(connection_, callback_), sql(sql_) {
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
- auto statements = connection.connection->ExtractStatements(sql);
352
- if (statements.size() == 0) {
353
- return;
354
- }
347
+ try {
348
+ auto statements = connection.connection->ExtractStatements(sql);
349
+ if (statements.empty()) {
350
+ return;
351
+ }
355
352
 
356
- // thanks Mark
357
- for (duckdb::idx_t i = 0; i < statements.size(); i++) {
358
- auto res = connection.connection->Query(move(statements[i]));
359
- if (!res->success) {
360
- success = false;
361
- error = res->error;
362
- break;
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
- std::string error;
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