Skip to content

Commit 0531201

Browse files
Fix: Gracefully handle execution errors in before after all (#4333)
1 parent 51fe510 commit 0531201

3 files changed

Lines changed: 95 additions & 20 deletions

File tree

sqlmesh/core/environment.py

Lines changed: 29 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
from sqlmesh.core.snapshot import SnapshotId, SnapshotTableInfo, Snapshot
1515
from sqlmesh.utils import word_characters_only
1616
from sqlmesh.utils.date import TimeLike, now_timestamp
17+
from sqlmesh.utils.errors import SQLMeshError
1718
from sqlmesh.utils.jinja import JinjaMacroRegistry
1819
from sqlmesh.utils.metaprogramming import Executable
1920
from sqlmesh.utils.pydantic import PydanticModel, field_validator, ValidationInfo
@@ -261,25 +262,35 @@ def execute_environment_statements(
261262
end: t.Optional[TimeLike] = None,
262263
execution_time: t.Optional[TimeLike] = None,
263264
) -> None:
264-
rendered_expressions = [
265-
expr
266-
for statements in environment_statements
267-
for expr in render_statements(
268-
statements=getattr(statements, runtime_stage.value),
269-
dialect=adapter.dialect,
270-
default_catalog=default_catalog,
271-
python_env=statements.python_env,
272-
jinja_macros=statements.jinja_macros,
273-
snapshots=snapshots,
274-
start=start,
275-
end=end,
276-
execution_time=execution_time,
277-
environment_naming_info=environment_naming_info,
278-
runtime_stage=runtime_stage,
279-
engine_adapter=adapter,
265+
try:
266+
rendered_expressions = [
267+
expr
268+
for statements in environment_statements
269+
for expr in render_statements(
270+
statements=getattr(statements, runtime_stage.value),
271+
dialect=adapter.dialect,
272+
default_catalog=default_catalog,
273+
python_env=statements.python_env,
274+
jinja_macros=statements.jinja_macros,
275+
snapshots=snapshots,
276+
start=start,
277+
end=end,
278+
execution_time=execution_time,
279+
environment_naming_info=environment_naming_info,
280+
runtime_stage=runtime_stage,
281+
engine_adapter=adapter,
282+
)
283+
]
284+
except Exception as e:
285+
raise SQLMeshError(
286+
f"An error occurred during rendering of the '{runtime_stage.value}' statements:\n\n{e}"
280287
)
281-
]
282288
if rendered_expressions:
283289
with adapter.transaction():
284290
for expr in rendered_expressions:
285-
adapter.execute(expr)
291+
try:
292+
adapter.execute(expr)
293+
except Exception as e:
294+
raise SQLMeshError(
295+
f"An error occurred during execution of the following '{runtime_stage.value}' statement:\n\n{expr}\n\n{e}"
296+
)

tests/core/test_integration.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from pytest_mock.plugin import MockerFixture
1919
from sqlglot import exp
2020
from sqlglot.expressions import DataType
21+
import re
2122

2223
from sqlmesh import CustomMaterialization
2324
from sqlmesh.cli.example_project import init_example_project
@@ -5501,6 +5502,69 @@ def test_plan_production_environment_statements(tmp_path: Path):
55015502
ctx.fetchdf("select * from not_create")
55025503

55035504

5505+
def test_environment_statements_error_handling(tmp_path: Path):
5506+
model_a = """
5507+
MODEL (
5508+
name test_schema.a,
5509+
kind FULL,
5510+
);
5511+
5512+
SELECT 1 AS account_id
5513+
"""
5514+
5515+
models_dir = tmp_path / "models"
5516+
models_dir.mkdir()
5517+
5518+
for path, defn in {"a.sql": model_a}.items():
5519+
with open(models_dir / path, "w") as f:
5520+
f.write(defn)
5521+
5522+
before_all = [
5523+
"CREATE TABLE identical_table (physical_schema_name VARCHAR)",
5524+
"CREATE TABLE identical_table (physical_schema_name VARCHAR)",
5525+
]
5526+
5527+
config = Config(
5528+
model_defaults=ModelDefaultsConfig(dialect="duckdb"),
5529+
before_all=before_all,
5530+
)
5531+
ctx = Context(paths=[tmp_path], config=config)
5532+
5533+
expected_error_message = re.escape(
5534+
"""An error occurred during execution of the following 'before_all' statement:
5535+
5536+
CREATE TABLE identical_table (physical_schema_name TEXT)
5537+
5538+
Catalog Error: Table with name "identical_table" already exists!"""
5539+
)
5540+
5541+
with pytest.raises(SQLMeshError, match=expected_error_message):
5542+
ctx.plan(auto_apply=True, no_prompts=True)
5543+
5544+
after_all = [
5545+
"@bad_macro()",
5546+
]
5547+
5548+
config = Config(
5549+
model_defaults=ModelDefaultsConfig(dialect="duckdb"),
5550+
after_all=after_all,
5551+
)
5552+
ctx = Context(paths=[tmp_path], config=config)
5553+
5554+
expected_error_message = re.escape(
5555+
"""An error occurred during rendering of the 'after_all' statements:
5556+
5557+
Failed to resolve macros for
5558+
5559+
@bad_macro()
5560+
5561+
Macro 'bad_macro' does not exist."""
5562+
)
5563+
5564+
with pytest.raises(SQLMeshError, match=expected_error_message):
5565+
ctx.plan(auto_apply=True, no_prompts=True)
5566+
5567+
55045568
@time_machine.travel("2025-03-08 00:00:00 UTC")
55055569
def test_tz(init_and_plan_context):
55065570
context, _ = init_and_plan_context("examples/sushi")

tests/integrations/jupyter/test_magics.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -325,8 +325,8 @@ def test_run_dag(
325325
)
326326
assert not output.stderr
327327

328-
# At least 6 outputs expected as the number of models in the particular batch might vary
329-
assert len(output.outputs) >= 6
328+
# At least 4 outputs expected as the number of models in the particular batch might vary
329+
assert len(output.outputs) >= 4
330330

331331
html_text_actual = convert_all_html_output_to_text(output)
332332

0 commit comments

Comments
 (0)