JingsongLi commented on code in PR #9148:
URL: https://github.com/apache/paimon/pull/9148#discussion_r3749922100


##########
paimon-python/pypaimon/common/options/core_options.py:
##########
@@ -1213,7 +1222,18 @@ def variant_shredding_schema(self) -> Optional[str]:
         return val
 
     def blob_descriptor_fields(self, default=None):
-        value = self.options.get(CoreOptions.BLOB_DESCRIPTOR_FIELD, default)
+        value = self.options.get(CoreOptions.BLOB_DESCRIPTOR_FIELD, None)
+        if isinstance(value, str):
+            value = value.strip()
+        if not value:

Review Comment:
   Please choose the legacy fallback based on key presence rather than value 
truthiness. Java `Options.applyWithOption` consults fallback keys only when the 
canonical key is absent. With `blob-descriptor-field=""` and 
`blob.stored-descriptor-fields` set, Java resolves an empty descriptor-field 
set, but this code revives the legacy fields. That can make Python interpret 
ordinary BLOB bytes as descriptors or choose a different write layout. An 
explicitly present blank canonical value should win.



##########
paimon-python/pypaimon/table/row/blob.py:
##########
@@ -118,6 +118,53 @@ def deserialize(cls, data: bytes) -> 'BlobDescriptor':
         descriptor._version = version
         return descriptor
 
+    @classmethod
+    def _try_parse_serialized(cls, data: bytes) -> Optional['BlobDescriptor']:
+        if not isinstance(data, (bytes, bytearray)):
+            return None
+        raw = bytes(data)
+        if len(raw) < 21:
+            return None
+        try:
+            offset = 0
+            version = raw[offset]
+            offset += 1
+            if version < 1 or version > cls.CURRENT_VERSION:
+                return None
+            if version > 1:
+                if offset + 8 > len(raw):
+                    return None
+                magic = struct.unpack('<Q', raw[offset:offset + 8])[0]
+                if magic != cls.MAGIC:
+                    return None
+                offset += 8
+            if offset + 4 > len(raw):
+                return None
+            uri_length = struct.unpack('<I', raw[offset:offset + 4])[0]
+            total = offset + 4 + uri_length + 16
+            if total != len(raw):

Review Comment:
   `from_descriptor_bytes` is used when the schema/storage context already says 
that the value is a descriptor, but this exact-length check rejects v1 bytes 
with trailing padding. Java `BlobDescriptor.deserialize` accepts trailing bytes 
for every supported version, so a padded legacy v1 descriptor can be read by 
Java but is rejected by Python (and the new test currently codifies that 
mismatch). Please deserialize v1 with the same Java semantics here, or 
coordinate a strict contract change on both implementations.



##########
paimon-python/pypaimon/write/blob_format_writer.py:
##########
@@ -290,17 +290,44 @@ def _write_blob_data(self, blob_value: Blob, crc32: int):
             data = blob_value.to_data()
             crc32 = self._write_with_crc(data, crc32)
         else:
+            expected_length = self._expected_blob_length(blob_value)

Review Comment:
   Please restrict the exact-length path to an exact `BlobRef` (for example, 
`type(blob_value) is BlobRef`). This currently trusts `to_descriptor().length` 
for every `Blob` subtype, so a custom subtype whose `new_input_stream()` 
exposes more bytes than its descriptor is silently truncated. I reproduced a 
descriptor length of 3 with a stream containing `abcdef`; this branch writes 
only `abc`, while the base branch writes all 6 bytes. The Java writer 
deliberately checks `blob.getClass() == BlobRef.class` and reads other 
implementations to EOF.



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