Repository navigation
fix: [bug] narrow BIGINT to INTEGER for int-domain function arguments (#5660) - #5728
AjimelecGonzalez wants to merge 1 commit into
Conversation
PR Reviewer Guide 🔍(Review updated until commit 4893e91)Here are some key observations to aid the review process:
|
PR Code Suggestions ✨Latest suggestions up to 19d66ce Explore these optional code suggestions:
Previous suggestionsSuggestions up to commit d277c86
Suggestions up to commit 4893e91
Suggestions up to commit 4893e91
Suggestions up to commit 2dde495
Suggestions up to commit 6b14555
|
c1295f6 to
0d3b11a
Compare
|
Persistent review updated to latest commit 0d3b11a |
dai-chen
left a comment
There was a problem hiding this comment.
High level question: the root cause is we widen arithmetic expressions to avoid overflow "unconditionally", can we list the options we've explored besides current PR approach?
|
FYI, in case it's useful — I did a quick check of how other databases handle integer arithmetic overflow. There are two independent decisions (partially done in #5603), and they explain our current implementation challenge in Calcite's combination. Option 1 — what type does
|
| Option | Engines | INT + INT |
consequence |
|---|---|---|---|
| Keep narrow | PostgreSQL, Calcite, Spark, Trino, DuckDB, SQL Server | INT |
overflow is possible at 32 bits, so Option 2 applies there |
| Widen one tier | MySQL, ClickHouse | BIGINT / Int64 |
overflow unreachable at 32 bits (int32×int32 = 2⁶² < 2⁶³); Option 2 applies at the 64-bit ceiling |
| One integer width | BigQuery (INT/SMALLINT/BIGINT are aliases for INT64) |
INT64 |
no narrow tier exists; Option 2 applies at 64 bits |
Note: widening only works because those engines' function libraries read every integer argument at 64 bits (MySQL
val_int()→longlong, ClickHousegetInt, BigQuery INT64-only, Trino@SqlType(INTEGER) long) — unlike Calcite, whoseleft/right/position/arrayItemOptional/sround/struncatetake primitiveintwith nolongoverload.
Option 2 — what happens when a value doesn't fit the type it was given?
| Option | Engines | observed |
|---|---|---|
| Throw | PostgreSQL, DuckDB, Trino, Spark-ANSI, SQL Server, BigQuery, MySQL | PG 2147483647+1 → ERROR 22003 integer out of range (int4pl) · DuckDB → Out of Range Error: Overflow in addition of INT32 · MySQL 9223372036854775807+1 → ERROR 1690 (22003) BIGINT value is out of range |
| No check — result wraps | Calcite default, Spark legacy, DataFusion, ClickHouse | Calcite 2147483647+1 → -2147483648 · ClickHouse toInt64(9223372036854775807)+1 → -9223372036854775808 |
| Per-call opt-out → NULL | BigQuery SAFE_ADD, Spark try_add, Trino/DuckDB TRY() |
explicit escape hatch layered over one of the above — never a default |
0d3b11a to
f6d8acb
Compare
|
Persistent review updated to latest commit f6d8acb |
As for the options explored: Options explored:
|
f6d8acb to
f1957a4
Compare
|
Persistent review updated to latest commit f1957a4 |
f1957a4 to
6b14555
Compare
|
Persistent review updated to latest commit 6b14555 |
Codecov Report❌ Patch coverage is ❌ Your project check has failed because the head coverage (75.66%) 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 #5728 +/- ##
============================================
+ Coverage 75.19% 75.66% +0.46%
- Complexity 10362 10429 +67
============================================
Files 962 963 +1
Lines 41098 41244 +146
Branches 4683 4726 +43
============================================
+ Hits 30905 31207 +302
+ Misses 8896 8727 -169
- Partials 1297 1310 +13
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:
|
6b14555 to
2dde495
Compare
|
Persistent review updated to latest commit 2dde495 |
f0602e3 to
4893e91
Compare
|
Persistent review updated to latest commit 4893e91 |
1 similar comment
|
Persistent review updated to latest commit 4893e91 |
| * Register an operator whose runtime implementation takes a Java {@code int} at {@code | ||
| * intArgPositions}, deriving the type checker from the operator. See {@link #narrowIntArgs}. | ||
| */ | ||
| protected void registerIntArgOperator( |
There was a problem hiding this comment.
- Can we avoid separate register method because this requires future developers' awareness?
- I recall in previous comment we align the narrowing can be down during function resolution instead of registration? Ref: fix: [bug] narrow BIGINT to INTEGER for int-domain function arguments (#5660) #5728 (comment)
There was a problem hiding this comment.
Thanks, both are addressed in the latest commit PR. The separate register method is gone and every function uses plain registerOperator again. Narrowing now happens in PPLFuncImpTable#resolve, as we agreed. I had moved it back to registration by mistake when replacing the type-family inference.
Now, there's also no list to maintain. For Calcite built-ins, the new CalciteIntParameters reads the Java methods Calcite actually calls and narrows a BIGINT only where no overload accepts a long. PPL UDFs now take long instead of int, so they need no narrowing. UDFIntParameterTest and BigintArgumentCodegenTest fail the build if a new function breaks either rule, so nobody has to remember anything.
4893e91 to
d277c86
Compare
…opensearch-project#5660) PPL queries that pass integer arithmetic as an argument to functions requiring Java int parameters fail when Calcite is enabled: mvindex(arr, 1 + 1) -> CompileException: arrayItemOptional(List, long, ...) left('abcdef', 1 + 1) -> Unable to implement: SqlFunctions.left(String, long) round(123.456, 1 + 0) -> SqlFunctions.sround(BigDecimal, long) Root cause: PPL widens INTEGER arithmetic to BIGINT for overflow safety (opensearch-project#5603), so expressions like `1 + 1` produce BIGINT. Some runtime methods take Java int parameters. Since SqlTypeFamily.INTEGER contains BIGINT, the call passes type checking but fails at code generation because the JVM cannot auto-narrow long to int. Fix: narrow BIGINT arguments to INTEGER during function resolution, only where the runtime method needs a Java int. After the matched implementation builds its call, PPLFuncImpTable#resolve checks the call's Calcite operator: CalciteIntParameters reads the runtime methods Calcite registers for it (RexImpTable) and narrows a position only when some overload takes an int there and none accepts a long. When the methods cannot be read (ITEM), the signature alternative that matched the call decides instead. No function list is kept and no special register method is needed. Narrowed: ITEM on arrays (index), ARRAY_SLICE, LEFT, RIGHT, ROUND/TRUNCATE (scale), RAND (seed), LOCATE/POSITION (start), REGEXP_REPLACE_5 (position, occurrence). MVINDEX is covered through ITEM and ARRAY_SLICE. Not narrowed: SUBSTRING/SUBSTR (Calcite has long overloads that clamp), ROUND's value operand, and ITEM map keys. PPL UDFs (CONV, SHA2, WEEK, SYSDATE, TONUMBER, REX_EXTRACT, REX_EXTRACT_MULTI, SPAN) now take long instead of int, because their runtime methods are not registered with Calcite and cannot be inspected. Values that must be an int are converted with UserDefinedFunctionUtils#toIntArg, which fails with the same "Value N out of range" error as Calcite's cast. CONV's number, TIMESTAMPADD amounts and ADDDATE/SUBDATE days keep their BIGINT values. Tests: UDFIntParameterTest fails if a UDF method takes an int; BigintArgumentCodegenTest compiles every registered signature with BIGINT in place of INTEGER and fails if only the BIGINT version breaks. Overflow safety is preserved: arithmetic still computes in BIGINT; only the final value handed to an int parameter is narrowed. Values outside the int range fail with an out-of-range error rather than wrapping. Also fixes the pre-existing case of passing cast(x as long) to these functions. Issue: opensearch-project#5660 Signed-off-by: Ajimelec Gonzalez <ajimelec@amazon.com>
d277c86 to
19d66ce
Compare
Description
PPL queries that pass integer arithmetic as an argument to functions requiring Java
intparameters fail when Calcite is enabled:Root cause: PPL widens
INTEGERarithmetic toBIGINTfor overflow safety (#5603), so expressions like1 + 1produceBIGINT. Some runtime methods take a Javaintwith nolongversion. These include Calcite built-ins such asITEM,LEFT,RIGHT,ROUND,TRUNCATE,RANDandLOCATE, and PPL functions such asCONV,SHA2andWEEK. SinceSqlTypeFamily.INTEGERcontainsBIGINT, the call passes type checking but fails at code generation because Java cannot pass alongto anintparameter.Fix:
PPLFuncImpTable#resolvechecks the call's operator. The newCalciteIntParametersreads the Java methods Calcite uses for that operator (fromRexImpTable). ABIGINTargument is cast toINTEGERonly when some version of the method takes anintat that position and none takes along.registerOperator.ITEM), the signature that matched the call decides. SoITEM(array, index)is narrowed andITEM(map, key)is not.CalciteIntParametersreads Calcite's implementor table reflectively; if that ever fails (for example after a Calcite upgrade), it falls back to the same signature rule, andBigintArgumentCodegenTestfails the build if any function stops compiling.longinstead ofint.CONV,SHA2,WEEK,SYSDATE,TONUMBER,REX_EXTRACT,REX_EXTRACT_MULTIandSPANbuild their method calls themselves at code generation time, so there is no registered method to inspect. They now acceptlongdirectly and convert withUserDefinedFunctionUtils#toIntArgwhere anintis needed. This needs no narrowing and changes nothing for valid inputs.INTEGERBIGINTITEMindex (arrays) /mvindexSUBSTRING/SUBSTR(Calcite haslongversions)ARRAY_SLICEstart, length /mvindexrangeROUND/TRUNCATELEFT,RIGHTlengthITEMmap keysROUND,TRUNCATEdecimal placeslong), e.g.conv(4294967296, 10, 16)still returns100000000RANDseedTIMESTAMPADDamount,ADDDATE/SUBDATEdaysLOCATE/POSITIONstartREGEXP_REPLACE_5position, occurrenceOverflow safety is kept: the math still runs in
BIGINT, and only the final value passed to anintargument is narrowed. Math operators, comparisons, aggregations and math on long fields are not changed. If a value is too big for anint, likeleft(s, 3000000000), the query fails withValue 3000000000 out of rangeinstead of a code generation error. It never wraps to a wrong value.This also fixes passing
cast(x as long)to these functions, which failed before #5603 too.Testing:
UDFIntParameterTest: fails the build if any PPL function method takes anint, and teststoIntArg.BigintArgumentCodegenTest: goes through every registered function and compiles each signature withINTEGERarguments, then withBIGINT. It fails if only theBIGINTversion breaks, so new functions are checked automatically.PPLFuncImpTableNarrowBigintTest:ITEMarrays,ARRAY_SLICE,LEFT,LOCATE,TRUNCATE.ITEMmap keys,SUBSTRING, theROUNDvalue, all PPL function arguments, theTIMESTAMPADDamount, theADDDATEdays.CalciteArrayFunctionIT,CalciteTextFunctionIT,CalciteMathematicalFunctionIT,CalcitePPLBuiltinFunctionIT):mvindex,left,right,substring,locate,round,truncate,rand,conv,sha2,week,tonumberandsysdate;cast(x as long)arguments work formvindex,left,rightandround.conv(4294967296, 10, 16)returns100000000, andadddate(date('2020-01-01'), 1 + 1)returns2020-01-03.substringwith aBIGINTabove theintrange returns the same results as before this change.stats,sort,whereandeventstatsusing these functions with arithmetic all pass.Related Issues
Resolves #5660
Related to #5603 (introduced the integer arithmetic widening that exposed this)
Check 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.