// checkBlpPositionList will ask the BinlogPlayerMap for its BlpPositionList, // and check it contains one entry with the right data. func checkBlpPositionList(t *testing.T, bpm *BinlogPlayerMap, vtClientSyncChannel chan *binlogplayer.VtClientMock) { // ask for BlpPositionList, make sure we got what we expect go func() { vtcm := binlogplayer.NewVtClientMock() vtcm.Result = &sqltypes.Result{ Fields: nil, RowsAffected: 1, InsertID: 0, Rows: [][]sqltypes.Value{ { sqltypes.MakeString([]byte("MariaDB/0-1-1235")), sqltypes.MakeString([]byte("")), }, }, } vtClientSyncChannel <- vtcm }() bpl, err := bpm.BlpPositionList() if err != nil { t.Errorf("BlpPositionList failed: %v", err) return } if len(bpl) != 1 || bpl[0].Uid != 1 || bpl[0].Position != "MariaDB/0-1-1235" { t.Errorf("unexpected BlpPositionList: %v", bpl) } }
// addGeneratedRows will add from-to generated rows. The rows (their primary // key) will be in the range [from, to). func (sq *testQueryService) addGeneratedRows(from, to int) { var rows [][]sqltypes.Value // ksids has keyspace ids which are covered by the shard key ranges -40 and 40-80. ksids := []uint64{0x2000000000000000, 0x6000000000000000} for id := from; id < to; id++ { // Only return the rows which are covered by this shard. shardIndex := id % 2 if sq.shardCount == 1 || shardIndex == sq.shardIndex { idValue, _ := sqltypes.BuildValue(int64(id)) row := []sqltypes.Value{ idValue, sqltypes.MakeString([]byte(fmt.Sprintf("Text for %v", id))), } if !sq.omitKeyspaceID { row = append(row, sqltypes.MakeString([]byte(fmt.Sprintf("%v", ksids[shardIndex])))) } rows = append(rows, row) } } if sq.rows == nil { sq.rows = rows } else { sq.rows = append(sq.rows, rows...) } }
func echoQueryResult(vals map[string]interface{}) *sqltypes.Result { qr := &sqltypes.Result{} var row []sqltypes.Value // The first two returned fields are always a field with a MySQL NULL value, // and another field with a zero-length string. // Client tests can use this to check that they correctly distinguish the two. qr.Fields = append(qr.Fields, &querypb.Field{Name: "null", Type: sqltypes.VarBinary}) row = append(row, sqltypes.NULL) qr.Fields = append(qr.Fields, &querypb.Field{Name: "emptyString", Type: sqltypes.VarBinary}) row = append(row, sqltypes.MakeString([]byte(""))) for k, v := range vals { qr.Fields = append(qr.Fields, &querypb.Field{Name: k, Type: sqltypes.VarBinary}) val := reflect.ValueOf(v) if val.Kind() == reflect.Map { row = append(row, sqltypes.MakeString(printSortedMap(val))) continue } row = append(row, sqltypes.MakeString([]byte(fmt.Sprintf("%v", v)))) } qr.Rows = [][]sqltypes.Value{row} return qr }
func (sq *sqlDifferTabletServer) StreamExecute(ctx context.Context, target *pb.Target, query *proto.Query, sendReply func(reply *mproto.QueryResult) error) error { sq.t.Logf("SqlDifferTabletServer: got query: %v", *query) // 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, }, }, }); err != nil { return err } // Send the values for i := 0; i < 1000; 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))), }, }, }); err != nil { return err } } return nil }
func TestRowsToProto3(t *testing.T) { rows := [][]sqltypes.Value{{ sqltypes.MakeString([]byte("aa")), sqltypes.NULL, sqltypes.MakeString([]byte("12")), }, { sqltypes.MakeString([]byte("bb")), sqltypes.NULL, sqltypes.NULL, }} p3 := RowsToProto3(rows) want := []*query.Row{ &query.Row{ Lengths: []int64{2, -1, 2}, Values: []byte("aa12"), }, &query.Row{ Lengths: []int64{2, -1, -1}, Values: []byte("bb"), }, } if !reflect.DeepEqual(p3, want) { t.Errorf("P3: %v, want %v", p3, want) } reverse := Proto3ToRows(p3) if !reflect.DeepEqual(reverse, rows) { t.Errorf("reverse: \n%#v, want \n%#v", reverse, rows) } }
// sourceRdonlyFactory fakes out the MIN, MAX query on the primary key. // (This query is used to calculate the split points for reading a table // using multiple threads.) func sourceRdonlyFactory(t *testing.T, dbAndTableName string, min, max int) func() (dbconnpool.PoolConnection, error) { f := NewFakePoolConnectionQuery(t, "sourceRdonly") f.addExpectedExecuteFetch(ExpectedExecuteFetch{ Query: fmt.Sprintf("SELECT MIN(id), MAX(id) FROM %s", dbAndTableName), QueryResult: &sqltypes.Result{ Fields: []*querypb.Field{ { Name: "min", Type: sqltypes.Int64, }, { Name: "max", Type: sqltypes.Int64, }, }, Rows: [][]sqltypes.Value{ { sqltypes.MakeString([]byte(strconv.Itoa(min))), sqltypes.MakeString([]byte(strconv.Itoa(max))), }, }, }, }) f.enableInfinite() return f.getFactory() }
// checkBlpPositionList will ask the BinlogPlayerMap for its BlpPositionList, // and check it contains one entry with the right data. func checkBlpPositionList(t *testing.T, bpm *BinlogPlayerMap, vtClientSyncChannel chan *binlogplayer.VtClientMock) { // ask for BlpPositionList, make sure we got what we expect go func() { vtcm := binlogplayer.NewVtClientMock() vtcm.Result = &mproto.QueryResult{ Fields: nil, RowsAffected: 1, InsertId: 0, Rows: [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeString([]byte("MariaDB/0-1-1235")), sqltypes.MakeString([]byte("")), }, }, } vtClientSyncChannel <- vtcm }() bpl, err := bpm.BlpPositionList() if err != nil { t.Errorf("BlpPositionList failed: %v", err) return } if len(bpl.Entries) != 1 || bpl.Entries[0].Uid != 1 || bpl.Entries[0].Position.GTIDSet.(myproto.MariadbGTID).Domain != 0 || bpl.Entries[0].Position.GTIDSet.(myproto.MariadbGTID).Server != 1 || bpl.Entries[0].Position.GTIDSet.(myproto.MariadbGTID).Sequence != 1235 { t.Errorf("unexpected BlpPositionList: %v", bpl) } }
// 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 } }
// on the source rdonly guy, should only have one query to find min & max func VerticalSourceRdonlyFactory(t *testing.T) func() (dbconnpool.PoolConnection, error) { return func() (dbconnpool.PoolConnection, error) { return &VerticalFakePoolConnection{ t: t, ExpectedExecuteFetch: []ExpectedExecuteFetch{ ExpectedExecuteFetch{ Query: "SELECT MIN(id), MAX(id) FROM vt_source_ks.moving1", QueryResult: &sqltypes.Result{ Fields: []*querypb.Field{ &querypb.Field{ Name: "min", Type: sqltypes.Int64, }, &querypb.Field{ Name: "max", Type: sqltypes.Int64, }, }, Rows: [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeString([]byte("100")), sqltypes.MakeString([]byte("200")), }, }, }, }, }, }, nil } }
func TestInvalidRowsProto(t *testing.T) { p3 := []*query.Row{ &query.Row{ Lengths: []int64{3, 5, -1, 6}, Values: []byte("aa12"), }, } rows := Proto3ToRows(p3) want := [][]sqltypes.Value{{ sqltypes.MakeString([]byte("aa1")), sqltypes.NULL, sqltypes.NULL, sqltypes.NULL, }} if !reflect.DeepEqual(rows, want) { t.Errorf("reverse: \n%#v, want \n%#v", rows, want) } p3 = []*query.Row{ &query.Row{ Lengths: []int64{2, -2, 2}, Values: []byte("aa12"), }, } rows = Proto3ToRows(p3) want = [][]sqltypes.Value{{ sqltypes.MakeString([]byte("aa")), sqltypes.NULL, sqltypes.MakeString([]byte("12")), }} if !reflect.DeepEqual(rows, want) { t.Errorf("reverse: \n%#v, want \n%#v", rows, want) } }
func createTestTableBaseShowTable(tableName string) []sqltypes.Value { return []sqltypes.Value{ sqltypes.MakeString([]byte(tableName)), sqltypes.MakeString([]byte("USER TABLE")), sqltypes.MakeString([]byte("1427325875")), sqltypes.MakeString([]byte("")), } }
func TestMiscTypes(t *testing.T) { client := framework.NewClient() defer client.Execute("delete from vitess_misc", nil) _, err := client.Execute( "insert into vitess_misc values(:id, :b, :d, :dt, :t)", map[string]interface{}{ "id": 1, "b": "\x01", "d": "2012-01-01", "dt": "2012-01-01 15:45:45", "t": "15:45:45", }, ) if err != nil { t.Error(err) return } qr, err := client.Execute("select * from vitess_misc where id = 1", nil) if err != nil { t.Error(err) return } want := sqltypes.Result{ Fields: []*querypb.Field{ { Name: "id", Type: sqltypes.Int32, }, { Name: "b", Type: sqltypes.Bit, }, { Name: "d", Type: sqltypes.Date, }, { Name: "dt", Type: sqltypes.Datetime, }, { Name: "t", Type: sqltypes.Time, }, }, RowsAffected: 1, Rows: [][]sqltypes.Value{ []sqltypes.Value{ sqltypes.MakeNumeric([]byte("1")), sqltypes.MakeString([]byte("\x01")), sqltypes.MakeString([]byte("2012-01-01")), sqltypes.MakeString([]byte("2012-01-01 15:45:45")), sqltypes.MakeString([]byte("15:45:45")), }, }, } if !reflect.DeepEqual(*qr, want) { t.Errorf("Execute: \n%#v, want \n%#v", *qr, want) } }
func (sq *testQueryService) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, sessionID int64, sendReply func(reply *sqltypes.Result) error) error { // Custom parsing of the query we expect min := 100 max := 200 var err error parts := strings.Split(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("testQueryService: got query: %v with min %v max %v", sql, min, max) // Send the headers if err := sendReply(&sqltypes.Result{ Fields: []*querypb.Field{ { Name: "id", Type: sqltypes.Int64, }, { Name: "msg", Type: sqltypes.VarChar, }, { Name: "keyspace_id", Type: sqltypes.Int64, }, }, }); err != nil { return err } // Send the values ksids := []uint64{0x2000000000000000, 0x6000000000000000} for i := min; i < max; i++ { if err := sendReply(&sqltypes.Result{ Rows: [][]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 }
func (sq *testQueryService) StreamExecute(ctx context.Context, query *proto.Query, sendReply func(reply *mproto.QueryResult) 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("testQueryService: 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 }
func (sq *sourceTabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, options *querypb.ExecuteOptions, sendReply func(reply *sqltypes.Result) error) error { if strings.Contains(sql, sq.excludedTable) { sq.t.Errorf("Split Diff operation on source should skip the excluded table: %v query: %v", sq.excludedTable, sql) } // we test for a keyspace_id where clause, except for v3 if !sq.v3 { if hasKeyspace := strings.Contains(sql, "WHERE `keyspace_id` < 0x4000000000000000"); hasKeyspace != true { sq.t.Errorf("Sql query on source should contain a keyspace_id WHERE clause; query received: %v", sql) } } sq.t.Logf("sourceTabletServer: got query: %v", sql) // Send the headers if err := sendReply(&sqltypes.Result{ Fields: []*querypb.Field{ { Name: "id", Type: sqltypes.Int64, }, { Name: "msg", Type: sqltypes.VarChar, }, { Name: "keyspace_id", Type: sqltypes.Int64, }, }, }); err != nil { return err } // Send the values ksids := []uint64{0x2000000000000000, 0x6000000000000000} for i := 0; i < 100; i++ { if !sq.v3 && i%2 == 1 { // for v2, filtering is done at SQL layer continue } if err := sendReply(&sqltypes.Result{ Rows: [][]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 } } return nil }
func createTestTableUpdatedStats(tableName string) []sqltypes.Value { return []sqltypes.Value{ sqltypes.MakeString([]byte(tableName)), sqltypes.MakeString([]byte("USER TABLE")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("0")), sqltypes.MakeString([]byte("")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("4")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("5")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("6")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("7")), } }
func TestSqlQuerySplitQueryInvalidMinMax(t *testing.T) { db := setUpSqlQueryTest() testUtils := newTestUtils() pkMinMaxQuery := "SELECT MIN(pk), MAX(pk) FROM test_table" pkMinMaxQueryResp := &mproto.QueryResult{ Fields: []mproto.Field{ mproto.Field{Name: "pk", Type: mproto.VT_LONG}, }, RowsAffected: 1, Rows: [][]sqltypes.Value{ // this make SplitQueryFail []sqltypes.Value{ sqltypes.MakeString([]byte("invalid")), sqltypes.MakeString([]byte("invalid")), }, }, } db.AddQuery(pkMinMaxQuery, pkMinMaxQueryResp) config := testUtils.newQueryServiceConfig() sqlQuery := NewSqlQuery(config) dbconfigs := testUtils.newDBConfigs() err := sqlQuery.allowQueries(nil, &dbconfigs, []SchemaOverride{}, testUtils.newMysqld(&dbconfigs)) if err != nil { t.Fatalf("allowQueries failed: %v", err) } defer sqlQuery.disallowQueries() ctx := context.Background() query := proto.SplitQueryRequest{ Query: proto.BoundQuery{ Sql: "select * from test_table where count > :count", BindVariables: nil, }, SplitCount: 10, SessionID: sqlQuery.sessionID, } reply := proto.SplitQueryResult{ Queries: []proto.QuerySplit{ proto.QuerySplit{ Query: proto.BoundQuery{ Sql: "", BindVariables: nil, }, RowCount: 10, }, }, } if err := sqlQuery.SplitQuery(ctx, nil, &query, &reply); err == nil { t.Fatalf("SqlQuery.SplitQuery should fail") } }
func (sq *destinationTabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, options *querypb.ExecuteOptions, sendReply func(reply *sqltypes.Result) error) error { if strings.Contains(sql, sq.excludedTable) { sq.t.Errorf("Split Diff operation on destination should skip the excluded table: %v query: %v", sq.excludedTable, sql) } if hasKeyspace := strings.Contains(sql, "WHERE `keyspace_id`"); hasKeyspace == true { sq.t.Errorf("Sql query on destination should not contain a keyspace_id WHERE clause; query received: %v", sql) } sq.t.Logf("destinationTabletServer: got query: %v", sql) // Send the headers if err := sendReply(&sqltypes.Result{ Fields: []*querypb.Field{ { Name: "id", Type: sqltypes.Int64, }, { Name: "msg", Type: sqltypes.VarChar, }, { Name: "keyspace_id", Type: sqltypes.Int64, }, }, }); err != nil { return err } // Send the values ksids := []uint64{0x2000000000000000, 0x6000000000000000} for i := 0; i < 100; i++ { // skip the out-of-range values if i%2 == 1 { continue } if err := sendReply(&sqltypes.Result{ Rows: [][]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 } } return nil }
func createTestTableBaseShowTable(tableName string) []sqltypes.Value { return []sqltypes.Value{ sqltypes.MakeString([]byte(tableName)), sqltypes.MakeString([]byte("USER TABLE")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("1427325875")), sqltypes.MakeString([]byte("")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("1")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("2")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("3")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("4")), sqltypes.MakeTrusted(sqltypes.Int32, []byte("5")), } }
func (sq *sourceSqlQuery) StreamExecute(ctx context.Context, target *pb.Target, query *proto.Query, sendReply func(reply *mproto.QueryResult) error) error { if strings.Contains(query.Sql, sq.excludedTable) { sq.t.Errorf("Split Diff operation on source should skip the excluded table: %v query: %v", sq.excludedTable, query.Sql) } // we test for a keyspace_id where clause, except for on views. if !strings.Contains(query.Sql, "view") { if hasKeyspace := strings.Contains(query.Sql, "WHERE keyspace_id < 0x4000000000000000"); hasKeyspace != true { sq.t.Errorf("Sql query on source should contain a keyspace_id WHERE clause; query received: %v", query.Sql) } } sq.t.Logf("sourceSqlQuery: got query: %v", *query) // 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 := 0; i < 100; 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 } } return nil }
func BuildValue(bytes []byte, fieldType uint32) sqltypes.Value { 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 (sq *verticalTabletServer) StreamExecute(ctx context.Context, target *querypb.Target, sql string, bindVariables map[string]interface{}, sessionID int64, sendReply func(reply *sqltypes.Result) error) error { // Custom parsing of the query we expect min := 100 max := 200 var err error parts := strings.Split(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("verticalTabletServer: got query: %v with min %v max %v", sql, min, max) // Send the headers if err := sendReply(&sqltypes.Result{ Fields: []*querypb.Field{ { Name: "id", Type: sqltypes.Int64, }, { Name: "msg", Type: sqltypes.VarChar, }, }, }); err != nil { return err } // Send the values for i := min; i < max; i++ { if err := sendReply(&sqltypes.Result{ Rows: [][]sqltypes.Value{ { sqltypes.MakeString([]byte(fmt.Sprintf("%v", i))), sqltypes.MakeString([]byte(fmt.Sprintf("Text for %v", i))), }, }, }); err != nil { return err } } return nil }
func (sq *verticalTabletServer) StreamExecute(ctx context.Context, target *pb.Target, query *proto.Query, sendReply func(reply *mproto.QueryResult) 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("verticalTabletServer: 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, }, }, }); err != nil { return err } // Send the values 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))), }, }, }); err != nil { return err } } return nil }
func (sq *destinationTabletServer) StreamExecute(ctx context.Context, target *pbq.Target, query *proto.Query, sendReply func(reply *sqltypes.Result) error) error { if strings.Contains(query.Sql, sq.excludedTable) { sq.t.Errorf("Split Diff operation on destination should skip the excluded table: %v query: %v", sq.excludedTable, query.Sql) } if hasKeyspace := strings.Contains(query.Sql, "WHERE keyspace_id"); hasKeyspace == true { sq.t.Errorf("Sql query on destination should not contain a keyspace_id WHERE clause; query received: %v", query.Sql) } sq.t.Logf("destinationTabletServer: got query: %v", *query) // Send the headers if err := sendReply(&sqltypes.Result{ Fields: []*pbq.Field{ &pbq.Field{ Name: "id", Type: sqltypes.Int64, }, &pbq.Field{ Name: "msg", Type: sqltypes.VarChar, }, &pbq.Field{ Name: "keyspace_id", Type: sqltypes.Int64, }, }, }); err != nil { return err } // Send the values ksids := []uint64{0x2000000000000000, 0x6000000000000000} for i := 0; i < 100; i++ { if err := sendReply(&sqltypes.Result{ 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 } } return nil }
func TestTabletServerStreamExecute(t *testing.T) { db := setUpTabletServerTest() testUtils := newTestUtils() // sql that will be executed in this test executeSQL := "select * from test_table limit 1000" executeSQLResult := &sqltypes.Result{ RowsAffected: 1, Rows: [][]sqltypes.Value{ {sqltypes.MakeString([]byte("row01"))}, }, } db.AddQuery(executeSQL, executeSQLResult) config := testUtils.newQueryServiceConfig() tsv := NewTabletServer(config) dbconfigs := testUtils.newDBConfigs(db) target := querypb.Target{TabletType: topodatapb.TabletType_MASTER} err := tsv.StartService(target, dbconfigs, testUtils.newMysqld(&dbconfigs)) if err != nil { t.Fatalf("StartService failed: %v", err) } defer tsv.StopService() ctx := context.Background() sendReply := func(*sqltypes.Result) error { return nil } if err := tsv.StreamExecute(ctx, &target, executeSQL, nil, sendReply); err != nil { t.Fatalf("TabletServer.StreamExecute should success: %s, but get error: %v", executeSQL, err) } }
// Proto3ToQueryResult converts a proto3 QueryResult to an internal data structure. func Proto3ToQueryResult(qr *pbq.QueryResult) *QueryResult { result := &QueryResult{ RowsAffected: qr.RowsAffected, InsertId: qr.InsertId, } if len(qr.Fields) > 0 { result.Fields = make([]Field, len(qr.Fields)) for i, f := range qr.Fields { result.Fields[i].Name = f.Name result.Fields[i].Type = int64(f.Type) result.Fields[i].Flags = int64(f.Flags) } } if len(qr.Rows) > 0 { result.Rows = make([][]sqltypes.Value, len(qr.Rows)) for i, r := range qr.Rows { result.Rows[i] = make([]sqltypes.Value, len(r.Values)) for j, c := range r.Values { if c == nil { result.Rows[i][j] = sqltypes.NULL } else { result.Rows[i][j] = sqltypes.MakeString(c) } } } } return result }
func TestDBConnStream(t *testing.T) { db := fakesqldb.Register() testUtils := newTestUtils() sql := "select * from test_table limit 1000" expectedResult := &mproto.QueryResult{ RowsAffected: 0, Rows: [][]sqltypes.Value{ []sqltypes.Value{sqltypes.MakeString([]byte("123"))}, }, } db.AddQuery(sql, expectedResult) connPool := testUtils.newConnPool() appParams := &sqldb.ConnParams{Engine: db.Name} dbaParams := &sqldb.ConnParams{Engine: db.Name} connPool.Open(appParams, dbaParams) defer connPool.Close() ctx, cancel := context.WithDeadline(context.Background(), time.Now().Add(10*time.Second)) defer cancel() queryServiceStats := NewQueryServiceStats("", false) dbConn, err := NewDBConn(connPool, appParams, dbaParams, queryServiceStats) defer dbConn.Close() var result mproto.QueryResult err = dbConn.Stream( ctx, sql, func(r *mproto.QueryResult) error { result = *r return nil }, 10) if err != nil { t.Fatalf("should not get an error, err: %v", err) } testUtils.checkEqual(t, expectedResult, &result) }
func TestQueryExecutorPlanPassSelectWithInATransaction(t *testing.T) { db := setUpQueryExecutorTest() testUtils := &testUtils{} fields := []mproto.Field{ mproto.Field{Name: "addr", Type: mproto.VT_LONG}, } query := "select addr from test_table where pk = 1 limit 1000" expected := &mproto.QueryResult{ Fields: fields, RowsAffected: 1, Rows: [][]sqltypes.Value{ []sqltypes.Value{sqltypes.MakeString([]byte("123"))}, }, } db.AddQuery(query, expected) db.AddQuery("select addr from test_table where 1 != 1", &mproto.QueryResult{ Fields: fields, }) qre, sqlQuery := newTestQueryExecutor( query, context.Background(), enableTx|enableStrict) defer sqlQuery.disallowQueries() defer testCommitHelper(t, sqlQuery, qre) checkPlanID(t, planbuilder.PLAN_PASS_SELECT, qre.plan.PlanId) testUtils.checkEqual(t, expected, qre.Execute()) }
func TestQueryExecutorPlanPassSelectWithInATransaction(t *testing.T) { db := setUpQueryExecutorTest() fields := []mproto.Field{ mproto.Field{Name: "addr", Type: mproto.VT_LONG}, } query := "select addr from test_table where pk = 1 limit 1000" want := &mproto.QueryResult{ Fields: fields, RowsAffected: 1, Rows: [][]sqltypes.Value{ []sqltypes.Value{sqltypes.MakeString([]byte("123"))}, }, } db.AddQuery(query, want) db.AddQuery("select addr from test_table where 1 != 1", &mproto.QueryResult{ Fields: fields, }) ctx := context.Background() sqlQuery := newTestSQLQuery(ctx, enableStrict) qre := newTestQueryExecutor(ctx, sqlQuery, query, newTransaction(sqlQuery)) defer sqlQuery.disallowQueries() defer testCommitHelper(t, sqlQuery, qre) checkPlanID(t, planbuilder.PLAN_PASS_SELECT, qre.plan.PlanId) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) } if !reflect.DeepEqual(got, want) { t.Fatalf("got: %v, want: %v", got, want) } }
func TestQueryExecutorPlanPassSelectWithInATransaction(t *testing.T) { db := setUpQueryExecutorTest() fields := []*querypb.Field{ {Name: "addr", Type: sqltypes.Int32}, } query := "select addr from test_table where pk = 1 limit 1000" want := &sqltypes.Result{ Fields: fields, RowsAffected: 1, Rows: [][]sqltypes.Value{ {sqltypes.MakeString([]byte("123"))}, }, } db.AddQuery(query, want) db.AddQuery("select addr from test_table where 1 != 1", &sqltypes.Result{ Fields: fields, }) ctx := context.Background() tsv := newTestTabletServer(ctx, enableStrict, db) qre := newTestQueryExecutor(ctx, tsv, query, newTransaction(tsv)) defer tsv.StopService() defer testCommitHelper(t, tsv, qre) checkPlanID(t, planbuilder.PlanPassSelect, qre.plan.PlanID) got, err := qre.Execute() if err != nil { t.Fatalf("qre.Execute() = %v, want nil", err) } if !reflect.DeepEqual(got, want) { t.Fatalf("got: %v, want: %v", got, want) } }