Repository: tobymao/sqlglot

Issue:
Parser fails if table property key is not quoted
This is a snippet that reproduces the problem:

```python
import sqlglot

working_sql = """
CREATE TABLE my_table ()
TBLPROPERTIES (
  'delta.dataSkippingNumIndexedCols' = 15
);
"""

broken_sql = """
CREATE TABLE my_table ()
TBLPROPERTIES (
  delta.dataSkippingNumIndexedCols = 15
);
"""

sqlglot.parse_one(working_sql, read="databricks")
print("If the table property key is a string literal it works")

print("But if it is not then it breaks:")
sqlglot.parse_one(broken_sql, read="databricks")
```

According to [Databricks' documentation](https://docs.databricks.com/en/sql/language-manual/sql-ref-syntax-ddl-tblproperties.html#syntax), table property keys can be string literals or dot-separated identifiers. 

Thanks!

Discussion:

Patch:
diff --git a/sqlglot/dialects/hive.py b/sqlglot/dialects/hive.py
index 655e92a6..a4278707 100644
--- a/sqlglot/dialects/hive.py
+++ b/sqlglot/dialects/hive.py
@@ -111,7 +111,7 @@ def _array_sort_sql(self: Hive.Generator, expression: exp.ArraySort) -> str:
 
 
 def _property_sql(self: Hive.Generator, expression: exp.Property) -> str:
-    return f"'{expression.name}'={self.sql(expression, 'value')}"
+    return f"{self.property_name(expression, string_key=True)}={self.sql(expression, 'value')}"
 
 
 def _str_to_unix_sql(self: Hive.Generator, expression: exp.StrToUnix) -> str:
diff --git a/sqlglot/expressions.py b/sqlglot/expressions.py
index 37343332..ae143531 100644
--- a/sqlglot/expressions.py
+++ b/sqlglot/expressions.py
@@ -1175,7 +1175,7 @@ class Column(Condition):
             if self.args.get(part)
         ]
 
-    def to_dot(self) -> Dot:
+    def to_dot(self) -> Dot | Identifier:
         """Converts the column into a dot expression."""
         parts = self.parts
         parent = self.parent
@@ -1185,7 +1185,7 @@ class Column(Condition):
                 parts.append(parent.expression)
             parent = parent.parent
 
-        return Dot.build(deepcopy(parts))
+        return Dot.build(deepcopy(parts)) if len(parts) > 1 else parts[0]
 
 
 class ColumnPosition(Expression):
diff --git a/sqlglot/generator.py b/sqlglot/generator.py
index f3b77219..0d99fb20 100644
--- a/sqlglot/generator.py
+++ b/sqlglot/generator.py
@@ -1084,10 +1084,15 @@ class Generator:
 
         return properties_locs
 
+    def property_name(self, expression: exp.Property, string_key: bool = False) -> str:
+        if isinstance(expression.this, exp.Dot):
+            return self.sql(expression, "this")
+        return f"'{expression.name}'" if string_key else expression.name
+
     def property_sql(self, expression: exp.Property) -> str:
         property_cls = expression.__class__
         if property_cls == exp.Property:
-            return f"{expression.name}={self.sql(expression, 'value')}"
+            return f"{self.property_name(expression)}={self.sql(expression, 'value')}"
 
         property_name = exp.Properties.PROPERTY_TO_NAME.get(property_cls)
         if not property_name:
diff --git a/sqlglot/parser.py b/sqlglot/parser.py
index 51dbcd34..09cf86b8 100644
--- a/sqlglot/parser.py
+++ b/sqlglot/parser.py
@@ -1410,20 +1410,18 @@ class Parser(metaclass=_Parser):
         if self._match_text_seq("SQL", "SECURITY"):
             return self.expression(exp.SqlSecurityProperty, definer=self._match_text_seq("DEFINER"))
 
-        assignment = self._match_pair(
-            TokenType.VAR, TokenType.EQ, advance=False
-        ) or self._match_pair(TokenType.STRING, TokenType.EQ, advance=False)
+        index = self._index
+        key = self._parse_column()
 
-        if assignment:
-            key = self._parse_var_or_string()
-            self._match(TokenType.EQ)
-            return self.expression(
-                exp.Property,
-                this=key,
-                value=self._parse_column() or self._parse_var(any_token=True),
-            )
+        if not self._match(TokenType.EQ):
+            self._retreat(index)
+            return None
 
-        return None
+        return self.expression(
+            exp.Property,
+            this=key.to_dot() if isinstance(key, exp.Column) else key,
+            value=self._parse_column() or self._parse_var(any_token=True),
+        )
 
     def _parse_stored(self) -> exp.FileFormatProperty:
         self._match(TokenType.ALIAS)


Test patch:
diff --git a/tests/dialects/test_databricks.py b/tests/dialects/test_databricks.py
index 239b8510..3df968b0 100644
--- a/tests/dialects/test_databricks.py
+++ b/tests/dialects/test_databricks.py
@@ -6,6 +6,8 @@ class TestDatabricks(Validator):
 
     def test_databricks(self):
         self.validate_identity("CREATE TABLE t (c STRUCT<interval: DOUBLE COMMENT 'aaa'>)")
+        self.validate_identity("CREATE TABLE my_table () TBLPROPERTIES (a.b=15)")
+        self.validate_identity("CREATE TABLE my_table () TBLPROPERTIES ('a.b'=15)")
         self.validate_identity("SELECT CAST('11 23:4:0' AS INTERVAL DAY TO HOUR)")
         self.validate_identity("SELECT CAST('11 23:4:0' AS INTERVAL DAY TO MINUTE)")
         self.validate_identity("SELECT CAST('11 23:4:0' AS INTERVAL DAY TO SECOND)")