Skip to main content

audit-comet-expression

Audit an existing Comet expression for correctness and test coverage. Studies the Spark implementation across versions 3.4.3, 3.5.8, 4.0.1, and 4.1.1, reviews the Comet and DataFusion implementations, identifies missing test coverage, and offers to implement additional tests.

소스 정보

저장소
apache/datafusion-comet
최근 소스 활동
2026년 8월 31일 18:24
감지된 SKILL.md 언어
영어
스타
1,288
포크
388

설치 방법

기본적으로 소스를 먼저 확인하는 Prompt가 선택됩니다. 직접 명령으로 전환하거나 로컬 사본을 다운로드할 수도 있습니다.

소스 파일 검토

설치 여부를 결정하기 전에 SKILL.md와 SkillsMP에 표시된 보조 파일을 읽어 보세요.

SKILL.md 표시 중

SKILL.md
소스 지침 · 읽기 전용 미리보기
name
audit-comet-expression
description
Audit an existing Comet expression for correctness and test coverage. Studies the Spark implementation across versions 3.4.3, 3.5.8, 4.0.1, and 4.1.1, reviews the Comet and DataFusion implementations, identifies missing test coverage, and offers to implement additional tests.
argument-hint
<expression-name>
<!-- Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. --> Audit the Comet implementation of the `$ARGUMENTS` expression for correctness and test coverage. ## Overview This audit covers: 1. Spark implementation across versions 3.4.3, 3.5.8, 4.0.1, and 4.1.1 2. Comet Scala serde implementation 3. Comet Rust / DataFusion implementation 4. Existing test coverage (Comet SQL Tests and Comet Scala Tests) 5. Gap analysis and test recommendations --- ## Step 1: Locate the Spark Implementations Clone specific Spark version tags (use shallow clones to avoid polluting the workspace). Only clone a version if it is not already present. ```bash set -eu -o pipefail for tag in v3.4.3 v3.5.8 v4.0.1 v4.1.1; do dir="/tmp/spark-${tag}" if [ ! -d "$dir" ]; then git clone --depth 1 --branch "$tag" https://github.com/apache/spark.git "$dir" fi done ``` ### Find the expression class in each Spark version Search the Catalyst SQL expressions source: ```bash for tag in v3.4.3 v3.5.8 v4.0.1 v4.1.1; do dir="/tmp/spark-${tag}" echo "=== $tag ===" find "$dir/sql/catalyst/src/main/scala" -name "*.scala" | \ xargs grep -l "case class $ARGUMENTS\b\|object $ARGUMENTS\b" 2>/dev/null done ``` If the expression is not found in catalyst, also check core: ```bash for tag in v3.4.3 v3.5.8 v4.0.1 v4.1.1; do dir="/tmp/spark-${tag}" echo "=== $tag ===" find "$dir/sql" -name "*.scala" | \ xargs grep -l "case class $ARGUMENTS\b\|object $ARGUMENTS\b" 2>/dev/null done ``` ### Read the Spark source for each version For each Spark version, read the expression file and note: - The `eval`, `nullSafeEval`, and `doGenCode` / `doGenCodeSafe` methods - The `inputTypes` and `dataType` fields (accepted input types, return type) - Null handling strategy (`nullable`, `nullSafeEval`) - ANSI mode behavior (`ansiEnabled`, `failOnError`) - Special cases, guards, `require` assertions, and runtime exceptions - Any constants or configuration the expression reads ### Compare across Spark versions Produce a concise diff summary of what changed between: - 3.4.3 → 3.5.8 - 3.5.8 → 4.0.1 - 4.0.1 → 4.1.1 Pay attention to: - New input types added or removed - Behavior changes for edge cases (null, overflow, empty, boundary) - New ANSI mode branches - New parameters or configuration - Breaking API changes that Comet must shim --- ## Step 2: Locate the Spark Tests ```bash for tag in v3.4.3 v3.5.8 v4.0.1 v4.1.1; do dir="/tmp/spark-${tag}" echo "=== $tag ===" find "$dir/sql" -name "*.scala" -path "*/test/*" | \ xargs grep -l "$ARGUMENTS" 2>/dev/null done ``` Read the relevant Spark test files and produce a list of: - Input types covered - Edge cases exercised (null, empty, overflow, negative, boundary values, special characters, etc.) - ANSI mode tests - Error cases This list will be the reference for the coverage gap analysis in Step 5. --- ## Step 3: Locate the Comet Implementation ### Scala serde ```bash # Find the serde object grep -r "$ARGUMENTS" spark/src/main/scala/org/apache/comet/serde/ --include="*.scala" -l grep -r "$ARGUMENTS" spark/src/main/scala/org/apache/comet/ --include="*.scala" -l ``` Read the serde implementation and check: - Which Spark versions the serde handles - Whether all input types Spark accepts are handled - Whether `convert` validates expression shape (e.g. literal-only arguments) before serializing Then audit the support-level and reason methods as described below. #### Audit `getSupportLevel`, `getIncompatibleReasons`, `getUnsupportedReasons`, and `convert` These four methods must stay aligned. Each has a distinct purpose, and the most common bugs in Comet serdes are misalignments between them. **Pick the right support level.** - `Unsupported(Some(reason))`: Comet cannot run this case at all. The dispatcher in `QueryPlanSerde.exprToProtoInternal` falls back to Spark unconditionally. Use this when an input type, option, or expression shape is not implemented, or when running the native path would crash or error. - `Incompatible(Some(reason))`: Comet can run this, but results may differ from Spark. The dispatcher only allows it when `spark.comet.expression.<exprName>.allowIncompatible=true` is set. Use this for known result differences such as locale sensitivity, timezone handling, ordering ambiguity, or floating-point precision. - `Compatible(None)`: full Spark compatibility for this combination of inputs and options. - `Compatible(Some(note))`: fully compatible but with a docs-only caveat. Note that any `Some(...)` on `Compatible` triggers a runtime `logWarning`, so reserve it for genuinely useful caveats. Decision rule: if the user would be surprised by a _wrong answer_, it is `Incompatible`. If the user would be surprised by a _runtime error or unsupported-type message_, it is `Unsupported`. **Runtime vs docs split.** - The `notes` field on `Incompatible(Some(...))` and `Unsupported(Some(...))` flows into EXPLAIN output via the dispatcher (see `QueryPlanSerde.exprToProtoInternal`, around the `case Incompatible` / `case Unsupported` branches). This is what users see when they ask why Comet fell back. - `getIncompatibleReasons()` and `getUnsupportedReasons()` are read only by `GenerateDocs` when building `docs/source/user-guide/compatibility.md`. They are static (no `expr` argument) and should enumerate every distinct reason the expression could ever return at runtime. - `convert` may also call `withInfo(expr, "reason")` and return `None` for cases that cannot be detected from the expression alone (for example, non-literal arguments, or child conversion failures). Those reasons belong in `getUnsupportedReasons()` too. **Consistency rules.** 1. If `getSupportLevel` can return `Incompatible(...)` for any input, override `getIncompatibleReasons()` and include a reason for every incompatible branch. 2. If `getSupportLevel` can return `Unsupported(...)` for any input, override `getUnsupportedReasons()` and include a reason for every unsupported branch. 3. If `convert` has its own `withInfo(...); None` fallbacks (e.g. literal checks), enumerate those reasons in `getUnsupportedReasons()` too. 4. Extract each reason into a `private val` and reference it from both `getSupportLevel` and `get*Reasons()`. Do not duplicate the string inline. Canonical example: `spark/src/main/scala/org/apache/comet/serde/arrays.scala::CometArrayIntersect`. It declares `private val incompatReason` and `private val unsupportedCollationReason`, and each is referenced from `getSupportLevel` and the matching reasons method. 5. Prefer `Incompatible(Some(reason))` over `Incompatible(None)`. The `None` form drops the reason from EXPLAIN output, leaving users with a generic "not fully compatible" message and forcing them to read the docs to find out why. 6. Gate compatibility decisions in `getSupportLevel`, not inside `convert`. Putting the check inside `convert` (e.g. reading a config flag and calling `withInfo`) bypasses the dispatcher's `allowIncompatible` handling and the EXPLAIN message becomes inconsistent with what the doc generator produces. **Wording guidelines for reason strings.** Reasons appear verbatim in the Compatibility Guide (rendered as Markdown) and in EXPLAIN output, so they are user-facing. - Lead with the user-observable effect, then the cause if helpful. ✅ "Result array element order may differ from Spark when the right array is longer than the left." ❌ "DataFusion probes the longer side." - Use sentence case and end with a period. - Use backticks around config keys, type names, and SQL identifiers. - Link to a tracking GitHub issue for known incompatibilities so users can follow progress: `(https://github.com/apache/datafusion-comet/issues/NNNN)`. **Verify the issue exists and is open** before citing it (`gh issue view <N> --repo apache/datafusion-comet`). Issue numbers invented from context or recalled from memory are a recurring failure mode: a stale link is worse than no link because the reader follows it and finds nothing. - Keep it concise. Single sentence is best. - Do not write "Incompatible reason: ..." or "Unsupported because ...". The doc generator adds the framing. - Phrase Incompatible reasons as _what differs from Spark_, not _what is missing_. Phrase Unsupported reasons as _what does not run_. If you find yourself writing an "Incompatible" reason that says "Comet only supports X" or "Y is not supported", the support level is probably wrong: it should be `Unsupported`. **Common antipatterns to flag during the audit.** - A `private val reason` constant declared near the top of the object, but `getSupportLevel` hardcodes a different string inline. The doc and the EXPLAIN message will drift. Real example: `CometHour` declares `incompatReason` then hardcodes a near-duplicate string in `getSupportLevel`. - Reason text duplicated in two places without a shared constant. Real examples: `CometMinute`, `CometSecond`. - `Incompatible(None)` paired with a populated `getIncompatibleReasons()`. The reason reaches the docs but not EXPLAIN output. Real example: `CometInitCap`. - `getIncompatibleReasons()` overridden but `getSupportLevel` never returns `Incompatible(...)`. Either the reasons method is dead code, or `getSupportLevel` is missing a branch. - A reason string phrased as "X is not supported" attached to an `Incompatible` branch (or vice versa). Re-read it and decide which support level it really belongs to. - `convert` bails out via `withInfo` for a case that is fully knowable from the expression (e.g. an unsupported child data type). Move the check into `getSupportLevel` so the dispatcher handles it uniformly. ### Shims ```bash find spark/src/main -name "CometExprShim.scala" | xargs grep -l "$ARGUMENTS" 2>/dev/null ``` If shims exist, read them and note any version-specific handling. ### Rust / DataFusion implementation ```bash # Search for the function in native/spark-expr grep -r "$ARGUMENTS" native/spark-expr/src/ --include="*.rs" -l grep -r "$ARGUMENTS" native/core/src/ --include="*.rs" -l ``` If the expression delegates to DataFusion, find it there too. Set `$DATAFUSION_SRC` to a local DataFusion checkout, or fall back to searching the cargo registry: ```bash if [ -n "${DATAFUSION_SRC:-}" ]; then grep -r "$ARGUMENTS" "$DATAFUSION_SRC" --include="*.rs" -l 2>/dev/null | head -10 else # Fall back to cargo registry (may include unrelated crates) grep -r "$ARGUMENTS" ~/.cargo/registry/src/*/datafusion* --include="*.rs" -l 2>/dev/null | head -10 fi ``` Read the Rust implementation and check: - Null handling (does it propagate nulls correctly?) - Overflow and underflow handling (returns `Err` vs panics) - Type dispatch (does it handle all types that Spark supports?) - ANSI / fail-on-error mode --- ## Step 4: Locate Existing Comet Tests ### Comet SQL Tests ```bash # Find SQL test files for this expression find spark/src/test/resources/sql-tests/expressions/ -name "*.sql" | \ xargs grep -l "$ARGUMENTS" 2>/dev/null # Also check if there's a dedicated file find spark/src/test/resources/sql-tests/expressions/ -name "*$(echo $ARGUMENTS | tr '[:upper:]' '[:lower:]')*" ``` Read every SQL test file found and list: - Table schemas and data values used - Queries exercised - Query modes used (`query`, `spark_answer_only`, `tolerance`, `ignore`, `expect_error`) - Any ConfigMatrix directives ### Comet Scala Tests ```bash grep -r "$ARGUMENTS" spark/src/test/scala/ --include="*.scala" -l ``` Read the relevant Comet Scala Tests and list: - Input types covered - Edge cases exercised - Whether constant folding is disabled for literal tests --- ## Step 5: Gap Analysis Compare the Spark test coverage (Step 2) against the Comet test coverage (Step 4). Produce a structured gap report: ### Coverage matrix For each of the following dimensions, note whether it is covered in Comet tests or missing: | Dimension | Spark tests it | Comet SQL Test | Comet Scala Test | Gap? | | ------------------------------------------------------------------------------------------------------ | -------------- | -------------- | ---------------- | ---- | | Column reference argument(s) | | | | | | Literal argument(s) | | | | | | NULL input | | | | | | Empty string / empty array / empty map | | | | | | Array/map with NULL elements | | | | | | Zero, negative zero, negative values (numeric) | | | | | | Underflow, overflow | | | | | | Boundary values (INT_MIN, INT_MAX, Long.MinValue, minimum positive, etc.) | | | | | | NaN, Infinity, -Infinity, subnormal (float/double) | | | | | | Multibyte / special UTF-8 (composed vs decomposed, e.g. `é` U+00E9 vs `e` + U+0301, non-Latin scripts) | | | | | | ANSI mode (failOnError=true) | | | | | | Non-ANSI mode (failOnError=false) | | | | | | All supported input types | | | | | | Parquet dictionary encoding (ConfigMatrix) | | | | | | Cross-version behavior differences | | | | | ### Implementation gaps Also review the Comet implementation (Step 3) against the Spark behavior (Step 1): - Are there input types that Spark supports but `getSupportLevel` returns `Unsupported` without comment? - Are there behavioral differences that are NOT marked `Incompatible` but should be? - Are there behavioral differences between Spark versions that the Comet implementation does not account for (missing shim)? - Does the Rust implementation match the Spark behavior for all edge cases? ### Support-level consistency audit Walk through this checklist against the serde. Each failed item is a finding for Step 6. 1. **Support level matches behavior.** For each branch of `getSupportLevel`, decide whether the user-observable effect is a wrong
GitHub에서 보기
이 SKILL.md는 매우 커서 SkillsMP가 여기에는 첫 섹션만 미리 보여줍니다. GitHub에서 보기