MCPcopy Create free account
hub / github.com/apache/datafusion / compare_op_for_nested

Function compare_op_for_nested

datafusion/physical-expr-common/src/datum.rs:135–207  ·  view source on GitHub ↗

Compare on nested type List, Struct, and so on

(
    op: Operator,
    lhs: &dyn Datum,
    rhs: &dyn Datum,
)

Source from the content-addressed store, hash-verified

133
134/// Compare on nested type List, Struct, and so on
135pub fn compare_op_for_nested(
136 op: Operator,
137 lhs: &dyn Datum,
138 rhs: &dyn Datum,
139) -> Result<BooleanArray> {
140 let (l, is_l_scalar) = lhs.get();
141 let (r, is_r_scalar) = rhs.get();
142 let l_len = l.len();
143 let r_len = r.len();
144
145 assert_or_internal_err!(l_len == r_len || is_l_scalar || is_r_scalar, "len mismatch");
146
147 let len = match is_l_scalar {
148 true => r_len,
149 false => l_len,
150 };
151
152 // fast path, if compare with one null and operator is not 'distinct', then we can return null array directly
153 if !matches!(op, Operator::IsDistinctFrom | Operator::IsNotDistinctFrom)
154 && (is_l_scalar && l.null_count() == 1 || is_r_scalar && r.null_count() == 1)
155 {
156 return Ok(BooleanArray::new_null(len));
157 }
158
159 // TODO: make SortOptions configurable
160 // we choose the default behaviour from arrow-rs which has null-first that follow spark's behaviour
161 let cmp = make_comparator(l, r, SortOptions::default())?;
162
163 let cmp_with_op = |i, j| match op {
164 Operator::Eq | Operator::IsNotDistinctFrom => cmp(i, j).is_eq(),
165 Operator::Lt => cmp(i, j).is_lt(),
166 Operator::Gt => cmp(i, j).is_gt(),
167 Operator::LtEq => !cmp(i, j).is_gt(),
168 Operator::GtEq => !cmp(i, j).is_lt(),
169 Operator::NotEq | Operator::IsDistinctFrom => !cmp(i, j).is_eq(),
170 _ => unreachable!("unexpected operator found"),
171 };
172
173 let values = match (is_l_scalar, is_r_scalar) {
174 (false, false) => BooleanBuffer::collect_bool(len, |i| cmp_with_op(i, i)),
175 (true, false) => BooleanBuffer::collect_bool(len, |i| cmp_with_op(0, i)),
176 (false, true) => BooleanBuffer::collect_bool(len, |i| cmp_with_op(i, 0)),
177 (true, true) => std::iter::once(cmp_with_op(0, 0)).collect(),
178 };
179
180 // Distinct understand how to compare with NULL
181 // i.e NULL is distinct from NULL -> false
182 if matches!(op, Operator::IsDistinctFrom | Operator::IsNotDistinctFrom) {
183 Ok(BooleanArray::new(values, None))
184 } else {
185 // If one of the side is NULL, we return NULL
186 // i.e. NULL eq NULL -> NULL
187 // For nested comparisons, we need to ensure the null buffer matches the result length
188 let nulls = match (is_l_scalar, is_r_scalar) {
189 (false, false) | (true, true) => NullBuffer::union(l.nulls(), r.nulls()),
190 (true, false) => {
191 // When left is null-scalar and right is array, expand left nulls to match result length
192 match l.nulls().filter(|nulls| nulls.is_null(0)) {

Callers 4

eq_dyn_nullFunction · 0.85
criterion_benchmarkFunction · 0.85
apply_cmp_for_nestedFunction · 0.85
compare_with_eqFunction · 0.85

Calls 12

newFunction · 0.85
null_countMethod · 0.80
is_gtMethod · 0.80
collectMethod · 0.80
unionFunction · 0.50
getMethod · 0.45
lenMethod · 0.45
is_eqMethod · 0.45
nullsMethod · 0.45
filterMethod · 0.45
is_nullMethod · 0.45
clonedMethod · 0.45

Tested by

no test coverage detected

Used in the wild real call sites across dependent graphs

searching dependent graphs…