Skip to content

Commit

Permalink
feat: support mysql 8.0 hints (#33)
Browse files Browse the repository at this point in the history
Co-authored-by: maronghe <[email protected]>
  • Loading branch information
maronghe and maronghe authored Apr 8, 2023
1 parent ea21d44 commit 6c840d5
Show file tree
Hide file tree
Showing 14 changed files with 1,514 additions and 765 deletions.
10 changes: 6 additions & 4 deletions ast/ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -702,10 +702,12 @@ const (
)

// IndexOption is the index options.
// KEY_BLOCK_SIZE [=] value
// | index_type
// | WITH PARSER parser_name
// | COMMENT 'string'
//
// KEY_BLOCK_SIZE [=] value
// | index_type
// | WITH PARSER parser_name
// | COMMENT 'string'
//
// See http://dev.mysql.com/doc/refman/5.7/en/create-table.html
type IndexOption struct {
node
Expand Down
24 changes: 16 additions & 8 deletions ast/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -1759,9 +1759,10 @@ type StatisticsSpec struct {

// CreateStatisticsStmt is a statement to create extended statistics.
// Examples:
// CREATE STATISTICS stats1 (cardinality) ON t(a, b, c);
// CREATE STATISTICS stats2 (dependency) ON t(a, b);
// CREATE STATISTICS stats3 (correlation) ON t(a, b);
//
// CREATE STATISTICS stats1 (cardinality) ON t(a, b, c);
// CREATE STATISTICS stats2 (dependency) ON t(a, b);
// CREATE STATISTICS stats3 (correlation) ON t(a, b);
type CreateStatisticsStmt struct {
stmtNode

Expand Down Expand Up @@ -1829,7 +1830,8 @@ func (n *CreateStatisticsStmt) Accept(v Visitor) (Node, bool) {

// DropStatisticsStmt is a statement to drop extended statistics.
// Examples:
// DROP STATISTICS stats1;
//
// DROP STATISTICS stats1;
type DropStatisticsStmt struct {
stmtNode

Expand Down Expand Up @@ -1960,6 +1962,7 @@ const (
)

// ShowSlow is used for the following command:
//
// admin show slow top [ internal | all] N
// admin show slow recent N
type ShowSlow struct {
Expand Down Expand Up @@ -3417,7 +3420,7 @@ func (n *TableOptimizerHint) Restore(ctx *format.RestoreCtx) error {
}
// Hints without args except query block.
switch n.HintName.L {
case "hash_agg", "stream_agg", "agg_to_cop", "read_consistent_replica", "no_index_merge", "qb_name", "ignore_plan_cache", "limit_to_cop":
case "hash_agg", "stream_agg", "agg_to_cop", "read_consistent_replica", "qb_name", "ignore_plan_cache", "limit_to_cop":
ctx.WritePlain(")")
return nil
}
Expand All @@ -3430,15 +3433,20 @@ func (n *TableOptimizerHint) Restore(ctx *format.RestoreCtx) error {
ctx.WritePlainf("%d", n.HintData.(uint64))
case "nth_plan":
ctx.WritePlainf("%d", n.HintData.(int64))
case "tidb_hj", "tidb_smj", "tidb_inlj", "hash_join", "merge_join", "inl_join", "broadcast_join", "broadcast_join_local", "inl_hash_join", "inl_merge_join",
"join_prefix", "join_order", "join_suffix", "merge", "no_merge":
case "tidb_hj", "tidb_smj", "tidb_inlj", "hash_join", "merge_join", "inl_join", "broadcast_join",
"broadcast_join_local", "inl_hash_join", "inl_merge_join", "no_hash_join", "bka", "no_bka",
"bnl", "no_bnl", "derived_condition_pushdown", "no_derived_condition_pushdown",
"join_fixed_order", "join_prefix", "join_order", "join_suffix", "merge", "no_merge":
for i, table := range n.Tables {
if i != 0 {
ctx.WritePlain(", ")
}
table.Restore(ctx)
}
case "use_index", "ignore_index", "use_index_merge", "force_index", "mrr", "no_mrr", "no_icp", "no_range_optimization", "order_index", "no_order_index", "skip_scan", "no_skip_scan":
case "use_index", "ignore_index", "use_index_merge", "force_index", "mrr", "no_mrr", "no_icp",
"no_range_optimization", "order_index", "no_order_index", "skip_scan", "no_skip_scan",
"group_index", "no_group_index", "index", "no_index", "index_merge", "no_index_merge",
"join_index", "no_join_index":
n.Tables[0].Restore(ctx)
if len(n.Indexes) != 0 {
ctx.WritePlain(" ")
Expand Down
73 changes: 68 additions & 5 deletions ast/misc_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,69 @@ func TestUserSpec(t *testing.T) {

func TestTableOptimizerHintRestore(t *testing.T) {
testCases := []NodeRestoreTestCase{
// mysql 8.0 table level
{"BKA()", "BKA()"},
{"BKA(@qb1)", "BKA(@`qb1` )"},
{"BKA(tbl1)", "BKA(`tbl1`)"},
{"BKA(tbl1@qb1,tbl2@qb2)", "BKA(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
{"NO_BKA()", "NO_BKA()"},
{"NO_BKA(@qb1)", "NO_BKA(@`qb1` )"},
{"NO_BKA(tbl1)", "NO_BKA(`tbl1`)"},
{"NO_BKA(tbl1@qb1,tbl2@qb2)", "NO_BKA(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
{"BNL()", "BNL()"},
{"BNL(@qb1)", "BNL(@`qb1` )"},
{"BNL(tbl1)", "BNL(`tbl1`)"},
{"BNL(tbl1@qb1,tbl2@qb2)", "BNL(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
{"NO_BNL()", "NO_BNL()"},
{"NO_BNL(@qb1)", "NO_BNL(@`qb1` )"},
{"NO_BNL(tbl1)", "NO_BNL(`tbl1`)"},
{"NO_BNL(tbl1@qb1,tbl2@qb2)", "NO_BNL(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
{"DERIVED_CONDITION_PUSHDOWN()", "DERIVED_CONDITION_PUSHDOWN()"},
{"DERIVED_CONDITION_PUSHDOWN(@qb1)", "DERIVED_CONDITION_PUSHDOWN(@`qb1` )"},
{"DERIVED_CONDITION_PUSHDOWN(tbl1)", "DERIVED_CONDITION_PUSHDOWN(`tbl1`)"},
{"DERIVED_CONDITION_PUSHDOWN(tbl1@qb1,tbl2@qb2)", "DERIVED_CONDITION_PUSHDOWN(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
{"NO_DERIVED_CONDITION_PUSHDOWN()", "NO_DERIVED_CONDITION_PUSHDOWN()"},
{"NO_DERIVED_CONDITION_PUSHDOWN(@qb1)", "NO_DERIVED_CONDITION_PUSHDOWN(@`qb1` )"},
{"NO_DERIVED_CONDITION_PUSHDOWN(tbl1)", "NO_DERIVED_CONDITION_PUSHDOWN(`tbl1`)"},
{"NO_DERIVED_CONDITION_PUSHDOWN(tbl1@qb1,tbl2@qb2)", "NO_DERIVED_CONDITION_PUSHDOWN(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
{"HASH_JOIN()", "HASH_JOIN()"},
{"HASH_JOIN(@qb1)", "HASH_JOIN(@`qb1` )"},
{"HASH_JOIN(tbl1)", "HASH_JOIN(`tbl1`)"},
{"HASH_JOIN(tbl1@qb1,tbl2@qb2)", "HASH_JOIN(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
{"NO_HASH_JOIN()", "NO_HASH_JOIN()"},
{"NO_HASH_JOIN(@qb1)", "NO_HASH_JOIN(@`qb1` )"},
{"NO_HASH_JOIN(tbl1)", "NO_HASH_JOIN(`tbl1`)"},
{"NO_HASH_JOIN(tbl1@qb1,tbl2@qb2)", "NO_HASH_JOIN(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
{"JOIN_FIXED_ORDER()", "JOIN_FIXED_ORDER()"},
{"JOIN_FIXED_ORDER(@qb1)", "JOIN_FIXED_ORDER(@`qb1` )"},
{"JOIN_FIXED_ORDER(tbl1)", "JOIN_FIXED_ORDER(`tbl1`)"},
{"JOIN_FIXED_ORDER(tbl1@qb1,tbl2@qb2)", "JOIN_FIXED_ORDER(`tbl1`@`qb1`, `tbl2`@`qb2`)"},
// mysql 8.0 index level
{"GROUP_INDEX(t1 c1)", "GROUP_INDEX(`t1` `c1`)"},
{"GROUP_INDEX(@sel1 t1 c1)", "GROUP_INDEX(@`sel1` `t1` `c1`)"},
{"GROUP_INDEX(t1@sel1 c1)", "GROUP_INDEX(`t1`@`sel1` `c1`)"},
{"NO_GROUP_INDEX(t1 c1)", "NO_GROUP_INDEX(`t1` `c1`)"},
{"NO_GROUP_INDEX(@sel1 t1 c1)", "NO_GROUP_INDEX(@`sel1` `t1` `c1`)"},
{"NO_GROUP_INDEX(t1@sel1 c1)", "NO_GROUP_INDEX(`t1`@`sel1` `c1`)"},
{"INDEX(t1 c1)", "INDEX(`t1` `c1`)"},
{"INDEX(@sel1 t1 c1)", "INDEX(@`sel1` `t1` `c1`)"},
{"INDEX(t1@sel1 c1)", "INDEX(`t1`@`sel1` `c1`)"},
{"NO_INDEX(t1@sel1 c1)", "NO_INDEX(`t1`@`sel1` `c1`)"},
{"NO_INDEX(@sel1 t1 c1)", "NO_INDEX(@`sel1` `t1` `c1`)"},
{"NO_INDEX(t1@sel1 c1)", "NO_INDEX(`t1`@`sel1` `c1`)"},
{"JOIN_INDEX(t1 c1)", "JOIN_INDEX(`t1` `c1`)"},
{"JOIN_INDEX(@sel1 t1 c1)", "JOIN_INDEX(@`sel1` `t1` `c1`)"},
{"JOIN_INDEX(t1@sel1 c1)", "JOIN_INDEX(`t1`@`sel1` `c1`)"},
{"NO_JOIN_INDEX(t1 c1)", "NO_JOIN_INDEX(`t1` `c1`)"},
{"NO_JOIN_INDEX(@sel1 t1 c1)", "NO_JOIN_INDEX(@`sel1` `t1` `c1`)"},
{"NO_JOIN_INDEX(t1@sel1 c1)", "NO_JOIN_INDEX(`t1`@`sel1` `c1`)"},
{"INDEX_MERGE(t1 c1)", "INDEX_MERGE(`t1` `c1`)"},
{"INDEX_MERGE(@sel1 t1 c1)", "INDEX_MERGE(@`sel1` `t1` `c1`)"},
{"INDEX_MERGE(t1@sel1 c1)", "INDEX_MERGE(`t1`@`sel1` `c1`)"},
{"NO_INDEX_MERGE(t1 c1)", "NO_INDEX_MERGE(`t1` `c1`)"},
{"NO_INDEX_MERGE(@sel1 t1 c1)", "NO_INDEX_MERGE(@`sel1` `t1` `c1`)"},
{"NO_INDEX_MERGE(t1@sel1 c1)", "NO_INDEX_MERGE(`t1`@`sel1` `c1`)"},

{"USE_INDEX(t1 c1)", "USE_INDEX(`t1` `c1`)"},
{"USE_INDEX(test.t1 c1)", "USE_INDEX(`test`.`t1` `c1`)"},
{"USE_INDEX(@sel_1 t1 c1)", "USE_INDEX(@`sel_1` `t1` `c1`)"},
Expand Down Expand Up @@ -239,7 +302,6 @@ func TestTableOptimizerHintRestore(t *testing.T) {
{"INL_HASH_JOIN(t1,t2)", "INL_HASH_JOIN(`t1`, `t2`)"},
{"INL_MERGE_JOIN(t1,t2)", "INL_MERGE_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)"},
{"MAX_EXECUTION_TIME(@sel1 3000)", "MAX_EXECUTION_TIME(@`sel1` 3000)"},
{"USE_INDEX_MERGE(t1 c1)", "USE_INDEX_MERGE(`t1` `c1`)"},
Expand All @@ -265,8 +327,6 @@ func TestTableOptimizerHintRestore(t *testing.T) {
{"AGG_TO_COP()", "AGG_TO_COP()"},
{"AGG_TO_COP(@sel_1)", "AGG_TO_COP(@`sel_1`)"},
{"LIMIT_TO_COP()", "LIMIT_TO_COP()"},
{"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`)"},
Expand Down Expand Up @@ -323,7 +383,11 @@ func TestTableOptimizerHintRestore(t *testing.T) {
{"SUBQUERY(@subq1 INTOEXISTS, MATERIALIZATION)", "SUBQUERY(@`subq1` INTOEXISTS, MATERIALIZATION)"},
}
extractNodeFunc := func(node ast.Node) ast.Node {
return node.(*ast.SelectStmt).TableHints[0]
hints := node.(*ast.SelectStmt).TableHints
if len(hints) > 0 {
return hints[0]
}
return &ast.TableOptimizerHint{} // avoid panic
}
runNodeRestoreTest(t, testCases, "select /*+ %s */ * from t1 join t2", extractNodeFunc)
}
Expand Down Expand Up @@ -373,6 +437,5 @@ func TestBRIESecureText(t *testing.T) {
n, ok := node.(ast.SensitiveStmtNode)
require.True(t, ok, comment)
require.Regexp(t, tc.secured, n.SecureText(), comment)

}
}
27 changes: 14 additions & 13 deletions auth/mysql_native_password.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,20 @@ import (

// CheckScrambledPassword check scrambled password received from client.
// The new authentication is performed in following manner:
// SERVER: public_seed=create_random_string()
// send(public_seed)
// CLIENT: recv(public_seed)
// hash_stage1=sha1("password")
// hash_stage2=sha1(hash_stage1)
// reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
// // this three steps are done in scramble()
// send(reply)
// SERVER: recv(reply)
// hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
// candidate_hash2=sha1(hash_stage1)
// check(candidate_hash2==hash_stage2)
// // this three steps are done in check_scramble()
//
// SERVER: public_seed=create_random_string()
// send(public_seed)
// CLIENT: recv(public_seed)
// hash_stage1=sha1("password")
// hash_stage2=sha1(hash_stage1)
// reply=xor(hash_stage1, sha1(public_seed,hash_stage2)
// // this three steps are done in scramble()
// send(reply)
// SERVER: recv(reply)
// hash_stage1=xor(reply, sha1(public_seed,hash_stage2))
// candidate_hash2=sha1(hash_stage1)
// check(candidate_hash2==hash_stage2)
// // this three steps are done in check_scramble()
func CheckScrambledPassword(salt, hpwd, auth []byte) bool {
crypt := sha1.New()
_, err := crypt.Write(salt)
Expand Down
34 changes: 22 additions & 12 deletions format/format.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,21 +56,28 @@ var replace = map[rune]string{
// nest. The Formatter writes to io.Writer 'w' and inserts one 'indent'
// string per current indent level value.
// Behaviour of commands reaching negative indent levels is undefined.
// IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
//
// IndentFormatter(os.Stdout, "\t").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
//
// output:
// abc3%e
// x
// y
// z
//
// abc3%e
// x
// y
// z
//
// The Go quoted string literal form of the above is:
// "abc%%e\n\tx\n\tx\nz\n"
//
// "abc%%e\n\tx\n\tx\nz\n"
//
// The commands can be scattered between separate invocations of Format(),
// i.e. the formatter keeps track of the indent level and knows if it is
// positioned on start of a line and should emit indentation(s).
// The same output as above can be produced by e.g.:
// f := IndentFormatter(os.Stdout, " ")
// f.Format("abc%d%%e%i\nx\n", 3)
// f.Format("y\n%uz\n")
//
// f := IndentFormatter(os.Stdout, " ")
// f.Format("abc%d%%e%i\nx\n", 3)
// f.Format("y\n%uz\n")
func IndentFormatter(w io.Writer, indent string) Formatter {
return &indentFormatter{w, []byte(indent), 0, stBOL}
}
Expand Down Expand Up @@ -169,9 +176,12 @@ type flatFormatter indentFormatter
//
// The FlatFormatter is intended for flattening of normally nested structure textual representation to
// a one top level structure per line form.
// FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
//
// FlatFormatter(os.Stdout, " ").Format("abc%d%%e%i\nx\ny\n%uz\n", 3)
//
// output in the form of a Go quoted string literal:
// "abc3%%e x y z\n"
//
// "abc3%%e x y z\n"
func FlatFormatter(w io.Writer) Formatter {
return (*flatFormatter)(IndentFormatter(w, "").(*indentFormatter))
}
Expand All @@ -195,7 +205,7 @@ func OutputFormat(s string) string {
return buf.String()
}

//RestoreFlag mark the Restore format
// RestoreFlag mark the Restore format
type RestoreFlags uint64

// Mutually exclusive group of `RestoreFlags`:
Expand Down
20 changes: 9 additions & 11 deletions goyacc/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@

// Goyacc is a version of yacc generating Go parsers.
//
// Usage
// # Usage
//
// Note: If no non flag arguments are given, goyacc reads standard input.
//
Expand All @@ -42,9 +42,7 @@
// -xegen examplesFile Generate a file suitable for -xe automatically from the grammar.
// The file must not exist. ("")
//
//
//
// Changelog
// # Changelog
//
// 2015-03-24: The search for a custom error message is now extended to include
// also the last state that was shifted into, if any. This change resolves a
Expand All @@ -70,7 +68,7 @@
// by parsing code fragments. If it returns true the parser exits immediately
// with return value -1.
//
// Overview
// # Overview
//
// The generated parser is reentrant and mostly backwards compatible with
// parsers generated by go tool yacc[0]. yyParse expects to be given an
Expand Down Expand Up @@ -104,7 +102,7 @@
// generated code. Setting it to distinct values allows multiple grammars to be
// placed in a single package.
//
// Differences wrt go tool yacc
// # Differences wrt go tool yacc
//
// - goyacc implements ideas from "Generating LR Syntax Error Messages from
// Examples"[1]. Use the -xe flag to pass a name of the example file. For more
Expand All @@ -115,14 +113,14 @@
//
// - Minor changes in parser debug output.
//
// Links
// # Links
//
// Referenced from elsewhere:
//
// [0]: http://golang.org/cmd/yacc/
// [1]: http://people.via.ecp.fr/~stilgar/doc/compilo/parser/Generating%20LR%20Syntax%20Error%20Messages.pdf
// [2]: http://godoc.org/github.com/cznic/y#hdr-Error_Examples
// [3]: http://www.gnu.org/software/bison/manual/html_node/Precedence-Only.html#Precedence-Only
// [0]: http://golang.org/cmd/yacc/
// [1]: http://people.via.ecp.fr/~stilgar/doc/compilo/parser/Generating%20LR%20Syntax%20Error%20Messages.pdf
// [2]: http://godoc.org/github.com/cznic/y#hdr-Error_Examples
// [3]: http://www.gnu.org/software/bison/manual/html_node/Precedence-Only.html#Precedence-Only
package main

import (
Expand Down
Loading

0 comments on commit 6c840d5

Please sign in to comment.