| /* Copyright (c) 2001-2010, The HSQL Development Group |
| * All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are met: |
| * |
| * Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * Redistributions in binary form must reproduce the above copyright notice, |
| * this list of conditions and the following disclaimer in the documentation |
| * and/or other materials provided with the distribution. |
| * |
| * Neither the name of the HSQL Development Group nor the names of its |
| * contributors may be used to endorse or promote products derived from this |
| * software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" |
| * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| * ARE DISCLAIMED. IN NO EVENT SHALL HSQL DEVELOPMENT GROUP, HSQLDB.ORG, |
| * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
| * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
| * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
| * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| |
| package org.hsqldb; |
| |
| import org.hsqldb.HsqlNameManager.SimpleName; |
| import org.hsqldb.ParserDQL.CompileContext; |
| import org.hsqldb.error.Error; |
| import org.hsqldb.error.ErrorCode; |
| import org.hsqldb.index.Index; |
| import org.hsqldb.lib.HashMap; |
| import org.hsqldb.lib.HashMappedList; |
| import org.hsqldb.lib.HashSet; |
| import org.hsqldb.lib.HsqlArrayList; |
| import org.hsqldb.lib.HsqlList; |
| import org.hsqldb.lib.OrderedHashSet; |
| import org.hsqldb.lib.OrderedIntHashSet; |
| import org.hsqldb.navigator.RangeIterator; |
| import org.hsqldb.navigator.RowIterator; |
| import org.hsqldb.persist.PersistentStore; |
| import org.hsqldb.store.ValuePool; |
| import org.hsqldb.types.Type; |
| |
| /** |
| * Metadata for range variables, including conditions. |
| * |
| * @author Fred Toussi (fredt@users dot sourceforge.net) |
| * @version 2.0.0 |
| * @since 1.9.0 |
| */ |
| public final class RangeVariable implements Cloneable { |
| |
| static final RangeVariable[] emptyArray = new RangeVariable[]{}; |
| |
| // |
| final Table rangeTable; |
| final SimpleName tableAlias; |
| private OrderedHashSet columnAliases; |
| private SimpleName[] columnAliasNames; |
| private OrderedHashSet columnNames; |
| OrderedHashSet namedJoinColumns; |
| HashMap namedJoinColumnExpressions; |
| private Object[] emptyData; |
| boolean[] columnsInGroupBy; |
| boolean hasKeyedColumnInGroupBy; |
| boolean[] usedColumns; |
| boolean[] updatedColumns; |
| |
| // |
| RangeVariableConditions[] joinConditions; |
| RangeVariableConditions[] whereConditions; |
| int subRangeCount; |
| |
| // non-index conditions |
| Expression joinCondition; |
| |
| // |
| boolean isLeftJoin; // table joined with LEFT / FULL OUTER JOIN |
| boolean isRightJoin; // table joined with RIGHT / FULL OUTER JOIN |
| boolean isBoundary; |
| |
| // |
| int level; |
| |
| // |
| int rangePosition; |
| |
| // |
| int parsePosition; |
| |
| // for variable and argument lists |
| HashMappedList variables; |
| |
| // variable v.s. argument |
| boolean isVariable; |
| |
| // |
| boolean isGenerated; |
| |
| RangeVariable(HashMappedList variables, boolean isVariable) { |
| |
| this.variables = variables; |
| this.isVariable = isVariable; |
| rangeTable = null; |
| tableAlias = null; |
| emptyData = null; |
| columnsInGroupBy = null; |
| usedColumns = null; |
| joinConditions = new RangeVariableConditions[]{ |
| new RangeVariableConditions(this, true) }; |
| whereConditions = new RangeVariableConditions[]{ |
| new RangeVariableConditions(this, false) }; |
| } |
| |
| RangeVariable(Table table, SimpleName alias, OrderedHashSet columnList, |
| SimpleName[] columnNameList, CompileContext compileContext) { |
| |
| rangeTable = table; |
| tableAlias = alias; |
| columnAliases = columnList; |
| columnAliasNames = columnNameList; |
| joinConditions = new RangeVariableConditions[]{ |
| new RangeVariableConditions(this, true) }; |
| whereConditions = new RangeVariableConditions[]{ |
| new RangeVariableConditions(this, false) }; |
| |
| compileContext.registerRangeVariable(this); |
| |
| SubQuery subQuery = rangeTable.getSubQuery(); |
| |
| if (subQuery == null || subQuery.isResolved()) { |
| setRangeTableVariables(); |
| } |
| } |
| |
| RangeVariable(Table table, int position) { |
| |
| rangeTable = table; |
| tableAlias = null; |
| emptyData = rangeTable.getEmptyRowData(); |
| columnsInGroupBy = rangeTable.getNewColumnCheckList(); |
| usedColumns = rangeTable.getNewColumnCheckList(); |
| rangePosition = position; |
| joinConditions = new RangeVariableConditions[]{ |
| new RangeVariableConditions(this, true) }; |
| whereConditions = new RangeVariableConditions[]{ |
| new RangeVariableConditions(this, false) }; |
| } |
| |
| public void setRangeTableVariables() { |
| |
| if (columnAliasNames != null |
| && rangeTable.getColumnCount() != columnAliasNames.length) { |
| throw Error.error(ErrorCode.X_42593); |
| } |
| |
| emptyData = rangeTable.getEmptyRowData(); |
| columnsInGroupBy = rangeTable.getNewColumnCheckList(); |
| usedColumns = rangeTable.getNewColumnCheckList(); |
| joinConditions[0].rangeIndex = rangeTable.getPrimaryIndex(); |
| } |
| |
| public RangeVariable duplicate() { |
| |
| RangeVariable r = null; |
| |
| try { |
| r = (RangeVariable) super.clone(); |
| } catch (CloneNotSupportedException ex) { |
| throw Error.runtimeError(ErrorCode.U_S0500, "RangeVariable"); |
| } |
| |
| r.resetConditions(); |
| |
| return r; |
| } |
| |
| void setJoinType(boolean isLeft, boolean isRight) { |
| |
| isLeftJoin = isLeft; |
| isRightJoin = isRight; |
| |
| if (isRightJoin) { |
| whereConditions[0].rangeIndex = rangeTable.getPrimaryIndex(); |
| } |
| } |
| |
| public void addNamedJoinColumns(OrderedHashSet columns) { |
| namedJoinColumns = columns; |
| } |
| |
| public void addColumn(int columnIndex) { |
| usedColumns[columnIndex] = true; |
| } |
| |
| public void addAllColumns() {} |
| |
| void addNamedJoinColumnExpression(String name, Expression e) { |
| |
| if (namedJoinColumnExpressions == null) { |
| namedJoinColumnExpressions = new HashMap(); |
| } |
| |
| namedJoinColumnExpressions.put(name, e); |
| } |
| |
| ExpressionColumn getColumnExpression(String name) { |
| |
| return namedJoinColumnExpressions == null ? null |
| : (ExpressionColumn) namedJoinColumnExpressions |
| .get(name); |
| } |
| |
| Table getTable() { |
| return rangeTable; |
| } |
| |
| boolean hasIndexCondition() { |
| return joinConditions.length == 1 |
| && joinConditions[0].indexedColumnCount > 0; |
| } |
| |
| /** |
| * Used for sort |
| */ |
| Index getSortIndex() { |
| |
| if (joinConditions.length == 1) { |
| return joinConditions[0].rangeIndex; |
| } else { |
| return null; |
| } |
| } |
| |
| /** |
| * Used for sort |
| */ |
| boolean setSortIndex(Index index, boolean reversed) { |
| |
| if (joinConditions.length == 1) { |
| if (joinConditions[0].indexedColumnCount == 0) { |
| joinConditions[0].rangeIndex = index; |
| joinConditions[0].reversed = reversed; |
| |
| return true; |
| } |
| } |
| |
| return false; |
| } |
| |
| boolean reverseOrder() { |
| |
| joinConditions[0].reverseIndexCondition(); |
| |
| return true; |
| } |
| |
| public OrderedHashSet getColumnNames() { |
| |
| if (columnNames == null) { |
| columnNames = new OrderedHashSet(); |
| |
| rangeTable.getColumnNames(this.usedColumns, columnNames); |
| } |
| |
| return columnNames; |
| } |
| |
| public OrderedHashSet getUniqueColumnNameSet() { |
| |
| OrderedHashSet set = new OrderedHashSet(); |
| |
| if (columnAliases != null) { |
| set.addAll(columnAliases); |
| |
| return set; |
| } |
| |
| for (int i = 0; i < rangeTable.columnList.size(); i++) { |
| String name = rangeTable.getColumn(i).getName().name; |
| boolean added = set.add(name); |
| |
| if (!added) { |
| throw Error.error(ErrorCode.X_42578, name); |
| } |
| } |
| |
| return set; |
| } |
| |
| /** |
| * Retruns index for column |
| * |
| * @param columnName name of column |
| * @return int index or -1 if not found |
| */ |
| public int findColumn(String columnName) { |
| |
| if (namedJoinColumnExpressions != null |
| && namedJoinColumnExpressions.containsKey(columnName)) { |
| return -1; |
| } |
| |
| if (variables != null) { |
| return variables.getIndex(columnName); |
| } else if (columnAliases != null) { |
| return columnAliases.getIndex(columnName); |
| } else { |
| return rangeTable.findColumn(columnName); |
| } |
| } |
| |
| ColumnSchema getColumn(String columnName) { |
| |
| int index = findColumn(columnName); |
| |
| return index < 0 ? null |
| : rangeTable.getColumn(index); |
| } |
| |
| ColumnSchema getColumn(int i) { |
| |
| if (variables == null) { |
| return rangeTable.getColumn(i); |
| } else { |
| return (ColumnSchema) variables.get(i); |
| } |
| } |
| |
| String getColumnAlias(int i) { |
| |
| SimpleName name = getColumnAliasName(i); |
| |
| return name.name; |
| } |
| |
| public SimpleName getColumnAliasName(int i) { |
| |
| if (columnAliases == null) { |
| return rangeTable.getColumn(i).getName(); |
| } else { |
| return columnAliasNames[i]; |
| } |
| } |
| |
| boolean hasColumnAlias() { |
| return columnAliases != null; |
| } |
| |
| String getTableAlias() { |
| |
| SimpleName name = getTableAliasName(); |
| |
| return name.name; |
| } |
| |
| SimpleName getTableAliasName() { |
| return tableAlias == null ? rangeTable.getName() |
| : tableAlias; |
| } |
| |
| boolean resolvesTableName(ExpressionColumn e) { |
| |
| if (e.tableName == null) { |
| return true; |
| } |
| |
| if (e.schema == null) { |
| if (tableAlias == null) { |
| if (e.tableName.equals(rangeTable.getName().name)) { |
| return true; |
| } |
| } else if (e.tableName.equals(tableAlias.name)) { |
| return true; |
| } |
| } else { |
| if (tableAlias == null) { |
| if (e.tableName.equals(rangeTable.getName().name) |
| && e.schema.equals(rangeTable.getSchemaName().name)) { |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| public boolean resolvesTableName(String name) { |
| |
| if (name == null) { |
| return true; |
| } |
| |
| if (tableAlias == null) { |
| if (name.equals(rangeTable.getName().name)) { |
| return true; |
| } |
| } else if (name.equals(tableAlias.name)) { |
| return true; |
| } |
| |
| return false; |
| } |
| |
| boolean resolvesSchemaName(String name) { |
| |
| if (name == null) { |
| return true; |
| } |
| |
| if (tableAlias != null) { |
| return false; |
| } |
| |
| return name.equals(rangeTable.getSchemaName().name); |
| } |
| |
| /** |
| * Add all columns to a list of expressions |
| */ |
| void addTableColumns(HsqlArrayList exprList) { |
| |
| if (namedJoinColumns != null) { |
| int count = exprList.size(); |
| int position = 0; |
| |
| for (int i = 0; i < count; i++) { |
| Expression e = (Expression) exprList.get(i); |
| String columnName = e.getColumnName(); |
| |
| if (namedJoinColumns.contains(columnName)) { |
| if (position != i) { |
| exprList.remove(i); |
| exprList.add(position, e); |
| } |
| |
| e = getColumnExpression(columnName); |
| |
| exprList.set(position, e); |
| |
| position++; |
| } |
| } |
| } |
| |
| addTableColumns(exprList, exprList.size(), namedJoinColumns); |
| } |
| |
| /** |
| * Add all columns to a list of expressions |
| */ |
| int addTableColumns(HsqlArrayList expList, int position, HashSet exclude) { |
| |
| Table table = getTable(); |
| int count = table.getColumnCount(); |
| |
| for (int i = 0; i < count; i++) { |
| ColumnSchema column = table.getColumn(i); |
| String columnName = columnAliases == null ? column.getName().name |
| : (String) columnAliases |
| .get(i); |
| |
| if (exclude != null && exclude.contains(columnName)) { |
| continue; |
| } |
| |
| Expression e = new ExpressionColumn(this, i); |
| |
| expList.add(position++, e); |
| } |
| |
| return position; |
| } |
| |
| void addTableColumns(Expression expression, HashSet exclude) { |
| |
| HsqlArrayList list = new HsqlArrayList(); |
| Table table = getTable(); |
| int count = table.getColumnCount(); |
| |
| for (int i = 0; i < count; i++) { |
| ColumnSchema column = table.getColumn(i); |
| String columnName = columnAliases == null ? column.getName().name |
| : (String) columnAliases |
| .get(i); |
| |
| if (exclude != null && exclude.contains(columnName)) { |
| continue; |
| } |
| |
| Expression e = new ExpressionColumn(this, i); |
| |
| list.add(e); |
| } |
| |
| Expression[] nodes = new Expression[list.size()]; |
| |
| list.toArray(nodes); |
| |
| expression.nodes = nodes; |
| } |
| |
| /** |
| * Removes reference to Index to avoid possible memory leaks after alter |
| * table or drop index |
| */ |
| void setForCheckConstraint() { |
| joinConditions[0].rangeIndex = null; |
| } |
| |
| /** |
| * used before condition processing |
| */ |
| Expression getJoinCondition() { |
| return joinCondition; |
| } |
| |
| void addJoinCondition(Expression e) { |
| joinCondition = ExpressionLogical.andExpressions(joinCondition, e); |
| } |
| |
| void resetConditions() { |
| |
| Index index = joinConditions[0].rangeIndex; |
| |
| joinConditions = new RangeVariableConditions[]{ |
| new RangeVariableConditions(this, true) }; |
| joinConditions[0].rangeIndex = index; |
| whereConditions = new RangeVariableConditions[]{ |
| new RangeVariableConditions(this, false) }; |
| } |
| |
| OrderedHashSet getSubqueries() { |
| |
| OrderedHashSet set = null; |
| |
| if (joinCondition != null) { |
| set = joinCondition.collectAllSubqueries(set); |
| } |
| |
| if (rangeTable instanceof TableDerived) { |
| QueryExpression baseQueryExpression = |
| ((TableDerived) rangeTable).getQueryExpression(); |
| |
| if (((TableDerived) rangeTable).view != null) { |
| if (set == null) { |
| set = new OrderedHashSet(); |
| } |
| |
| set.addAll(((TableDerived) rangeTable).view.getSubqueries()); |
| } else if (baseQueryExpression == null) { |
| set = OrderedHashSet.add(set, rangeTable.getSubQuery()); |
| } else { |
| OrderedHashSet temp = baseQueryExpression.getSubqueries(); |
| |
| set = OrderedHashSet.addAll(set, temp); |
| set = OrderedHashSet.add(set, rangeTable.getSubQuery()); |
| } |
| } |
| |
| return set; |
| } |
| |
| public void replaceColumnReference(RangeVariable range, |
| Expression[] list) { |
| |
| if (joinCondition != null) { |
| joinCondition.replaceColumnReferences(range, list); |
| } |
| } |
| |
| public void replaceRangeVariables(RangeVariable[] ranges, |
| RangeVariable[] newRanges) { |
| |
| if (joinCondition != null) { |
| joinCondition.replaceRangeVariables(ranges, newRanges); |
| } |
| } |
| |
| public void resolveRangeTable(Session session, |
| RangeVariable[] rangeVariables, |
| int rangeCount) { |
| |
| Table table = rangeTable; |
| SubQuery subQuery = table.getSubQuery(); |
| |
| if (subQuery != null && !subQuery.isResolved()) { |
| if (subQuery.dataExpression != null) { |
| HsqlList unresolved = |
| subQuery.dataExpression.resolveColumnReferences( |
| RangeVariable.emptyArray, null); |
| |
| if (unresolved != null) { |
| unresolved = |
| subQuery.dataExpression.resolveColumnReferences( |
| rangeVariables, rangeCount, null, true); |
| } |
| |
| if (unresolved != null) { |
| throw Error.error( |
| ErrorCode.X_42501, |
| ((Expression) unresolved.get(0)).getSQL()); |
| } |
| |
| subQuery.dataExpression.resolveTypes(session, null); |
| setRangeTableVariables(); |
| } |
| |
| if (subQuery.queryExpression != null) { |
| subQuery.queryExpression.resolveReferences(session); |
| |
| HsqlList list = |
| subQuery.queryExpression.getUnresolvedExpressions(); |
| |
| // todo resove against i ranges |
| HsqlList unresolved = |
| Expression.resolveColumnSet(rangeVariables, rangeCount, |
| list, null); |
| |
| if (unresolved != null) { |
| throw Error.error( |
| ErrorCode.X_42501, |
| ((Expression) unresolved.get(0)).getSQL()); |
| } |
| |
| subQuery.queryExpression.resolveTypes(session); |
| subQuery.prepareTable(session); |
| subQuery.setCorrelated(); |
| setRangeTableVariables(); |
| } |
| } |
| } |
| |
| /** |
| * Retreives a String representation of this obejct. <p> |
| * |
| * The returned String describes this object's table, alias |
| * access mode, index, join mode, Start, End and And conditions. |
| * |
| * @return a String representation of this object |
| */ |
| public String describe(Session session, int blanks) { |
| |
| RangeVariableConditions[] conditionsArray = joinConditions; |
| StringBuffer sb; |
| String b = ValuePool.spaceString.substring(0, blanks); |
| |
| sb = new StringBuffer(); |
| |
| String temp = "INNER"; |
| |
| if (isLeftJoin) { |
| temp = "LEFT OUTER"; |
| |
| if (isRightJoin) { |
| temp = "FULL"; |
| } |
| } else if (isRightJoin) { |
| temp = "RIGHT OUTER"; |
| } |
| |
| sb.append(b).append("join type=").append(temp).append("\n"); |
| sb.append(b).append("table=").append(rangeTable.getName().name).append( |
| "\n"); |
| |
| if (tableAlias != null) { |
| sb.append(b).append("alias=").append(tableAlias.name).append("\n"); |
| } |
| |
| boolean fullScan = !conditionsArray[0].hasIndexCondition(); |
| |
| sb.append(b).append("access=").append(fullScan ? "FULL SCAN" |
| : "INDEX PRED").append( |
| "\n"); |
| |
| for (int i = 0; i < conditionsArray.length; i++) { |
| RangeVariableConditions conditions = this.joinConditions[i]; |
| |
| if (i > 0) { |
| sb.append(b).append("OR condition = ["); |
| } else { |
| sb.append(b).append("condition = ["); |
| } |
| |
| sb.append(conditions.describe(session, blanks + 2)); |
| sb.append(b).append("]\n"); |
| } |
| |
| return sb.toString(); |
| } |
| |
| public RangeIteratorMain getIterator(Session session) { |
| |
| RangeIteratorMain it; |
| |
| if (this.isRightJoin) { |
| it = new RangeIteratorRight(session, this, null); |
| } else { |
| it = new RangeIteratorMain(session, this); |
| } |
| |
| session.sessionContext.setRangeIterator(it); |
| |
| return it; |
| } |
| |
| public static RangeIterator getIterator(Session session, |
| RangeVariable[] rangeVars) { |
| |
| if (rangeVars.length == 1) { |
| return rangeVars[0].getIterator(session); |
| } |
| |
| RangeIteratorMain[] iterators = |
| new RangeIteratorMain[rangeVars.length]; |
| |
| for (int i = 0; i < rangeVars.length; i++) { |
| iterators[i] = rangeVars[i].getIterator(session); |
| } |
| |
| return new RangeIteratorJoined(iterators); |
| } |
| |
| public static class RangeIteratorBase implements RangeIterator { |
| |
| Session session; |
| int rangePosition; |
| RowIterator it; |
| PersistentStore store; |
| Object[] currentData; |
| Row currentRow; |
| boolean isBeforeFirst; |
| RangeVariable rangeVar; |
| |
| RangeIteratorBase() {} |
| |
| public RangeIteratorBase(Session session, PersistentStore store, |
| TableBase t, int position) { |
| |
| this.session = session; |
| this.rangePosition = position; |
| this.store = store; |
| it = t.rowIterator(store); |
| isBeforeFirst = true; |
| } |
| |
| public boolean isBeforeFirst() { |
| return isBeforeFirst; |
| } |
| |
| public boolean next() { |
| |
| if (isBeforeFirst) { |
| isBeforeFirst = false; |
| } else { |
| if (it == null) { |
| return false; |
| } |
| } |
| |
| currentRow = it.getNextRow(); |
| |
| if (currentRow == null) { |
| return false; |
| } else { |
| currentData = currentRow.getData(); |
| |
| return true; |
| } |
| } |
| |
| public Row getCurrentRow() { |
| return currentRow; |
| } |
| |
| public Object[] getCurrent() { |
| return currentData; |
| } |
| |
| public void setCurrent(Object[] data) { |
| currentData = data; |
| } |
| |
| public long getRowId() { |
| |
| return currentRow == null ? 0 |
| : ((long) rangeVar.rangeTable.getId() << 32) |
| + ((long) currentRow.getPos()); |
| } |
| |
| public Object getRowidObject() { |
| return currentRow == null ? null |
| : ValuePool.getLong(getRowId()); |
| } |
| |
| public void remove() {} |
| |
| public void reset() { |
| |
| if (it != null) { |
| it.release(); |
| } |
| |
| it = null; |
| currentRow = null; |
| isBeforeFirst = true; |
| } |
| |
| public int getRangePosition() { |
| return rangePosition; |
| } |
| |
| public RangeVariable getRange() { |
| return rangeVar; |
| } |
| |
| public Row getNextRow() { |
| throw Error.runtimeError(ErrorCode.U_S0500, "RangeVariable"); |
| } |
| |
| public boolean hasNext() { |
| throw Error.runtimeError(ErrorCode.U_S0500, "RangeVariable"); |
| } |
| |
| public Object[] getNext() { |
| throw Error.runtimeError(ErrorCode.U_S0500, "RangeVariable"); |
| } |
| |
| public boolean setRowColumns(boolean[] columns) { |
| throw Error.runtimeError(ErrorCode.U_S0500, "RangeVariable"); |
| } |
| |
| public void release() { |
| |
| if (it != null) { |
| it.release(); |
| } |
| } |
| } |
| |
| public static class RangeIteratorMain extends RangeIteratorBase { |
| |
| boolean hasLeftOuterRow; |
| boolean isFullIterator; |
| RangeVariableConditions[] conditions; |
| RangeVariableConditions[] whereConditions; |
| RangeVariableConditions[] joinConditions; |
| int condIndex = 0; |
| |
| // |
| OrderedIntHashSet lookup; |
| |
| // |
| Object[] currentJoinData = null; |
| |
| RangeIteratorMain() { |
| super(); |
| } |
| |
| private RangeIteratorMain(Session session, RangeVariable rangeVar) { |
| |
| this.rangePosition = rangeVar.rangePosition; |
| this.store = rangeVar.rangeTable.getRowStore(session); |
| this.session = session; |
| this.rangeVar = rangeVar; |
| currentData = rangeVar.emptyData; |
| isBeforeFirst = true; |
| |
| if (rangeVar.isRightJoin) { |
| lookup = new OrderedIntHashSet(); |
| } |
| |
| conditions = rangeVar.joinConditions; |
| |
| if (rangeVar.whereConditions[0].hasIndexCondition()) { |
| conditions = rangeVar.whereConditions; |
| } |
| |
| whereConditions = rangeVar.whereConditions; |
| joinConditions = rangeVar.joinConditions; |
| } |
| |
| public boolean isBeforeFirst() { |
| return isBeforeFirst; |
| } |
| |
| 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; |
| } |
| |
| public void remove() {} |
| |
| public void reset() { |
| |
| if (it != null) { |
| it.release(); |
| } |
| |
| it = null; |
| currentData = rangeVar.emptyData; |
| currentRow = null; |
| isBeforeFirst = true; |
| } |
| |
| public int getRangePosition() { |
| return rangeVar.rangePosition; |
| } |
| |
| /** |
| */ |
| protected void initialiseIterator() { |
| |
| if (condIndex == 0) { |
| hasLeftOuterRow = rangeVar.isLeftJoin; |
| } |
| |
| if (conditions[condIndex].isFalse) { |
| it = conditions[condIndex].rangeIndex.emptyIterator(); |
| |
| return; |
| } |
| |
| SubQuery subQuery = rangeVar.rangeTable.getSubQuery(); |
| |
| if (subQuery != null) { |
| subQuery.materialiseCorrelated(session); |
| } |
| |
| if (conditions[condIndex].indexCond == null) { |
| it = conditions[condIndex].reversed |
| ? conditions[condIndex].rangeIndex.lastRow(session, store) |
| : conditions[condIndex].rangeIndex.firstRow(session, |
| store); |
| } else { |
| getFirstRow(); |
| |
| if (!conditions[condIndex].isJoin) { |
| hasLeftOuterRow = false; |
| } |
| } |
| } |
| |
| private void getFirstRow() { |
| |
| if (currentJoinData == null |
| || currentJoinData.length |
| < conditions[condIndex].indexedColumnCount) { |
| currentJoinData = |
| new Object[conditions[condIndex].indexedColumnCount]; |
| } |
| |
| for (int i = 0; i < conditions[condIndex].indexedColumnCount; |
| i++) { |
| int range = 0; |
| int opType = i == conditions[condIndex].indexedColumnCount - 1 |
| ? conditions[condIndex].opType |
| : conditions[condIndex].indexCond[i].getType(); |
| |
| if (opType == OpTypes.IS_NULL || opType == OpTypes.NOT |
| || opType == OpTypes.MAX) { |
| currentJoinData[i] = null; |
| |
| continue; |
| } |
| |
| Type valueType = |
| conditions[condIndex].indexCond[i].getRightNode() |
| .getDataType(); |
| Object value = |
| conditions[condIndex].indexCond[i].getRightNode().getValue( |
| session); |
| Type targetType = |
| conditions[condIndex].indexCond[i].getLeftNode() |
| .getDataType(); |
| |
| if (targetType != valueType) { |
| range = targetType.compareToTypeRange(value); |
| |
| if (range == 0) { |
| if (targetType.typeComparisonGroup |
| != valueType.typeComparisonGroup) { |
| value = targetType.convertToType(session, value, |
| valueType); |
| } |
| } |
| } |
| |
| if (i == 0) { |
| int exprType = |
| conditions[condIndex].indexCond[0].getType(); |
| |
| if (range < 0) { |
| switch (exprType) { |
| |
| case OpTypes.GREATER_EQUAL : |
| case OpTypes.GREATER : |
| value = null; |
| break; |
| |
| default : |
| it = conditions[condIndex].rangeIndex |
| .emptyIterator(); |
| |
| return; |
| } |
| } else if (range > 0) { |
| switch (exprType) { |
| |
| case OpTypes.NOT : |
| value = null; |
| break; |
| |
| default : |
| it = conditions[condIndex].rangeIndex |
| .emptyIterator(); |
| |
| return; |
| } |
| } |
| } |
| |
| currentJoinData[i] = value; |
| } |
| |
| it = conditions[condIndex].rangeIndex.findFirstRow(session, store, |
| currentJoinData, conditions[condIndex].indexedColumnCount, |
| conditions[condIndex].opType, |
| conditions[condIndex].reversed, null); |
| } |
| |
| /** |
| * Advances to the next available value. <p> |
| * |
| * @return true if a next value is available upon exit |
| */ |
| protected boolean findNext() { |
| |
| boolean result = false; |
| |
| while (true) { |
| currentRow = it.getNextRow(); |
| |
| if (currentRow == null) { |
| break; |
| } |
| |
| currentData = currentRow.getData(); |
| |
| 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; |
| } |
| |
| protected void addFoundRow() { |
| |
| if (rangeVar.isRightJoin) { |
| lookup.add(currentRow.getPos()); |
| } |
| } |
| } |
| |
| public static class RangeIteratorRight extends RangeIteratorMain { |
| |
| private RangeIteratorRight(Session session, RangeVariable rangeVar, |
| RangeIteratorMain main) { |
| |
| super(session, rangeVar); |
| |
| isFullIterator = true; |
| } |
| |
| boolean isOnRightOuterRows; |
| |
| public void setOnOuterRows() { |
| |
| conditions = rangeVar.whereConditions; |
| isOnRightOuterRows = true; |
| hasLeftOuterRow = false; |
| condIndex = 0; |
| |
| initialiseIterator(); |
| } |
| |
| public boolean next() { |
| |
| if (isOnRightOuterRows) { |
| if (it == null) { |
| return false; |
| } |
| |
| return findNextRight(); |
| } else { |
| return super.next(); |
| } |
| } |
| |
| protected boolean findNextRight() { |
| |
| boolean result = false; |
| |
| while (true) { |
| currentRow = it.getNextRow(); |
| |
| if (currentRow == null) { |
| break; |
| } |
| |
| currentData = currentRow.getData(); |
| |
| if (conditions[condIndex].indexEndCondition != null |
| && !conditions[condIndex].indexEndCondition |
| .testCondition(session)) { |
| break; |
| } |
| |
| if (conditions[condIndex].nonIndexCondition != null |
| && !conditions[condIndex].nonIndexCondition |
| .testCondition(session)) { |
| continue; |
| } |
| |
| if (!lookupAndTest()) { |
| continue; |
| } |
| |
| result = true; |
| |
| break; |
| } |
| |
| if (result) { |
| return true; |
| } |
| |
| it.release(); |
| |
| currentRow = null; |
| currentData = rangeVar.emptyData; |
| |
| return result; |
| } |
| |
| private boolean lookupAndTest() { |
| |
| boolean result = !lookup.contains(currentRow.getPos()); |
| |
| if (result) { |
| currentData = currentRow.getData(); |
| |
| if (conditions[condIndex].nonIndexCondition != null |
| && !conditions[condIndex].nonIndexCondition |
| .testCondition(session)) { |
| result = false; |
| } |
| } |
| |
| return result; |
| } |
| } |
| |
| public static class RangeIteratorJoined extends RangeIteratorBase { |
| |
| RangeIteratorMain[] rangeIterators; |
| int currentIndex = 0; |
| |
| public RangeIteratorJoined(RangeIteratorMain[] rangeIterators) { |
| this.rangeIterators = rangeIterators; |
| isBeforeFirst = true; |
| } |
| |
| public boolean isBeforeFirst() { |
| return isBeforeFirst; |
| } |
| |
| public boolean next() { |
| |
| while (currentIndex >= 0) { |
| RangeIteratorMain it = rangeIterators[currentIndex]; |
| |
| if (it.next()) { |
| if (currentIndex < rangeIterators.length - 1) { |
| currentIndex++; |
| |
| continue; |
| } |
| |
| currentRow = rangeIterators[currentIndex].currentRow; |
| currentData = currentRow.getData(); |
| |
| return true; |
| } else { |
| it.reset(); |
| |
| currentIndex--; |
| |
| continue; |
| } |
| } |
| |
| currentData = |
| rangeIterators[rangeIterators.length - 1].rangeVar.emptyData; |
| currentRow = null; |
| |
| for (int i = 0; i < rangeIterators.length; i++) { |
| rangeIterators[i].reset(); |
| } |
| |
| return false; |
| } |
| |
| public void remove() {} |
| |
| public void release() { |
| |
| if (it != null) { |
| it.release(); |
| } |
| |
| for (int i = 0; i < rangeIterators.length; i++) { |
| rangeIterators[i].reset(); |
| } |
| } |
| |
| public void reset() { |
| |
| super.reset(); |
| |
| for (int i = 0; i < rangeIterators.length; i++) { |
| rangeIterators[i].reset(); |
| } |
| } |
| |
| public int getRangePosition() { |
| return 0; |
| } |
| |
| public RangeVariable getRange() { |
| return null; |
| } |
| } |
| |
| public static class RangeVariableConditions { |
| |
| final RangeVariable rangeVar; |
| Expression[] indexCond; |
| Expression[] indexEndCond; |
| Expression indexEndCondition; |
| int indexedColumnCount; |
| Index rangeIndex; |
| final boolean isJoin; |
| Expression excludeConditions; |
| Expression nonIndexCondition; |
| int opType; |
| int opTypeEnd; |
| boolean isFalse; |
| boolean reversed; |
| |
| RangeVariableConditions(RangeVariable rangeVar, boolean isJoin) { |
| this.rangeVar = rangeVar; |
| this.isJoin = isJoin; |
| } |
| |
| RangeVariableConditions(RangeVariableConditions base) { |
| |
| this.rangeVar = base.rangeVar; |
| this.isJoin = base.isJoin; |
| nonIndexCondition = base.nonIndexCondition; |
| } |
| |
| boolean hasIndexCondition() { |
| return indexedColumnCount > 0; |
| } |
| |
| void addCondition(Expression e) { |
| |
| if (e == null) { |
| return; |
| } |
| |
| nonIndexCondition = |
| ExpressionLogical.andExpressions(nonIndexCondition, e); |
| |
| if (Expression.EXPR_FALSE.equals(nonIndexCondition)) { |
| isFalse = true; |
| } |
| |
| if (rangeIndex == null || rangeIndex.getColumnCount() == 0) { |
| return; |
| } |
| |
| if (indexedColumnCount == 0) { |
| return; |
| } |
| |
| if (e.getIndexableExpression(rangeVar) == null) { |
| return; |
| } |
| |
| int colIndex = e.getLeftNode().getColumnIndex(); |
| |
| switch (e.getType()) { |
| |
| case OpTypes.GREATER : |
| case OpTypes.GREATER_EQUAL : { |
| |
| // replaces existing condition |
| if (opType == OpTypes.NOT) { |
| if (rangeIndex.getColumns()[indexedColumnCount - 1] |
| == colIndex) { |
| nonIndexCondition = |
| ExpressionLogical.andExpressions( |
| nonIndexCondition, |
| indexCond[indexedColumnCount - 1]); |
| indexCond[indexedColumnCount - 1] = e; |
| opType = e.opType; |
| } |
| } else { |
| addToIndexConditions(e); |
| } |
| |
| break; |
| } |
| case OpTypes.SMALLER : |
| case OpTypes.SMALLER_EQUAL : { |
| if (opType == OpTypes.GREATER |
| || opType == OpTypes.GREATER_EQUAL |
| || opType == OpTypes.NOT) { |
| if (opTypeEnd != OpTypes.MAX) { |
| break; |
| } |
| |
| if (rangeIndex.getColumns()[indexedColumnCount - 1] |
| == colIndex) { |
| indexEndCond[indexedColumnCount - 1] = e; |
| indexEndCondition = |
| ExpressionLogical.andExpressions( |
| indexEndCondition, e); |
| opTypeEnd = e.getType(); |
| } |
| } |
| |
| break; |
| } |
| default : |
| } |
| } |
| |
| boolean addToIndexConditions(Expression e) { |
| |
| if (opType == OpTypes.EQUAL || opType == OpTypes.IS_NULL) { |
| if (indexedColumnCount < rangeIndex.getColumnCount()) { |
| if (rangeIndex.getColumns()[indexedColumnCount] |
| == e.getLeftNode().getColumnIndex()) { |
| indexCond[indexedColumnCount] = e; |
| |
| indexedColumnCount++; |
| |
| opType = e.opType; |
| opTypeEnd = OpTypes.MAX; |
| |
| return true; |
| } |
| } |
| } |
| |
| return false; |
| } |
| |
| /** |
| * |
| * @param exprList list of expressions |
| * @param index Index to use |
| * @param colCount number of columns searched |
| */ |
| void addIndexCondition(Expression[] exprList, Index index, |
| int colCount) { |
| |
| rangeIndex = index; |
| opType = exprList[0].getType(); |
| |
| switch (opType) { |
| |
| case OpTypes.NOT : |
| indexCond = exprList; |
| indexEndCond = new Expression[exprList.length]; |
| opTypeEnd = OpTypes.MAX; |
| break; |
| |
| case OpTypes.GREATER : |
| case OpTypes.GREATER_EQUAL : |
| indexCond = exprList; |
| indexEndCond = new Expression[exprList.length]; |
| opTypeEnd = OpTypes.MAX; |
| break; |
| |
| case OpTypes.SMALLER : |
| case OpTypes.SMALLER_EQUAL : { |
| Expression e = exprList[0].getLeftNode(); |
| |
| e = new ExpressionLogical(OpTypes.IS_NULL, e); |
| e = new ExpressionLogical(OpTypes.NOT, e); |
| indexCond = new Expression[]{ e }; |
| indexEndCond = new Expression[exprList.length]; |
| indexEndCond[0] = indexEndCondition = exprList[0]; |
| opTypeEnd = opType; |
| opType = OpTypes.NOT; |
| |
| break; |
| } |
| case OpTypes.IS_NULL : |
| case OpTypes.EQUAL : { |
| indexCond = exprList; |
| indexEndCond = new Expression[exprList.length]; |
| |
| for (int i = 0; i < colCount; i++) { |
| Expression e = exprList[i]; |
| |
| indexEndCond[i] = e; |
| indexEndCondition = |
| ExpressionLogical.andExpressions(indexEndCondition, |
| e); |
| opType = e.getType(); |
| } |
| |
| opTypeEnd = opType; |
| |
| break; |
| } |
| default : |
| Error.runtimeError(ErrorCode.U_S0500, "RangeVariable"); |
| } |
| |
| indexedColumnCount = colCount; |
| } |
| |
| void reverseIndexCondition() { |
| |
| if (opType == OpTypes.EQUAL || opType == OpTypes.IS_NULL) { |
| return; |
| } |
| |
| indexEndCondition = null; |
| |
| for (int i = 0; i < indexedColumnCount; i++) { |
| Expression e = indexCond[i]; |
| |
| indexCond[i] = indexEndCond[i]; |
| indexEndCond[i] = e; |
| indexEndCondition = |
| ExpressionLogical.andExpressions(indexEndCondition, e); |
| } |
| |
| opType = opTypeEnd; |
| reversed = true; |
| } |
| |
| String describe(Session session, int blanks) { |
| |
| StringBuffer sb = new StringBuffer(); |
| String b = ValuePool.spaceString.substring(0, blanks); |
| |
| sb.append(b).append("index=").append( |
| rangeIndex.getName().name).append("\n"); |
| |
| if (hasIndexCondition()) { |
| if (indexedColumnCount > 0) { |
| sb.append(b).append("start conditions=["); |
| |
| for (int j = 0; j < indexedColumnCount; j++) { |
| if (indexCond != null && indexCond[j] != null) { |
| sb.append(indexCond[j].describe(session, blanks)); |
| } |
| } |
| |
| sb.append("]\n"); |
| } |
| |
| if (indexEndCondition != null) { |
| String temp = indexEndCondition.describe(session, blanks); |
| |
| sb.append(b).append("end condition=[").append(temp).append( |
| "]\n"); |
| } |
| } |
| |
| if (nonIndexCondition != null) { |
| String temp = nonIndexCondition.describe(session, blanks); |
| |
| sb.append(b).append("other condition=[").append(temp).append( |
| "]\n"); |
| } |
| |
| return sb.toString(); |
| } |
| } |
| } |