spyrospav opened a new pull request, #57052:
URL: https://github.com/apache/spark/pull/57052

   ### What changes were proposed in this pull request?
   
   `translate(input, from, to)` replaces characters and, when `to` is shorter 
than `from`, deletes the extra matched characters. The deletion case was 
signalled in-band by mapping those characters to the NUL character (`U+0000`) 
and checking `"\0".equals(...)` in the execution loop. That in-band marker 
collided with a legitimate replacement value: translating a character *to* a 
literal `U+0000` was interpreted as deletion instead of a one-character NUL 
replacement.
   
   This PR switches the in-band deletion marker to the empty string, which can 
never be a valid one-character replacement, and interprets dictionary values 
uniformly:
   
   - `null` mapping -> no mapping, keep the original character;
   - empty-string mapping -> delete the character;
   - non-empty mapping -> replace (including a literal `U+0000`).
   
   The change is applied consistently across all translate paths so the 
collations stay in sync:
   
   - `StringTranslate.buildDict` (`stringExpressions.scala`) now uses `""` as 
the deletion marker (and `!dict.containsKey(...)` for the first-occurrence-wins 
guard);
   - `UTF8String.translate` (UTF8_BINARY path);
   - `CollationAwareUTF8String.lowercaseTranslate` (UTF8_LCASE path) and 
`CollationAwareUTF8String.translate` (ICU path).
   
   ### Why are the changes needed?
   
   It is a correctness bug: a literal `U+0000` is a valid replacement value, 
but the in-band NUL deletion marker made `translate` drop the character instead 
of replacing it. For example, `translate('A', 'A', char(0))` returned `''` 
(deletion) instead of a single `U+0000` character.
   
   ### Does this PR introduce _any_ user-facing change?
   
   Yes, in one edge case. When a character in `to` is a literal `U+0000`:
   
   - Before: the matched character was deleted from the output.
   - After: the matched character is replaced with `U+0000`.
   
   Deletion semantics for the normal case (`to` shorter than `from`) are 
unchanged. All other translations are unchanged.
   
   ### How was this patch tested?
   
   Added regression coverage and ran the affected suites:
   
   - `CollationSupportSuite.testStringTranslate` -- for each collation 
(`UTF8_BINARY`, `UTF8_LCASE`, `UNICODE`, `UNICODE_CI`): literal `U+0000` 
replacement is preserved (with a byte/length assertion), deletion with a 
shorter `to`, mixed replacement + deletion, and first-occurrence-wins on a 
duplicate `from` key. The test's dictionary-building helper was updated to the 
empty-string marker to match production.
   - `UTF8StringSuite.translate` -- migrated the existing deletion dictionaries 
from the `"\0"` marker to `""`, and added a positive literal-`U+0000` 
replacement case.
   - `StringExpressionsSuite.translate` -- expression-level literal `U+0000` 
replacement and mixed replacement/deletion cases.
   
   All pass. I also micro-benchmarked the `translate` execution loop 
(UTF8_BINARY / UTF8_LCASE) before and after; the change is performance-neutral 
(`isEmpty()` is cheaper-or-equal to `"\0".equals(...)`), with differences 
within run-to-run noise.
   
   ### Was this patch authored or co-authored using generative AI tooling?
   
   Generated-by: Claude Code (Opus 4.8)
   


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