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

   Part of #6615.
   
   ## What / Why
   
   This issue moves hash and bitwise coverage to Comet SQL tests under 
`sql-tests/expressions/hash/` and `sql-tests/expressions/bitwise/`. The sources 
are `CometHashExpressionSuite`, `CometBitwiseExpressionSuite`, and the hash and 
bitwise tests in `CometExpressionSuite`. Static analysis on `main` at 
3bc2faa934 found the following:
   
   | Suite | Covered | Convert | Blocked | Split | Keep | Total |
   |---|---|---|---|---|---|---|
   | `CometExpressionSuite` | 3 | 4 | 0 | 0 | 0 | 7 |
   | `CometHashExpressionSuite` | 6 | 35 | 0 | 0 | 1 | 42 |
   | `CometBitwiseExpressionSuite` | 1 | 6 | 1 | 0 | 0 | 8 |
   | **Total** | **10** | **45** | **1** | **0** | **1** | **57** |
   
   `hash.sql` hashes only values built inside the query. The Scala tests hash 
lists, structs and maps read from Parquet, cover every primitive type at its 
boundaries, and include the #5754 required-child and #5778 batched 
array-of-struct regressions. The bitwise fixture runs `bit_get` only as 
`spark_answer_only` and has no tinyint, smallint or boolean `bit_count`.
   
   ## Work
   
   Line numbers refer to `CometHashExpressionSuite` unless marked.
   
   **Hash**
   
   - `hash/hash_types.sql` (new): L38, L46, L54, L62, L70, L103, L111, L120.
     - One column per type: boolean, tinyint, smallint, int, bigint, binary, 
date and timestamp.
     - Each column holds 0, -1, the type's min and max, and NULL 
(true/false/NULL for boolean).
     - Query: `SELECT c, hash(c), xxhash64(c)`.
     - Also add timestamp_ntz. Only the fuzz test hashes that type today.
   - `hash/hash_decimal.sql` (new): L132, L142, L153, L162, L171, plus 
`CometExpressionSuite` L2751 and L2772.
     - Plain queries for decimal(1,0), (10,2), (17,2), (18,0), (18,2) and 
(18,10), each with 1.23, -1.23, 0.0 and NULL.
     - `expect_fallback(precision > 18)` for decimal(19,2), (20,2), (38,10) and 
(38,37), and for `array<decimal(20,2)>`, `struct<a int, b decimal(20,2)>` and 
`map<string, decimal(20,2)>`.
     - The map case needs `spark.sql.legacy.allowHashOnMapType=true`, which is 
file-level, so it may need its own file.
     - `expect_fallback` is stronger than the Scala answer-only check: the hash 
serdes declare precision > 18 Unsupported and have no dispatcher route.
   - `hash/hash_nested.sql` (new, with `Config: 
spark.sql.legacy.allowHashOnMapType=true`): L184-L250, L356-L395, L423-L487, 
L517, L533, L557.
     - Lists, structs and maps read from Parquet, including empty, NULL, 
null-element and null-field rows.
     - Multi-column chains (int, array, string, struct), and an all-NULL 
single-row file.
     - A 1000-element list via `INSERT INTO t SELECT sequence(1, 1000)`.
     - The 9-row oracle for the batched `array<struct>` path (#5778), and the 
nested case that can't be batched.
   - `hash/hash_required_child.sql` (new): L262, L288, L313.
     - Build the table with CTAS, for example `CREATE TABLE t USING parquet AS 
SELECT /*+ COALESCE(1) */ c FROM VALUES (named_struct('a', 1)), (NULL), 
(named_struct('a', 3)), (NULL) AS v(c)`. The inline table's column type stays 
`struct<a: int NOT NULL>`, and CTAS writes that schema as-is.
     - Do not use CREATE + INSERT. On Spark 3.5+, 
`TableOutputResolver.resolveStructType` rebuilds struct columns from 
`GetStructField`, so the child is written optional and the fixture would pass 
without testing anything.
     - The framework cannot assert the written schema, so check once, for 
example by reverting #5754, that the fixture fails without the fix.
   - `hash/hash.sql` (`CometExpressionSuite` L2713)
     - Int Min/Max, and float Max, Min and MinPositive.
     - Strings containing `\t`, `\r`, `\n`, `/`, `:` and `T`.
   
   **Bitwise**
   
   - `bitwise/bitwise.sql` (existing)
     - `CometBitwiseExpressionSuite` L57: bigint `shiftright` / `shiftleft` 
with literal and column amounts. The serde casts the amount to bigint, a path 
no fixture reaches.
     - L91, L112: a typed table (tinyint, smallint, int, bigint).
       - Values: NULL, 0, ±1, each type's min and max, 
4294967295/4294967294/4294967293, and wrapped byte/short values.
       - Query `bit_get` at boundary positions: 0 and 7 for tinyint, 0 and 15 
for smallint, 0 and 31 for int, and 0, 31, 32 and 63 for bigint. The Scala 
positions come from unseeded `Random`.
       - The Scala tests assert native `bit_get`, so also try upgrading 
`bitwise.sql:111` and `:121` from `spark_answer_only` to `query`.
     - L139, L162, L184: `bit_count` over tinyint, smallint, int, bigint and 
boolean with min, max, -1, 0 and NULL. Also `bit_count(true)` and 
`bit_count(false)`, and the bigints next to 2^32.
   - `bitwise/deep_bitwise_chains.sql` (new, `CometExpressionSuite` L3863): 
200-term `&`, `|` and `^` chains. These stay left-deep in SQL.
   
   **Blocked on #6617: `CometBitwiseExpressionSuite` L77** ("bitwise_get - 
throws exceptions")
   
   - As written, the test never reaches Comet. ConstantFolding throws during 
optimization in both runs, and the two `bit_count(x, y)` calls are analyzer 
arity errors. Drop those two.
   - A column-based fixture would hit datafusion-spark's message, "bit_get: 
position -30 is out of bounds. Expected pos < 32 and pos >= 0". That matches 
neither Spark 3.x ("Invalid bit position: -30 is less than zero") nor 4.x 
(`INVALID_PARAMETER_VALUE.BIT_POSITION_RANGE`).
   - This was found by reading the code, not by running it. If a run confirms 
it, file it as an error-parity bug while converting.
   
   ## Stays in Scala
   
   - **L577, the hash fuzz test.** Its type breadth is covered by 
`hash_types.sql` once timestamp_ntz is added. The randomness is the point.
   
   ## Notes on the Scala tests
   
   - **"hash - float" and "hash - double" insert a bare `-0.0`,** which is 
stored as +0.0. `hash.sql` uses `float('-0.0')` and `double('-0.0')`, so the 
fixture is the stronger test.
   - **Constant-folded literals.** `bit_count(true)`, `bit_count(false)` and 
`~(11)` are folded by Spark in the Scala suite, but reach native code in a 
fixture.
   - **Near-duplicates.**
     - "hash - empty strings and arrays" nearly duplicates "hash - multiple 
columns with arrays".
     - "bitwise_count - random values (spark gen)" is subsumed by the min/max 
conversion.
   
   ## 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 |
   |---|---|---|---|---|
   | `CometBitwiseExpressionSuite` | 32 | bitwise expressions | covered | 
bitwise/bitwise.sql |
   | `CometBitwiseExpressionSuite` | 57 | bitwise shift with different 
left/right types | convert | bitwise/bitwise.sql |
   | `CometBitwiseExpressionSuite` | 77 | bitwise_get - throws exceptions | 
blocked: #6617 | bitwise/bit_get_errors.sql (new; split MaxSparkVersion 3.5 / 
MinSparkVersion 4.0 if patterns differ) |
   | `CometBitwiseExpressionSuite` | 91 | bitwise_get - random values (spark 
parquet gen) | convert | bitwise/bitwise.sql |
   | `CometBitwiseExpressionSuite` | 112 | bitwise_get - random values (native 
parquet gen) | convert | bitwise/bitwise.sql |
   | `CometBitwiseExpressionSuite` | 139 | bitwise_count - min/max values | 
convert | bitwise/bitwise.sql |
   | `CometBitwiseExpressionSuite` | 162 | bitwise_count - random values (spark 
gen) | convert | bitwise/bitwise.sql |
   | `CometBitwiseExpressionSuite` | 184 | bitwise_count - random values 
(native parquet gen) | convert | bitwise/bitwise.sql |
   | `CometExpressionSuite` | 2035 | md5 | covered | hash/hash.sql |
   | `CometExpressionSuite` | 2592 | hash functions | covered | hash/hash.sql + 
hash/crc32.sql |
   | `CometExpressionSuite` | 2618 | sha2 with all-literal arguments and 
ConstantFolding disabled | covered | hash/hash.sql |
   | `CometExpressionSuite` | 2713 | hash functions with random input | convert 
| hash/hash.sql |
   | `CometExpressionSuite` | 2751 | hash function with decimal input | convert 
| hash/hash_decimal.sql (new) |
   | `CometExpressionSuite` | 2772 | xxhash64 function with decimal input | 
convert | hash/hash_decimal.sql (new) |
   | `CometExpressionSuite` | 3863 | deep bitwise And/Or/Xor chains do not 
overflow the protobuf recursion limit | convert | 
bitwise/deep_bitwise_chains.sql (new) |
   | `CometHashExpressionSuite` | 38 | hash - boolean | convert | 
hash/hash_types.sql (new) |
   | `CometHashExpressionSuite` | 46 | hash - byte/tinyint | convert | 
hash/hash_types.sql (new) |
   | `CometHashExpressionSuite` | 54 | hash - short/smallint | convert | 
hash/hash_types.sql (new) |
   | `CometHashExpressionSuite` | 62 | hash - int | convert | 
hash/hash_types.sql (new) |
   | `CometHashExpressionSuite` | 70 | hash - long/bigint | convert | 
hash/hash_types.sql (new) |
   | `CometHashExpressionSuite` | 79 | hash - float | covered | hash/hash.sql |
   | `CometHashExpressionSuite` | 87 | hash - double | covered | hash/hash.sql |
   | `CometHashExpressionSuite` | 95 | hash - string | covered | hash/hash.sql |
   | `CometHashExpressionSuite` | 103 | hash - binary | convert | 
hash/hash_types.sql (new) |
   | `CometHashExpressionSuite` | 111 | hash - date | convert | 
hash/hash_types.sql (new) |
   | `CometHashExpressionSuite` | 120 | hash - timestamp | convert | 
hash/hash_types.sql (new) |
   | `CometHashExpressionSuite` | 132 | hash - decimal (precision <= 18) | 
convert | hash/hash_decimal.sql (new) |
   | `CometHashExpressionSuite` | 142 | hash - decimal (precision > 18) | 
convert | hash/hash_decimal.sql (new) |
   | `CometHashExpressionSuite` | 153 | hash - array of decimal (precision > 
18) falls back to Spark | convert | hash/hash_decimal.sql (new) |
   | `CometHashExpressionSuite` | 162 | hash - struct with decimal (precision > 
18) falls back to Spark | convert | hash/hash_decimal.sql (new) |
   | `CometHashExpressionSuite` | 171 | hash - map with decimal (precision > 
18) value falls back to Spark | convert | hash/hash_decimal.sql (new) |
   | `CometHashExpressionSuite` | 184 | hash - array of integers | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 198 | hash - array of strings | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 213 | hash - array of doubles | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 225 | hash - nested array (array of arrays) | 
convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 237 | hash - struct | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 250 | hash - nested struct | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 262 | hash - null struct with a required 
child field | convert | hash/hash_required_child.sql (new) |
   | `CometHashExpressionSuite` | 288 | hash - null struct whose child is 
itself a struct | convert | hash/hash_required_child.sql (new) |
   | `CometHashExpressionSuite` | 313 | hash - list element wrapping a null 
struct with a required child field | convert | hash/hash_required_child.sql 
(new) |
   | `CometHashExpressionSuite` | 356 | hash - struct with array field | 
convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 368 | hash - array of structs | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 380 | hash - map | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 395 | hash - map with complex value type | 
convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 410 | hash - multiple primitive columns | 
covered | hash/hash.sql |
   | `CometHashExpressionSuite` | 423 | hash - multiple columns with arrays | 
convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 436 | hash - multiple columns with structs | 
convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 449 | hash - empty strings and arrays | 
convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 458 | hash - all nulls | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 467 | hash - with custom seed | covered | 
hash/hash.sql |
   | `CometHashExpressionSuite` | 477 | hash - large array | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 487 | hash - deeply nested structure | 
convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 504 | hash - with dictionary encoding | 
covered | hash/hash.sql |
   | `CometHashExpressionSuite` | 517 | hash - array with dictionary encoding | 
convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 533 | hash - array of struct | convert | 
hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 557 | hash - array of struct not eligible for 
batching | convert | hash/hash_nested.sql (new) |
   | `CometHashExpressionSuite` | 577 | hash - fuzz test | 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