hsqldb源码分析系列5 查询引擎之查询操作
执行过程先分析select语句的解析过程:
select语句的解析集中在ParserDQL类的XreadQueryExpression方法中,其中XreadQueryExpressionBody select除了limit,offset,fetch关键字外的其他条件解析,XreadOrderByExpression是limit,offset,fetch,ordre by的解析,我们分析简单的select * from user where * order by * limit 2 offset 5 这种语句,对于嵌套子查询和联合查询待会简单过下。
QueryExpression queryExpression = XreadQueryExpressionBody(); SortAndSlice sortAndSlice = XreadOrderByExpression(); if (queryExpression.sortAndSlice == null) { queryExpression.addSortAndSlice(sortAndSlice); } else { if (queryExpression.sortAndSlice.hasLimit()) { if (sortAndSlice.hasLimit()) { throw Error.error(ErrorCode.X_42549); } for (int i = 0; i < sortAndSlice.exprList.size(); i++) { Expression e = (Expression) sortAndSlice.exprList.get(i); queryExpression.sortAndSlice.addOrderExpression(e); } } else { queryExpression.addSortAndSlice(sortAndSlice); } }
selelct top 20 和limit 20这种条件先查询,XreadTopOrLimit读取top和limit的值并作为一个条件放到SortAndSlice对象中,XreadFromClause是读取嵌套查询或者联合查询的,readWhereGroupHaving是处理where条件和group by,having的
QuerySpecification XreadQuerySpecification() { QuerySpecification select = XreadSelect(); if (!select.isValueList) { XreadTableExpression(select); } return select; } void XreadTableExpression(QuerySpecification select) { XreadFromClause(select); readWhereGroupHaving(select); }
如果有DISTINCT,则设置isDistinctSelect为true,while 循环处理每个token,XreadValueExpression 是读取对于返回的列名(包括对改列进行的一些加减等计算操作)
QuerySpecification XreadSelect() { QuerySpecification select = new QuerySpecification(compileContext); readThis(Tokens.SELECT); if (token.tokenType == Tokens.TOP || token.tokenType == Tokens.LIMIT) { SortAndSlice sortAndSlice = XreadTopOrLimit(); if (sortAndSlice != null) { select.addSortAndSlice(sortAndSlice); } } if (token.tokenType == Tokens.DISTINCT) { select.isDistinctSelect = true; read(); } else if (token.tokenType == Tokens.ALL) { read(); } while (true) { Expression e = XreadValueExpression(); if (token.tokenType == Tokens.AS) { read(); checkIsNonCoreReservedIdentifier(); } if (isNonCoreReservedIdentifier()) { e.setAlias(HsqlNameManager.getSimpleName(token.tokenString, isDelimitedIdentifier())); read(); } select.addSelectColumnExpression(e); if (token.tokenType == Tokens.FROM) { break; } if (token.tokenType == Tokens.INTO) { break; } if (readIfThis(Tokens.COMMA)) { continue; } if (token.tokenType == Tokens.CLOSEBRACKET || token.tokenType == Tokens.X_ENDPARSE) { if (database.sqlSyntaxMss || database.sqlSyntaxMys || database.sqlSyntaxPgs) { Expression[] exprList = new Expression[select.exprColumnList.size()]; select.exprColumnList.toArray(exprList); Expression valueList = new Expression(OpTypes.VALUELIST, exprList); for (int i = 0; i < valueList.nodes.length; i++) { if (valueList.nodes[i].opType != OpTypes.ROW) { valueList.nodes[i] = new Expression(OpTypes.ROW, new Expression[]{ valueList.nodes[i] }); } } compileContext.incrementDepth(); TableDerived td = prepareSubqueryTable(valueList, OpTypes.VALUELIST); select = new QuerySpecification(session, td, compileContext, true); compileContext.decrementDepth(); return select; } } throw unexpectedToken(); } return select; }
XreadAllTypesCommonValueExpression读取每列的一些四则运算,然后如果有括号还得做优先级处理
Expression XreadValueExpression() { Expression e = XreadAllTypesCommonValueExpression(true); if (token.tokenType == Tokens.LEFTBRACKET) { read(); Expression e1 = XreadNumericValueExpression(); readThis(Tokens.RIGHTBRACKET); e = new ExpressionAccessor(e, e1); } return e; }
这个是先处理加减操作,乘除操作优先级高会在XreadTerm中先封装成一个Expression对象,然后在作为操作放到当前加减操作的Expression中,也就是Expression中可以嵌套多级Expression对象,
Expression XreadNumericValueExpression() { Expression e = XreadTerm(); while (true) { int type; if (token.tokenType == Tokens.PLUS) { type = OpTypes.ADD; } else if (token.tokenType == Tokens.MINUS) { type = OpTypes.SUBTRACT; } else { break; } read(); Expression a = e; e = XreadTerm(); e = new ExpressionArithmetic(type, a, e); } return e; }
XreadFactor读取常量部分
Expression XreadTerm() { Expression e = XreadFactor(); int type; while (true) { if (token.tokenType == Tokens.ASTERISK) { type = OpTypes.MULTIPLY; } else if (token.tokenType == Tokens.DIVIDE) { type = OpTypes.DIVIDE; } else { break; } read(); Expression a = e; e = XreadFactor(); if (e == null) { throw unexpectedToken(); } e = new ExpressionArithmetic(type, a, e); } return e; }
prepareSubqueryTable 子查询就不跟踪了,
预编译就是把sql的解析过程缓存起来,保存在statementManger中,下次就不用再解析编译了。
select的字段内容如下:select atime, adate from dttest where adate < '16:44:31'
返回字段只有atime,别名为adate
我们看看编译后的表达式内容 第一个node表示是where语句第一个条件左边的表达式,adate optype=2表示VALUE,也就是字段值,第一个node的optype为1,表示常量。而整个Expression的optype是44,表示第一个node小于第二个node的值,当然还有每个node的类型
看执行过程了,这sql有点问题把where语句的adate改为atime,要不然报日期格式不对,
metaData表示返回的列类型
回到QuerySpecification类的buildResult(Session session, int[] limits) 返回limits表示limit,offset,top的参数,这里没有指定,则默认取所有记录,isSipleCount是取聚合的而一个值的操作,返回总数之类的操作,
我们这里是执行到这里的查询逻辑,这里取出我们需要的列值
在这里执行索引查询where条件查询it.next()
RangeIterator it = rangeIterators[currentIndex]; if (it.next()) { if (currentIndex < rangeVariables.length - 1) { currentIndex++; continue; } } else { it.reset(); currentIndex--; continue; }
it next方法保存当前indexAVL树中查找到的节点,然后查找下一个满足条件的记录
在RangeVariable执行这个next操作,操作结果就保存在session,后面从session中就能获取数据了。 data[i] = exprColumns[i].getValue(session); 在这里获取下一个数据。
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; }
查询索引的过程 首先在AVL树中查询满足条件的第一个结果节点,然后findnext就在这个节点上查找下一个节点
indexAVL类的findNode
public RowIterator findFirstRow(Session session, PersistentStore store, Object[] rowdata, int matchCount, int distinctCount, int compareType, boolean reversed, boolean[] map) { if (compareType == OpTypes.MAX) { return lastRow(session, store); } NodeAVL node = findNode(session, store, rowdata, defaultColMap, matchCount, compareType, TransactionManager.ACTION_READ, reversed); if (node == null) { return emptyIterator; } return new IndexRowIterator(session, store, this, node, distinctCount, false, reversed); }
/** * Finds a match with a row from a different table * * @param session Session * @param store PersistentStore * @param rowdata array containing data for the index columns * @param rowColMap map of the data to columns * @param fieldCount int * @param compareType int * @param readMode int * @return matching node or null */ NodeAVL findNode(Session session, PersistentStore store, Object[] rowdata, int[] rowColMap, int fieldCount, int compareType, int readMode, boolean reversed) { readLock.lock(); try { NodeAVL x = getAccessor(store); NodeAVL n = null; NodeAVL result = null; Row currentRow = null; if (compareType != OpTypes.EQUAL && compareType != OpTypes.IS_NULL) { fieldCount--; } while (x != null) { currentRow = x.getRow(store); int i = 0; if (fieldCount > 0) { i = compareRowNonUnique(session, currentRow.getData(), rowdata, rowColMap, fieldCount); } if (i == 0) { switch (compareType) { case OpTypes.IS_NULL : case OpTypes.EQUAL : { result = x; n = x.getLeft(store); break; } case OpTypes.NOT : case OpTypes.GREATER : { i = compareObject(session, currentRow.getData(), rowdata, rowColMap, fieldCount); if (i <= 0) { n = x.getRight(store); } else { result = x; n = x.getLeft(store); } break; } case OpTypes.GREATER_EQUAL : { i = compareObject(session, currentRow.getData(), rowdata, rowColMap, fieldCount); if (i < 0) { n = x.getRight(store); } else { result = x; n = x.getLeft(store); } break; } case OpTypes.SMALLER : { i = compareObject(session, currentRow.getData(), rowdata, rowColMap, fieldCount); if (i < 0) { result = x; n = x.getRight(store); } else { n = x.getLeft(store); } break; } case OpTypes.SMALLER_EQUAL : { i = compareObject(session, currentRow.getData(), rowdata, rowColMap, fieldCount); if (i <= 0) { result = x; n = x.getRight(store); } else { n = x.getLeft(store); } break; } default : Error.runtimeError(ErrorCode.U_S0500, "Index"); } } else if (i < 0) { n = x.getRight(store); } else if (i > 0) { n = x.getLeft(store); } if (n == null) { break; } x = n; } // MVCC 190 if (session == null) { return result; } while (result != null) { currentRow = result.getRow(store); if (session.database.txManager.canRead(session, currentRow, readMode, colIndex)) { break; } result = reversed ? last(store, result) : next(store, result); if (result == null) { break; } currentRow = result.getRow(store); if (fieldCount > 0 && compareRowNonUnique( session, currentRow.getData(), rowdata, rowColMap, fieldCount) != 0) { result = null; break; } } return result; } finally { readLock.unlock(); } }
/** * Advances to the next available value. <p> * * @return true if a next value is available upon exit */ private boolean findNext() { boolean result = false; while (true) { currentRow = it.getNextRow(); if (currentRow == null) { break; } currentData = currentRow.getData(); if (conditions[condIndex].terminalCondition != null && !conditions[condIndex].terminalCondition .testCondition(session)) { break; } if (conditions[condIndex].indexEndCondition != null && !conditions[condIndex].indexEndCondition .testCondition(session)) { if (!conditions[condIndex].isJoin) { hasLeftOuterRow = false; } break; } if (joinConditions[condIndex].nonIndexCondition != null && !joinConditions[condIndex].nonIndexCondition .testCondition(session)) { continue; } if (whereConditions[condIndex].nonIndexCondition != null && !whereConditions[condIndex].nonIndexCondition .testCondition(session)) { hasLeftOuterRow = false; addFoundRow(); continue; } Expression e = conditions[condIndex].excludeConditions; if (e != null && e.testCondition(session)) { continue; } addFoundRow(); hasLeftOuterRow = false; return true; } it.release(); currentRow = null; currentData = rangeVar.emptyData; if (hasLeftOuterRow && condIndex == conditions.length - 1) { result = (whereConditions[condIndex].nonIndexCondition == null || whereConditions[condIndex].nonIndexCondition .testCondition(session)); hasLeftOuterRow = false; } return result; } private void addFoundRow() { if (rangeVar.isRightJoin) { lookup.add(currentRow.getPos()); } }
比如一个notequal操作会在这里的testCondition判断是否符合