badalprasadsingh commented on code in PR #2521:
URL: https://github.com/apache/iceberg-go/pull/2521#discussion_r4577755208


##########
table/row_delta.go:
##########
@@ -465,6 +469,77 @@ func (rd *RowDelta) validateAddedDeletionVectors() error {
        return nil
 }
 
+// validateDeletesMatchDataFiles rejects an added position delete file
+// that names its data file in referenced_data_file, a deletion vector or a
+// v2 file-scoped position delete, when its partition spec or partition
+// differs from that data file's. The spec requires them to match. Scans
+// match deletion vectors by path alone, so a mismatched DV would otherwise
+// commit and be applied, and a mismatched v2 position delete would commit
+// a table whose scans fail. A reference to a data file that is neither
+// added here nor live in the current snapshot is left to the conflict
+// checks.
+func (rd *RowDelta) validateDeletesMatchDataFiles(ctx context.Context, fs 
iceio.IO, meta *MetadataBuilder) error {
+       byRef := make(map[string][]iceberg.DataFile)
+       for _, f := range rd.delFiles {
+               if f.ContentType() != iceberg.EntryContentPosDeletes {
+                       continue
+               }
+               if ref := explicitReferencedDataFile(f); ref != "" {
+                       byRef[ref] = append(byRef[ref], f)
+               }
+       }
+       if len(byRef) == 0 {
+               return nil
+       }
+
+       check := func(data iceberg.DataFile) error {
+               dels, ok := byRef[data.FilePath()]
+               if !ok {
+                       return nil
+               }
+               delete(byRef, data.FilePath())
+               for _, del := range dels {
+                       if del.SpecID() != data.SpecID() {
+                               return fmt.Errorf("delete file %s uses 
partition spec %d, but its referenced data file %s uses partition spec %d; 
write the delete file with the data file's spec",
+                                       del.FilePath(), del.SpecID(), 
data.FilePath(), data.SpecID())
+                       }
+                       if !partitionsMatch(dataFilePartition(data), 
dataFilePartition(del)) {
+                               return fmt.Errorf("delete file %s partition 
does not match its referenced data file %s",
+                                       del.FilePath(), data.FilePath())
+                       }

Review Comment:
   P1: "partitionsMatch" treats the same value in two go types as different. 
"DayTransform.Transformer": givesan int32 day, but manifests decode 
"iceberg.Date", so a valid DV is rejected; identity dates, timestamps, times 
and UUIDs too.
   
   better: can we compare "canonicalPartitionKey", as 
"validateAddedDeletionVectorTargets" does? It handles decimals, so the 
"comparablePartitionKey" change can go.



##########
table/row_delta.go:
##########
@@ -465,6 +469,77 @@ func (rd *RowDelta) validateAddedDeletionVectors() error {
        return nil
 }
 
+// validateDeletesMatchDataFiles rejects an added position delete file
+// that names its data file in referenced_data_file, a deletion vector or a
+// v2 file-scoped position delete, when its partition spec or partition
+// differs from that data file's. The spec requires them to match. Scans
+// match deletion vectors by path alone, so a mismatched DV would otherwise
+// commit and be applied, and a mismatched v2 position delete would commit
+// a table whose scans fail. A reference to a data file that is neither
+// added here nor live in the current snapshot is left to the conflict
+// checks.
+func (rd *RowDelta) validateDeletesMatchDataFiles(ctx context.Context, fs 
iceio.IO, meta *MetadataBuilder) error {
+       byRef := make(map[string][]iceberg.DataFile)
+       for _, f := range rd.delFiles {
+               if f.ContentType() != iceberg.EntryContentPosDeletes {
+                       continue
+               }
+               if ref := explicitReferencedDataFile(f); ref != "" {
+                       byRef[ref] = append(byRef[ref], f)
+               }

Review Comment:
   p1: this reads only the explicit "referenced_data_file", a v2 pos-delete 
without it, whose "file_path" bounds name one data file, is path-scoped for 
scans too, so a mismatched one still commits and scans of its data file fail
   
   better: can we use "referencedDataFilePath(f)", iceberg-ava's 
"ContentFileUtil" rule, and update the doc?



##########
table/dynamic_partition_overwrite_test.go:
##########
@@ -595,10 +595,10 @@ func 
TestDynamicPartitionOverwriteKeepsDeleteForSurvivingOldSpecData(t *testing.
        require.NotZero(t, fieldID)
        nullPartition := map[int]any{fieldID: nil}
 
-       // A v2 position delete can target an old-spec data file even if its
-       // manifest stores it under a new partition tuple with a null value.
-       posDelete := newTestPosDeleteFileForSpec(t, currentSpec, 
"file://old-spec-delete.parquet",
-               nullPartition, oldData.FilePath())
+       // A v2 position delete for an old-spec data file carries that file's
+       // spec and partition; overwriting the new spec's partitions must keep 
it.
+       posDelete := newTestPosDeleteFileForSpec(t, oldSpec, 
"file://old-spec-delete.parquet",
+               map[int]any{1000: int32(1)}, oldData.FilePath())

Review Comment:
   the old fixture is what v2 mor Delete writes after spec evolution (current 
spec, null new field). On the data file's spec, the delete is kept as another 
spec's, so this test and lin773 no longer need the 
"survivingDataPaths"/"removedDataPaths" checks
   
   better: could you keep an old-layout case via the producer?



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