duckdb 0.4.1-dev2.0 → 0.4.1-dev2127.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,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 &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 {
@@ -75,94 +78,28 @@ struct JSArgs {
75
78
  duckdb::DataChunk *args;
76
79
  duckdb::Vector *result;
77
80
  bool done;
78
- std::string error;
81
+ duckdb::PreservedError error;
79
82
  };
80
83
 
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) {
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
- // 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));
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
- descr.Set("args", args_descr);
165
- descr.Set("ret", ret_descr);
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,49 +118,87 @@ 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 = ret_descr.ToObject()
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
- 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
-
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
- 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
- }
187
+ duckdb::FlatVector::SetNull(*jsargs->result, row_idx, false);
208
188
  }
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
189
  }
190
+ }
191
+ } catch (const duckdb::Exception &e) {
192
+ jsargs->error = duckdb::PreservedError(e);
218
193
  } catch (const std::exception &e) {
219
- jsargs->error = e.what();
194
+ jsargs->error = duckdb::PreservedError(e);
220
195
  }
221
196
  jsargs->done = true;
222
197
  }
223
198
 
224
199
  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_) {
200
+ RegisterTask(Connection &connection, std::string name, std::string return_type_name, Napi::Function callback)
201
+ : Task(connection, callback), name(std::move(name)), return_type_name(std::move(return_type_name)) {
227
202
  }
228
203
 
229
204
  void DoWork() override {
@@ -234,7 +209,7 @@ struct RegisterTask : public Task {
234
209
  // here we can do only DuckDB stuff because we do not have a functioning env
235
210
 
236
211
  // Flatten all args to simplify udfs
237
- args.Normalify();
212
+ args.Flatten();
238
213
 
239
214
  JSArgs jsargs;
240
215
  jsargs.rows = args.size();
@@ -246,8 +221,8 @@ struct RegisterTask : public Task {
246
221
  while (!jsargs.done) {
247
222
  std::this_thread::yield();
248
223
  }
249
- if (!jsargs.error.empty()) {
250
- throw duckdb::IOException(jsargs.error);
224
+ if (jsargs.error) {
225
+ jsargs.error.Throw();
251
226
  }
252
227
  };
253
228
 
@@ -282,8 +257,8 @@ Napi::Value Connection::Register(const Napi::CallbackInfo &info) {
282
257
  return env.Null();
283
258
  }
284
259
 
285
- auto udf = DuckDBNodeUDFFUnction::New(env, udf_callback, "duckdb_node_udf" + name, 0, 1, nullptr,
286
- [](Napi::Env, void *, nullptr_t *ctx) {});
260
+ auto udf = duckdb_node_udf_function_t::New(env, udf_callback, "duckdb_node_udf" + name, 0, 1, nullptr,
261
+ [](Napi::Env, void *, std::nullptr_t *ctx) {});
287
262
 
288
263
  // we have to unref the udf because otherwise there is a circular ref with the connection somehow(?)
289
264
  // this took far too long to figure out
@@ -297,8 +272,8 @@ Napi::Value Connection::Register(const Napi::CallbackInfo &info) {
297
272
  }
298
273
 
299
274
  struct UnregisterTask : public Task {
300
- UnregisterTask(Connection &connection_, std::string name_, Napi::Function callback_)
301
- : Task(connection_, callback_), name(name_) {
275
+ UnregisterTask(Connection &connection, std::string name, Napi::Function callback)
276
+ : Task(connection, callback), name(std::move(name)) {
302
277
  }
303
278
 
304
279
  void DoWork() override {
@@ -340,39 +315,44 @@ Napi::Value Connection::Unregister(const Napi::CallbackInfo &info) {
340
315
  }
341
316
 
342
317
  struct ExecTask : public Task {
343
- ExecTask(Connection &connection_, std::string sql_, Napi::Function callback_)
344
- : Task(connection_, callback_), sql(sql_) {
318
+ ExecTask(Connection &connection, std::string sql, Napi::Function callback)
319
+ : Task(connection, callback), sql(std::move(sql)) {
345
320
  }
346
321
 
347
322
  void DoWork() override {
348
323
  auto &connection = Get<Connection>();
349
324
 
350
325
  success = true;
351
- auto statements = connection.connection->ExtractStatements(sql);
352
- if (statements.size() == 0) {
353
- return;
354
- }
326
+ try {
327
+ auto statements = connection.connection->ExtractStatements(sql);
328
+ if (statements.empty()) {
329
+ return;
330
+ }
355
331
 
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;
332
+ for (duckdb::idx_t i = 0; i < statements.size(); i++) {
333
+ auto res = connection.connection->Query(move(statements[i]));
334
+ if (res->HasError()) {
335
+ success = false;
336
+ error = res->GetErrorObject();
337
+ break;
338
+ }
363
339
  }
340
+ } catch (duckdb::ParserException &e) {
341
+ success = false;
342
+ error = duckdb::PreservedError(e);
343
+ return;
364
344
  }
365
345
  }
366
346
 
367
347
  void Callback() override {
368
348
  auto env = object.Env();
369
349
  Napi::HandleScope scope(env);
370
- callback.Value().MakeCallback(object.Value(), {success ? env.Null() : Napi::String::New(env, error)});
350
+ callback.Value().MakeCallback(object.Value(), {success ? env.Null() : Napi::String::New(env, error.Message())});
371
351
  };
372
352
 
373
353
  std::string sql;
374
354
  bool success;
375
- std::string error;
355
+ duckdb::PreservedError error;
376
356
  };
377
357
 
378
358
  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