sql-glider 0.1.8__tar.gz → 0.1.10__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (137) hide show
  1. {sql_glider-0.1.8 → sql_glider-0.1.10}/PKG-INFO +1 -1
  2. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/_version.py +2 -2
  3. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/lineage/analyzer.py +28 -1
  4. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/lineage/test_analyzer.py +103 -0
  5. {sql_glider-0.1.8 → sql_glider-0.1.10}/.github/workflows/ci.yml +0 -0
  6. {sql_glider-0.1.8 → sql_glider-0.1.10}/.github/workflows/publish.yml +0 -0
  7. {sql_glider-0.1.8 → sql_glider-0.1.10}/.gitignore +0 -0
  8. {sql_glider-0.1.8 → sql_glider-0.1.10}/.python-version +0 -0
  9. {sql_glider-0.1.8 → sql_glider-0.1.10}/ARCHITECTURE.md +0 -0
  10. {sql_glider-0.1.8 → sql_glider-0.1.10}/CLAUDE.md +0 -0
  11. {sql_glider-0.1.8 → sql_glider-0.1.10}/LICENSE +0 -0
  12. {sql_glider-0.1.8 → sql_glider-0.1.10}/README.md +0 -0
  13. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-05-column-level-lineage.md +0 -0
  14. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-05-reverse-lineage.md +0 -0
  15. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-06-config-file-support.md +0 -0
  16. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-06-graph-lineage.md +0 -0
  17. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-06-unify-single-multi-query.md +0 -0
  18. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-07-sample-data-model.md +0 -0
  19. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-07-sql-templating.md +0 -0
  20. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-08-tables-command.md +0 -0
  21. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-09-graph-query-paths.md +0 -0
  22. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-13-dissect-command.md +0 -0
  23. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2025-12-14-tables-pull-command.md +0 -0
  24. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2026-01-25-fix-union-lineage-chain.md +0 -0
  25. {sql_glider-0.1.8 → sql_glider-0.1.10}/plans/2026-01-26-file-scoped-schema-context.md +0 -0
  26. {sql_glider-0.1.8 → sql_glider-0.1.10}/pyproject.toml +0 -0
  27. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/README.md +0 -0
  28. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/business/expire_dim_customer.sql +0 -0
  29. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/business/load_fact_orders.sql +0 -0
  30. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/business/load_fact_payments.sql +0 -0
  31. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/business/merge_dim_customer.sql +0 -0
  32. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/business/merge_dim_product.sql +0 -0
  33. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/business/update_dim_customer_metrics.sql +0 -0
  34. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/complex/conditional_merge.sql +0 -0
  35. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/complex/cte_insert.sql +0 -0
  36. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/complex/multi_table_transform.sql +0 -0
  37. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/dim_customer.sql +0 -0
  38. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/dim_product.sql +0 -0
  39. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/fact_orders.sql +0 -0
  40. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/fact_payments.sql +0 -0
  41. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/raw_addresses.sql +0 -0
  42. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/raw_customers.sql +0 -0
  43. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/raw_order_items.sql +0 -0
  44. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/raw_orders.sql +0 -0
  45. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/raw_payments.sql +0 -0
  46. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/raw_products.sql +0 -0
  47. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/stg_customers.sql +0 -0
  48. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/stg_orders.sql +0 -0
  49. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/stg_payments.sql +0 -0
  50. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/ddl/stg_products.sql +0 -0
  51. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/incremental/incr_fact_orders.sql +0 -0
  52. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/incremental/incr_fact_payments.sql +0 -0
  53. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/incremental/incr_pres_sales_summary.sql +0 -0
  54. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/maintenance/delete_expired_customers.sql +0 -0
  55. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/maintenance/update_product_status.sql +0 -0
  56. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/presentation/load_pres_customer_360.sql +0 -0
  57. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/presentation/load_pres_customer_cohort.sql +0 -0
  58. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/presentation/load_pres_product_performance.sql +0 -0
  59. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/presentation/load_pres_sales_summary.sql +0 -0
  60. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/staging/load_stg_customers.sql +0 -0
  61. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/staging/load_stg_orders.sql +0 -0
  62. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/staging/load_stg_payments.sql +0 -0
  63. {sql_glider-0.1.8 → sql_glider-0.1.10}/sample_data_model/staging/load_stg_products.sql +0 -0
  64. {sql_glider-0.1.8 → sql_glider-0.1.10}/sqlglider.toml.example +0 -0
  65. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/__init__.py +0 -0
  66. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/catalog/__init__.py +0 -0
  67. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/catalog/base.py +0 -0
  68. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/catalog/databricks.py +0 -0
  69. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/catalog/registry.py +0 -0
  70. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/cli.py +0 -0
  71. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/dissection/__init__.py +0 -0
  72. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/dissection/analyzer.py +0 -0
  73. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/dissection/formatters.py +0 -0
  74. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/dissection/models.py +0 -0
  75. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/global_models.py +0 -0
  76. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/graph/__init__.py +0 -0
  77. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/graph/builder.py +0 -0
  78. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/graph/merge.py +0 -0
  79. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/graph/models.py +0 -0
  80. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/graph/query.py +0 -0
  81. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/graph/serialization.py +0 -0
  82. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/lineage/__init__.py +0 -0
  83. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/lineage/formatters.py +0 -0
  84. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/templating/__init__.py +0 -0
  85. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/templating/base.py +0 -0
  86. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/templating/jinja.py +0 -0
  87. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/templating/registry.py +0 -0
  88. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/templating/variables.py +0 -0
  89. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/utils/__init__.py +0 -0
  90. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/utils/config.py +0 -0
  91. {sql_glider-0.1.8 → sql_glider-0.1.10}/src/sqlglider/utils/file_utils.py +0 -0
  92. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/__init__.py +0 -0
  93. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/multi_file_queries/analytics_pipeline.sql +0 -0
  94. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/multi_file_queries/analytics_pipeline_union_merge.sql +0 -0
  95. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/multi_file_queries/customers.sql +0 -0
  96. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/multi_file_queries/orders.sql +0 -0
  97. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/multi_file_queries/reports.sql +0 -0
  98. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/multi_file_queries/view_based_merge.sql +0 -0
  99. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_cte.sql +0 -0
  100. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_cte_query.sql +0 -0
  101. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_cte_view_star.sql +0 -0
  102. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_generated_column_query.sql +0 -0
  103. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_multi.sql +0 -0
  104. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_multi_query.sql +0 -0
  105. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_single_query.sql +0 -0
  106. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_subquery.sql +0 -0
  107. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_tables.sql +0 -0
  108. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_view.sql +0 -0
  109. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/original_queries/test_view_window_cte.sql +0 -0
  110. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/fixtures/sample_manifest.csv +0 -0
  111. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/__init__.py +0 -0
  112. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/catalog/__init__.py +0 -0
  113. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/catalog/test_base.py +0 -0
  114. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/catalog/test_databricks.py +0 -0
  115. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/catalog/test_registry.py +0 -0
  116. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/dissection/__init__.py +0 -0
  117. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/dissection/test_analyzer.py +0 -0
  118. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/dissection/test_formatters.py +0 -0
  119. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/dissection/test_models.py +0 -0
  120. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/graph/__init__.py +0 -0
  121. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/graph/test_builder.py +0 -0
  122. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/graph/test_merge.py +0 -0
  123. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/graph/test_models.py +0 -0
  124. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/graph/test_query.py +0 -0
  125. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/graph/test_serialization.py +0 -0
  126. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/lineage/__init__.py +0 -0
  127. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/lineage/test_formatters.py +0 -0
  128. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/templating/__init__.py +0 -0
  129. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/templating/test_base.py +0 -0
  130. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/templating/test_jinja.py +0 -0
  131. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/templating/test_registry.py +0 -0
  132. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/templating/test_variables.py +0 -0
  133. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/test_cli.py +0 -0
  134. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/utils/__init__.py +0 -0
  135. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/utils/test_config.py +0 -0
  136. {sql_glider-0.1.8 → sql_glider-0.1.10}/tests/sqlglider/utils/test_file_utils.py +0 -0
  137. {sql_glider-0.1.8 → sql_glider-0.1.10}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sql-glider
3
- Version: 0.1.8
3
+ Version: 0.1.10
4
4
  Summary: SQL Utility Toolkit for better understanding, use, and governance of your queries in a native environment.
5
5
  Project-URL: Homepage, https://github.com/rycowhi/sql-glider/
6
6
  Project-URL: Repository, https://github.com/rycowhi/sql-glider/
@@ -28,7 +28,7 @@ version_tuple: VERSION_TUPLE
28
28
  commit_id: COMMIT_ID
29
29
  __commit_id__: COMMIT_ID
30
30
 
31
- __version__ = version = '0.1.8'
32
- __version_tuple__ = version_tuple = (0, 1, 8)
31
+ __version__ = version = '0.1.10'
32
+ __version_tuple__ = version_tuple = (0, 1, 10)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -615,6 +615,12 @@ class LineageAnalyzer:
615
615
  if isinstance(target, exp.Table):
616
616
  return (self._get_qualified_table_name(target), ObjectType.UNKNOWN)
617
617
 
618
+ # CACHE TABLE
619
+ elif isinstance(self.expr, exp.Cache):
620
+ target = self.expr.this
621
+ if isinstance(target, exp.Table):
622
+ return (self._get_qualified_table_name(target), ObjectType.TABLE)
623
+
618
624
  # DELETE FROM table
619
625
  elif isinstance(self.expr, exp.Delete):
620
626
  target = self.expr.this
@@ -706,6 +712,10 @@ class LineageAnalyzer:
706
712
  elif isinstance(self.expr, exp.Drop):
707
713
  return table_node is self.expr.this
708
714
 
715
+ # For CACHE TABLE, the target is self.expr.this
716
+ elif isinstance(self.expr, exp.Cache):
717
+ return table_node is self.expr.this
718
+
709
719
  return False
710
720
 
711
721
  def _analyze_column_lineage_internal(
@@ -741,7 +751,12 @@ class LineageAnalyzer:
741
751
 
742
752
  lineage_items = []
743
753
  # Get SQL for current expression only (not full multi-query SQL)
744
- current_query_sql = self.expr.sql(dialect=self.dialect)
754
+ # For CACHE TABLE, pass just the SELECT since sqlglot.lineage doesn't
755
+ # natively understand CACHE statements
756
+ if isinstance(self.expr, exp.Cache) and self.expr.expression:
757
+ current_query_sql = self.expr.expression.sql(dialect=self.dialect)
758
+ else:
759
+ current_query_sql = self.expr.sql(dialect=self.dialect)
745
760
 
746
761
  for col in columns_to_analyze:
747
762
  try:
@@ -889,6 +904,7 @@ class LineageAnalyzer:
889
904
  "Drop": f"DROP {getattr(target_expr, 'kind', '')}".strip(),
890
905
  "Alter": "ALTER",
891
906
  "Truncate": "TRUNCATE",
907
+ "Cache": "CACHE TABLE",
892
908
  "Command": "COMMAND",
893
909
  }
894
910
 
@@ -943,6 +959,17 @@ class LineageAnalyzer:
943
959
  ):
944
960
  return (target_name, select_node)
945
961
 
962
+ # Check for CACHE TABLE AS SELECT
963
+ elif isinstance(self.expr, exp.Cache):
964
+ target = self.expr.this
965
+ if isinstance(target, exp.Table):
966
+ target_name = self._get_qualified_table_name(target)
967
+ select_node = self.expr.expression
968
+ if isinstance(
969
+ select_node, (exp.Select, exp.Union, exp.Intersect, exp.Except)
970
+ ):
971
+ return (target_name, select_node)
972
+
946
973
  # Check for MERGE statement
947
974
  elif isinstance(self.expr, exp.Merge):
948
975
  target = self.expr.this
@@ -2820,3 +2820,106 @@ class TestSemiAntiJoinColumnResolution:
2820
2820
  analyzer_semi = LineageAnalyzer(sql_semi, dialect="spark")
2821
2821
  analyzer_semi.analyze_queries(level=AnalysisLevel.COLUMN)
2822
2822
  assert set(analyzer_semi._file_schema["v3"].keys()) == {"a"}
2823
+
2824
+
2825
+ class TestCacheTableStatements:
2826
+ """Tests for Spark SQL CACHE TABLE statement support."""
2827
+
2828
+ def test_cache_table_as_select_column_lineage(self):
2829
+ """CACHE TABLE t AS SELECT should trace columns through to sources."""
2830
+ sql = """
2831
+ CACHE TABLE cached_customers AS
2832
+ SELECT customer_id, customer_name FROM customers
2833
+ """
2834
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2835
+ results = analyzer.analyze_queries(level=AnalysisLevel.COLUMN)
2836
+
2837
+ assert len(results) == 1
2838
+ items = {
2839
+ item.output_name: item.source_name for item in results[0].lineage_items
2840
+ }
2841
+ assert items["cached_customers.customer_id"] == "customers.customer_id"
2842
+ assert items["cached_customers.customer_name"] == "customers.customer_name"
2843
+
2844
+ def test_cache_lazy_table_column_lineage(self):
2845
+ """CACHE LAZY TABLE should trace columns identically to CACHE TABLE."""
2846
+ sql = """
2847
+ CACHE LAZY TABLE cached_customers AS
2848
+ SELECT customer_id, customer_name FROM customers
2849
+ """
2850
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2851
+ results = analyzer.analyze_queries(level=AnalysisLevel.COLUMN)
2852
+
2853
+ assert len(results) == 1
2854
+ items = {
2855
+ item.output_name: item.source_name for item in results[0].lineage_items
2856
+ }
2857
+ assert items["cached_customers.customer_id"] == "customers.customer_id"
2858
+ assert items["cached_customers.customer_name"] == "customers.customer_name"
2859
+
2860
+ def test_cache_table_as_select_table_extraction(self):
2861
+ """CACHE TABLE t AS SELECT should show cached_orders as OUTPUT table."""
2862
+ sql = """
2863
+ CACHE TABLE cached_orders AS
2864
+ SELECT order_id, total FROM orders
2865
+ """
2866
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2867
+ results = analyzer.analyze_tables()
2868
+
2869
+ assert len(results) == 1
2870
+ tables_by_name = {t.name: t for t in results[0].tables}
2871
+
2872
+ assert "cached_orders" in tables_by_name
2873
+ assert tables_by_name["cached_orders"].usage.value == "OUTPUT"
2874
+ assert tables_by_name["cached_orders"].object_type.value == "TABLE"
2875
+
2876
+ assert "orders" in tables_by_name
2877
+ assert tables_by_name["orders"].usage.value == "INPUT"
2878
+
2879
+ def test_cache_table_as_select_with_join(self):
2880
+ """CACHE TABLE with a JOIN query should trace all sources."""
2881
+ sql = """
2882
+ CACHE TABLE summary AS
2883
+ SELECT c.customer_id, o.total
2884
+ FROM customers c
2885
+ JOIN orders o ON c.id = o.customer_id
2886
+ """
2887
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2888
+ results = analyzer.analyze_tables()
2889
+
2890
+ assert len(results) == 1
2891
+ tables_by_name = {t.name: t for t in results[0].tables}
2892
+
2893
+ assert "summary" in tables_by_name
2894
+ assert tables_by_name["summary"].usage.value == "OUTPUT"
2895
+ assert "customers" in tables_by_name
2896
+ assert tables_by_name["customers"].usage.value == "INPUT"
2897
+ assert "orders" in tables_by_name
2898
+ assert tables_by_name["orders"].usage.value == "INPUT"
2899
+
2900
+ def test_bare_cache_table_is_skipped(self):
2901
+ """CACHE TABLE t (without AS SELECT) should be skipped."""
2902
+ sql = "CACHE TABLE my_table"
2903
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2904
+ results = analyzer.analyze_queries(level=AnalysisLevel.COLUMN)
2905
+
2906
+ assert len(results) == 0
2907
+ skipped = analyzer.skipped_queries
2908
+ assert len(skipped) == 1
2909
+ assert "CACHE" in skipped[0].statement_type
2910
+
2911
+ def test_cache_table_in_multi_query(self):
2912
+ """CACHE TABLE should work alongside other statements in multi-query files."""
2913
+ sql = """
2914
+ SELECT id FROM users;
2915
+ CACHE TABLE cached_orders AS SELECT order_id FROM orders;
2916
+ DELETE FROM old_data;
2917
+ """
2918
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2919
+ results = analyzer.analyze_queries(level=AnalysisLevel.COLUMN)
2920
+
2921
+ # SELECT and CACHE TABLE should produce results; DELETE is skipped
2922
+ assert len(results) == 2
2923
+ skipped = analyzer.skipped_queries
2924
+ assert len(skipped) == 1
2925
+ assert "DELETE" in skipped[0].statement_type
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes