@nxtedition/rocksdb 7.0.61 → 7.0.64

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/binding.cc CHANGED
@@ -12,11 +12,12 @@
12
12
  #include <rocksdb/filter_policy.h>
13
13
  #include <rocksdb/merge_operator.h>
14
14
  #include <rocksdb/options.h>
15
+ #include <rocksdb/slice.h>
15
16
  #include <rocksdb/slice_transform.h>
17
+ #include <rocksdb/status.h>
16
18
  #include <rocksdb/table.h>
17
19
  #include <rocksdb/write_batch.h>
18
20
 
19
- #include <array>
20
21
  #include <memory>
21
22
  #include <optional>
22
23
  #include <set>
@@ -25,6 +26,7 @@
25
26
  #include <vector>
26
27
 
27
28
  #include "max_rev_operator.h"
29
+ #include "util.h"
28
30
 
29
31
  class NullLogger : public rocksdb::Logger {
30
32
  public:
@@ -37,298 +39,6 @@ struct Database;
37
39
  struct Iterator;
38
40
  struct Updates;
39
41
 
40
- #define NAPI_STATUS_RETURN(call) \
41
- { \
42
- auto _status = (call); \
43
- if (_status != napi_ok) { \
44
- return _status; \
45
- } \
46
- }
47
-
48
- #define ROCKS_STATUS_RETURN(call) \
49
- { \
50
- auto _status = (call); \
51
- if (!_status.ok()) { \
52
- return _status; \
53
- } \
54
- }
55
-
56
- #define ROCKS_STATUS_THROWS(call) \
57
- { \
58
- auto _status = (call); \
59
- if (!_status.ok()) { \
60
- napi_throw(env, ToError(env, _status)); \
61
- return NULL; \
62
- } \
63
- }
64
-
65
- static napi_value CreateError(napi_env env, const std::optional<std::string_view>& code, const std::string_view& msg) {
66
- napi_value codeValue = nullptr;
67
- if (code) {
68
- NAPI_STATUS_THROWS(napi_create_string_utf8(env, code->data(), code->size(), &codeValue));
69
- }
70
- napi_value msgValue;
71
- NAPI_STATUS_THROWS(napi_create_string_utf8(env, msg.data(), msg.size(), &msgValue));
72
- napi_value error;
73
- NAPI_STATUS_THROWS(napi_create_error(env, codeValue, msgValue, &error));
74
- return error;
75
- }
76
-
77
- static bool HasProperty(napi_env env, napi_value obj, const std::string_view& key) {
78
- bool has = false;
79
- napi_has_named_property(env, obj, key.data(), &has);
80
- return has;
81
- }
82
-
83
- static napi_value GetProperty(napi_env env, napi_value obj, const std::string_view& key) {
84
- napi_value value = nullptr;
85
- napi_get_named_property(env, obj, key.data(), &value);
86
- return value;
87
- }
88
-
89
- static std::optional<bool> BooleanProperty(napi_env env, napi_value obj, const std::string_view& key) {
90
- if (HasProperty(env, obj, key.data())) {
91
- const auto value = GetProperty(env, obj, key.data());
92
- bool result;
93
- napi_get_value_bool(env, value, &result);
94
- return result;
95
- }
96
-
97
- return {};
98
- }
99
-
100
- static bool EncodingIsBuffer(napi_env env, napi_value obj, const std::string_view& option) {
101
- napi_value value;
102
- size_t size;
103
-
104
- if (napi_get_named_property(env, obj, option.data(), &value) == napi_ok &&
105
- napi_get_value_string_utf8(env, value, nullptr, 0, &size) == napi_ok) {
106
- // Value is either "buffer" or "utf8" so we can tell them apart just by size
107
- return size == 6;
108
- }
109
-
110
- return false;
111
- }
112
-
113
- static std::optional<uint32_t> Uint32Property(napi_env env, napi_value obj, const std::string_view& key) {
114
- if (HasProperty(env, obj, key.data())) {
115
- const auto value = GetProperty(env, obj, key.data());
116
- uint32_t result;
117
- napi_get_value_uint32(env, value, &result);
118
- return result;
119
- }
120
-
121
- return {};
122
- }
123
-
124
- static std::optional<int> Int32Property(napi_env env, napi_value obj, const std::string_view& key) {
125
- if (HasProperty(env, obj, key.data())) {
126
- const auto value = GetProperty(env, obj, key.data());
127
- int result;
128
- napi_get_value_int32(env, value, &result);
129
- return result;
130
- }
131
-
132
- return {};
133
- }
134
-
135
- static napi_status ToString(napi_env env, napi_value from, std::string& to) {
136
- napi_valuetype type;
137
- NAPI_STATUS_RETURN(napi_typeof(env, from, &type));
138
-
139
- if (type == napi_string) {
140
- size_t length = 0;
141
- NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, nullptr, 0, &length));
142
- to.resize(length, '\0');
143
- NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, &to[0], length + 1, &length));
144
- } else {
145
- bool isBuffer;
146
- NAPI_STATUS_RETURN(napi_is_buffer(env, from, &isBuffer));
147
-
148
- if (isBuffer) {
149
- char* buf = nullptr;
150
- size_t length = 0;
151
- NAPI_STATUS_RETURN(napi_get_buffer_info(env, from, reinterpret_cast<void**>(&buf), &length));
152
- to.assign(buf, length);
153
- } else {
154
- return napi_invalid_arg;
155
- }
156
- }
157
-
158
- return napi_ok;
159
- }
160
-
161
- void noop(void* arg1, void* arg2) {}
162
-
163
- // TODO (fix): Should use rocksdb::Slice since "to" cannot outlive "from".
164
- static napi_status ToString(napi_env env, napi_value from, rocksdb::PinnableSlice& to) {
165
- napi_valuetype type;
166
- NAPI_STATUS_RETURN(napi_typeof(env, from, &type));
167
-
168
- if (type == napi_string) {
169
- size_t length = 0;
170
- NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, nullptr, 0, &length));
171
- to.GetSelf()->resize(length, '\0');
172
- NAPI_STATUS_RETURN(napi_get_value_string_utf8(env, from, &(*to.GetSelf())[0], length + 1, &length));
173
- to.PinSelf();
174
- } else {
175
- bool isBuffer;
176
- NAPI_STATUS_RETURN(napi_is_buffer(env, from, &isBuffer));
177
-
178
- if (isBuffer) {
179
- char* buf = nullptr;
180
- size_t length = 0;
181
- NAPI_STATUS_RETURN(napi_get_buffer_info(env, from, reinterpret_cast<void**>(&buf), &length));
182
-
183
- // TODO (fix): Should extend life of "from". Or "to" should be a non-pinnable slice.
184
- to.PinSlice(rocksdb::Slice(buf, length), noop, nullptr, nullptr);
185
- } else {
186
- return napi_invalid_arg;
187
- }
188
- }
189
-
190
- return napi_ok;
191
- }
192
-
193
- static std::optional<std::string> StringProperty(napi_env env, napi_value opts, const std::string_view& name) {
194
- if (HasProperty(env, opts, name)) {
195
- const auto property = GetProperty(env, opts, name);
196
- std::string value;
197
- ToString(env, property, value);
198
- return value;
199
- }
200
- return {};
201
- }
202
-
203
- static napi_status CallFunction(napi_env env, napi_value callback, const int argc, napi_value* argv) {
204
- napi_value global;
205
-
206
- NAPI_STATUS_RETURN(napi_get_global(env, &global));
207
- NAPI_STATUS_RETURN(napi_call_function(env, global, callback, argc, argv, nullptr));
208
-
209
- return napi_ok;
210
- }
211
-
212
- static napi_value ToError(napi_env env, const rocksdb::Status& status) {
213
- if (status.ok()) {
214
- return 0;
215
- }
216
-
217
- const auto msg = status.ToString();
218
-
219
- if (status.IsNotFound()) {
220
- return CreateError(env, "LEVEL_NOT_FOUND", msg);
221
- } else if (status.IsCorruption()) {
222
- return CreateError(env, "LEVEL_CORRUPTION", msg);
223
- } else if (status.IsTryAgain()) {
224
- return CreateError(env, "LEVEL_TRYAGAIN", msg);
225
- } else if (status.IsIOError()) {
226
- if (msg.find("IO error: lock ") != std::string::npos) { // env_posix.cc
227
- return CreateError(env, "LEVEL_LOCKED", msg);
228
- } else if (msg.find("IO error: LockFile ") != std::string::npos) { // env_win.cc
229
- return CreateError(env, "LEVEL_LOCKED", msg);
230
- } else if (msg.find("IO error: While lock file") != std::string::npos) { // env_mac.cc
231
- return CreateError(env, "LEVEL_LOCKED", msg);
232
- } else {
233
- return CreateError(env, "LEVEL_IO_ERROR", msg);
234
- }
235
- }
236
-
237
- return CreateError(env, {}, msg);
238
- }
239
-
240
- template <typename T>
241
- static void Finalize(napi_env env, void* data, void* hint) {
242
- if (hint) {
243
- delete reinterpret_cast<T*>(hint);
244
- }
245
- }
246
-
247
- template <typename T>
248
- napi_status Convert(napi_env env, T&& s, bool asBuffer, napi_value& result) {
249
- if (!s) {
250
- return napi_get_null(env, &result);
251
- } else if (asBuffer) {
252
- using Y = typename std::decay<decltype(*s)>::type;
253
- auto ptr = new Y(std::move(*s));
254
- return napi_create_external_buffer(env, ptr->size(), const_cast<char*>(ptr->data()), Finalize<Y>, ptr, &result);
255
- } else {
256
- return napi_create_string_utf8(env, s->data(), s->size(), &result);
257
- }
258
- }
259
-
260
- /**
261
- * Base worker class. Handles the async work. Derived classes can override the
262
- * following virtual methods (listed in the order in which they're called):
263
- *
264
- * - Execute (abstract, worker pool thread): main work
265
- * - OnOk (main thread): call JS callback on success
266
- * - OnError (main thread): call JS callback on error
267
- * - Destroy (main thread): do cleanup regardless of success
268
- */
269
- struct Worker {
270
- Worker(napi_env env, Database* database, napi_value callback, const std::string& resourceName) : database_(database) {
271
- NAPI_STATUS_THROWS_VOID(napi_create_reference(env, callback, 1, &callbackRef_));
272
- napi_value asyncResourceName;
273
- NAPI_STATUS_THROWS_VOID(napi_create_string_utf8(env, resourceName.data(), resourceName.size(), &asyncResourceName));
274
- NAPI_STATUS_THROWS_VOID(
275
- napi_create_async_work(env, callback, asyncResourceName, Worker::Execute, Worker::Complete, this, &asyncWork_));
276
- }
277
-
278
- virtual ~Worker() {}
279
-
280
- static void Execute(napi_env env, void* data) {
281
- auto self = reinterpret_cast<Worker*>(data);
282
- self->status_ = self->Execute(*self->database_);
283
- }
284
-
285
- static void Complete(napi_env env, napi_status status, void* data) {
286
- auto self = reinterpret_cast<Worker*>(data);
287
-
288
- // TODO (fix): napi status handling...
289
-
290
- napi_value callback;
291
- napi_get_reference_value(env, self->callbackRef_, &callback);
292
-
293
- if (self->status_.ok()) {
294
- self->OnOk(env, callback);
295
- } else {
296
- self->OnError(env, callback, ToError(env, self->status_));
297
- }
298
-
299
- self->Destroy(env);
300
-
301
- napi_delete_reference(env, self->callbackRef_);
302
- napi_delete_async_work(env, self->asyncWork_);
303
-
304
- delete self;
305
- }
306
-
307
- virtual rocksdb::Status Execute(Database& database) = 0;
308
-
309
- virtual napi_status OnOk(napi_env env, napi_value callback) {
310
- napi_value argv[1];
311
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
312
-
313
- return CallFunction(env, callback, 1, &argv[0]);
314
- }
315
-
316
- virtual napi_status OnError(napi_env env, napi_value callback, napi_value err) {
317
- return CallFunction(env, callback, 1, &err);
318
- }
319
-
320
- virtual void Destroy(napi_env env) {}
321
-
322
- void Queue(napi_env env) { napi_queue_async_work(env, asyncWork_); }
323
-
324
- Database* database_;
325
-
326
- private:
327
- napi_ref callbackRef_;
328
- napi_async_work asyncWork_;
329
- rocksdb::Status status_;
330
- };
331
-
332
42
  struct ColumnFamily {
333
43
  napi_ref ref;
334
44
  napi_value val;
@@ -336,79 +46,69 @@ struct ColumnFamily {
336
46
  rocksdb::ColumnFamilyDescriptor descriptor;
337
47
  };
338
48
 
339
- struct Database {
340
- void AttachIterator(napi_env env, Iterator* iterator) {
341
- iterators_.insert(iterator);
342
- IncrementPriorityWork(env);
343
- }
344
-
345
- void DetachIterator(napi_env env, Iterator* iterator) {
346
- iterators_.erase(iterator);
347
- DecrementPriorityWork(env);
348
- }
349
-
350
- void AttachUpdates(napi_env env, Updates* update) {
351
- updates_.insert(update);
352
- IncrementPriorityWork(env);
353
- }
49
+ struct Closable {
50
+ virtual ~Closable() {}
51
+ virtual rocksdb::Status Close() = 0;
52
+ };
354
53
 
355
- void DetachUpdates(napi_env env, Updates* update) {
356
- updates_.erase(update);
357
- DecrementPriorityWork(env);
358
- }
54
+ struct Database final {
55
+ ~Database() { assert(!db); }
359
56
 
360
- void IncrementPriorityWork(napi_env env) { napi_reference_ref(env, priorityRef_, &priorityWork_); }
57
+ rocksdb::Status Close() {
58
+ if (!db) {
59
+ return rocksdb::Status::OK();
60
+ }
361
61
 
362
- void DecrementPriorityWork(napi_env env) {
363
- napi_reference_unref(env, priorityRef_, &priorityWork_);
62
+ for (auto closable : closables) {
63
+ closable->Close();
64
+ }
65
+ closables.clear();
364
66
 
365
- if (priorityWork_ == 0 && pendingCloseWorker_) {
366
- pendingCloseWorker_->Queue(env);
367
- pendingCloseWorker_ = nullptr;
67
+ for (auto& [id, column] : columns) {
68
+ db->DestroyColumnFamilyHandle(column.handle);
368
69
  }
369
- }
70
+ columns.clear();
370
71
 
371
- bool HasPriorityWork() const { return priorityWork_ > 0; }
72
+ db->FlushWAL(true);
372
73
 
373
- std::unique_ptr<rocksdb::DB> db_;
374
- Worker* pendingCloseWorker_;
375
- std::set<Iterator*> iterators_;
376
- std::set<Updates*> updates_;
377
- std::map<int32_t, ColumnFamily> columns_;
378
- napi_ref priorityRef_;
74
+ auto db2 = std::move(db);
75
+ return db2->Close();
76
+ }
379
77
 
380
- private:
381
- uint32_t priorityWork_ = 0;
78
+ std::unique_ptr<rocksdb::DB> db;
79
+ std::set<Closable*> closables;
80
+ std::map<int32_t, ColumnFamily> columns;
382
81
  };
383
82
 
384
83
  enum BatchOp { Empty, Put, Delete, Merge, Data };
385
84
 
386
85
  struct BatchEntry {
387
86
  BatchOp op = BatchOp::Empty;
388
- std::optional<std::string> key;
389
- std::optional<std::string> val;
390
- std::optional<ColumnFamily> column;
87
+ std::optional<std::string> key = std::nullopt;
88
+ std::optional<std::string> val = std::nullopt;
89
+ std::optional<ColumnFamily> column = std::nullopt;
391
90
  };
392
91
 
393
92
  struct BatchIterator : public rocksdb::WriteBatch::Handler {
394
93
  BatchIterator(Database* database,
395
- bool keys = true,
396
- bool values = true,
397
- bool data = true,
398
- const rocksdb::ColumnFamilyHandle* column = nullptr,
399
- bool keyAsBuffer = false,
400
- bool valueAsBuffer = false)
94
+ const bool keys,
95
+ const bool values,
96
+ const bool data,
97
+ const rocksdb::ColumnFamilyHandle* column,
98
+ const Encoding keyEncoding,
99
+ const Encoding valueEncoding)
401
100
  : database_(database),
402
101
  keys_(keys),
403
102
  values_(values),
404
103
  data_(data),
405
104
  column_(column),
406
- keyAsBuffer_(keyAsBuffer),
407
- valueAsBuffer_(valueAsBuffer) {}
105
+ keyEncoding_(keyEncoding),
106
+ valueEncoding_(valueEncoding) {}
408
107
 
409
108
  napi_status Iterate(napi_env env, const rocksdb::WriteBatch& batch, napi_value* result) {
410
109
  cache_.reserve(batch.Count());
411
- batch.Iterate(this); // TODO (fix): Error?
110
+
111
+ ROCKS_STATUS_RETURN_NAPI(batch.Iterate(this));
412
112
 
413
113
  napi_value putStr;
414
114
  NAPI_STATUS_RETURN(napi_create_string_utf8(env, "put", NAPI_AUTO_LENGTH, &putStr));
@@ -443,11 +143,11 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
443
143
  NAPI_STATUS_RETURN(napi_set_element(env, *result, n * 4 + 0, op));
444
144
 
445
145
  napi_value key;
446
- NAPI_STATUS_RETURN(Convert(env, cache_[n].key, keyAsBuffer_, key));
146
+ NAPI_STATUS_RETURN(Convert(env, cache_[n].key, keyEncoding_, key));
447
147
  NAPI_STATUS_RETURN(napi_set_element(env, *result, n * 4 + 1, key));
448
148
 
449
149
  napi_value val;
450
- NAPI_STATUS_RETURN(Convert(env, cache_[n].val, valueAsBuffer_, val));
150
+ NAPI_STATUS_RETURN(Convert(env, cache_[n].val, valueEncoding_, val));
451
151
  NAPI_STATUS_RETURN(napi_set_element(env, *result, n * 4 + 2, val));
452
152
 
453
153
  // TODO (fix)
@@ -455,6 +155,8 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
455
155
  NAPI_STATUS_RETURN(napi_set_element(env, *result, n * 4 + 3, nullVal));
456
156
  }
457
157
 
158
+ cache_.clear();
159
+
458
160
  return napi_ok;
459
161
  }
460
162
 
@@ -463,9 +165,7 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
463
165
  return rocksdb::Status::OK();
464
166
  }
465
167
 
466
- BatchEntry entry;
467
-
468
- entry.op = BatchOp::Put;
168
+ BatchEntry entry = {BatchOp::Put};
469
169
 
470
170
  if (keys_) {
471
171
  entry.key = key.ToStringView();
@@ -475,8 +175,8 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
475
175
  entry.val = value.ToStringView();
476
176
  }
477
177
 
478
- // if (database_ && database_->columns_.find(column_family_id) != database_->columns_.end()) {
479
- // entry.column = database_->columns_[column_family_id];
178
+ // if (database_ && database_->columns.find(column_family_id) != database_->columns.end()) {
179
+ // entry.column = database_->columns[column_family_id];
480
180
  // }
481
181
 
482
182
  cache_.push_back(entry);
@@ -489,16 +189,14 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
489
189
  return rocksdb::Status::OK();
490
190
  }
491
191
 
492
- BatchEntry entry;
493
-
494
- entry.op = BatchOp::Delete;
192
+ BatchEntry entry = {BatchOp::Delete};
495
193
 
496
194
  if (keys_) {
497
195
  entry.key = key.ToStringView();
498
196
  }
499
197
 
500
- // if (database_ && database_->columns_.find(column_family_id) != database_->columns_.end()) {
501
- // entry.column = database_->columns_[column_family_id];
198
+ // if (database_ && database_->columns.find(column_family_id) != database_->columns.end()) {
199
+ // entry.column = database_->columns[column_family_id];
502
200
  // }
503
201
 
504
202
  cache_.push_back(entry);
@@ -511,9 +209,7 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
511
209
  return rocksdb::Status::OK();
512
210
  }
513
211
 
514
- BatchEntry entry;
515
-
516
- entry.op = BatchOp::Merge;
212
+ BatchEntry entry = {BatchOp::Merge};
517
213
 
518
214
  if (keys_) {
519
215
  entry.key = key.ToStringView();
@@ -523,8 +219,8 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
523
219
  entry.val = value.ToStringView();
524
220
  }
525
221
 
526
- // if (database_ && database_->columns_.find(column_family_id) != database_->columns_.end()) {
527
- // entry.column = database_->columns_[column_family_id];
222
+ // if (database_ && database_->columns.find(column_family_id) != database_->columns.end()) {
223
+ // entry.column = database_->columns[column_family_id];
528
224
  // }
529
225
 
530
226
  cache_.push_back(entry);
@@ -537,9 +233,7 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
537
233
  return;
538
234
  }
539
235
 
540
- BatchEntry entry;
541
-
542
- entry.op = BatchOp::Data;
236
+ BatchEntry entry = {BatchOp::Data};
543
237
 
544
238
  entry.val = data.ToStringView();
545
239
 
@@ -550,40 +244,47 @@ struct BatchIterator : public rocksdb::WriteBatch::Handler {
550
244
 
551
245
  private:
552
246
  Database* database_;
553
- bool keys_;
554
- bool values_;
555
- bool data_;
247
+ const bool keys_;
248
+ const bool values_;
249
+ const bool data_;
556
250
  const rocksdb::ColumnFamilyHandle* column_;
557
- bool keyAsBuffer_;
558
- bool valueAsBuffer_;
251
+ const Encoding keyEncoding_;
252
+ const Encoding valueEncoding_;
559
253
  std::vector<BatchEntry> cache_;
560
254
  };
561
255
 
562
- struct Updates : public BatchIterator {
256
+ struct Updates : public BatchIterator, public Closable {
563
257
  Updates(Database* database,
564
- int64_t seqNumber,
565
- bool keys,
566
- bool values,
567
- bool data,
258
+ const int64_t seqNumber,
259
+ const bool keys,
260
+ const bool values,
261
+ const bool data,
568
262
  const rocksdb::ColumnFamilyHandle* column,
569
- bool keyAsBuffer,
570
- bool valueAsBuffer)
571
- : BatchIterator(database, keys, values, data, column, keyAsBuffer, valueAsBuffer),
263
+ const Encoding keyEncoding,
264
+ const Encoding valueEncoding)
265
+ : BatchIterator(database, keys, values, data, column, keyEncoding, valueEncoding),
572
266
  database_(database),
573
267
  start_(seqNumber) {}
574
268
 
575
- void Close() { iterator_.reset(); }
269
+ virtual ~Updates() { assert(!iterator_); }
270
+
271
+ rocksdb::Status Close() override {
272
+ iterator_.reset();
273
+ return rocksdb::Status::OK();
274
+ }
576
275
 
577
- void Attach(napi_env env, napi_value context) {
578
- napi_create_reference(env, context, 1, &ref_);
579
- database_->AttachUpdates(env, this);
276
+ napi_status Attach(napi_env env, napi_value context) {
277
+ NAPI_STATUS_RETURN(napi_create_reference(env, context, 1, &ref_));
278
+ database_->closables.insert(this);
279
+ return napi_ok;
580
280
  }
581
281
 
582
- void Detach(napi_env env) {
583
- database_->DetachUpdates(env, this);
282
+ napi_status Detach(napi_env env) {
283
+ database_->closables.erase(this);
584
284
  if (ref_) {
585
- napi_delete_reference(env, ref_);
285
+ NAPI_STATUS_RETURN(napi_delete_reference(env, ref_));
586
286
  }
287
+ return napi_ok;
587
288
  }
588
289
 
589
290
  Database* database_;
@@ -594,7 +295,7 @@ struct Updates : public BatchIterator {
594
295
  napi_ref ref_ = nullptr;
595
296
  };
596
297
 
597
- struct BaseIterator {
298
+ struct BaseIterator : public Closable {
598
299
  BaseIterator(Database* database,
599
300
  rocksdb::ColumnFamilyHandle* column,
600
301
  const bool reverse,
@@ -667,9 +368,10 @@ struct BaseIterator {
667
368
  }
668
369
  }
669
370
 
670
- void Close() {
371
+ rocksdb::Status Close() override {
671
372
  snapshot_.reset();
672
373
  iterator_.reset();
374
+ return rocksdb::Status::OK();
673
375
  }
674
376
 
675
377
  bool Valid() const {
@@ -724,7 +426,7 @@ struct BaseIterator {
724
426
  readOptions.async_io = true;
725
427
  readOptions.adaptive_readahead = true;
726
428
 
727
- iterator_.reset(database_->db_->NewIterator(readOptions, column_));
429
+ iterator_.reset(database_->db->NewIterator(readOptions, column_));
728
430
  }
729
431
 
730
432
  int count_ = 0;
@@ -748,33 +450,35 @@ struct Iterator final : public BaseIterator {
748
450
  const std::optional<std::string>& gt,
749
451
  const std::optional<std::string>& gte,
750
452
  const bool fillCache,
751
- const bool keyAsBuffer,
752
- const bool valueAsBuffer,
453
+ const Encoding keyEncoding,
454
+ const Encoding valueEncoding,
753
455
  const size_t highWaterMarkBytes,
754
456
  std::shared_ptr<const rocksdb::Snapshot> snapshot)
755
457
  : BaseIterator(database, column, reverse, lt, lte, gt, gte, limit, fillCache, snapshot),
756
458
  keys_(keys),
757
459
  values_(values),
758
- keyAsBuffer_(keyAsBuffer),
759
- valueAsBuffer_(valueAsBuffer),
460
+ keyEncoding_(keyEncoding),
461
+ valueEncoding_(valueEncoding),
760
462
  highWaterMarkBytes_(highWaterMarkBytes) {}
761
463
 
762
- void Attach(napi_env env, napi_value context) {
763
- napi_create_reference(env, context, 1, &ref_);
764
- database_->AttachIterator(env, this);
464
+ napi_status Attach(napi_env env, napi_value context) {
465
+ NAPI_STATUS_RETURN(napi_create_reference(env, context, 1, &ref_));
466
+ database_->closables.insert(this);
467
+ return napi_ok;
765
468
  }
766
469
 
767
- void Detach(napi_env env) {
768
- database_->DetachIterator(env, this);
470
+ napi_status Detach(napi_env env) {
471
+ database_->closables.erase(this);
769
472
  if (ref_) {
770
- napi_delete_reference(env, ref_);
473
+ NAPI_STATUS_RETURN(napi_delete_reference(env, ref_));
771
474
  }
475
+ return napi_ok;
772
476
  }
773
477
 
774
478
  const bool keys_;
775
479
  const bool values_;
776
- const bool keyAsBuffer_;
777
- const bool valueAsBuffer_;
480
+ const Encoding keyEncoding_;
481
+ const Encoding valueEncoding_;
778
482
  const size_t highWaterMarkBytes_;
779
483
  bool first_ = true;
780
484
 
@@ -782,152 +486,20 @@ struct Iterator final : public BaseIterator {
782
486
  napi_ref ref_ = nullptr;
783
487
  };
784
488
 
785
- static napi_status GetColumnFamily(Database* database,
786
- napi_env env,
787
- napi_value options,
788
- rocksdb::ColumnFamilyHandle** column) {
789
- bool hasColumn = false;
790
- NAPI_STATUS_RETURN(napi_has_named_property(env, options, "column", &hasColumn));
791
-
792
- if (hasColumn) {
793
- napi_value value = nullptr;
794
- NAPI_STATUS_RETURN(napi_get_named_property(env, options, "column", &value));
795
-
796
- bool nully = false;
797
-
798
- napi_value nullVal;
799
- NAPI_STATUS_RETURN(napi_get_null(env, &nullVal));
800
- NAPI_STATUS_RETURN(napi_strict_equals(env, nullVal, value, &nully));
801
- if (nully) {
802
- *column = nullptr;
803
- return napi_ok;
804
- }
805
-
806
- napi_value undefinedVal;
807
- NAPI_STATUS_RETURN(napi_get_undefined(env, &undefinedVal));
808
- NAPI_STATUS_RETURN(napi_strict_equals(env, undefinedVal, value, &nully));
809
- if (nully) {
810
- *column = nullptr;
811
- return napi_ok;
812
- }
813
-
814
- NAPI_STATUS_RETURN(napi_get_value_external(env, value, reinterpret_cast<void**>(column)));
815
- } else if (database) {
816
- *column = database->db_->DefaultColumnFamily();
817
- } else {
818
- *column = nullptr;
819
- }
820
-
821
- return napi_ok;
822
- }
823
-
824
- template <typename State,
825
- typename ExecuteType = std::function<rocksdb::Status(State& state, Database& database)>,
826
- typename OnOkType = std::function<napi_status(State& state, napi_env env, napi_value callback)>>
827
- struct GenericWorker final : public Worker {
828
- template <typename T1, typename T2>
829
- GenericWorker(const std::string& name,
830
- napi_env env,
831
- napi_value callback,
832
- Database* database,
833
- bool priority,
834
- T1&& execute,
835
- T2&& onOK)
836
- : Worker(env, database, callback, name),
837
- priority_(priority),
838
- execute_(std::forward<T1>(execute)),
839
- onOk_(std::forward<T2>(onOK)) {
840
- if (priority_) {
841
- database_->IncrementPriorityWork(env);
842
- }
843
- }
844
-
845
- rocksdb::Status Execute(Database& database) override { return execute_(state_, database); }
846
-
847
- napi_status OnOk(napi_env env, napi_value callback) override { return onOk_(state_, env, callback); }
848
-
849
- void Destroy(napi_env env) override {
850
- if (priority_) {
851
- database_->DecrementPriorityWork(env);
852
- }
853
- Worker::Destroy(env);
854
- }
855
-
856
- private:
857
- State state_;
858
- bool priority_ = false;
859
- ExecuteType execute_;
860
- OnOkType onOk_;
861
- };
862
-
863
- template <typename State, typename T1, typename T2>
864
- Worker* createWorker(const std::string& name,
865
- napi_env env,
866
- napi_value callback,
867
- Database* database,
868
- bool priority,
869
- T1&& execute,
870
- T2&& onOk) {
871
- return new GenericWorker<State, typename std::decay<T1>::type, typename std::decay<T2>::type>(
872
- name, env, callback, database, priority, std::forward<T1>(execute), std::forward<T2>(onOk));
873
- }
874
-
875
- template <typename State, typename T1, typename T2>
876
- void runWorker(const std::string& name,
877
- napi_env env,
878
- napi_value callback,
879
- Database* database,
880
- bool priority,
881
- T1&& execute,
882
- T2&& onOk) {
883
- auto worker = new GenericWorker<State, typename std::decay<T1>::type, typename std::decay<T2>::type>(
884
- name, env, callback, database, priority, std::forward<T1>(execute), std::forward<T2>(onOk));
885
- worker->Queue(env);
886
- }
887
-
888
- /**
889
- * Hook for when the environment exits. This hook will be called after
890
- * already-scheduled napi_async_work items have finished, which gives us
891
- * the guarantee that no db operations will be in-flight at this time.
892
- */
893
489
  static void env_cleanup_hook(void* arg) {
894
490
  auto database = reinterpret_cast<Database*>(arg);
895
-
896
- // Do everything that db_close() does but synchronously. We're expecting that
897
- // GC did not (yet) collect the database because that would be a user mistake
898
- // (not closing their db) made during the lifetime of the environment. That's
899
- // different from an environment being torn down (like the main process or a
900
- // worker thread) where it's our responsibility to clean up. Note also, the
901
- // following code must be a safe noop if called before db_open() or after
902
- // db_close().
903
- if (database && database->db_) {
904
- for (auto& it : database->iterators_) {
905
- // TODO: does not do `napi_delete_reference`. Problem?
906
- it->Close();
907
- }
908
-
909
- for (auto& it : database->updates_) {
910
- // TODO: does not do `napi_delete_reference`. Problem?
911
- it->Close();
912
- }
913
-
914
- for (auto& it : database->columns_) {
915
- database->db_->DestroyColumnFamilyHandle(it.second.handle);
916
- }
917
-
918
- // Having closed the iterators (and released snapshots) we can safely close.
919
- database->db_->Close();
491
+ if (database) {
492
+ database->Close();
920
493
  }
921
494
  }
922
495
 
923
496
  static void FinalizeDatabase(napi_env env, void* data, void* hint) {
924
497
  if (data) {
925
498
  auto database = reinterpret_cast<Database*>(data);
499
+ database->Close();
926
500
  napi_remove_env_cleanup_hook(env, env_cleanup_hook, database);
927
- if (database->priorityRef_)
928
- napi_delete_reference(env, database->priorityRef_);
929
- for (auto& it : database->columns_) {
930
- napi_delete_reference(env, it.second.ref);
501
+ for (auto& [id, column] : database->columns) {
502
+ napi_delete_reference(env, column.ref);
931
503
  }
932
504
  delete database;
933
505
  }
@@ -940,94 +512,109 @@ NAPI_METHOD(db_init) {
940
512
  napi_value result;
941
513
  NAPI_STATUS_THROWS(napi_create_external(env, database, FinalizeDatabase, nullptr, &result));
942
514
 
943
- NAPI_STATUS_THROWS(napi_create_reference(env, result, 0, &database->priorityRef_));
944
-
945
515
  return result;
946
516
  }
947
517
 
948
518
  template <typename T, typename U>
949
- rocksdb::Status InitOptions(napi_env env, T& columnOptions, const U& options) {
519
+ napi_status InitOptions(napi_env env, T& columnOptions, const U& options) {
950
520
  rocksdb::ConfigOptions configOptions;
951
521
 
952
- const auto memtable_memory_budget = Uint32Property(env, options, "memtableMemoryBudget").value_or(256 * 1024 * 1024);
953
-
954
- const auto compaction = StringProperty(env, options, "compaction").value_or("level");
955
-
956
- if (compaction == "universal") {
957
- columnOptions.write_buffer_size = static_cast<size_t>(memtable_memory_budget / 4);
958
- // merge two memtables when flushing to L0
959
- columnOptions.min_write_buffer_number_to_merge = 2;
960
- // this means we'll use 50% extra memory in the worst case, but will reduce
961
- // write stalls.
962
- columnOptions.max_write_buffer_number = 6;
963
- // universal style compaction
964
- columnOptions.compaction_style = rocksdb::kCompactionStyleUniversal;
965
- columnOptions.compaction_options_universal.compression_size_percent = 80;
966
- } else {
967
- // merge two memtables when flushing to L0
968
- columnOptions.min_write_buffer_number_to_merge = 2;
969
- // this means we'll use 50% extra memory in the worst case, but will reduce
970
- // write stalls.
971
- columnOptions.max_write_buffer_number = 6;
972
- // start flushing L0->L1 as soon as possible. each file on level0 is
973
- // (memtable_memory_budget / 2). This will flush level 0 when it's bigger than
974
- // memtable_memory_budget.
975
- columnOptions.level0_file_num_compaction_trigger = 2;
976
- // doesn't really matter much, but we don't want to create too many files
977
- columnOptions.target_file_size_base = memtable_memory_budget / 8;
978
- // make Level1 size equal to Level0 size, so that L0->L1 compactions are fast
979
- columnOptions.max_bytes_for_level_base = memtable_memory_budget;
980
-
981
- // level style compaction
982
- columnOptions.compaction_style = rocksdb::kCompactionStyleLevel;
983
-
984
- // TODO (perf): only compress levels >= 2
522
+ size_t memtable_memory_budget = 256 * 1024 * 1024;
523
+ NAPI_STATUS_RETURN(GetProperty(env, options, "memtableMemoryBudget", memtable_memory_budget));
524
+
525
+ std::optional<std::string> compactionOpt;
526
+ NAPI_STATUS_RETURN(GetProperty(env, options, "compaction", compactionOpt));
527
+ if (compactionOpt) {
528
+ if (*compactionOpt == "universal") {
529
+ columnOptions.write_buffer_size = memtable_memory_budget / 4;
530
+ // merge two memtables when flushing to L0
531
+ columnOptions.min_write_buffer_number_to_merge = 2;
532
+ // this means we'll use 50% extra memory in the worst case, but will reduce
533
+ // write stalls.
534
+ columnOptions.max_write_buffer_number = 6;
535
+ // universal style compaction
536
+ columnOptions.compaction_style = rocksdb::kCompactionStyleUniversal;
537
+ columnOptions.compaction_options_universal.compression_size_percent = 80;
538
+ } else if (*compactionOpt == "level") {
539
+ // merge two memtables when flushing to L0
540
+ columnOptions.min_write_buffer_number_to_merge = 2;
541
+ // this means we'll use 50% extra memory in the worst case, but will reduce
542
+ // write stalls.
543
+ columnOptions.max_write_buffer_number = 6;
544
+ // start flushing L0->L1 as soon as possible. each file on level0 is
545
+ // (memtable_memory_budget / 2). This will flush level 0 when it's bigger than
546
+ // memtable_memory_budget.
547
+ columnOptions.level0_file_num_compaction_trigger = 2;
548
+ // doesn't really matter much, but we don't want to create too many files
549
+ columnOptions.target_file_size_base = memtable_memory_budget / 8;
550
+ // make Level1 size equal to Level0 size, so that L0->L1 compactions are fast
551
+ columnOptions.max_bytes_for_level_base = memtable_memory_budget;
552
+
553
+ // level style compaction
554
+ columnOptions.compaction_style = rocksdb::kCompactionStyleLevel;
555
+
556
+ // only compress levels >= 2
557
+ columnOptions.compression_per_level.resize(columnOptions.num_levels);
558
+ for (int i = 0; i < columnOptions.compression_per_level.size(); ++i) {
559
+ if (i < 2) {
560
+ columnOptions.compression_per_level[i] = rocksdb::kNoCompression;
561
+ } else {
562
+ columnOptions.compression_per_level[i] = rocksdb::kZSTD;
563
+ }
564
+ }
565
+ } else {
566
+ return napi_invalid_arg;
567
+ }
985
568
  }
986
569
 
987
- columnOptions.compression =
988
- BooleanProperty(env, options, "compression").value_or((true)) ? rocksdb::kZSTD : rocksdb::kNoCompression;
989
- if (columnOptions.compression == rocksdb::kZSTD) {
570
+ bool compression = true;
571
+ NAPI_STATUS_RETURN(GetProperty(env, options, "compression", compression));
572
+
573
+ if (compression) {
574
+ columnOptions.compression = rocksdb::kZSTD;
990
575
  columnOptions.compression_opts.max_dict_bytes = 16 * 1024;
991
576
  columnOptions.compression_opts.zstd_max_train_bytes = 16 * 1024 * 100;
992
577
  // TODO (perf): compression_opts.parallel_threads
993
578
  }
994
579
 
995
- const auto prefixExtractorOpt = StringProperty(env, options, "prefixExtractor");
580
+ std::optional<std::string> prefixExtractorOpt;
581
+ NAPI_STATUS_RETURN(GetProperty(env, options, "prefixExtractor", prefixExtractorOpt));
996
582
  if (prefixExtractorOpt) {
997
- ROCKS_STATUS_RETURN(
583
+ ROCKS_STATUS_RETURN_NAPI(
998
584
  rocksdb::SliceTransform::CreateFromString(configOptions, *prefixExtractorOpt, &columnOptions.prefix_extractor));
999
585
  }
1000
586
 
1001
- const auto comparatorOpt = StringProperty(env, options, "comparator");
587
+ std::optional<std::string> comparatorOpt;
588
+ NAPI_STATUS_RETURN(GetProperty(env, options, "comparator", comparatorOpt));
1002
589
  if (comparatorOpt) {
1003
- ROCKS_STATUS_RETURN(
590
+ ROCKS_STATUS_RETURN_NAPI(
1004
591
  rocksdb::Comparator::CreateFromString(configOptions, *comparatorOpt, &columnOptions.comparator));
1005
592
  }
1006
593
 
1007
- const auto mergeOperatorOpt = StringProperty(env, options, "mergeOperator");
594
+ std::optional<std::string> mergeOperatorOpt;
595
+ NAPI_STATUS_RETURN(GetProperty(env, options, "mergeOperator", mergeOperatorOpt));
1008
596
  if (mergeOperatorOpt) {
1009
- if (*mergeOperatorOpt == "maxRev") {
1010
- columnOptions.merge_operator = std::make_shared<MaxRevOperator>();
1011
- } else {
1012
- ROCKS_STATUS_RETURN(
1013
- rocksdb::MergeOperator::CreateFromString(configOptions, *mergeOperatorOpt, &columnOptions.merge_operator));
1014
- }
597
+ ROCKS_STATUS_RETURN_NAPI(
598
+ rocksdb::MergeOperator::CreateFromString(configOptions, *mergeOperatorOpt, &columnOptions.merge_operator));
1015
599
  }
1016
600
 
1017
- const auto cacheSize = Uint32Property(env, options, "cacheSize").value_or(8 << 20);
601
+ uint32_t cacheSize = 8 << 20;
602
+ NAPI_STATUS_RETURN(GetProperty(env, options, "cacheSize", cacheSize));
1018
603
 
1019
604
  rocksdb::BlockBasedTableOptions tableOptions;
1020
605
 
1021
606
  if (cacheSize) {
1022
607
  tableOptions.block_cache = rocksdb::NewLRUCache(cacheSize);
1023
- tableOptions.cache_index_and_filter_blocks =
1024
- BooleanProperty(env, options, "cacheIndexAndFilterBlocks").value_or(true);
608
+ tableOptions.cache_index_and_filter_blocks = true;
609
+ NAPI_STATUS_RETURN(
610
+ GetProperty(env, options, "cacheIndexAndFilterBlocks", tableOptions.cache_index_and_filter_blocks));
1025
611
  } else {
1026
612
  tableOptions.no_block_cache = true;
1027
613
  tableOptions.cache_index_and_filter_blocks = false;
1028
614
  }
1029
615
 
1030
- const auto optimize = StringProperty(env, options, "optimize").value_or("");
616
+ std::string optimize = "";
617
+ NAPI_STATUS_RETURN(GetProperty(env, options, "optimize", optimize));
1031
618
 
1032
619
  if (optimize == "point-lookup") {
1033
620
  tableOptions.data_block_index_type = rocksdb::BlockBasedTableOptions::kDataBlockBinaryAndHash;
@@ -1039,25 +626,29 @@ rocksdb::Status InitOptions(napi_env env, T& columnOptions, const U& options) {
1039
626
  } else if (optimize == "range-lookup") {
1040
627
  // TODO?
1041
628
  } else {
1042
- tableOptions.filter_policy.reset(rocksdb::NewRibbonFilterPolicy(10));
629
+ tableOptions.filter_policy.reset(rocksdb::NewBloomFilterPolicy(10));
1043
630
  }
1044
631
 
1045
- const auto filterPolicyOpt = StringProperty(env, options, "filterPolicy");
632
+ std::optional<std::string> filterPolicyOpt;
633
+ NAPI_STATUS_RETURN(GetProperty(env, options, "filterPolicy", filterPolicyOpt));
1046
634
  if (filterPolicyOpt) {
1047
- rocksdb::ConfigOptions configOptions;
1048
- ROCKS_STATUS_RETURN(
635
+ ROCKS_STATUS_RETURN_NAPI(
1049
636
  rocksdb::FilterPolicy::CreateFromString(configOptions, *filterPolicyOpt, &tableOptions.filter_policy));
1050
637
  }
1051
638
 
1052
- tableOptions.block_size = Uint32Property(env, options, "blockSize").value_or(4096);
1053
- tableOptions.block_restart_interval = Uint32Property(env, options, "blockRestartInterval").value_or(16);
639
+ NAPI_STATUS_RETURN(GetProperty(env, options, "blockSize", tableOptions.block_size));
640
+
641
+ NAPI_STATUS_RETURN(GetProperty(env, options, "blockRestartInterval", tableOptions.block_restart_interval));
642
+
1054
643
  tableOptions.format_version = 5;
1055
644
  tableOptions.checksum = rocksdb::kXXH3;
1056
- tableOptions.optimize_filters_for_memory = BooleanProperty(env, options, "optimizeFiltersForMemory").value_or(true);
645
+
646
+ tableOptions.optimize_filters_for_memory = true;
647
+ NAPI_STATUS_RETURN(GetProperty(env, options, "optimizeFiltersForMemory", tableOptions.optimize_filters_for_memory));
1057
648
 
1058
649
  columnOptions.table_factory.reset(rocksdb::NewBlockBasedTableFactory(tableOptions));
1059
650
 
1060
- return rocksdb::Status::OK();
651
+ return napi_ok;
1061
652
  }
1062
653
 
1063
654
  NAPI_METHOD(db_open) {
@@ -1067,32 +658,44 @@ NAPI_METHOD(db_open) {
1067
658
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1068
659
 
1069
660
  std::string location;
1070
- NAPI_STATUS_THROWS(ToString(env, argv[1], location));
661
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], location));
1071
662
 
1072
663
  rocksdb::Options dbOptions;
1073
664
 
1074
- dbOptions.IncreaseParallelism(Uint32Property(env, argv[2], "parallelism")
1075
- .value_or(std::max<uint32_t>(1, std::thread::hardware_concurrency() / 2)));
665
+ const auto options = argv[2];
666
+
667
+ int parallelism = std::max<int>(1, std::thread::hardware_concurrency() / 2);
668
+ NAPI_STATUS_THROWS(GetProperty(env, options, "parallelism", parallelism));
669
+ dbOptions.IncreaseParallelism(parallelism);
670
+
671
+ uint32_t walTTL = 0;
672
+ NAPI_STATUS_THROWS(GetProperty(env, options, "walTTL", walTTL));
673
+ dbOptions.WAL_ttl_seconds = walTTL / 1e3;
674
+
675
+ uint32_t walSizeLimit = 0;
676
+ NAPI_STATUS_THROWS(GetProperty(env, options, "walSizeLimit", walSizeLimit));
677
+ dbOptions.WAL_size_limit_MB = walSizeLimit / 1e6;
678
+
679
+ bool walCompression = false;
680
+ NAPI_STATUS_THROWS(GetProperty(env, options, "walCompression", walCompression));
681
+ dbOptions.wal_compression =
682
+ walCompression ? rocksdb::CompressionType::kZSTD : rocksdb::CompressionType::kNoCompression;
1076
683
 
1077
- dbOptions.create_if_missing = BooleanProperty(env, argv[2], "createIfMissing").value_or(true);
1078
- dbOptions.error_if_exists = BooleanProperty(env, argv[2], "errorIfExists").value_or(false);
1079
684
  dbOptions.avoid_unnecessary_blocking_io = true;
1080
685
  dbOptions.write_dbid_to_manifest = true;
1081
686
  dbOptions.use_adaptive_mutex = true; // We don't have soo many threads in the libuv thread pool...
1082
687
  dbOptions.enable_pipelined_write = false; // We only write in the main thread...
1083
- dbOptions.WAL_ttl_seconds = Uint32Property(env, argv[2], "walTTL").value_or(0) / 1e3;
1084
- dbOptions.WAL_size_limit_MB = Uint32Property(env, argv[2], "walSizeLimit").value_or(0) / 1e6;
1085
- dbOptions.wal_compression = BooleanProperty(env, argv[2], "walCompression").value_or(false)
1086
- ? rocksdb::CompressionType::kZSTD
1087
- : rocksdb::CompressionType::kNoCompression;
1088
688
  dbOptions.create_missing_column_families = true;
1089
- dbOptions.unordered_write = BooleanProperty(env, argv[2], "unorderedWrite").value_or(false);
1090
689
  dbOptions.fail_if_options_file_error = true;
1091
- dbOptions.manual_wal_flush = BooleanProperty(env, argv[2], "manualWalFlush").value_or(false);
690
+
691
+ NAPI_STATUS_THROWS(GetProperty(env, options, "createIfMissing", dbOptions.create_if_missing));
692
+ NAPI_STATUS_THROWS(GetProperty(env, options, "errorIfExists", dbOptions.error_if_exists));
693
+ NAPI_STATUS_THROWS(GetProperty(env, options, "unorderedWrite", dbOptions.unordered_write));
1092
694
 
1093
695
  // TODO (feat): dbOptions.listeners
1094
696
 
1095
- const auto infoLogLevel = StringProperty(env, argv[2], "infoLogLevel").value_or("");
697
+ std::string infoLogLevel;
698
+ NAPI_STATUS_THROWS(GetProperty(env, options, "infoLogLevel", infoLogLevel));
1096
699
  if (infoLogLevel.size() > 0) {
1097
700
  rocksdb::InfoLogLevel lvl = {};
1098
701
 
@@ -1119,16 +722,16 @@ NAPI_METHOD(db_open) {
1119
722
  dbOptions.info_log.reset(new NullLogger());
1120
723
  }
1121
724
 
1122
- ROCKS_STATUS_THROWS(InitOptions(env, dbOptions, argv[2]));
725
+ NAPI_STATUS_THROWS(InitOptions(env, dbOptions, options));
1123
726
 
1124
727
  std::vector<rocksdb::ColumnFamilyDescriptor> descriptors;
1125
728
 
1126
729
  bool hasColumns;
1127
- NAPI_STATUS_THROWS(napi_has_named_property(env, argv[2], "columns", &hasColumns));
730
+ NAPI_STATUS_THROWS(napi_has_named_property(env, options, "columns", &hasColumns));
1128
731
 
1129
732
  if (hasColumns) {
1130
733
  napi_value columns;
1131
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[2], "columns", &columns));
734
+ NAPI_STATUS_THROWS(napi_get_named_property(env, options, "columns", &columns));
1132
735
 
1133
736
  napi_value keys;
1134
737
  NAPI_STATUS_THROWS(napi_get_property_names(env, columns, &keys));
@@ -1144,27 +747,25 @@ NAPI_METHOD(db_open) {
1144
747
  napi_value column;
1145
748
  NAPI_STATUS_THROWS(napi_get_property(env, columns, key, &column));
1146
749
 
1147
- ROCKS_STATUS_THROWS(InitOptions(env, descriptors[n].options, column));
750
+ NAPI_STATUS_THROWS(InitOptions(env, descriptors[n].options, column));
1148
751
 
1149
- NAPI_STATUS_THROWS(ToString(env, key, descriptors[n].name));
752
+ NAPI_STATUS_THROWS(GetValue(env, key, descriptors[n].name));
1150
753
  }
1151
754
  }
1152
755
 
1153
756
  auto callback = argv[3];
1154
757
 
1155
- runWorker<std::vector<rocksdb::ColumnFamilyHandle*>>(
1156
- "leveldown.open", env, callback, database, false,
1157
- [=](auto& handles, auto& database) -> rocksdb::Status {
758
+ runAsync<std::vector<rocksdb::ColumnFamilyHandle*>>(
759
+ "leveldown.open", env, callback,
760
+ [=](auto& handles) {
1158
761
  rocksdb::DB* db = nullptr;
1159
- const auto status = descriptors.empty()
1160
- ? rocksdb::DB::Open(dbOptions, location, &db)
1161
- : rocksdb::DB::Open(dbOptions, location, descriptors, &handles, &db);
1162
- database.db_.reset(db);
762
+ const auto status = descriptors.empty() ? rocksdb::DB::Open(dbOptions, location, &db)
763
+ : rocksdb::DB::Open(dbOptions, location, descriptors, &handles, &db);
764
+ database->db.reset(db);
1163
765
  return status;
1164
766
  },
1165
- [=](auto& handles, napi_env env, napi_value callback) -> napi_status {
1166
- napi_value argv[2];
1167
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
767
+ [=](auto& handles, auto env, auto& argv) {
768
+ argv.resize(2);
1168
769
 
1169
770
  const auto size = handles.size();
1170
771
  NAPI_STATUS_RETURN(napi_create_object(env, &argv[1]));
@@ -1175,13 +776,11 @@ NAPI_METHOD(db_open) {
1175
776
  column.descriptor = descriptors[n];
1176
777
  NAPI_STATUS_RETURN(napi_create_external(env, column.handle, nullptr, nullptr, &column.val));
1177
778
  NAPI_STATUS_RETURN(napi_create_reference(env, column.val, 1, &column.ref));
1178
-
1179
779
  NAPI_STATUS_RETURN(napi_set_named_property(env, argv[1], descriptors[n].name.c_str(), column.val));
1180
-
1181
- database->columns_[column.handle->GetID()] = column;
780
+ database->columns[column.handle->GetID()] = column;
1182
781
  }
1183
782
 
1184
- return CallFunction(env, callback, 2, argv);
783
+ return napi_ok;
1185
784
  });
1186
785
 
1187
786
  return 0;
@@ -1200,28 +799,9 @@ NAPI_METHOD(db_close) {
1200
799
  auto callback = argv[1];
1201
800
 
1202
801
  struct State {};
1203
- auto worker = createWorker<State>(
1204
- "leveldown.close", env, callback, database, false,
1205
- [=](auto& state, auto& database) -> rocksdb::Status {
1206
- for (auto& it : database.columns_) {
1207
- database.db_->DestroyColumnFamilyHandle(it.second.handle);
1208
- }
1209
- database.columns_.clear();
1210
-
1211
- return database.db_->Close();
1212
- },
1213
- [](auto& state, napi_env env, napi_value callback) -> napi_status {
1214
- napi_value argv[1];
1215
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
1216
-
1217
- return CallFunction(env, callback, 1, &argv[0]);
1218
- });
1219
-
1220
- if (!database->HasPriorityWork()) {
1221
- worker->Queue(env);
1222
- } else {
1223
- database->pendingCloseWorker_ = worker;
1224
- }
802
+ runAsync<State>(
803
+ "leveldown.close", env, callback, [=](auto& state) { return database->Close(); },
804
+ [](auto& state, auto env, auto& argv) { return napi_ok; });
1225
805
 
1226
806
  return 0;
1227
807
  }
@@ -1232,40 +812,38 @@ NAPI_METHOD(updates_init) {
1232
812
  Database* database;
1233
813
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1234
814
 
1235
- napi_value sinceProperty;
1236
- int64_t since;
1237
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[1], "since", &sinceProperty));
1238
- NAPI_STATUS_THROWS(napi_get_value_int64(env, sinceProperty, &since));
815
+ const auto options = argv[1];
816
+
817
+ int64_t since = 0;
818
+ NAPI_STATUS_THROWS(GetProperty(env, options, "since", since));
819
+
820
+ bool keys = true;
821
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keys", keys));
1239
822
 
1240
- napi_value keysProperty;
1241
- bool keys;
1242
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[1], "keys", &keysProperty));
1243
- NAPI_STATUS_THROWS(napi_get_value_bool(env, keysProperty, &keys));
823
+ bool values = true;
824
+ NAPI_STATUS_THROWS(GetProperty(env, options, "values", values));
1244
825
 
1245
- napi_value valuesProperty;
1246
- bool values;
1247
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[1], "values", &valuesProperty));
1248
- NAPI_STATUS_THROWS(napi_get_value_bool(env, valuesProperty, &values));
826
+ bool data = true;
827
+ NAPI_STATUS_THROWS(GetProperty(env, options, "data", data));
1249
828
 
1250
- napi_value dataProperty;
1251
- bool data;
1252
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[1], "data", &dataProperty));
1253
- NAPI_STATUS_THROWS(napi_get_value_bool(env, dataProperty, &data));
829
+ Encoding keyEncoding = Encoding::String;
830
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keyEncoding", keyEncoding));
1254
831
 
1255
- const bool keyAsBuffer = EncodingIsBuffer(env, argv[1], "keyEncoding");
1256
- const bool valueAsBuffer = EncodingIsBuffer(env, argv[1], "valueEncoding");
832
+ Encoding valueEncoding = Encoding::String;
833
+ NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
1257
834
 
1258
835
  rocksdb::ColumnFamilyHandle* column = nullptr;
1259
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[1], &column));
836
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1260
837
 
1261
- auto updates = std::make_unique<Updates>(database, since, keys, values, data, column, keyAsBuffer, valueAsBuffer);
838
+ auto updates =
839
+ std::unique_ptr<Updates>(new Updates(database, since, keys, values, data, column, keyEncoding, valueEncoding));
1262
840
 
1263
841
  napi_value result;
1264
842
  NAPI_STATUS_THROWS(napi_create_external(env, updates.get(), Finalize<Updates>, updates.get(), &result));
1265
843
 
1266
844
  // Prevent GC of JS object before the iterator is closed (explicitly or on
1267
845
  // db close) and keep track of non-closed iterators to end them on db close.
1268
- updates.release()->Attach(env, result);
846
+ NAPI_STATUS_THROWS(updates.release()->Attach(env, result));
1269
847
 
1270
848
  return result;
1271
849
  }
@@ -1278,12 +856,12 @@ NAPI_METHOD(updates_next) {
1278
856
 
1279
857
  auto callback = argv[1];
1280
858
 
1281
- runWorker<rocksdb::BatchResult>(
1282
- "leveldown.updates.next", env, callback, updates->database_, true,
1283
- [=](auto& batchResult, auto& database) -> rocksdb::Status {
859
+ runAsync<rocksdb::BatchResult>(
860
+ "leveldown.updates.next", env, callback,
861
+ [=](auto& batchResult) {
1284
862
  if (!updates->iterator_) {
1285
863
  rocksdb::TransactionLogIterator::ReadOptions options;
1286
- const auto status = database.db_->GetUpdatesSince(updates->start_, &updates->iterator_, options);
864
+ const auto status = updates->database_->db->GetUpdatesSince(updates->start_, &updates->iterator_, options);
1287
865
  if (!status.ok()) {
1288
866
  return status;
1289
867
  }
@@ -1299,22 +877,18 @@ NAPI_METHOD(updates_next) {
1299
877
 
1300
878
  return rocksdb::Status::OK();
1301
879
  },
1302
- [=](auto& batchResult, napi_env env, napi_value callback) -> napi_status {
1303
- napi_value argv[5];
1304
-
1305
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
1306
-
880
+ [=](auto& batchResult, auto env, auto& argv) {
1307
881
  if (!batchResult.writeBatchPtr) {
1308
- return CallFunction(env, callback, 1, argv);
882
+ return napi_ok;
1309
883
  }
1310
884
 
885
+ argv.resize(5);
1311
886
  NAPI_STATUS_RETURN(updates->Iterate(env, *batchResult.writeBatchPtr, &argv[1]));
1312
-
1313
887
  NAPI_STATUS_RETURN(napi_create_int64(env, batchResult.sequence, &argv[2]));
1314
888
  NAPI_STATUS_RETURN(napi_create_int64(env, batchResult.writeBatchPtr->Count(), &argv[3]));
1315
889
  NAPI_STATUS_RETURN(napi_create_int64(env, updates->start_, &argv[4]));
1316
890
 
1317
- return CallFunction(env, callback, 5, argv);
891
+ return napi_ok;
1318
892
  });
1319
893
 
1320
894
  return 0;
@@ -1326,8 +900,8 @@ NAPI_METHOD(updates_close) {
1326
900
  Updates* updates;
1327
901
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&updates)));
1328
902
 
1329
- updates->Detach(env);
1330
- updates->Close();
903
+ ROCKS_STATUS_THROWS_NAPI(updates->Close());
904
+ NAPI_STATUS_THROWS(updates->Detach(env));
1331
905
 
1332
906
  return 0;
1333
907
  }
@@ -1347,44 +921,53 @@ NAPI_METHOD(db_get_many) {
1347
921
  for (uint32_t n = 0; n < length; n++) {
1348
922
  napi_value element;
1349
923
  NAPI_STATUS_THROWS(napi_get_element(env, argv[1], n, &element));
1350
- NAPI_STATUS_THROWS(ToString(env, element, keys[n]));
924
+ NAPI_STATUS_THROWS(GetValue(env, element, keys[n]));
1351
925
  }
1352
926
  }
1353
927
 
1354
- const bool valueAsBuffer = EncodingIsBuffer(env, argv[2], "valueEncoding");
1355
- const bool fillCache = BooleanProperty(env, argv[2], "fillCache").value_or(true);
1356
- const bool ignoreRangeDeletions = BooleanProperty(env, argv[2], "ignoreRangeDeletions").value_or(false);
928
+ const auto options = argv[2];
929
+
930
+ Encoding valueEncoding = Encoding::String;
931
+ NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
1357
932
 
1358
- rocksdb::ColumnFamilyHandle* column;
1359
- NAPI_STATUS_THROWS(GetColumnFamily(database, env, argv[2], &column));
933
+ bool fillCache = true;
934
+ NAPI_STATUS_THROWS(GetProperty(env, options, "fillCache", fillCache));
935
+
936
+ bool ignoreRangeDeletions = false;
937
+ NAPI_STATUS_THROWS(GetProperty(env, options, "ignoreRangeDeletions", ignoreRangeDeletions));
938
+
939
+ rocksdb::ColumnFamilyHandle* column = database->db->DefaultColumnFamily();
940
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1360
941
 
1361
942
  auto callback = argv[3];
1362
943
 
1363
944
  auto snapshot = std::shared_ptr<const rocksdb::Snapshot>(
1364
- database->db_->GetSnapshot(), [database](auto ptr) { database->db_->ReleaseSnapshot(ptr); });
945
+ database->db->GetSnapshot(), [database](auto ptr) { database->db->ReleaseSnapshot(ptr); });
1365
946
 
1366
- runWorker<std::vector<rocksdb::PinnableSlice>>(
1367
- "leveldown.get.many", env, callback, database, true,
1368
- [=, keys = std::move(keys)](auto& values, Database& db) -> rocksdb::Status {
947
+ runAsync<std::vector<rocksdb::PinnableSlice>>(
948
+ "leveldown.get.many", env, callback,
949
+ [=, keys = std::move(keys), snapshot = std::move(snapshot)](auto& values) {
1369
950
  rocksdb::ReadOptions readOptions;
1370
951
  readOptions.fill_cache = fillCache;
1371
952
  readOptions.snapshot = snapshot.get();
1372
953
  readOptions.async_io = true;
1373
954
  readOptions.ignore_range_deletions = ignoreRangeDeletions;
1374
955
 
956
+ const auto size = keys.size();
957
+
1375
958
  std::vector<rocksdb::Slice> keys2;
1376
- keys2.reserve(keys.size());
959
+ keys2.reserve(size);
1377
960
  for (const auto& key : keys) {
1378
961
  keys2.emplace_back(key);
1379
962
  }
1380
963
  std::vector<rocksdb::Status> statuses;
1381
-
1382
- statuses.resize(keys2.size());
1383
- values.resize(keys2.size());
1384
964
 
1385
- db.db_->MultiGet(readOptions, column, keys2.size(), keys2.data(), values.data(), statuses.data());
965
+ statuses.resize(size);
966
+ values.resize(size);
1386
967
 
1387
- for (size_t idx = 0; idx < keys2.size(); idx++) {
968
+ database->db->MultiGet(readOptions, column, size, keys2.data(), values.data(), statuses.data());
969
+
970
+ for (size_t idx = 0; idx < size; idx++) {
1388
971
  if (statuses[idx].IsNotFound()) {
1389
972
  values[idx] = rocksdb::PinnableSlice(nullptr);
1390
973
  } else if (!statuses[idx].ok()) {
@@ -1394,23 +977,22 @@ NAPI_METHOD(db_get_many) {
1394
977
 
1395
978
  return rocksdb::Status::OK();
1396
979
  },
1397
- [=](auto& values, napi_env env, napi_value callback) -> napi_status {
1398
- napi_value argv[2];
1399
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
980
+ [=](auto& values, auto env, auto& argv) {
981
+ argv.resize(2);
1400
982
 
1401
983
  NAPI_STATUS_RETURN(napi_create_array_with_length(env, values.size(), &argv[1]));
1402
984
 
1403
985
  for (size_t idx = 0; idx < values.size(); idx++) {
1404
986
  napi_value element;
1405
987
  if (values[idx].GetSelf()) {
1406
- NAPI_STATUS_RETURN(Convert(env, &values[idx], valueAsBuffer, element));
988
+ NAPI_STATUS_RETURN(Convert(env, &values[idx], valueEncoding, element));
1407
989
  } else {
1408
990
  NAPI_STATUS_RETURN(napi_get_undefined(env, &element));
1409
991
  }
1410
992
  NAPI_STATUS_RETURN(napi_set_element(env, argv[1], static_cast<uint32_t>(idx), element));
1411
993
  }
1412
994
 
1413
- return CallFunction(env, callback, 2, argv);
995
+ return napi_ok;
1414
996
  });
1415
997
 
1416
998
  return 0;
@@ -1422,16 +1004,28 @@ NAPI_METHOD(db_clear) {
1422
1004
  Database* database;
1423
1005
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1424
1006
 
1425
- const auto reverse = BooleanProperty(env, argv[1], "reverse").value_or(false);
1426
- const auto limit = Int32Property(env, argv[1], "limit").value_or(-1);
1007
+ const auto options = argv[1];
1008
+
1009
+ bool reverse = false;
1010
+ NAPI_STATUS_THROWS(GetProperty(env, options, "reverse", reverse));
1011
+
1012
+ int32_t limit = -1;
1013
+ NAPI_STATUS_THROWS(GetProperty(env, options, "limit", limit));
1014
+
1015
+ rocksdb::ColumnFamilyHandle* column = database->db->DefaultColumnFamily();
1016
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1017
+
1018
+ std::optional<std::string> lt;
1019
+ NAPI_STATUS_THROWS(GetProperty(env, options, "lt", lt));
1427
1020
 
1428
- rocksdb::ColumnFamilyHandle* column;
1429
- NAPI_STATUS_THROWS(GetColumnFamily(database, env, argv[1], &column));
1021
+ std::optional<std::string> lte;
1022
+ NAPI_STATUS_THROWS(GetProperty(env, options, "lte", lte));
1430
1023
 
1431
- auto lt = StringProperty(env, argv[1], "lt");
1432
- auto lte = StringProperty(env, argv[1], "lte");
1433
- auto gt = StringProperty(env, argv[1], "gt");
1434
- auto gte = StringProperty(env, argv[1], "gte");
1024
+ std::optional<std::string> gt;
1025
+ NAPI_STATUS_THROWS(GetProperty(env, options, "gt", gt));
1026
+
1027
+ std::optional<std::string> gte;
1028
+ NAPI_STATUS_THROWS(GetProperty(env, options, "gte", gte));
1435
1029
 
1436
1030
  if (limit == -1) {
1437
1031
  rocksdb::PinnableSlice begin;
@@ -1456,7 +1050,7 @@ NAPI_METHOD(db_clear) {
1456
1050
 
1457
1051
  if (begin.compare(end) < 0) {
1458
1052
  rocksdb::WriteOptions writeOptions;
1459
- ROCKS_STATUS_THROWS(database->db_->DeleteRange(writeOptions, column, begin, end));
1053
+ ROCKS_STATUS_THROWS_NAPI(database->db->DeleteRange(writeOptions, column, begin, end));
1460
1054
  }
1461
1055
 
1462
1056
  return 0;
@@ -1464,8 +1058,8 @@ NAPI_METHOD(db_clear) {
1464
1058
  // TODO (fix): Error handling.
1465
1059
  // TODO (fix): This should be async...
1466
1060
 
1467
- std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db_->GetSnapshot(),
1468
- [=](const auto ptr) { database->db_->ReleaseSnapshot(ptr); });
1061
+ std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db->GetSnapshot(),
1062
+ [=](const auto ptr) { database->db->ReleaseSnapshot(ptr); });
1469
1063
  BaseIterator it(database, column, reverse, lt, lte, gt, gte, limit, false, snapshot);
1470
1064
 
1471
1065
  it.SeekToRange();
@@ -1489,7 +1083,7 @@ NAPI_METHOD(db_clear) {
1489
1083
  break;
1490
1084
  }
1491
1085
 
1492
- status = database->db_->Write(writeOptions, &batch);
1086
+ status = database->db->Write(writeOptions, &batch);
1493
1087
  if (!status.ok()) {
1494
1088
  break;
1495
1089
  }
@@ -1500,7 +1094,7 @@ NAPI_METHOD(db_clear) {
1500
1094
  it.Close();
1501
1095
 
1502
1096
  if (!status.ok()) {
1503
- ROCKS_STATUS_THROWS(status);
1097
+ ROCKS_STATUS_THROWS_NAPI(status);
1504
1098
  }
1505
1099
 
1506
1100
  return 0;
@@ -1514,10 +1108,10 @@ NAPI_METHOD(db_get_property) {
1514
1108
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1515
1109
 
1516
1110
  rocksdb::PinnableSlice property;
1517
- NAPI_STATUS_THROWS(ToString(env, argv[1], property));
1111
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], property));
1518
1112
 
1519
1113
  std::string value;
1520
- database->db_->GetProperty(property, &value);
1114
+ database->db->GetProperty(property, &value);
1521
1115
 
1522
1116
  napi_value result;
1523
1117
  NAPI_STATUS_THROWS(napi_create_string_utf8(env, value.data(), value.size(), &result));
@@ -1531,7 +1125,7 @@ NAPI_METHOD(db_get_latest_sequence) {
1531
1125
  Database* database;
1532
1126
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1533
1127
 
1534
- const auto seq = database->db_->GetLatestSequenceNumber();
1128
+ const auto seq = database->db->GetLatestSequenceNumber();
1535
1129
 
1536
1130
  napi_value result;
1537
1131
  NAPI_STATUS_THROWS(napi_create_int64(env, seq, &result));
@@ -1546,35 +1140,59 @@ NAPI_METHOD(iterator_init) {
1546
1140
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1547
1141
 
1548
1142
  const auto options = argv[1];
1549
- const auto reverse = BooleanProperty(env, options, "reverse").value_or(false);
1550
- const auto keys = BooleanProperty(env, options, "keys").value_or(true);
1551
- const auto values = BooleanProperty(env, options, "values").value_or(true);
1552
- const auto fillCache = BooleanProperty(env, options, "fillCache").value_or(false);
1553
- const bool keyAsBuffer = EncodingIsBuffer(env, options, "keyEncoding");
1554
- const bool valueAsBuffer = EncodingIsBuffer(env, options, "valueEncoding");
1555
- const auto limit = Int32Property(env, options, "limit").value_or(-1);
1556
- const auto highWaterMarkBytes = Uint32Property(env, options, "highWaterMarkBytes").value_or(64 * 1024);
1557
1143
 
1558
- const auto lt = StringProperty(env, options, "lt");
1559
- const auto lte = StringProperty(env, options, "lte");
1560
- const auto gt = StringProperty(env, options, "gt");
1561
- const auto gte = StringProperty(env, options, "gte");
1144
+ bool reverse = false;
1145
+ NAPI_STATUS_THROWS(GetProperty(env, options, "reverse", reverse));
1146
+
1147
+ bool keys = true;
1148
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keys", keys));
1149
+
1150
+ bool values = true;
1151
+ NAPI_STATUS_THROWS(GetProperty(env, options, "values", values));
1152
+
1153
+ bool fillCache = false;
1154
+ NAPI_STATUS_THROWS(GetProperty(env, options, "fillCache", fillCache));
1155
+
1156
+ Encoding keyEncoding = Encoding::String;
1157
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keyEncoding", keyEncoding));
1158
+
1159
+ Encoding valueEncoding = Encoding::String;
1160
+ NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
1161
+
1162
+ int32_t limit = -1;
1163
+ NAPI_STATUS_THROWS(GetProperty(env, options, "limit", limit));
1164
+
1165
+ int32_t highWaterMarkBytes = 64 * 1024;
1166
+ NAPI_STATUS_THROWS(GetProperty(env, options, "highWaterMarkBytes", highWaterMarkBytes));
1167
+
1168
+ std::optional<std::string> lt;
1169
+ NAPI_STATUS_THROWS(GetProperty(env, options, "lt", lt));
1170
+
1171
+ std::optional<std::string> lte;
1172
+ NAPI_STATUS_THROWS(GetProperty(env, options, "lte", lte));
1562
1173
 
1563
- rocksdb::ColumnFamilyHandle* column;
1564
- NAPI_STATUS_THROWS(GetColumnFamily(database, env, options, &column));
1174
+ std::optional<std::string> gt;
1175
+ NAPI_STATUS_THROWS(GetProperty(env, options, "gt", gt));
1565
1176
 
1566
- std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db_->GetSnapshot(),
1567
- [=](const auto ptr) { database->db_->ReleaseSnapshot(ptr); });
1177
+ std::optional<std::string> gte;
1178
+ NAPI_STATUS_THROWS(GetProperty(env, options, "gte", gte));
1568
1179
 
1569
- auto iterator = std::make_unique<Iterator>(database, column, reverse, keys, values, limit, lt, lte, gt, gte,
1570
- fillCache, keyAsBuffer, valueAsBuffer, highWaterMarkBytes, snapshot);
1180
+ rocksdb::ColumnFamilyHandle* column = database->db->DefaultColumnFamily();
1181
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1182
+
1183
+ std::shared_ptr<const rocksdb::Snapshot> snapshot(database->db->GetSnapshot(),
1184
+ [=](const auto ptr) { database->db->ReleaseSnapshot(ptr); });
1185
+
1186
+ auto iterator =
1187
+ std::unique_ptr<Iterator>(new Iterator(database, column, reverse, keys, values, limit, lt, lte, gt, gte,
1188
+ fillCache, keyEncoding, valueEncoding, highWaterMarkBytes, snapshot));
1571
1189
 
1572
1190
  napi_value result;
1573
1191
  NAPI_STATUS_THROWS(napi_create_external(env, iterator.get(), Finalize<Iterator>, iterator.get(), &result));
1574
1192
 
1575
1193
  // Prevent GC of JS object before the iterator is closed (explicitly or on
1576
1194
  // db close) and keep track of non-closed iterators to end them on db close.
1577
- iterator.release()->Attach(env, result);
1195
+ NAPI_STATUS_THROWS(iterator.release()->Attach(env, result));
1578
1196
 
1579
1197
  return result;
1580
1198
  }
@@ -1586,7 +1204,7 @@ NAPI_METHOD(iterator_seek) {
1586
1204
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&iterator)));
1587
1205
 
1588
1206
  rocksdb::PinnableSlice target;
1589
- NAPI_STATUS_THROWS(ToString(env, argv[1], target));
1207
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], target));
1590
1208
 
1591
1209
  iterator->first_ = true;
1592
1210
  iterator->Seek(target); // TODO: Does seek causing blocking IO?
@@ -1600,8 +1218,8 @@ NAPI_METHOD(iterator_close) {
1600
1218
  Iterator* iterator;
1601
1219
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&iterator)));
1602
1220
 
1603
- iterator->Detach(env);
1604
- iterator->Close();
1221
+ ROCKS_STATUS_THROWS_NAPI(iterator->Close());
1222
+ NAPI_STATUS_THROWS(iterator->Detach(env));
1605
1223
 
1606
1224
  return 0;
1607
1225
  }
@@ -1636,9 +1254,9 @@ NAPI_METHOD(iterator_nextv) {
1636
1254
  bool finished = false;
1637
1255
  };
1638
1256
 
1639
- runWorker<State>(
1640
- std::string("leveldown.iterator.next"), env, callback, iterator->database_, true,
1641
- [=](auto& state, Database& db) -> rocksdb::Status {
1257
+ runAsync<State>(
1258
+ std::string("leveldown.iterator.next"), env, callback,
1259
+ [=](auto& state) {
1642
1260
  if (!iterator->DidSeek()) {
1643
1261
  iterator->SeekToRange();
1644
1262
  }
@@ -1683,9 +1301,8 @@ NAPI_METHOD(iterator_nextv) {
1683
1301
 
1684
1302
  return iterator->Status();
1685
1303
  },
1686
- [=](auto& state, napi_env env, napi_value callback) -> napi_status {
1687
- napi_value argv[3];
1688
- NAPI_STATUS_RETURN(napi_get_null(env, &argv[0]));
1304
+ [=](auto& state, auto env, auto& argv) {
1305
+ argv.resize(3);
1689
1306
 
1690
1307
  NAPI_STATUS_RETURN(napi_create_array_with_length(env, state.cache.size(), &argv[1]));
1691
1308
 
@@ -1693,8 +1310,8 @@ NAPI_METHOD(iterator_nextv) {
1693
1310
  napi_value key;
1694
1311
  napi_value val;
1695
1312
 
1696
- NAPI_STATUS_RETURN(Convert(env, state.cache[n + 0], iterator->keyAsBuffer_, key));
1697
- NAPI_STATUS_RETURN(Convert(env, state.cache[n + 1], iterator->valueAsBuffer_, val));
1313
+ NAPI_STATUS_RETURN(Convert(env, state.cache[n + 0], iterator->keyEncoding_, key));
1314
+ NAPI_STATUS_RETURN(Convert(env, state.cache[n + 1], iterator->valueEncoding_, val));
1698
1315
 
1699
1316
  NAPI_STATUS_RETURN(napi_set_element(env, argv[1], static_cast<int>(n + 0), key));
1700
1317
  NAPI_STATUS_RETURN(napi_set_element(env, argv[1], static_cast<int>(n + 1), val));
@@ -1702,7 +1319,7 @@ NAPI_METHOD(iterator_nextv) {
1702
1319
 
1703
1320
  NAPI_STATUS_RETURN(napi_get_boolean(env, state.finished, &argv[2]));
1704
1321
 
1705
- return CallFunction(env, callback, 3, argv);
1322
+ return napi_ok;
1706
1323
  });
1707
1324
 
1708
1325
  return 0;
@@ -1716,63 +1333,49 @@ NAPI_METHOD(batch_do) {
1716
1333
 
1717
1334
  rocksdb::WriteBatch batch;
1718
1335
 
1336
+ auto elements = argv[1];
1337
+
1719
1338
  uint32_t length;
1720
- NAPI_STATUS_THROWS(napi_get_array_length(env, argv[1], &length));
1339
+ NAPI_STATUS_THROWS(napi_get_array_length(env, elements, &length));
1340
+
1341
+ rocksdb::PinnableSlice type;
1342
+ rocksdb::PinnableSlice key;
1343
+ rocksdb::PinnableSlice value;
1721
1344
 
1722
1345
  for (uint32_t i = 0; i < length; i++) {
1723
- rocksdb::PinnableSlice type;
1724
- rocksdb::PinnableSlice key;
1725
- rocksdb::PinnableSlice value;
1346
+ type.Reset();
1347
+ key.Reset();
1348
+ value.Reset();
1726
1349
 
1727
1350
  napi_value element;
1728
- NAPI_STATUS_THROWS(napi_get_element(env, argv[1], i, &element));
1351
+ NAPI_STATUS_THROWS(napi_get_element(env, elements, i, &element));
1729
1352
 
1730
- napi_value typeProperty;
1731
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "type", &typeProperty));
1732
- NAPI_STATUS_THROWS(ToString(env, typeProperty, type));
1353
+ NAPI_STATUS_THROWS(GetProperty(env, element, "type", type, true));
1733
1354
 
1734
- rocksdb::ColumnFamilyHandle* column;
1735
- NAPI_STATUS_THROWS(GetColumnFamily(database, env, element, &column));
1355
+ rocksdb::ColumnFamilyHandle* column = database->db->DefaultColumnFamily();
1356
+ NAPI_STATUS_THROWS(GetProperty(env, element, "column", column));
1736
1357
 
1737
1358
  if (type == "del") {
1738
- napi_value keyProperty;
1739
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "key", &keyProperty));
1740
- NAPI_STATUS_THROWS(ToString(env, keyProperty, key));
1741
-
1742
- ROCKS_STATUS_THROWS(batch.Delete(column, key));
1359
+ NAPI_STATUS_THROWS(GetProperty(env, element, "key", key, true));
1360
+ ROCKS_STATUS_THROWS_NAPI(batch.Delete(column, key));
1743
1361
  } else if (type == "put") {
1744
- napi_value keyProperty;
1745
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "key", &keyProperty));
1746
- NAPI_STATUS_THROWS(ToString(env, keyProperty, key));
1747
-
1748
- napi_value valueProperty;
1749
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "value", &valueProperty));
1750
- NAPI_STATUS_THROWS(ToString(env, valueProperty, value));
1751
-
1752
- ROCKS_STATUS_THROWS(batch.Put(column, key, value));
1362
+ NAPI_STATUS_THROWS(GetProperty(env, element, "key", key, true));
1363
+ NAPI_STATUS_THROWS(GetProperty(env, element, "value", value, true));
1364
+ ROCKS_STATUS_THROWS_NAPI(batch.Put(column, key, value));
1753
1365
  } else if (type == "data") {
1754
- napi_value valueProperty;
1755
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "value", &valueProperty));
1756
- NAPI_STATUS_THROWS(ToString(env, valueProperty, value));
1757
-
1758
- ROCKS_STATUS_THROWS(batch.PutLogData(value));
1366
+ NAPI_STATUS_THROWS(GetProperty(env, element, "value", value, true));
1367
+ ROCKS_STATUS_THROWS_NAPI(batch.PutLogData(value));
1759
1368
  } else if (type == "merge") {
1760
- napi_value keyProperty;
1761
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "key", &keyProperty));
1762
- NAPI_STATUS_THROWS(ToString(env, keyProperty, key));
1763
-
1764
- napi_value valueProperty;
1765
- NAPI_STATUS_THROWS(napi_get_named_property(env, element, "value", &valueProperty));
1766
- NAPI_STATUS_THROWS(ToString(env, valueProperty, value));
1767
-
1768
- ROCKS_STATUS_THROWS(batch.Merge(column, key, value));
1369
+ NAPI_STATUS_THROWS(GetProperty(env, element, "key", key, true));
1370
+ NAPI_STATUS_THROWS(GetProperty(env, element, "value", value, true));
1371
+ ROCKS_STATUS_THROWS_NAPI(batch.Merge(column, key, value));
1769
1372
  } else {
1770
- ROCKS_STATUS_THROWS(rocksdb::Status::InvalidArgument());
1373
+ NAPI_STATUS_THROWS(napi_invalid_arg);
1771
1374
  }
1772
1375
  }
1773
1376
 
1774
1377
  rocksdb::WriteOptions writeOptions;
1775
- ROCKS_STATUS_THROWS(database->db_->Write(writeOptions, &batch));
1378
+ ROCKS_STATUS_THROWS_NAPI(database->db->Write(writeOptions, &batch));
1776
1379
 
1777
1380
  return 0;
1778
1381
  }
@@ -1790,21 +1393,23 @@ NAPI_METHOD(batch_put) {
1790
1393
  NAPI_ARGV(4);
1791
1394
 
1792
1395
  rocksdb::WriteBatch* batch;
1793
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)(&batch)));
1396
+ NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&batch)));
1794
1397
 
1795
1398
  rocksdb::PinnableSlice key;
1796
- NAPI_STATUS_THROWS(ToString(env, argv[1], key));
1399
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], key));
1797
1400
 
1798
1401
  rocksdb::PinnableSlice val;
1799
- NAPI_STATUS_THROWS(ToString(env, argv[2], val));
1402
+ NAPI_STATUS_THROWS(GetValue(env, argv[2], val));
1800
1403
 
1801
- rocksdb::ColumnFamilyHandle* column;
1802
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[3], &column));
1404
+ const auto options = argv[3];
1405
+
1406
+ rocksdb::ColumnFamilyHandle* column = nullptr;
1407
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1803
1408
 
1804
1409
  if (column) {
1805
- ROCKS_STATUS_THROWS(batch->Put(column, key, val));
1410
+ ROCKS_STATUS_THROWS_NAPI(batch->Put(column, key, val));
1806
1411
  } else {
1807
- ROCKS_STATUS_THROWS(batch->Put(key, val));
1412
+ ROCKS_STATUS_THROWS_NAPI(batch->Put(key, val));
1808
1413
  }
1809
1414
 
1810
1415
  return 0;
@@ -1817,15 +1422,17 @@ NAPI_METHOD(batch_del) {
1817
1422
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&batch)));
1818
1423
 
1819
1424
  rocksdb::PinnableSlice key;
1820
- NAPI_STATUS_THROWS(ToString(env, argv[1], key));
1425
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], key));
1426
+
1427
+ const auto options = argv[2];
1821
1428
 
1822
- rocksdb::ColumnFamilyHandle* column;
1823
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[2], &column));
1429
+ rocksdb::ColumnFamilyHandle* column = nullptr;
1430
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1824
1431
 
1825
1432
  if (column) {
1826
- ROCKS_STATUS_THROWS(batch->Delete(column, key));
1433
+ ROCKS_STATUS_THROWS_NAPI(batch->Delete(column, key));
1827
1434
  } else {
1828
- ROCKS_STATUS_THROWS(batch->Delete(key));
1435
+ ROCKS_STATUS_THROWS_NAPI(batch->Delete(key));
1829
1436
  }
1830
1437
 
1831
1438
  return 0;
@@ -1838,18 +1445,20 @@ NAPI_METHOD(batch_merge) {
1838
1445
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], (void**)(&batch)));
1839
1446
 
1840
1447
  rocksdb::PinnableSlice key;
1841
- NAPI_STATUS_THROWS(ToString(env, argv[1], key));
1448
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], key));
1842
1449
 
1843
1450
  rocksdb::PinnableSlice val;
1844
- NAPI_STATUS_THROWS(ToString(env, argv[2], val));
1451
+ NAPI_STATUS_THROWS(GetValue(env, argv[2], val));
1452
+
1453
+ const auto options = argv[3];
1845
1454
 
1846
- rocksdb::ColumnFamilyHandle* column;
1847
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[3], &column));
1455
+ rocksdb::ColumnFamilyHandle* column = nullptr;
1456
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1848
1457
 
1849
1458
  if (column) {
1850
- ROCKS_STATUS_THROWS(batch->Merge(column, key, val));
1459
+ ROCKS_STATUS_THROWS_NAPI(batch->Merge(column, key, val));
1851
1460
  } else {
1852
- ROCKS_STATUS_THROWS(batch->Merge(key, val));
1461
+ ROCKS_STATUS_THROWS_NAPI(batch->Merge(key, val));
1853
1462
  }
1854
1463
 
1855
1464
  return 0;
@@ -1876,7 +1485,7 @@ NAPI_METHOD(batch_write) {
1876
1485
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
1877
1486
 
1878
1487
  rocksdb::WriteOptions writeOptions;
1879
- ROCKS_STATUS_THROWS(database->db_->Write(writeOptions, batch));
1488
+ ROCKS_STATUS_THROWS_NAPI(database->db->Write(writeOptions, batch));
1880
1489
 
1881
1490
  return 0;
1882
1491
  }
@@ -1888,9 +1497,9 @@ NAPI_METHOD(batch_put_log_data) {
1888
1497
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&batch)));
1889
1498
 
1890
1499
  rocksdb::PinnableSlice logData;
1891
- NAPI_STATUS_THROWS(ToString(env, argv[1], logData));
1500
+ NAPI_STATUS_THROWS(GetValue(env, argv[1], logData));
1892
1501
 
1893
- ROCKS_STATUS_THROWS(batch->PutLogData(logData));
1502
+ ROCKS_STATUS_THROWS_NAPI(batch->PutLogData(logData));
1894
1503
 
1895
1504
  return 0;
1896
1505
  }
@@ -1916,111 +1525,34 @@ NAPI_METHOD(batch_iterate) {
1916
1525
  rocksdb::WriteBatch* batch;
1917
1526
  NAPI_STATUS_THROWS(napi_get_value_external(env, argv[1], reinterpret_cast<void**>(&batch)));
1918
1527
 
1919
- napi_value keysProperty;
1920
- bool keys;
1921
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[2], "keys", &keysProperty));
1922
- NAPI_STATUS_THROWS(napi_get_value_bool(env, keysProperty, &keys));
1528
+ const auto options = argv[2];
1529
+
1530
+ bool keys = true;
1531
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keys", keys));
1532
+
1533
+ bool values = true;
1534
+ NAPI_STATUS_THROWS(GetProperty(env, options, "values", values));
1923
1535
 
1924
- napi_value valuesProperty;
1925
- bool values;
1926
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[2], "values", &valuesProperty));
1927
- NAPI_STATUS_THROWS(napi_get_value_bool(env, valuesProperty, &values));
1536
+ bool data = true;
1537
+ NAPI_STATUS_THROWS(GetProperty(env, options, "data", data));
1928
1538
 
1929
- napi_value dataProperty;
1930
- bool data;
1931
- NAPI_STATUS_THROWS(napi_get_named_property(env, argv[2], "data", &dataProperty));
1932
- NAPI_STATUS_THROWS(napi_get_value_bool(env, dataProperty, &data));
1539
+ Encoding keyEncoding = Encoding::String;
1540
+ NAPI_STATUS_THROWS(GetProperty(env, options, "keyEncoding", keyEncoding));
1933
1541
 
1934
- const bool keyAsBuffer = EncodingIsBuffer(env, argv[1], "keyEncoding");
1935
- const bool valueAsBuffer = EncodingIsBuffer(env, argv[1], "valueEncoding");
1542
+ Encoding valueEncoding = Encoding::String;
1543
+ NAPI_STATUS_THROWS(GetProperty(env, options, "valueEncoding", valueEncoding));
1936
1544
 
1937
1545
  rocksdb::ColumnFamilyHandle* column = nullptr;
1938
- NAPI_STATUS_THROWS(GetColumnFamily(nullptr, env, argv[2], &column));
1546
+ NAPI_STATUS_THROWS(GetProperty(env, options, "column", column));
1939
1547
 
1940
- napi_value result;
1941
- BatchIterator iterator(nullptr, keys, values, data, column, keyAsBuffer, valueAsBuffer);
1548
+ BatchIterator iterator(nullptr, keys, values, data, column, keyEncoding, valueEncoding);
1942
1549
 
1550
+ napi_value result;
1943
1551
  NAPI_STATUS_THROWS(iterator.Iterate(env, *batch, &result));
1944
1552
 
1945
1553
  return result;
1946
1554
  }
1947
1555
 
1948
- NAPI_METHOD(db_flush_wal) {
1949
- NAPI_ARGV(2);
1950
-
1951
- Database* database;
1952
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1953
-
1954
- const auto flush = BooleanProperty(env, argv[1], "flush").value_or(false);
1955
-
1956
- ROCKS_STATUS_THROWS(database->db_->FlushWAL(flush));
1957
-
1958
- return 0;
1959
- }
1960
-
1961
- template <typename T>
1962
- napi_status FromLogFile(napi_env env, const T& file, napi_value* obj) {
1963
- NAPI_STATUS_RETURN(napi_create_object(env, obj));
1964
-
1965
- napi_value pathName;
1966
- NAPI_STATUS_RETURN(napi_create_string_utf8(env, file->PathName().c_str(), NAPI_AUTO_LENGTH, &pathName));
1967
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "pathName", pathName));
1968
-
1969
- napi_value logNumber;
1970
- NAPI_STATUS_RETURN(napi_create_int64(env, file->LogNumber(), &logNumber));
1971
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "logNumber", logNumber));
1972
-
1973
- napi_value type;
1974
- NAPI_STATUS_RETURN(napi_create_int64(env, file->Type(), &type));
1975
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "type", type));
1976
-
1977
- napi_value startSequence;
1978
- NAPI_STATUS_RETURN(napi_create_int64(env, file->StartSequence(), &startSequence));
1979
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "startSequence", startSequence));
1980
-
1981
- napi_value sizeFileBytes;
1982
- NAPI_STATUS_RETURN(napi_create_int64(env, file->SizeFileBytes(), &sizeFileBytes));
1983
- NAPI_STATUS_RETURN(napi_set_named_property(env, *obj, "sizeFileBytes", sizeFileBytes));
1984
-
1985
- return napi_ok;
1986
- }
1987
-
1988
- NAPI_METHOD(db_get_sorted_wal_files) {
1989
- NAPI_ARGV(1);
1990
-
1991
- Database* database;
1992
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
1993
-
1994
- rocksdb::VectorLogPtr files;
1995
- ROCKS_STATUS_THROWS(database->db_->GetSortedWalFiles(files));
1996
-
1997
- napi_value ret;
1998
- NAPI_STATUS_THROWS(napi_create_array_with_length(env, files.size(), &ret));
1999
-
2000
- for (size_t n = 0; n < files.size(); ++n) {
2001
- napi_value obj;
2002
- NAPI_STATUS_THROWS(FromLogFile(env, files[n], &obj));
2003
- NAPI_STATUS_THROWS(napi_set_element(env, ret, n, obj));
2004
- }
2005
-
2006
- return ret;
2007
- }
2008
-
2009
- NAPI_METHOD(db_get_current_wal_file) {
2010
- NAPI_ARGV(1);
2011
-
2012
- Database* database;
2013
- NAPI_STATUS_THROWS(napi_get_value_external(env, argv[0], reinterpret_cast<void**>(&database)));
2014
-
2015
- std::unique_ptr<rocksdb::LogFile> file;
2016
- ROCKS_STATUS_THROWS(database->db_->GetCurrentWalFile(&file));
2017
-
2018
- napi_value ret;
2019
- NAPI_STATUS_THROWS(FromLogFile(env, file, &ret));
2020
-
2021
- return ret;
2022
- }
2023
-
2024
1556
  NAPI_INIT() {
2025
1557
  NAPI_EXPORT_FUNCTION(db_init);
2026
1558
  NAPI_EXPORT_FUNCTION(db_open);
@@ -2040,10 +1572,6 @@ NAPI_INIT() {
2040
1572
  NAPI_EXPORT_FUNCTION(updates_close);
2041
1573
  NAPI_EXPORT_FUNCTION(updates_next);
2042
1574
 
2043
- NAPI_EXPORT_FUNCTION(db_flush_wal);
2044
- NAPI_EXPORT_FUNCTION(db_get_sorted_wal_files);
2045
- NAPI_EXPORT_FUNCTION(db_get_current_wal_file);
2046
-
2047
1575
  NAPI_EXPORT_FUNCTION(batch_do);
2048
1576
  NAPI_EXPORT_FUNCTION(batch_init);
2049
1577
  NAPI_EXPORT_FUNCTION(batch_put);