Skip to content

Commit

Permalink
Make Schema Tracking case-sensitive (#14904)
Browse files Browse the repository at this point in the history
Signed-off-by: Manan Gupta <[email protected]>
  • Loading branch information
GuptaManan100 authored Jan 12, 2024
1 parent eddb39e commit 4a67c0c
Show file tree
Hide file tree
Showing 13 changed files with 118 additions and 313 deletions.
139 changes: 0 additions & 139 deletions go/mysql/endtoend/schema_change_test.go

This file was deleted.

27 changes: 0 additions & 27 deletions go/mysql/schema.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,33 +35,6 @@ const (
// ShowRowsRead is the query used to find the number of rows read.
ShowRowsRead = "show status like 'Innodb_rows_read'"

// DetectSchemaChange query detects if there is any schema change from previous copy.
DetectSchemaChange = `
SELECT DISTINCT table_name
FROM (
SELECT table_name, column_name, ordinal_position, character_set_name, collation_name, data_type, column_key
FROM information_schema.columns
WHERE table_schema = database()
UNION ALL
SELECT table_name, column_name, ordinal_position, character_set_name, collation_name, data_type, column_key
FROM %s.schemacopy
WHERE table_schema = database()
) _inner
GROUP BY table_name, column_name, ordinal_position, character_set_name, collation_name, data_type, column_key
HAVING COUNT(*) = 1
`

// ClearSchemaCopy query clears the schemacopy table.
ClearSchemaCopy = `delete from %s.schemacopy where table_schema = database()`

// InsertIntoSchemaCopy query copies over the schema information from information_schema.columns table.
InsertIntoSchemaCopy = `insert %s.schemacopy
select table_schema, table_name, column_name, ordinal_position, character_set_name, collation_name, data_type, column_key
from information_schema.columns
where table_schema = database()`

// GetColumnNamesQueryPatternForTable is used for mocking queries in unit tests
GetColumnNamesQueryPatternForTable = `SELECT COLUMN_NAME.*TABLE_NAME.*%s.*`
)
Expand Down
2 changes: 1 addition & 1 deletion go/test/endtoend/vreplication/sidecardb_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var ddls1, ddls2 []string

func init() {
sidecarDBTables = []string{"copy_state", "dt_participant", "dt_state", "heartbeat", "post_copy_action", "redo_state",
"redo_statement", "reparent_journal", "resharding_journal", "schema_migrations", "schema_version", "schemacopy", "tables",
"redo_statement", "reparent_journal", "resharding_journal", "schema_migrations", "schema_version", "tables",
"vdiff", "vdiff_log", "vdiff_table", "views", "vreplication", "vreplication_log"}
numSidecarDBTables = len(sidecarDBTables)
ddls1 = []string{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,12 +120,7 @@ func TestNewUnshardedTable(t *testing.T) {
require.NoError(t, err)
defer conn.Close()

vtgateVersion, err := cluster.GetMajorVersion("vtgate")
require.NoError(t, err)
expected := `[[VARCHAR("dual")] [VARCHAR("main")]]`
if vtgateVersion >= 17 {
expected = `[[VARCHAR("main")]]`
}
expected := `[[VARCHAR("main")]]`

// ensuring our initial table "main" is in the schema
utils.AssertMatchesWithTimeout(t, conn,
Expand All @@ -138,10 +133,7 @@ func TestNewUnshardedTable(t *testing.T) {
// create a new table which is not part of the VSchema
utils.Exec(t, conn, `create table new_table_tracked(id bigint, name varchar(100), primary key(id)) Engine=InnoDB`)

expected = `[[VARCHAR("dual")] [VARCHAR("main")] [VARCHAR("new_table_tracked")]]`
if vtgateVersion >= 17 {
expected = `[[VARCHAR("main")] [VARCHAR("new_table_tracked")]]`
}
expected = `[[VARCHAR("main")] [VARCHAR("new_table_tracked")]]`

// waiting for the vttablet's schema_reload interval to kick in
utils.AssertMatchesWithTimeout(t, conn,
Expand Down Expand Up @@ -176,14 +168,63 @@ func TestNewUnshardedTable(t *testing.T) {
utils.Exec(t, conn, `drop table new_table_tracked`)

// waiting for the vttablet's schema_reload interval to kick in
expected = `[[VARCHAR("dual")] [VARCHAR("main")]]`
if vtgateVersion >= 17 {
expected = `[[VARCHAR("main")]]`
}
expected = `[[VARCHAR("main")]]`
utils.AssertMatchesWithTimeout(t, conn,
"SHOW VSCHEMA TABLES",
expected,
100*time.Millisecond,
30*time.Second,
"new_table_tracked not in vschema tables")
}

// TestCaseSensitiveSchemaTracking tests that schema tracking is case-sensitive.
// This test only works on Linux (and not on Windows and Mac) since it has a case-sensitive file system, so it allows
// creating two tables having the same name differing only in casing, but other operating systems don't.
// More information at https://dev.mysql.com/doc/refman/8.0/en/identifier-case-sensitivity.html#:~:text=Table%20names%20are%20stored%20in,lowercase%20on%20storage%20and%20lookup.
func TestCaseSensitiveSchemaTracking(t *testing.T) {
utils.SkipIfBinaryIsBelowVersion(t, 19, "vttablet")
defer cluster.PanicHandler(t)

// create a sql connection
ctx := context.Background()
conn, err := mysql.Connect(ctx, &vtParams)
require.NoError(t, err)
defer conn.Close()

// ensuring our initial table "main" is in the schema
utils.AssertMatchesWithTimeout(t, conn,
"SHOW VSCHEMA TABLES",
`[[VARCHAR("main")]]`,
100*time.Millisecond,
30*time.Second,
"initial tables not found in vschema")

// Now we create two tables with the same name differing only in casing t1 and T1.
// For both of them we'll have different schema's and verify that we can read the data after schema tracking kicks in.
utils.Exec(t, conn, `create table t1(id bigint, primary key(id)) Engine=InnoDB`)
utils.Exec(t, conn, `create table T1(col bigint, col2 bigint, primary key(col)) Engine=InnoDB`)

// Wait for schema tracking to be caught up
utils.AssertMatchesWithTimeout(t, conn,
"SHOW VSCHEMA TABLES",
`[[VARCHAR("T1")] [VARCHAR("main")] [VARCHAR("t1")]]`,
100*time.Millisecond,
30*time.Second,
"schema tracking didn't track both the tables")

// Run DMLs
utils.Exec(t, conn, `insert into t1(id) values(0),(1)`)
utils.Exec(t, conn, `insert into T1(col, col2) values(0,0),(1,1)`)

// Verify the tables are queryable
utils.AssertMatchesWithTimeout(t, conn,
`select * from t1`, `[[INT64(0)] [INT64(1)]]`,
100*time.Millisecond,
30*time.Second,
"could not query expected rows in t1 through vtgate")
utils.AssertMatchesWithTimeout(t, conn,
`select * from T1`, `[[INT64(0) INT64(0)] [INT64(1) INT64(1)]]`,
100*time.Millisecond,
30*time.Second,
"could not query expected rows in T1 through vtgate")
}
4 changes: 2 additions & 2 deletions go/vt/sidecardb/schema/schemaengine/tables.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ limitations under the License.

CREATE TABLE IF NOT EXISTS tables
(
TABLE_SCHEMA varchar(64) NOT NULL,
TABLE_NAME varchar(64) NOT NULL,
TABLE_SCHEMA varchar(64) CHARACTER SET `utf8mb3` COLLATE `utf8mb3_bin` NOT NULL,
TABLE_NAME varchar(64) CHARACTER SET `utf8mb3` COLLATE `utf8mb3_bin` NOT NULL,
CREATE_STATEMENT longtext,
CREATE_TIME BIGINT,
PRIMARY KEY (TABLE_SCHEMA, TABLE_NAME)
Expand Down
4 changes: 2 additions & 2 deletions go/vt/sidecardb/schema/schemaengine/views.sql
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@ limitations under the License.

CREATE TABLE IF NOT EXISTS views
(
TABLE_SCHEMA varchar(64) NOT NULL,
TABLE_NAME varchar(64) NOT NULL,
TABLE_SCHEMA varchar(64) CHARACTER SET `utf8mb3` COLLATE `utf8mb3_bin` NOT NULL,
TABLE_NAME varchar(64) CHARACTER SET `utf8mb3` COLLATE `utf8mb3_bin` NOT NULL,
CREATE_STATEMENT longtext,
VIEW_DEFINITION longtext NOT NULL,
PRIMARY KEY (TABLE_SCHEMA, TABLE_NAME)
Expand Down
28 changes: 0 additions & 28 deletions go/vt/sidecardb/schema/schematracker/schemacopy.sql

This file was deleted.

Loading

0 comments on commit 4a67c0c

Please sign in to comment.