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:
Roman Gershman 2022-04-03 22:55:53 +03:00
parent 579ba3149b
commit d5cea3f5f3
8 changed files with 47 additions and 9 deletions

View File

@ -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.

12
doc/differences.md Normal file
View File

@ -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%.

View File

@ -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(&registry_);
GenericFamily::Register(&registry_);

View File

@ -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.

View File

@ -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.");

View File

@ -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"});

View File

@ -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);

View File

@ -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"));