vitess.io/vitess@v0.16.2/go/vt/vtgate/vindexes/vschema_test.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package vindexes 18 19 import ( 20 "context" 21 "encoding/json" 22 "errors" 23 "reflect" 24 "strings" 25 "testing" 26 27 "github.com/stretchr/testify/assert" 28 "github.com/stretchr/testify/require" 29 "google.golang.org/protobuf/proto" 30 31 "vitess.io/vitess/go/json2" 32 "vitess.io/vitess/go/sqltypes" 33 "vitess.io/vitess/go/test/utils" 34 "vitess.io/vitess/go/vt/key" 35 "vitess.io/vitess/go/vt/sqlparser" 36 37 querypb "vitess.io/vitess/go/vt/proto/query" 38 topodatapb "vitess.io/vitess/go/vt/proto/topodata" 39 vschemapb "vitess.io/vitess/go/vt/proto/vschema" 40 ) 41 42 // cheapVindex is a Functional, Unique Vindex. 43 type cheapVindex struct { 44 name string 45 Params map[string]string 46 } 47 48 func (v *cheapVindex) String() string { return v.name } 49 func (*cheapVindex) Cost() int { return 0 } 50 func (*cheapVindex) IsUnique() bool { return true } 51 func (*cheapVindex) NeedsVCursor() bool { return false } 52 func (*cheapVindex) Verify(context.Context, VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { 53 return []bool{}, nil 54 } 55 func (*cheapVindex) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 56 return nil, nil 57 } 58 59 func NewCheapVindex(name string, params map[string]string) (Vindex, error) { 60 return &cheapVindex{name: name, Params: params}, nil 61 } 62 63 var _ SingleColumn = (*stFU)(nil) 64 65 // stFU is a Functional, Unique Vindex. 66 type stFU struct { 67 name string 68 Params map[string]string 69 } 70 71 func (v *stFU) String() string { return v.name } 72 func (*stFU) Cost() int { return 1 } 73 func (*stFU) IsUnique() bool { return true } 74 func (*stFU) NeedsVCursor() bool { return false } 75 func (*stFU) Verify(context.Context, VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { 76 return []bool{}, nil 77 } 78 func (*stFU) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 79 return nil, nil 80 } 81 82 func NewSTFU(name string, params map[string]string) (Vindex, error) { 83 return &stFU{name: name, Params: params}, nil 84 } 85 86 var _ SingleColumn = (*stFU)(nil) 87 88 // stFN is a Functional, NonUnique Vindex. 89 type stFN struct { 90 name string 91 Params map[string]string 92 } 93 94 func (v *stFN) String() string { return v.name } 95 func (*stFN) Cost() int { return 1 } 96 func (*stFN) IsUnique() bool { return false } 97 func (*stFN) NeedsVCursor() bool { return false } 98 func (*stFN) Verify(context.Context, VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { 99 return []bool{}, nil 100 } 101 func (*stFN) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 102 return nil, nil 103 } 104 105 func NewSTFN(name string, params map[string]string) (Vindex, error) { 106 return &stFN{name: name, Params: params}, nil 107 } 108 109 var _ SingleColumn = (*stFN)(nil) 110 111 // stLN is a Lookup, NonUnique Vindex. 112 type stLN struct { 113 name string 114 Params map[string]string 115 } 116 117 func (v *stLN) String() string { return v.name } 118 func (*stLN) Cost() int { return 0 } 119 func (*stLN) IsUnique() bool { return false } 120 func (*stLN) NeedsVCursor() bool { return true } 121 func (*stLN) Verify(context.Context, VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { 122 return []bool{}, nil 123 } 124 func (*stLN) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 125 return nil, nil 126 } 127 func (*stLN) Create(context.Context, VCursor, [][]sqltypes.Value, [][]byte, bool) error { return nil } 128 func (*stLN) Delete(context.Context, VCursor, [][]sqltypes.Value, []byte) error { return nil } 129 func (*stLN) Update(context.Context, VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { 130 return nil 131 } 132 133 func NewSTLN(name string, params map[string]string) (Vindex, error) { 134 return &stLN{name: name, Params: params}, nil 135 } 136 137 var _ SingleColumn = (*stLN)(nil) 138 var _ Lookup = (*stLN)(nil) 139 140 // stLU is a Lookup, Unique Vindex. 141 type stLU struct { 142 name string 143 Params map[string]string 144 } 145 146 func (v *stLU) String() string { return v.name } 147 func (*stLU) Cost() int { return 2 } 148 func (*stLU) IsUnique() bool { return true } 149 func (*stLU) NeedsVCursor() bool { return true } 150 func (*stLU) Verify(context.Context, VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { 151 return []bool{}, nil 152 } 153 func (*stLU) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 154 return nil, nil 155 } 156 func (*stLU) Create(context.Context, VCursor, [][]sqltypes.Value, [][]byte, bool) error { return nil } 157 func (*stLU) Delete(context.Context, VCursor, [][]sqltypes.Value, []byte) error { return nil } 158 func (*stLU) Update(context.Context, VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { 159 return nil 160 } 161 162 func NewSTLU(name string, params map[string]string) (Vindex, error) { 163 return &stLU{name: name, Params: params}, nil 164 } 165 166 var _ SingleColumn = (*stLO)(nil) 167 var _ Lookup = (*stLO)(nil) 168 var _ WantOwnerInfo = (*stLO)(nil) 169 170 // stLO is a Lookup Vindex that wants owner columns. 171 type stLO struct { 172 keyspace string 173 name string 174 table string 175 cols []sqlparser.IdentifierCI 176 } 177 178 func (v *stLO) String() string { return v.name } 179 func (*stLO) Cost() int { return 2 } 180 func (*stLO) IsUnique() bool { return true } 181 func (*stLO) NeedsVCursor() bool { return true } 182 func (*stLO) Verify(context.Context, VCursor, []sqltypes.Value, [][]byte) ([]bool, error) { 183 return []bool{}, nil 184 } 185 func (*stLO) Map(ctx context.Context, vcursor VCursor, ids []sqltypes.Value) ([]key.Destination, error) { 186 return nil, nil 187 } 188 func (*stLO) Create(context.Context, VCursor, [][]sqltypes.Value, [][]byte, bool) error { return nil } 189 func (*stLO) Delete(context.Context, VCursor, [][]sqltypes.Value, []byte) error { return nil } 190 func (*stLO) Update(context.Context, VCursor, []sqltypes.Value, []byte, []sqltypes.Value) error { 191 return nil 192 } 193 func (v *stLO) SetOwnerInfo(keyspace, table string, cols []sqlparser.IdentifierCI) error { 194 v.keyspace = keyspace 195 v.table = table 196 v.cols = cols 197 return nil 198 } 199 200 func NewSTLO(name string, _ map[string]string) (Vindex, error) { 201 return &stLO{name: name}, nil 202 } 203 204 var _ SingleColumn = (*stLO)(nil) 205 var _ Lookup = (*stLO)(nil) 206 207 // mcFU is a multi-column Functional, Unique Vindex. 208 type mcFU struct { 209 name string 210 Params map[string]string 211 } 212 213 func (v *mcFU) String() string { return v.name } 214 func (*mcFU) Cost() int { return 1 } 215 func (*mcFU) IsUnique() bool { return true } 216 func (*mcFU) NeedsVCursor() bool { return false } 217 func (*mcFU) Verify(context.Context, VCursor, [][]sqltypes.Value, [][]byte) ([]bool, error) { 218 return []bool{}, nil 219 } 220 func (*mcFU) Map(ctx context.Context, vcursor VCursor, rowsColValues [][]sqltypes.Value) ([]key.Destination, error) { 221 return nil, nil 222 } 223 func (*mcFU) PartialVindex() bool { return false } 224 225 func NewMCFU(name string, params map[string]string) (Vindex, error) { 226 return &mcFU{name: name, Params: params}, nil 227 } 228 229 var _ MultiColumn = (*mcFU)(nil) 230 231 func init() { 232 Register("cheap", NewCheapVindex) 233 Register("stfu", NewSTFU) 234 Register("stfn", NewSTFN) 235 Register("stln", NewSTLN) 236 Register("stlu", NewSTLU) 237 Register("stlo", NewSTLO) 238 Register("region_experimental_test", NewRegionExperimental) 239 Register("mcfu", NewMCFU) 240 } 241 242 func TestUnshardedVSchemaValid(t *testing.T) { 243 err := ValidateKeyspace(&vschemapb.Keyspace{ 244 Sharded: false, 245 Vindexes: make(map[string]*vschemapb.Vindex), 246 Tables: make(map[string]*vschemapb.Table), 247 }) 248 require.NoError(t, err) 249 } 250 251 func TestUnshardedVSchema(t *testing.T) { 252 good := vschemapb.SrvVSchema{ 253 Keyspaces: map[string]*vschemapb.Keyspace{ 254 "unsharded": { 255 Tables: map[string]*vschemapb.Table{ 256 "t1": {}}}}} 257 258 got := BuildVSchema(&good) 259 require.NoError(t, got.Keyspaces["unsharded"].Error) 260 261 table, err := got.FindTable("unsharded", "t1") 262 require.NoError(t, err) 263 assert.NotNil(t, table) 264 265 table, err = got.FindTable("", "t1") 266 require.NoError(t, err) 267 assert.NotNil(t, table) 268 269 table, err = got.FindTable("", "dual") 270 require.NoError(t, err) 271 assert.Equal(t, TypeReference, table.Type) 272 } 273 274 func TestVSchemaColumns(t *testing.T) { 275 good := vschemapb.SrvVSchema{ 276 Keyspaces: map[string]*vschemapb.Keyspace{ 277 "unsharded": { 278 Tables: map[string]*vschemapb.Table{ 279 "t1": { 280 Columns: []*vschemapb.Column{{ 281 Name: "c1"}, { 282 Name: "c2", 283 Type: sqltypes.VarChar}}}}}}} 284 285 got := BuildVSchema(&good) 286 require.NoError(t, got.Keyspaces["unsharded"].Error) 287 288 t1, err := got.FindTable("unsharded", "t1") 289 require.NoError(t, err) 290 assertColumn(t, t1.Columns[0], "c1", sqltypes.Null) 291 assertColumn(t, t1.Columns[1], "c2", sqltypes.VarChar) 292 } 293 294 func TestVSchemaViews(t *testing.T) { 295 good := vschemapb.SrvVSchema{ 296 Keyspaces: map[string]*vschemapb.Keyspace{ 297 "unsharded": { 298 Tables: map[string]*vschemapb.Table{ 299 "t1": { 300 Columns: []*vschemapb.Column{{ 301 Name: "c1", 302 }, { 303 Name: "c2", 304 Type: sqltypes.VarChar}}}}}, 305 "main": { 306 Tables: map[string]*vschemapb.Table{ 307 "t1": { 308 Columns: []*vschemapb.Column{{ 309 Name: "c1", 310 }, { 311 Name: "c2", 312 Type: sqltypes.VarChar}}}}}}} 313 vschema := BuildVSchema(&good) 314 require.NoError(t, vschema.Keyspaces["unsharded"].Error) 315 316 // add view to unsharded keyspace. 317 vschema.AddView("unsharded", "v1", "SELECT c1+c2 AS added FROM t1") 318 319 view := vschema.FindView("unsharded", "v1") 320 assert.Equal(t, "select c1 + c2 as added from t1", sqlparser.String(view)) 321 322 view = vschema.FindView("", "v1") 323 assert.Equal(t, "select c1 + c2 as added from t1", sqlparser.String(view)) 324 325 out, err := json.MarshalIndent(vschema.Keyspaces["unsharded"], "", " ") 326 require.NoError(t, err) 327 got := string(out) 328 want := ` 329 { 330 "tables": { 331 "dual": { 332 "type": "reference", 333 "name": "dual" 334 }, 335 "t1": { 336 "name": "t1", 337 "columns": [ 338 { 339 "name": "c1", 340 "type": "NULL_TYPE" 341 }, 342 { 343 "name": "c2", 344 "type": "VARCHAR" 345 } 346 ] 347 } 348 }, 349 "views": { 350 "v1": "select c1 + c2 as added from t1" 351 } 352 }` 353 require.JSONEq(t, want, got) 354 } 355 356 func TestVSchemaColumnListAuthoritative(t *testing.T) { 357 good := vschemapb.SrvVSchema{ 358 Keyspaces: map[string]*vschemapb.Keyspace{ 359 "unsharded": { 360 Tables: map[string]*vschemapb.Table{ 361 "t1": { 362 Columns: []*vschemapb.Column{{ 363 Name: "c1"}, { 364 Name: "c2", 365 Type: sqltypes.VarChar}}, 366 ColumnListAuthoritative: true}}}}} 367 368 got := BuildVSchema(&good) 369 370 t1, err := got.FindTable("unsharded", "t1") 371 require.NoError(t, err) 372 assert.True(t, t1.ColumnListAuthoritative) 373 assertColumn(t, t1.Columns[0], "c1", sqltypes.Null) 374 assertColumn(t, t1.Columns[1], "c2", sqltypes.VarChar) 375 } 376 377 func TestVSchemaColumnsFail(t *testing.T) { 378 good := vschemapb.SrvVSchema{ 379 Keyspaces: map[string]*vschemapb.Keyspace{ 380 "unsharded": { 381 Tables: map[string]*vschemapb.Table{ 382 "t1": { 383 Columns: []*vschemapb.Column{{ 384 Name: "c1"}, { 385 Name: "c1"}}}}}}} 386 387 got := BuildVSchema(&good) 388 require.EqualError(t, got.Keyspaces["unsharded"].Error, "duplicate column name 'c1' for table: t1") 389 } 390 391 func TestVSchemaPinned(t *testing.T) { 392 good := vschemapb.SrvVSchema{ 393 Keyspaces: map[string]*vschemapb.Keyspace{ 394 "sharded": { 395 Sharded: true, 396 Tables: map[string]*vschemapb.Table{ 397 "t1": { 398 Pinned: "80"}}}}} 399 400 got := BuildVSchema(&good) 401 402 err := got.Keyspaces["sharded"].Error 403 require.NoError(t, err) 404 405 t1, err := got.FindTable("sharded", "t1") 406 require.NoError(t, err) 407 assert.Equal(t, "\x80", string(t1.Pinned)) 408 } 409 410 func TestShardedVSchemaOwned(t *testing.T) { 411 good := vschemapb.SrvVSchema{ 412 Keyspaces: map[string]*vschemapb.Keyspace{ 413 "sharded": { 414 Sharded: true, 415 Vindexes: map[string]*vschemapb.Vindex{ 416 "stfu1": { 417 Type: "stfu", 418 Params: map[string]string{ 419 "stfu1": "1"}, 420 Owner: "t1"}, 421 "stln1": { 422 Type: "stln", 423 Owner: "t1"}}, 424 Tables: map[string]*vschemapb.Table{ 425 "t1": { 426 ColumnVindexes: []*vschemapb.ColumnVindex{ 427 { 428 Column: "c1", 429 Name: "stfu1"}, { 430 Column: "c2", 431 Name: "stln1"}}}}}}} 432 433 got := BuildVSchema(&good) 434 err := got.Keyspaces["sharded"].Error 435 require.NoError(t, err) 436 437 t1, err := got.FindTable("sharded", "t1") 438 require.NoError(t, err) 439 440 vindex1 := &stFU{ 441 name: "stfu1", 442 Params: map[string]string{ 443 "stfu1": "1"}} 444 assertVindexMatches(t, t1.ColumnVindexes[0], vindex1, "stfu1", false) 445 446 vindex2 := &stLN{name: "stln1"} 447 assertVindexMatches(t, t1.ColumnVindexes[1], vindex2, "stln1", true) 448 449 assert.Equal(t, []string{"stln1", "stfu1"}, vindexNames(t1.Ordered)) 450 } 451 452 func TestShardedVSchemaOwnerInfo(t *testing.T) { 453 good := vschemapb.SrvVSchema{ 454 Keyspaces: map[string]*vschemapb.Keyspace{ 455 "sharded": { 456 Sharded: true, 457 Vindexes: map[string]*vschemapb.Vindex{ 458 "stfu": { 459 Type: "stfu", 460 }, 461 "stlo1": { 462 Type: "stlo", 463 Owner: "t1", 464 }, 465 "stlo2": { 466 Type: "stlo", 467 Owner: "t2", 468 }, 469 "stlo3": { 470 Type: "stlo", 471 Owner: "none", 472 }, 473 }, 474 Tables: map[string]*vschemapb.Table{ 475 "t1": { 476 ColumnVindexes: []*vschemapb.ColumnVindex{{ 477 Column: "id", 478 Name: "stfu", 479 }, { 480 Column: "c1", 481 Name: "stlo1", 482 }}, 483 }, 484 "t2": { 485 ColumnVindexes: []*vschemapb.ColumnVindex{{ 486 Column: "id", 487 Name: "stfu", 488 }, { 489 Columns: []string{"c1", "c2"}, 490 Name: "stlo2", 491 }}, 492 }, 493 "t3": { 494 ColumnVindexes: []*vschemapb.ColumnVindex{{ 495 Column: "id", 496 Name: "stfu", 497 }, { 498 Columns: []string{"c1", "c2"}, 499 Name: "stlo3", 500 }}, 501 }, 502 }, 503 }, 504 }, 505 } 506 got := BuildVSchema(&good) 507 err := got.Keyspaces["sharded"].Error 508 require.NoError(t, err) 509 results := []struct { 510 name string 511 keyspace string 512 table string 513 cols []string 514 }{{ 515 name: "stlo1", 516 keyspace: "sharded", 517 table: "t1", 518 cols: []string{"c1"}, 519 }, { 520 name: "stlo2", 521 keyspace: "sharded", 522 table: "t2", 523 cols: []string{"c1", "c2"}, 524 }, { 525 name: "stlo3", 526 keyspace: "", 527 table: "", 528 cols: nil, 529 }} 530 for _, want := range results { 531 var gotcols []string 532 vdx := got.Keyspaces["sharded"].Vindexes[want.name].(*stLO) 533 if vdx.table != want.table { 534 t.Errorf("Table(%s): %v, want %v", want.name, vdx.table, want.table) 535 } 536 if vdx.keyspace != want.keyspace { 537 t.Errorf("Keyspace(%s): %v, want %v", want.name, vdx.table, want.keyspace) 538 } 539 for _, col := range vdx.cols { 540 gotcols = append(gotcols, col.String()) 541 } 542 if !reflect.DeepEqual(gotcols, want.cols) { 543 t.Errorf("Cols(%s): %v, want %v", want.name, gotcols, want.cols) 544 } 545 } 546 } 547 548 func TestVSchemaRoutingRules(t *testing.T) { 549 input := vschemapb.SrvVSchema{ 550 RoutingRules: &vschemapb.RoutingRules{ 551 Rules: []*vschemapb.RoutingRule{{ 552 FromTable: "rt1", 553 ToTables: []string{"ks1.t1", "ks2.t2"}, 554 }, { 555 FromTable: "rt2", 556 ToTables: []string{"ks2.t2"}, 557 }, { 558 FromTable: "escaped", 559 ToTables: []string{"`ks2`.`t2`"}, 560 }, { 561 FromTable: "dup", 562 ToTables: []string{"ks1.t1"}, 563 }, { 564 FromTable: "dup", 565 ToTables: []string{"ks1.t1"}, 566 }, { 567 FromTable: "badname", 568 ToTables: []string{"t1.t2.t3"}, 569 }, { 570 FromTable: "unqualified", 571 ToTables: []string{"t1"}, 572 }, { 573 FromTable: "badkeyspace", 574 ToTables: []string{"ks3.t1"}, 575 }, { 576 FromTable: "notfound", 577 ToTables: []string{"ks1.t2"}, 578 }}, 579 }, 580 Keyspaces: map[string]*vschemapb.Keyspace{ 581 "ks1": { 582 Sharded: true, 583 Vindexes: map[string]*vschemapb.Vindex{ 584 "stfu1": { 585 Type: "stfu", 586 }, 587 }, 588 Tables: map[string]*vschemapb.Table{ 589 "t1": { 590 ColumnVindexes: []*vschemapb.ColumnVindex{ 591 { 592 Column: "c1", 593 Name: "stfu1", 594 }, 595 }, 596 }, 597 }, 598 }, 599 "ks2": { 600 Tables: map[string]*vschemapb.Table{ 601 "t2": {}, 602 }, 603 }, 604 }, 605 } 606 got := BuildVSchema(&input) 607 ks1 := &Keyspace{ 608 Name: "ks1", 609 Sharded: true, 610 } 611 ks2 := &Keyspace{ 612 Name: "ks2", 613 } 614 vindex1 := &stFU{ 615 name: "stfu1", 616 } 617 t1 := &Table{ 618 Name: sqlparser.NewIdentifierCS("t1"), 619 Keyspace: ks1, 620 ColumnVindexes: []*ColumnVindex{{ 621 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 622 Type: "stfu", 623 Name: "stfu1", 624 Vindex: vindex1, 625 isUnique: vindex1.IsUnique(), 626 cost: vindex1.Cost(), 627 }}, 628 } 629 t1.Ordered = []*ColumnVindex{ 630 t1.ColumnVindexes[0], 631 } 632 t2 := &Table{ 633 Name: sqlparser.NewIdentifierCS("t2"), 634 Keyspace: ks2, 635 } 636 dual1 := &Table{ 637 Name: sqlparser.NewIdentifierCS("dual"), 638 Keyspace: ks1, 639 Type: TypeReference, 640 } 641 dual2 := &Table{ 642 Name: sqlparser.NewIdentifierCS("dual"), 643 Keyspace: ks2, 644 Type: TypeReference, 645 } 646 want := &VSchema{ 647 RoutingRules: map[string]*RoutingRule{ 648 "rt1": { 649 Error: errors.New("table rt1 has more than one target: [ks1.t1 ks2.t2]"), 650 }, 651 "rt2": { 652 Tables: []*Table{t2}, 653 }, 654 "escaped": { 655 Tables: []*Table{t2}, 656 }, 657 "dup": { 658 Error: errors.New("duplicate rule for entry dup"), 659 }, 660 "badname": { 661 Error: errors.New("invalid table name: t1.t2.t3, it must be of the qualified form <keyspace_name>.<table_name> (dots are not allowed in either name)"), 662 }, 663 "unqualified": { 664 Error: errors.New("invalid table name: t1, it must be of the qualified form <keyspace_name>.<table_name> (dots are not allowed in either name)"), 665 }, 666 "badkeyspace": { 667 Error: errors.New("VT05003: unknown database 'ks3' in vschema"), 668 }, 669 "notfound": { 670 Error: errors.New("table t2 not found"), 671 }, 672 }, 673 globalTables: map[string]*Table{ 674 "t1": t1, 675 "t2": t2, 676 "dual": dual1, 677 }, 678 uniqueVindexes: map[string]Vindex{ 679 "stfu1": vindex1, 680 }, 681 Keyspaces: map[string]*KeyspaceSchema{ 682 "ks1": { 683 Keyspace: ks1, 684 Tables: map[string]*Table{ 685 "t1": t1, 686 "dual": dual1, 687 }, 688 Vindexes: map[string]Vindex{ 689 "stfu1": vindex1, 690 }, 691 }, 692 "ks2": { 693 Keyspace: ks2, 694 Tables: map[string]*Table{ 695 "t2": t2, 696 "dual": dual2, 697 }, 698 Vindexes: map[string]Vindex{}, 699 }, 700 }, 701 } 702 gotb, _ := json.MarshalIndent(got, "", " ") 703 wantb, _ := json.MarshalIndent(want, "", " ") 704 assert.Equal(t, string(wantb), string(gotb), string(gotb)) 705 } 706 707 func TestChooseVindexForType(t *testing.T) { 708 testcases := []struct { 709 in querypb.Type 710 out string 711 }{{ 712 in: sqltypes.Null, 713 out: "", 714 }, { 715 in: sqltypes.Int8, 716 out: "hash", 717 }, { 718 in: sqltypes.Uint8, 719 out: "hash", 720 }, { 721 in: sqltypes.Int16, 722 out: "hash", 723 }, { 724 in: sqltypes.Uint16, 725 out: "hash", 726 }, { 727 in: sqltypes.Int24, 728 out: "hash", 729 }, { 730 in: sqltypes.Uint24, 731 out: "hash", 732 }, { 733 in: sqltypes.Int32, 734 out: "hash", 735 }, { 736 in: sqltypes.Uint32, 737 out: "hash", 738 }, { 739 in: sqltypes.Int64, 740 out: "hash", 741 }, { 742 in: sqltypes.Uint64, 743 out: "hash", 744 }, { 745 in: sqltypes.Float32, 746 out: "hash", 747 }, { 748 in: sqltypes.Float64, 749 out: "", 750 }, { 751 in: sqltypes.Timestamp, 752 out: "", 753 }, { 754 in: sqltypes.Date, 755 out: "", 756 }, { 757 in: sqltypes.Time, 758 out: "", 759 }, { 760 in: sqltypes.Datetime, 761 out: "", 762 }, { 763 in: sqltypes.Year, 764 out: "hash", 765 }, { 766 in: sqltypes.Decimal, 767 out: "", 768 }, { 769 in: sqltypes.Text, 770 out: "unicode_loose_md5", 771 }, { 772 in: sqltypes.Blob, 773 out: "binary_md5", 774 }, { 775 in: sqltypes.VarChar, 776 out: "unicode_loose_md5", 777 }, { 778 in: sqltypes.VarBinary, 779 out: "binary_md5", 780 }, { 781 in: sqltypes.Char, 782 out: "unicode_loose_md5", 783 }, { 784 in: sqltypes.Binary, 785 out: "binary_md5", 786 }, { 787 in: sqltypes.Bit, 788 out: "", 789 }, { 790 in: sqltypes.Enum, 791 out: "", 792 }, { 793 in: sqltypes.Set, 794 out: "", 795 }, { 796 in: sqltypes.Geometry, 797 out: "", 798 }, { 799 in: sqltypes.TypeJSON, 800 out: "", 801 }, { 802 in: sqltypes.Expression, 803 out: "", 804 }} 805 806 for _, tcase := range testcases { 807 out, err := ChooseVindexForType(tcase.in) 808 if out == "" { 809 assert.Error(t, err, tcase.in) 810 continue 811 } 812 assert.Equal(t, out, tcase.out, tcase.in) 813 } 814 } 815 816 func TestFindBestColVindex(t *testing.T) { 817 testSrvVSchema := &vschemapb.SrvVSchema{ 818 Keyspaces: map[string]*vschemapb.Keyspace{ 819 "ks1": { 820 Sharded: true, 821 Vindexes: map[string]*vschemapb.Vindex{ 822 "stfu": { 823 Type: "stfu"}, 824 "stfn": { 825 Type: "stfn"}, 826 "stlu": { 827 Type: "stlu"}, 828 "stln": { 829 Type: "stln"}, 830 "cheap": { 831 Type: "cheap"}, 832 }, 833 Tables: map[string]*vschemapb.Table{ 834 "t1": { 835 ColumnVindexes: []*vschemapb.ColumnVindex{{ 836 Name: "stfu", 837 Columns: []string{"id"}}}}, 838 "nogoodvindex": { 839 ColumnVindexes: []*vschemapb.ColumnVindex{{ 840 Name: "stlu", 841 Columns: []string{"id"}}}}, 842 "thirdvindexgood": { 843 ColumnVindexes: []*vschemapb.ColumnVindex{{ 844 Name: "stlu", 845 Columns: []string{"id"}}, { 846 Name: "stfn", 847 Columns: []string{"id"}}, { 848 Name: "stfu", 849 Columns: []string{"id"}}}}, 850 "cheapest": { 851 ColumnVindexes: []*vschemapb.ColumnVindex{{ 852 Name: "stfu", 853 Columns: []string{"id"}}, { 854 Name: "cheap", 855 Columns: []string{"id"}}}}}}, 856 "unsharded": { 857 Tables: map[string]*vschemapb.Table{ 858 "t2": {}}}}} 859 860 vs := BuildVSchema(testSrvVSchema) 861 862 testcases := []struct { 863 tablename string 864 vindexname string 865 err string 866 }{{ 867 tablename: "t1", 868 vindexname: "stfu", 869 }, { 870 tablename: "nogoodvindex", 871 err: "could not find a vindex to compute keyspace id for table nogoodvindex", 872 }, { 873 tablename: "thirdvindexgood", 874 vindexname: "stfu", 875 }, { 876 tablename: "cheapest", 877 vindexname: "cheap", 878 }, { 879 tablename: "t2", 880 err: "table t2 has no vindex", 881 }} 882 for _, tcase := range testcases { 883 table, err := vs.FindTable("", tcase.tablename) 884 require.NoError(t, err) 885 cv, err := FindBestColVindex(table) 886 if err != nil { 887 assert.EqualError(t, err, tcase.err, tcase.tablename) 888 continue 889 } 890 assert.NoError(t, err, tcase.tablename) 891 assert.Equal(t, cv.Name, tcase.vindexname, tcase.tablename) 892 } 893 } 894 895 func TestFindVindexForSharding(t *testing.T) { 896 ks := &Keyspace{ 897 Name: "sharded", 898 Sharded: true, 899 } 900 vindex1 := &stFU{ 901 name: "stfu1", 902 Params: map[string]string{ 903 "stfu1": "1", 904 }, 905 } 906 vindex2 := &stLN{name: "stln1"} 907 t1 := &Table{ 908 Name: sqlparser.NewIdentifierCS("t1"), 909 Keyspace: ks, 910 ColumnVindexes: []*ColumnVindex{ 911 { 912 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 913 Type: "stfu", 914 Name: "stfu1", 915 Vindex: vindex1, 916 isUnique: vindex1.IsUnique(), 917 cost: vindex1.Cost(), 918 }, 919 { 920 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c2")}, 921 Type: "stln", 922 Name: "stln1", 923 Owned: true, 924 Vindex: vindex2, 925 isUnique: vindex2.IsUnique(), 926 cost: vindex2.Cost(), 927 }, 928 }, 929 } 930 res, err := FindVindexForSharding(t1.Name.String(), t1.ColumnVindexes) 931 require.NoError(t, err) 932 if !reflect.DeepEqual(res, t1.ColumnVindexes[0]) { 933 t.Errorf("FindVindexForSharding:\n got\n%v, want\n%v", res, t1.ColumnVindexes[0]) 934 } 935 } 936 937 func TestFindVindexForShardingError(t *testing.T) { 938 ks := &Keyspace{ 939 Name: "sharded", 940 Sharded: true, 941 } 942 vindex1 := &stLU{name: "stlu1"} 943 vindex2 := &stLN{name: "stln1"} 944 t1 := &Table{ 945 Name: sqlparser.NewIdentifierCS("t1"), 946 Keyspace: ks, 947 ColumnVindexes: []*ColumnVindex{ 948 { 949 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 950 Type: "stlu", 951 Name: "stlu1", 952 Vindex: vindex1, 953 isUnique: vindex1.IsUnique(), 954 cost: vindex1.Cost(), 955 }, 956 { 957 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c2")}, 958 Type: "stln", 959 Name: "stln1", 960 Owned: true, 961 Vindex: vindex2, 962 isUnique: vindex2.IsUnique(), 963 cost: vindex2.Cost(), 964 }, 965 }, 966 } 967 res, err := FindVindexForSharding(t1.Name.String(), t1.ColumnVindexes) 968 want := `could not find a vindex to use for sharding table t1` 969 if err == nil || err.Error() != want { 970 t.Errorf("FindVindexForSharding: %v, want %v", err, want) 971 } 972 if res != nil { 973 t.Errorf("FindVindexForSharding:\n got\n%v, want\n%v", res, nil) 974 } 975 } 976 977 func TestFindVindexForSharding2(t *testing.T) { 978 ks := &Keyspace{ 979 Name: "sharded", 980 Sharded: true, 981 } 982 vindex1 := &stLU{name: "stlu1"} 983 vindex2 := &stFU{ 984 name: "stfu1", 985 Params: map[string]string{ 986 "stfu1": "1", 987 }, 988 } 989 t1 := &Table{ 990 Name: sqlparser.NewIdentifierCS("t1"), 991 Keyspace: ks, 992 ColumnVindexes: []*ColumnVindex{ 993 { 994 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 995 Type: "stlu", 996 Name: "stlu1", 997 Vindex: vindex1, 998 isUnique: vindex1.IsUnique(), 999 cost: vindex1.Cost(), 1000 }, 1001 { 1002 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c2")}, 1003 Type: "stfu", 1004 Name: "stfu1", 1005 Owned: true, 1006 Vindex: vindex2, 1007 isUnique: vindex2.IsUnique(), 1008 cost: vindex2.Cost(), 1009 }, 1010 }, 1011 } 1012 res, err := FindVindexForSharding(t1.Name.String(), t1.ColumnVindexes) 1013 require.NoError(t, err) 1014 if !reflect.DeepEqual(res, t1.ColumnVindexes[1]) { 1015 t.Errorf("FindVindexForSharding:\n got\n%v, want\n%v", res, t1.ColumnVindexes[1]) 1016 } 1017 } 1018 1019 func TestShardedVSchemaMultiColumnVindex(t *testing.T) { 1020 good := vschemapb.SrvVSchema{ 1021 Keyspaces: map[string]*vschemapb.Keyspace{ 1022 "sharded": { 1023 Sharded: true, 1024 Vindexes: map[string]*vschemapb.Vindex{ 1025 "stfu1": { 1026 Type: "stfu", 1027 Params: map[string]string{ 1028 "stfu1": "1"}, 1029 Owner: "t1"}}, 1030 Tables: map[string]*vschemapb.Table{ 1031 "t1": { 1032 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1033 Columns: []string{"c1", "c2"}, 1034 Name: "stfu1"}}}}}}} 1035 1036 got := BuildVSchema(&good) 1037 err := got.Keyspaces["sharded"].Error 1038 require.NoError(t, err) 1039 ks := &Keyspace{ 1040 Name: "sharded", 1041 Sharded: true, 1042 } 1043 vindex1 := &stFU{ 1044 name: "stfu1", 1045 Params: map[string]string{ 1046 "stfu1": "1", 1047 }, 1048 } 1049 t1 := &Table{ 1050 Name: sqlparser.NewIdentifierCS("t1"), 1051 Keyspace: ks, 1052 ColumnVindexes: []*ColumnVindex{ 1053 { 1054 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1"), sqlparser.NewIdentifierCI("c2")}, 1055 Type: "stfu", 1056 Name: "stfu1", 1057 Vindex: vindex1, 1058 isUnique: vindex1.IsUnique(), 1059 cost: vindex1.Cost(), 1060 }, 1061 }, 1062 } 1063 t1.Ordered = []*ColumnVindex{ 1064 t1.ColumnVindexes[0], 1065 } 1066 dual := &Table{ 1067 Name: sqlparser.NewIdentifierCS("dual"), 1068 Keyspace: ks, 1069 Type: TypeReference, 1070 } 1071 want := &VSchema{ 1072 RoutingRules: map[string]*RoutingRule{}, 1073 globalTables: map[string]*Table{ 1074 "t1": t1, 1075 "dual": dual, 1076 }, 1077 uniqueVindexes: map[string]Vindex{ 1078 "stfu1": vindex1, 1079 }, 1080 Keyspaces: map[string]*KeyspaceSchema{ 1081 "sharded": { 1082 Keyspace: ks, 1083 Tables: map[string]*Table{ 1084 "t1": t1, 1085 "dual": dual}, 1086 Vindexes: map[string]Vindex{ 1087 "stfu1": vindex1}, 1088 }}} 1089 if !reflect.DeepEqual(got, want) { 1090 gotjson, _ := json.Marshal(got) 1091 wantjson, _ := json.Marshal(want) 1092 t.Errorf("BuildVSchema:\n%s, want\n%s", gotjson, wantjson) 1093 } 1094 } 1095 1096 func TestShardedVSchemaNotOwned(t *testing.T) { 1097 good := vschemapb.SrvVSchema{ 1098 Keyspaces: map[string]*vschemapb.Keyspace{ 1099 "sharded": { 1100 Sharded: true, 1101 Vindexes: map[string]*vschemapb.Vindex{ 1102 "stlu1": { 1103 Type: "stlu", 1104 Owner: ""}, 1105 "stfu1": { 1106 Type: "stfu", 1107 Owner: ""}}, 1108 Tables: map[string]*vschemapb.Table{ 1109 "t1": { 1110 ColumnVindexes: []*vschemapb.ColumnVindex{{ 1111 Column: "c1", 1112 Name: "stlu1"}, { 1113 Column: "c2", 1114 Name: "stfu1"}}}}}}} 1115 got := BuildVSchema(&good) 1116 err := got.Keyspaces["sharded"].Error 1117 require.NoError(t, err) 1118 ks := &Keyspace{ 1119 Name: "sharded", 1120 Sharded: true, 1121 } 1122 vindex1 := &stLU{name: "stlu1"} 1123 vindex2 := &stFU{name: "stfu1"} 1124 t1 := &Table{ 1125 Name: sqlparser.NewIdentifierCS("t1"), 1126 Keyspace: ks, 1127 ColumnVindexes: []*ColumnVindex{ 1128 { 1129 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 1130 Type: "stlu", 1131 Name: "stlu1", 1132 Owned: false, 1133 Vindex: vindex1, 1134 isUnique: vindex1.IsUnique(), 1135 cost: vindex1.Cost(), 1136 }, { 1137 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c2")}, 1138 Type: "stfu", 1139 Name: "stfu1", 1140 Owned: false, 1141 Vindex: vindex2, 1142 isUnique: vindex2.IsUnique(), 1143 cost: vindex2.Cost()}}} 1144 t1.Ordered = []*ColumnVindex{ 1145 t1.ColumnVindexes[1], 1146 t1.ColumnVindexes[0]} 1147 dual := &Table{ 1148 Name: sqlparser.NewIdentifierCS("dual"), 1149 Keyspace: ks, 1150 Type: TypeReference} 1151 want := &VSchema{ 1152 RoutingRules: map[string]*RoutingRule{}, 1153 globalTables: map[string]*Table{ 1154 "t1": t1, 1155 "dual": dual}, 1156 uniqueVindexes: map[string]Vindex{ 1157 "stlu1": vindex1, 1158 "stfu1": vindex2}, 1159 Keyspaces: map[string]*KeyspaceSchema{ 1160 "sharded": { 1161 Keyspace: ks, 1162 Tables: map[string]*Table{ 1163 "t1": t1, 1164 "dual": dual}, 1165 Vindexes: map[string]Vindex{ 1166 "stlu1": vindex1, 1167 "stfu1": vindex2}, 1168 }}} 1169 if !reflect.DeepEqual(got, want) { 1170 gotjson, _ := json.Marshal(got) 1171 wantjson, _ := json.Marshal(want) 1172 t.Errorf("BuildVSchema:s\n%s, want\n%s", gotjson, wantjson) 1173 } 1174 } 1175 1176 func TestBuildVSchemaVindexNotFoundFail(t *testing.T) { 1177 bad := vschemapb.SrvVSchema{ 1178 Keyspaces: map[string]*vschemapb.Keyspace{ 1179 "sharded": { 1180 Sharded: true, 1181 Vindexes: map[string]*vschemapb.Vindex{ 1182 "noexist": { 1183 Type: "noexist", 1184 }, 1185 }, 1186 Tables: map[string]*vschemapb.Table{ 1187 "t1": { 1188 ColumnVindexes: []*vschemapb.ColumnVindex{ 1189 { 1190 Column: "c1", 1191 Name: "noexist", 1192 }, 1193 }, 1194 }, 1195 }, 1196 }, 1197 }, 1198 } 1199 got := BuildVSchema(&bad) 1200 err := got.Keyspaces["sharded"].Error 1201 want := `vindexType "noexist" not found` 1202 if err == nil || err.Error() != want { 1203 t.Errorf("BuildVSchema: %v, want %v", err, want) 1204 } 1205 } 1206 1207 func TestBuildVSchemaNoColumnVindexFail(t *testing.T) { 1208 bad := vschemapb.SrvVSchema{ 1209 Keyspaces: map[string]*vschemapb.Keyspace{ 1210 "sharded": { 1211 Sharded: true, 1212 Vindexes: map[string]*vschemapb.Vindex{ 1213 "stfu": { 1214 Type: "stfu", 1215 }, 1216 }, 1217 Tables: map[string]*vschemapb.Table{ 1218 "t1": {}, 1219 }, 1220 }, 1221 }, 1222 } 1223 got := BuildVSchema(&bad) 1224 err := got.Keyspaces["sharded"].Error 1225 want := "missing primary col vindex for table: t1" 1226 if err == nil || err.Error() != want { 1227 t.Errorf("BuildVSchema: %v, want %v", err, want) 1228 } 1229 } 1230 1231 func TestBuildVSchemaDupSeq(t *testing.T) { 1232 good := vschemapb.SrvVSchema{ 1233 Keyspaces: map[string]*vschemapb.Keyspace{ 1234 "ksa": { 1235 Tables: map[string]*vschemapb.Table{ 1236 "t1": { 1237 Type: "sequence"}}}, 1238 "ksb": { 1239 Tables: map[string]*vschemapb.Table{ 1240 "t1": { 1241 Type: "sequence"}}}}} 1242 ksa := &Keyspace{ 1243 Name: "ksa"} 1244 ksb := &Keyspace{ 1245 Name: "ksb"} 1246 got := BuildVSchema(&good) 1247 t1a := &Table{ 1248 Name: sqlparser.NewIdentifierCS("t1"), 1249 Keyspace: ksa, 1250 Type: "sequence"} 1251 t1b := &Table{ 1252 Name: sqlparser.NewIdentifierCS("t1"), 1253 Keyspace: ksb, 1254 Type: "sequence"} 1255 duala := &Table{ 1256 Name: sqlparser.NewIdentifierCS("dual"), 1257 Keyspace: ksa, 1258 Type: TypeReference} 1259 dualb := &Table{ 1260 Name: sqlparser.NewIdentifierCS("dual"), 1261 Keyspace: ksb, 1262 Type: TypeReference} 1263 want := &VSchema{ 1264 RoutingRules: map[string]*RoutingRule{}, 1265 globalTables: map[string]*Table{ 1266 "t1": nil, 1267 "dual": duala}, 1268 uniqueVindexes: map[string]Vindex{}, 1269 Keyspaces: map[string]*KeyspaceSchema{ 1270 "ksa": { 1271 Keyspace: ksa, 1272 Tables: map[string]*Table{ 1273 "t1": t1a, 1274 "dual": duala}, 1275 Vindexes: map[string]Vindex{}, 1276 }, 1277 "ksb": { 1278 Keyspace: ksb, 1279 Tables: map[string]*Table{ 1280 "t1": t1b, 1281 "dual": dualb}, 1282 Vindexes: map[string]Vindex{}}}} 1283 if !reflect.DeepEqual(got, want) { 1284 gotjson, _ := json.Marshal(got) 1285 wantjson, _ := json.Marshal(want) 1286 t.Errorf("BuildVSchema:\n%s, want\n%s", gotjson, wantjson) 1287 } 1288 } 1289 1290 func TestBuildVSchemaDupTable(t *testing.T) { 1291 good := vschemapb.SrvVSchema{ 1292 Keyspaces: map[string]*vschemapb.Keyspace{ 1293 "ksa": { 1294 Tables: map[string]*vschemapb.Table{ 1295 "t1": {}, 1296 }, 1297 }, 1298 "ksb": { 1299 Tables: map[string]*vschemapb.Table{ 1300 "t1": {}, 1301 }, 1302 }, 1303 }, 1304 } 1305 got := BuildVSchema(&good) 1306 ksa := &Keyspace{ 1307 Name: "ksa", 1308 } 1309 t1a := &Table{ 1310 Name: sqlparser.NewIdentifierCS("t1"), 1311 Keyspace: ksa, 1312 } 1313 ksb := &Keyspace{ 1314 Name: "ksb", 1315 } 1316 t1b := &Table{ 1317 Name: sqlparser.NewIdentifierCS("t1"), 1318 Keyspace: ksb, 1319 } 1320 duala := &Table{ 1321 Name: sqlparser.NewIdentifierCS("dual"), 1322 Keyspace: ksa, 1323 Type: TypeReference, 1324 } 1325 dualb := &Table{ 1326 Name: sqlparser.NewIdentifierCS("dual"), 1327 Keyspace: ksb, 1328 Type: TypeReference, 1329 } 1330 want := &VSchema{ 1331 RoutingRules: map[string]*RoutingRule{}, 1332 globalTables: map[string]*Table{ 1333 "t1": nil, 1334 "dual": duala, 1335 }, 1336 uniqueVindexes: map[string]Vindex{}, 1337 Keyspaces: map[string]*KeyspaceSchema{ 1338 "ksa": { 1339 Keyspace: ksa, 1340 Tables: map[string]*Table{ 1341 "t1": t1a, 1342 "dual": duala, 1343 }, 1344 Vindexes: map[string]Vindex{}, 1345 }, 1346 "ksb": { 1347 Keyspace: ksb, 1348 Tables: map[string]*Table{ 1349 "t1": t1b, 1350 "dual": dualb, 1351 }, 1352 Vindexes: map[string]Vindex{}, 1353 }, 1354 }, 1355 } 1356 if !reflect.DeepEqual(got, want) { 1357 gotjson, _ := json.Marshal(got) 1358 wantjson, _ := json.Marshal(want) 1359 t.Errorf("BuildVSchema:\n%s, want\n%s", gotjson, wantjson) 1360 } 1361 } 1362 1363 func TestBuildVSchemaDupVindex(t *testing.T) { 1364 good := vschemapb.SrvVSchema{ 1365 Keyspaces: map[string]*vschemapb.Keyspace{ 1366 "ksa": { 1367 Sharded: true, 1368 Vindexes: map[string]*vschemapb.Vindex{ 1369 "stlu1": { 1370 Type: "stlu", 1371 Owner: "", 1372 }, 1373 }, 1374 Tables: map[string]*vschemapb.Table{ 1375 "t1": { 1376 ColumnVindexes: []*vschemapb.ColumnVindex{ 1377 { 1378 Column: "c1", 1379 Name: "stlu1", 1380 }, 1381 }, 1382 }, 1383 }, 1384 }, 1385 "ksb": { 1386 Sharded: true, 1387 Vindexes: map[string]*vschemapb.Vindex{ 1388 "stlu1": { 1389 Type: "stlu", 1390 Owner: "", 1391 }, 1392 }, 1393 Tables: map[string]*vschemapb.Table{ 1394 "t1": { 1395 ColumnVindexes: []*vschemapb.ColumnVindex{ 1396 { 1397 Column: "c1", 1398 Name: "stlu1", 1399 }, 1400 }, 1401 }, 1402 }, 1403 }, 1404 }, 1405 } 1406 got := BuildVSchema(&good) 1407 err := got.Keyspaces["ksa"].Error 1408 err1 := got.Keyspaces["ksb"].Error 1409 require.NoError(t, err) 1410 if err1 != nil { 1411 t.Error(err1) 1412 } 1413 ksa := &Keyspace{ 1414 Name: "ksa", 1415 Sharded: true, 1416 } 1417 ksb := &Keyspace{ 1418 Name: "ksb", 1419 Sharded: true, 1420 } 1421 vindex1 := &stLU{name: "stlu1"} 1422 t1 := &Table{ 1423 Name: sqlparser.NewIdentifierCS("t1"), 1424 Keyspace: ksa, 1425 ColumnVindexes: []*ColumnVindex{ 1426 { 1427 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 1428 Type: "stlu", 1429 Name: "stlu1", 1430 Owned: false, 1431 Vindex: vindex1, 1432 isUnique: vindex1.IsUnique(), 1433 cost: vindex1.Cost(), 1434 }, 1435 }, 1436 } 1437 t1.Ordered = []*ColumnVindex{ 1438 t1.ColumnVindexes[0], 1439 } 1440 t2 := &Table{ 1441 Name: sqlparser.NewIdentifierCS("t1"), 1442 Keyspace: ksb, 1443 ColumnVindexes: []*ColumnVindex{ 1444 { 1445 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 1446 Type: "stlu", 1447 Name: "stlu1", 1448 Owned: false, 1449 Vindex: vindex1, 1450 isUnique: vindex1.IsUnique(), 1451 cost: vindex1.Cost(), 1452 }, 1453 }, 1454 } 1455 t2.Ordered = []*ColumnVindex{ 1456 t2.ColumnVindexes[0], 1457 } 1458 duala := &Table{ 1459 Name: sqlparser.NewIdentifierCS("dual"), 1460 Keyspace: ksa, 1461 Type: TypeReference, 1462 } 1463 dualb := &Table{ 1464 Name: sqlparser.NewIdentifierCS("dual"), 1465 Keyspace: ksb, 1466 Type: TypeReference, 1467 } 1468 want := &VSchema{ 1469 RoutingRules: map[string]*RoutingRule{}, 1470 globalTables: map[string]*Table{ 1471 "t1": nil, 1472 "dual": duala, 1473 }, 1474 uniqueVindexes: map[string]Vindex{ 1475 "stlu1": nil, 1476 }, 1477 Keyspaces: map[string]*KeyspaceSchema{ 1478 "ksa": { 1479 Keyspace: ksa, 1480 Tables: map[string]*Table{ 1481 "t1": t1, 1482 "dual": duala, 1483 }, 1484 Vindexes: map[string]Vindex{ 1485 "stlu1": vindex1, 1486 }, 1487 }, 1488 "ksb": { 1489 Keyspace: ksb, 1490 Tables: map[string]*Table{ 1491 "t1": t2, 1492 "dual": dualb, 1493 }, 1494 Vindexes: map[string]Vindex{ 1495 "stlu1": vindex1, 1496 }, 1497 }, 1498 }, 1499 } 1500 if !reflect.DeepEqual(got, want) { 1501 gotjson, _ := json.Marshal(got) 1502 wantjson, _ := json.Marshal(want) 1503 t.Errorf("BuildVSchema:\n%s, want\n%s", gotjson, wantjson) 1504 } 1505 } 1506 1507 func TestBuildVSchemaNoindexFail(t *testing.T) { 1508 bad := vschemapb.SrvVSchema{ 1509 Keyspaces: map[string]*vschemapb.Keyspace{ 1510 "sharded": { 1511 Sharded: true, 1512 Vindexes: map[string]*vschemapb.Vindex{ 1513 "stfu": { 1514 Type: "stfu", 1515 }, 1516 }, 1517 Tables: map[string]*vschemapb.Table{ 1518 "t1": { 1519 ColumnVindexes: []*vschemapb.ColumnVindex{ 1520 { 1521 Column: "c1", 1522 Name: "notexist", 1523 }, 1524 }, 1525 }, 1526 }, 1527 }, 1528 }, 1529 } 1530 got := BuildVSchema(&bad) 1531 err := got.Keyspaces["sharded"].Error 1532 want := "vindex notexist not found for table t1" 1533 if err == nil || err.Error() != want { 1534 t.Errorf("BuildVSchema: %v, want %v", err, want) 1535 } 1536 } 1537 1538 func TestBuildVSchemaColumnAndColumnsFail(t *testing.T) { 1539 bad := vschemapb.SrvVSchema{ 1540 Keyspaces: map[string]*vschemapb.Keyspace{ 1541 "sharded": { 1542 Sharded: true, 1543 Vindexes: map[string]*vschemapb.Vindex{ 1544 "stfu": { 1545 Type: "stfu", 1546 }, 1547 }, 1548 Tables: map[string]*vschemapb.Table{ 1549 "t1": { 1550 ColumnVindexes: []*vschemapb.ColumnVindex{ 1551 { 1552 Column: "c1", 1553 Columns: []string{"c2", "c3"}, 1554 Name: "stfu", 1555 }, 1556 }, 1557 }, 1558 }, 1559 }, 1560 }, 1561 } 1562 got := BuildVSchema(&bad) 1563 err := got.Keyspaces["sharded"].Error 1564 want := `can't use column and columns at the same time in vindex (stfu) and table (t1)` 1565 if err == nil || err.Error() != want { 1566 t.Errorf("BuildVSchema: %v, want %v", err, want) 1567 } 1568 } 1569 1570 func TestBuildVSchemaNoColumnsFail(t *testing.T) { 1571 bad := vschemapb.SrvVSchema{ 1572 Keyspaces: map[string]*vschemapb.Keyspace{ 1573 "sharded": { 1574 Sharded: true, 1575 Vindexes: map[string]*vschemapb.Vindex{ 1576 "stfu": { 1577 Type: "stfu", 1578 }, 1579 }, 1580 Tables: map[string]*vschemapb.Table{ 1581 "t1": { 1582 ColumnVindexes: []*vschemapb.ColumnVindex{ 1583 { 1584 Name: "stfu", 1585 }, 1586 }, 1587 }, 1588 }, 1589 }, 1590 }, 1591 } 1592 got := BuildVSchema(&bad) 1593 err := got.Keyspaces["sharded"].Error 1594 want := `must specify at least one column for vindex (stfu) and table (t1)` 1595 if err == nil || err.Error() != want { 1596 t.Errorf("BuildVSchema: %v, want %v", err, want) 1597 } 1598 } 1599 1600 func TestBuildVSchemaNotUniqueFail(t *testing.T) { 1601 bad := vschemapb.SrvVSchema{ 1602 Keyspaces: map[string]*vschemapb.Keyspace{ 1603 "sharded": { 1604 Sharded: true, 1605 Vindexes: map[string]*vschemapb.Vindex{ 1606 "stln": { 1607 Type: "stln", 1608 }, 1609 }, 1610 Tables: map[string]*vschemapb.Table{ 1611 "t1": { 1612 ColumnVindexes: []*vschemapb.ColumnVindex{ 1613 { 1614 Column: "c1", 1615 Name: "stln", 1616 }, 1617 }, 1618 }, 1619 }, 1620 }, 1621 }, 1622 } 1623 got := BuildVSchema(&bad) 1624 err := got.Keyspaces["sharded"].Error 1625 want := "primary vindex stln is not Unique for table t1" 1626 if err == nil || err.Error() != want { 1627 t.Errorf("BuildVSchema: %v, want %v", err, want) 1628 } 1629 } 1630 1631 func TestBuildVSchemaPrimaryCannotBeOwned(t *testing.T) { 1632 bad := vschemapb.SrvVSchema{ 1633 Keyspaces: map[string]*vschemapb.Keyspace{ 1634 "sharded": { 1635 Sharded: true, 1636 Vindexes: map[string]*vschemapb.Vindex{ 1637 "stlu": { 1638 Type: "stlu", 1639 Owner: "t1", 1640 }, 1641 }, 1642 Tables: map[string]*vschemapb.Table{ 1643 "t1": { 1644 ColumnVindexes: []*vschemapb.ColumnVindex{ 1645 { 1646 Column: "c1", 1647 Name: "stlu", 1648 }, 1649 }, 1650 }, 1651 }, 1652 }, 1653 }, 1654 } 1655 got := BuildVSchema(&bad) 1656 err := got.Keyspaces["sharded"].Error 1657 want := "primary vindex stlu cannot be owned for table t1" 1658 if err == nil || err.Error() != want { 1659 t.Errorf("BuildVSchema: %v, want %v", err, want) 1660 } 1661 } 1662 1663 func TestBuildVSchemaReferenceTableSourceMustBeQualified(t *testing.T) { 1664 input := vschemapb.SrvVSchema{ 1665 Keyspaces: map[string]*vschemapb.Keyspace{ 1666 "unsharded": { 1667 Sharded: false, 1668 Tables: map[string]*vschemapb.Table{ 1669 "src": {}, 1670 }, 1671 }, 1672 "sharded": { 1673 Sharded: true, 1674 Tables: map[string]*vschemapb.Table{ 1675 "ref": { 1676 Type: "reference", 1677 Source: "src", 1678 }, 1679 }, 1680 }, 1681 }, 1682 } 1683 vschema := BuildVSchema(&input) 1684 require.NoError(t, vschema.Keyspaces["unsharded"].Error) 1685 require.Error(t, vschema.Keyspaces["sharded"].Error) 1686 require.EqualError(t, vschema.Keyspaces["sharded"].Error, 1687 "invalid source \"src\" for reference table: ref; invalid table name: src, it must be of the qualified form <keyspace_name>.<table_name> (dots are not allowed in either name)") 1688 } 1689 1690 func TestBuildVSchemaReferenceTableSourceMustBeInDifferentKeyspace(t *testing.T) { 1691 input := vschemapb.SrvVSchema{ 1692 Keyspaces: map[string]*vschemapb.Keyspace{ 1693 "sharded": { 1694 Sharded: true, 1695 Vindexes: map[string]*vschemapb.Vindex{ 1696 "hash": { 1697 Type: "binary_md5", 1698 }, 1699 }, 1700 Tables: map[string]*vschemapb.Table{ 1701 "ref": { 1702 Type: "reference", 1703 Source: "sharded.src", 1704 }, 1705 "src": { 1706 ColumnVindexes: []*vschemapb.ColumnVindex{ 1707 { 1708 Column: "c1", 1709 Name: "hash", 1710 }, 1711 }, 1712 }, 1713 }, 1714 }, 1715 }, 1716 } 1717 vschema := BuildVSchema(&input) 1718 require.Error(t, vschema.Keyspaces["sharded"].Error) 1719 require.EqualError(t, vschema.Keyspaces["sharded"].Error, 1720 "source \"sharded.src\" may not reference a table in the same keyspace as table: ref") 1721 } 1722 1723 func TestBuildVSchemaReferenceTableSourceKeyspaceMustExist(t *testing.T) { 1724 input := vschemapb.SrvVSchema{ 1725 Keyspaces: map[string]*vschemapb.Keyspace{ 1726 "sharded": { 1727 Sharded: true, 1728 Tables: map[string]*vschemapb.Table{ 1729 "ref": { 1730 Type: "reference", 1731 Source: "unsharded.src", 1732 }, 1733 }, 1734 }, 1735 }, 1736 } 1737 vschema := BuildVSchema(&input) 1738 require.Error(t, vschema.Keyspaces["sharded"].Error) 1739 require.EqualError(t, vschema.Keyspaces["sharded"].Error, 1740 "source \"unsharded.src\" references a non-existent keyspace \"unsharded\"") 1741 } 1742 1743 func TestBuildVSchemaReferenceTableSourceTableMustExist(t *testing.T) { 1744 input := vschemapb.SrvVSchema{ 1745 Keyspaces: map[string]*vschemapb.Keyspace{ 1746 "unsharded": { 1747 Sharded: false, 1748 Tables: map[string]*vschemapb.Table{ 1749 "foo": {}, 1750 }, 1751 }, 1752 "sharded": { 1753 Sharded: true, 1754 Tables: map[string]*vschemapb.Table{ 1755 "ref": { 1756 Type: "reference", 1757 Source: "unsharded.src", 1758 }, 1759 }, 1760 }, 1761 }, 1762 } 1763 vschema := BuildVSchema(&input) 1764 require.Error(t, vschema.Keyspaces["sharded"].Error) 1765 require.EqualError(t, vschema.Keyspaces["sharded"].Error, 1766 "source \"unsharded.src\" references a table \"src\" that is not present in the VSchema of keyspace \"unsharded\"") 1767 } 1768 1769 func TestBuildVSchemaReferenceTableSourceMayUseShardedKeyspace(t *testing.T) { 1770 input := vschemapb.SrvVSchema{ 1771 Keyspaces: map[string]*vschemapb.Keyspace{ 1772 "sharded1": { 1773 Sharded: true, 1774 Vindexes: map[string]*vschemapb.Vindex{ 1775 "hash": { 1776 Type: "binary_md5", 1777 }, 1778 }, 1779 Tables: map[string]*vschemapb.Table{ 1780 "src": { 1781 ColumnVindexes: []*vschemapb.ColumnVindex{ 1782 { 1783 Column: "c1", 1784 Name: "hash", 1785 }, 1786 }, 1787 }, 1788 }, 1789 }, 1790 "sharded2": { 1791 Sharded: true, 1792 Tables: map[string]*vschemapb.Table{ 1793 "ref": { 1794 Type: "reference", 1795 Source: "sharded1.src", 1796 }, 1797 }, 1798 }, 1799 }, 1800 } 1801 vschema := BuildVSchema(&input) 1802 require.NoError(t, vschema.Keyspaces["sharded1"].Error) 1803 require.NoError(t, vschema.Keyspaces["sharded2"].Error) 1804 } 1805 1806 func TestBuildVSchemaReferenceTableSourceTableMustBeBasicOrReference(t *testing.T) { 1807 input := vschemapb.SrvVSchema{ 1808 Keyspaces: map[string]*vschemapb.Keyspace{ 1809 "unsharded1": { 1810 Sharded: false, 1811 Tables: map[string]*vschemapb.Table{ 1812 "src1": { 1813 Type: "sequence", 1814 }, 1815 }, 1816 }, 1817 "unsharded2": { 1818 Sharded: false, 1819 Tables: map[string]*vschemapb.Table{ 1820 "src2": { 1821 Type: "reference", 1822 Source: "unsharded1.src1", 1823 }, 1824 }, 1825 }, 1826 "unsharded3": { 1827 Tables: map[string]*vschemapb.Table{ 1828 "src3": { 1829 Type: "reference", 1830 }, 1831 }, 1832 }, 1833 "sharded1": { 1834 Sharded: true, 1835 Tables: map[string]*vschemapb.Table{ 1836 "ref1": { 1837 Type: "reference", 1838 Source: "unsharded1.src1", 1839 }, 1840 }, 1841 }, 1842 "sharded2": { 1843 Sharded: true, 1844 Tables: map[string]*vschemapb.Table{ 1845 "ref2": { 1846 Type: "reference", 1847 Source: "unsharded3.src3", 1848 }, 1849 }, 1850 }, 1851 }, 1852 } 1853 vschema := BuildVSchema(&input) 1854 require.Error(t, vschema.Keyspaces["sharded1"].Error) 1855 require.EqualError(t, vschema.Keyspaces["sharded1"].Error, 1856 "source \"unsharded1.src1\" may not reference a table of type \"sequence\": ref1") 1857 require.NoError(t, vschema.Keyspaces["sharded2"].Error) 1858 } 1859 1860 func TestBuildVSchemaSourceMayBeReferencedAtMostOncePerKeyspace(t *testing.T) { 1861 input := vschemapb.SrvVSchema{ 1862 Keyspaces: map[string]*vschemapb.Keyspace{ 1863 "unsharded": { 1864 Sharded: false, 1865 Tables: map[string]*vschemapb.Table{ 1866 "src": {}, 1867 }, 1868 }, 1869 "sharded": { 1870 Sharded: true, 1871 Tables: map[string]*vschemapb.Table{ 1872 "ref2": { 1873 Type: "reference", 1874 Source: "unsharded.src", 1875 }, 1876 "ref1": { 1877 Type: "reference", 1878 Source: "unsharded.src", 1879 }, 1880 }, 1881 }, 1882 }, 1883 } 1884 vschema := BuildVSchema(&input) 1885 require.Error(t, vschema.Keyspaces["sharded"].Error) 1886 require.EqualError(t, vschema.Keyspaces["sharded"].Error, 1887 "source \"unsharded.src\" may not be referenced more than once per keyspace: ref1, ref2") 1888 } 1889 1890 func TestBuildVSchemaMayNotChainReferences(t *testing.T) { 1891 input := vschemapb.SrvVSchema{ 1892 Keyspaces: map[string]*vschemapb.Keyspace{ 1893 "unsharded1": { 1894 Sharded: false, 1895 Tables: map[string]*vschemapb.Table{ 1896 "ref": { 1897 Type: TypeReference, 1898 Source: "unsharded2.ref", 1899 }, 1900 }, 1901 }, 1902 "unsharded2": { 1903 Sharded: false, 1904 Tables: map[string]*vschemapb.Table{ 1905 "ref": { 1906 Type: TypeReference, 1907 Source: "unsharded3.ref", 1908 }, 1909 }, 1910 }, 1911 "unsharded3": { 1912 Sharded: false, 1913 Tables: map[string]*vschemapb.Table{ 1914 "ref": { 1915 Type: "reference", 1916 Source: "unsharded1.ref", 1917 }, 1918 }, 1919 }, 1920 }, 1921 } 1922 vschema := BuildVSchema(&input) 1923 require.Error(t, vschema.Keyspaces["unsharded1"].Error) 1924 require.EqualError(t, vschema.Keyspaces["unsharded1"].Error, 1925 "reference chaining is not allowed ref => unsharded2.ref => unsharded3.ref: ref") 1926 } 1927 1928 func TestSequence(t *testing.T) { 1929 good := vschemapb.SrvVSchema{ 1930 Keyspaces: map[string]*vschemapb.Keyspace{ 1931 "unsharded": { 1932 Tables: map[string]*vschemapb.Table{ 1933 "seq": { 1934 Type: "sequence", 1935 }, 1936 }, 1937 }, 1938 "sharded": { 1939 Sharded: true, 1940 Vindexes: map[string]*vschemapb.Vindex{ 1941 "stfu1": { 1942 Type: "stfu", 1943 Params: map[string]string{ 1944 "stfu1": "1", 1945 }, 1946 }, 1947 }, 1948 Tables: map[string]*vschemapb.Table{ 1949 "t1": { 1950 ColumnVindexes: []*vschemapb.ColumnVindex{ 1951 { 1952 Column: "c1", 1953 Name: "stfu1", 1954 }, 1955 }, 1956 AutoIncrement: &vschemapb.AutoIncrement{ 1957 Column: "c1", 1958 Sequence: "seq", 1959 }, 1960 }, 1961 "t2": { 1962 ColumnVindexes: []*vschemapb.ColumnVindex{ 1963 { 1964 Column: "c1", 1965 Name: "stfu1", 1966 }, 1967 }, 1968 AutoIncrement: &vschemapb.AutoIncrement{ 1969 Column: "c2", 1970 Sequence: "`unsharded`.`seq`", 1971 }, 1972 }, 1973 }, 1974 }, 1975 }, 1976 } 1977 got := BuildVSchema(&good) 1978 err := got.Keyspaces["sharded"].Error 1979 require.NoError(t, err) 1980 err1 := got.Keyspaces["unsharded"].Error 1981 if err1 != nil { 1982 t.Error(err1) 1983 } 1984 ksu := &Keyspace{ 1985 Name: "unsharded", 1986 } 1987 kss := &Keyspace{ 1988 Name: "sharded", 1989 Sharded: true, 1990 } 1991 seq := &Table{ 1992 Name: sqlparser.NewIdentifierCS("seq"), 1993 Keyspace: ksu, 1994 Type: "sequence", 1995 } 1996 vindex1 := &stFU{ 1997 name: "stfu1", 1998 Params: map[string]string{ 1999 "stfu1": "1", 2000 }, 2001 } 2002 t1 := &Table{ 2003 Name: sqlparser.NewIdentifierCS("t1"), 2004 Keyspace: kss, 2005 ColumnVindexes: []*ColumnVindex{ 2006 { 2007 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 2008 Type: "stfu", 2009 Name: "stfu1", 2010 Vindex: vindex1, 2011 isUnique: vindex1.IsUnique(), 2012 cost: vindex1.Cost(), 2013 }, 2014 }, 2015 AutoIncrement: &AutoIncrement{ 2016 Column: sqlparser.NewIdentifierCI("c1"), 2017 Sequence: seq, 2018 }, 2019 } 2020 t1.Ordered = []*ColumnVindex{ 2021 t1.ColumnVindexes[0], 2022 } 2023 t2 := &Table{ 2024 Name: sqlparser.NewIdentifierCS("t2"), 2025 Keyspace: kss, 2026 ColumnVindexes: []*ColumnVindex{ 2027 { 2028 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("c1")}, 2029 Type: "stfu", 2030 Name: "stfu1", 2031 Vindex: vindex1, 2032 isUnique: vindex1.IsUnique(), 2033 cost: vindex1.Cost(), 2034 }, 2035 }, 2036 AutoIncrement: &AutoIncrement{ 2037 Column: sqlparser.NewIdentifierCI("c2"), 2038 Sequence: seq, 2039 }, 2040 } 2041 t2.Ordered = []*ColumnVindex{ 2042 t2.ColumnVindexes[0], 2043 } 2044 duala := &Table{ 2045 Name: sqlparser.NewIdentifierCS("dual"), 2046 Keyspace: ksu, 2047 Type: TypeReference, 2048 } 2049 dualb := &Table{ 2050 Name: sqlparser.NewIdentifierCS("dual"), 2051 Keyspace: kss, 2052 Type: TypeReference, 2053 } 2054 want := &VSchema{ 2055 RoutingRules: map[string]*RoutingRule{}, 2056 globalTables: map[string]*Table{ 2057 "seq": seq, 2058 "t1": t1, 2059 "t2": t2, 2060 "dual": dualb, 2061 }, 2062 uniqueVindexes: map[string]Vindex{ 2063 "stfu1": vindex1, 2064 }, 2065 Keyspaces: map[string]*KeyspaceSchema{ 2066 "unsharded": { 2067 Keyspace: ksu, 2068 Tables: map[string]*Table{ 2069 "seq": seq, 2070 "dual": duala, 2071 }, 2072 Vindexes: map[string]Vindex{}, 2073 }, 2074 "sharded": { 2075 Keyspace: kss, 2076 Tables: map[string]*Table{ 2077 "t1": t1, 2078 "t2": t2, 2079 "dual": dualb, 2080 }, 2081 Vindexes: map[string]Vindex{ 2082 "stfu1": vindex1, 2083 }, 2084 }, 2085 }, 2086 } 2087 if !reflect.DeepEqual(got, want) { 2088 gotjson, _ := json.Marshal(got) 2089 wantjson, _ := json.Marshal(want) 2090 t.Errorf("BuildVSchema:\n%s, want\n%s", gotjson, wantjson) 2091 } 2092 } 2093 2094 func TestBadSequence(t *testing.T) { 2095 bad := vschemapb.SrvVSchema{ 2096 Keyspaces: map[string]*vschemapb.Keyspace{ 2097 "unsharded": { 2098 Tables: map[string]*vschemapb.Table{ 2099 "valid_seq": { 2100 Type: "sequence", 2101 }, 2102 }, 2103 }, 2104 "sharded": { 2105 Sharded: true, 2106 Vindexes: map[string]*vschemapb.Vindex{ 2107 "stfu1": { 2108 Type: "stfu", 2109 }, 2110 }, 2111 Tables: map[string]*vschemapb.Table{ 2112 "t1": { 2113 ColumnVindexes: []*vschemapb.ColumnVindex{ 2114 { 2115 Column: "c1", 2116 Name: "stfu1", 2117 }, 2118 }, 2119 AutoIncrement: &vschemapb.AutoIncrement{ 2120 Column: "c1", 2121 Sequence: "invalid_seq", 2122 }, 2123 }, 2124 "t2": { 2125 ColumnVindexes: []*vschemapb.ColumnVindex{ 2126 { 2127 Column: "c1", 2128 Name: "stfu1", 2129 }, 2130 }, 2131 AutoIncrement: &vschemapb.AutoIncrement{ 2132 Column: "c1", 2133 Sequence: "valid_seq", 2134 }, 2135 }, 2136 }, 2137 }, 2138 }, 2139 } 2140 got := BuildVSchema(&bad) 2141 err := got.Keyspaces["sharded"].Error 2142 want := "cannot resolve sequence invalid_seq: table invalid_seq not found" 2143 if err == nil || err.Error() != want { 2144 t.Errorf("BuildVSchema: %v, want %v", err, want) 2145 } 2146 if t1 := got.Keyspaces["sharded"].Tables["t1"]; t1 != nil { 2147 t.Errorf("BuildVSchema: table t1 must not be present in the keyspace: %v", t1) 2148 } 2149 2150 // Verify that a failure to set up a sequence for t1 doesn't prevent setting up 2151 // a sequence for t2. 2152 t2Seq := got.Keyspaces["sharded"].Tables["t2"].AutoIncrement.Sequence 2153 if t2Seq.Name.String() != "valid_seq" { 2154 t.Errorf("BuildVSchema: unexpected t2 sequence name. Got: %v. Want: %v", 2155 t2Seq.AutoIncrement.Sequence.Name, 2156 "valid_seq", 2157 ) 2158 } 2159 } 2160 2161 func TestBadSequenceName(t *testing.T) { 2162 bad := vschemapb.SrvVSchema{ 2163 Keyspaces: map[string]*vschemapb.Keyspace{ 2164 "sharded": { 2165 Sharded: true, 2166 Vindexes: map[string]*vschemapb.Vindex{ 2167 "stfu1": { 2168 Type: "stfu", 2169 }, 2170 }, 2171 Tables: map[string]*vschemapb.Table{ 2172 "t1": { 2173 ColumnVindexes: []*vschemapb.ColumnVindex{ 2174 { 2175 Column: "c1", 2176 Name: "stfu1", 2177 }, 2178 }, 2179 AutoIncrement: &vschemapb.AutoIncrement{ 2180 Column: "c1", 2181 Sequence: "a.b.seq", 2182 }, 2183 }, 2184 }, 2185 }, 2186 }, 2187 } 2188 got := BuildVSchema(&bad) 2189 err := got.Keyspaces["sharded"].Error 2190 want := "invalid table name: a.b.seq" 2191 if err == nil || !strings.Contains(err.Error(), want) { 2192 t.Errorf("BuildVSchema: %v, must contain %v", err, want) 2193 } 2194 if t1 := got.Keyspaces["sharded"].Tables["t1"]; t1 != nil { 2195 t.Errorf("BuildVSchema: table t1 must not be present in the keyspace: %v", t1) 2196 } 2197 } 2198 2199 func TestBadShardedSequence(t *testing.T) { 2200 bad := vschemapb.SrvVSchema{ 2201 Keyspaces: map[string]*vschemapb.Keyspace{ 2202 "sharded": { 2203 Sharded: true, 2204 Tables: map[string]*vschemapb.Table{ 2205 "t1": { 2206 Type: "sequence", 2207 }, 2208 }, 2209 }, 2210 }, 2211 } 2212 got := BuildVSchema(&bad) 2213 err := got.Keyspaces["sharded"].Error 2214 want := "sequence table has to be in an unsharded keyspace or must be pinned: t1" 2215 if err == nil || err.Error() != want { 2216 t.Errorf("BuildVSchema: %v, want %v", err, want) 2217 } 2218 } 2219 2220 func TestFindTable(t *testing.T) { 2221 input := vschemapb.SrvVSchema{ 2222 Keyspaces: map[string]*vschemapb.Keyspace{ 2223 "ksa": { 2224 Tables: map[string]*vschemapb.Table{ 2225 "ta": {}, 2226 "t1": {}, 2227 "t2": {}, 2228 }, 2229 }, 2230 "ksb": { 2231 Sharded: true, 2232 Vindexes: map[string]*vschemapb.Vindex{ 2233 "stfu1": { 2234 Type: "stfu", 2235 Params: map[string]string{ 2236 "stfu1": "1", 2237 }, 2238 }, 2239 }, 2240 Tables: map[string]*vschemapb.Table{ 2241 "tb": { 2242 ColumnVindexes: []*vschemapb.ColumnVindex{ 2243 { 2244 Column: "c1", 2245 Name: "stfu1", 2246 }, 2247 }, 2248 }, 2249 "t1": { 2250 ColumnVindexes: []*vschemapb.ColumnVindex{ 2251 { 2252 Column: "c1", 2253 Name: "stfu1", 2254 }, 2255 }, 2256 }, 2257 "t2": { 2258 Type: "reference", 2259 Source: "ksa.t2", 2260 }, 2261 }, 2262 }, 2263 }, 2264 } 2265 vschema := BuildVSchema(&input) 2266 _, err := vschema.FindTable("", "t1") 2267 require.EqualError(t, err, "ambiguous table reference: t1") 2268 2269 _, err = vschema.FindTable("", "none") 2270 require.EqualError(t, err, "table none not found") 2271 2272 ta := &Table{ 2273 Name: sqlparser.NewIdentifierCS("ta"), 2274 Keyspace: &Keyspace{ 2275 Name: "ksa", 2276 }, 2277 } 2278 got, err := vschema.FindTable("", "ta") 2279 require.NoError(t, err) 2280 require.Equal(t, ta, got) 2281 2282 t2 := &Table{ 2283 Name: sqlparser.NewIdentifierCS("t2"), 2284 Keyspace: &Keyspace{ 2285 Name: "ksa", 2286 }, 2287 ReferencedBy: map[string]*Table{ 2288 "ksb": { 2289 Type: "reference", 2290 Name: sqlparser.NewIdentifierCS("t2"), 2291 Keyspace: &Keyspace{ 2292 Sharded: true, 2293 Name: "ksb", 2294 }, 2295 Source: &Source{ 2296 sqlparser.TableName{ 2297 Qualifier: sqlparser.NewIdentifierCS("ksa"), 2298 Name: sqlparser.NewIdentifierCS("t2"), 2299 }, 2300 }, 2301 }, 2302 }, 2303 } 2304 got, err = vschema.FindTable("", "t2") 2305 require.NoError(t, err) 2306 require.Equal(t, t2, got) 2307 2308 got, _ = vschema.FindTable("ksa", "ta") 2309 require.Equal(t, ta, got) 2310 2311 none := &Table{ 2312 Name: sqlparser.NewIdentifierCS("none"), 2313 Keyspace: &Keyspace{ 2314 Name: "ksa", 2315 }, 2316 } 2317 got, _ = vschema.FindTable("ksa", "none") 2318 require.Equal(t, none, got) 2319 2320 _, err = vschema.FindTable("ksb", "none") 2321 require.EqualError(t, err, "table none not found") 2322 2323 _, err = vschema.FindTable("none", "aa") 2324 require.EqualError(t, err, "VT05003: unknown database 'none' in vschema") 2325 } 2326 2327 func TestFindTableOrVindex(t *testing.T) { 2328 input := vschemapb.SrvVSchema{ 2329 RoutingRules: &vschemapb.RoutingRules{ 2330 Rules: []*vschemapb.RoutingRule{{ 2331 FromTable: "unqualified", 2332 ToTables: []string{"ksa.ta"}, 2333 }, { 2334 FromTable: "unqualified@replica", 2335 ToTables: []string{"ksb.t1"}, 2336 }, { 2337 FromTable: "newks.qualified", 2338 ToTables: []string{"ksa.ta"}, 2339 }, { 2340 FromTable: "newks.qualified@replica", 2341 ToTables: []string{"ksb.t1"}, 2342 }, { 2343 FromTable: "notarget", 2344 ToTables: []string{}, 2345 }}, 2346 }, 2347 Keyspaces: map[string]*vschemapb.Keyspace{ 2348 "ksa": { 2349 Tables: map[string]*vschemapb.Table{ 2350 "ta": {}, 2351 "t1": {}, 2352 }, 2353 }, 2354 "ksb": { 2355 Sharded: true, 2356 Vindexes: map[string]*vschemapb.Vindex{ 2357 "stfu1": { 2358 Type: "stfu", 2359 }, 2360 "dup": { 2361 Type: "stfu", 2362 }, 2363 }, 2364 Tables: map[string]*vschemapb.Table{ 2365 "t1": { 2366 ColumnVindexes: []*vschemapb.ColumnVindex{ 2367 { 2368 Column: "c1", 2369 Name: "stfu1", 2370 }, 2371 }, 2372 }, 2373 }, 2374 }, 2375 "ksc": { 2376 Sharded: true, 2377 Vindexes: map[string]*vschemapb.Vindex{ 2378 "dup": { 2379 Type: "stfu", 2380 }, 2381 "ta": { 2382 Type: "stfu", 2383 }, 2384 }, 2385 Tables: map[string]*vschemapb.Table{}, 2386 }, 2387 }, 2388 } 2389 vschema := BuildVSchema(&input) 2390 ta := vschema.Keyspaces["ksa"].Tables["ta"] 2391 t1 := vschema.Keyspaces["ksb"].Tables["t1"] 2392 2393 _, _, err := vschema.FindTableOrVindex("", "t1", topodatapb.TabletType_PRIMARY) 2394 wantErr := "ambiguous table reference: t1" 2395 if err == nil || err.Error() != wantErr { 2396 t.Errorf("FindTableOrVindex(\"\"): %v, want %s", err, wantErr) 2397 } 2398 2399 _, _, err = vschema.FindTableOrVindex("", "none", topodatapb.TabletType_PRIMARY) 2400 wantErr = "table none not found" 2401 if err == nil || err.Error() != wantErr { 2402 t.Errorf("FindTableOrVindex(\"\"): %v, want %s", err, wantErr) 2403 } 2404 2405 got, _, err := vschema.FindTableOrVindex("", "ta", topodatapb.TabletType_PRIMARY) 2406 if err != nil { 2407 t.Fatal(err) 2408 } 2409 if !reflect.DeepEqual(got, ta) { 2410 t.Errorf("FindTableOrVindex(\"t1a\"): %+v, want %+v", got, ta) 2411 } 2412 2413 _, vindex, err := vschema.FindTableOrVindex("", "stfu1", topodatapb.TabletType_PRIMARY) 2414 if err != nil { 2415 t.Fatal(err) 2416 } 2417 wantVindex := &stFU{ 2418 name: "stfu1", 2419 } 2420 if !reflect.DeepEqual(vindex, wantVindex) { 2421 t.Errorf("FindTableOrVindex(\"stfu1\"): %+v, want %+v", vindex, wantVindex) 2422 } 2423 2424 _, vindex, err = vschema.FindTableOrVindex("ksc", "ta", topodatapb.TabletType_PRIMARY) 2425 if err != nil { 2426 t.Fatal(err) 2427 } 2428 wantVindex = &stFU{ 2429 name: "ta", 2430 } 2431 if !reflect.DeepEqual(vindex, wantVindex) { 2432 t.Errorf("FindTableOrVindex(\"stfu1\"): %+v, want %+v", vindex, wantVindex) 2433 } 2434 2435 _, _, err = vschema.FindTableOrVindex("", "dup", topodatapb.TabletType_PRIMARY) 2436 wantErr = "ambiguous vindex reference: dup" 2437 if err == nil || err.Error() != wantErr { 2438 t.Errorf("FindTableOrVindex(\"\"): %v, want %s", err, wantErr) 2439 } 2440 2441 got, _, err = vschema.FindTableOrVindex("", "unqualified", topodatapb.TabletType_PRIMARY) 2442 if err != nil { 2443 t.Fatal(err) 2444 } 2445 if want := ta; !reflect.DeepEqual(got, want) { 2446 t.Errorf("FindTableOrVindex(unqualified): %+v, want %+v", got, want) 2447 } 2448 2449 got, _, err = vschema.FindTableOrVindex("", "unqualified", topodatapb.TabletType_REPLICA) 2450 if err != nil { 2451 t.Fatal(err) 2452 } 2453 if want := t1; !reflect.DeepEqual(got, want) { 2454 t.Errorf("FindTableOrVindex(unqualified): %+v, want %+v", got, want) 2455 } 2456 2457 got, _, err = vschema.FindTableOrVindex("newks", "qualified", topodatapb.TabletType_PRIMARY) 2458 if err != nil { 2459 t.Fatal(err) 2460 } 2461 if want := ta; !reflect.DeepEqual(got, want) { 2462 t.Errorf("FindTableOrVindex(unqualified): %+v, want %+v", got, want) 2463 } 2464 2465 got, _, err = vschema.FindTableOrVindex("newks", "qualified", topodatapb.TabletType_REPLICA) 2466 if err != nil { 2467 t.Fatal(err) 2468 } 2469 if want := t1; !reflect.DeepEqual(got, want) { 2470 t.Errorf("FindTableOrVindex(unqualified): %+v, want %+v", got, want) 2471 } 2472 2473 _, _, err = vschema.FindTableOrVindex("", "notarget", topodatapb.TabletType_PRIMARY) 2474 wantErr = "table notarget has been disabled" 2475 if err == nil || err.Error() != wantErr { 2476 t.Errorf("FindTableOrVindex(\"\"): %v, want %s", err, wantErr) 2477 } 2478 } 2479 2480 func TestBuildKeyspaceSchema(t *testing.T) { 2481 good := &vschemapb.Keyspace{ 2482 Tables: map[string]*vschemapb.Table{ 2483 "t1": { 2484 AutoIncrement: &vschemapb.AutoIncrement{ 2485 Column: "col", 2486 Sequence: "outside", 2487 }, 2488 }, 2489 "t2": {}, 2490 }, 2491 } 2492 got, _ := BuildKeyspaceSchema(good, "ks") 2493 err := got.Error 2494 require.NoError(t, err) 2495 ks := &Keyspace{ 2496 Name: "ks", 2497 } 2498 t1 := &Table{ 2499 Name: sqlparser.NewIdentifierCS("t1"), 2500 Keyspace: ks, 2501 } 2502 t2 := &Table{ 2503 Name: sqlparser.NewIdentifierCS("t2"), 2504 Keyspace: ks, 2505 } 2506 want := &KeyspaceSchema{ 2507 Keyspace: ks, 2508 Tables: map[string]*Table{ 2509 "t1": t1, 2510 "t2": t2, 2511 }, 2512 Vindexes: map[string]Vindex{}, 2513 } 2514 if !reflect.DeepEqual(got, want) { 2515 gs, _ := json.Marshal(got) 2516 ws, _ := json.Marshal(want) 2517 t.Errorf("BuildKeyspaceSchema:\n%s, want\n%s", gs, ws) 2518 } 2519 } 2520 2521 func TestValidate(t *testing.T) { 2522 good := &vschemapb.Keyspace{ 2523 Tables: map[string]*vschemapb.Table{ 2524 "t1": { 2525 AutoIncrement: &vschemapb.AutoIncrement{ 2526 Column: "col", 2527 Sequence: "outside", 2528 }, 2529 }, 2530 "t2": {}, 2531 }, 2532 } 2533 err := ValidateKeyspace(good) 2534 require.NoError(t, err) 2535 bad := &vschemapb.Keyspace{ 2536 Sharded: true, 2537 Vindexes: map[string]*vschemapb.Vindex{ 2538 "hash": { 2539 Type: "absent", 2540 }, 2541 }, 2542 Tables: map[string]*vschemapb.Table{ 2543 "t2": {}, 2544 }, 2545 } 2546 err = ValidateKeyspace(bad) 2547 want := `vindexType "absent" not found` 2548 if err == nil || !strings.HasPrefix(err.Error(), want) { 2549 t.Errorf("Validate: %v, must start with %s", err, want) 2550 } 2551 } 2552 2553 func TestVSchemaPBJSON(t *testing.T) { 2554 in := ` 2555 { 2556 "sharded": true, 2557 "tables": { 2558 "t1": { 2559 "column_vindexes":[{ 2560 "column":"c1", 2561 "name":"stfu1" 2562 },{ 2563 "column":"c2", 2564 "name":"stln1" 2565 }], 2566 "auto_increment": { 2567 "column": "col", 2568 "sequence": "outside" 2569 } 2570 }, 2571 "t2": { 2572 "columns":[{ 2573 "name": "c1", 2574 "type": "VARCHAR" 2575 }] 2576 } 2577 } 2578 } 2579 ` 2580 var got vschemapb.Keyspace 2581 if err := json2.Unmarshal([]byte(in), &got); err != nil { 2582 t.Error(err) 2583 } 2584 want := vschemapb.Keyspace{ 2585 Sharded: true, 2586 Tables: map[string]*vschemapb.Table{ 2587 "t1": { 2588 ColumnVindexes: []*vschemapb.ColumnVindex{ 2589 { 2590 Column: "c1", 2591 Name: "stfu1", 2592 }, { 2593 Column: "c2", 2594 Name: "stln1", 2595 }, 2596 }, 2597 AutoIncrement: &vschemapb.AutoIncrement{ 2598 Column: "col", 2599 Sequence: "outside", 2600 }, 2601 }, 2602 "t2": { 2603 Columns: []*vschemapb.Column{{ 2604 Name: "c1", 2605 Type: querypb.Type_VARCHAR, 2606 }}, 2607 }, 2608 }, 2609 } 2610 if !proto.Equal(&got, &want) { 2611 gs, _ := json2.MarshalPB(&got) 2612 ws, _ := json2.MarshalPB(&want) 2613 t.Errorf("vschemapb.SrvVSchemaForKeyspace():\n%s, want\n%s", gs, ws) 2614 } 2615 } 2616 2617 func TestVSchemaJSON(t *testing.T) { 2618 lkp, _ := NewLookupHash("n2", map[string]string{ 2619 "from": "f", 2620 "table": "t", 2621 "to": "2", 2622 }) 2623 in := map[string]*KeyspaceSchema{ 2624 "unsharded": { 2625 Keyspace: &Keyspace{ 2626 Name: "k1", 2627 }, 2628 Tables: map[string]*Table{ 2629 "t1": { 2630 Name: sqlparser.NewIdentifierCS("n1"), 2631 Columns: []Column{{ 2632 Name: sqlparser.NewIdentifierCI("c1"), 2633 }, { 2634 Name: sqlparser.NewIdentifierCI("c2"), 2635 Type: sqltypes.VarChar, 2636 }}, 2637 }, 2638 "t2": { 2639 Type: "sequence", 2640 Name: sqlparser.NewIdentifierCS("n2"), 2641 }, 2642 }, 2643 }, 2644 "sharded": { 2645 Keyspace: &Keyspace{ 2646 Name: "k2", 2647 Sharded: true, 2648 }, 2649 Tables: map[string]*Table{ 2650 "t3": { 2651 Name: sqlparser.NewIdentifierCS("n3"), 2652 ColumnVindexes: []*ColumnVindex{{ 2653 Columns: []sqlparser.IdentifierCI{sqlparser.NewIdentifierCI("aa")}, 2654 Type: "vtype", 2655 Name: "vname", 2656 Owned: true, 2657 Vindex: lkp, 2658 }}, 2659 }, 2660 }, 2661 }, 2662 } 2663 out, err := json.MarshalIndent(in, "", " ") 2664 if err != nil { 2665 t.Fatal(err) 2666 } 2667 got := string(out) 2668 want := `{ 2669 "sharded": { 2670 "sharded": true, 2671 "tables": { 2672 "t3": { 2673 "name": "n3", 2674 "column_vindexes": [ 2675 { 2676 "columns": [ 2677 "aa" 2678 ], 2679 "type": "vtype", 2680 "name": "vname", 2681 "owned": true, 2682 "vindex": { 2683 "table": "t", 2684 "from_columns": [ 2685 "f" 2686 ], 2687 "to": "2" 2688 } 2689 } 2690 ] 2691 } 2692 } 2693 }, 2694 "unsharded": { 2695 "tables": { 2696 "t1": { 2697 "name": "n1", 2698 "columns": [ 2699 { 2700 "name": "c1", 2701 "type": "NULL_TYPE" 2702 }, 2703 { 2704 "name": "c2", 2705 "type": "VARCHAR" 2706 } 2707 ] 2708 }, 2709 "t2": { 2710 "type": "sequence", 2711 "name": "n2" 2712 } 2713 } 2714 } 2715 }` 2716 if got != want { 2717 t.Errorf("json.Marshal():\n%s, want\n%s", got, want) 2718 } 2719 } 2720 2721 func TestFindSingleKeyspace(t *testing.T) { 2722 input := vschemapb.SrvVSchema{ 2723 Keyspaces: map[string]*vschemapb.Keyspace{ 2724 "ksa": { 2725 Tables: map[string]*vschemapb.Table{ 2726 "ta": {}, 2727 "t1": {}, 2728 }, 2729 }, 2730 }, 2731 } 2732 vschema := BuildVSchema(&input) 2733 none := &Table{ 2734 Name: sqlparser.NewIdentifierCS("none"), 2735 Keyspace: &Keyspace{ 2736 Name: "ksa", 2737 }, 2738 } 2739 got, _ := vschema.FindTable("", "none") 2740 if !reflect.DeepEqual(got, none) { 2741 t.Errorf("FindTable(\"t1a\"): %+v, want %+v", got, none) 2742 } 2743 input = vschemapb.SrvVSchema{ 2744 Keyspaces: map[string]*vschemapb.Keyspace{ 2745 "ksb": { 2746 Sharded: true, 2747 Vindexes: map[string]*vschemapb.Vindex{ 2748 "stfu1": { 2749 Type: "stfu", 2750 }, 2751 }, 2752 Tables: map[string]*vschemapb.Table{ 2753 "tb": { 2754 ColumnVindexes: []*vschemapb.ColumnVindex{ 2755 { 2756 Column: "c1", 2757 Name: "stfu1", 2758 }, 2759 }, 2760 }, 2761 "t1": { 2762 ColumnVindexes: []*vschemapb.ColumnVindex{ 2763 { 2764 Column: "c1", 2765 Name: "stfu1", 2766 }, 2767 }, 2768 }, 2769 }, 2770 }, 2771 }, 2772 } 2773 vschema = BuildVSchema(&input) 2774 _, err := vschema.FindTable("", "none") 2775 wantErr := "table none not found" 2776 if err == nil || err.Error() != wantErr { 2777 t.Errorf("FindTable(\"\"): %v, want %s", err, wantErr) 2778 } 2779 } 2780 2781 func TestMultiColVindexPartialAllowed(t *testing.T) { 2782 input := vschemapb.SrvVSchema{ 2783 Keyspaces: map[string]*vschemapb.Keyspace{ 2784 "ksa": { 2785 Sharded: true, 2786 Vindexes: map[string]*vschemapb.Vindex{ 2787 "regional_vdx": { 2788 Type: "region_experimental_test", 2789 Params: map[string]string{ 2790 "region_bytes": "1", 2791 }, 2792 }, 2793 }, 2794 Tables: map[string]*vschemapb.Table{ 2795 "user_region": { 2796 ColumnVindexes: []*vschemapb.ColumnVindex{ 2797 { 2798 Columns: []string{"cola", "colb"}, 2799 Name: "regional_vdx", 2800 }, 2801 }, 2802 }, 2803 }, 2804 }, 2805 }, 2806 } 2807 vschema := BuildVSchema(&input) 2808 table, err := vschema.FindTable("ksa", "user_region") 2809 require.NoError(t, err) 2810 require.Len(t, table.ColumnVindexes, 2) 2811 require.True(t, table.ColumnVindexes[0].IsUnique()) 2812 require.False(t, table.ColumnVindexes[1].IsUnique()) 2813 require.EqualValues(t, 1, table.ColumnVindexes[0].Cost()) 2814 require.EqualValues(t, 2, table.ColumnVindexes[1].Cost()) 2815 } 2816 2817 func TestMultiColVindexPartialNotAllowed(t *testing.T) { 2818 input := vschemapb.SrvVSchema{ 2819 Keyspaces: map[string]*vschemapb.Keyspace{ 2820 "ksa": { 2821 Sharded: true, 2822 Vindexes: map[string]*vschemapb.Vindex{ 2823 "multicol_vdx": { 2824 Type: "mcfu", 2825 }, 2826 }, 2827 Tables: map[string]*vschemapb.Table{ 2828 "multiColTbl": { 2829 ColumnVindexes: []*vschemapb.ColumnVindex{ 2830 { 2831 Columns: []string{"cola", "colb", "colc"}, 2832 Name: "multicol_vdx", 2833 }, 2834 }, 2835 }, 2836 }, 2837 }, 2838 }, 2839 } 2840 vschema := BuildVSchema(&input) 2841 table, err := vschema.FindTable("ksa", "multiColTbl") 2842 require.NoError(t, err) 2843 require.Len(t, table.ColumnVindexes, 1) 2844 require.True(t, table.ColumnVindexes[0].IsUnique()) 2845 require.EqualValues(t, 1, table.ColumnVindexes[0].Cost()) 2846 } 2847 2848 func TestSourceTableHasReferencedBy(t *testing.T) { 2849 input := vschemapb.SrvVSchema{ 2850 Keyspaces: map[string]*vschemapb.Keyspace{ 2851 "unsharded": { 2852 Sharded: false, 2853 Tables: map[string]*vschemapb.Table{ 2854 "src": {}, 2855 }, 2856 }, 2857 "sharded1": { 2858 Sharded: true, 2859 Tables: map[string]*vschemapb.Table{ 2860 "ref": { 2861 Type: "reference", 2862 Source: "unsharded.src", 2863 }, 2864 }, 2865 }, 2866 "sharded2": { 2867 Sharded: true, 2868 Tables: map[string]*vschemapb.Table{ 2869 "ref": { 2870 Type: "reference", 2871 Source: "unsharded.src", 2872 }, 2873 }, 2874 }, 2875 }, 2876 } 2877 vs := BuildVSchema(&input) 2878 ref1, err := vs.FindTable("sharded1", "ref") 2879 require.NoError(t, err) 2880 ref2, err := vs.FindTable("sharded2", "ref") 2881 require.NoError(t, err) 2882 src, err := vs.FindTable("unsharded", "src") 2883 require.NoError(t, err) 2884 require.Equal(t, src.ReferencedBy, map[string]*Table{ 2885 "sharded1": ref1, 2886 "sharded2": ref2, 2887 }) 2888 } 2889 2890 func TestReferenceTableAndSourceAreGloballyRoutable(t *testing.T) { 2891 input := vschemapb.SrvVSchema{ 2892 Keyspaces: map[string]*vschemapb.Keyspace{ 2893 "unsharded": { 2894 Sharded: false, 2895 Tables: map[string]*vschemapb.Table{ 2896 "t1": {}, 2897 }, 2898 }, 2899 "sharded": { 2900 Sharded: true, 2901 Tables: map[string]*vschemapb.Table{ 2902 "t1": { 2903 Type: "reference", 2904 Source: "unsharded.t1", 2905 }, 2906 }, 2907 }, 2908 }, 2909 } 2910 vs := BuildVSchema(&input) 2911 t1, err := vs.FindTable("unsharded", "t1") 2912 require.NoError(t, err) 2913 globalT1, err := vs.FindTable("", "t1") 2914 require.NoError(t, err) 2915 require.Equal(t, t1, globalT1) 2916 2917 input.Keyspaces["unsharded"].RequireExplicitRouting = true 2918 vs = BuildVSchema(&input) 2919 t1, err = vs.FindTable("sharded", "t1") 2920 require.NoError(t, err) 2921 globalT1, err = vs.FindTable("", "t1") 2922 require.NoError(t, err) 2923 require.Equal(t, t1, globalT1) 2924 } 2925 2926 func TestOtherTablesMakeReferenceTableAndSourceAmbiguous(t *testing.T) { 2927 input := vschemapb.SrvVSchema{ 2928 Keyspaces: map[string]*vschemapb.Keyspace{ 2929 "unsharded1": { 2930 Sharded: false, 2931 Tables: map[string]*vschemapb.Table{ 2932 "t1": {}, 2933 }, 2934 }, 2935 "unsharded2": { 2936 Sharded: false, 2937 Tables: map[string]*vschemapb.Table{ 2938 "t1": {}, 2939 }, 2940 }, 2941 "sharded": { 2942 Sharded: true, 2943 Tables: map[string]*vschemapb.Table{ 2944 "t1": { 2945 Type: "reference", 2946 Source: "unsharded1.t1", 2947 }, 2948 }, 2949 }, 2950 }, 2951 } 2952 vs := BuildVSchema(&input) 2953 _, err := vs.FindTable("", "t1") 2954 require.Error(t, err) 2955 } 2956 2957 func vindexNames(vindexes []*ColumnVindex) (result []string) { 2958 for _, vindex := range vindexes { 2959 result = append(result, vindex.Name) 2960 } 2961 return 2962 } 2963 2964 func assertVindexMatches(t *testing.T, cv *ColumnVindex, v Vindex, name string, owned bool) { 2965 utils.MustMatch(t, v, cv.Vindex) 2966 assert.Equal(t, name, cv.Name) 2967 assert.Equal(t, v.Cost(), cv.Cost(), "cost not correct") 2968 assert.Equal(t, v.IsUnique(), cv.IsUnique(), "isUnique not correct") 2969 assert.Equal(t, owned, cv.Owned, "owned is not correct") 2970 } 2971 2972 func assertColumn(t *testing.T, col Column, expectedName string, expectedType querypb.Type) { 2973 assert.True(t, col.Name.EqualString(expectedName), "column name does not match") 2974 assert.Equal(t, expectedType, col.Type, "column type does not match") 2975 2976 }