Skip to content

Commit 10dec03

Browse files
committed
perf(compile): bind each distinct audit and SQL test body once
Built-in not_null and unique audits on a column their closed target shape contains cannot produce a finding, so they skip native binding; any other case is still bound and reported unchanged. Identical normalized SQL with the same relations is bound once and its result shared, and SQL test bodies are prepared once per distinct body.
1 parent 9c954c4 commit 10dec03

2 files changed

Lines changed: 102 additions & 27 deletions

File tree

‎src/sqlbuild/compiler/compile/_helpers/diagnostics/resource_sql.py‎

Lines changed: 75 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
from sqlbuild.compiler.discovery.constants import SQL_ANALYSIS_CONFIG_KEY
3838
from sqlbuild.compiler.sql_analysis.constants import BINDING_UNKNOWN_TABLE_INTERNAL_CODE
3939
from sqlbuild.compiler.sql_analysis.main._normalize_analysis import normalize_analysis_sql
40+
from sqlbuild.compiler.sql_analysis.main._resolve_binding_column import resolve_binding_column
4041
from sqlbuild.compiler.sql_analysis.main._schema_validation import get_schema_validations
4142
from sqlbuild.compiler.sql_analysis.models import (
4243
SqlBindingDiagnostic,
@@ -59,6 +60,8 @@
5960
r"\b__empty_fixture\s*\(\s*\)", flags=re.IGNORECASE
6061
)
6162
_OPEN_EMPTY_FIXTURE_RELATION: str = "__sqlbuild_empty_fixture"
63+
_BUILT_IN_AUDIT_ROOT: str = "<built-in>"
64+
_COLUMN_ONLY_BUILT_IN_AUDITS: frozenset[str] = frozenset({"not_null", "unique"})
6265
_UNKNOWN_TABLE_MESSAGE_PREFIX: str = "Unknown table "
6366
_SYNTAX_ERROR_CODE: str = "P001"
6467
_RESOURCE_KIND_LABELS: dict[CompiledResourceType, str] = {
@@ -102,7 +105,7 @@ def get_resource_sql_diagnostics(
102105
resource
103106
for resource in dict.fromkeys(
104107
(
105-
*_audit_resources(project=project),
108+
*_audit_resources(project=project, shapes=shapes, profile=profile),
106109
*_sql_test_resources(project=project),
107110
*_hook_resources(project=project),
108111
)
@@ -112,19 +115,22 @@ def get_resource_sql_diagnostics(
112115
if not resources:
113116
return ()
114117
dialect: str | None = profile.sql_analysis_dialect
115-
cleaned: tuple[str, ...] = tuple(
116-
normalize_analysis_sql(
117-
sql=cursor_intrinsics_analysis_sql(sql=resource.sql, cursor_type=None),
118-
dialect=dialect,
119-
)
120-
for resource in resources
121-
)
118+
normalized: dict[str, str] = {}
119+
for resource in resources:
120+
if resource.sql not in normalized:
121+
normalized[resource.sql] = normalize_analysis_sql(
122+
sql=cursor_intrinsics_analysis_sql(sql=resource.sql, cursor_type=None),
123+
dialect=dialect,
124+
)
125+
cleaned: tuple[str, ...] = tuple(normalized[resource.sql] for resource in resources)
122126
known_functions: tuple[str, ...] = known_function_names(project.functions)
123127
known_types: tuple[str, ...] = known_declared_types(
124128
functions=project.functions, column_types=shapes
125129
)
126-
results: tuple[SqlBindingResult, ...] = get_schema_validations(
127-
requests=tuple(
130+
requests: dict[tuple[str, frozenset[str]], SqlSchemaValidationRequest] = {}
131+
for resource, cleaned_sql in zip(resources, cleaned, strict=True):
132+
_ = requests.setdefault(
133+
(cleaned_sql, resource.relations),
128134
SqlSchemaValidationRequest(
129135
sql=cleaned_sql,
130136
dialect=dialect,
@@ -133,9 +139,14 @@ def get_resource_sql_diagnostics(
133139
known_types=known_types,
134140
quoted_identifiers_ignore_case=profile.quoted_identifiers_ignore_case,
135141
catalog=project.binding_catalog,
136-
)
137-
for resource, cleaned_sql in zip(resources, cleaned, strict=True)
142+
),
138143
)
144+
unique_results: dict[tuple[str, frozenset[str]], SqlBindingResult] = dict(
145+
zip(requests, get_schema_validations(requests=tuple(requests.values())), strict=True)
146+
)
147+
results: tuple[SqlBindingResult, ...] = tuple(
148+
unique_results[(cleaned_sql, resource.relations)]
149+
for resource, cleaned_sql in zip(resources, cleaned, strict=True)
139150
)
140151
diagnostics: dict[tuple[object, ...], CompilerDiagnostic] = {}
141152
for resource, cleaned_sql, result in zip(resources, cleaned, results, strict=True):
@@ -246,11 +257,18 @@ def _reported_diagnostic(
246257
)
247258

248259

249-
def _audit_resources(*, project: CompiledProject) -> tuple[_ResourceSql, ...]:
260+
def _audit_resources(
261+
*,
262+
project: CompiledProject,
263+
shapes: dict[str, dict[str, str]],
264+
profile: ExpressionInferenceProfile,
265+
) -> tuple[_ResourceSql, ...]:
250266
analysis_disabled: frozenset[str] = _analysis_disabled_models(project)
251267
resources: list[_ResourceSql] = []
252268
for audit in project.audits:
253-
if audit.attached_target_name in analysis_disabled:
269+
if audit.attached_target_name in analysis_disabled or _binds_cleanly(
270+
audit=audit, shapes=shapes, profile=profile
271+
):
254272
continue
255273
relations: frozenset[str] = _relations(audit.references)
256274
authored_bodies: tuple[str | None, ...] = (
@@ -286,6 +304,30 @@ def _audit_resources(*, project: CompiledProject) -> tuple[_ResourceSql, ...]:
286304
return tuple(resources)
287305

288306

307+
def _binds_cleanly(
308+
*, audit: CompiledAudit, shapes: dict[str, dict[str, str]], profile: ExpressionInferenceProfile
309+
) -> bool:
310+
"""A built-in single-column audit on a column its closed target shape contains."""
311+
312+
if (
313+
audit.definition_name not in _COLUMN_ONLY_BUILT_IN_AUDITS
314+
or audit.audit_file.file_path.parts[:1] != (_BUILT_IN_AUDIT_ROOT,)
315+
or audit.attached_target_name is None
316+
or audit.attached_column_name is None
317+
):
318+
return False
319+
shape: dict[str, str] = shapes.get(audit.attached_target_name) or {}
320+
return bool(shape) and (
321+
resolve_binding_column(
322+
name=audit.attached_column_name,
323+
columns=shape,
324+
dialect=profile.sql_analysis_dialect,
325+
ignore_quoted_case=profile.quoted_identifiers_ignore_case,
326+
)
327+
is not None
328+
)
329+
330+
289331
def _audit_label(audit: CompiledAudit) -> str:
290332
label: str = f"audit '{audit.definition_name}'"
291333
if audit.attached_target_name is not None:
@@ -298,13 +340,13 @@ def _audit_label(audit: CompiledAudit) -> str:
298340
def _sql_test_resources(*, project: CompiledProject) -> tuple[_ResourceSql, ...]:
299341
analysis_disabled: frozenset[str] = _analysis_disabled_models(project)
300342
resources: list[_ResourceSql] = []
343+
prepared: dict[str, tuple[str, frozenset[str]]] = {}
301344
for test in project.sql_tests:
302345
if not test.sql_body.strip() or analysis_disabled.intersection(test.expected_model_names):
303346
continue
304-
sql: str = _EMPTY_FIXTURE_CTE_PATTERN.sub(
305-
lambda match: match.group("prefix") + match.group("name"), test.sql_body
306-
)
307-
sql = _EMPTY_FIXTURE_CALL_PATTERN.sub(_OPEN_EMPTY_FIXTURE_RELATION, sql)
347+
if test.sql_body not in prepared:
348+
prepared[test.sql_body] = _prepared_test_sql(test.sql_body)
349+
sql, relations = prepared[test.sql_body]
308350
resources.append(
309351
_ResourceSql(
310352
resource_type=CompiledResourceType.SQL_TEST,
@@ -320,20 +362,26 @@ def _sql_test_resources(*, project: CompiledProject) -> tuple[_ResourceSql, ...]
320362
contents=test.test_file.contents,
321363
body=test.test_block.sql_body,
322364
),
323-
relations=frozenset(
324-
(
325-
*_REFERENCE_CALL_PATTERN.findall(test.sql_body),
326-
*(
327-
match.group("name")
328-
for match in _EMPTY_FIXTURE_CTE_PATTERN.finditer(test.sql_body)
329-
),
330-
)
331-
),
365+
relations=relations,
332366
)
333367
)
334368
return tuple(resources)
335369

336370

371+
def _prepared_test_sql(sql_body: str) -> tuple[str, frozenset[str]]:
372+
"""Test SQL with empty fixtures opened, and the relations it reads."""
373+
374+
sql: str = _EMPTY_FIXTURE_CTE_PATTERN.sub(
375+
lambda match: match.group("prefix") + match.group("name"), sql_body
376+
)
377+
return _EMPTY_FIXTURE_CALL_PATTERN.sub(_OPEN_EMPTY_FIXTURE_RELATION, sql), frozenset(
378+
(
379+
*_REFERENCE_CALL_PATTERN.findall(sql_body),
380+
*(match.group("name") for match in _EMPTY_FIXTURE_CTE_PATTERN.finditer(sql_body)),
381+
)
382+
)
383+
384+
337385
def _hook_resources(*, project: CompiledProject) -> tuple[_ResourceSql, ...]:
338386
hook_files: dict[Path, str] = {
339387
hook_file.relative_path: hook_file.contents for hook_file in project.sql_hook_files

‎tests/e2e/src/sqlbuild/cli/commands/main/compile/test_resource_sql_validation.py‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -189,6 +189,33 @@
189189
),
190190
),
191191
),
192+
ResourceSqlValidationCase(
193+
description="built-in column audit on a column the model does not output",
194+
files=(resource_sql_orders_model(", columns (shipped_at (audits [not_null]))"),),
195+
expected_diagnostics=(
196+
(
197+
"B002",
198+
"audit 'not_null' on model 'orders' column 'shipped_at': "
199+
"Unknown column 'shipped_at' in table 'orders' (context: SELECT)",
200+
"audits/generic/not_null.sql",
201+
3,
202+
),
203+
(
204+
"B002",
205+
"audit 'not_null' on model 'orders' column 'shipped_at': "
206+
"Unknown column 'shipped_at' in table 'orders' (context: SELECT)",
207+
"audits/generic/not_null.sql",
208+
5,
209+
),
210+
(
211+
"K001",
212+
"declared column 'shipped_at' was not found in statically inferred output "
213+
"for model 'orders'",
214+
RESOURCE_SQL_ORDERS,
215+
1,
216+
),
217+
),
218+
),
192219
ResourceSqlValidationCase(
193220
description="unparseable SQL test fixture is a syntax error",
194221
files=(

0 commit comments

Comments
 (0)