前段时间事情太多,一直没有继续看Derby的源代码。国庆过去了,闲暇的时间多了些了,终于可以接着把这个系列写完了。不过也过了很长时间了,自己前面看的东西都忘的差不多了...... 慢慢找回来吧!
前面的文章提到了Derby的JDBC层,不敢说仔细的读,也就是大概的过了一遍,下面的文章主要是讲的SQL层,前面的JDBC实现(二)这篇文章中提到了JDBC层的sql语句执行的代码,其中在执行SQL的过程中,涉及到了Activation的获取,见org.apache.derby.impl.jdbc.EmbedStatement#execute(String, boolean, boolean, int, int[], String[]):
private boolean execute(String sql, boolean executeQuery, boolean executeUpdate, int autoGeneratedKeys,
int[] columnIndexes, String[] columnNames) throws SQLException {
synchronized (getConnectionSynchronization()) {
checkExecStatus();
if (sql == null) {
throw newSQLException(SQLState.NULL_SQL_TEXT);
}
checkIfInMiddleOfBatch();
/* 关闭与这个Statement关联的ResultSet */
clearResultSets();
setupContextStack();
SQLText = sql;
try {
/* 获取Activation对象 */
Activation activation;
try {
PreparedStatement preparedStatement = lcc.prepareInternalStatement(lcc.getDefaultSchema(), sql,
resultSetConcurrency == java.sql.ResultSet.CONCUR_READ_ONLY, false);
activation = preparedStatement.getActivation(lcc,
resultSetType == java.sql.ResultSet.TYPE_SCROLL_INSENSITIVE);
checkRequiresCallableStatement(activation);
} catch (Throwable t) {
throw handleException(t);
}
activation.setSingleExecution();
if (autoGeneratedKeys == Statement.RETURN_GENERATED_KEYS)
activation.setAutoGeneratedKeysResultsetInfo(columnIndexes, columnNames);
/* 执行Statement */
return executeStatement(activation, executeQuery, executeUpdate);
} finally {
restoreContextStack();
}
}
}
注意在try/catch中的语句,这里就是JDBC层与SQL层进行的一次“亲密接触”,这里首当其冲应该了解的就是这个“lcc”,它是LanguageConnectionContext的对象,在前面的文章JDBC(三)中提到了LanguageConnectionContext的初始化,是在BasicDatabase#setupConnection()中的:
public LanguageConnectionContext setupConnection(ContextManager cm, String user, String drdaID, String dbname)
throws StandardException {
TransactionController tc = getConnectionTransaction(cm);
cm.setLocaleFinder(this);
// push DatabaseContext到cm
pushDbContext(cm);
// 返回一个GenericLanguageConnectionContext实例,并且push到cm
LanguageConnectionContext lctx = lcf.newLanguageConnectionContext(cm, tc, lf, this, user, drdaID, dbname);
// push ClassFactory到cm
pushClassFactoryContext(cm, lcf.getClassFactory());
ExecutionFactory ef = lcf.getExecutionFactory();
// push ExecutionContext到cm
ef.newExecutionContext(cm);
lctx.initialize();
lctx.internalCommitNoSync(TransactionController.RELEASE_LOCKS
| TransactionController.READONLY_TRANSACTION_INITIALIZATION);
return lctx;
}
这里,LanguageConnectionFactory的newLanguageConnectionContext()方法返回了一个LanguageConnectionContext对象。根据默认配置,LanguageConnectionFactory的实现类是GenericLanguageConnectionFactory,它的newLanguageConnectionContext()方法返回了一个GenericLanguageConnectionContext对象作为LanguageConnectionContext的默认实现。
获得了实例之后,紧接着调用了LanguageConnectionContext的initialize()和internalCommitNoSync()两个方法,主要了解下initialize()这个方法的细节
public void initialize() throws StandardException {
// 创建此用户的授权类,IdUtil.getUserAuthorizationId()应该是对某些用户名进行特殊处理,例如加上双引号的
authorizer = new GenericAuthorizer(IdUtil.getUserAuthorizationId(userName), this);
if (SanityManager.DEBUG) {
if (getAuthorizationId() == null) {
SanityManager.THROWASSERT("User name is null," + " check the connection manager to make sure it is set"
+ " reasonably");
}
}
// 设定默认的Schema
setDefaultSchema(initDefaultSchemaDescriptor());
}
由于Schema作为数据字典中的顶层元素,所以这里还是要了解Schema的具体初始化过程的。
protected SchemaDescriptor initDefaultSchemaDescriptor() throws StandardException {
// 如果数据库支持Schema,并且与用户名同名的Schema存在,那么就设定这个Schema为用户的默认Schema,否则使用默认的"APP" Schema
if (cachedInitialDefaultSchemaDescr == null) {
// 数据字典,通过查看modules.properties,它的默认实现是org.apache.derby.impl.sql.catalog.DataDictionaryImpl
DataDictionary dd = getDataDictionary();
// authorizationId其实就是用户名
String authorizationId = getAuthorizationId();
// 先尝试获取与用户名同名的Schema描述
SchemaDescriptor sd = dd.getSchemaDescriptor(authorizationId, getTransactionCompile(), false);
if (sd == null) {
// 返回一个默认的UUID是null的Schema
sd = new SchemaDescriptor(dd, authorizationId, authorizationId, (UUID) null, false);
}
cachedInitialDefaultSchemaDescr = sd;
}
return cachedInitialDefaultSchemaDescr;
}
这里DataDictionary的实现类其实是DataDictionaryImpl(同样在modules.properties中可以找到),下面就来看看它的getSchemaDescriptor()方法:
public SchemaDescriptor getSchemaDescriptor(String schemaName, TransactionController tc, boolean raiseError)
throws StandardException {
if (tc == null) {
tc = getTransactionCompile();
}
// 先从系统Schema中寻找,然后是IBM Schema
if (getSystemSchemaDescriptor().getSchemaName().equals(schemaName)) {
return getSystemSchemaDescriptor();
} else if (getSysIBMSchemaDescriptor().getSchemaName().equals(schemaName)) {
if (dictionaryVersion.checkVersion(DataDictionary.DD_VERSION_CS_5_2, null)) {
return getSysIBMSchemaDescriptor();
}
}
// 在sys.SYSSCHEMAS中查找名称为schemaname的记录
SchemaDescriptor sd = locateSchemaRow(schemaName, tc);
// 如果Schema叫SESSION,那么是与临时表相关的操作,创建一个在内存中保存的Schema描述
if (sd == null && getDeclaredGlobalTemporaryTablesSchemaDescriptor().getSchemaName().equals(schemaName)) {
return getDeclaredGlobalTemporaryTablesSchemaDescriptor();
}
if (sd == null && raiseError) {
throw StandardException.newException(SQLState.LANG_SCHEMA_DOES_NOT_EXIST, schemaName);
} else {
return sd;
}
}
在这个方法中,先不去深入考虑细节的问题,因为那时Store层的工作。
LCC在默认Schema设定完毕后就完成了自身的初始化,还是回到前面的execute()方法,看一下LCC是怎么返回PreparedStatement的:
public PreparedStatement prepareInternalStatement(SchemaDescriptor compilationSchema, String sqlText,
boolean isForReadOnly, boolean forMetaData) throws StandardException {
if (forMetaData) {// 要保证对MetaData的获取始终在SYS Schema中进行
compilationSchema = getDataDictionary().getSystemSchemaDescriptor();
}
return connFactory.getStatement(compilationSchema, sqlText, isForReadOnly).prepare(this, forMetaData);
}
这个方法最后通过LanguageConnectionFactory返回一个Statement,然后再通过Statement的prepare()方法来返回PreparedStatement。
这里的getStatement()会每次new一个GenericStatement实例,下面还是主要看一下它的prepare()方法吧:
public PreparedStatement prepare(LanguageConnectionContext lcc, boolean forMetaData) throws StandardException {
return prepMinion(lcc, true, (Object[]) null, (SchemaDescriptor) null, forMetaData);
}
内部只有一条调用,这个prepMinion()方法是最终的实现,方法比较长:
private PreparedStatement prepMinion(LanguageConnectionContext lcc, boolean cacheMe, Object[] paramDefaults,
SchemaDescriptor spsSchema, boolean internalSQL) throws StandardException {
long beginTime = 0;
long parseTime = 0;
long bindTime = 0;
long optimizeTime = 0;
long generateTime = 0;
Timestamp beginTimestamp = null;
Timestamp endTimestamp = null;
StatementContext statementContext = null;
// 是不是已经有了一个新的preparedStmt
// 疑问:这个对于每个查询都使用一个新的GeneriStatement的情况是否有必要?
if (preparedStmt != null) {
if (preparedStmt.upToDate())// preparedStmt是不是最新的
return preparedStmt;
}
if (lcc.getOptimizerTrace())
lcc.setOptimizerTraceOutput(getSource() + "\n");
beginTime = getCurrentTimeMillis(lcc);
if (beginTime != 0) {
beginTimestamp = new Timestamp(beginTime);
}
// 隔离级别
prepareIsolationLevel = lcc.getPrepareIsolationLevel();
// preparedStmt的初始化,
boolean foundInCache = false;
if (preparedStmt == null) {
if (cacheMe)// 如果设置缓存了PreparedStament
// 在缓存中获取PreparedStament
preparedStmt = (GenericPreparedStatement) ((GenericLanguageConnectionContext) lcc)
.lookupStatement(this);
if (preparedStmt == null) {
preparedStmt = new GenericPreparedStatement(this);
} else {
foundInCache = true;
}
}
// 如果其他用户也在使用这个preparedStmt,那么不允许对方编译,直到自己编译完毕
synchronized (preparedStmt) {
for (;;) {
if (foundInCache) {
if (preparedStmt.referencesSessionSchema()) {// 如果引用了SESSION Schema
foundInCache = false;
preparedStmt = new GenericPreparedStatement(this);
break;
}
}
if (preparedStmt.upToDate()) {
return preparedStmt;
}
if (!preparedStmt.compilingStatement) {// 如果没有被编译,退出循环
break;
}
try {
preparedStmt.wait();// 等待编译完毕,此方法最后的finally中有notify()
} catch (InterruptedException ie) {
throw StandardException.interrupt(ie);
}
}
preparedStmt.compilingStatement = true;
preparedStmt.setActivationClass(null);
}
try {
HeaderPrintWriter istream = lcc.getLogStatementText() ? Monitor.getStream() : null;
// 如果preparedStmt是GenericStorablePreparedStatement的实例,对nested connection的处理
// 据我猜测,此处应该为对Java存储过程中要使用的nested connection的支持
if (!preparedStmt.isStorable() || lcc.getStatementDepth() == 0) {
statementContext = lcc.pushStatementContext(true, isForReadOnly, getSource(), null, false, 0L);
}
CompilerContext cc = lcc.pushCompilerContext(compilationSchema);
if (prepareIsolationLevel != ExecutionContext.UNSPECIFIED_ISOLATION_LEVEL) {// 隔离级别
cc.setScanIsolationLevel(prepareIsolationLevel);
}
if (internalSQL || (spsSchema != null) && (spsSchema.isSystemSchema())
&& (spsSchema.equals(compilationSchema))) {
// 如果是查询metadate,那么设定任何SQL都是合法的
cc.setReliability(CompilerContext.INTERNAL_SQL_LEGAL);
}
try {
if (istream != null) {
String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), "
+ LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), "
+ LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), "
+ LanguageConnectionContext.drdaStr + lcc.getDrdaID()
+ "), Begin compiling prepared statement: " + getSource() + " :End prepared statement");
}
// 解析器,内部有SQL解析器
Parser p = cc.getParser();
cc.setCurrentDependent(preparedStmt);
// 解析SQL文本,返回查询的树状结构(不是计算机科班,对数据库原理不太了解,这个以后还要自己研究)
StatementNode qt = p.parseStatement(statementText, paramDefaults);
parseTime = getCurrentTimeMillis(lcc);
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("DumpParseTree")) {
qt.treePrint();
}
if (SanityManager.DEBUG_ON("StopAfterParsing")) {
lcc.setLastQueryTree(qt);
throw StandardException.newException(SQLState.LANG_STOP_AFTER_PARSING);
}
}
DataDictionary dataDictionary = lcc.getDataDictionary();
int ddMode = dataDictionary == null ? 0 : dataDictionary.startReading(lcc);
try {
lcc.beginNestedTransaction(true);
// 对Statement进行绑定
qt.bindStatement();
bindTime = getCurrentTimeMillis(lcc);
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("DumpBindTree")) {
qt.treePrint();
}
if (SanityManager.DEBUG_ON("StopAfterBinding")) {
throw StandardException.newException(SQLState.LANG_STOP_AFTER_BINDING);
}
}
if (preparedStmt.referencesSessionSchema(qt)) {
if (foundInCache)
((GenericLanguageConnectionContext) lcc).removeStatement(this);
}
// statement优化
qt.optimizeStatement();
optimizeTime = getCurrentTimeMillis(lcc);
if (istream != null) {
String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), "
+ LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), "
+ LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), "
+ LanguageConnectionContext.drdaStr + lcc.getDrdaID()
+ "), End compiling prepared statement: " + getSource() + " :End prepared statement");
}
}
catch (StandardException se) {
lcc.commitNestedTransaction();
if (istream != null) {
String xactId = lcc.getTransactionExecute().getActiveStateTxIdString();
istream.printlnWithHeader(LanguageConnectionContext.xidStr + xactId + "), "
+ LanguageConnectionContext.lccStr + lcc.getInstanceNumber() + "), "
+ LanguageConnectionContext.dbnameStr + lcc.getDbname() + "), "
+ LanguageConnectionContext.drdaStr + lcc.getDrdaID()
+ "), Error compiling prepared statement: " + getSource() + " :End prepared statement");
}
throw se;
}
finally {
if (dataDictionary != null)
dataDictionary.doneReading(ddMode, lcc);
}
try {
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("DumpOptimizedTree")) {
qt.treePrint();
}
if (SanityManager.DEBUG_ON("StopAfterOptimizing")) {
throw StandardException.newException(SQLState.LANG_STOP_AFTER_OPTIMIZING);
}
}
// 生成Activation Class
GeneratedClass ac = qt.generate(preparedStmt.getByteCodeSaver());
generateTime = getCurrentTimeMillis(lcc);
if (generateTime != 0) {
endTimestamp = new Timestamp(generateTime);
}
if (SanityManager.DEBUG) {
if (SanityManager.DEBUG_ON("StopAfterGenerating")) {
throw StandardException.newException(SQLState.LANG_STOP_AFTER_GENERATING);
}
}
// 将编译后生成的对象设定到preparedStmt
preparedStmt.setConstantAction(qt.makeConstantAction());
preparedStmt.setSavedObjects(cc.getSavedObjects());
preparedStmt.setRequiredPermissionsList(cc.getRequiredPermissionsList());
preparedStmt.setActivationClass(ac);
preparedStmt.setNeedsSavepoint(qt.needsSavepoint());
preparedStmt.setCursorInfo((CursorInfo) cc.getCursorInfo());
preparedStmt.setIsAtomic(qt.isAtomic());
preparedStmt.setExecuteStatementNameAndSchema(qt.executeStatementName(), qt.executeSchemaName());
preparedStmt.setSPSName(qt.getSPSName());
preparedStmt.completeCompile(qt);
preparedStmt.setCompileTimeWarnings(cc.getWarnings());
} catch (StandardException e) {
lcc.commitNestedTransaction();
throw e;
}
if (lcc.getRunTimeStatisticsMode()) {
preparedStmt.setCompileTimeMillis(parseTime - beginTime, // parse time
bindTime - parseTime, // bind time
optimizeTime - bindTime, // optimize time
generateTime - optimizeTime, // generate time
getElapsedTimeMillis(beginTime), beginTimestamp, endTimestamp);
}
} finally {
lcc.popCompilerContext(cc);
}
} catch (StandardException se) {
if (foundInCache)
((GenericLanguageConnectionContext) lcc).removeStatement(this);
throw se;
} finally {
synchronized (preparedStmt) {
preparedStmt.compilingStatement = false;
preparedStmt.notifyAll();
}
}
lcc.commitNestedTransaction();
if (statementContext != null)
lcc.popStatementContext(statementContext, null);
return preparedStmt;
}
在这个方法中,有很多细节没有去仔细的研究,比如解析SQL文本、对Statement进行绑定、statement优化、生成Activation Class等,这些细节就要留待下面去研究了