Repository: reata/sqllineage

Issue:
Missing target table with tsql parsing into statements with union
**Describe the bug**
* There is an issue with tsql parsing into statements with union, unable to obtain target

**SQL**
```sql
select * into t3 from t1 union all select * from t2
```

- `if` CLI (Command Line Interface): provide the command you're calling and the output. 
For example:
```shell
sqllineage -e 'select * into t3 from t1 union all select * from t2' -d tsql
```
```
Statements(#): 1
Source Tables:
    <default>.t2
    <default>.t3
Target Tables:
```

**Python version (available via `python --version`)**
 - 3.12.1

**SQLLineage version (available via `sqllineage --version`):**
 - 1.5.0




Discussion:
The expected output should be:
```
Source Tables:
    <default>.t1
    <default>.t2
Target Tables:
    <default>.t3
```
Is that correct?
Yes, this is correct
Thanks for your confirmation. Looks like we have some issue in handling SELECT INTO + UNION case.
麻烦帮忙修复嘛，这对我真的很重要 0.0。
可以在这里请教一个问题嘛，对于SQL server存储过程的解析，我使用时，不能直接解析，需要去掉create ...  begin ... 之后，只保留sql，才可以。怎样可以直接解析，或者忽略掉 create ..... begin ... 语句，silent_mode = True，添加了，也无法解析.....
You need to do preprocessing yourself, because [sqlfluff](https://github.com/sqlfluff/sqlfluff) does not support stored procedures, not sqllineage.
ok



Patch:
diff --git a/sqllineage/core/parser/sqlfluff/extractors/select.py b/sqllineage/core/parser/sqlfluff/extractors/select.py
index f19a3b07..beee03f8 100644
--- a/sqllineage/core/parser/sqlfluff/extractors/select.py
+++ b/sqllineage/core/parser/sqlfluff/extractors/select.py
@@ -59,12 +59,7 @@ def extract(
         self.extract_subquery(subqueries, holder)
 
         for segment in segments:
-            self._handle_swap_partition(segment, holder)
-            self._handle_select_into(segment, holder)
-            self.tables.extend(
-                self._list_table_from_from_clause_or_join_clause(segment, holder)
-            )
-            self._handle_column(segment)
+            self._handle_select_statement_child_segments(segment, holder)
 
             if is_set_expression(segment):
                 for idx, sub_segment in enumerate(
@@ -75,12 +70,7 @@ def extract(
                             (len(self.columns), len(self.tables))
                         )
                     for seg in list_child_segments(sub_segment):
-                        self.tables.extend(
-                            self._list_table_from_from_clause_or_join_clause(
-                                seg, holder
-                            )
-                        )
-                        self._handle_column(seg)
+                        self._handle_select_statement_child_segments(seg, holder)
 
         self.end_of_query_cleanup(holder)
 
@@ -88,6 +78,16 @@ def extract(
 
         return holder
 
+    def _handle_select_statement_child_segments(
+        self, segment: BaseSegment, holder: SubQueryLineageHolder
+    ):
+        self._handle_swap_partition(segment, holder)
+        self._handle_select_into(segment, holder)
+        self.tables.extend(
+            self._list_table_from_from_clause_or_join_clause(segment, holder)
+        )
+        self._handle_column(segment)
+
     @staticmethod
     def _handle_swap_partition(segment: BaseSegment, holder: SubQueryLineageHolder):
         """


Test patch:
diff --git a/tests/sql/table/test_select_dialect_specific.py b/tests/sql/table/test_select_dialect_specific.py
index 8d2f2823..14aefc64 100644
--- a/tests/sql/table/test_select_dialect_specific.py
+++ b/tests/sql/table/test_select_dialect_specific.py
@@ -60,6 +60,14 @@ def test_select_into(dialect: str):
     assert_table_lineage_equal(sql, {"films"}, {"films_recent"}, dialect=dialect)
 
 
+@pytest.mark.parametrize("dialect", ["postgres", "tsql"])
+def test_select_into_with_union(dialect: str):
+    sql = "SELECT * INTO films_all FROM films UNION ALL SELECT * FROM films_backup"
+    assert_table_lineage_equal(
+        sql, {"films", "films_backup"}, {"films_all"}, dialect=dialect
+    )
+
+
 @pytest.mark.parametrize("dialect", ["athena"])
 def test_select_from_unnest_with_ordinality(dialect: str):
     """