Skip to content

Commit 8082a2c

Browse files
committed
Fix: Improve the table_name command
1 parent 7938dbd commit 8082a2c

3 files changed

Lines changed: 84 additions & 19 deletions

File tree

sqlmesh/cli/main.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"run",
3030
"environments",
3131
"invalidate",
32+
"table_name",
3233
)
3334
SKIP_CONTEXT_COMMANDS = ("init", "ui")
3435

@@ -988,17 +989,16 @@ def clean(obj: Context) -> None:
988989
@cli.command("table_name")
989990
@click.argument("model_name", required=True)
990991
@click.option(
991-
"--dev",
992-
is_flag=True,
993-
help="Print the name of the snapshot table used for previews in development environments.",
994-
default=False,
992+
"--environment",
993+
"--env",
994+
help="The environment to source the model version from.",
995995
)
996996
@click.pass_obj
997997
@error_handler
998998
@cli_analytics
999-
def table_name(obj: Context, model_name: str, dev: bool) -> None:
999+
def table_name(obj: Context, model_name: str, environment: t.Optional[str] = None) -> None:
10001000
"""Prints the name of the physical table for the given model."""
1001-
print(obj.table_name(model_name, dev))
1001+
print(obj.table_name(model_name, environment))
10021002

10031003

10041004
@cli.command("dlt_refresh")

sqlmesh/core/context.py

Lines changed: 27 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2135,25 +2135,41 @@ def _apply(self, plan: Plan, circuit_breaker: t.Optional[t.Callable[[], bool]])
21352135
)
21362136

21372137
@python_api_analytics
2138-
def table_name(self, model_name: str, dev: bool) -> str:
2139-
"""Returns the name of the pysical table for the given model name.
2138+
def table_name(self, model_name: str, environment: t.Optional[str] = None) -> str:
2139+
"""Returns the name of the pysical table for the given model name in the target environment.
21402140
21412141
Args:
21422142
model_name: The name of the model.
2143-
dev: Whether to use the deployability index for the table name.
2143+
environment: The environment to source the model version from.
21442144
21452145
Returns:
21462146
The name of the physical table.
21472147
"""
2148-
deployability_index = (
2149-
DeployabilityIndex.create(self.snapshots.values())
2150-
if dev
2151-
else DeployabilityIndex.all_deployable()
2148+
environment = environment or self.config.default_target_environment
2149+
fqn = self._node_or_snapshot_to_fqn(model_name)
2150+
target_env = self.state_reader.get_environment(environment)
2151+
if not target_env:
2152+
raise SQLMeshError(f"Environment '{environment}' was not found.")
2153+
2154+
snapshot_info = None
2155+
for s in target_env.snapshots:
2156+
if s.name == fqn:
2157+
snapshot_info = s
2158+
break
2159+
if not snapshot_info:
2160+
raise SQLMeshError(
2161+
f"Model '{model_name}' was not found in environment '{environment}'."
2162+
)
2163+
2164+
if target_env.name == c.PROD:
2165+
return snapshot_info.table_name()
2166+
2167+
snapshots = self.state_reader.get_snapshots(target_env.snapshots)
2168+
deployability_index = DeployabilityIndex.create(snapshots)
2169+
2170+
return snapshot_info.table_name(
2171+
is_deployable=deployability_index.is_representative(snapshot_info.snapshot_id)
21522172
)
2153-
snapshot = self.get_snapshot(model_name)
2154-
if not snapshot:
2155-
raise SQLMeshError(f"Model '{model_name}' was not found.")
2156-
return snapshot.table_name(is_deployable=deployability_index.is_deployable(snapshot))
21572173

21582174
def clear_caches(self) -> None:
21592175
for path in self.configs:

tests/core/test_integration.py

Lines changed: 51 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@
6161
SnapshotTableInfo,
6262
)
6363
from sqlmesh.utils.date import TimeLike, now, to_date, to_datetime, to_timestamp
64-
from sqlmesh.utils.errors import NoChangesPlanError
64+
from sqlmesh.utils.errors import NoChangesPlanError, SQLMeshError
6565
from sqlmesh.utils.pydantic import validate_string
6666
from tests.conftest import DuckDBMetadata, SushiDataValidator
6767
from tests.utils.test_helpers import use_terminal_console
@@ -3034,7 +3034,7 @@ def _dates_in_table(table_name: str) -> t.List[str]:
30343034
]
30353035

30363036
# mess with A independently of SQLMesh to prove a whole day gets restated for B instead of just 1hr
3037-
snapshot_table_name = ctx.table_name("test.a", False)
3037+
snapshot_table_name = ctx.table_name("test.a", "dev")
30383038
engine_adapter.execute(
30393039
f"delete from {snapshot_table_name} where cast(ts as date) == '2024-01-01'"
30403040
)
@@ -4094,6 +4094,55 @@ def test_evaluate_uncategorized_snapshot(init_and_plan_context: t.Callable):
40944094
assert set(df["one"].tolist()) == {1}
40954095

40964096

4097+
@time_machine.travel("2023-01-08 15:00:00 UTC")
4098+
def test_table_name(init_and_plan_context: t.Callable):
4099+
context, plan = init_and_plan_context("examples/sushi")
4100+
context.apply(plan)
4101+
4102+
snapshot = context.get_snapshot("sushi.waiter_revenue_by_day")
4103+
assert snapshot
4104+
assert (
4105+
context.table_name("sushi.waiter_revenue_by_day", "prod")
4106+
== f"memory.sqlmesh__sushi.sushi__waiter_revenue_by_day__{snapshot.version}"
4107+
)
4108+
4109+
with pytest.raises(SQLMeshError, match="Environment 'dev' was not found."):
4110+
context.table_name("sushi.waiter_revenue_by_day", "dev")
4111+
4112+
with pytest.raises(
4113+
SQLMeshError, match="Model 'sushi.missing' was not found in environment 'prod'."
4114+
):
4115+
context.table_name("sushi.missing", "prod")
4116+
4117+
# Add a new projection
4118+
model = context.get_model("sushi.waiter_revenue_by_day")
4119+
context.upsert_model(add_projection_to_model(t.cast(SqlModel, model)))
4120+
4121+
context.plan("dev_a", auto_apply=True, no_prompts=True, skip_tests=True)
4122+
4123+
new_snapshot = context.get_snapshot("sushi.waiter_revenue_by_day")
4124+
assert new_snapshot.version != snapshot.version
4125+
4126+
assert (
4127+
context.table_name("sushi.waiter_revenue_by_day", "dev_a")
4128+
== f"memory.sqlmesh__sushi.sushi__waiter_revenue_by_day__{new_snapshot.version}"
4129+
)
4130+
4131+
# Make a forward-only change
4132+
context.upsert_model(model, stamp="forward_only")
4133+
4134+
context.plan("dev_b", auto_apply=True, no_prompts=True, skip_tests=True, forward_only=True)
4135+
4136+
forward_only_snapshot = context.get_snapshot("sushi.waiter_revenue_by_day")
4137+
assert forward_only_snapshot.version == snapshot.version
4138+
assert forward_only_snapshot.dev_version != snapshot.version
4139+
4140+
assert (
4141+
context.table_name("sushi.waiter_revenue_by_day", "dev_b")
4142+
== f"memory.sqlmesh__sushi.sushi__waiter_revenue_by_day__{forward_only_snapshot.dev_version}__dev"
4143+
)
4144+
4145+
40974146
@time_machine.travel("2023-01-08 15:00:00 UTC")
40984147
def test_dbt_requirements(sushi_dbt_context: Context):
40994148
assert set(sushi_dbt_context.requirements) == {"dbt-core", "dbt-duckdb"}

0 commit comments

Comments
 (0)