未验证 提交 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,41 +44,38 @@ 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)
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 {
Log.Debug("http.Get %s Error: %v", resource, err)
content = string(body)
}
defer resp.Body.Close()
} 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())
Log.Error("os.Open %s Error: %v", resource, err)
return content
}
}()
if err == nil {
defer fd.Close()
body, err = ioutil.ReadAll(fd)
if err != nil {
Log.Debug("ioutil.ReadAll %s Error: %v", resource, err)
Log.Error("ioutil.ReadAll %s Error: %v", resource, err)
} else {
content = string(body)
}
} else {
Log.Debug("os.Open %s Error: %v", resource, err)
}
}
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 ]
}
......@@ -6,4 +6,3 @@ load test_helper
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.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
}
......@@ -184,6 +184,7 @@ type TableName struct {
TableInfo *model.TableInfo
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,6 +642,11 @@ func (n *PatternInExpr) Restore(ctx *RestoreCtx) error {
} else {
ctx.WriteKeyWord(" IN ")
}
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 {
......@@ -624,12 +656,8 @@ func (n *PatternInExpr) Restore(ctx *RestoreCtx) error {
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")
}
}
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 ")
}
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")
}
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)
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
......@@ -918,6 +918,15 @@ const (
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"
......@@ -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 {
......
......@@ -47,8 +47,10 @@ type StatementContext struct {
// If IsDDLJobInQueue is true, it means the DDL job is in the queue of storage, and it can be handled by the DDL worker.
IsDDLJobInQueue bool
InInsertStmt bool
InUpdateOrDeleteStmt bool
InUpdateStmt bool
InDeleteStmt bool
InSelectStmt bool
InLoadDataStmt bool
IgnoreTruncate bool
IgnoreZeroInDate bool
DupKeyAsWarning bool
......@@ -61,6 +63,7 @@ type StatementContext struct {
PadCharToFullLength bool
BatchCheck bool
InNullRejectCheck bool
AllowInvalidDate bool
// mu struct holds variables that change during execution.
mu struct {
......@@ -379,3 +382,21 @@ func (sc *StatementContext) GetExecDetails() execdetails.ExecDetails {
sc.mu.Unlock()
return details
}
// ShouldClipToZero indicates whether values less than 0 should be clipped to 0 for unsigned integer types.
// This is the case for `insert`, `update`, `alter table` and `load data infile` statements, when not in strict SQL mode.
// see https://dev.mysql.com/doc/refman/5.7/en/out-of-range-and-overflow.html
func (sc *StatementContext) ShouldClipToZero() bool {
// TODO: Currently altering column of integer to unsigned integer is not supported.
// If it is supported one day, that case should be added here.
return sc.InInsertStmt || sc.InLoadDataStmt
}
// ShouldIgnoreOverflowError indicates whether we should ignore the error when type conversion overflows,
// so we can leave it for further processing like clipping values less than 0 to 0 for unsigned integer types.
func (sc *StatementContext) ShouldIgnoreOverflowError() bool {
if (sc.InInsertStmt && sc.TruncateAsWarning) || sc.InLoadDataStmt {
return true
}
return false
}
......@@ -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
......
此差异已折叠。
......@@ -31,6 +31,7 @@ import (
)
// These constants are used to identify the SQL statement type.
// Changing this list will require reviewing all calls to Preview.
const (
StmtSelect = iota
StmtStream
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册