Repository: tobymao/sqlglot

Issue:
Support for clickhouse non-standard join types
ClickHouse supports a few non-traditional join types ([docs](https://clickhouse.com/docs/en/sql-reference/statements/select/join/)):

- `ANY <SIDE> JOIN`
- `LEFT ASOF JOIN`
- `ASOF JOIN`

Would be great if sqlglot supported these! Right now, these are interpreted as aliases for the left side of the join.


Discussion:
Is the form you mention in the first bullet an exception or something? It can't be derived from their syntax rule, and I'm wondering if there are other similar cases (e.g. for `ASOF`) we should handle. Syntax for reference:

```
[GLOBAL] [INNER|LEFT|RIGHT|FULL|CROSS] [OUTER|SEMI|ANTI|ANY|ASOF] JOIN <right_table>
```

(although it does come up in an example mentioned at the bottom of the page you linked..)
I'd be happy to just have the `<SIDE> ANY JOIN` instead of `ANY <SIDE> JOIN` as well. I don't know why the latter isn't specified anywhere.

Resolving patch:
diff --git a/sqlglot/dialects/clickhouse.py b/sqlglot/dialects/clickhouse.py
--- a/sqlglot/dialects/clickhouse.py
+++ b/sqlglot/dialects/clickhouse.py
@@ -21,14 +21,15 @@ class Tokenizer(tokens.Tokenizer):
 
         KEYWORDS = {
             **tokens.Tokenizer.KEYWORDS,
-            "FINAL": TokenType.FINAL,
+            "ASOF": TokenType.ASOF,
             "DATETIME64": TokenType.DATETIME,
-            "INT8": TokenType.TINYINT,
+            "FINAL": TokenType.FINAL,
+            "FLOAT32": TokenType.FLOAT,
+            "FLOAT64": TokenType.DOUBLE,
             "INT16": TokenType.SMALLINT,
             "INT32": TokenType.INT,
             "INT64": TokenType.BIGINT,
-            "FLOAT32": TokenType.FLOAT,
-            "FLOAT64": TokenType.DOUBLE,
+            "INT8": TokenType.TINYINT,
             "TUPLE": TokenType.STRUCT,
         }
 
@@ -38,6 +39,10 @@ class Parser(parser.Parser):
             "MAP": parse_var_map,
         }
 
+        JOIN_KINDS = {*parser.Parser.JOIN_KINDS, TokenType.ANY, TokenType.ASOF}
+
+        TABLE_ALIAS_TOKENS = {*parser.Parser.TABLE_ALIAS_TOKENS} - {TokenType.ANY}
+
         def _parse_table(self, schema=False):
             this = super()._parse_table(schema)
 
diff --git a/sqlglot/tokens.py b/sqlglot/tokens.py
--- a/sqlglot/tokens.py
+++ b/sqlglot/tokens.py
@@ -115,6 +115,7 @@ class TokenType(AutoName):
     APPLY = auto()
     ARRAY = auto()
     ASC = auto()
+    ASOF = auto()
     AT_TIME_ZONE = auto()
     AUTO_INCREMENT = auto()
     BEGIN = auto()


Test patch:
diff --git a/tests/dialects/test_clickhouse.py b/tests/dialects/test_clickhouse.py
--- a/tests/dialects/test_clickhouse.py
+++ b/tests/dialects/test_clickhouse.py
@@ -10,6 +10,10 @@ def test_clickhouse(self):
         self.validate_identity("SELECT * FROM x AS y FINAL")
         self.validate_identity("'a' IN mapKeys(map('a', 1, 'b', 2))")
         self.validate_identity("CAST((1, 2) AS Tuple(a Int8, b Int16))")
+        self.validate_identity("SELECT * FROM foo LEFT ANY JOIN bla")
+        self.validate_identity("SELECT * FROM foo LEFT ASOF JOIN bla")
+        self.validate_identity("SELECT * FROM foo ASOF JOIN bla")
+        self.validate_identity("SELECT * FROM foo ANY JOIN bla")
 
         self.validate_all(
             "SELECT fname, lname, age FROM person ORDER BY age DESC NULLS FIRST, fname ASC NULLS LAST, lname",