sql-glider 0.1.18__tar.gz → 0.1.19__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 (150) hide show
  1. {sql_glider-0.1.18 → sql_glider-0.1.19}/PKG-INFO +1 -1
  2. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/_version.py +2 -2
  3. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/lineage/analyzer.py +29 -19
  4. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/lineage/test_analyzer.py +56 -0
  5. {sql_glider-0.1.18 → sql_glider-0.1.19}/.github/workflows/ci.yml +0 -0
  6. {sql_glider-0.1.18 → sql_glider-0.1.19}/.github/workflows/publish.yml +0 -0
  7. {sql_glider-0.1.18 → sql_glider-0.1.19}/.gitignore +0 -0
  8. {sql_glider-0.1.18 → sql_glider-0.1.19}/.python-version +0 -0
  9. {sql_glider-0.1.18 → sql_glider-0.1.19}/ARCHITECTURE.md +0 -0
  10. {sql_glider-0.1.18 → sql_glider-0.1.19}/CLAUDE.md +0 -0
  11. {sql_glider-0.1.18 → sql_glider-0.1.19}/LICENSE +0 -0
  12. {sql_glider-0.1.18 → sql_glider-0.1.19}/README.md +0 -0
  13. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-05-column-level-lineage.md +0 -0
  14. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-05-reverse-lineage.md +0 -0
  15. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-06-config-file-support.md +0 -0
  16. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-06-graph-lineage.md +0 -0
  17. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-06-unify-single-multi-query.md +0 -0
  18. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-07-sample-data-model.md +0 -0
  19. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-07-sql-templating.md +0 -0
  20. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-08-tables-command.md +0 -0
  21. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-09-graph-query-paths.md +0 -0
  22. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-13-dissect-command.md +0 -0
  23. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2025-12-14-tables-pull-command.md +0 -0
  24. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2026-01-25-fix-union-lineage-chain.md +0 -0
  25. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2026-01-26-file-scoped-schema-context.md +0 -0
  26. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2026-01-28-sparksql-table-extraction.md +0 -0
  27. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2026-01-29-no-star-flag.md +0 -0
  28. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2026-01-29-resolve-schema.md +0 -0
  29. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2026-01-29-schema-pruning-optimization.md +0 -0
  30. {sql_glider-0.1.18 → sql_glider-0.1.19}/plans/2026-01-29-tables-scrape-command.md +0 -0
  31. {sql_glider-0.1.18 → sql_glider-0.1.19}/pyproject.toml +0 -0
  32. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/README.md +0 -0
  33. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/business/expire_dim_customer.sql +0 -0
  34. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/business/load_fact_orders.sql +0 -0
  35. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/business/load_fact_payments.sql +0 -0
  36. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/business/merge_dim_customer.sql +0 -0
  37. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/business/merge_dim_product.sql +0 -0
  38. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/business/update_dim_customer_metrics.sql +0 -0
  39. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/complex/conditional_merge.sql +0 -0
  40. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/complex/cte_insert.sql +0 -0
  41. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/complex/multi_table_transform.sql +0 -0
  42. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/dim_customer.sql +0 -0
  43. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/dim_product.sql +0 -0
  44. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/fact_orders.sql +0 -0
  45. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/fact_payments.sql +0 -0
  46. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/raw_addresses.sql +0 -0
  47. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/raw_customers.sql +0 -0
  48. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/raw_order_items.sql +0 -0
  49. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/raw_orders.sql +0 -0
  50. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/raw_payments.sql +0 -0
  51. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/raw_products.sql +0 -0
  52. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/stg_customers.sql +0 -0
  53. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/stg_orders.sql +0 -0
  54. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/stg_payments.sql +0 -0
  55. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/ddl/stg_products.sql +0 -0
  56. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/incremental/incr_fact_orders.sql +0 -0
  57. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/incremental/incr_fact_payments.sql +0 -0
  58. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/incremental/incr_pres_sales_summary.sql +0 -0
  59. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/maintenance/delete_expired_customers.sql +0 -0
  60. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/maintenance/update_product_status.sql +0 -0
  61. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/presentation/load_pres_customer_360.sql +0 -0
  62. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/presentation/load_pres_customer_cohort.sql +0 -0
  63. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/presentation/load_pres_product_performance.sql +0 -0
  64. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/presentation/load_pres_sales_summary.sql +0 -0
  65. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/staging/load_stg_customers.sql +0 -0
  66. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/staging/load_stg_orders.sql +0 -0
  67. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/staging/load_stg_payments.sql +0 -0
  68. {sql_glider-0.1.18 → sql_glider-0.1.19}/sample_data_model/staging/load_stg_products.sql +0 -0
  69. {sql_glider-0.1.18 → sql_glider-0.1.19}/sqlglider.toml.example +0 -0
  70. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/__init__.py +0 -0
  71. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/catalog/__init__.py +0 -0
  72. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/catalog/base.py +0 -0
  73. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/catalog/databricks.py +0 -0
  74. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/catalog/registry.py +0 -0
  75. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/cli.py +0 -0
  76. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/dissection/__init__.py +0 -0
  77. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/dissection/analyzer.py +0 -0
  78. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/dissection/formatters.py +0 -0
  79. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/dissection/models.py +0 -0
  80. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/global_models.py +0 -0
  81. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/graph/__init__.py +0 -0
  82. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/graph/builder.py +0 -0
  83. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/graph/formatters.py +0 -0
  84. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/graph/merge.py +0 -0
  85. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/graph/models.py +0 -0
  86. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/graph/query.py +0 -0
  87. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/graph/serialization.py +0 -0
  88. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/lineage/__init__.py +0 -0
  89. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/lineage/formatters.py +0 -0
  90. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/schema/__init__.py +0 -0
  91. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/schema/extractor.py +0 -0
  92. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/templating/__init__.py +0 -0
  93. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/templating/base.py +0 -0
  94. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/templating/jinja.py +0 -0
  95. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/templating/registry.py +0 -0
  96. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/templating/variables.py +0 -0
  97. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/utils/__init__.py +0 -0
  98. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/utils/config.py +0 -0
  99. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/utils/file_utils.py +0 -0
  100. {sql_glider-0.1.18 → sql_glider-0.1.19}/src/sqlglider/utils/schema.py +0 -0
  101. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/__init__.py +0 -0
  102. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/multi_file_queries/analytics_pipeline.sql +0 -0
  103. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/multi_file_queries/analytics_pipeline_union_merge.sql +0 -0
  104. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/multi_file_queries/customers.sql +0 -0
  105. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/multi_file_queries/orders.sql +0 -0
  106. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/multi_file_queries/reports.sql +0 -0
  107. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/multi_file_queries/view_based_merge.sql +0 -0
  108. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_cte.sql +0 -0
  109. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_cte_query.sql +0 -0
  110. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_cte_view_star.sql +0 -0
  111. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_generated_column_query.sql +0 -0
  112. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_multi.sql +0 -0
  113. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_multi_query.sql +0 -0
  114. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_single_query.sql +0 -0
  115. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_subquery.sql +0 -0
  116. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_tables.sql +0 -0
  117. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_view.sql +0 -0
  118. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/original_queries/test_view_window_cte.sql +0 -0
  119. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/fixtures/sample_manifest.csv +0 -0
  120. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/__init__.py +0 -0
  121. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/catalog/__init__.py +0 -0
  122. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/catalog/test_base.py +0 -0
  123. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/catalog/test_databricks.py +0 -0
  124. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/catalog/test_registry.py +0 -0
  125. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/dissection/__init__.py +0 -0
  126. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/dissection/test_analyzer.py +0 -0
  127. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/dissection/test_formatters.py +0 -0
  128. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/dissection/test_models.py +0 -0
  129. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/graph/__init__.py +0 -0
  130. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/graph/test_builder.py +0 -0
  131. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/graph/test_formatters.py +0 -0
  132. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/graph/test_merge.py +0 -0
  133. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/graph/test_models.py +0 -0
  134. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/graph/test_query.py +0 -0
  135. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/graph/test_serialization.py +0 -0
  136. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/lineage/__init__.py +0 -0
  137. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/lineage/test_formatters.py +0 -0
  138. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/schema/__init__.py +0 -0
  139. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/schema/test_extractor.py +0 -0
  140. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/templating/__init__.py +0 -0
  141. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/templating/test_base.py +0 -0
  142. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/templating/test_jinja.py +0 -0
  143. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/templating/test_registry.py +0 -0
  144. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/templating/test_variables.py +0 -0
  145. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/test_cli.py +0 -0
  146. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/utils/__init__.py +0 -0
  147. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/utils/test_config.py +0 -0
  148. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/utils/test_file_utils.py +0 -0
  149. {sql_glider-0.1.18 → sql_glider-0.1.19}/tests/sqlglider/utils/test_schema.py +0 -0
  150. {sql_glider-0.1.18 → sql_glider-0.1.19}/uv.lock +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sql-glider
3
- Version: 0.1.18
3
+ Version: 0.1.19
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.18'
32
- __version_tuple__ = version_tuple = (0, 1, 18)
31
+ __version__ = version = '0.1.19'
32
+ __version_tuple__ = version_tuple = (0, 1, 19)
33
33
 
34
34
  __commit_id__ = commit_id = None
@@ -1480,7 +1480,7 @@ class LineageAnalyzer:
1480
1480
 
1481
1481
  def _extract_schema_from_statement(self, expr: exp.Expression) -> None:
1482
1482
  """
1483
- Extract column definitions from CREATE VIEW/TABLE AS SELECT statements.
1483
+ Extract column definitions from CREATE VIEW/TABLE AS SELECT and CACHE TABLE statements.
1484
1484
 
1485
1485
  This method builds up file-scoped schema context as statements are processed,
1486
1486
  enabling SQLGlot to correctly expand SELECT * and trace cross-statement references.
@@ -1488,29 +1488,39 @@ class LineageAnalyzer:
1488
1488
  Args:
1489
1489
  expr: The SQL expression to extract schema from
1490
1490
  """
1491
- # Only handle CREATE VIEW or CREATE TABLE (AS SELECT)
1492
- if not isinstance(expr, exp.Create):
1493
- return
1494
- if expr.kind not in ("VIEW", "TABLE"):
1495
- return
1491
+ target_name: str | None = None
1492
+ select_node: exp.Expression | None = None
1496
1493
 
1497
- # Get target table/view name
1498
- target = expr.this
1499
- if isinstance(target, exp.Schema):
1500
- target = target.this
1501
- if not isinstance(target, exp.Table):
1502
- return
1494
+ if isinstance(expr, exp.Create):
1495
+ if expr.kind not in ("VIEW", "TABLE"):
1496
+ return
1497
+
1498
+ target = expr.this
1499
+ if isinstance(target, exp.Schema):
1500
+ target = target.this
1501
+ if not isinstance(target, exp.Table):
1502
+ return
1503
1503
 
1504
- target_name = self._get_qualified_table_name(target)
1504
+ target_name = self._get_qualified_table_name(target)
1505
+ select_node = expr.expression
1505
1506
 
1506
- # Get the SELECT node from the CREATE statement
1507
- select_node = expr.expression
1508
- if select_node is None:
1507
+ # Handle Subquery wrapper (e.g., CREATE VIEW AS (SELECT ...))
1508
+ if isinstance(select_node, exp.Subquery):
1509
+ select_node = select_node.this
1510
+
1511
+ elif isinstance(expr, exp.Cache):
1512
+ target = expr.this
1513
+ if not isinstance(target, exp.Table):
1514
+ return
1515
+
1516
+ target_name = self._get_qualified_table_name(target)
1517
+ select_node = expr.expression
1518
+
1519
+ else:
1509
1520
  return
1510
1521
 
1511
- # Handle Subquery wrapper (e.g., CREATE VIEW AS (SELECT ...))
1512
- if isinstance(select_node, exp.Subquery):
1513
- select_node = select_node.this
1522
+ if target_name is None or select_node is None:
1523
+ return
1514
1524
 
1515
1525
  if not isinstance(
1516
1526
  select_node, (exp.Select, exp.Union, exp.Intersect, exp.Except)
@@ -2949,6 +2949,62 @@ class TestCacheTableStatements:
2949
2949
  assert len(skipped) == 1
2950
2950
  assert "DELETE" in skipped[0].statement_type
2951
2951
 
2952
+ def test_cache_table_star_resolution_in_subsequent_query(self):
2953
+ """SELECT * FROM a cached table should resolve columns from the CACHE statement."""
2954
+ sql = """
2955
+ CACHE TABLE cached_orders AS SELECT customer_id, order_total FROM orders;
2956
+ SELECT * FROM cached_orders;
2957
+ """
2958
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2959
+ results = analyzer.analyze_queries(level=AnalysisLevel.COLUMN)
2960
+
2961
+ assert len(results) == 2
2962
+ # Second query should have resolved the star
2963
+ star_result = results[1]
2964
+ output_names = sorted(item.output_name for item in star_result.lineage_items)
2965
+ output_names = sorted(item.output_name for item in star_result.lineage_items)
2966
+ assert output_names == ["customer_id", "order_total"]
2967
+
2968
+ def test_cache_table_qualified_star_resolution(self):
2969
+ """table.* on a cached table should resolve columns."""
2970
+ sql = """
2971
+ CACHE TABLE cached_orders AS SELECT customer_id, order_total FROM orders;
2972
+ SELECT cached_orders.* FROM cached_orders;
2973
+ """
2974
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2975
+ results = analyzer.analyze_queries(level=AnalysisLevel.COLUMN)
2976
+
2977
+ assert len(results) == 2
2978
+ star_result = results[1]
2979
+ output_names = sorted(item.output_name for item in star_result.lineage_items)
2980
+ assert output_names == ["customer_id", "order_total"]
2981
+
2982
+ def test_cache_lazy_table_star_resolution(self):
2983
+ """CACHE LAZY TABLE should also register schema for star resolution."""
2984
+ sql = """
2985
+ CACHE LAZY TABLE cached_users AS SELECT id, name, email FROM users;
2986
+ SELECT * FROM cached_users;
2987
+ """
2988
+ analyzer = LineageAnalyzer(sql, dialect="spark")
2989
+ results = analyzer.analyze_queries(level=AnalysisLevel.COLUMN)
2990
+
2991
+ assert len(results) == 2
2992
+ star_result = results[1]
2993
+ output_names = sorted(item.output_name for item in star_result.lineage_items)
2994
+ assert output_names == ["email", "id", "name"]
2995
+
2996
+ def test_cache_table_schema_registered(self):
2997
+ """Cached table schema should be registered for downstream star resolution."""
2998
+ sql = """
2999
+ CACHE TABLE cached_orders AS SELECT customer_id, order_total FROM orders;
3000
+ SELECT * FROM cached_orders;
3001
+ """
3002
+ analyzer = LineageAnalyzer(sql, dialect="spark")
3003
+ schema = analyzer.extract_schema_only()
3004
+ assert "cached_orders" in schema
3005
+ assert "customer_id" in schema["cached_orders"]
3006
+ assert "order_total" in schema["cached_orders"]
3007
+
2952
3008
 
2953
3009
  class TestNoStar:
2954
3010
  """Tests for the --no-star flag that fails on unresolvable SELECT *."""
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes