Skip to content
Closed
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
20 changes: 19 additions & 1 deletion sqlmesh/core/selector.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,14 +191,32 @@ def expand_model_selections(
models_by_tags.setdefault(tag, set())
models_by_tags[tag].add(model.fqn)

def _normalize_for_match(name: str) -> str:
"""Strip surrounding identifier quotes on each component for glob-style matching."""
parts = name.split(".")

def _unquote(part: str) -> str:
if len(part) >= 2 and part[0] == part[-1] and part[0] in {"'", '"', "`", "["}:
# For [] quoting, ensure trailing ]
if part[0] == "[" and part[-1] != "]":
return part
return part[1:-1]
return part

return ".".join(_unquote(p) for p in parts)

def evaluate(node: exp.Expression) -> t.Set[str]:
if isinstance(node, exp.Var):
pattern = node.this
if "*" in pattern:
normalized_pattern = _normalize_for_match(pattern)
return {
fqn
for fqn, model in all_models.items()
if fnmatch.fnmatchcase(self._model_name(model), node.this)
if fnmatch.fnmatchcase(
_normalize_for_match(self._model_name(model)),
normalized_pattern,
)
}
return self._pattern_to_model_fqns(pattern, all_models)
if isinstance(node, exp.And):
Expand Down
29 changes: 29 additions & 0 deletions tests/core/test_selector_quoted_wildcard.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from __future__ import annotations

import fnmatch

import pytest

from sqlmesh.core.selector import parse


@pytest.mark.parametrize(
("selector", "expected"),
[
("foo.bar", "foo.bar"),
("foo*", "foo*"),
("*bar", "*bar"),
("'biquery-project'.schema.table", "'biquery-project'.schema.table"),
("'biquery-project'.schema*", "'biquery-project'.schema*"),
('"biquery-project".schema*', '"biquery-project".schema*'),
("`biquery-project`.schema*", "`biquery-project`.schema*"),
],
)
def test_parse_preserves_quotes_in_var(selector: str, expected: str) -> None:
node = parse(selector)
assert node.this == expected


def test_fnmatch_works_with_quotes_in_pattern() -> None:
model_name = "'biquery-project'.schema.table"
assert fnmatch.fnmatchcase(model_name, "'biquery-project'.schema*")