blob: 4066bc37ad4d228dc26744051ee64b37a429d8ce [file] [log] [blame]
/* 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.navigator;
import java.io.IOException;
import java.util.Comparator;
import java.util.TreeMap;
import org.hsqldb.QueryExpression;
import org.hsqldb.QuerySpecification;
import org.hsqldb.Row;
import org.hsqldb.Session;
import org.hsqldb.SortAndSlice;
import org.hsqldb.error.Error;
import org.hsqldb.error.ErrorCode;
import org.hsqldb.index.Index;
import org.hsqldb.lib.ArraySort;
import org.hsqldb.lib.ArrayUtil;
import org.hsqldb.lib.LongKeyHashMap;
import org.hsqldb.result.ResultMetaData;
import org.hsqldb.rowio.RowInputInterface;
import org.hsqldb.rowio.RowOutputInterface;
/**
* Implementation of RowSetNavigator for result sets.
*
* @author Fred Toussi (fredt@users dot sourceforge.net)
* @version 1.9.0
* @since 1.9.0
*/
public class RowSetNavigatorData extends RowSetNavigator
implements Comparator {
public static final Object[][] emptyTable = new Object[0][];
//
int currentOffset;
int baseBlockSize;
//
Object[][] table = emptyTable;
//
final Session session;
QueryExpression queryExpression;
int visibleColumnCount;
boolean isSimpleAggregate;
Object[] simpleAggregateData;
//
//
private Index mainIndex;
//
TreeMap rowMap;
LongKeyHashMap idMap;
RowSetNavigatorData(Session session) {
this.session = session;
}
public RowSetNavigatorData(Session session, QuerySpecification select) {
this.session = session;
this.queryExpression = select;
this.rangePosition = select.resultRangePosition;
visibleColumnCount = select.getColumnCount();
isSimpleAggregate = select.isAggregated && !select.isGrouped;
if (select.isGrouped) {
mainIndex = select.groupIndex;
rowMap = new TreeMap(this);
}
if (select.idIndex != null) {
idMap = new LongKeyHashMap();
}
}
public RowSetNavigatorData(Session session,
QueryExpression queryExpression) {
this.session = session;
this.queryExpression = queryExpression;
visibleColumnCount = queryExpression.getColumnCount();
}
public void sortFull() {
mainIndex = queryExpression.fullIndex;
ArraySort.sort(table, 0, size, this);
reset();
}
public void sortOrder() {
if (queryExpression.orderIndex != null) {
mainIndex = queryExpression.orderIndex;
ArraySort.sort(table, 0, size, this);
}
reset();
}
public void sortUnion(SortAndSlice sortAndSlice) {
if (sortAndSlice.index != null) {
mainIndex = sortAndSlice.index;
ArraySort.sort(table, 0, size, this);
reset();
}
}
public void add(Object[] data) {
ensureCapacity();
table[size] = data;
size++;
if (rowMap != null) {
rowMap.put(data, data);
}
if (idMap != null) {
Long id = (Long) data[visibleColumnCount];
idMap.put(id.longValue(), data);
}
}
public boolean addRow(Row row) {
throw Error.runtimeError(ErrorCode.U_S0500, "RowSetNavigatorClient");
}
public void update(Object[] oldData, Object[] newData) {
// noop
}
void addAdjusted(Object[] data, int[] columnMap) {
data = projectData(data, columnMap);
add(data);
}
void insertAdjusted(Object[] data, int[] columnMap) {
projectData(data, columnMap);
insert(data);
}
Object[] projectData(Object[] data, int[] columnMap) {
if (columnMap == null) {
data = (Object[]) ArrayUtil.resizeArrayIfDifferent(data,
visibleColumnCount);
} else {
Object[] newData = new Object[visibleColumnCount];
ArrayUtil.projectRow(data, columnMap, newData);
data = newData;
}
return data;
}
/**
* for union only
*/
void insert(Object[] data) {
ensureCapacity();
System.arraycopy(table, currentPos, table, currentPos + 1,
size - currentPos);
table[currentPos] = data;
size++;
}
public void clear() {
this.table = emptyTable;
this.size = table.length;
reset();
}
public boolean absolute(int position) {
return super.absolute(position);
}
public Object[] getCurrent() {
if (currentPos < 0 || currentPos >= size) {
return null;
}
if (currentPos == currentOffset + table.length) {
getBlock(currentOffset + table.length);
}
return table[currentPos - currentOffset];
}
public Row getCurrentRow() {
throw Error.runtimeError(ErrorCode.U_S0500, "RowSetNavigatorClient");
}
public Object[] getNextRowData() {
return next() ? getCurrent()
: null;
}
public boolean next() {
return super.next();
}
public void remove() {
System.arraycopy(table, currentPos + 1, table, currentPos,
size - currentPos - 1);
table[size - 1] = null;
currentPos--;
size--;
}
public void reset() {
super.reset();
}
public void close() {
super.close();
}
public boolean isMemory() {
return true;
}
public void read(RowInputInterface in,
ResultMetaData meta) throws IOException {}
public void write(RowOutputInterface out,
ResultMetaData meta) throws IOException {
reset();
out.writeLong(id);
out.writeInt(size);
out.writeInt(0); // offset
out.writeInt(size);
while (hasNext()) {
Object[] data = getNext();
out.writeData(meta.getExtendedColumnCount(), meta.columnTypes,
data, null, null);
}
reset();
}
public Object[] getData(long rowId) {
return (Object[]) idMap.get(rowId);
}
public void copy(RowSetNavigatorData other, int[] rightColumnIndexes) {
while (other.hasNext()) {
Object[] currentData = other.getNext();
addAdjusted(currentData, rightColumnIndexes);
}
other.close();
}
public void union(RowSetNavigatorData other) {
Object[] currentData;
removeDuplicates();
other.removeDuplicates();
while (other.hasNext()) {
currentData = other.getNext();
int position = ArraySort.searchFirst(table, 0, size, currentData,
this);
if (position < 0) {
position = -position - 1;
currentPos = position;
insert(currentData);
}
}
other.close();
reset();
}
public void unionAll(RowSetNavigatorData other) {
other.reset();
while (other.hasNext()) {
Object[] currentData = other.getNext();
add(currentData);
}
other.close();
reset();
}
public void intersect(RowSetNavigatorData other) {
removeDuplicates();
other.sortFull();
while (hasNext()) {
Object[] currentData = getNext();
boolean hasRow = other.containsRow(currentData);
if (!hasRow) {
remove();
}
}
other.close();
reset();
}
public void intersectAll(RowSetNavigatorData other) {
Object[] compareData = null;
RowIterator it;
Object[] otherData = null;
sortFull();
other.sortFull();
it = queryExpression.fullIndex.emptyIterator();
while (hasNext()) {
Object[] currentData = getNext();
boolean newGroup =
compareData == null
|| queryExpression.fullIndex.compareRowNonUnique(
session, currentData, compareData,
visibleColumnCount) != 0;
if (newGroup) {
compareData = currentData;
it = other.findFirstRow(currentData);
}
otherData = it.getNext();
if (otherData != null
&& queryExpression.fullIndex.compareRowNonUnique(
session, currentData, otherData,
visibleColumnCount) == 0) {
continue;
}
remove();
}
other.close();
reset();
}
public void except(RowSetNavigatorData other) {
removeDuplicates();
other.sortFull();
while (hasNext()) {
Object[] currentData = getNext();
boolean hasRow = other.containsRow(currentData);
if (hasRow) {
remove();
}
}
other.close();
reset();
}
public void exceptAll(RowSetNavigatorData other) {
Object[] compareData = null;
RowIterator it;
Object[] otherData = null;
sortFull();
other.sortFull();
it = queryExpression.fullIndex.emptyIterator();
while (hasNext()) {
Object[] currentData = getNext();
boolean newGroup =
compareData == null
|| queryExpression.fullIndex.compareRowNonUnique(
session, currentData, compareData,
queryExpression.fullIndex.getColumnCount()) != 0;
if (newGroup) {
compareData = currentData;
it = other.findFirstRow(currentData);
}
otherData = it.getNext();
if (otherData != null
&& queryExpression.fullIndex.compareRowNonUnique(
session, currentData, otherData,
queryExpression.fullIndex.getColumnCount()) == 0) {
remove();
}
}
other.close();
reset();
}
public boolean hasUniqueNotNullRows() {
sortFull();
reset();
Object[] lastRowData = null;
while (hasNext()) {
Object[] currentData = getNext();
if (hasNull(currentData)) {
continue;
}
if (lastRowData != null
&& queryExpression.fullIndex.compareRow(
session, lastRowData, currentData) == 0) {
return false;
} else {
lastRowData = currentData;
}
}
return true;
}
public void removeDuplicates() {
sortFull();
reset();
Object[] lastRowData = null;
while (hasNext()) {
Object[] currentData = getNext();
if (lastRowData != null
&& queryExpression.fullIndex.compareRow(
session, lastRowData, currentData) == 0) {
remove();
} else {
lastRowData = currentData;
}
}
reset();
}
public void trim(int limitstart, int limitcount) {
if (size == 0) {
return;
}
if (limitstart >= size) {
clear();
return;
}
if (limitstart != 0) {
reset();
for (int i = 0; i < limitstart; i++) {
next();
remove();
}
}
if (limitcount == 0 || limitcount >= size) {
return;
}
reset();
for (int i = 0; i < limitcount; i++) {
next();
}
while (hasNext()) {
next();
remove();
}
reset();
}
boolean hasNull(Object[] data) {
for (int i = 0; i < visibleColumnCount; i++) {
if (data[i] == null) {
return true;
}
}
return false;
}
/**
* Special case for isSimpleAggregate cannot use index lookup.
*/
public Object[] getGroupData(Object[] data) {
if (isSimpleAggregate) {
if (simpleAggregateData == null) {
simpleAggregateData = data;
return null;
}
return simpleAggregateData;
}
return (Object[]) rowMap.get(data);
}
boolean containsRow(Object[] data) {
int position = ArraySort.searchFirst(table, 0, size, data, this);
return position >= 0;
}
RowIterator findFirstRow(Object[] data) {
int position = ArraySort.searchFirst(table, 0, size, data, this);
if (position < 0) {
position = size;
} else {
position--;
}
return new DataIterator(position);
}
/**
* baseBlockSize remains unchanged.
*/
void getBlock(int offset) {
// no op for no blocks
}
private void ensureCapacity() {
if (size == table.length) {
int newSize = size == 0 ? 4
: size * 2;
Object[][] newTable = new Object[newSize][];
System.arraycopy(table, 0, newTable, 0, size);
table = newTable;
}
}
void implement() {
throw Error.error(ErrorCode.U_S0500, "RSND");
}
class DataIterator implements RowIterator {
int pos;
DataIterator(int position) {
pos = position;
}
public Row getNextRow() {
return null;
}
public Object[] getNext() {
if (hasNext()) {
pos++;
return table[pos];
}
return null;
}
public boolean hasNext() {
return pos < size - 1;
}
public void remove() {}
public boolean setRowColumns(boolean[] columns) {
return false;
}
public void release() {}
public long getRowId() {
return 0L;
}
}
public int compare(Object a, Object b) {
return mainIndex.compareRow(session, (Object[]) a, (Object[]) b);
}
}