From eddb39efe4e0ef54f160c3c555a30c685246685f Mon Sep 17 00:00:00 2001 From: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> Date: Fri, 12 Jan 2024 08:18:31 +0200 Subject: [PATCH] `schemadiff`: fix diffing of textual columns with implicit charsets (#14930) Signed-off-by: Shlomi Noach <2607934+shlomi-noach@users.noreply.github.com> Signed-off-by: Dirkjan Bussink Co-authored-by: Dirkjan Bussink --- go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go | 2 +- go/cmd/vtctl/vtctl.go | 5 +- go/cmd/vtctld/cli/plugin_grpcvtctldserver.go | 2 +- go/cmd/vtctldclient/command/root.go | 7 +- .../command/vreplication/common/utils_test.go | 3 +- .../vreplication/vdiff/vdiff_env_test.go | 3 +- .../vrepl/schemadiff_vrepl_suite_test.go | 20 +- go/vt/schemadiff/column.go | 95 +++++++++- go/vt/schemadiff/diff.go | 48 ++--- go/vt/schemadiff/diff_test.go | 172 ++++++++++++++--- go/vt/schemadiff/env.go | 28 +++ go/vt/schemadiff/schema.go | 34 ++-- go/vt/schemadiff/schema_diff_test.go | 12 +- go/vt/schemadiff/schema_test.go | 38 ++-- go/vt/schemadiff/semantics.go | 8 +- go/vt/schemadiff/table.go | 128 ++++++------- go/vt/schemadiff/table_test.go | 55 ++++-- go/vt/schemadiff/view.go | 5 +- go/vt/schemadiff/view_test.go | 18 +- go/vt/sidecardb/sidecardb.go | 47 ++++- go/vt/sidecardb/sidecardb_test.go | 19 +- go/vt/vtadmin/api_test.go | 22 +-- go/vt/vtadmin/testutil/cluster.go | 3 +- go/vt/vtctl/grpcvtctldclient/client_test.go | 7 +- .../endtoend/init_shard_primary_test.go | 4 +- go/vt/vtctl/grpcvtctldserver/server.go | 11 +- .../grpcvtctldserver/server_slow_test.go | 7 +- go/vt/vtctl/grpcvtctldserver/server_test.go | 177 +++++++++--------- go/vt/vtctl/workflow/materializer.go | 7 +- go/vt/vtctl/workflow/materializer_env_test.go | 3 +- go/vt/vtctl/workflow/materializer_test.go | 3 +- go/vt/vtctl/workflow/server.go | 33 ++-- go/vt/vtctl/workflow/server_test.go | 3 +- go/vt/vtexplain/vtexplain_vttablet.go | 9 + go/vt/vttablet/onlineddl/executor.go | 5 +- go/vt/vttablet/onlineddl/vrepl.go | 2 +- go/vt/vttablet/onlineddl/vrepl/foreign_key.go | 5 +- .../onlineddl/vrepl/foreign_key_test.go | 3 +- .../tabletmanager/rpc_vreplication_test.go | 9 +- .../vreplication/vreplicator_test.go | 3 +- go/vt/vttablet/tabletserver/schema/engine.go | 2 +- go/vt/vttest/local_cluster.go | 2 +- go/vt/wrangler/materializer.go | 5 +- go/vt/wrangler/reparent.go | 2 +- go/vt/wrangler/traffic_switcher.go | 4 +- go/vt/wrangler/vdiff.go | 3 +- go/vt/wrangler/vdiff_test.go | 4 +- go/vt/wrangler/wrangler.go | 2 +- 48 files changed, 707 insertions(+), 382 deletions(-) create mode 100644 go/vt/schemadiff/env.go diff --git a/go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go b/go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go index 62a5e2bb358..19c72435faf 100644 --- a/go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go +++ b/go/cmd/vtcombo/cli/plugin_grpcvtctldserver.go @@ -24,7 +24,7 @@ import ( func init() { servenv.OnRun(func() { if servenv.GRPCCheckServiceMap("vtctld") { - grpcvtctldserver.StartServer(servenv.GRPCServer, ts, parser) + grpcvtctldserver.StartServer(servenv.GRPCServer, ts, collationEnv, parser) } }) } diff --git a/go/cmd/vtctl/vtctl.go b/go/cmd/vtctl/vtctl.go index 8979028ea23..c659953ef39 100644 --- a/go/cmd/vtctl/vtctl.go +++ b/go/cmd/vtctl/vtctl.go @@ -131,6 +131,8 @@ func main() { ctx, cancel := context.WithTimeout(context.Background(), waitTime) installSignalHandlers(cancel) + collationEnv := collations.NewEnvironment(servenv.MySQLServerVersion()) + parser, err := sqlparser.New(sqlparser.Options{ MySQLServerVersion: servenv.MySQLServerVersion(), TruncateUILen: servenv.TruncateUILen, @@ -164,7 +166,7 @@ func main() { // New behavior. Strip off the prefix, and set things up to run through // the vtctldclient command tree, using the localvtctldclient (in-process) // client. - vtctld := grpcvtctldserver.NewVtctldServer(ts, parser) + vtctld := grpcvtctldserver.NewVtctldServer(ts, collationEnv, parser) localvtctldclient.SetServer(vtctld) command.VtctldClientProtocol = "local" @@ -180,7 +182,6 @@ func main() { fallthrough default: log.Warningf("WARNING: vtctl should only be used for VDiff v1 workflows. Please use VDiff v2 and consider using vtctldclient for all other commands.") - collationEnv := collations.NewEnvironment(servenv.MySQLServerVersion()) wr := wrangler.New(logutil.NewConsoleLogger(), ts, tmclient.NewTabletManagerClient(), collationEnv, parser) if args[0] == "--" { diff --git a/go/cmd/vtctld/cli/plugin_grpcvtctldserver.go b/go/cmd/vtctld/cli/plugin_grpcvtctldserver.go index 3385160e9f8..9966adc7544 100644 --- a/go/cmd/vtctld/cli/plugin_grpcvtctldserver.go +++ b/go/cmd/vtctld/cli/plugin_grpcvtctldserver.go @@ -24,7 +24,7 @@ import ( func init() { servenv.OnRun(func() { if servenv.GRPCCheckServiceMap("vtctld") { - grpcvtctldserver.StartServer(servenv.GRPCServer, ts, parser) + grpcvtctldserver.StartServer(servenv.GRPCServer, ts, collationEnv, parser) } }) } diff --git a/go/cmd/vtctldclient/command/root.go b/go/cmd/vtctldclient/command/root.go index 9e6b2df170b..5cd44575e03 100644 --- a/go/cmd/vtctldclient/command/root.go +++ b/go/cmd/vtctldclient/command/root.go @@ -28,6 +28,7 @@ import ( "github.com/spf13/cobra" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/trace" "vitess.io/vitess/go/vt/log" "vitess.io/vitess/go/vt/logutil" @@ -82,7 +83,8 @@ var ( actionTimeout time.Duration compactOutput bool - parser *sqlparser.Parser + collationEnv *collations.Environment + parser *sqlparser.Parser topoOptions = struct { implementation string @@ -212,7 +214,7 @@ func getClientForCommand(cmd *cobra.Command) (vtctldclient.VtctldClient, error) return nil }) }) - vtctld := grpcvtctldserver.NewVtctldServer(ts, parser) + vtctld := grpcvtctldserver.NewVtctldServer(ts, collationEnv, parser) localvtctldclient.SetServer(vtctld) VtctldClientProtocol = "local" server = "" @@ -230,6 +232,7 @@ func init() { Root.PersistentFlags().StringVar(&topoOptions.globalRoot, "topo-global-root", topoOptions.globalRoot, "the path of the global topology data in the global topology server") vreplcommon.RegisterCommands(Root) + collationEnv = collations.NewEnvironment(servenv.MySQLServerVersion()) var err error parser, err = sqlparser.New(sqlparser.Options{ MySQLServerVersion: servenv.MySQLServerVersion(), diff --git a/go/cmd/vtctldclient/command/vreplication/common/utils_test.go b/go/cmd/vtctldclient/command/vreplication/common/utils_test.go index 0660cb6d742..8c86a159140 100644 --- a/go/cmd/vtctldclient/command/vreplication/common/utils_test.go +++ b/go/cmd/vtctldclient/command/vreplication/common/utils_test.go @@ -26,6 +26,7 @@ import ( "vitess.io/vitess/go/cmd/vtctldclient/command" "vitess.io/vitess/go/cmd/vtctldclient/command/vreplication/common" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" "vitess.io/vitess/go/vt/topo/memorytopo" @@ -145,7 +146,7 @@ func SetupLocalVtctldClient(t *testing.T, ctx context.Context, cells ...string) tmclient.RegisterTabletManagerClientFactory("grpc", func() tmclient.TabletManagerClient { return nil }) - vtctld := grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + vtctld := grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) localvtctldclient.SetServer(vtctld) command.VtctldClientProtocol = "local" client, err := vtctldclient.New(command.VtctldClientProtocol, "") diff --git a/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_env_test.go b/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_env_test.go index 23a4f2e0bbd..a274d68c2f0 100644 --- a/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_env_test.go +++ b/go/cmd/vtctldclient/command/vreplication/vdiff/vdiff_env_test.go @@ -25,6 +25,7 @@ import ( "sync" "testing" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/grpcclient" "vitess.io/vitess/go/vt/sqlparser" @@ -84,7 +85,7 @@ func newTestVDiffEnv(t testing.TB, ctx context.Context, sourceShards, targetShar tabletType: topodatapb.TabletType_REPLICA, tmc: newTestVDiffTMClient(), } - env.ws = workflow.NewServer(env.topoServ, env.tmc, sqlparser.NewTestParser()) + env.ws = workflow.NewServer(env.topoServ, env.tmc, collations.MySQL8(), sqlparser.NewTestParser()) env.tmc.testEnv = env // Generate a unique dialer name. diff --git a/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go b/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go index 5a5ecf11428..b4411636036 100644 --- a/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go +++ b/go/test/endtoend/schemadiff/vrepl/schemadiff_vrepl_suite_test.go @@ -345,18 +345,19 @@ func ignoreAutoIncrement(t *testing.T, createTable string) string { func validateDiff(t *testing.T, fromCreateTable string, toCreateTable string, allowSchemadiffNormalization bool, hints *schemadiff.DiffHints) { // turn the "from" and "to" create statement strings (which we just read via SHOW CREATE TABLE into sqlparser.CreateTable statement) - fromStmt, err := sqlparser.NewTestParser().ParseStrictDDL(fromCreateTable) + env := schemadiff.NewTestEnv() + fromStmt, err := env.Parser.ParseStrictDDL(fromCreateTable) require.NoError(t, err) fromCreateTableStatement, ok := fromStmt.(*sqlparser.CreateTable) require.True(t, ok) - toStmt, err := sqlparser.NewTestParser().ParseStrictDDL(toCreateTable) + toStmt, err := env.Parser.ParseStrictDDL(toCreateTable) require.NoError(t, err) toCreateTableStatement, ok := toStmt.(*sqlparser.CreateTable) require.True(t, ok) // The actual diff logic here! - diff, err := schemadiff.DiffTables(fromCreateTableStatement, toCreateTableStatement, hints) + diff, err := schemadiff.DiffTables(env, fromCreateTableStatement, toCreateTableStatement, hints) assert.NoError(t, err) // The diff can be empty or there can be an actual ALTER TABLE statement @@ -385,7 +386,6 @@ func validateDiff(t *testing.T, fromCreateTable string, toCreateTable string, al // the table generated by the test's own ALTER statement? // But wait, there's caveats. - if toCreateTable != resultCreateTable { // schemadiff's ALTER statement can normalize away CHARACTER SET and COLLATION definitions: // when altering a column's CHARTSET&COLLATION into the table's values, schemadiff just strips the @@ -394,20 +394,20 @@ func validateDiff(t *testing.T, fromCreateTable string, toCreateTable string, al // structure is identical. And so we accept that there can be a normalization issue. if allowSchemadiffNormalization { { - stmt, err := sqlparser.NewTestParser().ParseStrictDDL(toCreateTable) + stmt, err := env.Parser.ParseStrictDDL(toCreateTable) require.NoError(t, err) createTableStatement, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - c, err := schemadiff.NewCreateTableEntity(createTableStatement) + c, err := schemadiff.NewCreateTableEntity(env, createTableStatement) require.NoError(t, err) toCreateTable = c.Create().CanonicalStatementString() } { - stmt, err := sqlparser.NewTestParser().ParseStrictDDL(resultCreateTable) + stmt, err := env.Parser.ParseStrictDDL(resultCreateTable) require.NoError(t, err) createTableStatement, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - c, err := schemadiff.NewCreateTableEntity(createTableStatement) + c, err := schemadiff.NewCreateTableEntity(env, createTableStatement) require.NoError(t, err) resultCreateTable = c.Create().CanonicalStatementString() } @@ -418,12 +418,12 @@ func validateDiff(t *testing.T, fromCreateTable string, toCreateTable string, al assert.Equal(t, toCreateTable, resultCreateTable, "mismatched table structure. ALTER query was: %s", diffedAlterQuery) // Also, let's see that our diff agrees there's no change: - resultStmt, err := sqlparser.NewTestParser().ParseStrictDDL(resultCreateTable) + resultStmt, err := env.Parser.ParseStrictDDL(resultCreateTable) require.NoError(t, err) resultCreateTableStatement, ok := resultStmt.(*sqlparser.CreateTable) require.True(t, ok) - resultDiff, err := schemadiff.DiffTables(toCreateTableStatement, resultCreateTableStatement, hints) + resultDiff, err := schemadiff.DiffTables(env, toCreateTableStatement, resultCreateTableStatement, hints) assert.NoError(t, err) assert.Nil(t, resultDiff) } diff --git a/go/vt/schemadiff/column.go b/go/vt/schemadiff/column.go index 4b8022ac289..c20f0d23787 100644 --- a/go/vt/schemadiff/column.go +++ b/go/vt/schemadiff/column.go @@ -19,7 +19,11 @@ package schemadiff import ( "strings" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/vt/vterrors" + + vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc" ) // columnDetails decorates a column with more details, used by diffing logic @@ -79,14 +83,95 @@ func NewColumnDefinitionEntity(c *sqlparser.ColumnDefinition) *ColumnDefinitionE // ColumnDiff compares this table statement with another table statement, and sees what it takes to // change this table to look like the other table. -// It returns an AlterTable statement if changes are found, or nil if not. -// the other table may be of different name; its name is ignored. -func (c *ColumnDefinitionEntity) ColumnDiff(other *ColumnDefinitionEntity, _ *DiffHints) *ModifyColumnDiff { +// It returns an ModifyColumnDiff statement if changes are found, or nil if not. +// The function also requires the charset/collate on the source & target tables. This is because the column's +// charset & collation, if undefined, are really defined by the table's charset & collation. +// +// Anecdotally, in CreateTableEntity.normalize() we actually actively strip away the charset/collate properties +// from the column definition, to get a cleaner table definition. +// +// Things get complicated when we consider hints.TableCharsetCollateStrategy. Consider this test case: +// +// from: "create table t (a varchar(64)) default charset=latin1", +// to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", +// +// In both cases, the column is really a latin1. But the tables themselves have different collations. +// We need to denormalize the column's charset/collate properties, so that the comparison can be done. +func (c *ColumnDefinitionEntity) ColumnDiff( + env *Environment, + other *ColumnDefinitionEntity, + t1cc *charsetCollate, + t2cc *charsetCollate, +) (*ModifyColumnDiff, error) { + if c.IsTextual() || other.IsTextual() { + // We will now denormalize the columns charset & collate as needed (if empty, populate from table.) + + if c.columnDefinition.Type.Charset.Name == "" && c.columnDefinition.Type.Options.Collate != "" { + // Column has explicit collation but no charset. We can infer the charset from the collation. + collationID := env.CollationEnv.LookupByName(c.columnDefinition.Type.Options.Collate) + charset := env.CollationEnv.LookupCharsetName(collationID) + if charset == "" { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot match charset to collation %v", c.columnDefinition.Type.Options.Collate) + } + defer func() { + c.columnDefinition.Type.Charset.Name = "" + }() + c.columnDefinition.Type.Charset.Name = charset + } + if c.columnDefinition.Type.Charset.Name == "" { + defer func() { + c.columnDefinition.Type.Charset.Name = "" + c.columnDefinition.Type.Options.Collate = "" + }() + c.columnDefinition.Type.Charset.Name = t1cc.charset + if c.columnDefinition.Type.Options.Collate == "" { + defer func() { + c.columnDefinition.Type.Options.Collate = "" + }() + c.columnDefinition.Type.Options.Collate = t1cc.collate + } + if c.columnDefinition.Type.Options.Collate = t1cc.collate; c.columnDefinition.Type.Options.Collate == "" { + collation := env.CollationEnv.DefaultCollationForCharset(t1cc.charset) + if collation == collations.Unknown { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot match collation to charset %v", t1cc.charset) + } + c.columnDefinition.Type.Options.Collate = env.CollationEnv.LookupName(collation) + } + } + if other.columnDefinition.Type.Charset.Name == "" && other.columnDefinition.Type.Options.Collate != "" { + // Column has explicit collation but no charset. We can infer the charset from the collation. + collationID := env.CollationEnv.LookupByName(other.columnDefinition.Type.Options.Collate) + charset := env.CollationEnv.LookupCharsetName(collationID) + if charset == "" { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot match charset to collation %v", other.columnDefinition.Type.Options.Collate) + } + defer func() { + other.columnDefinition.Type.Charset.Name = "" + }() + other.columnDefinition.Type.Charset.Name = charset + } + + if other.columnDefinition.Type.Charset.Name == "" { + defer func() { + other.columnDefinition.Type.Charset.Name = "" + other.columnDefinition.Type.Options.Collate = "" + }() + other.columnDefinition.Type.Charset.Name = t2cc.charset + if other.columnDefinition.Type.Options.Collate = t2cc.collate; other.columnDefinition.Type.Options.Collate == "" { + collation := env.CollationEnv.DefaultCollationForCharset(t2cc.charset) + if collation == collations.Unknown { + return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "cannot match collation to charset %v", t2cc.charset) + } + other.columnDefinition.Type.Options.Collate = env.CollationEnv.LookupName(collation) + } + } + } + if sqlparser.Equals.RefOfColumnDefinition(c.columnDefinition, other.columnDefinition) { - return nil + return nil, nil } - return NewModifyColumnDiffByDefinition(other.columnDefinition) + return NewModifyColumnDiffByDefinition(other.columnDefinition), nil } // IsTextual returns true when this column is of textual type, and is capable of having a character set property diff --git a/go/vt/schemadiff/diff.go b/go/vt/schemadiff/diff.go index b46a7d23cc6..fe58012bda0 100644 --- a/go/vt/schemadiff/diff.go +++ b/go/vt/schemadiff/diff.go @@ -27,11 +27,11 @@ func AllSubsequent(diff EntityDiff) (diffs []EntityDiff) { // DiffCreateTablesQueries compares two `CREATE TABLE ...` queries (in string form) and returns the diff from table1 to table2. // Either or both of the queries can be empty. Based on this, the diff could be // nil, CreateTable, DropTable or AlterTable -func DiffCreateTablesQueries(query1 string, query2 string, hints *DiffHints, parser *sqlparser.Parser) (EntityDiff, error) { +func DiffCreateTablesQueries(env *Environment, query1 string, query2 string, hints *DiffHints) (EntityDiff, error) { var fromCreateTable *sqlparser.CreateTable var ok bool if query1 != "" { - stmt, err := parser.ParseStrictDDL(query1) + stmt, err := env.Parser.ParseStrictDDL(query1) if err != nil { return nil, err } @@ -42,7 +42,7 @@ func DiffCreateTablesQueries(query1 string, query2 string, hints *DiffHints, par } var toCreateTable *sqlparser.CreateTable if query2 != "" { - stmt, err := parser.ParseStrictDDL(query2) + stmt, err := env.Parser.ParseStrictDDL(query2) if err != nil { return nil, err } @@ -51,34 +51,34 @@ func DiffCreateTablesQueries(query1 string, query2 string, hints *DiffHints, par return nil, ErrExpectedCreateTable } } - return DiffTables(fromCreateTable, toCreateTable, hints) + return DiffTables(env, fromCreateTable, toCreateTable, hints) } // DiffTables compares two tables and returns the diff from table1 to table2. // Either or both of the CreateTable statements can be nil. Based on this, the diff could be // nil, CreateTable, DropTable or AlterTable -func DiffTables(create1 *sqlparser.CreateTable, create2 *sqlparser.CreateTable, hints *DiffHints) (EntityDiff, error) { +func DiffTables(env *Environment, create1 *sqlparser.CreateTable, create2 *sqlparser.CreateTable, hints *DiffHints) (EntityDiff, error) { switch { case create1 == nil && create2 == nil: return nil, nil case create1 == nil: - c2, err := NewCreateTableEntity(create2) + c2, err := NewCreateTableEntity(env, create2) if err != nil { return nil, err } return c2.Create(), nil case create2 == nil: - c1, err := NewCreateTableEntity(create1) + c1, err := NewCreateTableEntity(env, create1) if err != nil { return nil, err } return c1.Drop(), nil default: - c1, err := NewCreateTableEntity(create1) + c1, err := NewCreateTableEntity(env, create1) if err != nil { return nil, err } - c2, err := NewCreateTableEntity(create2) + c2, err := NewCreateTableEntity(env, create2) if err != nil { return nil, err } @@ -89,11 +89,11 @@ func DiffTables(create1 *sqlparser.CreateTable, create2 *sqlparser.CreateTable, // DiffCreateViewsQueries compares two `CREATE TABLE ...` queries (in string form) and returns the diff from table1 to table2. // Either or both of the queries can be empty. Based on this, the diff could be // nil, CreateView, DropView or AlterView -func DiffCreateViewsQueries(query1 string, query2 string, hints *DiffHints, parser *sqlparser.Parser) (EntityDiff, error) { +func DiffCreateViewsQueries(env *Environment, query1 string, query2 string, hints *DiffHints) (EntityDiff, error) { var fromCreateView *sqlparser.CreateView var ok bool if query1 != "" { - stmt, err := parser.ParseStrictDDL(query1) + stmt, err := env.Parser.ParseStrictDDL(query1) if err != nil { return nil, err } @@ -104,7 +104,7 @@ func DiffCreateViewsQueries(query1 string, query2 string, hints *DiffHints, pars } var toCreateView *sqlparser.CreateView if query2 != "" { - stmt, err := parser.ParseStrictDDL(query2) + stmt, err := env.Parser.ParseStrictDDL(query2) if err != nil { return nil, err } @@ -113,34 +113,34 @@ func DiffCreateViewsQueries(query1 string, query2 string, hints *DiffHints, pars return nil, ErrExpectedCreateView } } - return DiffViews(fromCreateView, toCreateView, hints) + return DiffViews(env, fromCreateView, toCreateView, hints) } // DiffViews compares two views and returns the diff from view1 to view2 // Either or both of the CreateView statements can be nil. Based on this, the diff could be // nil, CreateView, DropView or AlterView -func DiffViews(create1 *sqlparser.CreateView, create2 *sqlparser.CreateView, hints *DiffHints) (EntityDiff, error) { +func DiffViews(env *Environment, create1 *sqlparser.CreateView, create2 *sqlparser.CreateView, hints *DiffHints) (EntityDiff, error) { switch { case create1 == nil && create2 == nil: return nil, nil case create1 == nil: - c2, err := NewCreateViewEntity(create2) + c2, err := NewCreateViewEntity(env, create2) if err != nil { return nil, err } return c2.Create(), nil case create2 == nil: - c1, err := NewCreateViewEntity(create1) + c1, err := NewCreateViewEntity(env, create1) if err != nil { return nil, err } return c1.Drop(), nil default: - c1, err := NewCreateViewEntity(create1) + c1, err := NewCreateViewEntity(env, create1) if err != nil { return nil, err } - c2, err := NewCreateViewEntity(create2) + c2, err := NewCreateViewEntity(env, create2) if err != nil { return nil, err } @@ -151,12 +151,12 @@ func DiffViews(create1 *sqlparser.CreateView, create2 *sqlparser.CreateView, hin // DiffSchemasSQL compares two schemas and returns the rich diff that turns // 1st schema into 2nd. Schemas are build from SQL, each of which can contain an arbitrary number of // CREATE TABLE and CREATE VIEW statements. -func DiffSchemasSQL(sql1 string, sql2 string, hints *DiffHints, parser *sqlparser.Parser) (*SchemaDiff, error) { - schema1, err := NewSchemaFromSQL(sql1, parser) +func DiffSchemasSQL(env *Environment, sql1 string, sql2 string, hints *DiffHints) (*SchemaDiff, error) { + schema1, err := NewSchemaFromSQL(env, sql1) if err != nil { return nil, err } - schema2, err := NewSchemaFromSQL(sql2, parser) + schema2, err := NewSchemaFromSQL(env, sql2) if err != nil { return nil, err } @@ -165,12 +165,12 @@ func DiffSchemasSQL(sql1 string, sql2 string, hints *DiffHints, parser *sqlparse // DiffSchemas compares two schemas and returns the list of diffs that turn // 1st schema into 2nd. Any of the schemas may be nil. -func DiffSchemas(schema1 *Schema, schema2 *Schema, hints *DiffHints) (*SchemaDiff, error) { +func DiffSchemas(env *Environment, schema1 *Schema, schema2 *Schema, hints *DiffHints) (*SchemaDiff, error) { if schema1 == nil { - schema1 = newEmptySchema() + schema1 = newEmptySchema(env) } if schema2 == nil { - schema2 = newEmptySchema() + schema2 = newEmptySchema(env) } return schema1.SchemaDiff(schema2, hints) } diff --git a/go/vt/schemadiff/diff_test.go b/go/vt/schemadiff/diff_test.go index 231cb4a352b..bd222ab6150 100644 --- a/go/vt/schemadiff/diff_test.go +++ b/go/vt/schemadiff/diff_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" ) @@ -38,6 +39,7 @@ func TestDiffTables(t *testing.T) { action string isError bool hints *DiffHints + env *Environment }{ { name: "identical", @@ -189,8 +191,113 @@ func TestDiffTables(t *testing.T) { TableQualifierHint: TableQualifierDeclared, }, }, + { + name: "changing table level defaults with column specific settings, ignore charset", + from: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin) default charset=latin1", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + }, + { + name: "changing table level defaults with column specific settings based on collation, ignore charset", + from: "create table t (a varchar(64) COLLATE latin1_bin) default charset=utf8mb4", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + }, + { + name: "error on unknown collation", + from: "create table t (a varchar(64) COLLATE latin1_nonexisting) default charset=utf8mb4", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + isError: true, + }, + { + name: "changing table level defaults with column specific settings", + from: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin) default charset=latin1", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + diff: "alter table t charset utf8mb4, algorithm = COPY", + cdiff: "ALTER TABLE `t` CHARSET utf8mb4, ALGORITHM = COPY", + action: "alter", + fromName: "t", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateStrict, + }, + }, + { + name: "changing table level defaults with column specific settings, table already normalized", + from: "create table t (a varchar(64)) default charset=latin1", + to: "create table t (a varchar(64) CHARACTER SET latin1 COLLATE latin1_bin)", + diff: "alter table t modify column a varchar(64) character set latin1 collate latin1_bin, charset utf8mb4, algorithm = COPY", + cdiff: "ALTER TABLE `t` MODIFY COLUMN `a` varchar(64) CHARACTER SET latin1 COLLATE latin1_bin, CHARSET utf8mb4, ALGORITHM = COPY", + action: "alter", + fromName: "t", + hints: &DiffHints{ + AlterTableAlgorithmStrategy: AlterTableAlgorithmStrategyCopy, + TableCharsetCollateStrategy: TableCharsetCollateStrict, + }, + }, + { + name: "changing table level charset to default", + from: `create table t (i int) default charset=latin1`, + to: `create table t (i int)`, + action: "alter", + diff: "alter table t charset utf8mb4", + cdiff: "ALTER TABLE `t` CHARSET utf8mb4", + }, + { + name: "no changes with normalization and utf8mb4", + from: `CREATE TABLE IF NOT EXISTS tables + ( + TABLE_SCHEMA varchar(64) NOT NULL, + TABLE_NAME varchar(64) NOT NULL, + CREATE_STATEMENT longtext, + CREATE_TIME BIGINT, + PRIMARY KEY (TABLE_SCHEMA, TABLE_NAME) + ) engine = InnoDB`, + to: "CREATE TABLE `tables` (" + + "`TABLE_SCHEMA` varchar(64) NOT NULL," + + "`TABLE_NAME` varchar(64) NOT NULL," + + "`CREATE_STATEMENT` longtext," + + "`CREATE_TIME` bigint DEFAULT NULL," + + "PRIMARY KEY (`TABLE_SCHEMA`,`TABLE_NAME`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci", + hints: &DiffHints{ + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + }, + { + name: "no changes with normalization and utf8mb3", + from: `CREATE TABLE IF NOT EXISTS tables + ( + TABLE_SCHEMA varchar(64) NOT NULL, + TABLE_NAME varchar(64) NOT NULL, + CREATE_STATEMENT longtext, + CREATE_TIME BIGINT, + PRIMARY KEY (TABLE_SCHEMA, TABLE_NAME) + ) engine = InnoDB`, + to: "CREATE TABLE `tables` (" + + "`TABLE_SCHEMA` varchar(64) NOT NULL," + + "`TABLE_NAME` varchar(64) NOT NULL," + + "`CREATE_STATEMENT` longtext," + + "`CREATE_TIME` bigint DEFAULT NULL," + + "PRIMARY KEY (`TABLE_SCHEMA`,`TABLE_NAME`)" + + ") ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci", + hints: &DiffHints{ + TableCharsetCollateStrategy: TableCharsetCollateIgnoreAlways, + }, + env: NewEnv(collations.NewEnvironment("5.7.9"), collations.CollationUtf8mb3ID, sqlparser.NewTestParser()), + }, } - parser := sqlparser.NewTestParser() + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { var fromCreateTable *sqlparser.CreateTable @@ -198,8 +305,11 @@ func TestDiffTables(t *testing.T) { if ts.hints != nil { hints = ts.hints } + if ts.env != nil { + env = ts.env + } if ts.from != "" { - fromStmt, err := parser.ParseStrictDDL(ts.from) + fromStmt, err := env.Parser.ParseStrictDDL(ts.from) assert.NoError(t, err) var ok bool fromCreateTable, ok = fromStmt.(*sqlparser.CreateTable) @@ -207,7 +317,7 @@ func TestDiffTables(t *testing.T) { } var toCreateTable *sqlparser.CreateTable if ts.to != "" { - toStmt, err := parser.ParseStrictDDL(ts.to) + toStmt, err := env.Parser.ParseStrictDDL(ts.to) assert.NoError(t, err) var ok bool toCreateTable, ok = toStmt.(*sqlparser.CreateTable) @@ -219,8 +329,8 @@ func TestDiffTables(t *testing.T) { // Technically, DiffCreateTablesQueries calls DiffTables, // but we expose both to users of this library. so we want to make sure // both work as expected irrespective of any relationship between them. - dq, dqerr := DiffCreateTablesQueries(ts.from, ts.to, hints, sqlparser.NewTestParser()) - d, err := DiffTables(fromCreateTable, toCreateTable, hints) + dq, dqerr := DiffCreateTablesQueries(env, ts.from, ts.to, hints) + d, err := DiffTables(env, fromCreateTable, toCreateTable, hints) switch { case ts.isError: assert.Error(t, err) @@ -228,8 +338,12 @@ func TestDiffTables(t *testing.T) { case ts.diff == "": assert.NoError(t, err) assert.NoError(t, dqerr) - assert.Nil(t, d) - assert.Nil(t, dq) + if !assert.Nil(t, d) { + assert.Failf(t, "found unexpected diff", "%v", d.CanonicalStatementString()) + } + if !assert.Nil(t, dq) { + assert.Failf(t, "found unexpected diff", "%v", dq.CanonicalStatementString()) + } default: assert.NoError(t, err) require.NotNil(t, d) @@ -242,7 +356,7 @@ func TestDiffTables(t *testing.T) { assert.Equal(t, ts.action, action) // validate we can parse back the statement - _, err = parser.ParseStrictDDL(diff) + _, err = env.Parser.ParseStrictDDL(diff) assert.NoError(t, err) eFrom, eTo := d.Entities() @@ -261,7 +375,7 @@ func TestDiffTables(t *testing.T) { assert.Equal(t, ts.action, action) // validate we can parse back the statement - _, err = parser.ParseStrictDDL(canonicalDiff) + _, err = env.Parser.ParseStrictDDL(canonicalDiff) assert.NoError(t, err) } // let's also check dq, and also validate that dq's statement is identical to d's @@ -323,12 +437,12 @@ func TestDiffViews(t *testing.T) { }, } hints := &DiffHints{} - parser := sqlparser.NewTestParser() + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { var fromCreateView *sqlparser.CreateView if ts.from != "" { - fromStmt, err := parser.ParseStrictDDL(ts.from) + fromStmt, err := env.Parser.ParseStrictDDL(ts.from) assert.NoError(t, err) var ok bool fromCreateView, ok = fromStmt.(*sqlparser.CreateView) @@ -336,7 +450,7 @@ func TestDiffViews(t *testing.T) { } var toCreateView *sqlparser.CreateView if ts.to != "" { - toStmt, err := parser.ParseStrictDDL(ts.to) + toStmt, err := env.Parser.ParseStrictDDL(ts.to) assert.NoError(t, err) var ok bool toCreateView, ok = toStmt.(*sqlparser.CreateView) @@ -348,8 +462,8 @@ func TestDiffViews(t *testing.T) { // Technically, DiffCreateTablesQueries calls DiffTables, // but we expose both to users of this library. so we want to make sure // both work as expected irrespective of any relationship between them. - dq, dqerr := DiffCreateViewsQueries(ts.from, ts.to, hints, parser) - d, err := DiffViews(fromCreateView, toCreateView, hints) + dq, dqerr := DiffCreateViewsQueries(env, ts.from, ts.to, hints) + d, err := DiffViews(env, fromCreateView, toCreateView, hints) switch { case ts.isError: assert.Error(t, err) @@ -357,8 +471,12 @@ func TestDiffViews(t *testing.T) { case ts.diff == "": assert.NoError(t, err) assert.NoError(t, dqerr) - assert.Nil(t, d) - assert.Nil(t, dq) + if !assert.Nil(t, d) { + assert.Failf(t, "found unexpected diff", "%v", d.CanonicalStatementString()) + } + if !assert.Nil(t, dq) { + assert.Failf(t, "found unexpected diff", "%v", dq.CanonicalStatementString()) + } default: assert.NoError(t, err) require.NotNil(t, d) @@ -371,7 +489,7 @@ func TestDiffViews(t *testing.T) { assert.Equal(t, ts.action, action) // validate we can parse back the statement - _, err = parser.ParseStrictDDL(diff) + _, err = env.Parser.ParseStrictDDL(diff) assert.NoError(t, err) eFrom, eTo := d.Entities() @@ -390,7 +508,7 @@ func TestDiffViews(t *testing.T) { assert.Equal(t, ts.action, action) // validate we can parse back the statement - _, err = parser.ParseStrictDDL(canonicalDiff) + _, err = env.Parser.ParseStrictDDL(canonicalDiff) assert.NoError(t, err) } @@ -798,13 +916,13 @@ func TestDiffSchemas(t *testing.T) { }, }, } - parser := sqlparser.NewTestParser() + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { hints := &DiffHints{ TableRenameStrategy: ts.tableRename, } - diff, err := DiffSchemasSQL(ts.from, ts.to, hints, parser) + diff, err := DiffSchemasSQL(env, ts.from, ts.to, hints) if ts.expectError != "" { require.Error(t, err) assert.Contains(t, err.Error(), ts.expectError) @@ -830,21 +948,21 @@ func TestDiffSchemas(t *testing.T) { // validate we can parse back the diff statements for _, s := range statements { - _, err := parser.ParseStrictDDL(s) + _, err := env.Parser.ParseStrictDDL(s) assert.NoError(t, err) } for _, s := range cstatements { - _, err := parser.ParseStrictDDL(s) + _, err := env.Parser.ParseStrictDDL(s) assert.NoError(t, err) } { // Validate "apply()" on "from" converges with "to" - schema1, err := NewSchemaFromSQL(ts.from, parser) + schema1, err := NewSchemaFromSQL(env, ts.from) require.NoError(t, err) schema1SQL := schema1.ToSQL() - schema2, err := NewSchemaFromSQL(ts.to, parser) + schema2, err := NewSchemaFromSQL(env, ts.to) require.NoError(t, err) applied, err := schema1.Apply(diffs) require.NoError(t, err) @@ -895,13 +1013,13 @@ func TestSchemaApplyError(t *testing.T) { }, } hints := &DiffHints{} - parser := sqlparser.NewTestParser() + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { // Validate "apply()" on "from" converges with "to" - schema1, err := NewSchemaFromSQL(ts.from, parser) + schema1, err := NewSchemaFromSQL(env, ts.from) assert.NoError(t, err) - schema2, err := NewSchemaFromSQL(ts.to, parser) + schema2, err := NewSchemaFromSQL(env, ts.to) assert.NoError(t, err) { diff --git a/go/vt/schemadiff/env.go b/go/vt/schemadiff/env.go new file mode 100644 index 00000000000..5f155464781 --- /dev/null +++ b/go/vt/schemadiff/env.go @@ -0,0 +1,28 @@ +package schemadiff + +import ( + "vitess.io/vitess/go/mysql/collations" + "vitess.io/vitess/go/vt/sqlparser" +) + +type Environment struct { + CollationEnv *collations.Environment + DefaultColl collations.ID + Parser *sqlparser.Parser +} + +func NewTestEnv() *Environment { + return &Environment{ + CollationEnv: collations.MySQL8(), + DefaultColl: collations.MySQL8().DefaultConnectionCharset(), + Parser: sqlparser.NewTestParser(), + } +} + +func NewEnv(collEnv *collations.Environment, defaultColl collations.ID, parser *sqlparser.Parser) *Environment { + return &Environment{ + CollationEnv: collEnv, + DefaultColl: defaultColl, + Parser: parser, + } +} diff --git a/go/vt/schemadiff/schema.go b/go/vt/schemadiff/schema.go index 664656b6cc0..47372eada25 100644 --- a/go/vt/schemadiff/schema.go +++ b/go/vt/schemadiff/schema.go @@ -40,10 +40,12 @@ type Schema struct { foreignKeyParents []*CreateTableEntity // subset of tables foreignKeyChildren []*CreateTableEntity // subset of tables + + env *Environment } // newEmptySchema is used internally to initialize a Schema object -func newEmptySchema() *Schema { +func newEmptySchema(env *Environment) *Schema { schema := &Schema{ tables: []*CreateTableEntity{}, views: []*CreateViewEntity{}, @@ -52,13 +54,15 @@ func newEmptySchema() *Schema { foreignKeyParents: []*CreateTableEntity{}, foreignKeyChildren: []*CreateTableEntity{}, + + env: env, } return schema } // NewSchemaFromEntities creates a valid and normalized schema based on list of entities -func NewSchemaFromEntities(entities []Entity) (*Schema, error) { - schema := newEmptySchema() +func NewSchemaFromEntities(env *Environment, entities []Entity) (*Schema, error) { + schema := newEmptySchema(env) for _, e := range entities { switch c := e.(type) { case *CreateTableEntity: @@ -74,18 +78,18 @@ func NewSchemaFromEntities(entities []Entity) (*Schema, error) { } // NewSchemaFromStatements creates a valid and normalized schema based on list of valid statements -func NewSchemaFromStatements(statements []sqlparser.Statement) (*Schema, error) { +func NewSchemaFromStatements(env *Environment, statements []sqlparser.Statement) (*Schema, error) { entities := make([]Entity, 0, len(statements)) for _, s := range statements { switch stmt := s.(type) { case *sqlparser.CreateTable: - c, err := NewCreateTableEntity(stmt) + c, err := NewCreateTableEntity(env, stmt) if err != nil { return nil, err } entities = append(entities, c) case *sqlparser.CreateView: - v, err := NewCreateViewEntity(stmt) + v, err := NewCreateViewEntity(env, stmt) if err != nil { return nil, err } @@ -94,27 +98,27 @@ func NewSchemaFromStatements(statements []sqlparser.Statement) (*Schema, error) return nil, &UnsupportedStatementError{Statement: sqlparser.CanonicalString(s)} } } - return NewSchemaFromEntities(entities) + return NewSchemaFromEntities(env, entities) } // NewSchemaFromQueries creates a valid and normalized schema based on list of queries -func NewSchemaFromQueries(queries []string, parser *sqlparser.Parser) (*Schema, error) { +func NewSchemaFromQueries(env *Environment, queries []string) (*Schema, error) { statements := make([]sqlparser.Statement, 0, len(queries)) for _, q := range queries { - stmt, err := parser.ParseStrictDDL(q) + stmt, err := env.Parser.ParseStrictDDL(q) if err != nil { return nil, err } statements = append(statements, stmt) } - return NewSchemaFromStatements(statements) + return NewSchemaFromStatements(env, statements) } // NewSchemaFromSQL creates a valid and normalized schema based on a SQL blob that contains // CREATE statements for various objects (tables, views) -func NewSchemaFromSQL(sql string, parser *sqlparser.Parser) (*Schema, error) { +func NewSchemaFromSQL(env *Environment, sql string) (*Schema, error) { var statements []sqlparser.Statement - tokenizer := parser.NewStringTokenizer(sql) + tokenizer := env.Parser.NewStringTokenizer(sql) for { stmt, err := sqlparser.ParseNextStrictDDL(tokenizer) if err != nil { @@ -125,7 +129,7 @@ func NewSchemaFromSQL(sql string, parser *sqlparser.Parser) (*Schema, error) { } statements = append(statements, stmt) } - return NewSchemaFromStatements(statements) + return NewSchemaFromStatements(env, statements) } // getForeignKeyParentTableNames analyzes a CREATE TABLE definition and extracts all referenced foreign key tables names. @@ -668,7 +672,7 @@ func (s *Schema) ToSQL() string { // copy returns a shallow copy of the schema. This is used when applying changes for example. // applying changes will ensure we copy new entities themselves separately. func (s *Schema) copy() *Schema { - dup := newEmptySchema() + dup := newEmptySchema(s.env) dup.tables = make([]*CreateTableEntity, len(s.tables)) copy(dup.tables, s.tables) dup.views = make([]*CreateViewEntity, len(s.views)) @@ -965,7 +969,7 @@ func (s *Schema) SchemaDiff(other *Schema, hints *DiffHints) (*SchemaDiff, error func (s *Schema) ValidateViewReferences() error { var errs error - schemaInformation := newDeclarativeSchemaInformation() + schemaInformation := newDeclarativeSchemaInformation(s.env) // Remember that s.Entities() is already ordered by dependency. ie. tables first, then views // that only depend on those tables (or on dual), then 2nd tier views, etc. diff --git a/go/vt/schemadiff/schema_diff_test.go b/go/vt/schemadiff/schema_diff_test.go index ca36afb8ae7..da2e1206d36 100644 --- a/go/vt/schemadiff/schema_diff_test.go +++ b/go/vt/schemadiff/schema_diff_test.go @@ -25,7 +25,6 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql/capabilities" - "vitess.io/vitess/go/vt/sqlparser" ) func TestPermutations(t *testing.T) { @@ -163,14 +162,15 @@ func TestPermutations(t *testing.T) { }, } hints := &DiffHints{RangeRotationStrategy: RangeRotationDistinctStatements} + env := NewTestEnv() for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { - fromSchema, err := NewSchemaFromQueries(tc.fromQueries, sqlparser.NewTestParser()) + fromSchema, err := NewSchemaFromQueries(env, tc.fromQueries) require.NoError(t, err) require.NotNil(t, fromSchema) - toSchema, err := NewSchemaFromQueries(tc.toQueries, sqlparser.NewTestParser()) + toSchema, err := NewSchemaFromQueries(env, tc.toQueries) require.NoError(t, err) require.NotNil(t, toSchema) @@ -875,16 +875,18 @@ func TestSchemaDiff(t *testing.T) { }, } hints := &DiffHints{RangeRotationStrategy: RangeRotationDistinctStatements} + env := NewTestEnv() + for _, tc := range tt { t.Run(tc.name, func(t *testing.T) { if tc.fromQueries == nil { tc.fromQueries = createQueries } - fromSchema, err := NewSchemaFromQueries(tc.fromQueries, sqlparser.NewTestParser()) + fromSchema, err := NewSchemaFromQueries(env, tc.fromQueries) require.NoError(t, err) require.NotNil(t, fromSchema) - toSchema, err := NewSchemaFromQueries(tc.toQueries, sqlparser.NewTestParser()) + toSchema, err := NewSchemaFromQueries(env, tc.toQueries) require.NoError(t, err) require.NotNil(t, toSchema) diff --git a/go/vt/schemadiff/schema_test.go b/go/vt/schemadiff/schema_test.go index 22a19b59e68..504aa56fd6a 100644 --- a/go/vt/schemadiff/schema_test.go +++ b/go/vt/schemadiff/schema_test.go @@ -84,7 +84,7 @@ var schemaTestExpectSortedViewNames = []string{ var schemaTestToSQL = "CREATE TABLE `t1` (\n\t`id` int\n);\nCREATE TABLE `t2` (\n\t`id` int\n);\nCREATE TABLE `t3` (\n\t`id` int,\n\t`type` enum('foo', 'bar') NOT NULL DEFAULT 'foo'\n);\nCREATE TABLE `t5` (\n\t`id` int\n);\nCREATE VIEW `v0` AS SELECT 1 FROM `dual`;\nCREATE VIEW `v3` AS SELECT *, `id` + 1 AS `id_plus`, `id` + 2 FROM `t3` AS `t3`;\nCREATE VIEW `v9` AS SELECT 1 FROM `dual`;\nCREATE VIEW `v1` AS SELECT * FROM `v3`;\nCREATE VIEW `v2` AS SELECT * FROM `v3`, `t2`;\nCREATE VIEW `v4` AS SELECT * FROM `t2` AS `something_else`, `v3`;\nCREATE VIEW `v5` AS SELECT * FROM `t1`, (SELECT * FROM `v3`) AS `some_alias`;\nCREATE VIEW `v6` AS SELECT * FROM `v4`;\n" func TestNewSchemaFromQueries(t *testing.T) { - schema, err := NewSchemaFromQueries(schemaTestCreateQueries, sqlparser.NewTestParser()) + schema, err := NewSchemaFromQueries(NewTestEnv(), schemaTestCreateQueries) assert.NoError(t, err) require.NotNil(t, schema) @@ -94,7 +94,7 @@ func TestNewSchemaFromQueries(t *testing.T) { } func TestNewSchemaFromSQL(t *testing.T) { - schema, err := NewSchemaFromSQL(strings.Join(schemaTestCreateQueries, ";"), sqlparser.NewTestParser()) + schema, err := NewSchemaFromSQL(NewTestEnv(), strings.Join(schemaTestCreateQueries, ";")) assert.NoError(t, err) require.NotNil(t, schema) @@ -108,7 +108,7 @@ func TestNewSchemaFromQueriesWithDuplicate(t *testing.T) { queries := append(schemaTestCreateQueries, "create view v2 as select * from v1, t2", ) - _, err := NewSchemaFromQueries(queries, sqlparser.NewTestParser()) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.Error(t, err) assert.EqualError(t, err, (&ApplyDuplicateEntityError{Entity: "v2"}).Error()) } @@ -118,7 +118,7 @@ func TestNewSchemaFromQueriesUnresolved(t *testing.T) { queries := append(schemaTestCreateQueries, "create view v7 as select * from v8, t2", ) - schema, err := NewSchemaFromQueries(queries, sqlparser.NewTestParser()) + schema, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.Error(t, err) assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) v := schema.sorted[len(schema.sorted)-1] @@ -131,7 +131,7 @@ func TestNewSchemaFromQueriesUnresolvedAlias(t *testing.T) { queries := append(schemaTestCreateQueries, "create view v7 as select * from something_else as t1, t2", ) - _, err := NewSchemaFromQueries(queries, sqlparser.NewTestParser()) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.Error(t, err) assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) } @@ -141,7 +141,7 @@ func TestNewSchemaFromQueriesViewFromDual(t *testing.T) { queries := []string{ "create view v20 as select 1 from dual", } - _, err := NewSchemaFromQueries(queries, sqlparser.NewTestParser()) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.NoError(t, err) } @@ -150,7 +150,7 @@ func TestNewSchemaFromQueriesViewFromDualImplicit(t *testing.T) { queries := []string{ "create view v20 as select 1", } - _, err := NewSchemaFromQueries(queries, sqlparser.NewTestParser()) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) assert.NoError(t, err) } @@ -160,14 +160,14 @@ func TestNewSchemaFromQueriesLoop(t *testing.T) { "create view v7 as select * from v8, t2", "create view v8 as select * from t1, v7", ) - _, err := NewSchemaFromQueries(queries, sqlparser.NewTestParser()) + _, err := NewSchemaFromQueries(NewTestEnv(), queries) require.Error(t, err) err = vterrors.UnwrapFirst(err) assert.EqualError(t, err, (&ViewDependencyUnresolvedError{View: "v7"}).Error()) } func TestToSQL(t *testing.T) { - schema, err := NewSchemaFromQueries(schemaTestCreateQueries, sqlparser.NewTestParser()) + schema, err := NewSchemaFromQueries(NewTestEnv(), schemaTestCreateQueries) assert.NoError(t, err) require.NotNil(t, schema) @@ -176,7 +176,7 @@ func TestToSQL(t *testing.T) { } func TestCopy(t *testing.T) { - schema, err := NewSchemaFromQueries(schemaTestCreateQueries, sqlparser.NewTestParser()) + schema, err := NewSchemaFromQueries(NewTestEnv(), schemaTestCreateQueries) assert.NoError(t, err) require.NotNil(t, schema) @@ -299,7 +299,7 @@ func TestTableForeignKeyOrdering(t *testing.T) { "v13", "v09", } - schema, err := NewSchemaFromQueries(fkQueries, sqlparser.NewTestParser()) + schema, err := NewSchemaFromQueries(NewTestEnv(), fkQueries) require.NoError(t, err) assert.NotNil(t, schema) @@ -406,7 +406,7 @@ func TestInvalidSchema(t *testing.T) { for _, ts := range tt { t.Run(ts.schema, func(t *testing.T) { - _, err := NewSchemaFromSQL(ts.schema, sqlparser.NewTestParser()) + _, err := NewSchemaFromSQL(NewTestEnv(), ts.schema) if ts.expectErr == nil { assert.NoError(t, err) } else { @@ -424,7 +424,7 @@ func TestInvalidTableForeignKeyReference(t *testing.T) { "create table t11 (id int primary key, i int, constraint f12 foreign key (i) references t12(id) on delete restrict)", "create table t15(id int, primary key(id))", } - s, err := NewSchemaFromQueries(fkQueries, sqlparser.NewTestParser()) + s, err := NewSchemaFromQueries(NewTestEnv(), fkQueries) assert.Error(t, err) // Even though there's errors, we still expect the schema to have been created. assert.NotNil(t, s) @@ -442,7 +442,7 @@ func TestInvalidTableForeignKeyReference(t *testing.T) { "create table t11 (id int primary key, i int, constraint f12 foreign key (i) references t12(id) on delete restrict)", "create table t12 (id int primary key, i int, constraint f13 foreign key (i) references t13(id) on delete restrict)", } - _, err := NewSchemaFromQueries(fkQueries, sqlparser.NewTestParser()) + _, err := NewSchemaFromQueries(NewTestEnv(), fkQueries) assert.Error(t, err) assert.ErrorContains(t, err, (&ForeignKeyDependencyUnresolvedError{Table: "t11"}).Error()) assert.ErrorContains(t, err, (&ForeignKeyDependencyUnresolvedError{Table: "t12"}).Error()) @@ -467,7 +467,7 @@ func TestGetEntityColumnNames(t *testing.T) { "create view vb as select *, now() from v8", } - schema, err := NewSchemaFromQueries(queries, sqlparser.NewTestParser()) + schema, err := NewSchemaFromQueries(NewTestEnv(), queries) require.NoError(t, err) require.NotNil(t, schema) @@ -489,7 +489,7 @@ func TestGetEntityColumnNames(t *testing.T) { entities := schema.Entities() require.Equal(t, len(entities), len(expectedColNames)) - tcmap := newDeclarativeSchemaInformation() + tcmap := newDeclarativeSchemaInformation(NewTestEnv()) // we test by order of dependency: for _, e := range entities { tbl := e.Name() @@ -745,7 +745,7 @@ func TestViewReferences(t *testing.T) { } for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - schema, err := NewSchemaFromQueries(ts.queries, sqlparser.NewTestParser()) + schema, err := NewSchemaFromQueries(NewTestEnv(), ts.queries) if ts.expectErr == nil { require.NoError(t, err) require.NotNil(t, schema) @@ -837,9 +837,9 @@ func TestMassiveSchema(t *testing.T) { queries1 = append(queries1, query) tableNames[tableName] = true } - schema0, err = NewSchemaFromQueries(queries0, sqlparser.NewTestParser()) + schema0, err = NewSchemaFromQueries(NewTestEnv(), queries0) require.NoError(t, err) - schema1, err = NewSchemaFromQueries(queries1, sqlparser.NewTestParser()) + schema1, err = NewSchemaFromQueries(NewTestEnv(), queries1) require.NoError(t, err) require.Equal(t, countModifiedTables, modifyTables) diff --git a/go/vt/schemadiff/semantics.go b/go/vt/schemadiff/semantics.go index ca4b57c62e1..00652344b63 100644 --- a/go/vt/schemadiff/semantics.go +++ b/go/vt/schemadiff/semantics.go @@ -38,11 +38,13 @@ var _ semantics.SchemaInformation = (*declarativeSchemaInformation)(nil) // to make it more simple and accessible to schemadiff's logic. type declarativeSchemaInformation struct { Tables map[string]*vindexes.Table + env *Environment } -func newDeclarativeSchemaInformation() *declarativeSchemaInformation { +func newDeclarativeSchemaInformation(env *Environment) *declarativeSchemaInformation { return &declarativeSchemaInformation{ Tables: make(map[string]*vindexes.Table), + env: env, } } @@ -53,11 +55,11 @@ func (si *declarativeSchemaInformation) FindTableOrVindex(tablename sqlparser.Ta } func (si *declarativeSchemaInformation) ConnCollation() collations.ID { - return collations.CollationUtf8mb4ID + return si.env.DefaultColl } func (si *declarativeSchemaInformation) CollationEnv() *collations.Environment { - return collations.MySQL8() + return si.env.CollationEnv } func (si *declarativeSchemaInformation) ForeignKeyMode(keyspace string) (vschemapb.Keyspace_ForeignKeyMode, error) { diff --git a/go/vt/schemadiff/table.go b/go/vt/schemadiff/table.go index eef39d51fa2..bcbe97a7740 100644 --- a/go/vt/schemadiff/table.go +++ b/go/vt/schemadiff/table.go @@ -25,12 +25,14 @@ import ( golcs "github.com/yudai/golcs" - "vitess.io/vitess/go/mysql/collations/colldata" - - "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" ) +type charsetCollate struct { + charset string + collate string +} + type AlterTableEntityDiff struct { from *CreateTableEntity to *CreateTableEntity @@ -329,13 +331,14 @@ func (d *RenameTableEntityDiff) SetSubsequentDiff(EntityDiff) { // CreateTableEntity stands for a TABLE construct. It contains the table's CREATE statement. type CreateTableEntity struct { *sqlparser.CreateTable + Env *Environment } -func NewCreateTableEntity(c *sqlparser.CreateTable) (*CreateTableEntity, error) { +func NewCreateTableEntity(env *Environment, c *sqlparser.CreateTable) (*CreateTableEntity, error) { if !c.IsFullyParsed() { return nil, &NotFullyParsedError{Entity: c.Table.Name.String(), Statement: sqlparser.CanonicalString(c)} } - entity := &CreateTableEntity{CreateTable: c} + entity := &CreateTableEntity{CreateTable: c, Env: env} entity.normalize() return entity, nil } @@ -362,12 +365,12 @@ func (c *CreateTableEntity) normalizeTableOptions() { switch opt.Name { case "charset": opt.String = strings.ToLower(opt.String) - if charset, ok := collationEnv.CharsetAlias(opt.String); ok { + if charset, ok := c.Env.CollationEnv.CharsetAlias(opt.String); ok { opt.String = charset } case "collate": opt.String = strings.ToLower(opt.String) - if collation, ok := collationEnv.CollationAlias(opt.String); ok { + if collation, ok := c.Env.CollationEnv.CollationAlias(opt.String); ok { opt.String = collation } case "engine": @@ -387,7 +390,7 @@ func (c *CreateTableEntity) GetCharset() string { for _, opt := range c.CreateTable.TableSpec.Options { if strings.ToLower(opt.Name) == "charset" { opt.String = strings.ToLower(opt.String) - if charsetName, ok := collationEnv.CharsetAlias(opt.String); ok { + if charsetName, ok := c.Env.CollationEnv.CharsetAlias(opt.String); ok { return charsetName } return opt.String @@ -402,7 +405,7 @@ func (c *CreateTableEntity) GetCollation() string { for _, opt := range c.CreateTable.TableSpec.Options { if strings.ToLower(opt.Name) == "collate" { opt.String = strings.ToLower(opt.String) - if collationName, ok := collationEnv.CollationAlias(opt.String); ok { + if collationName, ok := c.Env.CollationEnv.CollationAlias(opt.String); ok { return collationName } return opt.String @@ -412,45 +415,27 @@ func (c *CreateTableEntity) GetCollation() string { } func (c *CreateTableEntity) Clone() Entity { - return &CreateTableEntity{CreateTable: sqlparser.CloneRefOfCreateTable(c.CreateTable)} + return &CreateTableEntity{CreateTable: sqlparser.CloneRefOfCreateTable(c.CreateTable), Env: c.Env} } -// Right now we assume MySQL 8.0 for the collation normalization handling. -const mysqlCollationVersion = "8.0.0" - -var collationEnv = collations.NewEnvironment(mysqlCollationVersion) - -func defaultCharset() string { - collation := colldata.Lookup(collations.ID(collationEnv.DefaultConnectionCharset())) - if collation == nil { - return "" +func getTableCharsetCollate(env *Environment, tableOptions *sqlparser.TableOptions) *charsetCollate { + cc := &charsetCollate{ + charset: env.CollationEnv.LookupCharsetName(env.DefaultColl), + collate: env.CollationEnv.LookupName(env.DefaultColl), } - return collation.Charset().Name() -} - -func defaultCharsetCollation(charset string) string { - collation := collationEnv.DefaultCollationForCharset(charset) - if collation == collations.Unknown { - return "" + for _, option := range *tableOptions { + if strings.EqualFold(option.Name, "charset") { + cc.charset = option.String + } + if strings.EqualFold(option.Name, "collate") { + cc.collate = option.String + } } - return collationEnv.LookupName(collation) + return cc } func (c *CreateTableEntity) normalizeColumnOptions() { - tableCharset := defaultCharset() - tableCollation := "" - for _, option := range c.CreateTable.TableSpec.Options { - switch strings.ToUpper(option.Name) { - case "CHARSET": - tableCharset = option.String - case "COLLATE": - tableCollation = option.String - } - } - defaultCollation := defaultCharsetCollation(tableCharset) - if tableCollation == "" { - tableCollation = defaultCollation - } + cc := getTableCharsetCollate(c.Env, &c.CreateTable.TableSpec.Options) for _, col := range c.CreateTable.TableSpec.Columns { if col.Type.Options == nil { @@ -497,13 +482,13 @@ func (c *CreateTableEntity) normalizeColumnOptions() { // Map any charset aliases to the real charset. This applies mainly right // now to utf8 being an alias for utf8mb3. - if charset, ok := collationEnv.CharsetAlias(col.Type.Charset.Name); ok { + if charset, ok := c.Env.CollationEnv.CharsetAlias(col.Type.Charset.Name); ok { col.Type.Charset.Name = charset } // Map any collation aliases to the real collation. This applies mainly right // now to utf8 being an alias for utf8mb3 collations. - if collation, ok := collationEnv.CollationAlias(col.Type.Options.Collate); ok { + if collation, ok := c.Env.CollationEnv.CollationAlias(col.Type.Options.Collate); ok { col.Type.Options.Collate = collation } @@ -571,13 +556,13 @@ func (c *CreateTableEntity) normalizeColumnOptions() { if _, ok := charsetTypes[col.Type.Type]; ok { // If the charset is explicitly configured and it mismatches, we don't normalize // anything for charsets or collations and move on. - if col.Type.Charset.Name != "" && col.Type.Charset.Name != tableCharset { + if col.Type.Charset.Name != "" && col.Type.Charset.Name != cc.charset { continue } // Alright, first check if both charset and collation are the same as // the table level options, in that case we can remove both since that's equivalent. - if col.Type.Charset.Name == tableCharset && col.Type.Options.Collate == tableCollation { + if col.Type.Charset.Name == cc.charset && col.Type.Options.Collate == cc.collate { col.Type.Charset.Name = "" col.Type.Options.Collate = "" } @@ -595,13 +580,13 @@ func (c *CreateTableEntity) normalizeColumnOptions() { if col.Type.Charset.Name != "" { col.Type.Charset.Name = "" if col.Type.Options.Collate == "" { - col.Type.Options.Collate = defaultCollation + col.Type.Options.Collate = c.Env.CollationEnv.LookupName(c.Env.DefaultColl) } } // We now have one case left, which is when we have set a collation but it's the same // as the table level. In that case, we can clear it since that is equivalent. - if col.Type.Options.Collate == tableCollation { + if col.Type.Options.Collate == cc.collate { col.Type.Options.Collate = "" } } @@ -826,21 +811,21 @@ func (c *CreateTableEntity) TableDiff(other *CreateTableEntity, hints *DiffHints alterTable.Table.Qualifier = other.Table.Qualifier } - diffedTableCharset := "" + t1cc := getTableCharsetCollate(c.Env, &c.CreateTable.TableSpec.Options) + t2cc := getTableCharsetCollate(c.Env, &other.CreateTable.TableSpec.Options) + var parentAlterTableEntityDiff *AlterTableEntityDiff var partitionSpecs []*sqlparser.PartitionSpec var superfluousFulltextKeys []*sqlparser.AddIndexDefinition - { - t1Options := c.CreateTable.TableSpec.Options - t2Options := other.CreateTable.TableSpec.Options - diffedTableCharset = c.diffTableCharset(t1Options, t2Options) - } { // diff columns // ordered columns for both tables: + t1Columns := c.CreateTable.TableSpec.Columns t2Columns := other.CreateTable.TableSpec.Columns - c.diffColumns(alterTable, t1Columns, t2Columns, hints, diffedTableCharset != "") + if err := c.diffColumns(alterTable, t1Columns, t2Columns, hints, t1cc, t2cc); err != nil { + return nil, err + } } { // diff keys @@ -927,21 +912,11 @@ func (c *CreateTableEntity) TableDiff(other *CreateTableEntity, hints *DiffHints } func (c *CreateTableEntity) diffTableCharset( - t1Options sqlparser.TableOptions, - t2Options sqlparser.TableOptions, + t1cc *charsetCollate, + t2cc *charsetCollate, ) string { - getcharset := func(options sqlparser.TableOptions) string { - for _, option := range options { - if strings.EqualFold(option.Name, "CHARSET") { - return option.String - } - } - return "" - } - t1Charset := getcharset(t1Options) - t2Charset := getcharset(t2Options) - if t1Charset != t2Charset { - return t2Charset + if t1cc.charset != t2cc.charset { + return t2cc.charset } return "" } @@ -1019,7 +994,7 @@ func (c *CreateTableEntity) diffOptions(alterTable *sqlparser.AlterTable, case "CHARSET": switch hints.TableCharsetCollateStrategy { case TableCharsetCollateStrict: - tableOption = &sqlparser.TableOption{String: ""} + tableOption = &sqlparser.TableOption{Name: "CHARSET", String: c.Env.CollationEnv.LookupCharsetName(c.Env.DefaultColl), CaseSensitive: true} // in all other strategies we ignore the charset } case "CHECKSUM": @@ -1536,8 +1511,9 @@ func (c *CreateTableEntity) diffColumns(alterTable *sqlparser.AlterTable, t1Columns []*sqlparser.ColumnDefinition, t2Columns []*sqlparser.ColumnDefinition, hints *DiffHints, - tableCharsetChanged bool, -) { + t1cc *charsetCollate, + t2cc *charsetCollate, +) error { getColumnsMap := func(cols []*sqlparser.ColumnDefinition) map[string]*columnDetails { var prevCol *columnDetails m := map[string]*columnDetails{} @@ -1599,13 +1575,16 @@ func (c *CreateTableEntity) diffColumns(alterTable *sqlparser.AlterTable, t2ColEntity := NewColumnDefinitionEntity(t2Col) // check diff between before/after columns: - modifyColumnDiff := t1ColEntity.ColumnDiff(t2ColEntity, hints) + modifyColumnDiff, err := t1ColEntity.ColumnDiff(c.Env, t2ColEntity, t1cc, t2cc) + if err != nil { + return err + } if modifyColumnDiff == nil { // even if there's no apparent change, there can still be implicit changes // it is possible that the table charset is changed. the column may be some col1 TEXT NOT NULL, possibly in both versions 1 and 2, - // but implicitly the column has changed its characters set. So we need to explicitly ass a MODIFY COLUMN statement, so that + // but implicitly the column has changed its character set. So we need to explicitly add a MODIFY COLUMN statement, so that // MySQL rebuilds it. - if tableCharsetChanged && t2ColEntity.IsTextual() && t2Col.Type.Charset.Name == "" { + if t1cc.charset != t2cc.charset && t2ColEntity.IsTextual() && t2Col.Type.Charset.Name == "" { modifyColumnDiff = NewModifyColumnDiffByDefinition(t2Col) } } @@ -1666,6 +1645,7 @@ func (c *CreateTableEntity) diffColumns(alterTable *sqlparser.AlterTable, for _, c := range addColumns { alterTable.AlterOptions = append(alterTable.AlterOptions, c) } + return nil } func heuristicallyDetectColumnRenames( diff --git a/go/vt/schemadiff/table_test.go b/go/vt/schemadiff/table_test.go index 5e159ffca99..9d653c8f00d 100644 --- a/go/vt/schemadiff/table_test.go +++ b/go/vt/schemadiff/table_test.go @@ -1149,6 +1149,27 @@ func TestCreateTableDiff(t *testing.T) { diff: "alter table t modify column t1 varchar(128) not null, modify column t2 varchar(128) not null, modify column t3 tinytext, charset utf8mb4", cdiff: "ALTER TABLE `t` MODIFY COLUMN `t1` varchar(128) NOT NULL, MODIFY COLUMN `t2` varchar(128) NOT NULL, MODIFY COLUMN `t3` tinytext, CHARSET utf8mb4", }, + { + name: "change table collation", + from: "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_ai_ci", + to: "create table t (id int, primary key(id)) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_bin", + diff: "alter table t collate utf8mb4_0900_bin", + cdiff: "ALTER TABLE `t` COLLATE utf8mb4_0900_bin", + }, + { + name: "change table collation with textual column", + from: "create table t (id int, t varchar(192) not null) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_ai_ci", + to: "create table t (id int, t varchar(192) not null) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_bin", + diff: "alter table t modify column t varchar(192) not null, collate utf8mb4_0900_bin", + cdiff: "ALTER TABLE `t` MODIFY COLUMN `t` varchar(192) NOT NULL, COLLATE utf8mb4_0900_bin", + }, + { + name: "change table collation with textual column that has collation", + from: "create table t (id int, t varchar(192) not null collate utf8mb4_0900_bin) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_ai_ci", + to: "create table t (id int, t varchar(192) not null collate utf8mb4_0900_bin) DEFAULT CHARSET = utf8mb4 COLLATE utf8mb4_0900_bin", + diff: "alter table t collate utf8mb4_0900_bin", + cdiff: "ALTER TABLE `t` COLLATE utf8mb4_0900_bin", + }, { name: "normalized unsigned attribute", from: "create table t1 (id int primary key)", @@ -1265,21 +1286,22 @@ func TestCreateTableDiff(t *testing.T) { }, } standardHints := DiffHints{} + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - fromStmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.from) + fromStmt, err := env.Parser.ParseStrictDDL(ts.from) require.NoError(t, err) fromCreateTable, ok := fromStmt.(*sqlparser.CreateTable) require.True(t, ok) - toStmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.to) + toStmt, err := env.Parser.ParseStrictDDL(ts.to) require.NoError(t, err) toCreateTable, ok := toStmt.(*sqlparser.CreateTable) require.True(t, ok) - c, err := NewCreateTableEntity(fromCreateTable) + c, err := NewCreateTableEntity(env, fromCreateTable) require.NoError(t, err) - other, err := NewCreateTableEntity(toCreateTable) + other, err := NewCreateTableEntity(env, toCreateTable) require.NoError(t, err) hints := standardHints @@ -1332,7 +1354,7 @@ func TestCreateTableDiff(t *testing.T) { } } // validate we can parse back the statement - _, err := sqlparser.NewTestParser().ParseStrictDDL(diff) + _, err := env.Parser.ParseStrictDDL(diff) assert.NoError(t, err) // Validate "from/to" entities @@ -1362,7 +1384,7 @@ func TestCreateTableDiff(t *testing.T) { { cdiff := alter.CanonicalStatementString() assert.Equal(t, ts.cdiff, cdiff) - _, err := sqlparser.NewTestParser().ParseStrictDDL(cdiff) + _, err := env.Parser.ParseStrictDDL(cdiff) assert.NoError(t, err) } @@ -1857,19 +1879,20 @@ func TestValidate(t *testing.T) { }, } hints := DiffHints{} + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - stmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.from) + stmt, err := env.Parser.ParseStrictDDL(ts.from) require.NoError(t, err) fromCreateTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - stmt, err = sqlparser.NewTestParser().ParseStrictDDL(ts.alter) + stmt, err = env.Parser.ParseStrictDDL(ts.alter) require.NoError(t, err) alterTable, ok := stmt.(*sqlparser.AlterTable) require.True(t, ok) - from, err := NewCreateTableEntity(fromCreateTable) + from, err := NewCreateTableEntity(env, fromCreateTable) require.NoError(t, err) a := &AlterTableEntityDiff{from: from, alterTable: alterTable} applied, err := from.Apply(a) @@ -1888,12 +1911,12 @@ func TestValidate(t *testing.T) { require.True(t, ok) applied = c.normalize() - stmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.to) + stmt, err := env.Parser.ParseStrictDDL(ts.to) require.NoError(t, err) toCreateTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - to, err := NewCreateTableEntity(toCreateTable) + to, err := NewCreateTableEntity(env, toCreateTable) require.NoError(t, err) diff, err := applied.Diff(to, &hints) require.NoError(t, err) @@ -2170,14 +2193,15 @@ func TestNormalize(t *testing.T) { to: "CREATE TABLE `t` (\n\t`id` tinyint(1),\n\t`b` tinyint(1),\n\tPRIMARY KEY (`id`)\n)", }, } + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - stmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.from) + stmt, err := env.Parser.ParseStrictDDL(ts.from) require.NoError(t, err) fromCreateTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - from, err := NewCreateTableEntity(fromCreateTable) + from, err := NewCreateTableEntity(env, fromCreateTable) require.NoError(t, err) assert.Equal(t, ts.to, sqlparser.CanonicalString(from)) }) @@ -2261,11 +2285,12 @@ func TestIndexesCoveringForeignKeyColumns(t *testing.T) { }, } - stmt, err := sqlparser.NewTestParser().ParseStrictDDL(sql) + env := NewTestEnv() + stmt, err := env.Parser.ParseStrictDDL(sql) require.NoError(t, err) createTable, ok := stmt.(*sqlparser.CreateTable) require.True(t, ok) - c, err := NewCreateTableEntity(createTable) + c, err := NewCreateTableEntity(env, createTable) require.NoError(t, err) tableColumns := map[string]sqlparser.IdentifierCI{} for _, col := range c.CreateTable.TableSpec.Columns { diff --git a/go/vt/schemadiff/view.go b/go/vt/schemadiff/view.go index 4e32dfd9910..0810b2a3395 100644 --- a/go/vt/schemadiff/view.go +++ b/go/vt/schemadiff/view.go @@ -230,13 +230,14 @@ func (d *DropViewEntityDiff) SetSubsequentDiff(EntityDiff) { // CreateViewEntity stands for a VIEW construct. It contains the view's CREATE statement. type CreateViewEntity struct { *sqlparser.CreateView + env *Environment } -func NewCreateViewEntity(c *sqlparser.CreateView) (*CreateViewEntity, error) { +func NewCreateViewEntity(env *Environment, c *sqlparser.CreateView) (*CreateViewEntity, error) { if !c.IsFullyParsed() { return nil, &NotFullyParsedError{Entity: c.ViewName.Name.String(), Statement: sqlparser.CanonicalString(c)} } - entity := &CreateViewEntity{CreateView: c} + entity := &CreateViewEntity{CreateView: c, env: env} entity.normalize() return entity, nil } diff --git a/go/vt/schemadiff/view_test.go b/go/vt/schemadiff/view_test.go index d32739d7190..dd6659f6c0d 100644 --- a/go/vt/schemadiff/view_test.go +++ b/go/vt/schemadiff/view_test.go @@ -146,21 +146,22 @@ func TestCreateViewDiff(t *testing.T) { }, } hints := &DiffHints{} + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - fromStmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.from) + fromStmt, err := env.Parser.ParseStrictDDL(ts.from) assert.NoError(t, err) fromCreateView, ok := fromStmt.(*sqlparser.CreateView) assert.True(t, ok) - toStmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.to) + toStmt, err := env.Parser.ParseStrictDDL(ts.to) assert.NoError(t, err) toCreateView, ok := toStmt.(*sqlparser.CreateView) assert.True(t, ok) - c, err := NewCreateViewEntity(fromCreateView) + c, err := NewCreateViewEntity(env, fromCreateView) require.NoError(t, err) - other, err := NewCreateViewEntity(toCreateView) + other, err := NewCreateViewEntity(env, toCreateView) require.NoError(t, err) alter, err := c.Diff(other, hints) switch { @@ -177,7 +178,7 @@ func TestCreateViewDiff(t *testing.T) { diff := alter.StatementString() assert.Equal(t, ts.diff, diff) // validate we can parse back the statement - _, err := sqlparser.NewTestParser().ParseStrictDDL(diff) + _, err := env.Parser.ParseStrictDDL(diff) assert.NoError(t, err) eFrom, eTo := alter.Entities() @@ -199,7 +200,7 @@ func TestCreateViewDiff(t *testing.T) { { cdiff := alter.CanonicalStatementString() assert.Equal(t, ts.cdiff, cdiff) - _, err := sqlparser.NewTestParser().ParseStrictDDL(cdiff) + _, err := env.Parser.ParseStrictDDL(cdiff) assert.NoError(t, err) } } @@ -239,14 +240,15 @@ func TestNormalizeView(t *testing.T) { to: "CREATE SQL SECURITY INVOKER VIEW `v1` AS SELECT `a`, `b`, `c` FROM `t`", }, } + env := NewTestEnv() for _, ts := range tt { t.Run(ts.name, func(t *testing.T) { - stmt, err := sqlparser.NewTestParser().ParseStrictDDL(ts.from) + stmt, err := env.Parser.ParseStrictDDL(ts.from) require.NoError(t, err) fromCreateView, ok := stmt.(*sqlparser.CreateView) require.True(t, ok) - from, err := NewCreateViewEntity(fromCreateView) + from, err := NewCreateViewEntity(env, fromCreateView) require.NoError(t, err) assert.Equal(t, ts.to, sqlparser.CanonicalString(from)) }) diff --git a/go/vt/sidecardb/sidecardb.go b/go/vt/sidecardb/sidecardb.go index 4f3ea2e8252..1d1ad0ddacb 100644 --- a/go/vt/sidecardb/sidecardb.go +++ b/go/vt/sidecardb/sidecardb.go @@ -29,6 +29,7 @@ import ( "vitess.io/vitess/go/constants/sidecar" "vitess.io/vitess/go/history" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/config" "vitess.io/vitess/go/mysql/sqlerror" @@ -46,8 +47,9 @@ import ( ) const ( - sidecarDBExistsQuery = "select 'true' as 'dbexists' from information_schema.SCHEMATA where SCHEMA_NAME = %a" - showCreateTableQuery = "show create table %s.%s" + sidecarDBExistsQuery = "select 'true' as 'dbexists' from information_schema.SCHEMATA where SCHEMA_NAME = %a" + showCreateTableQuery = "show create table %s.%s" + sidecarCollationQuery = "select @@global.collation_server" maxDDLErrorHistoryLength = 100 @@ -198,6 +200,8 @@ type schemaInit struct { exec Exec dbCreated bool // The first upgrade/create query will also create the sidecar database if required. parser *sqlparser.Parser + collEnv *collations.Environment + coll collations.ID } // Exec is a callback that has to be passed to Init() to @@ -229,7 +233,7 @@ func getDDLErrorHistory() []*ddlError { // Init creates or upgrades the sidecar database based on // the declarative schema defined for all tables. -func Init(ctx context.Context, exec Exec, parser *sqlparser.Parser) error { +func Init(ctx context.Context, exec Exec, collEnv *collations.Environment, parser *sqlparser.Parser) error { printCallerDetails() // for debug purposes only, remove in v17 log.Infof("Starting sidecardb.Init()") @@ -238,9 +242,10 @@ func Init(ctx context.Context, exec Exec, parser *sqlparser.Parser) error { }) si := &schemaInit{ - ctx: ctx, - exec: exec, - parser: parser, + ctx: ctx, + exec: exec, + collEnv: collEnv, + parser: parser, } // There are paths in the tablet initialization where we @@ -269,6 +274,10 @@ func Init(ctx context.Context, exec Exec, parser *sqlparser.Parser) error { } defer resetSQLMode() + if si.coll, err = si.collation(); err != nil { + return err + } + for _, table := range sidecarTables { if err := si.ensureSchema(table); err != nil { return err @@ -342,6 +351,22 @@ func (si *schemaInit) setCurrentDatabase(dbName string) error { return err } +func (si *schemaInit) collation() (collations.ID, error) { + rs, err := si.exec(si.ctx, sidecarCollationQuery, 2, false) + if err != nil { + log.Error(err) + return collations.Unknown, err + } + + switch len(rs.Rows) { + case 1: + return si.collEnv.LookupByName(rs.Rows[0][0].ToString()), nil + default: + // This should never happen. + return collations.Unknown, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "invalid results for SidecarDB query %q as it produced %d rows", sidecarCollationQuery, len(rs.Rows)) + } +} + // Gets existing schema of a table in the sidecar database. func (si *schemaInit) getCurrentSchema(tableName string) (string, error) { var currentTableSchema string @@ -375,7 +400,8 @@ func (si *schemaInit) findTableSchemaDiff(tableName, current, desired string) (s TableCharsetCollateStrategy: schemadiff.TableCharsetCollateIgnoreAlways, AlterTableAlgorithmStrategy: schemadiff.AlterTableAlgorithmStrategyCopy, } - diff, err := schemadiff.DiffCreateTablesQueries(current, desired, hints, si.parser) + env := schemadiff.NewEnv(si.collEnv, si.coll, si.parser) + diff, err := schemadiff.DiffCreateTablesQueries(env, current, desired, hints) if err != nil { return "", err } @@ -495,7 +521,12 @@ func AddSchemaInitQueries(db *fakesqldb.DB, populateTables bool, parser *sqlpars config.DefaultSQLMode, ) db.AddQuery("select @@session.sql_mode as sql_mode", sqlModeResult) - + collationResult := sqltypes.MakeTestResult(sqltypes.MakeTestFields( + "@@global.collation_server ", + "varchar"), + "utf8mb4_0900_ai_ci", + ) + db.AddQuery("select @@global.collation_server", collationResult) db.AddQuery("set @@session.sql_mode=''", &sqltypes.Result{}) } diff --git a/go/vt/sidecardb/sidecardb_test.go b/go/vt/sidecardb/sidecardb_test.go index 1565e0cb754..535f9c65fda 100644 --- a/go/vt/sidecardb/sidecardb_test.go +++ b/go/vt/sidecardb/sidecardb_test.go @@ -32,6 +32,7 @@ import ( "vitess.io/vitess/go/stats" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/fakesqldb" "vitess.io/vitess/go/sqltypes" ) @@ -42,6 +43,7 @@ func TestInitErrors(t *testing.T) { db := fakesqldb.New(t) defer db.Close() + collEnv := collations.MySQL8() parser := sqlparser.NewTestParser() AddSchemaInitQueries(db, false, parser) @@ -87,7 +89,7 @@ func TestInitErrors(t *testing.T) { } require.Equal(t, int64(0), getDDLCount()) - err = Init(ctx, exec, parser) + err = Init(ctx, exec, collEnv, parser) require.NoError(t, err) require.Equal(t, int64(len(sidecarTables)-len(schemaErrors)), getDDLCount()) require.Equal(t, int64(len(schemaErrors)), getDDLErrorCount()) @@ -126,6 +128,7 @@ func TestMiscSidecarDB(t *testing.T) { db := fakesqldb.New(t) defer db.Close() + collEnv := collations.MySQL8() parser := sqlparser.NewTestParser() AddSchemaInitQueries(db, false, parser) db.AddQuery("use dbname", &sqltypes.Result{}) @@ -158,7 +161,7 @@ func TestMiscSidecarDB(t *testing.T) { ddlErrorCount.Set(0) ddlCount.Set(0) require.Equal(t, int64(0), getDDLCount()) - err = Init(ctx, exec, parser) + err = Init(ctx, exec, collEnv, parser) require.NoError(t, err) require.Equal(t, int64(len(sidecarTables)), getDDLCount()) @@ -167,15 +170,16 @@ func TestMiscSidecarDB(t *testing.T) { AddSchemaInitQueries(db, true, parser) // tests init on already inited db - err = Init(ctx, exec, parser) + err = Init(ctx, exec, collEnv, parser) require.NoError(t, err) require.Equal(t, int64(len(sidecarTables)), getDDLCount()) // tests misc paths not covered above si := &schemaInit{ - ctx: ctx, - exec: exec, - parser: parser, + ctx: ctx, + exec: exec, + collEnv: collations.MySQL8(), + parser: parser, } err = si.setCurrentDatabase(sidecar.GetIdentifier()) @@ -226,7 +230,8 @@ func TestAlterTableAlgorithm(t *testing.T) { {"modify column", "t1", "create table if not exists _vt.t1(i int)", "create table if not exists _vt.t(i float)"}, } si := &schemaInit{ - parser: sqlparser.NewTestParser(), + collEnv: collations.MySQL8(), + parser: sqlparser.NewTestParser(), } copyAlgo := sqlparser.AlgorithmValue("COPY") for _, tc := range testCases { diff --git a/go/vt/vtadmin/api_test.go b/go/vt/vtadmin/api_test.go index c7020bd4e20..ccd83afa239 100644 --- a/go/vt/vtadmin/api_test.go +++ b/go/vt/vtadmin/api_test.go @@ -1069,7 +1069,7 @@ func TestGetKeyspace(t *testing.T) { testutil.AddShards(ctx, t, ts, shards...) topos[i] = ts vtctlds[i] = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) } @@ -1313,10 +1313,10 @@ func TestGetKeyspaces(t *testing.T) { servers := []vtctlservicepb.VtctldServer{ testutil.NewVtctldServerWithTabletManagerClient(t, topos[0], nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }), testutil.NewVtctldServerWithTabletManagerClient(t, topos[1], nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }), } @@ -1548,7 +1548,7 @@ func TestGetSchema(t *testing.T) { defer cancel() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddTablets(ctx, t, tt.ts, nil, vtadmintestutil.TopodataTabletsFromVTAdminTablets(tt.tablets)...) @@ -2202,10 +2202,10 @@ func TestGetSchemas(t *testing.T) { vtctlds := []vtctlservicepb.VtctldServer{ testutil.NewVtctldServerWithTabletManagerClient(t, topos[0], &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }), testutil.NewVtctldServerWithTabletManagerClient(t, topos[1], &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }), } @@ -2641,7 +2641,7 @@ func TestGetSrvKeyspace(t *testing.T) { toposerver := memorytopo.NewServer(ctx, tt.cells...) vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -2805,7 +2805,7 @@ func TestGetSrvKeyspaces(t *testing.T) { } vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -2970,7 +2970,7 @@ func TestGetSrvVSchema(t *testing.T) { toposerver := memorytopo.NewServer(ctx, tt.cells...) vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -3264,7 +3264,7 @@ func TestGetSrvVSchemas(t *testing.T) { toposerver := memorytopo.NewServer(ctx, tt.cells...) vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { @@ -5116,7 +5116,7 @@ func TestVTExplain(t *testing.T) { } vtctldserver := testutil.NewVtctldServerWithTabletManagerClient(t, toposerver, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.WithTestServer(t, vtctldserver, func(t *testing.T, vtctldClient vtctldclient.VtctldClient) { diff --git a/go/vt/vtadmin/testutil/cluster.go b/go/vt/vtadmin/testutil/cluster.go index ca9dfe00dac..70e59bd2b9e 100644 --- a/go/vt/vtadmin/testutil/cluster.go +++ b/go/vt/vtadmin/testutil/cluster.go @@ -27,6 +27,7 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/grpc" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/grpcclient" @@ -171,7 +172,7 @@ func BuildIntegrationTestCluster(t testing.TB, ctx context.Context, c *vtadminpb ts, factory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := grpcvtctldtestutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) localclient := localvtctldclient.New(vtctld) diff --git a/go/vt/vtctl/grpcvtctldclient/client_test.go b/go/vt/vtctl/grpcvtctldclient/client_test.go index 7166bafbcff..05cfdfb764c 100644 --- a/go/vt/vtctl/grpcvtctldclient/client_test.go +++ b/go/vt/vtctl/grpcvtctldclient/client_test.go @@ -23,6 +23,7 @@ import ( "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/test/utils" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/topo" @@ -42,7 +43,7 @@ func TestFindAllShardsInKeyspace(t *testing.T) { ts := memorytopo.NewServer(ctx, "cell1") defer ts.Close() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.WithTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { @@ -89,7 +90,7 @@ func TestGetKeyspace(t *testing.T) { ts := memorytopo.NewServer(ctx, "cell1") defer ts.Close() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.WithTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { @@ -118,7 +119,7 @@ func TestGetKeyspaces(t *testing.T) { ts := memorytopo.NewServer(ctx, "cell1") defer ts.Close() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + return grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.WithTestServer(t, vtctld, func(t *testing.T, client vtctldclient.VtctldClient) { diff --git a/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go b/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go index c3915792e4a..1b16b8e2e23 100644 --- a/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go +++ b/go/vt/vtctl/grpcvtctldserver/endtoend/init_shard_primary_test.go @@ -95,7 +95,7 @@ func TestInitShardPrimary(t *testing.T) { tablet.TM.QueryServiceControl.(*tabletservermock.Controller).SetQueryServiceEnabledForTests(true) } - vtctld := grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + vtctld := grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) resp, err := vtctld.InitShardPrimary(context.Background(), &vtctldatapb.InitShardPrimaryRequest{ Keyspace: tablet1.Tablet.Keyspace, Shard: tablet1.Tablet.Shard, @@ -150,7 +150,7 @@ func TestInitShardPrimaryNoFormerPrimary(t *testing.T) { tablet.TM.QueryServiceControl.(*tabletservermock.Controller).SetQueryServiceEnabledForTests(true) } - vtctld := grpcvtctldserver.NewVtctldServer(ts, sqlparser.NewTestParser()) + vtctld := grpcvtctldserver.NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) _, err := vtctld.InitShardPrimary(context.Background(), &vtctldatapb.InitShardPrimaryRequest{ Keyspace: tablet1.Tablet.Keyspace, Shard: tablet1.Tablet.Shard, diff --git a/go/vt/vtctl/grpcvtctldserver/server.go b/go/vt/vtctl/grpcvtctldserver/server.go index 0df6d3ccaa9..8b61baa178e 100644 --- a/go/vt/vtctl/grpcvtctldserver/server.go +++ b/go/vt/vtctl/grpcvtctldserver/server.go @@ -34,6 +34,7 @@ import ( "google.golang.org/grpc" "vitess.io/vitess/go/event" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/netutil" "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sets" @@ -92,13 +93,13 @@ type VtctldServer struct { } // NewVtctldServer returns a new VtctldServer for the given topo server. -func NewVtctldServer(ts *topo.Server, parser *sqlparser.Parser) *VtctldServer { +func NewVtctldServer(ts *topo.Server, collationEnv *collations.Environment, parser *sqlparser.Parser) *VtctldServer { tmc := tmclient.NewTabletManagerClient() return &VtctldServer{ ts: ts, tmc: tmc, - ws: workflow.NewServer(ts, tmc, parser), + ws: workflow.NewServer(ts, tmc, collationEnv, parser), } } @@ -108,7 +109,7 @@ func NewTestVtctldServer(ts *topo.Server, tmc tmclient.TabletManagerClient) *Vtc return &VtctldServer{ ts: ts, tmc: tmc, - ws: workflow.NewServer(ts, tmc, sqlparser.NewTestParser()), + ws: workflow.NewServer(ts, tmc, collations.MySQL8(), sqlparser.NewTestParser()), } } @@ -4989,8 +4990,8 @@ func (s *VtctldServer) WorkflowUpdate(ctx context.Context, req *vtctldatapb.Work } // StartServer registers a VtctldServer for RPCs on the given gRPC server. -func StartServer(s *grpc.Server, ts *topo.Server, parser *sqlparser.Parser) { - vtctlservicepb.RegisterVtctldServer(s, NewVtctldServer(ts, parser)) +func StartServer(s *grpc.Server, ts *topo.Server, collationEnv *collations.Environment, parser *sqlparser.Parser) { + vtctlservicepb.RegisterVtctldServer(s, NewVtctldServer(ts, collationEnv, parser)) } // getTopologyCell is a helper method that returns a topology cell given its path. diff --git a/go/vt/vtctl/grpcvtctldserver/server_slow_test.go b/go/vt/vtctl/grpcvtctldserver/server_slow_test.go index 9625d0c281b..ba25163c0ef 100644 --- a/go/vt/vtctl/grpcvtctldserver/server_slow_test.go +++ b/go/vt/vtctl/grpcvtctldserver/server_slow_test.go @@ -26,6 +26,7 @@ import ( "vitess.io/vitess/go/vt/sqlparser" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/mysql" @@ -312,7 +313,7 @@ func TestEmergencyReparentShardSlow(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.EmergencyReparentShard(ctx, tt.req) @@ -610,7 +611,7 @@ func TestPlannedReparentShardSlow(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.PlannedReparentShard(ctx, tt.req) @@ -740,7 +741,7 @@ func TestSleepTablet(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) start := time.Now() diff --git a/go/vt/vtctl/grpcvtctldserver/server_test.go b/go/vt/vtctl/grpcvtctldserver/server_test.go index 67056c3c410..ef1b8a8347b 100644 --- a/go/vt/vtctl/grpcvtctldserver/server_test.go +++ b/go/vt/vtctl/grpcvtctldserver/server_test.go @@ -34,6 +34,7 @@ import ( "github.com/stretchr/testify/require" "vitess.io/vitess/go/mysql" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/replication" "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sqltypes" @@ -86,7 +87,7 @@ func TestPanicHandler(t *testing.T) { }() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, nil, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.AddCellInfo(context.Background(), nil) @@ -142,7 +143,7 @@ func TestAddCellInfo(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.AddCellInfo(ctx, tt.req) if tt.shouldErr { @@ -215,7 +216,7 @@ func TestAddCellsAlias(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.AddCellsAlias(ctx, tt.req) if tt.shouldErr { @@ -327,7 +328,7 @@ func TestApplyRoutingRules(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.ApplyRoutingRules(ctx, tt.req) if tt.shouldErr { @@ -595,7 +596,7 @@ func TestApplyVSchema(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddKeyspace(ctx, t, ts, &vtctldatapb.Keyspace{ @@ -878,7 +879,7 @@ func TestBackup(t *testing.T) { testutil.AddTablet(ctx, t, tt.ts, tt.tablet, nil) } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) client := localvtctldclient.New(vtctld) stream, err := client.Backup(ctx, tt.req) @@ -1218,7 +1219,7 @@ func TestBackupShard(t *testing.T) { }, tt.tablets..., ) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) client := localvtctldclient.New(vtctld) stream, err := client.BackupShard(ctx, tt.req) @@ -1438,7 +1439,7 @@ func TestCancelSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.CancelSchemaMigration(ctx, test.req) @@ -1671,7 +1672,7 @@ func TestChangeTabletType(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ TopoServer: ts, }, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ @@ -1719,7 +1720,7 @@ func TestChangeTabletType(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &testutil.TabletManagerClient{ TopoServer: nil, }, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddTablet(ctx, t, ts, &topodatapb.Tablet{ @@ -1941,7 +1942,7 @@ func TestCleanupSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.CleanupSchemaMigration(ctx, test.req) @@ -2143,7 +2144,7 @@ func TestForceCutOverSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.ForceCutOverSchemaMigration(ctx, test.req) @@ -2347,7 +2348,7 @@ func TestCompleteSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.CompleteSchemaMigration(ctx, test.req) @@ -2603,7 +2604,7 @@ func TestCreateKeyspace(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) for name, ks := range tt.topo { @@ -2881,7 +2882,7 @@ func TestCreateShard(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) for _, ks := range tt.keyspaces { @@ -2936,7 +2937,7 @@ func TestDeleteCellInfo(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.DeleteCellInfo(ctx, tt.req) if tt.shouldErr { @@ -2997,7 +2998,7 @@ func TestDeleteCellsAlias(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.DeleteCellsAlias(ctx, tt.req) if tt.shouldErr { @@ -3229,7 +3230,7 @@ func TestDeleteKeyspace(t *testing.T) { ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) defer ts.Close() vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddKeyspaces(ctx, t, ts, tt.keyspaces...) @@ -3743,7 +3744,7 @@ func TestDeleteShards(t *testing.T) { ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddShards(ctx, t, ts, tt.shards...) @@ -3886,7 +3887,7 @@ func TestDeleteSrvKeyspace(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.DeleteSrvVSchema(ctx, tt.req) if tt.shouldErr { @@ -4347,7 +4348,7 @@ func TestDeleteTablets(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) // Setup tablets and shards @@ -4576,7 +4577,7 @@ func TestEmergencyReparentShard(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.EmergencyReparentShard(ctx, tt.req) @@ -4718,7 +4719,7 @@ func TestExecuteFetchAsApp(t *testing.T) { testutil.AddTablet(ctx, t, ts, tt.tablet, nil) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.ExecuteFetchAsApp(ctx, tt.req) if tt.shouldErr { @@ -4845,7 +4846,7 @@ func TestExecuteFetchAsDBA(t *testing.T) { testutil.AddTablet(ctx, t, ts, tt.tablet, nil) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.ExecuteFetchAsDBA(ctx, tt.req) if tt.shouldErr { @@ -5030,7 +5031,7 @@ func TestExecuteHook(t *testing.T) { t.Run(tt.name, func(t *testing.T) { testutil.AddTablets(ctx, t, tt.ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.ExecuteHook(ctx, tt.req) @@ -5051,7 +5052,7 @@ func TestFindAllShardsInKeyspace(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) ks := &vtctldatapb.Keyspace{ @@ -5093,7 +5094,7 @@ func TestGetBackups(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.BackupStorage.Backups = map[string][]string{ @@ -5201,7 +5202,7 @@ func TestGetKeyspace(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) expected := &vtctldatapb.GetKeyspaceResponse{ @@ -5227,7 +5228,7 @@ func TestGetCellInfoNames(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2", "cell3") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.GetCellInfoNames(ctx, &vtctldatapb.GetCellInfoNamesRequest{}) @@ -5236,7 +5237,7 @@ func TestGetCellInfoNames(t *testing.T) { ts = memorytopo.NewServer(ctx) vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err = vtctld.GetCellInfoNames(ctx, &vtctldatapb.GetCellInfoNamesRequest{}) @@ -5245,7 +5246,7 @@ func TestGetCellInfoNames(t *testing.T) { ts, topofactory := memorytopo.NewServerAndFactory(ctx, "cell1") vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) topofactory.SetError(assert.AnError) @@ -5260,7 +5261,7 @@ func TestGetCellInfo(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) expected := &topodatapb.CellInfo{ @@ -5288,7 +5289,7 @@ func TestGetCellsAliases(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "c11", "c12", "c13", "c21", "c22") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) alias1 := &topodatapb.CellsAlias{ @@ -5315,7 +5316,7 @@ func TestGetCellsAliases(t *testing.T) { ts, topofactory := memorytopo.NewServerAndFactory(ctx) vtctld = testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) topofactory.SetError(assert.AnError) @@ -5396,7 +5397,7 @@ func TestGetFullStatus(t *testing.T) { ServerUuid: tt.serverUUID, }, }, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ @@ -5422,7 +5423,7 @@ func TestGetKeyspaces(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "cell1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.GetKeyspaces(ctx, &vtctldatapb.GetKeyspacesRequest{}) @@ -5590,7 +5591,7 @@ func TestGetPermissions(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.GetPermissions(ctx, tt.req) if tt.shouldErr { @@ -5666,7 +5667,7 @@ func TestGetRoutingRules(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.GetRoutingRules(ctx, &vtctldatapb.GetRoutingRulesRequest{}) if tt.shouldErr { @@ -5691,7 +5692,7 @@ func TestGetSchema(t *testing.T) { }{}, } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) validAlias := &topodatapb.TabletAlias{ @@ -6056,7 +6057,7 @@ func TestGetSchemaMigrations(t *testing.T) { ts, factory := memorytopo.NewServerAndFactory(ctx, cells...) testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{AlsoSetShardPrimary: true}, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) if test.failTopo { @@ -6147,7 +6148,7 @@ func TestGetShard(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddShards(ctx, t, ts, tt.topo...) @@ -6284,7 +6285,7 @@ func TestGetSrvKeyspaceNames(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.GetSrvKeyspaceNames(ctx, tt.req) if tt.shouldErr { @@ -6441,7 +6442,7 @@ func TestGetSrvKeyspaces(t *testing.T) { testutil.AddSrvKeyspaces(t, ts, tt.srvKeyspaces...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) if tt.topoErr != nil { @@ -6468,7 +6469,7 @@ func TestGetSrvVSchema(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "zone1", "zone2") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) zone1SrvVSchema := &vschemapb.SrvVSchema{ @@ -6679,7 +6680,7 @@ func TestGetSrvVSchemas(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, "zone1", "zone2", "zone3") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) zone1SrvVSchema := &vschemapb.SrvVSchema{ @@ -6739,7 +6740,7 @@ func TestGetTablet(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) tablet := &topodatapb.Tablet{ @@ -7362,7 +7363,7 @@ func TestGetTablets(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, tt.cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) @@ -7386,7 +7387,7 @@ func TestGetTopologyPath(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "cell1", "cell2", "cell3") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) err := ts.CreateKeyspace(ctx, "keyspace1", &topodatapb.Keyspace{}) @@ -7475,7 +7476,7 @@ func TestGetVSchema(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) t.Run("found", func(t *testing.T) { @@ -7706,7 +7707,7 @@ func TestLaunchSchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.LaunchSchemaMigration(ctx, test.req) @@ -7793,7 +7794,7 @@ func TestPingTablet(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.PingTablet(ctx, tt.req) @@ -8041,7 +8042,7 @@ func TestPlannedReparentShard(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.PlannedReparentShard(ctx, tt.req) @@ -8084,7 +8085,7 @@ func TestRebuildKeyspaceGraph(t *testing.T) { Name: "testkeyspace", }) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.RebuildKeyspaceGraph(ctx, &vtctldatapb.RebuildKeyspaceGraphRequest{ @@ -8101,7 +8102,7 @@ func TestRebuildKeyspaceGraph(t *testing.T) { ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.RebuildKeyspaceGraph(context.Background(), &vtctldatapb.RebuildKeyspaceGraphRequest{ @@ -8121,7 +8122,7 @@ func TestRebuildKeyspaceGraph(t *testing.T) { Name: "testkeyspace", }) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) factory.SetError(assert.AnError) @@ -8142,7 +8143,7 @@ func TestRebuildKeyspaceGraph(t *testing.T) { Name: "testkeyspace", }) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) lctx, unlock, lerr := ts.LockKeyspace(context.Background(), "testkeyspace", "test lock") @@ -8191,7 +8192,7 @@ func TestRebuildVSchemaGraph(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.RebuildVSchemaGraph(ctx, req) if tt.shouldErr { @@ -8290,7 +8291,7 @@ func TestRefreshState(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.RefreshState(ctx, tt.req) if tt.shouldErr { @@ -8475,7 +8476,7 @@ func TestRefreshStateByShard(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.RefreshStateByShard(ctx, tt.req) if tt.shouldErr { @@ -8579,7 +8580,7 @@ func TestReloadSchema(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.ReloadSchema(ctx, tt.req) if tt.shouldErr { @@ -8677,7 +8678,7 @@ func TestReloadSchemaKeyspace(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.ReloadSchemaKeyspace(ctx, tt.req) if tt.shouldErr { @@ -8835,7 +8836,7 @@ func TestReloadSchemaShard(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.ReloadSchemaShard(ctx, tt.req) if tt.shouldErr { @@ -8854,7 +8855,7 @@ func TestRemoveBackup(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) setup := func() { @@ -9045,7 +9046,7 @@ func TestRemoveKeyspaceCell(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) // Setup topo @@ -9334,7 +9335,7 @@ func TestRemoveShardCell(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) // Setup shard topos and replication graphs. @@ -9944,7 +9945,7 @@ func TestReparentTablet(t *testing.T) { defer cancel() ts, topofactory := memorytopo.NewServerAndFactory(ctx, cells...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddTablets(ctx, t, ts, &testutil.AddTabletOptions{ @@ -10077,7 +10078,7 @@ func TestRestoreFromBackup(t *testing.T) { }, tt.tablets..., ) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) client := localvtctldclient.New(vtctld) stream, err := client.RestoreFromBackup(ctx, tt.req) @@ -10295,7 +10296,7 @@ func TestRetrySchemaMigration(t *testing.T) { }, test.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, test.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.RetrySchemaMigration(ctx, test.req) @@ -10402,7 +10403,7 @@ func TestRunHealthCheck(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.RunHealthCheck(ctx, tt.req) if tt.shouldErr { @@ -10482,7 +10483,7 @@ func TestSetKeyspaceDurabilityPolicy(t *testing.T) { testutil.AddKeyspaces(ctx, t, ts, tt.keyspaces...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.SetKeyspaceDurabilityPolicy(ctx, tt.req) if tt.expectedErr != "" { @@ -10579,7 +10580,7 @@ func TestSetShardIsPrimaryServing(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.SetShardIsPrimaryServing(tt.ctx, tt.req) if tt.shouldErr { @@ -10829,7 +10830,7 @@ func TestSetShardTabletControl(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.SetShardTabletControl(tt.ctx, tt.req) if tt.shouldErr { @@ -11033,7 +11034,7 @@ func TestSetWritable(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.SetWritable(ctx, tt.req) @@ -11054,7 +11055,7 @@ func TestShardReplicationAdd(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) tablets := []*topodatapb.Tablet{ @@ -11349,7 +11350,7 @@ func TestShardReplicationPositions(t *testing.T) { }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) requestCtx := ctx @@ -11380,7 +11381,7 @@ func TestShardReplicationRemove(t *testing.T) { ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) tablets := []*topodatapb.Tablet{ @@ -11540,7 +11541,7 @@ func TestSourceShardAdd(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddShards(ctx, t, ts, tt.shards...) @@ -11675,7 +11676,7 @@ func TestSourceShardDelete(t *testing.T) { defer cancel() ts := memorytopo.NewServer(ctx, "zone1") vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) testutil.AddShards(ctx, t, ts, tt.shards...) @@ -11867,7 +11868,7 @@ func TestStartReplication(t *testing.T) { AlsoSetShardPrimary: true, }, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.StartReplication(ctx, tt.req) @@ -12004,7 +12005,7 @@ func TestStopReplication(t *testing.T) { testutil.AddTablets(ctx, t, ts, nil, tt.tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) _, err := vtctld.StopReplication(ctx, tt.req) @@ -12391,7 +12392,7 @@ func TestTabletExternallyReparented(t *testing.T) { TopoServer: ts, } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) if tt.tmcHasNoTopo { @@ -12576,7 +12577,7 @@ func TestUpdateCellInfo(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.UpdateCellInfo(ctx, tt.req) if tt.shouldErr { @@ -12726,7 +12727,7 @@ func TestUpdateCellsAlias(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.UpdateCellsAlias(ctx, tt.req) if tt.shouldErr { @@ -12834,7 +12835,7 @@ func TestValidate(t *testing.T) { SkipShardCreation: false, }, tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, nil, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.Validate(ctx, &vtctldatapb.ValidateRequest{ @@ -12951,7 +12952,7 @@ func TestValidateSchemaKeyspace(t *testing.T) { }, tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) schema1 := &tabletmanagerdatapb.SchemaDefinition{ @@ -13137,7 +13138,7 @@ func TestValidateVersionKeyspace(t *testing.T) { }, tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) tests := []*struct { @@ -13252,7 +13253,7 @@ func TestValidateVersionShard(t *testing.T) { }, tablets...) vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, ts, &tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) tests := []*struct { @@ -13844,7 +13845,7 @@ func TestValidateShard(t *testing.T) { } vtctld := testutil.NewVtctldServerWithTabletManagerClient(t, tt.ts, tt.tmc, func(ts *topo.Server) vtctlservicepb.VtctldServer { - return NewVtctldServer(ts, sqlparser.NewTestParser()) + return NewVtctldServer(ts, collations.MySQL8(), sqlparser.NewTestParser()) }) resp, err := vtctld.ValidateShard(ctx, tt.req) if tt.shouldErr { diff --git a/go/vt/vtctl/workflow/materializer.go b/go/vt/vtctl/workflow/materializer.go index eb9e7c25f32..0dea3034a31 100644 --- a/go/vt/vtctl/workflow/materializer.go +++ b/go/vt/vtctl/workflow/materializer.go @@ -24,6 +24,7 @@ import ( "text/template" "time" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/concurrency" "vitess.io/vitess/go/vt/key" @@ -64,7 +65,8 @@ type materializer struct { primaryVindexesDiffer bool workflowType binlogdatapb.VReplicationWorkflowType - parser *sqlparser.Parser + parser *sqlparser.Parser + collationEnv *collations.Environment } func (mz *materializer) getWorkflowSubType() (binlogdatapb.VReplicationWorkflowSubType, error) { @@ -454,7 +456,8 @@ func (mz *materializer) deploySchema() error { // We use schemadiff to normalize the schema. // For now, and because this is could have wider implications, we ignore any errors in // reading the source schema. - schema, err := schemadiff.NewSchemaFromQueries(applyDDLs, mz.parser) + env := schemadiff.NewEnv(mz.collationEnv, mz.collationEnv.DefaultConnectionCharset(), mz.parser) + schema, err := schemadiff.NewSchemaFromQueries(env, applyDDLs) if err != nil { log.Error(vterrors.Wrapf(err, "AtomicCopy: failed to normalize schema via schemadiff")) } else { diff --git a/go/vt/vtctl/workflow/materializer_env_test.go b/go/vt/vtctl/workflow/materializer_env_test.go index 14ea59f690e..4203e9591ce 100644 --- a/go/vt/vtctl/workflow/materializer_env_test.go +++ b/go/vt/vtctl/workflow/materializer_env_test.go @@ -28,6 +28,7 @@ import ( "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/sqltypes" "vitess.io/vitess/go/vt/mysqlctl/tmutils" "vitess.io/vitess/go/vt/sqlparser" @@ -83,7 +84,7 @@ func newTestMaterializerEnv(t *testing.T, ctx context.Context, ms *vtctldatapb.M tmc: newTestMaterializerTMClient(), } parser := sqlparser.NewTestParser() - env.ws = NewServer(env.topoServ, env.tmc, parser) + env.ws = NewServer(env.topoServ, env.tmc, collations.MySQL8(), parser) tabletID := 100 for _, shard := range sources { _ = env.addTablet(tabletID, env.ms.SourceKeyspace, shard, topodatapb.TabletType_PRIMARY) diff --git a/go/vt/vtctl/workflow/materializer_test.go b/go/vt/vtctl/workflow/materializer_test.go index defa3ba508a..4444a35a29b 100644 --- a/go/vt/vtctl/workflow/materializer_test.go +++ b/go/vt/vtctl/workflow/materializer_test.go @@ -28,6 +28,7 @@ import ( "golang.org/x/exp/maps" "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/sqltypes" @@ -3015,7 +3016,7 @@ func TestMaterializerNoSourcePrimary(t *testing.T) { cell: "cell", tmc: newTestMaterializerTMClient(), } - env.ws = NewServer(env.topoServ, env.tmc, sqlparser.NewTestParser()) + env.ws = NewServer(env.topoServ, env.tmc, collations.MySQL8(), sqlparser.NewTestParser()) defer env.close() tabletID := 100 diff --git a/go/vt/vtctl/workflow/server.go b/go/vt/vtctl/workflow/server.go index 6dc009fea6c..31a01d3b5e9 100644 --- a/go/vt/vtctl/workflow/server.go +++ b/go/vt/vtctl/workflow/server.go @@ -35,6 +35,7 @@ import ( "google.golang.org/protobuf/encoding/prototext" "google.golang.org/protobuf/proto" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/mysql/sqlerror" "vitess.io/vitess/go/protoutil" "vitess.io/vitess/go/sets" @@ -143,17 +144,19 @@ type Server struct { ts *topo.Server tmc tmclient.TabletManagerClient // Limit the number of concurrent background goroutines if needed. - sem *semaphore.Weighted - parser *sqlparser.Parser + sem *semaphore.Weighted + collationEnv *collations.Environment + parser *sqlparser.Parser } // NewServer returns a new server instance with the given topo.Server and // TabletManagerClient. -func NewServer(ts *topo.Server, tmc tmclient.TabletManagerClient, parser *sqlparser.Parser) *Server { +func NewServer(ts *topo.Server, tmc tmclient.TabletManagerClient, collationEnv *collations.Environment, parser *sqlparser.Parser) *Server { return &Server{ - ts: ts, - tmc: tmc, - parser: parser, + ts: ts, + tmc: tmc, + collationEnv: collationEnv, + parser: parser, } } @@ -161,6 +164,10 @@ func (s *Server) SQLParser() *sqlparser.Parser { return s.parser } +func (s *Server) CollationEnv() *collations.Environment { + return s.collationEnv +} + // CheckReshardingJournalExistsOnTablet returns the journal (or an empty // journal) and a boolean to indicate if the resharding_journal table exists on // the given tablet. @@ -1315,12 +1322,13 @@ func (s *Server) LookupVindexExternalize(ctx context.Context, req *vtctldatapb.L // tables based on the materialization specs. func (s *Server) Materialize(ctx context.Context, ms *vtctldatapb.MaterializeSettings) error { mz := &materializer{ - ctx: ctx, - ts: s.ts, - sourceTs: s.ts, - tmc: s.tmc, - ms: ms, - parser: s.SQLParser(), + ctx: ctx, + ts: s.ts, + sourceTs: s.ts, + tmc: s.tmc, + ms: ms, + parser: s.SQLParser(), + collationEnv: s.CollationEnv(), } err := mz.createMaterializerStreams() @@ -1460,6 +1468,7 @@ func (s *Server) moveTablesCreate(ctx context.Context, req *vtctldatapb.MoveTabl tmc: s.tmc, ms: ms, workflowType: workflowType, + collationEnv: s.CollationEnv(), parser: s.SQLParser(), } err = mz.createMoveTablesStreams(req) diff --git a/go/vt/vtctl/workflow/server_test.go b/go/vt/vtctl/workflow/server_test.go index e3b33e19dc9..bb1f6872273 100644 --- a/go/vt/vtctl/workflow/server_test.go +++ b/go/vt/vtctl/workflow/server_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/require" "google.golang.org/protobuf/encoding/prototext" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/sqltypes" @@ -144,7 +145,7 @@ func TestCheckReshardingJournalExistsOnTablet(t *testing.T) { }, } - ws := NewServer(nil, tmc, sqlparser.NewTestParser()) + ws := NewServer(nil, tmc, collations.MySQL8(), sqlparser.NewTestParser()) journal, exists, err := ws.CheckReshardingJournalExistsOnTablet(ctx, tt.tablet, 1) if tt.shouldErr { assert.Error(t, err) diff --git a/go/vt/vtexplain/vtexplain_vttablet.go b/go/vt/vtexplain/vtexplain_vttablet.go index fa01fc319b4..53153b699f4 100644 --- a/go/vt/vtexplain/vtexplain_vttablet.go +++ b/go/vt/vtexplain/vtexplain_vttablet.go @@ -304,6 +304,15 @@ func newTabletEnvironment(ddls []sqlparser.DDLStatement, opts *Options, collatio {sqltypes.NewVarChar("STRICT_TRANS_TABLES")}, }, }, + "select @@global.collation_server": { + Fields: []*querypb.Field{{ + Type: sqltypes.VarChar, + Charset: uint32(collations.SystemCollation.Collation), + }}, + Rows: [][]sqltypes.Value{ + {sqltypes.NewVarChar("utf8mb4_0900_ai_ci")}, + }, + }, "select @@session.sql_mode as sql_mode": { Fields: []*querypb.Field{{ Name: "sql_mode", diff --git a/go/vt/vttablet/onlineddl/executor.go b/go/vt/vttablet/onlineddl/executor.go index 71cb2380040..60434f72043 100644 --- a/go/vt/vttablet/onlineddl/executor.go +++ b/go/vt/vttablet/onlineddl/executor.go @@ -2795,12 +2795,13 @@ func (e *Executor) evaluateDeclarativeDiff(ctx context.Context, onlineDDL *schem if newShowCreateTable == "" { return nil, vterrors.Errorf(vtrpcpb.Code_INTERNAL, "unexpected: cannot find table or view even as it was just created: %v", onlineDDL.Table) } + senv := schemadiff.NewEnv(e.env.CollationEnv(), e.env.CollationEnv().DefaultConnectionCharset(), e.env.SQLParser()) hints := &schemadiff.DiffHints{AutoIncrementStrategy: schemadiff.AutoIncrementApplyHigher} switch ddlStmt.(type) { case *sqlparser.CreateTable: - diff, err = schemadiff.DiffCreateTablesQueries(existingShowCreateTable, newShowCreateTable, hints, e.env.SQLParser()) + diff, err = schemadiff.DiffCreateTablesQueries(senv, existingShowCreateTable, newShowCreateTable, hints) case *sqlparser.CreateView: - diff, err = schemadiff.DiffCreateViewsQueries(existingShowCreateTable, newShowCreateTable, hints, e.env.SQLParser()) + diff, err = schemadiff.DiffCreateViewsQueries(senv, existingShowCreateTable, newShowCreateTable, hints) default: return nil, vterrors.Errorf(vtrpcpb.Code_INVALID_ARGUMENT, "expected CREATE TABLE or CREATE VIEW in online DDL statement: %v", onlineDDL.SQL) } diff --git a/go/vt/vttablet/onlineddl/vrepl.go b/go/vt/vttablet/onlineddl/vrepl.go index 66611aa3b26..1bcd27610a0 100644 --- a/go/vt/vttablet/onlineddl/vrepl.go +++ b/go/vt/vttablet/onlineddl/vrepl.go @@ -462,7 +462,7 @@ func (v *VRepl) analyzeTables(ctx context.Context, conn *dbconnpool.DBConnection } v.addedUniqueKeys = vrepl.AddedUniqueKeys(sourceUniqueKeys, targetUniqueKeys, v.parser.ColumnRenameMap()) v.removedUniqueKeys = vrepl.RemovedUniqueKeys(sourceUniqueKeys, targetUniqueKeys, v.parser.ColumnRenameMap()) - v.removedForeignKeyNames, err = vrepl.RemovedForeignKeyNames(v.sqlparser, v.originalShowCreateTable, v.vreplShowCreateTable) + v.removedForeignKeyNames, err = vrepl.RemovedForeignKeyNames(v.sqlparser, v.collationEnv, v.originalShowCreateTable, v.vreplShowCreateTable) if err != nil { return err } diff --git a/go/vt/vttablet/onlineddl/vrepl/foreign_key.go b/go/vt/vttablet/onlineddl/vrepl/foreign_key.go index 26a46879f79..86ef4f74525 100644 --- a/go/vt/vttablet/onlineddl/vrepl/foreign_key.go +++ b/go/vt/vttablet/onlineddl/vrepl/foreign_key.go @@ -21,6 +21,7 @@ limitations under the License. package vrepl import ( + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/schemadiff" "vitess.io/vitess/go/vt/sqlparser" ) @@ -28,14 +29,16 @@ import ( // RemovedForeignKeyNames returns the names of removed foreign keys, ignoring mere name changes func RemovedForeignKeyNames( parser *sqlparser.Parser, + collEnv *collations.Environment, originalCreateTable string, vreplCreateTable string, ) (names []string, err error) { if originalCreateTable == "" || vreplCreateTable == "" { return nil, nil } + env := schemadiff.NewEnv(collEnv, collEnv.DefaultConnectionCharset(), parser) diffHints := schemadiff.DiffHints{ConstraintNamesStrategy: schemadiff.ConstraintNamesIgnoreAll} - diff, err := schemadiff.DiffCreateTablesQueries(originalCreateTable, vreplCreateTable, &diffHints, parser) + diff, err := schemadiff.DiffCreateTablesQueries(env, originalCreateTable, vreplCreateTable, &diffHints) if err != nil { return nil, err } diff --git a/go/vt/vttablet/onlineddl/vrepl/foreign_key_test.go b/go/vt/vttablet/onlineddl/vrepl/foreign_key_test.go index 7b8cf0e7363..7eac802312c 100644 --- a/go/vt/vttablet/onlineddl/vrepl/foreign_key_test.go +++ b/go/vt/vttablet/onlineddl/vrepl/foreign_key_test.go @@ -25,6 +25,7 @@ import ( "github.com/stretchr/testify/assert" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/sqlparser" ) @@ -68,7 +69,7 @@ func TestRemovedForeignKeyNames(t *testing.T) { } for _, tcase := range tcases { t.Run(tcase.before, func(t *testing.T) { - names, err := RemovedForeignKeyNames(sqlparser.NewTestParser(), tcase.before, tcase.after) + names, err := RemovedForeignKeyNames(sqlparser.NewTestParser(), collations.MySQL8(), tcase.before, tcase.after) assert.NoError(t, err) assert.Equal(t, tcase.names, names) }) diff --git a/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go b/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go index c5ece83bf4e..0bac5f486a3 100644 --- a/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go +++ b/go/vt/vttablet/tabletmanager/rpc_vreplication_test.go @@ -25,6 +25,7 @@ import ( "strings" "testing" + "vitess.io/vitess/go/mysql/collations" "vitess.io/vitess/go/vt/vttablet" "github.com/stretchr/testify/require" @@ -111,7 +112,7 @@ func TestCreateVReplicationWorkflow(t *testing.T) { targetTablet := tenv.addTablet(t, targetTabletUID, targetKs, shard) defer tenv.deleteTablet(targetTablet.tablet) - ws := workflow.NewServer(tenv.ts, tenv.tmc, sqlparser.NewTestParser()) + ws := workflow.NewServer(tenv.ts, tenv.tmc, collations.MySQL8(), sqlparser.NewTestParser()) tests := []struct { name string @@ -268,7 +269,7 @@ func TestMoveTables(t *testing.T) { }, }) - ws := workflow.NewServer(tenv.ts, tenv.tmc, sqlparser.NewTestParser()) + ws := workflow.NewServer(tenv.ts, tenv.tmc, collations.MySQL8(), sqlparser.NewTestParser()) tenv.mysqld.Schema = defaultSchema tenv.mysqld.Schema.DatabaseSchema = tenv.dbName @@ -656,7 +657,7 @@ func TestSourceShardSelection(t *testing.T) { defer tenv.deleteTablet(tt.tablet) } - ws := workflow.NewServer(tenv.ts, tenv.tmc, sqlparser.NewTestParser()) + ws := workflow.NewServer(tenv.ts, tenv.tmc, collations.MySQL8(), sqlparser.NewTestParser()) tenv.ts.SaveVSchema(ctx, sourceKs, &vschemapb.Keyspace{ Sharded: true, @@ -855,7 +856,7 @@ func TestFailedMoveTablesCreateCleanup(t *testing.T) { sourceKs, shard, table, table) tenv := newTestEnv(t, ctx, sourceKs, []string{shard}) defer tenv.close() - ws := workflow.NewServer(tenv.ts, tenv.tmc, sqlparser.NewTestParser()) + ws := workflow.NewServer(tenv.ts, tenv.tmc, collations.MySQL8(), sqlparser.NewTestParser()) sourceTablet := tenv.addTablet(t, sourceTabletUID, sourceKs, shard) defer tenv.deleteTablet(sourceTablet.tablet) diff --git a/go/vt/vttablet/tabletmanager/vreplication/vreplicator_test.go b/go/vt/vttablet/tabletmanager/vreplication/vreplicator_test.go index c38402dfd22..3be0525dc88 100644 --- a/go/vt/vttablet/tabletmanager/vreplication/vreplicator_test.go +++ b/go/vt/vttablet/tabletmanager/vreplication/vreplicator_test.go @@ -35,7 +35,6 @@ import ( "vitess.io/vitess/go/vt/dbconfigs" "vitess.io/vitess/go/vt/mysqlctl" "vitess.io/vitess/go/vt/schemadiff" - "vitess.io/vitess/go/vt/sqlparser" binlogdatapb "vitess.io/vitess/go/vt/proto/binlogdata" tabletmanagerdatapb "vitess.io/vitess/go/vt/proto/tabletmanagerdata" @@ -553,7 +552,7 @@ func TestDeferSecondaryKeys(t *testing.T) { // order in the table schema. if !tcase.expectFinalSchemaDiff { currentDDL := getCurrentDDL(tcase.tableName) - sdiff, err := schemadiff.DiffCreateTablesQueries(currentDDL, tcase.initialDDL, diffHints, sqlparser.NewTestParser()) + sdiff, err := schemadiff.DiffCreateTablesQueries(schemadiff.NewTestEnv(), currentDDL, tcase.initialDDL, diffHints) require.NoError(t, err) require.Nil(t, sdiff, "Expected no schema difference but got: %s", sdiff.CanonicalStatementString()) } diff --git a/go/vt/vttablet/tabletserver/schema/engine.go b/go/vt/vttablet/tabletserver/schema/engine.go index 62a1e9afa2b..074cbacff3e 100644 --- a/go/vt/vttablet/tabletserver/schema/engine.go +++ b/go/vt/vttablet/tabletserver/schema/engine.go @@ -162,7 +162,7 @@ func (se *Engine) syncSidecarDB(ctx context.Context, conn *dbconnpool.DBConnecti } return conn.ExecuteFetch(query, maxRows, true) } - if err := sidecardb.Init(ctx, exec, se.env.SQLParser()); err != nil { + if err := sidecardb.Init(ctx, exec, se.env.CollationEnv(), se.env.SQLParser()); err != nil { log.Errorf("Error in sidecardb.Init: %+v", err) if se.env.Config().DB.HasGlobalSettings() { log.Warning("Ignoring sidecardb.Init error for unmanaged tablets") diff --git a/go/vt/vttest/local_cluster.go b/go/vt/vttest/local_cluster.go index 8d75dcebe44..2e95b383e03 100644 --- a/go/vt/vttest/local_cluster.go +++ b/go/vt/vttest/local_cluster.go @@ -556,7 +556,7 @@ func (db *LocalCluster) createVTSchema() error { return db.ExecuteFetch(query, "") } - if err := sidecardb.Init(context.Background(), sidecardbExec, sqlparser.NewTestParser()); err != nil { + if err := sidecardb.Init(context.Background(), sidecardbExec, collations.MySQL8(), sqlparser.NewTestParser()); err != nil { return err } return nil diff --git a/go/vt/wrangler/materializer.go b/go/vt/wrangler/materializer.go index 9d39eec969d..1bda6222d2e 100644 --- a/go/vt/wrangler/materializer.go +++ b/go/vt/wrangler/materializer.go @@ -445,7 +445,7 @@ func (wr *Wrangler) checkIfPreviousJournalExists(ctx context.Context, mz *materi mu sync.Mutex exists bool tablets []string - ws = workflow.NewServer(wr.ts, wr.tmc, wr.parser) + ws = workflow.NewServer(wr.ts, wr.tmc, wr.collationEnv, wr.parser) ) err := forAllSources(func(si *topo.ShardInfo) error { @@ -1266,7 +1266,8 @@ func (mz *materializer) deploySchema(ctx context.Context) error { // We use schemadiff to normalize the schema. // For now, and because this is could have wider implications, we ignore any errors in // reading the source schema. - schema, err := schemadiff.NewSchemaFromQueries(applyDDLs, mz.wr.parser) + env := schemadiff.NewEnv(mz.wr.collationEnv, mz.wr.collationEnv.DefaultConnectionCharset(), mz.wr.parser) + schema, err := schemadiff.NewSchemaFromQueries(env, applyDDLs) if err != nil { log.Error(vterrors.Wrapf(err, "AtomicCopy: failed to normalize schema via schemadiff")) } else { diff --git a/go/vt/wrangler/reparent.go b/go/vt/wrangler/reparent.go index d23f3f016f8..e768b49d785 100644 --- a/go/vt/wrangler/reparent.go +++ b/go/vt/wrangler/reparent.go @@ -60,7 +60,7 @@ func (wr *Wrangler) InitShardPrimary(ctx context.Context, keyspace, shard string ev := &events.Reparent{} // do the work - err = grpcvtctldserver.NewVtctldServer(wr.ts, wr.parser).InitShardPrimaryLocked(ctx, ev, &vtctldatapb.InitShardPrimaryRequest{ + err = grpcvtctldserver.NewVtctldServer(wr.ts, wr.collationEnv, wr.parser).InitShardPrimaryLocked(ctx, ev, &vtctldatapb.InitShardPrimaryRequest{ Keyspace: keyspace, Shard: shard, PrimaryElectTabletAlias: primaryElectTabletAlias, diff --git a/go/vt/wrangler/traffic_switcher.go b/go/vt/wrangler/traffic_switcher.go index 3d7ac806c6c..0bed863d348 100644 --- a/go/vt/wrangler/traffic_switcher.go +++ b/go/vt/wrangler/traffic_switcher.go @@ -224,7 +224,7 @@ func (wr *Wrangler) getWorkflowState(ctx context.Context, targetKeyspace, workfl return nil, nil, err } - ws := workflow.NewServer(wr.ts, wr.tmc, wr.parser) + ws := workflow.NewServer(wr.ts, wr.tmc, wr.collationEnv, wr.parser) state := &workflow.State{ Workflow: workflowName, SourceKeyspace: ts.SourceKeyspaceName(), @@ -1187,7 +1187,7 @@ func (ts *trafficSwitcher) switchShardReads(ctx context.Context, cells []string, // If so, it also returns the list of sourceWorkflows that need to be switched. func (ts *trafficSwitcher) checkJournals(ctx context.Context) (journalsExist bool, sourceWorkflows []string, err error) { var ( - ws = workflow.NewServer(ts.TopoServer(), ts.TabletManagerClient(), ts.wr.parser) + ws = workflow.NewServer(ts.TopoServer(), ts.TabletManagerClient(), ts.wr.collationEnv, ts.wr.parser) mu sync.Mutex ) diff --git a/go/vt/wrangler/vdiff.go b/go/vt/wrangler/vdiff.go index 35b1e72a459..b19d2bb44f3 100644 --- a/go/vt/wrangler/vdiff.go +++ b/go/vt/wrangler/vdiff.go @@ -547,7 +547,8 @@ func getColumnCollations(table *tabletmanagerdatapb.TableDefinition, collationEn if !ok { return nil, vterrors.Wrapf(err, "invalid table schema %s for table %s", table.Schema, table.Name) } - tableschema, err := schemadiff.NewCreateTableEntity(createtable) + env := schemadiff.NewEnv(collationEnv, collationEnv.DefaultConnectionCharset(), parser) + tableschema, err := schemadiff.NewCreateTableEntity(env, createtable) if err != nil { return nil, vterrors.Wrapf(err, "invalid table schema %s for table %s", table.Schema, table.Name) } diff --git a/go/vt/wrangler/vdiff_test.go b/go/vt/wrangler/vdiff_test.go index cae4f6afca1..265fcd224ee 100644 --- a/go/vt/wrangler/vdiff_test.go +++ b/go/vt/wrangler/vdiff_test.go @@ -507,7 +507,7 @@ func TestVDiffPlanFailure(t *testing.T) { }} for _, tcase := range testcases { filter := &binlogdatapb.Filter{Rules: []*binlogdatapb.Rule{tcase.input}} - df := &vdiff{collationEnv: collations.MySQL8()} + df := &vdiff{collationEnv: collations.MySQL8(), parser: sqlparser.NewTestParser()} err := df.buildVDiffPlan(context.Background(), filter, schm, nil) assert.EqualError(t, err, tcase.err, tcase.input) } @@ -1137,7 +1137,7 @@ func TestVDiffPlanInclude(t *testing.T) { }}, } - df := &vdiff{} + df := &vdiff{collationEnv: collations.MySQL8(), parser: sqlparser.NewTestParser()} rule := &binlogdatapb.Rule{ Match: "/.*", } diff --git a/go/vt/wrangler/wrangler.go b/go/vt/wrangler/wrangler.go index 84141a68da7..fd0ee65d5ac 100644 --- a/go/vt/wrangler/wrangler.go +++ b/go/vt/wrangler/wrangler.go @@ -70,7 +70,7 @@ func New(logger logutil.Logger, ts *topo.Server, tmc tmclient.TabletManagerClien logger: logger, ts: ts, tmc: tmc, - vtctld: grpcvtctldserver.NewVtctldServer(ts, parser), + vtctld: grpcvtctldserver.NewVtctldServer(ts, collationEnv, parser), sourceTs: ts, collationEnv: collationEnv, parser: parser,