timsaucer commented on code in PR #1678:
URL:
https://github.com/apache/datafusion-python/pull/1678#discussion_r3905881375
##########
crates/core/src/codec.rs:
##########
@@ -223,32 +224,129 @@ fn strip_wire_header<'a>(
Ok(Some(&buf[py_minor_idx + 1..]))
}
+/// Run `f` against each codec in `chain`, returning the first `Ok`.
+///
+/// A codec signals "not mine" by returning an error, so the chain
+/// keeps trying until a codec succeeds. When every codec fails and the
+/// chain has more than one entry, the errors are aggregated into a
+/// single message — returning only the last error would surface the
+/// terminal `Default*ExtensionCodec` "not provided" message and mask
+/// the more specific diagnostic from an installed codec (e.g. a
+/// corrupt-token error from the codec that owns the payload family).
+fn chain_try<C: ?Sized, R>(chain: &[Arc<C>], what: &str, f: impl Fn(&C) ->
Result<R>) -> Result<R> {
+ let mut errors: Vec<datafusion::error::DataFusionError> = Vec::new();
+ for codec in chain {
Review Comment:
You were both right, and I've reworked the dispatch rather than keeping
trial decoding. Thanks for the #16980 / #16986 pointers — the
CSV-decoded-as-Parquet case is exactly the failure mode, and it's not one a
codec can defend against from inside, since `MyMessage::decode(buf)` has no
prefix to check and cannot decline.
Every payload an installed codec writes is now wrapped in an envelope naming
its author: `DFPYCHN | version: u8 | id_len: u32 (LE) | id | blob`. Decoding
reads the id and consults exactly one codec. Nothing is ever offered to a codec
that did not write it, so structurally similar prost messages can't cross
libraries. Codecs themselves are unchanged — they receive their payload byte
for byte and never see the envelope.
On reusing `ComposedPhysicalExtensionCodec`: I looked at it closely and it
doesn't fit here, for three reasons.
1. It keys on `encoder_position`, which is sound when both ends build the
same list in the same order. That's true for the consumers it was written for —
Ballista's list is a compile-time constant, and datafusion-distributed pins its
own codec at index 0 and appends user codecs rebuilt from the same startup code
on every node. It isn't true here: the chain is assembled by user Python, and
`Expr.to_bytes(ctx1)` / `Expr.from_bytes(ctx2)` puts two independently
configured sessions on either end of one payload. An index names a different
codec in the decoder as soon as install order differs.
`test_decode_survives_a_different_install_order` is the case.
2. It only exists for the physical layer. There's no
`ComposedLogicalExtensionCodec`, and this needs both — table providers and UDFs
go through the logical codec.
3. `encode_protobuf` always wraps in `DataEncoderTuple`, so a codec that
writes zero bytes still emits a non-empty payload. That sets `fun_definition`
and permanently skips the `FunctionRegistry` lookup the decoder does first,
which breaks DataFusion's encode-by-name path. I need that path to keep working
— `NameOnlyUdfCodec` in the FFI example owns functions that are fully described
by their names and encodes nothing at all.
The reasoning is recorded in the code at `crates/core/src/codec.rs` (see
`ChainEntry` and `chain_decode`, which cite both upstream issues) so the next
person doesn't re-derive it, and the "Composable codecs" section of
`docs/source/contributor-guide/ffi.md` covers it for extension authors. The
empty-payload case is the one place codecs are still consulted in turn, because
there are no bytes to carry an identity; that question is "do you own the
function named `x`", which is name-scoped, and two codecs disagreeing means
they've already collided in the function registry.
--
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]