@ -27,6 +27,7 @@ const (
|
|||||||
ExitError
|
ExitError
|
||||||
ExitBadConnection
|
ExitBadConnection
|
||||||
ExitInvalidInput // for txn, watch command
|
ExitInvalidInput // for txn, watch command
|
||||||
|
ExitBadFeature // provided a valid flag with an unsupported value
|
||||||
ExitBadArgs = 128
|
ExitBadArgs = 128
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -16,6 +16,7 @@ package command
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/spf13/cobra"
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/spf13/cobra"
|
||||||
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
||||||
@ -23,13 +24,23 @@ import (
|
|||||||
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
pb "github.com/coreos/etcd/etcdserver/etcdserverpb"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
rangeLimit int
|
||||||
|
rangeSortOrder string
|
||||||
|
rangeSortTarget string
|
||||||
|
)
|
||||||
|
|
||||||
// NewRangeCommand returns the cobra command for "range".
|
// NewRangeCommand returns the cobra command for "range".
|
||||||
func NewRangeCommand() *cobra.Command {
|
func NewRangeCommand() *cobra.Command {
|
||||||
return &cobra.Command{
|
cmd := &cobra.Command{
|
||||||
Use: "range",
|
Use: "range",
|
||||||
Short: "Range gets the keys in the range from the store.",
|
Short: "Range gets the keys in the range from the store.",
|
||||||
Run: rangeCommandFunc,
|
Run: rangeCommandFunc,
|
||||||
}
|
}
|
||||||
|
cmd.Flags().StringVar(&rangeSortOrder, "order", "", "order of results; ASCEND or DESCEND")
|
||||||
|
cmd.Flags().StringVar(&rangeSortTarget, "sort-by", "", "sort target; CREATE, KEY, MODIFY, VALUE, or VERSION")
|
||||||
|
cmd.Flags().IntVar(&rangeLimit, "limit", 0, "maximum number of results")
|
||||||
|
return cmd
|
||||||
}
|
}
|
||||||
|
|
||||||
// rangeCommandFunc executes the "range" command.
|
// rangeCommandFunc executes the "range" command.
|
||||||
@ -48,13 +59,51 @@ func rangeCommandFunc(cmd *cobra.Command, args []string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
ExitWithError(ExitError, err)
|
ExitWithError(ExitError, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sortByOrder := pb.RangeRequest_NONE
|
||||||
|
sortOrder := strings.ToUpper(rangeSortOrder)
|
||||||
|
switch {
|
||||||
|
case sortOrder == "ASCEND":
|
||||||
|
sortByOrder = pb.RangeRequest_ASCEND
|
||||||
|
case sortOrder == "DESCEND":
|
||||||
|
sortByOrder = pb.RangeRequest_DESCEND
|
||||||
|
case sortOrder == "":
|
||||||
|
sortByOrder = pb.RangeRequest_NONE
|
||||||
|
default:
|
||||||
|
ExitWithError(ExitBadFeature, fmt.Errorf("bad sort order %v", rangeSortOrder))
|
||||||
|
}
|
||||||
|
|
||||||
|
sortByTarget := pb.RangeRequest_KEY
|
||||||
|
sortTarget := strings.ToUpper(rangeSortTarget)
|
||||||
|
switch {
|
||||||
|
case sortTarget == "CREATE":
|
||||||
|
sortByTarget = pb.RangeRequest_CREATE
|
||||||
|
case sortTarget == "KEY":
|
||||||
|
sortByTarget = pb.RangeRequest_KEY
|
||||||
|
case sortTarget == "MODIFY":
|
||||||
|
sortByTarget = pb.RangeRequest_MOD
|
||||||
|
case sortTarget == "VALUE":
|
||||||
|
sortByTarget = pb.RangeRequest_VALUE
|
||||||
|
case sortTarget == "VERSION":
|
||||||
|
sortByTarget = pb.RangeRequest_VERSION
|
||||||
|
case sortTarget == "":
|
||||||
|
sortByTarget = pb.RangeRequest_KEY
|
||||||
|
default:
|
||||||
|
ExitWithError(ExitBadFeature, fmt.Errorf("bad sort target %v", rangeSortTarget))
|
||||||
|
}
|
||||||
|
|
||||||
conn, err := grpc.Dial(endpoint)
|
conn, err := grpc.Dial(endpoint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ExitWithError(ExitBadConnection, err)
|
ExitWithError(ExitBadConnection, err)
|
||||||
}
|
}
|
||||||
kv := pb.NewKVClient(conn)
|
kv := pb.NewKVClient(conn)
|
||||||
req := &pb.RangeRequest{Key: key, RangeEnd: rangeEnd}
|
req := &pb.RangeRequest{
|
||||||
|
Key: key,
|
||||||
|
RangeEnd: rangeEnd,
|
||||||
|
SortOrder: sortByOrder,
|
||||||
|
SortTarget: sortByTarget,
|
||||||
|
Limit: int64(rangeLimit),
|
||||||
|
}
|
||||||
resp, err := kv.Range(context.Background(), req)
|
resp, err := kv.Range(context.Background(), req)
|
||||||
for _, kv := range resp.Kvs {
|
for _, kv := range resp.Kvs {
|
||||||
fmt.Printf("%s %s\n", string(kv.Key), string(kv.Value))
|
fmt.Printf("%s %s\n", string(kv.Key), string(kv.Value))
|
||||||
|
@ -20,6 +20,58 @@ import fmt "fmt"
|
|||||||
// Reference imports to suppress errors if they are not otherwise used.
|
// Reference imports to suppress errors if they are not otherwise used.
|
||||||
var _ = proto.Marshal
|
var _ = proto.Marshal
|
||||||
|
|
||||||
|
type RangeRequest_SortOrder int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RangeRequest_NONE RangeRequest_SortOrder = 0
|
||||||
|
RangeRequest_ASCEND RangeRequest_SortOrder = 1
|
||||||
|
RangeRequest_DESCEND RangeRequest_SortOrder = 2
|
||||||
|
)
|
||||||
|
|
||||||
|
var RangeRequest_SortOrder_name = map[int32]string{
|
||||||
|
0: "NONE",
|
||||||
|
1: "ASCEND",
|
||||||
|
2: "DESCEND",
|
||||||
|
}
|
||||||
|
var RangeRequest_SortOrder_value = map[string]int32{
|
||||||
|
"NONE": 0,
|
||||||
|
"ASCEND": 1,
|
||||||
|
"DESCEND": 2,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x RangeRequest_SortOrder) String() string {
|
||||||
|
return proto.EnumName(RangeRequest_SortOrder_name, int32(x))
|
||||||
|
}
|
||||||
|
|
||||||
|
type RangeRequest_SortTarget int32
|
||||||
|
|
||||||
|
const (
|
||||||
|
RangeRequest_KEY RangeRequest_SortTarget = 0
|
||||||
|
RangeRequest_VERSION RangeRequest_SortTarget = 1
|
||||||
|
RangeRequest_CREATE RangeRequest_SortTarget = 2
|
||||||
|
RangeRequest_MOD RangeRequest_SortTarget = 3
|
||||||
|
RangeRequest_VALUE RangeRequest_SortTarget = 4
|
||||||
|
)
|
||||||
|
|
||||||
|
var RangeRequest_SortTarget_name = map[int32]string{
|
||||||
|
0: "KEY",
|
||||||
|
1: "VERSION",
|
||||||
|
2: "CREATE",
|
||||||
|
3: "MOD",
|
||||||
|
4: "VALUE",
|
||||||
|
}
|
||||||
|
var RangeRequest_SortTarget_value = map[string]int32{
|
||||||
|
"KEY": 0,
|
||||||
|
"VERSION": 1,
|
||||||
|
"CREATE": 2,
|
||||||
|
"MOD": 3,
|
||||||
|
"VALUE": 4,
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x RangeRequest_SortTarget) String() string {
|
||||||
|
return proto.EnumName(RangeRequest_SortTarget_name, int32(x))
|
||||||
|
}
|
||||||
|
|
||||||
type Compare_CompareResult int32
|
type Compare_CompareResult int32
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -94,6 +146,10 @@ type RangeRequest struct {
|
|||||||
// if the revision has been compacted, ErrCompaction will be returned in
|
// if the revision has been compacted, ErrCompaction will be returned in
|
||||||
// response.
|
// response.
|
||||||
Revision int64 `protobuf:"varint,4,opt,name=revision,proto3" json:"revision,omitempty"`
|
Revision int64 `protobuf:"varint,4,opt,name=revision,proto3" json:"revision,omitempty"`
|
||||||
|
// sort_order is the requested order for returned the results
|
||||||
|
SortOrder RangeRequest_SortOrder `protobuf:"varint,5,opt,name=sort_order,proto3,enum=etcdserverpb.RangeRequest_SortOrder" json:"sort_order,omitempty"`
|
||||||
|
// sort_target is the kv field to use for sorting
|
||||||
|
SortTarget RangeRequest_SortTarget `protobuf:"varint,6,opt,name=sort_target,proto3,enum=etcdserverpb.RangeRequest_SortTarget" json:"sort_target,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *RangeRequest) Reset() { *m = RangeRequest{} }
|
func (m *RangeRequest) Reset() { *m = RangeRequest{} }
|
||||||
@ -516,6 +572,8 @@ func (m *LeaseKeepAliveResponse) GetHeader() *ResponseHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
|
proto.RegisterEnum("etcdserverpb.RangeRequest_SortOrder", RangeRequest_SortOrder_name, RangeRequest_SortOrder_value)
|
||||||
|
proto.RegisterEnum("etcdserverpb.RangeRequest_SortTarget", RangeRequest_SortTarget_name, RangeRequest_SortTarget_value)
|
||||||
proto.RegisterEnum("etcdserverpb.Compare_CompareResult", Compare_CompareResult_name, Compare_CompareResult_value)
|
proto.RegisterEnum("etcdserverpb.Compare_CompareResult", Compare_CompareResult_name, Compare_CompareResult_value)
|
||||||
proto.RegisterEnum("etcdserverpb.Compare_CompareTarget", Compare_CompareTarget_name, Compare_CompareTarget_value)
|
proto.RegisterEnum("etcdserverpb.Compare_CompareTarget", Compare_CompareTarget_name, Compare_CompareTarget_value)
|
||||||
}
|
}
|
||||||
@ -1059,6 +1117,16 @@ func (m *RangeRequest) MarshalTo(data []byte) (int, error) {
|
|||||||
i++
|
i++
|
||||||
i = encodeVarintRpc(data, i, uint64(m.Revision))
|
i = encodeVarintRpc(data, i, uint64(m.Revision))
|
||||||
}
|
}
|
||||||
|
if m.SortOrder != 0 {
|
||||||
|
data[i] = 0x28
|
||||||
|
i++
|
||||||
|
i = encodeVarintRpc(data, i, uint64(m.SortOrder))
|
||||||
|
}
|
||||||
|
if m.SortTarget != 0 {
|
||||||
|
data[i] = 0x30
|
||||||
|
i++
|
||||||
|
i = encodeVarintRpc(data, i, uint64(m.SortTarget))
|
||||||
|
}
|
||||||
return i, nil
|
return i, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1971,6 +2039,12 @@ func (m *RangeRequest) Size() (n int) {
|
|||||||
if m.Revision != 0 {
|
if m.Revision != 0 {
|
||||||
n += 1 + sovRpc(uint64(m.Revision))
|
n += 1 + sovRpc(uint64(m.Revision))
|
||||||
}
|
}
|
||||||
|
if m.SortOrder != 0 {
|
||||||
|
n += 1 + sovRpc(uint64(m.SortOrder))
|
||||||
|
}
|
||||||
|
if m.SortTarget != 0 {
|
||||||
|
n += 1 + sovRpc(uint64(m.SortTarget))
|
||||||
|
}
|
||||||
return n
|
return n
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2551,6 +2625,38 @@ func (m *RangeRequest) Unmarshal(data []byte) error {
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
case 5:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field SortOrder", wireType)
|
||||||
|
}
|
||||||
|
m.SortOrder = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.SortOrder |= (RangeRequest_SortOrder(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
case 6:
|
||||||
|
if wireType != 0 {
|
||||||
|
return fmt.Errorf("proto: wrong wireType = %d for field SortTarget", wireType)
|
||||||
|
}
|
||||||
|
m.SortTarget = 0
|
||||||
|
for shift := uint(0); ; shift += 7 {
|
||||||
|
if iNdEx >= l {
|
||||||
|
return io.ErrUnexpectedEOF
|
||||||
|
}
|
||||||
|
b := data[iNdEx]
|
||||||
|
iNdEx++
|
||||||
|
m.SortTarget |= (RangeRequest_SortTarget(b) & 0x7F) << shift
|
||||||
|
if b < 0x80 {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
var sizeOfWire int
|
var sizeOfWire int
|
||||||
for {
|
for {
|
||||||
|
@ -67,6 +67,19 @@ message ResponseHeader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
message RangeRequest {
|
message RangeRequest {
|
||||||
|
enum SortOrder {
|
||||||
|
NONE = 0; // default, no sorting
|
||||||
|
ASCEND = 1; // lowest target value first
|
||||||
|
DESCEND = 2; // highest target value first
|
||||||
|
}
|
||||||
|
enum SortTarget {
|
||||||
|
KEY = 0;
|
||||||
|
VERSION = 1;
|
||||||
|
CREATE = 2;
|
||||||
|
MOD = 3;
|
||||||
|
VALUE = 4;
|
||||||
|
}
|
||||||
|
|
||||||
// if the range_end is not given, the request returns the key.
|
// if the range_end is not given, the request returns the key.
|
||||||
bytes key = 1;
|
bytes key = 1;
|
||||||
// if the range_end is given, it gets the keys in range [key, range_end).
|
// if the range_end is given, it gets the keys in range [key, range_end).
|
||||||
@ -78,6 +91,12 @@ message RangeRequest {
|
|||||||
// if the revision has been compacted, ErrCompaction will be returned in
|
// if the revision has been compacted, ErrCompaction will be returned in
|
||||||
// response.
|
// response.
|
||||||
int64 revision = 4;
|
int64 revision = 4;
|
||||||
|
|
||||||
|
// sort_order is the requested order for returned the results
|
||||||
|
SortOrder sort_order = 5;
|
||||||
|
|
||||||
|
// sort_target is the kv field to use for sorting
|
||||||
|
SortTarget sort_target = 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
message RangeResponse {
|
message RangeResponse {
|
||||||
|
@ -17,6 +17,7 @@ package etcdserver
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
|
"github.com/coreos/etcd/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
|
||||||
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
|
||||||
@ -191,6 +192,45 @@ func applyPut(txnID int64, kv dstorage.KV, p *pb.PutRequest) (*pb.PutResponse, e
|
|||||||
return resp, nil
|
return resp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type kvSort struct{ kvs []storagepb.KeyValue }
|
||||||
|
|
||||||
|
func (s *kvSort) Swap(i, j int) {
|
||||||
|
t := s.kvs[i]
|
||||||
|
s.kvs[i] = s.kvs[j]
|
||||||
|
s.kvs[j] = t
|
||||||
|
}
|
||||||
|
func (s *kvSort) Len() int { return len(s.kvs) }
|
||||||
|
|
||||||
|
type kvSortByKey struct{ *kvSort }
|
||||||
|
|
||||||
|
func (s *kvSortByKey) Less(i, j int) bool {
|
||||||
|
return bytes.Compare(s.kvs[i].Key, s.kvs[j].Key) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type kvSortByVersion struct{ *kvSort }
|
||||||
|
|
||||||
|
func (s *kvSortByVersion) Less(i, j int) bool {
|
||||||
|
return (s.kvs[i].Version - s.kvs[j].Version) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type kvSortByCreate struct{ *kvSort }
|
||||||
|
|
||||||
|
func (s *kvSortByCreate) Less(i, j int) bool {
|
||||||
|
return (s.kvs[i].CreateRevision - s.kvs[j].CreateRevision) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type kvSortByMod struct{ *kvSort }
|
||||||
|
|
||||||
|
func (s *kvSortByMod) Less(i, j int) bool {
|
||||||
|
return (s.kvs[i].ModRevision - s.kvs[j].ModRevision) < 0
|
||||||
|
}
|
||||||
|
|
||||||
|
type kvSortByValue struct{ *kvSort }
|
||||||
|
|
||||||
|
func (s *kvSortByValue) Less(i, j int) bool {
|
||||||
|
return bytes.Compare(s.kvs[i].Value, s.kvs[j].Value) < 0
|
||||||
|
}
|
||||||
|
|
||||||
func applyRange(txnID int64, kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeResponse, error) {
|
func applyRange(txnID int64, kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeResponse, error) {
|
||||||
resp := &pb.RangeResponse{}
|
resp := &pb.RangeResponse{}
|
||||||
resp.Header = &pb.ResponseHeader{}
|
resp.Header = &pb.ResponseHeader{}
|
||||||
@ -201,18 +241,49 @@ func applyRange(txnID int64, kv dstorage.KV, r *pb.RangeRequest) (*pb.RangeRespo
|
|||||||
err error
|
err error
|
||||||
)
|
)
|
||||||
|
|
||||||
|
limit := r.Limit
|
||||||
|
if r.SortOrder != pb.RangeRequest_NONE {
|
||||||
|
// fetch everything; sort and truncate afterwards
|
||||||
|
limit = 0
|
||||||
|
}
|
||||||
|
|
||||||
if txnID != noTxn {
|
if txnID != noTxn {
|
||||||
kvs, rev, err = kv.TxnRange(txnID, r.Key, r.RangeEnd, r.Limit, 0)
|
kvs, rev, err = kv.TxnRange(txnID, r.Key, r.RangeEnd, limit, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
kvs, rev, err = kv.Range(r.Key, r.RangeEnd, r.Limit, 0)
|
kvs, rev, err = kv.Range(r.Key, r.RangeEnd, limit, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if r.SortOrder != pb.RangeRequest_NONE {
|
||||||
|
var sorter sort.Interface
|
||||||
|
switch {
|
||||||
|
case r.SortTarget == pb.RangeRequest_KEY:
|
||||||
|
sorter = &kvSortByKey{&kvSort{kvs}}
|
||||||
|
case r.SortTarget == pb.RangeRequest_VERSION:
|
||||||
|
sorter = &kvSortByVersion{&kvSort{kvs}}
|
||||||
|
case r.SortTarget == pb.RangeRequest_CREATE:
|
||||||
|
sorter = &kvSortByCreate{&kvSort{kvs}}
|
||||||
|
case r.SortTarget == pb.RangeRequest_MOD:
|
||||||
|
sorter = &kvSortByMod{&kvSort{kvs}}
|
||||||
|
case r.SortTarget == pb.RangeRequest_VALUE:
|
||||||
|
sorter = &kvSortByValue{&kvSort{kvs}}
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case r.SortOrder == pb.RangeRequest_ASCEND:
|
||||||
|
sort.Sort(sorter)
|
||||||
|
case r.SortOrder == pb.RangeRequest_DESCEND:
|
||||||
|
sort.Sort(sort.Reverse(sorter))
|
||||||
|
}
|
||||||
|
if r.Limit > 0 && len(kvs) > int(r.Limit) {
|
||||||
|
kvs = kvs[:r.Limit]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
resp.Header.Revision = rev
|
resp.Header.Revision = rev
|
||||||
for i := range kvs {
|
for i := range kvs {
|
||||||
resp.Kvs = append(resp.Kvs, &kvs[i])
|
resp.Kvs = append(resp.Kvs, &kvs[i])
|
||||||
|
Reference in New Issue
Block a user