828 lines
29 KiB
Go
828 lines
29 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,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package variable
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/pingcap/tidb/config"
|
|
"github.com/pingcap/tidb/parser/mysql"
|
|
"github.com/pingcap/tidb/parser/terror"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestSysVar(t *testing.T) {
|
|
f := GetSysVar("autocommit")
|
|
require.NotNil(t, f)
|
|
|
|
f = GetSysVar("wrong-var-name")
|
|
require.Nil(t, f)
|
|
|
|
f = GetSysVar("explicit_defaults_for_timestamp")
|
|
require.NotNil(t, f)
|
|
require.Equal(t, "ON", f.Value)
|
|
|
|
f = GetSysVar("port")
|
|
require.NotNil(t, f)
|
|
require.Equal(t, "4000", f.Value)
|
|
|
|
f = GetSysVar("tidb_low_resolution_tso")
|
|
require.Equal(t, "OFF", f.Value)
|
|
|
|
f = GetSysVar("tidb_replica_read")
|
|
require.Equal(t, "leader", f.Value)
|
|
|
|
f = GetSysVar("tidb_enable_table_partition")
|
|
require.Equal(t, "ON", f.Value)
|
|
|
|
f = GetSysVar("version_compile_os")
|
|
require.Equal(t, runtime.GOOS, f.Value)
|
|
|
|
f = GetSysVar("version_compile_machine")
|
|
require.Equal(t, runtime.GOARCH, f.Value)
|
|
}
|
|
|
|
func TestTxnMode(t *testing.T) {
|
|
seVar := NewSessionVars()
|
|
require.NotNil(t, seVar)
|
|
require.Equal(t, "", seVar.TxnMode)
|
|
err := seVar.setTxnMode("pessimistic")
|
|
require.NoError(t, err)
|
|
err = seVar.setTxnMode("optimistic")
|
|
require.NoError(t, err)
|
|
err = seVar.setTxnMode("")
|
|
require.NoError(t, err)
|
|
err = seVar.setTxnMode("something else")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestError(t *testing.T) {
|
|
kvErrs := []*terror.Error{
|
|
ErrUnsupportedValueForVar,
|
|
ErrUnknownSystemVar,
|
|
ErrIncorrectScope,
|
|
ErrUnknownTimeZone,
|
|
ErrReadOnly,
|
|
ErrWrongValueForVar,
|
|
ErrWrongTypeForVar,
|
|
ErrTruncatedWrongValue,
|
|
ErrMaxPreparedStmtCountReached,
|
|
ErrUnsupportedIsolationLevel,
|
|
}
|
|
for _, err := range kvErrs {
|
|
require.True(t, terror.ToSQLError(err).Code != mysql.ErrUnknown)
|
|
}
|
|
}
|
|
|
|
func TestRegistrationOfNewSysVar(t *testing.T) {
|
|
count := len(GetSysVars())
|
|
sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeBool, SetSession: func(s *SessionVars, val string) error {
|
|
return fmt.Errorf("set should fail")
|
|
}}
|
|
|
|
RegisterSysVar(&sv)
|
|
require.Equal(t, len(GetSysVars()), count+1)
|
|
|
|
sysVar := GetSysVar("mynewsysvar")
|
|
require.NotNil(t, sysVar)
|
|
|
|
vars := NewSessionVars()
|
|
|
|
// It is a boolean, try to set it to a bogus value
|
|
_, err := sysVar.Validate(vars, "ABCD", ScopeSession)
|
|
require.Error(t, err)
|
|
|
|
// Boolean oN or 1 converts to canonical ON or OFF
|
|
normalizedVal, err := sysVar.Validate(vars, "oN", ScopeSession)
|
|
require.Equal(t, "ON", normalizedVal)
|
|
require.NoError(t, err)
|
|
normalizedVal, err = sysVar.Validate(vars, "0", ScopeSession)
|
|
require.Equal(t, "OFF", normalizedVal)
|
|
require.NoError(t, err)
|
|
|
|
err = sysVar.SetSessionFromHook(vars, "OFF") // default is on
|
|
require.Equal(t, "set should fail", err.Error())
|
|
|
|
// Test unregistration restores previous count
|
|
UnregisterSysVar("mynewsysvar")
|
|
require.Equal(t, len(GetSysVars()), count)
|
|
}
|
|
|
|
func TestIntValidation(t *testing.T) {
|
|
sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "123", Type: TypeInt, MinValue: 10, MaxValue: 300, AllowAutoValue: true}
|
|
vars := NewSessionVars()
|
|
|
|
_, err := sv.Validate(vars, "oN", ScopeSession)
|
|
require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error())
|
|
|
|
val, err := sv.Validate(vars, "301", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "300", val)
|
|
|
|
val, err = sv.Validate(vars, "5", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "10", val)
|
|
|
|
val, err = sv.Validate(vars, "300", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "300", val)
|
|
|
|
// out of range but permitted due to auto value
|
|
val, err = sv.Validate(vars, "-1", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "-1", val)
|
|
}
|
|
|
|
func TestUintValidation(t *testing.T) {
|
|
sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: "123", Type: TypeUnsigned, MinValue: 10, MaxValue: 300, AllowAutoValue: true}
|
|
vars := NewSessionVars()
|
|
|
|
_, err := sv.Validate(vars, "oN", ScopeSession)
|
|
require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error())
|
|
|
|
_, err = sv.Validate(vars, "", ScopeSession)
|
|
require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error())
|
|
|
|
val, err := sv.Validate(vars, "301", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "300", val)
|
|
|
|
val, err = sv.Validate(vars, "-301", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "10", val)
|
|
|
|
_, err = sv.Validate(vars, "-ERR", ScopeSession)
|
|
require.Equal(t, "[variable:1232]Incorrect argument type to variable 'mynewsysvar'", err.Error())
|
|
|
|
val, err = sv.Validate(vars, "5", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "10", val)
|
|
|
|
val, err = sv.Validate(vars, "300", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "300", val)
|
|
|
|
// out of range but permitted due to auto value
|
|
val, err = sv.Validate(vars, "-1", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "-1", val)
|
|
}
|
|
|
|
func TestEnumValidation(t *testing.T) {
|
|
sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}}
|
|
vars := NewSessionVars()
|
|
|
|
_, err := sv.Validate(vars, "randomstring", ScopeSession)
|
|
require.Equal(t, "[variable:1231]Variable 'mynewsysvar' can't be set to the value of 'randomstring'", err.Error())
|
|
|
|
val, err := sv.Validate(vars, "oFf", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "OFF", val)
|
|
|
|
val, err = sv.Validate(vars, "On", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ON", val)
|
|
|
|
val, err = sv.Validate(vars, "auto", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "AUTO", val)
|
|
|
|
// Also settable by numeric offset.
|
|
val, err = sv.Validate(vars, "2", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "AUTO", val)
|
|
}
|
|
|
|
func TestSynonyms(t *testing.T) {
|
|
sysVar := GetSysVar(TxnIsolation)
|
|
require.NotNil(t, sysVar)
|
|
|
|
vars := NewSessionVars()
|
|
|
|
// It does not permit SERIALIZABLE by default.
|
|
_, err := sysVar.Validate(vars, "SERIALIZABLE", ScopeSession)
|
|
require.Error(t, err)
|
|
require.Equal(t, "[variable:8048]The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", err.Error())
|
|
|
|
// Enable Skip isolation check
|
|
require.Nil(t, GetSysVar(TiDBSkipIsolationLevelCheck).SetSessionFromHook(vars, "ON"))
|
|
|
|
// Serializable is now permitted.
|
|
_, err = sysVar.Validate(vars, "SERIALIZABLE", ScopeSession)
|
|
require.NoError(t, err)
|
|
|
|
// Currently TiDB returns a warning because of SERIALIZABLE, but in future
|
|
// it may also return a warning because TxnIsolation is deprecated.
|
|
|
|
warn := vars.StmtCtx.GetWarnings()[0].Err
|
|
require.Equal(t, "[variable:8048]The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", warn.Error())
|
|
|
|
require.Nil(t, sysVar.SetSessionFromHook(vars, "SERIALIZABLE"))
|
|
|
|
// When we set TxnIsolation, it also updates TransactionIsolation.
|
|
require.Equal(t, "SERIALIZABLE", vars.systems[TxnIsolation])
|
|
require.Equal(t, vars.systems[TxnIsolation], vars.systems[TransactionIsolation])
|
|
}
|
|
|
|
func TestDeprecation(t *testing.T) {
|
|
sysVar := GetSysVar(TiDBIndexLookupConcurrency)
|
|
require.NotNil(t, sysVar)
|
|
|
|
vars := NewSessionVars()
|
|
|
|
_, err := sysVar.Validate(vars, "123", ScopeSession)
|
|
require.NoError(t, err)
|
|
|
|
// There was no error but there is a deprecation warning.
|
|
warn := vars.StmtCtx.GetWarnings()[0].Err
|
|
require.Equal(t, "[variable:1287]'tidb_index_lookup_concurrency' is deprecated and will be removed in a future release. Please use tidb_executor_concurrency instead", warn.Error())
|
|
}
|
|
|
|
func TestScope(t *testing.T) {
|
|
sv := SysVar{Scope: ScopeGlobal | ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}}
|
|
require.True(t, sv.HasSessionScope())
|
|
require.True(t, sv.HasGlobalScope())
|
|
require.False(t, sv.HasNoneScope())
|
|
|
|
sv = SysVar{Scope: ScopeGlobal, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}}
|
|
require.False(t, sv.HasSessionScope())
|
|
require.True(t, sv.HasGlobalScope())
|
|
require.False(t, sv.HasNoneScope())
|
|
|
|
sv = SysVar{Scope: ScopeSession, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}}
|
|
require.True(t, sv.HasSessionScope())
|
|
require.False(t, sv.HasGlobalScope())
|
|
require.False(t, sv.HasNoneScope())
|
|
|
|
sv = SysVar{Scope: ScopeNone, Name: "mynewsysvar", Value: On, Type: TypeEnum, PossibleValues: []string{"OFF", "ON", "AUTO"}}
|
|
require.False(t, sv.HasSessionScope())
|
|
require.False(t, sv.HasGlobalScope())
|
|
require.True(t, sv.HasNoneScope())
|
|
}
|
|
|
|
func TestBuiltInCase(t *testing.T) {
|
|
// All Sysvars should have lower case names.
|
|
// This tests builtins.
|
|
for name := range GetSysVars() {
|
|
require.Equal(t, strings.ToLower(name), name)
|
|
}
|
|
}
|
|
|
|
func TestSQLSelectLimit(t *testing.T) {
|
|
sv := GetSysVar(SQLSelectLimit)
|
|
vars := NewSessionVars()
|
|
val, err := sv.Validate(vars, "-10", ScopeSession)
|
|
require.NoError(t, err) // it has autoconvert out of range.
|
|
require.Equal(t, "0", val)
|
|
|
|
val, err = sv.Validate(vars, "9999", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "9999", val)
|
|
|
|
require.Nil(t, sv.SetSessionFromHook(vars, "9999")) // sets
|
|
require.Equal(t, uint64(9999), vars.SelectLimit)
|
|
}
|
|
|
|
func TestSQLModeVar(t *testing.T) {
|
|
sv := GetSysVar(SQLModeVar)
|
|
vars := NewSessionVars()
|
|
val, err := sv.Validate(vars, "strict_trans_tabLES ", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "STRICT_TRANS_TABLES", val)
|
|
|
|
_, err = sv.Validate(vars, "strict_trans_tabLES,nonsense_option", ScopeSession)
|
|
require.Equal(t, "ERROR 1231 (42000): Variable 'sql_mode' can't be set to the value of 'NONSENSE_OPTION'", err.Error())
|
|
|
|
val, err = sv.Validate(vars, "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION", val)
|
|
|
|
require.Nil(t, sv.SetSessionFromHook(vars, val)) // sets to strict from above
|
|
require.True(t, vars.StrictSQLMode)
|
|
|
|
sqlMode, err := mysql.GetSQLMode(val)
|
|
require.NoError(t, err)
|
|
require.Equal(t, sqlMode, vars.SQLMode)
|
|
|
|
// Set it to non strict.
|
|
val, err = sv.Validate(vars, "ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION", val)
|
|
|
|
require.Nil(t, sv.SetSessionFromHook(vars, val)) // sets to non-strict from above
|
|
require.False(t, vars.StrictSQLMode)
|
|
sqlMode, err = mysql.GetSQLMode(val)
|
|
require.NoError(t, err)
|
|
require.Equal(t, sqlMode, vars.SQLMode)
|
|
}
|
|
|
|
func TestMaxExecutionTime(t *testing.T) {
|
|
sv := GetSysVar(MaxExecutionTime)
|
|
vars := NewSessionVars()
|
|
|
|
val, err := sv.Validate(vars, "-10", ScopeSession)
|
|
require.NoError(t, err) // it has autoconvert out of range.
|
|
require.Equal(t, "0", val)
|
|
|
|
val, err = sv.Validate(vars, "99999", ScopeSession)
|
|
require.NoError(t, err) // it has autoconvert out of range.
|
|
require.Equal(t, "99999", val)
|
|
|
|
require.Nil(t, sv.SetSessionFromHook(vars, "99999")) // sets
|
|
require.Equal(t, uint64(99999), vars.MaxExecutionTime)
|
|
}
|
|
|
|
func TestCollationServer(t *testing.T) {
|
|
sv := GetSysVar(CollationServer)
|
|
vars := NewSessionVars()
|
|
|
|
val, err := sv.Validate(vars, "LATIN1_bin", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "latin1_bin", val) // test normalization
|
|
|
|
_, err = sv.Validate(vars, "BOGUSCOLLation", ScopeSession)
|
|
require.Equal(t, "[ddl:1273]Unknown collation: 'BOGUSCOLLation'", err.Error())
|
|
|
|
require.Nil(t, sv.SetSessionFromHook(vars, "latin1_bin"))
|
|
require.Equal(t, "latin1", vars.systems[CharacterSetServer]) // check it also changes charset.
|
|
|
|
require.Nil(t, sv.SetSessionFromHook(vars, "utf8mb4_bin"))
|
|
require.Equal(t, "utf8mb4", vars.systems[CharacterSetServer]) // check it also changes charset.
|
|
}
|
|
|
|
func TestTimeZone(t *testing.T) {
|
|
sv := GetSysVar(TimeZone)
|
|
vars := NewSessionVars()
|
|
|
|
// TiDB uses the Golang TZ library, so TZs are case-sensitive.
|
|
// Unfortunately this is not strictly MySQL compatible. i.e.
|
|
// This should not fail:
|
|
// val, err := sv.Validate(vars, "America/EDMONTON", ScopeSession)
|
|
// See: https://github.com/pingcap/tidb/issues/8087
|
|
|
|
val, err := sv.Validate(vars, "America/Edmonton", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "America/Edmonton", val)
|
|
|
|
val, err = sv.Validate(vars, "+10:00", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "+10:00", val)
|
|
|
|
val, err = sv.Validate(vars, "UTC", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "UTC", val)
|
|
|
|
val, err = sv.Validate(vars, "+00:00", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "+00:00", val)
|
|
|
|
require.Nil(t, sv.SetSessionFromHook(vars, "UTC")) // sets
|
|
tz, err := parseTimeZone("UTC")
|
|
require.NoError(t, err)
|
|
require.Equal(t, tz, vars.TimeZone)
|
|
|
|
}
|
|
|
|
func TestForeignKeyChecks(t *testing.T) {
|
|
sv := GetSysVar(ForeignKeyChecks)
|
|
vars := NewSessionVars()
|
|
|
|
val, err := sv.Validate(vars, "on", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "OFF", val) // warns and refuses to set ON.
|
|
|
|
warn := vars.StmtCtx.GetWarnings()[0].Err
|
|
require.Equal(t, "[variable:8047]variable 'foreign_key_checks' does not yet support value: on", warn.Error())
|
|
|
|
}
|
|
|
|
func TestTxnIsolation(t *testing.T) {
|
|
sv := GetSysVar(TxnIsolation)
|
|
vars := NewSessionVars()
|
|
|
|
_, err := sv.Validate(vars, "on", ScopeSession)
|
|
require.Equal(t, "[variable:1231]Variable 'tx_isolation' can't be set to the value of 'on'", err.Error())
|
|
|
|
val, err := sv.Validate(vars, "read-COMMitted", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "READ-COMMITTED", val)
|
|
|
|
_, err = sv.Validate(vars, "Serializable", ScopeSession)
|
|
require.Equal(t, "[variable:8048]The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", err.Error())
|
|
|
|
_, err = sv.Validate(vars, "read-uncommitted", ScopeSession)
|
|
require.Equal(t, "[variable:8048]The isolation level 'READ-UNCOMMITTED' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", err.Error())
|
|
|
|
// Enable global skip isolation check doesn't affect current session
|
|
require.Nil(t, GetSysVar(TiDBSkipIsolationLevelCheck).SetGlobalFromHook(vars, "ON", true))
|
|
_, err = sv.Validate(vars, "Serializable", ScopeSession)
|
|
require.Equal(t, "[variable:8048]The isolation level 'SERIALIZABLE' is not supported. Set tidb_skip_isolation_level_check=1 to skip this error", err.Error())
|
|
|
|
// Enable session skip isolation check
|
|
require.Nil(t, GetSysVar(TiDBSkipIsolationLevelCheck).SetSessionFromHook(vars, "ON"))
|
|
|
|
val, err = sv.Validate(vars, "Serializable", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "SERIALIZABLE", val)
|
|
|
|
// Init TiDBSkipIsolationLevelCheck like what loadCommonGlobalVariables does
|
|
vars = NewSessionVars()
|
|
require.NoError(t, vars.SetSystemVarWithRelaxedValidation(TiDBSkipIsolationLevelCheck, "1"))
|
|
val, err = sv.Validate(vars, "Serializable", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "SERIALIZABLE", val)
|
|
}
|
|
|
|
func TestTiDBMultiStatementMode(t *testing.T) {
|
|
sv := GetSysVar(TiDBMultiStatementMode)
|
|
vars := NewSessionVars()
|
|
|
|
val, err := sv.Validate(vars, "on", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ON", val)
|
|
require.Nil(t, sv.SetSessionFromHook(vars, val))
|
|
require.Equal(t, 1, vars.MultiStatementMode)
|
|
|
|
val, err = sv.Validate(vars, "0", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "OFF", val)
|
|
require.Nil(t, sv.SetSessionFromHook(vars, val))
|
|
require.Equal(t, 0, vars.MultiStatementMode)
|
|
|
|
val, err = sv.Validate(vars, "Warn", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "WARN", val)
|
|
require.Nil(t, sv.SetSessionFromHook(vars, val))
|
|
require.Equal(t, 2, vars.MultiStatementMode)
|
|
}
|
|
|
|
func TestReadOnlyNoop(t *testing.T) {
|
|
vars := NewSessionVars()
|
|
mock := NewMockGlobalAccessor4Tests()
|
|
mock.SessionVars = vars
|
|
vars.GlobalVarsAccessor = mock
|
|
noopFuncs := GetSysVar(TiDBEnableNoopFuncs)
|
|
|
|
// For session scope
|
|
for _, name := range []string{TxReadOnly, TransactionReadOnly} {
|
|
sv := GetSysVar(name)
|
|
val, err := sv.Validate(vars, "on", ScopeSession)
|
|
require.Equal(t, "[variable:1235]function READ ONLY has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions", err.Error())
|
|
require.Equal(t, "OFF", val)
|
|
|
|
require.NoError(t, noopFuncs.SetSessionFromHook(vars, "ON"))
|
|
_, err = sv.Validate(vars, "on", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.NoError(t, noopFuncs.SetSessionFromHook(vars, "OFF")) // restore default.
|
|
}
|
|
|
|
// For global scope
|
|
for _, name := range []string{TxReadOnly, TransactionReadOnly, OfflineMode, SuperReadOnly, ReadOnly} {
|
|
sv := GetSysVar(name)
|
|
val, err := sv.Validate(vars, "on", ScopeGlobal)
|
|
if name == OfflineMode {
|
|
require.Equal(t, "[variable:1235]function OFFLINE MODE has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions", err.Error())
|
|
} else {
|
|
require.Equal(t, "[variable:1235]function READ ONLY has only noop implementation in tidb now, use tidb_enable_noop_functions to enable these functions", err.Error())
|
|
}
|
|
require.Equal(t, "OFF", val)
|
|
require.NoError(t, vars.GlobalVarsAccessor.SetGlobalSysVar(TiDBEnableNoopFuncs, "ON"))
|
|
_, err = sv.Validate(vars, "on", ScopeGlobal)
|
|
require.NoError(t, err)
|
|
require.NoError(t, vars.GlobalVarsAccessor.SetGlobalSysVar(TiDBEnableNoopFuncs, "OFF"))
|
|
}
|
|
}
|
|
|
|
func TestSkipInit(t *testing.T) {
|
|
sv := SysVar{Scope: ScopeGlobal, Name: "skipinit1", Value: On, Type: TypeBool}
|
|
require.True(t, sv.SkipInit())
|
|
|
|
sv = SysVar{Scope: ScopeGlobal | ScopeSession, Name: "skipinit1", Value: On, Type: TypeBool}
|
|
require.False(t, sv.SkipInit())
|
|
|
|
sv = SysVar{Scope: ScopeSession, Name: "skipinit1", Value: On, Type: TypeBool}
|
|
require.False(t, sv.SkipInit())
|
|
|
|
sv = SysVar{Scope: ScopeSession, Name: "skipinit1", Value: On, Type: TypeBool, skipInit: true}
|
|
require.True(t, sv.SkipInit())
|
|
}
|
|
|
|
// TestIsNoop is used by the documentation to auto-generate docs for real sysvars.
|
|
func TestIsNoop(t *testing.T) {
|
|
sv := GetSysVar(TiDBMultiStatementMode)
|
|
require.False(t, sv.IsNoop)
|
|
|
|
sv = GetSysVar(InnodbLockWaitTimeout)
|
|
require.False(t, sv.IsNoop)
|
|
|
|
sv = GetSysVar(InnodbFastShutdown)
|
|
require.True(t, sv.IsNoop)
|
|
|
|
sv = GetSysVar(ReadOnly)
|
|
require.True(t, sv.IsNoop)
|
|
}
|
|
|
|
func TestInstanceScopedVars(t *testing.T) {
|
|
// This tests instance scoped variables through GetSessionOrGlobalSystemVar().
|
|
// Eventually these should be changed to use getters so that the switch
|
|
// statement in GetSessionOnlySysVars can be removed.
|
|
|
|
vars := NewSessionVars()
|
|
|
|
val, err := GetSessionOrGlobalSystemVar(vars, TiDBCurrentTS)
|
|
require.NoError(t, err)
|
|
require.Equal(t, fmt.Sprintf("%d", vars.TxnCtx.StartTS), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBLastTxnInfo)
|
|
require.NoError(t, err)
|
|
require.Equal(t, vars.LastTxnInfo, val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBLastQueryInfo)
|
|
require.NoError(t, err)
|
|
info, err := json.Marshal(vars.LastQueryInfo)
|
|
require.NoError(t, err)
|
|
require.Equal(t, string(info), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBGeneralLog)
|
|
require.NoError(t, err)
|
|
require.Equal(t, BoolToOnOff(ProcessGeneralLog.Load()), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBPProfSQLCPU)
|
|
require.NoError(t, err)
|
|
expected := "0"
|
|
if EnablePProfSQLCPU.Load() {
|
|
expected = "1"
|
|
}
|
|
require.Equal(t, expected, val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBExpensiveQueryTimeThreshold)
|
|
require.NoError(t, err)
|
|
require.Equal(t, fmt.Sprintf("%d", atomic.LoadUint64(&ExpensiveQueryTimeThreshold)), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBMemoryUsageAlarmRatio)
|
|
require.NoError(t, err)
|
|
require.Equal(t, fmt.Sprintf("%g", MemoryUsageAlarmRatio.Load()), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBForcePriority)
|
|
require.NoError(t, err)
|
|
require.Equal(t, mysql.Priority2Str[mysql.PriorityEnum(atomic.LoadInt32(&ForcePriority))], val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBDDLSlowOprThreshold)
|
|
require.NoError(t, err)
|
|
require.Equal(t, strconv.FormatUint(uint64(atomic.LoadUint32(&DDLSlowOprThreshold)), 10), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, PluginDir)
|
|
require.NoError(t, err)
|
|
require.Equal(t, config.GetGlobalConfig().Plugin.Dir, val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, PluginLoad)
|
|
require.NoError(t, err)
|
|
require.Equal(t, config.GetGlobalConfig().Plugin.Load, val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBSlowLogThreshold)
|
|
require.NoError(t, err)
|
|
require.Equal(t, strconv.FormatUint(atomic.LoadUint64(&config.GetGlobalConfig().Log.SlowThreshold), 10), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBRecordPlanInSlowLog)
|
|
require.NoError(t, err)
|
|
enabled := atomic.LoadUint32(&config.GetGlobalConfig().Log.RecordPlanInSlowLog) == 1
|
|
require.Equal(t, BoolToOnOff(enabled), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBEnableSlowLog)
|
|
require.NoError(t, err)
|
|
require.Equal(t, BoolToOnOff(config.GetGlobalConfig().Log.EnableSlowLog.Load()), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBQueryLogMaxLen)
|
|
require.NoError(t, err)
|
|
require.Equal(t, strconv.FormatUint(atomic.LoadUint64(&config.GetGlobalConfig().Log.QueryLogMaxLen), 10), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBCheckMb4ValueInUTF8)
|
|
require.NoError(t, err)
|
|
require.Equal(t, BoolToOnOff(config.GetGlobalConfig().CheckMb4ValueInUTF8), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBCapturePlanBaseline)
|
|
require.NoError(t, err)
|
|
require.Equal(t, CapturePlanBaseline.GetVal(), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBFoundInPlanCache)
|
|
require.NoError(t, err)
|
|
require.Equal(t, BoolToOnOff(vars.PrevFoundInPlanCache), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBFoundInBinding)
|
|
require.NoError(t, err)
|
|
require.Equal(t, BoolToOnOff(vars.PrevFoundInBinding), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBEnableCollectExecutionInfo)
|
|
require.NoError(t, err)
|
|
require.Equal(t, BoolToOnOff(config.GetGlobalConfig().EnableCollectExecutionInfo), val)
|
|
|
|
val, err = GetSessionOrGlobalSystemVar(vars, TiDBTxnScope)
|
|
require.NoError(t, err)
|
|
require.Equal(t, vars.TxnScope.GetVarValue(), val)
|
|
}
|
|
|
|
// TestDefaultValuesAreSettable that sysvars defaults are logically valid. i.e.
|
|
// the default itself must validate without error provided the scope and read-only is correct.
|
|
// The default values should also be normalized for consistency.
|
|
func TestDefaultValuesAreSettable(t *testing.T) {
|
|
vars := NewSessionVars()
|
|
vars.GlobalVarsAccessor = NewMockGlobalAccessor4Tests()
|
|
for _, sv := range GetSysVars() {
|
|
if sv.HasSessionScope() && !sv.ReadOnly {
|
|
val, err := sv.Validate(vars, sv.Value, ScopeSession)
|
|
require.Equal(t, val, sv.Value)
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
if sv.HasGlobalScope() && !sv.ReadOnly {
|
|
val, err := sv.Validate(vars, sv.Value, ScopeGlobal)
|
|
require.Equal(t, val, sv.Value)
|
|
require.NoError(t, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// TestSettersandGetters tests that sysvars are logically correct with getter and setter functions.
|
|
// i.e. it doesn't make sense to have a SetSession function on a variable that is only globally scoped.
|
|
func TestSettersandGetters(t *testing.T) {
|
|
for _, sv := range GetSysVars() {
|
|
if !sv.HasSessionScope() {
|
|
// There are some historial exceptions where global variables are loaded into the session.
|
|
// Please don't add to this list, the behavior is not MySQL compatible.
|
|
switch sv.Name {
|
|
case TiDBEnableChangeMultiSchema, TiDBDDLReorgBatchSize, TiDBEnableAlterPlacement,
|
|
TiDBMaxDeltaSchemaCount, InitConnect, MaxPreparedStmtCount,
|
|
TiDBDDLReorgWorkerCount, TiDBDDLErrorCountLimit, TiDBRowFormatVersion,
|
|
TiDBEnableTelemetry, TiDBEnablePointGetCache:
|
|
continue
|
|
}
|
|
require.Nil(t, sv.SetSession)
|
|
require.Nil(t, sv.GetSession)
|
|
}
|
|
if !sv.HasGlobalScope() {
|
|
require.Nil(t, sv.SetGlobal)
|
|
if sv.Name == Timestamp {
|
|
// The Timestamp sysvar will have GetGlobal func even though it does not have global scope.
|
|
// It's GetGlobal func will only be called when "set timestamp = default".
|
|
continue
|
|
}
|
|
require.Nil(t, sv.GetGlobal)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSecureAuth(t *testing.T) {
|
|
sv := GetSysVar(SecureAuth)
|
|
vars := NewSessionVars()
|
|
_, err := sv.Validate(vars, "OFF", ScopeGlobal)
|
|
require.Equal(t, "[variable:1231]Variable 'secure_auth' can't be set to the value of 'OFF'", err.Error())
|
|
val, err := sv.Validate(vars, "ON", ScopeGlobal)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "ON", val)
|
|
}
|
|
|
|
func TestValidateWithRelaxedValidation(t *testing.T) {
|
|
sv := GetSysVar(SecureAuth)
|
|
vars := NewSessionVars()
|
|
val := sv.ValidateWithRelaxedValidation(vars, "1", ScopeGlobal)
|
|
require.Equal(t, "ON", val)
|
|
}
|
|
|
|
func TestTiDBReplicaRead(t *testing.T) {
|
|
sv := GetSysVar(TiDBReplicaRead)
|
|
vars := NewSessionVars()
|
|
val, err := sv.Validate(vars, "follower", ScopeGlobal)
|
|
require.Equal(t, val, "follower")
|
|
require.NoError(t, err)
|
|
}
|
|
|
|
func TestSQLAutoIsNull(t *testing.T) {
|
|
svSQL, svNoop := GetSysVar(SQLAutoIsNull), GetSysVar(TiDBEnableNoopFuncs)
|
|
vars := NewSessionVars()
|
|
vars.GlobalVarsAccessor = NewMockGlobalAccessor4Tests()
|
|
_, err := svSQL.Validate(vars, "ON", ScopeSession)
|
|
require.True(t, terror.ErrorEqual(err, ErrFunctionsNoopImpl))
|
|
// change tidb_enable_noop_functions to 1, it will success
|
|
require.NoError(t, svNoop.SetSessionFromHook(vars, "ON"))
|
|
_, err = svSQL.Validate(vars, "ON", ScopeSession)
|
|
require.NoError(t, err)
|
|
require.NoError(t, svSQL.SetSessionFromHook(vars, "ON"))
|
|
res, ok := vars.GetSystemVar(SQLAutoIsNull)
|
|
require.True(t, ok)
|
|
require.Equal(t, "ON", res)
|
|
// restore tidb_enable_noop_functions to 0 failed, as sql_auto_is_null is 1
|
|
_, err = svNoop.Validate(vars, "OFF", ScopeSession)
|
|
require.True(t, terror.ErrorEqual(err, errValueNotSupportedWhen))
|
|
// after set sql_auto_is_null to 0, restore success
|
|
require.NoError(t, svSQL.SetSessionFromHook(vars, "OFF"))
|
|
require.NoError(t, svNoop.SetSessionFromHook(vars, "OFF"))
|
|
|
|
// Only test validate as MockGlobalAccessor do not support SetGlobalSysVar
|
|
_, err = svSQL.Validate(vars, "ON", ScopeGlobal)
|
|
require.True(t, terror.ErrorEqual(err, ErrFunctionsNoopImpl))
|
|
}
|
|
|
|
func TestLastInsertID(t *testing.T) {
|
|
vars := NewSessionVars()
|
|
val, err := GetSessionOrGlobalSystemVar(vars, LastInsertID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, "0")
|
|
|
|
vars.StmtCtx.PrevLastInsertID = 21
|
|
val, err = GetSessionOrGlobalSystemVar(vars, LastInsertID)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, "21")
|
|
}
|
|
|
|
func TestTimestamp(t *testing.T) {
|
|
vars := NewSessionVars()
|
|
val, err := GetSessionOrGlobalSystemVar(vars, Timestamp)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, "", val)
|
|
|
|
vars.systems[Timestamp] = "10"
|
|
val, err = GetSessionOrGlobalSystemVar(vars, Timestamp)
|
|
require.NoError(t, err)
|
|
require.Equal(t, "10", val)
|
|
|
|
vars.systems[Timestamp] = "0" // set to default
|
|
val, err = GetSessionOrGlobalSystemVar(vars, Timestamp)
|
|
require.NoError(t, err)
|
|
require.NotEqual(t, "", val)
|
|
require.NotEqual(t, "10", val)
|
|
}
|
|
|
|
func TestIdentity(t *testing.T) {
|
|
vars := NewSessionVars()
|
|
val, err := GetSessionOrGlobalSystemVar(vars, Identity)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, "0")
|
|
|
|
vars.StmtCtx.PrevLastInsertID = 21
|
|
val, err = GetSessionOrGlobalSystemVar(vars, Identity)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, "21")
|
|
}
|
|
|
|
func TestLcTimeNamesReadOnly(t *testing.T) {
|
|
sv := GetSysVar("lc_time_names")
|
|
vars := NewSessionVars()
|
|
vars.GlobalVarsAccessor = NewMockGlobalAccessor4Tests()
|
|
_, err := sv.Validate(vars, "newvalue", ScopeGlobal)
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestDDLWorkers(t *testing.T) {
|
|
svWorkerCount, svBatchSize := GetSysVar(TiDBDDLReorgWorkerCount), GetSysVar(TiDBDDLReorgBatchSize)
|
|
vars := NewSessionVars()
|
|
vars.GlobalVarsAccessor = NewMockGlobalAccessor4Tests()
|
|
|
|
val, err := svWorkerCount.Validate(vars, "-100", ScopeGlobal)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, "1") // converts it to min value
|
|
val, err = svWorkerCount.Validate(vars, "1234", ScopeGlobal)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, "256") // converts it to max value
|
|
val, err = svWorkerCount.Validate(vars, "100", ScopeGlobal)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, "100") // unchanged
|
|
|
|
val, err = svBatchSize.Validate(vars, "10", ScopeGlobal)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, fmt.Sprint(MinDDLReorgBatchSize)) // converts it to min value
|
|
val, err = svBatchSize.Validate(vars, "999999", ScopeGlobal)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, fmt.Sprint(MaxDDLReorgBatchSize)) // converts it to max value
|
|
val, err = svBatchSize.Validate(vars, "100", ScopeGlobal)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, "100") // unchanged
|
|
}
|
|
|
|
func TestDefaultCharsetAndCollation(t *testing.T) {
|
|
vars := NewSessionVars()
|
|
val, err := GetSessionOrGlobalSystemVar(vars, CharacterSetConnection)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, mysql.DefaultCharset)
|
|
val, err = GetSessionOrGlobalSystemVar(vars, CollationConnection)
|
|
require.NoError(t, err)
|
|
require.Equal(t, val, mysql.DefaultCollationName)
|
|
}
|