This is an automated email from the ASF dual-hosted git repository.

laskoviymishka pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/iceberg-go.git


The following commit(s) were added to refs/heads/main by this push:
     new 488fb35ef fix(dv): validate metadata before writing Puffin files 
(#1589)
488fb35ef is described below

commit 488fb35efd984803439e54e92245014503e6c15e
Author: Minh Vu <[email protected]>
AuthorDate: Thu Jul 30 09:40:43 2026 +0200

    fix(dv): validate metadata before writing Puffin files (#1589)
    
    ## What changed
    
    Construct and validate all deletion-vector `DataFile` metadata before
    writing the completed Puffin object. Extend the unknown-spec regression
    test to verify that no file is created.
    
    ## Why
    
    `Flush` previously persisted the Puffin file before resolving partition
    specs and building manifest metadata. A metadata error could therefore
    return failure while leaving an unreferenced object in storage.
    
    ## Testing
    
    - `go test ./table/dv`
    
    Signed-off-by: Minh Vu <[email protected]>
---
 table/dv/dv_writer.go      | 8 ++++----
 table/dv/dv_writer_test.go | 7 ++++++-
 2 files changed, 10 insertions(+), 5 deletions(-)

diff --git a/table/dv/dv_writer.go b/table/dv/dv_writer.go
index 682b2b8c2..7bb9cae2d 100644
--- a/table/dv/dv_writer.go
+++ b/table/dv/dv_writer.go
@@ -223,10 +223,6 @@ func (w *DVWriter) Flush(_ context.Context, location 
string) ([]iceberg.DataFile
        }
 
        fileBytes := buf.Bytes()
-       if err := w.fs.WriteFile(location, fileBytes); err != nil {
-               return nil, fmt.Errorf("write DV puffin file: %w", err)
-       }
-
        fileSize := int64(len(fileBytes))
 
        dataFiles := make([]iceberg.DataFile, 0, len(results))
@@ -260,6 +256,10 @@ func (w *DVWriter) Flush(_ context.Context, location 
string) ([]iceberg.DataFile
                dataFiles = append(dataFiles, df)
        }
 
+       if err := w.fs.WriteFile(location, fileBytes); err != nil {
+               return nil, fmt.Errorf("write DV puffin file: %w", err)
+       }
+
        w.entries = make(map[string]*dvEntry)
        w.order = nil
 
diff --git a/table/dv/dv_writer_test.go b/table/dv/dv_writer_test.go
index 66aaf79a8..dca90f5b2 100644
--- a/table/dv/dv_writer_test.go
+++ b/table/dv/dv_writer_test.go
@@ -19,6 +19,7 @@ package dv
 
 import (
        "context"
+       stdfs "io/fs"
        "testing"
 
        "github.com/apache/iceberg-go"
@@ -452,13 +453,17 @@ func TestDVWriterFlushMixedSpecIDs(t *testing.T) {
 func TestDVWriterFlushUnknownSpecID(t *testing.T) {
        fs := newTestFS()
        w := NewDVWriter(fs, unpartitionedResolver())
+       location := "mem://test/unknown-spec.puffin"
 
        // specID 99 is not registered with the resolver.
        require.NoError(t, w.Add("s3://bucket/file.parquet", []int64{1}, 99, 
nil))
 
-       _, err := w.Flush(context.Background(), 
"mem://test/unknown-spec.puffin")
+       _, err := w.Flush(context.Background(), location)
        require.Error(t, err)
        assert.Contains(t, err.Error(), "unknown partition spec id 99")
+
+       _, openErr := fs.Open(location)
+       assert.ErrorIs(t, openErr, stdfs.ErrNotExist)
 }
 
 func verifyDVReadBack(t *testing.T, fs iceio.IO, df iceberg.DataFile) {

Reply via email to