zeroshade commented on code in PR #1937:
URL: https://github.com/apache/iceberg-go/pull/1937#discussion_r3936147396


##########
table/arrow_scanner.go:
##########
@@ -661,6 +704,121 @@ func readDeletes(ctx context.Context, fs iceio.IO, 
dataFile iceberg.DataFile) (_
        return acc.finish(), nil
 }
 
+func newPositionDeleteRowGroupTester(schema *arrow.Schema, targets 
map[string]struct{}) (*tblutils.ParquetRowGroupTester, error) {
+       if len(targets) == 0 || len(targets) > inPredicateLimit {
+               return nil, nil

Review Comment:
   **minor** — Field-ID validation is skipped by the early return, so the same 
corrupt file errors or reads depending on query shape
   
   newPositionDeleteRowGroupTester returns (nil, nil) before calling 
positionDeletePruningEnabled whenever len(targets)==0 or 
len(targets)>inPredicateLimit. A delete file with swapped or duplicated 
reserved field IDs therefore fails with ErrInvalidSchema when a query touches 
1..200 data files, but reads successfully when it touches >200, or when any 
task lacks a usable path and the nil whole-file fallback kicks in. Neither path 
is unsafe (validation only gates pushdown, and the unvalidated paths do no 
pruning), so this is a consistency/support concern rather than a correctness 
one — but a scan that fails only for some query shapes is hard to diagnose. 
Consider validating unconditionally, or documenting that validation is 
deliberately scoped to the pushdown path.



##########
table/arrow_scanner.go:
##########
@@ -79,16 +80,35 @@ func releasePerFilePosDeletes(deletesPerFile 
perFilePosDeletes) {
 func readAllDeleteFiles(ctx context.Context, fs iceio.IO, tasks 
[]FileScanTask, concurrency int) (perFilePosDeletes, error) {
        deletesPerFile := make(perFilePosDeletes)
        uniqueDeletes := make(map[string]iceberg.DataFile)
+       targetsByDelete := make(perDeleteFileTargets)
 
        for _, t := range tasks {
                for _, d := range t.DeleteFiles {
                        if d.ContentType() != iceberg.EntryContentPosDeletes {
                                continue
                        }
 
-                       if _, ok := uniqueDeletes[d.FilePath()]; !ok {
-                               uniqueDeletes[d.FilePath()] = d
+                       deletePath := d.FilePath()
+                       if _, ok := uniqueDeletes[deletePath]; !ok {
+                               uniqueDeletes[deletePath] = d
                        }
+
+                       targets, ok := targetsByDelete[deletePath]
+                       if !ok {
+                               targets = make(map[string]struct{})
+                               targetsByDelete[deletePath] = targets
+                       }
+                       // A nil target set means that at least one task did 
not carry a
+                       // usable data-file path. Keep the old whole-file read 
in that case.
+                       if targets == nil {
+                               continue
+                       }
+                       if t.File == nil || t.File.FilePath() == "" {
+                               targetsByDelete[deletePath] = nil
+
+                               continue
+                       }
+                       targets[t.File.FilePath()] = struct{}{}
                }

Review Comment:
   **minor** — Filtering also drops deletes from a delete file not assigned to 
the task — a semantic change, not just perf
   
   The target set is built only from tasks that reference a given delete file, 
so rows in that file addressing a data file whose task did not list it are now 
discarded. Previously the whole file was read and those rows landed in 
deletesPerFile, where the other task's lookup would apply them. I believe the 
new behaviour is the spec-correct one (delete-to-data assignment is the 
planner's job, and applying an unassigned delete is over-deletion), and I 
confirmed no under-deletion is possible because targets always contains 
t.File.FilePath() for every task that references the file. Still, the PR is 
framed as pure perf with an explicit 'keeps the existing result shape' claim, 
and this changes observable output. Worth a sentence in the PR body and a 
regression test pinning the intended semantics.



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