Repository: reata/sqllineage

Issue:
subquery without alias raises exception
```
SELECT col1 FROM (SELECT col1 FROM tab1)
```

Subquery without alias name is valid syntax in SparkSQL. And this parsing result says: "An Identifier is expected, got Parenthesis[value: (select col1 from tab1)] instead".

Since we're not assuming any specific SQL dialect, we should support this.

Discussion:

Patch:
diff --git a/sqllineage/core.py b/sqllineage/core.py
index 11ee47e..6c6c494 100644
--- a/sqllineage/core.py
+++ b/sqllineage/core.py
@@ -113,18 +113,23 @@ class LineageAnalyzer:
                 if self.__token_negligible_before_tablename(sub_token):
                     continue
                 else:
-                    if not isinstance(sub_token, Identifier):
+                    if isinstance(sub_token, Identifier):
+                        if isinstance(sub_token.token_first(skip_cm=True), Parenthesis):
+                            # SELECT col1 FROM (SELECT col2 FROM tab1) dt, the subquery will be parsed as Identifier
+                            # and this Identifier's get_real_name method would return alias name dt
+                            # referring https://github.com/andialbrecht/sqlparse/issues/218 for further information
+                            pass
+                        else:
+                            self._lineage_result.read.add(Table.create(sub_token))
+                    elif isinstance(sub_token, Parenthesis):
+                        # SELECT col1 FROM (SELECT col2 FROM tab1), the subquery will be parsed as Parenthesis
+                        # This syntax without alias for subquery is invalid in MySQL, while valid for SparkSQL
+                        pass
+                    else:
                         raise SQLLineageException(
                             "An Identifier is expected, got %s[value: %s] instead"
                             % (type(sub_token).__name__, sub_token)
                         )
-                    if isinstance(sub_token.token_first(skip_cm=True), Parenthesis):
-                        # SELECT col1 FROM (SELECT col2 FROM tab1) dt, the subquery will be parsed as Identifier
-                        # and this Identifier's get_real_name method would return alias name dt
-                        # referring https://github.com/andialbrecht/sqlparse/issues/218 for further information
-                        pass
-                    else:
-                        self._lineage_result.read.add(Table.create(sub_token))
                     source_table_token_flag = False
             elif target_table_token_flag:
                 if self.__token_negligible_before_tablename(sub_token):


Test patch:
diff --git a/tests/test_select.py b/tests/test_select.py
index 30d8ee3..bfbf552 100644
--- a/tests/test_select.py
+++ b/tests/test_select.py
@@ -63,7 +63,12 @@ def test_select_count():
 
 
 def test_select_subquery():
-    helper("SELECT col1 FROM (SELECT col2 FROM tab1) dt", {"tab1"})
+    helper("SELECT col1 FROM (SELECT col1 FROM tab1) dt", {"tab1"})
+
+
+def test_select_subquery_without_alias():
+    """this syntax is valid in SparkSQL, not for MySQL"""
+    helper("SELECT col1 FROM (SELECT col1 FROM tab1)", {"tab1"})
 
 
 def test_select_inner_join():