Skip to content

Commit

Permalink
parser: allow drop stats of multiple tables (#38042)
Browse files Browse the repository at this point in the history
close #37872
  • Loading branch information
YangKeao authored Nov 14, 2022
1 parent 678aa56 commit b0c3381
Show file tree
Hide file tree
Showing 8 changed files with 438 additions and 354 deletions.
20 changes: 15 additions & 5 deletions executor/simple.go
Original file line number Diff line number Diff line change
Expand Up @@ -1776,14 +1776,24 @@ func (e *SimpleExec) executeAlterInstance(s *ast.AlterInstanceStmt) error {
func (e *SimpleExec) executeDropStats(s *ast.DropStatsStmt) (err error) {
h := domain.GetDomain(e.ctx).StatsHandle()
var statsIDs []int64
// TODO: GLOBAL option will be deprecated. Also remove this condition when the syntax is removed
if s.IsGlobalStats {
statsIDs = []int64{s.Table.TableInfo.ID}
statsIDs = []int64{s.Tables[0].TableInfo.ID}
} else {
if statsIDs, _, err = core.GetPhysicalIDsAndPartitionNames(s.Table.TableInfo, s.PartitionNames); err != nil {
return err
}
if len(s.PartitionNames) == 0 {
statsIDs = append(statsIDs, s.Table.TableInfo.ID)
for _, table := range s.Tables {
partitionStatIds, _, err := core.GetPhysicalIDsAndPartitionNames(table.TableInfo, nil)
if err != nil {
return err
}
statsIDs = append(statsIDs, partitionStatIds...)
statsIDs = append(statsIDs, table.TableInfo.ID)
}
} else {
// TODO: drop stats for specific partition is deprecated. Also remove this condition when the syntax is removed
if statsIDs, _, err = core.GetPhysicalIDsAndPartitionNames(s.Tables[0].TableInfo, s.PartitionNames); err != nil {
return err
}
}
}
if err := h.DeleteTableStatsFromKV(statsIDs); err != nil {
Expand Down
49 changes: 49 additions & 0 deletions executor/simpletest/simple_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -1000,6 +1000,7 @@ partition by range (a) (
checkPartitionStats("global", "p0", "p1", "global")

tk.MustExec("drop stats test_drop_gstats partition p0")
tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1681|'DROP STATS ... PARTITION ...' is deprecated and will be removed in a future release."))
checkPartitionStats("global", "p1", "global")

err := tk.ExecToErr("drop stats test_drop_gstats partition abcde")
Expand All @@ -1010,6 +1011,7 @@ partition by range (a) (
checkPartitionStats("global", "p1")

tk.MustExec("drop stats test_drop_gstats global")
tk.MustQuery("show warnings").Check(testkit.RowsWithSep("|", "Warning|1287|'DROP STATS ... GLOBAL' is deprecated and will be removed in a future release. Please use DROP STATS ... instead"))
checkPartitionStats("p1")

tk.MustExec("analyze table test_drop_gstats")
Expand Down Expand Up @@ -1056,3 +1058,50 @@ func TestDropStats(t *testing.T) {
require.True(t, statsTbl.Pseudo)
h.SetLease(0)
}

func TestDropStatsForMultipleTable(t *testing.T) {
store, dom := testkit.CreateMockStoreAndDomain(t)
testKit := testkit.NewTestKit(t, store)
testKit.MustExec("use test")
testKit.MustExec("create table t1 (c1 int, c2 int)")
testKit.MustExec("create table t2 (c1 int, c2 int)")

is := dom.InfoSchema()
tbl1, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t1"))
require.NoError(t, err)
tableInfo1 := tbl1.Meta()

tbl2, err := is.TableByName(model.NewCIStr("test"), model.NewCIStr("t2"))
require.NoError(t, err)
tableInfo2 := tbl2.Meta()

h := dom.StatsHandle()
h.Clear()
testKit.MustExec("analyze table t1, t2")
statsTbl1 := h.GetTableStats(tableInfo1)
require.False(t, statsTbl1.Pseudo)
statsTbl2 := h.GetTableStats(tableInfo2)
require.False(t, statsTbl2.Pseudo)

testKit.MustExec("drop stats t1, t2")
require.Nil(t, h.Update(is))
statsTbl1 = h.GetTableStats(tableInfo1)
require.True(t, statsTbl1.Pseudo)
statsTbl2 = h.GetTableStats(tableInfo2)
require.True(t, statsTbl2.Pseudo)

testKit.MustExec("analyze table t1, t2")
statsTbl1 = h.GetTableStats(tableInfo1)
require.False(t, statsTbl1.Pseudo)
statsTbl2 = h.GetTableStats(tableInfo2)
require.False(t, statsTbl2.Pseudo)

h.SetLease(1)
testKit.MustExec("drop stats t1, t2")
require.Nil(t, h.Update(is))
statsTbl1 = h.GetTableStats(tableInfo1)
require.True(t, statsTbl1.Pseudo)
statsTbl2 = h.GetTableStats(tableInfo2)
require.True(t, statsTbl2.Pseudo)
h.SetLease(0)
}
6 changes: 5 additions & 1 deletion parser/ast/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,11 @@ func TestMiscVisitorCover(t *testing.T) {
&ast.PrivElem{},
&ast.VariableAssignment{Value: valueExpr},
&ast.KillStmt{},
&ast.DropStatsStmt{Table: &ast.TableName{}},
&ast.DropStatsStmt{
Tables: []*ast.TableName{
{},
},
},
&ast.ShutdownStmt{},
}

Expand Down
23 changes: 16 additions & 7 deletions parser/ast/stats.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,19 +189,26 @@ func (n *AnalyzeTableStmt) Accept(v Visitor) (Node, bool) {
}

// DropStatsStmt is used to drop table statistics.
// if the PartitionNames is not empty, or IsGlobalStats is true, it will contain exactly one table
type DropStatsStmt struct {
stmtNode

Table *TableName
Tables []*TableName
PartitionNames []model.CIStr
IsGlobalStats bool
}

// Restore implements Node interface.
func (n *DropStatsStmt) Restore(ctx *format.RestoreCtx) error {
ctx.WriteKeyWord("DROP STATS ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while add table")

for index, table := range n.Tables {
if index != 0 {
ctx.WritePlain(", ")
}
if err := table.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore DropStatsStmt.Tables[%d]", index)
}
}

if n.IsGlobalStats {
Expand All @@ -228,11 +235,13 @@ func (n *DropStatsStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(newNode)
}
n = newNode.(*DropStatsStmt)
node, ok := n.Table.Accept(v)
if !ok {
return n, false
for i, val := range n.Tables {
node, ok := val.Accept(v)
if !ok {
return n, false
}
n.Tables[i] = node.(*TableName)
}
n.Table = node.(*TableName)
return v.Leave(n)
}

Expand Down
677 changes: 341 additions & 336 deletions parser/parser.go

Large diffs are not rendered by default.

12 changes: 8 additions & 4 deletions parser/parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -4779,21 +4779,25 @@ DropRoleStmt:
}

DropStatsStmt:
"DROP" "STATS" TableName
"DROP" "STATS" TableNameList
{
$$ = &ast.DropStatsStmt{Table: $3.(*ast.TableName)}
$$ = &ast.DropStatsStmt{Tables: $3.([]*ast.TableName)}
}
| "DROP" "STATS" TableName "PARTITION" PartitionNameList
{
yylex.AppendError(ErrWarnDeprecatedSyntaxNoReplacement.FastGenByArgs("DROP STATS ... PARTITION ..."))
parser.lastErrorAsWarn()
$$ = &ast.DropStatsStmt{
Table: $3.(*ast.TableName),
Tables: []*ast.TableName{$3.(*ast.TableName)},
PartitionNames: $5.([]model.CIStr),
}
}
| "DROP" "STATS" TableName "GLOBAL"
{
yylex.AppendError(ErrWarnDeprecatedSyntax.FastGenByArgs("DROP STATS ... GLOBAL", "DROP STATS ..."))
parser.lastErrorAsWarn()
$$ = &ast.DropStatsStmt{
Table: $3.(*ast.TableName),
Tables: []*ast.TableName{$3.(*ast.TableName)},
IsGlobalStats: true,
}
}
Expand Down
3 changes: 2 additions & 1 deletion parser/parser_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2660,9 +2660,10 @@ func TestDDL(t *testing.T) {
{"drop view if exists xxx", true, "DROP VIEW IF EXISTS `xxx`"},
{"drop view if exists xxx, yyy", true, "DROP VIEW IF EXISTS `xxx`, `yyy`"},
{"drop stats t", true, "DROP STATS `t`"},
{"drop stats t1, t2, t3", true, "DROP STATS `t1`, `t2`, `t3`"},
{"drop stats t global", true, "DROP STATS `t` GLOBAL"},
{"drop stats t partition p0", true, "DROP STATS `t` PARTITION `p0`"},
{"drop stats t partition p0, p1, p2", true, "DROP STATS `t` PARTITION `p0`,`p1`,`p2`"},
{"drop stats t global", true, "DROP STATS `t` GLOBAL"},
// for issue 974
{`CREATE TABLE address (
id bigint(20) NOT NULL AUTO_INCREMENT,
Expand Down
2 changes: 2 additions & 0 deletions parser/yy_parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ var (
ErrUnknownAlterAlgorithm = terror.ClassParser.NewStd(mysql.ErrUnknownAlterAlgorithm)
// ErrWrongValue returns for wrong value
ErrWrongValue = terror.ClassParser.NewStd(mysql.ErrWrongValue)
// ErrWarnDeprecatedSyntax return when the syntax was deprecated
ErrWarnDeprecatedSyntax = terror.ClassParser.NewStd(mysql.ErrWarnDeprecatedSyntax)
// ErrWarnDeprecatedSyntaxNoReplacement return when the syntax was deprecated and there is no replacement.
ErrWarnDeprecatedSyntaxNoReplacement = terror.ClassParser.NewStd(mysql.ErrWarnDeprecatedSyntaxNoReplacement)
// ErrWarnDeprecatedIntegerDisplayWidth share the same code 1681, and it will be returned when length is specified in integer.
Expand Down

0 comments on commit b0c3381

Please sign in to comment.