// Copyright 2013 The ql Authors. All rights reserved. // Use of this source code is governed by a BSD-style // license that can be found in the LICENSES/QL-LICENSE file. // Copyright 2015 PingCAP, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // See the License for the specific language governing permissions and // limitations under the License. package session import ( "encoding/hex" "fmt" "runtime/debug" "strconv" "strings" "time" "github.com/hanchuanchuan/goInception/ddl" "github.com/hanchuanchuan/goInception/infoschema" "github.com/hanchuanchuan/goInception/mysql" "github.com/hanchuanchuan/goInception/sessionctx/variable" "github.com/hanchuanchuan/goInception/terror" "github.com/hanchuanchuan/goInception/util/auth" "github.com/hanchuanchuan/goInception/util/chunk" "github.com/hanchuanchuan/goInception/util/timeutil" "github.com/pingcap/errors" log "github.com/sirupsen/logrus" "golang.org/x/net/context" ) const ( // CreateUserTable is the SQL statement creates User table in system db. CreateUserTable = `CREATE TABLE if not exists mysql.user ( Host CHAR(64), User CHAR(32), Password CHAR(41), Select_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Insert_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Update_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Delete_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Create_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Drop_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Process_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Grant_priv ENUM('N','Y') NOT NULL DEFAULT 'N', References_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Alter_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Show_db_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Super_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Create_tmp_table_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Lock_tables_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Execute_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Create_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Show_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Create_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Alter_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Index_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Create_user_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Event_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Trigger_priv ENUM('N','Y') NOT NULL DEFAULT 'N', PRIMARY KEY (Host, User));` // CreateDBPrivTable is the SQL statement creates DB scope privilege table in system db. CreateDBPrivTable = `CREATE TABLE if not exists mysql.db ( Host CHAR(60), DB CHAR(64), User CHAR(32), Select_priv ENUM('N','Y') Not Null DEFAULT 'N', Insert_priv ENUM('N','Y') Not Null DEFAULT 'N', Update_priv ENUM('N','Y') Not Null DEFAULT 'N', Delete_priv ENUM('N','Y') Not Null DEFAULT 'N', Create_priv ENUM('N','Y') Not Null DEFAULT 'N', Drop_priv ENUM('N','Y') Not Null DEFAULT 'N', Grant_priv ENUM('N','Y') Not Null DEFAULT 'N', References_priv ENUM('N','Y') Not Null DEFAULT 'N', Index_priv ENUM('N','Y') Not Null DEFAULT 'N', Alter_priv ENUM('N','Y') Not Null DEFAULT 'N', Create_tmp_table_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Lock_tables_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Create_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Show_view_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Create_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Alter_routine_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Execute_priv ENUM('N','Y') Not Null DEFAULT 'N', Event_priv ENUM('N','Y') NOT NULL DEFAULT 'N', Trigger_priv ENUM('N','Y') NOT NULL DEFAULT 'N', PRIMARY KEY (Host, DB, User));` // CreateTablePrivTable is the SQL statement creates table scope privilege table in system db. CreateTablePrivTable = `CREATE TABLE if not exists mysql.tables_priv ( Host CHAR(60), DB CHAR(64), User CHAR(32), Table_name CHAR(64), Grantor CHAR(77), Timestamp Timestamp DEFAULT CURRENT_TIMESTAMP, Table_priv SET('Select','Insert','Update','Delete','Create','Drop','Grant', 'Index','Alter'), Column_priv SET('Select','Insert','Update'), PRIMARY KEY (Host, DB, User, Table_name));` // CreateColumnPrivTable is the SQL statement creates column scope privilege table in system db. CreateColumnPrivTable = `CREATE TABLE if not exists mysql.columns_priv( Host CHAR(60), DB CHAR(64), User CHAR(32), Table_name CHAR(64), Column_name CHAR(64), Timestamp Timestamp DEFAULT CURRENT_TIMESTAMP, Column_priv SET('Select','Insert','Update'), PRIMARY KEY (Host, DB, User, Table_name, Column_name));` // CreateGloablVariablesTable is the SQL statement creates global variable table in system db. // TODO: MySQL puts GLOBAL_VARIABLES table in INFORMATION_SCHEMA db. // INFORMATION_SCHEMA is a virtual db in TiDB. So we put this table in system db. // Maybe we will put it back to INFORMATION_SCHEMA. CreateGloablVariablesTable = `CREATE TABLE if not exists mysql.GLOBAL_VARIABLES( VARIABLE_NAME VARCHAR(64) Not Null PRIMARY KEY, VARIABLE_VALUE VARCHAR(1024) DEFAULT Null);` // CreateTiDBTable is the SQL statement creates a table in system db. // This table is a key-value struct contains some information used by TiDB. // Currently we only put bootstrapped in it which indicates if the system is already bootstrapped. CreateTiDBTable = `CREATE TABLE if not exists mysql.tidb( VARIABLE_NAME VARCHAR(64) Not Null PRIMARY KEY, VARIABLE_VALUE VARCHAR(1024) DEFAULT Null, COMMENT VARCHAR(1024));` // CreateHelpTopic is the SQL statement creates help_topic table in system db. // See: https://dev.mysql.com/doc/refman/5.5/en/system-database.html#system-database-help-tables CreateHelpTopic = `CREATE TABLE if not exists mysql.help_topic ( help_topic_id int(10) unsigned NOT NULL, name char(64) NOT NULL, help_category_id smallint(5) unsigned NOT NULL, description text NOT NULL, example text NOT NULL, url text NOT NULL, PRIMARY KEY (help_topic_id), UNIQUE KEY name (name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 STATS_PERSISTENT=0 COMMENT='help topics';` // CreateStatsMetaTable stores the meta of table statistics. CreateStatsMetaTable = `CREATE TABLE if not exists mysql.stats_meta ( version bigint(64) unsigned NOT NULL, table_id bigint(64) NOT NULL, modify_count bigint(64) NOT NULL DEFAULT 0, count bigint(64) unsigned NOT NULL DEFAULT 0, index idx_ver(version), unique index tbl(table_id) );` // CreateStatsColsTable stores the statistics of table columns. CreateStatsColsTable = `CREATE TABLE if not exists mysql.stats_histograms ( table_id bigint(64) NOT NULL, is_index tinyint(2) NOT NULL, hist_id bigint(64) NOT NULL, distinct_count bigint(64) NOT NULL, null_count bigint(64) NOT NULL DEFAULT 0, tot_col_size bigint(64) NOT NULL DEFAULT 0, modify_count bigint(64) NOT NULL DEFAULT 0, version bigint(64) unsigned NOT NULL DEFAULT 0, cm_sketch blob, stats_ver bigint(64) NOT NULL DEFAULT 0, flag bigint(64) NOT NULL DEFAULT 0, unique index tbl(table_id, is_index, hist_id) );` // CreateStatsBucketsTable stores the histogram info for every table columns. CreateStatsBucketsTable = `CREATE TABLE if not exists mysql.stats_buckets ( table_id bigint(64) NOT NULL, is_index tinyint(2) NOT NULL, hist_id bigint(64) NOT NULL, bucket_id bigint(64) NOT NULL, count bigint(64) NOT NULL, repeats bigint(64) NOT NULL, upper_bound blob NOT NULL, lower_bound blob , unique index tbl(table_id, is_index, hist_id, bucket_id) );` // CreateGCDeleteRangeTable stores schemas which can be deleted by DeleteRange. CreateGCDeleteRangeTable = `CREATE TABLE IF NOT EXISTS mysql.gc_delete_range ( job_id BIGINT NOT NULL COMMENT "the DDL job ID", element_id BIGINT NOT NULL COMMENT "the schema element ID", start_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", end_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", ts BIGINT NOT NULL COMMENT "timestamp in uint64", UNIQUE KEY delete_range_index (job_id, element_id) );` // CreateGCDeleteRangeDoneTable stores schemas which are already deleted by DeleteRange. CreateGCDeleteRangeDoneTable = `CREATE TABLE IF NOT EXISTS mysql.gc_delete_range_done ( job_id BIGINT NOT NULL COMMENT "the DDL job ID", element_id BIGINT NOT NULL COMMENT "the schema element ID", start_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", end_key VARCHAR(255) NOT NULL COMMENT "encoded in hex", ts BIGINT NOT NULL COMMENT "timestamp in uint64", UNIQUE KEY delete_range_done_index (job_id, element_id) );` // CreateStatsFeedbackTable stores the feedback info which is used to update stats. CreateStatsFeedbackTable = `CREATE TABLE IF NOT EXISTS mysql.stats_feedback ( table_id bigint(64) NOT NULL, is_index tinyint(2) NOT NULL, hist_id bigint(64) NOT NULL, feedback blob NOT NULL, index hist(table_id, is_index, hist_id) );` ) // bootstrap initiates system DB for a store. func bootstrap(s Session) { b, err := checkBootstrapped(s) if err != nil { log.Fatal(err) } if b { upgrade(s) return } doDDLWorks(s) doDMLWorks(s) } const ( // The variable name in mysql.TiDB table. // It is used for checking if the store is boostrapped by any TiDB server. bootstrappedVar = "bootstrapped" // The variable value in mysql.TiDB table for bootstrappedVar. // If the value true, the store is already boostrapped by a TiDB server. bootstrappedVarTrue = "True" // The variable name in mysql.TiDB table. // It is used for getting the version of the TiDB server which bootstrapped the store. tidbServerVersionVar = "tidb_server_version" // The variable name in mysql.tidb table and it will be used when we want to know // system timezone. tidbSystemTZ = "system_tz" // Const for TiDB server version 2. version2 = 2 version3 = 3 version4 = 4 version5 = 5 version6 = 6 version7 = 7 version8 = 8 version9 = 9 version10 = 10 version11 = 11 version12 = 12 version13 = 13 version14 = 14 version15 = 15 version16 = 16 version17 = 17 version18 = 18 version19 = 19 version20 = 20 version21 = 21 version22 = 22 version23 = 23 version24 = 24 ) func checkBootstrapped(s Session) (bool, error) { // Check if system db exists. _, err := s.Execute(context.Background(), fmt.Sprintf("USE %s;", mysql.SystemDB)) if err != nil && infoschema.ErrDatabaseNotExists.NotEqual(err) { log.Fatal(err) } // Check bootstrapped variable value in TiDB table. sVal, _, err := getTiDBVar(s, bootstrappedVar) if err != nil { if infoschema.ErrTableNotExists.Equal(err) { return false, nil } return false, errors.Trace(err) } isBootstrapped := sVal == bootstrappedVarTrue if isBootstrapped { // Make sure that doesn't affect the following operations. if err = s.CommitTxn(context.Background()); err != nil { return false, errors.Trace(err) } } return isBootstrapped, nil } // getTiDBVar gets variable value from mysql.tidb table. // Those variables are used by TiDB server. func getTiDBVar(s Session, name string) (sVal string, isNull bool, e error) { sql := fmt.Sprintf(`SELECT HIGH_PRIORITY VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s"`, mysql.SystemDB, mysql.TiDBTable, name) ctx := context.Background() rs, err := s.Execute(ctx, sql) if err != nil { return "", true, errors.Trace(err) } if len(rs) != 1 { return "", true, errors.New("Wrong number of Recordset") } r := rs[0] defer terror.Call(r.Close) chk := r.NewChunk() err = r.Next(ctx, chk) if err != nil || chk.NumRows() == 0 { return "", true, errors.Trace(err) } row := chk.GetRow(0) if row.IsNull(0) { return "", true, nil } return row.GetString(0), false, nil } // upgrade function will do some upgrade works, when the system is boostrapped by low version TiDB server // For example, add new system variables into mysql.global_variables table. func upgrade(s Session) { ver, err := getBootstrapVersion(s) terror.MustNil(err) if ver >= currentBootstrapVersion { // It is already bootstrapped/upgraded by a higher version TiDB server. return } // Do upgrade works then update bootstrap version. if ver < version2 { upgradeToVer2(s) ver = version2 } if ver < version3 { upgradeToVer3(s) } if ver < version4 { upgradeToVer4(s) } if ver < version5 { upgradeToVer5(s) } if ver < version6 { upgradeToVer6(s) } if ver < version7 { upgradeToVer7(s) } if ver < version8 { upgradeToVer8(s) } if ver < version9 { upgradeToVer9(s) } if ver < version10 { upgradeToVer10(s) } if ver < version11 { upgradeToVer11(s) } if ver < version12 { upgradeToVer12(s) } if ver < version13 { upgradeToVer13(s) } if ver < version14 { upgradeToVer14(s) } if ver < version15 { upgradeToVer15(s) } if ver < version16 { upgradeToVer16(s) } if ver < version17 { upgradeToVer17(s) } if ver < version18 { upgradeToVer18(s) } if ver < version19 { upgradeToVer19(s) } if ver < version20 { upgradeToVer20(s) } if ver < version21 { upgradeToVer21(s) } if ver < version22 { upgradeToVer22(s) } if ver < version23 { upgradeToVer23(s) } if ver < version24 { upgradeToVer24(s) } updateBootstrapVer(s) _, err = s.Execute(context.Background(), "COMMIT") if err != nil { time.Sleep(1 * time.Second) // Check if TiDB is already upgraded. v, err1 := getBootstrapVersion(s) if err1 != nil { log.Fatal(err1) } if v >= currentBootstrapVersion { // It is already bootstrapped/upgraded by a higher version TiDB server. return } log.Errorf("[Upgrade] upgrade from %d to %d error", ver, currentBootstrapVersion) log.Fatal(err) } } // upgradeToVer2 updates to version 2. func upgradeToVer2(s Session) { // Version 2 add two system variable for DistSQL concurrency controlling. // Insert distsql related system variable. distSQLVars := []string{variable.TiDBDistSQLScanConcurrency} values := make([]string, 0, len(distSQLVars)) for _, v := range distSQLVars { value := fmt.Sprintf(`("%s", "%s")`, v, variable.SysVars[v].Value) values = append(values, value) } sql := fmt.Sprintf("INSERT HIGH_PRIORITY IGNORE INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, strings.Join(values, ", ")) mustExecute(s, sql) } // upgradeToVer3 updates to version 3. func upgradeToVer3(s Session) { // Version 3 fix tx_read_only variable value. sql := fmt.Sprintf("UPDATE HIGH_PRIORITY %s.%s set variable_value = '0' where variable_name = 'tx_read_only';", mysql.SystemDB, mysql.GlobalVariablesTable) mustExecute(s, sql) } // upgradeToVer4 updates to version 4. func upgradeToVer4(s Session) { sql := CreateStatsMetaTable mustExecute(s, sql) } func upgradeToVer5(s Session) { mustExecute(s, CreateStatsColsTable) mustExecute(s, CreateStatsBucketsTable) } func upgradeToVer6(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Super_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_db_priv`", infoschema.ErrColumnExists) // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Super_priv='Y'") } func upgradeToVer7(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Process_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Drop_priv`", infoschema.ErrColumnExists) // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Process_priv='Y'") } func upgradeToVer8(s Session) { // This is a dummy upgrade, it checks whether upgradeToVer7 success, if not, do it again. if _, err := s.Execute(context.Background(), "SELECT HIGH_PRIORITY `Process_priv` from mysql.user limit 0"); err == nil { return } upgradeToVer7(s) } func upgradeToVer9(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.user ADD COLUMN `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_user_priv`", infoschema.ErrColumnExists) // For reasons of compatibility, set the non-exists privilege column value to 'Y', as TiDB doesn't check them in older versions. mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Trigger_priv='Y'") } func doReentrantDDL(s Session, sql string, ignorableErrs ...error) { _, err := s.Execute(context.Background(), sql) for _, ignorableErr := range ignorableErrs { if terror.ErrorEqual(err, ignorableErr) { return } } if err != nil { log.Fatal(err) } } func upgradeToVer10(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets CHANGE COLUMN `value` `upper_bound` BLOB NOT NULL", infoschema.ErrColumnNotExists, infoschema.ErrColumnExists) doReentrantDDL(s, "ALTER TABLE mysql.stats_buckets ADD COLUMN `lower_bound` BLOB", infoschema.ErrColumnExists) doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `null_count` bigint(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms DROP COLUMN distinct_ratio", ddl.ErrCantDropFieldOrKey) doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms DROP COLUMN use_count_to_estimate", ddl.ErrCantDropFieldOrKey) } func upgradeToVer11(s Session) { _, err := s.Execute(context.Background(), "ALTER TABLE mysql.user ADD COLUMN `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Grant_priv`") if err != nil { if terror.ErrorEqual(err, infoschema.ErrColumnExists) { return } log.Fatal(err) } mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET References_priv='Y'") } func upgradeToVer12(s Session) { ctx := context.Background() _, err := s.Execute(ctx, "BEGIN") terror.MustNil(err) sql := "SELECT HIGH_PRIORITY user, host, password FROM mysql.user WHERE password != ''" rs, err := s.Execute(ctx, sql) terror.MustNil(err) r := rs[0] sqls := make([]string, 0, 1) defer terror.Call(r.Close) chk := r.NewChunk() it := chunk.NewIterator4Chunk(chk) err = r.Next(ctx, chk) for err == nil && chk.NumRows() != 0 { for row := it.Begin(); row != it.End(); row = it.Next() { user := row.GetString(0) host := row.GetString(1) pass := row.GetString(2) var newPass string newPass, err = oldPasswordUpgrade(pass) terror.MustNil(err) updateSQL := fmt.Sprintf(`UPDATE HIGH_PRIORITY mysql.user set password = "%s" where user="%s" and host="%s"`, newPass, user, host) sqls = append(sqls, updateSQL) } err = r.Next(ctx, chk) } terror.MustNil(err) for _, sql := range sqls { mustExecute(s, sql) } sql = fmt.Sprintf(`INSERT HIGH_PRIORITY INTO %s.%s VALUES ("%s", "%d", "TiDB bootstrap version.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%d"`, mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, version12, version12) mustExecute(s, sql) mustExecute(s, "COMMIT") } func upgradeToVer13(s Session) { sqls := []string{ "ALTER TABLE mysql.user ADD COLUMN `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Super_priv`", "ALTER TABLE mysql.user ADD COLUMN `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_tmp_table_priv`", "ALTER TABLE mysql.user ADD COLUMN `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Execute_priv`", "ALTER TABLE mysql.user ADD COLUMN `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_view_priv`", "ALTER TABLE mysql.user ADD COLUMN `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_view_priv`", "ALTER TABLE mysql.user ADD COLUMN `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_routine_priv`", "ALTER TABLE mysql.user ADD COLUMN `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_user_priv`", } ctx := context.Background() for _, sql := range sqls { _, err := s.Execute(ctx, sql) if err != nil { if terror.ErrorEqual(err, infoschema.ErrColumnExists) { continue } log.Fatal(err) } } mustExecute(s, "UPDATE HIGH_PRIORITY mysql.user SET Create_tmp_table_priv='Y',Lock_tables_priv='Y',Create_view_priv='Y',Show_view_priv='Y',Create_routine_priv='Y',Alter_routine_priv='Y',Event_priv='Y'") } func upgradeToVer14(s Session) { sqls := []string{ "ALTER TABLE mysql.db ADD COLUMN `References_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Grant_priv`", "ALTER TABLE mysql.db ADD COLUMN `Create_tmp_table_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Alter_priv`", "ALTER TABLE mysql.db ADD COLUMN `Lock_tables_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_tmp_table_priv`", "ALTER TABLE mysql.db ADD COLUMN `Create_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Lock_tables_priv`", "ALTER TABLE mysql.db ADD COLUMN `Show_view_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_view_priv`", "ALTER TABLE mysql.db ADD COLUMN `Create_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Show_view_priv`", "ALTER TABLE mysql.db ADD COLUMN `Alter_routine_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Create_routine_priv`", "ALTER TABLE mysql.db ADD COLUMN `Event_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Execute_priv`", "ALTER TABLE mysql.db ADD COLUMN `Trigger_priv` enum('N','Y') CHARACTER SET utf8 NOT NULL DEFAULT 'N' AFTER `Event_priv`", } ctx := context.Background() for _, sql := range sqls { _, err := s.Execute(ctx, sql) if err != nil { if terror.ErrorEqual(err, infoschema.ErrColumnExists) { continue } log.Fatal(err) } } } func upgradeToVer15(s Session) { var err error _, err = s.Execute(context.Background(), CreateGCDeleteRangeTable) if err != nil { log.Fatal(err) } } func upgradeToVer16(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `cm_sketch` blob", infoschema.ErrColumnExists) } func upgradeToVer17(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.user MODIFY User CHAR(32)") } func upgradeToVer18(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `tot_col_size` bigint(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) } func upgradeToVer19(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.db MODIFY User CHAR(32)") doReentrantDDL(s, "ALTER TABLE mysql.tables_priv MODIFY User CHAR(32)") doReentrantDDL(s, "ALTER TABLE mysql.columns_priv MODIFY User CHAR(32)") } func upgradeToVer20(s Session) { doReentrantDDL(s, CreateStatsFeedbackTable) } func upgradeToVer21(s Session) { mustExecute(s, CreateGCDeleteRangeDoneTable) doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range DROP INDEX job_id", ddl.ErrCantDropFieldOrKey) doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range ADD UNIQUE INDEX delete_range_index (job_id, element_id)", ddl.ErrDupKeyName) doReentrantDDL(s, "ALTER TABLE mysql.gc_delete_range DROP INDEX element_id", ddl.ErrCantDropFieldOrKey) } func upgradeToVer22(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `stats_ver` bigint(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) } func upgradeToVer23(s Session) { doReentrantDDL(s, "ALTER TABLE mysql.stats_histograms ADD COLUMN `flag` bigint(64) NOT NULL DEFAULT 0", infoschema.ErrColumnExists) } // writeSystemTZ writes system timezone info into mysql.tidb func writeSystemTZ(s Session) { sql := fmt.Sprintf(`INSERT HIGH_PRIORITY INTO %s.%s VALUES ("%s", "%s", "TiDB Global System Timezone.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%s"`, mysql.SystemDB, mysql.TiDBTable, tidbSystemTZ, timeutil.InferSystemTZ(), timeutil.InferSystemTZ()) mustExecute(s, sql) } // upgradeToVer24 initializes `System` timezone according to docs/design/2018-09-10-adding-tz-env.md func upgradeToVer24(s Session) { writeSystemTZ(s) } // updateBootstrapVer updates bootstrap version variable in mysql.TiDB table. func updateBootstrapVer(s Session) { // Update bootstrap version. sql := fmt.Sprintf(`INSERT HIGH_PRIORITY INTO %s.%s VALUES ("%s", "%d", "TiDB bootstrap version.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%d"`, mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, currentBootstrapVersion, currentBootstrapVersion) mustExecute(s, sql) } // getBootstrapVersion gets bootstrap version from mysql.tidb table; func getBootstrapVersion(s Session) (int64, error) { sVal, isNull, err := getTiDBVar(s, tidbServerVersionVar) if err != nil { return 0, errors.Trace(err) } if isNull { return 0, nil } return strconv.ParseInt(sVal, 10, 64) } // doDDLWorks executes DDL statements in bootstrap stage. func doDDLWorks(s Session) { // Create a test database. mustExecute(s, "CREATE DATABASE IF NOT EXISTS test") // Create system db. mustExecute(s, fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s;", mysql.SystemDB)) // Create user table. mustExecute(s, CreateUserTable) // Create privilege tables. mustExecute(s, CreateDBPrivTable) mustExecute(s, CreateTablePrivTable) mustExecute(s, CreateColumnPrivTable) // Create global system variable table. mustExecute(s, CreateGloablVariablesTable) // Create TiDB table. mustExecute(s, CreateTiDBTable) // Create help table. mustExecute(s, CreateHelpTopic) // Create stats_meta table. mustExecute(s, CreateStatsMetaTable) // Create stats_columns table. mustExecute(s, CreateStatsColsTable) // Create stats_buckets table. mustExecute(s, CreateStatsBucketsTable) // Create gc_delete_range table. mustExecute(s, CreateGCDeleteRangeTable) // Create gc_delete_range_done table. mustExecute(s, CreateGCDeleteRangeDoneTable) // Create stats_feedback table. mustExecute(s, CreateStatsFeedbackTable) } // doDMLWorks executes DML statements in bootstrap stage. // All the statements run in a single transaction. func doDMLWorks(s Session) { mustExecute(s, "BEGIN") // Insert a default user with empty password. mustExecute(s, `INSERT HIGH_PRIORITY INTO mysql.user VALUES ("%", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y")`) // Init global system variables table. values := make([]string, 0, len(variable.SysVars)) for k, v := range variable.SysVars { // Session only variable should not be inserted. if v.Scope != variable.ScopeSession { value := fmt.Sprintf(`("%s", "%s")`, strings.ToLower(k), v.Value) values = append(values, value) } } sql := fmt.Sprintf("INSERT HIGH_PRIORITY INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, strings.Join(values, ", ")) mustExecute(s, sql) sql = fmt.Sprintf(`INSERT HIGH_PRIORITY INTO %s.%s VALUES("%s", "%s", "Bootstrap flag. Do not delete.") ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%s"`, mysql.SystemDB, mysql.TiDBTable, bootstrappedVar, bootstrappedVarTrue, bootstrappedVarTrue) mustExecute(s, sql) sql = fmt.Sprintf(`INSERT HIGH_PRIORITY INTO %s.%s VALUES("%s", "%d", "Bootstrap version. Do not delete.")`, mysql.SystemDB, mysql.TiDBTable, tidbServerVersionVar, currentBootstrapVersion) mustExecute(s, sql) writeSystemTZ(s) _, err := s.Execute(context.Background(), "COMMIT") if err != nil { time.Sleep(1 * time.Second) // Check if TiDB is already bootstrapped. b, err1 := checkBootstrapped(s) if err1 != nil { log.Fatal(err1) } if b { return } log.Fatal(err) } } func mustExecute(s Session, sql string) { _, err := s.Execute(context.Background(), sql) if err != nil { debug.PrintStack() log.Fatal(err) } } // oldPasswordUpgrade upgrade password to MySQL compatible format func oldPasswordUpgrade(pass string) (string, error) { hash1, err := hex.DecodeString(pass) if err != nil { return "", errors.Trace(err) } hash2 := auth.Sha1Hash(hash1) newpass := fmt.Sprintf("*%X", hash2) return newpass, nil }