mirror of
https://gitee.com/dromara/mayfly-go
synced 2026-05-18 08:55:19 +08:00
refactor: 移除antlr4减小包体积&ai助手优化
This commit is contained in:
767
server/internal/db/dbm/sqlparser/mysql/parser.go
Normal file
767
server/internal/db/dbm/sqlparser/mysql/parser.go
Normal file
@@ -0,0 +1,767 @@
|
||||
package mysql
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"mayfly-go/internal/db/dbm/sqlparser/base"
|
||||
"mayfly-go/internal/db/dbm/sqlparser/sqlstmt"
|
||||
"mayfly-go/internal/db/dbm/sqlparser/tokenizer"
|
||||
)
|
||||
|
||||
// Parser MySQL 方言 SQL 解析器
|
||||
type Parser struct {
|
||||
*base.Lexer
|
||||
}
|
||||
|
||||
// NewParser 创建 MySQL 解析器
|
||||
func NewParser(sql string) *Parser {
|
||||
return &Parser{
|
||||
Lexer: base.NewLexer(sql, tokenizer.DialectConfig{
|
||||
BacktickAsIdentifier: true,
|
||||
HashLineComment: true,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// Parse 解析单条 SQL 语句
|
||||
func (p *Parser) Parse() (sqlstmt.Stmt, error) {
|
||||
p.SkipSemicolons()
|
||||
if p.Current().IsEOF() {
|
||||
return nil, nil
|
||||
}
|
||||
stmt := p.parseStatement()
|
||||
return stmt, nil
|
||||
}
|
||||
|
||||
func (p *Parser) parseStatement() sqlstmt.Stmt {
|
||||
tok := p.Current()
|
||||
switch {
|
||||
case tok.IsKeyword("SELECT") || tok.Value == "(":
|
||||
return p.parseSelect()
|
||||
case tok.IsKeyword("INSERT"):
|
||||
return p.parseInsert()
|
||||
case tok.IsKeyword("UPDATE"):
|
||||
return p.parseUpdate()
|
||||
case tok.IsKeyword("DELETE"):
|
||||
return p.parseDelete()
|
||||
case tok.IsKeyword("CREATE"):
|
||||
return p.parseCreate()
|
||||
case tok.IsKeyword("DROP"):
|
||||
return p.parseDrop()
|
||||
case tok.IsKeyword("ALTER"):
|
||||
return p.parseAlter()
|
||||
case tok.IsKeyword("WITH"):
|
||||
return p.parseWith()
|
||||
case tok.IsKeyword("SHOW"):
|
||||
return p.parseShow()
|
||||
case tok.IsKeyword("TRUNCATE"):
|
||||
return p.parseGenericDdl()
|
||||
default:
|
||||
return p.parseGenericStmt()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- SELECT 解析 ----------
|
||||
|
||||
func (p *Parser) parseSelect() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
var selectStmt *sqlstmt.SelectStmt
|
||||
|
||||
if p.Current().Value == "(" {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("SELECT") {
|
||||
selectStmt = p.parseSelectBody()
|
||||
} else {
|
||||
p.SkipToNextStatement()
|
||||
return &sqlstmt.OtherStmt{Base: sqlstmt.Base{Text: p.TextFrom(start)}}
|
||||
}
|
||||
p.ExpectValue(")")
|
||||
} else if p.Current().IsKeyword("SELECT") {
|
||||
selectStmt = p.parseSelectBody()
|
||||
} else {
|
||||
return &sqlstmt.OtherStmt{Base: sqlstmt.Base{Text: p.TextFrom(start)}}
|
||||
}
|
||||
|
||||
if selectStmt == nil {
|
||||
return &sqlstmt.OtherStmt{Base: sqlstmt.Base{Text: p.TextFrom(start)}}
|
||||
}
|
||||
|
||||
// UNION
|
||||
for p.Current().IsKeyword("UNION") {
|
||||
p.Consume()
|
||||
isAll := false
|
||||
if p.Current().IsKeyword("ALL") {
|
||||
p.Consume()
|
||||
isAll = true
|
||||
} else if p.Current().IsKeyword("DISTINCT") {
|
||||
p.Consume()
|
||||
}
|
||||
unionStart := p.Pos
|
||||
var unionSelect *sqlstmt.SelectStmt
|
||||
if p.Current().Value == "(" {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("SELECT") {
|
||||
unionSelect = p.parseSelectBody()
|
||||
}
|
||||
p.ExpectValue(")")
|
||||
} else if p.Current().IsKeyword("SELECT") {
|
||||
unionSelect = p.parseSelectBody()
|
||||
}
|
||||
if unionSelect != nil {
|
||||
// 如果 unionSelect 有 LIMIT 或 ORDER BY,移动到外层 selectStmt(UNION 的 LIMIT/ORDER BY 属于整个语句)
|
||||
if unionSelect.Limit != nil {
|
||||
selectStmt.Limit = unionSelect.Limit
|
||||
unionSelect.Limit = nil
|
||||
}
|
||||
if len(unionSelect.OrderBy) > 0 {
|
||||
selectStmt.OrderBy = unionSelect.OrderBy
|
||||
unionSelect.OrderBy = nil
|
||||
}
|
||||
unionSelect.Text = p.TextFrom(unionStart)
|
||||
}
|
||||
selectStmt.Unions = append(selectStmt.Unions, sqlstmt.UnionClause{
|
||||
Select: unionSelect,
|
||||
All: isAll,
|
||||
Text: p.TextFrom(unionStart),
|
||||
})
|
||||
}
|
||||
|
||||
// UNION 之后可能有 ORDER BY
|
||||
if p.Current().IsKeyword("ORDER") {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("BY") {
|
||||
p.Consume()
|
||||
}
|
||||
p.parseOrderBy(selectStmt)
|
||||
}
|
||||
|
||||
// LIMIT (UNION 之后的 LIMIT 覆盖子查询内部的 LIMIT)
|
||||
// 注意:只在确实解析到新 LIMIT 时才覆盖,避免 nil 覆盖有效值
|
||||
if p.Current().IsKeyword("LIMIT") {
|
||||
if limit := p.parseLimit(); limit != nil {
|
||||
selectStmt.Limit = limit
|
||||
}
|
||||
}
|
||||
|
||||
selectStmt.Text = p.TextFrom(start)
|
||||
return selectStmt
|
||||
}
|
||||
|
||||
func (p *Parser) parseSelectBody() *sqlstmt.SelectStmt {
|
||||
if !p.Current().IsKeyword("SELECT") {
|
||||
return nil
|
||||
}
|
||||
start := p.Pos
|
||||
p.Consume()
|
||||
|
||||
distinct := false
|
||||
if p.Current().IsKeyword("DISTINCT") {
|
||||
p.Consume()
|
||||
distinct = true
|
||||
} else if p.Current().IsKeyword("ALL") {
|
||||
p.Consume()
|
||||
}
|
||||
|
||||
items := p.parseSelectItems()
|
||||
|
||||
stmt := &sqlstmt.SelectStmt{
|
||||
Distinct: distinct,
|
||||
Items: items,
|
||||
}
|
||||
|
||||
if p.Current().IsKeyword("FROM") {
|
||||
p.Consume()
|
||||
p.parseFromClause(stmt)
|
||||
}
|
||||
|
||||
if p.Current().IsKeyword("WHERE") {
|
||||
p.Consume()
|
||||
whereStart := p.Pos
|
||||
p.SkipExpr()
|
||||
stmt.Where = &sqlstmt.Expr{Text: p.TextFromExclusive(whereStart)}
|
||||
}
|
||||
|
||||
if p.Current().IsKeyword("GROUP") {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("BY") {
|
||||
p.Consume()
|
||||
}
|
||||
p.SkipGroupByExpr()
|
||||
}
|
||||
|
||||
if p.Current().IsKeyword("HAVING") {
|
||||
p.Consume()
|
||||
p.SkipExpr()
|
||||
}
|
||||
|
||||
if p.Current().IsKeyword("ORDER") {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("BY") {
|
||||
p.Consume()
|
||||
}
|
||||
p.parseOrderBy(stmt)
|
||||
}
|
||||
|
||||
// LIMIT (子查询或简单 SELECT 的 LIMIT)
|
||||
if p.Current().IsKeyword("LIMIT") {
|
||||
stmt.Limit = p.parseLimit()
|
||||
}
|
||||
|
||||
stmt.Text = p.TextFrom(start)
|
||||
return stmt
|
||||
}
|
||||
|
||||
func (p *Parser) parseSelectItems() []sqlstmt.SelectItem {
|
||||
items := make([]sqlstmt.SelectItem, 0)
|
||||
for !p.Current().IsEOF() {
|
||||
tok := p.Current()
|
||||
if tok.Value == "," {
|
||||
p.Consume()
|
||||
continue
|
||||
}
|
||||
if p.IsSelectClauseEnd() {
|
||||
break
|
||||
}
|
||||
if tok.Value == "*" {
|
||||
p.Consume()
|
||||
items = append(items, sqlstmt.SelectItem{
|
||||
Kind: sqlstmt.SelectItemStar,
|
||||
Text: "*",
|
||||
})
|
||||
} else if tok.Type == tokenizer.TokenIdentifier && p.Peek(1).Value == "." && p.Peek(2).Value == "*" {
|
||||
tableAlias := p.Unquote(p.Consume().Value)
|
||||
p.Consume()
|
||||
p.Consume()
|
||||
items = append(items, sqlstmt.SelectItem{
|
||||
Kind: sqlstmt.SelectItemStar,
|
||||
Text: tableAlias + ".*",
|
||||
TableAlias: tableAlias,
|
||||
})
|
||||
} else {
|
||||
colStart := p.Pos
|
||||
p.skipSelectElement()
|
||||
colText := base.TrimTrailingComma(p.TextFromExclusive(colStart))
|
||||
colName, alias := p.ExtractColumnAndAlias(colText)
|
||||
kind := sqlstmt.SelectItemColumn
|
||||
if strings.Contains(colText, "(") {
|
||||
kind = sqlstmt.SelectItemExpr
|
||||
}
|
||||
items = append(items, sqlstmt.SelectItem{
|
||||
Kind: kind,
|
||||
Text: colText,
|
||||
Alias: alias,
|
||||
ColumnName: colName,
|
||||
})
|
||||
}
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
func (p *Parser) skipSelectElement() {
|
||||
for !p.Current().IsEOF() {
|
||||
tok := p.Current()
|
||||
if tok.Value == "," || p.IsSelectClauseEnd() {
|
||||
break
|
||||
}
|
||||
if tok.Value == "(" {
|
||||
p.SkipParentheses()
|
||||
continue
|
||||
}
|
||||
p.Consume()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- FROM / TableRef 解析 ----------
|
||||
|
||||
func (p *Parser) parseFromClause(stmt *sqlstmt.SelectStmt) {
|
||||
for !p.Current().IsEOF() {
|
||||
if p.IsFromClauseEnd() {
|
||||
break
|
||||
}
|
||||
if p.Current().Value == "," {
|
||||
p.Consume()
|
||||
continue
|
||||
}
|
||||
if p.Current().IsKeyword("JOIN") || p.IsJoinStart() {
|
||||
join := p.parseJoinClause()
|
||||
if join != nil {
|
||||
stmt.Joins = append(stmt.Joins, *join)
|
||||
}
|
||||
continue
|
||||
}
|
||||
tableRef := p.parseTableRef()
|
||||
if tableRef.Name != "" {
|
||||
stmt.From = append(stmt.From, tableRef)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseTableRef() sqlstmt.TableRef {
|
||||
if p.Current().Value == "(" {
|
||||
start := p.Pos
|
||||
p.SkipParentheses()
|
||||
alias := ""
|
||||
if p.Current().IsKeyword("AS") {
|
||||
p.Consume()
|
||||
}
|
||||
if p.Current().Type == tokenizer.TokenIdentifier {
|
||||
alias = p.Unquote(p.Consume().Value)
|
||||
}
|
||||
return sqlstmt.TableRef{
|
||||
Name: p.TextFrom(start),
|
||||
Alias: alias,
|
||||
}
|
||||
}
|
||||
|
||||
if p.Current().Type != tokenizer.TokenIdentifier && p.Current().Type != tokenizer.TokenString {
|
||||
return sqlstmt.TableRef{}
|
||||
}
|
||||
|
||||
ref := sqlstmt.TableRef{}
|
||||
part1 := p.Consume().Value
|
||||
|
||||
if p.Current().Value == "." {
|
||||
p.Consume()
|
||||
if p.Current().Type == tokenizer.TokenIdentifier || p.Current().Type == tokenizer.TokenString {
|
||||
part2 := p.Consume().Value
|
||||
ref.Schema = p.Unquote(part1)
|
||||
ref.Name = p.Unquote(part2)
|
||||
} else {
|
||||
ref.Name = p.Unquote(part1)
|
||||
}
|
||||
} else {
|
||||
ref.Name = p.Unquote(part1)
|
||||
}
|
||||
|
||||
if p.Current().IsKeyword("AS") {
|
||||
p.Consume()
|
||||
}
|
||||
if p.Current().Type == tokenizer.TokenIdentifier {
|
||||
ref.Alias = p.Unquote(p.Consume().Value)
|
||||
}
|
||||
|
||||
return ref
|
||||
}
|
||||
|
||||
func (p *Parser) parseJoinClause() *sqlstmt.JoinClause {
|
||||
start := p.Pos
|
||||
joinType := sqlstmt.JoinKindInner
|
||||
if p.Current().IsKeyword("LEFT") {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("OUTER") {
|
||||
p.Consume()
|
||||
}
|
||||
joinType = sqlstmt.JoinKindLeft
|
||||
} else if p.Current().IsKeyword("RIGHT") {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("OUTER") {
|
||||
p.Consume()
|
||||
}
|
||||
joinType = sqlstmt.JoinKindRight
|
||||
} else if p.Current().IsKeyword("FULL") {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("OUTER") {
|
||||
p.Consume()
|
||||
}
|
||||
joinType = sqlstmt.JoinKindFull
|
||||
} else if p.Current().IsKeyword("CROSS") {
|
||||
p.Consume()
|
||||
joinType = sqlstmt.JoinKindCross
|
||||
} else if p.Current().IsKeyword("NATURAL") {
|
||||
p.Consume()
|
||||
joinType = sqlstmt.JoinKindNatural
|
||||
} else if p.Current().IsKeyword("INNER") {
|
||||
p.Consume()
|
||||
}
|
||||
|
||||
if !p.Current().IsKeyword("JOIN") {
|
||||
p.Pos = start
|
||||
return nil
|
||||
}
|
||||
p.Consume()
|
||||
|
||||
tableRef := p.parseTableRef()
|
||||
if tableRef.Name == "" {
|
||||
p.Pos = start
|
||||
return nil
|
||||
}
|
||||
|
||||
var onExpr *sqlstmt.Expr
|
||||
if p.Current().IsKeyword("ON") {
|
||||
p.Consume()
|
||||
onStart := p.Pos
|
||||
p.SkipExpr()
|
||||
onExpr = &sqlstmt.Expr{Text: p.TextFromExclusive(onStart)}
|
||||
} else if p.Current().IsKeyword("USING") {
|
||||
p.Consume()
|
||||
if p.Current().Value == "(" {
|
||||
p.SkipParentheses()
|
||||
}
|
||||
}
|
||||
|
||||
return &sqlstmt.JoinClause{
|
||||
Kind: joinType,
|
||||
Table: tableRef,
|
||||
On: onExpr,
|
||||
Text: p.TextFrom(start),
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseOrderBy(stmt *sqlstmt.SelectStmt) {
|
||||
for !p.Current().IsEOF() {
|
||||
if p.Current().Value == "," {
|
||||
p.Consume()
|
||||
continue
|
||||
}
|
||||
if p.IsExprEnd() || p.Current().Value == ";" {
|
||||
break
|
||||
}
|
||||
itemStart := p.Pos
|
||||
p.skipOrderByItem()
|
||||
itemText := base.TrimTrailingComma(p.TextFromExclusive(itemStart))
|
||||
desc := false
|
||||
upper := strings.ToUpper(itemText)
|
||||
if strings.HasSuffix(upper, " DESC") {
|
||||
desc = true
|
||||
itemText = strings.TrimSpace(itemText[:len(itemText)-5])
|
||||
} else if strings.HasSuffix(upper, " ASC") {
|
||||
itemText = strings.TrimSpace(itemText[:len(itemText)-4])
|
||||
}
|
||||
stmt.OrderBy = append(stmt.OrderBy, sqlstmt.OrderByItem{
|
||||
Text: itemText,
|
||||
Desc: desc,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) skipOrderByItem() {
|
||||
for !p.Current().IsEOF() {
|
||||
if p.Current().Value == "," {
|
||||
break
|
||||
}
|
||||
if p.IsExprEnd() || p.Current().Value == ";" {
|
||||
break
|
||||
}
|
||||
if p.Current().Value == "(" {
|
||||
p.SkipParentheses()
|
||||
continue
|
||||
}
|
||||
p.Consume()
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- LIMIT 解析 ----------
|
||||
|
||||
func (p *Parser) parseLimit() *sqlstmt.Limit {
|
||||
if !p.Current().IsKeyword("LIMIT") {
|
||||
return nil
|
||||
}
|
||||
start := p.Pos
|
||||
p.Consume()
|
||||
rowCount := 0
|
||||
offset := 0
|
||||
if p.Current().Type == tokenizer.TokenNumber {
|
||||
rowCount = p.ParseInt(p.Consume().Value)
|
||||
}
|
||||
if p.Current().Value == "," {
|
||||
p.Consume()
|
||||
offset = rowCount
|
||||
if p.Current().Type == tokenizer.TokenNumber {
|
||||
rowCount = p.ParseInt(p.Consume().Value)
|
||||
}
|
||||
}
|
||||
if p.Current().IsKeyword("OFFSET") {
|
||||
p.Consume()
|
||||
if p.Current().Type == tokenizer.TokenNumber {
|
||||
offset = p.ParseInt(p.Consume().Value)
|
||||
}
|
||||
}
|
||||
return &sqlstmt.Limit{
|
||||
Text: p.TextFrom(start),
|
||||
Count: rowCount,
|
||||
Offset: offset,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- INSERT 解析 ----------
|
||||
|
||||
func (p *Parser) parseInsert() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("INTO") {
|
||||
p.Consume()
|
||||
}
|
||||
|
||||
tableRef := p.parseTableRef()
|
||||
columns := []string{}
|
||||
|
||||
if p.Current().Value == "(" {
|
||||
p.Consume()
|
||||
for !p.Current().IsEOF() && p.Current().Value != ")" {
|
||||
if p.Current().Value == "," {
|
||||
p.Consume()
|
||||
continue
|
||||
}
|
||||
if p.Current().Type == tokenizer.TokenIdentifier {
|
||||
columns = append(columns, p.Unquote(p.Consume().Value))
|
||||
} else {
|
||||
p.Consume()
|
||||
}
|
||||
}
|
||||
if p.Current().Value == ")" {
|
||||
p.Consume()
|
||||
}
|
||||
}
|
||||
|
||||
valuesStart := p.Pos
|
||||
if p.Current().IsKeyword("VALUES") {
|
||||
p.Consume()
|
||||
for !p.Current().IsEOF() && !p.IsExprEnd() {
|
||||
if p.Current().Value == "(" {
|
||||
p.SkipParentheses()
|
||||
continue
|
||||
}
|
||||
p.Consume()
|
||||
}
|
||||
} else if p.Current().IsKeyword("SELECT") {
|
||||
p.parseSelect()
|
||||
}
|
||||
|
||||
return &sqlstmt.InsertStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
Table: tableRef,
|
||||
Columns: columns,
|
||||
Values: p.TextFrom(valuesStart),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- UPDATE 解析 ----------
|
||||
|
||||
func (p *Parser) parseUpdate() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.Consume()
|
||||
|
||||
tables := make([]sqlstmt.TableRef, 0)
|
||||
for !p.Current().IsEOF() {
|
||||
if p.Current().IsKeyword("SET") || p.Current().IsKeyword("WHERE") || p.Current().Value == ";" {
|
||||
break
|
||||
}
|
||||
if p.Current().Value == "," {
|
||||
p.Consume()
|
||||
continue
|
||||
}
|
||||
if p.Current().IsKeyword("JOIN") || p.IsJoinStart() {
|
||||
p.parseJoinClause()
|
||||
continue
|
||||
}
|
||||
tableRef := p.parseTableRef()
|
||||
if tableRef.Name != "" {
|
||||
tables = append(tables, tableRef)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
assignments := make([]sqlstmt.Assignment, 0)
|
||||
if p.Current().IsKeyword("SET") {
|
||||
p.Consume()
|
||||
for !p.Current().IsEOF() {
|
||||
if p.Current().IsKeyword("WHERE") || p.Current().Value == ";" {
|
||||
break
|
||||
}
|
||||
assign := p.parseAssignment()
|
||||
if assign != nil {
|
||||
assignments = append(assignments, *assign)
|
||||
}
|
||||
if p.Current().Value == "," {
|
||||
p.Consume()
|
||||
continue
|
||||
}
|
||||
if p.Current().IsKeyword("WHERE") || p.Current().Value == ";" {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var where *sqlstmt.Expr
|
||||
if p.Current().IsKeyword("WHERE") {
|
||||
p.Consume()
|
||||
whereStart := p.Pos
|
||||
p.SkipExpr()
|
||||
where = &sqlstmt.Expr{Text: p.TextFromExclusive(whereStart)}
|
||||
}
|
||||
|
||||
// MySQL UPDATE 支持 ORDER BY, LIMIT
|
||||
if p.Current().IsKeyword("ORDER") {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("BY") {
|
||||
p.Consume()
|
||||
}
|
||||
p.SkipOrderByExpr()
|
||||
}
|
||||
if p.Current().IsKeyword("LIMIT") {
|
||||
p.parseLimit()
|
||||
}
|
||||
|
||||
return &sqlstmt.UpdateStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
Tables: tables,
|
||||
Set: assignments,
|
||||
Where: where,
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseAssignment() *sqlstmt.Assignment {
|
||||
start := p.Pos
|
||||
colText := ""
|
||||
for !p.Current().IsEOF() && p.Current().Value != "=" && p.Current().Value != "," &&
|
||||
!p.Current().IsKeyword("WHERE") && p.Current().Value != ";" {
|
||||
colText += p.Consume().Value
|
||||
}
|
||||
if p.Current().Value != "=" {
|
||||
p.Pos = start
|
||||
return nil
|
||||
}
|
||||
p.Consume()
|
||||
|
||||
valStart := p.Pos
|
||||
for !p.Current().IsEOF() && p.Current().Value != "," &&
|
||||
!p.Current().IsKeyword("WHERE") && p.Current().Value != ";" {
|
||||
if p.Current().Value == "(" {
|
||||
p.SkipParentheses()
|
||||
continue
|
||||
}
|
||||
p.Consume()
|
||||
}
|
||||
|
||||
return &sqlstmt.Assignment{
|
||||
Column: p.Unquote(strings.TrimSpace(colText)),
|
||||
Value: &sqlstmt.Expr{Text: p.TextFromExclusive(valStart)},
|
||||
Text: p.TextFrom(start),
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- DELETE 解析 ----------
|
||||
|
||||
func (p *Parser) parseDelete() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("FROM") {
|
||||
p.Consume()
|
||||
}
|
||||
|
||||
tables := make([]sqlstmt.TableRef, 0)
|
||||
for !p.Current().IsEOF() {
|
||||
if p.Current().IsKeyword("WHERE") || p.Current().IsKeyword("USING") ||
|
||||
p.Current().IsKeyword("ORDER") || p.Current().IsKeyword("LIMIT") ||
|
||||
p.Current().Value == ";" {
|
||||
break
|
||||
}
|
||||
if p.Current().Value == "," {
|
||||
p.Consume()
|
||||
continue
|
||||
}
|
||||
tableRef := p.parseTableRef()
|
||||
if tableRef.Name != "" {
|
||||
tables = append(tables, tableRef)
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
var where *sqlstmt.Expr
|
||||
if p.Current().IsKeyword("WHERE") {
|
||||
p.Consume()
|
||||
whereStart := p.Pos
|
||||
p.SkipExpr()
|
||||
where = &sqlstmt.Expr{Text: p.TextFromExclusive(whereStart)}
|
||||
}
|
||||
|
||||
// MySQL DELETE 支持 ORDER BY, LIMIT
|
||||
if p.Current().IsKeyword("ORDER") {
|
||||
p.Consume()
|
||||
if p.Current().IsKeyword("BY") {
|
||||
p.Consume()
|
||||
}
|
||||
p.SkipOrderByExpr()
|
||||
}
|
||||
if p.Current().IsKeyword("LIMIT") {
|
||||
p.parseLimit()
|
||||
}
|
||||
|
||||
return &sqlstmt.DeleteStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
Tables: tables,
|
||||
Where: where,
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- DDL 解析 ----------
|
||||
|
||||
func (p *Parser) parseCreate() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.SkipToNextStatement()
|
||||
return &sqlstmt.DdlStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
DdlKind: "CREATE",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseDrop() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.SkipToNextStatement()
|
||||
return &sqlstmt.DdlStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
DdlKind: "DROP",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseAlter() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.SkipToNextStatement()
|
||||
return &sqlstmt.DdlStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
DdlKind: "ALTER",
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Parser) parseGenericDdl() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.SkipToNextStatement()
|
||||
return &sqlstmt.DdlStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
DdlKind: "DDL",
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- SHOW 解析(MySQL 特有)----------
|
||||
|
||||
func (p *Parser) parseShow() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.SkipToNextStatement()
|
||||
return &sqlstmt.SelectStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- WITH 解析 ----------
|
||||
|
||||
func (p *Parser) parseWith() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.SkipToNextStatement()
|
||||
return &sqlstmt.WithStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 通用语句解析 ----------
|
||||
|
||||
func (p *Parser) parseGenericStmt() sqlstmt.Stmt {
|
||||
start := p.Pos
|
||||
p.SkipToNextStatement()
|
||||
return &sqlstmt.OtherStmt{
|
||||
Base: sqlstmt.Base{Text: p.TextFrom(start)},
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user