// Split will split the rows into subset for each distribution func (rs *RowSplitter) Split(result [][][]sqltypes.Value, rows [][]sqltypes.Value) error { if rs.KeyspaceIdType == pb.KeyspaceIdType_UINT64 { for _, row := range rows { v := sqltypes.MakeNumeric(row[rs.ValueIndex].Raw()) i, err := v.ParseUint64() if err != nil { return fmt.Errorf("Non numerical value: %v", err) } k := key.Uint64Key(i).Bytes() for i, kr := range rs.KeyRanges { if key.KeyRangeContains(kr, k) { result[i] = append(result[i], row) break } } } } else { for _, row := range rows { k := row[rs.ValueIndex].Raw() for i, kr := range rs.KeyRanges { if key.KeyRangeContains(kr, k) { result[i] = append(result[i], row) break } } } } return nil }
func (krval *bvcKeyRange) eval(bv interface{}, op Operator, onMismatch bool) bool { switch op { case QRIn: switch num, status := getuint64(bv); status { case QROK: k := key.Uint64Key(num).Bytes() return key.KeyRangeContains((*topodatapb.KeyRange)(krval), k) case QROutOfRange: return false } // Not a number. Check string. switch str, status := getstring(bv); status { case QROK: return key.KeyRangeContains((*topodatapb.KeyRange)(krval), []byte(str)) } case QRNotIn: switch num, status := getuint64(bv); status { case QROK: k := key.Uint64Key(num).Bytes() return !key.KeyRangeContains((*topodatapb.KeyRange)(krval), k) case QROutOfRange: return true } // Not a number. Check string. switch str, status := getstring(bv); status { case QROK: return !key.KeyRangeContains((*topodatapb.KeyRange)(krval), []byte(str)) } default: panic("unexpected:") } return onMismatch }
// Split will split the rows into subset for each distribution func (rs *RowSplitter) Split(result [][][]sqltypes.Value, rows [][]sqltypes.Value) error { for _, row := range rows { k, err := rs.KeyResolver.keyspaceID(row) if err != nil { return err } for i, kr := range rs.KeyRanges { if key.KeyRangeContains(kr, k) { result[i] = append(result[i], row) break } } } return nil }
func getShardForKeyspaceID(allShards []*pb.ShardReference, keyspaceID []byte) (string, error) { if len(allShards) == 0 { return "", vterrors.FromError(vtrpc.ErrorCode_BAD_INPUT, fmt.Errorf("No shards found for this tabletType"), ) } for _, shardReference := range allShards { if key.KeyRangeContains(shardReference.KeyRange, keyspaceID) { return shardReference.Name, nil } } return "", vterrors.FromError(vtrpc.ErrorCode_BAD_INPUT, fmt.Errorf("KeyspaceId %v didn't match any shards %+v", hex.EncodeToString(keyspaceID), allShards), ) }
// Route returns which shard (specified by the index of the list of shards // passed in NewRowRouter) contains the given row. func (rr *RowRouter) Route(row []sqltypes.Value) (int, error) { if len(rr.keyRanges) == 1 { // Fast path when there is only one destination shard. return 0, nil } k, err := rr.keyResolver.keyspaceID(row) if err != nil { return -1, err } for i, kr := range rr.keyRanges { if key.KeyRangeContains(kr, k) { return i, nil } } return -1, fmt.Errorf("no shard's key range includes the keyspace id: %v for the row: %#v", k, row) }
// 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(keyrange, sendTransaction)) // TODO(erez): Remove 'KeyspaceIdType' from here: it's no longer used. func KeyRangeFilterFunc(unused key.KeyspaceIdType, keyrange *pb.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: log.Warningf("Not forwarding DDL: %s", statement.Sql) continue case proto.BL_DML: keyspaceID, err := sqlannotation.ExtractKeySpaceID(string(statement.Sql)) if err != nil { if handleExtractKeySpaceIDError(err) { continue } else { // TODO(erez): Stop filtered-replication here, and alert. // Currently we skip. continue } } if !key.KeyRangeContains(keyrange, keyspaceID) { // Skip keyspace ids that don't belong to the destination shard. continue } filtered = append(filtered, statement) matched = true case proto.BL_UNRECOGNIZED: updateStreamErrors.Add("KeyRangeStream", 1) log.Errorf("Error parsing keyspace id: %s", statement.Sql) continue } } if matched { reply.Statements = filtered } else { reply.Statements = nil } return sendReply(reply) } }
// Recv is part of sqltypes.ResultStream interface. func (f *v3KeyRangeFilter) Recv() (*sqltypes.Result, error) { r, err := f.input.Recv() if err != nil { return nil, err } rows := make([][]sqltypes.Value, 0, len(r.Rows)) for _, row := range r.Rows { ksid, err := f.resolver.keyspaceID(row) if err != nil { return nil, err } if key.KeyRangeContains(f.keyRange, ksid) { rows = append(rows, row) } } r.Rows = rows return r, 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(kit key.KeyspaceIdType, keyrange *pb.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 !key.KeyRangeContains(keyrange, key.Uint64Key(id).Bytes()) { 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 !key.KeyRangeContains(keyrange, 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) } }