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
10 changes: 5 additions & 5 deletions mssql_python/connection_string_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,9 @@ def _escape_value(self, value: str) -> str:
"""
Escape a parameter value if it contains special characters.

Per MS-ODBCSTR specification:
- Values containing ';', '{', '}', '=', or spaces should be braced for safety
- '}' inside braced values is escaped as '}}'
- '{' inside braced values is escaped as '{{'
- '{' does not need to be escaped

Args:
value: Parameter value to escape
Expand All @@ -95,7 +94,7 @@ def _escape_value(self, value: str) -> str:
>>> builder._escape_value("local;host")
'{local;host}'
>>> builder._escape_value("p}w{d")
'{p}}w{{d}'
'{p}}w{d}'
>>> builder._escape_value("ODBC Driver 18 for SQL Server")
'{ODBC Driver 18 for SQL Server}'
"""
Expand All @@ -107,8 +106,9 @@ def _escape_value(self, value: str) -> str:
needs_braces = any(ch in value for ch in ";{}= ")

if needs_braces:
# Escape existing braces by doubling them
escaped = value.replace("}", "}}").replace("{", "{{")
# Escape closing braces by doubling them (ODBC requirement)
# Opening braces do not need to be escaped
escaped = value.replace("}", "}}")
return f"{{{escaped}}}"
else:
return value
16 changes: 3 additions & 13 deletions mssql_python/connection_string_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
Handles ODBC-specific syntax per MS-ODBCSTR specification:
- Semicolon-separated key=value pairs
- Braced values: {value}
- Escaped braces: }} → }, {{ → {
- Escaped braces: }} → } (only closing braces need escaping)

Parser behavior:
- Validates all key=value pairs
Expand Down Expand Up @@ -331,7 +331,7 @@ def _parse_braced_value(self, connection_str: str, start_pos: int) -> Tuple[str,
Braced values:
- Start with '{' and end with '}'
- '}' inside the value is escaped as '}}'
- '{' inside the value is escaped as '{{'
- '{' inside the value does not need escaping
- Can contain semicolons and other special characters

Args:
Expand Down Expand Up @@ -366,18 +366,8 @@ def _parse_braced_value(self, connection_str: str, start_pos: int) -> Tuple[str,
# Single '}' means end of braced value
start_pos += 1
return "".join(value), start_pos
elif ch == "{":
# Check if it's an escaped left brace
if start_pos + 1 < str_len and connection_str[start_pos + 1] == "{":
# Escaped left brace: '{{' → '{'
value.append("{")
start_pos += 2
else:
# Single '{' inside braced value - keep it as is
value.append(ch)
start_pos += 1
else:
# Regular character
# Regular character (including '{' which doesn't need escaping per ODBC spec)
value.append(ch)
start_pos += 1

Expand Down
12 changes: 6 additions & 6 deletions tests/test_010_connection_string_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,13 @@ def test_parse_braced_value_with_escaped_right_brace(self):
"""Test parsing braced values with escaped }}."""
parser = _ConnectionStringParser()
result = parser._parse("PWD={p}}w{{d}")
assert result == {"pwd": "p}w{d"}
assert result == {"pwd": "p}w{{d"}

def test_parse_braced_value_with_all_escapes(self):
"""Test parsing braced values with both {{ and }} escapes."""
"""Test parsing braced values with }} escape ({{ not an escape sequence)."""
parser = _ConnectionStringParser()
result = parser._parse("Value={test}}{{escape}")
assert result == {"value": "test}{escape"}
assert result == {"value": "test}{{escape"}

def test_parse_empty_value(self):
"""Test that empty value raises error."""
Expand Down Expand Up @@ -146,10 +146,10 @@ def test_parse_braced_value_with_left_brace(self):
assert result == {"value": "test{value"}

def test_parse_braced_value_double_left_brace(self):
"""Test parsing braced value with escaped {{ (left brace)."""
"""Test parsing braced value with {{ (not an escape sequence)."""
parser = _ConnectionStringParser()
result = parser._parse("Value={test{{value}")
assert result == {"value": "test{value"}
assert result == {"value": "test{{value"}

def test_parse_unicode_characters(self):
"""Test parsing values with unicode characters."""
Expand Down Expand Up @@ -197,7 +197,7 @@ def test_parse_special_characters_in_braced_values(self):

# Multiple special chars including braces
result = parser._parse("Token={Bearer: abc123; Expires={{2024-01-01}}}")
assert result == {"token": "Bearer: abc123; Expires={2024-01-01}"}
assert result == {"token": "Bearer: abc123; Expires={{2024-01-01}"}

def test_parse_numbers_and_symbols_in_passwords(self):
"""Test parsing passwords with various numbers and symbols."""
Expand Down
Loading