Copilot commented on code in PR #10575:
URL: https://github.com/apache/gravitino/pull/10575#discussion_r3008142705


##########
clients/client-python/gravitino/dto/util/dto_converters.py:
##########
@@ -643,3 +659,146 @@ def to_dtos(dtos):
         if not dtos:
             return []
         return [DTOConverters.to_dto(dto) for dto in dtos]
+
+    @singledispatchmethod
+    @staticmethod
+    def _to_column_update_request(change) -> TableUpdateRequestBase:
+        raise IllegalArgumentException(
+            f"Unknown column change type: {change.__class__.__name__}"
+        )
+
+    @_to_column_update_request.register
+    @staticmethod
+    def _(change: AddColumn) -> TableUpdateRequestBase:
+        default_value = (
+            Column.DEFAULT_VALUE_NOT_SET
+            if change.get_default_value() is None
+            or change.get_default_value() == Column.DEFAULT_VALUE_NOT_SET
+            else DTOConverters.to_function_arg(change.get_default_value())
+        )
+        return TableUpdateRequest.AddTableColumnRequest(
+            _field_name=change.field_name(),
+            _data_type=change.get_data_type(),
+            _comment=change.get_comment(),
+            _position=change.get_position(),
+            _nullable=change.is_nullable(),
+            _auto_increment=change.is_auto_increment(),
+            _default_value=default_value,
+        )

Review Comment:
   For `AddColumn` changes with no default value, the converter currently sets 
`_default_value` to `Column.DEFAULT_VALUE_NOT_SET`, which will typically 
serialize to a JSON `null` field rather than omitting `defaultValue`. To match 
existing `TableUpdateRequest.AddTableColumnRequest` serialization behavior 
(where `defaultValue` is omitted when not set), map the “not set” sentinel to 
`None` here instead.



##########
clients/client-python/tests/unittests/test_relational_catalog.py:
##########
@@ -276,3 +296,150 @@ def test_list_tables_invalid_namespace(self):
         ):
             with self.assertRaises(NoSuchSchemaException):
                 
self.catalog.list_tables(namespace=Namespace.of("invalid_schema"))
+
+    def test_drop_table(self):
+        resp_body = DropResponse(0, True)
+        mock_resp = self._get_mock_http_resp(resp_body.to_json())
+
+        with patch(
+            "gravitino.utils.http_client.HTTPClient.delete",
+            return_value=mock_resp,
+        ):
+            is_dropped = self.catalog.drop_table(self.table_identifier)
+            self.assertTrue(is_dropped)
+
+    def test_purge_table(self):
+        resp_body = DropResponse(0, True)
+        mock_resp = self._get_mock_http_resp(resp_body.to_json())
+
+        with patch(
+            "gravitino.utils.http_client.HTTPClient.delete",
+            return_value=mock_resp,
+        ):
+            is_dropped = self.catalog.purge_table(self.table_identifier)
+            self.assertTrue(is_dropped)

Review Comment:
   `test_purge_table` and `test_drop_table` only assert the boolean result; 
they don’t verify that the correct query params are sent (e.g., `purge_table` 
should pass `params={"purge": "true"}` while `drop_table` should omit it). 
Adding assertions on the mocked `HTTPClient.delete` call would prevent 
regressions in request construction.



##########
clients/client-python/tests/unittests/dto/util/test_dto_converters_to_table_update_request.py:
##########
@@ -0,0 +1,287 @@
+# 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.
+
+import unittest
+from typing import cast
+
+from gravitino.api.rel.column import Column
+from gravitino.api.rel.expressions.literals.literals import Literals
+from gravitino.api.rel.indexes.index import Index
+from gravitino.api.rel.table_change import TableChange
+from gravitino.api.rel.types.types import Types
+from gravitino.dto.rel.expressions.literal_dto import LiteralDTO
+from gravitino.dto.requests.table_update_request import TableUpdateRequest
+from gravitino.dto.util.dto_converters import DTOConverters
+from gravitino.exceptions.base import IllegalArgumentException
+
+
+class TestDTOConvertersToTableUpdateRequest(unittest.TestCase):
+    def test_to_table_update_request_add_column_with_default_value(self):
+        """Tests the conversion of an add column change with a default value 
to a TableUpdateRequest."""
+        add_column = TableChange.add_column(
+            field_name=["new_col"],
+            data_type=Types.IntegerType.get(),
+            comment="test column",
+            nullable=False,
+            auto_increment=True,
+            default_value=Literals.integer_literal(10),
+        )
+        result = cast(
+            TableUpdateRequest.AddTableColumnRequest,
+            DTOConverters.to_table_update_request(add_column),
+        )
+        self.assertIsInstance(result, TableUpdateRequest.AddTableColumnRequest)
+        add_column_request = cast(TableUpdateRequest.AddTableColumnRequest, 
result)
+        self.assertListEqual(add_column_request.field_name, ["new_col"])
+        self.assertEqual(add_column_request.data_type, Types.IntegerType.get())
+        self.assertEqual(add_column_request.comment, "test column")
+        self.assertIsNone(add_column_request.position)
+        self.assertFalse(add_column_request.is_nullable)
+        self.assertTrue(add_column_request.is_auto_increment)
+
+    def test_to_table_update_request_add_column_without_default_value(self):
+        """Tests the conversion of an add column change without a default 
value to a TableUpdateRequest."""
+        add_column = TableChange.add_column(
+            field_name=["new_col"],
+            data_type=Types.StringType.get(),
+        )
+        result = DTOConverters.to_table_update_request(add_column)
+        self.assertIsInstance(result, TableUpdateRequest.AddTableColumnRequest)
+        add_column_request = cast(TableUpdateRequest.AddTableColumnRequest, 
result)
+        self.assertEqual(
+            add_column_request.default_value,
+            Column.DEFAULT_VALUE_NOT_SET,
+        )

Review Comment:
   This test asserts `default_value == Column.DEFAULT_VALUE_NOT_SET` for “no 
default specified”. If `DTOConverters.to_table_update_request` is updated to 
use `None` for “not set” (so the `defaultValue` field is omitted instead of 
serialized as `null`), this assertion should be updated accordingly (and 
ideally validate the JSON output rather than the internal sentinel).



##########
clients/client-python/gravitino/client/relational_catalog.py:
##########
@@ -188,24 +193,96 @@ def list_tables(self, namespace: Namespace) -> 
list[NameIdentifier]:
             for ident in entity_list_resp.identifiers()
         ]
 
-    def load_table(self, identifier: NameIdentifier) -> Table:
+    @overload
+    def load_table(self, identifier: NameIdentifier) -> Table: ...
+
+    @overload
+    def load_table(  # pylint: disable=arguments-differ
+        self, identifier: NameIdentifier, required_privilege_names: 
set[Privilege.Name]
+    ) -> Table: ...
+
+    def load_table(
+        self,
+        identifier: NameIdentifier,
+        required_privilege_names: Optional[set[Privilege.Name]] = None,
+    ) -> Table:
         self._check_table_name_identifier(identifier)
         full_namespace = self._get_table_full_namespace(identifier.namespace())
+        query_params = (
+            {
+                RelationalCatalog.PRIVILEGES: ",".join(
+                    priv.name for priv in required_privilege_names
+                )
+            }
+            if required_privilege_names is not None

Review Comment:
   `required_privilege_names` is a `set`, so building the query string via 
`','.join(...)` is non-deterministic and can lead to unstable requests/tests. 
Consider sorting the privilege names before joining, and also treat an empty 
set the same as `None` (omit the `privileges` query param) to avoid sending 
`privileges=`.
   ```suggestion
                       sorted(priv.name for priv in required_privilege_names)
                   )
               }
               if required_privilege_names
   ```



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to