robustness fixes plus improve support for auxillary commands so we could run tcl tests.
GETRANGE - fix out of bounds bug. Add a decorator for "config get" Add a decorator for "function flush"
This commit is contained in:
parent
579ba3149b
commit
d5cea3f5f3
|
@ -223,7 +223,6 @@ API 2.0
|
|||
- [ ] SCRIPT DEBUG/KILL/FLUSH/EXISTS
|
||||
- [X] Set Family
|
||||
- [X] SSCAN
|
||||
- [X] SMISMEMBER
|
||||
- [X] Sorted Set Family
|
||||
- [ ] ZCOUNT
|
||||
- [ ] ZINTERSTORE
|
||||
|
@ -258,6 +257,7 @@ exact use-cases for this API.
|
|||
- [X] ROLE (2.8) decorator for for master withour replicas
|
||||
- [X] UNLINK (4.0) decorator for DEL command
|
||||
- [X] BGSAVE
|
||||
- [X] FUNCTION FLUSH
|
||||
## Milestone Nymph
|
||||
API 2,3,4 without cluster support, without modules, without memory inspection commands.
|
||||
Without support for keyspace notifications.
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
# Differences with Redis
|
||||
|
||||
## String lengths, indices.
|
||||
|
||||
String sizes are limited to 256MB.
|
||||
Indices (say in GETRANGE and SETRANGE commands) should be signed 32 bit integers in range
|
||||
[-2147483647, 2147483648].
|
||||
|
||||
## Expiry ranges.
|
||||
Expirations are limited to 365 days. For commands with millisecond precision like PEXPIRE or PSETEX,
|
||||
expirations greater than 2^25ms are quietly rounded to the nearest second loosing precision of ~0.002%.
|
||||
|
|
@ -937,6 +937,20 @@ void Service::Unsubscribe(CmdArgList args, ConnectionContext* cntx) {
|
|||
cntx->ChangeSubscription(false, true, std::move(args));
|
||||
}
|
||||
|
||||
// Not a real implementation. Serves as a decorator to accept some function commands
|
||||
// for testing.
|
||||
void Service::Function(CmdArgList args, ConnectionContext* cntx) {
|
||||
ToUpper(&args[1]);
|
||||
string_view sub_cmd = ArgS(args, 1);
|
||||
|
||||
if (sub_cmd == "FLUSH") {
|
||||
return (*cntx)->SendOk();
|
||||
}
|
||||
|
||||
string err = absl::StrCat("Unknown subcommand '", sub_cmd, "'. Try FUNCTION HELP.");
|
||||
return (*cntx)->SendError(err, kSyntaxErr);
|
||||
}
|
||||
|
||||
VarzValue::Map Service::GetVarzStats() {
|
||||
VarzValue::Map res;
|
||||
|
||||
|
@ -963,13 +977,14 @@ void Service::RegisterCommands() {
|
|||
|
||||
registry_ << CI{"QUIT", CO::READONLY | CO::FAST, 1, 0, 0, 0}.HFUNC(Quit)
|
||||
<< CI{"MULTI", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0}.HFUNC(Multi)
|
||||
<< CI{"DISCARD", CO::NOSCRIPT | CO::FAST| CO::LOADING, 1, 0, 0, 0}.MFUNC(Discard)
|
||||
<< CI{"DISCARD", CO::NOSCRIPT | CO::FAST | CO::LOADING, 1, 0, 0, 0}.MFUNC(Discard)
|
||||
<< CI{"EVAL", CO::NOSCRIPT, -3, 0, 0, 0}.MFUNC(Eval).SetValidator(&EvalValidator)
|
||||
<< CI{"EVALSHA", CO::NOSCRIPT, -3, 0, 0, 0}.MFUNC(EvalSha).SetValidator(&EvalValidator)
|
||||
<< CI{"EXEC", kExecMask, 1, 0, 0, 0}.MFUNC(Exec)
|
||||
<< CI{"PUBLISH", CO::LOADING | CO::FAST, 3, 0, 0, 0}.MFUNC(Publish)
|
||||
<< CI{"SUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.MFUNC(Subscribe)
|
||||
<< CI{"UNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.MFUNC(Unsubscribe);
|
||||
<< CI{"UNSUBSCRIBE", CO::NOSCRIPT | CO::LOADING, -2, 0, 0, 0}.MFUNC(Unsubscribe)
|
||||
<< CI{"FUNCTION", CO::NOSCRIPT, 2, 0, 0, 0}.MFUNC(Function);
|
||||
|
||||
StringFamily::Register(®istry_);
|
||||
GenericFamily::Register(®istry_);
|
||||
|
|
|
@ -78,6 +78,7 @@ class Service : public facade::ServiceInterface {
|
|||
void Publish(CmdArgList args, ConnectionContext* cntx);
|
||||
void Subscribe(CmdArgList args, ConnectionContext* cntx);
|
||||
void Unsubscribe(CmdArgList args, ConnectionContext* cntx);
|
||||
void Function(CmdArgList args, ConnectionContext* cntx);
|
||||
|
||||
struct EvalArgs {
|
||||
std::string_view sha; // only one of them is defined.
|
||||
|
|
|
@ -245,6 +245,11 @@ void ServerFamily::Config(CmdArgList args, ConnectionContext* cntx) {
|
|||
|
||||
if (sub_cmd == "SET") {
|
||||
return (*cntx)->SendOk();
|
||||
} else if (sub_cmd == "GET" && args.size() == 3) {
|
||||
string_view param = ArgS(args, 2);
|
||||
string_view res[2] = {param, "tbd"};
|
||||
|
||||
return (*cntx)->SendStringArr(res);
|
||||
} else {
|
||||
string err = absl::StrCat("Unknown subcommand or wrong number of arguments for '", sub_cmd,
|
||||
"'. Try CONFIG HELP.");
|
||||
|
|
|
@ -64,6 +64,9 @@ TEST_F(SetFamilyTest, SDiff) {
|
|||
resp = Run({"sdiffstore", "a", "b", "c"});
|
||||
EXPECT_THAT(resp[0], IntArg(3));
|
||||
|
||||
Run({"set", "str", "foo"});
|
||||
EXPECT_THAT(Run({"sdiff", "b", "str"}), ElementsAre(ErrArg("WRONGTYPE ")));
|
||||
|
||||
Run({"sadd", "bar", "x", "a", "b", "c"});
|
||||
Run({"sadd", "foo", "c"});
|
||||
Run({"sadd", "car", "a", "d"});
|
||||
|
|
|
@ -514,26 +514,26 @@ void StringFamily::GetRange(CmdArgList args, ConnectionContext* cntx) {
|
|||
if (!it_res.ok())
|
||||
return it_res.status();
|
||||
|
||||
if (start < 0 && end < 0 && start > end) {
|
||||
return OpStatus::OK;
|
||||
}
|
||||
const CompactObj& co = it_res.value()->second;
|
||||
size_t strlen = co.Size();
|
||||
|
||||
if (start < 0)
|
||||
start = strlen + start;
|
||||
else if (size_t(start) >= strlen)
|
||||
return OpStatus::OK;
|
||||
|
||||
if (end < 0)
|
||||
end = strlen + end;
|
||||
|
||||
if (strlen == 0 || start > end || size_t(start) >= strlen) {
|
||||
return OpStatus::OK;
|
||||
}
|
||||
|
||||
if (start < 0)
|
||||
start = 0;
|
||||
if (end < 0)
|
||||
end = 0;
|
||||
|
||||
if (size_t(end) >= strlen)
|
||||
end = strlen - 1;
|
||||
|
||||
string tmp;
|
||||
string_view slice = co.GetSlice(&tmp);
|
||||
|
||||
|
|
|
@ -267,6 +267,8 @@ TEST_F(StringFamilyTest, SetEx) {
|
|||
|
||||
TEST_F(StringFamilyTest, Range) {
|
||||
Run({"set", "key1", "Hello World"});
|
||||
EXPECT_THAT(Run({"getrange", "key1", "5", "3"}), RespEq(""));
|
||||
|
||||
Run({"SETRANGE", "key1", "6", "Earth"});
|
||||
EXPECT_THAT(Run({"get", "key1"}), RespEq("Hello Earth"));
|
||||
|
||||
|
|
Loading…
Reference in New Issue