Browse Source

v0.0.1

master
Erik Zscheile 2 years ago
commit
62ead0ce13
7 changed files with 415 additions and 0 deletions
  1. +18
    -0
      CMakeLists.txt
  2. +129
    -0
      args.cxx
  3. +30
    -0
      args.hpp
  4. +14
    -0
      error.cxx
  5. +10
    -0
      error.hpp
  6. +139
    -0
      main.cxx
  7. +75
    -0
      maint.cxx

+ 18
- 0
CMakeLists.txt View File

@@ -0,0 +1,18 @@
cmake_minimum_required(VERSION 3.0)
project(zstreedb2 CXX)
set(INSTALL_BIN_DIR bin CACHE PATH "Installation directory for binaries")
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

find_package(Threads REQUIRED)
find_package(RocksDB REQUIRED)

include_directories(${RocksDB_INCLUDE_DIRS})
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-rtti -fno-plt")

add_executable(zstreedb2 args.cxx error.cxx main.cxx)
target_link_libraries(zstreedb2 RocksDB::rocksdb-shared)
add_executable(zstreedb2maint maint.cxx)
target_link_libraries(zstreedb2maint Threads::Threads RocksDB::rocksdb-shared)

install(TARGETS zstreedb2 zstreedb2maint DESTINATION "${INSTALL_BIN_DIR}")

+ 129
- 0
args.cxx View File

@@ -0,0 +1,129 @@
/** zstreedb2 args.cxx
(C) 2018 Erik Zscheile
License: GPLv2
**/

#include <stdio.h>
#include "args.hpp"

namespace ztdb {

bool args_t::parse(const int argc, char *argv[]) {
// vars
bool ret = true;
int i = 1;
compress = rocksdb::kNoCompression;
cmd = UNKNOWN;

// options
for(; i < argc; ++i) {
const char *cca = argv[i];
if(cca[0] != '-') break;
if(!cca[1]) {
++i;
break;
}

if(!cca[2]) {
// parse short opts
switch(cca[1]) {
case 'j':
compress = rocksdb::kBZip2Compression;
break;

case 'J':
compress = rocksdb::kLZ4Compression;
break;

case 'z':
compress = rocksdb::kZlibCompression;
break;

case 'Z':
compress = rocksdb::kZSTD;
break;

default:
fprintf(stderr, "zstreedb2: unknown option '%s'\n", cca);
return false;
}
} else {
// parse long opts
const std::string arg = cca + 1;
if(arg == "-snappy") {
compress = rocksdb::kSnappyCompression;
} else {
fprintf(stderr, "zstreedb2: unknown option '%s'\n", cca);
return false;
}
}
}

if((argc - i) < 2) return false;

if(argv[i][0] == 0 || argv[i][1] != 0) {
fprintf(stderr, "zstreedb2: invalid command '%s'\n", argv[i]);
return false;
}

switch(argv[i++][0]) {
case 'g': cmd = GET; break;
case 's': cmd = SET; break;
case 'l': cmd = LS ; break;
case 'r': cmd = RM ; break;
case 'v': cmd = MV ; break;
case 'm': cmd = MATCH; break;
default:
fprintf(stderr, "zstreedb2: invalid command '%s'\n", argv[i]);
return false;
}
sel_node = argv[i++];

cmd_args.reserve(argc - i);
for(; i < argc; ++i) cmd_args.emplace_back(argv[i]);
return true;
}

bool args_t::flag_use_ro() const noexcept {
switch(cmd) {
case GET:
case LS:
case MATCH:
return true;
default:
return false;
}
}

void usage() {
puts("USAGE: zstreedb2 [-j|-J|-z|-Z] CMD NODE ARGS...");
}

void help() {
usage();

puts("\ncompression options:\n"
" -j use bzip2\n"
" -J use LZ4\n"
" -z use zlib\n"
" -Z use ZSTD\n"
" --snappy use snappy");

puts("\nkey/value commands:\n"
" g retrieve a NODE value\n"
" s assign a NODE value");

puts("\nrecursive node commands:\n"
" l list\n"
" r recursive remove\n"
" v move [ARGS = FROM TO]");

puts("\n m match [NODE as 'basename'; ARG+ = VALUE]");

puts("\nreturn codes:\n"
" 0 successful\n"
" 1 invocation error\n"
" 2 database error\n");
}

}

+ 30
- 0
args.hpp View File

@@ -0,0 +1,30 @@
/** zstreedb2 args.hpp
(C) 2018 Erik Zscheile
License: GPLv2
**/

#pragma once
#include <string>
#include <vector>
#include <rocksdb/options.h>

namespace ztdb {

struct args_t final {
rocksdb::CompressionType compress;
enum { UNKNOWN, GET, SET, LS, MV, RM, MATCH } cmd;
//std::string sel_ns;
std::string sel_node;
std::vector<std::string> cmd_args;

bool parse(int argc, char *argv[]);

// flag_use_ro: detects if the command can work on
// a read-only database
bool flag_use_ro() const noexcept;
};

void usage();
void help();

}

+ 14
- 0
error.cxx View File

@@ -0,0 +1,14 @@
/** zstreedb2 error.cxx
(C) 2018 Erik Zscheile
License: MIT
**/

#include "error.hpp"

extern const char *progname;

[[noreturn]]
void errmsg(const int ec, const std::string &em) {
fprintf(stderr, "%s: ERROR: %s\n", progname, em.c_str());
exit(ec);
}

+ 10
- 0
error.hpp View File

@@ -0,0 +1,10 @@
/** zstreedb2 error.hpp
(C) 2018 Erik Zscheile
License: MIT
**/

#pragma once
#include <string>

[[noreturn]]
void errmsg(int ec, const std::string &em);

+ 139
- 0
main.cxx View File

@@ -0,0 +1,139 @@
/** zstreedb2 main.cxx
(C) 2018 Erik Zscheile
License: GPLv2
**/

#include <stdio.h>
#include <unordered_map>
#include <rocksdb/db.h>

#include "args.hpp"
#include "error.hpp"

using namespace std;

const char *progname;
static rocksdb::DB* db;

static void cleanup() {
delete db;
}

int main(const int argc, char *argv[]) {
progname = argv[0];

ztdb::args_t args;
if(!args.parse(argc, argv)) {
ztdb::help();
errmsg(1, "invalid arguments passed");
}

rocksdb::Status st;

{
rocksdb::Options opts;
if(!args.flag_use_ro()) opts.IncreaseParallelism();
opts.compression = args.compress;
opts.create_if_missing = true;
st = args.flag_use_ro()
? rocksdb::DB::OpenForReadOnly(opts, ".", &db)
: rocksdb::DB::Open (opts, ".", &db);
if(!st.ok())
errmsg(2, "db open failed: " + st.ToString());
}

string value;

switch(args.cmd) {
#define AC(C) case ztdb::args_t::C:
AC(GET)
st = db->Get({}, args.sel_node, &value);
if(!value.empty()) puts(value.c_str());
break;

AC(SET)
if(args.cmd_args.empty()) {
db->Delete({}, args.sel_node);
} else {
st = db->Put({}, args.sel_node, args.cmd_args.front());
if(!st.ok()) errmsg(2, "db put failed: " + st.ToString());
}
break;

AC(LS)
{
const bool sall = (args.sel_node == "*");
const string xpar = args.sel_node + ".";
const auto it = db->NewIterator({});
if(sall) it->SeekToFirst();
else it->Seek(xpar);
for(; it->Valid(); it->Next()) {
rocksdb::Slice tmp = it->key();
if(!sall) {
if(!tmp.starts_with(xpar)) break;
tmp.remove_prefix(xpar.size());
}
printf("%.*s\n", static_cast<int>(tmp.size()), tmp.data());
}
delete it;
}
break;

AC(MV)
if(!args.cmd_args.empty() && args.sel_node != args.cmd_args.front()) {
const auto it = db->NewIterator({});
const string xpar = args.sel_node + ".";
rocksdb::WriteBatch batch;

for(it->Seek(xpar); it->Valid() && it->key().starts_with(xpar); it->Next()) {
batch.Delete(it->key());
string tmp = it->key().ToString();
tmp.replace(0, args.sel_node.size(), args.cmd_args.front());
batch.Put(tmp, it->value());
}

st = db->Write({}, &batch);
delete it;
if(!st.ok()) errmsg(2, "db write failed: " + st.ToString());
} else {
errmsg(1, "invalid invocation to 'zstreedb2 v FROM TO'");
}
break;

AC(RM)
{
const auto it = db->NewIterator({});
const string xpar = args.sel_node + ".";
rocksdb::WriteBatch batch;

// we could use DeleteRange, but it would be more complex,
// because we would need to handle invalid it after loop (EOF reached)
for(it->Seek(xpar); it->Valid() && it->key().starts_with(xpar); it->Next())
batch.Delete(it->key());

batch.Delete(args.sel_node);
st = db->Write({}, &batch);
delete it;
if(!st.ok()) errmsg(2, "db write failed: " + st.ToString());
}
break;

AC(MATCH)
{
if(args.cmd_args.empty())
errmsg(1, "invalid invocation to 'zstreedb2 m KEY VALUE'");

const string key = "." + args.sel_node;
const auto it = db->NewIterator({});
for(it->SeekToFirst(); it->Valid(); it->Next())
if(it->key().ends_with(key) && it->value() == args.cmd_args.front()) {
auto slc = it->key();
slc.remove_suffix(key.size());
printf("%.*s\n", static_cast<int>(slc.size()), slc.data());
}
}
break;
}

return 0;
}

+ 75
- 0
maint.cxx View File

@@ -0,0 +1,75 @@
/** zstreedb2 maint.cxx
(C) 2018 Erik Zscheile
License: GPLv2
**/

#include <stdio.h>
#include <rocksdb/db.h>
#include <memory>
#include <thread>

using namespace std;

static void pdberrmsg(const rocksdb::Status &st, const char *fn) {
auto str = st.ToString();
fprintf(stderr, "zstreedb2maint: ERROR: db %s failed: %s", fn, str.c_str());
}

int main(const int argc, char *argv[]) {
rocksdb::Status st;
rocksdb::Options opts;
opts.IncreaseParallelism(std::max(thread::hardware_concurrency(), 1u));
opts.allow_mmap_reads = true;
opts.allow_mmap_writes = true;

if(argc > 1)
switch(argv[1][0]) {
case 'j':
opts.compression = rocksdb::kBZip2Compression;
break;

case 'J':
opts.compression = rocksdb::kLZ4Compression;
break;

case 's':
opts.compression = rocksdb::kSnappyCompression;
break;

case 'z':
opts.compression = rocksdb::kZlibCompression;
break;

case 'Z':
opts.compression = rocksdb::kZSTD;
break;

case 'h':
puts("USAGE: zstreedb2maint [COMPR]");
puts(" COMPR := one of: j J s z Z");
return 0;
}

std::unique_ptr<rocksdb::DB> db;
{
rocksdb::DB *dbptr;
st = rocksdb::DB::Open(opts, ".", &dbptr);
if(!st.ok()) {
pdberrmsg(st, "open");
return 1;
}
db.reset(dbptr);
}

rocksdb::CompactRangeOptions copts;
copts.change_level = true;
copts.bottommost_level_compaction
= rocksdb::BottommostLevelCompaction::kForce;

st = db->CompactRange(copts, nullptr, nullptr);
if(!st.ok()) {
pdberrmsg(st, "compact");
return 1;
}
return 0;
}

Loading…
Cancel
Save