Repository: tobymao/sqlglot

Issue:
[spark dialect] Parser doesn't work when column name starts with digits and used with alias
reproduction:

```python
from sqlglot import parse_one
sql = "select A.1a as b from test_a A"
parse_one(sql, read='spark')
```

error:
```python
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/opt/homebrew/lib/python3.8/site-packages/sqlglot/__init__.py", line 84, in parse_one
    result = dialect.parse(sql, **opts)
  File "/opt/homebrew/lib/python3.8/site-packages/sqlglot/dialects/dialect.py", line 150, in parse
    return self.parser(**opts).parse(self.tokenizer.tokenize(sql), sql)
  File "/opt/homebrew/lib/python3.8/site-packages/sqlglot/parser.py", line 666, in parse
    return self._parse(
  File "/opt/homebrew/lib/python3.8/site-packages/sqlglot/parser.py", line 730, in _parse
    self.raise_error("Invalid expression / Unexpected token")
  File "/opt/homebrew/lib/python3.8/site-packages/sqlglot/parser.py", line 773, in raise_error
    raise error
sqlglot.errors.ParseError: Invalid expression / Unexpected token. Line 1, Col: 13.
  SELECT A.1a as b from test_a A
```

BTW when column name starts with digits but without alias, the parser works, but incorrectly,  the digits are recognized as table name 
```python
from sqlglot import parse_one
sql="""SELECT A.1a from test_a A"""
parse_one(sql, read='spark')
>>> (SELECT expressions: 
  (ALIAS this: 
    (COLUMN this: 
      (IDENTIFIER this: 1, quoted: False), table: 
      (IDENTIFIER this: A, quoted: False)), alias: 
    (IDENTIFIER this: a, quoted: False)), from: 
  (FROM expressions: 
    (TABLE this: 
      (IDENTIFIER this: test_a, quoted: False), alias: 
      (TABLEALIAS this: 
        (IDENTIFIER this: A, quoted: False)))))
```



Discussion:

Patch:
diff --git a/setup.py b/setup.py
index 2c0a3be2..8d8a9237 100644
--- a/setup.py
+++ b/setup.py
@@ -28,7 +28,7 @@ setup(
             "black",
             "duckdb",
             "isort",
-            "mypy",
+            "mypy>=0.990",
             "pandas",
             "pyspark",
             "python-dateutil",
diff --git a/sqlglot/dialects/hive.py b/sqlglot/dialects/hive.py
index 17d5b0c2..f1d5de5e 100644
--- a/sqlglot/dialects/hive.py
+++ b/sqlglot/dialects/hive.py
@@ -175,14 +175,6 @@ class Hive(Dialect):
         ESCAPES = ["\\"]
         ENCODE = "utf-8"
 
-        NUMERIC_LITERALS = {
-            "L": "BIGINT",
-            "S": "SMALLINT",
-            "Y": "TINYINT",
-            "D": "DOUBLE",
-            "F": "FLOAT",
-            "BD": "DECIMAL",
-        }
         KEYWORDS = {
             **tokens.Tokenizer.KEYWORDS,
             "ADD ARCHIVE": TokenType.COMMAND,
@@ -194,6 +186,17 @@ class Hive(Dialect):
             "WITH SERDEPROPERTIES": TokenType.SERDE_PROPERTIES,
         }
 
+        NUMERIC_LITERALS = {
+            "L": "BIGINT",
+            "S": "SMALLINT",
+            "Y": "TINYINT",
+            "D": "DOUBLE",
+            "F": "FLOAT",
+            "BD": "DECIMAL",
+        }
+
+        IDENTIFIER_CAN_START_WITH_DIGIT = True
+
     class Parser(parser.Parser):
         STRICT_CAST = False
 
diff --git a/sqlglot/serde.py b/sqlglot/serde.py
index e2eac045..a47ffdbd 100644
--- a/sqlglot/serde.py
+++ b/sqlglot/serde.py
@@ -4,8 +4,9 @@ import typing as t
 
 from sqlglot import expressions as exp
 
-JSON = t.Union[dict, list, str, float, int, bool]
-Node = t.Union[t.List["Node"], exp.DataType.Type, exp.Expression, JSON]
+if t.TYPE_CHECKING:
+    JSON = t.Union[dict, list, str, float, int, bool]
+    Node = t.Union[t.List["Node"], exp.DataType.Type, exp.Expression, JSON]
 
 
 def dump(node: Node) -> JSON:
diff --git a/sqlglot/tokens.py b/sqlglot/tokens.py
index 32989920..745152ff 100644
--- a/sqlglot/tokens.py
+++ b/sqlglot/tokens.py
@@ -725,6 +725,8 @@ class Tokenizer(metaclass=_Tokenizer):
     COMMENTS = ["--", ("/*", "*/")]
     KEYWORD_TRIE = None  # autofilled
 
+    IDENTIFIER_CAN_START_WITH_DIGIT = False
+
     __slots__ = (
         "sql",
         "size",
@@ -943,16 +945,23 @@ class Tokenizer(metaclass=_Tokenizer):
                 scientific += 1
                 self._advance()
             elif self._peek.isalpha():  # type: ignore
-                self._add(TokenType.NUMBER)
+                number_text = self._text
                 literal = []
                 while self._peek.isalpha():  # type: ignore
                     literal.append(self._peek.upper())  # type: ignore
                     self._advance()
+
                 literal = "".join(literal)  # type: ignore
                 token_type = self.KEYWORDS.get(self.NUMERIC_LITERALS.get(literal))  # type: ignore
+
                 if token_type:
+                    self._add(TokenType.NUMBER, number_text)
                     self._add(TokenType.DCOLON, "::")
                     return self._add(token_type, literal)  # type: ignore
+                elif self.IDENTIFIER_CAN_START_WITH_DIGIT:
+                    return self._add(TokenType.VAR)
+
+                self._add(TokenType.NUMBER, number_text)
                 return self._advance(-len(literal))
             else:
                 return self._add(TokenType.NUMBER)


Test patch:
diff --git a/tests/dialects/test_hive.py b/tests/dialects/test_hive.py
index ae0f1705..41dc6cea 100644
--- a/tests/dialects/test_hive.py
+++ b/tests/dialects/test_hive.py
@@ -338,6 +338,12 @@ class TestHive(Validator):
         )
 
     def test_hive(self):
+        self.validate_all(
+            "SELECT A.1a AS b FROM test_a AS A",
+            write={
+                "spark": "SELECT A.1a AS b FROM test_a AS A",
+            },
+        )
         self.validate_all(
             "PERCENTILE(x, 0.5)",
             write={