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 }