summaryrefslogtreecommitdiffstats
path: root/sqlglot/dialects/clickhouse.py
diff options
context:
space:
mode:
Diffstat (limited to 'sqlglot/dialects/clickhouse.py')
-rw-r--r--sqlglot/dialects/clickhouse.py56
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)