public Expression visitLeave()

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());
                }
            }
        });
    }