提交 482a96e8 编写于 作者: martianzhang's avatar martianzhang

fix -config arg parse bug

  1. fix -config arg parse bug
  2. code & comment lint
上级 6bdf1d96
...@@ -20,7 +20,7 @@ package common ...@@ -20,7 +20,7 @@ package common
var TestSQLs []string var TestSQLs []string
func init() { func init() {
// 所有的SQL都要以分号结尾,-list-test-sqls参数会打印这个list,以分号结尾可方便测试 // 所有的SQL都要以分号结尾,-list-test-sqls 参数会打印这个 list,以分号结尾可方便测试
// 如:./soar -list-test-sql | ./soar // 如:./soar -list-test-sql | ./soar
TestSQLs = []string{ TestSQLs = []string{
// single equality // single equality
...@@ -61,7 +61,7 @@ func init() { ...@@ -61,7 +61,7 @@ func init() {
"SELECT release_year FROM film WHERE length = 123 GROUP BY release_year ORDER BY release_year LIMIT 10;", // INDEX(length, release_year)", "SELECT release_year FROM film WHERE length = 123 GROUP BY release_year ORDER BY release_year LIMIT 10;", // INDEX(length, release_year)",
"SELECT * FROM film WHERE length = 123 ORDER BY release_year LIMIT 10;", // INDEX(length, release_year)", "SELECT * FROM film WHERE length = 123 ORDER BY release_year LIMIT 10;", // INDEX(length, release_year)",
"SELECT * FROM film ORDER BY release_year LIMIT 10;", // 不能单独给release_year加索引 "SELECT * FROM film ORDER BY release_year LIMIT 10;", // 不能单独给release_year加索引
"SELECT film_id FROM film ORDER BY release_year LIMIT 10;", // TODO: INDEX(release_year),film_id是主键查询列满足索引覆盖的情况才会使用到release_year索引 "SELECT film_id FROM film ORDER BY release_year LIMIT 10;", // TODO: INDEX(release_year),film_id 是主键查询列满足索引覆盖的情况才会使用到 release_year 索引
"SELECT * FROM film WHERE length > 100 ORDER BY length LIMIT 10;", // INDEX(length) This "range" is compatible with ORDER BY "SELECT * FROM film WHERE length > 100 ORDER BY length LIMIT 10;", // INDEX(length) This "range" is compatible with ORDER BY
"SELECT * FROM film WHERE length < 100 ORDER BY length LIMIT 10;", // INDEX(length) also works "SELECT * FROM film WHERE length < 100 ORDER BY length LIMIT 10;", // INDEX(length) also works
"SELECT * FROM customer WHERE address_id in (224,510) ORDER BY last_name;", // INDEX(address_id) "SELECT * FROM customer WHERE address_id in (224,510) ORDER BY last_name;", // INDEX(address_id)
...@@ -82,8 +82,8 @@ func init() { ...@@ -82,8 +82,8 @@ func init() {
// Join // Join
// 内连接 INNER JOIN // 内连接 INNER JOIN
// 在mysql中,inner join...on , join...on , 逗号...WHERE ,cross join...on是一样的含义。 // 在mysql中,inner join...on , join...on , 逗号...WHERE ,cross join...on是一样的含义。
// 但是在标准SQL中,它们并不等价,标准SQL中INNER JOIN与ON共同使用, CROSS JOIN用于其他情况。 // 但是在标准SQL中,它们并不等价,标准 SQL 中 INNER JOIN 与 ON共同使用, CROSS JOIN用于其他情况。
// 逗号不支持on和using语法, 逗号的优先级要低于INNER JOIN, CROSS JOIN, LEFT JOIN // 逗号不支持 on 和 using 语法, 逗号的优先级要低于INNER JOIN, CROSS JOIN, LEFT JOIN
// ON子句的语法格式为:tb1.col1 = tb2.col2列名可以不同,筛选连接后的结果,两表的对应列值相同才在结果集中。 // ON子句的语法格式为:tb1.col1 = tb2.col2列名可以不同,筛选连接后的结果,两表的对应列值相同才在结果集中。
// 当模式设计对联接表的列采用了相同的命名样式时,就可以使用 USING 语法来简化 ON 语法 // 当模式设计对联接表的列采用了相同的命名样式时,就可以使用 USING 语法来简化 ON 语法
...@@ -130,7 +130,7 @@ func init() { ...@@ -130,7 +130,7 @@ func init() {
"SELECT country_id, last_update FROM city NATURAL RIGHT JOIN country;", "SELECT country_id, last_update FROM city NATURAL RIGHT JOIN country;",
// STRAIGHT_JOIN 实际上与内连接 INNER JOIN 表现完全一致, // STRAIGHT_JOIN 实际上与内连接 INNER JOIN 表现完全一致,
// 不同的是使用了 STRAIGHT_JOIN后指定表载入的顺序,city先于country载入 // 不同的是使用了 STRAIGHT_JOIN 后指定表载入的顺序,city 先于 country 载入
"SELECT a.country_id, a.last_update FROM city a STRAIGHT_JOIN country b ON a.country_id=b.country_id;", "SELECT a.country_id, a.last_update FROM city a STRAIGHT_JOIN country b ON a.country_id=b.country_id;",
// SEMI JOIN // SEMI JOIN
......
...@@ -41,11 +41,11 @@ type Configration struct { ...@@ -41,11 +41,11 @@ type Configration struct {
// +++++++++++++++测试环境+++++++++++++++++ // +++++++++++++++测试环境+++++++++++++++++
OnlineDSN *dsn `yaml:"online-dsn"` // 线上环境数据库配置 OnlineDSN *dsn `yaml:"online-dsn"` // 线上环境数据库配置
TestDSN *dsn `yaml:"test-dsn"` // 测试环境数据库配置 TestDSN *dsn `yaml:"test-dsn"` // 测试环境数据库配置
AllowOnlineAsTest bool `yaml:"allow-online-as-test"` // 允许Online环境也可以当作Test环境 AllowOnlineAsTest bool `yaml:"allow-online-as-test"` // 允许 Online 环境也可以当作 Test 环境
DropTestTemporary bool `yaml:"drop-test-temporary"` // 是否清理Test环境产生的临时库表 DropTestTemporary bool `yaml:"drop-test-temporary"` // 是否清理Test环境产生的临时库表
CleanupTestDatabase bool `yaml:"cleanup-test-database"` // 清理残余的测试数据库(程序异常退出或未开启drop-test-temporary) issue #48 CleanupTestDatabase bool `yaml:"cleanup-test-database"` // 清理残余的测试数据库(程序异常退出或未开启drop-test-temporary) issue #48
OnlySyntaxCheck bool `yaml:"only-syntax-check"` // 只做语法检查不输出优化建议 OnlySyntaxCheck bool `yaml:"only-syntax-check"` // 只做语法检查不输出优化建议
SamplingStatisticTarget int `yaml:"sampling-statistic-target"` // 数据采样因子,对应postgres的default_statistics_target SamplingStatisticTarget int `yaml:"sampling-statistic-target"` // 数据采样因子,对应 PostgreSQL 的 default_statistics_target
Sampling bool `yaml:"sampling"` // 数据采样开关 Sampling bool `yaml:"sampling"` // 数据采样开关
Profiling bool `yaml:"profiling"` // 在开启数据采样的情况下,在测试环境执行进行profile Profiling bool `yaml:"profiling"` // 在开启数据采样的情况下,在测试环境执行进行profile
Trace bool `yaml:"trace"` // 在开启数据采样的情况下,在测试环境执行进行Trace Trace bool `yaml:"trace"` // 在开启数据采样的情况下,在测试环境执行进行Trace
...@@ -55,29 +55,29 @@ type Configration struct { ...@@ -55,29 +55,29 @@ type Configration struct {
Delimiter string `yaml:"delimiter"` // SQL分隔符 Delimiter string `yaml:"delimiter"` // SQL分隔符
// +++++++++++++++日志相关+++++++++++++++++ // +++++++++++++++日志相关+++++++++++++++++
// 日志级别,这里使用了beego的log // 日志级别,这里使用了 beego 的 log
// [0:Emergency, 1:Alert, 2:Critical, 3:Error, 4:Warning, 5:Notice, 6:Informational, 7:Debug] // [0:Emergency, 1:Alert, 2:Critical, 3:Error, 4:Warning, 5:Notice, 6:Informational, 7:Debug]
LogLevel int `yaml:"log-level"` LogLevel int `yaml:"log-level"`
// 日志输出位置,默认日志输出到控制台 // 日志输出位置,默认日志输出到控制台
// 目前只支持['console', 'file']两种形式,如非console形式这里需要指定文件的路径,可以是相对路径 // 目前只支持['console', 'file']两种形式,如非console形式这里需要指定文件的路径,可以是相对路径
LogOutput string `yaml:"log-output"` LogOutput string `yaml:"log-output"`
// 优化建议输出格式,目前支持: json, text, markdown格式,如指定其他格式会给pretty.Println的输出 // 优化建议输出格式,目前支持: json, text, markdown格式,如指定其他格式会给 pretty.Println 的输出
ReportType string `yaml:"report-type"` ReportType string `yaml:"report-type"`
// 当ReportType为html格式时使用的css风格,如不指定会提供一个默认风格。CSS可以是本地文件,也可以是一个URL // 当 ReportType 为 html 格式时使用的 css 风格,如不指定会提供一个默认风格。CSS可 以是本地文件,也可以是一个URL
ReportCSS string `yaml:"report-css"` ReportCSS string `yaml:"report-css"`
// 当ReportType为html格式时使用的javascript脚本,如不指定默认会加载SQL pretty使用的javascript。像CSS一样可以是本地文件,也可以是一个URL // 当 ReportType 为 html 格式时使用的 javascript 脚本,如不指定默认会加载SQL pretty 使用的 javascript。像CSS一样可以是本地文件,也可以是一个URL
ReportJavascript string `yaml:"report-javascript"` ReportJavascript string `yaml:"report-javascript"`
// 当ReportType为html格式时,HTML的title // 当ReportType 为 html 格式时,HTML 的 title
ReportTitle string `yaml:"report-title"` ReportTitle string `yaml:"report-title"`
// blackfriday markdown2html config // blackfriday markdown2html config
MarkdownExtensions int `yaml:"markdown-extensions"` // markdown转html支持的扩展包, 参考blackfriday MarkdownExtensions int `yaml:"markdown-extensions"` // markdown 转 html 支持的扩展包, 参考blackfriday
MarkdownHTMLFlags int `yaml:"markdown-html-flags"` // markdown转html支持的flag, 参考blackfriday, default 0 MarkdownHTMLFlags int `yaml:"markdown-html-flags"` // markdown 转 html 支持的 flag, 参考blackfriday, default 0
// ++++++++++++++优化建议相关++++++++++++++ // ++++++++++++++优化建议相关++++++++++++++
IgnoreRules []string `yaml:"ignore-rules"` // 忽略的优化建议规则 IgnoreRules []string `yaml:"ignore-rules"` // 忽略的优化建议规则
RewriteRules []string `yaml:"rewrite-rules"` // 生效的重写规则 RewriteRules []string `yaml:"rewrite-rules"` // 生效的重写规则
BlackList string `yaml:"blacklist"` // blacklist中的SQL不会被评审,可以是指纹,也可以是正则 BlackList string `yaml:"blacklist"` // blacklist 中的 SQL 不会被评审,可以是指纹,也可以是正则
MaxJoinTableCount int `yaml:"max-join-table-count"` // 单条SQL中JOIN表的最大数量 MaxJoinTableCount int `yaml:"max-join-table-count"` // 单条 SQL 中 JOIN 表的最大数量
MaxGroupByColsCount int `yaml:"max-group-by-cols-count"` // 单条SQL中GroupBy包含列的最大数量 MaxGroupByColsCount int `yaml:"max-group-by-cols-count"` // 单条SQL中GroupBy包含列的最大数量
MaxDistinctCount int `yaml:"max-distinct-count"` // 单条SQL中Distinct的最大数量 MaxDistinctCount int `yaml:"max-distinct-count"` // 单条SQL中Distinct的最大数量
MaxIdxColsCount int `yaml:"max-index-cols-count"` // 复合索引中包含列的最大数量 MaxIdxColsCount int `yaml:"max-index-cols-count"` // 复合索引中包含列的最大数量
...@@ -474,7 +474,7 @@ func readCmdFlags() error { ...@@ -474,7 +474,7 @@ func readCmdFlags() error {
return nil return nil
} }
config := flag.String("config", "", "Config file path") _ = flag.String("config", "", "Config file path")
// +++++++++++++++测试环境+++++++++++++++++ // +++++++++++++++测试环境+++++++++++++++++
onlineDSN := flag.String("online-dsn", FormatDSN(Config.OnlineDSN), "OnlineDSN, 线上环境数据库配置, username:password@ip:port/schema") onlineDSN := flag.String("online-dsn", FormatDSN(Config.OnlineDSN), "OnlineDSN, 线上环境数据库配置, username:password@ip:port/schema")
testDSN := flag.String("test-dsn", FormatDSN(Config.TestDSN), "TestDSN, 测试环境数据库配置, username:password@ip:port/schema") testDSN := flag.String("test-dsn", FormatDSN(Config.TestDSN), "TestDSN, 测试环境数据库配置, username:password@ip:port/schema")
...@@ -486,7 +486,7 @@ func readCmdFlags() error { ...@@ -486,7 +486,7 @@ func readCmdFlags() error {
trace := flag.Bool("trace", Config.Trace, "Trace, 开启数据采样的情况下在测试环境执行Trace") trace := flag.Bool("trace", Config.Trace, "Trace, 开启数据采样的情况下在测试环境执行Trace")
explain := flag.Bool("explain", Config.Explain, "Explain, 是否开启Explain执行计划分析") explain := flag.Bool("explain", Config.Explain, "Explain, 是否开启Explain执行计划分析")
sampling := flag.Bool("sampling", Config.Sampling, "Sampling, 数据采样开关") sampling := flag.Bool("sampling", Config.Sampling, "Sampling, 数据采样开关")
samplingStatisticTarget := flag.Int("sampling-statistic-target", Config.SamplingStatisticTarget, "SamplingStatisticTarget, 数据采样因子,对应postgres的default_statistics_target") samplingStatisticTarget := flag.Int("sampling-statistic-target", Config.SamplingStatisticTarget, "SamplingStatisticTarget, 数据采样因子,对应 postgres 的 default_statistics_target")
connTimeOut := flag.Int("conn-time-out", Config.ConnTimeOut, "ConnTimeOut, 数据库连接超时时间,单位秒") connTimeOut := flag.Int("conn-time-out", Config.ConnTimeOut, "ConnTimeOut, 数据库连接超时时间,单位秒")
queryTimeOut := flag.Int("query-time-out", Config.QueryTimeOut, "QueryTimeOut, 数据库SQL执行超时时间,单位秒") queryTimeOut := flag.Int("query-time-out", Config.QueryTimeOut, "QueryTimeOut, 数据库SQL执行超时时间,单位秒")
delimiter := flag.String("delimiter", Config.Delimiter, "Delimiter, SQL分隔符") delimiter := flag.String("delimiter", Config.Delimiter, "Delimiter, SQL分隔符")
...@@ -494,19 +494,19 @@ func readCmdFlags() error { ...@@ -494,19 +494,19 @@ func readCmdFlags() error {
logLevel := flag.Int("log-level", Config.LogLevel, "LogLevel, 日志级别, [0:Emergency, 1:Alert, 2:Critical, 3:Error, 4:Warning, 5:Notice, 6:Informational, 7:Debug]") logLevel := flag.Int("log-level", Config.LogLevel, "LogLevel, 日志级别, [0:Emergency, 1:Alert, 2:Critical, 3:Error, 4:Warning, 5:Notice, 6:Informational, 7:Debug]")
logOutput := flag.String("log-output", Config.LogOutput, "LogOutput, 日志输出位置") logOutput := flag.String("log-output", Config.LogOutput, "LogOutput, 日志输出位置")
reportType := flag.String("report-type", Config.ReportType, "ReportType, 化建议输出格式,目前支持: json, text, markdown, html等") reportType := flag.String("report-type", Config.ReportType, "ReportType, 化建议输出格式,目前支持: json, text, markdown, html等")
reportCSS := flag.String("report-css", Config.ReportCSS, "ReportCSS, 当ReportType为html格式时使用的css风格,如不指定会提供一个默认风格。CSS可以是本地文件,也可以是一个URL") reportCSS := flag.String("report-css", Config.ReportCSS, "ReportCSS, 当 ReportType 为 html 格式时使用的 css 风格,如不指定会提供一个默认风格。CSS可以是本地文件,也可以是一个URL")
reportJavascript := flag.String("report-javascript", Config.ReportJavascript, "ReportJavascript, 当ReportType为html格式时使用的javascript脚本,如不指定默认会加载SQL pretty使用的javascript。像CSS一样可以是本地文件,也可以是一个URL") reportJavascript := flag.String("report-javascript", Config.ReportJavascript, "ReportJavascript, 当 ReportType 为 html 格式时使用的javascript脚本,如不指定默认会加载SQL pretty 使用的 javascript。像CSS一样可以是本地文件,也可以是一个URL")
reportTitle := flag.String("report-title", Config.ReportTitle, "ReportTitle, 当ReportType为html格式时,HTML的title") reportTitle := flag.String("report-title", Config.ReportTitle, "ReportTitle, 当 ReportType 为 html 格式时,HTML 的 title")
// +++++++++++++++markdown+++++++++++++++++ // +++++++++++++++markdown+++++++++++++++++
markdownExtensions := flag.Int("markdown-extensions", Config.MarkdownExtensions, "MarkdownExtensions, markdownhtml支持的扩展包, 参考blackfriday") markdownExtensions := flag.Int("markdown-extensions", Config.MarkdownExtensions, "MarkdownExtensions, markdownhtml支持的扩展包, 参考blackfriday")
markdownHTMLFlags := flag.Int("markdown-html-flags", Config.MarkdownHTMLFlags, "MarkdownHTMLFlags, markdown转html支持的flag, 参考blackfriday") markdownHTMLFlags := flag.Int("markdown-html-flags", Config.MarkdownHTMLFlags, "MarkdownHTMLFlags, markdown 转 html 支持的 flag, 参考blackfriday")
// ++++++++++++++优化建议相关++++++++++++++ // ++++++++++++++优化建议相关++++++++++++++
ignoreRules := flag.String("ignore-rules", strings.Join(Config.IgnoreRules, ","), "IgnoreRules, 忽略的优化建议规则") ignoreRules := flag.String("ignore-rules", strings.Join(Config.IgnoreRules, ","), "IgnoreRules, 忽略的优化建议规则")
rewriteRules := flag.String("rewrite-rules", strings.Join(Config.RewriteRules, ","), "RewriteRules, 生效的重写规则") rewriteRules := flag.String("rewrite-rules", strings.Join(Config.RewriteRules, ","), "RewriteRules, 生效的重写规则")
blackList := flag.String("blacklist", Config.BlackList, "blacklist中的SQL不会被评审,可以是指纹,也可以是正则") blackList := flag.String("blacklist", Config.BlackList, "blacklist 中的 SQ L不会被评审,可以是指纹,也可以是正则")
maxJoinTableCount := flag.Int("max-join-table-count", Config.MaxJoinTableCount, "MaxJoinTableCount, 单条SQL中JOIN表的最大数量") maxJoinTableCount := flag.Int("max-join-table-count", Config.MaxJoinTableCount, "MaxJoinTableCount, 单条 SQL 中 JOIN 表的最大数量")
maxGroupByColsCount := flag.Int("max-group-by-cols-count", Config.MaxGroupByColsCount, "MaxGroupByColsCount, 单条SQL中GroupBy包含列的最大数量") maxGroupByColsCount := flag.Int("max-group-by-cols-count", Config.MaxGroupByColsCount, "MaxGroupByColsCount, 单条 SQL 中 GroupBy 包含列的最大数量")
maxDistinctCount := flag.Int("max-distinct-count", Config.MaxDistinctCount, "MaxDistinctCount, 单条SQL中Distinct的最大数量") maxDistinctCount := flag.Int("max-distinct-count", Config.MaxDistinctCount, "MaxDistinctCount, 单条 SQL 中 Distinct 的最大数量")
maxIdxColsCount := flag.Int("max-index-cols-count", Config.MaxIdxColsCount, "MaxIdxColsCount, 复合索引中包含列的最大数量") maxIdxColsCount := flag.Int("max-index-cols-count", Config.MaxIdxColsCount, "MaxIdxColsCount, 复合索引中包含列的最大数量")
maxTotalRows := flag.Int64("max-total-rows", Config.MaxTotalRows, "MaxTotalRows, 计算散粒度时,当数据行数大于MaxTotalRows即开启数据库保护模式,不计算散粒度") maxTotalRows := flag.Int64("max-total-rows", Config.MaxTotalRows, "MaxTotalRows, 计算散粒度时,当数据行数大于MaxTotalRows即开启数据库保护模式,不计算散粒度")
maxQueryCost := flag.Int64("max-query-cost", Config.MaxQueryCost, "MaxQueryCost, last_query_cost 超过该值时将给予警告") maxQueryCost := flag.Int64("max-query-cost", Config.MaxQueryCost, "MaxQueryCost, last_query_cost 超过该值时将给予警告")
...@@ -540,7 +540,7 @@ func readCmdFlags() error { ...@@ -540,7 +540,7 @@ func readCmdFlags() error {
// +++++++++++++++++其他+++++++++++++++++++ // +++++++++++++++++其他+++++++++++++++++++
printConfig := flag.Bool("print-config", false, "Print configs") printConfig := flag.Bool("print-config", false, "Print configs")
ver := flag.Bool("version", false, "Print version info") ver := flag.Bool("version", false, "Print version info")
query := flag.String("query", Config.Query, "待评审的SQL或SQL文件,如SQL中包含特殊字符建议使用文件名。") query := flag.String("query", Config.Query, "待评审的 SQL 或 SQL 文件,如 SQL 中包含特殊字符建议使用文件名。")
listHeuristicRules := flag.Bool("list-heuristic-rules", Config.ListHeuristicRules, "ListHeuristicRules, 打印支持的评审规则列表") listHeuristicRules := flag.Bool("list-heuristic-rules", Config.ListHeuristicRules, "ListHeuristicRules, 打印支持的评审规则列表")
listRewriteRules := flag.Bool("list-rewrite-rules", Config.ListRewriteRules, "ListRewriteRules, 打印支持的重写规则列表") listRewriteRules := flag.Bool("list-rewrite-rules", Config.ListRewriteRules, "ListRewriteRules, 打印支持的重写规则列表")
listTestSQLs := flag.Bool("list-test-sqls", Config.ListTestSqls, "ListTestSqls, 打印测试case用于测试") listTestSQLs := flag.Bool("list-test-sqls", Config.ListTestSqls, "ListTestSqls, 打印测试case用于测试")
...@@ -548,20 +548,13 @@ func readCmdFlags() error { ...@@ -548,20 +548,13 @@ func readCmdFlags() error {
verbose := flag.Bool("verbose", Config.Verbose, "Verbose") verbose := flag.Bool("verbose", Config.Verbose, "Verbose")
dryrun := flag.Bool("dry-run", Config.DryRun, "是否在预演环境执行") dryrun := flag.Bool("dry-run", Config.DryRun, "是否在预演环境执行")
maxPrettySQLLength := flag.Int("max-pretty-sql-length", Config.MaxPrettySQLLength, "MaxPrettySQLLength, 超出该长度的SQL会转换成指纹输出") maxPrettySQLLength := flag.Int("max-pretty-sql-length", Config.MaxPrettySQLLength, "MaxPrettySQLLength, 超出该长度的SQL会转换成指纹输出")
// 一个不存在log-level,用于更新usage。 // 一个不存在 log-level,用于更新 usage。
// 因为vitess里面也用了flag,这些vitess的参数我们不需要关注 // 因为 vitess 里面也用了 flag,这些 vitess 的参数我们不需要关注
if !Config.Verbose && runtime.GOOS != "windows" { if !Config.Verbose && runtime.GOOS != "windows" {
flag.Usage = usage flag.Usage = usage
} }
flag.Parse() flag.Parse()
if *config != "" {
err := Config.readConfigFile(*config)
if err != nil {
fmt.Println(err.Error())
}
}
Config.OnlineDSN = parseDSN(*onlineDSN, Config.OnlineDSN) Config.OnlineDSN = parseDSN(*onlineDSN, Config.OnlineDSN)
Config.TestDSN = parseDSN(*testDSN, Config.TestDSN) Config.TestDSN = parseDSN(*testDSN, Config.TestDSN)
Config.AllowOnlineAsTest = *allowOnlineAsTest Config.AllowOnlineAsTest = *allowOnlineAsTest
...@@ -841,3 +834,34 @@ func ListReportTypes() { ...@@ -841,3 +834,34 @@ func ListReportTypes() {
} }
} }
} }
// ArgConfig get -config arg value from cli
func ArgConfig() string {
var configFile string
if len(os.Args) > 1 && strings.HasPrefix(os.Args[1], "-config") {
if os.Args[1] == "-config" && len(os.Args) > 2 {
if os.Args[2] == "=" && len(os.Args) > 3 {
// -config = soar.yaml not support
fmt.Println("wrong format, no space between '=', eg: -config=soar.yaml")
} else {
// -config soar.yaml
configFile = os.Args[2]
}
if strings.HasPrefix(configFile, "=") {
// -config =soar.yaml
configFile = strings.Split(configFile, "=")[1]
}
}
if strings.Contains(os.Args[1], "=") {
// -config=soar.yaml
configFile = strings.Split(os.Args[1], "=")[1]
}
} else {
for i, c := range os.Args {
if strings.HasPrefix(c, "-config") && i != 1 {
fmt.Println("-config must be the first arg")
}
}
}
return configFile
}
...@@ -18,6 +18,7 @@ package common ...@@ -18,6 +18,7 @@ package common
import ( import (
"flag" "flag"
"os"
"runtime" "runtime"
"testing" "testing"
...@@ -83,3 +84,29 @@ func TestListReportTypes(t *testing.T) { ...@@ -83,3 +84,29 @@ func TestListReportTypes(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
} }
func TestArgConfig(t *testing.T) {
testArgs1 := [][]string{
{"soar", "-config", "=", "soar.yaml"},
{"soar", "-print-config", "-config", "soar.yaml"},
}
testArgs2 := [][]string{
{"soar", "-config", "soar.yaml"},
{"soar", "-config", "=soar.yaml"},
{"soar", "-config=soar.yaml"},
}
for _, args := range testArgs1 {
os.Args = args
configFile := ArgConfig()
if configFile != "" {
t.Errorf("should return '', but got %s", configFile)
}
}
for _, args := range testArgs2 {
os.Args = args
configFile := ArgConfig()
if configFile != "soar.yaml" {
t.Errorf("should return soar.yaml, but got %s", configFile)
}
}
}
...@@ -25,7 +25,7 @@ import ( ...@@ -25,7 +25,7 @@ import (
"github.com/astaxie/beego/logs" "github.com/astaxie/beego/logs"
) )
// Log 使用beego的log // Log 使用 beego 的 log
var Log *logs.BeeLogger var Log *logs.BeeLogger
// BaseDir 日志打印在binary的根路径 // BaseDir 日志打印在binary的根路径
...@@ -95,7 +95,7 @@ func fileName(original string) string { ...@@ -95,7 +95,7 @@ func fileName(original string) string {
return original[i+1:] return original[i+1:]
} }
// LogIfError 简化if err != nil打Error日志代码长度 // LogIfError 简化if err != nil 打 Error 日志代码长度
func LogIfError(err error, format string, v ...interface{}) { func LogIfError(err error, format string, v ...interface{}) {
if err != nil { if err != nil {
_, fn, line, _ := runtime.Caller(1) _, fn, line, _ := runtime.Caller(1)
...@@ -109,7 +109,7 @@ func LogIfError(err error, format string, v ...interface{}) { ...@@ -109,7 +109,7 @@ func LogIfError(err error, format string, v ...interface{}) {
} }
} }
// LogIfWarn 简化if err != nil打Warn日志代码长度 // LogIfWarn 简化if err != nil 打 Warn 日志代码长度
func LogIfWarn(err error, format string, v ...interface{}) { func LogIfWarn(err error, format string, v ...interface{}) {
if err != nil { if err != nil {
_, fn, line, _ := runtime.Caller(1) _, fn, line, _ := runtime.Caller(1)
......
...@@ -35,14 +35,14 @@ func TestLogger(t *testing.T) { ...@@ -35,14 +35,14 @@ func TestLogger(t *testing.T) {
func TestCaller(t *testing.T) { func TestCaller(t *testing.T) {
caller := Caller() caller := Caller()
if caller != "testing.tRunner" { if caller != "testing.tRunner" {
t.Error("get caller failer") t.Error("get caller failed")
} }
} }
func TestGetFunctionName(t *testing.T) { func TestGetFunctionName(t *testing.T) {
f := GetFunctionName() f := GetFunctionName()
if f != "TestGetFunctionName" { if f != "TestGetFunctionName" {
t.Error("get functionname failer") t.Error("get functionname failed")
} }
} }
......
此差异已折叠。
...@@ -50,7 +50,7 @@ func TestMarkdown2Html(t *testing.T) { ...@@ -50,7 +50,7 @@ func TestMarkdown2Html(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
// golden文件拷贝成html文件,这步是给人看的 // golden 文件拷贝成 html 文件,这步是给人看的
gd, err := os.OpenFile("testdata/"+t.Name()+".golden", os.O_RDONLY, 0666) gd, err := os.OpenFile("testdata/"+t.Name()+".golden", os.O_RDONLY, 0666)
if nil != err { if nil != err {
t.Fatal(err) t.Fatal(err)
......
...@@ -21,7 +21,7 @@ import ( ...@@ -21,7 +21,7 @@ import (
"strings" "strings"
) )
// Meta 以'database'为key, DB的map,按db->table->column组织的元数据 // Meta 以 'database' 为 key, DB 的 map, 按 db->table->column 组织的元数据
type Meta map[string]*DB type Meta map[string]*DB
// DB 数据库相关的结构体 // DB 数据库相关的结构体
...@@ -110,7 +110,7 @@ func JoinColumnsName(cols []*Column, sep string) string { ...@@ -110,7 +110,7 @@ func JoinColumnsName(cols []*Column, sep string) string {
return strings.Trim(name, sep) return strings.Trim(name, sep)
} }
// Tables 获取Meta中指定db的所有表名 // Tables 获取 Meta 中指定 db 的所有表名
// Input:数据库名 // Input:数据库名
// Output:表名组成的list // Output:表名组成的list
func (b Meta) Tables(db string) []string { func (b Meta) Tables(db string) []string {
...@@ -132,7 +132,7 @@ func (b Meta) SetDefault(defaultDB string) Meta { ...@@ -132,7 +132,7 @@ func (b Meta) SetDefault(defaultDB string) Meta {
for db := range b { for db := range b {
if db == "" { if db == "" {
// 当获取到的join中的DB为空的时候,说明SQL未显示的指定DB,即使用的是rEnv默认DB,需要将表合并到原DB // 当获取到的 join 中的 DB 为空的时候,说明 SQL 未显示的指定 DB,即使用的是 rEnv 默认 DB, 需要将表合并到原 DB
if _, ok := b[defaultDB]; ok { if _, ok := b[defaultDB]; ok {
for tbName, table := range b[""].Table { for tbName, table := range b[""].Table {
if _, ok := b[defaultDB].Table[tbName]; ok { if _, ok := b[defaultDB].Table[tbName]; ok {
...@@ -156,8 +156,8 @@ func (b Meta) SetDefault(defaultDB string) Meta { ...@@ -156,8 +156,8 @@ func (b Meta) SetDefault(defaultDB string) Meta {
return b return b
} }
// MergeColumn 将使用到的列按db->table组织去重 // MergeColumn 将使用到的列按 db->table 组织去重
// 注意:Column中的db, table信息可能为空,需要提前通过env环境补齐再调用该函数。 // 注意:Column 中的 db, table 信息可能为空,需要提前通过env环境补齐再调用该函数。
// @input: 目标列list, 源列list(可以将多个源合并到一个目标列list) // @input: 目标列list, 源列list(可以将多个源合并到一个目标列list)
// @output: 合并后的列list // @output: 合并后的列list
func MergeColumn(dst []*Column, src ...*Column) []*Column { func MergeColumn(dst []*Column, src ...*Column) []*Column {
......
...@@ -32,10 +32,8 @@ func HandleSignal(f func()) { ...@@ -32,10 +32,8 @@ func HandleSignal(f func()) {
syscall.SIGQUIT) syscall.SIGQUIT)
go func() { go func() {
select { n := <-sc
case n := <-sc: Log.Info("receive signal %v, closing", n)
Log.Info("receive signal %v, closing", n) f()
f()
}
}() }()
} }
/*
* 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 common
import (
"fmt"
"testing"
)
func TestHandleSignal(t *testing.T) {
HandleSignal(func() {
fmt.Println("done")
})
}
...@@ -28,7 +28,7 @@ import ( ...@@ -28,7 +28,7 @@ import (
"sort" "sort"
) )
// GoldenDiff 从gofmt学来的测试方法 // GoldenDiff 从 gofmt 学来的测试方法
// https://medium.com/soon-london/testing-with-golden-files-in-go-7fccc71c43d3 // https://medium.com/soon-london/testing-with-golden-files-in-go-7fccc71c43d3
func GoldenDiff(f func(), name string, update *bool) error { func GoldenDiff(f func(), name string, update *bool) error {
var b bytes.Buffer var b bytes.Buffer
......
...@@ -39,7 +39,7 @@ const ( ...@@ -39,7 +39,7 @@ const (
JSONFormatExplain // JSON格式输出 JSONFormatExplain // JSON格式输出
) )
// ExplainFormatType EXPLAIN支持的FORMAT_TYPE // ExplainFormatType EXPLAIN 支持的 FORMAT_TYPE
var ExplainFormatType = map[string]int{ var ExplainFormatType = map[string]int{
"traditional": 0, "traditional": 0,
"json": 1, "json": 1,
...@@ -80,7 +80,7 @@ type ExplainRow struct { ...@@ -80,7 +80,7 @@ type ExplainRow struct {
AccessType string AccessType string
PossibleKeys []string PossibleKeys []string
Key string Key string
KeyLen string // 索引长度,如果发生了index_merge, KeyLen格式为N,N,所以不能定义为整型 KeyLen string // 索引长度,如果发生了index_merge, KeyLen 格式为 N,N,所以不能定义为整型
Ref []string Ref []string
Rows int Rows int
Filtered float64 // 5.6 JSON, 5.7+, 5.5 EXTENDED Filtered float64 // 5.6 JSON, 5.7+, 5.5 EXTENDED
...@@ -88,7 +88,7 @@ type ExplainRow struct { ...@@ -88,7 +88,7 @@ type ExplainRow struct {
Extra string Extra string
} }
// ExplainWarning explain extended后SHOW WARNINGS输出的结果 // ExplainWarning explain extended 后 SHOW WARNINGS 输出的结果
type ExplainWarning struct { type ExplainWarning struct {
Level string Level string
Code int Code int
...@@ -117,7 +117,7 @@ type ExplainJSONMaterializedFromSubquery struct { ...@@ -117,7 +117,7 @@ type ExplainJSONMaterializedFromSubquery struct {
QueryBlock *ExplainJSONQueryBlock `json:"query_block"` QueryBlock *ExplainJSONQueryBlock `json:"query_block"`
} }
// 该变量用于存放JSON到Traditional模式的所有ExplainJSONTable // 该变量用于存放 JSON 到 Traditional 模式的所有 ExplainJSONTable
var explainJSONTables []*ExplainJSONTable var explainJSONTables []*ExplainJSONTable
// ExplainJSONTable JSON // ExplainJSONTable JSON
...@@ -451,8 +451,8 @@ func FormatJSONIntoTraditional(explainJSON string) []*ExplainRow { ...@@ -451,8 +451,8 @@ func FormatJSONIntoTraditional(explainJSON string) []*ExplainRow {
return explainRows return explainRows
} }
// ConvertExplainJSON2Row 将JSON格式转成ROW格式,为方便统一做优化建议 // ConvertExplainJSON2Row 将 JSON 格式转成 ROW 格式,为方便统一做优化建议
// 但是会损失一些JSON特有的分析结果 // 但是会损失一些 JSON 特有的分析结果
func ConvertExplainJSON2Row(explainJSON *ExplainJSON) []*ExplainRow { func ConvertExplainJSON2Row(explainJSON *ExplainJSON) []*ExplainRow {
buf, err := json.Marshal(explainJSON) buf, err := json.Marshal(explainJSON)
if err != nil { if err != nil {
...@@ -461,8 +461,8 @@ func ConvertExplainJSON2Row(explainJSON *ExplainJSON) []*ExplainRow { ...@@ -461,8 +461,8 @@ func ConvertExplainJSON2Row(explainJSON *ExplainJSON) []*ExplainRow {
return FormatJSONIntoTraditional(string(buf)) return FormatJSONIntoTraditional(string(buf))
} }
// 用于检测MySQL版本是否低于MySQL5.6 // 用于检测 MySQL 版本是否低于 MySQL5.6
// 低于5.6 返回 true, 表示需要改写非SELECT的SQL --> SELECT // 低于5.6 返回 true, 表示需要改写非 SELECT 的 SQL --> SELECT
func (db *Connector) supportExplainWrite() (bool, error) { func (db *Connector) supportExplainWrite() (bool, error) {
defer func() { defer func() {
err := recover() err := recover()
...@@ -471,8 +471,8 @@ func (db *Connector) supportExplainWrite() (bool, error) { ...@@ -471,8 +471,8 @@ func (db *Connector) supportExplainWrite() (bool, error) {
} }
}() }()
// 5.6以上版本支持EXPLAIN UPDATE/DELETE等语句,但需要开启写入 // 5.6以上版本支持 EXPLAIN UPDATE/DELETE 等语句,但需要开启写入
// 如开启了read_only,EXPLAIN UPDATE/DELETE也会受限制 // 如开启了 read_only, EXPLAIN UPDATE/DELETE 也会受限制
if common.Config.TestDSN.Version >= 560 { if common.Config.TestDSN.Version >= 560 {
readOnly, err := db.SingleIntValue("read_only") readOnly, err := db.SingleIntValue("read_only")
if err != nil { if err != nil {
...@@ -506,8 +506,8 @@ func (db *Connector) explainAbleSQL(sql string) (string, error) { ...@@ -506,8 +506,8 @@ func (db *Connector) explainAbleSQL(sql string) (string, error) {
} }
switch stmt.(type) { switch stmt.(type) {
case *sqlparser.Insert, *sqlparser.Update, *sqlparser.Delete: // REPLACE和INSERT的AST基本相同,只是Action不同 case *sqlparser.Insert, *sqlparser.Update, *sqlparser.Delete: // REPLACE 和 INSERT 的 AST 基本相同,只是 Action 不同
// 判断Explain的SQL是否需要被改写 // 判断 Explain 的 SQL 是否需要被改写
need, err := db.supportExplainWrite() need, err := db.supportExplainWrite()
if err != nil { if err != nil {
common.Log.Error("explainAbleSQL db.supportExplainWrite Error: %v", err) common.Log.Error("explainAbleSQL db.supportExplainWrite Error: %v", err)
...@@ -901,12 +901,12 @@ func parseJSONExplainText(content string) (*ExplainJSON, error) { ...@@ -901,12 +901,12 @@ func parseJSONExplainText(content string) (*ExplainJSON, error) {
return explainJSON, err return explainJSON, err
} }
// ParseExplainResult 分析mysql执行explain的结果,返回ExplainInfo结构化数据 // ParseExplainResult 分析 mysql 执行 explain 的结果,返回 ExplainInfo 结构化数据
func ParseExplainResult(res *QueryResult, formatType int) (exp *ExplainInfo, err error) { func ParseExplainResult(res *QueryResult, formatType int) (exp *ExplainInfo, err error) {
exp = &ExplainInfo{ exp = &ExplainInfo{
ExplainFormat: formatType, ExplainFormat: formatType,
} }
// JSON格式直接调用文本方式解析 // JSON 格式直接调用文本方式解析
if formatType == JSONFormatExplain { if formatType == JSONFormatExplain {
exp.ExplainJSON, err = parseJSONExplainText(res.Rows[0].Str(0)) exp.ExplainJSON, err = parseJSONExplainText(res.Rows[0].Str(0))
return exp, err return exp, err
...@@ -917,11 +917,11 @@ func ParseExplainResult(res *QueryResult, formatType int) (exp *ExplainInfo, err ...@@ -917,11 +917,11 @@ func ParseExplainResult(res *QueryResult, formatType int) (exp *ExplainInfo, err
for i, f := range res.Result.Fields() { for i, f := range res.Result.Fields() {
colIdx[i] = strings.ToLower(f.Name) colIdx[i] = strings.ToLower(f.Name)
} }
// 补全ExplainRows // 补全 ExplainRows
var explainrows []*ExplainRow var explainrows []*ExplainRow
for _, row := range res.Rows { for _, row := range res.Rows {
expRow := &ExplainRow{Partitions: "NULL", Filtered: 0.00} expRow := &ExplainRow{Partitions: "NULL", Filtered: 0.00}
// list到map的转换 // list 到 map 的转换
for i := range row { for i := range row {
switch colIdx[i] { switch colIdx[i] {
case "id": case "id":
...@@ -981,7 +981,7 @@ func ParseExplainResult(res *QueryResult, formatType int) (exp *ExplainInfo, err ...@@ -981,7 +981,7 @@ func ParseExplainResult(res *QueryResult, formatType int) (exp *ExplainInfo, err
return exp, err return exp, err
} }
// Explain 获取SQL的explain信息 // Explain 获取 SQL 的 explain 信息
func (db *Connector) Explain(sql string, explainType int, formatType int) (exp *ExplainInfo, err error) { func (db *Connector) Explain(sql string, explainType int, formatType int) (exp *ExplainInfo, err error) {
exp = &ExplainInfo{SQL: sql} exp = &ExplainInfo{SQL: sql}
if explainType != TraditionalExplainType { if explainType != TraditionalExplainType {
...@@ -1009,11 +1009,11 @@ func (db *Connector) Explain(sql string, explainType int, formatType int) (exp * ...@@ -1009,11 +1009,11 @@ func (db *Connector) Explain(sql string, explainType int, formatType int) (exp *
return exp, err return exp, err
} }
// PrintMarkdownExplainTable 打印markdown格式的explain table // PrintMarkdownExplainTable 打印 markdown 格式的 explain table
func PrintMarkdownExplainTable(exp *ExplainInfo) string { func PrintMarkdownExplainTable(exp *ExplainInfo) string {
var buf []string var buf []string
rows := exp.ExplainRows rows := exp.ExplainRows
// JSON转换为TRADITIONAL格式 // JSON 转换为 TRADITIONAL 格式
if exp.ExplainFormat == JSONFormatExplain { if exp.ExplainFormat == JSONFormatExplain {
buf = append(buf, fmt.Sprint("以下为JSON格式转为传统格式EXPLAIN表格", "\n\n")) buf = append(buf, fmt.Sprint("以下为JSON格式转为传统格式EXPLAIN表格", "\n\n"))
rows = ConvertExplainJSON2Row(exp.ExplainJSON) rows = ConvertExplainJSON2Row(exp.ExplainJSON)
......
...@@ -65,7 +65,7 @@ func (db *Connector) Query(sql string, params ...interface{}) (*QueryResult, err ...@@ -65,7 +65,7 @@ func (db *Connector) Query(sql string, params ...interface{}) (*QueryResult, err
return nil, errors.New("TestDsn Disable") return nil, errors.New("TestDsn Disable")
} }
// 数据库安全性检查:如果Connector的IP端口与TEST环境不一致,则启用SQL白名单 // 数据库安全性检查:如果 Connector 的 IP 端口与 TEST 环境不一致,则启用SQL白名单
// 不在白名单中的SQL不允许执行 // 不在白名单中的SQL不允许执行
// 执行环境与test环境不相同 // 执行环境与test环境不相同
if db.Addr != common.Config.TestDSN.Addr && db.dangerousQuery(sql) { if db.Addr != common.Config.TestDSN.Addr && db.dangerousQuery(sql) {
...@@ -97,7 +97,7 @@ func (db *Connector) Query(sql string, params ...interface{}) (*QueryResult, err ...@@ -97,7 +97,7 @@ func (db *Connector) Query(sql string, params ...interface{}) (*QueryResult, err
} }
} }
// SHOW WARNINGS并不会影响last_query_cost // SHOW WARNINGS 并不会影响 last_query_cost
if common.Config.ShowLastQueryCost { if common.Config.ShowLastQueryCost {
cost, _, err := conn.Query("SHOW SESSION STATUS LIKE 'last_query_cost'") cost, _, err := conn.Query("SHOW SESSION STATUS LIKE 'last_query_cost'")
if err == nil { if err == nil {
...@@ -278,7 +278,7 @@ func RemoveSQLComments(sql []byte) []byte { ...@@ -278,7 +278,7 @@ func RemoveSQLComments(sql []byte) []byte {
}) })
} }
// 为了防止在Online环境进行误操作,通过dangerousQuery来判断能否在Online执行 // 为了防止在 Online 环境进行误操作,通过 dangerousQuery 来判断能否在 Online 执行
func (db *Connector) dangerousQuery(query string) bool { func (db *Connector) dangerousQuery(query string) bool {
queries, err := sqlparser.SplitStatementToPieces(strings.TrimSpace(strings.ToLower(query))) queries, err := sqlparser.SplitStatementToPieces(strings.TrimSpace(strings.ToLower(query)))
if err != nil { if err != nil {
......
...@@ -42,7 +42,7 @@ type ProfilingRow struct { ...@@ -42,7 +42,7 @@ type ProfilingRow struct {
// Profiling 执行SQL,并对其Profiling // Profiling 执行SQL,并对其Profiling
func (db *Connector) Profiling(sql string, params ...interface{}) (*QueryResult, error) { func (db *Connector) Profiling(sql string, params ...interface{}) (*QueryResult, error) {
// 过滤不需要profiling的SQL // 过滤不需要 profiling 的 SQL
switch sqlparser.Preview(sql) { switch sqlparser.Preview(sql) {
case sqlparser.StmtSelect, sqlparser.StmtUpdate, sqlparser.StmtDelete: case sqlparser.StmtSelect, sqlparser.StmtUpdate, sqlparser.StmtDelete:
default: default:
...@@ -54,7 +54,7 @@ func (db *Connector) Profiling(sql string, params ...interface{}) (*QueryResult, ...@@ -54,7 +54,7 @@ func (db *Connector) Profiling(sql string, params ...interface{}) (*QueryResult,
return nil, errors.New("TestDsn Disable") return nil, errors.New("TestDsn Disable")
} }
// 数据库安全性检查:如果Connector的IP端口与TEST环境不一致,则启用SQL白名单 // 数据库安全性检查:如果 Connector 的 IP 端口与 TEST 环境不一致,则启用 SQL 白名单
// 不在白名单中的SQL不允许执行 // 不在白名单中的SQL不允许执行
// 执行环境与test环境不相同 // 执行环境与test环境不相同
if db.Addr != common.Config.TestDSN.Addr && db.dangerousQuery(sql) { if db.Addr != common.Config.TestDSN.Addr && db.dangerousQuery(sql) {
......
...@@ -52,7 +52,7 @@ func (db *Connector) SamplingData(remote Connector, tables ...string) error { ...@@ -52,7 +52,7 @@ func (db *Connector) SamplingData(remote Connector, tables ...string) error {
// 计算需要泵取的数据量 // 计算需要泵取的数据量
wantRowsCount := 300 * common.Config.SamplingStatisticTarget wantRowsCount := 300 * common.Config.SamplingStatisticTarget
// 设置数据采样单条SQL中value的数量 // 设置数据采样单条 SQL 中 value 的数量
// 该数值越大,在内存中缓存的data就越多,但相对的,插入时速度就越快 // 该数值越大,在内存中缓存的data就越多,但相对的,插入时速度就越快
maxValCount := 200 maxValCount := 200
...@@ -188,7 +188,7 @@ func startSampling(conn, localConn mysql.Conn, database, table string, factor fl ...@@ -188,7 +188,7 @@ func startSampling(conn, localConn mysql.Conn, database, table string, factor fl
} }
// 非text/varchar类的数据类型,如果dump出的数据为空,则说明该值为null值 // 非text/varchar类的数据类型,如果dump出的数据为空,则说明该值为null值
// 应转换其value为null,如果用空('')进行替代,会导致出现语法错误。 // 应转换其 value 为 null,如果用空('')进行替代,会导致出现语法错误。
if len(dataTypes) == len(res.Fields()) && values[i] == "" && if len(dataTypes) == len(res.Fields()) && values[i] == "" &&
(!strings.Contains(dataTypes[i], "char") || (!strings.Contains(dataTypes[i], "char") ||
!strings.Contains(dataTypes[i], "text")) { !strings.Contains(dataTypes[i], "text")) {
......
...@@ -41,7 +41,7 @@ type tableStatusRow struct { ...@@ -41,7 +41,7 @@ type tableStatusRow struct {
Engine string // 该表使用的存储引擎 Engine string // 该表使用的存储引擎
Version int // 该表的 .frm 文件版本号 Version int // 该表的 .frm 文件版本号
RowFormat string // 该表使用的行存储格式 RowFormat string // 该表使用的行存储格式
Rows int64 // 表行数InnoDB 引擎中为预估值,甚至可能会有40%~50%的数值偏差 Rows int64 // 表行数, InnoDB 引擎中为预估值,甚至可能会有40%~50%的数值偏差
AvgRowLength int // 平均行长度 AvgRowLength int // 平均行长度
// MyISAM: Data_length 为数据文件的大小,单位为 bytes // MyISAM: Data_length 为数据文件的大小,单位为 bytes
...@@ -333,7 +333,7 @@ func NewTableDesc(tableName string) *TableDesc { ...@@ -333,7 +333,7 @@ func NewTableDesc(tableName string) *TableDesc {
} }
} }
// ShowColumns 获取DB中所有的columns // ShowColumns 获取 DB 中所有的 columns
func (db *Connector) ShowColumns(tableName string) (*TableDesc, error) { func (db *Connector) ShowColumns(tableName string) (*TableDesc, error) {
tbDesc := NewTableDesc(tableName) tbDesc := NewTableDesc(tableName)
...@@ -545,8 +545,8 @@ type ReferenceValue struct { ...@@ -545,8 +545,8 @@ type ReferenceValue struct {
// ShowReference 查找所有的外键信息 // ShowReference 查找所有的外键信息
func (db *Connector) ShowReference(dbName string, tbName ...string) ([]ReferenceValue, error) { func (db *Connector) ShowReference(dbName string, tbName ...string) ([]ReferenceValue, error) {
var referenceValues []ReferenceValue var referenceValues []ReferenceValue
sql := `SELECT C.REFERENCED_TABLE_SCHEMA,C.REFERENCED_TABLE_NAME,C.TABLE_SCHEMA,C.TABLE_NAME,C.CONSTRAINT_NAME sql := `SELECT C.REFERENCED_TABLE_SCHEMA,C.REFERENCED_TABLE_NAME,C.TABLE_SCHEMA,C.TABLE_NAME,C.CONSTRAINT_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE C JOIN INFORMATION_SCHEMA. TABLES T ON T.TABLE_NAME = C.TABLE_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE C JOIN INFORMATION_SCHEMA. TABLES T ON T.TABLE_NAME = C.TABLE_NAME
WHERE C.REFERENCED_TABLE_NAME IS NOT NULL` WHERE C.REFERENCED_TABLE_NAME IS NOT NULL`
sql = sql + fmt.Sprintf(` AND C.TABLE_SCHEMA = "%s"`, dbName) sql = sql + fmt.Sprintf(` AND C.TABLE_SCHEMA = "%s"`, dbName)
......
...@@ -49,7 +49,7 @@ func (db *Connector) Trace(sql string, params ...interface{}) (*QueryResult, err ...@@ -49,7 +49,7 @@ func (db *Connector) Trace(sql string, params ...interface{}) (*QueryResult, err
return nil, errors.New("version < 5.6, not support trace") return nil, errors.New("version < 5.6, not support trace")
} }
// 过滤不需要Trace的SQL // 过滤不需要 Trace 的 SQL
switch sqlparser.Preview(sql) { switch sqlparser.Preview(sql) {
case sqlparser.StmtSelect, sqlparser.StmtUpdate, sqlparser.StmtDelete: case sqlparser.StmtSelect, sqlparser.StmtUpdate, sqlparser.StmtDelete:
sql = "explain " + sql sql = "explain " + sql
...@@ -63,7 +63,7 @@ func (db *Connector) Trace(sql string, params ...interface{}) (*QueryResult, err ...@@ -63,7 +63,7 @@ func (db *Connector) Trace(sql string, params ...interface{}) (*QueryResult, err
return nil, errors.New("TestDsn Disable") return nil, errors.New("TestDsn Disable")
} }
// 数据库安全性检查:如果Connector的IP端口与TEST环境不一致,则启用SQL白名单 // 数据库安全性检查:如果 Connector 的 IP 端口与 TEST 环境不一致,则启用SQL白名单
// 不在白名单中的SQL不允许执行 // 不在白名单中的SQL不允许执行
// 执行环境与test环境不相同 // 执行环境与test环境不相同
if db.Addr != common.Config.TestDSN.Addr && db.dangerousQuery(sql) { if db.Addr != common.Config.TestDSN.Addr && db.dangerousQuery(sql) {
......
...@@ -175,7 +175,7 @@ func (ve *VirtualEnv) CleanupTestDatabase() { ...@@ -175,7 +175,7 @@ func (ve *VirtualEnv) CleanupTestDatabase() {
continue continue
} }
subHour := time.Now().Sub(pastTime).Hours() subHour := time.Since(pastTime).Hours()
if subHour > float64(minHour) { if subHour > float64(minHour) {
if _, err := ve.Query("drop database %s", testDatabase); err != nil { if _, err := ve.Query("drop database %s", testDatabase); err != nil {
common.Log.Error("CleanupTestDatabase failed Error: %s", err.Error()) common.Log.Error("CleanupTestDatabase failed Error: %s", err.Error())
...@@ -188,7 +188,6 @@ func (ve *VirtualEnv) CleanupTestDatabase() { ...@@ -188,7 +188,6 @@ func (ve *VirtualEnv) CleanupTestDatabase() {
} }
common.Log.Debug("CleanupTestDatabase done") common.Log.Debug("CleanupTestDatabase done")
return
} }
// BuildVirtualEnv rEnv为SQL源环境,DB使用的信息从接口获取 // BuildVirtualEnv rEnv为SQL源环境,DB使用的信息从接口获取
...@@ -375,7 +374,7 @@ func (ve VirtualEnv) createDatabase(rEnv database.Connector, dbName string) erro ...@@ -375,7 +374,7 @@ func (ve VirtualEnv) createDatabase(rEnv database.Connector, dbName string) erro
如果一个SQL中存在多个数据库,则只能有一个数据库是没有在SQL中被显示指定的(即DSN中指定的数据库) 如果一个SQL中存在多个数据库,则只能有一个数据库是没有在SQL中被显示指定的(即DSN中指定的数据库)
TODO: TODO:
在一些可能的情况下,由于数据库配置的不一致(如SQL_MODE不同)导致remote环境的库表无法正确的在测试环境进行同步, 在一些可能的情况下,由于数据库配置的不一致(如SQL_MODE不同)导致remote环境的库表无法正确的在测试环境进行同步,
soar能够做出判断并进行session级别的修改,但是这一阶段可用性保证应该是由用户提供两个完全相同(或测试环境兼容线上环境) soar 能够做出判断并进行 session 级别的修改,但是这一阶段可用性保证应该是由用户提供两个完全相同(或测试环境兼容线上环境)
的数据库环境来实现的。 的数据库环境来实现的。
*/ */
func (ve VirtualEnv) createTable(rEnv database.Connector, dbName, tbName string) error { func (ve VirtualEnv) createTable(rEnv database.Connector, dbName, tbName string) error {
......
...@@ -115,6 +115,10 @@ func TestNewVirtualEnv(t *testing.T) { ...@@ -115,6 +115,10 @@ func TestNewVirtualEnv(t *testing.T) {
func TestCleanupTestDatabase(t *testing.T) { func TestCleanupTestDatabase(t *testing.T) {
vEnv, _ := BuildEnv() vEnv, _ := BuildEnv()
if common.Config.TestDSN.Disable {
common.Log.Warn("common.Config.TestDSN.Disable=true, by pass TestCleanupTestDatabase")
return
}
vEnv.Query("drop database if exists optimizer_060102150405_xxxxxxxxxxxxxxxx") vEnv.Query("drop database if exists optimizer_060102150405_xxxxxxxxxxxxxxxx")
_, err := vEnv.Query("create database optimizer_060102150405_xxxxxxxxxxxxxxxx") _, err := vEnv.Query("create database optimizer_060102150405_xxxxxxxxxxxxxxxx")
if err != nil { if err != nil {
...@@ -123,7 +127,7 @@ func TestCleanupTestDatabase(t *testing.T) { ...@@ -123,7 +127,7 @@ func TestCleanupTestDatabase(t *testing.T) {
vEnv.CleanupTestDatabase() vEnv.CleanupTestDatabase()
_, err = vEnv.Query("show create database optimizer_060102150405_xxxxxxxxxxxxxxxx") _, err = vEnv.Query("show create database optimizer_060102150405_xxxxxxxxxxxxxxxx")
if err == nil { if err == nil {
t.Error("optimizer_060102150405_xxxxxxxxxxxxxxxx exist, should be droped") t.Error("optimizer_060102150405_xxxxxxxxxxxxxxxx exist, should be dropped")
} }
vEnv.Query("drop database if exists optimizer_060102150405") vEnv.Query("drop database if exists optimizer_060102150405")
...@@ -134,7 +138,7 @@ func TestCleanupTestDatabase(t *testing.T) { ...@@ -134,7 +138,7 @@ func TestCleanupTestDatabase(t *testing.T) {
vEnv.CleanupTestDatabase() vEnv.CleanupTestDatabase()
_, err = vEnv.Query("drop database optimizer_060102150405") _, err = vEnv.Query("drop database optimizer_060102150405")
if err != nil { if err != nil {
t.Error("optimizer_060102150405 not exist, should not be droped") t.Error("optimizer_060102150405 not exist, should not be dropped")
} }
} }
......
...@@ -1016,68 +1016,68 @@ ...@@ -1016,68 +1016,68 @@
{ {
"checksumSHA1": "w8FCRjH70gM6QttB9QrEh9Y1x64=", "checksumSHA1": "w8FCRjH70gM6QttB9QrEh9Y1x64=",
"path": "vitess.io/vitess", "path": "vitess.io/vitess",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=", "checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=",
"path": "vitess.io/vitess/go/bytes2", "path": "vitess.io/vitess/go/bytes2",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=", "checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=",
"path": "vitess.io/vitess/go/hack", "path": "vitess.io/vitess/go/hack",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "e1WJ7vCnVrlQQQlc6n/FewCDMso=", "checksumSHA1": "e1WJ7vCnVrlQQQlc6n/FewCDMso=",
"path": "vitess.io/vitess/go/sqltypes", "path": "vitess.io/vitess/go/sqltypes",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=", "checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=",
"path": "vitess.io/vitess/go/vt/log", "path": "vitess.io/vitess/go/vt/log",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "XozR8bmeSR5KTe/nlUJkpJY2HKI=", "checksumSHA1": "XozR8bmeSR5KTe/nlUJkpJY2HKI=",
"path": "vitess.io/vitess/go/vt/proto/query", "path": "vitess.io/vitess/go/vt/proto/query",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "OnWsUHLDKcO3spwH0jD55SvKD24=", "checksumSHA1": "OnWsUHLDKcO3spwH0jD55SvKD24=",
"path": "vitess.io/vitess/go/vt/proto/topodata", "path": "vitess.io/vitess/go/vt/proto/topodata",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "sBAuZ/itMR8U8qbK4yLHxkP6Cpc=", "checksumSHA1": "sBAuZ/itMR8U8qbK4yLHxkP6Cpc=",
"path": "vitess.io/vitess/go/vt/proto/vtgate", "path": "vitess.io/vitess/go/vt/proto/vtgate",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "pLWM+SPGZs3k+IhjktE/cGUlpM0=", "checksumSHA1": "pLWM+SPGZs3k+IhjktE/cGUlpM0=",
"path": "vitess.io/vitess/go/vt/proto/vtrpc", "path": "vitess.io/vitess/go/vt/proto/vtrpc",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "re3V8oX+ujxHbNZuB+QEtrcXxE8=", "checksumSHA1": "re3V8oX+ujxHbNZuB+QEtrcXxE8=",
"path": "vitess.io/vitess/go/vt/sqlparser", "path": "vitess.io/vitess/go/vt/sqlparser",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
}, },
{ {
"checksumSHA1": "oF4XzuOzwvj1iduX/lYqNSyY/HM=", "checksumSHA1": "oF4XzuOzwvj1iduX/lYqNSyY/HM=",
"path": "vitess.io/vitess/go/vt/vterrors", "path": "vitess.io/vitess/go/vt/vterrors",
"revision": "3db5b2f0eb75f490c755bf20d35fd3afdacdd920", "revision": "6fca9975675109decbf1c389641597929824eeba",
"revisionTime": "2018-10-30T14:25:51Z" "revisionTime": "2018-10-31T20:10:04Z"
} }
], ],
"rootPath": "github.com/XiaoMi/soar" "rootPath": "github.com/XiaoMi/soar"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册