zhuxiangyi commented on code in PR #8334:
URL: https://github.com/apache/paimon/pull/8334#discussion_r3467987768


##########
paimon-flink/paimon-flink-common/src/main/java/org/apache/paimon/flink/action/DataEvolutionMergeIntoAction.java:
##########
@@ -526,7 +671,17 @@ private void checkSchema(Table source) {
                                         .getTypeRoot()
                                         .getFamilies()
                                         
.contains(DataTypeFamily.BINARY_STRING);
+                // For sub-field-level data evolution, a struct column's 
source value is a partial
+                // (subset) ROW carrying only the updated sub-fields, which is 
not directly
+                // cast-compatible with the full target struct. Accept it when 
every source
+                // sub-field exists in the target struct with a compatible 
cast.
+                boolean partialStructCompatible =

Review Comment:
   Agreed — fixed in ef144accb. The partial-struct check is now gated on 
`isSubFieldWrite(column)` (i.e. the column actually has dotted write paths like 
`nest.a`). Whole-column assignments such as `T.nest=S.nest` stay on the 
full-type compatibility check, so a narrower source struct is rejected instead 
of being written as an incomplete whole-struct file.



##########
paimon-api/src/main/java/org/apache/paimon/types/RowType.java:
##########
@@ -312,6 +312,144 @@ public RowType project(String... names) {
         return project(Arrays.asList(names));
     }
 
+    /**
+     * Project this row type by a list of (possibly nested) dotted paths, e.g. 
{@code ["f0",
+     * "nest.a"]}. A path without a dot selects the whole top-level field 
(same as {@link
+     * #project(List)}); a dotted path selects only the addressed sub-field of 
a nested {@link
+     * RowType}, preserving field ids and nullability of every level. Schema 
field order is
+     * preserved. This is used by data evolution to reconstruct the partial 
nested schema of a
+     * column-group file from its {@code writeCols}.
+     */
+    public RowType projectByPaths(List<String> paths) {
+        return projectTypeByPaths(this, paths);
+    }
+
+    private static RowType projectTypeByPaths(RowType type, List<String> 
paths) {
+        // group paths by their immediate child name; a child appearing 
without a tail (or also with
+        // a tail) is selected as a whole field
+        Map<String, List<String>> childToSubPaths = new HashMap<>();
+        Set<String> wholeChildren = new HashSet<>();
+        Set<String> fieldNames = new HashSet<>();
+        for (DataField field : type.getFields()) {
+            fieldNames.add(field.name());
+        }
+        for (String path : paths) {
+            int dot = path.indexOf('.');
+            // Prefer an exact field-name match so a column whose name itself 
contains a dot (and
+            // any
+            // plain top-level name) is selected whole; only split into 
head.tail for genuine nested
+            // sub-field paths that do not name a field directly. This keeps 
backward compatibility
+            // with the legacy exact-name project(List).
+            if (dot < 0 || fieldNames.contains(path)) {
+                childToSubPaths.computeIfAbsent(path, k -> new ArrayList<>());
+                wholeChildren.add(path);
+            } else {
+                String head = path.substring(0, dot);
+                String tail = path.substring(dot + 1);
+                childToSubPaths.computeIfAbsent(head, k -> new 
ArrayList<>()).add(tail);
+            }
+        }
+
+        Set<String> matched = new HashSet<>();
+        List<DataField> result = new ArrayList<>();
+        for (DataField field : type.getFields()) {
+            List<String> subPaths = childToSubPaths.get(field.name());
+            if (subPaths == null) {
+                continue;
+            }
+            matched.add(field.name());
+            if (wholeChildren.contains(field.name())
+                    || subPaths.isEmpty()
+                    || !(field.type() instanceof RowType)) {

Review Comment:
   Good point — fixed in ef144accb. `projectByPaths` now throws 
`IllegalArgumentException` when a dotted path's head field is not a ROW (e.g. 
`id.a`), instead of silently returning the whole `id`. Exact top-level matches 
(including column names that themselves contain a dot) are still selected 
whole. Added coverage in `DataTypesTest#testProjectByPaths`.



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