Skip to content

Feat: Add config flag to infer the state schema per dbt target#5485

Merged
erindru merged 5 commits intomainfrom
erin/dbt-state-per-target
Oct 8, 2025
Merged

Feat: Add config flag to infer the state schema per dbt target#5485
erindru merged 5 commits intomainfrom
erin/dbt-state-per-target

Conversation

@erindru
Copy link
Collaborator

@erindru erindru commented Oct 6, 2025

Currently, when storing state in the warehouse, by default SQLMesh assumes that you only have a single project accessing the warehouse and creates a schema called sqlmesh to store state in. This is because it also assumes that you want to use Virtual Data Environments.

However, this creates a problem in dbt projects with existing workflows that are not built around VDE's. A common pattern is to use different targets to point to the same warehouse and just override the default schema.

For example, a dev target may populate models into a dev schema while a prod target may put them in a prod schema, but these schemas exist side by side in the same warehouse. Creating models using --target dev allows analysts to test things out and then deploy by running the models against --target prod.

This creates a problem for SQLMesh because it assumes a single state schema but the state for these targets should not overlap.

So this PR:

  • Adds a new section to the root config called dbt with, for now, a single flag infer_state_schema_name
  • Uses this flag to, if set, infer a default state schema name based on the dbt profile name and target schema
  • Enables this flag by default for sqlmesh init -t dbt so that dbt projects by default have isolated state between targets

Note that users can still override the inferred schema by setting the state schema manually in sqlmesh.yaml as documented here. Existing projects will not have this new property set so will continue to behave as usual.

@erindru erindru force-pushed the erin/dbt-state-per-target branch from 8784e22 to bd16a2f Compare October 7, 2025 00:54
@erindru erindru marked this pull request as ready for review October 7, 2025 02:43
@erindru erindru changed the title Feat: Add 'state_schema_naming_pattern' to infer the state schema per dbt target Feat: Add config flag to infer the state schema per dbt target Oct 7, 2025
# for the 'dev' target is overriden to something user-specific, rather than making the target name itself user-specific.
# This means that the schema name is the indicator of isolated state, not the target name which may be re-used across multiple schemas.
target_schema = profile.target.schema_
gateway_kwargs["state_schema"] = f"sqlmesh_state_{profile_name}_{target_schema}"
Copy link
Collaborator

Choose a reason for hiding this comment

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

Should we check whether target_schema is empty?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Man, this was a rabbit hole.

So in dbt's Credentials object that defines this field, schema is required. If you don't define it, the base validator fails with something like:

Runtime Error
  Credentials in profile "jaffle_shop", target "postgres" invalid: 'schema' is a required property

(and some adapters, like the duckdb adapter, have a hardcoded default to pass this check).

dbt will, however, happily allow you to specify an empty string for the schema. If you do this, you push the failure to runtime:

23:11:33  Encountered an error:
Database Error
  zero-length delimited identifier at or near """"
  LINE 2: create schema if not exists ""

So i've changed our side to raise an exception if the schema is an empty string. If someone encounters this and their project works fine on dbt core then hopefully theyre willing to work with us to understand the correct behaviour here (and the workaround is of course to define the state schema name manually)

@erindru erindru force-pushed the erin/dbt-state-per-target branch from 3d4bf31 to c11c6d3 Compare October 7, 2025 23:20
@erindru erindru force-pushed the erin/dbt-state-per-target branch from c11c6d3 to 9cb3bce Compare October 7, 2025 23:50
@erindru erindru enabled auto-merge (squash) October 8, 2025 00:23
@erindru erindru merged commit 7d74690 into main Oct 8, 2025
35 of 36 checks passed
@erindru erindru deleted the erin/dbt-state-per-target branch October 8, 2025 00:29
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.

2 participants