我们先说说上篇结尾的思考题,如果各个命令/请求参数信息截然不同的话,那都用一个Result来封装,我觉得是不太妥当的。如果差异这么大,我觉得还不如用不同的类来表示不同的命令/请求,就像在web中,有不同的HttpServletRequest的实现。这HSQLDB里,请求响应都是用Result表示,而在web里则分别用HttpServletRequest、HttpServletResponse表示。
这篇我们将继续深入阅读源码,了解命令的处理过程。
点击(此处)折叠或打开
-
while (keepAlive) {
-
msgType = dataInput.readByte();
-
-
if (msgType < ResultConstants.MODE_UPPER_LIMIT) {
-
receiveResult(msgType);
-
} else {
-
receiveOdbcPacket((char) msgType);
-
}
-
}
这是在ServerConnection.run方法中一个片段,这里是命令处理的主要地方。我们这里仍然只分析HSQL_STREAM_PROTOCOL的处理过程。
进入到receiveResult方法中一探究竟:
-
private void receiveResult(int resultMode) throws CleanExit, IOException {
-
-
boolean terminate = false;
-
Result resultIn = Result.newResult(session, resultMode, dataInput,
-
rowIn);
-
-
resultIn.readLobResults(session, dataInput, rowIn);
-
server.printRequest(mThread, resultIn);
-
-
Result resultOut = null;
-
-
switch (resultIn.getType()) {
-
-
case ResultConstants.CONNECT : {
-
resultOut = setDatabase(resultIn);
-
-
break;
-
}
-
case ResultConstants.DISCONNECT : {
-
resultOut = Result.updateZeroResult;
-
terminate = true;
-
-
break;
-
}
-
case ResultConstants.RESETSESSION : {
-
session.resetSession();
-
-
resultOut = Result.updateZeroResult;
-
-
break;
-
}
-
case ResultConstants.EXECUTE_INVALID : {
-
resultOut =
-
Result.newErrorResult(Error.error(ErrorCode.X_07502));
-
-
break;
-
}
-
default : {
-
resultOut = session.execute(resultIn);
-
-
break;
-
}
-
}
-
-
resultOut.write(session, dataOutput, rowOut);
-
rowOut.reset(mainBuffer);
-
rowIn.resetRow(mainBuffer.length);
-
-
if (terminate) {
-
throw cleanExit;
-
}
-
}
我们看其它类型的处理都比较简单,我们要关注就是这个default的处理,继续进入到session.execute方法中。
-
public synchronized Result execute(Result cmd) {
-
-
if (isClosed) {
-
return Result.newErrorResult(Error.error(ErrorCode.X_08503));
-
}
-
-
sessionContext.currentMaxRows = 0;
-
isBatch = false;
-
-
JavaSystem.gc();
-
-
switch (cmd.mode) {
-
-
case ResultConstants.LARGE_OBJECT_OP : {
-
return performLOBOperation((ResultLob) cmd);
-
}
-
case ResultConstants.EXECUTE : {
-
int maxRows = cmd.getUpdateCount();
-
-
if (maxRows == -1) {
-
sessionContext.currentMaxRows = 0;
-
} else {
-
sessionContext.currentMaxRows = maxRows;
-
}
-
-
Statement cs = cmd.statement;
-
-
if (cs == null
-
|| cs.compileTimestamp
-
< database.schemaManager.schemaChangeTimestamp) {
-
long csid = cmd.getStatementID();
-
-
cs = statementManager.getStatement(this, csid);
-
-
cmd.setStatement(cs);
-
-
if (cs == null) {
-
-
// invalid sql has been removed already
-
return Result.newErrorResult(
-
Error.error(ErrorCode.X_07502));
-
}
-
}
-
-
Object[] pvals = (Object[]) cmd.valueData;
-
Result result = executeCompiledStatement(cs, pvals,
-
cmd.queryTimeout);
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
-
case ResultConstants.BATCHEXECUTE : {
-
isBatch = true;
-
-
Result result = executeCompiledBatchStatement(cmd);
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
-
case ResultConstants.EXECDIRECT : {
-
Result result = executeDirectStatement(cmd);
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
-
case ResultConstants.BATCHEXECDIRECT : {
-
isBatch = true;
-
-
Result result = executeDirectBatchStatement(cmd);
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
-
case ResultConstants.PREPARE : {
-
Statement cs;
-
-
try {
-
cs = statementManager.compile(this, cmd);
-
} catch (Throwable t) {
-
String errorString = cmd.getMainString();
-
-
if (database.getProperties().getErrorLevel()
-
== HsqlDatabaseProperties.NO_MESSAGE) {
-
errorString = null;
-
}
-
-
return Result.newErrorResult(t, errorString);
-
}
-
-
Result result = Result.newPrepareResponse(cs);
-
-
if (cs.getType() == StatementTypes.SELECT_CURSOR
-
|| cs.getType() == StatementTypes.CALL) {
-
sessionData.setResultSetProperties(cmd, result);
-
}
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
-
case ResultConstants.CLOSE_RESULT : {
-
closeNavigator(cmd.getResultId());
-
-
return Result.updateZeroResult;
-
}
-
case ResultConstants.UPDATE_RESULT : {
-
Result result = this.executeResultUpdate(cmd);
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
-
case ResultConstants.FREESTMT : {
-
statementManager.freeStatement(cmd.getStatementID());
-
-
return Result.updateZeroResult;
-
}
-
case ResultConstants.GETSESSIONATTR : {
-
int id = cmd.getStatementType();
-
-
return getAttributesResult(id);
-
}
-
case ResultConstants.SETSESSIONATTR : {
-
return setAttributes(cmd);
-
}
-
case ResultConstants.ENDTRAN : {
-
switch (cmd.getActionType()) {
-
-
case ResultConstants.TX_COMMIT :
-
try {
-
commit(false);
-
} catch (Throwable t) {
-
return Result.newErrorResult(t);
-
}
-
break;
-
-
case ResultConstants.TX_COMMIT_AND_CHAIN :
-
try {
-
commit(true);
-
} catch (Throwable t) {
-
return Result.newErrorResult(t);
-
}
-
break;
-
-
case ResultConstants.TX_ROLLBACK :
-
rollback(false);
-
break;
-
-
case ResultConstants.TX_ROLLBACK_AND_CHAIN :
-
rollback(true);
-
break;
-
-
case ResultConstants.TX_SAVEPOINT_NAME_RELEASE :
-
try {
-
String name = cmd.getMainString();
-
-
releaseSavepoint(name);
-
} catch (Throwable t) {
-
return Result.newErrorResult(t);
-
}
-
break;
-
-
case ResultConstants.TX_SAVEPOINT_NAME_ROLLBACK :
-
try {
-
rollbackToSavepoint(cmd.getMainString());
-
} catch (Throwable t) {
-
return Result.newErrorResult(t);
-
}
-
break;
-
-
case ResultConstants.PREPARECOMMIT :
-
try {
-
prepareCommit();
-
} catch (Throwable t) {
-
return Result.newErrorResult(t);
-
}
-
break;
-
}
-
-
return Result.updateZeroResult;
-
}
-
case ResultConstants.SETCONNECTATTR : {
-
switch (cmd.getConnectionAttrType()) {
-
-
case ResultConstants.SQL_ATTR_SAVEPOINT_NAME :
-
try {
-
savepoint(cmd.getMainString());
-
} catch (Throwable t) {
-
return Result.newErrorResult(t);
-
}
-
-
// case ResultConstants.SQL_ATTR_AUTO_IPD
-
// - always true
-
// default: throw - case never happens
-
}
-
-
return Result.updateZeroResult;
-
}
-
case ResultConstants.REQUESTDATA : {
-
return sessionData.getDataResultSlice(cmd.getResultId(),
-
cmd.getUpdateCount(),
-
cmd.getFetchSize());
-
}
-
case ResultConstants.DISCONNECT : {
-
close();
-
-
return Result.updateZeroResult;
-
}
-
default : {
-
return Result.newErrorResult(
-
Error.runtimeError(ErrorCode.U_S0500, "Session"));
-
}
-
}
-
}
很多种命令,看起来相当复杂。我们挑其中比较简单的
-
case ResultConstants.EXECDIRECT : {
-
Result result = executeDirectStatement(cmd);
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
当Result的mode为EXECDIRECT时,表示直接执行sql语句。(与之相对应的,我们知道有一种prepare之后再执行sql的方式)
我们先了解其处理过程,再回头来看其它命令,就更容易理解了。
进入executeDirectStatement:
-
public Result executeDirectStatement(Result cmd) {
-
-
String sql = cmd.getMainString();
-
HsqlArrayList list;
-
int maxRows = cmd.getUpdateCount();
-
-
if (maxRows == -1) {
-
sessionContext.currentMaxRows = 0;
-
} else if (sessionMaxRows == 0) {
-
sessionContext.currentMaxRows = maxRows;
-
} else {
-
sessionContext.currentMaxRows = sessionMaxRows;
-
sessionMaxRows = 0;
-
}
-
-
try {
-
list = parser.compileStatements(sql, cmd);
-
} catch (Throwable e) {
-
return Result.newErrorResult(e);
-
}
-
-
Result result = null;
-
boolean recompile = false;
-
HsqlName originalSchema = getCurrentSchemaHsqlName();
-
-
for (int i = 0; i < list.size(); i++) {
-
Statement cs = (Statement) list.get(i);
-
-
if (i > 0) {
-
if (cs.getCompileTimestamp()
-
> database.txManager.getGlobalChangeTimestamp()) {
-
recompile = true;
-
}
-
-
if (cs.getSchemaName() != null
-
&& cs.getSchemaName() != originalSchema) {
-
recompile = true;
-
}
-
}
-
-
if (recompile) {
-
cs = compileStatement(cs.getSQL(), cmd.getExecuteProperties());
-
}
-
-
cs.setGeneratedColumnInfo(cmd.getGeneratedResultType(),
-
cmd.getGeneratedResultMetaData());
-
-
result = executeCompiledStatement(cs, ValuePool.emptyObjectArray,
-
cmd.queryTimeout);
-
-
if (result.mode == ResultConstants.ERROR) {
-
break;
-
}
-
}
-
-
return result;
-
}
sql语句是字符串,所以需要解析其涵义,hsql先将sql语句进行编译:parser.compileStatements(sql, cmd);(我们下一章再去关注,这个sql语句的编译过程)。返回的是HsqlArrayList——存Statement的List,意味着可能一个命令中包含多个执行的sql语句,然后循环执行该list中的Statement对象——
executeCompiledStatement。
-
result = executeCompiledStatement(cs, ValuePool.emptyObjectArray,
-
cmd.queryTimeout);
再进入
executeCompiledStatement
-
public Result executeCompiledStatement(Statement cs, Object[] pvals,
-
int timeout) {
-
-
Result r;
-
-
if (abortTransaction) {
-
rollbackNoCheck(false);
-
-
return Result.newErrorResult(Error.error(ErrorCode.X_40001));
-
}
-
-
if (sessionContext.depth > 0) {
-
if (sessionContext.noSQL.booleanValue()
-
|| cs.isAutoCommitStatement()) {
-
return Result.newErrorResult(Error.error(ErrorCode.X_46000));
-
}
-
}
-
-
if (cs.isAutoCommitStatement()) {
-
if (isReadOnly()) {
-
return Result.newErrorResult(Error.error(ErrorCode.X_25006));
-
}
-
-
try {
-
-
/** special autocommit for backward compatibility */
-
commit(false);
-
} catch (HsqlException e) {
-
database.logger.logInfoEvent("Exception at commit");
-
}
-
}
-
-
sessionContext.currentStatement = cs;
-
-
boolean isTX = cs.isTransactionStatement();
-
-
if (!isTX) {
-
actionTimestamp =
-
database.txManager.getNextGlobalChangeTimestamp();
-
-
sessionContext.setDynamicArguments(pvals);
-
-
// statements such as DISCONNECT may close the session
-
if (database.logger.getSqlEventLogLevel()
-
>= SimpleLog.LOG_NORMAL) {
-
database.logger.logStatementEvent(this, cs, pvals,
-
Result.updateZeroResult,
-
SimpleLog.LOG_NORMAL);
-
}
-
-
r = cs.execute(this);
-
sessionContext.currentStatement = null;
-
-
return r;
-
}
-
-
while (true) {
-
actionIndex = rowActionList.size();
-
-
database.txManager.beginAction(this, cs);
-
-
cs = sessionContext.currentStatement;
-
-
if (cs == null) {
-
return Result.newErrorResult(Error.error(ErrorCode.X_07502));
-
}
-
-
if (abortTransaction) {
-
rollbackNoCheck(false);
-
-
sessionContext.currentStatement = null;
-
-
return Result.newErrorResult(Error.error(ErrorCode.X_40001));
-
}
-
-
timeoutManager.startTimeout(timeout);
-
-
try {
-
latch.await();
-
} catch (InterruptedException e) {
-
abortTransaction = true;
-
}
-
-
boolean abortAction = timeoutManager.endTimeout();
-
-
if (abortAction) {
-
r = Result.newErrorResult(Error.error(ErrorCode.X_40502));
-
-
endAction(r);
-
-
break;
-
}
-
-
if (abortTransaction) {
-
rollbackNoCheck(false);
-
-
sessionContext.currentStatement = null;
-
-
return Result.newErrorResult(Error.error(ErrorCode.X_40001));
-
}
-
-
database.txManager.beginActionResume(this);
-
-
// tempActionHistory.add("sql execute " + cs.sql + " " + actionTimestamp + " " + rowActionList.size());
-
sessionContext.setDynamicArguments(pvals);
-
-
r = cs.execute(this);
-
-
if (database.logger.getSqlEventLogLevel()
-
>= SimpleLog.LOG_NORMAL) {
-
database.logger.logStatementEvent(this, cs, pvals, r,
-
SimpleLog.LOG_NORMAL);
-
}
-
-
lockStatement = sessionContext.currentStatement;
-
-
// tempActionHistory.add("sql execute end " + actionTimestamp + " " + rowActionList.size());
-
endAction(r);
-
-
if (abortTransaction) {
-
rollbackNoCheck(false);
-
-
sessionContext.currentStatement = null;
-
-
return Result.newErrorResult(Error.error(r.getException(),
-
ErrorCode.X_40001, null));
-
}
-
-
if (redoAction) {
-
redoAction = false;
-
-
try {
-
latch.await();
-
} catch (InterruptedException e) {
-
abortTransaction = true;
-
}
-
} else {
-
break;
-
}
-
}
-
-
if (sessionContext.depth == 0
-
&& (sessionContext.isAutoCommit.booleanValue()
-
|| cs.isAutoCommitStatement())) {
-
try {
-
if (r.mode == ResultConstants.ERROR) {
-
rollbackNoCheck(false);
-
} else {
-
commit(false);
-
}
-
} catch (Exception e) {
-
sessionContext.currentStatement = null;
-
-
return Result.newErrorResult(Error.error(ErrorCode.X_40001,
-
e));
-
}
-
}
-
-
sessionContext.currentStatement = null;
-
-
return r;
-
}
又是一个好长的函数~
我们忽略掉其中的错误检查部分,看函数的主体内容,其实就一个:
其它代码主要都在处理事务的东西。
而cs.execute则是一个抽象方法
-
public abstract Result execute(Session session);
它的具体代码,由Statement的子类来实现。
代码分析到现在,我们来理清一下命令的处理过程:
1. 大多数命令,都会进入Session.execute(Result)进行处理
2. 在Session.execute中,根据命令的类型,进行分别的处理
3. 对于Execute的命令(如DirectExecute),需要Statement对象来执行
4. Statement由parser编译而来——编译器根据语句内容,生成了不同的Statement子类的实例
5. 执行Statement.execute(Session)方法,返回Result对象
理清命令处理的思路之后,再回头来看Session.execute(Result cmd)的其它命令的处理,过程就变得清晰一点了。
其中BATCHEXECDIRECT和EXECDIRECT是类似的,比较让人困惑的是EXECUTE
-
case ResultConstants.EXECUTE : {
-
int maxRows = cmd.getUpdateCount();
-
-
if (maxRows == -1) {
-
sessionContext.currentMaxRows = 0;
-
} else {
-
sessionContext.currentMaxRows = maxRows;
-
}
-
-
Statement cs = cmd.statement;
-
-
if (cs == null
-
|| cs.compileTimestamp
-
< database.schemaManager.schemaChangeTimestamp) {
-
long csid = cmd.getStatementID();
-
-
cs = statementManager.getStatement(this, csid);
-
-
cmd.setStatement(cs);
-
-
if (cs == null) {
-
-
// invalid sql has been removed already
-
return Result.newErrorResult(
-
Error.error(ErrorCode.X_07502));
-
}
-
}
-
-
Object[] pvals = (Object[]) cmd.valueData;
-
Result result = executeCompiledStatement(cs, pvals,
-
cmd.queryTimeout);
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
我在之前看这个代码的时候也很困惑,它的Statement居然是根据getStatementID取出来的。
而且在之前通信协议的解析中[Result.newResult]
-
case ResultConstants.EXECUTE :
-
result.updateCount = in.readInt();
-
result.fetchSize = in.readInt();
-
result.statementID = in.readLong();
-
result.rsProperties = in.readByte();
-
result.queryTimeout = in.readShort();
-
-
Statement statement =
-
session.statementManager.getStatement(session,
-
result.statementID);
-
-
if (statement == null) {
-
-
// invalid statement
-
result.mode = ResultConstants.EXECUTE_INVALID;
-
result.valueData = ValuePool.emptyObjectArray;
-
-
break;
-
}
-
-
result.statement = statement;
-
result.metaData = result.statement.getParametersMetaData();
-
result.valueData = readSimple(in, result.metaData);
-
break;
这里也是通过statementManager.getStatement取出来的。那这个Statement是什么时候存进去的呢?
秘密就在于Prepare
-
case ResultConstants.PREPARE : {
-
Statement cs;
-
-
try {
-
cs = statementManager.compile(this, cmd);
-
} catch (Throwable t) {
-
String errorString = cmd.getMainString();
-
-
if (database.getProperties().getErrorLevel()
-
== HsqlDatabaseProperties.NO_MESSAGE) {
-
errorString = null;
-
}
-
-
return Result.newErrorResult(t, errorString);
-
}
-
-
Result result = Result.newPrepareResponse(cs);
-
-
if (cs.getType() == StatementTypes.SELECT_CURSOR
-
|| cs.getType() == StatementTypes.CALL) {
-
sessionData.setResultSetProperties(cmd, result);
-
}
-
-
result = performPostExecute(cmd, result);
-
-
return result;
-
}
也就是在这里,hsql将sql进行了预编译,然后生成一个statementId返回给客户端,客户端在下次请求执行的时候,必须将这个statementId带上,然后hsql从statementManager中取出之前编译好的sql语句(即statement对象)。
所以,我们知道,为什么对于需要多次执行的sql语句,为什么prepare的性能要高一些,因为executeDirect的语句需要每次编译,而prepare的sql语句只需要编译一次。
这里我们就只解释这些命令了,其它命令相对比较简单,就不逐一进行说明了,有兴趣的自己查看源码。
好,本文就到这,下回我们分析sql语句的编译过程。
[相关源码文件]
Result.java
Session.java
StatementManager.java
阅读(2491) | 评论(0) | 转发(0) |