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


##########
hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchemaUtils.java:
##########
@@ -520,6 +520,49 @@ public static HoodieSchema 
generateProjectionSchema(HoodieSchema originalSchema,
     return HoodieSchema.createRecord(originalSchema.getName(), 
originalSchema.getNamespace().orElse(null), 
originalSchema.getDoc().orElse(null), projectedFields);
   }
 
+  /**
+   * Generate a reader schema off the provided writeSchema, to just project 
out the provided columns.
+   *
+   * <p>This overload is intended for callers that already have a 
name-to-field map,
+   * such as the realtime reader.</p>
+   *
+   * @param writeSchema      the source schema
+   * @param schemaFieldsMap  prebuilt case-insensitive field-name map
+   * @param fieldNames       the list of field names to include in the 
projection
+   * @return new HoodieSchema containing only the specified fields
+   */
+  public static HoodieSchema generateProjectionSchema(HoodieSchema writeSchema,

Review Comment:
   **major:** This leaves two byte-identical copies of the lookup loop in one 
class: the 2-arg overload's at `:510-518` and this one at `:552-560`, same 
error string included. #19835 (a7deb61f7426) had to patch this exact 
`Locale.ROOT` lookup in both files but pinned only the 2-arg copy 
(`testGenerateProjectionSchemaIgnoresDefaultLocale`), so the realtime reader's 
path is still untested. Could the 2-arg build its map and delegate here, so 
there is one loop and that test covers both callers?
   
   <details>
   <summary>Why the isError delta does not block delegating</summary>
   
   The only behavioural difference is `writeSchema.isError()`, and it is always 
`false` at the sole caller: `AbstractRealtimeRecordReader:177` runs 
`addPartitionFields` unconditionally, which rebuilds the record through 
`appendFieldsToSchemaBase` and `createNewSchemaFromFieldsWithReference`, and 
that uses the 4-arg `createRecord`, which hard-codes `isError=false`. #19841 
asked for the flag to be carried over without knowing it is dead here. 
Delegating makes the 2-arg preserve `isError` for its other callers, which is 
inert since no Hudi table schema is an Avro error record.
   </details>



##########
hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchemaUtils.java:
##########
@@ -520,6 +520,49 @@ public static HoodieSchema 
generateProjectionSchema(HoodieSchema originalSchema,
     return HoodieSchema.createRecord(originalSchema.getName(), 
originalSchema.getNamespace().orElse(null), 
originalSchema.getDoc().orElse(null), projectedFields);
   }
 
+  /**
+   * Generate a reader schema off the provided writeSchema, to just project 
out the provided columns.
+   *
+   * <p>This overload is intended for callers that already have a 
name-to-field map,
+   * such as the realtime reader.</p>
+   *
+   * @param writeSchema      the source schema
+   * @param schemaFieldsMap  prebuilt case-insensitive field-name map

Review Comment:
   **minor:** "prebuilt case-insensitive field-name map" understates the 
contract now that this is public: the lookup at `:553` needs keys already 
lowercased with `Locale.ROOT`. Same-typed original-case maps exist in the tree 
(`SparkDataSourceTableUtils:57-60`), and passing one fails every mixed-case 
name with "Field X not found in log schema", the #19835 symptom. Could the 
`@param` spell that out? Not blocking.
   ```suggestion
      * @param schemaFieldsMap  field map keyed by names lowercased with {@code 
Locale.ROOT}, as built by
      *                         {@code 
HoodieRealtimeRecordReaderUtils#getNameToFieldMap}
   ```



##########
hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchemaUtils.java:
##########
@@ -1001,4 +1044,19 @@ private static HoodieSchema 
appendFieldsToSchemaBase(HoodieSchema schema, List<H
 
     return createNewSchemaFromFieldsWithReference(schema, fields);
   }
+
+  /**
+   * Checks if a schema field is of type timestamp_millis (timestamp-millis or 
local-timestamp-millis).
+   *
+   * @param fieldSchema The schema of the field to check
+   * @return true if the field is of type timestamp_millis, false otherwise
+   */
+  public static boolean isTimestampMillisField(HoodieSchema fieldSchema) {

Review Comment:
   **minor:** Now that this is public, two union behaviours are undocumented: 
`["null","string","timestamp-millis"]` returns `false` because 
`getNonNullType()` yields a UNION, and `["null"]` throws IAE from 
`createUnion`. Not live today (`isColumnTypeSupported` denies UNION), but 
#19834 fixed exactly this pattern in the sibling 
`HoodieSchemaRepair#hasTimestampMillisField` three days ago, so after merge the 
two siblings answer multi-branch unions differently. Could the javadoc state 
that multi-branch unions answer `false`? Not blocking.



##########
hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchemaUtils.java:
##########
@@ -520,6 +520,49 @@ public static HoodieSchema 
generateProjectionSchema(HoodieSchema originalSchema,
     return HoodieSchema.createRecord(originalSchema.getName(), 
originalSchema.getNamespace().orElse(null), 
originalSchema.getDoc().orElse(null), projectedFields);

Review Comment:
   **minor:** The javadoc for this method (line 490) still says "see 
`HoodieRealtimeRecordReaderUtils#generateProjectionSchema`" for the 
Hive-lowercasing rationale, and this PR deletes that method; the sentence came 
in with #19835 four days ago. Could it point at `{@link 
#generateProjectionSchema(HoodieSchema, Map, List)}` instead, now that the 
sibling lives in this class? Not blocking.



##########
hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchemaUtils.java:
##########
@@ -520,6 +520,49 @@ public static HoodieSchema 
generateProjectionSchema(HoodieSchema originalSchema,
     return HoodieSchema.createRecord(originalSchema.getName(), 
originalSchema.getNamespace().orElse(null), 
originalSchema.getDoc().orElse(null), projectedFields);
   }
 
+  /**

Review Comment:
   **nit:** The contract indexes #19809 added are now stale: the class-level 
"projection and pruning" bullet (line 60) and the "two sibling projection 
helpers" block on `projectSchema` (line 828) list only the 2-arg overload, and 
`isTimestampMillisField` is missing from the predicates bullet next to 
`hasDecimalField` (line 75). Could both new public methods be registered there? 
Feel free to ignore.



##########
hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchemaUtils.java:
##########
@@ -520,6 +520,49 @@ public static HoodieSchema 
generateProjectionSchema(HoodieSchema originalSchema,
     return HoodieSchema.createRecord(originalSchema.getName(), 
originalSchema.getNamespace().orElse(null), 
originalSchema.getDoc().orElse(null), projectedFields);
   }
 
+  /**
+   * Generate a reader schema off the provided writeSchema, to just project 
out the provided columns.
+   *
+   * <p>This overload is intended for callers that already have a 
name-to-field map,
+   * such as the realtime reader.</p>
+   *
+   * @param writeSchema      the source schema
+   * @param schemaFieldsMap  prebuilt case-insensitive field-name map
+   * @param fieldNames       the list of field names to include in the 
projection
+   * @return new HoodieSchema containing only the specified fields
+   */
+  public static HoodieSchema generateProjectionSchema(HoodieSchema writeSchema,
+                                                      Map<String, 
HoodieSchemaField> schemaFieldsMap,
+                                                      List<String> fieldNames) 
{
+    ValidationUtils.checkArgument(writeSchema != null, "Write schema cannot be 
null");
+    ValidationUtils.checkArgument(schemaFieldsMap != null, "Schema fields map 
cannot be null");
+    ValidationUtils.checkArgument(fieldNames != null, "Field names cannot be 
null");
+
+    /*
+     * Avro & Presto field names seems to be case sensitive (support fields 
differing only in case) whereas
+     * Hive/Impala/SparkSQL(default) are case-insensitive. Spark allows this 
to be configurable using
+     * spark.sql.caseSensitive=true
+     *
+     * For a RT table setup with no delta-files (for a latest file-slice) -> 
we translate parquet schema to Avro Here
+     * the field-name case is dependent on parquet schema. Hive (1.x/2.x/CDH) 
translate column projections to
+     * lower-cases
+     *
+     */
+    List<HoodieSchemaField> projectedFields = new 
ArrayList<>(fieldNames.size());
+    for (String fn : fieldNames) {
+      HoodieSchemaField field = 
schemaFieldsMap.get(fn.toLowerCase(Locale.ROOT));
+      if (field == null) {
+        throw new HoodieException("Field " + fn + " not found in log schema. 
Query cannot proceed! "
+                + "Derived Schema Fields: " + new 
ArrayList<>(schemaFieldsMap.keySet()));

Review Comment:
   **nit:** Continuation lines here and at `:563` indent 16/12 spaces where the 
sibling overload uses 12, and the javadoc drops the `@since` and `@throws` tags 
the 2-arg carries. Checkstyle passes, so purely cosmetic. Feel free to ignore.



##########
hudi-common/src/test/java/org/apache/hudi/common/schema/TestHoodieSchemaUtils.java:
##########
@@ -2370,4 +2370,32 @@ public void 
testCreateDeleteLogSchemaWithUnknownOrderingField() {
         () -> HoodieSchemaUtils.createDeleteLogSchema(tableSchema, 
Collections.singletonList("not_a_field")));
     assertEquals("Ordering field not_a_field not found in table schema", 
exception.getMessage());
   }
+
+  @Test
+  void testIsTimestampMillisField() {

Review Comment:
   **minor:** The javadoc on `isTimestampMillisField` names 
local-timestamp-millis, but no assertion here exercises it. The claim holds 
(`createLocalTimestampMillis()` builds a `HoodieSchema.Timestamp`), so it is a 
free assertion. Could we add `createLocalTimestampMillis()` expecting `true` 
and `createLocalTimestampMicros()` expecting `false`? Not blocking.



##########
hudi-common/src/main/java/org/apache/hudi/common/schema/HoodieSchemaRepair.java:
##########
@@ -39,8 +39,8 @@
  * {@code 
org.apache.hudi.common.schema.internal.utils.AvroSchemaEvolutionUtils}.</p>
  *
  * <p>{@link #hasTimestampMillisField(HoodieSchema)} is the cheap pre-check 
used to decide whether the
- * repair is worth wiring in at all. Its sibling in the metadata-table domain 
is
- * {@code HoodieTableMetadataUtil#isTimestampMillisField}, which answers the 
same question for one field
+ * repair is worth wiring in at all. Its per-field sibling is
+ * {@code HoodieSchemaUtils#isTimestampMillisField}, which answers the same 
question for one field

Review Comment:
   **nit:** The PR body still describes the earlier direction: "Reuse 
`HoodieTableMetadataUtil.isTimestampMillisField` in `HoodieSchemaRepair`" and 
"make the timestamp-millis predicate accessible across the relevant packages", 
which is what 9cb20ca8 reversed. Since the body is what a committer reads at 
merge time, could it say the predicate moved into `HoodieSchemaUtils` with both 
callers delegating? Feel free to ignore.



##########
hudi-hadoop-mr/src/main/java/org/apache/hudi/hadoop/realtime/AbstractRealtimeRecordReader.java:
##########
@@ -183,7 +183,7 @@ private void init() throws Exception {
     // TODO(vc): In the future, the reader schema should be updated based on 
log files & be able
     // to null out fields not present before
 
-    readerSchema = 
HoodieRealtimeRecordReaderUtils.generateProjectionSchema(writerSchema, 
schemaFieldsMap, projectionFields);
+    readerSchema = HoodieSchemaUtils.generateProjectionSchema(writerSchema, 
schemaFieldsMap, projectionFields);

Review Comment:
   **nit:** `constructHiveOrderedSchema` just below is the third twin of this 
loop (both were born in 12523c379fe9, HUDI-298): same lowercased-map lookup, 
same `createNewSchemaField`, same 5-arg `createRecord`, differing only in 
skipping Hive virtual columns instead of throwing. Leaving it is reasonable 
given the skip semantics; is it deliberately out of #19841's scope? Feel free 
to ignore.



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