Files
tidb/sessionctx/variable/session.go
Ewan Chou ebbbf8993b ddl: speed up create table without the need to change lease. (#2332)
DDLs that doesn't modify rows like create/drop/truncate table, create/drop database
do not need to wait 2 lease on ddl worker, we can safely remove those wait to
speed up DDL.

But user may create a table, then insert data on that table on another TiDB server,
So we need to wait in session.

User can set a session variable to skip DDL wait if needed, and there is no risk of
data inconsistency.

This solution remove the need for user to set lease to zero when loading data,
It's much more safe and easy to use.
2016-12-28 22:08:19 +08:00

315 lines
8.9 KiB
Go

// 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 variable
import (
"strings"
"sync"
"github.com/juju/errors"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/terror"
)
const (
codeCantGetValidID terror.ErrCode = 1
codeCantSetToNull terror.ErrCode = 2
)
// Error instances.
var (
errCantGetValidID = terror.ClassVariable.New(codeCantGetValidID, "cannot get valid auto-increment id in retry")
ErrCantSetToNull = terror.ClassVariable.New(codeCantSetToNull, "cannot set variable to null")
)
// RetryInfo saves retry information.
type RetryInfo struct {
Retrying bool
currRetryOff int
autoIncrementIDs []int64
}
// Clean does some clean work.
func (r *RetryInfo) Clean() {
r.currRetryOff = 0
if len(r.autoIncrementIDs) > 0 {
r.autoIncrementIDs = r.autoIncrementIDs[:0]
}
}
// AddAutoIncrementID adds id to AutoIncrementIDs.
func (r *RetryInfo) AddAutoIncrementID(id int64) {
r.autoIncrementIDs = append(r.autoIncrementIDs, id)
}
// ResetOffset resets the current retry offset.
func (r *RetryInfo) ResetOffset() {
r.currRetryOff = 0
}
// GetCurrAutoIncrementID gets current AutoIncrementID.
func (r *RetryInfo) GetCurrAutoIncrementID() (int64, error) {
if r.currRetryOff >= len(r.autoIncrementIDs) {
return 0, errCantGetValidID
}
id := r.autoIncrementIDs[r.currRetryOff]
r.currRetryOff++
return id, nil
}
// TransactionContext is used to store variables that has transaction scope.
type TransactionContext struct {
ForUpdate bool
DirtyDB interface{}
Binlog interface{}
InfoSchema interface{}
Histroy interface{}
SchemaVersion int64
}
// SessionVars is to handle user-defined or global variables in current session.
type SessionVars struct {
// user-defined variables
Users map[string]string
// system variables
Systems map[string]string
// prepared statement
PreparedStmts map[uint32]interface{}
PreparedStmtNameToID map[string]uint32
// prepared statement auto increment id
preparedStmtID uint32
// retry information
RetryInfo *RetryInfo
// Should be reset on transaction finished.
TxnCtx *TransactionContext
// following variables are special for current session
Status uint16
LastInsertID uint64
// Client capability
ClientCapability uint32
// Connection ID
ConnectionID uint64
// Current user
User string
// Current DB
CurrentDB string
// Strict SQL mode
StrictSQLMode bool
// CommonGlobalLoaded indicates if common global variable has been loaded for this session.
CommonGlobalLoaded bool
// InRestrictedSQL indicates if the session is handling restricted SQL execution.
InRestrictedSQL bool
// SnapshotTS is used for reading history data. For simplicity, SnapshotTS only supports distsql request.
SnapshotTS uint64
// SnapshotInfoschema is used with SnapshotTS, when the schema version at snapshotTS less than current schema
// version, we load an old version schema for query.
SnapshotInfoschema interface{}
// SkipConstraintCheck is true when importing data.
SkipConstraintCheck bool
// SkipDDLWait can be set to true to skip 2 lease wait after create/drop/truncate table, create/drop database.
// Then if there are multiple TiDB servers, the new table may not be available for other TiDB servers.
SkipDDLWait bool
// GlobalAccessor is used to set and get global variables.
GlobalVarsAccessor GlobalVarAccessor
// StmtCtx holds variables for current executing statement.
StmtCtx *StatementContext
// CurrInsertValues is used to record current ValuesExpr's values.
// See http://dev.mysql.com/doc/refman/5.7/en/miscellaneous-functions.html#function_values
CurrInsertValues interface{}
}
// NewSessionVars creates a session vars object.
func NewSessionVars() *SessionVars {
return &SessionVars{
Users: make(map[string]string),
Systems: make(map[string]string),
PreparedStmts: make(map[uint32]interface{}),
PreparedStmtNameToID: make(map[string]uint32),
TxnCtx: &TransactionContext{},
RetryInfo: &RetryInfo{},
StrictSQLMode: true,
Status: mysql.ServerStatusAutocommit,
StmtCtx: new(StatementContext),
}
}
const (
characterSetConnection = "character_set_connection"
collationConnection = "collation_connection"
)
// GetCharsetInfo gets charset and collation for current context.
// What character set should the server translate a statement to after receiving it?
// For this, the server uses the character_set_connection and collation_connection system variables.
// It converts statements sent by the client from character_set_client to character_set_connection
// (except for string literals that have an introducer such as _latin1 or _utf8).
// collation_connection is important for comparisons of literal strings.
// For comparisons of strings with column values, collation_connection does not matter because columns
// have their own collation, which has a higher collation precedence.
// See https://dev.mysql.com/doc/refman/5.7/en/charset-connection.html
func (s *SessionVars) GetCharsetInfo() (charset, collation string) {
charset = s.Systems[characterSetConnection]
collation = s.Systems[collationConnection]
return
}
// SetLastInsertID saves the last insert id to the session context.
// TODO: we may store the result for last_insert_id sys var later.
func (s *SessionVars) SetLastInsertID(insertID uint64) {
s.LastInsertID = insertID
}
// SetStatusFlag sets the session server status variable.
// If on is ture sets the flag in session status,
// otherwise removes the flag.
func (s *SessionVars) SetStatusFlag(flag uint16, on bool) {
if on {
s.Status |= flag
return
}
s.Status &= (^flag)
}
// GetStatusFlag gets the session server status variable, returns true if it is on.
func (s *SessionVars) GetStatusFlag(flag uint16) bool {
return s.Status&flag > 0
}
// InTxn returns if the session is in transaction.
func (s *SessionVars) InTxn() bool {
return s.GetStatusFlag(mysql.ServerStatusInTrans)
}
// IsAutocommit returns if the session is set to autocommit.
func (s *SessionVars) IsAutocommit() bool {
return s.GetStatusFlag(mysql.ServerStatusAutocommit)
}
// GetNextPreparedStmtID generates and returns the next session scope prepared statement id.
func (s *SessionVars) GetNextPreparedStmtID() uint32 {
s.preparedStmtID++
return s.preparedStmtID
}
// special session variables.
const (
SQLModeVar = "sql_mode"
AutocommitVar = "autocommit"
CharacterSetResults = "character_set_results"
)
// GetTiDBSystemVar gets variable value for name.
// The variable should be a TiDB specific system variable (The vars in tidbSysVars map).
// We load the variable from session first, if not found, use local defined default variable.
func (s *SessionVars) GetTiDBSystemVar(name string) (string, error) {
key := strings.ToLower(name)
_, ok := tidbSysVars[key]
if !ok {
return "", errors.Errorf("%s is not a TiDB specific system variable.", name)
}
sVal, ok := s.Systems[key]
if ok {
return sVal, nil
}
return SysVars[key].Value, nil
}
// StatementContext contains variables for a statement.
// It should be reset before executing a statement.
type StatementContext struct {
/* Variables that are set before execution */
InUpdateStmt bool
IgnoreTruncate bool
TruncateAsWarning bool
/* Variables that changes during execution. */
mu struct {
sync.Mutex
affectedRows uint64
foundRows uint64
warnings []error
}
}
// AddAffectedRows adds affected rows.
func (sc *StatementContext) AddAffectedRows(rows uint64) {
sc.mu.Lock()
sc.mu.affectedRows += rows
sc.mu.Unlock()
}
// AffectedRows gets affected rows.
func (sc *StatementContext) AffectedRows() uint64 {
sc.mu.Lock()
rows := sc.mu.affectedRows
sc.mu.Unlock()
return rows
}
// FoundRows gets found rows.
func (sc *StatementContext) FoundRows() uint64 {
sc.mu.Lock()
rows := sc.mu.foundRows
sc.mu.Unlock()
return rows
}
// AddFoundRows adds found rows.
func (sc *StatementContext) AddFoundRows(rows uint64) {
sc.mu.Lock()
sc.mu.foundRows += rows
sc.mu.Unlock()
}
// GetWarnings gets warnings.
func (sc *StatementContext) GetWarnings() []error {
sc.mu.Lock()
warns := make([]error, len(sc.mu.warnings))
copy(warns, sc.mu.warnings)
sc.mu.Unlock()
return warns
}
// SetWarnings sets warnings.
func (sc *StatementContext) SetWarnings(warns []error) {
sc.mu.Lock()
sc.mu.warnings = warns
sc.mu.Unlock()
}
// AppendWarning appends a warning.
func (sc *StatementContext) AppendWarning(warn error) {
sc.mu.Lock()
sc.mu.warnings = append(sc.mu.warnings, warn)
sc.mu.Unlock()
}