// on the source rdonly guy, should only have one query to find min & max func SourceRdonlyFactory(t *testing.T) func() (dbconnpool.PoolConnection, error) { return func() (dbconnpool.PoolConnection, error) { return &FakePoolConnection{ t: t, ExpectedExecuteFetch: []ExpectedExecuteFetch{ ExpectedExecuteFetch{ Query: "SELECT MIN(id), MAX(id) FROM vt_ks.table1", QueryResult: &mproto.QueryResult{ Fields: []mproto.Field{ mproto.Field{ Name: "min", Type: mproto.VT_LONGLONG, }, mproto.Field{ Name: "max", Type: mproto.VT_LONGLONG, }, }, Rows: [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeString([]byte("100")), sqltypes.MakeString([]byte("200")), }, }, }, }, }, }, nil } }
func (sq *SqlQuery) StreamExecute(ctx context.Context, query *proto.Query, sendReply func(reply interface{}) error) error { // Custom parsing of the query we expect min := 100 max := 200 var err error parts := strings.Split(query.Sql, " ") for _, part := range parts { if strings.HasPrefix(part, "id>=") { min, err = strconv.Atoi(part[4:]) if err != nil { return err } } else if strings.HasPrefix(part, "id<") { max, err = strconv.Atoi(part[3:]) } } sq.t.Logf("SqlQuery: got query: %v with min %v max %v", *query, min, max) // Send the headers if err := sendReply(&mproto.QueryResult{ Fields: []mproto.Field{ mproto.Field{ Name: "id", Type: mproto.VT_LONGLONG, }, mproto.Field{ Name: "msg", Type: mproto.VT_VARCHAR, }, mproto.Field{ Name: "keyspace_id", Type: mproto.VT_LONGLONG, }, }, }); err != nil { return err } // Send the values ksids := []uint64{0x2000000000000000, 0x6000000000000000} for i := min; i < max; i++ { if err := sendReply(&mproto.QueryResult{ Rows: [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeString([]byte(fmt.Sprintf("%v", i))), sqltypes.MakeString([]byte(fmt.Sprintf("Text for %v", i))), sqltypes.MakeString([]byte(fmt.Sprintf("%v", ksids[i%2]))), }, }, }); err != nil { return err } } // SELECT id, msg, keyspace_id FROM table1 WHERE id>=180 AND id<190 ORDER BY id return nil }
// AsInterface converts the ValExpr to an interface. It converts // ValTuple to []interface{}, ValArg to string, StrVal to sqltypes.String, // NumVal to sqltypes.Numeric, NullVal to nil. // Otherwise, it returns an error. func AsInterface(node ValExpr) (interface{}, error) { switch node := node.(type) { case ValTuple: vals := make([]interface{}, 0, len(node)) for _, val := range node { v, err := AsInterface(val) if err != nil { return nil, err } vals = append(vals, v) } return vals, nil case ValArg: return string(node), nil case ListArg: return string(node), nil case StrVal: return sqltypes.MakeString(node), nil case NumVal: n, err := sqltypes.BuildNumeric(string(node)) if err != nil { return nil, fmt.Errorf("type mismatch: %s", err) } return n, nil case *NullVal: return nil, nil } return nil, fmt.Errorf("unexpected node %v", node) }
func BuildValue(bytes []byte, fieldType uint32) sqltypes.Value { if bytes == nil { return sqltypes.NULL } switch fieldType { case C.MYSQL_TYPE_DECIMAL, C.MYSQL_TYPE_FLOAT, C.MYSQL_TYPE_DOUBLE, C.MYSQL_TYPE_NEWDECIMAL: return sqltypes.MakeFractional(bytes) case C.MYSQL_TYPE_TIMESTAMP: return sqltypes.MakeString(bytes) } // The below condition represents the following list of values: // C.MYSQL_TYPE_TINY, C.MYSQL_TYPE_SHORT, C.MYSQL_TYPE_LONG, C.MYSQL_TYPE_LONGLONG, C.MYSQL_TYPE_INT24, C.MYSQL_TYPE_YEAR: if fieldType <= C.MYSQL_TYPE_INT24 || fieldType == C.MYSQL_TYPE_YEAR { return sqltypes.MakeNumeric(bytes) } return sqltypes.MakeString(bytes) }
func mapToSqlResults(row map[string]string) ([]proto.Field, []sqltypes.Value) { fields := make([]proto.Field, len(row)) values := make([]sqltypes.Value, len(row)) index := 0 for key, value := range row { fields[index] = proto.Field{Name: key} values[index] = sqltypes.MakeString(([]byte)(value)) index++ } return fields, values }
func TestAppendResult(t *testing.T) { qr := new(mproto.QueryResult) innerqr1 := &mproto.QueryResult{ Fields: []mproto.Field{}, Rows: [][]sqltypes.Value{}, } innerqr2 := &mproto.QueryResult{ Fields: []mproto.Field{ {Name: "foo", Type: 1}, }, RowsAffected: 1, InsertId: 1, Rows: [][]sqltypes.Value{ {sqltypes.MakeString([]byte("abcd"))}, }, } // test one empty result appendResult(qr, innerqr1) appendResult(qr, innerqr2) if len(qr.Fields) != 1 { t.Errorf("want 1, got %v", len(qr.Fields)) } if qr.RowsAffected != 1 { t.Errorf("want 1, got %v", qr.RowsAffected) } if qr.InsertId != 1 { t.Errorf("want 1, got %v", qr.InsertId) } if len(qr.Rows) != 1 { t.Errorf("want 1, got %v", len(qr.Rows)) } // test two valid results qr = new(mproto.QueryResult) appendResult(qr, innerqr2) appendResult(qr, innerqr2) if len(qr.Fields) != 1 { t.Errorf("want 1, got %v", len(qr.Fields)) } if qr.RowsAffected != 2 { t.Errorf("want 2, got %v", qr.RowsAffected) } if qr.InsertId != 1 { t.Errorf("want 1, got %v", qr.InsertId) } if len(qr.Rows) != 2 { t.Errorf("want 2, got %v", len(qr.Rows)) } }
func (rc *RowCache) decodeRow(b []byte) (row []sqltypes.Value) { rowlen := pack.Uint32(b) data := b[4+rowlen*4:] row = make([]sqltypes.Value, rowlen) for i := range row { length := pack.Uint32(b[4+i*4:]) if length == 0xFFFFFFFF { continue } if length > uint32(len(data)) { // Corrupt data return nil } if rc.tableInfo.Columns[i].Category == schema.CAT_NUMBER { row[i] = sqltypes.MakeNumeric(data[:length]) } else { row[i] = sqltypes.MakeString(data[:length]) } data = data[length:] } return row }
func validateKey(tableInfo *TableInfo, key string) (newKey string) { if key == "" { // TODO: Verify auto-increment table return } pieces := strings.Split(key, ".") if len(pieces) != len(tableInfo.PKColumns) { // TODO: Verify auto-increment table return "" } pkValues := make([]sqltypes.Value, len(tableInfo.PKColumns)) for i, piece := range pieces { if piece[0] == '\'' { s, err := base64.StdEncoding.DecodeString(piece[1 : len(piece)-1]) if err != nil { log.Warningf("Error decoding key %s for table %s: %v", key, tableInfo.Name, err) internalErrors.Add("Mismatch", 1) return } pkValues[i] = sqltypes.MakeString(s) } else if piece == "null" { // TODO: Verify auto-increment table return "" } else { n, err := sqltypes.BuildNumeric(piece) if err != nil { log.Warningf("Error decoding key %s for table %s: %v", key, tableInfo.Name, err) internalErrors.Add("Mismatch", 1) return } pkValues[i] = n } } if newKey = buildKey(pkValues); newKey != key { log.Warningf("Error: Key mismatch, received: %s, computed: %s", key, newKey) internalErrors.Add("Mismatch", 1) } return newKey }
func (ta *Table) AddColumn(name string, columnType string, defval sqltypes.Value, extra string) { index := len(ta.Columns) ta.Columns = append(ta.Columns, TableColumn{Name: name}) if strings.Contains(columnType, "int") { ta.Columns[index].Category = CAT_NUMBER } else if strings.HasPrefix(columnType, "varbinary") { ta.Columns[index].Category = CAT_VARBINARY } else { ta.Columns[index].Category = CAT_OTHER } if extra == "auto_increment" { ta.Columns[index].IsAuto = true // Ignore default value, if any return } if defval.IsNull() { return } if ta.Columns[index].Category == CAT_NUMBER { ta.Columns[index].Default = sqltypes.MakeNumeric(defval.Raw()) } else { ta.Columns[index].Default = sqltypes.MakeString(defval.Raw()) } }
func TestRowSplitterString(t *testing.T) { shards := []*topo.ShardInfo{ siBytes("", "E"), siBytes("E", "L"), siBytes("L", ""), } rs := NewRowSplitter(shards, key.KIT_BYTES, 1) // rows in different shards row0 := []sqltypes.Value{ sqltypes.MakeString([]byte("Ignored Value")), sqltypes.MakeString([]byte("A")), } row1 := []sqltypes.Value{ sqltypes.MakeString([]byte("Ignored Value")), sqltypes.MakeString([]byte("G")), } row2 := []sqltypes.Value{ sqltypes.MakeString([]byte("Ignored Value")), sqltypes.MakeString([]byte("Q")), } // basic split rows := [][]sqltypes.Value{row0, row1, row2, row2, row1, row2, row0} result := rs.StartSplit() if err := rs.Split(result, rows); err != nil { t.Fatalf("Split failed: %v", err) } if len(result) != 3 { t.Fatalf("Bad column count: %v", rows) } if !reflect.DeepEqual(result[0], [][]sqltypes.Value{row0, row0}) { t.Fatalf("Bad result[0]: %v", result[0]) } if !reflect.DeepEqual(result[1], [][]sqltypes.Value{row1, row1}) { t.Fatalf("Bad result[1]: %v", result[1]) } if !reflect.DeepEqual(result[2], [][]sqltypes.Value{row2, row2, row2}) { t.Fatalf("Bad result[2]: %v", result[2]) } }
func TestRowSplitterUint64(t *testing.T) { shards := []*topo.ShardInfo{ si("", "40"), si("40", "c0"), si("c0", ""), } rs := NewRowSplitter(shards, key.KIT_UINT64, 1) // rows in different shards row0 := []sqltypes.Value{ sqltypes.MakeString([]byte("Ignored Value")), sqltypes.MakeString([]byte(fmt.Sprintf("%v", 0x1000000000000000))), } row1 := []sqltypes.Value{ sqltypes.MakeString([]byte("Ignored Value")), sqltypes.MakeString([]byte(fmt.Sprintf("%v", 0x6000000000000000))), } row2 := []sqltypes.Value{ sqltypes.MakeString([]byte("Ignored Value")), sqltypes.MakeString([]byte(fmt.Sprintf("%v", uint64(0xe000000000000000)))), } // basic split rows := [][]sqltypes.Value{row0, row1, row2, row2, row1, row2, row0} result := rs.StartSplit() if err := rs.Split(result, rows); err != nil { t.Fatalf("Split failed: %v", err) } if len(result) != 3 { t.Fatalf("Bad column count: %v", rows) } if !reflect.DeepEqual(result[0], [][]sqltypes.Value{row0, row0}) { t.Fatalf("Bad result[0]: %v", result[0]) } if !reflect.DeepEqual(result[1], [][]sqltypes.Value{row1, row1}) { t.Fatalf("Bad result[1]: %v", result[1]) } if !reflect.DeepEqual(result[2], [][]sqltypes.Value{row2, row2, row2}) { t.Fatalf("Bad result[2]: %v", result[2]) } }
var testExecuteFetchResult = &mproto.QueryResult{ Fields: []mproto.Field{ mproto.Field{ Name: "column1", Type: mproto.VT_TINY_BLOB, }, mproto.Field{ Name: "column2", Type: mproto.VT_TIMESTAMP, }, }, RowsAffected: 10, InsertId: 32, Rows: [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeString([]byte("ABC")), }, }, } func (fra *fakeRpcAgent) ExecuteFetch(ctx context.Context, query string, maxrows int, wantFields, disableBinlogs bool) (*mproto.QueryResult, error) { compare(fra.t, "ExecuteFetch query", query, testExecuteFetchQuery) compare(fra.t, "ExecuteFetch maxrows", maxrows, testExecuteFetchMaxRows) compareBool(fra.t, "ExecuteFetch wantFields", wantFields) compareBool(fra.t, "ExecuteFetch disableBinlogs", disableBinlogs) return testExecuteFetchResult, nil } func agentRpcTestExecuteFetch(ctx context.Context, t *testing.T, client tmclient.TabletManagerClient, ti *topo.TabletInfo) { qr, err := client.ExecuteFetch(ctx, ti, testExecuteFetchQuery, testExecuteFetchMaxRows, true, true) compareError(t, "ExecuteFetch", err, qr, testExecuteFetchResult)
func (node StrVal) Format(buf *TrackedBuffer) { s := sqltypes.MakeString([]byte(node)) s.EncodeSql(buf) }
func TestParsedQuery(t *testing.T) { tcases := []struct { desc string query string bindVars map[string]interface{} output string }{ { "no subs", "select * from a where id = 2", map[string]interface{}{ "id": 1, }, "select * from a where id = 2", }, { "simple bindvar sub", "select * from a where id1 = :id1 and id2 = :id2", map[string]interface{}{ "id1": 1, "id2": nil, }, "select * from a where id1 = 1 and id2 = null", }, { "missing bind var", "select * from a where id1 = :id1 and id2 = :id2", map[string]interface{}{ "id1": 1, }, "missing bind var id2", }, { "unencodable bind var", "select * from a where id1 = :id", map[string]interface{}{ "id": make([]int, 1), }, "unsupported bind variable type []int: [0]", }, { "list inside bind vars", "select * from a where id in (:vals)", map[string]interface{}{ "vals": []sqltypes.Value{ sqltypes.MakeNumeric([]byte("1")), sqltypes.MakeString([]byte("aa")), }, }, "select * from a where id in (1, 'aa')", }, { "two lists inside bind vars", "select * from a where id in (:vals)", map[string]interface{}{ "vals": [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeNumeric([]byte("1")), sqltypes.MakeString([]byte("aa")), }, []sqltypes.Value{ sqltypes.Value{}, sqltypes.MakeString([]byte("bb")), }, }, }, "select * from a where id in ((1, 'aa'), (null, 'bb'))", }, { "list bind vars", "select * from a where id in ::vals", map[string]interface{}{ "vals": []interface{}{ 1, "aa", }, }, "select * from a where id in (1, 'aa')", }, { "list bind vars single argument", "select * from a where id in ::vals", map[string]interface{}{ "vals": []interface{}{ 1, }, }, "select * from a where id in (1)", }, { "list bind vars 0 arguments", "select * from a where id in ::vals", map[string]interface{}{ "vals": []interface{}{}, }, "empty list supplied for vals", }, { "non-list bind var supplied", "select * from a where id in ::vals", map[string]interface{}{ "vals": 1, }, "unexpected list arg type int for key vals", }, { "list bind var for non-list", "select * from a where id = :vals", map[string]interface{}{ "vals": []interface{}{1}, }, "unexpected arg type []interface {} for key vals", }, { "single column tuple equality", // We have to use an incorrect construct to get around the parser. "select * from a where b = :equality", map[string]interface{}{ "equality": TupleEqualityList{ Columns: []string{"pk"}, Rows: [][]sqltypes.Value{ []sqltypes.Value{sqltypes.MakeNumeric([]byte("1"))}, []sqltypes.Value{sqltypes.MakeString([]byte("aa"))}, }, }, }, "select * from a where b = pk in (1, 'aa')", }, { "multi column tuple equality", "select * from a where b = :equality", map[string]interface{}{ "equality": TupleEqualityList{ Columns: []string{"pk1", "pk2"}, Rows: [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeNumeric([]byte("1")), sqltypes.MakeString([]byte("aa")), }, []sqltypes.Value{ sqltypes.MakeNumeric([]byte("2")), sqltypes.MakeString([]byte("bb")), }, }, }, }, "select * from a where b = (pk1 = 1 and pk2 = 'aa') or (pk1 = 2 and pk2 = 'bb')", }, { "0 rows", "select * from a where b = :equality", map[string]interface{}{ "equality": TupleEqualityList{ Columns: []string{"pk"}, Rows: [][]sqltypes.Value{}, }, }, "cannot encode with 0 rows", }, { "values don't match column count", "select * from a where b = :equality", map[string]interface{}{ "equality": TupleEqualityList{ Columns: []string{"pk"}, Rows: [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeNumeric([]byte("1")), sqltypes.MakeString([]byte("aa")), }, }, }, }, "values don't match column count", }, } for _, tcase := range tcases { tree, err := Parse(tcase.query) if err != nil { t.Errorf("parse failed for %s: %v", tcase.desc, err) continue } buf := NewTrackedBuffer(nil) buf.Myprintf("%v", tree) pq := buf.ParsedQuery() bytes, err := pq.GenerateQuery(tcase.bindVars) var got string if err != nil { got = err.Error() } else { got = string(bytes) } if got != tcase.output { t.Errorf("for test case: %s, got: '%s', want '%s'", tcase.desc, got, tcase.output) } } }
encoded: "E\x00\x00\x00\x04Fields\x00\x05\x00\x00\x00\x00?RowsAffected\x00\x00\x00\x00\x00\x00\x00\x00\x00?InsertId\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04Rows\x00\x05\x00\x00\x00\x00\x00", }, // Only fields set { qr: QueryResult{ Fields: []Field{ {Name: "foo", Type: 1}, }, }, encoded: "i\x00\x00\x00\x04Fields\x00)\x00\x00\x00\x030\x00!\x00\x00\x00\x05Name\x00\x03\x00\x00\x00\x00foo\x12Type\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00\x00?RowsAffected\x00\x00\x00\x00\x00\x00\x00\x00\x00?InsertId\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04Rows\x00\x05\x00\x00\x00\x00\x00", }, // Only rows, no fields { qr: QueryResult{ Rows: [][]sqltypes.Value{ {sqltypes.MakeString([]byte("abcd")), sqltypes.MakeNumeric([]byte("1234")), sqltypes.MakeFractional([]byte("1.234"))}, }, }, encoded: "r\x00\x00\x00\x04Fields\x00\x05\x00\x00\x00\x00?RowsAffected\x00\x00\x00\x00\x00\x00\x00\x00\x00?InsertId\x00\x00\x00\x00\x00\x00\x00\x00\x00\x04Rows\x002\x00\x00\x00\x040\x00*\x00\x00\x00\x050\x00\x04\x00\x00\x00\x00abcd\x051\x00\x04\x00\x00\x00\x001234\x052\x00\x05\x00\x00\x00\x001.234\x00\x00\x00", }, // one row and one field { qr: QueryResult{ Fields: []Field{ {Name: "foo", Type: 1}, }, Rows: [][]sqltypes.Value{ {sqltypes.MakeString([]byte("abcd")), sqltypes.MakeNumeric([]byte("1234")), sqltypes.MakeFractional([]byte("1.234")), sqltypes.Value{}}, }, }, encoded: "",
func TestConvert(t *testing.T) { cases := []struct { Desc string Typ int64 Val sqltypes.Value Want interface{} }{{ Desc: "null", Typ: VT_LONG, Val: sqltypes.Value{}, Want: nil, }, { Desc: "decimal", Typ: VT_DECIMAL, Val: sqltypes.MakeString([]byte("aa")), Want: "aa", }, { Desc: "tiny", Typ: VT_TINY, Val: sqltypes.MakeString([]byte("1")), Want: int64(1), }, { Desc: "short", Typ: VT_SHORT, Val: sqltypes.MakeString([]byte("1")), Want: int64(1), }, { Desc: "long", Typ: VT_LONG, Val: sqltypes.MakeString([]byte("1")), Want: int64(1), }, { Desc: "longlong", Typ: VT_LONGLONG, Val: sqltypes.MakeString([]byte("1")), Want: int64(1), }, { Desc: "int24", Typ: VT_INT24, Val: sqltypes.MakeString([]byte("1")), Want: int64(1), }, { Desc: "float", Typ: VT_FLOAT, Val: sqltypes.MakeString([]byte("1")), Want: float64(1), }, { Desc: "double", Typ: VT_DOUBLE, Val: sqltypes.MakeString([]byte("1")), Want: float64(1), }, { Desc: "large int", Typ: VT_LONGLONG, Val: sqltypes.MakeString([]byte("9223372036854775808")), Want: uint64(9223372036854775808), }, { Desc: "float for int", Typ: VT_LONGLONG, Val: sqltypes.MakeString([]byte("1.1")), Want: `strconv.ParseUint: parsing "1.1": invalid syntax`, }, { Desc: "string for float", Typ: VT_FLOAT, Val: sqltypes.MakeString([]byte("aa")), Want: `strconv.ParseFloat: parsing "aa": invalid syntax`, }} for _, c := range cases { r, err := Convert(c.Typ, c.Val) if err != nil { r = err.Error() } else if _, ok := r.([]byte); ok { r = string(r.([]byte)) } if r != c.Want { t.Errorf("%s: %+v, want %+v", c.Desc, r, c.Want) } } }