duckdb 0.4.1-dev960.0 → 0.5.1-dev2.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/lib/duckdb.js CHANGED
@@ -1,32 +1,157 @@
1
+ /**
2
+ * @module duckdb
3
+ * @summary these jsdoc annotations are still a work in progress - feedback and suggestions are welcome!
4
+ */
5
+
1
6
  var duckdb = require('./duckdb-binding.js');
2
7
  module.exports = exports = duckdb;
3
8
 
9
+ /**
10
+ * Check that errno attribute equals this to check for a duckdb error
11
+ * @constant {number}
12
+ */
13
+ var ERROR = duckdb.ERROR;
14
+
15
+ /**
16
+ * Open database in readonly mode
17
+ * @constant {number}
18
+ */
19
+ var OPEN_READONLY = duckdb.OPEN_READONLY;
20
+ /**
21
+ * Currently ignored
22
+ * @constant {number}
23
+ */
24
+ var OPEN_READWRITE = duckdb.OPEN_READWRITE;
25
+ /**
26
+ * Currently ignored
27
+ * @constant {number}
28
+ */
29
+ var OPEN_CREATE = duckdb.OPEN_CREATE;
30
+ /**
31
+ * Currently ignored
32
+ * @constant {number}
33
+ */
34
+ var OPEN_FULLMUTEX = duckdb.OPEN_FULLMUTEX;
35
+ /**
36
+ * Currently ignored
37
+ * @constant {number}
38
+ */
39
+ var OPEN_SHAREDCACHE = duckdb.OPEN_SHAREDCACHE;
40
+ /**
41
+ * Currently ignored
42
+ * @constant {number}
43
+ */
44
+ var OPEN_PRIVATECACHE = duckdb.OPEN_PRIVATECACHE;
4
45
 
5
46
  // some wrappers for compatibilities sake
47
+ /**
48
+ * Main database interface
49
+ */
6
50
  var Database = duckdb.Database;
51
+ /**
52
+ * @class
53
+ */
7
54
  var Connection = duckdb.Connection;
55
+ /**
56
+ * @class
57
+ */
8
58
  var Statement = duckdb.Statement;
59
+ /**
60
+ * @class
61
+ */
62
+ var QueryResult = duckdb.QueryResult;
9
63
 
64
+ /**
65
+ * @method
66
+ * @return data chunk
67
+ */
68
+ QueryResult.prototype.nextChunk;
10
69
 
11
- Connection.prototype.run = function(sql) {
70
+ /**
71
+ * @name asyncIterator
72
+ * @memberof module:duckdb~QueryResult
73
+ * @method
74
+ * @instance
75
+ * @yields data chunks
76
+ */
77
+ QueryResult.prototype[Symbol.asyncIterator] = async function*() {
78
+ let prefetch = this.nextChunk();
79
+ while (true) {
80
+ const chunk = await prefetch;
81
+ // Null chunk indicates end of stream
82
+ if (!chunk) {
83
+ return;
84
+ }
85
+ // Prefetch the next chunk while we're iterating
86
+ prefetch = this.nextChunk();
87
+ for (const row of chunk) {
88
+ yield row;
89
+ }
90
+ }
91
+ }
92
+
93
+
94
+ /**
95
+ * Run a SQL statement and trigger a callback when done
96
+ * @arg sql
97
+ * @param {...*} params
98
+ * @param callback
99
+ * @return {void}
100
+ */
101
+ Connection.prototype.run = function (sql) {
12
102
  var statement = new Statement(this, sql);
13
103
  return statement.run.apply(statement, arguments);
14
104
  }
15
105
 
16
- Connection.prototype.all = function(sql) {
17
- var statement = new Statement(this,sql);
106
+ /**
107
+ * Run a SQL query and triggers the callback once for all result rows
108
+ * @arg sql
109
+ * @param {...*} params
110
+ * @param callback
111
+ * @return {void}
112
+ */
113
+ Connection.prototype.all = function (sql) {
114
+ var statement = new Statement(this, sql);
18
115
  return statement.all.apply(statement, arguments);
19
116
  }
20
117
 
21
- Connection.prototype.each = function(sql) {
118
+ /**
119
+ * Runs a SQL query and triggers the callback for each result row
120
+ * @arg sql
121
+ * @param {...*} params
122
+ * @param callback
123
+ * @return {void}
124
+ */
125
+ Connection.prototype.each = function (sql) {
22
126
  var statement = new Statement(this, sql);
23
127
  return statement.each.apply(statement, arguments);
24
128
  }
25
129
 
26
- // this follows the wasm udfs somewhat but is simpler because we can pass data much more cleanly
27
- Connection.prototype.register = function(name, return_type, fun) {
130
+ /**
131
+ * @arg sql
132
+ * @param {...*} params
133
+ * @yields row chunks
134
+ */
135
+ Connection.prototype.stream = async function* (sql) {
136
+ const statement = new Statement(this, sql);
137
+ const queryResult = await statement.stream.apply(statement, arguments);
138
+ for await (const result of queryResult) {
139
+ yield result;
140
+ }
141
+ }
142
+
143
+ /**
144
+ * Register a User Defined Function
145
+ *
146
+ * @arg name
147
+ * @arg return_type
148
+ * @arg fun
149
+ * @return {void}
150
+ * @note this follows the wasm udfs somewhat but is simpler because we can pass data much more cleanly
151
+ */
152
+ Connection.prototype.register = function (name, return_type, fun) {
28
153
  // TODO what if this throws an error somewhere? do we need a try/catch?
29
- return this.register_bulk(name, return_type, function(desc) {
154
+ return this.register_bulk(name, return_type, function (desc) {
30
155
  try {
31
156
  // Build an argument resolver
32
157
  const buildResolver = (arg) => {
@@ -126,62 +251,252 @@ Connection.prototype.register = function(name, return_type, fun) {
126
251
  desc.ret.data[i] = res;
127
252
  desc.ret.validity[i] = res === undefined || res === null ? 0 : 1;
128
253
  }
129
- } catch(error) { // work around recently fixed napi bug https://github.com/nodejs/node-addon-api/issues/912
254
+ } catch (error) { // work around recently fixed napi bug https://github.com/nodejs/node-addon-api/issues/912
130
255
  console.log(desc.ret);
131
256
  msg = error;
132
257
  if (typeof error == 'object' && 'message' in error) {
133
258
  msg = error.message
134
259
  }
135
- throw {name: 'DuckDB-UDF-Exception', message : msg};
260
+ throw { name: 'DuckDB-UDF-Exception', message: msg };
136
261
  }
137
262
  })
138
263
  }
139
264
 
140
- default_connection = function(o) {
265
+ /**
266
+ * Prepare a SQL query for execution
267
+ * @method
268
+ * @arg sql
269
+ * @param {...*} params
270
+ * @param callback
271
+ * @return {Statement}
272
+ */
273
+ Connection.prototype.prepare;
274
+ /**
275
+ * Execute a SQL query
276
+ * @method
277
+ * @arg sql
278
+ * @param {...*} params
279
+ * @param callback
280
+ * @return {void}
281
+ */
282
+ Connection.prototype.exec;
283
+ /**
284
+ * Register a User Defined Function
285
+ *
286
+ * @method
287
+ * @arg name
288
+ * @arg return_type
289
+ * @param callback
290
+ * @return {void}
291
+ */
292
+ Connection.prototype.register_bulk;
293
+ /**
294
+ * Unregister a User Defined Function
295
+ *
296
+ * @method
297
+ * @arg name
298
+ * @arg return_type
299
+ * @param callback
300
+ * @return {void}
301
+ */
302
+ Connection.prototype.unregister;
303
+
304
+ var default_connection = function (o) {
141
305
  if (o.default_connection == undefined) {
142
306
  o.default_connection = new duckdb.Connection(o);
143
307
  }
144
- return(o.default_connection);
308
+ return o.default_connection;
145
309
  }
146
310
 
147
- Database.prototype.prepare = function() {
311
+
312
+ /**
313
+ * Closes database instance
314
+ * @method
315
+ * @param callback
316
+ * @return {void}
317
+ */
318
+ Database.prototype.close = function() {
319
+ this.default_connection = null
320
+ this.close_internal.apply(this, arguments);
321
+ };
322
+
323
+ /**
324
+ * Internal method. Do not use, call Connection#close instead
325
+ * @method
326
+ * @param callback
327
+ * @return {void}
328
+ */
329
+ Database.prototype.close_internal;
330
+
331
+ /**
332
+ * Triggers callback when all scheduled database tasks have completed.
333
+ * @method
334
+ * @param callback
335
+ * @return {void}
336
+ */
337
+ Database.prototype.wait;
338
+
339
+ /**
340
+ * TODO: what does this do?
341
+ * @method
342
+ * @param callback
343
+ * @return {void}
344
+ */
345
+ Database.prototype.serialize;
346
+
347
+ /**
348
+ * TODO: what does this do?
349
+ * @method
350
+ * @param callback
351
+ * @return {void}
352
+ */
353
+ Database.prototype.parallelize;
354
+
355
+ /**
356
+ * Create a new database connection
357
+ * @method
358
+ * @arg path the database to connect to, either a file path, or `:memory:`
359
+ * @return {Connection}
360
+ */
361
+ Database.prototype.connect;
362
+
363
+ /**
364
+ * Supposedly interrupt queries, but currently does not do anything.
365
+ * @method
366
+ * @param callback
367
+ * @return {void}
368
+ */
369
+ Database.prototype.interrupt;
370
+
371
+ /**
372
+ * Prepare a SQL query for execution
373
+ * @arg sql
374
+ * @return {Statement}
375
+ */
376
+ Database.prototype.prepare = function () {
148
377
  return default_connection(this).prepare.apply(this.default_connection, arguments);
149
378
  }
150
379
 
151
- Database.prototype.run = function() {
380
+ /**
381
+ * Convenience method for Connection#run using a built-in default connection
382
+ * @arg sql
383
+ * @param {...*} params
384
+ * @param callback
385
+ * @return {void}
386
+ */
387
+ Database.prototype.run = function () {
152
388
  default_connection(this).run.apply(this.default_connection, arguments);
153
389
  return this;
154
390
  }
155
391
 
156
- Database.prototype.each = function() {
392
+ /**
393
+ * Convenience method for Connection#each using a built-in default connection
394
+ * @arg sql
395
+ * @param {...*} params
396
+ * @param callback
397
+ * @return {void}
398
+ */
399
+ Database.prototype.each = function () {
157
400
  default_connection(this).each.apply(this.default_connection, arguments);
158
401
  return this;
159
402
  }
160
403
 
161
- Database.prototype.all = function() {
404
+ /**
405
+ * Convenience method for Connection#apply using a built-in default connection
406
+ * @arg sql
407
+ * @param {...*} params
408
+ * @param callback
409
+ * @return {void}
410
+ */
411
+ Database.prototype.all = function () {
162
412
  default_connection(this).all.apply(this.default_connection, arguments);
163
413
  return this;
164
414
  }
165
415
 
166
- Database.prototype.exec = function() {
416
+ /**
417
+ * Convenience method for Connection#exec using a built-in default connection
418
+ * @arg sql
419
+ * @param {...*} params
420
+ * @param callback
421
+ * @return {void}
422
+ */
423
+ Database.prototype.exec = function () {
167
424
  default_connection(this).exec.apply(this.default_connection, arguments);
168
425
  return this;
169
426
  }
170
427
 
171
- Database.prototype.register = function() {
428
+ /**
429
+ * Convenience method for Connection#register using a built-in default connection
430
+ * @arg name
431
+ * @arg return_type
432
+ * @arg fun
433
+ * @return {this}
434
+ */
435
+ Database.prototype.register = function () {
172
436
  default_connection(this).register.apply(this.default_connection, arguments);
173
437
  return this;
174
438
  }
175
439
 
176
- Database.prototype.unregister = function() {
440
+ /**
441
+ * Convenience method for Connection#unregister using a built-in default connection
442
+ * @arg name
443
+ * @return {this}
444
+ */
445
+ Database.prototype.unregister = function () {
177
446
  default_connection(this).unregister.apply(this.default_connection, arguments);
178
447
  return this;
179
448
  }
180
449
 
181
- Database.prototype.get = function() {
450
+ /**
451
+ * Not implemented
452
+ */
453
+ Database.prototype.get = function () {
182
454
  throw "get() is not implemented because it's evil";
183
455
  }
184
456
 
185
- Statement.prototype.get = function() {
457
+ /**
458
+ * Not implemented
459
+ */
460
+ Statement.prototype.get = function () {
186
461
  throw "get() is not implemented because it's evil";
187
462
  }
463
+
464
+ /**
465
+ * @method
466
+ * @arg sql
467
+ * @param {...*} params
468
+ * @param callback
469
+ * @return {void}
470
+ */
471
+ Statement.prototype.run;
472
+ /**
473
+ * @method
474
+ * @arg sql
475
+ * @param {...*} params
476
+ * @param callback
477
+ * @return {void}
478
+ */
479
+ Statement.prototype.all;
480
+ /**
481
+ * @method
482
+ * @arg sql
483
+ * @param {...*} params
484
+ * @param callback
485
+ * @return {void}
486
+ */
487
+ Statement.prototype.each;
488
+ /**
489
+ * @method
490
+ * @arg sql
491
+ * @param {...*} params
492
+ * @param callback
493
+ * @return {void}
494
+ */
495
+ Statement.prototype.finalize
496
+ /**
497
+ * @method
498
+ * @arg sql
499
+ * @param {...*} params
500
+ * @yield callback
501
+ */
502
+ Statement.prototype.stream;
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-dev960.0",
4
+ "version": "0.5.1-dev2.0",
5
5
  "description": "DuckDB node.js API",
6
6
  "gypfile": true,
7
7
  "dependencies": {
@@ -26,6 +26,8 @@
26
26
  },
27
27
  "devDependencies": {
28
28
  "aws-sdk": "^2.790.0",
29
+ "chai": "^4.3.6",
30
+ "jsdoc3-parser": "^2.0.0",
29
31
  "mocha": "^8.3.0"
30
32
  },
31
33
  "repository": {
@@ -31,8 +31,29 @@ struct ConnectTask : public Task {
31
31
 
32
32
  void DoWork() override {
33
33
  auto &connection = Get<Connection>();
34
+ if (!connection.database_ref || !connection.database_ref->database) {
35
+ return;
36
+ }
34
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);
35
54
  }
55
+
56
+ bool success = false;
36
57
  };
37
58
 
38
59
  Connection::Connection(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Connection>(info) {
@@ -78,7 +99,7 @@ struct JSArgs {
78
99
  duckdb::DataChunk *args;
79
100
  duckdb::Vector *result;
80
101
  bool done;
81
- std::string error;
102
+ duckdb::PreservedError error;
82
103
  };
83
104
 
84
105
  void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, std::nullptr_t *, JSArgs *jsargs) {
@@ -188,8 +209,10 @@ void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, std::nullptr_t *
188
209
  }
189
210
  }
190
211
  }
212
+ } catch (const duckdb::Exception &e) {
213
+ jsargs->error = duckdb::PreservedError(e);
191
214
  } catch (const std::exception &e) {
192
- jsargs->error = e.what();
215
+ jsargs->error = duckdb::PreservedError(e);
193
216
  }
194
217
  jsargs->done = true;
195
218
  }
@@ -219,8 +242,8 @@ struct RegisterTask : public Task {
219
242
  while (!jsargs.done) {
220
243
  std::this_thread::yield();
221
244
  }
222
- if (!jsargs.error.empty()) {
223
- throw duckdb::IOException(jsargs.error);
245
+ if (jsargs.error) {
246
+ jsargs.error.Throw();
224
247
  }
225
248
  };
226
249
 
@@ -327,18 +350,17 @@ struct ExecTask : public Task {
327
350
  return;
328
351
  }
329
352
 
330
- // thanks Mark
331
353
  for (duckdb::idx_t i = 0; i < statements.size(); i++) {
332
354
  auto res = connection.connection->Query(move(statements[i]));
333
- if (!res->success) {
355
+ if (res->HasError()) {
334
356
  success = false;
335
- error = res->error;
357
+ error = res->GetErrorObject();
336
358
  break;
337
359
  }
338
360
  }
339
361
  } catch (duckdb::ParserException &e) {
340
362
  success = false;
341
- error = e.what();
363
+ error = duckdb::PreservedError(e);
342
364
  return;
343
365
  }
344
366
  }
@@ -346,12 +368,12 @@ struct ExecTask : public Task {
346
368
  void Callback() override {
347
369
  auto env = object.Env();
348
370
  Napi::HandleScope scope(env);
349
- 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())});
350
372
  };
351
373
 
352
374
  std::string sql;
353
375
  bool success;
354
- std::string error;
376
+ duckdb::PreservedError error;
355
377
  };
356
378
 
357
379
  Napi::Value Connection::Exec(const Napi::CallbackInfo &info) {
package/src/database.cpp CHANGED
@@ -11,7 +11,7 @@ Napi::Object Database::Init(Napi::Env env, Napi::Object exports) {
11
11
 
12
12
  Napi::Function t = DefineClass(
13
13
  env, "Database",
14
- {InstanceMethod("close", &Database::Close), InstanceMethod("wait", &Database::Wait),
14
+ {InstanceMethod("close_internal", &Database::Close), InstanceMethod("wait", &Database::Wait),
15
15
  InstanceMethod("serialize", &Database::Serialize), InstanceMethod("parallelize", &Database::Parallelize),
16
16
  InstanceMethod("connect", &Database::Connect), InstanceMethod("interrupt", &Database::Interrupt)});
17
17
 
@@ -60,8 +60,10 @@ struct OpenTask : public Task {
60
60
  extension.Load(*Get<Database>().database);
61
61
  success = true;
62
62
 
63
+ } catch (const duckdb::Exception &ex) {
64
+ error = duckdb::PreservedError(ex);
63
65
  } catch (std::exception &ex) {
64
- error = ex.what();
66
+ error = duckdb::PreservedError(ex);
65
67
  }
66
68
  }
67
69
 
@@ -71,7 +73,7 @@ struct OpenTask : public Task {
71
73
 
72
74
  std::vector<napi_value> args;
73
75
  if (!success) {
74
- args.push_back(Utils::CreateError(env, error));
76
+ args.push_back(Utils::CreateError(env, error.Message()));
75
77
  } else {
76
78
  args.push_back(env.Null());
77
79
  }
@@ -83,7 +85,7 @@ struct OpenTask : public Task {
83
85
 
84
86
  std::string filename;
85
87
  duckdb::DBConfig duckdb_config;
86
- std::string error = "";
88
+ duckdb::PreservedError error;
87
89
  bool success = false;
88
90
  };
89
91
 
@@ -141,9 +143,7 @@ static void TaskExecuteCallback(napi_env e, void *data) {
141
143
  static void TaskCompleteCallback(napi_env e, napi_status status, void *data) {
142
144
  std::unique_ptr<TaskHolder> holder((TaskHolder *)data);
143
145
  holder->db->TaskComplete(e);
144
- if (holder->task->callback.Value().IsFunction()) {
145
- holder->task->Callback();
146
- }
146
+ holder->task->DoCallback();
147
147
  }
148
148
 
149
149
  void Database::TaskComplete(Napi::Env env) {