Skip to content

Commit

Permalink
*: add global system variable tmp_table_size (pingcap#24827)
Browse files Browse the repository at this point in the history
  • Loading branch information
tiancaiamao committed Jun 23, 2021
1 parent 9bff3ea commit 727a217
Show file tree
Hide file tree
Showing 11 changed files with 106 additions and 9 deletions.
5 changes: 5 additions & 0 deletions errors.toml
Original file line number Diff line number Diff line change
Expand Up @@ -1386,6 +1386,11 @@ error = '''
Unknown column '%-.192s' in '%-.192s'
'''

["table:1114"]
error = '''
The table '%-.192s' is full
'''

["table:1192"]
error = '''
Can't execute the given command because you have active locked tables or an active transaction
Expand Down
4 changes: 2 additions & 2 deletions executor/set_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -884,9 +884,9 @@ func (s *testSuite5) TestValidateSetVar(c *C) {
result = tk.MustQuery("select @@tmp_table_size;")
result.Check(testkit.Rows("167772161"))

tk.MustExec("set @@tmp_table_size=18446744073709551615")
tk.MustExec("set @@tmp_table_size=9223372036854775807")
result = tk.MustQuery("select @@tmp_table_size;")
result.Check(testkit.Rows("18446744073709551615"))
result.Check(testkit.Rows("9223372036854775807"))

_, err = tk.Exec("set @@tmp_table_size=18446744073709551616")
c.Assert(terror.ErrorEqual(err, variable.ErrWrongTypeForVar), IsTrue)
Expand Down
31 changes: 31 additions & 0 deletions session/session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import (
"github.com/pingcap/tidb/config"
"github.com/pingcap/tidb/ddl/placement"
"github.com/pingcap/tidb/domain"
"github.com/pingcap/tidb/errno"
"github.com/pingcap/tidb/executor"
"github.com/pingcap/tidb/kv"
"github.com/pingcap/tidb/meta/autoid"
Expand Down Expand Up @@ -4593,6 +4594,36 @@ func (s *testSessionSuite) TestInTxnPSProtoPointGet(c *C) {
tk.MustExec("commit")
}

func (s *testSessionSuite) TestTMPTableSize(c *C) {
// Test the @@tmp_table_size system variable.
tk := testkit.NewTestKit(c, s.store)
tk.MustExec("use test")
tk.MustExec("set tidb_enable_global_temporary_table=on")
tk.MustExec("create global temporary table t (c1 int, c2 varchar(512)) on commit delete rows")

tk.MustQuery("select @@global.tmp_table_size").Check(testkit.Rows(strconv.Itoa(variable.DefTMPTableSize)))
c.Assert(tk.Se.GetSessionVars().TMPTableSize, Equals, int64(variable.DefTMPTableSize))

// Min value 1024, so the result is change to 1024, with a warning.
tk.MustExec("set @@global.tmp_table_size = 123")
tk.MustQuery("show warnings").Check(testkit.Rows("Warning 1292 Truncated incorrect tmp_table_size value: '123'"))

// Change the session scope value.
tk.MustExec("set @@session.tmp_table_size = 2097152")
c.Assert(tk.Se.GetSessionVars().TMPTableSize, Equals, int64(2097152))

// Check in another sessin, change session scope value does not affect the global scope.
tk1 := testkit.NewTestKit(c, s.store)
tk1.MustQuery("select @@global.tmp_table_size").Check(testkit.Rows("1024"))

// The value is now 1024, check the error when table size exceed it.
tk.MustExec("set @@session.tmp_table_size = 1024")
tk.MustExec("begin")
tk.MustExec("insert into t values (1, repeat('x', 512))")
tk.MustExec("insert into t values (1, repeat('x', 512))")
tk.MustGetErrCode("insert into t values (1, repeat('x', 512))", errno.ErrRecordFileFull)
}

func (s *testSessionSuite) TestTiDBEnableGlobalTemporaryTable(c *C) {
// Test the @@tidb_enable_global_temporary_table system variable.
tk := testkit.NewTestKit(c, s.store)
Expand Down
1 change: 0 additions & 1 deletion sessionctx/variable/noop.go
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ var noopSysVars = []*SysVar{
{Scope: ScopeNone, Name: "performance_schema_max_statement_classes", Value: "168"},
{Scope: ScopeGlobal, Name: "server_id", Value: "0"},
{Scope: ScopeGlobal, Name: "innodb_flushing_avg_loops", Value: "30"},
{Scope: ScopeGlobal | ScopeSession, Name: TmpTableSize, Value: "16777216", Type: TypeUnsigned, MinValue: 1024, MaxValue: math.MaxUint64, AutoConvertOutOfRange: true, IsHintUpdatable: true},
{Scope: ScopeGlobal, Name: "innodb_max_purge_lag", Value: "0"},
{Scope: ScopeGlobal | ScopeSession, Name: "preload_buffer_size", Value: "32768"},
{Scope: ScopeGlobal, Name: CheckProxyUsers, Value: Off, Type: TypeBool},
Expand Down
7 changes: 7 additions & 0 deletions sessionctx/variable/session.go
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,12 @@ type SessionVars struct {
// see https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_cte_max_recursion_depth
CTEMaxRecursionDepth int

// The temporary table size threshold
// In MySQL, when a temporary table exceed this size, it spills to disk.
// In TiDB, as we do not support spill to disk for now, an error is reported.
// See https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_tmp_table_size
TMPTableSize int64

// EnableGlobalTemporaryTable indicates whether to enable global temporary table
EnableGlobalTemporaryTable bool
}
Expand Down Expand Up @@ -1058,6 +1064,7 @@ func NewSessionVars() *SessionVars {
EnableIndexMergeJoin: DefTiDBEnableIndexMergeJoin,
AllowFallbackToTiKV: make(map[kv.StoreType]struct{}),
CTEMaxRecursionDepth: DefCTEMaxRecursionDepth,
TMPTableSize: DefTMPTableSize,
EnableGlobalTemporaryTable: DefTiDBEnableGlobalTemporaryTable,
}
vars.KVVars = tikvstore.NewVariables(&vars.Killed)
Expand Down
10 changes: 8 additions & 2 deletions sessionctx/variable/sysvar.go
Original file line number Diff line number Diff line change
Expand Up @@ -1692,8 +1692,14 @@ var defaultSysVars = []*SysVar{
{Scope: ScopeGlobal, Name: TiDBGCRunInterval, Value: "10m0s", Type: TypeDuration, MinValue: int64(time.Minute * 10), MaxValue: math.MaxInt64},
{Scope: ScopeGlobal, Name: TiDBGCLifetime, Value: "10m0s", Type: TypeDuration, MinValue: int64(time.Minute * 10), MaxValue: math.MaxInt64},
{Scope: ScopeGlobal, Name: TiDBGCConcurrency, Value: "-1", Type: TypeInt, MinValue: 1, MaxValue: 128, AllowAutoValue: true},
{Scope: ScopeGlobal, Name: TiDBGCScanLockMode, Value: "PHYSICAL", Type: TypeEnum, PossibleValues: []string{"PHYSICAL", "LEGACY"}},
{Scope: ScopeGlobal, Name: TiDBGCScanLockMode, Value: "LEGACY", Type: TypeEnum, PossibleValues: []string{"PHYSICAL", "LEGACY"}},

// See https://dev.mysql.com/doc/refman/8.0/en/server-system-variables.html#sysvar_tmp_table_size
{Scope: ScopeGlobal | ScopeSession, Name: TMPTableSize, Value: strconv.Itoa(DefTMPTableSize), Type: TypeUnsigned, MinValue: 1024, MaxValue: math.MaxInt64, AutoConvertOutOfRange: true, IsHintUpdatable: true, AllowEmpty: true, SetSession: func(s *SessionVars, val string) error {
s.TMPTableSize = tidbOptInt64(val, DefTMPTableSize)
return nil
}},
// variable for top SQL feature.
{Scope: ScopeGlobal, Name: TiDBEnableTopSQL, Value: BoolToOnOff(DefTiDBTopSQLEnable), Type: TypeBool, Hidden: true, AllowEmpty: true, GetSession: func(s *SessionVars) (string, error) {
return BoolToOnOff(TopSQLVariable.Enable.Load()), nil
Expand Down Expand Up @@ -1859,8 +1865,8 @@ const (
MaxConnectErrors = "max_connect_errors"
// TableDefinitionCache is the name for 'table_definition_cache' system variable.
TableDefinitionCache = "table_definition_cache"
// TmpTableSize is the name for 'tmp_table_size' system variable.
TmpTableSize = "tmp_table_size"
// TMPTableSize is the name for 'tmp_table_size' system variable.
TMPTableSize = "tmp_table_size"
// Timestamp is the name for 'timestamp' system variable.
Timestamp = "timestamp"
// ConnectTimeout is the name for 'connect_timeout' system variable.
Expand Down
1 change: 1 addition & 0 deletions sessionctx/variable/tidb_vars.go
Original file line number Diff line number Diff line change
Expand Up @@ -720,6 +720,7 @@ const (
DefTiDBTopSQLReportIntervalSeconds = 60
DefTiDBEnableGlobalTemporaryTable = false
DefTiDBEnableLocalTxn = false
DefTMPTableSize = 16777216
)

// Process global variables.
Expand Down
1 change: 1 addition & 0 deletions sessionctx/variable/varsutil_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ func (s *testVarsutilSuite) TestNewSessionVars(c *C) {
c.Assert(vars.ShardAllocateStep, Equals, int64(DefTiDBShardAllocateStep))
c.Assert(vars.AnalyzeVersion, Equals, DefTiDBAnalyzeVersion)
c.Assert(vars.CTEMaxRecursionDepth, Equals, DefCTEMaxRecursionDepth)
c.Assert(vars.TMPTableSize, Equals, int64(DefTMPTableSize))

assertFieldsGreaterThanZero(c, reflect.ValueOf(vars.MemQuota))
assertFieldsGreaterThanZero(c, reflect.ValueOf(vars.BatchSize))
Expand Down
2 changes: 2 additions & 0 deletions table/table.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,8 @@ var (
ErrSequenceHasRunOut = dbterror.ClassTable.NewStd(mysql.ErrSequenceRunOut)
// ErrRowDoesNotMatchGivenPartitionSet returns when the destination partition conflict with the partition selection.
ErrRowDoesNotMatchGivenPartitionSet = dbterror.ClassTable.NewStd(mysql.ErrRowDoesNotMatchGivenPartitionSet)
// ErrTempTableFull returns a table is full error, it's used by temporary table now.
ErrTempTableFull = dbterror.ClassTable.NewStd(mysql.ErrRecordFileFull)
)

// RecordIterFunc is used for low-level record iteration.
Expand Down
50 changes: 46 additions & 4 deletions table/tables/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,12 @@ func (t *TableCommon) UpdateRecord(ctx context.Context, sctx sessionctx.Context,
defer memBuffer.Cleanup(sh)

if m := t.Meta(); m.TempTableType == model.TempTableGlobal {
addTemporaryTable(sctx, m)
if tmpTable := addTemporaryTable(sctx, m); tmpTable != nil {
if tmpTable.GetSize() > sctx.GetSessionVars().TMPTableSize {
return table.ErrTempTableFull.GenWithStackByArgs(m.Name.O)
}
defer handleTempTableSize(tmpTable, txn.Size(), txn)
}
}

var colIDs, binlogColIDs []int64
Expand Down Expand Up @@ -590,9 +595,20 @@ func TryGetCommonPkColumns(tbl table.Table) []*table.Column {
return pkCols
}

func addTemporaryTable(sctx sessionctx.Context, tblInfo *model.TableInfo) {
func addTemporaryTable(sctx sessionctx.Context, tblInfo *model.TableInfo) tableutil.TempTable {
tempTable := sctx.GetSessionVars().GetTemporaryTable(tblInfo)
tempTable.SetModified(true)
return tempTable
}

// The size of a temporary table is calculated by accumulating the transaction size delta.
func handleTempTableSize(t tableutil.TempTable, txnSizeBefore int, txn kv.Transaction) {
txnSizeNow := txn.Size()
delta := txnSizeNow - txnSizeBefore

oldSize := t.GetSize()
newSize := oldSize + int64(delta)
t.SetSize(newSize)
}

// AddRecord implements table.Table AddRecord interface.
Expand All @@ -608,7 +624,12 @@ func (t *TableCommon) AddRecord(sctx sessionctx.Context, r []types.Datum, opts .
}

if m := t.Meta(); m.TempTableType == model.TempTableGlobal {
addTemporaryTable(sctx, m)
if tmpTable := addTemporaryTable(sctx, m); tmpTable != nil {
if tmpTable.GetSize() > sctx.GetSessionVars().TMPTableSize {
return nil, table.ErrTempTableFull.GenWithStackByArgs(m.Name.O)
}
defer handleTempTableSize(tmpTable, txn.Size(), txn)
}
}

var ctx context.Context
Expand Down Expand Up @@ -1009,8 +1030,17 @@ func (t *TableCommon) RemoveRecord(ctx sessionctx.Context, h kv.Handle, r []type
return err
}

txn, err := ctx.Txn(true)
if err != nil {
return err
}
if m := t.Meta(); m.TempTableType == model.TempTableGlobal {
addTemporaryTable(ctx, m)
if tmpTable := addTemporaryTable(ctx, m); tmpTable != nil {
if tmpTable.GetSize() > ctx.GetSessionVars().TMPTableSize {
return table.ErrTempTableFull.GenWithStackByArgs(m.Name.O)
}
defer handleTempTableSize(tmpTable, txn.Size(), txn)
}
}

// The table has non-public column and this column is doing the operation of "modify/change column".
Expand Down Expand Up @@ -1795,6 +1825,8 @@ type TemporaryTable struct {
stats *statistics.Table
// The autoID allocator of this table.
autoIDAllocator autoid.Allocator
// Table size.
size int64
}

// TempTableFromMeta builds a TempTable from model.TableInfo.
Expand Down Expand Up @@ -1825,3 +1857,13 @@ func (t *TemporaryTable) GetModified() bool {
func (t *TemporaryTable) GetStats() interface{} {
return t.stats
}

// GetSize gets the table size.
func (t *TemporaryTable) GetSize() int64 {
return t.size
}

// SetSize sets the table size.
func (t *TemporaryTable) SetSize(v int64) {
t.size = v
}
3 changes: 3 additions & 0 deletions util/tableutil/tableutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ type TempTable interface {
// The stats of this table (*statistics.Table).
// Define the return type as interface{} here to avoid cycle imports.
GetStats() interface{}

GetSize() int64
SetSize(int64)
}

// TempTableFromMeta builds a TempTable from *model.TableInfo.
Expand Down

0 comments on commit 727a217

Please sign in to comment.