Платформа ЦРНП "Мирокод" для разработки проектов
https://git.mirocod.ru
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
218 lines
7.4 KiB
218 lines
7.4 KiB
// 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 tidb |
|
|
|
import ( |
|
"fmt" |
|
"runtime/debug" |
|
"strings" |
|
|
|
"github.com/juju/errors" |
|
"github.com/ngaut/log" |
|
"github.com/pingcap/tidb/infoschema" |
|
"github.com/pingcap/tidb/mysql" |
|
"github.com/pingcap/tidb/sessionctx/variable" |
|
) |
|
|
|
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(16), |
|
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', |
|
Grant_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', |
|
Execute_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', |
|
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(16), |
|
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', |
|
Index_priv ENUM('N','Y') Not Null DEFAULT 'N', |
|
Alter_priv ENUM('N','Y') Not Null DEFAULT 'N', |
|
Execute_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(16), |
|
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(16), |
|
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));` |
|
) |
|
|
|
// Bootstrap initiates system DB for a store. |
|
func bootstrap(s Session) { |
|
b, err := checkBootstrapped(s) |
|
if err != nil { |
|
log.Fatal(err) |
|
} |
|
if b { |
|
return |
|
} |
|
doDDLWorks(s) |
|
doDMLWorks(s) |
|
} |
|
|
|
const ( |
|
bootstrappedVar = "bootstrapped" |
|
bootstrappedVarTrue = "True" |
|
) |
|
|
|
func checkBootstrapped(s Session) (bool, error) { |
|
// Check if system db exists. |
|
_, err := s.Execute(fmt.Sprintf("USE %s;", mysql.SystemDB)) |
|
if err != nil && infoschema.DatabaseNotExists.NotEqual(err) { |
|
log.Fatal(err) |
|
} |
|
// Check bootstrapped variable value in TiDB table. |
|
v, err := checkBootstrappedVar(s) |
|
if err != nil { |
|
return false, errors.Trace(err) |
|
} |
|
return v, nil |
|
} |
|
|
|
func checkBootstrappedVar(s Session) (bool, error) { |
|
sql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s"`, |
|
mysql.SystemDB, mysql.TiDBTable, bootstrappedVar) |
|
rs, err := s.Execute(sql) |
|
if err != nil { |
|
if infoschema.TableNotExists.Equal(err) { |
|
return false, nil |
|
} |
|
return false, errors.Trace(err) |
|
} |
|
|
|
if len(rs) != 1 { |
|
return false, errors.New("Wrong number of Recordset") |
|
} |
|
r := rs[0] |
|
row, err := r.Next() |
|
if err != nil || row == nil { |
|
return false, errors.Trace(err) |
|
} |
|
|
|
isBootstrapped := row.Data[0].GetString() == bootstrappedVarTrue |
|
if isBootstrapped { |
|
// Make sure that doesn't affect the following operations. |
|
|
|
if err = s.FinishTxn(false); err != nil { |
|
return false, errors.Trace(err) |
|
} |
|
} |
|
|
|
return isBootstrapped, nil |
|
} |
|
|
|
// Execute 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 systemt variable table. |
|
mustExecute(s, CreateGloablVariablesTable) |
|
// Create TiDB table. |
|
mustExecute(s, CreateTiDBTable) |
|
} |
|
|
|
// Execute 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 INTO mysql.user VALUES |
|
("%", "root", "", "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 { |
|
value := fmt.Sprintf(`("%s", "%s")`, strings.ToLower(k), v.Value) |
|
values = append(values, value) |
|
} |
|
sql := fmt.Sprintf("INSERT INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable, |
|
strings.Join(values, ", ")) |
|
mustExecute(s, sql) |
|
|
|
sql = fmt.Sprintf(`INSERT 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) |
|
mustExecute(s, "COMMIT") |
|
} |
|
|
|
func mustExecute(s Session, sql string) { |
|
_, err := s.Execute(sql) |
|
if err != nil { |
|
debug.PrintStack() |
|
log.Fatal(err) |
|
} |
|
}
|
|
|