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