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(PDataType.DATE)) {
return new PDatum() {
@Override
public boolean isNullable() {
return expression.isNullable();
}
@Override
public PDataType getDataType() {
return type;
}
@Override
public Integer getMaxLength() {
return expression.getMaxLength();
}
@Override
public Integer getScale() {
return expression.getScale();
}
@Override
public SortOrder getSortOrder() {
return expression.getSortOrder();
}
};
} else if (expression.getDataType() != null
&& expression.getDataType().isCoercibleTo(
PDataType.DATE)) {
return new PDatum() { // Same as with addition
@Override
public boolean isNullable() {
return expression.isNullable();
}
@Override
public PDataType getDataType() {
return PDataType.DECIMAL;
}
@Override
public Integer getMaxLength() {
return expression.getMaxLength();
}
@Override
public Integer getScale() {
return expression.getScale();
}
@Override
public SortOrder getSortOrder() {
return expression.getSortOrder();
}
};
}
// 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);
boolean isDeterministic = e1.isDeterministic() && e2.isDeterministic();
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 != PDataType.TIMESTAMP
&& type1 != PDataType.UNSIGNED_TIMESTAMP
&& type1.isCoercibleTo(PDataType.DATE);
boolean isType2Date =
type2 != null
&& type2 != PDataType.TIMESTAMP
&& type2 != PDataType.UNSIGNED_TIMESTAMP
&& type2.isCoercibleTo(PDataType.DATE);
if (isType1Date || isType2Date) {
if (isType1Date && isType2Date) {
i = 2;
theType = PDataType.LONG;
} else if (isType1Date && type2 != null
&& type2.isCoercibleTo(PDataType.DECIMAL)) {
i = 2;
theType = PDataType.DATE;
} 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 == PDataType.TIMESTAMP || type2 == PDataType.TIMESTAMP) {
i = 2;
theType = PDataType.TIMESTAMP;
} else if(type1 == PDataType.UNSIGNED_TIMESTAMP || type2 == PDataType.UNSIGNED_TIMESTAMP) {
i = 2;
theType = PDataType.UNSIGNED_TIMESTAMP;
}
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);
isDeterministic &= e.isDeterministic();
PDataType type = e.getDataType();
if (type == null) {
continue;
} else if (type.isCoercibleTo(PDataType.LONG)) {
if (theType == null) {
theType = PDataType.LONG;
}
} else if (type == PDataType.DECIMAL) {
// Coerce return type to DECIMAL from LONG or DOUBLE if DECIMAL child found,
// unless we're doing date arithmetic.
if (theType == null
|| !theType.isCoercibleTo(PDataType.DATE)) {
theType = PDataType.DECIMAL;
}
} else if (type.isCoercibleTo(PDataType.DOUBLE)) {
// 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 != PDataType.DECIMAL && !theType.isCoercibleTo(PDataType.DATE) )) {
theType = PDataType.DOUBLE;