|
|
@@ -0,0 +1,1168 @@
|
|
|
+// Package vrtxsql — TsDB2 的 database/sql 驱动。
|
|
|
+//
|
|
|
+// 让任意基于 database/sql 的工具/ORM (xorm、sqlx 以及自研 ORM) 直接对接
|
|
|
+// VRTX 引擎: 标准 SQL 文本 → 引擎查询体系 (core.Query/Condition/Table)。
|
|
|
+//
|
|
|
+// 解析器是刻意收窄的【ORM 子集翻译器】, 不是通用 SQL 引擎:
|
|
|
+//
|
|
|
+// SELECT cols|* FROM t [WHERE ...] [ORDER BY c [ASC|DESC], ...] [LIMIT n] [OFFSET n]
|
|
|
+// SELECT COUNT(*)|COUNT/SUM/MIN/MAX/AVG(col) FROM t [WHERE ...] (无 GROUP BY)
|
|
|
+// INSERT INTO t [(cols)] VALUES (...),(...)
|
|
|
+// UPDATE t SET c=v,... [WHERE ...]
|
|
|
+// DELETE FROM t [WHERE ...]
|
|
|
+// CREATE TABLE [IF NOT EXISTS] t (c TYPE[(arg)] [NULL|NOT NULL] [UNIQUE] ...,
|
|
|
+// [PRIMARY KEY (c)], [KEY k (c)], [UNIQUE KEY k (c)])
|
|
|
+// DROP TABLE [IF EXISTS] t
|
|
|
+//
|
|
|
+// WHERE 支持: = != <> < <= > >= AND OR NOT (..) IN (..) IS [NOT] NULL LIKE。
|
|
|
+// 不支持: JOIN/子查询/GROUP BY/HAVING/函数表达式/DEFAULT 非 NULL 值 —— 遇到
|
|
|
+// 返回明确错误而不是静默做错。
|
|
|
+package vrtxsql
|
|
|
+
|
|
|
+import (
|
|
|
+ "fmt"
|
|
|
+ "strconv"
|
|
|
+ "strings"
|
|
|
+)
|
|
|
+
|
|
|
+// =============================================================================
|
|
|
+// AST
|
|
|
+// =============================================================================
|
|
|
+
|
|
|
+type stmtKind int
|
|
|
+
|
|
|
+const (
|
|
|
+ kSelect stmtKind = iota + 1
|
|
|
+ kInsert
|
|
|
+ kUpdate
|
|
|
+ kDelete
|
|
|
+ kCreate
|
|
|
+ kCreateIndex
|
|
|
+ kDrop
|
|
|
+)
|
|
|
+
|
|
|
+type statement struct {
|
|
|
+ kind stmtKind
|
|
|
+ sel *selectStmt
|
|
|
+ ins *insertStmt
|
|
|
+ upd *updateStmt
|
|
|
+ del *deleteStmt
|
|
|
+ crt *createStmt
|
|
|
+ cidx *createIndexStmt
|
|
|
+ drop *dropStmt
|
|
|
+}
|
|
|
+
|
|
|
+func (s *statement) argCount() int {
|
|
|
+ n := 0
|
|
|
+ switch s.kind {
|
|
|
+ case kSelect:
|
|
|
+ n = exprMaxArg(s.sel.where)
|
|
|
+ case kInsert:
|
|
|
+ for _, row := range s.ins.rows {
|
|
|
+ for _, v := range row {
|
|
|
+ if a, ok := v.(argValue); ok && a.idx+1 > n {
|
|
|
+ n = a.idx + 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case kUpdate:
|
|
|
+ for _, set := range s.upd.sets {
|
|
|
+ if a, ok := set.val.(argValue); ok && a.idx+1 > n {
|
|
|
+ n = a.idx + 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ n = max2(n, exprMaxArg(s.upd.where))
|
|
|
+ case kDelete:
|
|
|
+ n = exprMaxArg(s.del.where)
|
|
|
+ }
|
|
|
+ return n
|
|
|
+}
|
|
|
+
|
|
|
+func exprMaxArg(e expr) int {
|
|
|
+ if e == nil {
|
|
|
+ return 0
|
|
|
+ }
|
|
|
+ m := 0
|
|
|
+ switch t := e.(type) {
|
|
|
+ case *andExpr:
|
|
|
+ m = max2(exprMaxArg(t.l), exprMaxArg(t.r))
|
|
|
+ case *orExpr:
|
|
|
+ m = max2(exprMaxArg(t.l), exprMaxArg(t.r))
|
|
|
+ case *notExpr:
|
|
|
+ m = exprMaxArg(t.e)
|
|
|
+ case *cmpExpr:
|
|
|
+ if a, ok := t.val.(argValue); ok {
|
|
|
+ m = a.idx + 1
|
|
|
+ }
|
|
|
+ case *inExpr:
|
|
|
+ for _, v := range t.vals {
|
|
|
+ if a, ok := v.(argValue); ok && a.idx+1 > m {
|
|
|
+ m = a.idx + 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ case *likeExpr:
|
|
|
+ if a, ok := t.pattern.(argValue); ok {
|
|
|
+ m = a.idx + 1
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return m
|
|
|
+}
|
|
|
+
|
|
|
+func max2(a, b int) int {
|
|
|
+ if a > b {
|
|
|
+ return a
|
|
|
+ }
|
|
|
+ return b
|
|
|
+}
|
|
|
+
|
|
|
+type selectStmt struct {
|
|
|
+ table string
|
|
|
+ columns []string // 空 = *
|
|
|
+ agg *aggSpec // 非空 = 单行聚合
|
|
|
+ where expr
|
|
|
+ orderBy []orderSpec
|
|
|
+ limit *int64
|
|
|
+ offset *int64
|
|
|
+}
|
|
|
+
|
|
|
+type aggSpec struct {
|
|
|
+ fn string // COUNT / SUM / MIN / MAX / AVG
|
|
|
+ col string // COUNT(*) 时为空
|
|
|
+}
|
|
|
+
|
|
|
+type orderSpec struct {
|
|
|
+ col string
|
|
|
+ desc bool
|
|
|
+}
|
|
|
+
|
|
|
+type insertStmt struct {
|
|
|
+ table string
|
|
|
+ columns []string // 空 = 按建表列序
|
|
|
+ rows [][]value
|
|
|
+}
|
|
|
+
|
|
|
+type updateStmt struct {
|
|
|
+ table string
|
|
|
+ sets []setClause
|
|
|
+ where expr
|
|
|
+}
|
|
|
+
|
|
|
+type setClause struct {
|
|
|
+ col string
|
|
|
+ val value
|
|
|
+}
|
|
|
+
|
|
|
+type deleteStmt struct {
|
|
|
+ table string
|
|
|
+ where expr
|
|
|
+}
|
|
|
+
|
|
|
+type createStmt struct {
|
|
|
+ table string
|
|
|
+ ifNotExists bool
|
|
|
+ columns []colDef
|
|
|
+ pkCol string // PRIMARY KEY (c) 或列级 PRIMARY KEY
|
|
|
+ indexes []inlineIndex
|
|
|
+}
|
|
|
+
|
|
|
+type colDef struct {
|
|
|
+ name string
|
|
|
+ typeName string
|
|
|
+ arg int // VARCHAR(n)/DECIMAL(p,s→取s) 的参数, 无则 -1
|
|
|
+ notNull bool
|
|
|
+ unique bool
|
|
|
+ pk bool // 列级 PRIMARY KEY
|
|
|
+}
|
|
|
+
|
|
|
+type inlineIndex struct {
|
|
|
+ name string
|
|
|
+ columns []string
|
|
|
+ unique bool
|
|
|
+}
|
|
|
+
|
|
|
+type dropStmt struct {
|
|
|
+ table string
|
|
|
+ ifExists bool
|
|
|
+}
|
|
|
+
|
|
|
+// createIndexStmt CREATE [UNIQUE] INDEX name ON t (col)
|
|
|
+type createIndexStmt struct {
|
|
|
+ unique bool
|
|
|
+ name string
|
|
|
+ table string
|
|
|
+ col string
|
|
|
+}
|
|
|
+
|
|
|
+// ---- WHERE 表达式 ----
|
|
|
+
|
|
|
+type expr interface{ isExpr() }
|
|
|
+
|
|
|
+type andExpr struct{ l, r expr }
|
|
|
+type orExpr struct{ l, r expr }
|
|
|
+type notExpr struct{ e expr }
|
|
|
+
|
|
|
+type cmpExpr struct {
|
|
|
+ col string
|
|
|
+ op string // = != < <= > >=
|
|
|
+ val value
|
|
|
+}
|
|
|
+
|
|
|
+type inExpr struct {
|
|
|
+ col string
|
|
|
+ vals []value
|
|
|
+ neg bool
|
|
|
+}
|
|
|
+
|
|
|
+type isNullExpr struct {
|
|
|
+ col string
|
|
|
+ neg bool
|
|
|
+}
|
|
|
+
|
|
|
+type likeExpr struct {
|
|
|
+ col string
|
|
|
+ pattern value
|
|
|
+ neg bool
|
|
|
+}
|
|
|
+
|
|
|
+func (*andExpr) isExpr() {}
|
|
|
+func (*orExpr) isExpr() {}
|
|
|
+func (*notExpr) isExpr() {}
|
|
|
+func (*cmpExpr) isExpr() {}
|
|
|
+func (*inExpr) isExpr() {}
|
|
|
+func (*isNullExpr) isExpr() {}
|
|
|
+func (*likeExpr) isExpr() {}
|
|
|
+
|
|
|
+// ---- 值 ----
|
|
|
+
|
|
|
+type value interface{ isValue() }
|
|
|
+
|
|
|
+type argValue struct{ idx int } // ? 占位 → args[idx]
|
|
|
+type litValue struct{ raw string }
|
|
|
+type strValue struct{ s string }
|
|
|
+type nullValue struct{}
|
|
|
+
|
|
|
+func (argValue) isValue() {}
|
|
|
+func (litValue) isValue() {}
|
|
|
+func (strValue) isValue() {}
|
|
|
+func (nullValue) isValue() {}
|
|
|
+
|
|
|
+// =============================================================================
|
|
|
+// 词法分析
|
|
|
+// =============================================================================
|
|
|
+
|
|
|
+type tokenKind int
|
|
|
+
|
|
|
+const (
|
|
|
+ tkEOF tokenKind = iota + 1
|
|
|
+ tkIdent
|
|
|
+ tkNumber
|
|
|
+ tkString
|
|
|
+ tkArg // ?
|
|
|
+ tkPunct // , ( ) * = != <> < <= > >=
|
|
|
+ tkKeyword
|
|
|
+)
|
|
|
+
|
|
|
+type token struct {
|
|
|
+ kind tokenKind
|
|
|
+ text string // 原文 (标识符保留原大小写, 关键字统一大写)
|
|
|
+}
|
|
|
+
|
|
|
+var keywords = map[string]bool{
|
|
|
+ "SELECT": true, "FROM": true, "WHERE": true, "AND": true, "OR": true,
|
|
|
+ "NOT": true, "IN": true, "LIKE": true, "IS": true, "NULL": true,
|
|
|
+ "ORDER": true, "BY": true, "ASC": true, "DESC": true,
|
|
|
+ "LIMIT": true, "OFFSET": true,
|
|
|
+ "INSERT": true, "INTO": true, "VALUES": true, "VALUE": true,
|
|
|
+ "UPDATE": true, "SET": true, "DELETE": true,
|
|
|
+ "CREATE": true, "TABLE": true, "DROP": true, "IF": true, "EXISTS": true,
|
|
|
+ "PRIMARY": true, "KEY": true, "UNIQUE": true,
|
|
|
+ "INDEX": true, "ON": true,
|
|
|
+ "COUNT": true, "SUM": true, "MIN": true, "MAX": true, "AVG": true,
|
|
|
+ "AS": true,
|
|
|
+}
|
|
|
+
|
|
|
+func tokenize(sqlText string) ([]token, error) {
|
|
|
+ var toks []token
|
|
|
+ i, n := 0, len(sqlText)
|
|
|
+ for i < n {
|
|
|
+ c := sqlText[i]
|
|
|
+ switch {
|
|
|
+ case c == ' ' || c == '\t' || c == '\n' || c == '\r':
|
|
|
+ i++
|
|
|
+ case c == '-' && i+1 < n && sqlText[i+1] == '-': // 行注释
|
|
|
+ for i < n && sqlText[i] != '\n' {
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ case c == '/' && i+1 < n && sqlText[i+1] == '*': // 块注释
|
|
|
+ j := strings.Index(sqlText[i+2:], "*/")
|
|
|
+ if j < 0 {
|
|
|
+ return nil, fmt.Errorf("未闭合的块注释")
|
|
|
+ }
|
|
|
+ i += 2 + j + 2
|
|
|
+ case c == '\'' || c == '"' || c == '`':
|
|
|
+ quote := c
|
|
|
+ i++
|
|
|
+ var sb strings.Builder
|
|
|
+ closed := false
|
|
|
+ for i < n {
|
|
|
+ if sqlText[i] == quote {
|
|
|
+ // '' 转义
|
|
|
+ if i+1 < n && sqlText[i+1] == quote {
|
|
|
+ sb.WriteByte(quote)
|
|
|
+ i += 2
|
|
|
+ continue
|
|
|
+ }
|
|
|
+ i++
|
|
|
+ closed = true
|
|
|
+ break
|
|
|
+ }
|
|
|
+ sb.WriteByte(sqlText[i])
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ if !closed {
|
|
|
+ return nil, fmt.Errorf("未闭合的字符串")
|
|
|
+ }
|
|
|
+ kind := tkString
|
|
|
+ if quote == '`' {
|
|
|
+ kind = tkIdent
|
|
|
+ }
|
|
|
+ toks = append(toks, token{kind, sb.String()})
|
|
|
+ case c == '?' :
|
|
|
+ toks = append(toks, token{tkArg, "?"})
|
|
|
+ i++
|
|
|
+ case isDigit(c):
|
|
|
+ j := i
|
|
|
+ for j < n && (isDigit(sqlText[j]) || sqlText[j] == '.') {
|
|
|
+ j++
|
|
|
+ }
|
|
|
+ toks = append(toks, token{tkNumber, sqlText[i:j]})
|
|
|
+ i = j
|
|
|
+ case isIdentStart(c):
|
|
|
+ j := i
|
|
|
+ for j < n && (isIdentChar(sqlText[j])) {
|
|
|
+ j++
|
|
|
+ }
|
|
|
+ word := sqlText[i:j]
|
|
|
+ up := strings.ToUpper(word)
|
|
|
+ if keywords[up] {
|
|
|
+ toks = append(toks, token{tkKeyword, up})
|
|
|
+ } else {
|
|
|
+ toks = append(toks, token{tkIdent, word})
|
|
|
+ }
|
|
|
+ i = j
|
|
|
+ case c == ',':
|
|
|
+ toks = append(toks, token{tkPunct, ","})
|
|
|
+ i++
|
|
|
+ case c == '(':
|
|
|
+ toks = append(toks, token{tkPunct, "("})
|
|
|
+ i++
|
|
|
+ case c == ')':
|
|
|
+ toks = append(toks, token{tkPunct, ")"})
|
|
|
+ i++
|
|
|
+ case c == '*':
|
|
|
+ toks = append(toks, token{tkPunct, "*"})
|
|
|
+ i++
|
|
|
+ case c == '=':
|
|
|
+ toks = append(toks, token{tkPunct, "="})
|
|
|
+ i++
|
|
|
+ case c == '!':
|
|
|
+ if i+1 < n && sqlText[i+1] == '=' {
|
|
|
+ toks = append(toks, token{tkPunct, "!="})
|
|
|
+ i += 2
|
|
|
+ } else {
|
|
|
+ return nil, fmt.Errorf("意外的字符 '!'")
|
|
|
+ }
|
|
|
+ case c == '<':
|
|
|
+ if i+1 < n && sqlText[i+1] == '=' {
|
|
|
+ toks = append(toks, token{tkPunct, "<="})
|
|
|
+ i += 2
|
|
|
+ } else if i+1 < n && sqlText[i+1] == '>' {
|
|
|
+ toks = append(toks, token{tkPunct, "<>"})
|
|
|
+ i += 2
|
|
|
+ } else {
|
|
|
+ toks = append(toks, token{tkPunct, "<"})
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ case c == '>':
|
|
|
+ if i+1 < n && sqlText[i+1] == '=' {
|
|
|
+ toks = append(toks, token{tkPunct, ">="})
|
|
|
+ i += 2
|
|
|
+ } else {
|
|
|
+ toks = append(toks, token{tkPunct, ">"})
|
|
|
+ i++
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("无法识别的字符 %q", string(c))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ toks = append(toks, token{tkEOF, ""})
|
|
|
+ return toks, nil
|
|
|
+}
|
|
|
+
|
|
|
+func isDigit(c byte) bool { return c >= '0' && c <= '9' }
|
|
|
+func isIdentStart(c byte) bool {
|
|
|
+ return c == '_' || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c >= 0x80
|
|
|
+}
|
|
|
+func isIdentChar(c byte) bool { return isIdentStart(c) || isDigit(c) }
|
|
|
+
|
|
|
+// =============================================================================
|
|
|
+// 语法分析 (递归下降, ORM 子集)
|
|
|
+// =============================================================================
|
|
|
+
|
|
|
+type parser struct {
|
|
|
+ toks []token
|
|
|
+ pos int
|
|
|
+ argSeq int // ? 占位符序号分配器 (每条语句从 0 开始)
|
|
|
+}
|
|
|
+
|
|
|
+func parse(sqlText string) (*statement, error) {
|
|
|
+ toks, err := tokenize(sqlText)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ p := &parser{toks: toks}
|
|
|
+ st, err := p.parseStatement()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if p.peek().kind != tkEOF {
|
|
|
+ return nil, fmt.Errorf("语句末尾有多余内容: %q", p.peek().text)
|
|
|
+ }
|
|
|
+ return st, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) peek() token { return p.toks[p.pos] }
|
|
|
+func (p *parser) next() token { t := p.toks[p.pos]; p.pos++; return t }
|
|
|
+func (p *parser) backup() { p.pos-- }
|
|
|
+
|
|
|
+func (p *parser) expectKeyword(kw string) error {
|
|
|
+ t := p.next()
|
|
|
+ if t.kind != tkKeyword || t.text != kw {
|
|
|
+ return fmt.Errorf("期望 %s, 实际是 %q", kw, t.text)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) acceptKeyword(kw string) bool {
|
|
|
+ if p.peek().kind == tkKeyword && p.peek().text == kw {
|
|
|
+ p.pos++
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) expectPunct(sym string) error {
|
|
|
+ t := p.next()
|
|
|
+ if t.kind != tkPunct || t.text != sym {
|
|
|
+ return fmt.Errorf("期望 %q, 实际是 %q", sym, t.text)
|
|
|
+ }
|
|
|
+ return nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) acceptPunct(sym string) bool {
|
|
|
+ if p.peek().kind == tkPunct && p.peek().text == sym {
|
|
|
+ p.pos++
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) expectIdent() (string, error) {
|
|
|
+ t := p.next()
|
|
|
+ if t.kind != tkIdent {
|
|
|
+ return "", fmt.Errorf("期望标识符, 实际是 %q", t.text)
|
|
|
+ }
|
|
|
+ return t.text, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseStatement() (*statement, error) {
|
|
|
+ t := p.peek()
|
|
|
+ if t.kind != tkKeyword {
|
|
|
+ return nil, fmt.Errorf("语句必须以 SELECT/INSERT/UPDATE/DELETE/CREATE/DROP 开头")
|
|
|
+ }
|
|
|
+ switch t.text {
|
|
|
+ case "SELECT":
|
|
|
+ return p.parseSelect()
|
|
|
+ case "INSERT":
|
|
|
+ return p.parseInsert()
|
|
|
+ case "UPDATE":
|
|
|
+ return p.parseUpdate()
|
|
|
+ case "DELETE":
|
|
|
+ return p.parseDelete()
|
|
|
+ case "CREATE":
|
|
|
+ return p.parseCreate()
|
|
|
+ case "DROP":
|
|
|
+ return p.parseDrop()
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("不支持的语句开头: %s", t.text)
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+// parseCreateIndex CREATE [UNIQUE] INDEX|KEY name ON t (col)
|
|
|
+func (p *parser) parseCreateIndex(unique bool) (*statement, error) {
|
|
|
+ t := p.next()
|
|
|
+ if t.kind != tkKeyword || (t.text != "INDEX" && t.text != "KEY") {
|
|
|
+ return nil, fmt.Errorf("期望 INDEX/KEY, 实际是 %q", t.text)
|
|
|
+ }
|
|
|
+ name, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if err := p.expectKeyword("ON"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ table, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ cols, err := p.parseParenCols()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if len(cols) != 1 {
|
|
|
+ return nil, fmt.Errorf("CREATE INDEX 仅支持单列, 实际 %d 列", len(cols))
|
|
|
+ }
|
|
|
+ return &statement{kind: kCreateIndex, cidx: &createIndexStmt{
|
|
|
+ unique: unique, name: name, table: table, col: cols[0],
|
|
|
+ }}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseSelect() (*statement, error) {
|
|
|
+ p.next() // SELECT
|
|
|
+ sel := &selectStmt{}
|
|
|
+
|
|
|
+ // SELECT 列表: * / 列名 / 聚合
|
|
|
+ if p.acceptPunct("*") {
|
|
|
+ // 纯 * 或 COUNT(*) 在聚合分支处理
|
|
|
+ } else if p.peek().kind == tkKeyword && isAggFn(p.peek().text) {
|
|
|
+ agg, err := p.parseAgg()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ sel.agg = agg
|
|
|
+ } else {
|
|
|
+ for {
|
|
|
+ col, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ sel.columns = append(sel.columns, col)
|
|
|
+ // 容忍 "col AS alias" (alias 丢弃, 引擎列名以表为准)
|
|
|
+ if p.acceptKeyword("AS") {
|
|
|
+ if _, err := p.expectIdent(); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if err := p.expectKeyword("FROM"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ table, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ sel.table = table
|
|
|
+
|
|
|
+ if p.acceptKeyword("WHERE") {
|
|
|
+ e, err := p.parseExpr()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ sel.where = e
|
|
|
+ }
|
|
|
+ if p.acceptKeyword("GROUP") {
|
|
|
+ return nil, fmt.Errorf("GROUP BY 暂不支持")
|
|
|
+ }
|
|
|
+ if p.acceptKeyword("ORDER") {
|
|
|
+ if err := p.expectKeyword("BY"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ for {
|
|
|
+ col, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ spec := orderSpec{col: col}
|
|
|
+ if p.acceptKeyword("DESC") {
|
|
|
+ spec.desc = true
|
|
|
+ } else {
|
|
|
+ p.acceptKeyword("ASC")
|
|
|
+ }
|
|
|
+ sel.orderBy = append(sel.orderBy, spec)
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if p.acceptKeyword("LIMIT") {
|
|
|
+ v, err := p.parseIntLiteral()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ sel.limit = &v
|
|
|
+ }
|
|
|
+ if p.acceptKeyword("OFFSET") {
|
|
|
+ v, err := p.parseIntLiteral()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ sel.offset = &v
|
|
|
+ }
|
|
|
+ return &statement{kind: kSelect, sel: sel}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func isAggFn(w string) bool {
|
|
|
+ switch w {
|
|
|
+ case "COUNT", "SUM", "MIN", "MAX", "AVG":
|
|
|
+ return true
|
|
|
+ }
|
|
|
+ return false
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseAgg() (*aggSpec, error) {
|
|
|
+ fn := p.next().text
|
|
|
+ spec := &aggSpec{fn: fn}
|
|
|
+ if err := p.expectPunct("("); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if fn == "COUNT" && p.acceptPunct("*") {
|
|
|
+ spec.col = ""
|
|
|
+ } else {
|
|
|
+ col, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ spec.col = col
|
|
|
+ }
|
|
|
+ if err := p.expectPunct(")"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return spec, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseIntLiteral() (int64, error) {
|
|
|
+ t := p.next()
|
|
|
+ if t.kind == tkNumber {
|
|
|
+ v, err := strconv.ParseInt(t.text, 10, 64)
|
|
|
+ if err == nil {
|
|
|
+ return v, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0, fmt.Errorf("期望整数, 实际是 %q", t.text)
|
|
|
+}
|
|
|
+
|
|
|
+// parseExpr: OR → AND → NOT → 原子
|
|
|
+func (p *parser) parseExpr() (expr, error) { return p.parseOr() }
|
|
|
+
|
|
|
+func (p *parser) parseOr() (expr, error) {
|
|
|
+ left, err := p.parseAnd()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ for p.peek().kind == tkKeyword && p.peek().text == "OR" {
|
|
|
+ p.next()
|
|
|
+ right, err := p.parseAnd()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ left = &orExpr{l: left, r: right}
|
|
|
+ }
|
|
|
+ return left, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseAnd() (expr, error) {
|
|
|
+ left, err := p.parseNot()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ for p.peek().kind == tkKeyword && p.peek().text == "AND" {
|
|
|
+ p.next()
|
|
|
+ right, err := p.parseNot()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ left = &andExpr{l: left, r: right}
|
|
|
+ }
|
|
|
+ return left, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseNot() (expr, error) {
|
|
|
+ if p.acceptKeyword("NOT") {
|
|
|
+ e, err := p.parseNot()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return ¬Expr{e: e}, nil
|
|
|
+ }
|
|
|
+ return p.parseAtom()
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseAtom() (expr, error) {
|
|
|
+ // ( expr )
|
|
|
+ if p.acceptPunct("(") {
|
|
|
+ e, err := p.parseExpr()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if err := p.expectPunct(")"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return e, nil
|
|
|
+ }
|
|
|
+ col, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ // IS [NOT] NULL
|
|
|
+ if p.peek().kind == tkKeyword && p.peek().text == "IS" {
|
|
|
+ p.next()
|
|
|
+ neg := p.acceptKeyword("NOT")
|
|
|
+ if err := p.expectKeyword("NULL"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return &isNullExpr{col: col, neg: neg}, nil
|
|
|
+ }
|
|
|
+ // [NOT] IN (...)
|
|
|
+ neg := false
|
|
|
+ if p.peek().kind == tkKeyword && p.peek().text == "NOT" {
|
|
|
+ p.next()
|
|
|
+ neg = true
|
|
|
+ }
|
|
|
+ if p.acceptKeyword("IN") {
|
|
|
+ if err := p.expectPunct("("); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ var vals []value
|
|
|
+ for {
|
|
|
+ v, err := p.parseValue()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ vals = append(vals, v)
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err := p.expectPunct(")"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return &inExpr{col: col, vals: vals, neg: neg}, nil
|
|
|
+ }
|
|
|
+ // [NOT] LIKE
|
|
|
+ if p.acceptKeyword("LIKE") {
|
|
|
+ pat, err := p.parseValue()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return &likeExpr{col: col, pattern: pat, neg: neg}, nil
|
|
|
+ }
|
|
|
+ if neg {
|
|
|
+ return nil, fmt.Errorf("NOT 后面期望 IN 或 LIKE")
|
|
|
+ }
|
|
|
+ // 比较符
|
|
|
+ op := p.next()
|
|
|
+ if op.kind != tkPunct {
|
|
|
+ return nil, fmt.Errorf("列 %q 后期望比较符, 实际是 %q", col, op.text)
|
|
|
+ }
|
|
|
+ switch op.text {
|
|
|
+ case "=", "!=", "<>", "<", "<=", ">", ">=":
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("不支持的比较符 %q", op.text)
|
|
|
+ }
|
|
|
+ v, err := p.parseValue()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if op.text == "<>" {
|
|
|
+ op.text = "!="
|
|
|
+ }
|
|
|
+ return &cmpExpr{col: col, op: op.text, val: v}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseValue() (value, error) {
|
|
|
+ t := p.next()
|
|
|
+ switch t.kind {
|
|
|
+ case tkArg:
|
|
|
+ idx := p.argSeq
|
|
|
+ p.argSeq++
|
|
|
+ return argValue{idx: idx}, nil
|
|
|
+ case tkNumber:
|
|
|
+ return litValue{raw: t.text}, nil
|
|
|
+ case tkString:
|
|
|
+ return strValue{s: t.text}, nil
|
|
|
+ case tkKeyword:
|
|
|
+ if t.text == "NULL" {
|
|
|
+ return nullValue{}, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return nil, fmt.Errorf("无法解析的值: %q", t.text)
|
|
|
+}
|
|
|
+
|
|
|
+// =============================================================================
|
|
|
+// INSERT / UPDATE / DELETE / CREATE / DROP
|
|
|
+// =============================================================================
|
|
|
+
|
|
|
+func (p *parser) parseInsert() (*statement, error) {
|
|
|
+ p.next() // INSERT
|
|
|
+ if err := p.expectKeyword("INTO"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ table, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ ins := &insertStmt{table: table}
|
|
|
+
|
|
|
+ if p.acceptPunct("(") {
|
|
|
+ for {
|
|
|
+ col, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ ins.columns = append(ins.columns, col)
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err := p.expectPunct(")"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if !p.acceptKeyword("VALUES") {
|
|
|
+ if err := p.expectKeyword("VALUE"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ for {
|
|
|
+ if err := p.expectPunct("("); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ var row []value
|
|
|
+ for {
|
|
|
+ v, err := p.parseValue()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ row = append(row, v)
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err := p.expectPunct(")"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ ins.rows = append(ins.rows, row)
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return &statement{kind: kInsert, ins: ins}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseUpdate() (*statement, error) {
|
|
|
+ p.next() // UPDATE
|
|
|
+ table, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if err := p.expectKeyword("SET"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ upd := &updateStmt{table: table}
|
|
|
+ for {
|
|
|
+ col, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if err := p.expectPunct("="); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ v, err := p.parseValue()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ upd.sets = append(upd.sets, setClause{col: col, val: v})
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if p.acceptKeyword("WHERE") {
|
|
|
+ e, err := p.parseExpr()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ upd.where = e
|
|
|
+ }
|
|
|
+ return &statement{kind: kUpdate, upd: upd}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseDelete() (*statement, error) {
|
|
|
+ p.next() // DELETE
|
|
|
+ if err := p.expectKeyword("FROM"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ table, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ del := &deleteStmt{table: table}
|
|
|
+ if p.acceptKeyword("WHERE") {
|
|
|
+ e, err := p.parseExpr()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ del.where = e
|
|
|
+ }
|
|
|
+ return &statement{kind: kDelete, del: del}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseCreate() (*statement, error) {
|
|
|
+ p.next() // CREATE
|
|
|
+ // CREATE UNIQUE INDEX ...
|
|
|
+ if p.peek().kind == tkKeyword && p.peek().text == "UNIQUE" {
|
|
|
+ p.next()
|
|
|
+ return p.parseCreateIndex(true)
|
|
|
+ }
|
|
|
+ // CREATE INDEX ...
|
|
|
+ if p.peek().kind == tkKeyword && (p.peek().text == "INDEX" || p.peek().text == "KEY") {
|
|
|
+ return p.parseCreateIndex(false)
|
|
|
+ }
|
|
|
+ if err := p.expectKeyword("TABLE"); err != nil {
|
|
|
+ return nil, fmt.Errorf("仅支持 CREATE TABLE / CREATE [UNIQUE] INDEX")
|
|
|
+ }
|
|
|
+ crt := &createStmt{}
|
|
|
+ if p.acceptKeyword("IF") {
|
|
|
+ if err := p.expectKeyword("NOT"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ if err := p.expectKeyword("EXISTS"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ crt.ifNotExists = true
|
|
|
+ }
|
|
|
+ table, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ crt.table = table
|
|
|
+ if err := p.expectPunct("("); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ for {
|
|
|
+ // 表级约束: PRIMARY KEY (c) / [UNIQUE] KEY k (c)
|
|
|
+ if p.peek().kind == tkKeyword {
|
|
|
+ switch p.peek().text {
|
|
|
+ case "PRIMARY":
|
|
|
+ p.next()
|
|
|
+ if err := p.expectKeyword("KEY"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ col, err := p.parseParenSingleCol()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ crt.pkCol = col
|
|
|
+ case "UNIQUE":
|
|
|
+ p.next()
|
|
|
+ idx, err := p.parseInlineIndex(true)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ crt.indexes = append(crt.indexes, idx)
|
|
|
+ case "KEY", "INDEX":
|
|
|
+ idx, err := p.parseInlineIndex(false)
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ crt.indexes = append(crt.indexes, idx)
|
|
|
+ default:
|
|
|
+ return nil, fmt.Errorf("CREATE TABLE 里不支持的表级项: %s", p.peek().text)
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ col, err := p.parseColumnDef()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ crt.columns = append(crt.columns, col)
|
|
|
+ }
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err := p.expectPunct(")"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ // 列级 PRIMARY KEY 提升为表级 pk (单列); 主键隐含 NOT NULL
|
|
|
+ if crt.pkCol == "" {
|
|
|
+ for _, c := range crt.columns {
|
|
|
+ if c.pk {
|
|
|
+ crt.pkCol = c.name
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if crt.pkCol != "" {
|
|
|
+ for i := range crt.columns {
|
|
|
+ if strings.EqualFold(crt.columns[i].name, crt.pkCol) {
|
|
|
+ crt.columns[i].notNull = true
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 容忍 CREATE TABLE ... 后的表选项被整体忽略? 不: 显式报错以免静默错配。
|
|
|
+ if p.peek().kind == tkKeyword && (p.peek().text == "ENGINE" || p.peek().text == "DEFAULT" || p.peek().text == "CHARSET") {
|
|
|
+ return nil, fmt.Errorf("CREATE TABLE 表选项 (ENGINE/DEFAULT CHARSET 等) 不支持")
|
|
|
+ }
|
|
|
+ return &statement{kind: kCreate, crt: crt}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseInlineIndex(unique bool) (inlineIndex, error) {
|
|
|
+ // 已消费 UNIQUE (若有); 当前在 KEY/INDEX
|
|
|
+ if p.peek().kind != tkKeyword || (p.peek().text != "KEY" && p.peek().text != "INDEX") {
|
|
|
+ return inlineIndex{}, fmt.Errorf("期望 KEY/INDEX, 实际是 %q", p.peek().text)
|
|
|
+ }
|
|
|
+ p.next()
|
|
|
+ name := ""
|
|
|
+ if p.peek().kind == tkIdent {
|
|
|
+ name = p.next().text
|
|
|
+ }
|
|
|
+ cols, err := p.parseParenCols()
|
|
|
+ if err != nil {
|
|
|
+ return inlineIndex{}, err
|
|
|
+ }
|
|
|
+ if len(cols) != 1 {
|
|
|
+ return inlineIndex{}, fmt.Errorf("内联索引仅支持单列, 实际 %d 列", len(cols))
|
|
|
+ }
|
|
|
+ return inlineIndex{name: name, columns: cols, unique: unique}, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseParenCols() ([]string, error) {
|
|
|
+ if err := p.expectPunct("("); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ var cols []string
|
|
|
+ for {
|
|
|
+ col, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ cols = append(cols, col)
|
|
|
+ if !p.acceptPunct(",") {
|
|
|
+ break
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if err := p.expectPunct(")"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ return cols, nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseParenSingleCol() (string, error) {
|
|
|
+ cols, err := p.parseParenCols()
|
|
|
+ if err != nil {
|
|
|
+ return "", err
|
|
|
+ }
|
|
|
+ if len(cols) != 1 {
|
|
|
+ return "", fmt.Errorf("PRIMARY KEY 仅支持单列, 实际 %d 列", len(cols))
|
|
|
+ }
|
|
|
+ return cols[0], nil
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseColumnDef() (colDef, error) {
|
|
|
+ name, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return colDef{}, err
|
|
|
+ }
|
|
|
+ def := colDef{name: name, arg: -1}
|
|
|
+ t := p.next()
|
|
|
+ if t.kind != tkIdent && t.kind != tkKeyword {
|
|
|
+ return colDef{}, fmt.Errorf("列 %q 后期望类型名, 实际是 %q", name, t.text)
|
|
|
+ }
|
|
|
+ def.typeName = strings.ToUpper(t.text)
|
|
|
+ // UNSIGNED 后缀: "BIGINT UNSIGNED" 是两词, 合并进类型名
|
|
|
+ if p.peek().kind == tkKeyword && p.peek().text == "UNSIGNED" {
|
|
|
+ p.next()
|
|
|
+ def.typeName += " UNSIGNED"
|
|
|
+ }
|
|
|
+ // 类型参数: VARCHAR(n) / DECIMAL(p,s) / CHAR(n)
|
|
|
+ if p.acceptPunct("(") {
|
|
|
+ a, err := p.parseIntLiteral()
|
|
|
+ if err != nil {
|
|
|
+ return colDef{}, err
|
|
|
+ }
|
|
|
+ def.arg = int(a)
|
|
|
+ if def.typeName == "DECIMAL" && p.acceptPunct(",") {
|
|
|
+ s, err := p.parseIntLiteral()
|
|
|
+ if err != nil {
|
|
|
+ return colDef{}, err
|
|
|
+ }
|
|
|
+ def.arg = int(s) // DECIMAL(p,s) 取 scale
|
|
|
+ }
|
|
|
+ if err := p.expectPunct(")"); err != nil {
|
|
|
+ return colDef{}, err
|
|
|
+ }
|
|
|
+ }
|
|
|
+ // 列级约束
|
|
|
+ for {
|
|
|
+ switch {
|
|
|
+ case p.acceptKeyword("NOT"):
|
|
|
+ if err := p.expectKeyword("NULL"); err != nil {
|
|
|
+ return colDef{}, err
|
|
|
+ }
|
|
|
+ def.notNull = true
|
|
|
+ case p.acceptKeyword("NULL"):
|
|
|
+ // 显式 NULL = 可空 (默认即可空, 忽略)
|
|
|
+ case p.acceptKeyword("UNIQUE"):
|
|
|
+ def.unique = true
|
|
|
+ case p.acceptKeyword("PRIMARY"):
|
|
|
+ if err := p.expectKeyword("KEY"); err != nil {
|
|
|
+ return colDef{}, err
|
|
|
+ }
|
|
|
+ def.pk = true
|
|
|
+ def.notNull = true // SQL: 主键列隐含 NOT NULL
|
|
|
+ case p.acceptKeyword("DEFAULT"):
|
|
|
+ // DEFAULT NULL 忽略; 其他默认值不支持 (避免静默错配)
|
|
|
+ v, err := p.parseValue()
|
|
|
+ if err != nil {
|
|
|
+ return colDef{}, err
|
|
|
+ }
|
|
|
+ if _, isNull := v.(nullValue); !isNull {
|
|
|
+ return colDef{}, fmt.Errorf("列 %q 的 DEFAULT 非 NULL 值暂不支持", name)
|
|
|
+ }
|
|
|
+ case p.acceptKeyword("AUTO_INCREMENT"):
|
|
|
+ // 自增列 = 引擎 pkID 镜像, 忽略标记
|
|
|
+ case p.acceptKeyword("COMMENT"):
|
|
|
+ if _, err := p.parseValue(); err != nil {
|
|
|
+ return colDef{}, err
|
|
|
+ }
|
|
|
+ default:
|
|
|
+ return def, nil
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+func (p *parser) parseDrop() (*statement, error) {
|
|
|
+ p.next() // DROP
|
|
|
+ if err := p.expectKeyword("TABLE"); err != nil {
|
|
|
+ return nil, fmt.Errorf("仅支持 DROP TABLE")
|
|
|
+ }
|
|
|
+ d := &dropStmt{}
|
|
|
+ if p.acceptKeyword("IF") {
|
|
|
+ if err := p.expectKeyword("EXISTS"); err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ d.ifExists = true
|
|
|
+ }
|
|
|
+ table, err := p.expectIdent()
|
|
|
+ if err != nil {
|
|
|
+ return nil, err
|
|
|
+ }
|
|
|
+ d.table = table
|
|
|
+ return &statement{kind: kDrop, drop: d}, nil
|
|
|
+}
|
|
|
+
|
|
|
+
|