namespace: check IsWithFromKey if keyLen equal 0. (#12307)

* namespace: check IsWithFromKey if keyLen equal 0.

Rename function isWithFromKey/isWithPrefix to IsOptsWithFromKey/IsOptsWithPrefix.

fixes: #12282

* integration: add test while WithFromKey/WithPrefix called in opts.
This commit is contained in:
CFC4N 2020-10-09 08:34:09 +08:00 committed by GitHub
parent 2c66612e0e
commit 11ba1a6109
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 96 additions and 8 deletions

View File

@ -48,7 +48,7 @@ func (kv *kvPrefix) Put(ctx context.Context, key, val string, opts ...clientv3.O
}
func (kv *kvPrefix) Get(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.GetResponse, error) {
if len(key) == 0 {
if len(key) == 0 && !(clientv3.IsOptsWithFromKey(opts) || clientv3.IsOptsWithPrefix(opts)) {
return nil, rpctypes.ErrEmptyKey
}
r, err := kv.KV.Do(ctx, kv.prefixOp(clientv3.OpGet(key, opts...)))
@ -61,7 +61,7 @@ func (kv *kvPrefix) Get(ctx context.Context, key string, opts ...clientv3.OpOpti
}
func (kv *kvPrefix) Delete(ctx context.Context, key string, opts ...clientv3.OpOption) (*clientv3.DeleteResponse, error) {
if len(key) == 0 {
if len(key) == 0 && !(clientv3.IsOptsWithFromKey(opts) || clientv3.IsOptsWithPrefix(opts)) {
return nil, rpctypes.ErrEmptyKey
}
r, err := kv.KV.Do(ctx, kv.prefixOp(clientv3.OpDelete(key, opts...)))

View File

@ -219,7 +219,7 @@ func (op Op) isWrite() bool {
// OpGet returns "get" operation based on given key and operation options.
func OpGet(key string, opts ...OpOption) Op {
// WithPrefix and WithFromKey are not supported together
if isWithPrefix(opts) && isWithFromKey(opts) {
if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
}
ret := Op{t: tRange, key: []byte(key)}
@ -230,7 +230,7 @@ func OpGet(key string, opts ...OpOption) Op {
// OpDelete returns "delete" operation based on given key and operation options.
func OpDelete(key string, opts ...OpOption) Op {
// WithPrefix and WithFromKey are not supported together
if isWithPrefix(opts) && isWithFromKey(opts) {
if IsOptsWithPrefix(opts) && IsOptsWithFromKey(opts) {
panic("`WithPrefix` and `WithFromKey` cannot be set at the same time, choose one")
}
ret := Op{t: tDeleteRange, key: []byte(key)}
@ -553,8 +553,8 @@ func toLeaseTimeToLiveRequest(id LeaseID, opts ...LeaseOption) *pb.LeaseTimeToLi
return &pb.LeaseTimeToLiveRequest{ID: int64(id), Keys: ret.attachedKeys}
}
// isWithPrefix returns true if WithPrefix is being called in the op
func isWithPrefix(opts []OpOption) bool { return isOpFuncCalled("WithPrefix", opts) }
// IsOptsWithPrefix returns true if WithPrefix option is called in the given opts.
func IsOptsWithPrefix(opts []OpOption) bool { return isOpFuncCalled("WithPrefix", opts) }
// isWithFromKey returns true if WithFromKey is being called in the op
func isWithFromKey(opts []OpOption) bool { return isOpFuncCalled("WithFromKey", opts) }
// IsOptsWithFromKey returns true if WithFromKey option is called in the given opts.
func IsOptsWithFromKey(opts []OpOption) bool { return isOpFuncCalled("WithFromKey", opts) }

88
integration/v3_kv_test.go Normal file
View File

@ -0,0 +1,88 @@
package integration
import (
"context"
"go.etcd.io/etcd/v3/clientv3"
"go.etcd.io/etcd/v3/clientv3/namespace"
"go.etcd.io/etcd/v3/embed"
"go.etcd.io/etcd/v3/etcdserver/api/v3client"
"go.etcd.io/etcd/v3/pkg/testutil"
"io/ioutil"
"os"
"testing"
)
// TestKVWithEmptyValue ensures that a get/delete with an empty value, and with WithFromKey/WithPrefix function will return an empty error.
func TestKVWithEmptyValue(t *testing.T) {
defer testutil.AfterTest(t)
cfg := embed.NewConfig()
// Use temporary data directory.
dir, err := ioutil.TempDir("", "etcd-")
if err != nil {
panic(err)
}
defer os.RemoveAll(dir)
cfg.Dir = dir
// Suppress server log to keep output clean.
//cfg.Logger = "zap"
//cfg.LogLevel = "error"
etcd, err := embed.StartEtcd(cfg)
if err != nil {
panic(err)
}
defer etcd.Close()
<-etcd.Server.ReadyNotify()
client := v3client.New(etcd.Server)
defer client.Close()
_, err = client.Put(context.Background(), "my-namespace/foobar", "data")
if err != nil {
t.Fatal(err)
}
_, err = client.Put(context.Background(), "my-namespace/foobar1", "data")
if err != nil {
t.Fatal(err)
}
_, err = client.Put(context.Background(), "namespace/foobar1", "data")
if err != nil {
t.Fatal(err)
}
// Range over all keys.
resp, err := client.Get(context.Background(), "", clientv3.WithFromKey())
if err != nil {
t.Fatal(err)
}
for _, kv := range resp.Kvs {
t.Log(string(kv.Key), "=", string(kv.Value))
}
// Range over all keys in a namespace.
client.KV = namespace.NewKV(client.KV, "my-namespace/")
resp, err = client.Get(context.Background(), "", clientv3.WithFromKey())
if err != nil {
t.Fatal(err)
}
for _, kv := range resp.Kvs {
t.Log(string(kv.Key), "=", string(kv.Value))
}
//Remove all keys without WithFromKey/WithPrefix func
respDel, err := client.Delete(context.Background(), "")
if err == nil {
// fatal error duo to without WithFromKey/WithPrefix func called.
t.Fatal(err)
}
respDel, err = client.Delete(context.Background(), "", clientv3.WithFromKey())
if err != nil {
// fatal error duo to with WithFromKey/WithPrefix func called.
t.Fatal(err)
}
t.Logf("delete keys:%d", respDel.Deleted)
}