func myScan(in proto.PkgScanReq, au Authorize, t *testing.T) proto.PkgScanResp { var pkg = make([]byte, in.Length()) _, err := in.Encode(pkg) if err != nil { t.Fatalf("Encode failed: ", err) } pkg = testTbl.Scan(&PkgArgs{in.Cmd, in.DbId, in.Seq, pkg}, au) var out proto.PkgScanResp _, err = out.Decode(pkg) if err != nil { t.Fatalf("Decode failed: ", err) } if out.ErrCode != 0 { t.Fatalf("Failed with ErrCode %d", out.ErrCode) } if out.DbId != in.DbId || out.Seq != in.Seq { t.Fatalf("DbId/Seq mismatch") } return out }
func (tbl *Table) Scan(req *PkgArgs, au Authorize) []byte { var out proto.PkgScanResp out.Cmd = req.Cmd out.DbId = req.DbId out.Seq = req.Seq var in proto.PkgScanReq n, err := in.Decode(req.Pkg) if err != nil || n != len(req.Pkg) { return errorHandle(&out, table.EcDecodeFail) } out.PkgFlag = in.PkgFlag if in.DbId == proto.AdminDbId { return errorHandle(&out, table.EcInvDbId) } if !au.IsAuth(in.DbId) { return errorHandle(&out, table.EcNoPrivilege) } if in.ColSpace == proto.ColSpaceScore1 { tbl.zScanSortScore(&in, &out) return replyHandle(&out) } var scanColSpace uint8 = proto.ColSpaceDefault if in.ColSpace == proto.ColSpaceScore2 { scanColSpace = proto.ColSpaceScore2 } var it = tbl.db.NewIterator(nil) defer it.Destroy() var scanAsc = (in.PkgFlag&proto.FlagScanAsc != 0) var startSeek = (in.PkgFlag&proto.FlagScanKeyStart != 0) if scanAsc { if startSeek { // Seek to the first element it.Seek(getRawKey(in.DbId, in.TableId, scanColSpace, in.RowKey, nil)) } else { it.Seek(getRawKey(in.DbId, in.TableId, scanColSpace, in.RowKey, in.ColKey)) } } else { if startSeek { // Seek to the last element it.Seek(getRawKey(in.DbId, in.TableId, scanColSpace+1, in.RowKey, nil)) } else { it.Seek(getRawKey(in.DbId, in.TableId, scanColSpace, in.RowKey, in.ColKey)) } if !it.Valid() { it.SeekToLast() } } out.PkgFlag |= proto.FlagScanEnd var first = true var scanNum = int(in.Num) var pkgLen = proto.HeadSize + 1000 for i := 0; it.Valid() && i < scanNum+1; iterMove(it, scanAsc) { _, dbId, tableId, colSpace, rowKey, colKey := parseRawKey(it.Key()) if dbId != in.DbId || tableId != in.TableId || colSpace != scanColSpace || bytes.Compare(rowKey, in.RowKey) != 0 { if first { first = false continue } else { break } } if first { first = false } if !startSeek { if scanAsc { if bytes.Compare(colKey, in.ColKey) <= 0 { continue } } else { if bytes.Compare(colKey, in.ColKey) >= 0 { continue } } } if i < scanNum { var kv proto.KeyValue kv.TableId = in.TableId kv.RowKey = in.RowKey kv.ColKey = colKey kv.Value, kv.Score = parseRawValue(it.Value()) if len(kv.Value) > 0 { kv.CtrlFlag |= proto.CtrlValue } if kv.Score != 0 { kv.CtrlFlag |= proto.CtrlScore } out.Kvs = append(out.Kvs, kv) pkgLen += kv.Length() if pkgLen > proto.MaxPkgLen/2 { out.PkgFlag &^= proto.FlagScanEnd break } } else { out.PkgFlag &^= proto.FlagScanEnd break } i++ } return replyHandle(&out) }
func TestTableZScan(t *testing.T) { // MZSET { var in proto.PkgMultiOp in.Cmd = proto.CmdMSet in.DbId = 2 in.Seq = 20 in.PkgFlag |= proto.FlagZop in.Kvs = append(in.Kvs, getTestKV(2, []byte("row2"), []byte("col0"), []byte("v0"), 40, 0)) in.Kvs = append(in.Kvs, getTestKV(2, []byte("row2"), []byte("col1"), []byte("v1"), 30, 0)) in.Kvs = append(in.Kvs, getTestKV(2, []byte("row2"), []byte("col2"), []byte("v2"), 20, 0)) in.Kvs = append(in.Kvs, getTestKV(2, []byte("row2"), []byte("col3"), []byte("v3"), 10, 0)) in.Kvs = append(in.Kvs, getTestKV(2, []byte("row2"), []byte("col4"), []byte("v4"), 0, 0)) myMSet(in, testAuth, getTestWA(), true, t) } // ZSCAN ASC order by SCORE var in proto.PkgScanReq in.Cmd = proto.CmdScan in.DbId = 2 in.Seq = 20 in.Num = 10 in.TableId = 2 in.RowKey = []byte("row2") in.SetScore(-1) in.ColKey = []byte("") in.PkgFlag |= proto.FlagScanAsc in.SetColSpace(proto.ColSpaceScore1) // order by SCORE out := myScan(in, testAuth, t) if len(out.Kvs) != 5 { t.Fatalf("Invalid KV number: %d", len(out.Kvs)) } if out.PkgFlag&proto.FlagScanEnd == 0 { t.Fatalf("Scan should end") } for i := 0; i < len(out.Kvs); i++ { idx := 4 - i if out.Kvs[i].ErrCode != 0 { t.Fatalf("ErrCode is 0") } if out.Kvs[i].TableId != 2 { t.Fatalf("TableId mismatch") } if bytes.Compare(out.Kvs[i].RowKey, []byte("row2")) != 0 { t.Fatalf("RowKey mismatch") } if bytes.Compare(out.Kvs[i].ColKey, []byte(fmt.Sprintf("col%d", idx))) != 0 { t.Fatalf("ColKey mismatch") } if out.Kvs[i].Score != int64(i*10) { t.Fatalf("Score mismatch") } if bytes.Compare(out.Kvs[i].Value, []byte(fmt.Sprintf("v%d", idx))) != 0 { t.Fatalf("Value mismatch") } } // ZSCAN DESC order by SCORE in.PkgFlag &^= 0xFF in.PkgFlag |= proto.FlagScanKeyStart out = myScan(in, testAuth, t) if len(out.Kvs) != 5 { t.Fatalf("Invalid KV number: %d", len(out.Kvs)) } if out.PkgFlag&proto.FlagScanEnd == 0 { t.Fatalf("Scan should end") } for i := 0; i < len(out.Kvs); i++ { idx := 4 - i if out.Kvs[i].ErrCode != 0 { t.Fatalf("ErrCode is 0") } if out.Kvs[i].TableId != 2 { t.Fatalf("TableId mismatch") } if bytes.Compare(out.Kvs[i].RowKey, []byte("row2")) != 0 { t.Fatalf("RowKey mismatch") } if bytes.Compare(out.Kvs[i].ColKey, []byte(fmt.Sprintf("col%d", i))) != 0 { t.Fatalf("ColKey mismatch") } if out.Kvs[i].Score != int64(idx*10) { t.Fatalf("Score mismatch") } if bytes.Compare(out.Kvs[i].Value, []byte(fmt.Sprintf("v%d", i))) != 0 { t.Fatalf("Value mismatch") } } }
func (c *Context) goScan(zop bool, tableId uint8, rowKey, colKey []byte, score int64, start, asc, orderByScore bool, num int, done chan *Call) (*Call, error) { call := c.cli.newCall(proto.CmdScan, done) if call.err != nil { return call, call.err } if num < 1 { c.cli.errCall(call, ErrInvScanNum) return call, call.err } var p proto.PkgScanReq p.Seq = call.seq p.DbId = c.dbId p.Cmd = call.cmd if asc { p.PkgFlag |= proto.FlagScanAsc } if start { p.PkgFlag |= proto.FlagScanKeyStart } p.Num = uint16(num) p.TableId = tableId p.RowKey = rowKey p.ColKey = colKey // ZScan if zop { p.PkgFlag |= proto.FlagZop p.SetScore(score) if orderByScore { p.SetColSpace(proto.ColSpaceScore1) } else { p.SetColSpace(proto.ColSpaceScore2) } } var pkgLen = p.Length() if pkgLen > proto.MaxPkgLen { c.cli.errCall(call, ErrInvPkgLen) return call, call.err } call.pkg = make([]byte, pkgLen) _, err := p.Encode(call.pkg) if err != nil { c.cli.errCall(call, err) return call, err } call.ctx = scanContext{tableId, rowKey, zop, asc, orderByScore, num} c.cli.sending <- call return call, nil }