github snowflakedb/snowflake-sqlalchemy v2.0.0
Release

5 hours ago
  • v2.0.0 (Oct 19, 2026)
    • Breaking changes
      • 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).
    • Documentation
      • Add a migration guide from 1.x to 2.x and a contributing guide for local development setup, Hatch usage, and running tests.
      • Document SSO/Okta authentication via the authenticator connect_args parameter (SNOW-715550).
      • Document how to enable connector bulk array binding for large executemany inserts via qmark paramstyle (SNOW-710474).
      • Document using a SQLAlchemy VALUES source with MergeInto (no staging table needed) (SNOW-889678 / GH #435).
      • Document the raw-connector workaround for PUT with an in-memory file_stream (BytesIO) (SNOW-645168 / #337).
      • Document CLUSTER BY usage in ORM/declarative models via SnowflakeTable (SNOW-638838 / #313).
      • Document AUTOINCREMENT and IDENTITY column usage, including Sequence vs Identity trade-offs for Hybrid tables (SNOW-1232362).
      • Document that Snowflake lacks IS TRUE/IS FALSE; use col == true()/col == false() (#680).

Don't miss a new snowflake-sqlalchemy release

NewReleases is sending notifications on new releases.