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  }