Skip to content

Commit 5648c9e

Browse files
authored
Fix!: tables with dots and more conservative optimization (#1052)
1 parent b783aaa commit 5648c9e

7 files changed

Lines changed: 52 additions & 44 deletions

File tree

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@
4545
"requests",
4646
"rich",
4747
"ruamel.yaml",
48-
"sqlglot~=16.4.2",
48+
"sqlglot~=16.7.0",
4949
"fsspec",
5050
],
5151
extras_require={

sqlmesh/core/dialect.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -673,5 +673,5 @@ def find_tables(expression: exp.Expression, dialect: DialectType = None) -> t.Se
673673
normalize_model_name(table, dialect=dialect)
674674
for scope in traverse_scope(expression)
675675
for table in scope.tables
676-
if isinstance(table.this, exp.Identifier) and exp.table_name(table) not in scope.cte_sources
676+
if not isinstance(table.this, exp.Func) and exp.table_name(table) not in scope.cte_sources
677677
}

sqlmesh/core/loader.py

Lines changed: 1 addition & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
from pathlib import Path
1313

1414
from sqlglot.errors import SqlglotError
15-
from sqlglot.optimizer.qualify_columns import validate_qualify_columns
1615
from sqlglot.schema import MappingSchema
1716

1817
from sqlmesh.core import constants as c
@@ -24,7 +23,6 @@
2423
ModelCache,
2524
OptimizedQueryCache,
2625
SeedModel,
27-
SqlModel,
2826
create_external_model,
2927
load_model,
3028
)
@@ -54,23 +52,12 @@ def update_model_schemas(
5452
continue
5553

5654
model.update_schema(schema)
57-
58-
cache_hit = optimized_query_cache.with_optimized_query(model)
55+
optimized_query_cache.with_optimized_query(model)
5956

6057
columns_to_types = model.columns_to_types
6158
if columns_to_types is not None:
6259
schema.add_table(name, columns_to_types, dialect=model.dialect)
6360

64-
if isinstance(model, SqlModel) and model.mapping_schema and not cache_hit:
65-
query = model.render_query()
66-
if query is not None:
67-
try:
68-
validate_qualify_columns(query)
69-
except SqlglotError as e:
70-
raise ConfigError(
71-
f"Column references could not be resolved for model '{name}' at '{model._path}'. {e}"
72-
)
73-
7461

7562
@dataclass
7663
class LoadedProject:

sqlmesh/core/renderer.py

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,9 @@ def render(
148148
except MacroEvalError as ex:
149149
raise_config_error(f"Failed to resolve macro for expression. {ex}", self._path)
150150

151-
_normalize_and_quote(expression, self._dialect)
151+
if expression:
152+
with _normalize_and_quote(expression, self._dialect) as expression:
153+
pass
152154

153155
self._cache[cache_key] = expression
154156

@@ -383,12 +385,13 @@ def _optimize_query(self, query: exp.Subqueryable) -> exp.Subqueryable:
383385
if should_optimize:
384386
query = query.copy()
385387

386-
qualify(
387-
query,
388-
dialect=self._dialect,
389-
schema=schema,
390-
infer_schema=False,
391-
validate_qualify_columns=False,
388+
simplify(
389+
qualify(
390+
query,
391+
dialect=self._dialect,
392+
schema=schema,
393+
infer_schema=False,
394+
)
392395
)
393396
except SqlglotError as ex:
394397
failure = True
@@ -405,13 +408,13 @@ def _optimize_query(self, query: exp.Subqueryable) -> exp.Subqueryable:
405408
):
406409
select.replace(exp.alias_(select, select.output_name))
407410

408-
return annotate_types(simplify(query), schema=schema)
411+
return annotate_types(query, schema=schema)
409412

410413

411414
@contextmanager
412415
def _normalize_and_quote(query: E, dialect: str) -> t.Iterator[E]:
413-
normalize_identifiers(query, dialect=dialect)
414416
qualify_tables(query)
417+
normalize_identifiers(query, dialect=dialect)
415418
yield query
416419
quote_identifiers(query, dialect=dialect)
417420

tests/core/test_audit.py

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ def test_no_query():
144144

145145

146146
def test_macro(model: Model):
147-
expected_query = """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0" WHERE "a" IS NULL"""
147+
expected_query = """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0" WHERE "a" IS NULL"""
148148

149149
audit = Audit(
150150
name="test_audit",
@@ -167,7 +167,7 @@ def test_not_null_audit(model: Model):
167167
)
168168
assert (
169169
rendered_query_a.sql()
170-
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0" WHERE "a" IS NULL"""
170+
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0" WHERE "a" IS NULL"""
171171
)
172172

173173
rendered_query_a_and_b = builtin.not_null_audit.render_query(
@@ -176,23 +176,23 @@ def test_not_null_audit(model: Model):
176176
)
177177
assert (
178178
rendered_query_a_and_b.sql()
179-
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0" WHERE "a" IS NULL OR "b" IS NULL"""
179+
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0" WHERE "a" IS NULL OR "b" IS NULL"""
180180
)
181181

182182

183183
def test_unique_values_audit(model: Model):
184184
rendered_query_a = builtin.unique_values_audit.render_query(model, columns=[exp.to_column("a")])
185185
assert (
186186
rendered_query_a.sql()
187-
== """SELECT * FROM (SELECT ROW_NUMBER() OVER (PARTITION BY "a" ORDER BY 1) AS "a_rank" FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0") AS "_q_1" WHERE "a_rank" > 1"""
187+
== """SELECT * FROM (SELECT ROW_NUMBER() OVER (PARTITION BY "a" ORDER BY 1) AS "a_rank" FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0") AS "_q_1" WHERE "a_rank" > 1"""
188188
)
189189

190190
rendered_query_a_and_b = builtin.unique_values_audit.render_query(
191191
model, columns=[exp.to_column("a"), exp.to_column("b")]
192192
)
193193
assert (
194194
rendered_query_a_and_b.sql()
195-
== """SELECT * FROM (SELECT ROW_NUMBER() OVER (PARTITION BY "a" ORDER BY 1) AS "a_rank", ROW_NUMBER() OVER (PARTITION BY "b" ORDER BY 1) AS "b_rank" FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0") AS "_q_1" WHERE "a_rank" > 1 OR "b_rank" > 1"""
195+
== """SELECT * FROM (SELECT ROW_NUMBER() OVER (PARTITION BY "a" ORDER BY 1) AS "a_rank", ROW_NUMBER() OVER (PARTITION BY "b" ORDER BY 1) AS "b_rank" FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0") AS "_q_1" WHERE "a_rank" > 1 OR "b_rank" > 1"""
196196
)
197197

198198

@@ -204,7 +204,7 @@ def test_accepted_values_audit(model: Model):
204204
)
205205
assert (
206206
rendered_query.sql()
207-
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0" WHERE NOT "a" IN ('value_a', 'value_b')"""
207+
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0" WHERE NOT "a" IN ('value_a', 'value_b')"""
208208
)
209209

210210

@@ -215,7 +215,7 @@ def test_number_of_rows_audit(model: Model):
215215
)
216216
assert (
217217
rendered_query.sql()
218-
== """SELECT 1 AS "1" FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0" HAVING COUNT(*) <= 0 LIMIT 1"""
218+
== """SELECT 1 AS "1" FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0" HAVING COUNT(*) <= 0 LIMIT 0 + 1"""
219219
)
220220

221221

@@ -226,7 +226,7 @@ def test_forall_audit(model: Model):
226226
)
227227
assert (
228228
rendered_query_a.sql()
229-
== '''SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0" WHERE NOT "a" >= "b"'''
229+
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0" WHERE NOT ("a" >= "b")"""
230230
)
231231

232232
rendered_query_a = builtin.forall_audit.render_query(
@@ -235,5 +235,5 @@ def test_forall_audit(model: Model):
235235
)
236236
assert (
237237
rendered_query_a.sql()
238-
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" <= '1970-01-01' AND "ds" >= '1970-01-01') AS "_q_0" WHERE NOT "a" >= "b" OR NOT "c" + "d" - "e" < 1.0"""
238+
== """SELECT * FROM (SELECT * FROM "db"."test_model" AS "test_model" WHERE "ds" BETWEEN '1970-01-01' AND '1970-01-01') AS "_q_0" WHERE NOT ("a" >= "b") OR NOT ("c" + "d" - "e" < 1.0)"""
239239
)

tests/core/test_model.py

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import logging
12
from datetime import date
23
from pathlib import Path
34
from unittest.mock import patch
@@ -195,6 +196,27 @@ def test_model_validation_union_query():
195196
model.validate_definition()
196197

197198

199+
def test_model_qualification():
200+
logger = logging.getLogger("sqlmesh.core.renderer")
201+
with patch.object(logger, "error") as mock_logger:
202+
expressions = d.parse(
203+
"""
204+
MODEL (
205+
name db.table,
206+
kind FULL,
207+
);
208+
209+
SELECT a
210+
"""
211+
)
212+
213+
model = load_model(expressions)
214+
model.render_query(optimize=True)
215+
assert (
216+
mock_logger.call_args[0][0] == "%s for '%s', the column may not exist or is ambiguous"
217+
)
218+
219+
198220
@pytest.mark.parametrize(
199221
"partition_by_input, partition_by_output, expected_exception",
200222
[
@@ -877,10 +899,8 @@ def test_render_query(assert_exp_eq):
877899
y AS y
878900
FROM x AS x
879901
WHERE
880-
y <= '2020-10-28'
881-
AND y <= DATE_STR_TO_DATE('2020-10-28')
882-
AND y >= '2020-10-28'
883-
AND y >= DATE_STR_TO_DATE('2020-10-28')
902+
y BETWEEN DATE_STR_TO_DATE('2020-10-28') AND DATE_STR_TO_DATE('2020-10-28')
903+
AND y BETWEEN '2020-10-28' AND '2020-10-28'
884904
""",
885905
)
886906
assert_exp_eq(
@@ -895,10 +915,8 @@ def test_render_query(assert_exp_eq):
895915
y AS y
896916
FROM x AS x
897917
WHERE
898-
y <= '2020-10-28'
899-
AND y <= DATE_STR_TO_DATE('2020-10-28')
900-
AND y >= '2020-10-28'
901-
AND y >= DATE_STR_TO_DATE('2020-10-28')
918+
y BETWEEN DATE_STR_TO_DATE('2020-10-28') AND DATE_STR_TO_DATE('2020-10-28')
919+
AND y BETWEEN '2020-10-28' AND '2020-10-28'
902920
) AS _subquery
903921
WHERE
904922
y BETWEEN TIME_STR_TO_TIME('2020-10-28T00:00:00+00:00') AND TIME_STR_TO_TIME('2020-10-28T23:59:59.999000+00:00')
@@ -1190,7 +1208,7 @@ def test_parse(assert_exp_eq):
11901208
ds AS ds
11911209
FROM x AS x
11921210
WHERE
1193-
ds <= '1970-01-01' AND ds >= '1970-01-01'
1211+
ds BETWEEN '1970-01-01' AND '1970-01-01'
11941212
""",
11951213
)
11961214

tests/dbt/test_transformation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,7 +228,7 @@ def test_config_jinja(sushi_test_project: Project):
228228
context = sushi_test_project.context
229229
model = t.cast(SqlModel, model_config.to_sqlmesh(context))
230230
assert hook in model.pre_statements[0].sql()
231-
assert model.render_pre_statements()[0].sql() == "bar"
231+
assert model.render_pre_statements()[0].sql() == '"bar"'
232232

233233

234234
def test_this(assert_exp_eq, sushi_test_project: Project):

0 commit comments

Comments
 (0)