From 876d7b035b2be81a0490ac87cd65ccab0e578aab Mon Sep 17 00:00:00 2001 From: Abdulselam Adill Date: Tue, 7 Apr 2026 12:33:57 -0400 Subject: [PATCH 1/3] SNOW-3324331: fix client_prefetch_threads=0 normalization --- DESCRIPTION.md | 1 + src/snowflake/connector/aio/_connection.py | 3 ++- src/snowflake/connector/connection.py | 19 +++++++++++++------ test/unit/aio/test_connection_async_unit.py | 14 ++++++++++++++ test/unit/test_connection.py | 21 +++++++++++++++++++++ 5 files changed, 51 insertions(+), 7 deletions(-) diff --git a/DESCRIPTION.md b/DESCRIPTION.md index 546b0e4cee..e6ce142f76 100644 --- a/DESCRIPTION.md +++ b/DESCRIPTION.md @@ -20,6 +20,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne - Renamed the environment variable for skipping config file permission warnings from `SF_SKIP_WARNING_FOR_READ_PERMISSIONS_ON_CONFIG_FILE` to `SF_SKIP_TOKEN_FILE_PERMISSIONS_VERIFICATION`. The old variable is still supported but emits a deprecation warning. - Fixed `unsafe_skip_file_permissions_check` flag not being respected when reading `connections.toml`. - Fixed JSONDecodeError in result_batch._load() when fetching large result sets + - Fixed a bug where `client_prefetch_threads=0` could fall back to the default thread count instead of following the existing lower-bound correction, and aligned async `client_prefetch_threads` validation with the sync path. - v4.3.0(February 12,2026) - Ensured proper list conversion - the converter runs to_snowflake on all list elements. diff --git a/src/snowflake/connector/aio/_connection.py b/src/snowflake/connector/aio/_connection.py index 094674a4f9..9b6eed308e 100644 --- a/src/snowflake/connector/aio/_connection.py +++ b/src/snowflake/connector/aio/_connection.py @@ -266,7 +266,7 @@ async def __open_connection(self): PARAMETER_CLIENT_SESSION_KEEP_ALIVE_HEARTBEAT_FREQUENCY ] = self._validate_client_session_keep_alive_heartbeat_frequency() - if self.client_prefetch_threads: + if self.client_prefetch_threads is not None: self._session_parameters[PARAMETER_CLIENT_PREFETCH_THREADS] = ( self._validate_client_prefetch_threads() ) @@ -841,6 +841,7 @@ def client_prefetch_threads(self) -> int: @client_prefetch_threads.setter def client_prefetch_threads(self, value) -> None: self._client_prefetch_threads = value + self._validate_client_prefetch_threads() @property def errorhandler(self) -> None: diff --git a/src/snowflake/connector/connection.py b/src/snowflake/connector/connection.py index 7619a7b675..f2bf99f334 100644 --- a/src/snowflake/connector/connection.py +++ b/src/snowflake/connector/connection.py @@ -2288,12 +2288,19 @@ def _validate_client_session_keep_alive_heartbeat_frequency(self) -> int: return self.client_session_keep_alive_heartbeat_frequency def _validate_client_prefetch_threads(self) -> int: - if self.client_prefetch_threads <= 0: - self._client_prefetch_threads = 1 - elif self.client_prefetch_threads > MAX_CLIENT_PREFETCH_THREADS: - self._client_prefetch_threads = MAX_CLIENT_PREFETCH_THREADS - self._client_prefetch_threads = int(self.client_prefetch_threads) - return self.client_prefetch_threads + value = self._client_prefetch_threads + + if value is None: + value = DEFAULT_CLIENT_PREFETCH_THREADS + else: + value = int(value) + if value <= 0: + value = 1 + elif value > MAX_CLIENT_PREFETCH_THREADS: + value = MAX_CLIENT_PREFETCH_THREADS + + self._client_prefetch_threads = value + return value def _update_parameters( self, diff --git a/test/unit/aio/test_connection_async_unit.py b/test/unit/aio/test_connection_async_unit.py index d4943d60e0..03852075f2 100644 --- a/test/unit/aio/test_connection_async_unit.py +++ b/test/unit/aio/test_connection_async_unit.py @@ -110,6 +110,20 @@ async def mock_post_request(request, url, headers, json_body, **kwargs): return request_body +async def test_connect_client_prefetch_threads_zero_clamps_to_one(mock_post_requests): + async with fake_db_conn(client_prefetch_threads=0) as conn: + assert conn.client_prefetch_threads == 1 + assert ( + mock_post_requests["data"]["SESSION_PARAMETERS"]["CLIENT_PREFETCH_THREADS"] + == 1 + ) + + +async def test_client_prefetch_threads_setter_zero_clamps_to_one(mock_post_requests): + async with fake_db_conn() as conn: + conn.client_prefetch_threads = 0 + assert conn.client_prefetch_threads == 1 + async def test_connect_with_service_name(mock_post_requests): async with fake_db_conn() as conn: diff --git a/test/unit/test_connection.py b/test/unit/test_connection.py index 827fbac654..fd6ceb5f0a 100644 --- a/test/unit/test_connection.py +++ b/test/unit/test_connection.py @@ -113,6 +113,27 @@ def test_connect_with_service_name(mock_post_requests): assert fake_connector().service_name == "FAKE_SERVICE_NAME" +def test_connect_client_prefetch_threads_zero_clamps_to_one(mock_post_requests): + conn = fake_connector(client_prefetch_threads=0) + try: + assert conn.client_prefetch_threads == 1 + assert ( + mock_post_requests["data"]["SESSION_PARAMETERS"]["CLIENT_PREFETCH_THREADS"] + == 1 + ) + finally: + conn.close() + + +def test_client_prefetch_threads_setter_zero_clamps_to_one(mock_post_requests): + conn = fake_connector() + try: + conn.client_prefetch_threads = 0 + assert conn.client_prefetch_threads == 1 + finally: + conn.close() + + @pytest.mark.skip(reason="Mock doesn't work as expected.") @patch("snowflake.connector.network.SnowflakeRestful._post_request") def test_connection_ignore_exception(mockSnowflakeRestfulPostRequest): From 29e4e624d8b63675fa3982d26e3f983cf16e4867 Mon Sep 17 00:00:00 2001 From: Abdulselam Adill Date: Tue, 14 Jul 2026 13:42:08 -0400 Subject: [PATCH 2/3] SNOW-3324331: fix lint E302 in async prefetch test --- test/unit/aio/test_connection_async_unit.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit/aio/test_connection_async_unit.py b/test/unit/aio/test_connection_async_unit.py index fc434f1a1d..bdefb8547a 100644 --- a/test/unit/aio/test_connection_async_unit.py +++ b/test/unit/aio/test_connection_async_unit.py @@ -110,6 +110,7 @@ async def mock_post_request(request, url, headers, json_body, **kwargs): return request_body + async def test_connect_client_prefetch_threads_zero_clamps_to_one(mock_post_requests): async with fake_db_conn(client_prefetch_threads=0) as conn: assert conn.client_prefetch_threads == 1 From 634a6e68f7ef952c95ff84655959c8efa763fb2b Mon Sep 17 00:00:00 2001 From: Abdulselam Adill Date: Wed, 15 Jul 2026 11:27:38 -0400 Subject: [PATCH 3/3] SNOW-3324331: move changelog entry to NEXT_RELEASE --- DESCRIPTION.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DESCRIPTION.md b/DESCRIPTION.md index d6cd7d2288..705c5d5c6b 100644 --- a/DESCRIPTION.md +++ b/DESCRIPTION.md @@ -10,6 +10,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne - NEXT_RELEASE(TBD) - Added support for Python 3.14t (free-threaded). - **Note:** Python 3.14t CI testing excludes `win_arm64` (no `cryptography` wheels available) and `mitmproxy` proxy tests on all platforms (transitive dependencies `aioquic`/`pylsqpack` lack free-threaded-compatible wheels). + - Fixed a bug where `client_prefetch_threads=0` could fall back to the default thread count instead of following the existing lower-bound correction, and aligned async `client_prefetch_threads` validation with the sync path. - v4.7.0(Jul 2,2026) - Fixed `python-connector.log` not rotating on Windows, and every record being logged twice, when easy logging is enabled via `config.toml` (SNOW-3680325). @@ -26,7 +27,6 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne - v4.6.0(May 28,2026) - Dropped support for Python 3.9. The minimum supported version is now Python 3.10. - Fixed sdist to only install the minicore binary matching the current platform (SNOW-3526469). Previous 4.x releases copied every platform's minicore `.so`/`.dylib`/`.dll` into the install prefix, breaking downstream packagers (e.g. Homebrew) whose audits reject foreign-arch binaries. - - Fixed a bug where `client_prefetch_threads=0` could fall back to the default thread count instead of following the existing lower-bound correction, and aligned async `client_prefetch_threads` validation with the sync path. - Added one in-band telemetry record per successful login describing which connection-identifier fields the user supplied (`account_provided`, `account_with_region`, `account_org_provided`, `region_provided`, `host_provided`). No hostname or account value is included. This is gated by the existing server-side `CLIENT_TELEMETRY_ENABLED` parameter and can additionally be disabled locally by setting `SF_TELEMETRY_DISABLE_CONNECTION_SHAPE=true`. The telemetry collection is time-boxed and will be removed in a future release. - Bumped up vendored `urllib3` to `2.7.0`