forked from oceanbase/ob-operator
-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathmanager.go
More file actions
115 lines (98 loc) · 3.51 KB
/
manager.go
File metadata and controls
115 lines (98 loc) · 3.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/*
Copyright (c) 2025 OceanBase
ob-operator is licensed under Mulan PSL v2.
You can use this software according to the terms and conditions of the Mulan PSL v2.
You may obtain a copy of Mulan PSL v2 at:
http://license.coscl.org.cn/MulanPSL2
THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
See the Mulan PSL v2 for more details.
*/
package analyzer
import (
"sync"
"time"
"github.com/antlr4-go/antlr/v4"
"github.com/oceanbase/ob-operator/internal/sql-analyzer/analyzer/rules"
"github.com/oceanbase/ob-operator/internal/sql-analyzer/api/model"
// Import the generated parser package.
// Note: This package path must match where 'make generate-parser' outputs the code.
obmysql "github.com/oceanbase/ob-operator/internal/sql-analyzer/parser/mysql"
logger "github.com/sirupsen/logrus"
)
type Manager struct {
rules []Rule
}
func NewManager() *Manager {
m := &Manager{
rules: []Rule{},
}
m.RegisterRules()
return m
}
func (m *Manager) RegisterRules() {
// Register all available rules here
m.rules = append(m.rules, rules.NewSelectAllRule())
m.rules = append(m.rules, rules.NewArithmeticRule())
m.rules = append(m.rules, rules.NewIsNullRule())
m.rules = append(m.rules, rules.NewLargeInClauseRule())
m.rules = append(m.rules, rules.NewMultiTableJoinRule())
m.rules = append(m.rules, rules.NewUpdateDeleteWithoutWhereRule())
m.rules = append(m.rules, rules.NewUpdateDeleteMultiTableRule())
m.rules = append(m.rules, rules.NewFullScanRule())
m.rules = append(m.rules, rules.NewIndexColumnFuzzyMatchRule())
m.rules = append(m.rules, rules.NewFunctionOnIndexedColumnRule())
}
func (m *Manager) Analyze(sql string, indexes []model.IndexInfo) []model.SqlDiagnoseInfo {
var diagnostics []model.SqlDiagnoseInfo
// Setup ANTLR input stream
inputStream := antlr.NewInputStream(sql)
// Create Lexer
lexer := obmysql.NewOBLexer(inputStream)
stream := antlr.NewCommonTokenStream(lexer, antlr.TokenDefaultChannel)
// Create Parser
p := obmysql.NewOBParser(stream)
// Add error listener to avoid printing to stdout
p.RemoveErrorListeners()
// Use SLL prediction mode for better performance, fallback to LL if it fails
p.GetInterpreter().SetPredictionMode(antlr.PredictionModeSLL)
p.SetErrorHandler(antlr.NewBailErrorStrategy())
// Parse the SQL (assuming 'Sql_stmt' is the entry point rule)
parseStart := time.Now()
var tree antlr.ParseTree
func() {
defer func() {
if r := recover(); r != nil {
// Fallback to LL prediction mode
stream.Seek(0)
p.SetTokenStream(stream)
p.GetInterpreter().SetPredictionMode(antlr.PredictionModeLL)
p.SetErrorHandler(antlr.NewDefaultErrorStrategy())
tree = p.Sql_stmt()
}
}()
tree = p.Sql_stmt()
}()
logger.Debugf("[Analyzer] Parse took %v", time.Since(parseStart))
// Run all registered rules
var mu sync.Mutex
var wg sync.WaitGroup
for _, rule := range m.rules {
wg.Add(1)
go func(r Rule) {
defer wg.Done()
results := r.Analyze(tree, indexes)
logger.Debugf("[Manager] Rule %s returned %d results", r.Name(), len(results))
if len(results) > 0 {
mu.Lock()
// Only keep the first result for each rule, currently each rule returns the same record, can be enriched to include the detailed sql statement part.
diagnostics = append(diagnostics, results[0])
mu.Unlock()
}
}(rule)
}
wg.Wait()
logger.Debugf("[Manager] Total diagnostics found: %d", len(diagnostics))
return diagnostics
}