语法解析器,根据不同类型的语句有不同的语法解析器去解析成成SQLStatement,SQL解析器的类图我用脑图画出来如下:
可以看到,不同的sql有不同的处理解析器去解析,解析完成之后,会将SQL解析成SQLStatement,看一下SQLParser的定义:
public interface SQLParser {
SQLStatement parse();
}
方法名 | 说明 |
---|---|
SQLType getType() | 获取SQL类型,有DDL,DML,DQL,DAL,TCL |
Tables getTables(); | 获取表集合 |
Conditions getConditions(); | 获取条件 |
List<SQLToken> getSqlTokens(); | 获取所有的SQLToken |
int getParametersIndex() | 获取参数位置 |
void setParametersIndex(int parametersIndex) | 重置参数位置 |
int increaseParametersIndex() | 增加参数位置 |
除了语法解析器SQLParser,还有SQL从句解析器SQLClauseParser,SQL别名解析器AliasExpressionParser,表达式解析器BasicExpressionParser。
如下图:
SQLStatement对象是个超类,具体实现类有很多。按照不同的语句,返回不同的SQLStatement。
api如下:
方法名 | 说明 |
---|---|
SQLType getType() | 获取SQL类型,有DDL,DML,DQL,DAL,TCL |
Tables getTables(); | 获取表集合 |
Conditions getConditions(); | 获取条件 |
List<SQLToken> getSqlTokens(); | 获取所有的SQLToken |
int getParametersIndex() | 获取参数位置 |
void setParametersIndex(int parametersIndex) | 重置参数位置 |
int increaseParametersIndex() | 增加参数位置 |
不同的语句,ddl,dml,tcl等,有不同的语法解析器SQLParser去解析,Sharding-sphere如何根据不同的SQL去选择不同的SQLParser?
这里和词法分析器一样,同样是使用工厂模式,按照不同类型的SQL选择不同的语法解析器。根据数据库类型,DB类型分词解析器获取语法解析器。
public final class SQLParserFactory {
public static SQLParser newInstance(final DatabaseType dbType, final TokenType tokenType, final ShardingRule shardingRule, final LexerEngine lexerEngine, final ShardingMetaData shardingMetaData) {
//如果是DQL语句 select语句
if (isDQL(tokenType)) {
return getDQLParser(dbType, shardingRule, lexerEngine, shardingMetaData);
}
//DML语句 insert,update,delete语句
if (isDML(tokenType)) {
return getDMLParser(dbType, tokenType, shardingRule, lexerEngine, shardingMetaData);
}
//DDL语句 create,alter,drop,TRUNCATE语句
if (isDDL(tokenType)) {
return getDDLParser(dbType, tokenType, shardingRule, lexerEngine);
}
//TCL语句 set,commit,rollBack,savePoint,begin语句
if (isTCL(tokenType)) {
return getTCLParser(dbType, shardingRule, lexerEngine);
}
//DAL语句,use,desc,describe,show语句
if (isDAL(tokenType)) {
return getDALParser(dbType, (Keyword) tokenType, shardingRule, lexerEngine);
}
throw new SQLParsingUnsupportedException(tokenType);
}
}
这里以insert语句为例,看一下如何通过数据库类型选择不同的SQLParser
private static SQLParser getDMLParser(
final DatabaseType dbType, final TokenType tokenType, final ShardingRule shardingRule, final LexerEngine lexerEngine, final ShardingMetaData shardingMetaData) {
switch ((DefaultKeyword) tokenType) {
case INSERT:
//如果Insert语句
return InsertParserFactory.newInstance(dbType, shardingRule, lexerEngine, shardingMetaData);
case UPDATE:
return UpdateParserFactory.newInstance(dbType, shardingRule, lexerEngine);
case DELETE:
return DeleteParserFactory.newInstance(dbType, shardingRule, lexerEngine);
default:
throw new SQLParsingUnsupportedException(tokenType);
}
}
public final class InsertParserFactory {
public static AbstractInsertParser newInstance(final DatabaseType dbType, final ShardingRule shardingRule, final LexerEngine lexerEngine, final ShardingMetaData shardingMetaData) {
switch (dbType) {
case H2:
case MySQL:
return new MySQLInsertParser(shardingRule, lexerEngine, shardingMetaData);
case Oracle:
return new OracleInsertParser(shardingRule, lexerEngine, shardingMetaData);
case SQLServer:
return new SQLServerInsertParser(shardingRule, lexerEngine, shardingMetaData);
case PostgreSQL:
return new PostgreSQLInsertParser(shardingRule, lexerEngine, shardingMetaData);
default:
throw new UnsupportedOperationException(String.format("Cannot support database [%s].", dbType));
}
}
}
能够看到,通过多个Factory,根据SQL语句类型(DML,DDL)等路由到不同的方法,根据具体类型(Insert,update)等找到对应的工厂模式,按照不同的数据库类型返回具体的语法解析器SQLParser。
一条sql在执行的时候,如何知道是什么类型的语句??
词法分析器Lexer在解析Sql的时候,第一个分词就是SQL的具体类型(select,update),所以在执行sql的时候,首先调用词法分析器解析第一个分词,获取语句类型,然后选择具体的语法解析器解析。和分词器引擎一样,SQL语句解析器也有自己的解析引擎SQLParsingEngine
public final class SQLParsingEngine {
//数据库类型
private final DatabaseType dbType;
//sql
private final String sql;
//分片规则
private final ShardingRule shardingRule;
private final ShardingMetaData shardingMetaData;
public SQLStatement parse(final boolean useCache) {
//是否从缓存中获取
Optional<SQLStatement> cachedSQLStatement = getSQLStatementFromCache(useCache);
if (cachedSQLStatement.isPresent()) {
return cachedSQLStatement.get();
}
//词法解析器,根据不同的sql,获取不同的词法解析引擎
LexerEngine lexerEngine = LexerEngineFactory.newInstance(dbType, sql);
//解析第一个分词,第一个分词能区分sql类型
lexerEngine.nextToken();
//不同的sql解析器去做解析,完成解析工作,返回SQLStatement
SQLStatement result = SQLParserFactory.newInstance(dbType, lexerEngine.getCurrentToken().getType(), shardingRule, lexerEngine, shardingMetaData).parse();
if (useCache) {
ParsingResultCache.getInstance().put(sql, result);
}
return result;
}
}
所以sql语句解析的过程如下图:
终于把sql语法解析器的流程梳理清楚了。
下一篇,分析insert语句的流程。