Remove tools/rdb from main repo (#9399)

Summary:
This PR is one proposal to resolve https://github.com/facebook/rocksdb/issues/9382.

Looking at the code, I can't think of a reason why rdb is an internal component of RocksDB: it does not require
any header files NOT in `include/rocksdb`. It's a better idea to host it somewhere else.

Plus, rdb requires python2 which is not supported any more. No fixes or improvements will be made, even for potential
security bugs (https://www.python.org/doc/sunset-python-2/).

Pull Request resolved: https://github.com/facebook/rocksdb/pull/9399

Test Plan: make check

Reviewed By: ajkr

Differential Revision: D33641965

Pulled By: riversand963

fbshipit-source-id: 2a6a74693e5de36834f355e41d6865db206af48b
main
Yanqin Jin 3 years ago committed by Facebook GitHub Bot
parent 50135c1bf3
commit 5d30668cab
  1. 2
      Makefile
  2. 1
      tools/rdb/.gitignore
  3. 178
      tools/rdb/API.md
  4. 40
      tools/rdb/README.md
  5. 25
      tools/rdb/binding.gyp
  6. 523
      tools/rdb/db_wrapper.cc
  7. 58
      tools/rdb/db_wrapper.h
  8. 3
      tools/rdb/rdb
  9. 14
      tools/rdb/rdb.cc
  10. 125
      tools/rdb/unit_test.js

@ -561,7 +561,7 @@ ifneq ($(filter check-headers, $(MAKECMDGOALS)),)
# TODO: add/support JNI headers
DEV_HEADER_DIRS := $(sort include/ $(dir $(ALL_SOURCES)))
# Some headers like in port/ are platform-specific
DEV_HEADERS := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | egrep -v 'port/|plugin/|lua/|range_tree/|tools/rdb/db_wrapper.h|include/rocksdb/utilities/env_librados.h')
DEV_HEADERS := $(shell $(FIND) $(DEV_HEADER_DIRS) -type f -name '*.h' | egrep -v 'port/|plugin/|lua/|range_tree/|include/rocksdb/utilities/env_librados.h')
else
DEV_HEADERS :=
endif

@ -1 +0,0 @@
build

@ -1,178 +0,0 @@
# JavaScript API
## DBWrapper
### Constructor
# Creates a new database wrapper object
RDB()
### Open
# Open a new or existing RocksDB database.
#
# db_name (string) - Location of the database (inside the
# `/tmp` directory).
# column_families (string[]) - Names of additional column families
# beyond the default. If there are no other
# column families, this argument can be
# left off.
#
# Returns true if the database was opened successfully, or false otherwise
db_obj.(db_name, column_families = [])
### Get
# Get the value of a given key.
#
# key (string) - Which key to get the value of.
# column_family (string) - Which column family to check for the key.
# This argument can be left off for the default
# column family
#
# Returns the value (string) that is associated with the given key if
# one exists, or null otherwise.
db_obj.get(key, column_family = { default })
### Put
# Associate a value with a key.
#
# key (string) - Which key to associate the value with.
# value (string) - The value to associate with the key.
# column_family (string) - Which column family to put the key-value pair
# in. This argument can be left off for the
# default column family.
#
# Returns true if the key-value pair was successfully stored in the
# database, or false otherwise.
db_obj.put(key, value, column_family = { default })
### Delete
# Delete a value associated with a given key.
#
# key (string) - Which key to delete the value of..
# column_family (string) - Which column family to check for the key.
# This argument can be left off for the default
# column family
#
# Returns true if an error occurred while trying to delete the key in
# the database, or false otherwise. Note that this is NOT the same as
# whether a value was deleted; in the case of a specified key not having
# a value, this will still return true. Use the `get` method prior to
# this method to check if a value existed before the call to `delete`.
db_obj.delete(key, column_family = { default })
### Dump
# Print out all the key-value pairs in a given column family of the
# database.
#
# column_family (string) - Which column family to dump the pairs from.
# This argument can be left off for the default
# column family.
#
# Returns true if the keys were successfully read from the database, or
# false otherwise.
db_obj.dump(column_family = { default })
### WriteBatch
# Execute an atomic batch of writes (i.e. puts and deletes) to the
# database.
#
# cf_batches (BatchObject[]; see below) - Put and Delete writes grouped
# by column family to execute
# atomically.
#
# Returns true if the argument array was well-formed and was
# successfully written to the database, or false otherwise.
db_obj.writeBatch(cf_batches)
### CreateColumnFamily
# Create a new column family for the database.
#
# column_family_name (string) - Name of the new column family.
#
# Returns true if the new column family was successfully created, or
# false otherwise.
db_obj.createColumnFamily(column_family_name)
### CompactRange
# Compact the underlying storage for a given range.
#
# In addition to the endpoints of the range, the method is overloaded to
# accept a non-default column family, a set of options, or both.
#
# begin (string) - First key in the range to compact.
# end (string) - Last key in the range to compact.
# options (object) - Contains a subset of the following key-value
# pairs:
# * 'target_level' => int
# * 'target_path_id' => int
# column_family (string) - Which column family to compact the range in.
db_obj.compactRange(begin, end)
db_obj.compactRange(begin, end, options)
db_obj.compactRange(begin, end, column_family)
db_obj.compactRange(begin, end, options, column_family)
### Close
# Close an a database and free the memory associated with it.
#
# Return null.
# db_obj.close()
## BatchObject
### Structure
A BatchObject must have at least one of the following key-value pairs:
* 'put' => Array of ['string1', 'string1'] pairs, each of which signifies that
the key 'string1' should be associated with the value 'string2'
* 'delete' => Array of strings, each of which is a key whose value should be
deleted.
The following key-value pair is optional:
* 'column_family' => The name (string) of the column family to apply the
changes to.
### Examples
# Writes the key-value pairs 'firstname' => 'Saghm' and
# 'lastname' => 'Rossi' atomically to the database.
db_obj.writeBatch([
{
put: [ ['firstname', 'Saghm'], ['lastname', 'Rossi'] ]
}
]);
# Deletes the values associated with 'firstname' and 'lastname' in
# the default column family and adds the key 'number_of_people' with
# with the value '2'. Additionally, adds the key-value pair
# 'name' => 'Saghm Rossi' to the column family 'user1' and the pair
# 'name' => 'Matt Blaze' to the column family 'user2'. All writes
# are done atomically.
db_obj.writeBatch([
{
put: [ ['number_of_people', '2'] ],
delete: ['firstname', 'lastname']
},
{
put: [ ['name', 'Saghm Rossi'] ],
column_family: 'user1'
},
{
put: [ ['name', Matt Blaze'] ],
column_family: 'user2'
}
]);

@ -1,40 +0,0 @@
# RDB - RocksDB Shell
RDB is a NodeJS-based shell interface to RocksDB. It can also be used as a
JavaScript binding for RocksDB within a Node application.
## Setup/Compilation
### Requirements
* static RocksDB library (i.e. librocksdb.a)
* libsnappy
* node (tested onv0.10.33, no guarantees on anything else!)
* node-gyp
* python2 (for node-gyp; tested with 2.7.8)
### Installation
NOTE: If your default `python` binary is not a version of python2, add
the arguments `--python /path/to/python2` to the `node-gyp` commands.
1. Make sure you have the static library (i.e. "librocksdb.a") in the root
directory of your rocksdb installation. If not, `cd` there and run
`make static_lib`.
2. Run `node-gyp configure` to generate the build.
3. Run `node-gyp build` to compile RDB.
## Usage
### Running the shell
Assuming everything compiled correctly, you can run the `rdb` executable
located in the root of the `tools/rdb` directory to start the shell. The file is
just a shell script that runs the node shell and loads the constructor for the
RDB object into the top-level function `RDB`.
### JavaScript API
See `API.md` for how to use RocksDB from the shell.

@ -1,25 +0,0 @@
{
"targets": [
{
"target_name": "rdb",
"sources": [
"rdb.cc",
"db_wrapper.cc",
"db_wrapper.h"
],
"cflags_cc!": [
"-fno-exceptions"
],
"cflags_cc+": [
"-std=c++11",
],
"include_dirs+": [
"../../include"
],
"libraries": [
"../../../librocksdb.a",
"-lsnappy"
],
}
]
}

@ -1,523 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
#include <iostream>
#include <memory>
#include <vector>
#include <v8.h>
#include <node.h>
#include "db/_wrapper.h"
#include "rocksdb/db.h"
#include "rocksdb/options.h"
#include "rocksdb/slice.h"
namespace {
void printWithBackSlashes(std::string str) {
for (std::string::size_type i = 0; i < str.size(); i++) {
if (str[i] == '\\' || str[i] == '"') {
std::cout << "\\";
}
std::cout << str[i];
}
}
bool has_key_for_array(Local<Object> obj, std::string key) {
return obj->Has(String::NewSymbol(key.c_str())) &&
obj->Get(String::NewSymbol(key.c_str()))->IsArray();
}
}
Persistent<Function> DBWrapper::constructor;
DBWrapper::DBWrapper() {
options_.IncreaseParallelism();
options_.OptimizeLevelStyleCompaction();
options_.disable_auto_compactions = true;
options_.create_if_missing = true;
}
DBWrapper::~DBWrapper() {
delete db_;
}
bool DBWrapper::HasFamilyNamed(std::string& name, DBWrapper* db) {
return db->columnFamilies_.find(name) != db->columnFamilies_.end();
}
void DBWrapper::Init(Handle<Object> exports) {
Local<FunctionTemplate> tpl = FunctionTemplate::New(New);
tpl->SetClassName(String::NewSymbol("DBWrapper"));
tpl->InstanceTemplate()->SetInternalFieldCount(8);
tpl->PrototypeTemplate()->Set(String::NewSymbol("open"),
FunctionTemplate::New(Open)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("get"),
FunctionTemplate::New(Get)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("put"),
FunctionTemplate::New(Put)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("delete"),
FunctionTemplate::New(Delete)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("dump"),
FunctionTemplate::New(Dump)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("createColumnFamily"),
FunctionTemplate::New(CreateColumnFamily)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("writeBatch"),
FunctionTemplate::New(WriteBatch)->GetFunction());
tpl->PrototypeTemplate()->Set(String::NewSymbol("compactRange"),
FunctionTemplate::New(CompactRange)->GetFunction());
constructor = Persistent<Function>::New(tpl->GetFunction());
exports->Set(String::NewSymbol("DBWrapper"), constructor);
}
Handle<Value> DBWrapper::Open(const Arguments& args) {
HandleScope scope;
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
if (!(args[0]->IsString() &&
(args[1]->IsUndefined() || args[1]->IsArray()))) {
return scope.Close(Boolean::New(false));
}
std::string db_file = *v8::String::Utf8Value(args[0]->ToString());
std::vector<std::string> cfs = {ROCKSDB_NAMESPACE::kDefaultColumnFamilyName};
if (!args[1]->IsUndefined()) {
Handle<Array> array = Handle<Array>::Cast(args[1]);
for (uint i = 0; i < array->Length(); i++) {
if (!array->Get(i)->IsString()) {
return scope.Close(Boolean::New(false));
}
cfs.push_back(*v8::String::Utf8Value(array->Get(i)->ToString()));
}
}
if (cfs.size() == 1) {
db_wrapper->status_ = ROCKSDB_NAMESPACE::DB::Open(
db_wrapper->options_, db_file, &db_wrapper->db_);
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
std::vector<ROCKSDB_NAMESPACE::ColumnFamilyDescriptor> families;
for (std::vector<int>::size_type i = 0; i < cfs.size(); i++) {
families.push_back(ROCKSDB_NAMESPACE::ColumnFamilyDescriptor(
cfs[i], ROCKSDB_NAMESPACE::ColumnFamilyOptions()));
}
std::vector<ROCKSDB_NAMESPACE::ColumnFamilyHandle*> handles;
db_wrapper->status_ = ROCKSDB_NAMESPACE::DB::Open(
db_wrapper->options_, db_file, families, &handles, &db_wrapper->db_);
if (!db_wrapper->status_.ok()) {
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
for (std::vector<int>::size_type i = 0; i < handles.size(); i++) {
db_wrapper->columnFamilies_[cfs[i]] = handles[i];
}
return scope.Close(Boolean::New(true));
}
Handle<Value> DBWrapper::New(const Arguments& args) {
HandleScope scope;
Handle<Value> to_return;
if (args.IsConstructCall()) {
DBWrapper* db_wrapper = new DBWrapper();
db_wrapper->Wrap(args.This());
return args.This();
}
const int argc = 0;
Local<Value> argv[0] = {};
return scope.Close(constructor->NewInstance(argc, argv));
}
Handle<Value> DBWrapper::Get(const Arguments& args) {
HandleScope scope;
if (!(args[0]->IsString() &&
(args[1]->IsUndefined() || args[1]->IsString()))) {
return scope.Close(Null());
}
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
std::string key = *v8::String::Utf8Value(args[0]->ToString());
std::string cf = *v8::String::Utf8Value(args[1]->ToString());
std::string value;
if (args[1]->IsUndefined()) {
db_wrapper->status_ =
db_wrapper->db_->Get(ROCKSDB_NAMESPACE::ReadOptions(), key, &value);
} else if (db_wrapper->HasFamilyNamed(cf, db_wrapper)) {
db_wrapper->status_ =
db_wrapper->db_->Get(ROCKSDB_NAMESPACE::ReadOptions(),
db_wrapper->columnFamilies_[cf], key, &value);
} else {
return scope.Close(Null());
}
Handle<Value> v = db_wrapper->status_.ok() ?
String::NewSymbol(value.c_str()) : Null();
return scope.Close(v);
}
Handle<Value> DBWrapper::Put(const Arguments& args) {
HandleScope scope;
if (!(args[0]->IsString() && args[1]->IsString() &&
(args[2]->IsUndefined() || args[2]->IsString()))) {
return scope.Close(Boolean::New(false));
}
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
std::string key = *v8::String::Utf8Value(args[0]->ToString());
std::string value = *v8::String::Utf8Value(args[1]->ToString());
std::string cf = *v8::String::Utf8Value(args[2]->ToString());
if (args[2]->IsUndefined()) {
db_wrapper->status_ =
db_wrapper->db_->Put(ROCKSDB_NAMESPACE::WriteOptions(), key, value);
} else if (db_wrapper->HasFamilyNamed(cf, db_wrapper)) {
db_wrapper->status_ =
db_wrapper->db_->Put(ROCKSDB_NAMESPACE::WriteOptions(),
db_wrapper->columnFamilies_[cf], key, value);
} else {
return scope.Close(Boolean::New(false));
}
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
Handle<Value> DBWrapper::Delete(const Arguments& args) {
HandleScope scope;
if (!args[0]->IsString()) {
return scope.Close(Boolean::New(false));
}
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
std::string arg0 = *v8::String::Utf8Value(args[0]->ToString());
std::string arg1 = *v8::String::Utf8Value(args[1]->ToString());
if (args[1]->IsUndefined()) {
db_wrapper->status_ =
db_wrapper->db_->Delete(ROCKSDB_NAMESPACE::WriteOptions(), arg0);
} else {
if (!db_wrapper->HasFamilyNamed(arg1, db_wrapper)) {
return scope.Close(Boolean::New(false));
}
db_wrapper->status_ =
db_wrapper->db_->Delete(ROCKSDB_NAMESPACE::WriteOptions(),
db_wrapper->columnFamilies_[arg1], arg0);
}
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
Handle<Value> DBWrapper::Dump(const Arguments& args) {
HandleScope scope;
std::unique_ptr<ROCKSDB_NAMESPACE::Iterator> iterator;
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
std::string arg0 = *v8::String::Utf8Value(args[0]->ToString());
if (args[0]->IsUndefined()) {
iterator.reset(
db_wrapper->db_->NewIterator(ROCKSDB_NAMESPACE::ReadOptions()));
} else {
if (!db_wrapper->HasFamilyNamed(arg0, db_wrapper)) {
return scope.Close(Boolean::New(false));
}
iterator.reset(db_wrapper->db_->NewIterator(
ROCKSDB_NAMESPACE::ReadOptions(), db_wrapper->columnFamilies_[arg0]));
}
iterator->SeekToFirst();
while (iterator->Valid()) {
std::cout << "\"";
printWithBackSlashes(iterator->key().ToString());
std::cout << "\" => \"";
printWithBackSlashes(iterator->value().ToString());
std::cout << "\"\n";
iterator->Next();
}
return scope.Close(Boolean::New(true));
}
Handle<Value> DBWrapper::CreateColumnFamily(const Arguments& args) {
HandleScope scope;
if (!args[0]->IsString()) {
return scope.Close(Boolean::New(false));
}
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
std::string cf_name = *v8::String::Utf8Value(args[0]->ToString());
if (db_wrapper->HasFamilyNamed(cf_name, db_wrapper)) {
return scope.Close(Boolean::New(false));
}
ROCKSDB_NAMESPACE::ColumnFamilyHandle* cf;
db_wrapper->status_ = db_wrapper->db_->CreateColumnFamily(
ROCKSDB_NAMESPACE::ColumnFamilyOptions(), cf_name, &cf);
if (!db_wrapper->status_.ok()) {
return scope.Close(Boolean::New(false));
}
db_wrapper->columnFamilies_[cf_name] = cf;
return scope.Close(Boolean::New(true));
}
bool DBWrapper::AddToBatch(ROCKSDB_NAMESPACE::WriteBatch& batch, bool del,
Handle<Array> array) {
Handle<Array> put_pair;
for (uint i = 0; i < array->Length(); i++) {
if (del) {
if (!array->Get(i)->IsString()) {
return false;
}
batch.Delete(*v8::String::Utf8Value(array->Get(i)->ToString()));
continue;
}
if (!array->Get(i)->IsArray()) {
return false;
}
put_pair = Handle<Array>::Cast(array->Get(i));
if (!put_pair->Get(0)->IsString() || !put_pair->Get(1)->IsString()) {
return false;
}
batch.Put(
*v8::String::Utf8Value(put_pair->Get(0)->ToString()),
*v8::String::Utf8Value(put_pair->Get(1)->ToString()));
}
return true;
}
bool DBWrapper::AddToBatch(ROCKSDB_NAMESPACE::WriteBatch& batch, bool del,
Handle<Array> array, DBWrapper* db_wrapper,
std::string cf) {
Handle<Array> put_pair;
for (uint i = 0; i < array->Length(); i++) {
if (del) {
if (!array->Get(i)->IsString()) {
return false;
}
batch.Delete(
db_wrapper->columnFamilies_[cf],
*v8::String::Utf8Value(array->Get(i)->ToString()));
continue;
}
if (!array->Get(i)->IsArray()) {
return false;
}
put_pair = Handle<Array>::Cast(array->Get(i));
if (!put_pair->Get(0)->IsString() || !put_pair->Get(1)->IsString()) {
return false;
}
batch.Put(
db_wrapper->columnFamilies_[cf],
*v8::String::Utf8Value(put_pair->Get(0)->ToString()),
*v8::String::Utf8Value(put_pair->Get(1)->ToString()));
}
return true;
}
Handle<Value> DBWrapper::WriteBatch(const Arguments& args) {
HandleScope scope;
if (!args[0]->IsArray()) {
return scope.Close(Boolean::New(false));
}
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
Handle<Array> sub_batches = Handle<Array>::Cast(args[0]);
Local<Object> sub_batch;
ROCKSDB_NAMESPACE::WriteBatch batch;
bool well_formed;
for (uint i = 0; i < sub_batches->Length(); i++) {
if (!sub_batches->Get(i)->IsObject()) {
return scope.Close(Boolean::New(false));
}
sub_batch = sub_batches->Get(i)->ToObject();
if (sub_batch->Has(String::NewSymbol("column_family"))) {
if (!has_key_for_array(sub_batch, "put") &&
!has_key_for_array(sub_batch, "delete")) {
return scope.Close(Boolean::New(false));
}
well_formed = db_wrapper->AddToBatch(
batch, false,
Handle<Array>::Cast(sub_batch->Get(String::NewSymbol("put"))),
db_wrapper, *v8::String::Utf8Value(sub_batch->Get(
String::NewSymbol("column_family"))));
well_formed = db_wrapper->AddToBatch(
batch, true,
Handle<Array>::Cast(sub_batch->Get(String::NewSymbol("delete"))),
db_wrapper, *v8::String::Utf8Value(sub_batch->Get(
String::NewSymbol("column_family"))));
} else {
well_formed = db_wrapper->AddToBatch(
batch, false,
Handle<Array>::Cast(sub_batch->Get(String::NewSymbol("put"))));
well_formed = db_wrapper->AddToBatch(
batch, true,
Handle<Array>::Cast(sub_batch->Get(String::NewSymbol("delete"))));
if (!well_formed) {
return scope.Close(Boolean::New(false));
}
}
}
db_wrapper->status_ =
db_wrapper->db_->Write(ROCKSDB_NAMESPACE::WriteOptions(), &batch);
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
Handle<Value> DBWrapper::CompactRangeDefault(const Arguments& args) {
HandleScope scope;
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
ROCKSDB_NAMESPACE::Slice begin = *v8::String::Utf8Value(args[0]->ToString());
ROCKSDB_NAMESPACE::Slice end = *v8::String::Utf8Value(args[1]->ToString());
db_wrapper->status_ = db_wrapper->db_->CompactRange(&end, &begin);
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
Handle<Value> DBWrapper::CompactColumnFamily(const Arguments& args) {
HandleScope scope;
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
ROCKSDB_NAMESPACE::Slice begin = *v8::String::Utf8Value(args[0]->ToString());
ROCKSDB_NAMESPACE::Slice end = *v8::String::Utf8Value(args[1]->ToString());
std::string cf = *v8::String::Utf8Value(args[2]->ToString());
db_wrapper->status_ = db_wrapper->db_->CompactRange(
db_wrapper->columnFamilies_[cf], &begin, &end);
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
Handle<Value> DBWrapper::CompactOptions(const Arguments& args) {
HandleScope scope;
if (!args[2]->IsObject()) {
return scope.Close(Boolean::New(false));
}
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
ROCKSDB_NAMESPACE::Slice begin = *v8::String::Utf8Value(args[0]->ToString());
ROCKSDB_NAMESPACE::Slice end = *v8::String::Utf8Value(args[1]->ToString());
Local<Object> options = args[2]->ToObject();
int target_level = -1, target_path_id = 0;
if (options->Has(String::NewSymbol("target_level")) &&
options->Get(String::NewSymbol("target_level"))->IsInt32()) {
target_level = (int)(options->Get(
String::NewSymbol("target_level"))->ToInt32()->Value());
if (options->Has(String::NewSymbol("target_path_id")) ||
options->Get(String::NewSymbol("target_path_id"))->IsInt32()) {
target_path_id = (int)(options->Get(
String::NewSymbol("target_path_id"))->ToInt32()->Value());
}
}
db_wrapper->status_ = db_wrapper->db_->CompactRange(
&begin, &end, true, target_level, target_path_id
);
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
Handle<Value> DBWrapper::CompactAll(const Arguments& args) {
HandleScope scope;
if (!args[2]->IsObject() || !args[3]->IsString()) {
return scope.Close(Boolean::New(false));
}
DBWrapper* db_wrapper = ObjectWrap::Unwrap<DBWrapper>(args.This());
ROCKSDB_NAMESPACE::Slice begin = *v8::String::Utf8Value(args[0]->ToString());
ROCKSDB_NAMESPACE::Slice end = *v8::String::Utf8Value(args[1]->ToString());
Local<Object> options = args[2]->ToObject();
std::string cf = *v8::String::Utf8Value(args[3]->ToString());
int target_level = -1, target_path_id = 0;
if (options->Has(String::NewSymbol("target_level")) &&
options->Get(String::NewSymbol("target_level"))->IsInt32()) {
target_level = (int)(options->Get(
String::NewSymbol("target_level"))->ToInt32()->Value());
if (options->Has(String::NewSymbol("target_path_id")) ||
options->Get(String::NewSymbol("target_path_id"))->IsInt32()) {
target_path_id = (int)(options->Get(
String::NewSymbol("target_path_id"))->ToInt32()->Value());
}
}
db_wrapper->status_ = db_wrapper->db_->CompactRange(
db_wrapper->columnFamilies_[cf], &begin, &end, true, target_level,
target_path_id);
return scope.Close(Boolean::New(db_wrapper->status_.ok()));
}
Handle<Value> DBWrapper::CompactRange(const Arguments& args) {
HandleScope scope;
if (!args[0]->IsString() || !args[1]->IsString()) {
return scope.Close(Boolean::New(false));
}
switch(args.Length()) {
case 2:
return CompactRangeDefault(args);
case 3:
return args[2]->IsString() ? CompactColumnFamily(args) :
CompactOptions(args);
default:
return CompactAll(args);
}
}
Handle<Value> DBWrapper::Close(const Arguments& args) {
HandleScope scope;
delete ObjectWrap::Unwrap<DBWrapper>(args.This());
return scope.Close(Null());
}

@ -1,58 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
#ifndef DBWRAPPER_H
#define DBWRAPPER_H
#include <map>
#include <node.h>
#include "rocksdb/db.h"
#include "rocksdb/slice.h"
#include "rocksdb/options.h"
// Used to encapsulate a particular instance of an opened database.
//
// This object should not be used directly in C++; it exists solely to provide
// a mapping from a JavaScript object to a C++ code that can use the RocksDB
// API.
class DBWrapper : public node::ObjectWrap {
public:
static void Init(Handle<Object> exports);
private:
explicit DBWrapper();
~DBWrapper();
// Helper methods
static bool HasFamilyNamed(std::string& name, DBWrapper* db);
static bool AddToBatch(ROCKSDB_NAMESPACE::WriteBatch& batch, bool del,
Handle<Array> array);
static bool AddToBatch(ROCKSDB_NAMESPACE::WriteBatch& batch, bool del,
Handle<Array> array, DBWrapper* db_wrapper,
std::string cf);
static Handle<Value> CompactRangeDefault(const v8::Arguments& args);
static Handle<Value> CompactColumnFamily(const Arguments& args);
static Handle<Value> CompactOptions(const Arguments& args);
static Handle<Value> CompactAll(const Arguments& args);
// C++ mappings of API methods
static Persistent<v8::Function> constructor;
static Handle<Value> Open(const Arguments& args);
static Handle<Value> New(const Arguments& args);
static Handle<Value> Get(const Arguments& args);
static Handle<Value> Put(const Arguments& args);
static Handle<Value> Delete(const Arguments& args);
static Handle<Value> Dump(const Arguments& args);
static Handle<Value> WriteBatch(const Arguments& args);
static Handle<Value> CreateColumnFamily(const Arguments& args);
static Handle<Value> CompactRange(const Arguments& args);
static Handle<Value> Close(const Arguments& args);
// Internal fields
ROCKSDB_NAMESPACE::Options options_;
ROCKSDB_NAMESPACE::Status status_;
ROCKSDB_NAMESPACE::DB* db_;
std::unordered_map<std::string, ROCKSDB_NAMESPACE::ColumnFamilyHandle*>
columnFamilies_;
};
#endif

@ -1,3 +0,0 @@
#!/usr/bin/env bash
node -e "RDB = require('./build/Release/rdb').DBWrapper; console.log('Loaded rocksdb in variable RDB'); repl = require('repl').start('> ');"

@ -1,14 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
#ifndef BUILDING_NODE_EXTENSION
#define BUILDING_NODE_EXTENSION
#endif
#include <node.h>
#include <v8.h>
#include "db/_wrapper.h"
void InitAll(Handle<Object> exports) {
DBWrapper::Init(exports);
}
NODE_MODULE(rdb, InitAll)

@ -1,125 +0,0 @@
// Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
assert = require('assert')
RDB = require('./build/Release/rdb').DBWrapper
exec = require('child_process').exec
util = require('util')
DB_NAME = '/tmp/rocksdbtest-' + process.getuid()
a = RDB()
assert.equal(a.open(DB_NAME, ['b']), false)
exec(
util.format(
"node -e \"RDB = require('./build/Release/rdb').DBWrapper; \
a = RDB('%s'); a.createColumnFamily('b')\"",
DB_NAME
).exitCode, null
)
exec(
util.format(
"node -e \"RDB = require('./build/Release/rdb').DBWrapper; \
a = RDB('%s', ['b'])\"",
DB_NAME
).exitCode, null
)
exec('rm -rf ' + DB_NAME)
a = RDB()
assert.equal(a.open(DB_NAME, ['a']), false)
assert(a.open(DB_NAME), true)
assert(a.createColumnFamily('temp'))
b = RDB()
assert.equal(b.open(DB_NAME), false)
exec('rm -rf ' + DB_NAME)
DB_NAME += 'b'
a = RDB()
assert(a.open(DB_NAME))
assert.equal(a.constructor.name, 'DBWrapper')
assert.equal(a.createColumnFamily(), false)
assert.equal(a.createColumnFamily(1), false)
assert.equal(a.createColumnFamily(['']), false)
assert(a.createColumnFamily('b'))
assert.equal(a.createColumnFamily('b'), false)
// Get and Put
assert.equal(a.get(1), null)
assert.equal(a.get(['a']), null)
assert.equal(a.get('a', 1), null)
assert.equal(a.get(1, 'a'), null)
assert.equal(a.get(1, 1), null)
assert.equal(a.put(1), false)
assert.equal(a.put(['a']), false)
assert.equal(a.put('a', 1), false)
assert.equal(a.put(1, 'a'), false)
assert.equal(a.put(1, 1), false)
assert.equal(a.put('a', 'a', 1), false)
assert.equal(a.put('a', 1, 'a'), false)
assert.equal(a.put(1, 'a', 'a'), false)
assert.equal(a.put('a', 1, 1), false)
assert.equal(a.put(1, 'a', 1), false)
assert.equal(a.put(1, 1, 'a'), false)
assert.equal(a.put(1, 1, 1), false)
assert.equal(a.get(), null)
assert.equal(a.get('a'), null)
assert.equal(a.get('a', 'c'), null)
assert.equal(a.put(), false)
assert.equal(a.put('a'), false)
assert.equal(a.get('a', 'b', 'c'), null)
assert(a.put('a', 'axe'))
assert(a.put('a', 'first'))
assert.equal(a.get('a'), 'first')
assert.equal(a.get('a', 'b'), null)
assert.equal(a.get('a', 'c'), null)
assert(a.put('a', 'apple', 'b'))
assert.equal(a.get('a', 'b'), 'apple')
assert.equal(a.get('a'), 'first')
assert(a.put('b', 'butter', 'b'), 'butter')
assert(a.put('b', 'banana', 'b'))
assert.equal(a.get('b', 'b'), 'banana')
assert.equal(a.get('b'), null)
assert.equal(a.get('b', 'c'), null)
// Delete
assert.equal(a.delete(1), false)
assert.equal(a.delete('a', 1), false)
assert.equal(a.delete(1, 'a'), false)
assert.equal(a.delete(1, 1), false)
assert.equal(a.delete('b'), true)
assert(a.delete('a'))
assert.equal(a.get('a'), null)
assert.equal(a.get('a', 'b'), 'apple')
assert.equal(a.delete('c', 'c'), false)
assert.equal(a.delete('c', 'b'), true)
assert(a.delete('b', 'b'))
assert.equal(a.get('b', 'b'), null)
// Dump
console.log("MARKER 1")
assert(a.dump())
console.log("Should be no output between 'MARKER 1' and here\n")
console.log('Next line should be "a" => "apple"')
assert(a.dump('b'))
console.log("\nMARKER 2")
assert.equal(a.dump('c'), false)
console.log("Should be no output between 'MARKER 2' and here\n")
// WriteBatch
// Clean up test database
exec('rm -rf ' + DB_NAME)
Loading…
Cancel
Save