Merge pull request #5806 from dolthub/fulghum/schema-merge

Report schema conflicts for merges with column type changes
This commit is contained in:
Jason Fulghum
2023-04-26 15:36:56 -07:00
committed by GitHub
2 changed files with 263 additions and 138 deletions
+53 -7
View File
@@ -92,7 +92,7 @@ type ColConflict struct {
func (c ColConflict) String() string {
switch c.Kind {
case NameCollision:
return fmt.Sprintf("two columns with the same name '%s' have different tags. See https://github.com/dolthub/dolt/issues/3963", c.Ours.Name)
return fmt.Sprintf("incompatible column types for column '%s': %s and %s", c.Ours.Name, c.Ours.TypeInfo, c.Theirs.TypeInfo)
case TagCollision:
return fmt.Sprintf("different column definitions for our column %s and their column %s", c.Ours.Name, c.Theirs.Name)
}
@@ -152,7 +152,7 @@ func SchemaMerge(ctx context.Context, format *types.NomsBinFormat, ourSch, their
}
var mergedCC *schema.ColCollection
mergedCC, sc.ColConflicts, err = mergeColumns(ourSch.GetAllCols(), theirSch.GetAllCols(), ancSch.GetAllCols())
mergedCC, sc.ColConflicts, err = mergeColumns(format, ourSch.GetAllCols(), theirSch.GetAllCols(), ancSch.GetAllCols())
if err != nil {
return nil, SchemaConflict{}, err
}
@@ -303,9 +303,11 @@ func ForeignKeysMerge(ctx context.Context, mergedRoot, ourRoot, theirRoot, ancRo
// mergeColumns merges the columns from |ourCC|, |theirCC| into a single column collection, using the ancestor column
// definitions in |ancCC| to determine on which side a column has changed. If merging is not possible because of
// conflicting changes to the columns in |ourCC| and |theirCC|, then a set of ColConflict instances are returned
// describing the conflicts. If any other, unexpected error occurs, then that error is returned and the other response
// fields should be ignored.
func mergeColumns(ourCC, theirCC, ancCC *schema.ColCollection) (*schema.ColCollection, []ColConflict, error) {
// describing the conflicts. |format| indicates what storage format is in use, and is needed to determine compatibility
// between types, since different storage formats have different restrictions on how much types can change and remain
// compatible with the current stored format. If any unexpected error occurs, then that error is returned and the
// other response fields should be ignored.
func mergeColumns(format *types.NomsBinFormat, ourCC, theirCC, ancCC *schema.ColCollection) (*schema.ColCollection, []ColConflict, error) {
columnMappings, err := mapColumns(ourCC, theirCC, ancCC)
if err != nil {
return nil, nil, err
@@ -344,9 +346,25 @@ func mergeColumns(ourCC, theirCC, ancCC *schema.ColCollection) (*schema.ColColle
if oursChanged && theirsChanged {
// This is a schema change conflict and has already been handled by checkSchemaConflicts
} else if theirsChanged {
mergedColumns = append(mergedColumns, *theirs)
if columnTypesAreCompatible(format, *ours, *theirs) {
mergedColumns = append(mergedColumns, *theirs)
} else {
conflicts = append(conflicts, ColConflict{
Kind: NameCollision,
Ours: *ours,
Theirs: *theirs,
})
}
} else {
mergedColumns = append(mergedColumns, *ours)
if columnTypesAreCompatible(format, *theirs, *ours) {
mergedColumns = append(mergedColumns, *ours)
} else {
conflicts = append(conflicts, ColConflict{
Kind: NameCollision,
Ours: *ours,
Theirs: *theirs,
})
}
}
} else if ours.Equals(*theirs) {
// if the columns are identical, just use ours
@@ -477,6 +495,34 @@ func checkSchemaConflicts(columnMappings columnMappings) ([]ColConflict, error)
return conflicts, nil
}
// columnTypesAreCompatible returns true if the change from |from| to |to| is a compatible type change.
// Currently, no type change for the DOLT storage format is considered compatible, but over time we will
// widen this to include safe type migrations (e.g. smallint to bigint, varchar(100) to varchar(200)), which
// can require rewriting existing stored data to be compatible with the new type. For the older LD_1 storage
// format, we are looser with type equality and consider them compatible as long as the types are in the
// same type family/kind.
func columnTypesAreCompatible(format *types.NomsBinFormat, from, to schema.Column) bool {
if !from.TypeInfo.Equals(to.TypeInfo) {
if types.IsFormat_DOLT(format) {
// All type changes are incompatible, for the DOLT storage format.
// TODO: this is overly broad, and should be narrowed down
return false
}
if from.Kind != to.Kind {
return false
}
if schema.IsColSpatialType(to) {
// We need to do this because some spatial type changes require a full table check, but not all.
// TODO: This could be narrowed down to a smaller set of spatial type changes
return false
}
}
return true
}
// columnMapping describes the mapping for a column being merged between the two sides of the merge as well as the ancestor.
type columnMapping struct {
anc *schema.Column
@@ -3721,6 +3721,8 @@ var DoltVerifyConstraintsTestScripts = []queries.ScriptTest{
var errTmplNoAutomaticMerge = "table %s can't be automatically merged.\nTo merge this table, make the schema on the source and target branch equal."
var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
// Data conflicts during a merge with schema changes
{
Name: "data conflict",
AncSetUpScript: []string{
@@ -3760,51 +3762,8 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
},
{
Name: "unique constraint violation",
AncSetUpScript: []string{
"set autocommit = 0;",
"CREATE table t (pk varchar(100) primary key, col1 int, col2 varchar(100), UNIQUE KEY unique1 (col2));",
"INSERT into t values ('0', 0, '');",
"alter table t add index idx1 (pk, col2);",
},
RightSetUpScript: []string{
"alter table t drop column col1;",
"INSERT into t (pk, col2) values ('10', 'same');",
},
LeftSetUpScript: []string{
"INSERT into t values ('1', 10, 'same');",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "call dolt_merge('right');",
Expected: []sql.Row{{0, 0x1}},
},
{
Query: "select * from dolt_conflicts;",
Expected: []sql.Row{},
},
{
Query: "select * from dolt_constraint_violations;",
Expected: []sql.Row{{"t", uint(2)}},
},
{
Query: "select violation_type, pk, col2, violation_info from dolt_constraint_violations_t;",
Expected: []sql.Row{
{uint(2), "1", "same", types.JSONDocument{Val: merge.UniqCVMeta{Columns: []string{"col2"}, Name: "unique1"}}},
{uint(2), "10", "same", types.JSONDocument{Val: merge.UniqCVMeta{Columns: []string{"col2"}, Name: "unique1"}}},
},
},
{
Query: "select pk, col2 from t;",
Expected: []sql.Row{
{"0", ""},
{"1", "same"},
{"10", "same"},
},
},
},
},
// Basic column changes adds/drops/renames/reorders
{
Name: "dropping columns",
AncSetUpScript: []string{
@@ -3877,8 +3836,6 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
"alter table t add index idx4 (col1, col2);",
"alter table t add index idx5 (col2, col1);",
"alter table t add index idx6 (col2, pk, col1);",
// TODO: This duplicate index causes a race condition in the merge code
//"alter table t add index idx7 (col2);",
},
RightSetUpScript: []string{
"alter table t rename column col1 to col11;",
@@ -4034,6 +3991,8 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
},
// Constraint changes
{
Name: "removing a not-null constraint",
AncSetUpScript: []string{
@@ -4160,6 +4119,51 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
},
{
Name: "unique constraint violation",
AncSetUpScript: []string{
"set autocommit = 0;",
"CREATE table t (pk varchar(100) primary key, col1 int, col2 varchar(100), UNIQUE KEY unique1 (col2));",
"INSERT into t values ('0', 0, '');",
"alter table t add index idx1 (pk, col2);",
},
RightSetUpScript: []string{
"alter table t drop column col1;",
"INSERT into t (pk, col2) values ('10', 'same');",
},
LeftSetUpScript: []string{
"INSERT into t values ('1', 10, 'same');",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "call dolt_merge('right');",
Expected: []sql.Row{{0, 0x1}},
},
{
Query: "select * from dolt_conflicts;",
Expected: []sql.Row{},
},
{
Query: "select * from dolt_constraint_violations;",
Expected: []sql.Row{{"t", uint(2)}},
},
{
Query: "select violation_type, pk, col2, violation_info from dolt_constraint_violations_t;",
Expected: []sql.Row{
{uint(2), "1", "same", types.JSONDocument{Val: merge.UniqCVMeta{Columns: []string{"col2"}, Name: "unique1"}}},
{uint(2), "10", "same", types.JSONDocument{Val: merge.UniqCVMeta{Columns: []string{"col2"}, Name: "unique1"}}},
},
},
{
Query: "select pk, col2 from t;",
Expected: []sql.Row{
{"0", ""},
{"1", "same"},
{"10", "same"},
},
},
},
},
{
Name: "dropping a unique key",
AncSetUpScript: []string{
@@ -4187,7 +4191,85 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
// Schema conflict test cases
// Schema conflicts
{
// Type widening - these changes move from smaller types to bigger types, so they are guaranteed to be safe.
// TODO: We don't support automatically converting column types in merges yet, so currently these won't
// automatically merge and instead return schema conflicts.
Name: "type widening",
AncSetUpScript: []string{
"CREATE table t (pk int primary key, col1 enum('blue', 'green'), col2 float, col3 smallint, " +
"col4 decimal(4,2), col5 varchar(10), col6 set('a', 'b'), col7 bit(1));",
"INSERT into t values (1, 'blue', 1.0, 1, 0.1, 'one', 'a,b', 1);",
"alter table t add index idx1 (col1);",
},
RightSetUpScript: []string{
"alter table t modify column col1 enum('blue', 'green', 'red');",
"alter table t modify column col2 double;",
"alter table t modify column col3 bigint;",
"alter table t modify column col4 decimal(8,4);",
"alter table t modify column col5 varchar(20);",
"alter table t modify column col6 set('a', 'b', 'c');",
"alter table t modify column col7 bit(2);",
"INSERT into t values (3, 'red', 3.0, 420, 0.001, 'three', 'a,b,c', 3);",
},
LeftSetUpScript: []string{
"INSERT into t values (2, 'green', 2.0, 2, 0.2, 'two', 'a,b', 1);",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "call dolt_merge('right');",
Expected: []sql.Row{{0, 0x1}},
},
{
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
Expected: []sql.Row{{"t",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green'),\n `col2` float,\n `col3` smallint,\n `col4` decimal(4,2),\n `col5` varchar(10),\n `col6` set('a','b'),\n `col7` bit(1),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green','red'),\n `col2` double,\n `col3` bigint,\n `col4` decimal(8,4),\n `col5` varchar(20),\n `col6` set('a','b','c'),\n `col7` bit(2),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green'),\n `col2` float,\n `col3` smallint,\n `col4` decimal(4,2),\n `col5` varchar(10),\n `col6` set('a','b'),\n `col7` bit(1),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
},
},
},
{
// Type shortening these changes move from a larger type to a smaller type and are not always safe.
// For now, we automatically fail all of these with a schema conflict that the user must resolve, but in
// theory, we could try to apply these changes and see if the data in the tables is compatible or not, but
// that's an optimization left for the future. Until then, customers can manually alter their schema to
// get merges to work, based on the schema conflict information.
Name: "type shortening",
AncSetUpScript: []string{
"CREATE TABLE t (pk int primary key, col1 enum('blue','green','red'), col2 double, col3 bigint, col4 decimal(8,4), " +
"col5 varchar(20), col6 set('a','b','c'), col7 bit(2));",
"INSERT into t values (3, 'green', 3.0, 420, 0.001, 'three', 'a,b', 1);",
"alter table t add index idx1 (col1);",
},
RightSetUpScript: []string{
"alter table t modify column col1 enum('blue', 'green');",
"alter table t modify column col2 float;",
"alter table t modify column col3 smallint;",
"alter table t modify column col4 decimal(4,2);",
"alter table t modify column col5 varchar(10);",
"alter table t modify column col6 set('a', 'b');",
"alter table t modify column col7 bit(1);",
"INSERT into t values (1, 'blue', 1.0, 1, 0.1, 'one', 'a,b', 1);",
},
LeftSetUpScript: []string{
"INSERT into t values (2, 'green', 2.0, 2, 0.2, 'two', 'a,b', 1);",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "call dolt_merge('right');",
Expected: []sql.Row{{0, 0x1}},
},
{
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
Expected: []sql.Row{{"t",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green','red'),\n `col2` double,\n `col3` bigint,\n `col4` decimal(8,4),\n `col5` varchar(20),\n `col6` set('a','b','c'),\n `col7` bit(2),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green'),\n `col2` float,\n `col3` smallint,\n `col4` decimal(4,2),\n `col5` varchar(10),\n `col6` set('a','b'),\n `col7` bit(1),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` enum('blue','green','red'),\n `col2` double,\n `col3` bigint,\n `col4` decimal(8,4),\n `col5` varchar(20),\n `col6` set('a','b','c'),\n `col7` bit(2),\n PRIMARY KEY (`pk`),\n KEY `idx1` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
},
},
},
{
Name: "index conflicts: both sides add an index with the same name, same columns, but different type",
AncSetUpScript: []string{
@@ -4216,7 +4298,6 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
},
{
// https://github.com/dolthub/dolt/issues/2973
Name: "modifying a column on one side of a merge, and deleting it on the other",
@@ -4262,15 +4343,69 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
},
// Smarter merge conflict detection
{
// This merge tests reports a conflict on pk=1, because the tuple value is different on the left side, right
Name: "changing the type of a column",
AncSetUpScript: []string{
"create table t (pk int primary key, col1 int);",
"insert into t values (1, 10), (2, 20);",
},
RightSetUpScript: []string{
"alter table t modify column col1 varchar(100)",
"insert into t values (3, 'thirty'), (4, 'forty')",
},
LeftSetUpScript: []string{
"insert into t values (5, 50), (6, 60);",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "call dolt_merge('right');",
Expected: []sql.Row{{0, 1}},
},
{
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
Expected: []sql.Row{{"t",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` int,\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(100),\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` int,\n PRIMARY KEY (`pk`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
},
},
},
{
Name: "changing the type of a column with an index",
AncSetUpScript: []string{
"create table t (pk int primary key, col1 int, INDEX col1_idx (col1));",
"insert into t values (1, 100), (2, 20);",
},
RightSetUpScript: []string{
"alter table t modify column col1 varchar(100);",
"insert into t values (3, 'thirty'), (4, 'forty')",
},
LeftSetUpScript: []string{
"insert into t values (5, 50), (6, 60);",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "call dolt_merge('right');",
Expected: []sql.Row{{0, 1}},
},
{
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
Expected: []sql.Row{{"t",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` int,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(100),\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` int,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
},
},
},
// Unsupported automatic merge cases
{
// This merge test reports a conflict on pk=1, because the tuple value is different on the left side, right
// side, and base. The value is the base is (10, '100'), on the right is nil, and on the left is ('100'),
// because the data migration for the schema change happens before the diff iterator is invoked.
// This should NOT be a conflict for a user Dolt should not conflate the schema merge data migration with
// a real data conflict created by a user. Allowing this is still better than completely blocking all schema
// merges though, so we can live with this while we continue iterating and fine tuning schema merge logic.
// merges though, so we can live with this while we continue iterating and fine-tuning schema merge logic.
Name: "schema change combined with drop row",
AncSetUpScript: []string{
"SET autocommit = 0",
@@ -4304,10 +4439,8 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
},
// Unsupported automatic merge cases
{
Name: "adding a non-null column to one side",
Name: "adding a non-null column with a default value to one side",
AncSetUpScript: []string{
"create table t (pk int primary key, col1 int);",
"insert into t values (1, 1);",
@@ -4334,71 +4467,7 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
{
Name: "changing the type of a column",
AncSetUpScript: []string{
"create table t (pk int primary key, col1 int);",
"insert into t values (1, 10), (2, 20);",
},
RightSetUpScript: []string{
"alter table t modify column col1 varchar(100)",
"insert into t values (3, 'thirty'), (4, 'forty')",
},
LeftSetUpScript: []string{
"insert into t values (5, 50), (6, 60);",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "call dolt_merge('right');",
ExpectedErrStr: fmt.Sprintf(errTmplNoAutomaticMerge, "t"),
},
{
Query: "select pk, col1 from t;",
Expected: []sql.Row{
{1, "10"},
{2, "20"},
{3, "thirty"},
{4, "forty"},
{5, "50"},
{6, "60"},
},
Skip: true,
},
},
},
{
Name: "changing the type of a column with an index",
AncSetUpScript: []string{
"create table t (pk int primary key, col1 int, INDEX col1_idx (col1));",
"insert into t values (1, 100), (2, 20);",
},
RightSetUpScript: []string{
"alter table t modify column col1 varchar(100);",
"insert into t values (3, 'thirty'), (4, 'forty')",
},
LeftSetUpScript: []string{
"insert into t values (5, 50), (6, 60);",
},
Assertions: []queries.ScriptTestAssertion{
{
Query: "call dolt_merge('right');",
ExpectedErrStr: fmt.Sprintf(errTmplNoAutomaticMerge, "t"),
},
{
Skip: true,
Query: "select pk, col1 from t order by col1;",
Expected: []sql.Row{
{1, "100"},
{2, "20"},
{3, "thirty"},
{4, "forty"},
{5, "50"},
{6, "60"},
},
},
},
},
{
Name: "adding a not-null constraint with default to a column",
Name: "adding a not-null constraint and default value to a column",
AncSetUpScript: []string{
"create table t (pk int primary key, col1 int);",
"insert into t values (1, null), (2, null);",
@@ -4451,9 +4520,9 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
},
{
// It would make sense if we table-scanned for check constraints during merge
// and flagged failing constraints as violations in `dolt_constraint_violations`.
Name: "adding a check-constraint should abort the merge.",
// TODO: We should scan for check constraints during merge and flag failing
// constraints as violations in `dolt_constraint_violations`.
Name: "adding a check-constraint",
AncSetUpScript: []string{
"create table t (pk int primary key, col1 int);",
"insert into t values (1, 1);",
@@ -4467,14 +4536,18 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
},
Assertions: []queries.ScriptTestAssertion{
{
Skip: true,
Query: "call dolt_merge('right');",
ExpectedErrStr: "some schema error",
// TODO: Dolt currently merges this without an error, but it shouldn't;
// There is a constraint violation that should be reported.
Skip: true,
Query: "call dolt_merge('right');",
Expected: []sql.Row{{0, 0x1}},
},
},
},
{
Name: "changing the collation of an indexed column is broken",
// TODO: Changing a column's collation requires rewriting the table and any indexes containing that column.
// For now, we just detect the schema incompatibility and return schema conflict metadata.
Name: "changing the collation of an indexed column",
AncSetUpScript: []string{
"create table t (pk int primary key, col1 varchar(32) character set utf8mb4 collate utf8mb4_bin, index col1_idx (col1));",
"insert into t values (1, 'ab'), (2, 'Ab');",
@@ -4486,10 +4559,16 @@ var ThreeWayMergeWithSchemaChangeTestScripts = []MergeScriptTest{
"insert into t values (3, 'c');",
},
Assertions: []queries.ScriptTestAssertion{
// TODO: Fails secondary index validation. Changing the ordinal ordering of secondary indexes definitely breaks merge
{
Query: "call dolt_merge('right');",
ExpectedErrStr: fmt.Sprintf(errTmplNoAutomaticMerge, "t"),
Query: "call dolt_merge('right');",
Expected: []sql.Row{{0, 1}},
},
{
Query: "select table_name, our_schema, their_schema, base_schema from dolt_schema_conflicts;",
Expected: []sql.Row{{"t",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(32) COLLATE utf8mb4_bin,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(32) COLLATE utf8mb4_general_ci,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;",
"CREATE TABLE `t` (\n `pk` int NOT NULL,\n `col1` varchar(32) COLLATE utf8mb4_bin,\n PRIMARY KEY (`pk`),\n KEY `col1_idx` (`col1`)\n) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_bin;"}},
},
},
},