From 2c673292f59dfad27651636b61cbed03c80b5d8e Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Sat, 25 Jul 2026 13:52:22 -0700 Subject: [PATCH 1/2] fix(dialect): make extend_sqlglot idempotent _override saved whatever was currently installed under _, so a second extend_sqlglot() call saved the override itself as the original. The wrapper then called itself and every subsequent type/CAST parse raised RecursionError, with sqlglot's original method lost. _override now returns early when the override is already installed. Adds tests/core/test_dialect.py::test_extend_sqlglot_is_idempotent, which fails with RecursionError without the fix. Signed-off-by: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> --- sqlmesh/core/dialect.py | 4 ++++ tests/core/test_dialect.py | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/sqlmesh/core/dialect.py b/sqlmesh/core/dialect.py index 67918b6d14..de1f03fb53 100644 --- a/sqlmesh/core/dialect.py +++ b/sqlmesh/core/dialect.py @@ -782,6 +782,10 @@ def _parse_interval_span(self: Parser, this: exp.Expr) -> exp.Interval: def _override(klass: t.Type[Tokenizer | Parser], func: t.Callable) -> None: name = func.__name__ + if getattr(klass, name, None) is func: + # Already overridden. Re-applying would save the override itself as the + # "original", making the wrapper call itself and recurse infinitely. + return setattr(klass, f"_{name}", getattr(klass, name)) setattr(klass, name, func) diff --git a/tests/core/test_dialect.py b/tests/core/test_dialect.py index e2f1daba3d..0ea3fbcf4b 100644 --- a/tests/core/test_dialect.py +++ b/tests/core/test_dialect.py @@ -1185,3 +1185,11 @@ def test_pipe_syntax(): ast.sql("bigquery") == "SELECT * FROM (WITH __tmp1 AS (SELECT id FROM t2) SELECT * FROM __tmp1)" ) + + +def test_extend_sqlglot_is_idempotent(): + # extend_sqlglot() runs at import time; calling it again must not re-wrap the + # already-installed overrides, otherwise they call themselves (RecursionError). + d.extend_sqlglot() + + assert parse_one("SELECT CAST(1 AS INT)").sql() == "SELECT CAST(1 AS INT)" From 5f2c7b9c0835f04a98012fc88dbb86956beeb2b3 Mon Sep 17 00:00:00 2001 From: Sanjay Santhanam <51058514+Sanjays2402@users.noreply.github.com> Date: Thu, 30 Jul 2026 09:01:59 -0700 Subject: [PATCH 2/2] fix(dialect): guard UNWRAPPED_INTERVAL_VALUES from repeated appends --- sqlmesh/core/dialect.py | 11 ++++++----- tests/core/test_dialect.py | 6 ++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/sqlmesh/core/dialect.py b/sqlmesh/core/dialect.py index de1f03fb53..f96887a61c 100644 --- a/sqlmesh/core/dialect.py +++ b/sqlmesh/core/dialect.py @@ -1174,11 +1174,12 @@ def extend_sqlglot() -> None: MacroDef, ) - generator.UNWRAPPED_INTERVAL_VALUES = ( - *generator.UNWRAPPED_INTERVAL_VALUES, - MacroStrReplace, - MacroVar, - ) + if MacroVar not in generator.UNWRAPPED_INTERVAL_VALUES: + generator.UNWRAPPED_INTERVAL_VALUES = ( + *generator.UNWRAPPED_INTERVAL_VALUES, + MacroStrReplace, + MacroVar, + ) _override(Parser, _parse_select) _override(Parser, _parse_statement) diff --git a/tests/core/test_dialect.py b/tests/core/test_dialect.py index 0ea3fbcf4b..6cbd9107a0 100644 --- a/tests/core/test_dialect.py +++ b/tests/core/test_dialect.py @@ -1190,6 +1190,12 @@ def test_pipe_syntax(): def test_extend_sqlglot_is_idempotent(): # extend_sqlglot() runs at import time; calling it again must not re-wrap the # already-installed overrides, otherwise they call themselves (RecursionError). + from sqlglot.generator import Generator + + before = Generator.UNWRAPPED_INTERVAL_VALUES + d.extend_sqlglot() assert parse_one("SELECT CAST(1 AS INT)").sql() == "SELECT CAST(1 AS INT)" + # The class-level registries must not grow on repeated calls. + assert Generator.UNWRAPPED_INTERVAL_VALUES == before