Skip to content

Commit

Permalink
Add Lua Redis set commands (SAdd, SIsMember, SMembers, SRem, SCard)
Browse files Browse the repository at this point in the history
Introduced Lua functions for Redis set operations including adding members, checking membership, retrieving members, removing members, and getting the set cardinality. Updated constants and Lua table registration to integrate these new commands for seamless usage within Lua scripts.

Signed-off-by: Christian Roessner <[email protected]>
  • Loading branch information
Christian Roessner committed Jul 25, 2024
1 parent effaf11 commit b4482e7
Show file tree
Hide file tree
Showing 2 changed files with 236 additions and 7 deletions.
25 changes: 20 additions & 5 deletions server/global/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -917,7 +917,6 @@ const (
LuaCommandAddMFAValue
)

// LuaFnCtxSet represents the function name for "context_set" in Lua
const (
// LuaFnCtxSet represents the function name for "context_set" in Lua
LuaFnCtxSet = "context_set"
Expand Down Expand Up @@ -1003,18 +1002,34 @@ const (
// LuaFnRedisHGetAll represents the function name for "redis_hgetall" in Lua
LuaFnRedisHGetAll = "redis_hgetall"

// LuaFNRedisHIncrBy represents the function name for "redis_hincrby" in Lua.
LuaFNRedisHIncrBy = "redis_hincrby"
// LuaFnRedisHIncrBy represents the function name for "redis_hincrby" in Lua.
LuaFnRedisHIncrBy = "redis_hincrby"

// LuaFNRedisHIncrByFloat represents the function name for "redis_hincrbyfloat" in Lua.
LuaFNRedisHIncrByFloat = "redis_hincrbyfloat"
// LuaFnRedisHIncrByFloat represents the function name for "redis_hincrbyfloat" in Lua.
LuaFnRedisHIncrByFloat = "redis_hincrbyfloat"

// LuaFnRedisHExists represents the Lua function name for checking if a field exists in a Redis hash.
LuaFnRedisHExists = "redis_hexists"

// LuaFnRedisRename represebts the Lua function name "redis_rename" to rename an existing Redis key.
LuaFnRedisRename = "redis_rename"

// LuaFnRedisSAdd represents the Lua function name for adding one or more members to a set in Redis.
LuaFnRedisSAdd = "redis_sadd"

// LuaFnRedisSIsMember represents the name of the Redis function "SISMEMBER" used to check if a member exists in a set.
LuaFnRedisSIsMember = "redis_sismember"

// LuaFnRedisSMembers represents the Redis command "SMEMBERS" which returns all the members
// of a set stored at the specified key.
LuaFnRedisSMembers = "redis_smembers"

// LuaFnRedisSRem represents a Lua function that removes one or more members from a set in Redis.
LuaFnRedisSRem = "redis_srem"

// LuaFnRedisSCard represents a Lua function that returns the number of elements in a Redis set.
LuaFnRedisSCard = "redis_scard"

// LuaFnApplyBackendResult applies changes to the backend result from a former authentication process.
LuaFnApplyBackendResult = "apply_backend_result"

Expand Down
218 changes: 216 additions & 2 deletions server/lualib/rediscli.go
Original file line number Diff line number Diff line change
Expand Up @@ -539,6 +539,215 @@ func RedisRename(L *lua.LState) int {
return 1
}

// RedisSAdd adds one or more members to a Redis set. It maps directly to the SADD command in Redis.
//
// Parameters:
// - L: The Lua state, which includes the arguments passed from Lua to Go.
// The first argument should be the key of the Redis set (string),
// and the subsequent arguments should be the members to add (various types).
//
// Returns:
// - 1 value if successful: the number of elements added to the set (integer).
// - 2 values if an error occurs: nil and an error message (string).
//
// Lua Usage Example:
//
// redis.sadd("myset", "value1", "value2", 123, true)
func RedisSAdd(L *lua.LState) int {
key := L.CheckString(1)
values := make([]any, L.GetTop()-1)
for i := 2; i <= L.GetTop(); i++ {
value, err := convertLuaValue(L.Get(i))
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))

return 2
}

values[i-2] = value
}

cmd := rediscli.WriteHandle.SAdd(ctx, key, values...)
if cmd.Err() != nil {
L.Push(lua.LNil)
L.Push(lua.LString(cmd.Err().Error()))

return 2
} else {
stats.RedisWriteCounter.Inc()
L.Push(lua.LNumber(cmd.Val()))

return 1
}
}

// RedisSIsMember checks if a given value is a member of the set stored at a specified key in Redis.
//
// Parameters:
//
// L *lua.LState - The current Lua state that's used for interfacing with Lua scripts.
//
// Returns:
//
// int - The number of results returned to Lua.
// It returns the following to the Lua stack:
// - If an error occurs, it returns nil followed by the error message.
// - If the operation is successful, it returns a boolean indicating presence of the value in the set.
//
// Lua Usage:
//
// result, err = RedisSIsMember(key, value)
//
// Example:
//
// local is_member, err = RedisSIsMember("my_set", "value1")
// if err then
// print("Error:", err)
// else
// if is_member then
// print("Value is a member of the set.")
// else
// print("Value is not a member of the set.")
// end
// end
func RedisSIsMember(L *lua.LState) int {
key := L.CheckString(1)
value := L.CheckString(2)

cmd := rediscli.ReadHandle.SIsMember(ctx, key, value)
if cmd.Err() != nil {
L.Push(lua.LNil)
L.Push(lua.LString(cmd.Err().Error()))

return 2
} else {
stats.RedisReadCounter.Inc()
L.Push(lua.LBool(cmd.Val()))

return 1
}
}

// RedisSMembers retrieves all the members of a set in Redis corresponding to the given key.
//
// Parameters:
// - L: A Lua state object. The first argument in the Lua state is expected to be a string representing the Redis key.
//
// Returns:
// - If the operation is successful, returns a Lua table containing all the members of the set.
// - If the operation fails, returns nil and an error message.
//
// Example usage:
// members = redis_smembers("myset")
// if members ~= nil then
//
// for _, member in ipairs(members) do
// print(member)
// end
//
// else
//
// print("Error retrieving members from Redis")
//
// end
//
// This function utilizes the SMembers command from the Redis client to fetch the set members,
// and it increments the RedisReadCounter to track the read operation.
func RedisSMembers(L *lua.LState) int {
key := L.CheckString(1)

cmd := rediscli.ReadHandle.SMembers(ctx, key)
if cmd.Err() != nil {
L.Push(lua.LNil)
L.Push(lua.LString(cmd.Err().Error()))

return 2
} else {
members := cmd.Val()
table := L.NewTable()
for _, member := range members {
table.Append(lua.LString(member))
}

stats.RedisReadCounter.Inc()
L.Push(table)

return 1
}
}

// RedisSRem removes one or more members from a Redis set.
//
// This function wraps the Redis SREM command. It accepts a key and a variable
// number of values to remove from the set. If any value cannot be converted,
// it returns an error.
//
// Parameters:
// - L (lua.LState): The current Lua state.
//
// Returns:
// - int: The number of return values on the Lua stack. On error, it returns
// two values: nil and the error message. On success, it returns the number
// of removed elements.
func RedisSRem(L *lua.LState) int {
key := L.CheckString(1)
values := make([]any, L.GetTop()-1)

for i := 2; i <= L.GetTop(); i++ {
value, err := convertLuaValue(L.Get(i))
if err != nil {
L.Push(lua.LNil)
L.Push(lua.LString(err.Error()))

return 2
}

values[i-2] = value
}

cmd := rediscli.WriteHandle.SRem(ctx, key, values...)
if cmd.Err() != nil {
L.Push(lua.LNil)
L.Push(lua.LString(cmd.Err().Error()))

return 2
} else {
stats.RedisWriteCounter.Inc()
L.Push(lua.LNumber(cmd.Val()))

return 1
}
}

// RedisSCard fetches the cardinality (number of elements) of the set stored at the specified key in Redis.
//
// Parameters:
// - L (lua.LState): The Lua state that provides the key as its first argument.
//
// Returns:
// - 1 result on the Lua stack representing the number of elements in the set if successful (lua.LNumber).
// - 2 results on the Lua stack (lua.LNil and lua.LString) if there is an error.
//
// Usage example:
// local count = redis_scard("myset")
func RedisSCard(L *lua.LState) int {
key := L.CheckString(1)

cmd := rediscli.ReadHandle.SCard(ctx, key)
if cmd.Err() != nil {
L.Push(lua.LNil)
L.Push(lua.LString(cmd.Err().Error()))

return 2
} else {
stats.RedisReadCounter.Inc()
L.Push(lua.LNumber(cmd.Val()))

return 1
}
}

// SetUPRedisFunctions is a function that associates a set of Redis-related functions to a Lua table.
// Each function is linked to a string that corresponds to its name in the global Lua functions namespace.
// The provided Lua state `L` and the Lua table `table` are used to facilitate this setting up process.
Expand All @@ -565,8 +774,13 @@ func SetUPRedisFunctions(table *lua.LTable, L *lua.LState) {
table.RawSetString(global.LuaFnRedisHDel, L.NewFunction(RedisHDel))
table.RawSetString(global.LuaFnRedisHLen, L.NewFunction(RedisHLen))
table.RawSetString(global.LuaFnRedisHGetAll, L.NewFunction(RedisHGetAll))
table.RawSetString(global.LuaFNRedisHIncrBy, L.NewFunction(RedisHIncrBy))
table.RawSetString(global.LuaFNRedisHIncrByFloat, L.NewFunction(RedisHIncrByFloat))
table.RawSetString(global.LuaFnRedisHIncrBy, L.NewFunction(RedisHIncrBy))
table.RawSetString(global.LuaFnRedisHIncrByFloat, L.NewFunction(RedisHIncrByFloat))
table.RawSetString(global.LuaFnRedisHExists, L.NewFunction(RedisHExists))
table.RawSetString(global.LuaFnRedisRename, L.NewFunction(RedisRename))
table.RawSetString(global.LuaFnRedisSAdd, L.NewFunction(RedisSAdd))
table.RawSetString(global.LuaFnRedisSIsMember, L.NewFunction(RedisSIsMember))
table.RawSetString(global.LuaFnRedisSMembers, L.NewFunction(RedisSMembers))
table.RawSetString(global.LuaFnRedisSRem, L.NewFunction(RedisSRem))
table.RawSetString(global.LuaFnRedisSCard, L.NewFunction(RedisSCard))
}

0 comments on commit b4482e7

Please sign in to comment.