github-actions[bot] commented on code in PR #67325:
URL: https://github.com/apache/doris/pull/67325#discussion_r3985617451


##########
be/src/format_v2/lance/lance_reader_helper.cpp:
##########
@@ -178,8 +270,313 @@ Status arrow_field_to_doris_type(const 
std::shared_ptr<arrow::Field>& field,
     }
 }
 
+// Determine whether a field subtree contains values that require Lance 
normalization.
+Status field_requires_lance_normalization(const std::shared_ptr<arrow::Field>& 
field,
+                                          bool* requires_normalization) {
+    DORIS_CHECK(field != nullptr);
+    DORIS_CHECK(requires_normalization != nullptr);
+
+    LanceExtensionKind extension_kind;
+    std::shared_ptr<arrow::DataType> storage_type;
+    RETURN_IF_ERROR(get_lance_extension(field, &extension_kind, 
&storage_type));
+    bool required = extension_kind == LanceExtensionKind::BFLOAT16 ||
+                    field->type()->id() == arrow::Type::EXTENSION;
+    for (const auto& child : storage_type->fields()) {
+        bool child_required = false;
+        RETURN_IF_ERROR(field_requires_lance_normalization(child, 
&child_required));
+        required |= child_required;
+    }
+    *requires_normalization = required;
+    return Status::OK();
+}
+
+// Widen little-endian Lance BFloat16 values to Arrow Float32 without 
precision loss.
+Status convert_bfloat16_array(const std::shared_ptr<arrow::Array>& array,
+                              std::shared_ptr<arrow::Array>* normalized) {
+    DORIS_CHECK(array != nullptr);
+    DORIS_CHECK(normalized != nullptr);
+    const auto fixed_binary = 
std::dynamic_pointer_cast<arrow::FixedSizeBinaryArray>(array);
+    if (fixed_binary == nullptr || fixed_binary->byte_width() != 2) {
+        return Status::InvalidArgument("invalid Lance BFloat16 array storage: 
{}",
+                                       array->type()->ToString());
+    }
+    if (config::enable_arrow_input_validation) {
+        check_arrow_fixed_width_buffer(*fixed_binary, sizeof(uint16_t));
+    }
+
+    arrow::FloatBuilder builder;
+    auto arrow_status = builder.Reserve(fixed_binary->length());
+    if (!arrow_status.ok()) {
+        return Status::InternalError("reserve Lance BFloat16 output failed: 
{}",
+                                     arrow_status.message());
+    }
+    for (int64_t row = 0; row < fixed_binary->length(); ++row) {
+        if (fixed_binary->IsNull(row)) {
+            arrow_status = builder.AppendNull();
+        } else {
+            const auto bits = 
LittleEndian::Load16(fixed_binary->GetValue(row));
+            arrow_status = 
builder.Append(std::bit_cast<float>(static_cast<uint32_t>(bits) << 16));
+        }
+        if (!arrow_status.ok()) {
+            return Status::InternalError("append Lance BFloat16 value failed: 
{}",
+                                         arrow_status.message());
+        }
+    }
+    std::shared_ptr<arrow::FloatArray> result;
+    arrow_status = builder.Finish(&result);
+    if (!arrow_status.ok()) {
+        return Status::InternalError("finish Lance BFloat16 conversion failed: 
{}",
+                                     arrow_status.message());
+    }
+    *normalized = std::move(result);
+    return Status::OK();
+}
+
+// Materialize the visible range into an offset-zero Arrow array for Doris 
SerDes.
+Status compact_lance_array(const std::shared_ptr<arrow::Array>& array,
+                           std::shared_ptr<arrow::Array>* compacted) {
+    const auto validation = array->ValidateFull();
+    if (!validation.ok()) {
+        return Status::InvalidArgument("validate sliced Lance array failed: 
{}",
+                                       validation.message());
+    }
+    auto builder_result = arrow::MakeBuilder(array->type(), 
arrow::default_memory_pool());

Review Comment:
   [P2] Normalize registered extension children before compacting this parent. 
Arrow 24's C Data importer rehydrates the built-in `arrow.json` type 
recursively, so a sliced `List`/`Struct`/`Map` can still have an 
`ExtensionType` child here. `MakeBuilder(array->type())` recursively asks for 
child builders, and Arrow returns `NotImplemented` for extension types; the 
scan therefore fails before the later child-unwrapping loop runs. Please 
recurse/unwrap before parent compaction (or build from storage-typed children) 
and add a sliced nested `arrow.json` import/materialization regression.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/lance/LanceTypeConverter.java:
##########
@@ -37,28 +37,37 @@
 public final class LanceTypeConverter {
     private static final int MAX_DECIMAL_PRECISION = 76;
     private static final String ARROW_EXTENSION_NAME = "ARROW:extension:name";
+    private static final String ARROW_JSON_EXTENSION = "arrow.json";
+    private static final String LANCE_JSON_EXTENSION = "lance.json";
+    private static final String LANCE_BFLOAT16_EXTENSION = "lance.bfloat16";
 
     private LanceTypeConverter() {
     }
 
+    /** Converts Arrow fields exposed by Lance to Doris types. */
     public static Type toDorisType(Field field) {
-        // Arrow Java exposes unknown extension types through their storage 
type and field
-        // metadata. Treating the storage type as the logical type would make 
DESC report
-        // Blob, JSON, or BFloat16 as supported even though the scanner cannot 
decode their
-        // extension semantics. Dictionary arrays are likewise not decoded by 
the BE reader.
+        return toDorisType(field, true);
+    }
+
+    /** Converts an Arrow field, allowing Doris NULL only at the top level. */
+    private static Type toDorisType(Field field, boolean allowNull) {
         // TODO(lance): Dataset.getSchema() currently erases the Dictionary 
marker, while
         // Dataset.getLanceSchema() fails to convert a schema containing 
Dictionary in the
         // Lance 9.1.0-beta.3 Java SDK. Reject physical Dictionary columns 
after that SDK
         // conversion is fixed; an unmarked Int16 field cannot be 
distinguished safely here.
         String extensionName = field.getMetadata() == null
                 ? null : field.getMetadata().get(ARROW_EXTENSION_NAME);
-        if (field.getDictionary() != null
-                || (extensionName != null && !extensionName.isEmpty())) {
+        if (field.getDictionary() != null) {
             return Type.UNSUPPORTED;
         }
+        if (extensionName != null && !extensionName.isEmpty()) {

Review Comment:
   [P2] Gate catalog/S3 execution on BE support for these new encodings. This 
converter can now advertise JSONB/FLOAT from FE metadata, but `LanceScanNode` 
still schedules through the ordinary backend policy, including a smooth-upgrade 
source BE running the previous reader. BFloat16 then reaches that reader as 
`FixedSizeBinary(2)` and is rejected by the FLOAT SerDe (JSON similarly lacks 
the new normalization). The local-TVF pin does not cover this FE-discovery 
path; please filter/reject old source BEs when these types are projected and 
add a mixed-version catalog regression.



##########
fe/fe-core/src/main/java/org/apache/doris/datasource/FederationBackendPolicy.java:
##########
@@ -156,13 +156,25 @@ public void init() throws UserException {
     }
 
     public void init(List<String> preLocations) throws UserException {
+        init(preLocations, Collections.emptyList());
+    }
+
+    /** Initialize the policy with exactly one eligible backend. */
+    public void initWithBackendId(long backendId) throws UserException {
+        init(Collections.emptyList(), Collections.singletonList(backendId));
+    }
+
+    /** Build the standard external-scan policy with optional location and 
backend-ID constraints. */
+    private void init(List<String> preLocations, List<Long> requiredBackendIds)
+            throws UserException {
         // scan node is used for query
         BeSelectionPolicy.Builder builder = new BeSelectionPolicy.Builder();
         builder.needQueryAvailable()
                 .needLoadAvailable()
                 
.preferComputeNode(Config.prefer_compute_node_for_external_table)
                 .assignExpectBeNum(Config.min_backend_num_for_external_table)
-                .addPreLocations(preLocations);
+                .addPreLocations(preLocations)
+                .addRequiredBackendIds(requiredBackendIds);

Review Comment:
   [P2] Select the schema BE from the same eligibility set used here. 
Shared-storage local Lance currently picks `backendIdForRequest` from all alive 
nodes, but this standard policy additionally rejects compute-only nodes when 
`prefer_compute_node_for_external_table=false` (the default), plus 
query/load-disabled, decommissioning, blacklisted, or out-of-group nodes. If 
one of those served schema discovery, the required-ID filter leaves no 
candidates and planning fails with `No available backends`. Please align schema 
selection with execution eligibility (or make the exact-ID role semantics 
explicit while retaining real availability checks) and cover a 
compute-only/ineligible selected BE.



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

Reply via email to