// AddColumn adds a column to the index. func (idx *Index) AddColumn(name string, cardinality uint64) { idx.Columns = append(idx.Columns, cistring.New(name)) if cardinality == 0 { cardinality = uint64(len(idx.Cardinality) + 1) } idx.Cardinality = append(idx.Cardinality, cardinality) }
func resolveAutoIncrement(source *vschemapb.SrvVSchema, vschema *VSchema) error { for ksname, ks := range source.Keyspaces { ksvschema := vschema.Keyspaces[ksname] for tname, table := range ks.Tables { t := ksvschema.Tables[tname] if table.AutoIncrement == nil { continue } t.AutoIncrement = &AutoIncrement{Column: cistring.New(table.AutoIncrement.Column), ColumnVindexNum: -1} seq := vschema.tables[table.AutoIncrement.Sequence] // TODO(sougou): improve this search. if seq == nil { return fmt.Errorf("sequence %s not found for table %s", table.AutoIncrement.Sequence, tname) } t.AutoIncrement.Sequence = seq for i, cv := range t.ColumnVindexes { if t.AutoIncrement.Column.Equal(cv.Column) { t.AutoIncrement.ColumnVindexNum = i break } } } } return nil }
func TestTableColumnString(t *testing.T) { c := &TableColumn{Name: cistring.New("my_column"), Type: querypb.Type_INT8} want := "{Name: 'my_column', Type: INT8}" got := fmt.Sprintf("%v", c) if got != want { t.Errorf("want: %v, got: %v", want, got) } }
// FindColumn finds a column in the table. It returns the index if found. // Otherwise, it returns -1. func (ta *Table) FindColumn(name string) int { ciName := cistring.New(name) for i, col := range ta.Columns { if col.Name.Equal(ciName) { return i } } return -1 }
// FindDataColumn finds a data column in the index. It returns the index if found. // Otherwise, it returns -1. func (idx *Index) FindDataColumn(name string) int { ciName := cistring.New(name) for i, colName := range idx.DataColumns { if colName.Equal(ciName) { return i } } return -1 }
// AddColumn adds a column to the Table. func (ta *Table) AddColumn(name string, columnType querypb.Type, defval sqltypes.Value, extra string) { index := len(ta.Columns) ta.Columns = append(ta.Columns, TableColumn{Name: cistring.New(name)}) ta.Columns[index].Type = columnType if extra == "auto_increment" { ta.Columns[index].IsAuto = true // Ignore default value, if any return } if defval.IsNull() { return } // Schema values are trusted. ta.Columns[index].Default = sqltypes.MakeTrusted(ta.Columns[index].Type, defval.Raw()) }
// NewQuerySplitter creates a new QuerySplitter. query is the original query // to split and splitCount is the desired number of splits. splitCount must // be a positive int, if not it will be set to 1. func NewQuerySplitter( sql string, bindVariables map[string]interface{}, splitColumn string, splitCount int64, schemaInfo *SchemaInfo) *QuerySplitter { if splitCount < 1 { splitCount = 1 } return &QuerySplitter{ sql: sql, bindVariables: bindVariables, splitCount: splitCount, schemaInfo: schemaInfo, splitColumn: cistring.New(splitColumn), } }
// NewColIdent makes a new ColIdent. func NewColIdent(str string) ColIdent { return ColIdent(cistring.New(str)) }
// NewIndex creates a new Index. func NewIndex(name string) *Index { return &Index{Name: cistring.New(name)} }
func TestGetWhereClause(t *testing.T) { splitter := &QuerySplitter{} sql := "select * from test_table where count > :count" statement, _ := sqlparser.Parse(sql) splitter.sel, _ = statement.(*sqlparser.Select) splitter.splitColumn = cistring.New("id") bindVars := make(map[string]interface{}) // no boundary case, start = end = nil, should not change the where clause nilValue := sqltypes.Value{} clause := splitter.getWhereClause(splitter.sel.Where, bindVars, nilValue, nilValue) want := " where count > :count" got := sqlparser.String(clause) if !reflect.DeepEqual(got, want) { t.Errorf("incorrect where clause for nil ranges, got:%v, want:%v", got, want) } // Set lower bound, should add the lower bound condition to where clause startVal := int64(20) start, _ := sqltypes.BuildValue(startVal) bindVars = make(map[string]interface{}) bindVars[":count"] = 300 clause = splitter.getWhereClause(splitter.sel.Where, bindVars, start, nilValue) want = " where (count > :count) and (id >= :" + startBindVarName + ")" got = sqlparser.String(clause) if !reflect.DeepEqual(got, want) { t.Errorf("incorrect where clause, got:%v, want:%v", got, want) } v, ok := bindVars[startBindVarName] if !ok { t.Fatalf("bind var: %s not found got: nil, want: %v", startBindVarName, startVal) } if v != startVal { t.Fatalf("bind var: %s not found got: %v, want: %v", startBindVarName, v, startVal) } // Set upper bound, should add the upper bound condition to where clause endVal := int64(40) end, _ := sqltypes.BuildValue(endVal) bindVars = make(map[string]interface{}) clause = splitter.getWhereClause(splitter.sel.Where, bindVars, nilValue, end) want = " where (count > :count) and (id < :" + endBindVarName + ")" got = sqlparser.String(clause) if !reflect.DeepEqual(got, want) { t.Errorf("incorrect where clause, got:%v, want:%v", got, want) } v, ok = bindVars[endBindVarName] if !ok { t.Fatalf("bind var: %s not found got: nil, want: %v", endBindVarName, endVal) } if v != endVal { t.Fatalf("bind var: %s not found got: %v, want: %v", endBindVarName, v, endVal) } // Set both bounds, should add two conditions to where clause bindVars = make(map[string]interface{}) clause = splitter.getWhereClause(splitter.sel.Where, bindVars, start, end) want = fmt.Sprintf(" where (count > :count) and (id >= :%s and id < :%s)", startBindVarName, endBindVarName) got = sqlparser.String(clause) if !reflect.DeepEqual(got, want) { t.Errorf("incorrect where clause, got:%v, want:%v", got, want) } // Original query with no where clause sql = "select * from test_table" statement, _ = sqlparser.Parse(sql) splitter.sel, _ = statement.(*sqlparser.Select) bindVars = make(map[string]interface{}) // no boundary case, start = end = nil should return no where clause clause = splitter.getWhereClause(splitter.sel.Where, bindVars, nilValue, nilValue) want = "" got = sqlparser.String(clause) if !reflect.DeepEqual(got, want) { t.Errorf("incorrect where clause for nil ranges, got:%v, want:%v", got, want) } bindVars = make(map[string]interface{}) // Set both bounds, should add two conditions to where clause clause = splitter.getWhereClause(splitter.sel.Where, bindVars, start, end) want = fmt.Sprintf(" where id >= :%s and id < :%s", startBindVarName, endBindVarName) got = sqlparser.String(clause) if !reflect.DeepEqual(got, want) { t.Errorf("incorrect where clause, got:%v, want:%v", got, want) } v, ok = bindVars[startBindVarName] if !ok { t.Fatalf("bind var: %s not found got: nil, want: %v", startBindVarName, startVal) } if v != startVal { t.Fatalf("bind var: %s not found got: %v, want: %v", startBindVarName, v, startVal) } v, ok = bindVars[endBindVarName] if !ok { t.Fatalf("bind var: %s not found got: nil, want: %v", endBindVarName, endVal) } if v != endVal { t.Fatalf("bind var: %s not found got: %v, want: %v", endBindVarName, v, endVal) } }
func TestVSchemaJSON(t *testing.T) { lkp, _ := NewLookupHash("n2", map[string]string{ "from": "f", "table": "t", "to": "2", }) in := map[string]*KeyspaceSchema{ "unsharded": { Keyspace: &Keyspace{ Name: "k1", }, Tables: map[string]*Table{ "t1": { Name: "n1", }, "t2": { IsSequence: true, Name: "n2", }, }, }, "sharded": { Keyspace: &Keyspace{ Name: "k2", Sharded: true, }, Tables: map[string]*Table{ "t3": { Name: "n3", ColumnVindexes: []*ColumnVindex{{ Column: cistring.New("aa"), Type: "vtype", Name: "vname", Owned: true, Vindex: lkp, }}, }, }, }, } out, err := json.MarshalIndent(in, "", " ") if err != nil { t.Fatal(err) } got := string(out) want := `{ "sharded": { "sharded": true, "tables": { "t3": { "name": "n3", "column_vindexes": [ { "column": "aa", "type": "vtype", "name": "vname", "owned": true, "vindex": { "table": "t", "from": "f", "to": "2" } } ] } } }, "unsharded": { "tables": { "t1": { "name": "n1" }, "t2": { "is_sequence": true, "name": "n2" } } } }` if got != want { t.Errorf("json.Marshal():\n%s, want\n%s", got, want) } }
func TestSequence(t *testing.T) { good := vschemapb.SrvVSchema{ Keyspaces: map[string]*vschemapb.Keyspace{ "unsharded": { Tables: map[string]*vschemapb.Table{ "seq": { Type: "sequence", }, }, }, "sharded": { Sharded: true, Vindexes: map[string]*vschemapb.Vindex{ "stfu1": { Type: "stfu", Params: map[string]string{ "stfu1": "1", }, }, }, Tables: map[string]*vschemapb.Table{ "t1": { ColumnVindexes: []*vschemapb.ColumnVindex{ { Column: "c1", Name: "stfu1", }, }, AutoIncrement: &vschemapb.AutoIncrement{ Column: "c1", Sequence: "seq", }, }, }, }, }, } got, err := BuildVSchema(&good) if err != nil { t.Error(err) } ksu := &Keyspace{ Name: "unsharded", } kss := &Keyspace{ Name: "sharded", Sharded: true, } seq := &Table{ Name: "seq", Keyspace: ksu, IsSequence: true, } t1 := &Table{ Name: "t1", Keyspace: kss, ColumnVindexes: []*ColumnVindex{ { Column: cistring.New("c1"), Type: "stfu", Name: "stfu1", Vindex: &stFU{ name: "stfu1", Params: map[string]string{ "stfu1": "1", }, }, }, }, AutoIncrement: &AutoIncrement{ Column: cistring.New("c1"), Sequence: seq, }, } t1.Ordered = []*ColumnVindex{ t1.ColumnVindexes[0], } want := &VSchema{ tables: map[string]*Table{ "seq": seq, "t1": t1, }, Keyspaces: map[string]*KeyspaceSchema{ "unsharded": { Keyspace: ksu, Tables: map[string]*Table{ "seq": seq, }, }, "sharded": { Keyspace: kss, Tables: map[string]*Table{ "t1": t1, }, }, }, } if !reflect.DeepEqual(got, want) { gotjson, _ := json.Marshal(got) wantjson, _ := json.Marshal(want) t.Errorf("BuildVSchema:s\n%s, want\n%s", gotjson, wantjson) } }
func TestShardedVSchemaNotOwned(t *testing.T) { good := vschemapb.SrvVSchema{ Keyspaces: map[string]*vschemapb.Keyspace{ "sharded": { Sharded: true, Vindexes: map[string]*vschemapb.Vindex{ "stlu1": { Type: "stlu", Owner: "", }, "stfu1": { Type: "stfu", Owner: "", }, }, Tables: map[string]*vschemapb.Table{ "t1": { ColumnVindexes: []*vschemapb.ColumnVindex{ { Column: "c1", Name: "stlu1", }, { Column: "c2", Name: "stfu1", }, }, }, }, }, }, } got, err := BuildVSchema(&good) if err != nil { t.Error(err) } ks := &Keyspace{ Name: "sharded", Sharded: true, } t1 := &Table{ Name: "t1", Keyspace: ks, ColumnVindexes: []*ColumnVindex{ { Column: cistring.New("c1"), Type: "stlu", Name: "stlu1", Owned: false, Vindex: &stLU{name: "stlu1"}, }, { Column: cistring.New("c2"), Type: "stfu", Name: "stfu1", Owned: false, Vindex: &stFU{name: "stfu1"}, }, }, } t1.Ordered = []*ColumnVindex{ t1.ColumnVindexes[1], t1.ColumnVindexes[0], } want := &VSchema{ tables: map[string]*Table{ "t1": t1, }, Keyspaces: map[string]*KeyspaceSchema{ "sharded": { Keyspace: ks, Tables: map[string]*Table{ "t1": t1, }, }, }, } if !reflect.DeepEqual(got, want) { t.Errorf("BuildVSchema:s\n%v, want\n%v", got, want) } }
func TestShardedVSchemaOwned(t *testing.T) { good := vschemapb.SrvVSchema{ Keyspaces: map[string]*vschemapb.Keyspace{ "sharded": { Sharded: true, Vindexes: map[string]*vschemapb.Vindex{ "stfu1": { Type: "stfu", Params: map[string]string{ "stfu1": "1", }, Owner: "t1", }, "stln1": { Type: "stln", Owner: "t1", }, }, Tables: map[string]*vschemapb.Table{ "t1": { ColumnVindexes: []*vschemapb.ColumnVindex{ { Column: "c1", Name: "stfu1", }, { Column: "c2", Name: "stln1", }, }, }, }, }, }, } got, err := BuildVSchema(&good) if err != nil { t.Error(err) } ks := &Keyspace{ Name: "sharded", Sharded: true, } t1 := &Table{ Name: "t1", Keyspace: ks, ColumnVindexes: []*ColumnVindex{ { Column: cistring.New("c1"), Type: "stfu", Name: "stfu1", Vindex: &stFU{ name: "stfu1", Params: map[string]string{ "stfu1": "1", }, }, }, { Column: cistring.New("c2"), Type: "stln", Name: "stln1", Owned: true, Vindex: &stLN{name: "stln1"}, }, }, } t1.Ordered = []*ColumnVindex{ t1.ColumnVindexes[1], t1.ColumnVindexes[0], } t1.Owned = t1.ColumnVindexes[1:] want := &VSchema{ tables: map[string]*Table{ "t1": t1, }, Keyspaces: map[string]*KeyspaceSchema{ "sharded": { Keyspace: ks, Tables: map[string]*Table{ "t1": t1, }, }, }, } if !reflect.DeepEqual(got, want) { gotjson, _ := json.Marshal(got) wantjson, _ := json.Marshal(want) t.Errorf("BuildVSchema:s\n%s, want\n%s", gotjson, wantjson) } }
func buildTables(source *vschemapb.SrvVSchema, vschema *VSchema) error { for ksname, ks := range source.Keyspaces { keyspace := vschema.Keyspaces[ksname].Keyspace vindexes := make(map[string]Vindex) for vname, vindexInfo := range ks.Vindexes { vindex, err := CreateVindex(vindexInfo.Type, vname, vindexInfo.Params) if err != nil { return err } switch vindex.(type) { case Unique: case NonUnique: default: return fmt.Errorf("vindex %s needs to be Unique or NonUnique", vname) } vindexes[vname] = vindex } for tname, table := range ks.Tables { t := &Table{ Name: tname, Keyspace: keyspace, } if _, ok := vschema.tables[tname]; ok { vschema.tables[tname] = nil } else { vschema.tables[tname] = t } vschema.Keyspaces[ksname].Tables[tname] = t if table.Type == "sequence" { t.IsSequence = true } if keyspace.Sharded && len(table.ColumnVindexes) == 0 { return fmt.Errorf("missing primary col vindex for table: %s", tname) } for i, ind := range table.ColumnVindexes { vindexInfo, ok := ks.Vindexes[ind.Name] if !ok { return fmt.Errorf("vindex %s not found for table %s", ind.Name, tname) } vindex := vindexes[ind.Name] owned := false if _, ok := vindex.(Lookup); ok && vindexInfo.Owner == tname { owned = true } columnVindex := &ColumnVindex{ Column: cistring.New(ind.Column), Type: vindexInfo.Type, Name: ind.Name, Owned: owned, Vindex: vindex, } if i == 0 { // Perform Primary vindex check. if _, ok := columnVindex.Vindex.(Unique); !ok { return fmt.Errorf("primary vindex %s is not Unique for table %s", ind.Name, tname) } if owned { return fmt.Errorf("primary vindex %s cannot be owned for table %s", ind.Name, tname) } } t.ColumnVindexes = append(t.ColumnVindexes, columnVindex) if owned { t.Owned = append(t.Owned, columnVindex) } } t.Ordered = colVindexSorted(t.ColumnVindexes) } } return nil }