Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,7 @@ Options:
application (prod environment only).
--enable-preview Enable preview for forward-only models when
targeting a development environment.
--diff-rendered Output text differences for the rendered versions of models and standalone audits
-v, --verbose Verbose output.
--help Show this message and exit.
```
Expand Down
4 changes: 3 additions & 1 deletion docs/reference/notebook.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ options:
[--no-auto-categorization] [--include-unmodified]
[--select-model [SELECT_MODEL ...]]
[--backfill-model [BACKFILL_MODEL ...]] [--no-diff] [--run]
[environment]
[environment] [--diff-rendered]

Goes through a set of prompts to both establish a plan and apply it

Expand Down Expand Up @@ -153,6 +153,8 @@ options:
--no-diff Hide text differences for changed models.
--run Run latest intervals as part of the plan application
(prod environment only).
--diff-rendered Output text differences for the rendered versions of models and standalone audits

```

#### run_dag
Expand Down
5 changes: 5 additions & 0 deletions sqlmesh/cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,11 @@ def diff(ctx: click.Context, environment: t.Optional[str] = None) -> None:
help="Enable preview for forward-only models when targeting a development environment.",
default=None,
)
@click.option(
"--diff-rendered",
is_flag=True,
help="Output text differences for the rendered versions of the models and standalone audits",
)
@opt.verbose
@click.pass_context
@error_handler
Expand Down
21 changes: 17 additions & 4 deletions sqlmesh/core/audit/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,11 +283,12 @@ def metadata_hash(self) -> str:
self._metadata_hash = hash_data(data)
return self._metadata_hash

def text_diff(self, other: Node) -> str:
def text_diff(self, other: Node, rendered: bool = False) -> str:
"""Produce a text diff against another node.

Args:
other: The node to diff against.
rendered: Whether the diff should be between raw vs rendered nodes

Returns:
A unified text diff showing additions and deletions.
Expand All @@ -298,11 +299,17 @@ def text_diff(self, other: Node) -> str:
)

return d.text_diff(
self.render_definition(), other.render_definition(), self.dialect, other.dialect
self.render_definition(render_query=rendered),
other.render_definition(render_query=rendered),
self.dialect,
other.dialect,
).strip()

def render_definition(
self, include_python: bool = True, include_defaults: bool = False
self,
include_python: bool = True,
include_defaults: bool = False,
render_query: bool = False,
) -> t.List[exp.Expression]:
"""Returns the original list of sql expressions comprising the model definition.

Expand Down Expand Up @@ -348,7 +355,13 @@ def render_definition(

jinja_expressions = self.jinja_macros.to_expressions()

return [audit, *python_expressions, *jinja_expressions, *self.expressions, self.query]
return [
audit,
*python_expressions,
*jinja_expressions,
*self.expressions,
self.render_audit_query() if render_query else self.query,
]

@property
def is_audit(self) -> bool:
Expand Down
8 changes: 8 additions & 0 deletions sqlmesh/core/context.py
Original file line number Diff line number Diff line change
Expand Up @@ -1068,6 +1068,7 @@ def plan(
enable_preview: t.Optional[bool] = None,
no_diff: t.Optional[bool] = None,
run: bool = False,
diff_rendered: bool = False,
) -> Plan:
"""Interactively creates a plan.

Expand Down Expand Up @@ -1111,6 +1112,7 @@ def plan(
enable_preview: Indicates whether to enable preview for forward-only models in development environments.
no_diff: Hide text differences for changed models.
run: Whether to run latest intervals as part of the plan application.
diff_rendered: Whether the diff should compare raw vs rendered models

Returns:
The populated Plan object.
Expand All @@ -1136,6 +1138,7 @@ def plan(
categorizer_config=categorizer_config,
enable_preview=enable_preview,
run=run,
diff_rendered=diff_rendered,
)

self.console.plan(
Expand Down Expand Up @@ -1172,6 +1175,7 @@ def plan_builder(
categorizer_config: t.Optional[CategorizerConfig] = None,
enable_preview: t.Optional[bool] = None,
run: bool = False,
diff_rendered: bool = False,
) -> PlanBuilder:
"""Creates a plan builder.

Expand Down Expand Up @@ -1207,6 +1211,7 @@ def plan_builder(
backfill_models: A list of model selection strings to filter the models for which the data should be backfilled.
enable_preview: Indicates whether to enable preview for forward-only models in development environments.
run: Whether to run latest intervals as part of the plan application.
diff_rendered: Whether the diff should compare raw vs rendered models

Returns:
The plan builder.
Expand Down Expand Up @@ -1270,6 +1275,7 @@ def plan_builder(
force_no_diff=restate_models is not None
or (backfill_models is not None and not backfill_models),
ensure_finalized_snapshots=self.config.plan.use_finalized_state,
diff_rendered=diff_rendered,
)
modified_model_names = {
*context_diff.modified_snapshots,
Expand Down Expand Up @@ -2142,6 +2148,7 @@ def _context_diff(
create_from: t.Optional[str] = None,
force_no_diff: bool = False,
ensure_finalized_snapshots: bool = False,
diff_rendered: bool = False,
) -> ContextDiff:
environment = Environment.sanitize_name(environment)
if force_no_diff:
Expand All @@ -2155,6 +2162,7 @@ def _context_diff(
provided_requirements=self._requirements,
excluded_requirements=self._excluded_requirements,
ensure_finalized_snapshots=ensure_finalized_snapshots,
diff_rendered=diff_rendered,
)

def _run_janitor(self, ignore_ttl: bool = False) -> None:
Expand Down
6 changes: 5 additions & 1 deletion sqlmesh/core/context_diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@ class ContextDiff(PydanticModel):
"""Previous requirements."""
requirements: t.Dict[str, str] = {}
"""Python dependencies."""
diff_rendered: bool = False
"""Whether the diff should compare raw vs rendered models"""

@classmethod
def create(
Expand All @@ -87,6 +89,7 @@ def create(
ensure_finalized_snapshots: bool = False,
provided_requirements: t.Optional[t.Dict[str, str]] = None,
excluded_requirements: t.Optional[t.Set[str]] = None,
diff_rendered: bool = False,
) -> ContextDiff:
"""Create a ContextDiff object.

Expand Down Expand Up @@ -212,6 +215,7 @@ def create(
previous_finalized_snapshots=env.previous_finalized_snapshots if env else None,
previous_requirements=env.requirements if env else {},
requirements=requirements,
diff_rendered=diff_rendered,
)

@classmethod
Expand Down Expand Up @@ -390,7 +394,7 @@ def text_diff(self, name: str) -> str:

new, old = self.modified_snapshots[name]
try:
return old.node.text_diff(new.node)
return old.node.text_diff(new.node, rendered=self.diff_rendered)
except SQLMeshError as e:
logger.warning("Failed to diff model '%s': %s", name, str(e))
return ""
Expand Down
43 changes: 33 additions & 10 deletions sqlmesh/core/model/definition.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,7 +187,10 @@ def render(
)

def render_definition(
self, include_python: bool = True, include_defaults: bool = False
self,
include_python: bool = True,
include_defaults: bool = False,
render_query: bool = False,
) -> t.List[exp.Expression]:
"""Returns the original list of sql expressions comprising the model definition.

Expand Down Expand Up @@ -674,11 +677,12 @@ def ctas_query(self, **render_kwarg: t.Any) -> exp.Query:
)
return query

def text_diff(self, other: Node) -> str:
def text_diff(self, other: Node, rendered: bool = False) -> str:
"""Produce a text diff against another node.

Args:
other: The node to diff against.
rendered: Whether the diff should compare raw vs rendered models

Returns:
A unified text diff showing additions and deletions.
Expand All @@ -689,7 +693,10 @@ def text_diff(self, other: Node) -> str:
)

return d.text_diff(
self.render_definition(), other.render_definition(), self.dialect, other.dialect
self.render_definition(render_query=rendered),
other.render_definition(render_query=rendered),
self.dialect,
other.dialect,
).strip()

def set_time_format(self, default_time_format: str = c.DEFAULT_TIME_COLUMN_FORMAT) -> None:
Expand Down Expand Up @@ -1217,15 +1224,26 @@ def render_query(
return query

def render_definition(
self, include_python: bool = True, include_defaults: bool = False
self,
include_python: bool = True,
include_defaults: bool = False,
render_query: bool = False,
) -> t.List[exp.Expression]:
result = super().render_definition(
include_python=include_python, include_defaults=include_defaults
)
result.extend(self.pre_statements)
result.append(self.query)
result.extend(self.post_statements)
result.extend(self.on_virtual_update)

if render_query:
result.extend(self.render_pre_statements())
result.append(self.render_query() or self.query)
Comment thread
VaggelisD marked this conversation as resolved.
Resolved
result.extend(self.render_post_statements())
result.extend(self.render_on_virtual_update())
else:
result.extend(self.pre_statements)
result.append(self.query)
result.extend(self.post_statements)
result.extend(self.on_virtual_update)

return result

@property
Expand Down Expand Up @@ -1652,11 +1670,16 @@ def render(
raise SQLMeshError(f"Error executing Python model '{self.name}'")

def render_definition(
self, include_python: bool = True, include_defaults: bool = False
self,
include_python: bool = True,
include_defaults: bool = False,
render_query: bool = False,
) -> t.List[exp.Expression]:
# Ignore the provided value for the include_python flag, since the Pyhon model's
# definition without Python code is meaningless.
return super().render_definition(include_python=True, include_defaults=include_defaults)
return super().render_definition(
include_python=True, include_defaults=include_defaults, render_query=render_query
)

@property
def is_python(self) -> bool:
Expand Down
2 changes: 1 addition & 1 deletion sqlmesh/core/node.py
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,7 @@ def cron_floor(self, value: TimeLike, estimate: bool = False) -> datetime:
"""
return self.croniter(self.cron_next(value, estimate=estimate)).get_prev(estimate=True)

def text_diff(self, other: Node) -> str:
def text_diff(self, other: Node, rendered: bool = False) -> str:
"""Produce a text diff against another node.

Args:
Expand Down
6 changes: 6 additions & 0 deletions sqlmesh/magics.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,6 +418,11 @@ def test(self, context: Context, line: str, test_def_raw: t.Optional[str] = None
help="Enable preview for forward-only models when targeting a development environment.",
default=None,
)
@argument(
"--diff-rendered",
action="store_true",
help="Output text differences for the rendered versions of the models and standalone audits",
)
@line_magic
@pass_sqlmesh_context
def plan(self, context: Context, line: str) -> None:
Expand Down Expand Up @@ -446,6 +451,7 @@ def plan(self, context: Context, line: str) -> None:
no_diff=args.no_diff,
run=args.run,
enable_preview=args.enable_preview,
diff_rendered=args.diff_rendered,
)

@magic_arguments()
Expand Down
18 changes: 18 additions & 0 deletions tests/core/test_audit.py
Original file line number Diff line number Diff line change
Expand Up @@ -898,3 +898,21 @@ def test_audit_query_normalization():
rendered_audit_query.sql("snowflake")
== """SELECT * FROM (SELECT * FROM "DB"."TEST_MODEL" AS "TEST_MODEL") AS "_Q_0" WHERE "A" IS NULL AND TRUE"""
)


def test_rendered_diff():
audit1 = StandaloneAudit(
name="test_audit", query=parse_one("SELECT * FROM 'test' WHERE @AND(TRUE, NULL) > 2")
)

audit2 = StandaloneAudit(
name="test_audit", query=parse_one("SELECT * FROM 'test' WHERE @OR(FALSE, NULL) > 2")
)

assert """@@ -6,4 +6,4 @@

*
FROM "test" AS "test"
WHERE
- TRUE > 2
+ FALSE > 2""" in audit1.text_diff(audit2, rendered=True)
Loading