/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.eigenbase.sarg;
import java.util.*;
import org.eigenbase.reltype.*;
import org.eigenbase.rex.*;
import org.eigenbase.sql.*;
/**
* SargIntervalExpr represents an expression which can be resolved to a fixed
* {@link SargInterval}.
*
* <p>Null values require special treatment in expressions. Normally, for
* intervals of any kind, nulls are not considered to be within the domain of
* search values. This behavior can be modified by setting the {@link
* SqlNullSemantics} to a value other than the default. This happens implicitly
* when a point interval is created matching the null value. When null values
* are considered to be part of the domain, the ordering is defined as for
* {@link SargInterval}.
*/
public class SargIntervalExpr extends SargIntervalBase implements SargExpr {
//~ Instance fields --------------------------------------------------------
private SqlNullSemantics nullSemantics;
//~ Constructors -----------------------------------------------------------
/**
* @see SargFactory#newIntervalExpr
*/
SargIntervalExpr(
SargFactory factory,
RelDataType dataType,
SqlNullSemantics nullSemantics) {
super(factory, dataType);
this.nullSemantics = nullSemantics;
}
//~ Methods ----------------------------------------------------------------
/**
* @return null semantics which apply for searches on this interval
*/
public SqlNullSemantics getNullSemantics() {
return nullSemantics;
}
// publicize SargIntervalBase
public void setPoint(RexNode coordinate) {
super.setPoint(coordinate);
if (RexLiteral.isNullLiteral(coordinate)) {
// since they explicitly asked for the null value as a point,
// adjust the null semantics to match
if (nullSemantics == SqlNullSemantics.NULL_MATCHES_NOTHING) {
nullSemantics = SqlNullSemantics.NULL_MATCHES_NULL;
}
}
}
// publicize SargIntervalBase
public void setNull() {
super.setNull();
}
// publicize SargIntervalBase
public void setLower(RexNode coordinate, SargStrictness strictness) {
super.setLower(coordinate, strictness);
}
// publicize SargIntervalBase
public void setUpper(RexNode coordinate, SargStrictness strictness) {
super.setUpper(coordinate, strictness);
}
// publicize SargIntervalBase
public void unsetLower() {
super.unsetLower();
}
// publicize SargIntervalBase
public void unsetUpper() {
super.unsetUpper();
}
// publicize SargIntervalBase
public void setUnconstrained() {
super.setUnconstrained();
}
// publicize SargIntervalBase
public void setEmpty() {
super.setEmpty();
}
// implement SargExpr
public String toString() {
String s = super.toString();
if (nullSemantics == SqlNullSemantics.NULL_MATCHES_NOTHING) {
// default semantics, so omit them for brevity
return s;
} else {
return s + " " + nullSemantics;
}
}
// implement SargExpr
public SargIntervalSequence evaluate() {
SargIntervalSequence seq = new SargIntervalSequence();
// If at least one of the bounds got flipped by overflow, the
// result is empty.
if ((lowerBound.getBoundType() != SargBoundType.LOWER)
|| (upperBound.getBoundType() != SargBoundType.UPPER)) {
// empty sequence
return seq;
}
// Under the default null semantics, if one of the endpoints is
// known to be null, the result is empty.
if ((nullSemantics == SqlNullSemantics.NULL_MATCHES_NOTHING)
&& (lowerBound.isNull() || upperBound.isNull())) {
// empty sequence
return seq;
}
// Copy the endpoints to the new interval.
SargInterval interval =
new SargInterval(
factory,
getDataType());
interval.copyFrom(this);
// Then adjust for null semantics.
// REVIEW jvs 17-Jan-2006: For unconstrained intervals, we
// include null values. Why is this?
if ((nullSemantics == SqlNullSemantics.NULL_MATCHES_NOTHING)
&& getDataType().isNullable()
&& (lowerBound.isFinite() || upperBound.isFinite())
&& (!lowerBound.isFinite() || lowerBound.isNull())) {
// The test above says that this is a constrained range
// with no lower bound (or null for the lower bound). Since nulls
// aren't supposed to match anything, adjust the lower bound
// to exclude null.
interval.setLower(
factory.newNullLiteral(),
SargStrictness.OPEN);
} else if (nullSemantics == SqlNullSemantics.NULL_MATCHES_ANYTHING) {
if (!lowerBound.isFinite()
|| lowerBound.isNull()
|| upperBound.isNull()) {
// Since null is supposed to match anything, and it
// is included in the interval, expand the interval to
// match anything.
interval.setUnconstrained();
}
}
// NOTE jvs 27-Jan-2006: We don't currently filter out the empty
// interval here because we rely on being able to create them
// explicitly. See related comment in FennelRelUtil.convertSargExpr;
// if that changes, we could filter out the empty interval here.
seq.addInterval(interval);
return seq;
}
// implement SargExpr
public void collectDynamicParams(Set<RexDynamicParam> dynamicParams) {
if (lowerBound.getCoordinate() instanceof RexDynamicParam) {
dynamicParams.add((RexDynamicParam) lowerBound.getCoordinate());
}
if (upperBound.getCoordinate() instanceof RexDynamicParam) {
dynamicParams.add((RexDynamicParam) upperBound.getCoordinate());
}
}
// implement SargExpr
public SargIntervalSequence evaluateComplemented() {
SargIntervalSequence originalSeq = evaluate();
SargIntervalSequence seq = new SargIntervalSequence();
// Complement of empty set is unconstrained set.
if (originalSeq.getList().isEmpty()) {
seq.addInterval(
new SargInterval(
factory,
getDataType()));
return seq;
}
assert originalSeq.getList().size() == 1;
SargInterval originalInterval = originalSeq.getList().get(0);
// Complement of universal set is empty set.
if (originalInterval.isUnconstrained()) {
return seq;
}
// Use null as a lower bound rather than infinity (see
// http://issues.eigenbase.org/browse/LDB-60).
// REVIEW jvs 17-Apr-2006: This assumes NULL_MATCHES_NOTHING
// semantics. We've lost the original null semantics
// flag by now. Is there ever a case where other null
// semantics are required here?
SargInterval interval =
new SargInterval(
factory,
getDataType());
interval.setLower(
factory.newNullLiteral(),
SargStrictness.OPEN);
if (originalInterval.getUpperBound().isFinite()
&& originalInterval.getLowerBound().isFinite()) {
// Complement of a fully bounded range is the union of two
// disjoint half-bounded ranges.
interval.setUpper(
originalInterval.getLowerBound().getCoordinate(),
originalInterval.getLowerBound().getStrictnessComplement());
if (!originalInterval.getLowerBound().isNull()) {
seq.addInterval(interval);
} else {
// Don't bother adding an empty interval.
}
interval =
new SargInterval(
factory,
getDataType());
interval.setLower(
originalInterval.getUpperBound().getCoordinate(),
originalInterval.getUpperBound().getStrictnessComplement());
seq.addInterval(interval);
} else if (originalInterval.getLowerBound().isFinite()) {
// Complement of a half-bounded range is the opposite
// half-bounded range (with open for closed and vice versa)
interval.setUpper(
originalInterval.getLowerBound().getCoordinate(),
originalInterval.getLowerBound().getStrictnessComplement());
if (!originalInterval.getLowerBound().isNull()) {
seq.addInterval(interval);
} else {
// Don't bother adding an empty interval.
}
} else {
// Mirror image of previous case.
assert originalInterval.getUpperBound().isFinite();
interval.setLower(
originalInterval.getUpperBound().getCoordinate(),
originalInterval.getUpperBound().getStrictnessComplement());
seq.addInterval(interval);
}
return seq;
}
}
// End SargIntervalExpr.java