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 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(descr) {
29
+ return this.register_bulk(name, return_type, function(desc) {
57
30
  try {
58
- const data_arr = [];
59
- const validity_arr = [];
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
- for (const idx in descr.args) {
62
- const arg = descr.args[idx];
63
- validity_arr.push(arg.data_buffers[arg.validity_buffer]);
64
- data_arr.push(ptr_to_arr(arg.data_buffers[arg.data_buffer], arg.physical_type, descr.rows));
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
- const out_data = ptr_to_arr(descr.ret.data_buffers[descr.ret.data_buffer], descr.ret.physical_type, descr.rows);
68
- const out_validity = descr.ret.data_buffers[descr.ret.validity_buffer];
69
-
70
- switch (descr.args.length) {
71
- case 0:
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 1:
79
- for (let i = 0; i < descr.rows; ++i) {
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 2:
86
- for (let i = 0; i < descr.rows; ++i) {
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 3:
93
- for (let i = 0; i < descr.rows; ++i) {
94
- const res = fun(validity_arr[0][i] ? data_arr[0][i] : undefined, validity_arr[1][i] ? data_arr[1][i] : undefined, validity_arr[2][i] ? data_arr[2][i] : undefined);
95
- out_data[i] = res;
96
- out_validity[i] = res == undefined || res == null ? 0 : 1;
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
- default:
100
- throw "Unsupported argument count";
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
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "duckdb",
3
3
  "main": "./lib/duckdb.js",
4
- "version": "0.4.1-dev9.0",
4
+ "version": "0.4.1-dev953.0",
5
5
  "description": "DuckDB node.js API",
6
6
  "gypfile": true,
7
7
  "dependencies": {
@@ -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 {
@@ -78,91 +81,25 @@ struct JSArgs {
78
81
  std::string 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,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 = 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()));
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 &connection_, std::string name_, std::string return_type_name_, Napi::Function callback_)
226
- : Task(connection_, callback_), name(name_), return_type_name(return_type_name_) {
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.Normalify();
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 = DuckDBNodeUDFFUnction::New(env, udf_callback, "duckdb_node_udf" + name, 0, 1, nullptr,
286
- [](Napi::Env, void *, nullptr_t *ctx) {});
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 &connection_, std::string name_, Napi::Function callback_)
301
- : Task(connection_, callback_), name(name_) {
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 &connection_, std::string sql_, Napi::Function callback_)
344
- : Task(connection_, callback_), sql(sql_) {
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
- auto statements = connection.connection->ExtractStatements(sql);
352
- if (statements.size() == 0) {
353
- return;
354
- }
324
+ try {
325
+ auto statements = connection.connection->ExtractStatements(sql);
326
+ if (statements.empty()) {
327
+ return;
328
+ }
355
329
 
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;
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