You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Drop SQLAlchemy 1.4 support: the dialect now requires SQLAlchemy>=2.0.44,<2.1. SQLAlchemy 2.1.0 removed sqlalchemy.orm.context.ORMSelectCompileState and changed other APIs the dialect and async shim import at module load time, so 2.1 is excluded. The internal snowflake.sqlalchemy.compat module (SQLAlchemy 1.4/2.0 shim) was removed. Users still on SQLAlchemy 1.4 should pin snowflake-sqlalchemy<2.0.0.
Require snowflake-connector-python>=5.0.0 (connector 5.x), which provides the async driver. Connector 4.x is no longer supported.
Supported Python versions are now 3.11–3.14 (drop 3.8, 3.9 and 3.10), since connector 5.x itself requires Python 3.11+. Updates requires-python, Python classifiers, and mypy/ruff targets accordingly.
enable_structured_type_json is on by default (see Features); explicitly setting it to False restores raw JSON strings and emits a DeprecationWarning (SNOW-3942921).
Remove the force_div_is_floordiv dialect flag. The / operator always performs true division (left / right) and // always performs floor division (FLOOR(left / right)). Passing the flag to create_engine() now raises TypeError. Coming from a release older than 1.11.1, integer // previously compiled to plain / (true division in Snowflake) under the default flag value; it now floors (GH #756).
Remove the legacy_url_params opt-out and its SNOWFLAKE_SQLALCHEMY_LEGACY_URL_PARAMS environment variable. Passing it to create_engine() now raises TypeError and the environment variable has no effect; sensitive connection parameters must be passed via connect_args= (SNOW-3942921). unsafe_skip_config_file_permissions_check and crl_unsafe_skip_file_permissions_check are now also blocked from the URL query string.
Features
Async support: the dialect now implements SQLAlchemy's asyncio extension. Use create_async_engine() with the same snowflake:// URL as the sync dialect — there is no separate +driver suffix — to obtain AsyncEngine / AsyncSession for non-blocking database access. The async dialect checks for greenlet when the engine is created and raises an ImportError with install instructions.
Add the enable_structured_type_json dialect flag (URL parameter or create_engine(..., enable_structured_type_json=...), on by default). For semi-structured VARIANT, OBJECT and ARRAY columns, reading deserializes the JSON text Snowflake returns into native Python (dict/list/...), and writing native dict/list values serializes them and wraps them in PARSE_JSON — rendered as INSERT ... SELECT (single row and multi-row UNION ALL) since Snowflake rejects functions in a VALUES clause, and as SET col = PARSE_JSON(...) for UPDATE. Typed structured columns (MAP(...), ARRAY(<type>), OBJECT(...) with fields) are covered too: writes render CAST(PARSE_JSON(...) AS <declared type>) and reads return dict/list. Element values are not converted to the declared types: they come back as JSON decodes them (for example MAP(NUMBER, ...) keys stay strings and DATE elements stay ISO strings). The engine's json_serializer/json_deserializer are used when provided (SNOW-1820647 / GH #107).
Support UUID and DECFLOAT values inside VARIANT and structured types. Structured type compilers now compile their inner types against the Snowflake dialect, so ARRAY/MAP/OBJECT element, key and field types render consistently with standalone columns. The semi-structured JSON write path serializes decimal.Decimal as a JSON string (preserving all 38 DECFLOAT digits, which a JSON number would truncate); reading one back requires an intermediate ::VARCHAR cast, since Snowflake rejects a direct TEXT -> DECFLOAT cast on PARSE_JSON output (SNOW-4081644).
Add the opt-in enable_native_uuid dialect flag (URL parameter or create_engine(..., enable_native_uuid=True)), which renders SQLAlchemy's generic Uuid and Mapped[uuid.UUID] as native Snowflake UUID instead of CHAR(32) and allows uuid.UUID inside semi-structured payloads. uuid.UUID values are bound as strings and reads honour as_uuid (True returns uuid.UUID, False returns str). Off by default, since enabling it changes the DDL of existing Uuid columns (SNOW-4081644).
Add strict host-name mode (strict_host_names / SNOWFLAKE_SQLALCHEMY_STRICT_HOST_NAMES): the dialect no longer infers or appends domains to host names and forwards connection parameters, including connection_name profiles, to the connector unvalidated. Explicit stage endpoints are supported (SNOW-4248737).
Add COLLATE support: expression-level Column.collate('<spec>') and column-type collation (String(collation='<spec>')) now render the collation as a single-quoted Snowflake string literal (e.g. COLLATE 'en-ci') instead of an invalid double-quoted identifier. Enables the SQLAlchemy suite CollateTest (SNOW-629086).
Add subscript access for semi-structured columns: col["key"] and col[index] on VARIANT, OBJECT, ARRAY and MAP columns now compile to Snowflake's native bracket accessor (e.g. col['key'], col[0]), including nested chains such as col["a"]["b"]. See Reading keys and elements with subscript access (SNOW-1820647 / GH #546).
Add support for Snowflake's multi-table INSERT ALL / INSERT FIRST statements via the new InsertMulti custom command, including conditional (when/else_), OVERWRITE, and per-target column/value mapping (SNOW-782235 / GH #403).
Add support for row access policies on SnowflakeTable via the row_access_policy parameter; the policy name accepts a string (optionally fully qualified, e.g. 'db.schema.policy') or a snowflake.sqlalchemy.FQN. See Row Access Policy Support (SNOW-884382 / GH #432).
Add opt-in snowflake_rely=True to render RELY on PRIMARY KEY / FOREIGN KEY / UNIQUE constraints so the optimizer can trust them for query rewrites such as join elimination (SNOW-1023317 / GH #463).
Support json_serializer and json_deserializer parameters in create_engine, matching the built-in SQLAlchemy dialects (SNOW-889293 / GH #433).
Support asdecimal parameter on _CUSTOM_DECIMAL type to allow opt-in conversion of numeric results to float instead of Decimal; default remains Decimal for backward compatibility (SNOW-728151 / GH #373).
Add a python_type property to Snowflake custom types (VARIANT, OBJECT, MAP, ARRAY, VECTOR, TIMESTAMP_*, GEOGRAPHY, GEOMETRY, DECFLOAT) for SQLAlchemy compatibility (SNOW-1866493 / GH #562).
Enhance JSONFormatter with the full Snowflake TYPE=JSON option set (date_format, time_format, timestamp_format, binary_format, trim_space, null_if, enable_octal, allow_duplicate, strip_outer_array, strip_null_values, replace_invalid_characters, ignore_utf8_errors, skip_byte_order_mark) (SNOW-589946).
Add if_not_exists and comment options to CreateFileFormat for closer parity with Snowflake's CREATE FILE FORMAT (SNOW-589962 / GH #291).
The dialect now sets enable_server_session_keep_alive_auto_detection=True explicitly on new connections. This pins the connector's present default so behaviour stays stable when that default changes, and silences the connector's FutureWarning. A value supplied via the URL or connect_args still takes precedence.
Fixes
Improve stage reference handling in compiled COPY INTO and staged-file SQL (CWE-89): ExternalStage paths, including sub-paths created with from_parent_stage, are rendered safely with escaping when quoting is required. Ordinary bare-path values retain their existing rendering (SNOW-4134196).
Fix NULL_IF backslash breakout in COPY FILE_FORMAT options (SNOW-4134195).
Fix repr() of CopyIntoStorage unloading into an ExternalStage or cloud location (AWSBucket, AzureContainer, GCSBucket), which raised UnsupportedCompilationError; the target now uses its own secret-masking repr().
Fix hybrid table index reflection: normalize reflected index names so unquoted identifiers round-trip in SQLAlchemy's lowercase convention (quoted/case-sensitive names preserved), preserve the caller's original schema (including None) in get_multi_indexes result keys so indexes attach when reflecting under the default schema, and return [] instead of [''] for empty INCLUDE columns (GH #755, fixes GH #754).
Fix get_multi_indexes() raising TypeError when filter_names is None (e.g. Inspector.get_multi_indexes(schema=...)) and the schema contains Hybrid Tables; it now returns indexes for all Hybrid Tables in the schema.
Fix reflection failing on schemas/databases with more than 10,000 objects by paginating the underlying object-listing SHOW commands (get_table_names, get_view_names, get_temp_table_names, get_schema_names, get_sequence_names) with LIMIT ... FROM ... (SNOW-796954 / GH #406).
Fix PrivateLink/regional account parsing where org-style accounts with dashes (e.g., gnamsrm-vi65876.privatelink) were incorrectly truncated at the dash, breaking JWT/key-pair authentication. Account derivation now delegates to the Snowflake Connector's own parse_account, so all host notations (plain, regional, regional/regionless PrivateLink, and .global) match the driver (SNOW-730644, also fixes SNOW-1209099).
Fix URL failing to encode [, ], ? and # in passwords, which produced connect strings that could not be parsed (SNOW-828206 / GH #415).
Fix invalid LIMIT -1 SQL generated for a SELECT with OFFSET but no LIMIT; the SnowflakeCompiler now emits LIMIT NULL OFFSET ... as Snowflake requires (NO-SNOW / GH #745).
Detect expired-session/token and closed-connection errors, including error code 390195 (authentication token expired, variant), as disconnects so connection_invalidated is set and pooled connections are recycled by pool_pre_ping (SNOW-669163 / GH #348, SNOW-3882904 / GH #702).