Skip to content
Open
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
1 change: 1 addition & 0 deletions DESCRIPTION.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ Source code is also available at: https://github.com/snowflakedb/snowflake-conne
# Release Notes
- v4.7.6 (Unreleased)
- Fixed external-browser (SSO) authentication to validate the `Origin` header on the local callback server, rejecting tokens delivered from unexpected origins. A trailing slash in the origin (e.g. `https://account.snowflakecomputing.com/`) is now accepted on par with the bare origin, matching JDBC and other driver behaviour. Preconnect probe connections (empty recv) no longer count against the retry budget and no longer abort the login flow.
- Fixed finite `decimal.Decimal` values being quoted as strings in client-side parameter binding, preserving their exact numeric representation (#2455).
- Added the `SNOWFLAKE_TLS_CIPHERS` environment variable to restrict which TLS ciphers the connector offers. It takes a colon-separated list; names beginning with `TLS_` are applied as TLS 1.3 cipher suites and the remainder as the cipher list for TLS 1.2 and below, so a single variable covers both. Leaving it unset keeps OpenSSL's defaults unchanged, and an unrecognized cipher name is rejected rather than silently ignored. The restriction covers Snowflake API traffic, cloud-storage (stage) transfers, OCSP/CRL fetches and IdP requests. Requests issued by the AWS and Azure SDKs, and asynchronous connections, are not covered — for TLS 1.3 suites specifically they cannot be, because the Python standard library exposes no API for restricting them.
- Raised the minimum `pyOpenSSL` requirement to 25.3.0, the first version providing `set_tls13_ciphersuites`. This does not narrow the set of installable versions in practice: earlier releases cap `cryptography` below 46 and so were already uninstallable alongside the connector's own `cryptography>=46.0.5` requirement.
- Added the `workload_identity_host` connection option that overrides the STS host used by AWS Workload Identity Federation, for endpoints the driver cannot derive from the region (such as an interface VPC endpoint). The default STS host is now resolved via botocore so partitions that do not use `amazonaws.com` (ISO, European Sovereign Cloud, ...) get the correct hostname. A privately routed host cannot be reached by Snowflake on the default GetCallerIdentity path, so a VPC or PrivateLink STS endpoint also requires `workload_identity_aws_use_outbound_token=True` (SNOW-4017192).
Expand Down
8 changes: 6 additions & 2 deletions src/snowflake/connector/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -639,9 +639,11 @@ def _timedelta_to_snowflake(self, value: timedelta) -> str:
hour=hours, minute=mins, second=secs
)

def _decimal_to_snowflake(self, value: decimal.Decimal) -> str | None:
def _decimal_to_snowflake(
self, value: decimal.Decimal
) -> decimal.Decimal | str | None:
if isinstance(value, decimal.Decimal):
return str(value)
return value if value.is_finite() else str(value)

return None

Expand Down Expand Up @@ -739,6 +741,8 @@ def quote(value) -> str:
return "NULL"
elif isinstance(value, bool):
return "TRUE" if value else "FALSE"
elif isinstance(value, decimal.Decimal):
return str(value)
elif IS_NUMERIC(value):
return str(repr(value))
elif IS_BINARY(value):
Expand Down
29 changes: 29 additions & 0 deletions test/unit/test_converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,35 @@
ConverterSnowSQL = SnowflakeConverterSnowSQL


@pytest.mark.parametrize(
"literal",
["1.3", "12345678901234567890123456789012345678", "-0.00", "1.2300", "1E-20"],
)
def test_decimal_client_side_binding(literal):
converter = SnowflakeConverter()
value = converter.to_snowflake(Decimal(literal))
assert converter.quote(converter.escape(value)) == literal


@pytest.mark.parametrize("literal", ["NaN", "sNaN", "Infinity", "-Infinity"])
def test_non_finite_decimal_client_side_binding(literal):
converter = SnowflakeConverter()
value = converter.to_snowflake(Decimal(literal))
assert converter.quote(converter.escape(value)) == f"'{literal}'"


def test_decimal_list_client_side_binding():
converter = SnowflakeConverter()
value = converter.to_snowflake([Decimal("1.30"), "1.30", None, Decimal("-0.00")])
assert converter.quote(converter.escape(value)) == "1.30,'1.30',NULL,-0.00"


def test_decimal_server_side_binding():
converter = SnowflakeConverter()
value = Decimal("12345678901234567890.123456789")
assert converter.to_snowflake_bindings("FIXED", value) == str(value)


def test_is_dst():
"""SNOW-6020: Failed to convert to local time during DST is being changed."""
# DST to non-DST
Expand Down