Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 47 additions & 12 deletions sqlmesh/core/renderer.py
Original file line number Diff line number Diff line change
Expand Up @@ -330,12 +330,32 @@ def _resolve_table(
table_mapping: t.Optional[t.Dict[str, str]] = None,
deployability_index: t.Optional[DeployabilityIndex] = None,
) -> exp.Table:
table_mapping = table_mapping or {}
if isinstance(table_name, str):
# An exact FQN match avoids scanning unrelated snapshots.
snapshot = snapshots.get(table_name) if snapshots else None
if snapshot is None and table_name not in table_mapping:
# Keys normalized under different dialects may differ in casing or quoting.
# Fall back to the full mapping so exp.replace_tables can reconcile them.
mapping = {
**self._to_table_mapping((snapshots or {}).values(), deployability_index),
**table_mapping,
}
else:
mapping = {
**self._to_table_mapping([snapshot] if snapshot else [], deployability_index),
# Keep all explicit overrides to preserve precedence for equivalent keys.
**table_mapping,
}
else:
mapping = {
**self._to_table_mapping((snapshots or {}).values(), deployability_index),
**table_mapping,
}

table = exp.replace_tables(
t.cast(exp.Table, exp.maybe_parse(table_name, into=exp.Table, dialect=self._dialect)),
{
**self._to_table_mapping((snapshots or {}).values(), deployability_index),
**(table_mapping or {}),
},
mapping,
dialect=self._dialect,
copy=False,
)
Expand Down Expand Up @@ -363,12 +383,15 @@ def _resolve_tables(

expression = expression.copy()
with self._normalize_and_quote(expression) as expression:
# An expression with no exp.Table node at all (e.g. session/virtual properties) has
# nothing for `expand` to expand or for a table mapping to replace - skip building
# the expand set and model_mapping too, not just the mapping/replace_tables below,
# since both of those are themselves O(N) in the number of snapshots.
if not expression.find(exp.Table):
return expression

snapshots = snapshots or {}
table_mapping = table_mapping or {}
mapping = {
**self._to_table_mapping(snapshots.values(), deployability_index),
**table_mapping,
}
expand = set(expand) | {
name for name, snapshot in snapshots.items() if snapshot.is_embedded
}
Expand Down Expand Up @@ -410,10 +433,22 @@ def _expand(node: exp.Expr) -> exp.Expr:

expression = expression.transform(_expand, copy=False) # type: ignore

if mapping:
expression = exp.replace_tables(
expression, mapping, dialect=self._dialect, copy=False
)
# Building the full snapshot -> table-name mapping and normalizing it in
# exp.replace_tables is O(N) in the number of snapshots in the environment; skip it
# entirely for expressions that don't reference any table at all (e.g. session/
# virtual properties), since there's nothing for the mapping to replace.
if expression.find(exp.Table):
# mypy loses the `snapshots`/`table_mapping` narrowing above because they're
# captured by the `_expand` closure defined earlier in this block.
assert snapshots is not None and table_mapping is not None
mapping = {
**self._to_table_mapping(snapshots.values(), deployability_index),
**table_mapping,
}
if mapping:
expression = exp.replace_tables(
expression, mapping, dialect=self._dialect, copy=False
)

return expression

Expand Down
Loading
Loading