duckdb 0.4.1-dev211.0 → 0.4.1-dev2145.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,154 @@
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
+ * @arg sql
96
+ * @param {...*} params
97
+ * @param callback
98
+ * @return {void}
99
+ */
100
+ Connection.prototype.run = function (sql) {
12
101
  var statement = new Statement(this, sql);
13
102
  return statement.run.apply(statement, arguments);
14
103
  }
15
104
 
16
- Connection.prototype.all = function(sql) {
17
- var statement = new Statement(this,sql);
105
+ /**
106
+ * @arg sql
107
+ * @param {...*} params
108
+ * @param callback
109
+ * @return {void}
110
+ */
111
+ Connection.prototype.all = function (sql) {
112
+ var statement = new Statement(this, sql);
18
113
  return statement.all.apply(statement, arguments);
19
114
  }
20
115
 
21
- Connection.prototype.each = function(sql) {
116
+ /**
117
+ * @arg sql
118
+ * @param {...*} params
119
+ * @param callback
120
+ * @return {void}
121
+ */
122
+ Connection.prototype.each = function (sql) {
22
123
  var statement = new Statement(this, sql);
23
124
  return statement.each.apply(statement, arguments);
24
125
  }
25
126
 
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) {
127
+ /**
128
+ * @arg sql
129
+ * @param {...*} params
130
+ * @yields row chunks
131
+ */
132
+ Connection.prototype.stream = async function* (sql) {
133
+ const statement = new Statement(this, sql);
134
+ const queryResult = await statement.stream.apply(statement, arguments);
135
+ for await (const result of queryResult) {
136
+ yield result;
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Register a User Defined Function
142
+ *
143
+ * @arg name
144
+ * @arg return_type
145
+ * @arg fun
146
+ * @return {void}
147
+ * @note this follows the wasm udfs somewhat but is simpler because we can pass data much more cleanly
148
+ */
149
+ Connection.prototype.register = function (name, return_type, fun) {
28
150
  // TODO what if this throws an error somewhere? do we need a try/catch?
29
- return this.register_bulk(name, return_type, function(desc) {
151
+ return this.register_bulk(name, return_type, function (desc) {
30
152
  try {
31
153
  // Build an argument resolver
32
154
  const buildResolver = (arg) => {
@@ -126,62 +248,236 @@ Connection.prototype.register = function(name, return_type, fun) {
126
248
  desc.ret.data[i] = res;
127
249
  desc.ret.validity[i] = res === undefined || res === null ? 0 : 1;
128
250
  }
129
- } catch(error) { // work around recently fixed napi bug https://github.com/nodejs/node-addon-api/issues/912
251
+ } catch (error) { // work around recently fixed napi bug https://github.com/nodejs/node-addon-api/issues/912
130
252
  console.log(desc.ret);
131
253
  msg = error;
132
254
  if (typeof error == 'object' && 'message' in error) {
133
255
  msg = error.message
134
256
  }
135
- throw {name: 'DuckDB-UDF-Exception', message : msg};
257
+ throw { name: 'DuckDB-UDF-Exception', message: msg };
136
258
  }
137
259
  })
138
260
  }
139
261
 
140
- default_connection = function(o) {
262
+ /**
263
+ * @method
264
+ * @arg sql
265
+ * @param {...*} params
266
+ * @param callback
267
+ * @return {Statement}
268
+ */
269
+ Connection.prototype.prepare;
270
+ /**
271
+ * @method
272
+ * @arg sql
273
+ * @param {...*} params
274
+ * @param callback
275
+ * @return {void}
276
+ */
277
+ Connection.prototype.exec;
278
+ /**
279
+ * Register a User Defined Function
280
+ *
281
+ * @method
282
+ * @arg name
283
+ * @arg return_type
284
+ * @param callback
285
+ * @return {void}
286
+ */
287
+ Connection.prototype.register_bulk;
288
+ /**
289
+ * Unregister a User Defined Function
290
+ *
291
+ * @method
292
+ * @arg name
293
+ * @arg return_type
294
+ * @param callback
295
+ * @return {void}
296
+ */
297
+ Connection.prototype.unregister;
298
+
299
+ default_connection = function (o) {
141
300
  if (o.default_connection == undefined) {
142
301
  o.default_connection = new duckdb.Connection(o);
143
302
  }
144
- return(o.default_connection);
303
+ return o.default_connection;
145
304
  }
146
305
 
147
- Database.prototype.prepare = function() {
306
+
307
+ /**
308
+ * @method
309
+ * @param callback
310
+ * @return {void}
311
+ */
312
+ Database.prototype.close;
313
+
314
+ /**
315
+ * @method
316
+ * @param callback
317
+ * TODO: what does this do?
318
+ * @return {void}
319
+ */
320
+ Database.prototype.wait;
321
+
322
+ /**
323
+ * TODO: what does this do?
324
+ * @method
325
+ * @param callback
326
+ * @return {void}
327
+ */
328
+ Database.prototype.serialize;
329
+
330
+ /**
331
+ * TODO: what does this do?
332
+ * @method
333
+ * @param callback
334
+ * @return {void}
335
+ */
336
+ Database.prototype.parallelize;
337
+
338
+ /**
339
+ * @method
340
+ * @arg path the database to connect to, either a file path, or `:memory:`
341
+ * @return {Connection}
342
+ */
343
+ Database.prototype.connect;
344
+
345
+ /**
346
+ * TODO: what does this do?
347
+ * @method
348
+ * @param callback
349
+ * @return {void}
350
+ */
351
+ Database.prototype.interrupt;
352
+
353
+ /**
354
+ * @arg sql
355
+ * @return {Statement}
356
+ */
357
+ Database.prototype.prepare = function () {
148
358
  return default_connection(this).prepare.apply(this.default_connection, arguments);
149
359
  }
150
360
 
151
- Database.prototype.run = function() {
361
+ /**
362
+ * @arg sql
363
+ * @param {...*} params
364
+ * @param callback
365
+ * @return {void}
366
+ */
367
+ Database.prototype.run = function () {
152
368
  default_connection(this).run.apply(this.default_connection, arguments);
153
369
  return this;
154
370
  }
155
371
 
156
- Database.prototype.each = function() {
372
+ /**
373
+ * @arg sql
374
+ * @param {...*} params
375
+ * @param callback
376
+ * @return {void}
377
+ */
378
+ Database.prototype.each = function () {
157
379
  default_connection(this).each.apply(this.default_connection, arguments);
158
380
  return this;
159
381
  }
160
382
 
161
- Database.prototype.all = function() {
383
+ /**
384
+ * @arg sql
385
+ * @param {...*} params
386
+ * @param callback
387
+ * @return {void}
388
+ */
389
+ Database.prototype.all = function () {
162
390
  default_connection(this).all.apply(this.default_connection, arguments);
163
391
  return this;
164
392
  }
165
393
 
166
- Database.prototype.exec = function() {
394
+ /**
395
+ * @arg sql
396
+ * @param {...*} params
397
+ * @param callback
398
+ * @return {void}
399
+ */
400
+ Database.prototype.exec = function () {
167
401
  default_connection(this).exec.apply(this.default_connection, arguments);
168
402
  return this;
169
403
  }
170
404
 
171
- Database.prototype.register = function() {
405
+ /**
406
+ * Register a User Defined Function
407
+ *
408
+ * Convenience method for Connection#register
409
+ * @arg name
410
+ * @arg return_type
411
+ * @arg fun
412
+ * @return {this}
413
+ */
414
+ Database.prototype.register = function () {
172
415
  default_connection(this).register.apply(this.default_connection, arguments);
173
416
  return this;
174
417
  }
175
418
 
176
- Database.prototype.unregister = function() {
419
+ /**
420
+ * Unregister a User Defined Function
421
+ *
422
+ * Convenience method for Connection#unregister
423
+ * @arg name
424
+ * @return {this}
425
+ */
426
+ Database.prototype.unregister = function () {
177
427
  default_connection(this).unregister.apply(this.default_connection, arguments);
178
428
  return this;
179
429
  }
180
430
 
181
- Database.prototype.get = function() {
431
+ /**
432
+ * Not implemented
433
+ */
434
+ Database.prototype.get = function () {
182
435
  throw "get() is not implemented because it's evil";
183
436
  }
184
437
 
185
- Statement.prototype.get = function() {
438
+ /**
439
+ * Not implemented
440
+ */
441
+ Statement.prototype.get = function () {
186
442
  throw "get() is not implemented because it's evil";
187
443
  }
444
+
445
+ /**
446
+ * @method
447
+ * @arg sql
448
+ * @param {...*} params
449
+ * @param callback
450
+ * @return {void}
451
+ */
452
+ Statement.prototype.run;
453
+ /**
454
+ * @method
455
+ * @arg sql
456
+ * @param {...*} params
457
+ * @param callback
458
+ * @return {void}
459
+ */
460
+ Statement.prototype.all;
461
+ /**
462
+ * @method
463
+ * @arg sql
464
+ * @param {...*} params
465
+ * @param callback
466
+ * @return {void}
467
+ */
468
+ Statement.prototype.each;
469
+ /**
470
+ * @method
471
+ * @arg sql
472
+ * @param {...*} params
473
+ * @param callback
474
+ * @return {void}
475
+ */
476
+ Statement.prototype.finalize
477
+ /**
478
+ * @method
479
+ * @arg sql
480
+ * @param {...*} params
481
+ * @yield callback
482
+ */
483
+ 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-dev211.0",
4
+ "version": "0.4.1-dev2145.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": {
@@ -78,7 +78,7 @@ struct JSArgs {
78
78
  duckdb::DataChunk *args;
79
79
  duckdb::Vector *result;
80
80
  bool done;
81
- std::string error;
81
+ duckdb::PreservedError error;
82
82
  };
83
83
 
84
84
  void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, std::nullptr_t *, JSArgs *jsargs) {
@@ -188,8 +188,10 @@ void DuckDBNodeUDFLauncher(Napi::Env env, Napi::Function jsudf, std::nullptr_t *
188
188
  }
189
189
  }
190
190
  }
191
+ } catch (const duckdb::Exception &e) {
192
+ jsargs->error = duckdb::PreservedError(e);
191
193
  } catch (const std::exception &e) {
192
- jsargs->error = e.what();
194
+ jsargs->error = duckdb::PreservedError(e);
193
195
  }
194
196
  jsargs->done = true;
195
197
  }
@@ -207,7 +209,7 @@ struct RegisterTask : public Task {
207
209
  // here we can do only DuckDB stuff because we do not have a functioning env
208
210
 
209
211
  // Flatten all args to simplify udfs
210
- args.Normalify();
212
+ args.Flatten();
211
213
 
212
214
  JSArgs jsargs;
213
215
  jsargs.rows = args.size();
@@ -219,8 +221,8 @@ struct RegisterTask : public Task {
219
221
  while (!jsargs.done) {
220
222
  std::this_thread::yield();
221
223
  }
222
- if (!jsargs.error.empty()) {
223
- throw duckdb::IOException(jsargs.error);
224
+ if (jsargs.error) {
225
+ jsargs.error.Throw();
224
226
  }
225
227
  };
226
228
 
@@ -321,31 +323,36 @@ struct ExecTask : public Task {
321
323
  auto &connection = Get<Connection>();
322
324
 
323
325
  success = true;
324
- auto statements = connection.connection->ExtractStatements(sql);
325
- if (statements.empty()) {
326
- return;
327
- }
326
+ try {
327
+ auto statements = connection.connection->ExtractStatements(sql);
328
+ if (statements.empty()) {
329
+ return;
330
+ }
328
331
 
329
- // thanks Mark
330
- for (duckdb::idx_t i = 0; i < statements.size(); i++) {
331
- auto res = connection.connection->Query(move(statements[i]));
332
- if (!res->success) {
333
- success = false;
334
- error = res->error;
335
- 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
+ }
336
339
  }
340
+ } catch (duckdb::ParserException &e) {
341
+ success = false;
342
+ error = duckdb::PreservedError(e);
343
+ return;
337
344
  }
338
345
  }
339
346
 
340
347
  void Callback() override {
341
348
  auto env = object.Env();
342
349
  Napi::HandleScope scope(env);
343
- 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())});
344
351
  };
345
352
 
346
353
  std::string sql;
347
354
  bool success;
348
- std::string error;
355
+ duckdb::PreservedError error;
349
356
  };
350
357
 
351
358
  Napi::Value Connection::Exec(const Napi::CallbackInfo &info) {
@@ -14,7 +14,7 @@ Napi::Array EncodeDataChunk(Napi::Env env, duckdb::DataChunk &chunk, bool with_t
14
14
  // Make sure we only have flat vectors hereafter (for now)
15
15
  auto &chunk_vec = chunk.data[col_idx];
16
16
  if (with_data) {
17
- chunk_vec.Normalify(chunk.size());
17
+ chunk_vec.Flatten(chunk.size());
18
18
  }
19
19
 
20
20
  // Do a post-order DFS traversal
@@ -52,7 +52,7 @@ Napi::Array EncodeDataChunk(Napi::Env env, duckdb::DataChunk &chunk, bool with_t
52
52
 
53
53
  // Create validity vector
54
54
  if (with_data) {
55
- vec->Normalify(chunk.size());
55
+ vec->Flatten(chunk.size());
56
56
  auto &validity = duckdb::FlatVector::Validity(*vec);
57
57
  auto validity_buffer = Napi::Uint8Array::New(env, chunk.size());
58
58
  for (idx_t row_idx = 0; row_idx < chunk.size(); row_idx++) {
package/src/database.cpp CHANGED
@@ -1,4 +1,5 @@
1
1
  #include "duckdb_node.hpp"
2
+ #include "napi.h"
2
3
  #include "parquet-amalgamation.hpp"
3
4
 
4
5
  namespace node_duckdb {
@@ -22,23 +23,47 @@ Napi::Object Database::Init(Napi::Env env, Napi::Object exports) {
22
23
  }
23
24
 
24
25
  struct OpenTask : public Task {
25
- OpenTask(Database &database_, std::string filename_, bool read_only_, Napi::Function callback_)
26
- : Task(database_, callback_), filename(filename_), read_only(read_only_) {
26
+ OpenTask(Database &database_, std::string filename_, duckdb::AccessMode access_mode_, Napi::Object config_,
27
+ Napi::Function callback_)
28
+ : Task(database_, callback_), filename(filename_) {
29
+
30
+ duckdb_config.options.access_mode = access_mode_;
31
+ Napi::Env env = database_.Env();
32
+ Napi::HandleScope scope(env);
33
+
34
+ if (!config_.IsUndefined()) {
35
+ const Napi::Array config_names = config_.GetPropertyNames();
36
+
37
+ for (duckdb::idx_t config_idx = 0; config_idx < config_names.Length(); config_idx++) {
38
+ std::string key = config_names.Get(config_idx).As<Napi::String>();
39
+ std::string val = config_.Get(key).As<Napi::String>();
40
+ auto config_property = duckdb::DBConfig::GetOptionByName(key);
41
+ if (!config_property) {
42
+ Napi::TypeError::New(env, "Unrecognized configuration property" + key).ThrowAsJavaScriptException();
43
+ return;
44
+ }
45
+ try {
46
+ duckdb_config.SetOption(*config_property, duckdb::Value(val));
47
+ } catch (std::exception &e) {
48
+ Napi::TypeError::New(env, "Failed to set configuration option " + key + ": " + e.what())
49
+ .ThrowAsJavaScriptException();
50
+ return;
51
+ }
52
+ }
53
+ }
27
54
  }
28
55
 
29
56
  void DoWork() override {
30
57
  try {
31
- duckdb::DBConfig config;
32
- if (read_only) {
33
- config.access_mode = duckdb::AccessMode::READ_ONLY;
34
- }
35
- Get<Database>().database = duckdb::make_unique<duckdb::DuckDB>(filename, &config);
58
+ Get<Database>().database = duckdb::make_unique<duckdb::DuckDB>(filename, &duckdb_config);
36
59
  duckdb::ParquetExtension extension;
37
60
  extension.Load(*Get<Database>().database);
38
61
  success = true;
39
62
 
63
+ } catch (const duckdb::Exception &ex) {
64
+ error = duckdb::PreservedError(ex);
40
65
  } catch (std::exception &ex) {
41
- error = ex.what();
66
+ error = duckdb::PreservedError(ex);
42
67
  }
43
68
  }
44
69
 
@@ -48,7 +73,7 @@ struct OpenTask : public Task {
48
73
 
49
74
  std::vector<napi_value> args;
50
75
  if (!success) {
51
- args.push_back(Utils::CreateError(env, error));
76
+ args.push_back(Utils::CreateError(env, error.Message()));
52
77
  } else {
53
78
  args.push_back(env.Null());
54
79
  }
@@ -59,22 +84,35 @@ struct OpenTask : public Task {
59
84
  }
60
85
 
61
86
  std::string filename;
62
- bool read_only = false;
63
- std::string error = "";
87
+ duckdb::DBConfig duckdb_config;
88
+ duckdb::PreservedError error;
64
89
  bool success = false;
65
90
  };
66
91
 
67
- Database::Database(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Database>(info), task_inflight(false) {
92
+ Database::Database(const Napi::CallbackInfo &info)
93
+ : Napi::ObjectWrap<Database>(info), task_inflight(false), env(info.Env()) {
68
94
  auto env = info.Env();
95
+
69
96
  if (info.Length() < 1 || !info[0].IsString()) {
70
97
  Napi::TypeError::New(env, "Database location expected").ThrowAsJavaScriptException();
71
98
  return;
72
99
  }
73
100
  std::string filename = info[0].As<Napi::String>();
74
101
  unsigned int pos = 1;
102
+
103
+ duckdb::AccessMode access_mode = duckdb::AccessMode::AUTOMATIC;
104
+
75
105
  int mode = 0;
76
106
  if (info.Length() >= pos && info[pos].IsNumber() && Utils::OtherIsInt(info[pos].As<Napi::Number>())) {
77
107
  mode = info[pos++].As<Napi::Number>().Int32Value();
108
+ if (mode == DUCKDB_NODEJS_READONLY) {
109
+ access_mode = duckdb::AccessMode::READ_ONLY;
110
+ }
111
+ }
112
+
113
+ Napi::Object config;
114
+ if (info.Length() >= pos && info[pos].IsObject() && !info[pos].IsFunction()) {
115
+ config = info[pos++].As<Napi::Object>();
78
116
  }
79
117
 
80
118
  Napi::Function callback;
@@ -82,7 +120,11 @@ Database::Database(const Napi::CallbackInfo &info) : Napi::ObjectWrap<Database>(
82
120
  callback = info[pos++].As<Napi::Function>();
83
121
  }
84
122
 
85
- Schedule(env, duckdb::make_unique<OpenTask>(*this, filename, mode == DUCKDB_NODEJS_READONLY, callback));
123
+ Schedule(env, duckdb::make_unique<OpenTask>(*this, filename, access_mode, config, callback));
124
+ }
125
+
126
+ Database::~Database() {
127
+ Napi::MemoryManagement::AdjustExternalMemory(env, -bytes_allocated);
86
128
  }
87
129
 
88
130
  void Database::Schedule(Napi::Env env, std::unique_ptr<Task> task) {
@@ -101,9 +143,7 @@ static void TaskExecuteCallback(napi_env e, void *data) {
101
143
  static void TaskCompleteCallback(napi_env e, napi_status status, void *data) {
102
144
  std::unique_ptr<TaskHolder> holder((TaskHolder *)data);
103
145
  holder->db->TaskComplete(e);
104
- if (holder->task->callback.Value().IsFunction()) {
105
- holder->task->Callback();
106
- }
146
+ holder->task->DoCallback();
107
147
  }
108
148
 
109
149
  void Database::TaskComplete(Napi::Env env) {
@@ -112,6 +152,16 @@ void Database::TaskComplete(Napi::Env env) {
112
152
  task_inflight = false;
113
153
  }
114
154
  Process(env);
155
+
156
+ if (database) {
157
+ // Bookkeeping: tell node (and the node GC in particular) how much
158
+ // memory we're using, such that it can make better decisions on when to
159
+ // trigger collections.
160
+ auto &buffer_manager = duckdb::BufferManager::GetBufferManager(*database->instance);
161
+ auto current_bytes = buffer_manager.GetUsedMemory();
162
+ Napi::MemoryManagement::AdjustExternalMemory(env, current_bytes - bytes_allocated);
163
+ bytes_allocated = current_bytes;
164
+ }
115
165
  }
116
166
 
117
167
  void Database::Process(Napi::Env env) {