未验证 提交 7e7eead0 编写于 作者: X xiyangxixian 提交者: GitHub

Merge pull request #186 from XiaoMi/dev

fix #184 and #173
...@@ -29,6 +29,3 @@ script: ...@@ -29,6 +29,3 @@ script:
- make docker - make docker
- make cover - make cover
- make test-cli - make test-cli
after_success:
- bash <(curl -s https://codecov.io/bash)
# CHANGELOG # CHANGELOG
## 2018-12 ## 2019-01
- DOING: english translation - DOING: english translation
- add JSONFind function, which support JSON iterate
- add new test database `world_x`
- SplitStatement support optimizer hint `/*+xxx */`
- include [bats](https://github.com/bats-core/bats-core) bash auto test framework
- fix explain result with multi rows error
- fix #178 JSON datatype only support utf8mb4
## 2018-12
- replace mysql database driver mymysql with go-sql-driver - replace mysql database driver mymysql with go-sql-driver
- add new -report-type [ast-json, tiast-json] - add new -report-type [ast-json, tiast-json]
- command line dsn args support '@', '/', ':' in password - command line dsn args support '@', '/', ':' in password
......
...@@ -187,7 +187,7 @@ release: build ...@@ -187,7 +187,7 @@ release: build
.PHONY: docker .PHONY: docker
docker: docker:
@echo "$(CGREEN)Build mysql test enviorment ...$(CEND)" @echo "$(CGREEN)Build mysql test environment ...$(CEND)"
@docker stop soar-mysql 2>/dev/null || true @docker stop soar-mysql 2>/dev/null || true
@docker wait soar-mysql 2>/dev/null >/dev/null || true @docker wait soar-mysql 2>/dev/null >/dev/null || true
@echo "docker run --name soar-mysql $(MYSQL_RELEASE):$(MYSQL_VERSION)" @echo "docker run --name soar-mysql $(MYSQL_RELEASE):$(MYSQL_VERSION)"
...@@ -204,7 +204,7 @@ docker: ...@@ -204,7 +204,7 @@ docker:
timeout=`expr $$timeout - 1`; \ timeout=`expr $$timeout - 1`; \
printf '.' ; sleep 1 ; \ printf '.' ; sleep 1 ; \
else \ else \
echo "." ; echo "mysql test enviorment is ready!" ; break ; \ echo "." ; echo "mysql test environment is ready!" ; break ; \
fi ; \ fi ; \
if [ $$timeout = 0 ] ; then \ if [ $$timeout = 0 ] ; then \
echo "." ; echo "$(CRED)docker soar-mysql start timeout(180 s)!$(CEND)" ; exit 1 ; \ echo "." ; echo "$(CRED)docker soar-mysql start timeout(180 s)!$(CEND)" ; exit 1 ; \
......
...@@ -31,6 +31,7 @@ import ( ...@@ -31,6 +31,7 @@ import (
"github.com/percona/go-mysql/query" "github.com/percona/go-mysql/query"
tidb "github.com/pingcap/parser/ast" tidb "github.com/pingcap/parser/ast"
"github.com/pingcap/parser/mysql" "github.com/pingcap/parser/mysql"
"github.com/tidwall/gjson"
"vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/sqlparser"
) )
...@@ -1312,37 +1313,16 @@ func (q *Query4Audit) RuleLoadFile() Rule { ...@@ -1312,37 +1313,16 @@ func (q *Query4Audit) RuleLoadFile() Rule {
func (q *Query4Audit) RuleMultiCompare() Rule { func (q *Query4Audit) RuleMultiCompare() Rule {
var rule = q.RuleOK() var rule = q.RuleOK()
if q.TiStmt != nil { if q.TiStmt != nil {
for _, tiStmt := range q.TiStmt { json := ast.StmtNode2JSON(q.Query, "", "")
switch node := tiStmt.(type) { whereJSON := common.JSONFind(json, "Where")
case *tidb.SelectStmt: for _, where := range whereJSON {
switch where := node.Where.(type) { conds := []string{where}
case *tidb.BinaryOperationExpr: conds = append(conds, common.JSONFind(where, "L")...)
switch where.L.(type) { conds = append(conds, common.JSONFind(where, "R")...)
case *tidb.BinaryOperationExpr: for _, cond := range conds {
if where.Op.String() == "eq" { if gjson.Get(cond, "Op").Int() == 7 && gjson.Get(cond, "L.Op").Int() == 7 {
rule = HeuristicRules["RES.009"] rule = HeuristicRules["RES.009"]
} return rule
}
}
case *tidb.UpdateStmt:
switch where := node.Where.(type) {
case *tidb.BinaryOperationExpr:
switch where.L.(type) {
case *tidb.BinaryOperationExpr:
if where.Op.String() == "eq" {
rule = HeuristicRules["RES.009"]
}
}
}
case *tidb.DeleteStmt:
switch where := node.Where.(type) {
case *tidb.BinaryOperationExpr:
switch where.L.(type) {
case *tidb.BinaryOperationExpr:
if where.Op.String() == "eq" {
rule = HeuristicRules["RES.009"]
}
}
} }
} }
} }
......
...@@ -946,6 +946,9 @@ func TestRuleMultiCompare(t *testing.T) { ...@@ -946,6 +946,9 @@ func TestRuleMultiCompare(t *testing.T) {
sqls := [][]string{ sqls := [][]string{
{ {
"SELECT * FROM tbl WHERE col = col = 'abc'", "SELECT * FROM tbl WHERE col = col = 'abc'",
"SELECT * FROM tbl WHERE col = 'def' and col = col = 'abc'",
"SELECT * FROM tbl WHERE col = 'def' or col = col = 'abc'",
"SELECT * FROM tbl WHERE col = col = 'abc' and col = 'def'",
"UPDATE tbl set col = 1 WHERE col = col = 'abc'", "UPDATE tbl set col = 1 WHERE col = col = 'abc'",
"DELETE FROM tbl WHERE col = col = 'abc'", "DELETE FROM tbl WHERE col = col = 'abc'",
}, },
......
...@@ -17,14 +17,14 @@ ...@@ -17,14 +17,14 @@
package ast package ast
import ( import (
"encoding/json"
"github.com/XiaoMi/soar/common" "github.com/XiaoMi/soar/common"
"github.com/kr/pretty" "github.com/kr/pretty"
"github.com/pingcap/parser" "github.com/pingcap/parser"
"github.com/pingcap/parser/ast" "github.com/pingcap/parser/ast"
json "github.com/CorgiMan/json2"
// for pingcap parser // for pingcap parser
_ "github.com/pingcap/tidb/types/parser_driver" _ "github.com/pingcap/tidb/types/parser_driver"
) )
......
...@@ -210,6 +210,7 @@ func initQuery(query string) string { ...@@ -210,6 +210,7 @@ func initQuery(query string) string {
if err != nil { if err != nil {
common.Log.Critical("ioutil.ReadAll Error: %v", err) common.Log.Critical("ioutil.ReadAll Error: %v", err)
} }
common.Log.Debug("initQuery get query from os.Stdin")
return string(data) return string(data)
} }
...@@ -219,6 +220,7 @@ func initQuery(query string) string { ...@@ -219,6 +220,7 @@ func initQuery(query string) string {
if err != nil { if err != nil {
common.Log.Critical("ioutil.ReadFile Error: %v", err) common.Log.Critical("ioutil.ReadFile Error: %v", err)
} }
common.Log.Debug("initQuery get query from file: %s", query)
return string(data) return string(data)
} }
......
...@@ -93,7 +93,7 @@ type Configuration struct { ...@@ -93,7 +93,7 @@ type Configuration struct {
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"` // 复合索引中包含列的最大数量
MaxTextColsCount int `yaml:"max-text-cols-count"` // 表中含有的 text/blob 列的最大数量 MaxTextColsCount int `yaml:"max-text-cols-count"` // 表中含有的 text/blob 列的最大数量
MaxTotalRows int64 `yaml:"max-total-rows"` // 计算散粒度时,当数据行数大于 MaxTotalRows 即开启数据库保护模式,散粒度返回结果可信度下降 MaxTotalRows uint64 `yaml:"max-total-rows"` // 计算散粒度时,当数据行数大于 MaxTotalRows 即开启数据库保护模式,散粒度返回结果可信度下降
MaxQueryCost int64 `yaml:"max-query-cost"` // last_query_cost 超过该值时将给予警告 MaxQueryCost int64 `yaml:"max-query-cost"` // last_query_cost 超过该值时将给予警告
SpaghettiQueryLength int `yaml:"spaghetti-query-length"` // SQL最大长度警告,超过该长度会给警告 SpaghettiQueryLength int `yaml:"spaghetti-query-length"` // SQL最大长度警告,超过该长度会给警告
AllowDropIndex bool `yaml:"allow-drop-index"` // 允许输出删除重复索引的建议 AllowDropIndex bool `yaml:"allow-drop-index"` // 允许输出删除重复索引的建议
...@@ -426,7 +426,7 @@ func parseDSN(odbc string, d *Dsn) *Dsn { ...@@ -426,7 +426,7 @@ func parseDSN(odbc string, d *Dsn) *Dsn {
func ParseDSN(odbc string, d *Dsn) *Dsn { func ParseDSN(odbc string, d *Dsn) *Dsn {
cfg, err := mysql.ParseDSN(odbc) cfg, err := mysql.ParseDSN(odbc)
if err != nil { if err != nil {
Log.Warn("go-sql-driver/mysql.ParseDSN Error: %s, DSN: %s, try to use old version parseDSN", err.Error(), odbc) Log.Debug("go-sql-driver/mysql.ParseDSN Error: %s, DSN: %s, try to use old version parseDSN", err.Error(), odbc)
return parseDSN(odbc, d) return parseDSN(odbc, d)
} }
return newDSN(cfg) return newDSN(cfg)
...@@ -596,7 +596,7 @@ func readCmdFlags() error { ...@@ -596,7 +596,7 @@ func readCmdFlags() error {
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, 复合索引中包含列的最大数量")
maxTextColsCount := flag.Int("max-text-cols-count", Config.MaxTextColsCount, "MaxTextColsCount, 表中含有的 text/blob 列的最大数量") maxTextColsCount := flag.Int("max-text-cols-count", Config.MaxTextColsCount, "MaxTextColsCount, 表中含有的 text/blob 列的最大数量")
maxTotalRows := flag.Int64("max-total-rows", Config.MaxTotalRows, "MaxTotalRows, 计算散粒度时,当数据行数大于MaxTotalRows即开启数据库保护模式,不计算散粒度") maxTotalRows := flag.Uint64("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 超过该值时将给予警告")
spaghettiQueryLength := flag.Int("spaghetti-query-length", Config.SpaghettiQueryLength, "SpaghettiQueryLength, SQL最大长度警告,超过该长度会给警告") spaghettiQueryLength := flag.Int("spaghetti-query-length", Config.SpaghettiQueryLength, "SpaghettiQueryLength, SQL最大长度警告,超过该长度会给警告")
allowDropIdx := flag.Bool("allow-drop-index", Config.AllowDropIndex, "AllowDropIndex, 允许输出删除重复索引的建议") allowDropIdx := flag.Bool("allow-drop-index", Config.AllowDropIndex, "AllowDropIndex, 允许输出删除重复索引的建议")
......
[McLaughlin Hunter Harold]
[{
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
}]
[ binary binary utf8mb4_bin ]
...@@ -25,6 +25,8 @@ import ( ...@@ -25,6 +25,8 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"sort" "sort"
"github.com/tidwall/gjson"
) )
// GoldenDiff 从 gofmt 学来的测试方法 // GoldenDiff 从 gofmt 学来的测试方法
...@@ -104,3 +106,35 @@ func SortedKey(m interface{}) []string { ...@@ -104,3 +106,35 @@ func SortedKey(m interface{}) []string {
sort.Strings(keys) sort.Strings(keys)
return keys return keys
} }
// jsonFind internal function
func jsonFind(json string, name string, find *[]string) (next []string) {
res := gjson.Parse(json)
res.ForEach(func(key, value gjson.Result) bool {
if key.String() == name {
*find = append(*find, value.String())
} else {
switch value.Type {
case gjson.Number, gjson.True, gjson.False, gjson.Null:
default:
next = append(next, value.String())
}
}
return true // keep iterating
})
return next
}
// JSONFind iterate find name in json
func JSONFind(json string, name string) []string {
var find []string
next := []string{json}
for len(next) > 0 {
var tmpNext []string
for _, subJSON := range next {
tmpNext = append(tmpNext, jsonFind(subJSON, name, &find)...)
}
next = tmpNext
}
return find
}
...@@ -24,6 +24,7 @@ import ( ...@@ -24,6 +24,7 @@ import (
) )
func TestCaptureOutput(t *testing.T) { func TestCaptureOutput(t *testing.T) {
Log.Debug("Entering function: %s", GetFunctionName())
c1 := make(chan string, 1) c1 := make(chan string, 1)
// test output buf large than 65535 // test output buf large than 65535
length := 1<<16 + 1 length := 1<<16 + 1
...@@ -48,4 +49,343 @@ func TestCaptureOutput(t *testing.T) { ...@@ -48,4 +49,343 @@ func TestCaptureOutput(t *testing.T) {
case <-time.After(1 * time.Second): case <-time.After(1 * time.Second):
t.Error("capture timeout, pipe read hangup") t.Error("capture timeout, pipe read hangup")
} }
Log.Debug("Exiting function: %s", GetFunctionName())
}
func TestJSONFind(t *testing.T) {
Log.Debug("Entering function: %s", GetFunctionName())
jsons := []string{
`{
"programmers": [
{
"firstName": "Janet",
"Collate": "McLaughlin",
}, {
"firstName": "Elliotte",
"Collate": "Hunter",
}, {
"firstName": "Jason",
"Collate": "Harold",
}
]
}`,
`
{
"widget": {
"debug": "on",
"Collate": {
"title": "Sample Konfabulator Widget",
"name": "main_window",
"width": 500,
"height": 500
},
"image": {
"src": "Images/Sun.png",
"hOffset": 250,
"vOffset": 250,
"alignment": "center"
},
"text": {
"data": "Click Here",
"size": 36,
"style": "bold",
"vOffset": 100,
"alignment": "center",
"onMouseUp": "sun1.opacity = (sun1.opacity / 100) * 90;"
}
}
}
`,
`
[
{
"SQLCache": true,
"CalcFoundRows": false,
"StraightJoin": false,
"Priority": 0,
"Distinct": false,
"From": {
"TableRefs": {
"Left": {
"Source": {
"Schema": {
"O": "",
"L": ""
},
"Name": {
"O": "tb",
"L": "tb"
},
"DBInfo": null,
"TableInfo": null,
"IndexHints": null
},
"AsName": {
"O": "",
"L": ""
}
},
"Right": null,
"Tp": 0,
"On": null,
"Using": null,
"NaturalJoin": false,
"StraightJoin": false
}
},
"Where": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Op": 4,
"L": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Op": 7,
"L": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Name": {
"Schema": {
"O": "",
"L": ""
},
"Table": {
"O": "",
"L": ""
},
"Name": {
"O": "col3",
"L": "col3"
}
},
"Refer": null
},
"R": {
"Type": {
"Tp": 8,
"Flag": 128,
"Flen": 1,
"Decimal": 0,
"Charset": "binary",
"Collate": "binary",
"Elems": null
}
}
},
"R": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Op": 1,
"L": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Op": 7,
"L": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Name": {
"Schema": {
"O": "",
"L": ""
},
"Table": {
"O": "",
"L": ""
},
"Name": {
"O": "col3",
"L": "col3"
}
},
"Refer": null
},
"R": {
"Type": {
"Tp": 8,
"Flag": 128,
"Flen": 1,
"Decimal": 0,
"Charset": "binary",
"Collate": "binary",
"Elems": null
}
}
},
"R": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Op": 7,
"L": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Op": 7,
"L": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Name": {
"Schema": {
"O": "",
"L": ""
},
"Table": {
"O": "",
"L": ""
},
"Name": {
"O": "col1",
"L": "col1"
}
},
"Refer": null
},
"R": {
"Type": {
"Tp": 0,
"Flag": 0,
"Flen": 0,
"Decimal": 0,
"Charset": "",
"Collate": "",
"Elems": null
},
"Name": {
"Schema": {
"O": "",
"L": ""
},
"Table": {
"O": "",
"L": ""
},
"Name": {
"O": "col2",
"L": "col2"
}
},
"Refer": null
}
},
"R": {
"Type": {
"Tp": 253,
"Flag": 0,
"Flen": 3,
"Decimal": -1,
"Charset": "utf8mb4",
"Collate": "utf8mb4_bin",
"Elems": null
}
}
}
}
},
"Fields": {
"Fields": [
{
"Offset": 7,
"WildCard": {
"Table": {
"O": "",
"L": ""
},
"Schema": {
"O": "",
"L": ""
}
},
"Expr": null,
"AsName": {
"O": "",
"L": ""
},
"Auxiliary": false
}
]
},
"GroupBy": null,
"Having": null,
"WindowSpecs": null,
"OrderBy": null,
"Limit": null,
"LockTp": 0,
"TableHints": null,
"IsAfterUnionDistinct": false,
"IsInBraces": false
}
]
`,
}
err := GoldenDiff(func() {
for _, json := range jsons {
result := JSONFind(json, "Collate")
fmt.Println(result)
}
}, t.Name(), update)
if err != nil {
t.Error(err)
}
Log.Debug("Exiting function: %s", GetFunctionName())
} }
...@@ -26,7 +26,7 @@ import ( ...@@ -26,7 +26,7 @@ import (
"vitess.io/vitess/go/vt/sqlparser" "vitess.io/vitess/go/vt/sqlparser"
) )
// Profiling show profile输出的结果 // Profiling show profile 输出的结果
type Profiling struct { type Profiling struct {
Rows []ProfilingRow Rows []ProfilingRow
} }
...@@ -35,7 +35,7 @@ type Profiling struct { ...@@ -35,7 +35,7 @@ type Profiling struct {
type ProfilingRow struct { type ProfilingRow struct {
Status string Status string
Duration float64 Duration float64
// TODO: 支持show profile all,不过目前看all的信息过多有点眼花缭乱 // TODO: 支持show profile all, 不过目前看所有的信息过多有点眼花缭乱
} }
// Profiling 执行SQL,并对其 Profile // Profiling 执行SQL,并对其 Profile
...@@ -48,14 +48,14 @@ func (db *Connector) Profiling(sql string, params ...interface{}) ([]ProfilingRo ...@@ -48,14 +48,14 @@ func (db *Connector) Profiling(sql string, params ...interface{}) ([]ProfilingRo
return rows, errors.New("no need profiling") return rows, errors.New("no need profiling")
} }
// 测试环境如果检查是关闭的,则SQL不会被执行 // 测试环境如果检查是关闭的,则 SQL 不会被执行
if common.Config.TestDSN.Disable { if common.Config.TestDSN.Disable {
return rows, errors.New("dsn is disable") return rows, errors.New("dsn is 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) {
return rows, fmt.Errorf("query execution deny: Execute SQL with DSN(%s/%s) '%s'", return rows, fmt.Errorf("query execution deny: Execute SQL with DSN(%s/%s) '%s'",
db.Addr, db.Database, fmt.Sprintf(sql, params...)) db.Addr, db.Database, fmt.Sprintf(sql, params...))
...@@ -114,7 +114,7 @@ func (db *Connector) Profiling(sql string, params ...interface{}) ([]ProfilingRo ...@@ -114,7 +114,7 @@ func (db *Connector) Profiling(sql string, params ...interface{}) ([]ProfilingRo
return rows, err return rows, err
} }
// FormatProfiling 格式化输出Profiling信息 // FormatProfiling 格式化输出 Profiling 信息
func FormatProfiling(rows []ProfilingRow) string { func FormatProfiling(rows []ProfilingRow) string {
str := []string{"| Status | Duration |"} str := []string{"| Status | Duration |"}
str = append(str, "| --- | --- |") str = append(str, "| --- | --- |")
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"testing" "testing"
"github.com/XiaoMi/soar/common" "github.com/XiaoMi/soar/common"
"github.com/kr/pretty" "github.com/kr/pretty"
) )
......
...@@ -41,25 +41,25 @@ type tableStatusRow struct { ...@@ -41,25 +41,25 @@ type tableStatusRow struct {
Engine []byte // 该表使用的存储引擎 Engine []byte // 该表使用的存储引擎
Version []byte // 该表的 .frm 文件版本号 Version []byte // 该表的 .frm 文件版本号
RowFormat []byte // 该表使用的行存储格式 RowFormat []byte // 该表使用的行存储格式
Rows int64 // 表行数, InnoDB 引擎中为预估值,甚至可能会有40%~50%的数值偏差 Rows uint64 // 表行数, InnoDB 引擎中为预估值,甚至可能会有40%~50%的数值偏差
AvgRowLength int // 平均行长度 AvgRowLength uint64 // 平均行长度
// MyISAM: Data_length 为数据文件的大小,单位为 bytes // MyISAM: Data_length 为数据文件的大小,单位为 bytes
// InnoDB: Data_length 为聚簇索引分配的近似内存量,单位为 bytes, 计算方式为聚簇索引数量乘以 InnoDB 页面大小 // InnoDB: Data_length 为聚簇索引分配的近似内存量,单位为 bytes, 计算方式为聚簇索引数量乘以 InnoDB 页面大小
// 其他不同的存储引擎中该值的意义可能不尽相同 // 其他不同的存储引擎中该值的意义可能不尽相同
DataLength int DataLength uint64
// MyISAM: Max_data_length 为数据文件长度的最大值。这是在给定使用的数据指针大小的情况下,可以存储在表中的数据的最大字节数 // MyISAM: Max_data_length 为数据文件长度的最大值。这是在给定使用的数据指针大小的情况下,可以存储在表中的数据的最大字节数
// InnoDB: 未使用 // InnoDB: 未使用
// 其他不同的存储引擎中该值的意义可能不尽相同 // 其他不同的存储引擎中该值的意义可能不尽相同
MaxDataLength int MaxDataLength uint64
// MyISAM: Index_length 为 index 文件的大小,单位为 bytes // MyISAM: Index_length 为 index 文件的大小,单位为 bytes
// InnoDB: Index_length 为非聚簇索引分配的近似内存量,单位为 bytes,计算方式为非聚簇索引数量乘以 InnoDB 页面大小 // InnoDB: Index_length 为非聚簇索引分配的近似内存量,单位为 bytes,计算方式为非聚簇索引数量乘以 InnoDB 页面大小
// 其他不同的存储引擎中该值的意义可能不尽相同 // 其他不同的存储引擎中该值的意义可能不尽相同
IndexLength int IndexLength uint64
DataFree int // 已分配但未使用的字节数 DataFree uint64 // 已分配但未使用的字节数
AutoIncrement []byte // 下一个自增值 AutoIncrement []byte // 下一个自增值
CreateTime []byte // 创建时间 CreateTime []byte // 创建时间
UpdateTime []byte // 最近一次更新时间,该值不准确 UpdateTime []byte // 最近一次更新时间,该值不准确
......
...@@ -90,7 +90,7 @@ func BuildEnv() (*VirtualEnv, *database.Connector) { ...@@ -90,7 +90,7 @@ func BuildEnv() (*VirtualEnv, *database.Connector) {
common.Config.TestDSN.Disable = true common.Config.TestDSN.Disable = true
} }
// 检查是否允许Online和Test一致,防止误操作 // 检查是否允许 Online 和 Test 一致,防止误操作
if common.FormatDSN(common.Config.OnlineDSN) == common.FormatDSN(common.Config.TestDSN) && if common.FormatDSN(common.Config.OnlineDSN) == common.FormatDSN(common.Config.TestDSN) &&
!common.Config.AllowOnlineAsTest { !common.Config.AllowOnlineAsTest {
common.Log.Warn("BuildEnv AllowOnlineAsTest: %s:********@%s/%s OnlineDSN can't config as TestDSN", common.Log.Warn("BuildEnv AllowOnlineAsTest: %s:********@%s/%s OnlineDSN can't config as TestDSN",
...@@ -108,7 +108,7 @@ func BuildEnv() (*VirtualEnv, *database.Connector) { ...@@ -108,7 +108,7 @@ func BuildEnv() (*VirtualEnv, *database.Connector) {
return vEnv, connOnline return vEnv, connOnline
} }
// RealDB 从测试环境中获取通过hash后的DB // RealDB 从测试环境中获取通过 hash 后的 DB
func (vEnv *VirtualEnv) RealDB(hash string) string { func (vEnv *VirtualEnv) RealDB(hash string) string {
if _, ok := vEnv.Hash2DB[hash]; ok { if _, ok := vEnv.Hash2DB[hash]; ok {
return vEnv.Hash2DB[hash] return vEnv.Hash2DB[hash]
...@@ -120,7 +120,7 @@ func (vEnv *VirtualEnv) RealDB(hash string) string { ...@@ -120,7 +120,7 @@ func (vEnv *VirtualEnv) RealDB(hash string) string {
return hash return hash
} }
// DBHash 从测试环境中根据DB找到对应的hash // DBHash 从测试环境中根据 DB 找到对应的 hash
func (vEnv *VirtualEnv) DBHash(db string) string { func (vEnv *VirtualEnv) DBHash(db string) string {
if _, ok := vEnv.DBRef[db]; ok { if _, ok := vEnv.DBRef[db]; ok {
return vEnv.DBRef[db] return vEnv.DBRef[db]
...@@ -194,15 +194,15 @@ func (vEnv *VirtualEnv) CleanupTestDatabase() { ...@@ -194,15 +194,15 @@ func (vEnv *VirtualEnv) CleanupTestDatabase() {
common.Log.Debug("CleanupTestDatabase done") common.Log.Debug("CleanupTestDatabase done")
} }
// BuildVirtualEnv rEnv为SQL源环境,DB使用的信息从接口获取 // BuildVirtualEnv rEnv 为 SQL 源环境,DB 使用的信息从接口获取
// 注意:如果是USE,DDL等语句,执行完第一条就会返回,后面的SQL不会执行 // 注意:如果是 USE, DDL 等语句,执行完第一条就会返回,后面的 SQL 不会执行
func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string) bool { func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string) bool {
var stmt sqlparser.Statement var stmt sqlparser.Statement
var err error var err error
// 置空错误信息 // 置空错误信息
vEnv.Error = nil vEnv.Error = nil
// 检测是否已经创建初始数据库,如果未创建则创建一个名称hash过的映射数据库 // 检测是否已经创建初始数据库,如果未创建则创建一个名称 hash 过的映射数据库
err = vEnv.createDatabase(rEnv) err = vEnv.createDatabase(rEnv)
common.LogIfWarn(err, "") common.LogIfWarn(err, "")
...@@ -212,7 +212,7 @@ func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string ...@@ -212,7 +212,7 @@ func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string
return true return true
} }
// 判断rEnv中是否指定了DB // 判断 rEnv 中是否指定了 DB
if rEnv.Database == "" { if rEnv.Database == "" {
common.Log.Error("BuildVirtualEnv no database specified, TestDSN init failed") common.Log.Error("BuildVirtualEnv no database specified, TestDSN init failed")
return false return false
...@@ -221,9 +221,7 @@ func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string ...@@ -221,9 +221,7 @@ func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string
// 库表提取 // 库表提取
meta := make(map[string]*common.DB) meta := make(map[string]*common.DB)
for _, sql := range SQLs { for _, sql := range SQLs {
common.Log.Debug("BuildVirtualEnv Database&TableName Mapping, SQL: %s", sql) common.Log.Debug("BuildVirtualEnv Database&TableName Mapping, SQL: %s", sql)
stmt, err = sqlparser.Parse(sql) stmt, err = sqlparser.Parse(sql)
if err != nil { if err != nil {
common.Log.Error("BuildVirtualEnv Error : %v", err) common.Log.Error("BuildVirtualEnv Error : %v", err)
...@@ -249,7 +247,7 @@ func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string ...@@ -249,7 +247,7 @@ func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string
// 为不影响其他SQL操作,复制一个Connector对象,将数据库切换到对应的DB上直接执行 // 为不影响其他SQL操作,复制一个Connector对象,将数据库切换到对应的DB上直接执行
vEnv.Database = vEnv.DBRef[rEnv.Database] vEnv.Database = vEnv.DBRef[rEnv.Database]
// 为了支持并发,需要将DB进行映射,但db.table这种形式无法保证DB的映射是正确的 // 为了支持并发,需要将DB进行映射,但 db.table 这种形式无法保证 DB 的映射是正确的
// TODO:暂不支持 create db.tableName (id int) 形式的建表语句 // TODO:暂不支持 create db.tableName (id int) 形式的建表语句
if stmt.Table.Qualifier.String() != "" { if stmt.Table.Qualifier.String() != "" {
common.Log.Error("BuildVirtualEnv DDL Not support db.tb format") common.Log.Error("BuildVirtualEnv DDL Not support db.tb format")
...@@ -300,7 +298,7 @@ func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string ...@@ -300,7 +298,7 @@ func (vEnv *VirtualEnv) BuildVirtualEnv(rEnv *database.Connector, SQLs ...string
meta := ast.GetMeta(stmt, nil) meta := ast.GetMeta(stmt, nil)
// 由于DB环境可能是变的,所以需要每一次都单独的提取库表结构,整体随着rEnv的变动而发生变化 // 由于 DB 环境可能是变的,所以需要每一次都单独的提取库表结构,整体随着 rEnv 的变动而发生变化
for db, table := range meta { for db, table := range meta {
if db == "" { if db == "" {
db = rEnv.Database db = rEnv.Database
...@@ -361,7 +359,7 @@ func (vEnv *VirtualEnv) createDatabase(rEnv *database.Connector) error { ...@@ -361,7 +359,7 @@ func (vEnv *VirtualEnv) createDatabase(rEnv *database.Connector) error {
// optimizer_YYMMDDHHmmss_xxxx // optimizer_YYMMDDHHmmss_xxxx
dbHash := fmt.Sprintf("optimizer_%s_%s", // Total 39 bytes dbHash := fmt.Sprintf("optimizer_%s_%s", // Total 39 bytes
time.Now().Format("060102150405"), // 12 Bytes 20180102030405 time.Now().Format("060102150405"), // 12 Bytes 180102030405
strings.ToLower(uniuri.New())) // 16 Bytes random string strings.ToLower(uniuri.New())) // 16 Bytes random string
common.Log.Debug("createDatabase, mapping `%s` :`%s`-->`%s`", rEnv.Database, rEnv.Database, dbHash) common.Log.Debug("createDatabase, mapping `%s` :`%s`-->`%s`", rEnv.Database, rEnv.Database, dbHash)
ddl, err := rEnv.ShowCreateDatabase(rEnv.Database) ddl, err := rEnv.ShowCreateDatabase(rEnv.Database)
...@@ -496,7 +494,7 @@ func (vEnv *VirtualEnv) GenTableColumns(meta common.Meta) common.TableColumns { ...@@ -496,7 +494,7 @@ func (vEnv *VirtualEnv) GenTableColumns(meta common.Meta) common.TableColumns {
} }
if len(tb.Column) == 0 { if len(tb.Column) == 0 {
// tb.column为空说明SQL里这个表是用的*来查询 // tb.column 为空说明 SQL 里这个表是用的*来查询
if err != nil { if err != nil {
common.Log.Error("ast.Rewrite ShowColumns, Error: %v", err) common.Log.Error("ast.Rewrite ShowColumns, Error: %v", err)
break break
......
#!/usr/bin/env bats
load test_helper
@test "Simple Query Optimizer" {
${SOAR_BIN_ENV} -query "select * from film where length > 120" | grep -v "散粒度" > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
@test "Run all test cases" {
${SOAR_BIN} -list-test-sqls | ${SOAR_BIN_ENV} | grep -v "散粒度" > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
online-dsn:
user: ""
password: '********'
net: tcp
addr: 127.0.0.1:3306
schema: information_schema
charset: utf8
collation: utf8_general_ci
loc: UTC
tls: ""
server-public-key: ""
maxallowedpacket: 4194304
params:
charset: utf8
timeout: 0
read-timeout: 0
write-timeout: 0
allow-native-passwords: true
allow-old-passwords: false
disable: false
test-dsn:
user: ""
password: '********'
net: tcp
addr: 127.0.0.1:3306
schema: information_schema
charset: utf8
collation: utf8_general_ci
loc: UTC
tls: ""
server-public-key: ""
maxallowedpacket: 4194304
params:
charset: utf8
timeout: 0
read-timeout: 0
write-timeout: 0
allow-native-passwords: true
allow-old-passwords: false
disable: false
allow-online-as-test: false
drop-test-temporary: true
cleanup-test-database: false
only-syntax-check: false
sampling-statistic-target: 100
sampling: false
sampling-condition: ""
profiling: false
trace: false
explain: true
delimiter: ;
log-level: 3
log-output: /dev/null
report-type: markdown
report-css: ""
report-javascript: ""
report-title: SQL优化分析报告
markdown-extensions: 94
markdown-html-flags: 0
ignore-rules:
- COL.011
rewrite-rules:
- delimiter
- orderbynull
- groupbyconst
- dmlorderby
- having
- star2columns
- insertcolumns
- distinctstar
blacklist: ""
max-join-table-count: 5
max-group-by-cols-count: 5
max-distinct-count: 5
max-index-cols-count: 5
max-text-cols-count: 2
max-total-rows: 9999999
max-query-cost: 9999
spaghetti-query-length: 2048
allow-drop-index: false
max-in-count: 10
max-index-bytes-percolumn: 767
max-index-bytes: 3072
allow-charsets:
- utf8
- utf8mb4
allow-collates: []
allow-engines:
- innodb
max-index-count: 10
max-column-count: 40
max-value-count: 100
index-prefix: idx_
unique-key-prefix: uk_
max-subquery-depth: 5
max-varchar-length: 1024
column-not-allow-type:
- boolean
min-cardinality: 0
explain-sql-report-type: pretty
explain-type: extended
explain-format: traditional
explain-warn-select-type:
- ""
explain-warn-access-type:
- ALL
explain-max-keys: 3
explain-min-keys: 0
explain-max-rows: 10000
explain-warn-extra:
- Using temporary
- Using filesort
explain-max-filtered: 100
explain-warn-scalability:
- O(n)
show-warnings: false
show-last-query-cost: false
query: ""
list-heuristic-rules: false
list-rewrite-rules: false
list-test-sqls: false
list-report-types: false
verbose: false
dry-run: true
max-pretty-sql-length: 1024
...@@ -2,19 +2,41 @@ ...@@ -2,19 +2,41 @@
load test_helper load test_helper
@test "Simple Query Optimizer" { @test "Test soar version" {
${SOAR_BIN_ENV} -query "select * from film where length > 120" | grep -v "散粒度" > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden run ${SOAR_BIN} -version
run golden_diff ${BATS_TEST_NAME} [ "$status" -eq 0 ]
[ $status -eq 0 ] [ "${lines[0]%% *}" == "Version:" ]
[ "${lines[1]%% *}" == "Branch:" ]
[ "${lines[2]%% *}" == "Compile:" ]
[ $(expr "${lines[2]}" : "Compile: $(date +'%Y-%m-%d').*") -ne 0 ]
} }
@test "Syntax Check" { @test "No arguments prints message" {
run ${SOAR_BIN} -query "select * frm film" -only-syntax-check run ${SOAR_BIN}
[ $status -eq 1 ] [ $status -eq 1 ]
[ "${lines[0]}" == 'Args format error, use --help see how to use it!' ]
}
@test "Run default printconfig cases" {
${SOAR_BIN} -print-config -log-output=/dev/null > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
@test "Check config cases" {
run ${SOAR_BIN_ENV} -check-config
[ $status -eq 0 ]
[ -z ${output} ]
} }
@test "Run all test cases" { @test "Syntax Check OK" {
${SOAR_BIN} -list-test-sqls | ${SOAR_BIN_ENV} | grep -v "散粒度" > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden run ${SOAR_BIN} -query "select * from film" -only-syntax-check
run golden_diff ${BATS_TEST_NAME}
[ $status -eq 0 ] [ $status -eq 0 ]
[ -z $ouput ]
}
@test "Syntax Check Error" {
run ${SOAR_BIN} -query "select * frm film" -only-syntax-check
[ $status -eq 1 ]
[ -n $ouput ]
} }
#!/usr/bin/env bats
load test_helper
#!/usr/bin/env bats
load test_helper
@test "Check Query Optimizer" {
run ${SOAR_BIN} -query "select * from film where length > 120"
[ $status -eq 0 ]
}
...@@ -7,8 +7,7 @@ setup() { ...@@ -7,8 +7,7 @@ setup() {
mkdir -p "${BATS_TMP_DIRNAME}" mkdir -p "${BATS_TMP_DIRNAME}"
} }
# golden_diff like gofmt golden file check method, use this function check output different with template
golden_diff() { golden_diff() {
FUNC_NAME=$1 diff "${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden" "${BATS_FIXTURE_DIRNAME}/${BATS_TEST_NAME}.golden" >/dev/null
diff "${BATS_TMP_DIRNAME}/${FUNC_NAME}.golden" "${BATS_FIXTURE_DIRNAME}/${FUNC_NAME}.golden" >/dev/null
return $?
} }
此差异已折叠。
此差异已折叠。
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json2
import "bytes"
// Compact appends to dst the JSON-encoded src with
// insignificant space characters elided.
func Compact(dst *bytes.Buffer, src []byte) error {
return compact(dst, src, false)
}
func compact(dst *bytes.Buffer, src []byte, escape bool) error {
origLen := dst.Len()
var scan scanner
scan.reset()
start := 0
for i, c := range src {
if escape && (c == '<' || c == '>' || c == '&') {
if start < i {
dst.Write(src[start:i])
}
dst.WriteString(`\u00`)
dst.WriteByte(hex[c>>4])
dst.WriteByte(hex[c&0xF])
start = i + 1
}
// Convert U+2028 and U+2029 (E2 80 A8 and E2 80 A9).
if c == 0xE2 && i+2 < len(src) && src[i+1] == 0x80 && src[i+2]&^1 == 0xA8 {
if start < i {
dst.Write(src[start:i])
}
dst.WriteString(`\u202`)
dst.WriteByte(hex[src[i+2]&0xF])
start = i + 3
}
v := scan.step(&scan, int(c))
if v >= scanSkipSpace {
if v == scanError {
break
}
if start < i {
dst.Write(src[start:i])
}
start = i + 1
}
}
if scan.eof() == scanError {
dst.Truncate(origLen)
return scan.err
}
if start < len(src) {
dst.Write(src[start:])
}
return nil
}
func newline(dst *bytes.Buffer, prefix, indent string, depth int) {
dst.WriteByte('\n')
dst.WriteString(prefix)
for i := 0; i < depth; i++ {
dst.WriteString(indent)
}
}
// Indent appends to dst an indented form of the JSON-encoded src.
// Each element in a JSON object or array begins on a new,
// indented line beginning with prefix followed by one or more
// copies of indent according to the indentation nesting.
// The data appended to dst has no trailing newline, to make it easier
// to embed inside other formatted JSON data.
func Indent(dst *bytes.Buffer, src []byte, prefix, indent string) error {
origLen := dst.Len()
var scan scanner
scan.reset()
needIndent := false
depth := 0
for _, c := range src {
scan.bytes++
v := scan.step(&scan, int(c))
if v == scanSkipSpace {
continue
}
if v == scanError {
break
}
if needIndent && v != scanEndObject && v != scanEndArray {
needIndent = false
depth++
newline(dst, prefix, indent, depth)
}
// Emit semantically uninteresting bytes
// (in particular, punctuation in strings) unmodified.
if v == scanContinue {
dst.WriteByte(c)
continue
}
// Add spacing around real punctuation.
switch c {
case '{', '[':
// delay indent so that empty object and array are formatted as {} and [].
needIndent = true
dst.WriteByte(c)
case ',':
dst.WriteByte(c)
newline(dst, prefix, indent, depth)
case ':':
dst.WriteByte(c)
dst.WriteByte(' ')
case '}', ']':
if needIndent {
// suppress indent in empty object/array
needIndent = false
} else {
depth--
newline(dst, prefix, indent, depth)
}
dst.WriteByte(c)
default:
dst.WriteByte(c)
}
}
if scan.eof() == scanError {
dst.Truncate(origLen)
return scan.err
}
return nil
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json2
// JSON value parser state machine.
// Just about at the limit of what is reasonable to write by hand.
// Some parts are a bit tedious, but overall it nicely factors out the
// otherwise common code from the multiple scanning functions
// in this package (Compact, Indent, checkValid, nextValue, etc).
//
// This file starts with two simple examples using the scanner
// before diving into the scanner itself.
import "strconv"
// checkValid verifies that data is valid JSON-encoded data.
// scan is passed in for use by checkValid to avoid an allocation.
func checkValid(data []byte, scan *scanner) error {
scan.reset()
for _, c := range data {
scan.bytes++
if scan.step(scan, int(c)) == scanError {
return scan.err
}
}
if scan.eof() == scanError {
return scan.err
}
return nil
}
// nextValue splits data after the next whole JSON value,
// returning that value and the bytes that follow it as separate slices.
// scan is passed in for use by nextValue to avoid an allocation.
func nextValue(data []byte, scan *scanner) (value, rest []byte, err error) {
scan.reset()
for i, c := range data {
v := scan.step(scan, int(c))
if v >= scanEnd {
switch v {
case scanError:
return nil, nil, scan.err
case scanEnd:
return data[0:i], data[i:], nil
}
}
}
if scan.eof() == scanError {
return nil, nil, scan.err
}
return data, nil, nil
}
// A SyntaxError is a description of a JSON syntax error.
type SyntaxError struct {
msg string // description of error
Offset int64 // error occurred after reading Offset bytes
}
func (e *SyntaxError) Error() string { return e.msg }
// A scanner is a JSON scanning state machine.
// Callers call scan.reset() and then pass bytes in one at a time
// by calling scan.step(&scan, c) for each byte.
// The return value, referred to as an opcode, tells the
// caller about significant parsing events like beginning
// and ending literals, objects, and arrays, so that the
// caller can follow along if it wishes.
// The return value scanEnd indicates that a single top-level
// JSON value has been completed, *before* the byte that
// just got passed in. (The indication must be delayed in order
// to recognize the end of numbers: is 123 a whole value or
// the beginning of 12345e+6?).
type scanner struct {
// The step is a func to be called to execute the next transition.
// Also tried using an integer constant and a single func
// with a switch, but using the func directly was 10% faster
// on a 64-bit Mac Mini, and it's nicer to read.
step func(*scanner, int) int
// Reached end of top-level value.
endTop bool
// Stack of what we're in the middle of - array values, object keys, object values.
parseState []int
// Error that happened, if any.
err error
// 1-byte redo (see undo method)
redo bool
redoCode int
redoState func(*scanner, int) int
// total bytes consumed, updated by decoder.Decode
bytes int64
}
// These values are returned by the state transition functions
// assigned to scanner.state and the method scanner.eof.
// They give details about the current state of the scan that
// callers might be interested to know about.
// It is okay to ignore the return value of any particular
// call to scanner.state: if one call returns scanError,
// every subsequent call will return scanError too.
const (
// Continue.
scanContinue = iota // uninteresting byte
scanBeginLiteral // end implied by next result != scanContinue
scanBeginObject // begin object
scanObjectKey // just finished object key (string)
scanObjectValue // just finished non-last object value
scanEndObject // end object (implies scanObjectValue if possible)
scanBeginArray // begin array
scanArrayValue // just finished array value
scanEndArray // end array (implies scanArrayValue if possible)
scanSkipSpace // space byte; can skip; known to be last "continue" result
// Stop.
scanEnd // top-level value ended *before* this byte; known to be first "stop" result
scanError // hit an error, scanner.err.
)
// These values are stored in the parseState stack.
// They give the current state of a composite value
// being scanned. If the parser is inside a nested value
// the parseState describes the nested state, outermost at entry 0.
const (
parseObjectKey = iota // parsing object key (before colon)
parseObjectValue // parsing object value (after colon)
parseArrayValue // parsing array value
)
// reset prepares the scanner for use.
// It must be called before calling s.step.
func (s *scanner) reset() {
s.step = stateBeginValue
s.parseState = s.parseState[0:0]
s.err = nil
s.redo = false
s.endTop = false
}
// eof tells the scanner that the end of input has been reached.
// It returns a scan status just as s.step does.
func (s *scanner) eof() int {
if s.err != nil {
return scanError
}
if s.endTop {
return scanEnd
}
s.step(s, ' ')
if s.endTop {
return scanEnd
}
if s.err == nil {
s.err = &SyntaxError{"unexpected end of JSON input", s.bytes}
}
return scanError
}
// pushParseState pushes a new parse state p onto the parse stack.
func (s *scanner) pushParseState(p int) {
s.parseState = append(s.parseState, p)
}
// popParseState pops a parse state (already obtained) off the stack
// and updates s.step accordingly.
func (s *scanner) popParseState() {
n := len(s.parseState) - 1
s.parseState = s.parseState[0:n]
s.redo = false
if n == 0 {
s.step = stateEndTop
s.endTop = true
} else {
s.step = stateEndValue
}
}
func isSpace(c rune) bool {
return c == ' ' || c == '\t' || c == '\r' || c == '\n'
}
// stateBeginValueOrEmpty is the state after reading `[`.
func stateBeginValueOrEmpty(s *scanner, c int) int {
if c <= ' ' && isSpace(rune(c)) {
return scanSkipSpace
}
if c == ']' {
return stateEndValue(s, c)
}
return stateBeginValue(s, c)
}
// stateBeginValue is the state at the beginning of the input.
func stateBeginValue(s *scanner, c int) int {
if c <= ' ' && isSpace(rune(c)) {
return scanSkipSpace
}
switch c {
case '{':
s.step = stateBeginStringOrEmpty
s.pushParseState(parseObjectKey)
return scanBeginObject
case '[':
s.step = stateBeginValueOrEmpty
s.pushParseState(parseArrayValue)
return scanBeginArray
case '"':
s.step = stateInString
return scanBeginLiteral
case '-':
s.step = stateNeg
return scanBeginLiteral
case '0': // beginning of 0.123
s.step = state0
return scanBeginLiteral
case 't': // beginning of true
s.step = stateT
return scanBeginLiteral
case 'f': // beginning of false
s.step = stateF
return scanBeginLiteral
case 'n': // beginning of null
s.step = stateN
return scanBeginLiteral
}
if '1' <= c && c <= '9' { // beginning of 1234.5
s.step = state1
return scanBeginLiteral
}
return s.error(c, "looking for beginning of value")
}
// stateBeginStringOrEmpty is the state after reading `{`.
func stateBeginStringOrEmpty(s *scanner, c int) int {
if c <= ' ' && isSpace(rune(c)) {
return scanSkipSpace
}
if c == '}' {
n := len(s.parseState)
s.parseState[n-1] = parseObjectValue
return stateEndValue(s, c)
}
return stateBeginString(s, c)
}
// stateBeginString is the state after reading `{"key": value,`.
func stateBeginString(s *scanner, c int) int {
if c <= ' ' && isSpace(rune(c)) {
return scanSkipSpace
}
if c == '"' {
s.step = stateInString
return scanBeginLiteral
}
return s.error(c, "looking for beginning of object key string")
}
// stateEndValue is the state after completing a value,
// such as after reading `{}` or `true` or `["x"`.
func stateEndValue(s *scanner, c int) int {
n := len(s.parseState)
if n == 0 {
// Completed top-level before the current byte.
s.step = stateEndTop
s.endTop = true
return stateEndTop(s, c)
}
if c <= ' ' && isSpace(rune(c)) {
s.step = stateEndValue
return scanSkipSpace
}
ps := s.parseState[n-1]
switch ps {
case parseObjectKey:
if c == ':' {
s.parseState[n-1] = parseObjectValue
s.step = stateBeginValue
return scanObjectKey
}
return s.error(c, "after object key")
case parseObjectValue:
if c == ',' {
s.parseState[n-1] = parseObjectKey
s.step = stateBeginString
return scanObjectValue
}
if c == '}' {
s.popParseState()
return scanEndObject
}
return s.error(c, "after object key:value pair")
case parseArrayValue:
if c == ',' {
s.step = stateBeginValue
return scanArrayValue
}
if c == ']' {
s.popParseState()
return scanEndArray
}
return s.error(c, "after array element")
}
return s.error(c, "")
}
// stateEndTop is the state after finishing the top-level value,
// such as after reading `{}` or `[1,2,3]`.
// Only space characters should be seen now.
func stateEndTop(s *scanner, c int) int {
if c != ' ' && c != '\t' && c != '\r' && c != '\n' {
// Complain about non-space byte on next call.
s.error(c, "after top-level value")
}
return scanEnd
}
// stateInString is the state after reading `"`.
func stateInString(s *scanner, c int) int {
if c == '"' {
s.step = stateEndValue
return scanContinue
}
if c == '\\' {
s.step = stateInStringEsc
return scanContinue
}
if c < 0x20 {
return s.error(c, "in string literal")
}
return scanContinue
}
// stateInStringEsc is the state after reading `"\` during a quoted string.
func stateInStringEsc(s *scanner, c int) int {
switch c {
case 'b', 'f', 'n', 'r', 't', '\\', '/', '"':
s.step = stateInString
return scanContinue
}
if c == 'u' {
s.step = stateInStringEscU
return scanContinue
}
return s.error(c, "in string escape code")
}
// stateInStringEscU is the state after reading `"\u` during a quoted string.
func stateInStringEscU(s *scanner, c int) int {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU1
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU1 is the state after reading `"\u1` during a quoted string.
func stateInStringEscU1(s *scanner, c int) int {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU12
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU12 is the state after reading `"\u12` during a quoted string.
func stateInStringEscU12(s *scanner, c int) int {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInStringEscU123
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateInStringEscU123 is the state after reading `"\u123` during a quoted string.
func stateInStringEscU123(s *scanner, c int) int {
if '0' <= c && c <= '9' || 'a' <= c && c <= 'f' || 'A' <= c && c <= 'F' {
s.step = stateInString
return scanContinue
}
// numbers
return s.error(c, "in \\u hexadecimal character escape")
}
// stateNeg is the state after reading `-` during a number.
func stateNeg(s *scanner, c int) int {
if c == '0' {
s.step = state0
return scanContinue
}
if '1' <= c && c <= '9' {
s.step = state1
return scanContinue
}
return s.error(c, "in numeric literal")
}
// state1 is the state after reading a non-zero integer during a number,
// such as after reading `1` or `100` but not `0`.
func state1(s *scanner, c int) int {
if '0' <= c && c <= '9' {
s.step = state1
return scanContinue
}
return state0(s, c)
}
// state0 is the state after reading `0` during a number.
func state0(s *scanner, c int) int {
if c == '.' {
s.step = stateDot
return scanContinue
}
if c == 'e' || c == 'E' {
s.step = stateE
return scanContinue
}
return stateEndValue(s, c)
}
// stateDot is the state after reading the integer and decimal point in a number,
// such as after reading `1.`.
func stateDot(s *scanner, c int) int {
if '0' <= c && c <= '9' {
s.step = stateDot0
return scanContinue
}
return s.error(c, "after decimal point in numeric literal")
}
// stateDot0 is the state after reading the integer, decimal point, and subsequent
// digits of a number, such as after reading `3.14`.
func stateDot0(s *scanner, c int) int {
if '0' <= c && c <= '9' {
s.step = stateDot0
return scanContinue
}
if c == 'e' || c == 'E' {
s.step = stateE
return scanContinue
}
return stateEndValue(s, c)
}
// stateE is the state after reading the mantissa and e in a number,
// such as after reading `314e` or `0.314e`.
func stateE(s *scanner, c int) int {
if c == '+' {
s.step = stateESign
return scanContinue
}
if c == '-' {
s.step = stateESign
return scanContinue
}
return stateESign(s, c)
}
// stateESign is the state after reading the mantissa, e, and sign in a number,
// such as after reading `314e-` or `0.314e+`.
func stateESign(s *scanner, c int) int {
if '0' <= c && c <= '9' {
s.step = stateE0
return scanContinue
}
return s.error(c, "in exponent of numeric literal")
}
// stateE0 is the state after reading the mantissa, e, optional sign,
// and at least one digit of the exponent in a number,
// such as after reading `314e-2` or `0.314e+1` or `3.14e0`.
func stateE0(s *scanner, c int) int {
if '0' <= c && c <= '9' {
s.step = stateE0
return scanContinue
}
return stateEndValue(s, c)
}
// stateT is the state after reading `t`.
func stateT(s *scanner, c int) int {
if c == 'r' {
s.step = stateTr
return scanContinue
}
return s.error(c, "in literal true (expecting 'r')")
}
// stateTr is the state after reading `tr`.
func stateTr(s *scanner, c int) int {
if c == 'u' {
s.step = stateTru
return scanContinue
}
return s.error(c, "in literal true (expecting 'u')")
}
// stateTru is the state after reading `tru`.
func stateTru(s *scanner, c int) int {
if c == 'e' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal true (expecting 'e')")
}
// stateF is the state after reading `f`.
func stateF(s *scanner, c int) int {
if c == 'a' {
s.step = stateFa
return scanContinue
}
return s.error(c, "in literal false (expecting 'a')")
}
// stateFa is the state after reading `fa`.
func stateFa(s *scanner, c int) int {
if c == 'l' {
s.step = stateFal
return scanContinue
}
return s.error(c, "in literal false (expecting 'l')")
}
// stateFal is the state after reading `fal`.
func stateFal(s *scanner, c int) int {
if c == 's' {
s.step = stateFals
return scanContinue
}
return s.error(c, "in literal false (expecting 's')")
}
// stateFals is the state after reading `fals`.
func stateFals(s *scanner, c int) int {
if c == 'e' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal false (expecting 'e')")
}
// stateN is the state after reading `n`.
func stateN(s *scanner, c int) int {
if c == 'u' {
s.step = stateNu
return scanContinue
}
return s.error(c, "in literal null (expecting 'u')")
}
// stateNu is the state after reading `nu`.
func stateNu(s *scanner, c int) int {
if c == 'l' {
s.step = stateNul
return scanContinue
}
return s.error(c, "in literal null (expecting 'l')")
}
// stateNul is the state after reading `nul`.
func stateNul(s *scanner, c int) int {
if c == 'l' {
s.step = stateEndValue
return scanContinue
}
return s.error(c, "in literal null (expecting 'l')")
}
// stateError is the state after reaching a syntax error,
// such as after reading `[1}` or `5.1.2`.
func stateError(s *scanner, c int) int {
return scanError
}
// error records an error and switches to the error state.
func (s *scanner) error(c int, context string) int {
s.step = stateError
s.err = &SyntaxError{"invalid character " + quoteChar(c) + " " + context, s.bytes}
return scanError
}
// quoteChar formats c as a quoted character literal
func quoteChar(c int) string {
// special cases - different from quoted strings
if c == '\'' {
return `'\''`
}
if c == '"' {
return `'"'`
}
// use quoted string with different quotation marks
s := strconv.Quote(string(c))
return "'" + s[1:len(s)-1] + "'"
}
// undo causes the scanner to return scanCode from the next state transition.
// This gives callers a simple 1-byte undo mechanism.
func (s *scanner) undo(scanCode int) {
if s.redo {
panic("json: invalid use of scanner")
}
s.redoCode = scanCode
s.redoState = s.step
s.step = stateRedo
s.redo = true
}
// stateRedo helps implement the scanner's 1-byte undo.
func stateRedo(s *scanner, c int) int {
s.redo = false
s.step = s.redoState
return s.redoCode
}
// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json2
import (
"bytes"
"errors"
"io"
)
// A Decoder reads and decodes JSON objects from an input stream.
type Decoder struct {
r io.Reader
buf []byte
d decodeState
scan scanner
err error
}
// NewDecoder returns a new decoder that reads from r.
//
// The decoder introduces its own buffering and may
// read data from r beyond the JSON values requested.
func NewDecoder(r io.Reader) *Decoder {
return &Decoder{r: r}
}
// UseNumber causes the Decoder to unmarshal a number into an interface{} as a
// Number instead of as a float64.
func (dec *Decoder) UseNumber() { dec.d.useNumber = true }
// Decode reads the next JSON-encoded value from its
// input and stores it in the value pointed to by v.
//
// See the documentation for Unmarshal for details about
// the conversion of JSON into a Go value.
func (dec *Decoder) Decode(v interface{}) error {
if dec.err != nil {
return dec.err
}
n, err := dec.readValue()
if err != nil {
return err
}
// Don't save err from unmarshal into dec.err:
// the connection is still usable since we read a complete JSON
// object from it before the error happened.
dec.d.init(dec.buf[0:n])
err = dec.d.unmarshal(v)
// Slide rest of data down.
rest := copy(dec.buf, dec.buf[n:])
dec.buf = dec.buf[0:rest]
return err
}
// Buffered returns a reader of the data remaining in the Decoder's
// buffer. The reader is valid until the next call to Decode.
func (dec *Decoder) Buffered() io.Reader {
return bytes.NewReader(dec.buf)
}
// readValue reads a JSON value into dec.buf.
// It returns the length of the encoding.
func (dec *Decoder) readValue() (int, error) {
dec.scan.reset()
scanp := 0
var err error
Input:
for {
// Look in the buffer for a new value.
for i, c := range dec.buf[scanp:] {
dec.scan.bytes++
v := dec.scan.step(&dec.scan, int(c))
if v == scanEnd {
scanp += i
break Input
}
// scanEnd is delayed one byte.
// We might block trying to get that byte from src,
// so instead invent a space byte.
if (v == scanEndObject || v == scanEndArray) && dec.scan.step(&dec.scan, ' ') == scanEnd {
scanp += i + 1
break Input
}
if v == scanError {
dec.err = dec.scan.err
return 0, dec.scan.err
}
}
scanp = len(dec.buf)
// Did the last read have an error?
// Delayed until now to allow buffer scan.
if err != nil {
if err == io.EOF {
if dec.scan.step(&dec.scan, ' ') == scanEnd {
break Input
}
if nonSpace(dec.buf) {
err = io.ErrUnexpectedEOF
}
}
dec.err = err
return 0, err
}
// Make room to read more into the buffer.
const minRead = 512
if cap(dec.buf)-len(dec.buf) < minRead {
newBuf := make([]byte, len(dec.buf), 2*cap(dec.buf)+minRead)
copy(newBuf, dec.buf)
dec.buf = newBuf
}
// Read. Delay error for next iteration (after scan).
var n int
n, err = dec.r.Read(dec.buf[len(dec.buf):cap(dec.buf)])
dec.buf = dec.buf[0 : len(dec.buf)+n]
}
return scanp, nil
}
func nonSpace(b []byte) bool {
for _, c := range b {
if !isSpace(rune(c)) {
return true
}
}
return false
}
// An Encoder writes JSON objects to an output stream.
type Encoder struct {
w io.Writer
e encodeState
err error
}
// NewEncoder returns a new encoder that writes to w.
func NewEncoder(w io.Writer) *Encoder {
return &Encoder{w: w}
}
// Encode writes the JSON encoding of v to the stream.
//
// See the documentation for Marshal for details about the
// conversion of Go values to JSON.
func (enc *Encoder) Encode(v interface{}) error {
if enc.err != nil {
return enc.err
}
e := newEncodeState()
err := e.marshal(v)
if err != nil {
return err
}
// Terminate each value with a newline.
// This makes the output look a little nicer
// when debugging, and some kind of space
// is required if the encoded value was a number,
// so that the reader knows there aren't more
// digits coming.
e.WriteByte('\n')
if _, err = enc.w.Write(e.Bytes()); err != nil {
enc.err = err
}
putEncodeState(e)
return err
}
// RawMessage is a raw encoded JSON object.
// It implements Marshaler and Unmarshaler and can
// be used to delay JSON decoding or precompute a JSON encoding.
type RawMessage []byte
// MarshalJSON returns *m as the JSON encoding of m.
func (m *RawMessage) MarshalJSON() ([]byte, error) {
return *m, nil
}
// UnmarshalJSON sets *m to a copy of data.
func (m *RawMessage) UnmarshalJSON(data []byte) error {
if m == nil {
return errors.New("json.RawMessage: UnmarshalJSON on nil pointer")
}
*m = append((*m)[0:0], data...)
return nil
}
var _ Marshaler = (*RawMessage)(nil)
var _ Unmarshaler = (*RawMessage)(nil)
// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package json2
import (
"strings"
)
// tagOptions is the string following a comma in a struct field's "json"
// tag, or the empty string. It does not include the leading comma.
type tagOptions string
// parseTag splits a struct field's json tag into its name and
// comma-separated options.
func parseTag(tag string) (string, tagOptions) {
if idx := strings.Index(tag, ","); idx != -1 {
return tag[:idx], tagOptions(tag[idx+1:])
}
return tag, tagOptions("")
}
// Contains reports whether a comma-separated list of options
// contains a particular substr flag. substr must be surrounded by a
// string boundary or commas.
func (o tagOptions) Contains(optionName string) bool {
if len(o) == 0 {
return false
}
s := string(o)
for s != "" {
var next string
i := strings.Index(s, ",")
if i >= 0 {
s, next = s[:i], s[i+1:]
}
if s == optionName {
return true
}
s = next
}
return false
}
...@@ -18,6 +18,7 @@ package ast ...@@ -18,6 +18,7 @@ package ast
import ( import (
"io" "io"
. "github.com/pingcap/parser/format"
"github.com/pingcap/parser/model" "github.com/pingcap/parser/model"
"github.com/pingcap/parser/types" "github.com/pingcap/parser/types"
) )
......
...@@ -16,6 +16,7 @@ package ast ...@@ -16,6 +16,7 @@ package ast
import ( import (
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/pingcap/parser/auth" "github.com/pingcap/parser/auth"
. "github.com/pingcap/parser/format"
"github.com/pingcap/parser/model" "github.com/pingcap/parser/model"
"github.com/pingcap/parser/types" "github.com/pingcap/parser/types"
) )
......
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/pingcap/parser/auth" "github.com/pingcap/parser/auth"
. "github.com/pingcap/parser/format"
"github.com/pingcap/parser/model" "github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql" "github.com/pingcap/parser/mysql"
) )
...@@ -1293,6 +1294,7 @@ const ( ...@@ -1293,6 +1294,7 @@ const (
ShowStatus ShowStatus
ShowCollation ShowCollation
ShowCreateTable ShowCreateTable
ShowCreateUser
ShowGrants ShowGrants
ShowTriggers ShowTriggers
ShowProcedureStatus ShowProcedureStatus
...@@ -1323,7 +1325,7 @@ type ShowStmt struct { ...@@ -1323,7 +1325,7 @@ type ShowStmt struct {
Column *ColumnName // Used for `desc table column`. Column *ColumnName // Used for `desc table column`.
Flag int // Some flag parsed from sql, such as FULL. Flag int // Some flag parsed from sql, such as FULL.
Full bool Full bool
User *auth.UserIdentity // Used for show grants. User *auth.UserIdentity // Used for show grants/create user.
IfNotExists bool // Used for `show create database if not exists` IfNotExists bool // Used for `show create database if not exists`
// GlobalScope is used by show variables // GlobalScope is used by show variables
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"strings" "strings"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/pingcap/parser/format"
"github.com/pingcap/parser/model" "github.com/pingcap/parser/model"
"github.com/pingcap/parser/opcode" "github.com/pingcap/parser/opcode"
) )
......
...@@ -18,6 +18,7 @@ import ( ...@@ -18,6 +18,7 @@ import (
"io" "io"
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/pingcap/parser/format"
"github.com/pingcap/parser/model" "github.com/pingcap/parser/model"
"github.com/pingcap/parser/types" "github.com/pingcap/parser/types"
) )
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/pingcap/parser/auth" "github.com/pingcap/parser/auth"
. "github.com/pingcap/parser/format"
"github.com/pingcap/parser/model" "github.com/pingcap/parser/model"
"github.com/pingcap/parser/mysql" "github.com/pingcap/parser/mysql"
) )
......
...@@ -15,6 +15,7 @@ package ast ...@@ -15,6 +15,7 @@ package ast
import ( import (
"github.com/pingcap/errors" "github.com/pingcap/errors"
. "github.com/pingcap/parser/format"
"github.com/pingcap/parser/model" "github.com/pingcap/parser/model"
) )
......
...@@ -13,12 +13,6 @@ ...@@ -13,12 +13,6 @@
package ast package ast
import (
"fmt"
"io"
"strings"
)
// IsReadOnly checks whether the input ast is readOnly. // IsReadOnly checks whether the input ast is readOnly.
func IsReadOnly(node Node) bool { func IsReadOnly(node Node) bool {
switch st := node.(type) { switch st := node.(type) {
...@@ -65,152 +59,3 @@ func (checker *readOnlyChecker) Enter(in Node) (out Node, skipChildren bool) { ...@@ -65,152 +59,3 @@ func (checker *readOnlyChecker) Enter(in Node) (out Node, skipChildren bool) {
func (checker *readOnlyChecker) Leave(in Node) (out Node, ok bool) { func (checker *readOnlyChecker) Leave(in Node) (out Node, ok bool) {
return in, checker.readOnly return in, checker.readOnly
} }
//RestoreFlag mark the Restore format
type RestoreFlags uint64
// Mutually exclusive group of `RestoreFlags`:
// [RestoreStringSingleQuotes, RestoreStringDoubleQuotes]
// [RestoreKeyWordUppercase, RestoreKeyWordLowercase]
// [RestoreNameUppercase, RestoreNameLowercase]
// [RestoreNameDoubleQuotes, RestoreNameBackQuotes]
// The flag with the left position in each group has a higher priority.
const (
RestoreStringSingleQuotes RestoreFlags = 1 << iota
RestoreStringDoubleQuotes
RestoreStringEscapeBackslash
RestoreKeyWordUppercase
RestoreKeyWordLowercase
RestoreNameUppercase
RestoreNameLowercase
RestoreNameDoubleQuotes
RestoreNameBackQuotes
)
const (
DefaultRestoreFlags = RestoreStringSingleQuotes | RestoreKeyWordUppercase | RestoreNameBackQuotes
)
func (rf RestoreFlags) has(flag RestoreFlags) bool {
return rf&flag != 0
}
// HasStringSingleQuotesFlag returns a boolean indicating when `rf` has `RestoreStringSingleQuotes` flag.
func (rf RestoreFlags) HasStringSingleQuotesFlag() bool {
return rf.has(RestoreStringSingleQuotes)
}
// HasStringDoubleQuotesFlag returns a boolean indicating whether `rf` has `RestoreStringDoubleQuotes` flag.
func (rf RestoreFlags) HasStringDoubleQuotesFlag() bool {
return rf.has(RestoreStringDoubleQuotes)
}
// HasStringEscapeBackslashFlag returns a boolean indicating whether `rf` has `RestoreStringEscapeBackslash` flag.
func (rf RestoreFlags) HasStringEscapeBackslashFlag() bool {
return rf.has(RestoreStringEscapeBackslash)
}
// HasKeyWordUppercaseFlag returns a boolean indicating whether `rf` has `RestoreKeyWordUppercase` flag.
func (rf RestoreFlags) HasKeyWordUppercaseFlag() bool {
return rf.has(RestoreKeyWordUppercase)
}
// HasKeyWordLowercaseFlag returns a boolean indicating whether `rf` has `RestoreKeyWordLowercase` flag.
func (rf RestoreFlags) HasKeyWordLowercaseFlag() bool {
return rf.has(RestoreKeyWordLowercase)
}
// HasNameUppercaseFlag returns a boolean indicating whether `rf` has `RestoreNameUppercase` flag.
func (rf RestoreFlags) HasNameUppercaseFlag() bool {
return rf.has(RestoreNameUppercase)
}
// HasNameLowercaseFlag returns a boolean indicating whether `rf` has `RestoreNameLowercase` flag.
func (rf RestoreFlags) HasNameLowercaseFlag() bool {
return rf.has(RestoreNameLowercase)
}
// HasNameDoubleQuotesFlag returns a boolean indicating whether `rf` has `RestoreNameDoubleQuotes` flag.
func (rf RestoreFlags) HasNameDoubleQuotesFlag() bool {
return rf.has(RestoreNameDoubleQuotes)
}
// HasNameBackQuotesFlag returns a boolean indicating whether `rf` has `RestoreNameBackQuotes` flag.
func (rf RestoreFlags) HasNameBackQuotesFlag() bool {
return rf.has(RestoreNameBackQuotes)
}
// RestoreCtx is `Restore` context to hold flags and writer.
type RestoreCtx struct {
Flags RestoreFlags
In io.Writer
JoinLevel int
}
// NewRestoreCtx returns a new `RestoreCtx`.
func NewRestoreCtx(flags RestoreFlags, in io.Writer) *RestoreCtx {
return &RestoreCtx{flags, in, 0}
}
// WriteKeyWord writes the `keyWord` into writer.
// `keyWord` will be converted format(uppercase and lowercase for now) according to `RestoreFlags`.
func (ctx *RestoreCtx) WriteKeyWord(keyWord string) {
switch {
case ctx.Flags.HasKeyWordUppercaseFlag():
keyWord = strings.ToUpper(keyWord)
case ctx.Flags.HasKeyWordLowercaseFlag():
keyWord = strings.ToLower(keyWord)
}
fmt.Fprint(ctx.In, keyWord)
}
// WriteString writes the string into writer
// `str` may be wrapped in quotes and escaped according to RestoreFlags.
func (ctx *RestoreCtx) WriteString(str string) {
if ctx.Flags.HasStringEscapeBackslashFlag() {
str = strings.Replace(str, `\`, `\\`, -1)
}
quotes := ""
switch {
case ctx.Flags.HasStringSingleQuotesFlag():
str = strings.Replace(str, `'`, `''`, -1)
quotes = `'`
case ctx.Flags.HasStringDoubleQuotesFlag():
str = strings.Replace(str, `"`, `""`, -1)
quotes = `"`
}
fmt.Fprint(ctx.In, quotes, str, quotes)
}
// WriteName writes the name into writer
// `name` maybe wrapped in quotes and escaped according to RestoreFlags.
func (ctx *RestoreCtx) WriteName(name string) {
switch {
case ctx.Flags.HasNameUppercaseFlag():
name = strings.ToUpper(name)
case ctx.Flags.HasNameLowercaseFlag():
name = strings.ToLower(name)
}
quotes := ""
switch {
case ctx.Flags.HasNameDoubleQuotesFlag():
name = strings.Replace(name, `"`, `""`, -1)
quotes = `"`
case ctx.Flags.HasNameBackQuotesFlag():
name = strings.Replace(name, "`", "``", -1)
quotes = "`"
}
fmt.Fprint(ctx.In, quotes, name, quotes)
}
// WritePlain writes the plain text into writer without any handling.
func (ctx *RestoreCtx) WritePlain(plainText string) {
fmt.Fprint(ctx.In, plainText)
}
// WritePlainf write the plain text into writer without any handling.
func (ctx *RestoreCtx) WritePlainf(format string, a ...interface{}) {
fmt.Fprintf(ctx.In, format, a...)
}
...@@ -21,6 +21,7 @@ import ( ...@@ -21,6 +21,7 @@ import (
"bytes" "bytes"
"fmt" "fmt"
"io" "io"
"strings"
) )
const ( const (
...@@ -193,3 +194,152 @@ func OutputFormat(s string) string { ...@@ -193,3 +194,152 @@ func OutputFormat(s string) string {
return buf.String() return buf.String()
} }
//RestoreFlag mark the Restore format
type RestoreFlags uint64
// Mutually exclusive group of `RestoreFlags`:
// [RestoreStringSingleQuotes, RestoreStringDoubleQuotes]
// [RestoreKeyWordUppercase, RestoreKeyWordLowercase]
// [RestoreNameUppercase, RestoreNameLowercase]
// [RestoreNameDoubleQuotes, RestoreNameBackQuotes]
// The flag with the left position in each group has a higher priority.
const (
RestoreStringSingleQuotes RestoreFlags = 1 << iota
RestoreStringDoubleQuotes
RestoreStringEscapeBackslash
RestoreKeyWordUppercase
RestoreKeyWordLowercase
RestoreNameUppercase
RestoreNameLowercase
RestoreNameDoubleQuotes
RestoreNameBackQuotes
)
const (
DefaultRestoreFlags = RestoreStringSingleQuotes | RestoreKeyWordUppercase | RestoreNameBackQuotes
)
func (rf RestoreFlags) has(flag RestoreFlags) bool {
return rf&flag != 0
}
// HasStringSingleQuotesFlag returns a boolean indicating when `rf` has `RestoreStringSingleQuotes` flag.
func (rf RestoreFlags) HasStringSingleQuotesFlag() bool {
return rf.has(RestoreStringSingleQuotes)
}
// HasStringDoubleQuotesFlag returns a boolean indicating whether `rf` has `RestoreStringDoubleQuotes` flag.
func (rf RestoreFlags) HasStringDoubleQuotesFlag() bool {
return rf.has(RestoreStringDoubleQuotes)
}
// HasStringEscapeBackslashFlag returns a boolean indicating whether `rf` has `RestoreStringEscapeBackslash` flag.
func (rf RestoreFlags) HasStringEscapeBackslashFlag() bool {
return rf.has(RestoreStringEscapeBackslash)
}
// HasKeyWordUppercaseFlag returns a boolean indicating whether `rf` has `RestoreKeyWordUppercase` flag.
func (rf RestoreFlags) HasKeyWordUppercaseFlag() bool {
return rf.has(RestoreKeyWordUppercase)
}
// HasKeyWordLowercaseFlag returns a boolean indicating whether `rf` has `RestoreKeyWordLowercase` flag.
func (rf RestoreFlags) HasKeyWordLowercaseFlag() bool {
return rf.has(RestoreKeyWordLowercase)
}
// HasNameUppercaseFlag returns a boolean indicating whether `rf` has `RestoreNameUppercase` flag.
func (rf RestoreFlags) HasNameUppercaseFlag() bool {
return rf.has(RestoreNameUppercase)
}
// HasNameLowercaseFlag returns a boolean indicating whether `rf` has `RestoreNameLowercase` flag.
func (rf RestoreFlags) HasNameLowercaseFlag() bool {
return rf.has(RestoreNameLowercase)
}
// HasNameDoubleQuotesFlag returns a boolean indicating whether `rf` has `RestoreNameDoubleQuotes` flag.
func (rf RestoreFlags) HasNameDoubleQuotesFlag() bool {
return rf.has(RestoreNameDoubleQuotes)
}
// HasNameBackQuotesFlag returns a boolean indicating whether `rf` has `RestoreNameBackQuotes` flag.
func (rf RestoreFlags) HasNameBackQuotesFlag() bool {
return rf.has(RestoreNameBackQuotes)
}
// RestoreCtx is `Restore` context to hold flags and writer.
type RestoreCtx struct {
Flags RestoreFlags
In io.Writer
JoinLevel int
}
// NewRestoreCtx returns a new `RestoreCtx`.
func NewRestoreCtx(flags RestoreFlags, in io.Writer) *RestoreCtx {
return &RestoreCtx{flags, in, 0}
}
// WriteKeyWord writes the `keyWord` into writer.
// `keyWord` will be converted format(uppercase and lowercase for now) according to `RestoreFlags`.
func (ctx *RestoreCtx) WriteKeyWord(keyWord string) {
switch {
case ctx.Flags.HasKeyWordUppercaseFlag():
keyWord = strings.ToUpper(keyWord)
case ctx.Flags.HasKeyWordLowercaseFlag():
keyWord = strings.ToLower(keyWord)
}
fmt.Fprint(ctx.In, keyWord)
}
// WriteString writes the string into writer
// `str` may be wrapped in quotes and escaped according to RestoreFlags.
func (ctx *RestoreCtx) WriteString(str string) {
if ctx.Flags.HasStringEscapeBackslashFlag() {
str = strings.Replace(str, `\`, `\\`, -1)
}
quotes := ""
switch {
case ctx.Flags.HasStringSingleQuotesFlag():
str = strings.Replace(str, `'`, `''`, -1)
quotes = `'`
case ctx.Flags.HasStringDoubleQuotesFlag():
str = strings.Replace(str, `"`, `""`, -1)
quotes = `"`
}
fmt.Fprint(ctx.In, quotes, str, quotes)
}
// WriteName writes the name into writer
// `name` maybe wrapped in quotes and escaped according to RestoreFlags.
func (ctx *RestoreCtx) WriteName(name string) {
switch {
case ctx.Flags.HasNameUppercaseFlag():
name = strings.ToUpper(name)
case ctx.Flags.HasNameLowercaseFlag():
name = strings.ToLower(name)
}
quotes := ""
switch {
case ctx.Flags.HasNameDoubleQuotesFlag():
name = strings.Replace(name, `"`, `""`, -1)
quotes = `"`
case ctx.Flags.HasNameBackQuotesFlag():
name = strings.Replace(name, "`", "``", -1)
quotes = "`"
}
fmt.Fprint(ctx.In, quotes, name, quotes)
}
// WritePlain writes the plain text into writer without any handling.
func (ctx *RestoreCtx) WritePlain(plainText string) {
fmt.Fprint(ctx.In, plainText)
}
// WritePlainf write the plain text into writer without any handling.
func (ctx *RestoreCtx) WritePlainf(format string, a ...interface{}) {
fmt.Fprintf(ctx.In, format, a...)
}
...@@ -7,9 +7,9 @@ require ( ...@@ -7,9 +7,9 @@ require (
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186 github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a github.com/cznic/y v0.0.0-20170802143616-045f81c6662a
github.com/pingcap/check v0.0.0-20181213055612-5c2b07721bdb github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8
github.com/pingcap/errors v0.11.0 github.com/pingcap/errors v0.11.0
github.com/pingcap/tidb v0.0.0-20181217070741-096bb68e6bef github.com/pingcap/tidb v0.0.0-20180108134023-971629b9477a
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323
github.com/sirupsen/logrus v1.2.0 github.com/sirupsen/logrus v1.2.0
golang.org/x/text v0.3.0 golang.org/x/text v0.3.0
......
...@@ -50,6 +50,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME ...@@ -50,6 +50,7 @@ github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeME
github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E= github.com/go-ole/go-ole v1.2.1 h1:2lOsA72HgjxAuMlKpFiCbHTvu44PIVkZ5hqm3RSdI/E=
github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8= github.com/go-ole/go-ole v1.2.1/go.mod h1:7FAglXiTm7HKlQRDeOQ6ZNUHidzCWXuZWq/1dTyBNF8=
github.com/go-sql-driver/mysql v0.0.0-20170715192408-3955978caca4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= github.com/go-sql-driver/mysql v0.0.0-20170715192408-3955978caca4/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
github.com/gogo/protobuf v0.0.0-20180717141946-636bf0302bc9/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo= github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
...@@ -58,6 +59,7 @@ github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff h1:kOkM9whyQYodu ...@@ -58,6 +59,7 @@ github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff h1:kOkM9whyQYodu
github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20181024230925-c65c006176ff/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= github.com/golang/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E=
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
github.com/golang/protobuf v0.0.0-20180814211427-aa810b61a9c7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM= github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w= github.com/golang/snappy v0.0.0-20180518054509-2e65f85255db h1:woRePGFeVFfLKN/pOkfl+p/TAqKOfFu+7KPlMVpok/w=
...@@ -95,7 +97,6 @@ github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7 h1:7KAv7KMGTTqSmYZtNdc ...@@ -95,7 +97,6 @@ github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7 h1:7KAv7KMGTTqSmYZtNdc
github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7/go.mod h1:iWMfgwqYW+e8n5lC/jjNEhwcjbRDpl5NT7n2h+4UNcI= github.com/ngaut/pools v0.0.0-20180318154953-b7bc8c42aac7/go.mod h1:iWMfgwqYW+e8n5lC/jjNEhwcjbRDpl5NT7n2h+4UNcI=
github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef h1:K0Fn+DoFqNqktdZtdV3bPQ/0cuYh2H4rkg0tytX/07k= github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef h1:K0Fn+DoFqNqktdZtdV3bPQ/0cuYh2H4rkg0tytX/07k=
github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef/go.mod h1:7WjlapSfwQyo6LNmIvEWzsW1hbBQfpUO4JWnuQRmva8= github.com/ngaut/sync2 v0.0.0-20141008032647-7a24ed77b2ef/go.mod h1:7WjlapSfwQyo6LNmIvEWzsW1hbBQfpUO4JWnuQRmva8=
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs= github.com/onsi/ginkgo v1.7.0 h1:WSHQ+IS43OoUrWtD1/bbclrwK8TTH5hzp+umCiuxHgs=
github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
...@@ -105,21 +106,24 @@ github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7l ...@@ -105,21 +106,24 @@ github.com/opentracing/basictracer-go v1.0.0 h1:YyUAhaEfjoWXclZVJ9sGoNct7j4TVk7l
github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74= github.com/opentracing/basictracer-go v1.0.0/go.mod h1:QfBfYuafItcjQuMwinw9GhYKwFXS9KnPs5lxoYwgW74=
github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg= github.com/opentracing/opentracing-go v1.0.2 h1:3jA2P6O1F9UOrWVpwrIo17pu01KWvNWg4X946/Y5Zwg=
github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/opentracing/opentracing-go v1.0.2/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
github.com/pingcap/check v0.0.0-20181213055612-5c2b07721bdb h1:RGm4hzUgf7wxELKAzOBV27WFMxBD33OQkDwX6VOs/W4= github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg=
github.com/pingcap/check v0.0.0-20181213055612-5c2b07721bdb/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ= github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ=
github.com/pingcap/errors v0.11.0 h1:DCJQB8jrHbQ1VVlMFIrbj2ApScNNotVmkSNplu2yUt4= github.com/pingcap/errors v0.11.0 h1:DCJQB8jrHbQ1VVlMFIrbj2ApScNNotVmkSNplu2yUt4=
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8= github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/gofail v0.0.0-20181217135706-6a951c1e42c3 h1:04yuCf5NMvLU8rB2m4Qs3rynH7EYpMno3lHkewIOdMo=
github.com/pingcap/gofail v0.0.0-20181217135706-6a951c1e42c3/go.mod h1:DazNTg0PTldtpsQiT9I5tVJwV1onHMKBBgXzmJUlMns=
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e h1:P73/4dPCL96rGrobssy1nVy2VaVpNCuLpCbr+FEaTA8= github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e h1:P73/4dPCL96rGrobssy1nVy2VaVpNCuLpCbr+FEaTA8=
github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw= github.com/pingcap/goleveldb v0.0.0-20171020122428-b9ff6c35079e/go.mod h1:O17XtbryoCJhkKGbT62+L2OlrniwqiGLSqrmdHCMzZw=
github.com/pingcap/kvproto v0.0.0-20181105061835-1b5d69cd1d26 h1:JK4VLNYbSn36QSbCnqALi2ySXdH0DfcMssT/zmLf4Ls= github.com/pingcap/kvproto v0.0.0-20181203065228-c14302da291c h1:Qf5St5XGwKgKQLar9lEXoeO0hJMVaFBj3JqvFguWtVg=
github.com/pingcap/kvproto v0.0.0-20181105061835-1b5d69cd1d26/go.mod h1:0gwbe1F2iBIjuQ9AH0DbQhL+Dpr5GofU8fgYyXk+ykk= github.com/pingcap/kvproto v0.0.0-20181203065228-c14302da291c/go.mod h1:Ja9XPjot9q4/3JyCZodnWDGNXt4pKemhIYCvVJM7P24=
github.com/pingcap/parser v0.0.0-20181214132045-732efe993f70/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA= github.com/pingcap/parser v0.0.0-20190108044100-02812c3c22e7/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE= github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE=
github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E= github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E=
github.com/pingcap/tidb v0.0.0-20181217070741-096bb68e6bef h1:a00XEAUzCi+RlsZcAg/LJx3zTL6FY+lwPwyxz5ZlnsI= github.com/pingcap/tidb v0.0.0-20180108134023-971629b9477a h1:+UaU3bgZJtmgn1IJOWQU63z+HaVNhOfyxT8puMZ32rc=
github.com/pingcap/tidb v0.0.0-20181217070741-096bb68e6bef/go.mod h1:YrstANCcWGHO/mbgK4nofaNCj3zOpmkhmfMwlltzPSE= github.com/pingcap/tidb v0.0.0-20180108134023-971629b9477a/go.mod h1:ytMJRc0YwJxWtwJpuu60xXIXHSTGwi1jhmt3TnxifYw=
github.com/pingcap/tidb-tools v0.0.0-20181112132202-4860a0d5de03 h1:xVuo5U+l6XAWHsb+xhkZ8zz3jerIwDfCHAO6kR2Kaog= github.com/pingcap/tidb-tools v2.1.1-0.20181218072513-b2235d442b06+incompatible h1:Bsd+NHosPVowEGB3BCx+2d8wUQGDTXSSC5ljeNS6cXo=
github.com/pingcap/tidb-tools v0.0.0-20181112132202-4860a0d5de03/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM= github.com/pingcap/tidb-tools v2.1.1-0.20181218072513-b2235d442b06+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM=
github.com/pingcap/tipb v0.0.0-20170310053819-1043caee48da/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 h1:mRKKzRjDNaUNPnAkPAHnRqpNmwNWBX1iA+hxlmvQ93I= github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 h1:mRKKzRjDNaUNPnAkPAHnRqpNmwNWBX1iA+hxlmvQ93I=
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI= github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw= github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
...@@ -178,24 +182,28 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf ...@@ -178,24 +182,28 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20180702182130-06c8688daad7/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181005035420-146acd28ed58/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816 h1:mVFkLpejdFLXVUv9E42f3XJVfMdqd0IVLVIVLjZWn5o= golang.org/x/net v0.0.0-20181029044818-c44066c5c816 h1:mVFkLpejdFLXVUv9E42f3XJVfMdqd0IVLVIVLjZWn5o=
golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181029044818-c44066c5c816/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33 h1:I6FyU15t786LL7oL/hn43zqTuEGr4PN7F4XJ1p4E3Y8=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e h1:o3PsSEY8E4eXWkXrIP9YJALUkVZqzHJT5DOasTyn8Vs=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/text v0.0.0-20171214130843-f21a4dfb5e38/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c h1:fqgJT0MGcGpPgpWU7VRdRjuArfcOvC4AoJmILihzhDg=
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20180828015842-6cd1fcedba52/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20181008205924-a2b3f7f249e9/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f h1:FU37niK8AQ59mHcskRyQL7H0ErSeNh650vdcj8HqdSI=
google.golang.org/genproto v0.0.0-20181004005441-af9cb2a35e7f/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
google.golang.org/grpc v0.0.0-20180607172857-7a6a684ca69e/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw=
google.golang.org/grpc v1.16.0 h1:dz5IJGuC2BB7qXR5AyHNwAUBhZscK2xVez7mznh72sY= google.golang.org/grpc v1.16.0 h1:dz5IJGuC2BB7qXR5AyHNwAUBhZscK2xVez7mznh72sY=
google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio= google.golang.org/grpc v1.16.0/go.mod h1:0JHn/cJsOMiMfNA9+DeHDlAU7KAAB5GDlYFpa9MZMio=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
......
...@@ -123,11 +123,17 @@ func (s *Scanner) stmtText() string { ...@@ -123,11 +123,17 @@ func (s *Scanner) stmtText() string {
// Scanner satisfies yyLexer interface which need this function. // Scanner satisfies yyLexer interface which need this function.
func (s *Scanner) Errorf(format string, a ...interface{}) { func (s *Scanner) Errorf(format string, a ...interface{}) {
str := fmt.Sprintf(format, a...) str := fmt.Sprintf(format, a...)
val := s.r.s[s.r.pos().Offset:] col := s.r.p.Col
startPos := s.stmtStartPos
if s.r.s[startPos] == '\n' {
startPos++
col--
}
val := s.r.s[startPos:]
if len(val) > 2048 { if len(val) > 2048 {
val = val[:2048] val = val[:2048]
} }
err := fmt.Errorf("line %d column %d near \"%s\"%s (total length %d)", s.r.p.Line, s.r.p.Col, val, str, len(s.r.s)) err := fmt.Errorf("line %d column %d near \"%s\"%s (total length %d)", s.r.p.Line, col, val, str, len(s.r.s))
s.errs = append(s.errs, err) s.errs = append(s.errs, err)
} }
......
...@@ -399,7 +399,7 @@ var AllColumnPrivs = []PrivilegeType{SelectPriv, InsertPriv, UpdatePriv} ...@@ -399,7 +399,7 @@ var AllColumnPrivs = []PrivilegeType{SelectPriv, InsertPriv, UpdatePriv}
const AllPrivilegeLiteral = "ALL PRIVILEGES" const AllPrivilegeLiteral = "ALL PRIVILEGES"
// DefaultSQLMode for GLOBAL_VARIABLES // DefaultSQLMode for GLOBAL_VARIABLES
const DefaultSQLMode = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION" const DefaultSQLMode = "ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION"
// DefaultLengthOfMysqlTypes is the map for default physical length of MySQL data types. // DefaultLengthOfMysqlTypes is the map for default physical length of MySQL data types.
// See http://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html // See http://dev.mysql.com/doc/refman/5.7/en/storage-requirements.html
......
...@@ -600,7 +600,7 @@ import ( ...@@ -600,7 +600,7 @@ import (
RevokeStmt "Revoke statement" RevokeStmt "Revoke statement"
RollbackStmt "ROLLBACK statement" RollbackStmt "ROLLBACK statement"
SetStmt "Set variable statement" SetStmt "Set variable statement"
ShowStmt "Show engines/databases/tables/columns/warnings/status statement" ShowStmt "Show engines/databases/tables/user/columns/warnings/status statement"
Statement "statement" Statement "statement"
TraceStmt "TRACE statement" TraceStmt "TRACE statement"
TraceableStmt "traceable statment" TraceableStmt "traceable statment"
...@@ -5907,6 +5907,14 @@ ShowStmt: ...@@ -5907,6 +5907,14 @@ ShowStmt:
DBName: $5.(string), DBName: $5.(string),
} }
} }
| "SHOW" "CREATE" "USER" Username
{
// See https://dev.mysql.com/doc/refman/5.7/en/show-create-user.html
$$ = &ast.ShowStmt{
Tp: ast.ShowCreateUser,
User: $4.(*auth.UserIdentity),
}
}
| "SHOW" "GRANTS" | "SHOW" "GRANTS"
{ {
// See https://dev.mysql.com/doc/refman/5.7/en/show-grants.html // See https://dev.mysql.com/doc/refman/5.7/en/show-grants.html
......
...@@ -20,6 +20,7 @@ import ( ...@@ -20,6 +20,7 @@ import (
"github.com/pingcap/errors" "github.com/pingcap/errors"
"github.com/pingcap/parser/ast" "github.com/pingcap/parser/ast"
"github.com/pingcap/parser/format"
"github.com/pingcap/parser/mysql" "github.com/pingcap/parser/mysql"
"github.com/pingcap/tidb/types" "github.com/pingcap/tidb/types"
"github.com/pingcap/tidb/util/hack" "github.com/pingcap/tidb/util/hack"
...@@ -69,7 +70,7 @@ type ValueExpr struct { ...@@ -69,7 +70,7 @@ type ValueExpr struct {
} }
// Restore implements Node interface. // Restore implements Node interface.
func (n *ValueExpr) Restore(ctx *ast.RestoreCtx) error { func (n *ValueExpr) Restore(ctx *format.RestoreCtx) error {
switch n.Kind() { switch n.Kind() {
case types.KindNull: case types.KindNull:
ctx.WriteKeyWord("NULL") ctx.WriteKeyWord("NULL")
...@@ -195,7 +196,7 @@ type ParamMarkerExpr struct { ...@@ -195,7 +196,7 @@ type ParamMarkerExpr struct {
} }
// Restore implements Node interface. // Restore implements Node interface.
func (n *ParamMarkerExpr) Restore(ctx *ast.RestoreCtx) error { func (n *ParamMarkerExpr) Restore(ctx *format.RestoreCtx) error {
ctx.WritePlain("?") ctx.WritePlain("?")
return nil return nil
} }
......
...@@ -2165,6 +2165,8 @@ var dateFormatParserTable = map[string]dateFormatParser{ ...@@ -2165,6 +2165,8 @@ var dateFormatParserTable = map[string]dateFormatParser{
"%S": secondsNumeric, // Seconds (00..59) "%S": secondsNumeric, // Seconds (00..59)
"%T": time24Hour, // Time, 24-hour (hh:mm:ss) "%T": time24Hour, // Time, 24-hour (hh:mm:ss)
"%Y": yearNumericFourDigits, // Year, numeric, four digits "%Y": yearNumericFourDigits, // Year, numeric, four digits
// Deprecated since MySQL 5.7.5
"%y": yearNumericTwoDigits, // Year, numeric (two digits)
// TODO: Add the following... // TODO: Add the following...
// "%a": abbreviatedWeekday, // Abbreviated weekday name (Sun..Sat) // "%a": abbreviatedWeekday, // Abbreviated weekday name (Sun..Sat)
// "%D": dayOfMonthWithSuffix, // Day of the month with English suffix (0th, 1st, 2nd, 3rd) // "%D": dayOfMonthWithSuffix, // Day of the month with English suffix (0th, 1st, 2nd, 3rd)
...@@ -2176,8 +2178,6 @@ var dateFormatParserTable = map[string]dateFormatParser{ ...@@ -2176,8 +2178,6 @@ var dateFormatParserTable = map[string]dateFormatParser{
// "%w": dayOfWeek, // Day of the week (0=Sunday..6=Saturday) // "%w": dayOfWeek, // Day of the week (0=Sunday..6=Saturday)
// "%X": yearOfWeek, // Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V // "%X": yearOfWeek, // Year for the week where Sunday is the first day of the week, numeric, four digits; used with %V
// "%x": yearOfWeek, // Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v // "%x": yearOfWeek, // Year for the week, where Monday is the first day of the week, numeric, four digits; used with %v
// Deprecated since MySQL 5.7.5
// "%y": yearTwoDigits, // Year, numeric (two digits)
} }
// GetFormatType checks the type(Duration, Date or Datetime) of a format string. // GetFormatType checks the type(Duration, Date or Datetime) of a format string.
...@@ -2235,7 +2235,7 @@ func matchDateWithToken(t *MysqlTime, date string, token string, ctx map[string] ...@@ -2235,7 +2235,7 @@ func matchDateWithToken(t *MysqlTime, date string, token string, ctx map[string]
} }
func parseDigits(input string, count int) (int, bool) { func parseDigits(input string, count int) (int, bool) {
if len(input) < count { if count <= 0 || len(input) < count {
return 0, false return 0, false
} }
...@@ -2432,12 +2432,31 @@ func microSeconds(t *MysqlTime, input string, ctx map[string]int) (string, bool) ...@@ -2432,12 +2432,31 @@ func microSeconds(t *MysqlTime, input string, ctx map[string]int) (string, bool)
} }
func yearNumericFourDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) { func yearNumericFourDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) {
v, succ := parseDigits(input, 4) return yearNumericNDigits(t, input, ctx, 4)
if !succ { }
func yearNumericTwoDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) {
return yearNumericNDigits(t, input, ctx, 2)
}
func yearNumericNDigits(t *MysqlTime, input string, ctx map[string]int, n int) (string, bool) {
effectiveCount, effectiveValue := 0, 0
for effectiveCount+1 <= n {
value, succeed := parseDigits(input, effectiveCount+1)
if !succeed {
break
}
effectiveCount++
effectiveValue = value
}
if effectiveCount == 0 {
return input, false return input, false
} }
t.year = uint16(v) if effectiveCount <= 2 {
return input[4:], true effectiveValue = adjustYear(effectiveValue)
}
t.year = uint16(effectiveValue)
return input[effectiveCount:], true
} }
func dayOfYearThreeDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) { func dayOfYearThreeDigits(t *MysqlTime, input string, ctx map[string]int) (string, bool) {
......
...@@ -111,106 +111,106 @@ ...@@ -111,106 +111,106 @@
"revisionTime": "2018-10-24T15:10:47Z" "revisionTime": "2018-10-24T15:10:47Z"
}, },
{ {
"checksumSHA1": "oPVvRBag6XbaB4dN38RkdejKr70=", "checksumSHA1": "DJypl3jfSRspsBR2fc6eyBktkq4=",
"path": "github.com/pingcap/parser", "path": "github.com/pingcap/parser",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "+KVexpbQ1kxBZA/iUahnFkIUGsU=", "checksumSHA1": "+IqkkjB5E83Q1mUjZ1V+75iHocA=",
"path": "github.com/pingcap/parser/ast", "path": "github.com/pingcap/parser/ast",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "skWGV4FNvD3vr+5olepaPPnylUw=", "checksumSHA1": "skWGV4FNvD3vr+5olepaPPnylUw=",
"path": "github.com/pingcap/parser/auth", "path": "github.com/pingcap/parser/auth",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "t4UHo966WzU9Z0IJkyGHRp0loOk=", "checksumSHA1": "t4UHo966WzU9Z0IJkyGHRp0loOk=",
"path": "github.com/pingcap/parser/charset", "path": "github.com/pingcap/parser/charset",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "SInoXbsRe0tnBwmatmtZYfSFbdk=", "checksumSHA1": "ohLJW2u9NJEzYIJL/AjOqcuKfMY=",
"path": "github.com/pingcap/parser/format", "path": "github.com/pingcap/parser/format",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "ZADwr2/PcEd9VI3XF9OvN4HkJ+8=", "checksumSHA1": "ZADwr2/PcEd9VI3XF9OvN4HkJ+8=",
"path": "github.com/pingcap/parser/model", "path": "github.com/pingcap/parser/model",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "kkqyRzO7TCqnABxjJEo+JclJZLM=", "checksumSHA1": "ge+W5BLlgqKIlvmappsPTLgVJLk=",
"path": "github.com/pingcap/parser/mysql", "path": "github.com/pingcap/parser/mysql",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "mxpiJJ3b08I0o0Sd2rJLYMwz7uw=", "checksumSHA1": "mxpiJJ3b08I0o0Sd2rJLYMwz7uw=",
"path": "github.com/pingcap/parser/opcode", "path": "github.com/pingcap/parser/opcode",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "XvnUllvwMYd6HrMvMiKnn4cGN2M=", "checksumSHA1": "XvnUllvwMYd6HrMvMiKnn4cGN2M=",
"path": "github.com/pingcap/parser/terror", "path": "github.com/pingcap/parser/terror",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "CpuZhpMNeho4tIFPwY2GUDvuEfQ=", "checksumSHA1": "CpuZhpMNeho4tIFPwY2GUDvuEfQ=",
"path": "github.com/pingcap/parser/types", "path": "github.com/pingcap/parser/types",
"revision": "5f15dc90ca5964d59634063e29c22ff6c7d9e49e", "revision": "35fab0be7fca9ea7f7b5350af83a8d1e2775abe4",
"revisionTime": "2019-01-05T06:04:45Z" "revisionTime": "2019-01-08T10:41:42Z"
}, },
{ {
"checksumSHA1": "MxoLdFWi8nwd0uqTJnYqw+JaDAY=", "checksumSHA1": "MxoLdFWi8nwd0uqTJnYqw+JaDAY=",
"path": "github.com/pingcap/tidb/sessionctx/stmtctx", "path": "github.com/pingcap/tidb/sessionctx/stmtctx",
"revision": "78a51a4626999279749c460f3f42a2e92897c2e3", "revision": "c68ee7318319b34fbad53d5abf18275b0273ae41",
"revisionTime": "2019-01-05T13:32:32Z" "revisionTime": "2019-01-08T12:33:36Z"
}, },
{ {
"checksumSHA1": "wlD7aGqTJ5eBQYK0ub4b2Ick1j8=", "checksumSHA1": "1PFyexjkPlACP5S2pRrT6TsjcQ0=",
"path": "github.com/pingcap/tidb/types", "path": "github.com/pingcap/tidb/types",
"revision": "78a51a4626999279749c460f3f42a2e92897c2e3", "revision": "c68ee7318319b34fbad53d5abf18275b0273ae41",
"revisionTime": "2019-01-05T13:32:32Z" "revisionTime": "2019-01-08T12:33:36Z"
}, },
{ {
"checksumSHA1": "DWVD7+ygtT66IQ+cqXmMJ5OVqUk=", "checksumSHA1": "DWVD7+ygtT66IQ+cqXmMJ5OVqUk=",
"path": "github.com/pingcap/tidb/types/json", "path": "github.com/pingcap/tidb/types/json",
"revision": "78a51a4626999279749c460f3f42a2e92897c2e3", "revision": "c68ee7318319b34fbad53d5abf18275b0273ae41",
"revisionTime": "2019-01-05T13:32:32Z" "revisionTime": "2019-01-08T12:33:36Z"
}, },
{ {
"checksumSHA1": "6vi/eCZXqNTa5eAUpxDZet4LPlY=", "checksumSHA1": "yKeU1hJFc7X3afXESYV0Wz5ZPXQ=",
"path": "github.com/pingcap/tidb/types/parser_driver", "path": "github.com/pingcap/tidb/types/parser_driver",
"revision": "78a51a4626999279749c460f3f42a2e92897c2e3", "revision": "c68ee7318319b34fbad53d5abf18275b0273ae41",
"revisionTime": "2019-01-05T13:32:32Z" "revisionTime": "2019-01-08T12:33:36Z"
}, },
{ {
"checksumSHA1": "SS7twHZofFKr8w/pwIKmkp3u5qU=", "checksumSHA1": "SS7twHZofFKr8w/pwIKmkp3u5qU=",
"path": "github.com/pingcap/tidb/util/execdetails", "path": "github.com/pingcap/tidb/util/execdetails",
"revision": "78a51a4626999279749c460f3f42a2e92897c2e3", "revision": "c68ee7318319b34fbad53d5abf18275b0273ae41",
"revisionTime": "2019-01-05T13:32:32Z" "revisionTime": "2019-01-08T12:33:36Z"
}, },
{ {
"checksumSHA1": "nUC7zVoAMNR2a+z2iGqHoN2AkFE=", "checksumSHA1": "nUC7zVoAMNR2a+z2iGqHoN2AkFE=",
"path": "github.com/pingcap/tidb/util/hack", "path": "github.com/pingcap/tidb/util/hack",
"revision": "78a51a4626999279749c460f3f42a2e92897c2e3", "revision": "c68ee7318319b34fbad53d5abf18275b0273ae41",
"revisionTime": "2019-01-05T13:32:32Z" "revisionTime": "2019-01-08T12:33:36Z"
}, },
{ {
"checksumSHA1": "xSyepiuqsoaaeDch7cXeumvVHKM=", "checksumSHA1": "xSyepiuqsoaaeDch7cXeumvVHKM=",
"path": "github.com/pingcap/tidb/util/memory", "path": "github.com/pingcap/tidb/util/memory",
"revision": "78a51a4626999279749c460f3f42a2e92897c2e3", "revision": "c68ee7318319b34fbad53d5abf18275b0273ae41",
"revisionTime": "2019-01-05T13:32:32Z" "revisionTime": "2019-01-08T12:33:36Z"
}, },
{ {
"checksumSHA1": "SmYeIK/fIYXNu8IKxD6HOVQVTuU=", "checksumSHA1": "SmYeIK/fIYXNu8IKxD6HOVQVTuU=",
...@@ -407,62 +407,62 @@ ...@@ -407,62 +407,62 @@
{ {
"checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=", "checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=",
"path": "vitess.io/vitess/go/bytes2", "path": "vitess.io/vitess/go/bytes2",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=", "checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=",
"path": "vitess.io/vitess/go/hack", "path": "vitess.io/vitess/go/hack",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "F5pcGq+2W1FHEjgktTdKOE6W8mk=", "checksumSHA1": "F5pcGq+2W1FHEjgktTdKOE6W8mk=",
"path": "vitess.io/vitess/go/sqltypes", "path": "vitess.io/vitess/go/sqltypes",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=", "checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=",
"path": "vitess.io/vitess/go/vt/log", "path": "vitess.io/vitess/go/vt/log",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "HHIcl3lpWkzLARkkNv94fVaObjo=", "checksumSHA1": "HHIcl3lpWkzLARkkNv94fVaObjo=",
"path": "vitess.io/vitess/go/vt/proto/query", "path": "vitess.io/vitess/go/vt/proto/query",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "YLWTmL+rvz0htn0niRMrIUI6rKc=", "checksumSHA1": "YLWTmL+rvz0htn0niRMrIUI6rKc=",
"path": "vitess.io/vitess/go/vt/proto/topodata", "path": "vitess.io/vitess/go/vt/proto/topodata",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "tNNlcSFFnlOauS2hXnrz/zA/wfk=", "checksumSHA1": "tNNlcSFFnlOauS2hXnrz/zA/wfk=",
"path": "vitess.io/vitess/go/vt/proto/vtgate", "path": "vitess.io/vitess/go/vt/proto/vtgate",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "qz32abYdmm9NfKTc++K0l1EvXXM=", "checksumSHA1": "qz32abYdmm9NfKTc++K0l1EvXXM=",
"path": "vitess.io/vitess/go/vt/proto/vtrpc", "path": "vitess.io/vitess/go/vt/proto/vtrpc",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "IDe+9Bn42lZVsuoYO/epdguiErk=", "checksumSHA1": "IDe+9Bn42lZVsuoYO/epdguiErk=",
"path": "vitess.io/vitess/go/vt/sqlparser", "path": "vitess.io/vitess/go/vt/sqlparser",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12:04Z"
}, },
{ {
"checksumSHA1": "Jx+gOh/kiBDSZxEIWHyYn9brjdo=", "checksumSHA1": "Jx+gOh/kiBDSZxEIWHyYn9brjdo=",
"path": "vitess.io/vitess/go/vt/vterrors", "path": "vitess.io/vitess/go/vt/vterrors",
"revision": "ae79dd48f3157c96d083c890f670011cdebf0a2b", "revision": "42f5c760cca59b18c4ea877284f36eb0be7d6468",
"revisionTime": "2019-01-04T23:32:40Z" "revisionTime": "2019-01-06T20:12: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.
先完成此消息的编辑!
想要评论请 注册