diff --git a/.secrets.baseline b/.secrets.baseline index 96bf780809c..bdbf199f662 100644 --- a/.secrets.baseline +++ b/.secrets.baseline @@ -1426,7 +1426,7 @@ "filename": "sdk/python/tests/unit/infra/utils/snowflake/test_snowflake_utils.py", "hashed_secret": "a94a8fe5ccb19ba61c4c0873d391e987982fbbd3", "is_verified": false, - "line_number": 10 + "line_number": 14 } ], "sdk/python/tests/unit/local_feast_tests/test_init.py": [ @@ -1539,5 +1539,5 @@ } ] }, - "generated_at": "2026-04-07T15:56:56Z" + "generated_at": "2026-04-09T03:30:18Z" } diff --git a/sdk/python/feast/infra/utils/snowflake/snowflake_utils.py b/sdk/python/feast/infra/utils/snowflake/snowflake_utils.py index b9254e72699..66e3cb22acd 100644 --- a/sdk/python/feast/infra/utils/snowflake/snowflake_utils.py +++ b/sdk/python/feast/infra/utils/snowflake/snowflake_utils.py @@ -123,6 +123,8 @@ def assert_snowflake_feature_names(feature_view: FeatureView) -> None: def execute_snowflake_statement(conn: SnowflakeConnection, query) -> SnowflakeCursor: + if not query.strip(): + return conn.cursor() cursor = conn.cursor().execute(query) if cursor is None: raise SnowflakeQueryUnknownError(query) diff --git a/sdk/python/tests/unit/infra/utils/snowflake/test_snowflake_utils.py b/sdk/python/tests/unit/infra/utils/snowflake/test_snowflake_utils.py index 8ae6ec63ba5..212944210a7 100644 --- a/sdk/python/tests/unit/infra/utils/snowflake/test_snowflake_utils.py +++ b/sdk/python/tests/unit/infra/utils/snowflake/test_snowflake_utils.py @@ -1,11 +1,15 @@ import tempfile from typing import Optional +from unittest.mock import MagicMock import pytest from cryptography.hazmat.primitives import serialization from cryptography.hazmat.primitives.asymmetric import rsa -from feast.infra.utils.snowflake.snowflake_utils import parse_private_key_path +from feast.infra.utils.snowflake.snowflake_utils import ( + execute_snowflake_statement, + parse_private_key_path, +) PRIVATE_KEY_PASSPHRASE = "test" @@ -69,3 +73,48 @@ def test_parse_private_key_path_key_path_encrypted(encrypted_private_key): f.name, None, ) + + +class TestExecuteSnowflakeStatement: + def test_empty_query_returns_cursor_without_executing(self): + mock_conn = MagicMock() + mock_cursor = MagicMock() + mock_conn.cursor.return_value = mock_cursor + + result = execute_snowflake_statement(mock_conn, "") + + assert result is mock_cursor + mock_conn.cursor.assert_called_once() + mock_cursor.execute.assert_not_called() + + def test_whitespace_only_query_returns_cursor_without_executing(self): + mock_conn = MagicMock() + mock_cursor = MagicMock() + mock_conn.cursor.return_value = mock_cursor + + result = execute_snowflake_statement(mock_conn, " \t\n ") + + assert result is mock_cursor + mock_conn.cursor.assert_called_once() + mock_cursor.execute.assert_not_called() + + def test_valid_query_executes_and_returns_cursor(self): + mock_conn = MagicMock() + mock_cursor = MagicMock() + mock_executed_cursor = MagicMock() + mock_conn.cursor.return_value = mock_cursor + mock_cursor.execute.return_value = mock_executed_cursor + + result = execute_snowflake_statement(mock_conn, "SELECT 1") + + assert result is mock_executed_cursor + mock_cursor.execute.assert_called_once_with("SELECT 1") + + def test_valid_query_raises_on_none_cursor(self): + mock_conn = MagicMock() + mock_cursor = MagicMock() + mock_conn.cursor.return_value = mock_cursor + mock_cursor.execute.return_value = None + + with pytest.raises(Exception, match="Snowflake query failed"): + execute_snowflake_statement(mock_conn, "SELECT 1")