自己動手寫數據庫系統:實現一個小型SQL解釋器(中)

我們接上節內容繼續完成SQL解釋器的代碼解析工作。下面我們實現對update語句的解析,其語法如下:
UpdateCmd -> INSERT | DELETE | MODIFY | CREATE
Create -> CreateTable | CreateView | CreateIndex
Insert -> INSERT INTO ID LEFT_PARAS FieldList RIGHT_PARAS VALUES LEFT_PARS ConstList RIGHT_PARAS
FieldList -> Field ( COMMA FieldList)?
ConstList -> Constant ( COMMA ConstList)?
Delete -> DELETE FROM ID [WHERE Predicate)?
Modify -> UPDATE ID SET Field ASSIGN_OPERATOR Expression (WHERE Predicate)?
CreateTable -> CREATE TABLE ID (FieldDefs)?
FieldDefs -> FieldDef ( COMMA FieldDefs)?
FieldDef -> ID TypeDef
TypeDef -> INT | VARCHAT LEFT_PARAS NUM RIGHT_PARAS
CreateView -> CREATE VIEW ID AS Query
CreateIndex -> CREATE INDEX ID ON ID LEFT_PARAS Field RIGHT_PARAS

我們對上面的語法做一些基本說明:
UpdateCmd -> INSERT | DELETE | MODIFY | CREATE
這句語法表明SQL語言中用于更新表的語句一定由insert, delete, modify , create等幾個命令開始。insert 語句由關鍵字insert開始,然后跟著insert into兩個關鍵字,接著是左括號,跟著是由列名(column)組成的字符串,他們之間由逗號隔開,然后跟著右括號,接著是關鍵字VALUES,然后是左括號,接著是一系列常量和逗號組成的序列,最后以又括號結尾,其他語法大家可以參照SQL相關命令來理解,下面我們看看代碼的實現,繼續在parser.go中添加如下代碼:

func (p *SQLParser) UpdateCmd() interface{} {tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag == lexer.INSERT {p.sqlLexer.ReverseScan()return p.Insert()} else if tok.Tag == lexer.DELETE {p.sqlLexer.ReverseScan()return p.Delete()} else if tok.Tag == lexer.UPDATE {p.sqlLexer.ReverseScan()return p.Update()} else {p.sqlLexer.ReverseScan()return p.Create()}
}func (p *SQLParser) Create() interface{} {tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != lexer.CREATE {panic("token is not create")}tok, err = p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag == lexer.TABLE {return p.CreateTable()} else if tok.Tag == lexer.VIEW {return p.CreateView()} else {return p.CreateIndex()}
}func (p *SQLParser) CreateView() interface{} {return nil
}func (p *SQLParser) CreateIndex() interface{} {return nil
}func (p *SQLParser) CreateTable() interface{} {tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != lexer.ID {panic("token should be ID for table name")}tblName := p.sqlLexer.Lexemetok, err = p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != lexer.LEFT_BRACKET {panic("missing left bracket")}sch := p.FieldDefs()tok, err = p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != lexer.RIGHT_BRACKET {panic("missing right bracket")}return NewCreateTableData(tblName, sch)
}func (p *SQLParser) FieldDefs() *record_manager.Schema {schema := p.FieldDef()tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag == lexer.COMMA {schema2 := p.FieldDefs()schema.AddAll(schema2)} else {p.sqlLexer.ReverseScan()}return schema
}func (p *SQLParser) FieldDef() *record_manager.Schema {_, fldName := p.Field()return p.FieldType(fldName)
}func (p *SQLParser) FieldType(fldName string) *record_manager.Schema {schema := record_manager.NewSchema()tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag == lexer.INT {schema.AddIntField(fldName)} else if tok.Tag == lexer.VARCHAR {tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != lexer.LEFT_BRACKET {panic("missing left bracket")}tok, err = p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != lexer.NUM {panic("it is not a number for varchar")}num := p.sqlLexer.LexemefldLen, err := strconv.Atoi(num)if err != nil {panic(err)}schema.AddStringField(fldName, fldLen)tok, err = p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != lexer.RIGHT_BRACKET {panic("missing right bracket")}}return schema
}

在上面代碼中我們需要定義一個CreateTableData結構,因此增加一個create_data.go文件,添加代碼如下:

package parserimport ("record_manager"
)type CreateTableData struct {tblName stringsch     *record_manager.Schema
}func NewCreateTableData(tblName string, sch *record_manager.Schema) *CreateTableData {return &CreateTableData{tblName: tblName,sch:     sch,}
}func (c *CreateTableData) TableName() string {return c.tblName
}func (c *CreateTableData) NewSchema() *record_manager.Schema {return c.sch
}

最后我們在main.go中添加代碼,調用上面的代碼實現:

package main//import (
//	bmg "buffer_manager"
//	fm "file_manager"
//	"fmt"
//	lm "log_manager"
//	"math/rand"
//	mm "metadata_management"
//	record_mgr "record_manager"
//	"tx"
//)import ("parser"
)func main() {sql := "create table person (PersonID int, LastName varchar(255), FirstName varchar(255)," +"Address varchar(255), City varchar(255) )"sqlParser := parser.NewSQLParser(sql)sqlParser.UpdateCmd()}

在main中,我們定義了一個create table的sql語句,然后調用UpdateCmd接口實現語法解析,大家可以在b站搜索”coding迪斯尼“,查看代碼的調試演示視頻,由于上面語法解析的邏輯稍微復雜和繁瑣,因此通過視頻來跟蹤代碼的單步調試過程才能更簡單省力的理解實現邏輯。

下面我們看看insert語句的解析實現,在parser.go中添加代碼如下:

func (p *SQLParser) checkWordTag(wordTag lexer.Tag) {tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != wordTag {panic("token is not match")}
}func (p *SQLParser) isMatchTag(wordTag lexer.Tag) bool {tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag == wordTag {return true} else {p.sqlLexer.ReverseScan()return false}
}func (p *SQLParser) fieldList() []string {L := make([]string, 0)_, field := p.Field()L = append(L, field)if p.isMatchTag(lexer.COMMA) {fields := p.fieldList()L = append(L, fields...)}return L
}func (p *SQLParser) constList() []*query.Constant {L := make([]*query.Constant, 0)L = append(L, p.Constant())if p.isMatchTag(lexer.COMMA) {consts := p.constList()L = append(L, consts...)}return L
}func (p *SQLParser) Insert() interface{} {/*根據語法規則:Insert -> INSERT INTO ID LEFT_PARAS FieldList RIGHT_PARAS VALUES LEFT_PARS ConstList RIGHT_PARAS我們首先要匹配四個關鍵字,分別為insert, into, id, 左括號,然后就是一系列由逗號隔開的field,接著就是右括號,然后是關鍵字values接著是常量序列,最后以右括號結尾*/p.checkWordTag(lexer.INSERT)p.checkWordTag(lexer.INTO)p.checkWordTag(lexer.ID)tblName := p.sqlLexer.Lexemep.checkWordTag(lexer.LEFT_BRACKET)flds := p.fieldList()p.checkWordTag(lexer.RIGHT_BRACKET)p.checkWordTag(lexer.VALUES)p.checkWordTag(lexer.LEFT_BRACKET)vals := p.constList()p.checkWordTag(lexer.RIGHT_BRACKET)return NewInsertData(tblName, flds, vals)
}

我們調用上面代碼測試一下解析效果:

func main() {sql := "INSERT INTO Customers (CustomerName, ContactName, Address, City, PostalCode, Country) " +"VALUES (\"Cardinal\", \"Tom B. Erichsen\", \"Skagen 21\", \"Stavanger\", 4006, \"Norway\")"sqlParser := parser.NewSQLParser(sql)sqlParser.UpdateCmd()} 

請大家在b站搜索coding迪斯尼,通過視頻調試演示的方式能更直白和有效的了解代碼邏輯。接下來我們看看 create 命令如何創建 view 和 index 兩個對象,首先我們看看 view 的創建,根據 create view 的語法:

CreateView -> CREATE VIEW ID AS QUERY

首先我們要判斷語句的前兩個 token 是否對應 關鍵字 CREATE, VIEW,然后接著的token 必須是 ID類型,然后跟著關鍵字 AS,最后我們調用 QUERY 對應的解析規則來解析后面的字符串,我們看看代碼實現,在 parser.go 中添加如下代碼:

func (p *SQLParser) CreateView() interface{} {p.checkWordTag(lexer.ID)viewName := p.sqlLexer.Lexemep.checkWordTag(lexer.AS)qd := p.Query()vd := NewViewData(viewName, qd)vdDef := fmt.Sprintf("vd def: %s", vd.ToString())fmt.Println(vdDef)return vd
}

然后新增文件 create_view.go,添加如下代碼:

package parserimport "fmt"type ViewData struct {viewName  stringqueryData *QueryData
}func NewViewData(viewName string, qd *QueryData) *ViewData {return &ViewData{viewName:  viewName,queryData: qd,}
}func (v *ViewData) ViewName() string {return v.viewName
}func (v *ViewData) ViewDef() string {return v.queryData.ToString()
}func (v *ViewData) ToString() string {s := fmt.Sprintf("view name %s, viewe def: %s", v.viewName, v.ViewDef())return s
}

最后我們在 main.go 中添加如下測試代碼:

func main() {//sql := "create table person (PersonID int, LastName varchar(255), FirstName varchar(255)," +//	"Address varchar(255), City varchar(255) )"sql := "create view Customer as select CustomerName, ContactName from customers where country=\"China\""sqlParser := parser.NewSQLParser(sql)sqlParser.UpdateCmd()}

上面代碼運行后結果如下:

vd def: view name Customer, viewe def: select CustomerName, ContactName, from customers, where  and country=China

更詳細的內容請在 b 站搜索 coding 迪斯尼。下面我們看看索引創建的語法解析,其對應的語法為:

CreateIndex -> CREATE INDEX ID ON ID LEFT_BRACKET Field RIGHT_BRACKET

從語法規則可以看出,在解析時我們需要判斷語句必須以 CREATE INDEX 這兩個關鍵字開頭,然后接著的字符串要能滿足 ID 的定義,然后又需要跟著關鍵字 ON, 然后跟著的字符串要滿足 ID 定義,接下來讀入的字符必須是左括號,然后接著的內容要滿足 Field 的定義,最后要以右括號結尾,我們看看代碼實現在 parser.go 中添加如下代碼:

func (p *SQLParser) Create() interface{} {tok, err := p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag != lexer.CREATE {panic("token is not create")}tok, err = p.sqlLexer.Scan()if err != nil {panic(err)}if tok.Tag == lexer.TABLE {return p.CreateTable()} else if tok.Tag == lexer.VIEW {return p.CreateView()} else if tok.Tag == lexer.INDEX {return p.CreateIndex()}panic("sql string with create should not end here")
}func (p *SQLParser) CreateIndex() interface{} {p.checkWordTag(lexer.ID)idexName := p.sqlLexer.Lexemep.checkWordTag(lexer.ON)p.checkWordTag(lexer.ID)tableName := p.sqlLexer.Lexemep.checkWordTag(lexer.LEFT_BRACKET)_, fldName := p.Field()p.checkWordTag(lexer.RIGHT_BRACKET)idxData := NewIndexData(idexName, tableName, fldName)fmt.Printf("create index result: %s", idxData.ToString())return idxData
}

新建 create_index_data.go 文件,在里面添加代碼如下:

package parserimport "fmt"type IndexData struct {idxName stringtblName stringfldName string
}func NewIndexData(idxName string, tblName string, fldName string) *IndexData {return &IndexData{idxName: idxName,tblName: tblName,fldName: fldName,}
}func (i *IndexData) IdexName() string {return i.idxName
}func (i *IndexData) tableName() string {return i.tblName
}func (i *IndexData) fieldName() string {return i.fldName
}func (i *IndexData) ToString() string {str := fmt.Sprintf("index name: %s, table name: %s, field name: %s", i.idxName, i.tblName, i.fldName)return str
}

在 main.go 中我們使用 sql 語句中的 create index 語句測試一下上面代碼實現:

func main() {//sql := "create table person (PersonID int, LastName varchar(255), FirstName varchar(255)," +//	"Address varchar(255), City varchar(255) )"sql := "create index idxLastName on persons (lastname)"sqlParser := parser.NewSQLParser(sql)sqlParser.UpdateCmd()}

上面代碼運行后所得結果如下:

create index result: index name: idxLastName, table name: persons, field name: lastname

到這里所有有關 create 語句的解析就基本完成,更多的調試演示和代碼邏輯的講解,請在 b 站搜索 coding 迪斯尼

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/40102.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/40102.shtml
英文地址,請注明出處:http://en.pswp.cn/news/40102.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

后端項目打包上傳服務器記錄

后端項目打包上傳服務器記錄 文章目錄 后端項目打包上傳服務器記錄1、項目打包2、jar包上傳服務器 本文記錄打包一個后端項目,上傳公司服務器的過程。 1、項目打包 通過IDEA的插件進行打包: 打成一個jar包,jar包的位置在控制臺可以看到。 2、…

ssm蜀都天香酒樓網站設計與實現

ssm蜀都天香酒樓的網站設計與實現028 開發工具:idea 數據庫mysql5.7 數據庫鏈接工具:navcat,小海豚等 技術:ssm 摘要 近年來,信息化管理行業的不斷興起,使得人們的日常生活越來越離不開計算機和互聯網技術。首…

機器學習基礎(六)

貝葉斯分析 介紹 “貝葉斯”是指托馬斯貝葉斯(1702–1761),他證明了一個特例,也就是現在的貝葉斯定理的特例。 貝葉斯定理(英語:Bayes theorem)是概率論中的一個定理,描述在已知一些條件下,某事件的發生概率。比如,如果已知某種健康問題與壽命有關,使用貝葉斯定理則…

selenium語法進階+常用API

目錄 瀏覽器操作 瀏覽器回退,前進 與刷新 瀏覽器窗口設置大小 瀏覽器設置寬高 瀏覽器窗口最大化 瀏覽器控制滾動條 信息打印 打印頁面的標題和當前頁面的URL 定位一組元素 鼠標和鍵盤事件 鍵盤 鼠標 下拉框操作 通過索引定位(se…

【BASH】回顧與知識點梳理(三十二)

【BASH】回顧與知識點梳理 三十二 三十二. SELinux 初探32.1 什么是 SELinux當初設計的目標:避免資源的誤用傳統的文件權限與賬號關系:自主式訪問控制, DAC以政策規則訂定特定進程讀取特定文件:委任式訪問控制, MAC 32.2 SELinux 的運作模式安…

安科瑞變電所運維平臺在電力系統中應用分析

摘要:現代居民生活、工作對電力資源的需求量相對較多,給我國的電力產業帶來了良好的發展機遇與挑戰。探索電力系統基本構成, 將變電運維安全管理以及相應的設備維護工作系統性開展,能夠根據項目實踐工作要求,將滿足要求…

C語言暑假刷題沖刺篇——day2

目錄 一、選擇題 二、編程題 🎈個人主頁:庫庫的里昂 🎐CSDN新晉作者 🎉歡迎 👍點贊?評論?收藏?收錄專欄:C語言每日一練 ?其他專欄:代碼小游戲C語言初階🤝希望作者的文章能對你…

最小生成樹,prim算法

Prim算法和Kruskal算法都是用于解決最小生成樹問題的經典算法,它們在不同情況下有不同的適用性和特點。 Prim算法: Prim算法是一種貪心算法,用于構建一個無向圖的最小生成樹。算法從一個初始節點開始,逐步添加與當前樹連接且具有…

【自動電壓調節器】無功功率控制的終端電壓控制研究(Simulink)

💥💥💞💞歡迎來到本博客????💥💥 🏆博主優勢:🌞🌞🌞博客內容盡量做到思維縝密,邏輯清晰,為了方便讀者。 ??座右銘&a…

小白的Node.js學習筆記大全---不定期更新

let、const、var的區別 (1)塊級作用域: 塊作用域由 { }包括,let和const具有塊級作用域,var不存在塊級作用域。塊級作用域解決了ES5中的兩個問題: 內層變量可能覆蓋外層變量 用來計數的循環變量泄露為全局…

【加強管理】《別輸在不懂管理上》學習記錄,黃金41條

成功有時是很難效法的,但失敗是可以避免的,從失敗中吸取經驗和教訓才是管理者的必修課。釋義: 圖形含義🌲一級重要🍀二級重要🌿三級主要🍁存在問題🌼解決辦法 1 不能從頭管到腳 不…

【討論】視頻監控集中存儲方案如何做?

視頻監控集中存儲是指將多個視頻監控攝像頭所捕捉到的視頻信號集中存儲于一個中央設備,這個中央設備可以是服務器、網絡存儲設備或其他專用設備。通過集中存儲,可以避免因為存儲設備分散而導致的管理不便和難以有效地管理和檢索視頻數據,同時…

RTT(RT-Thread)ADC設備(RTT保姆級介紹)

目錄 ADC設備 前言 ADC相關參數說明 訪問ADC設備 配置ADC設備 ADC實例 硬件設計 軟件設計 ADC設備 前言 ADC(Analog-to-Digital Converter) 指模數轉換器。是指將連續變化的模擬信號轉換為離散的數字信號的器件。 對于ADC的詳細介紹和在STM32中的裸機應用可參考以下…

pandas數據分析38——數據框表格拓展以及縮回對齊

案例背景 需求是這個樣的: 把這個表格進行拓展。 代碼實現: df pd.DataFrame(np.array([[1, 2, 3,4], [a,b, c,d], [小明,小紅, 小馬,小天]])) df 方法一:自定義函數: def expand_dataframe(df):m, n df.shapenew_df pd.Dat…

linux系統中設置服務開機自啟動

1:背景描述 最近根據工作需要,需要服務實現開機自啟動的效果,因為平時只使用過nohup的后臺掛起操作,很少接觸開機,鏡像裝機服務自啟動的功能,因此,這里簡單記錄一下。 注意,開機自…

解鎖數據潛力:信息抽取、數據增強與UIE的完美融合

解鎖數據潛力:信息抽取、數據增強與UIE的完美融合 1.信息抽取(Information Extraction) 1.1 IE簡介 信息抽取是 NLP 任務中非常常見的一種任務,其目的在于從一段自然文本中提取出我們想要的關鍵信息結構。 舉例來講&#xff0…

從NLP到聊天機器人

一、說明 今天,當打電話給銀行或其他公司時,聽到電話另一端的機器人向你打招呼是很常見的:“你好,我是你的數字助理。請問你的問題。是的,機器人現在不僅可以說人類語言,還可以用人類語言與用戶互動。這是由…

windows權限維持—黃金白銀票據隱藏用戶遠控RustDeskGotoHttp

windows權限維持—黃金白銀票據&隱藏用戶&遠控&RustDesk&GotoHttp 1. 前置1.1. 初始問題1.1.1. 解決辦法 2. 隱藏用戶2.1. 工具原理2.2. 案例操作2.2.1. 單機添加用戶2.2.1.1. 工具添加用戶2.2.1.2. 工具查看隱藏用戶2.2.1.3. 本地查看隱藏用戶 2.2.2. 域內添加…

CentOS系統環境搭建(二)——Centos7設置時間為網絡時間

centos系統環境搭建專欄🔗點擊跳轉 Centos7設置時間為網絡時間 安裝ntpdate工具 yum -y install ntp ntpdate關閉ntpd service ntpd stop設置系統時間與網絡時間同步 ntpdate 0.asia.pool.ntp.org將系統時間寫入硬件時間 hwclock --systohc查看和設置時區 使…

NeuralNLP-NeuralClassifier的使用記錄(二),訓練預測自己的【中文文本多分類】

NeuralNLP-NeuralClassifier的使用記錄,訓練預測自己的【中文文本多分類】 數據準備: ? 與英文的訓練預測一致,都使用相同的數據格式,將數據通過代碼處理為JSON格式,以下是我使用的一種,不同的原數據情況…