简介
在上两篇文中,我们探索了 SQLToken 和真实 SQL 的生成的想关代码,本文继续来探索最开始的一个 LogicSQL 的生成,补全这一块拼图
源码探索
继续上面两篇的探索:
其中生成真实的 SQL 有两个要素:
目前就还差 index 生成的东西,我们接下来就看看 LogicSQL 的相关代码:
寻找切入点
下面是 LogicSQL 的生成部分:
private ExecutionContext createExecutionContext() { LogicSQL logicSQL = createLogicSQL(); SQLCheckEngine.check(logicSQL.getSqlStatementContext().getSqlStatement(), logicSQL.getParameters(), metaDataContexts.getMetaData(connection.getSchemaName()).getRuleMetaData().getRules(), connection.getSchemaName(), metaDataContexts.getMetaDataMap(), null); ExecutionContext result = kernelProcessor.generateExecutionContext(logicSQL, metaDataContexts.getMetaData(connection.getSchemaName()), metaDataContexts.getProps()); findGeneratedKey(result).ifPresent(generatedKey -> generatedValues.addAll(generatedKey.getGeneratedValues())); return result; } private LogicSQL createLogicSQL() { List<Object> parameters = new ArrayList<>(getParameters()); SQLStatementContext<?> sqlStatementContext = SQLStatementContextFactory.newInstance(metaDataContexts.getMetaDataMap(), parameters, sqlStatement, connection.getSchemaName()); return new LogicSQL(sqlStatementContext, sql, parameters); }
复制代码
但通过 debug 看到相关的东西其实之前就生成好了:
其中的 preparedStatement 就有了相关的 index 信息,看来是在某一步就初始化好了的,我们找到对应的初始化语句,如下:
# ShardingSpherePreparedStatement.java private ShardingSpherePreparedStatement(final ShardingSphereConnection connection, final String sql, final int resultSetType, final int resultSetConcurrency, final int resultSetHoldability, final boolean returnGeneratedKeys) throws SQLException { if (Strings.isNullOrEmpty(sql)) { throw new SQLException(SQLExceptionConstant.SQL_STRING_NULL_OR_EMPTY); } this.connection = connection; metaDataContexts = connection.getContextManager().getMetaDataContexts(); this.sql = sql; statements = new ArrayList<>(); parameterSets = new ArrayList<>(); ShardingSphereSQLParserEngine sqlParserEngine = new ShardingSphereSQLParserEngine( DatabaseTypeRegistry.getTrunkDatabaseTypeName(metaDataContexts.getMetaData(connection.getSchemaName()).getResource().getDatabaseType())); // 这里进行生成的 sqlStatement = sqlParserEngine.parse(sql, true); parameterMetaData = new ShardingSphereParameterMetaData(sqlStatement); statementOption = returnGeneratedKeys ? new StatementOption(true) : new StatementOption(resultSetType, resultSetConcurrency, resultSetHoldability); JDBCExecutor jdbcExecutor = new JDBCExecutor(metaDataContexts.getExecutorEngine(), connection.isHoldTransaction()); driverJDBCExecutor = new DriverJDBCExecutor(connection.getSchemaName(), metaDataContexts, jdbcExecutor); rawExecutor = new RawExecutor(metaDataContexts.getExecutorEngine(), connection.isHoldTransaction(), metaDataContexts.getProps()); // TODO Consider FederateRawExecutor federateExecutor = new FederateJDBCExecutor(connection.getSchemaName(), metaDataContexts.getOptimizeContextFactory(), metaDataContexts.getProps(), jdbcExecutor); batchPreparedStatementExecutor = new BatchPreparedStatementExecutor(metaDataContexts, jdbcExecutor, connection.getSchemaName()); kernelProcessor = new KernelProcessor(); }
复制代码
我们再往前找找,看到是在 OrderRepositoryImpl.java 中进行触发的:
# OrderRepositoryImpl.java @Override public Long insert(final Order order) throws SQLException { String sql = "INSERT INTO t_order (user_id, address_id, status) VALUES (?, ?, ?)"; try (Connection connection = dataSource.getConnection(); PreparedStatement preparedStatement = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS)) { preparedStatement.setInt(1, order.getUserId()); preparedStatement.setLong(2, order.getAddressId()); preparedStatement.setString(3, order.getStatus()); preparedStatement.executeUpdate(); try (ResultSet resultSet = preparedStatement.getGeneratedKeys()) { if (resultSet.next()) { order.setOrderId(resultSet.getLong(1)); } } } return order.getOrderId(); }
复制代码
那我们就继续探索:sqlStatement = sqlParserEngine.parse(sql, true);
我们一直跟着下去,来到一个 SQL 处理的相关类:MySQLStatementParser.java
其给人的第一个感觉是相当的复杂,我们跟着 debug 下去,看到进入到相关的 insert 处理的分支
case XA: enterOuterAlt(_localctx, 1); { setState(1246); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,0,_ctx) ) { case 1: { setState(1146); select(); } break; case 2: { setState(1147); insert(); } break;
复制代码
跟着下去,来到 insert 语句处理具体函数:
# MySQLStatementParser.java public final InsertContext insert() throws RecognitionException { InsertContext _localctx = new InsertContext(_ctx, getState()); enterRule(_localctx, 2, RULE_insert); int _la; try { enterOuterAlt(_localctx, 1); { setState(1258); // Insert相关处理 match(INSERT); setState(1259); insertSpecification(); setState(1261); _errHandler.sync(this); _la = _input.LA(1); if (_la==INTO) { { setState(1260); // into 相关处理 match(INTO); } }
setState(1263); // 表名相关处理 tableName(); setState(1265); _errHandler.sync(this); _la = _input.LA(1); if (_la==PARTITION) { { setState(1264); partitionNames(); } }
setState(1270); _errHandler.sync(this); switch ( getInterpreter().adaptivePredict(_input,6,_ctx) ) { case 1: { setState(1267); // values相关处理 insertValuesClause(); } break; case 2: { setState(1268); setAssignmentsClause(); } break; case 3: { setState(1269); insertSelectClause(); } break; } setState(1273); _errHandler.sync(this); _la = _input.LA(1); if (_la==ON) { { setState(1272); onDuplicateKeyClause(); } }
} } catch (RecognitionException re) { _localctx.exception = re; _errHandler.reportError(this, re); _errHandler.recover(this, re); } finally { exitRule(); } return _localctx; }
复制代码
在上面的函数中,我们大意看到几个比较关键的处理函数:
其规则跟下来有点复杂了,有循环和嵌套处理的,目前是梳理不清楚了
但其大意都是得到对应的开始和结束位置之类的,如下图:
最终得到结果如下:
到结果后,相关的返回函数如下:
@RequiredArgsConstructorpublic final class SQLParserExecutor { private final String databaseType; /** * Parse SQL. * * @param sql SQL to be parsed * @return parse tree */ public ParseTree parse(final String sql) { ParseASTNode result = twoPhaseParse(sql); if (result.getRootNode() instanceof ErrorNode) { throw new SQLParsingException("Unsupported SQL of `%s`", sql); } return result.getRootNode(); }}
复制代码
result.getRootNode() 如下:
@RequiredArgsConstructorpublic final class ParseASTNode implements ASTNode { private final ParseTree parseTree; /** * Get root node. * * @return root node */ public ParseTree getRootNode() { return parseTree.getChild(0); }}
复制代码
而 result 的结果如下图,getChild(0)就是得到上面我们 Insert 解析后得到的结果
总结
感觉看的迷迷糊糊的,很多地方目前还不能很好的理解
但我们起码通过本次的探索知道了真实 SQL 的关键路径:
评论