SQL 解析與執行流程
一、前言
在先前的技術部落格中,我們已經詳細介紹過資料庫的 parser 模組與執行流程:使用者輸入的 SQL 語句透過詞法解析器生成 token,再透過語法分析器生成抽象語法樹(AST),經過 AST 生成對應的 planNode,最後執行 planNode。本期部落格我們將以新增語法為例,重點介紹一條 SQL 語句需要歷經的流程,以及如何自定義 SQL 語句和功能。
二、新增 SQL 語法
KaiwuDB 是透過 goyacc 解析 sql.y 中的程式碼生成 AST,而我們想要新增一個語法,則需要在 sql.y 中新增對應的關鍵字以及對應的語法解析規則,最後在程式碼中新增對應的語法節點以及功能實現。我們以建立時序資料庫的語法 CREATE TS DATABASE xxx 為例,講解如何新增新 SQL 語句,以及該語句的執行過程。
1. 定義新的關鍵字
詞法分析器是透過一個個關鍵字解析整條語句,所以第一步我們需要將整條語句涉及到的所有關鍵字定義完畢。
搜尋 pkg/sql/parser/sql.y 檔案,在檔案中找到 %token(這個代表著定義的關鍵字),我們可以在這裡看到已經有 CREATE 和 DATABASE 關鍵字,所以需要新增一個 TS 關鍵字。
Go
......
%token <str> TS
......
加好關鍵字後,我們還需定義其是保留關鍵字或是非保留關鍵字,非保留關鍵字可以作為識別符號使用並在使用時需要加上雙引號。所以,我們需要將 TS 加到非保留關鍵字裡中。
Go
unreserved_keyword:
......
| TS
......
新增完畢後,詞法分析器即可解析整條語句的所有關鍵字,接下來我們需要定義一個新的語法規則,使得語法解析器可以處理這條新的語句。
2. 新增新的語法規則
新的語法首先要給它定義一個型別。在 sql.y 中,一條 SQL 語句的型別都是 %type <tree.Statement> 。
Go
......
%type <tree.Statement> create_ts_database_stmt
......
然後將該語法放到語法 case 列表中,這條語法屬於 create_ddl_stmt 的一部分,我們將其放在 create_ddl_stmt 下方即可。
Go
create_ddl_stmt:
......
| create_ts_database_stmt // EXTEND WITH HELP: CREATE TS_DATABASE
......
接下來需要為該語法新增對應的語法規則和幫助資訊:
Go
// %Help: CREATE TS_DATABASE - create a new ts database
// %Category: DDL
// %Text: CREATE TS_DATABASE <name>
create_ts_database_stmt:
CREATE TS DATABASE database_name{
......
}
| CREATE TS DATABASE error // SHOW HELP: CREATE DATABASE
到這裡,整個 parser 部分就可以識別這條新的語法並提示相應資訊,但現在還沒有新增具體的語法操作,所以整個語法還不能完全執行。
3. 新增執行語法操作
在解析器可以成功解析語法後,我們需要新增對應的語義,來讓整條 SQL 語句執行。而這一步就是生成一個抽象語法樹(AST),將語句資訊從 parser 階段傳到執行階段。
在上文中我們將 create_ts_database_stmt 新增為 tree.Statement 型別,所以我們還需要實現 tree.Statement 型別的介面,其後還需要實現以下幾個方法:
- fmt.Stringer
- NodeFormatter
- StatementType()
- StatementTag()
- StatOp()
- StatTargetType()
為此,所以首先要定義一個結構體,可以作為整條語句解析的返回值,用以實現上述的幾種方法。對於我們想要新增的語句,可以複用原來的 CreateDatabase 結構體並在其中新增一個欄位 EngineType 用來代表是否為時序資料庫。該結構體已經實現了以上幾種方法,但我們新增了新的語法,所以要在 Format 方法中將新的語法 Format 方式新增進去。
Go
// Format implements the NodeFormatter interface.
func (node *CreateDatabase) Format(ctx *FmtCtx) {
ctx.WriteString("CREATE ")
if node.EngineType == EngineTypeTimeseries {
ctx.WriteString("TS ")
}
......
}
接下來我們將 parser 部分補全,讓它返回一個對應的 CreateDatabase 節點。
Go
// %Help: CREATE TS_DATABASE - create a new ts database
// %Category: DDL
// %Text: CREATE TS_DATABASE <name>
create_ts_database_stmt:
CREATE TS DATABASE database_name{
$$.val = &tree.CreateDatabase{
Name: tree.Name($4),
EngineType: 1,
}
}
| CREATE TS DATABASE error // SHOW HELP: CREATE DATABASE
至此,整條語句已經可以成功識別並執行。但由於我們是複用已有的 CreateDatabase 結構,所以執行流程還需要對應的修改。如果是新增一個新的結構體,我們需要在 plan.go 中新加一個 planNode,用於生成執行計劃,
Go
var _ planNode = &createDatabaseNode{}
planNode 也有以下幾個介面需要實現:
- startExec(params runParams)
- Next(params runParams)
- Values()
- Close(ctx context.Context)
在 buildOpaque 新增一個 case,用於執行時識別 AST 結構,生成對應的 planNode。
Go
......
switch n := stmt.(type) {
case *tree.CreateDatabase:
plan, err = p.CreateDatabase(ctx, n)
......
目前我們已有對應的 createDatabaseNode ,所以無需再新增。而在 CreateDatabase 中需要我們將 AST 轉成 planNode,並需要做出語義上的檢查與限制。
最後一步就是要定義如何執行整條語句,在 startExec 方法中,透過構建好的 planNode 去實現我們所需的語法功能。
Go
func (n *createDatabaseNode) startExec(params runParams) error {
......
}
至此,新增的該語法功能已實現。
來自 “ ITPUB部落格 ” ,連結:https://blog.itpub.net/70027415/viewspace-3003124/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- mysql的sql語句執行流程MySql
- 原始碼解析MyBatis Sharding-Jdbc SQL語句執行流程詳解(文末有流程圖)原始碼MyBatisJDBCSQL流程圖
- 深入解析 DolphinScheduler 任務排程、拆分與執行全流程
- Spark學習(一)——執行模式與執行流程Spark模式
- 解析MySQL基礎架構及一條SQL語句的執行流程和流轉MySql架構
- MySQL原理簡介—1.SQL的執行流程MySql
- Mybatis原始碼解析之執行SQL語句MyBatis原始碼SQL
- 死磕 java執行緒系列之執行緒池深入解析——普通任務執行流程Java執行緒
- oracle常用後臺程序及sql語句執行流程OracleSQL
- 死磕 java執行緒系列之執行緒池深入解析——未來任務執行流程Java執行緒
- GaussDB SQL查詢語句執行過程解析SQL
- SQL 查詢語句的執行順序解析SQL
- MyBatis執行流程MyBatis
- HA執行流程
- MapReduce執行流程
- Mysql 執行流程MySql
- SpringMVC執行流程SpringMVC
- MyBatis SQL執行MyBatisSQL
- PHP執行sqlPHPSQL
- [ORACLE] SQL執行OracleSQL
- 「MySQL」 MySQL執行流程MySql
- javaWeb的執行流程JavaWeb
- MapReduce程式執行流程
- Dapr Outbox 執行流程
- Ansible playbook 執行流程
- for 迴圈執行流程
- SQL 執行 - 執行器最佳化SQL
- [原始碼分析] 帶你梳理 Flink SQL / Table API內部執行流程原始碼SQLAPI
- Oracle - 執行過的SQL、正在執行的SQL、消耗資源最多的SQLOracleSQL
- Spark SQL原始碼解析(五)SparkPlan準備和執行階段SparkSQL原始碼
- 【SQL】Oracle sql語句 minus函式執行效率與join對比SQLOracle函式
- peewee 執行原生 sqlSQL
- Sql執行順序SQL
- Activiti的流程例項【ProcessInstance】與執行例項【Execution】
- Docker從入門到精通(三)——概念與執行流程Docker
- oracle查詢sql執行耗時、執行時間、sql_idOracleSQL
- 深入理解執行緒池的執行流程執行緒
- 演算法相同,純SQL在資料庫內執行 與Golang呼叫database. SQL執行的速率?演算法SQL資料庫GolangDatabase