in phoenix-core-client/src/main/java/org/apache/phoenix/compile/ExpressionCompiler.java [878:1000]
public Expression visitLeave(SubtractParseNode node, List<Expression> children) throws SQLException {
return visitLeave(node, children, new ArithmeticExpressionBinder() {
@Override
public PDatum getBindMetaData(int i, List<Expression> children,
final Expression expression) {
final PDataType type;
// If we're binding the first parameter and the second parameter
// is a date
// we know that the first parameter must be a date type too.
if (i == 0 && (type = children.get(1).getDataType()) != null
&& type.isCoercibleTo(PDate.INSTANCE)) {
return getPDatumByExpression(expression, type);
} else if (expression.getDataType() != null
&& expression.getDataType().isCoercibleTo(PDate.INSTANCE)) {
return getPDatumByExpression(expression, PDecimal.INSTANCE);
}
// Otherwise just go with what was calculated for the expression
return expression;
}
}, new ArithmeticExpressionFactory() {
@Override
public Expression create(ArithmeticParseNode node,
List<Expression> children) throws SQLException {
int i = 0;
PDataType theType = null;
Expression e1 = children.get(0);
Expression e2 = children.get(1);
Determinism determinism = e1.getDeterminism().combine(e2.getDeterminism());
PDataType type1 = e1.getDataType();
PDataType type2 = e2.getDataType();
// TODO: simplify this special case for DATE conversion
/**
* For date1-date2, we want to coerce to a LONG because this
* cannot be compared against another date. It has essentially
* become a number. For date1-5, we want to preserve the DATE
* type because this can still be compared against another date
* and cannot be multiplied or divided. Any other time occurs is
* an error. For example, 5-date1 is an error. The nulls occur if
* we have bind variables.
*/
boolean isType1Date =
type1 != null
&& type1 != PTimestamp.INSTANCE
&& type1 != PUnsignedTimestamp.INSTANCE
&& type1.isCoercibleTo(PDate.INSTANCE);
boolean isType2Date =
type2 != null
&& type2 != PTimestamp.INSTANCE
&& type2 != PUnsignedTimestamp.INSTANCE
&& type2.isCoercibleTo(PDate.INSTANCE);
if (isType1Date || isType2Date) {
if (isType1Date && isType2Date) {
i = 2;
theType = PDecimal.INSTANCE;
} else if (isType1Date && type2 != null
&& type2.isCoercibleTo(PDecimal.INSTANCE)) {
i = 2;
theType = PDate.INSTANCE;
} else if (type1 == null || type2 == null) {
/*
* FIXME: Could be either a Date or BigDecimal, but we
* don't know if we're comparing to a date or a number
* which would be disambiguate it.
*/
i = 2;
theType = null;
}
} else if(type1 == PTimestamp.INSTANCE || type2 == PTimestamp.INSTANCE) {
i = 2;
theType = PTimestamp.INSTANCE;
} else if(type1 == PUnsignedTimestamp.INSTANCE || type2 == PUnsignedTimestamp.INSTANCE) {
i = 2;
theType = PUnsignedTimestamp.INSTANCE;
}
for (; i < children.size(); i++) {
// This logic finds the common type to which all child types are coercible
// without losing precision.
Expression e = children.get(i);
determinism = determinism.combine(e.getDeterminism());
PDataType type = e.getDataType();
if (type == null) {
continue;
} else if (type.isCoercibleTo(PLong.INSTANCE)) {
if (theType == null) {
theType = PLong.INSTANCE;
}
} else if (type == PDecimal.INSTANCE) {
// Coerce return type to DECIMAL from LONG or DOUBLE if DECIMAL child found,
// unless we're doing date arithmetic.
if (theType == null
|| !theType.isCoercibleTo(PDate.INSTANCE)) {
theType = PDecimal.INSTANCE;
}
} else if (type.isCoercibleTo(PDouble.INSTANCE)) {
// Coerce return type to DOUBLE from LONG if DOUBLE child found,
// unless we're doing date arithmetic or we've found another child of type DECIMAL
if (theType == null
|| (theType != PDecimal.INSTANCE && !theType.isCoercibleTo(PDate.INSTANCE) )) {
theType = PDouble.INSTANCE;
}
} else {
throw TypeMismatchException.newException(type, node.toString());
}
}
if (theType == PDecimal.INSTANCE) {
return new DecimalSubtractExpression(children);
} else if (theType == PLong.INSTANCE) {
return new LongSubtractExpression(children);
} else if (theType == PDouble.INSTANCE) {
return new DoubleSubtractExpression(children);
} else if (theType == null) {
return LiteralExpression.newConstant(null, theType, determinism);
} else if (theType == PTimestamp.INSTANCE || theType == PUnsignedTimestamp.INSTANCE) {
return new TimestampSubtractExpression(children);
} else if (theType.isCoercibleTo(PDate.INSTANCE)) {
return new DateSubtractExpression(children);
} else {
throw TypeMismatchException.newException(theType, node.toString());
}
}
});
}