[
https://issues.apache.org/jira/browse/CAMEL-25013?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
Claus Ibsen updated CAMEL-25013:
--------------------------------
Fix Version/s: 4.23.0
> Simple and PredicateBuilder comparisons truncate decimals when the two
> numbers have different types: ${header.amount} > 100 is false for a
> BigDecimal 100.50, and 2.5 == 2 is true
> ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-25013
> URL: https://issues.apache.org/jira/browse/CAMEL-25013
> Project: Camel
> Issue Type: Bug
> Components: camel-core
> Reporter: shashank
> Priority: Minor
> Fix For: 4.23.0
>
>
> {{ObjectHelper.typeCoerceCompare}}
> ({{core/camel-support/src/main/java/org/apache/camel/support/ObjectHelper.java:281-363}})
> has fast paths for two values of the same type and for String/number pairs.
> When both values are numbers of *different* types ({{Double}} vs {{Integer}},
> {{BigDecimal}} vs {{Integer}}, {{BigDecimal}} vs {{Double}}, {{Float}} vs
> {{Long}}, ...), none of them applies and the code falls through to:
> {code:java}
> // if both values is numeric then compare using numeric
> Long leftNum = converter.tryConvertTo(Long.class, leftValue);
> Long rightNum = converter.tryConvertTo(Long.class, rightValue);
> if (leftNum != null && rightNum != null) {
> return leftNum.compareTo(rightNum);
> }
> {code}
> The type converter converts a {{Number}} to {{Long}} with {{longValue()}}
> ({{ObjectConverter.toLong(Number)}}), which drops the fractional part. So
> {{2.5}} and {{2}} compare as equal.
> {{typeCoerceEquals}} has the same problem in {{tryConverters}}
> ({{ObjectHelper.java:142-170}}): it converts the left value to the class of
> the right value, so {{Double 2.5}} becomes {{Integer 2}}, and {{2.5 == 2}} is
> true.
> These methods back all Simple comparison operators ({{>}}, {{>=}}, {{<}},
> {{<=}}, {{==}}, {{!=}}, {{range}}) and the Java DSL predicates
> ({{header("x").isGreaterThan(...)}}, {{isEqualTo(...)}}). The left side is
> often a {{Double}} (JSONPath, Jackson) or a {{BigDecimal}} (JDBC/SQL). The
> right side is often an integer literal. The result is silent wrong routing:
> no error and no log.
> *Reproduction* (standalone program against main):
> {noformat}
> typeCoerceCompare(Double 2.5, Integer 2) = 0 (expected > 0)
> typeCoerceCompare(BigDecimal 100.50, Integer 100) = 0 (expected > 0)
> typeCoerceCompare(BigDecimal 2.5, Double 2.0) = 0 (expected > 0)
> typeCoerceCompare(Double -0.9, Integer 0) = 0 (expected < 0)
> typeCoerceEquals(Double 2.5, Integer 2) = true (expected false)
> typeCoerceEquals(BigDecimal 99.99, Long 99) = true (expected false)
> header p = 2.5 (Double, BigDecimal or Float):
> ${header.p} > 2 -> false
> ${header.p} == 2 -> true
> ${header.p} != 2 -> false
> ${header.p} <= 2 -> true
> ${header.p} range '1..2' -> true
> ${header.p} > 2.0 -> false for BigDecimal and Float (true for
> Double)
> from("direct:order").choice()
> .when(simple("${header.amount} > 100")).to("large")
> .otherwise().to("small");
> amount = new BigDecimal("100.50") -> small
> amount = 100.75d -> small
> from("direct:java").filter(header("amount").isGreaterThan(100)) ...
> amount = BigDecimal 100.50 or Double 100.75 -> filtered out
> {noformat}
> Controls behave correctly: same-type values, the String "2.5" vs "2"
> (CAMEL-21109), and a String header "100.50". The same happens for a String
> right-hand side against a {{BigDecimal}}: {{typeCoerceCompare(BigDecimal 2.5,
> "2") = 0}}. A property-based test (3000 random Double/BigDecimal vs Integer
> pairs) shrinks the failure to {{0.01}} vs {{0}} comparing as equal.
> Two {{BigDecimal}} (or two {{BigInteger}}) values have no fast path either
> and go through the same {{Long}} fallback: {{typeCoerceCompare(BigDecimal
> 2.5, BigDecimal 2.4) = 0}}, and a {{BigInteger}} that does not fit in a long
> wraps. {{typeCoerceEquals}} of two {{BigDecimal}} values uses
> {{BigDecimal.equals}}, which also compares the scale, so {{2.50 == 2.5}} is
> false while {{<=}} and {{>=}} are both true.
> The same fallback code is in camel-2.25.4, camel-3.0.0, camel-4.0.0 and
> camel-4.14.0, so all maintained versions are affected.
> A Lean model proves the following. For every integer n ≥ 0 and every digit d
> = 1..9, the current code says {{n.d == n}}. Values without a fractional part
> always compare correctly, so the fix only changes the result when a value has
> a fractional part.
> *Proposed fix:* in {{typeCoerceCompare}}, after the existing fast paths,
> compare two numbers, or a number and a numeric String, by their values: as
> {{long}} when both are integral, otherwise as {{BigDecimal}}
> ({{Double}}/{{Float}} through their decimal representation, NaN and infinity
> as {{double}}). In {{typeCoerceEquals}}, do the same for two numbers of
> different types and for two {{BigDecimal}} values. {{2.0 == 2}} and {{2 <
> 2.5}} keep working, and {{2.5 == 2}} becomes false. Add an upgrade guide
> note, as existing routes may get a different result.
> _Filed with Claude Code on behalf of allthingssecurity._
--
This message was sent by Atlassian Jira
(v8.20.10#820010)