in src/storage/scripting.cc [728:837]
int RedisGenericCommand(lua_State *lua, int raise_error) {
auto *script_run_ctx = GetFromRegistry<ScriptRunCtx>(lua, REGISTRY_SCRIPT_RUN_CTX_NAME);
CHECK(script_run_ctx != nullptr);
int argc = lua_gettop(lua);
if (argc == 0) {
PushError(lua, "Please specify at least one argument for redis.call()");
return raise_error ? RaiseError(lua) : 1;
}
std::vector<std::string> args;
for (int j = 1; j <= argc; j++) {
if (lua_type(lua, j) == LUA_TNUMBER) {
lua_Number num = lua_tonumber(lua, j);
args.emplace_back(fmt::format("{:.17g}", static_cast<double>(num)));
} else {
size_t obj_len = 0;
const char *obj_s = lua_tolstring(lua, j, &obj_len);
if (obj_s == nullptr) {
PushError(lua, "Lua redis.call() command arguments must be strings or integers");
return raise_error ? RaiseError(lua) : 1;
}
args.emplace_back(obj_s, obj_len);
}
}
auto cmd_s = Server::LookupAndCreateCommand(args[0]);
if (!cmd_s) {
PushError(lua, "Unknown Redis command called from Lua script");
return raise_error ? RaiseError(lua) : 1;
}
auto cmd = *std::move(cmd_s);
auto attributes = cmd->GetAttributes();
if (!attributes->CheckArity(argc)) {
PushError(lua, "Wrong number of args while calling Redis command from Lua script");
return raise_error ? RaiseError(lua) : 1;
}
auto cmd_flags = attributes->GenerateFlags(args);
if ((script_run_ctx->flags & ScriptFlagType::kScriptNoWrites) && !(cmd_flags & redis::kCmdReadOnly)) {
PushError(lua, "Write commands are not allowed from read-only scripts");
return raise_error ? RaiseError(lua) : 1;
}
if ((cmd_flags & redis::kCmdNoScript) || (cmd_flags & redis::kCmdExclusive)) {
PushError(lua, "This Redis command is not allowed from scripts");
return raise_error ? RaiseError(lua) : 1;
}
std::string cmd_name = attributes->name;
auto *conn = script_run_ctx->conn;
auto *srv = conn->GetServer();
Config *config = srv->GetConfig();
cmd->SetArgs(args);
auto s = cmd->Parse();
if (!s) {
PushError(lua, s.Msg().data());
return raise_error ? RaiseError(lua) : 1;
}
if (config->cluster_enabled) {
if (script_run_ctx->flags & ScriptFlagType::kScriptNoCluster) {
PushError(lua, "Can not run script on cluster, 'no-cluster' flag is set");
return raise_error ? RaiseError(lua) : 1;
}
auto s = srv->cluster->CanExecByMySelf(attributes, args, conn, script_run_ctx);
if (!s.IsOK()) {
if (s.Is<Status::RedisMoved>()) {
PushError(lua, "Script attempted to access a non local key in a cluster node script");
} else {
PushError(lua, redis::StatusToRedisErrorMsg(s).c_str());
}
return raise_error ? RaiseError(lua) : 1;
}
}
if ((cmd_flags & redis::kCmdAdmin) && !conn->IsAdmin()) {
PushError(lua, redis::errAdminPermissionRequired);
return raise_error ? RaiseError(lua) : 1;
}
if (config->slave_readonly && srv->IsSlave() && (cmd_flags & redis::kCmdWrite)) {
PushError(lua, "READONLY You can't write against a read only slave.");
return raise_error ? RaiseError(lua) : 1;
}
if (!config->slave_serve_stale_data && srv->IsSlave() && cmd_name != "info" && cmd_name != "slaveof" &&
srv->GetReplicationState() != kReplConnected) {
PushError(lua,
"MASTERDOWN Link with MASTER is down "
"and slave-serve-stale-data is set to 'no'.");
return raise_error ? RaiseError(lua) : 1;
}
std::string output;
s = conn->ExecuteCommand(*script_run_ctx->ctx, cmd_name, args, cmd.get(), &output);
if (!s) {
PushError(lua, s.Msg().data());
return raise_error ? RaiseError(lua) : 1;
}
srv->FeedMonitorConns(conn, args);
RedisProtocolToLuaType(lua, output.data());
return 1;
}