1 // Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
2 #include <iostream>
3 #include <memory>
4 #include <vector>
5 #include <v8.h>
6 #include <node.h>
7 
8 #include "db/_wrapper.h"
9 #include "rocksdb/db.h"
10 #include "rocksdb/options.h"
11 #include "rocksdb/slice.h"
12 
13 namespace {
printWithBackSlashes(std::string str)14   void printWithBackSlashes(std::string str) {
15     for (std::string::size_type i = 0; i < str.size(); i++) {
16       if (str[i] == '\\' || str[i] == '"') {
17         std::cout << "\\";
18       }
19 
20       std::cout << str[i];
21     }
22   }
23 
has_key_for_array(Local<Object> obj,std::string key)24   bool has_key_for_array(Local<Object> obj, std::string key) {
25     return obj->Has(String::NewSymbol(key.c_str())) &&
26         obj->Get(String::NewSymbol(key.c_str()))->IsArray();
27   }
28 }
29 
30 using namespace v8;
31 
32 
33 Persistent<Function> DBWrapper::constructor;
34 
DBWrapper()35 DBWrapper::DBWrapper() {
36   options_.IncreaseParallelism();
37   options_.OptimizeLevelStyleCompaction();
38   options_.disable_auto_compactions = true;
39   options_.create_if_missing = true;
40 }
41 
~DBWrapper()42 DBWrapper::~DBWrapper() {
43   delete db_;
44 }
45 
HasFamilyNamed(std::string & name,DBWrapper * db)46 bool DBWrapper::HasFamilyNamed(std::string& name, DBWrapper* db) {
47   return db->columnFamilies_.find(name) != db->columnFamilies_.end();
48 }
49 
50 
Init(Handle<Object> exports)51 void DBWrapper::Init(Handle<Object> exports) {
52   Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
53   tpl->SetClassName(String::NewSymbol("DBWrapper"));
54   tpl->InstanceTemplate()->SetInternalFieldCount(8);
55   tpl->PrototypeTemplate()->Set(String::NewSymbol("open"),
56       FunctionTemplate::New(Open)->GetFunction());
57   tpl->PrototypeTemplate()->Set(String::NewSymbol("get"),
58       FunctionTemplate::New(Get)->GetFunction());
59   tpl->PrototypeTemplate()->Set(String::NewSymbol("put"),
60       FunctionTemplate::New(Put)->GetFunction());
61   tpl->PrototypeTemplate()->Set(String::NewSymbol("delete"),
62       FunctionTemplate::New(Delete)->GetFunction());
63   tpl->PrototypeTemplate()->Set(String::NewSymbol("dump"),
64       FunctionTemplate::New(Dump)->GetFunction());
65   tpl->PrototypeTemplate()->Set(String::NewSymbol("createColumnFamily"),
66       FunctionTemplate::New(CreateColumnFamily)->GetFunction());
67   tpl->PrototypeTemplate()->Set(String::NewSymbol("writeBatch"),
68       FunctionTemplate::New(WriteBatch)->GetFunction());
69   tpl->PrototypeTemplate()->Set(String::NewSymbol("compactRange"),
70       FunctionTemplate::New(CompactRange)->GetFunction());
71 
72   constructor = Persistent<Function>::New(tpl->GetFunction());
73   exports->Set(String::NewSymbol("DBWrapper"), constructor);
74 }
75 
Open(const Arguments & args)76 Handle<Value> DBWrapper::Open(const Arguments& args) {
77   HandleScope scope;
78   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
79 
80   if (!(args[0]->IsString() &&
81        (args[1]->IsUndefined() || args[1]->IsArray()))) {
82     return scope.Close(Boolean::New(false));
83   }
84 
85   std::string db_file = *v8::String::Utf8Value(args[0]->ToString());
86 
87   std::vector<std::string> cfs = {ROCKSDB_NAMESPACE::kDefaultColumnFamilyName};
88 
89   if (!args[1]->IsUndefined()) {
90     Handle<Array> array = Handle<Array>::Cast(args[1]);
91     for (uint i = 0; i < array->Length(); i++) {
92       if (!array->Get(i)->IsString()) {
93         return scope.Close(Boolean::New(false));
94       }
95 
96       cfs.push_back(*v8::String::Utf8Value(array->Get(i)->ToString()));
97     }
98   }
99 
100   if (cfs.size() == 1) {
101     db_wrapper->status_ = ROCKSDB_NAMESPACE::DB::Open(
102         db_wrapper->options_, db_file, &db_wrapper->db_);
103 
104     return scope.Close(Boolean::New(db_wrapper->status_.ok()));
105   }
106 
107   std::vector<ROCKSDB_NAMESPACE::ColumnFamilyDescriptor> families;
108 
109   for (std::vector<int>::size_type i = 0; i < cfs.size(); i++) {
110     families.push_back(ROCKSDB_NAMESPACE::ColumnFamilyDescriptor(
111         cfs[i], ROCKSDB_NAMESPACE::ColumnFamilyOptions()));
112   }
113 
114   std::vector<ROCKSDB_NAMESPACE::ColumnFamilyHandle*> handles;
115   db_wrapper->status_ = ROCKSDB_NAMESPACE::DB::Open(
116       db_wrapper->options_, db_file, families, &handles, &db_wrapper->db_);
117 
118   if (!db_wrapper->status_.ok()) {
119     return scope.Close(Boolean::New(db_wrapper->status_.ok()));
120   }
121 
122   for (std::vector<int>::size_type i = 0; i < handles.size(); i++) {
123     db_wrapper->columnFamilies_[cfs[i]] = handles[i];
124   }
125 
126   return scope.Close(Boolean::New(true));
127 }
128 
129 
New(const Arguments & args)130 Handle<Value> DBWrapper::New(const Arguments& args) {
131   HandleScope scope;
132   Handle<Value> to_return;
133 
134   if (args.IsConstructCall()) {
135     DBWrapper* db_wrapper = new DBWrapper();
136     db_wrapper->Wrap(args.This());
137 
138     return args.This();
139   }
140 
141   const int argc = 0;
142   Local<Value> argv[0] = {};
143 
144   return scope.Close(constructor->NewInstance(argc, argv));
145 }
146 
Get(const Arguments & args)147 Handle<Value> DBWrapper::Get(const Arguments& args) {
148   HandleScope scope;
149 
150   if (!(args[0]->IsString() &&
151         (args[1]->IsUndefined() || args[1]->IsString()))) {
152     return scope.Close(Null());
153   }
154 
155   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
156   std::string key       = *v8::String::Utf8Value(args[0]->ToString());
157   std::string cf        = *v8::String::Utf8Value(args[1]->ToString());
158   std::string value;
159 
160   if (args[1]->IsUndefined()) {
161     db_wrapper->status_ =
162         db_wrapper->db_->Get(ROCKSDB_NAMESPACE::ReadOptions(), key, &value);
163   } else if (db_wrapper->HasFamilyNamed(cf, db_wrapper)) {
164     db_wrapper->status_ =
165         db_wrapper->db_->Get(ROCKSDB_NAMESPACE::ReadOptions(),
166                              db_wrapper->columnFamilies_[cf], key, &value);
167   } else {
168     return scope.Close(Null());
169   }
170 
171   Handle<Value> v = db_wrapper->status_.ok() ?
172       String::NewSymbol(value.c_str()) : Null();
173 
174   return scope.Close(v);
175 }
176 
Put(const Arguments & args)177 Handle<Value> DBWrapper::Put(const Arguments& args) {
178   HandleScope scope;
179 
180   if (!(args[0]->IsString() && args[1]->IsString() &&
181        (args[2]->IsUndefined() || args[2]->IsString()))) {
182     return scope.Close(Boolean::New(false));
183   }
184 
185   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
186   std::string key       = *v8::String::Utf8Value(args[0]->ToString());
187   std::string value     = *v8::String::Utf8Value(args[1]->ToString());
188   std::string cf        = *v8::String::Utf8Value(args[2]->ToString());
189 
190   if (args[2]->IsUndefined()) {
191     db_wrapper->status_ =
192         db_wrapper->db_->Put(ROCKSDB_NAMESPACE::WriteOptions(), key, value);
193   } else if (db_wrapper->HasFamilyNamed(cf, db_wrapper)) {
194     db_wrapper->status_ =
195         db_wrapper->db_->Put(ROCKSDB_NAMESPACE::WriteOptions(),
196                              db_wrapper->columnFamilies_[cf], key, value);
197   } else {
198     return scope.Close(Boolean::New(false));
199   }
200 
201 
202   return scope.Close(Boolean::New(db_wrapper->status_.ok()));
203 }
204 
Delete(const Arguments & args)205 Handle<Value> DBWrapper::Delete(const Arguments& args) {
206   HandleScope scope;
207 
208   if (!args[0]->IsString()) {
209     return scope.Close(Boolean::New(false));
210   }
211 
212   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
213   std::string arg0      = *v8::String::Utf8Value(args[0]->ToString());
214   std::string arg1      = *v8::String::Utf8Value(args[1]->ToString());
215 
216   if (args[1]->IsUndefined()) {
217     db_wrapper->status_ =
218         db_wrapper->db_->Delete(ROCKSDB_NAMESPACE::WriteOptions(), arg0);
219   } else {
220     if (!db_wrapper->HasFamilyNamed(arg1, db_wrapper)) {
221       return scope.Close(Boolean::New(false));
222     }
223     db_wrapper->status_ =
224         db_wrapper->db_->Delete(ROCKSDB_NAMESPACE::WriteOptions(),
225                                 db_wrapper->columnFamilies_[arg1], arg0);
226   }
227 
228   return scope.Close(Boolean::New(db_wrapper->status_.ok()));
229 }
230 
Dump(const Arguments & args)231 Handle<Value> DBWrapper::Dump(const Arguments& args) {
232   HandleScope scope;
233   std::unique_ptr<ROCKSDB_NAMESPACE::Iterator> iterator;
234   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
235   std::string arg0      = *v8::String::Utf8Value(args[0]->ToString());
236 
237   if (args[0]->IsUndefined()) {
238     iterator.reset(
239         db_wrapper->db_->NewIterator(ROCKSDB_NAMESPACE::ReadOptions()));
240   } else {
241     if (!db_wrapper->HasFamilyNamed(arg0, db_wrapper)) {
242       return scope.Close(Boolean::New(false));
243     }
244 
245     iterator.reset(db_wrapper->db_->NewIterator(
246         ROCKSDB_NAMESPACE::ReadOptions(), db_wrapper->columnFamilies_[arg0]));
247   }
248 
249   iterator->SeekToFirst();
250 
251   while (iterator->Valid()) {
252     std::cout << "\"";
253     printWithBackSlashes(iterator->key().ToString());
254     std::cout << "\" => \"";
255     printWithBackSlashes(iterator->value().ToString());
256     std::cout << "\"\n";
257     iterator->Next();
258   }
259 
260   return scope.Close(Boolean::New(true));
261 }
262 
CreateColumnFamily(const Arguments & args)263 Handle<Value> DBWrapper::CreateColumnFamily(const Arguments& args) {
264   HandleScope scope;
265 
266   if (!args[0]->IsString()) {
267     return scope.Close(Boolean::New(false));
268   }
269 
270   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
271   std::string cf_name   = *v8::String::Utf8Value(args[0]->ToString());
272 
273   if (db_wrapper->HasFamilyNamed(cf_name, db_wrapper)) {
274     return scope.Close(Boolean::New(false));
275   }
276 
277   ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf;
278   db_wrapper->status_ = db_wrapper->db_->CreateColumnFamily(
279       ROCKSDB_NAMESPACE::ColumnFamilyOptions(), cf_name, &cf);
280 
281   if (!db_wrapper->status_.ok()) {
282     return scope.Close(Boolean::New(false));
283   }
284 
285   db_wrapper->columnFamilies_[cf_name] = cf;
286 
287   return scope.Close(Boolean::New(true));
288 }
289 
AddToBatch(ROCKSDB_NAMESPACE::WriteBatch & batch,bool del,Handle<Array> array)290 bool DBWrapper::AddToBatch(ROCKSDB_NAMESPACE::WriteBatch& batch, bool del,
291                            Handle<Array> array) {
292   Handle<Array> put_pair;
293   for (uint i = 0; i < array->Length(); i++) {
294     if (del) {
295       if (!array->Get(i)->IsString()) {
296         return false;
297       }
298 
299       batch.Delete(*v8::String::Utf8Value(array->Get(i)->ToString()));
300       continue;
301     }
302 
303     if (!array->Get(i)->IsArray()) {
304       return false;
305     }
306 
307     put_pair = Handle<Array>::Cast(array->Get(i));
308 
309     if (!put_pair->Get(0)->IsString() || !put_pair->Get(1)->IsString()) {
310       return false;
311     }
312 
313     batch.Put(
314         *v8::String::Utf8Value(put_pair->Get(0)->ToString()),
315         *v8::String::Utf8Value(put_pair->Get(1)->ToString()));
316   }
317 
318   return true;
319 }
320 
AddToBatch(ROCKSDB_NAMESPACE::WriteBatch & batch,bool del,Handle<Array> array,DBWrapper * db_wrapper,std::string cf)321 bool DBWrapper::AddToBatch(ROCKSDB_NAMESPACE::WriteBatch& batch, bool del,
322                            Handle<Array> array, DBWrapper* db_wrapper,
323                            std::string cf) {
324   Handle<Array> put_pair;
325   for (uint i = 0; i < array->Length(); i++) {
326     if (del) {
327       if (!array->Get(i)->IsString()) {
328         return false;
329       }
330 
331       batch.Delete(
332           db_wrapper->columnFamilies_[cf],
333           *v8::String::Utf8Value(array->Get(i)->ToString()));
334       continue;
335     }
336 
337     if (!array->Get(i)->IsArray()) {
338       return false;
339     }
340 
341     put_pair = Handle<Array>::Cast(array->Get(i));
342 
343     if (!put_pair->Get(0)->IsString() || !put_pair->Get(1)->IsString()) {
344       return false;
345     }
346 
347     batch.Put(
348         db_wrapper->columnFamilies_[cf],
349         *v8::String::Utf8Value(put_pair->Get(0)->ToString()),
350         *v8::String::Utf8Value(put_pair->Get(1)->ToString()));
351   }
352 
353   return true;
354 }
355 
WriteBatch(const Arguments & args)356 Handle<Value> DBWrapper::WriteBatch(const Arguments& args) {
357   HandleScope scope;
358 
359   if (!args[0]->IsArray()) {
360     return scope.Close(Boolean::New(false));
361   }
362 
363   DBWrapper* db_wrapper     = ObjectWrap::Unwrap<DBWrapper>(args.This());
364   Handle<Array> sub_batches = Handle<Array>::Cast(args[0]);
365   Local<Object> sub_batch;
366   ROCKSDB_NAMESPACE::WriteBatch batch;
367   bool well_formed;
368 
369   for (uint i = 0; i < sub_batches->Length(); i++) {
370     if (!sub_batches->Get(i)->IsObject()) {
371       return scope.Close(Boolean::New(false));
372     }
373     sub_batch = sub_batches->Get(i)->ToObject();
374 
375     if (sub_batch->Has(String::NewSymbol("column_family"))) {
376       if (!has_key_for_array(sub_batch, "put") &&
377           !has_key_for_array(sub_batch, "delete")) {
378         return scope.Close(Boolean::New(false));
379       }
380 
381       well_formed = db_wrapper->AddToBatch(
382         batch, false,
383         Handle<Array>::Cast(sub_batch->Get(String::NewSymbol("put"))),
384         db_wrapper, *v8::String::Utf8Value(sub_batch->Get(
385             String::NewSymbol("column_family"))));
386 
387       well_formed = db_wrapper->AddToBatch(
388           batch, true,
389           Handle<Array>::Cast(sub_batch->Get(String::NewSymbol("delete"))),
390           db_wrapper, *v8::String::Utf8Value(sub_batch->Get(
391           String::NewSymbol("column_family"))));
392     } else {
393       well_formed = db_wrapper->AddToBatch(
394           batch, false,
395           Handle<Array>::Cast(sub_batch->Get(String::NewSymbol("put"))));
396       well_formed = db_wrapper->AddToBatch(
397           batch, true,
398           Handle<Array>::Cast(sub_batch->Get(String::NewSymbol("delete"))));
399 
400       if (!well_formed) {
401         return scope.Close(Boolean::New(false));
402       }
403     }
404   }
405 
406   db_wrapper->status_ =
407       db_wrapper->db_->Write(ROCKSDB_NAMESPACE::WriteOptions(), &batch);
408 
409   return scope.Close(Boolean::New(db_wrapper->status_.ok()));
410 }
411 
CompactRangeDefault(const Arguments & args)412 Handle<Value> DBWrapper::CompactRangeDefault(const Arguments& args) {
413   HandleScope scope;
414 
415   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
416   ROCKSDB_NAMESPACE::Slice begin = *v8::String::Utf8Value(args[0]->ToString());
417   ROCKSDB_NAMESPACE::Slice end = *v8::String::Utf8Value(args[1]->ToString());
418   db_wrapper->status_    = db_wrapper->db_->CompactRange(&end, &begin);
419 
420   return scope.Close(Boolean::New(db_wrapper->status_.ok()));
421 }
422 
CompactColumnFamily(const Arguments & args)423 Handle<Value> DBWrapper::CompactColumnFamily(const Arguments& args) {
424   HandleScope scope;
425 
426   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
427   ROCKSDB_NAMESPACE::Slice begin = *v8::String::Utf8Value(args[0]->ToString());
428   ROCKSDB_NAMESPACE::Slice end = *v8::String::Utf8Value(args[1]->ToString());
429   std::string cf        = *v8::String::Utf8Value(args[2]->ToString());
430   db_wrapper->status_    = db_wrapper->db_->CompactRange(
431       db_wrapper->columnFamilies_[cf], &begin, &end);
432 
433   return scope.Close(Boolean::New(db_wrapper->status_.ok()));
434 }
435 
CompactOptions(const Arguments & args)436 Handle<Value> DBWrapper::CompactOptions(const Arguments& args) {
437   HandleScope scope;
438 
439   if (!args[2]->IsObject()) {
440     return scope.Close(Boolean::New(false));
441   }
442 
443   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
444   ROCKSDB_NAMESPACE::Slice begin = *v8::String::Utf8Value(args[0]->ToString());
445   ROCKSDB_NAMESPACE::Slice end = *v8::String::Utf8Value(args[1]->ToString());
446   Local<Object> options  = args[2]->ToObject();
447   int target_level = -1, target_path_id = 0;
448 
449   if (options->Has(String::NewSymbol("target_level")) &&
450       options->Get(String::NewSymbol("target_level"))->IsInt32()) {
451     target_level = (int)(options->Get(
452         String::NewSymbol("target_level"))->ToInt32()->Value());
453 
454     if (options->Has(String::NewSymbol("target_path_id")) ||
455         options->Get(String::NewSymbol("target_path_id"))->IsInt32()) {
456       target_path_id = (int)(options->Get(
457           String::NewSymbol("target_path_id"))->ToInt32()->Value());
458     }
459   }
460 
461   db_wrapper->status_ = db_wrapper->db_->CompactRange(
462     &begin, &end, true, target_level, target_path_id
463   );
464 
465   return scope.Close(Boolean::New(db_wrapper->status_.ok()));
466 }
467 
CompactAll(const Arguments & args)468 Handle<Value> DBWrapper::CompactAll(const Arguments& args) {
469   HandleScope scope;
470 
471   if (!args[2]->IsObject() || !args[3]->IsString()) {
472     return scope.Close(Boolean::New(false));
473   }
474 
475   DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
476   ROCKSDB_NAMESPACE::Slice begin = *v8::String::Utf8Value(args[0]->ToString());
477   ROCKSDB_NAMESPACE::Slice end = *v8::String::Utf8Value(args[1]->ToString());
478   Local<Object> options = args[2]->ToObject();
479   std::string cf        = *v8::String::Utf8Value(args[3]->ToString());
480 
481   int target_level = -1, target_path_id = 0;
482 
483   if (options->Has(String::NewSymbol("target_level")) &&
484       options->Get(String::NewSymbol("target_level"))->IsInt32()) {
485     target_level = (int)(options->Get(
486         String::NewSymbol("target_level"))->ToInt32()->Value());
487 
488     if (options->Has(String::NewSymbol("target_path_id")) ||
489         options->Get(String::NewSymbol("target_path_id"))->IsInt32()) {
490       target_path_id = (int)(options->Get(
491           String::NewSymbol("target_path_id"))->ToInt32()->Value());
492     }
493   }
494 
495   db_wrapper->status_ = db_wrapper->db_->CompactRange(
496     db_wrapper->columnFamilies_[cf], &begin, &end, true, target_level,
497     target_path_id);
498 
499   return scope.Close(Boolean::New(db_wrapper->status_.ok()));
500 }
501 
CompactRange(const Arguments & args)502 Handle<Value> DBWrapper::CompactRange(const Arguments& args) {
503   HandleScope scope;
504 
505   if (!args[0]->IsString() || !args[1]->IsString()) {
506     return scope.Close(Boolean::New(false));
507   }
508 
509   switch(args.Length()) {
510   case 2:
511     return CompactRangeDefault(args);
512   case 3:
513     return args[2]->IsString() ? CompactColumnFamily(args) :
514         CompactOptions(args);
515   default:
516     return CompactAll(args);
517   }
518 }
519 
Close(const Arguments & args)520 Handle<Value> DBWrapper::Close(const Arguments& args) {
521   HandleScope scope;
522 
523   delete ObjectWrap::Unwrap<DBWrapper>(args.This());
524 
525   return scope.Close(Null());
526 }
527