未验证 提交 881ca0d3 编写于 作者: X xiyangxixian 提交者: GitHub

Merge pull request #4 from XiaoMi/master

merge by xiaomi
# CHANGELOG
## 2018-12
- DOING: english translation
## 2018-11
- add all third-party lib into vendor
- support `-report-type chardet`
- add more heuristic rules: TBL.008, KEY.010, ARG.012, KWR.004
......@@ -21,33 +23,36 @@
- fix #112 multi-line comment will cause line counter error, when -report-type=lint
- fix #110 remove bom before auditing
- fix #104 case insensitive regex @ CLA.009
- fix #87 RuleImplicitConversion value type mistach check bug
- fix #87 RuleImplicitConversion value type mismatch check bug
- fix #38 always true where condition check
- abandon stdin terminal interactive mod, which may seems like hangup
## 2018-10
- Fix SplitStatement mulitstatement eof bug #66
- Fix SplitStatement multistatement eof bug #66
- Fix pretty func hangup issue #47
- Fix some foolish code spell error
- Use travis for CI
- Fix Go 1.8 defapth GOPATH compatible issue BUG #5
- Fix Go 1.8 default GOPATH compatible issue BUG #5
- 2018-10-20 开源先锋日(OSCAR)对外正式开源发布代码
## 2018-09
- 修复多个启发式建议不准确BUG,优化部分建议文案使得建议更清晰
- 基于TiDB Parser完善多个DDL类型语句的建议
- 基于 TiDB Parser 完善多个 DDL 类型语句的建议
- 新增lint report-type类型,支持Vim Plugin优化建议输出
- 更新整理项目文档,开源准备
- 2018-09-21 Gdevops SOAR首次对外进行技术分享宣传
## 2018-08
- 利用docker临时容器进行daily测试
- 利用 docker 临时容器进行 daily 测试
- 添加main_test全功能回归测试
- 修复在测试中发现的问题
- mymysql合并MySQL8.0相关PR,修改vendor依赖
- mymysql 合并 MySQL8.0 相关PR,修改vendor依赖
- 改善HeuristicRule中的文案
- 持续集成Vitess Parser的改进
- NewQuery4Audit结构体中引入TiDB Parser
- NewQuery4Audit 结构体中引入 TiDB Parser
- 通过TiAST完成大量与 DDL 相关的TODO
- 修改heuristic rules检查的返回值,提升拓展性
- 建议中引入Position,用于表示建议产生于SQL的位置
......@@ -58,22 +63,25 @@
- 优化 doc 文档
## 2018-07
- 补充文档,添加项目LOGO
- 改善代码质量提升测试覆盖度
- mymysql升级,支持MySQL 8.0
- 提供remove-comment小工具
- 提供索引重复检查小工具
- HeuristicRule新增RuleSpaceAfterDot
- HeuristicRule 新增 RuleSpaceAfterDot
- 支持字符集和Collation不相同时的隐式数据类型转换的检查
## 2018-06
- 支持更多的SQL Rewrite规则
- 添加SQL执行超时限制
- 索引优化建议支持对约束的检查
- 修复数据采样中null值处理不正确的问题
- Explain支持last_query_cost
- 修复数据采样中 NULL 值处理不正确的问题
- Explain 支持 last_query_cost
## 2018-05
- 添加数据采样功能
- 添加语句执行安全检查
- 支持DDL语法检查
......@@ -84,6 +92,7 @@
- 支持SQL Pretty输出
## 2018-04
- 支持语法检查
- 支持测试环境
- 支持MySQL原数据的获取
......@@ -93,7 +102,8 @@
- 引入配置文件
## 2018-03
- 基本架构设计
- 添加大量底层函数用于处理AST
- 添加Insert、Delete、Update转写成Select的基本函数
- 添加Insert、Delete、Update 转写成 Select 的基本函数
- 支持MySQL Explain信息输出
......@@ -18,6 +18,7 @@ BUILD_TIME=`date +%Y%m%d%H%M`
COMMIT_VERSION=`git rev-parse HEAD`
# Add mysql version for testing `MYSQL_RELEASE=percona MYSQL_VERSION=5.7 make docker`
# MySQL 5.1 `MYSQL_RELEASE=vsamov/mysql-5.1.73 make docker`
# MYSQL_RELEASE: mysql, percona, mariadb ...
# MYSQL_VERSION: latest, 8.0, 5.7, 5.6, 5.5 ...
# use mysql:latest as default
......@@ -169,6 +170,7 @@ release: build
docker:
@echo "\033[92mBuild mysql test enviorment\033[0m"
@docker stop soar-mysql 2>/dev/null || true
@docker wait soar-mysql 2>/dev/null || true
@echo "docker run --name soar-mysql $(MYSQL_RELEASE):$(MYSQL_VERSION)"
@docker run --name soar-mysql --rm -d \
-e MYSQL_ROOT_PASSWORD=1tIsB1g3rt \
......@@ -178,16 +180,21 @@ docker:
$(MYSQL_RELEASE):$(MYSQL_VERSION)
@echo "waiting for sakila database initializing "
@while ! mysql -h 127.0.0.1 -u root sakila -p1tIsB1g3rt -NBe "do 1;" 2>/dev/null; do \
@while ! docker exec soar-mysql mysql --user=root --password=1tIsB1g3rt --host "127.0.0.1" --silent -NBe "do 1" >/dev/null 2>&1 ; do \
printf '.' ; \
sleep 1 ; \
done ; \
echo '.'
@echo "mysql test enviorment is ready!"
.PHONY: connect
connect:
mysql -h 127.0.0.1 -u root -p1tIsB1g3rt -c
.PHONY: docker-connect
docker-connect:
docker exec -it soar-mysql mysql --user=root --password=1tIsB1g3rt --host "127.0.0.1"
# attach docker container with bash interactive mode
.PHONY: docker-it
docker-it:
docker exec -it soar-mysql /bin/bash
.PHONY: main_test
main_test: install
......
......@@ -1319,7 +1319,9 @@ func (q *Query4Audit) RuleMultiCompare() Rule {
case *tidb.BinaryOperationExpr:
switch where.L.(type) {
case *tidb.BinaryOperationExpr:
rule = HeuristicRules["RES.009"]
if where.Op.String() == "eq" {
rule = HeuristicRules["RES.009"]
}
}
}
case *tidb.UpdateStmt:
......@@ -1327,7 +1329,9 @@ func (q *Query4Audit) RuleMultiCompare() Rule {
case *tidb.BinaryOperationExpr:
switch where.L.(type) {
case *tidb.BinaryOperationExpr:
rule = HeuristicRules["RES.009"]
if where.Op.String() == "eq" {
rule = HeuristicRules["RES.009"]
}
}
}
case *tidb.DeleteStmt:
......@@ -1335,7 +1339,9 @@ func (q *Query4Audit) RuleMultiCompare() Rule {
case *tidb.BinaryOperationExpr:
switch where.L.(type) {
case *tidb.BinaryOperationExpr:
rule = HeuristicRules["RES.009"]
if where.Op.String() == "eq" {
rule = HeuristicRules["RES.009"]
}
}
}
}
......
......@@ -952,6 +952,10 @@ func TestRuleMultiCompare(t *testing.T) {
},
{
"SELECT * FROM tbl WHERE col = 'abc'",
// https://github.com/XiaoMi/soar/issues/169
"SELECT * FROM tbl WHERE col = 'abc' and c = 1",
"update tb set c = 1 where a = 2 and b = 3",
"delete from tb where a = 2 and b = 3",
},
}
......
......@@ -22,8 +22,6 @@ import (
"testing"
"github.com/XiaoMi/soar/common"
"vitess.io/vitess/go/vt/sqlparser"
)
var update = flag.Bool("update", false, "update .golden files")
......@@ -157,16 +155,6 @@ func TestIsKeyword(t *testing.T) {
}
}
func TestRemoveComments(t *testing.T) {
for _, sql := range TestSqlsPretty {
stmt, _ := sqlparser.Parse(sql)
newSQL := sqlparser.String(stmt)
if newSQL != sql {
fmt.Print(newSQL)
}
}
}
func TestMysqlEscapeString(t *testing.T) {
var strs = []map[string]string{
{
......
......@@ -17,11 +17,14 @@
package ast
import (
"encoding/json"
"github.com/XiaoMi/soar/common"
"github.com/kr/pretty"
"github.com/pingcap/parser"
"github.com/pingcap/parser/ast"
// for pincap parser
_ "github.com/pingcap/tidb/types/parser_driver"
)
......@@ -29,7 +32,12 @@ import (
// TiParse TiDB 语法解析
func TiParse(sql, charset, collation string) ([]ast.StmtNode, error) {
p := parser.New()
return p.Parse(sql, charset, collation)
stmt, warn, err := p.Parse(sql, charset, collation)
// TODO: bypass warning info
for _, w := range warn {
common.Log.Warn(w.Error())
}
return stmt, err
}
// PrintPrettyStmtNode 打印TiParse语法树
......@@ -43,20 +51,19 @@ func PrintPrettyStmtNode(sql, charset, collation string) {
}
}
// TiVisitor TODO:
type TiVisitor struct {
EnterFunc func(node ast.Node) bool
LeaveFunc func(node ast.Node) bool
}
// Enter TODO:
func (visitor *TiVisitor) Enter(n ast.Node) (node ast.Node, skip bool) {
skip = visitor.EnterFunc(n)
return
}
// Leave TODO:
func (visitor *TiVisitor) Leave(n ast.Node) (node ast.Node, ok bool) {
ok = visitor.LeaveFunc(n)
return
// StmtNode2JSON TiParse AST tree into json format
func StmtNode2JSON(sql, charset, collation string) string {
var str string
tree, err := TiParse(sql, charset, collation)
if err != nil {
common.Log.Warning(err.Error())
} else {
b, err := json.MarshalIndent(tree, "", " ")
if err != nil {
common.Log.Error(err.Error())
} else {
str = string(b)
}
}
return str
}
/*
* Copyright 2018 Xiaomi, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package ast
import (
"encoding/json"
"github.com/XiaoMi/soar/common"
"github.com/kr/pretty"
"vitess.io/vitess/go/vt/sqlparser"
)
// PrintPrettyVitessStmtNode print vitess AST struct data
func PrintPrettyVitessStmtNode(sql string) {
tree, err := sqlparser.Parse(sql)
if err != nil {
common.Log.Warning(err.Error())
} else {
_, err = pretty.Println(tree)
common.LogIfWarn(err, "")
}
}
// VitessStmtNode2JSON vitess AST tree into json format
func VitessStmtNode2JSON(sql string) string {
var str string
tree, err := sqlparser.Parse(sql)
if err != nil {
common.Log.Warning(err.Error())
} else {
b, err := json.MarshalIndent(tree, "", " ")
if err != nil {
common.Log.Error(err.Error())
} else {
str = string(b)
}
}
return str
}
......@@ -31,7 +31,6 @@ import (
"github.com/kr/pretty"
"github.com/percona/go-mysql/query"
"github.com/ziutek/mymysql/mysql"
"vitess.io/vitess/go/vt/sqlparser"
)
func main() {
......@@ -143,20 +142,21 @@ func main() {
fmt.Println(ast.Compress(sql) + common.Config.Delimiter)
continue
case "ast":
// SQL 抽象语法树
var tree sqlparser.Statement
tree, err = sqlparser.Parse(sql)
if err != nil {
fmt.Println(err)
} else {
_, err = pretty.Println(tree)
common.LogIfWarn(err, "")
}
// print vitess AST data struct
ast.PrintPrettyVitessStmtNode(sql)
continue
case "ast-json":
// print vitess SQL AST into json format
fmt.Println(ast.VitessStmtNode2JSON(sql))
continue
case "tiast":
// TiDB SQL 抽象语法树
// print TiDB AST data struct
ast.PrintPrettyStmtNode(sql, "", "")
continue
case "tiast-json":
// print TiDB SQL AST into json format
fmt.Println(ast.StmtNode2JSON(sql, "", ""))
continue
case "tokenize":
// SQL 切词
_, err = pretty.Println(ast.Tokenize(sql))
......@@ -185,7 +185,7 @@ func main() {
if syntaxErr != nil {
errContent := fmt.Sprintf("At SQL %d : %v", sqlCounter, syntaxErr)
common.Log.Warning(errContent)
if common.Config.OnlySyntaxCheck {
if common.Config.OnlySyntaxCheck || common.Config.ReportType == "rewrite" {
fmt.Println(errContent)
os.Exit(1)
}
......@@ -424,4 +424,9 @@ func main() {
}
return
}
// syntax check verbose mode, add output for success!
if common.Config.OnlySyntaxCheck && common.Config.Verbose {
fmt.Println("Syntax check OK!")
}
}
......@@ -257,7 +257,7 @@ func parseDSN(odbc string, d *dsn) *dsn {
if d != nil {
// 原来有个判断,后来判断条件被删除了就导致第一次addr无论如何都会被修改。所以这边先注释掉
//addr = d.Addr
// addr = d.Addr
user = d.User
password = d.Password
schema = d.Schema
......@@ -273,27 +273,24 @@ func parseDSN(odbc string, d *dsn) *dsn {
var userInfo, hostInfo, query string
// DSN 格式匹配
// userInfo@hostInfo/database
if res := regexp.MustCompile( `^(.*)@(.*?)/(.*?)($|\?)(.*)`).FindStringSubmatch(odbc); len(res) > 5 {
if res := regexp.MustCompile(`^(.*)@(.*?)/(.*?)($|\?)(.*)`).FindStringSubmatch(odbc); len(res) > 5 {
// userInfo@hostInfo/database
userInfo = res[1]
hostInfo = res[2]
schema = res[3]
query = res[5]
// hostInfo/database
} else if res := regexp.MustCompile(`^(.*)/(.*?)($|\?)(.*)`).FindStringSubmatch(odbc); len(res) > 4 {
// hostInfo/database
hostInfo = res[1]
schema = res[2]
query = res[4]
// userInfo@hostInfo
} else if res := regexp.MustCompile(`^(.*)@(.*?)($|\?)(.*)`).FindStringSubmatch(odbc); len(res) > 4 {
// userInfo@hostInfo
userInfo = res[1]
hostInfo = res[2]
query = res[4]
// hostInfo
} else {
// hostInfo
hostInfo = odbc
}
......@@ -301,7 +298,7 @@ func parseDSN(odbc string, d *dsn) *dsn {
if userInfo != "" {
user = strings.Split(userInfo, ":")[0]
// 防止密码中含有与用户名相同的字符, 所以用正则替换, 剩下的就是密码
password = strings.TrimLeft(regexp.MustCompile("^" + user).ReplaceAllString(userInfo, ""), ":")
password = strings.TrimLeft(regexp.MustCompile("^"+user).ReplaceAllString(userInfo, ""), ":")
}
// 解析主机信息
......@@ -310,13 +307,13 @@ func parseDSN(odbc string, d *dsn) *dsn {
if host == "" {
host = "127.0.0.1"
}
if port == ""{
if port == "" {
port = "3306"
}
addr = host + ":" + port
// 解析查询字符串
if (query != "") {
if query != "" {
params := strings.Split(query, "&")
for _, f := range params {
attr := strings.Split(f, "=")
......@@ -515,7 +512,7 @@ func readCmdFlags() error {
maxGroupByColsCount := flag.Int("max-group-by-cols-count", Config.MaxGroupByColsCount, "MaxGroupByColsCount, 单条 SQL 中 GroupBy 包含列的最大数量")
maxDistinctCount := flag.Int("max-distinct-count", Config.MaxDistinctCount, "MaxDistinctCount, 单条 SQL 中 Distinct 的最大数量")
maxIdxColsCount := flag.Int("max-index-cols-count", Config.MaxIdxColsCount, "MaxIdxColsCount, 复合索引中包含列的最大数量")
maxTextColsCount := flag.Int("max-texst-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即开启数据库保护模式,不计算散粒度")
maxQueryCost := flag.Int64("max-query-cost", Config.MaxQueryCost, "MaxQueryCost, last_query_cost 超过该值时将给予警告")
spaghettiQueryLength := flag.Int("spaghetti-query-length", Config.SpaghettiQueryLength, "SpaghettiQueryLength, SQL最大长度警告,超过该长度会给警告")
......
......@@ -222,24 +222,22 @@ func (db *Connector) ShowIndex(tableName string) (*TableIndexInfo, error) {
null := res.Result.Map("Null")
idxType := res.Result.Map("Index_type")
comment := res.Result.Map("Comment")
idxComment := res.Result.Map("Index_comment")
// 获取值
for _, row := range res.Rows {
value := TableIndexRow{
Table: row.Str(table),
NonUnique: row.Int(unique),
KeyName: row.Str(keyName),
SeqInIndex: row.Int(seq),
ColumnName: row.Str(cName),
Collation: row.Str(collation),
Cardinality: row.Int(cardinality),
SubPart: row.Int(subPart),
Packed: row.Int(packed),
Null: row.Str(null),
IndexType: row.Str(idxType),
Comment: row.Str(comment),
IndexComment: row.Str(idxComment),
Table: row.Str(table),
NonUnique: row.Int(unique),
KeyName: row.Str(keyName),
SeqInIndex: row.Int(seq),
ColumnName: row.Str(cName),
Collation: row.Str(collation),
Cardinality: row.Int(cardinality),
SubPart: row.Int(subPart),
Packed: row.Int(packed),
Null: row.Str(null),
IndexType: row.Str(idxType),
Comment: row.Str(comment),
}
tbIndex.IdxRows = append(tbIndex.IdxRows, value)
}
......
#!/bin/bash
NEEDED_COMMANDS="mysql docker git go govendor retool"
NEEDED_COMMANDS="docker git go govendor retool"
for cmd in ${NEEDED_COMMANDS} ; do
if ! command -v "${cmd}" &> /dev/null ; then
......@@ -11,10 +11,6 @@ for cmd in ${NEEDED_COMMANDS} ; do
fi
done
# MySQL client
## Mac OS: brew install mysql
## Ubuntu: apt-get install mysql-client
# Docker
## https://www.docker.com
......
......@@ -17,7 +17,6 @@ package ast
import (
"io"
"strings"
"github.com/pingcap/parser/model"
"github.com/pingcap/parser/types"
......@@ -27,7 +26,7 @@ import (
// Interfaces embed Node should have 'Node' name suffix.
type Node interface {
// Restore returns the sql text from ast tree
Restore(sb *strings.Builder) error
Restore(ctx *RestoreCtx) error
// Accept accepts Visitor to visit itself.
// The returned node should replace original node.
// ok returns false to stop visiting.
......
......@@ -14,8 +14,6 @@
package ast
import (
"strings"
"github.com/pingcap/errors"
"github.com/pingcap/parser/auth"
"github.com/pingcap/parser/model"
......@@ -65,15 +63,17 @@ type DatabaseOption struct {
Value string
}
// Restore implements Recoverable interface.
func (n *DatabaseOption) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *DatabaseOption) Restore(ctx *RestoreCtx) error {
switch n.Tp {
case DatabaseOptionCharset:
sb.WriteString("CHARACTER SET = ")
sb.WriteString(n.Value)
ctx.WriteKeyWord("CHARACTER SET")
ctx.WritePlain(" = ")
ctx.WritePlain(n.Value)
case DatabaseOptionCollate:
sb.WriteString("COLLATE = ")
sb.WriteString(n.Value)
ctx.WriteKeyWord("COLLATE")
ctx.WritePlain(" = ")
ctx.WritePlain(n.Value)
default:
return errors.Errorf("invalid DatabaseOptionType: %d", n.Tp)
}
......@@ -90,16 +90,16 @@ type CreateDatabaseStmt struct {
Options []*DatabaseOption
}
// Restore implements Recoverable interface.
func (n *CreateDatabaseStmt) Restore(sb *strings.Builder) error {
sb.WriteString("CREATE DATABASE ")
// Restore implements Node interface.
func (n *CreateDatabaseStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("CREATE DATABASE ")
if n.IfNotExists {
sb.WriteString("IF NOT EXISTS ")
ctx.WriteKeyWord("IF NOT EXISTS ")
}
WriteName(sb, n.Name)
ctx.WriteName(n.Name)
for _, option := range n.Options {
sb.WriteString(" ")
err := option.Restore(sb)
ctx.WritePlain(" ")
err := option.Restore(ctx)
if err != nil {
return errors.Trace(err)
}
......@@ -126,13 +126,13 @@ type DropDatabaseStmt struct {
Name string
}
// Restore implements Recoverable interface.
func (n *DropDatabaseStmt) Restore(sb *strings.Builder) error {
sb.WriteString("DROP DATABASE ")
// Restore implements Node interface.
func (n *DropDatabaseStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("DROP DATABASE ")
if n.IfExists {
sb.WriteString("IF EXISTS ")
ctx.WriteKeyWord("IF EXISTS ")
}
WriteName(sb, n.Name)
ctx.WriteName(n.Name)
return nil
}
......@@ -154,8 +154,8 @@ type IndexColName struct {
Length int
}
// Restore implements Recoverable interface.
func (n *IndexColName) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *IndexColName) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -185,8 +185,8 @@ type ReferenceDef struct {
OnUpdate *OnUpdateOpt
}
// Restore implements Recoverable interface.
func (n *ReferenceDef) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ReferenceDef) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -255,8 +255,8 @@ type OnDeleteOpt struct {
ReferOpt ReferOptionType
}
// Restore implements Recoverable interface.
func (n *OnDeleteOpt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *OnDeleteOpt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -276,8 +276,8 @@ type OnUpdateOpt struct {
ReferOpt ReferOptionType
}
// Restore implements Recoverable interface.
func (n *OnUpdateOpt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *OnUpdateOpt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -325,8 +325,8 @@ type ColumnOption struct {
Refer *ReferenceDef
}
// Restore implements Recoverable interface.
func (n *ColumnOption) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ColumnOption) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -361,9 +361,32 @@ type IndexOption struct {
Comment string
}
// Restore implements Recoverable interface.
func (n *IndexOption) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *IndexOption) Restore(ctx *RestoreCtx) error {
hasPrevOption := false
if n.KeyBlockSize > 0 {
ctx.WriteKeyWord("KEY_BLOCK_SIZE")
ctx.WritePlainf("=%d", n.KeyBlockSize)
hasPrevOption = true
}
if n.Tp != model.IndexTypeInvalid {
if hasPrevOption {
ctx.WritePlain(" ")
}
ctx.WriteKeyWord("USING ")
ctx.WritePlain(n.Tp.String())
hasPrevOption = true
}
if n.Comment != "" {
if hasPrevOption {
ctx.WritePlain(" ")
}
ctx.WriteKeyWord("COMMENT ")
ctx.WriteString(n.Comment)
}
return nil
}
// Accept implements Node Accept interface.
......@@ -406,8 +429,8 @@ type Constraint struct {
Option *IndexOption // Index Options
}
// Restore implements Recoverable interface.
func (n *Constraint) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *Constraint) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -451,8 +474,8 @@ type ColumnDef struct {
Options []*ColumnOption
}
// Restore implements Recoverable interface.
func (n *ColumnDef) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ColumnDef) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -494,8 +517,8 @@ type CreateTableStmt struct {
Select ResultSetNode
}
// Restore implements Recoverable interface.
func (n *CreateTableStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *CreateTableStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -553,8 +576,8 @@ type DropTableStmt struct {
IsView bool
}
// Restore implements Recoverable interface.
func (n *DropTableStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *DropTableStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -588,8 +611,8 @@ type RenameTableStmt struct {
TableToTables []*TableToTable
}
// Restore implements Recoverable interface.
func (n *RenameTableStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *RenameTableStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -629,8 +652,8 @@ type TableToTable struct {
NewTable *TableName
}
// Restore implements Recoverable interface.
func (n *TableToTable) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *TableToTable) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -669,8 +692,8 @@ type CreateViewStmt struct {
CheckOption model.ViewCheckOption
}
// Restore implements Recoverable interface.
func (n *CreateViewStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *CreateViewStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -706,8 +729,8 @@ type CreateIndexStmt struct {
IndexOption *IndexOption
}
// Restore implements Recoverable interface.
func (n *CreateIndexStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *CreateIndexStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -750,9 +773,20 @@ type DropIndexStmt struct {
Table *TableName
}
// Restore implements Recoverable interface.
func (n *DropIndexStmt) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *DropIndexStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("DROP INDEX ")
if n.IfExists {
ctx.WriteKeyWord("IF EXISTS ")
}
ctx.WriteName(n.IndexName)
ctx.WriteKeyWord(" ON ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while add index")
}
return nil
}
// Accept implements Node Accept interface.
......@@ -843,8 +877,8 @@ type ColumnPosition struct {
RelativeColumn *ColumnName
}
// Restore implements Recoverable interface.
func (n *ColumnPosition) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ColumnPosition) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -925,8 +959,8 @@ type AlterTableSpec struct {
Num uint64
}
// Restore implements Recoverable interface.
func (n *AlterTableSpec) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *AlterTableSpec) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -984,8 +1018,8 @@ type AlterTableStmt struct {
Specs []*AlterTableSpec
}
// Restore implements Recoverable interface.
func (n *AlterTableStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *AlterTableStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -1019,8 +1053,8 @@ type TruncateTableStmt struct {
Table *TableName
}
// Restore implements Recoverable interface.
func (n *TruncateTableStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *TruncateTableStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......
......@@ -14,8 +14,6 @@
package ast
import (
"strings"
"github.com/pingcap/errors"
"github.com/pingcap/parser/auth"
"github.com/pingcap/parser/model"
......@@ -85,8 +83,8 @@ type Join struct {
StraightJoin bool
}
// Restore implements Recoverable interface.
func (n *Join) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *Join) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -133,16 +131,16 @@ type TableName struct {
IndexHints []*IndexHint
}
// Restore implements Recoverable interface.
func (n *TableName) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *TableName) Restore(ctx *RestoreCtx) error {
if n.Schema.String() != "" {
WriteName(sb, n.Schema.String())
sb.WriteString(".")
ctx.WriteName(n.Schema.String())
ctx.WritePlain(".")
}
WriteName(sb, n.Name.String())
ctx.WriteName(n.Name.String())
for _, value := range n.IndexHints {
sb.WriteString(" ")
if err := value.Restore(sb); err != nil {
ctx.WritePlain(" ")
if err := value.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing IndexHints")
}
}
......@@ -178,7 +176,7 @@ type IndexHint struct {
}
// IndexHint Restore (The const field uses switch to facilitate understanding)
func (n *IndexHint) Restore(sb *strings.Builder) error {
func (n *IndexHint) Restore(ctx *RestoreCtx) error {
indexHintType := ""
switch n.HintType {
case 1:
......@@ -204,17 +202,16 @@ func (n *IndexHint) Restore(sb *strings.Builder) error {
default: // Prevent accidents
return errors.New("IndexHintScope has an error while matching")
}
sb.WriteString(indexHintType)
sb.WriteString(indexHintScope)
sb.WriteString(" (")
ctx.WriteKeyWord(indexHintType)
ctx.WriteKeyWord(indexHintScope)
ctx.WritePlain(" (")
for i, value := range n.IndexNames {
if i > 0 {
sb.WriteString(", ")
ctx.WritePlain(", ")
}
WriteName(sb, value.O)
ctx.WriteName(value.O)
}
sb.WriteString(")")
ctx.WritePlain(")")
return nil
}
......@@ -235,8 +232,8 @@ type DeleteTableList struct {
Tables []*TableName
}
// Restore implements Recoverable interface.
func (n *DeleteTableList) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *DeleteTableList) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -266,8 +263,8 @@ type OnCondition struct {
Expr ExprNode
}
// Restore implements Recoverable interface.
func (n *OnCondition) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *OnCondition) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -298,8 +295,8 @@ type TableSource struct {
AsName model.CIStr
}
// Restore implements Recoverable interface.
func (n *TableSource) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *TableSource) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -349,8 +346,8 @@ type WildCardField struct {
Schema model.CIStr
}
// Restore implements Recoverable interface.
func (n *WildCardField) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *WildCardField) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -384,8 +381,8 @@ type SelectField struct {
Auxiliary bool
}
// Restore implements Recoverable interface.
func (n *SelectField) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *SelectField) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -413,8 +410,8 @@ type FieldList struct {
Fields []*SelectField
}
// Restore implements Recoverable interface.
func (n *FieldList) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *FieldList) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -442,8 +439,8 @@ type TableRefsClause struct {
TableRefs *Join
}
// Restore implements Recoverable interface.
func (n *TableRefsClause) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *TableRefsClause) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -470,8 +467,8 @@ type ByItem struct {
Desc bool
}
// Restore implements Recoverable interface.
func (n *ByItem) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ByItem) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -496,8 +493,8 @@ type GroupByClause struct {
Items []*ByItem
}
// Restore implements Recoverable interface.
func (n *GroupByClause) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *GroupByClause) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -524,8 +521,8 @@ type HavingClause struct {
Expr ExprNode
}
// Restore implements Recoverable interface.
func (n *HavingClause) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *HavingClause) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -551,8 +548,8 @@ type OrderByClause struct {
ForUnion bool
}
// Restore implements Recoverable interface.
func (n *OrderByClause) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *OrderByClause) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -609,8 +606,8 @@ type SelectStmt struct {
IsInBraces bool
}
// Restore implements Recoverable interface.
func (n *SelectStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -708,8 +705,8 @@ type UnionSelectList struct {
Selects []*SelectStmt
}
// Restore implements Recoverable interface.
func (n *UnionSelectList) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *UnionSelectList) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -741,8 +738,8 @@ type UnionStmt struct {
Limit *Limit
}
// Restore implements Recoverable interface.
func (n *UnionStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *UnionStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -786,8 +783,8 @@ type Assignment struct {
Expr ExprNode
}
// Restore implements Recoverable interface.
func (n *Assignment) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *Assignment) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -825,8 +822,8 @@ type LoadDataStmt struct {
IgnoreLines uint64
}
// Restore implements Recoverable interface.
func (n *LoadDataStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *LoadDataStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -883,8 +880,8 @@ type InsertStmt struct {
Select ResultSetNode
}
// Restore implements Recoverable interface.
func (n *InsertStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *InsertStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -964,8 +961,8 @@ type DeleteStmt struct {
TableHints []*TableOptimizerHint
}
// Restore implements Recoverable interface.
func (n *DeleteStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *DeleteStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -1029,8 +1026,8 @@ type UpdateStmt struct {
TableHints []*TableOptimizerHint
}
// Restore implements Recoverable interface.
func (n *UpdateStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *UpdateStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -1085,9 +1082,19 @@ type Limit struct {
Offset ExprNode
}
// Restore implements Recoverable interface.
func (n *Limit) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *Limit) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("LIMIT ")
if n.Offset != nil {
if err := n.Offset.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore Limit.Offset")
}
ctx.WritePlain(",")
}
if err := n.Count.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore Limit.Count")
}
return nil
}
// Accept implements Node Accept interface.
......@@ -1170,8 +1177,8 @@ type ShowStmt struct {
Where ExprNode
}
// Restore implements Recoverable interface.
func (n *ShowStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ShowStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -1235,8 +1242,8 @@ type WindowSpec struct {
Frame *FrameClause
}
// Restore implements Recoverable interface.
func (n *WindowSpec) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *WindowSpec) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -1278,8 +1285,8 @@ type PartitionByClause struct {
Items []*ByItem
}
// Restore implements Recoverable interface.
func (n *PartitionByClause) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *PartitionByClause) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -1319,8 +1326,8 @@ type FrameClause struct {
Extent FrameExtent
}
// Restore implements Recoverable interface.
func (n *FrameClause) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *FrameClause) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -1372,8 +1379,8 @@ type FrameBound struct {
Unit ExprNode
}
// Restore implements Recoverable interface.
func (n *FrameBound) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *FrameBound) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......
......@@ -79,9 +79,24 @@ type BetweenExpr struct {
Not bool
}
// Restore implements Recoverable interface.
func (n *BetweenExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *BetweenExpr) Restore(ctx *RestoreCtx) error {
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore BetweenExpr.Expr")
}
if n.Not {
ctx.WriteKeyWord(" NOT BETWEEN ")
} else {
ctx.WriteKeyWord(" BETWEEN ")
}
if err := n.Left.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore BetweenExpr.Left")
}
ctx.WriteKeyWord(" AND ")
if err := n.Right.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore BetweenExpr.Right ")
}
return nil
}
// Format the ExprNode into a Writer.
......@@ -137,9 +152,19 @@ type BinaryOperationExpr struct {
R ExprNode
}
// Restore implements Recoverable interface.
func (n *BinaryOperationExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *BinaryOperationExpr) Restore(ctx *RestoreCtx) error {
if err := n.L.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred when restore BinaryOperationExpr.L")
}
if err := n.Op.Restore(ctx.In); err != nil {
return errors.Annotate(err, "An error occurred when restore BinaryOperationExpr.Op")
}
if err := n.R.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred when restore BinaryOperationExpr.R")
}
return nil
}
// Format the ExprNode into a Writer.
......@@ -183,9 +208,17 @@ type WhenClause struct {
Result ExprNode
}
// Restore implements Recoverable interface.
func (n *WhenClause) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *WhenClause) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("WHEN ")
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore WhenClauses.Expr")
}
ctx.WriteKeyWord(" THEN ")
if err := n.Result.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore WhenClauses.Result")
}
return nil
}
// Accept implements Node Accept interface.
......@@ -221,9 +254,30 @@ type CaseExpr struct {
ElseClause ExprNode
}
// Restore implements Recoverable interface.
func (n *CaseExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *CaseExpr) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("CASE")
if n.Value != nil {
ctx.WritePlain(" ")
if err := n.Value.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore CaseExpr.Value")
}
}
for _, clause := range n.WhenClauses {
ctx.WritePlain(" ")
if err := clause.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore CaseExpr.WhenClauses")
}
}
if n.ElseClause != nil {
ctx.WriteKeyWord(" ELSE ")
if err := n.ElseClause.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore CaseExpr.ElseClause")
}
}
ctx.WriteKeyWord(" END")
return nil
}
// Format the ExprNode into a Writer.
......@@ -290,8 +344,8 @@ type SubqueryExpr struct {
Exists bool
}
// Restore implements Recoverable interface.
func (n *SubqueryExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *SubqueryExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -331,8 +385,8 @@ type CompareSubqueryExpr struct {
All bool
}
// Restore implements Recoverable interface.
func (n *CompareSubqueryExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *CompareSubqueryExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -369,17 +423,17 @@ type ColumnName struct {
Name model.CIStr
}
// Restore implements Recoverable interface.
func (n *ColumnName) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ColumnName) Restore(ctx *RestoreCtx) error {
if n.Schema.O != "" {
WriteName(sb, n.Schema.O)
sb.WriteString(".")
ctx.WriteName(n.Schema.O)
ctx.WritePlain(".")
}
if n.Table.O != "" {
WriteName(sb, n.Table.O)
sb.WriteString(".")
ctx.WriteName(n.Table.O)
ctx.WritePlain(".")
}
WriteName(sb, n.Name.O)
ctx.WriteName(n.Name.O)
return nil
}
......@@ -431,10 +485,9 @@ type ColumnNameExpr struct {
Refer *ResultField
}
// Restore implements Recoverable interface.
func (n *ColumnNameExpr) Restore(sb *strings.Builder) error {
err := n.Name.Restore(sb)
if err != nil {
// Restore implements Node interface.
func (n *ColumnNameExpr) Restore(ctx *RestoreCtx) error {
if err := n.Name.Restore(ctx); err != nil {
return errors.Trace(err)
}
return nil
......@@ -468,9 +521,17 @@ type DefaultExpr struct {
Name *ColumnName
}
// Restore implements Recoverable interface.
func (n *DefaultExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *DefaultExpr) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("DEFAULT")
if n.Name != nil {
ctx.WritePlain("(")
if err := n.Name.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore DefaultExpr.Name")
}
ctx.WritePlain(")")
}
return nil
}
// Format the ExprNode into a Writer.
......@@ -505,8 +566,8 @@ type ExistsSubqueryExpr struct {
Not bool
}
// Restore implements Recoverable interface.
func (n *ExistsSubqueryExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ExistsSubqueryExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -543,8 +604,8 @@ type PatternInExpr struct {
Sel ExprNode
}
// Restore implements Recoverable interface.
func (n *PatternInExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *PatternInExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -603,9 +664,16 @@ type IsNullExpr struct {
Not bool
}
// Restore implements Recoverable interface.
func (n *IsNullExpr) Restore(sb *strings.Builder) error {
n.Format(sb)
// Restore implements Node interface.
func (n *IsNullExpr) Restore(ctx *RestoreCtx) error {
if err := n.Expr.Restore(ctx); err != nil {
return errors.Trace(err)
}
if n.Not {
ctx.WriteKeyWord(" IS NOT NULL")
} else {
ctx.WriteKeyWord(" IS NULL")
}
return nil
}
......@@ -645,9 +713,22 @@ type IsTruthExpr struct {
True int64
}
// Restore implements Recoverable interface.
func (n *IsTruthExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *IsTruthExpr) Restore(ctx *RestoreCtx) error {
if err := n.Expr.Restore(ctx); err != nil {
return errors.Trace(err)
}
if n.Not {
ctx.WriteKeyWord(" IS NOT")
} else {
ctx.WriteKeyWord(" IS")
}
if n.True > 0 {
ctx.WriteKeyWord(" TRUE")
} else {
ctx.WriteKeyWord(" FALSE")
}
return nil
}
// Format the ExprNode into a Writer.
......@@ -696,9 +777,23 @@ type PatternLikeExpr struct {
PatTypes []byte
}
// Restore implements Recoverable interface.
func (n *PatternLikeExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *PatternLikeExpr) Restore(ctx *RestoreCtx) error {
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore PatternLikeExpr.Expr")
}
if n.Not {
ctx.WriteKeyWord(" NOT LIKE ")
} else {
ctx.WriteKeyWord(" LIKE ")
}
if err := n.Pattern.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore PatternLikeExpr.Pattern")
}
return nil
}
// Format the ExprNode into a Writer.
......@@ -754,9 +849,14 @@ type ParenthesesExpr struct {
Expr ExprNode
}
// Restore implements Recoverable interface.
func (n *ParenthesesExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *ParenthesesExpr) Restore(ctx *RestoreCtx) error {
ctx.WritePlain("(")
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred when restore ParenthesesExpr.Expr")
}
ctx.WritePlain(")")
return nil
}
// Format the ExprNode into a Writer.
......@@ -796,8 +896,8 @@ type PositionExpr struct {
Refer *ResultField
}
// Restore implements Recoverable interface.
func (n *PositionExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *PositionExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -839,9 +939,23 @@ type PatternRegexpExpr struct {
Sexpr *string
}
// Restore implements Recoverable interface.
func (n *PatternRegexpExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *PatternRegexpExpr) Restore(ctx *RestoreCtx) error {
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore PatternRegexpExpr.Expr")
}
if n.Not {
ctx.WriteKeyWord(" NOT REGEXP ")
} else {
ctx.WriteKeyWord(" REGEXP ")
}
if err := n.Pattern.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore PatternRegexpExpr.Pattern")
}
return nil
}
// Format the ExprNode into a Writer.
......@@ -883,8 +997,8 @@ type RowExpr struct {
Values []ExprNode
}
// Restore implements Recoverable interface.
func (n *RowExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *RowExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -919,9 +1033,14 @@ type UnaryOperationExpr struct {
V ExprNode
}
// Restore implements Recoverable interface.
func (n *UnaryOperationExpr) Restore(sb *strings.Builder) error {
n.Format(sb)
// Restore implements Node interface.
func (n *UnaryOperationExpr) Restore(ctx *RestoreCtx) error {
if err := n.Op.Restore(ctx.In); err != nil {
return errors.Trace(err)
}
if err := n.V.Restore(ctx); err != nil {
return errors.Trace(err)
}
return nil
}
......@@ -953,9 +1072,16 @@ type ValuesExpr struct {
Column *ColumnNameExpr
}
// Restore implements Recoverable interface.
func (n *ValuesExpr) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *ValuesExpr) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("VALUES")
ctx.WritePlain("(")
if err := n.Column.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore ValuesExpr.Column")
}
ctx.WritePlain(")")
return nil
}
// Format the ExprNode into a Writer.
......@@ -995,8 +1121,8 @@ type VariableExpr struct {
Value ExprNode
}
// Restore implements Recoverable interface.
func (n *VariableExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *VariableExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -1029,8 +1155,8 @@ type MaxValueExpr struct {
exprNode
}
// Restore implements Recoverable interface.
func (n *MaxValueExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *MaxValueExpr) Restore(ctx *RestoreCtx) error {
panic("Not implemented")
}
......
......@@ -16,7 +16,6 @@ package ast
import (
"fmt"
"io"
"strings"
"github.com/pingcap/errors"
"github.com/pingcap/parser/model"
......@@ -329,8 +328,8 @@ type FuncCallExpr struct {
Args []ExprNode
}
// Restore implements Recoverable interface.
func (n *FuncCallExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *FuncCallExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -406,8 +405,8 @@ type FuncCastExpr struct {
FunctionType CastFunctionType
}
// Restore implements Recoverable interface.
func (n *FuncCastExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *FuncCastExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -519,8 +518,8 @@ type AggregateFuncExpr struct {
Distinct bool
}
// Restore implements Recoverable interface.
func (n *AggregateFuncExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *AggregateFuncExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -592,8 +591,8 @@ type WindowFuncExpr struct {
Spec WindowSpec
}
// Restore implements Recoverable interface.
func (n *WindowFuncExpr) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *WindowFuncExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......
......@@ -98,8 +98,8 @@ type TraceStmt struct {
Format string
}
// Restore implements Recoverable interface.
func (n *TraceStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *TraceStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -129,8 +129,8 @@ type ExplainStmt struct {
Analyze bool
}
// Restore implements Recoverable interface.
func (n *ExplainStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ExplainStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -160,8 +160,8 @@ type PrepareStmt struct {
SQLVar *VariableExpr
}
// Restore implements Recoverable interface.
func (n *PrepareStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *PrepareStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -190,8 +190,8 @@ type DeallocateStmt struct {
Name string
}
// Restore implements Recoverable interface.
func (n *DeallocateStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *DeallocateStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -223,8 +223,8 @@ type ExecuteStmt struct {
ExecID uint32
}
// Restore implements Recoverable interface.
func (n *ExecuteStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *ExecuteStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -251,8 +251,8 @@ type BeginStmt struct {
stmtNode
}
// Restore implements Recoverable interface.
func (n *BeginStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *BeginStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -274,8 +274,8 @@ type BinlogStmt struct {
Str string
}
// Restore implements Recoverable interface.
func (n *BinlogStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *BinlogStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -295,8 +295,8 @@ type CommitStmt struct {
stmtNode
}
// Restore implements Recoverable interface.
func (n *CommitStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *CommitStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -316,8 +316,8 @@ type RollbackStmt struct {
stmtNode
}
// Restore implements Recoverable interface.
func (n *RollbackStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *RollbackStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -339,10 +339,10 @@ type UseStmt struct {
DBName string
}
// Restore implements Recoverable interface.
func (n *UseStmt) Restore(sb *strings.Builder) error {
sb.WriteString("USE ")
WriteName(sb, n.DBName)
// Restore implements Node interface.
func (n *UseStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("USE ")
ctx.WriteName(n.DBName)
return nil
}
......@@ -377,8 +377,8 @@ type VariableAssignment struct {
ExtendValue ValueExpr
}
// Restore implements Recoverable interface.
func (n *VariableAssignment) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *VariableAssignment) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -418,8 +418,8 @@ type FlushStmt struct {
ReadLock bool
}
// Restore implements Recoverable interface.
func (n *FlushStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *FlushStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -453,8 +453,8 @@ type KillStmt struct {
TiDBExtension bool
}
// Restore implements Recoverable interface.
func (n *KillStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *KillStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -475,8 +475,8 @@ type SetStmt struct {
Variables []*VariableAssignment
}
// Restore implements Recoverable interface.
func (n *SetStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *SetStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -527,8 +527,8 @@ type SetPwdStmt struct {
Password string
}
// Restore implements Recoverable interface.
func (n *SetPwdStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *SetPwdStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -595,8 +595,8 @@ type CreateUserStmt struct {
Specs []*UserSpec
}
// Restore implements Recoverable interface.
func (n *CreateUserStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *CreateUserStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -631,8 +631,8 @@ type AlterUserStmt struct {
Specs []*UserSpec
}
// Restore implements Recoverable interface.
func (n *AlterUserStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *AlterUserStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -666,8 +666,8 @@ type DropUserStmt struct {
UserList []*auth.UserIdentity
}
// Restore implements Recoverable interface.
func (n *DropUserStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *DropUserStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -688,8 +688,8 @@ type DoStmt struct {
Exprs []ExprNode
}
// Restore implements Recoverable interface.
func (n *DoStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *DoStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -780,8 +780,8 @@ type AdminStmt struct {
ShowSlow *ShowSlow
}
// Restore implements Recoverable interface.
func (n *AdminStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *AdminStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -812,8 +812,8 @@ type PrivElem struct {
Cols []*ColumnName
}
// Restore implements Recoverable interface.
func (n *PrivElem) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *PrivElem) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -875,8 +875,8 @@ type RevokeStmt struct {
Users []*UserSpec
}
// Restore implements Recoverable interface.
func (n *RevokeStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *RevokeStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -908,8 +908,8 @@ type GrantStmt struct {
WithGrant bool
}
// Restore implements Recoverable interface.
func (n *GrantStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *GrantStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -978,8 +978,8 @@ type TableOptimizerHint struct {
MaxExecutionTime uint64
}
// Restore implements Recoverable interface.
func (n *TableOptimizerHint) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *TableOptimizerHint) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......
......@@ -14,8 +14,6 @@
package ast
import (
"strings"
"github.com/pingcap/errors"
"github.com/pingcap/parser/model"
)
......@@ -39,8 +37,8 @@ type AnalyzeTableStmt struct {
IndexFlag bool
}
// Restore implements Recoverable interface.
func (n *AnalyzeTableStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *AnalyzeTableStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......@@ -68,9 +66,14 @@ type DropStatsStmt struct {
Table *TableName
}
// Restore implements Recoverable interface.
func (n *DropStatsStmt) Restore(sb *strings.Builder) error {
return errors.New("Not implemented")
// Restore implements Node interface.
func (n *DropStatsStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("DROP STATS ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while add table")
}
return nil
}
// Accept implements Node Accept interface.
......@@ -95,8 +98,8 @@ type LoadStatsStmt struct {
Path string
}
// Restore implements Recoverable interface.
func (n *LoadStatsStmt) Restore(sb *strings.Builder) error {
// Restore implements Node interface.
func (n *LoadStatsStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
......
......@@ -13,7 +13,11 @@
package ast
import "strings"
import (
"fmt"
"io"
"strings"
)
// IsReadOnly checks whether the input ast is readOnly.
func IsReadOnly(node Node) bool {
......@@ -62,14 +66,150 @@ func (checker *readOnlyChecker) Leave(in Node) (out Node, ok bool) {
return in, checker.readOnly
}
// WriteName append escaped `name` with back quote to `sb`.
func WriteName(sb *strings.Builder, name string) {
sb.WriteString("`")
sb.WriteString(EscapeName(name))
sb.WriteString("`")
//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
}
// NewRestoreCtx returns a new `RestoreCtx`.
func NewRestoreCtx(flags RestoreFlags, in io.Writer) *RestoreCtx {
return &RestoreCtx{flags, in}
}
// 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)
}
// EscapeName escape the `name`
func EscapeName(name string) string {
return strings.Replace(name, "`", "``", -1)
// 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,11 +7,10 @@ require (
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a
github.com/pingcap/check v0.0.0-20171206051426-1c287c953996
github.com/pingcap/check v0.0.0-20181213055612-5c2b07721bdb
github.com/pingcap/errors v0.11.0
github.com/pingcap/tidb v0.0.0-20181203021530-741adcee43e2
github.com/pingcap/tidb v0.0.0-20181217070741-096bb68e6bef
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323
github.com/sirupsen/logrus v1.2.0
golang.org/x/net v0.0.0-20181029044818-c44066c5c816
golang.org/x/text v0.3.0
)
......@@ -38,6 +38,7 @@ type Scanner struct {
buf bytes.Buffer
errs []error
warns []error
stmtStartPos int
// For scanning such kind of comment: /*! MySQL-specific code */ or /*+ optimizer hint */
......@@ -88,9 +89,9 @@ func (s *optimizerHintScanner) scan() (tok int, pos Pos, lit string) {
return
}
// Errors returns the errors during a scan.
func (s *Scanner) Errors() []error {
return s.errs
// Errors returns the errors and warns during a scan.
func (s *Scanner) Errors() (warns []error, errs []error) {
return s.warns, s.errs
}
// reset resets the sql string to be scanned.
......@@ -98,6 +99,7 @@ func (s *Scanner) reset(sql string) {
s.r = reader{s: sql, p: Pos{Line: 1}}
s.buf.Reset()
s.errs = s.errs[:0]
s.warns = s.warns[:0]
s.stmtStartPos = 0
s.specialComment = nil
}
......
......@@ -250,6 +250,14 @@ func (t *TableInfo) GetAutoIncrementColInfo() *ColumnInfo {
return nil
}
func (t *TableInfo) IsAutoIncColUnsigned() bool {
col := t.GetAutoIncrementColInfo()
if col == nil {
return false
}
return mysql.HasUnsignedFlag(col.Flag)
}
// Cols returns the columns of the table in public state.
func (t *TableInfo) Cols() []*ColumnInfo {
publicColumns := make([]*ColumnInfo, len(t.Columns))
......
......@@ -16,6 +16,8 @@ package opcode
import (
"fmt"
"io"
"github.com/pingcap/errors"
)
// Op is opcode type.
......@@ -136,3 +138,12 @@ var opsLiteral = map[Op]string{
func (o Op) Format(w io.Writer) {
fmt.Fprintf(w, "%s", opsLiteral[o])
}
// Restore the Op into a Writer
func (o Op) Restore(w io.Writer) error {
if v, ok := opsLiteral[o]; ok {
fmt.Fprint(w, v)
return nil
}
return errors.Errorf("Invalid opcode type %d during restoring AST to SQL text", o)
}
......@@ -2926,6 +2926,8 @@ IndexOptionList:
opt1.Comment = opt2.Comment
} else if opt2.Tp != 0 {
opt1.Tp = opt2.Tp
} else if opt2.KeyBlockSize > 0 {
opt1.KeyBlockSize = opt2.KeyBlockSize
}
$$ = opt1
}
......@@ -2936,8 +2938,7 @@ IndexOption:
"KEY_BLOCK_SIZE" EqOpt LengthNum
{
$$ = &ast.IndexOption{
// TODO bug should be fix here!
// KeyBlockSize: $1.(uint64),
KeyBlockSize: $3.(uint64),
}
}
| IndexType
......@@ -5190,6 +5191,11 @@ TableOptimizerHints:
{
$$ = $2
}
| hintBegin error hintEnd
{
yyerrok()
parser.lastErrorAsWarn()
}
HintTableList:
Identifier
......
......@@ -90,7 +90,7 @@ func New() *Parser {
// Parse parses a query string to raw ast.StmtNode.
// If charset or collation is "", default charset and collation will be used.
func (parser *Parser) Parse(sql, charset, collation string) ([]ast.StmtNode, error) {
func (parser *Parser) Parse(sql, charset, collation string) (stmt []ast.StmtNode, warns []error, err error) {
if charset == "" {
charset = mysql.DefaultCharset
}
......@@ -107,19 +107,33 @@ func (parser *Parser) Parse(sql, charset, collation string) ([]ast.StmtNode, err
l = &parser.lexer
yyParse(l, parser)
if len(l.Errors()) != 0 {
return nil, errors.Trace(l.Errors()[0])
warns, errs := l.Errors()
if len(warns) > 0 {
warns = append([]error(nil), warns...)
} else {
warns = nil
}
if len(errs) != 0 {
return nil, warns, errors.Trace(errs[0])
}
for _, stmt := range parser.result {
ast.SetFlag(stmt)
}
return parser.result, nil
return parser.result, warns, nil
}
func (parser *Parser) lastErrorAsWarn() {
if len(parser.lexer.errs) == 0 {
return
}
parser.lexer.warns = append(parser.lexer.warns, parser.lexer.errs[len(parser.lexer.errs)-1])
parser.lexer.errs = parser.lexer.errs[:len(parser.lexer.errs)-1]
}
// ParseOneStmt parses a query and returns an ast.StmtNode.
// The query must have one statement, otherwise ErrSyntax is returned.
func (parser *Parser) ParseOneStmt(sql, charset, collation string) (ast.StmtNode, error) {
stmts, err := parser.Parse(sql, charset, collation)
stmts, _, err := parser.Parse(sql, charset, collation)
if err != nil {
return nil, errors.Trace(err)
}
......
......@@ -65,8 +65,29 @@ type StatementContext struct {
// mu struct holds variables that change during execution.
mu struct {
sync.Mutex
affectedRows uint64
foundRows uint64
affectedRows uint64
foundRows uint64
/*
following variables are ported from 'COPY_INFO' struct of MySQL server source,
they are used to count rows for INSERT/REPLACE/UPDATE queries:
If a row is inserted then the copied variable is incremented.
If a row is updated by the INSERT ... ON DUPLICATE KEY UPDATE and the
new data differs from the old one then the copied and the updated
variables are incremented.
The touched variable is incremented if a row was touched by the update part
of the INSERT ... ON DUPLICATE KEY UPDATE no matter whether the row
was actually changed or not.
see https://github.com/mysql/mysql-server/blob/d2029238d6d9f648077664e4cdd611e231a6dc14/sql/sql_data_change.h#L60 for more details
*/
records uint64
updated uint64
copied uint64
touched uint64
message string
warnings []SQLWarn
histogramsNotLoad bool
execDetails execdetails.ExecDetails
......@@ -122,6 +143,81 @@ func (sc *StatementContext) AddFoundRows(rows uint64) {
sc.mu.Unlock()
}
// RecordRows is used to generate info message
func (sc *StatementContext) RecordRows() uint64 {
sc.mu.Lock()
rows := sc.mu.records
sc.mu.Unlock()
return rows
}
// AddRecordRows adds record rows.
func (sc *StatementContext) AddRecordRows(rows uint64) {
sc.mu.Lock()
sc.mu.records += rows
sc.mu.Unlock()
}
// UpdatedRows is used to generate info message
func (sc *StatementContext) UpdatedRows() uint64 {
sc.mu.Lock()
rows := sc.mu.updated
sc.mu.Unlock()
return rows
}
// AddUpdatedRows adds updated rows.
func (sc *StatementContext) AddUpdatedRows(rows uint64) {
sc.mu.Lock()
sc.mu.updated += rows
sc.mu.Unlock()
}
// CopiedRows is used to generate info message
func (sc *StatementContext) CopiedRows() uint64 {
sc.mu.Lock()
rows := sc.mu.copied
sc.mu.Unlock()
return rows
}
// AddCopiedRows adds copied rows.
func (sc *StatementContext) AddCopiedRows(rows uint64) {
sc.mu.Lock()
sc.mu.copied += rows
sc.mu.Unlock()
}
// TouchedRows is used to generate info message
func (sc *StatementContext) TouchedRows() uint64 {
sc.mu.Lock()
rows := sc.mu.touched
sc.mu.Unlock()
return rows
}
// AddTouchedRows adds touched rows.
func (sc *StatementContext) AddTouchedRows(rows uint64) {
sc.mu.Lock()
sc.mu.touched += rows
sc.mu.Unlock()
}
// GetMessage returns the extra message of the last executed command, if there is no message, it returns empty string
func (sc *StatementContext) GetMessage() string {
sc.mu.Lock()
msg := sc.mu.message
sc.mu.Unlock()
return msg
}
// SetMessage sets the info message generated by some commands
func (sc *StatementContext) SetMessage(msg string) {
sc.mu.Lock()
sc.mu.message = msg
sc.mu.Unlock()
}
// GetWarnings gets warnings.
func (sc *StatementContext) GetWarnings() []SQLWarn {
sc.mu.Lock()
......@@ -247,6 +343,11 @@ func (sc *StatementContext) ResetForRetry() {
sc.mu.Lock()
sc.mu.affectedRows = 0
sc.mu.foundRows = 0
sc.mu.records = 0
sc.mu.updated = 0
sc.mu.copied = 0
sc.mu.touched = 0
sc.mu.message = ""
sc.mu.warnings = nil
sc.mu.Unlock()
sc.TableIDs = sc.TableIDs[:0]
......
......@@ -128,12 +128,24 @@ func DefaultParamTypeForValue(value interface{}, tp *FieldType) {
tp.Decimal = UnspecifiedLength
default:
DefaultTypeForValue(value, tp)
if hasVariantFieldLength(tp) {
tp.Flen = UnspecifiedLength
}
if tp.Tp == mysql.TypeUnspecified {
tp.Tp = mysql.TypeVarString
}
}
}
func hasVariantFieldLength(tp *FieldType) bool {
switch tp.Tp {
case mysql.TypeLonglong, mysql.TypeVarString, mysql.TypeDouble, mysql.TypeBlob,
mysql.TypeBit, mysql.TypeDuration, mysql.TypeNewDecimal, mysql.TypeEnum, mysql.TypeSet:
return true
}
return false
}
// DefaultTypeForValue returns the default FieldType for the value.
func DefaultTypeForValue(value interface{}, tp *FieldType) {
switch x := value.(type) {
......
......@@ -17,7 +17,6 @@ import (
"fmt"
"io"
"strconv"
"strings"
"github.com/pingcap/errors"
"github.com/pingcap/parser/ast"
......@@ -69,11 +68,45 @@ type ValueExpr struct {
projectionOffset int
}
// Restore implements Recoverable interface.
func (n *ValueExpr) Restore(sb *strings.Builder) error {
err := n.format(sb)
if err != nil {
return errors.Trace(err)
// Restore implements Node interface.
func (n *ValueExpr) Restore(ctx *ast.RestoreCtx) error {
switch n.Kind() {
case types.KindNull:
ctx.WriteKeyWord("NULL")
case types.KindInt64:
if n.Type.Flag&mysql.IsBooleanFlag != 0 {
if n.GetInt64() > 0 {
ctx.WriteKeyWord("TRUE")
} else {
ctx.WriteKeyWord("FALSE")
}
} else {
ctx.WritePlain(strconv.FormatInt(n.GetInt64(), 10))
}
case types.KindUint64:
ctx.WritePlain(strconv.FormatUint(n.GetUint64(), 10))
case types.KindFloat32:
ctx.WritePlain(strconv.FormatFloat(n.GetFloat64(), 'e', -1, 32))
case types.KindFloat64:
ctx.WritePlain(strconv.FormatFloat(n.GetFloat64(), 'e', -1, 64))
case types.KindString, types.KindBytes:
ctx.WriteString(n.GetString())
case types.KindMysqlDecimal:
ctx.WritePlain(n.GetMysqlDecimal().String())
case types.KindBinaryLiteral:
if n.Type.Flag&mysql.UnsignedFlag != 0 {
ctx.WritePlainf("x'%x'", n.GetBytes())
} else {
ctx.WritePlain(n.GetBinaryLiteral().ToBitLiteralString(true))
}
case types.KindMysqlDuration, types.KindMysqlEnum,
types.KindMysqlBit, types.KindMysqlSet, types.KindMysqlTime,
types.KindInterface, types.KindMinNotNull, types.KindMaxValue,
types.KindRaw, types.KindMysqlJSON:
// TODO implement Restore function
return errors.New("Not implemented")
default:
return errors.New("can't format to string")
}
return nil
}
......@@ -84,7 +117,7 @@ func (n *ValueExpr) GetDatumString() string {
}
// Format the ExprNode into a Writer.
func (n *ValueExpr) format(w io.Writer) error {
func (n *ValueExpr) Format(w io.Writer) {
var s string
switch n.Kind() {
case types.KindNull:
......@@ -116,18 +149,9 @@ func (n *ValueExpr) format(w io.Writer) error {
s = n.GetBinaryLiteral().ToBitLiteralString(true)
}
default:
return errors.New("can't format to string")
}
fmt.Fprint(w, s)
return nil
}
// Format the ExprNode into a Writer.
func (n *ValueExpr) Format(w io.Writer) {
err := n.format(w)
if err != nil {
panic("Can't format to string")
}
fmt.Fprint(w, s)
}
// newValueExpr creates a ValueExpr with value, and sets default field type.
......@@ -170,9 +194,9 @@ type ParamMarkerExpr struct {
Order int
}
// Restore implements Recoverable interface.
func (n *ParamMarkerExpr) Restore(sb *strings.Builder) error {
sb.WriteString("?")
// Restore implements Node interface.
func (n *ParamMarkerExpr) Restore(ctx *ast.RestoreCtx) error {
ctx.WritePlain("?")
return nil
}
......
......@@ -105,106 +105,106 @@
"revisionTime": "2018-10-24T15:10:47Z"
},
{
"checksumSHA1": "xbV0lm0Qw8rFC82Dttxbf5ypBjA=",
"checksumSHA1": "cbEXJvrHC69taa1/5o/VlbkZy/g=",
"path": "github.com/pingcap/parser",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "RosqMriA/39ZEtqGgNU+bOfRBVc=",
"checksumSHA1": "9pUt2OvfLX8RpWG8gq4efJ33eQw=",
"path": "github.com/pingcap/parser/ast",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "skWGV4FNvD3vr+5olepaPPnylUw=",
"path": "github.com/pingcap/parser/auth",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "t4UHo966WzU9Z0IJkyGHRp0loOk=",
"path": "github.com/pingcap/parser/charset",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "SInoXbsRe0tnBwmatmtZYfSFbdk=",
"path": "github.com/pingcap/parser/format",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "reRV2qecd6NpB7tIW3JeK46K/sk=",
"checksumSHA1": "WZYTGDMnc1UfTdjdZoBbISsnpRY=",
"path": "github.com/pingcap/parser/model",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "QBa9yiMDQNl2cLLwqlRoNTpCPNg=",
"path": "github.com/pingcap/parser/mysql",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "oNBCSwJRykKuzIKgPCttatB9hAo=",
"checksumSHA1": "+O6CYIE0jT8pIDxWRP9FtKwFZjI=",
"path": "github.com/pingcap/parser/opcode",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "XvnUllvwMYd6HrMvMiKnn4cGN2M=",
"path": "github.com/pingcap/parser/terror",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "YoDiJ3sniNqxkP5X/BDkx6efteA=",
"path": "github.com/pingcap/parser/types",
"revision": "f20218bc290343c2752c4926c1bf4264343bbef4",
"revisionTime": "2018-12-12T04:21:31Z"
"revision": "651ad7108326d8bc3991288d749bb6c778135216",
"revisionTime": "2018-12-21T12:21:43Z"
},
{
"checksumSHA1": "fWqL/7jTYOiqDNmiUcQi3u45Hw0=",
"checksumSHA1": "+0bf1l46m6GG7JtACNufnZG0OUw=",
"path": "github.com/pingcap/tidb/sessionctx/stmtctx",
"revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
"revisionTime": "2018-12-13T09:52:39Z"
"revision": "ef7082da1cb2da10bee4ad70eb6ec9412bc96a90",
"revisionTime": "2018-12-22T14:19:41Z"
},
{
"checksumSHA1": "0CCq+3fAyaXs9XU+xWaRvbbtSOQ=",
"checksumSHA1": "kXyszfR2fQ6bHvuCCFlHRkt1mF0=",
"path": "github.com/pingcap/tidb/types",
"revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
"revisionTime": "2018-12-13T09:52:39Z"
"revision": "ef7082da1cb2da10bee4ad70eb6ec9412bc96a90",
"revisionTime": "2018-12-22T14:19:41Z"
},
{
"checksumSHA1": "DWVD7+ygtT66IQ+cqXmMJ5OVqUk=",
"path": "github.com/pingcap/tidb/types/json",
"revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
"revisionTime": "2018-12-13T09:52:39Z"
"revision": "ef7082da1cb2da10bee4ad70eb6ec9412bc96a90",
"revisionTime": "2018-12-22T14:19:41Z"
},
{
"checksumSHA1": "Zp5ME8OXNTmHnYTwJJUZlydN4/U=",
"checksumSHA1": "6vi/eCZXqNTa5eAUpxDZet4LPlY=",
"path": "github.com/pingcap/tidb/types/parser_driver",
"revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
"revisionTime": "2018-12-13T09:52:39Z"
"revision": "ef7082da1cb2da10bee4ad70eb6ec9412bc96a90",
"revisionTime": "2018-12-22T14:19:41Z"
},
{
"checksumSHA1": "s709bhSrG2Ec35406mGtrySid4s=",
"path": "github.com/pingcap/tidb/util/execdetails",
"revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
"revisionTime": "2018-12-13T09:52:39Z"
"revision": "ef7082da1cb2da10bee4ad70eb6ec9412bc96a90",
"revisionTime": "2018-12-22T14:19:41Z"
},
{
"checksumSHA1": "nUC7zVoAMNR2a+z2iGqHoN2AkFE=",
"path": "github.com/pingcap/tidb/util/hack",
"revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
"revisionTime": "2018-12-13T09:52:39Z"
"revision": "ef7082da1cb2da10bee4ad70eb6ec9412bc96a90",
"revisionTime": "2018-12-22T14:19:41Z"
},
{
"checksumSHA1": "xSyepiuqsoaaeDch7cXeumvVHKM=",
"path": "github.com/pingcap/tidb/util/memory",
"revision": "4ccd7456fcdc5e62e68f49a0a542cbcf88e38343",
"revisionTime": "2018-12-13T09:52:39Z"
"revision": "ef7082da1cb2da10bee4ad70eb6ec9412bc96a90",
"revisionTime": "2018-12-22T14:19:41Z"
},
{
"checksumSHA1": "SmYeIK/fIYXNu8IKxD6HOVQVTuU=",
......@@ -401,62 +401,62 @@
{
"checksumSHA1": "aKn1oKcY74N8TRLm3Ayt7Q4bbI4=",
"path": "vitess.io/vitess/go/bytes2",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "JVCEN4UGRmg3TofIBdzZMZ3G0Ww=",
"path": "vitess.io/vitess/go/hack",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "e1WJ7vCnVrlQQQlc6n/FewCDMso=",
"checksumSHA1": "F5pcGq+2W1FHEjgktTdKOE6W8mk=",
"path": "vitess.io/vitess/go/sqltypes",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "ntFIQYkBS51G6y+FEkjFW40+HOU=",
"path": "vitess.io/vitess/go/vt/log",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "tPQFPwbMdjuX0qjNl4Zl8zc37JQ=",
"path": "vitess.io/vitess/go/vt/proto/query",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "o0tR/c7lgr0pLkxk7CdvjiNDAKU=",
"path": "vitess.io/vitess/go/vt/proto/topodata",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "77UojBqi0yyeQvR70j7C3kcKclQ=",
"path": "vitess.io/vitess/go/vt/proto/vtgate",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "QpWGhoVDwM+8+sgYLI/YU+95iGU=",
"path": "vitess.io/vitess/go/vt/proto/vtrpc",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "lENrUY/YyxwYFHYN+21TBH92P3U=",
"checksumSHA1": "IDe+9Bn42lZVsuoYO/epdguiErk=",
"path": "vitess.io/vitess/go/vt/sqlparser",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
},
{
"checksumSHA1": "oF4XzuOzwvj1iduX/lYqNSyY/HM=",
"checksumSHA1": "Jx+gOh/kiBDSZxEIWHyYn9brjdo=",
"path": "vitess.io/vitess/go/vt/vterrors",
"revision": "eb5a5e0e435db30856600b794d8cff235fdf24b7",
"revisionTime": "2018-12-13T22:28:01Z"
"revision": "23f135ea6e773f3cc7c43c3d10c482b2221e9db9",
"revisionTime": "2018-12-22T19:18:51Z"
}
],
"rootPath": "github.com/XiaoMi/soar"
......
......@@ -16,7 +16,9 @@ limitations under the License.
package sqltypes
import "reflect"
import (
"vitess.io/vitess/go/vt/vterrors"
)
// QueryResponse represents a query response for ExecuteBatch.
type QueryResponse struct {
......@@ -34,7 +36,7 @@ func QueryResponsesEqual(r1, r2 []QueryResponse) bool {
if !r.QueryResult.Equal(r2[i].QueryResult) {
return false
}
if !reflect.DeepEqual(r.QueryError, r2[i].QueryError) {
if !vterrors.Equals(r.QueryError, r2[i].QueryError) {
return false
}
}
......
......@@ -3691,7 +3691,7 @@ mustEscape:
}
func compliantName(in string) string {
var buf bytes.Buffer
var buf strings.Builder
for i, c := range in {
if !isLetter(uint16(c)) {
if i == 0 || !isDigit(uint16(c)) {
......
Copyright (c) 2015, Dave Cheney <dave@cheney.net>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
/*
Copyright 2017 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreedto in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package vterrors provides helpers for propagating internal errors
// through the Vitess system (including across RPC boundaries) in a
// structured way.
package vterrors
/*
Vitess uses canonical error codes for error reporting. This is based
on years of industry experience with error reporting. This idea is
that errors should be classified into a small set of errors (10 or so)
with very specific meaning. Each error has a code, and a message. When
errors are passed around (even through RPCs), the code is
propagated. To handle errors, only the code should be looked at (and
not string-matching on the error message).
Vitess defines the error codes in /proto/vtrpc.proto. Along with an
RPCError message that can be used to transmit errors through RPCs, in
the message payloads. These codes match the names and numbers defined
by gRPC.
Vitess also defines a standardized error implementation that allows
you to build an error with an associated canonical code.
While sending an error through gRPC, these codes are transmitted
using gRPC's error propagation mechanism and decoded back to
the original code on the other end.
*/
package vterrors
/* This file is copied from https://github.com/pkg/errors/blob/v0.8.0/stack.go */
import (
"fmt"
"io"
"path"
"runtime"
"strings"
)
// Frame represents a program counter inside a stack frame.
type Frame uintptr
// pc returns the program counter for this frame;
// multiple frames may have the same PC value.
func (f Frame) pc() uintptr { return uintptr(f) - 1 }
// file returns the full path to the file that contains the
// function for this Frame's pc.
func (f Frame) file() string {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return "unknown"
}
file, _ := fn.FileLine(f.pc())
return file
}
// line returns the line number of source code of the
// function for this Frame's pc.
func (f Frame) line() int {
fn := runtime.FuncForPC(f.pc())
if fn == nil {
return 0
}
_, line := fn.FileLine(f.pc())
return line
}
// Format formats the frame according to the fmt.Formatter interface.
//
// %s source file
// %d source line
// %n function name
// %v equivalent to %s:%d
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+s path of source file relative to the compile time GOPATH
// %+v equivalent to %+s:%d
func (f Frame) Format(s fmt.State, verb rune) {
switch verb {
case 's':
switch {
case s.Flag('+'):
pc := f.pc()
fn := runtime.FuncForPC(pc)
if fn == nil {
io.WriteString(s, "unknown")
} else {
file, _ := fn.FileLine(pc)
fmt.Fprintf(s, "%s\n\t%s", fn.Name(), file)
}
default:
io.WriteString(s, path.Base(f.file()))
}
case 'd':
fmt.Fprintf(s, "%d", f.line())
case 'n':
name := runtime.FuncForPC(f.pc()).Name()
io.WriteString(s, funcname(name))
case 'v':
f.Format(s, 's')
io.WriteString(s, ":")
f.Format(s, 'd')
}
}
// StackTrace is stack of Frames from innermost (newest) to outermost (oldest).
type StackTrace []Frame
// Format format the stacktrace according to the fmt.Formatter interface.
//
// %s source file
// %d source line
// %n function name
// %v equivalent to %s:%d
//
// Format accepts flags that alter the printing of some verbs, as follows:
//
// %+s path of source file relative to the compile time GOPATH
// %+v equivalent to %+s:%d
func (st StackTrace) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case s.Flag('+'):
for _, f := range st {
fmt.Fprintf(s, "\n%+v", f)
}
case s.Flag('#'):
fmt.Fprintf(s, "%#v", []Frame(st))
default:
fmt.Fprintf(s, "%v", []Frame(st))
}
case 's':
fmt.Fprintf(s, "%s", []Frame(st))
}
}
// stack represents a stack of program counters.
type stack []uintptr
func (s *stack) Format(st fmt.State, verb rune) {
switch verb {
case 'v':
switch {
case st.Flag('+'):
for _, pc := range *s {
f := Frame(pc)
fmt.Fprintf(st, "\n%+v", f)
}
}
}
}
func (s *stack) StackTrace() StackTrace {
f := make([]Frame, len(*s))
for i := 0; i < len(f); i++ {
f[i] = Frame((*s)[i])
}
return f
}
func callers() *stack {
const depth = 32
var pcs [depth]uintptr
n := runtime.Callers(3, pcs[:])
var st stack = pcs[0:n]
return &st
}
// funcname removes the path prefix component of a function's name reported by func.Name().
func funcname(name string) string {
i := strings.LastIndex(name, "/")
name = name[i+1:]
i = strings.Index(name, ".")
return name[i+1:]
}
func trimGOPATH(name, file string) string {
// Here we want to get the source file path relative to the compile time
// GOPATH. As of Go 1.6.x there is no direct way to know the compiled
// GOPATH at runtime, but we can infer the number of path segments in the
// GOPATH. We note that fn.Name() returns the function name qualified by
// the import path, which does not include the GOPATH. Thus we can trim
// segments from the beginning of the file path until the number of path
// separators remaining is one more than the number of path separators in
// the function name. For example, given:
//
// GOPATH /home/user
// file /home/user/src/pkg/sub/file.go
// fn.Name() pkg/sub.Type.Method
//
// We want to produce:
//
// pkg/sub/file.go
//
// From this we can easily see that fn.Name() has one less path separator
// than our desired output. We count separators from the end of the file
// path until it finds two more than in the function name and then move
// one character forward to preserve the initial path segment without a
// leading separator.
const sep = "/"
goal := strings.Count(name, sep) + 2
i := len(file)
for n := 0; n < goal; n++ {
i = strings.LastIndex(file[:i], sep)
if i == -1 {
// not enough separators found, set i so that the slice expression
// below leaves file unmodified
i = -len(sep)
break
}
}
// get back to 0 or trim the leading separator
file = file[i+len(sep):]
return file
}
/*
Copyright 2017 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreedto in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package vterrors provides simple error handling primitives for Vitess
//
// In all Vitess code, errors should be propagated using vterrors.Wrapf()
// and not fmt.Errorf().
//
// New errors should be created using vterrors.New
//
// Vitess uses canonical error codes for error reporting. This is based
// on years of industry experience with error reporting. This idea is
// that errors should be classified into a small set of errors (10 or so)
// with very specific meaning. Each error has a code, and a message. When
// errors are passed around (even through RPCs), the code is
// propagated. To handle errors, only the code should be looked at (and
// not string-matching on the error message).
//
// Error codes are defined in /proto/vtrpc.proto. Along with an
// RPCError message that can be used to transmit errors through RPCs, in
// the message payloads. These codes match the names and numbers defined
// by gRPC.
//
// A standardized error implementation that allows you to build an error
// with an associated canonical code is also defined.
// While sending an error through gRPC, these codes are transmitted
// using gRPC's error propagation mechanism and decoded back to
// the original code on the other end.
//
// Retrieving the cause of an error
//
// Using vterrors.Wrap constructs a stack of errors, adding context to the
// preceding error. Depending on the nature of the error it may be necessary
// to reverse the operation of errors.Wrap to retrieve the original error
// for inspection. Any error value which implements this interface
//
// type causer interface {
// Cause() error
// }
//
// can be inspected by vterrors.Cause and vterrors.RootCause.
//
// * vterrors.Cause will find the immediate cause if one is available, or nil
// if the error is not a `causer` or if no cause is available.
// * vterrors.RootCause will recursively retrieve
// the topmost error which does not implement causer, which is assumed to be
// the original cause. For example:
//
// switch err := errors.RootCause(err).(type) {
// case *MyError:
// // handle specifically
// default:
// // unknown error
// }
//
// causer interface is not exported by this package, but is considered a part
// of stable public API.
//
// Formatted printing of errors
//
// All error values returned from this package implement fmt.Formatter and can
// be formatted by the fmt package. The following verbs are supported
//
// %s print the error. If the error has a Cause it will be
// printed recursively
// %v see %s
// %+v extended format. Each Frame of the error's StackTrace will
// be printed in detail.
//
// Most but not all of the code in this file was originally copied from
// https://github.com/pkg/errors/blob/v0.8.0/errors.go
package vterrors
import (
"fmt"
"golang.org/x/net/context"
"io"
vtrpcpb "vitess.io/vitess/go/vt/proto/vtrpc"
)
type vtError struct {
code vtrpcpb.Code
err string
// New returns an error with the supplied message.
// New also records the stack trace at the point it was called.
func New(code vtrpcpb.Code, message string) error {
return &fundamental{
msg: message,
code: code,
stack: callers(),
}
}
// New creates a new error using the code and input string.
func New(code vtrpcpb.Code, in string) error {
if code == vtrpcpb.Code_OK {
panic("OK is an invalid error code; use INTERNAL instead")
}
return &vtError{
code: code,
err: in,
// NewWithoutCode returns an error when no applicable error code is available
// It will record the stack trace when creating the error
func NewWithoutCode(message string) error {
return &fundamental{
msg: message,
code: vtrpcpb.Code_UNKNOWN,
stack: callers(),
}
}
// Wrap wraps the given error, returning a new error with the given message as a prefix but with the same error code (if err was a vterror) and message of the passed error.
func Wrap(err error, message string) error {
return New(Code(err), fmt.Sprintf("%v: %v", message, err.Error()))
// Errorf formats according to a format specifier and returns the string
// as a value that satisfies error.
// Errorf also records the stack trace at the point it was called.
func Errorf(code vtrpcpb.Code, format string, args ...interface{}) error {
return &fundamental{
msg: fmt.Sprintf(format, args...),
code: code,
stack: callers(),
}
}
// Wrapf wraps the given error, returning a new error with the given format string as a prefix but with the same error code (if err was a vterror) and message of the passed error.
func Wrapf(err error, format string, args ...interface{}) error {
return Wrap(err, fmt.Sprintf(format, args...))
// fundamental is an error that has a message and a stack, but no caller.
type fundamental struct {
msg string
code vtrpcpb.Code
*stack
}
// Errorf returns a new error built using Printf style arguments.
func Errorf(code vtrpcpb.Code, format string, args ...interface{}) error {
return New(code, fmt.Sprintf(format, args...))
}
func (f *fundamental) Error() string { return f.msg }
func (e *vtError) Error() string {
return e.err
func (f *fundamental) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
io.WriteString(s, "Code: "+f.code.String()+"\n")
io.WriteString(s, f.msg+"\n")
f.stack.Format(s, verb)
return
}
fallthrough
case 's':
io.WriteString(s, f.msg)
case 'q':
fmt.Fprintf(s, "%q", f.msg)
}
}
// Code returns the error code if it's a vtError.
// If err is nil, it returns ok. Otherwise, it returns unknown.
// If err is nil, it returns ok.
func Code(err error) vtrpcpb.Code {
if err == nil {
return vtrpcpb.Code_OK
}
if err, ok := err.(*vtError); ok {
if err, ok := err.(*fundamental); ok {
return err.code
}
cause := Cause(err)
if cause != err && cause != nil {
// If we did not find an error code at the outer level, let's find the cause and check it's code
return Code(cause)
}
// Handle some special cases.
switch err {
case context.Canceled:
......@@ -78,16 +158,111 @@ func Code(err error) vtrpcpb.Code {
return vtrpcpb.Code_UNKNOWN
}
// Wrap returns an error annotating err with a stack trace
// at the point Wrap is called, and the supplied message.
// If err is nil, Wrap returns nil.
func Wrap(err error, message string) error {
if err == nil {
return nil
}
return &wrapping{
cause: err,
msg: message,
stack: callers(),
}
}
// Wrapf returns an error annotating err with a stack trace
// at the point Wrapf is call, and the format specifier.
// If err is nil, Wrapf returns nil.
func Wrapf(err error, format string, args ...interface{}) error {
if err == nil {
return nil
}
return &wrapping{
cause: err,
msg: fmt.Sprintf(format, args...),
stack: callers(),
}
}
type wrapping struct {
cause error
msg string
stack *stack
}
func (w *wrapping) Error() string { return w.msg + ": " + w.cause.Error() }
func (w *wrapping) Cause() error { return w.cause }
func (w *wrapping) Format(s fmt.State, verb rune) {
switch verb {
case 'v':
if s.Flag('+') {
fmt.Fprintf(s, "%+v\n", w.Cause())
io.WriteString(s, w.msg)
w.stack.Format(s, verb)
return
}
fallthrough
case 's', 'q':
io.WriteString(s, w.Error())
}
}
// RootCause returns the underlying cause of the error, if possible.
// An error value has a cause if it implements the following
// interface:
//
// type causer interface {
// Cause() error
// }
//
// If the error does not implement Cause, the original error will
// be returned. If the error is nil, nil will be returned without further
// investigation.
func RootCause(err error) error {
for {
cause := Cause(err)
if cause == nil {
return err
}
err = cause
}
}
//
// Cause will return the immediate cause, if possible.
// An error value has a cause if it implements the following
// interface:
//
// type causer interface {
// Cause() error
// }
// If the error does not implement Cause, nil will be returned
func Cause(err error) error {
type causer interface {
Cause() error
}
causerObj, ok := err.(causer)
if !ok {
return nil
}
return causerObj.Cause()
}
// Equals returns true iff the error message and the code returned by Code()
// is equal.
// are equal.
func Equals(a, b error) bool {
if a == nil && b == nil {
// Both are nil.
return true
}
if a == nil && b != nil || a != nil && b == nil {
// One of the two is nil.
if a == nil || b == nil {
// One of the two is nil, since we know both are not nil.
return false
}
......@@ -97,5 +272,5 @@ func Equals(a, b error) bool {
// Print is meant to print the vtError object in test failures.
// For comparing two vterrors, use Equals() instead.
func Print(err error) string {
return fmt.Sprintf("%v: %v", Code(err), err.Error())
return fmt.Sprintf("%v: %v\n", Code(err), err.Error())
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册