Skip to content

Commit

Permalink
Implement FUNCTION group of commands (redis#2475)
Browse files Browse the repository at this point in the history
* Implements function load, load replace and flush

* Implements `FUNCTIONS LIST`

* Fixes ineffectual assignment to err

* Extends other tests from the `FUNCTION` group

* Changes RdsFunction to Function

* Renames FunctionList command, for consistency

* Small fixes after review

* More fixes after review

* Implements the `LIBRARYNAME` and `WITHCODE` args for `FUNCTION LIST`

* Implements `FUNCTION DUMP`. Solves redis#2396

* Use a pointer instead of value for `First()`

* Implements `FUNCTION RESTORE`. Closes redis#2401.

* chore: tidy code

Signed-off-by: monkey92t <[email protected]>

---------

Signed-off-by: monkey92t <[email protected]>
Co-authored-by: monkey92t <[email protected]>
  • Loading branch information
elena-kolevska and monkey92t authored Mar 9, 2023
1 parent cbfe6cd commit 6501a8b
Show file tree
Hide file tree
Showing 3 changed files with 452 additions and 0 deletions.
152 changes: 152 additions & 0 deletions command.go
Original file line number Diff line number Diff line change
Expand Up @@ -3840,3 +3840,155 @@ func (cmd *ZSliceWithKeyCmd) readReply(rd *proto.Reader) (err error) {

return nil
}

type Function struct {
Name string
Description string
Flags []string
}

type Library struct {
Name string
Engine string
Functions []Function
Code string
}

type FunctionListCmd struct {
baseCmd

val []Library
}

var _ Cmder = (*FunctionListCmd)(nil)

func NewFunctionListCmd(ctx context.Context, args ...interface{}) *FunctionListCmd {
return &FunctionListCmd{
baseCmd: baseCmd{
ctx: ctx,
args: args,
},
}
}

func (cmd *FunctionListCmd) SetVal(val []Library) {
cmd.val = val
}

func (cmd *FunctionListCmd) String() string {
return cmdString(cmd, cmd.val)
}

func (cmd *FunctionListCmd) Val() []Library {
return cmd.val
}

func (cmd *FunctionListCmd) Result() ([]Library, error) {
return cmd.val, cmd.err
}

func (cmd *FunctionListCmd) First() (*Library, error) {
if cmd.err != nil {
return nil, cmd.err
}
if len(cmd.val) > 0 {
return &cmd.val[0], nil
}
return nil, Nil
}

func (cmd *FunctionListCmd) readReply(rd *proto.Reader) (err error) {
n, err := rd.ReadArrayLen()
if err != nil {
return err
}

libraries := make([]Library, n)
for i := 0; i < n; i++ {
nn, err := rd.ReadMapLen()
if err != nil {
return err
}

library := Library{}
for f := 0; f < nn; f++ {
key, err := rd.ReadString()
if err != nil {
return err
}

switch key {
case "library_name":
library.Name, err = rd.ReadString()
case "engine":
library.Engine, err = rd.ReadString()
case "functions":
library.Functions, err = cmd.readFunctions(rd)
case "library_code":
library.Code, err = rd.ReadString()
default:
return fmt.Errorf("redis: function list unexpected key %s", key)
}

if err != nil {
return err
}
}

libraries[i] = library
}
cmd.val = libraries
return nil
}

func (cmd *FunctionListCmd) readFunctions(rd *proto.Reader) ([]Function, error) {
n, err := rd.ReadArrayLen()
if err != nil {
return nil, err
}

functions := make([]Function, n)
for i := 0; i < n; i++ {
nn, err := rd.ReadMapLen()
if err != nil {
return nil, err
}

function := Function{}
for f := 0; f < nn; f++ {
key, err := rd.ReadString()
if err != nil {
return nil, err
}

switch key {
case "name":
if function.Name, err = rd.ReadString(); err != nil {
return nil, err
}
case "description":
if function.Description, err = rd.ReadString(); err != nil && err != Nil {
return nil, err
}
case "flags":
// resp set
nx, err := rd.ReadArrayLen()
if err != nil {
return nil, err
}

function.Flags = make([]string, nx)
for j := 0; j < nx; j++ {
if function.Flags[j], err = rd.ReadString(); err != nil {
return nil, err
}
}
default:
return nil, fmt.Errorf("redis: function list unexpected key %s", key)
}
}

functions[i] = function
}
return functions, nil
}
79 changes: 79 additions & 0 deletions commands.go
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,15 @@ type Cmdable interface {
ScriptKill(ctx context.Context) *StatusCmd
ScriptLoad(ctx context.Context, script string) *StringCmd

FunctionLoad(ctx context.Context, code string) *StringCmd
FunctionLoadReplace(ctx context.Context, code string) *StringCmd
FunctionDelete(ctx context.Context, libName string) *StringCmd
FunctionFlush(ctx context.Context) *StringCmd
FunctionFlushAsync(ctx context.Context) *StringCmd
FunctionList(ctx context.Context, q FunctionListQuery) *FunctionListCmd
FunctionDump(ctx context.Context) *StringCmd
FunctionRestore(ctx context.Context, libDump string) *StringCmd

Publish(ctx context.Context, channel string, message interface{}) *IntCmd
SPublish(ctx context.Context, channel string, message interface{}) *IntCmd
PubSubChannels(ctx context.Context, pattern string) *StringSliceCmd
Expand Down Expand Up @@ -3270,6 +3279,76 @@ func (c cmdable) ScriptLoad(ctx context.Context, script string) *StringCmd {
return cmd
}

// ------------------------------------------------------------------------------

// FunctionListQuery is used with FunctionList to query for Redis libraries
//
// LibraryNamePattern - Use an empty string to get all libraries.
// - Use a glob-style pattern to match multiple libraries with a matching name
// - Use a library's full name to match a single library
// WithCode - If true, it will return the code of the library
type FunctionListQuery struct {
LibraryNamePattern string
WithCode bool
}

func (c cmdable) FunctionLoad(ctx context.Context, code string) *StringCmd {
cmd := NewStringCmd(ctx, "function", "load", code)
_ = c(ctx, cmd)
return cmd
}

func (c cmdable) FunctionLoadReplace(ctx context.Context, code string) *StringCmd {
cmd := NewStringCmd(ctx, "function", "load", "replace", code)
_ = c(ctx, cmd)
return cmd
}

func (c cmdable) FunctionDelete(ctx context.Context, libName string) *StringCmd {
cmd := NewStringCmd(ctx, "function", "delete", libName)
_ = c(ctx, cmd)
return cmd
}

func (c cmdable) FunctionFlush(ctx context.Context) *StringCmd {
cmd := NewStringCmd(ctx, "function", "flush")
_ = c(ctx, cmd)
return cmd
}

func (c cmdable) FunctionFlushAsync(ctx context.Context) *StringCmd {
cmd := NewStringCmd(ctx, "function", "flush", "async")
_ = c(ctx, cmd)
return cmd
}

func (c cmdable) FunctionList(ctx context.Context, q FunctionListQuery) *FunctionListCmd {
args := make([]interface{}, 2, 5)
args[0] = "function"
args[1] = "list"
if q.LibraryNamePattern != "" {
args = append(args, "libraryname", q.LibraryNamePattern)
}
if q.WithCode {
args = append(args, "withcode")
}
cmd := NewFunctionListCmd(ctx, args...)
_ = c(ctx, cmd)
return cmd
}

func (c cmdable) FunctionDump(ctx context.Context) *StringCmd {
cmd := NewStringCmd(ctx, "function", "dump")
_ = c(ctx, cmd)
return cmd
}

func (c cmdable) FunctionRestore(ctx context.Context, libDump string) *StringCmd {
cmd := NewStringCmd(ctx, "function", "restore", libDump)
_ = c(ctx, cmd)
return cmd
}

//------------------------------------------------------------------------------

// Publish posts the message to the channel.
Expand Down
Loading

0 comments on commit 6501a8b

Please sign in to comment.