PDGGK opened a new pull request, #39587:
URL: https://github.com/apache/beam/pull/39587

   `Row.toString()` throws `NullPointerException` whenever a null sits inside 
an array, an iterable, a map or a nested row.
   
   ```java
   Schema schema = Schema.builder().addArrayField("a", 
FieldType.STRING.withNullable(true)).build();
   Row.withSchema(schema).addValue(Arrays.asList("x", null)).build().toString();
   // java.lang.NullPointerException: Cannot invoke "String.replace(...)" 
because "string" is null
   //     at SchemaUtils.toPrettyFieldValueString(SchemaUtils.java:256)
   ```
   
   `toPrettyRowString` skips a row's own null fields, but every recursive call 
hands the raw element to `toPrettyFieldValueString`, which has no null check 
and dereferences it — `string.replace` for `STRING`, `row.getValues()` for 
`ROW`, `value.getClass()` for `ARRAY`/`MAP`. Only the numeric and boolean 
branches survive, and only because `Objects.toString` already renders a null as 
`"null"`.
   
   These values are legal: `RowUtils` explicitly accepts null array elements 
and null map values when the element type is nullable.
   
   ## The fix
   
   Return `"null"` for a null value at the top of `toPrettyFieldValueString`. 
That is not a new rendering — it is exactly what the numeric branches have 
always produced, so this makes the remaining types consistent with the ones 
that already worked. Seven lines, one file, no API or wire-format change.
   
   `toPrettyRowString`'s deliberate skipping of top-level null fields is left 
alone; that is the printer's intended terse style.
   
   ## Why this regressed
   
   `Row.toString()` used to call `toString(true)`, whose private helper opens 
with `if (value == null) { return "<null>"; }` and is therefore null-safe at 
every depth. That method is still present. `Row.toString()` was rerouted to 
`SchemaUtils.toPrettyString(this)` in #35150, and 
`SchemaUtils.toPrettyString(this)` first appears in `Row.java` at `v2.69.0` — 
it is absent from `v2.67.0` and `v2.68.0` — so every release from 2.69.0 on has 
it.
   
   `toString()` is called from logging, `PAssert` failure messages, exception 
messages and debugger inspection, so in practice the NPE fires while a pipeline 
is already reporting a different problem, and hides it.
   
   `SchemaUtilsTest` is 107 lines and does not reference the pretty-printer at 
all, which is why the 279 lines added in #35150 went unnoticed.
   
   ## Testing
   
   Four tests added to `SchemaUtilsTest`: null inside an array of strings, a 
null map value, a null row inside an array, and — as a control that pins the 
existing behaviour — null inside an array of ints. The first three fail on 
current `master` with the NPEs above and pass with this change; the control 
passes both before and after, which is what makes the `"null"` rendering a 
consistency fix rather than a new choice.
   
   - `:sdks:java:core:test --tests "org.apache.beam.sdk.schemas.*Test"` with 
`--rerun-tasks` — 522 tests, 0 failures, 0 errors
   - `:sdks:java:core:spotlessJavaCheck` — passes
   
   Fixes #21063
   


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

Reply via email to