Repository: apache/iceberg (language: python)

Issue:
Iceberg rest catalog with postgres - List namespaces with parent returns wrong children namespaces
### Apache Iceberg version

1.4.2

### Query engine

None

### Please describe the bug 🐞

We are using version 1.4.2 but we are planning to upgrade to 1.5.0 and I confirmed that the problem is still there.

Problem: When you have 2 namespaces with similar names
E.g.: 
namespaces:
- test.auto_rag
- test_auto_rag

API CALL: http://<iceberg-rest-catalog>/v1/namespaces?parent=test_auto_rag

Response:
```
{
    "namespaces": [
        [
            "test",
            "auto_rag"
        ]
    ]
}
```
That is incorrect. `test_auto_rag` namespace doesn't have children namespaces.

In a quick look the issue seems to be with the behavior of this query with postgres:
- https://github.com/apache/iceberg/blob/866021d7d34f274349ce7de1f29d113395e7f28c/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java#L428
- https://github.com/apache/iceberg/blob/866021d7d34f274349ce7de1f29d113395e7f28c/core/src/main/java/org/apache/iceberg/jdbc/JdbcUtil.java#L340

<img width="488" alt="image" src="https://github.com/apache/iceberg/assets/48991881/ba6a162e-b611-4cab-98e9-0cdbf07493ce">

Notice:
In case is useful, this is the version we are using:  `PostgreSQL 14.10 on x86_64-pc-linux-gnu, compiled by gcc (GCC) 11.4.1 20230605 (Red Hat 11.4.1-2), 64-bit`


Discussion / hints:
See: https://github.com/apache/iceberg/issues/10443#issuecomment-2152262679
I duplicated the issue given I forgot that i created this one with a better analysis.
I think that LIKE must be replaced by equal (=).

Fix patch:
diff --git a/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java b/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java
index 4e10ee96d125..5e985faafbed 100644
--- a/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java
+++ b/core/src/main/java/org/apache/iceberg/jdbc/JdbcCatalog.java
@@ -468,6 +468,16 @@ public List<Namespace> listNamespaces(Namespace namespace) throws NoSuchNamespac
                             .toArray(String[]::new)))
             // remove duplicates
             .distinct()
+            // exclude fuzzy matches when `namespace` contains `%` or `_`
+            .filter(
+                n -> {
+                  for (int i = 0; i < namespace.levels().length; i++) {
+                    if (!n.levels()[i].equals(namespace.levels()[i])) {
+                      return false;
+                    }
+                  }
+                  return true;
+                })
             .collect(Collectors.toList());
 
     return namespaces;


Test patch:
diff --git a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
index 90492b51097a..9fe15aa21525 100644
--- a/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
+++ b/core/src/test/java/org/apache/iceberg/jdbc/TestJdbcCatalog.java
@@ -753,8 +753,11 @@ public void testListNamespace() {
     TableIdentifier tbl4 = TableIdentifier.of("db", "metadata");
     TableIdentifier tbl5 = TableIdentifier.of("db2", "metadata");
     TableIdentifier tbl6 = TableIdentifier.of("tbl6");
+    TableIdentifier tbl7 = TableIdentifier.of("db2", "ns4", "tbl5");
+    TableIdentifier tbl8 = TableIdentifier.of("d_", "ns5", "tbl6");
+    TableIdentifier tbl9 = TableIdentifier.of("d%", "ns6", "tbl7");
 
-    Lists.newArrayList(tbl1, tbl2, tbl3, tbl4, tbl5, tbl6)
+    Lists.newArrayList(tbl1, tbl2, tbl3, tbl4, tbl5, tbl6, tbl7, tbl8, tbl9)
         .forEach(t -> catalog.createTable(t, SCHEMA, PartitionSpec.unpartitioned()));
 
     List<Namespace> nsp1 = catalog.listNamespaces(Namespace.of("db"));
@@ -768,11 +771,19 @@ public void testListNamespace() {
 
     List<Namespace> nsp3 = catalog.listNamespaces();
     Set<String> tblSet2 = Sets.newHashSet(nsp3.stream().map(Namespace::toString).iterator());
-    assertThat(tblSet2).hasSize(3).contains("db", "db2", "");
+    assertThat(tblSet2).hasSize(5).contains("db", "db2", "d_", "d%", "");
 
     List<Namespace> nsp4 = catalog.listNamespaces();
     Set<String> tblSet3 = Sets.newHashSet(nsp4.stream().map(Namespace::toString).iterator());
-    assertThat(tblSet3).hasSize(3).contains("db", "db2", "");
+    assertThat(tblSet3).hasSize(5).contains("db", "db2", "d_", "d%", "");
+
+    List<Namespace> nsp5 = catalog.listNamespaces(Namespace.of("d_"));
+    assertThat(nsp5).hasSize(1);
+    assertThat(nsp5.get(0)).hasToString("d_.ns5");
+
+    List<Namespace> nsp6 = catalog.listNamespaces(Namespace.of("d%"));
+    assertThat(nsp6).hasSize(1);
+    assertThat(nsp6.get(0)).hasToString("d%.ns6");
 
     Assertions.assertThatThrownBy(() -> catalog.listNamespaces(Namespace.of("db", "db2", "ns2")))
         .isInstanceOf(NoSuchNamespaceException.class)