diff --git a/README.md b/README.md index 1d3d419..51026f3 100644 --- a/README.md +++ b/README.md @@ -142,6 +142,12 @@ SELECT CLUSTER_COUNTKEYSINSLOT() SELECT DBSIZE() ``` +### HMGET + +```sql +SELECT HMGET('some-key','field1',...,'fieldn') +``` + ### LLEN ```sql diff --git a/commands.md b/commands.md index 03d1a3b..d8c4379 100644 --- a/commands.md +++ b/commands.md @@ -115,7 +115,7 @@ | HINCRBYFLOAT | 🚧 | HKEYS | 🚧 | HLEN | 🚧 -| HMGET | 🚧 +| HMGET | ✅ [`HMGET`](https://github.com/augmentable-dev/reqlite/tree/main/internal/redis/hmget) | HMSET | 🚧 | HSET | 🚧 | HSETNX | 🚧 diff --git a/internal/redis/hmget/hmget.go b/internal/redis/hmget/hmget.go new file mode 100644 index 0000000..8f07a56 --- /dev/null +++ b/internal/redis/hmget/hmget.go @@ -0,0 +1,41 @@ +package hmget + +import ( + "context" + "fmt" + + "github.com/go-redis/redis/v8" + "go.riyazali.net/sqlite" +) + +type hmget struct { + rdb *redis.Client +} + +func (f *hmget) Args() int { return -1 } +func (f *hmget) Deterministic() bool { return false } +func (f *hmget) Apply(ctx *sqlite.Context, values ...sqlite.Value) { + var ( + key string + fields []string + ) + + if len(values) >= 2 { + key = values[0].Text() + for i := 1; i < len(values); i++ { + fields = append(fields, values[i].Text()) + } + } else { + ctx.ResultError(fmt.Errorf("must supply argument to redis hmget command (key,field1,...,fieldn")) + return + } + + result := f.rdb.HMGet(context.TODO(), key, fields...) + + ctx.ResultText(fmt.Sprintf("%+q", result.String())) +} + +// New returns a sqlite function for reading the contents of a file +func New(rdb *redis.Client) sqlite.Function { + return &hmget{rdb} +} diff --git a/internal/redis/hmget/hmget_test.go b/internal/redis/hmget/hmget_test.go new file mode 100644 index 0000000..d9dcf4e --- /dev/null +++ b/internal/redis/hmget/hmget_test.go @@ -0,0 +1,46 @@ +package hmget_test + +import ( + "testing" + + "github.com/augmentable-dev/reqlite/internal/redis/hmget" + _ "github.com/augmentable-dev/reqlite/internal/sqlite" + "github.com/go-redis/redismock/v8" + "github.com/jmoiron/sqlx" + _ "github.com/mattn/go-sqlite3" + "go.riyazali.net/sqlite" +) + +func TestHmget(t *testing.T) { + rdb, mock := redismock.NewClientMock() + fields := []string{"field1", "field2", "field3"} + values := []interface{}{"val1", "val2", "val3"} + sqlite.Register(func(api *sqlite.ExtensionApi) (sqlite.ErrorCode, error) { + if err := api.CreateFunction("hmget", hmget.New(rdb)); err != nil { + return sqlite.SQLITE_ERROR, err + } + return sqlite.SQLITE_OK, nil + }) + mock.ExpectHMGet("mykey", fields...).SetVal(values) + db, err := sqlx.Open("sqlite3", ":memory:") + if err != nil { + t.Fatal(err) + } + defer db.Close() + + row := db.QueryRow("SELECT hmget('mykey','field1','field2','field3')") + err = row.Err() + if err != nil { + t.Fatal(err) + } + + var s string + err = row.Scan(&s) + if err != nil { + t.Fatal(err) + } + + if err := mock.ExpectationsWereMet(); err != nil { + t.Error(err) + } +} diff --git a/pkg/ext/ext.go b/pkg/ext/ext.go index 77850c5..57ddf26 100644 --- a/pkg/ext/ext.go +++ b/pkg/ext/ext.go @@ -17,6 +17,7 @@ import ( "github.com/augmentable-dev/reqlite/internal/redis/dump" "github.com/augmentable-dev/reqlite/internal/redis/echo" "github.com/augmentable-dev/reqlite/internal/redis/hgetall" + "github.com/augmentable-dev/reqlite/internal/redis/hmget" "github.com/augmentable-dev/reqlite/internal/redis/llen" "github.com/augmentable-dev/reqlite/internal/redis/lrange" "github.com/go-redis/redis/v8" @@ -98,6 +99,10 @@ func init() { return sqlite.SQLITE_ERROR, err } + if err := api.CreateFunction("hmget", hmget.New(rdb)); err != nil { + return sqlite.SQLITE_ERROR, err + } + if err := api.CreateFunction("llen", llen.New(rdb)); err != nil { return sqlite.SQLITE_ERROR, err }