Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 39 additions & 12 deletions ast/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -2026,8 +2026,10 @@ type TableOptimizerHint struct {
// Table hints has no schema info
// It allows only table name or alias (if table has an alias)
HintName model.CIStr
Tables []model.CIStr
Indexes []model.CIStr
// QBName is the default effective query block of this hint.
QBName model.CIStr
Tables []HintTable
Indexes []model.CIStr
// Statement Execution Time Optimizer Hints
// See https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time
MaxExecutionTime uint64
Expand All @@ -2036,16 +2038,40 @@ type TableOptimizerHint struct {
HintFlag bool
}

// HintTable is table in the hint. It may have query block info.
type HintTable struct {
TableName model.CIStr
QBName model.CIStr
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why need we save the QBName in both HintTable and TableOptimizerHint?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Because we can specify the QB within which the hint will take effect, and also specify a table in some QB?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

For example: /*+ HASH_JOIN(@sel1 t1, t2) */ and /*+ HASH_JOIN(t1@sel1, t2@sel2).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, you can take a look at the test.

}

func (ht *HintTable) Restore(ctx *RestoreCtx) {
ctx.WriteName(ht.TableName.String())
if ht.QBName.L != "" {
ctx.WriteKeyWord("@")
ctx.WriteName(ht.QBName.String())
}
}

// Restore implements Node interface.
func (n *TableOptimizerHint) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord(n.HintName.String())
// Hints without args.
ctx.WritePlain("(")
if n.QBName.L != "" {
if n.HintName.L != "qb_name" {
ctx.WriteKeyWord("@")
}
ctx.WriteName(n.QBName.String())
}
// Hints without args except query block.
switch n.HintName.L {
case "hash_agg", "stream_agg", "read_consistent_replica", "no_index_merge":
case "hash_agg", "stream_agg", "read_consistent_replica", "no_index_merge", "qb_name":
ctx.WritePlain(")")
return nil
}
// Hints with args.
ctx.WritePlain("(")
if n.QBName.L != "" {
ctx.WritePlain(" ")
}
// Hints with args except query block.
switch n.HintName.L {
case "max_execution_time":
ctx.WritePlainf("%d", n.MaxExecutionTime)
Expand All @@ -2054,14 +2080,15 @@ func (n *TableOptimizerHint) Restore(ctx *RestoreCtx) error {
if i != 0 {
ctx.WritePlain(", ")
}
ctx.WriteName(table.String())
table.Restore(ctx)
}
case "index", "use_index_merge":
if len(n.Tables) != 0 {
ctx.WriteName(n.Tables[0].String())
}
for _, index := range n.Indexes {
ctx.WritePlain(", ")
n.Tables[0].Restore(ctx)
ctx.WritePlain(" ")
for i, index := range n.Indexes {
if i != 0 {
ctx.WritePlain(", ")
}
ctx.WriteName(index.String())
}
case "use_toja", "enable_plan_cache":
Expand Down
32 changes: 26 additions & 6 deletions ast/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -205,32 +205,52 @@ func (ts *testMiscSuite) TestUserSpec(c *C) {

func (ts *testMiscSuite) TestTableOptimizerHintRestore(c *C) {
testCases := []NodeRestoreTestCase{
{"INDEX(t1, c1)", "INDEX(`t1`, `c1`)"},
{"INDEX(t1 c1)", "INDEX(`t1` `c1`)"},
{"INDEX(@sel_1 t1 c1)", "INDEX(@`sel_1` `t1` `c1`)"},
{"INDEX(t1@sel_1 c1)", "INDEX(`t1`@`sel_1` `c1`)"},
{"TIDB_SMJ(`t1`)", "TIDB_SMJ(`t1`)"},
{"TIDB_SMJ(t1)", "TIDB_SMJ(`t1`)"},
{"TIDB_SMJ(t1,t2)", "TIDB_SMJ(`t1`, `t2`)"},
{"TIDB_SMJ(@sel1 t1,t2)", "TIDB_SMJ(@`sel1` `t1`, `t2`)"},
{"TIDB_SMJ(t1@sel1,t2@sel2)", "TIDB_SMJ(`t1`@`sel1`, `t2`@`sel2`)"},
{"TIDB_INLJ(t1,t2)", "TIDB_INLJ(`t1`, `t2`)"},
{"TIDB_INLJ(@sel1 t1,t2)", "TIDB_INLJ(@`sel1` `t1`, `t2`)"},
{"TIDB_INLJ(t1@sel1,t2@sel2)", "TIDB_INLJ(`t1`@`sel1`, `t2`@`sel2`)"},
{"TIDB_HJ(t1,t2)", "TIDB_HJ(`t1`, `t2`)"},
{"TIDB_HJ(@sel1 t1,t2)", "TIDB_HJ(@`sel1` `t1`, `t2`)"},
{"TIDB_HJ(t1@sel1,t2@sel2)", "TIDB_HJ(`t1`@`sel1`, `t2`@`sel2`)"},
{"SM_JOIN(t1,t2)", "SM_JOIN(`t1`, `t2`)"},
{"INL_JOIN(t1,t2)", "INL_JOIN(`t1`, `t2`)"},
{"HASH_JOIN(t1,t2)", "HASH_JOIN(`t1`, `t2`)"},
{"MAX_EXECUTION_TIME(3000)", "MAX_EXECUTION_TIME(3000)"},
{"USE_INDEX_MERGE(t1, c1)", "USE_INDEX_MERGE(`t1`, `c1`)"},
{"MAX_EXECUTION_TIME(@sel1 3000)", "MAX_EXECUTION_TIME(@`sel1` 3000)"},
{"USE_INDEX_MERGE(t1 c1)", "USE_INDEX_MERGE(`t1` `c1`)"},
{"USE_INDEX_MERGE(@sel1 t1 c1)", "USE_INDEX_MERGE(@`sel1` `t1` `c1`)"},
{"USE_INDEX_MERGE(t1@sel1 c1)", "USE_INDEX_MERGE(`t1`@`sel1` `c1`)"},
{"USE_TOJA(TRUE)", "USE_TOJA(TRUE)"},
{"USE_TOJA(FALSE)", "USE_TOJA(FALSE)"},
{"USE_TOJA(@sel1 TRUE)", "USE_TOJA(@`sel1` TRUE)"},
{"QUERY_TYPE(OLAP)", "QUERY_TYPE(OLAP)"},
{"QUERY_TYPE(OLTP)", "QUERY_TYPE(OLTP)"},
{"QUERY_TYPE(@sel1 OLTP)", "QUERY_TYPE(@`sel1` OLTP)"},
{"MEMORY_QUOTA(1 G)", "MEMORY_QUOTA(1024 M)"},
{"HASH_AGG", "HASH_AGG"},
{"STREAM_AGG", "STREAM_AGG"},
{"NO_INDEX_MERGE", "NO_INDEX_MERGE"},
{"READ_CONSISTENT_REPLICA", "READ_CONSISTENT_REPLICA"},
{"MEMORY_QUOTA(@sel1 1 G)", "MEMORY_QUOTA(@`sel1` 1024 M)"},
{"HASH_AGG()", "HASH_AGG()"},
{"HASH_AGG(@sel1)", "HASH_AGG(@`sel1`)"},
{"STREAM_AGG()", "STREAM_AGG()"},
{"STREAM_AGG(@sel1)", "STREAM_AGG(@`sel1`)"},
{"NO_INDEX_MERGE()", "NO_INDEX_MERGE()"},
{"NO_INDEX_MERGE(@sel1)", "NO_INDEX_MERGE(@`sel1`)"},
{"READ_CONSISTENT_REPLICA()", "READ_CONSISTENT_REPLICA()"},
{"READ_CONSISTENT_REPLICA(@sel1)", "READ_CONSISTENT_REPLICA(@`sel1`)"},
{"QB_NAME(sel1)", "QB_NAME(`sel1`)"},
}
extractNodeFunc := func(node Node) Node {
return node.(*SelectStmt).TableHints[0]
}
RunNodeRestoreTest(c, testCases, "select /*+ %s */ * from t1 join t2", extractNodeFunc)
}

func (ts *testMiscSuite) TestChangeStmtRestore(c *C) {
testCases := []NodeRestoreTestCase{
{"CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'", "CHANGE PUMP TO NODE_STATE ='paused' FOR NODE_ID '127.0.0.1:9090'"},
Expand Down
1 change: 1 addition & 0 deletions misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,7 @@ var tokenMap = map[string]int{
"PROFILE": profile,
"PROFILES": profiles,
"PUMP": pump,
"QB_NAME": hintQBName,
"QUARTER": quarter,
"QUERY": query,
"QUERY_TYPE": hintQueryType,
Expand Down
Loading