bito-code-review[bot] commented on code in PR #44683:
URL: https://github.com/apache/superset/pull/44683#discussion_r4113535020


##########
superset/commands/database/tables.py:
##########
@@ -177,3 +175,40 @@ def validate(self) -> None:
         self._model = cast(Database, DatabaseDAO.find_by_id(self._db_id))
         if not self._model:
             raise DatabaseNotFoundError()
+
+        self._catalog_name = self._catalog_name or 
self._model.get_default_catalog()
+        if not self._model.db_engine_spec.supports_schemas:
+            self._schema_name = None
+
+        if self._schema_name:
+            self._validate_schema(self._schema_name)
+
+    def _validate_schema(self, schema_name: str) -> None:
+        """
+        Accept only a schema that the schemas endpoint would list for this 
user.
+
+        The schema has to exist in the database and be accessible to the user,
+        otherwise ``DatabaseSchemaNotFoundError`` is raised before any table or
+        view lookup is run.
+        """
+        try:
+            schemas = self._model.get_all_schema_names(
+                catalog=self._catalog_name,
+                cache=self._model.schema_cache_enabled,
+                cache_timeout=self._model.schema_cache_timeout or None,
+                force=self._force,
+            )

Review Comment:
   <!-- Bito Reply -->
   The explanation provided clarifies that the schema lookup is necessary to 
distinguish between an empty schema and a missing one, which the existing table 
lookups cannot do. Since the call uses the same cached mechanism and settings 
as the schemas endpoint, it maintains consistency with existing behavior. This 
approach is acceptable for ensuring schema existence validation before 
proceeding with table lookups.



##########
superset/commands/database/tables.py:
##########
@@ -177,3 +175,40 @@ def validate(self) -> None:
         self._model = cast(Database, DatabaseDAO.find_by_id(self._db_id))
         if not self._model:
             raise DatabaseNotFoundError()
+
+        self._catalog_name = self._catalog_name or 
self._model.get_default_catalog()
+        if not self._model.db_engine_spec.supports_schemas:
+            self._schema_name = None
+
+        if self._schema_name:
+            self._validate_schema(self._schema_name)
+
+    def _validate_schema(self, schema_name: str) -> None:
+        """
+        Accept only a schema that the schemas endpoint would list for this 
user.
+
+        The schema has to exist in the database and be accessible to the user,
+        otherwise ``DatabaseSchemaNotFoundError`` is raised before any table or
+        view lookup is run.
+        """
+        try:
+            schemas = self._model.get_all_schema_names(
+                catalog=self._catalog_name,
+                cache=self._model.schema_cache_enabled,
+                cache_timeout=self._model.schema_cache_timeout or None,
+                force=self._force,
+            )
+            accessible = schema_name in schemas and bool(

Review Comment:
   <!-- Bito Reply -->
   The reviewer's suggestion to use case-insensitive matching is based on the 
concern that raw identifiers from the database might not match the unnormalized 
input from the API, potentially causing valid schemas to be incorrectly 
rejected. However, your assessment that exact matching is required for 
consistency with other parts of the system, such as the schemas endpoint and 
existing access permissions, is correct. Given that some database engines treat 
case-sensitive quoted identifiers as distinct, maintaining strict matching is 
the appropriate approach to avoid unintended behavior.



##########
tests/unit_tests/commands/databases/tables_test.py:
##########
@@ -291,3 +309,130 @@ def test_tables_without_catalog(
         cache=database_without_catalog.table_cache_enabled,
         cache_timeout=database_without_catalog.table_cache_timeout,
     )
+
+
+def test_tables_unknown_schema(
+    mocker: MockerFixture,
+    database_without_catalog: MagicMock,
+) -> None:
+    """
+    A schema that does not exist in the database is rejected before any
+    table or view lookup runs.
+    """
+    get_datasources_accessible_by_user = mocker.patch.object(
+        security_manager,
+        "get_datasources_accessible_by_user",
+    )
+
+    with pytest.raises(DatabaseSchemaNotFoundError):
+        TablesDatabaseCommand(1, None, "not_a_schema", False).run()
+
+    database_without_catalog.get_all_table_names_in_schema.assert_not_called()
+    database_without_catalog.get_all_view_names_in_schema.assert_not_called()
+    
database_without_catalog.get_all_materialized_view_names_in_schema.assert_not_called()

Review Comment:
   <!-- Bito Reply -->
   The reviewer's suggestion to wrap the line is based on the repository's 
88-character line length limit, which is enforced by the project's 
configuration. While pre-commit may not have flagged this specific line in your 
local environment, CI pipelines often use stricter configurations that will 
catch this violation. Applying the wrap is recommended to ensure consistency 
with the project's style guidelines and to avoid potential CI failures.
   
   **tests/unit_tests/commands/databases/tables_test.py**
   ```
   database_without_catalog.get_all_table_names_in_schema.assert_not_called()
       database_without_catalog.get_all_view_names_in_schema.assert_not_called()
       
database_without_catalog.get_all_materialized_view_names_in_schema.assert_not_called()
   ```



-- 
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]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to