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

zeroshade 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 5675b8cf feat(catalog): hadoop list drop rename operations (#970)
5675b8cf is described below

commit 5675b8cfeed542802ca6142ab36fec2abba0603c
Author: Tanmay Rauth <[email protected]>
AuthorDate: Thu May 7 09:17:27 2026 -0700

    feat(catalog): hadoop list drop rename operations (#970)
    
    5: ListTables + DropTable + RenameTable
    
    Implement the remaining table catalog methods. ListTables verifies the
    namespace exists then scans the namespace directory for table subdirs
    using isTableDir. DropTable verifies the table exists then calls
    os.RemoveAll on the table directory. RenameTable returns an unsupported
    error. Tests cover list empty namespace, list with tables, list
    non-existent namespace, drop existing table and verify directory is
    removed, drop non-existent table, and rename returns error.
    
    Depends on #968 #963
    Relates to #798
---
 Makefile                                  |   2 +-
 README.md                                 |  46 +++---
 catalog/hadoop/hadoop.go                  |  71 +++++++--
 catalog/hadoop/hadoop_integration_test.go | 233 ++++++++++++++++++++++++++++++
 catalog/hadoop/hadoop_test.go             | 194 ++++++++++++++++++++++++-
 internal/recipe/hadoop_validation.py      |   2 +-
 6 files changed, 513 insertions(+), 35 deletions(-)

diff --git a/Makefile b/Makefile
index 437987c2..355a4aed 100644
--- a/Makefile
+++ b/Makefile
@@ -69,4 +69,4 @@ integration-hadoop:
        go test -tags=integration -v -run="^TestHadoopIntegration" 
./catalog/hadoop/...
 
 integration-hadoop-clean:
-       rm -rf /tmp/iceberg-hadoop-warehouse/*
+       rm -rf /tmp/iceberg-hadoop-warehouse/*
\ No newline at end of file
diff --git a/README.md b/README.md
index 1f41bc34..36279345 100644
--- a/README.md
+++ b/README.md
@@ -110,29 +110,29 @@ make lint-install
 
 ### Catalog Support
 
-| Operation                   | REST | Hive |  Glue  | SQL  |
-|:----------------------------|:----:|:----:|:------:|:----:|
-| Load Table                  |  X   |  X   |   X    |  X   |
-| List Tables                 |  X   |  X   |   X    |  X   |
-| Create Table                |  X   |  X   |   X    |  X   |
-| Register Table              |  X   |  X   |   X    |      |
-| Update Current Snapshot     |  X   |  X   |   X    |  X   |
-| Create New Snapshot         |  X   |  X   |   X    |  X   |
-| Rename Table                |  X   |  X   |   X    |  X   |
-| Drop Table                  |  X   |  X   |   X    |  X   |
-| Alter Table                 |  X   |  X   |   X    |  X   |
-| Check Table Exists          |  X   |  X   |   X    |  X   |
-| Set Table Properties        |  X   |  X   |   X    |  X   |
-| List Namespaces             |  X   |  X   |   X    |  X   |
-| Create Namespace            |  X   |  X   |   X    |  X   |
-| Check Namespace Exists      |  X   |  X   |   X    |  X   |
-| Drop Namespace              |  X   |  X   |   X    |  X   |
-| Update Namespace Properties |  X   |  X   |   X    |  X   |
-| Create View                 |  X   |  X   |        |  X   |
-| Load View                   |      |  X   |        |  X   |
-| List View                   |  X   |  X   |        |  X   |
-| Drop View                   |  X   |  X   |        |  X   |
-| Check View Exists           |  X   |  X   |        |  X   |
+| Operation                   | REST | Hive |  Glue  | SQL  | Hadoop |
+|:----------------------------|:----:|:----:|:------:|:----:|:------:|
+| Load Table                  |  X   |  X   |   X    |  X   |        |
+| List Tables                 |  X   |  X   |   X    |  X   |   X    |
+| Create Table                |  X   |  X   |   X    |  X   |        |
+| Register Table              |  X   |  X   |   X    |      |        |
+| Update Current Snapshot     |  X   |  X   |   X    |  X   |        |
+| Create New Snapshot         |  X   |  X   |   X    |  X   |        |
+| Rename Table                |  X   |  X   |   X    |  X   |        |
+| Drop Table                  |  X   |  X   |   X    |  X   |   X    |
+| Alter Table                 |  X   |  X   |   X    |  X   |        |
+| Check Table Exists          |  X   |  X   |   X    |  X   |        |
+| Set Table Properties        |  X   |  X   |   X    |  X   |        |
+| List Namespaces             |  X   |  X   |   X    |  X   |   X    |
+| Create Namespace            |  X   |  X   |   X    |  X   |   X    |
+| Check Namespace Exists      |  X   |  X   |   X    |  X   |   X    |
+| Drop Namespace              |  X   |  X   |   X    |  X   |   X    |
+| Update Namespace Properties |  X   |  X   |   X    |  X   |        |
+| Create View                 |  X   |  X   |        |  X   |        |
+| Load View                   |      |  X   |        |  X   |        |
+| List View                   |  X   |  X   |        |  X   |        |
+| Drop View                   |  X   |  X   |        |  X   |        |
+| Check View Exists           |  X   |  X   |        |  X   |        |
 
 ### Read/Write Data Support
 
diff --git a/catalog/hadoop/hadoop.go b/catalog/hadoop/hadoop.go
index be656450..d3e7638f 100644
--- a/catalog/hadoop/hadoop.go
+++ b/catalog/hadoop/hadoop.go
@@ -304,22 +304,75 @@ func (c *Catalog) CommitTable(_ context.Context, _ 
table.Identifier, _ []table.R
        return nil, "", errors.New("hadoop catalog: CommitTable not yet 
implemented")
 }
 
-func (c *Catalog) ListTables(_ context.Context, _ table.Identifier) 
iter.Seq2[table.Identifier, error] {
+func (c *Catalog) ListTables(_ context.Context, ns table.Identifier) 
iter.Seq2[table.Identifier, error] {
        return func(yield func(table.Identifier, error) bool) {
-               yield(nil, errors.New("hadoop catalog: ListTables not yet 
implemented"))
+               if len(ns) == 0 {
+                       yield(nil, errors.New("hadoop catalog: namespace 
identifier must not be empty"))
+
+                       return
+               }
+
+               nsPath := c.namespaceToPath(ns)
+
+               info, err := os.Stat(nsPath)
+               if os.IsNotExist(err) || (err == nil && !info.IsDir()) {
+                       yield(nil, fmt.Errorf("%w: %s", 
catalog.ErrNoSuchNamespace, strings.Join(ns, ".")))
+
+                       return
+               }
+
+               if err != nil {
+                       yield(nil, fmt.Errorf("hadoop catalog: failed to stat 
namespace: %w", err))
+
+                       return
+               }
+
+               entries, err := os.ReadDir(nsPath)
+               if err != nil {
+                       yield(nil, fmt.Errorf("hadoop catalog: failed to read 
namespace directory: %w", err))
+
+                       return
+               }
+
+               for _, e := range entries {
+                       if !e.IsDir() {
+                               continue
+                       }
+
+                       child := filepath.Join(nsPath, e.Name())
+                       if !isTableDir(child) {
+                               continue
+                       }
+
+                       ident := make(table.Identifier, len(ns)+1)
+                       copy(ident, ns)
+                       ident[len(ns)] = e.Name()
+                       if !yield(ident, nil) {
+                               return
+                       }
+               }
        }
 }
 
-func (c *Catalog) LoadTable(_ context.Context, _ table.Identifier) 
(*table.Table, error) {
-       return nil, errors.New("hadoop catalog: LoadTable not yet implemented")
-}
+func (c *Catalog) DropTable(_ context.Context, ident table.Identifier) error {
+       if len(ident) < 2 {
+               return errors.New("hadoop catalog: table identifier must have 
at least a namespace and table name")
+       }
+
+       tablePath := c.tableToPath(ident)
+       if !isTableDir(tablePath) {
+               return fmt.Errorf("%w: %s", catalog.ErrNoSuchTable, 
strings.Join(ident, "."))
+       }
 
-func (c *Catalog) DropTable(_ context.Context, _ table.Identifier) error {
-       return errors.New("hadoop catalog: DropTable not yet implemented")
+       return os.RemoveAll(tablePath)
 }
 
 func (c *Catalog) RenameTable(_ context.Context, _, _ table.Identifier) 
(*table.Table, error) {
-       return nil, errors.New("hadoop catalog: RenameTable not yet 
implemented")
+       return nil, errors.New("hadoop catalog: rename table is not supported")
+}
+
+func (c *Catalog) LoadTable(_ context.Context, _ table.Identifier) 
(*table.Table, error) {
+       return nil, errors.New("hadoop catalog: LoadTable not yet implemented")
 }
 
 func (c *Catalog) CheckTableExists(_ context.Context, _ table.Identifier) 
(bool, error) {
@@ -462,5 +515,5 @@ func (c *Catalog) LoadNamespaceProperties(_ 
context.Context, ns table.Identifier
 }
 
 func (c *Catalog) UpdateNamespaceProperties(_ context.Context, _ 
table.Identifier, _ []string, _ iceberg.Properties) 
(catalog.PropertiesUpdateSummary, error) {
-       return catalog.PropertiesUpdateSummary{}, errors.New("hadoop catalog: 
namespace properties are not supported")
+       return catalog.PropertiesUpdateSummary{}, errors.New("hadoop catalog: 
UpdateNamespaceProperties not yet implemented")
 }
diff --git a/catalog/hadoop/hadoop_integration_test.go 
b/catalog/hadoop/hadoop_integration_test.go
new file mode 100644
index 00000000..ca97c6f2
--- /dev/null
+++ b/catalog/hadoop/hadoop_integration_test.go
@@ -0,0 +1,233 @@
+// Licensed to the Apache Software Foundation (ASF) under one
+// or more contributor license agreements.  See the NOTICE file
+// distributed with this work for additional information
+// regarding copyright ownership.  The ASF licenses this file
+// to you under the Apache License, Version 2.0 (the
+// "License"); you may not use this file except in compliance
+// with the License.  You may obtain a copy of the License at
+//
+//   http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing,
+// software distributed under the License is distributed on an
+// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+// KIND, either express or implied.  See the License for the
+// specific language governing permissions and limitations
+// under the License.
+
+//go:build integration
+
+package hadoop_test
+
+import (
+       "context"
+       "os"
+       "path/filepath"
+       "strings"
+       "testing"
+
+       "github.com/apache/iceberg-go/catalog"
+       "github.com/apache/iceberg-go/catalog/hadoop"
+       "github.com/apache/iceberg-go/internal/recipe"
+       "github.com/apache/iceberg-go/table"
+       "github.com/stretchr/testify/suite"
+       "github.com/testcontainers/testcontainers-go/modules/compose"
+)
+
+type HadoopIntegrationSuite struct {
+       suite.Suite
+
+       ctx       context.Context
+       cat       *hadoop.Catalog
+       warehouse string
+       stack     *compose.DockerCompose
+}
+
+func (s *HadoopIntegrationSuite) SetupSuite() {
+       // Use the same warehouse path as docker-compose and 
hadoop_validation.py
+       // so that both Go tests and Spark see the same directory.
+       s.warehouse = "/tmp/iceberg-hadoop-warehouse"
+       s.Require().NoError(os.MkdirAll(s.warehouse, 0o755))
+
+       var err error
+       s.stack, err = recipe.Start(s.T())
+       s.Require().NoError(err)
+}
+
+func (s *HadoopIntegrationSuite) SetupTest() {
+       s.ctx = context.Background()
+
+       cat, err := hadoop.NewCatalog("hadoop_test", s.warehouse, nil)
+       s.Require().NoError(err)
+       s.cat = cat
+}
+
+func (s *HadoopIntegrationSuite) TearDownTest() {
+       entries, _ := os.ReadDir(s.warehouse)
+       for _, e := range entries {
+               os.RemoveAll(filepath.Join(s.warehouse, e.Name()))
+       }
+}
+
+func (s *HadoopIntegrationSuite) sparkSQL(sql string) string {
+       s.T().Helper()
+
+       output, err := recipe.ExecuteSpark(s.T(), "./hadoop_validation.py", 
"--sql", sql)
+       s.Require().NoError(err)
+
+       return output
+}
+
+// createFakeTable creates a minimal table directory structure owned by
+// the runner process. This avoids root-ownership issues that arise when
+// Spark creates tables via Docker (root in container).
+func (s *HadoopIntegrationSuite) createFakeTable(ident table.Identifier) {
+       s.T().Helper()
+
+       tablePath := filepath.Join(append([]string{s.warehouse}, ident...)...)
+       metaDir := filepath.Join(tablePath, "metadata")
+       s.Require().NoError(os.MkdirAll(metaDir, 0o755))
+       s.Require().NoError(os.WriteFile(
+               filepath.Join(metaDir, "v1.metadata.json"), []byte("{}"), 
0o644))
+}
+
+// TestListTablesSparkCreated creates tables in Spark and verifies that
+// the Go Hadoop catalog can list them.
+func (s *HadoopIntegrationSuite) TestListTablesSparkCreated() {
+       s.sparkSQL("CREATE NAMESPACE IF NOT EXISTS hadoop_test.list_ns")
+       s.sparkSQL("CREATE TABLE hadoop_test.list_ns.tbl_a (id INT, name 
STRING) USING iceberg")
+       s.sparkSQL("CREATE TABLE hadoop_test.list_ns.tbl_b (id INT, name 
STRING) USING iceberg")
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(s.ctx, []string{"list_ns"}) {
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Len(tables, 2)
+
+       names := make([]string, len(tables))
+       for i, t := range tables {
+               names[i] = t[len(t)-1]
+       }
+
+       s.Contains(names, "tbl_a")
+       s.Contains(names, "tbl_b")
+}
+
+// TestListTablesEmptyNamespace creates an empty namespace via Spark and
+// verifies ListTables returns zero results.
+func (s *HadoopIntegrationSuite) TestListTablesEmptyNamespace() {
+       s.sparkSQL("CREATE NAMESPACE IF NOT EXISTS hadoop_test.empty_ns")
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(s.ctx, []string{"empty_ns"}) {
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Empty(tables)
+}
+
+// TestListTablesNoSuchNamespace verifies that listing tables for a
+// non-existent namespace returns ErrNoSuchNamespace.
+func (s *HadoopIntegrationSuite) TestListTablesNoSuchNamespace() {
+       for _, err := range s.cat.ListTables(s.ctx, []string{"does_not_exist"}) 
{
+               s.ErrorIs(err, catalog.ErrNoSuchNamespace)
+
+               break
+       }
+}
+
+// TestDropTableExisting creates a table from Go (to avoid root-ownership
+// issues with Spark-created dirs), drops it, and verifies removal.
+func (s *HadoopIntegrationSuite) TestDropTableExisting() {
+       err := s.cat.CreateNamespace(s.ctx, []string{"drop_ns"}, nil)
+       s.Require().NoError(err)
+
+       s.createFakeTable([]string{"drop_ns", "to_drop"})
+
+       tablePath := filepath.Join(s.warehouse, "drop_ns", "to_drop")
+       _, err = os.Stat(tablePath)
+       s.Require().NoError(err, "table directory should exist before drop")
+
+       err = s.cat.DropTable(s.ctx, []string{"drop_ns", "to_drop"})
+       s.Require().NoError(err)
+
+       _, err = os.Stat(tablePath)
+       s.True(os.IsNotExist(err), "table directory should be removed after 
drop")
+}
+
+// TestDropTableNotExists verifies that dropping a non-existent table
+// returns ErrNoSuchTable.
+func (s *HadoopIntegrationSuite) TestDropTableNotExists() {
+       err := s.cat.DropTable(s.ctx, []string{"no_ns", "no_tbl"})
+       s.ErrorIs(err, catalog.ErrNoSuchTable)
+}
+
+// TestDropTableNamespacePreserved verifies that dropping a table does
+// not remove the parent namespace directory.
+func (s *HadoopIntegrationSuite) TestDropTableNamespacePreserved() {
+       err := s.cat.CreateNamespace(s.ctx, []string{"preserve_ns"}, nil)
+       s.Require().NoError(err)
+
+       s.createFakeTable([]string{"preserve_ns", "tbl"})
+
+       err = s.cat.DropTable(s.ctx, []string{"preserve_ns", "tbl"})
+       s.Require().NoError(err)
+
+       info, err := os.Stat(filepath.Join(s.warehouse, "preserve_ns"))
+       s.Require().NoError(err)
+       s.True(info.IsDir(), "namespace directory should still exist")
+}
+
+// TestRenameTableUnsupported verifies that RenameTable returns an error.
+func (s *HadoopIntegrationSuite) TestRenameTableUnsupported() {
+       _, err := s.cat.RenameTable(s.ctx, []string{"ns", "old"}, 
[]string{"ns", "new"})
+       s.Require().Error(err)
+       s.True(strings.Contains(err.Error(), "not supported"))
+}
+
+// TestNamespaceRoundTrip creates a namespace in Go, creates a table in
+// Spark under it, then lists from Go.
+func (s *HadoopIntegrationSuite) TestNamespaceRoundTrip() {
+       err := s.cat.CreateNamespace(s.ctx, []string{"round_trip"}, nil)
+       s.Require().NoError(err)
+
+       s.sparkSQL("CREATE TABLE hadoop_test.round_trip.rt_tbl (id INT, val 
STRING) USING iceberg")
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(s.ctx, []string{"round_trip"}) 
{
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Len(tables, 1)
+       s.Equal("rt_tbl", tables[0][len(tables[0])-1])
+}
+
+// TestDropTableThenListReflects verifies that after dropping, the table
+// no longer appears in ListTables.
+func (s *HadoopIntegrationSuite) TestDropTableThenListReflects() {
+       err := s.cat.CreateNamespace(s.ctx, []string{"droplist_ns"}, nil)
+       s.Require().NoError(err)
+
+       s.createFakeTable([]string{"droplist_ns", "keep"})
+       s.createFakeTable([]string{"droplist_ns", "remove"})
+
+       err = s.cat.DropTable(s.ctx, []string{"droplist_ns", "remove"})
+       s.Require().NoError(err)
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(s.ctx, 
[]string{"droplist_ns"}) {
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Len(tables, 1)
+       s.Equal("keep", tables[0][len(tables[0])-1])
+}
+
+func TestHadoopIntegration(t *testing.T) {
+       suite.Run(t, new(HadoopIntegrationSuite))
+}
diff --git a/catalog/hadoop/hadoop_test.go b/catalog/hadoop/hadoop_test.go
index dbdeb27d..f9a36ca9 100644
--- a/catalog/hadoop/hadoop_test.go
+++ b/catalog/hadoop/hadoop_test.go
@@ -566,7 +566,7 @@ func (s *HadoopCatalogTestSuite) 
TestLoadNamespacePropertiesFileNotDir() {
 func (s *HadoopCatalogTestSuite) TestUpdateNamespacePropertiesUnsupported() {
        _, err := s.cat.UpdateNamespaceProperties(context.Background(), 
[]string{"ns"}, nil, nil)
        s.Require().Error(err)
-       s.Contains(err.Error(), "not supported")
+       s.Contains(err.Error(), "not yet implemented")
 }
 
 func (s *HadoopCatalogTestSuite) TestDropNamespaceWithRegularFilesOnly() {
@@ -710,3 +710,195 @@ func (s *HadoopCatalogTestSuite) 
TestIsTableDirFalseNonMatchingFiles() {
 
        s.False(isTableDir(tableDir))
 }
+
+// Helper to create a fake table directory with a metadata file.
+func (s *HadoopCatalogTestSuite) createFakeTable(ident table.Identifier) {
+       metaDir := filepath.Join(s.cat.tableToPath(ident), "metadata")
+       s.Require().NoError(os.MkdirAll(metaDir, 0o755))
+       s.Require().NoError(os.WriteFile(filepath.Join(metaDir, 
"v1.metadata.json"), []byte("{}"), 0o644))
+}
+
+// ListTables tests
+
+func (s *HadoopCatalogTestSuite) TestListTablesEmpty() {
+       ctx := context.Background()
+       s.Require().NoError(os.Mkdir(filepath.Join(s.warehouse, "ns"), 0o755))
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(ctx, []string{"ns"}) {
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Empty(tables)
+}
+
+func (s *HadoopCatalogTestSuite) TestListTablesWithTables() {
+       ctx := context.Background()
+       s.Require().NoError(os.Mkdir(filepath.Join(s.warehouse, "ns"), 0o755))
+
+       s.createFakeTable([]string{"ns", "tbl1"})
+       s.createFakeTable([]string{"ns", "tbl2"})
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(ctx, []string{"ns"}) {
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Len(tables, 2)
+       s.Contains(tables, table.Identifier{"ns", "tbl1"})
+       s.Contains(tables, table.Identifier{"ns", "tbl2"})
+}
+
+func (s *HadoopCatalogTestSuite) TestListTablesNoNamespace() {
+       ctx := context.Background()
+
+       for _, err := range s.cat.ListTables(ctx, []string{"nope"}) {
+               s.ErrorIs(err, catalog.ErrNoSuchNamespace)
+
+               break
+       }
+}
+
+func (s *HadoopCatalogTestSuite) TestListTablesEmptyIdentifier() {
+       ctx := context.Background()
+
+       for _, err := range s.cat.ListTables(ctx, []string{}) {
+               s.Require().Error(err)
+               s.Contains(err.Error(), "must not be empty")
+
+               break
+       }
+}
+
+func (s *HadoopCatalogTestSuite) TestListTablesMixedContent() {
+       // Namespace with tables, child namespaces, and regular files.
+       // Only table dirs should be returned.
+       ctx := context.Background()
+       nsDir := filepath.Join(s.warehouse, "ns")
+       s.Require().NoError(os.Mkdir(nsDir, 0o755))
+
+       s.createFakeTable([]string{"ns", "tbl1"})
+       s.Require().NoError(os.Mkdir(filepath.Join(nsDir, "child_ns"), 0o755))
+       s.Require().NoError(os.WriteFile(filepath.Join(nsDir, 
"stray_file.txt"), nil, 0o644))
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(ctx, []string{"ns"}) {
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Len(tables, 1)
+       s.Equal(table.Identifier{"ns", "tbl1"}, tables[0])
+}
+
+func (s *HadoopCatalogTestSuite) TestListTablesNestedNamespace() {
+       ctx := context.Background()
+       s.Require().NoError(os.MkdirAll(filepath.Join(s.warehouse, "a", "b"), 
0o755))
+
+       s.createFakeTable([]string{"a", "b", "tbl1"})
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(ctx, []string{"a", "b"}) {
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Len(tables, 1)
+       s.Equal(table.Identifier{"a", "b", "tbl1"}, tables[0])
+}
+
+// DropTable tests
+
+func (s *HadoopCatalogTestSuite) TestDropTable() {
+       ctx := context.Background()
+       s.Require().NoError(os.Mkdir(filepath.Join(s.warehouse, "ns"), 0o755))
+       s.createFakeTable([]string{"ns", "tbl"})
+
+       err := s.cat.DropTable(ctx, []string{"ns", "tbl"})
+       s.Require().NoError(err)
+
+       // Verify the table directory is completely removed.
+       _, statErr := os.Stat(filepath.Join(s.warehouse, "ns", "tbl"))
+       s.True(os.IsNotExist(statErr))
+}
+
+func (s *HadoopCatalogTestSuite) TestDropTableNotExists() {
+       err := s.cat.DropTable(context.Background(), []string{"ns", "tbl"})
+       s.ErrorIs(err, catalog.ErrNoSuchTable)
+}
+
+func (s *HadoopCatalogTestSuite) TestDropTableVerifyCleanup() {
+       ctx := context.Background()
+       s.Require().NoError(os.Mkdir(filepath.Join(s.warehouse, "ns"), 0o755))
+       s.createFakeTable([]string{"ns", "tbl"})
+
+       // Add a data file to confirm everything is purged.
+       dataDir := filepath.Join(s.warehouse, "ns", "tbl", "data")
+       s.Require().NoError(os.MkdirAll(dataDir, 0o755))
+       s.Require().NoError(os.WriteFile(filepath.Join(dataDir, 
"00000-0-0-00000.parquet"), []byte("data"), 0o644))
+
+       err := s.cat.DropTable(ctx, []string{"ns", "tbl"})
+       s.Require().NoError(err)
+
+       _, statErr := os.Stat(filepath.Join(s.warehouse, "ns", "tbl"))
+       s.True(os.IsNotExist(statErr))
+}
+
+func (s *HadoopCatalogTestSuite) TestDropTableShortIdentifier() {
+       err := s.cat.DropTable(context.Background(), []string{"tbl"})
+       s.Require().Error(err)
+       s.Contains(err.Error(), "at least a namespace and table name")
+}
+
+func (s *HadoopCatalogTestSuite) TestDropTableNamespacePreserved() {
+       // Dropping a table should not affect the parent namespace directory.
+       ctx := context.Background()
+       s.Require().NoError(os.Mkdir(filepath.Join(s.warehouse, "ns"), 0o755))
+       s.createFakeTable([]string{"ns", "tbl"})
+
+       s.Require().NoError(s.cat.DropTable(ctx, []string{"ns", "tbl"}))
+
+       // Namespace dir should still exist.
+       info, err := os.Stat(filepath.Join(s.warehouse, "ns"))
+       s.Require().NoError(err)
+       s.True(info.IsDir())
+}
+
+// RenameTable tests
+
+func (s *HadoopCatalogTestSuite) TestRenameTableUnsupported() {
+       _, err := s.cat.RenameTable(context.Background(), []string{"ns", 
"old"}, []string{"ns", "new"})
+       s.Require().Error(err)
+       s.Contains(err.Error(), "not supported")
+}
+
+func (s *HadoopCatalogTestSuite) TestListTablesNamespaceIsFile() {
+       ctx := context.Background()
+       s.Require().NoError(os.WriteFile(filepath.Join(s.warehouse, 
"not_a_ns"), nil, 0o644))
+
+       for _, err := range s.cat.ListTables(ctx, []string{"not_a_ns"}) {
+               s.ErrorIs(err, catalog.ErrNoSuchNamespace)
+
+               break
+       }
+}
+
+func (s *HadoopCatalogTestSuite) TestListTablesIdentifierIsolation() {
+       // Verify that yielded identifiers don't alias the namespace slice.
+       ctx := context.Background()
+       s.Require().NoError(os.Mkdir(filepath.Join(s.warehouse, "ns"), 0o755))
+       s.createFakeTable([]string{"ns", "tbl1"})
+       s.createFakeTable([]string{"ns", "tbl2"})
+
+       var tables []table.Identifier
+       for ident, err := range s.cat.ListTables(ctx, []string{"ns"}) {
+               s.Require().NoError(err)
+               tables = append(tables, ident)
+       }
+
+       s.Len(tables, 2)
+       // Each identifier should be independent — no aliasing.
+       s.NotEqual(tables[0][len(tables[0])-1], tables[1][len(tables[1])-1])
+}
diff --git a/internal/recipe/hadoop_validation.py 
b/internal/recipe/hadoop_validation.py
index b3f90a08..8b02bd24 100644
--- a/internal/recipe/hadoop_validation.py
+++ b/internal/recipe/hadoop_validation.py
@@ -55,4 +55,4 @@ if __name__ == "__main__":
         if args.assert_rows:
             runSQLAssert(args.sql)
         else:
-            runSQL(args.sql)
+            runSQL(args.sql)
\ No newline at end of file

Reply via email to