allthingssecurity opened a new pull request, #28752: URL: https://github.com/apache/camel/pull/28752
# Description [CAMEL-26329](https://issues.apache.org/jira/browse/CAMEL-26329), a follow-up of [CAMEL-25013](https://issues.apache.org/jira/browse/CAMEL-25013) (#26873, #26930) #26873 made `ObjectHelper.typeCoerceCompare` compare a number and a numeric `String` by their values, and its 4.23 upgrade guide entry lists "a number compared with a numeric `String`" among the comparisons that compare values (its examples include `==`). When #26930 did the same for `in`, the review said it "makes `in` agree with `==`". But `typeCoerceEquals` only got the number/number case. When a number and a numeric `String` are written differently, `==` still converted the `String` to the type of the number and used `equals` (`BigDecimal.equals` also compares the scale), or compared the text. So `==` and `!=` disagreed with `<=`, `>=` and `in`: ``` amount = BigDecimal 10.00 (a DECIMAL(10,2) column, for example) ${header.amount} <= '10' true ${header.amount} >= '10' true ${header.amount} == '10' false (also with a String header "10" on the right) ${header.text} == 10 false for a String "10.00" (<= and >= are true) ${header.count} == '3.0' false for an Integer 3, while ${header.count} in '3.0' is true ``` `simple-operators.adoc` shows `${header.bar} == '100'` as the way to compare a header with a number, so a `choice()` on a `BigDecimal` amount with a scale took the wrong branch, with no error. The `==` result is the same in older versions; 4.23 made the other operators compare the values, and its guide entry says so. Changes: - `typeCoerceEquals`: a number other than a `Double` or `Float`, and a `String` that parses as a `BigDecimal` (leading and trailing spaces ignored, as the `in` operator does since #26930), are compared with the same `compareNumbers` that `typeCoerceCompare` uses. Both argument orders are covered. - `typeCoerceCompare` parses the `String` the same way (`asNumber`), so `<`/`>` with `" 9007199254740992"` or `"1e0"` compare exact values instead of doubles, and agree with `==`. For plain decimal text such as `10.5` nothing changes. - The text is checked to have the form of a number (sign, digits, decimal point, exponent) before it is parsed, so text that is not a number, including text such as `-` or `2026-10-26`, is not handed to the `BigDecimal` parser and throws no `NumberFormatException` there (the type converter fallback for such text is as before). - Text longer than 1000 characters is not parsed as a number. Parsing a `BigDecimal` takes time that grows with the square of the number of digits: with this change and no limit, `5 ==` a decimal `String` of 100,000 digits (`111...1.5`) took 129 ms (1 ms on main) and a million digits take about 12 s. With the limit, `==` and `<`/`>` with such text take about 1 ms; on main, `<` and `>` with a 100,000-digit decimal already took 126 ms. - Unchanged: text that is not a number, `null`, two `String` values (the numeric comparison of digit strings from CAMEL-24580 stays as is), and `Double`/`Float` values, which were already compared with a numeric `String` by converting it to a double. - Upgrade guide (4.24): a `camel-core` entry. `==` and `!=` give different results than in 4.23 for these values, in `filter`, `choice`, `=~`, `contains` on a collection, and the Java DSL and mock predicates (`header("amount").isEqualTo("10")`). Users who want the text compared can use `equals`. - This is for main only: the older branches do not have the value comparison of CAMEL-25013. Who sees a difference: only a comparison where one side is a `Number` object (a JDBC `BigDecimal`, an integer JSON value, an `Integer` header) and the other side is a `String` with the same value written differently (`'10'` vs `10.00`, `'3.0'`, `' 7'`, `'1e3'`). A header that is a `String` compared with a quoted literal is `String` vs `String`, and that is unchanged. So are leading zeros with an `Integer` or `Long`: `7 == '007'` was already `true`. A phone number such as `"+4915112345678"` was already equal to the `Long` 4915112345678, through the type converter. No comparison that was `true` becomes `false`: I compared 385 number/text pairs in both orders, and 29 Simple, Java DSL and `contains` predicates, on main and with this change. Only the cases above changed, from `false` to `true` (and `!=` from `true` to `false`), plus the ordering of `" 9007199254740992"`. Tests: - `TypeCoerceCompareTest`: `testEqualsNumberAndNumericStringWrittenDifferently`, `testCompareNumberAndNumericStringWithSpaceOrExponent`, `testEqualsNumberAndTextThatIsNotANumber` and `testEqualsNumberAndTooLongNumericString`; `SimpleOperatorTest.testEqualsNumberWithNumericTextWrittenDifferently`. All five fail on main and pass with the change. `testEqualsNumberAndTooLongNumericString` also fails without the length limit. The negative cases of `testEqualsNumberAndTextThatIsNotANumber` (such as `-` or `2026-10-26`) pass on main too. They check that the form check before parsing does not change the result. - Offline, on main e4c4dbbeb5da with this change applied (the files of this commit are the same there as on its base aa63c0a4cb94): the whole of camel-core, 8238 tests, 0 failures, 45 skipped, 1 error in `ValidatorExternalResourceTest`, which loads an XSD from raw.githubusercontent.com and fails the same way on main without network access (2 file tests failed once on file timing and passed on the rerun); the tests of the other core modules, 450 tests, 0 failures. The formatter and import-sort plugins ran and changed nothing. How it was found: a differential fuzz harness of the Simple numeric operators (`==`, `!=`, `<`, `<=`, `>`, `>=`, `range`, `in` on typed `Integer`/`Long`/`Short`/`BigDecimal`/`BigInteger`/`String` headers against quoted, unquoted and header right sides, checked against a comparison of the values). Of 5 million inputs, 1,829 were wrong on main. Every one was `==` or `!=` of a number and a numeric `String` with the same value written differently, where `<=` and `>=` were both true. I ran the same 5 million inputs with this change in a second class loader: all 1,829 are now right and no other input gives a different result. # Target - [x] I checked that the commit is targeting the correct branch (Camel 4 uses the `main` branch) # Tracking - [x] If this is a large change, bug fix, or code improvement, I checked there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL-26329) filed for the change (usually before you start working on it): [CAMEL-26329](https://issues.apache.org/jira/browse/CAMEL-26329). # Apache Camel coding standards and style - [x] I checked that each commit in the pull request has a meaningful subject line and body. - [ ] I have run `mvn clean install -DskipTests` locally from root folder and I have committed all auto-generated changes. (I ran the core module tests above with the formatter and import-sort plugins; no generated files are affected. I did not run the full root build.) # AI-assisted contributions - [x] If this PR includes AI-generated code, commits have proper co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR description identifies the AI tool used. This PR was prepared with Claude Code (Claude Opus 5.5). The commit carries a `Co-Authored-By` trailer. _Claude Code on behalf of allthingssecurity_ 🤖 Generated with [Claude Code](https://claude.com/claude-code) -- This is an automated message from the Apache Git Service. To respond to the message, please log on to GitHub and use the URL above to go to the specific comment. To unsubscribe, e-mail: [email protected] For queries about this service, please contact Infrastructure at: [email protected]
