Visitar URL original
feat: dbt_map_relations maps the tables of raw SQL to ref() and source() calls by hkaltai · Pull Request #1737 · AltimateAI/altimate-code · GitHub
Skip to content

feat: dbt_map_relations maps the tables of raw SQL to ref() and source() calls - #1737

Draft
hkaltai wants to merge 7 commits into
mainfrom
feat/dbt-map-relations-tool
Draft

hkaltai wants to merge 7 commits into
mainfrom
feat/dbt-map-relations-tool

Conversation

@hkaltai

@hkaltai hkaltai commented Nov 13, 2026 •

Copy link
Copy Markdown
Contributor

What

dbt_map_relations { sql, dialect?, manifest_path? } is a read-only tool. It reads the tables of a raw SQL query and matches each one to the relation_name of a model, snapshot, seed or source in the dbt manifest. For each table it returns { raw, replaceWith, match, candidates }, where match is exact, case_insensitive, partial or none and replaceWith is a {{ ref('x') }} or {{ source('s', 't') }} call (the table as written when nothing matched). A model is preferred over a source when both match. It backs the plugin's /to-model command.

Tables come from altimate-core extractMetadata, which keeps only the last name part. The schema and database the person wrote are read back from the SQL text, so a fully written name picks the right relation. Quoted, upper-case, two- and three-part names are covered for Snowflake, BigQuery and Databricks.

Tests

bun test test/altimate/dbt-map-relations.test.ts: 27 pass. The first block prints what extractMetadata returns for each dialect and the matching rules are based on that. Every match value is covered, plus a missing manifest, unparseable SQL and SQL with no tables. bunx tsgo --noEmit is clean for the new file.

Cost side effects

None. No LLM calls, no warehouse calls, no adapter start. It reads manifest.json only.

Changelog lines

  • dbt_map_relations tool: map the tables of raw SQL to ref() and source() calls from the dbt manifest.

The plugin PR (/to-model) depends on this one. Merge this first.

🤖 Generated with Claude Code


Summary by cubic

Ships the first dbt Power User tool: dbt_map_relations maps each table a raw SQL query reads to the ref() or source() call that builds it, using the dbt manifest. The other eight tools are registered stubs that answer "not implemented".

Details

  • A tokenizer reads the SQL per dialect: quoted names, strings, and dollar quotes are skipped, and whitespace or comments around dots and tables after a subquery in a FROM list are handled; a CTE's name is not a table, and a physical table a CTE shares its name with keeps its qualifiers.
  • Each table returns its best match and all candidates, ranked exact, case_insensitive, partial, then none, preferring a model over a source when both match; versioned models are referenced with v=.
  • Quoted, upper-case, and two- and three-part names are covered for Snowflake, BigQuery, and Databricks.
  • dbtAdapterFor gates every adapter-backed tool: it refuses read-only and plan-mode calls, sessions in another dbt project, and adapters that fail to start or load.
  • AltimateApi.apiRequest calls the backend with the stored credentials and names the credentials file in errors.
  • The tool is read-only: no LLM, warehouse, or adapter calls; it reads manifest.json only, and fails clearly when run outside a dbt project without manifest_path.

Written for commit 0f33f9a. Summary will update on new commits.

View guided diff Turn on auto-fix

hkaltai and others added 4 commits November 13, 2026 16:30
Nine tool stubs exposed through `altimate tool run` (dbt_run_results,
dbt_defer_state, dbt_map_relations, docs_share_comments, dbt_cost_estimate,
dbt_health, dbt_sql_graph, dbt_docs_coverage, dbt_cte_profile), an exported
`AltimateApi.apiRequest` that calls the backend with the stored credentials,
and `dbtAdapterFor`, the one gate every adapter-backed tool goes through:
it refuses read-only calls, other projects, and an adapter that did not start.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…e() calls

Each table the SQL reads is matched against the relation_name of the manifest's
models, snapshots, seeds and sources, with the match reported as exact,
case_insensitive, partial or none and every candidate listed.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Nov 13, 2026

Copy link
Copy Markdown

Important

Draft PR not reviewed

Draft PRs are not automatically reviewed by default.

  • Trigger a manual review

To automatically review draft PRs, update your CodeRabbit configuration:

reviews:
  auto_review:
    drafts: true
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

Share

@github-actions

Copy link
Copy Markdown

This PR doesn't fully meet our contributing guidelines and PR template.

What needs to be fixed:

  • PR description is missing required template sections. Please use the PR template.
  • "Cost side effects" section is missing or empty. Describe any model calls, queries or cloud resources whose cost grows with user data, or write "None".

Please edit this PR description to address the above within 2 hours, or it will be automatically closed.

If you believe this was flagged incorrectly, please let a maintainer know.

@github-actions

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

hkaltai and others added 3 commits November 13, 2026 17:12
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
# Conflicts:
#	packages/opencode/src/altimate/tools/dbt-map-relations.ts
…nd versioned models

The table scan now walks tokens instead of matching keywords in text: quoted
names, strings and dollar quotes are skipped by dialect, dots may have
whitespace or comments around them, tables after a subquery or table function
in a FROM list are read, a CTE's name is not a table, and a physical table a
CTE shares its name with keeps its qualifiers. Versioned models are referenced
with v=.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

1 similar comment
@github-actions

Copy link
Copy Markdown

👋 This PR was automatically closed by our quality checks.

Common reasons:

  • New GitHub account with limited contribution history
  • PR description doesn't meet our guidelines
  • Contribution appears to be AI-generated without meaningful review

If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you.

@hkaltai

hkaltai commented Nov 13, 2026

Copy link
Copy Markdown
Contributor Author

Review round: findings and what was done

Two independent reviewers (Claude and Codex, separate CLI processes) read this PR and the plugin PR together; a fresh reviewer then attacked the fixes. Fixes are in 0f33f9a.

Fixed

  1. A CTE named like a table (both reviewers, MAJOR): the bare CTE reference was mapped to a ref(), and the physical table the CTE reads was dropped because altimate-core leaves out any name a CTE shares. The scan now collects CTE names, skips references to them, and keeps the qualified table. A table read inside its own non-recursive CTE of the same name counts as the real table.
  2. Versioned models (both, MAJOR): ref('orders') resolves to the latest version, so a match on orders_v1 pointed at other data. Versioned nodes now give ref('orders', v=1) (ref('pkg', 'orders', v=1) for a package).
  3. Whitespace or a comment around a dot dropped the qualifier (Codex, MAJOR).
  4. A table after a subquery or table function in a comma list was missed (Codex, MAJOR).
  5. from, join, using and so on inside quoted names, strings, $$ quotes, BigQuery raw strings produced made-up tables (Codex, MAJOR). The text scan is now a tokenizer with per-dialect rules for strings, quoted names, backslash escapes and brackets; EXTRACT(... FROM ...), TRIM(... FROM ...) and IS DISTINCT FROM are not clauses.
  6. Differently written forms of one table collapsed into one row (Codex, MINOR): rows are now kept per written form.
  7. The dialect name is lower-cased; the "outside a dbt project" test no longer reads $HOME (Claude, MINOR).
  8. With no dbt project around the working directory the tool said No manifest at target/manifest.json; it now says no dbt project was found (found while running the tool through altimate tool run).

The fix round's own reviewer found and I fixed: ClickHouse double quotes treated as strings, hyphenated BigQuery project ids after a comma, bracket names for fabric/synapse, insert into table x, dollar-quote tags with digits.

Not changed, with reasons

  • A partial match ignores case (Claude, MINOR, confidence 0.5): partial is the "confirm with me" tier, and case rules differ by dialect.
  • A CTE defined inside one subquery that shares its name with a real table used elsewhere is still treated as a CTE everywhere. Rare; recorded as a limitation.
  • Reviewers' parts <-> raw spellings such as a.b and a . b stay separate rows by design.
  • STRUCTURED_FIELDS over MCP (Claude, comment 0.4): the plugin calls the CLI, whose --json carries the whole metadata.

Needs an owner, not in this PR's files: test/altimate/dbt-adapter-access.test.ts ("each placeholder answers that it isn't implemented") runs every feature tool, so it fails as soon as any one of them is implemented, including this one. It needs to skip tools that no longer are placeholders; every package PR hits it.

Tests: bun test test/altimate/dbt-map-relations.test.ts 42 pass; bunx tsgo --noEmit clean for these files.

🤖 Generated with Claude Code

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant