func TestCodexBuildSecondaryList(t *testing.T) { pk1 := "pk1" pk2 := "pk2" tableInfo := createTableInfo("Table", []string{"pk1", "pk2", "col1"}, []string{"int", "varbinary(128)", "int"}, []string{pk1, pk2}) // set pk2 = 'xyz' where pk1=1 and pk2 = 'abc' bindVars := map[string]interface{}{} pk1Val, _ := sqltypes.BuildValue(1) pk2Val, _ := sqltypes.BuildValue("abc") pkValues := []interface{}{pk1Val, pk2Val} pkList, _ := buildValueList(&tableInfo, pkValues, bindVars) pk2SecVal, _ := sqltypes.BuildValue("xyz") secondaryPKValues := []interface{}{nil, pk2SecVal} // want [[1 xyz]] want := [][]sqltypes.Value{ []sqltypes.Value{pk1Val, pk2SecVal}} got, _ := buildSecondaryList(&tableInfo, pkList, secondaryPKValues, bindVars) if !reflect.DeepEqual(got, want) { t.Fatalf("case 1 failed, got %v, want %v", got, want) } secondaryPKValues = []interface{}{"invalid_type", 1} _, err := buildSecondaryList(&tableInfo, pkList, secondaryPKValues, bindVars) if err == nil { t.Fatalf("should get an error, column 0 is int type, but secondary list provides a string") } }
func TestSplitQueryFractionalColumn(t *testing.T) { schemaInfo := getSchemaInfo() query := &proto.BoundQuery{ Sql: "select * from test_table where count > :count", } splitter := NewQuerySplitter(query, "", 3, schemaInfo) splitter.validateQuery() min, _ := sqltypes.BuildValue(10.5) max, _ := sqltypes.BuildValue(490.5) minField := &pbq.Field{ Name: "min", Type: sqltypes.Float32, } maxField := &pbq.Field{ Name: "max", Type: sqltypes.Float32, } fields := []*pbq.Field{minField, maxField} pkMinMax := &sqltypes.Result{ Fields: fields, Rows: [][]sqltypes.Value{[]sqltypes.Value{min, max}}, } splits, err := splitter.split(sqltypes.Float32, pkMinMax) if err != nil { t.Fatalf("unexpected error: %v", err) } got := []proto.BoundQuery{} for _, split := range splits { if split.RowCount != 160 { t.Errorf("wrong RowCount, got: %v, want: %v", split.RowCount, 160) } got = append(got, split.Query) } want := []proto.BoundQuery{ { Sql: "select * from test_table where (count > :count) and (id < :" + endBindVarName + ")", BindVariables: map[string]interface{}{endBindVarName: 170.5}, }, { Sql: fmt.Sprintf("select * from test_table where (count > :count) and (id >= :%s and id < :%s)", startBindVarName, endBindVarName), BindVariables: map[string]interface{}{ startBindVarName: 170.5, endBindVarName: 330.5, }, }, { Sql: "select * from test_table where (count > :count) and (id >= :" + startBindVarName + ")", BindVariables: map[string]interface{}{startBindVarName: 330.5}, }, } if !reflect.DeepEqual(got, want) { t.Errorf("wrong splits, got: %v, want: %v", got, want) } }
func (qs *QuerySplitter) splitBoundariesIntColumn(pkMinMax *mproto.QueryResult) ([]sqltypes.Value, error) { boundaries := []sqltypes.Value{} if pkMinMax == nil || len(pkMinMax.Rows) != 1 || pkMinMax.Rows[0][0].IsNull() || pkMinMax.Rows[0][1].IsNull() { return boundaries, nil } minNumeric := sqltypes.MakeNumeric(pkMinMax.Rows[0][0].Raw()) maxNumeric := sqltypes.MakeNumeric(pkMinMax.Rows[0][1].Raw()) if pkMinMax.Rows[0][0].Raw()[0] == '-' { // signed values, use int64 min, err := minNumeric.ParseInt64() if err != nil { return nil, err } max, err := maxNumeric.ParseInt64() if err != nil { return nil, err } interval := (max - min) / int64(qs.splitCount) if interval == 0 { return nil, err } qs.rowCount = interval for i := int64(1); i < int64(qs.splitCount); i++ { v, err := sqltypes.BuildValue(min + interval*i) if err != nil { return nil, err } boundaries = append(boundaries, v) } return boundaries, nil } // unsigned values, use uint64 min, err := minNumeric.ParseUint64() if err != nil { return nil, err } max, err := maxNumeric.ParseUint64() if err != nil { return nil, err } interval := (max - min) / uint64(qs.splitCount) if interval == 0 { return nil, err } qs.rowCount = int64(interval) for i := uint64(1); i < uint64(qs.splitCount); i++ { v, err := sqltypes.BuildValue(min + interval*i) if err != nil { return nil, err } boundaries = append(boundaries, v) } return boundaries, nil }
func toChunk(start, end interface{}, number, total int) (chunk, error) { startValue, err := sqltypes.BuildValue(start) if err != nil { return chunk{}, fmt.Errorf("Failed to convert calculated start value (%v) into internal sqltypes.Value: %v", start, err) } endValue, err := sqltypes.BuildValue(end) if err != nil { return chunk{}, fmt.Errorf("Failed to convert calculated end value (%v) into internal sqltypes.Value: %v", end, err) } return chunk{startValue, endValue, number, total}, nil }
// populateLocalMetadata creates and fills the _vt.local_metadata table, // which is a per-tablet table that is never replicated. This allows queries // against local_metadata to return different values on different tablets, // which is used for communicating between Vitess and MySQL-level tools like // Orchestrator (http://github.com/outbrain/orchestrator). func populateLocalMetadata(mysqld MysqlDaemon, localMetadata map[string]string) error { log.Infof("Populating _vt.local_metadata table...") // Get a non-pooled DBA connection. conn, err := mysqld.GetDbaConnection() if err != nil { return err } defer conn.Close() // Disable replication on this session. We close the connection after using // it, so there's no need to re-enable replication when we're done. if _, err := conn.ExecuteFetch("SET @@session.sql_log_bin = 0", 0, false); err != nil { return err } // Create the database and table if necessary. if _, err := conn.ExecuteFetch("CREATE DATABASE IF NOT EXISTS _vt", 0, false); err != nil { return err } if _, err := conn.ExecuteFetch(sqlCreateLocalMetadataTable, 0, false); err != nil { return err } if _, err := conn.ExecuteFetch("BEGIN", 0, false); err != nil { return err } for name, val := range localMetadata { nameValue, err := sqltypes.BuildValue(name) if err != nil { return err } valValue, err := sqltypes.BuildValue(val) if err != nil { return err } queryBuf := bytes.Buffer{} queryBuf.WriteString("INSERT INTO _vt.local_metadata (name,value) VALUES (") nameValue.EncodeSQL(&queryBuf) queryBuf.WriteByte(',') valValue.EncodeSQL(&queryBuf) queryBuf.WriteString(") ON DUPLICATE KEY UPDATE value = ") valValue.EncodeSQL(&queryBuf) if _, err := conn.ExecuteFetch(queryBuf.String(), 0, false); err != nil { return err } } _, err = conn.ExecuteFetch("COMMIT", 0, false) return err }
func createRowMultiPk(id int) []sqltypes.Value { // Map id from the one dimensional space to a two-dimensional space. // Examples: 0, 1, 2, 3, 4 => (0, 0), (0, 1), (1, 0), (1, 1), (2, 0) newID := id / 2 subID := id % 2 idValue, _ := sqltypes.BuildValue(int32(newID)) subIDValue, _ := sqltypes.BuildValue(int32(subID)) return []sqltypes.Value{ idValue, subIDValue, sqltypes.MakeString([]byte(fmt.Sprintf("msg %d", id))), } }
func TestSplitQuery(t *testing.T) { schemaInfo := getSchemaInfo() query := &proto.BoundQuery{ Sql: "select * from test_table where count > :count", } splitter := NewQuerySplitter(query, "", 3, schemaInfo) splitter.validateQuery() min, _ := sqltypes.BuildValue(0) max, _ := sqltypes.BuildValue(300) minField := mproto.Field{ Name: "min", Type: mproto.VT_LONGLONG, } maxField := mproto.Field{ Name: "min", Type: mproto.VT_LONGLONG, } fields := []mproto.Field{minField, maxField} pkMinMax := &mproto.QueryResult{ Fields: fields, } // Ensure that empty min max does not cause panic or return any error splits, err := splitter.split(pkMinMax) if err != nil { t.Errorf("unexpected error while splitting on empty pkMinMax, %s", err) } pkMinMax.Rows = [][]sqltypes.Value{[]sqltypes.Value{min, max}} splits, err = splitter.split(pkMinMax) if err != nil { t.Fatalf("unexpected error: %v", err) } got := []string{} for _, split := range splits { if split.RowCount != 100 { t.Errorf("wrong RowCount, got: %v, want: %v", split.RowCount, 100) } got = append(got, split.Query.Sql) } want := []string{ "select * from test_table where count > :count and id < 100", "select * from test_table where count > :count and id >= 100 and id < 200", "select * from test_table where count > :count and id >= 200", } if !reflect.DeepEqual(got, want) { t.Errorf("wrong splits, got: %v, want: %v", got, want) } }
func (rci *RowcacheInvalidator) handleDMLEvent(event *blproto.StreamEvent) { invalidations := int64(0) tableInfo := rci.qe.schemaInfo.GetTable(event.TableName) if tableInfo == nil { panic(NewTabletError(ErrFail, "Table %s not found", event.TableName)) } if tableInfo.CacheType == schema.CACHE_NONE { return } sqlTypeKeys := make([]sqltypes.Value, 0, len(event.PKColNames)) for _, pkTuple := range event.PKValues { sqlTypeKeys = sqlTypeKeys[:0] for _, pkVal := range pkTuple { key, err := sqltypes.BuildValue(pkVal) if err != nil { log.Errorf("Error building invalidation key for %#v: '%v'", event, err) rci.qe.queryServiceStats.InternalErrors.Add("Invalidation", 1) return } sqlTypeKeys = append(sqlTypeKeys, key) } newKey := validateKey(tableInfo, buildKey(sqlTypeKeys), rci.qe.queryServiceStats) if newKey == "" { continue } tableInfo.Cache.Delete(context.Background(), newKey) invalidations++ } tableInfo.invalidations.Add(invalidations) }
func EncodeValue(buf *bytes.Buffer, value interface{}) error { switch bindVal := value.(type) { case nil: buf.WriteString("null") case []sqltypes.Value: for i := 0; i < len(bindVal); i++ { if i != 0 { buf.WriteString(", ") } if err := EncodeValue(buf, bindVal[i]); err != nil { return err } } case [][]sqltypes.Value: for i := 0; i < len(bindVal); i++ { if i != 0 { buf.WriteString(", ") } buf.WriteByte('(') if err := EncodeValue(buf, bindVal[i]); err != nil { return err } buf.WriteByte(')') } default: v, err := sqltypes.BuildValue(bindVal) if err != nil { return err } v.EncodeSql(buf) } return nil }
func resolveValue(col *schema.TableColumn, value interface{}, bindVars map[string]interface{}) (result sqltypes.Value, err error) { switch v := value.(type) { case string: lookup, ok := bindVars[v[1:]] if !ok { return result, NewTabletError(FAIL, "missing bind var %s", v) } if sqlval, err := sqltypes.BuildValue(lookup); err != nil { return result, NewTabletError(FAIL, err.Error()) } else { result = sqlval } case sqltypes.Value: result = v case nil: // no op default: panic(fmt.Sprintf("incompatible value type %v", v)) } if err = validateValue(col, result); err != nil { return result, err } return result, nil }
// 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 (qs *QuerySplitter) splitBoundariesUintColumn(pkMinMax *sqltypes.Result) ([]sqltypes.Value, error) { boundaries := []sqltypes.Value{} if pkMinMax == nil || len(pkMinMax.Rows) != 1 || pkMinMax.Rows[0][0].IsNull() || pkMinMax.Rows[0][1].IsNull() { return boundaries, nil } minNumeric := pkMinMax.Rows[0][0] maxNumeric := pkMinMax.Rows[0][1] min, err := minNumeric.ParseUint64() if err != nil { return nil, err } max, err := maxNumeric.ParseUint64() if err != nil { return nil, err } interval := (max - min) / uint64(qs.splitCount) if interval == 0 { return nil, err } qs.rowCount = int64(interval) for i := uint64(1); i < uint64(qs.splitCount); i++ { v, err := sqltypes.BuildValue(min + interval*i) if err != nil { return nil, err } boundaries = append(boundaries, v) } return boundaries, nil }
func createRowSinglePk(id int) []sqltypes.Value { idValue, _ := sqltypes.BuildValue(int32(id)) return []sqltypes.Value{ idValue, sqltypes.MakeString([]byte(fmt.Sprintf("msg %d", id))), } }
func (qs *QuerySplitter) splitBoundariesFloatColumn(pkMinMax *sqltypes.Result) ([]sqltypes.Value, error) { boundaries := []sqltypes.Value{} if pkMinMax == nil || len(pkMinMax.Rows) != 1 || pkMinMax.Rows[0][0].IsNull() || pkMinMax.Rows[0][1].IsNull() { return boundaries, nil } min, err := strconv.ParseFloat(pkMinMax.Rows[0][0].String(), 64) if err != nil { return nil, err } max, err := strconv.ParseFloat(pkMinMax.Rows[0][1].String(), 64) if err != nil { return nil, err } interval := (max - min) / float64(qs.splitCount) if interval == 0 { return nil, err } qs.rowCount = int64(interval) for i := 1; i < int(qs.splitCount); i++ { boundary := min + interval*float64(i) v, err := sqltypes.BuildValue(boundary) if err != nil { return nil, err } boundaries = append(boundaries, v) } return boundaries, nil }
func (rowCache *InvalidationProcessor) buildDmlData(event *mysqlctl.UpdateResponse) (*proto.DmlType, error) { if !isDmlEvent(event.SqlType) { rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, fmt.Sprintf("Bad Dml type, '%v'", event.SqlType), event.BinlogPosition.String())) return nil, nil } dml := new(proto.DmlType) dml.Table = event.TableName dml.Keys = make([]interface{}, 0, len(event.PkValues)) sqlTypeKeys := make([]sqltypes.Value, 0, len(event.PkColNames)) for _, pkTuple := range event.PkValues { sqlTypeKeys = sqlTypeKeys[:0] if len(pkTuple) == 0 { continue } for _, pkVal := range pkTuple { key, err := sqltypes.BuildValue(pkVal) if err != nil { rowCache.updateErrCounters(NewInvalidationError(INVALID_EVENT, fmt.Sprintf("Error building invalidation key '%v'", err), event.BinlogPosition.String())) return nil, nil } sqlTypeKeys = append(sqlTypeKeys, key) } invalidateKey := buildKey(sqlTypeKeys) if invalidateKey != "" { dml.Keys = append(dml.Keys, invalidateKey) } } return dml, nil }
// Returns an escaped literal string func Literal(v interface{}) Expression { value, err := sqltypes.BuildValue(v) if err != nil { panic("sqlbuilder: invalid literal value: " + err.Error()) } return &literalExpression{value: value} }
// writeStartedSwap registers in the _vt.shard_metadata table in the database the information // about the new schema swap process being started. func (shardSwap *shardSchemaSwap) writeStartedSwap(sql string) error { tablet, err := shardSwap.getMasterTablet() if err != nil { return err } query := fmt.Sprintf( "INSERT INTO _vt.shard_metadata (name, value) VALUES ('%s', '%d') ON DUPLICATE KEY UPDATE value = '%d'", lastStartedMetadataName, shardSwap.parent.swapID, shardSwap.parent.swapID) _, err = shardSwap.executeAdminQuery(tablet, query, 0 /* maxRows */) if err != nil { return err } queryBuf := bytes.Buffer{} queryBuf.WriteString("INSERT INTO _vt.shard_metadata (name, value) VALUES ('") queryBuf.WriteString(currentSQLMetadataName) queryBuf.WriteString("',") sqlValue, err := sqltypes.BuildValue(sql) if err != nil { return err } sqlValue.EncodeSQL(&queryBuf) queryBuf.WriteString(") ON DUPLICATE KEY UPDATE value = ") sqlValue.EncodeSQL(&queryBuf) _, err = shardSwap.executeAdminQuery(tablet, queryBuf.String(), 0 /* maxRows */) return err }
func getSchemaInfo() *SchemaInfo { table := &schema.Table{ Name: "test_table", } zero, _ := sqltypes.BuildValue(0) table.AddColumn("id", sqltypes.Int64, zero, "") table.AddColumn("id2", sqltypes.Int64, zero, "") table.AddColumn("count", sqltypes.Int64, zero, "") table.PKColumns = []int{0} primaryIndex := table.AddIndex("PRIMARY") primaryIndex.AddColumn("id", 12345) id2Index := table.AddIndex("idx_id2") id2Index.AddColumn("id2", 1234) tables := make(map[string]*TableInfo, 1) tables["test_table"] = &TableInfo{Table: table} tableNoPK := &schema.Table{ Name: "test_table_no_pk", } tableNoPK.AddColumn("id", sqltypes.Int64, zero, "") tableNoPK.PKColumns = []int{} tables["test_table_no_pk"] = &TableInfo{Table: tableNoPK} return &SchemaInfo{tables: tables} }
func (qs *QuerySplitter) parseFloat(pkMinMax *mproto.QueryResult) ([]sqltypes.Value, error) { boundaries := []sqltypes.Value{} min, err := strconv.ParseFloat(pkMinMax.Rows[0][0].String(), 64) if err != nil { return nil, err } max, err := strconv.ParseFloat(pkMinMax.Rows[0][1].String(), 64) if err != nil { return nil, err } interval := (max - min) / float64(qs.splitCount) if interval == 0 { return nil, err } qs.rowCount = int64(interval) for i := 1; i < qs.splitCount; i++ { boundary := min + interval*float64(i) v, err := sqltypes.BuildValue(boundary) if err != nil { return nil, err } boundaries = append(boundaries, v) } return boundaries, nil }
func TestSplitQuery(t *testing.T) { schemaInfo := getSchemaInfo() query := &proto.BoundQuery{ Sql: "select * from test_table where count > :count", } splitter := NewQuerySplitter(query, 3, schemaInfo) splitter.validateQuery() min, _ := sqltypes.BuildValue(0) max, _ := sqltypes.BuildValue(300) minField := mproto.Field{ Name: "min", Type: mproto.VT_LONGLONG, } maxField := mproto.Field{ Name: "min", Type: mproto.VT_LONGLONG, } fields := []mproto.Field{minField, maxField} row := []sqltypes.Value{min, max} rows := [][]sqltypes.Value{row} pkMinMax := &mproto.QueryResult{ Rows: rows, Fields: fields, } splits := splitter.split(pkMinMax) got := []string{} for _, split := range splits { if split.RowCount != 100 { t.Errorf("wrong RowCount, got: %v, want: %v", split.RowCount, 100) } got = append(got, split.Query.Sql) } want := []string{ "select * from test_table where count > :count and id < 100", "select * from test_table where count > :count and id >= 100 and id < 200", "select * from test_table where count > :count and id >= 200", } if !reflect.DeepEqual(got, want) { t.Errorf("wrong splits, got: %v, want: %v", got, want) } }
func TestBuildStreamComment(t *testing.T) { pk1 := "pk1" pk2 := "pk2" tableInfo := createTableInfo("Table", map[string]string{pk1: "int", pk2: "varchar(128)", "col1": "int"}, []string{pk1, pk2}) // set pk2 = 'xyz' where pk1=1 and pk2 = 'abc' bindVars := map[string]interface{}{} pk1Val, _ := sqltypes.BuildValue(1) pk2Val, _ := sqltypes.BuildValue("abc") pkValues := []interface{}{pk1Val, pk2Val} pkList, _ := buildValueList(&tableInfo, pkValues, bindVars) pk2SecVal, _ := sqltypes.BuildValue("xyz") secondaryPKValues := []interface{}{nil, pk2SecVal} secondaryList, _ := buildSecondaryList(&tableInfo, pkList, secondaryPKValues, bindVars) want := []byte(" /* _stream Table (pk1 pk2 ) (1 'YWJj' ) (1 'eHl6' ); */") got := buildStreamComment(&tableInfo, pkList, secondaryList) if !reflect.DeepEqual(got, want) { t.Errorf("case 1 failed, got %v, want %v", got, want) } }
func TestBuildSecondaryList(t *testing.T) { pk1 := "pk1" pk2 := "pk2" tableInfo := createTableInfo("Table", map[string]string{pk1: "int", pk2: "varchar(128)", "col1": "int"}, []string{pk1, pk2}) // set pk2 = 'xyz' where pk1=1 and pk2 = 'abc' bindVars := map[string]interface{}{} pk1Val, _ := sqltypes.BuildValue(1) pk2Val, _ := sqltypes.BuildValue("abc") pkValues := []interface{}{pk1Val, pk2Val} pkList, _ := buildValueList(&tableInfo, pkValues, bindVars) pk2SecVal, _ := sqltypes.BuildValue("xyz") secondaryPKValues := []interface{}{nil, pk2SecVal} // want [[1 xyz]] want := [][]sqltypes.Value{ []sqltypes.Value{pk1Val, pk2SecVal}} got, _ := buildSecondaryList(&tableInfo, pkList, secondaryPKValues, bindVars) if !reflect.DeepEqual(got, want) { t.Errorf("case 1 failed, got %v, want %v", got, want) } }
func TestBuildINValueList(t *testing.T) { pk1 := "pk1" tableInfo := createTableInfo("Table", map[string]string{pk1: "int", "col1": "int"}, []string{pk1}) // case 1: single PK IN clause // e.g. where pk1 in(1, 2, 3) bindVars := map[string]interface{}{} pk1Val, _ := sqltypes.BuildValue(1) pk1Val2, _ := sqltypes.BuildValue(2) pk1Val3, _ := sqltypes.BuildValue(3) pkValues := []interface{}{pk1Val, pk1Val2, pk1Val3} // want [[1][2][3]] want := [][]sqltypes.Value{ []sqltypes.Value{pk1Val}, []sqltypes.Value{pk1Val2}, []sqltypes.Value{pk1Val3}} got, _ := buildINValueList(&tableInfo, pkValues, bindVars) if !reflect.DeepEqual(got, want) { t.Errorf("case 1 failed, got %v, want %v", got, want) } }
func TestCodexBuildStreamComment(t *testing.T) { pk1 := "pk1" pk2 := "pk2" tableInfo := createTableInfo("Table", []string{"pk1", "pk2", "col1"}, []querypb.Type{sqltypes.Int64, sqltypes.VarBinary, sqltypes.Int32}, []string{pk1, pk2}) // set pk2 = 'xyz' where pk1=1 and pk2 = 'abc' bindVars := map[string]interface{}{} pk1Val, _ := sqltypes.BuildValue(1) pk2Val, _ := sqltypes.BuildValue("abc") pkValues := []interface{}{pk1Val, pk2Val} pkList, _ := buildValueList(&tableInfo, pkValues, bindVars) pk2SecVal, _ := sqltypes.BuildValue("xyz") secondaryPKValues := []interface{}{nil, pk2SecVal} secondaryList, _ := buildSecondaryList(&tableInfo, pkList, secondaryPKValues, bindVars) want := []byte(" /* _stream Table (pk1 pk2 ) (1 'YWJj' ) (1 'eHl6' ); */") got := buildStreamComment(&tableInfo, pkList, secondaryList) if !reflect.DeepEqual(got, want) { t.Fatalf("case 1 failed, got %v, want %v", got, want) } }
// EncodeValue encodes one bind variable value into the query. func EncodeValue(buf *bytes.Buffer, value interface{}) error { switch bindVal := value.(type) { case nil: buf.WriteString("null") case []sqltypes.Value: for i := 0; i < len(bindVal); i++ { if i != 0 { buf.WriteString(", ") } if err := EncodeValue(buf, bindVal[i]); err != nil { return err } } case [][]sqltypes.Value: for i := 0; i < len(bindVal); i++ { if i != 0 { buf.WriteString(", ") } buf.WriteByte('(') if err := EncodeValue(buf, bindVal[i]); err != nil { return err } buf.WriteByte(')') } case []interface{}: buf.WriteByte('(') for i, v := range bindVal { if i != 0 { buf.WriteString(", ") } if err := EncodeValue(buf, v); err != nil { return err } } buf.WriteByte(')') case TupleEqualityList: if err := bindVal.Encode(buf); err != nil { return err } default: v, err := sqltypes.BuildValue(bindVal) if err != nil { return err } v.EncodeSQL(buf) } return nil }
func TestGetSplitBoundaries(t *testing.T) { min, _ := sqltypes.BuildValue(10) max, _ := sqltypes.BuildValue(60) row := []sqltypes.Value{min, max} rows := [][]sqltypes.Value{row} minField := mproto.Field{Name: "min", Type: mproto.VT_LONGLONG} maxField := mproto.Field{Name: "max", Type: mproto.VT_LONGLONG} fields := []mproto.Field{minField, maxField} pkMinMax := &mproto.QueryResult{ Fields: fields, Rows: rows, } splitter := &QuerySplitter{} splitter.splitCount = 5 boundaries := splitter.getSplitBoundaries(pkMinMax) if len(boundaries) != splitter.splitCount-1 { t.Errorf("wrong number of boundaries got: %v, want: %v", len(boundaries), splitter.splitCount-1) } got := splitter.getSplitBoundaries(pkMinMax) want := []sqltypes.Value{buildVal(20), buildVal(30), buildVal(40), buildVal(50)} if !reflect.DeepEqual(got, want) { t.Errorf("incorrect boundaries, got: %v, want: %v", got, want) } // Test negative min value min, _ = sqltypes.BuildValue(-100) max, _ = sqltypes.BuildValue(100) row = []sqltypes.Value{min, max} rows = [][]sqltypes.Value{row} pkMinMax.Rows = rows got = splitter.getSplitBoundaries(pkMinMax) want = []sqltypes.Value{buildVal(-60), buildVal(-20), buildVal(20), buildVal(60)} if !reflect.DeepEqual(got, want) { t.Errorf("incorrect boundaries, got: %v, want: %v", got, want) } // Test float min max min, _ = sqltypes.BuildValue(10.5) max, _ = sqltypes.BuildValue(60.5) row = []sqltypes.Value{min, max} rows = [][]sqltypes.Value{row} minField = mproto.Field{Name: "min", Type: mproto.VT_DOUBLE} maxField = mproto.Field{Name: "max", Type: mproto.VT_DOUBLE} fields = []mproto.Field{minField, maxField} pkMinMax.Rows = rows pkMinMax.Fields = fields got = splitter.getSplitBoundaries(pkMinMax) want = []sqltypes.Value{buildVal(20.5), buildVal(30.5), buildVal(40.5), buildVal(50.5)} if !reflect.DeepEqual(got, want) { t.Errorf("incorrect boundaries, got: %v, want: %v", got, want) } }
// TODO(shengzhe): support split based on min, max from the string column. func (qs *QuerySplitter) splitBoundariesStringColumn() ([]sqltypes.Value, error) { splitRange := int64(0xFFFFFFFF) + 1 splitSize := splitRange / int64(qs.splitCount) //TODO(shengzhe): have a better estimated row count based on table size. qs.rowCount = int64(splitSize) var boundaries []sqltypes.Value for i := 1; i < int(qs.splitCount); i++ { buf := make([]byte, 4) binary.BigEndian.PutUint32(buf, uint32(splitSize)*uint32(i)) val, err := sqltypes.BuildValue(buf) if err != nil { return nil, err } boundaries = append(boundaries, val) } return boundaries, nil }
// getSchema returns a fake schema object that can be given to SplitParams func getTestSchema() map[string]*schema.Table { table := schema.Table{ Name: "test_table", } zero, _ := sqltypes.BuildValue(0) table.AddColumn("id", sqltypes.Int64, zero, "") table.AddColumn("int32_col", sqltypes.Int32, zero, "") table.AddColumn("uint32_col", sqltypes.Uint32, zero, "") table.AddColumn("int64_col", sqltypes.Int64, zero, "") table.AddColumn("uint64_col", sqltypes.Uint64, zero, "") table.AddColumn("float32_col", sqltypes.Float32, zero, "") table.AddColumn("float64_col", sqltypes.Float64, zero, "") table.AddColumn("user_id", sqltypes.Int64, zero, "") table.AddColumn("user_id2", sqltypes.Int64, zero, "") table.AddColumn("id2", sqltypes.Int64, zero, "") table.AddColumn("count", sqltypes.Int64, zero, "") table.PKColumns = []int{0, 7} addIndexToTable(&table, "PRIMARY", "id", "user_id") addIndexToTable(&table, "idx_id2", "id2") addIndexToTable(&table, "idx_int64_col", "int64_col") addIndexToTable(&table, "idx_uint64_col", "uint64_col") addIndexToTable(&table, "idx_float64_col", "float64_col") addIndexToTable(&table, "idx_id_user_id", "id", "user_id") addIndexToTable(&table, "idx_id_user_id_user_id_2", "id", "user_id", "user_id2") table.SetMysqlStats( int64Value(1000), /* TableRows */ int64Value(100), /* DataLength */ int64Value(123), /* IndexLength */ int64Value(456), /* DataFree */ ) result := make(map[string]*schema.Table) result["test_table"] = &table tableNoPK := schema.Table{ Name: "test_table_no_pk", } tableNoPK.AddColumn("id", sqltypes.Int64, zero, "") tableNoPK.PKColumns = []int{} result["test_table_no_pk"] = &tableNoPK return result }
func resolveListArg(col *schema.TableColumn, key string, bindVars map[string]interface{}) ([]sqltypes.Value, error) { val, _, err := sqlparser.FetchBindVar(key, bindVars) if err != nil { return nil, NewTabletError(ErrFail, vtrpc.ErrorCode_BAD_INPUT, "%v", err) } list := val.([]interface{}) resolved := make([]sqltypes.Value, len(list)) for i, v := range list { sqlval, err := sqltypes.BuildValue(v) if err != nil { return nil, NewTabletError(ErrFail, vtrpc.ErrorCode_BAD_INPUT, "%v", err) } if err = validateValue(col, sqlval); err != nil { return nil, err } resolved[i] = sqlval } return resolved, nil }
func getSchemaInfo() *SchemaInfo { table := &schema.Table{ Name: "test_table", } zero, _ := sqltypes.BuildValue(0) table.AddColumn("id", "int", zero, "") table.AddColumn("count", "int", zero, "") table.PKColumns = []int{0} tables := make(map[string]*TableInfo, 1) tables["test_table"] = &TableInfo{Table: table} tableNoPK := &schema.Table{ Name: "test_table_no_pk", } tableNoPK.AddColumn("id", "int", zero, "") tableNoPK.PKColumns = []int{} tables["test_table_no_pk"] = &TableInfo{Table: tableNoPK} return &SchemaInfo{tables: tables} }