@nxtedition/rocksdb 7.0.62 → 7.0.65

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