SQL作为一种DSL(domain-specific language),可以理解为数据库的一种“编程语言”,与C、Java一样,真正执行这些文本字符串,需要先进行词法、语法分析,然后进行语义分析,编译器或者解释器才能将这些字符串转化为一系列确定的操作指令。
SQL解析引擎的作用就是词法、语法分析,将SQL解析成一颗抽象语法树AST,从而方便后续直接通过高级编程语言进行读取。当然与C、Java等编程语言相比,SQL相对来说简单很多,没有作用域、类、复杂的分支判断等。
抽象语法树 (Abstract Syntax Tree),简称 AST,它是源代码语法结构的一种抽象表示。它以树状的形式表现编程语言的语法结构,树上的每个节点都表示源代码中的一种结构。
抽象语法树
ShardingSphere的解析引擎经过了三个版本的演化:
第一代SQL解析器:
sharding-jdbc在1.4.x 之前的版本使用的alibaba的druid(https://github.com/alibaba/druid),,druid) druid包含了一个手写的SQL解析器,优点是速度快,缺点是扩展不是很方便,只能通过修改源码。
第二代 SQL 解析器
从 1.5.x 版本开始,ShardingSphere 重新实现了一个简化版 SQL 解析引擎。因为ShardingSphere 并不需要像druid那样将 SQL 转为完整的AST,所以采用对 SQL 半理解的方式,仅提炼数据分片需要关注的上下文,在满足需要的前提下,SQL 解析的性能和兼容性得到了进一步的提高。
第三代 SQL 解析器
则从 3.0.x 版本开始,ShardingSphere统一将SQL解析器换成了基于antlr4实现,目的是为了更方便、更完整的支持SQL,例如对于复杂的表达式、递归、子查询等语句,因为后期ShardingSphere的定位已不仅仅是数据分片功能。
antlr4通过.g4文件定义解析词法和语法规则,ShardingSphere中将词法和语法文件进行了分离定义,例如mysql对应的g4文件,词法规则文件包括Alphabet.g4、Comments.g4、Keyword.g4、Literals.g4、MySQLKeyword.g4、Symbol.g4,语法规则文件有:BaseRule.g4、DALStatement.g4、DCLStatement.g4、DDLStatement.g4、DMLStatement.g4、RLStatement.g4、TCLStatement.g4,每个文件分别定义了一类关键字或者SQL类型规则。
Antlr4的g4文件
Keyword.g4它是一个纯词法规则文件,定义了SQL中通用的关键字
lexer grammar Keyword;
import Alphabet;
/* 跳过spaces, tabs, newlines */
WS
: [ \t\r\n] + ->skip
;
SELECT
: S E L E C T
;
INSERT
: I N S E R T
;
UPDATE
: U P D A T E
;
DELETE
: D E L E T E
;
CREATE
: C R E A T E
;
ALTER
: A L T E R
;
DROP
: D R O P
;
…
Symbol.g4定义了SQL中对应的计算、谓词运算符以及括号分号等标识符。
lexer grammar Symbol;
AND_: '&&';
OR_: '||';
NOT_: '!';
TILDE_: '~';
VERTICAL_BAR_: '|';
AMPERSAND_: '&';
SIGNED_LEFT_SHIFT_: '<
SIGNED_RIGHT_SHIFT_: '>>';
CARET_: '^';
MOD_: '%';
COLON_: ':';
PLUS_: '+';
MINUS_: '-';
ASTERISK_: '*';
SLASH_: '/';
BACKSLASH_: '\\';
…
Literals.g4定义SQL中的字面量值规则
lexer grammar Literals;
import Alphabet, Symbol;
IDENTIFIER_
: [A-Za-z_$0-9]*?[A-Za-z_$]+?[A-Za-z_$0-9]*
| BQ_ ~'`'+ BQ_
| (DQ_ ( '\\'. | '""' | ~('"'| '\\') )* DQ_)
;
STRING_
: (DQ_ ( '\\'. | '""' | ~('"'| '\\') )* DQ_)
| (SQ_ ('\\'. | '\'\'' | ~('\'' | '\\'))* SQ_)
;
NUMBER_
: INT_? DOT_? INT_ (E (PLUS_ | MINUS_)? INT_)?
;
…
MySQLKeyword.g4定义了MySQL中特有的关键字
lexer grammar MySQLKeyword;
import Alphabet;
USE
: U S E
;
DESCRIBE
: D E S C R I B E
;
SHOW
: S H O W
;
DATABASES
: D A T A B A S E S
;
DATABASE
: D A T A B A S E
;
SCHEMAS
: S C H E M A S
;
TABLES
: T A B L E S
;
TABLESPACE
: T A B L E S P A C E
;
COLUMNS
: C O L U M N S
;
FIELDS
: F I E L D S
;
…
Comments.g4定义SQL中注释词法规则
lexer grammar Comments;
import Symbol;
BLOCK_COMMENT: '/*' .*? '*/' -> channel(HIDDEN);
INLINE_COMMENT: (('-- ' | '#') ~[\r\n]* ('\r'? '\n' | EOF) | '--' ('\r'? '\n' | EOF)) -> channel(HIDDEN);
BaseRule.g4定义了SQL中的各类字面值语法规则
grammar BaseRule;
import Symbol, Keyword, MySQLKeyword, Literals;
parameterMarker
: QUESTION_
;
literals
: stringLiterals
| numberLiterals
| dateTimeLiterals
| hexadecimalLiterals
| bitValueLiterals
| booleanLiterals
| nullValueLiterals
;
stringLiterals
: characterSetName_? STRING_ collateClause_?
;
numberLiterals
: MINUS_? NUMBER_
;
dateTimeLiterals
: (DATE | TIME | TIMESTAMP) STRING_
| LBE_ identifier STRING_ RBE_
;
…
DMLStatement.g4定义DML语句的语法规则
grammar DMLStatement;
import Symbol, Keyword, MySQLKeyword, Literals, BaseRule;
insert
: INSERT insertSpecification_ INTO? tableName partitionNames_? (insertValuesClause | setAssignmentsClause | insertSelectClause) onDuplicateKeyClause?
;
insertSpecification_
: (LOW_PRIORITY | DELAYED | HIGH_PRIORITY)? IGNORE?
;
insertValuesClause
: columnNames? (VALUES | VALUE) assignmentValues (COMMA_ assignmentValues)*
;
insertSelectClause
: columnNames? select
;
onDuplicateKeyClause
: ON DUPLICATE KEY UPDATE assignment (COMMA_ assignment)*
;
replace
: REPLACE replaceSpecification_? INTO? tableName partitionNames_? (insertValuesClause | setAssignmentsClause | insertSelectClause)
;
replaceSpecification_
: LOW_PRIORITY | DELAYED
;
update
: UPDATE updateSpecification_ tableReferences setAssignmentsClause whereClause? orderByClause? limitClause?
;
updateSpecification_
: LOW_PRIORITY? IGNORE?
;
assignment
: columnName EQ_ assignmentValue
;
setAssignmentsClause
: SET assignment (COMMA_ assignment)*
;
assignmentValues
: LP_ assignmentValue (COMMA_ assignmentValue)* RP_
| LP_ RP_
;
assignmentValue
: expr | DEFAULT | blobValue
;
blobValue
: UL_BINARY STRING_
;
delete
: DELETE deleteSpecification_ (singleTableClause | multipleTablesClause) whereClause?
;
deleteSpecification_
: LOW_PRIORITY? QUICK? IGNORE?
;
DDLStatement.g4定义了DDL语句语法规则
grammar DDLStatement;
import Symbol, Keyword, MySQLKeyword, Literals, BaseRule, DMLStatement;
createTable
: CREATE createTableSpecification_? TABLE tableNotExistClause_ tableName (createDefinitionClause | createLikeClause)
;
alterTable
: ALTER TABLE tableName alterDefinitionClause?
;
dropTable
: DROP dropTableSpecification_ TABLE tableExistClause_ tableNames
;
dropIndex
: DROP INDEX dropIndexSpecification_? indexName (ON tableName)?
( ALGORITHM EQ_? (DEFAULT | INPLACE | COPY) | LOCK EQ_? (DEFAULT | NONE | SHARED | EXCLUSIVE) )*
;
truncateTable
: TRUNCATE TABLE? tableName
;
…
限于篇幅,其它的.g4文件这里就不贴其内容了,通过这些g4规则文件可以快速的得知目前ShardingSphere支持的SQL种类,对于不支持的,也可以通过修改或增加g4文件中规则进行扩展,这种方式要比druid在代码中写死的方式要灵活很多。不过这种自动生成的解析器相比手写解析器性能要低,官方文档给出的数据比第二代自研的 SQL 解析引擎慢 3-10 倍左右。
SQL种类
在看代码前,首先我们看下SQL的分类,因为ShardingSpher