Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

*: better handle sysvar upgrades from older versions #31583

Merged
merged 11 commits into from
Mar 10, 2022
4 changes: 2 additions & 2 deletions expression/integration_serial_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3851,8 +3851,8 @@ func TestSetVariables(t *testing.T) {
tk.MustExec("set global tidb_enable_noop_functions=1")

_, err = tk.Exec("set @@global.max_user_connections='';")
require.NoError(t, err)
//require.Error(t, err, variable.ErrWrongTypeForVar.GenWithStackByArgs("max_user_connections").Error())
require.Error(t, err)
require.Error(t, err, variable.ErrWrongTypeForVar.GenWithStackByArgs("max_user_connections").Error())
Comment on lines +3738 to +3739
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So it's still a compatibility breaker? I'll add a label.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure. We can add the label, but it is not very likely since this was only introduced in #31566 (5.4) - This PR reverts the previous PR with one that handles noops and other sysvars.

_, err = tk.Exec("set @@global.max_prepared_stmt_count='';")
require.Error(t, err)
require.Error(t, err, variable.ErrWrongTypeForVar.GenWithStackByArgs("max_prepared_stmt_count").Error())
Expand Down
8 changes: 7 additions & 1 deletion session/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -1239,7 +1239,13 @@ func (s *session) GetGlobalSysVar(name string) (string, error) {
}
// It might have been written from an earlier TiDB version, so we should do type validation
// See https://github.com/pingcap/tidb/issues/30255 for why we don't do full validation.
return sv.ValidateFromType(s.GetSessionVars(), sysVar, variable.ScopeGlobal)
// If validation fails, we should return the default value:
// See: https://github.com/pingcap/tidb/pull/31566
sysVar, err = sv.ValidateFromType(s.GetSessionVars(), sysVar, variable.ScopeGlobal)
if err != nil {
return sv.Value, nil
}
return sysVar, nil
}

// SetGlobalSysVar implements GlobalVarAccessor.SetGlobalSysVar interface.
Expand Down
40 changes: 39 additions & 1 deletion session/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -691,15 +691,53 @@ func (s *testSessionSuite) TestGlobalVarAccessor(c *C) {
_, err = tk.Exec("set global time_zone = 'timezone'")
c.Assert(err, NotNil)
c.Assert(terror.ErrorEqual(err, variable.ErrUnknownTimeZone), IsTrue)
}

func (s *testSessionSuite) TestUpgradeSysvars(c *C) {

morgo marked this conversation as resolved.
Show resolved Hide resolved
tk := testkit.NewTestKitWithInit(c, s.store)
se := tk.Se.(variable.GlobalVarAccessor)

// Set the global var to a non canonical form of the value
// i.e. implying that it was set from an earlier version of TiDB.

tk.MustExec(`REPLACE INTO mysql.global_variables (variable_name, variable_value) VALUES ('tidb_enable_noop_functions', '0')`)
domain.GetDomain(tk.Se).NotifyUpdateSysVarCache() // update cache
v, err = se.GetGlobalSysVar("tidb_enable_noop_functions")
v, err := se.GetGlobalSysVar("tidb_enable_noop_functions")
c.Assert(err, IsNil)
c.Assert(v, Equals, "OFF")

// Set the global var to "" which is the invalid version of this from TiDB 4.0.16
// the err is quashed by the GetGlobalSysVar, and the default value is restored.
// This helps callers of GetGlobalSysVar(), which can't individually be expected
// to handle upgrade/downgrade issues correctly.

tk.MustExec(`REPLACE INTO mysql.global_variables (variable_name, variable_value) VALUES ('rpl_semi_sync_slave_enabled', '')`)
domain.GetDomain(tk.Se).NotifyUpdateSysVarCache() // update cache
v, err = se.GetGlobalSysVar("rpl_semi_sync_slave_enabled")
c.Assert(err, IsNil)
c.Assert(v, Equals, "OFF") // the default value is restored.
result := tk.MustQuery("SHOW VARIABLES LIKE 'rpl_semi_sync_slave_enabled'")
result.Check(testkit.Rows("rpl_semi_sync_slave_enabled OFF"))

// Ensure variable out of range is converted to in range after upgrade.
// This further helps for https://github.com/pingcap/tidb/pull/28842

tk.MustExec(`REPLACE INTO mysql.global_variables (variable_name, variable_value) VALUES ('tidb_executor_concurrency', '999')`)
domain.GetDomain(tk.Se).NotifyUpdateSysVarCache() // update cache
v, err = se.GetGlobalSysVar("tidb_executor_concurrency")
c.Assert(err, IsNil)
c.Assert(v, Equals, "256") // the max value is restored.

// Handle the case of a completely bogus value from an earlier version of TiDB.
// This could be the case if an ENUM sysvar removes a value.

tk.MustExec(`REPLACE INTO mysql.global_variables (variable_name, variable_value) VALUES ('tidb_enable_noop_functions', 'SOMEVAL')`)
domain.GetDomain(tk.Se).NotifyUpdateSysVarCache() // update cache
v, err = se.GetGlobalSysVar("tidb_enable_noop_functions")
c.Assert(err, IsNil)
c.Assert(v, Equals, "OFF") // the default value is restored.

}

func (s *testSessionSuite) TestMatchIdentity(c *C) {
Expand Down
13 changes: 0 additions & 13 deletions sessionctx/variable/sysvar_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -834,16 +834,3 @@ func TestIndexMergeSwitcher(t *testing.T) {
require.Equal(t, DefTiDBEnableIndexMerge, true)
require.Equal(t, BoolToOnOff(DefTiDBEnableIndexMerge), val)
}

func TestNoValidateForNoop(t *testing.T) {
vars := NewSessionVars()

// for noop variables, no error
val, err := GetSysVar("rpl_semi_sync_slave_enabled").ValidateFromType(vars, "", ScopeGlobal)
require.NoError(t, err)
require.Equal(t, val, "")

// for other variables, error
_, err = GetSysVar(TiDBAllowBatchCop).ValidateFromType(vars, "", ScopeGlobal)
require.Error(t, err)
}
4 changes: 0 additions & 4 deletions sessionctx/variable/variable.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,10 +261,6 @@ func (sv *SysVar) Validate(vars *SessionVars, value string, scope ScopeFlag) (st

// ValidateFromType provides automatic validation based on the SysVar's type
func (sv *SysVar) ValidateFromType(vars *SessionVars, value string, scope ScopeFlag) (string, error) {
// TODO: this is a temporary solution for issue: https://github.com/pingcap/tidb/issues/31538, an elegant solution is needed.
if value == "" && sv.IsNoop {
return value, nil
}
// Some sysvars in TiDB have a special behavior where the empty string means
// "use the config file value". This needs to be cleaned up once the behavior
// for instance variables is determined.
Expand Down