fix(core): fix internal errors due to bad implicit casting#6443
fix(core): fix internal errors due to bad implicit casting#6443bluestreak01 merged 17 commits intomasterfrom
Conversation
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThis PR refactors the type conversion system by removing the Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Areas requiring extra attention:
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Tip 📝 Customizable high-level summaries are now available in beta!You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.
Example instruction:
Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
previously, isToSameOrWider() would return true because `isBuiltInWideningCast()` accidentally returned true too. even there is no builtin casting between these types.
|
@coderabbitai - please review |
|
@jerrinot: I'll review the changes in this PR. 🧠 Learnings used✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
core/src/main/java/io/questdb/griffin/engine/functions/conditional/SwitchFunctionFactory.java (1)
95-100: Keys are not being cast when convertible types require casting functions.The code has an asymmetric type conversion pattern:
- Lines 86-104 (key loop,
i = 1, 3, 5...): UsesisConvertibleFromto validate key types but applies NO casting tokeyFunc- Lines 106-119 (value loop,
i = 2, 4, 6...): AppliesCaseCommon.getCastFunctionto convert values toreturnType- Lines 121-125 (else branch): Also applies
CaseCommon.getCastFunctionSince
isConvertibleFrommay allow conversions requiring casting functions, keys should also receive casting viagetCastFunctionto ensure proper type conversion at runtime, similar to how values and the else branch are handled. Add a loop to cast key arguments (at indices 1, 3, 5...) tokeyTypeafter the value casting loop.
🧹 Nitpick comments (2)
core/src/test/java/io/questdb/test/cairo/ColumnTypeTest.java (1)
27-313: Robust contract test forisBuiltInWideningCast, minor range clarificationThe new
testIsBuiltInWideningCastContractpluscallGetterForType/createTestFunctionis a solid way to pin down the contract that “ifisBuiltInWideningCast(from, to)is true, then the corresponding getter must not throwUnsupportedOperationException”, while still toleratingImplicitCastExceptionfor value‑level failures and logging cases where getters work but aren’t advertised as widening casts. This should make future changes toColumnTypemuch safer.One thing to double‑check: the iteration bounds
short allTypesLowerBoundInc = ColumnType.UNDEFINED + 1; short allTypesUpperBoundEx = ColumnType.NULL;mean you never exercise
fromType == ColumnType.NULLortoType == ColumnType.NULL, even thoughisBuiltInWideningCasthas special behavior forfromTag == NULL. If the intent is to include NULL in the contract check, it might be clearer to either extend the upper bound to include it or add it explicitly, and to document which tags are intentionally excluded viaunsupportedTypes. Otherwise, the helpers and the rest of the test logic look correct and comprehensive.core/src/test/java/io/questdb/test/griffin/engine/functions/rnd/LongSequenceTest.java (1)
35-38: Consider adding a positive test case for completeness.The negative test case correctly validates graceful failure for incompatible argument types. Consider adding a complementary positive test case that verifies
long_sequenceworks correctly with valid integer arguments (e.g.,long_sequence(1, 5, 1)), ensuring the fix doesn't break valid usage.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (18)
core/src/main/java/io/questdb/cairo/ColumnType.java(4 hunks)core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java(1 hunks)core/src/main/java/io/questdb/griffin/SqlCompilerImpl.java(5 hunks)core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayAccessFunctionFactory.java(2 hunks)core/src/main/java/io/questdb/griffin/engine/functions/conditional/SwitchFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/rnd/LongSequenceFunctionFactory.java(2 hunks)core/src/main/java/io/questdb/griffin/engine/functions/rnd/RndDoubleArrayFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/window/LagDateFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/window/LagDoubleFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/window/LagLongFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/window/LagTimestampFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/window/LeadDateFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/window/LeadDoubleFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/window/LeadLongFunctionFactory.java(1 hunks)core/src/main/java/io/questdb/griffin/engine/functions/window/LeadTimestampFunctionFactory.java(1 hunks)core/src/test/java/io/questdb/test/cairo/ArrayTest.java(5 hunks)core/src/test/java/io/questdb/test/cairo/ColumnTypeTest.java(3 hunks)core/src/test/java/io/questdb/test/griffin/engine/functions/rnd/LongSequenceTest.java(1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-07-31T08:21:17.390Z
Learnt from: mtopolnik
Repo: questdb/questdb PR: 5997
File: core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayAccessFunctionFactory.java:127-131
Timestamp: 2025-07-31T08:21:17.390Z
Learning: In DoubleArrayAccessFunctionFactory.java, overflow validation for array index casting from long to int is performed in the validateIndexArgs() method rather than at each individual cast site. The validateIndexArgs() method is called early in newInstance() to validate all index arguments, making subsequent casts safe.
Applied to files:
core/src/main/java/io/questdb/griffin/engine/functions/rnd/RndDoubleArrayFunctionFactory.javacore/src/main/java/io/questdb/griffin/engine/functions/rnd/LongSequenceFunctionFactory.javacore/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayAccessFunctionFactory.javacore/src/test/java/io/questdb/test/cairo/ArrayTest.javacore/src/main/java/io/questdb/griffin/engine/functions/window/LagDoubleFunctionFactory.java
📚 Learning: 2025-11-19T12:21:00.062Z
Learnt from: jerrinot
Repo: questdb/questdb PR: 6413
File: core/src/test/java/io/questdb/test/cutlass/pgwire/PGJobContextTest.java:11982-12002
Timestamp: 2025-11-19T12:21:00.062Z
Learning: QuestDB Java tests use a deterministic random seed. The test utilities (e.g., io.questdb.test.tools.TestUtils and io.questdb.std.Rnd) produce reproducible sequences, so rnd_* functions (including rnd_uuid4) yield deterministic outputs across runs. Do not flag tests in core/src/test/** that assert against values produced by rnd_* as flaky due to randomness.
Applied to files:
core/src/main/java/io/questdb/griffin/engine/functions/rnd/RndDoubleArrayFunctionFactory.javacore/src/test/java/io/questdb/test/griffin/engine/functions/rnd/LongSequenceTest.javacore/src/test/java/io/questdb/test/cairo/ArrayTest.java
📚 Learning: 2025-08-04T09:16:27.366Z
Learnt from: jerrinot
Repo: questdb/questdb PR: 5996
File: core/src/test/java/io/questdb/test/cutlass/http/line/LineHttpSenderTest.java:350-356
Timestamp: 2025-08-04T09:16:27.366Z
Learning: In QuestDB ILP tests, maximum dimension values like `(1 << 28) - 1` are intentionally hard-coded instead of using constants like `ArrayView.DIM_MAX_LEN` to serve as "canaries" that will cause test failures if the API limits change, ensuring developers are aware of breaking changes to the ILP API.
Applied to files:
core/src/test/java/io/questdb/test/cairo/ArrayTest.java
🧬 Code graph analysis (15)
core/src/main/java/io/questdb/griffin/engine/functions/window/LeadDoubleFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/SqlCompilerImpl.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/rnd/RndDoubleArrayFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/test/java/io/questdb/test/cairo/ColumnTypeTest.java (10)
core/src/main/java/io/questdb/std/Rnd.java (1)
Rnd(39-481)core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)core/src/main/java/io/questdb/griffin/engine/functions/constants/NullConstant.java (1)
NullConstant(46-296)core/src/main/java/io/questdb/griffin/engine/functions/constants/GeoByteConstant.java (1)
GeoByteConstant(33-57)core/src/main/java/io/questdb/griffin/engine/functions/constants/GeoShortConstant.java (1)
GeoShortConstant(33-58)core/src/main/java/io/questdb/griffin/engine/functions/constants/GeoIntConstant.java (1)
GeoIntConstant(33-58)core/src/main/java/io/questdb/griffin/engine/functions/constants/GeoLongConstant.java (1)
GeoLongConstant(33-58)core/src/main/java/io/questdb/griffin/engine/functions/constants/NullBinConstant.java (1)
NullBinConstant(33-56)core/src/main/java/io/questdb/griffin/engine/functions/constants/Long128Constant.java (1)
Long128Constant(32-63)core/src/main/java/io/questdb/griffin/engine/functions/constants/IPv4Constant.java (1)
IPv4Constant(32-59)
core/src/main/java/io/questdb/griffin/engine/functions/window/LagTimestampFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/rnd/LongSequenceFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayAccessFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/test/java/io/questdb/test/cairo/ArrayTest.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/window/LagDateFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/window/LeadTimestampFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/window/LagLongFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/window/LeadDateFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/window/LagDoubleFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
core/src/main/java/io/questdb/griffin/engine/functions/window/LeadLongFunctionFactory.java (1)
core/src/main/java/io/questdb/cairo/ColumnType.java (1)
ColumnType(58-1153)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (35)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-other)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-pgwire)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-cairo-sub)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-cairo-root)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-fuzz2)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-fuzz1)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-griffin-sub)
- GitHub Check: New pull request (Rust Test and Lint on linux-jdk17)
- GitHub Check: New pull request (SelfHosted Running tests with cover on linux-griffin-root)
- GitHub Check: New pull request (SelfHosted Griffin tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Other tests on linux-arm64)
- GitHub Check: New pull request (SelfHosted Other tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Other tests on linux-x86-graal)
- GitHub Check: New pull request (Hosted Running tests on windows-other-2)
- GitHub Check: New pull request (Hosted Running tests on windows-other-1)
- GitHub Check: New pull request (Hosted Running tests on windows-pgwire)
- GitHub Check: New pull request (Hosted Running tests on windows-cairo-2)
- GitHub Check: New pull request (Hosted Running tests on windows-cairo-1)
- GitHub Check: New pull request (Hosted Running tests on windows-fuzz2)
- GitHub Check: New pull request (Hosted Running tests on windows-fuzz1)
- GitHub Check: New pull request (Hosted Running tests on windows-griffin-sub)
- GitHub Check: New pull request (Hosted Running tests on windows-griffin-base)
- GitHub Check: New pull request (Hosted Running tests on mac-other)
- GitHub Check: New pull request (Hosted Running tests on mac-pgwire)
- GitHub Check: New pull request (Hosted Running tests on mac-cairo-fuzz)
- GitHub Check: New pull request (Hosted Running tests on mac-cairo)
- GitHub Check: New pull request (SelfHosted Cairo tests on linux-x64-zfs)
- GitHub Check: New pull request (SelfHosted Griffin tests on linux-x86-graal)
- GitHub Check: New pull request (SelfHosted Griffin tests on linux-arm64)
- GitHub Check: New pull request (Hosted Running tests on mac-griffin)
- GitHub Check: New pull request (SelfHosted Cairo tests on linux-x86-graal)
- GitHub Check: New pull request (SelfHosted Cairo tests on linux-arm64)
- GitHub Check: New pull request (Check Changes Check changes)
- GitHub Check: build
- GitHub Check: build
🔇 Additional comments (18)
core/src/main/java/io/questdb/griffin/engine/functions/array/DoubleArrayAccessFunctionFactory.java (2)
160-162: LGTM! More precise type checking for array indices.The change from a broad assignability check to
isSameOrBuiltInWideningCastcorrectly restricts array indices to LONG or types with built-in widening casts to LONG (e.g., INT, SHORT, BYTE). This prevents implicit casting issues mentioned in the PR objectives, such as mixed numeric index types likearr[1, 3.0].
164-171: LGTM! Improved error message readability.Using
ColumnType.nameOf(argType)instead of the raw type code provides users with a human-readable type name in error messages (e.g., "DOUBLE" instead of "8").core/src/main/java/io/questdb/griffin/engine/functions/window/LeadLongFunctionFactory.java (1)
68-70: LGTM! Fixes the lead/lag default value type mismatch.This change correctly prevents DOUBLE from being used as a default value for LONG, addressing one of the internal error reproductions mentioned in the PR objectives. The new check ensures only LONG or types with built-in widening casts to LONG (e.g., INT, SHORT, BYTE) are accepted.
core/src/main/java/io/questdb/griffin/engine/functions/window/LagDateFunctionFactory.java (1)
67-69: Types that can widen to DATE are correctly restricted to NULL only.The implementation in
ColumnType.isBuiltInWideningCast(lines 460-475) confirms that numeric types (BYTE, SHORT, CHAR) cannot widen to DATE due to explicit exclusions on lines 465-467. Only NULL (line 470) can widen to DATE, and TIMESTAMP/LONG correctly widen FROM DATE to LONG (lines 472-473), not the reverse. The validation inLagDateFunctionFactory(line 67) andLeadDateFunctionFactory(line 68) correctly usesisSameOrBuiltInWideningCast, accepting only DATE itself or NULL as valid default values, which fixes the false positives mentioned in the PR description.core/src/main/java/io/questdb/griffin/engine/functions/window/LeadTimestampFunctionFactory.java (1)
69-71: Verify TIMESTAMP variant compatibility—critical gap discovered.The change correctly tightens validation and fixes the false positives (BYTE→TIMESTAMP, SHORT→TIMESTAMP, CHAR→TIMESTAMP). However, verification reveals an undocumented limitation: TIMESTAMP_NANO and TIMESTAMP_MICRO are treated as incompatible types by
isBuiltInWideningCast.When a column is TIMESTAMP_NANO and the default value is TIMESTAMP_MICRO (or vice versa), validation fails because both variants have the same base tag (8) but different encoded values, and there is no special handling for TIMESTAMP precision variant conversion in the widening cast logic. This means:
- Column
ts TIMESTAMP_NANOwith default valuecast(x as TIMESTAMP)(microseconds) → rejected- No explicit test coverage exists for this scenario in window function tests
The error message "default value must be can cast to timestamp" is also misleading, as it implies broader compatibility than actually supported—it requires the same TIMESTAMP precision.
Confirm whether TIMESTAMP variant incompatibility is intentional or requires documentation/test coverage.
core/src/main/java/io/questdb/griffin/SqlCodeGenerator.java (1)
749-754:isConvertibleFromusage and argument order look correctUsing
ColumnType.isConvertibleFrom(func.getType(), type)here aligns with the documented(fromType, toType)contract and enforces that only constant/runtime‑constant expressions that are actually convertible to the required target type are accepted before proceeding, which matches the intent ofcoerceRuntimeConstantType.core/src/main/java/io/questdb/griffin/engine/functions/rnd/RndDoubleArrayFunctionFactory.java (1)
110-127: Integer argument validation correctly aligned with built‑in widening semanticsSwitching to
ColumnType.isSameOrBuiltInWideningCast(arg.getType(), ColumnType.LONG)before callinggetLong(null)matches the new contract tests and ensures we only accept args with a safe built‑in long getter, while the explicit0..Integer.MAX_VALUErange check keeps overflow handling central and predictable. LGTM.core/src/main/java/io/questdb/griffin/engine/functions/window/LagDoubleFunctionFactory.java (1)
67-70: Safe default‑value validation for DOUBLE lagThe new
isSameOrBuiltInWideningCast(defaultValue.getType(), ColumnType.DOUBLE)check correctly guarantees thatdefaultValue.getDouble(record)is only used for types with a built‑in double getter, preventing internalUnsupportedOperationExceptions. Looks good.core/src/main/java/io/questdb/griffin/engine/functions/window/LeadDoubleFunctionFactory.java (1)
67-70: Lead default‑value check consistent with DOUBLE semanticsUsing
ColumnType.isSameOrBuiltInWideningCast(defaultValue.getType(), ColumnType.DOUBLE)here mirrors the lag implementation and ensures only types with a safe built‑in double getter are accepted as defaults. No issues spotted.core/src/main/java/io/questdb/griffin/engine/functions/window/LagLongFunctionFactory.java (1)
67-70: Long default‑value validation correctly narrowed to built‑in castsThe switch to
ColumnType.isSameOrBuiltInWideningCast(defaultValue.getType(), ColumnType.LONG)is appropriate given we calldefaultValue.getLong(record)in the implementation. This avoids internal errors for types that claimed generic assignability but lack a long getter.core/src/main/java/io/questdb/griffin/engine/functions/window/LagTimestampFunctionFactory.java (1)
67-71: Timestamp default validation correctly tied to argument’s timestamp flavorValidating with
ColumnType.isSameOrBuiltInWideningCast(defaultValue.getType(), args.getQuick(0).getType())aligns the default with the actual timestamp flavor of the first argument and ensuresdefaultValue.getTimestamp(record)integrates safely withTimestampDriver.from(..). This should eliminate the previous ambiguous‑cast/UOE corner cases.core/src/main/java/io/questdb/griffin/engine/functions/rnd/LongSequenceFunctionFactory.java (2)
65-78: Graceful handling of non‑integral single argument typesUsing
ColumnType.isConvertibleFrom(countFunc.getType(), ColumnType.LONG)as a guard and then translating anyUnsupportedOperationExceptionfromcountFunc.getLong(null)into aSqlExceptionwith a clear “argument type X is not supported” message addresses the internal‑error issue cleanly while still allowing all long‑convertible constants. The slight redundancy between the convertibility check and the try/catch is acceptable here and keeps error messages more targeted than a pure try‑only approach.If you want to tighten the contract further, you could consider switching this branch to
isSameOrBuiltInWideningCastas well and relying less on theUnsupportedOperationExceptionpath, but that would narrow which implicit casts are accepted and might be a behavioral change—worth double‑checking against existing queries and tests.
80-93: Seeded variant correctly restricted to built‑in long wideningsFor the 3‑argument seeded form, requiring
ColumnType.isSameOrBuiltInWideningCast(_, ColumnType.LONG)oncount,seedLo, andseedHibefore callinggetLong(null)ensures that only types with a guaranteed long getter participate, which matches the strengthenedisBuiltInWideningCastcontract and removes the need for defensive exception handling here. The semantics between unseeded and seeded variants are now intentionally different but consistent with the broader type‑system refactor.core/src/main/java/io/questdb/griffin/engine/functions/window/LeadDateFunctionFactory.java (1)
67-70: Date default‑value check aligned with DATE getter contractThe move to
ColumnType.isSameOrBuiltInWideningCast(defaultValue.getType(), ColumnType.DATE)matches the later use ofdefaultValue.getDate(record)and avoids advertising castability when no date getter exists. This looks correct and consistent with the other window factories.core/src/test/java/io/questdb/test/griffin/engine/functions/rnd/LongSequenceTest.java (1)
40-43: LGTM!The factory override is correctly implemented according to the test framework pattern.
core/src/main/java/io/questdb/griffin/SqlCompilerImpl.java (3)
764-764: LGTM!The change from
isAssignableFromtoisConvertibleFromcorrectly clarifies the semantics for timestamp UPDATE cast validation. The method now explicitly checks for any available conversion path, which aligns with the PR's objective to fix implicit casting issues.
2783-2783: LGTM!The conversion checks in the INSERT AS SELECT flow now correctly use
isConvertibleFrom, which checks for any available conversion path (including narrowing casts). This is appropriate since the code later generates the necessaryRecordToRowCopierto handle the actual data conversion, and incompatible types are properly rejected with clear error messages.Also applies to: 2839-2839
4038-4039: LGTM!The UPDATE operation type validation correctly uses
isConvertibleFromfor checking string/varchar compatibility. This allows the necessary flexibility for UPDATE statements while maintaining proper error reporting for truly incompatible types. The changes are consistent with the broader refactoring in this PR.
|
@jerrinot please send Ent tandem |
|
@jerrinot you're still unlucky: |
|
@puzpuzpuz the failing test is now fixed |
[PR Coverage check]😍 pass : 39 / 40 (97.50%) file detail
|
This change fixes internal errors caused by the database being too lenient with type checking.
Example no. 1 - default values for lead/lag window functions:
Example no. 2 - array access
Example no. 3 - rnd_double_array() SQL function:
Before this change, all of the examples above would fail with an internal error. After the change, they fail gracefully and produce a sensible error message.
Implementation details:
I suspect the main contributor to these bugs was poor naming:
ColumnType.isAssignableFrom(fromType, toType)creates the impression that if it returns true for some combination ofTypeAandTypeB, then it's legal to invoketypeAFunction.getTypeB(). Without adding any cast function. However, this is not the contract ofisAssignableFrom(). This method merely indicates if it's possible to convert typeA and typeB at all, even it might requires a casting function. To prevent this confusion I renamed it fromisAssignableFrom()toisConvertibleFrom()+ added JavaDoc to explain the meaning. + I replaced the obviously bad invocations ofisAssignableFrom()withisBuiltInWideningCast()orisSameOrBuiltInWideningCast().This PR also fixes another bug:
ColumnType.isBuiltInWideningCast()indicated some types were convertible between each other without any casting function even that was not the case. Example:ShortFunction.getTimestamp()throwsUnsupportedOperationExceptionbutisBuiltInWideningCast(short, timestamp)used to return true. This is no longer the case. There were 3 cases in total, where the function used to lie:All these cases are now fixed. Insert used to rely on these bugs - since they would propagate to
isToSameOrWider()and the insert machinery would create a casting function. After removing these cases fromisBuiltInWideningCast(), I had to re-add them toisImplicitParsingCast()where they belongs anyway. It is a widening cast, but it is not built-in to base functions.There is one more change to
isBuiltInWideningCast(): It used to indicate that STRING can be converted to numeric (which is the case), but it would not advertise that VARCHAR can be converted to numeric just as well. I suspect this could have led to creating unnecessary casting functions. So I fixed this too.I added a test validating that if
isBuiltInWideningCast()returns true for some pair of types then we won't get theUnsupportedOperationExceptionwhen we try to use them as such. I also tried to add the inverse test - that ifisBuiltInWideningCast()returnsfalsethen types cannot be converted without a casting function, but this is more complicated - for exampleBooleanFunctionhas a function implementation ofgetByte(), butisBuiltInWideningCast()does not advertise it. I assume it's because it's not merely widening, but an actual conversion which should always be explicit. There are other cases like this, so I decided to just print them, instead of failing the test. Here is a list of these pairs:fixes #6429
Considerations for reviewers: The bugs caused by accidental usage of
isAssignableFrom()are directly connected to function signatures with vararg. Since without varargFunctionParsercreates casting functions as necessary and the target function receives arguments already converted to the expected type. However, functions accepting varargs are on their own - ideally, they should create implicit casting functions consistently with FunctionParser. That is NOT the case with this current state of the PR! This means even the fixed function will NOT perform implicit casting unless the casting is builtin.tandem: https://github.com/questdb/questdb-enterprise/pull/797