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


##########
hudi-trino/src/main/java/io/trino/plugin/hudi/HudiUtil.java:
##########
@@ -272,6 +272,12 @@ public static HoodieTableMetaClient buildTableMetaClient(
 
     public static Schema constructSchema(List<String> columnNames, 
List<HiveType> columnTypes)
     {
+        // A count(*)-style query projects no columns at all; 
determineSchemaOrThrowException rejects
+        // an empty column list, so build the empty record schema directly.
+        if (columnNames.isEmpty()) {
+            return SchemaBuilder.record("baseRecord").fields().endRecord();

Review Comment:
   Updated the description -- the serializer rework and the `count(*)` fix are 
now their own items, and the impact section calls out the new 
`HudiAvroSerializer` constructor. The `constructSchema` contract change is gone 
entirely (see the serializer thread).



##########
hudi-trino/src/main/java/io/trino/plugin/hudi/HudiUtil.java:
##########
@@ -651,8 +657,13 @@ static LinkedHashSet<String> 
mergeRequiredColumnNames(HoodieTableConfig tableCon
         // Delete markers and the operation field decide record deletion at 
merge time
         requiredColumnNames.add(HOODIE_IS_DELETED_FIELD);
         requiredColumnNames.add(OPERATION_METADATA_FIELD);
-        String deleteKey = tableConfig.getProps().getProperty(DELETE_KEY);
-        String deleteMarker = 
tableConfig.getProps().getProperty(DELETE_MARKER);
+        // Resolve the delete key/marker the way the file-group reader does 
(ConfigUtils.getMergeProps):
+        // table merge properties first -- v9+ tables persist these PREFIXED 
(hoodie.record.merge.property.*)
+        // and getTableMergeProperties() strips the prefix and bridges legacy 
delete payloads -- falling back
+        // to the raw table props, where pre-prefix tables and reader/write 
configs carry the plain keys.
+        Map<String, String> tableMergeProps = 
tableConfig.getTableMergeProperties();

Review Comment:
   Good catch -- this module's profile being off by default let it slip past 
the local build. Now passing `tableConfig.getPayloadClass()`, matching what 
`ConfigUtils.getMergeProps` resolves here.



##########
hudi-trino/src/main/java/io/trino/plugin/hudi/HudiUtil.java:
##########
@@ -715,14 +726,19 @@ public static List<HiveColumnHandle> 
appendMissingMergeRequiredColumns(
     /**
      * Builds {@link HiveColumnHandle}s, preserving physical (data-column) 
index, for the data columns whose names
      * appear in {@code columnNames}. Names that are not data columns (e.g. 
Hudi meta fields) or whose types are not
-     * supported by the storage format are skipped.
+     * supported by the storage format are skipped. Matching is 
case-insensitive: predicted merge columns carry the
+     * table schema's field casing (e.g. the {@code Op} delete key of DMS 
tables) while metastore column names are
+     * lowercased.
      */
     private static List<HiveColumnHandle> buildColumnHandles(Table table, 
TypeManager typeManager, Set<String> columnNames, HiveTimestampPrecision 
timestampPrecision)
     {
+        Set<String> lowercaseColumnNames = columnNames.stream()
+                .map(name -> name.toLowerCase(Locale.ROOT))
+                .collect(Collectors.toSet());
         ImmutableList.Builder<HiveColumnHandle> columns = 
ImmutableList.builder();
         int hiveColumnIndex = 0;
         for (Column field : table.getDataColumns()) {
-            if (columnNames.contains(field.getName())) {
+            if 
(lowercaseColumnNames.contains(field.getName().toLowerCase(Locale.ROOT))) {

Review Comment:
   You're right -- `appendMissingMergeRequiredColumns` recovers it on every 
merge path (and the full-schema branch is a superset), so the folding changed 
no outcome. Reverted; the javadoc now points at the recovery instead.



##########
hudi-trino/src/main/java/io/trino/plugin/hudi/reader/HudiTrinoReaderContext.java:
##########
@@ -176,9 +184,25 @@ private List<HiveColumnHandle> 
buildRequiredColumnHandles(HoodieSchema requiredS
     {
         List<HiveColumnHandle> handles = new ArrayList<>();
         for (HoodieSchemaField field : requiredSchema.getFields()) {
-            handles.add(colNameToHandle.computeIfAbsent(
+            HiveColumnHandle handle = colNameToHandle.computeIfAbsent(
                     field.name().toLowerCase(Locale.ROOT),
-                    _ -> HudiUtil.toColumnHandle(field)));
+                    _ -> HudiUtil.toColumnHandle(field));
+            if (!handle.getName().equals(field.name())) {

Review Comment:
   Covered by the serializer change now: parquet resolves columns 
case-insensitively and hudi-common reads merge fields off the record's schema, 
which `serialize()` stamps with the table casing. Removed.



##########
hudi-trino/src/main/java/io/trino/plugin/hudi/util/HudiAvroSerializer.java:
##########
@@ -112,23 +112,66 @@ public class HudiAvroSerializer
     private final List<HiveColumnHandle> columnHandles;
     private final List<Type> columnTypes;
     private final Schema schema;
+    // serialize() writes page channel i at record position 
channelToFieldPosition[i]. -1 marks a
+    // hidden column: a Trino synthesized metadata column ($path, $file_size, 
$file_modified_time,
+    // $partition) with no counterpart in the data file or table schema -- its 
value is prefilled
+    // per split from PrefilledColumnValues -- so it has no record field and 
its channel is skipped.
+    private final int[] channelToFieldPosition;
 
     public HudiAvroSerializer(List<HiveColumnHandle> columnHandles, 
PrefilledColumnValues prefilledColumnValues)
     {
         this.columnHandles = columnHandles;
         this.columnTypes = 
columnHandles.stream().map(HiveColumnHandle::getType).toList();
-        // Fetches projected schema
+        // The record schema carries only columns that exist in the data: 
hidden (synthesized
+        // metadata) columns are answered from the split by 
PrefilledColumnValues, not read from
+        // the file, and cannot be described as Avro fields
         this.schema = constructSchema(columnHandles.stream().filter(ch -> 
!ch.isHidden()).map(HiveColumnHandle::getName).toList(),
                 columnHandles.stream().filter(ch -> 
!ch.isHidden()).map(HiveColumnHandle::getHiveType).toList());
         this.prefilledColumnValues = prefilledColumnValues;
+        // With hidden columns excluded from the schema, the remaining 
channels map to sequential
+        // field positions; hidden channels map to -1
+        int[] mapping = new int[columnHandles.size()];

Review Comment:
   Went with fixing it at the source -- the two-arg constructor no longer 
builds a schema (`serialize()` requires the schema-carrying one), and the 
`constructSchema` special case is reverted. The empty projection stays pinned 
by the bare `count(*)` asserts in `TestHudiMorMergeModeSemantics`.



##########
hudi-trino/src/main/java/io/trino/plugin/hudi/reader/HudiTrinoReaderContext.java:
##########
@@ -243,9 +267,8 @@ protected Option<HoodieRecordMerger> 
getRecordMerger(RecordMergeMode mergeMode,
         // Dispatch on the table's merge mode, mirroring 
HoodieAvroReaderContext. The Trino reader
         // operates on IndexedRecord, so the Avro mergers apply directly. 
Using the read-time merger
         // (combineAndGetUpdateValue) rather than a fixed preCombine merger 
keeps COMMIT_TIME_ORDERING
-        // and custom-payload tables correct on MoR reads.
-        // TODO(apache/hudi#18898): add MoR read tests for delete markers and 
custom payloads to
-        //  exercise the EVENT_TIME_ORDERING (combineAndGetUpdateValue) and 
CUSTOM branches below.
+        // and custom-payload tables correct on MoR reads; 
TestHudiMorMergeModeSemantics and
+        // TestHudiMorPayloadSemantics cover these branches end-to-end 
(apache/hudi#18898).

Review Comment:
   Narrowed the claim to the CUSTOM arm. Didn't reopen the TODO though: 
`BufferedRecordMergerFactory` picks the buffered merger from the mode alone and 
never dereferences the ordering arms' return values (only 
`getMergingStrategy()`), so there's nothing those arms could be tested against 
-- the comment now says exactly that.



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