Changelog#

All notable SQLSpec changes are summarized here. Entries are grouped by release and focus on user-visible behavior, public API changes, compatibility notes, and important operational fixes.

Recent Updates#

Unreleased#

Added:

  • BigQuery supports native query resource controls, explicit STRUCT parameters, typed empty arrays, and configurable Storage Write stream modes while retaining the atomic PENDING default. (#812)

  • The mssql-python adapter can load Arrow streams with native BulkCopy options. Columns map by name by default, and overwrite still uses DELETE. (#819)

  • Pymssql connection types include native encryption settings. (#819)

  • SQLite and aiosqlite can register custom window functions on Python 3.11 and later when the SQLite runtime supports them. Choose a transaction lock mode or set the batch size for Arrow imports. Defaults stay the same. (#820)

  • Spanner forwards native query, request, and directed-read options through existing execution and session APIs. last_statement=True marks the final DML statement; it does not commit the transaction. Opt-in Batch Write accepts read sessions for Arrow imports without overwrite. Overwrite uses a transaction for both the delete and replacement mutations. (#814)

  • Cloud Spanner and Spangres SQLGlot dialects isolate custom SpannerParser, SpangresParser, SpannerGenerator, and SpangresGenerator subclasses and expand AST and transpilation support for INTERLEAVE IN PARENT with ON DELETE, TTL, ROW DELETION POLICY, SEARCH / SCORE / SNIPPETS / TOKENLIST, VECTOR_INDEX with OPTIONS, GRAPH_TABLE, FLOAT32, SAFE_CAST / TRY_CAST, SPANNER.ML_PREDICT_ROW, Spanner sequences, and spangres DDL/DML transpilation and catalog query packs. (#813)

  • Arrow ODBC runs execute_many() one row at a time. It reports an unknown row count since the native driver does not return the number of changed rows. (#818)

  • ADBC FlightSQL adds options for TLS/mTLS, RPC timeouts, message size, cookies and headers. Values set in native db_kwargs take precedence. (#818)

  • PostgreSQL adapters expose native asyncpg custom codecs and per-query timeouts, psycopg null pools and JSON codecs, supported CockroachDB startup settings, and psqlpy dense-vector conversion. PgBouncer compatibility mode avoids explicit prepared stack statements without weakening transaction cleanup. Null pools preserve concurrency limits, and timeout forwarding retains explicit zero values. (#822)

  • Added an IBM Db2 adapter for Db2 LUW 11.5 and later with sync (Db2SyncConfig) and async (Db2AsyncConfig) configurations built on ibm_db. It includes connection pooling, catalog reflection, migrations, and Litestar session, events queue, and Google ADK stores. See Db2. (#811)

  • Added a db2 SQL dialect. It renders Db2 paging, special registers, labeled durations, isolation and lock clauses, and Db2 data types, and translates builder row locks to Db2 lock clauses. (#811)

  • The arrow-odbc adapter supports IBM Db2 through the IBM CLI/ODBC driver, including Db2 connection keywords, transactions, lowercase result columns, and its Litestar session, events queue, and Google ADK stores. (#811)

Changed:

  • Extracted shared MySQL driver primitives into sqlspec/adapters/mysql_common.py across aiomysql, asyncmy, pymysql, and mysqlconnector, and reorganized dialect data-dictionary definitions into dialect-scoped sqlspec/data_dictionary/dialects/<dialect>/ packages with shared MySQL data-dictionary base classes. (#821)

  • Simplified OracleDB and Db2 typing and helper boundaries: encapsulated OraclePipelineDriver protocol typing, exported Db2ConnectionParams and Db2DriverFeatures, removed redundant if not TYPE_CHECKING: typing blocks in oracledb and db2, internalized adapter core helpers, and made Oracle JSON, UUID, and vector type-handler registration idempotent. (#817, #823)

Fixed:

  • MySQL Connector can create async pools again. Db2 exposes the installed driver's connection and cursor types to apps.

  • DuckDB returns UUID objects for UUID columns on the first query, on cache hits, and in row streams. Disabling UUID input conversion does not change result types. Text columns still return strings.

  • Schema checks can read table DDL inside SQL blocks with dialect-specific quotes. They also handle Oracle blocks whose trailing text is not supported by the parser.

  • Spanner binds Decimal values as NUMERIC and boolean arrays as BOOL. Typed null dictionaries use JSON, and JSON null results stay None. (#814)

  • ADBC ADK stores reuse cached PostgreSQL placeholder conversion and preserve question marks in quoted identifiers, literals, and comments. (#818)

  • Arrow ODBC pagination reuses compiled placeholder positions instead of parsing SQL again. ADBC keeps bound values in its ADK store queries. DuckDB Arrow loads keep sparse dictionary fields and quote table names. (#818)

  • T-SQL event store CREATE INDEX OBJECT_ID guards across arrow_odbc, pymssql, and mssql_python use the configured table and generated index names directly so arrow_odbc index checks no longer capture column list text inside the table name. (#816, #819, #828, #829)

  • Arrow read failures in the mssql-python adapter use SQLSpec error types. (#819)

  • SQLite pools replace lost in-memory connections. Arrow imports roll back writes on failure or cancellation when the adapter owns the transaction. (#820)

  • Builder results keep CTE trees independent, and column pruning no longer exposes its cached expression to mutation. SQL generation avoids redundant copies of temporary trees while preserving caller and cache ownership. (#829)

  • SQL Server migration drivers retain the previous default schema if restoring it fails, so cleanup can be retried. The migration guide clarifies that this setting belongs to the database user rather than one connection. (#829)

  • Asyncpg stack telemetry reports sequential prepared execution rather than native pipelining. Each statement still returns its own result. (#822, #829)

  • Fixture files keep JSON strings such as "true" and "[1]" as strings during export and load round-trips across DuckDB, SQLite, ADBC, and MySQL (#824), normalize bare string conflict_keys values and ignore tables absent from subset loads (#825), and support sparse row keys, ignore_unknown_columns, and exclude_update_columns for upserts (#826). Column filtering respects case. See Testing. (#827, #829)

  • Quoted and schema-qualified version_table identifiers are preserved across migration trackers and DDL builders (CreateTable, DropTable, AlterTable). Trackers keep unquoted catalog lookup names in version_table_name and version_table_schema while retaining exact identifier quotes in DDL and tracking queries, including names with spaces and mixed-case Oracle identifiers. (#815, #828, #829)

  • MySQL pools release connections when setup fails. They discard connections that fail to roll back. MySQL Connector keeps native async pooling on Connector 9.4 and later, plus direct connections on older versions. Asyncmy retains native LOAD DATA LOCAL INFILE support. (#821, #829)

  • Oracle keeps Thick-mode options for sync pools. Async pools reject Thick mode before they open. Pool shutdown preserves native checks for borrowed connections. Custom handlers still convert LOBs, and JSON handlers preserve the user's callbacks. (#817, #829)

  • Db2 pools clean up after failed or cancelled setup. Batch results keep an unknown row count when the driver cannot report one. String searches keep their start position and requested occurrence. (#811, #829)

  • Spanner schema queries no longer require a table name. SQL output keeps JOIN hints and plain comments. Sequence statements keep qualified names and IF NOT EXISTS guards. Cached row converters refresh when the configured JSON deserializer changes. (#813, #829)

  • Psycopg reads COPY files in chunks, not all at once. ADK stores use RETURNING to cut round trips. Psqlpy closes a connection if setup fails. (#822)

  • Builder upserts emit MERGE for the db2 dialect. (#811)

  • The arrow-odbc adapter detects the SQL dialect from the ODBC driver name only, so database, host, or user names no longer select the wrong dialect. (#811)

v0.64.0 - Startup performance, connection normalization, and adapter lifecycle hardening#

Added:

  • Added a cursor pagination guide with a tested example. It shows how to move through pages in both directions and sign page tokens. Examples cover service paginate() and direct driver select() with CursorFilter.build_page(), plus Litestar and FastAPI filter setup.

  • FastAPI filter dependencies support cursor pagination, including signed tokens, dynamic sorting, page-size bounds, and HTTP 422 cursor errors.

  • Litestar filter dependencies support cursor pagination with signed tokens, bounded page sizes, dynamic sorting, and client validation errors.

  • Pass a cursor or offset filter to service paginate() to choose the page type. Both sync and async calls return typed rows and can use your session. Use paginate_limit_offset() or paginate_cursor() to select a mode explicitly and retain a precise return type with dynamic filter lists. Litestar routes can return either page type with typed items and an OpenAPI union response. Pagination[T] aliases both page types for concise return annotations.

  • Use cursor filters with sync and async driver select() calls. CursorFilter.build_page() builds a page from the rows. Tests cover quoted BigQuery table names and the SQL Server dialect alias.

  • Added type-preserving pagination cursor tokens with optional HMAC signing and a generic CursorPagination response container. Malformed cursor inputs raise InvalidCursorError consistently in Python and compiled installations.

  • Added CursorKey and CursorFilter for bidirectional keyset pagination, including explicit NULL placement, composite sort keys, and page cursor creation. Use a field name to sort from low to high. Use pairs to set each field's sort order, or CursorKey for more control.

  • Defer public exports, query builders, and migration helpers in pure-Python installations on first access to accelerate cold import performance. (#798)

  • Added parse_odbc_connection_string() in sqlspec.utils.config_tools to tokenize ODBC connection strings with brace escaping per the MS-ODBCSTR specification. (#789)

  • Added parse_mysql_dsn() in sqlspec.utils.config_tools to parse URL and semicolon-delimited key-value MySQL DSN strings into keyword arguments. (#791, #796)

  • Added DSN connection URL support across all MySQL adapters: aiomysql, asyncmy, mysqlconnector, and pymysql. (#791, #796)

  • The arrow-odbc adapter accepts individual ODBC connection fields alongside a connection string, and the asyncmy adapter accepts stmt_cache_size. (#786, #790)

  • Bulk ingestion uses each driver's native Arrow path where one exists. (#786)

Changed:

  • Examples, tests, and documentation tooling use explicit forward references; lint now enforces the future-annotations import ban throughout the repository.

  • Generated filter dependencies reject pageSize values above pagination_max_size (default 1000); set pagination_max_size in FilterConfig to change the limit.

  • SQLSpec-built ordering (OrderByFilter, SQL.order_by, builder order_by, Column.asc()/desc(), and window ordering) leaves NULL placement to the database unless requested. It no longer adds implicit NULLS FIRST/NULLS LAST clauses or a CASE sort key. On PostgreSQL, Oracle, Snowflake, and Redshift, affected ascending items move NULL rows from first to last; descending items move them from last to first. On DuckDB, ClickHouse, and Trino, affected ascending items move NULL rows to last. On MySQL, SQL Server, SQLite, BigQuery, and Spanner, Column.asc() and expression-based window ordering move NULL rows to first. Request placement with OrderByFilter(nulls=...), Column.asc(nulls=...) / Column.desc(nulls=...), or a string such as "id DESC NULLS LAST".

  • Pure-Python installations defer unused query builders and migration commands. Compiled wheels retain eager exports to preserve concurrent access after package initialization. Public import paths, typing, and mypyc compilation support stay the same. (#798)

  • Configs build migration commands and custom trackers on first use. Built-in checks still run when you create a config. Call config.get_migration_commands() at startup to check custom tracker setup and find extension migrations early; see Initialization and startup checks. (#798)

  • Query optimizer rules load only when queries require optimization, and optional asyncpg serializer registration is deferred until first use. (#798)

  • Normalized connection parameter aliases across all database adapters:

    • PostgreSQL & CockroachDB (asyncpg, cockroach_asyncpg, cockroach_psycopg, psqlpy, psycopg): Normalized conninfo, dsn, url, and connection_string; database, db, and dbname; user and username. Redundant driver-incompatible keys are popped before passing to driver constructors. (#792)

    • SQLite, DuckDB & AioSQLite: Normalized path, db, and file aliases to database, and prevented aliases from leaking into driver connect() kwargs. (#793)

    • OracleDB & PyMSSQL: Normalized url and connection_string to dsn, and username to user for OracleDB; mapped host to server, db to database, and username to user for PyMSSQL. (#794)

    • ADBC: Normalized url, dsn, and connection_string aliases to uri. Normalized embedded database path aliases (database, db, path, file) and driver/dialect routing. (#795)

    • Google Cloud Platform (BigQuery & Spanner): Normalized project_id to project, and dataset/database/db to dataset_id for BigQuery; normalized project_id to project, instance to instance_id, and database/db to database_id for Spanner. Reordered configuration builder definitions before config classes. (#797)

    • MySQL Adapters (aiomysql, asyncmy, mysqlconnector, pymysql): Normalized username to user and db to database. (#791, #796)

  • MSSQL and Arrow ODBC discrete connection configuration fields can override connection string options while preserving remaining connection string attributes. (#789, #790)

  • Use the types in each adapter's Litestar and Events package to tune its tables. Shared settings stay in sqlspec.config. The extension_config layout stays the same. ADK vector, BM25, and ScaNN keys move to the asyncpg and psycopg ADK types. BigQuery uses a boolean for partitioning; Oracle uses a mapping. (#786)

  • Extension stores and event channels reject keys they cannot use. Remove the unused run_migrations key from extension settings. Run migrations with the commands and migration_config. The Events listener_queue_capacity key is for asyncpg and psycopg. (#786)

  • create_connection() returns a connection the caller owns on the adapters that previously handed back a pooled one. It consumes no pool slot and must be closed by the caller. (#786)

  • The MySQL adapters connect with utf8mb4 unless a charset is configured, matching the character set their bulk-load path already declares. (#786)

  • CockroachDB reports that it does not support transactional DDL. A schema migration runs without a wrapping transaction unless it carries its own transactional directive. (#786)

  • DuckDB extension_flags are applied as database startup settings, so an unrecognized flag is reported when the database opens instead of being ignored. (#786)

  • Streaming row sources take an error flag when they close, and mapping rows to dictionaries reports a missing column description rather than returning no rows. (#786)

  • sqlspec.exceptions.TransactionRetryError and sqlspec.utils.type_guards.has_value_attribute are removed, along with build_insert_statement, coerce_records_for_execute_many, and encode_records_for_binary_copy from the psqlpy adapter. Serialization failures are reported as SerializationConflictError. (#786)

Fixed:

  • Parameter-only statement copies preserve the shared parameter-validator cache and its configured size when rebinding values.

  • Adapters, services, and builders import shared driver, ordering, and parameter helpers through their owning packages instead of private implementation modules.

  • Cursor provider dependency caches distinguish byte secrets from their text representation, preserving each endpoint's configured signing key.

  • Framework dependency caches preserve configured list order so the first sort field and cursor key sequence retain their declared meaning.

  • Explicit NULL placement remains explicit for PostgreSQL-compatible adapters, including CockroachDB, whose default NULL ordering differs from PostgreSQL.

  • Arrow ODBC renders SQL Server TOP page-size controls as validated integers while retaining bound data parameters, including queries with CTEs. Native select_to_arrow applies the same SQL Server pagination controls.

  • SQL.order_by("id", desc=True) now sorts descending, and Select.order_by("id", desc=True) no longer emits a doubled direction.

  • limit, offset, and paginate on set operations render valid SQL Server pagination while retaining the requested result ordering.

  • Statement filters and SQL.where/SQL.order_by apply to the whole result of UNION, INTERSECT, and EXCEPT queries, preserving CTEs and result ordering. Pagination filters produce valid set-operation SQL.

  • Preserve parameter alignment when repeated BigQuery queries inline NULL values, including copied statements and transitions between NULL and non-NULL values.

  • Psycopg percent escaping preserves existing %% pairs and modulo expressions when parameters are bound, including repeated preparation, and retains returned rows when legacy modulo syntax cannot be classified by the SQL parser.

  • Missing positional bindings no longer consume values reserved for named placeholders, including names that collide with generated parameter aliases and script literals.

  • Repeated and reordered numeric placeholders bind by their written indexes when converted to another placeholder style; native numeric mappings retain written index order on the first call and cache hits.

  • Sequences for named placeholders and mappings for positional placeholders bind consistently on the first execution and cache hits, including repeated names.

  • Ambiguous mixes of numeric and ordinal placeholders reject sequence payloads instead of silently binding values to the wrong slots.

  • PostgreSQL ?? escapes become ? operators, including after filters modify the statement; output transformers receive the driver's execution placeholder style.

  • Spanner execute_many converts tuple rows and mixed placeholder mappings before calling the driver, preserving bindings on cache hits.

  • Filters supplied to the SQL constructor are applied once before call-site filters, including when statements are reused.

  • Statements combining positional and named values now bind each value to its own placeholder, including filters and where_* helpers.

  • PostgreSQL JSONB existence operators followed by literals or bound parameters are recognized without consuming a parameter slot.

  • DuckDB execute_many preserves INSERT expressions, conflict clauses, and column order and defaults by restricting bulk loading to plain VALUES inserts.

  • Psycopg preserves literal percent characters alongside bound parameters, including cached statements, batch execution, streams, and pipelines.

  • Parameters supplied to execute_script use dialect-correct escaped literals. A placeholder without a value now raises instead of rendering as NULL.

  • The MySQL adapters (aiomysql, asyncmy, mysqlconnector, pymysql) now pass statement parameters to the driver for binding. Cross-adapter safety checks cover quotes, backslashes, and placeholder-like text supplied as bound values.

  • Statement modifiers on empty or unparsable SQL raise SQLParsingError instead of leaking a sqlglot ParseError, including during concurrent resets.

  • Tests, including Litestar connection-provider tests, close aiosqlite pools before their event loops shut down, and unhandled worker-thread exceptions now fail the test suite.

  • The SQLite and aiosqlite pools retry enabling WAL mode when several connections first open a new database at the same time; previously this could fail with database is locked.

  • Close async example connection pools before their event loops shut down.

  • Correct Litestar filter query parameter titles and pagination schema documentation.

  • Driver exception handling uses native error classes through adapter facades. SQL Server and Arrow ODBC no longer fall back to catching every exception when a driver error export is missing. (#786)

  • Oracle AQ visibility accepts DEQ_IMMEDIATE and DEQ_ON_COMMIT names. The previously advertised AQMSG_* names do not exist in python-oracledb. Omitting visibility continues to use the driver's default. (#786)

  • DuckDB reports failed commits to the caller. It also closes the file-backed connection when a commit fails. (#786)

  • PostgreSQL and CockroachDB close new connections if a setup hook fails or the task is cancelled. This prevents a leak before the caller can take ownership. (#786)

  • A DuckDB session that exits with an exception no longer discards an in-memory database, and opening a standalone connection no longer resets the storage setup already prepared for the thread. (#786)

  • CockroachDB retries a transaction only for a genuine serialization conflict, and an error that escapes a failed rollback keeps the cause that identifies it. (#786)

  • Oracle returns the same value types whether or not a statement was already cached. (#786)

  • Spanner declares a parameter type for UUID values. (#786)

  • ODBC connection values that are already quoted are passed through unchanged, and an error code is read only from the driver's own diagnostic field. (#786)

Requirements:

  • The duckdb extra installs pyarrow. Minimum versions are raised for oracledb (3.4), psqlpy (0.12.1), and mssql-python (1.13). (#786)

v0.63.1 - Slotted service subclass compatibility#

Fixed:

  • Keep SQLSpecAsyncService and SQLSpecSyncService as ordinary slotted Python classes in compiled wheels. Application subclasses preserve generic typing and __slots__ without downstream slotscheck exclusions. Query execution and transaction management remain mypyc-compiled in a private runtime module.

Changed:

  • Expand compiled-wheel smoke checks to cover multi-level, slotted subclasses of SQLSpecAsyncService and SQLSpecSyncService, including inherited queries, transaction contexts, overridden session acquisition, and queries inside a caller's exception handler.

  • Update slotscheck and the Codecov action pin.

v0.63.0 - Transactions, table fixtures, SQL fragments, storage, and kwargs parameter binding#

Added:

  • Native AlloyDB and PostgreSQL BM25 full-text search support via the pg_textsearch extension. Includes the PGTextSearch dialect registered under sqlglot.dialects, custom AST operator support for the BM25 relevance ranking operator (<@>), automatic extension detection, and enable_pg_textsearch configuration across all PostgreSQL adapters (AsyncPG, Psycopg, ADBC, and PsqlPy).

  • Public is_postgres_extension_active() helper in sqlspec.core.config_runtime and adapter modules, with active_extensions capability tracking on runtime driver features.

  • Exposed pg_textsearch_available property across AsyncpgConfig, PsycopgSyncConfig, PsycopgAsyncConfig, AdbcConfig, and PsqlpyConfig. (#782)

  • Public table-queue primitives extracted to sqlspec.extensions.events.primitives and exported from sqlspec.extensions.events: lock_clause(), row_limit_clause(), select_limit_prefix(), and claim_verified(). SyncTableEventQueue and AsyncTableEventQueue delegate to them for dialect-aware row-limiting, row-locking, and claim lease verification. (#776)

  • Added supports_reliable_rowcount capability flag to configurations, indicating whether rows_affected can be trusted without re-querying (defaults to True; set to False for ADBC and arrow-odbc). (#773)

  • SQL Server extension stores and documentation alignment: added MssqlPythonADKMemoryStore to complete ADK store parity across SQL Server adapters, added an end-to-end SQL Server recipes guide (SQL Server), and registered mssql_python and pymssql across shared integration test suites for Google ADK, durable event queues, and Litestar session stores. ADK migration 0002 provisions missing mssql-python memory tables and lookup indexes for existing installations; downgrading that additive repair preserves memory data. mssql-python ADK JSON storage defaults to driver-supported NVARCHAR(MAX); the explicit native_json=True override remains available. (#781)

  • Expose TypeCoercionCapabilities on all adapter configuration classes as a type_coercion_capabilities ClassVar, declaring each adapter's datetime binding mode (native, iso_text, or naive_utc), timestamp precision (microsecond, millisecond, or second), JSON column decoding behavior, and UUID binding mode (native or text). (#780)

  • Sync and async drivers provide transaction(), a context manager that begins a transaction, commits when the block succeeds, and rolls back and re-raises when it fails; a failed commit is followed by a rollback attempt. A block entered while the connection already has an open transaction joins it and ends it on exit. A block entered inside another transaction() or service begin_transaction() block on the same driver uses a savepoint instead of committing. Services allow nested begin_transaction() blocks the same way: an inner block runs in a savepoint on the same session, so a failure such as a unique violation undoes only the inner work and the outer block can still commit. Adapters without savepoint support (DuckDB, BigQuery, Spanner, and ADBC to DuckDB/BigQuery/Snowflake) raise ImproperConfigurationError when a block is nested. Services also expose public service.config, the configuration they were built from (or None when built from a session), enabling construction of collaborating services without accessing private attributes. See Driver and Service Layer Pattern. (#765)

  • Table data fixtures in sqlspec.utils.fixtures load and export table datasets with load_table_fixtures_sync / load_table_fixtures_async and export_table_fixtures_sync / export_table_fixtures_async. Each table uses a single <table>.json or <table>.jsonl file (with optional gzip compression). Loading supports table subsets, explicit dependency ordering, batched inserts, upserts on conflict_keys (ON CONFLICT ... DO UPDATE on PostgreSQL-family, SQLite, and DuckDB; ON DUPLICATE KEY UPDATE on MySQL), automatic type coercion against live data dictionary metadata, and PostgreSQL identity/serial sequence resynchronization (resync_sequences=True). Generated columns are excluded from export and omitted on load. Table and column identifiers are quoted for exact-case and reserved-word compatibility. See Testing. (#766)

  • SQL file loader supports reusable SQL sections via -- fragment: name directives spliced with /* include: name */, and dynamic fill points via /* slot: name */ comments. Slots accept default fallbacks via -- slot: name = default and can be populated at load time via loader.get_sql(name, **slots) or spec.get_sql(name, **slots) using strings, sqlglot expressions, or SQL instances with parameter merging. See Fragments and Slots. (#763)

  • DuckDB transfers object-store data natively without materializing Python Arrow tables. load_from_storage issues INSERT INTO ... SELECT * FROM read_parquet(...) for remote Parquet, and select_to_storage issues COPY (...) TO ... for Parquet and CSV. Requests incompatible with native engine execution fall back transparently to Arrow streaming. (#752)

  • CockroachDB adapters (asyncpg and psycopg) introduce opt-in native storage transfers via enable_native_storage=True in driver features. select_to_storage executes server-side EXPORT INTO, returning generated destination filenames in telemetry, and load_from_storage executes server-side IMPORT INTO for remote Parquet and CSV files with configurable native_storage_csv_options (nullas, nullif, skip). (#753)

  • BigQuery adapter supports direct query exports to cloud object storage. (#746)

  • Data dictionary column metadata provides expanded introspection attributes: is_primary on DuckDB, MySQL, and CockroachDB; identity_generation and sequence_name on PostgreSQL and CockroachDB; column_type, column_key, and extra on MySQL; and is_generated on DuckDB. On PostgreSQL and CockroachDB, get_columns(table=...) without an explicit schema resolves tables through the session search_path instead of defaulting to public.

  • SQLSpecChannelsBackend provides preflight payload budget validation and Prometheus metrics for PostgreSQL NOTIFY envelopes. measure(data) returns the UTF-8 encoded envelope size, fits(data) verifies compliance against notify_budget (derived from MAX_NOTIFY_BYTES), and metrics_snapshot() aggregates channel database metrics with queue depth and dropped message counts. AsyncEventChannel and SyncEventChannel also expose backend_name and metrics_snapshot(). (#756)

  • Litestar plugin registers a default exception handler for IntegrityError and subclasses (e.g. unique constraint violations), returning an HTTP 409 Conflict response with generic detail "Conflict" to prevent internal schema text from leaking. In autocommit mode, this error status automatically triggers a request transaction rollback. (#760)

  • Litestar extension introduces the manage_lifespan configuration option, governing whether the plugin initializes and disposes driver connection pools during application startup and shutdown. Defaults to the inverse of disable_di, allowing external dependency injection containers to reuse SQLSpec pool lifecycles. (#760)

  • Top-level sqlspec package exports identifier generators uuid4, uuid6, uuid7, and nanoid. sqlspec.extensions.litestar exports CorrelationMiddleware and TRACE_CONTEXT_FALLBACK_HEADERS. Reference documentation now includes supported public APIs in sqlspec.utils.text, sqlspec.utils.serializers, sqlspec.utils.correlation, and sqlspec.utils.schema. (#758)

  • Service layer allows constructing instances directly from database configurations via service = Service(config=config), opening short-lived sessions per query. (#745)

  • Storage pipelines provide resolve_destination(), returning a ResolvedStorageTarget(uri, protocol) without requiring an active database session.

  • SQL Server migration runners (mssql_python and pymssql) support default schemas via default_schema in migration configuration, using session-level user schema switching with validation against sys.schemas and automatic schema reset. All migration adapters supporting schema scoping now support per-migration -- schema: <name> directives to override the configured default schema for individual migration scripts. (#770)

  • Data dictionary dialect configurations map integer, bigint, float, and varchar logical types across all supported dialects. get_optimal_type() supports an optional length parameter for bounded types (such as varchar), falling back to unbounded text types when length is omitted. (#777)

  • Database configurations support remove_extension_migrations(), allowing runtime unregistration of extension migrations. The method removes the extension entry from extension_config and migration_config["include_extensions"], rebuilding cached migration commands when found. (#774)

Changed:

  • Query builder validates dialect capabilities in build() and to_statement() instead of silently dropping unsupported clauses. .for_update() and .for_share() raise SQLBuilderError on dialects without these locking clauses (T-SQL, SQLite, DuckDB, BigQuery), and skip_locked=True validates against supports_skip_locked. .on_conflict() automatically transpiles to ON DUPLICATE KEY UPDATE for MySQL and MariaDB (with do_nothing() rewriting to self-assignment), while raising SQLBuilderError suggesting sql.merge() on dialects lacking native upsert support (Oracle, T-SQL, BigQuery). Spanner supports native upserts and plain FOR UPDATE; PostgreSQL-mode upserts validate assignment restrictions. Oracle rejects shared locks, while MariaDB renders LOCK IN SHARE MODE. Query builder build() also normalizes dialect aliases (mssql to tsql, mariadb to mysql, and cockroachdb to postgres). (#778)

  • Decoupled the ParadeDB dialect so it inherits directly from Postgres rather than PGVector, allowing clean independent combinations of vector search and BM25 extensions.

  • Standardized PostgreSQL extension detection across all adapters on a single first-connection probe via build_postgres_extension_probe_names(), removing ad-hoc ADK probe branches. (#782)

  • Docs, examples, and built-in extensions now pass named query values as keyword arguments (execute(sql, a=1, b=2) or execute(sql, **params)). Drivers still accept a dict, list, or tuple as a positional argument. Existing calls need no changes. execute_many still accepts a collection of rows. (#769)

  • The Litestar extension now requires litestar>=2.23.0.

  • Litestar plugin registers correlation and SQLCommenter middleware at the outermost position of the middleware stack, ensuring requests rejected by upstream authentication or guard handlers retain correlation headers in logs and error responses.

  • Litestar plugin automatically deduplicates CorrelationMiddleware when already configured in the application middleware stack, logging diagnostics if configured header settings differ.

  • Unified sqlspec.extensions.litestar.LitestarConfig with sqlspec.config.LitestarConfig for consistent typing and schema export.

  • Parameter pipeline internal execution and placeholder conversion are consolidated to use single-traversal rendering, shared type-coercion dispatcher registries across adapters, and direct compiler result consumption without intermediate tuple relays. (#771)

  • sql.values(...) creates a Values builder that binds values for bulk row lists. Use sql.column("values") to refer to a column named values. (#779)

  • Removed unused private helpers and shared repeated code across builders, drivers, and migrations. Loader, service, and ADK artifact modules now group public methods before private helpers. Supported public APIs and query behavior stay the same. (#784)

Fixed:

  • Cached statements no longer retry SQL when a query or result conversion fails. This prevents duplicate writes. Cached dict and record rows keep their values. With pymssql, statement stacks leave the caller's open transaction in place. (#742)

  • Preserve JSON objects and arrays as individual query parameters after placeholder conversion, instead of reinterpreting them as batches during parameter validation.

  • SQLite and aiosqlite adapters map primary key constraint violations (extended error code 1555 and SQLITE_CONSTRAINT_PRIMARYKEY) to UniqueViolationError. (#775)

  • DuckDB adapter maps TransactionException update conflicts ("Conflict on update") to SerializationConflictError, and all other transaction failures to OperationalError. (#775)

  • Fixed SQL Server Litestar session store parameter placeholder and binary conversion in MssqlPythonStore and PymssqlStore (CONVERT(VARBINARY(MAX), ?)), removed unsupported FOR UPDATE hints from PymssqlConfig.get_event_runtime_hints() for event queue polling, and corrected the documentation feature table and metadata to designate mssql-python as a sync-only driver with Arrow support. SQL Server ADK memory inserts now deduplicate concurrent event IDs with a single key-range-locked statement. (#781)

  • Update.from_() accepts query builders and subqueries with parameter merging instead of raising a runtime type error, and dialect checks raise a descriptive SQLBuilderError on dialects without native FROM clause support. (#779)

  • CTEs registered via with_cte() or with_() render on Update and Delete statements and merge bound parameters without inlining or dropping explicit common table expressions. (#779)

  • Query builder keeps ON CONFLICT ... DO UPDATE and ON DUPLICATE KEY UPDATE assignments in written order. Assignments such as do_update(name=exp.column("name", table="excluded")) no longer render reversed. Conflict targets and update columns are quoted, allowing reserved words (e.g. order, group) to be used as column names.

  • Executing a SQL object that already carries bound named parameters with additional keyword arguments merges both sets, with execute-time arguments overriding matching names. Previously, pre-bound parameters were dropped. (#762)

  • DuckDB secrets declared in driver_features["secrets"] issue CREATE [PERSISTENT] SECRET IF NOT EXISTS by default. Existing secrets matching type, provider, and unredacted settings are reused, avoiding connection setup race conditions on shared in-memory databases. Setting replace=True issues CREATE OR REPLACE for credential rotation. (#754)

  • PymssqlDriver.begin() no longer issues duplicate BEGIN TRANSACTION on connections with autocommit disabled, ensuring transaction work commits durably. On autocommit connections, commit() and rollback() correctly finalize T-SQL transactions.

  • The psqlpy driver correctly tracks transaction state initiated with begin(), avoiding false positives from coroutine-returning transaction inspections.

  • Adapters without savepoint support (DuckDB, BigQuery, Spanner, Snowflake) raise ImproperConfigurationError when attempting nested transaction blocks instead of sending unsupported DDL. Oracle skips unsupported RELEASE SAVEPOINT statements.

  • Litestar routes raising NotFoundError preserve headers added by route middleware and avoid duplicate after_exception hook invocations. (#760)

  • Migration tracker records empty up() statement executions in the schema tracking table, preventing no-op migrations from remaining in pending state indefinitely. (#748)

  • Migration squashing accepts all documented version range identifier formats. (#743)

  • Compiled wheel installations for PymssqlConfig and MssqlPythonConfig permit interpreted tracker subclasses under mypyc without raising inheritance TypeErrors. (#747)

  • Query builder Select.group_by() accepts sql.rollup(), sql.cube(), and sql.grouping_sets() factory expressions directly. Grouping set inputs must be tuples or lists of columns; bare strings raise SQLBuilderError.

  • MySQL bulk loading accepts local_infile=True or allow_local_infile=True without requiring an isolated bulk-load feature flag.

  • Storage obstore backend corrects glob matching, prefix listing, and ensures resolved paths remain strictly contained within the backend root directory. (#736, #737)

  • Resolved Sphinx autodoc forward reference errors during documentation builds for ExecutionResult NamedTuple type hints.

  • Storage benchmark scripts type-check cleanly under Python 3.10. (#772)

  • DDL statement builders (CreateTable and AlterTable) parse column definitions using the effective or target dialect, allowing dialect-specific data types (such as SQL Server DATETIME2(6)) to parse correctly. Re-building a statement for a different dialect re-parses column types for the target dialect. (#777)

Removed:

  • Retracted experimental storage staging methods (stage_artifact(), flush_staging_artifacts(), get_storage_job(), allocate_staging_artifacts(), cleanup_staging_artifacts()) and related staging capability options in favor of direct storage pipeline execution. (#740)

  • Removed undocumented sqlspec.exceptions.wrap_exceptions helper, superseded by typed per-adapter exception handlers.

  • Dynamic column access on the sql factory (sql.some_column) has been removed. Use sql.column("some_column") or Column("some_column"). (#771)

v0.62.2 - Litestar config lookup diagnostics#

Fixed:

  • SQLSpecPlugin.get_config() raises KeyError listing the available bind keys and dependency keys when an identifier matches no configuration. On a plugin not yet registered with a Litestar application, unknown names, unmatched config types, and configs from another registry previously raised ImproperConfigurationError about registration instead. Generated Litestar dependency keys remain registration-bound.

v0.62.1 - PostgreSQL ADK memory and migration fixes#

Fixed:

  • ADK now saves all memory vectors on PostgreSQL, including null values. Vector and hybrid searches pass query values with a portable float8[] cast. Asyncpg and psycopg no longer need optional pgvector codecs for these tasks.

  • BM25 search now checks for pg_textsearch. It no longer treats ParadeDB's pg_search as the same feature. The ADK migration enables pg_textsearch before it creates a BM25 index. Table checks and searches never try to install the extension.

  • Migration commands now set the SQL dialect before they build the context. Calls made from Python no longer capture an empty statement config.

Upgrade notes:

  • BM25 needs a server that ships pg_textsearch. The role that runs the migration must be able to run CREATE EXTENSION. AlloyDB offers BM25 on PostgreSQL 17 and 18. Use an alloydbsuperuser role or ask an administrator to install the extension first.

v0.62.0 - ADK session paging and retention, migrations, and event payloads#

Added:

  • Native PostgreSQL notification payloads can be measured before publishing. MAX_NOTIFY_BYTES, measure_notify_payload(), and fits_notify_payload() are exported from sqlspec.extensions.events, so producers can chunk a batch instead of discovering an oversized envelope as an exception. Measurement covers the complete encoded envelope in UTF-8 bytes and shares one serialization function with the encoder.

  • Google ADK artifact versions can be pruned by age. prune_artifacts() and prune_artifacts_sync() delete SQL metadata first, then remove the referenced content objects on a best-effort basis through the configured storage registry, reporting the number of version rows removed. Artifact stores gained an overridable delete_artifacts_older_than() hook that is non-abstract, so existing third-party subclasses continue to instantiate.

  • Google ADK session listings can be ordered and paged. SQLSpecSessionService.list_sessions() and every adapter session store accept order_by ("create_time" or "update_time"), descending, limit, and offset. The bounds are applied in SQL, so a page no longer reads the whole table into memory before slicing it. limit=0 returns an empty result without reaching the database, and a positive offset requires a finite limit. The SessionOrderBy literal and the normalize_session_list_options() validator are exported so third-party stores can share the same allowlist and bounds checking.

  • SQLSpecPlugin.get_config() resolves a configuration by instance, by concrete type, or by bind_key immediately after plugin construction, which makes CLI commands, migration scripts, and standalone workers able to share the application's plugin instance.

Changed:

  • Session listings order by the requested timestamp column and then by id in the same direction. The tie-break keeps paging deterministic when several sessions share a timestamp, where equal timestamps previously came back in whatever order the database chose. Unbounded listings still default to update_time descending.

  • prune_sessions(), prune_events(), prune_memory(), and prune_user_state() reject a non-positive retention age. idle_days=0 or older_than_days=0 previously resolved to a cutoff of "now" and deleted every row; all five prune helpers now raise ValueError, matching prune_artifacts().

  • enable_sessions and enable_memory are now the only per-feature gates for ADK migrations, and they gate both the upgrade and downgrade directions consistently.

  • The native notification payload ceiling is 7,999 encoded bytes rather than 8,000. PostgreSQL requires a payload shorter than 8,000 bytes, so an envelope of exactly 8,000 bytes was previously accepted locally and then rejected by the server. Oversize errors now report the measured size, the maximum, and the helper to use.

  • Publication timestamps in native notification envelopes are serialized at fixed microsecond width, so envelope size no longer varies with the clock reading. Previously emitted variable-width timestamps still decode.

Removed:

  • include_sessions_migration and include_memory_migration are removed from the ADK configuration without an alias or deprecation shim. The first was never read and the second duplicated enable_memory; use enable_sessions and enable_memory instead. Passing the removed keys raises at configuration construction.

  • The unused destructive 0002_reset_adk_tables migration is deleted. 0001_create_adk_tables remains the single canonical ADK schema migration.

Fixed:

  • Fresh PostgreSQL databases can run the packaged ADK schema migration with memory enabled. The migration now emits CREATE EXTENSION IF NOT EXISTS vector immediately before the first statement that declares a VECTOR column, scoped to PostgreSQL-family dialects. Previously the memory DDL declared vector columns and indexes without installing the extension, failing with type "vector" does not exist on any server where pgvector was not already present. Startup paths such as create_tables() and ensure_tables() never install extensions.

  • SQLSpecPlugin.get_config() no longer requires application registration to resolve a configuration, and it honors each configuration's bind_key. Resolving by type raises KeyError listing candidate bind keys when several configurations share a concrete type, rather than returning the first match. Generated Litestar dependency keys remain registration-bound.

  • Compiled async drivers no longer re-raise an exception already being handled by the caller when a database operation inside that handler succeeds.

Upgrade notes:

  • The pgvector migration change requires the database server to have the pgvector files available and the migration role to be permitted to run CREATE EXTENSION. On managed services where that privilege is withheld, a DBA or the provider must pre-provision the extension. The failure now surfaces at CREATE EXTENSION rather than later as a missing type.

v0.61.0 - Scoped memory recall and ADK modernization#

Added:

  • Google ADK memory stores now support scoped memory recall. The adk_memory table stores a scope column ('user' or 'app') with composite indexes for efficient partitioned lookups across all 14 database dialect adapters.

  • Memory recall retrieves both user-scoped memory (scope = 'user') and app-scoped memory (scope = 'app') by default, and supports filtering via scope_filter ("all", "user", or "app").

  • Memory ingestion methods (add_memories, add_events_to_memory, and add_session_to_memory) accept a scope argument (defaulting to 'user').

  • New sqlspec.extensions.adk.maintenance module provides prune_events(), prune_sessions(), and prune_user_state() for programmatic retention maintenance with structured deletion reporting.

  • The adk_event table now persists app_name and user_id directly across all database adapters, backed by (app_name, timestamp) composite indexes.

  • psycopg now supports AlloyDB / PostgreSQL 17+ BM25 full-text indexing, vector embeddings, and hybrid similarity ranking parity with asyncpg.

  • MemoryService.search_memory() accepts an optional embedding vector parameter to fuse vector similarity scoring with text search.

  • Added ADK configuration options for vector search: vector_index_type, vector_dimensions, enable_bm25, scann_num_leaves, and scann_quantizer.

Changed:

  • ADK domain record types are modernized to clean domain models: StoredMemory, StoredSession, StoredEvent, and StoredArtifact. Legacy type aliases (MemoryRecord, SessionRecord, etc.) have been removed.

  • ADK database table names are standardized to singular form across all 14 dialect adapters: adk_session, adk_event, adk_memory, adk_app_state, adk_user_state, adk_artifact, and adk_internal_metadata.

  • ADK table migration 0001_create_adk_tables.py consolidates singular table names and the scope column into the base migration.

  • Table maintenance (maintain_tables) strictly performs row retention pruning across all stores; dialect-specific storage commands (VACUUM, ANALYZE, CHECKPOINT, etc.) and the unused reindex flag have been removed.

  • Oracle ADK storage features (compression, in-memory, partitioning) emit DDL clauses directly without dynamic v$option server probes at runtime.

Fixed:

  • ADK retention pruning (delete_entries_older_than) now consistently honors app_name filtering across all stores and database adapters.

  • Arrow conversion now preserves declared SQL / cursor column types for columns containing only NULL values when using psycopg or MySQL drivers (aiomysql, asyncmy, mysqlconnector, pymysql), preventing type collapse to null or string.

v0.60.0 - Queue limits#

Added:

  • The new listener_queue_capacity setting caps each PostgreSQL listener queue. It works with asyncpg, psqlpy, and sync or async psycopg listeners. The default has no limit. Bad values raise ImproperConfigurationError.

  • SQLSpecChannelsBackend accepts output_queue_capacity. It caps decoded messages from any async event transport, including PostgreSQL, Oracle, and polling channels. The default has no limit. Bad values raise ValueError.

  • output_queue_depth shows the current Litestar backlog. dropped_message_count shows the total number of overflow drops.

  • PostgreSQL listener metrics now track events.listener.queue.depth and the total events.listener.queue.dropped count for the hub.

Changed:

  • A full capped queue drops its oldest item before it adds the new one. Each PostgreSQL consumer has its own queue. Shutdown clears queued items and sets depth to zero. Drop counts stay in place after a restart.

  • An overflow in PostgreSQL notify drops transient data. With notify_queue, it drops only a wake-up marker. The durable row stays in the table and can be found by the next scan.

  • Oracle AQ and TxEventQ still read from their native queues. They do not add a listener queue. Table-backed poll_queue stores check listener_queue_capacity, but the setting does not change how they poll.

  • Bad Litestar channel payloads are logged and acknowledged. They do not increase dropped_message_count.

v0.59.0 - Data dictionary and loader access#

Added:

  • SQLSpec.loader gives read-only access to the registry's SQL file loader. SQLSpec creates the loader on first access when one was not supplied.

  • Object storage backends expose resolve_uri(path) to format one backend-relative path as an absolute local path or protocol-qualified remote URI without storage I/O. Custom backends that implement ObjectStoreProtocol must define it too.

Changed:

  • Explicit database cancellation now raises OperationCancelledError. Timeouts and deadlines still raise QueryTimeoutError. Both exceptions inherit from OperationalError. Applications that caught QueryTimeoutError for both outcomes must catch both exceptions, or catch OperationalError.

  • Data dictionary SQL files now live beside each dialect package. SQLSpec loads these files through package resources.

  • Data dictionary query helpers now use separate domain and operation names. mode is optional. Update direct calls that pass one flat query name.

  • Spanner now sends UUID values as 36-character text, not base64 bytes. To keep UUID objects unchanged, set driver_features={"enable_uuid_conversion": False}. Text is the new default.

Fixed:

  • SQL files supplied with Windows drive paths now resolve from their requested directory instead of the process working directory.

  • Async statement errors from mypyc-compiled drivers are translated into SQLSpecError instead of terminating the process.

  • ADBC adapters for PostgreSQL now keep None in arrays. Each value binds as SQL NULL. This keeps null values in place.

v0.58.3 - Data and store fixes#

Fixed:

  • Nested msgspec structs now use their own encoded field names. This works for optional fields, annotations, lists, tuples, and maps. Mixed rename rules no longer reuse the outer struct's rule. Keys in plain maps stay unchanged.

  • ensure_async_() now has a collections.abc.Coroutine return type. This matches the value it has always returned at run time and removes the need for a cast.

  • SQLSpec Litestar session stores now inherit litestar.stores.base.Store. They keep its async context manager and work with StoreRegistry.

  • Each extension now loads its SQL migration query names on its own. A file such as 0001_create_table.sql can use migrate-0001-up and migrate-0001-down with no clash across extensions. SQLSpec still stores the prefixed tracker version.

v0.58.2 - SQL file parameter diagnostics#

Fixed:

  • Malformed -- param: directives now produce actionable warnings that name the SQL file, line number, and malformed directive. Structured log records expose the numeric line_number and textual directive separately. Non-strict loading continues to warn and skip malformed directives, while strict loading continues to raise SQLFileParseError.

v0.58.1 - Migration template configuration#

Fixed:

  • migration_config accepts the three keys that customize generated migration files: templates, default_format, and title. The key validation added in v0.58.0 did not recognize them, so any configuration using a customized migration template raised ImproperConfigurationError at construction. default_format was additionally reported with a suggestion to use default_schema, an unrelated setting.

  • Template overrides are validated when the configuration is built rather than when a migration is generated. A misspelling inside templates.sql or templates.py reports its full path and the closest valid key, and an override that is not a mapping is named along with the type supplied.

  • MigrationConfig documents the real default for version_table_name, which is ddl_migrations.

Added:

v0.58.0 - Configuration and storage correctness#

Changed:

  • migration_config now rejects keys SQLSpec does not read, raising ImproperConfigurationError with the closest valid key. A misspelling such as version_table instead of version_table_name was previously accepted and silently ignored, leaving the setting at its default. Remove or correct unrecognized keys to upgrade.

  • Storage writes reject formats that cannot carry the payload being written, raising StorageCapabilityError before any encoding or storage I/O. Row writes accept only json and jsonl; Arrow table writes accept only parquet, arrow-ipc, and csv. A mismatched format previously wrote one payload type under another format label. Read APIs still accept all five formats.

  • stream_arrow_sync() and stream_arrow_async() accept only file_format="parquet" and raise StorageCapabilityError for other formats. Use the regular Arrow read APIs for CSV, Arrow IPC, JSON, and JSONL payloads.

  • JSONL payloads decode through PyArrow's native JSON reader. Its type inference applies to the result, so date-like strings now decode as Arrow timestamps rather than strings.

Fixed:

  • Arrow batch streaming reads one Parquet row group at a time across the local, fsspec, and obstore backends, and accepts a batch_size bounding each record batch. The obstore backend streams through its seekable reader instead of buffering the whole object in memory, resolves cloud base_path only once, and closes readers deterministically when a stream is closed early.

  • Decoding a JSONL payload containing a row larger than 1 MiB no longer fails with ArrowInvalid: straddling object straddles two block boundaries.

  • ADBC PostgreSQL connections recover after a failed statement. The aborted transaction is now cleared through the connection rather than by sending a ROLLBACK statement on a cursor, which the driver rejects on a connection that is already in an error state. Every later statement on that connection previously failed with INVALID_STATE: [libpq] cannot start transaction. Uncommitted work in the aborted transaction is discarded, as PostgreSQL requires; commit before a statement whose failure you intend to recover from.

  • Pointing --config at a module rather than a configuration object now reports the module:attribute references that module exports, instead of failing later with AttributeError: module has no attribute 'bind_key'.

  • Configuration references that misuse : report the accepted syntax rather than an import failure.

  • Errors raised while importing a configuration module keep their original type and message instead of being rewrapped as an import failure.

  • The "No SQLSpec config found" help text shows the [tool.sqlspec] section name, which console markup previously consumed, and no longer mangles config paths that contain colons.

  • author is declared on MigrationConfig. The migration generator already read it, but type checkers rejected it.

  • psycopg record loads preserve JSON and JSONB object shapes through Arrow COPY. JSON mappings previously reached psycopg COPY as Python dictionaries, which it cannot adapt in text COPY mode.

Performance:

  • AsyncpgDriver.load_from_records() writes records directly with one binary COPY call instead of round-tripping them through Arrow. The removed conversion dominated small and medium batches; large batches also overtake executemany() throughput.

v0.57.0#

Added:

  • Packages distributed separately from SQLSpec can now ship Python migrations. Set migrations_path in an extension_config entry to point at a directory or a '<dotted.module>:<subdir>' specification. The extension is discovered and auto-included without appearing in include_extensions.

  • Added add_extension_migrations(name, migrations_path, settings=None) on database configurations, for packages that register migrations at runtime rather than declaratively.

Fixed:

  • Extension names containing underscores no longer lose their version. A migration such as ext_my_extension_0001_init.sql previously resolved to the version ext_my_extension, dropping 0001 and recording a malformed version in the migration tracking table.

  • A configured extension that cannot be resolved now reports which module was tried and that no migrations were registered, instead of the ambiguous Extension <name> not found.

  • Extensions that ship no migrations directory no longer log a warning. Six of the bundled extensions have no migrations by design, so the warning was noise.

  • sqlspec.utils.module_loader.module_to_os_path() resolves namespace packages to their search location instead of returning a path named None.

  • Compiled wheels now return correct results from isinstance() and issubclass() across SQLSpec class hierarchies. A previous check could poison a shared abstract-base cache and cause later query-builder execution to fail.

  • Compiled migration runners no longer raise TypeError while resolving the default schema when no configuration is attached.

Changed:

  • sqlspec.utils.module_loader.module_to_os_path() raises ModuleNotFoundError rather than TypeError when a module cannot be found, so callers can catch the real condition.

  • SQLSpec base classes no longer use ABCMeta at runtime because mypyc shares its abstract-base caches across compiled class hierarchies. Static type checkers still enforce abstract methods. Runtime code should not rely on inspect.isabstract() or abstract-class instantiation errors for these bases. StatementResult remains structurally iterable.

Known limitations:

  • Extension-owned SQL migration files are discovered but cannot yet resolve their prefixed named queries. Separately distributed packages should ship Python migration files for this release.

v0.56.2#

Added:

  • Added uuid_from_string(), uuid_from_bytes(), and uuid_from_int() in sqlspec.utils.uuids. They always return uuid.UUID. Text parsing uses Rust when uuid-utils is installed.

Fixed:

  • PostgreSQL-family ADBC row APIs now decode scalar UUID and UUID[] data to Python UUIDs. This works for buffered and streamed rows. Native Arrow results keep the extension schema, and enable_arrow_extension_types=False restores raw storage bytes on row APIs.

  • Oracle 12c through 20c can bind direct Python JSON values to BLOB IS JSON columns. SQLSpec writes UTF-8 JSON to a BLOB locator for sync, async, batch, and streaming calls. Oracle 21c and newer still use native JSON binding. Explicit OracleClob values remain CLOBs.

Changed:

  • BigQuery load_from_records() now reuses fully checked lists of plain dictionaries when no fields must move. It still copies rows for explicit columns, mapping subclasses, and different key orders.

  • Spanner and Oracle now reuse bind data when no value needs a conversion. They copy only after the first changed value. Bound values and checks are unchanged.

  • UUID parsing now uses sqlspec.utils.uuids across adapters and type converters.

Docs:

  • The Oracle guide now covers JSON storage, LOB, UUID, and VECTOR behavior. It also covers driver options and an Oracle MERGE upsert recipe.

  • The ADBC guide now explains UUID row and Arrow results. It also documents the enable_arrow_extension_types switch.

v0.56.1#

Added:

  • PostgreSQL-family ADBC drivers accept a list or tuple of UUIDs as a single array parameter, so queries such as WHERE id = ANY(CAST(? AS UUID[])) now work. The cast is added automatically when the query does not already supply one.

Fixed:

  • PostgreSQL-family ADBC drivers no longer fail when a UUID is a statement's only parameter. Repeated executions of such a statement previously reused a cached plan that skipped UUID binding and reached PostgreSQL as bytea.

  • EXPLAIN statements now return their query plan. Explained statements were classified as non-row-returning, so drivers ran the EXPLAIN and then discarded the plan, leaving select() and execute() with no rows. This affected every adapter except DuckDB, ADBC, and BigQuery. SHOW and DESCRIBE are now classified the same way. Oracle .explain() calls now tag the plan-table entry, return DBMS_XPLAN rows, and remove the entry before returning. Raw caller-owned EXPLAIN PLAN statements remain unchanged. (#655)

  • Explaining a statement no longer discards its configuration. An explained PostgreSQL statement previously compiled with ? placeholders instead of $n, and a named parameter used more than once was sent once per use instead of being deduplicated. SQL.copy(statement_config=...) also raised TypeError and now accepts an override.

  • TABLE table_name statements now return their rows on PostgreSQL, DuckDB, and MySQL. SQLGlot does not currently model this shorthand for SELECT * FROM table_name, so SQLSpec previously classified it as a non-row-returning command and discarded the result.

Changed:

  • PostgreSQL-family ADBC drivers convert UUID parameters faster. UUID objects are now formatted directly instead of being re-parsed on every execution, which roughly halves the conversion cost of large execute_many batches. Bound values are unchanged.

  • Binding a UUID array containing None now raises an explicit error. The PostgreSQL ADBC driver encodes null array elements as empty strings, which PostgreSQL rejects for UUID[]; the previous behavior was an opaque invalid input syntax for type uuid failure from the server.

v0.56.0#

Breaking changes:

  • Extension storage keys that a selected ADK, Litestar, or Events backend cannot honor now raise an explicit configuration error instead of being silently ignored.

Added:

  • New SchemaTarget and SchemaEnsureResult types plus sync and async schema checks can create missing tables and add columns. Use ensure_schema_sync() or ensure_schema_async() for each driver mode. Stores use manage_schema and create_schema for these checks. Run migrations as a separate step.

  • Oracle ADK, durable event, and Litestar session tables now share opt-in compression, partitioning, In-Memory, and table-option configuration.

  • BigQuery session and queue partition options and CockroachDB session hash sharding and row-level TTL are now available.

  • Applicable Litestar, Events, and ADK stores now expose PostgreSQL table and autovacuum tuning, MySQL and MariaDB table/index options, Spanner sharding and table/index options, and opt-in SQLite extension PRAGMA profiles.

Changed:

  • Raised the minimum supported sqlglot and sqlglot[c] version to 30.13.0.

  • ADK, Litestar session, and durable event stores now derive additive schema currency from their canonical DDL. ADK no longer seeds or bumps a schema_version row for additive changes.

  • Oracle server-version, JSON storage, and extension-table capability detection now share the config/pool-scoped data dictionary cache. Requested storage optimizations degrade to structured warnings when an option is unavailable.

Fixed:

  • PostgreSQL-family ADBC connections now bind top-level UUID parameters as PostgreSQL uuid values across ordinary, batch, streaming, and Arrow execution routes. (#650)

  • Psycopg sync and async transactions now restore the connection's original autocommit mode after SQLSpec-owned commit or rollback operations (#648).

  • Spanner data-dictionary queries now cast nullable metadata filters to their concrete STRING or TIMESTAMP types, avoiding conflicting parameter inference when optional filters are omitted.

  • Builder caching now reuses value-independent expression templates and binds each call's current parameters and statement configuration. This also isolates CTE bodies and returned ASTs instead of sharing mutable cached objects. (#644)

  • Optimized-expression cache keys now include complete schema table, column, and type information, preventing different same-sized schemas from sharing an incompatible optimized AST.

  • mssql_python transactions now use the driver's DBAPI transaction state, persist committed work, roll back pending work, and restore the connection's original autocommit mode. (#642)

  • psqlpy now reports exact affected-row counts for non-returning single-row and multi-row INSERT, UPDATE, and DELETE statements while preserving the existing RETURNING result path. (#645)

  • mssql_python now materializes cached result rows as real tuples, matching its declared result format. (#630)

  • mssql_python and pymssql now classify SQL Server constraint messages even when the native driver omits or embeds the numeric error code, and pymssql translates named pyformat input to its reliable positional execution style.

v0.55.0#

Breaking changes:

  • SQLite and aiosqlite connections now follow the stdlib sqlite3 default by leaving PRAGMA foreign_keys disabled unless connection_config={"enable_foreign_keys": True} is passed. Both adapters now share a 5000 ms busy timeout and aligned optimization PRAGMAs when the default enable_optimizations=True setting is active.

  • Began replacing the old narrow data-dictionary interface with a consistent metadata contract based on MetadataCapabilityProfile, MetadataCapability, MetadataResult, ObjectIdentity, and DDLResult. This is a pre-1.0 breaking change: structural domain lookups return result envelopes, object DDL lookups return DDLResult directly, and callers should inspect capability or DDL status instead of treating empty lists as unsupported metadata.

  • Standardized event transport configuration on notify, notify_queue, poll_queue, aq, and txeventq. Retired transport names now raise an explicit configuration error with the canonical replacement.

Added:

  • Added sync and async event-channel publish_many() APIs. Batch-capable implementations preserve input order and publish a grouped call in one transaction; custom backends retain an ordered single-event fallback.

  • Added event_poll_interval for durable event reconciliation, independently of native listener wakeups. poll_interval remains a compatibility input.

Changed:

  • PostgreSQL listeners now hold one dedicated long-lived connection while publishers use short pooled sessions. Native PostgreSQL batch publication reuses one publisher transaction per grouped call.

  • notify_queue batch publication now bulk-inserts durable rows and sends one compact wakeup marker per channel rather than one notification per event.

Fixed:

  • Event channels now honor adapter events_backend driver features when no extension-level backend is configured.

  • Early mysql-connector row-stream cleanup now consumes unread results without reconnecting underneath an active transaction.

  • Public row streams continue to clean up duck-typed sources whose close() method uses the original no-argument contract.

  • Durable notification queues now drain all rows represented by a batch marker, suppress duplicate markers, and recover missed markers through periodic durable reconciliation.

  • Durable batch publication now preserves input delivery order, rolls back row inserts when marker publication fails, and drains all recovered rows after a lost marker without another native wait per event.

  • Event listener shutdown now cancels async waits concurrently and bounds sync thread joins. Empty table queues do not poll faster than event_poll_interval.

Docs:

  • Expanded the data dictionary guide with capability vocabulary, support matrix, DDL/dependency guidance, and safe system-metadata opt-in behavior.

  • Documented event transport delivery semantics, adapter support, connection ownership, batch behavior, and polling recovery.

v0.54.0 - SQL processing correctness and cleanup#

Changed:

  • Standardized adapter create_mapped_exception() helper signatures to accept (error, *, logger=None) across backends while preserving existing exception mapping behavior.

  • Standardized adapter apply_driver_features() helpers to return an updated statement config plus normalized driver-feature dictionary across backends.

  • MySQL-family adapter config, driver, and pool modules now resolve runtime vendor symbols through adapter-local typing modules.

  • Oracle LOB fetches now default to direct string/byte materialization where python-oracledb supports it. Pass fetch_lobs=True when application code needs native Oracle LOB locators. Unconstrained LOB contents are no longer parsed with content heuristics; native JSON, IS JSON CLOB/BLOB, and OSON-capable values still decode through Oracle JSON metadata.

  • Driver statement-object caches are now bounded by the configured statement cache size, and cached named-parameter rebinding reuses driver-owned processing state.

  • MySQL local-infile support now requires explicit opt-in consent before enabling client-side file reads.

  • Removed unused private builder, driver, compiler, cache, parameter, SQL-file loader, storage, ADK, migration, and adapter internals while preserving public imports and compatibility surfaces.

Fixed:

  • Dynamic SQLCommenter context and trace attributes are appended after stable SQL compilation, so repeated compiles reuse cached uncommented SQL while still using the current request context.

  • Statement configs are frozen before pipeline fingerprinting so repeated compiles avoid avoidable cache-key hashing.

  • Repeated statement-cache stores now skip redundant processed-state cloning when the raw SQL is already cached.

  • Parameter extraction, type-dispatch misses, scalar coercion, and execute-many fingerprints now avoid unnecessary hashing and allocation on hot paths.

  • No-op AST transformers no longer force full SQL finalization when they return the original expression and parameter objects.

  • Simple dict and keyword-parameter executions can use the direct statement cache path when the cached query profile can safely rebind them.

  • Oracle lock-target rendering for builder-generated FOR UPDATE OF clauses is handled by SQLGlot generation rather than post-render SQL rewriting.

  • adbc and arrow-odbc configs now honor on_connection_create driver hooks after creating raw connections.

  • CockroachDB psycopg session contexts now resolve callable statement configs at session entry, matching the rest of the PostgreSQL family.

  • Bridge cursor cleanup now suppresses close failures consistently so cleanup errors do not mask an in-flight database exception.

  • The mssql-python connection pool implementation now lives in its adapter pool module while preserving the existing public import.

  • mssql_python stack execution no longer raises the base _connection_in_transaction() error before applying batched statements.

  • arrow-odbc SQL Server transactions now rely on the connection commit/rollback API instead of sending a raw BEGIN TRANSACTION statement, so committed DML remains visible to later sessions.

  • Spanner adapter modules no longer expose module-level proxy lookup hooks.

  • Async migration squash now builds its internal migration runner with a real migration context, matching the synchronous command path.

  • ObStore Arrow streaming no longer resolves cloud base_path twice for async streams.

  • sql.decode() now renders a trailing default argument as the ELSE clause documented for DECODE-style expressions.

  • Async drivers can use the statement-cache direct execution path when the cursor supports awaitable execute(), activating adapter row and rowcount hooks that were previously bypassed.

  • aiomysql ADK table DDL now honors generated event columns, covering indexes, and adapter-local MySQL table options.

  • MySQL-family ADK stores now recognize missing-table errors reported through an errno attribute as well as positional error arguments.

  • Count-query generation no longer infers a missing outer FROM from tables nested inside scalar subqueries.

  • Data dictionary default driver features now come from the dialect-specific mixin instead of being hidden by the generic compatibility mixin.

  • Explicit optimize_expression=True now overrides a builder created with optimization disabled.

  • where_in() now binds plain string values as scalar parameters, matching where_not_in() and the OR helper variants.

  • Documentation builds now filter the known pymssql stub-only QueryParams guarded-import warning through the custom Sphinx tooling instead of changing adapter runtime code.

v0.52.0 - SQL Server adapters, ADK profiles, and cloud connectors#

Added:

  • Added the sync pymssql SQL Server adapter with config, driver, connection pool, data dictionary, migrations, event-store, Litestar session-store, and ADK store support.

  • Added SQL Server support for arrow_odbc adapter contracts, ADK session/event storage, event queue storage, and Litestar session storage.

  • Added ADK store and tuning profiles across SQLite, DuckDB, PostgreSQL, CockroachDB, MySQL, BigQuery, Spanner, mssql-python, and arrow_odbc. These profiles expose adapter-local table, index, full-text search, retention, and backend-specific DDL options.

  • Added Google Cloud connector support for sync adapters: Cloud SQL for pymysql and AlloyDB for sync psycopg.

  • Added native Oracle event backends for Advanced Queuing and Transactional Event Queues.

  • Added row-locking capability introspection across data dictionary dialects.

  • Added docs coverage for the new SQL Server adapters, cloud connector setup, ADK backend matrix entries, and package extras parity.

Changed:

  • Standardized Oracle native event backend names to aq and txeventq; poll_queue remains the default backend.

  • Moved ADK optimization and storage tuning options into adapter-local config types instead of the shared global config surface.

  • Tightened adapter typing and core pipeline internals for the compiler, splitter, parameter handling, filters, result handling, cache/runtime helpers, and mypyc-ready adapter boundaries.

  • Updated package extras to include current adapter and framework integrations, including arrow-odbc, mssql-python, pymssql, sanic, and starlette.

Fixed:

  • Removed the obsolete aioodbc extra and added docs/package parity checks so the installation guide matches available extras.

  • Corrected pymysql stack transaction-state detection so nested stack execution reflects the real driver transaction state.

  • Localized ADK optimization config to adapter implementations so backend tuning no longer depends on unused shared config keys.

v0.51.0 - ADK 2.0 clean-break store contract#

Breaking changes:

  • The ADK session and event store contract is rebuilt for Google ADK 2.0 (verified through google-adk 2.3.0). Sessions are now keyed by (app_name, user_id, session_id) across every adapter, and the session service APIs (create_session, get_session, list_sessions, delete_session) are keyword-only.

  • get_session(), delete_session(), and update_session_state() on the store now require app_name and user_id in addition to session_id. update_session_state(app_name, user_id, session_id, state) replaces the former two-argument form.

  • The event payload column was renamed from event_json to event_data on every ADK adapter store.

  • Session state is split into scoped tables. Alongside adk_session and adk_event, stores now manage adk_app_state, adk_user_state, and adk_internal_metadata.

  • Migration 0002_reset_adk_tables is destructive: it unconditionally drops legacy ADK tables (sessions, events, app/user state, metadata, memory) and recreates them in the 2.0 shape. Back up ADK data before upgrading.

  • sqlspec.utils.sync_tools.async_() now uses SQLSpec's managed ThreadPoolExecutor by default instead of delegating to the event loop's default executor through asyncio.to_thread(). Configure the worker limit with SQLSPEC_ASYNC_THREAD_LIMIT or enable_default_async_thread_pool().

Added:

  • Typed environment parsing helpers in sqlspec.utils.env.

  • ThreadPoolExecutor support for sqlspec.utils.sync_tools.async_(), plus bounded async bridge controls through SQLSPEC_ASYNC_THREAD_LIMIT, enable_default_async_thread_pool(), set_default_async_executor(), get_default_async_executor(), and shutdown_default_async_executor().

  • Scoped-state accessors on every ADK store: get_app_state, get_user_state, upsert_app_state, upsert_user_state, get_metadata, and set_metadata.

  • append_event_and_update_state() accepts optional app_state and user_state deltas and applies them atomically with the session and event write, returning the updated StoredSession.

Fixed:

  • Preserved contextvars when async_() routes sync work through explicit or shared thread executors.

  • Removed a dead storage_uri key from the artifact-store config normalization; the artifact storage URI is supplied to ADKArtifactService through its constructor and was never read from the store config.

  • DMLResult.all() and one_or_none() no longer raise AttributeError when called with schema_type; the fast DML result path now initializes its schema-row caches.

  • SQLProcessor.clear_cache() now resets the single-entry micro-cache, so the next compile of a previously compiled statement is recorded as a miss and repopulates the cache instead of returning a stale result.

  • The SQL statement splitter caches results on the script text rather than hash(sql), preventing a hash collision between two distinct scripts from returning the wrong split.

  • hash_parameters no longer raises TypeError for named parameters with unhashable values (for example set or bytearray); such values now fall back to a stable repr-based key, matching the positional path.

v0.50.1 - DuckDB extension lifecycle and SQLGlot builder modernization#

Changed:

  • Modernized the SQLGlot builder code paths.

Fixed:

  • Separated DuckDB extension installation from loading with a best-effort lifecycle, so a failing optional extension no longer aborts connection setup.

v0.50.0 - Adapter config modernization, row streaming, and fetch tuning#

Added:

  • Native row streaming via select_stream() across all adapters, built on a new Arrow-streaming foundation with Arrow-native streaming paths.

  • Driver-level cache and fetch tuning controls.

  • SQLite runtime connection setup.

  • Oracle sparse VECTOR passthrough.

  • SQL-file parameter metadata annotations (-- param:).

Changed:

  • Modernized adapter configuration across the full adapter suite: sqlite, aiosqlite, asyncpg, psycopg, psqlpy, oracledb, duckdb, asyncmy, aiomysql, mysqlconnector, pymysql, adbc, arrow-odbc, bigquery, spanner, mssql, and the cockroach (asyncpg/psycopg) configs.

Fixed:

  • Honored optimizer flags in the query builder.

  • Preserved the ADBC driver-manager configuration.

v0.49.1 - Transaction context-manager propagation#

Fixed:

  • begin_transaction context managers no longer suppress exceptions raised inside the block.

v0.49.0 - Driver-contract matrix consolidation#

Changed:

  • Consolidated the adapter suite into a shared driver-contract test matrix as part of a mypyc and code-quality overhaul.

Fixed:

  • Normalized dialect identifier bindings.

  • Generated Oracle-safe parameter names.

v0.48.2 - Filter-provider deepcopy fix#

Fixed:

  • Dropped the filter-provider modules from mypyc compilation to restore copy.deepcopy support for providers.

v0.48.1 - deepcopy and pickle for compiled value objects#

Fixed:

  • Supported copy.deepcopy and pickle on mypyc-compiled value objects.

v0.48.0 - Arrow ODBC and mssql-python adapters, migration schemas#

Added:

  • New arrow_odbc and mssql_python adapters.

  • Support for specifying a schema for migrations.

Fixed:

  • Repaired filter providers and adapter regressions.

v0.47.0 - Persistent listeners, schema builders, and performance polish#

Breaking changes:

  • schema_dump(), serialize_collection(), and get_collection_serializer() now default wire_format=False. Msgspec structs with rename= now emit Python attribute names by default, matching Pydantic, dataclasses, and attrs. Pass wire_format=True to keep wire-aligned names.

  • Third-party ADK stores implementing append_event_and_update_state() must return the updated StoredSession.

  • Data dictionary metadata/version helpers now live under sqlspec.data_dictionary. ColumnMetadata, ForeignKeyMetadata, IndexMetadata, TableMetadata, VersionInfo, and VersionCacheResult are no longer exported from sqlspec.typing or sqlspec.core.

  • Removed modernization compatibility shims and deprecated helpers. Use SQL.raw_sql instead of SQL.sql, CorrelationContext.context() instead of sqlspec.utils.correlation.correlation_context(), MSSQL_CONFIG.default_schema instead of resolve_mssql_default_schema(), Insert.values_from() and Insert.values_from_many() instead of Insert.values_from_dict() and Insert.values_from_dicts(), clear_all_caches() or reset_stats_only() instead of reset_cache_stats() or SQLSpec.reset_cache_stats(), and len(cache) instead of LRUCache.size(). Oracle session callbacks are now always installed, so requires_session_callback() was removed.

  • Removed filter compatibility APIs. PaginationFilter and create_filters() are gone, LimitOffsetFilter now subclasses StatementFilter, and OrderByFilter rejects invalid sort_order values instead of silently coercing them to asc.

  • Tightened parameter and serializer helpers. ParameterStyleConfig.hash() was removed in favor of hash(config). build_null_pruning_transform() and replace_null_parameters_with_literals() no longer accept validator= and require an explicit parameter_profile for non-empty parameter sets. build_time_iso_converter() was replaced by the shared time_iso_convert helper.

  • Operation/result semantics changed. OperationType no longer includes UNKNOWN; parse fallback now uses COMMAND. SQLResult operation helpers now use canonical operation values directly, and create_sql_result() exposes explicit keyword arguments instead of accepting arbitrary **kwargs.

  • SQLFileLoader.get_sql() now compiles named statements on lookup and returns the cached SQL object for repeated normalized names until clear_cache() is called.

  • Result and adapter internals dropped importable compatibility helpers: sqlspec.core.result._io and its rows_to_pandas() / rows_to_polars() helpers, ArrowOdbcTypeConverter, BQ_TYPE_MAP, DuckDBOutputConverter.convert_duckdb_value(), DuckDBOutputConverter.prepare_duckdb_parameter(), and psqlpy.normalize_scalar_parameter().

  • Oracle cleanup removed OracleVectorType and the legacy OracleOutputConverter.detect_json_storage_type(), OracleOutputConverter.format_datetime_for_oracle(), OracleOutputConverter.handle_large_lob(), and OracleOutputConverter.convert_oracle_value() helper methods.

  • Migration internals moved. BaseMigrationRunner is no longer exported from sqlspec.migrations.base; import it from sqlspec.migrations.runner if subclassing migration runners.

  • PyMySQL no longer unwraps connection_config["extra"] into raw driver keyword arguments; pass driver kwargs directly in connection_config.

  • Several public implementation classes are now marked @final for typing/mypyc correctness. Downstream subclasses of these classes will fail static type checking. Affected classes include driver/converter internals such as AdbcDriver, AdbcExceptionHandler, BigQueryOutputConverter, DuckDBOutputConverter, SpannerOutputConverter, builder wrapper/factory types, JoinBuilder, SQLFactory, OperationProfile, CompiledSQL, SQLProcessor, dialect config classes, CachedQuery, QueryCache, event message/queue types, and MigrationVersion.

  • Performance cleanup tightened additional compatibility-sensitive contracts: storage backend_type is a class attribute, parameter builders expose generate_unique_parameter_name(), statement observers are protocol based, and legacy aliases such as BackendNotRegisteredError were removed.

Added:

  • Added Insert.values_from(), Insert.values_from_many(), and Update.set_from() for schema-aware SQL builders. These helpers accept dicts, dataclasses, msgspec structs, Pydantic models, and attrs classes while preserving Python attribute names for SQL columns.

  • Added on_pool_destroying lifecycle hooks so components can release checked-out resources before pools close.

  • Added runtime lifecycle hook registration through ObservabilityRuntime.register_lifecycle_hook().

  • Added async lifecycle hook execution for pool, connection, session, query, and error events. Async SQLSpec paths now await hooks registered for on_pool_create, on_pool_destroying, on_pool_destroy, on_connection_create, on_connection_destroy, on_session_start, on_session_end, on_query_start, on_query_complete, and on_error.

Fixed:

  • Reworked native event listener backends for asyncpg, psycopg, psqlpy, and Oracle AQ to use persistent per-channel listeners, avoiding connection races, callback churn, dropped secondary subscriptions, and ignored Oracle poll_interval settings.

  • Honored builder optimization flags by wiring explicit sqlglot optimizer rules, so optimize_joins, optimize_predicates, and simplify_expressions now disable only their matching steps instead of always running the full default pipeline.

  • Passing a sqlglot Dialect class to EXPLAIN builders or StatementConfig.dialect now resolves to the correct dialect name.

  • Avoided parser round-trips for simple builder identifiers and MERGE JSON source construction while preserving rendered SQL.

  • Deferred temporal version-generator registration until temporal builder APIs are used. Code that hand-builds exp.Version nodes should call sqlspec.builder.register_version_generators() before rendering them.

  • Routed async pool teardown through the base config lifecycle path so on_pool_destroy and on_pool_destroying fire consistently across async adapters.

  • Registered binary json and jsonb codecs for AsyncPG and CockroachDB AsyncPG connections, allowing Arrow bulk loads into PostgreSQL JSON columns.

  • Restored Litestar request decoding for handlers annotated with np.ndarray.

  • Bounded missing named-SQL error messages and added structured lookup context through SQLStatementNotFoundError.

  • Normalized framework orderBy aliases so camel-case API values can map to SQL-facing snake-case fields while preserving the configured field allowlist.

  • Hardened BigQuery emulator handling for simple inserts and unsupported bulk load paths.

  • Preserved Oracle implicit identifier casing for expression-backed query builder statements, fixing FOR UPDATE, vector-distance, and migration tracker queries against unquoted Oracle objects.

  • Preserved repeated same-named bind parameters in expression-backed pagination count and window-count queries.

Performance:

  • Expanded mypyc coverage to sqlglot dialect helpers, data-dictionary dialects, selected extension helpers, ADK record types, and measured hot-path helpers.

  • Added librt to the performance extra for compiled string assembly in SQL splitting and psqlpy copy encoding.

v0.46.3 - Plugin initialization and loader diagnostics#

Fixed:

  • SQLSpecPlugin.on_app_init() now mutates app_config.plugins in place, preserving Litestar plugin discovery for plugins registered later in the startup sequence.

  • Missing named SQL statements now report bounded diagnostics instead of dumping every loaded statement name.

v0.46.2 - Framework filter wire-name normalization#

Fixed:

  • Framework filter providers now normalize configured sort fields against wire-facing names, fixing camel-case frontend values such as orderBy=uploadedCollections when the SQL field is snake_case.

v0.46.1 - Litestar filter provider binding#

Fixed:

  • Litestar generated filter providers now use unique dependency parameter names for sibling IN, NOT IN, null, not-null, and range filters, preventing values from one filter from binding to another.

v0.46.0 - Service typing and serializer registry#

Fixed:

  • Restored async and sync service overload narrowing for paginate() and get_one() when schema_type is provided.

  • Extracted DEFAULT_TYPE_ENCODERS and applied them through the Litestar plugin while preserving user encoder precedence.

  • Added Litestar decoders for NumPy arrays and uuid_utils.UUID values.

v0.45.0 - Services, filters, Oracle types, and Sanic#

Added:

  • Added first-party SQLSpecAsyncService and SQLSpecSyncService helpers with pagination, lookup, existence, and transaction convenience methods.

  • Added Sanic framework integration.

  • Added Oracle native JSON, VECTOR ergonomics, UUID/LOB handling, and smarter type coercion for Oracle workloads.

Fixed:

  • Qualified statement filters correctly for joined queries and count queries.

  • Tightened SearchFilter and NotInSearchFilter validation so unsupported field names fail instead of silently dropping predicates.

  • Fixed raw ORDER BY handling and widened computed-column support for search and sort filters.

  • Exposed LIKE-pattern escaping helpers for callers that bypass the standard filter pipeline.

v0.44.0 - Aiomysql, schema wire names, and pagination introspection#

Added:

  • Added the aiomysql adapter with driver, config, Arrow, migrations, ADK, event queue, Litestar store, data-dictionary, and integration coverage.

Changed:

  • Removed the mock adapter and updated the testing docs around real adapter fixtures.

  • Converted OffsetPagination to a stdlib dataclass while keeping the public import path intact.

Fixed:

  • schema_dump() now honors msgspec rename= metadata for wire-format output.

  • OffsetPagination preserves runtime annotations for mypyc wheels and Litestar OpenAPI generation.

v0.43.0 - SQLCommenter, ADK stale sessions, and docs build fixes#

Added:

  • Added Google SQLCommenter support.

  • Added ADK stale-session detection.

Fixed:

  • Added ParadeDB and pgvector dialect configuration to the SQL splitter.

  • Fixed mypyc compilation issues, exception handling, filter providers, and vector-distance SQL generation.

  • Removed the Sphinx Toolbox dependency to keep documentation building on Sphinx 9.x.

v0.42.0 - ADK store alignment#

Changed:

  • Overhauled the ADK backend to align with the ADK 1.0 store contract.

Fixed:

  • Addressed serializer follow-ups found by mypyc builds.

v0.41.1 - Path and documentation fixes#

Fixed:

  • Resolved root paths to the parent directory for file-based paths.

  • Fixed documentation references for vector distance and Flask examples.

v0.41.0 - Documentation, PostgreSQL dialects, and storage polish#

Added:

  • Added PostgreSQL extension dialect support.

  • Added CSV format support for Arrow table export and import.

Changed:

  • Overhauled the documentation structure and content.

  • Moved sqlglot dialect modules into the top-level sqlspec.dialects package.

  • Improved mypyc configuration and CI validation paths.

Fixed:

  • Supported set operations in pagination and count queries.

  • Isolated AioSQLite in-memory databases with unique URIs per config instance.

  • Added Oracle BLOB support and byte-length thresholds for LOB coercion.

  • Used the portal fallback when await_() is called from an async task.

  • Deduplicated named parameters and fixed SearchFilter placeholder reuse.

v0.40.0 - SQLGlot refresh#

Changed:

  • Updated the sqlglot dependency pin to the latest supported version.

v0.39.0 - Migration squash and hot-path performance#

Breaking changes:

  • Renamed storage sync methods to the *_sync pattern.

  • Reworked the parsing pipeline around parse-once AST preservation and structural parameter fingerprinting.

Added:

  • Added the migration squash engine.

  • Added benchmark scripts and hot-path performance optimizations for parsing, parameter processing, serialization, and Arrow conversion.

Fixed:

  • Fixed in-memory Arrow streaming with an async sentinel pattern.

  • Improved AioSQLite pool shutdown and thread handling.

  • Restored documentation search and hardened hot-path optimizations.

v0.38.4 - Pool and storage race fixes#

Fixed:

  • Fixed a race condition during connection pool initialization.

  • Buffered storage streams consistently.

v0.38.3 - Connection lifecycle hooks and migration tracking#

Added:

  • Added on_connection_create lifecycle hooks.

  • Improved migration logging and tracking.

Fixed:

  • Fixed DuckDB variable persistence across connections.

v0.38.2 - Storage paths and logging options#

Added:

  • Added migration use_logger support and SQL logging include_driver_name controls.

Fixed:

  • Fixed storage backend path handling.

  • Avoided blocking behavior in async storage streaming.

v0.38.1 - Python 3.14 and compiled-wheel readiness#

Added:

  • Added Python 3.14 CI coverage and mypyc wheel builds.

Fixed:

  • Fixed driver parameter normalization.

  • Fixed Litestar plugin session-provider behavior.

  • Fixed MySQL build issues.

v0.38.0 - Structured logging and exception mapping#

Added:

  • Added value_type support to select_value methods.

  • Added structured SQL logging context and COMMAND operation logging.

Changed:

  • Added more granular database exception mapping.

v0.37.1 - Column pruning and pagination filters#

Added:

  • Added column-pruning optimization.

Fixed:

  • Fixed pagination parameter filtering.

v0.37.0 - Builder and count-query improvements#

Added:

  • Enhanced query-builder support for count queries.

v0.36.3 - Select helper corrections#

Fixed:

  • Corrected select_with_count and select_only behavior.

v0.36.2 - Exception handler edge cases#

Fixed:

  • Handled additional exception-handler edge cases.

v0.36.1 - DuckDB connection close behavior#

Fixed:

  • Closed DuckDB file-based connections on context-manager exit.

v0.36.0 - Documentation restructure and adapter exceptions#

Changed:

  • Restructured the documentation.

Fixed:

  • Improved exception handling across adapters.

v0.35.0 - SQL class unification, ADK enhancements, and EXPLAIN#

Added:

  • Added dialect-aware EXPLAIN plan support.

  • Added ADK enhancements and EXPLAIN-plan integration.

  • Added type narrowing for parameter-conversion helpers.

Changed:

  • Unified SQL class query modifications and expanded observability support.

  • Simplified the event backend.

  • Reorganized unit and integration tests.

v0.34.0 - Database event channels and utility IDs#

Added:

  • Added the database event channels extension with queue-backed publish/listen APIs and native backend support.

  • Added UUID and ID generation utilities.

Fixed:

  • Moved event configuration to the extension_config pattern.

  • Fixed mypyc signature generation for portal helpers.

v0.33.0 - Config naming, multi-config resolution, and filter additions#

Breaking changes:

  • Standardized adapter config names from pool_config to connection_config and from pool_instance to connection_instance across all adapters.

Added:

  • Added environment-variable and pyproject.toml multi-config resolution for the CLI.

  • Added NullFilter and NotNullFilter.

  • Added URL signing methods to storage object protocols and backends.

  • Simplified add_config() return typing.

Fixed:

  • Fixed AioSQLite 0.22 compatibility after Connection stopped inheriting from Thread.

  • Fixed builder edge cases, SearchFilter empty/None handling, and Update.set() edge cases.

v0.32.0 - Spanner, vector search, and result conversion#

Added:

  • Added the Google Spanner driver.

  • Added vector search support in the query builder.

  • Added result conversion helpers for Arrow, Pandas, and Polars.

  • Added driver fetch* compatibility aliases.

Fixed:

  • Improved BigQuery execute_many bulk inserts.

  • Improved Spanner write handling.

  • Improved async handling for migration commands.

v0.31.0 - Data dictionary and execution correctness#

Added:

  • Added topological sorting and foreign-key retrieval enhancements.

Fixed:

  • Correctly mapped execute_many parameters for all drivers.

  • Fixed returns_row false negatives.

  • Corrected query-builder edge cases and typing.

  • Avoided DuckDB locks in testing documentation examples.

v0.30.2 - Compiled migration path fix#

Fixed:

  • Temporarily removed the migration path that was unsafe for compiled builds.

v0.30.1 - Mypyc and count-query fixes#

Fixed:

  • Fixed mypyc compatibility around dynamic imports and lifecycle dispatcher guard attributes.

  • Validated FROM clauses during count-query generation.

v0.30.0 - Query stack, telemetry, and migration templates#

Added:

  • Added pipelined stack execution.

  • Added telemetry integrations.

  • Added DuckDB community-extension flags.

  • Added improved migration template customization.

Fixed:

  • Fixed Litestar sync context-manager handling.

  • Corrected Oracle JSON support-version lookup.

v0.29.0 - Storage pipelines, connectors, and migration convenience#

Added:

  • Added sync and async storage capabilities and pipelines.

  • Added Google Cloud SQL and AlloyDB connector support.

  • Added Oracle RAW(16) UUID conversion and handlers.

  • Added migration convenience methods to config classes.

  • Added disable_di controls for framework integrations.

Fixed:

  • Fixed migration crashes with null values and malformed regex patterns.

  • Added Decimal JSON encoding support.

  • Improved COPY detection, MERGE behavior, parameter profiles, and config consistency.

v0.28.1 - Empty SQL files and project commands#

Added:

  • Added SQLSpec project agent commands.

Fixed:

  • Improved handling of empty SQL files.

v0.28.0 - Arrow support and additional framework extensions#

Added:

  • Added FastAPI, Starlette, and Flask extensions.

  • Added the Arrow type-system foundation and select_to_arrow() support.

  • Added native Arrow support for ADBC, DuckDB, BigQuery, PostgreSQL adapters, SQLite, MySQL, and Oracle.

  • Added NumPy array serialization through the SQLSpec plugin.

Fixed:

  • Updated ADK store signatures and session-key consistency.

  • Made ADK store SQL table creation asynchronous.

v0.27.0 - ADK sessions, migrations, and Python 3.10 baseline#

Breaking changes:

  • Dropped Python 3.9 support and moved to Python 3.10+ type-hint syntax.

  • Refactored the Litestar extension to remove wrapper classes and unify handlers.

Added:

  • Added SQLSpec documentation, Litestar session backend support, and the Google ADK session backend.

  • Added optional NumPy serialization and Oracle NumPy integration.

  • Added schema_type support to SQLResult helper methods.

  • Added hybrid timestamp/sequential migration versioning, transactional migrations, shell completion docs/tests, and migration author defaults from git config.

Fixed:

  • Improved granular database exception handling and schema conversion caching.

  • Fixed duplicate SQL file loading, migration dry-run handling, CLI path handling, and pgvector registration logging.

  • Added automatic Oracle CLOB hydration for msgspec integration.

v0.26.0 - Data dictionary and async migrations#

Added:

  • Added data-dictionary support for database metadata.

  • Added async migrations and callable config support.

  • Added query-builder FOR UPDATE locking.

  • Added bind_key support to all adapter configs.

Changed:

  • Enhanced serialization, type conversion, sync tooling, and migration infrastructure.

v0.25.0 - Public API and NumPy decoder polish#

Added:

  • Added NumPy decoder support.

Fixed:

  • Correctly handled duplicate use of the same bind parameter.

  • Removed private-variable usage from public APIs.

v0.24.1 - RETURNING clause detection#

Fixed:

  • Correctly detected SQL RETURNING clauses.

v0.24.0 - Builder consolidation#

Added:

  • Added builder support for merged parameter names and OR composition.

Changed:

  • Refactored builder code to reduce duplication.

Previous Versions#

For releases before v0.24.0, see the repository tag history and GitHub release records.