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=Truemarks 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, andSpangresGeneratorsubclasses and expand AST and transpilation support forINTERLEAVE IN PARENTwithON DELETE,TTL,ROW DELETION POLICY,SEARCH/SCORE/SNIPPETS/TOKENLIST,VECTOR_INDEXwithOPTIONS,GRAPH_TABLE,FLOAT32,SAFE_CAST/TRY_CAST,SPANNER.ML_PREDICT_ROW, Spanner sequences, andspangresDDL/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_kwargstake 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 onibm_db. It includes connection pooling, catalog reflection, migrations, and Litestar session, events queue, and Google ADK stores. See Db2. (#811)Added a
db2SQL 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.pyacrossaiomysql,asyncmy,pymysql, andmysqlconnector, and reorganized dialect data-dictionary definitions into dialect-scopedsqlspec/data_dictionary/dialects/<dialect>/packages with shared MySQL data-dictionary base classes. (#821)Simplified OracleDB and Db2 typing and helper boundaries: encapsulated
OraclePipelineDriverprotocol typing, exportedDb2ConnectionParamsandDb2DriverFeatures, removed redundantif not TYPE_CHECKING:typing blocks inoracledbanddb2, 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 INDEXOBJECT_IDguards acrossarrow_odbc,pymssql, andmssql_pythonuse the configured table and generated index names directly soarrow_odbcindex 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 stringconflict_keysvalues and ignore tables absent from subset loads (#825), and support sparse row keys,ignore_unknown_columns, andexclude_update_columnsfor upserts (#826). Column filtering respects case. See Testing. (#827, #829)Quoted and schema-qualified
version_tableidentifiers are preserved across migration trackers and DDL builders (CreateTable,DropTable,AlterTable). Trackers keep unquoted catalog lookup names inversion_table_nameandversion_table_schemawhile 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 INFILEsupport. (#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 EXISTSguards. 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
MERGEfor thedb2dialect. (#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 driverselect()withCursorFilter.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. Usepaginate_limit_offset()orpaginate_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
CursorPaginationresponse container. Malformed cursor inputs raiseInvalidCursorErrorconsistently in Python and compiled installations.Added
CursorKeyandCursorFilterfor 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, orCursorKeyfor 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()insqlspec.utils.config_toolsto tokenize ODBC connection strings with brace escaping per the MS-ODBCSTR specification. (#789)Added
parse_mysql_dsn()insqlspec.utils.config_toolsto 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, andpymysql. (#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
pageSizevalues abovepagination_max_size(default1000); setpagination_max_sizeinFilterConfigto change the limit.SQLSpec-built ordering (
OrderByFilter,SQL.order_by, builderorder_by,Column.asc()/desc(), and window ordering) leaves NULL placement to the database unless requested. It no longer adds implicitNULLS FIRST/NULLS LASTclauses or aCASEsort 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 withOrderByFilter(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): Normalizedconninfo,dsn,url, andconnection_string;database,db, anddbname;userandusername. Redundant driver-incompatible keys are popped before passing to driver constructors. (#792)SQLite, DuckDB & AioSQLite: Normalized
path,db, andfilealiases todatabase, and prevented aliases from leaking into driverconnect()kwargs. (#793)OracleDB & PyMSSQL: Normalized
urlandconnection_stringtodsn, andusernametouserfor OracleDB; mappedhosttoserver,dbtodatabase, andusernametouserfor PyMSSQL. (#794)ADBC: Normalized
url,dsn, andconnection_stringaliases touri. Normalized embedded database path aliases (database,db,path,file) and driver/dialect routing. (#795)Google Cloud Platform (BigQuery & Spanner): Normalized
project_idtoproject, anddataset/database/dbtodataset_idfor BigQuery; normalizedproject_idtoproject,instancetoinstance_id, anddatabase/dbtodatabase_idfor Spanner. Reordered configuration builder definitions before config classes. (#797)MySQL Adapters (
aiomysql,asyncmy,mysqlconnector,pymysql): Normalizedusernametouseranddbtodatabase. (#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. Theextension_configlayout stays the same. ADK vector, BM25, and ScaNN keys move to the asyncpg and psycopg ADK types. BigQuery uses a boolean forpartitioning; Oracle uses a mapping. (#786)Extension stores and event channels reject keys they cannot use. Remove the unused
run_migrationskey from extension settings. Run migrations with the commands andmigration_config. The Eventslistener_queue_capacitykey 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
utf8mb4unless 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
transactionaldirective. (#786)DuckDB
extension_flagsare 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
errorflag when they close, and mapping rows to dictionaries reports a missing column description rather than returning no rows. (#786)sqlspec.exceptions.TransactionRetryErrorandsqlspec.utils.type_guards.has_value_attributeare removed, along withbuild_insert_statement,coerce_records_for_execute_many, andencode_records_for_binary_copyfrom the psqlpy adapter. Serialization failures are reported asSerializationConflictError. (#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
TOPpage-size controls as validated integers while retaining bound data parameters, including queries with CTEs. Nativeselect_to_arrowapplies the same SQL Server pagination controls.SQL.order_by("id", desc=True)now sorts descending, andSelect.order_by("id", desc=True)no longer emits a doubled direction.limit,offset, andpaginateon set operations render valid SQL Server pagination while retaining the requested result ordering.Statement filters and
SQL.where/SQL.order_byapply to the whole result ofUNION,INTERSECT, andEXCEPTqueries, 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_manyconverts tuple rows and mixed placeholder mappings before calling the driver, preserving bindings on cache hits.Filters supplied to the
SQLconstructor 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_manypreserves 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_scriptuse dialect-correct escaped literals. A placeholder without a value now raises instead of rendering asNULL.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
SQLParsingErrorinstead of leaking a sqlglotParseError, 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_IMMEDIATEandDEQ_ON_COMMITnames. The previously advertisedAQMSG_*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
duckdbextra installspyarrow. Minimum versions are raised fororacledb(3.4),psqlpy(0.12.1), andmssql-python(1.13). (#786)
v0.63.1 - Slotted service subclass compatibility#
Fixed:
Keep
SQLSpecAsyncServiceandSQLSpecSyncServiceas 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
SQLSpecAsyncServiceandSQLSpecSyncService, 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_textsearchextension. Includes thePGTextSearchdialect registered undersqlglot.dialects, custom AST operator support for the BM25 relevance ranking operator (<@>), automatic extension detection, andenable_pg_textsearchconfiguration across all PostgreSQL adapters (AsyncPG, Psycopg, ADBC, and PsqlPy).Public
is_postgres_extension_active()helper insqlspec.core.config_runtimeand adapter modules, withactive_extensionscapability tracking on runtime driver features.Exposed
pg_textsearch_availableproperty acrossAsyncpgConfig,PsycopgSyncConfig,PsycopgAsyncConfig,AdbcConfig, andPsqlpyConfig. (#782)Public table-queue primitives extracted to
sqlspec.extensions.events.primitivesand exported fromsqlspec.extensions.events:lock_clause(),row_limit_clause(),select_limit_prefix(), andclaim_verified().SyncTableEventQueueandAsyncTableEventQueuedelegate to them for dialect-aware row-limiting, row-locking, and claim lease verification. (#776)Added
supports_reliable_rowcountcapability flag to configurations, indicating whetherrows_affectedcan be trusted without re-querying (defaults toTrue; set toFalsefor ADBC and arrow-odbc). (#773)SQL Server extension stores and documentation alignment: added
MssqlPythonADKMemoryStoreto complete ADK store parity across SQL Server adapters, added an end-to-end SQL Server recipes guide (SQL Server), and registeredmssql_pythonandpymssqlacross shared integration test suites for Google ADK, durable event queues, and Litestar session stores. ADK migration0002provisions 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-supportedNVARCHAR(MAX); the explicitnative_json=Trueoverride remains available. (#781)Expose
TypeCoercionCapabilitieson all adapter configuration classes as atype_coercion_capabilitiesClassVar, declaring each adapter's datetime binding mode (native,iso_text, ornaive_utc), timestamp precision (microsecond,millisecond, orsecond), JSON column decoding behavior, and UUID binding mode (nativeortext). (#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 anothertransaction()or servicebegin_transaction()block on the same driver uses a savepoint instead of committing. Services allow nestedbegin_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) raiseImproperConfigurationErrorwhen a block is nested. Services also expose publicservice.config, the configuration they were built from (orNonewhen 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.fixturesload and export table datasets withload_table_fixtures_sync/load_table_fixtures_asyncandexport_table_fixtures_sync/export_table_fixtures_async. Each table uses a single<table>.jsonor<table>.jsonlfile (with optional gzip compression). Loading supports table subsets, explicit dependency ordering, batched inserts, upserts onconflict_keys(ON CONFLICT ... DO UPDATEon PostgreSQL-family, SQLite, and DuckDB;ON DUPLICATE KEY UPDATEon 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: namedirectives spliced with/* include: name */, and dynamic fill points via/* slot: name */comments. Slots accept default fallbacks via-- slot: name = defaultand can be populated at load time vialoader.get_sql(name, **slots)orspec.get_sql(name, **slots)using strings, sqlglot expressions, orSQLinstances with parameter merging. See Fragments and Slots. (#763)DuckDB transfers object-store data natively without materializing Python Arrow tables.
load_from_storageissuesINSERT INTO ... SELECT * FROM read_parquet(...)for remote Parquet, andselect_to_storageissuesCOPY (...) 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=Truein driver features.select_to_storageexecutes server-sideEXPORT INTO, returning generated destination filenames in telemetry, andload_from_storageexecutes server-sideIMPORT INTOfor remote Parquet and CSV files with configurablenative_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_primaryon DuckDB, MySQL, and CockroachDB;identity_generationandsequence_nameon PostgreSQL and CockroachDB;column_type,column_key, andextraon MySQL; andis_generatedon DuckDB. On PostgreSQL and CockroachDB,get_columns(table=...)without an explicit schema resolves tables through the sessionsearch_pathinstead of defaulting topublic.SQLSpecChannelsBackendprovides preflight payload budget validation and Prometheus metrics for PostgreSQLNOTIFYenvelopes.measure(data)returns the UTF-8 encoded envelope size,fits(data)verifies compliance againstnotify_budget(derived fromMAX_NOTIFY_BYTES), andmetrics_snapshot()aggregates channel database metrics with queue depth and dropped message counts.AsyncEventChannelandSyncEventChannelalso exposebackend_nameandmetrics_snapshot(). (#756)Litestar plugin registers a default exception handler for
IntegrityErrorand 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_lifespanconfiguration option, governing whether the plugin initializes and disposes driver connection pools during application startup and shutdown. Defaults to the inverse ofdisable_di, allowing external dependency injection containers to reuse SQLSpec pool lifecycles. (#760)Top-level
sqlspecpackage exports identifier generatorsuuid4,uuid6,uuid7, andnanoid.sqlspec.extensions.litestarexportsCorrelationMiddlewareandTRACE_CONTEXT_FALLBACK_HEADERS. Reference documentation now includes supported public APIs insqlspec.utils.text,sqlspec.utils.serializers,sqlspec.utils.correlation, andsqlspec.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 aResolvedStorageTarget(uri, protocol)without requiring an active database session.SQL Server migration runners (
mssql_pythonandpymssql) support default schemas viadefault_schemain migration configuration, using session-level user schema switching with validation againstsys.schemasand 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, andvarcharlogical types across all supported dialects.get_optimal_type()supports an optionallengthparameter for bounded types (such asvarchar), 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 fromextension_configandmigration_config["include_extensions"], rebuilding cached migration commands when found. (#774)
Changed:
Query builder validates dialect capabilities in
build()andto_statement()instead of silently dropping unsupported clauses..for_update()and.for_share()raiseSQLBuilderErroron dialects without these locking clauses (T-SQL, SQLite, DuckDB, BigQuery), andskip_locked=Truevalidates againstsupports_skip_locked..on_conflict()automatically transpiles toON DUPLICATE KEY UPDATEfor MySQL and MariaDB (withdo_nothing()rewriting to self-assignment), while raisingSQLBuilderErrorsuggestingsql.merge()on dialects lacking native upsert support (Oracle, T-SQL, BigQuery). Spanner supports native upserts and plainFOR UPDATE; PostgreSQL-mode upserts validate assignment restrictions. Oracle rejects shared locks, while MariaDB rendersLOCK IN SHARE MODE. Query builderbuild()also normalizes dialect aliases (mssqltotsql,mariadbtomysql, andcockroachdbtopostgres). (#778)Decoupled the
ParadeDBdialect so it inherits directly fromPostgresrather thanPGVector, 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)orexecute(sql, **params)). Drivers still accept a dict, list, or tuple as a positional argument. Existing calls need no changes.execute_manystill 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
CorrelationMiddlewarewhen already configured in the application middleware stack, logging diagnostics if configured header settings differ.Unified
sqlspec.extensions.litestar.LitestarConfigwithsqlspec.config.LitestarConfigfor 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 aValuesbuilder that binds values for bulk row lists. Usesql.column("values")to refer to a column namedvalues. (#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) toUniqueViolationError. (#775)DuckDB adapter maps
TransactionExceptionupdate conflicts ("Conflict on update") toSerializationConflictError, and all other transaction failures toOperationalError. (#775)Fixed SQL Server Litestar session store parameter placeholder and binary conversion in
MssqlPythonStoreandPymssqlStore(CONVERT(VARBINARY(MAX), ?)), removed unsupportedFOR UPDATEhints fromPymssqlConfig.get_event_runtime_hints()for event queue polling, and corrected the documentation feature table and metadata to designatemssql-pythonas 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 descriptiveSQLBuilderErroron dialects without nativeFROMclause support. (#779)CTEs registered via
with_cte()orwith_()render onUpdateandDeletestatements and merge bound parameters without inlining or dropping explicit common table expressions. (#779)Query builder keeps
ON CONFLICT ... DO UPDATEandON DUPLICATE KEY UPDATEassignments in written order. Assignments such asdo_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
SQLobject 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"]issueCREATE [PERSISTENT] SECRET IF NOT EXISTSby default. Existing secrets matching type, provider, and unredacted settings are reused, avoiding connection setup race conditions on shared in-memory databases. Settingreplace=TrueissuesCREATE OR REPLACEfor credential rotation. (#754)PymssqlDriver.begin()no longer issues duplicateBEGIN TRANSACTIONon connections with autocommit disabled, ensuring transaction work commits durably. On autocommit connections,commit()androllback()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
ImproperConfigurationErrorwhen attempting nested transaction blocks instead of sending unsupported DDL. Oracle skips unsupportedRELEASE SAVEPOINTstatements.Litestar routes raising
NotFoundErrorpreserve headers added by route middleware and avoid duplicateafter_exceptionhook 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
PymssqlConfigandMssqlPythonConfigpermit interpreted tracker subclasses under mypyc without raising inheritance TypeErrors. (#747)Query builder
Select.group_by()acceptssql.rollup(),sql.cube(), andsql.grouping_sets()factory expressions directly. Grouping set inputs must be tuples or lists of columns; bare strings raiseSQLBuilderError.MySQL bulk loading accepts
local_infile=Trueorallow_local_infile=Truewithout 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
ExecutionResultNamedTuple type hints.Storage benchmark scripts type-check cleanly under Python 3.10. (#772)
DDL statement builders (
CreateTableandAlterTable) parse column definitions using the effective or target dialect, allowing dialect-specific data types (such as SQL ServerDATETIME2(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_exceptionshelper, superseded by typed per-adapter exception handlers.Dynamic column access on the
sqlfactory (sql.some_column) has been removed. Usesql.column("some_column")orColumn("some_column"). (#771)
v0.62.2 - Litestar config lookup diagnostics#
Fixed:
SQLSpecPlugin.get_config()raisesKeyErrorlisting 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 raisedImproperConfigurationErrorabout 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'spg_searchas the same feature. The ADK migration enablespg_textsearchbefore 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 runCREATE EXTENSION. AlloyDB offers BM25 on PostgreSQL 17 and 18. Use analloydbsuperuserrole 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(), andfits_notify_payload()are exported fromsqlspec.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()andprune_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 overridabledelete_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 acceptorder_by("create_time"or"update_time"),descending,limit, andoffset. The bounds are applied in SQL, so a page no longer reads the whole table into memory before slicing it.limit=0returns an empty result without reaching the database, and a positiveoffsetrequires a finite limit. TheSessionOrderByliteral and thenormalize_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 bybind_keyimmediately 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
idin 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 toupdate_timedescending.prune_sessions(),prune_events(),prune_memory(), andprune_user_state()reject a non-positive retention age.idle_days=0orolder_than_days=0previously resolved to a cutoff of "now" and deleted every row; all five prune helpers now raiseValueError, matchingprune_artifacts().enable_sessionsandenable_memoryare 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_migrationandinclude_memory_migrationare removed from the ADK configuration without an alias or deprecation shim. The first was never read and the second duplicatedenable_memory; useenable_sessionsandenable_memoryinstead. Passing the removed keys raises at configuration construction.The unused destructive
0002_reset_adk_tablesmigration is deleted.0001_create_adk_tablesremains 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 vectorimmediately before the first statement that declares aVECTORcolumn, scoped to PostgreSQL-family dialects. Previously the memory DDL declared vector columns and indexes without installing the extension, failing withtype "vector" does not existon any server where pgvector was not already present. Startup paths such ascreate_tables()andensure_tables()never install extensions.SQLSpecPlugin.get_config()no longer requires application registration to resolve a configuration, and it honors each configuration'sbind_key. Resolving by type raisesKeyErrorlisting 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 atCREATE EXTENSIONrather 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_memorytable stores ascopecolumn ('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 viascope_filter("all","user", or"app").Memory ingestion methods (
add_memories,add_events_to_memory, andadd_session_to_memory) accept ascopeargument (defaulting to'user').New
sqlspec.extensions.adk.maintenancemodule providesprune_events(),prune_sessions(), andprune_user_state()for programmatic retention maintenance with structured deletion reporting.The
adk_eventtable now persistsapp_nameanduser_iddirectly 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 optionalembeddingvector 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, andscann_quantizer.
Changed:
ADK domain record types are modernized to clean domain models:
StoredMemory,StoredSession,StoredEvent, andStoredArtifact. 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, andadk_internal_metadata.ADK table migration
0001_create_adk_tables.pyconsolidates singular table names and thescopecolumn 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 unusedreindexflag have been removed.Oracle ADK storage features (compression, in-memory, partitioning) emit DDL clauses directly without dynamic
v$optionserver probes at runtime.
Fixed:
ADK retention pruning (
delete_entries_older_than) now consistently honorsapp_namefiltering across all stores and database adapters.Arrow conversion now preserves declared SQL / cursor column types for columns containing only
NULLvalues when using psycopg or MySQL drivers (aiomysql, asyncmy, mysqlconnector, pymysql), preventing type collapse tonullorstring.
v0.60.0 - Queue limits#
Added:
The new
listener_queue_capacitysetting caps each PostgreSQL listener queue. It works with asyncpg, psqlpy, and sync or async psycopg listeners. The default has no limit. Bad values raiseImproperConfigurationError.SQLSpecChannelsBackendacceptsoutput_queue_capacity. It caps decoded messages from any async event transport, including PostgreSQL, Oracle, and polling channels. The default has no limit. Bad values raiseValueError.output_queue_depthshows the current Litestar backlog.dropped_message_countshows the total number of overflow drops.PostgreSQL listener metrics now track
events.listener.queue.depthand the totalevents.listener.queue.droppedcount 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
notifydrops transient data. Withnotify_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_queuestores checklistener_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.loadergives 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 implementObjectStoreProtocolmust define it too.
Changed:
Explicit database cancellation now raises
OperationCancelledError. Timeouts and deadlines still raiseQueryTimeoutError. Both exceptions inherit fromOperationalError. Applications that caughtQueryTimeoutErrorfor both outcomes must catch both exceptions, or catchOperationalError.Data dictionary SQL files now live beside each dialect package. SQLSpec loads these files through package resources.
Data dictionary query helpers now use separate
domainandoperationnames.modeis 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
SQLSpecErrorinstead of terminating the process.ADBC adapters for PostgreSQL now keep
Nonein arrays. Each value binds as SQLNULL. 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 acollections.abc.Coroutinereturn 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 withStoreRegistry.Each extension now loads its SQL migration query names on its own. A file such as
0001_create_table.sqlcan usemigrate-0001-upandmigrate-0001-downwith 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 numericline_numberand textualdirectiveseparately. Non-strict loading continues to warn and skip malformed directives, while strict loading continues to raiseSQLFileParseError.
v0.58.1 - Migration template configuration#
Fixed:
migration_configaccepts the three keys that customize generated migration files:templates,default_format, andtitle. The key validation added in v0.58.0 did not recognize them, so any configuration using a customized migration template raisedImproperConfigurationErrorat construction.default_formatwas additionally reported with a suggestion to usedefault_schema, an unrelated setting.Template overrides are validated when the configuration is built rather than when a migration is generated. A misspelling inside
templates.sqlortemplates.pyreports its full path and the closest valid key, and an override that is not a mapping is named along with the type supplied.MigrationConfigdocuments the real default forversion_table_name, which isddl_migrations.
Added:
MigrationTemplates,SQLTemplateOverride, andPythonTemplateOverridedescribe the template override shape, so type checkers now cover it.The migrations guide documents template customization, including the placeholders available to each fragment.
v0.58.0 - Configuration and storage correctness#
Changed:
migration_confignow rejects keys SQLSpec does not read, raisingImproperConfigurationErrorwith the closest valid key. A misspelling such asversion_tableinstead ofversion_table_namewas 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
StorageCapabilityErrorbefore any encoding or storage I/O. Row writes accept onlyjsonandjsonl; Arrow table writes accept onlyparquet,arrow-ipc, andcsv. A mismatched format previously wrote one payload type under another format label. Read APIs still accept all five formats.stream_arrow_sync()andstream_arrow_async()accept onlyfile_format="parquet"and raiseStorageCapabilityErrorfor 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_sizebounding each record batch. The obstore backend streams through its seekable reader instead of buffering the whole object in memory, resolves cloudbase_pathonly 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
ROLLBACKstatement 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 withINVALID_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
--configat a module rather than a configuration object now reports themodule:attributereferences that module exports, instead of failing later withAttributeError: 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.authoris declared onMigrationConfig. 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 overtakeexecutemany()throughput.
v0.57.0#
Added:
Packages distributed separately from SQLSpec can now ship Python migrations. Set
migrations_pathin anextension_configentry to point at a directory or a'<dotted.module>:<subdir>'specification. The extension is discovered and auto-included without appearing ininclude_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.sqlpreviously resolved to the versionext_my_extension, dropping0001and 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 namedNone.Compiled wheels now return correct results from
isinstance()andissubclass()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
TypeErrorwhile resolving the default schema when no configuration is attached.
Changed:
sqlspec.utils.module_loader.module_to_os_path()raisesModuleNotFoundErrorrather thanTypeErrorwhen a module cannot be found, so callers can catch the real condition.SQLSpec base classes no longer use
ABCMetaat runtime because mypyc shares its abstract-base caches across compiled class hierarchies. Static type checkers still enforce abstract methods. Runtime code should not rely oninspect.isabstract()or abstract-class instantiation errors for these bases.StatementResultremains 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(), anduuid_from_int()insqlspec.utils.uuids. They always returnuuid.UUID. Text parsing uses Rust whenuuid-utilsis installed.
Fixed:
PostgreSQL-family ADBC row APIs now decode scalar
UUIDandUUID[]data to Python UUIDs. This works for buffered and streamed rows. Native Arrow results keep the extension schema, andenable_arrow_extension_types=Falserestores raw storage bytes on row APIs.Oracle 12c through 20c can bind direct Python JSON values to
BLOB IS JSONcolumns. SQLSpec writes UTF-8 JSON to a BLOB locator for sync, async, batch, and streaming calls. Oracle 21c and newer still use nativeJSONbinding. ExplicitOracleClobvalues 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.uuidsacross 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
MERGEupsert recipe.The ADBC guide now explains UUID row and Arrow results. It also documents the
enable_arrow_extension_typesswitch.
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.EXPLAINstatements now return their query plan. Explained statements were classified as non-row-returning, so drivers ran theEXPLAINand then discarded the plan, leavingselect()andexecute()with no rows. This affected every adapter except DuckDB, ADBC, and BigQuery.SHOWandDESCRIBEare now classified the same way. Oracle.explain()calls now tag the plan-table entry, returnDBMS_XPLANrows, and remove the entry before returning. Raw caller-ownedEXPLAIN PLANstatements 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 raisedTypeErrorand now accepts an override.TABLE table_namestatements now return their rows on PostgreSQL, DuckDB, and MySQL. SQLGlot does not currently model this shorthand forSELECT * 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_manybatches. Bound values are unchanged.Binding a UUID array containing
Nonenow raises an explicit error. The PostgreSQL ADBC driver encodes null array elements as empty strings, which PostgreSQL rejects forUUID[]; the previous behavior was an opaqueinvalid input syntax for type uuidfailure 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
SchemaTargetandSchemaEnsureResulttypes plus sync and async schema checks can create missing tables and add columns. Useensure_schema_sync()orensure_schema_async()for each driver mode. Stores usemanage_schemaandcreate_schemafor 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
sqlglotandsqlglot[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_versionrow 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
uuidvalues 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
STRINGorTIMESTAMPtypes, 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_pythontransactions now use the driver's DBAPI transaction state, persist committed work, roll back pending work, and restore the connection's original autocommit mode. (#642)psqlpynow reports exact affected-row counts for non-returning single-row and multi-rowINSERT,UPDATE, andDELETEstatements while preserving the existingRETURNINGresult path. (#645)mssql_pythonnow materializes cached result rows as real tuples, matching its declared result format. (#630)mssql_pythonandpymssqlnow classify SQL Server constraint messages even when the native driver omits or embeds the numeric error code, andpymssqltranslates named pyformat input to its reliable positional execution style.
v0.55.0#
Breaking changes:
SQLite and aiosqlite connections now follow the stdlib
sqlite3default by leavingPRAGMA foreign_keysdisabled unlessconnection_config={"enable_foreign_keys": True}is passed. Both adapters now share a 5000 ms busy timeout and aligned optimization PRAGMAs when the defaultenable_optimizations=Truesetting is active.Began replacing the old narrow data-dictionary interface with a consistent metadata contract based on
MetadataCapabilityProfile,MetadataCapability,MetadataResult,ObjectIdentity, andDDLResult. This is a pre-1.0 breaking change: structural domain lookups return result envelopes, object DDL lookups returnDDLResultdirectly, 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, andtxeventq. 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_intervalfor durable event reconciliation, independently of native listener wakeups.poll_intervalremains 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_queuebatch 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_backenddriver 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=Truewhen application code needs native Oracle LOB locators. Unconstrained LOB contents are no longer parsed with content heuristics; nativeJSON,IS JSONCLOB/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 OFclauses is handled by SQLGlot generation rather than post-render SQL rewriting.adbcandarrow-odbcconfigs now honoron_connection_createdriver 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-pythonconnection pool implementation now lives in its adapter pool module while preserving the existing public import.mssql_pythonstack execution no longer raises the base_connection_in_transaction()error before applying batched statements.arrow-odbcSQL Server transactions now rely on the connection commit/rollback API instead of sending a rawBEGIN TRANSACTIONstatement, 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_pathtwice for async streams.sql.decode()now renders a trailing default argument as theELSEclause 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
errnoattribute as well as positional error arguments.Count-query generation no longer infers a missing outer
FROMfrom 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=Truenow overrides a builder created with optimization disabled.where_in()now binds plain string values as scalar parameters, matchingwhere_not_in()and the OR helper variants.Documentation builds now filter the known
pymssqlstub-onlyQueryParamsguarded-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
pymssqlSQL 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_odbcadapter 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, andarrow_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
pymysqland AlloyDB for syncpsycopg.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
aqandtxeventq;poll_queueremains 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, andstarlette.
Fixed:
Removed the obsolete
aioodbcextra and added docs/package parity checks so the installation guide matches available extras.Corrected
pymysqlstack 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-adk2.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(), andupdate_session_state()on the store now requireapp_nameanduser_idin addition tosession_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_jsontoevent_dataon every ADK adapter store.Session state is split into scoped tables. Alongside
adk_sessionandadk_event, stores now manageadk_app_state,adk_user_state, andadk_internal_metadata.Migration
0002_reset_adk_tablesis 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 managedThreadPoolExecutorby default instead of delegating to the event loop's default executor throughasyncio.to_thread(). Configure the worker limit withSQLSPEC_ASYNC_THREAD_LIMITorenable_default_async_thread_pool().
Added:
Typed environment parsing helpers in
sqlspec.utils.env.ThreadPoolExecutorsupport forsqlspec.utils.sync_tools.async_(), plus bounded async bridge controls throughSQLSPEC_ASYNC_THREAD_LIMIT,enable_default_async_thread_pool(),set_default_async_executor(),get_default_async_executor(), andshutdown_default_async_executor().Scoped-state accessors on every ADK store:
get_app_state,get_user_state,upsert_app_state,upsert_user_state,get_metadata, andset_metadata.append_event_and_update_state()accepts optionalapp_stateanduser_statedeltas and applies them atomically with the session and event write, returning the updatedStoredSession.
Fixed:
Preserved
contextvarswhenasync_()routes sync work through explicit or shared thread executors.Removed a dead
storage_urikey from the artifact-store config normalization; the artifact storage URI is supplied toADKArtifactServicethrough its constructor and was never read from the store config.DMLResult.all()andone_or_none()no longer raiseAttributeErrorwhen called withschema_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_parametersno longer raisesTypeErrorfor named parameters with unhashable values (for examplesetorbytearray); such values now fall back to a stablerepr-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
VECTORpassthrough.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_transactioncontext 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.deepcopysupport for providers.
v0.48.1 - deepcopy and pickle for compiled value objects#
Fixed:
Supported
copy.deepcopyandpickleon mypyc-compiled value objects.
v0.48.0 - Arrow ODBC and mssql-python adapters, migration schemas#
Added:
New
arrow_odbcandmssql_pythonadapters.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(), andget_collection_serializer()now defaultwire_format=False. Msgspec structs withrename=now emit Python attribute names by default, matching Pydantic, dataclasses, and attrs. Passwire_format=Trueto keep wire-aligned names.Third-party ADK stores implementing
append_event_and_update_state()must return the updatedStoredSession.Data dictionary metadata/version helpers now live under
sqlspec.data_dictionary.ColumnMetadata,ForeignKeyMetadata,IndexMetadata,TableMetadata,VersionInfo, andVersionCacheResultare no longer exported fromsqlspec.typingorsqlspec.core.Removed modernization compatibility shims and deprecated helpers. Use
SQL.raw_sqlinstead ofSQL.sql,CorrelationContext.context()instead ofsqlspec.utils.correlation.correlation_context(),MSSQL_CONFIG.default_schemainstead ofresolve_mssql_default_schema(),Insert.values_from()andInsert.values_from_many()instead ofInsert.values_from_dict()andInsert.values_from_dicts(),clear_all_caches()orreset_stats_only()instead ofreset_cache_stats()orSQLSpec.reset_cache_stats(), andlen(cache)instead ofLRUCache.size(). Oracle session callbacks are now always installed, sorequires_session_callback()was removed.Removed filter compatibility APIs.
PaginationFilterandcreate_filters()are gone,LimitOffsetFilternow subclassesStatementFilter, andOrderByFilterrejects invalidsort_ordervalues instead of silently coercing them toasc.Tightened parameter and serializer helpers.
ParameterStyleConfig.hash()was removed in favor ofhash(config).build_null_pruning_transform()andreplace_null_parameters_with_literals()no longer acceptvalidator=and require an explicitparameter_profilefor non-empty parameter sets.build_time_iso_converter()was replaced by the sharedtime_iso_converthelper.Operation/result semantics changed.
OperationTypeno longer includesUNKNOWN; parse fallback now usesCOMMAND.SQLResultoperation helpers now use canonical operation values directly, andcreate_sql_result()exposes explicit keyword arguments instead of accepting arbitrary**kwargs.SQLFileLoader.get_sql()now compiles named statements on lookup and returns the cachedSQLobject for repeated normalized names untilclear_cache()is called.Result and adapter internals dropped importable compatibility helpers:
sqlspec.core.result._ioand itsrows_to_pandas()/rows_to_polars()helpers,ArrowOdbcTypeConverter,BQ_TYPE_MAP,DuckDBOutputConverter.convert_duckdb_value(),DuckDBOutputConverter.prepare_duckdb_parameter(), andpsqlpy.normalize_scalar_parameter().Oracle cleanup removed
OracleVectorTypeand the legacyOracleOutputConverter.detect_json_storage_type(),OracleOutputConverter.format_datetime_for_oracle(),OracleOutputConverter.handle_large_lob(), andOracleOutputConverter.convert_oracle_value()helper methods.Migration internals moved.
BaseMigrationRunneris no longer exported fromsqlspec.migrations.base; import it fromsqlspec.migrations.runnerif subclassing migration runners.PyMySQL no longer unwraps
connection_config["extra"]into raw driver keyword arguments; pass driver kwargs directly inconnection_config.Several public implementation classes are now marked
@finalfor typing/mypyc correctness. Downstream subclasses of these classes will fail static type checking. Affected classes include driver/converter internals such asAdbcDriver,AdbcExceptionHandler,BigQueryOutputConverter,DuckDBOutputConverter,SpannerOutputConverter, builder wrapper/factory types,JoinBuilder,SQLFactory,OperationProfile,CompiledSQL,SQLProcessor, dialect config classes,CachedQuery,QueryCache, event message/queue types, andMigrationVersion.Performance cleanup tightened additional compatibility-sensitive contracts: storage
backend_typeis a class attribute, parameter builders exposegenerate_unique_parameter_name(), statement observers are protocol based, and legacy aliases such asBackendNotRegisteredErrorwere removed.
Added:
Added
Insert.values_from(),Insert.values_from_many(), andUpdate.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_destroyinglifecycle 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, andon_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 Oraclepoll_intervalsettings.Honored builder optimization flags by wiring explicit sqlglot optimizer rules, so
optimize_joins,optimize_predicates, andsimplify_expressionsnow disable only their matching steps instead of always running the full default pipeline.Passing a sqlglot
Dialectclass to EXPLAIN builders orStatementConfig.dialectnow 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.Versionnodes should callsqlspec.builder.register_version_generators()before rendering them.Routed async pool teardown through the base config lifecycle path so
on_pool_destroyandon_pool_destroyingfire consistently across async adapters.Registered binary
jsonandjsonbcodecs 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
orderByaliases 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
librtto theperformanceextra 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 mutatesapp_config.pluginsin 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=uploadedCollectionswhen 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()andget_one()whenschema_typeis provided.Extracted
DEFAULT_TYPE_ENCODERSand applied them through the Litestar plugin while preserving user encoder precedence.Added Litestar decoders for NumPy arrays and
uuid_utils.UUIDvalues.
v0.45.0 - Services, filters, Oracle types, and Sanic#
Added:
Added first-party
SQLSpecAsyncServiceandSQLSpecSyncServicehelpers 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
SearchFilterandNotInSearchFiltervalidation so unsupported field names fail instead of silently dropping predicates.Fixed raw
ORDER BYhandling 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
aiomysqladapter 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
OffsetPaginationto a stdlib dataclass while keeping the public import path intact.
Fixed:
schema_dump()now honors msgspecrename=metadata for wire-format output.OffsetPaginationpreserves 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.dialectspackage.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
SearchFilterplaceholder 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
*_syncpattern.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_createlifecycle 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_loggersupport and SQL logginginclude_driver_namecontrols.
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_typesupport toselect_valuemethods.Added structured SQL logging context and
COMMANDoperation 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_countandselect_onlybehavior.
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
EXPLAINplan 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_configpattern.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_configtoconnection_configand frompool_instancetoconnection_instanceacross all adapters.
Added:
Added environment-variable and
pyproject.tomlmulti-config resolution for the CLI.Added
NullFilterandNotNullFilter.Added URL signing methods to storage object protocols and backends.
Simplified
add_config()return typing.
Fixed:
Fixed AioSQLite 0.22 compatibility after
Connectionstopped inheriting fromThread.Fixed builder edge cases,
SearchFilterempty/Nonehandling, andUpdate.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_manybulk 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_manyparameters for all drivers.Fixed
returns_rowfalse 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
FROMclauses 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_dicontrols for framework integrations.
Fixed:
Fixed migration crashes with null values and malformed regex patterns.
Added Decimal JSON encoding support.
Improved
COPYdetection, 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_typesupport toSQLResulthelper 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 UPDATElocking.Added
bind_keysupport 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
RETURNINGclauses.
v0.24.0 - Builder consolidation#
Added:
Added builder support for merged parameter names and
ORcomposition.
Changed:
Refactored builder code to reduce duplication.
Previous Versions#
For releases before v0.24.0, see the repository tag history and GitHub
release records.