github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/bootstrap.go (about)

     1  // Copyright 2013 The ql Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSES/QL-LICENSE file.
     4  
     5  // Copyright 2015 PingCAP, Inc.
     6  //
     7  // Licensed under the Apache License, Version 2.0 (the "License");
     8  // you may not use this file except in compliance with the License.
     9  // You may obtain a copy of the License at
    10  //
    11  //     http://www.apache.org/licenses/LICENSE-2.0
    12  //
    13  // Unless required by applicable law or agreed to in writing, software
    14  // distributed under the License is distributed on an "AS IS" BASIS,
    15  // See the License for the specific language governing permissions and
    16  // limitations under the License.
    17  
    18  package tidb
    19  
    20  import (
    21  	"fmt"
    22  	"runtime/debug"
    23  	"strings"
    24  	"time"
    25  
    26  	"github.com/insionng/yougam/libraries/juju/errors"
    27  	"github.com/insionng/yougam/libraries/ngaut/log"
    28  	"github.com/insionng/yougam/libraries/pingcap/tidb/infoschema"
    29  	"github.com/insionng/yougam/libraries/pingcap/tidb/mysql"
    30  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/variable"
    31  )
    32  
    33  const (
    34  	// CreateUserTable is the SQL statement creates User table in system db.
    35  	CreateUserTable = `CREATE TABLE if not exists mysql.user (
    36  		Host			CHAR(64),
    37  		User			CHAR(16),
    38  		Password		CHAR(41),
    39  		Select_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    40  		Insert_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    41  		Update_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    42  		Delete_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    43  		Create_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    44  		Drop_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    45  		Grant_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    46  		Alter_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    47  		Show_db_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    48  		Execute_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    49  		Index_priv		ENUM('N','Y') NOT NULL  DEFAULT 'N',
    50  		Create_user_priv	ENUM('N','Y') NOT NULL  DEFAULT 'N',
    51  		PRIMARY KEY (Host, User));`
    52  	// CreateDBPrivTable is the SQL statement creates DB scope privilege table in system db.
    53  	CreateDBPrivTable = `CREATE TABLE if not exists mysql.db (
    54  		Host		CHAR(60),
    55  		DB		CHAR(64),
    56  		User		CHAR(16),
    57  		Select_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    58  		Insert_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    59  		Update_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    60  		Delete_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    61  		Create_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    62  		Drop_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    63  		Grant_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    64  		Index_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    65  		Alter_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    66  		Execute_priv	ENUM('N','Y') Not Null  DEFAULT 'N',
    67  		PRIMARY KEY (Host, DB, User));`
    68  	// CreateTablePrivTable is the SQL statement creates table scope privilege table in system db.
    69  	CreateTablePrivTable = `CREATE TABLE if not exists mysql.tables_priv (
    70  		Host		CHAR(60),
    71  		DB		CHAR(64),
    72  		User		CHAR(16),
    73  		Table_name	CHAR(64),
    74  		Grantor		CHAR(77),
    75  		Timestamp	Timestamp DEFAULT CURRENT_TIMESTAMP,
    76  		Table_priv	SET('Select','Insert','Update','Delete','Create','Drop','Grant', 'Index','Alter'),
    77  		Column_priv	SET('Select','Insert','Update'),
    78  		PRIMARY KEY (Host, DB, User, Table_name));`
    79  	// CreateColumnPrivTable is the SQL statement creates column scope privilege table in system db.
    80  	CreateColumnPrivTable = `CREATE TABLE if not exists mysql.columns_priv(
    81  		Host		CHAR(60),
    82  		DB		CHAR(64),
    83  		User		CHAR(16),
    84  		Table_name	CHAR(64),
    85  		Column_name	CHAR(64),
    86  		Timestamp	Timestamp DEFAULT CURRENT_TIMESTAMP,
    87  		Column_priv	SET('Select','Insert','Update'),
    88  		PRIMARY KEY (Host, DB, User, Table_name, Column_name));`
    89  	// CreateGloablVariablesTable is the SQL statement creates global variable table in system db.
    90  	// TODO: MySQL puts GLOBAL_VARIABLES table in INFORMATION_SCHEMA db.
    91  	// INFORMATION_SCHEMA is a virtual db in TiDB. So we put this table in system db.
    92  	// Maybe we will put it back to INFORMATION_SCHEMA.
    93  	CreateGloablVariablesTable = `CREATE TABLE if not exists mysql.GLOBAL_VARIABLES(
    94  		VARIABLE_NAME  VARCHAR(64) Not Null PRIMARY KEY,
    95  		VARIABLE_VALUE VARCHAR(1024) DEFAULT Null);`
    96  	// CreateTiDBTable is the SQL statement creates a table in system db.
    97  	// This table is a key-value struct contains some information used by TiDB.
    98  	// Currently we only put bootstrapped in it which indicates if the system is already bootstrapped.
    99  	CreateTiDBTable = `CREATE TABLE if not exists mysql.tidb(
   100  		VARIABLE_NAME  VARCHAR(64) Not Null PRIMARY KEY,
   101  		VARIABLE_VALUE VARCHAR(1024) DEFAULT Null,
   102  		COMMENT VARCHAR(1024));`
   103  )
   104  
   105  // Bootstrap initiates system DB for a store.
   106  func bootstrap(s Session) {
   107  	b, err := checkBootstrapped(s)
   108  	if err != nil {
   109  		log.Fatal(err)
   110  	}
   111  	if b {
   112  		return
   113  	}
   114  	doDDLWorks(s)
   115  	doDMLWorks(s)
   116  }
   117  
   118  const (
   119  	bootstrappedVar     = "bootstrapped"
   120  	bootstrappedVarTrue = "True"
   121  )
   122  
   123  func checkBootstrapped(s Session) (bool, error) {
   124  	//  Check if system db exists.
   125  	_, err := s.Execute(fmt.Sprintf("USE %s;", mysql.SystemDB))
   126  	if err != nil && infoschema.ErrDatabaseNotExists.NotEqual(err) {
   127  		log.Fatal(err)
   128  	}
   129  	// Check bootstrapped variable value in TiDB table.
   130  	v, err := checkBootstrappedVar(s)
   131  	if err != nil {
   132  		return false, errors.Trace(err)
   133  	}
   134  	return v, nil
   135  }
   136  
   137  func checkBootstrappedVar(s Session) (bool, error) {
   138  	sql := fmt.Sprintf(`SELECT VARIABLE_VALUE FROM %s.%s WHERE VARIABLE_NAME="%s"`,
   139  		mysql.SystemDB, mysql.TiDBTable, bootstrappedVar)
   140  	rs, err := s.Execute(sql)
   141  	if err != nil {
   142  		if infoschema.ErrTableNotExists.Equal(err) {
   143  			return false, nil
   144  		}
   145  		return false, errors.Trace(err)
   146  	}
   147  
   148  	if len(rs) != 1 {
   149  		return false, errors.New("Wrong number of Recordset")
   150  	}
   151  	r := rs[0]
   152  	row, err := r.Next()
   153  	if err != nil || row == nil {
   154  		return false, errors.Trace(err)
   155  	}
   156  
   157  	isBootstrapped := row.Data[0].GetString() == bootstrappedVarTrue
   158  	if isBootstrapped {
   159  		// Make sure that doesn't affect the following operations.
   160  
   161  		if err = s.FinishTxn(false); err != nil {
   162  			return false, errors.Trace(err)
   163  		}
   164  	}
   165  
   166  	return isBootstrapped, nil
   167  }
   168  
   169  // Execute DDL statements in bootstrap stage.
   170  func doDDLWorks(s Session) {
   171  	// Create a test database.
   172  	mustExecute(s, "CREATE DATABASE IF NOT EXISTS test")
   173  	// Create system db.
   174  	mustExecute(s, fmt.Sprintf("CREATE DATABASE IF NOT EXISTS %s;", mysql.SystemDB))
   175  	// Create user table.
   176  	mustExecute(s, CreateUserTable)
   177  	// Create privilege tables.
   178  	mustExecute(s, CreateDBPrivTable)
   179  	mustExecute(s, CreateTablePrivTable)
   180  	mustExecute(s, CreateColumnPrivTable)
   181  	// Create global systemt variable table.
   182  	mustExecute(s, CreateGloablVariablesTable)
   183  	// Create TiDB table.
   184  	mustExecute(s, CreateTiDBTable)
   185  }
   186  
   187  // Execute DML statements in bootstrap stage.
   188  // All the statements run in a single transaction.
   189  func doDMLWorks(s Session) {
   190  	mustExecute(s, "BEGIN")
   191  
   192  	// Insert a default user with empty password.
   193  	mustExecute(s, `INSERT INTO mysql.user VALUES
   194  		("%", "root", "", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y", "Y")`)
   195  
   196  	// Init global system variables table.
   197  	values := make([]string, 0, len(variable.SysVars))
   198  	for k, v := range variable.SysVars {
   199  		value := fmt.Sprintf(`("%s", "%s")`, strings.ToLower(k), v.Value)
   200  		values = append(values, value)
   201  	}
   202  	sql := fmt.Sprintf("INSERT INTO %s.%s VALUES %s;", mysql.SystemDB, mysql.GlobalVariablesTable,
   203  		strings.Join(values, ", "))
   204  	mustExecute(s, sql)
   205  
   206  	sql = fmt.Sprintf(`INSERT INTO %s.%s VALUES("%s", "%s", "Bootstrap flag. Do not delete.")
   207  		ON DUPLICATE KEY UPDATE VARIABLE_VALUE="%s"`,
   208  		mysql.SystemDB, mysql.TiDBTable, bootstrappedVar, bootstrappedVarTrue, bootstrappedVarTrue)
   209  	mustExecute(s, sql)
   210  	_, err := s.Execute("COMMIT")
   211  	if err != nil {
   212  		time.Sleep(1 * time.Second)
   213  		// Check if TiDB is already bootstrapped.
   214  		b, err1 := checkBootstrapped(s)
   215  		if err1 != nil {
   216  			log.Fatal(err1)
   217  		}
   218  		if b {
   219  			return
   220  		}
   221  		log.Fatal(err)
   222  	}
   223  }
   224  
   225  func mustExecute(s Session, sql string) {
   226  	_, err := s.Execute(sql)
   227  	if err != nil {
   228  		debug.PrintStack()
   229  		log.Fatal(err)
   230  	}
   231  }