首先声明,我不是标题党,我真的是用5000行左右的JS实现了一个轻量级的关系型数据库JSDB,核心是一个SQL编译器,支持增删改查。
源代码放到github上了:https://github.com/lavezhang/jsdb
如果你需要修改程序引入新的特性,请严格遵守GPL协议。
如果转发此文,请注明来源。
体验页面
前言
工作太忙,好久没写这种长文章了,难得今年国庆超长,又不便外出,这才有时间“不务正业”。
为什么要用一周的时间写这么个玩意儿?看起来也没什么用处,毕竟,没有哪个系统需要在浏览器中跑一个关系型数据库。
如果要搞一个"年度最无用项目"的颁奖,估计JSDB榜上有名。
我一直有一个梦想,要研发一款咱们中国人自己的列式存储分布式数据库!(此处应有掌声^_^)
古人讲,不积跬步无以至千里,JSDB就算探索数据库自研的一个开端吧。
为什么用TypeScript?因为coding效率非常高,跟Python差不多,而且有浏览器就能运行,非常方便,很适合做技术预研,正式开发时再改为C或Rust。
如文章开头所言,JSDB的核心是一个SQL编译器,准确地说,是解释器。学习过《编译原理》的同学,对这个不会陌生。
解释器也是属于编译器的范畴,所以,后面仍然会沿用“SQL编译器”的说法。
概述
按照执行顺序,JSDB的代码由四个部分构成:
1、词法分析,得到 token 列表。参见GitHub源代码,SqlLexer.ts 文件,基于状态机实现,详见 lex_state_flow.xlsx 文件。
2、语法语义分析,得到抽象语法数。参见 SqlParser.ts 文件,自上而下解析,这是行数最多的一个文件。
3、对抽象语法树的执行。参见SqlDatabase.ts文件,以及ast目录下的几十个语法节点的compute(ctx)方法。
4、单元测试和应用范例。test目录和test.html文件里运行着所有的单元测试,index.html文件就是文章开头的体验页面,语法高亮功能基于第三方组件codemirror实现,在 static/codemirror 目录里。
JSDB确实是一个关系型数据库,参照SQL92标准实现,但它并不完整,只实现了最核心的一小部分功能,可以满足日常基本需求。主要特性有:
01、create table 语句
02、insert 语句
03、update 语句
04、delete 语句
05、select 语句,含:distinct / from / join / left join / where / group by / having / order by / limit
06、算数运算符:+、-、*、/、%
07、关系运算符:>、>=、<、<=、=、<>
08、条件运算符:and、or、not
09、其它操作符:like、not like、is null、is not null、between
10、动态占位符:?
11、标准函数,目前只实现了:ifnull、len、substr、substring、instr、concat。
如果需要增加新的标准函数,可以在SqlContext类的构造函数中实现,所有的标准函数都注册到SqlContext.standardFunctions字段中。
尚未实现的重要特性有:
1、with / sub query / exists / alter / truncate 等
2、数据存储。一直在内存中运行,大家可以修改程序,写入浏览器localStorage中。
3、事务。这个需要事务日志来实现,以后再搞,不过在内存中模拟一个,问题也不大。
4、并发锁。JS是单线程,没有真正的并发,有了一个不用实现它的好理由。
5、其它功能。详见大学时的《数据库原理》。
如果大家多多点赞,我就把它实现得更加完整。^_^
本文针对编译器和数据库的入门读者,写了很多小白的内容,高手请飘过。
第一章 词法分析
关于词法分析,程序本身并不难。无论何种编程语言,它的词法分析模块一般都不超过300行,有些甚至只有几十行。
很多人喜欢用 lex/yacc/antr 之类的工具来自动生成,我不喜欢,我就是喜欢手撸的感觉。
词法分析就是要识别源代码中的一个个token,一般包括:关键字、标识符、字符串、数值、布尔值、空值、运算符、操作符、分隔符。
例如,一条SQL语句:
select name, (total_score / 4) as avg_score from student where id = '010123'