voonhous commented on code in PR #19687:
URL: https://github.com/apache/hudi/pull/19687#discussion_r3877859273


##########
hudi-common/src/main/java/org/apache/hudi/common/util/SortUtils.java:
##########
@@ -21,14 +21,127 @@
 import org.apache.hudi.common.avro.HoodieAvroUtils;
 import org.apache.hudi.common.model.HoodieRecord;
 import org.apache.hudi.common.schema.HoodieSchema;
+import org.apache.hudi.common.schema.HoodieSchemaField;
+import org.apache.hudi.common.schema.HoodieSchemaType;
 import org.apache.hudi.common.util.collection.FlatLists;
+import org.apache.hudi.common.util.collection.Pair;
+import org.apache.hudi.exception.HoodieException;
 
+import java.util.Locale;
+import java.util.Map;
 import java.util.function.Function;
+import java.util.stream.Collectors;
 
 /**
  * Utility functions used by BULK_INSERT practitioners while sorting records.
  */
 public class SortUtils {
+
+  /**
+   * Rejects sort columns whose type cannot serve as a sort key. Spark's 
RowOrdering.isOrderable
+   * is false for both VARIANT and MAP, which is the binding constraint on the 
row path. On the
+   * Avro path only MAP is outright uncomparable (GenericData.compare throws 
"Can't compare
+   * maps!"); a variant's {metadata, value} record does compare, but by its 
bytes, which is never
+   * a meaningful sort key. The walk recurses through records and array 
elements just as
+   * isOrderable does, so a struct or an array that merely holds a variant or 
a map at depth is
+   * rejected too, and the error names the nested member that made the column 
unorderable. Without
+   * this check the failure surfaces deep in the write job (an 
AnalysisException from the row
+   * partitioner, a ClassCastException from the record-based one) without 
naming the column.
+   *
+   * <p>Matching is case-insensitive, mirroring Spark's column resolution. 
Names absent from the
+   * schema (nested paths, meta columns on a data-only schema) are left for 
the caller to handle.
+   *
+   * @param sortColumns the configured sort columns, may be null or empty
+   * @param schema      schema of the data, with or without metadata fields
+   */
+  public static void validateSortableColumns(String[] sortColumns, 
HoodieSchema schema) {
+    if (sortColumns == null || sortColumns.length == 0
+        || schema == null || schema.getType() != HoodieSchemaType.RECORD) {
+      return;
+    }
+    Map<String, HoodieSchemaField> fieldsByLowerName = 
schema.getFields().stream()
+        .collect(Collectors.toMap(field -> 
field.name().toLowerCase(Locale.ROOT), Function.identity(), (first, second) -> 
first));
+    for (String sortColumn : sortColumns) {
+      String columnName = sortColumn.trim();
+      HoodieSchemaField field = 
fieldsByLowerName.get(columnName.toLowerCase(Locale.ROOT));
+      if (field == null) {
+        continue;

Review Comment:
   Moved. `SortUtils.resolveSortColumn` now carries the segment walk (records 
only, exact match before case-insensitive, `-1` split so `s.` does not 
collapse), `validateSortableColumns` resolves every column through it before 
checking the leaf, and `run_clustering` calls the same resolver instead of its 
private copy. So `hoodie.clustering.plan.strategy.sort.columns = 's.tags'` is 
rejected by the execution strategy / partitioner constructors with `Sorting by 
column 's.tags' of type MAP is not supported` rather than Spark's 
AnalysisException. Pinned in `TestSortUtils` (`s.m`, `S.M`, and the 
non-resolving `s.missing` / `m.key_value.value` / `v.metadata` / `s.` / `.s` 
left to the caller) and with an inline-clustering leg in 
`TestClusteringProcedure`. Fixed in 725f0269ac0d.



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