Skip to content
Merged
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
15 changes: 15 additions & 0 deletions mssql_python/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,7 @@ def __init__(
"ctype": ConstantsDDBC.SQL_WCHAR.value,
},
}
self._decoding_generation = 0

# Auth type for acquiring fresh tokens at bulk copy time.
# We intentionally do NOT cache the token — a fresh one is acquired
Expand Down Expand Up @@ -782,6 +783,7 @@ def _token_factory():

# Initialize output converters dictionary and its lock for thread safety
self._output_converters = {}
self._converters_generation = 0
self._converters_lock = threading.Lock()

# Initialize encoding/decoding settings lock for thread safety
Expand Down Expand Up @@ -1320,6 +1322,9 @@ def setdecoding(
"""
Sets the text decoding used when reading SQL_CHAR and SQL_WCHAR from the database.

Existing cursors refresh their cached SQL_CHAR/SQL_WCHAR decoding settings
before their next fetch.

This method configures how text data is decoded when reading from the database.
In Python 3, all text is Unicode (str), so this primarily affects the encoding
used to decode bytes from the database.
Expand Down Expand Up @@ -1443,6 +1448,7 @@ def setdecoding(
# Store the decoding settings for the specified sqltype (thread-safe with lock)
with self._encoding_lock:
self._decoding_settings[sqltype] = {"encoding": encoding, "ctype": ctype}
self._decoding_generation += 1

# Log with sanitized values for security
sqltype_name = {
Expand Down Expand Up @@ -1647,6 +1653,8 @@ def add_output_converter(self, sqltype: Union[int, type], func: Callable[[Any],

Thread-safe implementation that protects the converters dictionary with a lock.

Changes apply on the next fetch, including for an already executed result set.

⚠️ WARNING: Registering an output converter will cause the supplied Python function
to be executed on every matching database value. Do not register converters from
untrusted sources, as this can result in arbitrary code execution and security
Expand Down Expand Up @@ -1687,6 +1695,7 @@ def add_output_converter(self, sqltype: Union[int, type], func: Callable[[Any],
"""
with self._converters_lock:
self._output_converters[sqltype] = func
self._converters_generation += 1
# Pass to the underlying connection if native implementation supports it
if hasattr(self._conn, "add_output_converter"):
self._conn.add_output_converter(sqltype, func)
Expand Down Expand Up @@ -1717,6 +1726,8 @@ def remove_output_converter(self, sqltype: Union[int, type]) -> None:

Thread-safe implementation that protects the converters dictionary with a lock.

Existing cursors use the updated converters on their next fetch.

Args:
sqltype (int or type): The SQL type value to remove the converter for

Expand All @@ -1726,6 +1737,7 @@ def remove_output_converter(self, sqltype: Union[int, type]) -> None:
with self._converters_lock:
if sqltype in self._output_converters:
del self._output_converters[sqltype]
self._converters_generation += 1
# Pass to the underlying connection if native implementation supports it
if hasattr(self._conn, "remove_output_converter"):
self._conn.remove_output_converter(sqltype)
Expand All @@ -1737,11 +1749,14 @@ def clear_output_converters(self) -> None:

Thread-safe implementation that protects the converters dictionary with a lock.

Existing cursors stop applying converters on their next fetch.

Returns:
None
"""
with self._converters_lock:
self._output_converters.clear()
self._converters_generation += 1
# Pass to the underlying connection if native implementation supports it
if hasattr(self._conn, "clear_output_converters"):
self._conn.clear_output_converters()
Expand Down
Loading
Loading