Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions ast/ddl.go
Original file line number Diff line number Diff line change
Expand Up @@ -527,6 +527,7 @@ const (
ColumnOptionStorage
ColumnOptionAutoRandom
ColumnOptionSecondaryEngineAttribute
ColumnOptionSrid
)

var (
Expand Down Expand Up @@ -564,6 +565,8 @@ type ColumnOption struct {
ConstraintName string
PrimaryKeyTp PrimaryKeyType
SecondaryEngineAttr string
// UintValue is only for ColumnOptionSrid.
UintValue uint64
}

// Restore implements Node interface.
Expand Down Expand Up @@ -686,6 +689,9 @@ func (n *ColumnOption) Restore(ctx *format.RestoreCtx) error {
ctx.WriteKeyWord("SECONDARY_ENGINE_ATTRIBUTE")
ctx.WritePlain(" = ")
ctx.WriteString(n.StrValue)
case ColumnOptionSrid:
ctx.WriteKeyWord("SRID ")
ctx.WritePlainf("%d", n.UintValue)
default:
return errors.New("An error occurred while splicing ColumnOption")
}
Expand Down Expand Up @@ -933,6 +939,8 @@ const (
// It will be rewritten into ConstraintColumnar after preprocessor phase.
ConstraintVector
ConstraintColumnar
// ConstraintSpatial is only used in AST.
ConstraintSpatial
)

// Constraint is constraint for table definition.
Expand Down Expand Up @@ -987,6 +995,8 @@ func (n *Constraint) Restore(ctx *format.RestoreCtx) error {
ctx.WriteKeyWord("UNIQUE INDEX")
case ConstraintFulltext:
ctx.WriteKeyWord("FULLTEXT")
case ConstraintSpatial:
ctx.WriteKeyWord("SPATIAL")
case ConstraintCheck:
if n.Name != "" {
ctx.WriteKeyWord("CONSTRAINT ")
Expand Down
11 changes: 11 additions & 0 deletions mysql/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ const (
TypeGeometry byte = 0xff

TypeTiDBVectorFloat32 byte = 0xe1

// MySQL reports every spatial column as TypeGeometry on the wire; these
// bytes exist only so the parser can keep the declared spatial subtype
// (following the TypeTiDBVectorFloat32 precedent for non-wire types).
TypePoint byte = 0xe2
TypeLineString byte = 0xe3
TypePolygon byte = 0xe4
TypeMultiPoint byte = 0xe5
TypeMultiLineString byte = 0xe6
TypeMultiPolygon byte = 0xe7
TypeGeometryCollection byte = 0xe8
)

// Flag information.
Expand Down
8 changes: 8 additions & 0 deletions parser/keyword_classes.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions parser/keywords.go
Original file line number Diff line number Diff line change
Expand Up @@ -429,6 +429,8 @@ var Keywords = []KeywordsType{
{"FULL", false, "unreserved"},
{"FUNCTION", false, "unreserved"},
{"GENERAL", false, "unreserved"},
{"GEOMETRY", false, "unreserved"},
{"GEOMETRYCOLLECTION", false, "unreserved"},
{"GLOBAL", false, "unreserved"},
{"GRANTS", false, "unreserved"},
{"GROUP_REPLICATION", false, "unreserved"},
Expand Down Expand Up @@ -471,6 +473,7 @@ var Keywords = []KeywordsType{
{"LESS", false, "unreserved"},
{"LEVEL", false, "unreserved"},
{"LIBRARY", false, "unreserved"},
{"LINESTRING", false, "unreserved"},
{"LIST", false, "unreserved"},
{"LOAD_STATS", false, "unreserved"},
{"LOCAL", false, "unreserved"},
Expand Down Expand Up @@ -500,6 +503,9 @@ var Keywords = []KeywordsType{
{"MODE", false, "unreserved"},
{"MODIFY", false, "unreserved"},
{"MONTH", false, "unreserved"},
{"MULTILINESTRING", false, "unreserved"},
{"MULTIPOINT", false, "unreserved"},
{"MULTIPOLYGON", false, "unreserved"},
{"MUTEX", false, "unreserved"},
{"NAMES", false, "unreserved"},
{"NATIONAL", false, "unreserved"},
Expand Down Expand Up @@ -553,6 +559,7 @@ var Keywords = []KeywordsType{
{"PLUGINS", false, "unreserved"},
{"POINT", false, "unreserved"},
{"POLICY", false, "unreserved"},
{"POLYGON", false, "unreserved"},
{"PRECEDES", false, "unreserved"},
{"PRECEDING", false, "unreserved"},
{"PREPARE", false, "unreserved"},
Expand Down Expand Up @@ -647,6 +654,7 @@ var Keywords = []KeywordsType{
{"SQL_TSI_SECOND", false, "unreserved"},
{"SQL_TSI_WEEK", false, "unreserved"},
{"SQL_TSI_YEAR", false, "unreserved"},
{"SRID", false, "unreserved"},
{"STACKED", false, "unreserved"},
{"START", false, "unreserved"},
{"STARTS", false, "unreserved"},
Expand Down
4 changes: 2 additions & 2 deletions parser/keywords_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ func TestKeywords(t *testing.T) {
}

func TestKeywordsLength(t *testing.T) {
if !reflect.DeepEqual(751, len(parser.Keywords)) {
t.Fatalf("got %v, want %v", len(parser.Keywords), 751)
if !reflect.DeepEqual(759, len(parser.Keywords)) {
t.Fatalf("got %v, want %v", len(parser.Keywords), 759)
}

reservedNr := 0
Expand Down
18 changes: 14 additions & 4 deletions parser/misc.go
Original file line number Diff line number Diff line change
Expand Up @@ -450,6 +450,9 @@ var tokenMap = map[string]int{
"GC_TTL": gcTTL,
"GENERAL": general,
"GENERATED": generated,
"GEOMCOLLECTION": geometryCollectionType,
"GEOMETRY": geometryType,
"GEOMETRYCOLLECTION": geometryCollectionType,
"GET": get,
"GET_FORMAT": getFormat,
"GLOBAL": global,
Expand Down Expand Up @@ -557,6 +560,7 @@ var tokenMap = map[string]int{
"LITE": lite,
"LINEAR": linear,
"LINES": lines,
"LINESTRING": linestringType,
"LIST": list,
"LOAD": load,
"LOCAL": local,
Expand Down Expand Up @@ -608,6 +612,9 @@ var tokenMap = map[string]int{
"MODIFY": modify,
"MONITOR": monitor,
"MONTH": month,
"MULTILINESTRING": multilinestringType,
"MULTIPOINT": multipointType,
"MULTIPOLYGON": multipolygonType,
"MUTEX": mutex,
"NAMES": names,
"NATIONAL": national,
Expand Down Expand Up @@ -688,6 +695,7 @@ var tokenMap = map[string]int{
"POINT": point,
"POLICIES": policies,
"POLICY": policy,
"POLYGON": polygonType,
"POSITION": position,
"PRE_SPLIT_REGIONS": preSplitRegions,
"PRECEDES": precedes,
Expand Down Expand Up @@ -847,6 +855,7 @@ var tokenMap = map[string]int{
"SQLEXCEPTION": sqlexception,
"SQLSTATE": sqlstate,
"SQLWARNING": sqlwarning,
"SRID": srid,
"SSL": ssl,
"STACKED": stacked,
"STALENESS": staleness,
Expand Down Expand Up @@ -1087,10 +1096,11 @@ 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",
"SCHEMA": "DATABASE",
"SCHEMAS": "DATABASES",
"DEC": "DECIMAL",
"GEOMCOLLECTION": "GEOMETRYCOLLECTION",
"SUBSTR": "SUBSTRING",
}

// hintedTokens is a set of tokens which recognizes a hint.
Expand Down
2 changes: 1 addition & 1 deletion parser/parse_alter.go
Original file line number Diff line number Diff line change
Expand Up @@ -732,7 +732,7 @@ func (r *rdParser) parseAlterTableSpec() *ast.AlterTableSpec {
func (r *rdParser) parseAlterTableSpecAdd() *ast.AlterTableSpec {
r.expect(add)
switch r.tok() {
case constraint, primary, unique, fulltext, foreign, check, key, index:
case constraint, primary, unique, fulltext, spatial, foreign, check, key, index:
// "ADD" ConstraintWithColumnarIndex (the Constraint alternative)
return &ast.AlterTableSpec{
Tp: ast.AlterTableAddConstraint,
Expand Down
37 changes: 36 additions & 1 deletion parser/parse_column.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,8 @@ func (r *rdParser) isColumnOptionStart() bool {
switch r.tok() {
case not, not2, null, autoIncrement, primary, key, unique, defaultKwd,
serial, on, comment, check, constraint, generated, as, references,
collate, columnFormat, storage, autoRandom, secondaryEngineAttribute:
collate, columnFormat, storage, autoRandom, secondaryEngineAttribute,
srid:
return true
}
return false
Expand Down Expand Up @@ -262,6 +263,14 @@ func (r *rdParser) parseColumnOption() interface{} {
Tp: ast.ColumnOptionSecondaryEngineAttribute,
StrValue: r.expect(stringLit).lit,
}
case srid:
// ColumnOption: "SRID" LengthNum — the spatial column attribute
// (MySQL 26.7 §13.1.20.10); postdates the goyacc grammar.
r.advance()
return &ast.ColumnOption{
Tp: ast.ColumnOptionSrid,
UintValue: r.parseLengthNum(),
}
}
r.syntaxError()
return nil
Expand Down Expand Up @@ -748,6 +757,32 @@ func (r *rdParser) parseConstraintElem() *ast.Constraint {
c.Option = &ast.IndexOption{}
}
return c
case spatial:
// "SPATIAL" KeyOrIndexOpt IndexName '(' IndexPartSpecificationList ')' IndexOptionList
// — the MySQL 26.7 §15.1.20 table-constraint form, which postdates
// the goyacc grammar (it only had CREATE SPATIAL INDEX). Mirrors
// the FULLTEXT alternative.
r.advance()
if r.tok() == key || r.tok() == index {
r.advance()
}
name := r.parseIndexName()
r.expect(int('('))
keys := r.parseIndexPartSpecificationList()
r.expect(int(')'))
option := r.parseIndexOptionList()
c := &ast.Constraint{
Tp: ast.ConstraintSpatial,
Keys: keys,
Name: name.String,
IsEmptyIndex: name.Empty,
}
if option != nil {
c.Option = option
} else {
c.Option = &ast.IndexOption{}
}
return c
case key, index:
// KeyOrIndex IfNotExists IndexNameAndTypeOpt '(' IndexPartSpecificationList ')' IndexOptionList
r.advance()
Expand Down
2 changes: 1 addition & 1 deletion parser/parse_create_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -331,7 +331,7 @@ func (r *rdParser) parseTableElementList() []interface{} {
// ConstraintWithColumnarIndex.
func (r *rdParser) parseTableElement() interface{} {
switch r.tok() {
case constraint, primary, unique, fulltext, foreign, check, key, index:
case constraint, primary, unique, fulltext, spatial, foreign, check, key, index:
// Constraint: ConstraintKeywordOpt ConstraintElem
return r.parseConstraint()
case vectorType, columnar:
Expand Down
11 changes: 11 additions & 0 deletions parser/parse_func.go
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,17 @@ func (r *rdParser) parseCastType() *types.FieldType {
tp.SetCharset(charset.CharsetBin)
tp.SetCollate(charset.CollationBin)
return tp
case geometryType, point, linestringType, polygonType, multipointType,
multilinestringType, multipolygonType, geometryCollectionType:
// CastType: the spatial types — castable since MySQL 8.0.24
// (MySQL 26.7 §14.10); postdates the goyacc grammar.
b := spatialTypeByte(r.tok())
r.advance()
tp := types.NewFieldType(b)
tp.SetCharset(charset.CharsetBin)
tp.SetCollate(charset.CollationBin)
tp.AddFlag(mysql.BinaryFlag)
return tp
case vectorType:
// "VECTOR" OptVectorElementType OptFieldLen
r.advance()
Expand Down
61 changes: 58 additions & 3 deletions parser/parse_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,20 @@ func (r *rdParser) parseStringOrDateAndTimeType() *types.FieldType {
tp.SetCharset(charset.CharsetBin)
tp.SetCollate(charset.CollationBin)
return tp
case geometryType, point, linestringType, polygonType, multipointType,
multilinestringType, multipolygonType, geometryCollectionType:
// SpatialType: "GEOMETRY" | "POINT" | "LINESTRING" | "POLYGON"
// | "MULTIPOINT" | "MULTILINESTRING" | "MULTIPOLYGON"
// | "GEOMETRYCOLLECTION" — MySQL 26.7 §13.4.1. The spatial types
// postdate the goyacc grammar; GEOMCOLLECTION lexes as its
// GEOMETRYCOLLECTION synonym. Spatial values are stored in a
// binary format, like JSON.
b := spatialTypeByte(r.tok())
r.advance()
tp := types.NewFieldType(b)
tp.SetCharset(charset.CharsetBin)
tp.SetCollate(charset.CollationBin)
return tp
case long:
return r.parseLongType()
case vectorType:
Expand Down Expand Up @@ -390,29 +404,70 @@ func (r *rdParser) parseStringOrDateAndTimeType() *types.FieldType {
return nil
}

// spatialTypeByte maps a spatial type keyword token to its type byte.
func spatialTypeByte(tok int) byte {
switch tok {
case geometryType:
return mysql.TypeGeometry
case point:
return mysql.TypePoint
case linestringType:
return mysql.TypeLineString
case polygonType:
return mysql.TypePolygon
case multipointType:
return mysql.TypeMultiPoint
case multilinestringType:
return mysql.TypeMultiLineString
case multipolygonType:
return mysql.TypeMultiPolygon
case geometryCollectionType:
return mysql.TypeGeometryCollection
}
return 0
}

// parseCharTail finishes `Char/NChar FieldLen OptBinary` and
// `Char/NChar OptBinary` after the type keyword(s) have been consumed.
// Deviation from the goyacc grammar: the attribute is parsed as
// OptCharsetWithOptBinary, whose extra leading tokens admit the MySQL
// 26.7 §13.3.1 ASCII/UNICODE/BYTE attributes (`CHAR BYTE` is the
// documented alias for BINARY); goyacc allowed them only on TEXT,
// ENUM, SET, and LONG.
func (r *rdParser) parseCharTail() *types.FieldType {
tp := types.NewFieldType(mysql.TypeString)
if r.tok() == int('(') {
tp.SetFlen(r.parseFieldLen())
}
opt := r.parseOptBinary()
opt := r.parseOptCharsetWithOptBinary()
tp.SetCharset(opt.Charset)
if opt.Charset == charset.CharsetBin {
// The binary charset (spelled BYTE or CHARACTER SET binary) makes
// the column BINARY(n); normalize like the TextType action so the
// result matches what parsing BINARY(n) produces.
tp.AddFlag(mysql.BinaryFlag)
tp.SetCollate(charset.CollationBin)
}
if opt.IsBinary {
tp.AddFlag(mysql.BinaryFlag)
}
return tp
}

// parseVarcharTail finishes `Varchar/NVarchar FieldLen OptBinary` after
// the type keyword(s) have been consumed.
// the type keyword(s) have been consumed, with the same
// OptCharsetWithOptBinary deviation and binary-charset normalization as
// parseCharTail.
func (r *rdParser) parseVarcharTail() *types.FieldType {
flen := r.parseFieldLen()
opt := r.parseOptBinary()
opt := r.parseOptCharsetWithOptBinary()
tp := types.NewFieldType(mysql.TypeVarchar)
tp.SetFlen(flen)
tp.SetCharset(opt.Charset)
if opt.Charset == charset.CharsetBin {
tp.AddFlag(mysql.BinaryFlag)
tp.SetCollate(charset.CollationBin)
}
if opt.IsBinary {
tp.AddFlag(mysql.BinaryFlag)
}
Expand Down
12 changes: 12 additions & 0 deletions parser/testdata/parser/compat_types/input.sql
Original file line number Diff line number Diff line change
Expand Up @@ -29,3 +29,15 @@ CREATE TABLE t(id INT PRIMARY KEY, c1 LONG)
CREATE TABLE t(id INT PRIMARY KEY, c1 MIDDLEINT)
-- case
CREATE TABLE t(id INT PRIMARY KEY, c1 NUMERIC)
-- case
CREATE TABLE t(id INT PRIMARY KEY, c1 CHAR(5) BYTE)
-- case
CREATE TABLE t(id INT PRIMARY KEY, c1 CHAR(5) ASCII)
-- case
CREATE TABLE t(id INT PRIMARY KEY, c1 CHAR(5) UNICODE)
-- case
CREATE TABLE t(id INT PRIMARY KEY, c1 VARCHAR(10) BYTE)
-- case
CREATE TABLE t(id INT PRIMARY KEY, c1 VARCHAR(10) ASCII)
-- case
CREATE TABLE t(id INT PRIMARY KEY, c1 VARCHAR(10) UNICODE)
Loading
Loading