@Override
public Expression visitLeave(ComparisonParseNode node, List<Expression> children) throws SQLException {
ParseNode lhsNode = node.getChildren().get(0);
ParseNode rhsNode = node.getChildren().get(1);
Expression lhsExpr = children.get(0);
Expression rhsExpr = children.get(1);
boolean isDeterministic = lhsExpr.isDeterministic() || rhsExpr.isDeterministic();
PDataType lhsExprDataType = lhsExpr.getDataType();
PDataType rhsExprDataType = rhsExpr.getDataType();
checkComparability(node, lhsNode, rhsNode, lhsExpr, rhsExpr);
// We don't yet support comparison between entire arrays
if ( ( (lhsExprDataType != null && lhsExprDataType.isArrayType()) ||
(rhsExprDataType != null && rhsExprDataType.isArrayType()) ) &&
( node.getFilterOp() != CompareOp.EQUAL && node.getFilterOp() != CompareOp.NOT_EQUAL ) ) {
throw new SQLExceptionInfo.Builder(SQLExceptionCode.NON_EQUALITY_ARRAY_COMPARISON)
.setMessage(ComparisonExpression.toString(node.getFilterOp(), children)).build().buildException();
}
if (lhsExpr instanceof RowValueConstructorExpression || rhsExpr instanceof RowValueConstructorExpression) {
rhsExpr = RowValueConstructorExpression.coerce(lhsExpr, rhsExpr, node.getFilterOp());
// Always wrap both sides in row value constructor, so we don't have to consider comparing
// a non rvc with a rvc.
if ( ! ( lhsExpr instanceof RowValueConstructorExpression ) ) {
lhsExpr = new RowValueConstructorExpression(Collections.singletonList(lhsExpr), lhsExpr.isStateless());
}
children = Arrays.asList(lhsExpr, rhsExpr);
}
Object lhsValue = null;
// Can't use lhsNode.isConstant(), because we have cases in which we don't know
// in advance if a function evaluates to null (namely when bind variables are used)
// TODO: use lhsExpr.isStateless instead
if (lhsExpr instanceof LiteralExpression) {
lhsValue = ((LiteralExpression)lhsExpr).getValue();
if (lhsValue == null) {
return LiteralExpression.newConstant(false, PDataType.BOOLEAN, lhsExpr.isDeterministic());
}
}
Object rhsValue = null;
// TODO: use lhsExpr.isStateless instead
if (rhsExpr instanceof LiteralExpression) {
rhsValue = ((LiteralExpression)rhsExpr).getValue();
if (rhsValue == null) {
return LiteralExpression.newConstant(false, PDataType.BOOLEAN, rhsExpr.isDeterministic());
}
}
if (lhsValue != null && rhsValue != null) {
return LiteralExpression.newConstant(ByteUtil.compare(node.getFilterOp(),lhsExprDataType.compareTo(lhsValue, rhsValue, rhsExprDataType)), isDeterministic);
}
// Coerce constant to match type of lhs so that we don't need to
// convert at filter time. Since we normalize the select statement
// to put constants on the LHS, we don't need to check the RHS.
if (rhsValue != null) {
// Comparing an unsigned int/long against a negative int/long would be an example. We just need to take
// into account the comparison operator.
if (rhsExprDataType != lhsExprDataType
|| rhsExpr.getColumnModifier() != lhsExpr.getColumnModifier()
|| (rhsExpr.getMaxLength() != null && lhsExpr.getMaxLength() != null && rhsExpr.getMaxLength() < lhsExpr.getMaxLength())) {
// TODO: if lengths are unequal and fixed width?
if (rhsExprDataType.isCoercibleTo(lhsExprDataType, rhsValue)) { // will convert 2.0 -> 2
children = Arrays.asList(children.get(0), LiteralExpression.newConstant(rhsValue, lhsExprDataType,
lhsExpr.getMaxLength(), null, lhsExpr.getColumnModifier(), isDeterministic));
} else if (node.getFilterOp() == CompareOp.EQUAL) {
return LiteralExpression.newConstant(false, PDataType.BOOLEAN, true);
} else if (node.getFilterOp() == CompareOp.NOT_EQUAL) {
return LiteralExpression.newConstant(true, PDataType.BOOLEAN, true);
} else { // TODO: generalize this with PDataType.getMinValue(), PDataTypeType.getMaxValue() methods
switch(rhsExprDataType) {
case DECIMAL:
/*
* We're comparing an int/long to a constant decimal with a fraction part.
* We need the types to match in case this is used to form a key. To form the start/stop key,
* we need to adjust the decimal by truncating it or taking its ceiling, depending on the comparison
* operator, to get a whole number.
*/
int increment = 0;
switch (node.getFilterOp()) {
case GREATER_OR_EQUAL:
case LESS: // get next whole number
increment = 1;
default: // Else, we truncate the value
BigDecimal bd = (BigDecimal)rhsValue;
rhsValue = bd.longValue() + increment;
children = Arrays.asList(children.get(0), LiteralExpression.newConstant(rhsValue, lhsExprDataType, lhsExpr.getColumnModifier(), rhsExpr.isDeterministic()));
break;
}
break;
case LONG:
/*