SEPURI-SAI-KRISHNA opened a new issue, #12968:
URL: https://github.com/apache/seatunnel/issues/12968
### Search before asking
- [x] I searched the open and closed issues and pull requests and found no
report of this. Queries included `arrow struct null`, `arrow map null`,
`StructConverter null`, `MapConverter null`, `arrow converter null`, `doris
struct null`, `starrocks struct null` and `ROW type null arrow`, on both the
issue search and the separate pull request search. #11219 touches
`StructConverter` and `MapConverter` but only changes their SPI registration.
### What happened
`ArrowToSeatunnelRowReader` dispatches a column by its SeaTunnel `SqlType`:
`MAP` goes to `MapConverter`, `ROW` to `StructConverter`, `ARRAY` to the list
converters. Those three cases call the three argument `convert(rowIndex,
fieldVector, genericsConverters)` overload, which carries the element type
converters.
Two of the five complex converters do not check the Arrow validity bit in
that overload:
- **`StructConverter`**: the overload calls
`fieldVector.getObject(rowIndex)` and then `entrySet()` on the result. For a
null cell `getObject` returns null, so the read ends with a
`NullPointerException`. One null struct cell costs the whole split, not one
value.
- **`MapConverter`**: the overload positions a `UnionMapReader` and loops on
`reader.next()`, which yields nothing for a null cell, so the method returns an
empty `HashMap`. A column that holds no map and a column that holds an empty
map become the same value.
The three list converters all guard this case and return `null`. The
inconsistency has been there since the Arrow reader was added in #8137, and
that single commit introduced all five:
| Converter | two argument overload | generics overload |
|---|---|---|
| `ListConverter` | guards null | **guards null** |
| `LargeListConverter` | guards null | **guards null** |
| `FixedSizeListConverter` | guards null | **guards null** |
| `StructConverter` | guards null | **no guard** |
| `MapConverter` | guards null | **no guard** |
`git log -S'isNull'` on the two files shows no guard was ever added to the
generics overload and later removed, so this is an oversight rather than a
deliberate choice.
**Who is affected.** `ArrowToSeatunnelRowReader` has exactly two consumers
in `src/main`: `DorisValueReader` and `StarRocksBeReadClient`. So a nullable
`STRUCT` column in a Doris or StarRocks source fails the read, and a nullable
`MAP` column silently reads as an empty map.
**Why no test caught it.** `ArrowToSeatunnelRowReaderTest` has no
`STRUCT`/`ROW` column at all. Its `MAP` column is declared in the schema holder
but never populated: no `MapVector` is ever constructed, the `writeKeyAndValue`
helper is dead code, `mapData` is never written to, and the assertion is a `//
todo check map` noting that "The java api has problems building MapVectors,and
there are no examples on the official website" (apache/arrow#44664).
`DorisMultiReadIT` declares 14 nullable `MAP` columns, but builds its maps once
outside the row loop and inserts the same non-null single entry maps into all
100 rows, so no null map ever reaches the converter.
### SeaTunnel Version
Reproduced on `dev` at `14a27ac26a0a71b56722f86cdf8ec2779094ceaf`.
### SeaTunnel Config
The user-facing shape is a Doris or StarRocks source over a table with a
nullable complex column, for example:
```hocon
source {
Doris {
fenodes = "doris:8030"
username = "root"
password = ""
database = "test"
table = "events"
# events has a nullable `profile STRUCT<age:INT>` and a nullable `labels
MAP<INT,INT>`
}
}
```
I did not run a live Doris cluster. The reproduction below instead drives
the same production code path directly through `ArrowToSeatunnelRowReader`'s
byte array constructor, which is the constructor both sources use, so no
connector or container is required to observe either defect.
### Running Command
A single record batch Arrow stream with an `id` column plus one nullable
complex column, read through the production constructor. With a test of that
shape placed in `connector-common`:
```shell
JAVA_HOME=/usr/lib/jvm/java-17-openjdk-amd64 ./mvnw test \
-pl seatunnel-connectors-v2/connector-common \
-Dtest=ArrowToSeatunnelRowReaderNullCellTest -Dskip.spotless=true
```
Because the stream holds a single batch, neither failure can be attributed
to a record batch boundary.
### Error Exception
The `ROW` column aborts the read:
```
java.lang.NullPointerException: Cannot invoke "java.util.Map.entrySet()"
because "valueMap" is null
```
`valueMap` followed by `entrySet()` occurs in exactly one place in the
repository, `StructConverter.java:43`, so the message identifies the site on
its own.
The `MAP` column does not throw, it returns the wrong value:
```
org.opentest4j.AssertionFailedError: a null map cell must read as null, not
as an empty map ==> expected: <null> but was: <{}>
```
**Expected**: both read back as `null`, as the list converters already do,
while a cell holding a genuinely empty map keeps reading as an empty map so
that absent and empty stay distinct.
### Zeta or Flink or Spark Version
Not engine specific. The defect is in `connector-common`, below the engine,
on the source read path.
### Java or Scala Version
`openjdk version "17.0.20.1" 2026-08-18`, OpenJDK 64-Bit Server VM build
`17.0.20.1+1-1-24.04-Ubuntu`, with Apache Maven 3.8.4.
### Screenshots
_No response_
### Are you willing to submit PR?
- [x] Yes I am willing to submit a PR!
### Code of Conduct
- [x] I agree to follow this project's [Code of
Conduct](https://www.apache.org/foundation/policies/conduct)
--
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]