Skip to content

Commit 776e1cf

Browse files
authored
Added escape_name function to escape individual SQL names and escape_full_name function to escape dot-separated full names (#316)
Ported from https://github.com/databrickslabs/ucx by @larsgeorge-db , @JCZuurmond , and @asnare .
1 parent aae9ea1 commit 776e1cf

File tree

2 files changed

+53
-0
lines changed

2 files changed

+53
-0
lines changed

src/databricks/labs/lsql/escapes.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
def escape_name(name: str) -> str:
2+
"""Escapes the name to make it SQL safe."""
3+
return f"`{name.strip('`').replace('`', '``')}`"
4+
5+
6+
def escape_full_name(full_name: str) -> str:
7+
"""
8+
Escapes the full name components to make them SQL safe.
9+
10+
Args:
11+
full_name (str): The dot-separated name of a catalog object.
12+
13+
Returns:
14+
str: The path with all parts escaped in backticks.
15+
"""
16+
if not full_name:
17+
return full_name
18+
parts = full_name.split(".", maxsplit=2)
19+
return ".".join(escape_name(part) for part in parts)

tests/unit/test_escapes.py

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import pytest
2+
3+
from databricks.labs.lsql.escapes import escape_full_name, escape_name
4+
5+
6+
@pytest.mark.parametrize(
7+
"path,expected",
8+
[
9+
("a", "`a`"),
10+
("a.b", "`a`.`b`"),
11+
("a.b.c", "`a`.`b`.`c`"),
12+
("`a`.b.c", "`a`.`b`.`c`"),
13+
("a.`b`.c", "`a`.`b`.`c`"),
14+
("a.b.`c`", "`a`.`b`.`c`"),
15+
("`a.b`.c", "`a`.`b`.`c`"),
16+
("a.`b.c`", "`a`.`b`.`c`"),
17+
("`a.b`.`c`", "`a`.`b`.`c`"),
18+
("`a`.`b.c`", "`a`.`b`.`c`"),
19+
("`a`.`b`.`c`", "`a`.`b`.`c`"),
20+
("a.b.c.d", "`a`.`b`.`c.d`"),
21+
("a-b.c.d", "`a-b`.`c`.`d`"),
22+
("a.b-c.d", "`a`.`b-c`.`d`"),
23+
("a.b.c-d", "`a`.`b`.`c-d`"),
24+
("a.b.c`d", "`a`.`b`.`c``d`"),
25+
("✨.🍰.✨", "`✨`.`🍰`.`✨`"),
26+
("", ""),
27+
],
28+
)
29+
def test_escaped_path(path: str, expected: str) -> None:
30+
assert escape_full_name(path) == expected
31+
32+
33+
def test_escaped_when_column_contains_period() -> None:
34+
assert escape_name("column.with.periods") == "`column.with.periods`"

0 commit comments

Comments
 (0)