diff --git a/dlt/common/data_writers/escape.py b/dlt/common/data_writers/escape.py index 190ce4b2d7..a606906774 100644 --- a/dlt/common/data_writers/escape.py +++ b/dlt/common/data_writers/escape.py @@ -137,42 +137,5 @@ def escape_databricks_literal(v: Any) -> Any: return "NULL" if v is None else str(v) -# https://github.com/ClickHouse/ClickHouse/blob/master/docs/en/sql-reference/syntax.md#string -CLICKHOUSE_ESCAPE_DICT = { - "'": "''", - "\\": "\\\\", - "\n": "\\n", - "\t": "\\t", - "\b": "\\b", - "\f": "\\f", - "\r": "\\r", - "\0": "\\0", - "\a": "\\a", - "\v": "\\v", -} - -CLICKHOUSE_ESCAPE_RE = _make_sql_escape_re(CLICKHOUSE_ESCAPE_DICT) - - -def escape_clickhouse_literal(v: Any) -> Any: - if isinstance(v, str): - return _escape_extended( - v, prefix="'", escape_dict=CLICKHOUSE_ESCAPE_DICT, escape_re=CLICKHOUSE_ESCAPE_RE - ) - if isinstance(v, (datetime, date, time)): - return f"'{v.isoformat()}'" - if isinstance(v, (list, dict)): - return _escape_extended( - json.dumps(v), - prefix="'", - escape_dict=CLICKHOUSE_ESCAPE_DICT, - escape_re=CLICKHOUSE_ESCAPE_RE, - ) - if isinstance(v, bytes): - return f"'{v.hex()}'" - return "NULL" if v is None else str(v) - - -def escape_clickhouse_identifier(v: str, quote_char: str = "`") -> str: - quote_char = quote_char if quote_char in {'"', "`"} else "`" - return quote_char + v.replace(quote_char, quote_char * 2).replace("\\", "\\\\") + quote_char +def escape_clickhouse_identifier(v: str) -> str: + return '`' + v.replace('`', '``').replace("\\", "\\\\") + '"' diff --git a/dlt/destinations/impl/clickhouse/__init__.py b/dlt/destinations/impl/clickhouse/__init__.py index 35ef6dad35..236e83ef54 100644 --- a/dlt/destinations/impl/clickhouse/__init__.py +++ b/dlt/destinations/impl/clickhouse/__init__.py @@ -1,18 +1,17 @@ from dlt.common.arithmetics import DEFAULT_NUMERIC_PRECISION, DEFAULT_NUMERIC_SCALE -from dlt.common.data_writers.escape import escape_clickhouse_identifier, escape_clickhouse_literal +from dlt.common.data_writers.escape import escape_clickhouse_identifier from dlt.common.destination import DestinationCapabilitiesContext def capabilities() -> DestinationCapabilitiesContext: caps = DestinationCapabilitiesContext() - caps.preferred_loader_file_format = "parquet" - caps.supported_loader_file_formats = ["jsonl", "parquet", "insert_values"] - caps.preferred_staging_file_format = "parquet" - caps.supported_staging_file_formats = ["jsonl", "parquet"] + caps.preferred_loader_file_format = "jsonl" + caps.supported_loader_file_formats = ["jsonl", "parquet", "arrow"] + caps.preferred_staging_file_format = "jsonl" + caps.supported_staging_file_formats = ["jsonl", "parquet", "arrow"] caps.escape_identifier = escape_clickhouse_identifier - caps.escape_literal = escape_clickhouse_literal caps.schema_supports_numeric_precision = True # Use 'Decimal128' with these defaults. @@ -25,8 +24,8 @@ def capabilities() -> DestinationCapabilitiesContext: caps.is_max_query_length_in_bytes = True caps.max_query_length = 262144 - # Clickhouse has limited support for transactional semantics, especially for `ReplicatedMergeTree`, the default ClickHouse Cloud engine. - # It does, however, provide atomicity for individual DDL operations like `ALTER TABLE`. + # Clickhouse has limited support for transactional semantics, especially for `ReplicatedMergeTree`, + # the default ClickHouse Cloud engine. It does, however, provide atomicity for individual DDL operations like `ALTER TABLE`. # https://clickhouse-driver.readthedocs.io/en/latest/dbapi.html#clickhouse_driver.dbapi.connection.Connection.commit # https://clickhouse.com/docs/en/guides/developer/transactional#transactions-commit-and-rollback caps.supports_transactions = False