dlin-cli 0.2.1__tar.gz → 0.2.2__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.
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/Cargo.lock +8 -8
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/Cargo.toml +4 -4
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/PKG-INFO +1 -1
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin/src/cli.rs +10 -6
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin/src/main.rs +37 -4
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin/src/mcp.rs +88 -3
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin/tests/integration_test.rs +245 -1
- dlin_cli-0.2.2/crates/dlin-core/src/graph/builder/mod.rs +1254 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/builder/tests/build_graph.rs +1077 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/builder/tests/generic_tests.rs +184 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/builder/tests/mod.rs +49 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/builder/tests/ref_parsing.rs +97 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/builder/tests/versioned.rs +589 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/single_model.rs +3 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/tests/core.rs +62 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/mod.rs +598 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/collapse.rs +721 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/filter_graph.rs +190 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/mod.rs +188 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/output.rs +161 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/search.rs +95 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/selector.rs +323 -0
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_bfs_pseudoendpoint.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_bfs_pseudoendpoint.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_endpoints_fan_out.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_endpoints_fan_out.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_endpoints_leaf_model.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_endpoints_leaf_model.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_multiple_focus_models.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_multiple_focus_models.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_no_source_exposure.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_no_source_exposure.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_preserve_focus.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__collapse_snapshot_preserve_focus.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_node_type_filter.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__snapshot_transitive_node_type_filter.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_select_filter.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__snapshot_transitive_select_filter.snap +2 -1
- dlin_cli-0.2.1/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_select_with_node_type.snap → dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/snapshots/dlin_core__graph__filter__tests__collapse__snapshot_transitive_select_with_node_type.snap +2 -1
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/transitive.rs +177 -0
- dlin_cli-0.2.2/crates/dlin-core/src/graph/filter/tests/versioned.rs +128 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/impact.rs +1 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/types.rs +16 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/input.rs +49 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/cache.rs +2 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/jinja.rs +105 -8
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/manifest.rs +377 -4
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/sql.rs +195 -5
- dlin_cli-0.2.2/crates/dlin-core/src/parser/yaml_schema.rs +982 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/ascii.rs +23 -2
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/column_graph.rs +1 -16
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/dot.rs +54 -5
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/html.rs +1 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/json.rs +8 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/list.rs +1 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/mermaid.rs +6 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/mod.rs +50 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__summary__tests__snapshot_summary_json.snap +4 -1
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/summary.rs +28 -1
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/svg.rs +77 -14
- dlin_cli-0.2.1/crates/dlin-core/src/graph/builder.rs +0 -1614
- dlin_cli-0.2.1/crates/dlin-core/src/graph/filter.rs +0 -2396
- dlin_cli-0.2.1/crates/dlin-core/src/parser/yaml_schema.rs +0 -350
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/LICENSE +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/README.md +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin/Cargo.toml +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin/README.md +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/Cargo.toml +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/README.md +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/error.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/cache.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/cross_model.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/impact.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/mod.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/schema.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/tests/cache.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/tests/impact.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/tests/mod.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/column_lineage/types.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/graph/mod.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/lib.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/columns.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/discovery.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/manifest_cache.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/mod.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/parser/project.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/impact.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/layout.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/plain.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_all_transformation_types.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_escapes_special_chars.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_id_collision_avoided.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_impact_indirect.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_impact_single.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_single_model.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_via_path.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_mermaid.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_mermaid_indirect_edge_label.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain_multi_hop.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain_non_direct_intermediate.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_dotted_table_name.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_id_collision_avoided.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_label_escaping.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_single_model.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_model_path_non_direct_annotation.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_no_sources.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_single_model.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__group_by_node_type.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_all_edge_types.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_direction_tb.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_direction_tb_grouped.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_group_by_directory.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_lineage.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_transitive_edges.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__html__tests__snapshot_html_json.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_json.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_json_with_sql.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_text.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_text_with_sql.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_json_with_sql.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_lineage.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_node_metadata.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__list__tests__snapshot_list_json.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__list__tests__snapshot_list_plain.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__group_by_node_type.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__mixed_direct_and_transitive_edges.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_escapes_quotes.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_lineage.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_single_model.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_with_collapse.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_with_grouping.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_direction_tb.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_direction_tb_grouped.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_group_by_directory.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_lineage.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__transitive_edge_rendering.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__plain__tests__snapshot_plain.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/src/render/snapshots/dlin_core__render__summary__tests__snapshot_summary_text.snap +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/crates/dlin-core/tests/column_lineage_test.rs +0 -0
- {dlin_cli-0.2.1 → dlin_cli-0.2.2}/pyproject.toml +0 -0
|
@@ -230,7 +230,7 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
|
|
230
230
|
|
|
231
231
|
[[package]]
|
|
232
232
|
name = "dlin"
|
|
233
|
-
version = "0.2.
|
|
233
|
+
version = "0.2.2"
|
|
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.
|
|
250
|
+
version = "0.2.2"
|
|
251
251
|
dependencies = [
|
|
252
252
|
"anyhow",
|
|
253
253
|
"clap",
|
|
@@ -398,9 +398,9 @@ dependencies = [
|
|
|
398
398
|
|
|
399
399
|
[[package]]
|
|
400
400
|
name = "granit-parser"
|
|
401
|
-
version = "0.0.
|
|
401
|
+
version = "0.0.3"
|
|
402
402
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
403
|
-
checksum = "
|
|
403
|
+
checksum = "f50ba32164f9e098d5da618776a32afbb32270adcbe3d3d006107dae11e37c91"
|
|
404
404
|
dependencies = [
|
|
405
405
|
"arraydeque",
|
|
406
406
|
"smallvec",
|
|
@@ -597,9 +597,9 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
|
|
|
597
597
|
|
|
598
598
|
[[package]]
|
|
599
599
|
name = "polyglot-sql"
|
|
600
|
-
version = "0.4.
|
|
600
|
+
version = "0.4.4"
|
|
601
601
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
602
|
-
checksum = "
|
|
602
|
+
checksum = "b0d5ea9283f03d3b6c32a230e60e7c6bc854e5bec83c399c75cad02bf46e9622"
|
|
603
603
|
dependencies = [
|
|
604
604
|
"serde",
|
|
605
605
|
"serde_json",
|
|
@@ -750,9 +750,9 @@ dependencies = [
|
|
|
750
750
|
|
|
751
751
|
[[package]]
|
|
752
752
|
name = "serde-saphyr"
|
|
753
|
-
version = "0.0.
|
|
753
|
+
version = "0.0.27"
|
|
754
754
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
755
|
-
checksum = "
|
|
755
|
+
checksum = "5897b4c3faadadd35fdb6689f015641f3bc481d5adaaac56231ea15aeb243db3"
|
|
756
756
|
dependencies = [
|
|
757
757
|
"ahash",
|
|
758
758
|
"annotate-snippets",
|
|
@@ -3,7 +3,7 @@ members = ["crates/*"]
|
|
|
3
3
|
resolver = "3"
|
|
4
4
|
|
|
5
5
|
[workspace.package]
|
|
6
|
-
version = "0.2.
|
|
6
|
+
version = "0.2.2"
|
|
7
7
|
edition = "2024"
|
|
8
8
|
license = "MIT"
|
|
9
9
|
repository = "https://github.com/eitsupi/dlin"
|
|
@@ -13,7 +13,7 @@ homepage = "https://github.com/eitsupi/dlin"
|
|
|
13
13
|
petgraph = { version = "0.8", features = ["serde-1"] }
|
|
14
14
|
serde = { version = "1", features = ["derive"] }
|
|
15
15
|
serde_json = "1"
|
|
16
|
-
serde-saphyr = "0.0.
|
|
16
|
+
serde-saphyr = "0.0.27"
|
|
17
17
|
regex = "1"
|
|
18
18
|
walkdir = "2"
|
|
19
19
|
colored = "3"
|
|
@@ -23,7 +23,7 @@ indexmap = "2"
|
|
|
23
23
|
minijinja = "2"
|
|
24
24
|
rayon = "1"
|
|
25
25
|
globset = "0.4"
|
|
26
|
-
polyglot-sql = { version = "0.4.
|
|
26
|
+
polyglot-sql = { version = "0.4.4", 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.
|
|
37
|
+
dlin-core = { version = "0.2.2", path = "crates/dlin-core" }
|
|
38
38
|
|
|
39
39
|
[workspace.lints.rust]
|
|
40
40
|
unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tarpaulin_include)'] }
|
|
@@ -264,7 +264,7 @@ All selectors support glob patterns (*, **, ?, []):
|
|
|
264
264
|
value_delimiter = ',',
|
|
265
265
|
long_help = "\
|
|
266
266
|
Filter output by node type (comma-separated). Default: all types.
|
|
267
|
-
Available types: model, source, seed, snapshot, test, exposure.
|
|
267
|
+
Available types: model, source, seed, snapshot, test, exposure, semantic_model, metric, saved_query.
|
|
268
268
|
|
|
269
269
|
NOTE: In sql mode, generic tests are inferred from YAML declarations
|
|
270
270
|
with dlin-specific IDs. Use --source manifest for exact dependency resolution."
|
|
@@ -953,8 +953,10 @@ pub struct ColumnGraphArgs {
|
|
|
953
953
|
#[arg(short = 'o', long, default_value = "json")]
|
|
954
954
|
pub output: ColumnOutputFormat,
|
|
955
955
|
|
|
956
|
-
/// SQL dialect for parsing
|
|
957
|
-
///
|
|
956
|
+
/// SQL dialect for parsing compiled SQL.
|
|
957
|
+
/// Auto-detected from manifest.metadata.adapter_type when omitted; required if the manifest
|
|
958
|
+
/// does not declare an adapter_type.
|
|
959
|
+
/// [possible values: generic, bigquery, snowflake, postgres, duckdb, redshift, databricks, spark, trino, mysql, clickhouse, oracle, hive, sqlite, presto, athena, teradata, doris, starrocks, materialize, risingwave, singlestore, cockroachdb, tidb, tsql, druid, solr, tableau, dune, fabric, drill, dremio, exasol, datafusion]
|
|
958
960
|
#[arg(long)]
|
|
959
961
|
pub dialect: Option<DialectType>,
|
|
960
962
|
|
|
@@ -996,8 +998,10 @@ pub struct ColumnImpactArgs {
|
|
|
996
998
|
#[arg(short = 'o', long, default_value = "json")]
|
|
997
999
|
pub output: ColumnOutputFormat,
|
|
998
1000
|
|
|
999
|
-
/// SQL dialect for parsing
|
|
1000
|
-
///
|
|
1001
|
+
/// SQL dialect for parsing compiled SQL.
|
|
1002
|
+
/// Auto-detected from manifest.metadata.adapter_type when omitted; required if the manifest
|
|
1003
|
+
/// does not declare an adapter_type.
|
|
1004
|
+
/// [possible values: generic, bigquery, snowflake, postgres, duckdb, redshift, databricks, spark, trino, mysql, clickhouse, oracle, hive, sqlite, presto, athena, teradata, doris, starrocks, materialize, risingwave, singlestore, cockroachdb, tidb, tsql, druid, solr, tableau, dune, fabric, drill, dremio, exasol, datafusion]
|
|
1001
1005
|
#[arg(long)]
|
|
1002
1006
|
pub dialect: Option<DialectType>,
|
|
1003
1007
|
|
|
@@ -1218,7 +1222,7 @@ Examples:
|
|
|
1218
1222
|
value_delimiter = ',',
|
|
1219
1223
|
long_help = "\
|
|
1220
1224
|
Filter output by node type (comma-separated). Default: all types.
|
|
1221
|
-
Available types: model, source, seed, snapshot, test, exposure.
|
|
1225
|
+
Available types: model, source, seed, snapshot, test, exposure, semantic_model, metric, saved_query.
|
|
1222
1226
|
|
|
1223
1227
|
NOTE: In sql mode, generic tests are inferred from YAML declarations
|
|
1224
1228
|
with dlin-specific IDs. Use --source manifest for exact dependency resolution."
|
|
@@ -810,6 +810,39 @@ fn resolve_manifest_path(manifest_arg: &Path) -> Result<PathBuf> {
|
|
|
810
810
|
}
|
|
811
811
|
}
|
|
812
812
|
|
|
813
|
+
/// Resolve the SQL dialect to use for column lineage.
|
|
814
|
+
///
|
|
815
|
+
/// Precedence: CLI flag > manifest adapter_type > error.
|
|
816
|
+
/// Avoids silent fallback to Generic, which produces incorrect results for
|
|
817
|
+
/// warehouse-specific SQL (e.g. BigQuery backtick quoting, Snowflake QUALIFY).
|
|
818
|
+
fn resolve_dialect(
|
|
819
|
+
cli_dialect: Option<DialectType>,
|
|
820
|
+
manifest: &parser::manifest::Manifest,
|
|
821
|
+
) -> Result<DialectType> {
|
|
822
|
+
if let Some(d) = cli_dialect {
|
|
823
|
+
return Ok(d);
|
|
824
|
+
}
|
|
825
|
+
match manifest.metadata.adapter_type.as_deref() {
|
|
826
|
+
Some(adapter) if !adapter.trim().is_empty() => {
|
|
827
|
+
adapter.trim().parse::<DialectType>().map_err(|_| {
|
|
828
|
+
anyhow::anyhow!(
|
|
829
|
+
"manifest adapter_type '{}' has no matching SQL dialect; \
|
|
830
|
+
use --dialect to specify one explicitly (e.g. --dialect postgres)",
|
|
831
|
+
adapter.trim()
|
|
832
|
+
)
|
|
833
|
+
})
|
|
834
|
+
}
|
|
835
|
+
Some(_) => anyhow::bail!(
|
|
836
|
+
"manifest adapter_type is empty; \
|
|
837
|
+
use --dialect to specify the SQL dialect (e.g. --dialect bigquery)"
|
|
838
|
+
),
|
|
839
|
+
None => anyhow::bail!(
|
|
840
|
+
"manifest does not specify an adapter_type; \
|
|
841
|
+
use --dialect to specify the SQL dialect (e.g. --dialect bigquery)"
|
|
842
|
+
),
|
|
843
|
+
}
|
|
844
|
+
}
|
|
845
|
+
|
|
813
846
|
/// Run the `column-lineage` subcommand
|
|
814
847
|
#[cfg(not(tarpaulin_include))]
|
|
815
848
|
#[allow(clippy::too_many_arguments)]
|
|
@@ -824,8 +857,6 @@ fn run_column_lineage_command(
|
|
|
824
857
|
no_cache: bool,
|
|
825
858
|
refresh_cache: bool,
|
|
826
859
|
) -> Result<()> {
|
|
827
|
-
let dialect = dialect.unwrap_or(DialectType::Generic);
|
|
828
|
-
|
|
829
860
|
let project_dir = project_dir
|
|
830
861
|
.canonicalize()
|
|
831
862
|
.unwrap_or_else(|_| project_dir.to_path_buf());
|
|
@@ -847,6 +878,8 @@ fn run_column_lineage_command(
|
|
|
847
878
|
let resolved_manifest_path = resolve_manifest_path_or_default(manifest_path, &project_dir)?;
|
|
848
879
|
let manifest = parser::manifest::load_manifest(&resolved_manifest_path)?;
|
|
849
880
|
|
|
881
|
+
let dialect = resolve_dialect(dialect, &manifest)?;
|
|
882
|
+
|
|
850
883
|
let models = if input::has_path_like_input(&raw_inputs) {
|
|
851
884
|
let dag = parser::manifest::build_graph_from_parsed_manifest(&manifest)?;
|
|
852
885
|
let cwd = std::env::current_dir()
|
|
@@ -1038,8 +1071,6 @@ fn run_column_impact_command(
|
|
|
1038
1071
|
no_cache: bool,
|
|
1039
1072
|
refresh_cache: bool,
|
|
1040
1073
|
) -> Result<()> {
|
|
1041
|
-
let dialect = dialect.unwrap_or(DialectType::Generic);
|
|
1042
|
-
|
|
1043
1074
|
if columns.is_empty() {
|
|
1044
1075
|
anyhow::bail!("no columns specified (use --column)");
|
|
1045
1076
|
}
|
|
@@ -1051,6 +1082,8 @@ fn run_column_impact_command(
|
|
|
1051
1082
|
let resolved = resolve_manifest_path_or_default(manifest_path, &project_dir)?;
|
|
1052
1083
|
let manifest = parser::manifest::load_manifest(&resolved)?;
|
|
1053
1084
|
|
|
1085
|
+
let dialect = resolve_dialect(dialect, &manifest)?;
|
|
1086
|
+
|
|
1054
1087
|
let mut cache = if no_cache {
|
|
1055
1088
|
graph::column_lineage::ColumnLineageCache::disabled()
|
|
1056
1089
|
} else if refresh_cache {
|
|
@@ -231,7 +231,7 @@ fn tools() -> Vec<Value> {
|
|
|
231
231
|
"query": { "type": "string", "description": "Case-insensitive substring filter applied to node name, description, tags, and file path. Returns all nodes when omitted." },
|
|
232
232
|
"node_types": {
|
|
233
233
|
"type": "array",
|
|
234
|
-
"items": { "type": "string", "enum": ["model", "source", "seed", "snapshot", "test", "exposure"] },
|
|
234
|
+
"items": { "type": "string", "enum": ["model", "source", "seed", "snapshot", "test", "exposure", "semantic_model", "metric", "saved_query"] },
|
|
235
235
|
"description": "Node types to include. Defaults to all types when omitted."
|
|
236
236
|
}
|
|
237
237
|
},
|
|
@@ -247,7 +247,7 @@ fn tools() -> Vec<Value> {
|
|
|
247
247
|
"names": { "type": "array", "items": { "type": "string" }, "minItems": 1, "description": "Node names (e.g. 'orders') or dbt unique IDs (e.g. 'model.my_project.orders', 'source.my_project.raw.orders') to look up." },
|
|
248
248
|
"node_types": {
|
|
249
249
|
"type": "array",
|
|
250
|
-
"items": { "type": "string", "enum": ["model", "source", "seed", "snapshot", "test", "exposure"] },
|
|
250
|
+
"items": { "type": "string", "enum": ["model", "source", "seed", "snapshot", "test", "exposure", "semantic_model", "metric", "saved_query"] },
|
|
251
251
|
"description": "Restrict results to these node types. Nodes whose type does not match are reported in not_found."
|
|
252
252
|
}
|
|
253
253
|
},
|
|
@@ -451,6 +451,9 @@ fn node_type_from_str(s: &str) -> Option<NodeType> {
|
|
|
451
451
|
"snapshot" => Some(NodeType::Snapshot),
|
|
452
452
|
"test" => Some(NodeType::Test),
|
|
453
453
|
"exposure" => Some(NodeType::Exposure),
|
|
454
|
+
"semantic_model" => Some(NodeType::SemanticModel),
|
|
455
|
+
"metric" => Some(NodeType::Metric),
|
|
456
|
+
"saved_query" => Some(NodeType::SavedQuery),
|
|
454
457
|
_ => None,
|
|
455
458
|
}
|
|
456
459
|
}
|
|
@@ -770,7 +773,8 @@ fn normalize_manifest_unique_id(name: &str) -> Option<String> {
|
|
|
770
773
|
None
|
|
771
774
|
}
|
|
772
775
|
}
|
|
773
|
-
"model" | "seed" | "snapshot" | "exposure"
|
|
776
|
+
"model" | "seed" | "snapshot" | "exposure" | "semantic_model" | "metric"
|
|
777
|
+
| "saved_query" => {
|
|
774
778
|
parts.remove(1);
|
|
775
779
|
Some(parts.join("."))
|
|
776
780
|
}
|
|
@@ -1268,6 +1272,87 @@ mod tests {
|
|
|
1268
1272
|
assert_eq!(normalized, Some("test.my_test_name".to_string()));
|
|
1269
1273
|
}
|
|
1270
1274
|
|
|
1275
|
+
#[test]
|
|
1276
|
+
fn normalize_manifest_unique_id_supports_semantic_layer_types() {
|
|
1277
|
+
assert_eq!(
|
|
1278
|
+
normalize_manifest_unique_id("semantic_model.my_project.orders"),
|
|
1279
|
+
Some("semantic_model.orders".to_string())
|
|
1280
|
+
);
|
|
1281
|
+
assert_eq!(
|
|
1282
|
+
normalize_manifest_unique_id("metric.my_project.revenue"),
|
|
1283
|
+
Some("metric.revenue".to_string())
|
|
1284
|
+
);
|
|
1285
|
+
assert_eq!(
|
|
1286
|
+
normalize_manifest_unique_id("saved_query.my_project.revenue_metrics"),
|
|
1287
|
+
Some("saved_query.revenue_metrics".to_string())
|
|
1288
|
+
);
|
|
1289
|
+
}
|
|
1290
|
+
|
|
1291
|
+
#[test]
|
|
1292
|
+
fn find_nodes_resolves_semantic_layer_full_unique_ids() {
|
|
1293
|
+
let manifest_path = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
|
1294
|
+
.join("..")
|
|
1295
|
+
.join("..")
|
|
1296
|
+
.join("refs")
|
|
1297
|
+
.join("jaffle-shop")
|
|
1298
|
+
.join("target")
|
|
1299
|
+
.join("manifest.json");
|
|
1300
|
+
if !manifest_path.exists() {
|
|
1301
|
+
eprintln!(
|
|
1302
|
+
"SKIP: jaffle-shop fixture not found at {manifest_path:?}; run `make fixtures` to enable this test"
|
|
1303
|
+
);
|
|
1304
|
+
return;
|
|
1305
|
+
}
|
|
1306
|
+
let state = McpState::load(McpArgs {
|
|
1307
|
+
project_dir: manifest_path
|
|
1308
|
+
.parent()
|
|
1309
|
+
.unwrap()
|
|
1310
|
+
.parent()
|
|
1311
|
+
.unwrap()
|
|
1312
|
+
.to_path_buf(),
|
|
1313
|
+
manifest_path: Some(manifest_path),
|
|
1314
|
+
dialect: DialectType::Generic,
|
|
1315
|
+
})
|
|
1316
|
+
.unwrap();
|
|
1317
|
+
|
|
1318
|
+
let result = find_nodes(
|
|
1319
|
+
&json!({
|
|
1320
|
+
"names": [
|
|
1321
|
+
"semantic_model.jaffle_shop.supplies",
|
|
1322
|
+
"metric.jaffle_shop.revenue",
|
|
1323
|
+
"saved_query.jaffle_shop.revenue_metrics"
|
|
1324
|
+
]
|
|
1325
|
+
}),
|
|
1326
|
+
&state,
|
|
1327
|
+
)
|
|
1328
|
+
.unwrap();
|
|
1329
|
+
|
|
1330
|
+
assert_eq!(
|
|
1331
|
+
result["not_found"],
|
|
1332
|
+
json!([]),
|
|
1333
|
+
"all full unique IDs should resolve"
|
|
1334
|
+
);
|
|
1335
|
+
assert_eq!(result["count"], 3);
|
|
1336
|
+
let unique_ids: Vec<&str> = result["nodes"]
|
|
1337
|
+
.as_array()
|
|
1338
|
+
.unwrap()
|
|
1339
|
+
.iter()
|
|
1340
|
+
.map(|n| n["unique_id"].as_str().unwrap())
|
|
1341
|
+
.collect();
|
|
1342
|
+
assert!(
|
|
1343
|
+
unique_ids.contains(&"semantic_model.supplies"),
|
|
1344
|
+
"semantic_model node should resolve to correct type"
|
|
1345
|
+
);
|
|
1346
|
+
assert!(
|
|
1347
|
+
unique_ids.contains(&"metric.revenue"),
|
|
1348
|
+
"metric node should resolve to correct type"
|
|
1349
|
+
);
|
|
1350
|
+
assert!(
|
|
1351
|
+
unique_ids.contains(&"saved_query.revenue_metrics"),
|
|
1352
|
+
"saved_query node should resolve to correct type"
|
|
1353
|
+
);
|
|
1354
|
+
}
|
|
1355
|
+
|
|
1271
1356
|
#[test]
|
|
1272
1357
|
fn extract_column_not_found_name_parses_column_prefix() {
|
|
1273
1358
|
assert_eq!(
|
|
@@ -2401,7 +2401,7 @@ mod manifest_only_mode {
|
|
|
2401
2401
|
fs::create_dir_all(tmp.path().join("target")).unwrap();
|
|
2402
2402
|
|
|
2403
2403
|
let manifest_json = r#"{
|
|
2404
|
-
"metadata": {},
|
|
2404
|
+
"metadata": {"adapter_type": "postgres"},
|
|
2405
2405
|
"nodes": {
|
|
2406
2406
|
"model.test_project.stg_orders": {
|
|
2407
2407
|
"unique_id": "model.test_project.stg_orders",
|
|
@@ -2471,4 +2471,248 @@ mod manifest_only_mode {
|
|
|
2471
2471
|
stdout
|
|
2472
2472
|
);
|
|
2473
2473
|
}
|
|
2474
|
+
|
|
2475
|
+
/// Build a manifest JSON string with the given adapter_type value (or none).
|
|
2476
|
+
fn manifest_json_with_adapter(adapter_type: Option<&str>) -> String {
|
|
2477
|
+
let metadata = match adapter_type {
|
|
2478
|
+
Some(a) => format!(r#"{{"adapter_type": "{}"}}"#, a),
|
|
2479
|
+
None => "{}".to_string(),
|
|
2480
|
+
};
|
|
2481
|
+
format!(
|
|
2482
|
+
r#"{{
|
|
2483
|
+
"metadata": {},
|
|
2484
|
+
"nodes": {{
|
|
2485
|
+
"model.test_project.stg_orders": {{
|
|
2486
|
+
"unique_id": "model.test_project.stg_orders",
|
|
2487
|
+
"name": "stg_orders",
|
|
2488
|
+
"original_file_path": "models/stg_orders.sql",
|
|
2489
|
+
"resource_type": "model",
|
|
2490
|
+
"depends_on": {{"nodes": []}},
|
|
2491
|
+
"config": {{"materialized": "view", "tags": []}},
|
|
2492
|
+
"description": "",
|
|
2493
|
+
"compiled_code": "SELECT order_id FROM raw.orders",
|
|
2494
|
+
"columns": {{"order_id": {{"name": "order_id", "description": ""}}}}
|
|
2495
|
+
}}
|
|
2496
|
+
}},
|
|
2497
|
+
"sources": {{}},
|
|
2498
|
+
"exposures": {{}}
|
|
2499
|
+
}}"#,
|
|
2500
|
+
metadata
|
|
2501
|
+
)
|
|
2502
|
+
}
|
|
2503
|
+
|
|
2504
|
+
fn write_manifest(dir: &std::path::Path, json: &str) {
|
|
2505
|
+
fs::create_dir_all(dir.join("target")).unwrap();
|
|
2506
|
+
fs::write(dir.join("target/manifest.json"), json).unwrap();
|
|
2507
|
+
}
|
|
2508
|
+
|
|
2509
|
+
#[test]
|
|
2510
|
+
fn test_column_upstream_auto_detects_dialect_from_manifest_adapter_type() {
|
|
2511
|
+
// When --dialect is omitted but manifest has adapter_type, dialect is auto-detected.
|
|
2512
|
+
let tmp = tempfile::tempdir().unwrap();
|
|
2513
|
+
write_manifest(tmp.path(), &manifest_json_with_adapter(Some("postgres")));
|
|
2514
|
+
|
|
2515
|
+
let output = Command::new(binary_path())
|
|
2516
|
+
.args([
|
|
2517
|
+
"column",
|
|
2518
|
+
"upstream",
|
|
2519
|
+
"stg_orders",
|
|
2520
|
+
"--project-dir",
|
|
2521
|
+
tmp.path().to_str().unwrap(),
|
|
2522
|
+
"--no-cache",
|
|
2523
|
+
])
|
|
2524
|
+
.output()
|
|
2525
|
+
.expect("Failed to run binary");
|
|
2526
|
+
|
|
2527
|
+
assert!(
|
|
2528
|
+
output.status.success(),
|
|
2529
|
+
"should succeed with auto-detected dialect from adapter_type; stderr: {}",
|
|
2530
|
+
String::from_utf8_lossy(&output.stderr)
|
|
2531
|
+
);
|
|
2532
|
+
}
|
|
2533
|
+
|
|
2534
|
+
#[test]
|
|
2535
|
+
fn test_column_upstream_errors_when_no_adapter_type_and_no_dialect_flag() {
|
|
2536
|
+
// When both --dialect and manifest adapter_type are absent, the command must fail
|
|
2537
|
+
// with an actionable error rather than silently using Generic.
|
|
2538
|
+
let tmp = tempfile::tempdir().unwrap();
|
|
2539
|
+
write_manifest(tmp.path(), &manifest_json_with_adapter(None));
|
|
2540
|
+
|
|
2541
|
+
let output = Command::new(binary_path())
|
|
2542
|
+
.args([
|
|
2543
|
+
"column",
|
|
2544
|
+
"upstream",
|
|
2545
|
+
"stg_orders",
|
|
2546
|
+
"--project-dir",
|
|
2547
|
+
tmp.path().to_str().unwrap(),
|
|
2548
|
+
"--no-cache",
|
|
2549
|
+
])
|
|
2550
|
+
.output()
|
|
2551
|
+
.expect("Failed to run binary");
|
|
2552
|
+
|
|
2553
|
+
assert!(
|
|
2554
|
+
!output.status.success(),
|
|
2555
|
+
"should fail when adapter_type is absent and --dialect is not given"
|
|
2556
|
+
);
|
|
2557
|
+
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
2558
|
+
assert!(
|
|
2559
|
+
stderr.contains("does not specify an adapter_type"),
|
|
2560
|
+
"error message should say 'does not specify an adapter_type': {}",
|
|
2561
|
+
stderr
|
|
2562
|
+
);
|
|
2563
|
+
}
|
|
2564
|
+
|
|
2565
|
+
#[test]
|
|
2566
|
+
fn test_column_upstream_errors_on_unknown_adapter_type() {
|
|
2567
|
+
// An unrecognised adapter_type in the manifest must produce a clear error
|
|
2568
|
+
// telling the user to pass --dialect explicitly.
|
|
2569
|
+
let tmp = tempfile::tempdir().unwrap();
|
|
2570
|
+
write_manifest(
|
|
2571
|
+
tmp.path(),
|
|
2572
|
+
&manifest_json_with_adapter(Some("unknown_warehouse")),
|
|
2573
|
+
);
|
|
2574
|
+
|
|
2575
|
+
let output = Command::new(binary_path())
|
|
2576
|
+
.args([
|
|
2577
|
+
"column",
|
|
2578
|
+
"upstream",
|
|
2579
|
+
"stg_orders",
|
|
2580
|
+
"--project-dir",
|
|
2581
|
+
tmp.path().to_str().unwrap(),
|
|
2582
|
+
"--no-cache",
|
|
2583
|
+
])
|
|
2584
|
+
.output()
|
|
2585
|
+
.expect("Failed to run binary");
|
|
2586
|
+
|
|
2587
|
+
assert!(
|
|
2588
|
+
!output.status.success(),
|
|
2589
|
+
"should fail when adapter_type is not a known dialect"
|
|
2590
|
+
);
|
|
2591
|
+
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
2592
|
+
assert!(
|
|
2593
|
+
stderr.contains("unknown_warehouse") && stderr.contains("--dialect"),
|
|
2594
|
+
"error should name the unknown adapter and suggest --dialect: {}",
|
|
2595
|
+
stderr
|
|
2596
|
+
);
|
|
2597
|
+
}
|
|
2598
|
+
|
|
2599
|
+
#[test]
|
|
2600
|
+
fn test_column_upstream_dialect_flag_overrides_manifest_adapter_type() {
|
|
2601
|
+
// --dialect takes precedence over whatever adapter_type the manifest declares.
|
|
2602
|
+
let tmp = tempfile::tempdir().unwrap();
|
|
2603
|
+
// adapter_type is deliberately wrong (trino); --dialect postgres should win.
|
|
2604
|
+
write_manifest(tmp.path(), &manifest_json_with_adapter(Some("trino")));
|
|
2605
|
+
|
|
2606
|
+
let output = Command::new(binary_path())
|
|
2607
|
+
.args([
|
|
2608
|
+
"column",
|
|
2609
|
+
"upstream",
|
|
2610
|
+
"stg_orders",
|
|
2611
|
+
"--project-dir",
|
|
2612
|
+
tmp.path().to_str().unwrap(),
|
|
2613
|
+
"--dialect",
|
|
2614
|
+
"postgres",
|
|
2615
|
+
"--no-cache",
|
|
2616
|
+
])
|
|
2617
|
+
.output()
|
|
2618
|
+
.expect("Failed to run binary");
|
|
2619
|
+
|
|
2620
|
+
assert!(
|
|
2621
|
+
output.status.success(),
|
|
2622
|
+
"--dialect flag should override adapter_type in manifest; stderr: {}",
|
|
2623
|
+
String::from_utf8_lossy(&output.stderr)
|
|
2624
|
+
);
|
|
2625
|
+
}
|
|
2626
|
+
|
|
2627
|
+
#[test]
|
|
2628
|
+
fn test_column_downstream_errors_when_no_adapter_type_and_no_dialect_flag() {
|
|
2629
|
+
// Same error behaviour for the impact (downstream) subcommand.
|
|
2630
|
+
let tmp = tempfile::tempdir().unwrap();
|
|
2631
|
+
write_manifest(tmp.path(), &manifest_json_with_adapter(None));
|
|
2632
|
+
|
|
2633
|
+
let output = Command::new(binary_path())
|
|
2634
|
+
.args([
|
|
2635
|
+
"column",
|
|
2636
|
+
"downstream",
|
|
2637
|
+
"stg_orders",
|
|
2638
|
+
"--column",
|
|
2639
|
+
"order_id",
|
|
2640
|
+
"--project-dir",
|
|
2641
|
+
tmp.path().to_str().unwrap(),
|
|
2642
|
+
"--no-cache",
|
|
2643
|
+
])
|
|
2644
|
+
.output()
|
|
2645
|
+
.expect("Failed to run binary");
|
|
2646
|
+
|
|
2647
|
+
assert!(
|
|
2648
|
+
!output.status.success(),
|
|
2649
|
+
"column downstream should also fail without adapter_type and --dialect"
|
|
2650
|
+
);
|
|
2651
|
+
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
2652
|
+
assert!(
|
|
2653
|
+
stderr.contains("does not specify an adapter_type"),
|
|
2654
|
+
"error message should say 'does not specify an adapter_type': {}",
|
|
2655
|
+
stderr
|
|
2656
|
+
);
|
|
2657
|
+
}
|
|
2658
|
+
|
|
2659
|
+
#[test]
|
|
2660
|
+
fn test_column_upstream_errors_on_empty_adapter_type() {
|
|
2661
|
+
// An empty string adapter_type must not silently fall back to Generic.
|
|
2662
|
+
let tmp = tempfile::tempdir().unwrap();
|
|
2663
|
+
write_manifest(tmp.path(), &manifest_json_with_adapter(Some("")));
|
|
2664
|
+
|
|
2665
|
+
let output = Command::new(binary_path())
|
|
2666
|
+
.args([
|
|
2667
|
+
"column",
|
|
2668
|
+
"upstream",
|
|
2669
|
+
"stg_orders",
|
|
2670
|
+
"--project-dir",
|
|
2671
|
+
tmp.path().to_str().unwrap(),
|
|
2672
|
+
"--no-cache",
|
|
2673
|
+
])
|
|
2674
|
+
.output()
|
|
2675
|
+
.expect("Failed to run binary");
|
|
2676
|
+
|
|
2677
|
+
assert!(
|
|
2678
|
+
!output.status.success(),
|
|
2679
|
+
"empty adapter_type should fail rather than silently using Generic"
|
|
2680
|
+
);
|
|
2681
|
+
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
2682
|
+
assert!(
|
|
2683
|
+
stderr.contains("adapter_type is empty"),
|
|
2684
|
+
"error message should say 'adapter_type is empty': {}",
|
|
2685
|
+
stderr
|
|
2686
|
+
);
|
|
2687
|
+
}
|
|
2688
|
+
|
|
2689
|
+
#[test]
|
|
2690
|
+
fn test_column_upstream_errors_on_whitespace_only_adapter_type() {
|
|
2691
|
+
// A whitespace-only adapter_type must not silently fall back to Generic.
|
|
2692
|
+
let tmp = tempfile::tempdir().unwrap();
|
|
2693
|
+
write_manifest(tmp.path(), &manifest_json_with_adapter(Some(" ")));
|
|
2694
|
+
|
|
2695
|
+
let output = Command::new(binary_path())
|
|
2696
|
+
.args([
|
|
2697
|
+
"column",
|
|
2698
|
+
"upstream",
|
|
2699
|
+
"stg_orders",
|
|
2700
|
+
"--project-dir",
|
|
2701
|
+
tmp.path().to_str().unwrap(),
|
|
2702
|
+
"--no-cache",
|
|
2703
|
+
])
|
|
2704
|
+
.output()
|
|
2705
|
+
.expect("Failed to run binary");
|
|
2706
|
+
|
|
2707
|
+
assert!(
|
|
2708
|
+
!output.status.success(),
|
|
2709
|
+
"whitespace-only adapter_type should fail rather than silently using Generic"
|
|
2710
|
+
);
|
|
2711
|
+
let stderr = String::from_utf8_lossy(&output.stderr);
|
|
2712
|
+
assert!(
|
|
2713
|
+
stderr.contains("adapter_type is empty"),
|
|
2714
|
+
"error message should say 'adapter_type is empty': {}",
|
|
2715
|
+
stderr
|
|
2716
|
+
);
|
|
2717
|
+
}
|
|
2474
2718
|
}
|