上篇说到,通过词法分析、语法分析之后,字符串形式的SQL语句已经变成了树状的表达式。这很重要,相当于将无穷无尽,不可能穷举的SQL语句,变得可以穷举。
拿到这个树状表达式之后,上篇也说到,其作用就是执行(执行客户端的意图,select语句就返回数据库的数据,update就更新数据),这篇我们就来看看在HSQLDB中,如何实现这个过程的。
我们先在Session.java中找一段执行statement的函数,如:
-
public Result executeDirectStatement(String sql) {
-
-
try {
-
Statement cs = compileStatement(sql);
-
Result result = executeCompiledStatement(cs,
-
ValuePool.emptyObjectArray, 0);
-
-
return result;
-
} catch (HsqlException e) {
-
return Result.newErrorResult(e);
-
}
-
}
compileStatement之后,返回了Statement对象,我们今天要分析的就是executeCompiledStatement的内部过程。
转到executeCompiledStatement的内部,我们发现:
public Result executeCompiledStatement(Statement cs, Object[] pvals,
int timeout)
中
r = cs.execute(this);
这个语句直接返回了我们想要的Result的对象,我们要跟踪这个进去,发现是:
public abstract Result execute(Session session);
是Statement的一个抽象方法,也就是假如我们要分析一个查询语句的话,要进入StatementQuery的execute方法中,我们在StatementQuery的基类StatementDMQL中找到了execute方法:
-
public Result execute(Session session) {
-
-
Result result;
-
-
if (targetTable != null && session.isReadOnly()
-
&& !targetTable.isTemp()) {
-
HsqlException e = Error.error(ErrorCode.X_25006);
-
-
return Result.newErrorResult(e);
-
}
-
-
if (isExplain) {
-
return getExplainResult(session);
-
}
-
-
try {
-
if (subqueries.length > 0) {
-
materializeSubQueries(session);
-
}
-
-
result = getResult(session);
-
-
clearStructures(session);
-
} catch (Throwable t) {
-
clearStructures(session);
-
-
result = Result.newErrorResult(t, null);
-
-
result.getException().setStatementType(group, type);
-
}
-
-
return result;
-
}
继续查找getResult的实现(在StatementQuery.java中):
-
Result getResult(Session session) {
-
-
Result result = queryExpression.getResult(session,
-
session.getMaxRows());
-
-
result.setStatement(this);
-
-
return result;
-
}
简单一句queryExpression.getResult就完成,我们知道在上篇之中,最主要的过程就是构建好了queryExpression,现在终于派上用场了。继续往下看:
-
Result getResult(Session session, int maxRows) {
-
-
if (isRecursive) {
-
return getResultRecursive(session);
-
}
-
-
int currentMaxRows = unionType == UNION_ALL ? maxRows
-
: 0;
-
Result first = leftQueryExpression.getResult(session, currentMaxRows);
-
RowSetNavigatorData navigator =
-
(RowSetNavigatorData) first.getNavigator();
-
Result second = rightQueryExpression.getResult(session,
-
currentMaxRows);
-
RowSetNavigatorData rightNavigator =
-
(RowSetNavigatorData) second.getNavigator();
-
-
if (unionCorresponding) {
-
RowSetNavigatorData rowSet;
-
boolean memory =
-
session.resultMaxMemoryRows == 0
-
|| (navigator.getSize() < session.resultMaxMemoryRows
-
&& rightNavigator.getSize() < session.resultMaxMemoryRows);
-
-
if (memory) {
-
rowSet = new RowSetNavigatorData(session, this);
-
} else {
-
rowSet = new RowSetNavigatorDataTable(session, this);
-
}
-
-
rowSet.copy(navigator, leftQueryExpression.unionColumnMap);
-
navigator.release();
-
-
navigator = rowSet;
-
-
first.setNavigator(navigator);
-
-
first.metaData = this.getMetaData();
-
-
if (memory) {
-
rowSet = new RowSetNavigatorData(session, this);
-
} else {
-
rowSet = new RowSetNavigatorDataTable(session, this);
-
}
-
-
rowSet.copy(rightNavigator, rightQueryExpression.unionColumnMap);
-
rightNavigator.release();
-
-
rightNavigator = rowSet;
-
}
-
-
switch (unionType) {
-
-
case UNION :
-
navigator.union(session, rightNavigator);
-
break;
-
-
case UNION_ALL :
-
navigator.unionAll(session, rightNavigator);
-
break;
-
-
case INTERSECT :
-
navigator.intersect(session, rightNavigator);
-
break;
-
-
case INTERSECT_ALL :
-
navigator.intersectAll(session, rightNavigator);
-
break;
-
-
case EXCEPT :
-
navigator.except(session, rightNavigator);
-
break;
-
-
case EXCEPT_ALL :
-
navigator.exceptAll(session, rightNavigator);
-
break;
-
-
default :
-
throw Error.runtimeError(ErrorCode.U_S0500, "QueryExpression");
-
}
-
-
if (sortAndSlice.hasOrder()) {
-
navigator.sortOrderUnion(session, sortAndSlice);
-
}
-
-
if (sortAndSlice.hasLimit()) {
-
int[] limits = sortAndSlice.getLimits(session, this, maxRows);
-
-
navigator.trim(limits[0], limits[1]);
-
}
-
-
navigator.reset();
-
-
return first;
-
}
这里我们大概描述一下这个过程:
1、这是sql1 union sql2的处理,其中的leftQueryExpression就是sql1的表达式形式,rightQueryExpression就是sql2的表达式形式
2、返回的Result有一个getNavigator方法,可以通过getNavigator取到数据库中的数据
3、这里根据left、right的集合关系(交集、并集、补集等),返回其最终结果集
这里有一个困惑的地方,看红色的部分,我们发现leftQueryExpression.getResult的定义,又回到了getResult这个函数本身了——如果就是这么递归调用的话,岂不是死循环?
秘密在哪呢?其实就在QueryExpression还有子类,所以在leftQueryExpression调用的时候,其实是调用了子类的getResult方法。
好,那我们继续跟到getResult(QuerySpecification.java)方法:
-
Result getResult(Session session, int maxrows) {
-
-
//todo single row
-
Result r = getSingleResult(session, maxrows);
-
-
r.getNavigator().reset();
-
-
return r;
-
}
r.getNavigator().reset()是将当前记录,设置为初始状态。跟踪getSingleResult:
-
private Result getSingleResult(Session session, int maxRows) {
-
-
int[] limits = sortAndSlice.getLimits(session, this, maxRows);
-
Result r = buildResult(session, limits);
-
RowSetNavigatorData navigator = (RowSetNavigatorData) r.getNavigator();
-
-
if (isDistinctSelect) {
-
navigator.removeDuplicates(session);
-
}
-
-
if (sortAndSlice.hasOrder()) {
-
navigator.sortOrder(session);
-
}
-
-
if (limits != SortAndSlice.defaultLimits
-
&& !sortAndSlice.skipFullResult) {
-
navigator.trim(limits[0], limits[1]);
-
}
-
-
return r;
-
}
在这里我们又看到r.getNavigator(),可见对于查询语句来说,这是一个很重要的东西——其实就是数据导航器,我们的查询语句不就是查询数据么。所以也可以想见在buildResult中,我们可能要关注一下,setNavigator。
这里除了buildResult的其它意思,就是去重复数据、排序、跳过X项数据取X项数据。
继续进入buildResult的过程:
一个巨型函数,不过好在里面有一段比较短的,且直接返回了Result:
-
if (this.isSimpleCount) {
-
Object[] data = new Object[indexLimitData];
-
Table table = rangeVariables[0].getTable();
-
-
table.materialise(session);
-
-
PersistentStore store = table.getRowStore(session);
-
long count = store.elementCount(session);
-
-
data[0] = data[indexStartAggregates] = ValuePool.getLong(count);
-
-
navigator.add(data);
-
-
return result;
-
}
这里是简单查询单个表数据行数,
1、通过rangeVariables取到表数据
2、materialise准备持久化对象(也就是PersistentStore )至Session
3、table.getRowStore取到持久化对象
4、store.elementCount取到表的行数
5、navigator.add,将获取到的数据,添加到navigator就算完成使命
虽然命令比较简单,但大致流程可以看出来,我们猜测下面的其它查询操作也是类似的,我们来验证一下这个猜测,我们看buildResult的其它部分。
这里就不贴代码了,梳理一下,
1、从rangeVariables中取出多个表的数据(通过RangeIterator.next)
2、通过exprColumns[i].getValue(session)获得普通列的值,通过exprColumns[i].getAggregatedValue取得聚集列(如sum、avg)
3、在第二步取出的Object[]数组,是表示取出来的数据库的值,navigator.add, 再通过设置result.setNavigator(navigator)
这里想说的呢,就是表达式树里的内容,就是在这样一个过程中起作用了,比如rangeVariables,比如exprColumns。
就是要注意的一个地方的是:
-
if (it.next()) {
-
if (currentIndex < rangeVariables.length - 1) {
-
currentIndex++;
-
-
continue;
-
}
-
} else {
-
it.reset();
-
-
currentIndex--;
-
-
continue;
-
}
多个表join查询的时候,这里初看一下,是一个个table这样next、next一行行读取,其实这段代码是一个表next,再换下一个表next,直到所有表的当前行都取出来了,然后再取数据(如果是聚集函数列,每来一个行做一下update)。
it.next又是如何实现的,跟物理的东西又如何交互呢?
it.next是一个接口方法,我们转到RangeIteratorMain(因为之前是new的RangeIteratorMain的对象):
-
public boolean next() {
-
-
while (condIndex < conditions.length) {
-
if (isBeforeFirst) {
-
isBeforeFirst = false;
-
-
initialiseIterator();
-
}
-
-
boolean result = findNext();
-
-
if (result) {
-
return true;
-
}
-
-
reset();
-
-
condIndex++;
-
}
-
-
condIndex = 0;
-
-
return false;
-
}
initialiseIterator挺关键的:
-
protected void initialiseIterator() {
-
-
if (condIndex == 0) {
-
hasLeftOuterRow = rangeVar.isLeftJoin;
-
}
-
-
if (conditions[condIndex].isFalse) {
-
it = conditions[condIndex].rangeIndex.emptyIterator();
-
-
return;
-
}
-
-
rangeVar.rangeTable.materialiseCorrelated(session);
-
-
if (conditions[condIndex].indexCond == null) {
-
if (conditions[condIndex].reversed) {
-
it = conditions[condIndex].rangeIndex.lastRow(session,
-
store, rangeVar.indexDistinctCount);
-
} else {
-
it = conditions[condIndex].rangeIndex.firstRow(session,
-
store, rangeVar.indexDistinctCount);
-
}
-
} else {
-
getFirstRow();
-
-
if (!conditions[condIndex].isJoin) {
-
hasLeftOuterRow = false;
-
}
-
}
-
}
在这里,我们看到查询条件conditions起作用了,通过查询条件我们找到了索引(就像前面我们通过rangeVariables可以找到table一样)。再往下,通过索引从存储上找到数据。
那么next,取到第一个节点之后,也就继续通过索引查找下一个数据。
索引里如何实现的,我们下一篇再谈。
现在我们可以总结一下整个SQL编译的全部过程:
1、通过Scanner进行词法分析
2、通过Parser进行语法分析
3、通过Statement(其实主要还是语法树上的每个表达式节点)进行语义分析——也就是实现Statement的目的
最终,statement转换到了数据库基本对象(如Table, Index)等的操作了。
[相关源码文件]
Statement及其子类
Expression及其子类
navigator包下面的类——主要是来查找table、index的数据
阅读(1333) | 评论(0) | 转发(0) |