Skip to content

feat: add SQL and dbt support#17

Open
tglunde wants to merge 2 commits into
TtTRz:mainfrom
tglunde:sql-language
Open

feat: add SQL and dbt support#17
tglunde wants to merge 2 commits into
TtTRz:mainfrom
tglunde:sql-language

Conversation

@tglunde

@tglunde tglunde commented Jul 11, 2026

Copy link
Copy Markdown

Summary

Adds first-class SQL extraction and dbt project integration.

Added

  • SQL language support — extraction of DDL (tables/views), FROM/JOIN dependencies, foreign keys, and column-level lineage via tree-sitter-sequel. Relations are scoped globally by schema.name, so references resolve across files.
  • dbt project support — parses target/manifest.json into Relation nodes and depends_on edges. Compiled models under target/compiled/ are mapped back to their manifest node (via compiled_path) and analyzed for column-level lineage attributed to that model. Manifest parsing is the default; running dbt compile is opt-in via --dbt-compile (with --dbt-timeout-secs, default 120) since it executes the project's dbt.
  • Column-level lineage traces through CTEs and inline subqueries down to base-table columns (derives_from edges).
  • dbt models are identified by their warehouse-side name (alias, falling back to model name; identifier for sources), so plain-SQL references to materialized tables resolve to the dbt Relation node. The dbt-side name is kept in extra["dbt_name"]; relation_kind carries the actual resource type (model/seed/snapshot/source).

Notes

  • 3-part (catalog.schema.name) relation lookup: The relation ID is intentionally derived only from schema.name to merge representations of identical tables across files. If two relations share schema.name but differ in catalog, they will collide on the same ID. The catalog is extracted and preserved in the extra metadata.
  • Ambiguous unqualified columns (multiple tables in scope) produce no derives_from edge rather than an unknown_table placeholder.
  • Cross-file SQL stub resolution is scoped to SQL/dbt edges only and never affects edges from other extractors. The build pipeline strips per-file stubs after merging all extraction results (including dbt) and re-resolves once against the full graph, so a stub can never shadow a relation defined in another file.

Checklist

  • cargo fmt --all passes
  • cargo clippy --workspace -- -D warnings passes
  • cargo test --workspace passes
  • New code has tests
  • CHANGELOG.md updated
  • Documentation updated (ARCHITECTURE.md)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class SQL extraction and dbt project integration to Graphify’s AST extraction pipeline, including optional dbt compile support and cross-file SQL/dbt dependency resolution during CLI builds.

Changes:

  • Added a dedicated SQL extractor (tree-sitter-sequel) with dependency + FK + column lineage support and SQL-only cross-file stub resolution helpers.
  • Added dbt project detection + manifest parsing + compiled-model lineage extraction, with new CLI flags to optionally run dbt compile.
  • Wired SQL/dbt into the CLI build pipeline and updated architecture docs + changelog + workspace deps.

Reviewed changes

Copilot reviewed 16 out of 18 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
src/main.rs Adds CLI flags for dbt compile + timeout; threads dbt options into build command.
src/cmd_build.rs Integrates dbt detection/extraction and runs merged SQL/dbt cross-file resolution in the AST step.
docs/ARCHITECTURE.md Updates architecture docs to mention SQL/dbt and bumps supported language count.
crates/graphify-extract/tests/ast_extract.rs Adds SQL routing + integration tests (including cross-file resolution scenarios).
crates/graphify-extract/src/treesitter/treesitter_config.rs Explicitly bypasses generic tree-sitter config for SQL to force dedicated extractor.
crates/graphify-extract/src/sql.rs New SQL extractor implementation + SQL cross-file stub resolver + extensive unit tests.
crates/graphify-extract/src/lib.rs Routes .sql through the dedicated SQL extractor; documents deferral of SQL cross-file resolution.
crates/graphify-extract/src/dbt.rs New dbt extractor: optional compile, manifest parsing, compiled SQL lineage attribution.
crates/graphify-extract/Cargo.toml Adds dependencies needed for dbt + SQL extraction (graphify-detect, walkdir, tree-sitter-sequel).
crates/graphify-detect/src/lib.rs Exposes dbt detection APIs from graphify-detect.
crates/graphify-detect/src/dbt.rs New dbt project detection via dbt_project.yml + managed SQL path discovery.
crates/graphify-detect/src/constants.rs Classifies .sql as a code extension.
crates/graphify-detect/Cargo.toml Adds YAML parsing dependency and moves walkdir to workspace dependency.
crates/graphify-core/src/model.rs Adds new node types (Application/Relation/Column/Expression) to the core model.
CHANGELOG.md Documents the new SQL and dbt support under Unreleased.
Cargo.toml Adds workspace-level deps for walkdir and serde_yaml_ng.
Cargo.lock Locks new dependencies (serde_yaml_ng, tree-sitter-sequel, walkdir, etc.).
.gitignore Ignores *.patch files.
Comments suppressed due to low confidence (1)

src/cmd_build.rs:199

  • code_only mode returns early when code_files is empty, which also skips dbt extraction. This makes a repo with only dbt-managed SQL (filtered out above) produce no AST/dbt output even though dbt projects were detected.
    if code_files.is_empty() && code_only {
        info_print!(verb, "  No code files found. Nothing to extract.");
        return Ok(vec![]);
    }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/cmd_build.rs
Comment on lines +179 to +183
let dbt_projects = graphify_detect::detect_dbt_projects(root);
let mut dbt_managed_paths = std::collections::HashSet::new();
for project in &dbt_projects {
dbt_managed_paths.extend(project.managed_sql_paths.iter().cloned());
}
Comment on lines +265 to +269
fn handle_create_relation(&mut self, node: Node, kind: &str) {
if let Some(obj_ref) = self.find_child_by_kind_direct(node, "object_reference") {
let (catalog, schema, name) = self.parse_object_reference(obj_ref);
let relation_id = make_id(&["rel", catalog.as_deref().unwrap_or(""), &schema, &name]);
self.defined_relations.insert(relation_id.clone());
Comment on lines +234 to +237
let schema = node.get("schema").and_then(|v| v.as_str()).unwrap_or("");
let database = node.get("database").and_then(|v| v.as_str()).unwrap_or("");
let rel_id = make_id(&["rel", database, schema, identifier]);

Comment thread crates/graphify-extract/src/lib.rs Outdated
Co-authored-by: Copilot Autofix powered by AI <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants