in phoenix-core-client/src/main/java/org/apache/phoenix/expression/ComparisonExpression.java [117:280]
public static Expression create(CompareOperator op, List<Expression> children, ImmutableBytesWritable ptr, boolean rowKeyOrderOptimizable) throws SQLException {
Expression lhsExpr = children.get(0);
Expression rhsExpr = children.get(1);
PDataType lhsExprDataType = lhsExpr.getDataType();
PDataType rhsExprDataType = rhsExpr.getDataType();
if ((lhsExpr instanceof RowValueConstructorExpression || rhsExpr instanceof RowValueConstructorExpression) && !(lhsExpr instanceof ArrayElemRefExpression) && !(rhsExpr instanceof ArrayElemRefExpression)) {
if (op == CompareOperator.EQUAL || op == CompareOperator.NOT_EQUAL) {
List<Expression> andNodes = Lists.<Expression>newArrayListWithExpectedSize(Math.max(lhsExpr.getChildren().size(), rhsExpr.getChildren().size()));
rewriteRVCAsEqualityExpression(lhsExpr, rhsExpr, andNodes, ptr, rowKeyOrderOptimizable);
Expression expr = AndExpression.create(andNodes);
if (op == CompareOperator.NOT_EQUAL) {
expr = NotExpression.create(expr, ptr);
}
return expr;
}
rhsExpr = RowValueConstructorExpression.coerce(lhsExpr, rhsExpr, op, rowKeyOrderOptimizable);
// 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());
}
/*
At this point both sides should be in the same row format.
We add the inverts so the filtering can be done properly for mixed sort type RVCs.
The entire RVC has to be in ASC for the actual compare to work since compare simply does
a varbyte compare. See PHOENIX-4841
*/
RowValueConstructorExpressionRewriter rvcRewriter =
RowValueConstructorExpressionRewriter.getSingleton();
lhsExpr = rvcRewriter.rewriteAllChildrenAsc((RowValueConstructorExpression) lhsExpr);
rhsExpr = rvcRewriter.rewriteAllChildrenAsc((RowValueConstructorExpression) rhsExpr);
children = Arrays.asList(lhsExpr, rhsExpr);
} else if(lhsExprDataType != null && rhsExprDataType != null && !lhsExprDataType.isComparableTo(rhsExprDataType)) {
throw TypeMismatchException.newException(lhsExprDataType, rhsExprDataType,
toString(op, children));
}
Determinism determinism = lhsExpr.getDeterminism().combine(rhsExpr.getDeterminism());
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(null, PBoolean.INSTANCE, lhsExpr.getDeterminism());
}
}
Object rhsValue = null;
// TODO: use lhsExpr.isStateless instead
if (rhsExpr instanceof LiteralExpression) {
rhsValue = ((LiteralExpression)rhsExpr).getValue();
if (rhsValue == null) {
return LiteralExpression.newConstant(null, PBoolean.INSTANCE, rhsExpr.getDeterminism());
}
}
if (lhsValue != null && rhsValue != null) {
return LiteralExpression.newConstant(ByteUtil.compare(op,lhsExprDataType.compareTo(lhsValue, rhsValue, rhsExprDataType)), determinism);
}
// 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.getSortOrder() != lhsExpr.getSortOrder()
|| (rhsExprDataType.isFixedWidth() && rhsExpr.getMaxLength() != null &&
lhsExprDataType.isFixedWidth() && 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.getSortOrder(), determinism, rowKeyOrderOptimizable));
} else if (op == CompareOperator.EQUAL) {
return LiteralExpression.newConstant(false, PBoolean.INSTANCE, Determinism.ALWAYS);
} else if (op == CompareOperator.NOT_EQUAL) {
return LiteralExpression.newConstant(true, PBoolean.INSTANCE, Determinism.ALWAYS);
} else { // TODO: generalize this with PDataType.getMinValue(), PDataTypeType.getMaxValue() methods
if (rhsExprDataType == PDecimal.INSTANCE) {
/*
* 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 (op) {
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(lhsExpr, LiteralExpression.newConstant(rhsValue, lhsExprDataType, lhsExpr.getSortOrder(), rhsExpr.getDeterminism()));
break;
}
} else if (rhsExprDataType == PLong.INSTANCE) {
/*
* We are comparing an int, unsigned_int to a long, or an unsigned_long to a negative long.
* int has range of -2147483648 to 2147483647, and unsigned_int has a value range of 0 to 4294967295.
*
* If lhs is int or unsigned_int, since we already determined that we cannot coerce the rhs
* to become the lhs, we know the value on the rhs is greater than lhs if it's positive, or smaller than
* lhs if it's negative.
*
* If lhs is an unsigned_long, then we know the rhs is definitely a negative long. rhs in this case
* will always be bigger than rhs.
*/
if (lhsExprDataType == PInteger.INSTANCE ||
lhsExprDataType == PUnsignedInt.INSTANCE) {
switch (op) {
case LESS:
case LESS_OR_EQUAL:
if ((Long)rhsValue > 0) {
return LiteralExpression.newConstant(true, PBoolean.INSTANCE, determinism);
} else {
return LiteralExpression.newConstant(false, PBoolean.INSTANCE, determinism);
}
case GREATER:
case GREATER_OR_EQUAL:
if ((Long)rhsValue > 0) {
return LiteralExpression.newConstant(false, PBoolean.INSTANCE, determinism);
} else {
return LiteralExpression.newConstant(true, PBoolean.INSTANCE, determinism);
}
default:
break;
}
} else if (lhsExprDataType == PUnsignedLong.INSTANCE) {
switch (op) {
case LESS:
case LESS_OR_EQUAL:
return LiteralExpression.newConstant(false, PBoolean.INSTANCE, determinism);
case GREATER:
case GREATER_OR_EQUAL:
return LiteralExpression.newConstant(true, PBoolean.INSTANCE, determinism);
default:
break;
}
}
children = Arrays.asList(lhsExpr, LiteralExpression.newConstant(rhsValue, rhsExprDataType, lhsExpr.getSortOrder(), determinism));
}
}
}
// Determine if we know the expression must be TRUE or FALSE based on the max size of
// a fixed length expression.
if (children.get(1).getMaxLength() != null && lhsExpr.getMaxLength() != null && lhsExpr.getMaxLength() < children.get(1).getMaxLength()) {
switch (op) {
case EQUAL:
return LiteralExpression.newConstant(false, PBoolean.INSTANCE, determinism);
case NOT_EQUAL:
return LiteralExpression.newConstant(true, PBoolean.INSTANCE, determinism);
default:
break;
}
}
}
return new ComparisonExpression(children, op);
}