/* 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();
}
}
}