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 efcbcaf39 fix(hive): propagate object-loading failures during listing 
(#1572)
efcbcaf39 is described below

commit efcbcaf3983b5096d7e2fd7668d19d7b5d5f6309
Author: Minh Vu <[email protected]>
AuthorDate: Wed Aug 5 12:31:55 2026 +0200

    fix(hive): propagate object-loading failures during listing (#1572)
    
    * fix(hive): propagate object-loading failures during listing
    
    Signed-off-by: Minh Vu <[email protected]>
    
    * test(hive): verify listing error context
    
    Signed-off-by: Minh Vu <[email protected]>
    
    * fix(hive): narrow missing object classification
    
    * test(hive): remove redundant listing assertion
    
    * style(hive): satisfy nlreturn in listing tests
    
    ---------
    
    Signed-off-by: Minh Vu <[email protected]>
---
 catalog/hive/hive.go      |  26 ++++++++----
 catalog/hive/hive_test.go | 102 +++++++++++++++++++++++++++++++++++++++++++++-
 2 files changed, 120 insertions(+), 8 deletions(-)

diff --git a/catalog/hive/hive.go b/catalog/hive/hive.go
index 1c916a3cc..f5e79474a 100644
--- a/catalog/hive/hive.go
+++ b/catalog/hive/hive.go
@@ -150,7 +150,13 @@ func (c *Catalog) ListTables(ctx context.Context, 
namespace table.Identifier) it
                for _, tableName := range tableNames {
                        tbl, err := c.client.GetTable(ctx, database, tableName)
                        if err != nil {
-                               continue
+                               if isNoSuchObjectError(err) {
+                                       continue
+                               }
+
+                               yield(table.Identifier{}, fmt.Errorf("failed to 
load table %s.%s while listing: %w", database, tableName, err))
+
+                               return
                        }
                        if isIcebergTable(tbl) {
                                if !yield(TableIdentifier(database, tableName), 
nil) {
@@ -675,8 +681,13 @@ func (c *Catalog) ListViews(ctx context.Context, namespace 
table.Identifier) ite
                for _, viewName := range viewNames {
                        tbl, err := c.client.GetTable(ctx, database, viewName)
                        if err != nil {
-                               // Skip tables we fail to load, mirroring table 
listing behavior.
-                               continue
+                               if isNoSuchObjectError(err) {
+                                       continue
+                               }
+
+                               yield(table.Identifier{}, fmt.Errorf("failed to 
load view %s.%s while listing: %w", database, viewName, err))
+
+                               return
                        }
                        if isIcebergView(tbl) {
                                if !yield(TableIdentifier(database, viewName), 
nil) {
@@ -1027,11 +1038,12 @@ func isNoSuchObjectError(err error) bool {
                return false
        }
 
-       errStr := err.Error()
+       var noSuchObjectErr *hive_metastore.NoSuchObjectException
+       if errors.As(err, &noSuchObjectErr) {
+               return true
+       }
 
-       return strings.Contains(errStr, "NoSuchObjectException") ||
-               strings.Contains(errStr, "not found") ||
-               strings.Contains(errStr, "does not exist")
+       return strings.Contains(err.Error(), "NoSuchObjectException")
 }
 
 func isAlreadyExistsError(err error) bool {
diff --git a/catalog/hive/hive_test.go b/catalog/hive/hive_test.go
index 734feeaa8..f4f3095de 100644
--- a/catalog/hive/hive_test.go
+++ b/catalog/hive/hive_test.go
@@ -205,7 +205,7 @@ var testIcebergHiveView = &hive_metastore.Table{
 
 // Error helpers for mocking
 var (
-       errNoSuchObject     = errors.New("NoSuchObjectException: object not 
found")
+       errNoSuchObject     = &hive_metastore.NoSuchObjectException{}
        errAlreadyExists    = errors.New("AlreadyExistsException: object 
already exists")
        errInvalidOperation = errors.New("InvalidOperationException: Database 
is not empty")
 )
@@ -280,6 +280,65 @@ func TestHiveListTablesEmpty(t *testing.T) {
        mockClient.AssertExpectations(t)
 }
 
+func TestHiveListTablesHandlesObjectLoadingErrors(t *testing.T) {
+       t.Run("skips concurrently deleted tables", func(t *testing.T) {
+               mockClient := &mockHiveClient{}
+               mockClient.On("GetTables", mock.Anything, "test_database", 
"*").Return([]string{"deleted", "test_table"}, nil).Once()
+               mockClient.On("GetTable", mock.Anything, "test_database", 
"deleted").Return(nil, errNoSuchObject).Once()
+               mockClient.On("GetTable", mock.Anything, "test_database", 
"test_table").Return(testIcebergHiveTable1, nil).Once()
+
+               var tables []table.Identifier
+               for identifier, err := range NewCatalogWithClient(mockClient, 
iceberg.Properties{}).ListTables(context.Background(), 
DatabaseIdentifier("test_database")) {
+                       require.NoError(t, err)
+                       tables = append(tables, identifier)
+               }
+               require.Equal(t, 
[]table.Identifier{TableIdentifier("test_database", "test_table")}, tables)
+               mockClient.AssertExpectations(t)
+       })
+
+       t.Run("propagates operational failures", func(t *testing.T) {
+               loadErr := errors.New("metastore host not found")
+               mockClient := &mockHiveClient{}
+               mockClient.On("GetTables", mock.Anything, "test_database", 
"*").Return([]string{"deleted", "broken", "test_table"}, nil).Once()
+               mockClient.On("GetTable", mock.Anything, "test_database", 
"deleted").Return(nil, errNoSuchObject).Once()
+               mockClient.On("GetTable", mock.Anything, "test_database", 
"broken").Return(nil, loadErr).Once()
+
+               var gotErr error
+               for identifier, err := range NewCatalogWithClient(mockClient, 
iceberg.Properties{}).ListTables(context.Background(), 
DatabaseIdentifier("test_database")) {
+                       if err != nil {
+                               require.Equal(t, table.Identifier{}, identifier)
+                               gotErr = err
+
+                               break
+                       }
+               }
+               require.ErrorIs(t, gotErr, loadErr)
+               require.ErrorContains(t, gotErr, "failed to load table 
test_database.broken while listing")
+               mockClient.AssertNumberOfCalls(t, "GetTable", 2)
+               mockClient.AssertExpectations(t)
+       })
+}
+
+func TestIsNoSuchObjectError(t *testing.T) {
+       t.Run("recognizes typed thrift exception", func(t *testing.T) {
+               require.True(t, 
isNoSuchObjectError(&hive_metastore.NoSuchObjectException{}))
+       })
+
+       t.Run("recognizes wrapped thrift exception", func(t *testing.T) {
+               err := errors.Join(errors.New("failed to load object"), 
&hive_metastore.NoSuchObjectException{})
+
+               require.True(t, isNoSuchObjectError(err))
+       })
+
+       t.Run("recognizes narrow exception name fallback", func(t *testing.T) {
+               require.True(t, 
isNoSuchObjectError(errors.New("NoSuchObjectException: object disappeared")))
+       })
+
+       t.Run("does not classify operational not-found message", func(t 
*testing.T) {
+               require.False(t, isNoSuchObjectError(errors.New("metastore host 
not found")))
+       })
+}
+
 func TestHiveListNamespaces(t *testing.T) {
        assert := require.New(t)
 
@@ -1477,6 +1536,47 @@ func TestHiveListViews(t *testing.T) {
        mockClient.AssertExpectations(t)
 }
 
+func TestHiveListViewsHandlesObjectLoadingErrors(t *testing.T) {
+       t.Run("skips concurrently deleted views", func(t *testing.T) {
+               mockClient := &mockHiveClient{}
+               mockClient.On("GetTables", mock.Anything, "test_database", 
"*").Return([]string{"deleted", "test_view"}, nil).Once()
+               mockClient.On("GetDatabase", mock.Anything, 
"test_database").Return(&hive_metastore.Database{Name: "test_database"}, 
nil).Once()
+               mockClient.On("GetTable", mock.Anything, "test_database", 
"deleted").Return(nil, errNoSuchObject).Once()
+               mockClient.On("GetTable", mock.Anything, "test_database", 
"test_view").Return(testIcebergHiveView, nil).Once()
+
+               var views []table.Identifier
+               for identifier, err := range NewCatalogWithClient(mockClient, 
iceberg.Properties{}).ListViews(context.Background(), 
DatabaseIdentifier("test_database")) {
+                       require.NoError(t, err)
+                       views = append(views, identifier)
+               }
+               require.Equal(t, 
[]table.Identifier{TableIdentifier("test_database", "test_view")}, views)
+               mockClient.AssertExpectations(t)
+       })
+
+       t.Run("propagates operational failures", func(t *testing.T) {
+               loadErr := errors.New("metastore host not found")
+               mockClient := &mockHiveClient{}
+               mockClient.On("GetTables", mock.Anything, "test_database", 
"*").Return([]string{"deleted", "broken", "test_view"}, nil).Once()
+               mockClient.On("GetDatabase", mock.Anything, 
"test_database").Return(&hive_metastore.Database{Name: "test_database"}, 
nil).Once()
+               mockClient.On("GetTable", mock.Anything, "test_database", 
"deleted").Return(nil, errNoSuchObject).Once()
+               mockClient.On("GetTable", mock.Anything, "test_database", 
"broken").Return(nil, loadErr).Once()
+
+               var gotErr error
+               for identifier, err := range NewCatalogWithClient(mockClient, 
iceberg.Properties{}).ListViews(context.Background(), 
DatabaseIdentifier("test_database")) {
+                       if err != nil {
+                               require.Equal(t, table.Identifier{}, identifier)
+                               gotErr = err
+
+                               break
+                       }
+               }
+               require.ErrorIs(t, gotErr, loadErr)
+               require.ErrorContains(t, gotErr, "failed to load view 
test_database.broken while listing")
+               mockClient.AssertNumberOfCalls(t, "GetTable", 2)
+               mockClient.AssertExpectations(t)
+       })
+}
+
 func TestHiveLoadView(t *testing.T) {
        assert := require.New(t)
 

Reply via email to