Merge branch 'scan-feature' into develop

This commit is contained in:
siddontang 2015-03-02 14:44:56 +08:00
commit b1ccbb9c75
29 changed files with 702 additions and 727 deletions

View File

@ -1,4 +1,4 @@
//This file was generated by .tools/generate_commands.py on Fri Feb 06 2015 09:15:18 +0800
//This file was generated by .tools/generate_commands.py on Mon Mar 02 2015 14:43:25 +0800
package main
var helpCommands = [][]string{
@ -20,8 +20,6 @@ var helpCommands = [][]string{
{"BRPOP", "key [key ...] timeout", "List"},
{"BSETBIT", "key offset value", "Bitmap"},
{"BTTL", "key", "Bitmap"},
{"BXREVSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
{"BXSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
{"COMMIT", "-", "Transaction"},
{"CONFIG REWRITE", "-", "Server"},
{"DECR", "key", "KV"},
@ -59,8 +57,6 @@ var helpCommands = [][]string{
{"HSET", "key field value", "Hash"},
{"HTTL", "key", "Hash"},
{"HVALS", "key", "Hash"},
{"HXREVSCAN", "key [MATCH match] [COUNT count]", "Hash"},
{"HXSCAN", "key [MATCH match] [COUNT count]", "Hash"},
{"INCR", "key", "KV"},
{"INCRBY", "key increment", "KV"},
{"INFO", "[section]", "Server"},
@ -76,8 +72,6 @@ var helpCommands = [][]string{
{"LPUSH", "key value [value ...]", "List"},
{"LRANGE", "key start stop", "List"},
{"LTTL", "key", "List"},
{"LXREVSCAN", "key [MATCH match] [COUNT count]", "List"},
{"LXSCAN", "key [MATCH match] [COUNT count]", "List"},
{"MGET", "key [key ...]", "KV"},
{"MSET", "key value [key value ...]", "KV"},
{"PERSIST", "key", "KV"},
@ -116,23 +110,13 @@ var helpCommands = [][]string{
{"STTL", "key", "Set"},
{"SUNION", "key [key ...]", "Set"},
{"SUNIONSTORE", "destination key [key ...]", "Set"},
{"SXREVSCAN", "key [MATCH match] [COUNT count]", "Set"},
{"SXSCAN", "key [MATCH match] [COUNT count]", "Set"},
{"SYNC", "logid", "Replication"},
{"TIME", "-", "Server"},
{"TTL", "key", "KV"},
{"XBREVSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
{"XBSCAN", "key [MATCH match] [COUNT count]", "Bitmap"},
{"XHREVSCAN", "key [MATCH match] [COUNT count]", "Hash"},
{"XHSCAN", "key [MATCH match] [COUNT count]", "Hash"},
{"XLREVSCAN", "key [MATCH match] [COUNT count]", "List"},
{"XLSCAN", "key [MATCH match] [COUNT count]", "List"},
{"XREVSCAN", "key [MATCH match] [COUNT count]", "KV"},
{"XSCAN", "key [MATCH match] [COUNT count]", "KV"},
{"XSREVSCAN", "key [MATCH match] [COUNT count]", "Set"},
{"XSSCAN", "key [MATCH match] [COUNT count]", "Set"},
{"XZREVSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
{"XZSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
{"XHSCAN", "key cursor [MATCH match] [COUNT count]", "Hash"},
{"XSCAN", "type cursor [MATCH match] [COUNT count]", "Server"},
{"XSSCAN", "key cursor [MATCH match] [COUNT count]", "Set"},
{"XZSCAN", "key cursor [MATCH match] [COUNT count]", "ZSet"},
{"ZADD", "key score member [score member ...]", "ZSet"},
{"ZCARD", "key", "ZSet"},
{"ZCLEAR", "key", "ZSet"},
@ -159,6 +143,4 @@ var helpCommands = [][]string{
{"ZSCORE", "key member", "ZSet"},
{"ZTTL", "key", "ZSet"},
{"ZUNIONSTORE", "destkey numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]", "ZSet"},
{"ZXREVSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
{"ZXSCAN", "key [MATCH match] [COUNT count]", "ZSet"},
}

View File

@ -560,79 +560,7 @@
"group": "Transaction",
"readonly": false
},
"XSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "KV",
"readonly": true
},
"HXSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Hash",
"readonly": true
},
"LXSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "List",
"readonly": true
},
"SXSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Set",
"readonly": true
},
"ZXSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "ZSet",
"readonly": true
},
"BXSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Bitmap",
"readonly": true
},
"XREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "KV",
"readonly": true
},
"HXREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Hash",
"readonly": true
},
"LXREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "List",
"readonly": true
},
"SXREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Set",
"readonly": true
},
"ZXREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "ZSet",
"readonly": true
},
"BXREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Bitmap",
"readonly": true
},
"FLUSHALL": {
"arguments": "-",
"group": "Server",
@ -724,66 +652,30 @@
"readonly": true
},
"XBSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Bitmap",
"readonly": true
},
"XLSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "List",
"XSCAN": {
"arguments": "type cursor [MATCH match] [COUNT count]",
"group": "Server",
"readonly": true
},
"XHSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"arguments": "key cursor [MATCH match] [COUNT count]",
"group": "Hash",
"readonly": true
},
"XSSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"arguments": "key cursor [MATCH match] [COUNT count]",
"group": "Set",
"readonly": true
},
"XZSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"arguments": "key cursor [MATCH match] [COUNT count]",
"group": "ZSet",
"readonly": true
},
"XHREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Hash",
"readonly": true
},
"XLREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "List",
"readonly": true
},
"XSREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Set",
"readonly": true
},
"XZREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "ZSet",
"readonly": true
},
"XBREVSCAN": {
"arguments": "key [MATCH match] [COUNT count]",
"group": "Bitmap",
"readonly": true
},
"RESTORE": {
"arguments" : "key ttl value",
"group" : "Server",

View File

@ -27,8 +27,6 @@ Table of Contents
- [EXPIREAT key timestamp](#expireat-key-timestamp)
- [TTL key](#ttl-key)
- [PERSIST key](#persist-key)
- [XSCAN key [MATCH match] [COUNT count]](#xscan-key-match-match-count-count)
- [XREVSCAN key [MATCH match] [COUNT count]](#xrevscan-key-match-match-count-count)
- [DUMP key](#dump-key)
- [Hash](#hash)
- [HDEL key field [field ...]](#hdel-key-field-field-)
@ -48,10 +46,6 @@ Table of Contents
- [HEXPIREAT key timestamp](#hexpireat-key-timestamp)
- [HTTL key](#httl-key)
- [HPERSIST key](#hpersist-key)
- [HXSCAN key [MATCH match] [COUNT count]](#hxscan-key-match-match-count-count)
- [HXREVSCAN key [MATCH match] [COUNT count]](#hxrevscan-key-match-match-count-count)
- [XHSCAN key [MATCH match] [COUNT count]](#xhscan-key-match-match-count-count)
- [XHREVSCAN key [MATCH match] [COUNT count]](#xhrevscan-key-match-match-count-count)
- [HDUMP key](#hdump-key)
- [List](#list)
- [BLPOP key [key ...] timeout](#blpop-key-key--timeout)
@ -69,10 +63,6 @@ Table of Contents
- [LEXPIREAT key timestamp](#lexpireat-key-timestamp)
- [LTTL key](#lttl-key)
- [LPERSIST key](#lpersist-key)
- [LXSCAN key [MATCH match] [COUNT count]](#lxscan-key-match-match-count-count)
- [LXREVSCAN key [MATCH match] [COUNT count]](#lxrevscan-key-match-match-count-count)
- [XLSCAN key [MATCH match] [COUNT count]](#xlscan-key-match-match-count-count)
- [XLREVSCAN key [MATCH match] [COUNT count]](#xlrevscan-key-match-match-count-count)
- [LDUMP key](#ldump-key)
- [Set](#set)
- [SADD key member [member ...]](#sadd-key-member-member-)
@ -92,10 +82,6 @@ Table of Contents
- [SEXPIREAT key timestamp](#sexpireat-key-timestamp)
- [STTL key](#sttl-key)
- [SPERSIST key](#spersist-key)
- [SXSCAN key [MATCH match] [COUNT count]](#sxscan-key-match-match-count-count)
- [SXREVSCAN key [MATCH match] [COUNT count]](#sxrevscan-key-match-match-count-count)
- [XSSCAN key [MATCH match] [COUNT count]](#xsscan-key-match-match-count-count)
- [XSREVSCAN key [MATCH match] [COUNT count]](#xsrevscan-key-match-match-count-count)
- [SDUMP key](#sdump-key)
- [ZSet](#zset)
- [ZADD key score member [score member ...]](#zadd-key-score-member-score-member-)
@ -122,10 +108,6 @@ Table of Contents
](#zunionstore-destination-numkeys-key-key--weights-weight-weight--aggregate-summinmax)
- [ZINTERSTORE destination numkeys key [key ...] [WEIGHTS weight [weight ...]] [AGGREGATE SUM|MIN|MAX]
](#zinterstore-destination-numkeys-key-key--weights-weight-weight--aggregate-summinmax)
- [ZXSCAN key [MATCH match] [COUNT count]](#zxscan-key-match-match-count-count)
- [ZXREVSCAN key [MATCH match] [COUNT count]](#zxrevscan-key-match-match-count-count)
- [XZSCAN key [MATCH match] [COUNT count]](#xzscan-key-match-match-count-count)
- [XZREVSCAN key [MATCH match] [COUNT count]](#xzrevscan-key-match-match-count-count)
- [ZRANGEBYLEX key min max [LIMIT offset count]](#zrangebylex-key-min-max-limit-offset-count)
- [ZREMRANGEBYLEX key min max](#zremrangebylex-key-min-max)
- [ZLEXCOUNT key min max](#zlexcount-key-min-max)
@ -141,10 +123,11 @@ Table of Contents
- [BEXPIREAT key timestamp](#bexpireat-key-timestamp)
- [BTTL key](#bttl-key)
- [BPERSIST key](#bpersist-key)
- [BXSCAN key [MATCH match] [COUNT count]](#bxscan-key-match-match-count-count)
- [BXREVSCAN key [MATCH match] [COUNT count]](#bxrevscan-key-match-match-count-count)
- [XBSCAN key [MATCH match] [COUNT count]](#xbscan-key-match-match-count-count)
- [XBREVSCAN key [MATCH match] [COUNT count]](#xbrevscan-key-match-match-count-count)
- [Scan](#scan)
- [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count)
- [XHSCAN key cursor [MATCH match] [COUNT count]](#xhscan-key-cursor-match-match-count-count)
- [XSSCAN key cursor [MATCH match] [COUNT count]](#xsscan-key-cursor-match-match-count-count)
- [XZSCAN key cursor [MATCH match] [COUNT count]](#xzscan-key-cursor-match-match-count-count)
- [Replication](#replication)
- [SLAVEOF host port [RESTART] [READONLY]](#slaveof-host-port-restart-readonly)
- [FULLSYNC [NEW]](#fullsync-new)
@ -524,82 +507,6 @@ ledis> TTL mykey
(integer) -1
```
### XSCAN key [MATCH match] [COUNT count]
Iterate KV keys incrementally.
Key is the start for the current iteration.
Match is the regexp for checking matched key.
Count is the maximum retrieved elememts number, default is 10.
**Return value**
an array of two values, first value is the key for next iteration, second value is an array of elements.
**Examples**
```
ledis>set a 1
OK
ledis>set b 2
OK
ledis>set c 3
OK
127.0.0.1:6380>xscan ""
1) ""
2) ["a" "b" "c"]
ledis>xscan "" count 1
1) "a"
2) ["a"]
ledis>xscan "a" count 1
1) "b"
2) ["b"]
ledis>xscan "b" count 1
1) "c"
2) ["c"]
ledis>xscan "c" count 1
1) ""
2) []
```
### XREVSCAN key [MATCH match] [COUNT count]
Reverse iterate KV keys incrementally.
Key is the start for the current iteration.
Match is the regexp for checking matched key.
Count is the maximum retrieved elememts number, default is 10.
**Return value**
an array of two values, first value is the key for next iteration, second value is an array of elements.
**Examples**
```
ledis>set a 1
OK
ledis>set b 2
OK
ledis>set c 3
OK
127.0.0.1:6380>xrevscan ""
1) ""
2) ["c" "b" "a"]
ledis>xrevscan "" count 1
1) "c"
2) ["c"]
ledis>xrevscan "c" count 1
1) "b"
2) ["b"]
ledis>xrevscan "b" count 1
1) "a"
2) ["a"]
ledis>xrevscan "a" count 1
1) ""
2) []
```
### DUMP key
Serialize the value stored at key with KV type in a Redis-specific format like RDB and return it to the user. The returned value can be synthesized back into a key using the RESTORE command.
@ -980,31 +887,6 @@ ledis> HPERSIST not_exists_key
(integer) 0
```
### HXSCAN key [MATCH match] [COUNT count]
Iterate Hash keys incrementally.
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### HXREVSCAN key [MATCH match] [COUNT count]
Reverse iterate Hash keys incrementally.
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
### XHSCAN key [MATCH match] [COUNT count]
Iterate Hash keys incrementally.
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### XHREVSCAN key [MATCH match] [COUNT count]
Reverse iterate Hash keys incrementally.
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
### HDUMP key
See [DUMP](#dump-key) for more information.
@ -1331,31 +1213,6 @@ ledis> LPERSIST b
(integer) 0
```
### LXSCAN key [MATCH match] [COUNT count]
Iterate list keys incrementally.
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### LXREVSCAN key [MATCH match] [COUNT count]
Reverse iterate list keys incrementally.
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
### XLSCAN key [MATCH match] [COUNT count]
Iterate list keys incrementally.
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### XLREVSCAN key [MATCH match] [COUNT count]
Reverse iterate list keys incrementally.
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
### LDUMP key
See [DUMP](#dump-key) for more information.
@ -1782,33 +1639,6 @@ ledis> STTL key
(integer) -1
```
### SXSCAN key [MATCH match] [COUNT count]
Iterate Set keys incrementally.
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### SXREVSCAN key [MATCH match] [COUNT count]
Reverse iterate Set keys incrementally.
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
### XSSCAN key [MATCH match] [COUNT count]
Iterate Set keys incrementally.
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### XSREVSCAN key [MATCH match] [COUNT count]
Reverse iterate Set keys incrementally.
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
### SDUMP key
See [DUMP](#dump-key) for more information.
@ -2432,30 +2262,6 @@ ledis> ZRANGE out 0 -1 WITHSCORES
4) "10"
```
### ZXSCAN key [MATCH match] [COUNT count]
Iterate ZSet keys incrementally.
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### ZXREVSCAN key [MATCH match] [COUNT count]
Reverse iterate ZSet keys incrementally.
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
### XZSCAN key [MATCH match] [COUNT count]
Iterate ZSet keys incrementally.
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### XZREVSCAN key [MATCH match] [COUNT count]
Reverse iterate ZSet keys incrementally.
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
### ZRANGEBYLEX key min max [LIMIT offset count]
When all the elements in a sorted set are inserted with the same score, in order to force lexicographical ordering, this command returns all the elements in the sorted set at key with a value between min and max.
@ -2684,29 +2490,60 @@ ledis> BCOUNT flag 5 6
(refer to [PERSIST](#persist-key) api for other types)
### BXSCAN key [MATCH match] [COUNT count]
## Scan
Iterate Bitmap keys incrementally.
### XSCAN type cursor [MATCH match] [COUNT count]
See [XSCAN](#xscan-key-match-match-count-count) for more information.
Iterate data type keys incrementally.
### BXREVSCAN key [MATCH match] [COUNT count]
Type is "KV", "LIST", "HASH", "SET" or "ZSET".
Cursor is the start for the current iteration.
Match is the regexp for checking matched key.
Count is the maximum retrieved elememts number, default is 10.
Reverse iterate Bitmap keys incrementally.
**Return value**
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
an array of two values, first value is the cursor for next iteration, second value is an array of elements.
### XBSCAN key [MATCH match] [COUNT count]
**Examples**
Iterate Bitmap keys incrementally.
```
ledis>set a 1
OK
ledis>set b 2
OK
ledis>set c 3
OK
127.0.0.1:6380>xscan "KV" ""
1) ""
2) ["a" "b" "c"]
ledis>xscan "KV" "" count 1
1) "a"
2) ["a"]
ledis>xscan "KV" "a" count 1
1) "b"
2) ["b"]
ledis>xscan "KV" "b" count 1
1) "c"
2) ["c"]
ledis>xscan "KV" "c" count 1
1) ""
2) []
```
See [XSCAN](#xscan-key-match-match-count-count) for more information.
### XHSCAN key cursor [MATCH match] [COUNT count]
### XBREVSCAN key [MATCH match] [COUNT count]
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count), but return array of elements
contains two elements, a field and a value.
Reverse iterate Bitmap keys incrementally.
### XSSCAN key cursor [MATCH match] [COUNT count]
See [XREVSCAN](#xrevscan-key-match-match-count-count) for more information.
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count)
### XZSCAN key cursor [MATCH match] [COUNT count]
Same like [XSCAN type cursor [MATCH match] [COUNT count]](#xscan-type-cursor-match-match-count-count), but return array of elements
contains two elements, a member and its associated score.
## Replication

View File

@ -6,6 +6,43 @@ import (
const Version = "0.4"
type DataType byte
// for out use
const (
KV DataType = iota
LIST
HASH
SET
ZSET
)
func (d DataType) String() string {
switch d {
case KV:
return KVName
case LIST:
return ListName
case HASH:
return HashName
case SET:
return SetName
case ZSET:
return ZSetName
default:
return "unknown"
}
}
const (
KVName = "KV"
ListName = "LIST"
HashName = "HASH"
SetName = "SET"
ZSetName = "ZSET"
)
// for backend store
const (
NoneType byte = 0
KVType byte = 1

View File

@ -128,7 +128,7 @@ func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) {
}
var keys [][]byte
keys, err = db.scan(metaDataType, nil, 1024, false, "")
keys, err = db.scanGeneric(metaDataType, nil, 1024, false, "", false)
for len(keys) != 0 || err != nil {
for _, key := range keys {
deleteFunc(t, key)
@ -141,7 +141,7 @@ func (db *DB) flushType(t *batch, dataType byte) (drop int64, err error) {
} else {
drop += int64(len(keys))
}
keys, err = db.scan(metaDataType, nil, 1024, false, "")
keys, err = db.scanGeneric(metaDataType, nil, 1024, false, "", false)
}
return
}

View File

@ -9,19 +9,48 @@ import (
var errDataType = errors.New("error data type")
var errMetaKey = errors.New("error meta key")
func (db *DB) scan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.scanGeneric(dataType, key, count, inclusive, match, false)
//fif inclusive is true, scan range [cursor, inf) else (cursor, inf)
func (db *DB) Scan(dataType DataType, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
storeDataType, err := getDataStoreType(dataType)
if err != nil {
return nil, err
}
return db.scanGeneric(storeDataType, cursor, count, inclusive, match, false)
}
func (db *DB) revscan(dataType byte, key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.scanGeneric(dataType, key, count, inclusive, match, true)
//if inclusive is true, revscan range (-inf, cursor] else (inf, cursor)
func (db *DB) RevScan(dataType DataType, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
storeDataType, err := getDataStoreType(dataType)
if err != nil {
return nil, err
}
return db.scanGeneric(storeDataType, cursor, count, inclusive, match, true)
}
func (db *DB) scanGeneric(dataType byte, key []byte, count int,
inclusive bool, match string, reverse bool) ([][]byte, error) {
var minKey, maxKey []byte
func getDataStoreType(dataType DataType) (byte, error) {
var storeDataType byte
switch dataType {
case KV:
storeDataType = KVType
case LIST:
storeDataType = LMetaType
case HASH:
storeDataType = HSizeType
case SET:
storeDataType = SSizeType
case ZSET:
storeDataType = ZSizeType
default:
return 0, errDataType
}
return storeDataType, nil
}
func buildMatchRegexp(match string) (*regexp.Regexp, error) {
var err error
var r *regexp.Regexp
var r *regexp.Regexp = nil
if len(match) > 0 {
if r, err = regexp.Compile(match); err != nil {
@ -29,13 +58,24 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int,
}
}
return r, nil
}
func (db *DB) scanGeneric(storeDataType byte, key []byte, count int,
inclusive bool, match string, reverse bool) ([][]byte, error) {
var minKey, maxKey []byte
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
tp := store.RangeOpen
if !reverse {
if minKey, err = db.encodeScanMinKey(dataType, key); err != nil {
if minKey, err = db.encodeScanMinKey(storeDataType, key); err != nil {
return nil, err
}
if maxKey, err = db.encodeScanMaxKey(dataType, nil); err != nil {
if maxKey, err = db.encodeScanMaxKey(storeDataType, nil); err != nil {
return nil, err
}
@ -43,10 +83,10 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int,
tp = store.RangeROpen
}
} else {
if minKey, err = db.encodeScanMinKey(dataType, nil); err != nil {
if minKey, err = db.encodeScanMinKey(storeDataType, nil); err != nil {
return nil, err
}
if maxKey, err = db.encodeScanMaxKey(dataType, key); err != nil {
if maxKey, err = db.encodeScanMaxKey(storeDataType, key); err != nil {
return nil, err
}
@ -69,7 +109,7 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int,
v := make([][]byte, 0, count)
for i := 0; it.Valid() && i < count; it.Next() {
if k, err := db.decodeScanKey(dataType, it.Key()); err != nil {
if k, err := db.decodeScanKey(storeDataType, it.Key()); err != nil {
continue
} else if r != nil && !r.Match(k) {
continue
@ -82,36 +122,36 @@ func (db *DB) scanGeneric(dataType byte, key []byte, count int,
return v, nil
}
func (db *DB) encodeScanMinKey(dataType byte, key []byte) ([]byte, error) {
func (db *DB) encodeScanMinKey(storeDataType byte, key []byte) ([]byte, error) {
if len(key) == 0 {
return db.encodeScanKey(dataType, nil)
return db.encodeScanKey(storeDataType, nil)
} else {
if err := checkKeySize(key); err != nil {
return nil, err
}
return db.encodeScanKey(dataType, key)
return db.encodeScanKey(storeDataType, key)
}
}
func (db *DB) encodeScanMaxKey(dataType byte, key []byte) ([]byte, error) {
func (db *DB) encodeScanMaxKey(storeDataType byte, key []byte) ([]byte, error) {
if len(key) > 0 {
if err := checkKeySize(key); err != nil {
return nil, err
}
return db.encodeScanKey(dataType, key)
return db.encodeScanKey(storeDataType, key)
}
k, err := db.encodeScanKey(dataType, nil)
k, err := db.encodeScanKey(storeDataType, nil)
if err != nil {
return nil, err
}
k[len(k)-1] = dataType + 1
k[len(k)-1] = storeDataType + 1
return k, nil
}
func (db *DB) encodeScanKey(dataType byte, key []byte) ([]byte, error) {
switch dataType {
func (db *DB) encodeScanKey(storeDataType byte, key []byte) ([]byte, error) {
switch storeDataType {
case KVType:
return db.encodeKVKey(key), nil
case LMetaType:
@ -120,17 +160,137 @@ func (db *DB) encodeScanKey(dataType byte, key []byte) ([]byte, error) {
return db.hEncodeSizeKey(key), nil
case ZSizeType:
return db.zEncodeSizeKey(key), nil
case BitMetaType:
return db.bEncodeMetaKey(key), nil
case SSizeType:
return db.sEncodeSizeKey(key), nil
case BitMetaType:
return db.bEncodeMetaKey(key), nil
default:
return nil, errDataType
}
}
func (db *DB) decodeScanKey(dataType byte, ek []byte) ([]byte, error) {
if len(ek) < 2 || ek[0] != db.index || ek[1] != dataType {
func (db *DB) decodeScanKey(storeDataType byte, ek []byte) ([]byte, error) {
if len(ek) < 2 || ek[0] != db.index || ek[1] != storeDataType {
return nil, errMetaKey
}
return ek[2:], nil
}
// for specail data scan
func (db *DB) buildDataScanIterator(start []byte, stop []byte, inclusive bool) *store.RangeLimitIterator {
tp := store.RangeROpen
if !inclusive {
tp = store.RangeOpen
}
it := db.bucket.RangeIterator(start, stop, tp)
return it
}
func (db *DB) HScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]FVPair, error) {
if err := checkKeySize(key); err != nil {
return nil, err
}
start := db.hEncodeHashKey(key, cursor)
stop := db.hEncodeStopKey(key)
v := make([]FVPair, 0, 16)
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
it := db.buildDataScanIterator(start, stop, inclusive)
defer it.Close()
for i := 0; it.Valid() && i < count; it.Next() {
_, f, err := db.hDecodeHashKey(it.Key())
if err != nil {
return nil, err
} else if r != nil && !r.Match(f) {
continue
}
v = append(v, FVPair{Field: f, Value: it.Value()})
i++
}
return v, nil
}
func (db *DB) SScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([][]byte, error) {
if err := checkKeySize(key); err != nil {
return nil, err
}
start := db.sEncodeSetKey(key, cursor)
stop := db.sEncodeStopKey(key)
v := make([][]byte, 0, 16)
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
it := db.buildDataScanIterator(start, stop, inclusive)
defer it.Close()
for i := 0; it.Valid() && i < count; it.Next() {
_, m, err := db.sDecodeSetKey(it.Key())
if err != nil {
return nil, err
} else if r != nil && !r.Match(m) {
continue
}
v = append(v, m)
i++
}
return v, nil
}
func (db *DB) ZScan(key []byte, cursor []byte, count int, inclusive bool, match string) ([]ScorePair, error) {
if err := checkKeySize(key); err != nil {
return nil, err
}
start := db.zEncodeSetKey(key, cursor)
stop := db.zEncodeStopSetKey(key)
v := make([]ScorePair, 0, 16)
r, err := buildMatchRegexp(match)
if err != nil {
return nil, err
}
it := db.buildDataScanIterator(start, stop, inclusive)
defer it.Close()
for i := 0; it.Valid() && i < count; it.Next() {
_, m, err := db.zDecodeSetKey(it.Key())
if err != nil {
return nil, err
} else if r != nil && !r.Match(m) {
continue
}
score, err := Int64(it.Value(), nil)
if err != nil {
return nil, err
}
v = append(v, ScorePair{Score: score, Member: m})
i++
}
return v, nil
}

View File

@ -21,13 +21,13 @@ func TestDBScan(t *testing.T) {
db.FlushAll()
if v, err := db.Scan(nil, 10, true, ""); err != nil {
if v, err := db.Scan(KV, nil, 10, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 0 {
t.Fatal(len(v))
}
if v, err := db.RevScan(nil, 10, true, ""); err != nil {
if v, err := db.RevScan(KV, nil, 10, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 0 {
t.Fatal(len(v))
@ -37,73 +37,73 @@ func TestDBScan(t *testing.T) {
db.Set([]byte("b"), []byte{})
db.Set([]byte("c"), []byte{})
if v, err := db.Scan(nil, 1, true, ""); err != nil {
if v, err := db.Scan(KV, nil, 1, true, ""); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "a")
}
if v, err := db.Scan([]byte("a"), 2, false, ""); err != nil {
if v, err := db.Scan(KV, []byte("a"), 2, false, ""); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "b", "c")
}
if v, err := db.Scan(nil, 3, true, ""); err != nil {
if v, err := db.Scan(KV, nil, 3, true, ""); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "a", "b", "c")
}
if v, err := db.Scan(nil, 3, true, "b"); err != nil {
if v, err := db.Scan(KV, nil, 3, true, "b"); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "b")
}
if v, err := db.Scan(nil, 3, true, "."); err != nil {
if v, err := db.Scan(KV, nil, 3, true, "."); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "a", "b", "c")
}
if v, err := db.Scan(nil, 3, true, "a+"); err != nil {
if v, err := db.Scan(KV, nil, 3, true, "a+"); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "a")
}
if v, err := db.RevScan(nil, 1, true, ""); err != nil {
if v, err := db.RevScan(KV, nil, 1, true, ""); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "c")
}
if v, err := db.RevScan([]byte("c"), 2, false, ""); err != nil {
if v, err := db.RevScan(KV, []byte("c"), 2, false, ""); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "b", "a")
}
if v, err := db.RevScan(nil, 3, true, ""); err != nil {
if v, err := db.RevScan(KV, nil, 3, true, ""); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "c", "b", "a")
}
if v, err := db.RevScan(nil, 3, true, "b"); err != nil {
if v, err := db.RevScan(KV, nil, 3, true, "b"); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "b")
}
if v, err := db.RevScan(nil, 3, true, "."); err != nil {
if v, err := db.RevScan(KV, nil, 3, true, "."); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "c", "b", "a")
}
if v, err := db.RevScan(nil, 3, true, "c+"); err != nil {
if v, err := db.RevScan(KV, nil, 3, true, "c+"); err != nil {
t.Fatal(err)
} else {
checkTestScan(t, v, "c")
@ -111,7 +111,7 @@ func TestDBScan(t *testing.T) {
}
func TestDBHScan(t *testing.T) {
func TestDBHKeyScan(t *testing.T) {
db := getTestDB()
db.hFlush()
@ -125,7 +125,7 @@ func TestDBHScan(t *testing.T) {
k3 := []byte("k3")
db.HSet(k3, []byte("3"), []byte{})
if v, err := db.HScan(nil, 1, true, ""); err != nil {
if v, err := db.Scan(HASH, nil, 1, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid length ", len(v))
@ -133,7 +133,7 @@ func TestDBHScan(t *testing.T) {
t.Fatal("invalid value ", string(v[0]))
}
if v, err := db.HScan(k1, 2, true, ""); err != nil {
if v, err := db.Scan(HASH, k1, 2, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal("invalid length ", len(v))
@ -143,7 +143,7 @@ func TestDBHScan(t *testing.T) {
t.Fatal("invalid value ", string(v[1]))
}
if v, err := db.HScan(k1, 2, false, ""); err != nil {
if v, err := db.Scan(HASH, k1, 2, false, ""); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal("invalid length ", len(v))
@ -155,7 +155,7 @@ func TestDBHScan(t *testing.T) {
}
func TestDBZScan(t *testing.T) {
func TestDBZKeyScan(t *testing.T) {
db := getTestDB()
db.zFlush()
@ -169,7 +169,7 @@ func TestDBZScan(t *testing.T) {
k3 := []byte("k3")
db.ZAdd(k3, ScorePair{3, []byte("m")})
if v, err := db.ZScan(nil, 1, true, ""); err != nil {
if v, err := db.Scan(ZSET, nil, 1, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid length ", len(v))
@ -177,7 +177,7 @@ func TestDBZScan(t *testing.T) {
t.Fatal("invalid value ", string(v[0]))
}
if v, err := db.ZScan(k1, 2, true, ""); err != nil {
if v, err := db.Scan(ZSET, k1, 2, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal("invalid length ", len(v))
@ -187,7 +187,7 @@ func TestDBZScan(t *testing.T) {
t.Fatal("invalid value ", string(v[1]))
}
if v, err := db.ZScan(k1, 2, false, ""); err != nil {
if v, err := db.Scan(ZSET, k1, 2, false, ""); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal("invalid length ", len(v))
@ -199,7 +199,7 @@ func TestDBZScan(t *testing.T) {
}
func TestDBLScan(t *testing.T) {
func TestDBLKeyScan(t *testing.T) {
db := getTestDB()
db.lFlush()
@ -219,7 +219,7 @@ func TestDBLScan(t *testing.T) {
t.Fatal(err.Error())
}
if v, err := db.LScan(nil, 1, true, ""); err != nil {
if v, err := db.Scan(LIST, nil, 1, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid length ", len(v))
@ -227,7 +227,7 @@ func TestDBLScan(t *testing.T) {
t.Fatal("invalid value ", string(v[0]))
}
if v, err := db.LScan(k1, 2, true, ""); err != nil {
if v, err := db.Scan(LIST, k1, 2, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal("invalid length ", len(v))
@ -237,7 +237,7 @@ func TestDBLScan(t *testing.T) {
t.Fatal("invalid value ", string(v[1]))
}
if v, err := db.LScan(k1, 2, false, ""); err != nil {
if v, err := db.Scan(LIST, k1, 2, false, ""); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal("invalid length ", len(v))
@ -249,60 +249,10 @@ func TestDBLScan(t *testing.T) {
}
func TestDBBScan(t *testing.T) {
// db := getTestDB()
// db.bFlush()
// k1 := []byte("k1")
// if _, err := db.BSetBit(k1, 1, 1); err != nil {
// t.Fatal(err.Error())
// }
// k2 := []byte("k2")
// if _, err := db.BSetBit(k2, 1, 1); err != nil {
// t.Fatal(err.Error())
// }
// k3 := []byte("k3")
// if _, err := db.BSetBit(k3, 1, 0); err != nil {
// t.Fatal(err.Error())
// }
// if v, err := db.BScan(nil, 1, true, ""); err != nil {
// t.Fatal(err)
// } else if len(v) != 1 {
// t.Fatal("invalid length ", len(v))
// } else if string(v[0]) != "k1" {
// t.Fatal("invalid value ", string(v[0]))
// }
// if v, err := db.BScan(k1, 2, true, ""); err != nil {
// t.Fatal(err)
// } else if len(v) != 2 {
// t.Fatal("invalid length ", len(v))
// } else if string(v[0]) != "k1" {
// t.Fatal("invalid value ", string(v[0]))
// } else if string(v[1]) != "k2" {
// t.Fatal("invalid value ", string(v[1]))
// }
// if v, err := db.BScan(k1, 2, false, ""); err != nil {
// t.Fatal(err)
// } else if len(v) != 2 {
// t.Fatal("invalid length ", len(v))
// } else if string(v[0]) != "k2" {
// t.Fatal("invalid value ", string(v[0]))
// } else if string(v[1]) != "k3" {
// t.Fatal("invalid value ", string(v[1]))
// }
}
func TestDBSScan(t *testing.T) {
func TestDBSKeyScan(t *testing.T) {
db := getTestDB()
db.bFlush()
db.sFlush()
k1 := []byte("k1")
if _, err := db.SAdd(k1, []byte("1")); err != nil {
@ -319,7 +269,7 @@ func TestDBSScan(t *testing.T) {
t.Fatal(err.Error())
}
if v, err := db.SScan(nil, 1, true, ""); err != nil {
if v, err := db.Scan(SET, nil, 1, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid length ", len(v))
@ -327,7 +277,7 @@ func TestDBSScan(t *testing.T) {
t.Fatal("invalid value ", string(v[0]))
}
if v, err := db.SScan(k1, 2, true, ""); err != nil {
if v, err := db.Scan(SET, k1, 2, true, ""); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal("invalid length ", len(v))
@ -337,7 +287,7 @@ func TestDBSScan(t *testing.T) {
t.Fatal("invalid value ", string(v[1]))
}
if v, err := db.SScan(k1, 2, false, ""); err != nil {
if v, err := db.Scan(SET, k1, 2, false, ""); err != nil {
t.Fatal(err)
} else if len(v) != 2 {
t.Fatal("invalid length ", len(v))
@ -346,5 +296,77 @@ func TestDBSScan(t *testing.T) {
} else if string(v[1]) != "k3" {
t.Fatal("invalid value ", string(v[1]))
}
}
func TestDBHScan(t *testing.T) {
db := getTestDB()
key := []byte("scan_h_key")
value := []byte("hello world")
db.HSet(key, []byte("1"), value)
db.HSet(key, []byte("222"), value)
db.HSet(key, []byte("19"), value)
db.HSet(key, []byte("1234"), value)
v, err := db.HScan(key, nil, 100, true, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 4 {
t.Fatal("invalid count", len(v))
}
v, err = db.HScan(key, []byte("19"), 1, false, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid count", len(v))
} else if string(v[0].Field) != "222" {
t.Fatal(string(v[0].Field))
}
}
func TestDBSScan(t *testing.T) {
db := getTestDB()
key := []byte("scan_s_key")
db.SAdd(key, []byte("1"), []byte("222"), []byte("19"), []byte("1234"))
v, err := db.SScan(key, nil, 100, true, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 4 {
t.Fatal("invalid count", len(v))
}
v, err = db.SScan(key, []byte("19"), 1, false, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid count", len(v))
} else if string(v[0]) != "222" {
t.Fatal(string(v[0]))
}
}
func TestDBZScan(t *testing.T) {
db := getTestDB()
key := []byte("scan_z_key")
db.ZAdd(key, ScorePair{1, []byte("1")}, ScorePair{2, []byte("222")}, ScorePair{3, []byte("19")}, ScorePair{4, []byte("1234")})
v, err := db.ZScan(key, nil, 100, true, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 4 {
t.Fatal("invalid count", len(v))
}
v, err = db.ZScan(key, []byte("19"), 1, false, "")
if err != nil {
t.Fatal(err)
} else if len(v) != 1 {
t.Fatal("invalid count", len(v))
} else if string(v[0].Member) != "222" {
t.Fatal(string(v[0].Member))
}
}

View File

@ -922,16 +922,6 @@ func (db *DB) BPersist(key []byte) (int64, error) {
return n, err
}
func (db *DB) BScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
log.Error("bitmap type will be deprecated later, please use bit operations in kv type")
return db.scan(BitMetaType, key, count, inclusive, match)
}
func (db *DB) BRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
log.Error("bitmap type will be deprecated later, please use bit operations in kv type")
return db.revscan(BitMetaType, key, count, inclusive, match)
}
func (db *DB) bFlush() (drop int64, err error) {
t := db.binBatch
t.Lock()

View File

@ -354,6 +354,8 @@ func (db *DB) HGetAll(key []byte) ([]FVPair, error) {
v := make([]FVPair, 0, 16)
it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
defer it.Close()
for ; it.Valid(); it.Next() {
_, f, err := db.hDecodeHashKey(it.Key())
if err != nil {
@ -363,8 +365,6 @@ func (db *DB) HGetAll(key []byte) ([]FVPair, error) {
v = append(v, FVPair{Field: f, Value: it.Value()})
}
it.Close()
return v, nil
}
@ -379,6 +379,8 @@ func (db *DB) HKeys(key []byte) ([][]byte, error) {
v := make([][]byte, 0, 16)
it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
defer it.Close()
for ; it.Valid(); it.Next() {
_, f, err := db.hDecodeHashKey(it.Key())
if err != nil {
@ -387,8 +389,6 @@ func (db *DB) HKeys(key []byte) ([][]byte, error) {
v = append(v, f)
}
it.Close()
return v, nil
}
@ -403,6 +403,8 @@ func (db *DB) HValues(key []byte) ([][]byte, error) {
v := make([][]byte, 0, 16)
it := db.bucket.RangeLimitIterator(start, stop, store.RangeROpen, 0, -1)
defer it.Close()
for ; it.Valid(); it.Next() {
_, _, err := db.hDecodeHashKey(it.Key())
if err != nil {
@ -412,8 +414,6 @@ func (db *DB) HValues(key []byte) ([][]byte, error) {
v = append(v, it.Value())
}
it.Close()
return v, nil
}
@ -460,14 +460,6 @@ func (db *DB) hFlush() (drop int64, err error) {
return db.flushType(t, HashType)
}
func (db *DB) HScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.scan(HSizeType, key, count, inclusive, match)
}
func (db *DB) HRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.revscan(HSizeType, key, count, inclusive, match)
}
func (db *DB) HExpire(key []byte, duration int64) (int64, error) {
if duration <= 0 {
return 0, errExpireValue

View File

@ -114,7 +114,7 @@ func TestHFlush(t *testing.T) {
}
}
if v, err := db.HScan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(HASH, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 2000 {
t.Fatal("invalid value ", len(v))
@ -135,7 +135,7 @@ func TestHFlush(t *testing.T) {
t.Fatal("invalid value ", n)
}
if v, err := db.HScan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(HASH, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 0 {
t.Fatal("invalid value length ", len(v))

View File

@ -347,16 +347,6 @@ func (db *DB) flush() (drop int64, err error) {
return db.flushType(t, KVType)
}
//if inclusive is true, scan range [key, inf) else (key, inf)
func (db *DB) Scan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.scan(KVType, key, count, inclusive, match)
}
//if inclusive is true, revscan range (-inf, key] else (inf, key)
func (db *DB) RevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.revscan(KVType, key, count, inclusive, match)
}
func (db *DB) Expire(key []byte, duration int64) (int64, error) {
if duration <= 0 {
return 0, errExpireValue

View File

@ -282,7 +282,7 @@ func TestKVFlush(t *testing.T) {
}
}
if v, err := db.Scan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(KV, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 2000 {
t.Fatal("invalid value ", len(v))
@ -303,7 +303,7 @@ func TestKVFlush(t *testing.T) {
t.Fatal("invalid value ", n)
}
if v, err := db.Scan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(KV, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 0 {
t.Fatal("invalid value length ", len(v))

View File

@ -480,14 +480,6 @@ func (db *DB) LPersist(key []byte) (int64, error) {
return n, err
}
func (db *DB) LScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.scan(LMetaType, key, count, inclusive, match)
}
func (db *DB) LRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.revscan(LMetaType, key, count, inclusive, match)
}
func (db *DB) lEncodeMinKey() []byte {
return db.lEncodeMetaKey(nil)
}

View File

@ -145,7 +145,7 @@ func TestLFlush(t *testing.T) {
}
}
if v, err := db.LScan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(LIST, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 2000 {
t.Fatal("invalid value ", len(v))
@ -157,7 +157,7 @@ func TestLFlush(t *testing.T) {
t.Fatal("invalid value ", n)
}
if v, err := db.LScan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(LIST, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 0 {
t.Fatal("invalid value length ", len(v))

View File

@ -607,11 +607,3 @@ func (db *DB) SPersist(key []byte) (int64, error) {
err = t.Commit()
return n, err
}
func (db *DB) SScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.scan(SSizeType, key, count, inclusive, match)
}
func (db *DB) SRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.revscan(SSizeType, key, count, inclusive, match)
}

View File

@ -352,7 +352,7 @@ func TestSFlush(t *testing.T) {
}
}
if v, err := db.SScan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(SET, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 2000 {
t.Fatal("invalid value ", len(v))
@ -364,7 +364,7 @@ func TestSFlush(t *testing.T) {
t.Fatal("invalid value ", n)
}
if v, err := db.SScan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(SET, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 0 {
t.Fatal("invalid value length ", len(v))

View File

@ -936,14 +936,6 @@ func (db *DB) ZInterStore(destKey []byte, srcKeys [][]byte, weights []int64, agg
return n, nil
}
func (db *DB) ZScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.scan(ZSizeType, key, count, inclusive, match)
}
func (db *DB) ZRevScan(key []byte, count int, inclusive bool, match string) ([][]byte, error) {
return db.revscan(ZSizeType, key, count, inclusive, match)
}
func (db *DB) ZRangeByLex(key []byte, min []byte, max []byte, rangeType uint8, offset int, count int) ([][]byte, error) {
if min == nil {
min = db.zEncodeStartSetKey(key)

View File

@ -391,7 +391,7 @@ func TestZScan(t *testing.T) {
}
}
if v, err := db.ZScan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(ZSET, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 2000 {
t.Fatal("invalid value ", len(v))
@ -403,7 +403,7 @@ func TestZScan(t *testing.T) {
t.Fatal("invalid value ", n)
}
if v, err := db.ZScan(nil, 3000, true, ""); err != nil {
if v, err := db.Scan(ZSET, nil, 3000, true, ""); err != nil {
t.Fatal(err.Error())
} else if len(v) != 0 {
t.Fatal("invalid value length ", len(v))

View File

@ -274,14 +274,6 @@ func bpersistCommand(c *client) error {
return nil
}
func bxscanCommand(c *client) error {
return xscanGeneric(c, c.db.BScan)
}
func bxrevscanCommand(c *client) error {
return xscanGeneric(c, c.db.BRevScan)
}
func init() {
register("bget", bgetCommand)
register("bdelete", bdeleteCommand)
@ -294,8 +286,4 @@ func init() {
register("bexpireat", bexpireAtCommand)
register("bttl", bttlCommand)
register("bpersist", bpersistCommand)
register("bxscan", bxscanCommand)
register("bxrevscan", bxrevscanCommand)
register("xbscan", bxscanCommand)
register("xbrevscan", bxrevscanCommand)
}

View File

@ -292,14 +292,6 @@ func hpersistCommand(c *client) error {
return nil
}
func hxscanCommand(c *client) error {
return xscanGeneric(c, c.db.HScan)
}
func hxrevscanCommand(c *client) error {
return xscanGeneric(c, c.db.HRevScan)
}
func xhexistsCommand(c *client) error {
args := c.args
if len(args) != 1 {
@ -334,9 +326,5 @@ func init() {
register("hexpireat", hexpireAtCommand)
register("httl", httlCommand)
register("hpersist", hpersistCommand)
register("hxscan", hxscanCommand)
register("hxrevscan", hxrevscanCommand)
register("xhscan", hxscanCommand)
register("xhrevscan", hxrevscanCommand)
register("xhexists", xhexistsCommand)
}

View File

@ -1,10 +1,8 @@
package server
import (
"github.com/siddontang/go/hack"
"github.com/siddontang/ledisdb/ledis"
"strconv"
"strings"
)
// func getCommand(c *client) error {
@ -315,82 +313,6 @@ func persistCommand(c *client) error {
return nil
}
func parseScanArgs(c *client) (key []byte, match string, count int, err error) {
args := c.args
count = 10
switch len(args) {
case 0:
key = nil
return
case 1, 3, 5:
key = args[0]
break
default:
err = ErrCmdParams
return
}
if len(args) == 3 {
switch strings.ToLower(hack.String(args[1])) {
case "match":
match = hack.String(args[2])
case "count":
count, err = strconv.Atoi(hack.String(args[2]))
default:
err = ErrCmdParams
return
}
} else if len(args) == 5 {
if strings.ToLower(hack.String(args[1])) != "match" {
err = ErrCmdParams
return
} else if strings.ToLower(hack.String(args[3])) != "count" {
err = ErrCmdParams
return
}
match = hack.String(args[2])
count, err = strconv.Atoi(hack.String(args[4]))
}
if count <= 0 {
err = ErrCmdParams
}
return
}
func xscanGeneric(c *client,
f func(key []byte, count int, inclusive bool, match string) ([][]byte, error)) error {
key, match, count, err := parseScanArgs(c)
if err != nil {
return err
}
if ay, err := f(key, count, false, match); err != nil {
return err
} else {
data := make([]interface{}, 2)
if len(ay) < count {
data[0] = []byte("")
} else {
data[0] = ay[len(ay)-1]
}
data[1] = ay
c.resp.writeArray(data)
}
return nil
}
func xscanCommand(c *client) error {
return xscanGeneric(c, c.db.Scan)
}
func xrevscanCommand(c *client) error {
return xscanGeneric(c, c.db.RevScan)
}
func appendCommand(c *client) error {
args := c.args
if len(args) != 2 {
@ -619,6 +541,4 @@ func init() {
register("expireat", expireAtCommand)
register("ttl", ttlCommand)
register("persist", persistCommand)
register("xscan", xscanCommand)
register("xrevscan", xrevscanCommand)
}

View File

@ -231,14 +231,6 @@ func lpersistCommand(c *client) error {
return nil
}
func lxscanCommand(c *client) error {
return xscanGeneric(c, c.db.LScan)
}
func lxrevscanCommand(c *client) error {
return xscanGeneric(c, c.db.LRevScan)
}
func blpopCommand(c *client) error {
keys, timeout, err := lParseBPopArgs(c)
if err != nil {
@ -317,9 +309,5 @@ func init() {
register("lexpireat", lexpireAtCommand)
register("lttl", lttlCommand)
register("lpersist", lpersistCommand)
register("lxscan", lxscanCommand)
register("lxrevscan", lxrevscanCommand)
register("xlscan", lxscanCommand)
register("xlrevscan", lxrevscanCommand)
register("xlexists", xlexistsCommand)
}

View File

@ -165,15 +165,15 @@ func xttl(db *ledis.DB, tp string, key []byte) (int64, error) {
func xscan(db *ledis.DB, tp string, count int) ([][]byte, error) {
switch strings.ToUpper(tp) {
case "KV":
return db.Scan(nil, count, false, "")
return db.Scan(KV, nil, count, false, "")
case "HASH":
return db.HScan(nil, count, false, "")
return db.Scan(HASH, nil, count, false, "")
case "LIST":
return db.LScan(nil, count, false, "")
return db.Scan(LIST, nil, count, false, "")
case "SET":
return db.SScan(nil, count, false, "")
return db.Scan(SET, nil, count, false, "")
case "ZSET":
return db.ZScan(nil, count, false, "")
return db.Scan(ZSET, nil, count, false, "")
default:
return nil, fmt.Errorf("invalid key type %s", tp)
}

View File

@ -14,8 +14,8 @@ func checkDataEqual(master *App, slave *App) error {
mdb, _ := master.ldb.Select(0)
sdb, _ := slave.ldb.Select(0)
mkeys, _ := mdb.Scan(nil, 100, true, "")
skeys, _ := sdb.Scan(nil, 100, true, "")
mkeys, _ := mdb.Scan(KV, nil, 100, true, "")
skeys, _ := sdb.Scan(KV, nil, 100, true, "")
if len(mkeys) != len(skeys) {
return fmt.Errorf("keys number not equal %d != %d", len(mkeys), len(skeys))

215
server/cmd_scan.go Normal file
View File

@ -0,0 +1,215 @@
package server
import (
"fmt"
"github.com/siddontang/go/hack"
"github.com/siddontang/go/num"
"github.com/siddontang/ledisdb/ledis"
"strconv"
"strings"
)
func parseScanArgs(args [][]byte) (cursor []byte, match string, count int, err error) {
cursor = args[0]
args = args[1:]
count = 10
for i := 0; i < len(args); {
switch strings.ToUpper(hack.String(args[i])) {
case "MATCH":
if i+1 >= len(args) {
err = ErrCmdParams
return
}
match = hack.String(args[i+1])
i = i + 2
case "COUNT":
if i+1 >= len(args) {
err = ErrCmdParams
return
}
count, err = strconv.Atoi(hack.String(args[i+1]))
if err != nil {
return
}
i = i + 2
default:
err = fmt.Errorf("invalid argument %s", args[i])
return
}
}
return
}
// XSCAN type cursor [MATCH match] [COUNT count]
func xscanCommand(c *client) error {
args := c.args
if len(args) < 2 {
return ErrCmdParams
}
var dataType ledis.DataType
switch strings.ToUpper(hack.String(args[0])) {
case "KV":
dataType = ledis.KV
case "HASH":
dataType = ledis.HASH
case "LIST":
dataType = ledis.LIST
case "SET":
dataType = ledis.SET
case "ZSET":
dataType = ledis.ZSET
default:
return fmt.Errorf("invalid key type %s", args[0])
}
cursor, match, count, err := parseScanArgs(args[1:])
if err != nil {
return err
}
ay, err := c.db.Scan(dataType, cursor, count, false, match)
if err != nil {
return err
}
data := make([]interface{}, 2)
if len(ay) < count {
data[0] = []byte("")
} else {
data[0] = ay[len(ay)-1]
}
data[1] = ay
c.resp.writeArray(data)
return nil
}
// XHSCAN key cursor [MATCH match] [COUNT count]
func xhscanCommand(c *client) error {
args := c.args
if len(args) < 2 {
return ErrCmdParams
}
key := args[0]
cursor, match, count, err := parseScanArgs(args[1:])
if err != nil {
return err
}
ay, err := c.db.HScan(key, cursor, count, false, match)
if err != nil {
return err
}
data := make([]interface{}, 2)
if len(ay) < count {
data[0] = []byte("")
} else {
data[0] = ay[len(ay)-1].Field
}
vv := make([][]byte, 0, len(ay)*2)
for _, v := range ay {
vv = append(vv, v.Field, v.Value)
}
data[1] = vv
c.resp.writeArray(data)
return nil
}
// XSSCAN key cursor [MATCH match] [COUNT count]
func xsscanCommand(c *client) error {
args := c.args
if len(args) < 2 {
return ErrCmdParams
}
key := args[0]
cursor, match, count, err := parseScanArgs(args[1:])
if err != nil {
return err
}
ay, err := c.db.SScan(key, cursor, count, false, match)
if err != nil {
return err
}
data := make([]interface{}, 2)
if len(ay) < count {
data[0] = []byte("")
} else {
data[0] = ay[len(ay)-1]
}
data[1] = ay
c.resp.writeArray(data)
return nil
}
// XZSCAN key cursor [MATCH match] [COUNT count]
func xzscanCommand(c *client) error {
args := c.args
if len(args) < 2 {
return ErrCmdParams
}
key := args[0]
cursor, match, count, err := parseScanArgs(args[1:])
if err != nil {
return err
}
ay, err := c.db.ZScan(key, cursor, count, false, match)
if err != nil {
return err
}
data := make([]interface{}, 2)
if len(ay) < count {
data[0] = []byte("")
} else {
data[0] = ay[len(ay)-1].Member
}
vv := make([][]byte, 0, len(ay)*2)
for _, v := range ay {
vv = append(vv, v.Member, num.FormatInt64ToSlice(v.Score))
}
data[1] = vv
c.resp.writeArray(data)
return nil
}
func init() {
register("xscan", xscanCommand)
register("xhscan", xhscanCommand)
register("xsscan", xsscanCommand)
register("xzscan", xzscanCommand)
}

View File

@ -29,14 +29,14 @@ func TestScan(t *testing.T) {
defer c.Close()
testKVScan(t, c)
testHashScan(t, c)
testListScan(t, c)
testZSetScan(t, c)
testSetScan(t, c)
testHashKeyScan(t, c)
testListKeyScan(t, c)
testZSetKeyScan(t, c)
testSetKeyScan(t, c)
}
func checkScanValues(t *testing.T, ay interface{}, values ...int) {
func checkScanValues(t *testing.T, ay interface{}, values ...interface{}) {
a, err := ledis.Strings(ay, nil)
if err != nil {
t.Fatal(err)
@ -47,14 +47,14 @@ func checkScanValues(t *testing.T, ay interface{}, values ...int) {
}
for i, v := range a {
if string(v) != fmt.Sprintf("%d", values[i]) {
t.Fatal(fmt.Sprintf("%d %s != %d", string(v), values[i]))
if string(v) != fmt.Sprintf("%v", values[i]) {
t.Fatal(fmt.Sprintf("%d %s != %v", string(v), values[i]))
}
}
}
func checkScan(t *testing.T, c *ledis.Client, cmd string) {
if ay, err := ledis.Values(c.Do(cmd, "", "count", 5)); err != nil {
func checkScan(t *testing.T, c *ledis.Client, tp string) {
if ay, err := ledis.Values(c.Do("XSCAN", tp, "", "count", 5)); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
@ -64,7 +64,7 @@ func checkScan(t *testing.T, c *ledis.Client, cmd string) {
checkScanValues(t, ay[1], 0, 1, 2, 3, 4)
}
if ay, err := ledis.Values(c.Do(cmd, "4", "count", 6)); err != nil {
if ay, err := ledis.Values(c.Do("XSCAN", tp, "4", "count", 6)); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
@ -76,29 +76,6 @@ func checkScan(t *testing.T, c *ledis.Client, cmd string) {
}
func checkRevScan(t *testing.T, c *ledis.Client, cmd string) {
if ay, err := ledis.Values(c.Do(cmd, "", "count", 5)); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
} else if n := ay[0].([]byte); string(n) != "5" {
t.Fatal(string(n))
} else {
checkScanValues(t, ay[1], 9, 8, 7, 6, 5)
}
if ay, err := ledis.Values(c.Do(cmd, "5", "count", 6)); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
} else if n := ay[0].([]byte); string(n) != "" {
t.Fatal(string(n))
} else {
checkScanValues(t, ay[1], 4, 3, 2, 1, 0)
}
}
func testKVScan(t *testing.T, c *ledis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("set", fmt.Sprintf("%d", i), []byte("value")); err != nil {
@ -106,50 +83,95 @@ func testKVScan(t *testing.T, c *ledis.Client) {
}
}
checkScan(t, c, "xscan")
checkRevScan(t, c, "xrevscan")
checkScan(t, c, "KV")
}
func testHashScan(t *testing.T, c *ledis.Client) {
func testHashKeyScan(t *testing.T, c *ledis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("hset", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i), []byte("value")); err != nil {
t.Fatal(err)
}
}
checkScan(t, c, "xhscan")
checkRevScan(t, c, "xhrevscan")
checkScan(t, c, "HASH")
}
func testListScan(t *testing.T, c *ledis.Client) {
func testListKeyScan(t *testing.T, c *ledis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("lpush", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i)); err != nil {
t.Fatal(err)
}
}
checkScan(t, c, "xlscan")
checkRevScan(t, c, "xlrevscan")
checkScan(t, c, "LIST")
}
func testZSetScan(t *testing.T, c *ledis.Client) {
func testZSetKeyScan(t *testing.T, c *ledis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("zadd", fmt.Sprintf("%d", i), i, []byte("value")); err != nil {
t.Fatal(err)
}
}
checkScan(t, c, "zxscan")
checkRevScan(t, c, "zxrevscan")
checkScan(t, c, "ZSET")
}
func testSetScan(t *testing.T, c *ledis.Client) {
func testSetKeyScan(t *testing.T, c *ledis.Client) {
for i := 0; i < 10; i++ {
if _, err := c.Do("sadd", fmt.Sprintf("%d", i), fmt.Sprintf("%d", i)); err != nil {
t.Fatal(err)
}
}
checkScan(t, c, "xsscan")
checkRevScan(t, c, "xsrevscan")
checkScan(t, c, "SET")
}
func TestHashScan(t *testing.T) {
c := getTestConn()
defer c.Close()
key := "scan_hash"
c.Do("HMSET", key, "a", 1, "b", 2)
if ay, err := ledis.Values(c.Do("XHSCAN", key, "")); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
} else {
checkScanValues(t, ay[1], "a", 1, "b", 2)
}
}
func TestSetScan(t *testing.T) {
c := getTestConn()
defer c.Close()
key := "scan_set"
c.Do("SADD", key, "a", "b")
if ay, err := ledis.Values(c.Do("XSSCAN", key, "")); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
} else {
checkScanValues(t, ay[1], "a", "b")
}
}
func TestZSetScan(t *testing.T) {
c := getTestConn()
defer c.Close()
key := "scan_zset"
c.Do("ZADD", key, 1, "a", 2, "b")
if ay, err := ledis.Values(c.Do("XZSCAN", key, "")); err != nil {
t.Fatal(err)
} else if len(ay) != 2 {
t.Fatal(len(ay))
} else {
checkScanValues(t, ay[1], "a", 1, "b", 2)
}
}

View File

@ -262,14 +262,6 @@ func spersistCommand(c *client) error {
return nil
}
func sxscanCommand(c *client) error {
return xscanGeneric(c, c.db.SScan)
}
func sxrevscanCommand(c *client) error {
return xscanGeneric(c, c.db.SRevScan)
}
func xsexistsCommand(c *client) error {
args := c.args
if len(args) != 1 {
@ -301,10 +293,6 @@ func init() {
register("sexpireat", sexpireAtCommand)
register("sttl", sttlCommand)
register("spersist", spersistCommand)
register("sxscan", sxscanCommand)
register("sxrevscan", sxrevscanCommand)
register("xsscan", sxscanCommand)
register("xsrevscan", sxrevscanCommand)
register("xsexists", xsexistsCommand)
}

View File

@ -641,14 +641,6 @@ func zinterstoreCommand(c *client) error {
return err
}
func zxscanCommand(c *client) error {
return xscanGeneric(c, c.db.ZScan)
}
func zxrevscanCommand(c *client) error {
return xscanGeneric(c, c.db.ZRevScan)
}
func zparseMemberRange(minBuf []byte, maxBuf []byte) (min []byte, max []byte, rangeType uint8, err error) {
rangeType = store.RangeClose
if strings.ToLower(hack.String(minBuf)) == "-" {
@ -815,9 +807,5 @@ func init() {
register("zexpireat", zexpireAtCommand)
register("zttl", zttlCommand)
register("zpersist", zpersistCommand)
register("zxscan", zxscanCommand)
register("zxrevscan", zxrevscanCommand)
register("xzscan", zxscanCommand)
register("xzrevscan", zxrevscanCommand)
register("xzexists", xzexistsCommand)
}

View File

@ -2,6 +2,7 @@ package server
import (
"errors"
"github.com/siddontang/ledisdb/ledis"
)
var (
@ -26,12 +27,11 @@ var (
)
const (
KV = iota
LIST
HASH
SET
ZSET
BIT
KV ledis.DataType = ledis.KV
LIST = ledis.LIST
HASH = ledis.HASH
SET = ledis.SET
ZSET = ledis.ZSET
)
const (