andygrove opened a new issue, #6637:
URL: https://github.com/apache/datafusion-comet/issues/6637

   Part of #6615.
   
   ## What / Why
   
   `CometCodegenSuite` tests the JVM codegen dispatcher, which compiles Spark's 
own `doGenCode` into an Arrow batch kernel. Most of it is dispatcher internals 
and stays in Scala: ScalaUDF registration, kernel caching, Nondeterministic 
state, `TaskContext`, memory lifecycle and explain text. But about a third of 
the suite tests how a built-in Spark expression routes through the dispatcher, 
or what it returns. `expect_dispatch` / `expect_native` / `expect_fallback` can 
express those now. Static analysis on `main` at 3bc2faa934 found the following:
   
   | Suite | Covered | Convert | Blocked | Split | Keep | Total |
   |---|---|---|---|---|---|---|
   | `CometCodegenSuite` | 1 | 23 | 2 | 2 | 61 | 89 |
   
   **A built-in parent that always dispatches can stand in for an identity 
ScalaUDF.** `CometScalaUDF.emitJvmCodegenDispatch` binds the whole subtree 
under the dispatched root: only column references become data arguments, and 
every inner node is reported as dispatched. Several serdes always dispatch:
   
   - `map(...)`, `map_concat`
   - `transform`, `transform_values`, `map_filter`, `zip_with`, `aggregate`
   - `hypot`, `nanvl`, `pmod`, `add_months`, `Invoke`
   
   So `map('k', array_max(flatten(a)))` puts `array_max` and `flatten` in one 
kernel over the nested input, the same as `idBin(array_max(flatten(a)))`, and 
`expect_dispatch(array_max, flatten)` proves it. The same trick lets the 
`CometCodegenHOFSuite` tests in the array issue convert fully rather than split.
   
   ## Work
   
   Line numbers refer to `CometCodegenSuite`.
   
   **JSON routing**
   
   - New: `json/routing_json_{disabled,enabled,opt_in,opt_in_enabled}.sql`, one 
file per allowIncompatible × codegen cell.
     - Covers L80 (`json_array_length`) and the two L151 instances 
(`from_json`, `to_json`). Issue #4616's routing-matrix retrofit never reached 
JSON.
     - Use the Scala inputs: `'[1,2,3]'`, `'[]'`, `'not an array'`, NULL, and 
the struct/array JSON rows.
     - For `from_json`, use `expect_fallback(from_json: 
spark.comet.exec.scalaUDF.codegen.enabled=false)`, `expect_dispatch(from_json)` 
or `expect_native(from_json)` per cell.
     - **On Spark 4.x, `json_array_length` and `to_json` can't be named in 
`expect_native` / `expect_dispatch`.** Both lower to `StaticInvoke` / `Invoke` 
there. `Spark4xCometExprShim` rebuilds the original expression but copies back 
only the fallback reasons, so the coverage tags are lost.
       - Use `expect_fallback` where a cell falls back, which works on 4.x, and 
a plain `query` elsewhere.
       - A fully Comet plan with the dispatcher off proves the native path. A 
dispatcher-off fallback with `allowIncompatible=false` proves there is no 
native path, and then a fully Comet plan with the dispatcher on proves dispatch.
       - Only "opt-in on, dispatcher on" for natively supported shapes stays 
unpinned on 4.x. A `MaxSparkVersion: 3.5` copy could pin it on 3.x.
   - `struct/json_to_structs.sql`: change L24 and L28 from `spark_answer_only` 
to `expect_dispatch(from_json)`.
   
   **Arrays**
   
   - `array/sequence.sql`
     - L482: `expect_native(sequence)` on the explicit-step and default-step 
column queries.
     - L530: a `d DATE` table with `sequence(d, DATE'2024-06-01', INTERVAL 1 
MONTH)` as `expect_dispatch(sequence)`.
     - Upgrade L182-189 to `expect_dispatch(sequence)`.
   - `array/routing_arrays_{enabled,disabled,opt_in}.sql` (L514): `sequence(x, 
y, CASE WHEN x <= y THEN 2 ELSE -2 END)`.
   - `array/transform.sql` (L1815, L1832): `transform(sequence(0, 15), i -> i * 
10)` and a `named_struct` variant, both `expect_dispatch(transform)` with no 
FROM, so 16 elements land in a one-row batch (#4539).
   - `array/dispatch_nested_input.sql` (new, `spark.comet.batchSize=16`): 
L1863, L1884, L1934, L1947, L1960, L1976.
     - `map('k', array_distinct(s))` over `ARRAY<STRUCT<a INT, b STRING>>`.
     - `map('k', array_max(flatten(a)))` over BINARY, STRING, DECIMAL(10,2) and 
DECIMAL(30,2) nested arrays with inner NULLs, with `expect_dispatch(array_max, 
flatten)`.
     - L1884's 100 deterministic rows are generated in SQL as one file.
   - `array/dispatch_collection_output_sizes.sql` (new, 
`spark.comet.batchSize=8`): L2324, 10 instances.
     - A 256-row single-file seed table, with one query per output type built 
by always-dispatch HOFs.
   
   **Strings, math, datetime, decimal, misc**
   
   - `string/upper.sql` (L2023): `expect_dispatch(upper, substring)` on 
`upper(substring(s, 1, 2))`.
   - `string/upper_ansi.sql` (new, L2033): `expect_error(DIVIDE_BY_ZERO)` on 
`IF(flag, upper(substring('abc', CAST(1L DIV 0L AS INT), n)), NULL)`, plus a 
sentinel of the same shape (#5608).
   - `string/lpad_binary.sql` + `_dispatch_disabled.sql` (new, L2357): `lpad(b, 
8, unhex('FF'))` over binary.
     - Dispatcher on: `expect_dispatch(staticinvoke)`.
     - Dispatcher off: `expect_fallback(...)` (#5575).
   - `string/replace_dispatch_boundary.sql` + `_ansi.sql` (new, L701)
     - Eight `replace` shapes as `expect_dispatch(replace)`.
     - The ANSI null short-circuit shapes go in the ANSI file.
   - `string/is_valid_utf8.sql` (new, `MinSparkVersion: 4.0`, L2379): 
`expect_dispatch(invoke)` on `is_valid_utf8(s)` and `make_valid_utf8(s)`, 
including a malformed value.
   - `math/pmod.sql` (L2066)
     - Add the (NULL, NULL) row, and make L26-27 `expect_dispatch(pmod)`.
     - Add `map('k', a + b)` as `expect_dispatch(add)`.
   - `math/pmod_ansi.sql` (new, L2087)
     - A (NULL, 0) sentinel row with `expect_dispatch(pmod)`.
     - (7, 0) as `expect_error(BY_ZERO)`, or use a 4.0 / 4.1 file pair to keep 
DIVIDE_BY_ZERO versus REMAINDER_BY_ZERO exact.
   - `math/hypot.sql` (L602): `expect_dispatch(hypot, cast, checkoverflow, 
add)` on `hypot(CAST(amount + amount AS DOUBLE), 4.0D)` over a DECIMAL(10,2) 
column.
   - `math/hypot_dispatch_disabled.sql` (new, L796): `expect_fallback(hypot: 
spark.comet.exec.scalaUDF.codegen.enabled=false)`.
   - `datetime/add_months_ansi.sql` (new, L1992, #5218): 
`expect_error(CAST_INVALID_INPUT)` on `add_months(CAST(s AS DATE), i)`, plus an 
`expect_dispatch(add_months, cast)` sentinel over valid rows.
   - `decimal/decimal_ops.sql` (L576): `expect_native(checkoverflow, add)` on 
`a + b`.
   - `misc/dispatch_sliced_boolean.sql` (new, L1066)
     - Set `spark.sql.shuffle.partitions=1` and `spark.comet.batchSize=100`.
     - `expect_dispatch(regexp_replace)` on `regexp_replace(IF(b, s, 'zz'), 
'1', 'y')` over a grouped aggregate.
   
   The conversions above assume the kernel accepts output types such as 
`Map<String, Binary>` and `Map<String, Array<Struct>>`. The UDF tests' map and 
struct outputs make that likely, but it hasn't been verified, so run the new 
fixtures before deleting the Scala tests.
   
   **Blocked or partly kept**
   
   - **L80 and the `to_json` instance of L151:** blocked on the Spark 4.x 
coverage-tag loss described above. Pinning the mechanism on 4.x needs the shim 
to copy coverage tags back.
   - **L701:** the malformed `CAST(X'FF' AS STRING)` and 256 KiB `repeat(...)` 
literal cases need ConstantFolding (#6618), or the 262,144-character literal 
spelled out.
   - **L2379:** the 3.4/3.5 half (no SQL function reaches `CometInvoke` there) 
and the Invoke-with-arguments case on a folded literal target stay in Scala.
   
   ## Stays in Scala
   
   - **ScalaUDF registration and type surface:** L978, L987, L1086-L1155, L1296 
(10 instances), L1307-L1416, L1545-L1771.
   - **UDF-specific semantics:** L302, L498, L1030, L1075, L1440, L1461.
   - **Kernel cache and Nondeterministic state:** L818, L847, L864, L895, L921, 
L947, L1487, L1505.
   - **Dispatcher counters:** L317, L333.
   - **Direct kernel and serde tests:** L240, L2127 (TIME input, which has no 
SQL route), L2336, L2396.
   - **Memory lifecycle:** L270.
   - **Explain, COMET-INFO and coverage-stat text:** L357, L544, L626, L684.
   - **Test-helper self-tests:** L401, L430.
   - **Dataset API and DSv2 catalog functions:** L1040, L2441.
   
   **Already covered:** L1802, by `map/map_concat.sql` L46-47.
   
   ## Findings
   
   - **The #5218 short-circuit tests L2066 and L2087 never generate the 
short-circuit.** They wrap `pmod` / `a + b` in `idInt(...)`, and a ScalaUDF 
root is not `NullIntolerant`. A bare `pmod(a, b)` does take the path, so the 
fixture versions are stronger.
   - **L302's comment describes the wrong mechanism,** and its claim that 
`Concat(null, x) = x` is wrong for Spark.
   - **L847** never runs more than one batch per partition.
   - **L864's premise doesn't hold:** the table read forces columns nullable.
   - **Explain on Spark 4.x misreports dispatched `json_array_length` and 
`to_json`.** It likely does the same for `parse_url`, which goes through the 
same shim code. A dispatched call shows up as a native `staticinvoke` / 
`invoke`, and the COMET-INFO dispatcher line omits it. That is a product issue, 
not just a fixture gap. It was found by reading the code; worth confirming and 
filing separately.
   - **Fixture headers claim a mechanism nothing asserts:** `sequence.sql`, 
`json_array_length_default.sql`, `hypot.sql`, `nanvl.sql`, `pmod.sql`, 
`map_concat.sql`, `create_map.sql`, `transform.sql`.
   
   ## Done when
   
   - Every test in the table below is accounted for. Either a fixture covers it 
(the same inputs, configs and assertion strength, or stronger), the fixture 
that already covers it is cited, or it stays in Scala for the reason given 
above.
   - Each converted Scala test is deleted in the PR that adds its coverage. A 
suite left empty is deleted and removed from the suite lists in 
`.github/workflows/pr_build_linux.yml` and `pr_build_macos.yml`.
   - The new and changed fixtures pass on every Spark profile. Apply 
`run-all-spark-profiles`, because pull requests run only the default profile.
   
   <details><summary>Every Scala test in scope, with its verdict and target 
fixture</summary>
   
   | Suite | Line | Test | Verdict | Target |
   |---|---|---|---|---|
   | `CometCodegenSuite` | 80 | json_array_length routing follows native opt-in 
and dispatcher settings | blocked: other | json/routing_json_disabled.sql 
(new); json/routing_json_enabled.sql (new); json/routing_json_opt_in.sql (new); 
json/routing_json_opt_in_enabled.sql (new) |
   | `CometCodegenSuite` | 151 | from_json routing follows native opt-in and 
dispatcher settings (loop at L133 instance 1 of 2; template "$name routing 
follows native opt-in and dispatcher settings") | convert | 
json/routing_json_disabled.sql (new); json/routing_json_enabled.sql (new); 
json/routing_json_opt_in.sql (new); json/routing_json_opt_in_enabled.sql (new); 
struct/json_to_structs.sql |
   | `CometCodegenSuite` | 151 | to_json routing follows native opt-in and 
dispatcher settings (loop at L133 instance 2 of 2; template "$name routing 
follows native opt-in and dispatcher settings") | blocked: other | 
json/routing_json_disabled.sql (new); json/routing_json_enabled.sql (new); 
json/routing_json_opt_in.sql (new); json/routing_json_opt_in_enabled.sql (new); 
struct/structs_to_json.sql |
   | `CometCodegenSuite` | 240 | codegen kernel round-trips 
CalendarIntervalType | keep |  |
   | `CometCodegenSuite` | 270 | a closed allocateOutput vector releases all of 
its memory | keep |  |
   | `CometCodegenSuite` | 302 | ScalaUDF over concat(c1, c2) suppresses the 
null short-circuit | keep |  |
   | `CometCodegenSuite` | 317 | disabled mode bypasses the dispatcher | keep | 
 |
   | `CometCodegenSuite` | 333 | schema exceeding spark.sql.codegen.maxFields 
falls back to Spark | keep |  |
   | `CometCodegenSuite` | 357 | explain.codegen.enabled surfaces routed 
expressions in COMET-INFO | keep |  |
   | `CometCodegenSuite` | 401 | checkSparkAnswerAndImpl pins the mechanism and 
fails when the claim is wrong | keep |  |
   | `CometCodegenSuite` | 430 | an expression nested inside a dispatched 
subtree is classified as dispatched | keep |  |
   | `CometCodegenSuite` | 482 | sequence with leaf integral args runs natively 
| convert | array/sequence.sql |
   | `CometCodegenSuite` | 498 | sequence with zero-arg UDF stop routes through 
the dispatcher | keep |  |
   | `CometCodegenSuite` | 514 | sequence with non-leaf integral args routes 
through the dispatcher | convert | array/routing_arrays_enabled.sql; 
array/routing_arrays_disabled.sql; array/routing_arrays_opt_in.sql |
   | `CometCodegenSuite` | 530 | sequence with date element type routes through 
the dispatcher | convert | array/sequence.sql |
   | `CometCodegenSuite` | 544 | expression coverage stats split native from 
codegen-dispatch expressions | keep |  |
   | `CometCodegenSuite` | 576 | expression coverage stats survive the decimal 
promotion rewrite | convert | decimal/decimal_ops.sql |
   | `CometCodegenSuite` | 602 | codegen dispatch coverage survives the decimal 
promotion rewrite | convert | math/hypot.sql |
   | `CometCodegenSuite` | 626 | tags copied onto the shared TrueLiteral do not 
leak into unrelated plans | keep |  |
   | `CometCodegenSuite` | 684 | expression coverage stats count nothing when 
the plan falls back entirely | keep |  |
   | `CometCodegenSuite` | 701 | replace compatibility boundary cases stay on 
JVM codegen dispatcher | split (rest: #6618) | 
string/replace_dispatch_boundary.sql (new); 
string/replace_dispatch_boundary_ansi.sql (new) |
   | `CometCodegenSuite` | 796 | codegen dispatch fallback reasons name the 
expression | convert | math/hypot_dispatch_disabled.sql (new) |
   | `CometCodegenSuite` | 818 | dispatcher caches the compiled kernel across 
batches of one query | keep |  |
   | `CometCodegenSuite` | 847 | per-partition kernel preserves 
Nondeterministic state across batches | keep |  |
   | `CometCodegenSuite` | 864 | same UDF over nullable and non-nullable 
columns gets distinct kernels with independent state | keep |  |
   | `CometCodegenSuite` | 895 | Nondeterministic state persists across 
nullability flips within a partition | keep |  |
   | `CometCodegenSuite` | 921 | Nondeterministic state persists across two 
ScalaUDFs in one task | keep |  |
   | `CometCodegenSuite` | 947 | per-task cache isolates UDF state across 
sequential task runs in one session | keep |  |
   | `CometCodegenSuite` | 978 | registered string ScalaUDF routes through 
dispatcher | keep |  |
   | `CometCodegenSuite` | 987 | registered Java UDF1 routes through dispatcher 
| keep |  |
   | `CometCodegenSuite` | 1030 | boolean ScalaUDF argument sliced by an 
aggregate keeps its values | keep |  |
   | `CometCodegenSuite` | 1040 | typed Dataset.filter reads sliced booleans 
from a native aggregate | keep |  |
   | `CometCodegenSuite` | 1066 | dispatched regexp_replace reads a sliced 
boolean with its own values | convert | misc/dispatch_sliced_boolean.sql (new) |
   | `CometCodegenSuite` | 1075 | struct ScalaUDF input with a sliced boolean 
child keeps its values | keep |  |
   | `CometCodegenSuite` | 1086 | multi-arg ScalaUDF over string + literal 
routes through dispatcher | keep |  |
   | `CometCodegenSuite` | 1097 | ScalaUDF as a child of a native Spark 
expression | keep |  |
   | `CometCodegenSuite` | 1110 | composed ScalaUDFs outer(inner(s)) fuse into 
one kernel | keep |  |
   | `CometCodegenSuite` | 1126 | ScalaUDFs of different types compose: 
isShort(len(s)) | keep |  |
   | `CometCodegenSuite` | 1139 | three-deep ScalaUDF composition 
lvl3(lvl2(lvl1(s))) | keep |  |
   | `CometCodegenSuite` | 1155 | multi-column ScalaUDF composition 
join(upperU(c1), lowerU(c2)) | keep |  |
   | `CometCodegenSuite` | 1296 | identity ScalaUDF on ${c.label} routes 
through dispatcher [instances: Boolean, Byte, Short, Int, Long, Float, Double, 
Date, Timestamp, TimestampNTZ] | keep |  |
   | `CometCodegenSuite` | 1307 | ScalaUDF returning a different type than its 
input | keep |  |
   | `CometCodegenSuite` | 1319 | ScalaUDF returning BinaryType | keep |  |
   | `CometCodegenSuite` | 1331 | ScalaUDF on BinaryType | keep |  |
   | `CometCodegenSuite` | 1344 | ScalaUDF returning ArrayType(StringType) | 
keep |  |
   | `CometCodegenSuite` | 1361 | ScalaUDF returning ArrayType(IntegerType) | 
keep |  |
   | `CometCodegenSuite` | 1374 | zero-column ScalaUDF produces one row per 
input row | keep |  |
   | `CometCodegenSuite` | 1403 | ScalaUDF over Decimal(18, 9) routes through 
the unscaled-long fast path | keep |  |
   | `CometCodegenSuite` | 1416 | ScalaUDF over Decimal(38, 10) routes through 
the BigDecimal slow path | keep |  |
   | `CometCodegenSuite` | 1440 | ScalaUDF sees TaskContext.partitionId() per 
partition | keep |  |
   | `CometCodegenSuite` | 1461 | ScalaUDF sees TaskContext from fully-native 
parquet plan | keep |  |
   | `CometCodegenSuite` | 1487 | Rand seeded per partition across a 
multi-partition table | keep |  |
   | `CometCodegenSuite` | 1505 | ScalaUDF composed with reused scalar subquery 
across projection and filter | keep |  |
   | `CometCodegenSuite` | 1545 | ScalaUDF taking Seq[String] reads element by 
element | keep |  |
   | `CometCodegenSuite` | 1558 | ScalaUDF taking Seq[String] iterating all 
elements | keep |  |
   | `CometCodegenSuite` | 1571 | ScalaUDF taking Seq[Int] reads primitive 
elements | keep |  |
   | `CometCodegenSuite` | 1582 | ScalaUDF taking Seq[BigDecimal] hits 
short-precision decimal fast path | keep |  |
   | `CometCodegenSuite` | 1605 | ScalaUDF composes with struct-field access 
reading Struct<String, Int>.age | keep |  |
   | `CometCodegenSuite` | 1622 | ScalaUDF taking full Struct<String, Int> 
value (case class arg) | keep |  |
   | `CometCodegenSuite` | 1643 | ScalaUDF returning Struct<String, Int> (case 
class output) | keep |  |
   | `CometCodegenSuite` | 1652 | ScalaUDF taking Map<String, Int> | keep |  |
   | `CometCodegenSuite` | 1663 | ScalaUDF round-trips Map<Int, Int> (primitive 
key and value) | keep |  |
   | `CometCodegenSuite` | 1680 | ScalaUDF returning Map<String, Int> | keep |  
|
   | `CometCodegenSuite` | 1693 | ScalaUDF taking Map<String, Seq<Int>> 
exercises nested composition | keep |  |
   | `CometCodegenSuite` | 1710 | ScalaUDF round-trips Array<Array<Int>> 
(nested array input + output) | keep |  |
   | `CometCodegenSuite` | 1729 | ScalaUDF round-trips Struct<name, items: 
Array<Int>> | keep |  |
   | `CometCodegenSuite` | 1749 | ScalaUDF round-trips Map<String, Array<Int>> 
(nested value both sides) | keep |  |
   | `CometCodegenSuite` | 1771 | ScalaUDF round-trips Map<String, Struct<x: 
Int, y: String>> | keep |  |
   | `CometCodegenSuite` | 1802 | constant-folded map_concat output round-trips 
every key through the kernel (#4539) | covered | map/map_concat.sql |
   | `CometCodegenSuite` | 1815 | constant-folded array output writes every 
element past the pre-sized child (#4539) | convert | array/transform.sql |
   | `CometCodegenSuite` | 1832 | constant-folded Array<Struct<Int, String>> 
writes struct fields past the pre-sized child (#4539) | convert | 
array/transform.sql |
   | `CometCodegenSuite` | 1863 | array_distinct on Array<Struct<Int, String>> 
retains element identity across hash set | convert | 
array/dispatch_nested_input.sql (new) |
   | `CometCodegenSuite` | 1884 | array_max(flatten(arr)) on 
Array<Array<Binary>> with mixed null inner arrays | convert | 
array/dispatch_nested_input.sql (new) |
   | `CometCodegenSuite` | 1934 | array_max(flatten(arr)) on 
Array<Array<Binary>> with null inner Binary returns null | convert | 
array/dispatch_nested_input.sql (new) |
   | `CometCodegenSuite` | 1947 | array_max(flatten(arr)) on 
Array<Array<String>> with null inner String returns null | convert | 
array/dispatch_nested_input.sql (new) |
   | `CometCodegenSuite` | 1960 | array_max(flatten(arr)) on 
Array<Array<DECIMAL(10,2)>> with null inner Decimal (short-precision fast path) 
| convert | array/dispatch_nested_input.sql (new) |
   | `CometCodegenSuite` | 1976 | array_max(flatten(arr)) on 
Array<Array<DECIMAL(30,2)>> with null inner Decimal (long-precision slow path) 
| convert | array/dispatch_nested_input.sql (new) |
   | `CometCodegenSuite` | 1992 | multi-input NullIntolerant tree does not 
swallow an ANSI error (#5218) | convert | datetime/add_months_ansi.sql (new) |
   | `CometCodegenSuite` | 2023 | single-input NullIntolerant tree still 
short-circuits nulls | convert | string/upper.sql |
   | `CometCodegenSuite` | 2033 | single-input short-circuit does not swallow 
an ANSI error from a foldable subtree (#5608) | convert | string/upper_ansi.sql 
(new) |
   | `CometCodegenSuite` | 2066 | multi-input leaf-only NullIntolerant tree 
short-circuits nulls correctly (#5218) | convert | math/pmod.sql |
   | `CometCodegenSuite` | 2087 | leaf-only short-circuit preserves ANSI 
remainder-by-zero behaviour (#5218) | convert | math/pmod_ansi.sql (new) |
   | `CometCodegenSuite` | 2127 | TIME input column routes through the 
dispatcher (#5218) | keep |  |
   | `CometCodegenSuite` | 2324 | dynamically-sized ${c.label} output 
round-trips through codegen dispatch (#4539) [instances: Array<Int> with null 
elements, Array<Long>, Array<String> with null elements, Array<Decimal>, 
Array<Binary>, Map<Int, Int>, Map<String, Int>, Array<Array<Int>>, Map<Int, 
Array<Int>>, Array<Struct<Int, String>>] | convert | 
array/dispatch_collection_output_sizes.sql (new) |
   | `CometCodegenSuite` | 2336 | dispatch falls back cleanly when the bound 
tree cannot be closure-serialized (#5573) | keep |  |
   | `CometCodegenSuite` | 2357 | unrecognized StaticInvoke routes through the 
dispatcher instead of falling back (#5575) | convert | string/lpad_binary.sql 
(new); string/lpad_binary_dispatch_disabled.sql (new) |
   | `CometCodegenSuite` | 2379 | Invoke routes through the codegen dispatcher 
(#5575) | split (rest: #6618) | string/is_valid_utf8.sql (new) |
   | `CometCodegenSuite` | 2396 | the dispatcher declines calls into code other 
than Spark's (#6425) | keep |  |
   | `CometCodegenSuite` | 2441 | calls to a DataSource V2 function run in 
Spark (#6425) | keep |  |
   
   </details>
   


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