weiqingy commented on code in PR #2424:
URL: https://github.com/apache/auron/pull/2424#discussion_r3653804516
##########
native-engine/datafusion-ext-plans/src/common/row_null_checker.rs:
##########
@@ -52,15 +60,32 @@ impl RowNullChecker {
/// - `false` bits indicate rows that contain at least one null value
/// - `true` bits indicate rows where all fields are non-null
pub fn has_nulls(&self, rows: &Rows) -> NullBuffer {
- // Create NullBuffer from the collected bits
- NullBuffer::from_iter((0..rows.num_rows()).map(|row_index| {
- let row_data = rows.row(row_index);
- // Check if this row has any null values
- let has_null = self.has_null(row_data.as_ref());
- // NullBuffer uses true for valid (non-null) and false for null
- // So we need to invert the result since has_null returns true for
"has nulls"
- !has_null
- }))
+ let parser = self.row_converter.parser();
+ let parsed_rows = rows
+ .iter()
+ .map(|row| parser.parse(row.data()))
+ .collect::<Vec<_>>();
+ let key_columns = self
+ .row_converter
+ .convert_rows(parsed_rows)
+ .expect("failed to decode row data in RowNullChecker");
Review Comment:
`parse()` re-wraps the bytes with *this* converter's `fields`, which is how
they get past `assert!(Arc::ptr_eq(...), "rows were not produced by this
RowConverter")` in `convert_rows` (`arrow-row/src/lib.rs:701-704`). That assert
is what licenses the `unsafe { convert_raw(...) }` just below it (`:710-713`).
One consequence worth knowing before picking a fix for the panic already
raised here: `?` would not fully close it. `RowParser` forces `validate_utf8:
true` (`:844`), and that path runs `GenericStringArray::from(decoded)`
(`arrow-row/src/variable.rs:325`), whose `From` impl is
`try_from_binary(v).unwrap()` (`arrow-array/src/array/string_array.rs:74`). So
bad bytes panic inside Arrow before `convert_rows` can return `Err`.
Would reaching the producer's converter through the stream be an option?
`common/key_rows_output.rs:41-44` exposes only `schema()` and `keys()` today.
With the converter available, `convert_rows(rows.iter())` would work directly,
no `parse()` round-trip, and the assert would be doing real work.
##########
native-engine/datafusion-ext-plans/src/common/row_null_checker.rs:
##########
@@ -52,15 +60,32 @@ impl RowNullChecker {
/// - `false` bits indicate rows that contain at least one null value
/// - `true` bits indicate rows where all fields are non-null
pub fn has_nulls(&self, rows: &Rows) -> NullBuffer {
- // Create NullBuffer from the collected bits
- NullBuffer::from_iter((0..rows.num_rows()).map(|row_index| {
- let row_data = rows.row(row_index);
- // Check if this row has any null values
- let has_null = self.has_null(row_data.as_ref());
- // NullBuffer uses true for valid (non-null) and false for null
- // So we need to invert the result since has_null returns true for
"has nulls"
- !has_null
- }))
+ let parser = self.row_converter.parser();
+ let parsed_rows = rows
+ .iter()
+ .map(|row| parser.parse(row.data()))
+ .collect::<Vec<_>>();
+ let key_columns = self
+ .row_converter
+ .convert_rows(parsed_rows)
+ .expect("failed to decode row data in RowNullChecker");
+
+ NullBuffer::from_iter(
+ (0..rows.num_rows())
+ .map(|row_index| key_columns.iter().all(|column|
!column.is_null(row_index))),
Review Comment:
`Array::is_null` only reads the null buffer
(`arrow-array/src/array/mod.rs:250-252`), and `NullArray::nulls()` returns
`None` (`.../null_array.rs:112-114`). So for a `DataType::Null` key, which is
what Arrow decodes that field to (`arrow-row/src/lib.rs:1548`), `is_null` is
always `false`.
On master such a key is always null (`row_null_checker.rs:240`,
`DataTypeInfo::Null => true`), so after this change the null-skip arms at
`sort_merge_join_exec.rs:382-383` stop firing and rows that never matched
before would take part in the join. Spark's `NullType` does map to Arrow `Null`
(`ArrowUtils.scala:45`), though I could not get Spark to actually plan an SMJ
with one in `on`, so I cannot say it is reachable today.
Would `logical_nulls()` be the safer read here? Roughly this, in case it
helps:
```rust
let logical_nulls: Vec<_> = key_columns.iter().map(|c|
c.logical_nulls()).collect();
NullBuffer::from_iter((0..rows.num_rows()).map(|i| {
logical_nulls.iter().all(|n| n.as_ref().is_none_or(|n| n.is_valid(i)))
}))
```
--
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]