eldenmoon opened a new pull request, #68437:
URL: https://github.com/apache/doris/pull/68437

   ### What problem does this PR solve?
   
   Issue Number: None
   
   Related PR: #66204, #67675
   
   Problem Summary:
   
   A Variant schema template converts every value of a typed path to its 
declared type when a segment is written. The V2 path builder first infers one 
type for the path over every row written into the segment, then CASTs that 
column to the declared type and drops the values the CAST turns into NULL. When 
the rows put different value kinds on the path (a date string and a number, a 
string and a boolean, an integer and a double, an array and a scalar, or mixed 
elements inside an array), the inferred type is JSONB. CAST(JSONB -> DATE, 
DATETIME, IPV4, IPV6 or TIMESTAMPTZ) has no conversion at all (the FE also 
rejects `CAST(json AS DATE)`), so every value of the path is dropped, valid 
ones included. Whether a valid value survives therefore depends on the other 
rows of its segment; in a large load one stray number can wipe out a whole 
segment of dates.
   
   ```sql
   CREATE TABLE tpl2 (k INT, v VARIANT<'ts': DATETIME(3), 'd': DATE, 'i': INT>)
   DUPLICATE KEY(k) DISTRIBUTED BY HASH(k) BUCKETS 1 PROPERTIES 
("replication_num" = "1");
   INSERT INTO tpl2 VALUES
     (1, PARSE_TO_VARIANT('{"ts": "2024-01-01 10:00:00.123456", "d": 
"2024-01-01", "i": "5"}')),
     (2, PARSE_TO_VARIANT('{"ts": 5, "d": 5, "i": "x"}'));
   SELECT k, CAST(v AS STRING) FROM tpl2 ORDER BY k;
   ```
   
   Before, row 1 reads `{"i":5}`: the BE converted path `d` from JSONB to 
DateV2 and dropped 2 of 2 values. Written alone, the same row keeps `d` and 
`ts`. After, row 1 reads `{"d":"2024-01-01","i":5,"ts":"2024-01-01 
10:00:00.123000"}`, exactly as when it is written alone. `ARRAY<DATE>` paths 
had the same problem per element.
   
   The fix, one commit per step:
   
   1. When the inferred type holds JSONB, convert through Variant instead. 
Variant CAST groups values by kind and converts each group as a column of that 
kind, which is what a segment holding only that kind does, and what a query 
does when it CASTs a Variant value.
   2. A segment holding only a kind CAST cannot convert at all (a boolean on a 
DATE path, an array on an INT path, a number on an ARRAY path) used to fail the 
whole load with e.g. `[INVALID_ARGUMENT]CAST AS DateV2 not supported BOOL`, 
while the same value next to another kind was silently dropped. Such a 
conversion now retries through Variant, so the value is dropped either way.
   3. Text targets (STRING, or ARRAY of it) stay on the JSONB CAST: Variant 
CAST renders a null array element as the text "null", so `["a", null]` would 
have been stored as `["a", "null"]`.
   4. `create_ip_wrapper` admitted every number and IP type and 
`create_timestamptz_wrapper` every base type as a source, but only some have a 
`CastToImpl`; the rest failed at execution with `[RUNTIME_ERROR]not support 
NonStrictMode: from BIGINT cast to IPv4` instead of being rejected as 
unsupported. That error leaked out of the non-strict Variant CAST 
(`CAST(PARSE_TO_VARIANT('13') AS IPV4)` failed) and failed loads of numbers 
into IPV4/IPV6/TIMESTAMPTZ typed paths. Both wrappers now admit only the 
sources they implement. The FE already rejects the others (`cannot cast INT to 
IPV4`, also for TRY_CAST), so SQL CAST does not change.
   5. Test only: the regression suite covers IPV6 rather than a TIMESTAMPTZ 
template path, since #67675 rejects TIMESTAMPTZ in templates.
   
   Not changed here (existing behavior, left for follow-ups): reading an 
`ARRAY<STRING>` typed path returns null elements as "null" 
(`CAST(PARSE_TO_VARIANT('["a", null]') AS ARRAY<STRING>)` gives `["a", 
"null"]`); a STRING typed path stores a JSON `true` as "true" when kinds mix in 
its segment but as "1" otherwise; Variant CAST from a DATE value to BOOLEAN 
still fails with RUNTIME_ERROR.
   
   ### Release note
   
   Fix Variant schema template (typed) paths losing valid values, such as date 
strings on a DATE or DATETIME path or IP strings on an IPV4 path, when the same 
load also wrote another kind of value (such as a number) on that path. A value 
that cannot be converted to a typed path's declared type is now dropped instead 
of failing the load, and CAST of a Variant number or boolean to IPV4, IPV6 or 
TIMESTAMPTZ returns NULL instead of an error.
   
   ### Check List (For Author)
   
   - Test <!-- At least one of them must be included. -->
       - [x] Regression test
       - [x] Unit Test
       - [ ] Manual test (add detailed scripts or steps below)
       - [ ] No need to test or manual test. Explain why:
           - [ ] This is a refactor/code format and no logic has been changed.
           - [ ] Previous test can cover this change.
           - [ ] No code files have been changed.
           - [ ] Other reason <!-- Add your reason?  -->
   
       New BE UTs (each fails on the code before its commit): 
`VariantPathBuilderTest.TypedConversionDoesNotDependOnOtherValueKindsInBatch`, 
`TypedArrayConversionDoesNotDependOnOtherElementKindsInBatch`, 
`TypedStringArrayConversionKeepsNullElements`, 
`TypedConversionDropsKindsCastCannotConvert`, 
`VariantColumnWriterReaderTest.v2_typed_date_paths_keep_strings_batched_with_numbers`,
 `CastVariantV2FromTest.KindsWithoutIpOrTimestampTzConversionBecomeNull`. New 
regression suite `variant_p0/predefine/test_predefine_mixed_value_kinds`. Also 
run locally: every BE UT matching `*Variant*:*variant*:*Cast*:*cast*` (978 
passed, 6 skipped), all of `variant_p0` (173 of 174; the other needs S3 
credentials), `datatype_p0/{timestamptz,ip,timestamp_ns}` and 
`function_p0/cast` (201 suites), and the typed-path and IP suites in 
`fault_injection_p0`, `inverted_index_p0`, `ddl_p0`, `load_p0`, `query_p0`, 
`nereids_function_p0` and `jsonb_p0`.
   
   - Behavior changed:
       - [ ] No.
       - [x] Yes. <!-- Explain the behavior change -->
   
       Typed paths keep the valid values they used to drop. A value whose kind 
cannot be converted to the declared type is dropped instead of failing the 
load. CAST from VARIANT to IPV4, IPV6 or TIMESTAMPTZ returns NULL for numbers 
and booleans instead of an error.
   
   - Does this need documentation?
       - [ ] No.
       - [x] Yes. <!-- Add document PR link here. eg: 
https://github.com/apache/doris-website/pull/1214 -->
   
       apache/doris-website#4170 (draft) documents the old behavior of these 
cases and should follow this PR.
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label <!-- Add branch pick label that this PR should 
merge into -->
   
   🤖 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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to