Skip to content

fix: gate bit_length/octet_length on BinaryType and downgrade translate - #4594

Merged
andygrove merged 4 commits into
apache:mainfrom
andygrove:fix/string-audit-followups
Jun 12, 2026
Merged

fix: gate bit_length/octet_length on BinaryType and downgrade translate#4594
andygrove merged 4 commits into
apache:mainfrom
andygrove:fix/string-audit-followups

Conversation

@andygrove

Copy link
Copy Markdown
Member

Which issue does this PR close?

Closes #4464
Closes #4463

Rationale for this change

Two correctness issues surfaced by the string-expressions audit (#4461). In both cases, the serde reports Compatible while the underlying native path silently diverges from Spark — EXPLAIN, the auto-generated compatibility doc, and the dispatcher all see Compatible so the operator runs natively rather than falling back.

  • [Bug] bit_length and octet_length error natively for BinaryType input instead of falling back #4464: bit_length / octet_length report Compatible(None) for BinaryType, but DataFusion's BitLengthFunc / OctetLengthFunc use Signature::coercible(... logical_string() ...) and reject Binary at execution time. The result: bit_length(<binary>) and octet_length(<binary>) plan successfully under Comet, then surface as a native execution error rather than falling back cleanly. The sibling length already guards BinaryType via CometLength.

  • [Bug] translate uses graphemes vs Spark code points and ignores U+0000 deletion #4463: translate is wired as CometScalarFunction("translate") and reports Compatible, but DataFusion's translate (1) iterates over Unicode graphemes while Spark uses code points (so combining marks / ZWJ sequences disagree), and (2) substitutes U+0000 instead of treating it as a deletion sentinel like Spark's StringTranslate.buildDict.

What changes are included in this PR?

  • CometBitLength / CometOctetLength: new serdes that gate BinaryType as Unsupported(Some(...)) (mirroring the existing CometLength shape). The string path remains Compatible.
  • CometStringTranslate: new serde that returns Incompatible(Some(...)) so the divergent native path only runs when the user opts in via spark.comet.expression.StringTranslate.allowIncompatible=true. The notes call out both divergences (graphemes vs code points, U+0000 deletion).

How are these changes tested?

  • bit_length.sql / octet_length.sql: extended with expect_fallback(... on BinaryType is not supported) cases that confirm binary input falls back cleanly and Spark and Comet agree on the answer.
  • string_translate.sql: converted to expect_fallback(is not fully compatible with Spark) to assert the default-path fallback behaviour.
  • string_translate_enabled.sql: new fixture that sets spark.comet.expression.StringTranslate.allowIncompatible=true and exercises the native path on ASCII inputs where Spark and DataFusion agree.
  • CometStringExpressionSuite "length, reverse, instr, replace, translate" wrapped in withSQLConf(...allowIncompatible=true) so the existing translate assertion still runs natively.

Verified passing on Spark 3.5: CometSqlFileTestSuite expressions/string/ (45 tests succeeded), CometStringExpressionSuite (33 tests succeeded), spotless:check.

andygrove added 2 commits June 4, 2026 13:56
Surfaced by the string-expressions audit (apache#4461).

* `bit_length` / `octet_length`: report `Compatible` while DataFusion's
  native impls reject `BinaryType` at execution time, so calls on binary
  columns surface as a native error rather than falling back to Spark.
  Add `CometBitLength` / `CometOctetLength` serdes that gate `BinaryType`
  as `Unsupported`, mirroring the existing `CometLength` shape. Closes
  apache#4464.

* `translate`: report `Compatible` while DataFusion's `translate`
  iterates over Unicode graphemes (Spark uses code points) and
  substitutes U+0000 instead of treating it as a deletion sentinel. Add
  `CometStringTranslate` that returns `Incompatible(...)` so the
  divergent native path only runs when the user opts in via
  `spark.comet.expression.StringTranslate.allowIncompatible=true`.
  Closes apache#4463.

Tests: extend `bit_length.sql` / `octet_length.sql` with `expect_fallback`
cases on binary input; convert `string_translate.sql` to assert the
default fallback path; add `string_translate_enabled.sql` exercising the
opt-in native path on ASCII inputs where Spark and DataFusion agree.
The existing `CometStringExpressionSuite` translate assertion is wrapped
in `withSQLConf(StringTranslate.allowIncompatible=true)`.
@andygrove andygrove added this to the 0.17.0 milestone Jun 10, 2026

@parthchandra parthchandra left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lgtm. One minor comment

}

object CometStringTranslate extends CometScalarFunction[StringTranslate]("translate") {
override def getSupportLevel(expr: StringTranslate): SupportLevel = Incompatible(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we also want a getIncompatibleReasons here?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated. Thanks for catching that.

@andygrove
andygrove merged commit 03c1478 into apache:main Jun 12, 2026
81 of 82 checks passed
@andygrove
andygrove deleted the fix/string-audit-followups branch June 12, 2026 00:29
marvelshan pushed a commit to marvelshan/datafusion-comet that referenced this pull request Jul 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants