Repository: tobymao/sqlglot

Issue:
BUG: Using "left" and "right" as table identifiers is valid in snowflake but raises ParseError in sqlglot.transpile(read="snowflake")
Hi, this project looks great! I am trying to use it for snowflake, but hit a bug because I use `LEFT` and `RIGHT` as table aliases in snowflake, and `sqlglot.transpile` doesn't allow that. I'm on sqlglot 10.6.3, python 3.10.4. Thank you!

```python
import sqlglot

# works in snowflake, but raises KeyError
print(sqlglot.transpile("SELECT LEFT.C_CUSTKEY FROM CUSTOMER AS LEFT", read="snowflake"))
# this query is allowed
print(sqlglot.transpile("SELECT _LEFT.C_CUSTKEY FROM CUSTOMER AS _LEFT", read="snowflake"))
```

<details>
<summary>Stack trace</summary>

```python-backtrace
---------------------------------------------------------------------------
ParseError                                Traceback (most recent call last)
Cell In[52], line 4
      1 import sqlglot
      3 # raises KeyError
----> 4 print(sqlglot.transpile("SELECT LEFT.C_CUSTKEY FROM CUSTOMER AS LEFT", read="snowflake"))
      5 # this query is allowed
      6 print(sqlglot.transpile("SELECT _LEFT.C_CUSTKEY FROM CUSTOMER AS _LEFT", read="snowflake"))

File ~/opt/anaconda3/envs/ponder-product-testing/lib/python3.10/site-packages/sqlglot/__init__.py:176, in transpile(sql, read, write, identity, error_level, **opts)
    157 """
    158 Parses the given SQL string in accordance with the source dialect and returns a list of SQL strings transformed
    159 to conform to the target dialect. Each string in the returned list represents a single transformed SQL statement.
   (...)
    171     The list of transpiled SQL statements.
    172 """
    173 write = write or read if identity else write
    174 return [
    175     Dialect.get_or_raise(write)().generate(expression, **opts)
--> 176     for expression in parse(sql, read, error_level=error_level)
    177 ]

File ~/opt/anaconda3/envs/ponder-product-testing/lib/python3.10/site-packages/sqlglot/__init__.py:64, in parse(sql, read, **opts)
     52 """
     53 Parses the given SQL string into a collection of syntax trees, one per parsed SQL statement.
     54
   (...)
     61     The resulting syntax tree collection.
     62 """
     63 dialect = Dialect.get_or_raise(read)()
---> 64 return dialect.parse(sql, **opts)

File ~/opt/anaconda3/envs/ponder-product-testing/lib/python3.10/site-packages/sqlglot/dialects/dialect.py:157, in Dialect.parse(self, sql, **opts)
    156 def parse(self, sql, **opts):
--> 157     return self.parser(**opts).parse(self.tokenizer.tokenize(sql), sql)

File ~/opt/anaconda3/envs/ponder-product-testing/lib/python3.10/site-packages/sqlglot/parser.py:703, in Parser.parse(self, raw_tokens, sql)
    689 def parse(
    690     self, raw_tokens: t.List[Token], sql: t.Optional[str] = None
    691 ) -> t.List[t.Optional[exp.Expression]]:
    692     """
    693     Parses a list of tokens and returns a list of syntax trees, one tree
    694     per parsed SQL statement.
   (...)
    701         The list of syntax trees.
    702     """
--> 703     return self._parse(
    704         parse_method=self.__class__._parse_statement, raw_tokens=raw_tokens, sql=sql
    705     )

File ~/opt/anaconda3/envs/ponder-product-testing/lib/python3.10/site-packages/sqlglot/parser.py:769, in Parser._parse(self, parse_method, raw_tokens, sql)
    766     expressions.append(parse_method(self))
    768     if self._index < len(self._tokens):
--> 769         self.raise_error("Invalid expression / Unexpected token")
    771     self.check_errors()
    773 return expressions

File ~/opt/anaconda3/envs/ponder-product-testing/lib/python3.10/site-packages/sqlglot/parser.py:812, in Parser.raise_error(self, message, token)
    800 error = ParseError.new(
    801     f"{message}. Line {token.line}, Col: {token.col}.\n"
    802     f"  {start_context}\033[4m{highlight}\033[0m{end_context}",
   (...)
    808     end_context=end_context,
    809 )
    811 if self.error_level == ErrorLevel.IMMEDIATE:
--> 812     raise error
    814 self.errors.append(error)

ParseError: Invalid expression / Unexpected token. Line 1, Col: 12.
  SELECT LEFT.C_CUSTKEY FROM CUSTOMER AS LEFT
```
</details>


Discussion:

Resolving patch:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -194,6 +194,7 @@ class Parser(metaclass=_Parser):
         TokenType.INTERVAL,
         TokenType.LAZY,
         TokenType.LEADING,
+        TokenType.LEFT,
         TokenType.LOCAL,
         TokenType.MATERIALIZED,
         TokenType.MERGE,
@@ -208,6 +209,7 @@ class Parser(metaclass=_Parser):
         TokenType.PRECEDING,
         TokenType.RANGE,
         TokenType.REFERENCES,
+        TokenType.RIGHT,
         TokenType.ROW,
         TokenType.ROWS,
         TokenType.SCHEMA,
@@ -237,8 +239,10 @@ class Parser(metaclass=_Parser):
 
     TABLE_ALIAS_TOKENS = ID_VAR_TOKENS - {
         TokenType.APPLY,
+        TokenType.LEFT,
         TokenType.NATURAL,
         TokenType.OFFSET,
+        TokenType.RIGHT,
         TokenType.WINDOW,
     }
 


Test patch:
diff --git a/tests/fixtures/identity.sql b/tests/fixtures/identity.sql
--- a/tests/fixtures/identity.sql
+++ b/tests/fixtures/identity.sql
@@ -721,3 +721,6 @@ ALTER TABLE a ADD PRIMARY KEY (x, y) NOT ENFORCED
 ALTER TABLE a ADD FOREIGN KEY (x, y) REFERENCES bla
 SELECT end FROM a
 SELECT id FROM b.a AS a QUALIFY ROW_NUMBER() OVER (PARTITION BY br ORDER BY sadf DESC) = 1
+SELECT LEFT.FOO FROM BLA AS LEFT
+SELECT RIGHT.FOO FROM BLA AS RIGHT
+SELECT LEFT FROM LEFT LEFT JOIN RIGHT RIGHT JOIN LEFT