// Package vrtxsql — TsDB2 的 database/sql 驱动。 // // 让任意基于 database/sql 的工具/ORM (xorm、sqlx 以及自研 ORM) 直接对接 // VRTX 引擎: 标准 SQL 文本 → 引擎查询体系 (core.Query/Condition/Table)。 // // 解析器是刻意收窄的【ORM 子集翻译器】, 不是通用 SQL 引擎: // // SELECT cols|* FROM t [JOIN|INNER JOIN|LEFT [OUTER] JOIN t2 ON a.x = b.y ...] // [WHERE ...] [ORDER BY c [ASC|DESC], ...] [LIMIT n|?] [OFFSET n|?] // SELECT 值清单 (无 FROM, 仅字面量/NULL/? —— 探活语句 SELECT 1) // 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, // 以及 SQLite 方言 '||' 字符串拼接作 LIKE/NOT LIKE 左操作数 // (如 ','||col||',' LIKE '%,x,%', 仅匹配逗号分隔列表里的精确元素)。 // JOIN 支持: 多表链式 [INNER] JOIN / LEFT [OUTER] JOIN, ON 仅等值 a.x = b.y; // 列引用允许限定名 (别名.列 / 表名.列), 跨表条件作为残余谓词在组合行上过滤。 // LIMIT/OFFSET 接受整数字面量或 ? 占位符 (GORM 的 Limit/Offset 走参数)。 // 不支持: 子查询/GROUP BY/HAVING/函数表达式/DEFAULT 非 NULL 值/UPSERT // (ON CONFLICT/ON DUPLICATE KEY UPDATE —— 遇到返回明确错误引导改用 Updates)。 package vrtxsql import ( "fmt" "strconv" "strings" ) // ============================================================================= // AST // ============================================================================= type stmtKind int const ( kSelect stmtKind = iota + 1 kInsert kUpdate kDelete kCreate kCreateIndex kDrop kAlter kShow kTruncate kVacuum ) type statement struct { kind stmtKind sel *selectStmt ins *insertStmt upd *updateStmt del *deleteStmt crt *createStmt cidx *createIndexStmt drop *dropStmt alt *alterStmt show *showStmt trunc *truncateStmt } func (s *statement) argCount() int { n := 0 switch s.kind { case kSelect: n = exprMaxArg(s.sel.where) n = max2(n, valueMaxArg(s.sel.limit)) n = max2(n, valueMaxArg(s.sel.offset)) for _, v := range s.sel.literalVals { n = max2(n, valueMaxArg(v)) } 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 } // valueMaxArg 值里的 ? 占位符占到的最大参数序号 (无占位符返回 0)。 func valueMaxArg(v value) int { if a, ok := v.(argValue); ok { return a.idx + 1 } return 0 } type selectStmt struct { from tableRef // FROM 表 (alias 缺省 = 表名, 限定列名归一化用); noFrom 时为空 joins []joinClause columns []string // 空 = *; 元素可为 "*"(全表) / "t.*"(指定表全列) / 列名 / "t.列名" distinct bool // SELECT DISTINCT agg *aggSpec // 非空 = 单行聚合 where expr orderBy []orderSpec limit value // LIMIT 量: litValue(整数) 或 argValue(? 占位); nil = 无 offset value // OFFSET 量, 同上 // noFrom = true 表示 "SELECT 值清单" 无 FROM (探活语句 SELECT 1 等): // literalVals 只能是字面量 (数字/字符串/NULL) 或 ? 占位, 执行成单行走结果。 noFrom bool literalVals []value } // tableRef FROM/JOIN 里的表引用: 表名 + 别名 (AS 可省略)。 type tableRef struct { name string alias string } // joinClause 一个 JOIN 子句: [LEFT] JOIN 表 ON a.x = b.y (onL/onR 为限定列名)。 type joinClause struct { left bool // LEFT [OUTER] JOIN table tableRef onL string onR string } 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 } // alterOp ALTER TABLE 的单个操作: ADD [COLUMN] (drop=false,def) / DROP [COLUMN] (drop=true,col) type alterOp struct { drop bool col string // DROP 时列名 def colDef // ADD 时列定义 } // alterStmt ALTER TABLE t op [, op]... type alterStmt struct { table string ops []alterOp } // showStmt SHOW TABLES / SHOW [FULL] COLUMNS FROM t / DESCRIBE t (what=DESCRIBE) type showStmt struct { what string // "TABLES" / "COLUMNS" / "DESCRIBE" table string full bool } // truncateStmt TRUNCATE [TABLE] t type truncateStmt struct { table 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 } // colCmpExpr 列与列比较 (a.x = b.y): 引擎 Condition 只支持列与值, 该表达式仅 // 在 JOIN 查询里作为残余谓词在组合行上求值; 单表查询遇到会明确报错。 type colCmpExpr struct { left string right string op string // = != < <= > >= } type inExpr struct { col string vals []value neg bool } type isNullExpr struct { col string neg bool } type likeExpr struct { col string lhs *concatExpr // 非空 = 左操作数是 || 拼接表达式 (col 为空, 仅 LIKE/NOT LIKE 允许) pattern value neg bool } // concatPart 拼接链的单项: 字符串字面量 (isCol=false, lit) 或列引用 // (isCol=true, col, 可限定 "t.col" — 与列引用同一解析, 存原文)。 type concatPart struct { isCol bool lit string col string } // concatExpr SQLite || 字符串拼接链: term (|| term)*。只在 LIKE/NOT LIKE 的 // 左操作数位允许; 其它比较符/IN/IS NULL 遇到拼接左操作数在解析层直接报错。 type concatExpr struct { parts []concatPart } func (*andExpr) isExpr() {} func (*orExpr) isExpr() {} func (*notExpr) isExpr() {} func (*cmpExpr) isExpr() {} func (*colCmpExpr) 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, "AS": true, "ALTER": true, "ADD": true, "COLUMN": true, "SHOW": true, "DESCRIBE": true, "FULL": true, "TABLES": true, "COLUMNS": true, "TRUNCATE": true, "DISTINCT": true, "VACUUM": true, "JOIN": true, "INNER": true, "LEFT": true, "OUTER": 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 == '=': 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++ } case c == '|': // SQLite 方言: '||' 字符串拼接 (WHERE 里作 LIKE 左操作数, 见 concatExpr)。 // 单个 '|' 无对应语义, 维持"无法识别的字符"报错。 if i+1 < n && sqlText[i+1] == '|' { toks = append(toks, token{tkPunct, "||"}) i += 2 } else { return nil, fmt.Errorf("无法识别的字符 %q", string(c)) } 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, &SQLError{State: "42000", Message: err.Error(), cause: err} } p := &parser{toks: toks} st, err := p.parseStatement() if err != nil { return nil, &SQLError{State: "42000", Message: err.Error(), cause: err} } if p.peek().kind != tkEOF { msg := fmt.Errorf("语句末尾有多余内容: %q", p.peek().text) return nil, &SQLError{State: "42000", Message: msg.Error(), cause: msg} } return st, nil } func (p *parser) peek() token { return p.toks[p.pos] } func (p *parser) peekAt(n int) token { if p.pos+n < len(p.toks) { return p.toks[p.pos+n] } return token{kind: tkEOF} } 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/ALTER/SHOW/TRUNCATE/VACUUM 开头") } 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() case "ALTER": return p.parseAlter() case "SHOW": return p.parseShow() case "DESC", "DESCRIBE": return p.parseDescribe() case "TRUNCATE": return p.parseTruncate() case "VACUUM": return p.parseVacuum() default: return nil, fmt.Errorf("不支持的语句开头: %s", t.text) } } // parseVacuum VACUUM —— 整库重建 (回收孤儿页 / 重建索引 / 收缩文件)。 // 只接受裸语句, 不带任何子句; 细节 (目标表 / 并行度) 由引擎选项管, SQL 层不做。 func (p *parser) parseVacuum() (*statement, error) { p.next() // 吃掉 VACUUM return &statement{kind: kVacuum}, nil } // 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{} if p.acceptKeyword("DISTINCT") { sel.distinct = true } // SELECT 值清单 (无 FROM): 探活语句 "SELECT 1" / "SELECT 'a', 1"。值只能是 // 字面量 (数字/字符串/NULL) 或 ? 占位 —— 不支持表达式/函数/聚合, 遇到这类 // 开头直接走普通列清单解析, 会在期望 FROM 处给出明确错误。 if isSelectLiteralStart(p.peek()) { sel.noFrom = true for { v, err := p.parseValue() if err != nil { return nil, err } sel.literalVals = append(sel.literalVals, v) if !p.acceptPunct(",") { break } } if p.peek().kind != tkEOF { return nil, fmt.Errorf("SELECT 值清单 (无 FROM) 仅支持字面量, 遇到多余内容 %q (表达式/别名/函数请改用带 FROM 的查询)", p.peek().text) } return &statement{kind: kSelect, sel: sel}, nil } // SELECT 列表: * / t.* / 列名 / t.列名 (可混用); 聚合单行另走分支。 // 聚合函数名 (COUNT/SUM/...) 不是保留字 — 列名可以叫 count (GORM 模型常见); // 仅当名字后紧跟 '(' 才按聚合解析, 否则就是普通列引用。ident 保留原文大小写, // 判定统一转大写 (GORM 生成的小写 count(*) 也要认)。 if p.peek().kind == tkIdent && isAggFn(strings.ToUpper(p.peek().text)) && p.peekAt(1).kind == tkPunct && p.peekAt(1).text == "(" { agg, err := p.parseAgg() if err != nil { return nil, err } sel.agg = agg } else { for { if p.acceptPunct("*") { sel.columns = append(sel.columns, "*") } else { col, err := p.parseSelectCol() 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 } from, err := p.parseTableRef() if err != nil { return nil, err } sel.from = from // JOIN 链: [INNER] JOIN / LEFT [OUTER] JOIN 表 ON a.x = b.y (可多个, 3 表以上) for { jc, ok, err := p.parseJoinClause() if err != nil { return nil, err } if !ok { break } sel.joins = append(sel.joins, jc) } 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.parseColRef() 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.parseLimitValue() if err != nil { return nil, err } if p.acceptPunct(",") { // MySQL 兼容: LIMIT offset, count (逗号前是偏移量, 后是行数) c, err := p.parseLimitValue() if err != nil { return nil, err } sel.offset = v sel.limit = c } else { sel.limit = v } } if p.acceptKeyword("OFFSET") { v, err := p.parseLimitValue() if err != nil { return nil, err } sel.offset = v } return &statement{kind: kSelect, sel: sel}, nil } // isSelectLiteralStart 判断 SELECT 后是否直接跟字面量 (数字/字符串/NULL/?), // 即 "SELECT 值清单" 无 FROM 形态的开头。 func isSelectLiteralStart(t token) bool { switch t.kind { case tkNumber, tkString, tkArg: return true case tkKeyword: return t.text == "NULL" } return false } // parseLimitValue 解析 LIMIT/OFFSET 的量: 整数字面量或 ? 占位符 (GORM 的 // Limit/Offset 走参数时会发出 "LIMIT ?"; 字面量路径保持兼容)。 func (p *parser) parseLimitValue() (value, error) { if p.peek().kind == tkArg { p.next() idx := p.argSeq p.argSeq++ return argValue{idx: idx}, nil } t := p.next() if t.kind == tkNumber { if _, err := strconv.ParseInt(t.text, 10, 64); err == nil { return litValue{raw: t.text}, nil } } return nil, fmt.Errorf("期望整数或 ? 占位符, 实际是 %q", t.text) } func isAggFn(w string) bool { switch w { case "COUNT", "SUM", "MIN", "MAX", "AVG": return true } return false } func (p *parser) parseAgg() (*aggSpec, error) { // ident 保留原文大小写 (count / COUNT 都要认), 函数名归一成上限 — // 下游 queryAggregate 按 "COUNT"/"SUM"/... 大写匹配, MIN/MAX 的结果列名 // 也要这个形态。 fn := strings.ToUpper(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.parseColRef() 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) } // parseColRef 解析列引用: 列名 或 限定列名 "t.col" (两个 ident 由 '.' 连, 解析层拼)。 // 列引用统一存原文 ("t.col"), 执行期按 "." 拆分归属。 func (p *parser) parseColRef() (string, error) { col, err := p.expectIdent() if err != nil { return "", err } if p.acceptPunct(".") { c2, err := p.expectIdent() if err != nil { return "", err } return col + "." + c2, nil } return col, nil } // parseSelectCol 解析 SELECT 清单列项: 列名 / t.列名 / t.* (返回 "t.*")。 func (p *parser) parseSelectCol() (string, error) { col, err := p.expectIdent() if err != nil { return "", err } if p.acceptPunct(".") { if p.acceptPunct("*") { return col + ".*", nil } c2, err := p.expectIdent() if err != nil { return "", err } return col + "." + c2, nil } return col, nil } // parseTableRef 解析 "表名 [AS] 别名"; 无别名时 alias 归一为表名 (限定列名归一化用)。 func (p *parser) parseTableRef() (tableRef, error) { name, err := p.expectIdent() if err != nil { return tableRef{}, err } ref := tableRef{name: name, alias: name} if p.acceptKeyword("AS") { alias, err := p.expectIdent() if err != nil { return tableRef{}, err } ref.alias = alias } else if p.peek().kind == tkIdent { ref.alias = p.next().text } return ref, nil } // parseJoinClause 尝试解析一个 JOIN 子句; 当前不是 JOIN 开头时返回 ok=false。 // JOIN / INNER JOIN / LEFT JOIN / LEFT OUTER JOIN, ON 仅支持等值 a.x = b.y。 func (p *parser) parseJoinClause() (joinClause, bool, error) { var jc joinClause switch { case p.acceptKeyword("JOIN"): // JOIN 是 INNER JOIN 的简写 case p.acceptKeyword("INNER"): if err := p.expectKeyword("JOIN"); err != nil { return jc, false, err } case p.acceptKeyword("LEFT"): jc.left = true p.acceptKeyword("OUTER") if err := p.expectKeyword("JOIN"); err != nil { return jc, false, err } default: return jc, false, nil } ref, err := p.parseTableRef() if err != nil { return jc, false, err } jc.table = ref if err := p.expectKeyword("ON"); err != nil { return jc, false, err } onL, onR, err := p.parseJoinOn() if err != nil { return jc, false, err } jc.onL, jc.onR = onL, onR return jc, true, nil } // parseJoinOn 解析 "a.x = b.y" 形式的等值连接条件, 两侧都必须是限定列名。 func (p *parser) parseJoinOn() (string, string, error) { l, err := p.parseColRef() if err != nil { return "", "", err } op := p.next() if op.kind != tkPunct || op.text != "=" { return "", "", fmt.Errorf("JOIN ON 仅支持等值条件, 实际是 %q", op.text) } r, err := p.parseColRef() if err != nil { return "", "", err } return l, r, nil } // 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 } // 左操作数首项: 字符串字面量或列引用 (可限定)。'||' 拼接链只在 LIKE 左操作数位 // 允许, 这里先吃首项, 遇 "||" 走拼接分支, 否则按普通列谓词解析。 first, err := p.parseConcatTerm() if err != nil { return nil, err } if p.peek().kind == tkPunct && p.peek().text == "||" { parts := []concatPart{first} for p.acceptPunct("||") { part, err := p.parseConcatTerm() if err != nil { return nil, err } parts = append(parts, part) } return p.parseConcatLike(&concatExpr{parts: parts}) } if !first.isCol { return nil, fmt.Errorf("WHERE 条件期望列引用, 实际是字符串字面量 %q (字面量仅在 || 拼接中可作操作数)", first.lit) } col := first.col // 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) } // 右侧是列引用 → 列与列比较 (JOIN 残余谓词用); 否则按值解析 if p.peek().kind == tkIdent { rcol, err := p.parseColRef() if err != nil { return nil, err } if op.text == "<>" { op.text = "!=" } return &colCmpExpr{left: col, op: op.text, right: rcol}, nil } 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 } // parseConcatTerm 拼接链的单项: 字符串字面量或列引用 (可限定, 与列引用同一解析, // 存 "t.col" 原文; 限定词剥离在执行期做)。 func (p *parser) parseConcatTerm() (concatPart, error) { if p.peek().kind == tkString { return concatPart{lit: p.next().text}, nil } col, err := p.parseColRef() if err != nil { return concatPart{}, err } return concatPart{isCol: true, col: col}, nil } // parseConcatLike 拼接表达式后只允许 [NOT] LIKE; 遇到 IN / IS NULL / 其它比较符 // 一律给明确错误, 避免把 "','||col||',' = 'x'" 这类静默翻成错误的条件语义。 func (p *parser) parseConcatLike(concat *concatExpr) (expr, error) { neg := false if p.peek().kind == tkKeyword && p.peek().text == "NOT" { p.next() neg = true } if p.acceptKeyword("LIKE") { pat, err := p.parseValue() if err != nil { return nil, err } return &likeExpr{lhs: concat, pattern: pat, neg: neg}, nil } if neg { return nil, fmt.Errorf("NOT 后面期望 LIKE (|| 拼接仅支持 LIKE 匹配)") } return nil, fmt.Errorf("|| 拼接仅支持 LIKE 匹配") } 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 } } // VALUES 行组之后遇到 ON: UPSERT 语法 (PostgreSQL ON CONFLICT / // MySQL ON DUPLICATE KEY UPDATE)。引擎不支持, 给明确指引而不是笼统的 // "语句末尾有多余内容"。 if p.peek().kind == tkKeyword && p.peek().text == "ON" { return nil, fmt.Errorf("不支持 UPSERT (ON CONFLICT/ON DUPLICATE KEY UPDATE); 请改用 Updates 或拆成 查+插") } 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 } } } // 表选项 (ENGINE=xxx / DEFAULT CHARSET=xxx / COMMENT='...' 等): 解析并忽略。 // 嵌入式引擎没有这些概念, 但 ORM (GORM/xorm) 生成的 DDL 普遍携带 —— // 容错掉让语句可执行; 白名单之外的名字仍报错, 避免把拼写错误吞掉。 for { t := p.peek() if t.kind == tkEOF { break } switch strings.ToUpper(t.text) { case "ENGINE", "DEFAULT", "CHARSET", "COLLATE", "AUTO_INCREMENT", "COMMENT", "ROW_FORMAT", "KEY_BLOCK_SIZE", "COMPRESSION", "PACK_KEYS", "DELAY_KEY_WRITE", "STATS_PERSISTENT", "STATS_AUTO_RECALC", "CHECKSUM", "CONNECTION", "DATA", "INDEX", "INSERT_METHOD", "MAX_ROWS", "MIN_ROWS", "PASSWORD", "UNION": p.next() if p.acceptPunct("=") { _ = p.next() // 值 (数字/字符串/标识符) 忽略 } default: return nil, fmt.Errorf("不支持的表选项: %q", t.text) } } 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 } // ============================================================================= // ALTER TABLE / SHOW / DESCRIBE / TRUNCATE // ============================================================================= // parseAlter ALTER TABLE t ADD [COLUMN] colDef [, DROP [COLUMN] c]... func (p *parser) parseAlter() (*statement, error) { p.next() // ALTER if err := p.expectKeyword("TABLE"); err != nil { return nil, err } table, err := p.expectIdent() if err != nil { return nil, err } alt := &alterStmt{table: table} for { op, err := p.parseAlterOp() if err != nil { return nil, err } alt.ops = append(alt.ops, op) if !p.acceptPunct(",") { break } } return &statement{kind: kAlter, alt: alt}, nil } // parseAlterOp 解析单个 ADD / DROP COLUMN 操作。 func (p *parser) parseAlterOp() (alterOp, error) { switch { case p.acceptKeyword("ADD"): p.acceptKeyword("COLUMN") // COLUMN 关键字可选 def, err := p.parseColumnDef() if err != nil { return alterOp{}, err } return alterOp{def: def}, nil case p.acceptKeyword("DROP"): p.acceptKeyword("COLUMN") col, err := p.expectIdent() if err != nil { return alterOp{}, err } return alterOp{drop: true, col: col}, nil default: return alterOp{}, fmt.Errorf("ALTER TABLE 仅支持 ADD [COLUMN] / DROP [COLUMN], 实际是 %q", p.peek().text) } } // parseShow SHOW TABLES / SHOW [FULL] COLUMNS FROM t func (p *parser) parseShow() (*statement, error) { p.next() // SHOW full := p.acceptKeyword("FULL") t := p.next() if t.kind != tkKeyword { return nil, fmt.Errorf("SHOW 后期望 TABLES/COLUMNS, 实际是 %q", t.text) } switch t.text { case "TABLES": if full { return nil, fmt.Errorf("SHOW TABLES 不支持 FULL") } return &statement{kind: kShow, show: &showStmt{what: "TABLES"}}, nil case "COLUMNS": if err := p.expectKeyword("FROM"); err != nil { return nil, err } tbl, err := p.expectIdent() if err != nil { return nil, err } return &statement{kind: kShow, show: &showStmt{what: "COLUMNS", table: tbl, full: full}}, nil default: return nil, fmt.Errorf("SHOW 仅支持 TABLES / COLUMNS, 实际是 %q", t.text) } } // parseDescribe DESCRIBE t / DESC t (语句开头的 DESC 与排序 DESC 区分: 此处是首关键字) func (p *parser) parseDescribe() (*statement, error) { p.next() // DESC / DESCRIBE tbl, err := p.expectIdent() if err != nil { return nil, err } return &statement{kind: kShow, show: &showStmt{what: "DESCRIBE", table: tbl}}, nil } // parseTruncate TRUNCATE [TABLE] t func (p *parser) parseTruncate() (*statement, error) { p.next() // TRUNCATE p.acceptKeyword("TABLE") // TABLE 关键字可选 tbl, err := p.expectIdent() if err != nil { return nil, err } return &statement{kind: kTruncate, trunc: &truncateStmt{table: tbl}}, nil }