/* Copyright (c) 2001-2018, 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.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 2.3.5
* @since 1.9.0
*/
public class
RowSetNavigatorData extends
RowSetNavigator
implements
Comparator {
public static final
Object[][]
emptyTable = new
Object[0][];
//
private
Object[][]
dataTable =
emptyTable;
//
int
visibleColumnCount;
boolean
isAggregate;
boolean
isSimpleAggregate;
Object[]
simpleAggregateData;
//
boolean
reindexTable;
//
Index mainIndex;
Index fullIndex;
Index orderIndex;
Index groupIndex;
Index idIndex;
//
TreeMap rowMap;
LongKeyHashMap idMap;
RowSetNavigatorData(
Session session) {
this.
session =
session;
}
public
RowSetNavigatorData(
Session session,
QuerySpecification select) {
this.
session =
session;
this.
rangePosition =
select.
resultRangePosition;
visibleColumnCount =
select.
getColumnCount();
isSimpleAggregate =
select.
isAggregated && !
select.
isGrouped;
mainIndex =
select.
mainIndex;
fullIndex =
select.
fullIndex;
orderIndex =
select.
orderIndex;
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;
mainIndex =
queryExpression.
mainIndex;
fullIndex =
queryExpression.
fullIndex;
orderIndex =
queryExpression.
orderIndex;
visibleColumnCount =
queryExpression.
getColumnCount();
}
public
RowSetNavigatorData(
Session session,
RowSetNavigator navigator) {
this.
session =
session;
setCapacity(
navigator.
size);
while (
navigator.
next()) {
add(
navigator.
getCurrent());
}
}
public void
sortFull(
Session session) {
mainIndex =
fullIndex;
ArraySort.
sort(
dataTable,
size, this);
reset();
}
public void
sortOrder(
Session session) {
if (
orderIndex != null) {
mainIndex =
orderIndex;
ArraySort.
sort(
dataTable,
size, this);
}
reset();
}
public void
sortOrderUnion(
Session session,
SortAndSlice sortAndSlice) {
if (
sortAndSlice.
index != null) {
mainIndex =
sortAndSlice.
index;
ArraySort.
sort(
dataTable,
size, this);
reset();
}
}
public void
add(
Object[]
data) {
ensureCapacity();
dataTable[
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, "RowSetNavigatorData");
}
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(
dataTable,
currentPos,
dataTable,
currentPos + 1,
size -
currentPos);
dataTable[
currentPos] =
data;
size++;
}
public void
release() {
this.
dataTable =
emptyTable;
this.
size = 0;
reset();
isClosed = true;
}
public void
clear() {
this.
dataTable =
emptyTable;
this.
size = 0;
reset();
}
public boolean
absolute(int
position) {
return super.absolute(
position);
}
public
Object[]
getCurrent() {
if (
currentPos < 0 ||
currentPos >=
size) {
return null;
}
return
dataTable[
currentPos];
}
public
Row getCurrentRow() {
throw
Error.
runtimeError(
ErrorCode.
U_S0500, "RowSetNavigatorData");
}
public
Object[]
getNextRowData() {
return
next() ?
getCurrent()
: null;
}
public boolean
next() {
return super.next();
}
public void
removeCurrent() {
System.
arraycopy(
dataTable,
currentPos + 1,
dataTable,
currentPos,
size -
currentPos - 1);
dataTable[
size - 1] = null;
currentPos--;
size--;
}
public void
reset() {
super.reset();
}
public boolean
isMemory() {
return true;
}
public void
read(
RowInputInterface in,
ResultMetaData meta) {}
public void
write(
RowOutputInterface out,
ResultMetaData meta) {
reset();
out.
writeLong(
id);
out.
writeInt(
size);
out.
writeInt(0); // offset
out.
writeInt(
size);
while (
next()) {
Object[]
data =
getCurrent();
out.
writeData(
meta.
getExtendedColumnCount(),
meta.
columnTypes,
data, null, null);
}
reset();
}
public
Object[]
getData(long
rowId) {
return (
Object[])
idMap.
get(
rowId);
}
public void
copy(
RowIterator other, int[]
rightColumnIndexes) {
while (
other.
next()) {
Object[]
currentData =
other.
getCurrent();
addAdjusted(
currentData,
rightColumnIndexes);
}
}
public void
union(
Session session,
RowSetNavigatorData other) {
Object[]
currentData;
removeDuplicates(
session);
other.
removeDuplicates(
session);
mainIndex =
fullIndex;
while (
other.
next()) {
currentData =
other.
getCurrent();
int
position =
ArraySort.
searchFirst(
dataTable, 0,
size,
currentData, this);
if (
position < 0) {
position = -
position - 1;
currentPos =
position;
insert(
currentData);
}
}
reset();
}
public void
unionAll(
Session session,
RowSetNavigatorData other) {
mainIndex =
fullIndex;
other.
reset();
while (
other.
next()) {
Object[]
currentData =
other.
getCurrent();
add(
currentData);
}
reset();
}
public void
intersect(
Session session,
RowSetNavigatorData other) {
removeDuplicates(
session);
other.
sortFull(
session);
while (
next()) {
Object[]
currentData =
getCurrent();
boolean
hasRow =
other.
containsRow(
currentData);
if (!
hasRow) {
removeCurrent();
}
}
reset();
}
public void
intersectAll(
Session session,
RowSetNavigatorData other) {
Object[]
compareData = null;
RowIterator it;
Object[]
otherData = null;
sortFull(
session);
other.
sortFull(
session);
it =
fullIndex.
emptyIterator();
while (
next()) {
Object[]
currentData =
getCurrent();
boolean
newGroup =
compareData == null
||
fullIndex.
compareRowNonUnique(
session,
currentData,
compareData,
visibleColumnCount) != 0;
if (
newGroup) {
compareData =
currentData;
it =
other.
findFirstRow(
currentData);
}
if (
it.
next()) {
otherData =
it.
getCurrent();
if (
fullIndex.
compareRowNonUnique(
session,
currentData,
otherData,
visibleColumnCount) == 0) {
continue;
}
}
removeCurrent();
}
reset();
}
public void
except(
Session session,
RowSetNavigatorData other) {
removeDuplicates(
session);
other.
sortFull(
session);
while (
next()) {
Object[]
currentData =
getCurrent();
boolean
hasRow =
other.
containsRow(
currentData);
if (
hasRow) {
removeCurrent();
}
}
reset();
}
public void
exceptAll(
Session session,
RowSetNavigatorData other) {
Object[]
compareData = null;
RowIterator it;
Object[]
otherData = null;
sortFull(
session);
other.
sortFull(
session);
it =
fullIndex.
emptyIterator();
while (
next()) {
Object[]
currentData =
getCurrent();
boolean
newGroup =
compareData == null
||
fullIndex.
compareRowNonUnique(
session,
currentData,
compareData,
fullIndex.
getColumnCount()) != 0;
if (
newGroup) {
compareData =
currentData;
it =
other.
findFirstRow(
currentData);
}
if (
it.
next()) {
otherData =
it.
getCurrent();
if (
fullIndex.
compareRowNonUnique(
session,
currentData,
otherData,
fullIndex.
getColumnCount()) == 0) {
removeCurrent();
}
}
}
reset();
}
public boolean
hasUniqueNotNullRows(
Session session) {
sortFull(
session);
reset();
Object[]
lastRowData = null;
while (
next()) {
Object[]
currentData =
getCurrent();
if (
hasNull(
currentData)) {
continue;
}
if (
lastRowData != null
&&
fullIndex.
compareRow(
session,
lastRowData,
currentData)
== 0) {
return false;
} else {
lastRowData =
currentData;
}
}
return true;
}
public void
removeDuplicates(
Session session) {
sortFull(
session);
reset();
int
lastRowPos = -1;
Object[]
lastRowData = null;
while (
next()) {
Object[]
currentData =
getCurrent();
if (
lastRowData == null) {
lastRowPos =
currentPos;
lastRowData =
currentData;
continue;
}
if (
fullIndex.
compareRow(
session,
lastRowData,
currentData) != 0) {
lastRowPos++;
lastRowData =
currentData;
dataTable[
lastRowPos] =
currentData;
}
}
for (int
i =
lastRowPos + 1;
i <
size;
i++) {
dataTable[
i] = null;
}
super.
size =
lastRowPos + 1;
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();
removeCurrent();
}
}
if (
limitcount >=
size) {
return;
}
reset();
for (int
i = 0;
i <
limitcount;
i++) {
next();
}
while (
next()) {
removeCurrent();
}
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(
dataTable, 0,
size,
data, this);
return
position >= 0;
}
RowIterator findFirstRow(
Object[]
data) {
int
position =
ArraySort.
searchFirst(
dataTable, 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
setCapacity(int
newSize) {
if (
size >
dataTable.length) {
dataTable = new
Object[
newSize][];
}
}
private void
ensureCapacity() {
if (
size ==
dataTable.length) {
int
newSize =
size == 0 ? 4
:
size * 2;
Object[][]
newTable = new
Object[
newSize][];
System.
arraycopy(
dataTable, 0,
newTable, 0,
size);
dataTable =
newTable;
}
}
void
implement() {
throw
Error.
error(
ErrorCode.
U_S0500, "RSND");
}
class
DataIterator implements
RowIterator {
int
pos;
DataIterator(int
position) {
pos =
position;
}
public
Object getField(int
col) {
if (
pos <
size) {
return
dataTable[
pos][
col];
} else {
return null;
}
}
public boolean
next() {
if (
pos <
size - 1) {
pos++;
return true;
}
return false;
}
public
Row getCurrentRow() {
return null;
}
public
Object[]
getCurrent() {
if (
pos <
size) {
return
dataTable[
pos];
} else {
return null;
}
}
public void
removeCurrent() {}
public void
release() {}
public long
getRowId() {
return 0L;
}
}
public int
compare(
Object a,
Object b) {
return
mainIndex.
compareRow((
Session)
session, (
Object[])
a,
(
Object[])
b);
}
}