Repository: tobymao/sqlglot

Issue:
spark sql SELECT MAX without column alias throws error
**Fully reproducible code snippet**
```sql
spark = SparkSession.builder.config("sqlframe.dialect", "spark").getOrCreate()
df = spark.sql("""
    SELECT
        MAX(col)
    FROM (SELECT 1 as col) t
    """)
```
throws `sqlglot.errors.ParseError: No expression was parsed from ''` because its `name` [here](https://github.com/tobymao/sqlglot/blob/main/sqlglot/dataframe/sql/dataframe.py#L173) is an empty string. This seems to be an issue with expressions that inherit from [Func](https://github.com/tobymao/sqlglot/blob/main/sqlglot/expressions.py#L4330) (MIN, MAX, ABS, etc). Changing [Max](https://github.com/tobymao/sqlglot/blob/main/sqlglot/expressions.py#L5216) to inherit from [Condition](https://github.com/tobymao/sqlglot/blob/main/sqlglot/expressions.py#L903) directly fixes the issue. This isn't a problem for some more complex expressions that leverage multiple inheritance like [DateAdd](https://github.com/tobymao/sqlglot/blob/main/sqlglot/expressions.py#L4700).


**Official Documentation**
https://spark.apache.org/docs/latest/sql-ref-syntax-qry-select.html#parameters - aliases are optional in spark sql


Discussion:

Patch:
diff --git a/sqlglot/dataframe/sql/dataframe.py b/sqlglot/dataframe/sql/dataframe.py
index 0bacbf90..88295749 100644
--- a/sqlglot/dataframe/sql/dataframe.py
+++ b/sqlglot/dataframe/sql/dataframe.py
@@ -18,8 +18,6 @@ from sqlglot.dataframe.sql.transforms import replace_id_value
 from sqlglot.dataframe.sql.util import get_tables_from_expression_with_join
 from sqlglot.dataframe.sql.window import Window
 from sqlglot.helper import ensure_list, object_to_dict, seq_get
-from sqlglot.optimizer import optimize as optimize_func
-from sqlglot.optimizer.qualify_columns import quote_identifiers
 
 if t.TYPE_CHECKING:
     from sqlglot.dataframe.sql._typing import (
@@ -308,9 +306,8 @@ class DataFrame:
         for expression_type, select_expression in select_expressions:
             select_expression = select_expression.transform(replace_id_value, replacement_mapping)
             if optimize:
-                quote_identifiers(select_expression, dialect=dialect)
                 select_expression = t.cast(
-                    exp.Select, optimize_func(select_expression, dialect=dialect)
+                    exp.Select, self.spark._optimize(select_expression, dialect=dialect)
                 )
 
             select_expression = df._replace_cte_names_with_hashes(select_expression)
diff --git a/sqlglot/dataframe/sql/session.py b/sqlglot/dataframe/sql/session.py
index bfc022bd..4e47aaa9 100644
--- a/sqlglot/dataframe/sql/session.py
+++ b/sqlglot/dataframe/sql/session.py
@@ -12,6 +12,8 @@ from sqlglot.dataframe.sql.readwriter import DataFrameReader
 from sqlglot.dataframe.sql.types import StructType
 from sqlglot.dataframe.sql.util import get_column_mapping_from_schema_input
 from sqlglot.helper import classproperty
+from sqlglot.optimizer import optimize
+from sqlglot.optimizer.qualify_columns import quote_identifiers
 
 if t.TYPE_CHECKING:
     from sqlglot.dataframe.sql._typing import ColumnLiterals, SchemaInput
@@ -104,8 +106,15 @@ class SparkSession:
         sel_expression = exp.Select(**select_kwargs)
         return DataFrame(self, sel_expression)
 
+    def _optimize(
+        self, expression: exp.Expression, dialect: t.Optional[Dialect] = None
+    ) -> exp.Expression:
+        dialect = dialect or self.dialect
+        quote_identifiers(expression, dialect=dialect)
+        return optimize(expression, dialect=dialect)
+
     def sql(self, sqlQuery: str) -> DataFrame:
-        expression = sqlglot.parse_one(sqlQuery, read=self.dialect)
+        expression = self._optimize(sqlglot.parse_one(sqlQuery, read=self.dialect))
         if isinstance(expression, exp.Select):
             df = DataFrame(self, expression)
             df = df._convert_leaf_to_cte()


Test patch:
diff --git a/tests/dataframe/integration/test_session.py b/tests/dataframe/integration/test_session.py
index ec500340..3bb3e204 100644
--- a/tests/dataframe/integration/test_session.py
+++ b/tests/dataframe/integration/test_session.py
@@ -34,3 +34,10 @@ class TestSessionFunc(DataFrameValidator):
             .agg(SF.countDistinct(SF.col("employee_id")))
         )
         self.compare_spark_with_sqlglot(df, dfs, skip_schema_compare=True)
+
+    def test_nameless_column(self):
+        query = "SELECT MAX(age) FROM employee"
+        df = self.spark.sql(query)
+        dfs = self.sqlglot.sql(query)
+        # Spark will alias the column to `max(age)` while sqlglot will alias to `_col_0` so their schemas will differ
+        self.compare_spark_with_sqlglot(df, dfs, skip_schema_compare=True)
diff --git a/tests/dataframe/unit/test_session.py b/tests/dataframe/unit/test_session.py
index e2ebae42..848c6032 100644
--- a/tests/dataframe/unit/test_session.py
+++ b/tests/dataframe/unit/test_session.py
@@ -79,7 +79,7 @@ class TestDataframeSession(DataFrameSQLValidator):
         sqlglot.schema.add_table("table", {"cola": "string", "colb": "string"}, dialect="spark")
         df = self.spark.sql(query).groupBy(F.col("cola")).agg(F.sum("colb"))
         self.assertEqual(
-            "WITH t38189 AS (SELECT cola, colb FROM table), t42330 AS (SELECT cola, colb FROM t38189) SELECT cola, SUM(colb) FROM t42330 GROUP BY cola",
+            "WITH t26614 AS (SELECT `table`.`cola` AS `cola`, `table`.`colb` AS `colb` FROM `table` AS `table`), t23454 AS (SELECT cola, colb FROM t26614) SELECT cola, SUM(colb) FROM t23454 GROUP BY cola",
             df.sql(pretty=False, optimize=False)[0],
         )
 
@@ -87,14 +87,14 @@ class TestDataframeSession(DataFrameSQLValidator):
         query = "CREATE TABLE new_table AS WITH t1 AS (SELECT cola, colb FROM table) SELECT cola, colb, FROM t1"
         sqlglot.schema.add_table("table", {"cola": "string", "colb": "string"}, dialect="spark")
         df = self.spark.sql(query)
-        expected = "CREATE TABLE new_table AS SELECT `table`.`cola` AS `cola`, `table`.`colb` AS `colb` FROM `table` AS `table`"
+        expected = "CREATE TABLE `new_table` AS SELECT `table`.`cola` AS `cola`, `table`.`colb` AS `colb` FROM `table` AS `table`"
         self.compare_sql(df, expected)
 
     def test_sql_insert(self):
         query = "WITH t1 AS (SELECT cola, colb FROM table) INSERT INTO new_table SELECT cola, colb FROM t1"
         sqlglot.schema.add_table("table", {"cola": "string", "colb": "string"}, dialect="spark")
         df = self.spark.sql(query)
-        expected = "INSERT INTO new_table SELECT `table`.`cola` AS `cola`, `table`.`colb` AS `colb` FROM `table` AS `table`"
+        expected = "INSERT INTO `new_table` SELECT `table`.`cola` AS `cola`, `table`.`colb` AS `colb` FROM `table` AS `table`"
         self.compare_sql(df, expected)
 
     def test_session_create_builder_patterns(self):