diff --git a/go/test/endtoend/vtgate/gen4/system_schema_test.go b/go/test/endtoend/vtgate/gen4/system_schema_test.go index c075479bb11..fba953d51ae 100644 --- a/go/test/endtoend/vtgate/gen4/system_schema_test.go +++ b/go/test/endtoend/vtgate/gen4/system_schema_test.go @@ -126,7 +126,8 @@ func TestFKConstraintUsingInformationSchema(t *testing.T) { query := "select fk.referenced_table_name as to_table, fk.referenced_column_name as primary_key, fk.column_name as `column`, fk.constraint_name as name, rc.update_rule as on_update, rc.delete_rule as on_delete from information_schema.referential_constraints as rc join information_schema.key_column_usage as fk on fk.constraint_schema = rc.constraint_schema and fk.constraint_name = rc.constraint_name where fk.referenced_column_name is not null and fk.table_schema = database() and fk.table_name = 't7_fk' and rc.constraint_schema = database() and rc.table_name = 't7_fk'" utils.AssertMatchesAny(t, conn, query, `[[VARCHAR("t7_xxhash") VARCHAR("uid") VARCHAR("t7_uid") VARCHAR("t7_fk_ibfk_1") VARCHAR("CASCADE") VARCHAR("SET NULL")]]`, - `[[VARBINARY("t7_xxhash") VARCHAR("uid") VARCHAR("t7_uid") VARCHAR("t7_fk_ibfk_1") BINARY("CASCADE") BINARY("SET NULL")]]`) + `[[VARBINARY("t7_xxhash") VARCHAR("uid") VARCHAR("t7_uid") VARCHAR("t7_fk_ibfk_1") BINARY("CASCADE") BINARY("SET NULL")]]`, + `[[VARCHAR("t7_xxhash") VARCHAR("uid") VARCHAR("t7_uid") VARCHAR("t7_fk_ibfk_1") BINARY("CASCADE") BINARY("SET NULL")]]`) } func TestConnectWithSystemSchema(t *testing.T) { diff --git a/go/test/endtoend/vtgate/main_test.go b/go/test/endtoend/vtgate/main_test.go index 1d2bc59b50a..12abcf4dd01 100644 --- a/go/test/endtoend/vtgate/main_test.go +++ b/go/test/endtoend/vtgate/main_test.go @@ -73,7 +73,7 @@ func TestMain(m *testing.M) { VSchema: VSchema, } clusterInstance.VtGateExtraArgs = []string{"--schema_change_signal"} - clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-schema-change-signal", "--queryserver-config-schema-change-signal-interval", "0.1", "--queryserver-config-max-result-size", "100", "--queryserver-config-terse-errors"} + clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-schema-change-signal", "--queryserver-config-max-result-size", "100", "--queryserver-config-terse-errors"} err = clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 0, false) if err != nil { return 1 diff --git a/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go b/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go index 0b7b72a4f25..337ec3d2ff9 100644 --- a/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go +++ b/go/test/endtoend/vtgate/queries/informationschema/informationschema_test.go @@ -99,8 +99,8 @@ func TestInformationSchemaQueryGetsRoutedToTheRightTableAndKeyspace(t *testing.T utils.Exec(t, mcmp.VtConn, "insert into t1(id1, id2) values (1, 1), (2, 2), (3,3), (4,4)") - _ = utils.Exec(t, mcmp.VtConn, "SELECT /*vt+ PLANNER=gen4 */ * FROM t1000") // test that the routed table is available to us - result := utils.Exec(t, mcmp.VtConn, "SELECT /*vt+ PLANNER=gen4 */ * FROM information_schema.tables WHERE table_schema = database() and table_name='t1000'") + _ = utils.Exec(t, mcmp.VtConn, "SELECT * FROM t1000") // test that the routed table is available to us + result := utils.Exec(t, mcmp.VtConn, "SELECT * FROM information_schema.tables WHERE table_schema = database() and table_name='t1000'") assert.NotEmpty(t, result.Rows) } @@ -111,7 +111,8 @@ func TestFKConstraintUsingInformationSchema(t *testing.T) { query := "select fk.referenced_table_name as to_table, fk.referenced_column_name as primary_key, fk.column_name as `column`, fk.constraint_name as name, rc.update_rule as on_update, rc.delete_rule as on_delete from information_schema.referential_constraints as rc join information_schema.key_column_usage as fk on fk.constraint_schema = rc.constraint_schema and fk.constraint_name = rc.constraint_name where fk.referenced_column_name is not null and fk.table_schema = database() and fk.table_name = 't7_fk' and rc.constraint_schema = database() and rc.table_name = 't7_fk'" mcmp.AssertMatchesAny(query, `[[VARBINARY("t7_xxhash") VARCHAR("uid") VARCHAR("t7_uid") VARCHAR("t7_fk_ibfk_1") BINARY("CASCADE") BINARY("SET NULL")]]`, - `[[VARCHAR("t7_xxhash") VARCHAR("uid") VARCHAR("t7_uid") VARCHAR("t7_fk_ibfk_1") VARCHAR("CASCADE") VARCHAR("SET NULL")]]`) + `[[VARCHAR("t7_xxhash") VARCHAR("uid") VARCHAR("t7_uid") VARCHAR("t7_fk_ibfk_1") VARCHAR("CASCADE") VARCHAR("SET NULL")]]`, + `[[VARCHAR("t7_xxhash") VARCHAR("uid") VARCHAR("t7_uid") VARCHAR("t7_fk_ibfk_1") BINARY("CASCADE") BINARY("SET NULL")]]`) } func TestConnectWithSystemSchema(t *testing.T) { diff --git a/go/test/endtoend/vtgate/queries/informationschema/main_test.go b/go/test/endtoend/vtgate/queries/informationschema/main_test.go index c15c546dfc9..06c5b188d18 100644 --- a/go/test/endtoend/vtgate/queries/informationschema/main_test.go +++ b/go/test/endtoend/vtgate/queries/informationschema/main_test.go @@ -72,7 +72,7 @@ func TestMain(m *testing.M) { VSchema: vschema, } clusterInstance.VtGateExtraArgs = []string{"--schema_change_signal"} - clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-schema-change-signal", "--queryserver-config-schema-change-signal-interval", "0.1"} + clusterInstance.VtTabletExtraArgs = []string{"--queryserver-config-schema-change-signal"} err = clusterInstance.StartKeyspace(*keyspace, []string{"-80", "80-"}, 0, false) if err != nil { return 1 @@ -88,7 +88,6 @@ func TestMain(m *testing.M) { return 1 } - clusterInstance.VtGateExtraArgs = append(clusterInstance.VtGateExtraArgs, "--enable_system_settings=true") // Start vtgate err = clusterInstance.StartVtgate() if err != nil { diff --git a/go/vt/sqlparser/ast_funcs.go b/go/vt/sqlparser/ast_funcs.go index 0b7a0ea4541..1d7ab540c12 100644 --- a/go/vt/sqlparser/ast_funcs.go +++ b/go/vt/sqlparser/ast_funcs.go @@ -130,7 +130,7 @@ const ( type MatchAction int const ( - // DefaultAction indicates no action was explicitly specified. + // DefaultMatch indicates no action was explicitly specified. DefaultMatch MatchAction = iota Full Partial diff --git a/go/vt/vtgate/engine/cached_size.go b/go/vt/vtgate/engine/cached_size.go index 21a61f2ee62..26f407438a0 100644 --- a/go/vt/vtgate/engine/cached_size.go +++ b/go/vt/vtgate/engine/cached_size.go @@ -145,7 +145,7 @@ func (cached *DML) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(112) + size += int64(128) } // field Query string size += hack.RuntimeAllocSize(int64(len(cached.Query))) @@ -153,10 +153,17 @@ func (cached *DML) CachedSize(alloc bool) int64 { if cc, ok := cached.KsidVindex.(cachedObject); ok { size += cc.CachedSize(true) } - // field Table []*vitess.io/vitess/go/vt/vtgate/vindexes.Table + // field TableNames []string + { + size += hack.RuntimeAllocSize(int64(cap(cached.TableNames)) * int64(16)) + for _, elem := range cached.TableNames { + size += hack.RuntimeAllocSize(int64(len(elem))) + } + } + // field Vindexes []*vitess.io/vitess/go/vt/vtgate/vindexes.ColumnVindex { - size += hack.RuntimeAllocSize(int64(cap(cached.Table)) * int64(8)) - for _, elem := range cached.Table { + size += hack.RuntimeAllocSize(int64(cap(cached.Vindexes)) * int64(8)) + for _, elem := range cached.Vindexes { size += elem.CachedSize(true) } } @@ -306,7 +313,7 @@ func (cached *Insert) CachedSize(alloc bool) int64 { } size := int64(0) if alloc { - size += int64(224) + size += int64(240) } // field Keyspace *vitess.io/vitess/go/vt/vtgate/vindexes.Keyspace size += cached.Keyspace.CachedSize(true) @@ -338,8 +345,8 @@ func (cached *Insert) CachedSize(alloc bool) int64 { size += elem.CachedSize(true) } } - // field Table *vitess.io/vitess/go/vt/vtgate/vindexes.Table - size += cached.Table.CachedSize(true) + // field TableName string + size += hack.RuntimeAllocSize(int64(len(cached.TableName))) // field Generate *vitess.io/vitess/go/vt/vtgate/engine.Generate size += cached.Generate.CachedSize(true) // field Prefix string diff --git a/go/vt/vtgate/engine/delete.go b/go/vt/vtgate/engine/delete.go index 1db717450f8..e931d665b44 100644 --- a/go/vt/vtgate/engine/delete.go +++ b/go/vt/vtgate/engine/delete.go @@ -108,11 +108,7 @@ func (del *Delete) deleteVindexEntries(ctx context.Context, vcursor VCursor, bin return err } colnum := del.KsidLength - vindexTable, err := del.GetSingleTable() - if err != nil { - return err - } - for _, colVindex := range vindexTable.Owned { + for _, colVindex := range del.Vindexes { // Fetch the column values. colnum must keep incrementing. fromIds := make([]sqltypes.Value, 0, len(colVindex.Columns)) for range colVindex.Columns { diff --git a/go/vt/vtgate/engine/delete_test.go b/go/vt/vtgate/engine/delete_test.go index 8ee00edb2de..7312b4bd010 100644 --- a/go/vt/vtgate/engine/delete_test.go +++ b/go/vt/vtgate/engine/delete_test.go @@ -197,10 +197,9 @@ func TestDeleteOwnedVindex(t *testing.T) { Vindex: ks.Vindexes["hash"], Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, }, - Query: "dummy_delete", - Table: []*vindexes.Table{ - ks.Tables["t1"], - }, + Query: "dummy_delete", + TableNames: []string{ks.Tables["t1"].Name.String()}, + Vindexes: ks.Tables["t1"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["hash"], KsidLength: 1, @@ -285,10 +284,9 @@ func TestDeleteOwnedVindexMultiCol(t *testing.T) { Vindex: ks.Vindexes["rg_vdx"], Values: []evalengine.Expr{evalengine.NewLiteralInt(1), evalengine.NewLiteralInt(2)}, }, - Query: "dummy_delete", - Table: []*vindexes.Table{ - ks.Tables["rg_tbl"], - }, + Query: "dummy_delete", + TableNames: []string{ks.Tables["rg_tbl"].Name.String()}, + Vindexes: ks.Tables["rg_tbl"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["rg_vdx"], KsidLength: 2, @@ -368,10 +366,9 @@ func TestDeleteSharded(t *testing.T) { Opcode: Scatter, Keyspace: ks.Keyspace, }, - Query: "dummy_delete", - Table: []*vindexes.Table{ - ks.Tables["t2"], - }, + Query: "dummy_delete", + TableNames: []string{ks.Tables["t2"].Name.String()}, + Vindexes: ks.Tables["t2"].Owned, }, } @@ -397,10 +394,9 @@ func TestDeleteShardedStreaming(t *testing.T) { Opcode: Scatter, Keyspace: ks.Keyspace, }, - Query: "dummy_delete", - Table: []*vindexes.Table{ - ks.Tables["t2"], - }, + Query: "dummy_delete", + TableNames: []string{ks.Tables["t2"].Name.String()}, + Vindexes: ks.Tables["t2"].Owned, }, } @@ -423,10 +419,9 @@ func TestDeleteScatterOwnedVindex(t *testing.T) { Opcode: Scatter, Keyspace: ks.Keyspace, }, - Query: "dummy_delete", - Table: []*vindexes.Table{ - ks.Tables["t1"], - }, + Query: "dummy_delete", + TableNames: []string{ks.Tables["t1"].Name.String()}, + Vindexes: ks.Tables["t1"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["hash"], KsidLength: 1, @@ -515,10 +510,9 @@ func TestDeleteInChangedVindexMultiCol(t *testing.T) { evalengine.NewLiteralInt(3), }, }, - Query: "dummy_update", - Table: []*vindexes.Table{ - ks.Tables["rg_tbl"], - }, + Query: "dummy_update", + TableNames: []string{ks.Tables["rg_tbl"].Name.String()}, + Vindexes: ks.Tables["rg_tbl"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["rg_vdx"], KsidLength: 2, diff --git a/go/vt/vtgate/engine/dml.go b/go/vt/vtgate/engine/dml.go index 5201fe9f81e..51177f41e08 100644 --- a/go/vt/vtgate/engine/dml.go +++ b/go/vt/vtgate/engine/dml.go @@ -45,8 +45,11 @@ type DML struct { // KsidLength is number of columns that represents KsidVindex KsidLength int - // Table specifies the table for the update. - Table []*vindexes.Table + // TableNames are the name of the tables involved in the query. + TableNames []string + + // Vindexes are the column vindexes modified by this DML. + Vindexes []*vindexes.ColumnVindex // OwnedVindexQuery is used for updating changes in lookup vindexes. OwnedVindexQuery string @@ -103,29 +106,16 @@ func (dml *DML) GetKeyspaceName() string { // GetTableName specifies the table that this primitive routes to. func (dml *DML) GetTableName() string { - if dml.Table != nil { - tableNameMap := map[string]any{} - for _, table := range dml.Table { - tableNameMap[table.Name.String()] = nil - } - - var tableNames []string - for name := range tableNameMap { + sort.Strings(dml.TableNames) + var tableNames []string + var previousTbl string + for _, name := range dml.TableNames { + if name != previousTbl { tableNames = append(tableNames, name) + previousTbl = name } - sort.Strings(tableNames) - - return strings.Join(tableNames, ", ") - } - return "" -} - -// GetSingleTable returns single table used in dml. -func (dml *DML) GetSingleTable() (*vindexes.Table, error) { - if len(dml.Table) > 1 { - return nil, vterrors.Errorf(vtrpcpb.Code_UNIMPLEMENTED, "unsupported dml on complex table expression") } - return dml.Table[0], nil + return strings.Join(tableNames, ", ") } func allowOnlyPrimary(rss ...*srvtopo.ResolvedShard) error { diff --git a/go/vt/vtgate/engine/insert.go b/go/vt/vtgate/engine/insert.go index 014a1f8ef56..4100879fbd8 100644 --- a/go/vt/vtgate/engine/insert.go +++ b/go/vt/vtgate/engine/insert.go @@ -67,8 +67,8 @@ type ( // ColVindexes are the vindexes that will use the VindexValues ColVindexes []*vindexes.ColumnVindex - // Table specifies the table for the insert. - Table *vindexes.Table + // TableName is the name of the table on which row will be inserted. + TableName string // Generate is only set for inserts where a sequence must be generated. Generate *Generate @@ -135,16 +135,25 @@ func NewInsert( mid []string, suffix string, ) *Insert { - return &Insert{ + ins := &Insert{ Opcode: opcode, Ignore: ignore, Keyspace: keyspace, VindexValues: vindexValues, - Table: table, Prefix: prefix, Mid: mid, Suffix: suffix, } + if table != nil { + ins.TableName = table.Name.String() + for _, colVindex := range table.ColumnVindexes { + if colVindex.IsPartialVindex() { + continue + } + ins.ColVindexes = append(ins.ColVindexes, colVindex) + } + } + return ins } // Generate represents the instruction to generate @@ -208,10 +217,7 @@ func (ins *Insert) GetKeyspaceName() string { // GetTableName specifies the table that this primitive routes to. func (ins *Insert) GetTableName() string { - if ins.Table != nil { - return ins.Table.Name.String() - } - return "" + return ins.TableName } // TryExecute performs a non-streaming exec. @@ -384,10 +390,6 @@ func (ins *Insert) getInsertSelectQueries( rows []sqltypes.Row, ) ([]*srvtopo.ResolvedShard, []*querypb.BoundQuery, error) { colVindexes := ins.ColVindexes - if colVindexes == nil { - colVindexes = ins.Table.ColumnVindexes - } - if len(colVindexes) != len(ins.VindexValueOffset) { return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "vindex value offsets and vindex info do not match") } @@ -656,9 +658,6 @@ func (ins *Insert) getInsertShardedRoute( rowCount := 0 env := evalengine.NewExpressionEnv(ctx, bindVars, vcursor) colVindexes := ins.ColVindexes - if colVindexes == nil { - colVindexes = ins.Table.ColumnVindexes - } for vIdx, vColValues := range ins.VindexValues { if len(vColValues) != len(colVindexes[vIdx].Columns) { return nil, nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "[BUG] supplied vindex column values don't match vschema: %v %v", vColValues, colVindexes[vIdx].Columns) @@ -697,7 +696,7 @@ func (ins *Insert) getInsertShardedRoute( // results in an error. For 'ignore' type inserts, the keyspace // id is returned as nil, which is used later to drop the corresponding rows. if len(vindexRowsValues) == 0 || len(colVindexes) == 0 { - return nil, nil, vterrors.NewErrorf(vtrpcpb.Code_FAILED_PRECONDITION, vterrors.RequiresPrimaryKey, vterrors.PrimaryVindexNotSet, ins.Table.Name) + return nil, nil, vterrors.NewErrorf(vtrpcpb.Code_FAILED_PRECONDITION, vterrors.RequiresPrimaryKey, vterrors.PrimaryVindexNotSet, ins.TableName) } keyspaceIDs, err := ins.processPrimary(ctx, vcursor, vindexRowsValues[0], colVindexes[0]) if err != nil { diff --git a/go/vt/vtgate/engine/insert_test.go b/go/vt/vtgate/engine/insert_test.go index 72dec39045d..b651efe2b03 100644 --- a/go/vt/vtgate/engine/insert_test.go +++ b/go/vt/vtgate/engine/insert_test.go @@ -712,12 +712,6 @@ func TestInsertShardedGeo(t *testing.T) { []string{" mid1", " mid2"}, " suffix", ) - for _, colVindex := range ks.Tables["t1"].ColumnVindexes { - if colVindex.IsPartialVindex() { - continue - } - ins.ColVindexes = append(ins.ColVindexes, colVindex) - } vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20"} @@ -1511,7 +1505,6 @@ func TestInsertSelectSimple(t *testing.T) { Opcode: InsertSelect, Keyspace: ks.Keyspace, Query: "dummy_insert", - Table: ks.Tables["t1"], VindexValueOffset: [][]int{{1}}, Input: &Route{ Query: "dummy_select", @@ -1602,7 +1595,6 @@ func TestInsertSelectOwned(t *testing.T) { Opcode: InsertSelect, Keyspace: ks.Keyspace, Query: "dummy_insert", - Table: ks.Tables["t1"], VindexValueOffset: [][]int{ {1}, // The primary vindex has a single column as sharding key {0}}, // the onecol vindex uses the 'name' column @@ -1699,19 +1691,23 @@ func TestInsertSelectGenerate(t *testing.T) { vs := vindexes.BuildVSchema(invschema) ks := vs.Keyspaces["sharded"] - ins := &Insert{ - Opcode: InsertSelect, - Keyspace: ks.Keyspace, - Query: "dummy_insert", - Table: ks.Tables["t1"], - VindexValueOffset: [][]int{ - {1}}, // The primary vindex has a single column as sharding key - Input: &Route{ - Query: "dummy_select", - FieldQuery: "dummy_field_query", - RoutingParameters: &RoutingParameters{ - Opcode: Scatter, - Keyspace: ks.Keyspace}}} + ins := NewInsert( + InsertSelect, + false, + ks.Keyspace, + nil, + ks.Tables["t1"], + "prefix ", + nil, + " suffix") + ins.Query = "dummy_insert" + ins.VindexValueOffset = [][]int{{1}} // The primary vindex has a single column as sharding key + ins.Input = &Route{ + Query: "dummy_select", + FieldQuery: "dummy_field_query", + RoutingParameters: &RoutingParameters{ + Opcode: Scatter, + Keyspace: ks.Keyspace}} ins.Generate = &Generate{ Keyspace: &vindexes.Keyspace{ @@ -1721,8 +1717,6 @@ func TestInsertSelectGenerate(t *testing.T) { Query: "dummy_generate", Offset: 1, } - ins.Prefix = "prefix " - ins.Suffix = " suffix" vc := newDMLTestVCursor("-20", "20-") vc.shardForKsid = []string{"20-", "-20", "20-"} @@ -1795,7 +1789,6 @@ func TestStreamingInsertSelectGenerate(t *testing.T) { Opcode: InsertSelect, Keyspace: ks.Keyspace, Query: "dummy_insert", - Table: ks.Tables["t1"], VindexValueOffset: [][]int{ {1}}, // The primary vindex has a single column as sharding key Input: &Route{ @@ -1804,6 +1797,7 @@ func TestStreamingInsertSelectGenerate(t *testing.T) { RoutingParameters: &RoutingParameters{ Opcode: Scatter, Keyspace: ks.Keyspace}}} + ins.ColVindexes = ks.Tables["t1"].ColumnVindexes ins.Generate = &Generate{ Keyspace: &vindexes.Keyspace{ @@ -1891,7 +1885,6 @@ func TestInsertSelectGenerateNotProvided(t *testing.T) { Opcode: InsertSelect, Keyspace: ks.Keyspace, Query: "dummy_insert", - Table: ks.Tables["t1"], VindexValueOffset: [][]int{ {1}}, // The primary vindex has a single column as sharding key Input: &Route{ @@ -1901,6 +1894,7 @@ func TestInsertSelectGenerateNotProvided(t *testing.T) { Opcode: Scatter, Keyspace: ks.Keyspace}}} + ins.ColVindexes = ks.Tables["t1"].ColumnVindexes ins.Generate = &Generate{ Keyspace: &vindexes.Keyspace{ Name: "ks2", @@ -1979,7 +1973,6 @@ func TestStreamingInsertSelectGenerateNotProvided(t *testing.T) { Opcode: InsertSelect, Keyspace: ks.Keyspace, Query: "dummy_insert", - Table: ks.Tables["t1"], VindexValueOffset: [][]int{ {1}}, // The primary vindex has a single column as sharding key Input: &Route{ @@ -1989,6 +1982,7 @@ func TestStreamingInsertSelectGenerateNotProvided(t *testing.T) { Opcode: Scatter, Keyspace: ks.Keyspace}}} + ins.ColVindexes = ks.Tables["t1"].ColumnVindexes ins.Generate = &Generate{ Keyspace: &vindexes.Keyspace{ Name: "ks2", @@ -2077,7 +2071,6 @@ func TestInsertSelectUnowned(t *testing.T) { Opcode: InsertSelect, Keyspace: ks.Keyspace, Query: "dummy_insert", - Table: ks.Tables["t2"], VindexValueOffset: [][]int{ {0}}, // the onecol vindex as unowned lookup sharding column Input: &Route{ @@ -2199,7 +2192,6 @@ func TestInsertSelectShardingCases(t *testing.T) { Opcode: InsertSelect, Keyspace: sks1.Keyspace, Query: "dummy_insert", - Table: sks1.Tables["s1"], Prefix: "prefix ", Suffix: " suffix", ColVindexes: sks1.Tables["s1"].ColumnVindexes, @@ -2278,7 +2270,6 @@ func TestInsertSelectShardingCases(t *testing.T) { Opcode: InsertUnsharded, Keyspace: uks1.Keyspace, Query: "dummy_insert", - Table: uks1.Tables["s1"], Prefix: "prefix ", Suffix: " suffix", Input: sRoute, diff --git a/go/vt/vtgate/engine/update.go b/go/vt/vtgate/engine/update.go index 72aef9005c6..9ba06c6e0ee 100644 --- a/go/vt/vtgate/engine/update.go +++ b/go/vt/vtgate/engine/update.go @@ -128,11 +128,7 @@ func (upd *Update) updateVindexEntries(ctx context.Context, vcursor VCursor, bin return err } - vindexTable, err := upd.GetSingleTable() - if err != nil { - return err - } - for _, colVindex := range vindexTable.ColumnVindexes { + for _, colVindex := range upd.Vindexes { // Skip this vindex if no rows are being changed updColValues, ok := upd.ChangedVindexValues[colVindex.Name] if !ok { diff --git a/go/vt/vtgate/engine/update_test.go b/go/vt/vtgate/engine/update_test.go index f8d091879fb..026b23aa20d 100644 --- a/go/vt/vtgate/engine/update_test.go +++ b/go/vt/vtgate/engine/update_test.go @@ -247,10 +247,9 @@ func TestUpdateEqualChangedVindex(t *testing.T) { Vindex: ks.Vindexes["hash"], Values: []evalengine.Expr{evalengine.NewLiteralInt(1)}, }, - Query: "dummy_update", - Table: []*vindexes.Table{ - ks.Tables["t1"], - }, + Query: "dummy_update", + TableNames: []string{ks.Tables["t1"].Name.String()}, + Vindexes: ks.Tables["t1"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["hash"], KsidLength: 1, @@ -392,10 +391,9 @@ func TestUpdateEqualMultiColChangedVindex(t *testing.T) { Vindex: ks.Vindexes["rg_vdx"], Values: []evalengine.Expr{evalengine.NewLiteralInt(1), evalengine.NewLiteralInt(2)}, }, - Query: "dummy_update", - Table: []*vindexes.Table{ - ks.Tables["rg_tbl"], - }, + Query: "dummy_update", + TableNames: []string{ks.Tables["rg_tbl"].Name.String()}, + Vindexes: ks.Tables["rg_tbl"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["rg_vdx"], KsidLength: 2, @@ -513,10 +511,9 @@ func TestUpdateScatterChangedVindex(t *testing.T) { Opcode: Scatter, Keyspace: ks.Keyspace, }, - Query: "dummy_update", - Table: []*vindexes.Table{ - ks.Tables["t1"], - }, + Query: "dummy_update", + TableNames: []string{ks.Tables["t1"].Name.String()}, + Vindexes: ks.Tables["t1"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["hash"], KsidLength: 1, @@ -709,10 +706,9 @@ func TestUpdateInChangedVindex(t *testing.T) { evalengine.NewLiteralInt(2), }}, }, - Query: "dummy_update", - Table: []*vindexes.Table{ - ks.Tables["t1"], - }, + Query: "dummy_update", + TableNames: []string{ks.Tables["t1"].Name.String()}, + Vindexes: ks.Tables["t1"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["hash"], KsidLength: 1, @@ -840,10 +836,9 @@ func TestUpdateInChangedVindexMultiCol(t *testing.T) { evalengine.NewLiteralInt(3), }, }, - Query: "dummy_update", - Table: []*vindexes.Table{ - ks.Tables["rg_tbl"], - }, + Query: "dummy_update", + TableNames: []string{ks.Tables["rg_tbl"].Name.String()}, + Vindexes: ks.Tables["rg_tbl"].Owned, OwnedVindexQuery: "dummy_subquery", KsidVindex: ks.Vindexes["rg_vdx"], KsidLength: 2, diff --git a/go/vt/vtgate/planbuilder/delete.go b/go/vt/vtgate/planbuilder/delete.go index f462df6ce76..2c2641e8a3b 100644 --- a/go/vt/vtgate/planbuilder/delete.go +++ b/go/vt/vtgate/planbuilder/delete.go @@ -138,9 +138,11 @@ func rewriteSingleTbl(del *sqlparser.Delete) (*sqlparser.Delete, error) { func deleteUnshardedShortcut(stmt *sqlparser.Delete, ks *vindexes.Keyspace, tables []*vindexes.Table) logicalPlan { edml := engine.NewDML() edml.Keyspace = ks - edml.Table = tables edml.Opcode = engine.Unsharded edml.Query = generateQuery(stmt) + for _, tbl := range tables { + edml.TableNames = append(edml.TableNames, tbl.Name.String()) + } return &primitiveWrapper{prim: &engine.Delete{DML: edml}} } diff --git a/go/vt/vtgate/planbuilder/insert.go b/go/vt/vtgate/planbuilder/insert.go index c1c823d5def..864d1056908 100644 --- a/go/vt/vtgate/planbuilder/insert.go +++ b/go/vt/vtgate/planbuilder/insert.go @@ -94,7 +94,7 @@ func gen4InsertStmtPlanner(version querypb.ExecuteOptions_PlannerVersion, insStm func insertUnshardedShortcut(stmt *sqlparser.Insert, ks *vindexes.Keyspace, tables []*vindexes.Table) logicalPlan { eIns := &engine.Insert{} eIns.Keyspace = ks - eIns.Table = tables[0] + eIns.TableName = tables[0].Name.String() eIns.Opcode = engine.InsertUnsharded eIns.Query = generateQuery(stmt) return &insert{eInsert: eIns} diff --git a/go/vt/vtgate/planbuilder/operator_transformers.go b/go/vt/vtgate/planbuilder/operator_transformers.go index 260e39efd94..c0fab8a820c 100644 --- a/go/vt/vtgate/planbuilder/operator_transformers.go +++ b/go/vt/vtgate/planbuilder/operator_transformers.go @@ -402,7 +402,7 @@ func transformInsertPlan(ctx *plancontext.PlanningContext, op *operators.Route, eins := &engine.Insert{ Opcode: mapToInsertOpCode(op.Routing.OpCode(), ins.Input != nil), Keyspace: op.Routing.Keyspace(), - Table: ins.VTable, + TableName: ins.VTable.Name.String(), Ignore: ins.Ignore, ForceNonStreaming: ins.ForceNonStreaming, Generate: autoIncGenerate(ins.AutoIncrement), @@ -508,10 +508,9 @@ func transformUpdatePlan(ctx *plancontext.PlanningContext, op *operators.Route, return nil, err } edml := &engine.DML{ - Query: generateQuery(ast), - Table: []*vindexes.Table{ - upd.VTable, - }, + Query: generateQuery(ast), + TableNames: []string{upd.VTable.Name.String()}, + Vindexes: upd.VTable.ColumnVindexes, OwnedVindexQuery: upd.OwnedVindexQuery, RoutingParameters: rp, } @@ -535,10 +534,9 @@ func transformDeletePlan(ctx *plancontext.PlanningContext, op *operators.Route, return nil, err } edml := &engine.DML{ - Query: generateQuery(ast), - Table: []*vindexes.Table{ - del.VTable, - }, + Query: generateQuery(ast), + TableNames: []string{del.VTable.Name.String()}, + Vindexes: del.VTable.Owned, OwnedVindexQuery: del.OwnedVindexQuery, RoutingParameters: rp, } diff --git a/go/vt/vtgate/planbuilder/update.go b/go/vt/vtgate/planbuilder/update.go index d57853a5bd6..7813acb660f 100644 --- a/go/vt/vtgate/planbuilder/update.go +++ b/go/vt/vtgate/planbuilder/update.go @@ -94,8 +94,10 @@ func gen4UpdateStmtPlanner( func updateUnshardedShortcut(stmt *sqlparser.Update, ks *vindexes.Keyspace, tables []*vindexes.Table) logicalPlan { edml := engine.NewDML() edml.Keyspace = ks - edml.Table = tables edml.Opcode = engine.Unsharded edml.Query = generateQuery(stmt) + for _, tbl := range tables { + edml.TableNames = append(edml.TableNames, tbl.Name.String()) + } return &primitiveWrapper{prim: &engine.Update{DML: edml}} } diff --git a/go/vt/vtgate/schema/tracker.go b/go/vt/vtgate/schema/tracker.go index 9e64cb8c754..0df020869bf 100644 --- a/go/vt/vtgate/schema/tracker.go +++ b/go/vt/vtgate/schema/tracker.go @@ -18,6 +18,7 @@ package schema import ( "context" + "strings" "sync" "time" @@ -59,7 +60,7 @@ func NewTracker(ch chan *discovery.TabletHealth, enableViews bool) *Tracker { t := &Tracker{ ctx: context.Background(), ch: ch, - tables: &tableMap{m: map[keyspaceStr]map[tableNameStr][]vindexes.Column{}}, + tables: &tableMap{m: make(map[keyspaceStr]map[tableNameStr]*vindexes.TableInfo)}, tracked: map[keyspaceStr]*updateController{}, consumeDelay: defaultConsumeDelay, } @@ -197,17 +198,27 @@ func (t *Tracker) GetColumns(ks string, tbl string) []vindexes.Column { t.mu.Lock() defer t.mu.Unlock() - return t.tables.get(ks, tbl) + tblInfo := t.tables.get(ks, tbl) + return tblInfo.Columns +} + +// GetForeignKeys returns the foreign keys for table in the given keyspace. +func (t *Tracker) GetForeignKeys(ks string, tbl string) []*sqlparser.ForeignKeyDefinition { + t.mu.Lock() + defer t.mu.Unlock() + + tblInfo := t.tables.get(ks, tbl) + return tblInfo.ForeignKeys } // Tables returns a map with the columns for all known tables in the keyspace -func (t *Tracker) Tables(ks string) map[string][]vindexes.Column { +func (t *Tracker) Tables(ks string) map[string]*vindexes.TableInfo { t.mu.Lock() defer t.mu.Unlock() m := t.tables.m[ks] if m == nil { - return map[string][]vindexes.Column{} // we know nothing about this KS, so that is the info we can give out + return map[string]*vindexes.TableInfo{} // we know nothing about this KS, so that is the info we can give out } return m @@ -273,30 +284,60 @@ func (t *Tracker) updateTables(keyspace string, res map[string]string) { continue } - var collationName string - if ddl.TableSpec.Options != nil { - for _, option := range ddl.TableSpec.Options { - if option.Name == "" { - collationName = option.String - break - } - } + cols := getColumns(ddl.TableSpec) + fks := getForeignKeys(ddl.TableSpec) + t.tables.set(keyspace, tableName, cols, fks) + } +} + +func getColumns(tblSpec *sqlparser.TableSpec) []vindexes.Column { + tblCollation := getTableCollation(tblSpec) + cols := make([]vindexes.Column, 0, len(tblSpec.Columns)) + for _, column := range tblSpec.Columns { + colCollation := getColumnCollation(tblCollation, column) + cols = append(cols, + vindexes.Column{ + Name: column.Name, + Type: column.Type.SQLType(), + CollationName: colCollation, + }) + } + return cols +} + +func getForeignKeys(tblSpec *sqlparser.TableSpec) []*sqlparser.ForeignKeyDefinition { + if tblSpec.Constraints == nil { + return nil + } + var fks []*sqlparser.ForeignKeyDefinition + for _, constraint := range tblSpec.Constraints { + fkDef, ok := constraint.Details.(*sqlparser.ForeignKeyDefinition) + if !ok { + continue } - cols := make([]vindexes.Column, 0, len(ddl.TableSpec.Columns)) - for _, column := range ddl.TableSpec.Columns { - colCollation := collationName - if column.Type.Options != nil && column.Type.Options.Collate != "" { - colCollation = column.Type.Options.Collate - } - cols = append(cols, - vindexes.Column{ - Name: column.Name, - Type: column.Type.SQLType(), - CollationName: colCollation, - }) + fks = append(fks, fkDef) + } + return fks +} + +func getTableCollation(tblSpec *sqlparser.TableSpec) string { + if tblSpec.Options == nil { + return "" + } + collate := sqlparser.KeywordString(sqlparser.COLLATE) + for _, option := range tblSpec.Options { + if strings.EqualFold(option.Name, collate) { + return option.String } - t.tables.set(keyspace, tableName, cols) } + return "" +} + +func getColumnCollation(defaultCollation string, column *sqlparser.ColumnDefinition) string { + if column.Type.Options == nil || column.Type.Options.Collate == "" { + return defaultCollation + } + return column.Type.Options.Collate } func (t *Tracker) updatedViewSchema(th *discovery.TabletHealth) bool { @@ -351,22 +392,22 @@ func (t *Tracker) AddNewKeyspace(conn queryservice.QueryService, target *querypb } type tableMap struct { - m map[keyspaceStr]map[tableNameStr][]vindexes.Column + m map[keyspaceStr]map[tableNameStr]*vindexes.TableInfo } -func (tm *tableMap) set(ks, tbl string, cols []vindexes.Column) { +func (tm *tableMap) set(ks, tbl string, cols []vindexes.Column, fks []*sqlparser.ForeignKeyDefinition) { m := tm.m[ks] if m == nil { - m = make(map[tableNameStr][]vindexes.Column) + m = make(map[tableNameStr]*vindexes.TableInfo) tm.m[ks] = m } - m[tbl] = cols + m[tbl] = &vindexes.TableInfo{Columns: cols, ForeignKeys: fks} } -func (tm *tableMap) get(ks, tbl string) []vindexes.Column { +func (tm *tableMap) get(ks, tbl string) *vindexes.TableInfo { m := tm.m[ks] if m == nil { - return nil + return &vindexes.TableInfo{} } return m[tbl] } diff --git a/go/vt/vtgate/schema/tracker_test.go b/go/vt/vtgate/schema/tracker_test.go index 91baa2bc9ef..68b8bb206ce 100644 --- a/go/vt/vtgate/schema/tracker_test.go +++ b/go/vt/vtgate/schema/tracker_test.go @@ -63,97 +63,7 @@ func TestMain(m *testing.M) { os.Exit(exitCode) } -// TestTracking tests that the tracker is able to track tables. -func TestTracking(t *testing.T) { - target := &querypb.Target{Cell: cell, Keyspace: keyspace, Shard: "-80", TabletType: topodatapb.TabletType_PRIMARY} - tablet := &topodatapb.Tablet{Keyspace: target.Keyspace, Shard: target.Shard, Type: target.TabletType} - - schemaDefResult := []map[string]string{{ - "prior": "create table prior(id int primary key)", - }, { - "t1": "create table t1(id bigint primary key, name varchar(50))", - "t2": "create table t2(id varchar(50) primary key)", - }, { - "t2": "create table t2(id varchar(50) primary key, name varchar(50))", - "t3": "create table t3(id datetime primary key)", - }, { - "t4": "create table t4(name varchar(50) primary key)", - }} - - testcases := []struct { - testName string - updTbl []string - exp map[string][]vindexes.Column - }{{ - testName: "initial load", - exp: map[string][]vindexes.Column{ - "prior": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT32}}, - }, - }, { - testName: "new tables", - updTbl: []string{"t1", "t2"}, - exp: map[string][]vindexes.Column{ - "prior": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT32}}, - "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}}, - }, - }, { - testName: "delete prior, updated t2 and new t3", - updTbl: []string{"prior", "t2", "t3"}, - exp: map[string][]vindexes.Column{ - "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t3": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_DATETIME}}, - }, - }, { - testName: "new t4", - updTbl: []string{"t4"}, - exp: map[string][]vindexes.Column{ - "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - "t3": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_DATETIME}}, - "t4": {{Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, - }, - }} - - ch := make(chan *discovery.TabletHealth) - tracker := NewTracker(ch, false) - tracker.consumeDelay = 1 * time.Millisecond - tracker.Start() - defer tracker.Stop() - - wg := sync.WaitGroup{} - tracker.RegisterSignalReceiver(func() { - wg.Done() - }) - - sbc := sandboxconn.NewSandboxConn(tablet) - sbc.SetSchemaResult(schemaDefResult) - - for count, tcase := range testcases { - t.Run(tcase.testName, func(t *testing.T) { - wg.Add(1) - ch <- &discovery.TabletHealth{ - Conn: sbc, - Tablet: tablet, - Target: target, - Serving: true, - Stats: &querypb.RealtimeStats{TableSchemaChanged: tcase.updTbl}, - } - - require.False(t, waitTimeout(&wg, time.Second), "schema was updated but received no signal") - require.EqualValues(t, count+1, sbc.GetSchemaCount.Load()) - - _, keyspacePresent := tracker.tracked[target.Keyspace] - require.Equal(t, true, keyspacePresent) - - for k, v := range tcase.exp { - utils.MustMatch(t, v, tracker.GetColumns(keyspace, k), "mismatch for table: ", k) - } - }) - } -} - +// TestTrackingUnHealthyTablet tests that the tracker is sending GetSchema calls only when the tablet is healthy. func TestTrackingUnHealthyTablet(t *testing.T) { target := &querypb.Target{ Keyspace: keyspace, @@ -221,20 +131,7 @@ func TestTrackingUnHealthyTablet(t *testing.T) { require.EqualValues(t, 3, sbc.GetSchemaCount.Load()) } -func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { - c := make(chan struct{}) - go func() { - defer close(c) - wg.Wait() - }() - select { - case <-c: - return false // completed normally - case <-time.After(timeout): - return true // timed out - } -} - +// TestTrackerGetKeyspaceUpdateController tests table update controller initialization. func TestTrackerGetKeyspaceUpdateController(t *testing.T) { ks3 := &updateController{} tracker := Tracker{ @@ -267,15 +164,66 @@ func TestTrackerGetKeyspaceUpdateController(t *testing.T) { assert.Nil(t, ks3.reloadKeyspace, "ks3 already initialized") } +// TestTableTracking tests that the tracker is able to track table schema changes. +func TestTableTracking(t *testing.T) { + schemaDefResult := []map[string]string{{ + "prior": "create table prior(id int primary key)", + }, { + // initial load of view - kept empty + }, { + "t1": "create table t1(id bigint primary key, name varchar(50))", + "t2": "create table t2(id varchar(50) primary key)", + }, { + "t2": "create table t2(id varchar(50) primary key, name varchar(50))", + "t3": "create table t3(id datetime primary key)", + }, { + "t4": "create table t4(name varchar(50) primary key)", + }} + + testcases := []testCases{{ + testName: "initial table load", + expTbl: map[string][]vindexes.Column{ + "prior": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT32}}, + }, + }, { + testName: "new tables", + updTbl: []string{"t1", "t2"}, + expTbl: map[string][]vindexes.Column{ + "prior": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT32}}, + "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, + "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}}, + }, + }, { + testName: "delete prior, updated t2 and new t3", + updTbl: []string{"prior", "t2", "t3"}, + expTbl: map[string][]vindexes.Column{ + "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, + "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, + "t3": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_DATETIME}}, + }, + }, { + testName: "new t4", + updTbl: []string{"t4"}, + expTbl: map[string][]vindexes.Column{ + "t1": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, + "t2": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_VARCHAR}, {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, + "t3": {{Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_DATETIME}}, + "t4": {{Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR}}, + }, + }} + + testTracker(t, schemaDefResult, testcases) +} + // TestViewsTracking tests that the tracker is able to track views. func TestViewsTracking(t *testing.T) { - target := &querypb.Target{Cell: cell, Keyspace: keyspace, Shard: "-80", TabletType: topodatapb.TabletType_PRIMARY} - tablet := &topodatapb.Tablet{Keyspace: target.Keyspace, Shard: target.Shard, Type: target.TabletType} - schemaDefResult := []map[string]string{{ + // initial load of table - kept empty + }, { "prior": "create view prior as select 1 from tbl", - "t1": "create view t1 as select 1 from tbl1", - "t2": "create view t2 as select 1 from tbl2", + }, { + "t1": "create view t1 as select 1 from tbl1", + "t2": "create view t2 as select 1 from tbl2", }, { "t2": "create view t2 as select 1,2 from tbl2", "t3": "create view t3 as select 1 from tbl3", @@ -283,37 +231,109 @@ func TestViewsTracking(t *testing.T) { "t4": "create view t4 as select 1 from tbl4", }} - testcases := []struct { - testName string - updView []string - exp map[string]string - }{{ - testName: "new views", - updView: []string{"prior", "t1", "t2"}, - exp: map[string]string{ + testcases := []testCases{{ + testName: "initial view load", + expView: map[string]string{ + "prior": "select 1 from tbl"}, + }, { + testName: "new view t1, t2", + updView: []string{"t1", "t2"}, + expView: map[string]string{ "t1": "select 1 from tbl1", "t2": "select 1 from tbl2", "prior": "select 1 from tbl"}, }, { testName: "delete prior, updated t2 and new t3", updView: []string{"prior", "t2", "t3"}, - exp: map[string]string{ + expView: map[string]string{ "t1": "select 1 from tbl1", "t2": "select 1, 2 from tbl2", "t3": "select 1 from tbl3"}, }, { testName: "new t4", updView: []string{"t4"}, - exp: map[string]string{ + expView: map[string]string{ "t1": "select 1 from tbl1", "t2": "select 1, 2 from tbl2", "t3": "select 1 from tbl3", "t4": "select 1 from tbl4"}, }} + testTracker(t, schemaDefResult, testcases) +} + +// TestTableInfoRetrieval tests that the tracker is able to retrieve required information from ddl statement. +func TestTableInfoRetrieval(t *testing.T) { + schemaDefResult := []map[string]string{{ + "my_tbl": "CREATE TABLE `my_tbl` (" + + "`id` bigint NOT NULL AUTO_INCREMENT," + + "`name` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL," + + "`email` varbinary(100) DEFAULT NULL," + + "PRIMARY KEY (`id`)," + + "KEY `id` (`id`,`name`)) " + + "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", + }, { + // initial load of view - kept empty + }, { + "my_child_tbl": "CREATE TABLE `my_child_tbl` (" + + "`id` bigint NOT NULL AUTO_INCREMENT," + + "`name` varchar(50) CHARACTER SET latin1 COLLATE latin1_swedish_ci DEFAULT NULL," + + "`code` varchar(6) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL," + + "`my_id` bigint DEFAULT NULL," + + "PRIMARY KEY (`id`)," + + "KEY `my_id` (`my_id`,`name`)," + + "CONSTRAINT `my_child_tbl_ibfk_1` FOREIGN KEY (`my_id`, `name`) REFERENCES `my_tbl` (`id`, `name`) ON DELETE CASCADE) " + + "ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", + }} + + testcases := []testCases{{ + testName: "initial table load", + expTbl: map[string][]vindexes.Column{ + "my_tbl": { + {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "utf8mb4_0900_ai_ci"}, + {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, CollationName: "latin1_swedish_ci"}, + {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARBINARY, CollationName: "utf8mb4_0900_ai_ci"}, + }, + }, + }, { + testName: "new tables", + updTbl: []string{"my_child_tbl"}, + expTbl: map[string][]vindexes.Column{ + "my_tbl": { + {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "utf8mb4_0900_ai_ci"}, + {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, CollationName: "latin1_swedish_ci"}, + {Name: sqlparser.NewIdentifierCI("email"), Type: querypb.Type_VARBINARY, CollationName: "utf8mb4_0900_ai_ci"}, + }, + "my_child_tbl": { + {Name: sqlparser.NewIdentifierCI("id"), Type: querypb.Type_INT64, CollationName: "utf8mb4_0900_ai_ci"}, + {Name: sqlparser.NewIdentifierCI("name"), Type: querypb.Type_VARCHAR, CollationName: "latin1_swedish_ci"}, + {Name: sqlparser.NewIdentifierCI("code"), Type: querypb.Type_VARCHAR, CollationName: "utf8mb4_0900_ai_ci"}, + {Name: sqlparser.NewIdentifierCI("my_id"), Type: querypb.Type_INT64, CollationName: "utf8mb4_0900_ai_ci"}, + }, + }, + expFk: map[string]string{ + "my_tbl": "", + "my_child_tbl": "foreign key (my_id, `name`) references my_tbl (id, `name`) on delete cascade", + }, + }} + + testTracker(t, schemaDefResult, testcases) +} + +type testCases struct { + testName string + + updTbl []string + expTbl map[string][]vindexes.Column + expFk map[string]string + + updView []string + expView map[string]string +} + +func testTracker(t *testing.T, schemaDefResult []map[string]string, tcases []testCases) { ch := make(chan *discovery.TabletHealth) tracker := NewTracker(ch, true) - tracker.tables = nil // making tables map nil - so load keyspace does not try to load the tables information. tracker.consumeDelay = 1 * time.Millisecond tracker.Start() defer tracker.Stop() @@ -323,10 +343,13 @@ func TestViewsTracking(t *testing.T) { wg.Done() }) + target := &querypb.Target{Cell: cell, Keyspace: keyspace, Shard: "-80", TabletType: topodatapb.TabletType_PRIMARY} + tablet := &topodatapb.Tablet{Keyspace: target.Keyspace, Shard: target.Shard, Type: target.TabletType} + sbc := sandboxconn.NewSandboxConn(tablet) sbc.SetSchemaResult(schemaDefResult) - for count, tcase := range testcases { + for count, tcase := range tcases { t.Run(tcase.testName, func(t *testing.T) { wg.Add(1) ch <- &discovery.TabletHealth{ @@ -334,18 +357,42 @@ func TestViewsTracking(t *testing.T) { Tablet: tablet, Target: target, Serving: true, - Stats: &querypb.RealtimeStats{ViewSchemaChanged: tcase.updView}, + Stats: &querypb.RealtimeStats{TableSchemaChanged: tcase.updTbl, ViewSchemaChanged: tcase.updView}, } require.False(t, waitTimeout(&wg, time.Second), "schema was updated but received no signal") - require.EqualValues(t, count+1, sbc.GetSchemaCount.Load()) + require.EqualValues(t, count+2, sbc.GetSchemaCount.Load()) _, keyspacePresent := tracker.tracked[target.Keyspace] require.Equal(t, true, keyspacePresent) - for k, v := range tcase.exp { - utils.MustMatch(t, v, sqlparser.String(tracker.GetViews(keyspace, k)), "mismatch for table: ", k) + for k, v := range tcase.expTbl { + utils.MustMatch(t, v, tracker.GetColumns(keyspace, k), "mismatch columns for table: ", k) + if len(tcase.expFk[k]) > 0 { + fks := tracker.GetForeignKeys(keyspace, k) + for _, fk := range fks { + utils.MustMatch(t, tcase.expFk[k], sqlparser.String(fk), "mismatch foreign keys for table: ", k) + } + } + } + + for k, v := range tcase.expView { + utils.MustMatch(t, v, sqlparser.String(tracker.GetViews(keyspace, k)), "mismatch for view: ", k) } }) } } + +func waitTimeout(wg *sync.WaitGroup, timeout time.Duration) bool { + c := make(chan struct{}) + go func() { + defer close(c) + wg.Wait() + }() + select { + case <-c: + return false // completed normally + case <-time.After(timeout): + return true // timed out + } +} diff --git a/go/vt/vtgate/vindexes/cached_size.go b/go/vt/vtgate/vindexes/cached_size.go index c27c5f33485..a97411a6ac8 100644 --- a/go/vt/vtgate/vindexes/cached_size.go +++ b/go/vt/vtgate/vindexes/cached_size.go @@ -29,20 +29,6 @@ type cachedObject interface { CachedSize(alloc bool) int64 } -func (cached *AutoIncrement) CachedSize(alloc bool) int64 { - if cached == nil { - return int64(0) - } - size := int64(0) - if alloc { - size += int64(48) - } - // field Column vitess.io/vitess/go/vt/sqlparser.IdentifierCI - size += cached.Column.CachedSize(false) - // field Sequence *vitess.io/vitess/go/vt/vtgate/vindexes.Table - size += cached.Sequence.CachedSize(true) - return size -} func (cached *Binary) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -95,20 +81,6 @@ func (cached *CFC) CachedSize(alloc bool) int64 { size += cached.prefixCFC.CachedSize(true) return size } -func (cached *Column) CachedSize(alloc bool) int64 { - if cached == nil { - return int64(0) - } - size := int64(0) - if alloc { - size += int64(64) - } - // field Name vitess.io/vitess/go/vt/sqlparser.IdentifierCI - size += cached.Name.CachedSize(false) - // field CollationName string - size += hack.RuntimeAllocSize(int64(len(cached.CollationName))) - return size -} func (cached *ColumnVindex) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) @@ -517,87 +489,6 @@ func (cached *ReverseBits) CachedSize(alloc bool) int64 { } return size } -func (cached *Source) CachedSize(alloc bool) int64 { - if cached == nil { - return int64(0) - } - size := int64(0) - if alloc { - size += int64(32) - } - // field TableName vitess.io/vitess/go/vt/sqlparser.TableName - size += cached.TableName.CachedSize(false) - return size -} - -//go:nocheckptr -func (cached *Table) CachedSize(alloc bool) int64 { - if cached == nil { - return int64(0) - } - size := int64(0) - if alloc { - size += int64(192) - } - // field Type string - size += hack.RuntimeAllocSize(int64(len(cached.Type))) - // field Name vitess.io/vitess/go/vt/sqlparser.IdentifierCS - size += cached.Name.CachedSize(false) - // field Keyspace *vitess.io/vitess/go/vt/vtgate/vindexes.Keyspace - size += cached.Keyspace.CachedSize(true) - // field ColumnVindexes []*vitess.io/vitess/go/vt/vtgate/vindexes.ColumnVindex - { - size += hack.RuntimeAllocSize(int64(cap(cached.ColumnVindexes)) * int64(8)) - for _, elem := range cached.ColumnVindexes { - size += elem.CachedSize(true) - } - } - // field Ordered []*vitess.io/vitess/go/vt/vtgate/vindexes.ColumnVindex - { - size += hack.RuntimeAllocSize(int64(cap(cached.Ordered)) * int64(8)) - for _, elem := range cached.Ordered { - size += elem.CachedSize(true) - } - } - // field Owned []*vitess.io/vitess/go/vt/vtgate/vindexes.ColumnVindex - { - size += hack.RuntimeAllocSize(int64(cap(cached.Owned)) * int64(8)) - for _, elem := range cached.Owned { - size += elem.CachedSize(true) - } - } - // field AutoIncrement *vitess.io/vitess/go/vt/vtgate/vindexes.AutoIncrement - size += cached.AutoIncrement.CachedSize(true) - // field Columns []vitess.io/vitess/go/vt/vtgate/vindexes.Column - { - size += hack.RuntimeAllocSize(int64(cap(cached.Columns)) * int64(56)) - for _, elem := range cached.Columns { - size += elem.CachedSize(false) - } - } - // field Pinned []byte - { - size += hack.RuntimeAllocSize(int64(cap(cached.Pinned))) - } - // field ReferencedBy map[string]*vitess.io/vitess/go/vt/vtgate/vindexes.Table - if cached.ReferencedBy != nil { - size += int64(48) - hmap := reflect.ValueOf(cached.ReferencedBy) - numBuckets := int(math.Pow(2, float64((*(*uint8)(unsafe.Pointer(hmap.Pointer() + uintptr(9))))))) - numOldBuckets := (*(*uint16)(unsafe.Pointer(hmap.Pointer() + uintptr(10)))) - size += hack.RuntimeAllocSize(int64(numOldBuckets * 208)) - if len(cached.ReferencedBy) > 0 || numBuckets > 1 { - size += hack.RuntimeAllocSize(int64(numBuckets * 208)) - } - for k, v := range cached.ReferencedBy { - size += hack.RuntimeAllocSize(int64(len(k))) - size += v.CachedSize(true) - } - } - // field Source *vitess.io/vitess/go/vt/vtgate/vindexes.Source - size += cached.Source.CachedSize(true) - return size -} func (cached *UnicodeLooseMD5) CachedSize(alloc bool) int64 { if cached == nil { return int64(0) diff --git a/go/vt/vtgate/vindexes/vschema.go b/go/vt/vtgate/vindexes/vschema.go index 90becdf275f..2819a5375ec 100644 --- a/go/vt/vtgate/vindexes/vschema.go +++ b/go/vt/vtgate/vindexes/vschema.go @@ -111,6 +111,9 @@ type Table struct { // Source is a keyspace-qualified table name that points to the source of a // reference table. Only applicable for tables with Type set to "reference". Source *Source `json:"source,omitempty"` + + ChildForeignKeys []ChildFKInfo `json:"child_foreign_keys,omitempty"` + ParentForeignKeys []ParentFKInfo `json:"parent_foreign_keys,omitempty"` } // Keyspace contains the keyspcae info for each Table. @@ -132,6 +135,76 @@ type ColumnVindex struct { backfill bool } +// ParentFKInfo contains the parent foreign key info for the table. +type ParentFKInfo struct { + Table *Table + ParentColumns sqlparser.Columns + ChildColumns sqlparser.Columns +} + +// MarshalJSON returns a JSON representation of ParentFKInfo. +func (fk *ParentFKInfo) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Name string `json:"parent_table"` + ParentColumns sqlparser.Columns `json:"parent_columns"` + ChildColumns sqlparser.Columns `json:"child_columns"` + }{ + Name: fk.Table.Name.String(), + ChildColumns: fk.ChildColumns, + ParentColumns: fk.ParentColumns, + }) +} + +// NewParentFkInfo creates a new ParentFKInfo. +func NewParentFkInfo(parentTbl *Table, fkDef *sqlparser.ForeignKeyDefinition) ParentFKInfo { + return ParentFKInfo{ + Table: parentTbl, + ChildColumns: fkDef.Source, + ParentColumns: fkDef.ReferenceDefinition.ReferencedColumns, + } +} + +// ChildFKInfo contains the child foreign key info for the table. +type ChildFKInfo struct { + Table *Table + ChildColumns sqlparser.Columns + ParentColumns sqlparser.Columns + Match sqlparser.MatchAction + OnDelete sqlparser.ReferenceAction + OnUpdate sqlparser.ReferenceAction +} + +// MarshalJSON returns a JSON representation of ChildFKInfo. +func (fk *ChildFKInfo) MarshalJSON() ([]byte, error) { + return json.Marshal(struct { + Name string `json:"child_table"` + ChildColumns sqlparser.Columns `json:"child_columns"` + ParentColumns sqlparser.Columns `json:"parent_columns"` + }{ + Name: fk.Table.Name.String(), + ChildColumns: fk.ChildColumns, + ParentColumns: fk.ParentColumns, + }) +} + +// NewChildFkInfo creates a new ChildFKInfo. +func NewChildFkInfo(childTbl *Table, fkDef *sqlparser.ForeignKeyDefinition) ChildFKInfo { + return ChildFKInfo{ + Table: childTbl, + ChildColumns: fkDef.Source, + ParentColumns: fkDef.ReferenceDefinition.ReferencedColumns, + Match: fkDef.ReferenceDefinition.Match, + OnDelete: fkDef.ReferenceDefinition.OnDelete, + OnUpdate: fkDef.ReferenceDefinition.OnUpdate, + } +} + +// TableInfo contains column and foreign key info for a table. +type TableInfo struct { + Columns []Column + ForeignKeys []*sqlparser.ForeignKeyDefinition +} + // IsUnique is used to tell whether the ColumnVindex // will return a unique shard value or not when queried with // the given column list @@ -324,6 +397,26 @@ func replaceDefaultForeignKeyMode(fkMode vschemapb.Keyspace_ForeignKeyMode) vsch return fkMode } +// addForeignKey is for testing only. +func (vschema *VSchema) addForeignKey(ksname, childTableName string, fkConstraint *sqlparser.ForeignKeyDefinition) error { + ks, ok := vschema.Keyspaces[ksname] + if !ok { + return fmt.Errorf("keyspace %s not found in vschema", ksname) + } + cTbl, ok := ks.Tables[childTableName] + if !ok { + return fmt.Errorf("child table %s not found in keyspace %s", childTableName, ksname) + } + parentTableName := fkConstraint.ReferenceDefinition.ReferencedTable.Name.String() + pTbl, ok := ks.Tables[parentTableName] + if !ok { + return fmt.Errorf("parent table %s not found in keyspace %s", parentTableName, ksname) + } + pTbl.ChildForeignKeys = append(pTbl.ChildForeignKeys, NewChildFkInfo(cTbl, fkConstraint)) + cTbl.ParentForeignKeys = append(cTbl.ParentForeignKeys, NewParentFkInfo(pTbl, fkConstraint)) + return nil +} + func (vschema *VSchema) AddView(ksname string, viewName, query string) error { ks, ok := vschema.Keyspaces[ksname] if !ok { diff --git a/go/vt/vtgate/vindexes/vschema_test.go b/go/vt/vtgate/vindexes/vschema_test.go index c7257e0a633..7ace00a3569 100644 --- a/go/vt/vtgate/vindexes/vschema_test.go +++ b/go/vt/vtgate/vindexes/vschema_test.go @@ -380,6 +380,76 @@ func TestVSchemaViews(t *testing.T) { require.JSONEq(t, want, got) } +func TestVSchemaForeignKeys(t *testing.T) { + good := vschemapb.SrvVSchema{ + Keyspaces: map[string]*vschemapb.Keyspace{ + "unsharded": { + Tables: map[string]*vschemapb.Table{ + "t1": { + Columns: []*vschemapb.Column{{ + Name: "c1", + }, { + Name: "c2", + Type: sqltypes.VarChar}}}}}, + "main": { + Tables: map[string]*vschemapb.Table{ + "t1": { + Columns: []*vschemapb.Column{{ + Name: "c1", + }, { + Name: "c2", + Type: sqltypes.VarChar}}}}}}} + vschema := BuildVSchema(&good) + require.NoError(t, vschema.Keyspaces["main"].Error) + + // add fk containst a keyspace. + vschema.addForeignKey("main", "t1", &sqlparser.ForeignKeyDefinition{ + Source: sqlparser.Columns{sqlparser.NewIdentifierCI("c2")}, + ReferenceDefinition: &sqlparser.ReferenceDefinition{ + ReferencedTable: sqlparser.NewTableName("t1"), + ReferencedColumns: sqlparser.Columns{sqlparser.NewIdentifierCI("c1")}, + }, + }) + + out, err := json.MarshalIndent(vschema.Keyspaces["main"], "", " ") + require.NoError(t, err) + want := ` +{ + "foreignKeyMode": "FK_UNMANAGED", + "tables": { + "t1": { + "name": "t1", + "columns": [ + { + "name": "c1", + "type": "NULL_TYPE" + }, + { + "name": "c2", + "type": "VARCHAR" + } + ], + "parent_foreign_keys": [ + { + "parent_table": "t1", + "parent_columns": ["c1"], + "child_columns": ["c2"] + } + ], + "child_foreign_keys": [ + { + "child_table": "t1", + "child_columns": ["c2"], + "parent_columns": ["c1"] + } + ] + } + } +}` + got := string(out) + require.JSONEq(t, want, got) +} + func TestVSchemaColumnListAuthoritative(t *testing.T) { good := vschemapb.SrvVSchema{ Keyspaces: map[string]*vschemapb.Keyspace{ diff --git a/go/vt/vtgate/vschema_manager.go b/go/vt/vtgate/vschema_manager.go index baa232a87d8..5a06e698858 100644 --- a/go/vt/vtgate/vschema_manager.go +++ b/go/vt/vtgate/vschema_manager.go @@ -20,6 +20,7 @@ import ( "context" "sync" + topodatapb "vitess.io/vitess/go/vt/proto/topodata" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/srvtopo" @@ -48,7 +49,7 @@ type VSchemaManager struct { // SchemaInfo is an interface to schema tracker. type SchemaInfo interface { - Tables(ks string) map[string][]vindexes.Column + Tables(ks string) map[string]*vindexes.TableInfo Views(ks string) map[string]sqlparser.SelectStatement } @@ -192,22 +193,21 @@ func (vm *VSchemaManager) updateFromSchema(vschema *vindexes.VSchema) { for ksName, ks := range vschema.Keyspaces { m := vm.schema.Tables(ksName) - for tblName, columns := range m { - vTbl := ks.Tables[tblName] - if vTbl == nil { - // a table that is unknown by the vschema. we add it as a normal table - ks.Tables[tblName] = &vindexes.Table{ - Name: sqlparser.NewIdentifierCS(tblName), - Keyspace: ks.Keyspace, - Columns: columns, - ColumnListAuthoritative: true, + for tblName, tblInfo := range m { + vTbl := setColumns(ks, tblName, tblInfo.Columns) + for _, fkDef := range tblInfo.ForeignKeys { + parentTbl, err := vschema.FindRoutedTable(ksName, fkDef.ReferenceDefinition.ReferencedTable.Name.String(), topodatapb.TabletType_PRIMARY) + if err != nil { + log.Errorf("error finding parent table %s: %v", fkDef.ReferenceDefinition.ReferencedTable.Name.String(), err) + continue } - continue - } - if !vTbl.ColumnListAuthoritative { - // if we found the matching table and the vschema view of it is not authoritative, then we just update the columns of the table - vTbl.Columns = columns - vTbl.ColumnListAuthoritative = true + childTbl, err := vschema.FindRoutedTable(ksName, tblName, topodatapb.TabletType_PRIMARY) + if err != nil { + log.Errorf("error finding child table %s: %v", tblName, err) + continue + } + vTbl.ParentForeignKeys = append(vTbl.ParentForeignKeys, vindexes.NewParentFkInfo(parentTbl, fkDef)) + parentTbl.ChildForeignKeys = append(parentTbl.ChildForeignKeys, vindexes.NewChildFkInfo(childTbl, fkDef)) } } @@ -220,3 +220,23 @@ func (vm *VSchemaManager) updateFromSchema(vschema *vindexes.VSchema) { } } } + +func setColumns(ks *vindexes.KeyspaceSchema, tblName string, columns []vindexes.Column) *vindexes.Table { + vTbl := ks.Tables[tblName] + if vTbl == nil { + // a table that is unknown by the vschema. we add it as a normal table + ks.Tables[tblName] = &vindexes.Table{ + Name: sqlparser.NewIdentifierCS(tblName), + Keyspace: ks.Keyspace, + Columns: columns, + ColumnListAuthoritative: true, + } + return ks.Tables[tblName] + } + // if we found the matching table and the vschema view of it is not authoritative, then we just update the columns of the table + if !vTbl.ColumnListAuthoritative { + vTbl.Columns = columns + vTbl.ColumnListAuthoritative = true + } + return ks.Tables[tblName] +} diff --git a/go/vt/vtgate/vschema_manager_test.go b/go/vt/vtgate/vschema_manager_test.go index 4aa81442f38..2a307d5a43b 100644 --- a/go/vt/vtgate/vschema_manager_test.go +++ b/go/vt/vtgate/vschema_manager_test.go @@ -33,7 +33,7 @@ func TestVSchemaUpdate(t *testing.T) { name string srvVschema *vschemapb.SrvVSchema currentVSchema *vindexes.VSchema - schema map[string][]vindexes.Column + schema map[string]*vindexes.TableInfo expected *vindexes.VSchema }{{ name: "0 Schematracking- 1 srvVSchema", @@ -47,12 +47,12 @@ func TestVSchemaUpdate(t *testing.T) { }, { name: "1 Schematracking- 0 srvVSchema", srvVschema: makeTestSrvVSchema("ks", false, nil), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblCol1}), }, { name: "1 Schematracking - 1 srvVSchema (no columns) not authoritative", srvVschema: makeTestSrvVSchema("ks", false, map[string]*vschemapb.Table{"tbl": {}}), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, // schema will override what srvSchema has. expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblCol1}), }, { @@ -63,7 +63,7 @@ func TestVSchemaUpdate(t *testing.T) { ColumnListAuthoritative: false, }, }), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, // schema will override what srvSchema has. expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblCol1}), }, { @@ -71,7 +71,7 @@ func TestVSchemaUpdate(t *testing.T) { srvVschema: makeTestSrvVSchema("ks", false, map[string]*vschemapb.Table{"tbl": { ColumnListAuthoritative: true, }}), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, // schema will override what srvSchema has. expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblNoCol}), }, { @@ -82,17 +82,17 @@ func TestVSchemaUpdate(t *testing.T) { ColumnListAuthoritative: true, }, }), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, // schema tracker will be ignored for authoritative tables. expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblCol2}), }, { name: "srvVschema received as nil", - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, expected: makeTestEmptyVSchema(), }, { name: "srvVschema received as nil - have existing vschema", currentVSchema: &vindexes.VSchema{}, - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, expected: &vindexes.VSchema{}, }} @@ -138,7 +138,7 @@ func TestRebuildVSchema(t *testing.T) { tcases := []struct { name string srvVschema *vschemapb.SrvVSchema - schema map[string][]vindexes.Column + schema map[string]*vindexes.TableInfo expected *vindexes.VSchema }{{ name: "0 Schematracking- 1 srvVSchema", @@ -152,12 +152,12 @@ func TestRebuildVSchema(t *testing.T) { }, { name: "1 Schematracking- 0 srvVSchema", srvVschema: makeTestSrvVSchema("ks", false, nil), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblCol1}), }, { name: "1 Schematracking - 1 srvVSchema (no columns) not authoritative", srvVschema: makeTestSrvVSchema("ks", false, map[string]*vschemapb.Table{"tbl": {}}), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, // schema will override what srvSchema has. expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblCol1}), }, { @@ -168,7 +168,7 @@ func TestRebuildVSchema(t *testing.T) { ColumnListAuthoritative: false, }, }), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, // schema will override what srvSchema has. expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblCol1}), }, { @@ -176,7 +176,7 @@ func TestRebuildVSchema(t *testing.T) { srvVschema: makeTestSrvVSchema("ks", false, map[string]*vschemapb.Table{"tbl": { ColumnListAuthoritative: true, }}), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, // schema will override what srvSchema has. expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblNoCol}), }, { @@ -187,12 +187,12 @@ func TestRebuildVSchema(t *testing.T) { ColumnListAuthoritative: true, }, }), - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, // schema tracker will be ignored for authoritative tables. expected: makeTestVSchema("ks", false, map[string]*vindexes.Table{"tbl": tblCol2}), }, { name: "srvVschema received as nil", - schema: map[string][]vindexes.Column{"tbl": cols1}, + schema: map[string]*vindexes.TableInfo{"tbl": {Columns: cols1}}, }} vm := &VSchemaManager{} @@ -252,14 +252,14 @@ func makeTestSrvVSchema(ks string, sharded bool, tbls map[string]*vschemapb.Tabl } type fakeSchema struct { - t map[string][]vindexes.Column + t map[string]*vindexes.TableInfo } -func (f *fakeSchema) Tables(string) map[string][]vindexes.Column { +func (f *fakeSchema) Tables(string) map[string]*vindexes.TableInfo { return f.t } -func (f *fakeSchema) Views(ks string) map[string]sqlparser.SelectStatement { +func (f *fakeSchema) Views(string) map[string]sqlparser.SelectStatement { return nil }