tlfua commented on code in PR #7450:
URL: https://github.com/apache/datafusion-comet/pull/7450#discussion_r4460975671


##########
native/spark-expr/src/static_invoke/char_varchar_utils/read_side_padding.rs:
##########
@@ -181,6 +181,29 @@ fn spark_read_side_padding2(
                 ))),
             }
         }
+        // The string argument can be a scalar even when it references 
columns, e.g.
+        // `IF(p, (SELECT max(s) FROM t), s)` when every row takes the 
subquery branch.
+        // Expand it to an array so the arms above handle it.
+        [ColumnarValue::Scalar(string), rest @ ..] => {

Review Comment:
   Thanks, done in 719c9b1e7. A `Scalar(Int32(NULL))` length now returns NULL 
for every row, or a NULL scalar when the string is a scalar too, since 
`StringLPad`/`StringRPad` are null-intolerant. This also closes 
https://github.com/apache/datafusion-comet/issues/7452. Unit tests cover 2 and 
3 arguments for `lpad`, `rpad` and `read_side_padding`, and the SQL files add 
`lpad(s, (SELECT max(len) FROM t WHERE len > 100))` with and without a pad.
   



##########
native/spark-expr/src/static_invoke/char_varchar_utils/read_side_padding.rs:
##########
@@ -181,6 +181,29 @@ fn spark_read_side_padding2(
                 ))),
             }
         }
+        // The string argument can be a scalar even when it references 
columns, e.g.
+        // `IF(p, (SELECT max(s) FROM t), s)` when every row takes the 
subquery branch.
+        // Expand it to an array so the arms above handle it.
+        [ColumnarValue::Scalar(string), rest @ ..] => {
+            let num_rows = rest.iter().find_map(|arg| match arg {
+                ColumnarValue::Array(array) => Some(array.len()),
+                ColumnarValue::Scalar(_) => None,
+            });
+            let mut expanded = Vec::with_capacity(args.len());
+            expanded.push(ColumnarValue::Array(
+                string.to_array_of_size(num_rows.unwrap_or(1))?,

Review Comment:
   Agreed, thanks. The scalar is no longer expanded. The padding loop takes the 
strings as an iterator, and the scalar arm passes `std::iter::repeat_n(value, 
n)` (7c4f4ae0e, following @comphead's suggestion below). The output reservation 
no longer counts the scalar per row either. 
`wide_scalar_string_is_not_expanded` pads a 1 MiB scalar over 4096 rows to 
length 1, which panicked with `offset overflow` before.
   
   https://github.com/apache/datafusion-comet/pull/7316 splits batches at the 
planner only for column-free string scalars, so it does not reach a runtime 
scalar from `IF`/`CASE WHEN` like this one, which is why the kernel handles it. 
A dictionary-typed scalar string is now rejected rather than sent to the 
dictionary arm (https://github.com/apache/datafusion-comet/issues/7453).
   



##########
native/spark-expr/src/static_invoke/char_varchar_utils/read_side_padding.rs:
##########
@@ -181,6 +181,29 @@ fn spark_read_side_padding2(
                 ))),
             }
         }
+        // The string argument can be a scalar even when it references 
columns, e.g.
+        // `IF(p, (SELECT max(s) FROM t), s)` when every row takes the 
subquery branch.
+        // Expand it to an array so the arms above handle it.
+        [ColumnarValue::Scalar(string), rest @ ..] => {
+            let num_rows = rest.iter().find_map(|arg| match arg {
+                ColumnarValue::Array(array) => Some(array.len()),
+                ColumnarValue::Scalar(_) => None,
+            });
+            let mut expanded = Vec::with_capacity(args.len());
+            expanded.push(ColumnarValue::Array(
+                string.to_array_of_size(num_rows.unwrap_or(1))?,
+            ));
+            expanded.extend_from_slice(rest);
+            let result = spark_read_side_padding2(&expanded, truncate, 
is_left_pad)?;
+            match num_rows {
+                Some(_) => Ok(result),
+                // Every argument was a scalar, so the result is one too.
+                None => Ok(ColumnarValue::Scalar(ScalarValue::try_from_array(
+                    &result.to_array(1)?,
+                    0,
+                )?)),
+            }
+        }

Review Comment:
   Thanks, done in 7c4f4ae0e. The padding loop now takes the strings as an 
iterator, an `ascii` flag and a capacity floor. The array arms pass 
`string_array.iter()`, `is_ascii()` and `value_data().len()`, so their path is 
unchanged from `main`. The scalar arm passes `std::iter::repeat_n(value, n)`, 
`value.is_none_or(str::is_ascii)` and `0`, so nothing is expanded and the 
output is not sized by the scalar.
   
   One part stays: when every argument is a scalar, the loop runs with `n = 1` 
and builds a 1-row array, and `try_from_array` turns it back into a scalar, 
since the result has to be a scalar there. The `to_array_of_size` half of the 
round trip is gone.
   



##########
spark/src/test/resources/sql-tests/expressions/string/string_lpad.sql:
##########
@@ -59,3 +59,27 @@ SELECT lpad('hi', 5, 'x'), lpad('hello', 3, 'x'), lpad('', 
3, 'a'), lpad(NULL, 5
 
 query expect_dispatch(lpad)
 SELECT lpad('hi', 5, 'xy') FROM test_lpad
+
+-- The string argument references a column but is a scalar for the whole batch 
when
+-- every row takes the subquery branch (issue #7245).
+statement
+CREATE TABLE test_lpad_scalar_str(id int, s string) USING parquet
+
+statement
+INSERT INTO test_lpad_scalar_str VALUES (1, 'ab'), (2, 'cd')

Review Comment:
   Good idea, done in 7c4f4ae0e for both files. The queries now run on 
`test_lpad`/`test_rpad` with `IF(coalesce(len, 0) > -1000, (SELECT max(s) FROM 
t), s)`, so the scalar is `'é'` and reaches the non-ASCII path. With `len` as 
the length they also cover a scalar string with an array length. The `CASE 
WHEN`, 2-argument, literal length, NULL subquery, mixed batch and NULL length 
queries moved over too, and the extra tables are gone.
   



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