github.com/whtcorpsinc/milevadb-prod@v0.0.0-20211104133533-f57f4be3b597/interlock/set.go (about) 1 // Copyright 2020 WHTCORPS INC, 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 interlock 15 16 import ( 17 "context" 18 "fmt" 19 "strings" 20 21 "github.com/whtcorpsinc/errors" 22 "github.com/whtcorpsinc/BerolinaSQL/ast" 23 "github.com/whtcorpsinc/BerolinaSQL/charset" 24 "github.com/whtcorpsinc/BerolinaSQL/allegrosql" 25 "github.com/whtcorpsinc/BerolinaSQL/terror" 26 "github.com/whtcorpsinc/milevadb/petri" 27 "github.com/whtcorpsinc/milevadb/memex" 28 "github.com/whtcorpsinc/milevadb/plugin" 29 "github.com/whtcorpsinc/milevadb/stochastikctx/variable" 30 "github.com/whtcorpsinc/milevadb/types" 31 "github.com/whtcorpsinc/milevadb/soliton/chunk" 32 "github.com/whtcorpsinc/milevadb/soliton/defCauslate" 33 "github.com/whtcorpsinc/milevadb/soliton/gcutil" 34 "github.com/whtcorpsinc/milevadb/soliton/logutil" 35 "github.com/whtcorpsinc/milevadb/soliton/stmtsummary" 36 "github.com/whtcorpsinc/milevadb/soliton/stringutil" 37 "go.uber.org/zap" 38 ) 39 40 const ( 41 scopeGlobal = "global" 42 scopeStochastik = "stochastik" 43 ) 44 45 // SetInterlockingDirectorate executes set memex. 46 type SetInterlockingDirectorate struct { 47 baseInterlockingDirectorate 48 49 vars []*memex.VarAssignment 50 done bool 51 } 52 53 // Next implements the InterlockingDirectorate Next interface. 54 func (e *SetInterlockingDirectorate) Next(ctx context.Context, req *chunk.Chunk) error { 55 req.Reset() 56 if e.done { 57 return nil 58 } 59 e.done = true 60 stochastikVars := e.ctx.GetStochastikVars() 61 for _, v := range e.vars { 62 // Variable is case insensitive, we use lower case. 63 if v.Name == ast.SetNames || v.Name == ast.SetCharset { 64 // This is set charset stmt. 65 if v.IsDefault { 66 err := e.setCharset(allegrosql.DefaultCharset, "", v.Name == ast.SetNames) 67 if err != nil { 68 return err 69 } 70 continue 71 } 72 dt, err := v.Expr.(*memex.Constant).Eval(chunk.Event{}) 73 if err != nil { 74 return err 75 } 76 cs := dt.GetString() 77 var co string 78 if v.ExtendValue != nil { 79 co = v.ExtendValue.Value.GetString() 80 } 81 err = e.setCharset(cs, co, v.Name == ast.SetNames) 82 if err != nil { 83 return err 84 } 85 continue 86 } 87 name := strings.ToLower(v.Name) 88 if !v.IsSystem { 89 // Set user variable. 90 value, err := v.Expr.Eval(chunk.Event{}) 91 if err != nil { 92 return err 93 } 94 95 if value.IsNull() { 96 delete(stochastikVars.Users, name) 97 } else { 98 svalue, err1 := value.ToString() 99 if err1 != nil { 100 return err1 101 } 102 103 stochastikVars.SetUserVar(name, stringutil.Copy(svalue), value.DefCauslation()) 104 } 105 continue 106 } 107 108 syns := e.getSynonyms(name) 109 // Set system variable 110 for _, n := range syns { 111 err := e.setSysVariable(n, v) 112 if err != nil { 113 return err 114 } 115 } 116 } 117 return nil 118 } 119 120 func (e *SetInterlockingDirectorate) getSynonyms(varName string) []string { 121 synonyms, ok := variable.SynonymsSysVariables[varName] 122 if ok { 123 return synonyms 124 } 125 126 synonyms = []string{varName} 127 return synonyms 128 } 129 130 func (e *SetInterlockingDirectorate) setSysVariable(name string, v *memex.VarAssignment) error { 131 stochastikVars := e.ctx.GetStochastikVars() 132 sysVar := variable.GetSysVar(name) 133 if sysVar == nil { 134 return variable.ErrUnknownSystemVar.GenWithStackByArgs(name) 135 } 136 if sysVar.Scope == variable.ScopeNone { 137 return errors.Errorf("Variable '%s' is a read only variable", name) 138 } 139 var valStr string 140 var scopeStr string 141 if v.IsGlobal { 142 scopeStr = scopeGlobal 143 // Set global scope system variable. 144 if sysVar.Scope&variable.ScopeGlobal == 0 { 145 return errors.Errorf("Variable '%s' is a SESSION variable and can't be used with SET GLOBAL", name) 146 } 147 value, err := e.getVarValue(v, sysVar) 148 if err != nil { 149 return err 150 } 151 if value.IsNull() { 152 value.SetString("", allegrosql.DefaultDefCauslationName) 153 } 154 valStr, err = value.ToString() 155 if err != nil { 156 return err 157 } 158 err = stochastikVars.GlobalVarsAccessor.SetGlobalSysVar(name, valStr) 159 if err != nil { 160 return err 161 } 162 err = plugin.ForeachPlugin(plugin.Audit, func(p *plugin.Plugin) error { 163 auditPlugin := plugin.DeclareAuditManifest(p.Manifest) 164 if auditPlugin.OnGlobalVariableEvent != nil { 165 auditPlugin.OnGlobalVariableEvent(context.Background(), e.ctx.GetStochastikVars(), name, valStr) 166 } 167 return nil 168 }) 169 if err != nil { 170 return err 171 } 172 } else { 173 scopeStr = scopeStochastik 174 // Set stochastik scope system variable. 175 if sysVar.Scope&variable.ScopeStochastik == 0 { 176 return errors.Errorf("Variable '%s' is a GLOBAL variable and should be set with SET GLOBAL", name) 177 } 178 value, err := e.getVarValue(v, nil) 179 if err != nil { 180 return err 181 } 182 oldSnapshotTS := stochastikVars.SnapshotTS 183 if name == variable.TxnIsolationOneShot && stochastikVars.InTxn() { 184 return errors.Trace(ErrCantChangeTxCharacteristics) 185 } 186 if name == variable.MilevaDBFoundInCausetCache { 187 stochastikVars.StmtCtx.AppendWarning(fmt.Errorf("Set operation for '%s' will not take effect", variable.MilevaDBFoundInCausetCache)) 188 return nil 189 } 190 err = variable.SetStochastikSystemVar(stochastikVars, name, value) 191 if err != nil { 192 return err 193 } 194 newSnapshotIsSet := stochastikVars.SnapshotTS > 0 && stochastikVars.SnapshotTS != oldSnapshotTS 195 if newSnapshotIsSet { 196 err = gcutil.ValidateSnapshot(e.ctx, stochastikVars.SnapshotTS) 197 if err != nil { 198 stochastikVars.SnapshotTS = oldSnapshotTS 199 return err 200 } 201 } 202 err = e.loadSnapshotSchemaReplicantIfNeeded(name) 203 if err != nil { 204 stochastikVars.SnapshotTS = oldSnapshotTS 205 return err 206 } 207 if value.IsNull() { 208 valStr = "NULL" 209 } else { 210 var err error 211 valStr, err = value.ToString() 212 terror.Log(err) 213 } 214 } 215 if scopeStr == scopeGlobal { 216 logutil.BgLogger().Info(fmt.Sprintf("set %s var", scopeStr), zap.Uint64("conn", stochastikVars.ConnectionID), zap.String("name", name), zap.String("val", valStr)) 217 } else { 218 // Clients are often noisy in setting stochastik variables such as 219 // autocommit, timezone, query cache 220 logutil.BgLogger().Debug(fmt.Sprintf("set %s var", scopeStr), zap.Uint64("conn", stochastikVars.ConnectionID), zap.String("name", name), zap.String("val", valStr)) 221 } 222 223 switch name { 224 case variable.MilevaDBEnableStmtSummary: 225 return stmtsummary.StmtSummaryByDigestMap.SetEnabled(valStr, !v.IsGlobal) 226 case variable.MilevaDBStmtSummaryInternalQuery: 227 return stmtsummary.StmtSummaryByDigestMap.SetEnabledInternalQuery(valStr, !v.IsGlobal) 228 case variable.MilevaDBStmtSummaryRefreshInterval: 229 return stmtsummary.StmtSummaryByDigestMap.SetRefreshInterval(valStr, !v.IsGlobal) 230 case variable.MilevaDBStmtSummaryHistorySize: 231 return stmtsummary.StmtSummaryByDigestMap.SetHistorySize(valStr, !v.IsGlobal) 232 case variable.MilevaDBStmtSummaryMaxStmtCount: 233 return stmtsummary.StmtSummaryByDigestMap.SetMaxStmtCount(valStr, !v.IsGlobal) 234 case variable.MilevaDBStmtSummaryMaxALLEGROSQLLength: 235 return stmtsummary.StmtSummaryByDigestMap.SetMaxALLEGROSQLLength(valStr, !v.IsGlobal) 236 case variable.MilevaDBCaptureCausetBaseline: 237 variable.CaptureCausetBaseline.Set(strings.ToLower(valStr), !v.IsGlobal) 238 } 239 240 return nil 241 } 242 243 func (e *SetInterlockingDirectorate) setCharset(cs, co string, isSetName bool) error { 244 var err error 245 if len(co) == 0 { 246 if co, err = charset.GetDefaultDefCauslation(cs); err != nil { 247 return err 248 } 249 } else { 250 var defCausl *charset.DefCauslation 251 if defCausl, err = defCauslate.GetDefCauslationByName(co); err != nil { 252 return err 253 } 254 if defCausl.CharsetName != cs { 255 return charset.ErrDefCauslationCharsetMismatch.GenWithStackByArgs(defCausl.Name, cs) 256 } 257 } 258 stochastikVars := e.ctx.GetStochastikVars() 259 if isSetName { 260 for _, v := range variable.SetNamesVariables { 261 if err = stochastikVars.SetSystemVar(v, cs); err != nil { 262 return errors.Trace(err) 263 } 264 } 265 return errors.Trace(stochastikVars.SetSystemVar(variable.DefCauslationConnection, co)) 266 } 267 // Set charset memex, see also https://dev.allegrosql.com/doc/refman/8.0/en/set-character-set.html. 268 for _, v := range variable.SetCharsetVariables { 269 if err = stochastikVars.SetSystemVar(v, cs); err != nil { 270 return errors.Trace(err) 271 } 272 } 273 csDb, err := stochastikVars.GlobalVarsAccessor.GetGlobalSysVar(variable.CharsetDatabase) 274 if err != nil { 275 return err 276 } 277 coDb, err := stochastikVars.GlobalVarsAccessor.GetGlobalSysVar(variable.DefCauslationDatabase) 278 if err != nil { 279 return err 280 } 281 err = stochastikVars.SetSystemVar(variable.CharacterSetConnection, csDb) 282 if err != nil { 283 return errors.Trace(err) 284 } 285 return errors.Trace(stochastikVars.SetSystemVar(variable.DefCauslationConnection, coDb)) 286 } 287 288 func (e *SetInterlockingDirectorate) getVarValue(v *memex.VarAssignment, sysVar *variable.SysVar) (value types.Causet, err error) { 289 if v.IsDefault { 290 // To set a SESSION variable to the GLOBAL value or a GLOBAL value 291 // to the compiled-in MyALLEGROSQL default value, use the DEFAULT keyword. 292 // See http://dev.allegrosql.com/doc/refman/5.7/en/set-memex.html 293 if sysVar != nil { 294 value = types.NewStringCauset(sysVar.Value) 295 } else { 296 s, err1 := variable.GetGlobalSystemVar(e.ctx.GetStochastikVars(), v.Name) 297 if err1 != nil { 298 return value, err1 299 } 300 value = types.NewStringCauset(s) 301 } 302 return 303 } 304 value, err = v.Expr.Eval(chunk.Event{}) 305 return value, err 306 } 307 308 func (e *SetInterlockingDirectorate) loadSnapshotSchemaReplicantIfNeeded(name string) error { 309 if name != variable.MilevaDBSnapshot { 310 return nil 311 } 312 vars := e.ctx.GetStochastikVars() 313 if vars.SnapshotTS == 0 { 314 vars.SnapshotschemaReplicant = nil 315 return nil 316 } 317 logutil.BgLogger().Info("load snapshot info schemaReplicant", zap.Uint64("conn", vars.ConnectionID), zap.Uint64("SnapshotTS", vars.SnapshotTS)) 318 dom := petri.GetPetri(e.ctx) 319 snapInfo, err := dom.GetSnapshotSchemaReplicant(vars.SnapshotTS) 320 if err != nil { 321 return err 322 } 323 vars.SnapshotschemaReplicant = snapInfo 324 return nil 325 }