diff --git a/pypika/dialects.py b/pypika/dialects.py index dab72e07..b91df3c3 100644 --- a/pypika/dialects.py +++ b/pypika/dialects.py @@ -798,12 +798,18 @@ def __init__(self, **kwargs) -> None: super().__init__(**kwargs) self._sample = None self._sample_offset = None + self._settings = None @builder def sample(self, sample: int, offset: Optional[int] = None) -> "ClickHouseQueryBuilder": self._sample = sample self._sample_offset = offset + @builder + def settings(self, **kwargs: Any) -> "ClickHouseQueryBuilder": + self._settings = self._settings.copy() if self._settings else {} + self._settings.update(kwargs) + @staticmethod def _delete_sql(**kwargs: Any) -> str: return 'ALTER TABLE' @@ -820,6 +826,8 @@ def _from_sql(self, with_namespace: bool = False, **kwargs: Any) -> str: clauses.append(f"SAMPLE {self._sample}") if self._sample_offset is not None: clauses.append(f"OFFSET {self._sample_offset}") + if self._settings: + clauses.append(f"SETTINGS {', '.join(f'{k}={v}' for k, v in sorted(self._settings.items()))}") return " FROM {clauses}".format(clauses=" ".join(clauses)) def _set_sql(self, **kwargs: Any) -> str: diff --git a/pypika/tests/dialects/test_clickhouse.py b/pypika/tests/dialects/test_clickhouse.py index 8da62701..93c43cd7 100644 --- a/pypika/tests/dialects/test_clickhouse.py +++ b/pypika/tests/dialects/test_clickhouse.py @@ -23,6 +23,14 @@ def test_use_SAMPLE_with_offset_keyword(self): query = ClickHouseQuery.from_(t).select(t.foo).sample(10, 5) self.assertEqual(str(query), 'SELECT "foo" FROM "abc" SAMPLE 10 OFFSET 5') + def test_settings(self) -> None: + t = Table('abc') + query1 = ClickHouseQuery.from_(t).select(t.foo).settings(foo="bar") + query2 = query1.settings(baz="qux") + # Settings get deep-copied: + self.assertEqual(str(query1), 'SELECT "foo" FROM "abc" SETTINGS foo=bar') + # Settings are ordered alphabetically in the query string: + self.assertEqual(str(query2), 'SELECT "foo" FROM "abc" SETTINGS baz=qux, foo=bar') class ClickHouseDeleteTests(TestCase): table_abc = Table("abc")