SQLAlchemy release notes for 2026

This article contains the release notes for the SQLAlchemy, including the following when applicable:

  • Behavior changes
  • New features
  • Customer-facing bug fixes

Snowflake uses semantic versioning for SQLAlchemy updates.

See Using the Snowflake SQLAlchemy toolkit with the Python Connector for documentation.

Version 1.10.0 (May 20, 2026)

New features and updates

  • Added Google Cloud Storage (GCS) bucket support for the CopyIntoStorage expression.
  • Added SnowflakeBase, snowflake_declarative_base(), and SnowflakeSession to enable efficient bulk inserts for ORM models with nullable optional columns. Using SnowflakeBase (or snowflake_declarative_base()) along with SnowflakeSession batches all objects into a single executemany INSERT, instead of producing one INSERT statement per distinct set of non-None column keys.
  • Added the case_sensitive_identifiers opt-in engine flag (constructor keyword argument or ?case_sensitive_identifiers=True URL parameter) that governs case-sensitive identifier handling. The default value is False, so existing applications aren’t affected unless they explicitly opt in. When you opt in:
    • All-uppercase reserved-word identifiers (for example, TABLE) are normalized to quoted_name("table", True) to prevent key-lookup mismatches between creation and reflection.
    • Mixed-case reflected identifiers (for example, MyCol from a quoted Snowflake column) are returned as quoted_name("MyCol", True) instead of a plain str.
    • Schema strings with inner double quotes (for example, '"myschema"' or '"mydb"."myschema"') have their extracted parts marked quote=True, preserving case sensitivity in emitted SQL.
  • Added the create_snowflake_engine(url, schema=..., case_sensitive_schema=True) helper. The helper URL-encodes case-sensitive schema names using %22 so the Snowflake connector receives the literal double-quoted form. Schema names are now always URL-encoded regardless of case_sensitive_schema, preventing special characters (?, #, /) from being misinterpreted as URL delimiters.
  • Added snowflake.sqlalchemy.alembic_util.render_item, a drop-in Alembic render_item hook for env.py that serializes quoted_name columns with quote=True correctly in generated migration files, preventing Alembic autogenerate from silently converting case-sensitive column names to uppercase.
  • Added support for cross-database schema reflection using schema='database.schema' notation, so you can reflect and join tables from different databases in a single session without using raw SQL.
  • Added composite key ordering.
  • Added a SnowflakeWarning that’s emitted at DDL compile time when Identity() is used on a primary key column. The warning alerts you that ORM flush operations will raise a FlushError. Use Sequence() instead. The warning is emitted once per unique (table, column) pair per Python process.
  • Mapped the Snowflake UUID column type to sqlalchemy.sql.sqltypes.UUID for reflection on SQLAlchemy 2.x. The column was previously reflected as NullType. Values are returned as plain strings (as_uuid=False) rather than uuid.UUID instances. There’s no change on SQLAlchemy 1.4, where the generic UUID type doesn’t exist.
  • Optimized reflection performance:
    • Added get_multi_columns, get_multi_pk_constraint, get_multi_unique_constraints, and get_multi_foreign_keys for SQLAlchemy 2.x bulk reflection. Each method issues one schema-wide query per reflection pass instead of one query per table.
    • On SQLAlchemy 2.x, get_pk_constraint, get_unique_constraints, get_foreign_keys, and get_indexes now automatically use per-table SHOW … IN TABLE queries without any opt-in flag. These methods previously always issued SHOW … IN SCHEMA, even for single-table Inspector calls, which caused approximately 20-second delays on schemas with thousands of tables.
    • The cache_column_metadata=True opt-in now enables per-table SHOW … IN TABLE queries for get_pk_constraint, get_unique_constraints, get_foreign_keys, and get_indexes on SQLAlchemy 1.4.
    • SQLAlchemy 2.x get_columns now uses DESC TABLE directly so that temporary tables and dynamic tables are reflected correctly.

Bug fixes

  • Fixed with_loader_criteria silently dropping filters on non-Snowflake dialects. Importing snowflake-sqlalchemy previously altered SQLAlchemy’s ORM compilation for every dialect in the process, causing loader-criteria filters to be omitted inside sealed subqueries when using PostgreSQL, MySQL, SQLite, and others. Snowflake dialect behavior is unchanged; the BCR-1057 lateral-join workaround is now scoped to Snowflake connections only.
  • Scoped referred_schema=None normalization in foreign key reflection to the default schema only. When reflecting the default schema, same-schema foreign keys (default to default) keep the established SQLAlchemy convention of referred_schema=None. When reflecting a non-default schema, every foreign key keeps its actual referred_schema. This change prevents Alembic autogenerate mismatches that previously occurred for cross-schema foreign keys that targeted the default schema.
  • Fixed case-sensitive identifier handling:
    • _split_schema_by_dot now correctly parses SQL-escaped double quotes ("") inside quoted schema and database identifiers (for example, "my""schema" becomes my"schema), preventing silent truncation of identifiers containing literal quote characters.
    • denormalize_column_name now correctly double-quotes quoted_name("mycol", True) columns in CLUSTER BY clauses, instead of silently dropping the case-sensitivity signal.
    • _has_object (used by has_table and has_sequence) now applies denormalize_name to both the schema and object name before building the DESC SQL, making it consistent with all other reflection methods.
    • create_connect_args now atomically replaces the name_utils instance when the URL’s case_sensitive_identifiers value differs from the current dialect state, so concurrent readers on other threads never observe a torn update.
  • Restored backward-compatible SQL generation for true division (/) when div_is_floordiv=True. The Snowflake compiler now correctly delegates to the SQLAlchemy base implementation, emitting CAST(col AS NUMERIC) for integer operands as it did before.
  • Fixed foreign key referred_schema resolution so reflected foreign keys keep their actual schema unless the target is in the connection’s default schema. Foreign keys whose target shared the reflected non-default schema were previously reported with referred_schema=None, which caused SQLAlchemy’s _reflect_fk to autoload from the wrong schema and raise NoReferencedColumnError during Alembic autogenerate.
  • Replaced SHOW TABLES LIKE with SHOW INDEXES IN TABLE for single-table index reflection, eliminating SQL LIKE wildcard false positives and case-sensitivity bugs.

Version 1.9.0 (March 04, 2026)

New features and updates

  • Added support for DECFLOAT and VECTOR data types.
  • Added support for server_version_info support.
  • Added support for ILIKE in queries.
  • Introduced a shared helper for fully-qualified schema name resolution, replacing inconsistent ad-hoc patterns across reflection methods.
  • Refactored column reflection internals into dedicated helpers to reduce complexity without changing behavior.
  • Added pytest-xdist parallel test support via per-worker schema provisioning hooks.
  • Bumped pandas lower bound in the sa14 test environment from <2.1 to >=2.1.1,<2.2 to ensure pre-built wheels are available for Python 3.12.
  • Added support for timezone in timestamp and datetime types.

Bug fixes

  • Fixed SYSDATE() rendering.
  • Fixed and improved schema reflection.
  • Fixed a crash issue when reflecting without specifying a schema, caused by None arguments in internal schema resolution.
  • Fixed a crash issue when SHOW TABLES returns empty string table names, causing IndexError during reflection.
  • Fixed incomplete identity column reflection metadata. This column now includes all fields required by SQLAlchemy 2.0+ (always, cycle, order, and so on).
  • Fixed SQLAlchemy version parsing.