diff --git a/database/database.sql b/database/database.sql deleted file mode 100644 index e69de29..0000000 diff --git a/database/sqlite.db b/database/sqlite.db new file mode 100644 index 0000000..b979187 Binary files /dev/null and b/database/sqlite.db differ diff --git a/go.mod b/go.mod index e357883..796d9dd 100644 --- a/go.mod +++ b/go.mod @@ -2,10 +2,12 @@ module GoRedis go 1.24.4 -require github.com/redis/go-redis/v9 v9.11.0 +require ( + github.com/mattn/go-sqlite3 v1.14.32 + github.com/redis/go-redis/v9 v9.11.0 +) require ( github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect - github.com/mattn/go-sqlite3 v1.14.32 // indirect ) diff --git a/src/handlers/ready.go b/src/handlers/ready.go index aa5f2e6..c332905 100644 --- a/src/handlers/ready.go +++ b/src/handlers/ready.go @@ -10,9 +10,10 @@ import ( ) func ReadyHandler(w http.ResponseWriter, r *http.Request) { + route := utils.ReadyRoute + " " response := &utils.HTTPResponse{Message: "Server is ready.", Status: 200} responseJson, _ := json.Marshal(response) - log.Print(utils.TextGreen("/ready " + strconv.Itoa(response.Status))) + log.Print(utils.TextGreen(route + strconv.Itoa(response.Status))) fmt.Fprint(w, string(responseJson)) } diff --git a/src/handlers/redis.go b/src/handlers/redis.go index 76ad2b3..7b967b6 100644 --- a/src/handlers/redis.go +++ b/src/handlers/redis.go @@ -13,11 +13,12 @@ import ( func RedisReadyHandler(w http.ResponseWriter, r *http.Request) { ctx := context.Background() + route := utils.ReadyRoute + " " redisClient := services.NewRedisClient() _, err := redisClient.Ping(ctx).Result() if err != nil { - log.Print(utils.TextRed("/redis/ready " + strconv.Itoa(500))) + log.Print(utils.TextRed(route + strconv.Itoa(500))) errorResponse := &utils.HTTPResponse{Message: "Failed redis connection.", Status: 500} errorResponseJson, _ := json.Marshal(errorResponse) fmt.Fprint(w, string(errorResponseJson)) @@ -26,6 +27,6 @@ func RedisReadyHandler(w http.ResponseWriter, r *http.Request) { response := &utils.HTTPResponse{Message: "Redis is ready.", Status: 200} responseJson, _ := json.Marshal(response) - log.Print(utils.TextGreen("/redis/ready " + strconv.Itoa(response.Status))) + log.Print(utils.TextGreen(route + strconv.Itoa(response.Status))) fmt.Fprint(w, string(responseJson)) } diff --git a/src/handlers/sqlite.go b/src/handlers/sqlite.go index 7040c99..90baf0a 100644 --- a/src/handlers/sqlite.go +++ b/src/handlers/sqlite.go @@ -1,6 +1,7 @@ package handlers import ( + "GoRedis/src/services" "GoRedis/src/utils" "encoding/json" "fmt" @@ -10,8 +11,20 @@ import ( ) func SQLiteReadyHandler(w http.ResponseWriter, r *http.Request) { + route := utils.SQLReadyRoute + " " + err := services.SQLCreateTable() + + if err != nil { + fmt.Println(utils.TextRed(err.Error())) + errResponse := &utils.HTTPResponse{Message: "Something is wrong", Status: 500} + errResponseJson, _ := json.Marshal(errResponse) + log.Print(utils.TextRed(route + strconv.Itoa(errResponse.Status))) + fmt.Fprint(w, string(errResponseJson)) + return + } + response := &utils.HTTPResponse{Message: "SQLite is ready", Status: 200} responseJson, _ := json.Marshal(response) - log.Print(utils.TextGreen("/sqlite/ready " + strconv.Itoa(response.Status))) + log.Print(utils.TextGreen(route + strconv.Itoa(response.Status))) fmt.Fprint(w, string(responseJson)) } diff --git a/src/services/sqlite_service.go b/src/services/sqlite_service.go index 5e568ea..087439c 100644 --- a/src/services/sqlite_service.go +++ b/src/services/sqlite_service.go @@ -1 +1,45 @@ package services + +import ( + "GoRedis/src/utils" + "database/sql" + "fmt" + + _ "github.com/mattn/go-sqlite3" +) + +func CreateSQLInstance() (*sql.DB, error) { + db, err := sql.Open("sqlite3", "database/sqlite.db") + + if err != nil { + return nil, err + } + + return db, nil +} + +func SQLCreateTable() error { + db, err := CreateSQLInstance() + + if err != nil { + return err + } + + defer db.Close() + + createTableStatement := ` + CREATE TABLE IF NOT EXISTS users ( + id INTEGER PRIMARY KEY AUTOINCREMENT, + name TEXT NOT NULL, + email TEXT UNIQUE + );` + + _, err = db.Exec(createTableStatement) + + if err != nil { + fmt.Println(utils.TextRed(err.Error())) + return err + } + + return nil +} diff --git a/src/utils/routes.go b/src/utils/routes.go new file mode 100644 index 0000000..e270fd0 --- /dev/null +++ b/src/utils/routes.go @@ -0,0 +1,7 @@ +package utils + +const ( + ReadyRoute = "/ready" + RedisReadyRoute = "/redis/ready" + SQLReadyRoute = "/database/ready" +) diff --git a/vendor/github.com/cespare/xxhash/v2/LICENSE.txt b/vendor/github.com/cespare/xxhash/v2/LICENSE.txt deleted file mode 100644 index 24b5306..0000000 --- a/vendor/github.com/cespare/xxhash/v2/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -Copyright (c) 2016 Caleb Spare - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/github.com/cespare/xxhash/v2/README.md b/vendor/github.com/cespare/xxhash/v2/README.md deleted file mode 100644 index 33c8830..0000000 --- a/vendor/github.com/cespare/xxhash/v2/README.md +++ /dev/null @@ -1,74 +0,0 @@ -# xxhash - -[![Go Reference](https://pkg.go.dev/badge/github.com/cespare/xxhash/v2.svg)](https://pkg.go.dev/github.com/cespare/xxhash/v2) -[![Test](https://github.com/cespare/xxhash/actions/workflows/test.yml/badge.svg)](https://github.com/cespare/xxhash/actions/workflows/test.yml) - -xxhash is a Go implementation of the 64-bit [xxHash] algorithm, XXH64. This is a -high-quality hashing algorithm that is much faster than anything in the Go -standard library. - -This package provides a straightforward API: - -``` -func Sum64(b []byte) uint64 -func Sum64String(s string) uint64 -type Digest struct{ ... } - func New() *Digest -``` - -The `Digest` type implements hash.Hash64. Its key methods are: - -``` -func (*Digest) Write([]byte) (int, error) -func (*Digest) WriteString(string) (int, error) -func (*Digest) Sum64() uint64 -``` - -The package is written with optimized pure Go and also contains even faster -assembly implementations for amd64 and arm64. If desired, the `purego` build tag -opts into using the Go code even on those architectures. - -[xxHash]: http://cyan4973.github.io/xxHash/ - -## Compatibility - -This package is in a module and the latest code is in version 2 of the module. -You need a version of Go with at least "minimal module compatibility" to use -github.com/cespare/xxhash/v2: - -* 1.9.7+ for Go 1.9 -* 1.10.3+ for Go 1.10 -* Go 1.11 or later - -I recommend using the latest release of Go. - -## Benchmarks - -Here are some quick benchmarks comparing the pure-Go and assembly -implementations of Sum64. - -| input size | purego | asm | -| ---------- | --------- | --------- | -| 4 B | 1.3 GB/s | 1.2 GB/s | -| 16 B | 2.9 GB/s | 3.5 GB/s | -| 100 B | 6.9 GB/s | 8.1 GB/s | -| 4 KB | 11.7 GB/s | 16.7 GB/s | -| 10 MB | 12.0 GB/s | 17.3 GB/s | - -These numbers were generated on Ubuntu 20.04 with an Intel Xeon Platinum 8252C -CPU using the following commands under Go 1.19.2: - -``` -benchstat <(go test -tags purego -benchtime 500ms -count 15 -bench 'Sum64$') -benchstat <(go test -benchtime 500ms -count 15 -bench 'Sum64$') -``` - -## Projects using this package - -- [InfluxDB](https://github.com/influxdata/influxdb) -- [Prometheus](https://github.com/prometheus/prometheus) -- [VictoriaMetrics](https://github.com/VictoriaMetrics/VictoriaMetrics) -- [FreeCache](https://github.com/coocood/freecache) -- [FastCache](https://github.com/VictoriaMetrics/fastcache) -- [Ristretto](https://github.com/dgraph-io/ristretto) -- [Badger](https://github.com/dgraph-io/badger) diff --git a/vendor/github.com/cespare/xxhash/v2/testall.sh b/vendor/github.com/cespare/xxhash/v2/testall.sh deleted file mode 100644 index 94b9c44..0000000 --- a/vendor/github.com/cespare/xxhash/v2/testall.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash -set -eu -o pipefail - -# Small convenience script for running the tests with various combinations of -# arch/tags. This assumes we're running on amd64 and have qemu available. - -go test ./... -go test -tags purego ./... -GOARCH=arm64 go test -GOARCH=arm64 go test -tags purego diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash.go b/vendor/github.com/cespare/xxhash/v2/xxhash.go deleted file mode 100644 index 78bddf1..0000000 --- a/vendor/github.com/cespare/xxhash/v2/xxhash.go +++ /dev/null @@ -1,243 +0,0 @@ -// Package xxhash implements the 64-bit variant of xxHash (XXH64) as described -// at http://cyan4973.github.io/xxHash/. -package xxhash - -import ( - "encoding/binary" - "errors" - "math/bits" -) - -const ( - prime1 uint64 = 11400714785074694791 - prime2 uint64 = 14029467366897019727 - prime3 uint64 = 1609587929392839161 - prime4 uint64 = 9650029242287828579 - prime5 uint64 = 2870177450012600261 -) - -// Store the primes in an array as well. -// -// The consts are used when possible in Go code to avoid MOVs but we need a -// contiguous array for the assembly code. -var primes = [...]uint64{prime1, prime2, prime3, prime4, prime5} - -// Digest implements hash.Hash64. -// -// Note that a zero-valued Digest is not ready to receive writes. -// Call Reset or create a Digest using New before calling other methods. -type Digest struct { - v1 uint64 - v2 uint64 - v3 uint64 - v4 uint64 - total uint64 - mem [32]byte - n int // how much of mem is used -} - -// New creates a new Digest with a zero seed. -func New() *Digest { - return NewWithSeed(0) -} - -// NewWithSeed creates a new Digest with the given seed. -func NewWithSeed(seed uint64) *Digest { - var d Digest - d.ResetWithSeed(seed) - return &d -} - -// Reset clears the Digest's state so that it can be reused. -// It uses a seed value of zero. -func (d *Digest) Reset() { - d.ResetWithSeed(0) -} - -// ResetWithSeed clears the Digest's state so that it can be reused. -// It uses the given seed to initialize the state. -func (d *Digest) ResetWithSeed(seed uint64) { - d.v1 = seed + prime1 + prime2 - d.v2 = seed + prime2 - d.v3 = seed - d.v4 = seed - prime1 - d.total = 0 - d.n = 0 -} - -// Size always returns 8 bytes. -func (d *Digest) Size() int { return 8 } - -// BlockSize always returns 32 bytes. -func (d *Digest) BlockSize() int { return 32 } - -// Write adds more data to d. It always returns len(b), nil. -func (d *Digest) Write(b []byte) (n int, err error) { - n = len(b) - d.total += uint64(n) - - memleft := d.mem[d.n&(len(d.mem)-1):] - - if d.n+n < 32 { - // This new data doesn't even fill the current block. - copy(memleft, b) - d.n += n - return - } - - if d.n > 0 { - // Finish off the partial block. - c := copy(memleft, b) - d.v1 = round(d.v1, u64(d.mem[0:8])) - d.v2 = round(d.v2, u64(d.mem[8:16])) - d.v3 = round(d.v3, u64(d.mem[16:24])) - d.v4 = round(d.v4, u64(d.mem[24:32])) - b = b[c:] - d.n = 0 - } - - if len(b) >= 32 { - // One or more full blocks left. - nw := writeBlocks(d, b) - b = b[nw:] - } - - // Store any remaining partial block. - copy(d.mem[:], b) - d.n = len(b) - - return -} - -// Sum appends the current hash to b and returns the resulting slice. -func (d *Digest) Sum(b []byte) []byte { - s := d.Sum64() - return append( - b, - byte(s>>56), - byte(s>>48), - byte(s>>40), - byte(s>>32), - byte(s>>24), - byte(s>>16), - byte(s>>8), - byte(s), - ) -} - -// Sum64 returns the current hash. -func (d *Digest) Sum64() uint64 { - var h uint64 - - if d.total >= 32 { - v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 - h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) - h = mergeRound(h, v1) - h = mergeRound(h, v2) - h = mergeRound(h, v3) - h = mergeRound(h, v4) - } else { - h = d.v3 + prime5 - } - - h += d.total - - b := d.mem[:d.n&(len(d.mem)-1)] - for ; len(b) >= 8; b = b[8:] { - k1 := round(0, u64(b[:8])) - h ^= k1 - h = rol27(h)*prime1 + prime4 - } - if len(b) >= 4 { - h ^= uint64(u32(b[:4])) * prime1 - h = rol23(h)*prime2 + prime3 - b = b[4:] - } - for ; len(b) > 0; b = b[1:] { - h ^= uint64(b[0]) * prime5 - h = rol11(h) * prime1 - } - - h ^= h >> 33 - h *= prime2 - h ^= h >> 29 - h *= prime3 - h ^= h >> 32 - - return h -} - -const ( - magic = "xxh\x06" - marshaledSize = len(magic) + 8*5 + 32 -) - -// MarshalBinary implements the encoding.BinaryMarshaler interface. -func (d *Digest) MarshalBinary() ([]byte, error) { - b := make([]byte, 0, marshaledSize) - b = append(b, magic...) - b = appendUint64(b, d.v1) - b = appendUint64(b, d.v2) - b = appendUint64(b, d.v3) - b = appendUint64(b, d.v4) - b = appendUint64(b, d.total) - b = append(b, d.mem[:d.n]...) - b = b[:len(b)+len(d.mem)-d.n] - return b, nil -} - -// UnmarshalBinary implements the encoding.BinaryUnmarshaler interface. -func (d *Digest) UnmarshalBinary(b []byte) error { - if len(b) < len(magic) || string(b[:len(magic)]) != magic { - return errors.New("xxhash: invalid hash state identifier") - } - if len(b) != marshaledSize { - return errors.New("xxhash: invalid hash state size") - } - b = b[len(magic):] - b, d.v1 = consumeUint64(b) - b, d.v2 = consumeUint64(b) - b, d.v3 = consumeUint64(b) - b, d.v4 = consumeUint64(b) - b, d.total = consumeUint64(b) - copy(d.mem[:], b) - d.n = int(d.total % uint64(len(d.mem))) - return nil -} - -func appendUint64(b []byte, x uint64) []byte { - var a [8]byte - binary.LittleEndian.PutUint64(a[:], x) - return append(b, a[:]...) -} - -func consumeUint64(b []byte) ([]byte, uint64) { - x := u64(b) - return b[8:], x -} - -func u64(b []byte) uint64 { return binary.LittleEndian.Uint64(b) } -func u32(b []byte) uint32 { return binary.LittleEndian.Uint32(b) } - -func round(acc, input uint64) uint64 { - acc += input * prime2 - acc = rol31(acc) - acc *= prime1 - return acc -} - -func mergeRound(acc, val uint64) uint64 { - val = round(0, val) - acc ^= val - acc = acc*prime1 + prime4 - return acc -} - -func rol1(x uint64) uint64 { return bits.RotateLeft64(x, 1) } -func rol7(x uint64) uint64 { return bits.RotateLeft64(x, 7) } -func rol11(x uint64) uint64 { return bits.RotateLeft64(x, 11) } -func rol12(x uint64) uint64 { return bits.RotateLeft64(x, 12) } -func rol18(x uint64) uint64 { return bits.RotateLeft64(x, 18) } -func rol23(x uint64) uint64 { return bits.RotateLeft64(x, 23) } -func rol27(x uint64) uint64 { return bits.RotateLeft64(x, 27) } -func rol31(x uint64) uint64 { return bits.RotateLeft64(x, 31) } diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s b/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s deleted file mode 100644 index 3e8b132..0000000 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_amd64.s +++ /dev/null @@ -1,209 +0,0 @@ -//go:build !appengine && gc && !purego -// +build !appengine -// +build gc -// +build !purego - -#include "textflag.h" - -// Registers: -#define h AX -#define d AX -#define p SI // pointer to advance through b -#define n DX -#define end BX // loop end -#define v1 R8 -#define v2 R9 -#define v3 R10 -#define v4 R11 -#define x R12 -#define prime1 R13 -#define prime2 R14 -#define prime4 DI - -#define round(acc, x) \ - IMULQ prime2, x \ - ADDQ x, acc \ - ROLQ $31, acc \ - IMULQ prime1, acc - -// round0 performs the operation x = round(0, x). -#define round0(x) \ - IMULQ prime2, x \ - ROLQ $31, x \ - IMULQ prime1, x - -// mergeRound applies a merge round on the two registers acc and x. -// It assumes that prime1, prime2, and prime4 have been loaded. -#define mergeRound(acc, x) \ - round0(x) \ - XORQ x, acc \ - IMULQ prime1, acc \ - ADDQ prime4, acc - -// blockLoop processes as many 32-byte blocks as possible, -// updating v1, v2, v3, and v4. It assumes that there is at least one block -// to process. -#define blockLoop() \ -loop: \ - MOVQ +0(p), x \ - round(v1, x) \ - MOVQ +8(p), x \ - round(v2, x) \ - MOVQ +16(p), x \ - round(v3, x) \ - MOVQ +24(p), x \ - round(v4, x) \ - ADDQ $32, p \ - CMPQ p, end \ - JLE loop - -// func Sum64(b []byte) uint64 -TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 - // Load fixed primes. - MOVQ ·primes+0(SB), prime1 - MOVQ ·primes+8(SB), prime2 - MOVQ ·primes+24(SB), prime4 - - // Load slice. - MOVQ b_base+0(FP), p - MOVQ b_len+8(FP), n - LEAQ (p)(n*1), end - - // The first loop limit will be len(b)-32. - SUBQ $32, end - - // Check whether we have at least one block. - CMPQ n, $32 - JLT noBlocks - - // Set up initial state (v1, v2, v3, v4). - MOVQ prime1, v1 - ADDQ prime2, v1 - MOVQ prime2, v2 - XORQ v3, v3 - XORQ v4, v4 - SUBQ prime1, v4 - - blockLoop() - - MOVQ v1, h - ROLQ $1, h - MOVQ v2, x - ROLQ $7, x - ADDQ x, h - MOVQ v3, x - ROLQ $12, x - ADDQ x, h - MOVQ v4, x - ROLQ $18, x - ADDQ x, h - - mergeRound(h, v1) - mergeRound(h, v2) - mergeRound(h, v3) - mergeRound(h, v4) - - JMP afterBlocks - -noBlocks: - MOVQ ·primes+32(SB), h - -afterBlocks: - ADDQ n, h - - ADDQ $24, end - CMPQ p, end - JG try4 - -loop8: - MOVQ (p), x - ADDQ $8, p - round0(x) - XORQ x, h - ROLQ $27, h - IMULQ prime1, h - ADDQ prime4, h - - CMPQ p, end - JLE loop8 - -try4: - ADDQ $4, end - CMPQ p, end - JG try1 - - MOVL (p), x - ADDQ $4, p - IMULQ prime1, x - XORQ x, h - - ROLQ $23, h - IMULQ prime2, h - ADDQ ·primes+16(SB), h - -try1: - ADDQ $4, end - CMPQ p, end - JGE finalize - -loop1: - MOVBQZX (p), x - ADDQ $1, p - IMULQ ·primes+32(SB), x - XORQ x, h - ROLQ $11, h - IMULQ prime1, h - - CMPQ p, end - JL loop1 - -finalize: - MOVQ h, x - SHRQ $33, x - XORQ x, h - IMULQ prime2, h - MOVQ h, x - SHRQ $29, x - XORQ x, h - IMULQ ·primes+16(SB), h - MOVQ h, x - SHRQ $32, x - XORQ x, h - - MOVQ h, ret+24(FP) - RET - -// func writeBlocks(d *Digest, b []byte) int -TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 - // Load fixed primes needed for round. - MOVQ ·primes+0(SB), prime1 - MOVQ ·primes+8(SB), prime2 - - // Load slice. - MOVQ b_base+8(FP), p - MOVQ b_len+16(FP), n - LEAQ (p)(n*1), end - SUBQ $32, end - - // Load vN from d. - MOVQ s+0(FP), d - MOVQ 0(d), v1 - MOVQ 8(d), v2 - MOVQ 16(d), v3 - MOVQ 24(d), v4 - - // We don't need to check the loop condition here; this function is - // always called with at least one block of data to process. - blockLoop() - - // Copy vN back to d. - MOVQ v1, 0(d) - MOVQ v2, 8(d) - MOVQ v3, 16(d) - MOVQ v4, 24(d) - - // The number of bytes written is p minus the old base pointer. - SUBQ b_base+8(FP), p - MOVQ p, ret+32(FP) - - RET diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s b/vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s deleted file mode 100644 index 7e3145a..0000000 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_arm64.s +++ /dev/null @@ -1,183 +0,0 @@ -//go:build !appengine && gc && !purego -// +build !appengine -// +build gc -// +build !purego - -#include "textflag.h" - -// Registers: -#define digest R1 -#define h R2 // return value -#define p R3 // input pointer -#define n R4 // input length -#define nblocks R5 // n / 32 -#define prime1 R7 -#define prime2 R8 -#define prime3 R9 -#define prime4 R10 -#define prime5 R11 -#define v1 R12 -#define v2 R13 -#define v3 R14 -#define v4 R15 -#define x1 R20 -#define x2 R21 -#define x3 R22 -#define x4 R23 - -#define round(acc, x) \ - MADD prime2, acc, x, acc \ - ROR $64-31, acc \ - MUL prime1, acc - -// round0 performs the operation x = round(0, x). -#define round0(x) \ - MUL prime2, x \ - ROR $64-31, x \ - MUL prime1, x - -#define mergeRound(acc, x) \ - round0(x) \ - EOR x, acc \ - MADD acc, prime4, prime1, acc - -// blockLoop processes as many 32-byte blocks as possible, -// updating v1, v2, v3, and v4. It assumes that n >= 32. -#define blockLoop() \ - LSR $5, n, nblocks \ - PCALIGN $16 \ - loop: \ - LDP.P 16(p), (x1, x2) \ - LDP.P 16(p), (x3, x4) \ - round(v1, x1) \ - round(v2, x2) \ - round(v3, x3) \ - round(v4, x4) \ - SUB $1, nblocks \ - CBNZ nblocks, loop - -// func Sum64(b []byte) uint64 -TEXT ·Sum64(SB), NOSPLIT|NOFRAME, $0-32 - LDP b_base+0(FP), (p, n) - - LDP ·primes+0(SB), (prime1, prime2) - LDP ·primes+16(SB), (prime3, prime4) - MOVD ·primes+32(SB), prime5 - - CMP $32, n - CSEL LT, prime5, ZR, h // if n < 32 { h = prime5 } else { h = 0 } - BLT afterLoop - - ADD prime1, prime2, v1 - MOVD prime2, v2 - MOVD $0, v3 - NEG prime1, v4 - - blockLoop() - - ROR $64-1, v1, x1 - ROR $64-7, v2, x2 - ADD x1, x2 - ROR $64-12, v3, x3 - ROR $64-18, v4, x4 - ADD x3, x4 - ADD x2, x4, h - - mergeRound(h, v1) - mergeRound(h, v2) - mergeRound(h, v3) - mergeRound(h, v4) - -afterLoop: - ADD n, h - - TBZ $4, n, try8 - LDP.P 16(p), (x1, x2) - - round0(x1) - - // NOTE: here and below, sequencing the EOR after the ROR (using a - // rotated register) is worth a small but measurable speedup for small - // inputs. - ROR $64-27, h - EOR x1 @> 64-27, h, h - MADD h, prime4, prime1, h - - round0(x2) - ROR $64-27, h - EOR x2 @> 64-27, h, h - MADD h, prime4, prime1, h - -try8: - TBZ $3, n, try4 - MOVD.P 8(p), x1 - - round0(x1) - ROR $64-27, h - EOR x1 @> 64-27, h, h - MADD h, prime4, prime1, h - -try4: - TBZ $2, n, try2 - MOVWU.P 4(p), x2 - - MUL prime1, x2 - ROR $64-23, h - EOR x2 @> 64-23, h, h - MADD h, prime3, prime2, h - -try2: - TBZ $1, n, try1 - MOVHU.P 2(p), x3 - AND $255, x3, x1 - LSR $8, x3, x2 - - MUL prime5, x1 - ROR $64-11, h - EOR x1 @> 64-11, h, h - MUL prime1, h - - MUL prime5, x2 - ROR $64-11, h - EOR x2 @> 64-11, h, h - MUL prime1, h - -try1: - TBZ $0, n, finalize - MOVBU (p), x4 - - MUL prime5, x4 - ROR $64-11, h - EOR x4 @> 64-11, h, h - MUL prime1, h - -finalize: - EOR h >> 33, h - MUL prime2, h - EOR h >> 29, h - MUL prime3, h - EOR h >> 32, h - - MOVD h, ret+24(FP) - RET - -// func writeBlocks(d *Digest, b []byte) int -TEXT ·writeBlocks(SB), NOSPLIT|NOFRAME, $0-40 - LDP ·primes+0(SB), (prime1, prime2) - - // Load state. Assume v[1-4] are stored contiguously. - MOVD d+0(FP), digest - LDP 0(digest), (v1, v2) - LDP 16(digest), (v3, v4) - - LDP b_base+8(FP), (p, n) - - blockLoop() - - // Store updated state. - STP (v1, v2), 0(digest) - STP (v3, v4), 16(digest) - - BIC $31, n - MOVD n, ret+32(FP) - RET diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go b/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go deleted file mode 100644 index 78f95f2..0000000 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_asm.go +++ /dev/null @@ -1,15 +0,0 @@ -//go:build (amd64 || arm64) && !appengine && gc && !purego -// +build amd64 arm64 -// +build !appengine -// +build gc -// +build !purego - -package xxhash - -// Sum64 computes the 64-bit xxHash digest of b with a zero seed. -// -//go:noescape -func Sum64(b []byte) uint64 - -//go:noescape -func writeBlocks(d *Digest, b []byte) int diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_other.go b/vendor/github.com/cespare/xxhash/v2/xxhash_other.go deleted file mode 100644 index 118e49e..0000000 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_other.go +++ /dev/null @@ -1,76 +0,0 @@ -//go:build (!amd64 && !arm64) || appengine || !gc || purego -// +build !amd64,!arm64 appengine !gc purego - -package xxhash - -// Sum64 computes the 64-bit xxHash digest of b with a zero seed. -func Sum64(b []byte) uint64 { - // A simpler version would be - // d := New() - // d.Write(b) - // return d.Sum64() - // but this is faster, particularly for small inputs. - - n := len(b) - var h uint64 - - if n >= 32 { - v1 := primes[0] + prime2 - v2 := prime2 - v3 := uint64(0) - v4 := -primes[0] - for len(b) >= 32 { - v1 = round(v1, u64(b[0:8:len(b)])) - v2 = round(v2, u64(b[8:16:len(b)])) - v3 = round(v3, u64(b[16:24:len(b)])) - v4 = round(v4, u64(b[24:32:len(b)])) - b = b[32:len(b):len(b)] - } - h = rol1(v1) + rol7(v2) + rol12(v3) + rol18(v4) - h = mergeRound(h, v1) - h = mergeRound(h, v2) - h = mergeRound(h, v3) - h = mergeRound(h, v4) - } else { - h = prime5 - } - - h += uint64(n) - - for ; len(b) >= 8; b = b[8:] { - k1 := round(0, u64(b[:8])) - h ^= k1 - h = rol27(h)*prime1 + prime4 - } - if len(b) >= 4 { - h ^= uint64(u32(b[:4])) * prime1 - h = rol23(h)*prime2 + prime3 - b = b[4:] - } - for ; len(b) > 0; b = b[1:] { - h ^= uint64(b[0]) * prime5 - h = rol11(h) * prime1 - } - - h ^= h >> 33 - h *= prime2 - h ^= h >> 29 - h *= prime3 - h ^= h >> 32 - - return h -} - -func writeBlocks(d *Digest, b []byte) int { - v1, v2, v3, v4 := d.v1, d.v2, d.v3, d.v4 - n := len(b) - for len(b) >= 32 { - v1 = round(v1, u64(b[0:8:len(b)])) - v2 = round(v2, u64(b[8:16:len(b)])) - v3 = round(v3, u64(b[16:24:len(b)])) - v4 = round(v4, u64(b[24:32:len(b)])) - b = b[32:len(b):len(b)] - } - d.v1, d.v2, d.v3, d.v4 = v1, v2, v3, v4 - return n - len(b) -} diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go deleted file mode 100644 index 05f5e7d..0000000 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_safe.go +++ /dev/null @@ -1,16 +0,0 @@ -//go:build appengine -// +build appengine - -// This file contains the safe implementations of otherwise unsafe-using code. - -package xxhash - -// Sum64String computes the 64-bit xxHash digest of s with a zero seed. -func Sum64String(s string) uint64 { - return Sum64([]byte(s)) -} - -// WriteString adds more data to d. It always returns len(s), nil. -func (d *Digest) WriteString(s string) (n int, err error) { - return d.Write([]byte(s)) -} diff --git a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go b/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go deleted file mode 100644 index cf9d42a..0000000 --- a/vendor/github.com/cespare/xxhash/v2/xxhash_unsafe.go +++ /dev/null @@ -1,58 +0,0 @@ -//go:build !appengine -// +build !appengine - -// This file encapsulates usage of unsafe. -// xxhash_safe.go contains the safe implementations. - -package xxhash - -import ( - "unsafe" -) - -// In the future it's possible that compiler optimizations will make these -// XxxString functions unnecessary by realizing that calls such as -// Sum64([]byte(s)) don't need to copy s. See https://go.dev/issue/2205. -// If that happens, even if we keep these functions they can be replaced with -// the trivial safe code. - -// NOTE: The usual way of doing an unsafe string-to-[]byte conversion is: -// -// var b []byte -// bh := (*reflect.SliceHeader)(unsafe.Pointer(&b)) -// bh.Data = (*reflect.StringHeader)(unsafe.Pointer(&s)).Data -// bh.Len = len(s) -// bh.Cap = len(s) -// -// Unfortunately, as of Go 1.15.3 the inliner's cost model assigns a high enough -// weight to this sequence of expressions that any function that uses it will -// not be inlined. Instead, the functions below use a different unsafe -// conversion designed to minimize the inliner weight and allow both to be -// inlined. There is also a test (TestInlining) which verifies that these are -// inlined. -// -// See https://github.com/golang/go/issues/42739 for discussion. - -// Sum64String computes the 64-bit xxHash digest of s with a zero seed. -// It may be faster than Sum64([]byte(s)) by avoiding a copy. -func Sum64String(s string) uint64 { - b := *(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)})) - return Sum64(b) -} - -// WriteString adds more data to d. It always returns len(s), nil. -// It may be faster than Write([]byte(s)) by avoiding a copy. -func (d *Digest) WriteString(s string) (n int, err error) { - d.Write(*(*[]byte)(unsafe.Pointer(&sliceHeader{s, len(s)}))) - // d.Write always returns len(s), nil. - // Ignoring the return output and returning these fixed values buys a - // savings of 6 in the inliner's cost model. - return len(s), nil -} - -// sliceHeader is similar to reflect.SliceHeader, but it assumes that the layout -// of the first two words is the same as the layout of a string. -type sliceHeader struct { - s string - cap int -} diff --git a/vendor/github.com/dgryski/go-rendezvous/LICENSE b/vendor/github.com/dgryski/go-rendezvous/LICENSE deleted file mode 100644 index 22080f7..0000000 --- a/vendor/github.com/dgryski/go-rendezvous/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2017-2020 Damian Gryski - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN -THE SOFTWARE. diff --git a/vendor/github.com/dgryski/go-rendezvous/rdv.go b/vendor/github.com/dgryski/go-rendezvous/rdv.go deleted file mode 100644 index 7a6f820..0000000 --- a/vendor/github.com/dgryski/go-rendezvous/rdv.go +++ /dev/null @@ -1,79 +0,0 @@ -package rendezvous - -type Rendezvous struct { - nodes map[string]int - nstr []string - nhash []uint64 - hash Hasher -} - -type Hasher func(s string) uint64 - -func New(nodes []string, hash Hasher) *Rendezvous { - r := &Rendezvous{ - nodes: make(map[string]int, len(nodes)), - nstr: make([]string, len(nodes)), - nhash: make([]uint64, len(nodes)), - hash: hash, - } - - for i, n := range nodes { - r.nodes[n] = i - r.nstr[i] = n - r.nhash[i] = hash(n) - } - - return r -} - -func (r *Rendezvous) Lookup(k string) string { - // short-circuit if we're empty - if len(r.nodes) == 0 { - return "" - } - - khash := r.hash(k) - - var midx int - var mhash = xorshiftMult64(khash ^ r.nhash[0]) - - for i, nhash := range r.nhash[1:] { - if h := xorshiftMult64(khash ^ nhash); h > mhash { - midx = i + 1 - mhash = h - } - } - - return r.nstr[midx] -} - -func (r *Rendezvous) Add(node string) { - r.nodes[node] = len(r.nstr) - r.nstr = append(r.nstr, node) - r.nhash = append(r.nhash, r.hash(node)) -} - -func (r *Rendezvous) Remove(node string) { - // find index of node to remove - nidx := r.nodes[node] - - // remove from the slices - l := len(r.nstr) - r.nstr[nidx] = r.nstr[l] - r.nstr = r.nstr[:l] - - r.nhash[nidx] = r.nhash[l] - r.nhash = r.nhash[:l] - - // update the map - delete(r.nodes, node) - moved := r.nstr[nidx] - r.nodes[moved] = nidx -} - -func xorshiftMult64(x uint64) uint64 { - x ^= x >> 12 // a - x ^= x << 25 // b - x ^= x >> 27 // c - return x * 2685821657736338717 -} diff --git a/vendor/github.com/redis/go-redis/v9/.gitignore b/vendor/github.com/redis/go-redis/v9/.gitignore deleted file mode 100644 index 0d99709..0000000 --- a/vendor/github.com/redis/go-redis/v9/.gitignore +++ /dev/null @@ -1,11 +0,0 @@ -*.rdb -testdata/* -.idea/ -.DS_Store -*.tar.gz -*.dic -redis8tests.sh -coverage.txt -**/coverage.txt -.vscode -tmp/* diff --git a/vendor/github.com/redis/go-redis/v9/.golangci.yml b/vendor/github.com/redis/go-redis/v9/.golangci.yml deleted file mode 100644 index 872454f..0000000 --- a/vendor/github.com/redis/go-redis/v9/.golangci.yml +++ /dev/null @@ -1,34 +0,0 @@ -version: "2" -run: - timeout: 5m - tests: false -linters: - settings: - staticcheck: - checks: - - all - # Incorrect or missing package comment. - # https://staticcheck.dev/docs/checks/#ST1000 - - -ST1000 - # Omit embedded fields from selector expression. - # https://staticcheck.dev/docs/checks/#QF1008 - - -QF1008 - - -ST1003 - exclusions: - generated: lax - presets: - - comments - - common-false-positives - - legacy - - std-error-handling - paths: - - third_party$ - - builtin$ - - examples$ -formatters: - exclusions: - generated: lax - paths: - - third_party$ - - builtin$ - - examples$ diff --git a/vendor/github.com/redis/go-redis/v9/.prettierrc.yml b/vendor/github.com/redis/go-redis/v9/.prettierrc.yml deleted file mode 100644 index 8b7f044..0000000 --- a/vendor/github.com/redis/go-redis/v9/.prettierrc.yml +++ /dev/null @@ -1,4 +0,0 @@ -semi: false -singleQuote: true -proseWrap: always -printWidth: 100 diff --git a/vendor/github.com/redis/go-redis/v9/CHANGELOG.md b/vendor/github.com/redis/go-redis/v9/CHANGELOG.md deleted file mode 100644 index e1652b1..0000000 --- a/vendor/github.com/redis/go-redis/v9/CHANGELOG.md +++ /dev/null @@ -1,133 +0,0 @@ -## Unreleased - -### Changed - -* `go-redis` won't skip span creation if the parent spans is not recording. ([#2980](https://github.com/redis/go-redis/issues/2980)) - Users can use the OpenTelemetry sampler to control the sampling behavior. - For instance, you can use the `ParentBased(NeverSample())` sampler from `go.opentelemetry.io/otel/sdk/trace` to keep - a similar behavior (drop orphan spans) of `go-redis` as before. - -## [9.0.5](https://github.com/redis/go-redis/compare/v9.0.4...v9.0.5) (2023-05-29) - - -### Features - -* Add ACL LOG ([#2536](https://github.com/redis/go-redis/issues/2536)) ([31ba855](https://github.com/redis/go-redis/commit/31ba855ddebc38fbcc69a75d9d4fb769417cf602)) -* add field protocol to setupClusterQueryParams ([#2600](https://github.com/redis/go-redis/issues/2600)) ([840c25c](https://github.com/redis/go-redis/commit/840c25cb6f320501886a82a5e75f47b491e46fbe)) -* add protocol option ([#2598](https://github.com/redis/go-redis/issues/2598)) ([3917988](https://github.com/redis/go-redis/commit/391798880cfb915c4660f6c3ba63e0c1a459e2af)) - - - -## [9.0.4](https://github.com/redis/go-redis/compare/v9.0.3...v9.0.4) (2023-05-01) - - -### Bug Fixes - -* reader float parser ([#2513](https://github.com/redis/go-redis/issues/2513)) ([46f2450](https://github.com/redis/go-redis/commit/46f245075e6e3a8bd8471f9ca67ea95fd675e241)) - - -### Features - -* add client info command ([#2483](https://github.com/redis/go-redis/issues/2483)) ([b8c7317](https://github.com/redis/go-redis/commit/b8c7317cc6af444603731f7017c602347c0ba61e)) -* no longer verify HELLO error messages ([#2515](https://github.com/redis/go-redis/issues/2515)) ([7b4f217](https://github.com/redis/go-redis/commit/7b4f2179cb5dba3d3c6b0c6f10db52b837c912c8)) -* read the structure to increase the judgment of the omitempty op… ([#2529](https://github.com/redis/go-redis/issues/2529)) ([37c057b](https://github.com/redis/go-redis/commit/37c057b8e597c5e8a0e372337f6a8ad27f6030af)) - - - -## [9.0.3](https://github.com/redis/go-redis/compare/v9.0.2...v9.0.3) (2023-04-02) - -### New Features - -- feat(scan): scan time.Time sets the default decoding (#2413) -- Add support for CLUSTER LINKS command (#2504) -- Add support for acl dryrun command (#2502) -- Add support for COMMAND GETKEYS & COMMAND GETKEYSANDFLAGS (#2500) -- Add support for LCS Command (#2480) -- Add support for BZMPOP (#2456) -- Adding support for ZMPOP command (#2408) -- Add support for LMPOP (#2440) -- feat: remove pool unused fields (#2438) -- Expiretime and PExpireTime (#2426) -- Implement `FUNCTION` group of commands (#2475) -- feat(zadd): add ZAddLT and ZAddGT (#2429) -- Add: Support for COMMAND LIST command (#2491) -- Add support for BLMPOP (#2442) -- feat: check pipeline.Do to prevent confusion with Exec (#2517) -- Function stats, function kill, fcall and fcall_ro (#2486) -- feat: Add support for CLUSTER SHARDS command (#2507) -- feat(cmd): support for adding byte,bit parameters to the bitpos command (#2498) - -### Fixed - -- fix: eval api cmd.SetFirstKeyPos (#2501) -- fix: limit the number of connections created (#2441) -- fixed #2462 v9 continue support dragonfly, it's Hello command return "NOAUTH Authentication required" error (#2479) -- Fix for internal/hscan/structmap.go:89:23: undefined: reflect.Pointer (#2458) -- fix: group lag can be null (#2448) - -### Maintenance - -- Updating to the latest version of redis (#2508) -- Allowing for running tests on a port other than the fixed 6380 (#2466) -- redis 7.0.8 in tests (#2450) -- docs: Update redisotel example for v9 (#2425) -- chore: update go mod, Upgrade golang.org/x/net version to 0.7.0 (#2476) -- chore: add Chinese translation (#2436) -- chore(deps): bump github.com/bsm/gomega from 1.20.0 to 1.26.0 (#2421) -- chore(deps): bump github.com/bsm/ginkgo/v2 from 2.5.0 to 2.7.0 (#2420) -- chore(deps): bump actions/setup-go from 3 to 4 (#2495) -- docs: add instructions for the HSet api (#2503) -- docs: add reading lag field comment (#2451) -- test: update go mod before testing(go mod tidy) (#2423) -- docs: fix comment typo (#2505) -- test: remove testify (#2463) -- refactor: change ListElementCmd to KeyValuesCmd. (#2443) -- fix(appendArg): appendArg case special type (#2489) - -## [9.0.2](https://github.com/redis/go-redis/compare/v9.0.1...v9.0.2) (2023-02-01) - -### Features - -* upgrade OpenTelemetry, use the new metrics API. ([#2410](https://github.com/redis/go-redis/issues/2410)) ([e29e42c](https://github.com/redis/go-redis/commit/e29e42cde2755ab910d04185025dc43ce6f59c65)) - -## v9 2023-01-30 - -### Breaking - -- Changed Pipelines to not be thread-safe any more. - -### Added - -- Added support for [RESP3](https://github.com/antirez/RESP3/blob/master/spec.md) protocol. It was - contributed by @monkey92t who has done the majority of work in this release. -- Added `ContextTimeoutEnabled` option that controls whether the client respects context timeouts - and deadlines. See - [Redis Timeouts](https://redis.uptrace.dev/guide/go-redis-debugging.html#timeouts) for details. -- Added `ParseClusterURL` to parse URLs into `ClusterOptions`, for example, - `redis://user:password@localhost:6789?dial_timeout=3&read_timeout=6s&addr=localhost:6790&addr=localhost:6791`. -- Added metrics instrumentation using `redisotel.IstrumentMetrics`. See - [documentation](https://redis.uptrace.dev/guide/go-redis-monitoring.html) -- Added `redis.HasErrorPrefix` to help working with errors. - -### Changed - -- Removed asynchronous cancellation based on the context timeout. It was racy in v8 and is - completely gone in v9. -- Reworked hook interface and added `DialHook`. -- Replaced `redisotel.NewTracingHook` with `redisotel.InstrumentTracing`. See - [example](example/otel) and - [documentation](https://redis.uptrace.dev/guide/go-redis-monitoring.html). -- Replaced `*redis.Z` with `redis.Z` since it is small enough to be passed as value without making - an allocation. -- Renamed the option `MaxConnAge` to `ConnMaxLifetime`. -- Renamed the option `IdleTimeout` to `ConnMaxIdleTime`. -- Removed connection reaper in favor of `MaxIdleConns`. -- Removed `WithContext` since `context.Context` can be passed directly as an arg. -- Removed `Pipeline.Close` since there is no real need to explicitly manage pipeline resources and - it can be safely reused via `sync.Pool` etc. `Pipeline.Discard` is still available if you want to - reset commands for some reason. - -### Fixed - -- Improved and fixed pipeline retries. -- As usually, added support for more commands and fixed some bugs. diff --git a/vendor/github.com/redis/go-redis/v9/CONTRIBUTING.md b/vendor/github.com/redis/go-redis/v9/CONTRIBUTING.md deleted file mode 100644 index 7228a4a..0000000 --- a/vendor/github.com/redis/go-redis/v9/CONTRIBUTING.md +++ /dev/null @@ -1,118 +0,0 @@ -# Contributing - -## Introduction - -We appreciate your interest in considering contributing to go-redis. -Community contributions mean a lot to us. - -## Contributions we need - -You may already know how you'd like to contribute, whether it's a fix for a bug you -encountered, or a new feature your team wants to use. - -If you don't know where to start, consider improving -documentation, bug triaging, and writing tutorials are all examples of -helpful contributions that mean less work for you. - -## Your First Contribution - -Unsure where to begin contributing? You can start by looking through -[help-wanted -issues](https://github.com/redis/go-redis/issues?q=is%3Aopen+is%3Aissue+label%3ahelp-wanted). - -Never contributed to open source before? Here are a couple of friendly -tutorials: - -- -- - -## Getting Started - -Here's how to get started with your code contribution: - -1. Create your own fork of go-redis -2. Do the changes in your fork -3. If you need a development environment, run `make docker.start`. - -> Note: this clones and builds the docker containers specified in `docker-compose.yml`, to understand more about -> the infrastructure that will be started you can check the `docker-compose.yml`. You also have the possiblity -> to specify the redis image that will be pulled with the env variable `CLIENT_LIBS_TEST_IMAGE`. -> By default the docker image that will be pulled and started is `redislabs/client-libs-test:rs-7.4.0-v2`. -> If you want to test with newer Redis version, using a newer version of `redislabs/client-libs-test` should work out of the box. - -4. While developing, make sure the tests pass by running `make test` (if you have the docker containers running, `make test.ci` may be sufficient). -> Note: `make test` will try to start all containers, run the tests with `make test.ci` and then stop all containers. -5. If you like the change and think the project could use it, send a - pull request - -To see what else is part of the automation, run `invoke -l` - - -## Testing - -### Setting up Docker -To run the tests, you need to have Docker installed and running. If you are using a host OS that does not support -docker host networks out of the box (e.g. Windows, OSX), you need to set up a docker desktop and enable docker host networks. - -### Running tests -Call `make test` to run all tests. - -Continuous Integration uses these same wrappers to run all of these -tests against multiple versions of redis. Feel free to test your -changes against all the go versions supported, as declared by the -[build.yml](./.github/workflows/build.yml) file. - -### Troubleshooting - -If you get any errors when running `make test`, make sure -that you are using supported versions of Docker and go. - -## How to Report a Bug - -### Security Vulnerabilities - -**NOTE**: If you find a security vulnerability, do NOT open an issue. -Email [Redis Open Source ()](mailto:oss@redis.com) instead. - -In order to determine whether you are dealing with a security issue, ask -yourself these two questions: - -- Can I access something that's not mine, or something I shouldn't - have access to? -- Can I disable something for other people? - -If the answer to either of those two questions are *yes*, then you're -probably dealing with a security issue. Note that even if you answer -*no* to both questions, you may still be dealing with a security -issue, so if you're unsure, just email [us](mailto:oss@redis.com). - -### Everything Else - -When filing an issue, make sure to answer these five questions: - -1. What version of go-redis are you using? -2. What version of redis are you using? -3. What did you do? -4. What did you expect to see? -5. What did you see instead? - -## Suggest a feature or enhancement - -If you'd like to contribute a new feature, make sure you check our -issue list to see if someone has already proposed it. Work may already -be underway on the feature you want or we may have rejected a -feature like it already. - -If you don't see anything, open a new issue that describes the feature -you would like and how it should work. - -## Code review process - -The core team regularly looks at pull requests. We will provide -feedback as soon as possible. After receiving our feedback, please respond -within two weeks. After that time, we may close your PR if it isn't -showing any activity. - -## Support - -Maintainers can provide limited support to contributors on discord: https://discord.gg/W4txy5AeKM diff --git a/vendor/github.com/redis/go-redis/v9/LICENSE b/vendor/github.com/redis/go-redis/v9/LICENSE deleted file mode 100644 index f4967db..0000000 --- a/vendor/github.com/redis/go-redis/v9/LICENSE +++ /dev/null @@ -1,25 +0,0 @@ -Copyright (c) 2013 The github.com/redis/go-redis Authors. -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/github.com/redis/go-redis/v9/Makefile b/vendor/github.com/redis/go-redis/v9/Makefile deleted file mode 100644 index 655f16f..0000000 --- a/vendor/github.com/redis/go-redis/v9/Makefile +++ /dev/null @@ -1,67 +0,0 @@ -GO_MOD_DIRS := $(shell find . -type f -name 'go.mod' -exec dirname {} \; | sort) - -docker.start: - docker compose --profile all up -d --quiet-pull - -docker.stop: - docker compose --profile all down - -test: - $(MAKE) docker.start - @if [ -z "$(REDIS_VERSION)" ]; then \ - echo "REDIS_VERSION not set, running all tests"; \ - $(MAKE) test.ci; \ - else \ - MAJOR_VERSION=$$(echo "$(REDIS_VERSION)" | cut -d. -f1); \ - if [ "$$MAJOR_VERSION" -ge 8 ]; then \ - echo "REDIS_VERSION $(REDIS_VERSION) >= 8, running all tests"; \ - $(MAKE) test.ci; \ - else \ - echo "REDIS_VERSION $(REDIS_VERSION) < 8, skipping vector_sets tests"; \ - $(MAKE) test.ci.skip-vectorsets; \ - fi; \ - fi - $(MAKE) docker.stop - -test.ci: - set -e; for dir in $(GO_MOD_DIRS); do \ - echo "go test in $${dir}"; \ - (cd "$${dir}" && \ - go mod tidy -compat=1.18 && \ - go vet && \ - go test -v -coverprofile=coverage.txt -covermode=atomic ./... -race -skip Example); \ - done - cd internal/customvet && go build . - go vet -vettool ./internal/customvet/customvet - -test.ci.skip-vectorsets: - set -e; for dir in $(GO_MOD_DIRS); do \ - echo "go test in $${dir} (skipping vector sets)"; \ - (cd "$${dir}" && \ - go mod tidy -compat=1.18 && \ - go vet && \ - go test -v -coverprofile=coverage.txt -covermode=atomic ./... -race \ - -run '^(?!.*(?:VectorSet|vectorset|ExampleClient_vectorset)).*$$' -skip Example); \ - done - cd internal/customvet && go build . - go vet -vettool ./internal/customvet/customvet - -bench: - go test ./... -test.run=NONE -test.bench=. -test.benchmem -skip Example - -.PHONY: all test test.ci test.ci.skip-vectorsets bench fmt - -build: - go build . - -fmt: - gofumpt -w ./ - goimports -w -local github.com/redis/go-redis ./ - -go_mod_tidy: - set -e; for dir in $(GO_MOD_DIRS); do \ - echo "go mod tidy in $${dir}"; \ - (cd "$${dir}" && \ - go get -u ./... && \ - go mod tidy -compat=1.18); \ - done diff --git a/vendor/github.com/redis/go-redis/v9/README.md b/vendor/github.com/redis/go-redis/v9/README.md deleted file mode 100644 index c37a52e..0000000 --- a/vendor/github.com/redis/go-redis/v9/README.md +++ /dev/null @@ -1,458 +0,0 @@ -# Redis client for Go - -[![build workflow](https://github.com/redis/go-redis/actions/workflows/build.yml/badge.svg)](https://github.com/redis/go-redis/actions) -[![PkgGoDev](https://pkg.go.dev/badge/github.com/redis/go-redis/v9)](https://pkg.go.dev/github.com/redis/go-redis/v9?tab=doc) -[![Documentation](https://img.shields.io/badge/redis-documentation-informational)](https://redis.uptrace.dev/) -[![Go Report Card](https://goreportcard.com/badge/github.com/redis/go-redis/v9)](https://goreportcard.com/report/github.com/redis/go-redis/v9) -[![codecov](https://codecov.io/github/redis/go-redis/graph/badge.svg?token=tsrCZKuSSw)](https://codecov.io/github/redis/go-redis) - -[![Discord](https://img.shields.io/discord/697882427875393627.svg?style=social&logo=discord)](https://discord.gg/W4txy5AeKM) -[![Twitch](https://img.shields.io/twitch/status/redisinc?style=social)](https://www.twitch.tv/redisinc) -[![YouTube](https://img.shields.io/youtube/channel/views/UCD78lHSwYqMlyetR0_P4Vig?style=social)](https://www.youtube.com/redisinc) -[![Twitter](https://img.shields.io/twitter/follow/redisinc?style=social)](https://twitter.com/redisinc) -[![Stack Exchange questions](https://img.shields.io/stackexchange/stackoverflow/t/go-redis?style=social&logo=stackoverflow&label=Stackoverflow)](https://stackoverflow.com/questions/tagged/go-redis) - -> go-redis is the official Redis client library for the Go programming language. It offers a straightforward interface for interacting with Redis servers. - -## Supported versions - -In `go-redis` we are aiming to support the last three releases of Redis. Currently, this means we do support: -- [Redis 7.2](https://raw.githubusercontent.com/redis/redis/7.2/00-RELEASENOTES) - using Redis Stack 7.2 for modules support -- [Redis 7.4](https://raw.githubusercontent.com/redis/redis/7.4/00-RELEASENOTES) - using Redis Stack 7.4 for modules support -- [Redis 8.0](https://raw.githubusercontent.com/redis/redis/8.0/00-RELEASENOTES) - using Redis CE 8.0 where modules are included - -Although the `go.mod` states it requires at minimum `go 1.18`, our CI is configured to run the tests against all three -versions of Redis and latest two versions of Go ([1.23](https://go.dev/doc/devel/release#go1.23.0), -[1.24](https://go.dev/doc/devel/release#go1.24.0)). We observe that some modules related test may not pass with -Redis Stack 7.2 and some commands are changed with Redis CE 8.0. -Please do refer to the documentation and the tests if you experience any issues. We do plan to update the go version -in the `go.mod` to `go 1.24` in one of the next releases. - -## How do I Redis? - -[Learn for free at Redis University](https://university.redis.com/) - -[Build faster with the Redis Launchpad](https://launchpad.redis.com/) - -[Try the Redis Cloud](https://redis.com/try-free/) - -[Dive in developer tutorials](https://developer.redis.com/) - -[Join the Redis community](https://redis.com/community/) - -[Work at Redis](https://redis.com/company/careers/jobs/) - -## Documentation - -- [English](https://redis.uptrace.dev) -- [简体中文](https://redis.uptrace.dev/zh/) - -## Resources - -- [Discussions](https://github.com/redis/go-redis/discussions) -- [Chat](https://discord.gg/W4txy5AeKM) -- [Reference](https://pkg.go.dev/github.com/redis/go-redis/v9) -- [Examples](https://pkg.go.dev/github.com/redis/go-redis/v9#pkg-examples) - -## Ecosystem - -- [Redis Mock](https://github.com/go-redis/redismock) -- [Distributed Locks](https://github.com/bsm/redislock) -- [Redis Cache](https://github.com/go-redis/cache) -- [Rate limiting](https://github.com/go-redis/redis_rate) - -This client also works with [Kvrocks](https://github.com/apache/incubator-kvrocks), a distributed -key value NoSQL database that uses RocksDB as storage engine and is compatible with Redis protocol. - -## Features - -- Redis commands except QUIT and SYNC. -- Automatic connection pooling. -- [StreamingCredentialsProvider (e.g. entra id, oauth)](#1-streaming-credentials-provider-highest-priority) (experimental) -- [Pub/Sub](https://redis.uptrace.dev/guide/go-redis-pubsub.html). -- [Pipelines and transactions](https://redis.uptrace.dev/guide/go-redis-pipelines.html). -- [Scripting](https://redis.uptrace.dev/guide/lua-scripting.html). -- [Redis Sentinel](https://redis.uptrace.dev/guide/go-redis-sentinel.html). -- [Redis Cluster](https://redis.uptrace.dev/guide/go-redis-cluster.html). -- [Redis Ring](https://redis.uptrace.dev/guide/ring.html). -- [Redis Performance Monitoring](https://redis.uptrace.dev/guide/redis-performance-monitoring.html). -- [Redis Probabilistic [RedisStack]](https://redis.io/docs/data-types/probabilistic/) - -## Installation - -go-redis supports 2 last Go versions and requires a Go version with -[modules](https://github.com/golang/go/wiki/Modules) support. So make sure to initialize a Go -module: - -```shell -go mod init github.com/my/repo -``` - -Then install go-redis/**v9**: - -```shell -go get github.com/redis/go-redis/v9 -``` - -## Quickstart - -```go -import ( - "context" - "fmt" - - "github.com/redis/go-redis/v9" -) - -var ctx = context.Background() - -func ExampleClient() { - rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", // no password set - DB: 0, // use default DB - }) - - err := rdb.Set(ctx, "key", "value", 0).Err() - if err != nil { - panic(err) - } - - val, err := rdb.Get(ctx, "key").Result() - if err != nil { - panic(err) - } - fmt.Println("key", val) - - val2, err := rdb.Get(ctx, "key2").Result() - if err == redis.Nil { - fmt.Println("key2 does not exist") - } else if err != nil { - panic(err) - } else { - fmt.Println("key2", val2) - } - // Output: key value - // key2 does not exist -} -``` - -### Authentication - -The Redis client supports multiple ways to provide authentication credentials, with a clear priority order. Here are the available options: - -#### 1. Streaming Credentials Provider (Highest Priority) - Experimental feature - -The streaming credentials provider allows for dynamic credential updates during the connection lifetime. This is particularly useful for managed identity services and token-based authentication. - -```go -type StreamingCredentialsProvider interface { - Subscribe(listener CredentialsListener) (Credentials, UnsubscribeFunc, error) -} - -type CredentialsListener interface { - OnNext(credentials Credentials) // Called when credentials are updated - OnError(err error) // Called when an error occurs -} - -type Credentials interface { - BasicAuth() (username string, password string) - RawCredentials() string -} -``` - -Example usage: -```go -rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - StreamingCredentialsProvider: &MyCredentialsProvider{}, -}) -``` - -**Note:** The streaming credentials provider can be used with [go-redis-entraid](https://github.com/redis/go-redis-entraid) to enable Entra ID (formerly Azure AD) authentication. This allows for seamless integration with Azure's managed identity services and token-based authentication. - -Example with Entra ID: -```go -import ( - "github.com/redis/go-redis/v9" - "github.com/redis/go-redis-entraid" -) - -// Create an Entra ID credentials provider -provider := entraid.NewDefaultAzureIdentityProvider() - -// Configure Redis client with Entra ID authentication -rdb := redis.NewClient(&redis.Options{ - Addr: "your-redis-server.redis.cache.windows.net:6380", - StreamingCredentialsProvider: provider, - TLSConfig: &tls.Config{ - MinVersion: tls.VersionTLS12, - }, -}) -``` - -#### 2. Context-based Credentials Provider - -The context-based provider allows credentials to be determined at the time of each operation, using the context. - -```go -rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - CredentialsProviderContext: func(ctx context.Context) (string, string, error) { - // Return username, password, and any error - return "user", "pass", nil - }, -}) -``` - -#### 3. Regular Credentials Provider - -A simple function-based provider that returns static credentials. - -```go -rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - CredentialsProvider: func() (string, string) { - // Return username and password - return "user", "pass" - }, -}) -``` - -#### 4. Username/Password Fields (Lowest Priority) - -The most basic way to provide credentials is through the `Username` and `Password` fields in the options. - -```go -rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Username: "user", - Password: "pass", -}) -``` - -#### Priority Order - -The client will use credentials in the following priority order: -1. Streaming Credentials Provider (if set) -2. Context-based Credentials Provider (if set) -3. Regular Credentials Provider (if set) -4. Username/Password fields (if set) - -If none of these are set, the client will attempt to connect without authentication. - -### Protocol Version - -The client supports both RESP2 and RESP3 protocols. You can specify the protocol version in the options: - -```go -rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", // no password set - DB: 0, // use default DB - Protocol: 3, // specify 2 for RESP 2 or 3 for RESP 3 -}) -``` - -### Connecting via a redis url - -go-redis also supports connecting via the -[redis uri specification](https://github.com/redis/redis-specifications/tree/master/uri/redis.txt). -The example below demonstrates how the connection can easily be configured using a string, adhering -to this specification. - -```go -import ( - "github.com/redis/go-redis/v9" -) - -func ExampleClient() *redis.Client { - url := "redis://user:password@localhost:6379/0?protocol=3" - opts, err := redis.ParseURL(url) - if err != nil { - panic(err) - } - - return redis.NewClient(opts) -} - -``` - -### Instrument with OpenTelemetry - -```go -import ( - "github.com/redis/go-redis/v9" - "github.com/redis/go-redis/extra/redisotel/v9" - "errors" -) - -func main() { - ... - rdb := redis.NewClient(&redis.Options{...}) - - if err := errors.Join(redisotel.InstrumentTracing(rdb), redisotel.InstrumentMetrics(rdb)); err != nil { - log.Fatal(err) - } -``` - - -### Advanced Configuration - -go-redis supports extending the client identification phase to allow projects to send their own custom client identification. - -#### Default Client Identification - -By default, go-redis automatically sends the client library name and version during the connection process. This feature is available in redis-server as of version 7.2. As a result, the command is "fire and forget", meaning it should fail silently, in the case that the redis server does not support this feature. - -#### Disabling Identity Verification - -When connection identity verification is not required or needs to be explicitly disabled, a `DisableIdentity` configuration option exists. -Initially there was a typo and the option was named `DisableIndentity` instead of `DisableIdentity`. The misspelled option is marked as Deprecated and will be removed in V10 of this library. -Although both options will work at the moment, the correct option is `DisableIdentity`. The deprecated option will be removed in V10 of this library, so please use the correct option name to avoid any issues. - -To disable verification, set the `DisableIdentity` option to `true` in the Redis client options: - -```go -rdb := redis.NewClient(&redis.Options{ - Addr: "localhost:6379", - Password: "", - DB: 0, - DisableIdentity: true, // Disable set-info on connect -}) -``` - -#### Unstable RESP3 Structures for RediSearch Commands -When integrating Redis with application functionalities using RESP3, it's important to note that some response structures aren't final yet. This is especially true for more complex structures like search and query results. We recommend using RESP2 when using the search and query capabilities, but we plan to stabilize the RESP3-based API-s in the coming versions. You can find more guidance in the upcoming release notes. - -To enable unstable RESP3, set the option in your client configuration: - -```go -redis.NewClient(&redis.Options{ - UnstableResp3: true, - }) -``` -**Note:** When UnstableResp3 mode is enabled, it's necessary to use RawResult() and RawVal() to retrieve a raw data. - Since, raw response is the only option for unstable search commands Val() and Result() calls wouldn't have any affect on them: - -```go -res1, err := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{}).RawResult() -val1 := client.FTSearchWithArgs(ctx, "txt", "foo bar", &redis.FTSearchOptions{}).RawVal() -``` - -#### Redis-Search Default Dialect - -In the Redis-Search module, **the default dialect is 2**. If needed, you can explicitly specify a different dialect using the appropriate configuration in your queries. - -**Important**: Be aware that the query dialect may impact the results returned. If needed, you can revert to a different dialect version by passing the desired dialect in the arguments of the command you want to execute. -For example: -``` - res2, err := rdb.FTSearchWithArgs(ctx, - "idx:bicycle", - "@pickup_zone:[CONTAINS $bike]", - &redis.FTSearchOptions{ - Params: map[string]interface{}{ - "bike": "POINT(-0.1278 51.5074)", - }, - DialectVersion: 3, - }, - ).Result() -``` -You can find further details in the [query dialect documentation](https://redis.io/docs/latest/develop/interact/search-and-query/advanced-concepts/dialects/). - -## Contributing -We welcome contributions to the go-redis library! If you have a bug fix, feature request, or improvement, please open an issue or pull request on GitHub. -We appreciate your help in making go-redis better for everyone. -If you are interested in contributing to the go-redis library, please check out our [contributing guidelines](CONTRIBUTING.md) for more information on how to get started. - -## Look and feel - -Some corner cases: - -```go -// SET key value EX 10 NX -set, err := rdb.SetNX(ctx, "key", "value", 10*time.Second).Result() - -// SET key value keepttl NX -set, err := rdb.SetNX(ctx, "key", "value", redis.KeepTTL).Result() - -// SORT list LIMIT 0 2 ASC -vals, err := rdb.Sort(ctx, "list", &redis.Sort{Offset: 0, Count: 2, Order: "ASC"}).Result() - -// ZRANGEBYSCORE zset -inf +inf WITHSCORES LIMIT 0 2 -vals, err := rdb.ZRangeByScoreWithScores(ctx, "zset", &redis.ZRangeBy{ - Min: "-inf", - Max: "+inf", - Offset: 0, - Count: 2, -}).Result() - -// ZINTERSTORE out 2 zset1 zset2 WEIGHTS 2 3 AGGREGATE SUM -vals, err := rdb.ZInterStore(ctx, "out", &redis.ZStore{ - Keys: []string{"zset1", "zset2"}, - Weights: []int64{2, 3} -}).Result() - -// EVAL "return {KEYS[1],ARGV[1]}" 1 "key" "hello" -vals, err := rdb.Eval(ctx, "return {KEYS[1],ARGV[1]}", []string{"key"}, "hello").Result() - -// custom command -res, err := rdb.Do(ctx, "set", "key", "value").Result() -``` - -## Run the test - -go-redis will start a redis-server and run the test cases. - -The paths of redis-server bin file and redis config file are defined in `main_test.go`: - -```go -var ( - redisServerBin, _ = filepath.Abs(filepath.Join("testdata", "redis", "src", "redis-server")) - redisServerConf, _ = filepath.Abs(filepath.Join("testdata", "redis", "redis.conf")) -) -``` - -For local testing, you can change the variables to refer to your local files, or create a soft link -to the corresponding folder for redis-server and copy the config file to `testdata/redis/`: - -```shell -ln -s /usr/bin/redis-server ./go-redis/testdata/redis/src -cp ./go-redis/testdata/redis.conf ./go-redis/testdata/redis/ -``` - -Lastly, run: - -```shell -go test -``` - -Another option is to run your specific tests with an already running redis. The example below, tests -against a redis running on port 9999.: - -```shell -REDIS_PORT=9999 go test -``` - -## See also - -- [Golang ORM](https://bun.uptrace.dev) for PostgreSQL, MySQL, MSSQL, and SQLite -- [Golang PostgreSQL](https://bun.uptrace.dev/postgres/) -- [Golang HTTP router](https://bunrouter.uptrace.dev/) -- [Golang ClickHouse ORM](https://github.com/uptrace/go-clickhouse) - -## Contributors - -> The go-redis project was originally initiated by :star: [**uptrace/uptrace**](https://github.com/uptrace/uptrace). -> Uptrace is an open-source APM tool that supports distributed tracing, metrics, and logs. You can -> use it to monitor applications and set up automatic alerts to receive notifications via email, -> Slack, Telegram, and others. -> -> See [OpenTelemetry](https://github.com/redis/go-redis/tree/master/example/otel) example which -> demonstrates how you can use Uptrace to monitor go-redis. - -Thanks to all the people who already contributed! - - - - diff --git a/vendor/github.com/redis/go-redis/v9/RELEASE-NOTES.md b/vendor/github.com/redis/go-redis/v9/RELEASE-NOTES.md deleted file mode 100644 index 6475490..0000000 --- a/vendor/github.com/redis/go-redis/v9/RELEASE-NOTES.md +++ /dev/null @@ -1,201 +0,0 @@ -# Release Notes - -# 9.11.0 (2025-06-24) - -## 🚀 Highlights - -Fixes TxPipeline to work correctly in cluster scenarios, allowing execution of commands -only in the same slot. - -# Changes - -## 🚀 New Features - -- Set cluster slot for `scan` commands, rather than random ([#2623](https://github.com/redis/go-redis/pull/2623)) -- Add CredentialsProvider field to UniversalOptions ([#2927](https://github.com/redis/go-redis/pull/2927)) -- feat(redisotel): add WithCallerEnabled option ([#3415](https://github.com/redis/go-redis/pull/3415)) - -## 🐛 Bug Fixes - -- fix(txpipeline): keyless commands should take the slot of the keyed ([#3411](https://github.com/redis/go-redis/pull/3411)) -- fix(loading): cache the loaded flag for slave nodes ([#3410](https://github.com/redis/go-redis/pull/3410)) -- fix(txpipeline): should return error on multi/exec on multiple slots ([#3408](https://github.com/redis/go-redis/pull/3408)) -- fix: check if the shard exists to avoid returning nil ([#3396](https://github.com/redis/go-redis/pull/3396)) - -## 🧰 Maintenance - -- feat: optimize connection pool waitTurn ([#3412](https://github.com/redis/go-redis/pull/3412)) -- chore(ci): update CI redis builds ([#3407](https://github.com/redis/go-redis/pull/3407)) -- chore: remove a redundant method from `Ring`, `Client` and `ClusterClient` ([#3401](https://github.com/redis/go-redis/pull/3401)) -- test: refactor TestBasicCredentials using table-driven tests ([#3406](https://github.com/redis/go-redis/pull/3406)) -- perf: reduce unnecessary memory allocation operations ([#3399](https://github.com/redis/go-redis/pull/3399)) -- fix: insert entry during iterating over a map ([#3398](https://github.com/redis/go-redis/pull/3398)) -- DOC-5229 probabilistic data type examples ([#3413](https://github.com/redis/go-redis/pull/3413)) -- chore(deps): bump rojopolis/spellcheck-github-actions from 0.49.0 to 0.51.0 ([#3414](https://github.com/redis/go-redis/pull/3414)) - -## Contributors -We'd like to thank all the contributors who worked on this release! - -[@andy-stark-redis](https://github.com/andy-stark-redis), [@boekkooi-impossiblecloud](https://github.com/boekkooi-impossiblecloud), [@cxljs](https://github.com/cxljs), [@dcherubini](https://github.com/dcherubini), [@dependabot[bot]](https://github.com/apps/dependabot), [@iamamirsalehi](https://github.com/iamamirsalehi), [@ndyakov](https://github.com/ndyakov), [@pete-woods](https://github.com/pete-woods), [@twz915](https://github.com/twz915) and [dependabot[bot]](https://github.com/apps/dependabot) - -# 9.10.0 (2025-06-06) - -## 🚀 Highlights - -`go-redis` now supports [vector sets](https://redis.io/docs/latest/develop/data-types/vector-sets/). This data type is marked -as "in preview" in Redis and its support in `go-redis` is marked as experimental. You can find examples in the documentation and -in the `doctests` folder. - -# Changes - -## 🚀 New Features - -- feat: support vectorset ([#3375](https://github.com/redis/go-redis/pull/3375)) - -## 🧰 Maintenance - -- Add the missing NewFloatSliceResult for testing ([#3393](https://github.com/redis/go-redis/pull/3393)) -- DOC-5078 vector set examples ([#3394](https://github.com/redis/go-redis/pull/3394)) - -## Contributors -We'd like to thank all the contributors who worked on this release! - -[@AndBobsYourUncle](https://github.com/AndBobsYourUncle), [@andy-stark-redis](https://github.com/andy-stark-redis), [@fukua95](https://github.com/fukua95) and [@ndyakov](https://github.com/ndyakov) - - - -# 9.9.0 (2025-05-27) - -## 🚀 Highlights -- **Token-based Authentication**: Added `StreamingCredentialsProvider` for dynamic credential updates (experimental) - - Can be used with [go-redis-entraid](https://github.com/redis/go-redis-entraid) for Azure AD authentication -- **Connection Statistics**: Added connection waiting statistics for better monitoring -- **Failover Improvements**: Added `ParseFailoverURL` for easier failover configuration -- **Ring Client Enhancements**: Added shard access methods for better Pub/Sub management - -## ✨ New Features -- Added `StreamingCredentialsProvider` for token-based authentication ([#3320](https://github.com/redis/go-redis/pull/3320)) - - Supports dynamic credential updates - - Includes connection close hooks - - Note: Currently marked as experimental -- Added `ParseFailoverURL` for parsing failover URLs ([#3362](https://github.com/redis/go-redis/pull/3362)) -- Added connection waiting statistics ([#2804](https://github.com/redis/go-redis/pull/2804)) -- Added new utility functions: - - `ParseFloat` and `MustParseFloat` in public utils package ([#3371](https://github.com/redis/go-redis/pull/3371)) - - Unit tests for `Atoi`, `ParseInt`, `ParseUint`, and `ParseFloat` ([#3377](https://github.com/redis/go-redis/pull/3377)) -- Added Ring client shard access methods: - - `GetShardClients()` to retrieve all active shard clients - - `GetShardClientForKey(key string)` to get the shard client for a specific key ([#3388](https://github.com/redis/go-redis/pull/3388)) - -## 🐛 Bug Fixes -- Fixed routing reads to loading slave nodes ([#3370](https://github.com/redis/go-redis/pull/3370)) -- Added support for nil lag in XINFO GROUPS ([#3369](https://github.com/redis/go-redis/pull/3369)) -- Fixed pool acquisition timeout issues ([#3381](https://github.com/redis/go-redis/pull/3381)) -- Optimized unnecessary copy operations ([#3376](https://github.com/redis/go-redis/pull/3376)) - -## 📚 Documentation -- Updated documentation for XINFO GROUPS with nil lag support ([#3369](https://github.com/redis/go-redis/pull/3369)) -- Added package-level comments for new features - -## ⚡ Performance and Reliability -- Optimized `ReplaceSpaces` function ([#3383](https://github.com/redis/go-redis/pull/3383)) -- Set default value for `Options.Protocol` in `init()` ([#3387](https://github.com/redis/go-redis/pull/3387)) -- Exported pool errors for public consumption ([#3380](https://github.com/redis/go-redis/pull/3380)) - -## 🔧 Dependencies and Infrastructure -- Updated Redis CI to version 8.0.1 ([#3372](https://github.com/redis/go-redis/pull/3372)) -- Updated spellcheck GitHub Actions ([#3389](https://github.com/redis/go-redis/pull/3389)) -- Removed unused parameters ([#3382](https://github.com/redis/go-redis/pull/3382), [#3384](https://github.com/redis/go-redis/pull/3384)) - -## 🧪 Testing -- Added unit tests for pool acquisition timeout ([#3381](https://github.com/redis/go-redis/pull/3381)) -- Added unit tests for utility functions ([#3377](https://github.com/redis/go-redis/pull/3377)) - -## 👥 Contributors - -We would like to thank all the contributors who made this release possible: - -[@ndyakov](https://github.com/ndyakov), [@ofekshenawa](https://github.com/ofekshenawa), [@LINKIWI](https://github.com/LINKIWI), [@iamamirsalehi](https://github.com/iamamirsalehi), [@fukua95](https://github.com/fukua95), [@lzakharov](https://github.com/lzakharov), [@DengY11](https://github.com/DengY11) - -## 📝 Changelog - -For a complete list of changes, see the [full changelog](https://github.com/redis/go-redis/compare/v9.8.0...v9.9.0). - -# 9.8.0 (2025-04-30) - -## 🚀 Highlights -- **Redis 8 Support**: Full compatibility with Redis 8.0, including testing and CI integration -- **Enhanced Hash Operations**: Added support for new hash commands (`HGETDEL`, `HGETEX`, `HSETEX`) and `HSTRLEN` command -- **Search Improvements**: Enabled Search DIALECT 2 by default and added `CountOnly` argument for `FT.Search` - -## ✨ New Features -- Added support for new hash commands: `HGETDEL`, `HGETEX`, `HSETEX` ([#3305](https://github.com/redis/go-redis/pull/3305)) -- Added `HSTRLEN` command for hash operations ([#2843](https://github.com/redis/go-redis/pull/2843)) -- Added `Do` method for raw query by single connection from `pool.Conn()` ([#3182](https://github.com/redis/go-redis/pull/3182)) -- Prevent false-positive marshaling by treating zero time.Time as empty in isEmptyValue ([#3273](https://github.com/redis/go-redis/pull/3273)) -- Added FailoverClusterClient support for Universal client ([#2794](https://github.com/redis/go-redis/pull/2794)) -- Added support for cluster mode with `IsClusterMode` config parameter ([#3255](https://github.com/redis/go-redis/pull/3255)) -- Added client name support in `HELLO` RESP handshake ([#3294](https://github.com/redis/go-redis/pull/3294)) -- **Enabled Search DIALECT 2 by default** ([#3213](https://github.com/redis/go-redis/pull/3213)) -- Added read-only option for failover configurations ([#3281](https://github.com/redis/go-redis/pull/3281)) -- Added `CountOnly` argument for `FT.Search` to use `LIMIT 0 0` ([#3338](https://github.com/redis/go-redis/pull/3338)) -- Added `DB` option support in `NewFailoverClusterClient` ([#3342](https://github.com/redis/go-redis/pull/3342)) -- Added `nil` check for the options when creating a client ([#3363](https://github.com/redis/go-redis/pull/3363)) - -## 🐛 Bug Fixes -- Fixed `PubSub` concurrency safety issues ([#3360](https://github.com/redis/go-redis/pull/3360)) -- Fixed panic caused when argument is `nil` ([#3353](https://github.com/redis/go-redis/pull/3353)) -- Improved error handling when fetching master node from sentinels ([#3349](https://github.com/redis/go-redis/pull/3349)) -- Fixed connection pool timeout issues and increased retries ([#3298](https://github.com/redis/go-redis/pull/3298)) -- Fixed context cancellation error leading to connection spikes on Primary instances ([#3190](https://github.com/redis/go-redis/pull/3190)) -- Fixed RedisCluster client to consider `MASTERDOWN` a retriable error ([#3164](https://github.com/redis/go-redis/pull/3164)) -- Fixed tracing to show complete commands instead of truncated versions ([#3290](https://github.com/redis/go-redis/pull/3290)) -- Fixed OpenTelemetry instrumentation to prevent multiple span reporting ([#3168](https://github.com/redis/go-redis/pull/3168)) -- Fixed `FT.Search` Limit argument and added `CountOnly` argument for limit 0 0 ([#3338](https://github.com/redis/go-redis/pull/3338)) -- Fixed missing command in interface ([#3344](https://github.com/redis/go-redis/pull/3344)) -- Fixed slot calculation for `COUNTKEYSINSLOT` command ([#3327](https://github.com/redis/go-redis/pull/3327)) -- Updated PubSub implementation with correct context ([#3329](https://github.com/redis/go-redis/pull/3329)) - -## 📚 Documentation -- Added hash search examples ([#3357](https://github.com/redis/go-redis/pull/3357)) -- Fixed documentation comments ([#3351](https://github.com/redis/go-redis/pull/3351)) -- Added `CountOnly` search example ([#3345](https://github.com/redis/go-redis/pull/3345)) -- Added examples for list commands: `LLEN`, `LPOP`, `LPUSH`, `LRANGE`, `RPOP`, `RPUSH` ([#3234](https://github.com/redis/go-redis/pull/3234)) -- Added `SADD` and `SMEMBERS` command examples ([#3242](https://github.com/redis/go-redis/pull/3242)) -- Updated `README.md` to use Redis Discord guild ([#3331](https://github.com/redis/go-redis/pull/3331)) -- Updated `HExpire` command documentation ([#3355](https://github.com/redis/go-redis/pull/3355)) -- Featured OpenTelemetry instrumentation more prominently ([#3316](https://github.com/redis/go-redis/pull/3316)) -- Updated `README.md` with additional information ([#310ce55](https://github.com/redis/go-redis/commit/310ce55)) - -## ⚡ Performance and Reliability -- Bound connection pool background dials to configured dial timeout ([#3089](https://github.com/redis/go-redis/pull/3089)) -- Ensured context isn't exhausted via concurrent query ([#3334](https://github.com/redis/go-redis/pull/3334)) - -## 🔧 Dependencies and Infrastructure -- Updated testing image to Redis 8.0-RC2 ([#3361](https://github.com/redis/go-redis/pull/3361)) -- Enabled CI for Redis CE 8.0 ([#3274](https://github.com/redis/go-redis/pull/3274)) -- Updated various dependencies: - - Bumped golangci/golangci-lint-action from 6.5.0 to 7.0.0 ([#3354](https://github.com/redis/go-redis/pull/3354)) - - Bumped rojopolis/spellcheck-github-actions ([#3336](https://github.com/redis/go-redis/pull/3336)) - - Bumped golang.org/x/net in example/otel ([#3308](https://github.com/redis/go-redis/pull/3308)) -- Migrated golangci-lint configuration to v2 format ([#3354](https://github.com/redis/go-redis/pull/3354)) - -## ⚠️ Breaking Changes -- **Enabled Search DIALECT 2 by default** ([#3213](https://github.com/redis/go-redis/pull/3213)) -- Dropped RedisGears (Triggers and Functions) support ([#3321](https://github.com/redis/go-redis/pull/3321)) -- Dropped FT.PROFILE command that was never enabled ([#3323](https://github.com/redis/go-redis/pull/3323)) - -## 🔒 Security -- Fixed network error handling on SETINFO (CVE-2025-29923) ([#3295](https://github.com/redis/go-redis/pull/3295)) - -## 🧪 Testing -- Added integration tests for Redis 8 behavior changes in Redis Search ([#3337](https://github.com/redis/go-redis/pull/3337)) -- Added vector types INT8 and UINT8 tests ([#3299](https://github.com/redis/go-redis/pull/3299)) -- Added test codes for search_commands.go ([#3285](https://github.com/redis/go-redis/pull/3285)) -- Fixed example test sorting ([#3292](https://github.com/redis/go-redis/pull/3292)) - -## 👥 Contributors - -We would like to thank all the contributors who made this release possible: - -[@alexander-menshchikov](https://github.com/alexander-menshchikov), [@EXPEbdodla](https://github.com/EXPEbdodla), [@afti](https://github.com/afti), [@dmaier-redislabs](https://github.com/dmaier-redislabs), [@four_leaf_clover](https://github.com/four_leaf_clover), [@alohaglenn](https://github.com/alohaglenn), [@gh73962](https://github.com/gh73962), [@justinmir](https://github.com/justinmir), [@LINKIWI](https://github.com/LINKIWI), [@liushuangbill](https://github.com/liushuangbill), [@golang88](https://github.com/golang88), [@gnpaone](https://github.com/gnpaone), [@ndyakov](https://github.com/ndyakov), [@nikolaydubina](https://github.com/nikolaydubina), [@oleglacto](https://github.com/oleglacto), [@andy-stark-redis](https://github.com/andy-stark-redis), [@rodneyosodo](https://github.com/rodneyosodo), [@dependabot](https://github.com/dependabot), [@rfyiamcool](https://github.com/rfyiamcool), [@frankxjkuang](https://github.com/frankxjkuang), [@fukua95](https://github.com/fukua95), [@soleymani-milad](https://github.com/soleymani-milad), [@ofekshenawa](https://github.com/ofekshenawa), [@khasanovbi](https://github.com/khasanovbi) diff --git a/vendor/github.com/redis/go-redis/v9/RELEASING.md b/vendor/github.com/redis/go-redis/v9/RELEASING.md deleted file mode 100644 index 1115db4..0000000 --- a/vendor/github.com/redis/go-redis/v9/RELEASING.md +++ /dev/null @@ -1,15 +0,0 @@ -# Releasing - -1. Run `release.sh` script which updates versions in go.mod files and pushes a new branch to GitHub: - -```shell -TAG=v1.0.0 ./scripts/release.sh -``` - -2. Open a pull request and wait for the build to finish. - -3. Merge the pull request and run `tag.sh` to create tags for packages: - -```shell -TAG=v1.0.0 ./scripts/tag.sh -``` diff --git a/vendor/github.com/redis/go-redis/v9/acl_commands.go b/vendor/github.com/redis/go-redis/v9/acl_commands.go deleted file mode 100644 index 9cb800b..0000000 --- a/vendor/github.com/redis/go-redis/v9/acl_commands.go +++ /dev/null @@ -1,89 +0,0 @@ -package redis - -import "context" - -type ACLCmdable interface { - ACLDryRun(ctx context.Context, username string, command ...interface{}) *StringCmd - - ACLLog(ctx context.Context, count int64) *ACLLogCmd - ACLLogReset(ctx context.Context) *StatusCmd - - ACLSetUser(ctx context.Context, username string, rules ...string) *StatusCmd - ACLDelUser(ctx context.Context, username string) *IntCmd - ACLList(ctx context.Context) *StringSliceCmd - - ACLCat(ctx context.Context) *StringSliceCmd - ACLCatArgs(ctx context.Context, options *ACLCatArgs) *StringSliceCmd -} - -type ACLCatArgs struct { - Category string -} - -func (c cmdable) ACLDryRun(ctx context.Context, username string, command ...interface{}) *StringCmd { - args := make([]interface{}, 0, 3+len(command)) - args = append(args, "acl", "dryrun", username) - args = append(args, command...) - cmd := NewStringCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ACLLog(ctx context.Context, count int64) *ACLLogCmd { - args := make([]interface{}, 0, 3) - args = append(args, "acl", "log") - if count > 0 { - args = append(args, count) - } - cmd := NewACLLogCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ACLLogReset(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "acl", "log", "reset") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ACLDelUser(ctx context.Context, username string) *IntCmd { - cmd := NewIntCmd(ctx, "acl", "deluser", username) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ACLSetUser(ctx context.Context, username string, rules ...string) *StatusCmd { - args := make([]interface{}, 3+len(rules)) - args[0] = "acl" - args[1] = "setuser" - args[2] = username - for i, rule := range rules { - args[i+3] = rule - } - cmd := NewStatusCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ACLList(ctx context.Context) *StringSliceCmd { - cmd := NewStringSliceCmd(ctx, "acl", "list") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ACLCat(ctx context.Context) *StringSliceCmd { - cmd := NewStringSliceCmd(ctx, "acl", "cat") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ACLCatArgs(ctx context.Context, options *ACLCatArgs) *StringSliceCmd { - // if there is a category passed, build new cmd, if there isn't - use the ACLCat method - if options != nil && options.Category != "" { - cmd := NewStringSliceCmd(ctx, "acl", "cat", options.Category) - _ = c(ctx, cmd) - return cmd - } - - return c.ACLCat(ctx) -} diff --git a/vendor/github.com/redis/go-redis/v9/auth/auth.go b/vendor/github.com/redis/go-redis/v9/auth/auth.go deleted file mode 100644 index 1f5c802..0000000 --- a/vendor/github.com/redis/go-redis/v9/auth/auth.go +++ /dev/null @@ -1,61 +0,0 @@ -// Package auth package provides authentication-related interfaces and types. -// It also includes a basic implementation of credentials using username and password. -package auth - -// StreamingCredentialsProvider is an interface that defines the methods for a streaming credentials provider. -// It is used to provide credentials for authentication. -// The CredentialsListener is used to receive updates when the credentials change. -type StreamingCredentialsProvider interface { - // Subscribe subscribes to the credentials provider for updates. - // It returns the current credentials, a cancel function to unsubscribe from the provider, - // and an error if any. - // TODO(ndyakov): Should we add context to the Subscribe method? - Subscribe(listener CredentialsListener) (Credentials, UnsubscribeFunc, error) -} - -// UnsubscribeFunc is a function that is used to cancel the subscription to the credentials provider. -// It is used to unsubscribe from the provider when the credentials are no longer needed. -type UnsubscribeFunc func() error - -// CredentialsListener is an interface that defines the methods for a credentials listener. -// It is used to receive updates when the credentials change. -// The OnNext method is called when the credentials change. -// The OnError method is called when an error occurs while requesting the credentials. -type CredentialsListener interface { - OnNext(credentials Credentials) - OnError(err error) -} - -// Credentials is an interface that defines the methods for credentials. -// It is used to provide the credentials for authentication. -type Credentials interface { - // BasicAuth returns the username and password for basic authentication. - BasicAuth() (username string, password string) - // RawCredentials returns the raw credentials as a string. - // This can be used to extract the username and password from the raw credentials or - // additional information if present in the token. - RawCredentials() string -} - -type basicAuth struct { - username string - password string -} - -// RawCredentials returns the raw credentials as a string. -func (b *basicAuth) RawCredentials() string { - return b.username + ":" + b.password -} - -// BasicAuth returns the username and password for basic authentication. -func (b *basicAuth) BasicAuth() (username string, password string) { - return b.username, b.password -} - -// NewBasicCredentials creates a new Credentials object from the given username and password. -func NewBasicCredentials(username, password string) Credentials { - return &basicAuth{ - username: username, - password: password, - } -} diff --git a/vendor/github.com/redis/go-redis/v9/auth/reauth_credentials_listener.go b/vendor/github.com/redis/go-redis/v9/auth/reauth_credentials_listener.go deleted file mode 100644 index 40076a0..0000000 --- a/vendor/github.com/redis/go-redis/v9/auth/reauth_credentials_listener.go +++ /dev/null @@ -1,47 +0,0 @@ -package auth - -// ReAuthCredentialsListener is a struct that implements the CredentialsListener interface. -// It is used to re-authenticate the credentials when they are updated. -// It contains: -// - reAuth: a function that takes the new credentials and returns an error if any. -// - onErr: a function that takes an error and handles it. -type ReAuthCredentialsListener struct { - reAuth func(credentials Credentials) error - onErr func(err error) -} - -// OnNext is called when the credentials are updated. -// It calls the reAuth function with the new credentials. -// If the reAuth function returns an error, it calls the onErr function with the error. -func (c *ReAuthCredentialsListener) OnNext(credentials Credentials) { - if c.reAuth == nil { - return - } - - err := c.reAuth(credentials) - if err != nil { - c.OnError(err) - } -} - -// OnError is called when an error occurs. -// It can be called from both the credentials provider and the reAuth function. -func (c *ReAuthCredentialsListener) OnError(err error) { - if c.onErr == nil { - return - } - - c.onErr(err) -} - -// NewReAuthCredentialsListener creates a new ReAuthCredentialsListener. -// Implements the auth.CredentialsListener interface. -func NewReAuthCredentialsListener(reAuth func(credentials Credentials) error, onErr func(err error)) *ReAuthCredentialsListener { - return &ReAuthCredentialsListener{ - reAuth: reAuth, - onErr: onErr, - } -} - -// Ensure ReAuthCredentialsListener implements the CredentialsListener interface. -var _ CredentialsListener = (*ReAuthCredentialsListener)(nil) diff --git a/vendor/github.com/redis/go-redis/v9/bitmap_commands.go b/vendor/github.com/redis/go-redis/v9/bitmap_commands.go deleted file mode 100644 index a215582..0000000 --- a/vendor/github.com/redis/go-redis/v9/bitmap_commands.go +++ /dev/null @@ -1,161 +0,0 @@ -package redis - -import ( - "context" - "errors" -) - -type BitMapCmdable interface { - GetBit(ctx context.Context, key string, offset int64) *IntCmd - SetBit(ctx context.Context, key string, offset int64, value int) *IntCmd - BitCount(ctx context.Context, key string, bitCount *BitCount) *IntCmd - BitOpAnd(ctx context.Context, destKey string, keys ...string) *IntCmd - BitOpOr(ctx context.Context, destKey string, keys ...string) *IntCmd - BitOpXor(ctx context.Context, destKey string, keys ...string) *IntCmd - BitOpNot(ctx context.Context, destKey string, key string) *IntCmd - BitPos(ctx context.Context, key string, bit int64, pos ...int64) *IntCmd - BitPosSpan(ctx context.Context, key string, bit int8, start, end int64, span string) *IntCmd - BitField(ctx context.Context, key string, values ...interface{}) *IntSliceCmd - BitFieldRO(ctx context.Context, key string, values ...interface{}) *IntSliceCmd -} - -func (c cmdable) GetBit(ctx context.Context, key string, offset int64) *IntCmd { - cmd := NewIntCmd(ctx, "getbit", key, offset) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) SetBit(ctx context.Context, key string, offset int64, value int) *IntCmd { - cmd := NewIntCmd( - ctx, - "setbit", - key, - offset, - value, - ) - _ = c(ctx, cmd) - return cmd -} - -type BitCount struct { - Start, End int64 - Unit string // BYTE(default) | BIT -} - -const BitCountIndexByte string = "BYTE" -const BitCountIndexBit string = "BIT" - -func (c cmdable) BitCount(ctx context.Context, key string, bitCount *BitCount) *IntCmd { - args := make([]any, 2, 5) - args[0] = "bitcount" - args[1] = key - if bitCount != nil { - args = append(args, bitCount.Start, bitCount.End) - if bitCount.Unit != "" { - if bitCount.Unit != BitCountIndexByte && bitCount.Unit != BitCountIndexBit { - cmd := NewIntCmd(ctx) - cmd.SetErr(errors.New("redis: invalid bitcount index")) - return cmd - } - args = append(args, bitCount.Unit) - } - } - cmd := NewIntCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) bitOp(ctx context.Context, op, destKey string, keys ...string) *IntCmd { - args := make([]interface{}, 3+len(keys)) - args[0] = "bitop" - args[1] = op - args[2] = destKey - for i, key := range keys { - args[3+i] = key - } - cmd := NewIntCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) BitOpAnd(ctx context.Context, destKey string, keys ...string) *IntCmd { - return c.bitOp(ctx, "and", destKey, keys...) -} - -func (c cmdable) BitOpOr(ctx context.Context, destKey string, keys ...string) *IntCmd { - return c.bitOp(ctx, "or", destKey, keys...) -} - -func (c cmdable) BitOpXor(ctx context.Context, destKey string, keys ...string) *IntCmd { - return c.bitOp(ctx, "xor", destKey, keys...) -} - -func (c cmdable) BitOpNot(ctx context.Context, destKey string, key string) *IntCmd { - return c.bitOp(ctx, "not", destKey, key) -} - -// BitPos is an API before Redis version 7.0, cmd: bitpos key bit start end -// if you need the `byte | bit` parameter, please use `BitPosSpan`. -func (c cmdable) BitPos(ctx context.Context, key string, bit int64, pos ...int64) *IntCmd { - args := make([]interface{}, 3+len(pos)) - args[0] = "bitpos" - args[1] = key - args[2] = bit - switch len(pos) { - case 0: - case 1: - args[3] = pos[0] - case 2: - args[3] = pos[0] - args[4] = pos[1] - default: - panic("too many arguments") - } - cmd := NewIntCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -// BitPosSpan supports the `byte | bit` parameters in redis version 7.0, -// the bitpos command defaults to using byte type for the `start-end` range, -// which means it counts in bytes from start to end. you can set the value -// of "span" to determine the type of `start-end`. -// span = "bit", cmd: bitpos key bit start end bit -// span = "byte", cmd: bitpos key bit start end byte -func (c cmdable) BitPosSpan(ctx context.Context, key string, bit int8, start, end int64, span string) *IntCmd { - cmd := NewIntCmd(ctx, "bitpos", key, bit, start, end, span) - _ = c(ctx, cmd) - return cmd -} - -// BitField accepts multiple values: -// - BitField("set", "i1", "offset1", "value1","cmd2", "type2", "offset2", "value2") -// - BitField([]string{"cmd1", "type1", "offset1", "value1","cmd2", "type2", "offset2", "value2"}) -// - BitField([]interface{}{"cmd1", "type1", "offset1", "value1","cmd2", "type2", "offset2", "value2"}) -func (c cmdable) BitField(ctx context.Context, key string, values ...interface{}) *IntSliceCmd { - args := make([]interface{}, 2, 2+len(values)) - args[0] = "bitfield" - args[1] = key - args = appendArgs(args, values) - cmd := NewIntSliceCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -// BitFieldRO - Read-only variant of the BITFIELD command. -// It is like the original BITFIELD but only accepts GET subcommand and can safely be used in read-only replicas. -// - BitFieldRO(ctx, key, "", "", "","") -func (c cmdable) BitFieldRO(ctx context.Context, key string, values ...interface{}) *IntSliceCmd { - args := make([]interface{}, 2, 2+len(values)) - args[0] = "BITFIELD_RO" - args[1] = key - if len(values)%2 != 0 { - panic("BitFieldRO: invalid number of arguments, must be even") - } - for i := 0; i < len(values); i += 2 { - args = append(args, "GET", values[i], values[i+1]) - } - cmd := NewIntSliceCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} diff --git a/vendor/github.com/redis/go-redis/v9/cluster_commands.go b/vendor/github.com/redis/go-redis/v9/cluster_commands.go deleted file mode 100644 index 4857b01..0000000 --- a/vendor/github.com/redis/go-redis/v9/cluster_commands.go +++ /dev/null @@ -1,199 +0,0 @@ -package redis - -import "context" - -type ClusterCmdable interface { - ClusterMyShardID(ctx context.Context) *StringCmd - ClusterMyID(ctx context.Context) *StringCmd - ClusterSlots(ctx context.Context) *ClusterSlotsCmd - ClusterShards(ctx context.Context) *ClusterShardsCmd - ClusterLinks(ctx context.Context) *ClusterLinksCmd - ClusterNodes(ctx context.Context) *StringCmd - ClusterMeet(ctx context.Context, host, port string) *StatusCmd - ClusterForget(ctx context.Context, nodeID string) *StatusCmd - ClusterReplicate(ctx context.Context, nodeID string) *StatusCmd - ClusterResetSoft(ctx context.Context) *StatusCmd - ClusterResetHard(ctx context.Context) *StatusCmd - ClusterInfo(ctx context.Context) *StringCmd - ClusterKeySlot(ctx context.Context, key string) *IntCmd - ClusterGetKeysInSlot(ctx context.Context, slot int, count int) *StringSliceCmd - ClusterCountFailureReports(ctx context.Context, nodeID string) *IntCmd - ClusterCountKeysInSlot(ctx context.Context, slot int) *IntCmd - ClusterDelSlots(ctx context.Context, slots ...int) *StatusCmd - ClusterDelSlotsRange(ctx context.Context, min, max int) *StatusCmd - ClusterSaveConfig(ctx context.Context) *StatusCmd - ClusterSlaves(ctx context.Context, nodeID string) *StringSliceCmd - ClusterFailover(ctx context.Context) *StatusCmd - ClusterAddSlots(ctx context.Context, slots ...int) *StatusCmd - ClusterAddSlotsRange(ctx context.Context, min, max int) *StatusCmd - ReadOnly(ctx context.Context) *StatusCmd - ReadWrite(ctx context.Context) *StatusCmd -} - -func (c cmdable) ClusterMyShardID(ctx context.Context) *StringCmd { - cmd := NewStringCmd(ctx, "cluster", "myshardid") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterMyID(ctx context.Context) *StringCmd { - cmd := NewStringCmd(ctx, "cluster", "myid") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterSlots(ctx context.Context) *ClusterSlotsCmd { - cmd := NewClusterSlotsCmd(ctx, "cluster", "slots") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterShards(ctx context.Context) *ClusterShardsCmd { - cmd := NewClusterShardsCmd(ctx, "cluster", "shards") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterLinks(ctx context.Context) *ClusterLinksCmd { - cmd := NewClusterLinksCmd(ctx, "cluster", "links") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterNodes(ctx context.Context) *StringCmd { - cmd := NewStringCmd(ctx, "cluster", "nodes") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterMeet(ctx context.Context, host, port string) *StatusCmd { - cmd := NewStatusCmd(ctx, "cluster", "meet", host, port) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterForget(ctx context.Context, nodeID string) *StatusCmd { - cmd := NewStatusCmd(ctx, "cluster", "forget", nodeID) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterReplicate(ctx context.Context, nodeID string) *StatusCmd { - cmd := NewStatusCmd(ctx, "cluster", "replicate", nodeID) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterResetSoft(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "cluster", "reset", "soft") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterResetHard(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "cluster", "reset", "hard") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterInfo(ctx context.Context) *StringCmd { - cmd := NewStringCmd(ctx, "cluster", "info") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterKeySlot(ctx context.Context, key string) *IntCmd { - cmd := NewIntCmd(ctx, "cluster", "keyslot", key) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterGetKeysInSlot(ctx context.Context, slot int, count int) *StringSliceCmd { - cmd := NewStringSliceCmd(ctx, "cluster", "getkeysinslot", slot, count) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterCountFailureReports(ctx context.Context, nodeID string) *IntCmd { - cmd := NewIntCmd(ctx, "cluster", "count-failure-reports", nodeID) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterCountKeysInSlot(ctx context.Context, slot int) *IntCmd { - cmd := NewIntCmd(ctx, "cluster", "countkeysinslot", slot) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterDelSlots(ctx context.Context, slots ...int) *StatusCmd { - args := make([]interface{}, 2+len(slots)) - args[0] = "cluster" - args[1] = "delslots" - for i, slot := range slots { - args[2+i] = slot - } - cmd := NewStatusCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterDelSlotsRange(ctx context.Context, min, max int) *StatusCmd { - size := max - min + 1 - slots := make([]int, size) - for i := 0; i < size; i++ { - slots[i] = min + i - } - return c.ClusterDelSlots(ctx, slots...) -} - -func (c cmdable) ClusterSaveConfig(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "cluster", "saveconfig") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterSlaves(ctx context.Context, nodeID string) *StringSliceCmd { - cmd := NewStringSliceCmd(ctx, "cluster", "slaves", nodeID) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterFailover(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "cluster", "failover") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterAddSlots(ctx context.Context, slots ...int) *StatusCmd { - args := make([]interface{}, 2+len(slots)) - args[0] = "cluster" - args[1] = "addslots" - for i, num := range slots { - args[2+i] = num - } - cmd := NewStatusCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClusterAddSlotsRange(ctx context.Context, min, max int) *StatusCmd { - size := max - min + 1 - slots := make([]int, size) - for i := 0; i < size; i++ { - slots[i] = min + i - } - return c.ClusterAddSlots(ctx, slots...) -} - -func (c cmdable) ReadOnly(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "readonly") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ReadWrite(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "readwrite") - _ = c(ctx, cmd) - return cmd -} diff --git a/vendor/github.com/redis/go-redis/v9/command.go b/vendor/github.com/redis/go-redis/v9/command.go deleted file mode 100644 index b79338c..0000000 --- a/vendor/github.com/redis/go-redis/v9/command.go +++ /dev/null @@ -1,5736 +0,0 @@ -package redis - -import ( - "bufio" - "context" - "fmt" - "net" - "regexp" - "strconv" - "strings" - "sync" - "time" - - "github.com/redis/go-redis/v9/internal" - "github.com/redis/go-redis/v9/internal/hscan" - "github.com/redis/go-redis/v9/internal/proto" - "github.com/redis/go-redis/v9/internal/util" -) - -// keylessCommands contains Redis commands that have empty key specifications (9th slot empty) -// Only includes core Redis commands, excludes FT.*, ts.*, timeseries.*, search.* and subcommands -var keylessCommands = map[string]struct{}{ - "acl": {}, - "asking": {}, - "auth": {}, - "bgrewriteaof": {}, - "bgsave": {}, - "client": {}, - "cluster": {}, - "config": {}, - "debug": {}, - "discard": {}, - "echo": {}, - "exec": {}, - "failover": {}, - "function": {}, - "hello": {}, - "latency": {}, - "lolwut": {}, - "module": {}, - "monitor": {}, - "multi": {}, - "pfselftest": {}, - "ping": {}, - "psubscribe": {}, - "psync": {}, - "publish": {}, - "pubsub": {}, - "punsubscribe": {}, - "quit": {}, - "readonly": {}, - "readwrite": {}, - "replconf": {}, - "replicaof": {}, - "role": {}, - "save": {}, - "script": {}, - "select": {}, - "shutdown": {}, - "slaveof": {}, - "slowlog": {}, - "subscribe": {}, - "swapdb": {}, - "sync": {}, - "unsubscribe": {}, - "unwatch": {}, -} - -type Cmder interface { - // command name. - // e.g. "set k v ex 10" -> "set", "cluster info" -> "cluster". - Name() string - - // full command name. - // e.g. "set k v ex 10" -> "set", "cluster info" -> "cluster info". - FullName() string - - // all args of the command. - // e.g. "set k v ex 10" -> "[set k v ex 10]". - Args() []interface{} - - // format request and response string. - // e.g. "set k v ex 10" -> "set k v ex 10: OK", "get k" -> "get k: v". - String() string - - stringArg(int) string - firstKeyPos() int8 - SetFirstKeyPos(int8) - - readTimeout() *time.Duration - readReply(rd *proto.Reader) error - readRawReply(rd *proto.Reader) error - SetErr(error) - Err() error -} - -func setCmdsErr(cmds []Cmder, e error) { - for _, cmd := range cmds { - if cmd.Err() == nil { - cmd.SetErr(e) - } - } -} - -func cmdsFirstErr(cmds []Cmder) error { - for _, cmd := range cmds { - if err := cmd.Err(); err != nil { - return err - } - } - return nil -} - -func writeCmds(wr *proto.Writer, cmds []Cmder) error { - for _, cmd := range cmds { - if err := writeCmd(wr, cmd); err != nil { - return err - } - } - return nil -} - -func writeCmd(wr *proto.Writer, cmd Cmder) error { - return wr.WriteArgs(cmd.Args()) -} - -// cmdFirstKeyPos returns the position of the first key in the command's arguments. -// If the command does not have a key, it returns 0. -// TODO: Use the data in CommandInfo to determine the first key position. -func cmdFirstKeyPos(cmd Cmder) int { - if pos := cmd.firstKeyPos(); pos != 0 { - return int(pos) - } - - name := cmd.Name() - - // first check if the command is keyless - if _, ok := keylessCommands[name]; ok { - return 0 - } - - switch name { - case "eval", "evalsha", "eval_ro", "evalsha_ro": - if cmd.stringArg(2) != "0" { - return 3 - } - - return 0 - case "publish": - return 1 - case "memory": - // https://github.com/redis/redis/issues/7493 - if cmd.stringArg(1) == "usage" { - return 2 - } - } - return 1 -} - -func cmdString(cmd Cmder, val interface{}) string { - b := make([]byte, 0, 64) - - for i, arg := range cmd.Args() { - if i > 0 { - b = append(b, ' ') - } - b = internal.AppendArg(b, arg) - } - - if err := cmd.Err(); err != nil { - b = append(b, ": "...) - b = append(b, err.Error()...) - } else if val != nil { - b = append(b, ": "...) - b = internal.AppendArg(b, val) - } - - return util.BytesToString(b) -} - -//------------------------------------------------------------------------------ - -type baseCmd struct { - ctx context.Context - args []interface{} - err error - keyPos int8 - rawVal interface{} - _readTimeout *time.Duration -} - -var _ Cmder = (*Cmd)(nil) - -func (cmd *baseCmd) Name() string { - if len(cmd.args) == 0 { - return "" - } - // Cmd name must be lower cased. - return internal.ToLower(cmd.stringArg(0)) -} - -func (cmd *baseCmd) FullName() string { - switch name := cmd.Name(); name { - case "cluster", "command": - if len(cmd.args) == 1 { - return name - } - if s2, ok := cmd.args[1].(string); ok { - return name + " " + s2 - } - return name - default: - return name - } -} - -func (cmd *baseCmd) Args() []interface{} { - return cmd.args -} - -func (cmd *baseCmd) stringArg(pos int) string { - if pos < 0 || pos >= len(cmd.args) { - return "" - } - arg := cmd.args[pos] - switch v := arg.(type) { - case string: - return v - case []byte: - return string(v) - default: - // TODO: consider using appendArg - return fmt.Sprint(v) - } -} - -func (cmd *baseCmd) firstKeyPos() int8 { - return cmd.keyPos -} - -func (cmd *baseCmd) SetFirstKeyPos(keyPos int8) { - cmd.keyPos = keyPos -} - -func (cmd *baseCmd) SetErr(e error) { - cmd.err = e -} - -func (cmd *baseCmd) Err() error { - return cmd.err -} - -func (cmd *baseCmd) readTimeout() *time.Duration { - return cmd._readTimeout -} - -func (cmd *baseCmd) setReadTimeout(d time.Duration) { - cmd._readTimeout = &d -} - -func (cmd *baseCmd) readRawReply(rd *proto.Reader) (err error) { - cmd.rawVal, err = rd.ReadReply() - return err -} - -//------------------------------------------------------------------------------ - -type Cmd struct { - baseCmd - - val interface{} -} - -func NewCmd(ctx context.Context, args ...interface{}) *Cmd { - return &Cmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *Cmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *Cmd) SetVal(val interface{}) { - cmd.val = val -} - -func (cmd *Cmd) Val() interface{} { - return cmd.val -} - -func (cmd *Cmd) Result() (interface{}, error) { - return cmd.val, cmd.err -} - -func (cmd *Cmd) Text() (string, error) { - if cmd.err != nil { - return "", cmd.err - } - return toString(cmd.val) -} - -func toString(val interface{}) (string, error) { - switch val := val.(type) { - case string: - return val, nil - default: - err := fmt.Errorf("redis: unexpected type=%T for String", val) - return "", err - } -} - -func (cmd *Cmd) Int() (int, error) { - if cmd.err != nil { - return 0, cmd.err - } - switch val := cmd.val.(type) { - case int64: - return int(val), nil - case string: - return strconv.Atoi(val) - default: - err := fmt.Errorf("redis: unexpected type=%T for Int", val) - return 0, err - } -} - -func (cmd *Cmd) Int64() (int64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return toInt64(cmd.val) -} - -func toInt64(val interface{}) (int64, error) { - switch val := val.(type) { - case int64: - return val, nil - case string: - return strconv.ParseInt(val, 10, 64) - default: - err := fmt.Errorf("redis: unexpected type=%T for Int64", val) - return 0, err - } -} - -func (cmd *Cmd) Uint64() (uint64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return toUint64(cmd.val) -} - -func toUint64(val interface{}) (uint64, error) { - switch val := val.(type) { - case int64: - return uint64(val), nil - case string: - return strconv.ParseUint(val, 10, 64) - default: - err := fmt.Errorf("redis: unexpected type=%T for Uint64", val) - return 0, err - } -} - -func (cmd *Cmd) Float32() (float32, error) { - if cmd.err != nil { - return 0, cmd.err - } - return toFloat32(cmd.val) -} - -func toFloat32(val interface{}) (float32, error) { - switch val := val.(type) { - case int64: - return float32(val), nil - case string: - f, err := strconv.ParseFloat(val, 32) - if err != nil { - return 0, err - } - return float32(f), nil - default: - err := fmt.Errorf("redis: unexpected type=%T for Float32", val) - return 0, err - } -} - -func (cmd *Cmd) Float64() (float64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return toFloat64(cmd.val) -} - -func toFloat64(val interface{}) (float64, error) { - switch val := val.(type) { - case int64: - return float64(val), nil - case string: - return strconv.ParseFloat(val, 64) - default: - err := fmt.Errorf("redis: unexpected type=%T for Float64", val) - return 0, err - } -} - -func (cmd *Cmd) Bool() (bool, error) { - if cmd.err != nil { - return false, cmd.err - } - return toBool(cmd.val) -} - -func toBool(val interface{}) (bool, error) { - switch val := val.(type) { - case bool: - return val, nil - case int64: - return val != 0, nil - case string: - return strconv.ParseBool(val) - default: - err := fmt.Errorf("redis: unexpected type=%T for Bool", val) - return false, err - } -} - -func (cmd *Cmd) Slice() ([]interface{}, error) { - if cmd.err != nil { - return nil, cmd.err - } - switch val := cmd.val.(type) { - case []interface{}: - return val, nil - default: - return nil, fmt.Errorf("redis: unexpected type=%T for Slice", val) - } -} - -func (cmd *Cmd) StringSlice() ([]string, error) { - slice, err := cmd.Slice() - if err != nil { - return nil, err - } - - ss := make([]string, len(slice)) - for i, iface := range slice { - val, err := toString(iface) - if err != nil { - return nil, err - } - ss[i] = val - } - return ss, nil -} - -func (cmd *Cmd) Int64Slice() ([]int64, error) { - slice, err := cmd.Slice() - if err != nil { - return nil, err - } - - nums := make([]int64, len(slice)) - for i, iface := range slice { - val, err := toInt64(iface) - if err != nil { - return nil, err - } - nums[i] = val - } - return nums, nil -} - -func (cmd *Cmd) Uint64Slice() ([]uint64, error) { - slice, err := cmd.Slice() - if err != nil { - return nil, err - } - - nums := make([]uint64, len(slice)) - for i, iface := range slice { - val, err := toUint64(iface) - if err != nil { - return nil, err - } - nums[i] = val - } - return nums, nil -} - -func (cmd *Cmd) Float32Slice() ([]float32, error) { - slice, err := cmd.Slice() - if err != nil { - return nil, err - } - - floats := make([]float32, len(slice)) - for i, iface := range slice { - val, err := toFloat32(iface) - if err != nil { - return nil, err - } - floats[i] = val - } - return floats, nil -} - -func (cmd *Cmd) Float64Slice() ([]float64, error) { - slice, err := cmd.Slice() - if err != nil { - return nil, err - } - - floats := make([]float64, len(slice)) - for i, iface := range slice { - val, err := toFloat64(iface) - if err != nil { - return nil, err - } - floats[i] = val - } - return floats, nil -} - -func (cmd *Cmd) BoolSlice() ([]bool, error) { - slice, err := cmd.Slice() - if err != nil { - return nil, err - } - - bools := make([]bool, len(slice)) - for i, iface := range slice { - val, err := toBool(iface) - if err != nil { - return nil, err - } - bools[i] = val - } - return bools, nil -} - -func (cmd *Cmd) readReply(rd *proto.Reader) (err error) { - cmd.val, err = rd.ReadReply() - return err -} - -//------------------------------------------------------------------------------ - -type SliceCmd struct { - baseCmd - - val []interface{} -} - -var _ Cmder = (*SliceCmd)(nil) - -func NewSliceCmd(ctx context.Context, args ...interface{}) *SliceCmd { - return &SliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *SliceCmd) SetVal(val []interface{}) { - cmd.val = val -} - -func (cmd *SliceCmd) Val() []interface{} { - return cmd.val -} - -func (cmd *SliceCmd) Result() ([]interface{}, error) { - return cmd.val, cmd.err -} - -func (cmd *SliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -// Scan scans the results from the map into a destination struct. The map keys -// are matched in the Redis struct fields by the `redis:"field"` tag. -func (cmd *SliceCmd) Scan(dst interface{}) error { - if cmd.err != nil { - return cmd.err - } - - // Pass the list of keys and values. - // Skip the first two args for: HMGET key - var args []interface{} - if cmd.args[0] == "hmget" { - args = cmd.args[2:] - } else { - // Otherwise, it's: MGET field field ... - args = cmd.args[1:] - } - - return hscan.Scan(dst, args, cmd.val) -} - -func (cmd *SliceCmd) readReply(rd *proto.Reader) (err error) { - cmd.val, err = rd.ReadSlice() - return err -} - -//------------------------------------------------------------------------------ - -type StatusCmd struct { - baseCmd - - val string -} - -var _ Cmder = (*StatusCmd)(nil) - -func NewStatusCmd(ctx context.Context, args ...interface{}) *StatusCmd { - return &StatusCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *StatusCmd) SetVal(val string) { - cmd.val = val -} - -func (cmd *StatusCmd) Val() string { - return cmd.val -} - -func (cmd *StatusCmd) Result() (string, error) { - return cmd.val, cmd.err -} - -func (cmd *StatusCmd) Bytes() ([]byte, error) { - return util.StringToBytes(cmd.val), cmd.err -} - -func (cmd *StatusCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StatusCmd) readReply(rd *proto.Reader) (err error) { - cmd.val, err = rd.ReadString() - return err -} - -//------------------------------------------------------------------------------ - -type IntCmd struct { - baseCmd - - val int64 -} - -var _ Cmder = (*IntCmd)(nil) - -func NewIntCmd(ctx context.Context, args ...interface{}) *IntCmd { - return &IntCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *IntCmd) SetVal(val int64) { - cmd.val = val -} - -func (cmd *IntCmd) Val() int64 { - return cmd.val -} - -func (cmd *IntCmd) Result() (int64, error) { - return cmd.val, cmd.err -} - -func (cmd *IntCmd) Uint64() (uint64, error) { - return uint64(cmd.val), cmd.err -} - -func (cmd *IntCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *IntCmd) readReply(rd *proto.Reader) (err error) { - cmd.val, err = rd.ReadInt() - return err -} - -//------------------------------------------------------------------------------ - -type IntSliceCmd struct { - baseCmd - - val []int64 -} - -var _ Cmder = (*IntSliceCmd)(nil) - -func NewIntSliceCmd(ctx context.Context, args ...interface{}) *IntSliceCmd { - return &IntSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *IntSliceCmd) SetVal(val []int64) { - cmd.val = val -} - -func (cmd *IntSliceCmd) Val() []int64 { - return cmd.val -} - -func (cmd *IntSliceCmd) Result() ([]int64, error) { - return cmd.val, cmd.err -} - -func (cmd *IntSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *IntSliceCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]int64, n) - for i := 0; i < len(cmd.val); i++ { - if cmd.val[i], err = rd.ReadInt(); err != nil { - return err - } - } - return nil -} - -//------------------------------------------------------------------------------ - -type DurationCmd struct { - baseCmd - - val time.Duration - precision time.Duration -} - -var _ Cmder = (*DurationCmd)(nil) - -func NewDurationCmd(ctx context.Context, precision time.Duration, args ...interface{}) *DurationCmd { - return &DurationCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - precision: precision, - } -} - -func (cmd *DurationCmd) SetVal(val time.Duration) { - cmd.val = val -} - -func (cmd *DurationCmd) Val() time.Duration { - return cmd.val -} - -func (cmd *DurationCmd) Result() (time.Duration, error) { - return cmd.val, cmd.err -} - -func (cmd *DurationCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *DurationCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadInt() - if err != nil { - return err - } - switch n { - // -2 if the key does not exist - // -1 if the key exists but has no associated expire - case -2, -1: - cmd.val = time.Duration(n) - default: - cmd.val = time.Duration(n) * cmd.precision - } - return nil -} - -//------------------------------------------------------------------------------ - -type TimeCmd struct { - baseCmd - - val time.Time -} - -var _ Cmder = (*TimeCmd)(nil) - -func NewTimeCmd(ctx context.Context, args ...interface{}) *TimeCmd { - return &TimeCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *TimeCmd) SetVal(val time.Time) { - cmd.val = val -} - -func (cmd *TimeCmd) Val() time.Time { - return cmd.val -} - -func (cmd *TimeCmd) Result() (time.Time, error) { - return cmd.val, cmd.err -} - -func (cmd *TimeCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *TimeCmd) readReply(rd *proto.Reader) error { - if err := rd.ReadFixedArrayLen(2); err != nil { - return err - } - second, err := rd.ReadInt() - if err != nil { - return err - } - microsecond, err := rd.ReadInt() - if err != nil { - return err - } - cmd.val = time.Unix(second, microsecond*1000) - return nil -} - -//------------------------------------------------------------------------------ - -type BoolCmd struct { - baseCmd - - val bool -} - -var _ Cmder = (*BoolCmd)(nil) - -func NewBoolCmd(ctx context.Context, args ...interface{}) *BoolCmd { - return &BoolCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *BoolCmd) SetVal(val bool) { - cmd.val = val -} - -func (cmd *BoolCmd) Val() bool { - return cmd.val -} - -func (cmd *BoolCmd) Result() (bool, error) { - return cmd.val, cmd.err -} - -func (cmd *BoolCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *BoolCmd) readReply(rd *proto.Reader) (err error) { - cmd.val, err = rd.ReadBool() - - // `SET key value NX` returns nil when key already exists. But - // `SETNX key value` returns bool (0/1). So convert nil to bool. - if err == Nil { - cmd.val = false - err = nil - } - return err -} - -//------------------------------------------------------------------------------ - -type StringCmd struct { - baseCmd - - val string -} - -var _ Cmder = (*StringCmd)(nil) - -func NewStringCmd(ctx context.Context, args ...interface{}) *StringCmd { - return &StringCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *StringCmd) SetVal(val string) { - cmd.val = val -} - -func (cmd *StringCmd) Val() string { - return cmd.val -} - -func (cmd *StringCmd) Result() (string, error) { - return cmd.val, cmd.err -} - -func (cmd *StringCmd) Bytes() ([]byte, error) { - return util.StringToBytes(cmd.val), cmd.err -} - -func (cmd *StringCmd) Bool() (bool, error) { - if cmd.err != nil { - return false, cmd.err - } - return strconv.ParseBool(cmd.val) -} - -func (cmd *StringCmd) Int() (int, error) { - if cmd.err != nil { - return 0, cmd.err - } - return strconv.Atoi(cmd.Val()) -} - -func (cmd *StringCmd) Int64() (int64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return strconv.ParseInt(cmd.Val(), 10, 64) -} - -func (cmd *StringCmd) Uint64() (uint64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return strconv.ParseUint(cmd.Val(), 10, 64) -} - -func (cmd *StringCmd) Float32() (float32, error) { - if cmd.err != nil { - return 0, cmd.err - } - f, err := strconv.ParseFloat(cmd.Val(), 32) - if err != nil { - return 0, err - } - return float32(f), nil -} - -func (cmd *StringCmd) Float64() (float64, error) { - if cmd.err != nil { - return 0, cmd.err - } - return strconv.ParseFloat(cmd.Val(), 64) -} - -func (cmd *StringCmd) Time() (time.Time, error) { - if cmd.err != nil { - return time.Time{}, cmd.err - } - return time.Parse(time.RFC3339Nano, cmd.Val()) -} - -func (cmd *StringCmd) Scan(val interface{}) error { - if cmd.err != nil { - return cmd.err - } - return proto.Scan([]byte(cmd.val), val) -} - -func (cmd *StringCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StringCmd) readReply(rd *proto.Reader) (err error) { - cmd.val, err = rd.ReadString() - return err -} - -//------------------------------------------------------------------------------ - -type FloatCmd struct { - baseCmd - - val float64 -} - -var _ Cmder = (*FloatCmd)(nil) - -func NewFloatCmd(ctx context.Context, args ...interface{}) *FloatCmd { - return &FloatCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *FloatCmd) SetVal(val float64) { - cmd.val = val -} - -func (cmd *FloatCmd) Val() float64 { - return cmd.val -} - -func (cmd *FloatCmd) Result() (float64, error) { - return cmd.val, cmd.err -} - -func (cmd *FloatCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *FloatCmd) readReply(rd *proto.Reader) (err error) { - cmd.val, err = rd.ReadFloat() - return err -} - -//------------------------------------------------------------------------------ - -type FloatSliceCmd struct { - baseCmd - - val []float64 -} - -var _ Cmder = (*FloatSliceCmd)(nil) - -func NewFloatSliceCmd(ctx context.Context, args ...interface{}) *FloatSliceCmd { - return &FloatSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *FloatSliceCmd) SetVal(val []float64) { - cmd.val = val -} - -func (cmd *FloatSliceCmd) Val() []float64 { - return cmd.val -} - -func (cmd *FloatSliceCmd) Result() ([]float64, error) { - return cmd.val, cmd.err -} - -func (cmd *FloatSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *FloatSliceCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - cmd.val = make([]float64, n) - for i := 0; i < len(cmd.val); i++ { - switch num, err := rd.ReadFloat(); { - case err == Nil: - cmd.val[i] = 0 - case err != nil: - return err - default: - cmd.val[i] = num - } - } - return nil -} - -//------------------------------------------------------------------------------ - -type StringSliceCmd struct { - baseCmd - - val []string -} - -var _ Cmder = (*StringSliceCmd)(nil) - -func NewStringSliceCmd(ctx context.Context, args ...interface{}) *StringSliceCmd { - return &StringSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *StringSliceCmd) SetVal(val []string) { - cmd.val = val -} - -func (cmd *StringSliceCmd) Val() []string { - return cmd.val -} - -func (cmd *StringSliceCmd) Result() ([]string, error) { - return cmd.val, cmd.err -} - -func (cmd *StringSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StringSliceCmd) ScanSlice(container interface{}) error { - return proto.ScanSlice(cmd.Val(), container) -} - -func (cmd *StringSliceCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]string, n) - for i := 0; i < len(cmd.val); i++ { - switch s, err := rd.ReadString(); { - case err == Nil: - cmd.val[i] = "" - case err != nil: - return err - default: - cmd.val[i] = s - } - } - return nil -} - -//------------------------------------------------------------------------------ - -type KeyValue struct { - Key string - Value string -} - -type KeyValueSliceCmd struct { - baseCmd - - val []KeyValue -} - -var _ Cmder = (*KeyValueSliceCmd)(nil) - -func NewKeyValueSliceCmd(ctx context.Context, args ...interface{}) *KeyValueSliceCmd { - return &KeyValueSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *KeyValueSliceCmd) SetVal(val []KeyValue) { - cmd.val = val -} - -func (cmd *KeyValueSliceCmd) Val() []KeyValue { - return cmd.val -} - -func (cmd *KeyValueSliceCmd) Result() ([]KeyValue, error) { - return cmd.val, cmd.err -} - -func (cmd *KeyValueSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -// Many commands will respond to two formats: -// 1. 1) "one" -// 2. (double) 1 -// 2. 1) "two" -// 2. (double) 2 -// -// OR: -// 1. "two" -// 2. (double) 2 -// 3. "one" -// 4. (double) 1 -func (cmd *KeyValueSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - // If the n is 0, can't continue reading. - if n == 0 { - cmd.val = make([]KeyValue, 0) - return nil - } - - typ, err := rd.PeekReplyType() - if err != nil { - return err - } - array := typ == proto.RespArray - - if array { - cmd.val = make([]KeyValue, n) - } else { - cmd.val = make([]KeyValue, n/2) - } - - for i := 0; i < len(cmd.val); i++ { - if array { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - } - - if cmd.val[i].Key, err = rd.ReadString(); err != nil { - return err - } - - if cmd.val[i].Value, err = rd.ReadString(); err != nil { - return err - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type BoolSliceCmd struct { - baseCmd - - val []bool -} - -var _ Cmder = (*BoolSliceCmd)(nil) - -func NewBoolSliceCmd(ctx context.Context, args ...interface{}) *BoolSliceCmd { - return &BoolSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *BoolSliceCmd) SetVal(val []bool) { - cmd.val = val -} - -func (cmd *BoolSliceCmd) Val() []bool { - return cmd.val -} - -func (cmd *BoolSliceCmd) Result() ([]bool, error) { - return cmd.val, cmd.err -} - -func (cmd *BoolSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *BoolSliceCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]bool, n) - for i := 0; i < len(cmd.val); i++ { - if cmd.val[i], err = rd.ReadBool(); err != nil { - return err - } - } - return nil -} - -//------------------------------------------------------------------------------ - -type MapStringStringCmd struct { - baseCmd - - val map[string]string -} - -var _ Cmder = (*MapStringStringCmd)(nil) - -func NewMapStringStringCmd(ctx context.Context, args ...interface{}) *MapStringStringCmd { - return &MapStringStringCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *MapStringStringCmd) Val() map[string]string { - return cmd.val -} - -func (cmd *MapStringStringCmd) SetVal(val map[string]string) { - cmd.val = val -} - -func (cmd *MapStringStringCmd) Result() (map[string]string, error) { - return cmd.val, cmd.err -} - -func (cmd *MapStringStringCmd) String() string { - return cmdString(cmd, cmd.val) -} - -// Scan scans the results from the map into a destination struct. The map keys -// are matched in the Redis struct fields by the `redis:"field"` tag. -func (cmd *MapStringStringCmd) Scan(dest interface{}) error { - if cmd.err != nil { - return cmd.err - } - - strct, err := hscan.Struct(dest) - if err != nil { - return err - } - - for k, v := range cmd.val { - if err := strct.Scan(k, v); err != nil { - return err - } - } - - return nil -} - -func (cmd *MapStringStringCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadMapLen() - if err != nil { - return err - } - - cmd.val = make(map[string]string, n) - for i := 0; i < n; i++ { - key, err := rd.ReadString() - if err != nil { - return err - } - - value, err := rd.ReadString() - if err != nil { - return err - } - - cmd.val[key] = value - } - return nil -} - -//------------------------------------------------------------------------------ - -type MapStringIntCmd struct { - baseCmd - - val map[string]int64 -} - -var _ Cmder = (*MapStringIntCmd)(nil) - -func NewMapStringIntCmd(ctx context.Context, args ...interface{}) *MapStringIntCmd { - return &MapStringIntCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *MapStringIntCmd) SetVal(val map[string]int64) { - cmd.val = val -} - -func (cmd *MapStringIntCmd) Val() map[string]int64 { - return cmd.val -} - -func (cmd *MapStringIntCmd) Result() (map[string]int64, error) { - return cmd.val, cmd.err -} - -func (cmd *MapStringIntCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *MapStringIntCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadMapLen() - if err != nil { - return err - } - - cmd.val = make(map[string]int64, n) - for i := 0; i < n; i++ { - key, err := rd.ReadString() - if err != nil { - return err - } - - nn, err := rd.ReadInt() - if err != nil { - return err - } - cmd.val[key] = nn - } - return nil -} - -// ------------------------------------------------------------------------------ -type MapStringSliceInterfaceCmd struct { - baseCmd - val map[string][]interface{} -} - -func NewMapStringSliceInterfaceCmd(ctx context.Context, args ...interface{}) *MapStringSliceInterfaceCmd { - return &MapStringSliceInterfaceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *MapStringSliceInterfaceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *MapStringSliceInterfaceCmd) SetVal(val map[string][]interface{}) { - cmd.val = val -} - -func (cmd *MapStringSliceInterfaceCmd) Result() (map[string][]interface{}, error) { - return cmd.val, cmd.err -} - -func (cmd *MapStringSliceInterfaceCmd) Val() map[string][]interface{} { - return cmd.val -} - -func (cmd *MapStringSliceInterfaceCmd) readReply(rd *proto.Reader) (err error) { - readType, err := rd.PeekReplyType() - if err != nil { - return err - } - - cmd.val = make(map[string][]interface{}) - - switch readType { - case proto.RespMap: - n, err := rd.ReadMapLen() - if err != nil { - return err - } - for i := 0; i < n; i++ { - k, err := rd.ReadString() - if err != nil { - return err - } - nn, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val[k] = make([]interface{}, nn) - for j := 0; j < nn; j++ { - value, err := rd.ReadReply() - if err != nil { - return err - } - cmd.val[k][j] = value - } - } - case proto.RespArray: - // RESP2 response - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - for i := 0; i < n; i++ { - // Each entry in this array is itself an array with key details - itemLen, err := rd.ReadArrayLen() - if err != nil { - return err - } - - key, err := rd.ReadString() - if err != nil { - return err - } - cmd.val[key] = make([]interface{}, 0, itemLen-1) - for j := 1; j < itemLen; j++ { - // Read the inner array for timestamp-value pairs - data, err := rd.ReadReply() - if err != nil { - return err - } - cmd.val[key] = append(cmd.val[key], data) - } - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type StringStructMapCmd struct { - baseCmd - - val map[string]struct{} -} - -var _ Cmder = (*StringStructMapCmd)(nil) - -func NewStringStructMapCmd(ctx context.Context, args ...interface{}) *StringStructMapCmd { - return &StringStructMapCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *StringStructMapCmd) SetVal(val map[string]struct{}) { - cmd.val = val -} - -func (cmd *StringStructMapCmd) Val() map[string]struct{} { - return cmd.val -} - -func (cmd *StringStructMapCmd) Result() (map[string]struct{}, error) { - return cmd.val, cmd.err -} - -func (cmd *StringStructMapCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *StringStructMapCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - cmd.val = make(map[string]struct{}, n) - for i := 0; i < n; i++ { - key, err := rd.ReadString() - if err != nil { - return err - } - cmd.val[key] = struct{}{} - } - return nil -} - -//------------------------------------------------------------------------------ - -type XMessage struct { - ID string - Values map[string]interface{} -} - -type XMessageSliceCmd struct { - baseCmd - - val []XMessage -} - -var _ Cmder = (*XMessageSliceCmd)(nil) - -func NewXMessageSliceCmd(ctx context.Context, args ...interface{}) *XMessageSliceCmd { - return &XMessageSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *XMessageSliceCmd) SetVal(val []XMessage) { - cmd.val = val -} - -func (cmd *XMessageSliceCmd) Val() []XMessage { - return cmd.val -} - -func (cmd *XMessageSliceCmd) Result() ([]XMessage, error) { - return cmd.val, cmd.err -} - -func (cmd *XMessageSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XMessageSliceCmd) readReply(rd *proto.Reader) (err error) { - cmd.val, err = readXMessageSlice(rd) - return err -} - -func readXMessageSlice(rd *proto.Reader) ([]XMessage, error) { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - - msgs := make([]XMessage, n) - for i := 0; i < len(msgs); i++ { - if msgs[i], err = readXMessage(rd); err != nil { - return nil, err - } - } - return msgs, nil -} - -func readXMessage(rd *proto.Reader) (XMessage, error) { - if err := rd.ReadFixedArrayLen(2); err != nil { - return XMessage{}, err - } - - id, err := rd.ReadString() - if err != nil { - return XMessage{}, err - } - - v, err := stringInterfaceMapParser(rd) - if err != nil { - if err != proto.Nil { - return XMessage{}, err - } - } - - return XMessage{ - ID: id, - Values: v, - }, nil -} - -func stringInterfaceMapParser(rd *proto.Reader) (map[string]interface{}, error) { - n, err := rd.ReadMapLen() - if err != nil { - return nil, err - } - - m := make(map[string]interface{}, n) - for i := 0; i < n; i++ { - key, err := rd.ReadString() - if err != nil { - return nil, err - } - - value, err := rd.ReadString() - if err != nil { - return nil, err - } - - m[key] = value - } - return m, nil -} - -//------------------------------------------------------------------------------ - -type XStream struct { - Stream string - Messages []XMessage -} - -type XStreamSliceCmd struct { - baseCmd - - val []XStream -} - -var _ Cmder = (*XStreamSliceCmd)(nil) - -func NewXStreamSliceCmd(ctx context.Context, args ...interface{}) *XStreamSliceCmd { - return &XStreamSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *XStreamSliceCmd) SetVal(val []XStream) { - cmd.val = val -} - -func (cmd *XStreamSliceCmd) Val() []XStream { - return cmd.val -} - -func (cmd *XStreamSliceCmd) Result() ([]XStream, error) { - return cmd.val, cmd.err -} - -func (cmd *XStreamSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XStreamSliceCmd) readReply(rd *proto.Reader) error { - typ, err := rd.PeekReplyType() - if err != nil { - return err - } - - var n int - if typ == proto.RespMap { - n, err = rd.ReadMapLen() - } else { - n, err = rd.ReadArrayLen() - } - if err != nil { - return err - } - cmd.val = make([]XStream, n) - for i := 0; i < len(cmd.val); i++ { - if typ != proto.RespMap { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - } - if cmd.val[i].Stream, err = rd.ReadString(); err != nil { - return err - } - if cmd.val[i].Messages, err = readXMessageSlice(rd); err != nil { - return err - } - } - return nil -} - -//------------------------------------------------------------------------------ - -type XPending struct { - Count int64 - Lower string - Higher string - Consumers map[string]int64 -} - -type XPendingCmd struct { - baseCmd - val *XPending -} - -var _ Cmder = (*XPendingCmd)(nil) - -func NewXPendingCmd(ctx context.Context, args ...interface{}) *XPendingCmd { - return &XPendingCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *XPendingCmd) SetVal(val *XPending) { - cmd.val = val -} - -func (cmd *XPendingCmd) Val() *XPending { - return cmd.val -} - -func (cmd *XPendingCmd) Result() (*XPending, error) { - return cmd.val, cmd.err -} - -func (cmd *XPendingCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XPendingCmd) readReply(rd *proto.Reader) error { - var err error - if err = rd.ReadFixedArrayLen(4); err != nil { - return err - } - cmd.val = &XPending{} - - if cmd.val.Count, err = rd.ReadInt(); err != nil { - return err - } - - if cmd.val.Lower, err = rd.ReadString(); err != nil && err != Nil { - return err - } - - if cmd.val.Higher, err = rd.ReadString(); err != nil && err != Nil { - return err - } - - n, err := rd.ReadArrayLen() - if err != nil && err != Nil { - return err - } - cmd.val.Consumers = make(map[string]int64, n) - for i := 0; i < n; i++ { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - - consumerName, err := rd.ReadString() - if err != nil { - return err - } - consumerPending, err := rd.ReadInt() - if err != nil { - return err - } - cmd.val.Consumers[consumerName] = consumerPending - } - return nil -} - -//------------------------------------------------------------------------------ - -type XPendingExt struct { - ID string - Consumer string - Idle time.Duration - RetryCount int64 -} - -type XPendingExtCmd struct { - baseCmd - val []XPendingExt -} - -var _ Cmder = (*XPendingExtCmd)(nil) - -func NewXPendingExtCmd(ctx context.Context, args ...interface{}) *XPendingExtCmd { - return &XPendingExtCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *XPendingExtCmd) SetVal(val []XPendingExt) { - cmd.val = val -} - -func (cmd *XPendingExtCmd) Val() []XPendingExt { - return cmd.val -} - -func (cmd *XPendingExtCmd) Result() ([]XPendingExt, error) { - return cmd.val, cmd.err -} - -func (cmd *XPendingExtCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XPendingExtCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]XPendingExt, n) - - for i := 0; i < len(cmd.val); i++ { - if err = rd.ReadFixedArrayLen(4); err != nil { - return err - } - - if cmd.val[i].ID, err = rd.ReadString(); err != nil { - return err - } - - if cmd.val[i].Consumer, err = rd.ReadString(); err != nil && err != Nil { - return err - } - - idle, err := rd.ReadInt() - if err != nil && err != Nil { - return err - } - cmd.val[i].Idle = time.Duration(idle) * time.Millisecond - - if cmd.val[i].RetryCount, err = rd.ReadInt(); err != nil && err != Nil { - return err - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type XAutoClaimCmd struct { - baseCmd - - start string - val []XMessage -} - -var _ Cmder = (*XAutoClaimCmd)(nil) - -func NewXAutoClaimCmd(ctx context.Context, args ...interface{}) *XAutoClaimCmd { - return &XAutoClaimCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *XAutoClaimCmd) SetVal(val []XMessage, start string) { - cmd.val = val - cmd.start = start -} - -func (cmd *XAutoClaimCmd) Val() (messages []XMessage, start string) { - return cmd.val, cmd.start -} - -func (cmd *XAutoClaimCmd) Result() (messages []XMessage, start string, err error) { - return cmd.val, cmd.start, cmd.err -} - -func (cmd *XAutoClaimCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XAutoClaimCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - switch n { - case 2, // Redis 6 - 3: // Redis 7: - // ok - default: - return fmt.Errorf("redis: got %d elements in XAutoClaim reply, wanted 2/3", n) - } - - cmd.start, err = rd.ReadString() - if err != nil { - return err - } - - cmd.val, err = readXMessageSlice(rd) - if err != nil { - return err - } - - if n >= 3 { - if err := rd.DiscardNext(); err != nil { - return err - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type XAutoClaimJustIDCmd struct { - baseCmd - - start string - val []string -} - -var _ Cmder = (*XAutoClaimJustIDCmd)(nil) - -func NewXAutoClaimJustIDCmd(ctx context.Context, args ...interface{}) *XAutoClaimJustIDCmd { - return &XAutoClaimJustIDCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *XAutoClaimJustIDCmd) SetVal(val []string, start string) { - cmd.val = val - cmd.start = start -} - -func (cmd *XAutoClaimJustIDCmd) Val() (ids []string, start string) { - return cmd.val, cmd.start -} - -func (cmd *XAutoClaimJustIDCmd) Result() (ids []string, start string, err error) { - return cmd.val, cmd.start, cmd.err -} - -func (cmd *XAutoClaimJustIDCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XAutoClaimJustIDCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - switch n { - case 2, // Redis 6 - 3: // Redis 7: - // ok - default: - return fmt.Errorf("redis: got %d elements in XAutoClaimJustID reply, wanted 2/3", n) - } - - cmd.start, err = rd.ReadString() - if err != nil { - return err - } - - nn, err := rd.ReadArrayLen() - if err != nil { - return err - } - - cmd.val = make([]string, nn) - for i := 0; i < nn; i++ { - cmd.val[i], err = rd.ReadString() - if err != nil { - return err - } - } - - if n >= 3 { - if err := rd.DiscardNext(); err != nil { - return err - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type XInfoConsumersCmd struct { - baseCmd - val []XInfoConsumer -} - -type XInfoConsumer struct { - Name string - Pending int64 - Idle time.Duration - Inactive time.Duration -} - -var _ Cmder = (*XInfoConsumersCmd)(nil) - -func NewXInfoConsumersCmd(ctx context.Context, stream string, group string) *XInfoConsumersCmd { - return &XInfoConsumersCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: []interface{}{"xinfo", "consumers", stream, group}, - }, - } -} - -func (cmd *XInfoConsumersCmd) SetVal(val []XInfoConsumer) { - cmd.val = val -} - -func (cmd *XInfoConsumersCmd) Val() []XInfoConsumer { - return cmd.val -} - -func (cmd *XInfoConsumersCmd) Result() ([]XInfoConsumer, error) { - return cmd.val, cmd.err -} - -func (cmd *XInfoConsumersCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XInfoConsumersCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]XInfoConsumer, n) - - for i := 0; i < len(cmd.val); i++ { - nn, err := rd.ReadMapLen() - if err != nil { - return err - } - - var key string - for f := 0; f < nn; f++ { - key, err = rd.ReadString() - if err != nil { - return err - } - - switch key { - case "name": - cmd.val[i].Name, err = rd.ReadString() - case "pending": - cmd.val[i].Pending, err = rd.ReadInt() - case "idle": - var idle int64 - idle, err = rd.ReadInt() - cmd.val[i].Idle = time.Duration(idle) * time.Millisecond - case "inactive": - var inactive int64 - inactive, err = rd.ReadInt() - cmd.val[i].Inactive = time.Duration(inactive) * time.Millisecond - default: - return fmt.Errorf("redis: unexpected content %s in XINFO CONSUMERS reply", key) - } - if err != nil { - return err - } - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type XInfoGroupsCmd struct { - baseCmd - val []XInfoGroup -} - -type XInfoGroup struct { - Name string - Consumers int64 - Pending int64 - LastDeliveredID string - EntriesRead int64 - // Lag represents the number of pending messages in the stream not yet - // delivered to this consumer group. Returns -1 when the lag cannot be determined. - Lag int64 -} - -var _ Cmder = (*XInfoGroupsCmd)(nil) - -func NewXInfoGroupsCmd(ctx context.Context, stream string) *XInfoGroupsCmd { - return &XInfoGroupsCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: []interface{}{"xinfo", "groups", stream}, - }, - } -} - -func (cmd *XInfoGroupsCmd) SetVal(val []XInfoGroup) { - cmd.val = val -} - -func (cmd *XInfoGroupsCmd) Val() []XInfoGroup { - return cmd.val -} - -func (cmd *XInfoGroupsCmd) Result() ([]XInfoGroup, error) { - return cmd.val, cmd.err -} - -func (cmd *XInfoGroupsCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XInfoGroupsCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]XInfoGroup, n) - - for i := 0; i < len(cmd.val); i++ { - group := &cmd.val[i] - - nn, err := rd.ReadMapLen() - if err != nil { - return err - } - - var key string - for j := 0; j < nn; j++ { - key, err = rd.ReadString() - if err != nil { - return err - } - - switch key { - case "name": - group.Name, err = rd.ReadString() - if err != nil { - return err - } - case "consumers": - group.Consumers, err = rd.ReadInt() - if err != nil { - return err - } - case "pending": - group.Pending, err = rd.ReadInt() - if err != nil { - return err - } - case "last-delivered-id": - group.LastDeliveredID, err = rd.ReadString() - if err != nil { - return err - } - case "entries-read": - group.EntriesRead, err = rd.ReadInt() - if err != nil && err != Nil { - return err - } - case "lag": - group.Lag, err = rd.ReadInt() - - // lag: the number of entries in the stream that are still waiting to be delivered - // to the group's consumers, or a NULL(Nil) when that number can't be determined. - // In that case, we return -1. - if err != nil && err != Nil { - return err - } else if err == Nil { - group.Lag = -1 - } - default: - return fmt.Errorf("redis: unexpected key %q in XINFO GROUPS reply", key) - } - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type XInfoStreamCmd struct { - baseCmd - val *XInfoStream -} - -type XInfoStream struct { - Length int64 - RadixTreeKeys int64 - RadixTreeNodes int64 - Groups int64 - LastGeneratedID string - MaxDeletedEntryID string - EntriesAdded int64 - FirstEntry XMessage - LastEntry XMessage - RecordedFirstEntryID string -} - -var _ Cmder = (*XInfoStreamCmd)(nil) - -func NewXInfoStreamCmd(ctx context.Context, stream string) *XInfoStreamCmd { - return &XInfoStreamCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: []interface{}{"xinfo", "stream", stream}, - }, - } -} - -func (cmd *XInfoStreamCmd) SetVal(val *XInfoStream) { - cmd.val = val -} - -func (cmd *XInfoStreamCmd) Val() *XInfoStream { - return cmd.val -} - -func (cmd *XInfoStreamCmd) Result() (*XInfoStream, error) { - return cmd.val, cmd.err -} - -func (cmd *XInfoStreamCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XInfoStreamCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadMapLen() - if err != nil { - return err - } - cmd.val = &XInfoStream{} - - for i := 0; i < n; i++ { - key, err := rd.ReadString() - if err != nil { - return err - } - switch key { - case "length": - cmd.val.Length, err = rd.ReadInt() - if err != nil { - return err - } - case "radix-tree-keys": - cmd.val.RadixTreeKeys, err = rd.ReadInt() - if err != nil { - return err - } - case "radix-tree-nodes": - cmd.val.RadixTreeNodes, err = rd.ReadInt() - if err != nil { - return err - } - case "groups": - cmd.val.Groups, err = rd.ReadInt() - if err != nil { - return err - } - case "last-generated-id": - cmd.val.LastGeneratedID, err = rd.ReadString() - if err != nil { - return err - } - case "max-deleted-entry-id": - cmd.val.MaxDeletedEntryID, err = rd.ReadString() - if err != nil { - return err - } - case "entries-added": - cmd.val.EntriesAdded, err = rd.ReadInt() - if err != nil { - return err - } - case "first-entry": - cmd.val.FirstEntry, err = readXMessage(rd) - if err != nil && err != Nil { - return err - } - case "last-entry": - cmd.val.LastEntry, err = readXMessage(rd) - if err != nil && err != Nil { - return err - } - case "recorded-first-entry-id": - cmd.val.RecordedFirstEntryID, err = rd.ReadString() - if err != nil { - return err - } - default: - return fmt.Errorf("redis: unexpected key %q in XINFO STREAM reply", key) - } - } - return nil -} - -//------------------------------------------------------------------------------ - -type XInfoStreamFullCmd struct { - baseCmd - val *XInfoStreamFull -} - -type XInfoStreamFull struct { - Length int64 - RadixTreeKeys int64 - RadixTreeNodes int64 - LastGeneratedID string - MaxDeletedEntryID string - EntriesAdded int64 - Entries []XMessage - Groups []XInfoStreamGroup - RecordedFirstEntryID string -} - -type XInfoStreamGroup struct { - Name string - LastDeliveredID string - EntriesRead int64 - Lag int64 - PelCount int64 - Pending []XInfoStreamGroupPending - Consumers []XInfoStreamConsumer -} - -type XInfoStreamGroupPending struct { - ID string - Consumer string - DeliveryTime time.Time - DeliveryCount int64 -} - -type XInfoStreamConsumer struct { - Name string - SeenTime time.Time - ActiveTime time.Time - PelCount int64 - Pending []XInfoStreamConsumerPending -} - -type XInfoStreamConsumerPending struct { - ID string - DeliveryTime time.Time - DeliveryCount int64 -} - -var _ Cmder = (*XInfoStreamFullCmd)(nil) - -func NewXInfoStreamFullCmd(ctx context.Context, args ...interface{}) *XInfoStreamFullCmd { - return &XInfoStreamFullCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *XInfoStreamFullCmd) SetVal(val *XInfoStreamFull) { - cmd.val = val -} - -func (cmd *XInfoStreamFullCmd) Val() *XInfoStreamFull { - return cmd.val -} - -func (cmd *XInfoStreamFullCmd) Result() (*XInfoStreamFull, error) { - return cmd.val, cmd.err -} - -func (cmd *XInfoStreamFullCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *XInfoStreamFullCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadMapLen() - if err != nil { - return err - } - - cmd.val = &XInfoStreamFull{} - - for i := 0; i < n; i++ { - key, err := rd.ReadString() - if err != nil { - return err - } - - switch key { - case "length": - cmd.val.Length, err = rd.ReadInt() - if err != nil { - return err - } - case "radix-tree-keys": - cmd.val.RadixTreeKeys, err = rd.ReadInt() - if err != nil { - return err - } - case "radix-tree-nodes": - cmd.val.RadixTreeNodes, err = rd.ReadInt() - if err != nil { - return err - } - case "last-generated-id": - cmd.val.LastGeneratedID, err = rd.ReadString() - if err != nil { - return err - } - case "entries-added": - cmd.val.EntriesAdded, err = rd.ReadInt() - if err != nil { - return err - } - case "entries": - cmd.val.Entries, err = readXMessageSlice(rd) - if err != nil { - return err - } - case "groups": - cmd.val.Groups, err = readStreamGroups(rd) - if err != nil { - return err - } - case "max-deleted-entry-id": - cmd.val.MaxDeletedEntryID, err = rd.ReadString() - if err != nil { - return err - } - case "recorded-first-entry-id": - cmd.val.RecordedFirstEntryID, err = rd.ReadString() - if err != nil { - return err - } - default: - return fmt.Errorf("redis: unexpected key %q in XINFO STREAM FULL reply", key) - } - } - return nil -} - -func readStreamGroups(rd *proto.Reader) ([]XInfoStreamGroup, error) { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - groups := make([]XInfoStreamGroup, 0, n) - for i := 0; i < n; i++ { - nn, err := rd.ReadMapLen() - if err != nil { - return nil, err - } - - group := XInfoStreamGroup{} - - for j := 0; j < nn; j++ { - key, err := rd.ReadString() - if err != nil { - return nil, err - } - - switch key { - case "name": - group.Name, err = rd.ReadString() - if err != nil { - return nil, err - } - case "last-delivered-id": - group.LastDeliveredID, err = rd.ReadString() - if err != nil { - return nil, err - } - case "entries-read": - group.EntriesRead, err = rd.ReadInt() - if err != nil && err != Nil { - return nil, err - } - case "lag": - // lag: the number of entries in the stream that are still waiting to be delivered - // to the group's consumers, or a NULL(Nil) when that number can't be determined. - group.Lag, err = rd.ReadInt() - if err != nil && err != Nil { - return nil, err - } - case "pel-count": - group.PelCount, err = rd.ReadInt() - if err != nil { - return nil, err - } - case "pending": - group.Pending, err = readXInfoStreamGroupPending(rd) - if err != nil { - return nil, err - } - case "consumers": - group.Consumers, err = readXInfoStreamConsumers(rd) - if err != nil { - return nil, err - } - default: - return nil, fmt.Errorf("redis: unexpected key %q in XINFO STREAM FULL reply", key) - } - } - - groups = append(groups, group) - } - - return groups, nil -} - -func readXInfoStreamGroupPending(rd *proto.Reader) ([]XInfoStreamGroupPending, error) { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - - pending := make([]XInfoStreamGroupPending, 0, n) - - for i := 0; i < n; i++ { - if err = rd.ReadFixedArrayLen(4); err != nil { - return nil, err - } - - p := XInfoStreamGroupPending{} - - p.ID, err = rd.ReadString() - if err != nil { - return nil, err - } - - p.Consumer, err = rd.ReadString() - if err != nil { - return nil, err - } - - delivery, err := rd.ReadInt() - if err != nil { - return nil, err - } - p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond)) - - p.DeliveryCount, err = rd.ReadInt() - if err != nil { - return nil, err - } - - pending = append(pending, p) - } - - return pending, nil -} - -func readXInfoStreamConsumers(rd *proto.Reader) ([]XInfoStreamConsumer, error) { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - - consumers := make([]XInfoStreamConsumer, 0, n) - - for i := 0; i < n; i++ { - nn, err := rd.ReadMapLen() - if err != nil { - return nil, err - } - - c := XInfoStreamConsumer{} - - for f := 0; f < nn; f++ { - cKey, err := rd.ReadString() - if err != nil { - return nil, err - } - - switch cKey { - case "name": - c.Name, err = rd.ReadString() - case "seen-time": - seen, err := rd.ReadInt() - if err != nil { - return nil, err - } - c.SeenTime = time.UnixMilli(seen) - case "active-time": - active, err := rd.ReadInt() - if err != nil { - return nil, err - } - c.ActiveTime = time.UnixMilli(active) - case "pel-count": - c.PelCount, err = rd.ReadInt() - case "pending": - pendingNumber, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - - c.Pending = make([]XInfoStreamConsumerPending, 0, pendingNumber) - - for pn := 0; pn < pendingNumber; pn++ { - if err = rd.ReadFixedArrayLen(3); err != nil { - return nil, err - } - - p := XInfoStreamConsumerPending{} - - p.ID, err = rd.ReadString() - if err != nil { - return nil, err - } - - delivery, err := rd.ReadInt() - if err != nil { - return nil, err - } - p.DeliveryTime = time.Unix(delivery/1000, delivery%1000*int64(time.Millisecond)) - - p.DeliveryCount, err = rd.ReadInt() - if err != nil { - return nil, err - } - - c.Pending = append(c.Pending, p) - } - default: - return nil, fmt.Errorf("redis: unexpected content %s "+ - "in XINFO STREAM FULL reply", cKey) - } - if err != nil { - return nil, err - } - } - consumers = append(consumers, c) - } - - return consumers, nil -} - -//------------------------------------------------------------------------------ - -type ZSliceCmd struct { - baseCmd - - val []Z -} - -var _ Cmder = (*ZSliceCmd)(nil) - -func NewZSliceCmd(ctx context.Context, args ...interface{}) *ZSliceCmd { - return &ZSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *ZSliceCmd) SetVal(val []Z) { - cmd.val = val -} - -func (cmd *ZSliceCmd) Val() []Z { - return cmd.val -} - -func (cmd *ZSliceCmd) Result() ([]Z, error) { - return cmd.val, cmd.err -} - -func (cmd *ZSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ZSliceCmd) readReply(rd *proto.Reader) error { // nolint:dupl - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - // If the n is 0, can't continue reading. - if n == 0 { - cmd.val = make([]Z, 0) - return nil - } - - typ, err := rd.PeekReplyType() - if err != nil { - return err - } - array := typ == proto.RespArray - - if array { - cmd.val = make([]Z, n) - } else { - cmd.val = make([]Z, n/2) - } - - for i := 0; i < len(cmd.val); i++ { - if array { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - } - - if cmd.val[i].Member, err = rd.ReadString(); err != nil { - return err - } - - if cmd.val[i].Score, err = rd.ReadFloat(); err != nil { - return err - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type ZWithKeyCmd struct { - baseCmd - - val *ZWithKey -} - -var _ Cmder = (*ZWithKeyCmd)(nil) - -func NewZWithKeyCmd(ctx context.Context, args ...interface{}) *ZWithKeyCmd { - return &ZWithKeyCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *ZWithKeyCmd) SetVal(val *ZWithKey) { - cmd.val = val -} - -func (cmd *ZWithKeyCmd) Val() *ZWithKey { - return cmd.val -} - -func (cmd *ZWithKeyCmd) Result() (*ZWithKey, error) { - return cmd.val, cmd.err -} - -func (cmd *ZWithKeyCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ZWithKeyCmd) readReply(rd *proto.Reader) (err error) { - if err = rd.ReadFixedArrayLen(3); err != nil { - return err - } - cmd.val = &ZWithKey{} - - if cmd.val.Key, err = rd.ReadString(); err != nil { - return err - } - if cmd.val.Member, err = rd.ReadString(); err != nil { - return err - } - if cmd.val.Score, err = rd.ReadFloat(); err != nil { - return err - } - - return nil -} - -//------------------------------------------------------------------------------ - -type ScanCmd struct { - baseCmd - - page []string - cursor uint64 - - process cmdable -} - -var _ Cmder = (*ScanCmd)(nil) - -func NewScanCmd(ctx context.Context, process cmdable, args ...interface{}) *ScanCmd { - return &ScanCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - process: process, - } -} - -func (cmd *ScanCmd) SetVal(page []string, cursor uint64) { - cmd.page = page - cmd.cursor = cursor -} - -func (cmd *ScanCmd) Val() (keys []string, cursor uint64) { - return cmd.page, cmd.cursor -} - -func (cmd *ScanCmd) Result() (keys []string, cursor uint64, err error) { - return cmd.page, cmd.cursor, cmd.err -} - -func (cmd *ScanCmd) String() string { - return cmdString(cmd, cmd.page) -} - -func (cmd *ScanCmd) readReply(rd *proto.Reader) error { - if err := rd.ReadFixedArrayLen(2); err != nil { - return err - } - - cursor, err := rd.ReadUint() - if err != nil { - return err - } - cmd.cursor = cursor - - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.page = make([]string, n) - - for i := 0; i < len(cmd.page); i++ { - if cmd.page[i], err = rd.ReadString(); err != nil { - return err - } - } - return nil -} - -// Iterator creates a new ScanIterator. -func (cmd *ScanCmd) Iterator() *ScanIterator { - return &ScanIterator{ - cmd: cmd, - } -} - -//------------------------------------------------------------------------------ - -type ClusterNode struct { - ID string - Addr string - NetworkingMetadata map[string]string -} - -type ClusterSlot struct { - Start int - End int - Nodes []ClusterNode -} - -type ClusterSlotsCmd struct { - baseCmd - - val []ClusterSlot -} - -var _ Cmder = (*ClusterSlotsCmd)(nil) - -func NewClusterSlotsCmd(ctx context.Context, args ...interface{}) *ClusterSlotsCmd { - return &ClusterSlotsCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *ClusterSlotsCmd) SetVal(val []ClusterSlot) { - cmd.val = val -} - -func (cmd *ClusterSlotsCmd) Val() []ClusterSlot { - return cmd.val -} - -func (cmd *ClusterSlotsCmd) Result() ([]ClusterSlot, error) { - return cmd.val, cmd.err -} - -func (cmd *ClusterSlotsCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ClusterSlotsCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]ClusterSlot, n) - - for i := 0; i < len(cmd.val); i++ { - n, err = rd.ReadArrayLen() - if err != nil { - return err - } - if n < 2 { - return fmt.Errorf("redis: got %d elements in cluster info, expected at least 2", n) - } - - start, err := rd.ReadInt() - if err != nil { - return err - } - - end, err := rd.ReadInt() - if err != nil { - return err - } - - // subtract start and end. - nodes := make([]ClusterNode, n-2) - - for j := 0; j < len(nodes); j++ { - nn, err := rd.ReadArrayLen() - if err != nil { - return err - } - if nn < 2 || nn > 4 { - return fmt.Errorf("got %d elements in cluster info address, expected 2, 3, or 4", n) - } - - ip, err := rd.ReadString() - if err != nil { - return err - } - - port, err := rd.ReadString() - if err != nil { - return err - } - - nodes[j].Addr = net.JoinHostPort(ip, port) - - if nn >= 3 { - id, err := rd.ReadString() - if err != nil { - return err - } - nodes[j].ID = id - } - - if nn >= 4 { - metadataLength, err := rd.ReadMapLen() - if err != nil { - return err - } - - networkingMetadata := make(map[string]string, metadataLength) - - for i := 0; i < metadataLength; i++ { - key, err := rd.ReadString() - if err != nil { - return err - } - value, err := rd.ReadString() - if err != nil { - return err - } - networkingMetadata[key] = value - } - - nodes[j].NetworkingMetadata = networkingMetadata - } - } - - cmd.val[i] = ClusterSlot{ - Start: int(start), - End: int(end), - Nodes: nodes, - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -// GeoLocation is used with GeoAdd to add geospatial location. -type GeoLocation struct { - Name string - Longitude, Latitude, Dist float64 - GeoHash int64 -} - -// GeoRadiusQuery is used with GeoRadius to query geospatial index. -type GeoRadiusQuery struct { - Radius float64 - // Can be m, km, ft, or mi. Default is km. - Unit string - WithCoord bool - WithDist bool - WithGeoHash bool - Count int - // Can be ASC or DESC. Default is no sort order. - Sort string - Store string - StoreDist string - - // WithCoord+WithDist+WithGeoHash - withLen int -} - -type GeoLocationCmd struct { - baseCmd - - q *GeoRadiusQuery - locations []GeoLocation -} - -var _ Cmder = (*GeoLocationCmd)(nil) - -func NewGeoLocationCmd(ctx context.Context, q *GeoRadiusQuery, args ...interface{}) *GeoLocationCmd { - return &GeoLocationCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: geoLocationArgs(q, args...), - }, - q: q, - } -} - -func geoLocationArgs(q *GeoRadiusQuery, args ...interface{}) []interface{} { - args = append(args, q.Radius) - if q.Unit != "" { - args = append(args, q.Unit) - } else { - args = append(args, "km") - } - if q.WithCoord { - args = append(args, "withcoord") - q.withLen++ - } - if q.WithDist { - args = append(args, "withdist") - q.withLen++ - } - if q.WithGeoHash { - args = append(args, "withhash") - q.withLen++ - } - if q.Count > 0 { - args = append(args, "count", q.Count) - } - if q.Sort != "" { - args = append(args, q.Sort) - } - if q.Store != "" { - args = append(args, "store") - args = append(args, q.Store) - } - if q.StoreDist != "" { - args = append(args, "storedist") - args = append(args, q.StoreDist) - } - return args -} - -func (cmd *GeoLocationCmd) SetVal(locations []GeoLocation) { - cmd.locations = locations -} - -func (cmd *GeoLocationCmd) Val() []GeoLocation { - return cmd.locations -} - -func (cmd *GeoLocationCmd) Result() ([]GeoLocation, error) { - return cmd.locations, cmd.err -} - -func (cmd *GeoLocationCmd) String() string { - return cmdString(cmd, cmd.locations) -} - -func (cmd *GeoLocationCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.locations = make([]GeoLocation, n) - - for i := 0; i < len(cmd.locations); i++ { - // only name - if cmd.q.withLen == 0 { - if cmd.locations[i].Name, err = rd.ReadString(); err != nil { - return err - } - continue - } - - // +name - if err = rd.ReadFixedArrayLen(cmd.q.withLen + 1); err != nil { - return err - } - - if cmd.locations[i].Name, err = rd.ReadString(); err != nil { - return err - } - if cmd.q.WithDist { - if cmd.locations[i].Dist, err = rd.ReadFloat(); err != nil { - return err - } - } - if cmd.q.WithGeoHash { - if cmd.locations[i].GeoHash, err = rd.ReadInt(); err != nil { - return err - } - } - if cmd.q.WithCoord { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - if cmd.locations[i].Longitude, err = rd.ReadFloat(); err != nil { - return err - } - if cmd.locations[i].Latitude, err = rd.ReadFloat(); err != nil { - return err - } - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -// GeoSearchQuery is used for GEOSearch/GEOSearchStore command query. -type GeoSearchQuery struct { - Member string - - // Latitude and Longitude when using FromLonLat option. - Longitude float64 - Latitude float64 - - // Distance and unit when using ByRadius option. - // Can use m, km, ft, or mi. Default is km. - Radius float64 - RadiusUnit string - - // Height, width and unit when using ByBox option. - // Can be m, km, ft, or mi. Default is km. - BoxWidth float64 - BoxHeight float64 - BoxUnit string - - // Can be ASC or DESC. Default is no sort order. - Sort string - Count int - CountAny bool -} - -type GeoSearchLocationQuery struct { - GeoSearchQuery - - WithCoord bool - WithDist bool - WithHash bool -} - -type GeoSearchStoreQuery struct { - GeoSearchQuery - - // When using the StoreDist option, the command stores the items in a - // sorted set populated with their distance from the center of the circle or box, - // as a floating-point number, in the same unit specified for that shape. - StoreDist bool -} - -func geoSearchLocationArgs(q *GeoSearchLocationQuery, args []interface{}) []interface{} { - args = geoSearchArgs(&q.GeoSearchQuery, args) - - if q.WithCoord { - args = append(args, "withcoord") - } - if q.WithDist { - args = append(args, "withdist") - } - if q.WithHash { - args = append(args, "withhash") - } - - return args -} - -func geoSearchArgs(q *GeoSearchQuery, args []interface{}) []interface{} { - if q.Member != "" { - args = append(args, "frommember", q.Member) - } else { - args = append(args, "fromlonlat", q.Longitude, q.Latitude) - } - - if q.Radius > 0 { - if q.RadiusUnit == "" { - q.RadiusUnit = "km" - } - args = append(args, "byradius", q.Radius, q.RadiusUnit) - } else { - if q.BoxUnit == "" { - q.BoxUnit = "km" - } - args = append(args, "bybox", q.BoxWidth, q.BoxHeight, q.BoxUnit) - } - - if q.Sort != "" { - args = append(args, q.Sort) - } - - if q.Count > 0 { - args = append(args, "count", q.Count) - if q.CountAny { - args = append(args, "any") - } - } - - return args -} - -type GeoSearchLocationCmd struct { - baseCmd - - opt *GeoSearchLocationQuery - val []GeoLocation -} - -var _ Cmder = (*GeoSearchLocationCmd)(nil) - -func NewGeoSearchLocationCmd( - ctx context.Context, opt *GeoSearchLocationQuery, args ...interface{}, -) *GeoSearchLocationCmd { - return &GeoSearchLocationCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - opt: opt, - } -} - -func (cmd *GeoSearchLocationCmd) SetVal(val []GeoLocation) { - cmd.val = val -} - -func (cmd *GeoSearchLocationCmd) Val() []GeoLocation { - return cmd.val -} - -func (cmd *GeoSearchLocationCmd) Result() ([]GeoLocation, error) { - return cmd.val, cmd.err -} - -func (cmd *GeoSearchLocationCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *GeoSearchLocationCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - cmd.val = make([]GeoLocation, n) - for i := 0; i < n; i++ { - _, err = rd.ReadArrayLen() - if err != nil { - return err - } - - var loc GeoLocation - - loc.Name, err = rd.ReadString() - if err != nil { - return err - } - if cmd.opt.WithDist { - loc.Dist, err = rd.ReadFloat() - if err != nil { - return err - } - } - if cmd.opt.WithHash { - loc.GeoHash, err = rd.ReadInt() - if err != nil { - return err - } - } - if cmd.opt.WithCoord { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - loc.Longitude, err = rd.ReadFloat() - if err != nil { - return err - } - loc.Latitude, err = rd.ReadFloat() - if err != nil { - return err - } - } - - cmd.val[i] = loc - } - - return nil -} - -//------------------------------------------------------------------------------ - -type GeoPos struct { - Longitude, Latitude float64 -} - -type GeoPosCmd struct { - baseCmd - - val []*GeoPos -} - -var _ Cmder = (*GeoPosCmd)(nil) - -func NewGeoPosCmd(ctx context.Context, args ...interface{}) *GeoPosCmd { - return &GeoPosCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *GeoPosCmd) SetVal(val []*GeoPos) { - cmd.val = val -} - -func (cmd *GeoPosCmd) Val() []*GeoPos { - return cmd.val -} - -func (cmd *GeoPosCmd) Result() ([]*GeoPos, error) { - return cmd.val, cmd.err -} - -func (cmd *GeoPosCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *GeoPosCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]*GeoPos, n) - - for i := 0; i < len(cmd.val); i++ { - err = rd.ReadFixedArrayLen(2) - if err != nil { - if err == Nil { - cmd.val[i] = nil - continue - } - return err - } - - longitude, err := rd.ReadFloat() - if err != nil { - return err - } - latitude, err := rd.ReadFloat() - if err != nil { - return err - } - - cmd.val[i] = &GeoPos{ - Longitude: longitude, - Latitude: latitude, - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type CommandInfo struct { - Name string - Arity int8 - Flags []string - ACLFlags []string - FirstKeyPos int8 - LastKeyPos int8 - StepCount int8 - ReadOnly bool -} - -type CommandsInfoCmd struct { - baseCmd - - val map[string]*CommandInfo -} - -var _ Cmder = (*CommandsInfoCmd)(nil) - -func NewCommandsInfoCmd(ctx context.Context, args ...interface{}) *CommandsInfoCmd { - return &CommandsInfoCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *CommandsInfoCmd) SetVal(val map[string]*CommandInfo) { - cmd.val = val -} - -func (cmd *CommandsInfoCmd) Val() map[string]*CommandInfo { - return cmd.val -} - -func (cmd *CommandsInfoCmd) Result() (map[string]*CommandInfo, error) { - return cmd.val, cmd.err -} - -func (cmd *CommandsInfoCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *CommandsInfoCmd) readReply(rd *proto.Reader) error { - const numArgRedis5 = 6 - const numArgRedis6 = 7 - const numArgRedis7 = 10 - - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make(map[string]*CommandInfo, n) - - for i := 0; i < n; i++ { - nn, err := rd.ReadArrayLen() - if err != nil { - return err - } - - switch nn { - case numArgRedis5, numArgRedis6, numArgRedis7: - // ok - default: - return fmt.Errorf("redis: got %d elements in COMMAND reply, wanted 6/7/10", nn) - } - - cmdInfo := &CommandInfo{} - if cmdInfo.Name, err = rd.ReadString(); err != nil { - return err - } - - arity, err := rd.ReadInt() - if err != nil { - return err - } - cmdInfo.Arity = int8(arity) - - flagLen, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmdInfo.Flags = make([]string, flagLen) - for f := 0; f < len(cmdInfo.Flags); f++ { - switch s, err := rd.ReadString(); { - case err == Nil: - cmdInfo.Flags[f] = "" - case err != nil: - return err - default: - if !cmdInfo.ReadOnly && s == "readonly" { - cmdInfo.ReadOnly = true - } - cmdInfo.Flags[f] = s - } - } - - firstKeyPos, err := rd.ReadInt() - if err != nil { - return err - } - cmdInfo.FirstKeyPos = int8(firstKeyPos) - - lastKeyPos, err := rd.ReadInt() - if err != nil { - return err - } - cmdInfo.LastKeyPos = int8(lastKeyPos) - - stepCount, err := rd.ReadInt() - if err != nil { - return err - } - cmdInfo.StepCount = int8(stepCount) - - if nn >= numArgRedis6 { - aclFlagLen, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmdInfo.ACLFlags = make([]string, aclFlagLen) - for f := 0; f < len(cmdInfo.ACLFlags); f++ { - switch s, err := rd.ReadString(); { - case err == Nil: - cmdInfo.ACLFlags[f] = "" - case err != nil: - return err - default: - cmdInfo.ACLFlags[f] = s - } - } - } - - if nn >= numArgRedis7 { - if err := rd.DiscardNext(); err != nil { - return err - } - if err := rd.DiscardNext(); err != nil { - return err - } - if err := rd.DiscardNext(); err != nil { - return err - } - } - - cmd.val[cmdInfo.Name] = cmdInfo - } - - return nil -} - -//------------------------------------------------------------------------------ - -type cmdsInfoCache struct { - fn func(ctx context.Context) (map[string]*CommandInfo, error) - - once internal.Once - cmds map[string]*CommandInfo -} - -func newCmdsInfoCache(fn func(ctx context.Context) (map[string]*CommandInfo, error)) *cmdsInfoCache { - return &cmdsInfoCache{ - fn: fn, - } -} - -func (c *cmdsInfoCache) Get(ctx context.Context) (map[string]*CommandInfo, error) { - err := c.once.Do(func() error { - cmds, err := c.fn(ctx) - if err != nil { - return err - } - - lowerCmds := make(map[string]*CommandInfo, len(cmds)) - - // Extensions have cmd names in upper case. Convert them to lower case. - for k, v := range cmds { - lowerCmds[internal.ToLower(k)] = v - } - - c.cmds = lowerCmds - return nil - }) - return c.cmds, err -} - -//------------------------------------------------------------------------------ - -type SlowLog struct { - ID int64 - Time time.Time - Duration time.Duration - Args []string - // These are also optional fields emitted only by Redis 4.0 or greater: - // https://redis.io/commands/slowlog#output-format - ClientAddr string - ClientName string -} - -type SlowLogCmd struct { - baseCmd - - val []SlowLog -} - -var _ Cmder = (*SlowLogCmd)(nil) - -func NewSlowLogCmd(ctx context.Context, args ...interface{}) *SlowLogCmd { - return &SlowLogCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *SlowLogCmd) SetVal(val []SlowLog) { - cmd.val = val -} - -func (cmd *SlowLogCmd) Val() []SlowLog { - return cmd.val -} - -func (cmd *SlowLogCmd) Result() ([]SlowLog, error) { - return cmd.val, cmd.err -} - -func (cmd *SlowLogCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *SlowLogCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]SlowLog, n) - - for i := 0; i < len(cmd.val); i++ { - nn, err := rd.ReadArrayLen() - if err != nil { - return err - } - if nn < 4 { - return fmt.Errorf("redis: got %d elements in slowlog get, expected at least 4", nn) - } - - if cmd.val[i].ID, err = rd.ReadInt(); err != nil { - return err - } - - createdAt, err := rd.ReadInt() - if err != nil { - return err - } - cmd.val[i].Time = time.Unix(createdAt, 0) - - costs, err := rd.ReadInt() - if err != nil { - return err - } - cmd.val[i].Duration = time.Duration(costs) * time.Microsecond - - cmdLen, err := rd.ReadArrayLen() - if err != nil { - return err - } - if cmdLen < 1 { - return fmt.Errorf("redis: got %d elements commands reply in slowlog get, expected at least 1", cmdLen) - } - - cmd.val[i].Args = make([]string, cmdLen) - for f := 0; f < len(cmd.val[i].Args); f++ { - cmd.val[i].Args[f], err = rd.ReadString() - if err != nil { - return err - } - } - - if nn >= 5 { - if cmd.val[i].ClientAddr, err = rd.ReadString(); err != nil { - return err - } - } - - if nn >= 6 { - if cmd.val[i].ClientName, err = rd.ReadString(); err != nil { - return err - } - } - } - - return nil -} - -//----------------------------------------------------------------------- - -type MapStringInterfaceCmd struct { - baseCmd - - val map[string]interface{} -} - -var _ Cmder = (*MapStringInterfaceCmd)(nil) - -func NewMapStringInterfaceCmd(ctx context.Context, args ...interface{}) *MapStringInterfaceCmd { - return &MapStringInterfaceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *MapStringInterfaceCmd) SetVal(val map[string]interface{}) { - cmd.val = val -} - -func (cmd *MapStringInterfaceCmd) Val() map[string]interface{} { - return cmd.val -} - -func (cmd *MapStringInterfaceCmd) Result() (map[string]interface{}, error) { - return cmd.val, cmd.err -} - -func (cmd *MapStringInterfaceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *MapStringInterfaceCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadMapLen() - if err != nil { - return err - } - - cmd.val = make(map[string]interface{}, n) - for i := 0; i < n; i++ { - k, err := rd.ReadString() - if err != nil { - return err - } - v, err := rd.ReadReply() - if err != nil { - if err == Nil { - cmd.val[k] = Nil - continue - } - if err, ok := err.(proto.RedisError); ok { - cmd.val[k] = err - continue - } - return err - } - cmd.val[k] = v - } - return nil -} - -//----------------------------------------------------------------------- - -type MapStringStringSliceCmd struct { - baseCmd - - val []map[string]string -} - -var _ Cmder = (*MapStringStringSliceCmd)(nil) - -func NewMapStringStringSliceCmd(ctx context.Context, args ...interface{}) *MapStringStringSliceCmd { - return &MapStringStringSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *MapStringStringSliceCmd) SetVal(val []map[string]string) { - cmd.val = val -} - -func (cmd *MapStringStringSliceCmd) Val() []map[string]string { - return cmd.val -} - -func (cmd *MapStringStringSliceCmd) Result() ([]map[string]string, error) { - return cmd.val, cmd.err -} - -func (cmd *MapStringStringSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *MapStringStringSliceCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - cmd.val = make([]map[string]string, n) - for i := 0; i < n; i++ { - nn, err := rd.ReadMapLen() - if err != nil { - return err - } - cmd.val[i] = make(map[string]string, nn) - for f := 0; f < nn; f++ { - k, err := rd.ReadString() - if err != nil { - return err - } - - v, err := rd.ReadString() - if err != nil { - return err - } - cmd.val[i][k] = v - } - } - return nil -} - -// ----------------------------------------------------------------------- - -// MapMapStringInterfaceCmd represents a command that returns a map of strings to interface{}. -type MapMapStringInterfaceCmd struct { - baseCmd - val map[string]interface{} -} - -func NewMapMapStringInterfaceCmd(ctx context.Context, args ...interface{}) *MapMapStringInterfaceCmd { - return &MapMapStringInterfaceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *MapMapStringInterfaceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *MapMapStringInterfaceCmd) SetVal(val map[string]interface{}) { - cmd.val = val -} - -func (cmd *MapMapStringInterfaceCmd) Result() (map[string]interface{}, error) { - return cmd.val, cmd.err -} - -func (cmd *MapMapStringInterfaceCmd) Val() map[string]interface{} { - return cmd.val -} - -// readReply will try to parse the reply from the proto.Reader for both resp2 and resp3 -func (cmd *MapMapStringInterfaceCmd) readReply(rd *proto.Reader) (err error) { - data, err := rd.ReadReply() - if err != nil { - return err - } - resultMap := map[string]interface{}{} - - switch midResponse := data.(type) { - case map[interface{}]interface{}: // resp3 will return map - for k, v := range midResponse { - stringKey, ok := k.(string) - if !ok { - return fmt.Errorf("redis: invalid map key %#v", k) - } - resultMap[stringKey] = v - } - case []interface{}: // resp2 will return array of arrays - n := len(midResponse) - for i := 0; i < n; i++ { - finalArr, ok := midResponse[i].([]interface{}) // final array that we need to transform to map - if !ok { - return fmt.Errorf("redis: unexpected response %#v", data) - } - m := len(finalArr) - if m%2 != 0 { // since this should be map, keys should be even number - return fmt.Errorf("redis: unexpected response %#v", data) - } - - for j := 0; j < m; j += 2 { - stringKey, ok := finalArr[j].(string) // the first one - if !ok { - return fmt.Errorf("redis: invalid map key %#v", finalArr[i]) - } - resultMap[stringKey] = finalArr[j+1] // second one is value - } - } - default: - return fmt.Errorf("redis: unexpected response %#v", data) - } - - cmd.val = resultMap - return nil -} - -//----------------------------------------------------------------------- - -type MapStringInterfaceSliceCmd struct { - baseCmd - - val []map[string]interface{} -} - -var _ Cmder = (*MapStringInterfaceSliceCmd)(nil) - -func NewMapStringInterfaceSliceCmd(ctx context.Context, args ...interface{}) *MapStringInterfaceSliceCmd { - return &MapStringInterfaceSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *MapStringInterfaceSliceCmd) SetVal(val []map[string]interface{}) { - cmd.val = val -} - -func (cmd *MapStringInterfaceSliceCmd) Val() []map[string]interface{} { - return cmd.val -} - -func (cmd *MapStringInterfaceSliceCmd) Result() ([]map[string]interface{}, error) { - return cmd.val, cmd.err -} - -func (cmd *MapStringInterfaceSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *MapStringInterfaceSliceCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - cmd.val = make([]map[string]interface{}, n) - for i := 0; i < n; i++ { - nn, err := rd.ReadMapLen() - if err != nil { - return err - } - cmd.val[i] = make(map[string]interface{}, nn) - for f := 0; f < nn; f++ { - k, err := rd.ReadString() - if err != nil { - return err - } - v, err := rd.ReadReply() - if err != nil { - if err != Nil { - return err - } - } - cmd.val[i][k] = v - } - } - return nil -} - -//------------------------------------------------------------------------------ - -type KeyValuesCmd struct { - baseCmd - - key string - val []string -} - -var _ Cmder = (*KeyValuesCmd)(nil) - -func NewKeyValuesCmd(ctx context.Context, args ...interface{}) *KeyValuesCmd { - return &KeyValuesCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *KeyValuesCmd) SetVal(key string, val []string) { - cmd.key = key - cmd.val = val -} - -func (cmd *KeyValuesCmd) Val() (string, []string) { - return cmd.key, cmd.val -} - -func (cmd *KeyValuesCmd) Result() (string, []string, error) { - return cmd.key, cmd.val, cmd.err -} - -func (cmd *KeyValuesCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *KeyValuesCmd) readReply(rd *proto.Reader) (err error) { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - - cmd.key, err = rd.ReadString() - if err != nil { - return err - } - - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]string, n) - for i := 0; i < n; i++ { - cmd.val[i], err = rd.ReadString() - if err != nil { - return err - } - } - - return nil -} - -//------------------------------------------------------------------------------ - -type ZSliceWithKeyCmd struct { - baseCmd - - key string - val []Z -} - -var _ Cmder = (*ZSliceWithKeyCmd)(nil) - -func NewZSliceWithKeyCmd(ctx context.Context, args ...interface{}) *ZSliceWithKeyCmd { - return &ZSliceWithKeyCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *ZSliceWithKeyCmd) SetVal(key string, val []Z) { - cmd.key = key - cmd.val = val -} - -func (cmd *ZSliceWithKeyCmd) Val() (string, []Z) { - return cmd.key, cmd.val -} - -func (cmd *ZSliceWithKeyCmd) Result() (string, []Z, error) { - return cmd.key, cmd.val, cmd.err -} - -func (cmd *ZSliceWithKeyCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ZSliceWithKeyCmd) readReply(rd *proto.Reader) (err error) { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - - cmd.key, err = rd.ReadString() - if err != nil { - return err - } - - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - typ, err := rd.PeekReplyType() - if err != nil { - return err - } - array := typ == proto.RespArray - - if array { - cmd.val = make([]Z, n) - } else { - cmd.val = make([]Z, n/2) - } - - for i := 0; i < len(cmd.val); i++ { - if array { - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - } - - if cmd.val[i].Member, err = rd.ReadString(); err != nil { - return err - } - - if cmd.val[i].Score, err = rd.ReadFloat(); err != nil { - return err - } - } - - 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 -} - -// FunctionStats contains information about the scripts currently executing on the server, and the available engines -// - Engines: -// Statistics about the engine like number of functions and number of libraries -// - RunningScript: -// The script currently running on the shard we're connecting to. -// For Redis Enterprise and Redis Cloud, this represents the -// function with the longest running time, across all the running functions, on all shards -// - RunningScripts -// All scripts currently running in a Redis Enterprise clustered database. -// Only available on Redis Enterprise -type FunctionStats struct { - Engines []Engine - isRunning bool - rs RunningScript - allrs []RunningScript -} - -func (fs *FunctionStats) Running() bool { - return fs.isRunning -} - -func (fs *FunctionStats) RunningScript() (RunningScript, bool) { - return fs.rs, fs.isRunning -} - -// AllRunningScripts returns all scripts currently running in a Redis Enterprise clustered database. -// Only available on Redis Enterprise -func (fs *FunctionStats) AllRunningScripts() []RunningScript { - return fs.allrs -} - -type RunningScript struct { - Name string - Command []string - Duration time.Duration -} - -type Engine struct { - Language string - LibrariesCount int64 - FunctionsCount int64 -} - -type FunctionStatsCmd struct { - baseCmd - val FunctionStats -} - -var _ Cmder = (*FunctionStatsCmd)(nil) - -func NewFunctionStatsCmd(ctx context.Context, args ...interface{}) *FunctionStatsCmd { - return &FunctionStatsCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *FunctionStatsCmd) SetVal(val FunctionStats) { - cmd.val = val -} - -func (cmd *FunctionStatsCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *FunctionStatsCmd) Val() FunctionStats { - return cmd.val -} - -func (cmd *FunctionStatsCmd) Result() (FunctionStats, error) { - return cmd.val, cmd.err -} - -func (cmd *FunctionStatsCmd) readReply(rd *proto.Reader) (err error) { - n, err := rd.ReadMapLen() - if err != nil { - return err - } - - var key string - var result FunctionStats - for f := 0; f < n; f++ { - key, err = rd.ReadString() - if err != nil { - return err - } - - switch key { - case "running_script": - result.rs, result.isRunning, err = cmd.readRunningScript(rd) - case "engines": - result.Engines, err = cmd.readEngines(rd) - case "all_running_scripts": // Redis Enterprise only - result.allrs, result.isRunning, err = cmd.readRunningScripts(rd) - default: - return fmt.Errorf("redis: function stats unexpected key %s", key) - } - - if err != nil { - return err - } - } - - cmd.val = result - return nil -} - -func (cmd *FunctionStatsCmd) readRunningScript(rd *proto.Reader) (RunningScript, bool, error) { - err := rd.ReadFixedMapLen(3) - if err != nil { - if err == Nil { - return RunningScript{}, false, nil - } - return RunningScript{}, false, err - } - - var runningScript RunningScript - for i := 0; i < 3; i++ { - key, err := rd.ReadString() - if err != nil { - return RunningScript{}, false, err - } - - switch key { - case "name": - runningScript.Name, err = rd.ReadString() - case "duration_ms": - runningScript.Duration, err = cmd.readDuration(rd) - case "command": - runningScript.Command, err = cmd.readCommand(rd) - default: - return RunningScript{}, false, fmt.Errorf("redis: function stats unexpected running_script key %s", key) - } - - if err != nil { - return RunningScript{}, false, err - } - } - - return runningScript, true, nil -} - -func (cmd *FunctionStatsCmd) readEngines(rd *proto.Reader) ([]Engine, error) { - n, err := rd.ReadMapLen() - if err != nil { - return nil, err - } - - engines := make([]Engine, 0, n) - for i := 0; i < n; i++ { - engine := Engine{} - engine.Language, err = rd.ReadString() - if err != nil { - return nil, err - } - - err = rd.ReadFixedMapLen(2) - if err != nil { - return nil, fmt.Errorf("redis: function stats unexpected %s engine map length", engine.Language) - } - - for i := 0; i < 2; i++ { - key, err := rd.ReadString() - switch key { - case "libraries_count": - engine.LibrariesCount, err = rd.ReadInt() - case "functions_count": - engine.FunctionsCount, err = rd.ReadInt() - } - if err != nil { - return nil, err - } - } - - engines = append(engines, engine) - } - return engines, nil -} - -func (cmd *FunctionStatsCmd) readDuration(rd *proto.Reader) (time.Duration, error) { - t, err := rd.ReadInt() - if err != nil { - return time.Duration(0), err - } - return time.Duration(t) * time.Millisecond, nil -} - -func (cmd *FunctionStatsCmd) readCommand(rd *proto.Reader) ([]string, error) { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - - command := make([]string, 0, n) - for i := 0; i < n; i++ { - x, err := rd.ReadString() - if err != nil { - return nil, err - } - command = append(command, x) - } - - return command, nil -} - -func (cmd *FunctionStatsCmd) readRunningScripts(rd *proto.Reader) ([]RunningScript, bool, error) { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, false, err - } - - runningScripts := make([]RunningScript, 0, n) - for i := 0; i < n; i++ { - rs, _, err := cmd.readRunningScript(rd) - if err != nil { - return nil, false, err - } - runningScripts = append(runningScripts, rs) - } - - return runningScripts, len(runningScripts) > 0, nil -} - -//------------------------------------------------------------------------------ - -// LCSQuery is a parameter used for the LCS command -type LCSQuery struct { - Key1 string - Key2 string - Len bool - Idx bool - MinMatchLen int - WithMatchLen bool -} - -// LCSMatch is the result set of the LCS command. -type LCSMatch struct { - MatchString string - Matches []LCSMatchedPosition - Len int64 -} - -type LCSMatchedPosition struct { - Key1 LCSPosition - Key2 LCSPosition - - // only for withMatchLen is true - MatchLen int64 -} - -type LCSPosition struct { - Start int64 - End int64 -} - -type LCSCmd struct { - baseCmd - - // 1: match string - // 2: match len - // 3: match idx LCSMatch - readType uint8 - val *LCSMatch -} - -func NewLCSCmd(ctx context.Context, q *LCSQuery) *LCSCmd { - args := make([]interface{}, 3, 7) - args[0] = "lcs" - args[1] = q.Key1 - args[2] = q.Key2 - - cmd := &LCSCmd{readType: 1} - if q.Len { - cmd.readType = 2 - args = append(args, "len") - } else if q.Idx { - cmd.readType = 3 - args = append(args, "idx") - if q.MinMatchLen != 0 { - args = append(args, "minmatchlen", q.MinMatchLen) - } - if q.WithMatchLen { - args = append(args, "withmatchlen") - } - } - cmd.baseCmd = baseCmd{ - ctx: ctx, - args: args, - } - - return cmd -} - -func (cmd *LCSCmd) SetVal(val *LCSMatch) { - cmd.val = val -} - -func (cmd *LCSCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *LCSCmd) Val() *LCSMatch { - return cmd.val -} - -func (cmd *LCSCmd) Result() (*LCSMatch, error) { - return cmd.val, cmd.err -} - -func (cmd *LCSCmd) readReply(rd *proto.Reader) (err error) { - lcs := &LCSMatch{} - switch cmd.readType { - case 1: - // match string - if lcs.MatchString, err = rd.ReadString(); err != nil { - return err - } - case 2: - // match len - if lcs.Len, err = rd.ReadInt(); err != nil { - return err - } - case 3: - // read LCSMatch - if err = rd.ReadFixedMapLen(2); err != nil { - return err - } - - // read matches or len field - for i := 0; i < 2; i++ { - key, err := rd.ReadString() - if err != nil { - return err - } - - switch key { - case "matches": - // read array of matched positions - if lcs.Matches, err = cmd.readMatchedPositions(rd); err != nil { - return err - } - case "len": - // read match length - if lcs.Len, err = rd.ReadInt(); err != nil { - return err - } - } - } - } - - cmd.val = lcs - return nil -} - -func (cmd *LCSCmd) readMatchedPositions(rd *proto.Reader) ([]LCSMatchedPosition, error) { - n, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - - positions := make([]LCSMatchedPosition, n) - for i := 0; i < n; i++ { - pn, err := rd.ReadArrayLen() - if err != nil { - return nil, err - } - - if positions[i].Key1, err = cmd.readPosition(rd); err != nil { - return nil, err - } - if positions[i].Key2, err = cmd.readPosition(rd); err != nil { - return nil, err - } - - // read match length if WithMatchLen is true - if pn > 2 { - if positions[i].MatchLen, err = rd.ReadInt(); err != nil { - return nil, err - } - } - } - - return positions, nil -} - -func (cmd *LCSCmd) readPosition(rd *proto.Reader) (pos LCSPosition, err error) { - if err = rd.ReadFixedArrayLen(2); err != nil { - return pos, err - } - if pos.Start, err = rd.ReadInt(); err != nil { - return pos, err - } - if pos.End, err = rd.ReadInt(); err != nil { - return pos, err - } - - return pos, nil -} - -// ------------------------------------------------------------------------ - -type KeyFlags struct { - Key string - Flags []string -} - -type KeyFlagsCmd struct { - baseCmd - - val []KeyFlags -} - -var _ Cmder = (*KeyFlagsCmd)(nil) - -func NewKeyFlagsCmd(ctx context.Context, args ...interface{}) *KeyFlagsCmd { - return &KeyFlagsCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *KeyFlagsCmd) SetVal(val []KeyFlags) { - cmd.val = val -} - -func (cmd *KeyFlagsCmd) Val() []KeyFlags { - return cmd.val -} - -func (cmd *KeyFlagsCmd) Result() ([]KeyFlags, error) { - return cmd.val, cmd.err -} - -func (cmd *KeyFlagsCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *KeyFlagsCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - if n == 0 { - cmd.val = make([]KeyFlags, 0) - return nil - } - - cmd.val = make([]KeyFlags, n) - - for i := 0; i < len(cmd.val); i++ { - - if err = rd.ReadFixedArrayLen(2); err != nil { - return err - } - - if cmd.val[i].Key, err = rd.ReadString(); err != nil { - return err - } - flagsLen, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val[i].Flags = make([]string, flagsLen) - - for j := 0; j < flagsLen; j++ { - if cmd.val[i].Flags[j], err = rd.ReadString(); err != nil { - return err - } - } - } - - return nil -} - -// --------------------------------------------------------------------------------------------------- - -type ClusterLink struct { - Direction string - Node string - CreateTime int64 - Events string - SendBufferAllocated int64 - SendBufferUsed int64 -} - -type ClusterLinksCmd struct { - baseCmd - - val []ClusterLink -} - -var _ Cmder = (*ClusterLinksCmd)(nil) - -func NewClusterLinksCmd(ctx context.Context, args ...interface{}) *ClusterLinksCmd { - return &ClusterLinksCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *ClusterLinksCmd) SetVal(val []ClusterLink) { - cmd.val = val -} - -func (cmd *ClusterLinksCmd) Val() []ClusterLink { - return cmd.val -} - -func (cmd *ClusterLinksCmd) Result() ([]ClusterLink, error) { - return cmd.val, cmd.err -} - -func (cmd *ClusterLinksCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ClusterLinksCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]ClusterLink, n) - - for i := 0; i < len(cmd.val); i++ { - m, err := rd.ReadMapLen() - if err != nil { - return err - } - - for j := 0; j < m; j++ { - key, err := rd.ReadString() - if err != nil { - return err - } - - switch key { - case "direction": - cmd.val[i].Direction, err = rd.ReadString() - case "node": - cmd.val[i].Node, err = rd.ReadString() - case "create-time": - cmd.val[i].CreateTime, err = rd.ReadInt() - case "events": - cmd.val[i].Events, err = rd.ReadString() - case "send-buffer-allocated": - cmd.val[i].SendBufferAllocated, err = rd.ReadInt() - case "send-buffer-used": - cmd.val[i].SendBufferUsed, err = rd.ReadInt() - default: - return fmt.Errorf("redis: unexpected key %q in CLUSTER LINKS reply", key) - } - - if err != nil { - return err - } - } - } - - return nil -} - -// ------------------------------------------------------------------------------------------------------------------ - -type SlotRange struct { - Start int64 - End int64 -} - -type Node struct { - ID string - Endpoint string - IP string - Hostname string - Port int64 - TLSPort int64 - Role string - ReplicationOffset int64 - Health string -} - -type ClusterShard struct { - Slots []SlotRange - Nodes []Node -} - -type ClusterShardsCmd struct { - baseCmd - - val []ClusterShard -} - -var _ Cmder = (*ClusterShardsCmd)(nil) - -func NewClusterShardsCmd(ctx context.Context, args ...interface{}) *ClusterShardsCmd { - return &ClusterShardsCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *ClusterShardsCmd) SetVal(val []ClusterShard) { - cmd.val = val -} - -func (cmd *ClusterShardsCmd) Val() []ClusterShard { - return cmd.val -} - -func (cmd *ClusterShardsCmd) Result() ([]ClusterShard, error) { - return cmd.val, cmd.err -} - -func (cmd *ClusterShardsCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ClusterShardsCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val = make([]ClusterShard, n) - - for i := 0; i < n; i++ { - m, err := rd.ReadMapLen() - if err != nil { - return err - } - - for j := 0; j < m; j++ { - key, err := rd.ReadString() - if err != nil { - return err - } - - switch key { - case "slots": - l, err := rd.ReadArrayLen() - if err != nil { - return err - } - for k := 0; k < l; k += 2 { - start, err := rd.ReadInt() - if err != nil { - return err - } - - end, err := rd.ReadInt() - if err != nil { - return err - } - - cmd.val[i].Slots = append(cmd.val[i].Slots, SlotRange{Start: start, End: end}) - } - case "nodes": - nodesLen, err := rd.ReadArrayLen() - if err != nil { - return err - } - cmd.val[i].Nodes = make([]Node, nodesLen) - for k := 0; k < nodesLen; k++ { - nodeMapLen, err := rd.ReadMapLen() - if err != nil { - return err - } - - for l := 0; l < nodeMapLen; l++ { - nodeKey, err := rd.ReadString() - if err != nil { - return err - } - - switch nodeKey { - case "id": - cmd.val[i].Nodes[k].ID, err = rd.ReadString() - case "endpoint": - cmd.val[i].Nodes[k].Endpoint, err = rd.ReadString() - case "ip": - cmd.val[i].Nodes[k].IP, err = rd.ReadString() - case "hostname": - cmd.val[i].Nodes[k].Hostname, err = rd.ReadString() - case "port": - cmd.val[i].Nodes[k].Port, err = rd.ReadInt() - case "tls-port": - cmd.val[i].Nodes[k].TLSPort, err = rd.ReadInt() - case "role": - cmd.val[i].Nodes[k].Role, err = rd.ReadString() - case "replication-offset": - cmd.val[i].Nodes[k].ReplicationOffset, err = rd.ReadInt() - case "health": - cmd.val[i].Nodes[k].Health, err = rd.ReadString() - default: - return fmt.Errorf("redis: unexpected key %q in CLUSTER SHARDS node reply", nodeKey) - } - - if err != nil { - return err - } - } - } - default: - return fmt.Errorf("redis: unexpected key %q in CLUSTER SHARDS reply", key) - } - } - } - - return nil -} - -// ----------------------------------------- - -type RankScore struct { - Rank int64 - Score float64 -} - -type RankWithScoreCmd struct { - baseCmd - - val RankScore -} - -var _ Cmder = (*RankWithScoreCmd)(nil) - -func NewRankWithScoreCmd(ctx context.Context, args ...interface{}) *RankWithScoreCmd { - return &RankWithScoreCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *RankWithScoreCmd) SetVal(val RankScore) { - cmd.val = val -} - -func (cmd *RankWithScoreCmd) Val() RankScore { - return cmd.val -} - -func (cmd *RankWithScoreCmd) Result() (RankScore, error) { - return cmd.val, cmd.err -} - -func (cmd *RankWithScoreCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *RankWithScoreCmd) readReply(rd *proto.Reader) error { - if err := rd.ReadFixedArrayLen(2); err != nil { - return err - } - - rank, err := rd.ReadInt() - if err != nil { - return err - } - - score, err := rd.ReadFloat() - if err != nil { - return err - } - - cmd.val = RankScore{Rank: rank, Score: score} - - return nil -} - -// -------------------------------------------------------------------------------------------------- - -// ClientFlags is redis-server client flags, copy from redis/src/server.h (redis 7.0) -type ClientFlags uint64 - -const ( - ClientSlave ClientFlags = 1 << 0 /* This client is a replica */ - ClientMaster ClientFlags = 1 << 1 /* This client is a master */ - ClientMonitor ClientFlags = 1 << 2 /* This client is a slave monitor, see MONITOR */ - ClientMulti ClientFlags = 1 << 3 /* This client is in a MULTI context */ - ClientBlocked ClientFlags = 1 << 4 /* The client is waiting in a blocking operation */ - ClientDirtyCAS ClientFlags = 1 << 5 /* Watched keys modified. EXEC will fail. */ - ClientCloseAfterReply ClientFlags = 1 << 6 /* Close after writing entire reply. */ - ClientUnBlocked ClientFlags = 1 << 7 /* This client was unblocked and is stored in server.unblocked_clients */ - ClientScript ClientFlags = 1 << 8 /* This is a non-connected client used by Lua */ - ClientAsking ClientFlags = 1 << 9 /* Client issued the ASKING command */ - ClientCloseASAP ClientFlags = 1 << 10 /* Close this client ASAP */ - ClientUnixSocket ClientFlags = 1 << 11 /* Client connected via Unix domain socket */ - ClientDirtyExec ClientFlags = 1 << 12 /* EXEC will fail for errors while queueing */ - ClientMasterForceReply ClientFlags = 1 << 13 /* Queue replies even if is master */ - ClientForceAOF ClientFlags = 1 << 14 /* Force AOF propagation of current cmd. */ - ClientForceRepl ClientFlags = 1 << 15 /* Force replication of current cmd. */ - ClientPrePSync ClientFlags = 1 << 16 /* Instance don't understand PSYNC. */ - ClientReadOnly ClientFlags = 1 << 17 /* Cluster client is in read-only state. */ - ClientPubSub ClientFlags = 1 << 18 /* Client is in Pub/Sub mode. */ - ClientPreventAOFProp ClientFlags = 1 << 19 /* Don't propagate to AOF. */ - ClientPreventReplProp ClientFlags = 1 << 20 /* Don't propagate to slaves. */ - ClientPreventProp ClientFlags = ClientPreventAOFProp | ClientPreventReplProp - ClientPendingWrite ClientFlags = 1 << 21 /* Client has output to send but a-write handler is yet not installed. */ - ClientReplyOff ClientFlags = 1 << 22 /* Don't send replies to client. */ - ClientReplySkipNext ClientFlags = 1 << 23 /* Set ClientREPLY_SKIP for next cmd */ - ClientReplySkip ClientFlags = 1 << 24 /* Don't send just this reply. */ - ClientLuaDebug ClientFlags = 1 << 25 /* Run EVAL in debug mode. */ - ClientLuaDebugSync ClientFlags = 1 << 26 /* EVAL debugging without fork() */ - ClientModule ClientFlags = 1 << 27 /* Non connected client used by some module. */ - ClientProtected ClientFlags = 1 << 28 /* Client should not be freed for now. */ - ClientExecutingCommand ClientFlags = 1 << 29 /* Indicates that the client is currently in the process of handling - a command. usually this will be marked only during call() - however, blocked clients might have this flag kept until they - will try to reprocess the command. */ - ClientPendingCommand ClientFlags = 1 << 30 /* Indicates the client has a fully * parsed command ready for execution. */ - ClientTracking ClientFlags = 1 << 31 /* Client enabled keys tracking in order to perform client side caching. */ - ClientTrackingBrokenRedir ClientFlags = 1 << 32 /* Target client is invalid. */ - ClientTrackingBCAST ClientFlags = 1 << 33 /* Tracking in BCAST mode. */ - ClientTrackingOptIn ClientFlags = 1 << 34 /* Tracking in opt-in mode. */ - ClientTrackingOptOut ClientFlags = 1 << 35 /* Tracking in opt-out mode. */ - ClientTrackingCaching ClientFlags = 1 << 36 /* CACHING yes/no was given, depending on optin/optout mode. */ - ClientTrackingNoLoop ClientFlags = 1 << 37 /* Don't send invalidation messages about writes performed by myself.*/ - ClientInTimeoutTable ClientFlags = 1 << 38 /* This client is in the timeout table. */ - ClientProtocolError ClientFlags = 1 << 39 /* Protocol error chatting with it. */ - ClientCloseAfterCommand ClientFlags = 1 << 40 /* Close after executing commands * and writing entire reply. */ - ClientDenyBlocking ClientFlags = 1 << 41 /* Indicate that the client should not be blocked. currently, turned on inside MULTI, Lua, RM_Call, and AOF client */ - ClientReplRDBOnly ClientFlags = 1 << 42 /* This client is a replica that only wants RDB without replication buffer. */ - ClientNoEvict ClientFlags = 1 << 43 /* This client is protected against client memory eviction. */ - ClientAllowOOM ClientFlags = 1 << 44 /* Client used by RM_Call is allowed to fully execute scripts even when in OOM */ - ClientNoTouch ClientFlags = 1 << 45 /* This client will not touch LFU/LRU stats. */ - ClientPushing ClientFlags = 1 << 46 /* This client is pushing notifications. */ -) - -// ClientInfo is redis-server ClientInfo, not go-redis *Client -type ClientInfo struct { - ID int64 // redis version 2.8.12, a unique 64-bit client ID - Addr string // address/port of the client - LAddr string // address/port of local address client connected to (bind address) - FD int64 // file descriptor corresponding to the socket - Name string // the name set by the client with CLIENT SETNAME - Age time.Duration // total duration of the connection in seconds - Idle time.Duration // idle time of the connection in seconds - Flags ClientFlags // client flags (see below) - DB int // current database ID - Sub int // number of channel subscriptions - PSub int // number of pattern matching subscriptions - SSub int // redis version 7.0.3, number of shard channel subscriptions - Multi int // number of commands in a MULTI/EXEC context - Watch int // redis version 7.4 RC1, number of keys this client is currently watching. - QueryBuf int // qbuf, query buffer length (0 means no query pending) - QueryBufFree int // qbuf-free, free space of the query buffer (0 means the buffer is full) - ArgvMem int // incomplete arguments for the next command (already extracted from query buffer) - MultiMem int // redis version 7.0, memory is used up by buffered multi commands - BufferSize int // rbs, usable size of buffer - BufferPeak int // rbp, peak used size of buffer in last 5 sec interval - OutputBufferLength int // obl, output buffer length - OutputListLength int // oll, output list length (replies are queued in this list when the buffer is full) - OutputMemory int // omem, output buffer memory usage - TotalMemory int // tot-mem, total memory consumed by this client in its various buffers - IoThread int // io-thread id - Events string // file descriptor events (see below) - LastCmd string // cmd, last command played - User string // the authenticated username of the client - Redir int64 // client id of current client tracking redirection - Resp int // redis version 7.0, client RESP protocol version - LibName string // redis version 7.2, client library name - LibVer string // redis version 7.2, client library version -} - -type ClientInfoCmd struct { - baseCmd - - val *ClientInfo -} - -var _ Cmder = (*ClientInfoCmd)(nil) - -func NewClientInfoCmd(ctx context.Context, args ...interface{}) *ClientInfoCmd { - return &ClientInfoCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *ClientInfoCmd) SetVal(val *ClientInfo) { - cmd.val = val -} - -func (cmd *ClientInfoCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ClientInfoCmd) Val() *ClientInfo { - return cmd.val -} - -func (cmd *ClientInfoCmd) Result() (*ClientInfo, error) { - return cmd.val, cmd.err -} - -func (cmd *ClientInfoCmd) readReply(rd *proto.Reader) (err error) { - txt, err := rd.ReadString() - if err != nil { - return err - } - - // sds o = catClientInfoString(sdsempty(), c); - // o = sdscatlen(o,"\n",1); - // addReplyVerbatim(c,o,sdslen(o),"txt"); - // sdsfree(o); - cmd.val, err = parseClientInfo(strings.TrimSpace(txt)) - return err -} - -// fmt.Sscanf() cannot handle null values -func parseClientInfo(txt string) (info *ClientInfo, err error) { - info = &ClientInfo{} - for _, s := range strings.Split(txt, " ") { - kv := strings.Split(s, "=") - if len(kv) != 2 { - return nil, fmt.Errorf("redis: unexpected client info data (%s)", s) - } - key, val := kv[0], kv[1] - - switch key { - case "id": - info.ID, err = strconv.ParseInt(val, 10, 64) - case "addr": - info.Addr = val - case "laddr": - info.LAddr = val - case "fd": - info.FD, err = strconv.ParseInt(val, 10, 64) - case "name": - info.Name = val - case "age": - var age int - if age, err = strconv.Atoi(val); err == nil { - info.Age = time.Duration(age) * time.Second - } - case "idle": - var idle int - if idle, err = strconv.Atoi(val); err == nil { - info.Idle = time.Duration(idle) * time.Second - } - case "flags": - if val == "N" { - break - } - - for i := 0; i < len(val); i++ { - switch val[i] { - case 'S': - info.Flags |= ClientSlave - case 'O': - info.Flags |= ClientSlave | ClientMonitor - case 'M': - info.Flags |= ClientMaster - case 'P': - info.Flags |= ClientPubSub - case 'x': - info.Flags |= ClientMulti - case 'b': - info.Flags |= ClientBlocked - case 't': - info.Flags |= ClientTracking - case 'R': - info.Flags |= ClientTrackingBrokenRedir - case 'B': - info.Flags |= ClientTrackingBCAST - case 'd': - info.Flags |= ClientDirtyCAS - case 'c': - info.Flags |= ClientCloseAfterCommand - case 'u': - info.Flags |= ClientUnBlocked - case 'A': - info.Flags |= ClientCloseASAP - case 'U': - info.Flags |= ClientUnixSocket - case 'r': - info.Flags |= ClientReadOnly - case 'e': - info.Flags |= ClientNoEvict - case 'T': - info.Flags |= ClientNoTouch - default: - return nil, fmt.Errorf("redis: unexpected client info flags(%s)", string(val[i])) - } - } - case "db": - info.DB, err = strconv.Atoi(val) - case "sub": - info.Sub, err = strconv.Atoi(val) - case "psub": - info.PSub, err = strconv.Atoi(val) - case "ssub": - info.SSub, err = strconv.Atoi(val) - case "multi": - info.Multi, err = strconv.Atoi(val) - case "watch": - info.Watch, err = strconv.Atoi(val) - case "qbuf": - info.QueryBuf, err = strconv.Atoi(val) - case "qbuf-free": - info.QueryBufFree, err = strconv.Atoi(val) - case "argv-mem": - info.ArgvMem, err = strconv.Atoi(val) - case "multi-mem": - info.MultiMem, err = strconv.Atoi(val) - case "rbs": - info.BufferSize, err = strconv.Atoi(val) - case "rbp": - info.BufferPeak, err = strconv.Atoi(val) - case "obl": - info.OutputBufferLength, err = strconv.Atoi(val) - case "oll": - info.OutputListLength, err = strconv.Atoi(val) - case "omem": - info.OutputMemory, err = strconv.Atoi(val) - case "tot-mem": - info.TotalMemory, err = strconv.Atoi(val) - case "events": - info.Events = val - case "cmd": - info.LastCmd = val - case "user": - info.User = val - case "redir": - info.Redir, err = strconv.ParseInt(val, 10, 64) - case "resp": - info.Resp, err = strconv.Atoi(val) - case "lib-name": - info.LibName = val - case "lib-ver": - info.LibVer = val - case "io-thread": - info.IoThread, err = strconv.Atoi(val) - default: - return nil, fmt.Errorf("redis: unexpected client info key(%s)", key) - } - - if err != nil { - return nil, err - } - } - - return info, nil -} - -// ------------------------------------------- - -type ACLLogEntry struct { - Count int64 - Reason string - Context string - Object string - Username string - AgeSeconds float64 - ClientInfo *ClientInfo - EntryID int64 - TimestampCreated int64 - TimestampLastUpdated int64 -} - -type ACLLogCmd struct { - baseCmd - - val []*ACLLogEntry -} - -var _ Cmder = (*ACLLogCmd)(nil) - -func NewACLLogCmd(ctx context.Context, args ...interface{}) *ACLLogCmd { - return &ACLLogCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *ACLLogCmd) SetVal(val []*ACLLogEntry) { - cmd.val = val -} - -func (cmd *ACLLogCmd) Val() []*ACLLogEntry { - return cmd.val -} - -func (cmd *ACLLogCmd) Result() ([]*ACLLogEntry, error) { - return cmd.val, cmd.err -} - -func (cmd *ACLLogCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *ACLLogCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadArrayLen() - if err != nil { - return err - } - - cmd.val = make([]*ACLLogEntry, n) - for i := 0; i < n; i++ { - cmd.val[i] = &ACLLogEntry{} - entry := cmd.val[i] - respLen, err := rd.ReadMapLen() - if err != nil { - return err - } - for j := 0; j < respLen; j++ { - key, err := rd.ReadString() - if err != nil { - return err - } - - switch key { - case "count": - entry.Count, err = rd.ReadInt() - case "reason": - entry.Reason, err = rd.ReadString() - case "context": - entry.Context, err = rd.ReadString() - case "object": - entry.Object, err = rd.ReadString() - case "username": - entry.Username, err = rd.ReadString() - case "age-seconds": - entry.AgeSeconds, err = rd.ReadFloat() - case "client-info": - txt, err := rd.ReadString() - if err != nil { - return err - } - entry.ClientInfo, err = parseClientInfo(strings.TrimSpace(txt)) - if err != nil { - return err - } - case "entry-id": - entry.EntryID, err = rd.ReadInt() - case "timestamp-created": - entry.TimestampCreated, err = rd.ReadInt() - case "timestamp-last-updated": - entry.TimestampLastUpdated, err = rd.ReadInt() - default: - return fmt.Errorf("redis: unexpected key %q in ACL LOG reply", key) - } - - if err != nil { - return err - } - } - } - - return nil -} - -// LibraryInfo holds the library info. -type LibraryInfo struct { - LibName *string - LibVer *string -} - -// WithLibraryName returns a valid LibraryInfo with library name only. -func WithLibraryName(libName string) LibraryInfo { - return LibraryInfo{LibName: &libName} -} - -// WithLibraryVersion returns a valid LibraryInfo with library version only. -func WithLibraryVersion(libVer string) LibraryInfo { - return LibraryInfo{LibVer: &libVer} -} - -// ------------------------------------------- - -type InfoCmd struct { - baseCmd - val map[string]map[string]string -} - -var _ Cmder = (*InfoCmd)(nil) - -func NewInfoCmd(ctx context.Context, args ...interface{}) *InfoCmd { - return &InfoCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *InfoCmd) SetVal(val map[string]map[string]string) { - cmd.val = val -} - -func (cmd *InfoCmd) Val() map[string]map[string]string { - return cmd.val -} - -func (cmd *InfoCmd) Result() (map[string]map[string]string, error) { - return cmd.val, cmd.err -} - -func (cmd *InfoCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *InfoCmd) readReply(rd *proto.Reader) error { - val, err := rd.ReadString() - if err != nil { - return err - } - - section := "" - scanner := bufio.NewScanner(strings.NewReader(val)) - for scanner.Scan() { - line := scanner.Text() - if strings.HasPrefix(line, "#") { - if cmd.val == nil { - cmd.val = make(map[string]map[string]string) - } - section = strings.TrimPrefix(line, "# ") - cmd.val[section] = make(map[string]string) - } else if line != "" { - if section == "Modules" { - moduleRe := regexp.MustCompile(`module:name=(.+?),(.+)$`) - kv := moduleRe.FindStringSubmatch(line) - if len(kv) == 3 { - cmd.val[section][kv[1]] = kv[2] - } - } else { - kv := strings.SplitN(line, ":", 2) - if len(kv) == 2 { - cmd.val[section][kv[0]] = kv[1] - } - } - } - } - - return nil -} - -func (cmd *InfoCmd) Item(section, key string) string { - if cmd.val == nil { - return "" - } else if cmd.val[section] == nil { - return "" - } else { - return cmd.val[section][key] - } -} - -type MonitorStatus int - -const ( - monitorStatusIdle MonitorStatus = iota - monitorStatusStart - monitorStatusStop -) - -type MonitorCmd struct { - baseCmd - ch chan string - status MonitorStatus - mu sync.Mutex -} - -func newMonitorCmd(ctx context.Context, ch chan string) *MonitorCmd { - return &MonitorCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: []interface{}{"monitor"}, - }, - ch: ch, - status: monitorStatusIdle, - mu: sync.Mutex{}, - } -} - -func (cmd *MonitorCmd) String() string { - return cmdString(cmd, nil) -} - -func (cmd *MonitorCmd) readReply(rd *proto.Reader) error { - ctx, cancel := context.WithCancel(cmd.ctx) - go func(ctx context.Context) { - for { - select { - case <-ctx.Done(): - return - default: - err := cmd.readMonitor(rd, cancel) - if err != nil { - cmd.err = err - return - } - } - } - }(ctx) - return nil -} - -func (cmd *MonitorCmd) readMonitor(rd *proto.Reader, cancel context.CancelFunc) error { - for { - cmd.mu.Lock() - st := cmd.status - pk, _ := rd.Peek(1) - cmd.mu.Unlock() - if len(pk) != 0 && st == monitorStatusStart { - cmd.mu.Lock() - line, err := rd.ReadString() - cmd.mu.Unlock() - if err != nil { - return err - } - cmd.ch <- line - } - if st == monitorStatusStop { - cancel() - break - } - } - return nil -} - -func (cmd *MonitorCmd) Start() { - cmd.mu.Lock() - defer cmd.mu.Unlock() - cmd.status = monitorStatusStart -} - -func (cmd *MonitorCmd) Stop() { - cmd.mu.Lock() - defer cmd.mu.Unlock() - cmd.status = monitorStatusStop -} - -type VectorScoreSliceCmd struct { - baseCmd - - val []VectorScore -} - -var _ Cmder = (*VectorScoreSliceCmd)(nil) - -func NewVectorInfoSliceCmd(ctx context.Context, args ...any) *VectorScoreSliceCmd { - return &VectorScoreSliceCmd{ - baseCmd: baseCmd{ - ctx: ctx, - args: args, - }, - } -} - -func (cmd *VectorScoreSliceCmd) SetVal(val []VectorScore) { - cmd.val = val -} - -func (cmd *VectorScoreSliceCmd) Val() []VectorScore { - return cmd.val -} - -func (cmd *VectorScoreSliceCmd) Result() ([]VectorScore, error) { - return cmd.val, cmd.err -} - -func (cmd *VectorScoreSliceCmd) String() string { - return cmdString(cmd, cmd.val) -} - -func (cmd *VectorScoreSliceCmd) readReply(rd *proto.Reader) error { - n, err := rd.ReadMapLen() - if err != nil { - return err - } - - cmd.val = make([]VectorScore, n) - for i := 0; i < n; i++ { - name, err := rd.ReadString() - if err != nil { - return err - } - cmd.val[i].Name = name - - score, err := rd.ReadFloat() - if err != nil { - return err - } - cmd.val[i].Score = score - } - return nil -} diff --git a/vendor/github.com/redis/go-redis/v9/commands.go b/vendor/github.com/redis/go-redis/v9/commands.go deleted file mode 100644 index c035800..0000000 --- a/vendor/github.com/redis/go-redis/v9/commands.go +++ /dev/null @@ -1,733 +0,0 @@ -package redis - -import ( - "context" - "encoding" - "errors" - "fmt" - "io" - "net" - "reflect" - "runtime" - "strings" - "time" - - "github.com/redis/go-redis/v9/internal" -) - -// KeepTTL is a Redis KEEPTTL option to keep existing TTL, it requires your redis-server version >= 6.0, -// otherwise you will receive an error: (error) ERR syntax error. -// For example: -// -// rdb.Set(ctx, key, value, redis.KeepTTL) -const KeepTTL = -1 - -func usePrecise(dur time.Duration) bool { - return dur < time.Second || dur%time.Second != 0 -} - -func formatMs(ctx context.Context, dur time.Duration) int64 { - if dur > 0 && dur < time.Millisecond { - internal.Logger.Printf( - ctx, - "specified duration is %s, but minimal supported value is %s - truncating to 1ms", - dur, time.Millisecond, - ) - return 1 - } - return int64(dur / time.Millisecond) -} - -func formatSec(ctx context.Context, dur time.Duration) int64 { - if dur > 0 && dur < time.Second { - internal.Logger.Printf( - ctx, - "specified duration is %s, but minimal supported value is %s - truncating to 1s", - dur, time.Second, - ) - return 1 - } - return int64(dur / time.Second) -} - -func appendArgs(dst, src []interface{}) []interface{} { - if len(src) == 1 { - return appendArg(dst, src[0]) - } - - dst = append(dst, src...) - return dst -} - -func appendArg(dst []interface{}, arg interface{}) []interface{} { - switch arg := arg.(type) { - case []string: - for _, s := range arg { - dst = append(dst, s) - } - return dst - case []interface{}: - dst = append(dst, arg...) - return dst - case map[string]interface{}: - for k, v := range arg { - dst = append(dst, k, v) - } - return dst - case map[string]string: - for k, v := range arg { - dst = append(dst, k, v) - } - return dst - case time.Time, time.Duration, encoding.BinaryMarshaler, net.IP: - return append(dst, arg) - case nil: - return dst - default: - // scan struct field - v := reflect.ValueOf(arg) - if v.Type().Kind() == reflect.Ptr { - if v.IsNil() { - // error: arg is not a valid object - return dst - } - v = v.Elem() - } - - if v.Type().Kind() == reflect.Struct { - return appendStructField(dst, v) - } - - return append(dst, arg) - } -} - -// appendStructField appends the field and value held by the structure v to dst, and returns the appended dst. -func appendStructField(dst []interface{}, v reflect.Value) []interface{} { - typ := v.Type() - for i := 0; i < typ.NumField(); i++ { - tag := typ.Field(i).Tag.Get("redis") - if tag == "" || tag == "-" { - continue - } - name, opt, _ := strings.Cut(tag, ",") - if name == "" { - continue - } - - field := v.Field(i) - - // miss field - if omitEmpty(opt) && isEmptyValue(field) { - continue - } - - if field.CanInterface() { - dst = append(dst, name, field.Interface()) - } - } - - return dst -} - -func omitEmpty(opt string) bool { - for opt != "" { - var name string - name, opt, _ = strings.Cut(opt, ",") - if name == "omitempty" { - return true - } - } - return false -} - -func isEmptyValue(v reflect.Value) bool { - switch v.Kind() { - case reflect.Array, reflect.Map, reflect.Slice, reflect.String: - return v.Len() == 0 - case reflect.Bool: - return !v.Bool() - case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64: - return v.Int() == 0 - case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr: - return v.Uint() == 0 - case reflect.Float32, reflect.Float64: - return v.Float() == 0 - case reflect.Interface, reflect.Pointer: - return v.IsNil() - case reflect.Struct: - if v.Type() == reflect.TypeOf(time.Time{}) { - return v.IsZero() - } - // Only supports the struct time.Time, - // subsequent iterations will follow the func Scan support decoder. - } - return false -} - -type Cmdable interface { - Pipeline() Pipeliner - Pipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) - - TxPipelined(ctx context.Context, fn func(Pipeliner) error) ([]Cmder, error) - TxPipeline() Pipeliner - - Command(ctx context.Context) *CommandsInfoCmd - CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd - CommandGetKeys(ctx context.Context, commands ...interface{}) *StringSliceCmd - CommandGetKeysAndFlags(ctx context.Context, commands ...interface{}) *KeyFlagsCmd - ClientGetName(ctx context.Context) *StringCmd - Echo(ctx context.Context, message interface{}) *StringCmd - Ping(ctx context.Context) *StatusCmd - Quit(ctx context.Context) *StatusCmd - Unlink(ctx context.Context, keys ...string) *IntCmd - - BgRewriteAOF(ctx context.Context) *StatusCmd - BgSave(ctx context.Context) *StatusCmd - ClientKill(ctx context.Context, ipPort string) *StatusCmd - ClientKillByFilter(ctx context.Context, keys ...string) *IntCmd - ClientList(ctx context.Context) *StringCmd - ClientInfo(ctx context.Context) *ClientInfoCmd - ClientPause(ctx context.Context, dur time.Duration) *BoolCmd - ClientUnpause(ctx context.Context) *BoolCmd - ClientID(ctx context.Context) *IntCmd - ClientUnblock(ctx context.Context, id int64) *IntCmd - ClientUnblockWithError(ctx context.Context, id int64) *IntCmd - ConfigGet(ctx context.Context, parameter string) *MapStringStringCmd - ConfigResetStat(ctx context.Context) *StatusCmd - ConfigSet(ctx context.Context, parameter, value string) *StatusCmd - ConfigRewrite(ctx context.Context) *StatusCmd - DBSize(ctx context.Context) *IntCmd - FlushAll(ctx context.Context) *StatusCmd - FlushAllAsync(ctx context.Context) *StatusCmd - FlushDB(ctx context.Context) *StatusCmd - FlushDBAsync(ctx context.Context) *StatusCmd - Info(ctx context.Context, section ...string) *StringCmd - LastSave(ctx context.Context) *IntCmd - Save(ctx context.Context) *StatusCmd - Shutdown(ctx context.Context) *StatusCmd - ShutdownSave(ctx context.Context) *StatusCmd - ShutdownNoSave(ctx context.Context) *StatusCmd - SlaveOf(ctx context.Context, host, port string) *StatusCmd - SlowLogGet(ctx context.Context, num int64) *SlowLogCmd - Time(ctx context.Context) *TimeCmd - DebugObject(ctx context.Context, key string) *StringCmd - MemoryUsage(ctx context.Context, key string, samples ...int) *IntCmd - - ModuleLoadex(ctx context.Context, conf *ModuleLoadexConfig) *StringCmd - - ACLCmdable - BitMapCmdable - ClusterCmdable - GenericCmdable - GeoCmdable - HashCmdable - HyperLogLogCmdable - ListCmdable - ProbabilisticCmdable - PubSubCmdable - ScriptingFunctionsCmdable - SearchCmdable - SetCmdable - SortedSetCmdable - StringCmdable - StreamCmdable - TimeseriesCmdable - JSONCmdable - VectorSetCmdable -} - -type StatefulCmdable interface { - Cmdable - Auth(ctx context.Context, password string) *StatusCmd - AuthACL(ctx context.Context, username, password string) *StatusCmd - Select(ctx context.Context, index int) *StatusCmd - SwapDB(ctx context.Context, index1, index2 int) *StatusCmd - ClientSetName(ctx context.Context, name string) *BoolCmd - ClientSetInfo(ctx context.Context, info LibraryInfo) *StatusCmd - Hello(ctx context.Context, ver int, username, password, clientName string) *MapStringInterfaceCmd -} - -var ( - _ Cmdable = (*Client)(nil) - _ Cmdable = (*Tx)(nil) - _ Cmdable = (*Ring)(nil) - _ Cmdable = (*ClusterClient)(nil) -) - -type cmdable func(ctx context.Context, cmd Cmder) error - -type statefulCmdable func(ctx context.Context, cmd Cmder) error - -//------------------------------------------------------------------------------ - -func (c statefulCmdable) Auth(ctx context.Context, password string) *StatusCmd { - cmd := NewStatusCmd(ctx, "auth", password) - _ = c(ctx, cmd) - return cmd -} - -// AuthACL Perform an AUTH command, using the given user and pass. -// Should be used to authenticate the current connection with one of the connections defined in the ACL list -// when connecting to a Redis 6.0 instance, or greater, that is using the Redis ACL system. -func (c statefulCmdable) AuthACL(ctx context.Context, username, password string) *StatusCmd { - cmd := NewStatusCmd(ctx, "auth", username, password) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) Wait(ctx context.Context, numSlaves int, timeout time.Duration) *IntCmd { - cmd := NewIntCmd(ctx, "wait", numSlaves, int(timeout/time.Millisecond)) - cmd.setReadTimeout(timeout) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) WaitAOF(ctx context.Context, numLocal, numSlaves int, timeout time.Duration) *IntCmd { - cmd := NewIntCmd(ctx, "waitAOF", numLocal, numSlaves, int(timeout/time.Millisecond)) - cmd.setReadTimeout(timeout) - _ = c(ctx, cmd) - return cmd -} - -func (c statefulCmdable) Select(ctx context.Context, index int) *StatusCmd { - cmd := NewStatusCmd(ctx, "select", index) - _ = c(ctx, cmd) - return cmd -} - -func (c statefulCmdable) SwapDB(ctx context.Context, index1, index2 int) *StatusCmd { - cmd := NewStatusCmd(ctx, "swapdb", index1, index2) - _ = c(ctx, cmd) - return cmd -} - -// ClientSetName assigns a name to the connection. -func (c statefulCmdable) ClientSetName(ctx context.Context, name string) *BoolCmd { - cmd := NewBoolCmd(ctx, "client", "setname", name) - _ = c(ctx, cmd) - return cmd -} - -// ClientSetInfo sends a CLIENT SETINFO command with the provided info. -func (c statefulCmdable) ClientSetInfo(ctx context.Context, info LibraryInfo) *StatusCmd { - err := info.Validate() - if err != nil { - panic(err.Error()) - } - - var cmd *StatusCmd - if info.LibName != nil { - libName := fmt.Sprintf("go-redis(%s,%s)", *info.LibName, internal.ReplaceSpaces(runtime.Version())) - cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-NAME", libName) - } else { - cmd = NewStatusCmd(ctx, "client", "setinfo", "LIB-VER", *info.LibVer) - } - - _ = c(ctx, cmd) - return cmd -} - -// Validate checks if only one field in the struct is non-nil. -func (info LibraryInfo) Validate() error { - if info.LibName != nil && info.LibVer != nil { - return errors.New("both LibName and LibVer cannot be set at the same time") - } - if info.LibName == nil && info.LibVer == nil { - return errors.New("at least one of LibName and LibVer should be set") - } - return nil -} - -// Hello sets the resp protocol used. -func (c statefulCmdable) Hello(ctx context.Context, - ver int, username, password, clientName string, -) *MapStringInterfaceCmd { - args := make([]interface{}, 0, 7) - args = append(args, "hello", ver) - if password != "" { - if username != "" { - args = append(args, "auth", username, password) - } else { - args = append(args, "auth", "default", password) - } - } - if clientName != "" { - args = append(args, "setname", clientName) - } - cmd := NewMapStringInterfaceCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -//------------------------------------------------------------------------------ - -func (c cmdable) Command(ctx context.Context) *CommandsInfoCmd { - cmd := NewCommandsInfoCmd(ctx, "command") - _ = c(ctx, cmd) - return cmd -} - -// FilterBy is used for the `CommandList` command parameter. -type FilterBy struct { - Module string - ACLCat string - Pattern string -} - -func (c cmdable) CommandList(ctx context.Context, filter *FilterBy) *StringSliceCmd { - args := make([]interface{}, 0, 5) - args = append(args, "command", "list") - if filter != nil { - if filter.Module != "" { - args = append(args, "filterby", "module", filter.Module) - } else if filter.ACLCat != "" { - args = append(args, "filterby", "aclcat", filter.ACLCat) - } else if filter.Pattern != "" { - args = append(args, "filterby", "pattern", filter.Pattern) - } - } - cmd := NewStringSliceCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) CommandGetKeys(ctx context.Context, commands ...interface{}) *StringSliceCmd { - args := make([]interface{}, 2+len(commands)) - args[0] = "command" - args[1] = "getkeys" - copy(args[2:], commands) - cmd := NewStringSliceCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) CommandGetKeysAndFlags(ctx context.Context, commands ...interface{}) *KeyFlagsCmd { - args := make([]interface{}, 2+len(commands)) - args[0] = "command" - args[1] = "getkeysandflags" - copy(args[2:], commands) - cmd := NewKeyFlagsCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -// ClientGetName returns the name of the connection. -func (c cmdable) ClientGetName(ctx context.Context) *StringCmd { - cmd := NewStringCmd(ctx, "client", "getname") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) Echo(ctx context.Context, message interface{}) *StringCmd { - cmd := NewStringCmd(ctx, "echo", message) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) Ping(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "ping") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) Do(ctx context.Context, args ...interface{}) *Cmd { - cmd := NewCmd(ctx, args...) - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) Quit(_ context.Context) *StatusCmd { - panic("not implemented") -} - -//------------------------------------------------------------------------------ - -func (c cmdable) BgRewriteAOF(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "bgrewriteaof") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) BgSave(ctx context.Context) *StatusCmd { - cmd := NewStatusCmd(ctx, "bgsave") - _ = c(ctx, cmd) - return cmd -} - -func (c cmdable) ClientKill(ctx context.Context, ipPort string) *StatusCmd { - cmd := NewStatusCmd(ctx, "client", "kill", ipPort) - _ = c(ctx, cmd) - return cmd -} - -// ClientKillByFilter is new style syntax, while the ClientKill is old -// -// CLIENT KILL