blob: bf03f3e104c2eb203be04195da5c9c6682a358b4 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*
*/
#pragma once
#include <string>
#include <vector>
#include "lua.hpp"
#include "server/redis_connection.h"
#include "status.h"
#include "storage/storage.h"
namespace engine {
struct Context;
}
inline constexpr const char REDIS_LUA_FUNC_SHA_PREFIX[] = "f_";
inline constexpr const char REDIS_LUA_FUNC_SHA_FLAGS[] = "f_{}_flags_";
inline constexpr const char REDIS_LUA_REGISTER_FUNC_PREFIX[] = "__redis_registered_";
inline constexpr const char REDIS_LUA_REGISTER_FUNC_FLAGS_PREFIX[] = "__redis_registered_flags_";
inline constexpr const char REDIS_FUNCTION_LIBNAME[] = "REDIS_FUNCTION_LIBNAME";
inline constexpr const char REDIS_FUNCTION_NEEDSTORE[] = "REDIS_FUNCTION_NEEDSTORE";
inline constexpr const char REDIS_FUNCTION_LIBRARIES[] = "REDIS_FUNCTION_LIBRARIES";
inline constexpr const char REGISTRY_SCRIPT_RUN_CTX_NAME[] = "__SCRIPT_RUN_CTX";
inline constexpr const char REGISTRY_KEYS_NAME[] = "__CURRENT_KEYS";
namespace lua {
lua_State *CreateState();
void DestroyState(lua_State *lua);
void LoadFuncs(lua_State *lua);
void LoadLibraries(lua_State *lua);
void RemoveUnsupportedFunctions(lua_State *lua);
void EnableGlobalsProtection(lua_State *lua);
int RedisCallCommand(lua_State *lua);
int RedisPCallCommand(lua_State *lua);
int RedisGenericCommand(lua_State *lua, int raise_error);
int RedisSha1hexCommand(lua_State *lua);
int RedisStatusReplyCommand(lua_State *lua);
int RedisErrorReplyCommand(lua_State *lua);
int RedisLogCommand(lua_State *lua);
int RedisRegisterFunction(lua_State *lua);
int RedisSetResp(lua_State *lua);
Status CreateFunction(Server *srv, const std::string &body, std::string *sha, lua_State *lua, bool need_to_store);
Status EvalGenericCommand(redis::Connection *conn, engine::Context *ctx, const std::string &body_or_sha,
const std::vector<std::string> &keys, const std::vector<std::string> &argv, bool evalsha,
std::string *output, bool read_only = false);
bool ScriptExists(lua_State *lua, const std::string &sha);
Status FunctionLoad(redis::Connection *conn, engine::Context *ctx, const std::string &script, bool need_to_store,
bool replace, std::string *lib_name);
Status FunctionCall(redis::Connection *conn, engine::Context *ctx, const std::string &name,
const std::vector<std::string> &keys, const std::vector<std::string> &argv, std::string *output,
bool read_only = false);
Status FunctionList(Server *srv, const redis::Connection *conn, engine::Context &ctx, const std::string &libname,
bool with_code, std::string *output);
Status FunctionListFunc(Server *srv, const redis::Connection *conn, engine::Context &ctx, const std::string &funcname,
std::string *output);
Status FunctionListLib(redis::Connection *conn, const std::string &libname, std::string *output);
Status FunctionDelete(engine::Context &ctx, redis::Connection *conn, const std::string &name);
bool FunctionIsLibExist(redis::Connection *conn, engine::Context *ctx, const std::string &libname,
bool need_check_storage = true);
Status FunctionFlush(redis::Connection *conn, engine::Context *ctx);
const char *RedisProtocolToLuaType(lua_State *lua, const char *reply);
const char *RedisProtocolToLuaTypeInt(lua_State *lua, const char *reply);
const char *RedisProtocolToLuaTypeBulk(lua_State *lua, const char *reply);
const char *RedisProtocolToLuaTypeStatus(lua_State *lua, const char *reply);
const char *RedisProtocolToLuaTypeError(lua_State *lua, const char *reply);
const char *RedisProtocolToLuaTypeAggregate(lua_State *lua, const char *reply, int atype);
const char *RedisProtocolToLuaTypeNull(lua_State *lua, const char *reply);
const char *RedisProtocolToLuaTypeBool(lua_State *lua, const char *reply, int tf);
const char *RedisProtocolToLuaTypeDouble(lua_State *lua, const char *reply);
const char *RedisProtocolToLuaTypeBigNumber(lua_State *lua, const char *reply);
const char *RedisProtocolToLuaTypeVerbatimString(lua_State *lua, const char *reply);
std::string ReplyToRedisReply(redis::Connection *conn, lua_State *lua);
void PushError(lua_State *lua, const char *err);
[[noreturn]] int RaiseError(lua_State *lua);
void SortArray(lua_State *lua);
void SetGlobalArray(lua_State *lua, const std::string &var, const std::vector<std::string> &elems);
void PushArray(lua_State *lua, const std::vector<std::string> &elems);
void SHA1Hex(char *digest, const char *script, size_t len);
int RedisMathRandom(lua_State *l);
int RedisMathRandomSeed(lua_State *l);
/// ScriptFlagType turn on/off constraints or indicate properties in Eval scripts and functions
///
/// Note: The default for Eval scripts are different than the default for functions(default is 0).
/// As soon as Redis sees the #! comment, it'll treat the script as if it declares flags, even if no flags are defined,
/// it still has a different set of defaults compared to a script without a #! line.
/// Another difference is that scripts without #! can run commands that access keys belonging to different cluster hash
/// slots, but ones with #! inherit the default flags, so they cannot.
enum ScriptFlagType : uint64_t {
kScriptNoWrites = 1ULL << 0, // "no-writes" flag
kScriptAllowOom = 1ULL << 1, // "allow-oom" flag
kScriptAllowStale = 1ULL << 2, // "allow-stale" flag
kScriptNoCluster = 1ULL << 3, // "no-cluster" flag
kScriptAllowCrossSlotKeys = 1ULL << 4, // "allow-cross-slot-keys" flag
};
/// ScriptFlags is composed of one or more ScriptFlagTypes combined by an OR operation
/// For example, ScriptFlags flags = kScriptNoWrites | kScriptNoCluster
using ScriptFlags = uint64_t;
[[nodiscard]] StatusOr<std::string> ExtractLibNameFromShebang(std::string_view shebang);
[[nodiscard]] StatusOr<ScriptFlags> ExtractFlagsFromShebang(std::string_view shebang);
/// GetFlagsFromStrings gets flags from flags_content and composites them together.
/// Each element in flags_content should correspond to a string form of ScriptFlagType
[[nodiscard]] StatusOr<ScriptFlags> GetFlagsFromStrings(const std::vector<std::string> &flags_content);
/// ExtractFlagsFromRegisterFunction extracts the flags from the redis.register_function
///
/// Note: When using it, you should make sure that
/// the top of the stack of lua is the flags parameter of redis.register_function.
/// The flags parameter in Lua is a table that stores strings.
/// After use, the original flags table on the top of the stack will be popped.
[[nodiscard]] StatusOr<ScriptFlags> ExtractFlagsFromRegisterFunction(lua_State *lua);
/// ScriptRunCtx is used to record context information during the running of Eval scripts and functions.
struct ScriptRunCtx {
// ScriptFlags
uint64_t flags = 0;
// current_slot tracks the slot currently accessed by the script
// and is used to detect whether there is cross-slot access
// between multiple commands in a script or function.
int current_slot = -1;
// the current connection
redis::Connection *conn = nullptr;
// the storage context
engine::Context *ctx = nullptr;
};
/// SaveOnRegistry saves user-defined data to lua REGISTRY
///
/// Note: Since lua_pushlightuserdata, you need to manage the life cycle of the data stored in the Registry yourself.
template <typename T>
void SaveOnRegistry(lua_State *lua, const char *name, T *ptr) {
lua_pushstring(lua, name);
if (ptr) {
lua_pushlightuserdata(lua, (void *)ptr);
} else {
lua_pushnil(lua);
}
lua_settable(lua, LUA_REGISTRYINDEX);
}
template <typename T>
T *GetFromRegistry(lua_State *lua, const char *name) {
lua_pushstring(lua, name);
lua_gettable(lua, LUA_REGISTRYINDEX);
if (lua_isnil(lua, -1)) {
// pops the value
lua_pop(lua, 1);
return nullptr;
}
// must be light user data
CHECK(lua_islightuserdata(lua, -1));
auto *ptr = static_cast<T *>(lua_touserdata(lua, -1));
CHECK(ptr != nullptr);
// pops the value
lua_pop(lua, 1);
return ptr;
}
void RemoveFromRegistry(lua_State *lua, const char *name);
} // namespace lua