github.com/XiaoMi/Gaea@v1.2.5/parser/stmtctx/stmtctx.go (about)

     1  // Copyright 2017 PingCAP, Inc.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package stmtctx
    15  
    16  import (
    17  	"math"
    18  	"sync"
    19  	"time"
    20  
    21  	"github.com/XiaoMi/Gaea/mysql"
    22  )
    23  
    24  const (
    25  	// WarnLevelError represents level "Error" for 'SHOW WARNINGS' syntax.
    26  	WarnLevelError = "Error"
    27  	// WarnLevelWarning represents level "Warning" for 'SHOW WARNINGS' syntax.
    28  	WarnLevelWarning = "Warning"
    29  	// WarnLevelNote represents level "Note" for 'SHOW WARNINGS' syntax.
    30  	WarnLevelNote = "Note"
    31  )
    32  
    33  // SQLWarn relates a sql warning and it's level.
    34  type SQLWarn struct {
    35  	Level string
    36  	Err   error
    37  }
    38  
    39  // StatementContext contains variables for a statement.
    40  // It should be reset before executing a statement.
    41  type StatementContext struct {
    42  	// Set the following variables before execution
    43  
    44  	// IsDDLJobInQueue is used to mark whether the DDL job is put into the queue.
    45  	// If IsDDLJobInQueue is true, it means the DDL job is in the queue of storage, and it can be handled by the DDL worker.
    46  	IsDDLJobInQueue        bool
    47  	InInsertStmt           bool
    48  	InUpdateStmt           bool
    49  	InDeleteStmt           bool
    50  	InSelectStmt           bool
    51  	InLoadDataStmt         bool
    52  	IgnoreTruncate         bool
    53  	IgnoreZeroInDate       bool
    54  	DupKeyAsWarning        bool
    55  	BadNullAsWarning       bool
    56  	DividedByZeroAsWarning bool
    57  	TruncateAsWarning      bool
    58  	OverflowAsWarning      bool
    59  	InShowWarning          bool
    60  	UseCache               bool
    61  	PadCharToFullLength    bool
    62  	BatchCheck             bool
    63  	InNullRejectCheck      bool
    64  	AllowInvalidDate       bool
    65  
    66  	// mu struct holds variables that change during execution.
    67  	mu struct {
    68  		sync.Mutex
    69  
    70  		affectedRows uint64
    71  		foundRows    uint64
    72  
    73  		/*
    74  			following variables are ported from 'COPY_INFO' struct of MySQL server source,
    75  			they are used to count rows for INSERT/REPLACE/UPDATE queries:
    76  			  If a row is inserted then the copied variable is incremented.
    77  			  If a row is updated by the INSERT ... ON DUPLICATE KEY UPDATE and the
    78  			     new data differs from the old one then the copied and the updated
    79  			     variables are incremented.
    80  			  The touched variable is incremented if a row was touched by the update part
    81  			     of the INSERT ... ON DUPLICATE KEY UPDATE no matter whether the row
    82  			     was actually changed or not.
    83  
    84  			see https://github.com/mysql/mysql-server/blob/d2029238d6d9f648077664e4cdd611e231a6dc14/sql/sql_data_change.h#L60 for more details
    85  		*/
    86  		records uint64
    87  		updated uint64
    88  		copied  uint64
    89  		touched uint64
    90  
    91  		message           string
    92  		warnings          []SQLWarn
    93  		histogramsNotLoad bool
    94  	}
    95  	// PrevAffectedRows is the affected-rows value(DDL is 0, DML is the number of affected rows).
    96  	PrevAffectedRows int64
    97  	// PrevLastInsertID is the last insert ID of previous statement.
    98  	PrevLastInsertID uint64
    99  	// LastInsertID is the auto-generated ID in the current statement.
   100  	LastInsertID uint64
   101  	// InsertID is the given insert ID of an auto_increment column.
   102  	InsertID uint64
   103  
   104  	// Copied from SessionVars.TimeZone.
   105  	TimeZone     *time.Location
   106  	Priority     mysql.PriorityEnum
   107  	NotFillCache bool
   108  	TableIDs     []int64
   109  	IndexIDs     []int64
   110  	NowTs        time.Time
   111  	SysTs        time.Time
   112  	StmtType     string
   113  }
   114  
   115  // AddAffectedRows adds affected rows.
   116  func (sc *StatementContext) AddAffectedRows(rows uint64) {
   117  	sc.mu.Lock()
   118  	sc.mu.affectedRows += rows
   119  	sc.mu.Unlock()
   120  }
   121  
   122  // AffectedRows gets affected rows.
   123  func (sc *StatementContext) AffectedRows() uint64 {
   124  	sc.mu.Lock()
   125  	rows := sc.mu.affectedRows
   126  	sc.mu.Unlock()
   127  	return rows
   128  }
   129  
   130  // FoundRows gets found rows.
   131  func (sc *StatementContext) FoundRows() uint64 {
   132  	sc.mu.Lock()
   133  	rows := sc.mu.foundRows
   134  	sc.mu.Unlock()
   135  	return rows
   136  }
   137  
   138  // AddFoundRows adds found rows.
   139  func (sc *StatementContext) AddFoundRows(rows uint64) {
   140  	sc.mu.Lock()
   141  	sc.mu.foundRows += rows
   142  	sc.mu.Unlock()
   143  }
   144  
   145  // RecordRows is used to generate info message
   146  func (sc *StatementContext) RecordRows() uint64 {
   147  	sc.mu.Lock()
   148  	rows := sc.mu.records
   149  	sc.mu.Unlock()
   150  	return rows
   151  }
   152  
   153  // AddRecordRows adds record rows.
   154  func (sc *StatementContext) AddRecordRows(rows uint64) {
   155  	sc.mu.Lock()
   156  	sc.mu.records += rows
   157  	sc.mu.Unlock()
   158  }
   159  
   160  // UpdatedRows is used to generate info message
   161  func (sc *StatementContext) UpdatedRows() uint64 {
   162  	sc.mu.Lock()
   163  	rows := sc.mu.updated
   164  	sc.mu.Unlock()
   165  	return rows
   166  }
   167  
   168  // AddUpdatedRows adds updated rows.
   169  func (sc *StatementContext) AddUpdatedRows(rows uint64) {
   170  	sc.mu.Lock()
   171  	sc.mu.updated += rows
   172  	sc.mu.Unlock()
   173  }
   174  
   175  // CopiedRows is used to generate info message
   176  func (sc *StatementContext) CopiedRows() uint64 {
   177  	sc.mu.Lock()
   178  	rows := sc.mu.copied
   179  	sc.mu.Unlock()
   180  	return rows
   181  }
   182  
   183  // AddCopiedRows adds copied rows.
   184  func (sc *StatementContext) AddCopiedRows(rows uint64) {
   185  	sc.mu.Lock()
   186  	sc.mu.copied += rows
   187  	sc.mu.Unlock()
   188  }
   189  
   190  // TouchedRows is used to generate info message
   191  func (sc *StatementContext) TouchedRows() uint64 {
   192  	sc.mu.Lock()
   193  	rows := sc.mu.touched
   194  	sc.mu.Unlock()
   195  	return rows
   196  }
   197  
   198  // AddTouchedRows adds touched rows.
   199  func (sc *StatementContext) AddTouchedRows(rows uint64) {
   200  	sc.mu.Lock()
   201  	sc.mu.touched += rows
   202  	sc.mu.Unlock()
   203  }
   204  
   205  // GetMessage returns the extra message of the last executed command, if there is no message, it returns empty string
   206  func (sc *StatementContext) GetMessage() string {
   207  	sc.mu.Lock()
   208  	msg := sc.mu.message
   209  	sc.mu.Unlock()
   210  	return msg
   211  }
   212  
   213  // SetMessage sets the info message generated by some commands
   214  func (sc *StatementContext) SetMessage(msg string) {
   215  	sc.mu.Lock()
   216  	sc.mu.message = msg
   217  	sc.mu.Unlock()
   218  }
   219  
   220  // GetWarnings gets warnings.
   221  func (sc *StatementContext) GetWarnings() []SQLWarn {
   222  	sc.mu.Lock()
   223  	warns := make([]SQLWarn, len(sc.mu.warnings))
   224  	copy(warns, sc.mu.warnings)
   225  	sc.mu.Unlock()
   226  	return warns
   227  }
   228  
   229  // WarningCount gets warning count.
   230  func (sc *StatementContext) WarningCount() uint16 {
   231  	if sc.InShowWarning {
   232  		return 0
   233  	}
   234  	sc.mu.Lock()
   235  	wc := uint16(len(sc.mu.warnings))
   236  	sc.mu.Unlock()
   237  	return wc
   238  }
   239  
   240  // NumWarnings gets warning count. It's different from `WarningCount` in that
   241  // `WarningCount` return the warning count of the last executed command, so if
   242  // the last command is a SHOW statement, `WarningCount` return 0. On the other
   243  // hand, `NumWarnings` always return number of warnings(or errors if `errOnly`
   244  // is set).
   245  func (sc *StatementContext) NumWarnings(errOnly bool) uint16 {
   246  	var wc uint16
   247  	sc.mu.Lock()
   248  	defer sc.mu.Unlock()
   249  	if errOnly {
   250  		for _, warn := range sc.mu.warnings {
   251  			if warn.Level == WarnLevelError {
   252  				wc++
   253  			}
   254  		}
   255  	} else {
   256  		wc = uint16(len(sc.mu.warnings))
   257  	}
   258  	return wc
   259  }
   260  
   261  // SetWarnings sets warnings.
   262  func (sc *StatementContext) SetWarnings(warns []SQLWarn) {
   263  	sc.mu.Lock()
   264  	sc.mu.warnings = warns
   265  	sc.mu.Unlock()
   266  }
   267  
   268  // AppendWarning appends a warning with level 'Warning'.
   269  func (sc *StatementContext) AppendWarning(warn error) {
   270  	sc.mu.Lock()
   271  	if len(sc.mu.warnings) < math.MaxUint16 {
   272  		sc.mu.warnings = append(sc.mu.warnings, SQLWarn{WarnLevelWarning, warn})
   273  	}
   274  	sc.mu.Unlock()
   275  }
   276  
   277  // AppendNote appends a warning with level 'Note'.
   278  func (sc *StatementContext) AppendNote(warn error) {
   279  	sc.mu.Lock()
   280  	if len(sc.mu.warnings) < math.MaxUint16 {
   281  		sc.mu.warnings = append(sc.mu.warnings, SQLWarn{WarnLevelNote, warn})
   282  	}
   283  	sc.mu.Unlock()
   284  }
   285  
   286  // AppendError appends a warning with level 'Error'.
   287  func (sc *StatementContext) AppendError(warn error) {
   288  	sc.mu.Lock()
   289  	if len(sc.mu.warnings) < math.MaxUint16 {
   290  		sc.mu.warnings = append(sc.mu.warnings, SQLWarn{WarnLevelError, warn})
   291  	}
   292  	sc.mu.Unlock()
   293  }
   294  
   295  // SetHistogramsNotLoad sets histogramsNotLoad.
   296  func (sc *StatementContext) SetHistogramsNotLoad() {
   297  	sc.mu.Lock()
   298  	sc.mu.histogramsNotLoad = true
   299  	sc.mu.Unlock()
   300  }
   301  
   302  // HistogramsNotLoad gets histogramsNotLoad.
   303  func (sc *StatementContext) HistogramsNotLoad() bool {
   304  	sc.mu.Lock()
   305  	notLoad := sc.mu.histogramsNotLoad
   306  	sc.mu.Unlock()
   307  	return notLoad
   308  }
   309  
   310  // HandleTruncate ignores or returns the error based on the StatementContext state.
   311  func (sc *StatementContext) HandleTruncate(err error) error {
   312  	// TODO: At present we have not checked whether the error can be ignored or treated as warning.
   313  	// We will do that later, and then append WarnDataTruncated instead of the error itself.
   314  	if err == nil {
   315  		return nil
   316  	}
   317  	if sc.IgnoreTruncate {
   318  		return nil
   319  	}
   320  	if sc.TruncateAsWarning {
   321  		sc.AppendWarning(err)
   322  		return nil
   323  	}
   324  	return err
   325  }
   326  
   327  // HandleOverflow treats ErrOverflow as warnings or returns the error based on the StmtCtx.OverflowAsWarning state.
   328  func (sc *StatementContext) HandleOverflow(err error, warnErr error) error {
   329  	if err == nil {
   330  		return nil
   331  	}
   332  
   333  	if sc.OverflowAsWarning {
   334  		sc.AppendWarning(warnErr)
   335  		return nil
   336  	}
   337  	return err
   338  }
   339  
   340  // ResetForRetry resets the changed states during execution.
   341  func (sc *StatementContext) ResetForRetry() {
   342  	sc.mu.Lock()
   343  	sc.mu.affectedRows = 0
   344  	sc.mu.foundRows = 0
   345  	sc.mu.records = 0
   346  	sc.mu.updated = 0
   347  	sc.mu.copied = 0
   348  	sc.mu.touched = 0
   349  	sc.mu.message = ""
   350  	sc.mu.warnings = nil
   351  	sc.mu.Unlock()
   352  	sc.TableIDs = sc.TableIDs[:0]
   353  	sc.IndexIDs = sc.IndexIDs[:0]
   354  }
   355  
   356  // ShouldClipToZero indicates whether values less than 0 should be clipped to 0 for unsigned integer types.
   357  // This is the case for `insert`, `update`, `alter table` and `load data infile` statements, when not in strict SQL mode.
   358  // see https://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html
   359  func (sc *StatementContext) ShouldClipToZero() bool {
   360  	// TODO: Currently altering column of integer to unsigned integer is not supported.
   361  	// If it is supported one day, that case should be added here.
   362  	return sc.InInsertStmt || sc.InLoadDataStmt
   363  }
   364  
   365  // ShouldIgnoreOverflowError indicates whether we should ignore the error when type conversion overflows,
   366  // so we can leave it for further processing like clipping values less than 0 to 0 for unsigned integer types.
   367  func (sc *StatementContext) ShouldIgnoreOverflowError() bool {
   368  	if (sc.InInsertStmt && sc.TruncateAsWarning) || sc.InLoadDataStmt {
   369  		return true
   370  	}
   371  	return false
   372  }