dlin-cli 0.2.4b1__tar.gz → 0.2.4rc1__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 (136) hide show
  1. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/Cargo.lock +51 -40
  2. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/Cargo.toml +3 -3
  3. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/PKG-INFO +1 -1
  4. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/mod.rs +3 -1
  5. dlin_cli-0.2.4rc1/crates/dlin-core/src/graph/column_lineage/single_model.rs +257 -0
  6. dlin_cli-0.2.4rc1/crates/dlin-core/src/graph/column_lineage/star_guard.rs +652 -0
  7. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/tests/core_star.rs +458 -55
  8. dlin_cli-0.2.4b1/crates/dlin-core/src/graph/column_lineage/single_model.rs +0 -450
  9. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/LICENSE +0 -0
  10. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/README.md +0 -0
  11. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin/Cargo.toml +0 -0
  12. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin/README.md +0 -0
  13. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin/src/cli.rs +0 -0
  14. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin/src/main.rs +0 -0
  15. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin/src/mcp.rs +0 -0
  16. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin/tests/integration_test.rs +0 -0
  17. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/Cargo.toml +0 -0
  18. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/README.md +0 -0
  19. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/error.rs +0 -0
  20. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/builder/mod.rs +0 -0
  21. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/builder/tests/build_graph.rs +0 -0
  22. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/builder/tests/generic_tests.rs +0 -0
  23. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/builder/tests/mod.rs +0 -0
  24. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/builder/tests/ref_parsing.rs +0 -0
  25. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/builder/tests/versioned.rs +0 -0
  26. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/cache.rs +0 -0
  27. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/cross_model.rs +0 -0
  28. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/impact.rs +0 -0
  29. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/schema.rs +0 -0
  30. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/tests/cache.rs +0 -0
  31. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/tests/core_basic.rs +0 -0
  32. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/tests/core_cross_model.rs +0 -0
  33. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/tests/core_util.rs +0 -0
  34. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/tests/impact.rs +0 -0
  35. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/tests/mod.rs +0 -0
  36. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/column_lineage/types.rs +0 -0
  37. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/mod.rs +0 -0
  38. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/collapse.rs +0 -0
  39. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/filter_graph.rs +0 -0
  40. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/mod.rs +0 -0
  41. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/output.rs +0 -0
  42. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/search.rs +0 -0
  43. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/selector.rs +0 -0
  44. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot.snap +0 -0
  45. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_bfs_pseudoendpoint.snap +0 -0
  46. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_endpoints_fan_out.snap +0 -0
  47. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_endpoints_leaf_model.snap +0 -0
  48. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_multiple_focus_models.snap +0 -0
  49. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_no_source_exposure.snap +0 -0
  50. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_preserve_focus.snap +0 -0
  51. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__snapshot_transitive_node_type_filter.snap +0 -0
  52. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__snapshot_transitive_select_filter.snap +0 -0
  53. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__snapshot_transitive_select_with_node_type.snap +0 -0
  54. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/transitive.rs +0 -0
  55. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/filter/tests/versioned.rs +0 -0
  56. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/impact.rs +0 -0
  57. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/mod.rs +0 -0
  58. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/graph/types.rs +0 -0
  59. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/input.rs +0 -0
  60. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/lib.rs +0 -0
  61. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/cache.rs +0 -0
  62. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/columns.rs +0 -0
  63. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/discovery.rs +0 -0
  64. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/jinja.rs +0 -0
  65. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/manifest.rs +0 -0
  66. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/manifest_cache.rs +0 -0
  67. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/mod.rs +0 -0
  68. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/project.rs +0 -0
  69. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/sql.rs +0 -0
  70. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/parser/yaml_schema.rs +0 -0
  71. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/ascii.rs +0 -0
  72. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/column_graph.rs +0 -0
  73. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/dot.rs +0 -0
  74. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/html.rs +0 -0
  75. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/impact.rs +0 -0
  76. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/json.rs +0 -0
  77. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/layout.rs +0 -0
  78. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/list.rs +0 -0
  79. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/mermaid.rs +0 -0
  80. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/mod.rs +0 -0
  81. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/plain.rs +0 -0
  82. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_all_transformation_types.snap +0 -0
  83. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_escapes_special_chars.snap +0 -0
  84. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_id_collision_avoided.snap +0 -0
  85. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_impact_indirect.snap +0 -0
  86. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_impact_single.snap +0 -0
  87. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_single_model.snap +0 -0
  88. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_via_path.snap +0 -0
  89. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_mermaid.snap +0 -0
  90. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_mermaid_indirect_edge_label.snap +0 -0
  91. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain.snap +0 -0
  92. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain_multi_hop.snap +0 -0
  93. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain_non_direct_intermediate.snap +0 -0
  94. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_dotted_table_name.snap +0 -0
  95. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_id_collision_avoided.snap +0 -0
  96. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_label_escaping.snap +0 -0
  97. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_single_model.snap +0 -0
  98. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_model_path_non_direct_annotation.snap +0 -0
  99. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_no_sources.snap +0 -0
  100. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_single_model.snap +0 -0
  101. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__group_by_node_type.snap +0 -0
  102. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_all_edge_types.snap +0 -0
  103. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_direction_tb.snap +0 -0
  104. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_direction_tb_grouped.snap +0 -0
  105. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_group_by_directory.snap +0 -0
  106. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_lineage.snap +0 -0
  107. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_transitive_edges.snap +0 -0
  108. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__html__tests__snapshot_html_json.snap +0 -0
  109. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_json.snap +0 -0
  110. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_json_with_sql.snap +0 -0
  111. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_text.snap +0 -0
  112. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_text_with_sql.snap +0 -0
  113. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_json_with_sql.snap +0 -0
  114. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_lineage.snap +0 -0
  115. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_node_metadata.snap +0 -0
  116. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__list__tests__snapshot_list_json.snap +0 -0
  117. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__list__tests__snapshot_list_plain.snap +0 -0
  118. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__group_by_node_type.snap +0 -0
  119. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__mixed_direct_and_transitive_edges.snap +0 -0
  120. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_escapes_quotes.snap +0 -0
  121. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_lineage.snap +0 -0
  122. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_single_model.snap +0 -0
  123. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_with_collapse.snap +0 -0
  124. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_with_grouping.snap +0 -0
  125. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_direction_tb.snap +0 -0
  126. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_direction_tb_grouped.snap +0 -0
  127. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_group_by_directory.snap +0 -0
  128. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_lineage.snap +0 -0
  129. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__transitive_edge_rendering.snap +0 -0
  130. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__plain__tests__snapshot_plain.snap +0 -0
  131. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__summary__tests__snapshot_summary_json.snap +0 -0
  132. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/snapshots/dlin_core__render__summary__tests__snapshot_summary_text.snap +0 -0
  133. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/summary.rs +0 -0
  134. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/src/render/svg.rs +0 -0
  135. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/crates/dlin-core/tests/column_lineage_test.rs +0 -0
  136. {dlin_cli-0.2.4b1 → dlin_cli-0.2.4rc1}/pyproject.toml +0 -0
@@ -87,9 +87,9 @@ dependencies = [
87
87
 
88
88
  [[package]]
89
89
  name = "anyhow"
90
- version = "1.0.103"
90
+ version = "1.0.104"
91
91
  source = "registry+https://github.com/rust-lang/crates.io-index"
92
- checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
92
+ checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470"
93
93
 
94
94
  [[package]]
95
95
  name = "arraydeque"
@@ -139,9 +139,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
139
139
 
140
140
  [[package]]
141
141
  name = "clap"
142
- version = "4.6.1"
142
+ version = "4.6.4"
143
143
  source = "registry+https://github.com/rust-lang/crates.io-index"
144
- checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
144
+ checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7"
145
145
  dependencies = [
146
146
  "clap_builder",
147
147
  "clap_derive",
@@ -149,9 +149,9 @@ dependencies = [
149
149
 
150
150
  [[package]]
151
151
  name = "clap_builder"
152
- version = "4.6.0"
152
+ version = "4.6.2"
153
153
  source = "registry+https://github.com/rust-lang/crates.io-index"
154
- checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
154
+ checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b"
155
155
  dependencies = [
156
156
  "anstream",
157
157
  "anstyle",
@@ -161,14 +161,14 @@ dependencies = [
161
161
 
162
162
  [[package]]
163
163
  name = "clap_derive"
164
- version = "4.6.1"
164
+ version = "4.6.4"
165
165
  source = "registry+https://github.com/rust-lang/crates.io-index"
166
- checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
166
+ checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061"
167
167
  dependencies = [
168
168
  "heck",
169
169
  "proc-macro2",
170
170
  "quote",
171
- "syn",
171
+ "syn 3.0.3",
172
172
  ]
173
173
 
174
174
  [[package]]
@@ -230,7 +230,7 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
230
230
 
231
231
  [[package]]
232
232
  name = "dlin"
233
- version = "0.2.4-beta.1"
233
+ version = "0.2.4-rc.1"
234
234
  dependencies = [
235
235
  "anyhow",
236
236
  "clap",
@@ -247,7 +247,7 @@ dependencies = [
247
247
 
248
248
  [[package]]
249
249
  name = "dlin-core"
250
- version = "0.2.4-beta.1"
250
+ version = "0.2.4-rc.1"
251
251
  dependencies = [
252
252
  "anyhow",
253
253
  "clap",
@@ -266,7 +266,7 @@ dependencies = [
266
266
  "serde_json",
267
267
  "serial_test",
268
268
  "tempfile",
269
- "thiserror 2.0.18",
269
+ "thiserror 2.0.19",
270
270
  "walkdir",
271
271
  ]
272
272
 
@@ -385,9 +385,9 @@ dependencies = [
385
385
 
386
386
  [[package]]
387
387
  name = "globset"
388
- version = "0.4.18"
388
+ version = "0.4.19"
389
389
  source = "registry+https://github.com/rust-lang/crates.io-index"
390
- checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3"
390
+ checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd"
391
391
  dependencies = [
392
392
  "aho-corasick",
393
393
  "bstr",
@@ -473,9 +473,9 @@ dependencies = [
473
473
 
474
474
  [[package]]
475
475
  name = "libc"
476
- version = "0.2.186"
476
+ version = "0.2.189"
477
477
  source = "registry+https://github.com/rust-lang/crates.io-index"
478
- checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
478
+ checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2"
479
479
 
480
480
  [[package]]
481
481
  name = "linux-raw-sys"
@@ -597,9 +597,9 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
597
597
 
598
598
  [[package]]
599
599
  name = "polyglot-sql"
600
- version = "0.6.2"
600
+ version = "0.6.3"
601
601
  source = "registry+https://github.com/rust-lang/crates.io-index"
602
- checksum = "009cbd62f8c82de46697221ee66b0cf3a6dacbda16f8c5a2ce25853940073bd5"
602
+ checksum = "b54acea84522c6c51f6cbfa56600097cd8c6c5583a70e67f2787b6f70c3dd956"
603
603
  dependencies = [
604
604
  "polyglot-sql-ast-derive",
605
605
  "serde",
@@ -610,13 +610,13 @@ dependencies = [
610
610
 
611
611
  [[package]]
612
612
  name = "polyglot-sql-ast-derive"
613
- version = "0.6.2"
613
+ version = "0.6.3"
614
614
  source = "registry+https://github.com/rust-lang/crates.io-index"
615
- checksum = "e3250a6ffcd1d39670eb532e2b1877ad430799c49cb91b243d63c9425b868161"
615
+ checksum = "c19af779599f7bf0ec303044dbb8e1c3f6c5b4dca29ea703b033519860104031"
616
616
  dependencies = [
617
617
  "proc-macro2",
618
618
  "quote",
619
- "syn",
619
+ "syn 2.0.117",
620
620
  ]
621
621
 
622
622
  [[package]]
@@ -737,9 +737,9 @@ checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
737
737
 
738
738
  [[package]]
739
739
  name = "serde"
740
- version = "1.0.228"
740
+ version = "1.0.229"
741
741
  source = "registry+https://github.com/rust-lang/crates.io-index"
742
- checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
742
+ checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba"
743
743
  dependencies = [
744
744
  "serde_core",
745
745
  "serde_derive",
@@ -766,29 +766,29 @@ dependencies = [
766
766
 
767
767
  [[package]]
768
768
  name = "serde_core"
769
- version = "1.0.228"
769
+ version = "1.0.229"
770
770
  source = "registry+https://github.com/rust-lang/crates.io-index"
771
- checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
771
+ checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48"
772
772
  dependencies = [
773
773
  "serde_derive",
774
774
  ]
775
775
 
776
776
  [[package]]
777
777
  name = "serde_derive"
778
- version = "1.0.228"
778
+ version = "1.0.229"
779
779
  source = "registry+https://github.com/rust-lang/crates.io-index"
780
- checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
780
+ checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348"
781
781
  dependencies = [
782
782
  "proc-macro2",
783
783
  "quote",
784
- "syn",
784
+ "syn 3.0.3",
785
785
  ]
786
786
 
787
787
  [[package]]
788
788
  name = "serde_json"
789
- version = "1.0.150"
789
+ version = "1.0.151"
790
790
  source = "registry+https://github.com/rust-lang/crates.io-index"
791
- checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
791
+ checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
792
792
  dependencies = [
793
793
  "itoa",
794
794
  "memchr",
@@ -819,7 +819,7 @@ checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c"
819
819
  dependencies = [
820
820
  "proc-macro2",
821
821
  "quote",
822
- "syn",
822
+ "syn 2.0.117",
823
823
  ]
824
824
 
825
825
  [[package]]
@@ -857,6 +857,17 @@ dependencies = [
857
857
  "unicode-ident",
858
858
  ]
859
859
 
860
+ [[package]]
861
+ name = "syn"
862
+ version = "3.0.3"
863
+ source = "registry+https://github.com/rust-lang/crates.io-index"
864
+ checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3"
865
+ dependencies = [
866
+ "proc-macro2",
867
+ "quote",
868
+ "unicode-ident",
869
+ ]
870
+
860
871
  [[package]]
861
872
  name = "tempfile"
862
873
  version = "3.27.0"
@@ -881,11 +892,11 @@ dependencies = [
881
892
 
882
893
  [[package]]
883
894
  name = "thiserror"
884
- version = "2.0.18"
895
+ version = "2.0.19"
885
896
  source = "registry+https://github.com/rust-lang/crates.io-index"
886
- checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
897
+ checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9"
887
898
  dependencies = [
888
- "thiserror-impl 2.0.18",
899
+ "thiserror-impl 2.0.19",
889
900
  ]
890
901
 
891
902
  [[package]]
@@ -896,18 +907,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1"
896
907
  dependencies = [
897
908
  "proc-macro2",
898
909
  "quote",
899
- "syn",
910
+ "syn 2.0.117",
900
911
  ]
901
912
 
902
913
  [[package]]
903
914
  name = "thiserror-impl"
904
- version = "2.0.18"
915
+ version = "2.0.19"
905
916
  source = "registry+https://github.com/rust-lang/crates.io-index"
906
- checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
917
+ checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd"
907
918
  dependencies = [
908
919
  "proc-macro2",
909
920
  "quote",
910
- "syn",
921
+ "syn 3.0.3",
911
922
  ]
912
923
 
913
924
  [[package]]
@@ -991,7 +1002,7 @@ dependencies = [
991
1002
  "bumpalo",
992
1003
  "proc-macro2",
993
1004
  "quote",
994
- "syn",
1005
+ "syn 2.0.117",
995
1006
  "wasm-bindgen-shared",
996
1007
  ]
997
1008
 
@@ -1051,7 +1062,7 @@ checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89"
1051
1062
  dependencies = [
1052
1063
  "proc-macro2",
1053
1064
  "quote",
1054
- "syn",
1065
+ "syn 2.0.117",
1055
1066
  ]
1056
1067
 
1057
1068
  [[package]]
@@ -3,7 +3,7 @@ members = ["crates/*"]
3
3
  resolver = "3"
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.4-beta.1"
6
+ version = "0.2.4-rc.1"
7
7
  edition = "2024"
8
8
  license = "MIT"
9
9
  repository = "https://github.com/eitsupi/dlin"
@@ -23,7 +23,7 @@ indexmap = "2"
23
23
  minijinja = "2"
24
24
  rayon = "1"
25
25
  globset = "0.4"
26
- polyglot-sql = { version = "0.6.2", default-features = false, features = ["all-dialects", "semantic"] }
26
+ polyglot-sql = { version = "0.6.3", default-features = false, features = ["all-dialects", "semantic"] }
27
27
  path-slash = "0.2.1"
28
28
  clap = { version = "4", features = ["derive", "env"] }
29
29
  libc = "0.2"
@@ -34,7 +34,7 @@ insta = "1"
34
34
  serial_test = "3.4.0"
35
35
 
36
36
  # internal
37
- dlin-core = { version = "0.2.4-beta.1", path = "crates/dlin-core" }
37
+ dlin-core = { version = "0.2.4-rc.1", path = "crates/dlin-core" }
38
38
 
39
39
  [workspace.lints.rust]
40
40
  unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tarpaulin_include)'] }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dlin-cli
3
- Version: 0.2.4b1
3
+ Version: 0.2.4rc1
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -11,6 +11,7 @@ mod cross_model;
11
11
  mod impact;
12
12
  mod schema;
13
13
  mod single_model;
14
+ mod star_guard;
14
15
  #[cfg(test)]
15
16
  mod tests;
16
17
  mod types;
@@ -24,7 +25,8 @@ pub use impact::{
24
25
  compute_column_impact_with_manifest_path,
25
26
  };
26
27
  use schema::{build_yaml_schema_for_node, compute_manifest_columns_hash, infer_output_columns};
27
- use single_model::{has_unresolved_stars, prepare_lineage_context_with_expr, run_column_lineage};
28
+ use single_model::{prepare_lineage_context_with_expr, run_column_lineage};
29
+ use star_guard::has_unresolved_stars;
28
30
  pub use types::{
29
31
  ColumnLineageEntry, ColumnLineageError, ColumnLineageErrorKind, ColumnSource,
30
32
  ModelColumnLineage, TransformationType,
@@ -0,0 +1,257 @@
1
+ use polyglot_sql::lineage::LineageNode;
2
+ use polyglot_sql::scope::SourceKind;
3
+ use polyglot_sql::{DialectType, Expression};
4
+
5
+ use crate::parser::manifest::Manifest;
6
+
7
+ use super::schema::build_schema_from_manifest;
8
+ use super::star_guard::StarGuard;
9
+ use super::{ColumnSource, TransformationType};
10
+
11
+ pub(super) struct LineageContext {
12
+ pub(super) expanded_expr: Expression,
13
+ schema: Option<polyglot_sql::MappingSchema>,
14
+ dialect: DialectType,
15
+ star_guard: StarGuard,
16
+ }
17
+
18
+ pub(super) fn prepare_lineage_context(
19
+ compiled_code: &str,
20
+ manifest: &Manifest,
21
+ node: &crate::parser::manifest::ManifestNode,
22
+ dialect: DialectType,
23
+ ) -> Result<LineageContext, String> {
24
+ prepare_lineage_context_with_expr(compiled_code, manifest, node, dialect, None)
25
+ }
26
+
27
+ pub(super) fn prepare_lineage_context_with_expr(
28
+ compiled_code: &str,
29
+ manifest: &Manifest,
30
+ node: &crate::parser::manifest::ManifestNode,
31
+ dialect: DialectType,
32
+ parsed_expr: Option<&Expression>,
33
+ ) -> Result<LineageContext, String> {
34
+ let expr = match parsed_expr {
35
+ Some(e) => e.clone(),
36
+ None => polyglot_sql::parse_one(compiled_code, dialect).map_err(|e| format!("{}", e))?,
37
+ };
38
+ let schema = build_schema_from_manifest(manifest, node, dialect);
39
+
40
+ let mut expanded_expr = expr;
41
+ polyglot_sql::lineage::expand_cte_stars(
42
+ &mut expanded_expr,
43
+ schema.as_ref().map(|s| s as &dyn polyglot_sql::Schema),
44
+ );
45
+ let star_guard = StarGuard::new(&expanded_expr, Some(dialect));
46
+ Ok(LineageContext {
47
+ expanded_expr,
48
+ schema,
49
+ dialect,
50
+ star_guard,
51
+ })
52
+ }
53
+
54
+ pub(super) fn run_column_lineage(
55
+ col_name: &str,
56
+ ctx: &LineageContext,
57
+ ) -> Result<ColumnLineageResult, String> {
58
+ let modifier_expr =
59
+ ctx.star_guard
60
+ .materialize_star_modifier(&ctx.expanded_expr, col_name, ctx.dialect);
61
+ let lineage_expr = modifier_expr.as_ref().unwrap_or(&ctx.expanded_expr);
62
+ let lineage_result = lineage_node(col_name, lineage_expr, ctx);
63
+
64
+ match lineage_result {
65
+ Ok(node) => {
66
+ if ctx.star_guard.rejects(&node) {
67
+ Err(format!("Cannot find column '{}' in query", col_name))
68
+ } else {
69
+ Ok(extract_leaf_sources(&node))
70
+ }
71
+ }
72
+ Err(e) => explicit_set_operand_lineage(col_name, lineage_expr, ctx, &e)
73
+ .ok_or_else(|| format_lineage_error(&e)),
74
+ }
75
+ }
76
+
77
+ fn lineage_node(
78
+ col_name: &str,
79
+ expr: &Expression,
80
+ ctx: &LineageContext,
81
+ ) -> Result<LineageNode, polyglot_sql::Error> {
82
+ let dialect = Some(ctx.dialect);
83
+ if let Some(ref schema) = ctx.schema {
84
+ polyglot_sql::lineage::lineage_with_schema(
85
+ col_name,
86
+ expr,
87
+ Some(schema as &dyn polyglot_sql::Schema),
88
+ dialect,
89
+ false,
90
+ )
91
+ .or_else(|_| polyglot_sql::lineage::lineage(col_name, expr, dialect, false))
92
+ } else {
93
+ polyglot_sql::lineage::lineage(col_name, expr, dialect, false)
94
+ }
95
+ }
96
+
97
+ // TODO: this locates an operand's projection by ordinal (see
98
+ // `StarGuard::explicit_set_operands`) but then re-derives lineage by looking that
99
+ // projection's output *name* back up via `lineage_node`, instead of tracing the
100
+ // already-located projection directly. Unaliased expressions have no name to look
101
+ // up (e.g. `fee * 2` in `SELECT other.*, fee FROM a UNION ALL SELECT x, fee * 2
102
+ // FROM b`) and are silently dropped; duplicate output names (`SELECT a.id, b.id`)
103
+ // make the lookup ambiguous and can retrace the wrong projection. Redesign: keep
104
+ // projection-list position and output ordinal as distinct types and trace
105
+ // positionally instead of round-tripping through a name.
106
+ fn explicit_set_operand_lineage(
107
+ col_name: &str,
108
+ expr: &Expression,
109
+ ctx: &LineageContext,
110
+ original_error: &polyglot_sql::Error,
111
+ ) -> Option<ColumnLineageResult> {
112
+ let error = original_error.to_string();
113
+ if !error.contains("Cannot find column") || !error.contains("in set operation") {
114
+ return None;
115
+ }
116
+
117
+ // A set's leftmost star can make its output names unknowable to polyglot-sql.
118
+ // Trace only intact operands that explicitly project the requested ordinal;
119
+ // unresolved star operands contribute no source instead of a guessed one.
120
+ let operands = StarGuard::explicit_set_operands(expr, col_name, Some(ctx.dialect))?;
121
+ let mut results = operands.into_iter().filter_map(|(operand, operand_name)| {
122
+ let node = lineage_node(operand_name, operand, ctx).ok()?;
123
+ (!ctx.star_guard.rejects(&node)).then(|| extract_leaf_sources(&node))
124
+ });
125
+ let first = results.next()?;
126
+ Some(results.fold(first, merge_lineage_results))
127
+ }
128
+
129
+ fn merge_lineage_results(
130
+ mut left: ColumnLineageResult,
131
+ right: ColumnLineageResult,
132
+ ) -> ColumnLineageResult {
133
+ if left.transformation == TransformationType::Direct
134
+ && right.transformation != TransformationType::Direct
135
+ {
136
+ left.transformation = right.transformation;
137
+ }
138
+ left.sources.extend(right.sources);
139
+ left.sources
140
+ .sort_by(|a, b| (&a.table, &a.column).cmp(&(&b.table, &b.column)));
141
+ left.sources.dedup();
142
+ left
143
+ }
144
+
145
+ pub(super) fn format_lineage_error(e: &polyglot_sql::Error) -> String {
146
+ let msg = e.to_string();
147
+ if let Some(rest) = msg
148
+ .strip_prefix("Parse error at line 0, column 0: ")
149
+ .or_else(|| msg.strip_prefix("Syntax error at line 0, column 0: "))
150
+ {
151
+ format!("lineage failed: {}", rest)
152
+ } else if msg.starts_with("Internal error: ") {
153
+ format!(
154
+ "lineage failed: {}",
155
+ msg.strip_prefix("Internal error: ").unwrap()
156
+ )
157
+ } else {
158
+ msg
159
+ }
160
+ }
161
+
162
+ fn classify_transformation(node: &LineageNode) -> TransformationType {
163
+ let t = classify_expression(&node.expression);
164
+ if t != TransformationType::Direct || node.downstream.is_empty() {
165
+ return t;
166
+ }
167
+ // Direct pass-through: traverse downstream to find the actual transformation type.
168
+ // This handles the case where a CTE computes an expression and a later SELECT
169
+ // references the result by name (which looks like Column → Direct at the surface).
170
+ for child in &node.downstream {
171
+ let child_t = classify_transformation(child);
172
+ if child_t != TransformationType::Direct {
173
+ return child_t;
174
+ }
175
+ }
176
+ TransformationType::Direct
177
+ }
178
+
179
+ fn classify_expression(expr: &Expression) -> TransformationType {
180
+ match expr {
181
+ Expression::Column(_) | Expression::Identifier(_) => TransformationType::Direct,
182
+ Expression::Alias(alias) => classify_expression(&alias.this),
183
+ Expression::Count(_)
184
+ | Expression::Sum(_)
185
+ | Expression::Avg(_)
186
+ | Expression::Min(_)
187
+ | Expression::Max(_) => TransformationType::Aggregation,
188
+ Expression::Cast(_) => TransformationType::Cast,
189
+ Expression::Case(_) => TransformationType::Conditional,
190
+ Expression::Add(_) | Expression::Sub(_) | Expression::Mul(_) | Expression::Div(_) => {
191
+ TransformationType::Expression
192
+ }
193
+ Expression::Anonymous(_) | Expression::Coalesce(_) | Expression::NullIf(_) => {
194
+ TransformationType::Expression
195
+ }
196
+ // Generic function calls (covers CONCAT, NULLIF, SPLIT, etc. parsed as Function)
197
+ Expression::Function(_) => TransformationType::Expression,
198
+ // Specialized scalar function variants with their own AST types
199
+ Expression::Upper(_)
200
+ | Expression::Lower(_)
201
+ | Expression::Length(_)
202
+ | Expression::Concat(_) => TransformationType::Expression,
203
+ _ => TransformationType::Unknown,
204
+ }
205
+ }
206
+
207
+ pub(super) struct ColumnLineageResult {
208
+ pub(super) sources: Vec<ColumnSource>,
209
+ pub(super) transformation: TransformationType,
210
+ }
211
+
212
+ fn extract_leaf_sources(node: &LineageNode) -> ColumnLineageResult {
213
+ let transformation = classify_transformation(node);
214
+ let mut sources = Vec::new();
215
+ collect_leaves(node, &mut sources);
216
+ sources.sort_by(|a, b| (&a.table, &a.column).cmp(&(&b.table, &b.column)));
217
+ sources.dedup();
218
+ ColumnLineageResult {
219
+ sources,
220
+ transformation,
221
+ }
222
+ }
223
+
224
+ fn collect_leaves(node: &LineageNode, sources: &mut Vec<ColumnSource>) {
225
+ if node.downstream.is_empty() {
226
+ if node.source_kind == SourceKind::Virtual {
227
+ return;
228
+ }
229
+ let name = &node.name;
230
+ if let Some((alias, column)) = name.rsplit_once('.') {
231
+ // polyglot-sql puts the SQL alias in node.name (e.g. "c.customer_id") and the
232
+ // actual table name in source_name (e.g. "stg_customers"). Use source_name
233
+ // when it genuinely differs from the alias, including fully-qualified forms
234
+ // (e.g. "shop.main.stg_orders" when the SQL uses the short name "stg_orders").
235
+ let table = if !node.source_name.is_empty() && node.source_name != alias {
236
+ node.source_name.as_str()
237
+ } else {
238
+ alias
239
+ };
240
+ sources.push(ColumnSource {
241
+ table: table.to_string(),
242
+ column: column.to_string(),
243
+ model_path: vec![],
244
+ });
245
+ } else {
246
+ sources.push(ColumnSource {
247
+ table: node.source_name.clone(),
248
+ column: name.to_string(),
249
+ model_path: vec![],
250
+ });
251
+ }
252
+ } else {
253
+ for child in &node.downstream {
254
+ collect_leaves(child, sources);
255
+ }
256
+ }
257
+ }