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  }