// UnmarshalBson bson-decodes into KeyRangeQuery. func (keyRangeQuery *KeyRangeQuery) UnmarshalBson(buf *bytes.Buffer, kind byte) { switch kind { case bson.EOO, bson.Object: // valid case bson.Null: return default: panic(bson.NewBsonError("unexpected kind %v for KeyRangeQuery", kind)) } bson.Next(buf, 4) for kind := bson.NextByte(buf); kind != bson.EOO; kind = bson.NextByte(buf) { switch bson.ReadCString(buf) { case "Sql": keyRangeQuery.Sql = bson.DecodeString(buf, kind) case "BindVariables": // map[string]interface{} if kind != bson.Null { if kind != bson.Object { panic(bson.NewBsonError("unexpected kind %v for keyRangeQuery.BindVariables", kind)) } bson.Next(buf, 4) keyRangeQuery.BindVariables = make(map[string]interface{}) for kind := bson.NextByte(buf); kind != bson.EOO; kind = bson.NextByte(buf) { _k := bson.ReadCString(buf) var _v1 interface{} _v1 = bson.DecodeInterface(buf, kind) keyRangeQuery.BindVariables[_k] = _v1 } } case "Keyspace": keyRangeQuery.Keyspace = bson.DecodeString(buf, kind) case "KeyRanges": // []kproto.KeyRange if kind != bson.Null { if kind != bson.Array { panic(bson.NewBsonError("unexpected kind %v for keyRangeQuery.KeyRanges", kind)) } bson.Next(buf, 4) keyRangeQuery.KeyRanges = make([]kproto.KeyRange, 0, 8) for kind := bson.NextByte(buf); kind != bson.EOO; kind = bson.NextByte(buf) { bson.SkipIndex(buf) var _v2 kproto.KeyRange _v2.UnmarshalBson(buf, kind) keyRangeQuery.KeyRanges = append(keyRangeQuery.KeyRanges, _v2) } } case "TabletType": keyRangeQuery.TabletType.UnmarshalBson(buf, kind) case "Session": // *Session if kind != bson.Null { keyRangeQuery.Session = new(Session) (*keyRangeQuery.Session).UnmarshalBson(buf, kind) } default: bson.Skip(buf, kind) } } }
// This maps a list of keyranges to shard names. func resolveKeyRangeToShards(topoServer SrvTopoServer, cell, keyspace string, tabletType topo.TabletType, kr key.KeyRange) ([]string, error) { srvKeyspace, err := topoServer.GetSrvKeyspace(cell, keyspace) if err != nil { return nil, fmt.Errorf("Error in reading the keyspace %v", err) } tabletTypePartition, ok := srvKeyspace.Partitions[tabletType] if !ok { return nil, fmt.Errorf("No shards available for tablet type '%v' in keyspace '%v'", tabletType, keyspace) } topo.SrvShardArray(tabletTypePartition.Shards).Sort() shards := make([]string, 0, 1) if !kr.IsPartial() { for j := 0; j < len(tabletTypePartition.Shards); j++ { shards = append(shards, tabletTypePartition.Shards[j].ShardName()) } return shards, nil } for j := 0; j < len(tabletTypePartition.Shards); j++ { shard := tabletTypePartition.Shards[j] if key.KeyRangesIntersect(kr, shard.KeyRange) { shards = append(shards, shard.ShardName()) } if kr.End != key.MaxKey && kr.End < shard.KeyRange.Start { break } } return shards, nil }
// This maps a list of keyranges to shard names. func resolveKeyRangeToShards(allShards []topo.ShardReference, kr key.KeyRange) ([]string, error) { shards := make([]string, 0, 1) if !kr.IsPartial() { for j := 0; j < len(allShards); j++ { shards = append(shards, allShards[j].Name) } return shards, nil } for j := 0; j < len(allShards); j++ { shard := allShards[j] if key.KeyRangesIntersect(kr, shard.KeyRange) { shards = append(shards, shard.Name) } } return shards, nil }
// KeyRangeFilterFunc returns a function that calls sendReply only if statements // in the transaction match the specified keyrange. The resulting function can be // passed into the BinlogStreamer: bls.Stream(file, pos, sendTransaction) -> // bls.Stream(file, pos, KeyRangeFilterFunc(sendTransaction)) func KeyRangeFilterFunc(keyrange key.KeyRange, sendReply sendTransactionFunc) sendTransactionFunc { return func(reply *proto.BinlogTransaction) error { matched := false filtered := make([]proto.Statement, 0, len(reply.Statements)) for _, statement := range reply.Statements { switch statement.Category { case proto.BL_SET: filtered = append(filtered, statement) case proto.BL_DDL: filtered = append(filtered, statement) matched = true case proto.BL_DML: keyspaceIndex := bytes.LastIndex(statement.Sql, KEYSPACE_ID_COMMENT) if keyspaceIndex == -1 { updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", string(statement.Sql)) continue } idstart := keyspaceIndex + len(KEYSPACE_ID_COMMENT) idend := bytes.Index(statement.Sql[idstart:], SPACE) if idend == -1 { updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", string(statement.Sql)) continue } id, err := strconv.ParseUint(string(statement.Sql[idstart:idstart+idend]), 10, 64) if err != nil { updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", string(statement.Sql)) continue } if !keyrange.Contains(key.Uint64Key(id).KeyspaceId()) { continue } filtered = append(filtered, statement) matched = true } } if matched { reply.Statements = filtered } else { reply.Statements = nil } return sendReply(reply) } }
// This maps a list of keyranges to shard names. func resolveKeyRangeToShards(allShards []topo.SrvShard, kr key.KeyRange) ([]string, error) { shards := make([]string, 0, 1) topo.SrvShardArray(allShards).Sort() if !kr.IsPartial() { for j := 0; j < len(allShards); j++ { shards = append(shards, allShards[j].ShardName()) } return shards, nil } for j := 0; j < len(allShards); j++ { shard := allShards[j] if key.KeyRangesIntersect(kr, shard.KeyRange) { shards = append(shards, shard.ShardName()) } if kr.End != key.MaxKey && kr.End < shard.KeyRange.Start { break } } return shards, nil }
func buildBindVarCondition(bvc interface{}) (name string, onAbsent, onMismatch bool, op Operator, value interface{}, err error) { bvcinfo, ok := bvc.(map[string]interface{}) if !ok { err = NewTabletError(FAIL, "Expecting json object for bind var conditions") return } var v interface{} v, ok = bvcinfo["Name"] if !ok { err = NewTabletError(FAIL, "Name missing in BindVarConds") return } name, ok = v.(string) if !ok { err = NewTabletError(FAIL, "Expecting string for Name in BindVarConds") return } v, ok = bvcinfo["OnAbsent"] if !ok { err = NewTabletError(FAIL, "OnAbsent missing in BindVarConds") return } onAbsent, ok = v.(bool) if !ok { err = NewTabletError(FAIL, "Expecting bool for OnAbsent") return } v, ok = bvcinfo["Operator"] if !ok { err = NewTabletError(FAIL, "Operator missing in BindVarConds") return } strop, ok := v.(string) if !ok { err = NewTabletError(FAIL, "Expecting string for Operator") return } op, ok = opmap[strop] if !ok { err = NewTabletError(FAIL, "Invalid Operator %s", strop) return } if op == QR_NOOP { return } v, ok = bvcinfo["Value"] if !ok { err = NewTabletError(FAIL, "Value missing in BindVarConds") return } if op >= QR_EQ && op <= QR_LE { strvalue, ok := v.(string) if !ok { err = NewTabletError(FAIL, "Expecting string: %v", v) return } if strop[0] == 'U' { value, err = strconv.ParseUint(strvalue, 0, 64) if err != nil { err = NewTabletError(FAIL, "Expecting uint64: %s", strvalue) return } } else if strop[0] == 'I' { value, err = strconv.ParseInt(strvalue, 0, 64) if err != nil { err = NewTabletError(FAIL, "Expecting int64: %s", strvalue) return } } else if strop[0] == 'S' { value = strvalue } else { panic("unexpected") } } else if op == QR_MATCH || op == QR_NOMATCH { strvalue, ok := v.(string) if !ok { err = NewTabletError(FAIL, "Expecting string: %v", v) return } value = strvalue } else if op == QR_IN || op == QR_NOTIN { kr, ok := v.(map[string]interface{}) if !ok { err = NewTabletError(FAIL, "Expecting keyrange for Value") return } var keyrange key.KeyRange strstart, ok := kr["Start"] if !ok { err = NewTabletError(FAIL, "Start missing in KeyRange") return } start, ok := strstart.(string) if !ok { err = NewTabletError(FAIL, "Expecting string for Start") return } keyrange.Start = key.KeyspaceId(start) strend, ok := kr["End"] if !ok { err = NewTabletError(FAIL, "End missing in KeyRange") return } end, ok := strend.(string) if !ok { err = NewTabletError(FAIL, "Expecting string for End") return } keyrange.End = key.KeyspaceId(end) value = keyrange } v, ok = bvcinfo["OnMismatch"] if !ok { err = NewTabletError(FAIL, "OnMismatch missing in BindVarConds") return } onMismatch, ok = v.(bool) if !ok { err = NewTabletError(FAIL, "Expecting bool for OnAbsent") return } return }
// KeyRangeFilterFunc returns a function that calls sendReply only if statements // in the transaction match the specified keyrange. The resulting function can be // passed into the BinlogStreamer: bls.Stream(file, pos, sendTransaction) -> // bls.Stream(file, pos, KeyRangeFilterFunc(sendTransaction)) func KeyRangeFilterFunc(kit key.KeyspaceIdType, keyrange key.KeyRange, sendReply sendTransactionFunc) sendTransactionFunc { isInteger := true if kit == key.KIT_BYTES { isInteger = false } return func(reply *proto.BinlogTransaction) error { matched := false filtered := make([]proto.Statement, 0, len(reply.Statements)) for _, statement := range reply.Statements { switch statement.Category { case proto.BL_SET: filtered = append(filtered, statement) case proto.BL_DDL: log.Warningf("Not forwarding DDL: %s", string(statement.Sql)) continue case proto.BL_DML: keyspaceIndex := bytes.LastIndex(statement.Sql, KEYSPACE_ID_COMMENT) if keyspaceIndex == -1 { updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", string(statement.Sql)) continue } idstart := keyspaceIndex + len(KEYSPACE_ID_COMMENT) idend := bytes.Index(statement.Sql[idstart:], SPACE) if idend == -1 { updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", string(statement.Sql)) continue } textId := string(statement.Sql[idstart : idstart+idend]) if isInteger { id, err := strconv.ParseUint(textId, 10, 64) if err != nil { updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", string(statement.Sql)) continue } if !keyrange.Contains(key.Uint64Key(id).KeyspaceId()) { continue } } else { data, err := base64.StdEncoding.DecodeString(textId) if err != nil { updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", string(statement.Sql)) continue } if !keyrange.Contains(key.KeyspaceId(data)) { continue } } filtered = append(filtered, statement) matched = true case proto.BL_UNRECOGNIZED: updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", string(statement.Sql)) continue } } if matched { reply.Statements = filtered } else { reply.Statements = nil } return sendReply(reply) } }