前段时间事情太多,一直没有继续看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等,这些细节就要留待下面去研究了