Repository navigation
Conversation
Calcite 1.42 allows a cast from a string to MAP or ARRAY, so leastRestrictive gave a string literal and an object or nested field the container as their common type. IN, BETWEEN and CASE then cast the string to a map or list, and the generated code failed to compile with HTTP 500. COALESCE declared a map column and failed with a ClassCastException when it took its string fallback. OpenSearchTypeFactory.leastRestrictive now returns null when a VARCHAR type meets an object or array type, which is what Calcite 1.41 returned. IN, BETWEEN and CASE return their existing 400 while analyzing. COALESCE returns a string column, as it already does for an object and a number. array() of an object and a string now fails while analyzing, as array() of an object and a number already does. Signed-off-by: Chayanin Noramuttha <cnoramut@amazon.com>
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Explore these optional code suggestions:
|
Codecov Report✅ All modified and coverable lines are covered by tests. ❌ Your project check has failed because the head coverage (63.83%) is below the target coverage (99.00%). You can increase the head coverage or adjust the target coverage. Additional details and impacted files@@ Coverage Diff @@
## main #5870 +/- ##
============================================
+ Coverage 63.80% 63.83% +0.03%
- Complexity 9070 9084 +14
============================================
Files 962 962
Lines 40989 41001 +12
Branches 4660 4666 +6
============================================
+ Hits 26152 26173 +21
+ Misses 13960 13951 -9
Partials 877 877
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
Description
where f in ('x', 'y'),where f between 'a' and 'z'andeval x = case(host = 'a', f else 'z')return 500 whenfis anobjectornestedfield, withCannot cast "java.lang.String" to "java.util.Map".coalesce(f, 'z')returns 500 with aClassCastExceptiononce a row hasfmissing.Root cause. Calcite 1.42 added two branches to
SqlTypeUtil.canCastFromthat accept a string as the source of a cast toMAPorARRAY.leastRestrictiveByCastrelies on that check, so a string and an object or nested field now get the container as their common type.visitIn,visitBetweenandvisitCasereject a query only when no common type exists. They now cast the string to a map, and the generated code fails to compile.EnhancedCoalesceFunctiontakes its return type from the same call, so it declares a map column and then puts the string in it.Fix.
OpenSearchTypeFactory.leastRestrictivereturns null when a string type meets an object or array type, which is the answer Calcite 1.41 gave. The three guards return their existing 400s again, andcoalescefalls back to thestringcolumn it already returns forcoalesce(f, 1).Before,
source=objs | where f in ('x', 'y') | fields host, HTTP 500After, the same query, HTTP 400
Notes
OpenSearchTypeFactoryTestpins the type answers, andissues/5856.ymlruns each query end to end. The error cases match the exception type, theanalyzingstage and the message as well as the status, because on the assertion-enabled test nodes the nestedincase already returns 400 without the fix. Without the fix, 3 unit tests and 7 of the 8 YAML cases fail, and the keyword control passes.coalescenever goes throughCalciteRexNodeVisitor. A stricterSqlTypeMappingRulecannot help, since the new branches return before the rule is read. The same method already special-casesVARBINARY. AnANYoperand is left to Calcite, which keepsANYas the common type.coalesce(obj, 'z')returns an object column while no row needs the fallback andarray(obj, 'z')returns an array thatmvindexthen fails on with 500. Both 200s arrived with the Calcite 1.42 bump in Bump Apache Calcite 1.41.0 -> 1.42.0 (CVE-2026-46718) #5619, and each is one step from a 500, thecoalescecolumn as soon as a row lacksobj. This branch returns the answermaingave before the bump for both, shown in the table below.These queries ran on
3a837689c, the lastmaincommit before the Calcite 1.42 bump in #5619, onmainat779bdf5d2, and on this branch, each built from source with Calcite enabled. This branch returns a byte-identical response to the first column on every row. The bump commit6a07689a7gives the same status codes as779bdf5d2.3a837689c, before #5619779bdf5d2where obj in ('x', 'y')where obj between 'a' and 'z'case(host = 'a', obj else 'z')where items in ('x', 'y'), nestedAssertionErrorcoalesce(obj, 'z'), a row lacksobjstringstringcoalesce(obj, 'z'), every row hasobjstringstructstringcoalesce(items, 'z'), a row lacksitemsstringstringcoalesce(obj, 1)stringstringstringarray(obj, 'z')arrayarray(obj, 'z')thenmvindex(x, 1)where host in ('a', 'c'), keyword controlThe nested
inrow hits an engine assertion first on these assertion-enabled nodes and returns 400AssertionErroronmain. Without assertions the same query returns 500 onmain, reproduced at866dafcbd.lookupwithappendwhere a string field and an object field share a name still returns 500. It now fails while analyzing withCannot infer return type for COALESCE, which by source reading is also what 1.41 did.unionof an object with a string returns 500 fromSchemaUnifier, which casts by type name without askingleastRestrictive, so this change does not touch it.Related Issues
Resolves #5856
Testing
Measured on a local single-node
mainat866dafcbdagainst the same index before and after, with Calcite and pushdown enabled. Across 105 queries, every changed line is one of the 500s turning into a 400 or a 200, a change listed in notes 3 and 4, or anarraycall that already returned 400 and now fails while analyzing. The keyword, long and temporalinandbetweenpaths,mvappend,append,multisearch,if,ifnulland anANYbranch are byte-identical.:core:test,:ppl:test,:opensearch:testCalcitePPLLookupITandCalcitePPLEnhancedCoalesceIT:integ-test:yamlRestTestCheck List
--signoffor-s.By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check here.