Parquet forbids empty groups, so a message containing a field whose
type is an empty proto message converted into a schema that writer
construction rejects with "Cannot write a schema with an empty group"
(InvalidSchemaException) - a single such field made the whole message
type unwritable.
Terminate such fields as BINARY holding the serialized message (zero
bytes when the field is set), the same mechanism PARQUET-1711 uses for
recursion beyond maxRecursion, preserving the field's repetition:
LIST-wrapped binary in parquet-specs mode, repeated binary in the old
style, optional binary for map values inside key_value. Field presence
and cardinality round-trip; only a message that is empty at the root is
still rejected.
ProtoWriteSupport's truncated-field detection now looks through the
LIST/MAP wrapper (getContentType) so BinaryWriter lines up with these
schemas.
Signed-off-by: Puškár, Peter <peter.puskar@firma.seznam.cz>
Rationale for this change
Protobuf allows empty message definitions, but Parquet forbids empty groups. Converting a message
that merely contains a field of an empty message type produces a schema with an empty group,
which writer construction rejects with InvalidSchemaException: Cannot write a schema with an empty group. Such fields appear in real-world schemas (deprecated stubs, marker/placeholder
messages), and a single one makes the whole message type unwritable.
What changes are included in this PR?
ProtoSchemaConverter.addMessageField terminates a field whose message type has no fields as a
BINARY column holding the serialized message — the same mechanism PARQUET-1711 uses for
recursion beyond maxRecursion. Since an empty message serializes to zero bytes, the column is
cheap, and field presence still round-trips (null = unset vs empty bytes = set):
addRepeatedPrimitive, so element cardinality survives;
ProtoWriteSupport.createMessageWriter's existing truncated-field check (primitive BINARY where a
message field was declared → BinaryWriter) is generalized to look through the LIST/MAP wrapper
(getGroupType → getContentType), so the writer tree lines up with these schemas.
A message that is empty at the root is still rejected — there is no parent field to hold the
bytes, and a Parquet file with zero columns is not representable.
Are these changes tested?
Yes. New ProtoEmptyMessageTest (new test messages Stub/StubBox in Trees.proto) writes
through the real write path (ProtoParquetWriter → MessageColumnIO, both specs-compliant and
old style) and reads back with GroupReadSupport:
zero-byte values;
empty group").
ProtoSchemaConverterTest.testEmptyMessageFields pins the converted schema. The full
parquet-protobuf suite passes (114 tests).
Are there any user-facing changes?
Message types that previously could not be written to Parquet at all now can; fields of empty
message types appear as (possibly LIST/MAP-wrapped) binary columns. No change for schemas that
were previously writable. Error behavior for an empty root message is unchanged.
Closes #2142