|
10 | 10 | from databricks.sdk.service.catalog import CatalogInfo, SchemaInfo, TableInfo |
11 | 11 |
|
12 | 12 | from databricks.labs.ucx.framework.owners import AdministratorLocator |
| 13 | +from databricks.labs.ucx.framework.utils import escape_sql_identifier |
13 | 14 | from databricks.labs.ucx.hive_metastore.grants import MigrateGrants |
14 | 15 | from databricks.labs.ucx.hive_metastore.locations import ExternalLocations |
15 | 16 | from databricks.labs.ucx.hive_metastore.mapping import ( |
@@ -1483,3 +1484,60 @@ def test_table_migration_status_source_table_unknown() -> None: |
1483 | 1484 |
|
1484 | 1485 | assert owner == "an_admin" |
1485 | 1486 | table_ownership.owner_of.assert_not_called() |
| 1487 | + |
| 1488 | + |
| 1489 | +class MockBackendWithGeneralException(MockBackend): |
| 1490 | + """Mock backend that allows raising a general exception. |
| 1491 | +
|
| 1492 | + Note: we want to raise a Spark AnalysisException, for which we do not have the dependency to raise explicitly. |
| 1493 | + """ |
| 1494 | + |
| 1495 | + @staticmethod |
| 1496 | + def _api_error_from_message(error_message: str): # No return type to avoid mypy complains on different return type |
| 1497 | + return Exception(error_message) |
| 1498 | + |
| 1499 | + |
| 1500 | +def test_migrate_tables_handles_table_with_empty_column(caplog) -> None: |
| 1501 | + table_crawler = create_autospec(TablesCrawler) |
| 1502 | + table = Table("hive_metastore", "schema", "table", "MANAGED", "DELTA") |
| 1503 | + |
| 1504 | + error_message = ( |
| 1505 | + "INVALID_PARAMETER_VALUE: Invalid input: RPC CreateTable Field managedcatalog.ColumnInfo.name: " |
| 1506 | + 'At columns.21: name "" is not a valid name`' |
| 1507 | + ) |
| 1508 | + query = f"ALTER TABLE {escape_sql_identifier(table.full_name)} SET TBLPROPERTIES ('upgraded_to' = 'catalog.schema.table');" |
| 1509 | + backend = MockBackendWithGeneralException(fails_on_first={query: error_message}) |
| 1510 | + |
| 1511 | + ws = create_autospec(WorkspaceClient) |
| 1512 | + ws.get_workspace_id.return_value = 123456789 |
| 1513 | + |
| 1514 | + table_mapping = create_autospec(TableMapping) |
| 1515 | + rule = Rule("workspace", "catalog", "schema", "schema", "table", "table") |
| 1516 | + table_to_migrate = TableToMigrate(table, rule) |
| 1517 | + table_mapping.get_tables_to_migrate.return_value = [table_to_migrate] |
| 1518 | + |
| 1519 | + migration_status_refresher = create_autospec(TableMigrationStatusRefresher) |
| 1520 | + migration_status_refresher.get_seen_tables.return_value = {} |
| 1521 | + migration_status_refresher.index.return_value = [] |
| 1522 | + |
| 1523 | + migrate_grants = create_autospec(MigrateGrants) |
| 1524 | + external_locations = create_autospec(ExternalLocations) |
| 1525 | + table_migrator = TablesMigrator( |
| 1526 | + table_crawler, |
| 1527 | + ws, |
| 1528 | + backend, |
| 1529 | + table_mapping, |
| 1530 | + migration_status_refresher, |
| 1531 | + migrate_grants, |
| 1532 | + external_locations, |
| 1533 | + ) |
| 1534 | + |
| 1535 | + with caplog.at_level(logging.WARN, logger="databricks.labs.ucx.hive_metastore"): |
| 1536 | + table_migrator.migrate_tables(table.what) |
| 1537 | + assert "failed-to-migrate: Table with empty column name 'hive_metastore.schema.table'" in caplog.messages |
| 1538 | + |
| 1539 | + table_crawler.snapshot.assert_not_called() # Mocking table mapping instead |
| 1540 | + ws.get_workspace_id.assert_not_called() # Errors before getting here |
| 1541 | + migration_status_refresher.index.assert_not_called() # Only called when migrating view |
| 1542 | + migrate_grants.apply.assert_not_called() # Errors before getting here |
| 1543 | + external_locations.resolve_mount.assert_not_called() # Only called when migrating external table |
0 commit comments