duckdb 0.3.3-dev440.0 → 0.3.3-dev471.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
@@ -18,6 +18,8 @@ clean:
18
18
  rm -f test/support/big.db-journal
19
19
  rm -rf ./node_modules/
20
20
  rm -rf src/duckdb.*
21
+ rm -rf src/parquet-amalgamation.*
22
+
21
23
 
22
24
  test:
23
25
  npm test
package/binding.gyp CHANGED
@@ -61,14 +61,14 @@
61
61
  },
62
62
  ], # OS=="win"
63
63
  ], # conditions
64
- # uncomment this to build against existing lib
65
- # "libraries": [
66
- # "/Users/hannes/source/duckdb/build/release/src/libduckdb_static.a",
67
- # "/Users/hannes/source/duckdb/build/release/third_party/fmt/libfmt.a",
68
- # "/Users/hannes/source/duckdb/build/release/third_party/libpg_query/libpg_query.a",
69
- # "/Users/hannes/source/duckdb/build/release/third_party/utf8proc/libutf8proc.a",
70
- # "/Users/hannes/source/duckdb/build/release/third_party/re2/libduckdb_re2.a"
71
- # ]
64
+ # uncomment this to build against existing lib
65
+ # "libraries": [
66
+ # "/Users/hannes/source/duckdb/build/release/src/libduckdb_static.a",
67
+ # "/Users/hannes/source/duckdb/build/release/third_party/fmt/libduckdb_fmt.a",
68
+ # "/Users/hannes/source/duckdb/build/release/third_party/libpg_query/libduckdb_pg_query.a",
69
+ # "/Users/hannes/source/duckdb/build/release/third_party/utf8proc/libduckdb_utf8proc.a",
70
+ # "/Users/hannes/source/duckdb/build/release/third_party/re2/libduckdb_re2.a"
71
+ # ]
72
72
  },
73
73
  {
74
74
  "target_name": "action_after_build",
package/lib/duckdb.js CHANGED
@@ -23,42 +23,130 @@ Connection.prototype.each = function(sql) {
23
23
  return statement.each.apply(statement, arguments);
24
24
  }
25
25
 
26
- Database.prototype.prepare = function() {
27
- if (this.default_connection == undefined) {
28
- this.default_connection = new duckdb.Connection(this);
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
29
50
  }
30
- return this.default_connection.prepare.apply(this.default_connection, arguments);
31
51
  }
32
52
 
33
- Database.prototype.run = function() {
34
- if (this.default_connection == undefined) {
35
- this.default_connection = new Connection(this);
53
+ // this follows the wasm udfs somewhat but is simpler because we can pass data much more cleanly
54
+ Connection.prototype.register = function(name, return_type, fun) {
55
+ // TODO what if this throws an error somewhere? do we need a try/catch?
56
+ return this.register_bulk(name, return_type, function(descr) {
57
+ try {
58
+ const data_arr = [];
59
+ const validity_arr = [];
60
+
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));
65
+ }
66
+
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
+ }
77
+ 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
+ }
84
+ 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
+ }
91
+ 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
+ }
98
+ break;
99
+ default:
100
+ throw "Unsupported argument count";
101
+ }
102
+ } catch(error) { // work around recently fixed napi bug https://github.com/nodejs/node-addon-api/issues/912
103
+ msg = error;
104
+ if (typeof error == 'object' && 'message' in error) {
105
+ msg = error.message
106
+ }
107
+ throw {name: 'DuckDB-UDF-Exception', message : msg};
108
+ }
109
+ })
110
+ }
111
+
112
+ default_connection = function(o) {
113
+ if (o.default_connection == undefined) {
114
+ o.default_connection = new duckdb.Connection(o);
36
115
  }
37
- this.default_connection.run.apply(this.default_connection, arguments);
116
+ return(o.default_connection);
117
+ }
118
+
119
+ Database.prototype.prepare = function() {
120
+ return default_connection(this).prepare.apply(this.default_connection, arguments);
121
+ }
122
+
123
+ Database.prototype.run = function() {
124
+ default_connection(this).run.apply(this.default_connection, arguments);
38
125
  return this;
39
126
  }
40
127
 
41
128
  Database.prototype.each = function() {
42
- if (this.default_connection == undefined) {
43
- this.default_connection = new Connection(this);
44
- }
45
- this.default_connection.each.apply(this.default_connection, arguments);
129
+ default_connection(this).each.apply(this.default_connection, arguments);
46
130
  return this;
47
131
  }
48
132
 
49
133
  Database.prototype.all = function() {
50
- if (this.default_connection == undefined) {
51
- this.default_connection = new Connection(this);
52
- }
53
- this.default_connection.all.apply(this.default_connection, arguments);
134
+ default_connection(this).all.apply(this.default_connection, arguments);
54
135
  return this;
55
136
  }
56
137
 
57
138
  Database.prototype.exec = function() {
58
- if (this.default_connection == undefined) {
59
- this.default_connection = new Connection(this);
60
- }
61
- this.default_connection.exec.apply(this.default_connection, arguments);
139
+ default_connection(this).exec.apply(this.default_connection, arguments);
140
+ return this;
141
+ }
142
+
143
+ Database.prototype.register = function() {
144
+ default_connection(this).register.apply(this.default_connection, arguments);
145
+ return this;
146
+ }
147
+
148
+ Database.prototype.unregister = function() {
149
+ default_connection(this).unregister.apply(this.default_connection, arguments);
62
150
  return this;
63
151
  }
64
152
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "duckdb",
3
3
  "main": "./lib/duckdb.js",
4
- "version": "0.3.3-dev440.0",
4
+ "version": "0.3.3-dev471.0",
5
5
  "description": "DuckDB node.js API",
6
6
  "gypfile": true,
7
7
  "dependencies": {
@@ -1,5 +1,7 @@
1
1
  #include "duckdb_node.hpp"
2
2
 
3
+ #include <thread>
4
+
3
5
  namespace node_duckdb {
4
6
 
5
7
  Napi::FunctionReference Connection::constructor;
@@ -9,7 +11,9 @@ Napi::Object Connection::Init(Napi::Env env, Napi::Object exports) {
9
11
 
10
12
  Napi::Function t =
11
13
  DefineClass(env, "Connection",
12
- {InstanceMethod("prepare", &Connection::Prepare), InstanceMethod("exec", &Connection::Exec)});
14
+ {InstanceMethod("prepare", &Connection::Prepare), InstanceMethod("exec", &Connection::Exec),
15
+ InstanceMethod("register_bulk", &Connection::Register),
16
+ InstanceMethod("unregister", &Connection::Unregister)});
13
17
 
14
18
  constructor = Napi::Persistent(t);
15
19
  constructor.SuppressDestruct();
@@ -66,6 +70,275 @@ Napi::Value Connection::Prepare(const Napi::CallbackInfo &info) {
66
70
  return res->Value();
67
71
  }
68
72
 
73
+ struct JSArgs {
74
+ duckdb::idx_t rows;
75
+ duckdb::DataChunk *args;
76
+ duckdb::Vector *result;
77
+ bool done;
78
+ std::string error;
79
+ };
80
+
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) {
151
+ try { // if we dont catch exceptions here we terminate node if one happens ^^
152
+
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));
163
+ descr.Set("rows", jsargs->rows);
164
+ descr.Set("args", args_descr);
165
+ descr.Set("ret", ret_descr);
166
+
167
+ // actually call the UDF, or rather its vectorized wrapper from duckdb.js/Connection.prototype.register wrapper
168
+ jsudf({descr});
169
+
170
+ if (env.IsExceptionPending()) {
171
+ // bit of a dance to get a nice error message if possible
172
+ auto exception = env.GetAndClearPendingException();
173
+ std::string msg = exception.Message();
174
+ if (msg.empty()) {
175
+ auto exception_value = exception.Value();
176
+ if (exception_value.IsObject() && exception_value.Has("message")) {
177
+ msg = exception_value.Get("message").ToString().Utf8Value();
178
+ }
179
+ }
180
+ throw duckdb::IOException("UDF Execution Error: " + msg);
181
+ }
182
+
183
+ // 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>>();
189
+ for (duckdb::idx_t row_idx = 0; row_idx < jsargs->rows; row_idx++) {
190
+ duckdb::FlatVector::SetNull(*jsargs->result, row_idx, !return_validity[row_idx]);
191
+ }
192
+
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
+
201
+ 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
+ }
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
+ }
218
+ } catch (const std::exception &e) {
219
+ jsargs->error = e.what();
220
+ }
221
+ jsargs->done = true;
222
+ }
223
+
224
+ 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_) {
227
+ }
228
+
229
+ void DoWork() override {
230
+ auto &connection = Get<Connection>();
231
+ auto &udf_ptr = connection.udfs[name];
232
+ duckdb::scalar_function_t udf_function = [&udf_ptr](duckdb::DataChunk &args, duckdb::ExpressionState &state,
233
+ duckdb::Vector &result) -> void {
234
+ // here we can do only DuckDB stuff because we do not have a functioning env
235
+
236
+ // Flatten all args to simplify udfs
237
+ args.Normalify();
238
+
239
+ JSArgs jsargs;
240
+ jsargs.rows = args.size();
241
+ jsargs.args = &args;
242
+ jsargs.result = &result;
243
+ jsargs.done = false;
244
+
245
+ udf_ptr.BlockingCall(&jsargs);
246
+ while (!jsargs.done) {
247
+ std::this_thread::yield();
248
+ }
249
+ if (!jsargs.error.empty()) {
250
+ throw duckdb::IOException(jsargs.error);
251
+ }
252
+ };
253
+
254
+ auto expr = duckdb::Parser::ParseExpressionList(duckdb::StringUtil::Format("asdf::%s", return_type_name));
255
+ auto &cast = (duckdb::CastExpression &)*expr[0];
256
+ auto return_type = cast.cast_type;
257
+
258
+ connection.connection->CreateVectorizedFunction(name, std::vector<duckdb::LogicalType> {}, return_type,
259
+ udf_function, duckdb::LogicalType::ANY);
260
+ }
261
+ std::string name;
262
+ std::string return_type_name;
263
+ };
264
+
265
+ Napi::Value Connection::Register(const Napi::CallbackInfo &info) {
266
+ auto env = info.Env();
267
+ if (info.Length() < 3 || !info[0].IsString() || !info[1].IsString() || !info[2].IsFunction()) {
268
+ Napi::TypeError::New(env, "Holding it wrong").ThrowAsJavaScriptException();
269
+ return env.Null();
270
+ }
271
+
272
+ std::string name = info[0].As<Napi::String>();
273
+ std::string return_type_name = info[1].As<Napi::String>();
274
+ Napi::Function udf_callback = info[2].As<Napi::Function>();
275
+ Napi::Function completion_callback;
276
+ if (info.Length() > 3 && info[3].IsFunction()) {
277
+ completion_callback = info[3].As<Napi::Function>();
278
+ }
279
+
280
+ if (udfs.find(name) != udfs.end()) {
281
+ Napi::TypeError::New(env, "UDF with this name already exists").ThrowAsJavaScriptException();
282
+ return env.Null();
283
+ }
284
+
285
+ auto udf = DuckDBNodeUDFFUnction::New(env, udf_callback, "duckdb_node_udf" + name, 0, 1, nullptr,
286
+ [](Napi::Env, void *, nullptr_t *ctx) {});
287
+
288
+ // we have to unref the udf because otherwise there is a circular ref with the connection somehow(?)
289
+ // this took far too long to figure out
290
+ udf.Unref(env);
291
+ udfs[name] = udf;
292
+
293
+ database_ref->Schedule(info.Env(),
294
+ duckdb::make_unique<RegisterTask>(*this, name, return_type_name, completion_callback));
295
+
296
+ return Value();
297
+ }
298
+
299
+ struct UnregisterTask : public Task {
300
+ UnregisterTask(Connection &connection_, std::string name_, Napi::Function callback_)
301
+ : Task(connection_, callback_), name(name_) {
302
+ }
303
+
304
+ void DoWork() override {
305
+ auto &connection = Get<Connection>();
306
+ if (connection.udfs.find(name) == connection.udfs.end()) { // silently ignore
307
+ return;
308
+ }
309
+
310
+ connection.udfs[name].Release();
311
+ connection.udfs.erase(name);
312
+ auto &con = *connection.connection;
313
+ con.BeginTransaction();
314
+ auto &context = *con.context;
315
+ auto &catalog = duckdb::Catalog::GetCatalog(context);
316
+ duckdb::DropInfo info;
317
+ info.type = duckdb::CatalogType::SCALAR_FUNCTION_ENTRY;
318
+ info.name = name;
319
+ catalog.DropEntry(context, &info);
320
+ con.Commit();
321
+ }
322
+ std::string name;
323
+ };
324
+
325
+ Napi::Value Connection::Unregister(const Napi::CallbackInfo &info) {
326
+ auto env = info.Env();
327
+ if (info.Length() < 1 || !info[0].IsString()) {
328
+ Napi::TypeError::New(env, "Holding it wrong").ThrowAsJavaScriptException();
329
+ return env.Null();
330
+ }
331
+ std::string name = info[0].As<Napi::String>();
332
+
333
+ Napi::Function callback;
334
+ if (info.Length() > 1 && info[1].IsFunction()) {
335
+ callback = info[1].As<Napi::Function>();
336
+ }
337
+
338
+ database_ref->Schedule(info.Env(), duckdb::make_unique<UnregisterTask>(*this, name, callback));
339
+ return Value();
340
+ }
341
+
69
342
  struct ExecTask : public Task {
70
343
  ExecTask(Connection &connection_, std::string sql_, Napi::Function callback_)
71
344
  : Task(connection_, callback_), sql(sql_) {
package/src/database.cpp CHANGED
@@ -28,6 +28,7 @@ struct OpenTask : public Task {
28
28
 
29
29
  void DoWork() override {
30
30
  try {
31
+
31
32
  Get<Database>().database = duckdb::make_unique<duckdb::DuckDB>(filename);
32
33
  duckdb::ParquetExtension extension;
33
34
  extension.Load(*Get<Database>().database);
package/src/duckdb.cpp CHANGED
@@ -44384,6 +44384,9 @@ static bool BlobCastSwitch(Vector &source, Vector &result, idx_t count, string *
44384
44384
  // blob to varchar
44385
44385
  VectorStringCast<string_t, duckdb::CastFromBlob>(source, result, count);
44386
44386
  break;
44387
+ case LogicalTypeId::AGGREGATE_STATE:
44388
+ result.Reinterpret(source);
44389
+ break;
44387
44390
  default:
44388
44391
  return TryVectorNullCast(source, result, count, error_message);
44389
44392
  }
@@ -44572,6 +44575,15 @@ static bool EnumCastSwitch(Vector &source, Vector &result, idx_t count, string *
44572
44575
  return true;
44573
44576
  }
44574
44577
 
44578
+ static bool AggregateStateToBlobCast(Vector &source, Vector &result, idx_t count, string *error_message, bool strict) {
44579
+ if (result.GetType().id() != LogicalTypeId::BLOB) {
44580
+ throw TypeMismatchException(source.GetType(), result.GetType(),
44581
+ "Cannot cast AGGREGATE_STATE to anything but BLOB");
44582
+ }
44583
+ result.Reinterpret(source);
44584
+ return true;
44585
+ }
44586
+
44575
44587
  static bool StructCastSwitch(Vector &source, Vector &result, idx_t count, string *error_message) {
44576
44588
  switch (result.GetType().id()) {
44577
44589
  case LogicalTypeId::STRUCT:
@@ -44687,6 +44699,8 @@ bool VectorOperations::TryCast(Vector &source, Vector &result, idx_t count, stri
44687
44699
  return ListCastSwitch(source, result, count, error_message);
44688
44700
  case LogicalTypeId::ENUM:
44689
44701
  return EnumCastSwitch(source, result, count, error_message, strict);
44702
+ case LogicalTypeId::AGGREGATE_STATE:
44703
+ return AggregateStateToBlobCast(source, result, count, error_message, strict);
44690
44704
  default:
44691
44705
  return TryVectorNullCast(source, result, count, error_message);
44692
44706
  }
@@ -69315,6 +69329,10 @@ struct VarianceFun {
69315
69329
  static void RegisterFunction(BuiltinFunctions &set);
69316
69330
  };
69317
69331
 
69332
+ struct StandardErrorOfTheMeanFun {
69333
+ static void RegisterFunction(BuiltinFunctions &set);
69334
+ };
69335
+
69318
69336
  } // namespace duckdb
69319
69337
 
69320
69338
  //===----------------------------------------------------------------------===//
@@ -69909,6 +69927,20 @@ struct STDDevPopOperation : public STDDevBaseOperation {
69909
69927
  }
69910
69928
  }
69911
69929
  };
69930
+
69931
+ struct StandardErrorOfTheMeanOperation : public STDDevBaseOperation {
69932
+ template <class T, class STATE>
69933
+ static void Finalize(Vector &result, FunctionData *, STATE *state, T *target, ValidityMask &mask, idx_t idx) {
69934
+ if (state->count == 0) {
69935
+ mask.SetInvalid(idx);
69936
+ } else {
69937
+ target[idx] = sqrt(state->dsquared / state->count) / sqrt((state->count));
69938
+ if (!Value::DoubleIsValid(target[idx])) {
69939
+ throw OutOfRangeException("SEM is out of range!");
69940
+ }
69941
+ }
69942
+ }
69943
+ };
69912
69944
  } // namespace duckdb
69913
69945
 
69914
69946
  //===----------------------------------------------------------------------===//
@@ -70070,6 +70102,13 @@ void VarianceFun::RegisterFunction(BuiltinFunctions &set) {
70070
70102
  set.AddFunction(var_samp);
70071
70103
  }
70072
70104
 
70105
+ void StandardErrorOfTheMeanFun::RegisterFunction(BuiltinFunctions &set) {
70106
+ AggregateFunctionSet sem("sem");
70107
+ sem.AddFunction(AggregateFunction::UnaryAggregate<StddevState, double, double, StandardErrorOfTheMeanOperation>(
70108
+ LogicalType::DOUBLE, LogicalType::DOUBLE));
70109
+ set.AddFunction(sem);
70110
+ }
70111
+
70073
70112
  } // namespace duckdb
70074
70113
 
70075
70114
 
@@ -70087,7 +70126,7 @@ void BuiltinFunctions::RegisterAlgebraicAggregates() {
70087
70126
  Register<VarPopFun>();
70088
70127
  Register<VarSampFun>();
70089
70128
  Register<VarianceFun>();
70090
-
70129
+ Register<StandardErrorOfTheMeanFun>();
70091
70130
  Register<Corr>();
70092
70131
  }
70093
70132
 
@@ -71244,7 +71283,7 @@ struct FirstFunctionString : public FirstFunctionBase {
71244
71283
  if (!state->is_set || state->is_null) {
71245
71284
  mask.SetInvalid(idx);
71246
71285
  } else {
71247
- target[idx] = StringVector::AddString(result, state->value);
71286
+ target[idx] = StringVector::AddStringOrBlob(result, state->value);
71248
71287
  }
71249
71288
  }
71250
71289
 
@@ -92902,9 +92941,13 @@ static void AggregateStateCombine(DataChunk &input, ExpressionState &state_p, Ve
92902
92941
 
92903
92942
  D_ASSERT(input.data.size() == 2);
92904
92943
  D_ASSERT(input.data[0].GetType().id() == LogicalTypeId::AGGREGATE_STATE);
92905
- D_ASSERT(input.data[0].GetType() == input.data[1].GetType());
92906
92944
  D_ASSERT(input.data[0].GetType() == result.GetType());
92907
92945
 
92946
+ if (input.data[0].GetType().InternalType() != input.data[1].GetType().InternalType()) {
92947
+ throw IOException("Aggregate state combine type mismatch, expect %s, got %s",
92948
+ input.data[0].GetType().ToString(), input.data[1].GetType().ToString());
92949
+ }
92950
+
92908
92951
  VectorData state0_data, state1_data;
92909
92952
  input.data[0].Orrify(input.size(), state0_data);
92910
92953
  input.data[1].Orrify(input.size(), state1_data);
@@ -92935,8 +92978,10 @@ static void AggregateStateCombine(DataChunk &input, ExpressionState &state_p, Ve
92935
92978
  }
92936
92979
 
92937
92980
  // we actually have to combine
92938
- D_ASSERT(state0.GetSize() == bind_data.state_size);
92939
- D_ASSERT(state1.GetSize() == bind_data.state_size);
92981
+ if (state0.GetSize() != bind_data.state_size || state1.GetSize() != bind_data.state_size) {
92982
+ throw IOException("Aggregate state size mismatch, expect %llu, got %llu and %llu", bind_data.state_size,
92983
+ state0.GetSize(), state1.GetSize());
92984
+ }
92940
92985
 
92941
92986
  memcpy(local_state.state_buffer0.get(), state0.GetDataUnsafe(), bind_data.state_size);
92942
92987
  memcpy(local_state.state_buffer1.get(), state1.GetDataUnsafe(), bind_data.state_size);
@@ -92963,7 +93008,8 @@ static unique_ptr<FunctionData> BindAggregateState(ClientContext &context, Scala
92963
93008
  throw BinderException("Can only FINALIZE aggregate state, not %s", arg_return_type.ToString());
92964
93009
  }
92965
93010
  // combine
92966
- if (arguments.size() == 2 && arguments[0]->return_type != arguments[1]->return_type) {
93011
+ if (arguments.size() == 2 && arguments[0]->return_type != arguments[1]->return_type &&
93012
+ arguments[1]->return_type.id() != LogicalTypeId::BLOB) {
92967
93013
  throw BinderException("Cannot COMBINE aggregate states from different functions, %s <> %s",
92968
93014
  arguments[0]->return_type.ToString(), arguments[1]->return_type.ToString());
92969
93015
  }
@@ -93069,7 +93115,7 @@ ScalarFunction ExportAggregateFunction::GetFinalize() {
93069
93115
  }
93070
93116
 
93071
93117
  ScalarFunction ExportAggregateFunction::GetCombine() {
93072
- return ScalarFunction("combine", {LogicalTypeId::AGGREGATE_STATE, LogicalTypeId::AGGREGATE_STATE},
93118
+ return ScalarFunction("combine", {LogicalTypeId::AGGREGATE_STATE, LogicalTypeId::ANY},
93073
93119
  LogicalTypeId::AGGREGATE_STATE, AggregateStateCombine, false, BindAggregateState, nullptr,
93074
93120
  nullptr, InitCombineState);
93075
93121
  }
package/src/duckdb.hpp CHANGED
@@ -11,8 +11,8 @@ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLI
11
11
  #pragma once
12
12
  #define DUCKDB_AMALGAMATION 1
13
13
  #define DUCKDB_AMALGAMATION_EXTENDED 1
14
- #define DUCKDB_SOURCE_ID "f7a783d93"
15
- #define DUCKDB_VERSION "v0.3.3-dev440"
14
+ #define DUCKDB_SOURCE_ID "fbe58d359"
15
+ #define DUCKDB_VERSION "v0.3.3-dev471"
16
16
  //===----------------------------------------------------------------------===//
17
17
  // DuckDB
18
18
  //