|
39 | 39 | logger = logging.getLogger(__name__) |
40 | 40 |
|
41 | 41 | RECOMMENDED_STATE_SYNC_ENGINES = {"postgres", "gcp_postgres", "mysql", "duckdb"} |
| 42 | +FORBIDDEN_STATE_SYNC_ENGINES = { |
| 43 | + # Do not support row-level operations |
| 44 | + "spark", |
| 45 | + "trino", |
| 46 | + # Nullable types are problematic |
| 47 | + "clickhouse", |
| 48 | +} |
42 | 49 |
|
43 | 50 |
|
44 | 51 | class ConnectionConfig(abc.ABC, BaseConfig): |
@@ -84,9 +91,14 @@ def _cursor_init(self) -> t.Optional[t.Callable[[t.Any], None]]: |
84 | 91 |
|
85 | 92 | @property |
86 | 93 | def is_recommended_for_state_sync(self) -> bool: |
87 | | - """Whether this connection is recommended for being used as a state sync for production state syncs""" |
| 94 | + """Whether this engine is recommended for being used as a state sync for production state syncs""" |
88 | 95 | return self.type_ in RECOMMENDED_STATE_SYNC_ENGINES |
89 | 96 |
|
| 97 | + @property |
| 98 | + def is_forbidden_for_state_sync(self) -> bool: |
| 99 | + """Whether this engine is forbidden from being used as a state sync""" |
| 100 | + return self.type_ in FORBIDDEN_STATE_SYNC_ENGINES |
| 101 | + |
90 | 102 | @property |
91 | 103 | def _connection_factory_with_kwargs(self) -> t.Callable[[], t.Any]: |
92 | 104 | """A function that is called to return a connection object for the given Engine Adapter""" |
@@ -1364,6 +1376,73 @@ def _static_connection_kwargs(self) -> t.Dict[str, t.Any]: |
1364 | 1376 | } |
1365 | 1377 |
|
1366 | 1378 |
|
| 1379 | +class ClickhouseConnectionConfig(ConnectionConfig): |
| 1380 | + """ |
| 1381 | + Clickhouse Connection Configuration. |
| 1382 | +
|
| 1383 | + Property reference: https://clickhouse.com/docs/en/integrations/python#client-initialization |
| 1384 | + """ |
| 1385 | + |
| 1386 | + host: str |
| 1387 | + username: str |
| 1388 | + password: t.Optional[str] = None |
| 1389 | + port: t.Optional[int] = None |
| 1390 | + cluster: t.Optional[str] = None |
| 1391 | + connect_timeout: int = 10 |
| 1392 | + send_receive_timeout: int = 300 |
| 1393 | + verify: bool = True |
| 1394 | + query_limit: int = 0 |
| 1395 | + use_compression: bool = True |
| 1396 | + compression_method: t.Optional[str] = None |
| 1397 | + |
| 1398 | + concurrent_tasks: int = 1 |
| 1399 | + register_comments: bool = True |
| 1400 | + pre_ping: bool = False |
| 1401 | + |
| 1402 | + type_: Literal["clickhouse"] = Field(alias="type", default="clickhouse") |
| 1403 | + |
| 1404 | + @property |
| 1405 | + def _connection_kwargs_keys(self) -> t.Set[str]: |
| 1406 | + kwargs = { |
| 1407 | + "host", |
| 1408 | + "username", |
| 1409 | + "port", |
| 1410 | + "password", |
| 1411 | + "connect_timeout", |
| 1412 | + "send_receive_timeout", |
| 1413 | + "verify", |
| 1414 | + "query_limit", |
| 1415 | + } |
| 1416 | + return kwargs |
| 1417 | + |
| 1418 | + @property |
| 1419 | + def _engine_adapter(self) -> t.Type[EngineAdapter]: |
| 1420 | + return engine_adapter.ClickhouseEngineAdapter |
| 1421 | + |
| 1422 | + @property |
| 1423 | + def _connection_factory(self) -> t.Callable: |
| 1424 | + from clickhouse_connect.dbapi import connect # type: ignore |
| 1425 | + |
| 1426 | + return connect |
| 1427 | + |
| 1428 | + @property |
| 1429 | + def _extra_engine_config(self) -> t.Dict[str, t.Any]: |
| 1430 | + return {"cluster": self.cluster} |
| 1431 | + |
| 1432 | + @property |
| 1433 | + def _static_connection_kwargs(self) -> t.Dict[str, t.Any]: |
| 1434 | + from sqlmesh import __version__ |
| 1435 | + |
| 1436 | + # False = no compression |
| 1437 | + # True = Clickhouse default compression method |
| 1438 | + # string = specific compression method |
| 1439 | + compress: bool | str = self.use_compression |
| 1440 | + if compress and self.compression_method: |
| 1441 | + compress = self.compression_method |
| 1442 | + |
| 1443 | + return {"compress": compress, "client_name": f"SQLMesh/{__version__}"} |
| 1444 | + |
| 1445 | + |
1367 | 1446 | CONNECTION_CONFIG_TO_TYPE = { |
1368 | 1447 | # Map all subclasses of ConnectionConfig to the value of their `type_` field. |
1369 | 1448 | tpe.all_field_infos()["type_"].default: tpe |
|
0 commit comments