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
6 changes: 6 additions & 0 deletions singlestoredb/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from .utils.config import check_float # noqa: F401
from .utils.config import check_int # noqa: F401
from .utils.config import check_optional_bool # noqa: F401
from .utils.config import check_socket_options # noqa: F401
from .utils.config import check_str # noqa: F401
from .utils.config import check_url # noqa: F401
from .utils.config import describe_option # noqa: F401
Expand Down Expand Up @@ -263,6 +264,11 @@
environ='SINGLESTOREDB_FUSION_ENABLED',
)

register_option(
'socket_options', 'dict', check_socket_options, None,
'Format for socket options',
)

#
# Query results options
#
Expand Down
6 changes: 6 additions & 0 deletions singlestoredb/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1340,6 +1340,7 @@ def connect(
vector_data_format: Optional[str] = None,
parse_json: Optional[bool] = None,
interpolate_query_with_empty_args: Optional[bool] = None,
socket_options: Optional[Dict[int, Dict[int, Any]]] = None,
) -> Connection:
"""
Return a SingleStoreDB connection.
Expand Down Expand Up @@ -1428,6 +1429,11 @@ def connect(
interpolate_query_with_empty_args : bool, optional
Should the connector apply parameter interpolation even when the
parameters are empty? This corresponds to pymysql/mysqlclient's handling
socket_options : dict, optional
Socket options to set on the underlying socket. The keys should be
socket level constants (e.g., socket.SOL_SOCKET) and the values should be
dictionaries mapping socket option constants (e.g., socket.SO_KEEPALIVE) to
the desired value for that option.

Examples
--------
Expand Down
12 changes: 11 additions & 1 deletion singlestoredb/mysql/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -230,6 +230,10 @@ class Connection(BaseConnection):
Set to true to check the server's identity.
tls_sni_servername: str, optional
Set server host name for TLS connection
socket_options: Dict[int, Dict[int, any]], optional
A dictionary of socket options to set on the connection.
The keys are the socket level constants (e.g., socket.SOL_SOCKET),
and the values are dictionaries mapping option names to values.
read_default_group : str, optional
Group to read from in the configuration file.
autocommit : bool, optional
Expand Down Expand Up @@ -341,6 +345,7 @@ def __init__( # noqa: C901
ssl_verify_cert=None,
ssl_verify_identity=None,
tls_sni_servername=None,
socket_options=None,
parse_json=True,
invalid_values=None,
pure_python=None,
Expand Down Expand Up @@ -477,7 +482,7 @@ def _config(key, arg):
self.collation = collation
self.use_unicode = use_unicode
self.encoding_errors = encoding_errors

self._socket_options = socket_options or {}
self.encoding = charset_by_name(self.charset).encoding

client_flag |= CLIENT.CAPABILITIES
Expand Down Expand Up @@ -1107,6 +1112,11 @@ def connect(self, sock=None):
print('connected using socket')
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_KEEPALIVE, 1)

for level, options in self._socket_options.items():
for opt, value in options.items():
sock.setsockopt(level, opt, value)

sock.settimeout(None)

self._sock = sock
Expand Down
49 changes: 49 additions & 0 deletions singlestoredb/utils/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -646,6 +646,55 @@ def check_str(
return out


def check_socket_options(
value: Any,
) -> Optional[Dict[int, Dict[int, Any]]]:
"""
Validate socket options.

Parameters
----------
value : dict
The value to validate. It must be a dictionary where the keys are
socket level constants (e.g., socket.SOL_SOCKET) and the values are
dictionaries mapping socket option constants (e.g., socket.SO_KEEPALIVE)
to the desired value for that option.

Returns
-------
dict
The validated socket options

"""
if value is None:
return None

if not isinstance(value, Mapping):
raise ValueError(
'value {} must be of type dict'.format(value),
)

out: dict[int, dict[int, Any]] = {}
for level, options in value.items():
if not isinstance(level, int):
raise ValueError(
f'keys in {value} must be integers corresponding to socket levels',
)
if not isinstance(options, Mapping):
raise ValueError(
f'values in {value} must be dicts.',
)
out[level] = {}
for opt, val in options.items():
if not isinstance(opt, int):
raise ValueError(
f'keys in sub-dicts of {value} must be integers.',
)
out[level][opt] = val

return out


def check_dict_str_str(
value: Any,
) -> Optional[Dict[str, str]]:
Expand Down
Loading