未验证 提交 86afd238 编写于 作者: martianzhang's avatar martianzhang 提交者: GitHub

polish code before lunar new year (#193)

* add test case  and notes

* vendor update

* use `tput setaf/srg0` set color in Makefile

* fix loadExternalResource fail cause panic
上级 b1860bb1
......@@ -18,13 +18,10 @@ BUILD_TIME=`date +%Y%m%d%H%M`
COMMIT_VERSION=`git rev-parse HEAD`
# colors compatible setting
COLOR_ENABLE=$(shell tput colors > /dev/null; echo $$?)
ifeq "$(COLOR_ENABLE)" "0"
CRED=$(shell printf "\033[91m")
CGREEN=$(shell printf "\033[92m")
CYELLOW=$(shell printf "\033[93m")
CEND=$(shell printf "\033[0m")
endif
CRED:=$(shell tput setaf 1 2>/dev/null)
CGREEN:=$(shell tput setaf 2 2>/dev/null)
CYELLOW:=$(shell tput setaf 3 2>/dev/null)
CEND:=$(shell tput sgr0 2>/dev/null)
# 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`
......@@ -161,8 +158,6 @@ pingcap-parser: tidb
.PHONY: vendor
vendor: vitess pingcap-parser
# gometalinter
# 如果有不想改的lint问题可以使用metalinter.sh加黑名单
#@bash doc/example/metalinter.sh
.PHONY: lint
lint: build
@echo "$(CGREEN)Run linter check ...$(CEND)"
......
......@@ -44,40 +44,37 @@ func MarkdownEscape(str string) string {
return str
}
//
// loadExternalResource load js/css resource from http[s] url
func loadExternalResource(resource string) string {
var content string
var body []byte
if strings.HasPrefix(resource, "http") {
resp, err := http.Get(resource)
if err == nil {
body, err = ioutil.ReadAll(resp.Body)
if err == nil {
content = string(body)
} else {
Log.Debug("ioutil.ReadAll %s Error: %v", resource, err)
}
} else {
Log.Debug("http.Get %s Error: %v", resource, err)
if err != nil {
Log.Error("http.Get %s Error: %v", resource, err)
return content
}
defer resp.Body.Close()
body, err = ioutil.ReadAll(resp.Body)
if err != nil {
Log.Error("ioutil.ReadAll %s Error: %v", resource, err)
} else {
content = string(body)
}
} else {
fd, err := os.Open(resource)
defer func() {
err = fd.Close()
if err != nil {
Log.Error("loadExternalResource(%s) fd.Close failed: %s", resource, err.Error())
}
}()
if err == nil {
body, err = ioutil.ReadAll(fd)
if err != nil {
Log.Debug("ioutil.ReadAll %s Error: %v", resource, err)
} else {
content = string(body)
}
if err != nil {
Log.Error("os.Open %s Error: %v", resource, err)
return content
}
defer fd.Close()
body, err = ioutil.ReadAll(fd)
if err != nil {
Log.Error("ioutil.ReadAll %s Error: %v", resource, err)
} else {
Log.Debug("os.Open %s Error: %v", resource, err)
content = string(body)
}
}
return content
......
......@@ -27,8 +27,8 @@ allow-online-as-test: true
drop-test-temporary: true
# 语法检查小工具
only-syntax-check: false
sampling-data-factor: 100
sampling: true
sampling-statistic-target: 100
sampling: false
# 日志级别,[0:Emergency, 1:Alert, 2:Critical, 3:Error, 4:Warning, 5:Notice, 6:Informational, 7:Debug]
log-level: 7
log-output: ${your_log_dir}/soar.log
......@@ -78,6 +78,10 @@ soar -h
### 命令行参数配置DSN
SOAR 最新版本已经使用`go-sql-driver`替代了`mymysql`,DSN将使用`go-sql-driver`格式并且保持向前兼容,请参考[go-sql-driver](https://github.com/go-sql-driver/mysql#dsn-data-source-name)文档。
**以下DSN格式不再推荐使用**
> 账号密码中如包含特殊符号(如:'@',':','/'等)可在配置文件中设置,存在特殊字符的情况不适合在命令行中使用。目前`soar`只支持 tcp 协议的 MySQL 数据库连接方式,如需要配置本机MySQL环境建议将`localhost`修改为'127.0.0.1',并检查对应的 'user'@'127.0.0.1' 账号是否存在。
```bash
......
......@@ -99,9 +99,9 @@ func BuildEnv() (*VirtualEnv, *database.Connector) {
common.Config.OnlineDSN.Disable = true
}
// 判断测试环境与remote环境版本是否一致
// 判断测试环境与线上环境版本是否一致,要求测试环境版本不低于线上环境
if vEnvVersion < rEnvVersion {
common.Log.Warning("TestDSN MySQL version older than OnlineDSN(%d), TestDSN(%d) will not be used", vEnvVersion, rEnvVersion)
common.Log.Warning("TestDSN MySQL version older than OnlineDSN(%d), TestDSN(%d) will not be used", rEnvVersion, vEnvVersion)
common.Config.TestDSN.Disable = true
}
......
此差异已折叠。
此差异已折叠。
{
"687D590364E29465": {
"CLA.001": {
"Item": "CLA.001",
"Severity": "L4",
"Summary": "最外层 SELECT 未指定 WHERE 条件",
"Content": "SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。",
"Case": "select id from tbl",
"Position": 0
},
"COL.001": {
"Item": "COL.001",
"Severity": "L1",
"Summary": "不建议使用 SELECT * 类型查询",
"Content": "当表结构变更时,使用 * 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。",
"Case": "select * from tbl where id=1",
"Position": 0
}
}
}
# Query: 687D590364E29465
★ ★ ★ ☆ ☆ 75分
```sql
SELECT
*
FROM
film
```
## 最外层 SELECT 未指定 WHERE 条件
* **Item:** CLA.001
* **Severity:** L4
* **Content:** SELECT 语句没有 WHERE 子句,可能检查比预期更多的行(全表扫描)。对于 SELECT COUNT(\*) 类型的请求如果不要求精度,建议使用 SHOW TABLE STATUS 或 EXPLAIN 替代。
## 不建议使用 SELECT * 类型查询
* **Item:** COL.001
* **Severity:** L1
* **Content:** 当表结构变更时,使用 \* 通配符选择所有列将导致查询的含义和行为会发生更改,可能导致查询返回更多的数据。
online-dsn:
user: online-test
password: '********'
net: tcp
addr: 192.168.12.200:3307
schema: information_schema
charset: utf8mb4
collation: utf8mb4_general_ci
loc: GMT
tls: dsfsfdsf
server-public-key: AAAAB3NzaC1yc2EAAAADAQABAAABAQC0JFhoEgrl5/51UHlIIlfWwhiJLR/EEeF8enGnY0PnAldLQ8STdWD8Um2BUtVjgE9COl1X3mN4vMvnSm8A6aPn66enHn0hKzwh1GvcuZNTPgeaZyGKWG0kcvbziUjAPsxxvRvvoaUspSkRYAP/9vpq3SImJKuIHCPfjnGMurKV1n7W/QfpmNjUEwYOswDjL1Ik6Jy6Lrzf8T0hQEy+dYoV4zNM0HcROCXFuu1LyG+WTch3FW660BecNT8+c4sVTHuUMXgGot8OUtwgfXrt5ZL5as7cuyKiWsLVrtrtvL3T0ZHlV8qxQ3DT1gqjSw6jBOzyDOx3jwthAbdsWjyK4Oqp
maxallowedpacket: 419437
params:
charset: utf8mb4
timeout: 60
read-timeout: 70
write-timeout: 80
allow-native-passwords: false
allow-old-passwords: true
disable: false
test-dsn:
user: test-user
password: '********'
net: tcp
addr: 192.168.12.34:3309
schema: information_schema
charset: utf8mb4
collation: utf8mb4_general_ci
loc: GMT
tls: aabbbaa
server-public-key: this is a tset serverpublic
maxallowedpacket: 4194309
params:
charset: utf8mb4
timeout: 50
read-timeout: 40
write-timeout: 30
allow-native-passwords: false
allow-old-passwords: true
disable: false
allow-online-as-test: true
drop-test-temporary: false
cleanup-test-database: true
only-syntax-check: true
sampling-statistic-target: 110
sampling: true
sampling-condition: aaa
profiling: true
trace: true
explain: false
delimiter: ;
log-level: 3
log-output: /dev/null
report-type: html
report-css: sdfs
report-javascript: sdfsd
report-title: SQL优化分析报告-test
markdown-extensions: 92
markdown-html-flags: 10
ignore-rules:
- COL.012
rewrite-rules:
- delimiter
- orderbynull
- groupbyconst
- dmlorderby
- having
- star2columns
- insertcolumns
- distinctstar
blacklist: /tmp/blacklist
max-join-table-count: 12
max-group-by-cols-count: 15
max-distinct-count: 7
max-index-cols-count: 2
max-text-cols-count: 3
max-total-rows: 9999991
max-query-cost: 9992
spaghetti-query-length: 2041
allow-drop-index: true
max-in-count: 101
max-index-bytes-percolumn: 762
max-index-bytes: 3073
allow-charsets:
- utf8
- utf8mb4
allow-collates: []
allow-engines:
- innodb
- tokudb
max-index-count: 12
max-column-count: 41
max-value-count: 102
index-prefix: idx_
unique-key-prefix: uk_
max-subquery-depth: 6
max-varchar-length: 1022
column-not-allow-type:
- boolean
min-cardinality: 2
explain-sql-report-type: pretty
explain-type: extended
explain-format: traditional
explain-warn-select-type:
- ""
explain-warn-access-type:
- ALL
explain-max-keys: 31
explain-min-keys: 10
explain-max-rows: 10002
explain-warn-extra:
- Using temporary
- Using filesort
explain-max-filtered: 120
explain-warn-scalability:
- O(log(n))
show-warnings: true
show-last-query-cost: true
query: ""
list-heuristic-rules: true
list-rewrite-rules: true
list-test-sqls: true
list-report-types: true
verbose: true
dry-run: false
max-pretty-sql-length: 1022
......@@ -2,41 +2,139 @@
load test_helper
# 1. 检查版本输出格式是否正确
# 2. 检查版本是否为当天编译的
@test "Test soar version" {
run ${SOAR_BIN} -version
[ "$status" -eq 0 ]
[ "${lines[0]%% *}" == "Version:" ]
[ "${lines[1]%% *}" == "Branch:" ]
[ "${lines[2]%% *}" == "Compile:" ]
[ $(expr "${lines[2]}" : "Compile: $(date +'%Y-%m-%d').*") -ne 0 ]
[ $(expr "${lines[2]}" : "Compile: $(date +'%Y-%m-%d').*") -ne 0 ] # 检查当前版本是否为今日编译的
}
# 3. 无参数执行是否正确
@test "No arguments prints message" {
run ${SOAR_BIN}
[ $status -eq 1 ]
[ "${lines[0]}" == 'Args format error, use --help see how to use it!' ]
}
# 4. 检查输出的默认值是否改变 soar -print-config 加log-outpt 是因为日志默认是相对路径
@test "Run default printconfig cases" {
${SOAR_BIN} -print-config -log-output=/dev/null > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
# 5. soar 使用 config 配置文件路径是否正确
# 13. soar -check-config 数据库连接配置检查 *
# soar 数据库测试(线上、线下、-allow-online-as-test)
@test "Check config cases" {
run ${SOAR_BIN_ENV} -check-config
[ $status -eq 0 ]
[ -z ${output} ]
}
# 6. soar 使用配置文件修改默认参数是否正确
# 注意:不启用的配置为默认配置项目
@test "Check the default config of the changes" {
${SOAR_BIN} -config ${BATS_FIXTURE_DIRNAME}/${BATS_TEST_NAME}.golden -print-config -log-output=/dev/null > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
# 8. 执行 soar -query 为文件时是否正常
@test "Check soar query for input file" {
${SOAR_BIN} -query <(${SOAR_BIN} -list-test-sqls) > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
# 9. 管道输入 SQL 是否正常
@test "Check soar for pipe input" {
${SOAR_BIN} -list-test-sqls |${SOAR_BIN} > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
# 10. report 为 json 格式是否正常
@test "Check soar report for json" {
${SOAR_BIN} -query "select * from film" \
-report-type json > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
# 10. report 为 markdown 格式是否正常
@test "Check soar report for markdown" {
${SOAR_BIN} -query "select * from film" \
-report-type markdown > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
# 11. report 格式 html 检查
@test "Check soar report for html" {
${SOAR_BIN} -query "select * from film" \
-report-title "soar report check" \
-report-type html > ${BATS_TMP_DIRNAME}/${BATS_TEST_NAME}.golden
run golden_diff
[ $status -eq 0 ]
}
# 12. 黑名单功能是否正常
# soar 的日志和黑名单的相对路径都相对于 soar 的二进制文件路径说的
@test "Check soar blacklist" {
run ${SOAR_BIN} -blacklist ../etc/soar.blacklist -query "show processlist;"
[ $status -eq 0 ]
[ -z ${output} ]
}
# 13. soar -check-config 数据库连接配置检查 *
# 参见 5
# 14. soar -help 检查
@test "Check soar help" {
run ${SOAR_BIN} -help
[ $status -eq 2 ]
[ "${#lines[@]}" -gt 30 ]
}
# 15. soar 数据库测试(线上、线下、-allow-online-as-test)
# 参见 5
# 16. 语法检查(正确)
@test "Syntax Check OK" {
run ${SOAR_BIN} -query "select * from film" -only-syntax-check
[ $status -eq 0 ]
[ -z $ouput ]
}
# 16. 语法检查(错误)
@test "Syntax Check Error" {
run ${SOAR_BIN} -query "select * frm film" -only-syntax-check
[ $status -eq 1 ]
[ -n $ouput ]
}
# 17. dsn 检查
@test "Check soar test dsn root:passwd@host:port/db" {
run ${SOAR_BIN} -online-dsn="root:pase@D@192.168.12.11:3306/testDB" -print-config
[ $(expr "$output" : ".*user: root") -ne 0 ]
[ $(expr "$output" : ".*addr: 192.168.12.11:3306") -ne 0 ]
[ $(expr "$output" : ".*schema: testDB") -ne 0 ]
[ $(expr "$output" : ".*charset: utf8") -ne 0 ]
}
# 18. 日志中是否含有密码
@test "Check log has password" {
${SOAR_BIN_ENV} -query "select * from film" -log-level=7
run grep "1tIsB1g3rt" ${SOAR_BIN}.log
[ ${status} -eq 1 ]
}
# 18. 输出中是否含有密码
@test "Check stdout has password" {
run ${SOAR_BIN_ENV} -query "select * from film" -log-level=7
[ $(expr "$output" : ".*1tIsB1g3rt.*") -eq 0 ]
[ ${status} -eq 0 ]
}
......@@ -5,5 +5,4 @@ load test_helper
@test "Check Query Optimizer" {
run ${SOAR_BIN} -query "select * from film where length > 120"
[ $status -eq 0 ]
}
}
\ No newline at end of file
......@@ -370,7 +370,51 @@ type ColumnOption struct {
// Restore implements Node interface.
func (n *ColumnOption) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
switch n.Tp {
case ColumnOptionNoOption:
return nil
case ColumnOptionPrimaryKey:
ctx.WriteKeyWord("PRIMARY KEY")
case ColumnOptionNotNull:
ctx.WriteKeyWord("NOT NULL")
case ColumnOptionAutoIncrement:
ctx.WriteKeyWord("AUTO_INCREMENT")
case ColumnOptionDefaultValue:
ctx.WriteKeyWord("DEFAULT ")
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing ColumnOption DefaultValue Expr")
}
case ColumnOptionUniqKey:
ctx.WriteKeyWord("UNIQUE KEY")
case ColumnOptionNull:
ctx.WriteKeyWord("NULL")
case ColumnOptionOnUpdate:
ctx.WriteKeyWord("ON UPDATE ")
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing ColumnOption ON UPDATE Expr")
}
case ColumnOptionFulltext:
return errors.New("TiDB Parser ignore the `ColumnOptionFulltext` type now")
case ColumnOptionComment:
ctx.WriteKeyWord("COMMENT ")
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing ColumnOption COMMENT Expr")
}
case ColumnOptionGenerated:
ctx.WriteKeyWord("GENERATED ALWAYS AS")
ctx.WritePlain("(")
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing ColumnOption GENERATED ALWAYS Expr")
}
ctx.WritePlain(")")
case ColumnOptionReference:
if err := n.Refer.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing ColumnOption ReferenceDef")
}
default:
return errors.New("An error occurred while splicing ColumnOption")
}
return nil
}
// Accept implements Node Accept interface.
......@@ -489,13 +533,18 @@ func (n *Constraint) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("UNIQUE KEY")
case ConstraintUniqIndex:
ctx.WriteKeyWord("UNIQUE INDEX")
case ConstraintForeignKey:
ctx.WriteKeyWord("FOREIGN KEY")
case ConstraintFulltext:
ctx.WriteKeyWord("FULLTEXT")
}
if n.Name != "" {
if n.Tp == ConstraintForeignKey {
ctx.WriteKeyWord("CONSTRAINT ")
if n.Name != "" {
ctx.WriteName(n.Name)
ctx.WritePlain(" ")
}
ctx.WriteKeyWord("FOREIGN KEY ")
} else if n.Name != "" {
ctx.WritePlain(" ")
ctx.WriteName(n.Name)
}
......@@ -570,7 +619,22 @@ type ColumnDef struct {
// Restore implements Node interface.
func (n *ColumnDef) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
if err := n.Name.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing ColumnDef Name")
}
if n.Tp != nil {
ctx.WritePlain(" ")
if err := n.Tp.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing ColumnDef Type")
}
}
for i, options := range n.Options {
ctx.WritePlain(" ")
if err := options.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing ColumnDef ColumnOption: [%v]", i)
}
}
return nil
}
// Accept implements Node Accept interface.
......@@ -613,7 +677,73 @@ type CreateTableStmt struct {
// Restore implements Node interface.
func (n *CreateTableStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WriteKeyWord("CREATE TABLE ")
if n.IfNotExists {
ctx.WriteKeyWord("IF NOT EXISTS ")
}
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Table")
}
ctx.WritePlain(" ")
if n.ReferTable != nil {
ctx.WriteKeyWord("LIKE ")
if err := n.ReferTable.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing CreateTableStmt ReferTable")
}
}
if lenCols := len(n.Cols); lenCols > 0 {
ctx.WritePlain("(")
for i, col := range n.Cols {
if i > 0 {
ctx.WritePlain(",")
}
if err := col.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt ColumnDef: [%v]", i)
}
}
for i, constraint := range n.Constraints {
if i > 0 || lenCols >= 1 {
ctx.WritePlain(",")
}
if err := constraint.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt Constraints: [%v]", i)
}
}
ctx.WritePlain(")")
}
for i, option := range n.Options {
ctx.WritePlain(" ")
if err := option.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing CreateTableStmt TableOption: [%v]", i)
}
}
if n.Partition != nil {
ctx.WritePlain(" ")
if err := n.Partition.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Partition")
}
}
if n.Select != nil {
switch n.OnDuplicate {
case OnDuplicateCreateTableSelectError:
ctx.WriteKeyWord(" AS ")
case OnDuplicateCreateTableSelectIgnore:
ctx.WriteKeyWord(" IGNORE AS ")
case OnDuplicateCreateTableSelectReplace:
ctx.WriteKeyWord(" REPLACE AS ")
}
if err := n.Select.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing CreateTableStmt Select")
}
}
return nil
}
// Accept implements Node Accept interface.
......@@ -822,7 +952,59 @@ type CreateViewStmt struct {
// Restore implements Node interface.
func (n *CreateViewStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WriteKeyWord("CREATE ")
if n.OrReplace {
ctx.WriteKeyWord("OR REPLACE ")
}
ctx.WriteKeyWord("ALGORITHM")
ctx.WritePlain(" = ")
ctx.WriteKeyWord(n.Algorithm.String())
ctx.WriteKeyWord(" DEFINER")
ctx.WritePlain(" = ")
// todo Use n.Definer.Restore(ctx) to replace this part
if n.Definer.CurrentUser {
ctx.WriteKeyWord("current_user")
} else {
ctx.WriteName(n.Definer.Username)
if n.Definer.Hostname != "" {
ctx.WritePlain("@")
ctx.WriteName(n.Definer.Hostname)
}
}
ctx.WriteKeyWord(" SQL SECURITY ")
ctx.WriteKeyWord(n.Security.String())
ctx.WriteKeyWord(" VIEW ")
if err := n.ViewName.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while create CreateViewStmt.ViewName")
}
for i, col := range n.Cols {
if i == 0 {
ctx.WritePlain(" (")
} else {
ctx.WritePlain(",")
}
ctx.WriteName(col.O)
if i == len(n.Cols)-1 {
ctx.WritePlain(")")
}
}
ctx.WriteKeyWord(" AS ")
if err := n.Select.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while create CreateViewStmt.Select")
}
if n.CheckOption != model.CheckOptionCascaded {
ctx.WriteKeyWord(" WITH ")
ctx.WriteKeyWord(n.CheckOption.String())
ctx.WriteKeyWord(" CHECK OPTION")
}
return nil
}
// Accept implements Node Accept interface.
......@@ -859,7 +1041,36 @@ type CreateIndexStmt struct {
// Restore implements Node interface.
func (n *CreateIndexStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WriteKeyWord("CREATE ")
if n.Unique {
ctx.WriteKeyWord("UNIQUE ")
}
ctx.WriteKeyWord("INDEX ")
ctx.WriteName(n.IndexName)
ctx.WriteKeyWord(" ON ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.Table")
}
ctx.WritePlain(" (")
for i, indexColName := range n.IndexColNames {
if i != 0 {
ctx.WritePlain(", ")
}
if err := indexColName.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore CreateIndexStmt.IndexColNames: [%v]", i)
}
}
ctx.WritePlain(")")
if n.IndexOption.Tp != model.IndexTypeInvalid || n.IndexOption.KeyBlockSize > 0 || n.IndexOption.Comment != "" {
ctx.WritePlain(" ")
if err := n.IndexOption.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore CreateIndexStmt.IndexOption")
}
}
return nil
}
// Accept implements Node Accept interface.
......@@ -986,6 +1197,103 @@ type TableOption struct {
UintValue uint64
}
func (n *TableOption) Restore(ctx *RestoreCtx) error {
switch n.Tp {
case TableOptionEngine:
ctx.WriteKeyWord("ENGINE ")
ctx.WritePlain("= ")
if n.StrValue != "" {
ctx.WritePlain(n.StrValue)
} else {
ctx.WritePlain("''")
}
case TableOptionCharset:
ctx.WriteKeyWord("DEFAULT CHARACTER SET ")
ctx.WritePlain("= ")
ctx.WriteKeyWord(n.StrValue)
case TableOptionCollate:
ctx.WriteKeyWord("DEFAULT COLLATE ")
ctx.WritePlain("= ")
ctx.WriteKeyWord(n.StrValue)
case TableOptionAutoIncrement:
ctx.WriteKeyWord("AUTO_INCREMENT ")
ctx.WritePlain("= ")
ctx.WritePlainf("%d", n.UintValue)
case TableOptionComment:
ctx.WriteKeyWord("COMMENT ")
ctx.WritePlain("= ")
ctx.WriteString(n.StrValue)
case TableOptionAvgRowLength:
ctx.WriteKeyWord("AVG_ROW_LENGTH ")
ctx.WritePlain("= ")
ctx.WritePlainf("%d", n.UintValue)
case TableOptionCheckSum:
ctx.WriteKeyWord("CHECKSUM ")
ctx.WritePlain("= ")
ctx.WritePlainf("%d", n.UintValue)
case TableOptionCompression:
ctx.WriteKeyWord("COMPRESSION ")
ctx.WritePlain("= ")
ctx.WriteString(n.StrValue)
case TableOptionConnection:
ctx.WriteKeyWord("CONNECTION ")
ctx.WritePlain("= ")
ctx.WriteString(n.StrValue)
case TableOptionPassword:
ctx.WriteKeyWord("PASSWORD ")
ctx.WritePlain("= ")
ctx.WriteString(n.StrValue)
case TableOptionKeyBlockSize:
ctx.WriteKeyWord("KEY_BLOCK_SIZE ")
ctx.WritePlain("= ")
ctx.WritePlainf("%d", n.UintValue)
case TableOptionMaxRows:
ctx.WriteKeyWord("MAX_ROWS ")
ctx.WritePlain("= ")
ctx.WritePlainf("%d", n.UintValue)
case TableOptionMinRows:
ctx.WriteKeyWord("MIN_ROWS ")
ctx.WritePlain("= ")
ctx.WritePlainf("%d", n.UintValue)
case TableOptionDelayKeyWrite:
ctx.WriteKeyWord("DELAY_KEY_WRITE ")
ctx.WritePlain("= ")
ctx.WritePlainf("%d", n.UintValue)
case TableOptionRowFormat:
ctx.WriteKeyWord("ROW_FORMAT ")
ctx.WritePlain("= ")
switch n.UintValue {
case RowFormatDefault:
ctx.WriteKeyWord("DEFAULT")
case RowFormatDynamic:
ctx.WriteKeyWord("DYNAMIC")
case RowFormatFixed:
ctx.WriteKeyWord("FIXED")
case RowFormatCompressed:
ctx.WriteKeyWord("COMPRESSED")
case RowFormatRedundant:
ctx.WriteKeyWord("REDUNDANT")
case RowFormatCompact:
ctx.WriteKeyWord("COMPACT")
default:
return errors.Errorf("invalid TableOption: TableOptionRowFormat: %d", n.UintValue)
}
case TableOptionStatsPersistent:
// TODO: not support
ctx.WritePlain(" /* TableOptionStatsPersistent is not supported */ ")
case TableOptionShardRowID:
ctx.WriteKeyWord("SHARD_ROW_ID_BITS ")
ctx.WritePlain("= ")
ctx.WritePlainf("%d", n.UintValue)
case TableOptionPackKeys:
// TODO: not support
ctx.WritePlain(" /* TableOptionPackKeys is not supported */ ")
default:
return errors.Errorf("invalid TableOption: %d", n.Tp)
}
return nil
}
// ColumnPositionType is the type for ColumnPosition.
type ColumnPositionType int
......@@ -1072,6 +1380,20 @@ const (
// See https://dev.mysql.com/doc/refman/5.7/en/alter-table.html#alter-table-concurrency
type LockType byte
func (n LockType) String() string {
switch n {
case LockTypeNone:
return "NONE"
case LockTypeDefault:
return "DEFAULT"
case LockTypeShared:
return "SHARED"
case LockTypeExclusive:
return "EXCLUSIVE"
}
return ""
}
// Lock Types.
const (
LockTypeNone LockType = iota + 1
......@@ -1102,7 +1424,159 @@ type AlterTableSpec struct {
// Restore implements Node interface.
func (n *AlterTableSpec) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
switch n.Tp {
case AlterTableOption:
switch {
case len(n.Options) == 2 &&
n.Options[0].Tp == TableOptionCharset &&
n.Options[1].Tp == TableOptionCollate:
ctx.WriteKeyWord("CONVERT TO CHARACTER SET ")
ctx.WriteKeyWord(n.Options[0].StrValue)
ctx.WriteKeyWord(" COLLATE ")
ctx.WriteKeyWord(n.Options[1].StrValue)
default:
for i, opt := range n.Options {
if i != 0 {
ctx.WritePlain(", ")
}
if err := opt.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.Options[%d]", i)
}
}
}
case AlterTableAddColumns:
ctx.WriteKeyWord("ADD COLUMN ")
if n.Position != nil && len(n.NewColumns) == 1 {
if err := n.NewColumns[0].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewColumns[%d]", 0)
}
if n.Position.Tp != ColumnPositionNone {
ctx.WritePlain(" ")
}
if err := n.Position.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position")
}
} else {
ctx.WritePlain("(")
for i, col := range n.NewColumns {
if i != 0 {
ctx.WritePlain(", ")
}
if err := col.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.NewColumns[%d]", i)
}
}
ctx.WritePlain(")")
}
case AlterTableAddConstraint:
ctx.WriteKeyWord("ADD ")
if err := n.Constraint.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Constraint")
}
case AlterTableDropColumn:
ctx.WriteKeyWord("DROP COLUMN ")
if err := n.OldColumnName.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName")
}
// TODO: RestrictOrCascadeOpt not support
case AlterTableDropPrimaryKey:
ctx.WriteKeyWord("DROP PRIMARY KEY")
case AlterTableDropIndex:
ctx.WriteKeyWord("DROP INDEX ")
ctx.WriteName(n.Name)
case AlterTableDropForeignKey:
ctx.WriteKeyWord("DROP FOREIGN KEY ")
ctx.WriteName(n.Name)
case AlterTableModifyColumn:
ctx.WriteKeyWord("MODIFY COLUMN ")
if err := n.NewColumns[0].Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]")
}
if n.Position.Tp != ColumnPositionNone {
ctx.WritePlain(" ")
}
if err := n.Position.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position")
}
case AlterTableChangeColumn:
ctx.WriteKeyWord("CHANGE COLUMN ")
if err := n.OldColumnName.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.OldColumnName")
}
ctx.WritePlain(" ")
if err := n.NewColumns[0].Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]")
}
if n.Position.Tp != ColumnPositionNone {
ctx.WritePlain(" ")
}
if err := n.Position.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.Position")
}
case AlterTableRenameTable:
ctx.WriteKeyWord("RENAME AS ")
if err := n.NewTable.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewTable")
}
case AlterTableAlterColumn:
ctx.WriteKeyWord("ALTER COLUMN ")
if err := n.NewColumns[0].Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0]")
}
if len(n.NewColumns[0].Options) == 1 {
ctx.WriteKeyWord("SET DEFAULT ")
if err := n.NewColumns[0].Options[0].Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableSpec.NewColumns[0].Options[0].Expr")
}
} else {
ctx.WriteKeyWord(" DROP DEFAULT")
}
case AlterTableLock:
ctx.WriteKeyWord("LOCK ")
ctx.WritePlain("= ")
ctx.WriteKeyWord(n.LockType.String())
case AlterTableAlgorithm:
// TODO: not support
ctx.WritePlain(" /* AlterTableAlgorithm is not supported */ ")
case AlterTableRenameIndex:
ctx.WriteKeyWord("RENAME INDEX ")
ctx.WriteName(n.FromKey.O)
ctx.WriteKeyWord(" TO ")
ctx.WriteName(n.ToKey.O)
case AlterTableForce:
// TODO: not support
ctx.WritePlain(" /* AlterTableForce is not supported */ ")
case AlterTableAddPartitions:
ctx.WriteKeyWord("ADD PARTITION")
if n.PartDefinitions != nil {
ctx.WritePlain(" (")
for i, def := range n.PartDefinitions {
if i != 0 {
ctx.WritePlain(", ")
}
if err := def.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterTableSpec.PartDefinitions[%d]", i)
}
}
ctx.WritePlain(")")
} else if n.Num != 0 {
ctx.WriteKeyWord(" PARTITIONS ")
ctx.WritePlainf("%d", n.Num)
}
case AlterTableCoalescePartitions:
ctx.WriteKeyWord("COALESCE PARTITION ")
ctx.WritePlainf("%d", n.Num)
case AlterTableDropPartition:
ctx.WriteKeyWord("DROP PARTITION ")
ctx.WriteName(n.Name)
case AlterTableTruncatePartition:
ctx.WriteKeyWord("TRUNCATE PARTITION ")
ctx.WriteName(n.Name)
default:
// TODO: not support
ctx.WritePlainf(" /* AlterTableType(%d) is not supported */ ", n.Tp)
}
return nil
}
// Accept implements Node Accept interface.
......@@ -1161,7 +1635,21 @@ type AlterTableStmt struct {
// Restore implements Node interface.
func (n *AlterTableStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WriteKeyWord("ALTER TABLE ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AlterTableStmt.Table")
}
for i, spec := range n.Specs {
if i == 0 {
ctx.WritePlain(" ")
} else {
ctx.WritePlain(", ")
}
if err := spec.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterTableStmt.Specs[%d]", i)
}
}
return nil
}
// Accept implements Node Accept interface.
......@@ -1226,6 +1714,28 @@ type PartitionDefinition struct {
Comment string
}
// Restore implements Node interface.
func (n *PartitionDefinition) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("PARTITION ")
ctx.WriteName(n.Name.O)
if n.LessThan != nil {
ctx.WriteKeyWord(" VALUES LESS THAN ")
ctx.WritePlain("(")
for k, less := range n.LessThan {
if err := less.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore PartitionDefinition.LessThan[%d]", k)
}
}
ctx.WritePlain(")")
}
if n.Comment != "" {
ctx.WriteKeyWord(" COMMENT ")
ctx.WritePlain("= ")
ctx.WriteString(n.Comment)
}
return nil
}
// PartitionOptions specifies the partition options.
type PartitionOptions struct {
Tp model.PartitionType
......@@ -1234,3 +1744,52 @@ type PartitionOptions struct {
Definitions []*PartitionDefinition
Num uint64
}
func (n *PartitionOptions) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("PARTITION BY ")
switch n.Tp {
case model.PartitionTypeRange:
ctx.WriteKeyWord("RANGE ")
case model.PartitionTypeHash:
ctx.WriteKeyWord("HASH ")
case model.PartitionTypeList:
return errors.New("TiDB Parser ignore the `PartitionTypeList` type now")
default:
return errors.Errorf("invalid model.PartitionType: %d", n.Tp)
}
ctx.WritePlain("(")
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore PartitionOptions Expr")
}
ctx.WritePlain(") ")
for i, col := range n.ColumnNames {
if i > 0 {
ctx.WritePlain(",")
}
if err := col.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing PartitionOptions ColumnName: [%v]", i)
}
}
if n.Num > 0 {
ctx.WriteKeyWord("PARTITIONS ")
ctx.WritePlainf("%d", n.Num)
}
if len(n.Definitions) > 0 {
ctx.WritePlain("(")
for i, def := range n.Definitions {
if i > 0 {
ctx.WritePlain(",")
}
if err := def.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing PartitionOptions Definitions: [%v]", i)
}
}
ctx.WritePlain(")")
}
return nil
}
......@@ -183,7 +183,8 @@ type TableName struct {
DBInfo *model.DBInfo
TableInfo *model.TableInfo
IndexHints []*IndexHint
IndexHints []*IndexHint
PartitionNames []model.CIStr
}
// Restore implements Node interface.
......@@ -756,7 +757,110 @@ type SelectStmt struct {
// Restore implements Node interface.
func (n *SelectStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WriteKeyWord("SELECT ")
if n.SelectStmtOpts.Priority > 0 {
ctx.WriteKeyWord(mysql.Priority2Str[n.SelectStmtOpts.Priority])
ctx.WritePlain(" ")
}
if !n.SelectStmtOpts.SQLCache {
ctx.WriteKeyWord("SQL_NO_CACHE ")
}
if n.TableHints != nil && len(n.TableHints) != 0 {
ctx.WritePlain("/*+ ")
for i, tableHint := range n.TableHints {
if err := tableHint.Restore(ctx); err != nil {
errors.Annotatef(err, "An error occurred while restore SelectStmt.TableHints[%d]", i)
}
}
ctx.WritePlain("*/ ")
}
if n.Distinct {
ctx.WriteKeyWord("DISTINCT ")
}
if n.SelectStmtOpts.StraightJoin {
ctx.WriteKeyWord("STRAIGHT_JOIN ")
}
if n.Fields != nil {
for i, field := range n.Fields.Fields {
if i != 0 {
ctx.WritePlain(",")
}
if err := field.Restore(ctx); err != nil {
errors.Annotatef(err, "An error occurred while restore SelectStmt.Fields[%d]", i)
}
}
}
if n.From != nil {
ctx.WriteKeyWord(" FROM ")
if err := n.From.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.From")
}
}
if n.From == nil && n.Where != nil {
ctx.WriteKeyWord(" FROM DUAL")
}
if n.Where != nil {
ctx.WriteKeyWord(" WHERE ")
if err := n.Where.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.Where")
}
}
if n.GroupBy != nil {
ctx.WritePlain(" ")
if err := n.GroupBy.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.GroupBy")
}
}
if n.Having != nil {
ctx.WritePlain(" ")
if err := n.Having.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.Having")
}
}
if n.WindowSpecs != nil {
ctx.WriteKeyWord(" WINDOW ")
for i, windowsSpec := range n.WindowSpecs {
if i != 0 {
ctx.WritePlain(",")
}
if err := windowsSpec.Restore(ctx); err != nil {
errors.Annotatef(err, "An error occurred while restore SelectStmt.WindowSpec[%d]", i)
}
}
}
if n.OrderBy != nil {
ctx.WritePlain(" ")
if err := n.OrderBy.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.OrderBy")
}
}
if n.Limit != nil {
ctx.WritePlain(" ")
if err := n.Limit.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore SelectStmt.Limit")
}
}
switch n.LockTp {
case SelectLockInShareMode:
ctx.WriteKeyWord(" LOCK ")
ctx.WriteKeyWord(n.LockTp.String())
case SelectLockForUpdate:
ctx.WritePlain(" ")
ctx.WriteKeyWord(n.LockTp.String())
}
return nil
}
// Accept implements Node Accept interface.
......@@ -855,7 +959,24 @@ type UnionSelectList struct {
// Restore implements Node interface.
func (n *UnionSelectList) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
for i, selectStmt := range n.Selects {
if i != 0 {
ctx.WriteKeyWord(" UNION ")
if !selectStmt.IsAfterUnionDistinct {
ctx.WriteKeyWord("ALL ")
}
}
if selectStmt.IsInBraces {
ctx.WritePlain("(")
}
if err := selectStmt.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore UnionSelectList.SelectStmt")
}
if selectStmt.IsInBraces {
ctx.WritePlain(")")
}
}
return nil
}
// Accept implements Node Accept interface.
......@@ -888,7 +1009,24 @@ type UnionStmt struct {
// Restore implements Node interface.
func (n *UnionStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
if err := n.SelectList.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore UnionStmt.SelectList")
}
if n.OrderBy != nil {
ctx.WritePlain(" ")
if err := n.OrderBy.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore UnionStmt.OrderBy")
}
}
if n.Limit != nil {
ctx.WritePlain(" ")
if err := n.Limit.Restore(ctx); err != nil {
errors.Annotate(err, "An error occurred while restore UnionStmt.Limit")
}
}
return nil
}
// Accept implements Node Accept interface.
......@@ -979,7 +1117,36 @@ type LoadDataStmt struct {
// Restore implements Node interface.
func (n *LoadDataStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WriteKeyWord("LOAD DATA ")
if n.IsLocal {
ctx.WriteKeyWord("LOCAL ")
}
ctx.WriteKeyWord("INFILE ")
ctx.WriteString(n.Path)
ctx.WriteKeyWord(" INTO TABLE ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore LoadDataStmt.Table")
}
n.FieldsInfo.Restore(ctx)
n.LinesInfo.Restore(ctx)
if n.IgnoreLines != 0 {
ctx.WriteKeyWord(" IGNORE ")
ctx.WritePlainf("%d", n.IgnoreLines)
ctx.WriteKeyWord(" LINES")
}
if len(n.Columns) != 0 {
ctx.WritePlain(" (")
for i, column := range n.Columns {
if i != 0 {
ctx.WritePlain(",")
}
if err := column.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore LoadDataStmt.Columns")
}
}
ctx.WritePlain(")")
}
return nil
}
// Accept implements Node Accept interface.
......@@ -1013,12 +1180,52 @@ type FieldsClause struct {
Escaped byte
}
// Restore for FieldsClause
func (n *FieldsClause) Restore(ctx *RestoreCtx) error {
if n.Terminated != "\t" || n.Escaped != '\\' {
ctx.WriteKeyWord(" FIELDS")
if n.Terminated != "\t" {
ctx.WriteKeyWord(" TERMINATED BY ")
ctx.WriteString(n.Terminated)
}
if n.Enclosed != 0 {
ctx.WriteKeyWord(" ENCLOSED BY ")
ctx.WriteString(string(n.Enclosed))
}
if n.Escaped != '\\' {
ctx.WriteKeyWord(" ESCAPED BY ")
if n.Escaped == 0 {
ctx.WritePlain("''")
} else {
ctx.WriteString(string(n.Escaped))
}
}
}
return nil
}
// LinesClause represents lines references clause in load data statement.
type LinesClause struct {
Starting string
Terminated string
}
// Restore for LinesClause
func (n *LinesClause) Restore(ctx *RestoreCtx) error {
if n.Starting != "" || n.Terminated != "\n" {
ctx.WriteKeyWord(" LINES")
if n.Starting != "" {
ctx.WriteKeyWord(" STARTING BY ")
ctx.WriteString(n.Starting)
}
if n.Terminated != "\n" {
ctx.WriteKeyWord(" TERMINATED BY ")
ctx.WriteString(n.Terminated)
}
}
return nil
}
// InsertStmt is a statement to insert new rows into an existing table.
// See https://dev.mysql.com/doc/refman/5.7/en/insert.html
type InsertStmt struct {
......@@ -1037,7 +1244,90 @@ type InsertStmt struct {
// Restore implements Node interface.
func (n *InsertStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
if n.IsReplace {
ctx.WriteKeyWord("REPLACE ")
} else {
ctx.WriteKeyWord("INSERT ")
}
switch n.Priority {
case mysql.LowPriority:
ctx.WriteKeyWord("LOW_PRIORITY ")
case mysql.HighPriority:
ctx.WriteKeyWord("HIGH_PRIORITY ")
case mysql.DelayedPriority:
ctx.WriteKeyWord("DELAYED ")
}
if n.IgnoreErr {
ctx.WriteKeyWord("IGNORE ")
}
ctx.WriteKeyWord("INTO ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore InsertStmt.Table")
}
if n.Columns != nil {
ctx.WritePlain(" (")
for i, v := range n.Columns {
if i != 0 {
ctx.WritePlain(",")
}
if err := v.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore InsertStmt.Columns[%d]", i)
}
}
ctx.WritePlain(")")
}
if n.Lists != nil {
ctx.WriteKeyWord(" VALUES ")
for i, row := range n.Lists {
if i != 0 {
ctx.WritePlain(",")
}
ctx.WritePlain("(")
for j, v := range row {
if j != 0 {
ctx.WritePlain(",")
}
if err := v.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore InsertStmt.Lists[%d][%d]", i, j)
}
}
ctx.WritePlain(")")
}
}
if n.Select != nil {
switch v := n.Select.(type) {
case *SelectStmt, *UnionStmt:
if err := v.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore InsertStmt.Select")
}
default:
return errors.Errorf("Incorrect type for InsertStmt.Select: %T", v)
}
}
if n.Setlist != nil {
ctx.WriteKeyWord(" SET ")
for i, v := range n.Setlist {
if i != 0 {
ctx.WritePlain(",")
}
if err := v.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore InsertStmt.Setlist[%d]", i)
}
}
}
if n.OnDuplicate != nil {
ctx.WriteKeyWord(" ON DUPLICATE KEY UPDATE ")
for i, v := range n.OnDuplicate {
if i != 0 {
ctx.WritePlain(",")
}
if err := v.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore InsertStmt.OnDuplicate[%d]", i)
}
}
}
return nil
}
// Accept implements Node Accept interface.
......@@ -1294,6 +1584,7 @@ const (
ShowStatus
ShowCollation
ShowCreateTable
ShowCreateView
ShowCreateUser
ShowGrants
ShowTriggers
......@@ -1311,6 +1602,7 @@ const (
ShowMasterStatus
ShowPrivileges
ShowErrors
ShowBindings
)
// ShowStmt is a statement to provide information about databases, tables, columns and so on.
......@@ -1336,7 +1628,170 @@ type ShowStmt struct {
// Restore implements Node interface.
func (n *ShowStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
restoreOptFull := func() {
if n.Full {
ctx.WriteKeyWord("FULL ")
}
}
restoreShowDatabaseNameOpt := func() {
if n.DBName != "" {
// FROM OR IN
ctx.WriteKeyWord(" IN ")
ctx.WriteName(n.DBName)
}
}
restoreGlobalScope := func() {
if n.GlobalScope {
ctx.WriteKeyWord("GLOBAL ")
} else {
ctx.WriteKeyWord("SESSION ")
}
}
restoreShowLikeOrWhereOpt := func() error {
if n.Pattern != nil && n.Pattern.Pattern != nil {
ctx.WriteKeyWord(" LIKE ")
if err := n.Pattern.Pattern.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore ShowStmt.Pattern")
}
} else if n.Where != nil {
ctx.WriteKeyWord(" WHERE ")
if err := n.Where.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore ShowStmt.Where")
}
}
return nil
}
restoreUserName := func() error {
if n.User.CurrentUser {
ctx.WriteKeyWord("CURRENT_USER")
} else {
ctx.WriteString(n.User.Username)
if n.User.Hostname != "" {
ctx.WritePlain("@")
ctx.WriteString(n.User.Hostname)
}
}
return nil
}
ctx.WriteKeyWord("SHOW ")
switch n.Tp {
case ShowCreateTable:
ctx.WriteKeyWord("CREATE TABLE ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore ShowStmt.Table")
}
case ShowCreateView:
ctx.WriteKeyWord("CREATE VIEW ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore ShowStmt.VIEW")
}
case ShowCreateDatabase:
ctx.WriteKeyWord("CREATE DATABASE ")
if n.IfNotExists {
ctx.WriteKeyWord("IF NOT EXISTS ")
}
ctx.WriteName(n.DBName)
case ShowCreateUser:
ctx.WriteKeyWord("CREATE USER ")
restoreUserName()
case ShowGrants:
ctx.WriteKeyWord("GRANTS")
if n.User != nil {
ctx.WriteKeyWord(" FOR ")
restoreUserName()
}
case ShowMasterStatus:
ctx.WriteKeyWord("MASTER STATUS")
case ShowProcessList:
restoreOptFull()
ctx.WriteKeyWord("PROCESSLIST")
case ShowStatsMeta:
ctx.WriteKeyWord("STATS_META")
if err := restoreShowLikeOrWhereOpt(); err != nil {
return err
}
case ShowStatsHistograms:
ctx.WriteKeyWord("STATS_HISTOGRAMS")
if err := restoreShowLikeOrWhereOpt(); err != nil {
return err
}
case ShowStatsBuckets:
ctx.WriteKeyWord("STATS_BUCKETS")
if err := restoreShowLikeOrWhereOpt(); err != nil {
return err
}
case ShowStatsHealthy:
ctx.WriteKeyWord("STATS_HEALTHY")
if err := restoreShowLikeOrWhereOpt(); err != nil {
return err
}
case ShowProfiles:
ctx.WriteKeyWord("PROFILES")
case ShowPrivileges:
ctx.WriteKeyWord("PRIVILEGES")
// ShowTargetFilterable
default:
switch n.Tp {
case ShowEngines:
ctx.WriteKeyWord("ENGINES")
case ShowDatabases:
ctx.WriteKeyWord("DATABASES")
case ShowCharset:
ctx.WriteKeyWord("CHARSET")
case ShowTables:
restoreOptFull()
ctx.WriteKeyWord("TABLES")
restoreShowDatabaseNameOpt()
case ShowTableStatus:
ctx.WriteKeyWord("TABLE STATUS")
restoreShowDatabaseNameOpt()
case ShowIndex:
// here can be INDEX INDEXES KEYS
// FROM or IN
ctx.WriteKeyWord("INDEX IN ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while resotre ShowStmt.Table")
} // TODO: remember to check this case
case ShowColumns: // equivalent to SHOW FIELDS
restoreOptFull()
ctx.WriteKeyWord("COLUMNS")
if n.Table != nil {
// FROM or IN
ctx.WriteKeyWord(" IN ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while resotre ShowStmt.Table")
}
}
restoreShowDatabaseNameOpt()
case ShowWarnings:
ctx.WriteKeyWord("WARNINGS")
case ShowErrors:
ctx.WriteKeyWord("ERRORS")
case ShowVariables:
restoreGlobalScope()
ctx.WriteKeyWord("VARIABLES")
case ShowStatus:
restoreGlobalScope()
ctx.WriteKeyWord("STATUS")
case ShowCollation:
ctx.WriteKeyWord("COLLATION")
case ShowTriggers:
ctx.WriteKeyWord("TRIGGERS")
restoreShowDatabaseNameOpt()
case ShowProcedureStatus:
ctx.WriteKeyWord("PROCEDURE STATUS")
case ShowEvents:
ctx.WriteKeyWord("EVENTS")
restoreShowDatabaseNameOpt()
case ShowPlugins:
ctx.WriteKeyWord("PLUGINS")
default:
return errors.New("Unknown ShowStmt type")
}
restoreShowLikeOrWhereOpt()
}
return nil
}
// Accept implements Node Accept interface.
......
......@@ -158,7 +158,7 @@ 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 {
if err := n.Op.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred when restore BinaryOperationExpr.Op")
}
if err := n.R.Restore(ctx); err != nil {
......@@ -347,7 +347,12 @@ type SubqueryExpr struct {
// Restore implements Node interface.
func (n *SubqueryExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WritePlain("(")
if err := n.Query.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore SubqueryExpr.Query")
}
ctx.WritePlain(")")
return nil
}
// Format the ExprNode into a Writer.
......@@ -388,7 +393,21 @@ type CompareSubqueryExpr struct {
// Restore implements Node interface.
func (n *CompareSubqueryExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
if err := n.L.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore CompareSubqueryExpr.L")
}
if err := n.Op.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore CompareSubqueryExpr.Op")
}
if n.All {
ctx.WriteKeyWord("ALL ")
} else {
ctx.WriteKeyWord("ANY ")
}
if err := n.R.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore CompareSubqueryExpr.R")
}
return nil
}
// Format the ExprNode into a Writer.
......@@ -569,7 +588,15 @@ type ExistsSubqueryExpr struct {
// Restore implements Node interface.
func (n *ExistsSubqueryExpr) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
if n.Not {
ctx.WriteKeyWord("NOT EXISTS ")
} else {
ctx.WriteKeyWord("EXISTS ")
}
if err := n.Sel.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore ExistsSubqueryExpr.Sel")
}
return nil
}
// Format the ExprNode into a Writer.
......@@ -615,21 +642,22 @@ func (n *PatternInExpr) Restore(ctx *RestoreCtx) error {
} else {
ctx.WriteKeyWord(" IN ")
}
ctx.WritePlain("(")
for i, expr := range n.List {
if i != 0 {
ctx.WritePlain(",")
}
if err := expr.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore PatternInExpr.List[%d]", i)
}
}
if n.Sel != nil {
if err := n.Sel.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore PatternInExpr.Sel")
}
} else {
ctx.WritePlain("(")
for i, expr := range n.List {
if i != 0 {
ctx.WritePlain(",")
}
if err := expr.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore PatternInExpr.List[%d]", i)
}
}
ctx.WritePlain(")")
}
ctx.WritePlain(")")
return nil
}
......@@ -817,6 +845,12 @@ func (n *PatternLikeExpr) Restore(ctx *RestoreCtx) error {
return errors.Annotate(err, "An error occurred while restore PatternLikeExpr.Pattern")
}
escape := string(n.Escape)
if escape != "\\" {
ctx.WriteKeyWord(" ESCAPE ")
ctx.WriteString(escape)
}
return nil
}
......@@ -1071,7 +1105,7 @@ type UnaryOperationExpr struct {
// Restore implements Node interface.
func (n *UnaryOperationExpr) Restore(ctx *RestoreCtx) error {
if err := n.Op.Restore(ctx.In); err != nil {
if err := n.Op.Restore(ctx); err != nil {
return errors.Trace(err)
}
if err := n.V.Restore(ctx); err != nil {
......
......@@ -16,6 +16,7 @@ package ast
import (
"fmt"
"io"
"strings"
"github.com/pingcap/errors"
. "github.com/pingcap/parser/format"
......@@ -340,15 +341,7 @@ func (n *FuncCallExpr) Restore(ctx *RestoreCtx) error {
}
ctx.WriteKeyWord(" USING ")
ctx.WriteKeyWord(n.Args[1].GetType().Charset)
case "adddate":
if err := n.Args[0].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore FuncCallExpr")
}
ctx.WritePlain(", ")
if err := n.Args[1].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore FuncCallExpr")
}
case "date_add":
case "adddate", "subdate", "date_add", "date_sub":
if err := n.Args[0].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore FuncCallExpr")
}
......@@ -413,6 +406,15 @@ func (n *FuncCallExpr) Restore(ctx *RestoreCtx) error {
return errors.Annotatef(err, "An error occurred while restore FuncCallExpr")
}
}
case "timestampdiff", "timestampadd":
ctx.WriteKeyWord(n.Args[0].(ValueExpr).GetString())
for i := 1; i < len(n.Args); {
ctx.WritePlain(", ")
if err := n.Args[i].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore FuncCallExpr")
}
i++
}
default:
for i, argv := range n.Args {
if i != 0 {
......@@ -644,12 +646,28 @@ func (n *AggregateFuncExpr) Restore(ctx *RestoreCtx) error {
if n.Distinct {
ctx.WriteKeyWord("DISTINCT ")
}
for i, argv := range n.Args {
if i != 0 {
ctx.WritePlain(", ")
switch strings.ToLower(n.F) {
case "group_concat":
for i := 0; i < len(n.Args)-1; i++ {
if i != 0 {
ctx.WritePlain(", ")
}
if err := n.Args[i].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AggregateFuncExpr.Args[%d]", i)
}
}
ctx.WriteKeyWord(" SEPARATOR ")
if err := n.Args[len(n.Args)-1].Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore AggregateFuncExpr.Args SEPARATOR")
}
if err := argv.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AggregateFuncExpr.Args %d", i)
default:
for i, argv := range n.Args {
if i != 0 {
ctx.WritePlain(", ")
}
if err := argv.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AggregateFuncExpr.Args[%d]", i)
}
}
}
ctx.WritePlain(")")
......
......@@ -44,6 +44,8 @@ var (
_ StmtNode = &UseStmt{}
_ StmtNode = &FlushStmt{}
_ StmtNode = &KillStmt{}
_ StmtNode = &CreateBindingStmt{}
_ StmtNode = &DropBindingStmt{}
_ Node = &PrivElem{}
_ Node = &VariableAssignment{}
......@@ -685,6 +687,64 @@ func (n *DropUserStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}
// CreateBindingStmt creates sql binding hint.
type CreateBindingStmt struct {
stmtNode
GlobalScope bool
OriginSel StmtNode
HintedSel StmtNode
}
func (n *CreateBindingStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
func (n *CreateBindingStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*CreateBindingStmt)
selnode, ok := n.OriginSel.Accept(v)
if !ok {
return n, false
}
n.OriginSel = selnode.(*SelectStmt)
hintedSelnode, ok := n.HintedSel.Accept(v)
if !ok {
return n, false
}
n.HintedSel = hintedSelnode.(*SelectStmt)
return v.Leave(n)
}
// DropBindingStmt deletes sql binding hint.
type DropBindingStmt struct {
stmtNode
GlobalScope bool
OriginSel StmtNode
}
func (n *DropBindingStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
}
func (n *DropBindingStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*DropBindingStmt)
selnode, ok := n.OriginSel.Accept(v)
if !ok {
return n, false
}
n.OriginSel = selnode.(*SelectStmt)
return v.Leave(n)
}
// DoStmt is the struct for DO statement.
type DoStmt struct {
stmtNode
......@@ -985,7 +1045,20 @@ type TableOptimizerHint struct {
// Restore implements Node interface.
func (n *TableOptimizerHint) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WriteKeyWord(n.HintName.String())
ctx.WritePlain("(")
if n.HintName.L == "max_execution_time" {
ctx.WritePlainf("%d", n.MaxExecutionTime)
} else {
for i, table := range n.Tables {
if i != 0 {
ctx.WritePlain(", ")
}
ctx.WriteName(table.String())
}
}
ctx.WritePlain(")")
return nil
}
// Accept implements Node Accept interface.
......
......@@ -19,3 +19,12 @@ jobs:
- run:
name: "Build & Test"
command: make test
- run:
name: "Integration Test"
command: |
cd /go/src/github.com/pingcap/
git clone git@github.com:pingcap/tidb.git
cd tidb
rm go.sum
GO111MODULE=on go mod edit -replace github.com/pingcap/parser=github.com/${CIRCLE_PR_USERNAME:-$CIRCLE_PROJECT_USERNAME}/${CIRCLE_PR_REPONAME:-$CIRCLE_PROJECT_REPONAME}@$CIRCLE_SHA1
make test
......@@ -2,15 +2,15 @@ module github.com/pingcap/parser
require (
github.com/cznic/golex v0.0.0-20181122101858-9c343928389c // indirect
github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548
github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65
github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8
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-20190102082844-67f458068fc8
github.com/pingcap/errors v0.11.0
github.com/pingcap/tidb v0.0.0-20180108134023-971629b9477a
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323
github.com/pingcap/tidb v0.0.0-20190108123336-c68ee7318319
github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7
github.com/sirupsen/logrus v1.2.0
golang.org/x/text v0.3.0
)
......@@ -23,12 +23,14 @@ github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbp
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
github.com/cznic/golex v0.0.0-20181122101858-9c343928389c h1:G8zTsaqyVfIHpgMFcGgdbhHSFhlNc77rAKkhVbQ9kQg=
github.com/cznic/golex v0.0.0-20181122101858-9c343928389c/go.mod h1:+bmmJDNmKlhWNG+gwWCkaBoTy39Fs+bzRxVBzoTQbIc=
github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7 h1:y+DH9ARrWiiNBV+6waYP2IPcsRbxdU1qsnycPfShF4c=
github.com/cznic/mathutil v0.0.0-20181021201202-eba54fb065b7/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548 h1:iwZdTE0PVqJCos1vaoKsclOGD3ADKpshg3SRtYBbwso=
github.com/cznic/mathutil v0.0.0-20181122101859-297441e03548/go.mod h1:e6NPNENfs9mPDVNRekM7lKScauxd5kXTr1Mfyig6TDM=
github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1 h1:uWcWCkSP+E1w1z8r082miT+c+9vzg+5UdrgGCo15lMo=
github.com/cznic/parser v0.0.0-20160622100904-31edd927e5b1/go.mod h1:2B43mz36vGZNZEwkWi8ayRSSUXLfjL8OkbzwW4NcPMM=
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65 h1:hxuZop6tSoOi0sxFzoGGYdRqNrPubyaIf9KoBG9tPiE=
github.com/cznic/sortutil v0.0.0-20150617083342-4c7342852e65/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8 h1:LpMLYGyy67BoAFGda1NeOBQwqlv7nUXpm+rIVHGxZZ4=
github.com/cznic/sortutil v0.0.0-20181122101858-f5f958428db8/go.mod h1:q2w6Bg5jeox1B+QkJ6Wp/+Vn0G/bo3f1uY7Fn3vivIQ=
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186 h1:0rkFMAbn5KBKNpJyHQ6Prb95vIKanmAe62KxsrN+sqA=
github.com/cznic/strutil v0.0.0-20171016134553-529a34b1c186/go.mod h1:AHHPPPXTw0h6pVabbcbyGRK1DckRn7r/STdZEeIDzZc=
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a h1:N2rDAvHuM46OGscJkGX4Dw4BBqZgg6mGNGLYs5utVVo=
......@@ -119,13 +121,14 @@ github.com/pingcap/kvproto v0.0.0-20181203065228-c14302da291c/go.mod h1:Ja9XPjot
github.com/pingcap/parser v0.0.0-20190108044100-02812c3c22e7/go.mod h1:1FNvfp9+J0wvc4kl8eGNh7Rqrxveg15jJoWo/a0uHwA=
github.com/pingcap/pd v2.1.0-rc.4+incompatible h1:/buwGk04aHO5odk/+O8ZOXGs4qkUjYTJ2UpCJXna8NE=
github.com/pingcap/pd v2.1.0-rc.4+incompatible/go.mod h1:nD3+EoYes4+aNNODO99ES59V83MZSI+dFbhyr667a0E=
github.com/pingcap/tidb v0.0.0-20180108134023-971629b9477a h1:+UaU3bgZJtmgn1IJOWQU63z+HaVNhOfyxT8puMZ32rc=
github.com/pingcap/tidb v0.0.0-20180108134023-971629b9477a/go.mod h1:ytMJRc0YwJxWtwJpuu60xXIXHSTGwi1jhmt3TnxifYw=
github.com/pingcap/tidb-tools v2.1.1-0.20181218072513-b2235d442b06+incompatible h1:Bsd+NHosPVowEGB3BCx+2d8wUQGDTXSSC5ljeNS6cXo=
github.com/pingcap/tidb-tools v2.1.1-0.20181218072513-b2235d442b06+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM=
github.com/pingcap/tidb v0.0.0-20190108123336-c68ee7318319 h1:ltRU5YUxYpW29ywVKnFXIRRTnY6r2cYxauB79L5gU2E=
github.com/pingcap/tidb v0.0.0-20190108123336-c68ee7318319/go.mod h1:qXpdYNt83vgSegvc/TNcxKGiAo4Pa4EtIJl0ka7yGXE=
github.com/pingcap/tidb-tools v2.1.3-0.20190104033906-883b07a04a73+incompatible h1:Ba48wwPwPq5hd1kkQpgua49dqB5cthC2zXVo7fUUDec=
github.com/pingcap/tidb-tools v2.1.3-0.20190104033906-883b07a04a73+incompatible/go.mod h1:XGdcy9+yqlDSEMTpOXnwf3hiTeqrV6MN/u1se9N8yIM=
github.com/pingcap/tipb v0.0.0-20170310053819-1043caee48da/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323 h1:mRKKzRjDNaUNPnAkPAHnRqpNmwNWBX1iA+hxlmvQ93I=
github.com/pingcap/tipb v0.0.0-20181012112600-11e33c750323/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7 h1:wnjdQRhybddDesBVBKyOLUPgDaOFdtqA92pduBgWvVQ=
github.com/pingcap/tipb v0.0.0-20190107072121-abbec73437b7/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
......
......@@ -533,6 +533,8 @@ var tokenMap = map[string]int{
"YEAR": yearType,
"YEAR_MONTH": yearMonth,
"ZEROFILL": zerofill,
"BINDING": binding,
"BINDINGS": bindings,
}
// See https://dev.mysql.com/doc/refman/5.7/en/function-resolution.html for details
......
......@@ -511,6 +511,11 @@ func (m SQLMode) HasNoAutoCreateUserMode() bool {
return m&ModeNoAutoCreateUser == ModeNoAutoCreateUser
}
// HasAllowInvalidDatesMode detects if 'ALLOW_INVALID_DATES' mode is set in SQLMode
func (m SQLMode) HasAllowInvalidDatesMode() bool {
return m&ModeAllowInvalidDates == ModeAllowInvalidDates
}
// consts for sql modes.
const (
ModeNone SQLMode = 0
......@@ -546,6 +551,7 @@ const (
ModeHighNotPrecedence
ModeNoEngineSubstitution
ModePadCharToFullLength
ModeAllowInvalidDates
)
// FormatSQLModeStr re-format 'SQL_MODE' variable.
......@@ -623,6 +629,7 @@ var Str2SQLMode = map[string]SQLMode{
"HIGH_NOT_PRECEDENCE": ModeHighNotPrecedence,
"NO_ENGINE_SUBSTITUTION": ModeNoEngineSubstitution,
"PAD_CHAR_TO_FULL_LENGTH": ModePadCharToFullLength,
"ALLOW_INVALID_DATES": ModeAllowInvalidDates,
}
// CombinationSQLMode is the special modes that provided as shorthand for combinations of mode values.
......
......@@ -901,7 +901,7 @@ const (
ErrWindowFrameEndIllegal = 3585
ErrWindowFrameIllegal = 3586
ErrWindowRangeFrameOrderType = 3587
ErrWindowRangeFrameTEMPORALType = 3588
ErrWindowRangeFrameTemporalType = 3588
ErrWindowRangeFrameNumericType = 3589
ErrWindowRangeBoundNotConstant = 3590
ErrWindowDuplicateName = 3591
......@@ -915,9 +915,18 @@ const (
ErrWindowFunctionIgnoresFrame = 3599
// TiDB self-defined errors.
ErrMemExceedThreshold = 8001
ErrForUpdateCantRetry = 8002
ErrAdminCheckTable = 8003
ErrMemExceedThreshold = 8001
ErrForUpdateCantRetry = 8002
ErrAdminCheckTable = 8003
ErrInvalidPluginID = 8101
ErrInvalidPluginManifest = 8102
ErrInvalidPluginName = 8103
ErrInvalidPluginVersion = 8104
ErrDuplicatePlugin = 8105
ErrInvalidPluginSysVarName = 8106
ErrRequireVersionCheckFail = 8107
ErrUnsupportedReloadPlugin = 8018
ErrUnsupportedReloadPluginVar = 8019
// TiKV/PD errors.
ErrPDServerTimeout = 9001
......
......@@ -250,7 +250,7 @@ var MySQLErrName = map[uint16]string{
ErrWrongTypeForVar: "Incorrect argument type to variable '%-.64s'",
ErrVarCantBeRead: "Variable '%-.64s' can only be set, not read",
ErrCantUseOptionHere: "Incorrect usage/placement of '%s'",
ErrNotSupportedYet: "This version of MySQL doesn't yet support '%s'",
ErrNotSupportedYet: "This version of TiDB doesn't yet support '%s'",
ErrMasterFatalErrorReadingBinlog: "Got fatal error %d from master when reading data from binary log: '%-.320s'",
ErrSlaveIgnoredTable: "Slave SQL thread ignored the query because of replicate-*-table rules",
ErrIncorrectGlobalLocalVar: "Variable '%-.192s' is a %s variable",
......@@ -898,7 +898,7 @@ var MySQLErrName = map[uint16]string{
ErrWindowFrameEndIllegal: "Window '%s': frame end cannot be UNBOUNDED PRECEDING.",
ErrWindowFrameIllegal: "Window '%s': frame start or end is negative, NULL or of non-integral type",
ErrWindowRangeFrameOrderType: "Window '%s' with RANGE N PRECEDING/FOLLOWING frame requires exactly one ORDER BY expression, of numeric or temporal type",
ErrWindowRangeFrameTEMPORALType: "Window '%s' with RANGE frame has ORDER BY expression of datetime type. Only INTERVAL bound value allowed.",
ErrWindowRangeFrameTemporalType: "Window '%s' with RANGE frame has ORDER BY expression of datetime type. Only INTERVAL bound value allowed.",
ErrWindowRangeFrameNumericType: "Window '%s' with RANGE frame has ORDER BY expression of numeric type, INTERVAL bound value not allowed.",
ErrWindowRangeBoundNotConstant: "Window '%s' has a non-constant frame bound.",
ErrWindowDuplicateName: "Window '%s' is defined twice.",
......@@ -916,6 +916,16 @@ var MySQLErrName = map[uint16]string{
ErrForUpdateCantRetry: "[%d] can not retry select for update statement",
ErrAdminCheckTable: "TiDB admin check table failed.",
ErrInvalidPluginID: "Wrong plugin id: %s, valid plugin id is [name]-[version], both name and version should not contain '-'",
ErrInvalidPluginManifest: "Cannot read plugin %s's manifest",
ErrInvalidPluginName: "Plugin load with %s but got wrong name %s",
ErrInvalidPluginVersion: "Plugin load with %s but got %s",
ErrDuplicatePlugin: "Plugin [%s] is redeclared",
ErrInvalidPluginSysVarName: "Plugin %s's sysVar %s must start with its plugin name %s",
ErrRequireVersionCheckFail: "Plugin %s require %s be %v but got %v",
ErrUnsupportedReloadPlugin: "Plugin %s isn't loaded so cannot be reloaded",
ErrUnsupportedReloadPluginVar: "Reload plugin with different sysVar is unsupported %v",
// TiKV/PD errors.
ErrPDServerTimeout: "PD server timeout",
ErrTiKVServerTimeout: "TiKV server timeout",
......
......@@ -18,6 +18,7 @@ import (
"io"
"github.com/pingcap/errors"
. "github.com/pingcap/parser/format"
)
// Op is opcode type.
......@@ -102,9 +103,9 @@ func (o Op) String() string {
}
var opsLiteral = map[Op]string{
LogicAnd: "&&",
LogicOr: "||",
LogicXor: "^",
LogicAnd: " AND ",
LogicOr: " OR ",
LogicXor: " XOR ",
LeftShift: "<<",
RightShift: ">>",
GE: ">=",
......@@ -140,9 +141,9 @@ func (o Op) Format(w io.Writer) {
}
// Restore the Op into a Writer
func (o Op) Restore(w io.Writer) error {
func (o Op) Restore(ctx *RestoreCtx) error {
if v, ok := opsLiteral[o]; ok {
fmt.Fprint(w, v)
ctx.WriteKeyWord(v)
return nil
}
return errors.Errorf("Invalid opcode type %d during restoring AST to SQL text", o)
......
......@@ -423,6 +423,8 @@ import (
value "VALUE"
variables "VARIABLES"
view "VIEW"
binding "BINDING"
bindings "BINDINGS"
warnings "WARNINGS"
identSQLErrors "ERRORS"
week "WEEK"
......@@ -573,6 +575,7 @@ import (
CreateUserStmt "CREATE User statement"
CreateDatabaseStmt "Create Database Statement"
CreateIndexStmt "CREATE INDEX statement"
CreateBindingStmt "CREATE BINDING statement"
DoStmt "Do statement"
DropDatabaseStmt "DROP DATABASE statement"
DropIndexStmt "DROP INDEX statement"
......@@ -580,6 +583,7 @@ import (
DropTableStmt "DROP TABLE statement"
DropUserStmt "DROP USER"
DropViewStmt "DROP VIEW statement"
DropBindingStmt "DROP BINDING statement"
DeallocateStmt "Deallocate prepared statement"
DeleteFromStmt "DELETE FROM statement"
EmptyStmt "empty statement"
......@@ -723,6 +727,7 @@ import (
PartitionDefinitionListOpt "Partition definition list option"
PartitionOpt "Partition option"
PartitionNameList "Partition name list"
PartitionNameListOpt "table partition names list optional"
PartitionNumOpt "PARTITION NUM option"
PartDefValuesOpt "VALUES {LESS THAN {(expr | value_list) | MAXVALUE} | IN {value_list}"
PartDefOptionsOpt "PartDefOptionList option"
......@@ -1084,7 +1089,7 @@ AlterTableSpec:
Tp: ast.AlterTableDropPartition,
Name: $3,
}
}
}
| "TRUNCATE" "PARTITION" Identifier
{
$$ = &ast.AlterTableSpec{
......@@ -2988,7 +2993,7 @@ UnReservedKeyword:
| "COLLATION" | "COMMENT" | "AVG_ROW_LENGTH" | "CONNECTION" | "CHECKSUM" | "COMPRESSION" | "KEY_BLOCK_SIZE" | "MASTER" | "MAX_ROWS"
| "MIN_ROWS" | "NATIONAL" | "ROW_FORMAT" | "QUARTER" | "GRANTS" | "TRIGGERS" | "DELAY_KEY_WRITE" | "ISOLATION" | "JSON"
| "REPEATABLE" | "RESPECT" | "COMMITTED" | "UNCOMMITTED" | "ONLY" | "SERIALIZABLE" | "LEVEL" | "VARIABLES" | "SQL_CACHE" | "INDEXES" | "PROCESSLIST"
| "SQL_NO_CACHE" | "DISABLE" | "ENABLE" | "REVERSE" | "PRIVILEGES" | "NO" | "BINLOG" | "FUNCTION" | "VIEW" | "MODIFY" | "EVENTS" | "PARTITIONS"
| "SQL_NO_CACHE" | "DISABLE" | "ENABLE" | "REVERSE" | "PRIVILEGES" | "NO" | "BINLOG" | "FUNCTION" | "VIEW" | "BINDING" | "BINDINGS" | "MODIFY" | "EVENTS" | "PARTITIONS"
| "NONE" | "NULLS" | "SUPER" | "EXCLUSIVE" | "STATS_PERSISTENT" | "ROW_COUNT" | "COALESCE" | "MONTH" | "PROCESS" | "PROFILES"
| "MICROSECOND" | "MINUTE" | "PLUGINS" | "PRECEDING" | "QUERY" | "QUERIES" | "SECOND" | "SEPARATOR" | "SHARE" | "SHARED" | "SLOW" | "MAX_CONNECTIONS_PER_HOUR" | "MAX_QUERIES_PER_HOUR" | "MAX_UPDATES_PER_HOUR"
| "MAX_USER_CONNECTIONS" | "REPLICATION" | "CLIENT" | "SLAVE" | "RELOAD" | "TEMPORARY" | "ROUTINE" | "EVENT" | "ALGORITHM" | "DEFINER" | "INVOKER" | "MERGE" | "TEMPTABLE" | "UNDEFINED" | "SECURITY" | "CASCADED" | "RECOVER"
......@@ -4719,7 +4724,7 @@ WindowFrameStart:
}
| "INTERVAL" Expression TimeUnit "PRECEDING"
{
$$ = ast.FrameBound{Type: ast.Preceding, Expr: ast.NewValueExpr($2), Unit: ast.NewValueExpr($3),}
$$ = ast.FrameBound{Type: ast.Preceding, Expr: $2, Unit: ast.NewValueExpr($3),}
}
| "CURRENT" "ROW"
{
......@@ -4751,7 +4756,7 @@ WindowFrameBound:
}
| "INTERVAL" Expression TimeUnit "FOLLOWING"
{
$$ = ast.FrameBound{Type: ast.Following, Expr: ast.NewValueExpr($2), Unit: ast.NewValueExpr($3),}
$$ = ast.FrameBound{Type: ast.Following, Expr: $2, Unit: ast.NewValueExpr($3),}
}
OptWindowingClause:
......@@ -4937,11 +4942,12 @@ TableRef:
}
TableFactor:
TableName TableAsNameOpt IndexHintListOpt
TableName PartitionNameListOpt TableAsNameOpt IndexHintListOpt
{
tn := $1.(*ast.TableName)
tn.IndexHints = $3.([]*ast.IndexHint)
$$ = &ast.TableSource{Source: tn, AsName: $2.(model.CIStr)}
tn.PartitionNames = $2.([]model.CIStr)
tn.IndexHints = $4.([]*ast.IndexHint)
$$ = &ast.TableSource{Source: tn, AsName: $3.(model.CIStr)}
}
| '(' SelectStmt ')' TableAsName
{
......@@ -4959,6 +4965,16 @@ TableFactor:
$$ = $2
}
PartitionNameListOpt:
/* empty */
{
$$ = []model.CIStr{}
}
| "PARTITION" '(' PartitionNameList ')'
{
$$ = $3
}
TableAsNameOpt:
{
$$ = model.CIStr{}
......@@ -5764,11 +5780,26 @@ AdminStmt:
Index: string($5),
}
}
| "ADMIN" "RESTORE" "TABLE" "BY" "JOB" NumList
| "ADMIN" "RESTORE" "TABLE" "BY" "JOB" NUM
{
$$ = &ast.AdminStmt{
Tp: ast.AdminRestoreTable,
JobIDs: $6.([]int64),
JobIDs: []int64{$6.(int64)},
}
}
| "ADMIN" "RESTORE" "TABLE" TableName
{
$$ = &ast.AdminStmt{
Tp: ast.AdminRestoreTable,
Tables: []*ast.TableName{$4.(*ast.TableName)},
}
}
| "ADMIN" "RESTORE" "TABLE" TableName NUM
{
$$ = &ast.AdminStmt{
Tp: ast.AdminRestoreTable,
Tables: []*ast.TableName{$4.(*ast.TableName)},
JobNumber: $5.(int64),
}
}
| "ADMIN" "CLEANUP" "INDEX" TableName Identifier
......@@ -5884,7 +5915,7 @@ ShowStmt:
{
stmt := $2.(*ast.ShowStmt)
if $3 != nil {
if x, ok := $3.(*ast.PatternLikeExpr); ok {
if x, ok := $3.(*ast.PatternLikeExpr); ok && x.Expr == nil {
stmt.Pattern = x
} else {
stmt.Where = $3.(ast.ExprNode)
......@@ -5899,6 +5930,13 @@ ShowStmt:
Table: $4.(*ast.TableName),
}
}
| "SHOW" "CREATE" "VIEW" TableName
{
$$ = &ast.ShowStmt{
Tp: ast.ShowCreateView,
Table: $4.(*ast.TableName),
}
}
| "SHOW" "CREATE" "DATABASE" IfNotExists DBName
{
$$ = &ast.ShowStmt{
......@@ -5947,7 +5985,7 @@ ShowStmt:
Tp: ast.ShowStatsMeta,
}
if $3 != nil {
if x, ok := $3.(*ast.PatternLikeExpr); ok {
if x, ok := $3.(*ast.PatternLikeExpr); ok && x.Expr == nil {
stmt.Pattern = x
} else {
stmt.Where = $3.(ast.ExprNode)
......@@ -5961,7 +5999,7 @@ ShowStmt:
Tp: ast.ShowStatsHistograms,
}
if $3 != nil {
if x, ok := $3.(*ast.PatternLikeExpr); ok {
if x, ok := $3.(*ast.PatternLikeExpr); ok && x.Expr == nil {
stmt.Pattern = x
} else {
stmt.Where = $3.(ast.ExprNode)
......@@ -5975,7 +6013,7 @@ ShowStmt:
Tp: ast.ShowStatsBuckets,
}
if $3 != nil {
if x, ok := $3.(*ast.PatternLikeExpr); ok {
if x, ok := $3.(*ast.PatternLikeExpr); ok && x.Expr == nil {
stmt.Pattern = x
} else {
stmt.Where = $3.(ast.ExprNode)
......@@ -5989,7 +6027,7 @@ ShowStmt:
Tp: ast.ShowStatsHealthy,
}
if $3 != nil {
if x, ok := $3.(*ast.PatternLikeExpr); ok {
if x, ok := $3.(*ast.PatternLikeExpr); ok && x.Expr == nil {
stmt.Pattern = x
} else {
stmt.Where = $3.(ast.ExprNode)
......@@ -6102,6 +6140,13 @@ ShowTargetFilterable:
GlobalScope: $1.(bool),
}
}
| GlobalScope "BINDINGS"
{
$$ = &ast.ShowStmt{
Tp: ast.ShowBindings,
GlobalScope: $1.(bool),
}
}
| "COLLATION"
{
$$ = &ast.ShowStmt{
......@@ -6277,6 +6322,7 @@ Statement:
| CreateTableStmt
| CreateViewStmt
| CreateUserStmt
| CreateBindingStmt
| DoStmt
| DropDatabaseStmt
| DropIndexStmt
......@@ -6284,6 +6330,7 @@ Statement:
| DropViewStmt
| DropUserStmt
| DropStatsStmt
| DropBindingStmt
| FlushStmt
| GrantStmt
| InsertIntoStmt
......@@ -7285,6 +7332,55 @@ HashString:
$$ = $1
}
/*******************************************************************
*
* Create Binding Statement
*
* Example:
* CREATE GLOBAL BINDING FOR select Col1,Col2 from table USING select Col1,Col2 from table use index(Col1)
*******************************************************************/
CreateBindingStmt:
"CREATE" GlobalScope "BINDING" "FOR" SelectStmt "USING" SelectStmt
{
startOffset := parser.startOffset(&yyS[yypt-2])
endOffset := parser.startOffset(&yyS[yypt-1])
selStmt := $5.(*ast.SelectStmt)
selStmt.SetText(strings.TrimSpace(parser.src[startOffset:endOffset]))
startOffset = parser.startOffset(&yyS[yypt])
hintedSelStmt := $7.(*ast.SelectStmt)
hintedSelStmt.SetText(strings.TrimSpace(parser.src[startOffset:]))
x := &ast.CreateBindingStmt {
OriginSel: selStmt,
HintedSel: hintedSelStmt,
GlobalScope: $2.(bool),
}
$$ = x
}
/*******************************************************************
*
* Drop Binding Statement
*
* Example:
* DROP GLOBAL BINDING FOR select Col1,Col2 from table
*******************************************************************/
DropBindingStmt:
"DROP" GlobalScope "BINDING" "FOR" SelectStmt
{
startOffset := parser.startOffset(&yyS[yypt])
selStmt := $5.(*ast.SelectStmt)
selStmt.SetText(strings.TrimSpace(parser.src[startOffset:]))
x := &ast.DropBindingStmt {
OriginSel: selStmt,
GlobalScope: $2.(bool),
}
$$ = x
}
/*************************************************************************************
* Grant statement
* See https://dev.mysql.com/doc/refman/5.7/en/grant.html
......
......@@ -82,6 +82,7 @@ const (
ClassJSON
ClassTiKV
ClassSession
ClassPlugin
// Add more as needed.
)
......@@ -109,6 +110,7 @@ var errClz2Str = map[ErrClass]string{
ClassJSON: "json",
ClassTiKV: "tikv",
ClassSession: "session",
ClassPlugin: "plugin",
}
// String implements fmt.Stringer interface.
......
......@@ -195,6 +195,61 @@ func (ft *FieldType) String() string {
return strings.Join(strs, " ")
}
// Restore implements Node interface.
func (ft *FieldType) Restore(ctx *format.RestoreCtx) error {
ctx.WriteKeyWord(TypeToStr(ft.Tp, ft.Charset))
switch ft.Tp {
case mysql.TypeEnum, mysql.TypeSet:
ctx.WritePlain("(")
for i, e := range ft.Elems {
if i != 0 {
ctx.WritePlain(",")
}
ctx.WriteString(e)
}
ctx.WritePlain(")")
case mysql.TypeTimestamp, mysql.TypeDatetime, mysql.TypeDuration:
if ft.Flen > 0 && ft.Decimal > 0 {
ctx.WritePlainf("(%d)", ft.Decimal)
}
case mysql.TypeDouble, mysql.TypeFloat:
if ft.Flen > 0 && ft.Decimal > 0 {
ctx.WritePlainf("(%d,%d)", ft.Flen, ft.Decimal)
}
case mysql.TypeNewDecimal:
if ft.Flen > 0 && ft.Decimal > 0 {
ctx.WritePlainf("(%d,%d)", ft.Flen, ft.Decimal)
}
case mysql.TypeBit, mysql.TypeShort, mysql.TypeTiny, mysql.TypeInt24, mysql.TypeLong, mysql.TypeLonglong, mysql.TypeVarchar, mysql.TypeString, mysql.TypeVarString:
if ft.Flen > 0 {
ctx.WritePlainf("(%d)", ft.Flen)
}
}
if mysql.HasUnsignedFlag(ft.Flag) {
ctx.WriteKeyWord(" UNSIGNED")
}
if mysql.HasZerofillFlag(ft.Flag) {
ctx.WriteKeyWord(" ZEROFILL")
}
if mysql.HasBinaryFlag(ft.Flag) && ft.Charset != charset.CharsetBin {
ctx.WriteKeyWord(" BINARY")
}
if IsTypeChar(ft.Tp) || IsTypeBlob(ft.Tp) {
if ft.Charset != "" && ft.Charset != charset.CharsetBin {
ctx.WriteKeyWord(" CHARACTER SET " + ft.Charset)
}
if ft.Collate != "" && ft.Collate != charset.CharsetBin {
ctx.WriteKeyWord(" COLLATE ")
ctx.WritePlain(ft.Collate)
}
}
return nil
}
// FormatAsCastType is used for write AST back to string.
func (ft *FieldType) FormatAsCastType(w io.Writer) {
switch ft.Tp {
......
......@@ -217,7 +217,7 @@ func (bj BinaryJSON) objectSearchKey(key []byte) (BinaryJSON, bool) {
idx := sort.Search(elemCount, func(i int) bool {
return bytes.Compare(bj.objectGetKey(i), key) >= 0
})
if idx < elemCount && bytes.Compare(bj.objectGetKey(idx), key) == 0 {
if idx < elemCount && bytes.Equal(bj.objectGetKey(idx), key) {
return bj.objectGetVal(idx), true
}
return BinaryJSON{}, false
......
......@@ -232,8 +232,7 @@ func (v weekBehaviour) test(flag weekBehaviour) bool {
}
func weekMode(mode int) weekBehaviour {
var weekFormat weekBehaviour
weekFormat = weekBehaviour(mode & 7)
weekFormat := weekBehaviour(mode & 7)
if (weekFormat & weekBehaviourMondayFirst) == 0 {
weekFormat ^= weekBehaviourFirstWeekday
}
......
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册