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 6693e2f3b refactor(catalog)!: rename PurgeableTable to 
PurgeableCatalog (#2305)
6693e2f3b is described below

commit 6693e2f3b4896a2edef68abbbdec356d184875c4
Author: Minh Vu <[email protected]>
AuthorDate: Sun Oct 25 19:05:56 2026 +0100

    refactor(catalog)!: rename PurgeableTable to PurgeableCatalog (#2305)
---
 catalog/catalog.go                              | 15 +++++++--------
 catalog/example_multi_table_transaction_test.go |  2 ++
 catalog/glue/glue.go                            |  2 +-
 catalog/hadoop/hadoop.go                        |  2 +-
 catalog/hive/hive.go                            |  2 +-
 catalog/multi_table_transaction_test.go         |  2 ++
 catalog/rest/rest.go                            |  2 +-
 catalog/sql/sql.go                              |  2 +-
 catalog/sql/sql_test.go                         | 11 ++---------
 cmd/iceberg/drop_test.go                        |  4 ++++
 cmd/iceberg/main.go                             | 10 +---------
 cmd/iceberg/properties_test.go                  |  4 ++++
 cmd/iceberg/upgrade_rollback_test.go            |  4 ++++
 website/src/releases.md                         |  1 +
 14 files changed, 32 insertions(+), 31 deletions(-)

diff --git a/catalog/catalog.go b/catalog/catalog.go
index d5bda95dc..6bd810d2b 100644
--- a/catalog/catalog.go
+++ b/catalog/catalog.go
@@ -121,6 +121,9 @@ func NewCreateViewCfg() CreateViewCfg {
 type Catalog interface {
        io.Closer
 
+       // PurgeableCatalog provides the mandatory physical table deletion API.
+       PurgeableCatalog
+
        // CatalogType returns the type of the catalog.
        CatalogType() Type
 
@@ -180,13 +183,9 @@ type TransactionalCatalog interface {
        CommitTransaction(ctx context.Context, commits []table.TableCommit) 
error
 }
 
-// PurgeableTable is an optional interface that catalogs can implement
-// to support physical table deletion (catalog entry + underlying files).
-// Callers should check for this capability via a type assertion:
-//
-//     if purger, ok := cat.(catalog.PurgeableTable); ok {
-//         err := purger.PurgeTable(ctx, ident)
-//     }
+// PurgeableCatalog is the small table purge interface embedded by Catalog.
+// Every Catalog must support PurgeTable, which removes the catalog entry and
+// performs best-effort physical file deletion.
 //
 // For REST catalogs the purge is delegated server-side. For client-side
 // catalogs (SQL, Glue, Hive, Hadoop) the table is first dropped from
@@ -199,7 +198,7 @@ type TransactionalCatalog interface {
 // Any file-deletion, directory walk, or listing errors encountered are logged
 // as warnings, and the operation succeeds (returns nil) so that the catalog 
drop
 // remains the single source of truth.
-type PurgeableTable interface {
+type PurgeableCatalog interface {
        PurgeTable(ctx context.Context, identifier table.Identifier) error
 }
 
diff --git a/catalog/example_multi_table_transaction_test.go 
b/catalog/example_multi_table_transaction_test.go
index 5a8614b58..7188d87a1 100644
--- a/catalog/example_multi_table_transaction_test.go
+++ b/catalog/example_multi_table_transaction_test.go
@@ -64,6 +64,8 @@ func (c *exampleCatalog) CommitTable(context.Context, 
table.Identifier, []table.
 
 func (c *exampleCatalog) DropTable(context.Context, table.Identifier) error { 
return nil }
 
+func (c *exampleCatalog) PurgeTable(context.Context, table.Identifier) error { 
return nil }
+
 func (c *exampleCatalog) RenameTable(context.Context, table.Identifier, 
table.Identifier) (*table.Table, error) {
        return nil, nil
 }
diff --git a/catalog/glue/glue.go b/catalog/glue/glue.go
index 5341340cd..9e70b0153 100644
--- a/catalog/glue/glue.go
+++ b/catalog/glue/glue.go
@@ -154,7 +154,7 @@ type glueAPI interface {
        UpdateDatabase(ctx context.Context, params *glue.UpdateDatabaseInput, 
optFns ...func(*glue.Options)) (*glue.UpdateDatabaseOutput, error)
 }
 
-var _ catalog.PurgeableTable = (*Catalog)(nil)
+var _ catalog.Catalog = (*Catalog)(nil)
 
 type Catalog struct {
        glueSvc   glueAPI
diff --git a/catalog/hadoop/hadoop.go b/catalog/hadoop/hadoop.go
index fc277687a..822126bf1 100644
--- a/catalog/hadoop/hadoop.go
+++ b/catalog/hadoop/hadoop.go
@@ -184,7 +184,7 @@ func validateIdentifierParts(ident table.Identifier, 
identifierName, componentNa
 // Catalog is a filesystem-based Iceberg catalog that requires no external
 // metastore. All state lives on disk as directories and versioned JSON
 // metadata files. Currently only local filesystem paths are supported.
-var _ catalog.PurgeableTable = (*Catalog)(nil)
+var _ catalog.Catalog = (*Catalog)(nil)
 
 type Catalog struct {
        name       string
diff --git a/catalog/hive/hive.go b/catalog/hive/hive.go
index d460c2191..3354affa0 100644
--- a/catalog/hive/hive.go
+++ b/catalog/hive/hive.go
@@ -51,7 +51,7 @@ func init() {
        }))
 }
 
-var _ catalog.PurgeableTable = (*Catalog)(nil)
+var _ catalog.Catalog = (*Catalog)(nil)
 
 type Catalog struct {
        client HiveClient
diff --git a/catalog/multi_table_transaction_test.go 
b/catalog/multi_table_transaction_test.go
index 88ddc42ec..03c3be089 100644
--- a/catalog/multi_table_transaction_test.go
+++ b/catalog/multi_table_transaction_test.go
@@ -81,6 +81,8 @@ func (s *fullStubCatalog) CommitTable(context.Context, 
table.Identifier, []table
 
 func (s *fullStubCatalog) DropTable(context.Context, table.Identifier) error { 
return nil }
 
+func (s *fullStubCatalog) PurgeTable(context.Context, table.Identifier) error 
{ return nil }
+
 func (s *fullStubCatalog) RenameTable(context.Context, table.Identifier, 
table.Identifier) (*table.Table, error) {
        return nil, nil
 }
diff --git a/catalog/rest/rest.go b/catalog/rest/rest.go
index 7e2eb7602..933392372 100644
--- a/catalog/rest/rest.go
+++ b/catalog/rest/rest.go
@@ -897,7 +897,7 @@ func toProps(o *options) iceberg.Properties {
        return props
 }
 
-var _ catalog.PurgeableTable = (*Catalog)(nil)
+var _ catalog.Catalog = (*Catalog)(nil)
 
 type Catalog struct {
        baseURI *url.URL
diff --git a/catalog/sql/sql.go b/catalog/sql/sql.go
index 1b1e216b0..6eaaef4db 100644
--- a/catalog/sql/sql.go
+++ b/catalog/sql/sql.go
@@ -382,7 +382,7 @@ func isRetryableSerializableError(err error) bool {
                strings.Contains(msg, "ORA-00060")
 }
 
-var _ catalog.PurgeableTable = (*Catalog)(nil)
+var _ catalog.Catalog = (*Catalog)(nil)
 
 type Catalog struct {
        db            *bun.DB
diff --git a/catalog/sql/sql_test.go b/catalog/sql/sql_test.go
index 889c9609d..c2250d7c7 100644
--- a/catalog/sql/sql_test.go
+++ b/catalog/sql/sql_test.go
@@ -1565,11 +1565,7 @@ func (s *SqliteCatalogTestSuite) TestPurgeTable() {
                s.Require().NoError(err)
                s.Require().NoError(os.WriteFile(metaLoc, newMetaBytes, 0o644))
 
-               // Assert that the catalog implements PurgeableTable
-               purger, ok := any(tt.cat).(catalog.PurgeableTable)
-               s.Require().True(ok, "catalog must implement PurgeableTable")
-
-               s.NoError(purger.PurgeTable(context.Background(), tt.tblID))
+               s.NoError(tt.cat.PurgeTable(context.Background(), tt.tblID))
 
                // The table catalog entry should be gone
                _, err = tt.cat.LoadTable(context.Background(), tt.tblID)
@@ -1640,10 +1636,7 @@ func (s *SqliteCatalogTestSuite) 
TestPurgeTableGCDisabled() {
                metaLoc := strings.TrimPrefix(tbl.MetadataLocation(), "file://")
                s.FileExists(metaLoc)
 
-               purger, ok := any(tt.cat).(catalog.PurgeableTable)
-               s.Require().True(ok, "catalog must implement PurgeableTable")
-
-               s.NoError(purger.PurgeTable(context.Background(), tt.tblID))
+               s.NoError(tt.cat.PurgeTable(context.Background(), tt.tblID))
 
                // The table catalog entry should be gone
                _, err = tt.cat.LoadTable(context.Background(), tt.tblID)
diff --git a/cmd/iceberg/drop_test.go b/cmd/iceberg/drop_test.go
index ca4723c1b..aa141be4b 100644
--- a/cmd/iceberg/drop_test.go
+++ b/cmd/iceberg/drop_test.go
@@ -70,6 +70,10 @@ func (m *mockCatalogForDrop) DropTable(ctx context.Context, 
identifier table.Ide
        return m.dropErr
 }
 
+func (m *mockCatalogForDrop) PurgeTable(context.Context, table.Identifier) 
error {
+       return errors.New("catalog does not support purge")
+}
+
 func (m *mockCatalogForDrop) RenameTable(ctx context.Context, from, to 
table.Identifier) (*table.Table, error) {
        return nil, nil
 }
diff --git a/cmd/iceberg/main.go b/cmd/iceberg/main.go
index 0e6ac1bc1..47adb4a34 100644
--- a/cmd/iceberg/main.go
+++ b/cmd/iceberg/main.go
@@ -615,15 +615,7 @@ func runDrop(ctx context.Context, output Output, cat 
catalog.Catalog, cmd *DropC
                ident := catalog.ToIdentifier(cmd.Table.Identifier)
 
                if cmd.Table.Purge {
-                       purger, ok := cat.(catalog.PurgeableTable)
-                       if !ok {
-                               output.Error(fmt.Errorf("catalog %s does not 
support purge", cat.CatalogType()))
-                               osExit(1)
-
-                               return
-                       }
-
-                       if err := purger.PurgeTable(ctx, ident); err != nil {
+                       if err := cat.PurgeTable(ctx, ident); err != nil {
                                output.Error(err)
                                osExit(1)
 
diff --git a/cmd/iceberg/properties_test.go b/cmd/iceberg/properties_test.go
index ebaec58f2..fb812fb90 100644
--- a/cmd/iceberg/properties_test.go
+++ b/cmd/iceberg/properties_test.go
@@ -65,6 +65,10 @@ func (m *mockCatalogForProperties) 
DropTable(context.Context, table.Identifier)
        panic("DropTable must not be called")
 }
 
+func (m *mockCatalogForProperties) PurgeTable(context.Context, 
table.Identifier) error {
+       panic("PurgeTable must not be called")
+}
+
 func (m *mockCatalogForProperties) RenameTable(context.Context, 
table.Identifier, table.Identifier) (*table.Table, error) {
        panic("RenameTable must not be called")
 }
diff --git a/cmd/iceberg/upgrade_rollback_test.go 
b/cmd/iceberg/upgrade_rollback_test.go
index 0774c7f28..2dec3923a 100644
--- a/cmd/iceberg/upgrade_rollback_test.go
+++ b/cmd/iceberg/upgrade_rollback_test.go
@@ -431,6 +431,10 @@ func (p *panicCatalog) DropTable(context.Context, 
table.Identifier) error {
        panic("DropTable must not be called")
 }
 
+func (p *panicCatalog) PurgeTable(context.Context, table.Identifier) error {
+       panic("PurgeTable must not be called")
+}
+
 func (p *panicCatalog) RenameTable(context.Context, table.Identifier, 
table.Identifier) (*table.Table, error) {
        panic("RenameTable must not be called")
 }
diff --git a/website/src/releases.md b/website/src/releases.md
index e0d398b74..b83a6a6ef 100644
--- a/website/src/releases.md
+++ b/website/src/releases.md
@@ -42,6 +42,7 @@ Per-release notes (highlights, breaking changes, 
contributors) are published on
 
 ### Unreleased compatibility notes
 
+- **Table purge is required on catalogs.** The `catalog.Catalog` interface now 
embeds `catalog.PurgeableCatalog`; every custom catalog implementation must 
provide `PurgeTable(ctx, identifier) error`. The narrow 
`catalog.PurgeableTable` interface is renamed to `catalog.PurgeableCatalog`. 
Built-in purge behavior is unchanged.
 - **Unify statistics blob types with Puffin.** Replace `table.BlobType` with 
`puffin.BlobType` when constructing `table.BlobMetadata`. Replace 
`table.BlobTypeApacheDatasketchesThetaV1` with 
`puffin.BlobTypeDataSketchesTheta` and `table.BlobTypeDeletionVectorV1` with 
`puffin.BlobTypeDeletionVector`. Use `puffin.BlobType.IsKnown()` to identify 
the types defined by the Puffin specification; the redundant 
`table.BlobType.IsValid()` method is removed. Unknown non-empty blob type names 
still r [...]
 - **View representation field uses the Go SQL initialism.** Replace 
`view.Representation.Sql` with `view.Representation.SQL`. Serialized view 
metadata still uses the `sql` JSON field.
 - **Orphan cleanup results now expose only `OrphanFiles`.** Replace 
`result.OrphanFileLocations` with `result.OrphanFiles` (each entry has `Path` 
and `SizeBytes`). `OrphanCleanupPlan.Files()` still provides path-only lists 
during planning.

Reply via email to