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