/**
* Copyright (C) 2009-2013 FoundationDB, LLC
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.foundationdb.server.types.texpressions;
import com.foundationdb.qp.operator.QueryBindings;
import com.foundationdb.qp.operator.QueryContext;
import com.foundationdb.qp.row.Row;
import com.foundationdb.server.explain.*;
import com.foundationdb.server.explain.std.TExpressionExplainer;
import com.foundationdb.server.types.TInstance;
import com.foundationdb.server.types.TPreptimeValue;
import com.foundationdb.server.types.aksql.aktypes.AkBool;
import com.foundationdb.server.types.mcompat.mtypes.MNumeric;
import com.foundationdb.server.types.value.Value;
import com.foundationdb.server.types.value.ValueSource;
import com.foundationdb.server.types.value.ValueSources;
public abstract class TComparisonExpressionBase implements TPreparedExpression {
protected abstract int compare(TInstance leftInstance, ValueSource left,
TInstance rightInstance, ValueSource right);
@Override
public TPreptimeValue evaluateConstant(QueryContext queryContext) {
// First check both sides. If either is a constant null, the result is null
TPreptimeValue leftPrep = left.evaluateConstant(queryContext);
ValueSource oneVal = leftPrep.value();
if (oneVal != null && oneVal.isNull()) {
TInstance type = AkBool.INSTANCE.instance(true);
return new TPreptimeValue(ValueSources.getNullSource(type));
}
TPreptimeValue rightPrep = right.evaluateConstant(queryContext);
ValueSource twoVal = rightPrep.value();
if (twoVal != null && twoVal.isNull()) {
TInstance type = AkBool.INSTANCE.instance(true);
return new TPreptimeValue(ValueSources.getNullSource(type));
}
// Neither side is constant null. If both sides are constant, evaluate
ValueSource resultSource = null;
boolean nullable;
if (oneVal != null && twoVal != null) {
final boolean result = doEval(leftPrep.type(), oneVal, rightPrep.type(), twoVal);
resultSource = new Value(AkBool.INSTANCE.instance(false), result);
nullable = resultSource.isNull();
}
else {
nullable = left.resultType().nullability() || right.resultType().nullability();
}
return new TPreptimeValue(MNumeric.INT.instance(nullable), resultSource);
}
@Override
public TInstance resultType() {
return AkBool.INSTANCE.instance(left.resultType().nullability() || right.resultType().nullability());
}
@Override
public TEvaluatableExpression build() {
TInstance leftInstance = left.resultType();
TEvaluatableExpression leftEval = left.build();
TInstance rightInstance = right.resultType();
TEvaluatableExpression rightEval = right.build();
return new CompareEvaluation(leftInstance, leftEval, rightInstance, rightEval);
}
@Override
public CompoundExplainer getExplainer(ExplainContext context) {
CompoundExplainer ex = new TExpressionExplainer(Type.BINARY_OPERATOR, comparison.toString(), context, left, right);
ex.addAttribute(Label.INFIX_REPRESENTATION, PrimitiveExplainer.getInstance(comparison.toString()));
return ex;
}
@Override
public String toString() {
return left + " " + comparison + ' ' + right;
}
@Override
public boolean isLiteral() {
return false;
}
public TComparisonExpressionBase(TPreparedExpression left, Comparison comparison, TPreparedExpression right) {
this.left = left;
this.comparison = comparison;
this.right = right;
}
private boolean doEval(TInstance leftInstance, ValueSource left, TInstance rightInstance, ValueSource right) {
int cmpI = compare(leftInstance, left, rightInstance, right);
final Comparison actualComparison;
if (cmpI == 0)
actualComparison = Comparison.EQ;
else if (cmpI < 0)
actualComparison = Comparison.LT;
else
actualComparison = Comparison.GT;
final boolean result;
switch (actualComparison) {
case EQ:
result = (comparison == Comparison.EQ) || (comparison == Comparison.LE) || (comparison == Comparison.GE);
break;
case GT:
result = (comparison == Comparison.GT || comparison == Comparison.GE || comparison == Comparison.NE);
break;
case LT:
result = (comparison == Comparison.LT || comparison == Comparison.LE || comparison == Comparison.NE);
break;
default:
throw new AssertionError(actualComparison);
}
return result;
}
private final Comparison comparison;
private final TPreparedExpression left;
private final TPreparedExpression right;
private class CompareEvaluation implements TEvaluatableExpression {
@Override
public ValueSource resultValue() {
if (value == null)
throw new IllegalStateException("not evaluated");
return value;
}
@Override
public void evaluate() {
if (value == null)
value = new Value(AkBool.INSTANCE.instance(true));
left.evaluate();
ValueSource leftSource = left.resultValue();
if (leftSource.isNull()) {
value.putNull();
return;
}
right.evaluate();
ValueSource rightSource = right.resultValue();
if (rightSource.isNull()) {
value.putNull();
return;
}
boolean result = doEval(leftInstance, leftSource, rightInstance, rightSource);
value.putBool(result);
}
@Override
public void with(Row row) {
left.with(row);
right.with(row);
}
@Override
public void with(QueryContext context) {
left.with(context);
right.with(context);
}
@Override
public void with(QueryBindings bindings) {
left.with(bindings);
right.with(bindings);
}
private CompareEvaluation(TInstance leftInstance, TEvaluatableExpression left,
TInstance rightInstance, TEvaluatableExpression right)
{
this.leftInstance = leftInstance;
this.rightInstance = rightInstance;
this.left = left;
this.right = right;
}
private final TInstance leftInstance;
private final TInstance rightInstance;
private final TEvaluatableExpression left;
private final TEvaluatableExpression right;
private Value value;
}
}