From 34a0b96520a1d9365e974ea4aee48daf80306b19 Mon Sep 17 00:00:00 2001 From: Peran Truscott Date: Mon, 9 Nov 2026 13:58:02 +0000 Subject: [PATCH] fix: stop DIALECT_PATTERN backtracking over runs of block comments A MODEL or AUDIT block with no dialect key made the dialect search fail, and the lazy block-comment match could stretch across several comments, so the failed search tried every grouping of a run of comments. Loading a model with a few dozen block comments hung. A block comment now matches balanced, so each '*/' closes exactly one '/*' and a run of comments has a single match. Nesting is matched up to three levels deep, since SQLGlot reads block comments as nested. Signed-off-by: Peran Truscott --- sqlmesh/core/dialect.py | 9 ++++++++- tests/core/test_model.py | 20 ++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/sqlmesh/core/dialect.py b/sqlmesh/core/dialect.py index bcfb300ba1..6dafe52c43 100644 --- a/sqlmesh/core/dialect.py +++ b/sqlmesh/core/dialect.py @@ -1124,7 +1124,14 @@ def text_diff( return "\n".join(unified_diff(a_sql, b_sql)) -WS_OR_COMMENT = r"(?:\s|--[^\n]*\n|/\*.*?\*/)" +# Each '*/' closes exactly one '/*', so a run of comments matches in exactly one way. A lazy +# '/\*.*?\*/' under DOTALL can stretch across several comments, and a failed search then +# backtracks through every grouping of them. SQLGlot reads block comments as nested, so this +# matches nesting up to three levels deep; a regex cannot match arbitrary depth. +BLOCK_COMMENT = r"/\*(?:[^*]|\*(?!/))*\*/" +for _ in range(2): + BLOCK_COMMENT = rf"/\*(?:[^*/]|\*(?!/)|/(?!\*)|{BLOCK_COMMENT})*\*/" +WS_OR_COMMENT = rf"(?:\s|--[^\n]*\n|{BLOCK_COMMENT})" HEADER = r"\b(?:model|audit)\b(?=\s*\()" KEY_BOUNDARY = r"(?:\(|,)" # key is preceded by either '(' or ',' DIALECT_VALUE = r"['\"]?(?P[a-z][a-z0-9]*)['\"]?" diff --git a/tests/core/test_model.py b/tests/core/test_model.py index c5d1a71bee..34bb5d1d1d 100644 --- a/tests/core/test_model.py +++ b/tests/core/test_model.py @@ -3151,6 +3151,26 @@ def assert_match(test_sql: str, expected_value: t.Optional[str] = "duckdb"): """ ) + # a run of block comments before the dialect key + comments = "\n".join(f"/* note {i} */" for i in range(40)) + assert_match(make_test_sql(f"tag my_tag, {comments}\ndialect duckdb")) + + # a run of block comments and no dialect key: the search fails without backtracking + # through every grouping of the comments + test_sql = make_test_sql(f"tag my_tag, {comments}\ncron '@daily'") + matches = list(d.DIALECT_PATTERN.finditer(test_sql)) + assert not matches + + # nested block comments before the dialect key + assert_match( + make_test_sql( + """ + tag my_tag, /* outer /* inner /* innermost */ */ still outer */ + dialect duckdb + """ + ) + ) + # full parse sql = """ MODEL (