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 }