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

   ## Problem
   
   After an outer join, `IS NULL` on a physical `NOT NULL` column (e.g. the 
right-side dimension column of a `LEFT JOIN`) crashes the BE:
   
   ```
   [E-7412] Check failed: is_column_nullable(*dst)
   ```
   
   Core stack:
   ```
   doris::segment_v2::FileColumnIterator::read_by_rowids          
column_reader.cpp
   doris::segment_v2::SegmentIterator::_read_columns_by_rowids    
segment_iterator.cpp
   doris::segment_v2::SegmentIterator::_next_batch_internal       
segment_iterator.cpp:2997
   ```
   
   `doris_main.cpp:518` logs `Failed to initialize JNI` / SIGABRT with the same 
`Check failed` when the query runs.
   
   ## Root cause
   
   Introduced by #62304 (commit `85ede0bafba0`, "[opt](nereids) Optimize I/O 
operations for the IS NULL predicate"). That PR makes `col IS NULL` generate a 
`col.NULL` access path so BE reads only the null map (`NULL_MAP_ONLY` mode), 
saving I/O. The premise is that the column is physically nullable (has a null 
bitmap).
   
   The bug: the rule only checks expression nullability (`arg.nullable()`). A 
`LEFT JOIN` makes the right-side `NOT NULL` column's output slot nullable, so 
`arg.nullable() == true` and a `[col, NULL]` access path is generated even 
though the physical column has no null bitmap. BE then reads the null map by 
rowids on a NOT NULL column and hits the `is_column_nullable(*dst)` check — the 
backend aborts, failing the user's base-table query.
   
   This is unrelated to the IVM branch that first surfaced it: any ordinary 
base-table `LEFT JOIN` query with `enable_prune_nested_column = true` (default) 
reproduces the crash.
   
   ## Fix
   
   In `AccessPathExpressionCollector.visitIsNull`, skip the NULL-only access 
path generation when the `SlotReference`'s physical column is `NOT NULL` (only 
made nullable by the outer join). The join output stays nullable and IS NULL is 
evaluated on the regular data path; no access info is sent to BE for the 
whole-column read (`shouldSkipAccessInfo` drops the single `[col]` path).
   
   Also merged the existing sub-column-path early return into the same guard.
   
   ## Verification
   
   - `PruneNestedColumnTest.testIsNullOnNotNullColumnAfterLeftJoin`: asserts no 
`[segment, NULL]` access path is generated after LEFT JOIN (50 tests, all pass)
   - Regression `left_join_not_null_column.groovy`: LEFT JOIN aggregate query 
returns correct result, BE stays alive; `explain` asserts no `segment.NULL` in 
the plan
   - Before the fix, the same regression crashes the BE with the exact stack 
above (reproduced locally with a 3-row fact table)
   


-- 
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