andygrove opened a new issue, #6630:
URL: https://github.com/apache/datafusion-comet/issues/6630
Part of #6615.
## What / Why
This issue moves the generator coverage in `exec/CometGenerateExecSuite` to
Comet SQL tests: `explode`, `explode_outer`, `posexplode`, `posexplode_outer`,
over arrays and struct arrays, across batch boundaries, and under `LIMIT ...
OFFSET`. Fixtures go under `sql-tests/expressions/array/`, next to
`explode.sql` and `posexplode.sql`. Static analysis on `main` at 3bc2faa934
found the following:
| Suite | Covered | Convert | Blocked | Split | Keep | Total |
|---|---|---|---|---|---|---|
| `CometGenerateExecSuite` | 1 | 40 | 0 | 0 | 2 | 43 |
Nearly everything converts. The plan-shape test and the ScalaUDF test stay.
## Work
Line numbers refer to `CometGenerateExecSuite`.
- **`explode_local_table_scan.sql` (new)**, covering the 24 `Seq.toDF` tests
from L114 to L436 except L214, L227 and L393
- Use `-- Config: spark.comet.exec.localTableScan.enabled=true` with
inline `VALUES`.
- The behavior is already in `explode.sql` / `posexplode.sql` over
Parquet. What this file adds is the source: Parquet always reads arrays back
with `containsNull=true`, while `Seq.toDF` gives non-null elements, which is
the #4789 hazard.
- `ConvertToLocalRelation` folds only Project, Filter and Limit, so a
Generate over `VALUES` still reaches the native explode.
- Write NULL arrays as bare `NULL`, because `CAST(NULL AS ARRAY<INT>)`
makes the elements nullable again.
- Seven of these behaviors are in no fixture today: L180, L191, L202,
L240, L297, L381 and L436.
- **`explode_disabled.sql` (new, L214):** set
`spark.comet.exec.explode.enabled=false` and assert the fallback reason.
- **`explode.sql` (L227):** add the `explode(m)` fallback.
- Also move the `rand()` query out into its own file. The whole file is
gated `MinSparkVersion: 3.5` for that one query, so today it is skipped
entirely on Spark 3.4.
- **`posexplode.sql` (L100):** add `posexplode(split(s, 'x'))`.
- **`explode_small_batches.sql` (new):** L447, L465, L488, L613, L629, L646,
using `-- ConfigMatrix: spark.comet.batchSize=4,8` and single-file tables.
- **`explode_limit_offset.sql` (new):** L523, L539, L561, L580, using
single-file tables with `LIMIT 4 OFFSET 1` (#5224).
- **`explode_boolean_struct.sql` (new):** L691 (four instances) and L710,
using a 3000-row table generated with `hash()` (#6464).
- **`explode_boolean_struct_batch_100.sql` (new):** L700, with
`spark.comet.batchSize=100`.
Use `INSERT INTO t SELECT /*+ COALESCE(1) */ ...` or `range(0, n, 1, 1)` for
every batch-boundary fixture. `INSERT ... VALUES` writes one file per row and
would never put two rows in one batch.
## Stays in Scala
- **L38 (8 instances)** pins `CometExplodeExec` canonicalization (outer and
non-outer must differ, identical generators must match) and a
`ReusedExchangeExec` over the generator with AQE on. That is plan shape.
- **L717** feeds an exploded boolean into a Scala UDF (#6464), and UDF
registration has no SQL form. The rest of that shape is covered by L710.
## Notes on the Scala tests
- **The non-outer NULL/empty tests never reach the native operator with
those rows** (L125, L136, L263, L323, L334, L406). Spark's
`InferFiltersFromGenerate` adds a non-NULL, non-empty filter, and
`ConvertToLocalRelation` applies it to the `Seq` data at plan time. The
fixtures keep the queries, but the coverage that matters is in the `_outer`
variants.
- **L447** never has two input batches in one partition.
- **Bare `-0.0`.** `explode.sql:142,156` and `posexplode.sql:131` write
`cast(-0.0 as float/double)`, which is `+0.0`. Use `float('-0.0')` /
`double('-0.0')`.
## 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 |
|---|---|---|---|---|
| `CometGenerateExecSuite` | 38 | generator identity preserves exchange
reuse: $generator($input), AQE=$adaptive [8 instances: explode/posexplode x
s.arr/slice(s.arr, 1, 10) x AQE false/true] | keep | |
| `CometGenerateExecSuite` | 100 | posexplode with a computed array from
Parquet | convert | array/posexplode.sql |
| `CometGenerateExecSuite` | 114 | explode with simple array | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 125 | explode with empty array | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 136 | explode with null array | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 147 | explode_outer with simple array | convert
| array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 158 | explode_outer with empty array | convert
| array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 169 | explode_outer with null array | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 180 | explode with multiple columns | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 191 | explode with array of strings | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 202 | explode with filter | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 214 | explode fallback when disabled | convert
| array/explode_disabled.sql (new) |
| `CometGenerateExecSuite` | 227 | explode with map input falls back |
convert | array/explode.sql |
| `CometGenerateExecSuite` | 240 | explode with nullable projected column |
convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 251 | explode_outer with nullable projected
column | convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 263 | explode with mixed null, empty, and
non-empty arrays | convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 280 | explode_outer with mixed null, empty, and
non-empty arrays | convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 297 | explode with multiple nullable columns |
convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 312 | posexplode with simple array | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 323 | posexplode with empty array | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 334 | posexplode with null array | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 345 | posexplode_outer with simple array |
convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 356 | posexplode with array of strings |
convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 367 | posexplode with nullable elements |
convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 381 | posexplode with multiple projected
columns | convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 393 | posexplode with map input falls back |
covered | array/posexplode.sql |
| `CometGenerateExecSuite` | 406 | posexplode with array of structs |
convert | array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 421 | posexplode in lateral view | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 436 | posexplode of literal array | convert |
array/explode_local_table_scan.sql (new) |
| `CometGenerateExecSuite` | 447 | posexplode across batch boundary with
small batch size | convert | array/explode_small_batches.sql (new) |
| `CometGenerateExecSuite` | 465 | explode_outer across batch boundary with
mixed empty/null rows | convert | array/explode_small_batches.sql (new) |
| `CometGenerateExecSuite` | 488 | posexplode_outer across batch boundary
with mixed empty/null rows | convert | array/explode_small_batches.sql (new) |
| `CometGenerateExecSuite` | 523 | posexplode over limit with offset |
convert | array/explode_limit_offset.sql (new) |
| `CometGenerateExecSuite` | 539 | posexplode_outer over limit with offset |
convert | array/explode_limit_offset.sql (new) |
| `CometGenerateExecSuite` | 561 | explode over limit with offset | convert
| array/explode_limit_offset.sql (new) |
| `CometGenerateExecSuite` | 580 | explode_outer over limit with offset |
convert | array/explode_limit_offset.sql (new) |
| `CometGenerateExecSuite` | 613 | posexplode single row exceeds batch size
| convert | array/explode_small_batches.sql (new) |
| `CometGenerateExecSuite` | 629 | posexplode_outer single row exceeds batch
size | convert | array/explode_small_batches.sql (new) |
| `CometGenerateExecSuite` | 646 | explode single row exceeds batch size |
convert | array/explode_small_batches.sql (new) |
| `CometGenerateExecSuite` | 691 | $generator of structs keeps boolean
fields past the first output batch [instances: explode, explode_outer,
posexplode, posexplode_outer] | convert | array/explode_boolean_struct.sql
(new) |
| `CometGenerateExecSuite` | 700 | explode of structs keeps boolean fields
when one row exceeds the batch size | convert |
array/explode_boolean_struct_batch_100.sql (new) |
| `CometGenerateExecSuite` | 710 | named_struct over an exploded boolean
keeps its values | convert | array/explode_boolean_struct.sql (new) |
| `CometGenerateExecSuite` | 717 | boolean ScalaUDF over an exploded boolean
keeps its values | 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]