diff options
Diffstat (limited to 'sqlglot/dialects/clickhouse.py')
-rw-r--r-- | sqlglot/dialects/clickhouse.py | 56 |
1 files changed, 52 insertions, 4 deletions
diff --git a/sqlglot/dialects/clickhouse.py b/sqlglot/dialects/clickhouse.py index c8a9525..fc48379 100644 --- a/sqlglot/dialects/clickhouse.py +++ b/sqlglot/dialects/clickhouse.py @@ -27,14 +27,15 @@ class ClickHouse(Dialect): class Tokenizer(tokens.Tokenizer): COMMENTS = ["--", "#", "#!", ("/*", "*/")] IDENTIFIERS = ['"', "`"] + STRING_ESCAPES = ["'", "\\"] BIT_STRINGS = [("0b", "")] HEX_STRINGS = [("0x", ""), ("0X", "")] KEYWORDS = { **tokens.Tokenizer.KEYWORDS, - "ASOF": TokenType.ASOF, "ATTACH": TokenType.COMMAND, "DATETIME64": TokenType.DATETIME64, + "DICTIONARY": TokenType.DICTIONARY, "FINAL": TokenType.FINAL, "FLOAT32": TokenType.FLOAT, "FLOAT64": TokenType.DOUBLE, @@ -97,7 +98,6 @@ class ClickHouse(Dialect): TABLE_ALIAS_TOKENS = {*parser.Parser.TABLE_ALIAS_TOKENS} - { TokenType.ANY, - TokenType.ASOF, TokenType.SEMI, TokenType.ANTI, TokenType.SETTINGS, @@ -182,7 +182,7 @@ class ClickHouse(Dialect): return self.expression(exp.CTE, this=statement, alias=statement and statement.this) - def _parse_join_side_and_kind( + def _parse_join_parts( self, ) -> t.Tuple[t.Optional[Token], t.Optional[Token], t.Optional[Token]]: is_global = self._match(TokenType.GLOBAL) and self._prev @@ -201,7 +201,7 @@ class ClickHouse(Dialect): join = super()._parse_join(skip_join_token) if join: - join.set("global", join.args.pop("natural", None)) + join.set("global", join.args.pop("method", None)) return join def _parse_function( @@ -245,6 +245,23 @@ class ClickHouse(Dialect): ) -> t.List[t.Optional[exp.Expression]]: return super()._parse_wrapped_id_vars(optional=True) + def _parse_primary_key( + self, wrapped_optional: bool = False, in_props: bool = False + ) -> exp.Expression: + return super()._parse_primary_key( + wrapped_optional=wrapped_optional or in_props, in_props=in_props + ) + + def _parse_on_property(self) -> t.Optional[exp.Property]: + index = self._index + if self._match_text_seq("CLUSTER"): + this = self._parse_id_var() + if this: + return self.expression(exp.OnCluster, this=this) + else: + self._retreat(index) + return None + class Generator(generator.Generator): STRUCT_DELIMITER = ("(", ")") @@ -292,6 +309,7 @@ class ClickHouse(Dialect): **generator.Generator.PROPERTIES_LOCATION, exp.VolatileProperty: exp.Properties.Location.UNSUPPORTED, exp.PartitionedByProperty: exp.Properties.Location.POST_SCHEMA, + exp.OnCluster: exp.Properties.Location.POST_NAME, } JOIN_HINTS = False @@ -299,6 +317,18 @@ class ClickHouse(Dialect): EXPLICIT_UNION = True GROUPINGS_SEP = "" + # there's no list in docs, but it can be found in Clickhouse code + # see `ClickHouse/src/Parsers/ParserCreate*.cpp` + ON_CLUSTER_TARGETS = { + "DATABASE", + "TABLE", + "VIEW", + "DICTIONARY", + "INDEX", + "FUNCTION", + "NAMED COLLECTION", + } + def cte_sql(self, expression: exp.CTE) -> str: if isinstance(expression.this, exp.Alias): return self.sql(expression, "this") @@ -321,3 +351,21 @@ class ClickHouse(Dialect): def placeholder_sql(self, expression: exp.Placeholder) -> str: return f"{{{expression.name}: {self.sql(expression, 'kind')}}}" + + def oncluster_sql(self, expression: exp.OnCluster) -> str: + return f"ON CLUSTER {self.sql(expression, 'this')}" + + def createable_sql( + self, + expression: exp.Create, + locations: dict[exp.Properties.Location, list[exp.Property]], + ) -> str: + kind = self.sql(expression, "kind").upper() + if kind in self.ON_CLUSTER_TARGETS and locations.get(exp.Properties.Location.POST_NAME): + this_name = self.sql(expression.this, "this") + this_properties = " ".join( + [self.sql(prop) for prop in locations[exp.Properties.Location.POST_NAME]] + ) + this_schema = self.schema_columns_sql(expression.this) + return f"{this_name}{self.sep()}{this_properties}{self.sep()}{this_schema}" + return super().createable_sql(expression, locations) |