package org.reldb.rel.v0.values;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NoSuchElementException;
import java.util.Set;
import org.reldb.rel.exceptions.*;
import org.reldb.rel.v0.generator.Generator;
import org.reldb.rel.v0.storage.RelDatabase;
import org.reldb.rel.v0.storage.temporary.TempIndex;
import org.reldb.rel.v0.storage.temporary.TempIndexImplementation;
import org.reldb.rel.v0.types.*;
import org.reldb.rel.v0.types.builtin.TypeInteger;
import org.reldb.rel.v0.vm.Context;
public abstract class ValueRelation extends ValueAbstract implements Projectable, TupleIteratable {
public ValueRelation(Generator generator) {
super(generator);
}
private static final long serialVersionUID = 0;
/** Obtain a serializable clone of this value. */
public Value getSerializableClone() {
// TODO - fix this to use TempStorageTuples in an appropriate manner, i.e., one that doesn't invoke getSerializableClone()
ValueRelationLiteral newRelation = new ValueRelationLiteral(getGenerator());
TupleIterator iterator = iterator();
try {
while (iterator.hasNext())
newRelation.insert((ValueTuple)iterator.next().getSerializableClone());
} finally {
iterator.close();
}
return newRelation;
}
public static ValueRelation getDum(Generator generator) {
return new ValueRelation(generator) {
private static final long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
public boolean hasNext() {
return false;
}
public ValueTuple next() {
throw new NoSuchElementException();
}
public void close() {}
};
}
};
}
public static ValueRelation getDee(Generator generator) {
return new ValueRelation(generator) {
private static final long serialVersionUID = 0;
public int hashCode() {
return 1;
}
public TupleIterator newIterator() {
return new TupleIterator() {
public boolean available = true;
public boolean hasNext() {
return available;
}
public ValueTuple next() {
if (available)
try {
return ValueTuple.getEmptyTuple(getGenerator());
} finally {
available = false;
}
throw new NoSuchElementException();
}
public void close() {}
};
}
};
}
public ValueTuple getTuple() {
TupleIterator iterator = iterator();
try {
if (iterator.hasNext()) {
ValueTuple tuple = iterator.next();
if (!iterator.hasNext())
return tuple;
}
} finally {
iterator.close();
}
throw new ExceptionSemantic("RS0274: TUPLE FROM expects a relation with cardinality equal to one.");
}
public Value project(final AttributeMap map) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIteratorUnique(new TupleIterator() {
TupleIterator tuples = ValueRelation.this.iterator();
public boolean hasNext() {
return tuples.hasNext();
}
public ValueTuple next() {
return (ValueTuple)tuples.next().project(map);
}
public void close() {
tuples.close();
}
});
}
};
}
public ValueRelation union(final ValueRelation rightRelation) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIteratorUnique(new TupleIterator() {
TupleIterator left = ValueRelation.this.iterator();
TupleIterator right = rightRelation.iterator();
public boolean hasNext() {
return (left.hasNext() || right.hasNext());
}
public ValueTuple next() {
if (left.hasNext())
return left.next();
else if (right.hasNext())
return right.next();
throw new NoSuchElementException();
}
public void close() {
left.close();
right.close();
}
});
}
};
}
public ValueRelation xunion(final ValueRelation rightRelation) {
return union(rightRelation).minus(intersect(rightRelation));
}
public ValueRelation dunion(final ValueRelation rightRelation) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIteratorDisjoint(new TupleIterator() {
TupleIterator left = ValueRelation.this.iterator();
TupleIterator right = rightRelation.iterator();
public boolean hasNext() {
return (left.hasNext() || right.hasNext());
}
public ValueTuple next() {
if (left.hasNext())
return left.next();
else if (right.hasNext())
return right.next();
throw new NoSuchElementException();
}
public void close() {
left.close();
right.close();
}
});
}
};
}
public ValueRelation intersect(final ValueRelation rightRelation) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
TupleIterator left = ValueRelation.this.iterator();
ValueTuple current = null;
public boolean hasNext() {
if (current != null)
return true;
ValueTuple leftTuple;
do {
if (!left.hasNext())
return false;
leftTuple = left.next();
} while (!rightRelation.contains(leftTuple));
current = leftTuple;
return true;
}
public ValueTuple next() {
if (hasNext())
try {
return current;
} finally {
current = null;
}
throw new NoSuchElementException();
}
public void close() {
left.close();
}
};
}
};
}
public ValueRelation minus(final ValueRelation rightRelation) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
TupleIterator left = ValueRelation.this.iterator();
ValueTuple current = null;
public boolean hasNext() {
if (current != null)
return true;
ValueTuple leftTuple;
do {
if (!left.hasNext())
return false;
leftTuple = left.next();
} while (rightRelation.contains(leftTuple));
current = leftTuple;
return true;
}
public ValueTuple next() {
if (hasNext())
try {
return current;
} finally {
current = null;
}
throw new NoSuchElementException();
}
public void close() {
left.close();
}
};
}
};
}
public Value iminus(ValueRelation rightRelation) {
if (!rightRelation.isSubsetOf(this))
throw new ExceptionSemantic("RS0275: In I_MINUS, the right operand must be a subset of the left operand.");
return minus(rightRelation);
}
public ValueRelation product(final ValueRelation rightRelation) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
TupleIterator left = ValueRelation.this.iterator();
TupleIterator right = null;
ValueTuple current = null;
ValueTuple leftTuple = null;
public boolean hasNext() {
if (current != null)
return true;
if (leftTuple != null) {
if (!right.hasNext())
leftTuple = null;
else {
current = leftTuple.joinDisjoint(right.next());
return true;
}
}
if (leftTuple == null) {
if (!left.hasNext())
return false;
leftTuple = left.next();
if (right != null)
right.close();
right = rightRelation.iterator();
if (!right.hasNext())
return false;
current = leftTuple.joinDisjoint(right.next());
}
return true;
}
public ValueTuple next() {
if (hasNext())
try {
return current;
} finally {
current = null;
}
throw new NoSuchElementException();
}
public void close() {
left.close();
if (right != null)
right.close();
}
};
}
};
}
public ValueRelation join(final RelDatabase database, final JoinMap map, final ValueRelation rightRelation) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
boolean indexed = false;
TempIndex rightTuples = new TempIndexImplementation(database);
TupleIterator leftIterator = ValueRelation.this.iterator();
TupleIterator rightIterator;
ValueTuple current = null;
ValueTuple leftTuple = null;
ValueTuple rightTuple = null;
public boolean hasNextPair() {
while (true) {
if (rightIterator == null) {
if (!leftIterator.hasNext())
return false;
leftTuple = leftIterator.next();
if (indexed)
rightIterator = rightTuples.keySearch(map.getLeftTupleCommon(getGenerator(), leftTuple));
else
rightIterator = rightRelation.iterator();
}
if (rightIterator.hasNext()) {
rightTuple = rightIterator.next();
if (indexed)
return true;
else {
ValueTuple rightClone = (ValueTuple)rightTuple.getSerializableClone();
rightTuples.put((ValueTuple)map.getRightTupleCommon(getGenerator(), rightClone), rightClone);
if (map.isJoinable(leftTuple, rightTuple))
return true;
}
} else {
indexed = true;
rightIterator.close();
rightIterator = null;
}
}
}
public boolean hasNext() {
if (current != null)
return true;
if (!hasNextPair())
return false;
current = leftTuple.join(map, rightTuple);
return true;
}
public ValueTuple next() {
if (hasNext())
try {
return current;
} finally {
current = null;
}
throw new NoSuchElementException();
}
public void close() {
if (rightIterator != null)
rightIterator.close();
leftIterator.close();
rightTuples.close();
}
};
}
};
}
/**
* Apply a native tuple operator to each tuple in a given relation, in order to
* generate a new relation.
*
* @param map - a TupleMap
* @return - a ValueRelation
*/
public TupleIteratable map(final TupleMap map) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
TupleIterator iterator = ValueRelation.this.iterator();
public boolean hasNext() {
return iterator.hasNext();
}
public ValueTuple next() {
return map.map(iterator.next());
}
public void close() {
iterator.close();
}
};
}
};
}
/**
* Order by attributes in orderMap. For each group of equal orderMap attributes,
* return one tuple of orderMap attributes with all groupAttributes in an appended relation-valued attribute.
*
* @param orderAttributes
* @param groupAttributes
* @return - a ValueRelation
*/
public Value group(AttributeMap orderAttributes, AttributeMap groupAttributes) {
// TODO - MEM - fix so that high-cardinality relations don't run out of RAM
final Map<ValueTuple, ValueRelationLiteral> group = new HashMap<ValueTuple, ValueRelationLiteral>();
(new TupleIteration(iterator()) {
public void process(ValueTuple tuple) {
ValueTuple orderTuple = (ValueTuple)tuple.project(orderAttributes);
ValueTuple groupAttributeTuple = (ValueTuple)tuple.project(groupAttributes);
ValueRelationLiteral groupAttributeValue = group.get(orderTuple);
if (groupAttributeValue == null) {
groupAttributeValue = new ValueRelationLiteral(getGenerator());
group.put(orderTuple, groupAttributeValue);
}
groupAttributeValue.insert(groupAttributeTuple);
}
}).run();
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
Set<Entry<ValueTuple, ValueRelationLiteral>> groupedData = group.entrySet();
Iterator<Entry<ValueTuple, ValueRelationLiteral>> iterator = groupedData.iterator();
public boolean hasNext() {
return iterator.hasNext();
}
public ValueTuple next() {
Entry<ValueTuple, ValueRelationLiteral> entry = iterator.next();
ValueTuple sortedTuple = entry.getKey();
ValueRelationLiteral rva = entry.getValue();
Value[] rvaTupleArray = {rva};
ValueTuple rvaTuple = new ValueTuple(getGenerator(), rvaTupleArray);
return sortedTuple.joinDisjoint(rvaTuple);
}
public void close() {
}
};
}
};
}
/**
* Ungroup a relation.
*
* Note that the result includes ALL the original tuple attributes,
* including the specified RVA.
*
* @param sourceMap -
* mapping between original tuples and new tuples
* @param rvaMap -
* mapping between original tuples' RVA and new tuples
* @param rvaIndex -
* index of RVA in original tuple
* @param source -
* ValueRelation containing at least one RVA, to which rvaIndex
* points
* @return - ValueRelation
*/
public ValueRelation ungroup(final int resultDegree, final AttributeMap sourceMap, final AttributeMap rvaMap, final int rvaIndex) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
ValueTuple current = null;
TupleIterator outerIterator = ValueRelation.this.iterator();
TupleIterator innerIterator = null;
ValueTuple outerTuple;
boolean done = false;
public boolean hasNext() {
if (done)
return false;
if (current != null)
return true;
while (innerIterator == null || !innerIterator.hasNext()) {
if (!outerIterator.hasNext()) {
done = true;
return false;
}
outerTuple = outerIterator.next();
if (innerIterator != null)
innerIterator.close();
innerIterator = ((ValueRelation)outerTuple.getValues()[rvaIndex]).iterator();
if (innerIterator.hasNext())
break;
else
continue;
}
Value[] buffer = new Value[resultDegree];
current = new ValueTuple(getGenerator(), buffer);
current.assign(rvaMap, innerIterator.next());
current.assign(sourceMap, outerTuple);
return true;
}
public ValueTuple next() {
if (hasNext())
try {
return current;
} finally {
current = null;
}
throw new NoSuchElementException();
}
public void close() {
outerIterator.close();
if (innerIterator != null)
innerIterator.close();
}
};
}
};
}
/**
* Apply a native boolean operator to each tuple in a given relation, in order to
* generate a new relation.
*
* Tuples only appear in result if the operator returns true.
*/
public ValueRelation select(final TupleFilter filter) {
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
return new TupleIterator() {
TupleIterator iterator = ValueRelation.this.iterator();
ValueTuple current = null;
public boolean hasNext() {
if (current != null)
return true;
boolean testResult;
ValueTuple next;
do {
if (!iterator.hasNext())
return false;
next = iterator.next();
testResult = filter.filter(next);
} while (!testResult);
current = next;
return true;
}
public ValueTuple next() {
if (hasNext())
try {
return current;
} finally {
current = null;
}
throw new NoSuchElementException();
}
public void close() {
iterator.close();
}
};
}
};
}
public static Value sequence(Generator generator, ValueInteger start, ValueInteger end, ValueInteger step) {
return new ValueRelation(generator) {
private static final long serialVersionUID = 1L;
@Override
public TupleIterator newIterator() {
return new TupleIterator() {
long currentValue = start.longValue();
ValueTuple current = getCurrentValue();
private ValueTuple getCurrentValue() {
return new ValueTuple(generator, new Value[] {ValueInteger.select(generator, currentValue)});
}
public boolean hasNext() {
if (current != null)
return true;
currentValue += step.longValue();
if ((step.longValue() > 0) ? currentValue > end.longValue() : currentValue < end.longValue())
return false;
current = getCurrentValue();
return true;
}
public ValueTuple next() {
if (hasNext())
try {
return current;
} finally {
current = null;
}
throw new NoSuchElementException();
}
public void close() {
}
};
}
@Override
public int hashCode() {
return 0;
}
};
}
public static Value sequence(Generator generator, ValueInteger start, ValueInteger end) {
return sequence(generator, start, end, ValueInteger.select(generator, 1));
}
private ValueArray doSort(OrderMap map) {
// TODO - MEM - fix so that high-cardinality relations don't run out of RAM
final ArrayList<ValueTuple> array = new ArrayList<ValueTuple>();
(new TupleIteration(iterator()) {
public void process(ValueTuple tuple) {
array.add(tuple);
}
}).run();
Collections.sort(array, new Sorter(map));
return new ValueArray(getGenerator(), array);
}
/** Return a new, possibly-sorted ValueArray */
public ValueArray sort(OrderMap map) {
if (map.getMap().length == 0)
return new ValueArray(getGenerator(), this);
else
return doSort(map);
}
/** Return a ranked relation. Each ValueTuple has an appended ValueInteger that indicates the tuple's rank. */
public ValueRelation rank(OrderMap map) {
ValueArray array = doSort(map);
return new ValueRelation(getGenerator()) {
private final static long serialVersionUID = 0;
public int hashCode() {
return 0;
}
public TupleIterator newIterator() {
TupleIterator source = array.iterator();
return new TupleIterator() {
long rank = 0;
Value[] oldTupleValues = null;
@Override
public boolean hasNext() {
return source.hasNext();
}
@Override
public ValueTuple next() {
ValueTuple newTuple = source.next();
Value[] newTupleValues = newTuple.getValues();
if (oldTupleValues == null || map.isDifferentSortKey(oldTupleValues, newTupleValues))
rank++;
oldTupleValues = newTupleValues;
Value[] rankTupleRaw = new Value[] {ValueInteger.select(getGenerator(), rank)};
ValueTuple rankTuple = new ValueTuple(getGenerator(), rankTupleRaw);
return newTuple.joinDisjoint(rankTuple);
}
@Override
public void close() {
source.close();
}
};
}
};
}
public abstract TupleIterator newIterator();
public abstract int hashCode();
public final TupleIterator iterator() {
return newIterator();
}
// TODO - MEM - replace this with something that won't out-of-memory on high-cardinality relations
private HashSet<ValueTuple> cache = null;
private void buildCache() {
cache = new HashSet<ValueTuple>();
(new TupleIteration(iterator()) {
public void process(ValueTuple tuple) {
cache.add(tuple);
}
}).run();
}
public boolean contains(ValueTuple findMe) {
if (cache == null)
buildCache();
return cache.contains(findMe);
}
public String getTypeName() {
return "RELATION";
}
/** Output this Value to a PrintStream. */
public void toStream(Context context, Type type, PrintStream p, int depth) {
Heading heading = ((TypeRelation)type).getHeading();
TypeTuple tupleType = new TypeTuple(heading);
p.print("RELATION" + " " + heading + " {");
long count = 0;
TupleIterator iterator = iterator();
try {
while (iterator.hasNext()) {
ValueTuple tuple = iterator.next();
if (count++ > 0)
p.print(',');
p.print("\n\t");
tuple.toStream(context, tupleType, p, depth + 1);
}
} finally {
iterator.close();
}
p.print("\n}");
}
// For debugging purposes
public String toString() {
String s = "";
s += "RELATION {";
long count = 0;
TupleIterator iterator = iterator();
try {
while (iterator.hasNext()) {
ValueTuple tuple = iterator.next();
if (count++ > 0)
s += ", ";
s += tuple.toString();
}
} finally {
iterator.close();
}
s += "}";
return s;
}
public long getCardinality() {
if (cache == null)
buildCache();
return cache.size();
}
private final boolean isTestedSubsetOf(ValueRelation rightSide) {
TupleIterator iterator = iterator();
try {
while (iterator.hasNext()) {
ValueTuple tuple = iterator.next();
if (!rightSide.contains(tuple))
return false;
}
} finally {
iterator.close();
}
return true;
}
/** Test for <=, i.e., left is subset of right */
public boolean isSubsetOf(ValueRelation rightSide) {
if (getCardinality() > rightSide.getCardinality()) {
return false;
} else {
return isTestedSubsetOf(rightSide);
}
}
/** Test for >=, i.e., left is superset of right */
public boolean isSupersetOf(ValueRelation v) {
return v.isSubsetOf(this);
}
/** Test for >, i.e. left is proper superset of right */
public boolean isProperSupersetOf(ValueRelation v) {
return v.isProperSubsetOf(this);
}
/** Test for <, i.e. left is proper subset of right */
public boolean isProperSubsetOf(ValueRelation v) {
return isSubsetOf(v) && neq(v);
}
public int compareTo(Value v) {
ValueRelation rightSide = ((ValueRelation)v);
long cardinalityLeft = getCardinality();
long cardinalityRight = rightSide.getCardinality();
if (cardinalityLeft == cardinalityRight)
return isTestedSubsetOf(rightSide) ? 0 : 1;
else
return 1;
}
/** Test this relation and another for equality. */
public boolean eq(ValueRelation rightSide) {
if (getCardinality() == rightSide.getCardinality())
return isTestedSubsetOf(rightSide);
else
return false;
}
/** Test this relation and another for non-equality. */
public boolean neq(ValueRelation v) {
return !eq(v);
}
private static ValueRelation tclose(Generator generator, JoinMap joinMap, AttributeMap projectMap, ValueRelation xy) {
ValueRelation ttt = xy.union((ValueRelation)xy.join(generator.getDatabase(), joinMap, xy).project(projectMap));
return (ttt.eq(xy)) ? ttt : tclose(generator, joinMap, projectMap, ttt);
}
public Value tclose() {
Type attributeType = TypeInteger.getInstance(); // type is irrelevant here; any will do
Heading left = new Heading();
left.add("X", attributeType);
left.add("LINK", attributeType);
Heading right = new Heading();
right.add("LINK", attributeType);
right.add("Y", attributeType);
Heading joinTarget = new Heading();
joinTarget.add("X", attributeType);
joinTarget.add("LINK", attributeType);
joinTarget.add("Y", attributeType);
Heading projectTarget = new Heading();
projectTarget.add("X", attributeType);
projectTarget.add("Y", attributeType);
JoinMap joinMap = new JoinMap(joinTarget, left, right);
AttributeMap projectMap = new AttributeMap(projectTarget, joinTarget);
return tclose(getGenerator(), joinMap, projectMap, this);
}
public ValueBoolean is_empty() {
TupleIterator iterator = iterator();
try {
return (ValueBoolean)ValueBoolean.select(getGenerator(), !iterator.hasNext());
} finally {
iterator.close();
}
}
/** Aggregate operator */
public ValueBoolean exactly(long nCount, int attributeIndex) {
long trueCount = 0;
TupleIterator iterator = iterator();
try {
while (iterator.hasNext()) {
ValueTuple t = iterator.next();
trueCount += ((((ValueBoolean)t.getValues()[attributeIndex]).booleanValue()) ? 1 : 0);
}
} finally {
iterator.close();
}
return (ValueBoolean)ValueBoolean.select(getGenerator(), trueCount == nCount);
}
}