SQL解析器:數據庫的"翻譯官"圖解與代碼詳解
圖解SQL解析過程
SQL解析器就像是人類語言與計算機之間的翻譯官,將我們書寫的SQL語句轉換成數據庫能夠理解和執行的結構。
1. 詞法分析:從文本到標記
詞法分析器的工作就像是將一段完整的句子拆分成單詞和標點符號。對于SQL語句 SELECT id, name FROM users WHERE age > 18
:
詞法分析器核心代碼示例
詞法分析器讀取SQL文本,按字符處理,輸出標記序列:
// 詞法分析器核心代碼 - 簡化展示
func (l *Lexer) NextToken() Token {// 跳過空白字符l.skipWhitespace()// 根據當前字符判斷Token類型switch l.ch {case '=': // 識別等號return Token{Type: EQUAL, Literal: "="}case ',': // 識別逗號return Token{Type: COMMA, Literal: ","}case '>': // 識別大于號return Token{Type: GREATER, Literal: ">"}// ... 其他特殊字符處理default:if isLetter(l.ch) { // 識別關鍵字或標識符literal := l.readIdentifier()tokenType := lookupKeyword(literal) // 判斷是否是關鍵字return Token{Type: tokenType, Literal: literal}} else if isDigit(l.ch) { // 識別數字return Token{Type: NUMBER, Literal: l.readNumber()}}}
}
這段代碼展示了詞法分析器如何一個字符一個字符地讀取SQL文本,并根據字符類型創建不同的標記。
2. 語法分析:構建有意義的結構
語法分析器接收標記流,根據SQL語法規則構建語句結構:
語法分析的入口代碼
語法分析器根據第一個標記判斷SQL語句類型,并分派給相應的處理函數:
// 語法分析入口 - 判斷SQL語句類型
func (p *Parser) Parse() (ast.Statement, error) {switch p.currToken.Type {case lexer.SELECT: // 處理SELECT語句return p.parseSelectStatement()case lexer.INSERT: // 處理INSERT語句return p.parseInsertStatement()case lexer.UPDATE: // 處理UPDATE語句return p.parseUpdateStatement()case lexer.DELETE: // 處理DELETE語句return p.parseDeleteStatement()case lexer.CREATE: // 處理CREATE語句if p.peekTokenIs(lexer.TABLE) {return p.parseCreateTableStatement()}return nil, fmt.Errorf("不支持的CREATE語句")case lexer.DROP: // 處理DROP語句if p.peekTokenIs(lexer.TABLE) {return p.parseDropTableStatement()}return nil, fmt.Errorf("不支持的DROP語句")default:return nil, fmt.Errorf("不支持的SQL語句類型: %s", p.currToken.Literal)}
}
SELECT語句的解析流程
解析SELECT語句的代碼展示了如何逐步構建語句結構:
// SELECT語句解析 - 關鍵步驟
func (p *Parser) parseSelectStatement() (*ast.SelectStatement, error) {stmt := &ast.SelectStatement{} // 初始化空的SELECT語句節點p.nextToken() // 跳過SELECT關鍵字// 1. 解析列名列表columns, err := p.parseExpressionList(lexer.COMMA)if err != nil {return nil, err}stmt.Columns = columns // 設置選擇的列// 2. 解析FROM子句和表名if !p.expectPeek(lexer.FROM) { // 期望下一個標記是FROMreturn nil, fmt.Errorf("期望FROM,但得到%s", p.peekToken.Literal)}p.nextToken() // 跳過FROMif !p.currTokenIs(lexer.IDENTIFIER) { // 期望當前標記是標識符(表名)return nil, fmt.Errorf("期望表名,但得到%s", p.currToken.Literal)}stmt.TableName = p.currToken.Literal // 設置表名// 3. 解析WHERE子句(可選)p.nextToken()if p.currTokenIs(lexer.WHERE) {p.nextToken() // 跳過WHEREexpr, err := p.parseExpression(LOWEST) // 解析條件表達式if err != nil {return nil, err}stmt.Where = expr // 設置WHERE條件}return stmt, nil // 返回完整的SELECT語句節點
}
3. 抽象語法樹(AST):SQL的結構化表示
抽象語法樹是SQL語句的樹狀結構表示,每個節點代表語句的一個組成部分。
AST的基本節點類型
// AST核心接口定義
type Node interface { // 所有AST節點的基礎接口TokenLiteral() string // 返回節點對應的詞法單元字面值String() string // 返回節點的字符串表示
}type Statement interface { // SQL語句節點NodestatementNode() // 標記方法,表明這是語句節點
}type Expression interface { // 表達式節點NodeexpressionNode() // 標記方法,表明這是表達式節點
}
示例:SELECT語句的AST圖解
對于 SELECT id, name FROM users WHERE age > 18
:
AST節點的定義
AST節點類型展示了如何用代碼表示SQL的各個組成部分:
// SELECT語句節點定義
type SelectStatement struct {Columns []Expression // 選擇的列列表,如 id, nameTableName string // 查詢的表名,如 usersTableAlias string // 表別名,如 uWhere Expression // WHERE條件,如 age > 18
}// 二元表達式節點(用于WHERE條件等)
type BinaryExpression struct {Left Expression // 左操作數Operator TokenType // 操作符(如 >, =, AND)Right Expression // 右操作數
}// 標識符節點(列名、表名等)
type Identifier struct {Value string // 標識符的名稱,如 "id", "users"
}
小結與實際應用
SQL解析器看似復雜,但每個部分都有明確的功能:
- 詞法分析:將SQL文本拆分成標記
- 語法分析:將標記組織成有意義的語句結構
- 表達式解析:處理運算符優先級和嵌套表達式
- 抽象語法樹:提供SQL的結構化表示
這些組件共同工作,將人類可讀的SQL轉換為數據庫可處理的結構,為執行引擎、優化器和查詢計劃生成器提供基礎。
在后續章節中,我們將進一步探索如何基于這個解析器實現更多高級功能,包括嵌套查詢,join方法等等。