[parser] parser: support syntax for AUTO_RANDOM (#571)
This commit is contained in:
@ -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")
|
||||
}
|
||||
|
||||
@ -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]
|
||||
|
||||
@ -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('/')
|
||||
|
||||
@ -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 {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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))
|
||||
|
||||
@ -1037,6 +1037,7 @@ const (
|
||||
ErrInvalidDDLJobVersion = 8213
|
||||
ErrCancelledDDLJob = 8214
|
||||
ErrRepairTable = 8215
|
||||
ErrInvalidAutoRandom = 8216
|
||||
|
||||
// TiKV/PD errors.
|
||||
ErrPDServerTimeout = 9001
|
||||
|
||||
@ -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'",
|
||||
|
||||
17337
parser/parser.go
17337
parser/parser.go
File diff suppressed because it is too large
Load Diff
@ -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"
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
Reference in New Issue
Block a user