提交 a1c7d035 编写于 作者: martianzhang's avatar martianzhang

fix issue #235 & update vendor

上级 7944a57e
......@@ -35,7 +35,7 @@ SOAR(SQL Optimizer And Rewriter) 是一个对 SQL 进行优化和改写的自动
## 交流与反馈
* 欢迎通过 Github Issues 提交问题报告与建议
* QQ 群:779359816(满) 758940447(新
* QQ 群:779359816(未满) 758940447(已满
* [Gitter](https://gitter.im/xiaomi-dba/soar) 推荐
![xiaomi_sa](https://raw.githubusercontent.com/XiaoMi/soar/master/doc/images/xiaomi_sa.png)
......
......@@ -1827,7 +1827,7 @@ func (q *Query4Audit) RuleIndexAttributeOrder() Rule {
for _, tiStmt := range q.TiStmt {
switch node := tiStmt.(type) {
case *tidb.CreateIndexStmt:
if len(node.IndexColNames) > 1 {
if len(node.IndexPartSpecifications) > 1 {
rule = HeuristicRules["KEY.004"]
break
}
......@@ -2808,7 +2808,7 @@ func (q *Query4Audit) RuleTooManyKeyParts() Rule {
return HeuristicRules["KEY.006"]
}
if constraint.Refer != nil && len(constraint.Refer.IndexColNames) > common.Config.MaxIdxColsCount {
if constraint.Refer != nil && len(constraint.Refer.IndexPartSpecifications) > common.Config.MaxIdxColsCount {
return HeuristicRules["KEY.006"]
}
}
......@@ -2823,7 +2823,7 @@ func (q *Query4Audit) RuleTooManyKeyParts() Rule {
}
if spec.Constraint.Refer != nil {
if len(spec.Constraint.Refer.IndexColNames) > common.Config.MaxIdxColsCount {
if len(spec.Constraint.Refer.IndexPartSpecifications) > common.Config.MaxIdxColsCount {
return HeuristicRules["KEY.006"]
}
}
......
......@@ -62,7 +62,7 @@ func NewQuery4Audit(sql string, options ...string) (*Query4Audit, error) {
}
// TODO: charset, collation
// tdib parser 语法解析
// tidb parser 语法解析
q.TiStmt, err = ast.TiParse(sql, charset, collation)
return q, err
}
......
......@@ -69,3 +69,18 @@ func TestIsIgnoreRule(t *testing.T) {
}
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
}
func TestNewQuery4Audit(t *testing.T) {
common.Log.Debug("Entering function: %s", common.GetFunctionName())
sqls := []string{
`SELECT CONVERT("abc" using gbk)`,
`SET NAMES gbk`,
}
for _, sql := range sqls {
_, err := NewQuery4Audit(sql)
if err != nil {
t.Errorf("SQL: %s, Error: %s", sql, err.Error())
}
}
common.Log.Debug("Exiting function: %s", common.GetFunctionName())
}
......@@ -38,6 +38,14 @@ func TiParse(sql, charset, collation string) ([]ast.StmtNode, error) {
p := parser.New()
sql = removeIncompatibleWords(sql)
stmt, warn, err := p.Parse(sql, charset, collation)
if err != nil {
// issue: https://github.com/XiaoMi/soar/issues/235
// TODO: bypass charset error, pingcap/parser not support so much charsets
if strings.Contains(err.Error(), "Unknown character set") {
err = nil
}
}
// TODO: bypass warning info
for _, w := range warn {
common.Log.Warn(w.Error())
......
.PHONY: all parser clean
ARCH:="`uname -s`"
MAC:="Darwin"
LINUX:="Linux"
all: fmt parser
all: parser.go fmt
test: parser.go fmt
test: fmt parser
sh test.sh
parser.go: parser.y
make parser
parser: bin/goyacc
bin/goyacc -o /dev/null parser.y
bin/goyacc -o parser.go parser.y 2>&1 | egrep "(shift|reduce)/reduce" | awk '{print} END {if (NR > 0) {print "Find conflict in parser.y. Please check y.output for more information."; exit 1;}}'
rm -f y.output
parser: parser.go hintparser.go
@if [ $(ARCH) = $(LINUX) ]; \
then \
sed -i -e 's|//line.*||' -e 's/yyEofCode/yyEOFCode/' parser.go; \
elif [ $(ARCH) = $(MAC) ]; \
then \
/usr/bin/sed -i "" 's|//line.*||' parser.go; \
/usr/bin/sed -i "" 's/yyEofCode/yyEOFCode/' parser.go; \
fi
%arser.go: prefix = $(@:parser.go=)
%arser.go: %arser.y bin/goyacc
@echo "bin/goyacc -o $@ -p yy$(prefix) -t $(prefix)Parser $<"
@bin/goyacc -o $@ -p yy$(prefix) -t $(prefix)Parser $< || ( rm -f $@ && echo 'Please check y.output for more information' && exit 1 )
@rm -f y.output
@awk 'BEGIN{print "// Code generated by goyacc DO NOT EDIT."} {print $0}' parser.go > tmp_parser.go && mv tmp_parser.go parser.go;
%arser_golden.y: %arser.y
@bin/goyacc -fmt -fmtout $@ $<
@(git diff --no-index --exit-code $< $@ && rm $@) || (mv $@ $< && >&2 echo "formatted $<" && exit 1)
bin/goyacc: goyacc/main.go
GO111MODULE=on go build -o bin/goyacc goyacc/main.go
bin/goyacc: goyacc/main.go goyacc/format_yacc.go
GO111MODULE=on go build -o bin/goyacc goyacc/main.go goyacc/format_yacc.go
fmt:
fmt: bin/goyacc parser_golden.y hintparser_golden.y
@echo "gofmt (simplify)"
@gofmt -s -l -w . 2>&1 | awk '{print} END{if(NR>0) {exit 1}}'
clean:
go clean -i ./...
rm -rf *.out
rm parser.go
rm -f parser.go hintparser.go
......@@ -7,6 +7,33 @@ TiDB SQL Parser
## How to use it
```go
import (
"fmt"
"github.com/pingcap/parser"
_ "github.com/pingcap/tidb/types/parser_driver"
)
// This example show how to parse a text sql into ast.
func example() {
// 0. make sure import parser_driver implemented by TiDB(user also can implement own driver by self).
// and add `import _ "github.com/pingcap/tidb/types/parser_driver"` in the head of file.
// 1. Create a parser. The parser is NOT goroutine safe and should
// not be shared among multiple goroutines. However, parser is also
// heavy, so each goroutine should reuse its own local instance if
// possible.
p := parser.New()
// 2. Parse a text SQL into AST([]ast.StmtNode).
stmtNodes, _, err := p.Parse("select * from tbl where id = 1", "", "")
// 3. Use AST to do cool things.
fmt.Println(stmtNodes[0], err)
}
```
See [https://godoc.org/github.com/pingcap/parser](https://godoc.org/github.com/pingcap/parser)
## How to update parser for TiDB
......
// Copyright 2019 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package ast
import (
. "github.com/pingcap/parser/format"
)
var _ StmtNode = &IndexAdviseStmt{}
// IndexAdviseStmt is used to advise indexes
type IndexAdviseStmt struct {
stmtNode
IsLocal bool
Path string
MaxMinutes uint64
MaxIndexNum *MaxIndexNumClause
LinesInfo *LinesClause
}
// Restore implements Node Accept interface.
func (n *IndexAdviseStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("INDEX ADVISE ")
if n.IsLocal {
ctx.WriteKeyWord("LOCAL ")
}
ctx.WriteKeyWord("INFILE ")
ctx.WriteString(n.Path)
if n.MaxMinutes != UnspecifiedSize {
ctx.WriteKeyWord(" MAX_MINUTES ")
ctx.WritePlainf("%d", n.MaxMinutes)
}
if n.MaxIndexNum != nil {
n.MaxIndexNum.Restore(ctx)
}
n.LinesInfo.Restore(ctx)
return nil
}
// Accept implements Node Accept interface.
func (n *IndexAdviseStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*IndexAdviseStmt)
return v.Leave(n)
}
// MaxIndexNumClause represents 'maximum number of indexes' clause in index advise statement.
type MaxIndexNumClause struct {
PerTable uint64
PerDB uint64
}
// Restore for max index num clause
func (n *MaxIndexNumClause) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord(" MAX_IDXNUM")
if n.PerTable != UnspecifiedSize {
ctx.WriteKeyWord(" PER_TABLE ")
ctx.WritePlainf("%d", n.PerTable)
}
if n.PerDB != UnspecifiedSize {
ctx.WriteKeyWord(" PER_DB ")
ctx.WritePlainf("%d", n.PerDB)
}
return nil
}
......@@ -29,9 +29,11 @@ var (
_ DDLNode = &CreateIndexStmt{}
_ DDLNode = &CreateTableStmt{}
_ DDLNode = &CreateViewStmt{}
_ DDLNode = &CreateSequenceStmt{}
_ DDLNode = &DropDatabaseStmt{}
_ DDLNode = &DropIndexStmt{}
_ DDLNode = &DropTableStmt{}
_ DDLNode = &DropSequenceStmt{}
_ DDLNode = &RenameTableStmt{}
_ DDLNode = &TruncateTableStmt{}
_ DDLNode = &RepairTableStmt{}
......@@ -41,7 +43,7 @@ var (
_ Node = &ColumnOption{}
_ Node = &ColumnPosition{}
_ Node = &Constraint{}
_ Node = &IndexColName{}
_ Node = &IndexPartSpecification{}
_ Node = &ReferenceDef{}
)
......@@ -192,18 +194,27 @@ func (n *DropDatabaseStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}
// IndexColName is used for parsing index column name from SQL.
type IndexColName struct {
// IndexPartSpecifications is used for parsing index column name or index expression from SQL.
type IndexPartSpecification struct {
node
Column *ColumnName
Length int
Expr ExprNode
}
// Restore implements Node interface.
func (n *IndexColName) Restore(ctx *RestoreCtx) error {
func (n *IndexPartSpecification) Restore(ctx *RestoreCtx) error {
if n.Expr != nil {
ctx.WritePlain("(")
if err := n.Expr.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing IndexPartSpecifications")
}
ctx.WritePlain(")")
return nil
}
if err := n.Column.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing IndexColName")
return errors.Annotate(err, "An error occurred while splicing IndexPartSpecifications")
}
if n.Length > 0 {
ctx.WritePlainf("(%d)", n.Length)
......@@ -212,12 +223,20 @@ func (n *IndexColName) Restore(ctx *RestoreCtx) error {
}
// Accept implements Node Accept interface.
func (n *IndexColName) Accept(v Visitor) (Node, bool) {
func (n *IndexPartSpecification) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*IndexColName)
n = newNode.(*IndexPartSpecification)
if n.Expr != nil {
node, ok := n.Expr.Accept(v)
if !ok {
return n, false
}
n.Expr = node.(ExprNode)
return v.Leave(n)
}
node, ok := n.Column.Accept(v)
if !ok {
return n, false
......@@ -242,11 +261,11 @@ const (
type ReferenceDef struct {
node
Table *TableName
IndexColNames []*IndexColName
OnDelete *OnDeleteOpt
OnUpdate *OnUpdateOpt
Match MatchType
Table *TableName
IndexPartSpecifications []*IndexPartSpecification
OnDelete *OnDeleteOpt
OnUpdate *OnUpdateOpt
Match MatchType
}
// Restore implements Node interface.
......@@ -258,14 +277,14 @@ func (n *ReferenceDef) Restore(ctx *RestoreCtx) error {
}
}
if n.IndexColNames != nil {
if n.IndexPartSpecifications != nil {
ctx.WritePlain("(")
for i, indexColNames := range n.IndexColNames {
for i, indexColNames := range n.IndexPartSpecifications {
if i > 0 {
ctx.WritePlain(", ")
}
if err := indexColNames.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing IndexColNames: [%v]", i)
return errors.Annotatef(err, "An error occurred while splicing IndexPartSpecifications: [%v]", i)
}
}
ctx.WritePlain(")")
......@@ -309,12 +328,12 @@ func (n *ReferenceDef) Accept(v Visitor) (Node, bool) {
return n, false
}
n.Table = node.(*TableName)
for i, val := range n.IndexColNames {
for i, val := range n.IndexPartSpecifications {
node, ok = val.Accept(v)
if !ok {
return n, false
}
n.IndexColNames[i] = node.(*IndexColName)
n.IndexPartSpecifications[i] = node.(*IndexPartSpecification)
}
onDelete, ok := n.OnDelete.Accept(v)
if !ok {
......@@ -430,6 +449,7 @@ const (
ColumnOptionCheck
ColumnOptionColumnFormat
ColumnOptionStorage
ColumnOptionAutoRandom
)
var (
......@@ -452,8 +472,9 @@ type ColumnOption struct {
// Stored is only for ColumnOptionGenerated, default is false.
Stored bool
// Refer is used for foreign key.
Refer *ReferenceDef
StrValue string
Refer *ReferenceDef
StrValue string
AutoRandomBitLength int
// Enforced is only for Check, default is true.
Enforced bool
}
......@@ -530,6 +551,11 @@ func (n *ColumnOption) Restore(ctx *RestoreCtx) error {
case ColumnOptionStorage:
ctx.WriteKeyWord("STORAGE ")
ctx.WriteKeyWord(n.StrValue)
case ColumnOptionAutoRandom:
ctx.WriteKeyWord("AUTO_RANDOM")
if n.AutoRandomBitLength != types.UnspecifiedLength {
ctx.WritePlainf("(%d)", n.AutoRandomBitLength)
}
default:
return errors.New("An error occurred while splicing ColumnOption")
}
......@@ -667,7 +693,7 @@ type Constraint struct {
Tp ConstraintType
Name string
Keys []*IndexColName // Used for PRIMARY KEY, UNIQUE, ......
Keys []*IndexPartSpecification // Used for PRIMARY KEY, UNIQUE, ......
Refer *ReferenceDef // Used for foreign key.
......@@ -778,7 +804,7 @@ func (n *Constraint) Accept(v Visitor) (Node, bool) {
if !ok {
return n, false
}
n.Keys[i] = node.(*IndexColName)
n.Keys[i] = node.(*IndexPartSpecification)
}
if n.Refer != nil {
node, ok := n.Refer.Accept(v)
......@@ -1064,6 +1090,54 @@ func (n *DropTableStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}
// DropSequenceStmt is a statement to drop a Sequence.
type DropSequenceStmt struct {
ddlNode
IfExists bool
Sequences []*TableName
IsTemporary bool
}
// Restore implements Node interface.
func (n *DropSequenceStmt) Restore(ctx *RestoreCtx) error {
if n.IsTemporary {
ctx.WriteKeyWord("DROP TEMPORARY SEQUENCE ")
} else {
ctx.WriteKeyWord("DROP SEQUENCE ")
}
if n.IfExists {
ctx.WriteKeyWord("IF EXISTS ")
}
for i, sequence := range n.Sequences {
if i != 0 {
ctx.WritePlain(", ")
}
if err := sequence.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore DropSequenceStmt.Sequences[%d]", i)
}
}
return nil
}
// Accept implements Node Accept interface.
func (n *DropSequenceStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*DropSequenceStmt)
for i, val := range n.Sequences {
node, ok := val.Accept(v)
if !ok {
return n, false
}
n.Sequences[i] = node.(*TableName)
}
return v.Leave(n)
}
// RenameTableStmt is a statement to rename a table.
// See http://dev.mysql.com/doc/refman/5.7/en/rename-table.html
type RenameTableStmt struct {
......@@ -1252,6 +1326,65 @@ func (n *CreateViewStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(n)
}
// CreateSequenceStmt is a statement to create a Sequence.
type CreateSequenceStmt struct {
ddlNode
// TODO : support or replace if need : care for it will conflict on temporaryOpt.
OrReplace bool
IsTemporary bool
IfNotExists bool
Name *TableName
SeqOptions []*SequenceOption
TblOptions []*TableOption
}
// Restore implements Node interface.
func (n *CreateSequenceStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("CREATE ")
if n.OrReplace {
ctx.WriteKeyWord("OR REPLACE ")
}
if n.IsTemporary {
ctx.WriteKeyWord("TEMPORARY ")
}
ctx.WriteKeyWord("SEQUENCE ")
if n.IfNotExists {
ctx.WriteKeyWord("IF NOT EXISTS ")
}
if err := n.Name.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while create CreateSequenceStmt.Name")
}
for i, option := range n.SeqOptions {
ctx.WritePlain(" ")
if err := option.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing CreateSequenceStmt SequenceOption: [%v]", i)
}
}
for i, option := range n.TblOptions {
ctx.WritePlain(" ")
if err := option.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while splicing CreateSequenceStmt TableOption: [%v]", i)
}
}
return nil
}
// Accept implements Node Accept interface.
func (n *CreateSequenceStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*CreateSequenceStmt)
node, ok := n.Name.Accept(v)
if !ok {
return n, false
}
n.Name = node.(*TableName)
return v.Leave(n)
}
// IndexLockAndAlgorithm stores the algorithm option and the lock option.
type IndexLockAndAlgorithm struct {
node
......@@ -1311,12 +1444,12 @@ type CreateIndexStmt struct {
// see https://mariadb.com/kb/en/library/create-index/
IfNotExists bool
IndexName string
Table *TableName
IndexColNames []*IndexColName
IndexOption *IndexOption
KeyType IndexKeyType
LockAlg *IndexLockAndAlgorithm
IndexName string
Table *TableName
IndexPartSpecifications []*IndexPartSpecification
IndexOption *IndexOption
KeyType IndexKeyType
LockAlg *IndexLockAndAlgorithm
}
// Restore implements Node interface.
......@@ -1341,12 +1474,12 @@ func (n *CreateIndexStmt) Restore(ctx *RestoreCtx) error {
}
ctx.WritePlain(" (")
for i, indexColName := range n.IndexColNames {
for i, indexColName := range n.IndexPartSpecifications {
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)
return errors.Annotatef(err, "An error occurred while restore CreateIndexStmt.IndexPartSpecifications: [%v]", i)
}
}
ctx.WritePlain(")")
......@@ -1380,12 +1513,12 @@ func (n *CreateIndexStmt) Accept(v Visitor) (Node, bool) {
return n, false
}
n.Table = node.(*TableName)
for i, val := range n.IndexColNames {
for i, val := range n.IndexPartSpecifications {
node, ok = val.Accept(v)
if !ok {
return n, false
}
n.IndexColNames[i] = node.(*IndexColName)
n.IndexPartSpecifications[i] = node.(*IndexPartSpecification)
}
if n.IndexOption != nil {
node, ok := n.IndexOption.Accept(v)
......@@ -1670,6 +1803,11 @@ const (
OnDuplicateKeyHandlingReplace
)
const (
TableOptionCharsetWithoutConvertTo uint64 = 0
TableOptionCharsetWithConvertTo uint64 = 1
)
// TableOption is used for parsing table option from SQL.
type TableOption struct {
Tp TableOptionType
......@@ -1690,9 +1828,20 @@ func (n *TableOption) Restore(ctx *RestoreCtx) error {
ctx.WritePlain("''")
}
case TableOptionCharset:
ctx.WriteKeyWord("DEFAULT CHARACTER SET ")
ctx.WritePlain("= ")
ctx.WriteKeyWord(n.StrValue)
if n.UintValue == TableOptionCharsetWithConvertTo {
ctx.WriteKeyWord("CONVERT TO ")
} else {
ctx.WriteKeyWord("DEFAULT ")
}
ctx.WriteKeyWord("CHARACTER SET ")
if n.UintValue == TableOptionCharsetWithoutConvertTo {
ctx.WriteKeyWord("= ")
}
if n.Default {
ctx.WriteKeyWord("DEFAULT")
} else {
ctx.WriteKeyWord(n.StrValue)
}
case TableOptionCollate:
ctx.WriteKeyWord("DEFAULT COLLATE ")
ctx.WritePlain("= ")
......@@ -1864,6 +2013,69 @@ func (n *TableOption) Restore(ctx *RestoreCtx) error {
return nil
}
// SequenceOptionType is the type for SequenceOption
type SequenceOptionType int
// SequenceOption types.
const (
SequenceOptionNone SequenceOptionType = iota
SequenceOptionIncrementBy
SequenceStartWith
SequenceNoMinValue
SequenceMinValue
SequenceNoMaxValue
SequenceMaxValue
SequenceNoCache
SequenceCache
SequenceNoCycle
SequenceCycle
SequenceNoOrder
SequenceOrder
)
// SequenceOption is used for parsing sequence option from SQL.
type SequenceOption struct {
Tp SequenceOptionType
IntValue int64
}
func (n *SequenceOption) Restore(ctx *RestoreCtx) error {
switch n.Tp {
case SequenceOptionIncrementBy:
ctx.WriteKeyWord("INCREMENT BY ")
ctx.WritePlainf("%d", n.IntValue)
case SequenceStartWith:
ctx.WriteKeyWord("START WITH ")
ctx.WritePlainf("%d", n.IntValue)
case SequenceNoMinValue:
ctx.WriteKeyWord("NO MINVALUE")
case SequenceMinValue:
ctx.WriteKeyWord("MINVALUE ")
ctx.WritePlainf("%d", n.IntValue)
case SequenceNoMaxValue:
ctx.WriteKeyWord("NO MAXVALUE")
case SequenceMaxValue:
ctx.WriteKeyWord("MAXVALUE ")
ctx.WritePlainf("%d", n.IntValue)
case SequenceNoCache:
ctx.WriteKeyWord("NOCACHE")
case SequenceCache:
ctx.WriteKeyWord("CACHE ")
ctx.WritePlainf("%d", n.IntValue)
case SequenceNoCycle:
ctx.WriteKeyWord("NOCYCLE")
case SequenceCycle:
ctx.WriteKeyWord("CYCLE")
case SequenceNoOrder:
ctx.WriteKeyWord("NOORDER")
case SequenceOrder:
ctx.WriteKeyWord("ORDER")
default:
return errors.Errorf("invalid SequenceOption: %d", n.Tp)
}
return nil
}
// ColumnPositionType is the type for ColumnPosition.
type ColumnPositionType int
......@@ -2106,13 +2318,23 @@ func (n *AlterTableSpec) Restore(ctx *RestoreCtx) error {
}
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)
case len(n.Options) == 2 && n.Options[0].Tp == TableOptionCharset && n.Options[1].Tp == TableOptionCollate:
if n.Options[0].UintValue == TableOptionCharsetWithConvertTo {
ctx.WriteKeyWord("CONVERT TO ")
}
ctx.WriteKeyWord("CHARACTER SET ")
if n.Options[0].Default {
ctx.WriteKeyWord("DEFAULT")
} else {
ctx.WriteKeyWord(n.Options[0].StrValue)
}
ctx.WriteKeyWord(" COLLATE ")
ctx.WriteKeyWord(n.Options[1].StrValue)
case n.Options[0].Tp == TableOptionCharset && n.Options[0].Default:
if n.Options[0].UintValue == TableOptionCharsetWithConvertTo {
ctx.WriteKeyWord("CONVERT TO ")
}
ctx.WriteKeyWord("CHARACTER SET DEFAULT")
default:
for i, opt := range n.Options {
if i != 0 {
......@@ -3162,3 +3384,47 @@ func (n *RecoverTableStmt) Accept(v Visitor) (Node, bool) {
}
return v.Leave(n)
}
// FlashBackTableStmt is a statement to restore a dropped/truncate table.
type FlashBackTableStmt struct {
ddlNode
Table *TableName
Timestamp ValueExpr
NewName string
}
// Restore implements Node interface.
func (n *FlashBackTableStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("FLASHBACK TABLE ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing RecoverTableStmt Table")
}
ctx.WriteKeyWord(" UNTIL TIMESTAMP ")
if err := n.Timestamp.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while splicing FlashBackTableStmt Table")
}
if len(n.NewName) > 0 {
ctx.WriteKeyWord(" TO ")
ctx.WriteName(n.NewName)
}
return nil
}
// Accept implements Node Accept interface.
func (n *FlashBackTableStmt) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*FlashBackTableStmt)
if n.Table != nil {
node, ok := n.Table.Accept(v)
if !ok {
return n, false
}
n.Table = node.(*TableName)
}
return v.Leave(n)
}
......@@ -1842,6 +1842,7 @@ const (
ShowCreateTable
ShowCreateView
ShowCreateUser
ShowCreateSequence
ShowGrants
ShowTriggers
ShowProcedureStatus
......@@ -1897,6 +1898,7 @@ type ShowStmt struct {
User *auth.UserIdentity // Used for show grants/create user.
Roles []*auth.RoleIdentity // Used for show grants .. using
IfNotExists bool // Used for `show create database if not exists`
Extended bool // Used for `show extended columns from ...`
// GlobalScope is used by `show variables` and `show bindings`
GlobalScope bool
......@@ -1962,6 +1964,11 @@ func (n *ShowStmt) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("IF NOT EXISTS ")
}
ctx.WriteName(n.DBName)
case ShowCreateSequence:
ctx.WriteKeyWord("CREATE SEQUENCE ")
if err := n.Table.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore ShowStmt.SEQUENCE")
}
case ShowCreateUser:
ctx.WriteKeyWord("CREATE USER ")
if err := n.User.Restore(ctx); err != nil {
......@@ -2085,6 +2092,9 @@ func (n *ShowStmt) Restore(ctx *RestoreCtx) error {
return errors.Annotate(err, "An error occurred while resotre ShowStmt.Table")
} // TODO: remember to check this case
case ShowColumns: // equivalent to SHOW FIELDS
if n.Extended {
ctx.WriteKeyWord("EXTENDED ")
}
restoreOptFull()
ctx.WriteKeyWord("COLUMNS")
if n.Table != nil {
......@@ -2608,3 +2618,39 @@ func (n *SplitOption) Restore(ctx *RestoreCtx) error {
}
return nil
}
type FulltextSearchModifier int
const (
FulltextSearchModifierNaturalLanguageMode = 0
FulltextSearchModifierBooleanMode = 1
FulltextSearchModifierModeMask = 0xF
FulltextSearchModifierWithQueryExpansion = 1 << 4
)
func (m FulltextSearchModifier) IsBooleanMode() bool {
return m&FulltextSearchModifierModeMask == FulltextSearchModifierBooleanMode
}
func (m FulltextSearchModifier) IsNaturalLanguageMode() bool {
return m&FulltextSearchModifierModeMask == FulltextSearchModifierNaturalLanguageMode
}
func (m FulltextSearchModifier) WithQueryExpansion() bool {
return m&FulltextSearchModifierWithQueryExpansion == FulltextSearchModifierWithQueryExpansion
}
type TimestampBound struct {
Mode TimestampBoundMode
Timestamp ExprNode
}
type TimestampBoundMode int
const (
TimestampBoundStrong TimestampBoundMode = iota
TimestampBoundMaxStaleness
TimestampBoundExactStaleness
TimestampBoundReadTimestamp
TimestampBoundMinReadTimestamp
)
......@@ -45,6 +45,7 @@ var (
_ ExprNode = &UnaryOperationExpr{}
_ ExprNode = &ValuesExpr{}
_ ExprNode = &VariableExpr{}
_ ExprNode = &MatchAgainst{}
_ Node = &ColumnName{}
_ Node = &WhenClause{}
......@@ -1273,3 +1274,83 @@ func (n *MaxValueExpr) Accept(v Visitor) (Node, bool) {
}
return v.Leave(n)
}
// MatchAgainst is the expression for matching against fulltext index.
type MatchAgainst struct {
exprNode
// ColumnNames are the columns to match.
ColumnNames []*ColumnName
// Against
Against ExprNode
// Modifier
Modifier FulltextSearchModifier
}
func (n *MatchAgainst) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("MATCH")
ctx.WritePlain(" (")
for i, v := range n.ColumnNames {
if i != 0 {
ctx.WritePlain(",")
}
if err := v.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore MatchAgainst.ColumnNames[%d]", i)
}
}
ctx.WritePlain(") ")
ctx.WriteKeyWord("AGAINST")
ctx.WritePlain(" (")
if err := n.Against.Restore(ctx); err != nil {
return errors.Annotate(err, "An error occurred while restore MatchAgainst.Against")
}
if n.Modifier.IsBooleanMode() {
ctx.WritePlain(" IN BOOLEAN MODE")
if n.Modifier.WithQueryExpansion() {
return errors.New("BOOLEAN MODE doesn't support QUERY EXPANSION")
}
} else if n.Modifier.WithQueryExpansion() {
ctx.WritePlain(" WITH QUERY EXPANSION")
}
ctx.WritePlain(")")
return nil
}
func (n *MatchAgainst) Format(w io.Writer) {
fmt.Fprint(w, "MATCH(")
for i, v := range n.ColumnNames {
if i != 0 {
fmt.Fprintf(w, ",%s", v.String())
} else {
fmt.Fprint(w, v.String())
}
}
fmt.Fprint(w, ") AGAINST(")
n.Against.Format(w)
if n.Modifier.IsBooleanMode() {
fmt.Fprint(w, " IN BOOLEAN MODE")
} else if n.Modifier.WithQueryExpansion() {
fmt.Fprint(w, " WITH QUERY EXPANSION")
}
fmt.Fprint(w, ")")
}
func (n *MatchAgainst) Accept(v Visitor) (Node, bool) {
newNode, skipChildren := v.Enter(n)
if skipChildren {
return v.Leave(newNode)
}
n = newNode.(*MatchAgainst)
for i, colName := range n.ColumnNames {
newColName, ok := colName.Accept(v)
if !ok {
return n, false
}
n.ColumnNames[i] = newColName.(*ColumnName)
}
newAgainst, ok := n.Against.Accept(v)
if !ok {
return n, false
}
n.Against = newAgainst.(ExprNode)
return v.Leave(n)
}
......@@ -120,7 +120,7 @@ const (
CurrentTimestamp = "current_timestamp"
Curtime = "curtime"
Date = "date"
DateLiteral = "dateliteral"
DateLiteral = "'tidb`.(dateliteral"
DateAdd = "date_add"
DateFormat = "date_format"
DateSub = "date_sub"
......@@ -154,12 +154,12 @@ const (
SubTime = "subtime"
Sysdate = "sysdate"
Time = "time"
TimeLiteral = "timeliteral"
TimeLiteral = "'tidb`.(timeliteral"
TimeFormat = "time_format"
TimeToSec = "time_to_sec"
TimeDiff = "timediff"
Timestamp = "timestamp"
TimestampLiteral = "timestampliteral"
TimestampLiteral = "'tidb`.(timestampliteral"
TimestampAdd = "timestampadd"
TimestampDiff = "timestampdiff"
ToDays = "to_days"
......@@ -338,6 +338,23 @@ type FuncCallExpr struct {
// Restore implements Node interface.
func (n *FuncCallExpr) Restore(ctx *RestoreCtx) error {
var specialLiteral string
switch n.FnName.L {
case DateLiteral:
specialLiteral = "DATE "
case TimeLiteral:
specialLiteral = "TIME "
case TimestampLiteral:
specialLiteral = "TIMESTAMP "
}
if specialLiteral != "" {
ctx.WritePlain(specialLiteral)
if err := n.Args[0].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore FuncCastExpr.Expr")
}
return nil
}
ctx.WriteKeyWord(n.FnName.O)
ctx.WritePlain("(")
switch n.FnName.L {
......
......@@ -63,10 +63,11 @@ const (
RepeatableRead = "REPEATABLE-READ"
// Valid formats for explain statement.
ExplainFormatROW = "row"
ExplainFormatDOT = "dot"
PumpType = "PUMP"
DrainerType = "DRAINER"
ExplainFormatROW = "row"
ExplainFormatDOT = "dot"
ExplainFormatHint = "hint"
PumpType = "PUMP"
DrainerType = "DRAINER"
)
// Transaction mode constants.
......@@ -80,6 +81,7 @@ var (
ExplainFormats = []string{
ExplainFormatROW,
ExplainFormatDOT,
ExplainFormatHint,
}
)
......@@ -323,6 +325,7 @@ type Prepared struct {
SchemaVersion int64
UseCache bool
CachedPlan interface{}
CachedNames interface{}
}
// ExecuteStmt is a statement to execute PreparedStmt.
......@@ -376,13 +379,37 @@ func (n *ExecuteStmt) Accept(v Visitor) (Node, bool) {
// See https://dev.mysql.com/doc/refman/5.7/en/commit.html
type BeginStmt struct {
stmtNode
Mode string
Mode string
ReadOnly bool
Bound *TimestampBound
}
// Restore implements Node interface.
func (n *BeginStmt) Restore(ctx *RestoreCtx) error {
if n.Mode == "" {
ctx.WriteKeyWord("START TRANSACTION")
if n.ReadOnly {
ctx.WriteKeyWord("START TRANSACTION READ ONLY")
if n.Bound != nil {
switch n.Bound.Mode {
case TimestampBoundStrong:
ctx.WriteKeyWord(" WITH TIMESTAMP BOUND STRONG")
case TimestampBoundMaxStaleness:
ctx.WriteKeyWord(" WITH TIMESTAMP BOUND MAX STALENESS ")
return n.Bound.Timestamp.Restore(ctx)
case TimestampBoundExactStaleness:
ctx.WriteKeyWord(" WITH TIMESTAMP BOUND EXACT STALENESS ")
return n.Bound.Timestamp.Restore(ctx)
case TimestampBoundReadTimestamp:
ctx.WriteKeyWord(" WITH TIMESTAMP BOUND READ TIMESTAMP ")
return n.Bound.Timestamp.Restore(ctx)
case TimestampBoundMinReadTimestamp:
ctx.WriteKeyWord(" WITH TIMESTAMP BOUND MIN READ TIMESTAMP ")
return n.Bound.Timestamp.Restore(ctx)
}
}
} else {
ctx.WriteKeyWord("START TRANSACTION")
}
} else {
ctx.WriteKeyWord("BEGIN ")
ctx.WriteKeyWord(n.Mode)
......@@ -397,6 +424,13 @@ func (n *BeginStmt) Accept(v Visitor) (Node, bool) {
return v.Leave(newNode)
}
n = newNode.(*BeginStmt)
if n.Bound != nil && n.Bound.Timestamp != nil {
newTimestamp, ok := n.Bound.Timestamp.Accept(v)
if !ok {
return n, false
}
n.Bound.Timestamp = newTimestamp.(ExprNode)
}
return v.Leave(n)
}
......@@ -571,6 +605,20 @@ const (
FlushPrivileges
FlushStatus
FlushTiDBPlugin
FlushHosts
FlushLogs
)
// LogType is the log type used in FLUSH statement.
type LogType int8
const (
LogTypeDefault LogType = iota
LogTypeBinary
LogTypeEngine
LogTypeError
LogTypeGeneral
LogTypeSlow
)
// FlushStmt is a statement to flush tables/privileges/optimizer costs and so on.
......@@ -579,6 +627,7 @@ type FlushStmt struct {
Tp FlushStmtType // Privileges/Tables/...
NoWriteToBinLog bool
LogType LogType
Tables []*TableName // For FlushTableStmt, if Tables is empty, it means flush all tables.
ReadLock bool
Plugins []string
......@@ -620,8 +669,27 @@ func (n *FlushStmt) Restore(ctx *RestoreCtx) error {
}
ctx.WritePlain(v)
}
case FlushHosts:
ctx.WriteKeyWord("HOSTS")
case FlushLogs:
var logType string
switch n.LogType {
case LogTypeDefault:
logType = "LOGS"
case LogTypeBinary:
logType = "BINARY LOGS"
case LogTypeEngine:
logType = "ENGINE LOGS"
case LogTypeError:
logType = "ERROR LOGS"
case LogTypeGeneral:
logType = "GENERAL LOGS"
case LogTypeSlow:
logType = "SLOW LOGS"
}
ctx.WriteKeyWord(logType)
default:
return errors.New("Unsupported type of FlushTables")
return errors.New("Unsupported type of FlushStmt")
}
return nil
}
......@@ -979,12 +1047,12 @@ const (
Subject
)
type TslOption struct {
type TLSOption struct {
Type int
Value string
}
func (t *TslOption) Restore(ctx *RestoreCtx) error {
func (t *TLSOption) Restore(ctx *RestoreCtx) error {
switch t.Type {
case TslNone:
ctx.WriteKeyWord("NONE")
......@@ -999,10 +1067,10 @@ func (t *TslOption) Restore(ctx *RestoreCtx) error {
ctx.WriteKeyWord("ISSUER ")
ctx.WriteString(t.Value)
case Subject:
ctx.WriteKeyWord("CIPHER")
ctx.WriteKeyWord("SUBJECT ")
ctx.WriteString(t.Value)
default:
return errors.Errorf("Unsupported TslOption.Type %d", t.Type)
return errors.Errorf("Unsupported TLSOption.Type %d", t.Type)
}
return nil
}
......@@ -1080,7 +1148,7 @@ type CreateUserStmt struct {
IsCreateRole bool
IfNotExists bool
Specs []*UserSpec
TslOptions []*TslOption
TLSOptions []*TLSOption
ResourceOptions []*ResourceOption
PasswordOrLockOptions []*PasswordOrLockOption
}
......@@ -1104,19 +1172,16 @@ func (n *CreateUserStmt) Restore(ctx *RestoreCtx) error {
}
}
tslOptionLen := len(n.TslOptions)
if tslOptionLen != 0 {
if len(n.TLSOptions) != 0 {
ctx.WriteKeyWord(" REQUIRE ")
}
// Restore `tslOptions` reversely to keep order the same with original sql
for i := tslOptionLen; i > 0; i-- {
if i != tslOptionLen {
for i, option := range n.TLSOptions {
if i != 0 {
ctx.WriteKeyWord(" AND ")
}
if err := n.TslOptions[i-1].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.TslOptions[%d]", i)
if err := option.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore CreateUserStmt.TLSOptions[%d]", i)
}
}
......@@ -1169,7 +1234,7 @@ type AlterUserStmt struct {
IfExists bool
CurrentAuth *AuthOption
Specs []*UserSpec
TslOptions []*TslOption
TLSOptions []*TLSOption
ResourceOptions []*ResourceOption
PasswordOrLockOptions []*PasswordOrLockOption
}
......@@ -1196,19 +1261,16 @@ func (n *AlterUserStmt) Restore(ctx *RestoreCtx) error {
}
}
tslOptionLen := len(n.TslOptions)
if tslOptionLen != 0 {
if len(n.TLSOptions) != 0 {
ctx.WriteKeyWord(" REQUIRE ")
}
// Restore `tslOptions` reversely to keep order the same with original sql
for i := tslOptionLen; i > 0; i-- {
if i != tslOptionLen {
for i, option := range n.TLSOptions {
if i != 0 {
ctx.WriteKeyWord(" AND ")
}
if err := n.TslOptions[i-1].Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.TslOptions[%d]", i)
if err := option.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore AlterUserStmt.TLSOptions[%d]", i)
}
}
......@@ -1346,10 +1408,27 @@ type DropBindingStmt struct {
GlobalScope bool
OriginSel StmtNode
HintedSel StmtNode
}
func (n *DropBindingStmt) Restore(ctx *RestoreCtx) error {
return errors.New("Not implemented")
ctx.WriteKeyWord("DROP ")
if n.GlobalScope {
ctx.WriteKeyWord("GLOBAL ")
} else {
ctx.WriteKeyWord("SESSION ")
}
ctx.WriteKeyWord("BINDING FOR ")
if err := n.OriginSel.Restore(ctx); err != nil {
return errors.Trace(err)
}
if n.HintedSel != nil {
ctx.WriteKeyWord(" USING ")
if err := n.HintedSel.Restore(ctx); err != nil {
return errors.Trace(err)
}
}
return nil
}
func (n *DropBindingStmt) Accept(v Visitor) (Node, bool) {
......@@ -1363,6 +1442,13 @@ func (n *DropBindingStmt) Accept(v Visitor) (Node, bool) {
return n, false
}
n.OriginSel = selnode.(*SelectStmt)
if n.HintedSel != nil {
selnode, ok = n.HintedSel.Accept(v)
if !ok {
return n, false
}
n.HintedSel = selnode.(*SelectStmt)
}
return v.Leave(n)
}
......@@ -1425,6 +1511,9 @@ const (
AdminReloadOptRuleBlacklist
AdminPluginDisable
AdminPluginEnable
AdminFlushBindings
AdminCaptureBindings
AdminEvolveBindings
)
// HandleRange represents a range where handle value >= Begin and < End.
......@@ -1625,6 +1714,12 @@ func (n *AdminStmt) Restore(ctx *RestoreCtx) error {
}
ctx.WritePlain(v)
}
case AdminFlushBindings:
ctx.WriteKeyWord("FLUSH BINDINGS")
case AdminCaptureBindings:
ctx.WriteKeyWord("CAPTURE BINDINGS")
case AdminEvolveBindings:
ctx.WriteKeyWord("EVOLVE BINDINGS")
default:
return errors.New("Unsupported AdminStmt type")
}
......@@ -1879,6 +1974,7 @@ type GrantStmt struct {
ObjectType ObjectTypeType
Level *GrantLevel
Users []*UserSpec
TLSOptions []*TLSOption
WithGrant bool
}
......@@ -1914,6 +2010,19 @@ func (n *GrantStmt) Restore(ctx *RestoreCtx) error {
return errors.Annotatef(err, "An error occurred while restore GrantStmt.Users[%d]", i)
}
}
if n.TLSOptions != nil {
if len(n.TLSOptions) != 0 {
ctx.WriteKeyWord(" REQUIRE ")
}
for i, option := range n.TLSOptions {
if i != 0 {
ctx.WriteKeyWord(" AND ")
}
if err := option.Restore(ctx); err != nil {
return errors.Annotatef(err, "An error occurred while restore GrantStmt.TLSOptions[%d]", i)
}
}
}
if n.WithGrant {
ctx.WriteKeyWord(" WITH GRANT OPTION")
}
......@@ -2073,11 +2182,16 @@ type TableOptimizerHint struct {
// HintTable is table in the hint. It may have query block info.
type HintTable struct {
DBName model.CIStr
TableName model.CIStr
QBName model.CIStr
}
func (ht *HintTable) Restore(ctx *RestoreCtx) {
if ht.DBName.L != "" {
ctx.WriteName(ht.DBName.String())
ctx.WriteKeyWord(".")
}
ctx.WriteName(ht.TableName.String())
if ht.QBName.L != "" {
ctx.WriteKeyWord("@")
......
......@@ -13,6 +13,13 @@
package ast
import "math"
// UnspecifiedSize is unspecified size.
const (
UnspecifiedSize = math.MaxUint64
)
// IsReadOnly checks whether the input ast is readOnly.
func IsReadOnly(node Node) bool {
switch st := node.(type) {
......
......@@ -21,14 +21,9 @@ import (
"github.com/pingcap/parser/terror"
)
const (
codeCollationCharsetMismatch = terror.ErrCode(mysql.ErrCollationCharsetMismatch)
codeUnknownCollation = terror.ErrCode(mysql.ErrUnknownCollation)
)
var (
ErrUnknownCollation = terror.ClassDDL.New(codeUnknownCollation, mysql.MySQLErrName[mysql.ErrUnknownCollation])
ErrCollationCharsetMismatch = terror.ClassDDL.New(codeCollationCharsetMismatch, mysql.MySQLErrName[mysql.ErrCollationCharsetMismatch])
ErrUnknownCollation = terror.ClassDDL.New(mysql.ErrUnknownCollation, mysql.MySQLErrName[mysql.ErrUnknownCollation])
ErrCollationCharsetMismatch = terror.ClassDDL.New(mysql.ErrCollationCharsetMismatch, mysql.MySQLErrName[mysql.ErrCollationCharsetMismatch])
)
// Charset is a charset.
......
......@@ -3,7 +3,7 @@ version: 2
jobs:
build-ut:
docker:
- image: golang:1.11
- image: golang:1.13
working_directory: /go/src/github.com/pingcap/parser
steps:
- checkout
......@@ -24,22 +24,10 @@ jobs:
command: bash <(curl -s https://codecov.io/bash)
build-integration:
docker:
- image: golang:1.11
- image: golang:1.13
working_directory: /go/src/github.com/pingcap/parser
steps:
- checkout
- run:
name: "Verify parser.go is up-to-date"
command: |
mv parser.go parser.go.committed
make parser
diff -u parser.go.committed parser.go
- run:
name: "Check code format"
command: make fmt
- run:
name: "Build"
command: make
- run:
name: "Integration Test"
command: |
......@@ -48,10 +36,11 @@ jobs:
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 gotest
# use only 1 thread to minimize memory usage (we've only got 2 CPU + 4 GB on Circle CI).
make gotest P=1
workflows:
version: 2
build_and_test:
jobs:
- build-ut
- build-integration
\ No newline at end of file
- build-integration
codecov:
require_ci_to_pass: no
notify:
wait_for_ci: no
coverage:
status:
project:
default:
threshold: 0.2
patch:
default:
target: 0% # trial operation
changes: no
comment:
layout: "header, diff"
behavior: default
require_changes: no
......@@ -18,9 +18,11 @@ import (
"crypto/sha256"
"fmt"
hash2 "hash"
"reflect"
"strings"
"sync"
"unicode"
"unsafe"
)
// DigestHash generates the digest of statements.
......@@ -28,6 +30,8 @@ import (
// which removes general property of a statement but keeps specific property.
//
// for example: both DigestHash('select 1') and DigestHash('select 2') => e1c71d1661ae46e09b7aaec1c390957f0d6260410df4e4bc71b9c8d681021471
//
// Deprecated: It is logically consistent with NormalizeDigest.
func DigestHash(sql string) (result string) {
d := digesterPool.Get().(*sqlDigester)
result = d.doDigest(sql)
......@@ -35,6 +39,20 @@ func DigestHash(sql string) (result string) {
return
}
// DigestNormalized generates the digest of a normalized sql.
// it will generate a hash on a normalized sql.
// Normalize + DigestNormalized equals to NormalizeDigest.
//
// for example: DigestNormalized('select ?')
// DigestNormalized should be called with a normalized SQL string (like 'select ?') generated by function Normalize.
// do not call with SQL which is not normalized, DigestNormalized('select 1') and DigestNormalized('select 2') is not the same
func DigestNormalized(normalized string) (result string) {
d := digesterPool.Get().(*sqlDigester)
result = d.doDigestNormalized(normalized)
digesterPool.Put(d)
return
}
// Normalize generates the normalized statements.
// it will get normalized form of statement text
// which removes general property of a statement but keeps specific property.
......@@ -47,7 +65,7 @@ func Normalize(sql string) (result string) {
return
}
// NormalizeDigest combines Normalize and DigestHash into one method.
// NormalizeDigest combines Normalize and DigestNormalized into one method.
func NormalizeDigest(sql string) (normalized, digest string) {
d := digesterPool.Get().(*sqlDigester)
normalized, digest = d.doNormalizeDigest(sql)
......@@ -72,6 +90,19 @@ type sqlDigester struct {
tokens tokenDeque
}
func (d *sqlDigester) doDigestNormalized(normalized string) (result string) {
hdr := *(*reflect.StringHeader)(unsafe.Pointer(&normalized))
b := *(*[]byte)(unsafe.Pointer(&reflect.SliceHeader{
Data: hdr.Data,
Len: hdr.Len,
Cap: hdr.Len,
}))
d.hasher.Write(b)
result = fmt.Sprintf("%x", d.hasher.Sum(nil))
d.hasher.Reset()
return
}
func (d *sqlDigester) doDigest(sql string) (result string) {
d.normalize(sql)
d.hasher.Write(d.buffer.Bytes())
......@@ -142,17 +173,7 @@ func (d *sqlDigester) normalize(sql string) {
func (d *sqlDigester) reduceOptimizerHint(tok *token) (reduced bool) {
// ignore /*+..*/
if tok.tok == hintBegin {
for {
tok, _, _ := d.lexer.scan()
if tok == 0 || (tok == unicode.ReplacementChar && d.lexer.r.eof()) {
break
}
if tok == hintEnd {
reduced = true
break
}
}
if tok.tok == hintComment {
return
}
......
......@@ -7,10 +7,14 @@ require (
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/golang/protobuf v1.3.2 // indirect
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8
github.com/pingcap/errors v0.11.4
github.com/pingcap/tidb v0.0.0-20190703092821-755875aacb5a
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9
github.com/pingcap/tipb v0.0.0-20190428032612-535e1abaa330
github.com/sirupsen/logrus v1.3.0
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 // indirect
go.uber.org/zap v1.12.0
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2
)
go 1.13
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
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-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-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=
github.com/cznic/y v0.0.0-20170802143616-045f81c6662a/go.mod h1:1rk5VM7oSnA4vjp+hrLQ3HWHa+Y4yPCa3/CsJrcNnvs=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8 h1:USx2/E1bX46VG32FIw034Au6seQ2fY9NEILmNh/UlQg=
github.com/pingcap/check v0.0.0-20190102082844-67f458068fc8/go.mod h1:B1+S9LNcuMyLH/4HMTViQOJevkGiik3wW2AN9zb2fNQ=
github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4=
github.com/pingcap/errors v0.11.4/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9 h1:AJD9pZYm72vMgPcQDww9rkZ1DnWfl0pXV3BOWlkYIjA=
github.com/pingcap/log v0.0.0-20191012051959-b742a5d432e9/go.mod h1:4rbK1p9ILyIfb6hU7OG2CiWSqMXnp3JMbiaVJ6mvoY8=
github.com/pingcap/tipb v0.0.0-20190428032612-535e1abaa330 h1:rRMLMjIMFulCX9sGKZ1hoov/iROMsKyC8Snc02nSukw=
github.com/pingcap/tipb v0.0.0-20190428032612-535e1abaa330/go.mod h1:RtkHW8WbcNxj8lsbzjaILci01CtYnYbIkQhjyZWrWVI=
github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237 h1:HQagqIiBmr8YXawX/le3+O26N+vPPC1PtjaF3mwnook=
github.com/remyoudompheng/bigfft v0.0.0-20190728182440-6a916e37a237/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
go.uber.org/atomic v1.5.0 h1:OI5t8sDa1Or+q8AeE+yKeB/SDYioSHAgcVljj9JIETY=
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.3.0 h1:sFPn2GLc3poCkfrpIXGhBD2X0CMIo4Q/zSULXrj/+uc=
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee h1:0mgffUl7nfd+FpvXMVz4IDEaUSmT1ysygQC7qYo7sG4=
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
go.uber.org/zap v1.12.0 h1:dySoUQPFBGj6xwjmBzageVL8jGi8uxc6bEmJQjA06bw=
go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de h1:5hukYrvBGR8/eNkX5mdUezrA6JiaEZDtJb9Ei+1LlBs=
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2 h1:z99zHgr7hKfrUcX/KsoJk5FJfjTceCKIp96+biqP4To=
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5 h1:hKsoRgsbwY1NafxrwTs+k64bikrLBkAgPir1TNCj3Zs=
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8=
gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k=
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
honnef.co/go/tools v0.0.1-2019.2.3 h1:3JgtbtFHMiCmsznwGVTUWbgGov+pVqnlf1dEJTNAXeM=
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
此差异已折叠。
此差异已折叠。
%{
// Copyright 2020 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"math"
"strconv"
"github.com/pingcap/parser/ast"
"github.com/pingcap/parser/model"
)
%}
%union {
ident string
number uint64
hint *ast.TableOptimizerHint
hints []*ast.TableOptimizerHint
table ast.HintTable
}
%token <number>
/*yy:token "%d" */
hintIntLit "a 64-bit unsigned integer"
%token <ident>
/*yy:token "%c" */
hintIdentifier
/*yy:token "@%c" */
hintSingleAtIdentifier "identifier with single leading at"
/*yy:token "'%c'" */
hintStringLit
/* MySQL 8.0 hint names */
hintJoinFixedOrder "JOIN_FIXED_ORDER"
hintJoinOrder "JOIN_ORDER"
hintJoinPrefix "JOIN_PREFIX"
hintJoinSuffix "JOIN_SUFFIX"
hintBKA "BKA"
hintNoBKA "NO_BKA"
hintBNL "BNL"
hintNoBNL "NO_BNL"
hintHashJoin "HASH_JOIN"
hintNoHashJoin "NO_HASH_JOIN"
hintMerge "MERGE"
hintNoMerge "NO_MERGE"
hintIndexMerge "INDEX_MERGE"
hintNoIndexMerge "NO_INDEX_MERGE"
hintMRR "MRR"
hintNoMRR "NO_MRR"
hintNoICP "NO_ICP"
hintNoRangeOptimization "NO_RANGE_OPTIMIZATION"
hintSkipScan "SKIP_SCAN"
hintNoSkipScan "NO_SKIP_SCAN"
hintSemijoin "SEMIJOIN"
hintNoSemijoin "NO_SEMIJOIN"
hintMaxExecutionTime "MAX_EXECUTION_TIME"
hintSetVar "SET_VAR"
hintResourceGroup "RESOURCE_GROUP"
hintQBName "QB_NAME"
/* TiDB hint names */
hintAggToCop "AGG_TO_COP"
hintEnablePlanCache "ENABLE_PLAN_CACHE"
hintHashAgg "HASH_AGG"
hintIgnoreIndex "IGNORE_INDEX"
hintInlHashJoin "INL_HASH_JOIN"
hintInlJoin "INL_JOIN"
hintInlMergeJoin "INL_MERGE_JOIN"
hintMemoryQuota "MEMORY_QUOTA"
hintNoSwapJoinInputs "NO_SWAP_JOIN_INPUTS"
hintQueryType "QUERY_TYPE"
hintReadConsistentReplica "READ_CONSISTENT_REPLICA"
hintReadFromStorage "READ_FROM_STORAGE"
hintSMJoin "SM_JOIN"
hintStreamAgg "STREAM_AGG"
hintSwapJoinInputs "SWAP_JOIN_INPUTS"
hintUseIndexMerge "USE_INDEX_MERGE"
hintUseIndex "USE_INDEX"
hintUsePlanCache "USE_PLAN_CACHE"
hintUseToja "USE_TOJA"
/* Other keywords */
hintOLAP "OLAP"
hintOLTP "OLTP"
hintTiKV "TIKV"
hintTiFlash "TIFLASH"
hintFalse "FALSE"
hintTrue "TRUE"
hintMB "MB"
hintGB "GB"
hintDupsWeedOut "DUPSWEEDOUT"
hintFirstMatch "FIRSTMATCH"
hintLooseScan "LOOSESCAN"
hintMaterialization "MATERIALIZATION"
%type <ident>
Identifier "identifier (including keywords)"
QueryBlockOpt "Query block identifier optional"
JoinOrderOptimizerHintName
UnsupportedTableLevelOptimizerHintName
SupportedTableLevelOptimizerHintName
UnsupportedIndexLevelOptimizerHintName
SupportedIndexLevelOptimizerHintName
SubqueryOptimizerHintName
BooleanHintName "name of hints which take a boolean input"
NullaryHintName "name of hints which take no input"
SubqueryStrategy
Value "the value in the SET_VAR() hint"
HintQueryType "query type in optimizer hint (OLAP or OLTP)"
HintStorageType "storage type in optimizer hint (TiKV or TiFlash)"
%type <number>
UnitOfBytes "unit of bytes (MB or GB)"
CommaOpt "optional ','"
%type <hints>
OptimizerHintList "optimizer hint list"
StorageOptimizerHintOpt "storage level optimizer hint"
HintStorageTypeAndTableList "storage type and tables list in optimizer hint"
%type <hint>
TableOptimizerHintOpt "optimizer hint"
HintTableList "table list in optimizer hint"
HintTableListOpt "optional table list in optimizer hint"
HintIndexList "table name with index list in optimizer hint"
IndexNameList "index list in optimizer hint"
IndexNameListOpt "optional index list in optimizer hint"
SubqueryStrategies "subquery strategies"
SubqueryStrategiesOpt "optional subquery strategies"
HintTrueOrFalse "true or false in optimizer hint"
HintStorageTypeAndTable "storage type and tables in optimizer hint"
%type <table>
HintTable "Table in optimizer hint"
%start Start
%%
Start:
OptimizerHintList
{
parser.result = $1
}
OptimizerHintList:
TableOptimizerHintOpt
{
if $1 != nil {
$$ = []*ast.TableOptimizerHint{$1}
}
}
| OptimizerHintList CommaOpt TableOptimizerHintOpt
{
if $3 != nil {
$$ = append($1, $3)
} else {
$$ = $1
}
}
| StorageOptimizerHintOpt
{
$$ = $1
}
| OptimizerHintList CommaOpt StorageOptimizerHintOpt
{
$$ = append($1, $3...)
}
TableOptimizerHintOpt:
"JOIN_FIXED_ORDER" '(' QueryBlockOpt ')'
{
parser.warnUnsupportedHint($1)
$$ = nil
}
| JoinOrderOptimizerHintName '(' HintTableList ')'
{
parser.warnUnsupportedHint($1)
$$ = nil
}
| UnsupportedTableLevelOptimizerHintName '(' HintTableListOpt ')'
{
parser.warnUnsupportedHint($1)
$$ = nil
}
| SupportedTableLevelOptimizerHintName '(' HintTableListOpt ')'
{
h := $3
h.HintName = model.NewCIStr($1)
$$ = h
}
| UnsupportedIndexLevelOptimizerHintName '(' HintIndexList ')'
{
parser.warnUnsupportedHint($1)
$$ = nil
}
| SupportedIndexLevelOptimizerHintName '(' HintIndexList ')'
{
h := $3
h.HintName = model.NewCIStr($1)
$$ = h
}
| SubqueryOptimizerHintName '(' QueryBlockOpt SubqueryStrategiesOpt ')'
{
parser.warnUnsupportedHint($1)
$$ = nil
}
| "MAX_EXECUTION_TIME" '(' QueryBlockOpt hintIntLit ')'
{
$$ = &ast.TableOptimizerHint{
HintName: model.NewCIStr($1),
QBName: model.NewCIStr($3),
MaxExecutionTime: $4,
}
}
| "SET_VAR" '(' Identifier '=' Value ')'
{
parser.warnUnsupportedHint($1)
$$ = nil
}
| "RESOURCE_GROUP" '(' Identifier ')'
{
parser.warnUnsupportedHint($1)
$$ = nil
}
| "QB_NAME" '(' Identifier ')'
{
$$ = &ast.TableOptimizerHint{
HintName: model.NewCIStr($1),
QBName: model.NewCIStr($3),
}
}
| "MEMORY_QUOTA" '(' QueryBlockOpt hintIntLit UnitOfBytes ')'
{
maxValue := uint64(math.MaxInt64) / $5
if $4 <= maxValue {
$$ = &ast.TableOptimizerHint{
HintName: model.NewCIStr($1),
MemoryQuota: int64($4 * $5),
QBName: model.NewCIStr($3),
}
} else {
yylex.AppendError(ErrWarnMemoryQuotaOverflow.GenWithStackByArgs(math.MaxInt64))
parser.lastErrorAsWarn()
$$ = nil
}
}
| BooleanHintName '(' QueryBlockOpt HintTrueOrFalse ')'
{
h := $4
h.HintName = model.NewCIStr($1)
h.QBName = model.NewCIStr($3)
$$ = h
}
| NullaryHintName '(' QueryBlockOpt ')'
{
$$ = &ast.TableOptimizerHint{
HintName: model.NewCIStr($1),
QBName: model.NewCIStr($3),
}
}
| "QUERY_TYPE" '(' QueryBlockOpt HintQueryType ')'
{
$$ = &ast.TableOptimizerHint{
HintName: model.NewCIStr($1),
QBName: model.NewCIStr($3),
QueryType: model.NewCIStr($4),
}
}
StorageOptimizerHintOpt:
"READ_FROM_STORAGE" '(' QueryBlockOpt HintStorageTypeAndTableList ')'
{
hs := $4
name := model.NewCIStr($1)
qb := model.NewCIStr($3)
for _, h := range hs {
h.HintName = name
h.QBName = qb
}
$$ = hs
}
HintStorageTypeAndTableList:
HintStorageTypeAndTable
{
$$ = []*ast.TableOptimizerHint{$1}
}
| HintStorageTypeAndTableList ',' HintStorageTypeAndTable
{
$$ = append($1, $3)
}
HintStorageTypeAndTable:
HintStorageType '[' HintTableList ']'
{
h := $3
h.StoreType = model.NewCIStr($1)
$$ = h
}
QueryBlockOpt:
/* empty */
{
$$ = ""
}
| hintSingleAtIdentifier
CommaOpt:
/*empty*/
{}
| ','
{}
/**
* HintTableListOpt:
*
* [@query_block_name] [tbl_name [, tbl_name] ...]
* [tbl_name@query_block_name [, tbl_name@query_block_name] ...]
*
*/
HintTableListOpt:
HintTableList
| QueryBlockOpt
{
$$ = &ast.TableOptimizerHint{
QBName: model.NewCIStr($1),
}
}
HintTableList:
QueryBlockOpt HintTable
{
$$ = &ast.TableOptimizerHint{
Tables: []ast.HintTable{$2},
QBName: model.NewCIStr($1),
}
}
| HintTableList ',' HintTable
{
h := $1
h.Tables = append(h.Tables, $3)
$$ = h
}
HintTable:
Identifier QueryBlockOpt
{
$$ = ast.HintTable{
TableName: model.NewCIStr($1),
QBName: model.NewCIStr($2),
}
}
| Identifier '.' Identifier QueryBlockOpt
{
$$ = ast.HintTable{
DBName: model.NewCIStr($1),
TableName: model.NewCIStr($3),
QBName: model.NewCIStr($4),
}
}
/**
* HintIndexList:
*
* [@query_block_name] tbl_name [index_name [, index_name] ...]
* tbl_name@query_block_name [index_name [, index_name] ...]
*/
HintIndexList:
QueryBlockOpt HintTable CommaOpt IndexNameListOpt
{
h := $4
h.Tables = []ast.HintTable{$2}
h.QBName = model.NewCIStr($1)
$$ = h
}
IndexNameListOpt:
/* empty */
{
$$ = &ast.TableOptimizerHint{}
}
| IndexNameList
IndexNameList:
Identifier
{
$$ = &ast.TableOptimizerHint{
Indexes: []model.CIStr{model.NewCIStr($1)},
}
}
| IndexNameList ',' Identifier
{
h := $1
h.Indexes = append(h.Indexes, model.NewCIStr($3))
$$ = h
}
/**
* Miscellaneous rules
*/
SubqueryStrategiesOpt:
/* empty */
{}
| SubqueryStrategies
SubqueryStrategies:
SubqueryStrategy
{}
| SubqueryStrategies ',' SubqueryStrategy
Value:
hintStringLit
| Identifier
| hintIntLit
{
$$ = strconv.FormatUint($1, 10)
}
UnitOfBytes:
"MB"
{
$$ = 1024 * 1024
}
| "GB"
{
$$ = 1024 * 1024 * 1024
}
HintTrueOrFalse:
"TRUE"
{
$$ = &ast.TableOptimizerHint{HintFlag: true}
}
| "FALSE"
{
$$ = &ast.TableOptimizerHint{HintFlag: false}
}
JoinOrderOptimizerHintName:
"JOIN_ORDER"
| "JOIN_PREFIX"
| "JOIN_SUFFIX"
UnsupportedTableLevelOptimizerHintName:
"BKA"
| "NO_BKA"
| "BNL"
| "NO_BNL"
/* HASH_JOIN is supported by TiDB */
| "NO_HASH_JOIN"
| "MERGE"
| "NO_MERGE"
SupportedTableLevelOptimizerHintName:
"SM_JOIN"
| "INL_JOIN"
| "INL_HASH_JOIN"
| "SWAP_JOIN_INPUTS"
| "NO_SWAP_JOIN_INPUTS"
| "INL_MERGE_JOIN"
| "HASH_JOIN"
UnsupportedIndexLevelOptimizerHintName:
"INDEX_MERGE"
/* NO_INDEX_MERGE is currently a nullary hint in TiDB */
| "MRR"
| "NO_MRR"
| "NO_ICP"
| "NO_RANGE_OPTIMIZATION"
| "SKIP_SCAN"
| "NO_SKIP_SCAN"
SupportedIndexLevelOptimizerHintName:
"USE_INDEX"
| "IGNORE_INDEX"
| "USE_INDEX_MERGE"
SubqueryOptimizerHintName:
"SEMIJOIN"
| "NO_SEMIJOIN"
SubqueryStrategy:
"DUPSWEEDOUT"
| "FIRSTMATCH"
| "LOOSESCAN"
| "MATERIALIZATION"
BooleanHintName:
"USE_TOJA"
| "ENABLE_PLAN_CACHE"
NullaryHintName:
"USE_PLAN_CACHE"
| "HASH_AGG"
| "STREAM_AGG"
| "AGG_TO_COP"
| "NO_INDEX_MERGE"
| "READ_CONSISTENT_REPLICA"
HintQueryType:
"OLAP"
| "OLTP"
HintStorageType:
"TIKV"
| "TIFLASH"
Identifier:
hintIdentifier
/* MySQL 8.0 hint names */
| "JOIN_FIXED_ORDER"
| "JOIN_ORDER"
| "JOIN_PREFIX"
| "JOIN_SUFFIX"
| "BKA"
| "NO_BKA"
| "BNL"
| "NO_BNL"
| "HASH_JOIN"
| "NO_HASH_JOIN"
| "MERGE"
| "NO_MERGE"
| "INDEX_MERGE"
| "NO_INDEX_MERGE"
| "MRR"
| "NO_MRR"
| "NO_ICP"
| "NO_RANGE_OPTIMIZATION"
| "SKIP_SCAN"
| "NO_SKIP_SCAN"
| "SEMIJOIN"
| "NO_SEMIJOIN"
| "MAX_EXECUTION_TIME"
| "SET_VAR"
| "RESOURCE_GROUP"
| "QB_NAME"
/* TiDB hint names */
| "AGG_TO_COP"
| "ENABLE_PLAN_CACHE"
| "HASH_AGG"
| "IGNORE_INDEX"
| "INL_HASH_JOIN"
| "INL_JOIN"
| "INL_MERGE_JOIN"
| "MEMORY_QUOTA"
| "NO_SWAP_JOIN_INPUTS"
| "QUERY_TYPE"
| "READ_CONSISTENT_REPLICA"
| "READ_FROM_STORAGE"
| "SM_JOIN"
| "STREAM_AGG"
| "SWAP_JOIN_INPUTS"
| "USE_INDEX_MERGE"
| "USE_INDEX"
| "USE_PLAN_CACHE"
| "USE_TOJA"
/* other keywords */
| "OLAP"
| "OLTP"
| "TIKV"
| "TIFLASH"
| "FALSE"
| "TRUE"
| "MB"
| "GB"
| "DUPSWEEDOUT"
| "FIRSTMATCH"
| "LOOSESCAN"
| "MATERIALIZATION"
%%
// Copyright 2020 PingCAP, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// See the License for the specific language governing permissions and
// limitations under the License.
package parser
import (
"strconv"
"strings"
"unicode"
"github.com/pingcap/parser/ast"
"github.com/pingcap/parser/mysql"
"github.com/pingcap/parser/terror"
)
var (
ErrWarnOptimizerHintUnsupportedHint = terror.ClassParser.New(mysql.ErrWarnOptimizerHintUnsupportedHint, mysql.MySQLErrName[mysql.ErrWarnOptimizerHintUnsupportedHint])
ErrWarnOptimizerHintInvalidToken = terror.ClassParser.New(mysql.ErrWarnOptimizerHintInvalidToken, mysql.MySQLErrName[mysql.ErrWarnOptimizerHintInvalidToken])
ErrWarnMemoryQuotaOverflow = terror.ClassParser.New(mysql.ErrWarnMemoryQuotaOverflow, mysql.MySQLErrName[mysql.ErrWarnMemoryQuotaOverflow])
ErrWarnOptimizerHintParseError = terror.ClassParser.New(mysql.ErrWarnOptimizerHintParseError, mysql.MySQLErrName[mysql.ErrWarnOptimizerHintParseError])
ErrWarnOptimizerHintInvalidInteger = terror.ClassParser.New(mysql.ErrWarnOptimizerHintInvalidInteger, mysql.MySQLErrName[mysql.ErrWarnOptimizerHintInvalidInteger])
)
// hintScanner implements the yyhintLexer interface
type hintScanner struct {
Scanner
}
func (hs *hintScanner) Errorf(format string, args ...interface{}) error {
inner := hs.Scanner.Errorf(format, args...)
return ErrWarnOptimizerHintParseError.GenWithStackByArgs(inner)
}
func (hs *hintScanner) Lex(lval *yyhintSymType) int {
tok, pos, lit := hs.scan()
hs.lastScanOffset = pos.Offset
var errorTokenType string
switch tok {
case intLit:
n, e := strconv.ParseUint(lit, 10, 64)
if e != nil {
hs.AppendError(ErrWarnOptimizerHintInvalidInteger.GenWithStackByArgs(lit))
return int(unicode.ReplacementChar)
}
lval.number = n
return hintIntLit
case singleAtIdentifier:
lval.ident = lit
return hintSingleAtIdentifier
case identifier:
lval.ident = lit
if tok1, ok := hintTokenMap[strings.ToUpper(lit)]; ok {
return tok1
}
return hintIdentifier
case stringLit:
lval.ident = lit
if hs.sqlMode.HasANSIQuotesMode() && hs.r.s[pos.Offset] == '"' {
return hintIdentifier
}
return hintStringLit
case bitLit:
if strings.HasPrefix(lit, "0b") {
lval.ident = lit
return hintIdentifier
}
errorTokenType = "bit-value literal"
case hexLit:
if strings.HasPrefix(lit, "0x") {
lval.ident = lit
return hintIdentifier
}
errorTokenType = "hexadecimal literal"
case quotedIdentifier:
lval.ident = lit
return hintIdentifier
case eq:
return '='
case floatLit:
errorTokenType = "floating point number"
case decLit:
errorTokenType = "decimal number"
default:
if tok <= 0x7f {
return tok
}
errorTokenType = "unknown token"
}
hs.AppendError(ErrWarnOptimizerHintInvalidToken.GenWithStackByArgs(errorTokenType, lit, tok))
return int(unicode.ReplacementChar)
}
type hintParser struct {
lexer hintScanner
result []*ast.TableOptimizerHint
// the following fields are used by yyParse to reduce allocation.
cache []yyhintSymType
yylval yyhintSymType
yyVAL *yyhintSymType
}
func newHintParser() *hintParser {
return &hintParser{cache: make([]yyhintSymType, 50)}
}
func (hp *hintParser) parse(input string, sqlMode mysql.SQLMode, initPos Pos) ([]*ast.TableOptimizerHint, []error) {
hp.result = nil
hp.lexer.reset(input[3:])
hp.lexer.SetSQLMode(sqlMode)
hp.lexer.r.p = Pos{
Line: initPos.Line,
Col: initPos.Col + 3, // skipped the initial '/*+'
Offset: 0,
}
hp.lexer.inBangComment = true // skip the final '*/' (we need the '*/' for reporting warnings)
yyhintParse(&hp.lexer, hp)
warns, errs := hp.lexer.Errors()
if len(errs) == 0 {
errs = warns
}
return hp.result, errs
}
// ParseHint parses an optimizer hint (the interior of `/*+ ... */`).
func ParseHint(input string, sqlMode mysql.SQLMode, initPos Pos) ([]*ast.TableOptimizerHint, []error) {
hp := newHintParser()
return hp.parse(input, sqlMode, initPos)
}
func (hp *hintParser) warnUnsupportedHint(name string) {
warn := ErrWarnOptimizerHintUnsupportedHint.GenWithStackByArgs(name)
hp.lexer.warns = append(hp.lexer.warns, warn)
}
func (hp *hintParser) lastErrorAsWarn() {
hp.lexer.lastErrorAsWarn()
}
......@@ -42,8 +42,9 @@ type Scanner struct {
warns []error
stmtStartPos int
// For scanning such kind of comment: /*! MySQL-specific code */ or /*+ optimizer hint */
specialComment specialCommentScanner
// inBangComment is true if we are inside a `/*! ... */` block.
// It is used to ignore a stray `*/` when scanning.
inBangComment bool
sqlMode mysql.SQLMode
......@@ -55,51 +56,13 @@ type Scanner struct {
// lastScanOffset indicates last offset returned by scan().
// It's used to substring sql in syntax error message.
lastScanOffset int
}
type specialCommentScanner interface {
stmtTexter
scan() (tok int, pos Pos, lit string)
}
type mysqlSpecificCodeScanner struct {
*Scanner
Pos
}
func (s *mysqlSpecificCodeScanner) scan() (tok int, pos Pos, lit string) {
tok, pos, lit = s.Scanner.scan()
pos.Line += s.Pos.Line
pos.Col += s.Pos.Col
pos.Offset += s.Pos.Offset
return
}
// lastKeyword records the previous keyword returned by scan().
// determine whether an optimizer hint should be parsed or ignored.
lastKeyword int
type optimizerHintScanner struct {
*Scanner
Pos
end bool
}
func (s *optimizerHintScanner) scan() (tok int, pos Pos, lit string) {
tok, pos, lit = s.Scanner.scan()
pos.Line += s.Pos.Line
pos.Col += s.Pos.Col
pos.Offset += s.Pos.Offset
switch tok {
case 0:
if !s.end {
tok = hintEnd
s.end = true
}
case invalid:
// an optimizer hint is allowed to contain invalid characters, the
// remaining hints are just ignored.
// force advance the lexer even when encountering an invalid character
// to prevent infinite parser loop. (see issue #336)
s.r.inc()
}
return
// hintPos records the start position of the previous optimizer hint.
lastHintPos Pos
}
// Errors returns the errors and warns during a scan.
......@@ -114,14 +77,11 @@ func (s *Scanner) reset(sql string) {
s.errs = s.errs[:0]
s.warns = s.warns[:0]
s.stmtStartPos = 0
s.specialComment = nil
s.inBangComment = false
s.lastKeyword = 0
}
func (s *Scanner) stmtText() string {
if s.specialComment != nil {
return s.specialComment.stmtText()
}
endPos := s.r.pos().Offset
if s.r.s[endPos-1] == '\n' {
endPos = endPos - 1 // trim new line
......@@ -168,6 +128,7 @@ func (s *Scanner) AppendError(err error) {
func (s *Scanner) Lex(v *yySymType) int {
tok, pos, lit := s.scan()
s.lastScanOffset = pos.Offset
s.lastKeyword = 0
v.offset = pos.Offset
v.ident = lit
if tok == identifier {
......@@ -176,6 +137,7 @@ func (s *Scanner) Lex(v *yySymType) int {
if tok == identifier {
if tok1 := s.isTokenIdentifier(lit, pos.Offset); tok1 != 0 {
tok = tok1
s.lastKeyword = tok1
}
}
if s.sqlMode.HasANSIQuotesMode() &&
......@@ -253,19 +215,6 @@ func (s *Scanner) skipWhitespace() rune {
}
func (s *Scanner) scan() (tok int, pos Pos, lit string) {
if s.specialComment != nil {
// Enter specialComment scan mode.
// for scanning such kind of comment: /*! MySQL-specific code */
specialComment := s.specialComment
tok, pos, lit = specialComment.scan()
if tok != 0 {
// return the specialComment scan result as the result
return
}
// leave specialComment scan mode after all stream consumed.
s.specialComment = nil
}
ch0 := s.r.peek()
if unicode.IsSpace(ch0) {
ch0 = s.skipWhitespace()
......@@ -388,85 +337,95 @@ func startWithDash(s *Scanner) (tok int, pos Pos, lit string) {
func startWithSlash(s *Scanner) (tok int, pos Pos, lit string) {
pos = s.r.pos()
s.r.inc()
ch0 := s.r.peek()
if ch0 == '*' {
s.r.inc()
startWithAsterisk := false
for {
ch0 = s.r.readByte()
if startWithAsterisk && ch0 == '/' {
// Meets */, means comment end.
break
} else if ch0 == '*' {
startWithAsterisk = true
} else {
startWithAsterisk = false
}
if s.r.peek() != '*' {
tok = int('/')
return
}
if ch0 == unicode.ReplacementChar && s.r.eof() {
// unclosed comment
s.errs = append(s.errs, ParseErrorWith(s.r.data(&pos), s.r.p.Line))
return
}
isOptimizerHint := false
currentCharIsStar := false
s.r.inc() // we see '/*' so far.
switch s.r.readByte() {
case '!': // '/*!' MySQL-specific comments
// See http://dev.mysql.com/doc/refman/5.7/en/comments.html
// in '/*!', which we always recognize regardless of version.
_ = s.scanVersionDigits(5, 5)
s.inBangComment = true
return s.scan()
case 'T': // '/*T' maybe TiDB-specific comments
if s.r.peek() != '!' {
// '/*TX' is just normal comment.
break
}
s.r.inc()
// in '/*T!', try to consume the 5 to 6 digit version string.
commentVersion := s.scanVersionDigits(5, 6)
if commentVersion <= CommentCodeCurrentVersion {
s.inBangComment = true
return s.scan()
}
comment := s.r.data(&pos)
case 'M': // '/*M' maybe MariaDB-specific comments
// no special treatment for now.
break
case '+': // '/*+' optimizer hints
// See https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html
if strings.HasPrefix(comment, "/*+") {
begin := sqlOffsetInComment(comment)
end := len(comment) - 2
sql := comment[begin:end]
s.specialComment = &optimizerHintScanner{
Scanner: s.InheritScanner(sql),
Pos: Pos{
pos.Line,
pos.Col,
pos.Offset + begin,
},
}
tok = hintBegin
return
if _, ok := hintedTokens[s.lastKeyword]; ok {
// only recognize optimizers hints directly followed by certain
// keywords like SELECT, INSERT, etc.
isOptimizerHint = true
}
// See http://dev.mysql.com/doc/refman/5.7/en/comments.html
// Convert "/*!VersionNumber MySQL-specific-code */" to "MySQL-specific-code".
if strings.HasPrefix(comment, "/*!") {
sql := specCodePattern.ReplaceAllStringFunc(comment, TrimComment)
s.specialComment = &mysqlSpecificCodeScanner{
Scanner: s.InheritScanner(sql),
Pos: Pos{
pos.Line,
pos.Col,
pos.Offset + sqlOffsetInComment(comment),
},
}
}
case '*': // '/**' if the next char is '/' it would close the comment.
currentCharIsStar = true
return s.scan()
default:
break
}
tok = int('/')
return
}
func sqlOffsetInComment(comment string) int {
// find the first SQL token offset in pattern like "/*!40101 mysql specific code */"
offset := 0
for i := 0; i < len(comment); i++ {
if unicode.IsSpace(rune(comment[i])) {
offset = i
break
// standard C-like comment. read until we see '*/' then drop it.
for {
if currentCharIsStar || s.r.incAsLongAs(func(ch rune) bool { return ch != '*' }) == '*' {
switch s.r.readByte() {
case '/':
// Meets */, means comment end.
if isOptimizerHint {
s.lastHintPos = pos
return hintComment, pos, s.r.data(&pos)
} else {
return s.scan()
}
case 0:
break
case '*':
currentCharIsStar = true
continue
default:
currentCharIsStar = false
continue
}
}
// unclosed comment or other errors.
s.errs = append(s.errs, ParseErrorWith(s.r.data(&pos), s.r.p.Line))
return
}
for offset < len(comment) {
offset++
if !unicode.IsSpace(rune(comment[offset])) {
break
}
}
func startWithStar(s *Scanner) (tok int, pos Pos, lit string) {
pos = s.r.pos()
s.r.inc()
// skip and exit '/*!' if we see '*/'
if s.inBangComment && s.r.peek() == '/' {
s.inBangComment = false
s.r.inc()
return s.scan()
}
return offset
// otherwise it is just a normal star.
return '*', pos, "*"
}
func startWithAt(s *Scanner) (tok int, pos Pos, lit string) {
......@@ -787,6 +746,33 @@ func (s *Scanner) scanDigits() string {
return s.r.data(&pos)
}
// scanVersionDigits scans for `min` to `max` digits (range inclusive) used in
// `/*!12345 ... */` comments.
func (s *Scanner) scanVersionDigits(min, max int) (version CommentCodeVersion) {
pos := s.r.pos()
for i := 0; i < max; i++ {
ch := s.r.peek()
if isDigit(ch) {
version = version*10 + CommentCodeVersion(ch-'0')
s.r.inc()
} else if i < min {
s.r.p = pos
return CommentCodeNoVersion
} else {
break
}
}
return
}
func (s *Scanner) lastErrorAsWarn() {
if len(s.errs) == 0 {
return
}
s.warns = append(s.warns, s.errs[len(s.errs)-1])
s.errs = s.errs[:len(s.errs)-1]
}
type reader struct {
s string
p Pos
......
......@@ -14,11 +14,31 @@
package parser
import (
"fmt"
"strings"
"github.com/pingcap/parser/charset"
)
// CommentCodeVersion is used to track the highest version can be parsed in the comment with pattern /*T!00001 xxx */
type CommentCodeVersion int
const (
CommentCodeNoVersion CommentCodeVersion = iota
CommentCodeAutoRandom CommentCodeVersion = 40000
CommentCodeCurrentVersion
)
func (ccv CommentCodeVersion) String() string {
return fmt.Sprintf("%05d", ccv)
}
// WrapStringWithCodeVersion convert a string `str` to `/*T!xxxxx str */`, where `xxxxx` is determined by CommentCodeVersion.
func WrapStringWithCodeVersion(str string, ccv CommentCodeVersion) string {
return fmt.Sprintf("/*T!%05d %s */", ccv, str)
}
func isLetter(ch rune) bool {
return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z')
}
......@@ -82,7 +102,6 @@ func init() {
// set root trie node's token to invalid, so when input match nothing
// in the trie, invalid will be the default return token.
ruleTable.token = invalid
initTokenByte('*', int('*'))
initTokenByte('/', int('/'))
initTokenByte('+', int('+'))
initTokenByte('>', int('>'))
......@@ -121,6 +140,7 @@ func init() {
initTokenFunc("@", startWithAt)
initTokenFunc("/", startWithSlash)
initTokenFunc("*", startWithStar)
initTokenFunc("-", startWithDash)
initTokenFunc("#", startWithSharp)
initTokenFunc("Xx", startWithXx)
......@@ -138,9 +158,10 @@ var tokenMap = map[string]int{
"ACTION": action,
"ADD": add,
"ADDDATE": addDate,
"ADVISE": advise,
"ADMIN": admin,
"AFTER": after,
"AGG_TO_COP": hintAggToCop,
"AGAINST": against,
"ALL": all,
"ALGORITHM": algorithm,
"ALTER": alter,
......@@ -152,6 +173,7 @@ var tokenMap = map[string]int{
"ASC": asc,
"ASCII": ascii,
"AUTO_INCREMENT": autoIncrement,
"AUTO_RANDOM": autoRandom,
"AVG": avg,
"AVG_ROW_LENGTH": avgRowLength,
"BEGIN": begin,
......@@ -168,16 +190,19 @@ var tokenMap = map[string]int{
"BOOL": boolType,
"BOOLEAN": booleanType,
"BOTH": both,
"BOUND": bound,
"BTREE": btree,
"BUCKETS": buckets,
"BUILTINS": builtins,
"BY": by,
"BYTE": byteType,
"CACHE": cache,
"CANCEL": cancel,
"CASCADE": cascade,
"CASCADED": cascaded,
"CASE": caseKwd,
"CAST": cast,
"CAPTURE": capture,
"CHANGE": change,
"CHAR": charType,
"CHARACTER": character,
......@@ -217,6 +242,7 @@ var tokenMap = map[string]int{
"CURRENT_USER": currentUser,
"CURRENT_ROLE": currentRole,
"CURTIME": curTime,
"CYCLE": cycle,
"DATA": data,
"DATABASE": database,
"DATABASES": databases,
......@@ -257,7 +283,6 @@ var tokenMap = map[string]int{
"DYNAMIC": dynamic,
"ELSE": elseKwd,
"ENABLE": enable,
"ENABLE_PLAN_CACHE": hintEnablePlanCache,
"ENCLOSED": enclosed,
"ENCRYPTION": encryption,
"END": end,
......@@ -265,25 +290,32 @@ var tokenMap = map[string]int{
"ENGINE": engine,
"ENGINES": engines,
"ENUM": enum,
"ERROR": errorKwd,
"ESCAPE": escape,
"ESCAPED": escaped,
"EVENT": event,
"EVENTS": events,
"EVOLVE": evolve,
"EXACT": exact,
"EXCLUSIVE": exclusive,
"EXCEPT": except,
"EXCHANGE": exchange,
"EXECUTE": execute,
"EXISTS": exists,
"EXPANSION": expansion,
"EXPIRE": expire,
"EXPLAIN": explain,
"EXTENDED": extended,
"EXTRACT": extract,
"FALSE": falseKwd,
"FAULTS": faultsSym,
"FIELDS": fields,
"FILE": file,
"FIRST": first,
"FIXED": fixed,
"FLOAT": floatType,
"FLUSH": flush,
"FLASHBACK": flashback,
"FOLLOWING": following,
"FOR": forKwd,
"FORCE": force,
......@@ -294,6 +326,7 @@ var tokenMap = map[string]int{
"FULLTEXT": fulltext,
"FUNCTION": function,
"GENERATED": generated,
"GENERAL": general,
"GET_FORMAT": getFormat,
"GLOBAL": global,
"GRANT": grant,
......@@ -301,11 +334,10 @@ var tokenMap = map[string]int{
"GROUP": group,
"GROUP_CONCAT": groupConcat,
"HASH": hash,
"HASH_AGG": hintHASHAGG,
"HASH_JOIN": hintHJ,
"HAVING": having,
"HIGH_PRIORITY": highPriority,
"HISTORY": history,
"HOSTS": hosts,
"HOUR": hour,
"HOUR_MICROSECOND": hourMicrosecond,
"HOUR_MINUTE": hourMinute,
......@@ -313,14 +345,13 @@ var tokenMap = map[string]int{
"IDENTIFIED": identified,
"IF": ifKwd,
"IGNORE": ignore,
"IGNORE_INDEX": hintIgnoreIndex,
"IMPORT": importKwd,
"IN": in,
"INCREMENT": increment,
"INCREMENTAL": incremental,
"INDEX": index,
"INDEXES": indexes,
"INFILE": infile,
"INL_JOIN": hintINLJ,
"INNER": inner,
"INPLACE": inplace,
"INSTANT": instant,
......@@ -352,6 +383,7 @@ var tokenMap = map[string]int{
"KEYS": keys,
"KILL": kill,
"LABELS": labels,
"LANGUAGE": language,
"LAST": last,
"LEADING": leading,
"LEFT": left,
......@@ -368,6 +400,7 @@ var tokenMap = map[string]int{
"LOCALTIMESTAMP": localTs,
"LOCATION": location,
"LOCK": lock,
"LOGS": logs,
"LONG": long,
"LONGBLOB": longblobType,
"LONGTEXT": longtextType,
......@@ -376,7 +409,8 @@ var tokenMap = map[string]int{
"MATCH": match,
"MAX": max,
"MAX_CONNECTIONS_PER_HOUR": maxConnectionsPerHour,
"MAX_EXECUTION_TIME": maxExecutionTime,
"MAX_IDXNUM": max_idxnum,
"MAX_MINUTES": max_minutes,
"MAX_QUERIES_PER_HOUR": maxQueriesPerHour,
"MAX_ROWS": maxRows,
"MAX_UPDATES_PER_HOUR": maxUpdatesPerHour,
......@@ -386,7 +420,6 @@ var tokenMap = map[string]int{
"MEDIUMINT": mediumIntType,
"MEDIUMTEXT": mediumtextType,
"MEMORY": memory,
"MEMORY_QUOTA": hintMemoryQuota,
"MERGE": merge,
"MICROSECOND": microsecond,
"MIN": min,
......@@ -394,6 +427,7 @@ var tokenMap = map[string]int{
"MINUTE": minute,
"MINUTE_MICROSECOND": minuteMicrosecond,
"MINUTE_SECOND": minuteSecond,
"MINVALUE": minValue,
"MOD": mod,
"MODE": mode,
"MODIFY": modify,
......@@ -404,12 +438,16 @@ var tokenMap = map[string]int{
"NEVER": never,
"NEXT_ROW_ID": next_row_id,
"NO": no,
"NO_INDEX_MERGE": hintNoIndexMerge,
"NO_WRITE_TO_BINLOG": noWriteToBinLog,
"NOCACHE": nocache,
"NOCYCLE": nocycle,
"NODE_ID": nodeID,
"NODE_STATE": nodeState,
"NODEGROUP": nodegroup,
"NOMAXVALUE": nomaxvalue,
"NOMINVALUE": nominvalue,
"NONE": none,
"NOORDER": noorder,
"NOT": not,
"NOW": now,
"NULL": null,
......@@ -418,8 +456,6 @@ var tokenMap = map[string]int{
"NCHAR": ncharType,
"NVARCHAR": nvarcharType,
"OFFSET": offset,
"OLAP": hintOLAP,
"OLTP": hintOLTP,
"ON": on,
"ONLY": only,
"OPTIMISTIC": optimistic,
......@@ -438,6 +474,8 @@ var tokenMap = map[string]int{
"PARTITIONS": partitions,
"PASSWORD": password,
"PESSIMISTIC": pessimistic,
"PER_TABLE": per_table,
"PER_DB": per_db,
"PLUGINS": plugins,
"POSITION": position,
"PRECEDING": preceding,
......@@ -451,10 +489,8 @@ var tokenMap = map[string]int{
"PROFILE": profile,
"PROFILES": profiles,
"PUMP": pump,
"QB_NAME": hintQBName,
"QUARTER": quarter,
"QUERY": query,
"QUERY_TYPE": hintQueryType,
"QUERIES": queries,
"QUICK": quick,
"SHARD_ROW_ID_BITS": shardRowIDBits,
......@@ -463,8 +499,6 @@ var tokenMap = map[string]int{
"RECOVER": recover,
"REBUILD": rebuild,
"READ": read,
"READ_CONSISTENT_REPLICA": hintReadConsistentReplica,
"READ_FROM_STORAGE": hintReadFromStorage,
"REAL": realType,
"RECENT": recent,
"REDUNDANT": redundant,
......@@ -506,6 +540,7 @@ var tokenMap = map[string]int{
"SECOND_MICROSECOND": secondMicrosecond,
"SECURITY": security,
"SELECT": selectKwd,
"SEQUENCE": sequence,
"SERIAL": serial,
"SERIALIZABLE": serializable,
"SESSION": session,
......@@ -519,7 +554,6 @@ var tokenMap = map[string]int{
"SIMPLE": simple,
"SLAVE": slave,
"SLOW": slow,
"SM_JOIN": hintSMJ,
"SMALLINT": smallIntType,
"SNAPSHOT": snapshot,
"SOME": some,
......@@ -542,6 +576,7 @@ var tokenMap = map[string]int{
"SQL_TSI_YEAR": sqlTsiYear,
"SOURCE": source,
"SSL": ssl,
"STALENESS": staleness,
"START": start,
"STARTING": starting,
"STATS": stats,
......@@ -564,7 +599,7 @@ var tokenMap = map[string]int{
"STDDEV_SAMP": stddevSamp,
"STORED": stored,
"STRAIGHT_JOIN": straightJoin,
"STREAM_AGG": hintSTREAMAGG,
"STRONG": strong,
"SUBDATE": subDate,
"SUBJECT": subject,
"SUBPARTITION": subpartition,
......@@ -584,11 +619,7 @@ var tokenMap = map[string]int{
"THAN": than,
"THEN": then,
"TIDB": tidb,
"TIDB_HJ": hintHJ,
"TIDB_INLJ": hintINLJ,
"TIDB_SMJ": hintSMJ,
"TIKV": hintTiKV,
"TIFLASH": hintTiFlash,
"TIFLASH": tiFlash,
"TIME": timeType,
"TIMESTAMP": timestampType,
"TIMESTAMPADD": timestampAdd,
......@@ -626,13 +657,10 @@ var tokenMap = map[string]int{
"UNKNOWN": unknown,
"UNLOCK": unlock,
"UNSIGNED": unsigned,
"UNTIL": until,
"UPDATE": update,
"USAGE": usage,
"USE": use,
"USE_INDEX": hintUseIndex,
"USE_INDEX_MERGE": hintUseIndexMerge,
"USE_PLAN_CACHE": hintUsePlanCache,
"USE_TOJA": hintUseToja,
"USER": user,
"USING": using,
"UTC_DATE": utcDate,
......@@ -729,13 +757,94 @@ var windowFuncTokenMap = map[string]int{
// aliases are strings directly map to another string and use the same token.
var aliases = map[string]string{
"SCHEMA": "DATABASE",
"SCHEMAS": "DATABASES",
"DEC": "DECIMAL",
"SUBSTR": "SUBSTRING",
"TIDB_HJ": "HASH_JOIN",
"TIDB_INLJ": "INL_JOIN",
"TIDB_SMJ": "SM_JOIN",
"SCHEMA": "DATABASE",
"SCHEMAS": "DATABASES",
"DEC": "DECIMAL",
"SUBSTR": "SUBSTRING",
}
// hintedTokens is a set of tokens which recognizes a hint.
// According to https://dev.mysql.com/doc/refman/8.0/en/optimizer-hints.html,
// only SELECT, INSERT, REPLACE, UPDATE and DELETE accept optimizer hints.
// additionally we support CREATE and PARTITION for hints at table creation.
var hintedTokens = map[int]struct{}{
selectKwd: {},
insert: {},
replace: {},
update: {},
deleteKwd: {},
create: {},
partition: {},
}
var hintTokenMap = map[string]int{
// MySQL 8.0 hint names
"JOIN_FIXED_ORDER": hintJoinFixedOrder,
"JOIN_ORDER": hintJoinOrder,
"JOIN_PREFIX": hintJoinPrefix,
"JOIN_SUFFIX": hintJoinSuffix,
"BKA": hintBKA,
"NO_BKA": hintNoBKA,
"BNL": hintBNL,
"NO_BNL": hintNoBNL,
"HASH_JOIN": hintHashJoin,
"NO_HASH_JOIN": hintNoHashJoin,
"MERGE": hintMerge,
"NO_MERGE": hintNoMerge,
"INDEX_MERGE": hintIndexMerge,
"NO_INDEX_MERGE": hintNoIndexMerge,
"MRR": hintMRR,
"NO_MRR": hintNoMRR,
"NO_ICP": hintNoICP,
"NO_RANGE_OPTIMIZATION": hintNoRangeOptimization,
"SKIP_SCAN": hintSkipScan,
"NO_SKIP_SCAN": hintNoSkipScan,
"SEMIJOIN": hintSemijoin,
"NO_SEMIJOIN": hintNoSemijoin,
"MAX_EXECUTION_TIME": hintMaxExecutionTime,
"SET_VAR": hintSetVar,
"RESOURCE_GROUP": hintResourceGroup,
"QB_NAME": hintQBName,
// TiDB hint names
"AGG_TO_COP": hintAggToCop,
"ENABLE_PLAN_CACHE": hintEnablePlanCache,
"HASH_AGG": hintHashAgg,
"IGNORE_INDEX": hintIgnoreIndex,
"INL_HASH_JOIN": hintInlHashJoin,
"INL_JOIN": hintInlJoin,
"INL_MERGE_JOIN": hintInlMergeJoin,
"MEMORY_QUOTA": hintMemoryQuota,
"NO_SWAP_JOIN_INPUTS": hintNoSwapJoinInputs,
"QUERY_TYPE": hintQueryType,
"READ_CONSISTENT_REPLICA": hintReadConsistentReplica,
"READ_FROM_STORAGE": hintReadFromStorage,
"SM_JOIN": hintSMJoin,
"STREAM_AGG": hintStreamAgg,
"SWAP_JOIN_INPUTS": hintSwapJoinInputs,
"USE_INDEX_MERGE": hintUseIndexMerge,
"USE_INDEX": hintUseIndex,
"USE_PLAN_CACHE": hintUsePlanCache,
"USE_TOJA": hintUseToja,
// TiDB hint aliases
"TIDB_HJ": hintHashJoin,
"TIDB_INLJ": hintInlJoin,
"TIDB_SMJ": hintSMJoin,
// Other keywords
"OLAP": hintOLAP,
"OLTP": hintOLTP,
"TIKV": hintTiKV,
"TIFLASH": hintTiFlash,
"FALSE": hintFalse,
"TRUE": hintTrue,
"MB": hintMB,
"GB": hintGB,
"DUPSWEEDOUT": hintDupsWeedOut,
"FIRSTMATCH": hintFirstMatch,
"LOOSESCAN": hintLooseScan,
"MATERIALIZATION": hintMaterialization,
}
func (s *Scanner) isTokenIdentifier(lit string, offset int) int {
......@@ -759,7 +868,7 @@ func (s *Scanner) isTokenIdentifier(lit string, offset int) int {
}
}
checkBtFuncToken, tokenStr := false, string(data)
checkBtFuncToken := false
if s.r.peek() == '(' {
checkBtFuncToken = true
} else if s.sqlMode.HasIgnoreSpaceMode() {
......@@ -769,7 +878,7 @@ func (s *Scanner) isTokenIdentifier(lit string, offset int) int {
}
}
if checkBtFuncToken {
if tok := btFuncTokenMap[tokenStr]; tok != 0 {
if tok := btFuncTokenMap[string(data)]; tok != 0 {
return tok
}
}
......
......@@ -61,10 +61,18 @@ const (
ActionRepairTable ActionType = 29
ActionSetTiFlashReplica ActionType = 30
ActionUpdateTiFlashReplicaStatus ActionType = 31
ActionAddPrimaryKey ActionType = 32
ActionDropPrimaryKey ActionType = 33
ActionCreateSequence ActionType = 34
ActionAlterSequence ActionType = 35
ActionDropSequence ActionType = 36
)
// AddIndexStr is a string related to the operation of "add index".
const AddIndexStr = "add index"
const (
// AddIndexStr is a string related to the operation of "add index".
AddIndexStr = "add index"
AddPrimaryKeyStr = "add primary key"
)
var actionMap = map[ActionType]string{
ActionCreateSchema: "create schema",
......@@ -98,6 +106,11 @@ var actionMap = map[ActionType]string{
ActionRepairTable: "repair table",
ActionSetTiFlashReplica: "set tiflash replica",
ActionUpdateTiFlashReplicaStatus: "update tiflash replica status",
ActionAddPrimaryKey: AddPrimaryKeyStr,
ActionDropPrimaryKey: "drop primary key",
ActionCreateSequence: "create sequence",
ActionAlterSequence: "alter sequence",
ActionDropSequence: "drop sequence",
}
// String return current ddl action in string
......
......@@ -93,6 +93,8 @@ type ColumnInfo struct {
types.FieldType `json:"type"`
State SchemaState `json:"state"`
Comment string `json:"comment"`
// A hidden column is used internally(expression index) and are not accessible by users.
Hidden bool `json:"hidden"`
// Version means the version of the column info.
// Version = 0: For OriginDefaultValue and DefaultValue of timestamp column will stores the default time in system time zone.
// That is a bug if multiple TiDB servers in different system time zone.
......@@ -141,6 +143,18 @@ func (c *ColumnInfo) GetDefaultValue() interface{} {
return c.DefaultValue
}
// GetTypeDesc gets the description for column type.
func (c *ColumnInfo) GetTypeDesc() string {
desc := c.FieldType.CompactStr()
if mysql.HasUnsignedFlag(c.Flag) && c.Tp != mysql.TypeBit && c.Tp != mysql.TypeYear {
desc += " unsigned"
}
if mysql.HasZerofillFlag(c.Flag) && c.Tp != mysql.TypeYear {
desc += " zerofill"
}
return desc
}
// FindColumnInfo finds ColumnInfo in cols by name.
func FindColumnInfo(cols []*ColumnInfo, name string) *ColumnInfo {
name = strings.ToLower(name)
......@@ -221,6 +235,8 @@ type TableInfo struct {
ShardRowIDBits uint64
// MaxShardRowIDBits uses to record the max ShardRowIDBits be used so far.
MaxShardRowIDBits uint64 `json:"max_shard_row_id_bits"`
// AutoRandomBits is used to set the bit number to shard automatically when PKIsHandle.
AutoRandomBits uint64 `json:"auto_random_bits"`
// PreSplitRegions specify the pre-split region when create table.
// The pre-split region num is 2^(PreSplitRegions-1).
// And the PreSplitRegions should less than or equal to ShardRowIDBits.
......@@ -231,6 +247,9 @@ type TableInfo struct {
Compression string `json:"compression"`
View *ViewInfo `json:"view"`
Sequence *SequenceInfo `json:"sequence"`
// Lock represent the table lock info.
Lock *TableLockInfo `json:"Lock"`
......@@ -379,11 +398,9 @@ func (t *TableInfo) Clone() *TableInfo {
// GetPkName will return the pk name if pk exists.
func (t *TableInfo) GetPkName() CIStr {
if t.PKIsHandle {
for _, colInfo := range t.Columns {
if mysql.HasPriKeyFlag(colInfo.Flag) {
return colInfo.Name
}
for _, colInfo := range t.Columns {
if mysql.HasPriKeyFlag(colInfo.Flag) {
return colInfo.Name
}
}
return CIStr{}
......@@ -417,6 +434,19 @@ func (t *TableInfo) IsAutoIncColUnsigned() bool {
return mysql.HasUnsignedFlag(col.Flag)
}
// ContainsAutoRandomBits indicates whether a table contains auto_random column.
func (t *TableInfo) ContainsAutoRandomBits() bool {
return t.AutoRandomBits != 0
}
// IsAutoRandomBitColUnsigned indicates whether the auto_random column is unsigned. Make sure the table contains auto_random before calling this method.
func (t *TableInfo) IsAutoRandomBitColUnsigned() bool {
if !t.PKIsHandle || t.AutoRandomBits == 0 {
return false
}
return mysql.HasUnsignedFlag(t.GetPkColInfo().Flag)
}
// Cols returns the columns of the table in public state.
func (t *TableInfo) Cols() []*ColumnInfo {
publicColumns := make([]*ColumnInfo, len(t.Columns))
......@@ -472,11 +502,16 @@ func (t *TableInfo) ColumnIsInIndex(c *ColumnInfo) bool {
return false
}
// IsView checks if tableinfo is a view
// IsView checks if TableInfo is a view.
func (t *TableInfo) IsView() bool {
return t.View != nil
}
// IsSequence checks if TableInfo is a sequence.
func (t *TableInfo) IsSequence() bool {
return t.Sequence != nil
}
// ViewAlgorithm is VIEW's SQL AlGORITHM characteristic.
// See https://dev.mysql.com/doc/refman/5.7/en/view-algorithms.html
type ViewAlgorithm int
......@@ -550,6 +585,33 @@ type ViewInfo struct {
Cols []CIStr `json:"view_cols"`
}
const (
DefaultSequenceCacheBool = true
DefaultSequenceCycleBool = false
DefaultSequenceOrderBool = false
DefaultSequenceCacheValue = int64(1000)
DefaultSequenceIncrementValue = int64(1)
DefaultPositiveSequenceStartValue = int64(1)
DefaultNegativeSequenceStartValue = int64(-1)
DefaultPositiveSequenceMinValue = int64(1)
DefaultPositiveSequenceMaxValue = int64(9223372036854775806)
DefaultNegativeSequenceMaxValue = int64(-1)
DefaultNegativeSequenceMinValue = int64(-9223372036854775807)
)
// SequenceInfo provide meta data describing a DB sequence.
type SequenceInfo struct {
Start int64 `json:"sequence_start"`
Cache bool `json:"sequence_cache"`
Order bool `json:"sequence_order"`
Cycle bool `json:"sequence_cycle"`
MinValue int64 `json:"sequence_min_value"`
MaxValue int64 `json:"sequence_max_value"`
Increment int64 `json:"sequence_increment"`
CacheValue int64 `json:"sequence_cache_value"`
Comment string `json:"sequence_comment"`
}
// PartitionType is the type for PartitionInfo
type PartitionType int
......
......@@ -168,6 +168,8 @@ const (
const (
// SystemDB is the name of system database.
SystemDB = "mysql"
// GlobalPrivTable is the table in system db contains global scope privilege info.
GlobalPrivTable = "global_priv"
// UserTable is the table in system db contains user info.
UserTable = "User"
// DBTable is the table in system db contains db scope privilege info.
......@@ -242,6 +244,10 @@ const (
// ShutdownPriv the privilege to shutdown a server.
ShutdownPriv
// ReloadPriv is the privilege to enable the use of the FLUSH statement.
ReloadPriv
// FilePriv is the privilege to enable the use of LOAD DATA and SELECT ... INTO OUTFILE.
FilePriv
// AllPriv is the privilege for all actions.
AllPriv
......@@ -315,6 +321,8 @@ var Priv2UserCol = map[PrivilegeType]string{
AlterRoutinePriv: "Alter_routine_priv",
EventPriv: "Event_priv",
ShutdownPriv: "Shutdown_priv",
ReloadPriv: "Reload_priv",
FilePriv: "File_priv",
}
// Col2PrivType is the privilege tables column name to privilege type.
......@@ -345,6 +353,8 @@ var Col2PrivType = map[string]PrivilegeType{
"Alter_routine_priv": AlterRoutinePriv,
"Event_priv": EventPriv,
"Shutdown_priv": ShutdownPriv,
"Reload_priv": ReloadPriv,
"File_priv": FilePriv,
}
// Command2Str is the command information to command name.
......@@ -411,6 +421,8 @@ var Priv2Str = map[PrivilegeType]string{
AlterRoutinePriv: "ALTER ROUTINE",
EventPriv: "EVENT",
ShutdownPriv: "SHUTDOWN",
ReloadPriv: "RELOAD",
FilePriv: "FILE",
}
// Priv2SetStr is the map for privilege to string.
......@@ -449,7 +461,7 @@ var SetStr2Priv = map[string]PrivilegeType{
}
// AllGlobalPrivs is all the privileges in global scope.
var AllGlobalPrivs = []PrivilegeType{SelectPriv, InsertPriv, UpdatePriv, DeletePriv, CreatePriv, DropPriv, ProcessPriv, ReferencesPriv, AlterPriv, ShowDBPriv, SuperPriv, ExecutePriv, IndexPriv, CreateUserPriv, TriggerPriv, CreateViewPriv, ShowViewPriv, CreateRolePriv, DropRolePriv, CreateTMPTablePriv, LockTablesPriv, CreateRoutinePriv, AlterRoutinePriv, EventPriv, ShutdownPriv}
var AllGlobalPrivs = []PrivilegeType{SelectPriv, InsertPriv, UpdatePriv, DeletePriv, CreatePriv, DropPriv, ProcessPriv, ReferencesPriv, AlterPriv, ShowDBPriv, SuperPriv, ExecutePriv, IndexPriv, CreateUserPriv, TriggerPriv, CreateViewPriv, ShowViewPriv, CreateRolePriv, DropRolePriv, CreateTMPTablePriv, LockTablesPriv, CreateRoutinePriv, AlterRoutinePriv, EventPriv, ShutdownPriv, ReloadPriv, FilePriv}
// AllDBPrivs is all the privileges in database scope.
var AllDBPrivs = []PrivilegeType{SelectPriv, InsertPriv, UpdatePriv, DeletePriv, CreatePriv, DropPriv, AlterPriv, ExecutePriv, IndexPriv, CreateViewPriv, ShowViewPriv}
......
......@@ -884,6 +884,7 @@ const (
ErrErrorLast = 1863
ErrMaxExecTimeExceeded = 1907
ErrInvalidFieldSize = 3013
ErrIncorrectType = 3064
ErrInvalidJSONData = 3069
ErrGeneratedColumnFunctionIsNotAllowed = 3102
ErrUnsupportedAlterInplaceOnVirtualColumn = 3103
......@@ -899,8 +900,9 @@ const (
ErrInvalidJSONPathWildcard = 3149
ErrInvalidJSONContainsPathType = 3150
ErrJSONUsedAsKey = 3152
ErrInvalidJSONPathArrayCell = 3165
ErrBadUser = 3162
ErrUserAlreadyExists = 3163
ErrInvalidJSONPathArrayCell = 3165
ErrInvalidEncryptionOption = 3184
ErrRoleNotGranted = 3530
ErrLockAcquireFailAndNoWaitSet = 3572
......@@ -925,28 +927,144 @@ const (
ErrWindowNoGroupOrderUnused = 3597
ErrWindowExplainJson = 3598
ErrWindowFunctionIgnoresFrame = 3599
ErrDataTruncatedFunctionalIndex = 3751
ErrDataOutOfRangeFunctionalIndex = 3752
ErrFunctionalIndexOnJsonOrGeometryFunction = 3753
ErrFunctionalIndexRefAutoIncrement = 3754
ErrCannotDropColumnFunctionalIndex = 3755
ErrFunctionalIndexPrimaryKey = 3756
ErrFunctionalIndexOnLob = 3757
ErrFunctionalIndexFunctionIsNotAllowed = 3758
ErrFulltextFunctionalIndex = 3759
ErrSpatialFunctionalIndex = 3760
ErrWrongKeyColumnFunctionalIndex = 3761
ErrFunctionalIndexOnField = 3762
ErrFKIncompatibleColumns = 3780
ErrFunctionalIndexRowValueIsNotAllowed = 3800
ErrDependentByFunctionalIndex = 3837
ErrInvalidJsonValueForFuncIndex = 3903
ErrJsonValueOutOfRangeForFuncIndex = 3904
ErrFunctionalIndexDataIsTooLong = 3907
ErrFunctionalIndexNotApplicable = 3909
// MariaDB errors.
ErrOnlyOneDefaultPartionAllowed = 4030
ErrWrongPartitionTypeExpectedSystemTime = 4113
ErrSystemVersioningWrongPartitions = 4128
ErrSequenceRunOut = 4135
ErrSequenceInvalidData = 4136
ErrSequenceAccessFail = 4137
ErrNotSequence = 4138
ErrUnknownSequence = 4139
ErrWrongInsertIntoSequence = 4140
ErrSequenceInvalidTableStructure = 4141
// TiDB self-defined errors.
ErrMemExceedThreshold = 8001
ErrForUpdateCantRetry = 8002
ErrAdminCheckTable = 8003
ErrTxnTooLarge = 8004
ErrWriteConflictInTiDB = 8005
ErrInvalidPluginID = 8101
ErrInvalidPluginManifest = 8102
ErrInvalidPluginName = 8103
ErrInvalidPluginVersion = 8104
ErrDuplicatePlugin = 8105
ErrInvalidPluginSysVarName = 8106
ErrRequireVersionCheckFail = 8107
ErrUnsupportedReloadPlugin = 8018
ErrUnsupportedReloadPluginVar = 8019
ErrTableLocked = 8020
ErrMemExceedThreshold = 8001
ErrForUpdateCantRetry = 8002
ErrAdminCheckTable = 8003
ErrTxnTooLarge = 8004
ErrWriteConflictInTiDB = 8005
ErrInvalidPluginID = 8101
ErrInvalidPluginManifest = 8102
ErrInvalidPluginName = 8103
ErrInvalidPluginVersion = 8104
ErrDuplicatePlugin = 8105
ErrInvalidPluginSysVarName = 8106
ErrRequireVersionCheckFail = 8107
ErrUnsupportedType = 8108
ErrAnalyzeMissIndex = 8109
ErrCartesianProductUnsupported = 8110
ErrPreparedStmtNotFound = 8111
ErrWrongParamCount = 8112
ErrSchemaChanged = 8113
ErrUnknownPlan = 8114
ErrPrepareMulti = 8115
ErrPrepareDDL = 8116
ErrResultIsEmpty = 8117
ErrBuildExecutor = 8118
ErrBatchInsertFail = 8119
ErrGetStartTS = 8120
ErrPrivilegeCheckFail = 8121
ErrInvalidWildCard = 8122
ErrMixOfGroupFuncAndFieldsIncompatible = 8123
ErrUnsupportedReloadPlugin = 8018
ErrUnsupportedReloadPluginVar = 8019
ErrTableLocked = 8020
ErrNotExist = 8021
ErrTxnRetryable = 8022
ErrCannotSetNilValue = 8023
ErrInvalidTxn = 8024
ErrEntryTooLarge = 8025
ErrNotImplemented = 8026
ErrInfoSchemaExpired = 8027
ErrInfoSchemaChanged = 8028
ErrBadNumber = 8029
ErrCastAsSignedOverflow = 8030
ErrCastNegIntAsUnsigned = 8031
ErrInvalidYearFormat = 8032
ErrInvalidYear = 8033
ErrIncorrectDatetimeValue = 8034
ErrInvalidTimeFormat = 8036
ErrInvalidWeekModeFormat = 8037
ErrFieldGetDefaultFailed = 8038
ErrIndexOutBound = 8039
ErrUnsupportedOp = 8040
ErrRowNotFound = 8041
ErrTableStateCantNone = 8042
ErrColumnStateNonPublic = 8043
ErrIndexStateCantNone = 8044
ErrInvalidRecordKey = 8045
ErrColumnStateCantNone = 8046
ErrUnsupportedValueForVar = 8047
ErrUnsupportedIsolationLevel = 8048
ErrLoadPrivilege = 8049
ErrInvalidPrivilegeType = 8050
ErrUnknownFieldType = 8051
ErrInvalidSequence = 8052
ErrCantGetValidID = 8053
ErrCantSetToNull = 8054
ErrSnapshotTooOld = 8055
ErrInvalidTableID = 8056
ErrInvalidType = 8057
ErrUnknownAllocatorType = 8058
ErrAutoRandReadFailed = 8059
ErrInvalidIncrementAndOffset = 8060
ErrWarnOptimizerHintUnsupportedHint = 8061
ErrWarnOptimizerHintInvalidToken = 8062
ErrWarnMemoryQuotaOverflow = 8063
ErrWarnOptimizerHintParseError = 8064
ErrWarnOptimizerHintInvalidInteger = 8065
// Error codes used by TiDB ddl package
ErrUnsupportedDDLOperation = 8200
ErrNotOwner = 8201
ErrCantDecodeIndex = 8202
ErrInvalidDDLWorker = 8203
ErrInvalidDDLJob = 8204
ErrInvalidDDLJobFlag = 8205
ErrWaitReorgTimeout = 8206
ErrInvalidStoreVersion = 8207
ErrUnknownTypeLength = 8208
ErrUnknownFractionLength = 8209
ErrInvalidDDLState = 8210
ErrReorgPanic = 8211
ErrInvalidSplitRegionRanges = 8212
ErrInvalidDDLJobVersion = 8213
ErrCancelledDDLJob = 8214
ErrRepairTable = 8215
ErrInvalidAutoRandom = 8216
ErrInvalidHashKeyFlag = 8217
ErrInvalidListIndex = 8218
ErrInvalidListMetaData = 8219
ErrWriteOnSnapshot = 8220
ErrInvalidKey = 8221
ErrInvalidIndexKey = 8222
ErrDataInConsistent = 8223
ErrDDLJobNotFound = 8224
ErrCancelFinishedDDLJob = 8225
ErrCannotCancelDDLJob = 8226
ErrSequenceUnsupportedTableOption = 8227
// TiKV/PD errors.
ErrPDServerTimeout = 9001
......@@ -956,4 +1074,5 @@ const (
ErrRegionUnavailable = 9005
ErrGCTooEarly = 9006
ErrWriteConflict = 9007
ErrTiKVStoreLimit = 9008
)
{
mv go.mod1 go.mod
mv go.sum1 go.sum
GO111MODULE=on go test -p 1 -race -covermode=atomic -coverprofile=coverage.txt -coverpkg=./... ./...
} || {
mv go.mod go.mod1
mv go.sum go.sum1
}
mv go.mod go.mod1
mv go.sum go.sum1
GO111MODULE=on go test -p 1 -race -covermode=atomic -coverprofile=coverage.txt -coverpkg=./... ./...
此差异已折叠。
此差异已折叠。
/*
Copyright 2017 Google Inc.
Copyright 2019 The Vitess Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册