From ec061c3746913de9cad2680aa2cea9fb758d810f Mon Sep 17 00:00:00 2001 From: Kevin Smith Date: Tue, 13 Oct 2026 13:47:53 -0400 Subject: [PATCH] Expose sql_mode connection parameter Add sql_mode to singlestoredb.connect() and register it as a config option (SINGLESTOREDB_SQL_MODE). The MySQL driver already applied sql_mode on connect; it is now reachable via kwargs, URLs, and env. The HTTP Data API is stateless and has no session-variable support, so the HTTP connection accepts sql_mode for API parity but emits a RuntimeWarning that it is ignored. Co-Authored-By: Claude Opus 5.5 --- singlestoredb/config.py | 7 +++++++ singlestoredb/connection.py | 5 +++++ singlestoredb/http/connection.py | 9 +++++++++ singlestoredb/tests/test_connection.py | 18 ++++++++++++++++++ 4 files changed, 39 insertions(+) diff --git a/singlestoredb/config.py b/singlestoredb/config.py index bb9914b69..191d9cd9d 100644 --- a/singlestoredb/config.py +++ b/singlestoredb/config.py @@ -93,6 +93,13 @@ environ='SINGLESTOREDB_ENCODING_ERRORS', ) +register_option( + 'sql_mode', 'string', check_str, None, + 'Specifies the sql_mode session variable to set when connecting. ' + 'This is only applied to MySQL protocol connections.', + environ='SINGLESTOREDB_SQL_MODE', +) + register_option( 'local_infile', 'bool', check_bool, False, 'Should it be possible to load local files?', diff --git a/singlestoredb/connection.py b/singlestoredb/connection.py index 10a81b2c2..facfbc584 100644 --- a/singlestoredb/connection.py +++ b/singlestoredb/connection.py @@ -1382,6 +1382,7 @@ def connect( parse_json: Optional[bool] = None, interpolate_query_with_empty_args: Optional[bool] = None, socket_options: Optional[Dict[int, Dict[int, Any]]] = None, + sql_mode: Optional[str] = None, ) -> Connection: """ Return a SingleStoreDB connection. @@ -1475,6 +1476,10 @@ def connect( 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. + sql_mode : str, optional + Value of the ``sql_mode`` session variable to set when connecting. + This is not supported by HTTP connections and will be ignored + with a warning. Examples -------- diff --git a/singlestoredb/http/connection.py b/singlestoredb/http/connection.py index 1187afad9..fb01d5a58 100644 --- a/singlestoredb/http/connection.py +++ b/singlestoredb/http/connection.py @@ -9,6 +9,7 @@ import os import re import time +import warnings from base64 import b64decode from collections.abc import Iterable from collections.abc import Sequence @@ -1040,6 +1041,13 @@ def __init__(self, **kwargs: Any): 'statements within a query', ) + if kwargs.get('sql_mode', None) is not None: + warnings.warn( + 'The `sql_mode=` parameter is not supported by the Data API ' + 'and will be ignored for HTTP connections.', + RuntimeWarning, + ) + self._version = kwargs.get('version', 'v2') self.driver = kwargs.get('driver', 'https') @@ -1268,5 +1276,6 @@ def connect( track_env: Optional[bool] = None, enable_extended_data_types: Optional[bool] = None, vector_data_format: Optional[str] = None, + sql_mode: Optional[str] = None, ) -> Connection: return Connection(**dict(locals())) diff --git a/singlestoredb/tests/test_connection.py b/singlestoredb/tests/test_connection.py index 2ae5cf1d2..280d78c8e 100755 --- a/singlestoredb/tests/test_connection.py +++ b/singlestoredb/tests/test_connection.py @@ -356,6 +356,24 @@ def test_connection_attr(self): with self.conn as conn: assert conn is self.conn + def test_sql_mode(self): + if self.conn.driver in ['http', 'https']: + with self.assertWarns(RuntimeWarning): + s2.connect(database=type(self).dbname, sql_mode='ANSI_QUOTES') + return + + with s2.connect(database=type(self).dbname, sql_mode='ANSI_QUOTES') as conn: + with conn.cursor() as cur: + cur.execute('select @@sql_mode') + assert 'ANSI_QUOTES' in cur.fetchone()[0] + + def test_sql_mode_param(self): + out = sc.build_params(host='me:p455w0rd@s2host.com/mydb?sql_mode=ANSI_QUOTES') + assert out['sql_mode'] == 'ANSI_QUOTES', out['sql_mode'] + + out = sc.build_params(host='s2host.com', sql_mode='PIPES_AS_CONCAT') + assert out['sql_mode'] == 'PIPES_AS_CONCAT', out['sql_mode'] + def test_executemany(self): # NOTE: Doesn't actually do anything since no rows match self.cur.executemany(