github.com/insionng/yougam@v0.0.0-20170714101924-2bc18d833463/libraries/pingcap/tidb/tidb.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  	"net/http"
    22  	"time"
    23  	// For pprof
    24  	_ "net/http/pprof"
    25  	"net/url"
    26  	"os"
    27  	"strings"
    28  	"sync"
    29  
    30  	"github.com/insionng/yougam/libraries/juju/errors"
    31  	"github.com/insionng/yougam/libraries/ngaut/log"
    32  	"github.com/insionng/yougam/libraries/pingcap/tidb/ast"
    33  	"github.com/insionng/yougam/libraries/pingcap/tidb/context"
    34  	"github.com/insionng/yougam/libraries/pingcap/tidb/domain"
    35  	"github.com/insionng/yougam/libraries/pingcap/tidb/executor"
    36  	"github.com/insionng/yougam/libraries/pingcap/tidb/kv"
    37  	"github.com/insionng/yougam/libraries/pingcap/tidb/metric"
    38  	"github.com/insionng/yougam/libraries/pingcap/tidb/parser"
    39  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/autocommit"
    40  	"github.com/insionng/yougam/libraries/pingcap/tidb/sessionctx/variable"
    41  	"github.com/insionng/yougam/libraries/pingcap/tidb/store/localstore"
    42  	"github.com/insionng/yougam/libraries/pingcap/tidb/store/localstore/engine"
    43  	"github.com/insionng/yougam/libraries/pingcap/tidb/store/localstore/goleveldb"
    44  	"github.com/insionng/yougam/libraries/pingcap/tidb/util/types"
    45  )
    46  
    47  // Engine prefix name
    48  const (
    49  	EngineGoLevelDBMemory = "memory://"
    50  	defaultMaxRetries     = 30
    51  	retrySleepInterval    = 500 * time.Millisecond
    52  )
    53  
    54  type domainMap struct {
    55  	domains map[string]*domain.Domain
    56  	mu      sync.Mutex
    57  }
    58  
    59  func (dm *domainMap) Get(store kv.Storage) (d *domain.Domain, err error) {
    60  	key := store.UUID()
    61  	dm.mu.Lock()
    62  	defer dm.mu.Unlock()
    63  	d = dm.domains[key]
    64  	if d != nil {
    65  		return
    66  	}
    67  
    68  	lease := time.Duration(0)
    69  	if !localstore.IsLocalStore(store) {
    70  		lease = schemaLease
    71  	}
    72  	d, err = domain.NewDomain(store, lease)
    73  	if err != nil {
    74  		return nil, errors.Trace(err)
    75  	}
    76  	dm.domains[key] = d
    77  	return
    78  }
    79  
    80  var (
    81  	domap = &domainMap{
    82  		domains: map[string]*domain.Domain{},
    83  	}
    84  	stores = make(map[string]kv.Driver)
    85  	// EnablePprof indicates whether to enable HTTP Pprof or not.
    86  	EnablePprof = os.Getenv("TIDB_PPROF") != "0"
    87  	// PprofAddr is the pprof url.
    88  	PprofAddr = ":8888"
    89  	// store.UUID()-> IfBootstrapped
    90  	storeBootstrapped = make(map[string]bool)
    91  
    92  	// schemaLease is the time for re-updating remote schema.
    93  	// In online DDL, we must wait 2 * SchemaLease time to guarantee
    94  	// all servers get the neweset schema.
    95  	// Default schema lease time is 1 second, you can change it with a proper time,
    96  	// but you must know that too little may cause badly performance degradation.
    97  	// For production, you should set a big schema lease, like 300s+.
    98  	schemaLease = 1 * time.Second
    99  )
   100  
   101  // SetSchemaLease changes the default schema lease time for DDL.
   102  // This function is very dangerous, don't use it if you really know what you do.
   103  // SetSchemaLease only affects not local storage after bootstrapped.
   104  func SetSchemaLease(lease time.Duration) {
   105  	schemaLease = lease
   106  }
   107  
   108  // What character set should the server translate a statement to after receiving it?
   109  // For this, the server uses the character_set_connection and collation_connection system variables.
   110  // It converts statements sent by the client from character_set_client to character_set_connection
   111  // (except for string literals that have an introducer such as _latin1 or _utf8).
   112  // collation_connection is important for comparisons of literal strings.
   113  // For comparisons of strings with column values, collation_connection does not matter because columns
   114  // have their own collation, which has a higher collation precedence.
   115  // See: https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html
   116  func getCtxCharsetInfo(ctx context.Context) (string, string) {
   117  	sessionVars := variable.GetSessionVars(ctx)
   118  	charset := sessionVars.Systems["character_set_connection"]
   119  	collation := sessionVars.Systems["collation_connection"]
   120  	return charset, collation
   121  }
   122  
   123  // Parse parses a query string to raw ast.StmtNode.
   124  func Parse(ctx context.Context, src string) ([]ast.StmtNode, error) {
   125  	log.Debug("compiling", src)
   126  	charset, collation := getCtxCharsetInfo(ctx)
   127  	stmts, err := parser.Parse(src, charset, collation)
   128  	if err != nil {
   129  		log.Warnf("compiling %s, error: %v", src, err)
   130  		return nil, errors.Trace(err)
   131  	}
   132  	return stmts, nil
   133  }
   134  
   135  // Compile is safe for concurrent use by multiple goroutines.
   136  func Compile(ctx context.Context, rawStmt ast.StmtNode) (ast.Statement, error) {
   137  	compiler := &executor.Compiler{}
   138  	st, err := compiler.Compile(ctx, rawStmt)
   139  	if err != nil {
   140  		return nil, errors.Trace(err)
   141  	}
   142  	return st, nil
   143  }
   144  
   145  func runStmt(ctx context.Context, s ast.Statement, args ...interface{}) (ast.RecordSet, error) {
   146  	var err error
   147  	var rs ast.RecordSet
   148  	// before every execution, we must clear affectedrows.
   149  	variable.GetSessionVars(ctx).SetAffectedRows(0)
   150  	if s.IsDDL() {
   151  		err = ctx.FinishTxn(false)
   152  		if err != nil {
   153  			return nil, errors.Trace(err)
   154  		}
   155  	}
   156  	rs, err = s.Exec(ctx)
   157  	// All the history should be added here.
   158  	se := ctx.(*session)
   159  	se.history.add(0, s)
   160  	// MySQL DDL should be auto-commit
   161  	if s.IsDDL() || autocommit.ShouldAutocommit(ctx) {
   162  		if err != nil {
   163  			ctx.FinishTxn(true)
   164  		} else {
   165  			err = ctx.FinishTxn(false)
   166  		}
   167  	}
   168  	return rs, errors.Trace(err)
   169  }
   170  
   171  // GetRows gets all the rows from a RecordSet.
   172  func GetRows(rs ast.RecordSet) ([][]types.Datum, error) {
   173  	if rs == nil {
   174  		return nil, nil
   175  	}
   176  	var rows [][]types.Datum
   177  	defer rs.Close()
   178  	// Negative limit means no limit.
   179  	for {
   180  		row, err := rs.Next()
   181  		if err != nil {
   182  			return nil, errors.Trace(err)
   183  		}
   184  		if row == nil {
   185  			break
   186  		}
   187  		rows = append(rows, row.Data)
   188  	}
   189  	return rows, nil
   190  }
   191  
   192  // RegisterStore registers a kv storage with unique name and its associated Driver.
   193  func RegisterStore(name string, driver kv.Driver) error {
   194  	name = strings.ToLower(name)
   195  
   196  	if _, ok := stores[name]; ok {
   197  		return errors.Errorf("%s is already registered", name)
   198  	}
   199  
   200  	stores[name] = driver
   201  	return nil
   202  }
   203  
   204  // RegisterLocalStore registers a local kv storage with unique name and its associated engine Driver.
   205  func RegisterLocalStore(name string, driver engine.Driver) error {
   206  	d := localstore.Driver{Driver: driver}
   207  	return RegisterStore(name, d)
   208  }
   209  
   210  // NewStore creates a kv Storage with path.
   211  //
   212  // The path must be a URL format 'engine://path?params' like the one for
   213  // tidb.Open() but with the dbname cut off.
   214  // Examples:
   215  //    goleveldb://relative/path
   216  //    boltdb:///absolute/path
   217  //    hbase://zk1,zk2,zk3/hbasetbl?tso=127.0.0.1:1234
   218  //
   219  // The engine should be registered before creating storage.
   220  func NewStore(path string) (kv.Storage, error) {
   221  	return newStoreWithRetry(path, defaultMaxRetries)
   222  }
   223  
   224  func newStoreWithRetry(path string, maxRetries int) (kv.Storage, error) {
   225  	url, err := url.Parse(path)
   226  	if err != nil {
   227  		return nil, errors.Trace(err)
   228  	}
   229  
   230  	name := strings.ToLower(url.Scheme)
   231  	d, ok := stores[name]
   232  	if !ok {
   233  		return nil, errors.Errorf("invalid uri format, storage %s is not registered", name)
   234  	}
   235  
   236  	var s kv.Storage
   237  	for i := 1; i <= maxRetries; i++ {
   238  		s, err = d.Open(path)
   239  		if err == nil || !kv.IsRetryableError(err) {
   240  			break
   241  		}
   242  		sleepTime := time.Duration(uint64(retrySleepInterval) * uint64(i))
   243  		log.Warnf("Waiting store to get ready, sleep %v and try again...", sleepTime)
   244  		time.Sleep(sleepTime)
   245  	}
   246  	return s, errors.Trace(err)
   247  }
   248  
   249  var queryStmtTable = []string{"explain", "select", "show", "execute", "describe", "desc", "admin"}
   250  
   251  func trimSQL(sql string) string {
   252  	// Trim space.
   253  	sql = strings.TrimSpace(sql)
   254  	// Trim leading /*comment*/
   255  	// There may be multiple comments
   256  	for strings.HasPrefix(sql, "/*") {
   257  		i := strings.Index(sql, "*/")
   258  		if i != -1 && i < len(sql)+1 {
   259  			sql = sql[i+2:]
   260  			sql = strings.TrimSpace(sql)
   261  			continue
   262  		}
   263  		break
   264  	}
   265  	// Trim leading '('. For `(select 1);` is also a query.
   266  	return strings.TrimLeft(sql, "( ")
   267  }
   268  
   269  // IsQuery checks if a sql statement is a query statement.
   270  func IsQuery(sql string) bool {
   271  	sqlText := strings.ToLower(trimSQL(sql))
   272  	for _, key := range queryStmtTable {
   273  		if strings.HasPrefix(sqlText, key) {
   274  			return true
   275  		}
   276  	}
   277  
   278  	return false
   279  }
   280  
   281  var tpsMetrics metric.TPSMetrics
   282  
   283  // GetTPS gets tidb tps.
   284  func GetTPS() int64 {
   285  	return tpsMetrics.Get()
   286  }
   287  
   288  func init() {
   289  	// Register default memory and goleveldb storage
   290  	RegisterLocalStore("memory", goleveldb.MemoryDriver{})
   291  	RegisterLocalStore("goleveldb", goleveldb.Driver{})
   292  	// start pprof handlers
   293  	if EnablePprof {
   294  		go http.ListenAndServe(PprofAddr, nil)
   295  	}
   296  
   297  	// Init metrics
   298  	tpsMetrics = metric.NewTPSMetrics()
   299  }