Repository: apache/iceberg-go

Issue:
"table X.Y is not an iceberg table" on a valid iceberg table
### Apache Iceberg version

None

### Please describe the bug 🐞

We are appending data to a table in AWS Glue Catalog using PyIceberg & PyArrow
then try to load the table from `iceberg-go` but `LoadTable` fails
with "table X.Y is not an iceberg table". The table parameters map
contains key "table_type" as required by [Glue catalog implementation](https://github.com/apache/iceberg-go/blob/237da8fd2034d772cd5969a372d2e411e3dfc66d/catalog/glue/glue.go#L697-L699)
but its value is not all-upper "ICEBERG", rather it's lower case.

On the writer side (PyIceberg/PyArrow) we use the [AWS Glue Iceberg REST endpoint](https://docs.aws.amazon.com/glue/latest/dg/connect-glu-iceberg-rest.html).

Both [PyIceberg](https://github.com/apache/iceberg-python/blob/main/pyiceberg/catalog/glue.py#L376) and [java](https://github.com/apache/iceberg/blob/061ae58986db3495ff3af6f1932a96dd086e5fbd/aws/src/main/java/org/apache/iceberg/aws/glue/GlueCatalog.java#L329) implementations
perform the same check ignoring the case of the value.
It seems that the condition check should be implemented in case-insensitive
way for `iceberg-go` too, wdyt?

Discussion:
@tommika: Hi @vbekiaris ... It looks like your fix may have been broken by this change:
https://github.com/apache/iceberg-go/commit/1aa2a521cc873e8d93df5705697a99a02aa250a4

which has introduced a new case-sensitive check on the table type:
https://github.com/apache/iceberg-go/blob/main/catalog/glue/glue.go#L671-L673




Solution patch:
diff --git a/catalog/glue/glue.go b/catalog/glue/glue.go
index 65b3899..866cf09 100644
--- a/catalog/glue/glue.go
+++ b/catalog/glue/glue.go
@@ -24,6 +24,7 @@ import (
 	"iter"
 	"maps"
 	"strconv"
+	"strings"
 	_ "unsafe"
 
 	"github.com/apache/iceberg-go"
@@ -694,7 +695,7 @@ func (c *Catalog) getTable(ctx context.Context, database, tableName string) (*ty
 		return nil, fmt.Errorf("failed to get table %s.%s: %w", database, tableName, err)
 	}
 
-	if tblRes.Table.Parameters[tableTypePropsKey] != glueTypeIceberg {
+	if !strings.EqualFold(tblRes.Table.Parameters[tableTypePropsKey], glueTypeIceberg) {
 		return nil, fmt.Errorf("table %s.%s is not an iceberg table", database, tableName)
 	}
 
@@ -749,7 +750,7 @@ func DatabaseIdentifier(database string) table.Identifier {
 func filterTableListByType(database string, tableList []types.Table, tableType string) []table.Identifier {
 	var filtered []table.Identifier
 	for _, tbl := range tableList {
-		if tbl.Parameters[tableTypePropsKey] != tableType {
+		if !strings.EqualFold(tbl.Parameters[tableTypePropsKey], tableType) {
 			continue
 		}
 		filtered = append(filtered, TableIdentifier(database, aws.ToString(tbl.Name)))



Test patch:
diff --git a/catalog/glue/glue_test.go b/catalog/glue/glue_test.go
index 2a685e1..d56d4ef 100644
--- a/catalog/glue/glue_test.go
+++ b/catalog/glue/glue_test.go
@@ -145,6 +145,14 @@ var testIcebergGlueTable5 = types.Table{
 	},
 }
 
+var testIcebergGlueTable6 = types.Table{
+	Name: aws.String("test_table6"),
+	Parameters: map[string]string{
+		tableTypePropsKey:        "iceberg",
+		metadataLocationPropsKey: "s3://test-bucket/test_table/metadata/abc123456-789.metadata.json",
+	},
+}
+
 var testNonIcebergGlueTable = types.Table{
 	Name: aws.String("other_table"),
 	Parameters: map[string]string{
@@ -189,6 +197,53 @@ func TestGlueGetTable(t *testing.T) {
 	assert.Equal("s3://test-bucket/test_table/metadata/abc123-123.metadata.json", tbl.Parameters[metadataLocationPropsKey])
 }
 
+func TestGlueGetTableCaseInsensitive(t *testing.T) {
+	assert := require.New(t)
+
+	testCases := []struct {
+		name      string
+		tableType string
+		shouldErr bool
+	}{
+		{"uppercase", "ICEBERG", false},
+		{"lowercase", "iceberg", false},
+		{"mixed case", "IcEbErG", false},
+		{"non-iceberg", "HIVE", true},
+	}
+
+	for _, tc := range testCases {
+		t.Run(tc.name, func(t *testing.T) {
+			mockGlueSvc := &mockGlueClient{}
+
+			testTable := types.Table{
+				Name: aws.String("test_table"),
+				Parameters: map[string]string{
+					tableTypePropsKey:        tc.tableType,
+					metadataLocationPropsKey: "s3://test-bucket/test_table/metadata/abc123-123.metadata.json",
+				},
+			}
+
+			mockGlueSvc.On("GetTable", mock.Anything, &glue.GetTableInput{
+				DatabaseName: aws.String("test_database"),
+				Name:         aws.String("test_table"),
+			}, mock.Anything).Return(&glue.GetTableOutput{Table: &testTable}, nil)
+
+			glueCatalog := &Catalog{
+				glueSvc: mockGlueSvc,
+			}
+
+			tbl, err := glueCatalog.getTable(context.TODO(), "test_database", "test_table")
+			if tc.shouldErr {
+				assert.Error(err)
+				assert.Contains(err.Error(), "is not an iceberg table")
+			} else {
+				assert.NoError(err)
+				assert.Equal("s3://test-bucket/test_table/metadata/abc123-123.metadata.json", tbl.Parameters[metadataLocationPropsKey])
+			}
+		})
+	}
+}
+
 func TestGlueListTables(t *testing.T) {
 	assert := require.New(t)
 
@@ -197,7 +252,7 @@ func TestGlueListTables(t *testing.T) {
 	mockGlueSvc.On("GetTables", mock.Anything, &glue.GetTablesInput{
 		DatabaseName: aws.String("test_database"),
 	}, mock.Anything).Return(&glue.GetTablesOutput{
-		TableList: []types.Table{testIcebergGlueTable1, testNonIcebergGlueTable},
+		TableList: []types.Table{testIcebergGlueTable1, testIcebergGlueTable6, testNonIcebergGlueTable},
 	}, nil).Once()
 
 	glueCatalog := &Catalog{
@@ -215,8 +270,9 @@ func TestGlueListTables(t *testing.T) {
 		}
 	}
 	assert.NoError(lastErr)
-	assert.Len(tbls, 1)
+	assert.Len(tbls, 2)
 	assert.Equal([]string{"test_database", "test_table"}, tbls[0])
+	assert.Equal([]string{"test_database", "test_table6"}, tbls[1])
 }
 
 func TestGlueListTablesPagination(t *testing.T) {