[parser] parser: support syntax for AUTO_RANDOM (#571)

This commit is contained in:
Tanner
2019-12-10 21:09:00 +08:00
committed by Ti Chi Robot
parent 5264503d28
commit 385d05ce3e
12 changed files with 8797 additions and 8670 deletions

View File

@ -448,6 +448,7 @@ const (
ColumnOptionCheck
ColumnOptionColumnFormat
ColumnOptionStorage
ColumnOptionAutoRandom
)
var (
@ -470,8 +471,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
}
@ -548,6 +550,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")
}

View File

@ -231,6 +231,8 @@ func (ts *testDDLSuite) TestDDLColumnOptionRestore(c *C) {
{"STORAGE DEFAULT", "STORAGE DEFAULT"},
{"STORAGE DISK", "STORAGE DISK"},
{"STORAGE MEMORY", "STORAGE MEMORY"},
{"AUTO_RANDOM (3)", "AUTO_RANDOM(3)"},
{"AUTO_RANDOM", "AUTO_RANDOM"},
}
extractNodeFunc := func(node Node) Node {
return node.(*CreateTableStmt).Cols[0].Options[0]

View File

@ -445,6 +445,21 @@ func startWithSlash(s *Scanner) (tok int, pos Pos, lit string) {
}
}
if strings.HasPrefix(comment, "/*T!") {
commentVersion := extractVersionCodeInComment(comment)
if commentVersion != CommentCodeNoVersion && commentVersion <= CommentCodeCurrentVersion {
sql := SpecVersionCodePattern.ReplaceAllStringFunc(comment, TrimCodeVersionComment)
s.specialComment = &mysqlSpecificCodeScanner{
Scanner: s.InheritScanner(sql),
Pos: Pos{
pos.Line,
pos.Col,
pos.Offset + sqlOffsetInComment(comment),
},
}
}
}
return s.scan()
}
tok = int('/')

View File

@ -268,6 +268,30 @@ func (s *testLexerSuite) TestSpecialComment(c *C) {
c.Assert(pos, Equals, Pos{1, 1, 16})
}
func (s *testLexerSuite) TestSpecialCodeComment(c *C) {
specCmt := "/*T!123456 auto_random(5) */"
c.Assert(extractVersionCodeInComment(specCmt), Equals, CommentCodeVersion(123456))
specCmt = "/*T!40000 auto_random(5) */"
c.Assert(extractVersionCodeInComment(specCmt), Equals, CommentCodeAutoRandom)
l := NewScanner(specCmt)
tok, pos, lit := l.scan()
c.Assert(tok, Equals, identifier)
c.Assert(lit, Equals, "auto_random")
c.Assert(pos, Equals, Pos{0, 0, 10})
tok, pos, lit = l.scan()
c.Assert(tok, Equals, int('('))
tok, pos, lit = l.scan()
c.Assert(lit, Equals, "5")
c.Assert(pos, Equals, Pos{0, 12, 22})
tok, pos, lit = l.scan()
c.Assert(tok, Equals, int(')'))
l = NewScanner(WrapStringWithCodeVersion("auto_random(5)", CommentCodeCurrentVersion+1))
tok, pos, lit = l.scan()
c.Assert(tok, Equals, 0)
}
func (s *testLexerSuite) TestOptimizerHint(c *C) {
l := NewScanner(" /*+ BKA(t1) */")
tokens := []struct {

View File

@ -14,11 +14,40 @@
package parser
import (
"fmt"
"strconv"
"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)
}
func extractVersionCodeInComment(comment string) CommentCodeVersion {
code, err := strconv.Atoi(specVersionCodeValue.FindString(comment))
if err != nil {
return CommentCodeNoVersion
}
return CommentCodeVersion(code)
}
// 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')
}
@ -154,6 +183,7 @@ var tokenMap = map[string]int{
"ASC": asc,
"ASCII": ascii,
"AUTO_INCREMENT": autoIncrement,
"AUTO_RANDOM": autoRandom,
"AVG": avg,
"AVG_ROW_LENGTH": avgRowLength,
"BEGIN": begin,

View File

@ -235,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_shard_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.
@ -393,11 +395,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{}
@ -431,6 +431,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))

View File

@ -1037,6 +1037,7 @@ const (
ErrInvalidDDLJobVersion = 8213
ErrCancelledDDLJob = 8214
ErrRepairTable = 8215
ErrInvalidAutoRandom = 8216
// TiKV/PD errors.
ErrPDServerTimeout = 9001

View File

@ -1016,6 +1016,7 @@ var MySQLErrName = map[uint16]string{
ErrCantSetToNull: "cannot set variable to null",
ErrSnapshotTooOld: "snapshot is older than GC safe point %s",
ErrInvalidTableID: "invalid TableID",
ErrInvalidAutoRandom: "Invalid auto random: %s",
ErrUnsupportedType: "Unsupported type %T",
ErrAnalyzeMissIndex: "Index '%s' in field list does not exist in table '%s'",

File diff suppressed because it is too large Load Diff

View File

@ -289,6 +289,7 @@ import (
any "ANY"
ascii "ASCII"
autoIncrement "AUTO_INCREMENT"
autoRandom "AUTO_RANDOM"
avgRowLength "AVG_ROW_LENGTH"
avg "AVG"
begin "BEGIN"
@ -2621,6 +2622,10 @@ ColumnOption:
yylex.AppendError(yylex.Errorf("The STORAGE clause is parsed but ignored by all storage engines."))
parser.lastErrorAsWarn()
}
| "AUTO_RANDOM" OptFieldLen
{
$$ = &ast.ColumnOption{Tp: ast.ColumnOptionAutoRandom, AutoRandomBitLength: $2.(int)}
}
StorageMedia:
"DEFAULT" | "DISK" | "MEMORY"
@ -4628,7 +4633,7 @@ UnReservedKeyword:
| "MAX_USER_CONNECTIONS" | "REPLICATION" | "CLIENT" | "SLAVE" | "RELOAD" | "TEMPORARY" | "ROUTINE" | "EVENT" | "ALGORITHM" | "DEFINER" | "INVOKER" | "MERGE" | "TEMPTABLE" | "UNDEFINED" | "SECURITY" | "CASCADED"
| "RECOVER" | "CIPHER" | "SUBJECT" | "ISSUER" | "X509" | "NEVER" | "EXPIRE" | "ACCOUNT" | "INCREMENTAL" | "CPU" | "MEMORY" | "BLOCK" | "IO" | "CONTEXT" | "SWITCHES" | "PAGE" | "FAULTS" | "IPC" | "SWAPS" | "SOURCE"
| "TRADITIONAL" | "SQL_BUFFER_RESULT" | "DIRECTORY" | "HISTORY" | "LIST" | "NODEGROUP" | "SYSTEM_TIME" | "PARTIAL" | "SIMPLE" | "REMOVE" | "PARTITIONING" | "STORAGE" | "DISK" | "STATS_SAMPLE_PAGES" | "SECONDARY_ENGINE" | "SECONDARY_LOAD" | "SECONDARY_UNLOAD" | "VALIDATION"
| "WITHOUT" | "RTREE" | "EXCHANGE" | "COLUMN_FORMAT" | "REPAIR" | "IMPORT" | "DISCARD" | "TABLE_CHECKSUM" | "UNICODE"
| "WITHOUT" | "RTREE" | "EXCHANGE" | "COLUMN_FORMAT" | "REPAIR" | "IMPORT" | "DISCARD" | "TABLE_CHECKSUM" | "UNICODE" | "AUTO_RANDOM"
| "SQL_TSI_DAY" | "SQL_TSI_HOUR" | "SQL_TSI_MINUTE" | "SQL_TSI_MONTH" | "SQL_TSI_QUARTER" | "SQL_TSI_SECOND" |
"SQL_TSI_WEEK" | "SQL_TSI_YEAR" | "INVISIBLE" | "VISIBLE" | "TYPE" | "NOWAIT" | "REPLICA" | "LOCATION" | "LABELS"
| "LOGS" | "HOSTS" | "AGAINST" | "EXPANSION" | "INCREMENT" | "MINVALUE" | "NOMAXVALUE" | "NOMINVALUE" | "NOCACHE" | "CACHE" | "CYCLE" | "NOCYCLE" | "NOORDER" | "SEQUENCE" | "MAX_MINUTES" | "MAX_IDXNUM" | "PER_TABLE" | "PER_DB"

View File

@ -2812,9 +2812,16 @@ func (s *testParserSuite) TestDDL(c *C) {
{"create temporary sequence sEq order start with 0 minvalue 0 maxvalue 1000", true, "CREATE TEMPORARY SEQUENCE `sEq` ORDER START WITH 0 MINVALUE 0 MAXVALUE 1000"},
{"create sequence if not exists seq increment 1 start with 0 minvalue -2 maxvalue 1000", true, "CREATE SEQUENCE IF NOT EXISTS `seq` INCREMENT BY 1 START WITH 0 MINVALUE -2 MAXVALUE 1000"},
{"create sequence seq increment -1 start with -1 minvalue -1 maxvalue -1000 cache = 10 nocycle noorder", true, "CREATE SEQUENCE `seq` INCREMENT BY -1 START WITH -1 MINVALUE -1 MAXVALUE -1000 CACHE 10 NOCYCLE NOORDER"},
// test sequence is not a reserved keyword
{"create table sequence (a int)", true, "CREATE TABLE `sequence` (`a` INT)"},
{"create table t (sequence int)", true, "CREATE TABLE `t` (`sequence` INT)"},
// for auto_random
{"create table t (a bigint auto_random(3) primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM(3) PRIMARY KEY,`b` VARCHAR(255))"},
{"create table t (a bigint auto_random primary key, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT AUTO_RANDOM PRIMARY KEY,`b` VARCHAR(255))"},
{"create table t (a bigint primary key auto_random(4), b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT PRIMARY KEY AUTO_RANDOM(4),`b` VARCHAR(255))"},
{"create table t (a bigint primary key auto_random(3) primary key unique, b varchar(255))", true, "CREATE TABLE `t` (`a` BIGINT PRIMARY KEY AUTO_RANDOM(3) PRIMARY KEY UNIQUE KEY,`b` VARCHAR(255))"},
}
s.RunTest(c, table)
}

View File

@ -52,6 +52,10 @@ var (
specCodePattern = regexp.MustCompile(`\/\*!(M?[0-9]{5,6})?([^*]|\*+[^*/])*\*+\/`)
specCodeStart = regexp.MustCompile(`^\/\*!(M?[0-9]{5,6})?[ \t]*`)
specCodeEnd = regexp.MustCompile(`[ \t]*\*\/$`)
// SpecVersionCodePattern is a pattern for special comments with version.
SpecVersionCodePattern = regexp.MustCompile(`\/\*T![0-9]{5,6}([^*]|\*+[^*/])*\*+\/`)
specVersionCodeStart = regexp.MustCompile(`^\/\*T![0-9]{5,6}[ \t]*`)
specVersionCodeValue = regexp.MustCompile(`[0-9]{5,6}`)
)
func init() {
@ -76,6 +80,11 @@ func TrimComment(txt string) string {
return specCodeEnd.ReplaceAllString(txt, "")
}
func TrimCodeVersionComment(txt string) string {
txt = specVersionCodeStart.ReplaceAllString(txt, "")
return specCodeEnd.ReplaceAllString(txt, "")
}
// Parser represents a parser instance. Some temporary objects are stored in it to reduce object allocation during Parse function.
type Parser struct {
charset string