36 Star 195 Fork 73

Gitee 极速下载/SOAR

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
此仓库是为了提升国内下载速度的镜像仓库,每日同步一次。 原始仓库: https://github.com/XiaoMi/soar
克隆/下载
heuristic.go 80.79 KB
一键复制 编辑 原始数据 按行查看 历史
Leon Zhang 提交于 2018-10-23 12:50 +08:00 . fix #18, #20
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256
/*
* Copyright 2018 Xiaomi, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package advisor
import (
"bytes"
"fmt"
"regexp"
"strconv"
"strings"
"github.com/XiaoMi/soar/ast"
"github.com/XiaoMi/soar/common"
"github.com/XiaoMi/soar/database"
"github.com/gedex/inflector"
"github.com/percona/go-mysql/query"
tidb "github.com/pingcap/tidb/ast"
"github.com/pingcap/tidb/mysql"
"github.com/pingcap/tidb/types"
"vitess.io/vitess/go/vt/sqlparser"
)
// RuleOK OK
func (q *Query4Audit) RuleOK() Rule {
return HeuristicRules["OK"]
}
// RuleImplicitAlias ALI.001
func (q *Query4Audit) RuleImplicitAlias() Rule {
var rule = q.RuleOK()
tkns := ast.Tokenizer(q.Query)
if len(tkns) == 0 {
return rule
}
if tkns[0].Type != sqlparser.SELECT {
return rule
}
for i, tkn := range tkns {
if tkn.Type == sqlparser.ID && i+1 < len(tkns) && tkn.Type == tkns[i+1].Type {
rule = HeuristicRules["ALI.001"]
break
}
}
return rule
}
// RuleStarAlias ALI.002
func (q *Query4Audit) RuleStarAlias() Rule {
var rule = q.RuleOK()
re := regexp.MustCompile(`(?i)(\*\s+as\b)`)
if re.FindString(q.Query) != "" {
rule = HeuristicRules["ALI.002"]
}
return rule
}
// RuleSameAlias ALI.003
func (q *Query4Audit) RuleSameAlias() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch expr := node.(type) {
case *sqlparser.AliasedExpr:
switch n := expr.Expr.(type) {
case *sqlparser.ColName:
if n.Name.String() == expr.As.String() {
rule = HeuristicRules["ALI.003"]
return false, nil
}
}
case *sqlparser.AliasedTableExpr:
switch n := expr.Expr.(type) {
case sqlparser.TableName:
if n.Name.String() == expr.As.String() {
rule = HeuristicRules["ALI.003"]
return false, nil
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RulePrefixLike ARG.001
func (q *Query4Audit) RulePrefixLike() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch expr := node.(type) {
case *sqlparser.ComparisonExpr:
if expr.Operator == "like" {
switch sqlval := expr.Right.(type) {
case *sqlparser.SQLVal:
// prefix like with '%', '_'
if sqlval.Type == 0 && (sqlval.Val[0] == 0x25 || sqlval.Val[0] == 0x5f) {
rule = HeuristicRules["ARG.001"]
return false, nil
}
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleEqualLike ARG.002
func (q *Query4Audit) RuleEqualLike() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch expr := node.(type) {
case *sqlparser.ComparisonExpr:
if expr.Operator == "like" {
switch sqlval := expr.Right.(type) {
case *sqlparser.SQLVal:
// not start with '%', '_' && not end with '%', '_'
if sqlval.Type == 0 {
if sqlval.Val[0] != 0x25 &&
sqlval.Val[0] != 0x5f &&
sqlval.Val[len(sqlval.Val)-1] != 0x5f &&
sqlval.Val[len(sqlval.Val)-1] != 0x25 {
rule = HeuristicRules["ARG.002"]
return false, nil
}
} else {
rule = HeuristicRules["ARG.002"]
return false, nil
}
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleImplicitConversion ARG.003
// 隐式类型转换检查:该项检查一定是在开启测试环境或线上环境情境下下进行的
func (idxAdv *IndexAdvisor) RuleImplicitConversion() Rule {
/*
* 两个参数至少有一个是 NULL 时,比较的结果也是 NULL,例外是使用 <=> 对两个 NULL 做比较时会返回 1,这两种情况都不需要做类型转换
* 两个参数都是字符串,会按照字符串来比较,不做类型转换
* 两个参数都是整数,按照整数来比较,不做类型转换
* 十六进制的值和非数字做比较时,会被当做二进制串
* 有一个参数是 TIMESTAMP 或 DATETIME,并且另外一个参数是常量,常量会被转换为 timestamp
* 有一个参数是 decimal 类型,如果另外一个参数是 decimal 或者整数,会将整数转换为 decimal 后进行比较,如果另外一个参数是浮点数,则会把 decimal 转换为浮点数进行比较
* 所有其他情况下,两个参数都会被转换为浮点数再进行比较
*/
rule := HeuristicRules["OK"]
// 未开启测试环境不进行检查
if common.Config.TestDSN.Disable {
return rule
}
var content string
conditions := ast.FindAllCondition(idxAdv.Ast)
for _, cond := range conditions {
var colList []*common.Column
var values []*sqlparser.SQLVal
// condition 左右两侧有且只有如下几种可能:
// 1. 左列 & 右列
// 2. 左列 & 右值(含函数) (或相反)
// 3. 左值(含函数) & 右值(含函数) (无需关注)
switch node := cond.(type) {
case *sqlparser.ComparisonExpr:
// 获取condition左侧的信息
switch nLeft := node.Left.(type) {
case *sqlparser.SQLVal, *sqlparser.ValTuple:
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch val := node.(type) {
case *sqlparser.SQLVal:
values = append(values, val)
}
return true, nil
}, nLeft)
common.LogIfError(err, "")
case *sqlparser.ColName:
left := &common.Column{Name: nLeft.Name.String()}
if !nLeft.Qualifier.Name.IsEmpty() {
left.Table = nLeft.Qualifier.Name.String()
}
colList = append(colList, left)
}
// 获取condition右侧的信息
switch nRight := node.Right.(type) {
case *sqlparser.SQLVal, *sqlparser.ValTuple:
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch val := node.(type) {
case *sqlparser.SQLVal:
values = append(values, val)
}
return true, nil
}, nRight)
common.LogIfError(err, "")
case *sqlparser.ColName:
right := &common.Column{Name: nRight.Name.String()}
if !nRight.Qualifier.Name.IsEmpty() {
right.Table = nRight.Qualifier.Name.String()
}
colList = append(colList, right)
}
if len(colList) == 0 {
continue
}
// 补全列信息
colList = CompleteColumnsInfo(idxAdv.Ast, colList, idxAdv.vEnv)
// 列与列比较
if len(colList) == 2 {
// 列信息补全后如果依然没有表信息,说明在该数据库中不存在该列
// 如果列信息获取异常,可能会存在无法获取到数据类型的情况,对于这种情况将不会给予建议。
needBreak := false
for _, col := range colList {
if col.Table == "" {
common.Log.Warning("Column %s not exists", col.Name)
needBreak = true
}
if col.DataType == "" {
common.Log.Warning("Can't get column %s data type", col.Name)
needBreak = true
}
}
if needBreak {
break
}
// 检查数据类型不一致导致的隐式数据转换
type1 := common.GetDataTypeBase(colList[0].DataType)
type2 := common.GetDataTypeBase(colList[1].DataType)
common.Log.Debug("DataType: `%s`.`%s` (%s) VS `%s`.`%s` (%s)",
colList[0].Table, colList[0].Name, type1,
colList[1].Table, colList[1].Name, type2)
if strings.ToLower(type1) != strings.ToLower(type2) {
content += fmt.Sprintf("`%s`.`%s` (%s) VS `%s`.`%s` (%s) datatype not match",
colList[0].Table, colList[0].Name, type1,
colList[1].Table, colList[1].Name, type2)
continue
}
// 检查字符集不一致导致的隐式数据转换
common.Log.Debug("Charset: `%s`.`%s` (%s) VS `%s`.`%s` (%s)",
colList[0].Table, colList[0].Name, colList[0].Character,
colList[1].Table, colList[1].Name, colList[1].Character)
if colList[0].Character != colList[1].Character {
content += fmt.Sprintf("`%s`.`%s` (%s) VS `%s`.`%s` (%s) charset not match",
colList[0].Table, colList[0].Name, colList[0].Character,
colList[1].Table, colList[1].Name, colList[1].Character)
continue
}
// 检查排序排序不一致导致的隐式数据转换
common.Log.Debug("Collation: `%s`.`%s` (%s) VS `%s`.`%s` (%s)",
colList[0].Table, colList[0].Name, colList[0].Collation,
colList[1].Table, colList[1].Name, colList[1].Collation)
if colList[0].Collation != colList[1].Collation {
content += fmt.Sprintf("`%s`.`%s` (%s) VS `%s`.`%s` (%s) collation not match",
colList[0].Table, colList[0].Name, colList[0].Collation,
colList[1].Table, colList[1].Name, colList[1].Collation)
continue
}
}
typMap := map[sqlparser.ValType][]string{
// date, time, datetime, timestamp, year
sqlparser.StrVal: {
"char", "varchar", "tinytext", "text", "mediumtext", "longtext",
"date", "time", "datetime", "timestamp", "year",
},
sqlparser.IntVal: {
"tinyint", "smallint", "mediumint", "int", "integer", "bigint", "timestamp", "year",
},
sqlparser.FloatVal: {
"float", "double", "real", "decimal",
},
}
typNameMap := map[sqlparser.ValType]string{
sqlparser.StrVal: "string",
sqlparser.IntVal: "int",
sqlparser.FloatVal: "float",
}
// 列与值比较
for _, val := range values {
if colList[0].DataType == "" {
common.Log.Debug("Can't get %s datatype", colList[0].Name)
break
}
isCovered := true
if types, ok := typMap[val.Type]; ok {
for _, t := range types {
if strings.HasPrefix(colList[0].DataType, t) {
isCovered = false
}
}
}
if isCovered {
if colList[0].Table == "" {
common.Log.Warning("Column %s not exists", colList[0].Name)
continue
}
c := fmt.Sprintf("%s.%s definition is %s not %s",
colList[0].Table, colList[0].Name, colList[0].DataType, typNameMap[val.Type])
common.Log.Debug("Implicit data type conversion: %s", c)
content += c
}
}
case *sqlparser.RangeCond:
// TODO
case *sqlparser.IsExpr:
// TODO
}
}
if content != "" {
rule = HeuristicRules["ARG.003"]
rule.Content = content
}
return rule
}
// RuleNoWhere CLA.001 & CLA.014 & CLA.015
func (q *Query4Audit) RuleNoWhere() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.Select:
if n.Where == nil && sqlparser.String(n.From) != "dual" {
rule = HeuristicRules["CLA.001"]
return false, nil
}
case *sqlparser.Delete:
if n.Where == nil {
rule = HeuristicRules["CLA.014"]
return false, nil
}
case *sqlparser.Update:
if n.Where == nil {
rule = HeuristicRules["CLA.015"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleOrderByRand CLA.002
func (q *Query4Audit) RuleOrderByRand() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case sqlparser.OrderBy:
for _, order := range n {
switch expr := order.Expr.(type) {
case *sqlparser.FuncExpr:
if expr.Name.String() == "rand" {
rule = HeuristicRules["CLA.002"]
return false, nil
}
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleOffsetLimit CLA.003
func (q *Query4Audit) RuleOffsetLimit() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.Limit:
if n != nil && n.Offset != nil {
switch v := n.Offset.(type) {
case *sqlparser.SQLVal:
offset, err := strconv.Atoi(string(v.Val))
// 检查一下Offset阈值,太小了给这个建议也没什么用,阈值写死了没加配置
if err == nil && offset > 1000 {
rule = HeuristicRules["CLA.003"]
return false, nil
}
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleGroupByConst CLA.004
func (q *Query4Audit) RuleGroupByConst() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case sqlparser.GroupBy:
for _, group := range n {
switch group.(type) {
case *sqlparser.SQLVal:
rule = HeuristicRules["CLA.004"]
return false, nil
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleGroupByConst GRP.001
func (idxAdv *IndexAdvisor) RuleGroupByConst() Rule {
rule := HeuristicRules["OK"]
// 非GroupBy语句
if len(idxAdv.groupBy) == 0 || len(idxAdv.whereEQ) == 0 {
return rule
}
for _, groupByCols := range idxAdv.groupBy {
for _, whereEQCols := range idxAdv.whereEQ {
if (groupByCols.Name == whereEQCols.Name) &&
(groupByCols.DB == whereEQCols.DB) &&
(groupByCols.Table == whereEQCols.Table) {
rule = HeuristicRules["GRP.001"]
break
}
}
}
return rule
}
// RuleOrderByConst CLA.005
func (q *Query4Audit) RuleOrderByConst() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case sqlparser.OrderBy:
for _, order := range n {
switch order.Expr.(type) {
case *sqlparser.SQLVal:
rule = HeuristicRules["CLA.005"]
return false, nil
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleOrderByConst CLA.005
// TODO: SELECT col FROM tbl WHERE col IN('NEWS') ORDER BY col;
func (idxAdv *IndexAdvisor) RuleOrderByConst() Rule {
rule := HeuristicRules["OK"]
// 非GroupBy语句
if len(idxAdv.orderBy) == 0 || len(idxAdv.whereEQ) == 0 {
return rule
}
for _, groupbyCols := range idxAdv.orderBy {
for _, whereEQCols := range idxAdv.whereEQ {
if (groupbyCols.Name == whereEQCols.Name) &&
(groupbyCols.DB == whereEQCols.DB) &&
(groupbyCols.Table == whereEQCols.Table) {
rule = HeuristicRules["CLA.005"]
break
}
}
}
return rule
}
// RuleDiffGroupByOrderBy CLA.006
func (q *Query4Audit) RuleDiffGroupByOrderBy() Rule {
var rule = q.RuleOK()
var groupbyTbls []sqlparser.TableIdent
var orderbyTbls []sqlparser.TableIdent
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case sqlparser.GroupBy:
// 检查group by涉及到表的个数
for _, group := range n {
switch g := group.(type) {
case *sqlparser.ColName:
tblExist := false
for _, t := range groupbyTbls {
if t.String() == g.Qualifier.Name.String() {
tblExist = true
}
}
if !tblExist {
groupbyTbls = append(groupbyTbls, g.Qualifier.Name)
if len(groupbyTbls) > 1 {
rule = HeuristicRules["CLA.006"]
return false, nil
}
}
}
}
case sqlparser.OrderBy:
// 检查order by涉及到表的个数
for _, order := range n {
switch o := order.Expr.(type) {
case *sqlparser.ColName:
tblExist := false
for _, t := range orderbyTbls {
if t.String() == o.Qualifier.Name.String() {
tblExist = true
}
}
if !tblExist {
orderbyTbls = append(orderbyTbls, o.Qualifier.Name)
if len(orderbyTbls) > 1 {
rule = HeuristicRules["CLA.006"]
return false, nil
}
}
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
if rule.Item == "OK" {
// 检查group by, order by涉及到表的个数
for _, g := range groupbyTbls {
tblExist := false
for _, o := range orderbyTbls {
if g.String() == o.String() {
tblExist = true
}
}
if !tblExist && len(orderbyTbls) > 0 {
rule = HeuristicRules["CLA.006"]
return rule
}
}
}
return rule
}
// RuleMixOrderBy CLA.007
func (q *Query4Audit) RuleMixOrderBy() Rule {
var rule = q.RuleOK()
var direction string
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case sqlparser.OrderBy:
for _, order := range n {
// 比较相邻两个order by列的方向
if direction != "" && order.Direction != direction {
rule = HeuristicRules["CLA.007"]
return false, nil
}
direction = order.Direction
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleExplicitOrderBy CLA.008
func (q *Query4Audit) RuleExplicitOrderBy() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.Select:
// 有group by,但没有order by
if n.GroupBy != nil && n.OrderBy == nil {
rule = HeuristicRules["CLA.008"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleOrderByExpr CLA.009
func (q *Query4Audit) RuleOrderByExpr() Rule {
var rule = q.RuleOK()
var orderByCols []string
var selectCols []string
funcExp := regexp.MustCompile(`[a-z0-9]\(`)
allowExp := regexp.MustCompile("[a-z0-9_,.` ()]")
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case sqlparser.OrderBy:
orderBy := sqlparser.String(n)
// 函数名方式,如:from_unixtime(col)
if funcExp.MatchString(orderBy) {
rule = HeuristicRules["CLA.009"]
return false, nil
}
// 运算符方式,如:colA - colB
trim := allowExp.ReplaceAllFunc([]byte(orderBy), func(s []byte) []byte {
return []byte("")
})
if string(trim) != "" {
rule = HeuristicRules["CLA.009"]
return false, nil
}
for _, o := range strings.Split(strings.TrimPrefix(orderBy, " order by "), ",") {
orderByCols = append(orderByCols, strings.TrimSpace(strings.Split(o, " ")[0]))
}
case *sqlparser.Select:
for _, s := range n.SelectExprs {
selectCols = append(selectCols, sqlparser.String(s))
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
// AS情况,如:SELECT colA-colB a FROM tbl ORDER BY a;
for _, o := range orderByCols {
if o == "" {
continue
}
for _, s := range selectCols {
if strings.HasSuffix(s, " as "+o) {
buf := strings.TrimSuffix(s, " as "+o)
// 运算符
trim := allowExp.ReplaceAllFunc([]byte(buf), func(s []byte) []byte {
return []byte("")
})
if string(trim) != "" {
rule = HeuristicRules["CLA.009"]
}
// 函数
if funcExp.MatchString(s) {
rule = HeuristicRules["CLA.009"]
}
}
}
}
return rule
}
// RuleGroupByExpr CLA.010
func (q *Query4Audit) RuleGroupByExpr() Rule {
var rule = q.RuleOK()
var groupByCols []string
var selectCols []string
funcExp := regexp.MustCompile(`(?i)[a-z0-9]\(`)
allowExp := regexp.MustCompile("(?i)[a-z0-9_,.` ()]")
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case sqlparser.GroupBy:
groupBy := sqlparser.String(n)
// 函数名方式,如:from_unixtime(col)
if funcExp.MatchString(groupBy) {
rule = HeuristicRules["CLA.010"]
return false, nil
}
// 运算符方式,如:colA - colB
trim := allowExp.ReplaceAllFunc([]byte(groupBy), func(s []byte) []byte {
return []byte("")
})
if string(trim) != "" {
rule = HeuristicRules["CLA.010"]
return false, nil
}
for _, o := range strings.Split(strings.TrimPrefix(groupBy, " group by "), ",") {
groupByCols = append(groupByCols, strings.TrimSpace(strings.Split(o, " ")[0]))
}
case *sqlparser.Select:
for _, s := range n.SelectExprs {
selectCols = append(selectCols, sqlparser.String(s))
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
// AS情况,如:SELECT colA-colB a FROM tbl GROUP BY a;
for _, g := range groupByCols {
if g == "" {
continue
}
for _, s := range selectCols {
if strings.HasSuffix(s, " as "+g) {
buf := strings.TrimSuffix(s, " as "+g)
// 运算符
trim := allowExp.ReplaceAllFunc([]byte(buf), func(s []byte) []byte {
return []byte("")
})
if string(trim) != "" {
rule = HeuristicRules["CLA.010"]
}
// 函数
if funcExp.MatchString(s) {
rule = HeuristicRules["CLA.010"]
}
}
}
}
return rule
}
// RuleTblCommentCheck CLA.011
func (q *Query4Audit) RuleTblCommentCheck() Rule {
var rule = q.RuleOK()
switch node := q.Stmt.(type) {
case *sqlparser.DDL:
if node.Action != "create" {
return rule
}
if node.TableSpec == nil {
return rule
}
if options := node.TableSpec.Options; options == "" {
rule = HeuristicRules["CLA.011"]
} else {
reg := regexp.MustCompile("(?i)comment")
if !reg.MatchString(options) {
rule = HeuristicRules["CLA.011"]
}
}
}
return rule
}
// RuleSelectStar COL.001
func (q *Query4Audit) RuleSelectStar() Rule {
var rule = q.RuleOK()
// 先把count(*)替换为count(1)
re := regexp.MustCompile(`(?i)count\s*\(\s*\*\s*\)`)
sql := re.ReplaceAllString(q.Query, "count(1)")
stmt, err := sqlparser.Parse(sql)
if err != nil {
common.Log.Debug("RuleSelectStar sqlparser.Parse Error: %v", err)
return rule
}
err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.StarExpr:
rule = HeuristicRules["COL.001"]
return false, nil
}
return true, nil
}, stmt)
common.LogIfError(err, "")
return rule
}
// RuleInsertColDef COL.002
func (q *Query4Audit) RuleInsertColDef() Rule {
var rule = q.RuleOK()
switch node := q.Stmt.(type) {
case *sqlparser.Insert:
if node.Columns == nil {
rule = HeuristicRules["COL.002"]
return rule
}
}
return rule
}
// RuleAddDefaultValue COL.004
func (q *Query4Audit) RuleAddDefaultValue() Rule {
var rule = q.RuleOK()
for _, node := range q.TiStmt {
switch n := node.(type) {
case *tidb.CreateTableStmt:
for _, c := range n.Cols {
colDefault := false
for _, o := range c.Options {
// 忽略AutoIncrement类型的默认值检查
if o.Tp == tidb.ColumnOptionDefaultValue || o.Tp == tidb.ColumnOptionAutoIncrement {
colDefault = true
}
}
if !colDefault {
rule = HeuristicRules["COL.004"]
break
}
}
case *tidb.AlterTableStmt:
for _, s := range n.Specs {
switch s.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn, tidb.AlterTableModifyColumn:
for _, c := range s.NewColumns {
colDefault := false
for _, o := range c.Options {
// 忽略AutoIncrement类型的默认值检查
if o.Tp == tidb.ColumnOptionDefaultValue || o.Tp == tidb.ColumnOptionAutoIncrement {
colDefault = true
}
}
if !colDefault {
rule = HeuristicRules["COL.004"]
break
}
}
}
}
}
}
return rule
}
// RuleColCommentCheck COL.005
func (q *Query4Audit) RuleColCommentCheck() Rule {
var rule = q.RuleOK()
for _, node := range q.TiStmt {
switch n := node.(type) {
case *tidb.CreateTableStmt:
for _, c := range n.Cols {
colComment := false
for _, o := range c.Options {
if o.Tp == tidb.ColumnOptionComment {
colComment = true
}
}
if !colComment {
rule = HeuristicRules["COL.005"]
break
}
}
case *tidb.AlterTableStmt:
for _, s := range n.Specs {
switch s.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn, tidb.AlterTableModifyColumn:
for _, c := range s.NewColumns {
colComment := false
for _, o := range c.Options {
if o.Tp == tidb.ColumnOptionComment {
colComment = true
}
}
if !colComment {
rule = HeuristicRules["COL.005"]
break
}
}
}
}
}
}
return rule
}
// RuleIPString LIT.001
func (q *Query4Audit) RuleIPString() Rule {
var rule = q.RuleOK()
re := regexp.MustCompile(`['"]\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}`)
if re.FindString(q.Query) != "" {
rule = HeuristicRules["LIT.001"]
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RuleDataNotQuote LIT.002
func (q *Query4Audit) RuleDataNotQuote() Rule {
var rule = q.RuleOK()
// 2010-01-01
re := regexp.MustCompile(`.\d{4}\s*-\s*\d{1,2}\s*-\s*\d{1,2}\b`)
sqls := re.FindAllString(q.Query, -1)
for _, sql := range sqls {
re = regexp.MustCompile(`^['"\w-].*`)
if re.FindString(sql) == "" {
rule = HeuristicRules["LIT.002"]
}
}
// 10-01-01
re = regexp.MustCompile(`.\d{2}\s*-\s*\d{1,2}\s*-\s*\d{1,2}\b`)
sqls = re.FindAllString(q.Query, -1)
for _, sql := range sqls {
re = regexp.MustCompile(`^['"\w-].*`)
if re.FindString(sql) == "" {
rule = HeuristicRules["LIT.002"]
}
}
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
return rule
}
// RuleSQLCalcFoundRows KWR.001
func (q *Query4Audit) RuleSQLCalcFoundRows() Rule {
var rule = q.RuleOK()
tkns := ast.Tokenizer(q.Query)
for _, tkn := range tkns {
if tkn.Val == "sql_calc_found_rows" {
rule = HeuristicRules["KWR.001"]
break
}
}
return rule
}
// RuleCommaAnsiJoin JOI.001
func (q *Query4Audit) RuleCommaAnsiJoin() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.Select:
ansiJoin := false
commaJoin := false
for _, f := range n.From {
switch f.(type) {
case *sqlparser.JoinTableExpr:
ansiJoin = true
case *sqlparser.AliasedTableExpr:
commaJoin = true
}
}
if ansiJoin && commaJoin {
rule = HeuristicRules["JOI.001"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleDupJoin JOI.002
func (q *Query4Audit) RuleDupJoin() Rule {
var rule = q.RuleOK()
var tables []string
switch q.Stmt.(type) {
// TODO: 这里未检查UNION SELECT
case *sqlparser.Union:
return rule
default:
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.AliasedTableExpr:
switch table := n.Expr.(type) {
case sqlparser.TableName:
for _, t := range tables {
if t == table.Name.String() {
rule = HeuristicRules["JOI.002"]
return false, nil
}
}
tables = append(tables, table.Name.String())
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
}
return rule
}
// RuleImpossibleOuterJoin JOI.003
// TODO: 未实现完
func (idxAdv *IndexAdvisor) RuleImpossibleOuterJoin() Rule {
rule := HeuristicRules["OK"]
var joinTables []string // JOIN相关表名
var whereEQTables []string // WHERE等值判断条件表名
var joinNotWhereTables []string // 是JOIN相关表,但未出现在WHERE等值判断条件中的表名
// 非JOIN语句
if len(idxAdv.joinCond) == 0 || len(idxAdv.whereEQ) == 0 {
return rule
}
for _, l1 := range idxAdv.joinCond {
for _, l2 := range l1 {
if l2.Table != "" && l2.Table != "dual" {
joinTables = append(joinTables, l2.Table)
}
}
}
for _, w := range idxAdv.whereEQ {
whereEQTables = append(whereEQTables, w.Table)
}
for _, j := range joinTables {
found := false
for _, w := range whereEQTables {
if j == w {
found = true
}
}
if !found {
joinNotWhereTables = append(joinNotWhereTables, j)
}
}
// TODO:
fmt.Println(joinNotWhereTables)
/*
if len(joinNotWhereTables) == 0 {
rule = HeuristicRules["JOI.003"]
}
*/
rule = HeuristicRules["JOI.003"]
return rule
}
// TODO: JOI.004
// RuleNoDeterministicGroupby RES.001
func (q *Query4Audit) RuleNoDeterministicGroupby() Rule {
var rule = q.RuleOK()
var groupbyCols []*common.Column
var selectCols []*common.Column
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.Select:
// 过滤select列
selectCols = ast.FindColumn(n.SelectExprs)
// 过滤group by列
groupbyCols = ast.FindColumn(n.GroupBy)
// `select *`, but not `select count(*)`
if strings.Contains(sqlparser.String(n), " * ") && len(groupbyCols) > 0 {
rule = HeuristicRules["RES.001"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
// TODO:暂时只检查了列名,未对库表名进行检查,也未处理AS
for _, s := range selectCols {
// 无group by退出
if len(groupbyCols) == 0 {
break
}
found := false
for _, g := range groupbyCols {
if g.Name == s.Name {
found = true
}
}
if !found {
rule = HeuristicRules["RES.001"]
break
}
}
return rule
}
// RuleNoDeterministicLimit RES.002
func (q *Query4Audit) RuleNoDeterministicLimit() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.Select:
if n.Limit != nil && n.OrderBy == nil {
rule = HeuristicRules["RES.002"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleUpdateDeleteWithLimit RES.003
func (q *Query4Audit) RuleUpdateDeleteWithLimit() Rule {
var rule = q.RuleOK()
switch s := q.Stmt.(type) {
case *sqlparser.Update:
if s.Limit != nil {
rule = HeuristicRules["RES.003"]
}
}
return rule
}
// RuleUpdateDeleteWithOrderby RES.004
func (q *Query4Audit) RuleUpdateDeleteWithOrderby() Rule {
var rule = q.RuleOK()
switch s := q.Stmt.(type) {
case *sqlparser.Update:
if s.OrderBy != nil {
rule = HeuristicRules["RES.004"]
}
}
return rule
}
// RuleUpdateSetAnd RES.005
func (q *Query4Audit) RuleUpdateSetAnd() Rule {
var rule = q.RuleOK()
switch s := q.Stmt.(type) {
case *sqlparser.Update:
if strings.Contains(sqlparser.String(s.Exprs), " and ") {
rule = HeuristicRules["RES.005"]
}
}
return rule
}
// RuleImpossibleWhere RES.006
func (q *Query4Audit) RuleImpossibleWhere() Rule {
var rule = q.RuleOK()
// BETWEEN 10 AND 5
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.RangeCond:
if n.Operator == "between" {
from := 0
to := 0
switch s := n.From.(type) {
case *sqlparser.SQLVal:
from, _ = strconv.Atoi(string(s.Val))
}
switch s := n.To.(type) {
case *sqlparser.SQLVal:
to, _ = strconv.Atoi(string(s.Val))
}
if from > to {
rule = HeuristicRules["RES.006"]
return false, nil
}
}
case *sqlparser.ComparisonExpr:
factor := false
switch n.Operator {
case "!=", "<>":
case "=", "<=>":
factor = true
default:
return true, nil
}
var left []byte
var right []byte
// left
switch l := n.Left.(type) {
case *sqlparser.SQLVal:
left = l.Val
default:
return true, nil
}
// right
switch r := n.Right.(type) {
case *sqlparser.SQLVal:
right = r.Val
default:
return true, nil
}
// compare
if (!bytes.Equal(left, right) && factor) || (bytes.Equal(left, right) && !factor) {
rule = HeuristicRules["RES.006"]
}
return false, nil
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleMeaninglessWhere RES.007
func (q *Query4Audit) RuleMeaninglessWhere() Rule {
var rule = q.RuleOK()
// 1=1, 0=0
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.ComparisonExpr:
factor := false
switch n.Operator {
case "!=", "<>":
factor = true
case "=", "<=>":
default:
return true, nil
}
var left []byte
var right []byte
// left
switch l := n.Left.(type) {
case *sqlparser.SQLVal:
left = l.Val
default:
return true, nil
}
// right
switch r := n.Right.(type) {
case *sqlparser.SQLVal:
right = r.Val
default:
return true, nil
}
// compare
if (bytes.Equal(left, right) && !factor) || (!bytes.Equal(left, right) && factor) {
rule = HeuristicRules["RES.007"]
}
return false, nil
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleLoadFile RES.008
func (q *Query4Audit) RuleLoadFile() Rule {
var rule = q.RuleOK()
// 去除注释
sql := string(database.RemoveSQLComments([]byte(q.Query)))
// 去除多余的空格和回车
sql = strings.Join(strings.Fields(sql), " ")
tks := ast.Tokenize(sql)
for i, tk := range tks {
// 注意:每个关键字token的结尾是带空格的,这里偷懒没trimspace直接加空格比较
// LOAD DATA...
if strings.ToLower(tk.Val) == "load " && i+1 < len(tks) &&
strings.ToLower(tks[i+1].Val) == "data " {
rule = HeuristicRules["RES.008"]
break
}
// SELECT ... INTO OUTFILE
if strings.ToLower(tk.Val) == "into " && i+1 < len(tks) &&
(strings.ToLower(tks[i+1].Val) == "outfile " || strings.ToLower(tks[i+1].Val) == "dumpfile ") {
rule = HeuristicRules["RES.008"]
break
}
}
return rule
}
// RuleStandardINEQ STA.001
func (q *Query4Audit) RuleStandardINEQ() Rule {
var rule = q.RuleOK()
re := regexp.MustCompile(`(!=)`)
if re.FindString(q.Query) != "" {
rule = HeuristicRules["STA.001"]
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RuleUseKeyWord KWR.002
func (q *Query4Audit) RuleUseKeyWord() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
if q.TiStmt == nil {
common.Log.Error("TiStmt is nil, SQL: %s", q.Query)
return rule
}
for _, tiStmtNode := range q.TiStmt {
switch stmt := tiStmtNode.(type) {
case *tidb.AlterTableStmt:
// alter
for _, spec := range stmt.Specs {
for _, column := range spec.NewColumns {
if ast.IsMysqlKeyword(column.Name.String()) {
return HeuristicRules["KWR.002"]
}
}
}
case *tidb.CreateTableStmt:
// create
if ast.IsMysqlKeyword(stmt.Table.Name.String()) {
return HeuristicRules["KWR.002"]
}
for _, col := range stmt.Cols {
if ast.IsMysqlKeyword(col.Name.String()) {
return HeuristicRules["KWR.002"]
}
}
}
}
}
return rule
}
// RulePluralWord KWR.003
// Reference: https://en.wikipedia.org/wiki/English_plurals
func (q *Query4Audit) RulePluralWord() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
if q.TiStmt == nil {
common.Log.Error("TiStmt is nil, SQL: %s", q.Query)
return rule
}
for _, tiStmtNode := range q.TiStmt {
switch stmt := tiStmtNode.(type) {
case *tidb.AlterTableStmt:
// alter
for _, spec := range stmt.Specs {
for _, column := range spec.NewColumns {
if inflector.Singularize(column.Name.String()) != column.Name.String() {
return HeuristicRules["KWR.003"]
}
}
}
case *tidb.CreateTableStmt:
// create
if inflector.Singularize(stmt.Table.Name.String()) != stmt.Table.Name.String() {
return HeuristicRules["KWR.003"]
}
for _, col := range stmt.Cols {
if inflector.Singularize(col.Name.String()) != col.Name.String() {
return HeuristicRules["KWR.003"]
}
}
}
}
}
return rule
}
// RuleInsertSelect LCK.001
func (q *Query4Audit) RuleInsertSelect() Rule {
var rule = q.RuleOK()
switch n := q.Stmt.(type) {
case *sqlparser.Insert:
switch n.Rows.(type) {
case *sqlparser.Select:
rule = HeuristicRules["LCK.001"]
}
}
return rule
}
// RuleInsertOnDup LCK.002
func (q *Query4Audit) RuleInsertOnDup() Rule {
var rule = q.RuleOK()
switch n := q.Stmt.(type) {
case *sqlparser.Insert:
if n.OnDup != nil {
rule = HeuristicRules["LCK.002"]
return rule
}
}
return rule
}
// RuleInSubquery SUB.001
func (q *Query4Audit) RuleInSubquery() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.Subquery:
rule = HeuristicRules["SUB.001"]
return false, nil
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleSubqueryDepth SUB.004
func (q *Query4Audit) RuleSubqueryDepth() Rule {
var rule = q.RuleOK()
if depth := ast.GetSubqueryDepth(q.Stmt); depth > common.Config.MaxSubqueryDepth {
rule = HeuristicRules["SUB.004"]
}
return rule
}
// RuleSubQueryLimit SUB.005
// 只有IN的SUBQUERY限制了LIMIT,FROM子句中的SUBQUERY并未限制LIMIT
func (q *Query4Audit) RuleSubQueryLimit() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.ComparisonExpr:
if n.Operator == "in" {
switch r := n.Right.(type) {
case *sqlparser.Subquery:
switch s := r.Select.(type) {
case *sqlparser.Select:
if s.Limit != nil {
rule = HeuristicRules["SUB.005"]
return false, nil
}
}
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleSubQueryFunctions SUB.006
func (q *Query4Audit) RuleSubQueryFunctions() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.Subquery:
err = sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.FuncExpr:
rule = HeuristicRules["SUB.006"]
return false, nil
}
return true, nil
}, node)
common.LogIfError(err, "")
}
if rule.Item == "OK" {
return true, nil
}
return false, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleMultiValueAttribute LIT.003
func (q *Query4Audit) RuleMultiValueAttribute() Rule {
var rule = q.RuleOK()
re := regexp.MustCompile(`(?i)(id\s+varchar)|(id\s+text)|(id\s+regexp)`)
if re.FindString(q.Query) != "" {
rule = HeuristicRules["LIT.003"]
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RuleAddDelimiter LIT.004
func (q *Query4Audit) RuleAddDelimiter() Rule {
var rule = q.RuleOK()
re := regexp.MustCompile(`(?i)(^use\s+[0-9a-z_-]*)|(^show\s+databases)`)
if re.FindString(q.Query) != "" && !strings.HasSuffix(q.Query, common.Config.Delimiter) {
rule = HeuristicRules["LIT.004"]
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RuleRecursiveDependency KEY.003
func (q *Query4Audit) RuleRecursiveDependency() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
// create statement
for _, ref := range node.Constraints {
if ref != nil && ref.Tp == tidb.ConstraintForeignKey {
rule = HeuristicRules["KEY.003"]
}
}
case *tidb.AlterTableStmt:
// alter table statement
for _, spec := range node.Specs {
if spec.Constraint != nil && spec.Constraint.Tp == tidb.ConstraintForeignKey {
rule = HeuristicRules["KEY.003"]
}
}
}
}
}
if rule.Item == "KEY.003" {
re := regexp.MustCompile(`(?i)(\s+references\s+)`)
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RuleImpreciseDataType COL.009
func (q *Query4Audit) RuleImpreciseDataType() Rule {
var rule = q.RuleOK()
if q.TiStmt != nil {
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
// Create table statement
for _, col := range node.Cols {
switch col.Tp.Tp {
case mysql.TypeFloat, mysql.TypeDouble, mysql.TypeDecimal, mysql.TypeNewDecimal:
rule = HeuristicRules["COL.009"]
}
}
case *tidb.AlterTableStmt:
// Alter table statement
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
switch col.Tp.Tp {
case mysql.TypeFloat, mysql.TypeDouble,
mysql.TypeDecimal, mysql.TypeNewDecimal:
rule = HeuristicRules["COL.009"]
}
}
}
}
case *tidb.InsertStmt:
// Insert statement
for _, values := range node.Lists {
for _, value := range values {
switch value.GetDatum().Kind() {
case types.KindFloat32, types.KindFloat64, types.KindMysqlDecimal:
rule = HeuristicRules["COL.009"]
}
}
}
case *tidb.SelectStmt:
// Select statement
switch where := node.Where.(type) {
case *tidb.BinaryOperationExpr:
switch where.R.GetDatum().Kind() {
case types.KindFloat32, types.KindFloat64, types.KindMysqlDecimal:
rule = HeuristicRules["COL.009"]
}
}
}
}
}
return rule
}
// RuleValuesInDefinition COL.010
func (q *Query4Audit) RuleValuesInDefinition() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
switch col.Tp.Tp {
case mysql.TypeSet, mysql.TypeEnum, mysql.TypeBit:
rule = HeuristicRules["COL.010"]
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
switch col.Tp.Tp {
case mysql.TypeSet, mysql.TypeEnum, mysql.TypeBit:
rule = HeuristicRules["COL.010"]
}
}
}
}
}
}
}
return rule
}
// RuleIndexAttributeOrder KEY.004
func (q *Query4Audit) RuleIndexAttributeOrder() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateIndexStmt:
if len(node.IndexColNames) > 1 {
rule = HeuristicRules["KEY.004"]
break
}
case *tidb.CreateTableStmt:
for _, constraint := range node.Constraints {
// 当一条索引中包含多个列的时候给予建议
if len(constraint.Keys) > 1 {
rule = HeuristicRules["KEY.004"]
break
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
if spec.Tp == tidb.AlterTableAddConstraint && len(spec.Constraint.Keys) > 1 {
rule = HeuristicRules["KEY.004"]
break
}
}
}
}
}
return rule
}
// RuleNullUsage COL.011
func (q *Query4Audit) RuleNullUsage() Rule {
var rule = q.RuleOK()
re := regexp.MustCompile(`(?i)(\s+null\s+)`)
if re.FindString(q.Query) != "" {
rule = HeuristicRules["COL.011"]
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RuleStringConcatenation FUN.003
func (q *Query4Audit) RuleStringConcatenation() Rule {
var rule = q.RuleOK()
re := regexp.MustCompile(`(?i)(\|\|)`)
if re.FindString(q.Query) != "" {
rule = HeuristicRules["FUN.003"]
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RuleSysdate FUN.004
func (q *Query4Audit) RuleSysdate() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.FuncExpr:
if n.Name.String() == "sysdate" {
rule = HeuristicRules["FUN.004"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleCountConst FUN.005
func (q *Query4Audit) RuleCountConst() Rule {
var rule = q.RuleOK()
fingerprint := query.Fingerprint(q.Query)
countReg := regexp.MustCompile(`(?i)count\(\s*[0-9a-z?]*\s*\)`)
if countReg.MatchString(fingerprint) {
rule = HeuristicRules["FUN.005"]
if position := countReg.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RuleSumNPE FUN.006
func (q *Query4Audit) RuleSumNPE() Rule {
var rule = q.RuleOK()
fingerprint := query.Fingerprint(q.Query)
sumReg := regexp.MustCompile(`(?i)sum\(\s*[0-9a-z?]*\s*\)`)
isnullReg := regexp.MustCompile(`(?i)isnull\(sum\(\s*[0-9a-z?]*\s*\)\)`)
if sumReg.MatchString(fingerprint) && !isnullReg.MatchString(fingerprint) {
rule = HeuristicRules["FUN.006"]
if position := isnullReg.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
return rule
}
// RulePatternMatchingUsage ARG.007
func (q *Query4Audit) RulePatternMatchingUsage() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.Select:
re := regexp.MustCompile(`(?i)(\bregexp\b)|(\bsimilar to\b)`)
if re.FindString(q.Query) != "" {
rule = HeuristicRules["ARG.007"]
}
}
return rule
}
// RuleSpaghettiQueryAlert CLA.012
func (q *Query4Audit) RuleSpaghettiQueryAlert() Rule {
var rule = q.RuleOK()
if len(query.Fingerprint(q.Query)) > common.Config.SpaghettiQueryLength {
rule = HeuristicRules["CLA.012"]
}
return rule
}
// RuleReduceNumberOfJoin JOI.005
func (q *Query4Audit) RuleReduceNumberOfJoin() Rule {
var rule = q.RuleOK()
var tables []string
switch q.Stmt.(type) {
// TODO: UNION有可能有多张表,这里未检查UNION SELECT
case *sqlparser.Union:
return rule
default:
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.AliasedTableExpr:
switch table := n.Expr.(type) {
case sqlparser.TableName:
exist := false
for _, t := range tables {
if t == table.Name.String() {
exist = true
break
}
}
if !exist {
tables = append(tables, table.Name.String())
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
}
if len(tables) > common.Config.MaxJoinTableCount {
rule = HeuristicRules["JOI.005"]
}
return rule
}
// RuleDistinctUsage DIS.001
func (q *Query4Audit) RuleDistinctUsage() Rule {
// Distinct
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.Select:
re := regexp.MustCompile(`(?i)(\bdistinct\b)`)
if len(re.FindAllString(q.Query, -1)) > common.Config.MaxDistinctCount {
rule = HeuristicRules["DIS.001"]
}
}
return rule
}
// RuleCountDistinctMultiCol DIS.002
func (q *Query4Audit) RuleCountDistinctMultiCol() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.FuncExpr:
str := strings.ToLower(sqlparser.String(n))
if strings.HasPrefix(str, "count") && strings.Contains(str, ",") {
rule = HeuristicRules["DIS.002"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleDistinctStar DIS.003
func (q *Query4Audit) RuleDistinctStar() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.Select:
meta := ast.GetMeta(q.Stmt, nil)
for _, m := range meta {
if len(m.Table) == 1 {
// distinct tbl.* from tbl和 distinct *
re := regexp.MustCompile(`(?i)((\s+distinct\s*\*)|(\s+distinct\s+[0-9a-z_` + "`" + `]*\.\*))`)
if re.MatchString(q.Query) {
rule = HeuristicRules["DIS.003"]
}
}
break
}
}
return rule
}
// RuleHavingClause CLA.013
func (q *Query4Audit) RuleHavingClause() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch expr := node.(type) {
case *sqlparser.Select:
if expr.Having != nil {
rule = HeuristicRules["CLA.013"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleUpdatePrimaryKey CLA.016
func (idxAdv *IndexAdvisor) RuleUpdatePrimaryKey() Rule {
rule := HeuristicRules["OK"]
switch node := idxAdv.Ast.(type) {
case *sqlparser.Update:
var setColumns []*common.Column
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.UpdateExpr:
// 获取set操作的全部column
setColumns = append(setColumns, ast.FindAllCols(node)...)
}
return true, nil
}, node)
common.LogIfError(err, "")
setColumns = idxAdv.calcCardinality(CompleteColumnsInfo(idxAdv.Ast, setColumns, idxAdv.vEnv))
for _, col := range setColumns {
idxMeta := idxAdv.IndexMeta[idxAdv.vEnv.DBHash(col.DB)][col.Table]
if idxMeta == nil {
return rule
}
for _, idx := range idxMeta.IdxRows {
if idx.KeyName == "PRIMARY" {
if col.Name == idx.ColumnName {
rule = HeuristicRules["CLA.016"]
return rule
}
continue
}
}
}
}
return rule
}
// RuleForbiddenSyntax CLA.017
func (q *Query4Audit) RuleForbiddenSyntax() Rule {
var rule = q.RuleOK()
// 由于vitess对某些语法的支持不完善,使得如创建临时表等语句无法通过语法检查
// 所以这里使用正则对触发器、临时表、存储过程等进行匹配
// 但是目前支持的也不是非常全面,有待完善匹配规则
// TODO TiDB 目前还不支持触发器、存储过程、自定义函数、外键
forbidden := []*regexp.Regexp{
regexp.MustCompile(`(?i)CREATE\s+TRIGGER\s+`),
regexp.MustCompile(`(?i)CREATE\s+TEMPORARY\s+TABLE\s+`),
regexp.MustCompile(`(?i)CREATE\s+VIEW\s+`),
regexp.MustCompile(`(?i)REPLACE\s+VIEW\s+`),
regexp.MustCompile(`(?i)CREATE\s+PROCEDURE\s+`),
regexp.MustCompile(`(?i)CREATE\s+FUNCTION\s+`),
}
for _, reg := range forbidden {
if reg.MatchString(q.Query) {
rule = HeuristicRules["CLA.017"]
if position := reg.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
break
}
}
return rule
}
// RuleNestedSubQueries JOI.006
func (q *Query4Audit) RuleNestedSubQueries() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.Subquery:
rule = HeuristicRules["JOI.006"]
return false, nil
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleMultiDeleteUpdate JOI.007
func (q *Query4Audit) RuleMultiDeleteUpdate() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.Delete, *sqlparser.Update:
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.JoinTableExpr:
rule = HeuristicRules["JOI.007"]
return false, nil
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
}
return rule
}
// RuleMultiDBJoin JOI.008
func (q *Query4Audit) RuleMultiDBJoin() Rule {
var rule = q.RuleOK()
meta := ast.GetMeta(q.Stmt, nil)
dbCount := 0
for range meta {
dbCount++
}
if dbCount > 1 {
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.JoinTableExpr:
rule = HeuristicRules["JOI.008"]
return false, nil
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
}
return rule
}
// RuleORUsage ARG.008
func (q *Query4Audit) RuleORUsage() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.Select:
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch node.(type) {
case *sqlparser.OrExpr:
rule = HeuristicRules["ARG.008"]
return false, nil
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
}
return rule
}
// RuleSpaceWithQuote ARG.009
func (q *Query4Audit) RuleSpaceWithQuote() Rule {
var rule = q.RuleOK()
for _, tk := range ast.Tokenize(q.Query) {
if tk.Type == ast.TokenTypeQuote {
// 序列化的Val是带引号,所以要取第2个最倒数第二个,这样也就不用担心len<2了。
switch tk.Val[1] {
case ' ':
rule = HeuristicRules["ARG.009"]
}
switch tk.Val[len(tk.Val)-2] {
case ' ':
rule = HeuristicRules["ARG.009"]
}
}
}
return rule
}
// RuleHint ARG.010
// TODO: sql_no_cache, straight join
func (q *Query4Audit) RuleHint() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.IndexHints:
if n != nil {
rule = HeuristicRules["ARG.010"]
}
return false, nil
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleNot ARG.011
func (q *Query4Audit) RuleNot() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.ComparisonExpr:
if strings.HasPrefix(n.Operator, "not") {
rule = HeuristicRules["ARG.011"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleUNIONUsage SUB.002
func (q *Query4Audit) RuleUNIONUsage() Rule {
var rule = q.RuleOK()
switch s := q.Stmt.(type) {
case *sqlparser.Union:
if s.Type == "union" {
rule = HeuristicRules["SUB.002"]
}
}
return rule
}
// RuleDistinctJoinUsage SUB.003
func (q *Query4Audit) RuleDistinctJoinUsage() Rule {
var rule = q.RuleOK()
switch expr := q.Stmt.(type) {
case *sqlparser.Select:
if expr.Distinct != "" {
if expr.From != nil {
if len(expr.From) > 1 {
rule = HeuristicRules["SUB.003"]
}
}
}
}
return rule
}
// RuleReadablePasswords SEC.002
func (q *Query4Audit) RuleReadablePasswords() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
re := regexp.MustCompile(`(?i)(password)|(password)|(pwd)`)
for _, tiStmt := range q.TiStmt {
// create table stmt
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
switch col.Tp.Tp {
case mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString,
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob:
if re.FindString(q.Query) != "" {
return HeuristicRules["SEC.002"]
}
}
}
case *tidb.AlterTableStmt:
// alter table stmt
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableModifyColumn, tidb.AlterTableChangeColumn, tidb.AlterTableAddColumns:
for _, col := range spec.NewColumns {
switch col.Tp.Tp {
case mysql.TypeString, mysql.TypeVarchar, mysql.TypeVarString,
mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob:
if re.FindString(q.Query) != "" {
return HeuristicRules["SEC.002"]
}
}
}
}
}
}
}
}
return rule
}
// RuleDataDrop SEC.003
func (q *Query4Audit) RuleDataDrop() Rule {
var rule = q.RuleOK()
switch s := q.Stmt.(type) {
case *sqlparser.DBDDL:
if s.Action == "drop" {
rule = HeuristicRules["SEC.003"]
}
case *sqlparser.DDL:
if s.Action == "drop" || s.Action == "truncate" {
rule = HeuristicRules["SEC.003"]
}
case *sqlparser.Delete:
rule = HeuristicRules["SEC.003"]
}
return rule
}
// RuleCompareWithFunction FUN.001
func (q *Query4Audit) RuleCompareWithFunction() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.ComparisonExpr:
if strings.HasSuffix(sqlparser.String(n.Left), ")") {
rule = HeuristicRules["FUN.001"]
return false, nil
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleCountStar FUN.002
func (q *Query4Audit) RuleCountStar() Rule {
var rule = q.RuleOK()
switch n := q.Stmt.(type) {
case *sqlparser.Select:
// count(N), count(col), count(*)
re := regexp.MustCompile(`(?i)(count\(\s*[*0-9a-z_` + "`" + `]*\s*\))`)
if re.FindString(q.Query) != "" && n.Where != nil {
rule = HeuristicRules["FUN.002"]
}
}
return rule
}
// RuleTruncateTable SEC.001
func (q *Query4Audit) RuleTruncateTable() Rule {
var rule = q.RuleOK()
switch s := q.Stmt.(type) {
case *sqlparser.DDL:
if s.Action == "truncate" {
rule = HeuristicRules["SEC.001"]
}
}
return rule
}
// RuleIn ARG.005 && ARG.004
func (q *Query4Audit) RuleIn() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case *sqlparser.ComparisonExpr:
switch n.Operator {
case "in":
switch r := n.Right.(type) {
case sqlparser.ValTuple:
// IN (NULL)
for _, v := range r {
switch v.(type) {
case *sqlparser.NullVal:
rule = HeuristicRules["ARG.004"]
return false, nil
}
}
if len(r) > common.Config.MaxInCount {
rule = HeuristicRules["ARG.005"]
return false, nil
}
}
case "not in":
switch r := n.Right.(type) {
case sqlparser.ValTuple:
// NOT IN (NULL)
for _, v := range r {
switch v.(type) {
case *sqlparser.NullVal:
rule = HeuristicRules["ARG.004"]
return false, nil
}
}
}
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleIsNullIsNotNull ARG.006
func (q *Query4Audit) RuleIsNullIsNotNull() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.Select:
re := regexp.MustCompile(`(?i)is\s*(not)?\s+null\b`)
if re.FindString(q.Query) != "" {
rule = HeuristicRules["ARG.006"]
}
}
return rule
}
// RuleVarcharVSChar COL.008
func (q *Query4Audit) RuleVarcharVSChar() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
switch col.Tp.Tp {
// 在 TiDB 的 AST 中,char 和 binary 的 type 都是 mysql.TypeString
// 只是 binary 数据类型的 character 和 collate 是 binary
case mysql.TypeString:
rule = HeuristicRules["COL.008"]
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
switch col.Tp.Tp {
case mysql.TypeString:
rule = HeuristicRules["COL.008"]
}
}
}
}
}
}
}
return rule
}
// RuleCreateDualTable TBL.003
func (q *Query4Audit) RuleCreateDualTable() Rule {
var rule = q.RuleOK()
switch s := q.Stmt.(type) {
case *sqlparser.DDL:
if s.NewName.Name.String() == "dual" {
rule = HeuristicRules["TBL.003"]
}
}
return rule
}
// RuleAlterCharset ALT.001
func (q *Query4Audit) RuleAlterCharset() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableOption:
for _, option := range spec.Options {
if option.Tp == tidb.TableOptionCharset ||
option.Tp == tidb.TableOptionCollate {
rule = HeuristicRules["ALT.001"]
break
}
}
}
if rule.Item == "ALT.001" {
break
}
}
}
}
}
return rule
}
// RuleAlterDropColumn ALT.003
func (q *Query4Audit) RuleAlterDropColumn() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableDropColumn:
rule = HeuristicRules["ALT.003"]
}
}
}
}
if rule.Item == "ALT.003" {
re := regexp.MustCompile(`(?i)(drop\s+column)`)
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
}
}
return rule
}
// RuleAlterDropKey ALT.004
func (q *Query4Audit) RuleAlterDropKey() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableDropPrimaryKey,
tidb.AlterTableDropIndex,
tidb.AlterTableDropForeignKey:
rule = HeuristicRules["ALT.004"]
}
}
}
}
}
return rule
}
// RuleCantBeNull COL.012
func (q *Query4Audit) RuleCantBeNull() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
if !mysql.HasNotNullFlag(col.Tp.Flag) {
rule = HeuristicRules["COL.012"]
}
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableModifyColumn, tidb.AlterTableChangeColumn:
for _, col := range spec.NewColumns {
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeTinyBlob, mysql.TypeMediumBlob, mysql.TypeLongBlob:
if !mysql.HasNotNullFlag(col.Tp.Flag) {
rule = HeuristicRules["COL.012"]
}
}
}
}
}
}
}
}
return rule
}
// RuleTooManyKeys KEY.005
func (q *Query4Audit) RuleTooManyKeys() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
if len(node.Constraints) > common.Config.MaxIdxCount {
rule = HeuristicRules["KEY.005"]
}
}
}
}
return rule
}
// RuleTooManyKeyParts KEY.006
func (q *Query4Audit) RuleTooManyKeyParts() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, constraint := range node.Constraints {
if len(constraint.Keys) > common.Config.MaxIdxColsCount {
return HeuristicRules["KEY.006"]
}
if constraint.Refer != nil && len(constraint.Refer.IndexColNames) > common.Config.MaxIdxColsCount {
return HeuristicRules["KEY.006"]
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddConstraint:
if spec.Constraint != nil {
if len(spec.Constraint.Keys) > common.Config.MaxIdxColsCount {
return HeuristicRules["KEY.006"]
}
if spec.Constraint.Refer != nil {
if len(spec.Constraint.Refer.IndexColNames) > common.Config.MaxIdxColsCount {
return HeuristicRules["KEY.006"]
}
}
}
}
}
}
}
}
return rule
}
// RulePKNotInt KEY.007 && KEY.001
func (q *Query4Audit) RulePKNotInt() Rule {
var rule = q.RuleOK()
var pk sqlparser.ColIdent
switch s := q.Stmt.(type) {
case *sqlparser.DDL:
if s.Action == "create" {
if s.TableSpec == nil {
return rule
}
for _, idx := range s.TableSpec.Indexes {
if idx.Info.Type == "primary key" {
if len(idx.Columns) == 1 {
pk = idx.Columns[0].Column
break
}
}
}
// 未指定主键
if pk.String() == "" {
rule = HeuristicRules["KEY.007"]
return rule
}
// 主键非int, bigint类型
for _, col := range s.TableSpec.Columns {
if pk.String() == col.Name.String() {
switch col.Type.Type {
case "int", "bigint", "integer":
if !col.Type.Unsigned {
rule = HeuristicRules["KEY.007"]
}
if !col.Type.Autoincrement {
rule = HeuristicRules["KEY.001"]
}
default:
rule = HeuristicRules["KEY.007"]
}
}
}
}
}
return rule
}
// RuleOrderByMultiDirection KEY.008
func (q *Query4Audit) RuleOrderByMultiDirection() Rule {
var rule = q.RuleOK()
err := sqlparser.Walk(func(node sqlparser.SQLNode) (kontinue bool, err error) {
switch n := node.(type) {
case sqlparser.OrderBy:
order := ""
for _, col := range strings.Split(sqlparser.String(n), ",") {
orders := strings.Split(col, " ")
if order != "" && order != orders[len(orders)-1] {
rule = HeuristicRules["KEY.008"]
return false, nil
}
order = orders[len(orders)-1]
}
}
return true, nil
}, q.Stmt)
common.LogIfError(err, "")
return rule
}
// RuleUniqueKeyDup KEY.009
// TODO: 目前只是给建议,期望能够实现自动检查
func (q *Query4Audit) RuleUniqueKeyDup() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateIndexStmt:
// create index
if node.Unique {
re := regexp.MustCompile(`(?i)(create\s+(unique)\s)`)
rule = HeuristicRules["KEY.009"]
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
return rule
}
case *tidb.AlterTableStmt:
// alter table add constraint
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddConstraint:
if spec.Constraint == nil {
continue
}
switch spec.Constraint.Tp {
case tidb.ConstraintPrimaryKey, tidb.ConstraintUniq, tidb.ConstraintUniqKey, tidb.ConstraintUniqIndex:
re := regexp.MustCompile(`(?i)(add\s+(unique)\s)`)
rule = HeuristicRules["KEY.009"]
if position := re.FindIndex([]byte(q.Query)); len(position) > 0 {
rule.Position = position[0]
}
return rule
}
}
}
}
}
}
return rule
}
// RuleTimestampDefault COL.013
func (q *Query4Audit) RuleTimestampDefault() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
if col.Tp.Tp == mysql.TypeTimestamp {
hasDefault := false
for _, option := range col.Options {
if option.Tp == tidb.ColumnOptionDefaultValue {
hasDefault = true
}
}
if !hasDefault {
rule = HeuristicRules["COL.013"]
break
}
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddColumns,
tidb.AlterTableModifyColumn,
tidb.AlterTableChangeColumn,
tidb.AlterTableAlterColumn:
for _, col := range spec.NewColumns {
if col.Tp.Tp == mysql.TypeTimestamp {
hasDefault := false
for _, option := range col.Options {
if option.Tp == tidb.ColumnOptionDefaultValue {
hasDefault = true
}
}
if !hasDefault {
rule = HeuristicRules["COL.013"]
break
}
}
}
}
}
}
}
}
return rule
}
// RuleAutoIncrementInitNotZero TBL.004
func (q *Query4Audit) RuleAutoIncrementInitNotZero() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, opt := range node.Options {
if opt.Tp == tidb.TableOptionAutoIncrement && opt.UintValue > 1 {
rule = HeuristicRules["TBL.004"]
}
}
}
}
}
return rule
}
// RuleColumnWithCharset COL.014
func (q *Query4Audit) RuleColumnWithCharset() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
if col.Tp.Charset != "" || col.Tp.Collate != "" {
rule = HeuristicRules["COL.014"]
break
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAlterColumn, tidb.AlterTableChangeColumn,
tidb.AlterTableModifyColumn, tidb.AlterTableAddColumns:
for _, col := range spec.NewColumns {
if col.Tp.Charset != "" || col.Tp.Collate != "" {
rule = HeuristicRules["COL.014"]
break
}
}
}
}
}
}
}
return rule
}
// RuleTableCharsetCheck TBL.005
func (q *Query4Audit) RuleTableCharsetCheck() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
var allow bool
var hasCharset bool
for _, opt := range node.Options {
if opt.Tp == tidb.TableOptionCharset {
hasCharset = true
for _, ch := range common.Config.TableAllowCharsets {
if strings.TrimSpace(strings.ToLower(ch)) == strings.TrimSpace(strings.ToLower(opt.StrValue)) {
allow = true
break
}
}
}
}
// 未指定字符集使用MySQL默认配置字符集,我们认为MySQL的配置是被优化过的。
if hasCharset && !allow {
rule = HeuristicRules["TBL.005"]
break
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
var allow bool
var hasCharset bool
switch spec.Tp {
case tidb.AlterTableOption:
for _, opt := range spec.Options {
if opt.Tp == tidb.TableOptionCharset {
hasCharset = true
for _, ch := range common.Config.TableAllowCharsets {
if strings.TrimSpace(strings.ToLower(ch)) == strings.TrimSpace(strings.ToLower(opt.StrValue)) {
allow = true
break
}
}
}
}
// 未指定字符集使用MySQL默认配置字符集,我们认为MySQL的配置是被优化过的。
if hasCharset && !allow {
rule = HeuristicRules["TBL.005"]
break
}
}
}
}
}
}
return rule
}
// RuleBlobDefaultValue COL.015
func (q *Query4Audit) RuleBlobDefaultValue() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeLongBlob:
for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionDefaultValue && opt.Expr.GetType().Tp != mysql.TypeNull {
rule = HeuristicRules["COL.015"]
break
}
}
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableModifyColumn, tidb.AlterTableAlterColumn,
tidb.AlterTableChangeColumn, tidb.AlterTableAddColumns:
for _, col := range spec.NewColumns {
switch col.Tp.Tp {
case mysql.TypeBlob, mysql.TypeMediumBlob, mysql.TypeTinyBlob, mysql.TypeLongBlob:
for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionDefaultValue && opt.Expr.GetType().Tp != mysql.TypeNull {
rule = HeuristicRules["COL.015"]
break
}
}
}
}
}
}
}
}
}
return rule
}
// RuleIntPrecision COL.016
func (q *Query4Audit) RuleIntPrecision() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
switch col.Tp.Tp {
case mysql.TypeLong:
if (col.Tp.Flen < 10 || col.Tp.Flen > 11) && col.Tp.Flen > 0 {
// 有些语言ORM框架会生成int(11),有些语言的框架生成int(10)
rule = HeuristicRules["COL.016"]
break
}
case mysql.TypeLonglong:
if (col.Tp.Flen != 20) && col.Tp.Flen > 0 {
rule = HeuristicRules["COL.016"]
break
}
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn,
tidb.AlterTableAlterColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
switch col.Tp.Tp {
case mysql.TypeLong:
if (col.Tp.Flen < 10 || col.Tp.Flen > 11) && col.Tp.Flen > 0 {
// 有些语言ORM框架会生成int(11),有些语言的框架生成int(10)
rule = HeuristicRules["COL.016"]
break
}
case mysql.TypeLonglong:
if col.Tp.Flen != 20 && col.Tp.Flen > 0 {
rule = HeuristicRules["COL.016"]
break
}
}
}
}
}
}
}
}
return rule
}
// RuleVarcharLength COL.017
func (q *Query4Audit) RuleVarcharLength() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
switch col.Tp.Tp {
case mysql.TypeVarchar, mysql.TypeVarString:
if col.Tp.Flen > common.Config.MaxVarcharLength {
rule = HeuristicRules["COL.017"]
break
}
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableAddColumns, tidb.AlterTableChangeColumn,
tidb.AlterTableAlterColumn, tidb.AlterTableModifyColumn:
for _, col := range spec.NewColumns {
switch col.Tp.Tp {
case mysql.TypeVarchar, mysql.TypeVarString:
if col.Tp.Flen > common.Config.MaxVarcharLength {
rule = HeuristicRules["COL.017"]
break
}
}
}
}
}
}
}
}
return rule
}
// RuleNoOSCKey KEY.002
func (q *Query4Audit) RuleNoOSCKey() Rule {
var rule = q.RuleOK()
switch s := q.Stmt.(type) {
case *sqlparser.DDL:
if s.Action == "create" {
pkReg := regexp.MustCompile(`(?i)(primary\s+key)`)
if !pkReg.MatchString(q.Query) {
ukReg := regexp.MustCompile(`(?i)(unique\s+((key)|(index)))`)
if !ukReg.MatchString(q.Query) {
rule = HeuristicRules["KEY.002"]
}
}
}
}
return rule
}
// RuleTooManyFields COL.006
func (q *Query4Audit) RuleTooManyFields() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
if len(node.Cols) > common.Config.MaxColCount {
rule = HeuristicRules["COL.006"]
}
}
}
}
return rule
}
// RuleAllowEngine TBL.002
func (q *Query4Audit) RuleAllowEngine() Rule {
var rule = q.RuleOK()
var hasDefaultEngine bool
var allowedEngine bool
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, opt := range node.Options {
if opt.Tp == tidb.TableOptionEngine {
hasDefaultEngine = true
// 使用了非推荐的存储引擎
for _, engine := range common.Config.TableAllowEngines {
if strings.EqualFold(opt.StrValue, engine) {
allowedEngine = true
}
}
// common.Config.TableAllowEngines 为空时不给予建议
if !allowedEngine && len(common.Config.TableAllowEngines) > 0 {
rule = HeuristicRules["TBL.002"]
break
}
}
}
// 建表语句未指定表的存储引擎
if !hasDefaultEngine {
rule = HeuristicRules["TBL.002"]
break
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableOption:
for _, opt := range spec.Options {
if opt.Tp == tidb.TableOptionEngine {
// 使用了非推荐的存储引擎
for _, engine := range common.Config.TableAllowEngines {
if strings.EqualFold(opt.StrValue, engine) {
allowedEngine = true
}
}
// common.Config.TableAllowEngines 为空时不给予建议
if !allowedEngine && len(common.Config.TableAllowEngines) > 0 {
rule = HeuristicRules["TBL.002"]
break
}
}
}
}
}
}
}
}
return rule
}
// RulePartitionNotAllowed TBL.001
func (q *Query4Audit) RulePartitionNotAllowed() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
if node.Partition != nil {
rule = HeuristicRules["TBL.001"]
break
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
if len(spec.PartDefinitions) > 0 {
rule = HeuristicRules["TBL.001"]
break
}
}
}
}
}
return rule
}
// RuleAutoIncUnsigned COL.003:
func (q *Query4Audit) RuleAutoIncUnsigned() Rule {
var rule = q.RuleOK()
switch q.Stmt.(type) {
case *sqlparser.DDL:
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateTableStmt:
for _, col := range node.Cols {
for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionAutoIncrement {
if !mysql.HasUnsignedFlag(col.Tp.Flag) {
rule = HeuristicRules["COL.003"]
break
}
}
if rule.Item == "COL.003" {
break
}
}
}
case *tidb.AlterTableStmt:
for _, spec := range node.Specs {
switch spec.Tp {
case tidb.AlterTableChangeColumn, tidb.AlterTableAlterColumn,
tidb.AlterTableModifyColumn, tidb.AlterTableAddColumns:
for _, col := range spec.NewColumns {
for _, opt := range col.Options {
if opt.Tp == tidb.ColumnOptionAutoIncrement {
if !mysql.HasUnsignedFlag(col.Tp.Flag) {
rule = HeuristicRules["COL.003"]
break
}
}
if rule.Item == "COL.003" {
break
}
}
}
}
}
}
}
}
return rule
}
// RuleSpaceAfterDot STA.002
func (q *Query4Audit) RuleSpaceAfterDot() Rule {
var rule = q.RuleOK()
tks := ast.Tokenize(q.Query)
for i, tk := range tks {
switch tk.Type {
// SELECT * FROM db. tbl
// SELECT tbl. col FROM tbl
case ast.TokenTypeWord:
if len(tks) > i+1 &&
tks[i+1].Type == ast.TokenTypeWhitespace &&
strings.HasSuffix(tk.Val, ".") {
common.Log.Debug("RuleSpaceAfterDot: ", tk.Val, tks[i+1].Val)
rule = HeuristicRules["STA.002"]
return rule
}
default:
}
}
return rule
}
// RuleIdxPrefix STA.003
func (q *Query4Audit) RuleIdxPrefix() Rule {
var rule = q.RuleOK()
for _, node := range q.TiStmt {
switch n := node.(type) {
case *tidb.CreateTableStmt:
for _, c := range n.Constraints {
switch c.Tp {
case tidb.ConstraintIndex, tidb.ConstraintKey:
if !strings.HasPrefix(c.Name, common.Config.IdxPrefix) {
rule = HeuristicRules["STA.003"]
}
case tidb.ConstraintUniq, tidb.ConstraintUniqKey, tidb.ConstraintUniqIndex:
if !strings.HasPrefix(c.Name, common.Config.UkPrefix) {
rule = HeuristicRules["STA.003"]
}
}
}
case *tidb.AlterTableStmt:
for _, s := range n.Specs {
switch s.Tp {
case tidb.AlterTableAddConstraint:
switch s.Constraint.Tp {
case tidb.ConstraintIndex, tidb.ConstraintKey:
if !strings.HasPrefix(s.Constraint.Name, common.Config.IdxPrefix) {
rule = HeuristicRules["STA.003"]
}
case tidb.ConstraintUniq, tidb.ConstraintUniqKey, tidb.ConstraintUniqIndex:
if !strings.HasPrefix(s.Constraint.Name, common.Config.UkPrefix) {
rule = HeuristicRules["STA.003"]
}
}
}
}
}
}
return rule
}
// RuleStandardName STA.004
func (q *Query4Audit) RuleStandardName() Rule {
var rule = q.RuleOK()
allowReg := regexp.MustCompile(`(?i)[a-z0-9_` + "`" + `]`)
for _, tk := range ast.Tokenize(q.Query) {
if tk.Val == "``" {
rule = HeuristicRules["STA.004"]
}
switch tk.Type {
// 反引号中可能有乱七八糟的东西
case ast.TokenTypeBacktickQuote:
// 特殊字符,连续下划线
if allowReg.ReplaceAllString(tk.Val, "") != "" || strings.Contains(tk.Val, "__") {
rule = HeuristicRules["STA.004"]
}
// 统一大小写
if !(strings.ToLower(tk.Val) == tk.Val || strings.ToUpper(tk.Val) == tk.Val) {
rule = HeuristicRules["STA.004"]
}
case ast.TokenTypeWord:
// TOKEN_TYPE_WORD中处理连续下划线的情况,其他情况容易误伤
if strings.Contains(tk.Val, "__") {
rule = HeuristicRules["STA.004"]
}
default:
}
}
return rule
}
// MergeConflictHeuristicRules merge conflict rules
func MergeConflictHeuristicRules(rules map[string]Rule) map[string]Rule {
// KWR.001 VS ERR.000
// select sql_calc_found_rows * from film
if _, ok := rules["KWR.001"]; ok {
delete(rules, "ERR.000")
}
// SUB.001 VS OWN.004 VS JOI.006
if _, ok := rules["SUB.001"]; ok {
delete(rules, "ARG.005")
delete(rules, "JOI.006")
}
// SUB.004 VS SUB.001
if _, ok := rules["SUB.004"]; ok {
delete(rules, "SUB.001")
}
// KEY.007 VS KEY.002
if _, ok := rules["KEY.007"]; ok {
delete(rules, "KEY.002")
}
// JOI.002 VS JOI.006
if _, ok := rules["JOI.002"]; ok {
delete(rules, "JOI.006")
}
// JOI.008 VS JOI.007
if _, ok := rules["JOI.008"]; ok {
delete(rules, "JOI.007")
}
return rules
}
// RuleMySQLError ERR.XXX
func RuleMySQLError(item string, err error) Rule {
type MySQLError struct {
ErrCode string
ErrString string
}
// vitess 语法检查出错返回的是ERR.000
switch item {
case "ERR.000":
return Rule{
Item: item,
Summary: "MySQL执行出错 " + err.Error(),
Severity: "L8",
Content: err.Error(),
}
}
// Received #1146 error from MySQL server: "table xxx doesn't exist"
errReg := regexp.MustCompile(`(?i)Received #([0-9]+) error from MySQL server: ['"](.*)['"]`)
errStr := err.Error()
msg := errReg.FindStringSubmatch(errStr)
var mysqlError MySQLError
if len(msg) == 3 {
if msg[1] != "" && msg[2] != "" {
mysqlError = MySQLError{
ErrCode: msg[1],
ErrString: msg[2],
}
}
} else {
var errcode string
if strings.HasPrefix(err.Error(), "syntax error at position") {
errcode = "1064"
}
mysqlError = MySQLError{
ErrCode: errcode,
ErrString: err.Error(),
}
}
switch mysqlError.ErrCode {
// 1146 ER_NO_SUCH_TABLE
case "", "1146":
return Rule{
Item: item,
Summary: "MySQL执行出错",
Severity: "L0",
Content: "",
}
default:
return Rule{
Item: item,
Summary: "MySQL执行出错 " + mysqlError.ErrString,
Severity: "L8",
Content: mysqlError.ErrString,
}
}
}
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/mirrors/SOAR.git
git@gitee.com:mirrors/SOAR.git
mirrors
SOAR
SOAR
v0.8.1

搜索帮助