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

junrui pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/flink.git


The following commit(s) were added to refs/heads/master by this push:
     new ec391138d3c [FLINK-39195][Table SQL/API] ObjectPath should implement 
Comparable
ec391138d3c is described below

commit ec391138d3c0b0592652bc7c36867dfd41d15c05
Author: Myracle <[email protected]>
AuthorDate: Tue Mar 3 15:19:17 2026 +0800

    [FLINK-39195][Table SQL/API] ObjectPath should implement Comparable
---
 flink-python/pyflink/table/catalog.py              |  23 +++
 flink-python/pyflink/table/tests/test_catalog.py   | 160 +++++++++++++++++++++
 .../org/apache/flink/table/catalog/ObjectPath.java |  11 +-
 .../apache/flink/table/catalog/ObjectPathTest.java | 107 ++++++++++++++
 4 files changed, 300 insertions(+), 1 deletion(-)

diff --git a/flink-python/pyflink/table/catalog.py 
b/flink-python/pyflink/table/catalog.py
index d72eca4293c..27d9329ebb5 100644
--- a/flink-python/pyflink/table/catalog.py
+++ b/flink-python/pyflink/table/catalog.py
@@ -1251,6 +1251,29 @@ class ObjectPath(object):
     def get_full_name(self) -> str:
         return self._j_object_path.getFullName()
 
+    def compare_to(self, other: 'ObjectPath') -> int:
+        return self._j_object_path.compareTo(other._j_object_path)
+
+    def __lt__(self, other):
+        if not isinstance(other, ObjectPath):
+            return NotImplemented
+        return self.compare_to(other) < 0
+
+    def __le__(self, other):
+        if not isinstance(other, ObjectPath):
+            return NotImplemented
+        return self.compare_to(other) <= 0
+
+    def __gt__(self, other):
+        if not isinstance(other, ObjectPath):
+            return NotImplemented
+        return self.compare_to(other) > 0
+
+    def __ge__(self, other):
+        if not isinstance(other, ObjectPath):
+            return NotImplemented
+        return self.compare_to(other) >= 0
+
     @staticmethod
     def from_string(full_name: str) -> 'ObjectPath':
         gateway = get_gateway()
diff --git a/flink-python/pyflink/table/tests/test_catalog.py 
b/flink-python/pyflink/table/tests/test_catalog.py
index aaeb86ad514..1e3023cc462 100644
--- a/flink-python/pyflink/table/tests/test_catalog.py
+++ b/flink-python/pyflink/table/tests/test_catalog.py
@@ -1037,3 +1037,163 @@ class CatalogTestBase(PyFlinkTestCase):
         self.assertEqual(1,
                          len(self.catalog.list_partitions(
                              self.path1, 
self.create_another_partition_spec_subset())))
+
+
+class ObjectPathTest(PyFlinkTestCase):
+    """Unit tests for ObjectPath covering construction, property
+    access, equality, hashing, string representation, comparison
+    operators and edge cases."""
+
+    def test_get_database_name(self):
+        path = ObjectPath("db", "table")
+        self.assertEqual("db", path.get_database_name())
+
+    def test_get_object_name(self):
+        path = ObjectPath("db", "table")
+        self.assertEqual("table", path.get_object_name())
+
+    def test_get_full_name(self):
+        path = ObjectPath("db", "table")
+        self.assertEqual("db.table", path.get_full_name())
+
+    def test_str(self):
+        path = ObjectPath("mydb", "mytable")
+        self.assertEqual("mydb.mytable", str(path))
+
+    def test_from_string(self):
+        path = ObjectPath.from_string("mydb.mytable")
+        self.assertEqual("mydb", path.get_database_name())
+        self.assertEqual("mytable", path.get_object_name())
+
+    # ---- __eq__ and __hash__ tests ----
+
+    def test_eq_same_path(self):
+        path1 = ObjectPath("db", "table")
+        path2 = ObjectPath("db", "table")
+        self.assertEqual(path1, path2)
+
+    def test_eq_different_database(self):
+        path1 = ObjectPath("db1", "table")
+        path2 = ObjectPath("db2", "table")
+        self.assertNotEqual(path1, path2)
+
+    def test_eq_different_object(self):
+        path1 = ObjectPath("db", "table1")
+        path2 = ObjectPath("db", "table2")
+        self.assertNotEqual(path1, path2)
+
+    def test_eq_non_object_path(self):
+        path = ObjectPath("db", "table")
+        self.assertNotEqual(path, "db.table")
+        self.assertNotEqual(path, 42)
+        self.assertNotEqual(path, None)
+
+    def test_hash_equal_objects(self):
+        path1 = ObjectPath("db", "table")
+        path2 = ObjectPath("db", "table")
+        self.assertEqual(hash(path1), hash(path2))
+
+    def test_hash_can_be_dict_key(self):
+        path1 = ObjectPath("db", "t1")
+        path2 = ObjectPath("db", "t2")
+        d = {path1: "value1", path2: "value2"}
+        self.assertEqual("value1", d[ObjectPath("db", "t1")])
+        self.assertEqual("value2", d[ObjectPath("db", "t2")])
+
+    def test_hash_can_be_in_set(self):
+        path1 = ObjectPath("db", "table")
+        path2 = ObjectPath("db", "table")
+        s = {path1, path2}
+        self.assertEqual(1, len(s))
+
+    # ---- Comparison operator tests (happy path) ----
+
+    def test_lt(self):
+        path_a = ObjectPath("a", "table")
+        path_b = ObjectPath("b", "table")
+        self.assertTrue(path_a < path_b)
+        self.assertFalse(path_b < path_a)
+
+    def test_lt_same(self):
+        path1 = ObjectPath("db", "table")
+        path2 = ObjectPath("db", "table")
+        self.assertFalse(path1 < path2)
+
+    def test_le(self):
+        path_a = ObjectPath("a", "table")
+        path_b = ObjectPath("b", "table")
+        path_a2 = ObjectPath("a", "table")
+        self.assertTrue(path_a <= path_b)
+        self.assertTrue(path_a <= path_a2)
+        self.assertFalse(path_b <= path_a)
+
+    def test_gt(self):
+        path_a = ObjectPath("a", "table")
+        path_b = ObjectPath("b", "table")
+        self.assertTrue(path_b > path_a)
+        self.assertFalse(path_a > path_b)
+
+    def test_gt_same(self):
+        path1 = ObjectPath("db", "table")
+        path2 = ObjectPath("db", "table")
+        self.assertFalse(path1 > path2)
+
+    def test_ge(self):
+        path_a = ObjectPath("a", "table")
+        path_b = ObjectPath("b", "table")
+        path_b2 = ObjectPath("b", "table")
+        self.assertTrue(path_b >= path_a)
+        self.assertTrue(path_b >= path_b2)
+        self.assertFalse(path_a >= path_b)
+
+    def test_compare_by_object_name(self):
+        """Within the same database, ordering is by object name."""
+        path1 = ObjectPath("db", "alpha")
+        path2 = ObjectPath("db", "beta")
+        self.assertTrue(path1 < path2)
+        self.assertTrue(path2 > path1)
+
+    def test_sorting(self):
+        """Verify that a list of ObjectPath instances can be correctly 
sorted."""
+        paths = [
+            ObjectPath("db2", "table"),
+            ObjectPath("db1", "table"),
+            ObjectPath("db1", "alpha"),
+        ]
+        sorted_paths = sorted(paths)
+        self.assertEqual("db1.alpha", str(sorted_paths[0]))
+        self.assertEqual("db1.table", str(sorted_paths[1]))
+        self.assertEqual("db2.table", str(sorted_paths[2]))
+
+    # ---- Comparison with non-ObjectPath objects (edge cases) ----
+
+    def test_lt_non_object_path_raises_type_error(self):
+        """Comparison with a non-ObjectPath object should raise TypeError."""
+        path = ObjectPath("db", "table")
+        with self.assertRaises(TypeError):
+            path < 42
+
+    def test_le_non_object_path_raises_type_error(self):
+        path = ObjectPath("db", "table")
+        with self.assertRaises(TypeError):
+            path <= "some_string"
+
+    def test_gt_non_object_path_raises_type_error(self):
+        path = ObjectPath("db", "table")
+        with self.assertRaises(TypeError):
+            path > 3.14
+
+    def test_ge_non_object_path_raises_type_error(self):
+        path = ObjectPath("db", "table")
+        with self.assertRaises(TypeError):
+            path >= None
+
+    def test_comparison_with_list_raises_type_error(self):
+        path = ObjectPath("db", "table")
+        with self.assertRaises(TypeError):
+            path < ["db", "table"]
+
+    def test_comparison_with_dict_raises_type_error(self):
+        path = ObjectPath("db", "table")
+        with self.assertRaises(TypeError):
+            path >= {"db": "table"}
diff --git 
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/catalog/ObjectPath.java
 
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/catalog/ObjectPath.java
index c50644af7e7..209d812c565 100644
--- 
a/flink-table/flink-table-common/src/main/java/org/apache/flink/table/catalog/ObjectPath.java
+++ 
b/flink-table/flink-table-common/src/main/java/org/apache/flink/table/catalog/ObjectPath.java
@@ -28,7 +28,7 @@ import static 
org.apache.flink.util.Preconditions.checkArgument;
 
 /** A database name and object (table/view/function) name combo in a catalog. 
*/
 @PublicEvolving
-public class ObjectPath implements Serializable {
+public class ObjectPath implements Serializable, Comparable<ObjectPath> {
     private final String databaseName;
     private final String objectName;
 
@@ -96,4 +96,13 @@ public class ObjectPath implements Serializable {
     public String toString() {
         return String.format("%s.%s", databaseName, objectName);
     }
+
+    @Override
+    public int compareTo(ObjectPath other) {
+        int cmp = this.databaseName.compareTo(other.databaseName);
+        if (cmp != 0) {
+            return cmp;
+        }
+        return this.objectName.compareTo(other.objectName);
+    }
 }
diff --git 
a/flink-table/flink-table-common/src/test/java/org/apache/flink/table/catalog/ObjectPathTest.java
 
b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/catalog/ObjectPathTest.java
new file mode 100644
index 00000000000..146d689850e
--- /dev/null
+++ 
b/flink-table/flink-table-common/src/test/java/org/apache/flink/table/catalog/ObjectPathTest.java
@@ -0,0 +1,107 @@
+/*
+ * 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.
+ */
+
+package org.apache.flink.table.catalog;
+
+import org.junit.jupiter.api.Test;
+
+import java.util.Arrays;
+import java.util.List;
+
+import static org.assertj.core.api.Assertions.assertThat;
+
+/** Tests for {@link ObjectPath}. */
+class ObjectPathTest {
+
+    @Test
+    void testCompareToWithDifferentDatabaseName() {
+        ObjectPath path1 = new ObjectPath("aDatabase", "table1");
+        ObjectPath path2 = new ObjectPath("bDatabase", "table1");
+
+        assertThat(path1.compareTo(path2)).isLessThan(0);
+        assertThat(path2.compareTo(path1)).isGreaterThan(0);
+    }
+
+    @Test
+    void testCompareToWithSameDatabaseDifferentObjectName() {
+        ObjectPath path1 = new ObjectPath("myDb", "aTable");
+        ObjectPath path2 = new ObjectPath("myDb", "bTable");
+
+        assertThat(path1.compareTo(path2)).isLessThan(0);
+        assertThat(path2.compareTo(path1)).isGreaterThan(0);
+    }
+
+    @Test
+    void testCompareToWithEqualPaths() {
+        ObjectPath path1 = new ObjectPath("myDb", "myTable");
+        ObjectPath path2 = new ObjectPath("myDb", "myTable");
+
+        assertThat(path1.compareTo(path2)).isEqualTo(0);
+    }
+
+    @Test
+    void testCompareToWithSelf() {
+        ObjectPath path = new ObjectPath("myDb", "myTable");
+
+        assertThat(path.compareTo(path)).isEqualTo(0);
+    }
+
+    @Test
+    void testCompareToConsistentWithEquals() {
+        ObjectPath path1 = new ObjectPath("db", "table");
+        ObjectPath path2 = new ObjectPath("db", "table");
+
+        // compareTo == 0 should be consistent with equals
+        assertThat(path1.compareTo(path2) == 0).isEqualTo(path1.equals(path2));
+    }
+
+    @Test
+    void testSortingWithCollections() {
+        ObjectPath path1 = new ObjectPath("cDb", "zTable");
+        ObjectPath path2 = new ObjectPath("aDb", "mTable");
+        ObjectPath path3 = new ObjectPath("bDb", "aTable");
+        ObjectPath path4 = new ObjectPath("aDb", "aTable");
+
+        List<ObjectPath> paths = Arrays.asList(path1, path2, path3, path4);
+        paths.sort(null);
+
+        // After sorting: aDb.aTable, aDb.mTable, bDb.aTable, cDb.zTable
+        assertThat(paths).containsExactly(path4, path2, path3, path1);
+    }
+
+    @Test
+    void testCompareToTransitivity() {
+        ObjectPath path1 = new ObjectPath("aDb", "aTable");
+        ObjectPath path2 = new ObjectPath("bDb", "aTable");
+        ObjectPath path3 = new ObjectPath("cDb", "aTable");
+
+        // Transitivity: path1 < path2 and path2 < path3, then path1 < path3
+        assertThat(path1.compareTo(path2)).isLessThan(0);
+        assertThat(path2.compareTo(path3)).isLessThan(0);
+        assertThat(path1.compareTo(path3)).isLessThan(0);
+    }
+
+    @Test
+    void testCompareToDatabaseNameTakesPrecedence() {
+        // Even if objectName order is reversed, databaseName comparison takes 
precedence
+        ObjectPath path1 = new ObjectPath("aDb", "zTable");
+        ObjectPath path2 = new ObjectPath("bDb", "aTable");
+
+        assertThat(path1.compareTo(path2)).isLessThan(0);
+    }
+}

Reply via email to