|
13 | 13 | import sqlmesh.core.dialect as d |
14 | 14 | from sqlmesh.core.engine_adapter import BigQueryEngineAdapter |
15 | 15 | from sqlmesh.core.engine_adapter.bigquery import select_partitions_expr |
16 | | -from sqlmesh.core.engine_adapter.shared import DataObjectType |
| 16 | +from sqlmesh.core.engine_adapter.mixins import TableAlterDropClusterKeyOperation |
| 17 | +from sqlmesh.core.engine_adapter.shared import DataObject, DataObjectType |
| 18 | +from sqlmesh.core.schema_diff import TableAlterDropColumnOperation |
17 | 19 | from sqlmesh.core.node import IntervalUnit |
18 | 20 | from sqlmesh.utils import AttributeDict |
19 | 21 | from sqlmesh.utils.errors import SQLMeshError |
@@ -1380,3 +1382,108 @@ def test_sync_grants_config_no_schema( |
1380 | 1382 |
|
1381 | 1383 | with pytest.raises(ValueError, match="Table test_table does not have a schema \\(dataset\\)"): |
1382 | 1384 | adapter.sync_grants_config(relation, new_grants_config) |
| 1385 | + |
| 1386 | + |
| 1387 | +def _mock_bq_data_objects( |
| 1388 | + mocker: MockerFixture, |
| 1389 | + adapter: BigQueryEngineAdapter, |
| 1390 | + current_obj: DataObject, |
| 1391 | + target_obj: DataObject, |
| 1392 | +) -> None: |
| 1393 | + """Patch _get_data_objects on *adapter* to return the supplied DataObjects.""" |
| 1394 | + |
| 1395 | + def _side_effect( |
| 1396 | + schema_name: t.Any, object_names: t.Optional[t.Set[str]] = None |
| 1397 | + ) -> t.List[DataObject]: |
| 1398 | + results = [] |
| 1399 | + for obj in [current_obj, target_obj]: |
| 1400 | + if object_names is None or obj.name in object_names: |
| 1401 | + results.append(obj) |
| 1402 | + return results |
| 1403 | + |
| 1404 | + mocker.patch.object(adapter, "_get_data_objects", side_effect=_side_effect) |
| 1405 | + |
| 1406 | + |
| 1407 | +def test_bigquery_clustering_column_drop_before_clustering_change( |
| 1408 | + make_mocked_engine_adapter: t.Callable, mocker: MockerFixture |
| 1409 | +) -> None: |
| 1410 | + """BigQuery keeps the historical order: DROP COLUMN before clustering key change. |
| 1411 | +
|
| 1412 | + Dropping a column that belongs to the current clustering key must NOT trigger |
| 1413 | + a reorder. BigQuery must emit DROP COLUMN first, then DROP CLUSTERING KEY. |
| 1414 | + """ |
| 1415 | + adapter = make_mocked_engine_adapter(BigQueryEngineAdapter, patch_get_data_objects=False) |
| 1416 | + |
| 1417 | + current_columns = {"tenant_id": "INT64", "partition_month": "INT64"} |
| 1418 | + target_columns = {"tenant_id": "INT64"} |
| 1419 | + adapter.columns = lambda table_name, **kwargs: { |
| 1420 | + k: exp.DataType.build(v, dialect="bigquery") |
| 1421 | + for k, v in ( |
| 1422 | + current_columns if table_name == "project.dataset.current_table" else target_columns |
| 1423 | + ).items() |
| 1424 | + } |
| 1425 | + |
| 1426 | + _mock_bq_data_objects( |
| 1427 | + mocker, |
| 1428 | + adapter, |
| 1429 | + current_obj=DataObject( |
| 1430 | + catalog="project", |
| 1431 | + schema="dataset", |
| 1432 | + name="current_table", |
| 1433 | + type=DataObjectType.TABLE, |
| 1434 | + clustering_key="(partition_month)", |
| 1435 | + ), |
| 1436 | + target_obj=DataObject( |
| 1437 | + catalog="project", |
| 1438 | + schema="dataset", |
| 1439 | + name="target_table", |
| 1440 | + type=DataObjectType.TABLE, |
| 1441 | + clustering_key=None, |
| 1442 | + ), |
| 1443 | + ) |
| 1444 | + |
| 1445 | + operations = adapter.get_alter_operations( |
| 1446 | + "project.dataset.current_table", "project.dataset.target_table" |
| 1447 | + ) |
| 1448 | + |
| 1449 | + assert len(operations) == 2 |
| 1450 | + assert isinstance(operations[0], TableAlterDropColumnOperation) |
| 1451 | + assert isinstance(operations[1], TableAlterDropClusterKeyOperation) |
| 1452 | + |
| 1453 | + |
| 1454 | +def test_bigquery_no_column_change_clustering_drop_only( |
| 1455 | + make_mocked_engine_adapter: t.Callable, mocker: MockerFixture |
| 1456 | +) -> None: |
| 1457 | + """BigQuery: same columns, only clustering key removed — one DROP CLUSTERING KEY.""" |
| 1458 | + adapter = make_mocked_engine_adapter(BigQueryEngineAdapter, patch_get_data_objects=False) |
| 1459 | + |
| 1460 | + columns = {"tenant_id": "INT64", "partition_month": "INT64"} |
| 1461 | + adapter.columns = lambda table_name, **kwargs: { |
| 1462 | + k: exp.DataType.build(v, dialect="bigquery") for k, v in columns.items() |
| 1463 | + } |
| 1464 | + |
| 1465 | + _mock_bq_data_objects( |
| 1466 | + mocker, |
| 1467 | + adapter, |
| 1468 | + current_obj=DataObject( |
| 1469 | + catalog="project", |
| 1470 | + schema="dataset", |
| 1471 | + name="current_table", |
| 1472 | + type=DataObjectType.TABLE, |
| 1473 | + clustering_key="(partition_month)", |
| 1474 | + ), |
| 1475 | + target_obj=DataObject( |
| 1476 | + catalog="project", |
| 1477 | + schema="dataset", |
| 1478 | + name="target_table", |
| 1479 | + type=DataObjectType.TABLE, |
| 1480 | + clustering_key=None, |
| 1481 | + ), |
| 1482 | + ) |
| 1483 | + |
| 1484 | + operations = adapter.get_alter_operations( |
| 1485 | + "project.dataset.current_table", "project.dataset.target_table" |
| 1486 | + ) |
| 1487 | + |
| 1488 | + assert len(operations) == 1 |
| 1489 | + assert isinstance(operations[0], TableAlterDropClusterKeyOperation) |
0 commit comments