// found in the LICENSE file.
#include "util/ldb_cmd.h"
-#include <dirent.h>
-
-#include <sstream>
-#include <string>
-#include <stdexcept>
-#include "leveldb/write_batch.h"
#include "db/dbformat.h"
#include "db/log_reader.h"
#include "db/filename.h"
#include "db/write_batch_internal.h"
+#include "leveldb/write_batch.h"
+#include "util/coding.h"
+
+#include <ctime>
+#include <dirent.h>
+#include <sstream>
+#include <string>
+#include <stdexcept>
namespace leveldb {
const string LDBCommand::ARG_KEY_HEX = "key_hex";
const string LDBCommand::ARG_VALUE_HEX = "value_hex";
const string LDBCommand::ARG_TTL = "ttl";
+const string LDBCommand::ARG_TTL_START = "start_time";
+const string LDBCommand::ARG_TTL_END = "end_time";
+const string LDBCommand::ARG_TIMESTAMP = "timestamp";
const string LDBCommand::ARG_FROM = "from";
const string LDBCommand::ARG_TO = "to";
const string LDBCommand::ARG_MAX_KEYS = "max_keys";
// ----------------------------------------------------------------------------
+string ReadableTime(int unixtime) {
+ char time_buffer [80];
+ time_t rawtime = unixtime;
+ struct tm * timeinfo = localtime(&rawtime);
+ strftime(time_buffer, 80, "%c", timeinfo);
+ return string(time_buffer);
+}
+
+// This function only called when it's the sane case of >1 buckets in time-range
+// Also called only when timekv falls between ttl_start and ttl_end provided
+void IncBucketCounts(uint64_t* bucket_counts, int ttl_start, int time_range,
+ int bucket_size, int timekv, int num_buckets) {
+ if (time_range <= 0 || timekv < ttl_start || timekv > (ttl_start + time_range)
+ || bucket_size <= 0 || num_buckets < 2) {
+ fprintf(stderr, "Error: bucketizing\n");
+ return;
+ }
+ int bucket = (timekv - ttl_start);
+ bucket = (bucket == 0) ? 1 : ceil(bucket / (double)bucket_size);
+ bucket_counts[bucket - 1]++;
+}
+
+void PrintBucketCounts(uint64_t* bucket_counts, int ttl_start, int ttl_end,
+ int bucket_size, int num_buckets) {
+ int time_point = ttl_start;
+ for(int i = 0; i < num_buckets - 1; i++, time_point += bucket_size) {
+ fprintf(stdout, "Keys in range %s to %s : %lu\n",
+ ReadableTime(time_point).c_str(),
+ ReadableTime(time_point + bucket_size).c_str(), bucket_counts[i]);
+ }
+ fprintf(stdout, "Keys in range %s to %s : %lu\n",
+ ReadableTime(time_point).c_str(),
+ ReadableTime(ttl_end).c_str(), bucket_counts[num_buckets - 1]);
+}
+
const string DBDumperCommand::ARG_COUNT_ONLY = "count_only";
const string DBDumperCommand::ARG_STATS = "stats";
+const string DBDumperCommand::ARG_TTL_BUCKET = "bucket";
DBDumperCommand::DBDumperCommand(const vector<string>& params,
const map<string, string>& options, const vector<string>& flags) :
LDBCommand(options, flags, true,
BuildCmdLineOptions({ARG_TTL, ARG_HEX, ARG_KEY_HEX,
ARG_VALUE_HEX, ARG_FROM, ARG_TO,
- ARG_MAX_KEYS, ARG_COUNT_ONLY, ARG_STATS})),
+ ARG_MAX_KEYS, ARG_COUNT_ONLY, ARG_STATS,
+ ARG_TTL_START, ARG_TTL_END,
+ ARG_TTL_BUCKET, ARG_TIMESTAMP})),
null_from_(true),
null_to_(true),
max_keys_(-1),
ret.append(" ");
ret.append(DBDumperCommand::Name());
ret.append(HelpRangeCmdArgs());
+ ret.append(" [--" + ARG_TTL + "]");
ret.append(" [--" + ARG_MAX_KEYS + "=<N>]");
+ ret.append(" [--" + ARG_TIMESTAMP + "]");
ret.append(" [--" + ARG_COUNT_ONLY + "]");
ret.append(" [--" + ARG_STATS + "]");
+ ret.append(" [--" + ARG_TTL_BUCKET + "=<N>]");
+ ret.append(" [--" + ARG_TTL_START + "=<N>]");
+ ret.append(" [--" + ARG_TTL_END + "=<N>]");
ret.append("\n");
}
}
int max_keys = max_keys_;
+ int ttl_start;
+ if (!ParseIntOption(option_map_, ARG_TTL_START, ttl_start, exec_state_)) {
+ ttl_start = DBWithTTL::kMinTimestamp; // TTL introduction time
+ }
+ int ttl_end;
+ if (!ParseIntOption(option_map_, ARG_TTL_END, ttl_end, exec_state_)) {
+ ttl_end = DBWithTTL::kMaxTimestamp; // Max time allowed by TTL feature
+ }
+ if (ttl_end < ttl_start) {
+ fprintf(stderr, "Error: End time can't be less than start time\n");
+ delete iter;
+ return;
+ }
+ int time_range = ttl_end - ttl_start;
+ int bucket_size;
+ if (!ParseIntOption(option_map_, ARG_TTL_BUCKET, bucket_size, exec_state_) ||
+ bucket_size <= 0) {
+ bucket_size = time_range; // Will have just 1 bucket by default
+ }
+ // At this point, bucket_size=0 => time_range=0
+ uint64_t num_buckets =
+ bucket_size >= time_range ? 1 : ceil((double)time_range/bucket_size);
+ unique_ptr<uint64_t[]> bucket_counts(new uint64_t[num_buckets]);
+ fill(bucket_counts.get(), bucket_counts.get() + num_buckets, 0);
+ if (is_db_ttl_ && !count_only_ && timestamp_) {
+ fprintf(stdout, "Dumping key-values from %s to %s\n",
+ ReadableTime(ttl_start).c_str(), ReadableTime(ttl_end).c_str());
+ }
+
for (; iter->Valid(); iter->Next()) {
+ int rawtime = 0;
+ string value;
// If end marker was specified, we stop before it
if (!null_to_ && (iter->key().ToString() >= to_))
break;
// Terminate if maximum number of keys have been dumped
if (max_keys == 0)
break;
+ if (is_db_ttl_) {
+ TtlIterator* it_ttl = (TtlIterator*)iter;
+ struct ValueAndTimestamp val_ts = it_ttl->ValueWithTimestamp();
+ value = val_ts.value.ToString();
+ rawtime = val_ts.timestamp;
+ if (rawtime < ttl_start || rawtime > ttl_end) {
+ continue;
+ }
+ } else {
+ value = iter->value().ToString();
+ }
if (max_keys > 0) {
--max_keys;
}
+ if (is_db_ttl_ && num_buckets > 1) {
+ IncBucketCounts(bucket_counts.get(), ttl_start, time_range, bucket_size,
+ rawtime, num_buckets);
+ }
++count;
if (!count_only_) {
+ if (is_db_ttl_ && timestamp_) {
+ fprintf(stdout, "%s ", ReadableTime(rawtime).c_str());
+ }
string str = PrintKeyValue(iter->key().ToString(),
- iter->value().ToString(),
- is_key_hex_, is_value_hex_);
+ value, is_key_hex_, is_value_hex_);
fprintf(stdout, "%s\n", str.c_str());
}
}
- fprintf(stdout, "Keys in range: %lld\n", (long long) count);
+ if (num_buckets > 1 && is_db_ttl_) {
+ PrintBucketCounts(bucket_counts.get(), ttl_start, ttl_end, bucket_size,
+ num_buckets);
+ } else {
+ fprintf(stdout, "Keys in range: %lld\n", (long long) count);
+ }
// Clean up
delete iter;
}
ret.append(" ");
ret.append(GetCommand::Name());
ret.append(" <key>");
+ ret.append(" [--" + ARG_TTL + "]");
ret.append("\n");
}
ret.append(" ");
ret.append(BatchPutCommand::Name());
ret.append(" <key> <value> [<key> <value>] [..]");
+ ret.append(" [--" + ARG_TTL + "]");
ret.append("\n");
}
ScanCommand::ScanCommand(const vector<string>& params,
const map<string, string>& options, const vector<string>& flags) :
LDBCommand(options, flags, true,
- BuildCmdLineOptions({ARG_TTL, ARG_HEX, ARG_KEY_HEX,
- ARG_VALUE_HEX, ARG_FROM, ARG_TO,
- ARG_MAX_KEYS})),
+ BuildCmdLineOptions({ARG_TTL, ARG_HEX, ARG_KEY_HEX, ARG_TO,
+ ARG_VALUE_HEX, ARG_FROM, ARG_TIMESTAMP,
+ ARG_MAX_KEYS, ARG_TTL_START, ARG_TTL_END})),
start_key_specified_(false),
end_key_specified_(false),
max_keys_scanned_(-1) {
ret.append(" ");
ret.append(ScanCommand::Name());
ret.append(HelpRangeCmdArgs());
- ret.append("--" + ARG_MAX_KEYS + "=N] ");
+ ret.append(" [--" + ARG_TTL + "]");
+ ret.append(" [--" + ARG_TIMESTAMP + "]");
+ ret.append(" [--" + ARG_MAX_KEYS + "=<N>q] ");
+ ret.append(" [--" + ARG_TTL_START + "=<N>]");
+ ret.append(" [--" + ARG_TTL_END + "=<N>]");
ret.append("\n");
}
} else {
it->SeekToFirst();
}
+ int ttl_start;
+ if (!ParseIntOption(option_map_, ARG_TTL_START, ttl_start, exec_state_)) {
+ ttl_start = DBWithTTL::kMinTimestamp; // TTL introduction time
+ }
+ int ttl_end;
+ if (!ParseIntOption(option_map_, ARG_TTL_END, ttl_end, exec_state_)) {
+ ttl_end = DBWithTTL::kMaxTimestamp; // Max time allowed by TTL feature
+ }
+ if (ttl_end < ttl_start) {
+ fprintf(stderr, "Error: End time can't be less than start time\n");
+ delete it;
+ return;
+ }
+ if (is_db_ttl_ && timestamp_) {
+ fprintf(stdout, "Scanning key-values from %s to %s\n",
+ ReadableTime(ttl_start).c_str(), ReadableTime(ttl_end).c_str());
+ }
for ( ;
it->Valid() && (!end_key_specified_ || it->key().ToString() < end_key_);
it->Next()) {
string key = it->key().ToString();
- string value = it->value().ToString();
+ string value;
+ if (is_db_ttl_) {
+ TtlIterator* it_ttl = (TtlIterator*)it;
+ struct ValueAndTimestamp val_ts = it_ttl->ValueWithTimestamp();
+ int rawtime = val_ts.timestamp;
+ value = val_ts.value.ToString();
+ if (rawtime < ttl_start || rawtime > ttl_end) {
+ continue;
+ }
+ if (timestamp_) {
+ fprintf(stdout, "%s ", ReadableTime(rawtime).c_str());
+ }
+ } else {
+ value = it->value().ToString();
+ }
fprintf(stdout, "%s : %s\n",
(is_key_hex_ ? StringToHex(key) : key).c_str(),
(is_value_hex_ ? StringToHex(value) : value).c_str()
ret.append(" ");
ret.append(PutCommand::Name());
ret.append(" <key> <value> ");
+ ret.append(" [--" + ARG_TTL + "]");
ret.append("\n");
}
void DBQuerierCommand::Help(string& ret) {
ret.append(" ");
ret.append(DBQuerierCommand::Name());
+ ret.append(" [--" + ARG_TTL + "]");
ret.append("\n");
ret.append(" Starts a REPL shell. Type help for list of available "
"commands.");