vitess.io/vitess@v0.16.2/go/cmd/query_analyzer/query_analyzer.go (about) 1 /* 2 Copyright 2019 The Vitess Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package main 18 19 import ( 20 "bufio" 21 "bytes" 22 "fmt" 23 "io" 24 "os" 25 "sort" 26 27 "github.com/spf13/pflag" 28 29 "vitess.io/vitess/go/acl" 30 "vitess.io/vitess/go/exit" 31 "vitess.io/vitess/go/vt/log" 32 "vitess.io/vitess/go/vt/logutil" 33 "vitess.io/vitess/go/vt/servenv" 34 "vitess.io/vitess/go/vt/sqlparser" 35 36 // Include deprecation warnings for soon-to-be-unsupported flag invocations. 37 _flag "vitess.io/vitess/go/internal/flag" 38 ) 39 40 var ( 41 ignores = [][]byte{ 42 []byte("#"), 43 []byte("/*"), 44 []byte("SET"), 45 []byte("use"), 46 []byte("BEGIN"), 47 []byte("COMMIT"), 48 []byte("ROLLBACK"), 49 } 50 bindIndex = 0 51 queries = make(map[string]int) 52 ) 53 54 type stat struct { 55 Query string 56 Count int 57 } 58 59 type stats []stat 60 61 func (a stats) Len() int { return len(a) } 62 func (a stats) Swap(i, j int) { a[i], a[j] = a[j], a[i] } 63 func (a stats) Less(i, j int) bool { return a[i].Count > a[j].Count } 64 65 func main() { 66 defer exit.Recover() 67 fs := pflag.NewFlagSet("query_analyzer", pflag.ExitOnError) 68 log.RegisterFlags(fs) 69 logutil.RegisterFlags(fs) 70 acl.RegisterFlags(fs) 71 servenv.RegisterMySQLServerFlags(fs) 72 _flag.Parse(fs) 73 logutil.PurgeLogs() 74 for _, filename := range _flag.Args() { 75 fmt.Printf("processing: %s\n", filename) 76 if err := processFile(filename); err != nil { 77 log.Errorf("processFile error: %v", err) 78 exit.Return(1) 79 } 80 } 81 var stats = make(stats, 0, 128) 82 for k, v := range queries { 83 stats = append(stats, stat{Query: k, Count: v}) 84 } 85 sort.Sort(stats) 86 for _, s := range stats { 87 fmt.Printf("%d: %s\n", s.Count, s.Query) 88 } 89 } 90 91 func processFile(filename string) error { 92 f, err := os.Open(filename) 93 if err != nil { 94 return err 95 } 96 r := bufio.NewReader(f) 97 for { 98 line, err := r.ReadBytes('\n') 99 if err != nil { 100 if err == io.EOF { 101 break 102 } 103 return err 104 } 105 analyze(line) 106 } 107 return nil 108 } 109 110 func analyze(line []byte) { 111 for _, ignore := range ignores { 112 if bytes.HasPrefix(line, ignore) { 113 return 114 } 115 } 116 dml := string(bytes.TrimRight(line, "\n")) 117 ast, err := sqlparser.Parse(dml) 118 if err != nil { 119 log.Errorf("Error parsing %s", dml) 120 return 121 } 122 bindIndex = 0 123 buf := sqlparser.NewTrackedBuffer(formatWithBind) 124 buf.Myprintf("%v", ast) 125 addQuery(buf.ParsedQuery().Query) 126 } 127 128 func formatWithBind(buf *sqlparser.TrackedBuffer, node sqlparser.SQLNode) { 129 v, ok := node.(*sqlparser.Literal) 130 if !ok { 131 node.Format(buf) 132 return 133 } 134 switch v.Type { 135 case sqlparser.StrVal, sqlparser.HexVal, sqlparser.IntVal: 136 buf.WriteArg(":", fmt.Sprintf("v%d", bindIndex)) 137 bindIndex++ 138 default: 139 node.Format(buf) 140 } 141 } 142 143 func addQuery(query string) { 144 count, ok := queries[query] 145 if !ok { 146 count = 0 147 } 148 queries[query] = count + 1 149 }