diff --git a/clientv3/integration/kv_test.go b/clientv3/integration/kv_test.go index 4bdd5de62..efaa337fd 100644 --- a/clientv3/integration/kv_test.go +++ b/clientv3/integration/kv_test.go @@ -226,6 +226,21 @@ func TestKVRange(t *testing.T) { {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, }, }, + // range all with SortByKey, missing sorting order (ASCEND by default) + { + "a", "x", + 0, + []clientv3.OpOption{clientv3.WithSort(clientv3.SortByKey, clientv3.SortNone)}, + + []*mvccpb.KeyValue{ + {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, + {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, + {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, + {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, + {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, + {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, + }, + }, // range all with SortByCreateRevision, SortDescend { "a", "x", @@ -241,6 +256,21 @@ func TestKVRange(t *testing.T) { {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, }, }, + // range all with SortByCreateRevision, missing sorting order (ASCEND by default) + { + "a", "x", + 0, + []clientv3.OpOption{clientv3.WithSort(clientv3.SortByCreateRevision, clientv3.SortNone)}, + + []*mvccpb.KeyValue{ + {Key: []byte("a"), Value: nil, CreateRevision: 2, ModRevision: 2, Version: 1}, + {Key: []byte("b"), Value: nil, CreateRevision: 3, ModRevision: 3, Version: 1}, + {Key: []byte("c"), Value: nil, CreateRevision: 4, ModRevision: 6, Version: 3}, + {Key: []byte("foo"), Value: nil, CreateRevision: 7, ModRevision: 7, Version: 1}, + {Key: []byte("foo/abc"), Value: nil, CreateRevision: 8, ModRevision: 8, Version: 1}, + {Key: []byte("fop"), Value: nil, CreateRevision: 9, ModRevision: 9, Version: 1}, + }, + }, // range all with SortByModRevision, SortDescend { "a", "x", diff --git a/e2e/ctl_v3_kv_test.go b/e2e/ctl_v3_kv_test.go index 0f1024236..2e2dc1836 100644 --- a/e2e/ctl_v3_kv_test.go +++ b/e2e/ctl_v3_kv_test.go @@ -85,6 +85,7 @@ func getTest(cx ctlCtx) { {[]string{"key", "--prefix", "--limit=2"}, kvs[:2]}, {[]string{"key", "--prefix", "--order=ASCEND", "--sort-by=MODIFY"}, kvs}, {[]string{"key", "--prefix", "--order=ASCEND", "--sort-by=VERSION"}, kvs}, + {[]string{"key", "--prefix", "--sort-by=CREATE"}, kvs}, // ASCEND by default {[]string{"key", "--prefix", "--order=DESCEND", "--sort-by=CREATE"}, revkvs}, {[]string{"key", "--prefix", "--order=DESCEND", "--sort-by=KEY"}, revkvs}, } diff --git a/etcdctl/ctlv3/command/get_command.go b/etcdctl/ctlv3/command/get_command.go index 809ee8df2..5f9c74f3c 100644 --- a/etcdctl/ctlv3/command/get_command.go +++ b/etcdctl/ctlv3/command/get_command.go @@ -43,7 +43,7 @@ func NewGetCommand() *cobra.Command { } cmd.Flags().StringVar(&getConsistency, "consistency", "l", "Linearizable(l) or Serializable(s)") - cmd.Flags().StringVar(&getSortOrder, "order", "", "Order of results; ASCEND or DESCEND") + cmd.Flags().StringVar(&getSortOrder, "order", "", "Order of results; ASCEND or DESCEND (ASCEND by default)") cmd.Flags().StringVar(&getSortTarget, "sort-by", "", "Sort target; CREATE, KEY, MODIFY, VALUE, or VERSION") cmd.Flags().Int64Var(&getLimit, "limit", 0, "Maximum number of results") cmd.Flags().BoolVar(&getPrefix, "prefix", false, "Get keys with matching prefix") diff --git a/etcdserver/apply.go b/etcdserver/apply.go index 6162287b9..dbdc032df 100644 --- a/etcdserver/apply.go +++ b/etcdserver/apply.go @@ -304,7 +304,14 @@ func (a *applierV3backend) Range(txnID int64, r *pb.RangeRequest) (*pb.RangeResp pruneKVs(rr, f) } - if r.SortOrder != pb.RangeRequest_NONE { + sortOrder := r.SortOrder + if r.SortTarget != pb.RangeRequest_KEY && sortOrder == pb.RangeRequest_NONE { + // Since current mvcc.Range implementation returns results + // sorted by keys in lexiographically ascending order, + // sort ASCEND by default only when target is not 'KEY' + sortOrder = pb.RangeRequest_ASCEND + } + if sortOrder != pb.RangeRequest_NONE { var sorter sort.Interface switch { case r.SortTarget == pb.RangeRequest_KEY: @@ -319,9 +326,9 @@ func (a *applierV3backend) Range(txnID int64, r *pb.RangeRequest) (*pb.RangeResp sorter = &kvSortByValue{&kvSort{rr.KVs}} } switch { - case r.SortOrder == pb.RangeRequest_ASCEND: + case sortOrder == pb.RangeRequest_ASCEND: sort.Sort(sorter) - case r.SortOrder == pb.RangeRequest_DESCEND: + case sortOrder == pb.RangeRequest_DESCEND: sort.Sort(sort.Reverse(sorter)) } } diff --git a/integration/v3_grpc_test.go b/integration/v3_grpc_test.go index 24c198b8d..36c9a9ffd 100644 --- a/integration/v3_grpc_test.go +++ b/integration/v3_grpc_test.go @@ -858,6 +858,12 @@ func TestV3RangeRequest(t *testing.T) { SortOrder: pb.RangeRequest_DESCEND, SortTarget: pb.RangeRequest_CREATE, }, + { // sort ASCEND by default + Key: []byte("a"), RangeEnd: []byte("z"), + Limit: 10, + SortOrder: pb.RangeRequest_NONE, + SortTarget: pb.RangeRequest_CREATE, + }, }, [][]string{ @@ -866,8 +872,9 @@ func TestV3RangeRequest(t *testing.T) { {"b"}, {"c"}, {}, + {"b", "a", "c", "d"}, }, - []bool{true, true, true, true, false}, + []bool{true, true, true, true, false, false}, }, // min/max mod rev {