@@ -1428,6 +1428,87 @@ def test_exchange_tables(
14281428 ]
14291429
14301430
1431+ def _executed_sql (execute_mock : t .Any ) -> t .List [str ]:
1432+ return [
1433+ quote_identifiers (call .args [0 ]).sql ("clickhouse" )
1434+ if isinstance (call .args [0 ], exp .Expr )
1435+ else call .args [0 ]
1436+ for call in execute_mock .call_args_list
1437+ ]
1438+
1439+
1440+ def test_exchange_tables_reraises_other_errors (
1441+ make_mocked_engine_adapter : t .Callable , mocker : MockerFixture , make_temp_table_name : t .Callable
1442+ ):
1443+ from clickhouse_connect .driver .exceptions import DatabaseError # type: ignore
1444+
1445+ adapter = make_mocked_engine_adapter (ClickhouseEngineAdapter )
1446+
1447+ temp_table_mock = mocker .patch ("sqlmesh.core.engine_adapter.EngineAdapter._get_temp_table" )
1448+ temp_table_mock .return_value = make_temp_table_name ("table1" , "abcd" )
1449+
1450+ execute_mock = mocker .patch ("sqlmesh.core.engine_adapter.ClickhouseEngineAdapter.execute" )
1451+ execute_mock .side_effect = [
1452+ DatabaseError ("DB::Exception: Not enough privileges. (ACCESS_DENIED)" ),
1453+ None ,
1454+ None ,
1455+ None ,
1456+ ]
1457+
1458+ with pytest .raises (DatabaseError , match = "ACCESS_DENIED" ):
1459+ adapter ._exchange_tables ("table1" , "table2" )
1460+
1461+ # No RENAME fallback and no throwaway table drop
1462+ assert _executed_sql (execute_mock ) == ['EXCHANGE TABLES "table1" AND "table2"' ]
1463+
1464+
1465+ def test_insert_overwrite_by_condition_replace_exchange_error_propagates (
1466+ make_mocked_engine_adapter : t .Callable , mocker : MockerFixture , make_temp_table_name : t .Callable
1467+ ):
1468+ from clickhouse_connect .driver .exceptions import DatabaseError # type: ignore
1469+
1470+ adapter = make_mocked_engine_adapter (ClickhouseEngineAdapter )
1471+
1472+ temp_table_mock = mocker .patch ("sqlmesh.core.engine_adapter.EngineAdapter._get_temp_table" )
1473+ temp_table_mock .return_value = make_temp_table_name ("target" , "abcd" )
1474+
1475+ fetchone_mock = mocker .patch ("sqlmesh.core.engine_adapter.ClickhouseEngineAdapter.fetchone" )
1476+ fetchone_mock .return_value = None
1477+
1478+ insert_table_name = make_temp_table_name ("new_records" , "abcd" )
1479+ existing_table_name = make_temp_table_name ("existing_records" , "abcd" )
1480+
1481+ source_queries , columns_to_types = adapter ._get_source_queries_and_columns_to_types (
1482+ parse_one (f"SELECT * FROM { insert_table_name } " ),
1483+ {
1484+ "id" : exp .DataType .build ("Int8" , dialect = "clickhouse" ),
1485+ "ds" : exp .DataType .build ("Date" , dialect = "clickhouse" ),
1486+ },
1487+ existing_table_name ,
1488+ )
1489+
1490+ def execute_side_effect (sql : t .Any , * args : t .Any , ** kwargs : t .Any ) -> None :
1491+ if str (sql ).startswith ("EXCHANGE TABLES" ):
1492+ raise DatabaseError ("DB::Exception: Not enough privileges. (ACCESS_DENIED)" )
1493+
1494+ execute_mock = mocker .patch ("sqlmesh.core.engine_adapter.ClickhouseEngineAdapter.execute" )
1495+ execute_mock .side_effect = execute_side_effect
1496+
1497+ with pytest .raises (DatabaseError , match = "ACCESS_DENIED" ):
1498+ adapter ._insert_overwrite_by_condition (
1499+ existing_table_name .sql (),
1500+ source_queries ,
1501+ columns_to_types ,
1502+ )
1503+
1504+ executed = _executed_sql (execute_mock )
1505+ assert executed [- 2 :] == [
1506+ 'EXCHANGE TABLES "__temp_existing_records_abcd" AND "__temp_target_abcd"' ,
1507+ 'DROP TABLE IF EXISTS "__temp_target_abcd"' ,
1508+ ]
1509+ assert not any (sql .startswith ("RENAME" ) for sql in executed )
1510+
1511+
14311512def test_virtual_catalog_ddl_stripping (make_mocked_engine_adapter : t .Callable ):
14321513 """After inject_virtual_catalog(), create_schema() with the virtual catalog prefix must strip
14331514 the catalog and execute without raising, and with a wrong catalog must raise SQLMeshError."""
0 commit comments