diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 3ce770c73..81af3b486 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -65,7 +65,7 @@ jobs:
docs/*|zensical.toml|CONTRIBUTING.md|tools/docs/*|.github/workflows/docs.yml)
docs_required=true
;;
- sqlspec/*|pyproject.toml|uv.lock|.github/workflows/ci.yml)
+ sqlspec/*|pyproject.toml|uv.lock|Makefile|.github/workflows/ci.yml)
ci_required=true
docs_required=true
;;
@@ -548,12 +548,13 @@ jobs:
run: uv python install 3.12
- name: Install dependencies
- run: uv sync --group doc
+ run: uv sync --no-default-groups --group doc
- name: Verify public docstrings and CLI reference
run: |
uv run --no-sync python -m tools.docs.scan_public_docstrings --check-leaks --check-overloads --check-typeddicts sqlspec
uv run --no-sync python -m tools.docs.generate_cli_docs --check
+ uv run --no-sync python -m tools.docs.generate_data_dictionary_docs --check
- name: Build docs
run: make docs
diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml
index 92af2da45..1fa53b1f6 100644
--- a/.github/workflows/docs.yml
+++ b/.github/workflows/docs.yml
@@ -36,7 +36,7 @@ jobs:
run: uv python install 3.12
- name: Install dependencies
- run: uv sync --group doc
+ run: uv sync --no-default-groups --group doc
- name: Build documentation
run: make docs
diff --git a/Makefile b/Makefile
index 4c070025d..448080bd0 100644
--- a/Makefile
+++ b/Makefile
@@ -326,23 +326,18 @@ docs: docs-clean ## Build documentation
@echo "${INFO} Building documentation... 📝"
@uv run --no-sync python -m tools.docs.scan_public_docstrings --check-leaks --check-overloads --check-typeddicts sqlspec
@uv run --no-sync python -m tools.docs.generate_cli_docs --check
+ @uv run --no-sync python -m tools.docs.generate_data_dictionary_docs --check
@uv run --no-sync zensical build --clean
@uv run --no-sync python -m tools.docs.postbuild --config zensical.toml --docs-dir docs --site-dir site
@uv run --no-sync python -m tools.docs.verify_docs_artifacts --site-dir site
@echo "${OK} Documentation built successfully"
-.PHONY: docs-check
-docs-check: docs ## Build and verify documentation artifacts
-
.PHONY: docs-linkcheck
docs-linkcheck: ## Check documentation links and anchors
@echo "${INFO} Checking documentation links... đź”—"
@uv run --no-sync python -m tools.docs.verify_docs_artifacts --site-dir site
@echo "${OK} Link check complete"
-.PHONY: docs-linkcheck-full
-docs-linkcheck-full: docs-linkcheck ## Run full documentation link check
-
.PHONY: docs-demos
docs-demos: ## Generate CLI demo GIFs from VHS tapes
@if ! command -v vhs >/dev/null 2>&1; then \
diff --git a/README.md b/README.md
index 77dae4795..4c255cdde 100644
--- a/README.md
+++ b/README.md
@@ -82,7 +82,7 @@ with spec.provide_session(config) as session:
| Area | Overview |
| --- | --- |
| **Unified Execution** | Consistent `execute`, `select`, `select_one`, `select_one_or_none`, `select_value`, `execute_many`, `select_stream`, and transaction APIs across sync and `asyncio` drivers. |
-| **Database Adapters** | PostgreSQL (`asyncpg`, `psycopg`, `psqlpy`), CockroachDB, SQLite (`sqlite3`, `aiosqlite`), DuckDB, MySQL & MariaDB, Oracle, SQL Server, Db2, BigQuery, Cloud Spanner, ADBC, and Arrow ODBC. |
+| **Database Adapters** | PostgreSQL (`asyncpg`, `psycopg`, `psqlpy`), CockroachDB (`asyncpg`, `psycopg`), SQLite (`sqlite3`, `aiosqlite`), MySQL & MariaDB (`asyncmy`, `aiomysql`, `pymysql`, `mysql-connector-python`), Oracle (`oracledb`), SQL Server (`mssql-python`, `pymssql`), Sybase ASE (`pymssql`), SAP SQL Anywhere (`sqlanydb`), IBM Db2 (`ibm_db`), DuckDB (`duckdb`), BigQuery (`google-cloud-bigquery`), Cloud Spanner (`google-cloud-spanner`), ADBC (`adbc-driver-manager`), and Arrow ODBC (`arrow-odbc`). |
| **SQL Files & Builder** | Load named queries from `.sql` files via `SQLFileLoader` or build statements with `sql.select`, `sql.insert`, `sql.update`, `sql.delete`, and `sql.merge` with cached AST compilation. |
| **Typed Rows & Arrow** | Project rows into `msgspec.Struct`, `Pydantic`, `dataclass`, `TypedDict`, or `attrs` models, or stream `pyarrow`, `polars`, and `pandas` datasets across local, `fsspec`, and `obstore` backends. |
| **Integrations & Ops** | First-party plugins and stores for Litestar, Google ADK, FastAPI, Starlette, Flask, Sanic, Dishka, and Events, plus OpenTelemetry spans, Prometheus metrics, and CLI migrations. |
diff --git a/docs/changelog.md b/docs/changelog.md
index 7ae9e7884..d077efe62 100644
--- a/docs/changelog.md
+++ b/docs/changelog.md
@@ -7,6 +7,44 @@ important operational fixes.
## Recent Updates
+### v0.70.0 - Sybase ASE and SAP SQL Anywhere adapters, streaming Arrow ingestion, canonical DDL synthesis, sharded fixtures, and documentation overhaul
+
+**Added:**
+
+* Added Sybase Adaptive Server Enterprise (`sqlspec.adapters.sybase` with `SybaseSyncConfig` and `SybaseSyncDriver`) and SAP SQL Anywhere (`sqlspec.adapters.sqlanywhere` with `SqlAnywhereSyncConfig` and `SqlAnywhereSyncDriver`) database adapters, custom SQLGlot dialects (`Sybase` and `SQLAnywhere` in `sqlspec.dialects.sybase`), data dictionaries (`SybaseSyncDataDictionary` and `SqlAnywhereSyncDataDictionary` with `SYBASE_CONFIG` and `SQLANYWHERE_CONFIG`), type converters (`SybaseOutputConverter` and `SqlAnywhereOutputConverter`), and extension stores for Litestar sessions (`SybaseStore`, `SqlAnywhereStore`), Event queues (`SybaseEventQueueStore`, `SqlAnywhereEventQueueStore`), and Google ADK sessions and memory (`SybaseADKStore`, `SybaseADKMemoryStore`, `SqlAnywhereADKStore`, `SqlAnywhereADKMemoryStore`).
+ ([#870](https://github.com/litestar-org/sqlspec/pull/870))
+* Added `arrow_odbc` dialect auto-detection and `information_schema` / system catalog introspection for Sybase ASE (`Adaptive Server Enterprise` / `ASE`) and SAP SQL Anywhere (`SQL Anywhere` / `Sybase IQ`), routing detected connections to the `sybase` and `sqlanywhere` data dictionaries.
+ ([#870](https://github.com/litestar-org/sqlspec/pull/870))
+* Added streaming Apache Arrow batch ingestion (`iter_arrow_batches`), PostgreSQL binary `COPY` framing (`encode_arrow_batch_pg_binary`, `iter_pgcopy_binary_chunks`, `iter_pgcopy_binary_chunks_async`), staged `MERGE` / `ON CONFLICT` upserts (`execute_staged_load_sync`, `execute_staged_load_async`) across `asyncpg`, `psycopg`, `psqlpy`, `oracledb`, `duckdb`, `sqlite`, `aiosqlite`, `bigquery`, `spanner`, `mssql_python`, `pymssql`, `db2`, and MySQL adapters, and bulk-load secondary index drop/recreate lifecycle helpers (`disable_secondary_indexes`, `rebuild_secondary_indexes`) on `SyncDriverAdapterBase` and `AsyncDriverAdapterBase`.
+ ([#868](https://github.com/litestar-org/sqlspec/pull/868))
+* Added canonical DDL squashing (`MigrationSquasher.fold_canonical_ddl`, `sqlspec squash --canonical`), live database schema dumping (`MigrationCommands.dump_schema`, `sqlspec dump-schema` with `--dialect`, `--schema`, `--include-domains`, `--exclude-domains`, `--exclude-tables`, and `--create-migration`), and data-driven DDL synthesis (`CreateTable.from_metadata`, `CreateTable.from_arrow_schema`, `CreateTable.from_records`, `CreateIndex.from_metadata`, `compile_table_ddl_results`, `get_schema_ddl`, and `profile_table`).
+ ([#862](https://github.com/litestar-org/sqlspec/pull/862))
+* Added sharded fixture loading (`find_fixture_files`, `load_fixture`, `iter_fixture_records`, `write_fixture_shards`), vector embedding column serialization across PostgreSQL (`pgvector`), Oracle (`VECTOR`), DuckDB, BigQuery, Spanner, and SQLite, and cross-adapter primary key sequence resynchronization (`sync_table_sequences`, `sync_table_sequences_async`) after explicit-ID fixture loads.
+ ([#863](https://github.com/litestar-org/sqlspec/pull/863))
+* Added generated Data Dictionary dialect and SQL query reference documentation (`docs/reference/data-dictionary/dialects/` via `tools.docs.generate_data_dictionary_docs`) covering all 13 dialect packs (`bigquery`, `cockroach`, `db2`, `duckdb`, `mssql`, `mysql` / `mariadb`, `oracle`, `postgres`, `spanner`, `sqlanywhere`, `sqlite`, and `sybase`), 214 SQL query files, and 336 named catalog queries.
+
+**Changed:**
+
+* Replaced correlated per-column scalar subqueries in data dictionary `columns.sql` and `indexes.sql` catalog queries across `postgres`, `cockroach`, `mssql`, `mysql`, `oracle`, `db2`, and `duckdb` with pre-aggregated CTEs and joins, and reduced `ColumnMetadata` to its 18 core catalog fields.
+ ([#866](https://github.com/litestar-org/sqlspec/pull/866))
+* Redesigned the documentation site architecture around Zensical and `mkdocstrings-python` with Diataxis-aligned navigation (`Getting Started`, `Learn`, `Adapters`, `Extensions`, `Examples`, `Reference`), interactive WebAssembly SQL playground, runnable `docs/examples/` snippets, conceptual Mermaid diagrams, and tabbed package manager and `SQLFileLoader` examples.
+ ([#864](https://github.com/litestar-org/sqlspec/pull/864),
+ [#867](https://github.com/litestar-org/sqlspec/pull/867),
+ [#869](https://github.com/litestar-org/sqlspec/pull/869))
+* Migrated integration test infrastructure to clientless `pytest-databases` fixtures (`#859`).
+ ([#859](https://github.com/litestar-org/sqlspec/pull/859))
+* Upgraded CI GitHub Actions `astral-sh/setup-uv` from `10.3.1` to `10.3.3` and pinned `actions/checkout` (`6.0.2`), `actions/setup-python` (`7.1.0`), `actions/cache` (`6.0.0`), and `actions/upload-pages-artifact` / `actions/deploy-pages` in `.github/workflows/ci.yml` and `.github/workflows/docs.yml`.
+ ([#872](https://github.com/litestar-org/sqlspec/pull/872))
+
+**Fixed:**
+
+* Removed runtime `duckdb` imports from `sqlspec.adapters.adbc` (`AdbcDataDictionary` and `sqlspec/adapters/adbc/core.py`) and enforced strict adapter import isolation across all 20 database adapters in `tests/unit/test_import_time.py`.
+ ([#865](https://github.com/litestar-org/sqlspec/pull/865))
+* Eliminated extra row copies and `UserWarning` in `DuckDBDriver.select_to_arrow` on DuckDB 2.0+ by routing parameterless `SELECT` queries through `connection.sql(query).to_arrow_table()` and `to_arrow_reader(batch_size=...)` when no open DB-API transaction is active on the connection.
+* Fixed `compile_table_ddl_results` and `CreateIndex` cross-dialect DDL synthesis to emit `COMMENT ON` only on dialects that support it (`postgres`, `oracle`, `db2`, `duckdb`, `snowflake`), inline `COMMENT` clauses on `mysql` and `mariadb`, render `STORING (...)` on `spanner` and `spangres`, fold unquoted source identifiers to target case before quoting target-only reserved keywords, and preserve SQLite `NUMERIC` type affinity.
+* Fixed `MigrationSquasher.fold_canonical_ddl` to fall back cleanly to raw statement concatenation when a migration contains interleaved DML or procedural blocks (`INSERT`, `UPDATE`, `DELETE`, `MERGE`, `COPY`, `DO`, `CALL`), preserve table-level constraints across `ALTER TABLE ... RENAME COLUMN` and `DROP COLUMN`, and update `sqlspec_migrations` tracker history atomically inside a single transaction when applying squashed migrations.
+* Fixed `write_fixture_shards` to unlink stale shard and legacy single-file artifacts (`
.json`, `.json.gz`, and higher-numbered shards) before writing new shards so subsequent `load_fixture` calls do not read orphaned records.
+
### v0.69.0 - Oracle PKCS#12 wallet conversion, TNS alias resolution, proximity RRF, and server-side DBMS_CLOUD ingestion
**Added:**
diff --git a/docs/databases/adbc.md b/docs/databases/adbc.md
index 0b49f6a45..0ecf99aca 100644
--- a/docs/databases/adbc.md
+++ b/docs/databases/adbc.md
@@ -6,9 +6,29 @@
Install the core `adbc` extra alongside one or more PyPI-installable ADBC wire drivers:
-```bash
-uv add "sqlspec[adbc]" adbc-driver-postgresql
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[adbc]" adbc-driver-postgresql
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[adbc]" adbc-driver-postgresql
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[adbc]" adbc-driver-postgresql
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[adbc]" adbc-driver-postgresql
+ ```
| Target Engine | PyPI Package | Supported URI Schemes | Short `driver` Aliases |
| --- | --- | --- | --- |
@@ -104,6 +124,7 @@ with spec.provide_session(config) as session:
| --- | --- | --- |
| `driver` / `driver_name` / `uri` | `connection_config` | Short driver alias (`driver="duckdb"`, `driver="snowflake"`, `driver="bigquery"`), explicit module name (`driver_name="adbc_driver_duckdb"`), or connection URI with auto-detected scheme (`duckdb://`, `snowflake://`, `bigquery://`, `postgresql://`, `oracle://`, `db2://`, `quack://`, `grpc+tls://`). |
| `username` / `password` / `token` | `connection_config` | Credentials lifted automatically into each driver's expected `db_kwargs` (`adbc.quack.token`, Oracle `db_kwargs`, etc.). |
+| `auth_token_provider` | `driver_features` | Callable invoked when opening ADBC connections to supply fresh IAM, OAuth, or Bearer tokens dynamically. |
| `auth_type` / `tls_root_certs` / `headers` | `connection_config` | FlightSQL and GizmoSQL OAuth/SSO (`auth_type="external"`), mTLS, and gRPC options (`grpc_max_message_size`, `query_timeout`). |
| `db_kwargs` / `conn_kwargs` | `connection_config` | Low-level ADBC database and connection option dictionaries. |
| `enable_lowercase_column_names` | `driver_features` | Lowercases uppercase unquoted identifiers (`True` by default when `dialect` is `"oracle"` or `"db2"`). |
diff --git a/docs/databases/arrow-odbc.md b/docs/databases/arrow-odbc.md
index a1e62b1c1..071b82bf7 100644
--- a/docs/databases/arrow-odbc.md
+++ b/docs/databases/arrow-odbc.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[arrow-odbc]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[arrow-odbc]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[arrow-odbc]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[arrow-odbc]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[arrow-odbc]"
+ ```
Choose `ArrowOdbcConfig` when your environment standardizes on enterprise unixODBC / Windows ODBC drivers (such as `ODBC Driver 18 for SQL Server`, `psqlodbc`, or IBM Db2 `libdb2o.so`) and needs fast, bounded-memory Arrow batch extraction without row-by-row `pyodbc` tuple overhead.
diff --git a/docs/databases/bigquery.md b/docs/databases/bigquery.md
index 3de3fede3..05aa60e0e 100644
--- a/docs/databases/bigquery.md
+++ b/docs/databases/bigquery.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[bigquery]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[bigquery]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[bigquery]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[bigquery]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[bigquery]"
+ ```
Choose `BigQueryConfig` for serverless warehouse queries, multi-statement GoogleSQL procedural scripts, cost-governed analytical APIs (`maximum_bytes_billed`, `reservation`), and streaming Arrow/Parquet lakehouse pipelines.
diff --git a/docs/databases/cockroachdb.md b/docs/databases/cockroachdb.md
index e8955de2f..1c994e50d 100644
--- a/docs/databases/cockroachdb.md
+++ b/docs/databases/cockroachdb.md
@@ -4,9 +4,29 @@ SQLSpec provides two dedicated CockroachDB adapters—`sqlspec.adapters.cockroac
## Install and When to Choose
-```bash
-uv add "sqlspec[cockroachdb]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[cockroachdb]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[cockroachdb]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[cockroachdb]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[cockroachdb]"
+ ```
- Choose **`CockroachAsyncpgConfig`** for pure-`asyncio` services prioritizing binary-protocol performance.
- Choose **`CockroachPsycopgSyncConfig`** or **`CockroachPsycopgAsyncConfig`** when sharing sync/async pools or libpq TLS/GSSAPI certificate options.
@@ -72,6 +92,7 @@ uv add "sqlspec[cockroachdb]"
| `default_staleness` | `str` | Follower-read staleness expression (such as `"follower_read_timestamp()"` or `"-10s"`). |
| `enable_native_storage` | `bool` | Uses CockroachDB native CSV/nodelocal/cloud storage import/export paths where configured. |
| `enable_pgvector` | `bool` | Enables CockroachDB `VECTOR` column codecs and similarity search in ADK stores (`cockroach_asyncpg`). |
+| `auth_token_provider` | `Callable` | Dynamic credential callback invoked per connection creation for short-lived cluster OAuth/IAM tokens. |
See [Cockroach AsyncPG Reference](/reference/adapters/cockroach-asyncpg/) and [Cockroach Psycopg Reference](/reference/adapters/cockroach-psycopg/) for full configuration and retry TypedDict tables.
diff --git a/docs/databases/db2.md b/docs/databases/db2.md
index 6c99ed4b2..0d31eeb31 100644
--- a/docs/databases/db2.md
+++ b/docs/databases/db2.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[db2]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[db2]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[db2]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[db2]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[db2]"
+ ```
- Choose **`Db2SyncConfig`** or **`Db2AsyncConfig`** for transactional OLTP workloads over IBM's native `clidriver` (`ibm_db` / `ibm_db_dbi`).
- Note on Linux `aarch64`: IBM publishes prebuilt `clidriver` wheels for Linux `x86_64`, macOS `arm64`, and Windows. On Linux ARM64 hosts, connect to Db2 via [`adbc`](adbc.md) (`adbc-driver-db2` pure-Go DRDA wheel) or guard `ibm-db` with environment markers.
diff --git a/docs/databases/duckdb.md b/docs/databases/duckdb.md
index 8df9cf3f2..aa2d1e1f5 100644
--- a/docs/databases/duckdb.md
+++ b/docs/databases/duckdb.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[duckdb]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[duckdb]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[duckdb]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[duckdb]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[duckdb]"
+ ```
Choose `DuckDBConfig` for embedded OLAP analytics, local lakehouse queries over Parquet/CSV/JSON on S3 or GCS, fast feature engineering pipelines, and columnar ETL jobs running inside a single Python worker.
@@ -40,9 +60,9 @@ For persistent analytical databases, set `"database": "var/analytics.duckdb"` (a
| --- | --- | --- |
| `database` / `read_only` | `connection_config` | File path or `":memory:"` (shared across pooled cursors derived from the primary connection) and read-only open flag. |
| `pool_min_size` / `pool_max_size` | `connection_config` | Bounds for `DuckDBConnectionPool`. |
-| `threads` / `memory_limit` | `connection_config` | DuckDB engine worker threads and memory cap (such as `"4GB"`). |
+| `threads` / `memory_limit` | `connection_config` | DuckDB engine worker threads and memory cap (such as `"4GB"`). In Linux containers, `DuckDBConfig` automatically clamps default memory limits to cgroup v1/v2 container quotas (`resolve_cgroup_memory_limit`). |
| `extensions` | `driver_features` | Sequence of `DuckDBExtensionConfig` dicts (`name`, `repository`, `force_install`) installed/loaded on startup. |
-| `secrets` | `driver_features` | Sequence of `DuckDBSecretConfig` dicts configuring S3, GCS, Azure, R2, or HuggingFace credentials via `CREATE SECRET`. |
+| `secrets` | `driver_features` | Sequence of `DuckDBSecretConfig` dicts configuring S3, GCS, Azure, R2, or HuggingFace credentials via `CREATE SECRET`, supporting dynamic credential rotation via `value_provider` callables. |
| `enable_statement_profiling` | `driver_features` | Captures DuckDB JSON execution plans (`EXPLAIN (ANALYZE, FORMAT JSON)`) onto `StatementEvent.profile`. |
| `enable_uuid_conversion` | `driver_features` | Normalizes UUID columns consistently across native and Arrow execution paths. |
diff --git a/docs/databases/mssql.md b/docs/databases/mssql.md
index 1b5ae38f7..54c6f054b 100644
--- a/docs/databases/mssql.md
+++ b/docs/databases/mssql.md
@@ -4,11 +4,43 @@ SQLSpec provides two synchronous Microsoft SQL Server and Azure SQL adapters—`
## Install and When to Choose
-| Adapter | Install Command | Best Fit |
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[mssql-python]"
+ # or: uv add "sqlspec[pymssql]"
+ # or: uv add "sqlspec[arrow-odbc]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[mssql-python]"
+ # or: pip install "sqlspec[pymssql]"
+ # or: pip install "sqlspec[arrow-odbc]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[mssql-python]"
+ # or: pdm add "sqlspec[pymssql]"
+ # or: pdm add "sqlspec[arrow-odbc]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[mssql-python]"
+ # or: poetry add "sqlspec[pymssql]"
+ # or: poetry add "sqlspec[arrow-odbc]"
+ ```
+
+| Adapter | Extra | Best Fit |
| --- | --- | --- |
-| `mssql_python` | `uv add "sqlspec[mssql-python]"` | Microsoft's official modern Python driver with Entra ID (`ActiveDirectoryDefault`) auth and native Arrow `BulkCopy` ingestion. |
-| `pymssql` | `uv add "sqlspec[pymssql]"` | Lightweight FreeTDS-based synchronous driver for classic SQL authentication and portable Linux containers. |
-| [`arrow_odbc`](arrow-odbc.md) | `uv add "sqlspec[arrow-odbc]"` | High-throughput analytical reads streaming ODBC batches directly into Apache Arrow tables. |
+| `mssql_python` | `sqlspec[mssql-python]` | Microsoft's official modern Python driver with Entra ID (`ActiveDirectoryDefault`) auth and native Arrow `BulkCopy` ingestion. |
+| `pymssql` | `sqlspec[pymssql]` | Lightweight FreeTDS-based synchronous driver for classic SQL authentication and portable Linux containers. |
+| [`arrow_odbc`](arrow-odbc.md) | `sqlspec[arrow-odbc]` | High-throughput analytical reads streaming ODBC batches directly into Apache Arrow tables. |
## Minimal Connection and Pool Setup
diff --git a/docs/databases/mysql.md b/docs/databases/mysql.md
index 6d442ac2f..03341c82e 100644
--- a/docs/databases/mysql.md
+++ b/docs/databases/mysql.md
@@ -4,12 +4,48 @@ SQLSpec provides MySQL and MariaDB adapters sharing unified error translation (`
## Install and When to Choose
-| Adapter | Install Command | Mode | Best Fit |
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[asyncmy]"
+ # or: uv add "sqlspec[pymysql]"
+ # or: uv add "sqlspec[aiomysql]"
+ # or: uv add "sqlspec[mysql-connector]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[asyncmy]"
+ # or: pip install "sqlspec[pymysql]"
+ # or: pip install "sqlspec[aiomysql]"
+ # or: pip install "sqlspec[mysql-connector]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[asyncmy]"
+ # or: pdm add "sqlspec[pymysql]"
+ # or: pdm add "sqlspec[aiomysql]"
+ # or: pdm add "sqlspec[mysql-connector]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[asyncmy]"
+ # or: poetry add "sqlspec[pymysql]"
+ # or: poetry add "sqlspec[aiomysql]"
+ # or: poetry add "sqlspec[mysql-connector]"
+ ```
+
+| Adapter | Extra | Mode | Best Fit |
| --- | --- | --- | --- |
-| `asyncmy` | `uv add "sqlspec[asyncmy]"` | Async | Recommended Cython-accelerated `asyncio` driver for MySQL 8+ and MariaDB. |
-| `pymysql` | `uv add "sqlspec[pymysql]"` | Sync | Pure-Python synchronous driver with Google Cloud SQL connector support (`sqlspec[cloud-sql]`). |
-| `aiomysql` | `uv add "sqlspec[aiomysql]"` | Async | Pure-Python `asyncio` driver built over `PyMySQL` for PyPy or pure-Python environments. |
-| `mysqlconnector` | `uv add "sqlspec[mysql-connector]"` | Sync + Async | Official Oracle MySQL Connector/Python supporting both sync pools and native async connections. |
+| `asyncmy` | `sqlspec[asyncmy]` | Async | Recommended Cython-accelerated `asyncio` driver for MySQL 8+ and MariaDB. |
+| `pymysql` | `sqlspec[pymysql]` | Sync | Pure-Python synchronous driver with Google Cloud SQL connector support (`sqlspec[cloud-sql]`). |
+| `aiomysql` | `sqlspec[aiomysql]` | Async | Pure-Python `asyncio` driver built over `PyMySQL` for PyPy or pure-Python environments. |
+| `mysqlconnector` | `sqlspec[mysql-connector]` | Sync + Async | Official Oracle MySQL Connector/Python supporting both sync pools and native async connections. |
## Minimal Connection and Pool Setup
diff --git a/docs/databases/oracledb.md b/docs/databases/oracledb.md
index 126df91f8..4d7c5a8ac 100644
--- a/docs/databases/oracledb.md
+++ b/docs/databases/oracledb.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[oracledb]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[oracledb]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[oracledb]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[oracledb]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[oracledb]"
+ ```
`python-oracledb` runs in pure-Python/Cython **Thin mode** by default with zero Oracle Instant Client dependencies. Enable **Thick mode** only when connecting to legacy encryption suites, Kerberos/Radius OS authentication, or Continuous Query Notification features requiring Oracle Client libraries.
diff --git a/docs/databases/postgres/asyncpg.md b/docs/databases/postgres/asyncpg.md
index 5f3b30e7f..c09b514c6 100644
--- a/docs/databases/postgres/asyncpg.md
+++ b/docs/databases/postgres/asyncpg.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[asyncpg]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[asyncpg]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[asyncpg]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[asyncpg]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[asyncpg]"
+ ```
Choose `asyncpg` for pure-`asyncio` APIs and background workers that prioritize binary-protocol throughput, per-statement query timeouts, and automatic custom type codecs. If the same codebase also requires synchronous DB-API connections, see [`psycopg`](psycopg.md).
diff --git a/docs/databases/postgres/index.md b/docs/databases/postgres/index.md
index 5c8d98567..a0b7f7ade 100644
--- a/docs/databases/postgres/index.md
+++ b/docs/databases/postgres/index.md
@@ -4,11 +4,43 @@ SQLSpec provides three dedicated PostgreSQL adapters—[`asyncpg`](asyncpg.md),
## Install and When to Choose
-| Adapter | Install Command | Best Fit |
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[asyncpg]"
+ # or: uv add "sqlspec[psycopg]"
+ # or: uv add "sqlspec[psqlpy]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[asyncpg]"
+ # or: pip install "sqlspec[psycopg]"
+ # or: pip install "sqlspec[psqlpy]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[asyncpg]"
+ # or: pdm add "sqlspec[psycopg]"
+ # or: pdm add "sqlspec[psqlpy]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[asyncpg]"
+ # or: poetry add "sqlspec[psycopg]"
+ # or: poetry add "sqlspec[psqlpy]"
+ ```
+
+| Adapter | Extra | Best Fit |
| --- | --- | --- |
-| [`asyncpg`](asyncpg.md) | `uv add "sqlspec[asyncpg]"` | Lowest-latency pure `asyncio` OLTP services using PostgreSQL's binary wire protocol. |
-| [`psycopg`](psycopg.md) | `uv add "sqlspec[psycopg]"` | Applications needing both synchronous and `asyncio` pools, libpq parity, `NullConnectionPool`, or streaming `COPY`. |
-| [`psqlpy`](psqlpy.md) | `uv add "sqlspec[psqlpy]"` | High-concurrency `asyncio` services using a Rust/Tokio connection pool, native `PgVector` binding, and wire pipelining. |
+| [`asyncpg`](asyncpg.md) | `sqlspec[asyncpg]` | Lowest-latency pure `asyncio` OLTP services using PostgreSQL's binary wire protocol. |
+| [`psycopg`](psycopg.md) | `sqlspec[psycopg]` | Applications needing both synchronous and `asyncio` pools, libpq parity, `NullConnectionPool`, or streaming `COPY`. |
+| [`psqlpy`](psqlpy.md) | `sqlspec[psqlpy]` | High-concurrency `asyncio` services using a Rust/Tokio connection pool, native `PgVector` binding, and wire pipelining. |
For Google Cloud Managed Databases, add `sqlspec[cloud-sql]` (`cloud-sql-python-connector`) or `sqlspec[alloydb]` (`google-cloud-alloydb-connector`).
@@ -79,6 +111,7 @@ For Google Cloud Managed Databases, add `sqlspec[cloud-sql]` (`cloud-sql-python-
| `enable_pgvector` | `bool` | Registers `pgvector` distance operators (`<->`, `<=>`, `<#>`) and vector codecs when available. |
| `enable_paradedb` | `bool` | Enables ParadeDB `pg_search` BM25 (`@@@`) operators and hybrid search SQL generation. |
| `enable_pg_textsearch` | `bool` | Enables PostgreSQL `pg_textsearch` full-text search extensions in ADK memory stores. |
+| `auth_token_provider` | `Callable` | Sync or async callable invoked on connection creation to mint short-lived IAM authentication tokens (Google Cloud SQL IAM, AlloyDB IAM, AWS RDS IAM, Azure AD) without restarting pools. |
| `json_serializer` / `json_deserializer` | `Callable` | Customizes `json` and `jsonb` wire encoding (defaults to SQLSpec's centralized `msgspec` serializers). |
### Running Behind PgBouncer (`pooler_mode` + `direct_dsn`)
diff --git a/docs/databases/postgres/psqlpy.md b/docs/databases/postgres/psqlpy.md
index b26a281c3..5f3e17fb0 100644
--- a/docs/databases/postgres/psqlpy.md
+++ b/docs/databases/postgres/psqlpy.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[psqlpy]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[psqlpy]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[psqlpy]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[psqlpy]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[psqlpy]"
+ ```
Choose `psqlpy` for latency-sensitive `asyncio` workloads that benefit from Tokio's multi-threaded Rust socket pool, single-round-trip wire pipelining on `execute_stack`, and `pgvector` bindings without requiring an extra Python `pgvector` client dependency.
diff --git a/docs/databases/postgres/psycopg.md b/docs/databases/postgres/psycopg.md
index 3d0631716..a23bde63e 100644
--- a/docs/databases/postgres/psycopg.md
+++ b/docs/databases/postgres/psycopg.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[psycopg]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[psycopg]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[psycopg]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[psycopg]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[psycopg]"
+ ```
Choose `psycopg` when your service mixes synchronous CLI/worker processes with `asyncio` HTTP servers, relies on standard `libpq` environment variables and `.pgpass` / GSSAPI / Kerberos authentication, or runs on scale-to-zero serverless platforms where `null_pool=True` avoids idle connections.
diff --git a/docs/databases/spanner.md b/docs/databases/spanner.md
index 44d20dc0f..d5c0ba2c2 100644
--- a/docs/databases/spanner.md
+++ b/docs/databases/spanner.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[spanner]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[spanner]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[spanner]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[spanner]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[spanner]"
+ ```
Choose `SpannerAsyncConfig` or `SpannerSyncConfig` for globally distributed, externally consistent OLTP databases, multi-region scale-out workloads, property-graph and `ARRAY` vector search, and agentic Google ADK session/memory/artifact persistence.
@@ -71,6 +91,7 @@ See [Spanner API Reference](/reference/adapters/spanner/) for full `SpannerConne
- **Snapshot Reads vs Read-Write Retries (`run_in_transaction`)**: Use `provide_session(transaction=False)` for lock-free single-use or multi-use `Snapshot` reads (zero `Commit` RPC cost). For contended read-write transactions, call `config.run_in_transaction(...)` or `session.run_in_transaction(...)`, which automatically unwraps `DeadlockError` (`Aborted`) and re-executes the closure with Spanner's native backoff hint.
- **`THEN RETURN` DML Support**: Both sync and async drivers return projected rows and `rows_affected` on `INSERT ... THEN RETURN`, `UPDATE ... THEN RETURN`, and `DELETE ... THEN RETURN` statements.
+- **Automatic Mutation Budget Splitting**: During `load_from_arrow`, `load_from_storage`, and batched writes, Spanner drivers estimate per-row column and secondary-index mutations (`estimate_batch_mutations`) and split large batches automatically to stay safely below Spanner's per-commit mutation limit.
- **GoogleSQL (`spanner`) vs PostgreSQL (`spangres`) Dialects**: SQLSpec includes dedicated custom SQLGlot dialects for both `spanner` (`INTERLEAVE IN PARENT`, `ROW DELETION POLICY`, `GRAPH_TABLE`) and `spangres` (`TTL INTERVAL`, `$1` placeholders).
- **ADK Vector, Hybrid RRF, and Artifact Stores**: `Spanner*ADKMemoryStore` supports `ARRAY` embeddings with `APPROX_COSINE_DISTANCE`, FTS `SCORE`, and single-round-trip Hybrid RRF (`k=60`), plus `SpannerSyncADKArtifactStore` / `SpannerAsyncADKArtifactStore` in `sqlspec.adapters.spanner.adk`.
- **Spanner Emulator**: Set `SPANNER_EMULATOR_HOST="localhost:9010"` (or use `pytest-databases` `spanner_service`) to run full sync and async tests locally without GCP credentials.
diff --git a/docs/databases/sqlanywhere.md b/docs/databases/sqlanywhere.md
index 9c2393dbc..8914684d8 100644
--- a/docs/databases/sqlanywhere.md
+++ b/docs/databases/sqlanywhere.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[sqlanywhere]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[sqlanywhere]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[sqlanywhere]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[sqlanywhere]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[sqlanywhere]"
+ ```
- Choose **`SqlanywhereConfig`** for embedded, edge, and client-server applications interacting with SAP SQL Anywhere database engines.
- `sqlanydb` wraps SAP's native client C library (`dbcapi`) via `ctypes`. Ensure `libdbcapi_r.so` (Linux), `libdbcapi_r.dylib` (macOS), or `dbcapi.dll` (Windows) is installed and available in the system library path.
diff --git a/docs/databases/sqlite.md b/docs/databases/sqlite.md
index 2df004b17..805956ecf 100644
--- a/docs/databases/sqlite.md
+++ b/docs/databases/sqlite.md
@@ -4,16 +4,44 @@ SQLSpec ships `mypyc`-compiled synchronous (`sqlspec.adapters.sqlite`) and async
## Install and When to Choose
-=== "Sync (`sqlite` — Built-In)"
+=== "uv"
```bash
+ # Sync (sqlite3 — built-in)
uv add sqlspec
+
+ # Async (aiosqlite)
+ uv add "sqlspec[aiosqlite]"
```
-=== "Async (`aiosqlite`)"
+=== "pip"
```bash
- uv add "sqlspec[aiosqlite]"
+ # Sync (sqlite3 — built-in)
+ pip install sqlspec
+
+ # Async (aiosqlite)
+ pip install "sqlspec[aiosqlite]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ # Sync (sqlite3 — built-in)
+ pdm add sqlspec
+
+ # Async (aiosqlite)
+ pdm add "sqlspec[aiosqlite]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ # Sync (sqlite3 — built-in)
+ poetry add sqlspec
+
+ # Async (aiosqlite)
+ poetry add "sqlspec[aiosqlite]"
```
Choose **`SqliteConfig`** for CLI tools, synchronous web apps, embedded local storage, and sub-millisecond unit tests. Choose **`AiosqliteConfig`** for `asyncio` frameworks (Litestar, FastAPI, Starlette, Google ADK) where SQLite I/O runs on a dedicated worker thread without blocking the event loop.
diff --git a/docs/databases/sybase.md b/docs/databases/sybase.md
index 23d9b8838..754eb5581 100644
--- a/docs/databases/sybase.md
+++ b/docs/databases/sybase.md
@@ -4,9 +4,29 @@
## Install and When to Choose
-```bash
-uv add "sqlspec[sybase]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[sybase]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[sybase]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[sybase]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[sybase]"
+ ```
- Choose **`SybaseConfig`** for transactional OLTP applications communicating with SAP ASE database instances.
- Prebuilt `pymssql` wheels ship statically linked FreeTDS with native TDS 5.0 wire support across Linux, macOS, and Windows.
diff --git a/docs/examples/configuration/multi_database.py b/docs/examples/configuration/multi_database.py
index 46b36fa87..21d53cf1a 100644
--- a/docs/examples/configuration/multi_database.py
+++ b/docs/examples/configuration/multi_database.py
@@ -17,13 +17,12 @@ def main() -> tuple[list[dict[str, Any]], list[dict[str, Any]]]:
with tempfile.TemporaryDirectory() as tmp_dir:
base_dir = Path(tmp_dir)
- # --8<-- [start:example]
- primary = spec.add_config(SqliteConfig(connection_config={"database": str(base_dir / "primary.db")}))
- analytics = spec.add_config(SqliteConfig(connection_config={"database": str(base_dir / "analytics.db")}))
-
sql_dir = base_dir / "sql"
sql_dir.mkdir()
(sql_dir / "queries.sql").write_text("-- name: list_users\nSELECT id, name FROM users ORDER BY id;\n")
+ # --8<-- [start:example]
+ primary = spec.add_config(SqliteConfig(connection_config={"database": str(base_dir / "primary.db")}))
+ analytics = spec.add_config(SqliteConfig(connection_config={"database": str(base_dir / "analytics.db")}))
spec.load_sql_files(sql_dir)
with spec.provide_session(primary) as session:
diff --git a/docs/examples/patterns/migration_squash_and_schema.py b/docs/examples/patterns/migration_squash_and_schema.py
index 7cc71be1e..945046187 100644
--- a/docs/examples/patterns/migration_squash_and_schema.py
+++ b/docs/examples/patterns/migration_squash_and_schema.py
@@ -46,16 +46,16 @@ def main() -> tuple[str, bool, str]:
commands.upgrade(echo=False)
report = commands.verify(strict=True, echo=False)
- assert report["valid"] is True
commands.squash("0001", "0002", "initial_baseline", strategy="ddl", yes=True)
squashed_file = next(migration_dir.glob("0001_*.sql"))
squashed_sql = squashed_file.read_text(encoding="utf-8")
- assert "-- Squashed from: 0001, 0002" in squashed_sql
- assert "display_name" in squashed_sql
schema_sql = commands.dump_schema(migration_dir / "schema.sql", echo=False)
# --8<-- [end:example]
+ assert report["valid"] is True
+ assert "-- Squashed from: 0001, 0002" in squashed_sql
+ assert "display_name" in squashed_sql
assert "CREATE TABLE" in schema_sql
return squashed_file.name, bool(report["valid"]), schema_sql
finally:
diff --git a/docs/examples/patterns/schema_ddl_and_inference.py b/docs/examples/patterns/schema_ddl_and_inference.py
index e6848072c..ba5d6819a 100644
--- a/docs/examples/patterns/schema_ddl_and_inference.py
+++ b/docs/examples/patterns/schema_ddl_and_inference.py
@@ -30,10 +30,10 @@ def main() -> tuple[str, int, str, str]:
profiles = dd.get_table_profile(session, "raw_events")
inferred_ddl = dd.infer_table_ddl_from_data(session, "raw_events", target_table="events").ddl or ""
- records_ddl = CreateTable.from_records(
- "audit_events", [{"id": 1, "actor": "system", "active": True}], primary_key="id", dialect="postgres"
- ).to_sql()
- # --8<-- [end:example]
+ records_ddl = CreateTable.from_records(
+ "audit_events", [{"id": 1, "actor": "system", "active": True}], primary_key="id", dialect="postgres"
+ ).to_sql()
+ # --8<-- [end:example]
assert "teams" in teams_ddl
assert len(schema_items) >= 2
diff --git a/docs/examples/sql_files/declared_params.py b/docs/examples/sql_files/declared_params.py
index e762a1b56..7b1eca183 100644
--- a/docs/examples/sql_files/declared_params.py
+++ b/docs/examples/sql_files/declared_params.py
@@ -38,7 +38,6 @@ def main() -> tuple[dict[str, Any], list[dict[str, Any]]]:
# --8<-- [start:example]
declarations = spec.get_query_parameters("get_team_by_name")
query = spec.get_sql("get_team_by_name")
- assert query.declared_parameters == declarations
with spec.provide_session(config) as session:
session.execute("CREATE TABLE teams (id INTEGER PRIMARY KEY, name TEXT)")
@@ -46,8 +45,10 @@ def main() -> tuple[dict[str, Any], list[dict[str, Any]]]:
row = session.execute(query, name="SQLSpec").one()
optional_rows = session.execute(spec.get_sql("list_teams")).all()
- # --8<-- [end:example]
+ # --8<-- [end:example]
+ assert query.declared_parameters == declarations
+ with spec.provide_session(config) as session:
missing_error = ""
try:
session.execute(spec.get_sql("get_team_by_name"))
diff --git a/docs/index.md b/docs/index.md
index af9e5ea99..4b2b1afbc 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -25,17 +25,57 @@ hide:
## Install
-Install `sqlspec` with `uv`. Python's built-in `sqlite3` adapter works immediately with zero extra driver packages:
+Install `sqlspec` with your preferred package manager. Python's built-in `sqlite3` adapter works immediately with zero extra driver packages:
-```bash
-uv add sqlspec
-```
+=== "uv"
+
+ ```bash
+ uv add sqlspec
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install sqlspec
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add sqlspec
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add sqlspec
+ ```
Add optional database driver, schema validation, or framework extras when connecting to external engines:
-```bash
-uv add "sqlspec[asyncpg,msgspec,litestar]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[asyncpg,msgspec,litestar]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[asyncpg,msgspec,litestar]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[asyncpg,msgspec,litestar]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[asyncpg,msgspec,litestar]"
+ ```
## First Query
@@ -52,7 +92,7 @@ Run live SQLite queries directly in your browser in the [Interactive Playground]
| Section | What You Will Find |
| --- | --- |
| **[Learn](learn/index.md)** | Progressive SQLite-first tutorials covering configuration, querying, parameters, typed schema mapping, transactions, `.sql` files, query builders, filtering, migrations, Arrow pipelines, observability, testing, and production recipes. |
-| **[Databases](/databases/)** | Central Capability Matrix plus concise setup, connection pool, type coercion, and dialect guides for PostgreSQL, CockroachDB, SQLite, MySQL, MariaDB, Oracle, SQL Server, Db2, DuckDB, BigQuery, Spanner, ADBC, and Arrow ODBC. |
+| **[Databases](/databases/)** | Central Capability Matrix plus concise setup, connection pool, type coercion, and dialect guides for PostgreSQL, CockroachDB, SQLite, MySQL, MariaDB, Oracle, SQL Server, Sybase ASE, SAP SQL Anywhere, IBM Db2, DuckDB, BigQuery, Spanner, ADBC, and Arrow ODBC. |
| **[Integrations](/integrations/)** | Unified plugin, dependency injection, session store, event queue, and agent memory guides for Litestar, Google ADK, Events, FastAPI, Starlette, Flask, Sanic, and Dishka. |
| **[Playground](playground.md)** | Zero-install WebAssembly (Pyodide) SQLite environment running `SQLSpec` directly in your browser tab. |
| **[Reference](/reference/)** | Focused API reference pages for `SQLSpec`, configurations, drivers, statements, builders, migrations, storage, data dictionaries, extensions, CLI commands, and adapter classes. |
diff --git a/docs/integrations/adk.md b/docs/integrations/adk.md
index 22e98a930..4865ead52 100644
--- a/docs/integrations/adk.md
+++ b/docs/integrations/adk.md
@@ -4,12 +4,39 @@
## Installation
-```bash
-uv add "sqlspec[adk]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[adk]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[adk]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[adk]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[adk]"
+ ```
## Architecture and Store Services
+```mermaid
+flowchart LR
+ Runner["Google ADK Runner"] --> Services["Session / Memory / Artifact Services"]
+ Services --> Stores["Adapter ADK Stores (*ADKStore / *ADKMemoryStore)"]
+ Stores --> DB[("Database Tables + Vector / FTS Indexes")]
+```
+
| ADK Service | SQLSpec Class | Adapter Store Base | Responsibility |
| --- | --- | --- | --- |
| **Session & Event Store** | `SQLSpecSessionService` | `BaseAsyncADKStore` / `BaseSyncADKStore` | Persists conversation sessions, structured state (`app:`, `user:`, session state), and ordered `Event` records with optimistic revision checks and per-session concurrency locks. |
@@ -72,12 +99,11 @@ from pathlib import Path
from sqlspec import SQLSpec
from sqlspec.adapters.asyncpg import AsyncpgConfig
from sqlspec.adapters.asyncpg.adk import (
- AsyncpgADKArtifactStore,
+ AsyncpgADKConfig,
AsyncpgADKMemoryStore,
AsyncpgADKStore,
)
from sqlspec.extensions.adk import (
- SQLSpecArtifactService,
SQLSpecMemoryService,
SQLSpecSessionService,
)
@@ -87,8 +113,8 @@ BASE_DIR = Path(__file__).resolve().parent.parent.parent.parent
def create_adk_services(
spec: SQLSpec,
-) -> tuple[SQLSpecSessionService, SQLSpecMemoryService, SQLSpecArtifactService]:
- """Wire ADK stores and service adapters on an AsyncpgConfig instance."""
+) -> tuple[SQLSpecSessionService, SQLSpecMemoryService]:
+ """Wire ADK session and memory services on an AsyncpgConfig instance."""
config = spec.add_config(
AsyncpgConfig(
bind_key="ai",
@@ -98,33 +124,30 @@ def create_adk_services(
"include_extensions": ["adk"],
},
extension_config={
- "adk": {
- "session_table": "adk_sessions",
- "events_table": "adk_events",
- "memory_table": "adk_memory_entries",
- "artifact_table": "adk_artifacts",
- "memory_use_fts": True,
- "memory_use_vector": True,
- "memory_vector_dimensions": 768,
- }
+ "adk": AsyncpgADKConfig(
+ session_table="adk_sessions",
+ events_table="adk_events",
+ memory_table="adk_memory_entries",
+ memory_use_fts=True,
+ memory_use_vector=True,
+ memory_max_results=20,
+ )
},
)
)
session_store = AsyncpgADKStore(config)
memory_store = AsyncpgADKMemoryStore(config)
- artifact_store = AsyncpgADKArtifactStore(config)
return (
SQLSpecSessionService(session_store),
SQLSpecMemoryService(memory_store),
- SQLSpecArtifactService(artifact_store),
)
```
## Schema Migrations and CLI Maintenance
-Instead of calling `ensure_tables()` at runtime in production, enable `"adk"` in `migration_config={"enable_extensions": ["adk"]}` so `sqlspec upgrade` tracks ADK schema revisions alongside your application migrations.
+Instead of calling `ensure_tables()` at runtime in production, enable `"adk"` in `migration_config={"include_extensions": ["adk"]}` so `sqlspec upgrade` tracks ADK schema revisions alongside your application migrations.
Use the built-in `sqlspec adk memory` CLI commands to inspect, clean up, or verify memory stores:
diff --git a/docs/integrations/dishka.md b/docs/integrations/dishka.md
index eafd94ddc..92c28c365 100644
--- a/docs/integrations/dishka.md
+++ b/docs/integrations/dishka.md
@@ -4,9 +4,29 @@
## Installation
-```bash
-uv add sqlspec dishka
-```
+=== "uv"
+
+ ```bash
+ uv add sqlspec dishka
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install sqlspec dishka
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add sqlspec dishka
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add sqlspec dishka
+ ```
## Provider Pattern (`Scope.APP` + `Scope.REQUEST`)
diff --git a/docs/integrations/events.md b/docs/integrations/events.md
index 592974a71..1ecc967e3 100644
--- a/docs/integrations/events.md
+++ b/docs/integrations/events.md
@@ -2,6 +2,13 @@
`sqlspec.extensions.events` provides synchronous (`SyncEventChannel`) and asynchronous (`AsyncEventChannel`) pub/sub and competing-consumer event channels backed by your existing database configuration, supporting native PostgreSQL `LISTEN`/`NOTIFY`, durable hybrid `notify_queue` wakeups, Oracle Advanced Queuing (`aq` and `txeventq`), and portable table-backed polling (`poll_queue`) across supported database adapters.
+```mermaid
+flowchart LR
+ Pub["Publisher (publish / publish_many)"] --> Channel["Sync / AsyncEventChannel"]
+ Channel --> Backend["Backend (notify / notify_queue / poll_queue / aq)"]
+ Backend --> Consumer["Consumer (dequeue / iter_events -> ack / nack)"]
+```
+
## Choosing a Transport Backend
Set `extension_config["events"]["backend"]` according to your delivery and durability requirements:
diff --git a/docs/integrations/fastapi.md b/docs/integrations/fastapi.md
index 8e753b450..a3b75d819 100644
--- a/docs/integrations/fastapi.md
+++ b/docs/integrations/fastapi.md
@@ -4,9 +4,29 @@
## Installation
-```bash
-uv add "sqlspec[fastapi]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[fastapi]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[fastapi]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[fastapi]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[fastapi]"
+ ```
## Basic Setup and Session Injection
diff --git a/docs/integrations/flask.md b/docs/integrations/flask.md
index 4aeff6b9b..d1181753d 100644
--- a/docs/integrations/flask.md
+++ b/docs/integrations/flask.md
@@ -4,9 +4,29 @@
## Installation
-```bash
-uv add "sqlspec[flask]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[flask]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[flask]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[flask]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[flask]"
+ ```
## Application Factory Setup
diff --git a/docs/integrations/index.md b/docs/integrations/index.md
index d70250e35..1732581e8 100644
--- a/docs/integrations/index.md
+++ b/docs/integrations/index.md
@@ -13,7 +13,7 @@ SQLSpec ships first-party framework plugins and ecosystem extensions under `sqls
| **[Starlette](starlette.md)** | `sqlspec.extensions.starlette` | `sqlspec[starlette]` | `db_plugin.get_session(request)` | `manual`, `autocommit`, `autocommit_include_redirect` | — | — | `sqlspec` CLI |
| **[Flask](flask.md)** | `sqlspec.extensions.flask` | `sqlspec[flask]` | `plugin.get_session()` (`g`-scoped) | `manual`, `autocommit`, `autocommit_include_redirect` | — | — | `sqlspec` CLI |
| **[Sanic](sanic.md)** | `sqlspec.extensions.sanic` | `sqlspec[sanic]` | `db_plugin.get_session(request)` / `ext.dependency` | `manual`, `autocommit`, `autocommit_include_redirect` | — | — | `sqlspec` CLI |
-| **[Dishka](dishka.md)** | `dishka` | `uv add dishka` | `FromDishka[DriverT]` (`Scope.REQUEST`) | Explicit session or service scope | Combines with any adapter store | Combines with `SQLSpecAsyncService` / `SQLSpecSyncService` | Shared `Scope.APP` container across web, CLI, and workers |
+| **[Dishka](dishka.md)** | `dishka` | `dishka` | `FromDishka[DriverT]` (`Scope.REQUEST`) | Explicit session or service scope | Combines with any adapter store | Combines with `SQLSpecAsyncService` / `SQLSpecSyncService` | Shared `Scope.APP` container across web, CLI, and workers |
## Shared `extension_config` Pattern
diff --git a/docs/integrations/litestar.md b/docs/integrations/litestar.md
index 871b2948d..e150a520e 100644
--- a/docs/integrations/litestar.md
+++ b/docs/integrations/litestar.md
@@ -4,12 +4,40 @@
## Installation
-```bash
-uv add "sqlspec[litestar]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[litestar]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[litestar]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[litestar]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[litestar]"
+ ```
## Plugin Setup and Session Injection
+```mermaid
+flowchart LR
+ Lifespan["Litestar Lifespan (Pool Init & Close)"] --> Request["HTTP Request Middleware"]
+ Request --> DI["Inject db_session + Filter Dependencies"]
+ DI --> Handler["Route Handler / Domain Service"]
+ Handler --> Commit["Commit Mode (manual / autocommit)"]
+```
+
Register one or more database configurations on a `SQLSpec` instance, configure `extension_config["litestar"]`, and pass `SQLSpecPlugin(sqlspec=spec)` to `Litestar(plugins=[...])`:
```python
@@ -92,13 +120,13 @@ For bidirectional keyset pagination (`CursorPagination`), set `cursor_key="id"`
## Server-Side Session Stores
-Supported database adapters expose a Litestar `Store` implementation under `sqlspec.adapters..litestar` (`AsyncpgStore`, `PsycopgAsyncStore`, `PsqlpyStore`, `AiosqliteStore`, `SqliteStore`, `AsyncmyStore`, `AiomysqlStore`, `PymysqlStore`, `MysqlConnectorAsyncStore`, `OracleAsyncStore`, `SpannerSyncStore`, `SpannerAsyncStore`, `DuckDBStore`, `AdbcStore`, `ArrowOdbcStore`, and `Db2SyncStore`):
+Supported database adapters expose a Litestar `Store` implementation under `sqlspec.adapters..litestar` (`AsyncpgStore`, `PsycopgSyncStore`, `PsycopgAsyncStore`, `PsqlpyStore`, `CockroachAsyncpgStore`, `CockroachPsycopgSyncStore`, `CockroachPsycopgAsyncStore`, `AiosqliteStore`, `SQLiteStore`, `AsyncmyStore`, `AiomysqlStore`, `PyMysqlStore`, `MysqlConnectorSyncStore`, `MysqlConnectorAsyncStore`, `OracleSyncStore`, `OracleAsyncStore`, `MssqlPythonStore`, `PymssqlStore`, `SybaseStore`, `SqlanywhereStore`, `Db2SyncStore`, `Db2AsyncStore`, `DuckdbStore`, `BigQueryStore`, `SpannerSyncStore`, `SpannerAsyncStore`, `ADBCStore`, and `ArrowOdbcStore`):
```python
--8<-- "docs/examples/frameworks/litestar/session_stores.py:example"
```
-Call `await store.create_table()` during application startup (or include the extension migration via `enable_extensions=["litestar"]` in `migration_config`) and pass the store to Litestar's `ServerSideSessionConfig` or `stores={"sessions": store}` registry.
+Call `await store.create_table()` during application startup (or include the extension migration via `include_extensions=["litestar"]` in `migration_config`) and pass the store to Litestar's `ServerSideSessionConfig` or `stores={"sessions": store}` registry.
## Production Litestar + Dishka Architecture
diff --git a/docs/integrations/sanic.md b/docs/integrations/sanic.md
index f18ec852d..ea7ac6037 100644
--- a/docs/integrations/sanic.md
+++ b/docs/integrations/sanic.md
@@ -4,9 +4,29 @@
## Installation
-```bash
-uv add "sqlspec[sanic]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[sanic]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[sanic]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[sanic]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[sanic]"
+ ```
## Basic Setup
diff --git a/docs/integrations/starlette.md b/docs/integrations/starlette.md
index a1661d36e..fd2b149b2 100644
--- a/docs/integrations/starlette.md
+++ b/docs/integrations/starlette.md
@@ -4,9 +4,29 @@
## Installation
-```bash
-uv add "sqlspec[starlette]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[starlette]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[starlette]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[starlette]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[starlette]"
+ ```
## Basic Setup
diff --git a/docs/learn/arrow-and-storage.md b/docs/learn/arrow-and-storage.md
index b0ceda9fd..d8d1800a3 100644
--- a/docs/learn/arrow-and-storage.md
+++ b/docs/learn/arrow-and-storage.md
@@ -1,6 +1,13 @@
# Arrow and Storage
-SQLSpec bridges relational and analytical workloads with first-class **Apache Arrow** query results (`select_to_arrow`), pluggable object storage (`StorageRegistry` over local paths, `obstore`, and `fsspec`), Parquet/Arrow storage pipelines, and schema-aware JSONL table fixtures.
+SQLSpec bridges relational and analytical workloads with first-class **Apache Arrow** query results (`select_to_arrow`), streaming `RecordBatch` pipelines (`read_arrow_batches` / `write_arrow_batches`), pluggable object storage (`StorageRegistry` over local paths, `obstore`, and `fsspec`), multi-shard bulk ingestion, and schema-aware JSONL table fixtures.
+
+```mermaid
+flowchart LR
+ DB[("Database Session")] <-->|"select_to_arrow / load_from_arrow"| Arrow["Arrow Table / RecordBatch Stream"]
+ Arrow <-->|"Sync / AsyncStoragePipeline"| Store["StorageRegistry (local / obstore / fsspec)"]
+ Arrow -->|"Zero-Copy"| DF["Polars / Pandas / PyArrow"]
+```
## Querying Directly to Apache Arrow, Polars, and Pandas
@@ -67,6 +74,13 @@ with tempfile.TemporaryDirectory() as tmp_dir:
You can also write an `ArrowResult` directly to any storage destination via `arrow_result.write_to_storage_sync("alias://exports/metrics.parquet")` (or `await arrow_result.write_to_storage_async(...)`). On Windows environments where `obstore` is installed alongside local filesystem paths, pass `backend="local"` when registering local directory aliases so Windows drive prefixes resolve cleanly.
+### Streaming `RecordBatch` Coercion & Schema Validation
+
+For datasets larger than available RAM, `SyncStoragePipeline` and `AsyncStoragePipeline` expose `read_arrow_batches(...)` and `write_arrow_batches(...)` alongside `coerce_arrow_batches(...)` and `validate_arrow_schema(...)` from `sqlspec.utils.arrow_helpers`:
+
+- **Bounded-Memory Streaming**: `read_arrow_batches` yields `pyarrow.RecordBatch` chunks lazily from Parquet, Arrow IPC, or JSONL sources without materializing a full `pyarrow.Table`.
+- **Single-Pass Target Schema Coercion**: When `target_schema` (or target table `ColumnMetadata`) is supplied to `load_from_arrow` or `load_from_storage`, SQLSpec validates and coerces incoming batches (`DECIMAL`, `TIMESTAMP`, `UUID`, binary, and `FIXED_SIZE_LIST` vector columns) one `RecordBatch` at a time before streaming to the database driver.
+
## Schema-Aware Table Fixtures (`.jsonl` / `.json.gz`)
`sqlspec.utils.fixtures` exports and loads per-table `.json`, `.json.gz`, `.jsonl`, and `.jsonl.gz` files while introspecting live column types (`UUID`, ISO timestamps, `Decimal`, base64 `bytes`, `JSON`/`JSONB`, identity columns, and sequence resyncing) via `driver.data_dictionary`:
diff --git a/docs/learn/configuration.md b/docs/learn/configuration.md
index a98031ea7..7e980bde0 100644
--- a/docs/learn/configuration.md
+++ b/docs/learn/configuration.md
@@ -17,102 +17,131 @@ You can open sessions either through the `SQLSpec` registry (`spec.provide_sessi
A single `SQLSpec` instance can coordinate multiple databases—such as an OLTP primary database alongside an analytical or audit store—and share loaded `.sql` files across them:
-```python
---8<-- "docs/examples/configuration/multi_database.py:example"
-```
+=== "SQL (`sql/queries.sql`)"
-When integrating with the CLI or web frameworks, assign `bind_key="primary"` and `bind_key="analytics"` on each config so migrations (`sqlspec --bind-key analytics upgrade`) and framework dependency keys (`db_session`, `analytics_session`) address each database unambiguously.
+ ```sql
+ -- name: list_users
+ SELECT id, name
+ FROM users
+ ORDER BY id;
+ ```
-## Production Composition Root (`app/config.py`) and Settings
+=== "Python"
-In production web applications and background worker services, isolate connection settings and registry assembly into a dedicated composition root module (`app/config.py`). This pattern keeps configuration evaluation lazy (using PEP 562 `__getattr__`), separates short-lived web requests from long-running background tasks, and registers CLI migrations directly in `pyproject.toml`.
+ ```python
+ --8<-- "docs/examples/configuration/multi_database.py:example"
+ ```
-```python
-"""Application database configuration and composition root."""
+When integrating with the CLI or web frameworks, assign `bind_key="primary"` and `bind_key="analytics"` on each config so migrations (`sqlspec --bind-key analytics upgrade`) and framework dependency keys (`db_session`, `analytics_session`) address each database unambiguously.
-from pathlib import Path
-from typing import Any
+## Production Composition Root (`app/config.py`) and Settings
-from pydantic_settings import BaseSettings
+In production web applications and background worker services, isolate connection settings and registry assembly into a dedicated composition root module (`app/config.py`). This pattern keeps configuration evaluation lazy (using PEP 562 `__getattr__`), separates short-lived web requests from long-running background tasks, and registers CLI migrations directly in `pyproject.toml`.
-from sqlspec import SQLSpec
-from sqlspec.adapters.asyncpg import AsyncpgConfig
-
-BASE_DIR = Path(__file__).resolve().parent.parent
-
-
-class DatabaseSettings(BaseSettings):
- """Database connection and pool settings."""
-
- url: str = "postgresql://user:pass@localhost:5432/app"
- pool_min_size: int = 5
- pool_max_size: int = 20
- pool_timeout: float = 30.0
-
- def get_web_config(self) -> AsyncpgConfig:
- """Configure request-scoped database pool with framework hooks."""
- return AsyncpgConfig(
- bind_key="app-web",
- connection_config={
- "dsn": self.url,
- "min_size": self.pool_min_size,
- "max_size": self.pool_max_size,
- "command_timeout": 30.0,
- },
- migration_config={
- "script_location": str(BASE_DIR / "db" / "migrations"),
- "include_extensions": ["litestar", "adk"],
- },
- extension_config={
- "litestar": {
- "disable_di": True,
- "manage_lifespan": True,
- "session_table": "app_session",
- }
- },
- )
-
- def get_worker_config(self) -> AsyncpgConfig:
- """Configure worker database pool without request timeout bounds."""
- return AsyncpgConfig(
- bind_key="app-worker",
- connection_config={
- "dsn": self.url,
- "min_size": 2,
- "max_size": 10,
- "command_timeout": None,
- },
- extension_config={"litestar": {"session_key": "worker_session"}},
- )
-
-
-def _init_db_manager() -> tuple[SQLSpec, AsyncpgConfig]:
- settings = DatabaseSettings()
- db_manager = SQLSpec()
- db = db_manager.add_config(settings.get_web_config())
- db_manager.add_config(settings.get_worker_config())
-
- sql_dir = BASE_DIR / "db" / "sql"
- if sql_dir.exists():
- db_manager.load_sql_files(sql_dir)
-
- return db_manager, db
-
-
-_db_manager: SQLSpec | None = None
-_db: AsyncpgConfig | None = None
-
-
-def __getattr__(name: str) -> Any:
- """Lazily initialize db_manager and primary db handle on first access."""
- global _db, _db_manager
- if name in {"db_manager", "db"}:
- if _db_manager is None:
- _db_manager, _db = _init_db_manager()
- return _db_manager if name == "db_manager" else _db
- msg = f"module {__name__!r} has no attribute {name!r}"
- raise AttributeError(msg)
-```
+=== "Python (`app/config.py`)"
+
+ ```python
+ """Application database configuration and composition root."""
+
+ from pathlib import Path
+ from typing import Any
+
+ from pydantic_settings import BaseSettings
+
+ from sqlspec import SQLSpec
+ from sqlspec.adapters.asyncpg import AsyncpgConfig
+
+ BASE_DIR = Path(__file__).resolve().parent.parent
+
+
+ class DatabaseSettings(BaseSettings):
+ """Database connection and pool settings."""
+
+ url: str = "postgresql://user:pass@localhost:5432/app"
+ pool_min_size: int = 5
+ pool_max_size: int = 20
+ pool_timeout: float = 30.0
+
+ def get_web_config(self) -> AsyncpgConfig:
+ """Configure request-scoped database pool with framework hooks."""
+ return AsyncpgConfig(
+ bind_key="app-web",
+ connection_config={
+ "dsn": self.url,
+ "min_size": self.pool_min_size,
+ "max_size": self.pool_max_size,
+ "command_timeout": 30.0,
+ },
+ migration_config={
+ "script_location": str(BASE_DIR / "db" / "migrations"),
+ "include_extensions": ["litestar", "adk"],
+ },
+ extension_config={
+ "litestar": {
+ "disable_di": True,
+ "manage_lifespan": True,
+ "session_table": "app_session",
+ }
+ },
+ )
+
+ def get_worker_config(self) -> AsyncpgConfig:
+ """Configure worker database pool without request timeout bounds."""
+ return AsyncpgConfig(
+ bind_key="app-worker",
+ connection_config={
+ "dsn": self.url,
+ "min_size": 2,
+ "max_size": 10,
+ "command_timeout": None,
+ },
+ extension_config={"litestar": {"session_key": "worker_session"}},
+ )
+
+
+ def _init_db_manager() -> tuple[SQLSpec, AsyncpgConfig]:
+ settings = DatabaseSettings()
+ db_manager = SQLSpec()
+ db = db_manager.add_config(settings.get_web_config())
+ db_manager.add_config(settings.get_worker_config())
+
+ sql_dir = BASE_DIR / "db" / "sql"
+ if sql_dir.exists():
+ db_manager.load_sql_files(sql_dir)
+
+ return db_manager, db
+
+
+ _db_manager: SQLSpec | None = None
+ _db: AsyncpgConfig | None = None
+
+
+ def __getattr__(name: str) -> Any:
+ """Lazily initialize db_manager and primary db handle on first access."""
+ global _db, _db_manager
+ if name in {"db_manager", "db"}:
+ if _db_manager is None:
+ _db_manager, _db = _init_db_manager()
+ return _db_manager if name == "db_manager" else _db
+ msg = f"module {__name__!r} has no attribute {name!r}"
+ raise AttributeError(msg)
+ ```
+
+=== "SQL (`app/db/sql/queries.sql`)"
+
+ ```sql
+ -- name: get-user-by-id
+ -- param: id int Account identifier
+ SELECT id, email, created_at
+ FROM users
+ WHERE id = :id;
+
+ -- name: list-active-users
+ SELECT id, email, created_at
+ FROM users
+ WHERE is_active = TRUE
+ ORDER BY created_at DESC;
+ ```
Point the CLI migration runner directly to `app.config.db` in `pyproject.toml`:
diff --git a/docs/learn/data-dictionary.md b/docs/learn/data-dictionary.md
index 824a54147..0b52c4fd8 100644
--- a/docs/learn/data-dictionary.md
+++ b/docs/learn/data-dictionary.md
@@ -1,6 +1,16 @@
# Data Dictionary
-Every SQLSpec driver exposes `session.data_dictionary`—a dialect-aware catalog introspection and DDL synthesis interface returning normalized typed metadata (`TableMetadata`, `ColumnMetadata`, `IndexMetadata`, `ForeignKeyMetadata`, `ColumnProfileMetadata`, `DDLResult`, `VersionInfo`) across SQLite, PostgreSQL, CockroachDB, MySQL, MariaDB, Oracle, SQL Server, Db2, DuckDB, BigQuery, and Spanner.
+Every SQLSpec driver exposes `session.data_dictionary`—a dialect-aware catalog introspection and DDL synthesis interface returning normalized typed metadata (`TableMetadata`, `ColumnMetadata`, `IndexMetadata`, `ForeignKeyMetadata`, `ColumnProfileMetadata`, `DDLResult`, `VersionInfo`) across SQLite, PostgreSQL, CockroachDB, MySQL, MariaDB, Oracle, SQL Server, Sybase ASE, SAP SQL Anywhere, IBM Db2, DuckDB, BigQuery, and Cloud Spanner.
+
+```mermaid
+flowchart LR
+ Session["session.data_dictionary"] --> Cache["L1 / L2 Metadata Cache"]
+ Cache --> Loader["Dialect SQL Catalog (.sql)"]
+ Loader --> Normalize["Normalized Metadata Structs"]
+ Normalize --> DDL["Native / AST DDL & Transpilation"]
+```
+
+For the complete catalog of SQL introspection queries and `DialectConfig` profiles executed per engine, see the [Dialect SQL Catalogs Reference](/reference/data-dictionary/dialects/).
## Introspecting Tables, Columns, Indexes, and Foreign Keys
@@ -78,7 +88,7 @@ with spec.provide_session(config) as session:
| --- | --- |
| `TableMetadata` | `table_name`, `schema_name`, `table_type`, `dependency_level` |
| `ColumnMetadata` | `column_name`, `data_type`, `is_nullable`, `column_default`, `identity_generation`, `is_generated`, `generation_expression` |
-| `IndexMetadata` | `index_name`, `table_name`, `column_names`, `is_unique`, `is_primary` |
+| `IndexMetadata` | `index_name`, `table_name`, `columns`, `is_unique`, `is_primary` |
| `ForeignKeyMetadata` | `constraint_name`, `table_name`, `column_name`, `referenced_table`, `referenced_column`, `on_delete`, `on_update` |
| `ColumnProfileMetadata` | `column_name`, `declared_type`, `inferred_type`, `row_count`, `null_count`, `distinct_count`, `nullable`, `max_char_length`, `is_unique_candidate` |
| `DDLResult` | `identity`, `status`, `fidelity`, `source`, `ddl`, `dependencies`, `warnings` |
diff --git a/docs/learn/index.md b/docs/learn/index.md
index 0141e6495..228d531a0 100644
--- a/docs/learn/index.md
+++ b/docs/learn/index.md
@@ -2,7 +2,7 @@
This curriculum walks through SQLSpec from your first in-memory SQLite query to production connection management, Arrow analytics, observability, and multi-database recipes.
-Every core guide runs out of the box against Python's built-in `sqlite3` module (or `aiosqlite` for `asyncio`) so you can copy and run examples locally without starting external database containers. When you are ready to connect to PostgreSQL, Oracle, DuckDB, Spanner, BigQuery, MySQL, SQL Server, or Db2, pair these guides with the adapter setup pages under [Databases](/databases/).
+Every core guide runs out of the box against Python's built-in `sqlite3` module (or `aiosqlite` for `asyncio`) so you can copy and run examples locally without starting external database containers. When you are ready to connect to PostgreSQL, CockroachDB, Oracle, DuckDB, Spanner, BigQuery, MySQL, MariaDB, SQL Server, Sybase ASE, SAP SQL Anywhere, IBM Db2, ADBC, or Arrow ODBC, pair these guides with the adapter setup pages under [Databases](/databases/).
## Execution Architecture
diff --git a/docs/learn/installation.md b/docs/learn/installation.md
index 6a791af36..7d3e65c2b 100644
--- a/docs/learn/installation.md
+++ b/docs/learn/installation.md
@@ -16,6 +16,18 @@ SQLSpec targets **Python 3.11 and newer** (`3.11` through `3.15`). Installing th
pip install sqlspec
```
+=== "pdm"
+
+ ```bash
+ pdm add sqlspec
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add sqlspec
+ ```
+
Combine multiple optional extras in a single install command when adding drivers, serializers, or framework plugins:
=== "uv"
@@ -30,6 +42,18 @@ Combine multiple optional extras in a single install command when adding drivers
pip install "sqlspec[asyncpg,msgspec,litestar,opentelemetry]"
```
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[asyncpg,msgspec,litestar,opentelemetry]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[asyncpg,msgspec,litestar,opentelemetry]"
+ ```
+
## Database Driver Extras
Install only the driver extras needed by your deployment target. See [Databases](/databases/) for adapter-by-adapter connection and pool configuration.
@@ -43,6 +67,8 @@ Install only the driver extras needed by your deployment target. See [Databases]
| **MySQL / MariaDB** | `asyncmy`, `aiomysql`, `pymysql`, `mysql-connector` | `asyncmy>=0.2.13`, `aiomysql`, `pymysql`, or `mysql-connector-python` |
| **Oracle** | `oracledb` | `oracledb>=3.4` (Thin mode built-in; optional Instant Client for Thick mode) |
| **SQL Server** | `mssql-python`, `pymssql` | `mssql-python>=1.13` or `pymssql` |
+| **Sybase ASE** | `sybase` | `pymssql>=2.4.4` (FreeTDS TDS 5.0 wire protocol) |
+| **SAP SQL Anywhere** | `sqlanywhere` | `sqlanydb>=1.0.14` (`dbcapi` native client wrapper) |
| **IBM Db2** | `db2` | `ibm_db>=3.3.0` |
| **DuckDB** | `duckdb` | `duckdb`, `pyarrow`, `pytz` |
| **Google Cloud** | `bigquery`, `spanner` | `google-cloud-bigquery` (+ Storage/GCS) or `google-cloud-spanner>=3.64.0` |
diff --git a/docs/learn/migrations.md b/docs/learn/migrations.md
index 34f0def1c..a49fea6a1 100644
--- a/docs/learn/migrations.md
+++ b/docs/learn/migrations.md
@@ -2,6 +2,14 @@
SQLSpec includes a built-in schema migration runner supporting sequential `.sql` and `.py` revisions across every sync and async adapter—without requiring SQLAlchemy or Alembic when your project is pure SQL.
+```mermaid
+flowchart LR
+ Author["SQL / Python Revisions"] --> Upgrade["upgrade / downgrade"]
+ Upgrade --> Tracker[("ddl_migrations Table")]
+ Tracker --> Verify["verify --strict"]
+ Verify --> Squash["squash & dump-schema"]
+```
+
## Configure `migration_config` and `pyproject.toml`
Enable migrations on any adapter configuration by supplying `migration_config`:
@@ -24,7 +32,7 @@ Common `MigrationConfig` keys:
- `default_schema`: Default target schema for unqualified DDL objects.
- `version_table_schema`: Dedicated admin schema holding the version-tracking table.
- `include_extensions`: Optional list of extension schemas to bundle (such as `["litestar", "adk", "events"]`).
-- `schema_dump_path`: Default output path written by `dump_schema()` (`/schema.sql` when unset).
+- `schema_dump_path`: Default output path written by `dump_schema()` when `output_path` is omitted (`auto_dump_schema=True` falls back to `/schema.sql` when `schema_dump_path` is unset).
- `auto_dump_schema`: Set to `True` (or a file path) to refresh `schema.sql` automatically after every `upgrade` that applies revisions.
## Authoring SQL and Python Revisions
@@ -107,8 +115,6 @@ When `upgrade()` runs against downstream environments after a squash has been co
2. **No replaced versions applied**: Fresh databases execute the squashed migration once.
3. **Partial overlap**: If only a subset of replaced versions was applied, `upgrade()` and `verify --strict` raise an error rather than guessing which statements remain unapplied.
-Once every environment has reconciled onto the squashed revision, remove the `-- Squashed from:` / `__replaces__` header line to finalize the baseline.
-
## Schema Snapshots, Verification, and Data Bootstrap
- **Read-Only `verify`**: Checks applied tracker rows against migration files on disk without creating `version_table_name` on empty databases. Combine `--strict` with `--fail-on-pending` as a pre-deploy CI gate.
diff --git a/docs/learn/parameters.md b/docs/learn/parameters.md
index be7e9a571..3e15228bd 100644
--- a/docs/learn/parameters.md
+++ b/docs/learn/parameters.md
@@ -56,6 +56,13 @@ session.execute_many(
## Cross-Dialect Placeholder Translation
+```mermaid
+flowchart LR
+ Params["Input SQL + Parameters (:name, ?, $1)"] --> Lexer["SQL Lexer (Skips Strings & Casts)"]
+ Lexer --> Profile["Dialect Parameter Profile"]
+ Profile --> Wire["Compiled SQL + Bound Wire Args"]
+```
+
| Input Style Written in SQL | Example | Compiled Output (`asyncpg`) | Compiled Output (`sqlite` / `duckdb`) | Compiled Output (`spanner`) |
| --- | --- | --- | --- | --- |
| Named colon (`:name`) | `WHERE status = :status` | `WHERE status = $1` | `WHERE status = ?` | `WHERE status = @status` |
diff --git a/docs/learn/performance.md b/docs/learn/performance.md
index 8a6678b82..74805c932 100644
--- a/docs/learn/performance.md
+++ b/docs/learn/performance.md
@@ -65,9 +65,29 @@ SQLSpec compiles `162` hot-path modules—covering `core`, `driver`, `builder`,
Install the `mypyc` and `performance` extras in production deployments so SQLGlot also uses its compiled C tokenizer/parser (`sqlglot[c]`) alongside `msgspec`:
-```bash
-uv add "sqlspec[mypyc,performance]"
-```
+=== "uv"
+
+ ```bash
+ uv add "sqlspec[mypyc,performance]"
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install "sqlspec[mypyc,performance]"
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add "sqlspec[mypyc,performance]"
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add "sqlspec[mypyc,performance]"
+ ```
## Fast Typed Hydration with `msgspec.Struct`
@@ -88,8 +108,9 @@ For serialization on hot HTTP or queue paths, pair `msgspec.Struct` with SQLSpec
## Batched Writes and Arrow Thresholds
- **Prefer `execute_many` or `select_to_arrow` over row loops**: Single-pass parameter preparation in `execute_many` avoids repeated per-row compilation.
-- **DuckDB Hybrid Materialization**: Small DuckDB point lookups (`< 64` rows) and Arrow-divergent types (`HUGEINT`, `MAP`, `INTERVAL`, `TIMETZ`) materialize directly via `fetchall()` to avoid ~110 µs Arrow conversion overhead on 1-row reads, while larger analytical scans stream natively through Arrow.
-- **Large Analytical Exports**: Use `session.select_to_arrow(..., return_format="batches")` or `session.select_stream(..., chunk_size=1000)` to bound resident memory on multi-million-row reads.
+- **DuckDB Hybrid Materialization & Container Memory Clamping**: Small DuckDB point lookups (`< 64` rows) and Arrow-divergent types (`HUGEINT`, `MAP`, `INTERVAL`, `TIMETZ`) materialize directly via `fetchall()` to avoid ~110 µs Arrow conversion overhead on 1-row reads, while larger analytical scans stream natively through Arrow. In containerized deployments (Kubernetes / Cloud Run), `DuckDBConfig` inspects Linux cgroup v1/v2 memory limits (`resolve_cgroup_memory_limit`) so DuckDB's buffer manager sizes its default `memory_limit` to the container quota rather than host physical RAM.
+- **Large Analytical Exports & Streaming Coercion**: Use `session.select_to_arrow(..., return_format="batches")`, `pipeline.read_arrow_batches(...)`, or `session.select_stream(..., chunk_size=1000)` to bound resident memory on multi-million-row reads and bulk loads.
+- **Multi-Tier Metadata Caching (`MultiTierCache`)**: Pair in-process `UnifiedCache` (L1) with an external `CacheBackendProtocol` (L2, such as Redis or Memcached via `MultiTierCache`) when sharing expensive catalog introspection or schema metadata across multi-worker processes.
## Summary Checklist
diff --git a/docs/learn/query-builder.md b/docs/learn/query-builder.md
index 260edfebd..d6327b8af 100644
--- a/docs/learn/query-builder.md
+++ b/docs/learn/query-builder.md
@@ -131,7 +131,7 @@ events_ddl = CreateTable.from_records(
).to_sql()
```
-`CreateTable.from_metadata`, `CreateTable.from_arrow_schema`, and `CreateTable.from_records` map types and constraints into dialect-valid SQL across PostgreSQL, SQLite, DuckDB, MySQL, Oracle, SQL Server, Db2, Spanner, and BigQuery.
+`CreateTable.from_metadata`, `CreateTable.from_arrow_schema`, and `CreateTable.from_records` map types and constraints into dialect-valid SQL across PostgreSQL, CockroachDB, SQLite, DuckDB, MySQL, MariaDB, Oracle, SQL Server, Sybase ASE, SAP SQL Anywhere, IBM Db2, Spanner, and BigQuery.
## Raw SQL Fragments and `RETURNING`
@@ -145,7 +145,7 @@ Only pass trusted, static SQL expressions to `sql.raw(...)`; bind user-supplied
## Dialect Transpilation and Optimization Notes
-- **Automatic Dialect Targeting**: When executed through `session.execute(query)`, the builder compiles against the session's configured dialect (`postgres`, `sqlite`, `oracle`, `bigquery`, `spanner`, `spangres`, `duckdb`, `db2`, `tsql`, `mysql`) automatically.
+- **Automatic Dialect Targeting**: When executed through `session.execute(query)`, the builder compiles against the session's configured dialect (`postgres`, `cockroachdb`, `sqlite`, `oracle`, `bigquery`, `spanner`, `spangres`, `duckdb`, `db2`, `tsql`, `sybase`, `sqlanywhere`, `mysql`, `mariadb`) automatically.
- **Preserving Verbatim Identifier Casing**: By default, `StatementConfig(enable_optimization=True)` runs lightweight SQLGlot qualification and simplification rules. On engines like BigQuery where optimizer qualification lowercases column aliases, pass `StatementConfig(dialect="bigquery", enable_optimization=False)` if exact input casing must be preserved verbatim.
- **`sqlglot[c]` Subclassing Compatibility**: When `sqlspec[mypyc]` (`sqlglot[c]`) is installed, SQLGlot's core AST classes are compiled C extensions; compose queries with `sql.*` and `sql.raw(...)` rather than subclassing `sqlglot.exp` nodes at runtime.
diff --git a/docs/learn/querying.md b/docs/learn/querying.md
index 4850845f4..b95de21ec 100644
--- a/docs/learn/querying.md
+++ b/docs/learn/querying.md
@@ -2,6 +2,14 @@
Every SQLSpec driver exposes a uniform set of query methods on `session` (sync) and `await session` (async) so you can express row cardinality intent directly at the call site without unpacking raw DB-API cursors.
+```mermaid
+flowchart LR
+ Input["SQL String / Named SQL / Builder"] --> Compile["Compile & Statement Cache"]
+ Compile --> Execute["Adapter Wire Execution"]
+ Execute --> Result["SQLResult / ArrowResult"]
+ Result --> Output["dict / msgspec / Pydantic / Arrow"]
+```
+
## Choosing the Right Execution Method
| Method | Return Value | Behavior on Empty Result |
diff --git a/docs/learn/recipes/etl-and-bulk-ingest.md b/docs/learn/recipes/etl-and-bulk-ingest.md
index 189a91237..8f96d6b4b 100644
--- a/docs/learn/recipes/etl-and-bulk-ingest.md
+++ b/docs/learn/recipes/etl-and-bulk-ingest.md
@@ -1,8 +1,16 @@
# ETL and Bulk Ingest
-Moving data between operational databases, analytical engines, and cloud object storage requires bounded memory usage and batched or columnar execution. SQLSpec provides three graduated tiers depending on dataset volume: batched DB-API writes (`execute_many` + `select_stream`), zero-copy **Apache Arrow** transfers (`select_to_arrow` + `load_from_arrow`), and direct **Storage Bridge** jobs (`select_to_storage` + `load_from_storage`).
+Moving data between operational databases, analytical engines, and cloud object storage requires bounded memory usage and batched or columnar execution. SQLSpec provides graduated execution paths depending on dataset volume: batched DB-API writes (`execute_many` + `select_stream`), zero-copy **Apache Arrow** transfers (`select_to_arrow` + `load_from_arrow`), and direct **Storage Bridge** jobs (`select_to_storage` + `load_from_storage`).
+
+```mermaid
+flowchart LR
+ Source["Source DB / Parquet Shards"] --> Stream["RecordBatch / Row Stream"]
+ Stream --> Coerce["Target Schema Coercion"]
+ Coerce --> Bulk["Native Bulk Load (PGCOPY / BCP / Arrow / Stage)"]
+ Bulk --> Target[("Target Table / Upsert")]
+```
-## Two-Database Extract, Transform, and Load
+## Cross-Database Extract, Transform, and Load
Register source and target configurations on a single `SQLSpec` instance to extract filtered rows from an upstream store and batch-load transformed records into a downstream target:
@@ -116,10 +124,19 @@ with (
Several adapters accelerate `load_from_arrow` and `load_from_storage` using native wire or server-side bulk paths automatically:
-- **PostgreSQL (`psycopg` / `asyncpg` / `adbc`)**: Uses binary `COPY` / Arrow C-stream ingestion for high-throughput table loads.
+- **PostgreSQL (`psycopg` / `asyncpg` / `psqlpy` / `adbc`)**: Uses binary `PGCOPY` streaming (`sqlspec.core.pgcopy`, including binary `pgvector` framing for `FIXED_SIZE_LIST` columns) and Arrow C-stream ingestion for high-throughput table loads.
+- **SQL Server (`mssql_python`) & Arrow ODBC (`arrow_odbc`)**: Streams Arrow batches directly into native `BulkCopy` (`BCP`) or Rust ODBC batch writers (`bulk_insert_arrow`).
- **DuckDB & ADBC**: Zero-copy Arrow C Data Interface (`__arrow_c_stream__`) registration and native Parquet reads/writes.
- **Oracle Autonomous Database (`oracledb`)**: Supports `DBMS_CLOUD.COPY_DATA` server-side object-store ingestion when `driver_features={"dbms_cloud_credential": "..."}` is configured.
-- **BigQuery**: Streams columnar reads via the BigQuery Storage Read API and loads Parquet/Arrow via Storage Write / load jobs.
+- **BigQuery & Cloud Spanner**: Streams columnar reads via the BigQuery Storage Read API, loads Parquet/Arrow via Storage Write / load jobs, and splits Spanner batch mutations automatically under Spanner's per-commit mutation budget (`estimate_batch_mutations`).
+
+## Multi-Shard Fan-Out, Staged Bulk Upserts & Index Drop/Restore
+
+When ingesting partitioned Parquetprefixes (`s3://bucket/events/*.parquet`), resuming interrupted jobs, or bulk-loading into indexed tables, combine the advanced orchestration parameters on `load_from_storage`, `load_from_arrow`, and `session.data_dictionary.without_indexes`:
+
+- **Multi-Shard Parallel Fan-Out & Checkpointing**: Pass a glob URI alongside `max_workers=4` and `checkpoint_table="etl_ingest_checkpoints"` to `load_from_storage(...)`. SQLSpec resolves all matching shards via `StorageRegistry`, skips already-recorded shard URIs in `checkpoint_table`, ingests unapplied shards concurrently across worker sessions, and records each shard upon completion.
+- **Cross-Dialect Staged Bulk Upsert (`stage_table` + `conflict_keys`)**: Pass `stage_table="events_staging"` and `conflict_keys=["id"]` (plus optional `update_columns=[...]` or `exclude_update_columns=[...]`) to bulk-load batches into a staging table at wire speed (`PGCOPY`, `BCP`, or Arrow ingest) and atomically merge into the target table via dialect-native upsert SQL (`INSERT ... ON CONFLICT DO UPDATE` on PostgreSQL/CockroachDB/SQLite/DuckDB, `ON DUPLICATE KEY UPDATE` on MySQL/MariaDB, `MERGE INTO` on Oracle/SQL Server/Db2/BigQuery/Sybase/SQL Anywhere, and `INSERT OR UPDATE` mutations on Cloud Spanner).
+- **Index Drop and Recreate Context Manager (`without_indexes`)**: Wrap massive initial table loads in `with session.data_dictionary.without_indexes(session, "events"):` (or `async with session.data_dictionary.without_indexes(session, "events"):`) to capture secondary index DDL, drop non-constraint indexes before bulk ingestion, and recreate them in a single pass afterward.
## Recap
diff --git a/docs/learn/recipes/service-layer.md b/docs/learn/recipes/service-layer.md
index f45270d9e..9952cdbf1 100644
--- a/docs/learn/recipes/service-layer.md
+++ b/docs/learn/recipes/service-layer.md
@@ -24,127 +24,157 @@ When constructed with `config=`, a service checks out a short-lived pooled sessi
## Production Service Architecture Patterns
-In real-world architectures, services fall into two lifetime patterns:
+In real-world architectures, services follow either a bounded-session or request-scoped lifetime pattern:
-### Pattern 1: Bounded-Session Domain Services (`config=` + `provide_session()`)
+### Bounded-Session Domain Services (`config=` + `provide_session()`)
When a service coordinates external asynchronous I/O (such as password hashing, third-party REST APIs, or LLM embeddings), do not keep a database connection checked out during the entire operation. Instead, initialize the service with `config=` and check out short-lived sessions only when executing database commands:
-```python
-"""Bounded-session customer domain service using AsyncpgConfig and SQLFileLoader."""
+=== "SQL (`app/sql/customers.sql`)"
-from collections.abc import Sequence
-import msgspec
+ ```sql
+ -- name: get-customer-by-email
+ SELECT id, email, tier
+ FROM customers;
-from sqlspec import SQLFileLoader, StatementStack
-from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver
-from sqlspec.core import LimitOffsetFilter, OffsetPagination
-from sqlspec.service import SQLSpecAsyncService
+ -- name: list-customers
+ SELECT id, email, tier
+ FROM customers
+ ORDER BY id;
+ ```
+=== "Python (`app/services/customers.py`)"
-class Customer(msgspec.Struct, frozen=True, gc=False):
- """Customer entity representation."""
+ ```python
+ """Bounded-session customer service using AsyncpgConfig and SQLFileLoader."""
- id: int
- email: str
- tier: str
+ from collections.abc import Sequence
+ import msgspec
+ from sqlspec import SQLFileLoader, StatementStack
+ from sqlspec.adapters.asyncpg import AsyncpgConfig, AsyncpgDriver
+ from sqlspec.core import LimitOffsetFilter, OffsetPagination
+ from sqlspec.service import SQLSpecAsyncService
-class CustomerService(SQLSpecAsyncService[AsyncpgDriver]):
- """Domain service managing customer accounts and tier lifecycle."""
- def __init__(self, config: AsyncpgConfig, loader: SQLFileLoader) -> None:
- super().__init__(config=config, loader=loader)
+ class Customer(msgspec.Struct, frozen=True, gc=False):
+ """Customer entity representation."""
- async def get_by_email(self, email: str) -> Customer:
- """Fetch customer by email, checking out a short-lived connection."""
- query = self.get_sql("get-customer-by-email").where("email = :email")
- return await self.get_one(query, email=email, schema_type=Customer)
+ id: int
+ email: str
+ tier: str
- async def list_customers(
- self, *, limit: int = 25, offset: int = 0
- ) -> OffsetPagination[Customer]:
- """Paginate customers without holding a connection between pages."""
- query = self.get_sql("list-customers")
- return await self.paginate_limit_offset(
- query,
- LimitOffsetFilter(limit=limit, offset=offset),
- schema_type=Customer,
- )
- async def upgrade_tier_with_audit(
- self, customer_id: int, new_tier: str, audit_events: Sequence[str]
- ) -> None:
- """Execute atomic multi-table updates inside an explicit transaction."""
- stack = (
- StatementStack()
- .push_execute(
- """
- UPDATE customers
- SET tier = :tier
- WHERE id = :id
- """,
- tier=new_tier,
- id=customer_id,
+ class CustomerService(SQLSpecAsyncService[AsyncpgDriver]):
+ """Domain service managing customer accounts and tier lifecycle."""
+
+ def __init__(self, config: AsyncpgConfig, loader: SQLFileLoader) -> None:
+ super().__init__(config=config, loader=loader)
+
+ async def get_by_email(self, email: str) -> Customer:
+ """Fetch customer by email, checking out a short-lived connection."""
+ query = self.get_sql("get-customer-by-email").where("email = :email")
+ return await self.get_one(query, email=email, schema_type=Customer)
+
+ async def list_customers(
+ self, *, limit: int = 25, offset: int = 0
+ ) -> OffsetPagination[Customer]:
+ """Paginate customers without holding a connection between pages."""
+ query = self.get_sql("list-customers")
+ return await self.paginate_limit_offset(
+ query,
+ LimitOffsetFilter(limit=limit, offset=offset),
+ schema_type=Customer,
)
- .push_execute_many(
- """
- INSERT INTO tier_audit (customer_id, new_tier, event_type)
- VALUES (:id, :tier, :event)
- """,
- [
- {"id": customer_id, "tier": new_tier, "event": evt}
- for evt in audit_events
- ],
+
+ async def upgrade_tier_with_audit(
+ self, customer_id: int, new_tier: str, audit_events: Sequence[str]
+ ) -> None:
+ """Execute atomic multi-table updates inside an explicit transaction."""
+ stack = (
+ StatementStack()
+ .push_execute(
+ """
+ UPDATE customers
+ SET tier = :tier
+ WHERE id = :id
+ """,
+ tier=new_tier,
+ id=customer_id,
+ )
+ .push_execute_many(
+ """
+ INSERT INTO tier_audit (customer_id, new_tier, event_type)
+ VALUES (:id, :tier, :event)
+ """,
+ [
+ {"id": customer_id, "tier": new_tier, "event": evt}
+ for evt in audit_events
+ ],
+ )
)
- )
- async with self.begin_transaction() as session:
- await session.execute_stack(stack)
-```
+ async with self.begin_transaction() as session:
+ await session.execute_stack(stack)
+ ```
-### Pattern 2: Request-Scoped Driver Services (`session=driver`)
+### Request-Scoped Driver Services (`session=driver`)
When using dependency injection frameworks like Dishka or Litestar where a driver connection is already checked out for the lifetime of an HTTP request, inject the `driver` directly into `SQLSpecAsyncService(session)`:
-```python
-"""Request-scoped product service receiving an injected driver instance."""
+=== "SQL (`app/sql/products.sql`)"
-import msgspec
+ ```sql
+ -- name: get-product
+ SELECT id, sku, name, price_cents
+ FROM products;
-from sqlspec import SQLSpec
-from sqlspec.adapters.asyncpg import AsyncpgDriver
-from sqlspec.core import FilterTypes, OffsetPagination
-from sqlspec.service import SQLSpecAsyncService
+ -- name: list-products
+ SELECT id, sku, name, price_cents
+ FROM products
+ ORDER BY id;
+ ```
+=== "Python (`app/services/products.py`)"
-class Product(msgspec.Struct, frozen=True, gc=False):
- """Product domain entity."""
+ ```python
+ """Request-scoped product service receiving an injected driver instance."""
- id: int
- sku: str
- name: str
- price_cents: int
+ import msgspec
+ from sqlspec import SQLSpec
+ from sqlspec.adapters.asyncpg import AsyncpgDriver
+ from sqlspec.core import FilterTypes, OffsetPagination
+ from sqlspec.service import SQLSpecAsyncService
-class ProductService(SQLSpecAsyncService[AsyncpgDriver]):
- """Request-scoped service operating on a checked-out driver session."""
- def __init__(self, driver: AsyncpgDriver, spec: SQLSpec) -> None:
- super().__init__(driver)
- self.spec = spec
+ class Product(msgspec.Struct, frozen=True, gc=False):
+ """Product domain entity."""
- async def get_by_id(self, product_id: int) -> Product:
- """Retrieve a product by ID or raise NotFoundError."""
- query = self.spec.get_sql("get-product").where("id = :id")
- return await self.get_one(query, id=product_id, schema_type=Product)
+ id: int
+ sku: str
+ name: str
+ price_cents: int
- async def list_products(
- self, *filters: FilterTypes
- ) -> OffsetPagination[Product]:
- """Filter and paginate products using precompiled SQL templates."""
- query = self.spec.get_sql("list-products")
- return await self.paginate(query, *filters, schema_type=Product)
-```
+
+ class ProductService(SQLSpecAsyncService[AsyncpgDriver]):
+ """Request-scoped service operating on a checked-out driver session."""
+
+ def __init__(self, driver: AsyncpgDriver, spec: SQLSpec) -> None:
+ super().__init__(driver)
+ self.spec = spec
+
+ async def get_by_id(self, product_id: int) -> Product:
+ """Retrieve a product by ID or raise NotFoundError."""
+ query = self.spec.get_sql("get-product").where("id = :id")
+ return await self.get_one(query, id=product_id, schema_type=Product)
+
+ async def list_products(
+ self, *filters: FilterTypes
+ ) -> OffsetPagination[Product]:
+ """Filter and paginate products using precompiled SQL templates."""
+ query = self.spec.get_sql("list-products")
+ return await self.paginate(query, *filters, schema_type=Product)
+ ```
## Sharing an Existing Session Across Multiple Services
diff --git a/docs/learn/sql-files.md b/docs/learn/sql-files.md
index 950578d9a..00910d955 100644
--- a/docs/learn/sql-files.md
+++ b/docs/learn/sql-files.md
@@ -14,9 +14,20 @@ For small query registries, register named statements directly on `SQLSpec` with
Group related queries inside `.sql` files using `-- name: ` comment headers and load individual files or entire directories with `spec.load_sql_files(...)` (or `SQLFileLoader` directly):
-```python
---8<-- "docs/examples/sql_files/load_sql_files.py:example"
-```
+=== "SQL (`queries.sql`)"
+
+ ```sql
+ -- name: list_teams
+ SELECT id, name
+ FROM teams
+ ORDER BY id;
+ ```
+
+=== "Python"
+
+ ```python
+ --8<-- "docs/examples/sql_files/load_sql_files.py:example"
+ ```
Query names inside nested directories can be organized by namespace (or hyphenated identifiers normalized automatically when accessed) and shared across any session opened from `spec`. `SQLFileLoader` also integrates with `StorageRegistry` so SQL bundles can be loaded from local directories or registered object storage aliases.
@@ -24,31 +35,62 @@ Query names inside nested directories can be organized by namespace (or hyphenat
A single `.sql` file can group all reads and writes for a domain module under distinct `-- name:` headers, with optional dialect annotations and `-- param:` declarations:
-```sql
--- name: create_invoices_table
-CREATE TABLE IF NOT EXISTS invoices (
- id INTEGER PRIMARY KEY,
- customer_id INTEGER NOT NULL,
- status TEXT NOT NULL,
- total_cents INTEGER NOT NULL
-);
-
--- name: insert_invoice
--- param: customer_id int Account identifier
--- param: status str Initial invoice state
--- param: total_cents int Invoice total in minor currency units
-INSERT INTO invoices (customer_id, status, total_cents)
-VALUES (:customer_id, :status, :total_cents);
-
--- name: list_customer_invoices
--- param: customer_id int Account identifier
--- param: status str? Optional status filter
-SELECT id, customer_id, status, total_cents
-FROM invoices
-WHERE customer_id = :customer_id
- AND (:status IS NULL OR status = :status)
-ORDER BY id DESC;
-```
+=== "SQL (`app/sql/billing.sql`)"
+
+ ```sql
+ -- name: create_invoices_table
+ CREATE TABLE IF NOT EXISTS invoices (
+ id INTEGER PRIMARY KEY,
+ customer_id INTEGER NOT NULL,
+ status TEXT NOT NULL,
+ total_cents INTEGER NOT NULL
+ );
+
+ -- name: insert_invoice
+ -- param: customer_id int Account identifier
+ -- param: status str Initial invoice state
+ -- param: total_cents int Invoice total in minor currency units
+ INSERT INTO invoices (customer_id, status, total_cents)
+ VALUES (:customer_id, :status, :total_cents);
+
+ -- name: list_customer_invoices
+ -- param: customer_id int Account identifier
+ -- param: status str? Optional status filter
+ SELECT id, customer_id, status, total_cents
+ FROM invoices
+ WHERE customer_id = :customer_id
+ AND (:status IS NULL OR status = :status)
+ ORDER BY id DESC;
+ ```
+
+=== "Python"
+
+ ```python
+ from pathlib import Path
+
+ from sqlspec import SQLSpec
+ from sqlspec.adapters.sqlite import SqliteConfig
+
+ spec = SQLSpec()
+ config = spec.add_config(
+ SqliteConfig(connection_config={"database": ":memory:"})
+ )
+ spec.load_sql_files(Path("app/sql/billing.sql"))
+
+ with spec.provide_session(config) as session:
+ session.execute(spec.get_sql("create_invoices_table"))
+ session.execute(
+ spec.get_sql("insert_invoice"),
+ customer_id=42,
+ status="paid",
+ total_cents=9500,
+ )
+ invoices = session.select(
+ spec.get_sql("list_customer_invoices"),
+ customer_id=42,
+ status="paid",
+ )
+ ```
### Organizing Query Directories
@@ -62,9 +104,28 @@ When `spec.load_sql_files(Path("app/sql"))` scans a directory tree, you can keep
Add `-- param: [?] ` headers immediately below `-- name:` to document expected arguments and enforce automatic required-parameter validation before a query ever touches the database connection:
-```python
---8<-- "docs/examples/sql_files/declared_params.py:example"
-```
+=== "SQL (`teams.sql`)"
+
+ ```sql
+ -- name: get_team_by_name
+ -- param: name str The team name to look up
+ SELECT id, name
+ FROM teams
+ WHERE name = :name;
+
+ -- name: list_teams
+ -- param: name str? Optional team name filter
+ SELECT id, name
+ FROM teams
+ WHERE (:name IS NULL OR name = :name)
+ ORDER BY id;
+ ```
+
+=== "Python"
+
+ ```python
+ --8<-- "docs/examples/sql_files/declared_params.py:example"
+ ```
Suffixing the type with `?` (such as `str?` or `int?`) marks the parameter optional and defaults omitted keyword arguments to `None` (`NULL`), enabling clean `(:name IS NULL OR name = :name)` filter patterns in pure SQL. Omitting a required parameter raises `SQLSpecError` immediately with the missing parameter name.
@@ -72,9 +133,19 @@ Suffixing the type with `?` (such as `str?` or `int?`) marks the parameter optio
Static `.sql` files often need optional runtime predicates for search, tenant scoping, or pagination. Because `spec.get_sql(...)` returns an immutable `SQL` statement object, calling `.where(...)`, `.where_eq(...)`, `.where_like(...)`, or `.paginate(...)` returns a new derived `SQL` instance without mutating the cached base template:
-```python
---8<-- "docs/examples/sql_files/dynamic_where.py:example"
-```
+=== "SQL (`users.sql`)"
+
+ ```sql
+ -- name: list_users
+ SELECT id, name, status
+ FROM users
+ ```
+
+=== "Python"
+
+ ```python
+ --8<-- "docs/examples/sql_files/dynamic_where.py:example"
+ ```
You can also pass `StatementFilter` objects (`LimitOffsetFilter`, `SearchFilter`, `OrderByFilter`) alongside `spec.get_sql("...")` inside `session.select(...)`—remembering to keep all bound parameters named when filters are attached.
@@ -82,54 +153,59 @@ You can also pass `StatementFilter` objects (`LimitOffsetFilter`, `SearchFilter`
In production services, organize queries using `kebab-case` identifiers (`-- name: list-products`, `-- name: get-product`) matching domain entity routes. Combine loaded statements with `.where(...)` and decode rows directly into immutable `msgspec.Struct` models:
-```sql
--- name: list-products
--- param: sku str? Optional SKU match
-SELECT id, sku, name, price_cents
-FROM products
-WHERE (:sku IS NULL OR sku = :sku)
-ORDER BY id;
-
--- name: get-product
--- param: id int Product identifier
-SELECT id, sku, name, price_cents
-FROM products
-WHERE id = :id;
-```
+=== "SQL (`products.sql`)"
-```python
-from pathlib import Path
-import msgspec
+ ```sql
+ -- name: list-products
+ -- param: sku str? Optional SKU match
+ SELECT id, sku, name, price_cents
+ FROM products
+ WHERE (:sku IS NULL OR sku = :sku)
+ ORDER BY id;
-from sqlspec import SQLSpec
-from sqlspec.adapters.sqlite import SqliteConfig
+ -- name: get-product
+ -- param: id int Product identifier
+ SELECT id, sku, name, price_cents
+ FROM products
+ WHERE id = :id;
+ ```
+=== "Python"
-class Product(msgspec.Struct, frozen=True, gc=False):
- """Typed product projection."""
+ ```python
+ from pathlib import Path
+ import msgspec
- id: int
- sku: str
- name: str
- price_cents: int
+ from sqlspec import SQLSpec
+ from sqlspec.adapters.sqlite import SqliteConfig
-spec = SQLSpec()
-config = spec.add_config(
- SqliteConfig(connection_config={"database": ":memory:"})
-)
+ class Product(msgspec.Struct, frozen=True, gc=False):
+ """Typed product projection."""
-with spec.provide_session(config) as session:
- query = spec.get_sql("get-product")
- product = session.select_one(query, id=1, schema_type=Product)
+ id: int
+ sku: str
+ name: str
+ price_cents: int
- active_query = spec.get_sql("list-products").where(
- "price_cents >= :min_price"
- )
- catalog = session.select(
- active_query, min_price=1000, sku=None, schema_type=Product
+
+ spec = SQLSpec()
+ config = spec.add_config(
+ SqliteConfig(connection_config={"database": ":memory:"})
)
-```
+ spec.load_sql_files(Path("products.sql"))
+
+ with spec.provide_session(config) as session:
+ query = spec.get_sql("get-product")
+ product = session.select_one(query, id=1, schema_type=Product)
+
+ active_query = spec.get_sql("list-products").where(
+ "price_cents >= :min_price"
+ )
+ catalog = session.select(
+ active_query, min_price=1000, sku=None, schema_type=Product
+ )
+ ```
## Recap
diff --git a/docs/learn/testing.md b/docs/learn/testing.md
index a6134fe82..176634e03 100644
--- a/docs/learn/testing.md
+++ b/docs/learn/testing.md
@@ -39,9 +39,29 @@ When testing `:memory:` SQLite across multiple pooled connections or threads, us
Dialect-specific features—such as PostgreSQL `JSONB` / `pgvector` / `LISTEN/NOTIFY`, Oracle 26ai `VECTOR`, Spanner `INTERLEAVE`, or DuckDB Parquet pipelines—should be verified against real database containers using [`pytest-databases`](https://github.com/litestar-org/pytest-databases):
-```bash
-uv add --dev pytest pytest-asyncio pytest-databases
-```
+=== "uv"
+
+ ```bash
+ uv add --dev pytest pytest-asyncio pytest-databases
+ ```
+
+=== "pip"
+
+ ```bash
+ pip install pytest pytest-asyncio pytest-databases
+ ```
+
+=== "pdm"
+
+ ```bash
+ pdm add -d pytest pytest-asyncio pytest-databases
+ ```
+
+=== "poetry"
+
+ ```bash
+ poetry add --group dev pytest pytest-asyncio pytest-databases
+ ```
```python
import pytest
@@ -79,7 +99,7 @@ Instead of maintaining brittle 200-line `INSERT` setup helpers in test files, st
- **Formats & Sharding**: Supports `.jsonl.gz`, `.json.gz`, `.jsonl`, and `.json` formats, including multi-file sharded fixtures (`.001.jsonl.gz`, `.002.jsonl.gz`, ... or `..001.jsonl.gz`) loaded in numeric sequence for seed datasets that exceed single-file repository limits.
- **Cross-Adapter Vector Coercion**: Automatically coerces embedding arrays (`list[float]`) across database engines on load (PostgreSQL/CockroachDB `CAST(... AS vector)`, MySQL `STRING_TO_VECTOR`, Oracle `array.array`, Spanner `TypedParameter`, SQLite `float32` bytes) and normalizes vectors back to `list[float]` on export.
-- **Idempotency & Sequence Sync**: `conflict_keys={"users": "email"}` keeps repeated loads idempotent, while `resync_sequences=True` resynchronizes standalone sequences, identity columns (preserving `GENERATED ALWAYS`, `GENERATED BY DEFAULT`, and `GENERATED BY DEFAULT ON NULL` modes), and auto-increment counters across supported database engines (PostgreSQL, CockroachDB, DuckDB, Oracle, SQL Server, SQLite, IBM Db2, MySQL, MariaDB) past explicit fixture IDs so subsequent test inserts never collide.
+- **Idempotency & Sequence Sync**: `conflict_keys={"users": "email"}` keeps repeated loads idempotent, while `resync_sequences=True` resynchronizes standalone sequences, identity columns (preserving `GENERATED ALWAYS`, `GENERATED BY DEFAULT`, and `GENERATED BY DEFAULT ON NULL` modes), and auto-increment counters across supported database engines (PostgreSQL, CockroachDB, DuckDB, Oracle, SQL Server, SQLite, IBM Db2, MySQL, MariaDB) past explicit fixture IDs so subsequent test inserts never collide. On MySQL, MariaDB, and Oracle, sequence and identity resynchronization runs `ALTER TABLE` or `ALTER SEQUENCE` DDL statements that issue an implicit commit of the active transaction; when wrapping tests in rollback-only transactions on those engines, seed and resynchronize sequences during outer fixture setup or leave `resync_sequences=False` inside the per-test transaction.
- **Schema Evolution**: `ignore_unknown_columns=True` and `exclude_update_columns` allow shared fixture files to evolve cleanly across schema branches.
- **Sharded Export**: `export_table_fixtures_sync` and `export_table_fixtures_async` accept `max_shard_bytes` and `max_rows_per_shard` to partition tables into numbered shards with atomic replacement and stale file cleanup.
diff --git a/docs/reference/adapters/adbc.md b/docs/reference/adapters/adbc.md
index 6dafd9a04..bfdd4a561 100644
--- a/docs/reference/adapters/adbc.md
+++ b/docs/reference/adapters/adbc.md
@@ -17,7 +17,6 @@ Apache Arrow Database Connectivity (ADBC) adapter supporting zero-copy Arrow ing
::: sqlspec.adapters.adbc.AdbcConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.adbc.AdbcConnectionParams
options:
@@ -31,7 +30,7 @@ Apache Arrow Database Connectivity (ADBC) adapter supporting zero-copy Arrow ing
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.adbc.AdbcDriver
options:
@@ -43,24 +42,31 @@ Apache Arrow Database Connectivity (ADBC) adapter supporting zero-copy Arrow ing
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.adbc.data_dictionary.AdbcDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
::: sqlspec.adapters.adbc.litestar.ADBCStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.adbc.adk.AdbcADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.adbc.adk.AdbcADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.adbc.adk.AdbcADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.adbc.events.AdbcEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/aiomysql.md b/docs/reference/adapters/aiomysql.md
index 136ec6788..48a86aee6 100644
--- a/docs/reference/adapters/aiomysql.md
+++ b/docs/reference/adapters/aiomysql.md
@@ -11,14 +11,13 @@ from sqlspec.adapters.aiomysql import (
)
```
-Asynchronous MySQL and MariaDB adapter built on `aiomysql`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [MySQL & MariaDB Setup Guide](../../databases/mysql.md) for usage examples.
+Asynchronous MySQL and MariaDB adapter built on `aiomysql`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [MySQL & MariaDB Setup Guide](../../databases/mysql.md) for usage examples and the [MySQL](../data-dictionary/dialects/mysql.md) and [MariaDB](../data-dictionary/dialects/mariadb.md) Data Dictionary SQL Catalogs.
## Configuration & Parameters
::: sqlspec.adapters.aiomysql.AiomysqlConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.aiomysql.AiomysqlConnectionParams
options:
@@ -38,7 +37,7 @@ Asynchronous MySQL and MariaDB adapter built on `aiomysql`. Inherited execution
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.aiomysql.AiomysqlDriver
options:
@@ -50,24 +49,37 @@ Asynchronous MySQL and MariaDB adapter built on `aiomysql`. Inherited execution
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.aiomysql.data_dictionary.AiomysqlDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.aiomysql.litestar.AiomysqlLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.aiomysql.litestar.AiomysqlStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.aiomysql.adk.AiomysqlADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.aiomysql.adk.AiomysqlADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.aiomysql.adk.AiomysqlADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.aiomysql.events.AiomysqlEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/aiosqlite.md b/docs/reference/adapters/aiosqlite.md
index 67b60f035..d7c51de3a 100644
--- a/docs/reference/adapters/aiosqlite.md
+++ b/docs/reference/adapters/aiosqlite.md
@@ -12,14 +12,13 @@ from sqlspec.adapters.aiosqlite import (
)
```
-Asynchronous SQLite adapter built on `aiosqlite`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [SQLite Setup Guide](../../databases/sqlite.md) for async pool and shared-cache in-memory URI configuration.
+Asynchronous SQLite adapter built on `aiosqlite`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [SQLite Setup Guide](../../databases/sqlite.md) for async pool and shared-cache in-memory URI configuration, and the [SQLite Data Dictionary & SQL Catalog](../data-dictionary/dialects/sqlite.md).
## Configuration & Parameters
::: sqlspec.adapters.aiosqlite.AiosqliteConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.aiosqlite.AiosqliteConnectionParams
options:
@@ -39,7 +38,7 @@ Asynchronous SQLite adapter built on `aiosqlite`. Inherited execution methods ar
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.aiosqlite.AiosqliteDriver
options:
@@ -54,30 +53,44 @@ Asynchronous SQLite adapter built on `aiosqlite`. Inherited execution methods ar
::: sqlspec.adapters.aiosqlite.AiosqliteConnectionPool
options:
heading_level: 3
- members:
- - acquire
- - release
- - close_all
- - size
-## Extension Stores
+::: sqlspec.adapters.aiosqlite.data_dictionary.AiosqliteDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.aiosqlite.litestar.AiosqliteLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.aiosqlite.litestar.AiosqliteStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.aiosqlite.adk.AiosqliteADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.aiosqlite.adk.AiosqliteADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.aiosqlite.adk.AiosqliteADKMemoryStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.aiosqlite.events.AiosqliteEventsConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.aiosqlite.events.AiosqliteEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/arrow-odbc.md b/docs/reference/adapters/arrow-odbc.md
index bfdd12b1b..9cc90f2b9 100644
--- a/docs/reference/adapters/arrow-odbc.md
+++ b/docs/reference/adapters/arrow-odbc.md
@@ -17,7 +17,6 @@ Apache Arrow over ODBC adapter built on `arrow-odbc` for zero-copy Arrow batch s
::: sqlspec.adapters.arrow_odbc.ArrowOdbcConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.arrow_odbc.ArrowOdbcConnectionParams
options:
@@ -31,36 +30,44 @@ Apache Arrow over ODBC adapter built on `arrow-odbc` for zero-copy Arrow batch s
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.arrow_odbc.ArrowOdbcDriver
options:
heading_level: 3
- members: false
+ members:
+ - bulk_insert_arrow
::: sqlspec.adapters.arrow_odbc.ArrowOdbcExceptionHandler
options:
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.arrow_odbc.data_dictionary.ArrowOdbcDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
::: sqlspec.adapters.arrow_odbc.litestar.ArrowOdbcStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.arrow_odbc.adk.ArrowOdbcADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.arrow_odbc.adk.ArrowOdbcADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.arrow_odbc.adk.ArrowOdbcADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.arrow_odbc.events.ArrowOdbcEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/asyncmy.md b/docs/reference/adapters/asyncmy.md
index 7f18e53b6..a2e949f75 100644
--- a/docs/reference/adapters/asyncmy.md
+++ b/docs/reference/adapters/asyncmy.md
@@ -11,14 +11,13 @@ from sqlspec.adapters.asyncmy import (
)
```
-Asynchronous MySQL and MariaDB adapter built on `asyncmy`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [MySQL & MariaDB Setup Guide](../../databases/mysql.md) for usage examples.
+Asynchronous MySQL and MariaDB adapter built on `asyncmy`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [MySQL & MariaDB Setup Guide](../../databases/mysql.md) for usage examples and the [MySQL](../data-dictionary/dialects/mysql.md) and [MariaDB](../data-dictionary/dialects/mariadb.md) Data Dictionary SQL Catalogs.
## Configuration & Parameters
::: sqlspec.adapters.asyncmy.AsyncmyConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.asyncmy.AsyncmyConnectionParams
options:
@@ -38,7 +37,7 @@ Asynchronous MySQL and MariaDB adapter built on `asyncmy`. Inherited execution m
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.asyncmy.AsyncmyDriver
options:
@@ -50,24 +49,37 @@ Asynchronous MySQL and MariaDB adapter built on `asyncmy`. Inherited execution m
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.asyncmy.data_dictionary.AsyncmyDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.asyncmy.litestar.AsyncmyLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.asyncmy.litestar.AsyncmyStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.asyncmy.adk.AsyncmyADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.asyncmy.adk.AsyncmyADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.asyncmy.adk.AsyncmyADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.asyncmy.events.AsyncmyEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/asyncpg.md b/docs/reference/adapters/asyncpg.md
index e0ccb8660..a6a43bc80 100644
--- a/docs/reference/adapters/asyncpg.md
+++ b/docs/reference/adapters/asyncpg.md
@@ -12,14 +12,13 @@ from sqlspec.adapters.asyncpg import (
)
```
-Asynchronous PostgreSQL adapter built on `asyncpg`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [PostgreSQL (asyncpg) Setup Guide](../../databases/postgres/asyncpg.md) for connection pooling, JSON codecs, and pgvector configuration.
+Asynchronous PostgreSQL adapter built on `asyncpg`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [PostgreSQL (asyncpg) Setup Guide](../../databases/postgres/asyncpg.md) for connection pooling, JSON codecs, and pgvector configuration, and the [PostgreSQL Data Dictionary & SQL Catalog](../data-dictionary/dialects/postgres.md).
## Configuration & Parameters
::: sqlspec.adapters.asyncpg.AsyncpgConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.asyncpg.AsyncpgConnectionConfig
options:
@@ -39,7 +38,7 @@ Asynchronous PostgreSQL adapter built on `asyncpg`. Inherited execution methods
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.asyncpg.AsyncpgDriver
options:
@@ -56,29 +55,47 @@ Asynchronous PostgreSQL adapter built on `asyncpg`. Inherited execution methods
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.asyncpg.data_dictionary.AsyncpgDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.asyncpg.litestar.AsyncpgLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.asyncpg.litestar.AsyncpgStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.asyncpg.adk.AsyncpgADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.asyncpg.adk.AsyncpgADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.asyncpg.adk.AsyncpgADKMemoryStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.asyncpg.events.AsyncpgEventsConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.asyncpg.events.AsyncpgEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.asyncpg.events.AsyncpgEventsBackend
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/bigquery.md b/docs/reference/adapters/bigquery.md
index 7c044df12..28f9fc7fd 100644
--- a/docs/reference/adapters/bigquery.md
+++ b/docs/reference/adapters/bigquery.md
@@ -10,14 +10,13 @@ from sqlspec.adapters.bigquery import (
)
```
-Google Cloud BigQuery analytical adapter built on `google-cloud-bigquery` and `google-cloud-bigquery-storage` for high-throughput Arrow result streaming. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [BigQuery Setup Guide](../../databases/bigquery.md) for authentication and Storage Read API configuration.
+Google Cloud BigQuery analytical adapter built on `google-cloud-bigquery` and `google-cloud-bigquery-storage` for high-throughput Arrow result streaming. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [BigQuery Setup Guide](../../databases/bigquery.md) for authentication and Storage Read API configuration, and the [BigQuery Data Dictionary & SQL Catalog](../data-dictionary/dialects/bigquery.md).
## Configuration & Parameters
::: sqlspec.adapters.bigquery.BigQueryConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.bigquery.BigQueryConnectionParams
options:
@@ -31,31 +30,58 @@ Google Cloud BigQuery analytical adapter built on `google-cloud-bigquery` and `g
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.bigquery.BigQueryDriver
options:
heading_level: 3
- members: false
+ members:
+ - dry_run
::: sqlspec.adapters.bigquery.BigQueryExceptionHandler
options:
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.bigquery.data_dictionary.BigQueryDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.bigquery.litestar.BigQueryLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.bigquery.litestar.BigQueryStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.bigquery.adk.BigQueryADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.bigquery.adk.BigQueryADKRetentionConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.bigquery.adk.BigQueryADKStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.bigquery.events.BigQueryEventsConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.bigquery.events.BigQueryEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/cockroach-asyncpg.md b/docs/reference/adapters/cockroach-asyncpg.md
index 88fabaa69..9f78650e7 100644
--- a/docs/reference/adapters/cockroach-asyncpg.md
+++ b/docs/reference/adapters/cockroach-asyncpg.md
@@ -13,14 +13,13 @@ from sqlspec.adapters.cockroach_asyncpg import (
)
```
-Asynchronous CockroachDB adapter built on `asyncpg` with `SQLSTATE 40001` serialization retry handling and follower-read support. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [CockroachDB Setup Guide](../../databases/cockroachdb.md) for cluster topology and retry configuration.
+Asynchronous CockroachDB adapter built on `asyncpg` with `SQLSTATE 40001` serialization retry handling and follower-read support. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [CockroachDB Setup Guide](../../databases/cockroachdb.md) for cluster topology and retry configuration, and the [CockroachDB Data Dictionary & SQL Catalog](../data-dictionary/dialects/cockroachdb.md).
## Configuration & Parameters
::: sqlspec.adapters.cockroach_asyncpg.CockroachAsyncpgConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.cockroach_asyncpg.CockroachAsyncpgConnectionConfig
options:
@@ -43,14 +42,14 @@ Asynchronous CockroachDB adapter built on `asyncpg` with `SQLSTATE 40001` serial
::: sqlspec.adapters.cockroach_asyncpg.CockroachAsyncpgRetryConfig
options:
heading_level: 3
- members: false
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.cockroach_asyncpg.CockroachAsyncpgDriver
options:
heading_level: 3
- members: false
+ members:
+ - run_transaction_with_retry
::: sqlspec.adapters.cockroach_asyncpg.CockroachAsyncpgExceptionHandler
options:
@@ -62,24 +61,37 @@ Asynchronous CockroachDB adapter built on `asyncpg` with `SQLSTATE 40001` serial
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.cockroach_asyncpg.data_dictionary.CockroachAsyncpgDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.cockroach_asyncpg.litestar.CockroachAsyncpgLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.cockroach_asyncpg.litestar.CockroachAsyncpgStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.cockroach_asyncpg.adk.CockroachAsyncpgADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.cockroach_asyncpg.adk.CockroachAsyncpgADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.cockroach_asyncpg.adk.CockroachAsyncpgADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.cockroach_asyncpg.events.CockroachAsyncpgEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/cockroach-psycopg.md b/docs/reference/adapters/cockroach-psycopg.md
index aae680938..90b5dbb95 100644
--- a/docs/reference/adapters/cockroach-psycopg.md
+++ b/docs/reference/adapters/cockroach-psycopg.md
@@ -15,19 +15,17 @@ from sqlspec.adapters.cockroach_psycopg import (
)
```
-Synchronous and asynchronous CockroachDB adapters built on `psycopg` v3 with `SQLSTATE 40001` retry policies and follower-read support. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [CockroachDB Setup Guide](../../databases/cockroachdb.md) for usage examples.
+Synchronous and asynchronous CockroachDB adapters built on `psycopg` v3 with `SQLSTATE 40001` retry policies and follower-read support. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [CockroachDB Setup Guide](../../databases/cockroachdb.md) for usage examples and the [CockroachDB Data Dictionary & SQL Catalog](../data-dictionary/dialects/cockroachdb.md).
## Configuration & Parameters
::: sqlspec.adapters.cockroach_psycopg.CockroachPsycopgSyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.cockroach_psycopg.CockroachPsycopgAsyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.cockroach_psycopg.CockroachPsycopgConnectionConfig
options:
@@ -50,19 +48,20 @@ Synchronous and asynchronous CockroachDB adapters built on `psycopg` v3 with `SQ
::: sqlspec.adapters.cockroach_psycopg.CockroachPsycopgRetryConfig
options:
heading_level: 3
- members: false
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.cockroach_psycopg.CockroachPsycopgSyncDriver
options:
heading_level: 3
- members: false
+ members:
+ - run_transaction_with_retry
::: sqlspec.adapters.cockroach_psycopg.CockroachPsycopgAsyncDriver
options:
heading_level: 3
- members: false
+ members:
+ - run_transaction_with_retry
::: sqlspec.adapters.cockroach_psycopg.CockroachPsycopgSyncExceptionHandler
options:
@@ -74,44 +73,58 @@ Synchronous and asynchronous CockroachDB adapters built on `psycopg` v3 with `SQ
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.cockroach_psycopg.data_dictionary.CockroachPsycopgSyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.cockroach_psycopg.data_dictionary.CockroachPsycopgAsyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.cockroach_psycopg.litestar.CockroachPsycopgLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.cockroach_psycopg.litestar.CockroachPsycopgSyncStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.cockroach_psycopg.litestar.CockroachPsycopgAsyncStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.cockroach_psycopg.adk.CockroachPsycopgADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.cockroach_psycopg.adk.CockroachPsycopgSyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.cockroach_psycopg.adk.CockroachPsycopgAsyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.cockroach_psycopg.adk.CockroachPsycopgSyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.cockroach_psycopg.adk.CockroachPsycopgAsyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.cockroach_psycopg.events.CockroachPsycopgSyncEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.cockroach_psycopg.events.CockroachPsycopgAsyncEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/db2.md b/docs/reference/adapters/db2.md
index f0a0387aa..b55cead7f 100644
--- a/docs/reference/adapters/db2.md
+++ b/docs/reference/adapters/db2.md
@@ -15,19 +15,17 @@ from sqlspec.adapters.db2 import (
)
```
-Synchronous and asynchronous IBM Db2 LUW adapters built on `ibm_db`. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [IBM Db2 Setup Guide](../../databases/db2.md) for usage examples.
+Synchronous and asynchronous IBM Db2 LUW adapters built on `ibm_db`. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [IBM Db2 Setup Guide](../../databases/db2.md) for usage examples and the [IBM Db2 Data Dictionary & SQL Catalog](../data-dictionary/dialects/db2.md).
## Configuration & Parameters
::: sqlspec.adapters.db2.Db2SyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.db2.Db2AsyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.db2.Db2ConnectionParams
options:
@@ -53,7 +51,7 @@ Synchronous and asynchronous IBM Db2 LUW adapters built on `ibm_db`. Inherited e
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.db2.Db2SyncDriver
options:
@@ -75,44 +73,58 @@ Synchronous and asynchronous IBM Db2 LUW adapters built on `ibm_db`. Inherited e
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.db2.data_dictionary.Db2SyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.db2.data_dictionary.Db2AsyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.db2.litestar.Db2LitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.db2.litestar.Db2SyncStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.db2.litestar.Db2AsyncStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.db2.adk.Db2SyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.db2.adk.Db2AsyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.db2.adk.Db2SyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.db2.adk.Db2AsyncADKMemoryStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.db2.events.Db2EventsConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.db2.events.Db2SyncEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.db2.events.Db2AsyncEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/duckdb.md b/docs/reference/adapters/duckdb.md
index 37915db82..510c4b47d 100644
--- a/docs/reference/adapters/duckdb.md
+++ b/docs/reference/adapters/duckdb.md
@@ -14,14 +14,13 @@ from sqlspec.adapters.duckdb import (
)
```
-Embedded analytical database adapter built on `duckdb` with zero-copy Apache Arrow, Parquet, and Polars integration. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [DuckDB Setup Guide](../../databases/duckdb.md) for extensions, secrets, and Arrow streaming examples.
+Embedded analytical database adapter built on `duckdb` with zero-copy Apache Arrow, Parquet, and Polars integration. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [DuckDB Setup Guide](../../databases/duckdb.md) for extensions, secrets, and Arrow streaming examples, and the [DuckDB Data Dictionary & SQL Catalog](../data-dictionary/dialects/duckdb.md).
## Configuration & Parameters
::: sqlspec.adapters.duckdb.DuckDBConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.duckdb.DuckDBConnectionParams
options:
@@ -53,7 +52,7 @@ Embedded analytical database adapter built on `duckdb` with zero-copy Apache Arr
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.duckdb.DuckDBDriver
options:
@@ -68,30 +67,38 @@ Embedded analytical database adapter built on `duckdb` with zero-copy Apache Arr
::: sqlspec.adapters.duckdb.DuckDBConnectionPool
options:
heading_level: 3
- members:
- - acquire
- - release
- - close_all
- - size
-## Extension Stores
+::: sqlspec.adapters.duckdb.data_dictionary.DuckDBDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
::: sqlspec.adapters.duckdb.litestar.DuckdbStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.duckdb.adk.DuckdbADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.duckdb.adk.DuckdbADKFTSOptions
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.duckdb.adk.DuckdbADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.duckdb.adk.DuckdbADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.duckdb.events.DuckDBEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/index.md b/docs/reference/adapters/index.md
index 831b69ee9..c2719400b 100644
--- a/docs/reference/adapters/index.md
+++ b/docs/reference/adapters/index.md
@@ -23,13 +23,15 @@ Curated API reference for all SQLSpec database adapters (`sqlspec.adapters.*`).
| `sqlspec.adapters.pymysql` | [pymysql](pymysql.md) | `PyMysqlConfig` | `PyMysqlDriver` | [MySQL & MariaDB](../../databases/mysql.md) |
| `sqlspec.adapters.mysqlconnector` | [mysqlconnector](mysqlconnector.md) | `MysqlConnectorSyncConfig`, `MysqlConnectorAsyncConfig` | `MysqlConnectorSyncDriver`, `MysqlConnectorAsyncDriver` | [MySQL & MariaDB](../../databases/mysql.md) |
-## Oracle, SQL Server & IBM Db2
+## Enterprise & Relational Databases
| Adapter Module | Reference Page | Config Classes | Driver Classes | Setup Guide |
| --- | --- | --- | --- | --- |
| `sqlspec.adapters.oracledb` | [oracledb](oracledb.md) | `OracleSyncConfig`, `OracleAsyncConfig` | `OracleSyncDriver`, `OracleAsyncDriver` | [Oracle Database](../../databases/oracledb.md) |
| `sqlspec.adapters.mssql_python` | [mssql-python](mssql-python.md) | `MssqlPythonConfig` | `MssqlPythonDriver` | [SQL Server](../../databases/mssql.md) |
| `sqlspec.adapters.pymssql` | [pymssql](pymssql.md) | `PymssqlConfig` | `PymssqlDriver` | [SQL Server](../../databases/mssql.md) |
+| `sqlspec.adapters.sybase` | [sybase](sybase.md) | `SybaseConfig` | `SybaseDriver` | [Sybase ASE](../../databases/sybase.md) |
+| `sqlspec.adapters.sqlanywhere` | [sqlanywhere](sqlanywhere.md) | `SqlanywhereConfig` | `SqlanywhereDriver` | [SAP SQL Anywhere](../../databases/sqlanywhere.md) |
| `sqlspec.adapters.db2` | [db2](db2.md) | `Db2SyncConfig`, `Db2AsyncConfig` | `Db2SyncDriver`, `Db2AsyncDriver` | [IBM Db2](../../databases/db2.md) |
## Cloud, Analytics & Arrow Connectors
diff --git a/docs/reference/adapters/mssql-python.md b/docs/reference/adapters/mssql-python.md
index f94a1e393..7907a6970 100644
--- a/docs/reference/adapters/mssql-python.md
+++ b/docs/reference/adapters/mssql-python.md
@@ -13,14 +13,13 @@ from sqlspec.adapters.mssql_python import (
)
```
-Microsoft SQL Server and Azure SQL adapter built on Microsoft's official `mssql-python` driver with native Bulk Copy (`BCP`) support. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [SQL Server Setup Guide](../../databases/mssql.md) for setup and bulk copy examples.
+Microsoft SQL Server and Azure SQL adapter built on Microsoft's official `mssql-python` driver with native Bulk Copy (`BCP`) support. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [SQL Server Setup Guide](../../databases/mssql.md) for setup and bulk copy examples, and the [SQL Server Data Dictionary & SQL Catalog](../data-dictionary/dialects/mssql.md).
## Configuration & Parameters
::: sqlspec.adapters.mssql_python.MssqlPythonConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.mssql_python.MssqlPythonConnectionParams
options:
@@ -40,7 +39,7 @@ Microsoft SQL Server and Azure SQL adapter built on Microsoft's official `mssql-
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.mssql_python.MssqlPythonDriver
options:
@@ -51,7 +50,6 @@ Microsoft SQL Server and Azure SQL adapter built on Microsoft's official `mssql-
::: sqlspec.adapters.mssql_python.MssqlPythonBulkCopyResult
options:
heading_level: 3
- members: false
::: sqlspec.adapters.mssql_python.MssqlPythonExceptionHandler
options:
@@ -61,26 +59,32 @@ Microsoft SQL Server and Azure SQL adapter built on Microsoft's official `mssql-
::: sqlspec.adapters.mssql_python.MssqlPythonConnectionPool
options:
heading_level: 3
+
+::: sqlspec.adapters.mssql_python.data_dictionary.MssqlPythonSyncDataDictionary
+ options:
+ heading_level: 3
members: false
-## Extension Stores
+## Extension Stores & Configs
::: sqlspec.adapters.mssql_python.litestar.MssqlPythonStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.mssql_python.adk.MssqlPythonADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.mssql_python.adk.MssqlPythonADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.mssql_python.adk.MssqlPythonADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.mssql_python.events.MssqlPythonEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/mysqlconnector.md b/docs/reference/adapters/mysqlconnector.md
index f62ecca58..c2a59b309 100644
--- a/docs/reference/adapters/mysqlconnector.md
+++ b/docs/reference/adapters/mysqlconnector.md
@@ -15,19 +15,17 @@ from sqlspec.adapters.mysqlconnector import (
)
```
-Synchronous and asynchronous MySQL and MariaDB adapters built on `mysql-connector-python`. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [MySQL & MariaDB Setup Guide](../../databases/mysql.md) for usage examples.
+Synchronous and asynchronous MySQL and MariaDB adapters built on `mysql-connector-python`. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [MySQL & MariaDB Setup Guide](../../databases/mysql.md) for usage examples and the [MySQL](../data-dictionary/dialects/mysql.md) and [MariaDB](../data-dictionary/dialects/mariadb.md) Data Dictionary SQL Catalogs.
## Configuration & Parameters
::: sqlspec.adapters.mysqlconnector.MysqlConnectorSyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.mysqlconnector.MysqlConnectorAsyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.mysqlconnector.MysqlConnectorSyncConnectionParams
options:
@@ -54,7 +52,7 @@ Synchronous and asynchronous MySQL and MariaDB adapters built on `mysql-connecto
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.mysqlconnector.MysqlConnectorSyncDriver
options:
@@ -76,44 +74,58 @@ Synchronous and asynchronous MySQL and MariaDB adapters built on `mysql-connecto
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.mysqlconnector.data_dictionary.MysqlConnectorSyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.mysqlconnector.data_dictionary.MysqlConnectorAsyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.mysqlconnector.litestar.MysqlConnectorLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.mysqlconnector.litestar.MysqlConnectorSyncStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.mysqlconnector.litestar.MysqlConnectorAsyncStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.mysqlconnector.adk.MysqlConnectorADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.mysqlconnector.adk.MysqlConnectorSyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.mysqlconnector.adk.MysqlConnectorAsyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.mysqlconnector.adk.MysqlConnectorSyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.mysqlconnector.adk.MysqlConnectorAsyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.mysqlconnector.events.MysqlConnectorSyncEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.mysqlconnector.events.MysqlConnectorAsyncEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/oracledb.md b/docs/reference/adapters/oracledb.md
index 1ff7c1042..d1a52cd52 100644
--- a/docs/reference/adapters/oracledb.md
+++ b/docs/reference/adapters/oracledb.md
@@ -14,19 +14,17 @@ from sqlspec.adapters.oracledb import (
)
```
-Synchronous and asynchronous Oracle Database adapters built on `python-oracledb` (Thin and Thick modes) with `VECTOR`, `JSON`, `LOB`, and Advanced Queuing support. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [Oracle Database Setup Guide](../../databases/oracledb.md) for wallet, pool, and vector configuration.
+Synchronous and asynchronous Oracle Database adapters built on `python-oracledb` (Thin and Thick modes) with `VECTOR`, `JSON`, `LOB`, and Advanced Queuing support. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [Oracle Database Setup Guide](../../databases/oracledb.md) for wallet, pool, and vector configuration, and the [Oracle Data Dictionary & SQL Catalog](../data-dictionary/dialects/oracle.md).
## Configuration & Parameters
::: sqlspec.adapters.oracledb.OracleSyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.oracledb.OracleAsyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.oracledb.OracleConnectionParams
options:
@@ -46,7 +44,7 @@ Synchronous and asynchronous Oracle Database adapters built on `python-oracledb`
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.oracledb.OracleSyncDriver
options:
@@ -68,54 +66,80 @@ Synchronous and asynchronous Oracle Database adapters built on `python-oracledb`
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.oracledb.data_dictionary.OracledbSyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.oracledb.data_dictionary.OracledbAsyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.oracledb.litestar.OracleLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.oracledb.litestar.OracleSyncStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.oracledb.litestar.OracleAsyncStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.oracledb.adk.OracleADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.oracledb.adk.OracleSyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.oracledb.adk.OracleAsyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.oracledb.adk.OracleSyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.oracledb.adk.OracleAsyncADKMemoryStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.oracledb.events.OracleEventsConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.oracledb.events.OracleSyncEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.oracledb.events.OracleAsyncEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.oracledb.events.OracleSyncAQEventBackend
options:
heading_level: 4
- members: false
::: sqlspec.adapters.oracledb.events.OracleAsyncAQEventBackend
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.oracledb.events.OracleSyncTxEventQEventBackend
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.oracledb.events.OracleAsyncTxEventQEventBackend
+ options:
+ heading_level: 4
diff --git a/docs/reference/adapters/psqlpy.md b/docs/reference/adapters/psqlpy.md
index 63527706d..6117c7f36 100644
--- a/docs/reference/adapters/psqlpy.md
+++ b/docs/reference/adapters/psqlpy.md
@@ -11,14 +11,13 @@ from sqlspec.adapters.psqlpy import (
)
```
-Rust-backed asynchronous PostgreSQL adapter built on `psqlpy`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [PostgreSQL (psqlpy) Setup Guide](../../databases/postgres/psqlpy.md) for usage examples.
+Rust-backed asynchronous PostgreSQL adapter built on `psqlpy`. Inherited execution methods are documented on [`AsyncDriverAdapterBase`](../driver/async.md). See the [PostgreSQL (psqlpy) Setup Guide](../../databases/postgres/psqlpy.md) for usage examples and the [PostgreSQL Data Dictionary & SQL Catalog](../data-dictionary/dialects/postgres.md).
## Configuration & Parameters
::: sqlspec.adapters.psqlpy.PsqlpyConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.psqlpy.PsqlpyConnectionParams
options:
@@ -38,7 +37,7 @@ Rust-backed asynchronous PostgreSQL adapter built on `psqlpy`. Inherited executi
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.psqlpy.PsqlpyDriver
options:
@@ -50,29 +49,51 @@ Rust-backed asynchronous PostgreSQL adapter built on `psqlpy`. Inherited executi
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.psqlpy.data_dictionary.PsqlpyDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.psqlpy.litestar.PsqlpyLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.psqlpy.litestar.PsqlpyStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.psqlpy.adk.PsqlpyADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.psqlpy.adk.PsqlpyADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.psqlpy.adk.PsqlpyADKMemoryStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.psqlpy.events.PsqlpyEventsConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.psqlpy.events.PsqlpyEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.psqlpy.events.PsqlpyEventsBackend
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.psqlpy.events.PsqlpyHybridEventsBackend
+ options:
+ heading_level: 4
diff --git a/docs/reference/adapters/psycopg.md b/docs/reference/adapters/psycopg.md
index a2b95f4ce..6e4c8f2a1 100644
--- a/docs/reference/adapters/psycopg.md
+++ b/docs/reference/adapters/psycopg.md
@@ -14,19 +14,17 @@ from sqlspec.adapters.psycopg import (
)
```
-Synchronous and asynchronous PostgreSQL adapters built on `psycopg` v3 and `psycopg_pool`. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [PostgreSQL (psycopg) Setup Guide](../../databases/postgres/psycopg.md) for setup and `COPY` streaming examples.
+Synchronous and asynchronous PostgreSQL adapters built on `psycopg` v3 and `psycopg_pool`. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [PostgreSQL (psycopg) Setup Guide](../../databases/postgres/psycopg.md) for setup and `COPY` streaming examples, and the [PostgreSQL Data Dictionary & SQL Catalog](../data-dictionary/dialects/postgres.md).
## Configuration & Parameters
::: sqlspec.adapters.psycopg.PsycopgSyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.psycopg.PsycopgAsyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.psycopg.PsycopgConnectionParams
options:
@@ -46,7 +44,7 @@ Synchronous and asynchronous PostgreSQL adapters built on `psycopg` v3 and `psyc
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.psycopg.PsycopgSyncDriver
options:
@@ -68,44 +66,80 @@ Synchronous and asynchronous PostgreSQL adapters built on `psycopg` v3 and `psyc
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.psycopg.data_dictionary.PsycopgSyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.psycopg.data_dictionary.PsycopgAsyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.psycopg.litestar.PsycopgLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.psycopg.litestar.PsycopgSyncStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.psycopg.litestar.PsycopgAsyncStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.psycopg.adk.PsycopgADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.psycopg.adk.PsycopgSyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.psycopg.adk.PsycopgAsyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.psycopg.adk.PsycopgSyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.psycopg.adk.PsycopgAsyncADKMemoryStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.psycopg.events.PsycopgEventsConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.psycopg.events.PsycopgSyncEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.psycopg.events.PsycopgAsyncEventQueueStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.psycopg.events.PsycopgSyncEventsBackend
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.psycopg.events.PsycopgAsyncEventsBackend
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.psycopg.events.PsycopgSyncHybridEventsBackend
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.psycopg.events.PsycopgAsyncHybridEventsBackend
+ options:
+ heading_level: 4
diff --git a/docs/reference/adapters/pymssql.md b/docs/reference/adapters/pymssql.md
index 3d2391d19..b34a68aef 100644
--- a/docs/reference/adapters/pymssql.md
+++ b/docs/reference/adapters/pymssql.md
@@ -12,14 +12,13 @@ from sqlspec.adapters.pymssql import (
)
```
-Synchronous Microsoft SQL Server adapter built on `pymssql` (FreeTDS). Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [SQL Server Setup Guide](../../databases/mssql.md) for usage examples.
+Synchronous Microsoft SQL Server adapter built on `pymssql` (FreeTDS). Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [SQL Server Setup Guide](../../databases/mssql.md) for usage examples and the [SQL Server Data Dictionary & SQL Catalog](../data-dictionary/dialects/mssql.md).
## Configuration & Parameters
::: sqlspec.adapters.pymssql.PymssqlConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.pymssql.PymssqlConnectionParams
options:
@@ -39,7 +38,7 @@ Synchronous Microsoft SQL Server adapter built on `pymssql` (FreeTDS). Inherited
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.pymssql.PymssqlDriver
options:
@@ -54,26 +53,32 @@ Synchronous Microsoft SQL Server adapter built on `pymssql` (FreeTDS). Inherited
::: sqlspec.adapters.pymssql.PymssqlConnectionPool
options:
heading_level: 3
+
+::: sqlspec.adapters.pymssql.data_dictionary.PymssqlSyncDataDictionary
+ options:
+ heading_level: 3
members: false
-## Extension Stores
+## Extension Stores & Configs
::: sqlspec.adapters.pymssql.litestar.PymssqlStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.pymssql.adk.PymssqlADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.pymssql.adk.PymssqlADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.pymssql.adk.PymssqlADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.pymssql.events.PymssqlEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/pymysql.md b/docs/reference/adapters/pymysql.md
index 6fe642567..d6852ccc3 100644
--- a/docs/reference/adapters/pymysql.md
+++ b/docs/reference/adapters/pymysql.md
@@ -11,14 +11,13 @@ from sqlspec.adapters.pymysql import (
)
```
-Synchronous MySQL and MariaDB adapter built on `pymysql`. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [MySQL & MariaDB Setup Guide](../../databases/mysql.md) for usage examples.
+Synchronous MySQL and MariaDB adapter built on `pymysql`. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [MySQL & MariaDB Setup Guide](../../databases/mysql.md) for usage examples and the [MySQL](../data-dictionary/dialects/mysql.md) and [MariaDB](../data-dictionary/dialects/mariadb.md) Data Dictionary SQL Catalogs.
## Configuration & Parameters
::: sqlspec.adapters.pymysql.PyMysqlConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.pymysql.PyMysqlConnectionParams
options:
@@ -38,7 +37,7 @@ Synchronous MySQL and MariaDB adapter built on `pymysql`. Inherited execution me
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.pymysql.PyMysqlDriver
options:
@@ -50,24 +49,37 @@ Synchronous MySQL and MariaDB adapter built on `pymysql`. Inherited execution me
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.pymysql.data_dictionary.PyMysqlDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.pymysql.litestar.PyMysqlLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.pymysql.litestar.PyMysqlStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.pymysql.adk.PyMysqlADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.pymysql.adk.PyMysqlADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.pymysql.adk.PyMysqlADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.pymysql.events.PyMysqlEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/spanner.md b/docs/reference/adapters/spanner.md
index dfbd21023..7851233e3 100644
--- a/docs/reference/adapters/spanner.md
+++ b/docs/reference/adapters/spanner.md
@@ -14,19 +14,17 @@ from sqlspec.adapters.spanner import (
)
```
-Synchronous and asynchronous Google Cloud Spanner adapters supporting both GoogleSQL (`spanner`) and PostgreSQL (`spangres`) dialects, multiplexed sessions, read-only timestamp snapshots, and CommitResponse mutation tracking. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [Cloud Spanner Setup Guide](../../databases/spanner.md) for usage examples.
+Synchronous and asynchronous Google Cloud Spanner adapters supporting both GoogleSQL (`spanner`) and PostgreSQL (`spangres`) dialects, multiplexed sessions, read-only timestamp snapshots, and CommitResponse mutation tracking. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). See the [Cloud Spanner Setup Guide](../../databases/spanner.md) for usage examples and the [Spanner (GoogleSQL)](../data-dictionary/dialects/spanner.md) and [Spangres (PostgreSQL)](../data-dictionary/dialects/index.md) Data Dictionary SQL Catalogs.
## Configuration & Parameters
::: sqlspec.adapters.spanner.SpannerSyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.spanner.SpannerAsyncConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.spanner.SpannerConnectionParams
options:
@@ -46,17 +44,31 @@ Synchronous and asynchronous Google Cloud Spanner adapters supporting both Googl
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.spanner.SpannerSyncDriver
options:
heading_level: 3
- members: false
+ members:
+ - run_in_transaction
+ - execute_partitioned_dml
+ - write_insert
+ - write_upsert
+ - write_update
+ - write_delete
+ - bind_external_transaction
::: sqlspec.adapters.spanner.SpannerAsyncDriver
options:
heading_level: 3
- members: false
+ members:
+ - run_in_transaction
+ - execute_partitioned_dml
+ - write_insert
+ - write_upsert
+ - write_update
+ - write_delete
+ - bind_external_transaction
::: sqlspec.adapters.spanner.SpannerSyncExceptionHandler
options:
@@ -68,54 +80,72 @@ Synchronous and asynchronous Google Cloud Spanner adapters supporting both Googl
heading_level: 3
members: false
-## Extension Stores
+::: sqlspec.adapters.spanner.data_dictionary.SpannerSyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.spanner.data_dictionary.SpannerAsyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.spanner.litestar.SpannerLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.spanner.litestar.SpannerSyncStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.spanner.litestar.SpannerAsyncStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.spanner.adk.SpannerADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.spanner.adk.SpannerADKRetentionConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.spanner.adk.SpannerSyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.spanner.adk.SpannerAsyncADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.spanner.adk.SpannerSyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.spanner.adk.SpannerAsyncADKMemoryStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.spanner.adk.SpannerSyncADKArtifactStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.spanner.adk.SpannerAsyncADKArtifactStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.spanner.events.SpannerSyncEventQueueStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.spanner.events.SpannerAsyncEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/sqlanywhere.md b/docs/reference/adapters/sqlanywhere.md
new file mode 100644
index 000000000..e0d6c131b
--- /dev/null
+++ b/docs/reference/adapters/sqlanywhere.md
@@ -0,0 +1,91 @@
+# sqlanywhere Adapter
+
+```python
+from sqlspec.adapters.sqlanywhere import (
+ SqlanywhereConfig,
+ SqlanywhereConnectionParams,
+ SqlanywhereConnectionPool,
+ SqlanywhereDriver,
+ SqlanywhereDriverFeatures,
+ SqlanywhereExceptionHandler,
+ SqlanywherePoolParams,
+ SqlanywhereSyncDataDictionary,
+ SqlanywhereSyncMigrationTracker,
+)
+```
+
+Synchronous SAP SQL Anywhere 12+ adapter built on `sqlanydb` (`dbcapi` C library). Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [SAP SQL Anywhere Setup Guide](../../databases/sqlanywhere.md) for engine connection parameters and pooling, and the [SQL Anywhere Data Dictionary & SQL Catalog](../data-dictionary/dialects/sqlanywhere.md) for system catalog introspection queries.
+
+## Configuration & Parameters
+
+::: sqlspec.adapters.sqlanywhere.SqlanywhereConfig
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.sqlanywhere.SqlanywhereConnectionParams
+ options:
+ heading_level: 3
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.sqlanywhere.SqlanywherePoolParams
+ options:
+ heading_level: 3
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.sqlanywhere.SqlanywhereDriverFeatures
+ options:
+ heading_level: 3
+ separate_signature: false
+ show_signature_annotations: true
+
+## Driver, Pool & Data Dictionary
+
+::: sqlspec.adapters.sqlanywhere.SqlanywhereDriver
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.sqlanywhere.SqlanywhereExceptionHandler
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.sqlanywhere.SqlanywhereConnectionPool
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.sqlanywhere.data_dictionary.SqlanywhereSyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.sqlanywhere.migrations.SqlanywhereSyncMigrationTracker
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.sqlanywhere.litestar.SqlanywhereStore
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.sqlanywhere.adk.SqlanywhereADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.sqlanywhere.adk.SqlanywhereADKStore
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.sqlanywhere.adk.SqlanywhereADKMemoryStore
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.sqlanywhere.events.SqlanywhereSyncEventQueueStore
+ options:
+ heading_level: 4
diff --git a/docs/reference/adapters/sqlite.md b/docs/reference/adapters/sqlite.md
index 84a4d2ce8..6800ca6df 100644
--- a/docs/reference/adapters/sqlite.md
+++ b/docs/reference/adapters/sqlite.md
@@ -11,14 +11,13 @@ from sqlspec.adapters.sqlite import (
)
```
-Synchronous SQLite adapter built on Python's standard library `sqlite3` with thread-local connection pooling and WAL pragma helpers. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [SQLite Setup Guide](../../databases/sqlite.md) for in-memory URI and WAL configuration.
+Synchronous SQLite adapter built on Python's standard library `sqlite3` with thread-local connection pooling and WAL pragma helpers. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [SQLite Setup Guide](../../databases/sqlite.md) for in-memory URI and WAL configuration, and the [SQLite Data Dictionary & SQL Catalog](../data-dictionary/dialects/sqlite.md).
## Configuration & Parameters
::: sqlspec.adapters.sqlite.SqliteConfig
options:
heading_level: 3
- members: false
::: sqlspec.adapters.sqlite.SqliteConnectionParams
options:
@@ -32,7 +31,7 @@ Synchronous SQLite adapter built on Python's standard library `sqlite3` with thr
separate_signature: false
show_signature_annotations: true
-## Driver & Pool
+## Driver, Pool & Data Dictionary
::: sqlspec.adapters.sqlite.SqliteDriver
options:
@@ -47,30 +46,44 @@ Synchronous SQLite adapter built on Python's standard library `sqlite3` with thr
::: sqlspec.adapters.sqlite.SqliteConnectionPool
options:
heading_level: 3
- members:
- - acquire
- - release
- - close_all
- - size
-## Extension Stores
+::: sqlspec.adapters.sqlite.data_dictionary.SqliteDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.sqlite.litestar.SqliteLitestarConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.sqlite.litestar.SQLiteStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.sqlite.adk.SqliteADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.sqlite.adk.SqliteADKStore
options:
heading_level: 4
- members: false
::: sqlspec.adapters.sqlite.adk.SqliteADKMemoryStore
options:
heading_level: 4
- members: false
+
+::: sqlspec.adapters.sqlite.events.SqliteEventsConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
::: sqlspec.adapters.sqlite.events.SqliteEventQueueStore
options:
heading_level: 4
- members: false
diff --git a/docs/reference/adapters/sybase.md b/docs/reference/adapters/sybase.md
new file mode 100644
index 000000000..4c2089776
--- /dev/null
+++ b/docs/reference/adapters/sybase.md
@@ -0,0 +1,91 @@
+# sybase Adapter
+
+```python
+from sqlspec.adapters.sybase import (
+ SybaseConfig,
+ SybaseConnectionParams,
+ SybaseConnectionPool,
+ SybaseDriver,
+ SybaseDriverFeatures,
+ SybaseExceptionHandler,
+ SybasePoolParams,
+ SybaseSyncDataDictionary,
+ SybaseSyncMigrationTracker,
+)
+```
+
+Synchronous SAP Adaptive Server Enterprise (Sybase ASE) 16.0+ adapter built on `pymssql` over FreeTDS TDS 5.0 wire protocol. Inherited execution methods are documented on [`SyncDriverAdapterBase`](../driver/sync.md). See the [Sybase ASE Setup Guide](../../databases/sybase.md) for TDS 5.0 protocol negotiation and connection pooling, and the [Sybase Data Dictionary & SQL Catalog](../data-dictionary/dialects/sybase.md) for system catalog introspection queries.
+
+## Configuration & Parameters
+
+::: sqlspec.adapters.sybase.SybaseConfig
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.sybase.SybaseConnectionParams
+ options:
+ heading_level: 3
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.sybase.SybasePoolParams
+ options:
+ heading_level: 3
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.sybase.SybaseDriverFeatures
+ options:
+ heading_level: 3
+ separate_signature: false
+ show_signature_annotations: true
+
+## Driver, Pool & Data Dictionary
+
+::: sqlspec.adapters.sybase.SybaseDriver
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.sybase.SybaseExceptionHandler
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.sybase.SybaseConnectionPool
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.sybase.data_dictionary.SybaseSyncDataDictionary
+ options:
+ heading_level: 3
+ members: false
+
+::: sqlspec.adapters.sybase.migrations.SybaseSyncMigrationTracker
+ options:
+ heading_level: 3
+ members: false
+
+## Extension Stores & Configs
+
+::: sqlspec.adapters.sybase.litestar.SybaseStore
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.sybase.adk.SybaseADKConfig
+ options:
+ heading_level: 4
+ separate_signature: false
+ show_signature_annotations: true
+
+::: sqlspec.adapters.sybase.adk.SybaseADKStore
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.sybase.adk.SybaseADKMemoryStore
+ options:
+ heading_level: 4
+
+::: sqlspec.adapters.sybase.events.SybaseSyncEventQueueStore
+ options:
+ heading_level: 4
diff --git a/docs/reference/cli.md b/docs/reference/cli.md
index b77443468..025eac497 100644
--- a/docs/reference/cli.md
+++ b/docs/reference/cli.md
@@ -200,6 +200,7 @@ sqlspec squash [OPTIONS] [VERSION_RANGE]
| `--allow-gaps` | `flag` | `False` | Allow gaps in version sequence |
| `--output-format` | `choice(sql | py)` | `"sql"` | Output format for squashed migration (default: sql) |
| `--strategy` | `choice(ddl | schema | concat)` | `"concat"` | Squash strategy: keep every statement in order (concat), fold DDL into its final shape and refuse ranges that cannot be folded faithfully (ddl), or reflect the live database schema (schema). |
+| `--single-file`, `--split` | `flag` | `False` | Force a single squashed file (--single-file) or split when file types or execution directives change (--split). |
#### `sqlspec fix`
diff --git a/docs/reference/core/cache.md b/docs/reference/core/cache.md
index c4092e48e..82bbad898 100644
--- a/docs/reference/core/cache.md
+++ b/docs/reference/core/cache.md
@@ -3,11 +3,14 @@
```python
from sqlspec.core import (
CacheConfig,
+ CachedQuery,
CachedStatement,
CacheKey,
CacheStats,
LRUCache,
NamespacedCache,
+ QueryCache,
+ StatementCacheHolder,
clear_all_caches,
create_cache_key,
get_cache,
@@ -21,29 +24,44 @@ from sqlspec.core import (
)
```
-Multi-tier LRU caching and deterministic structural hashing for SQL statements, SQLGlot AST expressions, and parameter bindings. See the [Performance Guide](../../learn/performance.md) for tuning guidance.
+Coordinated L1/L2 LRU caching (`StatementCacheHolder`, `QueryCache`, `NamespacedCache`) and deterministic structural hashing for SQL statements, SQLGlot AST expressions, and parameter bindings. See the [Performance Guide](../../learn/performance.md) for tuning guidance.
-## Cache Configuration & Statistics
+## Per-Config Fast-Path Cache (`L1`)
+
+::: sqlspec.core.cache.StatementCacheHolder
+ options:
+ heading_level: 3
+
+::: sqlspec.core.cache.QueryCache
+ options:
+ heading_level: 3
+
+::: sqlspec.core.cache.CachedQuery
+ options:
+ heading_level: 3
+
+## Cache Configuration & Statistics (`L2`)
::: sqlspec.core.cache.CacheConfig
options:
heading_level: 3
- members: false
::: sqlspec.core.cache.CacheStats
options:
heading_level: 3
- members: false
+ members:
+ - record_hit
+ - record_miss
+ - record_eviction
+ - reset
::: sqlspec.core.cache.CachedStatement
options:
heading_level: 3
- members: false
::: sqlspec.core.cache.CacheKey
options:
heading_level: 3
- members: false
## Cache Implementations
@@ -61,7 +79,12 @@ Multi-tier LRU caching and deterministic structural hashing for SQL statements,
::: sqlspec.core.cache.NamespacedCache
options:
heading_level: 3
- members: false
+ members:
+ - get_statement
+ - put_statement
+ - get_expression
+ - put_expression
+ - clear_namespace
## Cache Management & Hashing Functions
diff --git a/docs/reference/core/results.md b/docs/reference/core/results.md
index c0ec22746..4a807ddc9 100644
--- a/docs/reference/core/results.md
+++ b/docs/reference/core/results.md
@@ -22,7 +22,11 @@ Result containers for row dictionaries, hydrated schema models, scalar values, A
::: sqlspec.core.result.StatementResult
options:
heading_level: 3
- members: false
+ members:
+ - is_success
+ - get_data
+ - get_metadata
+ - set_metadata
::: sqlspec.core.result.SQLResult
options:
@@ -36,11 +40,18 @@ Result containers for row dictionaries, hydrated schema models, scalar values, A
- to_arrow
- to_pandas
- to_polars
+ - write_to_storage_sync
+ - write_to_storage_async
::: sqlspec.core.result.ArrowResult
options:
heading_level: 3
- members: false
+ members:
+ - to_pandas
+ - to_polars
+ - to_dict
+ - write_to_storage_sync
+ - write_to_storage_async
::: sqlspec.core.result.DMLResult
options:
@@ -50,21 +61,22 @@ Result containers for row dictionaries, hydrated schema models, scalar values, A
::: sqlspec.core.result.StackResult
options:
heading_level: 3
- members: false
+ members:
+ - get_result
+ - is_sql_result
+ - is_arrow_result
+ - is_error
## Pagination Envelopes
::: sqlspec.core._pagination.Pagination
options:
heading_level: 3
- members: false
::: sqlspec.core._pagination.OffsetPagination
options:
heading_level: 3
- members: false
::: sqlspec.core._pagination.CursorPagination
options:
heading_level: 3
- members: false
diff --git a/docs/reference/core/statement.md b/docs/reference/core/statement.md
index efa65c589..0c8ede85b 100644
--- a/docs/reference/core/statement.md
+++ b/docs/reference/core/statement.md
@@ -39,12 +39,12 @@ SQL statement encapsulation, dialect configuration, batched `StatementStack` pip
::: sqlspec.core.statement.StatementConfig
options:
heading_level: 3
- members: false
::: sqlspec.core.statement.ProcessedState
options:
heading_level: 3
- members: false
+ members:
+ - reset
::: sqlspec.core.statement.get_default_config
options:
@@ -68,21 +68,17 @@ SQL statement encapsulation, dialect configuration, batched `StatementStack` pip
::: sqlspec.core.stack.StackOperation
options:
heading_level: 3
- members: false
::: sqlspec.core.metrics.StackExecutionMetrics
options:
heading_level: 3
- members: false
## Explain Options
::: sqlspec.core.explain.ExplainOptions
options:
heading_level: 3
- members: false
::: sqlspec.core.explain.ExplainFormat
options:
heading_level: 3
- members: false
diff --git a/docs/reference/data-dictionary.md b/docs/reference/data-dictionary.md
index 8bff9ea51..de2c3681a 100644
--- a/docs/reference/data-dictionary.md
+++ b/docs/reference/data-dictionary.md
@@ -24,21 +24,13 @@ from sqlspec.data_dictionary import (
)
```
-Cross-database catalog introspection metadata, DDL extraction, column profiling, capability profiles, dialect registry, and foreign-key topological sorting. Access runtime introspection through `driver.data_dictionary` on any [`SyncDriverAdapterBase`](driver/sync.md) or [`AsyncDriverAdapterBase`](driver/async.md) session (see [Runtime Introspection & DDL Synthesis](data-dictionary/runtime.md)).
+Cross-database catalog introspection metadata, DDL extraction, column profiling, capability profiles, dialect registry, and foreign-key topological sorting. Access runtime introspection through `driver.data_dictionary` on any [`SyncDriverAdapterBase`](driver/sync.md) or [`AsyncDriverAdapterBase`](driver/async.md) session (see [Runtime Introspection & DDL Synthesis](data-dictionary/runtime.md) and the [Dialect SQL Catalogs](data-dictionary/dialects/index.md)).
## Loader & Dialect Registry
::: sqlspec.data_dictionary._loader.DataDictionaryLoader
options:
heading_level: 3
- members:
- - get_query
- - get_query_text
- - list_queries
- - get_domain_query
- - get_domain_query_text
- - get_system_query
- - list_domains
::: sqlspec.data_dictionary._loader.get_data_dictionary_loader
options:
@@ -65,52 +57,42 @@ Cross-database catalog introspection metadata, DDL extraction, column profiling,
::: sqlspec.data_dictionary._types.VersionInfo
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.TableMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.ColumnMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.ColumnProfileMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.IndexMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.ForeignKeyMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.ConstraintMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.ViewMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.RoutineMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.TriggerMetadata
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.DDLResult
options:
@@ -122,12 +104,10 @@ Cross-database catalog introspection metadata, DDL extraction, column profiling,
::: sqlspec.data_dictionary._types.DialectConfig
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.MetadataCapabilityProfile
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.MetadataResult
options:
@@ -137,24 +117,20 @@ Cross-database catalog introspection metadata, DDL extraction, column profiling,
::: sqlspec.data_dictionary._types.SystemMetadataRequest
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._types.SystemMetadataResult
options:
heading_level: 4
- members: false
## Dependency Graph & Topological Sorting
::: sqlspec.data_dictionary._dependencies.DependencyEdge
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._dependencies.DependencySortResult
options:
heading_level: 4
- members: false
::: sqlspec.data_dictionary._dependencies.dependency_edges_from_foreign_keys
options:
diff --git a/docs/reference/data-dictionary/dialects/bigquery.md b/docs/reference/data-dictionary/dialects/bigquery.md
new file mode 100644
index 000000000..e034e2b6c
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/bigquery.md
@@ -0,0 +1,699 @@
+# Google BigQuery Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("bigquery")
+```
+
+Introspects dataset-scoped `INFORMATION_SCHEMA` views (`TABLES`, `COLUMNS`, `COLUMN_FIELD_PATHS`, `TABLE_OPTIONS`, `TABLE_CONSTRAINTS`, `KEY_COLUMN_USAGE`, `CONSTRAINT_COLUMN_USAGE`, `VIEWS`, `ROUTINES`). See the [Google BigQuery Guide](../../../databases/bigquery.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `bigquery` |
+| **Default Schema** | `None` |
+| **Version Pattern** | `.*` |
+| **DDL Strategy** | Native `INFORMATION_SCHEMA.TABLES.ddl` + AST fallback |
+| **Catalog `.sql` Files** | `14` files (`30` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `True` | `array` | `ARRAY` |
+| `supports_clustering` | `True` | `bigint` | `INT64` |
+| `supports_cte` | `True` | `bignumeric` | `BIGNUMERIC` |
+| `supports_for_update` | `False` | `blob` | `BYTES` |
+| `supports_geography` | `True` | `boolean` | `BOOL` |
+| `supports_json` | `True` | `float` | `FLOAT64` |
+| `supports_on_conflict` | `False` | `geography` | `GEOGRAPHY` |
+| `supports_partitioning` | `True` | `integer` | `INT64` |
+| `supports_prepared_statements` | `True` | `json` | `JSON` |
+| `supports_returning` | `False` | `numeric` | `NUMERIC` |
+| `supports_schemas` | `True` | `struct` | `STRUCT` |
+| `supports_skip_locked` | `False` | `text` | `STRING` |
+| `supports_structs` | `True` | `timestamp` | `TIMESTAMP` |
+| `supports_transactions` | `True` | `uuid` | `STRING` |
+| `supports_update_from` | `True` | `varchar` | `STRING` |
+| `supports_upsert` | `True` | - | - |
+| `supports_uuid` | `False` | - | - |
+| `supports_window_functions` | `True` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.bigquery.data_dictionary.BigQueryDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `bigquery` dialect pack defines `30` named queries across `14` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/bigquery/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_dataset`, `field_paths_by_dataset`, `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `constraints.sql` | `by_dataset` | `:schema_name`, `:table_name` |
+| `datasets.sql` | `list` | `:dataset_name` |
+| `ddl.sql` | `by_object`, `by_schema` | `:schema_name`, `:table_name` |
+| `dependencies.sql` | `hints_by_dataset` | `:schema_name` |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `search_by_dataset`, `vector_by_dataset`, `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `objects.sql` | `by_dataset` | `:schema_name`, `:table_name` |
+| `privileges.sql` | `by_dataset` | `:object_name`, `:schema_name` |
+| `routines.sql` | `by_dataset`, `options_by_dataset`, `parameters_by_dataset` | `:routine_name`, `:schema_name` |
+| `system.sql` | `jobs_by_region`, `reservations_by_region`, `sessions_by_region` | `:created_after`, `:is_active`, `:limit`, `:reservation_name`, `:state`, `:user_email` |
+| `tables.sql` | `options_by_dataset`, `partitions_by_dataset`, `storage_by_dataset`, `by_schema` | `:schema_name`, `:table_name` |
+| `version.sql` | `current` | *none* |
+| `views.sql` | `by_dataset`, `materialized_by_dataset` | `:schema_name`, `:view_name` |
+
+??? code "`columns.sql` (4 named queries)"
+
+ ```sql
+ -- name: by_dataset
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name,
+ column_name,
+ ordinal_position,
+ is_nullable,
+ data_type,
+ is_generated,
+ generation_expression,
+ is_stored,
+ is_hidden,
+ is_partitioning_column,
+ clustering_ordinal_position,
+ column_default,
+ rounding_mode
+ FROM {columns_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY table_schema, table_name, ordinal_position;
+
+ -- name: field_paths_by_dataset
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name,
+ column_name,
+ field_path,
+ data_type,
+ description,
+ collation_name,
+ rounding_mode
+ FROM {field_paths_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY table_schema, table_name, field_path;
+
+ -- name: by_table
+ -- dialect: bigquery
+ SELECT
+ column_name,
+ data_type,
+ is_nullable,
+ column_default
+ FROM {schema_prefix}INFORMATION_SCHEMA.COLUMNS
+ WHERE table_name = :table_name
+ AND (:schema_name IS NULL OR table_schema = :schema_name)
+ ORDER BY ordinal_position;
+
+ -- name: by_schema
+ -- dialect: bigquery
+ SELECT
+ table_name,
+ column_name,
+ data_type,
+ is_nullable,
+ column_default
+ FROM {schema_prefix}INFORMATION_SCHEMA.COLUMNS
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ ORDER BY table_name, ordinal_position;
+ ```
+
+??? code "`constraints.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_dataset
+ -- dialect: bigquery
+ SELECT
+ tc.constraint_catalog,
+ tc.constraint_schema,
+ tc.constraint_name,
+ tc.table_name,
+ tc.constraint_type,
+ kcu.column_name,
+ kcu.ordinal_position,
+ COALESCE(pk_kcu.table_name, ccu.table_name) AS referenced_table_name,
+ COALESCE(pk_kcu.column_name, ccu.column_name) AS referenced_column_name,
+ rc.match_option,
+ rc.update_rule,
+ rc.delete_rule
+ FROM {table_constraints_table} AS tc
+ LEFT JOIN {key_column_usage_table} AS kcu
+ ON tc.constraint_catalog = kcu.constraint_catalog
+ AND tc.constraint_schema = kcu.constraint_schema
+ AND tc.constraint_name = kcu.constraint_name
+ LEFT JOIN {referential_constraints_table} AS rc
+ ON tc.constraint_catalog = rc.constraint_catalog
+ AND tc.constraint_schema = rc.constraint_schema
+ AND tc.constraint_name = rc.constraint_name
+ LEFT JOIN {key_column_usage_table} AS pk_kcu
+ ON rc.unique_constraint_catalog = pk_kcu.constraint_catalog
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ AND rc.unique_constraint_name = pk_kcu.constraint_name
+ AND COALESCE(kcu.position_in_unique_constraint, kcu.ordinal_position) = pk_kcu.ordinal_position
+ LEFT JOIN {constraint_column_usage_table} AS ccu
+ ON tc.constraint_catalog = ccu.constraint_catalog
+ AND tc.constraint_schema = ccu.constraint_schema
+ AND tc.constraint_name = ccu.constraint_name
+ AND ccu.column_name = kcu.column_name
+ WHERE (:schema_name IS NULL OR tc.constraint_schema = :schema_name)
+ AND (:table_name IS NULL OR tc.table_name = :table_name)
+ ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position;
+ ```
+
+??? code "`datasets.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: bigquery
+ SELECT
+ s.catalog_name AS project_id,
+ s.schema_name AS dataset_name,
+ s.location,
+ ARRAY_AGG(STRUCT(so.option_name, so.option_type, so.option_value) ORDER BY so.option_name) AS options
+ FROM {schemata_table} AS s
+ LEFT JOIN {schemata_options_table} AS so
+ ON s.catalog_name = so.catalog_name
+ AND s.schema_name = so.schema_name
+ WHERE (:dataset_name IS NULL OR s.schema_name = :dataset_name)
+ GROUP BY s.catalog_name, s.schema_name, s.location
+ ORDER BY s.catalog_name, s.schema_name;
+ ```
+
+??? code "`ddl.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_object
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name,
+ table_type,
+ table_name AS object_name,
+ CASE table_type
+ WHEN 'VIEW' THEN 'view'
+ WHEN 'MATERIALIZED VIEW' THEN 'materialized_view'
+ ELSE 'table'
+ END AS object_type,
+ ddl,
+ 'native' AS fidelity
+ FROM {tables_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY table_schema, table_name;
+
+ -- name: by_schema
+ -- dialect: bigquery
+ SELECT
+ table_catalog AS catalog_name,
+ table_schema AS schema_name,
+ table_name AS object_name,
+ CASE table_type
+ WHEN 'VIEW' THEN 'view'
+ WHEN 'MATERIALIZED VIEW' THEN 'materialized_view'
+ ELSE 'table'
+ END AS object_type,
+ ddl,
+ 'native' AS fidelity
+ FROM {schema_prefix}INFORMATION_SCHEMA.TABLES
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND ddl IS NOT NULL
+ UNION ALL
+ SELECT
+ routine_catalog AS catalog_name,
+ routine_schema AS schema_name,
+ routine_name AS object_name,
+ CASE UPPER(COALESCE(routine_type, 'FUNCTION'))
+ WHEN 'PROCEDURE' THEN 'procedure'
+ ELSE 'function'
+ END AS object_type,
+ ddl,
+ 'native' AS fidelity
+ FROM {schema_prefix}INFORMATION_SCHEMA.ROUTINES
+ WHERE (:schema_name IS NULL OR routine_schema = :schema_name)
+ AND ddl IS NOT NULL
+ ORDER BY schema_name, object_type, object_name;
+ ```
+
+??? code "`dependencies.sql` (1 named queries)"
+
+ ```sql
+ -- name: hints_by_dataset
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name AS object_name,
+ table_type AS object_type,
+ ddl AS definition_text
+ FROM {tables_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND ddl IS NOT NULL
+ UNION ALL
+ SELECT
+ routine_catalog AS table_catalog,
+ routine_schema AS table_schema,
+ routine_name AS object_name,
+ routine_type AS object_type,
+ routine_definition AS definition_text
+ FROM {routines_table}
+ WHERE (:schema_name IS NULL OR routine_schema = :schema_name)
+ AND routine_definition IS NOT NULL
+ ORDER BY table_schema, object_name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: bigquery
+ SELECT
+ kcu.table_name,
+ kcu.column_name,
+ pk_kcu.table_name AS referenced_table_name,
+ pk_kcu.column_name AS referenced_column_name,
+ kcu.constraint_name,
+ kcu.table_schema,
+ pk_kcu.table_schema AS referenced_table_schema
+ FROM {kcu_table} kcu
+ JOIN {rc_table} rc ON kcu.constraint_name = rc.constraint_name
+ JOIN {kcu_table} pk_kcu
+ ON rc.unique_constraint_name = pk_kcu.constraint_name
+ AND kcu.ordinal_position = pk_kcu.ordinal_position
+ WHERE kcu.table_name = :table_name
+ AND (:schema_name IS NULL OR kcu.table_schema = :schema_name);
+
+ -- name: by_schema
+ -- dialect: bigquery
+ SELECT
+ kcu.table_name,
+ kcu.column_name,
+ pk_kcu.table_name AS referenced_table_name,
+ pk_kcu.column_name AS referenced_column_name,
+ kcu.constraint_name,
+ kcu.table_schema,
+ pk_kcu.table_schema AS referenced_table_schema
+ FROM {kcu_table} kcu
+ JOIN {rc_table} rc ON kcu.constraint_name = rc.constraint_name
+ JOIN {kcu_table} pk_kcu
+ ON rc.unique_constraint_name = pk_kcu.constraint_name
+ AND kcu.ordinal_position = pk_kcu.ordinal_position
+ WHERE (:schema_name IS NULL OR kcu.table_schema = :schema_name);
+ ```
+
+??? code "`indexes.sql` (4 named queries)"
+
+ ```sql
+ -- name: search_by_dataset
+ -- dialect: bigquery
+ SELECT
+ si.index_catalog AS schema_name,
+ si.table_name,
+ si.index_name,
+ ARRAY_AGG(sic.index_field_path ORDER BY sic.index_field_path) AS columns,
+ FALSE AS is_unique,
+ FALSE AS is_primary
+ FROM {search_indexes_table} AS si
+ LEFT JOIN {search_index_columns_table} AS sic
+ ON si.index_catalog = sic.index_catalog
+ AND si.index_schema = sic.index_schema
+ AND si.table_name = sic.table_name
+ AND si.index_name = sic.index_name
+ WHERE (:schema_name IS NULL OR si.index_schema = :schema_name)
+ AND (:table_name IS NULL OR si.table_name = :table_name)
+ GROUP BY si.index_catalog, si.table_name, si.index_name
+ ORDER BY si.table_name, si.index_name;
+
+ -- name: vector_by_dataset
+ -- dialect: bigquery
+ SELECT
+ vi.index_catalog AS schema_name,
+ vi.table_name,
+ vi.index_name,
+ ARRAY_AGG(vic.index_field_path ORDER BY vic.index_field_path) AS columns,
+ FALSE AS is_unique,
+ FALSE AS is_primary
+ FROM {vector_indexes_table} AS vi
+ LEFT JOIN {vector_index_columns_table} AS vic
+ ON vi.index_catalog = vic.index_catalog
+ AND vi.index_schema = vic.index_schema
+ AND vi.table_name = vic.table_name
+ AND vi.index_name = vic.index_name
+ WHERE (:schema_name IS NULL OR vi.index_schema = :schema_name)
+ AND (:table_name IS NULL OR vi.table_name = :table_name)
+ GROUP BY vi.index_catalog, vi.table_name, vi.index_name
+ ORDER BY vi.table_name, vi.index_name;
+
+ -- name: by_table
+ -- dialect: bigquery
+ SELECT
+ NULL AS index_name,
+ NULL AS table_name,
+ NULL AS is_unique,
+ NULL AS is_primary,
+ NULL AS columns
+ WHERE FALSE;
+
+ -- name: by_schema
+ -- dialect: bigquery
+ SELECT
+ NULL AS index_name,
+ NULL AS table_name,
+ NULL AS is_unique,
+ NULL AS is_primary,
+ NULL AS columns
+ WHERE FALSE;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_dataset
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name,
+ table_type,
+ creation_time,
+ ddl
+ FROM {tables_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY table_schema, table_name;
+ ```
+
+??? code "`privileges.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_dataset
+ -- dialect: bigquery
+ SELECT
+ object_catalog,
+ object_schema,
+ object_name,
+ object_type,
+ privilege_type,
+ grantee
+ FROM {object_privileges_table}
+ WHERE (:schema_name IS NULL OR object_schema = :schema_name)
+ AND (:object_name IS NULL OR object_name = :object_name)
+ ORDER BY object_schema, object_name, privilege_type, grantee;
+ ```
+
+??? code "`routines.sql` (3 named queries)"
+
+ ```sql
+ -- name: by_dataset
+ -- dialect: bigquery
+ SELECT
+ routine_catalog,
+ routine_schema,
+ routine_name,
+ routine_type,
+ data_type,
+ routine_body,
+ routine_definition,
+ external_language,
+ is_deterministic,
+ security_type,
+ created,
+ last_altered
+ FROM {routines_table}
+ WHERE (:schema_name IS NULL OR routine_schema = :schema_name)
+ AND (:routine_name IS NULL OR routine_name = :routine_name)
+ ORDER BY routine_schema, routine_name;
+
+ -- name: options_by_dataset
+ -- dialect: bigquery
+ SELECT
+ specific_catalog,
+ specific_schema,
+ specific_name,
+ option_name,
+ option_type,
+ option_value
+ FROM {routine_options_table}
+ WHERE (:schema_name IS NULL OR specific_schema = :schema_name)
+ AND (:routine_name IS NULL OR specific_name = :routine_name)
+ ORDER BY specific_schema, specific_name, option_name;
+
+ -- name: parameters_by_dataset
+ -- dialect: bigquery
+ SELECT
+ specific_catalog,
+ specific_schema,
+ specific_name,
+ ordinal_position,
+ parameter_mode,
+ is_result,
+ parameter_name,
+ data_type,
+ parameter_default
+ FROM {parameters_table}
+ WHERE (:schema_name IS NULL OR specific_schema = :schema_name)
+ AND (:routine_name IS NULL OR specific_name = :routine_name)
+ ORDER BY specific_schema, specific_name, ordinal_position;
+ ```
+
+??? code "`system.sql` (3 named queries)"
+
+ ```sql
+ -- name: jobs_by_region
+ -- dialect: bigquery
+ SELECT
+ project_id,
+ project_number,
+ user_email,
+ job_id,
+ job_type,
+ statement_type,
+ priority,
+ creation_time,
+ start_time,
+ end_time,
+ state,
+ reservation_id,
+ total_bytes_processed,
+ total_slot_ms,
+ cache_hit,
+ error_result,
+ query
+ FROM {jobs_table}
+ WHERE (:user_email IS NULL OR user_email = :user_email)
+ AND (:state IS NULL OR state = :state)
+ AND (:created_after IS NULL OR creation_time >= :created_after)
+ ORDER BY creation_time DESC
+ LIMIT :limit;
+
+ -- name: reservations_by_region
+ -- dialect: bigquery
+ SELECT
+ project_id,
+ project_number,
+ reservation_name,
+ slot_capacity,
+ ignore_idle_slots,
+ edition,
+ autoscale,
+ concurrency,
+ creation_time,
+ update_time
+ FROM {reservations_table}
+ WHERE (:reservation_name IS NULL OR reservation_name = :reservation_name)
+ ORDER BY reservation_name;
+
+ -- name: sessions_by_region
+ -- dialect: bigquery
+ SELECT
+ project_id,
+ project_number,
+ user_email,
+ session_id,
+ creation_time,
+ expiration_time,
+ is_active
+ FROM {sessions_table}
+ WHERE (:user_email IS NULL OR user_email = :user_email)
+ AND (:is_active IS NULL OR is_active = :is_active)
+ ORDER BY creation_time DESC
+ LIMIT :limit;
+ ```
+
+??? code "`tables.sql` (4 named queries)"
+
+ ```sql
+ -- name: options_by_dataset
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name,
+ option_name,
+ option_type,
+ option_value
+ FROM {table_options_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY table_schema, table_name, option_name;
+
+ -- name: partitions_by_dataset
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name,
+ partition_id,
+ total_rows,
+ total_logical_bytes,
+ last_modified_time,
+ storage_tier
+ FROM {partitions_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY table_schema, table_name, partition_id;
+
+ -- name: storage_by_dataset
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name,
+ total_rows,
+ total_partitions,
+ total_logical_bytes,
+ active_logical_bytes,
+ long_term_logical_bytes,
+ total_physical_bytes,
+ active_physical_bytes,
+ long_term_physical_bytes,
+ time_travel_physical_bytes,
+ storage_last_modified_time
+ FROM {table_storage_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY table_schema, table_name;
+
+ -- name: by_schema
+ -- dialect: bigquery
+ WITH RECURSIVE dependency_tree AS (
+ SELECT
+ t.table_name,
+ 0 AS level,
+ [t.table_name] AS path
+ FROM {tables_table} t
+ WHERE t.table_type = 'BASE TABLE'
+ AND (:schema_name IS NULL OR t.table_schema = :schema_name)
+ AND NOT EXISTS (
+ SELECT 1
+ FROM {kcu_table} kcu
+ JOIN {rc_table} rc
+ ON kcu.constraint_name = rc.constraint_name
+ AND kcu.constraint_schema = rc.constraint_schema
+ JOIN {kcu_table} pk_kcu
+ ON rc.unique_constraint_name = pk_kcu.constraint_name
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ AND kcu.ordinal_position = pk_kcu.ordinal_position
+ WHERE kcu.table_name = t.table_name
+ AND pk_kcu.table_name <> t.table_name
+ AND (:schema_name IS NULL OR kcu.table_schema = :schema_name)
+ )
+
+ UNION ALL
+
+ SELECT
+ kcu.table_name,
+ dt.level + 1,
+ ARRAY_CONCAT(dt.path, [kcu.table_name])
+ FROM {kcu_table} kcu
+ JOIN {rc_table} rc
+ ON kcu.constraint_name = rc.constraint_name
+ AND kcu.constraint_schema = rc.constraint_schema
+ JOIN {kcu_table} pk_kcu
+ ON rc.unique_constraint_name = pk_kcu.constraint_name
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ AND kcu.ordinal_position = pk_kcu.ordinal_position
+ JOIN dependency_tree dt ON pk_kcu.table_name = dt.table_name
+ WHERE pk_kcu.table_name <> kcu.table_name
+ AND kcu.table_name NOT IN UNNEST(dt.path)
+ AND (:schema_name IS NULL OR kcu.table_schema = :schema_name)
+ ),
+ dependency_levels AS (
+ SELECT
+ table_name,
+ MAX(level) AS dependency_level
+ FROM dependency_tree
+ GROUP BY table_name
+ )
+ SELECT
+ t.table_name,
+ COALESCE(dl.dependency_level, 0) AS dependency_level,
+ COALESCE(dl.dependency_level, 0) AS level
+ FROM {tables_table} t
+ LEFT JOIN dependency_levels dl ON dl.table_name = t.table_name
+ WHERE t.table_type = 'BASE TABLE'
+ AND (:schema_name IS NULL OR t.table_schema = :schema_name)
+ ORDER BY dependency_level, t.table_name;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: bigquery
+ SELECT NULL AS version;
+ ```
+
+??? code "`views.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_dataset
+ -- dialect: bigquery
+ SELECT
+ v.table_catalog,
+ v.table_schema,
+ v.table_name,
+ v.view_definition,
+ v.check_option,
+ v.use_standard_sql
+ FROM {views_table} AS v
+ WHERE (:schema_name IS NULL OR v.table_schema = :schema_name)
+ AND (:view_name IS NULL OR v.table_name = :view_name)
+ ORDER BY v.table_schema, v.table_name;
+
+ -- name: materialized_by_dataset
+ -- dialect: bigquery
+ SELECT
+ table_catalog,
+ table_schema,
+ table_name,
+ last_refresh_time,
+ refresh_watermark,
+ replica_source_catalog,
+ replica_source_schema,
+ replica_source_name,
+ replication_status,
+ replication_error
+ FROM {materialized_views_table}
+ WHERE (:schema_name IS NULL OR table_schema = :schema_name)
+ AND (:view_name IS NULL OR table_name = :view_name)
+ ORDER BY table_schema, table_name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/cockroachdb.md b/docs/reference/data-dictionary/dialects/cockroachdb.md
new file mode 100644
index 000000000..ed9176f38
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/cockroachdb.md
@@ -0,0 +1,580 @@
+# CockroachDB Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("cockroachdb")
+```
+
+Introspects `crdb_internal` (`create_statements`, `tables`) and `pg_catalog` compatibility views across `cockroach_asyncpg` and `cockroach_psycopg`. See the [CockroachDB Guide](../../../databases/cockroachdb.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `cockroachdb` |
+| **Default Schema** | `"public"` |
+| **Version Pattern** | `CockroachDB (?:CCL )?v(\d+)\.(\d+)\.(\d+)` |
+| **DDL Strategy** | Native `crdb_internal.create_statement` + AST fallback |
+| **Catalog `.sql` Files** | `15` files (`18` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `True` | `array` | `ARRAY` |
+| `supports_crdb_internal_metadata` | `False` | `bigint` | `INT8` |
+| `supports_for_update` | `True` | `blob` | `BYTES` |
+| `supports_on_conflict` | `True` | `boolean` | `BOOL` |
+| `supports_prepared_statements` | `True` | `float` | `FLOAT8` |
+| `supports_schemas` | `True` | `integer` | `INT8` |
+| `supports_skip_locked` | `True` | `json` | `JSONB` |
+| `supports_transactions` | `True` | `text` | `STRING` |
+| `supports_update_from` | `True` | `timestamp` | `TIMESTAMPTZ` |
+| `supports_uuid` | `True` | `uuid` | `UUID` |
+| `supports_cte` | `>= 19.1.0` | `varchar` | `VARCHAR({length})` |
+| `supports_json` | `>= 20.1.0` | - | - |
+| `supports_returning` | `>= 20.1.0` | - | - |
+| `supports_upsert` | `>= 19.2.0` | - | - |
+| `supports_window_functions` | `>= 19.1.0` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.cockroach_asyncpg.data_dictionary.CockroachAsyncpgDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.cockroach_psycopg.data_dictionary.CockroachPsycopgSyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.cockroach_psycopg.data_dictionary.CockroachPsycopgAsyncDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `cockroachdb` dialect pack defines `18` named queries across `15` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/cockroachdb/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `comments.sql` | `by_schema` | `:schema_name` |
+| `constraints.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `crdb_internal.sql` | `ranges` | `:schema_name`, `:table_name` |
+| `ddl.sql` | `by_object` | `:object_name`, `:object_type`, `:schema_name` |
+| `dependencies.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `objects.sql` | `by_schema` | `:schema_name` |
+| `privileges.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `schemas.sql` | `list` | *none* |
+| `sequences.sql` | `by_schema` | `:schema_name` |
+| `tables.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `version.sql` | `current` | *none* |
+| `views.sql` | `by_schema` | `:schema_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ WITH target_schema AS (
+ SELECT COALESCE(
+ :schema_name::text,
+ (
+ SELECT rn.nspname::text
+ FROM pg_catalog.pg_class rc
+ JOIN pg_catalog.pg_namespace rn ON rn.oid = rc.relnamespace
+ WHERE rc.oid = pg_catalog.to_regclass(pg_catalog.quote_ident(:table_name::text))
+ ),
+ current_schema()
+ ) AS schema_name
+ ),
+ pk_columns AS (
+ SELECT DISTINCT
+ tc.table_schema,
+ tc.table_name,
+ kcu.column_name
+ FROM target_schema ts
+ JOIN information_schema.table_constraints tc
+ ON tc.table_schema = ts.schema_name
+ JOIN information_schema.key_column_usage kcu
+ ON kcu.constraint_schema = tc.constraint_schema
+ AND kcu.constraint_name = tc.constraint_name
+ AND kcu.table_name = tc.table_name
+ WHERE tc.constraint_type = 'PRIMARY KEY'
+ AND (:table_name::text IS NULL OR tc.table_name = :table_name)
+ ),
+ identity_attrs AS (
+ SELECT
+ n.nspname AS table_schema,
+ c.relname AS table_name,
+ a.attname AS column_name,
+ CASE
+ WHEN a.attidentity = 'a' THEN 'ALWAYS'
+ WHEN a.attidentity = 'd' THEN 'BY DEFAULT'
+ ELSE NULL
+ END AS identity_generation,
+ CASE WHEN a.attidentity IN ('a', 'd') THEN 1 ELSE 0 END AS is_identity
+ FROM target_schema ts
+ JOIN pg_catalog.pg_namespace n ON n.nspname = ts.schema_name
+ JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid
+ JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
+ WHERE a.attnum > 0
+ AND NOT a.attisdropped
+ AND (:table_name::text IS NULL OR c.relname = :table_name)
+ )
+ SELECT
+ c.table_catalog::text AS table_catalog,
+ c.table_schema::text AS table_schema,
+ c.table_schema::text AS schema_name,
+ c.table_name::text AS table_name,
+ c.column_name::text AS column_name,
+ c.ordinal_position,
+ c.data_type::text AS data_type,
+ c.is_nullable::text AS is_nullable,
+ c.column_default::text AS column_default,
+ c.generation_expression::text AS generation_expression,
+ CASE WHEN c.generation_expression IS NOT NULL AND c.generation_expression <> '' THEN 1 ELSE 0 END AS is_generated,
+ CASE WHEN c.is_hidden = 'YES' THEN 1 ELSE 0 END AS is_hidden,
+ CASE WHEN pk.column_name IS NOT NULL THEN 1 ELSE 0 END AS is_primary,
+ ia.identity_generation,
+ COALESCE(ia.is_identity, 0) AS is_identity,
+ pg_catalog.pg_get_serial_sequence(
+ pg_catalog.quote_ident(c.table_schema) || '.' || pg_catalog.quote_ident(c.table_name), c.column_name
+ )::text AS sequence_name
+ FROM target_schema ts
+ JOIN information_schema.columns AS c
+ ON c.table_schema = ts.schema_name
+ LEFT JOIN pk_columns pk
+ ON pk.table_schema = c.table_schema
+ AND pk.table_name = c.table_name
+ AND pk.column_name = c.column_name
+ LEFT JOIN identity_attrs ia
+ ON ia.table_schema = c.table_schema
+ AND ia.table_name = c.table_name
+ AND ia.column_name = c.column_name
+ WHERE (:table_name::text IS NULL OR c.table_name = :table_name)
+ ORDER BY c.table_schema, c.table_name, c.ordinal_position;
+
+ -- name: by_table
+ -- dialect: cockroachdb
+ WITH target_schema AS (
+ SELECT COALESCE(
+ :schema_name::text,
+ (
+ SELECT rn.nspname::text
+ FROM pg_catalog.pg_class rc
+ JOIN pg_catalog.pg_namespace rn ON rn.oid = rc.relnamespace
+ WHERE rc.oid = pg_catalog.to_regclass(pg_catalog.quote_ident(:table_name::text))
+ ),
+ current_schema()
+ ) AS schema_name
+ ),
+ pk_columns AS (
+ SELECT DISTINCT
+ tc.table_schema,
+ tc.table_name,
+ kcu.column_name
+ FROM target_schema ts
+ JOIN information_schema.table_constraints tc
+ ON tc.table_schema = ts.schema_name
+ JOIN information_schema.key_column_usage kcu
+ ON kcu.constraint_schema = tc.constraint_schema
+ AND kcu.constraint_name = tc.constraint_name
+ AND kcu.table_name = tc.table_name
+ WHERE tc.constraint_type = 'PRIMARY KEY'
+ AND tc.table_name = :table_name
+ ),
+ identity_attrs AS (
+ SELECT
+ n.nspname AS table_schema,
+ c.relname AS table_name,
+ a.attname AS column_name,
+ CASE
+ WHEN a.attidentity = 'a' THEN 'ALWAYS'
+ WHEN a.attidentity = 'd' THEN 'BY DEFAULT'
+ ELSE NULL
+ END AS identity_generation,
+ CASE WHEN a.attidentity IN ('a', 'd') THEN 1 ELSE 0 END AS is_identity
+ FROM target_schema ts
+ JOIN pg_catalog.pg_namespace n ON n.nspname = ts.schema_name
+ JOIN pg_catalog.pg_class c ON c.relnamespace = n.oid
+ JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid
+ WHERE a.attnum > 0
+ AND NOT a.attisdropped
+ AND c.relname = :table_name
+ )
+ SELECT
+ c.table_catalog::text AS table_catalog,
+ c.table_schema::text AS table_schema,
+ c.table_schema::text AS schema_name,
+ c.table_name::text AS table_name,
+ c.column_name::text AS column_name,
+ c.ordinal_position,
+ c.data_type::text AS data_type,
+ c.is_nullable::text AS is_nullable,
+ c.column_default::text AS column_default,
+ c.generation_expression::text AS generation_expression,
+ CASE WHEN c.generation_expression IS NOT NULL AND c.generation_expression <> '' THEN 1 ELSE 0 END AS is_generated,
+ CASE WHEN c.is_hidden = 'YES' THEN 1 ELSE 0 END AS is_hidden,
+ CASE WHEN pk.column_name IS NOT NULL THEN 1 ELSE 0 END AS is_primary,
+ ia.identity_generation,
+ COALESCE(ia.is_identity, 0) AS is_identity,
+ pg_catalog.pg_get_serial_sequence(
+ pg_catalog.quote_ident(c.table_schema) || '.' || pg_catalog.quote_ident(c.table_name), c.column_name
+ )::text AS sequence_name
+ FROM target_schema ts
+ JOIN information_schema.columns AS c
+ ON c.table_schema = ts.schema_name
+ LEFT JOIN pk_columns pk
+ ON pk.table_schema = c.table_schema
+ AND pk.table_name = c.table_name
+ AND pk.column_name = c.column_name
+ LEFT JOIN identity_attrs ia
+ ON ia.table_schema = c.table_schema
+ AND ia.table_name = c.table_name
+ AND ia.column_name = c.column_name
+ WHERE c.table_name = :table_name
+ ORDER BY c.ordinal_position;
+ ```
+
+??? code "`comments.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ table_schema::text AS schema_name,
+ table_name::text AS object_name,
+ 'table'::text AS object_type,
+ NULL::text AS comment
+ FROM information_schema.tables
+ WHERE table_schema = :schema_name
+ ORDER BY table_schema, table_name;
+ ```
+
+??? code "`constraints.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ tc.constraint_catalog::text AS constraint_catalog,
+ tc.constraint_schema::text AS constraint_schema,
+ tc.table_schema::text AS schema_name,
+ tc.table_name::text AS table_name,
+ tc.constraint_name::text AS constraint_name,
+ tc.constraint_type::text AS constraint_type,
+ tc.is_deferrable::text AS is_deferrable,
+ tc.initially_deferred::text AS initially_deferred,
+ ARRAY(
+ SELECT kcu.column_name::text
+ FROM information_schema.key_column_usage kcu
+ WHERE kcu.constraint_catalog = tc.constraint_catalog
+ AND kcu.constraint_schema = tc.constraint_schema
+ AND kcu.constraint_name = tc.constraint_name
+ AND kcu.table_schema = tc.table_schema
+ AND kcu.table_name = tc.table_name
+ ORDER BY kcu.ordinal_position
+ )::text[] AS columns,
+ rc.unique_constraint_schema::text AS referenced_schema,
+ pk_tc.table_name::text AS referenced_table,
+ ARRAY(
+ SELECT pk_kcu.column_name::text
+ FROM information_schema.key_column_usage pk_kcu
+ WHERE pk_kcu.constraint_catalog = rc.unique_constraint_catalog
+ AND pk_kcu.constraint_schema = rc.unique_constraint_schema
+ AND pk_kcu.constraint_name = rc.unique_constraint_name
+ ORDER BY pk_kcu.ordinal_position
+ )::text[] AS referenced_columns
+ FROM information_schema.table_constraints tc
+ LEFT JOIN information_schema.referential_constraints rc
+ ON rc.constraint_catalog = tc.constraint_catalog
+ AND rc.constraint_schema = tc.constraint_schema
+ AND rc.constraint_name = tc.constraint_name
+ LEFT JOIN information_schema.table_constraints pk_tc
+ ON pk_tc.constraint_catalog = rc.unique_constraint_catalog
+ AND pk_tc.constraint_schema = rc.unique_constraint_schema
+ AND pk_tc.constraint_name = rc.unique_constraint_name
+ WHERE tc.table_schema = :schema_name
+ AND (:table_name::text IS NULL OR tc.table_name = :table_name)
+ ORDER BY tc.table_schema, tc.table_name, tc.constraint_name;
+ ```
+
+??? code "`crdb_internal.sql` (1 named queries)"
+
+ ```sql
+ -- name: ranges
+ -- dialect: cockroachdb
+ SELECT
+ database_name::text AS database_name,
+ schema_name::text AS schema_name,
+ table_name::text AS table_name,
+ range_id::text AS range_id
+ FROM crdb_internal.ranges
+ WHERE (:schema_name::text IS NULL OR schema_name = :schema_name)
+ AND (:table_name::text IS NULL OR table_name = :table_name)
+ ORDER BY database_name, schema_name, table_name, range_id;
+ ```
+
+??? code "`ddl.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_object
+ -- dialect: cockroachdb
+ SELECT
+ :schema_name::text AS schema_name,
+ :object_name::text AS object_name,
+ :object_type::text AS object_type,
+ NULL::text AS ddl,
+ 'lossy'::text AS fidelity,
+ 'SHOW CREATE requires a safely quoted identifier and is not emitted by the bind-only query pack'::text AS warning
+ WHERE :object_name::text IS NOT NULL;
+ ```
+
+??? code "`dependencies.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ tc.table_schema::text AS schema_name,
+ tc.table_name::text AS object_name,
+ 'table'::text AS object_type,
+ ccu.table_schema::text AS referenced_schema,
+ ccu.table_name::text AS referenced_object,
+ 'table'::text AS referenced_object_type,
+ 'foreign_key'::text AS dependency_type,
+ tc.constraint_name::text AS constraint_name
+ FROM information_schema.table_constraints tc
+ JOIN information_schema.constraint_column_usage ccu
+ ON ccu.constraint_catalog = tc.constraint_catalog
+ AND ccu.constraint_schema = tc.constraint_schema
+ AND ccu.constraint_name = tc.constraint_name
+ WHERE tc.constraint_type = 'FOREIGN KEY'
+ AND tc.table_schema = :schema_name
+ AND (:object_name::text IS NULL OR tc.table_name = :object_name)
+ ORDER BY tc.table_schema, tc.table_name, tc.constraint_name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: cockroachdb
+ SELECT
+ tc.constraint_name::text AS constraint_name,
+ kcu.column_name::text AS column_name,
+ pk_kcu.table_name::text AS referenced_table,
+ pk_kcu.column_name::text AS referenced_column,
+ tc.table_name::text AS table_name,
+ tc.table_schema::text AS table_schema,
+ pk_kcu.table_schema::text AS referenced_schema,
+ pk_kcu.table_schema::text AS referenced_table_schema,
+ rc.delete_rule::text AS on_delete,
+ rc.update_rule::text AS on_update
+ FROM information_schema.table_constraints tc
+ JOIN information_schema.key_column_usage kcu
+ ON tc.constraint_catalog = kcu.constraint_catalog
+ AND tc.constraint_schema = kcu.constraint_schema
+ AND tc.constraint_name = kcu.constraint_name
+ JOIN information_schema.referential_constraints rc
+ ON tc.constraint_catalog = rc.constraint_catalog
+ AND tc.constraint_schema = rc.constraint_schema
+ AND tc.constraint_name = rc.constraint_name
+ JOIN information_schema.key_column_usage pk_kcu
+ ON rc.unique_constraint_catalog = pk_kcu.constraint_catalog
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ AND rc.unique_constraint_name = pk_kcu.constraint_name
+ AND COALESCE(kcu.position_in_unique_constraint, kcu.ordinal_position) = pk_kcu.ordinal_position
+ WHERE tc.constraint_type = 'FOREIGN KEY'
+ AND tc.table_schema = :schema_name
+ AND tc.table_name = :table_name
+ ORDER BY tc.constraint_name, kcu.ordinal_position;
+
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ tc.constraint_name::text AS constraint_name,
+ kcu.column_name::text AS column_name,
+ pk_kcu.table_name::text AS referenced_table,
+ pk_kcu.column_name::text AS referenced_column,
+ tc.table_name::text AS table_name,
+ tc.table_schema::text AS table_schema,
+ pk_kcu.table_schema::text AS referenced_schema,
+ pk_kcu.table_schema::text AS referenced_table_schema,
+ rc.delete_rule::text AS on_delete,
+ rc.update_rule::text AS on_update
+ FROM information_schema.table_constraints tc
+ JOIN information_schema.key_column_usage kcu
+ ON tc.constraint_catalog = kcu.constraint_catalog
+ AND tc.constraint_schema = kcu.constraint_schema
+ AND tc.constraint_name = kcu.constraint_name
+ JOIN information_schema.referential_constraints rc
+ ON tc.constraint_catalog = rc.constraint_catalog
+ AND tc.constraint_schema = rc.constraint_schema
+ AND tc.constraint_name = rc.constraint_name
+ JOIN information_schema.key_column_usage pk_kcu
+ ON rc.unique_constraint_catalog = pk_kcu.constraint_catalog
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ AND rc.unique_constraint_name = pk_kcu.constraint_name
+ AND COALESCE(kcu.position_in_unique_constraint, kcu.ordinal_position) = pk_kcu.ordinal_position
+ WHERE tc.constraint_type = 'FOREIGN KEY'
+ AND tc.table_schema = :schema_name
+ ORDER BY tc.table_name, tc.constraint_name, kcu.ordinal_position;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ table_schema::text AS schema_name,
+ table_name::text AS table_name,
+ index_name::text AS index_name,
+ array_agg(column_name::text ORDER BY seq_in_index::int) AS columns,
+ (non_unique = false OR non_unique::text IN ('0', 'f', 'false', 'NO')) AS is_unique,
+ index_name = 'primary' AS is_primary
+ FROM information_schema.statistics
+ WHERE table_schema = :schema_name
+ AND (:table_name::text IS NULL OR table_name = :table_name)
+ GROUP BY table_schema, table_name, index_name, non_unique
+ ORDER BY table_schema, table_name, index_name;
+
+ -- name: by_table
+ -- dialect: cockroachdb
+ SELECT
+ i.relname::text AS index_name,
+ t.relname::text AS table_name,
+ ix.indisunique AS is_unique,
+ ix.indisprimary AS is_primary,
+ array_agg(a.attname::text ORDER BY array_position(ix.indkey, a.attnum)) AS columns
+ FROM pg_catalog.pg_class t
+ JOIN pg_catalog.pg_namespace n ON t.relnamespace = n.oid
+ JOIN pg_catalog.pg_index ix ON t.oid = ix.indrelid
+ JOIN pg_catalog.pg_class i ON i.oid = ix.indexrelid
+ JOIN pg_catalog.pg_attribute a ON a.attrelid = t.oid AND a.attnum = ANY(ix.indkey)
+ WHERE t.relname = :table_name
+ AND n.nspname = :schema_name
+ GROUP BY t.relname, i.relname, ix.indisunique, ix.indisprimary
+ ORDER BY i.relname;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ table_catalog::text AS catalog_name,
+ table_schema::text AS schema_name,
+ table_name::text AS object_name,
+ CASE table_type
+ WHEN 'BASE TABLE' THEN 'table'
+ WHEN 'VIEW' THEN 'view'
+ ELSE table_type::text
+ END AS object_type,
+ table_type::text AS native_object_type
+ FROM information_schema.tables
+ WHERE table_schema = :schema_name
+ ORDER BY table_schema, table_name;
+ ```
+
+??? code "`privileges.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ table_schema::text AS schema_name,
+ table_name::text AS object_name,
+ grantee::text AS grantee,
+ privilege_type::text AS privilege_type,
+ is_grantable::text AS is_grantable,
+ with_hierarchy::text AS with_hierarchy
+ FROM information_schema.table_privileges
+ WHERE table_schema = :schema_name
+ AND (:object_name::text IS NULL OR table_name = :object_name)
+ ORDER BY table_schema, table_name, grantee, privilege_type;
+ ```
+
+??? code "`schemas.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: cockroachdb
+ SELECT
+ catalog_name::text AS catalog_name,
+ schema_name::text AS schema_name,
+ schema_owner::text AS owner_name
+ FROM information_schema.schemata
+ WHERE schema_name NOT LIKE 'pg\_%'
+ AND schema_name <> 'information_schema'
+ ORDER BY schema_name;
+ ```
+
+??? code "`sequences.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ sequence_schema::text AS schema_name,
+ sequence_name::text AS sequence_name,
+ data_type::text AS data_type,
+ start_value::text AS start_value,
+ minimum_value::text AS minimum_value,
+ maximum_value::text AS maximum_value,
+ increment::text AS increment,
+ cycle_option::text AS cycle_option
+ FROM information_schema.sequences
+ WHERE sequence_schema = :schema_name
+ ORDER BY sequence_schema, sequence_name;
+ ```
+
+??? code "`tables.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ table_catalog::text AS table_catalog,
+ table_schema::text AS table_schema,
+ table_schema::text AS schema_name,
+ table_name::text AS table_name,
+ table_type::text AS table_type,
+ NULL::int AS dependency_level,
+ NULL::int AS level
+ FROM information_schema.tables
+ WHERE table_schema = :schema_name
+ AND (:table_name::text IS NULL OR table_name = :table_name)
+ AND table_type = 'BASE TABLE'
+ ORDER BY table_schema, table_name;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: cockroachdb
+ SELECT version();
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: cockroachdb
+ SELECT
+ table_schema::text AS schema_name,
+ table_name::text AS view_name,
+ view_definition::text AS definition,
+ check_option::text AS check_option,
+ is_updatable::text AS is_updatable
+ FROM information_schema.views
+ WHERE table_schema = :schema_name
+ ORDER BY table_schema, table_name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/db2.md b/docs/reference/data-dictionary/dialects/db2.md
new file mode 100644
index 000000000..ae1d7ffdc
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/db2.md
@@ -0,0 +1,519 @@
+# IBM Db2 Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("db2")
+```
+
+Introspects IBM Db2 `SYSCAT` views (`SYSCAT.TABLES`, `SYSCAT.COLUMNS`, `SYSCAT.INDEXES`, `SYSCAT.REFERENCES`, `SYSCAT.TABCONST`, `SYSCAT.VIEWS`, `SYSCAT.ROUTINES`, `SYSCAT.TRIGGERS`, `SYSCAT.SEQUENCES`, `SYSCAT.DATATYPES`). See the [IBM Db2 Guide](../../../databases/db2.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `db2` |
+| **Default Schema** | `None` |
+| **Version Pattern** | `(?:v|V)?(\d+)\.(\d+)(?:\.(\d+))?` |
+| **DDL Strategy** | AST synthesis + `SYSCAT` catalog text definitions |
+| **Catalog `.sql` Files** | `9` files (`16` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_for_update` | `True` | `bigint` | `BIGINT` |
+| `supports_in_memory` | `False` | `blob` | `BLOB` |
+| `supports_on_conflict` | `False` | `boolean` | `BOOLEAN` |
+| `supports_prepared_statements` | `True` | `decimal` | `DECIMAL(31, 10)` |
+| `supports_returning` | `False` | `float` | `DOUBLE` |
+| `supports_schemas` | `True` | `integer` | `INTEGER` |
+| `supports_sequences` | `True` | `json` | `CLOB` |
+| `supports_skip_locked` | `True` | `text` | `CLOB` |
+| `supports_transactions` | `True` | `timestamp` | `TIMESTAMP` |
+| `supports_update_from` | `False` | `uuid` | `VARCHAR(36)` |
+| `supports_cte` | `>= 8.1.0` | `varchar` | `VARCHAR(255)` |
+| `supports_generated_columns` | `>= 8.1.0` | - | - |
+| `supports_window_functions` | `>= 8.1.0` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.db2.data_dictionary.Db2SyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.db2.data_dictionary.Db2AsyncDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `db2` dialect pack defines `16` named queries across `9` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/db2/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `constraints.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `foreign_keys.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `objects.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `schemas.sql` | `by_schema`, `current` | `:schema_name` |
+| `tables.sql` | `by_schema`, `all_by_schema`, `names_by_schema` | `:schema_name`, `:table_name` |
+| `version.sql` | `current` | *none* |
+| `views.sql` | `by_schema` | `:schema_name`, `:view_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: db2
+ WITH unique_single_cols AS (
+ SELECT DISTINCT
+ RTRIM(TABSCHEMA) AS TABSCHEMA,
+ RTRIM(TABNAME) AS TABNAME,
+ RTRIM(SUBSTR(COLNAMES, 2)) AS COLNAME
+ FROM SYSCAT.INDEXES
+ WHERE UNIQUERULE IN ('U', 'P')
+ AND COLCOUNT = 1
+ AND TABSCHEMA NOT LIKE 'SYS%'
+ AND TABSCHEMA NOT LIKE 'NULLID%'
+ AND TABSCHEMA NOT LIKE 'SQLJ%'
+ AND TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:table_name AS VARCHAR(128)) IS NULL OR TABNAME = :table_name)
+ )
+ SELECT
+ RTRIM(c.TABSCHEMA) AS schema_name,
+ RTRIM(c.TABNAME) AS table_name,
+ RTRIM(c.COLNAME) AS column_name,
+ RTRIM(c.TYPENAME) AS data_type,
+ CASE c.NULLS WHEN 'Y' THEN 1 ELSE 0 END AS is_nullable,
+ c.DEFAULT AS column_default,
+ c.COLNO + 1 AS ordinal_position,
+ c.LENGTH AS max_length,
+ c.SCALE AS numeric_scale,
+ CASE WHEN c.KEYSEQ IS NOT NULL AND c.KEYSEQ > 0 THEN 1 ELSE 0 END AS is_primary,
+ CASE WHEN u.COLNAME IS NOT NULL THEN 1 ELSE 0 END AS is_unique,
+ CASE
+ WHEN c.IDENTITY = 'Y' AND c.GENERATED = 'A' THEN 'ALWAYS'
+ WHEN c.IDENTITY = 'Y' AND c.GENERATED = 'D' THEN 'BY DEFAULT'
+ WHEN c.IDENTITY = 'Y' THEN 'BY DEFAULT'
+ ELSE NULL
+ END AS identity_generation,
+ CASE WHEN c.IDENTITY = 'Y' THEN 1 ELSE 0 END AS is_identity,
+ CASE WHEN c.GENERATED IN ('A', 'D') AND COALESCE(c.IDENTITY, 'N') <> 'Y' THEN 1 ELSE 0 END AS is_generated,
+ c.REMARKS AS column_comment
+ FROM SYSCAT.COLUMNS c
+ JOIN SYSCAT.TABLES t ON c.TABSCHEMA = t.TABSCHEMA AND c.TABNAME = t.TABNAME
+ LEFT JOIN unique_single_cols u
+ ON u.TABSCHEMA = RTRIM(c.TABSCHEMA)
+ AND u.TABNAME = RTRIM(c.TABNAME)
+ AND u.COLNAME = RTRIM(c.COLNAME)
+ WHERE c.TABSCHEMA NOT LIKE 'SYS%'
+ AND c.TABSCHEMA NOT LIKE 'NULLID%'
+ AND c.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND c.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:table_name AS VARCHAR(128)) IS NULL OR c.TABNAME = :table_name)
+ ORDER BY c.TABSCHEMA, c.TABNAME, c.COLNO;
+
+ -- name: by_table
+ -- dialect: db2
+ WITH unique_single_cols AS (
+ SELECT DISTINCT
+ RTRIM(TABSCHEMA) AS TABSCHEMA,
+ RTRIM(TABNAME) AS TABNAME,
+ RTRIM(SUBSTR(COLNAMES, 2)) AS COLNAME
+ FROM SYSCAT.INDEXES
+ WHERE UNIQUERULE IN ('U', 'P')
+ AND COLCOUNT = 1
+ AND TABSCHEMA NOT LIKE 'SYS%'
+ AND TABSCHEMA NOT LIKE 'NULLID%'
+ AND TABSCHEMA NOT LIKE 'SQLJ%'
+ AND TABNAME = :table_name
+ AND TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ )
+ SELECT
+ RTRIM(c.TABSCHEMA) AS schema_name,
+ RTRIM(c.TABNAME) AS table_name,
+ RTRIM(c.COLNAME) AS column_name,
+ RTRIM(c.TYPENAME) AS data_type,
+ CASE c.NULLS WHEN 'Y' THEN 1 ELSE 0 END AS is_nullable,
+ c.DEFAULT AS column_default,
+ c.COLNO + 1 AS ordinal_position,
+ c.LENGTH AS max_length,
+ c.SCALE AS numeric_scale,
+ CASE WHEN c.KEYSEQ IS NOT NULL AND c.KEYSEQ > 0 THEN 1 ELSE 0 END AS is_primary,
+ CASE WHEN u.COLNAME IS NOT NULL THEN 1 ELSE 0 END AS is_unique,
+ CASE
+ WHEN c.IDENTITY = 'Y' AND c.GENERATED = 'A' THEN 'ALWAYS'
+ WHEN c.IDENTITY = 'Y' AND c.GENERATED = 'D' THEN 'BY DEFAULT'
+ WHEN c.IDENTITY = 'Y' THEN 'BY DEFAULT'
+ ELSE NULL
+ END AS identity_generation,
+ CASE WHEN c.IDENTITY = 'Y' THEN 1 ELSE 0 END AS is_identity,
+ CASE WHEN c.GENERATED IN ('A', 'D') AND COALESCE(c.IDENTITY, 'N') <> 'Y' THEN 1 ELSE 0 END AS is_generated,
+ c.REMARKS AS column_comment
+ FROM SYSCAT.COLUMNS c
+ LEFT JOIN unique_single_cols u
+ ON u.TABSCHEMA = RTRIM(c.TABSCHEMA)
+ AND u.TABNAME = RTRIM(c.TABNAME)
+ AND u.COLNAME = RTRIM(c.COLNAME)
+ WHERE c.TABSCHEMA NOT LIKE 'SYS%'
+ AND c.TABSCHEMA NOT LIKE 'NULLID%'
+ AND c.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND c.TABNAME = :table_name
+ AND c.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ ORDER BY c.COLNO;
+ ```
+
+??? code "`constraints.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: db2
+ SELECT
+ RTRIM(tc.TABSCHEMA) AS schema_name,
+ RTRIM(tc.TABNAME) AS table_name,
+ RTRIM(tc.CONSTNAME) AS constraint_name,
+ CASE tc.TYPE
+ WHEN 'P' THEN 'PRIMARY KEY'
+ WHEN 'U' THEN 'UNIQUE'
+ WHEN 'F' THEN 'FOREIGN KEY'
+ WHEN 'K' THEN 'CHECK'
+ ELSE tc.TYPE
+ END AS constraint_type,
+ RTRIM(ck.TEXT) AS check_clause,
+ tc.ENFORCED AS is_enforced
+ FROM SYSCAT.TABCONST tc
+ LEFT JOIN SYSCAT.CHECKS ck
+ ON tc.TABSCHEMA = ck.TABSCHEMA AND tc.TABNAME = ck.TABNAME AND tc.CONSTNAME = ck.CONSTNAME
+ WHERE tc.TABSCHEMA NOT LIKE 'SYS%'
+ AND tc.TABSCHEMA NOT LIKE 'NULLID%'
+ AND tc.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND tc.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:table_name AS VARCHAR(128)) IS NULL OR tc.TABNAME = :table_name)
+ ORDER BY tc.TABSCHEMA, tc.TABNAME, tc.CONSTNAME;
+
+ -- name: by_table
+ -- dialect: db2
+ SELECT
+ RTRIM(tc.TABSCHEMA) AS schema_name,
+ RTRIM(tc.TABNAME) AS table_name,
+ RTRIM(tc.CONSTNAME) AS constraint_name,
+ CASE tc.TYPE
+ WHEN 'P' THEN 'PRIMARY KEY'
+ WHEN 'U' THEN 'UNIQUE'
+ WHEN 'F' THEN 'FOREIGN KEY'
+ WHEN 'K' THEN 'CHECK'
+ ELSE tc.TYPE
+ END AS constraint_type,
+ RTRIM(ck.TEXT) AS check_clause,
+ tc.ENFORCED AS is_enforced
+ FROM SYSCAT.TABCONST tc
+ LEFT JOIN SYSCAT.CHECKS ck
+ ON tc.TABSCHEMA = ck.TABSCHEMA AND tc.TABNAME = ck.TABNAME AND tc.CONSTNAME = ck.CONSTNAME
+ WHERE tc.TABSCHEMA NOT LIKE 'SYS%'
+ AND tc.TABSCHEMA NOT LIKE 'NULLID%'
+ AND tc.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND tc.TABNAME = :table_name
+ AND tc.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ ORDER BY tc.CONSTNAME;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: db2
+ SELECT
+ RTRIM(r.TABSCHEMA) AS schema_name,
+ RTRIM(r.TABNAME) AS table_name,
+ RTRIM(r.CONSTNAME) AS constraint_name,
+ RTRIM(k.COLNAME) AS column_name,
+ RTRIM(r.REFTABSCHEMA) AS referenced_schema,
+ RTRIM(r.REFTABNAME) AS referenced_table,
+ RTRIM(refk.COLNAME) AS referenced_column
+ FROM SYSCAT.REFERENCES r
+ JOIN SYSCAT.KEYCOLUSE k
+ ON r.TABSCHEMA = k.TABSCHEMA AND r.CONSTNAME = k.CONSTNAME
+ JOIN SYSCAT.KEYCOLUSE refk
+ ON r.REFTABSCHEMA = refk.TABSCHEMA AND r.REFKEYNAME = refk.CONSTNAME AND k.COLSEQ = refk.COLSEQ
+ WHERE r.TABSCHEMA NOT LIKE 'SYS%'
+ AND r.TABSCHEMA NOT LIKE 'NULLID%'
+ AND r.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND r.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:table_name AS VARCHAR(128)) IS NULL OR r.TABNAME = :table_name)
+ ORDER BY r.TABSCHEMA, r.TABNAME, r.CONSTNAME, k.COLSEQ;
+
+ -- name: by_table
+ -- dialect: db2
+ SELECT
+ RTRIM(r.TABSCHEMA) AS schema_name,
+ RTRIM(r.TABNAME) AS table_name,
+ RTRIM(r.CONSTNAME) AS constraint_name,
+ RTRIM(k.COLNAME) AS column_name,
+ RTRIM(r.REFTABSCHEMA) AS referenced_schema,
+ RTRIM(r.REFTABNAME) AS referenced_table,
+ RTRIM(refk.COLNAME) AS referenced_column
+ FROM SYSCAT.REFERENCES r
+ JOIN SYSCAT.KEYCOLUSE k
+ ON r.TABSCHEMA = k.TABSCHEMA AND r.CONSTNAME = k.CONSTNAME
+ JOIN SYSCAT.KEYCOLUSE refk
+ ON r.REFTABSCHEMA = refk.TABSCHEMA AND r.REFKEYNAME = refk.CONSTNAME AND k.COLSEQ = refk.COLSEQ
+ WHERE r.TABSCHEMA NOT LIKE 'SYS%'
+ AND r.TABSCHEMA NOT LIKE 'NULLID%'
+ AND r.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND r.TABNAME = :table_name
+ AND r.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ ORDER BY r.CONSTNAME, k.COLSEQ;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: db2
+ SELECT
+ RTRIM(i.TABSCHEMA) AS schema_name,
+ RTRIM(i.TABNAME) AS table_name,
+ RTRIM(i.INDNAME) AS index_name,
+ RTRIM(c.COLNAME) AS column_name,
+ c.COLSEQ AS column_position,
+ CASE i.UNIQUERULE WHEN 'D' THEN 0 ELSE 1 END AS is_unique,
+ CASE i.UNIQUERULE WHEN 'P' THEN 1 ELSE 0 END AS is_primary
+ FROM SYSCAT.INDEXES i
+ JOIN SYSCAT.INDEXCOLUSE c
+ ON i.INDSCHEMA = c.INDSCHEMA AND i.INDNAME = c.INDNAME
+ WHERE i.TABSCHEMA NOT LIKE 'SYS%'
+ AND i.TABSCHEMA NOT LIKE 'NULLID%'
+ AND i.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND i.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:table_name AS VARCHAR(128)) IS NULL OR i.TABNAME = :table_name)
+ ORDER BY i.TABSCHEMA, i.TABNAME, i.INDNAME, c.COLSEQ;
+
+ -- name: by_table
+ -- dialect: db2
+ SELECT
+ RTRIM(i.TABSCHEMA) AS schema_name,
+ RTRIM(i.TABNAME) AS table_name,
+ RTRIM(i.INDNAME) AS index_name,
+ RTRIM(c.COLNAME) AS column_name,
+ c.COLSEQ AS column_position,
+ CASE i.UNIQUERULE WHEN 'D' THEN 0 ELSE 1 END AS is_unique,
+ CASE i.UNIQUERULE WHEN 'P' THEN 1 ELSE 0 END AS is_primary
+ FROM SYSCAT.INDEXES i
+ JOIN SYSCAT.INDEXCOLUSE c
+ ON i.INDSCHEMA = c.INDSCHEMA AND i.INDNAME = c.INDNAME
+ WHERE i.TABSCHEMA NOT LIKE 'SYS%'
+ AND i.TABSCHEMA NOT LIKE 'NULLID%'
+ AND i.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND i.TABNAME = :table_name
+ AND i.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ ORDER BY i.INDNAME, c.COLSEQ;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: db2
+ SELECT schema_name, object_name, object_type, created, remarks
+ FROM (
+ SELECT
+ RTRIM(t.TABSCHEMA) AS schema_name,
+ RTRIM(t.TABNAME) AS object_name,
+ CASE t.TYPE
+ WHEN 'T' THEN 'TABLE'
+ WHEN 'V' THEN 'VIEW'
+ WHEN 'A' THEN 'ALIAS'
+ WHEN 'S' THEN 'MATERIALIZED QUERY TABLE'
+ WHEN 'N' THEN 'NICKNAME'
+ WHEN 'G' THEN 'CREATED TEMPORARY TABLE'
+ ELSE t.TYPE
+ END AS object_type,
+ t.CREATE_TIME AS created,
+ t.REMARKS AS remarks
+ FROM SYSCAT.TABLES t
+ WHERE t.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:object_name AS VARCHAR(128)) IS NULL OR t.TABNAME = :object_name)
+
+ UNION ALL
+
+ SELECT
+ RTRIM(s.SEQSCHEMA) AS schema_name,
+ RTRIM(s.SEQNAME) AS object_name,
+ 'SEQUENCE' AS object_type,
+ s.CREATE_TIME AS created,
+ s.REMARKS AS remarks
+ FROM SYSCAT.SEQUENCES s
+ WHERE s.SEQTYPE = 'S'
+ AND s.SEQSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:object_name AS VARCHAR(128)) IS NULL OR s.SEQNAME = :object_name)
+
+ UNION ALL
+
+ SELECT
+ RTRIM(r.ROUTINESCHEMA) AS schema_name,
+ RTRIM(r.ROUTINENAME) AS object_name,
+ CASE r.ROUTINETYPE
+ WHEN 'F' THEN 'FUNCTION'
+ WHEN 'P' THEN 'PROCEDURE'
+ WHEN 'M' THEN 'METHOD'
+ ELSE r.ROUTINETYPE
+ END AS object_type,
+ r.CREATE_TIME AS created,
+ r.REMARKS AS remarks
+ FROM SYSCAT.ROUTINES r
+ WHERE r.ROUTINESCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:object_name AS VARCHAR(128)) IS NULL OR r.ROUTINENAME = :object_name)
+ ) objects
+ ORDER BY schema_name, object_type, object_name;
+ ```
+
+??? code "`schemas.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: db2
+ SELECT
+ RTRIM(s.SCHEMANAME) AS schema_name,
+ RTRIM(s.OWNER) AS owner_name,
+ s.CREATE_TIME AS create_time,
+ s.REMARKS AS comment
+ FROM SYSCAT.SCHEMATA s
+ WHERE s.SCHEMANAME NOT LIKE 'SYS%'
+ AND s.SCHEMANAME NOT LIKE 'NULLID%'
+ AND s.SCHEMANAME NOT LIKE 'SQLJ%'
+ AND (:schema_name IS NULL OR s.SCHEMANAME = :schema_name)
+ ORDER BY s.SCHEMANAME;
+
+ -- name: current
+ -- dialect: db2
+ VALUES CURRENT SCHEMA;
+ ```
+
+??? code "`tables.sql` (3 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: db2
+ WITH dependency_tree (schema_name, table_name, level, path) AS (
+ SELECT
+ RTRIM(t.TABSCHEMA) AS schema_name,
+ RTRIM(t.TABNAME) AS table_name,
+ 0 AS level,
+ VARCHAR('/' || RTRIM(t.TABSCHEMA) || '.' || RTRIM(t.TABNAME) || '/', 2000) AS path
+ FROM SYSCAT.TABLES t
+ WHERE t.TYPE = 'T'
+ AND t.TABSCHEMA NOT LIKE 'SYS%'
+ AND t.TABSCHEMA NOT LIKE 'NULLID%'
+ AND t.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND t.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:table_name AS VARCHAR(128)) IS NULL OR t.TABNAME = :table_name)
+ AND NOT EXISTS (
+ SELECT 1
+ FROM SYSCAT.REFERENCES r
+ WHERE r.TABSCHEMA = t.TABSCHEMA
+ AND r.TABNAME = t.TABNAME
+ AND (r.REFTABSCHEMA <> t.TABSCHEMA OR r.REFTABNAME <> t.TABNAME)
+ )
+
+ UNION ALL
+
+ SELECT
+ RTRIM(child.TABSCHEMA) AS schema_name,
+ RTRIM(child.TABNAME) AS table_name,
+ dt.level + 1 AS level,
+ VARCHAR(dt.path || RTRIM(child.TABSCHEMA) || '.' || RTRIM(child.TABNAME) || '/', 2000) AS path
+ FROM SYSCAT.REFERENCES r, SYSCAT.TABLES child, dependency_tree dt
+ WHERE r.TABSCHEMA = child.TABSCHEMA
+ AND r.TABNAME = child.TABNAME
+ AND r.REFTABSCHEMA = dt.schema_name
+ AND r.REFTABNAME = dt.table_name
+ AND (r.REFTABSCHEMA <> child.TABSCHEMA OR r.REFTABNAME <> child.TABNAME)
+ AND child.TYPE = 'T'
+ AND child.TABSCHEMA NOT LIKE 'SYS%'
+ AND child.TABSCHEMA NOT LIKE 'NULLID%'
+ AND child.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND child.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:table_name AS VARCHAR(128)) IS NULL OR child.TABNAME = :table_name)
+ AND LOCATE('/' || RTRIM(child.TABSCHEMA) || '.' || RTRIM(child.TABNAME) || '/', dt.path) = 0
+ )
+ SELECT
+ dt.schema_name,
+ dt.table_name,
+ 'BASE TABLE' AS table_type,
+ MAX(dt.level) AS dependency_level,
+ MAX(dt.level) AS level
+ FROM dependency_tree dt
+ GROUP BY dt.schema_name, dt.table_name
+ ORDER BY dependency_level, dt.schema_name, dt.table_name;
+
+ -- name: all_by_schema
+ -- dialect: db2
+ SELECT
+ RTRIM(t.TABSCHEMA) AS schema_name,
+ RTRIM(t.TABNAME) AS table_name,
+ CASE t.TYPE
+ WHEN 'T' THEN 'BASE TABLE'
+ WHEN 'V' THEN 'VIEW'
+ WHEN 'A' THEN 'ALIAS'
+ WHEN 'N' THEN 'NICKNAME'
+ ELSE 'TABLE'
+ END AS table_type,
+ 0 AS dependency_level,
+ 0 AS level,
+ t.CARD AS estimated_row_count,
+ t.REMARKS AS table_comment
+ FROM SYSCAT.TABLES t
+ WHERE t.TABSCHEMA NOT LIKE 'SYS%'
+ AND t.TABSCHEMA NOT LIKE 'NULLID%'
+ AND t.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND t.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:table_name AS VARCHAR(128)) IS NULL OR t.TABNAME = :table_name)
+ ORDER BY t.TABSCHEMA, t.TABNAME;
+
+ -- name: names_by_schema
+ -- dialect: db2
+ SELECT
+ RTRIM(t.TABNAME) AS table_name
+ FROM SYSCAT.TABLES t
+ WHERE t.TYPE = 'T'
+ AND t.TABSCHEMA NOT LIKE 'SYS%'
+ AND t.TABSCHEMA NOT LIKE 'NULLID%'
+ AND t.TABSCHEMA NOT LIKE 'SQLJ%'
+ AND t.TABSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ ORDER BY t.TABNAME;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: db2
+ SELECT
+ SERVICE_LEVEL AS service_level,
+ BLD_LEVEL AS bld_level,
+ FIXPACK_NUM AS fixpack_num,
+ INST_NAME AS inst_name
+ FROM TABLE(SYSPROC.ENV_GET_INST_INFO()) AS T;
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: db2
+ SELECT
+ RTRIM(v.VIEWSCHEMA) AS schema_name,
+ RTRIM(v.VIEWNAME) AS view_name,
+ v.TEXT AS definition,
+ CASE v.READONLY WHEN 'Y' THEN 1 ELSE 0 END AS is_read_only,
+ CASE v.VALID WHEN 'Y' THEN 1 ELSE 0 END AS is_valid
+ FROM SYSCAT.VIEWS v
+ WHERE v.VIEWSCHEMA NOT LIKE 'SYS%'
+ AND v.VIEWSCHEMA NOT LIKE 'NULLID%'
+ AND v.VIEWSCHEMA NOT LIKE 'SQLJ%'
+ AND v.VIEWSCHEMA = COALESCE(CAST(:schema_name AS VARCHAR(128)), CURRENT SCHEMA)
+ AND (CAST(:view_name AS VARCHAR(128)) IS NULL OR v.VIEWNAME = :view_name)
+ ORDER BY v.VIEWSCHEMA, v.VIEWNAME;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/duckdb.md b/docs/reference/data-dictionary/dialects/duckdb.md
new file mode 100644
index 000000000..44505c5d5
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/duckdb.md
@@ -0,0 +1,909 @@
+# DuckDB Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("duckdb")
+```
+
+Introspects DuckDB table functions (`duckdb_tables()`, `duckdb_columns()`, `duckdb_indexes()`, `duckdb_constraints()`, `duckdb_views()`, `duckdb_sequences()`, `duckdb_types()`, `duckdb_extensions()`). See the [DuckDB Guide](../../../databases/duckdb.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `duckdb` |
+| **Default Schema** | `None` |
+| **Version Pattern** | `v?(\d+)\.(\d+)\.(\d+)` |
+| **DDL Strategy** | Native `duckdb_*().sql` catalog functions + AST synthesis |
+| **Catalog `.sql` Files** | `17` files (`24` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `True` | `array` | `LIST` |
+| `supports_cte` | `True` | `bigint` | `BIGINT` |
+| `supports_for_update` | `False` | `blob` | `BLOB` |
+| `supports_json` | `True` | `boolean` | `BOOLEAN` |
+| `supports_maps` | `True` | `float` | `DOUBLE` |
+| `supports_on_conflict` | `True` | `integer` | `INTEGER` |
+| `supports_prepared_statements` | `True` | `json` | `JSON` |
+| `supports_schemas` | `True` | `map` | `MAP` |
+| `supports_skip_locked` | `False` | `struct` | `STRUCT` |
+| `supports_structs` | `True` | `text` | `TEXT` |
+| `supports_transactions` | `True` | `timestamp` | `TIMESTAMP` |
+| `supports_update_from` | `True` | `uuid` | `UUID` |
+| `supports_uuid` | `True` | `varchar` | `TEXT` |
+| `supports_window_functions` | `True` | - | - |
+| `supports_returning` | `>= 0.8.0` | - | - |
+| `supports_upsert` | `>= 0.8.0` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.duckdb.data_dictionary.DuckDBDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.adbc.data_dictionary.AdbcDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.arrow_odbc.data_dictionary.ArrowOdbcDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `duckdb` dialect pack defines `24` named queries across `17` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/duckdb/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `constraints.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `databases.sql` | `list` | *none* |
+| `ddl.sql` | `by_object`, `by_schema` | `:object_name`, `:object_type`, `:schema_name` |
+| `dependencies.sql` | `by_schema` | `:schema_name` |
+| `extensions.sql` | `list` | *none* |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `functions.sql` | `by_schema` | `:schema_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `objects.sql` | `by_schema` | `:schema_name` |
+| `schemas.sql` | `list` | *none* |
+| `sequences.sql` | `by_schema` | `:schema_name` |
+| `system.sql` | `logs`, `memory`, `settings` | *none* |
+| `tables.sql` | `by_schema` | `:schema_name` |
+| `types.sql` | `by_schema` | `:schema_name` |
+| `version.sql` | `current` | *none* |
+| `views.sql` | `by_schema` | `:schema_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ WITH table_ddl AS (
+ SELECT
+ database_name,
+ schema_name,
+ table_name,
+ regexp_replace(sql, '("([^"]|"")*")|''([^'']|'''')*''', '\1', 'g') AS ddl_quoted,
+ regexp_replace(sql, '"([^"]|"")*"|''([^'']|'''')*''', '', 'g') AS ddl_unquoted
+ FROM duckdb_tables()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ ),
+ pk_constraints AS (
+ SELECT
+ database_name,
+ schema_name,
+ table_name,
+ unnest(constraint_column_names) AS column_name
+ FROM duckdb_constraints()
+ WHERE constraint_type = 'PRIMARY KEY'
+ AND schema_name = COALESCE(:schema_name, current_schema())
+ )
+ SELECT
+ c.database_name,
+ c.schema_name,
+ c.table_name,
+ c.column_name,
+ c.column_index AS ordinal_position,
+ c.data_type,
+ c.is_nullable,
+ c.column_default,
+ c.character_maximum_length AS max_length,
+ c.numeric_precision,
+ c.numeric_scale,
+ c.comment,
+ c.internal,
+ pk.column_name IS NOT NULL AS is_primary,
+ COALESCE(
+ contains(
+ t.ddl_unquoted,
+ '(' || c.column_name || ' ' || regexp_replace(c.data_type, '"([^"]|"")*"|''([^'']|'''')*''', '', 'g')
+ || ' GENERATED ALWAYS AS('
+ )
+ OR contains(
+ t.ddl_unquoted,
+ ', ' || c.column_name || ' ' || regexp_replace(c.data_type, '"([^"]|"")*"|''([^'']|'''')*''', '', 'g')
+ || ' GENERATED ALWAYS AS('
+ )
+ OR contains(
+ t.ddl_quoted,
+ '("' || replace(c.column_name, '"', '""') || '" '
+ || regexp_replace(c.data_type, '("([^"]|"")*")|''([^'']|'''')*''', '\1', 'g') || ' GENERATED ALWAYS AS('
+ )
+ OR contains(
+ t.ddl_quoted,
+ ', "' || replace(c.column_name, '"', '""') || '" '
+ || regexp_replace(c.data_type, '("([^"]|"")*")|''([^'']|'''')*''', '\1', 'g') || ' GENERATED ALWAYS AS('
+ ),
+ FALSE
+ ) AS is_generated
+ FROM duckdb_columns() AS c
+ LEFT JOIN table_ddl AS t
+ ON t.database_name = c.database_name
+ AND t.schema_name = c.schema_name
+ AND t.table_name = c.table_name
+ LEFT JOIN pk_constraints AS pk
+ ON pk.database_name = c.database_name
+ AND pk.schema_name = c.schema_name
+ AND pk.table_name = c.table_name
+ AND pk.column_name = c.column_name
+ WHERE c.schema_name = COALESCE(:schema_name, current_schema())
+ AND NOT c.internal
+ ORDER BY c.table_name, c.column_index;
+
+ -- name: by_table
+ -- dialect: duckdb
+ WITH table_ddl AS (
+ SELECT
+ database_name,
+ schema_name,
+ table_name,
+ regexp_replace(sql, '("([^"]|"")*")|''([^'']|'''')*''', '\1', 'g') AS ddl_quoted,
+ regexp_replace(sql, '"([^"]|"")*"|''([^'']|'''')*''', '', 'g') AS ddl_unquoted
+ FROM duckdb_tables()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND table_name = :table_name
+ ),
+ pk_constraints AS (
+ SELECT
+ database_name,
+ schema_name,
+ table_name,
+ unnest(constraint_column_names) AS column_name
+ FROM duckdb_constraints()
+ WHERE constraint_type = 'PRIMARY KEY'
+ AND schema_name = COALESCE(:schema_name, current_schema())
+ AND table_name = :table_name
+ )
+ SELECT
+ c.database_name,
+ c.schema_name,
+ c.table_name,
+ c.column_name,
+ c.column_index AS ordinal_position,
+ c.data_type,
+ c.is_nullable,
+ c.column_default,
+ c.character_maximum_length AS max_length,
+ c.numeric_precision,
+ c.numeric_scale,
+ c.comment,
+ c.internal,
+ pk.column_name IS NOT NULL AS is_primary,
+ COALESCE(
+ contains(
+ t.ddl_unquoted,
+ '(' || c.column_name || ' ' || regexp_replace(c.data_type, '"([^"]|"")*"|''([^'']|'''')*''', '', 'g')
+ || ' GENERATED ALWAYS AS('
+ )
+ OR contains(
+ t.ddl_unquoted,
+ ', ' || c.column_name || ' ' || regexp_replace(c.data_type, '"([^"]|"")*"|''([^'']|'''')*''', '', 'g')
+ || ' GENERATED ALWAYS AS('
+ )
+ OR contains(
+ t.ddl_quoted,
+ '("' || replace(c.column_name, '"', '""') || '" '
+ || regexp_replace(c.data_type, '("([^"]|"")*")|''([^'']|'''')*''', '\1', 'g') || ' GENERATED ALWAYS AS('
+ )
+ OR contains(
+ t.ddl_quoted,
+ ', "' || replace(c.column_name, '"', '""') || '" '
+ || regexp_replace(c.data_type, '("([^"]|"")*")|''([^'']|'''')*''', '\1', 'g') || ' GENERATED ALWAYS AS('
+ ),
+ FALSE
+ ) AS is_generated
+ FROM duckdb_columns() AS c
+ LEFT JOIN table_ddl AS t
+ ON t.database_name = c.database_name
+ AND t.schema_name = c.schema_name
+ AND t.table_name = c.table_name
+ LEFT JOIN pk_constraints AS pk
+ ON pk.database_name = c.database_name
+ AND pk.schema_name = c.schema_name
+ AND pk.table_name = c.table_name
+ AND pk.column_name = c.column_name
+ WHERE c.schema_name = COALESCE(:schema_name, current_schema())
+ AND c.table_name = :table_name
+ AND NOT c.internal
+ ORDER BY c.column_index;
+ ```
+
+??? code "`constraints.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ table_name,
+ constraint_name,
+ constraint_type,
+ constraint_text,
+ expression,
+ constraint_column_names,
+ referenced_table,
+ referenced_column_names
+ FROM duckdb_constraints()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ ORDER BY table_name, constraint_index;
+
+ -- name: by_table
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ table_name,
+ constraint_name,
+ constraint_type,
+ constraint_text,
+ expression,
+ constraint_column_names,
+ referenced_table,
+ referenced_column_names
+ FROM duckdb_constraints()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND table_name = :table_name
+ ORDER BY constraint_index;
+ ```
+
+??? code "`databases.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ path,
+ type,
+ readonly,
+ internal
+ FROM duckdb_databases()
+ WHERE NOT internal
+ ORDER BY database_name;
+ ```
+
+??? code "`ddl.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_object
+ -- dialect: duckdb
+ WITH fk_tables AS (
+ SELECT DISTINCT
+ database_name,
+ schema_name,
+ table_name
+ FROM duckdb_constraints()
+ WHERE database_name = current_database()
+ AND constraint_type = 'FOREIGN KEY'
+ ),
+ ddl_objects AS (
+ SELECT
+ t.schema_name,
+ t.table_name AS object_name,
+ CASE WHEN t.temporary THEN 'temporary_table' ELSE 'table' END AS object_type,
+ t.table_name AS table_name,
+ t.sql,
+ fk.table_name IS NOT NULL AS has_foreign_keys
+ FROM duckdb_tables() AS t
+ LEFT JOIN fk_tables AS fk
+ ON fk.database_name = t.database_name
+ AND fk.schema_name = t.schema_name
+ AND fk.table_name = t.table_name
+ WHERE NOT t.internal
+ AND t.database_name = current_database()
+ AND t.sql IS NOT NULL
+ UNION ALL
+ SELECT
+ schema_name,
+ view_name AS object_name,
+ 'view' AS object_type,
+ NULL AS table_name,
+ sql,
+ FALSE AS has_foreign_keys
+ FROM duckdb_views()
+ WHERE NOT internal
+ AND database_name = current_database()
+ AND sql IS NOT NULL
+ UNION ALL
+ SELECT
+ schema_name,
+ index_name AS object_name,
+ 'index' AS object_type,
+ table_name AS table_name,
+ sql,
+ FALSE AS has_foreign_keys
+ FROM duckdb_indexes()
+ WHERE database_name = current_database()
+ AND sql IS NOT NULL
+ UNION ALL
+ SELECT
+ schema_name,
+ sequence_name AS object_name,
+ 'sequence' AS object_type,
+ NULL AS table_name,
+ 'CREATE SEQUENCE '
+ || CASE WHEN schema_name = 'main' THEN '' ELSE '"' || replace(schema_name, '"', '""') || '".' END
+ || '"' || replace(sequence_name, '"', '""') || '"'
+ || ' INCREMENT BY ' || increment_by
+ || ' MINVALUE ' || min_value
+ || ' MAXVALUE ' || max_value
+ || ' START WITH ' || start_value
+ || CASE WHEN cycle THEN ' CYCLE' ELSE ' NO CYCLE' END
+ || ';' AS sql,
+ FALSE AS has_foreign_keys
+ FROM duckdb_sequences()
+ WHERE database_name = current_database()
+ UNION ALL
+ SELECT
+ schema_name,
+ type_name AS object_name,
+ 'type' AS object_type,
+ NULL AS table_name,
+ CASE
+ WHEN logical_type = 'ENUM' AND labels IS NOT NULL THEN
+ 'CREATE TYPE '
+ || CASE WHEN schema_name = 'main' THEN '' ELSE '"' || replace(schema_name, '"', '""') || '".' END
+ || '"' || replace(type_name, '"', '""') || '" AS ENUM ('
+ || array_to_string(
+ list_transform(labels, lambda x: '''' || replace(x, '''', '''''') || ''''),
+ ', '
+ ) || ');'
+ ELSE NULL
+ END AS sql,
+ FALSE AS has_foreign_keys
+ FROM duckdb_types()
+ WHERE NOT internal
+ AND database_name = current_database()
+ AND logical_type = 'ENUM'
+ AND labels IS NOT NULL
+ )
+ SELECT
+ schema_name,
+ object_name,
+ object_type,
+ table_name,
+ sql AS ddl,
+ has_foreign_keys,
+ 'native' AS fidelity
+ FROM ddl_objects
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND object_name = :object_name
+ AND sql IS NOT NULL
+ AND (:object_type IS NULL OR object_type = LOWER(:object_type))
+ ORDER BY schema_name, object_type, object_name;
+
+ -- name: by_schema
+ -- dialect: duckdb
+ WITH fk_tables AS (
+ SELECT DISTINCT
+ database_name,
+ schema_name,
+ table_name
+ FROM duckdb_constraints()
+ WHERE database_name = current_database()
+ AND constraint_type = 'FOREIGN KEY'
+ ),
+ ddl_objects AS (
+ SELECT
+ t.schema_name,
+ t.table_name AS object_name,
+ CASE WHEN t.temporary THEN 'temporary_table' ELSE 'table' END AS object_type,
+ t.table_name AS table_name,
+ t.sql,
+ fk.table_name IS NOT NULL AS has_foreign_keys
+ FROM duckdb_tables() AS t
+ LEFT JOIN fk_tables AS fk
+ ON fk.database_name = t.database_name
+ AND fk.schema_name = t.schema_name
+ AND fk.table_name = t.table_name
+ WHERE NOT t.internal
+ AND t.database_name = current_database()
+ AND t.sql IS NOT NULL
+ UNION ALL
+ SELECT
+ schema_name,
+ view_name AS object_name,
+ 'view' AS object_type,
+ NULL AS table_name,
+ sql,
+ FALSE AS has_foreign_keys
+ FROM duckdb_views()
+ WHERE NOT internal
+ AND database_name = current_database()
+ AND sql IS NOT NULL
+ UNION ALL
+ SELECT
+ schema_name,
+ index_name AS object_name,
+ 'index' AS object_type,
+ table_name AS table_name,
+ sql,
+ FALSE AS has_foreign_keys
+ FROM duckdb_indexes()
+ WHERE database_name = current_database()
+ AND sql IS NOT NULL
+ UNION ALL
+ SELECT
+ schema_name,
+ sequence_name AS object_name,
+ 'sequence' AS object_type,
+ NULL AS table_name,
+ 'CREATE SEQUENCE '
+ || CASE WHEN schema_name = 'main' THEN '' ELSE '"' || replace(schema_name, '"', '""') || '".' END
+ || '"' || replace(sequence_name, '"', '""') || '"'
+ || ' INCREMENT BY ' || increment_by
+ || ' MINVALUE ' || min_value
+ || ' MAXVALUE ' || max_value
+ || ' START WITH ' || start_value
+ || CASE WHEN cycle THEN ' CYCLE' ELSE ' NO CYCLE' END
+ || ';' AS sql,
+ FALSE AS has_foreign_keys
+ FROM duckdb_sequences()
+ WHERE database_name = current_database()
+ UNION ALL
+ SELECT
+ schema_name,
+ type_name AS object_name,
+ 'type' AS object_type,
+ NULL AS table_name,
+ CASE
+ WHEN logical_type = 'ENUM' AND labels IS NOT NULL THEN
+ 'CREATE TYPE '
+ || CASE WHEN schema_name = 'main' THEN '' ELSE '"' || replace(schema_name, '"', '""') || '".' END
+ || '"' || replace(type_name, '"', '""') || '" AS ENUM ('
+ || array_to_string(
+ list_transform(labels, lambda x: '''' || replace(x, '''', '''''') || ''''),
+ ', '
+ ) || ');'
+ ELSE NULL
+ END AS sql,
+ FALSE AS has_foreign_keys
+ FROM duckdb_types()
+ WHERE NOT internal
+ AND database_name = current_database()
+ AND logical_type = 'ENUM'
+ AND labels IS NOT NULL
+ )
+ SELECT
+ schema_name,
+ object_name,
+ object_type,
+ table_name,
+ sql AS ddl,
+ has_foreign_keys,
+ 'native' AS fidelity
+ FROM ddl_objects
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND sql IS NOT NULL
+ ORDER BY
+ CASE object_type
+ WHEN 'type' THEN 1
+ WHEN 'sequence' THEN 2
+ WHEN 'table' THEN 3
+ WHEN 'temporary_table' THEN 4
+ WHEN 'view' THEN 5
+ WHEN 'index' THEN 6
+ ELSE 7
+ END,
+ object_name;
+ ```
+
+??? code "`dependencies.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ WITH object_inventory AS (
+ SELECT
+ database_name,
+ schema_name,
+ table_oid AS object_oid,
+ table_name AS object_name,
+ 'table' AS object_type
+ FROM duckdb_tables()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ UNION ALL
+ SELECT
+ database_name,
+ schema_name,
+ index_oid AS object_oid,
+ index_name AS object_name,
+ 'index' AS object_type
+ FROM duckdb_indexes()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ UNION ALL
+ SELECT
+ database_name,
+ schema_name,
+ view_oid AS object_oid,
+ view_name AS object_name,
+ 'view' AS object_type
+ FROM duckdb_views()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ )
+ SELECT
+ dependent.database_name,
+ dependent.schema_name,
+ dependent.object_name,
+ dependent.object_type,
+ referenced.object_name AS referenced_object_name,
+ referenced.object_type AS referenced_object_type,
+ dep.classid,
+ dep.objid,
+ dep.objsubid,
+ dep.refclassid,
+ dep.refobjid,
+ dep.refobjsubid,
+ dep.deptype
+ FROM duckdb_dependencies() AS dep
+ LEFT JOIN object_inventory AS dependent ON dependent.object_oid = dep.objid
+ LEFT JOIN object_inventory AS referenced ON referenced.object_oid = dep.refobjid
+ WHERE dependent.object_oid IS NOT NULL
+ OR referenced.object_oid IS NOT NULL
+ ORDER BY dependent.object_type, dependent.object_name, referenced.object_type, referenced.object_name;
+ ```
+
+??? code "`extensions.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: duckdb
+ SELECT
+ extension_name,
+ loaded,
+ installed,
+ install_path,
+ description,
+ aliases,
+ extension_version,
+ install_mode,
+ installed_from
+ FROM duckdb_extensions()
+ ORDER BY extension_name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: duckdb
+ SELECT
+ kcu.table_name,
+ kcu.column_name,
+ pk_kcu.table_name AS referenced_table_name,
+ pk_kcu.column_name AS referenced_column_name,
+ kcu.constraint_name,
+ kcu.table_schema,
+ pk_kcu.table_schema AS referenced_table_schema,
+ rc.delete_rule,
+ rc.update_rule
+ FROM information_schema.key_column_usage kcu
+ JOIN information_schema.referential_constraints rc
+ ON kcu.constraint_name = rc.constraint_name
+ AND kcu.constraint_schema = rc.constraint_schema
+ JOIN information_schema.key_column_usage pk_kcu
+ ON rc.unique_constraint_name = pk_kcu.constraint_name
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ AND kcu.ordinal_position = pk_kcu.ordinal_position
+ WHERE kcu.table_schema = COALESCE(:schema_name, current_schema())
+ AND kcu.table_name = :table_name
+ ORDER BY kcu.table_name, kcu.constraint_name, kcu.ordinal_position;
+
+ -- name: by_schema
+ -- dialect: duckdb
+ SELECT
+ kcu.table_name,
+ kcu.column_name,
+ pk_kcu.table_name AS referenced_table_name,
+ pk_kcu.column_name AS referenced_column_name,
+ kcu.constraint_name,
+ kcu.table_schema,
+ pk_kcu.table_schema AS referenced_table_schema,
+ rc.delete_rule,
+ rc.update_rule
+ FROM information_schema.key_column_usage kcu
+ JOIN information_schema.referential_constraints rc
+ ON kcu.constraint_name = rc.constraint_name
+ AND kcu.constraint_schema = rc.constraint_schema
+ JOIN information_schema.key_column_usage pk_kcu
+ ON rc.unique_constraint_name = pk_kcu.constraint_name
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ AND kcu.ordinal_position = pk_kcu.ordinal_position
+ WHERE kcu.table_schema = COALESCE(:schema_name, current_schema())
+ ORDER BY kcu.table_name, kcu.constraint_name, kcu.ordinal_position;
+ ```
+
+??? code "`functions.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ function_name,
+ function_type,
+ return_type,
+ parameters,
+ parameter_types,
+ macro_definition,
+ has_side_effects,
+ internal,
+ comment,
+ tags
+ FROM duckdb_functions()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ ORDER BY function_type, function_name;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ table_name,
+ index_name,
+ is_unique,
+ is_primary,
+ expressions AS columns,
+ comment,
+ tags,
+ sql AS native_sql
+ FROM duckdb_indexes()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ ORDER BY table_name, index_name;
+
+ -- name: by_table
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ table_name,
+ index_name,
+ is_unique,
+ is_primary,
+ expressions AS columns,
+ comment,
+ tags,
+ sql AS native_sql
+ FROM duckdb_indexes()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND table_name = :table_name
+ ORDER BY index_name;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ table_name AS object_name,
+ 'table' AS object_type,
+ comment,
+ tags,
+ internal,
+ temporary,
+ sql AS native_sql
+ FROM duckdb_tables()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND NOT internal
+ UNION ALL
+ SELECT
+ database_name,
+ schema_name,
+ view_name AS object_name,
+ 'view' AS object_type,
+ comment,
+ tags,
+ internal,
+ temporary,
+ sql AS native_sql
+ FROM duckdb_views()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND NOT internal
+ ORDER BY object_type, object_name;
+ ```
+
+??? code "`schemas.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ internal,
+ sql AS native_sql
+ FROM duckdb_schemas()
+ WHERE NOT internal
+ ORDER BY database_name, schema_name;
+ ```
+
+??? code "`sequences.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ sequence_name,
+ temporary,
+ start_value,
+ min_value,
+ max_value,
+ increment_by,
+ cycle,
+ last_value,
+ comment,
+ tags,
+ sql AS native_sql
+ FROM duckdb_sequences()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ ORDER BY sequence_name;
+ ```
+
+??? code "`system.sql` (3 named queries)"
+
+ ```sql
+ -- name: logs
+ -- dialect: duckdb
+ SELECT *
+ FROM duckdb_logs();
+
+ -- name: memory
+ -- dialect: duckdb
+ SELECT *
+ FROM duckdb_memory();
+
+ -- name: settings
+ -- dialect: duckdb
+ SELECT
+ name,
+ value,
+ description,
+ input_type,
+ scope,
+ aliases
+ FROM duckdb_settings()
+ ORDER BY name;
+ ```
+
+??? code "`tables.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ WITH RECURSIVE dependency_tree AS (
+ SELECT
+ t.table_name,
+ 0 AS level,
+ [t.table_name] AS path
+ FROM information_schema.tables t
+ WHERE t.table_type = 'BASE TABLE'
+ AND t.table_schema = COALESCE(:schema_name, current_schema())
+ AND NOT EXISTS (
+ SELECT 1
+ FROM information_schema.key_column_usage kcu
+ JOIN information_schema.referential_constraints rc
+ ON kcu.constraint_name = rc.constraint_name
+ AND kcu.constraint_schema = rc.constraint_schema
+ JOIN information_schema.key_column_usage pk_kcu
+ ON rc.unique_constraint_name = pk_kcu.constraint_name
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ WHERE kcu.table_name = t.table_name
+ AND kcu.table_schema = t.table_schema
+ AND pk_kcu.table_name <> t.table_name
+ )
+
+ UNION ALL
+
+ SELECT
+ kcu.table_name,
+ dt.level + 1,
+ list_append(dt.path, kcu.table_name)
+ FROM information_schema.key_column_usage kcu
+ JOIN information_schema.referential_constraints rc
+ ON kcu.constraint_name = rc.constraint_name
+ AND kcu.constraint_schema = rc.constraint_schema
+ JOIN information_schema.key_column_usage pk_kcu
+ ON rc.unique_constraint_name = pk_kcu.constraint_name
+ AND rc.unique_constraint_schema = pk_kcu.constraint_schema
+ JOIN dependency_tree dt ON dt.table_name = pk_kcu.table_name
+ WHERE kcu.table_schema = COALESCE(:schema_name, current_schema())
+ AND pk_kcu.table_name <> kcu.table_name
+ AND NOT list_contains(dt.path, kcu.table_name)
+ ),
+ dependency_levels AS (
+ SELECT
+ table_name,
+ MAX(level) AS dependency_level
+ FROM dependency_tree
+ GROUP BY table_name
+ )
+ SELECT
+ t.table_name,
+ COALESCE(dl.dependency_level, 0) AS level,
+ COALESCE(dl.dependency_level, 0) AS dependency_level
+ FROM information_schema.tables t
+ LEFT JOIN dependency_levels dl ON dl.table_name = t.table_name
+ WHERE t.table_type = 'BASE TABLE'
+ AND t.table_schema = COALESCE(:schema_name, current_schema())
+ ORDER BY level, t.table_name;
+ ```
+
+??? code "`types.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ type_name,
+ logical_type,
+ labels
+ FROM duckdb_types()
+ WHERE NOT internal
+ AND schema_name = COALESCE(:schema_name, current_schema())
+ ORDER BY type_name;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: duckdb
+ SELECT version() AS version;
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: duckdb
+ SELECT
+ database_name,
+ schema_name,
+ view_name,
+ comment,
+ tags,
+ internal,
+ temporary,
+ sql AS native_sql
+ FROM duckdb_views()
+ WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND NOT internal
+ ORDER BY view_name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/index.md b/docs/reference/data-dictionary/dialects/index.md
new file mode 100644
index 000000000..1f462c2da
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/index.md
@@ -0,0 +1,66 @@
+# Dialect SQL Catalogs
+
+```python
+from sqlspec.data_dictionary import DialectConfig, get_data_dictionary_loader, get_dialect_config
+```
+
+SQLSpec ships 13 dialect-specific SQL catalog packs under `sqlspec/data_dictionary/dialects/` containing named [`SQLFileLoader`](../../loader.md) queries (`-- name: ...`), version-gated feature profiles (`DialectConfig`), and logical-to-physical type mappings. See the [Data Dictionary Guide](../../../learn/data-dictionary.md) and [Data Dictionary Reference](../../data-dictionary.md) for programmatic usage.
+
+## Dialect Catalog Matrix
+
+| Dialect | Default Schema | DDL Resolution | Domain `.sql` Files | Named Queries | Reference |
+| --- | --- | --- | --- | --- | --- |
+| **PostgreSQL** (`postgres`) | `public` | AST synthesis + `pg_get_*def` catalog functions | `21` | `26` | [PostgreSQL Catalog](postgres.md) |
+| **CockroachDB** (`cockroachdb`) | `public` | Native `crdb_internal.create_statement` + AST fallback | `15` | `18` | [CockroachDB Catalog](cockroachdb.md) |
+| **SQLite** (`sqlite`) | `None` | Native `sqlite_master.sql` + `PRAGMA` synthesis | `15` | `23` | [SQLite Catalog](sqlite.md) |
+| **DuckDB** (`duckdb`) | `None` | Native `duckdb_*().sql` catalog functions + AST synthesis | `17` | `24` | [DuckDB Catalog](duckdb.md) |
+| **MySQL** (`mysql`) | `None` | Native `SHOW CREATE` + `information_schema` synthesis | `18` | `27` | [MySQL Catalog](mysql.md) |
+| **MariaDB** (`mariadb`) | `None` | Native `SHOW CREATE` + MariaDB sequence & constraint reflection | `17` | `27` | [MariaDB Catalog](mariadb.md) |
+| **Oracle Database** (`oracle`) | `None` | Native `DBMS_METADATA.GET_DDL` + AST fallback | `22` | `35` | [Oracle Database Catalog](oracle.md) |
+| **SQL Server** (`mssql`) | `dbo` | AST synthesis + `OBJECT_DEFINITION()` for views/routines/triggers | `19` | `31` | [SQL Server Catalog](mssql.md) |
+| **Sybase ASE** (`sybase`) | `dbo` | AST synthesis + `dbo.syscomments` object text reassembly | `9` | `16` | [Sybase ASE Catalog](sybase.md) |
+| **SAP SQL Anywhere** (`sqlanywhere`) | `DBA` | AST synthesis + `SYS.SYSTAB` / `SYS.SYSPROCEDURE` definitions | `9` | `16` | [SAP SQL Anywhere Catalog](sqlanywhere.md) |
+| **IBM Db2** (`db2`) | `None` | AST synthesis + `SYSCAT` catalog text definitions | `9` | `16` | [IBM Db2 Catalog](db2.md) |
+| **Google BigQuery** (`bigquery`) | `None` | Native `INFORMATION_SCHEMA.TABLES.ddl` + AST fallback | `14` | `30` | [Google BigQuery Catalog](bigquery.md) |
+| **Google Cloud Spanner** (`spanner`) | `None` | AST synthesis with `INTERLEAVE IN PARENT` & `ROW DELETION POLICY` (`GoogleSQL` & `PostgreSQL` modes) | `5` | `8` | [Google Cloud Spanner Catalog](spanner.md) |
+| **Total (`13` dialects)** | - | - | **`190`** | **`297`** | - |
+
+## Cross-Dialect Capability Matrix
+
+Each dialect pack declares boolean `feature_flags` and version-gated `feature_versions` inside its `DialectConfig`:
+
+| Dialect | Schemas | Returning | Upsert | Window | CTE | JSON | Arrays | Vectors | Transactions |
+| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |
+| [PostgreSQL](postgres.md) | `Yes` | `>=8.2.0` | `>=9.5.0` | `>=8.4.0` | `>=8.4.0` | `>=9.2.0` | `Yes` | `No` | `Yes` |
+| [CockroachDB](cockroachdb.md) | `Yes` | `>=20.1.0` | `>=19.2.0` | `>=19.1.0` | `>=19.1.0` | `>=20.1.0` | `Yes` | `No` | `Yes` |
+| [SQLite](sqlite.md) | `No` | `>=3.35.0` | `>=3.24.0` | `>=3.25.0` | `>=3.8.3` | `>=3.38.0` | `No` | `No` | `Yes` |
+| [DuckDB](duckdb.md) | `Yes` | `>=0.8.0` | `>=0.8.0` | `Yes` | `Yes` | `Yes` | `Yes` | `No` | `Yes` |
+| [MySQL](mysql.md) | `Yes` | `No` | `Yes` | `>=8.0.2` | `>=8.0.1` | `>=5.7.8` | `No` | `No` | `Yes` |
+| [MariaDB](mariadb.md) | `Yes` | `Yes` | `Yes` | `>=10.2.0` | `>=10.2.1` | `>=10.2.7` | `No` | `No` | `Yes` |
+| [Oracle Database](oracle.md) | `Yes` | `No` | `No` | `No` | `No` | `No` | `No` | `No` | `Yes` |
+| [SQL Server](mssql.md) | `Yes` | `No` | `No` | `No` | `No` | `No` | `No` | `No` | `Yes` |
+| [Sybase ASE](sybase.md) | `Yes` | `No` | `No` | `No` | `No` | `No` | `No` | `No` | `Yes` |
+| [SAP SQL Anywhere](sqlanywhere.md) | `Yes` | `No` | `Yes` | `>=10.0.0` | `>=10.0.0` | `No` | `No` | `No` | `Yes` |
+| [IBM Db2](db2.md) | `Yes` | `No` | `No` | `>=8.1.0` | `>=8.1.0` | `No` | `No` | `No` | `Yes` |
+| [Google BigQuery](bigquery.md) | `Yes` | `No` | `Yes` | `Yes` | `Yes` | `Yes` | `Yes` | `No` | `Yes` |
+| [Google Cloud Spanner](spanner.md) | `No` | `No` | `No` | `No` | `No` | `Yes` | `No` | `No` | `No` |
+
+## Logical-to-Physical Type Mappings
+
+`driver.data_dictionary.get_optimal_type(driver, type_category)` resolves portable logical type categories using each dialect's `DialectConfig.type_mappings` and active engine version:
+
+| Dialect | `json` | `uuid` | `boolean` | `timestamp` | `text` | `blob` | `vector` |
+| --- | --- | --- | --- | --- | --- | --- | --- |
+| [PostgreSQL](postgres.md) | `JSONB` | `UUID` | `BOOLEAN` | `TIMESTAMP WITH TIME ZONE` | `TEXT` | `BYTEA` | `-` |
+| [CockroachDB](cockroachdb.md) | `JSONB` | `UUID` | `BOOL` | `TIMESTAMPTZ` | `STRING` | `BYTES` | `-` |
+| [SQLite](sqlite.md) | `JSON` | `TEXT` | `INTEGER` | `TIMESTAMP` | `TEXT` | `BLOB` | `-` |
+| [DuckDB](duckdb.md) | `JSON` | `UUID` | `BOOLEAN` | `TIMESTAMP` | `TEXT` | `BLOB` | `-` |
+| [MySQL](mysql.md) | `JSON` | `VARCHAR(36)` | `TINYINT(1)` | `TIMESTAMP` | `TEXT` | `BLOB` | `-` |
+| [MariaDB](mariadb.md) | `JSON` | `VARCHAR(36)` | `TINYINT(1)` | `TIMESTAMP` | `TEXT` | `BLOB` | `-` |
+| [Oracle Database](oracle.md) | `JSON` | `RAW(16)` | `NUMBER(1)` | `TIMESTAMP` | `CLOB` | `BLOB` | `-` |
+| [SQL Server](mssql.md) | `NVARCHAR(MAX)` | `UNIQUEIDENTIFIER` | `BIT` | `DATETIME2(6)` | `NVARCHAR(MAX)` | `VARBINARY(MAX)` | `-` |
+| [Sybase ASE](sybase.md) | `TEXT` | `VARCHAR(36)` | `BIT` | `BIGDATETIME` | `TEXT` | `IMAGE` | `-` |
+| [SAP SQL Anywhere](sqlanywhere.md) | `LONG VARCHAR` | `UNIQUEIDENTIFIER` | `BIT` | `TIMESTAMP` | `LONG VARCHAR` | `LONG BINARY` | `-` |
+| [IBM Db2](db2.md) | `CLOB` | `VARCHAR(36)` | `BOOLEAN` | `TIMESTAMP` | `CLOB` | `BLOB` | `-` |
+| [Google BigQuery](bigquery.md) | `JSON` | `STRING` | `BOOL` | `TIMESTAMP` | `STRING` | `BYTES` | `-` |
+| [Google Cloud Spanner](spanner.md) | `JSON` | `BYTES(16)` | `BOOL` | `TIMESTAMP` | `STRING(MAX)` | `BYTES(MAX)` | `-` |
diff --git a/docs/reference/data-dictionary/dialects/mariadb.md b/docs/reference/data-dictionary/dialects/mariadb.md
new file mode 100644
index 000000000..bfe8aa0bb
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/mariadb.md
@@ -0,0 +1,629 @@
+# MariaDB Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("mariadb")
+```
+
+Extends MySQL `information_schema` catalog queries with MariaDB native `SEQUENCE` objects (`TABLE_TYPE = 'SEQUENCE'`), native `UUID` / `VECTOR` types, and MariaDB version detection. See the [MariaDB Guide](../../../databases/mysql.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `mariadb` |
+| **Default Schema** | `None` |
+| **Version Pattern** | `(\d+)\.(\d+)\.(\d+)` |
+| **DDL Strategy** | Native `SHOW CREATE` + MariaDB sequence & constraint reflection |
+| **Catalog `.sql` Files** | `17` files (`27` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `False` | `bigint` | `BIGINT` |
+| `supports_for_update` | `True` | `blob` | `BLOB` |
+| `supports_invisible_columns` | `False` | `boolean` | `TINYINT(1)` |
+| `supports_invisible_indexes` | `False` | `float` | `DOUBLE` |
+| `supports_on_conflict` | `False` | `integer` | `INT` |
+| `supports_prepared_statements` | `True` | `json` | `JSON` |
+| `supports_resource_groups` | `False` | `text` | `TEXT` |
+| `supports_returning` | `True` | `timestamp` | `TIMESTAMP` |
+| `supports_schemas` | `True` | `uuid` | `VARCHAR(36)` |
+| `supports_transactions` | `True` | `varchar` | `VARCHAR({length})` |
+| `supports_update_from` | `False` | - | - |
+| `supports_upsert` | `True` | - | - |
+| `supports_uuid` | `False` | - | - |
+| `supports_check_constraints` | `>= 10.2.1` | - | - |
+| `supports_cte` | `>= 10.2.1` | - | - |
+| `supports_events` | `>= 5.1.6` | - | - |
+| `supports_generated_columns` | `>= 10.2.0` | - | - |
+| `supports_json` | `>= 10.2.7` | - | - |
+| `supports_roles` | `>= 10.0.5` | - | - |
+| `supports_sequences` | `>= 10.3.0` | - | - |
+| `supports_skip_locked` | `>= 10.6.0` | - | - |
+| `supports_system_versioned_tables` | `>= 10.3.0` | - | - |
+| `supports_window_functions` | `>= 10.2.0` | - | - |
+
+## Catalog SQL Queries
+
+The `mariadb` dialect pack defines `27` named queries across `17` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/mariadb/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `constraints.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `ddl.sql` | `event_by_name`, `function_by_name`, `procedure_by_name`, `sequence_by_name`, `table_by_name`, `trigger_by_name`, `view_by_name` | *none* |
+| `events.sql` | `by_schema` | `:schema_name` |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `objects.sql` | `by_schema` | `:schema_name` |
+| `partitions.sql` | `by_schema` | `:schema_name` |
+| `plugins.sql` | `list` | *none* |
+| `privileges.sql` | `by_schema` | `:schema_name` |
+| `routines.sql` | `by_schema` | `:schema_name` |
+| `schemas.sql` | `list` | `:schema_name` |
+| `system.sql` | `performance_schema_tables` | *none* |
+| `tables.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `triggers.sql` | `by_schema` | `:schema_name` |
+| `version.sql` | `current`, `version` | *none* |
+| `views.sql` | `by_schema` | `:schema_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS schema_name,
+ table_name,
+ column_name,
+ ordinal_position,
+ column_default,
+ is_nullable,
+ data_type,
+ column_type,
+ character_maximum_length AS max_length,
+ numeric_precision,
+ numeric_scale,
+ character_set_name,
+ collation_name,
+ column_key,
+ column_key = 'PRI' AS is_primary,
+ extra,
+ extra LIKE '%auto_increment%' AS is_identity,
+ generation_expression,
+ CASE WHEN generation_expression IS NOT NULL AND generation_expression <> '' THEN 1 ELSE 0 END AS is_generated,
+ column_comment
+ FROM information_schema.columns
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY table_schema, table_name, ordinal_position;
+
+ -- name: by_table
+ -- dialect: mysql
+ SELECT
+ column_name AS `column_name`,
+ data_type AS `data_type`,
+ column_type AS `column_type`,
+ is_nullable AS `is_nullable`,
+ column_default AS `column_default`,
+ column_key AS `column_key`,
+ column_key = 'PRI' AS `is_primary`,
+ extra AS `extra`,
+ extra LIKE '%auto_increment%' AS `is_identity`,
+ generation_expression AS `generation_expression`,
+ CASE WHEN generation_expression IS NOT NULL AND generation_expression <> '' THEN 1 ELSE 0 END AS `is_generated`
+ FROM information_schema.columns
+ WHERE table_name = :table_name
+ AND table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY ordinal_position;
+ ```
+
+??? code "`constraints.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ tc.constraint_schema AS schema_name,
+ tc.table_name,
+ tc.constraint_name,
+ tc.constraint_type,
+ kcu.column_name,
+ kcu.ordinal_position,
+ kcu.referenced_table_schema,
+ kcu.referenced_table_name,
+ kcu.referenced_column_name,
+ rc.update_rule,
+ rc.delete_rule,
+ cc.check_clause
+ FROM information_schema.table_constraints tc
+ LEFT JOIN information_schema.key_column_usage kcu
+ ON kcu.constraint_schema = tc.constraint_schema
+ AND kcu.constraint_name = tc.constraint_name
+ AND kcu.table_schema = tc.table_schema
+ AND kcu.table_name = tc.table_name
+ LEFT JOIN information_schema.referential_constraints rc
+ ON rc.constraint_schema = tc.constraint_schema
+ AND rc.constraint_name = tc.constraint_name
+ LEFT JOIN information_schema.check_constraints cc
+ ON cc.constraint_schema = tc.constraint_schema
+ AND cc.constraint_name = tc.constraint_name
+ WHERE tc.table_schema = COALESCE(:schema_name, DATABASE())
+ AND (:table_name IS NULL OR tc.table_name = :table_name)
+ ORDER BY tc.table_schema, tc.table_name, tc.constraint_name, kcu.ordinal_position;
+ ```
+
+??? code "`ddl.sql` (7 named queries)"
+
+ ```sql
+ -- name: event_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE EVENT' AS statement_template;
+
+ -- name: function_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE FUNCTION' AS statement_template;
+
+ -- name: procedure_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE PROCEDURE' AS statement_template;
+
+ -- name: sequence_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE SEQUENCE' AS statement_template;
+
+ -- name: table_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE TABLE' AS statement_template;
+
+ -- name: trigger_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE TRIGGER' AS statement_template;
+
+ -- name: view_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE VIEW' AS statement_template;
+ ```
+
+??? code "`events.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ event_schema AS schema_name,
+ event_name,
+ definer,
+ time_zone,
+ event_body,
+ event_definition,
+ event_type,
+ execute_at,
+ interval_value,
+ interval_field,
+ sql_mode,
+ starts,
+ ends,
+ status,
+ on_completion,
+ created,
+ last_altered,
+ last_executed,
+ event_comment,
+ originator,
+ character_set_client,
+ collation_connection,
+ database_collation
+ FROM information_schema.events
+ WHERE event_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY event_schema, event_name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: mariadb
+ SELECT
+ kcu.table_name AS `table_name`,
+ kcu.column_name AS `column_name`,
+ kcu.referenced_table_name AS `referenced_table`,
+ kcu.referenced_column_name AS `referenced_column`,
+ kcu.constraint_name AS `constraint_name`,
+ kcu.table_schema AS `schema`,
+ kcu.referenced_table_schema AS `referenced_schema`,
+ rc.delete_rule AS `on_delete`,
+ rc.update_rule AS `on_update`
+ FROM information_schema.key_column_usage AS kcu
+ JOIN information_schema.referential_constraints AS rc
+ ON rc.constraint_schema = kcu.constraint_schema
+ AND rc.constraint_name = kcu.constraint_name
+ AND rc.table_name = kcu.table_name
+ WHERE kcu.referenced_table_name IS NOT NULL
+ AND kcu.table_name = :table_name
+ AND kcu.table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY kcu.constraint_name, kcu.ordinal_position;
+
+ -- name: by_schema
+ -- dialect: mariadb
+ SELECT
+ kcu.table_name AS `table_name`,
+ kcu.column_name AS `column_name`,
+ kcu.referenced_table_name AS `referenced_table`,
+ kcu.referenced_column_name AS `referenced_column`,
+ kcu.constraint_name AS `constraint_name`,
+ kcu.table_schema AS `schema`,
+ kcu.referenced_table_schema AS `referenced_schema`,
+ rc.delete_rule AS `on_delete`,
+ rc.update_rule AS `on_update`
+ FROM information_schema.key_column_usage AS kcu
+ JOIN information_schema.referential_constraints AS rc
+ ON rc.constraint_schema = kcu.constraint_schema
+ AND rc.constraint_name = kcu.constraint_name
+ AND rc.table_name = kcu.table_name
+ WHERE kcu.referenced_table_name IS NOT NULL
+ AND kcu.table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY kcu.table_name, kcu.constraint_name, kcu.ordinal_position;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS schema_name,
+ table_name,
+ index_name,
+ non_unique = 0 AS is_unique,
+ index_name = 'PRIMARY' AS is_primary,
+ index_type,
+ ignored,
+ GROUP_CONCAT(column_name ORDER BY seq_in_index) AS columns,
+ GROUP_CONCAT(sub_part ORDER BY seq_in_index) AS prefix_lengths,
+ GROUP_CONCAT(collation ORDER BY seq_in_index) AS column_orders,
+ index_comment
+ FROM information_schema.statistics
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ AND (:table_name IS NULL OR table_name = :table_name)
+ GROUP BY table_schema, table_name, index_name, non_unique, index_type, ignored, index_comment
+ ORDER BY table_schema, table_name, index_name;
+
+ -- name: by_table
+ -- dialect: mysql
+ SELECT
+ s.index_name AS index_name,
+ s.table_name AS table_name,
+ CASE WHEN s.non_unique = 0 THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN s.index_name = 'PRIMARY' THEN 1 ELSE 0 END AS is_primary,
+ GROUP_CONCAT(s.column_name ORDER BY s.seq_in_index) AS columns
+ FROM information_schema.statistics s
+ WHERE s.table_schema = COALESCE(:schema_name, DATABASE())
+ AND s.table_name = :table_name
+ GROUP BY s.index_name, s.table_name, s.non_unique
+ ORDER BY s.index_name;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS object_schema,
+ table_name AS object_name,
+ CASE
+ WHEN table_type = 'SEQUENCE' THEN 'SEQUENCE'
+ ELSE table_type
+ END AS object_type,
+ engine,
+ table_comment AS comment
+ FROM information_schema.tables
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ sequence_schema AS object_schema,
+ sequence_name AS object_name,
+ 'SEQUENCE' AS object_type,
+ NULL AS engine,
+ NULL AS comment
+ FROM information_schema.sequences
+ WHERE sequence_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ routine_schema AS object_schema,
+ routine_name AS object_name,
+ routine_type AS object_type,
+ NULL AS engine,
+ routine_comment AS comment
+ FROM information_schema.routines
+ WHERE routine_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ trigger_schema AS object_schema,
+ trigger_name AS object_name,
+ 'TRIGGER' AS object_type,
+ NULL AS engine,
+ NULL AS comment
+ FROM information_schema.triggers
+ WHERE trigger_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ event_schema AS object_schema,
+ event_name AS object_name,
+ 'EVENT' AS object_type,
+ NULL AS engine,
+ event_comment AS comment
+ FROM information_schema.events
+ WHERE event_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY object_schema, object_name, object_type;
+ ```
+
+??? code "`partitions.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS schema_name,
+ table_name,
+ partition_name,
+ subpartition_name,
+ partition_ordinal_position,
+ subpartition_ordinal_position,
+ partition_method,
+ subpartition_method,
+ partition_expression,
+ subpartition_expression,
+ partition_description,
+ table_rows,
+ avg_row_length,
+ data_length,
+ index_length,
+ create_time,
+ update_time,
+ check_time,
+ check_sum,
+ partition_comment
+ FROM information_schema.partitions
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ AND partition_name IS NOT NULL
+ ORDER BY table_schema, table_name, partition_ordinal_position, subpartition_ordinal_position;
+ ```
+
+??? code "`plugins.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: mysql
+ SELECT
+ plugin_name,
+ plugin_version,
+ plugin_status,
+ plugin_type,
+ plugin_type_version,
+ plugin_library,
+ plugin_library_version,
+ plugin_author,
+ plugin_description,
+ plugin_license,
+ load_option
+ FROM information_schema.plugins
+ ORDER BY plugin_type, plugin_name;
+ ```
+
+??? code "`privileges.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ grantee,
+ table_catalog,
+ table_schema AS schema_name,
+ table_name,
+ privilege_type,
+ is_grantable,
+ NULL AS column_name
+ FROM information_schema.table_privileges
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ grantee,
+ table_catalog,
+ table_schema AS schema_name,
+ table_name,
+ privilege_type,
+ is_grantable,
+ column_name
+ FROM information_schema.column_privileges
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY schema_name, table_name, column_name, grantee, privilege_type;
+ ```
+
+??? code "`routines.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ routine_schema AS schema_name,
+ routine_name,
+ routine_type,
+ data_type,
+ routine_definition,
+ external_language,
+ is_deterministic,
+ sql_data_access,
+ security_type,
+ created,
+ last_altered,
+ definer,
+ character_set_client,
+ collation_connection,
+ database_collation
+ FROM information_schema.routines
+ WHERE routine_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY routine_schema, routine_name;
+ ```
+
+??? code "`schemas.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: mysql
+ SELECT
+ schema_name,
+ default_character_set_name,
+ default_collation_name,
+ sql_path
+ FROM information_schema.schemata
+ WHERE :schema_name IS NULL OR schema_name = :schema_name
+ ORDER BY schema_name;
+ ```
+
+??? code "`system.sql` (1 named queries)"
+
+ ```sql
+ -- name: performance_schema_tables
+ -- dialect: mysql
+ SELECT
+ table_schema,
+ table_name,
+ table_rows,
+ avg_row_length,
+ data_length,
+ index_length
+ FROM information_schema.tables
+ WHERE table_schema = 'performance_schema'
+ ORDER BY table_name;
+ ```
+
+??? code "`tables.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ WITH RECURSIVE dependency_tree AS (
+ SELECT
+ table_name,
+ 0 AS level,
+ CAST(table_name AS CHAR(4000)) AS path
+ FROM information_schema.tables AS root_table
+ WHERE root_table.table_type = 'BASE TABLE'
+ AND root_table.table_schema = COALESCE(:schema_name, DATABASE())
+ AND NOT EXISTS (
+ SELECT 1
+ FROM information_schema.key_column_usage AS root_key
+ WHERE root_key.table_name = root_table.table_name
+ AND root_key.table_schema = root_table.table_schema
+ AND root_key.referenced_table_name IS NOT NULL
+ AND root_key.referenced_table_name <> root_table.table_name
+ )
+
+ UNION ALL
+
+ SELECT
+ child_key.table_name,
+ dependency_tree.level + 1,
+ CONCAT(dependency_tree.path, ',', child_key.table_name)
+ FROM information_schema.key_column_usage AS child_key
+ JOIN dependency_tree ON child_key.referenced_table_name = dependency_tree.table_name
+ WHERE child_key.table_schema = COALESCE(:schema_name, DATABASE())
+ AND child_key.referenced_table_name IS NOT NULL
+ AND child_key.referenced_table_name <> child_key.table_name
+ AND NOT FIND_IN_SET(child_key.table_name, dependency_tree.path)
+ ), dependency_levels AS (
+ SELECT table_name, MAX(level) AS dependency_level
+ FROM dependency_tree
+ GROUP BY table_name
+ )
+ SELECT
+ catalog_table.table_schema AS schema_name,
+ CAST(catalog_table.table_name AS CHAR) AS table_name,
+ catalog_table.table_type AS table_type,
+ catalog_table.engine AS engine,
+ catalog_table.version AS version,
+ catalog_table.row_format AS row_format,
+ catalog_table.table_rows AS table_rows,
+ catalog_table.avg_row_length AS avg_row_length,
+ catalog_table.data_length AS data_length,
+ catalog_table.max_data_length AS max_data_length,
+ catalog_table.index_length AS index_length,
+ catalog_table.data_free AS data_free,
+ catalog_table.auto_increment AS auto_increment,
+ catalog_table.create_time AS create_time,
+ catalog_table.update_time AS update_time,
+ catalog_table.check_time AS check_time,
+ catalog_table.table_collation AS table_collation,
+ catalog_table.checksum AS checksum,
+ catalog_table.create_options AS create_options,
+ catalog_table.table_comment AS table_comment,
+ COALESCE(dependency_levels.dependency_level, 0) AS dependency_level,
+ COALESCE(dependency_levels.dependency_level, 0) AS level
+ FROM information_schema.tables AS catalog_table
+ LEFT JOIN dependency_levels ON dependency_levels.table_name = catalog_table.table_name
+ WHERE catalog_table.table_schema = COALESCE(:schema_name, DATABASE())
+ AND (:table_name IS NULL OR catalog_table.table_name = :table_name)
+ ORDER BY dependency_level, catalog_table.table_name;
+ ```
+
+??? code "`triggers.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ trigger_schema AS schema_name,
+ trigger_name,
+ event_manipulation,
+ event_object_schema,
+ event_object_table,
+ action_order,
+ action_condition,
+ action_statement,
+ action_orientation,
+ action_timing,
+ sql_mode,
+ definer,
+ character_set_client,
+ collation_connection,
+ database_collation
+ FROM information_schema.triggers
+ WHERE trigger_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY trigger_schema, event_object_table, action_timing, event_manipulation, action_order;
+ ```
+
+??? code "`version.sql` (2 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: mysql
+ SELECT VERSION() AS version;
+
+ -- name: version
+ -- dialect: mysql
+ SELECT VERSION() AS version;
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS schema_name,
+ table_name AS view_name,
+ view_definition,
+ check_option,
+ is_updatable,
+ definer,
+ security_type,
+ character_set_client,
+ collation_connection
+ FROM information_schema.views
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY table_schema, table_name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/mssql.md b/docs/reference/data-dictionary/dialects/mssql.md
new file mode 100644
index 000000000..4c5bccf3e
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/mssql.md
@@ -0,0 +1,981 @@
+# SQL Server Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("mssql")
+```
+
+Introspects `sys.tables`, `sys.columns`, `sys.types`, `sys.indexes`, `sys.foreign_keys`, `sys.check_constraints`, `sys.default_constraints`, `sys.sequences`, and `sys.sql_modules` across `mssql_python` and `pymssql`. See the [SQL Server Guide](../../../databases/mssql.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `mssql` |
+| **Default Schema** | `"dbo"` |
+| **Version Pattern** | `(\d+)` |
+| **DDL Strategy** | AST synthesis + `OBJECT_DEFINITION()` for views/routines/triggers |
+| **Catalog `.sql` Files** | `19` files (`31` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_for_update` | `False` | `bigint` | `BIGINT` |
+| `supports_in_memory` | `True` | `blob` | `VARBINARY(MAX)` |
+| `supports_on_conflict` | `False` | `boolean` | `BIT` |
+| `supports_prepared_statements` | `True` | `bytea` | `VARBINARY(MAX)` |
+| `supports_schemas` | `True` | `float` | `FLOAT` |
+| `supports_skip_locked` | `False` | `integer` | `INT` |
+| `supports_transactions` | `True` | `json` | `NVARCHAR(MAX)` |
+| `supports_update_from` | `True` | `jsonb` | `NVARCHAR(MAX)` |
+| - | - | `text` | `NVARCHAR(MAX)` |
+| - | - | `timestamp` | `DATETIME2(6)` |
+| - | - | `timestamptz` | `DATETIMEOFFSET(6)` |
+| - | - | `uuid` | `UNIQUEIDENTIFIER` |
+| - | - | `varchar` | `NVARCHAR({length})` |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.mssql_python.data_dictionary.MssqlPythonSyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.pymssql.data_dictionary.PymssqlSyncDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `mssql` dialect pack defines `31` named queries across `19` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/mssql/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `constraints.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `ddl.sql` | `index_inputs_by_table`, `table_inputs_by_table` | `:ddl_index_inputs`, `:ddl_table_inputs`, `:mssql`, `:schema_name`, `:table_name` |
+| `dependencies.sql` | `by_schema` | `:schema_name` |
+| `extended_properties.sql` | `by_schema` | `:schema_name` |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `modules.sql` | `by_schema` | `:schema_name` |
+| `objects.sql` | `by_schema` | `:schema_name` |
+| `permissions.sql` | `by_schema`, `role_members` | `:schema_name` |
+| `routines.sql` | `by_schema`, `parameters_by_schema` | `:schema_name` |
+| `schemas.sql` | `list`, `current` | *none* |
+| `sequences.sql` | `by_schema` | `:schema_name` |
+| `storage.sql` | `by_schema` | `:schema_name` |
+| `system.sql` | `dmv_exec_requests`, `query_store_runtime` | `:dmv_exec_requests`, `:mssql`, `:query_store_runtime`, `@SPID` |
+| `tables.sql` | `all_by_schema`, `by_schema`, `details_by_table`, `names_by_schema` | `:schema_name`, `:table_name` |
+| `triggers.sql` | `by_schema` | `:schema_name` |
+| `version.sql` | `current` | `@VERSION` |
+| `views.sql` | `by_schema` | `:schema_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ WITH pk_cols AS (
+ SELECT DISTINCT
+ idx_col.object_id,
+ idx_col.column_id
+ FROM sys.indexes AS idx
+ INNER JOIN sys.index_columns AS idx_col
+ ON idx.object_id = idx_col.object_id
+ AND idx.index_id = idx_col.index_id
+ WHERE idx.is_primary_key = 1
+ ),
+ unique_cols AS (
+ SELECT DISTINCT
+ idx_col.object_id,
+ idx_col.column_id
+ FROM sys.indexes AS idx
+ INNER JOIN sys.index_columns AS idx_col
+ ON idx.object_id = idx_col.object_id
+ AND idx.index_id = idx_col.index_id
+ WHERE idx.is_unique = 1
+ )
+ SELECT
+ s.name AS schema_name,
+ tab.name AS table_name,
+ c.column_id AS ordinal_position,
+ c.name AS column_name,
+ typ.name AS data_type,
+ CASE WHEN c.is_nullable = 1 THEN 'YES' ELSE 'NO' END AS is_nullable,
+ OBJECT_DEFINITION(c.default_object_id) AS column_default,
+ c.max_length,
+ c.precision AS numeric_precision,
+ c.scale AS numeric_scale,
+ CAST(CASE WHEN pk_cols.column_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_primary,
+ CAST(CASE WHEN unique_cols.column_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_unique,
+ c.collation_name,
+ CAST(c.is_sparse AS BIT) AS is_sparse,
+ CAST(c.is_column_set AS BIT) AS is_column_set,
+ CAST(c.is_hidden AS BIT) AS is_hidden,
+ CAST(c.is_masked AS BIT) AS is_masked,
+ c.encryption_type_desc,
+ c.encryption_algorithm_name,
+ CAST(CASE WHEN cc.object_id IS NOT NULL OR c.generated_always_type <> 0 THEN 1 ELSE 0 END AS BIT) AS is_generated,
+ cc.definition AS generation_expression,
+ CAST(cc.is_persisted AS BIT) AS is_persisted_computed,
+ CAST(CASE WHEN ic.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_identity,
+ CASE WHEN ic.object_id IS NOT NULL THEN 'ALWAYS' ELSE NULL END AS identity_generation,
+ CONVERT(BIGINT, ic.seed_value) AS identity_seed,
+ CONVERT(BIGINT, ic.increment_value) AS identity_increment
+ FROM sys.columns AS c
+ INNER JOIN sys.types AS typ ON c.user_type_id = typ.user_type_id
+ INNER JOIN sys.tables AS tab ON c.object_id = tab.object_id
+ INNER JOIN sys.schemas AS s ON tab.schema_id = s.schema_id
+ LEFT JOIN sys.computed_columns AS cc ON c.object_id = cc.object_id AND c.column_id = cc.column_id
+ LEFT JOIN sys.identity_columns AS ic ON c.object_id = ic.object_id AND c.column_id = ic.column_id
+ LEFT JOIN pk_cols ON pk_cols.object_id = tab.object_id AND pk_cols.column_id = c.column_id
+ LEFT JOIN unique_cols ON unique_cols.object_id = tab.object_id AND unique_cols.column_id = c.column_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ ORDER BY s.name, tab.name, c.column_id;
+
+ -- name: by_table
+ -- dialect: mssql
+ WITH pk_cols AS (
+ SELECT DISTINCT
+ idx_col.object_id,
+ idx_col.column_id
+ FROM sys.indexes AS idx
+ INNER JOIN sys.index_columns AS idx_col
+ ON idx.object_id = idx_col.object_id
+ AND idx.index_id = idx_col.index_id
+ WHERE idx.is_primary_key = 1
+ ),
+ unique_cols AS (
+ SELECT DISTINCT
+ idx_col.object_id,
+ idx_col.column_id
+ FROM sys.indexes AS idx
+ INNER JOIN sys.index_columns AS idx_col
+ ON idx.object_id = idx_col.object_id
+ AND idx.index_id = idx_col.index_id
+ WHERE idx.is_unique = 1
+ )
+ SELECT
+ s.name AS schema_name,
+ tab.name AS table_name,
+ c.column_id AS ordinal_position,
+ c.name AS column_name,
+ typ.name AS data_type,
+ CASE WHEN c.is_nullable = 1 THEN 'YES' ELSE 'NO' END AS is_nullable,
+ OBJECT_DEFINITION(c.default_object_id) AS column_default,
+ c.max_length,
+ c.precision AS numeric_precision,
+ c.scale AS numeric_scale,
+ CAST(CASE WHEN pk_cols.column_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_primary,
+ CAST(CASE WHEN unique_cols.column_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_unique,
+ c.collation_name,
+ CAST(c.is_sparse AS BIT) AS is_sparse,
+ CAST(c.is_column_set AS BIT) AS is_column_set,
+ CAST(c.is_hidden AS BIT) AS is_hidden,
+ CAST(c.is_masked AS BIT) AS is_masked,
+ c.encryption_type_desc,
+ c.encryption_algorithm_name,
+ CAST(CASE WHEN cc.object_id IS NOT NULL OR c.generated_always_type <> 0 THEN 1 ELSE 0 END AS BIT) AS is_generated,
+ cc.definition AS generation_expression,
+ CAST(cc.is_persisted AS BIT) AS is_persisted_computed,
+ CAST(CASE WHEN ic.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_identity,
+ CASE WHEN ic.object_id IS NOT NULL THEN 'ALWAYS' ELSE NULL END AS identity_generation,
+ CONVERT(BIGINT, ic.seed_value) AS identity_seed,
+ CONVERT(BIGINT, ic.increment_value) AS identity_increment
+ FROM sys.columns AS c
+ INNER JOIN sys.types AS typ ON c.user_type_id = typ.user_type_id
+ INNER JOIN sys.tables AS tab ON c.object_id = tab.object_id
+ INNER JOIN sys.schemas AS s ON tab.schema_id = s.schema_id
+ LEFT JOIN sys.computed_columns AS cc ON c.object_id = cc.object_id AND c.column_id = cc.column_id
+ LEFT JOIN sys.identity_columns AS ic ON c.object_id = ic.object_id AND c.column_id = ic.column_id
+ LEFT JOIN pk_cols ON pk_cols.object_id = tab.object_id AND pk_cols.column_id = c.column_id
+ LEFT JOIN unique_cols ON unique_cols.object_id = tab.object_id AND unique_cols.column_id = c.column_id
+ WHERE s.name = COALESCE(:schema_name, SCHEMA_NAME())
+ AND tab.name = :table_name
+ ORDER BY c.column_id;
+ ```
+
+??? code "`constraints.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ kc.name AS constraint_name,
+ kc.type_desc AS constraint_type,
+ CAST(kc.is_system_named AS BIT) AS is_system_named,
+ STUFF((
+ SELECT ',' + c2.name
+ FROM sys.index_columns AS ic2
+ INNER JOIN sys.columns AS c2 ON ic2.object_id = c2.object_id AND ic2.column_id = c2.column_id
+ WHERE ic2.object_id = kc.parent_object_id
+ AND ic2.index_id = kc.unique_index_id
+ AND ic2.is_included_column = 0
+ ORDER BY ic2.key_ordinal
+ FOR XML PATH(''), TYPE
+ ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS column_name,
+ NULL AS definition
+ FROM sys.key_constraints AS kc
+ INNER JOIN sys.tables AS t ON kc.parent_object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ UNION ALL
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ cc.name AS constraint_name,
+ 'CHECK_CONSTRAINT' AS constraint_type,
+ CAST(cc.is_system_named AS BIT) AS is_system_named,
+ NULL AS column_name,
+ cc.definition
+ FROM sys.check_constraints AS cc
+ INNER JOIN sys.tables AS t ON cc.parent_object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ UNION ALL
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ dc.name AS constraint_name,
+ 'DEFAULT_CONSTRAINT' AS constraint_type,
+ CAST(dc.is_system_named AS BIT) AS is_system_named,
+ c.name AS column_name,
+ dc.definition
+ FROM sys.default_constraints AS dc
+ INNER JOIN sys.tables AS t ON dc.parent_object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ INNER JOIN sys.columns AS c ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ ORDER BY schema_name, table_name, constraint_name;
+
+ -- name: by_table
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ kc.name AS constraint_name,
+ kc.type_desc AS constraint_type,
+ CAST(kc.is_system_named AS BIT) AS is_system_named,
+ STUFF((
+ SELECT ',' + c2.name
+ FROM sys.index_columns AS ic2
+ INNER JOIN sys.columns AS c2 ON ic2.object_id = c2.object_id AND ic2.column_id = c2.column_id
+ WHERE ic2.object_id = kc.parent_object_id
+ AND ic2.index_id = kc.unique_index_id
+ AND ic2.is_included_column = 0
+ ORDER BY ic2.key_ordinal
+ FOR XML PATH(''), TYPE
+ ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS column_name,
+ NULL AS definition
+ FROM sys.key_constraints AS kc
+ INNER JOIN sys.tables AS t ON kc.parent_object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ WHERE s.name = :schema_name
+ AND t.name = :table_name
+ UNION ALL
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ cc.name AS constraint_name,
+ 'CHECK_CONSTRAINT' AS constraint_type,
+ CAST(cc.is_system_named AS BIT) AS is_system_named,
+ NULL AS column_name,
+ cc.definition
+ FROM sys.check_constraints AS cc
+ INNER JOIN sys.tables AS t ON cc.parent_object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ WHERE s.name = :schema_name
+ AND t.name = :table_name
+ UNION ALL
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ dc.name AS constraint_name,
+ 'DEFAULT_CONSTRAINT' AS constraint_type,
+ CAST(dc.is_system_named AS BIT) AS is_system_named,
+ c.name AS column_name,
+ dc.definition
+ FROM sys.default_constraints AS dc
+ INNER JOIN sys.tables AS t ON dc.parent_object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ INNER JOIN sys.columns AS c ON dc.parent_object_id = c.object_id AND dc.parent_column_id = c.column_id
+ WHERE s.name = :schema_name
+ AND t.name = :table_name
+ ORDER BY schema_name, table_name, constraint_name;
+ ```
+
+??? code "`ddl.sql` (2 named queries)"
+
+ ```sql
+ -- name: index_inputs_by_table
+ -- dialect: mssql
+ /* sqlspec:mssql:ddl_index_inputs */
+ WITH index_key_columns AS (
+ SELECT
+ ic.object_id,
+ ic.index_id,
+ STRING_AGG(CAST(c.name AS NVARCHAR(MAX)), ',') WITHIN GROUP (ORDER BY ic.key_ordinal) AS columns
+ FROM sys.index_columns AS ic
+ INNER JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
+ WHERE ic.is_included_column = 0
+ GROUP BY ic.object_id, ic.index_id
+ ),
+ index_included_columns AS (
+ SELECT
+ ic.object_id,
+ ic.index_id,
+ STRING_AGG(CAST(c.name AS NVARCHAR(MAX)), ',') WITHIN GROUP (ORDER BY ic.index_column_id) AS included_columns
+ FROM sys.index_columns AS ic
+ INNER JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
+ WHERE ic.is_included_column = 1
+ GROUP BY ic.object_id, ic.index_id
+ )
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ i.name AS index_name,
+ i.type_desc,
+ CAST(i.is_unique AS BIT) AS is_unique,
+ CAST(i.has_filter AS BIT) AS has_filter,
+ i.filter_definition,
+ kc.columns AS columns,
+ inc.included_columns AS included_columns
+ FROM sys.indexes AS i
+ INNER JOIN sys.tables AS t ON i.object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ LEFT JOIN index_key_columns AS kc ON kc.object_id = i.object_id AND kc.index_id = i.index_id
+ LEFT JOIN index_included_columns AS inc ON inc.object_id = i.object_id AND inc.index_id = i.index_id
+ WHERE s.name = :schema_name
+ AND t.name = :table_name
+ AND i.name IS NOT NULL
+ AND i.is_primary_key = 0
+ AND i.is_unique_constraint = 0
+ ORDER BY i.name;
+
+ -- name: table_inputs_by_table
+ -- dialect: mssql
+ /* sqlspec:mssql:ddl_table_inputs */
+ WITH primary_key_columns AS (
+ SELECT
+ pk_col.object_id,
+ pk_col.column_id,
+ pk.name AS primary_key_name,
+ pk_col.key_ordinal AS primary_key_ordinal
+ FROM sys.indexes AS pk
+ INNER JOIN sys.index_columns AS pk_col
+ ON pk.object_id = pk_col.object_id
+ AND pk.index_id = pk_col.index_id
+ WHERE pk.is_primary_key = 1
+ AND pk_col.is_included_column = 0
+ )
+ SELECT
+ s.name AS schema_name,
+ tab.name AS table_name,
+ c.name AS column_name,
+ typ.name AS data_type,
+ c.max_length,
+ c.precision AS numeric_precision,
+ c.scale AS numeric_scale,
+ CAST(c.is_nullable AS BIT) AS is_nullable,
+ c.column_id AS ordinal_position,
+ OBJECT_DEFINITION(c.default_object_id) AS column_default,
+ CAST(CASE WHEN cc.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_computed,
+ cc.definition AS computed_definition,
+ CAST(CASE WHEN ic.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_identity,
+ CONVERT(BIGINT, ic.seed_value) AS identity_seed,
+ CONVERT(BIGINT, ic.increment_value) AS identity_increment,
+ pk.primary_key_name AS primary_key_name,
+ pk.primary_key_ordinal AS primary_key_ordinal
+ FROM sys.columns AS c
+ INNER JOIN sys.types AS typ ON c.user_type_id = typ.user_type_id
+ INNER JOIN sys.tables AS tab ON c.object_id = tab.object_id
+ INNER JOIN sys.schemas AS s ON tab.schema_id = s.schema_id
+ LEFT JOIN sys.computed_columns AS cc ON c.object_id = cc.object_id AND c.column_id = cc.column_id
+ LEFT JOIN sys.identity_columns AS ic ON c.object_id = ic.object_id AND c.column_id = ic.column_id
+ LEFT JOIN primary_key_columns AS pk ON c.object_id = pk.object_id AND c.column_id = pk.column_id
+ WHERE s.name = :schema_name
+ AND tab.name = :table_name
+ ORDER BY c.column_id;
+ ```
+
+??? code "`dependencies.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ OBJECT_SCHEMA_NAME(dep.referencing_id) AS referencing_schema_name,
+ OBJECT_NAME(dep.referencing_id) AS referencing_object_name,
+ dep.referencing_class_desc,
+ dep.referenced_server_name,
+ dep.referenced_database_name,
+ dep.referenced_schema_name,
+ dep.referenced_entity_name,
+ dep.referenced_class_desc,
+ CAST(dep.is_schema_bound_reference AS BIT) AS is_schema_bound_reference,
+ CAST(dep.is_caller_dependent AS BIT) AS is_caller_dependent,
+ CAST(dep.is_ambiguous AS BIT) AS is_ambiguous
+ FROM sys.sql_expression_dependencies AS dep
+ WHERE (:schema_name IS NULL OR OBJECT_SCHEMA_NAME(dep.referencing_id) = :schema_name)
+ ORDER BY OBJECT_SCHEMA_NAME(dep.referencing_id), OBJECT_NAME(dep.referencing_id), dep.referenced_entity_name;
+ ```
+
+??? code "`extended_properties.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ o.name AS object_name,
+ o.type_desc AS object_type,
+ c.name AS column_name,
+ ep.name AS property_name,
+ CONVERT(NVARCHAR(MAX), ep.value) AS property_value,
+ ep.class_desc
+ FROM sys.extended_properties AS ep
+ LEFT JOIN sys.objects AS o ON ep.major_id = o.object_id
+ LEFT JOIN sys.schemas AS s ON o.schema_id = s.schema_id
+ LEFT JOIN sys.columns AS c ON ep.major_id = c.object_id AND ep.minor_id = c.column_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ ORDER BY s.name, o.name, c.name, ep.name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: mssql
+ SELECT
+ OBJECT_SCHEMA_NAME(fk.parent_object_id) AS schema_name,
+ OBJECT_SCHEMA_NAME(fk.parent_object_id) AS table_schema,
+ fk.name AS constraint_name,
+ OBJECT_NAME(fk.parent_object_id) AS table_name,
+ cpa.name AS column_name,
+ OBJECT_SCHEMA_NAME(fk.referenced_object_id) AS referenced_schema,
+ OBJECT_NAME(fk.referenced_object_id) AS referenced_table,
+ cref.name AS referenced_column
+ FROM sys.foreign_keys fk
+ INNER JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id
+ INNER JOIN sys.columns cpa
+ ON fkc.parent_object_id = cpa.object_id AND fkc.parent_column_id = cpa.column_id
+ INNER JOIN sys.columns cref
+ ON fkc.referenced_object_id = cref.object_id AND fkc.referenced_column_id = cref.column_id
+ WHERE OBJECT_SCHEMA_NAME(fk.parent_object_id) = :schema_name
+ AND OBJECT_NAME(fk.parent_object_id) = :table_name
+ ORDER BY fk.name, fkc.constraint_column_id;
+
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ OBJECT_SCHEMA_NAME(fk.parent_object_id) AS schema_name,
+ OBJECT_SCHEMA_NAME(fk.parent_object_id) AS table_schema,
+ fk.name AS constraint_name,
+ OBJECT_NAME(fk.parent_object_id) AS table_name,
+ cpa.name AS column_name,
+ OBJECT_SCHEMA_NAME(fk.referenced_object_id) AS referenced_schema,
+ OBJECT_NAME(fk.referenced_object_id) AS referenced_table,
+ cref.name AS referenced_column
+ FROM sys.foreign_keys fk
+ INNER JOIN sys.foreign_key_columns fkc ON fk.object_id = fkc.constraint_object_id
+ INNER JOIN sys.columns cpa
+ ON fkc.parent_object_id = cpa.object_id AND fkc.parent_column_id = cpa.column_id
+ INNER JOIN sys.columns cref
+ ON fkc.referenced_object_id = cref.object_id AND fkc.referenced_column_id = cref.column_id
+ WHERE (:schema_name IS NULL OR OBJECT_SCHEMA_NAME(fk.parent_object_id) = :schema_name)
+ ORDER BY OBJECT_SCHEMA_NAME(fk.parent_object_id), OBJECT_NAME(fk.parent_object_id), fk.name, fkc.constraint_column_id;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ i.name AS index_name,
+ i.type_desc,
+ CAST(i.is_unique AS BIT) AS is_unique,
+ CAST(i.is_primary_key AS BIT) AS is_primary,
+ CAST(i.is_unique_constraint AS BIT) AS is_unique_constraint,
+ CAST(i.has_filter AS BIT) AS has_filter,
+ i.filter_definition,
+ ds.name AS data_space_name,
+ ps.name AS partition_scheme_name,
+ STUFF((
+ SELECT ',' + c2.name
+ FROM sys.index_columns AS ic2
+ INNER JOIN sys.columns AS c2 ON ic2.object_id = c2.object_id AND ic2.column_id = c2.column_id
+ WHERE ic2.object_id = i.object_id
+ AND ic2.index_id = i.index_id
+ AND ic2.is_included_column = 0
+ ORDER BY ic2.key_ordinal
+ FOR XML PATH(''), TYPE
+ ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS columns,
+ STUFF((
+ SELECT ',' + c3.name
+ FROM sys.index_columns AS ic3
+ INNER JOIN sys.columns AS c3 ON ic3.object_id = c3.object_id AND ic3.column_id = c3.column_id
+ WHERE ic3.object_id = i.object_id
+ AND ic3.index_id = i.index_id
+ AND ic3.is_included_column = 1
+ ORDER BY ic3.index_column_id
+ FOR XML PATH(''), TYPE
+ ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS included_columns
+ FROM sys.indexes AS i
+ INNER JOIN sys.tables AS t ON i.object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ LEFT JOIN sys.data_spaces AS ds ON i.data_space_id = ds.data_space_id
+ LEFT JOIN sys.partition_schemes AS ps ON ds.data_space_id = ps.data_space_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ AND i.name IS NOT NULL
+ ORDER BY s.name, t.name, i.name;
+
+ -- name: by_table
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ i.name AS index_name,
+ i.type_desc,
+ CAST(i.is_unique AS BIT) AS is_unique,
+ CAST(i.is_primary_key AS BIT) AS is_primary,
+ CAST(i.is_unique_constraint AS BIT) AS is_unique_constraint,
+ CAST(i.has_filter AS BIT) AS has_filter,
+ i.filter_definition,
+ ds.name AS data_space_name,
+ ps.name AS partition_scheme_name,
+ STUFF((
+ SELECT ',' + c2.name
+ FROM sys.index_columns AS ic2
+ INNER JOIN sys.columns AS c2 ON ic2.object_id = c2.object_id AND ic2.column_id = c2.column_id
+ WHERE ic2.object_id = i.object_id
+ AND ic2.index_id = i.index_id
+ AND ic2.is_included_column = 0
+ ORDER BY ic2.key_ordinal
+ FOR XML PATH(''), TYPE
+ ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS columns,
+ STUFF((
+ SELECT ',' + c3.name
+ FROM sys.index_columns AS ic3
+ INNER JOIN sys.columns AS c3 ON ic3.object_id = c3.object_id AND ic3.column_id = c3.column_id
+ WHERE ic3.object_id = i.object_id
+ AND ic3.index_id = i.index_id
+ AND ic3.is_included_column = 1
+ ORDER BY ic3.index_column_id
+ FOR XML PATH(''), TYPE
+ ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS included_columns
+ FROM sys.indexes AS i
+ INNER JOIN sys.tables AS t ON i.object_id = t.object_id
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ LEFT JOIN sys.data_spaces AS ds ON i.data_space_id = ds.data_space_id
+ LEFT JOIN sys.partition_schemes AS ps ON ds.data_space_id = ps.data_space_id
+ WHERE s.name = :schema_name
+ AND t.name = :table_name
+ AND i.name IS NOT NULL
+ ORDER BY i.name;
+ ```
+
+??? code "`modules.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ DB_NAME() AS catalog_name,
+ s.name AS schema_name,
+ o.name AS object_name,
+ o.type AS object_type,
+ o.type_desc,
+ sm.definition,
+ OBJECT_DEFINITION(o.object_id) AS object_definition,
+ CAST(sm.uses_ansi_nulls AS BIT) AS uses_ansi_nulls,
+ CAST(sm.uses_quoted_identifier AS BIT) AS uses_quoted_identifier,
+ CAST(sm.is_schema_bound AS BIT) AS is_schema_bound
+ FROM sys.sql_modules AS sm
+ INNER JOIN sys.objects AS o ON sm.object_id = o.object_id
+ INNER JOIN sys.schemas AS s ON o.schema_id = s.schema_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ AND o.is_ms_shipped = 0
+ ORDER BY s.name, o.name;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ DB_NAME() AS catalog_name,
+ s.name AS schema_name,
+ o.name AS object_name,
+ o.type AS object_type,
+ o.type_desc,
+ o.create_date,
+ o.modify_date,
+ CAST(o.is_ms_shipped AS BIT) AS is_system_object
+ FROM sys.objects AS o
+ INNER JOIN sys.schemas AS s ON o.schema_id = s.schema_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ AND o.is_ms_shipped = 0
+ ORDER BY s.name, o.name;
+ ```
+
+??? code "`permissions.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ o.name AS object_name,
+ o.type_desc AS object_type,
+ grantee.name AS grantee_name,
+ grantor.name AS grantor_name,
+ perm.permission_name,
+ perm.state_desc,
+ perm.class_desc,
+ perm.major_id,
+ perm.minor_id
+ FROM sys.database_permissions AS perm
+ LEFT JOIN sys.objects AS o ON perm.major_id = o.object_id
+ LEFT JOIN sys.schemas AS s ON o.schema_id = s.schema_id
+ LEFT JOIN sys.database_principals AS grantee ON perm.grantee_principal_id = grantee.principal_id
+ LEFT JOIN sys.database_principals AS grantor ON perm.grantor_principal_id = grantor.principal_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ ORDER BY s.name, o.name, grantee.name, perm.permission_name;
+
+ -- name: role_members
+ -- dialect: mssql
+ SELECT
+ role_principal.name AS role_name,
+ member_principal.name AS member_name,
+ member_principal.type_desc AS member_type
+ FROM sys.database_role_members AS drm
+ INNER JOIN sys.database_principals AS role_principal ON drm.role_principal_id = role_principal.principal_id
+ INNER JOIN sys.database_principals AS member_principal ON drm.member_principal_id = member_principal.principal_id
+ ORDER BY role_principal.name, member_principal.name;
+ ```
+
+??? code "`routines.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ DB_NAME() AS catalog_name,
+ s.name AS schema_name,
+ p.name AS routine_name,
+ p.type AS routine_type,
+ p.type_desc,
+ sm.definition,
+ p.create_date,
+ p.modify_date
+ FROM sys.procedures AS p
+ INNER JOIN sys.schemas AS s ON p.schema_id = s.schema_id
+ LEFT JOIN sys.sql_modules AS sm ON p.object_id = sm.object_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ AND p.is_ms_shipped = 0
+ ORDER BY s.name, p.name;
+
+ -- name: parameters_by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ o.name AS routine_name,
+ p.parameter_id,
+ p.name AS parameter_name,
+ typ.name AS data_type,
+ p.max_length,
+ p.precision AS numeric_precision,
+ p.scale AS numeric_scale,
+ CAST(p.is_output AS BIT) AS is_output,
+ CAST(p.has_default_value AS BIT) AS has_default_value,
+ CONVERT(NVARCHAR(MAX), p.default_value) AS default_value
+ FROM sys.parameters AS p
+ INNER JOIN sys.objects AS o ON p.object_id = o.object_id
+ INNER JOIN sys.schemas AS s ON o.schema_id = s.schema_id
+ INNER JOIN sys.types AS typ ON p.user_type_id = typ.user_type_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ AND o.is_ms_shipped = 0
+ ORDER BY s.name, o.name, p.parameter_id;
+ ```
+
+??? code "`schemas.sql` (2 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ s.schema_id,
+ USER_NAME(s.principal_id) AS owner_name
+ FROM sys.schemas AS s
+ WHERE s.name NOT IN ('sys', 'INFORMATION_SCHEMA')
+ ORDER BY s.name;
+
+ -- name: current
+ -- dialect: mssql
+ SELECT SCHEMA_NAME() AS schema_name;
+ ```
+
+??? code "`sequences.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ seq.name AS sequence_name,
+ typ.name AS data_type,
+ CONVERT(NVARCHAR(128), seq.start_value) AS start_value,
+ CONVERT(NVARCHAR(128), seq.increment) AS increment_value,
+ CONVERT(NVARCHAR(128), seq.minimum_value) AS minimum_value,
+ CONVERT(NVARCHAR(128), seq.maximum_value) AS maximum_value,
+ CAST(seq.is_cycling AS BIT) AS is_cycling,
+ CAST(seq.is_cached AS BIT) AS is_cached,
+ seq.cache_size
+ FROM sys.sequences AS seq
+ INNER JOIN sys.schemas AS s ON seq.schema_id = s.schema_id
+ INNER JOIN sys.types AS typ ON seq.user_type_id = typ.user_type_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ ORDER BY s.name, seq.name;
+ ```
+
+??? code "`storage.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ i.name AS index_name,
+ ds.name AS data_space_name,
+ ds.type_desc AS data_space_type,
+ ps.name AS partition_scheme_name,
+ pf.name AS partition_function_name,
+ pf.type_desc AS partition_function_type,
+ CAST(p.partition_number AS INT) AS partition_number,
+ p.rows AS row_count
+ FROM sys.tables AS t
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ LEFT JOIN sys.indexes AS i ON t.object_id = i.object_id AND i.index_id IN (0, 1)
+ LEFT JOIN sys.data_spaces AS ds ON i.data_space_id = ds.data_space_id
+ LEFT JOIN sys.partition_schemes AS ps ON ds.data_space_id = ps.data_space_id
+ LEFT JOIN sys.partition_functions AS pf ON ps.function_id = pf.function_id
+ LEFT JOIN sys.partitions AS p ON i.object_id = p.object_id AND i.index_id = p.index_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ ORDER BY s.name, t.name, i.name, p.partition_number;
+ ```
+
+??? code "`system.sql` (2 named queries)"
+
+ ```sql
+ -- name: dmv_exec_requests
+ -- dialect: mssql
+ /* sqlspec:mssql:dmv_exec_requests */
+ SELECT
+ req.session_id,
+ sess.login_name,
+ req.status,
+ req.command,
+ req.cpu_time,
+ req.total_elapsed_time,
+ req.logical_reads,
+ req.reads,
+ req.writes,
+ DB_NAME(req.database_id) AS database_name,
+ txt.text AS sql_text
+ FROM sys.dm_exec_requests AS req
+ INNER JOIN sys.dm_exec_sessions AS sess ON req.session_id = sess.session_id
+ OUTER APPLY sys.dm_exec_sql_text(req.sql_handle) AS txt
+ WHERE req.session_id <> @@SPID
+ ORDER BY req.total_elapsed_time DESC;
+
+ -- name: query_store_runtime
+ -- dialect: mssql
+ /* sqlspec:mssql:query_store_runtime */
+ SELECT
+ qry.query_id,
+ plan.plan_id,
+ runtime_stats.count_executions,
+ runtime_stats.avg_duration,
+ runtime_stats.avg_cpu_time,
+ runtime_stats.avg_logical_io_reads,
+ runtime_stats.last_execution_time,
+ query_text.query_sql_text AS sql_text
+ FROM sys.query_store_query AS qry
+ INNER JOIN sys.query_store_query_text AS query_text ON qry.query_text_id = query_text.query_text_id
+ INNER JOIN sys.query_store_plan AS plan ON qry.query_id = plan.query_id
+ INNER JOIN sys.query_store_runtime_stats AS runtime_stats ON plan.plan_id = runtime_stats.plan_id
+ ORDER BY runtime_stats.last_execution_time DESC;
+ ```
+
+??? code "`tables.sql` (4 named queries)"
+
+ ```sql
+ -- name: all_by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ 'BASE TABLE' AS table_type,
+ DB_NAME() AS table_catalog,
+ t.create_date,
+ t.modify_date,
+ CAST(t.is_memory_optimized AS BIT) AS is_memory_optimized,
+ CAST(t.temporal_type AS INT) AS temporal_type,
+ t.temporal_type_desc
+ FROM sys.tables AS t
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ ORDER BY s.name, t.name;
+
+ -- name: by_schema
+ -- dialect: mssql
+ WITH dependency_tree AS (
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ t.object_id,
+ 0 AS level,
+ CAST(CONCAT('|', t.object_id, '|') AS NVARCHAR(MAX)) AS path
+ FROM sys.tables AS t
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ AND NOT EXISTS (
+ SELECT 1
+ FROM sys.foreign_keys AS fk
+ WHERE fk.parent_object_id = t.object_id
+ AND fk.referenced_object_id <> t.object_id
+ )
+
+ UNION ALL
+
+ SELECT
+ child_schema.name AS schema_name,
+ child_table.name AS table_name,
+ child_table.object_id,
+ dependency_tree.level + 1 AS level,
+ CAST(CONCAT(dependency_tree.path, child_table.object_id, '|') AS NVARCHAR(MAX)) AS path
+ FROM sys.foreign_keys AS fk
+ INNER JOIN sys.tables AS child_table ON fk.parent_object_id = child_table.object_id
+ INNER JOIN sys.schemas AS child_schema ON child_table.schema_id = child_schema.schema_id
+ INNER JOIN dependency_tree ON fk.referenced_object_id = dependency_tree.object_id
+ WHERE fk.referenced_object_id <> fk.parent_object_id
+ AND (:schema_name IS NULL OR child_schema.name = :schema_name)
+ AND CHARINDEX(CONCAT('|', child_table.object_id, '|'), dependency_tree.path) = 0
+ )
+ SELECT
+ schema_name,
+ table_name,
+ 'BASE TABLE' AS table_type,
+ DB_NAME() AS table_catalog,
+ MAX(level) AS level
+ FROM dependency_tree
+ GROUP BY schema_name, table_name
+ ORDER BY level, schema_name, table_name;
+
+ -- name: details_by_table
+ -- dialect: mssql
+ SELECT
+ DB_NAME() AS catalog_name,
+ s.name AS schema_name,
+ t.name AS table_name,
+ 'BASE TABLE' AS table_type,
+ t.create_date,
+ t.modify_date,
+ CAST(t.is_memory_optimized AS BIT) AS is_memory_optimized,
+ CAST(t.durability AS INT) AS durability,
+ t.durability_desc,
+ CAST(t.temporal_type AS INT) AS temporal_type,
+ t.temporal_type_desc,
+ history_schema.name AS history_schema_name,
+ history_table.name AS history_table_name
+ FROM sys.tables AS t
+ INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+ LEFT JOIN sys.tables AS history_table ON t.history_table_id = history_table.object_id
+ LEFT JOIN sys.schemas AS history_schema ON history_table.schema_id = history_schema.schema_id
+ WHERE s.name = :schema_name
+ AND t.name = :table_name
+ ORDER BY s.name, t.name;
+
+ -- name: names_by_schema
+ -- dialect: mssql
+ SELECT
+ s.name AS schema_name,
+ t.name AS table_name,
+ t.create_date,
+ t.modify_date
+ FROM sys.tables t
+ INNER JOIN sys.schemas s ON t.schema_id = s.schema_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ ORDER BY s.name, t.name;
+ ```
+
+??? code "`triggers.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ trigger_schema.name AS schema_name,
+ tr.name AS trigger_name,
+ parent_schema.name AS parent_schema_name,
+ parent_object.name AS parent_object_name,
+ tr.type_desc,
+ sm.definition,
+ CAST(tr.is_disabled AS BIT) AS is_disabled,
+ CAST(tr.is_instead_of_trigger AS BIT) AS is_instead_of_trigger,
+ tr.create_date,
+ tr.modify_date
+ FROM sys.triggers AS tr
+ LEFT JOIN sys.objects AS parent_object ON tr.parent_id = parent_object.object_id
+ LEFT JOIN sys.schemas AS parent_schema ON parent_object.schema_id = parent_schema.schema_id
+ LEFT JOIN sys.schemas AS trigger_schema ON parent_object.schema_id = trigger_schema.schema_id
+ LEFT JOIN sys.sql_modules AS sm ON tr.object_id = sm.object_id
+ WHERE (:schema_name IS NULL OR parent_schema.name = :schema_name)
+ AND tr.is_ms_shipped = 0
+ ORDER BY parent_schema.name, tr.name;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: mssql
+ SELECT
+ @@VERSION AS version_string,
+ CONVERT(NVARCHAR(128), SERVERPROPERTY('ProductVersion')) AS product_version,
+ CONVERT(NVARCHAR(256), SERVERPROPERTY('Edition')) AS edition,
+ CONVERT(INT, SERVERPROPERTY('EngineEdition')) AS engine_edition;
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mssql
+ SELECT
+ DB_NAME() AS catalog_name,
+ s.name AS schema_name,
+ v.name AS view_name,
+ CAST(v.is_ms_shipped AS BIT) AS is_system_object,
+ sm.definition,
+ CAST(sm.uses_ansi_nulls AS BIT) AS uses_ansi_nulls,
+ CAST(sm.uses_quoted_identifier AS BIT) AS uses_quoted_identifier,
+ v.create_date,
+ v.modify_date
+ FROM sys.views AS v
+ INNER JOIN sys.schemas AS s ON v.schema_id = s.schema_id
+ LEFT JOIN sys.sql_modules AS sm ON v.object_id = sm.object_id
+ WHERE (:schema_name IS NULL OR s.name = :schema_name)
+ AND v.is_ms_shipped = 0
+ ORDER BY s.name, v.name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/mysql.md b/docs/reference/data-dictionary/dialects/mysql.md
new file mode 100644
index 000000000..753b6109a
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/mysql.md
@@ -0,0 +1,659 @@
+# MySQL Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("mysql")
+```
+
+Introspects `information_schema` (`TABLES`, `COLUMNS`, `STATISTICS`, `KEY_COLUMN_USAGE`, `REFERENTIAL_CONSTRAINTS`, `CHECK_CONSTRAINTS`, `ROUTINES`, `TRIGGERS`) across `asyncmy`, `aiomysql`, `pymysql`, and `mysqlconnector`. See the [MySQL Guide](../../../databases/mysql.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `mysql` |
+| **Default Schema** | `None` |
+| **Version Pattern** | `(\d+)\.(\d+)\.(\d+)` |
+| **DDL Strategy** | Native `SHOW CREATE` + `information_schema` synthesis |
+| **Catalog `.sql` Files** | `18` files (`27` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `False` | `bigint` | `BIGINT` |
+| `supports_for_update` | `True` | `blob` | `BLOB` |
+| `supports_on_conflict` | `False` | `boolean` | `TINYINT(1)` |
+| `supports_prepared_statements` | `True` | `float` | `DOUBLE` |
+| `supports_returning` | `False` | `integer` | `INT` |
+| `supports_schemas` | `True` | `json` | `JSON` |
+| `supports_sequences` | `False` | `text` | `TEXT` |
+| `supports_system_versioned_tables` | `False` | `timestamp` | `TIMESTAMP` |
+| `supports_transactions` | `True` | `uuid` | `VARCHAR(36)` |
+| `supports_update_from` | `False` | `varchar` | `VARCHAR({length})` |
+| `supports_upsert` | `True` | - | - |
+| `supports_uuid` | `False` | - | - |
+| `supports_check_constraints` | `>= 8.0.16` | - | - |
+| `supports_cte` | `>= 8.0.1` | - | - |
+| `supports_events` | `>= 5.1.6` | - | - |
+| `supports_generated_columns` | `>= 5.7.0` | - | - |
+| `supports_histograms` | `>= 8.0.2` | - | - |
+| `supports_invisible_columns` | `>= 8.0.23` | - | - |
+| `supports_invisible_indexes` | `>= 8.0.0` | - | - |
+| `supports_json` | `>= 5.7.8` | - | - |
+| `supports_resource_groups` | `>= 8.0.3` | - | - |
+| `supports_roles` | `>= 8.0.0` | - | - |
+| `supports_skip_locked` | `>= 8.0.1` | - | - |
+| `supports_window_functions` | `>= 8.0.2` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.asyncmy.data_dictionary.AsyncmyDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.aiomysql.data_dictionary.AiomysqlDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.pymysql.data_dictionary.PyMysqlDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.mysqlconnector.data_dictionary.MysqlConnectorSyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.mysqlconnector.data_dictionary.MysqlConnectorAsyncDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `mysql` dialect pack defines `27` named queries across `18` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/mysql/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `components.sql` | `list` | *none* |
+| `constraints.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `ddl.sql` | `event_by_name`, `function_by_name`, `procedure_by_name`, `table_by_name`, `trigger_by_name`, `view_by_name` | *none* |
+| `events.sql` | `by_schema` | `:schema_name` |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `objects.sql` | `by_schema` | `:schema_name` |
+| `partitions.sql` | `by_schema` | `:schema_name` |
+| `plugins.sql` | `list` | *none* |
+| `privileges.sql` | `by_schema` | `:schema_name` |
+| `routines.sql` | `by_schema` | `:schema_name` |
+| `schemas.sql` | `list` | `:schema_name` |
+| `system.sql` | `performance_schema_tables`, `sys_schema_table_statistics` | `:schema_name` |
+| `tables.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `triggers.sql` | `by_schema` | `:schema_name` |
+| `version.sql` | `current` | *none* |
+| `views.sql` | `by_schema` | `:schema_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS schema_name,
+ table_name,
+ column_name,
+ ordinal_position,
+ column_default,
+ is_nullable,
+ data_type,
+ column_type,
+ character_maximum_length AS max_length,
+ numeric_precision,
+ numeric_scale,
+ character_set_name,
+ collation_name,
+ column_key,
+ column_key = 'PRI' AS is_primary,
+ extra,
+ extra LIKE '%auto_increment%' AS is_identity,
+ generation_expression,
+ CASE WHEN generation_expression IS NOT NULL AND generation_expression <> '' THEN 1 ELSE 0 END AS is_generated,
+ column_comment
+ FROM information_schema.columns
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY table_schema, table_name, ordinal_position;
+
+ -- name: by_table
+ -- dialect: mysql
+ SELECT
+ column_name AS `column_name`,
+ data_type AS `data_type`,
+ column_type AS `column_type`,
+ is_nullable AS `is_nullable`,
+ column_default AS `column_default`,
+ column_key AS `column_key`,
+ column_key = 'PRI' AS `is_primary`,
+ extra AS `extra`,
+ extra LIKE '%auto_increment%' AS `is_identity`,
+ generation_expression AS `generation_expression`,
+ CASE WHEN generation_expression IS NOT NULL AND generation_expression <> '' THEN 1 ELSE 0 END AS `is_generated`
+ FROM information_schema.columns
+ WHERE table_name = :table_name
+ AND table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY ordinal_position;
+ ```
+
+??? code "`components.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: mysql
+ SELECT
+ component_id,
+ component_group_id,
+ component_urn
+ FROM mysql.component
+ ORDER BY component_id;
+ ```
+
+??? code "`constraints.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ tc.constraint_schema AS schema_name,
+ tc.table_name,
+ tc.constraint_name,
+ tc.constraint_type,
+ kcu.column_name,
+ kcu.ordinal_position,
+ kcu.referenced_table_schema,
+ kcu.referenced_table_name,
+ kcu.referenced_column_name,
+ rc.update_rule,
+ rc.delete_rule,
+ cc.check_clause
+ FROM information_schema.table_constraints tc
+ LEFT JOIN information_schema.key_column_usage kcu
+ ON kcu.constraint_schema = tc.constraint_schema
+ AND kcu.constraint_name = tc.constraint_name
+ AND kcu.table_schema = tc.table_schema
+ AND kcu.table_name = tc.table_name
+ LEFT JOIN information_schema.referential_constraints rc
+ ON rc.constraint_schema = tc.constraint_schema
+ AND rc.constraint_name = tc.constraint_name
+ LEFT JOIN information_schema.check_constraints cc
+ ON cc.constraint_schema = tc.constraint_schema
+ AND cc.constraint_name = tc.constraint_name
+ WHERE tc.table_schema = COALESCE(:schema_name, DATABASE())
+ AND (:table_name IS NULL OR tc.table_name = :table_name)
+ ORDER BY tc.table_schema, tc.table_name, tc.constraint_name, kcu.ordinal_position;
+ ```
+
+??? code "`ddl.sql` (6 named queries)"
+
+ ```sql
+ -- name: event_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE EVENT' AS statement_template;
+
+ -- name: function_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE FUNCTION' AS statement_template;
+
+ -- name: procedure_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE PROCEDURE' AS statement_template;
+
+ -- name: table_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE TABLE' AS statement_template;
+
+ -- name: trigger_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE TRIGGER' AS statement_template;
+
+ -- name: view_by_name
+ -- dialect: mysql
+ SELECT 'SHOW CREATE VIEW' AS statement_template;
+ ```
+
+??? code "`events.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ event_schema AS schema_name,
+ event_name,
+ definer,
+ time_zone,
+ event_body,
+ event_definition,
+ event_type,
+ execute_at,
+ interval_value,
+ interval_field,
+ sql_mode,
+ starts,
+ ends,
+ status,
+ on_completion,
+ created,
+ last_altered,
+ last_executed,
+ event_comment,
+ originator,
+ character_set_client,
+ collation_connection,
+ database_collation
+ FROM information_schema.events
+ WHERE event_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY event_schema, event_name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: mysql
+ SELECT
+ kcu.table_name AS `table_name`,
+ kcu.column_name AS `column_name`,
+ kcu.referenced_table_name AS `referenced_table`,
+ kcu.referenced_column_name AS `referenced_column`,
+ kcu.constraint_name AS `constraint_name`,
+ kcu.table_schema AS `schema`,
+ kcu.referenced_table_schema AS `referenced_schema`,
+ rc.delete_rule AS `on_delete`,
+ rc.update_rule AS `on_update`
+ FROM information_schema.key_column_usage AS kcu
+ JOIN information_schema.referential_constraints AS rc
+ ON rc.constraint_schema = kcu.constraint_schema
+ AND rc.constraint_name = kcu.constraint_name
+ AND rc.table_name = kcu.table_name
+ WHERE kcu.referenced_table_name IS NOT NULL
+ AND kcu.table_name = :table_name
+ AND kcu.table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY kcu.constraint_name, kcu.ordinal_position;
+
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ kcu.table_name AS `table_name`,
+ kcu.column_name AS `column_name`,
+ kcu.referenced_table_name AS `referenced_table`,
+ kcu.referenced_column_name AS `referenced_column`,
+ kcu.constraint_name AS `constraint_name`,
+ kcu.table_schema AS `schema`,
+ kcu.referenced_table_schema AS `referenced_schema`,
+ rc.delete_rule AS `on_delete`,
+ rc.update_rule AS `on_update`
+ FROM information_schema.key_column_usage AS kcu
+ JOIN information_schema.referential_constraints AS rc
+ ON rc.constraint_schema = kcu.constraint_schema
+ AND rc.constraint_name = kcu.constraint_name
+ AND rc.table_name = kcu.table_name
+ WHERE kcu.referenced_table_name IS NOT NULL
+ AND kcu.table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY kcu.table_name, kcu.constraint_name, kcu.ordinal_position;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS schema_name,
+ table_name,
+ index_name,
+ non_unique = 0 AS is_unique,
+ index_name = 'PRIMARY' AS is_primary,
+ index_type,
+ is_visible,
+ GROUP_CONCAT(column_name ORDER BY seq_in_index) AS columns,
+ GROUP_CONCAT(sub_part ORDER BY seq_in_index) AS prefix_lengths,
+ GROUP_CONCAT(collation ORDER BY seq_in_index) AS column_orders,
+ index_comment
+ FROM information_schema.statistics
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ AND (:table_name IS NULL OR table_name = :table_name)
+ GROUP BY table_schema, table_name, index_name, non_unique, index_type, is_visible, index_comment
+ ORDER BY table_schema, table_name, index_name;
+
+ -- name: by_table
+ -- dialect: mysql
+ SELECT
+ s.index_name AS index_name,
+ s.table_name AS table_name,
+ CASE WHEN s.non_unique = 0 THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN s.index_name = 'PRIMARY' THEN 1 ELSE 0 END AS is_primary,
+ GROUP_CONCAT(s.column_name ORDER BY s.seq_in_index) AS columns
+ FROM information_schema.statistics s
+ WHERE s.table_schema = COALESCE(:schema_name, DATABASE())
+ AND s.table_name = :table_name
+ GROUP BY s.index_name, s.table_name, s.non_unique
+ ORDER BY s.index_name;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS object_schema,
+ table_name AS object_name,
+ table_type AS object_type,
+ engine,
+ table_comment AS comment
+ FROM information_schema.tables
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ routine_schema AS object_schema,
+ routine_name AS object_name,
+ routine_type AS object_type,
+ NULL AS engine,
+ routine_comment AS comment
+ FROM information_schema.routines
+ WHERE routine_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ trigger_schema AS object_schema,
+ trigger_name AS object_name,
+ 'TRIGGER' AS object_type,
+ NULL AS engine,
+ NULL AS comment
+ FROM information_schema.triggers
+ WHERE trigger_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ event_schema AS object_schema,
+ event_name AS object_name,
+ 'EVENT' AS object_type,
+ NULL AS engine,
+ event_comment AS comment
+ FROM information_schema.events
+ WHERE event_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY object_schema, object_name, object_type;
+ ```
+
+??? code "`partitions.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS schema_name,
+ table_name,
+ partition_name,
+ subpartition_name,
+ partition_ordinal_position,
+ subpartition_ordinal_position,
+ partition_method,
+ subpartition_method,
+ partition_expression,
+ subpartition_expression,
+ partition_description,
+ table_rows,
+ avg_row_length,
+ data_length,
+ index_length,
+ create_time,
+ update_time,
+ check_time,
+ check_sum,
+ partition_comment
+ FROM information_schema.partitions
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ AND partition_name IS NOT NULL
+ ORDER BY table_schema, table_name, partition_ordinal_position, subpartition_ordinal_position;
+ ```
+
+??? code "`plugins.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: mysql
+ SELECT
+ plugin_name,
+ plugin_version,
+ plugin_status,
+ plugin_type,
+ plugin_type_version,
+ plugin_library,
+ plugin_library_version,
+ plugin_author,
+ plugin_description,
+ plugin_license,
+ load_option
+ FROM information_schema.plugins
+ ORDER BY plugin_type, plugin_name;
+ ```
+
+??? code "`privileges.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ grantee,
+ table_catalog,
+ table_schema AS schema_name,
+ table_name,
+ privilege_type,
+ is_grantable,
+ NULL AS column_name
+ FROM information_schema.table_privileges
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ UNION ALL
+ SELECT
+ grantee,
+ table_catalog,
+ table_schema AS schema_name,
+ table_name,
+ privilege_type,
+ is_grantable,
+ column_name
+ FROM information_schema.column_privileges
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY schema_name, table_name, column_name, grantee, privilege_type;
+ ```
+
+??? code "`routines.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ routine_schema AS schema_name,
+ routine_name,
+ routine_type,
+ data_type,
+ routine_definition,
+ external_language,
+ is_deterministic,
+ sql_data_access,
+ security_type,
+ created,
+ last_altered,
+ definer,
+ character_set_client,
+ collation_connection,
+ database_collation
+ FROM information_schema.routines
+ WHERE routine_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY routine_schema, routine_name;
+ ```
+
+??? code "`schemas.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: mysql
+ SELECT
+ schema_name,
+ default_character_set_name,
+ default_collation_name,
+ sql_path
+ FROM information_schema.schemata
+ WHERE :schema_name IS NULL OR schema_name = :schema_name
+ ORDER BY schema_name;
+ ```
+
+??? code "`system.sql` (2 named queries)"
+
+ ```sql
+ -- name: performance_schema_tables
+ -- dialect: mysql
+ SELECT
+ table_schema,
+ table_name,
+ table_rows,
+ avg_row_length,
+ data_length,
+ index_length
+ FROM information_schema.tables
+ WHERE table_schema = 'performance_schema'
+ ORDER BY table_name;
+
+ -- name: sys_schema_table_statistics
+ -- dialect: mysql
+ SELECT
+ table_schema,
+ table_name,
+ rows_fetched,
+ rows_inserted,
+ rows_updated,
+ rows_deleted
+ FROM sys.schema_table_statistics
+ WHERE :schema_name IS NULL OR table_schema = :schema_name
+ ORDER BY table_schema, table_name;
+ ```
+
+??? code "`tables.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ WITH RECURSIVE dependency_tree AS (
+ SELECT
+ table_name,
+ 0 AS level,
+ CAST(table_name AS CHAR(4000)) AS path
+ FROM information_schema.tables AS root_table
+ WHERE root_table.table_type = 'BASE TABLE'
+ AND root_table.table_schema = COALESCE(:schema_name, DATABASE())
+ AND NOT EXISTS (
+ SELECT 1
+ FROM information_schema.key_column_usage AS root_key
+ WHERE root_key.table_name = root_table.table_name
+ AND root_key.table_schema = root_table.table_schema
+ AND root_key.referenced_table_name IS NOT NULL
+ AND root_key.referenced_table_name <> root_table.table_name
+ )
+
+ UNION ALL
+
+ SELECT
+ child_key.table_name,
+ dependency_tree.level + 1,
+ CONCAT(dependency_tree.path, ',', child_key.table_name)
+ FROM information_schema.key_column_usage AS child_key
+ JOIN dependency_tree ON child_key.referenced_table_name = dependency_tree.table_name
+ WHERE child_key.table_schema = COALESCE(:schema_name, DATABASE())
+ AND child_key.referenced_table_name IS NOT NULL
+ AND child_key.referenced_table_name <> child_key.table_name
+ AND NOT FIND_IN_SET(child_key.table_name, dependency_tree.path)
+ ), dependency_levels AS (
+ SELECT table_name, MAX(level) AS dependency_level
+ FROM dependency_tree
+ GROUP BY table_name
+ )
+ SELECT
+ catalog_table.table_schema AS schema_name,
+ CAST(catalog_table.table_name AS CHAR) AS table_name,
+ catalog_table.table_type AS table_type,
+ catalog_table.engine AS engine,
+ catalog_table.version AS version,
+ catalog_table.row_format AS row_format,
+ catalog_table.table_rows AS table_rows,
+ catalog_table.avg_row_length AS avg_row_length,
+ catalog_table.data_length AS data_length,
+ catalog_table.max_data_length AS max_data_length,
+ catalog_table.index_length AS index_length,
+ catalog_table.data_free AS data_free,
+ catalog_table.auto_increment AS auto_increment,
+ catalog_table.create_time AS create_time,
+ catalog_table.update_time AS update_time,
+ catalog_table.check_time AS check_time,
+ catalog_table.table_collation AS table_collation,
+ catalog_table.checksum AS checksum,
+ catalog_table.create_options AS create_options,
+ catalog_table.table_comment AS table_comment,
+ COALESCE(dependency_levels.dependency_level, 0) AS dependency_level,
+ COALESCE(dependency_levels.dependency_level, 0) AS level
+ FROM information_schema.tables AS catalog_table
+ LEFT JOIN dependency_levels ON dependency_levels.table_name = catalog_table.table_name
+ WHERE catalog_table.table_schema = COALESCE(:schema_name, DATABASE())
+ AND (:table_name IS NULL OR catalog_table.table_name = :table_name)
+ ORDER BY dependency_level, catalog_table.table_name;
+ ```
+
+??? code "`triggers.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ trigger_schema AS schema_name,
+ trigger_name,
+ event_manipulation,
+ event_object_schema,
+ event_object_table,
+ action_order,
+ action_condition,
+ action_statement,
+ action_orientation,
+ action_timing,
+ sql_mode,
+ definer,
+ character_set_client,
+ collation_connection,
+ database_collation
+ FROM information_schema.triggers
+ WHERE trigger_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY trigger_schema, event_object_table, action_timing, event_manipulation, action_order;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: mysql
+ SELECT VERSION() AS version;
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: mysql
+ SELECT
+ table_schema AS schema_name,
+ table_name AS view_name,
+ view_definition,
+ check_option,
+ is_updatable,
+ definer,
+ security_type,
+ character_set_client,
+ collation_connection
+ FROM information_schema.views
+ WHERE table_schema = COALESCE(:schema_name, DATABASE())
+ ORDER BY table_schema, table_name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/oracle.md b/docs/reference/data-dictionary/dialects/oracle.md
new file mode 100644
index 000000000..491e11569
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/oracle.md
@@ -0,0 +1,957 @@
+# Oracle Database Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("oracle")
+```
+
+Introspects `ALL_TABLES`, `ALL_TAB_COLS`, `ALL_INDEXES`, `ALL_IND_COLUMNS`, `ALL_CONSTRAINTS`, `ALL_CONS_COLUMNS`, `ALL_VIEWS`, `ALL_OBJECTS`, `ALL_TRIGGERS`, `ALL_SEQUENCES`, and `DBMS_METADATA`. See the [Oracle Database Guide](../../../databases/oracledb.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `oracle` |
+| **Default Schema** | `None` |
+| **Version Pattern** | `(\d+)` |
+| **DDL Strategy** | Native `DBMS_METADATA.GET_DDL` + AST fallback |
+| **Catalog `.sql` Files** | `22` files (`35` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_for_update` | `True` | `bigint` | `NUMBER(19)` |
+| `supports_in_memory` | `True` | `blob` | `BLOB` |
+| `supports_on_conflict` | `False` | `boolean` | `NUMBER(1)` |
+| `supports_prepared_statements` | `True` | `float` | `BINARY_DOUBLE` |
+| `supports_schemas` | `True` | `integer` | `NUMBER(10)` |
+| `supports_skip_locked` | `True` | `json` | `JSON` |
+| `supports_transactions` | `True` | `text` | `CLOB` |
+| `supports_update_from` | `False` | `timestamp` | `TIMESTAMP` |
+| - | - | `uuid` | `RAW(16)` |
+| - | - | `varchar` | `VARCHAR2({length})` |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.oracledb.data_dictionary.OracledbSyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.oracledb.data_dictionary.OracledbAsyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.oracledb.data_dictionary.OracleVersionInfo
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.oracledb.data_dictionary.JSONStorageType
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `oracle` dialect pack defines `35` named queries across `22` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/oracle/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `arguments.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `comments.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `constraints.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `ddl.sql` | `by_object` | `:object_name`, `:object_type`, `:owner` |
+| `dependencies.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `grants.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `lob_storage.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `materialized_views.sql` | `by_schema` | `:mview_name`, `:schema_name` |
+| `objects.sql` | `by_schema`, `cloud_ai_available` | `:object_name`, `:schema_name` |
+| `partitions.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `routines.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `schemas.sql` | `by_schema` | `:schema_name` |
+| `sequences.sql` | `by_schema` | `:schema_name`, `:sequence_name` |
+| `source.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `system.sql` | `awr`, `no_diagnostics`, `statspack`, `vector_memory_pool` | `:acknowledge_license`, `:include_diagnostics` |
+| `tables.sql` | `by_schema`, `names_by_schema` | `:schema_name`, `:table_name` |
+| `triggers.sql` | `by_schema` | `:schema_name`, `:trigger_name` |
+| `version.sql` | `current`, `autonomous_service`, `compatible`, `apex_release` | *none* |
+| `views.sql` | `by_schema`, `json_duality_views`, `json_duality_views_by_schema` | `:schema_name`, `:view_name` |
+
+??? code "`arguments.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ object_name,
+ package_name,
+ procedure_name,
+ argument_name,
+ position,
+ sequence,
+ data_level,
+ data_type,
+ defaulted,
+ default_value,
+ in_out,
+ data_length,
+ data_precision,
+ data_scale,
+ type_owner,
+ type_name,
+ type_subname,
+ pls_type,
+ char_length,
+ char_used,
+ origin_con_id
+ FROM all_arguments
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR object_name = :object_name)
+ ORDER BY owner, object_name, package_name, subprogram_id, sequence;
+ ```
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ WITH pk_columns AS (
+ SELECT DISTINCT
+ acc.owner,
+ acc.table_name,
+ acc.column_name
+ FROM all_constraints ac
+ JOIN all_cons_columns acc
+ ON acc.owner = ac.owner
+ AND acc.constraint_name = ac.constraint_name
+ AND acc.table_name = ac.table_name
+ WHERE ac.owner = COALESCE(:schema_name, USER)
+ AND (:table_name IS NULL OR ac.table_name = :table_name)
+ AND ac.constraint_type = 'P'
+ )
+ SELECT
+ c.owner AS schema_name,
+ c.table_name,
+ c.column_name,
+ c.data_type,
+ c.data_type_owner,
+ c.data_length AS max_length,
+ c.data_precision AS numeric_precision,
+ c.data_scale AS numeric_scale,
+ c.nullable AS is_nullable,
+ c.column_id AS ordinal_position,
+ c.data_default AS column_default,
+ CASE WHEN c.hidden_column = 'YES' THEN 1 ELSE 0 END AS is_hidden,
+ CASE WHEN c.virtual_column = 'YES' THEN 1 ELSE 0 END AS is_generated,
+ c.segment_column_id,
+ c.internal_column_id,
+ CASE WHEN c.identity_column = 'YES' THEN 1 ELSE 0 END AS is_identity,
+ CASE
+ WHEN ic.generation_type = 'BY DEFAULT' AND c.default_on_null = 'YES' THEN 'BY DEFAULT ON NULL'
+ ELSE ic.generation_type
+ END AS identity_generation,
+ ic.sequence_name AS sequence_name,
+ CASE WHEN pk.column_name IS NOT NULL THEN 1 ELSE 0 END AS is_primary,
+ c.collation,
+ cc.comments AS column_comment
+ FROM all_tab_cols c
+ LEFT JOIN pk_columns pk
+ ON pk.owner = c.owner
+ AND pk.table_name = c.table_name
+ AND pk.column_name = c.column_name
+ LEFT JOIN all_tab_identity_cols ic
+ ON ic.owner = c.owner
+ AND ic.table_name = c.table_name
+ AND ic.column_name = c.column_name
+ LEFT JOIN all_col_comments cc
+ ON cc.owner = c.owner
+ AND cc.table_name = c.table_name
+ AND cc.column_name = c.column_name
+ WHERE c.owner = COALESCE(:schema_name, USER)
+ AND (:table_name IS NULL OR c.table_name = :table_name)
+ ORDER BY c.owner, c.table_name, c.internal_column_id;
+
+ -- name: by_table
+ -- dialect: oracle
+ WITH pk_columns AS (
+ SELECT DISTINCT
+ acc.owner,
+ acc.table_name,
+ acc.column_name
+ FROM all_constraints ac
+ JOIN all_cons_columns acc
+ ON acc.owner = ac.owner
+ AND acc.constraint_name = ac.constraint_name
+ AND acc.table_name = ac.table_name
+ WHERE ac.owner = COALESCE(:schema_name, USER)
+ AND ac.table_name = :table_name
+ AND ac.constraint_type = 'P'
+ )
+ SELECT
+ c.owner AS schema_name,
+ c.table_name AS table_name,
+ c.column_name AS column_name,
+ c.data_type AS data_type,
+ c.nullable AS is_nullable,
+ c.data_default AS column_default,
+ c.column_id AS ordinal_position,
+ CASE WHEN c.hidden_column = 'YES' THEN 1 ELSE 0 END AS is_hidden,
+ CASE WHEN c.virtual_column = 'YES' THEN 1 ELSE 0 END AS is_generated,
+ CASE WHEN c.identity_column = 'YES' THEN 1 ELSE 0 END AS is_identity,
+ CASE
+ WHEN ic.generation_type = 'BY DEFAULT' AND c.default_on_null = 'YES' THEN 'BY DEFAULT ON NULL'
+ ELSE ic.generation_type
+ END AS identity_generation,
+ ic.sequence_name AS sequence_name,
+ CASE WHEN pk.column_name IS NOT NULL THEN 1 ELSE 0 END AS is_primary
+ FROM all_tab_cols c
+ LEFT JOIN pk_columns pk
+ ON pk.owner = c.owner
+ AND pk.table_name = c.table_name
+ AND pk.column_name = c.column_name
+ LEFT JOIN all_tab_identity_cols ic
+ ON ic.owner = c.owner
+ AND ic.table_name = c.table_name
+ AND ic.column_name = c.column_name
+ WHERE c.owner = COALESCE(:schema_name, USER)
+ AND c.table_name = :table_name
+ ORDER BY c.column_id;
+ ```
+
+??? code "`comments.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ table_name,
+ NULL AS column_name,
+ table_type AS object_type,
+ comments
+ FROM all_tab_comments
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR table_name = :object_name)
+ UNION ALL
+ SELECT
+ owner AS schema_name,
+ table_name,
+ column_name,
+ 'COLUMN' AS object_type,
+ comments
+ FROM all_col_comments
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR table_name = :object_name)
+ ORDER BY schema_name, table_name, column_name;
+ ```
+
+??? code "`constraints.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ c.owner AS schema_name,
+ c.table_name,
+ c.constraint_name,
+ c.constraint_type,
+ c.search_condition_vc AS search_condition,
+ c.r_owner AS referenced_schema,
+ r.table_name AS referenced_table,
+ c.r_constraint_name AS referenced_constraint,
+ c.delete_rule,
+ c.status,
+ c.deferrable,
+ c.deferred,
+ c.validated,
+ c.generated,
+ c.bad,
+ c.rely,
+ c.last_change,
+ LISTAGG(cc.column_name, ',') WITHIN GROUP (ORDER BY cc.position) AS columns
+ FROM all_constraints c
+ LEFT JOIN all_cons_columns cc
+ ON cc.owner = c.owner
+ AND cc.constraint_name = c.constraint_name
+ LEFT JOIN all_constraints r
+ ON r.owner = c.r_owner
+ AND r.constraint_name = c.r_constraint_name
+ WHERE c.owner = COALESCE(:schema_name, USER)
+ AND (:table_name IS NULL OR c.table_name = :table_name)
+ GROUP BY
+ c.owner,
+ c.table_name,
+ c.constraint_name,
+ c.constraint_type,
+ c.search_condition_vc,
+ c.r_owner,
+ r.table_name,
+ c.r_constraint_name,
+ c.delete_rule,
+ c.status,
+ c.deferrable,
+ c.deferred,
+ c.validated,
+ c.generated,
+ c.bad,
+ c.rely,
+ c.last_change
+ ORDER BY c.owner, c.table_name, c.constraint_name;
+ ```
+
+??? code "`ddl.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_object
+ -- dialect: oracle
+ SELECT DBMS_METADATA.GET_DDL(:object_type, :object_name, :owner) AS ddl
+ FROM dual;
+ ```
+
+??? code "`dependencies.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ name AS object_name,
+ type AS object_type,
+ referenced_owner,
+ referenced_name,
+ referenced_type,
+ referenced_link_name,
+ dependency_type,
+ origin_con_id
+ FROM all_dependencies
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR name = :object_name)
+ ORDER BY owner, name, referenced_owner, referenced_name, referenced_type;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: oracle
+ SELECT
+ c.table_name,
+ cc.column_name,
+ r.table_name AS referenced_table_name,
+ rcc.column_name AS referenced_column_name,
+ c.constraint_name,
+ c.owner AS table_schema,
+ r.owner AS referenced_table_schema
+ FROM all_constraints c
+ JOIN all_cons_columns cc
+ ON c.owner = cc.owner
+ AND c.constraint_name = cc.constraint_name
+ JOIN all_constraints r
+ ON c.r_owner = r.owner
+ AND c.r_constraint_name = r.constraint_name
+ JOIN all_cons_columns rcc
+ ON r.owner = rcc.owner
+ AND r.constraint_name = rcc.constraint_name
+ AND cc.position = rcc.position
+ WHERE c.constraint_type = 'R'
+ AND c.owner = COALESCE(:schema_name, USER)
+ AND (:table_name IS NULL OR c.table_name = :table_name);
+
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ c.table_name,
+ cc.column_name,
+ r.table_name AS referenced_table_name,
+ rcc.column_name AS referenced_column_name,
+ c.constraint_name,
+ c.owner AS table_schema,
+ r.owner AS referenced_table_schema
+ FROM all_constraints c
+ JOIN all_cons_columns cc
+ ON c.owner = cc.owner
+ AND c.constraint_name = cc.constraint_name
+ JOIN all_constraints r
+ ON c.r_owner = r.owner
+ AND c.r_constraint_name = r.constraint_name
+ JOIN all_cons_columns rcc
+ ON r.owner = rcc.owner
+ AND r.constraint_name = rcc.constraint_name
+ AND cc.position = rcc.position
+ WHERE c.constraint_type = 'R'
+ AND c.owner = COALESCE(:schema_name, USER);
+ ```
+
+??? code "`grants.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ table_name AS object_name,
+ NULL AS column_name,
+ grantor,
+ grantee,
+ privilege,
+ grantable,
+ hierarchy,
+ type,
+ common,
+ inherited
+ FROM all_tab_privs
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR table_name = :object_name)
+ UNION ALL
+ SELECT
+ owner AS schema_name,
+ table_name AS object_name,
+ column_name,
+ grantor,
+ grantee,
+ privilege,
+ grantable,
+ NULL AS hierarchy,
+ 'COLUMN' AS type,
+ common,
+ inherited
+ FROM all_col_privs
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR table_name = :object_name)
+ ORDER BY schema_name, object_name, column_name, grantee, privilege;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ i.owner AS schema_name,
+ i.table_name,
+ i.index_name,
+ i.index_type,
+ i.uniqueness,
+ i.compression,
+ i.prefix_length,
+ i.tablespace_name,
+ i.status,
+ i.partitioned,
+ i.temporary,
+ i.generated,
+ i.visibility,
+ i.blevel,
+ i.leaf_blocks,
+ i.distinct_keys,
+ i.avg_leaf_blocks_per_key,
+ i.avg_data_blocks_per_key,
+ i.clustering_factor,
+ LISTAGG(ic.column_name, ',') WITHIN GROUP (ORDER BY ic.column_position) AS columns,
+ LISTAGG(ie.column_expression, ',') WITHIN GROUP (ORDER BY ie.column_position) AS expressions
+ FROM all_indexes i
+ LEFT JOIN all_ind_columns ic
+ ON ic.index_owner = i.owner
+ AND ic.index_name = i.index_name
+ LEFT JOIN all_ind_expressions ie
+ ON ie.index_owner = i.owner
+ AND ie.index_name = i.index_name
+ AND ie.column_position = ic.column_position
+ WHERE i.owner = COALESCE(:schema_name, USER)
+ AND (:table_name IS NULL OR i.table_name = :table_name)
+ GROUP BY
+ i.owner,
+ i.table_name,
+ i.index_name,
+ i.index_type,
+ i.uniqueness,
+ i.compression,
+ i.prefix_length,
+ i.tablespace_name,
+ i.status,
+ i.partitioned,
+ i.temporary,
+ i.generated,
+ i.visibility,
+ i.blevel,
+ i.leaf_blocks,
+ i.distinct_keys,
+ i.avg_leaf_blocks_per_key,
+ i.avg_data_blocks_per_key,
+ i.clustering_factor
+ ORDER BY i.owner, i.table_name, i.index_name;
+
+ -- name: by_table
+ -- dialect: oracle
+ SELECT
+ i.index_name,
+ i.table_name AS table_name,
+ CASE WHEN i.uniqueness = 'UNIQUE' THEN 1 ELSE 0 END AS is_unique,
+ CASE
+ WHEN MAX(CASE WHEN c.constraint_type = 'P' THEN 1 ELSE 0 END) = 1 THEN 1
+ ELSE 0
+ END AS is_primary,
+ LISTAGG(ic.column_name, ',') WITHIN GROUP (ORDER BY ic.column_position) AS columns
+ FROM all_indexes i
+ JOIN all_ind_columns ic
+ ON i.owner = ic.index_owner
+ AND i.index_name = ic.index_name
+ LEFT JOIN all_constraints c
+ ON c.owner = i.owner
+ AND c.index_name = i.index_name
+ AND c.constraint_type = 'P'
+ WHERE i.table_name = :table_name
+ AND i.owner = COALESCE(:schema_name, USER)
+ GROUP BY i.index_name, i.table_name, i.uniqueness;
+ ```
+
+??? code "`lob_storage.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ table_name,
+ column_name,
+ segment_name,
+ tablespace_name,
+ index_name,
+ chunk,
+ pctversion,
+ retention,
+ freepools,
+ cache,
+ logging,
+ encrypt,
+ compression,
+ deduplication,
+ in_row,
+ format,
+ partitioned,
+ securefile,
+ retention_type,
+ retention_value
+ FROM all_lobs
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY owner, table_name, column_name;
+ ```
+
+??? code "`materialized_views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ mview_name,
+ container_name,
+ query,
+ updatable,
+ update_log,
+ master_rollback_seg,
+ master_link,
+ rewrite_enabled,
+ rewrite_capability,
+ refresh_mode,
+ refresh_method,
+ build_mode,
+ fast_refreshable,
+ last_refresh_type,
+ last_refresh_date,
+ staleness,
+ compile_state,
+ use_no_index,
+ stale_since
+ FROM all_mviews
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:mview_name IS NULL OR mview_name = :mview_name)
+ ORDER BY owner, mview_name;
+ ```
+
+??? code "`objects.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ object_name,
+ object_type,
+ status,
+ created,
+ last_ddl_time,
+ temporary,
+ generated,
+ secondary,
+ editionable,
+ oracle_maintained
+ FROM all_objects
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR object_name = :object_name)
+ ORDER BY owner, object_type, object_name;
+
+ -- name: cloud_ai_available
+ -- dialect: oracle
+ SELECT
+ COUNT(*) AS "cnt"
+ FROM all_objects
+ WHERE object_name = 'DBMS_CLOUD_AI'
+ AND object_type = 'PACKAGE';
+ ```
+
+??? code "`partitions.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ table_owner AS schema_name,
+ table_name,
+ partition_name,
+ composite,
+ subpartition_count,
+ high_value,
+ high_value_length,
+ partition_position,
+ tablespace_name,
+ pct_free,
+ pct_used,
+ ini_trans,
+ max_trans,
+ initial_extent,
+ next_extent,
+ min_extent,
+ max_extent,
+ max_size,
+ pct_increase,
+ freelists,
+ freelist_groups,
+ logging,
+ compression,
+ compress_for,
+ num_rows,
+ blocks,
+ empty_blocks,
+ avg_space,
+ chain_cnt,
+ avg_row_len,
+ sample_size,
+ last_analyzed,
+ interval,
+ segment_created,
+ indexing,
+ read_only,
+ inmemory
+ FROM all_tab_partitions
+ WHERE table_owner = COALESCE(:schema_name, USER)
+ AND (:table_name IS NULL OR table_name = :table_name)
+ ORDER BY table_owner, table_name, partition_position;
+ ```
+
+??? code "`routines.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ object_name,
+ procedure_name,
+ object_id,
+ subprogram_id,
+ object_type,
+ aggregate,
+ pipelined,
+ impltypeowner,
+ impltypename,
+ parallel,
+ interface,
+ deterministic,
+ authid
+ FROM all_procedures
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR object_name = :object_name)
+ ORDER BY owner, object_name, subprogram_id, procedure_name;
+ ```
+
+??? code "`schemas.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ username AS schema_name,
+ user_id,
+ created,
+ common,
+ oracle_maintained,
+ inherited,
+ default_collation
+ FROM all_users
+ WHERE username = COALESCE(:schema_name, username)
+ ORDER BY username;
+ ```
+
+??? code "`sequences.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ sequence_owner AS schema_name,
+ sequence_name,
+ min_value,
+ max_value,
+ increment_by,
+ cycle_flag,
+ order_flag,
+ cache_size,
+ last_number,
+ scale_flag,
+ extend_flag,
+ sharded_flag,
+ session_flag,
+ keep_value
+ FROM all_sequences
+ WHERE sequence_owner = COALESCE(:schema_name, USER)
+ AND (:sequence_name IS NULL OR sequence_name = :sequence_name)
+ ORDER BY sequence_owner, sequence_name;
+ ```
+
+??? code "`source.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ name,
+ type,
+ line,
+ text,
+ origin_con_id
+ FROM all_source
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:object_name IS NULL OR name = :object_name)
+ ORDER BY owner, name, type, line;
+ ```
+
+??? code "`system.sql` (4 named queries)"
+
+ ```sql
+ -- name: awr
+ -- dialect: oracle
+ SELECT
+ :acknowledge_license AS license_acknowledged,
+ :include_diagnostics AS diagnostics_enabled,
+ 'DBA_HIST_SNAPSHOT' AS required_view,
+ 'AWR/ASH/ADDM and DBA_HIST access requires explicit opt-in, privileges, and license acknowledgement.' AS warning
+ FROM dual
+ WHERE :include_diagnostics = 1
+ AND :acknowledge_license = 1;
+
+ -- name: no_diagnostics
+ -- dialect: oracle
+ SELECT
+ 0 AS diagnostics_enabled,
+ 'disabled' AS diagnostics_mode,
+ 'Oracle diagnostics, AWR/ASH/ADDM, Statspack, and DBA_HIST queries are disabled by default.' AS warning
+ FROM dual;
+
+ -- name: statspack
+ -- dialect: oracle
+ SELECT
+ :acknowledge_license AS license_acknowledged,
+ :include_diagnostics AS diagnostics_enabled,
+ 'STATS$SNAPSHOT' AS required_view,
+ 'Statspack access requires explicit opt-in and privileges.' AS warning
+ FROM dual
+ WHERE :include_diagnostics = 1
+ AND :acknowledge_license = 1;
+
+ -- name: vector_memory_pool
+ -- dialect: oracle
+ SELECT
+ pool AS "pool",
+ alloc_bytes AS "alloc_bytes",
+ used_bytes AS "used_bytes"
+ FROM v$vector_memory_pool;
+ ```
+
+??? code "`tables.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ t.owner AS schema_name,
+ t.table_name,
+ t.tablespace_name,
+ t.cluster_name,
+ t.iot_name,
+ t.status,
+ t.pct_free,
+ t.pct_used,
+ t.ini_trans,
+ t.max_trans,
+ t.logging,
+ t.num_rows,
+ t.blocks,
+ t.empty_blocks,
+ t.avg_space,
+ t.chain_cnt,
+ t.avg_row_len,
+ t.degree,
+ t.instances,
+ t.cache,
+ t.table_lock,
+ t.partitioned,
+ t.temporary,
+ t.secondary,
+ t.nested,
+ t.dropped,
+ t.read_only,
+ t.segment_created,
+ t.result_cache,
+ c.comments AS table_comment
+ FROM all_tables t
+ LEFT JOIN all_tab_comments c
+ ON c.owner = t.owner
+ AND c.table_name = t.table_name
+ WHERE t.owner = COALESCE(:schema_name, USER)
+ AND (:table_name IS NULL OR t.table_name = :table_name)
+ ORDER BY t.owner, t.table_name;
+
+ -- name: names_by_schema
+ -- dialect: oracle
+ SELECT
+ table_name
+ FROM all_tables
+ WHERE owner = COALESCE(:schema_name, USER)
+ ORDER BY table_name;
+ ```
+
+??? code "`triggers.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ trigger_name,
+ trigger_type,
+ triggering_event,
+ table_owner,
+ base_object_type,
+ table_name,
+ column_name,
+ referencing_names,
+ when_clause,
+ status,
+ description,
+ action_type,
+ trigger_body,
+ crossedition,
+ before_statement,
+ before_row,
+ after_row,
+ after_statement,
+ instead_of_row,
+ fire_once,
+ apply_server_only
+ FROM all_triggers
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:trigger_name IS NULL OR trigger_name = :trigger_name)
+ ORDER BY owner, trigger_name;
+ ```
+
+??? code "`version.sql` (4 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: oracle
+ SELECT
+ product AS "product",
+ version AS "version",
+ status AS "status"
+ FROM product_component_version
+ WHERE product LIKE 'Oracle%'
+ ORDER BY TO_NUMBER(REGEXP_SUBSTR(version, '^[0-9]+')) DESC, version DESC
+ FETCH FIRST 1 ROWS ONLY;
+
+ -- name: autonomous_service
+ -- dialect: oracle
+ SELECT sys_context('USERENV', 'CLOUD_SERVICE') AS "service" FROM dual;
+
+ -- name: compatible
+ -- dialect: oracle
+ SELECT value AS compatible
+ FROM v$parameter
+ WHERE name = 'compatible';
+
+ -- name: apex_release
+ -- dialect: oracle
+ SELECT
+ version_no AS "version_no",
+ api_compatibility AS "api_compatibility"
+ FROM apex_release;
+ ```
+
+??? code "`views.sql` (3 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: oracle
+ SELECT
+ owner AS schema_name,
+ view_name,
+ text_length,
+ text,
+ type_text_length,
+ type_text,
+ oid_text_length,
+ oid_text,
+ view_type_owner,
+ view_type,
+ superview_name,
+ editioning_view,
+ read_only,
+ container_data,
+ bequeath,
+ origin_con_id
+ FROM all_views
+ WHERE owner = COALESCE(:schema_name, USER)
+ AND (:view_name IS NULL OR view_name = :view_name)
+ ORDER BY owner, view_name;
+
+ -- name: json_duality_views
+ -- dialect: oracle
+ SELECT
+ view_name AS "view_name",
+ root_table_name AS "root_table_name",
+ root_table_owner AS "root_table_owner",
+ allow_insert AS "allow_insert",
+ allow_update AS "allow_update",
+ allow_delete AS "allow_delete"
+ FROM user_json_duality_views
+ ORDER BY view_name;
+
+ -- name: json_duality_views_by_schema
+ -- dialect: oracle
+ SELECT
+ view_name AS "view_name",
+ root_table_name AS "root_table_name",
+ root_table_owner AS "root_table_owner",
+ allow_insert AS "allow_insert",
+ allow_update AS "allow_update",
+ allow_delete AS "allow_delete"
+ FROM all_json_duality_views
+ WHERE (:schema_name IS NULL AND view_owner = SYS_CONTEXT('USERENV', 'CURRENT_SCHEMA'))
+ OR (:schema_name IS NOT NULL AND view_owner = :schema_name)
+ ORDER BY view_name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/postgres.md b/docs/reference/data-dictionary/dialects/postgres.md
new file mode 100644
index 000000000..e7ea7c44e
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/postgres.md
@@ -0,0 +1,984 @@
+# PostgreSQL Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("postgres")
+```
+
+Introspects `pg_catalog` (`pg_class`, `pg_attribute`, `pg_index`, `pg_constraint`, `pg_proc`, `pg_type`, `pg_extension`) and `information_schema` across `asyncpg`, `psycopg`, and `psqlpy`. See the [PostgreSQL Guide](../../../databases/postgres/index.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `postgres` |
+| **Default Schema** | `"public"` |
+| **Version Pattern** | `PostgreSQL (\d+)\.(\d+)(?:\.(\d+))?` |
+| **DDL Strategy** | AST synthesis + `pg_get_*def` catalog functions |
+| **Catalog `.sql` Files** | `21` files (`26` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `True` | `array` | `ARRAY` |
+| `supports_for_update` | `True` | `bigint` | `BIGINT` |
+| `supports_on_conflict` | `True` | `blob` | `BYTEA` |
+| `supports_prepared_statements` | `True` | `boolean` | `BOOLEAN` |
+| `supports_schemas` | `True` | `float` | `DOUBLE PRECISION` |
+| `supports_transactions` | `True` | `integer` | `INTEGER` |
+| `supports_update_from` | `True` | `json` | `JSONB` |
+| `supports_uuid` | `True` | `text` | `TEXT` |
+| `supports_cte` | `>= 8.4.0` | `timestamp` | `TIMESTAMP WITH TIME ZONE` |
+| `supports_json` | `>= 9.2.0` | `uuid` | `UUID` |
+| `supports_jsonb` | `>= 9.4.0` | `varchar` | `VARCHAR({length})` |
+| `supports_partitioning` | `>= 10.0.0` | - | - |
+| `supports_returning` | `>= 8.2.0` | - | - |
+| `supports_skip_locked` | `>= 9.5.0` | - | - |
+| `supports_upsert` | `>= 9.5.0` | - | - |
+| `supports_window_functions` | `>= 8.4.0` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.asyncpg.data_dictionary.AsyncpgDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.psycopg.data_dictionary.PsycopgSyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.psycopg.data_dictionary.PsycopgAsyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.psqlpy.data_dictionary.PsqlpyDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `postgres` dialect pack defines `26` named queries across `21` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/postgres/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `comments.sql` | `by_schema` | `:schema_name` |
+| `constraints.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `ddl.sql` | `by_object` | `:object_name`, `:object_type`, `:schema_name` |
+| `dependencies.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `extensions.sql` | `list` | *none* |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `materialized_views.sql` | `by_schema` | `:schema_name` |
+| `objects.sql` | `by_schema` | `:schema_name` |
+| `partitions.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `privileges.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `routines.sql` | `by_schema` | `:schema_name` |
+| `schemas.sql` | `list` | *none* |
+| `sequences.sql` | `by_schema` | `:schema_name` |
+| `system.sql` | `pg_stat_statements`, `settings`, `table_stats` | `:limit`, `:schema_name`, `:table_name` |
+| `tables.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `triggers.sql` | `by_schema` | `:schema_name`, `:table_name` |
+| `types.sql` | `by_schema` | `:schema_name` |
+| `version.sql` | `current` | *none* |
+| `views.sql` | `by_schema` | `:schema_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ WITH target_schema AS (
+ SELECT COALESCE(
+ :schema_name::text,
+ (
+ SELECT rn.nspname::text
+ FROM pg_catalog.pg_class rc
+ JOIN pg_catalog.pg_namespace rn ON rn.oid = rc.relnamespace
+ WHERE rc.oid = pg_catalog.to_regclass(pg_catalog.quote_ident(:table_name::text))
+ ),
+ current_schema()
+ ) AS schema_name
+ ),
+ table_constraints AS (
+ SELECT
+ con.conrelid,
+ unnest(con.conkey) AS attnum,
+ bool_or(con.contype = 'p') AS is_primary,
+ bool_or(con.contype = 'u') AS is_unique
+ FROM pg_catalog.pg_constraint con
+ JOIN pg_catalog.pg_class c ON c.oid = con.conrelid
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ JOIN target_schema ts ON ts.schema_name = n.nspname
+ WHERE con.contype IN ('p', 'u')
+ AND (:table_name::text IS NULL OR c.relname = :table_name)
+ GROUP BY con.conrelid, unnest(con.conkey)
+ )
+ SELECT
+ current_database()::text AS table_catalog,
+ n.nspname::text AS table_schema,
+ n.nspname::text AS schema_name,
+ c.relname::text AS table_name,
+ a.attname::text AS column_name,
+ a.attnum::integer AS ordinal_position,
+ pg_catalog.format_type(a.atttypid, a.atttypmod)::text AS data_type,
+ t.typname::text AS type_name,
+ CASE WHEN a.attnotnull THEN 'NO' ELSE 'YES' END AS is_nullable,
+ CASE WHEN a.attgenerated = '' THEN pg_catalog.pg_get_expr(d.adbin, d.adrelid)::text ELSE NULL END AS column_default,
+ CASE
+ WHEN a.attidentity = 'a' THEN 'ALWAYS'
+ WHEN a.attidentity = 'd' THEN 'BY DEFAULT'
+ ELSE NULL
+ END AS identity_generation,
+ CASE WHEN a.attidentity IN ('a', 'd') THEN 1 ELSE 0 END AS is_identity,
+ CASE WHEN a.attgenerated <> '' THEN 1 ELSE 0 END AS is_generated,
+ CASE WHEN a.attgenerated <> '' THEN pg_catalog.pg_get_expr(d.adbin, d.adrelid)::text ELSE NULL END AS generation_expression,
+ col_description(c.oid, a.attnum)::text AS comment,
+ COALESCE(tc.is_primary, false) AS is_primary,
+ COALESCE(tc.is_unique, false) AS is_unique,
+ pg_catalog.pg_get_serial_sequence(
+ pg_catalog.quote_ident(n.nspname) || '.' || pg_catalog.quote_ident(c.relname), a.attname
+ )::text AS sequence_name
+ FROM pg_catalog.pg_attribute a
+ JOIN pg_catalog.pg_class c ON c.oid = a.attrelid
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ JOIN target_schema ts ON ts.schema_name = n.nspname
+ JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
+ LEFT JOIN pg_catalog.pg_attrdef d ON d.adrelid = a.attrelid AND d.adnum = a.attnum
+ LEFT JOIN table_constraints tc ON tc.conrelid = c.oid AND tc.attnum = a.attnum
+ WHERE c.relkind IN ('r', 'p', 'v', 'm', 'f')
+ AND (:table_name::text IS NULL OR c.relname = :table_name)
+ AND a.attnum > 0
+ AND NOT a.attisdropped
+ ORDER BY n.nspname, c.relname, a.attnum;
+
+ -- name: by_table
+ -- dialect: postgres
+ WITH target_schema AS (
+ SELECT COALESCE(
+ :schema_name::text,
+ (
+ SELECT rn.nspname::text
+ FROM pg_catalog.pg_class rc
+ JOIN pg_catalog.pg_namespace rn ON rn.oid = rc.relnamespace
+ WHERE rc.oid = pg_catalog.to_regclass(pg_catalog.quote_ident(:table_name::text))
+ ),
+ current_schema()
+ ) AS schema_name
+ ),
+ table_constraints AS (
+ SELECT
+ con.conrelid,
+ unnest(con.conkey) AS attnum,
+ bool_or(con.contype = 'p') AS is_primary,
+ bool_or(con.contype = 'u') AS is_unique
+ FROM pg_catalog.pg_constraint con
+ JOIN pg_catalog.pg_class c ON c.oid = con.conrelid
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ JOIN target_schema ts ON ts.schema_name = n.nspname
+ WHERE con.contype IN ('p', 'u')
+ AND c.relname = :table_name::text
+ GROUP BY con.conrelid, unnest(con.conkey)
+ )
+ SELECT
+ current_database()::text AS table_catalog,
+ n.nspname::text AS table_schema,
+ n.nspname::text AS schema_name,
+ c.relname::text AS table_name,
+ a.attname::text AS column_name,
+ a.attnum::integer AS ordinal_position,
+ pg_catalog.format_type(a.atttypid, a.atttypmod)::text AS data_type,
+ t.typname::text AS type_name,
+ CASE WHEN a.attnotnull THEN 'NO' ELSE 'YES' END AS is_nullable,
+ CASE WHEN a.attgenerated = '' THEN pg_catalog.pg_get_expr(d.adbin, d.adrelid)::text ELSE NULL END AS column_default,
+ CASE
+ WHEN a.attidentity = 'a' THEN 'ALWAYS'
+ WHEN a.attidentity = 'd' THEN 'BY DEFAULT'
+ ELSE NULL
+ END AS identity_generation,
+ CASE WHEN a.attidentity IN ('a', 'd') THEN 1 ELSE 0 END AS is_identity,
+ CASE WHEN a.attgenerated <> '' THEN 1 ELSE 0 END AS is_generated,
+ CASE WHEN a.attgenerated <> '' THEN pg_catalog.pg_get_expr(d.adbin, d.adrelid)::text ELSE NULL END AS generation_expression,
+ col_description(c.oid, a.attnum)::text AS comment,
+ COALESCE(tc.is_primary, false) AS is_primary,
+ COALESCE(tc.is_unique, false) AS is_unique,
+ pg_catalog.pg_get_serial_sequence(
+ pg_catalog.quote_ident(n.nspname) || '.' || pg_catalog.quote_ident(c.relname), a.attname
+ )::text AS sequence_name
+ FROM pg_catalog.pg_attribute a
+ JOIN pg_catalog.pg_class c ON a.attrelid = c.oid
+ JOIN pg_catalog.pg_namespace n ON c.relnamespace = n.oid
+ JOIN target_schema ts ON ts.schema_name = n.nspname
+ JOIN pg_catalog.pg_type t ON t.oid = a.atttypid
+ LEFT JOIN pg_catalog.pg_attrdef d ON a.attrelid = d.adrelid AND a.attnum = d.adnum
+ LEFT JOIN table_constraints tc ON tc.conrelid = c.oid AND tc.attnum = a.attnum
+ WHERE c.relname = :table_name::text
+ AND a.attnum > 0
+ AND NOT a.attisdropped
+ ORDER BY a.attnum;
+ ```
+
+??? code "`comments.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ n.nspname::text AS schema_name,
+ c.relname::text AS object_name,
+ 'table'::text AS object_type,
+ d.description::text AS comment,
+ c.oid::text AS object_oid
+ FROM pg_catalog.pg_description d
+ JOIN pg_catalog.pg_class c ON c.oid = d.objoid
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE n.nspname = :schema_name
+ AND d.objsubid = 0
+ UNION ALL
+ SELECT
+ n.nspname::text AS schema_name,
+ c.relname::text || '.' || a.attname::text AS object_name,
+ 'column'::text AS object_type,
+ d.description::text AS comment,
+ c.oid::text AS object_oid
+ FROM pg_catalog.pg_description d
+ JOIN pg_catalog.pg_class c ON c.oid = d.objoid
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ JOIN pg_catalog.pg_attribute a ON a.attrelid = c.oid AND a.attnum = d.objsubid
+ WHERE n.nspname = :schema_name
+ AND d.objsubid > 0
+ ORDER BY schema_name, object_type, object_name;
+ ```
+
+??? code "`constraints.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ current_database()::text AS constraint_catalog,
+ n.nspname::text AS constraint_schema,
+ n.nspname::text AS schema_name,
+ c.relname::text AS table_name,
+ con.conname::text AS constraint_name,
+ CASE con.contype
+ WHEN 'c' THEN 'CHECK'
+ WHEN 'f' THEN 'FOREIGN KEY'
+ WHEN 'p' THEN 'PRIMARY KEY'
+ WHEN 'u' THEN 'UNIQUE'
+ WHEN 'x' THEN 'EXCLUDE'
+ ELSE con.contype::text
+ END AS constraint_type,
+ pg_catalog.pg_get_constraintdef(con.oid, true)::text AS definition,
+ ARRAY(
+ SELECT att.attname::text
+ FROM unnest(con.conkey) WITH ORDINALITY AS keys(attnum, ord)
+ JOIN pg_catalog.pg_attribute att ON att.attrelid = con.conrelid AND att.attnum = keys.attnum
+ ORDER BY keys.ord
+ )::text[] AS columns,
+ ref_ns.nspname::text AS referenced_schema,
+ ref.relname::text AS referenced_table,
+ ARRAY(
+ SELECT att.attname::text
+ FROM unnest(con.confkey) WITH ORDINALITY AS keys(attnum, ord)
+ JOIN pg_catalog.pg_attribute att ON att.attrelid = con.confrelid AND att.attnum = keys.attnum
+ ORDER BY keys.ord
+ )::text[] AS referenced_columns,
+ con.condeferrable AS is_deferrable,
+ con.condeferred AS initially_deferred,
+ dep.refobjid IS NOT NULL AS is_extension_owned
+ FROM pg_catalog.pg_constraint con
+ JOIN pg_catalog.pg_namespace n ON n.oid = con.connamespace
+ LEFT JOIN pg_catalog.pg_class c ON c.oid = con.conrelid
+ LEFT JOIN pg_catalog.pg_class ref ON ref.oid = con.confrelid
+ LEFT JOIN pg_catalog.pg_namespace ref_ns ON ref_ns.oid = ref.relnamespace
+ LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = con.oid AND dep.deptype = 'e'
+ WHERE n.nspname = :schema_name
+ AND (:table_name::text IS NULL OR c.relname = :table_name)
+ ORDER BY n.nspname, c.relname, con.conname;
+ ```
+
+??? code "`ddl.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_object
+ -- dialect: postgres
+ WITH extension_deps AS (
+ SELECT DISTINCT
+ classid,
+ objid
+ FROM pg_catalog.pg_depend
+ WHERE deptype = 'e'
+ )
+ SELECT
+ n.nspname::text AS schema_name,
+ c.relname::text AS object_name,
+ CASE c.relkind
+ WHEN 'v' THEN pg_catalog.pg_get_viewdef(c.oid, true)::text
+ WHEN 'm' THEN pg_catalog.pg_get_viewdef(c.oid, true)::text
+ WHEN 'S' THEN 'CREATE SEQUENCE ' || pg_catalog.quote_ident(n.nspname) || '.' || pg_catalog.quote_ident(c.relname)
+ ELSE NULL::text
+ END AS ddl,
+ CASE c.relkind
+ WHEN 'v' THEN 'native'
+ WHEN 'm' THEN 'native'
+ WHEN 'S' THEN 'generated'
+ ELSE 'unsupported'
+ END AS fidelity,
+ dep.objid IS NOT NULL AS is_extension_owned
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ LEFT JOIN extension_deps dep
+ ON dep.classid = 'pg_catalog.pg_class'::regclass
+ AND dep.objid = c.oid
+ WHERE n.nspname = :schema_name
+ AND c.relname = :object_name
+ AND (:object_type::text IS NULL OR c.relkind = CASE :object_type
+ WHEN 'view' THEN 'v'
+ WHEN 'materialized_view' THEN 'm'
+ WHEN 'sequence' THEN 'S'
+ WHEN 'table' THEN 'r'
+ ELSE c.relkind
+ END)
+ UNION ALL
+ SELECT
+ n.nspname::text AS schema_name,
+ p.proname::text AS object_name,
+ pg_catalog.pg_get_functiondef(p.oid)::text AS ddl,
+ 'native'::text AS fidelity,
+ dep.objid IS NOT NULL AS is_extension_owned
+ FROM pg_catalog.pg_proc p
+ JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
+ LEFT JOIN extension_deps dep
+ ON dep.classid = 'pg_catalog.pg_proc'::regclass
+ AND dep.objid = p.oid
+ WHERE n.nspname = :schema_name
+ AND p.proname = :object_name
+ AND (:object_type::text IS NULL OR :object_type IN ('routine', 'function', 'procedure'))
+ ORDER BY schema_name, object_name;
+ ```
+
+??? code "`dependencies.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ src_ns.nspname::text AS schema_name,
+ src.relname::text AS object_name,
+ src.relkind::text AS object_type,
+ target_ns.nspname::text AS referenced_schema,
+ target.relname::text AS referenced_object,
+ target.relkind::text AS referenced_object_type,
+ dep.deptype::text AS dependency_type,
+ ext.extname::text AS extension_name,
+ dep.objid::text AS object_oid,
+ dep.refobjid::text AS referenced_oid
+ FROM pg_catalog.pg_depend dep
+ JOIN pg_catalog.pg_class src ON src.oid = dep.objid
+ JOIN pg_catalog.pg_namespace src_ns ON src_ns.oid = src.relnamespace
+ LEFT JOIN pg_catalog.pg_class target ON target.oid = dep.refobjid
+ LEFT JOIN pg_catalog.pg_namespace target_ns ON target_ns.oid = target.relnamespace
+ LEFT JOIN pg_catalog.pg_extension ext ON ext.oid = dep.refobjid AND dep.deptype = 'e'
+ WHERE src_ns.nspname = :schema_name
+ AND (:object_name::text IS NULL OR src.relname = :object_name)
+ UNION ALL
+ SELECT
+ src_ns.nspname::text AS schema_name,
+ src.relname::text AS object_name,
+ 'table'::text AS object_type,
+ target_ns.nspname::text AS referenced_schema,
+ target.relname::text AS referenced_object,
+ 'table'::text AS referenced_object_type,
+ 'foreign_key'::text AS dependency_type,
+ NULL::text AS extension_name,
+ fk.oid::text AS object_oid,
+ fk.confrelid::text AS referenced_oid
+ FROM pg_catalog.pg_constraint fk
+ JOIN pg_catalog.pg_class src ON src.oid = fk.conrelid
+ JOIN pg_catalog.pg_namespace src_ns ON src_ns.oid = src.relnamespace
+ JOIN pg_catalog.pg_class target ON target.oid = fk.confrelid
+ JOIN pg_catalog.pg_namespace target_ns ON target_ns.oid = target.relnamespace
+ WHERE fk.contype = 'f'
+ AND src_ns.nspname = :schema_name
+ AND (:object_name::text IS NULL OR src.relname = :object_name)
+ ORDER BY schema_name, object_name, dependency_type, referenced_object;
+ ```
+
+??? code "`extensions.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: postgres
+ SELECT
+ ext.extname::text AS extension_name,
+ ext.extversion::text AS extension_version,
+ n.nspname::text AS schema_name,
+ ext.oid::text AS extension_oid,
+ pg_catalog.obj_description(ext.oid, 'pg_extension')::text AS comment
+ FROM pg_catalog.pg_extension ext
+ JOIN pg_catalog.pg_namespace n ON n.oid = ext.extnamespace
+ ORDER BY ext.extname;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: postgres
+ SELECT
+ c.relname::text AS table_name,
+ att.attname::text AS column_name,
+ ref.relname::text AS referenced_table_name,
+ ref_att.attname::text AS referenced_column_name,
+ con.conname::text AS constraint_name,
+ n.nspname::text AS table_schema,
+ ref_ns.nspname::text AS referenced_table_schema,
+ CASE con.confdeltype
+ WHEN 'a' THEN 'NO ACTION'
+ WHEN 'r' THEN 'RESTRICT'
+ WHEN 'c' THEN 'CASCADE'
+ WHEN 'n' THEN 'SET NULL'
+ WHEN 'd' THEN 'SET DEFAULT'
+ END AS on_delete,
+ CASE con.confupdtype
+ WHEN 'a' THEN 'NO ACTION'
+ WHEN 'r' THEN 'RESTRICT'
+ WHEN 'c' THEN 'CASCADE'
+ WHEN 'n' THEN 'SET NULL'
+ WHEN 'd' THEN 'SET DEFAULT'
+ END AS on_update
+ FROM pg_catalog.pg_constraint AS con
+ JOIN pg_catalog.pg_class AS c ON c.oid = con.conrelid
+ JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace
+ JOIN pg_catalog.pg_class AS ref ON ref.oid = con.confrelid
+ JOIN pg_catalog.pg_namespace AS ref_ns ON ref_ns.oid = ref.relnamespace
+ CROSS JOIN LATERAL unnest(con.conkey, con.confkey) WITH ORDINALITY AS keys(attnum, ref_attnum, ord)
+ JOIN pg_catalog.pg_attribute AS att ON att.attrelid = c.oid AND att.attnum = keys.attnum
+ JOIN pg_catalog.pg_attribute AS ref_att ON ref_att.attrelid = ref.oid AND ref_att.attnum = keys.ref_attnum
+ WHERE con.contype = 'f'
+ AND (:schema_name::text IS NULL OR n.nspname = :schema_name)
+ AND (:table_name::text IS NULL OR c.relname = :table_name)
+ ORDER BY c.relname, con.conname, keys.ord;
+
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ c.relname::text AS table_name,
+ att.attname::text AS column_name,
+ ref.relname::text AS referenced_table_name,
+ ref_att.attname::text AS referenced_column_name,
+ con.conname::text AS constraint_name,
+ n.nspname::text AS table_schema,
+ ref_ns.nspname::text AS referenced_table_schema,
+ CASE con.confdeltype
+ WHEN 'a' THEN 'NO ACTION'
+ WHEN 'r' THEN 'RESTRICT'
+ WHEN 'c' THEN 'CASCADE'
+ WHEN 'n' THEN 'SET NULL'
+ WHEN 'd' THEN 'SET DEFAULT'
+ END AS on_delete,
+ CASE con.confupdtype
+ WHEN 'a' THEN 'NO ACTION'
+ WHEN 'r' THEN 'RESTRICT'
+ WHEN 'c' THEN 'CASCADE'
+ WHEN 'n' THEN 'SET NULL'
+ WHEN 'd' THEN 'SET DEFAULT'
+ END AS on_update
+ FROM pg_catalog.pg_constraint AS con
+ JOIN pg_catalog.pg_class AS c ON c.oid = con.conrelid
+ JOIN pg_catalog.pg_namespace AS n ON n.oid = c.relnamespace
+ JOIN pg_catalog.pg_class AS ref ON ref.oid = con.confrelid
+ JOIN pg_catalog.pg_namespace AS ref_ns ON ref_ns.oid = ref.relnamespace
+ CROSS JOIN LATERAL unnest(con.conkey, con.confkey) WITH ORDINALITY AS keys(attnum, ref_attnum, ord)
+ JOIN pg_catalog.pg_attribute AS att ON att.attrelid = c.oid AND att.attnum = keys.attnum
+ JOIN pg_catalog.pg_attribute AS ref_att ON ref_att.attrelid = ref.oid AND ref_att.attnum = keys.ref_attnum
+ WHERE con.contype = 'f'
+ AND (:schema_name::text IS NULL OR n.nspname = :schema_name)
+ ORDER BY c.relname, con.conname, keys.ord;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ tn.nspname::text AS schema_name,
+ tbl.relname::text AS table_name,
+ idx.relname::text AS index_name,
+ am.amname::text AS access_method,
+ ix.indisunique AS is_unique,
+ ix.indisprimary AS is_primary,
+ ix.indisexclusion AS is_exclusion,
+ ix.indisvalid AS is_valid,
+ pg_catalog.pg_get_expr(ix.indpred, ix.indrelid)::text IS NOT NULL AS is_partial,
+ pg_catalog.pg_get_expr(ix.indpred, ix.indrelid)::text AS predicate,
+ pg_catalog.pg_get_indexdef(idx.oid)::text AS definition,
+ ARRAY(
+ SELECT COALESCE(att.attname::text, pg_catalog.pg_get_indexdef(idx.oid, keys.ord::integer, true)::text)
+ FROM unnest(ix.indkey) WITH ORDINALITY AS keys(attnum, ord)
+ LEFT JOIN pg_catalog.pg_attribute att ON att.attrelid = tbl.oid AND att.attnum = keys.attnum
+ WHERE keys.ord <= ix.indnkeyatts
+ ORDER BY keys.ord
+ )::text[] AS columns,
+ ARRAY(
+ SELECT pg_catalog.pg_get_indexdef(idx.oid, keys.ord::integer, true)::text
+ FROM unnest(ix.indkey) WITH ORDINALITY AS keys(attnum, ord)
+ WHERE keys.ord > ix.indnkeyatts
+ ORDER BY keys.ord
+ )::text[] AS included_columns,
+ ARRAY(
+ SELECT opc.opcname::text
+ FROM unnest(ix.indclass) WITH ORDINALITY AS classes(opclass_oid, ord)
+ JOIN pg_catalog.pg_opclass opc ON opc.oid = classes.opclass_oid
+ ORDER BY classes.ord
+ )::text[] AS opclasses,
+ dep.refobjid IS NOT NULL AS is_extension_owned
+ FROM pg_catalog.pg_index ix
+ JOIN pg_catalog.pg_class tbl ON tbl.oid = ix.indrelid
+ JOIN pg_catalog.pg_namespace tn ON tn.oid = tbl.relnamespace
+ JOIN pg_catalog.pg_class idx ON idx.oid = ix.indexrelid
+ JOIN pg_catalog.pg_am am ON am.oid = idx.relam
+ LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = idx.oid AND dep.deptype = 'e'
+ WHERE tn.nspname = :schema_name
+ AND (:table_name::text IS NULL OR tbl.relname = :table_name)
+ ORDER BY tn.nspname, tbl.relname, idx.relname;
+
+ -- name: by_table
+ -- dialect: postgres
+ SELECT
+ i.relname as index_name,
+ t.relname as table_name,
+ ix.indisunique as is_unique,
+ ix.indisprimary as is_primary,
+ array_agg(a.attname::text ORDER BY array_position(ix.indkey, a.attnum)) as columns
+ FROM
+ pg_class t,
+ pg_class i,
+ pg_index ix,
+ pg_attribute a,
+ pg_namespace n
+ WHERE
+ t.oid = ix.indrelid
+ AND i.oid = ix.indexrelid
+ AND a.attrelid = t.oid
+ AND a.attnum = ANY(ix.indkey)
+ AND t.relkind = 'r'
+ AND t.relnamespace = n.oid
+ AND n.nspname = :schema_name
+ AND t.relname = :table_name
+ GROUP BY
+ t.relname,
+ i.relname,
+ ix.indisunique,
+ ix.indisprimary;
+ ```
+
+??? code "`materialized_views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ n.nspname::text AS schema_name,
+ c.relname::text AS view_name,
+ c.oid::text AS object_oid,
+ pg_catalog.pg_get_viewdef(c.oid, true)::text AS definition,
+ r.rolname::text AS owner_name,
+ pg_catalog.obj_description(c.oid, 'pg_class')::text AS comment,
+ dep.refobjid IS NOT NULL AS is_extension_owned
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ LEFT JOIN pg_catalog.pg_roles r ON r.oid = c.relowner
+ LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = c.oid AND dep.deptype = 'e'
+ WHERE n.nspname = :schema_name
+ AND c.relkind = 'm'
+ ORDER BY n.nspname, c.relname;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ current_database()::text AS catalog_name,
+ n.nspname::text AS schema_name,
+ c.relname::text AS object_name,
+ CASE c.relkind
+ WHEN 'r' THEN 'table'
+ WHEN 'p' THEN 'partitioned_table'
+ WHEN 'v' THEN 'view'
+ WHEN 'm' THEN 'materialized_view'
+ WHEN 'S' THEN 'sequence'
+ WHEN 'f' THEN 'foreign_table'
+ ELSE c.relkind::text
+ END AS object_type,
+ c.oid::text AS object_oid,
+ r.rolname::text AS owner_name,
+ c.relispartition AS is_partition,
+ pg_catalog.obj_description(c.oid, 'pg_class')::text AS comment
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ LEFT JOIN pg_catalog.pg_roles r ON r.oid = c.relowner
+ WHERE n.nspname = :schema_name
+ AND c.relkind IN ('r', 'p', 'v', 'm', 'S', 'f')
+ ORDER BY n.nspname, object_type, c.relname;
+ ```
+
+??? code "`partitions.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ parent_ns.nspname::text AS schema_name,
+ parent.relname::text AS parent_table,
+ child_ns.nspname::text AS partition_schema,
+ child.relname::text AS partition_name,
+ pg_catalog.pg_get_expr(child.relpartbound, child.oid, true)::text AS partition_bound,
+ pt.partstrat::text AS partition_strategy,
+ inherit.inhseqno AS inherit_sequence
+ FROM pg_catalog.pg_inherits inherit
+ JOIN pg_catalog.pg_class parent ON parent.oid = inherit.inhparent
+ JOIN pg_catalog.pg_namespace parent_ns ON parent_ns.oid = parent.relnamespace
+ JOIN pg_catalog.pg_class child ON child.oid = inherit.inhrelid
+ JOIN pg_catalog.pg_namespace child_ns ON child_ns.oid = child.relnamespace
+ LEFT JOIN pg_catalog.pg_partitioned_table pt ON pt.partrelid = parent.oid
+ WHERE parent_ns.nspname = :schema_name
+ AND (:table_name::text IS NULL OR parent.relname = :table_name OR child.relname = :table_name)
+ ORDER BY parent_ns.nspname, parent.relname, inherit.inhseqno, child.relname;
+ ```
+
+??? code "`privileges.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ n.nspname::text AS schema_name,
+ c.relname::text AS object_name,
+ CASE c.relkind
+ WHEN 'r' THEN 'table'
+ WHEN 'p' THEN 'partitioned_table'
+ WHEN 'v' THEN 'view'
+ WHEN 'm' THEN 'materialized_view'
+ WHEN 'S' THEN 'sequence'
+ ELSE c.relkind::text
+ END AS object_type,
+ grant_row.grantee::text AS grantee,
+ grant_row.privilege_type::text AS privilege_type,
+ grant_row.is_grantable AS is_grantable
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ CROSS JOIN LATERAL pg_catalog.aclexplode(COALESCE(c.relacl, pg_catalog.acldefault('r', c.relowner))) AS acl
+ LEFT JOIN pg_catalog.pg_roles grantee ON grantee.oid = acl.grantee
+ CROSS JOIN LATERAL (
+ SELECT
+ COALESCE(grantee.rolname, 'PUBLIC') AS grantee,
+ acl.privilege_type,
+ acl.is_grantable
+ ) AS grant_row
+ WHERE n.nspname = :schema_name
+ AND (:object_name::text IS NULL OR c.relname = :object_name)
+ ORDER BY n.nspname, c.relname, grant_row.grantee, grant_row.privilege_type;
+ ```
+
+??? code "`routines.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ n.nspname::text AS schema_name,
+ p.proname::text AS routine_name,
+ p.oid::text AS routine_oid,
+ CASE p.prokind
+ WHEN 'f' THEN 'function'
+ WHEN 'p' THEN 'procedure'
+ WHEN 'a' THEN 'aggregate'
+ WHEN 'w' THEN 'window'
+ ELSE p.prokind::text
+ END AS routine_type,
+ pg_catalog.pg_get_function_arguments(p.oid)::text AS arguments,
+ pg_catalog.pg_get_function_result(p.oid)::text AS result_type,
+ lang.lanname::text AS language_name,
+ pg_catalog.pg_get_functiondef(p.oid)::text AS definition,
+ pg_catalog.obj_description(p.oid, 'pg_proc')::text AS comment,
+ dep.refobjid IS NOT NULL AS is_extension_owned
+ FROM pg_catalog.pg_proc p
+ JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
+ JOIN pg_catalog.pg_language lang ON lang.oid = p.prolang
+ LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = p.oid AND dep.deptype = 'e'
+ WHERE n.nspname = :schema_name
+ ORDER BY n.nspname, p.proname, p.oid;
+ ```
+
+??? code "`schemas.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: postgres
+ SELECT
+ current_database()::text AS catalog_name,
+ n.oid::text AS schema_oid,
+ n.nspname::text AS schema_name,
+ r.rolname::text AS owner_name,
+ pg_catalog.obj_description(n.oid, 'pg_namespace')::text AS comment
+ FROM pg_catalog.pg_namespace n
+ LEFT JOIN pg_catalog.pg_roles r ON r.oid = n.nspowner
+ WHERE n.nspname NOT LIKE 'pg\_%'
+ AND n.nspname <> 'information_schema'
+ ORDER BY n.nspname;
+ ```
+
+??? code "`sequences.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ WITH extension_deps AS (
+ SELECT DISTINCT objid
+ FROM pg_catalog.pg_depend
+ WHERE classid = 'pg_catalog.pg_class'::regclass
+ AND deptype = 'e'
+ ),
+ identity_deps AS (
+ SELECT DISTINCT objid
+ FROM pg_catalog.pg_depend
+ WHERE classid = 'pg_catalog.pg_class'::regclass
+ AND deptype = 'i'
+ )
+ SELECT
+ n.nspname::text AS schema_name,
+ c.relname::text AS sequence_name,
+ s.seqstart AS start_value,
+ s.seqmin AS minimum_value,
+ s.seqmax AS maximum_value,
+ s.seqincrement AS increment,
+ s.seqcycle AS cycles,
+ pg_catalog.obj_description(c.oid, 'pg_class')::text AS comment,
+ ext_dep.objid IS NOT NULL AS is_extension_owned,
+ id_dep.objid IS NOT NULL AS is_identity_owned
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ JOIN pg_catalog.pg_sequence s ON s.seqrelid = c.oid
+ LEFT JOIN extension_deps AS ext_dep ON ext_dep.objid = c.oid
+ LEFT JOIN identity_deps AS id_dep ON id_dep.objid = c.oid
+ WHERE n.nspname = :schema_name
+ ORDER BY n.nspname, c.relname;
+ ```
+
+??? code "`system.sql` (3 named queries)"
+
+ ```sql
+ -- name: pg_stat_statements
+ -- dialect: postgres
+ SELECT
+ userid::text AS user_oid,
+ dbid::text AS database_oid,
+ queryid::text AS query_id,
+ query::text AS query_text,
+ calls,
+ total_exec_time,
+ mean_exec_time,
+ rows
+ FROM pg_catalog.pg_stat_statements
+ ORDER BY total_exec_time DESC
+ LIMIT :limit;
+
+ -- name: settings
+ -- dialect: postgres
+ SELECT
+ name::text AS setting_name,
+ setting::text AS setting_value,
+ unit::text AS unit,
+ category::text AS category,
+ context::text AS context,
+ vartype::text AS value_type,
+ source::text AS source,
+ boot_val::text AS boot_value,
+ reset_val::text AS reset_value,
+ pending_restart
+ FROM pg_catalog.pg_settings
+ ORDER BY name;
+
+ -- name: table_stats
+ -- dialect: postgres
+ SELECT
+ schemaname::text AS schema_name,
+ relname::text AS table_name,
+ seq_scan,
+ seq_tup_read,
+ idx_scan,
+ idx_tup_fetch,
+ n_tup_ins,
+ n_tup_upd,
+ n_tup_del,
+ n_live_tup,
+ n_dead_tup,
+ vacuum_count,
+ autovacuum_count,
+ analyze_count,
+ autoanalyze_count
+ FROM pg_catalog.pg_stat_user_tables
+ WHERE (:schema_name::text IS NULL OR schemaname = :schema_name)
+ AND (:table_name::text IS NULL OR relname = :table_name)
+ ORDER BY schemaname, relname;
+ ```
+
+??? code "`tables.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ WITH RECURSIVE dependency_tree AS (
+ SELECT
+ c.oid,
+ n.nspname::text AS schema_name,
+ c.relname::text AS table_name,
+ 0 AS dependency_level,
+ ARRAY[c.oid] AS path
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ WHERE n.nspname = :schema_name
+ AND (:table_name::text IS NULL OR c.relname = :table_name)
+ AND c.relkind IN ('r', 'p')
+ AND NOT EXISTS (
+ SELECT 1
+ FROM pg_catalog.pg_constraint fk
+ WHERE fk.conrelid = c.oid
+ AND fk.contype = 'f'
+ AND fk.confrelid <> c.oid
+ )
+
+ UNION ALL
+
+ SELECT
+ child.oid,
+ child_ns.nspname::text AS schema_name,
+ child.relname::text AS table_name,
+ parent.dependency_level + 1,
+ parent.path || child.oid
+ FROM pg_catalog.pg_constraint fk
+ JOIN pg_catalog.pg_class child ON child.oid = fk.conrelid
+ JOIN pg_catalog.pg_namespace child_ns ON child_ns.oid = child.relnamespace
+ JOIN dependency_tree parent ON parent.oid = fk.confrelid
+ WHERE fk.contype = 'f'
+ AND fk.confrelid <> fk.conrelid
+ AND child_ns.nspname = :schema_name
+ AND (:table_name::text IS NULL OR child.relname = :table_name)
+ AND NOT child.oid = ANY(parent.path)
+ ),
+ dependency_levels AS (
+ SELECT
+ oid,
+ MAX(dependency_level) AS dependency_level
+ FROM dependency_tree
+ GROUP BY oid
+ )
+ SELECT
+ current_database()::text AS table_catalog,
+ n.nspname::text AS table_schema,
+ n.nspname::text AS schema_name,
+ c.relname::text AS table_name,
+ CASE c.relkind WHEN 'p' THEN 'PARTITIONED TABLE' ELSE 'BASE TABLE' END AS table_type,
+ COALESCE(t.dependency_level, 0) AS dependency_level,
+ COALESCE(t.dependency_level, 0) AS level,
+ c.reltuples::bigint AS estimated_row_count,
+ c.relispartition AS is_partition,
+ r.rolname::text AS owner_name,
+ pg_catalog.obj_description(c.oid, 'pg_class')::text AS comment
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ LEFT JOIN pg_catalog.pg_roles r ON r.oid = c.relowner
+ LEFT JOIN dependency_levels t ON t.oid = c.oid
+ WHERE n.nspname = :schema_name
+ AND (:table_name::text IS NULL OR c.relname = :table_name)
+ AND c.relkind IN ('r', 'p')
+ ORDER BY dependency_level, c.relname;
+ ```
+
+??? code "`triggers.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ n.nspname::text AS schema_name,
+ c.relname::text AS table_name,
+ tg.tgname::text AS trigger_name,
+ p.proname::text AS function_name,
+ pg_catalog.pg_get_triggerdef(tg.oid, true)::text AS definition,
+ NOT tg.tgenabled = 'D' AS is_enabled,
+ pg_catalog.obj_description(tg.oid, 'pg_trigger')::text AS comment,
+ dep.refobjid IS NOT NULL AS is_extension_owned
+ FROM pg_catalog.pg_trigger tg
+ JOIN pg_catalog.pg_class c ON c.oid = tg.tgrelid
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ JOIN pg_catalog.pg_proc p ON p.oid = tg.tgfoid
+ LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = tg.oid AND dep.deptype = 'e'
+ WHERE n.nspname = :schema_name
+ AND (:table_name::text IS NULL OR c.relname = :table_name)
+ AND NOT tg.tgisinternal
+ ORDER BY n.nspname, c.relname, tg.tgname;
+ ```
+
+??? code "`types.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ n.nspname::text AS schema_name,
+ t.typname::text AS type_name,
+ 'ENUM'::text AS logical_type,
+ array_agg(e.enumlabel::text ORDER BY e.enumsortorder) AS labels
+ FROM pg_catalog.pg_type AS t
+ JOIN pg_catalog.pg_namespace AS n ON n.oid = t.typnamespace
+ JOIN pg_catalog.pg_enum AS e ON e.enumtypid = t.oid
+ WHERE t.typtype = 'e'
+ AND n.nspname = :schema_name
+ AND NOT EXISTS (
+ SELECT 1
+ FROM pg_catalog.pg_depend AS dep
+ WHERE dep.classid = 'pg_catalog.pg_type'::regclass
+ AND dep.objid = t.oid
+ AND dep.deptype = 'e'
+ )
+ GROUP BY n.nspname, t.typname
+ ORDER BY t.typname;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: postgres
+ SELECT version();
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: postgres
+ SELECT
+ n.nspname::text AS schema_name,
+ c.relname::text AS view_name,
+ c.oid::text AS object_oid,
+ pg_catalog.pg_get_viewdef(c.oid, true)::text AS definition,
+ r.rolname::text AS owner_name,
+ pg_catalog.obj_description(c.oid, 'pg_class')::text AS comment,
+ dep.refobjid IS NOT NULL AS is_extension_owned
+ FROM pg_catalog.pg_class c
+ JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
+ LEFT JOIN pg_catalog.pg_roles r ON r.oid = c.relowner
+ LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = c.oid AND dep.deptype = 'e'
+ WHERE n.nspname = :schema_name
+ AND c.relkind = 'v'
+ ORDER BY n.nspname, c.relname;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/spanner.md b/docs/reference/data-dictionary/dialects/spanner.md
new file mode 100644
index 000000000..46aabb360
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/spanner.md
@@ -0,0 +1,277 @@
+# Google Cloud Spanner Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("spanner")
+```
+
+Provides dual SQL catalog packs (`googlesql` and `postgresql`) introspecting `INFORMATION_SCHEMA` tables, columns, indexes (`STORING`, `SEARCH`, `VECTOR`), foreign keys, parent-child interleaving, and row deletion policies. See the [Google Cloud Spanner Guide](../../../databases/spanner.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `spanner` |
+| **Default Schema** | `None` |
+| **Version Pattern** | `.*` |
+| **DDL Strategy** | AST synthesis with `INTERLEAVE IN PARENT` & `ROW DELETION POLICY` (`GoogleSQL` & `PostgreSQL` modes) |
+| **Catalog `.sql` Files** | `5` files (`8` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_for_update` | `False` | `array` | `ARRAY` |
+| `supports_generators` | `False` | `bigint` | `INT64` |
+| `supports_index_clustering` | `True` | `bignumeric` | `NUMERIC` |
+| `supports_interleaved_tables` | `True` | `blob` | `BYTES(MAX)` |
+| `supports_json` | `True` | `boolean` | `BOOL` |
+| `supports_on_conflict` | `True` | `float` | `FLOAT64` |
+| `supports_skip_locked` | `False` | `float32` | `FLOAT32` |
+| `supports_update_from` | `False` | `integer` | `INT64` |
+| - | - | `json` | `JSON` |
+| - | - | `numeric` | `NUMERIC` |
+| - | - | `text` | `STRING(MAX)` |
+| - | - | `timestamp` | `TIMESTAMP` |
+| - | - | `tokenlist` | `TOKENLIST` |
+| - | - | `uuid` | `BYTES(16)` |
+| - | - | `varchar` | `STRING({length})` |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.spanner.data_dictionary.SpannerSyncDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.spanner.data_dictionary.SpannerAsyncDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `spanner` dialect pack defines `8` named queries across `5` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/spanner/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `foreign_keys.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_table`, `by_schema` | `:schema_name`, `:table_name` |
+| `tables.sql` | `by_schema` | `:schema_name` |
+| `version.sql` | `current` | *none* |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: spanner
+ WITH pk_cols AS (
+ SELECT
+ ic.table_schema,
+ ic.table_name,
+ ic.column_name
+ FROM information_schema.index_columns AS ic
+ WHERE ic.index_name = 'PRIMARY_KEY'
+ AND ic.table_name = :table_name
+ AND (CAST(:schema_name AS STRING) IS NULL OR ic.table_schema = :schema_name)
+ )
+ SELECT
+ c.column_name,
+ c.spanner_type AS data_type,
+ c.is_nullable,
+ c.column_default,
+ CASE WHEN pk_cols.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS is_primary,
+ CASE WHEN c.is_identity = 'YES' THEN TRUE ELSE FALSE END AS is_identity,
+ CASE WHEN c.is_generated = 'ALWAYS' OR c.generation_expression IS NOT NULL THEN TRUE ELSE FALSE END AS is_generated,
+ c.generation_expression
+ FROM information_schema.columns AS c
+ LEFT JOIN pk_cols
+ ON c.table_schema = pk_cols.table_schema
+ AND c.table_name = pk_cols.table_name
+ AND c.column_name = pk_cols.column_name
+ WHERE c.table_name = :table_name
+ AND (CAST(:schema_name AS STRING) IS NULL OR c.table_schema = :schema_name)
+ ORDER BY c.ordinal_position;
+
+ -- name: by_schema
+ -- dialect: spanner
+ WITH pk_cols AS (
+ SELECT
+ ic.table_schema,
+ ic.table_name,
+ ic.column_name
+ FROM information_schema.index_columns AS ic
+ WHERE ic.index_name = 'PRIMARY_KEY'
+ AND (CAST(:schema_name AS STRING) IS NULL OR ic.table_schema = :schema_name)
+ )
+ SELECT
+ c.table_name,
+ c.column_name,
+ c.spanner_type AS data_type,
+ c.is_nullable,
+ c.column_default,
+ CASE WHEN pk_cols.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS is_primary,
+ CASE WHEN c.is_identity = 'YES' THEN TRUE ELSE FALSE END AS is_identity,
+ CASE WHEN c.is_generated = 'ALWAYS' OR c.generation_expression IS NOT NULL THEN TRUE ELSE FALSE END AS is_generated,
+ c.generation_expression
+ FROM information_schema.columns AS c
+ LEFT JOIN pk_cols
+ ON c.table_schema = pk_cols.table_schema
+ AND c.table_name = pk_cols.table_name
+ AND c.column_name = pk_cols.column_name
+ WHERE (CAST(:schema_name AS STRING) IS NULL OR c.table_schema = :schema_name)
+ ORDER BY c.table_name, c.ordinal_position;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: spanner
+ WITH fk_constraints AS (
+ SELECT
+ constraint_name,
+ unique_constraint_name
+ FROM information_schema.referential_constraints
+ WHERE (CAST(:schema_name AS STRING) IS NULL OR constraint_schema = :schema_name)
+ ),
+ fk_columns AS (
+ SELECT
+ constraint_name,
+ table_name,
+ column_name,
+ ordinal_position,
+ table_schema
+ FROM information_schema.key_column_usage
+ WHERE (CAST(:schema_name AS STRING) IS NULL OR constraint_schema = :schema_name)
+ AND (CAST(:table_name AS STRING) IS NULL OR table_name = :table_name)
+ ),
+ pk_columns AS (
+ SELECT
+ constraint_name,
+ table_name,
+ column_name,
+ ordinal_position,
+ table_schema
+ FROM information_schema.key_column_usage
+ WHERE (CAST(:schema_name AS STRING) IS NULL OR constraint_schema = :schema_name)
+ )
+ SELECT
+ fk.table_name,
+ fk.column_name,
+ pk.table_name AS referenced_table_name,
+ pk.column_name AS referenced_column_name,
+ fk.constraint_name,
+ fk.table_schema AS table_schema,
+ pk.table_schema AS referenced_table_schema
+ FROM fk_columns fk
+ JOIN fk_constraints rc
+ ON fk.constraint_name = rc.constraint_name
+ JOIN pk_columns pk
+ ON pk.constraint_name = rc.unique_constraint_name
+ AND pk.ordinal_position = fk.ordinal_position
+ WHERE (CAST(:table_name AS STRING) IS NULL OR fk.table_name = :table_name)
+ ORDER BY fk.table_name, fk.ordinal_position;
+
+ -- name: by_schema
+ -- dialect: spanner
+ WITH fk_constraints AS (
+ SELECT
+ constraint_name,
+ unique_constraint_name
+ FROM information_schema.referential_constraints
+ WHERE (CAST(:schema_name AS STRING) IS NULL OR constraint_schema = :schema_name)
+ ),
+ fk_columns AS (
+ SELECT
+ constraint_name,
+ table_name,
+ column_name,
+ ordinal_position,
+ table_schema
+ FROM information_schema.key_column_usage
+ WHERE (CAST(:schema_name AS STRING) IS NULL OR constraint_schema = :schema_name)
+ ),
+ pk_columns AS (
+ SELECT
+ constraint_name,
+ table_name,
+ column_name,
+ ordinal_position,
+ table_schema
+ FROM information_schema.key_column_usage
+ WHERE (CAST(:schema_name AS STRING) IS NULL OR constraint_schema = :schema_name)
+ )
+ SELECT
+ fk.table_name,
+ fk.column_name,
+ pk.table_name AS referenced_table_name,
+ pk.column_name AS referenced_column_name,
+ fk.constraint_name,
+ fk.table_schema AS table_schema,
+ pk.table_schema AS referenced_table_schema
+ FROM fk_columns fk
+ JOIN fk_constraints rc
+ ON fk.constraint_name = rc.constraint_name
+ JOIN pk_columns pk
+ ON pk.constraint_name = rc.unique_constraint_name
+ AND pk.ordinal_position = fk.ordinal_position
+ ORDER BY fk.table_name, fk.ordinal_position;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_table
+ -- dialect: spanner
+ SELECT
+ i.index_name,
+ i.table_name,
+ i.is_unique AS is_unique,
+ i.is_primary_key AS is_primary,
+ ARRAY_AGG(ic.column_name ORDER BY ic.ordinal_position) AS columns
+ FROM information_schema.indexes i
+ JOIN information_schema.index_columns ic
+ ON i.index_name = ic.index_name
+ AND i.table_name = ic.table_name
+ WHERE i.table_name = :table_name
+ AND (CAST(:schema_name AS STRING) IS NULL OR i.table_schema = :schema_name)
+ GROUP BY i.index_name, i.table_name, i.is_unique, i.is_primary_key;
+
+ -- name: by_schema
+ -- dialect: spanner
+ SELECT
+ i.index_name,
+ i.table_name,
+ i.is_unique AS is_unique,
+ i.is_primary_key AS is_primary,
+ ARRAY_AGG(ic.column_name ORDER BY ic.ordinal_position) AS columns
+ FROM information_schema.indexes i
+ JOIN information_schema.index_columns ic
+ ON i.index_name = ic.index_name
+ AND i.table_name = ic.table_name
+ WHERE (CAST(:schema_name AS STRING) IS NULL OR i.table_schema = :schema_name)
+ GROUP BY i.index_name, i.table_name, i.is_unique, i.is_primary_key;
+ ```
+
+??? code "`tables.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: spanner
+ SELECT
+ table_name,
+ 0 AS level
+ FROM information_schema.tables
+ WHERE table_type = 'BASE TABLE'
+ AND (CAST(:schema_name AS STRING) IS NULL OR table_schema = :schema_name)
+ ORDER BY table_name;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: spanner
+ SELECT NULL AS version;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/sqlanywhere.md b/docs/reference/data-dictionary/dialects/sqlanywhere.md
new file mode 100644
index 000000000..22c4fc4c6
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/sqlanywhere.md
@@ -0,0 +1,474 @@
+# SAP SQL Anywhere Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("sqlanywhere")
+```
+
+Introspects SAP SQL Anywhere `SYS` catalog views (`SYS.SYSTAB`, `SYS.SYSTABCOL`, `SYS.SYSIDX`, `SYS.SYSIDXCOL`, `SYS.SYSFKEY`, `SYS.SYSCONSTRAINT`, `SYS.SYSVIEW`, `SYS.SYSPROCEDURE`, `SYS.SYSTRIGGER`, `SYS.SYSSEQUENCE`). See the [SAP SQL Anywhere Guide](../../../databases/sqlanywhere.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `sqlanywhere` |
+| **Default Schema** | `"DBA"` |
+| **Version Pattern** | `(\d+)\.(\d+)\.(\d+)` |
+| **DDL Strategy** | AST synthesis + `SYS.SYSTAB` / `SYS.SYSPROCEDURE` definitions |
+| **Catalog `.sql` Files** | `9` files (`16` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `False` | `AutoIncrement` | `INTEGER DEFAULT AUTOINCREMENT` |
+| `supports_clustering` | `False` | `bigint` | `BIGINT` |
+| `supports_cte` | `True` | `blob` | `LONG BINARY` |
+| `supports_for_update` | `True` | `boolean` | `BIT` |
+| `supports_in_memory` | `False` | `bytes` | `VARBINARY(32767)` |
+| `supports_json` | `False` | `date` | `DATE` |
+| `supports_merge` | `True` | `datetime` | `TIMESTAMP` |
+| `supports_on_conflict` | `False` | `decimal` | `DECIMAL` |
+| `supports_prepared_statements` | `True` | `float` | `DOUBLE` |
+| `supports_returning` | `False` | `integer` | `INTEGER` |
+| `supports_schemas` | `True` | `json` | `LONG VARCHAR` |
+| `supports_sequences` | `True` | `smallint` | `SMALLINT` |
+| `supports_skip_locked` | `True` | `string` | `VARCHAR(255)` |
+| `supports_transactional_ddl` | `True` | `text` | `LONG VARCHAR` |
+| `supports_transactions` | `True` | `time` | `TIME` |
+| `supports_update_from` | `True` | `timestamp` | `TIMESTAMP` |
+| `supports_upsert` | `True` | `uuid` | `UNIQUEIDENTIFIER` |
+| `supports_uuid` | `True` | `varchar` | `VARCHAR(255)` |
+| `supports_window_functions` | `True` | - | - |
+| `supports_cte` | `>= 10.0.0` | - | - |
+| `supports_merge` | `>= 11.0.0` | - | - |
+| `supports_window_functions` | `>= 10.0.0` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.sqlanywhere.data_dictionary.SqlanywhereSyncDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `sqlanywhere` dialect pack defines `16` named queries across `9` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/sqlanywhere/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `constraints.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `foreign_keys.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `objects.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `schemas.sql` | `by_schema`, `current` | `:schema_name` |
+| `tables.sql` | `by_schema`, `all_by_schema`, `names_by_schema` | `:schema_name`, `:table_name` |
+| `version.sql` | `current` | *none* |
+| `views.sql` | `by_schema` | `:schema_name`, `:view_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlanywhere
+ WITH pk_cols AS (
+ SELECT DISTINCT
+ i.table_id,
+ ic.column_id
+ FROM SYS.SYSIDX i
+ JOIN SYS.SYSIDXCOL ic ON i.table_id = ic.table_id AND i.index_id = ic.index_id
+ WHERE i.index_category = 1
+ ),
+ unique_cols AS (
+ SELECT DISTINCT
+ i.table_id,
+ ic.column_id
+ FROM SYS.SYSIDX i
+ JOIN SYS.SYSIDXCOL ic ON i.table_id = ic.table_id AND i.index_id = ic.index_id
+ WHERE i."unique" IN (1, 2)
+ )
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ c.column_name AS column_name,
+ d.domain_name AS data_type,
+ CASE c.nulls WHEN 'Y' THEN 'YES' ELSE 'NO' END AS is_nullable,
+ c."default" AS column_default,
+ c.column_id AS ordinal_position,
+ c.width AS char_length,
+ c.width AS max_length,
+ c.width AS numeric_precision,
+ c.scale AS numeric_scale,
+ CASE WHEN pk.column_id IS NOT NULL THEN 1 ELSE 0 END AS is_primary,
+ CASE WHEN uq.column_id IS NOT NULL THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN c."default" LIKE '%autoincrement%' THEN 1 ELSE 0 END AS is_identity,
+ CASE WHEN c."default" LIKE '%autoincrement%' THEN 'ALWAYS' ELSE NULL END AS identity_generation,
+ CASE WHEN c.column_type = 'C' THEN 1 ELSE 0 END AS is_generated,
+ CASE WHEN c.column_type = 'C' THEN c."default" ELSE NULL END AS generation_expression,
+ 0 AS is_hidden,
+ NULL AS column_comment
+ FROM SYS.SYSTABCOL c
+ JOIN SYS.SYSTAB t ON c.table_id = t.table_id
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ JOIN SYS.SYSDOMAIN d ON c.domain_id = d.domain_id
+ LEFT JOIN pk_cols pk ON pk.table_id = t.table_id AND pk.column_id = c.column_id
+ LEFT JOIN unique_cols uq ON uq.table_id = t.table_id AND uq.column_id = c.column_id
+ WHERE t.table_type IN (1, 21)
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:table_name IS NULL OR t.table_name = :table_name)
+ ORDER BY u.user_name, t.table_name, c.column_id;
+
+ -- name: by_table
+ -- dialect: sqlanywhere
+ WITH pk_cols AS (
+ SELECT DISTINCT
+ i.table_id,
+ ic.column_id
+ FROM SYS.SYSIDX i
+ JOIN SYS.SYSIDXCOL ic ON i.table_id = ic.table_id AND i.index_id = ic.index_id
+ WHERE i.index_category = 1
+ ),
+ unique_cols AS (
+ SELECT DISTINCT
+ i.table_id,
+ ic.column_id
+ FROM SYS.SYSIDX i
+ JOIN SYS.SYSIDXCOL ic ON i.table_id = ic.table_id AND i.index_id = ic.index_id
+ WHERE i."unique" IN (1, 2)
+ )
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ c.column_name AS column_name,
+ d.domain_name AS data_type,
+ CASE c.nulls WHEN 'Y' THEN 'YES' ELSE 'NO' END AS is_nullable,
+ c."default" AS column_default,
+ c.column_id AS ordinal_position,
+ c.width AS char_length,
+ c.width AS max_length,
+ c.width AS numeric_precision,
+ c.scale AS numeric_scale,
+ CASE WHEN pk.column_id IS NOT NULL THEN 1 ELSE 0 END AS is_primary,
+ CASE WHEN uq.column_id IS NOT NULL THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN c."default" LIKE '%autoincrement%' THEN 1 ELSE 0 END AS is_identity,
+ CASE WHEN c."default" LIKE '%autoincrement%' THEN 'ALWAYS' ELSE NULL END AS identity_generation,
+ CASE WHEN c.column_type = 'C' THEN 1 ELSE 0 END AS is_generated,
+ CASE WHEN c.column_type = 'C' THEN c."default" ELSE NULL END AS generation_expression,
+ 0 AS is_hidden,
+ NULL AS column_comment
+ FROM SYS.SYSTABCOL c
+ JOIN SYS.SYSTAB t ON c.table_id = t.table_id
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ JOIN SYS.SYSDOMAIN d ON c.domain_id = d.domain_id
+ LEFT JOIN pk_cols pk ON pk.table_id = t.table_id AND pk.column_id = c.column_id
+ LEFT JOIN unique_cols uq ON uq.table_id = t.table_id AND uq.column_id = c.column_id
+ WHERE t.table_type IN (1, 21)
+ AND t.table_name = :table_name
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ ORDER BY c.column_id;
+ ```
+
+??? code "`constraints.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlanywhere
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ i.index_name AS constraint_name,
+ CASE i.index_category
+ WHEN 1 THEN 'PRIMARY KEY'
+ WHEN 2 THEN 'FOREIGN KEY'
+ ELSE 'UNIQUE'
+ END AS constraint_type,
+ NULL AS check_clause,
+ 'Y' AS is_enforced
+ FROM SYS.SYSIDX i
+ JOIN SYS.SYSTAB t ON i.table_id = t.table_id
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ WHERE t.table_type = 1
+ AND (i.index_category IN (1, 2) OR i."unique" IN (1, 2))
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:table_name IS NULL OR t.table_name = :table_name)
+ ORDER BY u.user_name, t.table_name, i.index_name;
+
+ -- name: by_table
+ -- dialect: sqlanywhere
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ i.index_name AS constraint_name,
+ CASE i.index_category
+ WHEN 1 THEN 'PRIMARY KEY'
+ WHEN 2 THEN 'FOREIGN KEY'
+ ELSE 'UNIQUE'
+ END AS constraint_type,
+ NULL AS check_clause,
+ 'Y' AS is_enforced
+ FROM SYS.SYSIDX i
+ JOIN SYS.SYSTAB t ON i.table_id = t.table_id
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ WHERE t.table_type = 1
+ AND (i.index_category IN (1, 2) OR i."unique" IN (1, 2))
+ AND t.table_name = :table_name
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ ORDER BY i.index_name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlanywhere
+ SELECT
+ fu.user_name AS schema_name,
+ ft.table_name AS table_name,
+ fi.index_name AS constraint_name,
+ fc.column_name AS column_name,
+ pu.user_name AS referenced_schema,
+ pt.table_name AS referenced_table,
+ pc.column_name AS referenced_column
+ FROM SYS.SYSFKEY fk
+ JOIN SYS.SYSTAB ft ON fk.foreign_table_id = ft.table_id
+ JOIN SYS.SYSUSER fu ON ft.creator = fu.user_id
+ JOIN SYS.SYSIDX fi ON fk.foreign_table_id = fi.table_id AND fk.foreign_index_id = fi.index_id
+ JOIN SYS.SYSIDXCOL fic ON fi.table_id = fic.table_id AND fi.index_id = fic.index_id
+ JOIN SYS.SYSTABCOL fc ON fic.table_id = fc.table_id AND fic.column_id = fc.column_id
+ JOIN SYS.SYSTAB pt ON fk.primary_table_id = pt.table_id
+ JOIN SYS.SYSUSER pu ON pt.creator = pu.user_id
+ JOIN SYS.SYSTABCOL pc ON pt.table_id = pc.table_id AND fic.primary_column_id = pc.column_id
+ WHERE fu.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:table_name IS NULL OR ft.table_name = :table_name)
+ ORDER BY fu.user_name, ft.table_name, fi.index_name, fic.sequence;
+
+ -- name: by_table
+ -- dialect: sqlanywhere
+ SELECT
+ fu.user_name AS schema_name,
+ ft.table_name AS table_name,
+ fi.index_name AS constraint_name,
+ fc.column_name AS column_name,
+ pu.user_name AS referenced_schema,
+ pt.table_name AS referenced_table,
+ pc.column_name AS referenced_column
+ FROM SYS.SYSFKEY fk
+ JOIN SYS.SYSTAB ft ON fk.foreign_table_id = ft.table_id
+ JOIN SYS.SYSUSER fu ON ft.creator = fu.user_id
+ JOIN SYS.SYSIDX fi ON fk.foreign_table_id = fi.table_id AND fk.foreign_index_id = fi.index_id
+ JOIN SYS.SYSIDXCOL fic ON fi.table_id = fic.table_id AND fi.index_id = fic.index_id
+ JOIN SYS.SYSTABCOL fc ON fic.table_id = fc.table_id AND fic.column_id = fc.column_id
+ JOIN SYS.SYSTAB pt ON fk.primary_table_id = pt.table_id
+ JOIN SYS.SYSUSER pu ON pt.creator = pu.user_id
+ JOIN SYS.SYSTABCOL pc ON pt.table_id = pc.table_id AND fic.primary_column_id = pc.column_id
+ WHERE ft.table_name = :table_name
+ AND fu.user_name = COALESCE(:schema_name, CURRENT USER)
+ ORDER BY fi.index_name, fic.sequence;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlanywhere
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ i.index_name AS index_name,
+ c.column_name AS column_name,
+ ic.sequence + 1 AS column_position,
+ CASE WHEN i."unique" IN (1, 2) THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN i.index_category = 1 THEN 1 ELSE 0 END AS is_primary
+ FROM SYS.SYSIDX i
+ JOIN SYS.SYSTAB t ON i.table_id = t.table_id
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ JOIN SYS.SYSIDXCOL ic ON i.table_id = ic.table_id AND i.index_id = ic.index_id
+ JOIN SYS.SYSTABCOL c ON ic.table_id = c.table_id AND ic.column_id = c.column_id
+ WHERE t.table_type = 1
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:table_name IS NULL OR t.table_name = :table_name)
+ ORDER BY u.user_name, t.table_name, i.index_name, ic.sequence;
+
+ -- name: by_table
+ -- dialect: sqlanywhere
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ i.index_name AS index_name,
+ c.column_name AS column_name,
+ ic.sequence + 1 AS column_position,
+ CASE WHEN i."unique" IN (1, 2) THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN i.index_category = 1 THEN 1 ELSE 0 END AS is_primary
+ FROM SYS.SYSIDX i
+ JOIN SYS.SYSTAB t ON i.table_id = t.table_id
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ JOIN SYS.SYSIDXCOL ic ON i.table_id = ic.table_id AND i.index_id = ic.index_id
+ JOIN SYS.SYSTABCOL c ON ic.table_id = c.table_id AND ic.column_id = c.column_id
+ WHERE t.table_type = 1
+ AND t.table_name = :table_name
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ ORDER BY i.index_name, ic.sequence;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlanywhere
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS object_name,
+ CASE t.table_type
+ WHEN 1 THEN 'TABLE'
+ WHEN 21 THEN 'VIEW'
+ ELSE 'OBJECT'
+ END AS object_type,
+ NULL AS created,
+ NULL AS remarks
+ FROM SYS.SYSTAB t
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ WHERE t.table_type IN (1, 21)
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:object_name IS NULL OR t.table_name = :object_name)
+ ORDER BY u.user_name, object_type, t.table_name;
+ ```
+
+??? code "`schemas.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlanywhere
+ SELECT
+ u.user_name AS schema_name,
+ u.user_name AS owner_name,
+ NULL AS create_time,
+ NULL AS comment
+ FROM SYS.SYSUSER u
+ WHERE u.user_name NOT IN ('SYS', 'PUBLIC', 'dbo', 'diagnostics', 'rs_systabgroup', 'SA_DEBUG', 'SYS_SPATIAL_ADMIN_ROLE')
+ AND (:schema_name IS NULL OR u.user_name = :schema_name)
+ ORDER BY u.user_name;
+
+ -- name: current
+ -- dialect: sqlanywhere
+ SELECT CURRENT USER AS schema_name;
+ ```
+
+??? code "`tables.sql` (3 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlanywhere
+ WITH RECURSIVE dependency_tree (table_id, schema_name, table_name, dependency_level) AS (
+ SELECT
+ t.table_id,
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ 0 AS dependency_level
+ FROM SYS.SYSTAB t
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ WHERE t.table_type = 1
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:table_name IS NULL OR t.table_name = :table_name)
+ AND NOT EXISTS (
+ SELECT 1
+ FROM SYS.SYSFKEY fk
+ WHERE fk.foreign_table_id = t.table_id
+ AND fk.primary_table_id <> t.table_id
+ )
+ UNION ALL
+ SELECT
+ child.table_id,
+ u.user_name AS schema_name,
+ child.table_name AS table_name,
+ parent.dependency_level + 1 AS dependency_level
+ FROM SYS.SYSFKEY fk
+ JOIN SYS.SYSTAB child ON child.table_id = fk.foreign_table_id
+ JOIN SYS.SYSUSER u ON child.creator = u.user_id
+ JOIN dependency_tree parent ON parent.table_id = fk.primary_table_id
+ WHERE child.table_type = 1
+ AND fk.primary_table_id <> fk.foreign_table_id
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:table_name IS NULL OR child.table_name = :table_name)
+ AND parent.dependency_level < 20
+ ),
+ dependency_levels AS (
+ SELECT
+ table_id,
+ MAX(dependency_level) AS dependency_level
+ FROM dependency_tree
+ GROUP BY table_id
+ )
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ 'BASE TABLE' AS table_type,
+ COALESCE(dl.dependency_level, 0) AS dependency_level,
+ COALESCE(dl.dependency_level, 0) AS level
+ FROM SYS.SYSTAB t
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ LEFT JOIN dependency_levels dl ON dl.table_id = t.table_id
+ WHERE t.table_type = 1
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:table_name IS NULL OR t.table_name = :table_name)
+ ORDER BY dependency_level, u.user_name, t.table_name;
+
+ -- name: all_by_schema
+ -- dialect: sqlanywhere
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS table_name,
+ CASE t.table_type
+ WHEN 1 THEN 'BASE TABLE'
+ WHEN 21 THEN 'VIEW'
+ ELSE 'TABLE'
+ END AS table_type,
+ 0 AS dependency_level,
+ 0 AS level,
+ t.count AS estimated_row_count,
+ NULL AS table_comment
+ FROM SYS.SYSTAB t
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ WHERE t.table_type IN (1, 21)
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:table_name IS NULL OR t.table_name = :table_name)
+ ORDER BY u.user_name, t.table_name;
+
+ -- name: names_by_schema
+ -- dialect: sqlanywhere
+ SELECT
+ t.table_name AS table_name
+ FROM SYS.SYSTAB t
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ WHERE t.table_type = 1
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ ORDER BY t.table_name;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: sqlanywhere
+ SELECT PROPERTY('ProductVersion') AS version_string;
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlanywhere
+ SELECT
+ u.user_name AS schema_name,
+ t.table_name AS view_name,
+ v.view_def AS definition,
+ 0 AS is_read_only,
+ 1 AS is_valid
+ FROM SYS.SYSTAB t
+ JOIN SYS.SYSUSER u ON t.creator = u.user_id
+ LEFT JOIN SYS.SYSVIEW v ON t.object_id = v.view_object_id
+ WHERE t.table_type = 21
+ AND u.user_name = COALESCE(:schema_name, CURRENT USER)
+ AND (:view_name IS NULL OR t.table_name = :view_name)
+ ORDER BY u.user_name, t.table_name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/sqlite.md b/docs/reference/data-dictionary/dialects/sqlite.md
new file mode 100644
index 000000000..b66c029b1
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/sqlite.md
@@ -0,0 +1,548 @@
+# SQLite Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("sqlite")
+```
+
+Introspects `sqlite_master` and table-valued `pragma_table_xinfo`, `pragma_index_list`, `pragma_index_info`, and `pragma_foreign_key_list` functions across `sqlite` and `aiosqlite`. See the [SQLite Guide](../../../databases/sqlite.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `sqlite` |
+| **Default Schema** | `None` |
+| **Version Pattern** | `(\d+)\.(\d+)\.(\d+)` |
+| **DDL Strategy** | Native `sqlite_master.sql` + `PRAGMA` synthesis |
+| **Catalog `.sql` Files** | `15` files (`23` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `False` | `bigint` | `INTEGER` |
+| `supports_for_update` | `False` | `blob` | `BLOB` |
+| `supports_on_conflict` | `True` | `boolean` | `INTEGER` |
+| `supports_prepared_statements` | `True` | `float` | `REAL` |
+| `supports_schemas` | `False` | `integer` | `INTEGER` |
+| `supports_skip_locked` | `False` | `json` | `JSON` |
+| `supports_transactions` | `True` | `text` | `TEXT` |
+| `supports_update_from` | `True` | `timestamp` | `TIMESTAMP` |
+| `supports_uuid` | `False` | `uuid` | `TEXT` |
+| `supports_cte` | `>= 3.8.3` | `varchar` | `TEXT` |
+| `supports_json` | `>= 3.38.0` | - | - |
+| `supports_returning` | `>= 3.35.0` | - | - |
+| `supports_upsert` | `>= 3.24.0` | - | - |
+| `supports_window_functions` | `>= 3.25.0` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.sqlite.data_dictionary.SqliteDataDictionary
+ options:
+ heading_level: 3
+
+::: sqlspec.adapters.aiosqlite.data_dictionary.AiosqliteDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `sqlite` dialect pack defines `23` named queries across `15` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/sqlite/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `compile_options.sql` | `list` | *none* |
+| `constraints.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `ddl.sql` | `by_object`, `by_schema` | `:object_name`, `:object_type`, `:schema_name` |
+| `foreign_keys.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `functions.sql` | `list` | *none* |
+| `indexes.sql` | `by_schema`, `by_table`, `columns_by_index`, `index_columns_by_index` | `:index_name`, `:schema_name`, `:table_name` |
+| `modules.sql` | `list` | *none* |
+| `objects.sql` | `by_schema` | `:schema_name` |
+| `schemas.sql` | `list` | *none* |
+| `system.sql` | `foreign_key_check`, `integrity_check` | *none* |
+| `tables.sql` | `by_schema` | `:schema_name` |
+| `triggers.sql` | `by_schema` | `:schema_name` |
+| `version.sql` | `current` | *none* |
+| `views.sql` | `by_schema` | `:schema_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlite
+ SELECT
+ tl.schema AS schema_name,
+ tl.name AS table_name,
+ ti.name AS column_name,
+ ti.type AS data_type,
+ CASE WHEN ti."notnull" THEN 'NO' ELSE 'YES' END AS is_nullable,
+ ti.dflt_value AS column_default,
+ ti.cid + 1 AS ordinal_position,
+ ti.pk AS is_primary,
+ CASE
+ WHEN ti.pk = 1 AND UPPER(ti.type) = 'INTEGER' AND UPPER(sm.sql) LIKE '%AUTOINCREMENT%' THEN 1
+ ELSE 0
+ END AS is_identity,
+ CASE
+ WHEN ti.hidden = 1 THEN 'hidden'
+ WHEN ti.hidden = 2 THEN 'virtual generated'
+ WHEN ti.hidden = 3 THEN 'stored generated'
+ WHEN ti.pk = 1 AND UPPER(ti.type) = 'INTEGER' AND UPPER(sm.sql) LIKE '%AUTOINCREMENT%' THEN 'autoincrement'
+ ELSE NULL
+ END AS extra,
+ ti.hidden IN (2, 3) AS is_generated,
+ CASE WHEN ti.hidden = 1 THEN 1 ELSE 0 END AS is_hidden
+ FROM pragma_table_list AS tl
+ JOIN pragma_table_xinfo(tl.name, COALESCE(:schema_name, 'main')) AS ti
+ LEFT JOIN {schema_prefix}sqlite_schema AS sm ON sm.type = 'table' AND sm.name = tl.name
+ WHERE tl.schema = COALESCE(:schema_name, 'main')
+ AND tl.type IN ('table', 'virtual')
+ AND tl.name NOT LIKE 'sqlite_%'
+ ORDER BY tl.name, ti.cid;
+
+ -- name: by_table
+ -- dialect: sqlite
+ SELECT
+ :schema_name AS schema_name,
+ :table_name AS table_name,
+ ti.name AS column_name,
+ ti.type AS data_type,
+ CASE WHEN ti."notnull" THEN 'NO' ELSE 'YES' END AS is_nullable,
+ ti.dflt_value AS column_default,
+ ti.cid + 1 AS ordinal_position,
+ ti.pk AS is_primary,
+ CASE
+ WHEN ti.pk = 1 AND UPPER(ti.type) = 'INTEGER' AND UPPER(sm.sql) LIKE '%AUTOINCREMENT%' THEN 1
+ ELSE 0
+ END AS is_identity,
+ CASE
+ WHEN ti.hidden = 1 THEN 'hidden'
+ WHEN ti.hidden = 2 THEN 'virtual generated'
+ WHEN ti.hidden = 3 THEN 'stored generated'
+ WHEN ti.pk = 1 AND UPPER(ti.type) = 'INTEGER' AND UPPER(sm.sql) LIKE '%AUTOINCREMENT%' THEN 'autoincrement'
+ ELSE NULL
+ END AS extra,
+ ti.hidden IN (2, 3) AS is_generated,
+ CASE WHEN ti.hidden = 1 THEN 1 ELSE 0 END AS is_hidden
+ FROM pragma_table_xinfo(:table_name, :schema_name) AS ti
+ LEFT JOIN {schema_prefix}sqlite_schema AS sm ON sm.type = 'table' AND sm.name = :table_name
+ ORDER BY ti.cid;
+ ```
+
+??? code "`compile_options.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: sqlite
+ PRAGMA compile_options;
+ ```
+
+??? code "`constraints.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlite
+ SELECT
+ COALESCE(:schema_name, 'main') AS schema_name,
+ tl.name AS table_name,
+ il.name AS index_name,
+ CASE WHEN il.origin = 'pk' THEN 'PRIMARY KEY' ELSE 'UNIQUE' END AS constraint_type,
+ ii.name AS column_name,
+ ii.seqno AS ordinal_position
+ FROM pragma_table_list AS tl
+ JOIN pragma_index_list(tl.name, COALESCE(:schema_name, 'main')) AS il
+ JOIN pragma_index_info(il.name, COALESCE(:schema_name, 'main')) AS ii
+ WHERE tl.schema = COALESCE(:schema_name, 'main')
+ AND tl.type IN ('table', 'virtual')
+ AND tl.name NOT LIKE 'sqlite_%'
+ AND il.origin IN ('pk', 'u')
+ AND (:table_name IS NULL OR tl.name = :table_name)
+ UNION ALL
+ SELECT
+ COALESCE(:schema_name, 'main') AS schema_name,
+ tl.name AS table_name,
+ NULL AS index_name,
+ 'PRIMARY KEY' AS constraint_type,
+ ti.name AS column_name,
+ ti.pk - 1 AS ordinal_position
+ FROM pragma_table_list AS tl
+ JOIN pragma_table_info(tl.name, COALESCE(:schema_name, 'main')) AS ti
+ WHERE tl.schema = COALESCE(:schema_name, 'main')
+ AND tl.type IN ('table', 'virtual')
+ AND tl.name NOT LIKE 'sqlite_%'
+ AND ti.pk > 0
+ AND (:table_name IS NULL OR tl.name = :table_name)
+ AND NOT EXISTS (
+ SELECT 1
+ FROM pragma_index_list(tl.name, COALESCE(:schema_name, 'main')) AS il_pk
+ WHERE il_pk.origin = 'pk'
+ )
+ ORDER BY table_name, index_name, ordinal_position;
+
+ -- name: by_table
+ -- dialect: sqlite
+ SELECT
+ COALESCE(:schema_name, 'main') AS schema_name,
+ :table_name AS table_name,
+ il.name AS index_name,
+ CASE WHEN il.origin = 'pk' THEN 'PRIMARY KEY' ELSE 'UNIQUE' END AS constraint_type,
+ ii.name AS column_name,
+ ii.seqno AS ordinal_position
+ FROM pragma_index_list(:table_name, COALESCE(:schema_name, 'main')) AS il
+ JOIN pragma_index_info(il.name, COALESCE(:schema_name, 'main')) AS ii
+ WHERE il.origin IN ('pk', 'u')
+ UNION ALL
+ SELECT
+ COALESCE(:schema_name, 'main') AS schema_name,
+ :table_name AS table_name,
+ NULL AS index_name,
+ 'PRIMARY KEY' AS constraint_type,
+ ti.name AS column_name,
+ ti.pk - 1 AS ordinal_position
+ FROM pragma_table_info(:table_name, COALESCE(:schema_name, 'main')) AS ti
+ WHERE ti.pk > 0
+ AND NOT EXISTS (
+ SELECT 1
+ FROM pragma_index_list(:table_name, COALESCE(:schema_name, 'main')) AS il_pk
+ WHERE il_pk.origin = 'pk'
+ )
+ ORDER BY index_name, ordinal_position;
+ ```
+
+??? code "`ddl.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_object
+ -- dialect: sqlite
+ SELECT
+ :schema_name AS schema_name,
+ name AS object_name,
+ type AS object_type,
+ tbl_name AS table_name,
+ sql AS native_sql,
+ sql AS ddl,
+ 'native' AS fidelity
+ FROM {schema_prefix}sqlite_schema
+ WHERE name = :object_name
+ AND (:object_type IS NULL OR LOWER(type) = LOWER(:object_type))
+ AND sql IS NOT NULL;
+
+ -- name: by_schema
+ -- dialect: sqlite
+ SELECT
+ :schema_name AS schema_name,
+ name AS object_name,
+ type AS object_type,
+ tbl_name AS table_name,
+ sql AS native_sql,
+ sql AS ddl,
+ 'native' AS fidelity
+ FROM {schema_prefix}sqlite_schema
+ WHERE name NOT LIKE 'sqlite_%'
+ AND sql IS NOT NULL
+ ORDER BY
+ CASE type
+ WHEN 'table' THEN 1
+ WHEN 'view' THEN 2
+ WHEN 'index' THEN 3
+ WHEN 'trigger' THEN 4
+ ELSE 5
+ END,
+ name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlite
+ SELECT
+ tl.schema AS schema_name,
+ tl.schema AS table_schema,
+ tl.name AS table_name,
+ fk."from" AS column_name,
+ fk."table" AS referenced_table_name,
+ fk."to" AS referenced_column_name,
+ CASE
+ WHEN COUNT(*) OVER (PARTITION BY tl.name, fk.id) > 1 THEN tl.name || '_fk_' || fk.id
+ ELSE NULL
+ END AS constraint_name,
+ fk.id AS id,
+ fk.seq AS ordinal_position,
+ fk.on_update,
+ fk.on_delete,
+ fk.match
+ FROM pragma_table_list AS tl
+ JOIN pragma_foreign_key_list(tl.name, COALESCE(:schema_name, 'main')) AS fk
+ WHERE tl.schema = COALESCE(:schema_name, 'main')
+ AND tl.type IN ('table', 'virtual')
+ AND tl.name NOT LIKE 'sqlite_%'
+ ORDER BY tl.name, fk.id, fk.seq;
+
+ -- name: by_table
+ -- dialect: sqlite
+ SELECT
+ COALESCE(:schema_name, 'main') AS schema_name,
+ COALESCE(:schema_name, 'main') AS table_schema,
+ :table_name AS table_name,
+ fk."from" AS column_name,
+ fk."table" AS referenced_table_name,
+ fk."to" AS referenced_column_name,
+ CASE
+ WHEN COUNT(*) OVER (PARTITION BY fk.id) > 1 THEN :table_name || '_fk_' || fk.id
+ ELSE NULL
+ END AS constraint_name,
+ fk.id AS id,
+ fk.seq AS ordinal_position,
+ fk.on_update,
+ fk.on_delete,
+ fk.match
+ FROM pragma_foreign_key_list(:table_name, :schema_name) AS fk
+ ORDER BY fk.id, fk.seq;
+ ```
+
+??? code "`functions.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: sqlite
+ PRAGMA function_list;
+ ```
+
+??? code "`indexes.sql` (4 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlite
+ SELECT
+ tl.schema AS schema_name,
+ tl.name AS table_name,
+ il.name AS index_name,
+ il."unique" AS is_unique,
+ CASE WHEN il.origin = 'pk' THEN 1 ELSE 0 END AS is_primary,
+ il.origin,
+ il.partial AS is_partial,
+ ix.seqno,
+ ix.cid AS column_id,
+ ix.name AS column_name,
+ ix.desc AS is_descending,
+ ix.coll AS collation,
+ ix.key AS is_key_column,
+ sm.sql AS index_sql
+ FROM pragma_table_list AS tl
+ JOIN pragma_index_list(tl.name, COALESCE(:schema_name, 'main')) AS il
+ JOIN pragma_index_xinfo(il.name, COALESCE(:schema_name, 'main')) AS ix
+ LEFT JOIN {schema_prefix}sqlite_schema AS sm
+ ON sm.type = 'index'
+ AND sm.name = il.name
+ WHERE tl.schema = COALESCE(:schema_name, 'main')
+ AND tl.type IN ('table', 'virtual')
+ AND tl.name NOT LIKE 'sqlite_%'
+ AND il.name NOT LIKE 'sqlite_%'
+ ORDER BY tl.name, il.seq, ix.seqno;
+
+ -- name: by_table
+ -- dialect: sqlite
+ SELECT
+ :schema_name AS schema_name,
+ :table_name AS table_name,
+ il.name AS index_name,
+ il."unique" AS is_unique,
+ CASE WHEN il.origin = 'pk' THEN 1 ELSE 0 END AS is_primary,
+ il.origin,
+ il.partial AS is_partial,
+ ix.seqno,
+ ix.cid AS column_id,
+ ix.name AS column_name,
+ ix.desc AS is_descending,
+ ix.coll AS collation,
+ ix.key AS is_key_column,
+ sm.sql AS index_sql
+ FROM pragma_index_list(:table_name, :schema_name) AS il
+ JOIN pragma_index_xinfo(il.name, :schema_name) AS ix
+ LEFT JOIN {schema_prefix}sqlite_schema AS sm
+ ON sm.type = 'index'
+ AND sm.name = il.name
+ WHERE il.name NOT LIKE 'sqlite_%'
+ ORDER BY il.seq, ix.seqno;
+
+ -- name: columns_by_index
+ -- dialect: sqlite
+ SELECT
+ :schema_name AS schema_name,
+ :index_name AS index_name,
+ ix.seqno,
+ ix.cid AS column_id,
+ ix.name AS column_name,
+ ix.desc AS is_descending,
+ ix.coll AS collation,
+ ix.key AS is_key_column
+ FROM pragma_index_xinfo(:index_name, :schema_name) AS ix
+ ORDER BY ix.seqno;
+
+ -- name: index_columns_by_index
+ -- dialect: sqlite
+ SELECT
+ seqno,
+ cid,
+ name
+ FROM pragma_index_info(:index_name, COALESCE(:schema_name, 'main'))
+ ORDER BY seqno;
+ ```
+
+??? code "`modules.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: sqlite
+ PRAGMA module_list;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlite
+ SELECT
+ :schema_name AS schema_name,
+ name AS object_name,
+ type AS object_type,
+ tbl_name AS table_name,
+ rootpage,
+ sql AS native_sql
+ FROM {schema_prefix}sqlite_schema
+ WHERE type IN ('table', 'index', 'view', 'trigger')
+ AND name NOT LIKE 'sqlite_%'
+ ORDER BY type, name;
+ ```
+
+??? code "`schemas.sql` (1 named queries)"
+
+ ```sql
+ -- name: list
+ -- dialect: sqlite
+ PRAGMA database_list;
+ ```
+
+??? code "`system.sql` (2 named queries)"
+
+ ```sql
+ -- name: foreign_key_check
+ -- dialect: sqlite
+ PRAGMA foreign_key_check;
+
+ -- name: integrity_check
+ -- dialect: sqlite
+ PRAGMA integrity_check;
+ ```
+
+??? code "`tables.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlite
+ WITH table_inventory AS (
+ SELECT
+ tl.schema AS schema_name,
+ tl.name AS table_name,
+ tl.type AS table_type,
+ tl.ncol,
+ tl.wr,
+ tl.strict
+ FROM pragma_table_list AS tl
+ WHERE tl.schema = COALESCE(:schema_name, 'main')
+ AND tl.type IN ('table', 'virtual')
+ AND tl.name NOT LIKE 'sqlite_%'
+ ),
+ fk_edges AS MATERIALIZED (
+ SELECT
+ ti.table_name,
+ fk."table" AS referenced_table
+ FROM table_inventory AS ti
+ JOIN pragma_foreign_key_list(ti.table_name, COALESCE(:schema_name, 'main')) AS fk
+ WHERE fk."table" <> ti.table_name
+ ),
+ dependency_tree AS (
+ SELECT
+ ti.table_name,
+ 0 AS level,
+ '/' || ti.table_name || '/' AS path
+ FROM table_inventory AS ti
+ WHERE NOT EXISTS (
+ SELECT 1
+ FROM fk_edges AS e
+ WHERE e.table_name = ti.table_name
+ )
+
+ UNION ALL
+
+ SELECT
+ e.table_name,
+ dt.level + 1,
+ dt.path || e.table_name || '/'
+ FROM fk_edges AS e
+ JOIN dependency_tree AS dt ON e.referenced_table = dt.table_name
+ WHERE instr(dt.path, '/' || e.table_name || '/') = 0
+ ),
+ dependency_levels AS (
+ SELECT
+ table_name,
+ MAX(level) AS dependency_level
+ FROM dependency_tree
+ GROUP BY table_name
+ )
+ SELECT
+ ti.schema_name,
+ ti.table_name,
+ ti.table_type,
+ COALESCE(dl.dependency_level, 0) AS dependency_level,
+ COALESCE(dl.dependency_level, 0) AS level,
+ ti.ncol,
+ ti.wr,
+ ti.strict
+ FROM table_inventory AS ti
+ LEFT JOIN dependency_levels AS dl ON dl.table_name = ti.table_name
+ ORDER BY COALESCE(dl.dependency_level, 0), ti.table_name;
+ ```
+
+??? code "`triggers.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlite
+ SELECT
+ :schema_name AS schema_name,
+ name AS trigger_name,
+ tbl_name AS table_name,
+ sql AS native_sql
+ FROM {schema_prefix}sqlite_schema
+ WHERE type = 'trigger'
+ ORDER BY name;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: sqlite
+ SELECT sqlite_version();
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sqlite
+ SELECT
+ :schema_name AS schema_name,
+ name AS view_name,
+ tbl_name AS table_name,
+ sql AS native_sql
+ FROM {schema_prefix}sqlite_schema
+ WHERE type = 'view'
+ ORDER BY name;
+ ```
diff --git a/docs/reference/data-dictionary/dialects/sybase.md b/docs/reference/data-dictionary/dialects/sybase.md
new file mode 100644
index 000000000..c29e251ea
--- /dev/null
+++ b/docs/reference/data-dictionary/dialects/sybase.md
@@ -0,0 +1,557 @@
+# Sybase ASE Data Dictionary & SQL Catalog
+
+```python
+from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config
+
+config = get_dialect_config("sybase")
+```
+
+Introspects SAP Adaptive Server Enterprise system tables (`dbo.sysobjects`, `dbo.syscolumns`, `dbo.sysindexes`, `dbo.sysreferences`, `dbo.syscomments`, `dbo.systypes`, `dbo.sysusers`). See the [Sybase ASE Guide](../../../databases/sybase.md) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.
+
+## Dialect Configuration
+
+| Setting | Value |
+| --- | --- |
+| **Dialect Name** | `sybase` |
+| **Default Schema** | `"dbo"` |
+| **Version Pattern** | `Adaptive Server Enterprise/(\d+)\.(\d+)(?:\s+SP(\d+)|\.\d+)?` |
+| **DDL Strategy** | AST synthesis + `dbo.syscomments` object text reassembly |
+| **Catalog `.sql` Files** | `9` files (`16` named queries) |
+
+### Capabilities & Type Mappings
+
+| Capability | Status / Minimum Version | Logical Type | Target Physical Type |
+| --- | --- | --- | --- |
+| `supports_arrays` | `False` | `AutoIncrement` | `INT IDENTITY` |
+| `supports_clustering` | `False` | `bigint` | `BIGINT` |
+| `supports_cte` | `False` | `blob` | `IMAGE` |
+| `supports_for_update` | `False` | `boolean` | `BIT` |
+| `supports_in_memory` | `False` | `bytes` | `VARBINARY(16384)` |
+| `supports_json` | `False` | `date` | `DATE` |
+| `supports_merge` | `False` | `datetime` | `BIGDATETIME` |
+| `supports_on_conflict` | `False` | `decimal` | `DECIMAL` |
+| `supports_prepared_statements` | `True` | `float` | `FLOAT` |
+| `supports_returning` | `False` | `integer` | `INT` |
+| `supports_schemas` | `True` | `json` | `TEXT` |
+| `supports_sequences` | `False` | `smallint` | `SMALLINT` |
+| `supports_skip_locked` | `False` | `string` | `VARCHAR(255)` |
+| `supports_transactional_ddl` | `False` | `text` | `TEXT` |
+| `supports_transactions` | `True` | `time` | `BIGTIME` |
+| `supports_update_from` | `True` | `timestamp` | `BIGDATETIME` |
+| `supports_upsert` | `False` | `uuid` | `VARCHAR(36)` |
+| `supports_uuid` | `False` | `varchar` | `VARCHAR(255)` |
+| `supports_window_functions` | `False` | - | - |
+
+## Adapter Data Dictionary Classes
+
+::: sqlspec.adapters.sybase.data_dictionary.SybaseSyncDataDictionary
+ options:
+ heading_level: 3
+
+## Catalog SQL Queries
+
+The `sybase` dialect pack defines `16` named queries across `9` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/sybase/`):
+
+| Domain Pack | Named Queries | Bind Parameters |
+| --- | --- | --- |
+| `columns.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `constraints.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `foreign_keys.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `indexes.sql` | `by_schema`, `by_table` | `:schema_name`, `:table_name` |
+| `objects.sql` | `by_schema` | `:object_name`, `:schema_name` |
+| `schemas.sql` | `by_schema`, `current` | `:schema_name` |
+| `tables.sql` | `by_schema`, `all_by_schema`, `names_by_schema` | `:schema_name`, `:table_name` |
+| `version.sql` | `current` | `@version` |
+| `views.sql` | `by_schema` | `:schema_name`, `:view_name` |
+
+??? code "`columns.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ c.name AS column_name,
+ t.name AS data_type,
+ CASE WHEN (c.status & 8) = 8 THEN 'YES' ELSE 'NO' END AS is_nullable,
+ cm.text AS column_default,
+ c.colid AS ordinal_position,
+ c.length AS char_length,
+ c.length AS max_length,
+ c.prec AS numeric_precision,
+ c.scale AS numeric_scale,
+ CASE WHEN pk.col_name IS NOT NULL THEN 1 ELSE 0 END AS is_primary,
+ CASE WHEN uq.col_name IS NOT NULL THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN (c.status & 128) = 128 THEN 1 ELSE 0 END AS is_identity,
+ CASE WHEN (c.status & 128) = 128 THEN 'ALWAYS' ELSE NULL END AS identity_generation,
+ 0 AS is_generated,
+ NULL AS generation_expression,
+ 0 AS is_hidden,
+ NULL AS column_comment
+ FROM dbo.syscolumns c
+ JOIN dbo.sysobjects o ON c.id = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ JOIN dbo.systypes t ON c.usertype = t.usertype
+ LEFT JOIN dbo.syscomments cm ON c.cdefault = cm.id AND cm.colid = 1
+ LEFT JOIN (
+ SELECT DISTINCT
+ i.id,
+ index_col(USER_NAME(o.uid) + '.' + o.name, i.indid, k.pos) AS col_name
+ FROM dbo.sysindexes i
+ JOIN dbo.sysobjects o ON i.id = o.id
+ CROSS JOIN (
+ SELECT 1 AS pos UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
+ UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
+ UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
+ UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
+ ) k
+ WHERE (i.status & 2048) = 2048
+ AND index_col(USER_NAME(o.uid) + '.' + o.name, i.indid, k.pos) IS NOT NULL
+ ) pk ON pk.id = o.id AND pk.col_name = c.name
+ LEFT JOIN (
+ SELECT DISTINCT
+ i.id,
+ index_col(USER_NAME(o.uid) + '.' + o.name, i.indid, k.pos) AS col_name
+ FROM dbo.sysindexes i
+ JOIN dbo.sysobjects o ON i.id = o.id
+ CROSS JOIN (
+ SELECT 1 AS pos UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
+ UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
+ UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
+ UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
+ ) k
+ WHERE (i.status & 2) = 2
+ AND index_col(USER_NAME(o.uid) + '.' + o.name, i.indid, k.pos) IS NOT NULL
+ ) uq ON uq.id = o.id AND uq.col_name = c.name
+ WHERE o.type IN ('U', 'V')
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:table_name IS NULL OR o.name = :table_name)
+ ORDER BY u.name, o.name, c.colid;
+
+ -- name: by_table
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ c.name AS column_name,
+ t.name AS data_type,
+ CASE WHEN (c.status & 8) = 8 THEN 'YES' ELSE 'NO' END AS is_nullable,
+ cm.text AS column_default,
+ c.colid AS ordinal_position,
+ c.length AS char_length,
+ c.length AS max_length,
+ c.prec AS numeric_precision,
+ c.scale AS numeric_scale,
+ CASE WHEN pk.col_name IS NOT NULL THEN 1 ELSE 0 END AS is_primary,
+ CASE WHEN uq.col_name IS NOT NULL THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN (c.status & 128) = 128 THEN 1 ELSE 0 END AS is_identity,
+ CASE WHEN (c.status & 128) = 128 THEN 'ALWAYS' ELSE NULL END AS identity_generation,
+ 0 AS is_generated,
+ NULL AS generation_expression,
+ 0 AS is_hidden,
+ NULL AS column_comment
+ FROM dbo.syscolumns c
+ JOIN dbo.sysobjects o ON c.id = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ JOIN dbo.systypes t ON c.usertype = t.usertype
+ LEFT JOIN dbo.syscomments cm ON c.cdefault = cm.id AND cm.colid = 1
+ LEFT JOIN (
+ SELECT DISTINCT
+ i.id,
+ index_col(USER_NAME(o.uid) + '.' + o.name, i.indid, k.pos) AS col_name
+ FROM dbo.sysindexes i
+ JOIN dbo.sysobjects o ON i.id = o.id
+ CROSS JOIN (
+ SELECT 1 AS pos UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
+ UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
+ UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
+ UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
+ ) k
+ WHERE (i.status & 2048) = 2048
+ AND index_col(USER_NAME(o.uid) + '.' + o.name, i.indid, k.pos) IS NOT NULL
+ ) pk ON pk.id = o.id AND pk.col_name = c.name
+ LEFT JOIN (
+ SELECT DISTINCT
+ i.id,
+ index_col(USER_NAME(o.uid) + '.' + o.name, i.indid, k.pos) AS col_name
+ FROM dbo.sysindexes i
+ JOIN dbo.sysobjects o ON i.id = o.id
+ CROSS JOIN (
+ SELECT 1 AS pos UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
+ UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
+ UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
+ UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
+ ) k
+ WHERE (i.status & 2) = 2
+ AND index_col(USER_NAME(o.uid) + '.' + o.name, i.indid, k.pos) IS NOT NULL
+ ) uq ON uq.id = o.id AND uq.col_name = c.name
+ WHERE o.type IN ('U', 'V')
+ AND o.name = :table_name
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ ORDER BY c.colid;
+ ```
+
+??? code "`constraints.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ co.name AS constraint_name,
+ CASE
+ WHEN (c.status & 1) = 1 THEN 'PRIMARY KEY'
+ WHEN (c.status & 2) = 2 THEN 'UNIQUE'
+ WHEN (c.status & 64) = 64 THEN 'FOREIGN KEY'
+ WHEN (c.status & 128) = 128 THEN 'CHECK'
+ ELSE 'CONSTRAINT'
+ END AS constraint_type,
+ cm.text AS check_clause,
+ 'Y' AS is_enforced
+ FROM dbo.sysconstraints c
+ JOIN dbo.sysobjects o ON c.tableid = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ JOIN dbo.sysobjects co ON c.constrid = co.id
+ LEFT JOIN dbo.syscomments cm ON co.id = cm.id AND cm.colid = 1
+ WHERE u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:table_name IS NULL OR o.name = :table_name)
+ UNION
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ i.name AS constraint_name,
+ CASE
+ WHEN (i.status & 2048) = 2048 THEN 'PRIMARY KEY'
+ WHEN (i.status & 2) = 2 THEN 'UNIQUE'
+ END AS constraint_type,
+ NULL AS check_clause,
+ 'Y' AS is_enforced
+ FROM dbo.sysindexes i
+ JOIN dbo.sysobjects o ON i.id = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ WHERE o.type = 'U'
+ AND ((i.status & 2048) = 2048 OR (i.status & 2) = 2)
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:table_name IS NULL OR o.name = :table_name)
+ AND NOT EXISTS (
+ SELECT 1
+ FROM dbo.sysconstraints sc
+ JOIN dbo.sysobjects sco ON sc.constrid = sco.id
+ WHERE sc.tableid = o.id AND sco.name = i.name
+ )
+ ORDER BY schema_name, table_name, constraint_name;
+
+ -- name: by_table
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ co.name AS constraint_name,
+ CASE
+ WHEN (c.status & 1) = 1 THEN 'PRIMARY KEY'
+ WHEN (c.status & 2) = 2 THEN 'UNIQUE'
+ WHEN (c.status & 64) = 64 THEN 'FOREIGN KEY'
+ WHEN (c.status & 128) = 128 THEN 'CHECK'
+ ELSE 'CONSTRAINT'
+ END AS constraint_type,
+ cm.text AS check_clause,
+ 'Y' AS is_enforced
+ FROM dbo.sysconstraints c
+ JOIN dbo.sysobjects o ON c.tableid = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ JOIN dbo.sysobjects co ON c.constrid = co.id
+ LEFT JOIN dbo.syscomments cm ON co.id = cm.id AND cm.colid = 1
+ WHERE o.name = :table_name
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ UNION
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ i.name AS constraint_name,
+ CASE
+ WHEN (i.status & 2048) = 2048 THEN 'PRIMARY KEY'
+ WHEN (i.status & 2) = 2 THEN 'UNIQUE'
+ END AS constraint_type,
+ NULL AS check_clause,
+ 'Y' AS is_enforced
+ FROM dbo.sysindexes i
+ JOIN dbo.sysobjects o ON i.id = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ WHERE o.type = 'U'
+ AND ((i.status & 2048) = 2048 OR (i.status & 2) = 2)
+ AND o.name = :table_name
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND NOT EXISTS (
+ SELECT 1
+ FROM dbo.sysconstraints sc
+ JOIN dbo.sysobjects sco ON sc.constrid = sco.id
+ WHERE sc.tableid = o.id AND sco.name = i.name
+ )
+ ORDER BY constraint_name;
+ ```
+
+??? code "`foreign_keys.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ co.name AS constraint_name,
+ fc.name AS column_name,
+ ru.name AS referenced_schema,
+ ro.name AS referenced_table,
+ rc.name AS referenced_column
+ FROM dbo.sysreferences r
+ JOIN dbo.sysobjects o ON r.tableid = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ JOIN dbo.sysobjects co ON r.constrid = co.id
+ JOIN dbo.sysobjects ro ON r.reftabid = ro.id
+ JOIN dbo.sysusers ru ON ro.uid = ru.uid
+ CROSS JOIN (
+ SELECT 1 AS pos UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
+ UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
+ UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
+ UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
+ ) k
+ JOIN dbo.syscolumns fc ON fc.id = o.id AND fc.colid = CASE k.pos
+ WHEN 1 THEN r.fokey1 WHEN 2 THEN r.fokey2 WHEN 3 THEN r.fokey3 WHEN 4 THEN r.fokey4
+ WHEN 5 THEN r.fokey5 WHEN 6 THEN r.fokey6 WHEN 7 THEN r.fokey7 WHEN 8 THEN r.fokey8
+ WHEN 9 THEN r.fokey9 WHEN 10 THEN r.fokey10 WHEN 11 THEN r.fokey11 WHEN 12 THEN r.fokey12
+ WHEN 13 THEN r.fokey13 WHEN 14 THEN r.fokey14 WHEN 15 THEN r.fokey15 WHEN 16 THEN r.fokey16
+ END
+ JOIN dbo.syscolumns rc ON rc.id = ro.id AND rc.colid = CASE k.pos
+ WHEN 1 THEN r.refkey1 WHEN 2 THEN r.refkey2 WHEN 3 THEN r.refkey3 WHEN 4 THEN r.refkey4
+ WHEN 5 THEN r.refkey5 WHEN 6 THEN r.refkey6 WHEN 7 THEN r.refkey7 WHEN 8 THEN r.refkey8
+ WHEN 9 THEN r.refkey9 WHEN 10 THEN r.refkey10 WHEN 11 THEN r.refkey11 WHEN 12 THEN r.refkey12
+ WHEN 13 THEN r.refkey13 WHEN 14 THEN r.refkey14 WHEN 15 THEN r.refkey15 WHEN 16 THEN r.refkey16
+ END
+ WHERE k.pos <= r.frgcnt
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:table_name IS NULL OR o.name = :table_name)
+ ORDER BY u.name, o.name, co.name, k.pos;
+
+ -- name: by_table
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ co.name AS constraint_name,
+ fc.name AS column_name,
+ ru.name AS referenced_schema,
+ ro.name AS referenced_table,
+ rc.name AS referenced_column
+ FROM dbo.sysreferences r
+ JOIN dbo.sysobjects o ON r.tableid = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ JOIN dbo.sysobjects co ON r.constrid = co.id
+ JOIN dbo.sysobjects ro ON r.reftabid = ro.id
+ JOIN dbo.sysusers ru ON ro.uid = ru.uid
+ CROSS JOIN (
+ SELECT 1 AS pos UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
+ UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
+ UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
+ UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
+ ) k
+ JOIN dbo.syscolumns fc ON fc.id = o.id AND fc.colid = CASE k.pos
+ WHEN 1 THEN r.fokey1 WHEN 2 THEN r.fokey2 WHEN 3 THEN r.fokey3 WHEN 4 THEN r.fokey4
+ WHEN 5 THEN r.fokey5 WHEN 6 THEN r.fokey6 WHEN 7 THEN r.fokey7 WHEN 8 THEN r.fokey8
+ WHEN 9 THEN r.fokey9 WHEN 10 THEN r.fokey10 WHEN 11 THEN r.fokey11 WHEN 12 THEN r.fokey12
+ WHEN 13 THEN r.fokey13 WHEN 14 THEN r.fokey14 WHEN 15 THEN r.fokey15 WHEN 16 THEN r.fokey16
+ END
+ JOIN dbo.syscolumns rc ON rc.id = ro.id AND rc.colid = CASE k.pos
+ WHEN 1 THEN r.refkey1 WHEN 2 THEN r.refkey2 WHEN 3 THEN r.refkey3 WHEN 4 THEN r.refkey4
+ WHEN 5 THEN r.refkey5 WHEN 6 THEN r.refkey6 WHEN 7 THEN r.refkey7 WHEN 8 THEN r.refkey8
+ WHEN 9 THEN r.refkey9 WHEN 10 THEN r.refkey10 WHEN 11 THEN r.refkey11 WHEN 12 THEN r.refkey12
+ WHEN 13 THEN r.refkey13 WHEN 14 THEN r.refkey14 WHEN 15 THEN r.refkey15 WHEN 16 THEN r.refkey16
+ END
+ WHERE k.pos <= r.frgcnt
+ AND o.name = :table_name
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ ORDER BY co.name, k.pos;
+ ```
+
+??? code "`indexes.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ i.name AS index_name,
+ index_col(u.name + '.' + o.name, i.indid, k.pos) AS column_name,
+ k.pos AS column_position,
+ CASE WHEN (i.status & 2) = 2 THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN (i.status & 2048) = 2048 THEN 1 ELSE 0 END AS is_primary
+ FROM dbo.sysindexes i
+ JOIN dbo.sysobjects o ON i.id = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ CROSS JOIN (
+ SELECT 1 AS pos UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
+ UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
+ UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
+ UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
+ ) k
+ WHERE o.type = 'U'
+ AND i.indid BETWEEN 1 AND 254
+ AND index_col(u.name + '.' + o.name, i.indid, k.pos) IS NOT NULL
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:table_name IS NULL OR o.name = :table_name)
+ ORDER BY u.name, o.name, i.name, k.pos;
+
+ -- name: by_table
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ i.name AS index_name,
+ index_col(u.name + '.' + o.name, i.indid, k.pos) AS column_name,
+ k.pos AS column_position,
+ CASE WHEN (i.status & 2) = 2 THEN 1 ELSE 0 END AS is_unique,
+ CASE WHEN (i.status & 2048) = 2048 THEN 1 ELSE 0 END AS is_primary
+ FROM dbo.sysindexes i
+ JOIN dbo.sysobjects o ON i.id = o.id
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ CROSS JOIN (
+ SELECT 1 AS pos UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4
+ UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
+ UNION ALL SELECT 9 UNION ALL SELECT 10 UNION ALL SELECT 11 UNION ALL SELECT 12
+ UNION ALL SELECT 13 UNION ALL SELECT 14 UNION ALL SELECT 15 UNION ALL SELECT 16
+ ) k
+ WHERE o.type = 'U'
+ AND i.indid BETWEEN 1 AND 254
+ AND index_col(u.name + '.' + o.name, i.indid, k.pos) IS NOT NULL
+ AND o.name = :table_name
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ ORDER BY i.name, k.pos;
+ ```
+
+??? code "`objects.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS object_name,
+ CASE o.type
+ WHEN 'U' THEN 'TABLE'
+ WHEN 'V' THEN 'VIEW'
+ WHEN 'P' THEN 'PROCEDURE'
+ WHEN 'SF' THEN 'FUNCTION'
+ WHEN 'TR' THEN 'TRIGGER'
+ ELSE o.type
+ END AS object_type,
+ o.crdate AS created,
+ NULL AS remarks
+ FROM dbo.sysobjects o
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ WHERE o.type IN ('U', 'V', 'P', 'SF', 'TR')
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:object_name IS NULL OR o.name = :object_name)
+ ORDER BY u.name, object_type, o.name;
+ ```
+
+??? code "`schemas.sql` (2 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ u.name AS owner_name,
+ NULL AS create_time,
+ NULL AS comment
+ FROM dbo.sysusers u
+ WHERE u.uid < 16384
+ AND u.name NOT IN ('public', 'probe', 'guest')
+ AND (:schema_name IS NULL OR u.name = :schema_name)
+ ORDER BY u.name;
+
+ -- name: current
+ -- dialect: sybase
+ SELECT USER_NAME() AS schema_name;
+ ```
+
+??? code "`tables.sql` (3 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ 'BASE TABLE' AS table_type,
+ 0 AS dependency_level,
+ 0 AS level
+ FROM dbo.sysobjects o
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ WHERE o.type = 'U'
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:table_name IS NULL OR o.name = :table_name)
+ ORDER BY u.name, o.name;
+
+ -- name: all_by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS table_name,
+ CASE o.type
+ WHEN 'U' THEN 'BASE TABLE'
+ WHEN 'V' THEN 'VIEW'
+ WHEN 'S' THEN 'SYSTEM TABLE'
+ ELSE 'TABLE'
+ END AS table_type,
+ 0 AS dependency_level,
+ 0 AS level,
+ NULL AS estimated_row_count,
+ NULL AS table_comment
+ FROM dbo.sysobjects o
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ WHERE o.type IN ('U', 'V')
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:table_name IS NULL OR o.name = :table_name)
+ ORDER BY u.name, o.name;
+
+ -- name: names_by_schema
+ -- dialect: sybase
+ SELECT
+ o.name AS table_name
+ FROM dbo.sysobjects o
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ WHERE o.type = 'U'
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ ORDER BY o.name;
+ ```
+
+??? code "`version.sql` (1 named queries)"
+
+ ```sql
+ -- name: current
+ -- dialect: sybase
+ SELECT @@version AS version_string;
+ ```
+
+??? code "`views.sql` (1 named queries)"
+
+ ```sql
+ -- name: by_schema
+ -- dialect: sybase
+ SELECT
+ u.name AS schema_name,
+ o.name AS view_name,
+ cm.text AS definition,
+ 0 AS is_read_only,
+ 1 AS is_valid
+ FROM dbo.sysobjects o
+ JOIN dbo.sysusers u ON o.uid = u.uid
+ LEFT JOIN dbo.syscomments cm ON o.id = cm.id AND cm.colid = 1
+ WHERE o.type = 'V'
+ AND u.name = COALESCE(:schema_name, USER_NAME())
+ AND (:view_name IS NULL OR o.name = :view_name)
+ ORDER BY u.name, o.name;
+ ```
diff --git a/docs/reference/data-dictionary/runtime.md b/docs/reference/data-dictionary/runtime.md
index 4668ac908..eecb17a4b 100644
--- a/docs/reference/data-dictionary/runtime.md
+++ b/docs/reference/data-dictionary/runtime.md
@@ -1,6 +1,6 @@
# Runtime Introspection & DDL Synthesis
-Runtime introspection, DDL extraction, and column profiling methods provided by `driver.data_dictionary` on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md).
+Runtime introspection, DDL extraction, and column profiling methods provided by `driver.data_dictionary` on [`SyncDriverAdapterBase`](../driver/sync.md) and [`AsyncDriverAdapterBase`](../driver/async.md). For dialect-specific system catalog SQL queries (`-- name: ...`) and capability profiles, see [Dialect SQL Catalogs](dialects/index.md).
## Synchronous Introspection
@@ -8,11 +8,27 @@ Runtime introspection, DDL extraction, and column profiling methods provided by
options:
heading_level: 3
members:
+ - get_version
+ - get_feature_flag
+ - get_optimal_type
+ - list_available_features
+ - get_metadata_capabilities
+ - get_schemas
+ - get_tables
+ - get_columns
+ - get_indexes
+ - get_foreign_keys
+ - get_constraints
+ - get_views
+ - get_routines
+ - get_sequences
+ - get_types
- get_ddl
- get_object_ddl
- get_schema_ddl
- get_table_profile
- infer_table_ddl_from_data
+ - get_system_metadata
## Asynchronous Introspection
@@ -20,8 +36,24 @@ Runtime introspection, DDL extraction, and column profiling methods provided by
options:
heading_level: 3
members:
+ - get_version
+ - get_feature_flag
+ - get_optimal_type
+ - list_available_features
+ - get_metadata_capabilities
+ - get_schemas
+ - get_tables
+ - get_columns
+ - get_indexes
+ - get_foreign_keys
+ - get_constraints
+ - get_views
+ - get_routines
+ - get_sequences
+ - get_types
- get_ddl
- get_object_ddl
- get_schema_ddl
- get_table_profile
- infer_table_ddl_from_data
+ - get_system_metadata
diff --git a/docs/reference/dialects.md b/docs/reference/dialects.md
index a1345065f..547c9aa1f 100644
--- a/docs/reference/dialects.md
+++ b/docs/reference/dialects.md
@@ -8,6 +8,8 @@ from sqlspec.dialects import (
PGVector,
Spangres,
Spanner,
+ SQLAnywhere,
+ Sybase,
)
```
@@ -23,6 +25,16 @@ Custom SQLGlot dialect implementations registered automatically via Python entry
options:
heading_level: 3
+## Sybase ASE & SAP SQL Anywhere Dialects
+
+::: sqlspec.dialects.sybase.Sybase
+ options:
+ heading_level: 3
+
+::: sqlspec.dialects.sybase.SQLAnywhere
+ options:
+ heading_level: 3
+
## IBM Db2 Dialect
::: sqlspec.dialects.db2.DB2
diff --git a/docs/reference/driver/async.md b/docs/reference/driver/async.md
index 6de178521..b1927e79f 100644
--- a/docs/reference/driver/async.md
+++ b/docs/reference/driver/async.md
@@ -10,7 +10,7 @@ from sqlspec.driver import (
)
```
-Base asynchronous driver adapter class inherited by all asynchronous SQLSpec database adapters (`AsyncpgDriver`, `PsycopgAsyncDriver`, `PsqlpyDriver`, `AiosqliteDriver`, `AsyncmyDriver`, `AiomysqlDriver`, `OracleAsyncDriver`, `SpannerAsyncDriver`, `Db2AsyncDriver`, `MssqlAsyncDriver`, `CockroachAsyncpgDriver`, `CockroachPsycopgAsyncDriver`). See [Querying](../../learn/querying.md) for usage examples.
+Base asynchronous driver adapter class inherited by all asynchronous SQLSpec database adapters (`AsyncpgDriver`, `PsycopgAsyncDriver`, `PsqlpyDriver`, `AiosqliteDriver`, `AsyncmyDriver`, `AiomysqlDriver`, `OracleAsyncDriver`, `SpannerAsyncDriver`, `Db2AsyncDriver`, `CockroachAsyncpgDriver`, `CockroachPsycopgAsyncDriver`). See [Querying](../../learn/querying.md) for usage examples.
## Asynchronous Driver Adapter
@@ -28,8 +28,12 @@ Base asynchronous driver adapter class inherited by all asynchronous SQLSpec dat
- select_value
- select_value_or_none
- select_with_total
+ - select_stream
- select_to_arrow
+ - select_to_storage
- load_from_arrow
+ - load_from_storage
+ - without_indexes
- begin
- commit
- rollback
@@ -37,7 +41,7 @@ Base asynchronous driver adapter class inherited by all asynchronous SQLSpec dat
## Asynchronous Data Dictionary & Streaming
-See [Data Dictionary](../data-dictionary.md) for catalog introspection, DDL extraction, and column profiling method signatures.
+See [Runtime Introspection & DDL Synthesis](../data-dictionary/runtime.md) and [Dialect SQL Catalogs](../data-dictionary/dialects/index.md) for full `AsyncDataDictionaryBase` method signatures and SQL queries.
::: sqlspec.driver._async.AsyncDataDictionaryBase
options:
@@ -47,16 +51,13 @@ See [Data Dictionary](../data-dictionary.md) for catalog introspection, DDL extr
::: sqlspec.driver._stream.AsyncRowStream
options:
heading_level: 3
- members: false
## Pool Context Managers
::: sqlspec.driver._async.AsyncPoolConnectionContext
options:
heading_level: 3
- members: false
::: sqlspec.driver._async.AsyncPoolSessionFactory
options:
heading_level: 3
- members: false
diff --git a/docs/reference/driver/sync.md b/docs/reference/driver/sync.md
index f60a58c58..263c72cef 100644
--- a/docs/reference/driver/sync.md
+++ b/docs/reference/driver/sync.md
@@ -10,7 +10,7 @@ from sqlspec.driver import (
)
```
-Base synchronous driver adapter class inherited by all synchronous SQLSpec database adapters (`SqliteDriver`, `PsycopgSyncDriver`, `PymysqlDriver`, `OracleSyncDriver`, `DuckDBDriver`, `BigQueryDriver`, `SpannerSyncDriver`, `Db2SyncDriver`, `MssqlSyncDriver`, `PymssqlDriver`, `AdbcDriver`, `ArrowOdbcDriver`). See [Querying](../../learn/querying.md) for usage examples.
+Base synchronous driver adapter class inherited by all synchronous SQLSpec database adapters (`SqliteDriver`, `PsycopgSyncDriver`, `PymysqlDriver`, `OracleSyncDriver`, `DuckDBDriver`, `BigQueryDriver`, `SpannerSyncDriver`, `Db2SyncDriver`, `MssqlSyncDriver`, `PymssqlDriver`, `SybaseSyncDriver`, `SqlAnywhereSyncDriver`, `AdbcDriver`, `ArrowOdbcDriver`). See [Querying](../../learn/querying.md) for usage examples.
## Synchronous Driver Adapter
@@ -28,8 +28,12 @@ Base synchronous driver adapter class inherited by all synchronous SQLSpec datab
- select_value
- select_value_or_none
- select_with_total
+ - select_stream
- select_to_arrow
+ - select_to_storage
- load_from_arrow
+ - load_from_storage
+ - without_indexes
- begin
- commit
- rollback
@@ -37,7 +41,7 @@ Base synchronous driver adapter class inherited by all synchronous SQLSpec datab
## Synchronous Data Dictionary & Streaming
-See [Data Dictionary](../data-dictionary.md) for catalog introspection, DDL extraction, and column profiling method signatures.
+See [Runtime Introspection & DDL Synthesis](../data-dictionary/runtime.md) and [Dialect SQL Catalogs](../data-dictionary/dialects/index.md) for full `SyncDataDictionaryBase` method signatures and SQL queries.
::: sqlspec.driver._sync.SyncDataDictionaryBase
options:
@@ -47,16 +51,13 @@ See [Data Dictionary](../data-dictionary.md) for catalog introspection, DDL extr
::: sqlspec.driver._stream.SyncRowStream
options:
heading_level: 3
- members: false
## Pool Context Managers
::: sqlspec.driver._sync.SyncPoolConnectionContext
options:
heading_level: 3
- members: false
::: sqlspec.driver._sync.SyncPoolSessionFactory
options:
heading_level: 3
- members: false
diff --git a/docs/reference/extensions/adk.md b/docs/reference/extensions/adk.md
index 2372db687..1eda8ef13 100644
--- a/docs/reference/extensions/adk.md
+++ b/docs/reference/extensions/adk.md
@@ -27,7 +27,7 @@ from sqlspec.extensions.adk import (
)
```
-Session, event, long-term memory, and artifact metadata services and base stores for the Google Agent Development Kit (ADK). Adapter-specific ADK store subclasses are documented on each [Database Adapter](../adapters/index.md) page. See the [Google ADK Integration Guide](../../integrations/adk.md) for setup examples.
+Session, event, long-term memory, and artifact metadata services and base stores for the Google Agent Development Kit (ADK). Adapter-specific ADK store and memory store subclasses are documented on each [Database Adapter](../adapters/index.md) page. See the [Google ADK Integration Guide](../../integrations/adk.md) for setup examples.
## ADK Services
@@ -37,54 +37,122 @@ Session, event, long-term memory, and artifact metadata services and base stores
members:
- create_session
- get_session
+ - list_sessions
+ - delete_session
- append_event
+ - get_user_state
+ - close
::: sqlspec.extensions.adk.memory.SQLSpecMemoryService
options:
heading_level: 3
- members: false
+ members:
+ - add_session_to_memory
+ - add_events_to_memory
+ - add_memory
+ - search_memory
::: sqlspec.extensions.adk.memory.SQLSpecSyncMemoryService
options:
heading_level: 3
- members: false
+ members:
+ - add_session_to_memory
+ - add_events_to_memory
+ - add_memory
+ - search_memory
::: sqlspec.extensions.adk.artifact.SQLSpecArtifactService
options:
heading_level: 3
- members: false
+ members:
+ - save_artifact
+ - load_artifact
+ - list_artifact_keys
+ - list_versions
+ - delete_artifact
+ - delete_artifacts_older_than
## Base ADK Stores
::: sqlspec.extensions.adk.store.BaseAsyncADKStore
options:
heading_level: 4
- members: false
+ members:
+ - create_tables
+ - ensure_tables
+ - reconcile_schema
+ - create_session
+ - get_session
+ - list_sessions
+ - delete_session
+ - append_event
+ - append_event_and_update_state
+ - get_events
::: sqlspec.extensions.adk.store.BaseSyncADKStore
options:
heading_level: 4
- members: false
+ members:
+ - create_tables
+ - ensure_tables
+ - reconcile_schema
+ - create_session
+ - get_session
+ - list_sessions
+ - delete_session
+ - append_event
+ - append_event_and_update_state
+ - get_events
::: sqlspec.extensions.adk.memory.BaseAsyncADKMemoryStore
options:
heading_level: 4
- members: false
+ members:
+ - create_tables
+ - ensure_tables
+ - reconcile_schema
+ - insert_memory_entries
+ - search_entries
+ - delete_entries_by_session
+ - delete_entries_older_than
::: sqlspec.extensions.adk.memory.BaseSyncADKMemoryStore
options:
heading_level: 4
- members: false
+ members:
+ - create_tables
+ - ensure_tables
+ - reconcile_schema
+ - insert_memory_entries
+ - search_entries
+ - delete_entries_by_session
+ - delete_entries_older_than
::: sqlspec.extensions.adk.artifact.BaseAsyncADKArtifactStore
options:
heading_level: 4
- members: false
+ members:
+ - create_table
+ - ensure_table
+ - insert_artifact
+ - get_artifact
+ - list_artifact_keys
+ - list_artifact_versions
+ - delete_artifact
+ - delete_artifacts_older_than
::: sqlspec.extensions.adk.artifact.BaseSyncADKArtifactStore
options:
heading_level: 4
- members: false
+ members:
+ - create_table
+ - ensure_table
+ - insert_artifact
+ - get_artifact
+ - list_artifact_keys
+ - list_artifact_versions
+ - delete_artifact
+ - delete_artifacts_older_than
## Configuration & Record Schemas
@@ -97,30 +165,41 @@ Session, event, long-term memory, and artifact metadata services and base stores
::: sqlspec.extensions.adk._types.StoredSession
options:
heading_level: 4
- members: false
::: sqlspec.extensions.adk._types.StoredEvent
options:
heading_level: 4
- members: false
::: sqlspec.extensions.adk.memory.StoredMemory
options:
heading_level: 4
- members: false
::: sqlspec.extensions.adk.artifact.StoredArtifact
options:
heading_level: 4
- members: false
## Maintenance & Pruning Helpers
::: sqlspec.extensions.adk.maintenance.PruneReport
options:
heading_level: 4
- members: false
::: sqlspec.extensions.adk.maintenance.prune_sessions
options:
heading_level: 4
+
+::: sqlspec.extensions.adk.maintenance.prune_events
+ options:
+ heading_level: 4
+
+::: sqlspec.extensions.adk.maintenance.prune_memory
+ options:
+ heading_level: 4
+
+::: sqlspec.extensions.adk.maintenance.prune_artifacts
+ options:
+ heading_level: 4
+
+::: sqlspec.extensions.adk.maintenance.prune_user_state
+ options:
+ heading_level: 4
diff --git a/docs/reference/extensions/events.md b/docs/reference/extensions/events.md
index 684a10be7..042d04ad5 100644
--- a/docs/reference/extensions/events.md
+++ b/docs/reference/extensions/events.md
@@ -18,7 +18,7 @@ from sqlspec.extensions.events import (
)
```
-Database-native pub/sub (`LISTEN/NOTIFY`, Oracle Advanced Queuing) and durable table-backed event queues across all supported adapters. See the [Event Channels Guide](../../integrations/events.md) for usage examples.
+Database-native pub/sub (`LISTEN/NOTIFY`, Oracle Advanced Queuing) and durable table-backed event queues across all supported adapters. Adapter-specific `EventQueueStore` and native `EventsBackend` classes are documented on each [Database Adapter](../adapters/index.md) page. See the [Event Channels Guide](../../integrations/events.md) for usage examples.
## Event Channels & Listeners
@@ -61,19 +61,32 @@ Database-native pub/sub (`LISTEN/NOTIFY`, Oracle Advanced Queuing) and durable t
::: sqlspec.extensions.events._models.EventMessage
options:
heading_level: 3
- members: false
## Durable Table Queues & Stores
::: sqlspec.extensions.events._queue.SyncTableEventQueue
options:
heading_level: 3
- members: false
+ members:
+ - publish
+ - publish_many
+ - dequeue
+ - dequeue_by_event_id
+ - ack
+ - nack
+ - shutdown
::: sqlspec.extensions.events._queue.AsyncTableEventQueue
options:
heading_level: 3
- members: false
+ members:
+ - publish
+ - publish_many
+ - dequeue
+ - dequeue_by_event_id
+ - ack
+ - nack
+ - shutdown
::: sqlspec.extensions.events._store.BaseEventQueueStore
options:
@@ -81,6 +94,10 @@ Database-native pub/sub (`LISTEN/NOTIFY`, Oracle Advanced Queuing) and durable t
members:
- create_statements
- drop_statements
+ - prepare_schema_sync
+ - prepare_schema_async
+ - reconcile_schema_sync
+ - reconcile_schema_async
::: sqlspec.extensions.events._queue.build_queue_backend
options:
@@ -91,7 +108,6 @@ Database-native pub/sub (`LISTEN/NOTIFY`, Oracle Advanced Queuing) and durable t
::: sqlspec.extensions.events._hints.EventRuntimeHints
options:
heading_level: 4
- members: false
::: sqlspec.extensions.events._hints.get_runtime_hints
options:
@@ -100,12 +116,10 @@ Database-native pub/sub (`LISTEN/NOTIFY`, Oracle Advanced Queuing) and durable t
::: sqlspec.extensions.events._protocols.SyncEventBackendProtocol
options:
heading_level: 4
- members: false
::: sqlspec.extensions.events._protocols.AsyncEventBackendProtocol
options:
heading_level: 4
- members: false
::: sqlspec.extensions.events._payload.encode_notify_payload
options:
diff --git a/docs/reference/extensions/fastapi.md b/docs/reference/extensions/fastapi.md
index 85f5030bb..f92d07763 100644
--- a/docs/reference/extensions/fastapi.md
+++ b/docs/reference/extensions/fastapi.md
@@ -33,12 +33,10 @@ FastAPI lifespan plugin, request-scoped `Depends()` session/connection providers
::: sqlspec.extensions.starlette.middleware.SQLSpecAutocommitMiddleware
options:
heading_level: 3
- members: false
::: sqlspec.extensions.starlette.middleware.SQLSpecManualMiddleware
options:
heading_level: 3
- members: false
## Filter Dependency Providers
@@ -49,9 +47,7 @@ FastAPI lifespan plugin, request-scoped `Depends()` session/connection providers
::: sqlspec.extensions.fastapi.providers.FilterConfig
options:
heading_level: 3
- members: false
::: sqlspec.extensions.fastapi.providers.DependencyDefaults
options:
heading_level: 3
- members: false
diff --git a/docs/reference/extensions/litestar.md b/docs/reference/extensions/litestar.md
index a8c11c0b5..45cc4776c 100644
--- a/docs/reference/extensions/litestar.md
+++ b/docs/reference/extensions/litestar.md
@@ -16,7 +16,7 @@ from sqlspec.extensions.litestar import (
)
```
-Litestar plugin, ASGI transaction and correlation middlewares, server-side session store base class, and Litestar Channels backend. See the [Litestar Integration Guide](../../integrations/litestar.md) for usage examples.
+Litestar plugin, ASGI transaction and correlation middlewares, server-side session store base class, and Litestar Channels backend. Adapter-specific `LitestarConfig` TypedDicts and `BaseSQLSpecStore` subclasses are documented on each [Database Adapter](../adapters/index.md) page. See the [Litestar Integration Guide](../../integrations/litestar.md) for usage examples.
## Plugin & Configuration
@@ -33,7 +33,6 @@ Litestar plugin, ASGI transaction and correlation middlewares, server-side sessi
::: sqlspec.extensions.litestar.plugin.CommitMode
options:
heading_level: 3
- members: false
::: sqlspec.config.LitestarConfig
options:
@@ -46,7 +45,6 @@ Litestar plugin, ASGI transaction and correlation middlewares, server-side sessi
::: sqlspec.extensions.litestar.plugin.CorrelationMiddleware
options:
heading_level: 3
- members: false
::: sqlspec.extensions.litestar.store.BaseSQLSpecStore
options:
diff --git a/docs/reference/observability.md b/docs/reference/observability.md
index bfd8d6a20..a2d9af6ad 100644
--- a/docs/reference/observability.md
+++ b/docs/reference/observability.md
@@ -39,38 +39,39 @@ Structured statement observers, parameter redaction, OpenTelemetry span manageme
::: sqlspec.observability._config.TelemetryConfig
options:
heading_level: 3
- members: false
::: sqlspec.observability._config.LoggingConfig
options:
heading_level: 3
- members: false
::: sqlspec.observability._config.RedactionConfig
options:
heading_level: 3
- members: false
::: sqlspec.observability._sampling.SamplingConfig
options:
heading_level: 3
- members: false
::: sqlspec.observability._runtime.ObservabilityRuntime
options:
heading_level: 3
members:
+ - create_statement_event
- emit_statement_event
+ - start_query_span
+ - start_storage_span
- start_span
+ - end_span
- increment_metric
- - diagnostics_snapshot
+ - record_metric
+ - lifecycle_snapshot
+ - metrics_snapshot
## Statement Events & Observers
::: sqlspec.observability._observer.StatementEvent
options:
heading_level: 3
- members: false
::: sqlspec.observability._observer.create_event
options:
@@ -93,51 +94,44 @@ Structured statement observers, parameter redaction, OpenTelemetry span manageme
::: sqlspec.observability._spans.SpanManager
options:
heading_level: 4
- members: false
::: sqlspec.observability._dispatcher.LifecycleDispatcher
options:
heading_level: 4
- members: false
+ members:
+ - register_hook
+ - snapshot
::: sqlspec.observability._dispatcher.LifecycleContext
options:
heading_level: 4
- members: false
::: sqlspec.observability._diagnostics.TelemetryDiagnostics
options:
heading_level: 4
- members: false
## Cloud & OpenTelemetry Log Formatters
::: sqlspec.observability._formatters.CloudLogFormatter
options:
heading_level: 4
- members: false
::: sqlspec.observability._formatters.GCPLogFormatter
options:
heading_level: 4
- members: false
::: sqlspec.observability._formatters.AWSLogFormatter
options:
heading_level: 4
- members: false
::: sqlspec.observability._formatters.AzureLogFormatter
options:
heading_level: 4
- members: false
::: sqlspec.observability._formatting.OTelJSONFormatter
options:
heading_level: 4
- members: false
::: sqlspec.observability._formatting.OTelConsoleFormatter
options:
heading_level: 4
- members: false
diff --git a/docs/reference/utils.md b/docs/reference/utils.md
index c97a193d1..3b22406a8 100644
--- a/docs/reference/utils.md
+++ b/docs/reference/utils.md
@@ -2,6 +2,7 @@
```python
from sqlspec.utils import (
+ arrow_helpers,
correlation,
deprecation,
env,
@@ -16,7 +17,7 @@ from sqlspec.utils import (
)
```
-Reusable helpers for runtime type narrowing, JSON serialization, schema model conversion, UUID/NanoID generation, table fixture loading/exporting, correlation tracking, and sync/async bridging.
+Reusable helpers for runtime type narrowing, JSON serialization, schema model conversion, Apache Arrow streaming and PostgreSQL binary `COPY` framing, UUID/NanoID generation, table fixture loading/exporting, correlation tracking, and sync/async bridging.
## Schema, Serialization & Identifiers
@@ -52,6 +53,24 @@ Reusable helpers for runtime type narrowing, JSON serialization, schema model co
options:
heading_level: 4
+## Apache Arrow & Binary COPY Helpers
+
+::: sqlspec.utils.arrow_helpers.coerce_arrow_table
+ options:
+ heading_level: 4
+
+::: sqlspec.utils.arrow_helpers.coerce_arrow_batches
+ options:
+ heading_level: 4
+
+::: sqlspec.utils.arrow_helpers.encode_arrow_batch_pg_binary
+ options:
+ heading_level: 4
+
+::: sqlspec.utils.arrow_helpers.iter_pgcopy_binary_chunks
+ options:
+ heading_level: 4
+
## Table Fixtures & Environment Helpers
::: sqlspec.utils.fixtures.load_table_fixtures_sync
@@ -109,7 +128,6 @@ Reusable helpers for runtime type narrowing, JSON serialization, schema model co
::: sqlspec.utils.correlation.CorrelationContext
options:
heading_level: 4
- members: false
::: sqlspec.utils.deprecation.deprecated
options:
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
index 5d773a6db..4820c14b2 100644
--- a/docs/stylesheets/extra.css
+++ b/docs/stylesheets/extra.css
@@ -582,28 +582,57 @@
.md-typeset .doc-heading {
font-weight: 600;
margin-top: 1.6rem;
+ display: flex;
+ flex-wrap: wrap;
+ align-items: center;
+ gap: 0.38rem;
}
.md-typeset h3.doc-heading {
- padding-top: 0.75rem;
+ padding-top: 0.85rem;
border-top: 1px solid var(--md-default-fg-color--lightest);
}
.md-typeset h4.doc-heading {
font-size: 0.92rem;
- margin-top: 1.15rem;
+ margin-top: 1.25rem;
+ padding-top: 0.55rem;
+ border-top: 1px dashed var(--md-default-fg-color--lightest);
+}
+
+.md-typeset .doc-signature {
+ border-radius: 7px;
+ margin-block: 0.45rem 0.75rem;
}
.md-typeset .doc-contents {
- padding-inline-start: 0.9rem;
+ padding-inline-start: 0.95rem;
border-inline-start: 2px solid var(--md-default-fg-color--lightest);
margin-bottom: 1.5rem;
}
+.md-typeset .doc-class > .doc-contents {
+ border-inline-start: 2px solid rgba(59, 130, 246, 0.28);
+}
+
+.md-typeset .doc-function > .doc-contents {
+ border-inline-start: 2px solid rgba(237, 182, 65, 0.42);
+}
+
.md-typeset .doc-contents:not(.first) {
padding-inline-start: 0.85rem;
}
+.md-typeset .doc-section-title {
+ display: inline-block;
+ font-size: 0.72rem;
+ font-weight: 700;
+ letter-spacing: 0.05em;
+ text-transform: uppercase;
+ color: var(--md-default-fg-color--light);
+ margin-top: 0.35rem;
+}
+
.md-typeset .doc-symbol {
border-radius: 4px;
font-size: 0.68rem;
@@ -624,6 +653,78 @@
border: 1px solid rgba(96, 165, 250, 0.32);
}
+.md-typeset .doc-labels {
+ display: inline-flex;
+ flex-wrap: wrap;
+ gap: 0.28rem;
+ margin-inline-start: 0.25rem;
+}
+
+.md-typeset .doc-label {
+ display: inline-flex;
+ align-items: center;
+ border-radius: 9999px;
+ font-family: var(--md-code-font-family);
+ font-size: 0.64rem;
+ font-weight: 600;
+ letter-spacing: 0.03em;
+ padding: 0.08rem 0.45rem;
+ line-height: 1.35;
+ border: 1px solid var(--sqlspec-inline-code-border);
+ background-color: var(--sqlspec-inline-code-bg);
+ color: var(--md-default-fg-color--light);
+}
+
+[data-md-color-scheme="default"] .md-typeset .doc-label-async code,
+[data-md-color-scheme="default"] .md-typeset .doc-label-async {
+ background-color: #f0f9ff;
+ border-color: #bae6fd;
+ color: #0369a1;
+}
+
+[data-md-color-scheme="slate"] .md-typeset .doc-label-async code,
+[data-md-color-scheme="slate"] .md-typeset .doc-label-async {
+ background-color: rgba(14, 165, 233, 0.16);
+ border-color: rgba(56, 189, 248, 0.35);
+ color: #7dd3fc;
+}
+
+[data-md-color-scheme="default"] .md-typeset .doc-label-classmethod,
+[data-md-color-scheme="default"] .md-typeset .doc-label-staticmethod {
+ background-color: #fffbeb;
+ border-color: #fde68a;
+ color: #b45309;
+}
+
+[data-md-color-scheme="slate"] .md-typeset .doc-label-classmethod,
+[data-md-color-scheme="slate"] .md-typeset .doc-label-staticmethod {
+ background-color: rgba(245, 158, 11, 0.15);
+ border-color: rgba(251, 191, 36, 0.35);
+ color: #fcd34d;
+}
+
+[data-md-color-scheme="default"] .md-typeset .doc-label-property,
+[data-md-color-scheme="default"] .md-typeset .doc-label-cached {
+ background-color: #ecfdf5;
+ border-color: #a7f3d0;
+ color: #047857;
+}
+
+[data-md-color-scheme="slate"] .md-typeset .doc-label-property,
+[data-md-color-scheme="slate"] .md-typeset .doc-label-cached {
+ background-color: rgba(16, 185, 129, 0.15);
+ border-color: rgba(52, 211, 153, 0.35);
+ color: #6ee7b7;
+}
+
+.md-typeset .doc-label code {
+ background: transparent;
+ border: 0;
+ padding: 0;
+ font-size: inherit;
+ color: inherit;
+}
+
.md-typeset .doc .md-typeset__table {
padding-inline: 0;
max-width: 100%;
@@ -658,6 +759,22 @@
width: 50%;
}
+.md-typeset .doc-contents table:not([class]) tr:has(> th:nth-child(4):last-child) > th:nth-child(1) {
+ width: 20%;
+}
+
+.md-typeset .doc-contents table:not([class]) tr:has(> th:nth-child(4):last-child) > th:nth-child(2) {
+ width: 24%;
+}
+
+.md-typeset .doc-contents table:not([class]) tr:has(> th:nth-child(4):last-child) > th:nth-child(3) {
+ width: 40%;
+}
+
+.md-typeset .doc-contents table:not([class]) tr:has(> th:nth-child(4):last-child) > th:nth-child(4) {
+ width: 16%;
+}
+
.md-typeset .doc-contents table:not([class]) th,
.md-typeset .doc-contents table:not([class]) td {
min-width: 0;
@@ -666,7 +783,8 @@
}
.md-typeset .doc-contents table:not([class]) td:nth-child(1) code,
-.md-typeset .doc-contents table:not([class]) td:nth-child(2) code {
+.md-typeset .doc-contents table:not([class]) td:nth-child(2) code,
+.md-typeset .doc-contents table:not([class]) td:nth-child(4) code {
white-space: normal;
overflow-wrap: anywhere;
word-break: break-word;
@@ -682,6 +800,19 @@
font-size: 0.78rem;
}
+.md-typeset > .md-typeset__scrollwrap table:not([class]) thead th {
+ background-color: var(--md-default-bg-color--light);
+ font-weight: 600;
+}
+
+.md-typeset table:not([class]) tbody tr:nth-child(even) {
+ background-color: rgba(148, 163, 184, 0.04);
+}
+
+.md-typeset table:not([class]) tbody tr:hover {
+ background-color: rgba(59, 130, 246, 0.06);
+}
+
.md-typeset > .md-typeset__scrollwrap table:not([class]) th,
.md-typeset > .md-typeset__scrollwrap table:not([class]) td {
padding: 0.55rem 0.75rem;
@@ -691,6 +822,17 @@
white-space: nowrap;
}
+.md-typeset pre.mermaid,
+.md-typeset div.mermaid {
+ background-color: var(--md-default-bg-color--light);
+ border: 1px solid var(--sqlspec-code-block-border);
+ border-radius: 10px;
+ padding: 1rem 1.25rem;
+ margin: 1.25rem 0;
+ text-align: center;
+ overflow-x: auto;
+}
+
.md-footer-meta:not(:has(.md-copyright__highlight, .md-social)) {
display: none;
}
diff --git a/pyproject.toml b/pyproject.toml
index 0ad4f0788..6fd2f187f 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -23,7 +23,7 @@ maintainers = [{ name = "Litestar Developers", email = "hello@litestar.dev" }]
name = "sqlspec"
readme = "README.md"
requires-python = ">=3.11, <4.0"
-version = "0.69.0"
+version = "0.70.0"
[project.urls]
Discord = "https://discord.gg/litestar"
@@ -292,7 +292,6 @@ include = [
"sqlspec/extensions/adk/_types.py", # ADK session/event record types
"sqlspec/extensions/adk/memory/_types.py", # ADK memory record types
"sqlspec/extensions/adk/artifact/_types.py", # ADK artifact record types
- "sqlspec/adapters/mysql_common.py", # Shared MySQL-family adapter helpers
"sqlspec/adapters/**/core.py", # Adapter compiled helpers
"sqlspec/adapters/**/type_converter.py", # All adapters type converters
"sqlspec/adapters/oracledb/_param_types.py", # Slot-based LOB/JSON parameter wrappers
@@ -340,7 +339,7 @@ opt_level = "3" # Maximum optimization (0-3)
allow_dirty = true
commit = false
commit_args = "--no-verify"
-current_version = "0.69.0"
+current_version = "0.70.0"
ignore_missing_files = false
ignore_missing_version = false
message = "chore(release): bump to v{new_version}"
diff --git a/sqlspec/adapters/adbc/core.py b/sqlspec/adapters/adbc/core.py
index bce37c48f..920addaf2 100644
--- a/sqlspec/adapters/adbc/core.py
+++ b/sqlspec/adapters/adbc/core.py
@@ -47,7 +47,6 @@
from sqlspec.typing import PGVECTOR_INSTALLED, Empty, import_optional_attr
from sqlspec.utils.module_loader import import_optional, import_string
from sqlspec.utils.serializers import from_json, to_json
-from sqlspec.utils.text import quote_identifier, split_qualified_identifier
from sqlspec.utils.type_converters import build_uuid_coercions
from sqlspec.utils.type_guards import has_rowcount, has_sqlstate
from sqlspec.utils.uuids import uuid4, uuid_from_string
@@ -74,7 +73,6 @@
"driver_kind_from_uri",
"driver_profile",
"enforce_strict_type_coercion",
- "format_identifier",
"get_statement_config",
"handle_postgres_rollback",
"is_postgres_dialect",
@@ -1021,25 +1019,6 @@ def create_duckdb_profile_path() -> Path:
return Path(tempfile.gettempdir()) / f"sqlspec_duckdb_profile_{uuid4().hex}.json"
-def configure_adbc_duckdb_profiling(connection: Any, profile_path: Path) -> None:
- """Enable DuckDB JSON statement profiling on an ADBC DuckDB connection.
-
- Args:
- connection: Active ADBC DuckDB connection.
- profile_path: Temporary file path where DuckDB writes statement profiles.
- """
- escaped_path = str(profile_path).replace("'", "''")
- cursor = connection.cursor()
- try:
- cursor.execute("PRAGMA enable_profiling='json'")
- with contextlib.suppress(Exception):
- cursor.execute("SET profiling_coverage='ALL'")
- cursor.execute(f"SET profiling_output='{escaped_path}'")
- finally:
- with contextlib.suppress(Exception):
- cursor.close()
-
-
def cleanup_duckdb_profile_path(profile_path: Path | str | None) -> None:
"""Remove a temporary DuckDB JSON statement profile file if it exists."""
if not profile_path:
@@ -1084,14 +1063,23 @@ def read_duckdb_profile_json(profile_path: Path | str | None, executed_sql: str
return cast("dict[str, Any]", parsed)
-def format_identifier(identifier: str) -> str:
- """Format and quote a potentially qualified SQL identifier for ADBC metadata queries."""
- cleaned = identifier.strip()
- if not cleaned:
- msg = "Table name must not be empty"
- raise SQLSpecError(msg)
- parts = split_qualified_identifier(cleaned, quote_chars='"')
- return ".".join(quote_identifier(part) for part in parts)
+def configure_adbc_duckdb_profiling(connection: Any, profile_path: Path) -> None:
+ """Enable DuckDB JSON statement profiling on an ADBC DuckDB connection.
+
+ Args:
+ connection: Active ADBC DuckDB connection.
+ profile_path: Temporary file path where DuckDB writes statement profiles.
+ """
+ escaped_path = str(profile_path).replace("'", "''")
+ cursor = connection.cursor()
+ try:
+ cursor.execute("PRAGMA enable_profiling='json'")
+ with contextlib.suppress(Exception):
+ cursor.execute("SET profiling_coverage='ALL'")
+ cursor.execute(f"SET profiling_output='{escaped_path}'")
+ finally:
+ with contextlib.suppress(Exception):
+ cursor.close()
def normalize_column_names(
diff --git a/sqlspec/adapters/adbc/data_dictionary.py b/sqlspec/adapters/adbc/data_dictionary.py
index aeef181bf..594ae2c84 100644
--- a/sqlspec/adapters/adbc/data_dictionary.py
+++ b/sqlspec/adapters/adbc/data_dictionary.py
@@ -5,7 +5,6 @@
from mypy_extensions import mypyc_attr
from sqlspec.adapters.adbc._typing import AdbcNotSupportedError, AdbcOperationalError
-from sqlspec.adapters.adbc.core import format_identifier
from sqlspec.data_dictionary import (
ColumnMetadata,
ConstraintMetadata,
@@ -44,10 +43,10 @@
from sqlspec.data_dictionary.dialects.postgres import resolve_postgres_json_type
from sqlspec.data_dictionary.dialects.sqlite import format_sqlite_schema_prefix, resolve_sqlite_json_type
from sqlspec.driver import SyncDataDictionaryBase
-from sqlspec.exceptions import OperationalError, SQLFileNotFoundError
+from sqlspec.exceptions import OperationalError, SQLFileNotFoundError, SQLSpecError
from sqlspec.utils.logging import get_logger
from sqlspec.utils.module_loader import ensure_pyarrow, import_optional
-from sqlspec.utils.text import normalize_identifier, quote_identifier
+from sqlspec.utils.text import normalize_identifier, quote_identifier, split_qualified_identifier
if TYPE_CHECKING:
from collections.abc import Sequence
@@ -273,7 +272,7 @@ def get_columns(
return driver.select(query_text, schema_name=schema_name, schema_type=ColumnMetadata)
query_text = self._get_query_text(dialect, "columns", "by_table").format(
schema_prefix=schema_prefix,
- table_name=format_identifier(f"{schema_name}.{table}" if schema_name else table),
+ table_name=_format_sqlite_identifier(f"{schema_name}.{table}" if schema_name else table),
)
return driver.select(query_text, table_name=table, schema_name=schema_name, schema_type=ColumnMetadata)
@@ -315,7 +314,7 @@ def get_indexes(
schema_prefix = format_sqlite_schema_prefix(schema_name)
table_identifier = f"{schema_name}.{table_name}" if schema_name else table_name
index_list_sql = self._get_query_text(dialect, "indexes", "by_table").format(
- schema_prefix=schema_prefix, table_name=format_identifier(table_identifier)
+ schema_prefix=schema_prefix, table_name=_format_sqlite_identifier(table_identifier)
)
index_list_rows = driver.select(index_list_sql, table_name=table_name, schema_name=schema_name)
index_metadata_list: list[IndexMetadata] = []
@@ -325,7 +324,7 @@ def get_indexes(
continue
index_identifier = f"{schema_name}.{index_name}" if schema_name else index_name
columns_sql = self._get_query_text(dialect, "indexes", "columns_by_index").format(
- index_name=format_identifier(index_identifier)
+ index_name=_format_sqlite_identifier(index_identifier)
)
columns_rows = driver.select(columns_sql, index_name=index_name, schema_name=schema_name)
columns: list[str] = []
@@ -402,7 +401,7 @@ def get_foreign_keys(
table_label = table.replace("'", "''")
table_identifier = f"{schema_name}.{table}" if schema_name else table
query_text = self._get_query_text(dialect, "foreign_keys", "by_table").format(
- table_name=format_identifier(table_identifier), table_label=table_label
+ table_name=_format_sqlite_identifier(table_identifier), table_label=table_label
)
return driver.select(query_text, table_name=table, schema_name=schema_name, schema_type=ForeignKeyMetadata)
@@ -1065,3 +1064,17 @@ def _normalize_native_statistics(
record["schema_name"] = str(schema_name)
statistics.append(record)
return statistics
+
+
+def _format_sqlite_identifier(identifier: str) -> str:
+ """Format and validate a SQLite identifier for PRAGMA table-valued function queries."""
+ cleaned = identifier.strip()
+ if not cleaned or "\x00" in cleaned:
+ msg = "Table name must not be empty"
+ raise SQLSpecError(msg)
+
+ parts = split_qualified_identifier(cleaned, quote_chars='"')
+ if not parts:
+ msg = f"Invalid SQLite identifier: {identifier!r}"
+ raise SQLSpecError(msg)
+ return ".".join(quote_identifier(part) for part in parts)
diff --git a/sqlspec/adapters/aiomysql/core.py b/sqlspec/adapters/aiomysql/core.py
index 063dca69e..005efd2ee 100644
--- a/sqlspec/adapters/aiomysql/core.py
+++ b/sqlspec/adapters/aiomysql/core.py
@@ -1,38 +1,37 @@
"""aiomysql adapter compiled helpers."""
import contextlib
-from typing import TYPE_CHECKING, Any, cast
+from collections.abc import Callable, Mapping, Sequence, Sized
+from datetime import timedelta
+from typing import TYPE_CHECKING, Any, Final, Literal, cast
-from sqlspec.adapters import mysql_common
from sqlspec.adapters.aiomysql._typing import AiomysqlSSCursor
-from sqlspec.adapters.mysql_common import (
- bool_to_int,
- build_insert_statement,
- build_load_data_statement,
- can_use_local_infile_records,
- collect_rows,
- collect_stream_rows,
- create_mapped_exception,
- detect_json_columns,
- detect_json_columns_from_description,
- encode_records_for_local_infile,
- escape_literal_percent,
- format_identifier,
- normalize_execute_many_parameters,
- normalize_execute_parameters,
- normalize_lastrowid,
- resolve_column_names,
- resolve_many_rowcount,
- resolve_row_plan,
- resolve_rowcount,
-)
from sqlspec.core import DriverParameterProfile, ParameterStyle, StatementConfig, build_statement_config_from_profile
+from sqlspec.driver import rows_to_dicts
+from sqlspec.exceptions import (
+ CheckViolationError,
+ ConnectionTimeoutError,
+ DatabaseConnectionError,
+ DataError,
+ DeadlockError,
+ ForeignKeyViolationError,
+ IntegrityError,
+ NotNullViolationError,
+ PermissionDeniedError,
+ QueryTimeoutError,
+ SQLParsingError,
+ SQLSpecError,
+ TransactionError,
+ UniqueViolationError,
+)
from sqlspec.utils.logging import get_logger
from sqlspec.utils.serializers import from_json, to_json
+from sqlspec.utils.text import quote_backtick_identifier, split_qualified_identifier
from sqlspec.utils.type_converters import build_uuid_coercions
+from sqlspec.utils.type_guards import has_cursor_metadata, has_lastrowid, has_rowcount, has_sqlstate, has_type_code
if TYPE_CHECKING:
- from collections.abc import Callable, Mapping
+ from sqlspec.core.parameters import ParameterValidator
__all__ = (
"AiomysqlStreamSource",
@@ -63,10 +62,87 @@
logger = get_logger("sqlspec.adapters.aiomysql")
-_MYSQL_ACCESS_ERROR_DISPATCH = mysql_common.MYSQL_ACCESS_ERROR_DISPATCH
-_MYSQL_CONNECTION_ERROR_DISPATCH = mysql_common.MYSQL_CONNECTION_ERROR_DISPATCH
-_MYSQL_CONSTRAINT_ERROR_DISPATCH = mysql_common.MYSQL_CONSTRAINT_ERROR_DISPATCH
-_MYSQL_TRANSACTION_ERROR_DISPATCH = mysql_common.MYSQL_TRANSACTION_ERROR_DISPATCH
+MYSQL_ER_DUP_ENTRY: Final[int] = 1062
+MYSQL_ER_NO_DEFAULT_FOR_FIELD: Final[int] = 1364
+MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: Final[int] = 3819
+MYSQL_ER_DBACCESS_DENIED: Final[int] = 1044
+MYSQL_ER_ACCESS_DENIED: Final[int] = 1045
+MYSQL_ER_TABLEACCESS_DENIED: Final[int] = 1142
+MYSQL_ER_LOCK_DEADLOCK: Final[int] = 1213
+MYSQL_ER_LOCK_WAIT_TIMEOUT: Final[int] = 1205
+
+MYSQL_CR_CONNECTION_ERROR: Final[int] = 2002
+MYSQL_CR_CONN_HOST_ERROR: Final[int] = 2003
+MYSQL_CR_UNKNOWN_HOST: Final[int] = 2005
+MYSQL_CR_SERVER_GONE_ERROR: Final[int] = 2006
+MYSQL_CR_SERVER_LOST: Final[int] = 2013
+MYSQL_CR_SSL_CONNECTION_ERROR: Final[int] = 2026
+MYSQL_SYNTAX_ERROR_MIN: Final[int] = 1064
+MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE: Final[int] = 1100
+
+_MYSQL_MIGRATION_ERROR_CODES: Final[frozenset[int]] = frozenset((1061, 1091))
+_MYSQL_SQLSTATE_EXACT_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
+ "23505": (UniqueViolationError, "unique constraint violation"),
+ "23503": (ForeignKeyViolationError, "foreign key constraint violation"),
+ "23502": (NotNullViolationError, "not-null constraint violation"),
+ "23514": (CheckViolationError, "check constraint violation"),
+}
+_MYSQL_SQLSTATE_PREFIX_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
+ "23": (IntegrityError, "integrity constraint violation"),
+ "28": (PermissionDeniedError, "authorization error"),
+ "40": (TransactionError, "transaction error"),
+ "42": (SQLParsingError, "SQL syntax error"),
+ "08": (DatabaseConnectionError, "connection error"),
+ "22": (DataError, "data error"),
+}
+_MYSQL_CONSTRAINT_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_DUP_ENTRY: (UniqueViolationError, "unique constraint violation"),
+ 1169: (UniqueViolationError, "unique constraint violation"),
+ 1586: (UniqueViolationError, "unique constraint violation"),
+ 1216: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1217: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1451: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1452: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1048: (NotNullViolationError, "not-null constraint violation"),
+ MYSQL_ER_NO_DEFAULT_FOR_FIELD: (NotNullViolationError, "not-null constraint violation"),
+ MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: (CheckViolationError, "check constraint violation"),
+ 4025: (CheckViolationError, "check constraint violation"),
+}
+_MYSQL_ACCESS_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_DBACCESS_DENIED: (PermissionDeniedError, "access denied"),
+ MYSQL_ER_ACCESS_DENIED: (PermissionDeniedError, "access denied"),
+ MYSQL_ER_TABLEACCESS_DENIED: (PermissionDeniedError, "access denied"),
+}
+_MYSQL_TRANSACTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_LOCK_DEADLOCK: (DeadlockError, "deadlock detected"),
+ MYSQL_ER_LOCK_WAIT_TIMEOUT: (QueryTimeoutError, "lock wait timeout"),
+ 1317: (QueryTimeoutError, "query execution interrupted"),
+ 1969: (QueryTimeoutError, "query execution timeout"),
+ 3024: (QueryTimeoutError, "query execution timeout"),
+}
+_MYSQL_CONNECTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_CR_SERVER_LOST: (ConnectionTimeoutError, "connection lost"),
+ MYSQL_CR_CONNECTION_ERROR: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_CONN_HOST_ERROR: (DatabaseConnectionError, "connection error"),
+ 2004: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_UNKNOWN_HOST: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_SERVER_GONE_ERROR: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_SSL_CONNECTION_ERROR: (DatabaseConnectionError, "ssl connection error"),
+ 2055: (DatabaseConnectionError, "connection error"),
+ 4031: (DatabaseConnectionError, "connection error"),
+}
+_MYSQL_DATA_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ 1264: (DataError, "data error"),
+ 1265: (DataError, "data error"),
+ 1292: (DataError, "data error"),
+ 1366: (DataError, "data error"),
+ 1406: (DataError, "data error"),
+ 1411: (DataError, "data error"),
+ 3140: (DataError, "data error"),
+ 3141: (DataError, "data error"),
+ 3142: (DataError, "data error"),
+ 3143: (DataError, "data error"),
+}
class AiomysqlStreamSource:
@@ -178,6 +254,406 @@ def apply_driver_features(
return statement_config, features
+def format_identifier(identifier: str) -> str:
+ """Format an SQL identifier with MySQL backtick quoting."""
+ cleaned = identifier.strip()
+ if not cleaned:
+ msg = "Table name must not be empty"
+ raise SQLSpecError(msg)
+ parts = split_qualified_identifier(cleaned, quote_chars="`", allow_bracket_quotes=False)
+ formatted = ".".join(quote_backtick_identifier(part) for part in parts)
+ return formatted or quote_backtick_identifier(cleaned)
+
+
+def build_insert_statement(table: str, columns: "list[str]") -> str:
+ """Construct an INSERT statement with positional placeholders."""
+ column_clause = ", ".join(quote_backtick_identifier(column) for column in columns)
+ placeholders = ", ".join("%s" for _ in columns)
+ return f"INSERT INTO {format_identifier(table)} ({column_clause}) VALUES ({placeholders})"
+
+
+def can_use_local_infile_records(arrow_table: Any, records: "Sequence[Sequence[Any]]") -> bool:
+ """Return whether Arrow records can be safely serialized via MySQL LOCAL INFILE TSV."""
+ schema = getattr(arrow_table, "schema", None)
+ if schema is not None:
+ all_safe_types = True
+ for field in schema:
+ field_type = getattr(field, "type", None)
+ type_str = str(field_type).lower() if field_type is not None else ""
+ if "binary" in type_str or "duration" in type_str:
+ return False
+ if not type_str or not type_str.startswith((
+ "int",
+ "uint",
+ "float",
+ "double",
+ "halffloat",
+ "bool",
+ "string",
+ "large_string",
+ "utf8",
+ "large_utf8",
+ "date",
+ "timestamp",
+ "time",
+ "decimal",
+ )):
+ all_safe_types = False
+ if all_safe_types:
+ return True
+ return not any(isinstance(value, (bytes, bytearray, memoryview, timedelta)) for row in records for value in row)
+
+
+def encode_records_for_local_infile(records: "list[tuple[Any, ...]]") -> bytes:
+ """Encode row tuples into MySQL LOCAL INFILE TSV byte payload."""
+ lines: list[str] = []
+ for record in records:
+ fields: list[str] = []
+ for value in record:
+ if value is None:
+ fields.append("\\N")
+ continue
+ if isinstance(value, bool):
+ value = int(value)
+ if isinstance(value, (bytes, bytearray, memoryview)):
+ text = bytes(value).decode("utf-8", errors="replace")
+ elif isinstance(value, str):
+ text = value
+ else:
+ text = str(value)
+ text = (
+ text
+ .replace("\\", "\\\\")
+ .replace("\x00", "\\0")
+ .replace("\t", "\\t")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ .replace("\x1a", "\\Z")
+ )
+ fields.append(text)
+ lines.append("\t".join(fields))
+ return ("\n".join(lines) + "\n").encode("utf-8")
+
+
+def normalize_execute_parameters(parameters: Any) -> Any:
+ """Normalize parameters for MySQL execute calls."""
+ return parameters or None
+
+
+def normalize_execute_many_parameters(parameters: Any) -> Any:
+ """Normalize parameters for MySQL executemany calls."""
+ return parameters
+
+
+def resolve_column_names(description: "Sequence[Any] | None") -> "list[str]":
+ """Extract column names from cursor description."""
+ if not description:
+ return []
+ return [col[0] for col in description]
+
+
+def resolve_row_plan(
+ description: "Sequence[Any] | None", json_type_codes: "set[int]"
+) -> "tuple[list[str], list[int] | None]":
+ """Resolve ordered column names and JSON column indexes in one pass."""
+ if not description:
+ return [], None
+
+ column_names: list[str] = []
+ if not json_type_codes:
+ column_names.extend(column[0] for column in description)
+ return column_names, None
+
+ json_indexes: list[int] = []
+ append_json = json_indexes.append
+ for index, column in enumerate(description):
+ column_names.append(column[0])
+ if isinstance(column, (tuple, list)):
+ type_code = column[1] if len(column) > 1 else None
+ else:
+ type_code = column.type_code if has_type_code(column) else None
+ if type_code in json_type_codes:
+ append_json(index)
+ return column_names, json_indexes or None
+
+
+def detect_json_columns_from_description(
+ description: "Sequence[Any] | None", json_type_codes: "set[int]"
+) -> "list[int]":
+ """Identify JSON column indexes from pre-fetched cursor description metadata."""
+ return resolve_row_plan(description, json_type_codes)[1] or []
+
+
+def detect_json_columns(
+ cursor: Any, json_type_codes: "set[int]", description: "Sequence[Any] | None" = None
+) -> "list[int]":
+ """Identify JSON column indexes from cursor metadata."""
+ if description is None:
+ if not has_cursor_metadata(cursor):
+ return []
+ description = cursor.description
+ return detect_json_columns_from_description(description, json_type_codes)
+
+
+def collect_stream_rows(
+ rows: "Sequence[Any]",
+ row_plan: "tuple[list[str], list[int] | None]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[dict[str, Any]]":
+ """Convert streamed rows to dictionaries with JSON decoding applied."""
+ column_names, json_indexes = row_plan
+ if not column_names or not rows:
+ return []
+ dict_rows = rows if isinstance(rows[0], dict) else rows_to_dicts(list(rows), column_names)
+ if json_indexes:
+ dict_rows = _deserialize_json_dict_rows(column_names, dict_rows, json_indexes, deserializer, logger=logger)
+ return dict_rows if isinstance(dict_rows, list) else list(dict_rows)
+
+
+def collect_rows(
+ fetched_data: "Sequence[Any] | None",
+ row_plan: "tuple[list[str], list[int] | None]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "tuple[list[Any], list[str], Literal['dict', 'tuple', 'record']]":
+ """Collect fetched rows with JSON decoding and zero-copy fast-path."""
+ column_names, json_indexes = row_plan
+ if not column_names:
+ return [], [], "tuple"
+ if not fetched_data:
+ return [], column_names, "tuple"
+
+ first_row = fetched_data[0]
+ if isinstance(first_row, dict):
+ rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
+ if json_indexes:
+ rows = _deserialize_json_dict_rows(column_names, rows, json_indexes, deserializer, logger=logger)
+ return rows, column_names, "dict"
+
+ if not json_indexes:
+ return fetched_data if isinstance(fetched_data, list) else list(fetched_data), column_names, "tuple"
+
+ rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
+ rows = _deserialize_json_tuple_rows(rows, json_indexes, deserializer, logger=logger)
+ return rows, column_names, "tuple"
+
+
+def resolve_rowcount(cursor: Any) -> int:
+ """Resolve rowcount safely from a cursor."""
+ if not has_rowcount(cursor):
+ return 0
+ rowcount = cursor.rowcount
+ if isinstance(rowcount, int) and rowcount >= 0:
+ return rowcount
+ return 0
+
+
+def resolve_many_rowcount(cursor: Any, parameters: Any, *, fallback_count: "int | None" = None) -> int:
+ """Resolve execute_many rowcount using cursor metadata with payload fallback."""
+ rowcount = resolve_rowcount(cursor)
+ if rowcount > 0:
+ return rowcount
+ if fallback_count is not None:
+ return fallback_count
+ if isinstance(parameters, Sized):
+ return len(parameters)
+ return 0
+
+
+def normalize_lastrowid(cursor: Any) -> int | None:
+ """Normalize lastrowid for MySQL when rowcount indicates success."""
+ if not has_rowcount(cursor):
+ return None
+ rowcount = cursor.rowcount
+ if not isinstance(rowcount, int) or rowcount <= 0:
+ return None
+ if not has_lastrowid(cursor):
+ return None
+ last_id = cursor.lastrowid
+ return last_id if isinstance(last_id, int) else None
+
+
+def bool_to_int(value: bool) -> int:
+ """Coerce boolean values to integer for MySQL parameter compatibility."""
+ return int(value)
+
+
+def create_mapped_exception(error: Any, *, logger: Any | None = None) -> "SQLSpecError | bool":
+ """Map MySQL driver exceptions to SQLSpec errors."""
+ error_code = getattr(error, "errno", None)
+ if error_code is None and isinstance(error, Exception) and error.args:
+ value = error.args[0]
+ if isinstance(value, int):
+ error_code = value
+ sqlstate = error.sqlstate if has_sqlstate(error) else None
+ sqlstate_prefix = sqlstate[:2] if isinstance(sqlstate, str) and sqlstate else None
+
+ if error_code in _MYSQL_MIGRATION_ERROR_CODES:
+ if logger is not None:
+ logger.warning("MySQL expected migration error (ignoring): %s", error)
+ return True
+
+ dispatch = _MYSQL_SQLSTATE_EXACT_DISPATCH.get(sqlstate) if sqlstate is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_CONSTRAINT_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ if sqlstate_prefix == "23":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["23"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_ACCESS_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "28":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["28"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_TRANSACTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "40":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["40"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ if sqlstate_prefix == "42":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["42"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if isinstance(error_code, int) and MYSQL_SYNTAX_ERROR_MIN <= error_code < MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE:
+ return _create_mysql_error(error, sqlstate, error_code, SQLParsingError, "SQL syntax error")
+
+ dispatch = _MYSQL_CONNECTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "08":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["08"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_DATA_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "22" or type(error).__name__ == "DataError":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["22"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ return _create_mysql_error(error, sqlstate, error_code, SQLSpecError, "database error")
+
+
+def escape_literal_percent(sql: str, parameters: Any, validator: "ParameterValidator") -> str:
+ """Escape literal percent characters before driver-side interpolation."""
+ if not parameters or "%" not in sql:
+ return sql
+ keep = {info.position for info in validator.extract_parameters(sql)}
+ return "".join("%%" if char == "%" and index not in keep else char for index, char in enumerate(sql))
+
+
+def build_load_data_statement(
+ table: str, columns: "list[str]", placeholder: str = "%s", *, escape_percent: bool = False
+) -> str:
+ """Build native LOAD DATA SQL with a bound filename."""
+ table_sql = format_identifier(table)
+ if escape_percent:
+ table_sql = table_sql.replace("%", "%%")
+ column_list = ", ".join(
+ quote_backtick_identifier(column).replace("%", "%%") if escape_percent else quote_backtick_identifier(column)
+ for column in columns
+ )
+ return (
+ f"LOAD DATA LOCAL INFILE {placeholder} INTO TABLE {table_sql} "
+ "CHARACTER SET utf8mb4 FIELDS TERMINATED BY '\\t' ESCAPED BY '\\\\' "
+ f"LINES TERMINATED BY '\\n' ({column_list})"
+ )
+
+
+def _deserialize_json_value(value: Any, deserializer: "Callable[[Any], Any]", *, logger: Any | None = None) -> Any:
+ """Safely decode JSON column values using the specified deserializer."""
+ if value is None:
+ return None
+ if isinstance(value, (dict, list)):
+ return value
+ if isinstance(value, (bytes, bytearray, memoryview)):
+ try:
+ raw_bytes = bytes(value) if isinstance(value, (bytearray, memoryview)) else value
+ return deserializer(raw_bytes)
+ except Exception:
+ if logger is not None:
+ logger.debug("Failed to deserialize JSON column", exc_info=True)
+ return value
+ if isinstance(value, str):
+ try:
+ return deserializer(value)
+ except Exception:
+ if logger is not None:
+ logger.debug("Failed to deserialize JSON column", exc_info=True)
+ return value
+ return value
+
+
+def _deserialize_json_dict_rows(
+ column_names: "Sequence[str]",
+ rows: "Sequence[dict[str, Any]]",
+ json_indexes: "list[int]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[dict[str, Any]]":
+ """Deserialize JSON fields in dictionary rows in-place."""
+ if not json_indexes:
+ return rows if isinstance(rows, list) else list(rows)
+ json_columns = [column_names[idx] for idx in json_indexes if idx < len(column_names)]
+ for row in rows:
+ for column in json_columns:
+ if column in row and row[column] is not None:
+ row[column] = _deserialize_json_value(row[column], deserializer, logger=logger)
+ return rows if isinstance(rows, list) else list(rows)
+
+
+def _deserialize_json_tuple_rows(
+ rows: "Sequence[tuple[Any, ...]]",
+ json_indexes: "list[int]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[tuple[Any, ...]]":
+ """Deserialize JSON fields in tuple rows using sparse index iteration and lazy row mutation."""
+ if not json_indexes:
+ return rows if isinstance(rows, list) else list(rows)
+ result: list[tuple[Any, ...]] = []
+ for row in rows:
+ row_len = len(row)
+ row_list: list[Any] | None = None
+ for idx in json_indexes:
+ if idx >= row_len:
+ continue
+ raw_val = row[idx]
+ if raw_val is None:
+ continue
+ decoded = _deserialize_json_value(raw_val, deserializer, logger=logger)
+ if decoded is not raw_val:
+ if row_list is None:
+ row_list = list(row)
+ row_list[idx] = decoded
+ result.append(tuple(row_list) if row_list is not None else row)
+ return result
+
+
+def _create_mysql_error(
+ error: Any, sqlstate: str | None, code: int | None, error_class: type[SQLSpecError], description: str
+) -> SQLSpecError:
+ """Create a MySQL error instance without raising it."""
+ code_str = f"[{sqlstate or code}]" if sqlstate or code else ""
+ msg = f"MySQL {description} {code_str}: {error}" if code_str else f"MySQL {description}: {error}"
+ exc = error_class(msg)
+ exc.__cause__ = error
+ return exc
+
+
driver_profile = build_profile()
default_statement_config = build_statement_config()
diff --git a/sqlspec/adapters/asyncmy/core.py b/sqlspec/adapters/asyncmy/core.py
index bc5212d2c..14bbed72e 100644
--- a/sqlspec/adapters/asyncmy/core.py
+++ b/sqlspec/adapters/asyncmy/core.py
@@ -1,37 +1,37 @@
"""AsyncMy adapter compiled helpers."""
import contextlib
-from typing import TYPE_CHECKING, Any, cast
+from collections.abc import Callable, Mapping, Sequence, Sized
+from datetime import timedelta
+from typing import TYPE_CHECKING, Any, Final, Literal, cast
-from sqlspec.adapters import mysql_common
from sqlspec.adapters.asyncmy._typing import AsyncmySSCursor
-from sqlspec.adapters.mysql_common import (
- bool_to_int,
- build_insert_statement,
- can_use_local_infile_records,
- collect_rows,
- collect_stream_rows,
- create_mapped_exception,
- detect_json_columns,
- detect_json_columns_from_description,
- encode_records_for_local_infile,
- escape_literal_percent,
- format_identifier,
- normalize_execute_many_parameters,
- normalize_execute_parameters,
- normalize_lastrowid,
- resolve_column_names,
- resolve_many_rowcount,
- resolve_row_plan,
- resolve_rowcount,
-)
from sqlspec.core import DriverParameterProfile, ParameterStyle, StatementConfig, build_statement_config_from_profile
+from sqlspec.driver import rows_to_dicts
+from sqlspec.exceptions import (
+ CheckViolationError,
+ ConnectionTimeoutError,
+ DatabaseConnectionError,
+ DataError,
+ DeadlockError,
+ ForeignKeyViolationError,
+ IntegrityError,
+ NotNullViolationError,
+ PermissionDeniedError,
+ QueryTimeoutError,
+ SQLParsingError,
+ SQLSpecError,
+ TransactionError,
+ UniqueViolationError,
+)
from sqlspec.utils.logging import get_logger
from sqlspec.utils.serializers import from_json, to_json
+from sqlspec.utils.text import quote_backtick_identifier, split_qualified_identifier
from sqlspec.utils.type_converters import build_uuid_coercions
+from sqlspec.utils.type_guards import has_cursor_metadata, has_lastrowid, has_rowcount, has_sqlstate, has_type_code
if TYPE_CHECKING:
- from collections.abc import Callable, Mapping
+ from sqlspec.core.parameters import ParameterValidator
__all__ = (
"AsyncmyStreamSource",
@@ -62,17 +62,87 @@
logger = get_logger("sqlspec.adapters.asyncmy")
-_MYSQL_ACCESS_ERROR_DISPATCH = mysql_common.MYSQL_ACCESS_ERROR_DISPATCH
-_MYSQL_CONNECTION_ERROR_DISPATCH = mysql_common.MYSQL_CONNECTION_ERROR_DISPATCH
-_MYSQL_CONSTRAINT_ERROR_DISPATCH = mysql_common.MYSQL_CONSTRAINT_ERROR_DISPATCH
-_MYSQL_TRANSACTION_ERROR_DISPATCH = mysql_common.MYSQL_TRANSACTION_ERROR_DISPATCH
+MYSQL_ER_DUP_ENTRY: Final[int] = 1062
+MYSQL_ER_NO_DEFAULT_FOR_FIELD: Final[int] = 1364
+MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: Final[int] = 3819
+MYSQL_ER_DBACCESS_DENIED: Final[int] = 1044
+MYSQL_ER_ACCESS_DENIED: Final[int] = 1045
+MYSQL_ER_TABLEACCESS_DENIED: Final[int] = 1142
+MYSQL_ER_LOCK_DEADLOCK: Final[int] = 1213
+MYSQL_ER_LOCK_WAIT_TIMEOUT: Final[int] = 1205
+MYSQL_CR_CONNECTION_ERROR: Final[int] = 2002
+MYSQL_CR_CONN_HOST_ERROR: Final[int] = 2003
+MYSQL_CR_UNKNOWN_HOST: Final[int] = 2005
+MYSQL_CR_SERVER_GONE_ERROR: Final[int] = 2006
+MYSQL_CR_SERVER_LOST: Final[int] = 2013
+MYSQL_CR_SSL_CONNECTION_ERROR: Final[int] = 2026
+MYSQL_SYNTAX_ERROR_MIN: Final[int] = 1064
+MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE: Final[int] = 1100
-def build_load_data_statement(table: str, columns: "list[str]") -> str:
- """Build native LOAD DATA SQL with a named bound filename for asyncmy."""
- return mysql_common.build_load_data_statement(
- table, columns, placeholder="%(sqlspec_infile_path)s", escape_percent=True
- )
+_MYSQL_MIGRATION_ERROR_CODES: Final[frozenset[int]] = frozenset((1061, 1091))
+_MYSQL_SQLSTATE_EXACT_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
+ "23505": (UniqueViolationError, "unique constraint violation"),
+ "23503": (ForeignKeyViolationError, "foreign key constraint violation"),
+ "23502": (NotNullViolationError, "not-null constraint violation"),
+ "23514": (CheckViolationError, "check constraint violation"),
+}
+_MYSQL_SQLSTATE_PREFIX_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
+ "23": (IntegrityError, "integrity constraint violation"),
+ "28": (PermissionDeniedError, "authorization error"),
+ "40": (TransactionError, "transaction error"),
+ "42": (SQLParsingError, "SQL syntax error"),
+ "08": (DatabaseConnectionError, "connection error"),
+ "22": (DataError, "data error"),
+}
+_MYSQL_CONSTRAINT_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_DUP_ENTRY: (UniqueViolationError, "unique constraint violation"),
+ 1169: (UniqueViolationError, "unique constraint violation"),
+ 1586: (UniqueViolationError, "unique constraint violation"),
+ 1216: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1217: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1451: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1452: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1048: (NotNullViolationError, "not-null constraint violation"),
+ MYSQL_ER_NO_DEFAULT_FOR_FIELD: (NotNullViolationError, "not-null constraint violation"),
+ MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: (CheckViolationError, "check constraint violation"),
+ 4025: (CheckViolationError, "check constraint violation"),
+}
+_MYSQL_ACCESS_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_DBACCESS_DENIED: (PermissionDeniedError, "access denied"),
+ MYSQL_ER_ACCESS_DENIED: (PermissionDeniedError, "access denied"),
+ MYSQL_ER_TABLEACCESS_DENIED: (PermissionDeniedError, "access denied"),
+}
+_MYSQL_TRANSACTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_LOCK_DEADLOCK: (DeadlockError, "deadlock detected"),
+ MYSQL_ER_LOCK_WAIT_TIMEOUT: (QueryTimeoutError, "lock wait timeout"),
+ 1317: (QueryTimeoutError, "query execution interrupted"),
+ 1969: (QueryTimeoutError, "query execution timeout"),
+ 3024: (QueryTimeoutError, "query execution timeout"),
+}
+_MYSQL_CONNECTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_CR_SERVER_LOST: (ConnectionTimeoutError, "connection lost"),
+ MYSQL_CR_CONNECTION_ERROR: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_CONN_HOST_ERROR: (DatabaseConnectionError, "connection error"),
+ 2004: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_UNKNOWN_HOST: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_SERVER_GONE_ERROR: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_SSL_CONNECTION_ERROR: (DatabaseConnectionError, "ssl connection error"),
+ 2055: (DatabaseConnectionError, "connection error"),
+ 4031: (DatabaseConnectionError, "connection error"),
+}
+_MYSQL_DATA_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ 1264: (DataError, "data error"),
+ 1265: (DataError, "data error"),
+ 1292: (DataError, "data error"),
+ 1366: (DataError, "data error"),
+ 1406: (DataError, "data error"),
+ 1411: (DataError, "data error"),
+ 3140: (DataError, "data error"),
+ 3141: (DataError, "data error"),
+ 3142: (DataError, "data error"),
+ 3143: (DataError, "data error"),
+}
class AsyncmyStreamSource:
@@ -184,6 +254,399 @@ def apply_driver_features(
return statement_config, features
+def format_identifier(identifier: str) -> str:
+ """Format an SQL identifier with MySQL backtick quoting."""
+ cleaned = identifier.strip()
+ if not cleaned:
+ msg = "Table name must not be empty"
+ raise SQLSpecError(msg)
+ parts = split_qualified_identifier(cleaned, quote_chars="`", allow_bracket_quotes=False)
+ formatted = ".".join(quote_backtick_identifier(part) for part in parts)
+ return formatted or quote_backtick_identifier(cleaned)
+
+
+def build_insert_statement(table: str, columns: "list[str]") -> str:
+ """Construct an INSERT statement with positional placeholders."""
+ column_clause = ", ".join(quote_backtick_identifier(column) for column in columns)
+ placeholders = ", ".join("%s" for _ in columns)
+ return f"INSERT INTO {format_identifier(table)} ({column_clause}) VALUES ({placeholders})"
+
+
+def can_use_local_infile_records(arrow_table: Any, records: "Sequence[Sequence[Any]]") -> bool:
+ """Return whether Arrow records can be safely serialized via MySQL LOCAL INFILE TSV."""
+ schema = getattr(arrow_table, "schema", None)
+ if schema is not None:
+ all_safe_types = True
+ for field in schema:
+ field_type = getattr(field, "type", None)
+ type_str = str(field_type).lower() if field_type is not None else ""
+ if "binary" in type_str or "duration" in type_str:
+ return False
+ if not type_str or not type_str.startswith((
+ "int",
+ "uint",
+ "float",
+ "double",
+ "halffloat",
+ "bool",
+ "string",
+ "large_string",
+ "utf8",
+ "large_utf8",
+ "date",
+ "timestamp",
+ "time",
+ "decimal",
+ )):
+ all_safe_types = False
+ if all_safe_types:
+ return True
+ return not any(isinstance(value, (bytes, bytearray, memoryview, timedelta)) for row in records for value in row)
+
+
+def encode_records_for_local_infile(records: "list[tuple[Any, ...]]") -> bytes:
+ """Encode row tuples into MySQL LOCAL INFILE TSV byte payload."""
+ lines: list[str] = []
+ for record in records:
+ fields: list[str] = []
+ for value in record:
+ if value is None:
+ fields.append("\\N")
+ continue
+ if isinstance(value, bool):
+ value = int(value)
+ if isinstance(value, (bytes, bytearray, memoryview)):
+ text = bytes(value).decode("utf-8", errors="replace")
+ elif isinstance(value, str):
+ text = value
+ else:
+ text = str(value)
+ text = (
+ text
+ .replace("\\", "\\\\")
+ .replace("\x00", "\\0")
+ .replace("\t", "\\t")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ .replace("\x1a", "\\Z")
+ )
+ fields.append(text)
+ lines.append("\t".join(fields))
+ return ("\n".join(lines) + "\n").encode("utf-8")
+
+
+def normalize_execute_parameters(parameters: Any) -> Any:
+ """Normalize parameters for MySQL execute calls."""
+ return parameters or None
+
+
+def normalize_execute_many_parameters(parameters: Any) -> Any:
+ """Normalize parameters for MySQL executemany calls."""
+ return parameters
+
+
+def resolve_column_names(description: "Sequence[Any] | None") -> "list[str]":
+ """Extract column names from cursor description."""
+ if not description:
+ return []
+ return [col[0] for col in description]
+
+
+def resolve_row_plan(
+ description: "Sequence[Any] | None", json_type_codes: "set[int]"
+) -> "tuple[list[str], list[int] | None]":
+ """Resolve ordered column names and JSON column indexes in one pass."""
+ if not description:
+ return [], None
+
+ column_names: list[str] = []
+ if not json_type_codes:
+ column_names.extend(column[0] for column in description)
+ return column_names, None
+
+ json_indexes: list[int] = []
+ append_json = json_indexes.append
+ for index, column in enumerate(description):
+ column_names.append(column[0])
+ if isinstance(column, (tuple, list)):
+ type_code = column[1] if len(column) > 1 else None
+ else:
+ type_code = column.type_code if has_type_code(column) else None
+ if type_code in json_type_codes:
+ append_json(index)
+ return column_names, json_indexes or None
+
+
+def detect_json_columns_from_description(
+ description: "Sequence[Any] | None", json_type_codes: "set[int]"
+) -> "list[int]":
+ """Identify JSON column indexes from pre-fetched cursor description metadata."""
+ return resolve_row_plan(description, json_type_codes)[1] or []
+
+
+def detect_json_columns(
+ cursor: Any, json_type_codes: "set[int]", description: "Sequence[Any] | None" = None
+) -> "list[int]":
+ """Identify JSON column indexes from cursor metadata."""
+ if description is None:
+ if not has_cursor_metadata(cursor):
+ return []
+ description = cursor.description
+ return detect_json_columns_from_description(description, json_type_codes)
+
+
+def collect_stream_rows(
+ rows: "Sequence[Any]",
+ row_plan: "tuple[list[str], list[int] | None]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[dict[str, Any]]":
+ """Convert streamed rows to dictionaries with JSON decoding applied."""
+ column_names, json_indexes = row_plan
+ if not column_names or not rows:
+ return []
+ dict_rows = rows if isinstance(rows[0], dict) else rows_to_dicts(list(rows), column_names)
+ if json_indexes:
+ dict_rows = _deserialize_json_dict_rows(column_names, dict_rows, json_indexes, deserializer, logger=logger)
+ return dict_rows if isinstance(dict_rows, list) else list(dict_rows)
+
+
+def collect_rows(
+ fetched_data: "Sequence[Any] | None",
+ row_plan: "tuple[list[str], list[int] | None]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "tuple[list[Any], list[str], Literal['dict', 'tuple', 'record']]":
+ """Collect fetched rows with JSON decoding and zero-copy fast-path."""
+ column_names, json_indexes = row_plan
+ if not column_names:
+ return [], [], "tuple"
+ if not fetched_data:
+ return [], column_names, "tuple"
+
+ first_row = fetched_data[0]
+ if isinstance(first_row, dict):
+ rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
+ if json_indexes:
+ rows = _deserialize_json_dict_rows(column_names, rows, json_indexes, deserializer, logger=logger)
+ return rows, column_names, "dict"
+
+ if not json_indexes:
+ return fetched_data if isinstance(fetched_data, list) else list(fetched_data), column_names, "tuple"
+
+ rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
+ rows = _deserialize_json_tuple_rows(rows, json_indexes, deserializer, logger=logger)
+ return rows, column_names, "tuple"
+
+
+def resolve_rowcount(cursor: Any) -> int:
+ """Resolve rowcount safely from a cursor."""
+ if not has_rowcount(cursor):
+ return 0
+ rowcount = cursor.rowcount
+ if isinstance(rowcount, int) and rowcount >= 0:
+ return rowcount
+ return 0
+
+
+def resolve_many_rowcount(cursor: Any, parameters: Any, *, fallback_count: "int | None" = None) -> int:
+ """Resolve execute_many rowcount using cursor metadata with payload fallback."""
+ rowcount = resolve_rowcount(cursor)
+ if rowcount > 0:
+ return rowcount
+ if fallback_count is not None:
+ return fallback_count
+ if isinstance(parameters, Sized):
+ return len(parameters)
+ return 0
+
+
+def normalize_lastrowid(cursor: Any) -> int | None:
+ """Normalize lastrowid for MySQL when rowcount indicates success."""
+ if not has_rowcount(cursor):
+ return None
+ rowcount = cursor.rowcount
+ if not isinstance(rowcount, int) or rowcount <= 0:
+ return None
+ if not has_lastrowid(cursor):
+ return None
+ last_id = cursor.lastrowid
+ return last_id if isinstance(last_id, int) else None
+
+
+def bool_to_int(value: bool) -> int:
+ """Coerce boolean values to integer for MySQL parameter compatibility."""
+ return int(value)
+
+
+def create_mapped_exception(error: Any, *, logger: Any | None = None) -> "SQLSpecError | bool":
+ """Map MySQL driver exceptions to SQLSpec errors."""
+ error_code = getattr(error, "errno", None)
+ if error_code is None and isinstance(error, Exception) and error.args:
+ value = error.args[0]
+ if isinstance(value, int):
+ error_code = value
+ sqlstate = error.sqlstate if has_sqlstate(error) else None
+ sqlstate_prefix = sqlstate[:2] if isinstance(sqlstate, str) and sqlstate else None
+
+ if error_code in _MYSQL_MIGRATION_ERROR_CODES:
+ if logger is not None:
+ logger.warning("MySQL expected migration error (ignoring): %s", error)
+ return True
+
+ dispatch = _MYSQL_SQLSTATE_EXACT_DISPATCH.get(sqlstate) if sqlstate is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_CONSTRAINT_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ if sqlstate_prefix == "23":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["23"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_ACCESS_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "28":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["28"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_TRANSACTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "40":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["40"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ if sqlstate_prefix == "42":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["42"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if isinstance(error_code, int) and MYSQL_SYNTAX_ERROR_MIN <= error_code < MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE:
+ return _create_mysql_error(error, sqlstate, error_code, SQLParsingError, "SQL syntax error")
+
+ dispatch = _MYSQL_CONNECTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "08":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["08"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_DATA_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "22" or type(error).__name__ == "DataError":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["22"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ return _create_mysql_error(error, sqlstate, error_code, SQLSpecError, "database error")
+
+
+def escape_literal_percent(sql: str, parameters: Any, validator: "ParameterValidator") -> str:
+ """Escape literal percent characters before driver-side interpolation."""
+ if not parameters or "%" not in sql:
+ return sql
+ keep = {info.position for info in validator.extract_parameters(sql)}
+ return "".join("%%" if char == "%" and index not in keep else char for index, char in enumerate(sql))
+
+
+def build_load_data_statement(table: str, columns: "list[str]") -> str:
+ """Build native LOAD DATA SQL with a named bound filename for asyncmy."""
+ table_sql = format_identifier(table).replace("%", "%%")
+ column_list = ", ".join(quote_backtick_identifier(column).replace("%", "%%") for column in columns)
+ return (
+ f"LOAD DATA LOCAL INFILE %(sqlspec_infile_path)s INTO TABLE {table_sql} "
+ "CHARACTER SET utf8mb4 FIELDS TERMINATED BY '\\t' ESCAPED BY '\\\\' "
+ f"LINES TERMINATED BY '\\n' ({column_list})"
+ )
+
+
+def _deserialize_json_value(value: Any, deserializer: "Callable[[Any], Any]", *, logger: Any | None = None) -> Any:
+ """Safely decode JSON column values using the specified deserializer."""
+ if value is None:
+ return None
+ if isinstance(value, (dict, list)):
+ return value
+ if isinstance(value, (bytes, bytearray, memoryview)):
+ try:
+ raw_bytes = bytes(value) if isinstance(value, (bytearray, memoryview)) else value
+ return deserializer(raw_bytes)
+ except Exception:
+ if logger is not None:
+ logger.debug("Failed to deserialize JSON column", exc_info=True)
+ return value
+ if isinstance(value, str):
+ try:
+ return deserializer(value)
+ except Exception:
+ if logger is not None:
+ logger.debug("Failed to deserialize JSON column", exc_info=True)
+ return value
+ return value
+
+
+def _deserialize_json_dict_rows(
+ column_names: "Sequence[str]",
+ rows: "Sequence[dict[str, Any]]",
+ json_indexes: "list[int]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[dict[str, Any]]":
+ """Deserialize JSON fields in dictionary rows in-place."""
+ if not json_indexes:
+ return rows if isinstance(rows, list) else list(rows)
+ json_columns = [column_names[idx] for idx in json_indexes if idx < len(column_names)]
+ for row in rows:
+ for column in json_columns:
+ if column in row and row[column] is not None:
+ row[column] = _deserialize_json_value(row[column], deserializer, logger=logger)
+ return rows if isinstance(rows, list) else list(rows)
+
+
+def _deserialize_json_tuple_rows(
+ rows: "Sequence[tuple[Any, ...]]",
+ json_indexes: "list[int]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[tuple[Any, ...]]":
+ """Deserialize JSON fields in tuple rows using sparse index iteration and lazy row mutation."""
+ if not json_indexes:
+ return rows if isinstance(rows, list) else list(rows)
+ result: list[tuple[Any, ...]] = []
+ for row in rows:
+ row_len = len(row)
+ row_list: list[Any] | None = None
+ for idx in json_indexes:
+ if idx >= row_len:
+ continue
+ raw_val = row[idx]
+ if raw_val is None:
+ continue
+ decoded = _deserialize_json_value(raw_val, deserializer, logger=logger)
+ if decoded is not raw_val:
+ if row_list is None:
+ row_list = list(row)
+ row_list[idx] = decoded
+ result.append(tuple(row_list) if row_list is not None else row)
+ return result
+
+
+def _create_mysql_error(
+ error: Any, sqlstate: str | None, code: int | None, error_class: type[SQLSpecError], description: str
+) -> SQLSpecError:
+ """Create a MySQL error instance without raising it."""
+ code_str = f"[{sqlstate or code}]" if sqlstate or code else ""
+ msg = f"MySQL {description} {code_str}: {error}" if code_str else f"MySQL {description}: {error}"
+ exc = error_class(msg)
+ exc.__cause__ = error
+ return exc
+
+
driver_profile = build_profile()
default_statement_config = build_statement_config()
diff --git a/sqlspec/adapters/db2/data_dictionary.py b/sqlspec/adapters/db2/data_dictionary.py
index 0afa6723f..aa776c98d 100644
--- a/sqlspec/adapters/db2/data_dictionary.py
+++ b/sqlspec/adapters/db2/data_dictionary.py
@@ -67,8 +67,9 @@ def build_column_metadata(rows: "Iterable[dict[str, Any]]") -> "list[ColumnMetad
Returns:
Column metadata in row order.
"""
- return [
- ColumnMetadata(
+ result: list[ColumnMetadata] = []
+ for r in rows:
+ meta = ColumnMetadata(
schema_name=str(r.get("schema_name", "")),
table_name=str(r.get("table_name", "")),
column_name=str(r.get("column_name", "")),
@@ -87,8 +88,18 @@ def build_column_metadata(rows: "Iterable[dict[str, Any]]") -> "list[ColumnMetad
),
is_generated=bool(r.get("is_generated")),
)
- for r in rows
- ]
+ if (raw_is_identity := r.get("is_identity")) is not None:
+ meta["is_identity"] = (
+ raw_is_identity.strip().upper() in {"1", "TRUE", "T", "YES", "Y", "ALWAYS"}
+ if isinstance(raw_is_identity, str)
+ else bool(raw_is_identity)
+ )
+ if r.get("generation_expression") is not None:
+ meta["generation_expression"] = str(r["generation_expression"])
+ if r.get("column_comment") is not None:
+ meta["column_comment"] = str(r["column_comment"])
+ result.append(meta)
+ return result
def build_index_metadata(rows: "Iterable[dict[str, Any]]") -> "list[IndexMetadata]":
diff --git a/sqlspec/adapters/db2/migrations.py b/sqlspec/adapters/db2/migrations.py
index 9a9b415b8..b9e383130 100644
--- a/sqlspec/adapters/db2/migrations.py
+++ b/sqlspec/adapters/db2/migrations.py
@@ -7,6 +7,8 @@
from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
if TYPE_CHECKING:
+ from collections.abc import Sequence
+
from sqlspec.builder import CreateTable, Insert
from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
@@ -55,7 +57,7 @@ def _tracking_table_exists_sql(self) -> "tuple[str, tuple[str | None, str]]":
"""
return TABLE_EXISTS_SQL, split_db2_table_name(self.version_table)
- def _record_migration_statement(
+ def _record_statement(
self,
version: str,
version_type: str,
@@ -64,44 +66,53 @@ def _record_migration_statement(
execution_time_ms: int,
checksum: str,
applied_by: str,
+ replaces: "Sequence[str] | str | None" = None,
) -> "Insert":
- """Return an INSERT for a migration record with a naive-UTC ``applied_at``.
-
- Args:
- version: Version number of the migration.
- version_type: Version format type.
- execution_sequence: Application order.
- description: Description of the migration.
- execution_time_ms: Execution time in milliseconds.
- checksum: Checksum of the migration content.
- applied_by: User who applied the migration.
+ """Build an INSERT statement for a standard or squashed Db2 migration record."""
+ columns = [
+ "version_num",
+ "version_type",
+ "execution_sequence",
+ "description",
+ "applied_at",
+ "execution_time_ms",
+ "checksum",
+ "applied_by",
+ ]
+ values: list[object] = [
+ version,
+ version_type,
+ execution_sequence,
+ description,
+ utc_now(),
+ execution_time_ms,
+ checksum,
+ applied_by,
+ ]
+ if replaces:
+ columns.append("replaces")
+ values.append(replaces if isinstance(replaces, str) else ",".join(replaces))
+ return sql.insert(self.version_table).columns(*columns).values(*values)
- Returns:
- INSERT builder.
- """
- return (
- sql
- .insert(self.version_table)
- .columns(
- "version_num",
- "version_type",
- "execution_sequence",
- "description",
- "applied_at",
- "execution_time_ms",
- "checksum",
- "applied_by",
- )
- .values(
- version,
- version_type,
- execution_sequence,
- description,
- utc_now(),
- execution_time_ms,
- checksum,
- applied_by,
- )
+ def _record_migration_statement(
+ self,
+ version: str,
+ version_type: str,
+ execution_sequence: int,
+ description: str,
+ execution_time_ms: int,
+ checksum: str,
+ applied_by: str,
+ ) -> "Insert":
+ """Return an INSERT for a migration record with a naive-UTC applied_at."""
+ return self._record_statement(
+ version=version,
+ version_type=version_type,
+ execution_sequence=execution_sequence,
+ description=description,
+ execution_time_ms=execution_time_ms,
+ checksum=checksum,
+ applied_by=applied_by,
)
def _record_squashed_migration_statement(
@@ -113,48 +124,18 @@ def _record_squashed_migration_statement(
execution_time_ms: int,
checksum: str,
applied_by: str,
- replaces: str,
+ replaces: "Sequence[str] | str",
) -> "Insert":
- """Return an INSERT for a squashed migration record with a naive-UTC ``applied_at``.
-
- Args:
- version: Version number of the squashed migration.
- version_type: Version format type.
- execution_sequence: Application order.
- description: Description of the migration.
- execution_time_ms: Execution time in milliseconds.
- checksum: Checksum of the migration content.
- applied_by: User who applied the migration.
- replaces: Comma-separated list of replaced versions.
-
- Returns:
- INSERT builder.
- """
- return (
- sql
- .insert(self.version_table)
- .columns(
- "version_num",
- "version_type",
- "execution_sequence",
- "description",
- "applied_at",
- "execution_time_ms",
- "checksum",
- "applied_by",
- "replaces",
- )
- .values(
- version,
- version_type,
- execution_sequence,
- description,
- utc_now(),
- execution_time_ms,
- checksum,
- applied_by,
- replaces,
- )
+ """Return an INSERT for a squashed migration record with a naive-UTC applied_at."""
+ return self._record_statement(
+ version=version,
+ version_type=version_type,
+ execution_sequence=execution_sequence,
+ description=description,
+ execution_time_ms=execution_time_ms,
+ checksum=checksum,
+ applied_by=applied_by,
+ replaces=replaces,
)
diff --git a/sqlspec/adapters/duckdb/core.py b/sqlspec/adapters/duckdb/core.py
index c9ecf6997..c19a21d93 100644
--- a/sqlspec/adapters/duckdb/core.py
+++ b/sqlspec/adapters/duckdb/core.py
@@ -456,16 +456,7 @@ def create_duckdb_profile_path() -> Path:
return Path(tempfile.gettempdir()) / f"sqlspec_duckdb_profile_{uuid4().hex}.json"
-def configure_duckdb_statement_profiling(connection: Any, profile_path: Path) -> None:
- """Enable DuckDB JSON statement profiling on a connection without observer emission."""
- escaped_path = str(profile_path).replace("'", "''")
- connection.execute("PRAGMA enable_profiling='json'")
- with contextlib.suppress(Exception):
- connection.execute("SET profiling_coverage='ALL'")
- connection.execute(f"SET profiling_output='{escaped_path}'")
-
-
-def cleanup_duckdb_profile_path(profile_path: "Path | str | None") -> None:
+def cleanup_duckdb_profile_path(profile_path: Path | str | None) -> None:
"""Remove a temporary DuckDB JSON statement profile file if it exists."""
if not profile_path:
return
@@ -473,9 +464,7 @@ def cleanup_duckdb_profile_path(profile_path: "Path | str | None") -> None:
Path(profile_path).unlink(missing_ok=True)
-def read_duckdb_profile_json(
- profile_path: "Path | str | None", executed_sql: str | None = None
-) -> "dict[str, Any] | None":
+def read_duckdb_profile_json(profile_path: Path | str | None, executed_sql: str | None = None) -> dict[str, Any] | None:
"""Read and validate the DuckDB JSON profile file written for executed_sql."""
if not profile_path:
return None
@@ -511,6 +500,15 @@ def read_duckdb_profile_json(
return cast("dict[str, Any]", parsed)
+def configure_duckdb_statement_profiling(connection: Any, profile_path: Path) -> None:
+ """Enable DuckDB JSON statement profiling on a connection without observer emission."""
+ escaped_path = str(profile_path).replace("'", "''")
+ connection.execute("PRAGMA enable_profiling='json'")
+ with contextlib.suppress(Exception):
+ connection.execute("SET profiling_coverage='ALL'")
+ connection.execute(f"SET profiling_output='{escaped_path}'")
+
+
_EXCEPTION_MAPPING: Final[dict[type[BaseException], tuple[type[SQLSpecError], str]]] = {
DuckDBCatalogException: (NotFoundError, "catalog error"),
DuckDBParserException: (SQLParsingError, "SQL parsing error"),
@@ -608,9 +606,12 @@ def duckdb_version_tuple() -> "tuple[int, int, int]":
return (0, 0, 0)
+_DUCKDB_COPY_FILENAME_FOLLOWS_QUERY: Final[bool] = duckdb_version_tuple() >= (2, 0, 0)
+
+
def duckdb_copy_filename_follows_query() -> bool:
"""Return whether DuckDB COPY TO parameter binding follows query bindings."""
- return duckdb_version_tuple() >= (2, 0, 0)
+ return _DUCKDB_COPY_FILENAME_FOLLOWS_QUERY
def _parse_memory_size_bytes(memory_limit: str) -> "int | None":
diff --git a/sqlspec/adapters/duckdb/driver.py b/sqlspec/adapters/duckdb/driver.py
index 0c9292428..882430b68 100644
--- a/sqlspec/adapters/duckdb/driver.py
+++ b/sqlspec/adapters/duckdb/driver.py
@@ -144,12 +144,25 @@ def dispatch_execute(self, cursor: "DuckDBConnection", statement: SQL) -> "Execu
profiling_enabled = bool(self.driver_features.get("enable_statement_profiling", False))
if profiling_enabled:
self._ensure_statement_profiling(cursor)
- cursor.execute(sql, normalize_execute_parameters(prepared_parameters))
- is_select_like = statement.returns_rows() or self._should_force_select(statement, cursor)
+ if statement.returns_rows():
+ data, column_names, row_count, row_format = self._execute_and_materialize_select(
+ cursor, sql, prepared_parameters, sql, profiling_enabled=profiling_enabled
+ )
+ if profiling_enabled:
+ self._capture_statement_profile(sql)
+ return self.create_execution_result(
+ cursor,
+ selected_data=data,
+ column_names=column_names,
+ data_row_count=row_count,
+ is_select_result=True,
+ row_format=row_format,
+ )
- if is_select_like:
- data, column_names, row_count, row_format = self._materialize_select_rows(cursor, sql)
+ cursor.execute(sql, normalize_execute_parameters(prepared_parameters))
+ if self._should_force_select(statement, cursor):
+ data, column_names, row_count, row_format = self._materialize_fetched_rows(cursor, sql)
if profiling_enabled:
self._capture_statement_profile(sql)
return self.create_execution_result(
@@ -405,12 +418,26 @@ def select_to_arrow(
try:
with exc_handler:
sql, driver_params = self._compiled_sql(prepared_statement, config)
- stream_cursor.execute(sql, driver_params or ())
- arrow_reader = (
- stream_cursor.to_arrow_reader(batch_size)
- if batch_size is not None
- else stream_cursor.to_arrow_reader()
- )
+ sql_method = getattr(stream_cursor, "sql", None)
+ if callable(sql_method):
+ relation: Any = sql_method(sql, params=driver_params or None)
+ arrow_reader = (
+ None
+ if relation is None
+ else (
+ relation.to_arrow_reader(batch_size)
+ if batch_size is not None
+ else relation.to_arrow_reader()
+ )
+ )
+ else:
+ stream_cursor.execute(sql, driver_params or ())
+ arrow_reader = (
+ stream_cursor.to_arrow_reader(batch_size)
+ if batch_size is not None
+ else stream_cursor.to_arrow_reader()
+ )
+ arrow_reader = _ensure_arrow_reader(arrow_reader)
if close_cursor:
arrow_reader = arrow_reader_with_deferred_close(arrow_reader, stream_cursor.close)
arrow_result = build_arrow_result_from_reader(
@@ -427,21 +454,47 @@ def select_to_arrow(
stream_cursor.close()
raise
else:
- with self.with_cursor(self.connection) as cursor, exc_handler:
- sql, driver_params = self._compiled_sql(prepared_statement, config)
- cursor.execute(sql, driver_params or ())
- arrow_table = cursor.to_arrow_table()
- if arrow_table is None:
- msg = "DuckDB cursor did not produce an Arrow table"
- raise SQLSpecError(msg)
- arrow_result = build_arrow_result_from_table(
- prepared_statement,
- arrow_table,
- return_format=return_format,
- batch_size=batch_size,
- arrow_schema=arrow_schema,
- )
- self._check_pending_exception(exc_handler)
+ stream_cursor, close_cursor = self._acquire_stream_cursor()
+ try:
+ with exc_handler:
+ sql, driver_params = self._compiled_sql(prepared_statement, config)
+ sql_method = getattr(stream_cursor, "sql", None)
+ if callable(sql_method):
+ relation = sql_method(sql, params=driver_params or None)
+ try:
+ arrow_table = (
+ None
+ if relation is None
+ else (
+ relation.to_arrow_table(batch_size)
+ if batch_size is not None
+ else relation.to_arrow_table()
+ )
+ )
+ finally:
+ if relation is not None:
+ close_rel = getattr(relation, "close", None)
+ if callable(close_rel):
+ with contextlib.suppress(Exception):
+ close_rel()
+ else:
+ stream_cursor.execute(sql, driver_params or ())
+ arrow_table = stream_cursor.to_arrow_table()
+ if arrow_table is None:
+ msg = "DuckDB cursor did not produce an Arrow table"
+ raise SQLSpecError(msg)
+ arrow_result = build_arrow_result_from_table(
+ prepared_statement,
+ arrow_table,
+ return_format=return_format,
+ batch_size=batch_size,
+ arrow_schema=arrow_schema,
+ )
+ self._check_pending_exception(exc_handler)
+ finally:
+ if close_cursor:
+ with contextlib.suppress(Exception):
+ stream_cursor.close()
if arrow_result is None:
msg = "Unreachable"
@@ -642,9 +695,10 @@ def _execute_cache_hit_with_cursor(
if profiling_enabled:
self._ensure_statement_profiling(cursor)
wire_sql = self._format_wire_sql(cached.compiled_sql)
- cursor.execute(wire_sql, normalize_execute_parameters(params))
if cached.operation_profile.returns_rows:
- data, column_names, row_count, row_format = self._materialize_select_rows(cursor, cached.compiled_sql)
+ data, column_names, row_count, row_format = self._execute_and_materialize_select(
+ cursor, wire_sql, params, cached.compiled_sql, profiling_enabled=profiling_enabled
+ )
if profiling_enabled:
self._capture_statement_profile(wire_sql)
return self.build_cached_select_result(
@@ -657,6 +711,7 @@ def _execute_cache_hit_with_cursor(
row_format=row_format,
compiled_sql=wire_sql,
)
+ cursor.execute(wire_sql, normalize_execute_parameters(params))
affected_rows = resolve_rowcount(cursor)
if profiling_enabled:
with contextlib.suppress(Exception):
@@ -679,21 +734,69 @@ def _capture_statement_profile(self, executed_sql: str) -> "dict[str, Any] | Non
self._pending_statement_profile = profile
return profile
+ def _execute_and_materialize_select(
+ self, cursor: Any, wire_sql: str, parameters: Any, cache_key: str, *, profiling_enabled: bool = False
+ ) -> "tuple[list[Any], list[str], int, RowFormat]":
+ """Execute a row-returning statement and materialize rows via Arrow or fetchall."""
+ hint = self._cache_holder.get_row_count_hint(cache_key)
+ sql_method = getattr(cursor, "sql", None)
+ if (
+ pa is not None
+ and (hint is None or hint >= ARROW_MIN_ROWS)
+ and not profiling_enabled
+ and callable(sql_method)
+ ):
+ relation: Any = sql_method(wire_sql, params=parameters or None)
+ if relation is None:
+ self._cache_holder.set_row_count_hint(cache_key, 0)
+ return [], [], 0, "tuple"
+ try:
+ description = relation.description
+ if description_has_arrow_divergent_types(description):
+ fetched_data = relation.fetchall()
+ data, column_names = collect_rows(fetched_data, description)
+ row_count = len(data)
+ self._cache_holder.set_row_count_hint(cache_key, row_count)
+ return data, column_names, row_count, "tuple"
+ arrow_table = relation.to_arrow_table()
+ data = arrow_table.to_pylist()
+ _restore_uuid_columns(data, description)
+ column_names = list(arrow_table.column_names)
+ row_count = len(data)
+ self._cache_holder.set_row_count_hint(cache_key, row_count)
+ return data, column_names, row_count, "dict"
+ finally:
+ close_method = getattr(relation, "close", None)
+ if callable(close_method):
+ with contextlib.suppress(Exception):
+ close_method()
+
+ cursor.execute(wire_sql, normalize_execute_parameters(parameters))
+ if callable(sql_method) or profiling_enabled or (hint is not None and hint < ARROW_MIN_ROWS):
+ return self._materialize_fetched_rows(cursor, cache_key)
+ return self._materialize_select_rows(cursor, cache_key)
+
+ def _materialize_fetched_rows(self, cursor: Any, cache_key: str) -> "tuple[list[Any], list[str], int, RowFormat]":
+ """Materialize already-executed cursor rows via fetchall."""
+ description = cursor.description
+ fetched_data = cursor.fetchall()
+ data, column_names = collect_rows(fetched_data, description)
+ row_count = len(data)
+ self._cache_holder.set_row_count_hint(cache_key, row_count)
+ return data, column_names, row_count, "tuple"
+
def _materialize_select_rows(self, cursor: Any, compiled_sql: str) -> "tuple[list[Any], list[str], int, RowFormat]":
"""Materialize SELECT rows using DuckDB's native Python type contract."""
description = cursor.description
hint = self._cache_holder.get_row_count_hint(compiled_sql)
if (
- (hint is not None and hint < ARROW_MIN_ROWS)
+ pa is None
+ or (hint is not None and hint < ARROW_MIN_ROWS)
or description_has_arrow_divergent_types(description)
or not hasattr(cursor, "to_arrow_table")
or bool(self.driver_features.get("enable_statement_profiling", False))
):
- fetched_data = cursor.fetchall()
- data, column_names = collect_rows(fetched_data, description)
- row_count = len(data)
- self._cache_holder.set_row_count_hint(compiled_sql, row_count)
- return data, column_names, row_count, "tuple"
+ return self._materialize_fetched_rows(cursor, compiled_sql)
arrow_table = cursor.to_arrow_table()
data = arrow_table.to_pylist()
@@ -850,9 +953,15 @@ def _open_stream_reader(self, sql: str, parameters: Any, chunk_size: int) -> "tu
stream_cursor, close_cursor = self._acquire_stream_cursor()
try:
with handler:
- result = stream_cursor.execute(sql, normalize_execute_parameters(parameters))
- description = result.description
- reader = result.to_arrow_reader(chunk_size)
+ sql_method = getattr(stream_cursor, "sql", None)
+ if callable(sql_method):
+ relation: Any = sql_method(sql, params=parameters or None)
+ description = relation.description if relation is not None else None
+ reader = relation.to_arrow_reader(chunk_size) if relation is not None else None
+ else:
+ result = stream_cursor.execute(sql, normalize_execute_parameters(parameters))
+ description = result.description
+ reader = result.to_arrow_reader(chunk_size)
self._check_pending_exception(handler)
reader = _ensure_arrow_reader(reader)
if close_cursor:
diff --git a/sqlspec/adapters/mssql_python/migrations.py b/sqlspec/adapters/mssql_python/migrations.py
index f8c4f2ce4..e1a469ca4 100644
--- a/sqlspec/adapters/mssql_python/migrations.py
+++ b/sqlspec/adapters/mssql_python/migrations.py
@@ -1,7 +1,6 @@
"""mssql-python-specific migration tracker."""
import logging
-import os
from contextlib import suppress
from typing import TYPE_CHECKING
@@ -13,7 +12,6 @@
)
from sqlspec.builder import CreateTable, sql
from sqlspec.migrations.tracker import SyncMigrationTracker
-from sqlspec.migrations.version import parse_version
from sqlspec.observability import resolve_db_system
from sqlspec.utils.logging import get_logger, log_with_context
@@ -115,26 +113,12 @@ def record_migration(
Squashed migrations persist their replaced versions in the ``replaces`` column so
multi-environment reconciliation can recognize fully applied pre-squash histories.
"""
- parsed_version = parse_version(version)
- version_type = parsed_version.type.value
+ version_type = self._derive_version_type(version)
result = driver.execute(self._next_execution_sequence_query())
- next_sequence = result.get_data()[0]["next_seq"] if result.data else 1
- applied_by = os.environ.get("USER", "unknown")
- if replaces:
- statement = self._record_squashed_migration_statement(
- version,
- version_type,
- next_sequence,
- description,
- execution_time_ms,
- checksum,
- applied_by,
- ",".join(replaces),
- )
- else:
- statement = self._record_migration_statement(
- version, version_type, next_sequence, description, execution_time_ms, checksum, applied_by
- )
+ next_sequence = self._extract_next_sequence(result)
+ statement = self._record_statement(
+ version, version_type, next_sequence, description, execution_time_ms, checksum, self._applied_by(), replaces
+ )
driver.execute(statement)
if commit:
self._commit_if_needed(driver)
diff --git a/sqlspec/adapters/mysql_common.py b/sqlspec/adapters/mysql_common.py
deleted file mode 100644
index 4b0844adb..000000000
--- a/sqlspec/adapters/mysql_common.py
+++ /dev/null
@@ -1,561 +0,0 @@
-"""Shared MySQL-family adapter helpers, constants, and error mappings."""
-
-from collections.abc import Callable, Sequence, Sized
-from datetime import timedelta
-from typing import TYPE_CHECKING, Any, Final, Literal
-
-from sqlspec.driver import rows_to_dicts
-from sqlspec.exceptions import (
- CheckViolationError,
- ConnectionTimeoutError,
- DatabaseConnectionError,
- DataError,
- DeadlockError,
- ForeignKeyViolationError,
- IntegrityError,
- NotNullViolationError,
- PermissionDeniedError,
- QueryTimeoutError,
- SQLParsingError,
- SQLSpecError,
- TransactionError,
- UniqueViolationError,
-)
-from sqlspec.utils.text import quote_backtick_identifier, split_qualified_identifier
-from sqlspec.utils.type_guards import has_cursor_metadata, has_lastrowid, has_rowcount, has_sqlstate, has_type_code
-
-if TYPE_CHECKING:
- from sqlspec.core.parameters import ParameterValidator
-
-__all__ = (
- "MYSQL_ACCESS_ERROR_DISPATCH",
- "MYSQL_CONNECTION_ERROR_DISPATCH",
- "MYSQL_CONSTRAINT_ERROR_DISPATCH",
- "MYSQL_CR_CONNECTION_ERROR",
- "MYSQL_CR_CONN_HOST_ERROR",
- "MYSQL_CR_SERVER_GONE_ERROR",
- "MYSQL_CR_SERVER_LOST",
- "MYSQL_CR_SSL_CONNECTION_ERROR",
- "MYSQL_CR_UNKNOWN_HOST",
- "MYSQL_DATA_ERROR_DISPATCH",
- "MYSQL_ER_ACCESS_DENIED",
- "MYSQL_ER_CHECK_CONSTRAINT_VIOLATED",
- "MYSQL_ER_DBACCESS_DENIED",
- "MYSQL_ER_DUP_ENTRY",
- "MYSQL_ER_LOCK_DEADLOCK",
- "MYSQL_ER_LOCK_WAIT_TIMEOUT",
- "MYSQL_ER_NO_DEFAULT_FOR_FIELD",
- "MYSQL_ER_TABLEACCESS_DENIED",
- "MYSQL_MIGRATION_ERROR_CODES",
- "MYSQL_SQLSTATE_EXACT_DISPATCH",
- "MYSQL_SQLSTATE_PREFIX_DISPATCH",
- "MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE",
- "MYSQL_SYNTAX_ERROR_MIN",
- "MYSQL_TRANSACTION_ERROR_DISPATCH",
- "bool_to_int",
- "build_insert_statement",
- "build_load_data_statement",
- "can_use_local_infile_records",
- "collect_rows",
- "collect_stream_rows",
- "create_mapped_exception",
- "detect_json_columns",
- "detect_json_columns_from_description",
- "encode_records_for_local_infile",
- "escape_literal_percent",
- "format_identifier",
- "normalize_execute_many_parameters",
- "normalize_execute_parameters",
- "normalize_lastrowid",
- "resolve_column_names",
- "resolve_many_rowcount",
- "resolve_row_plan",
- "resolve_rowcount",
-)
-
-MYSQL_ER_DUP_ENTRY: Final[int] = 1062
-MYSQL_ER_NO_DEFAULT_FOR_FIELD: Final[int] = 1364
-MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: Final[int] = 3819
-MYSQL_ER_DBACCESS_DENIED: Final[int] = 1044
-MYSQL_ER_ACCESS_DENIED: Final[int] = 1045
-MYSQL_ER_TABLEACCESS_DENIED: Final[int] = 1142
-MYSQL_ER_LOCK_DEADLOCK: Final[int] = 1213
-MYSQL_ER_LOCK_WAIT_TIMEOUT: Final[int] = 1205
-
-MYSQL_CR_CONNECTION_ERROR: Final[int] = 2002
-MYSQL_CR_CONN_HOST_ERROR: Final[int] = 2003
-MYSQL_CR_UNKNOWN_HOST: Final[int] = 2005
-MYSQL_CR_SERVER_GONE_ERROR: Final[int] = 2006
-MYSQL_CR_SERVER_LOST: Final[int] = 2013
-MYSQL_CR_SSL_CONNECTION_ERROR: Final[int] = 2026
-MYSQL_SYNTAX_ERROR_MIN: Final[int] = 1064
-MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE: Final[int] = 1100
-
-MYSQL_MIGRATION_ERROR_CODES: Final[frozenset[int]] = frozenset((1061, 1091))
-MYSQL_SQLSTATE_EXACT_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
- "23505": (UniqueViolationError, "unique constraint violation"),
- "23503": (ForeignKeyViolationError, "foreign key constraint violation"),
- "23502": (NotNullViolationError, "not-null constraint violation"),
- "23514": (CheckViolationError, "check constraint violation"),
-}
-MYSQL_SQLSTATE_PREFIX_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
- "23": (IntegrityError, "integrity constraint violation"),
- "28": (PermissionDeniedError, "authorization error"),
- "40": (TransactionError, "transaction error"),
- "42": (SQLParsingError, "SQL syntax error"),
- "08": (DatabaseConnectionError, "connection error"),
- "22": (DataError, "data error"),
-}
-MYSQL_CONSTRAINT_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
- MYSQL_ER_DUP_ENTRY: (UniqueViolationError, "unique constraint violation"),
- 1169: (UniqueViolationError, "unique constraint violation"),
- 1586: (UniqueViolationError, "unique constraint violation"),
- 1216: (ForeignKeyViolationError, "foreign key constraint violation"),
- 1217: (ForeignKeyViolationError, "foreign key constraint violation"),
- 1451: (ForeignKeyViolationError, "foreign key constraint violation"),
- 1452: (ForeignKeyViolationError, "foreign key constraint violation"),
- 1048: (NotNullViolationError, "not-null constraint violation"),
- MYSQL_ER_NO_DEFAULT_FOR_FIELD: (NotNullViolationError, "not-null constraint violation"),
- MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: (CheckViolationError, "check constraint violation"),
- 4025: (CheckViolationError, "check constraint violation"),
-}
-MYSQL_ACCESS_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
- MYSQL_ER_DBACCESS_DENIED: (PermissionDeniedError, "access denied"),
- MYSQL_ER_ACCESS_DENIED: (PermissionDeniedError, "access denied"),
- MYSQL_ER_TABLEACCESS_DENIED: (PermissionDeniedError, "access denied"),
-}
-MYSQL_TRANSACTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
- MYSQL_ER_LOCK_DEADLOCK: (DeadlockError, "deadlock detected"),
- MYSQL_ER_LOCK_WAIT_TIMEOUT: (QueryTimeoutError, "lock wait timeout"),
- 1317: (QueryTimeoutError, "query execution interrupted"),
- 1969: (QueryTimeoutError, "query execution timeout"),
- 3024: (QueryTimeoutError, "query execution timeout"),
-}
-MYSQL_CONNECTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
- MYSQL_CR_SERVER_LOST: (ConnectionTimeoutError, "connection lost"),
- MYSQL_CR_CONNECTION_ERROR: (DatabaseConnectionError, "connection error"),
- MYSQL_CR_CONN_HOST_ERROR: (DatabaseConnectionError, "connection error"),
- 2004: (DatabaseConnectionError, "connection error"),
- MYSQL_CR_UNKNOWN_HOST: (DatabaseConnectionError, "connection error"),
- MYSQL_CR_SERVER_GONE_ERROR: (DatabaseConnectionError, "connection error"),
- MYSQL_CR_SSL_CONNECTION_ERROR: (DatabaseConnectionError, "ssl connection error"),
- 2055: (DatabaseConnectionError, "connection error"),
- 4031: (DatabaseConnectionError, "connection error"),
-}
-MYSQL_DATA_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
- 1264: (DataError, "data error"),
- 1265: (DataError, "data error"),
- 1292: (DataError, "data error"),
- 1366: (DataError, "data error"),
- 1406: (DataError, "data error"),
- 1411: (DataError, "data error"),
- 3140: (DataError, "data error"),
- 3141: (DataError, "data error"),
- 3142: (DataError, "data error"),
- 3143: (DataError, "data error"),
-}
-
-
-def format_identifier(identifier: str) -> str:
- """Format an SQL identifier with MySQL backtick quoting."""
- cleaned = identifier.strip()
- if not cleaned:
- msg = "Table name must not be empty"
- raise SQLSpecError(msg)
- parts = split_qualified_identifier(cleaned, quote_chars="`", allow_bracket_quotes=False)
- formatted = ".".join(quote_backtick_identifier(part) for part in parts)
- return formatted or quote_backtick_identifier(cleaned)
-
-
-def build_insert_statement(table: str, columns: "list[str]") -> str:
- """Construct an INSERT statement with positional placeholders."""
- column_clause = ", ".join(quote_backtick_identifier(column) for column in columns)
- placeholders = ", ".join("%s" for _ in columns)
- return f"INSERT INTO {format_identifier(table)} ({column_clause}) VALUES ({placeholders})"
-
-
-def can_use_local_infile_records(arrow_table: Any, records: "Sequence[Sequence[Any]]") -> bool:
- """Return whether Arrow records can be safely serialized via MySQL LOCAL INFILE TSV."""
- schema = getattr(arrow_table, "schema", None)
- if schema is not None:
- all_safe_types = True
- for field in schema:
- field_type = getattr(field, "type", None)
- type_str = str(field_type).lower() if field_type is not None else ""
- if "binary" in type_str or "duration" in type_str:
- return False
- if not type_str or not type_str.startswith((
- "int",
- "uint",
- "float",
- "double",
- "halffloat",
- "bool",
- "string",
- "large_string",
- "utf8",
- "large_utf8",
- "date",
- "timestamp",
- "time",
- "decimal",
- )):
- all_safe_types = False
- if all_safe_types:
- return True
- return not any(isinstance(value, (bytes, bytearray, memoryview, timedelta)) for row in records for value in row)
-
-
-def encode_records_for_local_infile(records: "list[tuple[Any, ...]]") -> bytes:
- """Encode row tuples into MySQL LOCAL INFILE TSV byte payload."""
- lines: list[str] = []
- for record in records:
- fields: list[str] = []
- for value in record:
- if value is None:
- fields.append("\\N")
- continue
- if isinstance(value, bool):
- value = int(value)
- text = value if isinstance(value, str) else str(value)
- text = (
- text
- .replace("\\", "\\\\")
- .replace("\x00", "\\0")
- .replace("\t", "\\t")
- .replace("\n", "\\n")
- .replace("\r", "\\r")
- .replace("\x1a", "\\Z")
- )
- fields.append(text)
- lines.append("\t".join(fields))
- return ("\n".join(lines) + "\n").encode("utf-8")
-
-
-def build_load_data_statement(
- table: str, columns: "list[str]", placeholder: str = "%s", *, escape_percent: bool = False
-) -> str:
- """Build native LOAD DATA SQL with a bound filename.
-
- Args:
- table: Destination table identifier.
- columns: Destination column names.
- placeholder: Filename placeholder parameter style.
- escape_percent: Whether to escape literal percent characters in identifiers.
-
- Returns:
- SQL statement for LOCAL INFILE bulk load.
- """
- table_sql = format_identifier(table)
- if escape_percent:
- table_sql = table_sql.replace("%", "%%")
- column_list = ", ".join(
- quote_backtick_identifier(column).replace("%", "%%") if escape_percent else quote_backtick_identifier(column)
- for column in columns
- )
- return (
- f"LOAD DATA LOCAL INFILE {placeholder} INTO TABLE {table_sql} "
- "CHARACTER SET utf8mb4 FIELDS TERMINATED BY '\\t' ESCAPED BY '\\\\' "
- f"LINES TERMINATED BY '\\n' ({column_list})"
- )
-
-
-def normalize_execute_parameters(parameters: Any) -> Any:
- """Normalize parameters for MySQL execute calls."""
- return parameters or None
-
-
-def normalize_execute_many_parameters(parameters: Any) -> Any:
- """Normalize parameters for MySQL executemany calls."""
- return parameters
-
-
-def resolve_column_names(description: "Sequence[Any] | None") -> "list[str]":
- """Extract column names from cursor description."""
- if not description:
- return []
- return [col[0] for col in description]
-
-
-def resolve_row_plan(
- description: "Sequence[Any] | None", json_type_codes: "set[int]"
-) -> "tuple[list[str], list[int] | None]":
- """Resolve ordered column names and JSON column indexes in one pass."""
- if not description:
- return [], None
-
- column_names: list[str] = []
- if not json_type_codes:
- column_names.extend(column[0] for column in description)
- return column_names, None
-
- json_indexes: list[int] = []
- append_json = json_indexes.append
- for index, column in enumerate(description):
- column_names.append(column[0])
- if isinstance(column, (tuple, list)):
- type_code = column[1] if len(column) > 1 else None
- else:
- type_code = column.type_code if has_type_code(column) else None
- if type_code in json_type_codes:
- append_json(index)
- return column_names, json_indexes or None
-
-
-def detect_json_columns_from_description(
- description: "Sequence[Any] | None", json_type_codes: "set[int]"
-) -> "list[int]":
- """Identify JSON column indexes from pre-fetched cursor description metadata."""
- return resolve_row_plan(description, json_type_codes)[1] or []
-
-
-def detect_json_columns(
- cursor: Any, json_type_codes: "set[int]", description: "Sequence[Any] | None" = None
-) -> "list[int]":
- """Identify JSON column indexes from cursor metadata."""
- if description is None:
- if not has_cursor_metadata(cursor):
- return []
- description = cursor.description
- return detect_json_columns_from_description(description, json_type_codes)
-
-
-def collect_stream_rows(
- rows: "Sequence[Any]",
- row_plan: "tuple[list[str], list[int] | None]",
- deserializer: "Callable[[Any], Any]",
- *,
- logger: Any | None = None,
-) -> "list[dict[str, Any]]":
- """Convert streamed rows to dictionaries with JSON decoding applied."""
- column_names, json_indexes = row_plan
- if not column_names or not rows:
- return []
- dict_rows = rows if isinstance(rows[0], dict) else rows_to_dicts(list(rows), column_names)
- if json_indexes:
- dict_rows = _deserialize_json_dict_rows(column_names, dict_rows, json_indexes, deserializer, logger=logger)
- return dict_rows if isinstance(dict_rows, list) else list(dict_rows)
-
-
-def collect_rows(
- fetched_data: "Sequence[Any] | None",
- row_plan: "tuple[list[str], list[int] | None]",
- deserializer: "Callable[[Any], Any]",
- *,
- logger: Any | None = None,
-) -> "tuple[list[Any], list[str], Literal['dict', 'tuple', 'record']]":
- """Collect fetched rows with JSON decoding and zero-copy fast-path."""
- column_names, json_indexes = row_plan
- if not column_names:
- return [], [], "tuple"
- if not fetched_data:
- return [], column_names, "tuple"
-
- first_row = fetched_data[0]
- if isinstance(first_row, dict):
- rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
- if json_indexes:
- rows = _deserialize_json_dict_rows(column_names, rows, json_indexes, deserializer, logger=logger)
- return rows, column_names, "dict"
-
- if not json_indexes:
- return fetched_data if isinstance(fetched_data, list) else list(fetched_data), column_names, "tuple"
-
- rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
- rows = _deserialize_json_tuple_rows(rows, json_indexes, deserializer, logger=logger)
- return rows, column_names, "tuple"
-
-
-def resolve_rowcount(cursor: Any) -> int:
- """Resolve rowcount safely from a cursor."""
- if not has_rowcount(cursor):
- return 0
- rowcount = cursor.rowcount
- if isinstance(rowcount, int) and rowcount >= 0:
- return rowcount
- return 0
-
-
-def resolve_many_rowcount(cursor: Any, parameters: Any, *, fallback_count: "int | None" = None) -> int:
- """Resolve execute_many rowcount using cursor metadata with payload fallback."""
- rowcount = resolve_rowcount(cursor)
- if rowcount > 0:
- return rowcount
- if fallback_count is not None:
- return fallback_count
- if isinstance(parameters, Sized):
- return len(parameters)
- return 0
-
-
-def normalize_lastrowid(cursor: Any) -> int | None:
- """Normalize lastrowid for MySQL when rowcount indicates success."""
- if not has_rowcount(cursor):
- return None
- rowcount = cursor.rowcount
- if not isinstance(rowcount, int) or rowcount <= 0:
- return None
- if not has_lastrowid(cursor):
- return None
- last_id = cursor.lastrowid
- return last_id if isinstance(last_id, int) else None
-
-
-def bool_to_int(value: bool) -> int:
- """Coerce boolean values to integer for MySQL parameter compatibility."""
- return int(value)
-
-
-def create_mapped_exception(error: Any, *, logger: Any | None = None) -> "SQLSpecError | bool":
- """Map MySQL driver exceptions to SQLSpec errors."""
- error_code = getattr(error, "errno", None)
- if error_code is None and isinstance(error, Exception) and error.args:
- value = error.args[0]
- if isinstance(value, int):
- error_code = value
- sqlstate = error.sqlstate if has_sqlstate(error) else None
- sqlstate_prefix = sqlstate[:2] if isinstance(sqlstate, str) and sqlstate else None
-
- if error_code in MYSQL_MIGRATION_ERROR_CODES:
- if logger is not None:
- logger.warning("MySQL expected migration error (ignoring): %s", error)
- return True
-
- dispatch = MYSQL_SQLSTATE_EXACT_DISPATCH.get(sqlstate) if sqlstate is not None else None
- if dispatch is not None:
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
-
- dispatch = MYSQL_CONSTRAINT_ERROR_DISPATCH.get(error_code) if error_code is not None else None
- if dispatch is not None:
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
-
- if sqlstate_prefix == "23":
- dispatch = MYSQL_SQLSTATE_PREFIX_DISPATCH["23"]
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
-
- dispatch = MYSQL_ACCESS_ERROR_DISPATCH.get(error_code) if error_code is not None else None
- if dispatch is not None:
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
- if sqlstate_prefix == "28":
- dispatch = MYSQL_SQLSTATE_PREFIX_DISPATCH["28"]
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
-
- dispatch = MYSQL_TRANSACTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
- if dispatch is not None:
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
- if sqlstate_prefix == "40":
- dispatch = MYSQL_SQLSTATE_PREFIX_DISPATCH["40"]
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
-
- if sqlstate_prefix == "42":
- dispatch = MYSQL_SQLSTATE_PREFIX_DISPATCH["42"]
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
- if isinstance(error_code, int) and MYSQL_SYNTAX_ERROR_MIN <= error_code < MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE:
- return _create_mysql_error(error, sqlstate, error_code, SQLParsingError, "SQL syntax error")
-
- dispatch = MYSQL_CONNECTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
- if dispatch is not None:
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
- if sqlstate_prefix == "08":
- dispatch = MYSQL_SQLSTATE_PREFIX_DISPATCH["08"]
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
-
- dispatch = MYSQL_DATA_ERROR_DISPATCH.get(error_code) if error_code is not None else None
- if dispatch is not None:
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
- if sqlstate_prefix == "22" or type(error).__name__ == "DataError":
- dispatch = MYSQL_SQLSTATE_PREFIX_DISPATCH["22"]
- return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
-
- return _create_mysql_error(error, sqlstate, error_code, SQLSpecError, "database error")
-
-
-def escape_literal_percent(sql: str, parameters: Any, validator: "ParameterValidator") -> str:
- """Escape literal percent characters before driver-side interpolation."""
- if not parameters or "%" not in sql:
- return sql
- keep = {info.position for info in validator.extract_parameters(sql)}
- return "".join("%%" if char == "%" and index not in keep else char for index, char in enumerate(sql))
-
-
-def _deserialize_json_value(value: Any, deserializer: "Callable[[Any], Any]", *, logger: Any | None = None) -> Any:
- """Safely decode JSON column values using the specified deserializer."""
- if value is None:
- return None
- if isinstance(value, (dict, list)):
- return value
- if isinstance(value, (bytes, bytearray, memoryview)):
- try:
- raw_bytes = bytes(value) if isinstance(value, (bytearray, memoryview)) else value
- return deserializer(raw_bytes)
- except Exception:
- if logger is not None:
- logger.debug("Failed to deserialize JSON column", exc_info=True)
- return value
- if isinstance(value, str):
- try:
- return deserializer(value)
- except Exception:
- if logger is not None:
- logger.debug("Failed to deserialize JSON column", exc_info=True)
- return value
- return value
-
-
-def _deserialize_json_dict_rows(
- column_names: "Sequence[str]",
- rows: "Sequence[dict[str, Any]]",
- json_indexes: "list[int]",
- deserializer: "Callable[[Any], Any]",
- *,
- logger: Any | None = None,
-) -> "list[dict[str, Any]]":
- """Deserialize JSON fields in dictionary rows in-place."""
- if not json_indexes:
- return rows if isinstance(rows, list) else list(rows)
- json_columns = [column_names[idx] for idx in json_indexes if idx < len(column_names)]
- for row in rows:
- for column in json_columns:
- if column in row and row[column] is not None:
- row[column] = _deserialize_json_value(row[column], deserializer, logger=logger)
- return rows if isinstance(rows, list) else list(rows)
-
-
-def _deserialize_json_tuple_rows(
- rows: "Sequence[tuple[Any, ...]]",
- json_indexes: "list[int]",
- deserializer: "Callable[[Any], Any]",
- *,
- logger: Any | None = None,
-) -> "list[tuple[Any, ...]]":
- """Deserialize JSON fields in tuple rows using sparse index iteration and lazy row mutation."""
- if not json_indexes:
- return rows if isinstance(rows, list) else list(rows)
- result: list[tuple[Any, ...]] = []
- for row in rows:
- row_len = len(row)
- row_list: list[Any] | None = None
- for idx in json_indexes:
- if idx >= row_len:
- continue
- raw_val = row[idx]
- if raw_val is None:
- continue
- decoded = _deserialize_json_value(raw_val, deserializer, logger=logger)
- if decoded is not raw_val:
- if row_list is None:
- row_list = list(row)
- row_list[idx] = decoded
- result.append(tuple(row_list) if row_list is not None else row)
- return result
-
-
-def _create_mysql_error(
- error: Any, sqlstate: str | None, code: int | None, error_class: type[SQLSpecError], description: str
-) -> SQLSpecError:
- """Create a MySQL error instance without raising it."""
- code_str = f"[{sqlstate or code}]" if sqlstate or code else ""
- msg = f"MySQL {description} {code_str}: {error}" if code_str else f"MySQL {description}: {error}"
- exc = error_class(msg)
- exc.__cause__ = error
- return exc
diff --git a/sqlspec/adapters/mysqlconnector/core.py b/sqlspec/adapters/mysqlconnector/core.py
index ff4e6068d..5103ad39f 100644
--- a/sqlspec/adapters/mysqlconnector/core.py
+++ b/sqlspec/adapters/mysqlconnector/core.py
@@ -1,36 +1,36 @@
"""MysqlConnector adapter compiled helpers."""
import contextlib
-from typing import TYPE_CHECKING, Any, cast
-
-from sqlspec.adapters import mysql_common
-from sqlspec.adapters.mysql_common import (
- bool_to_int,
- build_insert_statement,
- build_load_data_statement,
- can_use_local_infile_records,
- collect_rows,
- collect_stream_rows,
- create_mapped_exception,
- detect_json_columns,
- detect_json_columns_from_description,
- encode_records_for_local_infile,
- escape_literal_percent,
- format_identifier,
- normalize_execute_many_parameters,
- normalize_execute_parameters,
- normalize_lastrowid,
- resolve_column_names,
- resolve_many_rowcount,
- resolve_row_plan,
- resolve_rowcount,
-)
+from collections.abc import Callable, Mapping, Sequence, Sized
+from datetime import timedelta
+from typing import TYPE_CHECKING, Any, Final, Literal, cast
+
from sqlspec.core import DriverParameterProfile, ParameterStyle, StatementConfig, build_statement_config_from_profile
+from sqlspec.driver import rows_to_dicts
+from sqlspec.exceptions import (
+ CheckViolationError,
+ ConnectionTimeoutError,
+ DatabaseConnectionError,
+ DataError,
+ DeadlockError,
+ ForeignKeyViolationError,
+ IntegrityError,
+ NotNullViolationError,
+ PermissionDeniedError,
+ QueryTimeoutError,
+ SQLParsingError,
+ SQLSpecError,
+ TransactionError,
+ UniqueViolationError,
+)
+from sqlspec.utils.logging import get_logger
from sqlspec.utils.serializers import from_json, to_json
+from sqlspec.utils.text import quote_backtick_identifier, split_qualified_identifier
from sqlspec.utils.type_converters import build_uuid_coercions
+from sqlspec.utils.type_guards import has_cursor_metadata, has_lastrowid, has_rowcount, has_sqlstate, has_type_code
if TYPE_CHECKING:
- from collections.abc import Callable, Mapping
+ from sqlspec.core.parameters import ParameterValidator
__all__ = (
"MysqlConnectorAsyncStreamSource",
@@ -60,10 +60,89 @@
"resolve_rowcount",
)
-_MYSQL_ACCESS_ERROR_DISPATCH = mysql_common.MYSQL_ACCESS_ERROR_DISPATCH
-_MYSQL_CONNECTION_ERROR_DISPATCH = mysql_common.MYSQL_CONNECTION_ERROR_DISPATCH
-_MYSQL_CONSTRAINT_ERROR_DISPATCH = mysql_common.MYSQL_CONSTRAINT_ERROR_DISPATCH
-_MYSQL_TRANSACTION_ERROR_DISPATCH = mysql_common.MYSQL_TRANSACTION_ERROR_DISPATCH
+logger = get_logger("sqlspec.adapters.mysqlconnector")
+
+MYSQL_ER_DUP_ENTRY: Final[int] = 1062
+MYSQL_ER_NO_DEFAULT_FOR_FIELD: Final[int] = 1364
+MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: Final[int] = 3819
+MYSQL_ER_DBACCESS_DENIED: Final[int] = 1044
+MYSQL_ER_ACCESS_DENIED: Final[int] = 1045
+MYSQL_ER_TABLEACCESS_DENIED: Final[int] = 1142
+MYSQL_ER_LOCK_DEADLOCK: Final[int] = 1213
+MYSQL_ER_LOCK_WAIT_TIMEOUT: Final[int] = 1205
+
+MYSQL_CR_CONNECTION_ERROR: Final[int] = 2002
+MYSQL_CR_CONN_HOST_ERROR: Final[int] = 2003
+MYSQL_CR_UNKNOWN_HOST: Final[int] = 2005
+MYSQL_CR_SERVER_GONE_ERROR: Final[int] = 2006
+MYSQL_CR_SERVER_LOST: Final[int] = 2013
+MYSQL_CR_SSL_CONNECTION_ERROR: Final[int] = 2026
+MYSQL_SYNTAX_ERROR_MIN: Final[int] = 1064
+MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE: Final[int] = 1100
+
+_MYSQL_MIGRATION_ERROR_CODES: Final[frozenset[int]] = frozenset((1061, 1091))
+_MYSQL_SQLSTATE_EXACT_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
+ "23505": (UniqueViolationError, "unique constraint violation"),
+ "23503": (ForeignKeyViolationError, "foreign key constraint violation"),
+ "23502": (NotNullViolationError, "not-null constraint violation"),
+ "23514": (CheckViolationError, "check constraint violation"),
+}
+_MYSQL_SQLSTATE_PREFIX_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
+ "23": (IntegrityError, "integrity constraint violation"),
+ "28": (PermissionDeniedError, "authorization error"),
+ "40": (TransactionError, "transaction error"),
+ "42": (SQLParsingError, "SQL syntax error"),
+ "08": (DatabaseConnectionError, "connection error"),
+ "22": (DataError, "data error"),
+}
+_MYSQL_CONSTRAINT_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_DUP_ENTRY: (UniqueViolationError, "unique constraint violation"),
+ 1169: (UniqueViolationError, "unique constraint violation"),
+ 1586: (UniqueViolationError, "unique constraint violation"),
+ 1216: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1217: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1451: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1452: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1048: (NotNullViolationError, "not-null constraint violation"),
+ MYSQL_ER_NO_DEFAULT_FOR_FIELD: (NotNullViolationError, "not-null constraint violation"),
+ MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: (CheckViolationError, "check constraint violation"),
+ 4025: (CheckViolationError, "check constraint violation"),
+}
+_MYSQL_ACCESS_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_DBACCESS_DENIED: (PermissionDeniedError, "access denied"),
+ MYSQL_ER_ACCESS_DENIED: (PermissionDeniedError, "access denied"),
+ MYSQL_ER_TABLEACCESS_DENIED: (PermissionDeniedError, "access denied"),
+}
+_MYSQL_TRANSACTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_LOCK_DEADLOCK: (DeadlockError, "deadlock detected"),
+ MYSQL_ER_LOCK_WAIT_TIMEOUT: (QueryTimeoutError, "lock wait timeout"),
+ 1317: (QueryTimeoutError, "query execution interrupted"),
+ 1969: (QueryTimeoutError, "query execution timeout"),
+ 3024: (QueryTimeoutError, "query execution timeout"),
+}
+_MYSQL_CONNECTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_CR_SERVER_LOST: (ConnectionTimeoutError, "connection lost"),
+ MYSQL_CR_CONNECTION_ERROR: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_CONN_HOST_ERROR: (DatabaseConnectionError, "connection error"),
+ 2004: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_UNKNOWN_HOST: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_SERVER_GONE_ERROR: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_SSL_CONNECTION_ERROR: (DatabaseConnectionError, "ssl connection error"),
+ 2055: (DatabaseConnectionError, "connection error"),
+ 4031: (DatabaseConnectionError, "connection error"),
+}
+_MYSQL_DATA_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ 1264: (DataError, "data error"),
+ 1265: (DataError, "data error"),
+ 1292: (DataError, "data error"),
+ 1366: (DataError, "data error"),
+ 1406: (DataError, "data error"),
+ 1411: (DataError, "data error"),
+ 3140: (DataError, "data error"),
+ 3141: (DataError, "data error"),
+ 3142: (DataError, "data error"),
+ 3143: (DataError, "data error"),
+}
class MysqlConnectorSyncStreamSource:
@@ -271,6 +350,406 @@ def apply_driver_features(
return statement_config, features
+def format_identifier(identifier: str) -> str:
+ """Format an SQL identifier with MySQL backtick quoting."""
+ cleaned = identifier.strip()
+ if not cleaned:
+ msg = "Table name must not be empty"
+ raise SQLSpecError(msg)
+ parts = split_qualified_identifier(cleaned, quote_chars="`", allow_bracket_quotes=False)
+ formatted = ".".join(quote_backtick_identifier(part) for part in parts)
+ return formatted or quote_backtick_identifier(cleaned)
+
+
+def build_insert_statement(table: str, columns: "list[str]") -> str:
+ """Construct an INSERT statement with positional placeholders."""
+ column_clause = ", ".join(quote_backtick_identifier(column) for column in columns)
+ placeholders = ", ".join("%s" for _ in columns)
+ return f"INSERT INTO {format_identifier(table)} ({column_clause}) VALUES ({placeholders})"
+
+
+def can_use_local_infile_records(arrow_table: Any, records: "Sequence[Sequence[Any]]") -> bool:
+ """Return whether Arrow records can be safely serialized via MySQL LOCAL INFILE TSV."""
+ schema = getattr(arrow_table, "schema", None)
+ if schema is not None:
+ all_safe_types = True
+ for field in schema:
+ field_type = getattr(field, "type", None)
+ type_str = str(field_type).lower() if field_type is not None else ""
+ if "binary" in type_str or "duration" in type_str:
+ return False
+ if not type_str or not type_str.startswith((
+ "int",
+ "uint",
+ "float",
+ "double",
+ "halffloat",
+ "bool",
+ "string",
+ "large_string",
+ "utf8",
+ "large_utf8",
+ "date",
+ "timestamp",
+ "time",
+ "decimal",
+ )):
+ all_safe_types = False
+ if all_safe_types:
+ return True
+ return not any(isinstance(value, (bytes, bytearray, memoryview, timedelta)) for row in records for value in row)
+
+
+def encode_records_for_local_infile(records: "list[tuple[Any, ...]]") -> bytes:
+ """Encode row tuples into MySQL LOCAL INFILE TSV byte payload."""
+ lines: list[str] = []
+ for record in records:
+ fields: list[str] = []
+ for value in record:
+ if value is None:
+ fields.append("\\N")
+ continue
+ if isinstance(value, bool):
+ value = int(value)
+ if isinstance(value, (bytes, bytearray, memoryview)):
+ text = bytes(value).decode("utf-8", errors="replace")
+ elif isinstance(value, str):
+ text = value
+ else:
+ text = str(value)
+ text = (
+ text
+ .replace("\\", "\\\\")
+ .replace("\x00", "\\0")
+ .replace("\t", "\\t")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ .replace("\x1a", "\\Z")
+ )
+ fields.append(text)
+ lines.append("\t".join(fields))
+ return ("\n".join(lines) + "\n").encode("utf-8")
+
+
+def normalize_execute_parameters(parameters: Any) -> Any:
+ """Normalize parameters for MySQL execute calls."""
+ return parameters or None
+
+
+def normalize_execute_many_parameters(parameters: Any) -> Any:
+ """Normalize parameters for MySQL executemany calls."""
+ return parameters
+
+
+def resolve_column_names(description: "Sequence[Any] | None") -> "list[str]":
+ """Extract column names from cursor description."""
+ if not description:
+ return []
+ return [col[0] for col in description]
+
+
+def resolve_row_plan(
+ description: "Sequence[Any] | None", json_type_codes: "set[int]"
+) -> "tuple[list[str], list[int] | None]":
+ """Resolve ordered column names and JSON column indexes in one pass."""
+ if not description:
+ return [], None
+
+ column_names: list[str] = []
+ if not json_type_codes:
+ column_names.extend(column[0] for column in description)
+ return column_names, None
+
+ json_indexes: list[int] = []
+ append_json = json_indexes.append
+ for index, column in enumerate(description):
+ column_names.append(column[0])
+ if isinstance(column, (tuple, list)):
+ type_code = column[1] if len(column) > 1 else None
+ else:
+ type_code = column.type_code if has_type_code(column) else None
+ if type_code in json_type_codes:
+ append_json(index)
+ return column_names, json_indexes or None
+
+
+def detect_json_columns_from_description(
+ description: "Sequence[Any] | None", json_type_codes: "set[int]"
+) -> "list[int]":
+ """Identify JSON column indexes from pre-fetched cursor description metadata."""
+ return resolve_row_plan(description, json_type_codes)[1] or []
+
+
+def detect_json_columns(
+ cursor: Any, json_type_codes: "set[int]", description: "Sequence[Any] | None" = None
+) -> "list[int]":
+ """Identify JSON column indexes from cursor metadata."""
+ if description is None:
+ if not has_cursor_metadata(cursor):
+ return []
+ description = cursor.description
+ return detect_json_columns_from_description(description, json_type_codes)
+
+
+def collect_stream_rows(
+ rows: "Sequence[Any]",
+ row_plan: "tuple[list[str], list[int] | None]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[dict[str, Any]]":
+ """Convert streamed rows to dictionaries with JSON decoding applied."""
+ column_names, json_indexes = row_plan
+ if not column_names or not rows:
+ return []
+ dict_rows = rows if isinstance(rows[0], dict) else rows_to_dicts(list(rows), column_names)
+ if json_indexes:
+ dict_rows = _deserialize_json_dict_rows(column_names, dict_rows, json_indexes, deserializer, logger=logger)
+ return dict_rows if isinstance(dict_rows, list) else list(dict_rows)
+
+
+def collect_rows(
+ fetched_data: "Sequence[Any] | None",
+ row_plan: "tuple[list[str], list[int] | None]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "tuple[list[Any], list[str], Literal['dict', 'tuple', 'record']]":
+ """Collect fetched rows with JSON decoding and zero-copy fast-path."""
+ column_names, json_indexes = row_plan
+ if not column_names:
+ return [], [], "tuple"
+ if not fetched_data:
+ return [], column_names, "tuple"
+
+ first_row = fetched_data[0]
+ if isinstance(first_row, dict):
+ rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
+ if json_indexes:
+ rows = _deserialize_json_dict_rows(column_names, rows, json_indexes, deserializer, logger=logger)
+ return rows, column_names, "dict"
+
+ if not json_indexes:
+ return fetched_data if isinstance(fetched_data, list) else list(fetched_data), column_names, "tuple"
+
+ rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
+ rows = _deserialize_json_tuple_rows(rows, json_indexes, deserializer, logger=logger)
+ return rows, column_names, "tuple"
+
+
+def resolve_rowcount(cursor: Any) -> int:
+ """Resolve rowcount safely from a cursor."""
+ if not has_rowcount(cursor):
+ return 0
+ rowcount = cursor.rowcount
+ if isinstance(rowcount, int) and rowcount >= 0:
+ return rowcount
+ return 0
+
+
+def resolve_many_rowcount(cursor: Any, parameters: Any, *, fallback_count: "int | None" = None) -> int:
+ """Resolve execute_many rowcount using cursor metadata with payload fallback."""
+ rowcount = resolve_rowcount(cursor)
+ if rowcount > 0:
+ return rowcount
+ if fallback_count is not None:
+ return fallback_count
+ if isinstance(parameters, Sized):
+ return len(parameters)
+ return 0
+
+
+def normalize_lastrowid(cursor: Any) -> int | None:
+ """Normalize lastrowid for MySQL when rowcount indicates success."""
+ if not has_rowcount(cursor):
+ return None
+ rowcount = cursor.rowcount
+ if not isinstance(rowcount, int) or rowcount <= 0:
+ return None
+ if not has_lastrowid(cursor):
+ return None
+ last_id = cursor.lastrowid
+ return last_id if isinstance(last_id, int) else None
+
+
+def bool_to_int(value: bool) -> int:
+ """Coerce boolean values to integer for MySQL parameter compatibility."""
+ return int(value)
+
+
+def create_mapped_exception(error: Any, *, logger: Any | None = None) -> "SQLSpecError | bool":
+ """Map MySQL driver exceptions to SQLSpec errors."""
+ error_code = getattr(error, "errno", None)
+ if error_code is None and isinstance(error, Exception) and error.args:
+ value = error.args[0]
+ if isinstance(value, int):
+ error_code = value
+ sqlstate = error.sqlstate if has_sqlstate(error) else None
+ sqlstate_prefix = sqlstate[:2] if isinstance(sqlstate, str) and sqlstate else None
+
+ if error_code in _MYSQL_MIGRATION_ERROR_CODES:
+ if logger is not None:
+ logger.warning("MySQL expected migration error (ignoring): %s", error)
+ return True
+
+ dispatch = _MYSQL_SQLSTATE_EXACT_DISPATCH.get(sqlstate) if sqlstate is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_CONSTRAINT_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ if sqlstate_prefix == "23":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["23"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_ACCESS_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "28":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["28"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_TRANSACTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "40":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["40"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ if sqlstate_prefix == "42":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["42"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if isinstance(error_code, int) and MYSQL_SYNTAX_ERROR_MIN <= error_code < MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE:
+ return _create_mysql_error(error, sqlstate, error_code, SQLParsingError, "SQL syntax error")
+
+ dispatch = _MYSQL_CONNECTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "08":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["08"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_DATA_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "22" or type(error).__name__ == "DataError":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["22"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ return _create_mysql_error(error, sqlstate, error_code, SQLSpecError, "database error")
+
+
+def escape_literal_percent(sql: str, parameters: Any, validator: "ParameterValidator") -> str:
+ """Escape literal percent characters before driver-side interpolation."""
+ if not parameters or "%" not in sql:
+ return sql
+ keep = {info.position for info in validator.extract_parameters(sql)}
+ return "".join("%%" if char == "%" and index not in keep else char for index, char in enumerate(sql))
+
+
+def build_load_data_statement(
+ table: str, columns: "list[str]", placeholder: str = "%s", *, escape_percent: bool = False
+) -> str:
+ """Build native LOAD DATA SQL with a bound filename."""
+ table_sql = format_identifier(table)
+ if escape_percent:
+ table_sql = table_sql.replace("%", "%%")
+ column_list = ", ".join(
+ quote_backtick_identifier(column).replace("%", "%%") if escape_percent else quote_backtick_identifier(column)
+ for column in columns
+ )
+ return (
+ f"LOAD DATA LOCAL INFILE {placeholder} INTO TABLE {table_sql} "
+ "CHARACTER SET utf8mb4 FIELDS TERMINATED BY '\\t' ESCAPED BY '\\\\' "
+ f"LINES TERMINATED BY '\\n' ({column_list})"
+ )
+
+
+def _deserialize_json_value(value: Any, deserializer: "Callable[[Any], Any]", *, logger: Any | None = None) -> Any:
+ """Safely decode JSON column values using the specified deserializer."""
+ if value is None:
+ return None
+ if isinstance(value, (dict, list)):
+ return value
+ if isinstance(value, (bytes, bytearray, memoryview)):
+ try:
+ raw_bytes = bytes(value) if isinstance(value, (bytearray, memoryview)) else value
+ return deserializer(raw_bytes)
+ except Exception:
+ if logger is not None:
+ logger.debug("Failed to deserialize JSON column", exc_info=True)
+ return value
+ if isinstance(value, str):
+ try:
+ return deserializer(value)
+ except Exception:
+ if logger is not None:
+ logger.debug("Failed to deserialize JSON column", exc_info=True)
+ return value
+ return value
+
+
+def _deserialize_json_dict_rows(
+ column_names: "Sequence[str]",
+ rows: "Sequence[dict[str, Any]]",
+ json_indexes: "list[int]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[dict[str, Any]]":
+ """Deserialize JSON fields in dictionary rows in-place."""
+ if not json_indexes:
+ return rows if isinstance(rows, list) else list(rows)
+ json_columns = [column_names[idx] for idx in json_indexes if idx < len(column_names)]
+ for row in rows:
+ for column in json_columns:
+ if column in row and row[column] is not None:
+ row[column] = _deserialize_json_value(row[column], deserializer, logger=logger)
+ return rows if isinstance(rows, list) else list(rows)
+
+
+def _deserialize_json_tuple_rows(
+ rows: "Sequence[tuple[Any, ...]]",
+ json_indexes: "list[int]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[tuple[Any, ...]]":
+ """Deserialize JSON fields in tuple rows using sparse index iteration and lazy row mutation."""
+ if not json_indexes:
+ return rows if isinstance(rows, list) else list(rows)
+ result: list[tuple[Any, ...]] = []
+ for row in rows:
+ row_len = len(row)
+ row_list: list[Any] | None = None
+ for idx in json_indexes:
+ if idx >= row_len:
+ continue
+ raw_val = row[idx]
+ if raw_val is None:
+ continue
+ decoded = _deserialize_json_value(raw_val, deserializer, logger=logger)
+ if decoded is not raw_val:
+ if row_list is None:
+ row_list = list(row)
+ row_list[idx] = decoded
+ result.append(tuple(row_list) if row_list is not None else row)
+ return result
+
+
+def _create_mysql_error(
+ error: Any, sqlstate: str | None, code: int | None, error_class: type[SQLSpecError], description: str
+) -> SQLSpecError:
+ """Create a MySQL error instance without raising it."""
+ code_str = f"[{sqlstate or code}]" if sqlstate or code else ""
+ msg = f"MySQL {description} {code_str}: {error}" if code_str else f"MySQL {description}: {error}"
+ exc = error_class(msg)
+ exc.__cause__ = error
+ return exc
+
+
driver_profile = build_profile()
default_statement_config = build_statement_config()
diff --git a/sqlspec/adapters/oracledb/migrations.py b/sqlspec/adapters/oracledb/migrations.py
index efb2604aa..7e24f2bff 100644
--- a/sqlspec/adapters/oracledb/migrations.py
+++ b/sqlspec/adapters/oracledb/migrations.py
@@ -278,21 +278,9 @@ def record_migration(
seq_data = next_seq_result.get_data()
execution_sequence = seq_data[0]["next_seq"] if seq_data else 1
- if replaces:
- record_sql = self._record_squashed_migration_statement(
- version,
- version_type,
- execution_sequence,
- description,
- execution_time_ms,
- checksum,
- applied_by,
- ",".join(replaces),
- )
- else:
- record_sql = self._record_migration_statement(
- version, version_type, execution_sequence, description, execution_time_ms, checksum, applied_by
- )
+ record_sql = self._record_statement(
+ version, version_type, execution_sequence, description, execution_time_ms, checksum, applied_by, replaces
+ )
driver.execute(record_sql)
if commit:
driver.commit()
@@ -516,21 +504,9 @@ async def record_migration(
seq_data = next_seq_result.get_data()
execution_sequence = seq_data[0]["next_seq"] if seq_data else 1
- if replaces:
- record_sql = self._record_squashed_migration_statement(
- version,
- version_type,
- execution_sequence,
- description,
- execution_time_ms,
- checksum,
- applied_by,
- ",".join(replaces),
- )
- else:
- record_sql = self._record_migration_statement(
- version, version_type, execution_sequence, description, execution_time_ms, checksum, applied_by
- )
+ record_sql = self._record_statement(
+ version, version_type, execution_sequence, description, execution_time_ms, checksum, applied_by, replaces
+ )
await driver.execute(record_sql)
if commit:
await driver.commit()
diff --git a/sqlspec/adapters/pymssql/migrations.py b/sqlspec/adapters/pymssql/migrations.py
index ae066d59d..e8ff99903 100644
--- a/sqlspec/adapters/pymssql/migrations.py
+++ b/sqlspec/adapters/pymssql/migrations.py
@@ -1,7 +1,6 @@
"""pymssql-specific migration tracker."""
import logging
-import os
from contextlib import suppress
from typing import TYPE_CHECKING
@@ -13,7 +12,6 @@
)
from sqlspec.builder import CreateTable, sql
from sqlspec.migrations.tracker import SyncMigrationTracker
-from sqlspec.migrations.version import parse_version
from sqlspec.observability import resolve_db_system
from sqlspec.utils.logging import get_logger, log_with_context
@@ -115,26 +113,12 @@ def record_migration(
Squashed migrations persist their replaced versions in the ``replaces`` column so
multi-environment reconciliation can recognize fully applied pre-squash histories.
"""
- parsed_version = parse_version(version)
- version_type = parsed_version.type.value
+ version_type = self._derive_version_type(version)
result = driver.execute(self._next_execution_sequence_query())
- next_sequence = result.get_data()[0]["next_seq"] if result.data else 1
- applied_by = os.environ.get("USER", "unknown")
- if replaces:
- statement = self._record_squashed_migration_statement(
- version,
- version_type,
- next_sequence,
- description,
- execution_time_ms,
- checksum,
- applied_by,
- ",".join(replaces),
- )
- else:
- statement = self._record_migration_statement(
- version, version_type, next_sequence, description, execution_time_ms, checksum, applied_by
- )
+ next_sequence = self._extract_next_sequence(result)
+ statement = self._record_statement(
+ version, version_type, next_sequence, description, execution_time_ms, checksum, self._applied_by(), replaces
+ )
driver.execute(statement)
if commit:
self._commit_if_needed(driver)
diff --git a/sqlspec/adapters/pymysql/core.py b/sqlspec/adapters/pymysql/core.py
index a36b809e2..b89db729d 100644
--- a/sqlspec/adapters/pymysql/core.py
+++ b/sqlspec/adapters/pymysql/core.py
@@ -1,37 +1,37 @@
"""PyMySQL adapter compiled helpers."""
import contextlib
-from typing import TYPE_CHECKING, Any, cast
-
-from sqlspec.adapters import mysql_common
-from sqlspec.adapters.mysql_common import (
- bool_to_int,
- build_insert_statement,
- build_load_data_statement,
- can_use_local_infile_records,
- collect_rows,
- collect_stream_rows,
- create_mapped_exception,
- detect_json_columns,
- detect_json_columns_from_description,
- encode_records_for_local_infile,
- escape_literal_percent,
- format_identifier,
- normalize_execute_many_parameters,
- normalize_execute_parameters,
- normalize_lastrowid,
- resolve_column_names,
- resolve_many_rowcount,
- resolve_row_plan,
- resolve_rowcount,
-)
+from collections.abc import Callable, Mapping, Sequence, Sized
+from datetime import timedelta
+from typing import TYPE_CHECKING, Any, Final, Literal, cast
+
from sqlspec.adapters.pymysql._typing import PyMysqlSSCursor
from sqlspec.core import DriverParameterProfile, ParameterStyle, StatementConfig, build_statement_config_from_profile
+from sqlspec.driver import rows_to_dicts
+from sqlspec.exceptions import (
+ CheckViolationError,
+ ConnectionTimeoutError,
+ DatabaseConnectionError,
+ DataError,
+ DeadlockError,
+ ForeignKeyViolationError,
+ IntegrityError,
+ NotNullViolationError,
+ PermissionDeniedError,
+ QueryTimeoutError,
+ SQLParsingError,
+ SQLSpecError,
+ TransactionError,
+ UniqueViolationError,
+)
+from sqlspec.utils.logging import get_logger
from sqlspec.utils.serializers import from_json, to_json
+from sqlspec.utils.text import quote_backtick_identifier, split_qualified_identifier
from sqlspec.utils.type_converters import build_uuid_coercions
+from sqlspec.utils.type_guards import has_cursor_metadata, has_lastrowid, has_rowcount, has_sqlstate, has_type_code
if TYPE_CHECKING:
- from collections.abc import Callable, Mapping
+ from sqlspec.core.parameters import ParameterValidator
__all__ = (
"PymysqlStreamSource",
@@ -60,10 +60,89 @@
"resolve_rowcount",
)
-_MYSQL_ACCESS_ERROR_DISPATCH = mysql_common.MYSQL_ACCESS_ERROR_DISPATCH
-_MYSQL_CONNECTION_ERROR_DISPATCH = mysql_common.MYSQL_CONNECTION_ERROR_DISPATCH
-_MYSQL_CONSTRAINT_ERROR_DISPATCH = mysql_common.MYSQL_CONSTRAINT_ERROR_DISPATCH
-_MYSQL_TRANSACTION_ERROR_DISPATCH = mysql_common.MYSQL_TRANSACTION_ERROR_DISPATCH
+logger = get_logger("sqlspec.adapters.pymysql")
+
+MYSQL_ER_DUP_ENTRY: Final[int] = 1062
+MYSQL_ER_NO_DEFAULT_FOR_FIELD: Final[int] = 1364
+MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: Final[int] = 3819
+MYSQL_ER_DBACCESS_DENIED: Final[int] = 1044
+MYSQL_ER_ACCESS_DENIED: Final[int] = 1045
+MYSQL_ER_TABLEACCESS_DENIED: Final[int] = 1142
+MYSQL_ER_LOCK_DEADLOCK: Final[int] = 1213
+MYSQL_ER_LOCK_WAIT_TIMEOUT: Final[int] = 1205
+
+MYSQL_CR_CONNECTION_ERROR: Final[int] = 2002
+MYSQL_CR_CONN_HOST_ERROR: Final[int] = 2003
+MYSQL_CR_UNKNOWN_HOST: Final[int] = 2005
+MYSQL_CR_SERVER_GONE_ERROR: Final[int] = 2006
+MYSQL_CR_SERVER_LOST: Final[int] = 2013
+MYSQL_CR_SSL_CONNECTION_ERROR: Final[int] = 2026
+MYSQL_SYNTAX_ERROR_MIN: Final[int] = 1064
+MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE: Final[int] = 1100
+
+_MYSQL_MIGRATION_ERROR_CODES: Final[frozenset[int]] = frozenset((1061, 1091))
+_MYSQL_SQLSTATE_EXACT_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
+ "23505": (UniqueViolationError, "unique constraint violation"),
+ "23503": (ForeignKeyViolationError, "foreign key constraint violation"),
+ "23502": (NotNullViolationError, "not-null constraint violation"),
+ "23514": (CheckViolationError, "check constraint violation"),
+}
+_MYSQL_SQLSTATE_PREFIX_DISPATCH: Final[dict[str, tuple[type[SQLSpecError], str]]] = {
+ "23": (IntegrityError, "integrity constraint violation"),
+ "28": (PermissionDeniedError, "authorization error"),
+ "40": (TransactionError, "transaction error"),
+ "42": (SQLParsingError, "SQL syntax error"),
+ "08": (DatabaseConnectionError, "connection error"),
+ "22": (DataError, "data error"),
+}
+_MYSQL_CONSTRAINT_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_DUP_ENTRY: (UniqueViolationError, "unique constraint violation"),
+ 1169: (UniqueViolationError, "unique constraint violation"),
+ 1586: (UniqueViolationError, "unique constraint violation"),
+ 1216: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1217: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1451: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1452: (ForeignKeyViolationError, "foreign key constraint violation"),
+ 1048: (NotNullViolationError, "not-null constraint violation"),
+ MYSQL_ER_NO_DEFAULT_FOR_FIELD: (NotNullViolationError, "not-null constraint violation"),
+ MYSQL_ER_CHECK_CONSTRAINT_VIOLATED: (CheckViolationError, "check constraint violation"),
+ 4025: (CheckViolationError, "check constraint violation"),
+}
+_MYSQL_ACCESS_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_DBACCESS_DENIED: (PermissionDeniedError, "access denied"),
+ MYSQL_ER_ACCESS_DENIED: (PermissionDeniedError, "access denied"),
+ MYSQL_ER_TABLEACCESS_DENIED: (PermissionDeniedError, "access denied"),
+}
+_MYSQL_TRANSACTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_ER_LOCK_DEADLOCK: (DeadlockError, "deadlock detected"),
+ MYSQL_ER_LOCK_WAIT_TIMEOUT: (QueryTimeoutError, "lock wait timeout"),
+ 1317: (QueryTimeoutError, "query execution interrupted"),
+ 1969: (QueryTimeoutError, "query execution timeout"),
+ 3024: (QueryTimeoutError, "query execution timeout"),
+}
+_MYSQL_CONNECTION_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ MYSQL_CR_SERVER_LOST: (ConnectionTimeoutError, "connection lost"),
+ MYSQL_CR_CONNECTION_ERROR: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_CONN_HOST_ERROR: (DatabaseConnectionError, "connection error"),
+ 2004: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_UNKNOWN_HOST: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_SERVER_GONE_ERROR: (DatabaseConnectionError, "connection error"),
+ MYSQL_CR_SSL_CONNECTION_ERROR: (DatabaseConnectionError, "ssl connection error"),
+ 2055: (DatabaseConnectionError, "connection error"),
+ 4031: (DatabaseConnectionError, "connection error"),
+}
+_MYSQL_DATA_ERROR_DISPATCH: Final[dict[int, tuple[type[SQLSpecError], str]]] = {
+ 1264: (DataError, "data error"),
+ 1265: (DataError, "data error"),
+ 1292: (DataError, "data error"),
+ 1366: (DataError, "data error"),
+ 1406: (DataError, "data error"),
+ 1411: (DataError, "data error"),
+ 3140: (DataError, "data error"),
+ 3141: (DataError, "data error"),
+ 3142: (DataError, "data error"),
+ 3143: (DataError, "data error"),
+}
class PymysqlStreamSource:
@@ -168,6 +247,406 @@ def apply_driver_features(
return statement_config, features
+def format_identifier(identifier: str) -> str:
+ """Format an SQL identifier with MySQL backtick quoting."""
+ cleaned = identifier.strip()
+ if not cleaned:
+ msg = "Table name must not be empty"
+ raise SQLSpecError(msg)
+ parts = split_qualified_identifier(cleaned, quote_chars="`", allow_bracket_quotes=False)
+ formatted = ".".join(quote_backtick_identifier(part) for part in parts)
+ return formatted or quote_backtick_identifier(cleaned)
+
+
+def build_insert_statement(table: str, columns: "list[str]") -> str:
+ """Construct an INSERT statement with positional placeholders."""
+ column_clause = ", ".join(quote_backtick_identifier(column) for column in columns)
+ placeholders = ", ".join("%s" for _ in columns)
+ return f"INSERT INTO {format_identifier(table)} ({column_clause}) VALUES ({placeholders})"
+
+
+def can_use_local_infile_records(arrow_table: Any, records: "Sequence[Sequence[Any]]") -> bool:
+ """Return whether Arrow records can be safely serialized via MySQL LOCAL INFILE TSV."""
+ schema = getattr(arrow_table, "schema", None)
+ if schema is not None:
+ all_safe_types = True
+ for field in schema:
+ field_type = getattr(field, "type", None)
+ type_str = str(field_type).lower() if field_type is not None else ""
+ if "binary" in type_str or "duration" in type_str:
+ return False
+ if not type_str or not type_str.startswith((
+ "int",
+ "uint",
+ "float",
+ "double",
+ "halffloat",
+ "bool",
+ "string",
+ "large_string",
+ "utf8",
+ "large_utf8",
+ "date",
+ "timestamp",
+ "time",
+ "decimal",
+ )):
+ all_safe_types = False
+ if all_safe_types:
+ return True
+ return not any(isinstance(value, (bytes, bytearray, memoryview, timedelta)) for row in records for value in row)
+
+
+def encode_records_for_local_infile(records: "list[tuple[Any, ...]]") -> bytes:
+ """Encode row tuples into MySQL LOCAL INFILE TSV byte payload."""
+ lines: list[str] = []
+ for record in records:
+ fields: list[str] = []
+ for value in record:
+ if value is None:
+ fields.append("\\N")
+ continue
+ if isinstance(value, bool):
+ value = int(value)
+ if isinstance(value, (bytes, bytearray, memoryview)):
+ text = bytes(value).decode("utf-8", errors="replace")
+ elif isinstance(value, str):
+ text = value
+ else:
+ text = str(value)
+ text = (
+ text
+ .replace("\\", "\\\\")
+ .replace("\x00", "\\0")
+ .replace("\t", "\\t")
+ .replace("\n", "\\n")
+ .replace("\r", "\\r")
+ .replace("\x1a", "\\Z")
+ )
+ fields.append(text)
+ lines.append("\t".join(fields))
+ return ("\n".join(lines) + "\n").encode("utf-8")
+
+
+def normalize_execute_parameters(parameters: Any) -> Any:
+ """Normalize parameters for MySQL execute calls."""
+ return parameters or None
+
+
+def normalize_execute_many_parameters(parameters: Any) -> Any:
+ """Normalize parameters for MySQL executemany calls."""
+ return parameters
+
+
+def resolve_column_names(description: "Sequence[Any] | None") -> "list[str]":
+ """Extract column names from cursor description."""
+ if not description:
+ return []
+ return [col[0] for col in description]
+
+
+def resolve_row_plan(
+ description: "Sequence[Any] | None", json_type_codes: "set[int]"
+) -> "tuple[list[str], list[int] | None]":
+ """Resolve ordered column names and JSON column indexes in one pass."""
+ if not description:
+ return [], None
+
+ column_names: list[str] = []
+ if not json_type_codes:
+ column_names.extend(column[0] for column in description)
+ return column_names, None
+
+ json_indexes: list[int] = []
+ append_json = json_indexes.append
+ for index, column in enumerate(description):
+ column_names.append(column[0])
+ if isinstance(column, (tuple, list)):
+ type_code = column[1] if len(column) > 1 else None
+ else:
+ type_code = column.type_code if has_type_code(column) else None
+ if type_code in json_type_codes:
+ append_json(index)
+ return column_names, json_indexes or None
+
+
+def detect_json_columns_from_description(
+ description: "Sequence[Any] | None", json_type_codes: "set[int]"
+) -> "list[int]":
+ """Identify JSON column indexes from pre-fetched cursor description metadata."""
+ return resolve_row_plan(description, json_type_codes)[1] or []
+
+
+def detect_json_columns(
+ cursor: Any, json_type_codes: "set[int]", description: "Sequence[Any] | None" = None
+) -> "list[int]":
+ """Identify JSON column indexes from cursor metadata."""
+ if description is None:
+ if not has_cursor_metadata(cursor):
+ return []
+ description = cursor.description
+ return detect_json_columns_from_description(description, json_type_codes)
+
+
+def collect_stream_rows(
+ rows: "Sequence[Any]",
+ row_plan: "tuple[list[str], list[int] | None]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[dict[str, Any]]":
+ """Convert streamed rows to dictionaries with JSON decoding applied."""
+ column_names, json_indexes = row_plan
+ if not column_names or not rows:
+ return []
+ dict_rows = rows if isinstance(rows[0], dict) else rows_to_dicts(list(rows), column_names)
+ if json_indexes:
+ dict_rows = _deserialize_json_dict_rows(column_names, dict_rows, json_indexes, deserializer, logger=logger)
+ return dict_rows if isinstance(dict_rows, list) else list(dict_rows)
+
+
+def collect_rows(
+ fetched_data: "Sequence[Any] | None",
+ row_plan: "tuple[list[str], list[int] | None]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "tuple[list[Any], list[str], Literal['dict', 'tuple', 'record']]":
+ """Collect fetched rows with JSON decoding and zero-copy fast-path."""
+ column_names, json_indexes = row_plan
+ if not column_names:
+ return [], [], "tuple"
+ if not fetched_data:
+ return [], column_names, "tuple"
+
+ first_row = fetched_data[0]
+ if isinstance(first_row, dict):
+ rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
+ if json_indexes:
+ rows = _deserialize_json_dict_rows(column_names, rows, json_indexes, deserializer, logger=logger)
+ return rows, column_names, "dict"
+
+ if not json_indexes:
+ return fetched_data if isinstance(fetched_data, list) else list(fetched_data), column_names, "tuple"
+
+ rows = fetched_data if isinstance(fetched_data, list) else list(fetched_data)
+ rows = _deserialize_json_tuple_rows(rows, json_indexes, deserializer, logger=logger)
+ return rows, column_names, "tuple"
+
+
+def resolve_rowcount(cursor: Any) -> int:
+ """Resolve rowcount safely from a cursor."""
+ if not has_rowcount(cursor):
+ return 0
+ rowcount = cursor.rowcount
+ if isinstance(rowcount, int) and rowcount >= 0:
+ return rowcount
+ return 0
+
+
+def resolve_many_rowcount(cursor: Any, parameters: Any, *, fallback_count: "int | None" = None) -> int:
+ """Resolve execute_many rowcount using cursor metadata with payload fallback."""
+ rowcount = resolve_rowcount(cursor)
+ if rowcount > 0:
+ return rowcount
+ if fallback_count is not None:
+ return fallback_count
+ if isinstance(parameters, Sized):
+ return len(parameters)
+ return 0
+
+
+def normalize_lastrowid(cursor: Any) -> int | None:
+ """Normalize lastrowid for MySQL when rowcount indicates success."""
+ if not has_rowcount(cursor):
+ return None
+ rowcount = cursor.rowcount
+ if not isinstance(rowcount, int) or rowcount <= 0:
+ return None
+ if not has_lastrowid(cursor):
+ return None
+ last_id = cursor.lastrowid
+ return last_id if isinstance(last_id, int) else None
+
+
+def bool_to_int(value: bool) -> int:
+ """Coerce boolean values to integer for MySQL parameter compatibility."""
+ return int(value)
+
+
+def create_mapped_exception(error: Any, *, logger: Any | None = None) -> "SQLSpecError | bool":
+ """Map MySQL driver exceptions to SQLSpec errors."""
+ error_code = getattr(error, "errno", None)
+ if error_code is None and isinstance(error, Exception) and error.args:
+ value = error.args[0]
+ if isinstance(value, int):
+ error_code = value
+ sqlstate = error.sqlstate if has_sqlstate(error) else None
+ sqlstate_prefix = sqlstate[:2] if isinstance(sqlstate, str) and sqlstate else None
+
+ if error_code in _MYSQL_MIGRATION_ERROR_CODES:
+ if logger is not None:
+ logger.warning("MySQL expected migration error (ignoring): %s", error)
+ return True
+
+ dispatch = _MYSQL_SQLSTATE_EXACT_DISPATCH.get(sqlstate) if sqlstate is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_CONSTRAINT_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ if sqlstate_prefix == "23":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["23"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_ACCESS_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "28":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["28"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_TRANSACTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "40":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["40"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ if sqlstate_prefix == "42":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["42"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if isinstance(error_code, int) and MYSQL_SYNTAX_ERROR_MIN <= error_code < MYSQL_SYNTAX_ERROR_MAX_EXCLUSIVE:
+ return _create_mysql_error(error, sqlstate, error_code, SQLParsingError, "SQL syntax error")
+
+ dispatch = _MYSQL_CONNECTION_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "08":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["08"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ dispatch = _MYSQL_DATA_ERROR_DISPATCH.get(error_code) if error_code is not None else None
+ if dispatch is not None:
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+ if sqlstate_prefix == "22" or type(error).__name__ == "DataError":
+ dispatch = _MYSQL_SQLSTATE_PREFIX_DISPATCH["22"]
+ return _create_mysql_error(error, sqlstate, error_code, dispatch[0], dispatch[1])
+
+ return _create_mysql_error(error, sqlstate, error_code, SQLSpecError, "database error")
+
+
+def escape_literal_percent(sql: str, parameters: Any, validator: "ParameterValidator") -> str:
+ """Escape literal percent characters before driver-side interpolation."""
+ if not parameters or "%" not in sql:
+ return sql
+ keep = {info.position for info in validator.extract_parameters(sql)}
+ return "".join("%%" if char == "%" and index not in keep else char for index, char in enumerate(sql))
+
+
+def build_load_data_statement(
+ table: str, columns: "list[str]", placeholder: str = "%s", *, escape_percent: bool = False
+) -> str:
+ """Build native LOAD DATA SQL with a bound filename."""
+ table_sql = format_identifier(table)
+ if escape_percent:
+ table_sql = table_sql.replace("%", "%%")
+ column_list = ", ".join(
+ quote_backtick_identifier(column).replace("%", "%%") if escape_percent else quote_backtick_identifier(column)
+ for column in columns
+ )
+ return (
+ f"LOAD DATA LOCAL INFILE {placeholder} INTO TABLE {table_sql} "
+ "CHARACTER SET utf8mb4 FIELDS TERMINATED BY '\\t' ESCAPED BY '\\\\' "
+ f"LINES TERMINATED BY '\\n' ({column_list})"
+ )
+
+
+def _deserialize_json_value(value: Any, deserializer: "Callable[[Any], Any]", *, logger: Any | None = None) -> Any:
+ """Safely decode JSON column values using the specified deserializer."""
+ if value is None:
+ return None
+ if isinstance(value, (dict, list)):
+ return value
+ if isinstance(value, (bytes, bytearray, memoryview)):
+ try:
+ raw_bytes = bytes(value) if isinstance(value, (bytearray, memoryview)) else value
+ return deserializer(raw_bytes)
+ except Exception:
+ if logger is not None:
+ logger.debug("Failed to deserialize JSON column", exc_info=True)
+ return value
+ if isinstance(value, str):
+ try:
+ return deserializer(value)
+ except Exception:
+ if logger is not None:
+ logger.debug("Failed to deserialize JSON column", exc_info=True)
+ return value
+ return value
+
+
+def _deserialize_json_dict_rows(
+ column_names: "Sequence[str]",
+ rows: "Sequence[dict[str, Any]]",
+ json_indexes: "list[int]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[dict[str, Any]]":
+ """Deserialize JSON fields in dictionary rows in-place."""
+ if not json_indexes:
+ return rows if isinstance(rows, list) else list(rows)
+ json_columns = [column_names[idx] for idx in json_indexes if idx < len(column_names)]
+ for row in rows:
+ for column in json_columns:
+ if column in row and row[column] is not None:
+ row[column] = _deserialize_json_value(row[column], deserializer, logger=logger)
+ return rows if isinstance(rows, list) else list(rows)
+
+
+def _deserialize_json_tuple_rows(
+ rows: "Sequence[tuple[Any, ...]]",
+ json_indexes: "list[int]",
+ deserializer: "Callable[[Any], Any]",
+ *,
+ logger: Any | None = None,
+) -> "list[tuple[Any, ...]]":
+ """Deserialize JSON fields in tuple rows using sparse index iteration and lazy row mutation."""
+ if not json_indexes:
+ return rows if isinstance(rows, list) else list(rows)
+ result: list[tuple[Any, ...]] = []
+ for row in rows:
+ row_len = len(row)
+ row_list: list[Any] | None = None
+ for idx in json_indexes:
+ if idx >= row_len:
+ continue
+ raw_val = row[idx]
+ if raw_val is None:
+ continue
+ decoded = _deserialize_json_value(raw_val, deserializer, logger=logger)
+ if decoded is not raw_val:
+ if row_list is None:
+ row_list = list(row)
+ row_list[idx] = decoded
+ result.append(tuple(row_list) if row_list is not None else row)
+ return result
+
+
+def _create_mysql_error(
+ error: Any, sqlstate: str | None, code: int | None, error_class: type[SQLSpecError], description: str
+) -> SQLSpecError:
+ """Create a MySQL error instance without raising it."""
+ code_str = f"[{sqlstate or code}]" if sqlstate or code else ""
+ msg = f"MySQL {description} {code_str}: {error}" if code_str else f"MySQL {description}: {error}"
+ exc = error_class(msg)
+ exc.__cause__ = error
+ return exc
+
+
driver_profile = build_profile()
default_statement_config = build_statement_config()
diff --git a/sqlspec/adapters/sqlanywhere/driver.py b/sqlspec/adapters/sqlanywhere/driver.py
index b4048fe3a..445f2e357 100644
--- a/sqlspec/adapters/sqlanywhere/driver.py
+++ b/sqlspec/adapters/sqlanywhere/driver.py
@@ -227,11 +227,6 @@ def rollback(self) -> None:
finally:
self._transaction_active = False
- def _release_open_work(self) -> None:
- """Roll back work left open before connection is returned to the pool."""
- with contextlib.suppress(SQLSpecError):
- self.rollback()
-
def with_cursor(self, connection: "SqlanywhereConnection") -> "SqlanywhereCursor":
return SqlanywhereCursor(connection)
@@ -391,6 +386,11 @@ def collect_rows(self, cursor: "SqlanywhereRawCursor", fetched: "list[Any]") ->
def resolve_rowcount(self, cursor: "SqlanywhereRawCursor") -> int:
return resolve_rowcount(cursor)
+ def _release_open_work(self) -> None:
+ """Roll back work left open before connection is returned to the pool."""
+ with contextlib.suppress(SQLSpecError):
+ self.rollback()
+
def _execute_cache_hit(
self, sql: str, params: "tuple[Any, ...] | list[Any] | dict[str, Any]", cached: "CachedQuery"
) -> "SQLResult":
diff --git a/sqlspec/adapters/sqlanywhere/pool.py b/sqlspec/adapters/sqlanywhere/pool.py
index db1d4effb..e10f930f8 100644
--- a/sqlspec/adapters/sqlanywhere/pool.py
+++ b/sqlspec/adapters/sqlanywhere/pool.py
@@ -22,14 +22,6 @@
_ADAPTER_NAME = "sqlanywhere"
-def _load_sqlanydb_module() -> Any:
- """Import ``sqlanydb`` lazily or raise ``MissingDependencyError``."""
- try:
- return import_module("sqlanydb")
- except ImportError as exc:
- raise MissingDependencyError(package="sqlanydb", install_package="sqlanywhere") from exc
-
-
class SqlanywhereConnectionPool:
"""Thread-local connection manager for SAP SQL Anywhere over sqlanydb."""
@@ -235,3 +227,11 @@ def _close_thread_connection(self) -> None:
self._thread_local.created_at = 0.0
self._thread_local.last_used = 0.0
self._thread_local.generation = -1
+
+
+def _load_sqlanydb_module() -> Any:
+ """Import ``sqlanydb`` lazily or raise ``MissingDependencyError``."""
+ try:
+ return import_module("sqlanydb")
+ except ImportError as exc:
+ raise MissingDependencyError(package="sqlanydb", install_package="sqlanywhere") from exc
diff --git a/sqlspec/adapters/sybase/pool.py b/sqlspec/adapters/sybase/pool.py
index fe16526f7..7fe52604a 100644
--- a/sqlspec/adapters/sybase/pool.py
+++ b/sqlspec/adapters/sybase/pool.py
@@ -25,14 +25,6 @@
pymssql = PYMSSQL_MODULE
-def _load_pymssql_module() -> Any:
- """Import ``pymssql`` lazily or raise ``MissingDependencyError``."""
- try:
- return import_module("pymssql")
- except ImportError as exc:
- raise MissingDependencyError(package="pymssql", install_package="sybase") from exc
-
-
class SybaseConnectionPool:
"""Thread-local connection manager for Sybase ASE over pymssql (FreeTDS TDS 5.0)."""
@@ -273,3 +265,11 @@ def _close_thread_connection(self) -> None:
del self._thread_local.created_at
if "last_used" in thread_state:
del self._thread_local.last_used
+
+
+def _load_pymssql_module() -> Any:
+ """Import ``pymssql`` lazily or raise ``MissingDependencyError``."""
+ try:
+ return import_module("pymssql")
+ except ImportError as exc:
+ raise MissingDependencyError(package="pymssql", install_package="sybase") from exc
diff --git a/sqlspec/builder/_ddl.py b/sqlspec/builder/_ddl.py
index ae38985bd..95adab5bc 100644
--- a/sqlspec/builder/_ddl.py
+++ b/sqlspec/builder/_ddl.py
@@ -24,6 +24,7 @@
_is_simple_identifier,
_mark_explicit_table_quotes,
_normalize_dialect,
+ _truthy_metadata_flag,
parse_order_expression,
)
from sqlspec.core import SQL
@@ -60,7 +61,6 @@
"RenameTable",
"Truncate",
"compile_table_ddl_results",
- "truthy_metadata_flag",
)
CONSTRAINT_TYPE_PRIMARY_KEY = "PRIMARY KEY"
@@ -282,6 +282,9 @@
_ORACLE_ON_DELETE_ACTIONS = frozenset({FOREIGN_KEY_ACTION_CASCADE, FOREIGN_KEY_ACTION_SET_NULL})
_NO_PARTIAL_INDEX_DIALECTS = frozenset({"duckdb"})
_NO_CASCADE_SEQUENCE_DIALECTS = frozenset({"tsql", "oracle", "spanner", "bigquery", "db2"})
+_COMMENT_ON_DIALECTS = frozenset({"postgres", "oracle", "db2", "duckdb", "snowflake"})
+_INCLUDE_INDEX_DIALECTS = frozenset({"postgres", "tsql", "db2"})
+_STORING_INDEX_DIALECTS = frozenset({"spanner", "spangres"})
_PARENTHESIZED_DEFAULT_DIALECTS = frozenset({"sqlite", "mysql", "spanner"})
_SELF_PARENTHESIZED_GENERATED_DIALECTS = frozenset({"postgres", "mysql", "sqlite"})
_GENERIC_NORMALIZATION_DIALECTS = frozenset({"oracle", "db2", "snowflake"})
@@ -981,7 +984,7 @@ def from_metadata(
dialect=dialect,
fragment_dialect=fragment_dialect,
primary_key=use_inline_pk and col_name == pk_cols[0],
- unique=truthy_metadata_flag(_first_present(row, "is_unique", "unique"))
+ unique=_truthy_metadata_flag(_first_present(row, "is_unique", "unique"))
and col_name not in pk_cols
and (col_name,) not in seen_unique_cols,
fallback_default=default_constraint_map.get(col_name),
@@ -1303,7 +1306,7 @@ def _add_metadata_column(
identity_val = None if is_computed else _resolve_metadata_identity(row)
auto_increment = False if (is_computed or identity_val is not None) else _resolve_metadata_auto_increment(row)
if (identity_val is not None or auto_increment) and (
- norm_dialect == "duckdb" or (norm_dialect == "sqlite" and not primary_key)
+ norm_dialect in {"duckdb", "bigquery"} or (norm_dialect == "sqlite" and not primary_key)
):
warnings.append(
f"Column {col_name!r} is an identity/auto-increment column; {norm_dialect} cannot express it on this "
@@ -1333,6 +1336,9 @@ def _add_metadata_column(
"declare; emitted as text"
)
dtype = sql_type_for_category("text", norm_dialect)
+ col_comment = (
+ _first_non_empty_str(row, "comment", "column_comment", "description") if norm_dialect == "mysql" else None
+ )
self.column(
name=_catalog_name(col_name, dialect, fragment_dialect),
dtype=dtype,
@@ -1341,6 +1347,7 @@ def _add_metadata_column(
primary_key=primary_key,
unique=unique,
auto_increment=auto_increment,
+ comment=col_comment,
check=_first_non_empty_str(row, "check"),
collate=_first_non_empty_str(row, "collate", "collation", "collation_name"),
identity=identity_val,
@@ -1670,9 +1677,9 @@ def _from_metadata_row(
known_columns: "Sequence[str] | None",
warnings: "list[str]",
) -> "CreateIndex | None":
- if truthy_metadata_flag(_first_present(row, "is_primary", "is_primary_key", "primary_key")):
+ if _truthy_metadata_flag(_first_present(row, "is_primary", "is_primary_key", "primary_key")):
return None
- if truthy_metadata_flag(row.get("is_unique_constraint")):
+ if _truthy_metadata_flag(row.get("is_unique_constraint")):
return None
index_name = _first_non_empty_str(row, "index_name", "name")
@@ -1686,6 +1693,13 @@ def _from_metadata_row(
if not resolved_table:
return None
+ norm_dialect = _normalize_dialect(dialect) if dialect else None
+ if norm_dialect == "bigquery":
+ warnings.append(
+ f"BigQuery does not support secondary CREATE INDEX statements; index '{index_name}' omitted"
+ )
+ return None
+
fragment_dialect = source_dialect if source_dialect is not None else dialect
storage_type = str(row.get("type_desc") or "").strip().upper().replace("_", " ")
if storage_type and storage_type not in {"CLUSTERED", "NONCLUSTERED", "HEAP"}:
@@ -1713,8 +1727,8 @@ def _from_metadata_row(
return None
where_text = _first_non_empty_str(row, "where", "filter_definition", "predicate")
has_filter_val = row.get("has_filter")
- is_partial = truthy_metadata_flag(has_filter_val) or truthy_metadata_flag(row.get("is_partial"))
- if where_text and (has_filter_val is None or truthy_metadata_flag(has_filter_val)):
+ is_partial = _truthy_metadata_flag(has_filter_val) or _truthy_metadata_flag(row.get("is_partial"))
+ if where_text and (has_filter_val is None or _truthy_metadata_flag(has_filter_val)):
where_expr = _parse_catalog_fragment(where_text, fragment_dialect)
if where_expr is None:
warnings.append(f"Index {index_name}: predicate could not be parsed ({where_text}); index omitted")
@@ -1723,7 +1737,7 @@ def _from_metadata_row(
warnings.append(f"Index {index_name} is partial but the catalog did not report its predicate; omitted")
return None
- if where_expr is not None and _normalize_dialect(dialect) in _NO_PARTIAL_INDEX_DIALECTS:
+ if where_expr is not None and norm_dialect in _NO_PARTIAL_INDEX_DIALECTS:
warnings.append(f"DuckDB does not support partial indexes; index '{index_name}' omitted")
return None
@@ -1732,12 +1746,21 @@ def _from_metadata_row(
builder.in_schema(_catalog_name(resolved_schema, dialect, fragment_dialect))
builder.on(_catalog_name(resolved_table, dialect, fragment_dialect), *key_exprs)
- if truthy_metadata_flag(_first_present(row, "is_unique", "unique")):
+ if _truthy_metadata_flag(_first_present(row, "is_unique", "unique")):
builder.unique()
inc_cols = _split_metadata_columns(_first_present(row, "included_columns", "include", "include_columns"))
if inc_cols:
- builder.include(*(_catalog_name(col, dialect, fragment_dialect) for col in inc_cols))
+ if (
+ norm_dialect is None
+ or norm_dialect in _INCLUDE_INDEX_DIALECTS
+ or norm_dialect in _STORING_INDEX_DIALECTS
+ ):
+ builder.include(*(_catalog_name(col, dialect, fragment_dialect) for col in inc_cols))
+ else:
+ warnings.append(
+ f"Index {index_name}: covering INCLUDE columns are not supported on {norm_dialect} and were omitted"
+ )
if where_expr is not None:
builder.where(where_expr)
@@ -1748,7 +1771,6 @@ def _from_metadata_row(
using_method = _first_non_empty_str(row, "using", "index_type", "access_method")
if using_method:
using_upper = using_method.strip().upper().replace("_", " ")
- norm_dialect = _normalize_dialect(dialect) if dialect else None
if using_upper not in _MSSQL_STORAGE_INDEX_TYPES and not (
using_upper == "BTREE" and norm_dialect in {"tsql", "mssql", "sqlite"}
):
@@ -1840,6 +1862,15 @@ def _optimize_expression(
opt_columns[idx].set("desc", False)
return optimized
+ def _has_sql_postprocessing(self) -> bool:
+ return bool(self._include_columns)
+
+ def _postprocess_built_sql(self, sql_string: str, dialect: "DialectType" = None) -> str:
+ target_dialect = _normalize_dialect(dialect) if dialect is not None else self.dialect_name
+ if self._include_columns and target_dialect in _STORING_INDEX_DIALECTS:
+ return sql_string.replace(" INCLUDE (", " STORING (", 1)
+ return sql_string
+
class Truncate(DDLBuilder, _CascadeRestrictDDLMixin):
"""Builder for ``TRUNCATE TABLE ... [CASCADE|RESTRICT] [RESTART IDENTITY|CONTINUE IDENTITY]``."""
@@ -2887,9 +2918,23 @@ def compile_table_ddl_results(
)
lossy: list[str] = []
+ normalized_columns = [_normalize_metadata_row(col) for col in columns]
+ fragment_dialect = source_dialect if source_dialect is not None else dialect
+
+ merged_column_comments: dict[str, str] = {}
+ for row in normalized_columns:
+ col_name = _first_non_empty_str(row, "column_name", "name")
+ col_comment = _first_non_empty_str(row, "comment", "column_comment", "description")
+ if col_name and col_comment:
+ merged_column_comments[col_name] = col_comment
+ if column_comments:
+ merged_column_comments.update({
+ col_name: col_comment for col_name, col_comment in column_comments.items() if col_name and col_comment
+ })
+
table_builder = CreateTable.from_metadata(
table_name,
- columns,
+ normalized_columns,
constraints=constraints,
foreign_keys=foreign_keys,
schema=schema,
@@ -2897,15 +2942,25 @@ def compile_table_ddl_results(
source_dialect=source_dialect,
warnings=lossy,
)
+ if dialect_str == "mysql":
+ if table_comment:
+ table_builder.comment(table_comment)
+ if merged_column_comments:
+ by_builder_name = {
+ _catalog_name(col_name, dialect, fragment_dialect): col_comment
+ for col_name, col_comment in merged_column_comments.items()
+ }
+ for col_def in table_builder._columns:
+ col_comment_val = by_builder_name.get(col_def.name) or merged_column_comments.get(col_def.name)
+ if col_comment_val:
+ col_def.comment = col_comment_val
statements: list[str] = [table_builder.build(dialect=dialect).sql.rstrip(";").strip()]
- normalized_columns = [_normalize_metadata_row(col) for col in columns]
known_columns = [
col_name
for col_name in (_first_non_empty_str(row, "column_name", "name") for row in normalized_columns)
if col_name
]
- fragment_dialect = source_dialect if source_dialect is not None else dialect
constraint_names: set[str] = {
c.name
for c in table_builder._constraints
@@ -2924,7 +2979,7 @@ def compile_table_ddl_results(
raw_idx_name = _first_non_empty_str(index_row, "index_name", "name")
if raw_idx_name and _catalog_name(raw_idx_name, dialect, fragment_dialect) in constraint_names:
continue
- idx_builder = CreateIndex.from_metadata(
+ idx_builder = CreateIndex._from_metadata_row(
index_row,
table_name=table_name,
schema=schema or "",
@@ -2950,34 +3005,31 @@ def compile_table_ddl_results(
continue
statements.append(idx_builder.build(dialect=dialect).sql.rstrip(";").strip())
- quoted_table = _catalog_name(table_name, dialect, source_dialect)
- quoted_schema = _catalog_name(schema, dialect, source_dialect) if schema else None
- if table_comment:
- tbl_comment = CommentOn(dialect=dialect).on_table(quoted_table).is_(table_comment)
- if quoted_schema:
- tbl_comment.in_schema(quoted_schema)
- statements.append(tbl_comment.build(dialect=dialect).sql.rstrip(";").strip())
-
- merged_column_comments: dict[str, str] = {}
- for row in normalized_columns:
- col_name = _first_non_empty_str(row, "column_name", "name")
- col_comment = _first_non_empty_str(row, "comment", "column_comment", "description")
- if col_name and col_comment:
- merged_column_comments[col_name] = col_comment
- if column_comments:
- merged_column_comments.update({
- col_name: col_comment for col_name, col_comment in column_comments.items() if col_name and col_comment
- })
-
- for col_name, col_comment in merged_column_comments.items():
- col_comment_builder = (
- CommentOn(dialect=dialect)
- .on_column(quoted_table, _catalog_name(col_name, dialect, source_dialect))
- .is_(col_comment)
+ if dialect_str is None or dialect_str in _COMMENT_ON_DIALECTS:
+ quoted_table = _catalog_name(table_name, dialect, source_dialect)
+ quoted_schema = _catalog_name(schema, dialect, source_dialect) if schema else None
+ if table_comment:
+ tbl_comment = CommentOn(dialect=dialect).on_table(quoted_table).is_(table_comment)
+ if quoted_schema:
+ tbl_comment.in_schema(quoted_schema)
+ statements.append(tbl_comment.build(dialect=dialect).sql.rstrip(";").strip())
+
+ for col_name, col_comment in merged_column_comments.items():
+ col_comment_builder = (
+ CommentOn(dialect=dialect)
+ .on_column(quoted_table, _catalog_name(col_name, dialect, source_dialect))
+ .is_(col_comment)
+ )
+ if quoted_schema:
+ col_comment_builder.in_schema(quoted_schema)
+ statements.append(col_comment_builder.build(dialect=dialect).sql.rstrip(";").strip())
+ elif dialect_str != "mysql":
+ if table_comment:
+ lossy.append(f"Table comment is not supported by {dialect_str}; comment omitted")
+ lossy.extend(
+ f"Column comment on {col_name!r} is not supported by {dialect_str}; comment omitted"
+ for col_name in merged_column_comments
)
- if quoted_schema:
- col_comment_builder.in_schema(quoted_schema)
- statements.append(col_comment_builder.build(dialect=dialect).sql.rstrip(";").strip())
ddl_text = ";\n\n".join(stmt for stmt in statements if stmt) + ";"
return DDLResult(
@@ -2990,26 +3042,10 @@ def compile_table_ddl_results(
)
-def truthy_metadata_flag(value: Any) -> bool:
- """Interpret a catalog flag value (bool, number, or ``yes``/``true``/``1`` text) as a boolean.
-
- Args:
- value: Raw catalog flag value.
-
- Returns:
- Whether the flag is set.
- """
- if isinstance(value, bool):
- return value
- if isinstance(value, (int, float)):
- return value != 0
- if isinstance(value, str):
- return value.strip().lower() in {"1", "true", "t", "yes", "y"}
- return bool(value)
-
-
def _normalize_metadata_row(item: Any) -> dict[str, Any]:
"""Normalize a metadata mapping, ObjectMetadata, or ForeignKeyMetadata into a dict."""
+ if type(item) is dict and "attributes" not in item and "identity" not in item and all(type(k) is str for k in item):
+ return item
if isinstance(item, Mapping):
result: dict[str, Any] = {str(k): v for k, v in item.items()}
nested_attrs = result.get("attributes")
@@ -3120,7 +3156,7 @@ def _resolve_metadata_column_type(row: Mapping[str, Any], dialect: "DialectType
if max_length is not None:
raw_len = _safe_int(max_length, 0)
if raw_len > 0:
- is_mssql_bytes = truthy_metadata_flag(row.get("from_mssql_byte_length")) or (
+ is_mssql_bytes = _truthy_metadata_flag(row.get("from_mssql_byte_length")) or (
char_len is None
and (
norm_dialect in {"tsql", "mssql"}
@@ -3165,13 +3201,17 @@ def _translate_catalog_type(
if norm_source is None or norm_source == norm_target:
return dtype
with contextlib.suppress(ParseError):
- return exp.DataType.build(dtype, dialect=norm_source).sql(dialect=norm_target)
+ built = exp.DataType.build(dtype, dialect=norm_source)
+ rendered = built.sql(dialect=norm_target)
+ if norm_target == "sqlite" and _sqlite_type_affinity(rendered) != _sqlite_type_affinity(dtype):
+ return dtype
+ return rendered
return dtype
def _resolve_metadata_not_null(row: Mapping[str, Any]) -> bool:
if row.get("not_null") is not None:
- return truthy_metadata_flag(row["not_null"])
+ return _truthy_metadata_flag(row["not_null"])
nullable_val = _first_present(row, "is_nullable", "nullable")
if nullable_val is None:
return False
@@ -3195,7 +3235,7 @@ def _resolve_metadata_computed(row: Mapping[str, Any]) -> tuple[str | exp.Expr |
persisted_val = _first_present(row, "is_persisted_computed", "is_stored", "persisted")
persisted: bool | None
if persisted_val is not None:
- persisted = truthy_metadata_flag(persisted_val)
+ persisted = _truthy_metadata_flag(persisted_val)
elif generated_kind in {"v", "virtual"} or "virtual generated" in extra:
persisted = False
elif generated_kind in {"s", "stored"} or "stored generated" in extra:
@@ -3204,9 +3244,9 @@ def _resolve_metadata_computed(row: Mapping[str, Any]) -> tuple[str | exp.Expr |
persisted = None
is_generated = (
- truthy_metadata_flag(row.get("is_computed"))
- or truthy_metadata_flag(row.get("is_generated"))
- or truthy_metadata_flag(row.get("virtual_column"))
+ _truthy_metadata_flag(row.get("is_computed"))
+ or _truthy_metadata_flag(row.get("is_generated"))
+ or _truthy_metadata_flag(row.get("virtual_column"))
or generated_kind in {"s", "v", "stored", "virtual"}
or extra in {"generated", "virtual generated", "stored generated"}
)
@@ -3229,7 +3269,7 @@ def _resolve_metadata_computed(row: Mapping[str, Any]) -> tuple[str | exp.Expr |
def _is_hidden_metadata_column(row: Mapping[str, Any]) -> bool:
"""Return whether a catalog column is a system-generated hidden column (Oracle ``SYS_NC*`` columns)."""
- if not truthy_metadata_flag(row.get("hidden_column")):
+ if not _truthy_metadata_flag(row.get("hidden_column")):
return False
name = _first_non_empty_str(row, "column_name", "name") or ""
return name.upper().startswith("SYS_")
@@ -3249,7 +3289,7 @@ def _resolve_metadata_identity(row: Mapping[str, Any]) -> str | None:
if ident_clean not in {"", "N", "NO", "NONE", "FALSE", "0", "NOT_APPLICABLE"}:
return ident_clean
- if truthy_metadata_flag(row.get("is_identity")) or truthy_metadata_flag(row.get("identity_column")):
+ if _truthy_metadata_flag(row.get("is_identity")) or _truthy_metadata_flag(row.get("identity_column")):
seed = row.get("identity_seed")
inc = row.get("identity_increment")
if seed is not None and inc is not None:
@@ -3263,7 +3303,7 @@ def _resolve_metadata_identity(row: Mapping[str, Any]) -> str | None:
def _resolve_metadata_auto_increment(row: Mapping[str, Any]) -> bool:
- if truthy_metadata_flag(row.get("auto_increment")):
+ if _truthy_metadata_flag(row.get("auto_increment")):
return True
extra = str(row.get("extra") or "").lower()
return "auto_increment" in extra or "autoincrement" in extra
@@ -3422,7 +3462,7 @@ def _extract_metadata_primary_key(
is_pk_val = _first_present(row, "is_primary", "is_primary_key", "primary_key")
if isinstance(is_pk_val, int) and not isinstance(is_pk_val, bool) and is_pk_val > 0:
pk_candidates.append((is_pk_val, idx, col_name))
- elif truthy_metadata_flag(is_pk_val) or str(row.get("column_key") or "").strip().upper() == "PRI":
+ elif _truthy_metadata_flag(is_pk_val) or str(row.get("column_key") or "").strip().upper() == "PRI":
pk_candidates.append((idx + 1, idx, col_name))
pk_candidates.sort(key=lambda item: (item[0], item[1]))
@@ -3471,12 +3511,17 @@ def _supported_foreign_key_actions(
if on_update and on_update != FOREIGN_KEY_ACTION_RESTRICT:
warnings.append(f"Foreign key {label}: Oracle does not support ON UPDATE {on_update}; action omitted")
return resolved_delete, None
- if norm_dialect == "duckdb":
+ if norm_dialect in {"duckdb", "bigquery"}:
+ label_dialect = "BigQuery" if norm_dialect == "bigquery" else "DuckDB"
implicit_actions = {FOREIGN_KEY_ACTION_RESTRICT}
if on_delete and on_delete not in implicit_actions:
- warnings.append(f"Foreign key {label}: DuckDB does not support ON DELETE {on_delete}; action omitted")
+ warnings.append(
+ f"Foreign key {label}: {label_dialect} does not support ON DELETE {on_delete}; action omitted"
+ )
if on_update and on_update not in implicit_actions:
- warnings.append(f"Foreign key {label}: DuckDB does not support ON UPDATE {on_update}; action omitted")
+ warnings.append(
+ f"Foreign key {label}: {label_dialect} does not support ON UPDATE {on_update}; action omitted"
+ )
return None, None
if norm_dialect == "tsql":
return (
@@ -3508,6 +3553,8 @@ def _find_matching_metadata_fk(
ref_table: str,
quoted_cols: Sequence[str],
quoted_ref_cols: Sequence[str],
+ on_delete: str | None = None,
+ on_update: str | None = None,
) -> dict[str, Any] | None:
"""Find an existing merged FK entry that belongs to the same constraint group."""
for existing in merged:
@@ -3525,6 +3572,20 @@ def _find_matching_metadata_fk(
and tuple(existing["ref_columns"]) == tuple(quoted_ref_cols)
):
return existing
+ if (
+ quoted_name is None
+ and fk_group_id is None
+ and existing["name"] is None
+ and existing.get("_fk_group_id") is None
+ and existing["ref_table"] == ref_table
+ and existing["on_delete"] == on_delete
+ and existing["on_update"] == on_update
+ and len(quoted_cols) == 1
+ and len(quoted_ref_cols) == 1
+ and quoted_cols[0] not in existing["columns"]
+ and quoted_ref_cols[0] not in existing["ref_columns"]
+ ):
+ return existing
return None
@@ -3603,8 +3664,8 @@ def _extract_metadata_foreign_keys(
ref_table = f"{_catalog_name(ref_schema, dialect, source_dialect)}.{ref_table}"
on_delete, on_update = _metadata_fk_actions(row, defn)
- deferrable = truthy_metadata_flag(_first_present(row, "deferrable", "is_deferrable"))
- initially_deferred = truthy_metadata_flag(row.get("initially_deferred"))
+ deferrable = _truthy_metadata_flag(_first_present(row, "deferrable", "is_deferrable"))
+ initially_deferred = _truthy_metadata_flag(row.get("initially_deferred"))
quoted_cols = [_catalog_name(col, dialect, source_dialect) for col in cols]
quoted_ref_cols = (
[_catalog_name(col, dialect, source_dialect) for col in ref_cols] if ref_is_catalog_name else ref_cols
@@ -3618,6 +3679,8 @@ def _extract_metadata_foreign_keys(
ref_table=ref_table,
quoted_cols=quoted_cols,
quoted_ref_cols=quoted_ref_cols,
+ on_delete=on_delete,
+ on_update=on_update,
)
if matched_existing is not None:
@@ -3748,7 +3811,7 @@ def _metadata_index_descending_flags(row: Mapping[str, Any], count: int) -> list
else [raw_orders]
)
flags = [
- str(value).strip().upper() in {"D", "DESC"} or (not isinstance(value, str) and truthy_metadata_flag(value))
+ str(value).strip().upper() in {"D", "DESC"} or (not isinstance(value, str) and _truthy_metadata_flag(value))
for value in values
]
return (flags + [False] * count)[:count]
@@ -3768,7 +3831,12 @@ def _explicit_or_bare_identifier(
"""Return an identifier for a raw catalog name, explicitly quoted when its spelling requires it."""
if not _catalog_name_needs_quotes(name, dialect, source_dialect):
return exp.to_identifier(name)
- return _explicit_identifier(name)
+ norm_dialect = _normalize_dialect(dialect)
+ case_dialect = norm_dialect if source_dialect is None else _normalize_dialect(source_dialect)
+ target_name = name
+ if _BARE_IDENTIFIER_RE.fullmatch(name) and _fold_identifier(name, case_dialect) == name:
+ target_name = _fold_identifier(name, norm_dialect)
+ return _explicit_identifier(target_name)
def _explicit_identifier(name: str) -> exp.Identifier:
@@ -3922,7 +3990,12 @@ def _catalog_name(name: str, dialect: "DialectType | None", source_dialect: "Dia
"""
if not _catalog_name_needs_quotes(name, dialect, source_dialect):
return name
- return exp.Identifier(this=name, quoted=True).sql(dialect=_normalize_dialect(dialect))
+ norm_dialect = _normalize_dialect(dialect)
+ case_dialect = norm_dialect if source_dialect is None else _normalize_dialect(source_dialect)
+ target_name = name
+ if _BARE_IDENTIFIER_RE.fullmatch(name) and _fold_identifier(name, case_dialect) == name:
+ target_name = _fold_identifier(name, norm_dialect)
+ return exp.Identifier(this=target_name, quoted=True).sql(dialect=norm_dialect)
def _fold_identifier(name: str, dialect: str | None) -> str:
@@ -4262,10 +4335,11 @@ def _dialect_foreign_key_actions(
on_update = constraint.on_update
norm_dialect = _normalize_dialect(dialect) if dialect else None
implicit_actions = {FOREIGN_KEY_ACTION_NO_ACTION, FOREIGN_KEY_ACTION_RESTRICT}
- if norm_dialect == "duckdb":
+ if norm_dialect in {"duckdb", "bigquery"}:
+ label_dialect = "BigQuery" if norm_dialect == "bigquery" else "DuckDB"
for action_type, action in (("ON DELETE", on_delete), ("ON UPDATE", on_update)):
if action and action not in implicit_actions:
- msg = f"DuckDB does not support {action_type} {action} on foreign keys"
+ msg = f"{label_dialect} does not support {action_type} {action} on foreign keys"
raise SQLBuilderError(msg)
return None, None
if norm_dialect != "oracle":
diff --git a/sqlspec/builder/_parsing_utils.py b/sqlspec/builder/_parsing_utils.py
index c53c9af6a..c1d2e7e7a 100644
--- a/sqlspec/builder/_parsing_utils.py
+++ b/sqlspec/builder/_parsing_utils.py
@@ -80,6 +80,15 @@
"where",
})
_PARAMETER_VALIDATOR = ParameterValidator()
+_DIALECT_ALIASES: Final[dict[str, str]] = {
+ "ase": "sybase",
+ "cockroach": "postgres",
+ "cockroachdb": "postgres",
+ "mariadb": "mysql",
+ "mssql": "tsql",
+ "postgresql": "postgres",
+ "sa": "sqlanywhere",
+}
def extract_column_name(column: str | exp.Column) -> str:
@@ -442,19 +451,30 @@ def _normalize_dialect(dialect: "DialectType | None") -> str | None:
name = dialect.__name__.lower()
else:
name = type(dialect).__name__.lower()
- resolved = {
- "mssql": "tsql",
- "mariadb": "mysql",
- "cockroachdb": "postgres",
- "postgresql": "postgres",
- "ase": "sybase",
- "sa": "sqlanywhere",
- }.get(name, name)
+ resolved = _DIALECT_ALIASES.get(name, name)
if resolved in {"sybase", "sqlanywhere"} and resolved not in Dialect.classes:
importlib.import_module("sqlspec.dialects.sybase")
return resolved
+def _truthy_metadata_flag(value: Any) -> bool:
+ """Interpret a catalog flag value (bool, number, or ``yes``/``true``/``1`` text) as a boolean.
+
+ Args:
+ value: Raw catalog flag value.
+
+ Returns:
+ Whether the flag is set.
+ """
+ if isinstance(value, bool):
+ return value
+ if isinstance(value, (int, float)):
+ return value != 0
+ if isinstance(value, str):
+ return value.strip().lower() in {"1", "true", "t", "yes", "y"}
+ return bool(value)
+
+
def _merge_sql_parameters(sql_obj: Any, builder: Any) -> dict[str, str]:
"""Merge parameters from SQL object into builder and return renamed placeholder mapping.
diff --git a/sqlspec/cli.py b/sqlspec/cli.py
index 4f91eb7b5..7db0d7de5 100644
--- a/sqlspec/cli.py
+++ b/sqlspec/cli.py
@@ -9,6 +9,7 @@
import rich_click as click
from rich import get_console
+from rich.console import Console
from rich.markup import escape
from rich.prompt import Confirm, Prompt
from rich.table import Table
@@ -436,6 +437,7 @@ def _run_multi_config_operation(
progress_message: "Callable[[str], str] | None",
success_message: "Callable[[str], str] | None",
failure_message: "Callable[[str, Exception], str]",
+ output_console: "Console | None" = None,
) -> None:
"""Dispatch a migration operation across multiple configs.
@@ -450,21 +452,23 @@ def _run_multi_config_operation(
progress_message: Builds the per-config progress line, or None to skip.
success_message: Builds the per-config success line, or None to skip.
failure_message: Builds the per-config failure line.
+ output_console: Optional Console override for progress and error output.
"""
+ active_console = output_console if output_console is not None else console
sync_configs, async_configs = _partition_configs_by_async(configs_to_process)
errors: list[tuple[str, Exception]] = []
for config_name, config in sync_configs:
if progress_message is not None and should_echo(config):
- console.print(progress_message(config_name))
+ active_console.print(progress_message(config_name))
try:
sync_apply(config)
if success_message is not None and should_echo(config):
- console.print(success_message(config_name))
+ active_console.print(success_message(config_name))
except Exception as e:
errors.append((config_name, e))
if should_echo(config):
- console.print(failure_message(config_name, e))
+ active_console.print(failure_message(config_name, e))
if async_configs:
@@ -472,15 +476,15 @@ async def _run_async_configs() -> None:
for config_name, config in async_configs:
had_pool = getattr(config, "connection_instance", None) is not None
if progress_message is not None and should_echo(config):
- console.print(progress_message(config_name))
+ active_console.print(progress_message(config_name))
try:
await async_apply(config)
if success_message is not None and should_echo(config):
- console.print(success_message(config_name))
+ active_console.print(success_message(config_name))
except Exception as e:
errors.append((config_name, e))
if should_echo(config):
- console.print(failure_message(config_name, e))
+ active_console.print(failure_message(config_name, e))
finally:
await _close_cli_created_pool(config, had_pool=had_pool)
@@ -499,6 +503,7 @@ def process_multiple_configs(
dry_run: bool,
operation_name: str,
echo: bool = True,
+ output_console: "Console | None" = None,
) -> "list[tuple[str, Any]] | None":
"""Process configuration selection for multi-config operations.
@@ -513,10 +518,12 @@ def process_multiple_configs(
dry_run: Whether this is a dry run.
operation_name: Name of the operation for display.
echo: Whether to echo output to the console.
+ output_console: Optional Console override for status messages.
Returns:
List of (config_name, config) tuples to process, or None for single config mode.
"""
+ active_console = output_console if output_console is not None else console
if bind_key and not include and not exclude:
return None
@@ -530,14 +537,16 @@ def process_multiple_configs(
if not configs_to_process:
if echo:
- console.print("[yellow]No configurations match the specified criteria.[/]")
+ active_console.print("[yellow]No configurations match the specified criteria.[/]")
return []
if dry_run:
if echo:
- console.print(f"[blue]Dry run: Would {operation_name} {len(configs_to_process)} configuration(s)[/]")
+ active_console.print(
+ f"[blue]Dry run: Would {operation_name} {len(configs_to_process)} configuration(s)[/]"
+ )
for config_name, _ in configs_to_process:
- console.print(f" • {config_name}")
+ active_console.print(f" • {config_name}")
return []
return configs_to_process
@@ -840,10 +849,7 @@ def init_sqlspec( # pyright: ignore[reportUnusedFunction]
target_directory = (
str(migration_config_dict.get("script_location", "migrations")) if directory is None else directory
)
- migration_commands: SyncMigrationCommands[Any] = cast(
- "SyncMigrationCommands[Any]",
- migration_commands_module.create_migration_commands(config=cast("Any", config)),
- )
+ migration_commands = migration_commands_module.create_migration_commands(config=cast("Any", config))
migration_commands.init(directory=target_directory, package=package)
if async_configs:
@@ -905,6 +911,14 @@ def create_revision(
"""Create a new database revision."""
ctx = _ensure_click_context()
+ if (
+ from_data is not None
+ and not target_table
+ and not migration_commands_module.MigrationCommandsCore._looks_like_data_path(from_data)
+ ):
+ msg = "--target-table is required when --from-data specifies a source table name."
+ raise click.ClickException(msg)
+
console.rule("[yellow]Creating new migration revision[/]", align="left")
message_text = message
if message_text is None:
@@ -1011,6 +1025,15 @@ async def _async_fix(config: Any) -> None:
"ranges that cannot be folded faithfully (ddl), or reflect the live database schema (schema)."
),
)
+ @click.option(
+ "--single-file/--split",
+ "single_file",
+ default=None,
+ help=(
+ "Force a single squashed file (--single-file) or split when file types or "
+ "execution directives change (--split)."
+ ),
+ )
def squash_migrations(
bind_key: str | None,
version_range: str | None,
@@ -1026,6 +1049,7 @@ def squash_migrations(
allow_gaps: bool,
output_format: str,
strategy: str,
+ single_file: bool | None,
) -> None:
"""Squash multiple sequential migrations into a single file.
@@ -1055,6 +1079,8 @@ def squash_migrations(
"output_format": output_format.lower(),
"strategy": strategy.lower(),
}
+ if single_file is not None:
+ squash_kwargs["single_file"] = single_file
def _squash_for_config(config: Any) -> None:
_dispatch_migration_call(config, "squash", **squash_kwargs)
@@ -1159,12 +1185,26 @@ async def _async_dump(config: Any) -> None:
)
)
+ stderr_console = Console(stderr=True)
configs_to_process = process_multiple_configs(
- ctx, bind_key, include, exclude, dry_run=False, operation_name="dump schema", echo=echo_value
+ ctx,
+ bind_key,
+ include,
+ exclude,
+ dry_run=False,
+ operation_name="dump schema",
+ echo=echo_value,
+ output_console=stderr_console,
)
if configs_to_process is not None:
if not configs_to_process:
return
+ if output is not None:
+ msg = (
+ "Cannot use a single --output file when dumping schema across multiple configurations; "
+ "specify --bind-key or omit --output."
+ )
+ raise click.ClickException(msg)
_run_multi_config_operation(
configs_to_process,
sync_apply=_dump_for_config,
@@ -1173,6 +1213,7 @@ async def _async_dump(config: Any) -> None:
progress_message=lambda name: f"[blue]Dumping schema for configuration: {name}[/]",
success_message=None,
failure_message=lambda name, e: f"[red]âś— Failed to dump schema for {name}: {e}[/]",
+ output_console=stderr_console,
)
return
diff --git a/sqlspec/data_dictionary/dialects/duckdb/sql/ddl.sql b/sqlspec/data_dictionary/dialects/duckdb/sql/ddl.sql
index 1bfe1b59c..d5bff8440 100644
--- a/sqlspec/data_dictionary/dialects/duckdb/sql/ddl.sql
+++ b/sqlspec/data_dictionary/dialects/duckdb/sql/ddl.sql
@@ -1,21 +1,27 @@
-- name: by_object
-- dialect: duckdb
-WITH ddl_objects AS (
+WITH fk_tables AS (
+ SELECT DISTINCT
+ database_name,
+ schema_name,
+ table_name
+ FROM duckdb_constraints()
+ WHERE database_name = current_database()
+ AND constraint_type = 'FOREIGN KEY'
+),
+ddl_objects AS (
SELECT
t.schema_name,
t.table_name AS object_name,
CASE WHEN t.temporary THEN 'temporary_table' ELSE 'table' END AS object_type,
t.table_name AS table_name,
t.sql,
- EXISTS (
- SELECT 1
- FROM duckdb_constraints() AS c
- WHERE c.database_name = t.database_name
- AND c.schema_name = t.schema_name
- AND c.table_name = t.table_name
- AND c.constraint_type = 'FOREIGN KEY'
- ) AS has_foreign_keys
+ fk.table_name IS NOT NULL AS has_foreign_keys
FROM duckdb_tables() AS t
+ LEFT JOIN fk_tables AS fk
+ ON fk.database_name = t.database_name
+ AND fk.schema_name = t.schema_name
+ AND fk.table_name = t.table_name
WHERE NOT t.internal
AND t.database_name = current_database()
AND t.sql IS NOT NULL
@@ -75,16 +81,14 @@ WITH ddl_objects AS (
list_transform(labels, lambda x: '''' || replace(x, '''', '''''') || ''''),
', '
) || ');'
- WHEN logical_type NOT IN ('STRUCT', 'LIST', 'ARRAY', 'MAP', 'UNION') THEN
- 'CREATE TYPE '
- || CASE WHEN schema_name = 'main' THEN '' ELSE '"' || replace(schema_name, '"', '""') || '".' END
- || '"' || replace(type_name, '"', '""') || '" AS ' || logical_type || ';'
ELSE NULL
END AS sql,
FALSE AS has_foreign_keys
FROM duckdb_types()
WHERE NOT internal
AND database_name = current_database()
+ AND logical_type = 'ENUM'
+ AND labels IS NOT NULL
)
SELECT
schema_name,
@@ -97,27 +101,34 @@ SELECT
FROM ddl_objects
WHERE schema_name = COALESCE(:schema_name, current_schema())
AND object_name = :object_name
+ AND sql IS NOT NULL
AND (:object_type IS NULL OR object_type = LOWER(:object_type))
ORDER BY schema_name, object_type, object_name;
-- name: by_schema
-- dialect: duckdb
-WITH ddl_objects AS (
+WITH fk_tables AS (
+ SELECT DISTINCT
+ database_name,
+ schema_name,
+ table_name
+ FROM duckdb_constraints()
+ WHERE database_name = current_database()
+ AND constraint_type = 'FOREIGN KEY'
+),
+ddl_objects AS (
SELECT
t.schema_name,
t.table_name AS object_name,
CASE WHEN t.temporary THEN 'temporary_table' ELSE 'table' END AS object_type,
t.table_name AS table_name,
t.sql,
- EXISTS (
- SELECT 1
- FROM duckdb_constraints() AS c
- WHERE c.database_name = t.database_name
- AND c.schema_name = t.schema_name
- AND c.table_name = t.table_name
- AND c.constraint_type = 'FOREIGN KEY'
- ) AS has_foreign_keys
+ fk.table_name IS NOT NULL AS has_foreign_keys
FROM duckdb_tables() AS t
+ LEFT JOIN fk_tables AS fk
+ ON fk.database_name = t.database_name
+ AND fk.schema_name = t.schema_name
+ AND fk.table_name = t.table_name
WHERE NOT t.internal
AND t.database_name = current_database()
AND t.sql IS NOT NULL
@@ -177,16 +188,14 @@ WITH ddl_objects AS (
list_transform(labels, lambda x: '''' || replace(x, '''', '''''') || ''''),
', '
) || ');'
- WHEN logical_type NOT IN ('STRUCT', 'LIST', 'ARRAY', 'MAP', 'UNION') THEN
- 'CREATE TYPE '
- || CASE WHEN schema_name = 'main' THEN '' ELSE '"' || replace(schema_name, '"', '""') || '".' END
- || '"' || replace(type_name, '"', '""') || '" AS ' || logical_type || ';'
ELSE NULL
END AS sql,
FALSE AS has_foreign_keys
FROM duckdb_types()
WHERE NOT internal
AND database_name = current_database()
+ AND logical_type = 'ENUM'
+ AND labels IS NOT NULL
)
SELECT
schema_name,
@@ -198,6 +207,7 @@ SELECT
'native' AS fidelity
FROM ddl_objects
WHERE schema_name = COALESCE(:schema_name, current_schema())
+ AND sql IS NOT NULL
ORDER BY
CASE object_type
WHEN 'type' THEN 1
diff --git a/sqlspec/data_dictionary/dialects/mssql/sql/columns.sql b/sqlspec/data_dictionary/dialects/mssql/sql/columns.sql
index c8f6ea353..2007c0d4e 100644
--- a/sqlspec/data_dictionary/dialects/mssql/sql/columns.sql
+++ b/sqlspec/data_dictionary/dialects/mssql/sql/columns.sql
@@ -40,8 +40,7 @@ SELECT
CAST(c.is_masked AS BIT) AS is_masked,
c.encryption_type_desc,
c.encryption_algorithm_name,
- c.generated_always_type_desc,
- CAST(CASE WHEN cc.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_generated,
+ CAST(CASE WHEN cc.object_id IS NOT NULL OR c.generated_always_type <> 0 THEN 1 ELSE 0 END AS BIT) AS is_generated,
cc.definition AS generation_expression,
CAST(cc.is_persisted AS BIT) AS is_persisted_computed,
CAST(CASE WHEN ic.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_identity,
@@ -101,8 +100,7 @@ SELECT
CAST(c.is_masked AS BIT) AS is_masked,
c.encryption_type_desc,
c.encryption_algorithm_name,
- c.generated_always_type_desc,
- CAST(CASE WHEN cc.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_generated,
+ CAST(CASE WHEN cc.object_id IS NOT NULL OR c.generated_always_type <> 0 THEN 1 ELSE 0 END AS BIT) AS is_generated,
cc.definition AS generation_expression,
CAST(cc.is_persisted AS BIT) AS is_persisted_computed,
CAST(CASE WHEN ic.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_identity,
diff --git a/sqlspec/data_dictionary/dialects/mssql/sql/ddl.sql b/sqlspec/data_dictionary/dialects/mssql/sql/ddl.sql
index c0b431677..af055ba00 100644
--- a/sqlspec/data_dictionary/dialects/mssql/sql/ddl.sql
+++ b/sqlspec/data_dictionary/dialects/mssql/sql/ddl.sql
@@ -1,6 +1,26 @@
-- name: index_inputs_by_table
-- dialect: mssql
/* sqlspec:mssql:ddl_index_inputs */
+WITH index_key_columns AS (
+ SELECT
+ ic.object_id,
+ ic.index_id,
+ STRING_AGG(CAST(c.name AS NVARCHAR(MAX)), ',') WITHIN GROUP (ORDER BY ic.key_ordinal) AS columns
+ FROM sys.index_columns AS ic
+ INNER JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
+ WHERE ic.is_included_column = 0
+ GROUP BY ic.object_id, ic.index_id
+),
+index_included_columns AS (
+ SELECT
+ ic.object_id,
+ ic.index_id,
+ STRING_AGG(CAST(c.name AS NVARCHAR(MAX)), ',') WITHIN GROUP (ORDER BY ic.index_column_id) AS included_columns
+ FROM sys.index_columns AS ic
+ INNER JOIN sys.columns AS c ON ic.object_id = c.object_id AND ic.column_id = c.column_id
+ WHERE ic.is_included_column = 1
+ GROUP BY ic.object_id, ic.index_id
+)
SELECT
s.name AS schema_name,
t.name AS table_name,
@@ -9,29 +29,13 @@ SELECT
CAST(i.is_unique AS BIT) AS is_unique,
CAST(i.has_filter AS BIT) AS has_filter,
i.filter_definition,
- STUFF((
- SELECT ',' + c2.name
- FROM sys.index_columns AS ic2
- INNER JOIN sys.columns AS c2 ON ic2.object_id = c2.object_id AND ic2.column_id = c2.column_id
- WHERE ic2.object_id = i.object_id
- AND ic2.index_id = i.index_id
- AND ic2.is_included_column = 0
- ORDER BY ic2.key_ordinal
- FOR XML PATH(''), TYPE
- ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS columns,
- STUFF((
- SELECT ',' + c3.name
- FROM sys.index_columns AS ic3
- INNER JOIN sys.columns AS c3 ON ic3.object_id = c3.object_id AND ic3.column_id = c3.column_id
- WHERE ic3.object_id = i.object_id
- AND ic3.index_id = i.index_id
- AND ic3.is_included_column = 1
- ORDER BY ic3.index_column_id
- FOR XML PATH(''), TYPE
- ).value('.', 'NVARCHAR(MAX)'), 1, 1, '') AS included_columns
+ kc.columns AS columns,
+ inc.included_columns AS included_columns
FROM sys.indexes AS i
INNER JOIN sys.tables AS t ON i.object_id = t.object_id
INNER JOIN sys.schemas AS s ON t.schema_id = s.schema_id
+LEFT JOIN index_key_columns AS kc ON kc.object_id = i.object_id AND kc.index_id = i.index_id
+LEFT JOIN index_included_columns AS inc ON inc.object_id = i.object_id AND inc.index_id = i.index_id
WHERE s.name = :schema_name
AND t.name = :table_name
AND i.name IS NOT NULL
@@ -42,6 +46,19 @@ ORDER BY i.name;
-- name: table_inputs_by_table
-- dialect: mssql
/* sqlspec:mssql:ddl_table_inputs */
+WITH primary_key_columns AS (
+ SELECT
+ pk_col.object_id,
+ pk_col.column_id,
+ pk.name AS primary_key_name,
+ pk_col.key_ordinal AS primary_key_ordinal
+ FROM sys.indexes AS pk
+ INNER JOIN sys.index_columns AS pk_col
+ ON pk.object_id = pk_col.object_id
+ AND pk.index_id = pk_col.index_id
+ WHERE pk.is_primary_key = 1
+ AND pk_col.is_included_column = 0
+)
SELECT
s.name AS schema_name,
tab.name AS table_name,
@@ -58,22 +75,15 @@ SELECT
CAST(CASE WHEN ic.object_id IS NULL THEN 0 ELSE 1 END AS BIT) AS is_identity,
CONVERT(BIGINT, ic.seed_value) AS identity_seed,
CONVERT(BIGINT, ic.increment_value) AS identity_increment,
- pk.name AS primary_key_name,
- pk_col.key_ordinal AS primary_key_ordinal
+ pk.primary_key_name AS primary_key_name,
+ pk.primary_key_ordinal AS primary_key_ordinal
FROM sys.columns AS c
INNER JOIN sys.types AS typ ON c.user_type_id = typ.user_type_id
INNER JOIN sys.tables AS tab ON c.object_id = tab.object_id
INNER JOIN sys.schemas AS s ON tab.schema_id = s.schema_id
LEFT JOIN sys.computed_columns AS cc ON c.object_id = cc.object_id AND c.column_id = cc.column_id
LEFT JOIN sys.identity_columns AS ic ON c.object_id = ic.object_id AND c.column_id = ic.column_id
-LEFT JOIN sys.index_columns AS pk_col
- ON c.object_id = pk_col.object_id
- AND c.column_id = pk_col.column_id
- AND pk_col.is_included_column = 0
-LEFT JOIN sys.indexes AS pk
- ON pk_col.object_id = pk.object_id
- AND pk_col.index_id = pk.index_id
- AND pk.is_primary_key = 1
+LEFT JOIN primary_key_columns AS pk ON c.object_id = pk.object_id AND c.column_id = pk.column_id
WHERE s.name = :schema_name
AND tab.name = :table_name
ORDER BY c.column_id;
diff --git a/sqlspec/data_dictionary/dialects/postgres/sql/ddl.sql b/sqlspec/data_dictionary/dialects/postgres/sql/ddl.sql
index c7c67504a..7119e197f 100644
--- a/sqlspec/data_dictionary/dialects/postgres/sql/ddl.sql
+++ b/sqlspec/data_dictionary/dialects/postgres/sql/ddl.sql
@@ -1,5 +1,12 @@
-- name: by_object
-- dialect: postgres
+WITH extension_deps AS (
+ SELECT DISTINCT
+ classid,
+ objid
+ FROM pg_catalog.pg_depend
+ WHERE deptype = 'e'
+)
SELECT
n.nspname::text AS schema_name,
c.relname::text AS object_name,
@@ -15,10 +22,12 @@ SELECT
WHEN 'S' THEN 'generated'
ELSE 'unsupported'
END AS fidelity,
- dep.refobjid IS NOT NULL AS is_extension_owned
+ dep.objid IS NOT NULL AS is_extension_owned
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
-LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = c.oid AND dep.deptype = 'e'
+LEFT JOIN extension_deps dep
+ ON dep.classid = 'pg_catalog.pg_class'::regclass
+ AND dep.objid = c.oid
WHERE n.nspname = :schema_name
AND c.relname = :object_name
AND (:object_type::text IS NULL OR c.relkind = CASE :object_type
@@ -34,10 +43,12 @@ SELECT
p.proname::text AS object_name,
pg_catalog.pg_get_functiondef(p.oid)::text AS ddl,
'native'::text AS fidelity,
- dep.refobjid IS NOT NULL AS is_extension_owned
+ dep.objid IS NOT NULL AS is_extension_owned
FROM pg_catalog.pg_proc p
JOIN pg_catalog.pg_namespace n ON n.oid = p.pronamespace
-LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = p.oid AND dep.deptype = 'e'
+LEFT JOIN extension_deps dep
+ ON dep.classid = 'pg_catalog.pg_proc'::regclass
+ AND dep.objid = p.oid
WHERE n.nspname = :schema_name
AND p.proname = :object_name
AND (:object_type::text IS NULL OR :object_type IN ('routine', 'function', 'procedure'))
diff --git a/sqlspec/data_dictionary/dialects/postgres/sql/sequences.sql b/sqlspec/data_dictionary/dialects/postgres/sql/sequences.sql
index 7c49142f8..fb247e6c2 100644
--- a/sqlspec/data_dictionary/dialects/postgres/sql/sequences.sql
+++ b/sqlspec/data_dictionary/dialects/postgres/sql/sequences.sql
@@ -1,5 +1,17 @@
-- name: by_schema
-- dialect: postgres
+WITH extension_deps AS (
+ SELECT DISTINCT objid
+ FROM pg_catalog.pg_depend
+ WHERE classid = 'pg_catalog.pg_class'::regclass
+ AND deptype = 'e'
+),
+identity_deps AS (
+ SELECT DISTINCT objid
+ FROM pg_catalog.pg_depend
+ WHERE classid = 'pg_catalog.pg_class'::regclass
+ AND deptype = 'i'
+)
SELECT
n.nspname::text AS schema_name,
c.relname::text AS sequence_name,
@@ -9,17 +21,12 @@ SELECT
s.seqincrement AS increment,
s.seqcycle AS cycles,
pg_catalog.obj_description(c.oid, 'pg_class')::text AS comment,
- dep.refobjid IS NOT NULL AS is_extension_owned,
- EXISTS (
- SELECT 1
- FROM pg_catalog.pg_depend AS identity_dep
- WHERE identity_dep.classid = 'pg_catalog.pg_class'::regclass
- AND identity_dep.objid = c.oid
- AND identity_dep.deptype = 'i'
- ) AS is_identity_owned
+ ext_dep.objid IS NOT NULL AS is_extension_owned,
+ id_dep.objid IS NOT NULL AS is_identity_owned
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
JOIN pg_catalog.pg_sequence s ON s.seqrelid = c.oid
-LEFT JOIN pg_catalog.pg_depend dep ON dep.objid = c.oid AND dep.deptype = 'e'
+LEFT JOIN extension_deps AS ext_dep ON ext_dep.objid = c.oid
+LEFT JOIN identity_deps AS id_dep ON id_dep.objid = c.oid
WHERE n.nspname = :schema_name
ORDER BY n.nspname, c.relname;
diff --git a/sqlspec/data_dictionary/dialects/spanner/sql/columns.sql b/sqlspec/data_dictionary/dialects/spanner/sql/columns.sql
index 37537dbf3..ecc5e8749 100644
--- a/sqlspec/data_dictionary/dialects/spanner/sql/columns.sql
+++ b/sqlspec/data_dictionary/dialects/spanner/sql/columns.sql
@@ -1,23 +1,58 @@
-- name: by_table
-- dialect: spanner
+WITH pk_cols AS (
+ SELECT
+ ic.table_schema,
+ ic.table_name,
+ ic.column_name
+ FROM information_schema.index_columns AS ic
+ WHERE ic.index_name = 'PRIMARY_KEY'
+ AND ic.table_name = :table_name
+ AND (CAST(:schema_name AS STRING) IS NULL OR ic.table_schema = :schema_name)
+)
SELECT
- column_name,
- spanner_type AS data_type,
- is_nullable,
- column_default
-FROM information_schema.columns
-WHERE table_name = :table_name
- AND (CAST(:schema_name AS STRING) IS NULL OR table_schema = :schema_name)
-ORDER BY ordinal_position;
+ c.column_name,
+ c.spanner_type AS data_type,
+ c.is_nullable,
+ c.column_default,
+ CASE WHEN pk_cols.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS is_primary,
+ CASE WHEN c.is_identity = 'YES' THEN TRUE ELSE FALSE END AS is_identity,
+ CASE WHEN c.is_generated = 'ALWAYS' OR c.generation_expression IS NOT NULL THEN TRUE ELSE FALSE END AS is_generated,
+ c.generation_expression
+FROM information_schema.columns AS c
+LEFT JOIN pk_cols
+ ON c.table_schema = pk_cols.table_schema
+ AND c.table_name = pk_cols.table_name
+ AND c.column_name = pk_cols.column_name
+WHERE c.table_name = :table_name
+ AND (CAST(:schema_name AS STRING) IS NULL OR c.table_schema = :schema_name)
+ORDER BY c.ordinal_position;
-- name: by_schema
-- dialect: spanner
+WITH pk_cols AS (
+ SELECT
+ ic.table_schema,
+ ic.table_name,
+ ic.column_name
+ FROM information_schema.index_columns AS ic
+ WHERE ic.index_name = 'PRIMARY_KEY'
+ AND (CAST(:schema_name AS STRING) IS NULL OR ic.table_schema = :schema_name)
+)
SELECT
- table_name,
- column_name,
- spanner_type AS data_type,
- is_nullable,
- column_default
-FROM information_schema.columns
-WHERE (CAST(:schema_name AS STRING) IS NULL OR table_schema = :schema_name)
-ORDER BY table_name, ordinal_position;
+ c.table_name,
+ c.column_name,
+ c.spanner_type AS data_type,
+ c.is_nullable,
+ c.column_default,
+ CASE WHEN pk_cols.column_name IS NOT NULL THEN TRUE ELSE FALSE END AS is_primary,
+ CASE WHEN c.is_identity = 'YES' THEN TRUE ELSE FALSE END AS is_identity,
+ CASE WHEN c.is_generated = 'ALWAYS' OR c.generation_expression IS NOT NULL THEN TRUE ELSE FALSE END AS is_generated,
+ c.generation_expression
+FROM information_schema.columns AS c
+LEFT JOIN pk_cols
+ ON c.table_schema = pk_cols.table_schema
+ AND c.table_name = pk_cols.table_name
+ AND c.column_name = pk_cols.column_name
+WHERE (CAST(:schema_name AS STRING) IS NULL OR c.table_schema = :schema_name)
+ORDER BY c.table_name, c.ordinal_position;
diff --git a/sqlspec/data_dictionary/dialects/sqlite/sql/columns.sql b/sqlspec/data_dictionary/dialects/sqlite/sql/columns.sql
index 662662445..dcae8f703 100644
--- a/sqlspec/data_dictionary/dialects/sqlite/sql/columns.sql
+++ b/sqlspec/data_dictionary/dialects/sqlite/sql/columns.sql
@@ -9,6 +9,10 @@ SELECT
ti.dflt_value AS column_default,
ti.cid + 1 AS ordinal_position,
ti.pk AS is_primary,
+ CASE
+ WHEN ti.pk = 1 AND UPPER(ti.type) = 'INTEGER' AND UPPER(sm.sql) LIKE '%AUTOINCREMENT%' THEN 1
+ ELSE 0
+ END AS is_identity,
CASE
WHEN ti.hidden = 1 THEN 'hidden'
WHEN ti.hidden = 2 THEN 'virtual generated'
@@ -37,6 +41,10 @@ SELECT
ti.dflt_value AS column_default,
ti.cid + 1 AS ordinal_position,
ti.pk AS is_primary,
+ CASE
+ WHEN ti.pk = 1 AND UPPER(ti.type) = 'INTEGER' AND UPPER(sm.sql) LIKE '%AUTOINCREMENT%' THEN 1
+ ELSE 0
+ END AS is_identity,
CASE
WHEN ti.hidden = 1 THEN 'hidden'
WHEN ti.hidden = 2 THEN 'virtual generated'
diff --git a/sqlspec/data_dictionary/dialects/sqlite/sql/foreign_keys.sql b/sqlspec/data_dictionary/dialects/sqlite/sql/foreign_keys.sql
index 6c7a9362f..f01a4d7a0 100644
--- a/sqlspec/data_dictionary/dialects/sqlite/sql/foreign_keys.sql
+++ b/sqlspec/data_dictionary/dialects/sqlite/sql/foreign_keys.sql
@@ -11,6 +11,7 @@ SELECT
WHEN COUNT(*) OVER (PARTITION BY tl.name, fk.id) > 1 THEN tl.name || '_fk_' || fk.id
ELSE NULL
END AS constraint_name,
+ fk.id AS id,
fk.seq AS ordinal_position,
fk.on_update,
fk.on_delete,
@@ -35,6 +36,7 @@ SELECT
WHEN COUNT(*) OVER (PARTITION BY fk.id) > 1 THEN :table_name || '_fk_' || fk.id
ELSE NULL
END AS constraint_name,
+ fk.id AS id,
fk.seq AS ordinal_position,
fk.on_update,
fk.on_delete,
diff --git a/sqlspec/driver/_common.py b/sqlspec/driver/_common.py
index 6e001a091..647dce1b0 100644
--- a/sqlspec/driver/_common.py
+++ b/sqlspec/driver/_common.py
@@ -7,6 +7,7 @@
import contextlib
import hashlib
+import inspect
import logging
import re
from collections.abc import Mapping
@@ -23,6 +24,7 @@
from sqlspec.builder import QueryBuilder
from sqlspec.builder._ddl import compile_table_ddl_results
+from sqlspec.builder._parsing_utils import _normalize_dialect
from sqlspec.core import (
SQL,
CachedStatement,
@@ -630,7 +632,22 @@ def resolve_ddl_dialect(self, driver: Any) -> "str | None":
Returns:
SQLGlot dialect name, or None for generic SQL.
"""
- _ = driver
+ engine_family = getattr(driver, "engine_family", None)
+ if not isinstance(engine_family, str) or not engine_family.strip():
+ engine_cache = getattr(self, "_engine_version_cache", None)
+ if isinstance(engine_cache, Mapping) and id(driver) in engine_cache:
+ engine_family = getattr(engine_cache[id(driver)], "engine_family", None)
+ else:
+ get_engine_version = getattr(self, "_get_engine_version", None)
+ if (
+ callable(get_engine_version)
+ and not getattr(driver, "is_async", False)
+ and not inspect.iscoroutinefunction(get_engine_version)
+ ):
+ with contextlib.suppress(Exception):
+ engine_family = getattr(get_engine_version(driver), "engine_family", None)
+ if isinstance(engine_family, str) and engine_family.strip():
+ return _ddl_dialect_or_none(engine_family.strip().lower())
return _ddl_dialect_or_none(type(self).dialect)
def _transpile_ddl_result(self, result: "DDLResult", target_dialect: str | None = None) -> "DDLResult":
@@ -3759,13 +3776,6 @@ def _check_declared_parameters(sql_statement: "SQL") -> None:
_GENERIC_DDL_DIALECTS: Final = frozenset({"", "generic", "unknown"})
-_SQLGLOT_DIALECT_ALIASES: Final[dict[str, str]] = {
- "cockroach": "postgres",
- "cockroachdb": "postgres",
- "mariadb": "mysql",
- "mssql": "tsql",
- "postgresql": "postgres",
-}
_TABLE_OBJECT_TYPES: Final = frozenset({"table", "temporary_table", "base table"})
_TABLE_DEPENDENT_OBJECT_TYPES: Final = frozenset({"index", "trigger"})
_SCHEMA_DDL_OBJECT_DOMAINS: Final[dict[str, str]] = {
@@ -3820,10 +3830,7 @@ def _ddl_dialect_or_none(dialect: str | None) -> str | None:
def _sqlglot_dialect(dialect: str | None) -> str | None:
"""Map a data dictionary dialect name to the SQLGlot dialect used to parse and render it."""
- resolved = _ddl_dialect_or_none(dialect)
- if resolved is None:
- return None
- return _SQLGLOT_DIALECT_ALIASES.get(resolved, resolved)
+ return _normalize_dialect(_ddl_dialect_or_none(dialect))
_LOOSE_PROFILE_TYPES: Final = frozenset({
diff --git a/sqlspec/migrations/commands.py b/sqlspec/migrations/commands.py
index 6f88cdd66..faf2e7c8f 100644
--- a/sqlspec/migrations/commands.py
+++ b/sqlspec/migrations/commands.py
@@ -13,7 +13,7 @@
import time
from collections.abc import Awaitable, Callable, Sequence
from pathlib import Path
-from typing import TYPE_CHECKING, Any, Final, Generic, NamedTuple, NoReturn, ParamSpec, TypeVar, cast
+from typing import TYPE_CHECKING, Any, Final, Generic, NamedTuple, NoReturn, ParamSpec, TypeVar, cast, overload
import sqlglot
from rich.console import Console
@@ -31,7 +31,7 @@
from sqlspec.migrations.squash import MigrationSquasher, schema_ddl_statements
from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
from sqlspec.migrations.utils import create_migration_file
-from sqlspec.migrations.validation import validate_migration_order
+from sqlspec.migrations.validation import validate_migration_order, validate_squash_range
from sqlspec.migrations.version import generate_conversion_map, generate_timestamp_version, parse_version
from sqlspec.observability import resolve_db_system
from sqlspec.utils.logging import get_logger, log_with_context
@@ -334,6 +334,15 @@ def _resolve_data_source(self, from_data: "str | Path") -> "Path | str":
raise MigrationError(msg)
return path
+ def _prepare_revision_source(self, *, from_schema: bool, from_data: "str | Path | None") -> "Path | str | None":
+ """Validate revision source flags and classify ``from_data`` as a file ``Path`` or table name."""
+ if from_schema and from_data is not None:
+ msg = "Cannot specify both from_schema and from_data"
+ raise MigrationError(msg)
+ if from_data is None:
+ return None
+ return self._resolve_data_source(from_data)
+
def _data_file_revision_sql(
self, data_path: Path, *, schema: str | None, target_table: str | None
) -> "tuple[list[str], list[str]]":
@@ -796,23 +805,62 @@ def _print_squash_plan(self, plans: "list[SquashPlan]") -> None:
f"\n[yellow]{total_migrations} migrations will be squashed into {len(plans)} file(s): {target_files}[/]"
)
+ def _print_squash_dry_run(self, squasher: MigrationSquasher, plans: "list[SquashPlan]") -> None:
+ """Extract and print the planned UP and DOWN SQL for each squash plan in dry-run mode."""
+ for plan in plans:
+ up_sql, down_sql = squasher.extract_plan_sql(plan)
+ console.print(f"\n[cyan]-- {plan.target_path.name} (up)[/]")
+ for statement in up_sql:
+ console.print(statement.rstrip())
+ if down_sql:
+ console.print(f"\n[cyan]-- {plan.target_path.name} (down)[/]")
+ for statement in down_sql:
+ console.print(statement.rstrip())
+ console.print("[yellow][Preview Mode - No changes made][/]")
+
+ def _validate_schema_squash_applied(
+ self,
+ runner: SyncMigrationRunner,
+ all_migrations: "list[tuple[str, Path]]",
+ start_version: str,
+ end_version: str,
+ *,
+ allow_gaps: bool,
+ applied_migrations: "list[AppliedMigrationRecord]",
+ ) -> None:
+ """Ensure every migration in a ``schema`` squash range is already applied in the database."""
+ source_migrations = validate_squash_range(all_migrations, start_version, end_version, allow_gaps=allow_gaps)
+ covered_versions: set[str] = set()
+ for record in applied_migrations:
+ covered_versions.add(record["version_num"])
+ raw_replaces = record.get("replaces")
+ if isinstance(raw_replaces, str) and raw_replaces:
+ covered_versions.update(part.strip() for part in raw_replaces.split(",") if part.strip())
+ unapplied: list[str] = []
+ for version, file_path in source_migrations:
+ if version in covered_versions:
+ continue
+ migration = runner.load_migration(file_path, version)
+ replaces = list(migration.get("replaces") or [])
+ if replaces and all(rep in covered_versions for rep in replaces):
+ continue
+ unapplied.append(version)
+ if unapplied:
+ msg = (
+ "Cannot squash with the schema strategy because the database has unapplied migrations "
+ f"({', '.join(unapplied)}). Run 'upgrade' so all migrations through {end_version} are applied."
+ )
+ raise SquashValidationError(msg)
+
def _apply_squash_files(self, squasher: MigrationSquasher, plans: "list[SquashPlan]") -> None:
"""Write squashed migration files, keeping the backup until the caller commits or rolls back."""
squasher.apply_squash(plans, cleanup_backup=False)
for plan in plans:
console.print(f"[green]âś“ Created squashed migration: {plan.target_path.name}[/]")
- def _squash_record_checksum(self, squasher: MigrationSquasher, plan: "SquashPlan") -> str:
+ def _squash_record_checksum(self, plan: "SquashPlan") -> str:
"""Return the checksum recorded for a squashed migration in the tracking table."""
- if plan.target_path.exists():
- content = plan.target_path.read_text(encoding="utf-8")
- else:
- up_sql, down_sql = squasher.extract_plan_sql(plan)
- if plan.target_path.suffix == ".py":
- content = squasher.generate_python_squash(plan, up_sql, down_sql)
- else:
- content = squasher.generate_squashed_content(plan, up_sql, down_sql)
- return self.runner.calculate_checksum(content)
+ return self.runner.calculate_checksum(plan.target_path.read_text(encoding="utf-8"))
def _finish_squash(self, squasher: MigrationSquasher) -> None:
"""Remove the squash backup, drop stale migration caches, and report completion."""
@@ -942,14 +990,6 @@ def _raise_partial_squash(
)
raise SquashValidationError(msg)
- @staticmethod
- def _attach_schema_ddl(plans: "list[SquashPlan]", create_res: Any, drop_res: Any) -> None:
- """Attach live-schema UP/DOWN DDL to every plan for the ``schema`` squash strategy."""
- up_ddl, down_ddl = schema_ddl_statements(create_res, drop_res)
- for plan in plans:
- plan.schema_ddl_up = up_ddl
- plan.schema_ddl_down = down_ddl
-
@staticmethod
def _driver_dialect(driver: Any) -> str | None:
"""Return the SQL dialect of a driver, or None for generic SQL."""
@@ -1529,10 +1569,7 @@ def revision(
Raises:
MigrationError: When both sources are given or DDL cannot be inferred.
"""
- if from_schema and from_data is not None:
- msg = "Cannot specify both from_schema and from_data"
- raise MigrationError(msg)
-
+ source = self._prepare_revision_source(from_schema=from_schema, from_data=from_data)
up_sql: Sequence[str] | None = None
down_sql: Sequence[str] | None = None
if from_schema:
@@ -1546,23 +1583,19 @@ def revision(
driver, schema=schema, exclude_tables=excluded, direction="drop"
),
)
- elif from_data is not None:
- source = self._resolve_data_source(from_data)
- if isinstance(source, Path):
- up_sql, down_sql = self._data_file_revision_sql(source, schema=schema, target_table=target_table)
- else:
- source_table, source_schema = self._split_qualified_table(source, schema)
- with self.config.provide_session() as driver:
- try:
- ddl_res = driver.data_dictionary.infer_table_ddl_from_data(
- driver, source_table, schema=source_schema, target_table=target_table
- )
- except (SqlglotError, SQLBuilderError) as exc:
- msg = f"Could not infer DDL from table '{source}': {exc}"
- raise MigrationError(msg) from exc
- up_sql, down_sql = self._staging_table_revision_sql(
- ddl_res.ddl, source, self._driver_dialect(driver)
+ elif isinstance(source, Path):
+ up_sql, down_sql = self._data_file_revision_sql(source, schema=schema, target_table=target_table)
+ elif isinstance(source, str):
+ source_table, source_schema = self._split_qualified_table(source, schema)
+ with self.config.provide_session() as driver:
+ try:
+ ddl_res = driver.data_dictionary.infer_table_ddl_from_data(
+ driver, source_table, schema=source_schema, target_table=target_table
)
+ except (SqlglotError, SQLBuilderError) as exc:
+ msg = f"Could not infer DDL from table '{source}': {exc}"
+ raise MigrationError(msg) from exc
+ up_sql, down_sql = self._staging_table_revision_sql(ddl_res.ddl, source, self._driver_dialect(driver))
return self._write_revision(message, file_type, up_sql, down_sql)
@@ -1653,7 +1686,7 @@ def squash(
allow_gaps: bool = False,
output_format: str = "sql",
strategy: str = "concat",
- single_file: bool = True,
+ single_file: bool | None = None,
) -> None:
"""Squash a range of migrations into a single file.
@@ -1673,11 +1706,12 @@ def squash(
output_format: Output format ("sql" or "py").
strategy: Squash strategy: "concat" (default) keeps every statement in order, "ddl" folds DDL and
refuses ranges it cannot fold faithfully, "schema" reflects the live database schema.
- single_file: Emit a single squashed file across the range.
+ single_file: Force a single squashed file when True, split by file type/directives when False or None.
"""
resolved_strategy = strategy.lower()
+ all_migrations = self.runner.get_migration_files()
squasher = self._create_squasher(self.runner, resolved_strategy)
- version_range = self._resolve_squash_range(self.runner.get_migration_files(), start_version, end_version)
+ version_range = self._resolve_squash_range(all_migrations, start_version, end_version)
if version_range is None:
return
if description is None:
@@ -1685,6 +1719,32 @@ def squash(
"initial_schema" if yes else Prompt.ask("Migration description", default="squashed_migrations")
)
+ schema_ddl: tuple[list[str], list[str]] | None = None
+ if resolved_strategy == "schema":
+ squasher.plan_squash(
+ version_range[0],
+ version_range[1],
+ description,
+ allow_gaps=allow_gaps,
+ output_format=output_format,
+ strategy=resolved_strategy,
+ single_file=single_file,
+ )
+ excluded = self._version_table_exclusion()
+ with self.config.provide_session() as driver:
+ self._validate_schema_squash_applied(
+ self.runner,
+ all_migrations,
+ version_range[0],
+ version_range[1],
+ allow_gaps=allow_gaps,
+ applied_migrations=self._read_applied_migrations(driver),
+ )
+ schema_ddl = schema_ddl_statements(
+ driver.data_dictionary.get_schema_ddl(driver, exclude_tables=excluded, direction="create"),
+ driver.data_dictionary.get_schema_ddl(driver, exclude_tables=excluded, direction="drop"),
+ )
+
plans = squasher.plan_squash(
version_range[0],
version_range[1],
@@ -1693,40 +1753,35 @@ def squash(
output_format=output_format,
strategy=resolved_strategy,
single_file=single_file,
+ schema_ddl=schema_ddl,
)
self._print_squash_plan(plans)
if dry_run:
- console.print("[yellow][Preview Mode - No changes made][/]")
+ self._print_squash_dry_run(squasher, plans)
return
if not yes and not Confirm.ask("Proceed with squash?", default=False):
console.print("[yellow]Squash cancelled[/]")
return
try:
- if update_database or resolved_strategy == "schema":
+ if update_database:
with self.config.provide_session() as driver:
- if resolved_strategy == "schema":
- excluded = self._version_table_exclusion()
- self._attach_schema_ddl(
- plans,
- driver.data_dictionary.get_schema_ddl(driver, exclude_tables=excluded, direction="create"),
- driver.data_dictionary.get_schema_ddl(driver, exclude_tables=excluded, direction="drop"),
- )
+ self.tracker.ensure_tracking_table(driver)
+ applied_plans = [
+ plan
+ for plan in plans
+ if self.tracker.is_squash_already_applied(driver, plan.target_version, plan.source_versions)
+ ]
self._apply_squash_files(squasher, plans)
- if update_database:
- self.tracker.ensure_tracking_table(driver)
- for plan in plans:
- if self.tracker.is_squash_already_applied(
- driver, plan.target_version, plan.source_versions
- ):
- self.tracker.replace_with_squash(
- driver,
- plan.target_version,
- plan.source_versions,
- description,
- self._squash_record_checksum(squasher, plan),
- )
- console.print("[green]âś“ Updated migration tracking table[/]")
+ for plan in applied_plans:
+ self.tracker.replace_with_squash(
+ driver,
+ plan.target_version,
+ plan.source_versions,
+ description,
+ self._squash_record_checksum(plan),
+ )
+ console.print("[green]âś“ Updated migration tracking table[/]")
else:
self._apply_squash_files(squasher, plans)
except Exception:
@@ -2539,10 +2594,7 @@ async def revision(
Raises:
MigrationError: When both sources are given or DDL cannot be inferred.
"""
- if from_schema and from_data is not None:
- msg = "Cannot specify both from_schema and from_data"
- raise MigrationError(msg)
-
+ source = self._prepare_revision_source(from_schema=from_schema, from_data=from_data)
up_sql: Sequence[str] | None = None
down_sql: Sequence[str] | None = None
if from_schema:
@@ -2556,23 +2608,19 @@ async def revision(
driver, schema=schema, exclude_tables=excluded, direction="drop"
),
)
- elif from_data is not None:
- source = self._resolve_data_source(from_data)
- if isinstance(source, Path):
- up_sql, down_sql = self._data_file_revision_sql(source, schema=schema, target_table=target_table)
- else:
- source_table, source_schema = self._split_qualified_table(source, schema)
- async with self.config.provide_session() as driver:
- try:
- ddl_res = await driver.data_dictionary.infer_table_ddl_from_data(
- driver, source_table, schema=source_schema, target_table=target_table
- )
- except (SqlglotError, SQLBuilderError) as exc:
- msg = f"Could not infer DDL from table '{source}': {exc}"
- raise MigrationError(msg) from exc
- up_sql, down_sql = self._staging_table_revision_sql(
- ddl_res.ddl, source, self._driver_dialect(driver)
+ elif isinstance(source, Path):
+ up_sql, down_sql = self._data_file_revision_sql(source, schema=schema, target_table=target_table)
+ elif isinstance(source, str):
+ source_table, source_schema = self._split_qualified_table(source, schema)
+ async with self.config.provide_session() as driver:
+ try:
+ ddl_res = await driver.data_dictionary.infer_table_ddl_from_data(
+ driver, source_table, schema=source_schema, target_table=target_table
)
+ except (SqlglotError, SQLBuilderError) as exc:
+ msg = f"Could not infer DDL from table '{source}': {exc}"
+ raise MigrationError(msg) from exc
+ up_sql, down_sql = self._staging_table_revision_sql(ddl_res.ddl, source, self._driver_dialect(driver))
return self._write_revision(message, file_type, up_sql, down_sql)
@@ -2663,7 +2711,7 @@ async def squash(
allow_gaps: bool = False,
output_format: str = "sql",
strategy: str = "concat",
- single_file: bool = True,
+ single_file: bool | None = None,
) -> None:
"""Squash a range of migrations into a single file.
@@ -2683,12 +2731,13 @@ async def squash(
output_format: Output format ("sql" or "py").
strategy: Squash strategy: "concat" (default) keeps every statement in order, "ddl" folds DDL and
refuses ranges it cannot fold faithfully, "schema" reflects the live database schema.
- single_file: Emit a single squashed file across the range.
+ single_file: Force a single squashed file when True, split by file type/directives when False or None.
"""
resolved_strategy = strategy.lower()
sync_runner = self._create_squash_runner()
+ all_migrations = sync_runner.get_migration_files()
squasher = self._create_squasher(sync_runner, resolved_strategy)
- version_range = self._resolve_squash_range(sync_runner.get_migration_files(), start_version, end_version)
+ version_range = self._resolve_squash_range(all_migrations, start_version, end_version)
if version_range is None:
return
if description is None:
@@ -2698,6 +2747,32 @@ async def squash(
else await asyncio.to_thread(Prompt.ask, "Migration description", default="squashed_migrations")
)
+ schema_ddl: tuple[list[str], list[str]] | None = None
+ if resolved_strategy == "schema":
+ squasher.plan_squash(
+ version_range[0],
+ version_range[1],
+ description,
+ allow_gaps=allow_gaps,
+ output_format=output_format,
+ strategy=resolved_strategy,
+ single_file=single_file,
+ )
+ excluded = self._version_table_exclusion()
+ async with self.config.provide_session() as driver:
+ self._validate_schema_squash_applied(
+ sync_runner,
+ all_migrations,
+ version_range[0],
+ version_range[1],
+ allow_gaps=allow_gaps,
+ applied_migrations=await self._read_applied_migrations(driver),
+ )
+ schema_ddl = schema_ddl_statements(
+ await driver.data_dictionary.get_schema_ddl(driver, exclude_tables=excluded, direction="create"),
+ await driver.data_dictionary.get_schema_ddl(driver, exclude_tables=excluded, direction="drop"),
+ )
+
plans = squasher.plan_squash(
version_range[0],
version_range[1],
@@ -2706,44 +2781,37 @@ async def squash(
output_format=output_format,
strategy=resolved_strategy,
single_file=single_file,
+ schema_ddl=schema_ddl,
)
self._print_squash_plan(plans)
if dry_run:
- console.print("[yellow][Preview Mode - No changes made][/]")
+ self._print_squash_dry_run(squasher, plans)
return
if not yes and not await asyncio.to_thread(Confirm.ask, "Proceed with squash?", default=False):
console.print("[yellow]Squash cancelled[/]")
return
try:
- if update_database or resolved_strategy == "schema":
+ if update_database:
async with self.config.provide_session() as driver:
- if resolved_strategy == "schema":
- excluded = self._version_table_exclusion()
- self._attach_schema_ddl(
- plans,
- await driver.data_dictionary.get_schema_ddl(
- driver, exclude_tables=excluded, direction="create"
- ),
- await driver.data_dictionary.get_schema_ddl(
- driver, exclude_tables=excluded, direction="drop"
- ),
+ await self.tracker.ensure_tracking_table(driver)
+ applied_plans = [
+ plan
+ for plan in plans
+ if await self.tracker.is_squash_already_applied(
+ driver, plan.target_version, plan.source_versions
)
+ ]
self._apply_squash_files(squasher, plans)
- if update_database:
- await self.tracker.ensure_tracking_table(driver)
- for plan in plans:
- if await self.tracker.is_squash_already_applied(
- driver, plan.target_version, plan.source_versions
- ):
- await self.tracker.replace_with_squash(
- driver,
- plan.target_version,
- plan.source_versions,
- description,
- self._squash_record_checksum(squasher, plan),
- )
- console.print("[green]âś“ Updated migration tracking table[/]")
+ for plan in applied_plans:
+ await self.tracker.replace_with_squash(
+ driver,
+ plan.target_version,
+ plan.source_versions,
+ description,
+ self._squash_record_checksum(plan),
+ )
+ console.print("[green]âś“ Updated migration tracking table[/]")
else:
self._apply_squash_files(squasher, plans)
except Exception:
@@ -3084,6 +3152,14 @@ async def remove_version(exec_time: int, version: str = version, use_txn: bool =
return execution_time
+@overload
+def create_migration_commands(config: "SyncConfigT") -> "SyncMigrationCommands[SyncConfigT]": ...
+
+
+@overload
+def create_migration_commands(config: "AsyncConfigT") -> "AsyncMigrationCommands[AsyncConfigT]": ...
+
+
def create_migration_commands(
config: "SyncConfigT | AsyncConfigT",
) -> "SyncMigrationCommands[SyncConfigT] | AsyncMigrationCommands[AsyncConfigT]":
diff --git a/sqlspec/migrations/squash.py b/sqlspec/migrations/squash.py
index 9247a6f28..d7f87bca3 100644
--- a/sqlspec/migrations/squash.py
+++ b/sqlspec/migrations/squash.py
@@ -93,7 +93,7 @@
_MAX_TYPE_TOKENS = 12
_TYPE_PLACEHOLDER = "__sqlspec_column_type__"
_ALTER_TYPE_KEYWORDS = frozenset({"SET", "DATA", "TYPE"})
-_MYSQL_FAMILY = frozenset({"mysql", "singlestore", "doris", "starrocks", "tidb"})
+_MYSQL_FAMILY = frozenset({"mysql", "mariadb", "singlestore", "doris", "starrocks", "tidb"})
_SEQUENCE_FUNCTIONS = frozenset({"NEXTVAL", "CURRVAL", "SETVAL", "LASTVAL"})
@@ -135,8 +135,9 @@ class DDLStateFolder:
)
def __init__(self, dialect: str | None = None) -> None:
- self._dialect = dialect
- self._dialect_impl: Dialect = Dialect.get_or_raise(dialect)
+ normalized_dialect = "mysql" if dialect == "mariadb" else dialect
+ self._dialect = normalized_dialect
+ self._dialect_impl: Dialect = Dialect.get_or_raise(normalized_dialect)
self._schemas: dict[str, _FoldedStatement] = {}
self._sequences: dict[str, _FoldedStatement] = {}
self._tables: dict[str, _FoldedStatement] = {}
@@ -628,6 +629,14 @@ def _drop_column(
"NOT NULL state, which the folded CREATE TABLE cannot reproduce"
)
raise self._refusal(msg)
+ if (self._dialect or "") in _MYSQL_FAMILY and self._has_multi_column_index_or_unique(
+ table_key, schema, column_key
+ ):
+ msg = (
+ f"dropping column {column_key} on {table_key} narrows a multi-column index or unique constraint "
+ "in MySQL, which the folded CREATE TABLE cannot reproduce"
+ )
+ raise self._refusal(msg)
self._assert_views_allow(table_key, {column_key}, "DROP COLUMN")
for other_key, other in self._tables.items():
if other_key != table_key and column_key in self._referenced_columns(other.expr, table_key):
@@ -1371,6 +1380,21 @@ def _primary_key_columns(self, table_key: str) -> set[str]:
columns.update(self._local_column_keys(item, table_key))
return columns
+ def _has_multi_column_index_or_unique(self, table_key: str, schema: exp.Schema, column_key: str) -> bool:
+ for index_entry in self._indexes.values():
+ if index_entry.owner == table_key:
+ index_cols = self._index_column_keys(index_entry)
+ if column_key in index_cols and len(index_cols) > 1:
+ return True
+ for item in schema.expressions:
+ if isinstance(item, exp.ColumnDef):
+ continue
+ if item.find(exp.UniqueColumnConstraint, exp.IndexColumnConstraint) is not None:
+ local_cols = self._local_column_keys(item, table_key)
+ if column_key in local_cols and len(local_cols) > 1:
+ return True
+ return False
+
def _local_column_keys(self, node: exp.Expr, owner_key: str) -> set[str]:
return {self._ident_key(ident) for ident in self._local_column_refs(node, owner_key)}
@@ -1392,10 +1416,17 @@ def _local_column_refs(self, node: exp.Expr, owner_key: str) -> list[exp.Identif
if parent.this is ident and parent.find_ancestor(exp.Reference) is None:
refs.append(ident)
elif isinstance(parent, exp.Schema):
- if parent.this is None or (
- isinstance(parent.parent, exp.Reference)
- and isinstance(parent.this, exp.Table)
- and self._resolve_reference_key(parent.this) == owner_key
+ if (
+ parent.this is None
+ or (
+ isinstance(parent.parent, (exp.UniqueColumnConstraint, exp.IndexColumnConstraint))
+ and ident.arg_key == "expressions"
+ )
+ or (
+ isinstance(parent.parent, exp.Reference)
+ and isinstance(parent.this, exp.Table)
+ and self._resolve_reference_key(parent.this) == owner_key
+ )
):
refs.append(ident)
elif isinstance(parent, (exp.ForeignKey, exp.PrimaryKey)) and ident.arg_key == "expressions":
@@ -1675,15 +1706,7 @@ class MigrationSquasher:
Combines multiple sequential or timestamped migrations into a single file.
"""
- __slots__ = (
- "backup_path",
- "migrations_path",
- "runner",
- "schema_ddl",
- "strategy",
- "template_settings",
- "version_table",
- )
+ __slots__ = ("backup_path", "migrations_path", "runner", "strategy", "template_settings", "version_table")
def __init__(
self,
@@ -1692,7 +1715,6 @@ def __init__(
template_settings: "MigrationTemplateSettings | None" = None,
*,
strategy: str = "concat",
- schema_ddl: tuple[list[str], list[str]] | None = None,
version_table: str = "ddl_migrations",
) -> None:
"""Initialize the migration squasher.
@@ -1702,7 +1724,6 @@ def __init__(
runner: SyncMigrationRunner instance for loading migrations.
template_settings: Optional template settings for generating squashed file.
strategy: Default squash strategy ("concat", "ddl", or "schema").
- schema_ddl: Optional pre-fetched (up_ddl, down_ddl) tuple for "schema" strategy.
version_table: Migration tracking table excluded from live "schema" extraction.
"""
self.migrations_path = migrations_path
@@ -1710,7 +1731,6 @@ def __init__(
self.template_settings = template_settings
self.backup_path: Path | None = None
self.strategy = self._resolve_strategy(strategy)
- self.schema_ddl = schema_ddl
self.version_table = version_table
def plan_squash(
@@ -1734,33 +1754,64 @@ def plan_squash(
allow_gaps: If True, allow gaps in version sequence.
output_format: Output file format ("sql" or "py").
strategy: Squash strategy override ("concat", "ddl", or "schema").
- single_file: When True, emit a single SquashPlan across mixed file types/directives.
+ single_file: When True, require a single SquashPlan (raising if directives or
+ file types conflict); when False or None, split when directives or file types change.
schema_ddl: Optional pre-fetched (up_ddl, down_ddl) tuple for "schema" strategy.
Returns:
List of SquashPlan objects with details of planned operations.
"""
resolved_strategy = self._resolve_strategy(strategy or self.strategy)
- resolved_schema_ddl = schema_ddl if schema_ddl is not None else self.schema_ddl
all_migrations = self.runner.get_migration_files()
source_migrations = validate_squash_range(all_migrations, start_version, end_version, allow_gaps=allow_gaps)
+ parsed_start = parse_version(start_version)
+
+ if resolved_strategy == "schema":
+ stream_migrations = _stream_migrations(all_migrations, parsed_start.extension)
+ if (
+ not stream_migrations
+ or source_migrations[0][0] != stream_migrations[0][0]
+ or source_migrations[-1][0] != stream_migrations[-1][0]
+ or len(source_migrations) != len(stream_migrations)
+ ):
+ first_ver = stream_migrations[0][0] if stream_migrations else start_version
+ last_ver = stream_migrations[-1][0] if stream_migrations else end_version
+ msg = (
+ "The schema squash strategy reflects the full current database schema and must squash from the "
+ f"first migration ({first_ver}) through head ({last_ver})."
+ )
+ raise SquashValidationError(msg)
safe_description = slugify(description, separator="_")[:50] or "migration"
if output_format == "py":
- directive_groups: list[tuple[str, bool | None, str | None, list[tuple[str, Path]]]] = [
- ("py", None, None, source_migrations)
- ]
+ tx_dir, schema_dir = _coalesce_sql_directives(source_migrations)
+ if tx_dir is not None or schema_dir is not None:
+ msg = (
+ "Cannot squash SQL migrations with '-- transactional:' or '-- schema:' directives into a Python "
+ "migration because Python migrations do not support SQL header directives."
+ )
+ raise SquashValidationError(msg)
+ if single_file is False:
+ directive_groups: list[tuple[str, bool | None, str | None, list[tuple[str, Path]]]] = [
+ ("py", None, None, grp) for _, _, _, grp in _group_migrations_with_directives(source_migrations)
+ ]
+ else:
+ directive_groups = [("py", None, None, source_migrations)]
elif single_file is True:
file_type = "py" if all(p.suffix == ".py" for _, p in source_migrations) else "sql"
tx_dir, schema_dir = _coalesce_sql_directives(source_migrations) if file_type == "sql" else (None, None)
directive_groups = [(file_type, tx_dir, schema_dir, source_migrations)]
else:
- directive_groups = _group_migrations_with_directives(
- source_migrations, split_by_directives=single_file is False
+ directive_groups = _group_migrations_with_directives(source_migrations)
+
+ if resolved_strategy == "schema" and len(directive_groups) > 1:
+ msg = (
+ "The schema squash strategy emits a single full-schema migration and cannot split across multiple "
+ "files with differing file types or directives."
)
+ raise SquashValidationError(msg)
- parsed_start = parse_version(start_version)
if parsed_start.sequence is not None:
version_counter = parsed_start.sequence
else:
@@ -1798,8 +1849,8 @@ def plan_squash(
transactional=transactional,
schema=schema,
strategy=resolved_strategy,
- schema_ddl_up=list(resolved_schema_ddl[0]) if resolved_schema_ddl is not None else None,
- schema_ddl_down=list(resolved_schema_ddl[1]) if resolved_schema_ddl is not None else None,
+ schema_ddl_up=list(schema_ddl[0]) if schema_ddl is not None else None,
+ schema_ddl_down=list(schema_ddl[1]) if schema_ddl is not None else None,
)
)
version_counter += 1
@@ -2052,9 +2103,8 @@ def _extract_with_strategy(
schema_ddl: tuple[list[str], list[str]] | None,
) -> tuple[list[str], list[str], list[str]]:
if strategy == "schema":
- effective_schema_ddl = schema_ddl if schema_ddl is not None else self.schema_ddl
- if effective_schema_ddl is not None:
- return list(effective_schema_ddl[0]), list(effective_schema_ddl[1]), []
+ if schema_ddl is not None:
+ return list(schema_ddl[0]), list(schema_ddl[1]), []
schema_up, schema_down = self._extract_schema_ddl_from_context()
return schema_up, schema_down, []
@@ -2307,6 +2357,21 @@ def _normalize_range_endpoint(endpoint: str) -> str:
return _format_sequential_version(endpoint)
+def _stream_migrations(all_migrations: list[tuple[str, Path]], extension: str | None) -> list[tuple[str, Path]]:
+ """Filter ``all_migrations`` to the core or extension stream matching ``extension``."""
+ stream: list[tuple[str, Path]] = []
+ for ver_str, path in all_migrations:
+ try:
+ parsed = parse_version(ver_str)
+ except ValueError:
+ if extension is None:
+ stream.append((ver_str, path))
+ continue
+ if parsed.extension == extension:
+ stream.append((ver_str, path))
+ return stream
+
+
def _extract_sql_directives(path: Path) -> tuple[bool | None, str | None]:
"""Extract transactional and schema directives from a SQL migration file if it exists."""
if path.suffix != ".sql" or not path.exists():
@@ -2323,33 +2388,34 @@ def _extract_sql_directives(path: Path) -> tuple[bool | None, str | None]:
def _coalesce_sql_directives(migrations: list[tuple[str, Path]]) -> tuple[bool | None, str | None]:
- """Coalesce uniform transactional and schema directives across SQL migrations."""
+ """Coalesce uniform transactional and schema directives across SQL migrations.
+
+ Raises:
+ SquashValidationError: If SQL migrations in ``migrations`` have conflicting
+ ``-- transactional:`` or ``-- schema:`` directives.
+ """
sql_paths = [path for _, path in migrations if path.suffix == ".sql"]
if not sql_paths:
return None, None
extracted = [_extract_sql_directives(path) for path in sql_paths]
tx_values = {tx for tx, _ in extracted}
schema_values = {sc for _, sc in extracted}
- uniform_tx = next(iter(tx_values)) if len(tx_values) == 1 else None
- uniform_schema = next(iter(schema_values)) if len(schema_values) == 1 else None
- return uniform_tx, uniform_schema
+ if len(tx_values) > 1 or len(schema_values) > 1:
+ msg = (
+ "Cannot squash into a single file (--single-file) when SQL migrations have conflicting "
+ "'-- transactional:' or '-- schema:' directives; use --split or omit --single-file."
+ )
+ raise SquashValidationError(msg)
+ return next(iter(tx_values)), next(iter(schema_values))
def _group_migrations_with_directives(
- migrations: list[tuple[str, Path]], *, split_by_directives: bool = False
+ migrations: list[tuple[str, Path]],
) -> list[tuple[str, bool | None, str | None, list[tuple[str, Path]]]]:
- """Group consecutive migrations by file type and optionally SQL execution directives."""
+ """Group consecutive migrations by file type and SQL execution directives."""
if not migrations:
return []
- if not split_by_directives:
- type_groups = group_migrations_by_type(migrations)
- result: list[tuple[str, bool | None, str | None, list[tuple[str, Path]]]] = []
- for file_type, group_items in type_groups:
- tx_dir, schema_dir = _coalesce_sql_directives(group_items) if file_type == "sql" else (None, None)
- result.append((file_type, tx_dir, schema_dir, group_items))
- return result
-
groups: list[tuple[str, bool | None, str | None, list[tuple[str, Path]]]] = []
current_key: tuple[str, bool | None, str | None] | None = None
current_group: list[tuple[str, Path]] = []
diff --git a/sqlspec/migrations/tracker.py b/sqlspec/migrations/tracker.py
index cafbbd553..73cea0dd9 100644
--- a/sqlspec/migrations/tracker.py
+++ b/sqlspec/migrations/tracker.py
@@ -101,21 +101,9 @@ def record_migration(
seq_result = driver.execute(self._next_execution_sequence_query())
next_sequence = self._extract_next_sequence(seq_result)
- if replaces:
- statement = self._record_squashed_migration_statement(
- version,
- version_type,
- next_sequence,
- description,
- execution_time_ms,
- checksum,
- self._applied_by(),
- ",".join(replaces),
- )
- else:
- statement = self._record_migration_statement(
- version, version_type, next_sequence, description, execution_time_ms, checksum, self._applied_by()
- )
+ statement = self._record_statement(
+ version, version_type, next_sequence, description, execution_time_ms, checksum, self._applied_by(), replaces
+ )
driver.execute(statement)
if commit:
self._safe_commit(driver)
@@ -198,9 +186,8 @@ def replace_with_squash(
next_sequence = self._extract_next_sequence(seq_result)
version_type = self._derive_version_type(squashed_version)
- replaces_str = ",".join(replaced_versions)
driver.execute(
- self._record_squashed_migration_statement(
+ self._record_statement(
squashed_version,
version_type,
next_sequence,
@@ -208,7 +195,7 @@ def replace_with_squash(
0,
checksum,
self._applied_by(),
- replaces_str,
+ replaced_versions,
)
)
@@ -280,6 +267,30 @@ def _safe_commit(self, driver: "SyncDriverAdapterBase") -> None:
else:
raise
+ def _record_statement(
+ self,
+ version: str,
+ version_type: str,
+ execution_sequence: int,
+ description: str,
+ execution_time_ms: int,
+ checksum: str,
+ applied_by: str,
+ replaces: "Sequence[str] | None" = None,
+ ) -> Any:
+ """Build an INSERT statement for a standard or squashed migration record."""
+ return _build_record_statement(
+ self,
+ version,
+ version_type,
+ execution_sequence,
+ description,
+ execution_time_ms,
+ checksum,
+ applied_by,
+ replaces,
+ )
+
@mypyc_attr(allow_interpreted_subclasses=True)
class AsyncMigrationTracker(BaseMigrationTracker["AsyncDriverAdapterBase"]):
@@ -352,21 +363,9 @@ async def record_migration(
seq_result = await driver.execute(self._next_execution_sequence_query())
next_sequence = self._extract_next_sequence(seq_result)
- if replaces:
- statement = self._record_squashed_migration_statement(
- version,
- version_type,
- next_sequence,
- description,
- execution_time_ms,
- checksum,
- self._applied_by(),
- ",".join(replaces),
- )
- else:
- statement = self._record_migration_statement(
- version, version_type, next_sequence, description, execution_time_ms, checksum, self._applied_by()
- )
+ statement = self._record_statement(
+ version, version_type, next_sequence, description, execution_time_ms, checksum, self._applied_by(), replaces
+ )
await driver.execute(statement)
if commit:
await self._safe_commit(driver)
@@ -449,9 +448,8 @@ async def replace_with_squash(
next_sequence = self._extract_next_sequence(seq_result)
version_type = self._derive_version_type(squashed_version)
- replaces_str = ",".join(replaced_versions)
await driver.execute(
- self._record_squashed_migration_statement(
+ self._record_statement(
squashed_version,
version_type,
next_sequence,
@@ -459,7 +457,7 @@ async def replace_with_squash(
0,
checksum,
self._applied_by(),
- replaces_str,
+ replaced_versions,
)
)
@@ -533,6 +531,62 @@ async def _safe_commit(self, driver: "AsyncDriverAdapterBase") -> None:
else:
raise
+ def _record_statement(
+ self,
+ version: str,
+ version_type: str,
+ execution_sequence: int,
+ description: str,
+ execution_time_ms: int,
+ checksum: str,
+ applied_by: str,
+ replaces: "Sequence[str] | None" = None,
+ ) -> Any:
+ """Build an INSERT statement for a standard or squashed migration record."""
+ return _build_record_statement(
+ self,
+ version,
+ version_type,
+ execution_sequence,
+ description,
+ execution_time_ms,
+ checksum,
+ applied_by,
+ replaces,
+ )
+
+
+def _build_record_statement(
+ tracker: BaseMigrationTracker[Any],
+ version: str,
+ version_type: str,
+ execution_sequence: int,
+ description: str,
+ execution_time_ms: int,
+ checksum: str,
+ applied_by: str,
+ replaces: "Sequence[str] | None" = None,
+) -> Any:
+ """Return an INSERT builder for either a standard or squashed migration record."""
+ if replaces:
+ return tracker._record_squashed_migration_statement(
+ version,
+ version_type,
+ execution_sequence,
+ description,
+ execution_time_ms,
+ checksum,
+ applied_by,
+ ",".join(replaces),
+ )
+ return tracker._record_migration_statement(
+ version, version_type, execution_sequence, description, execution_time_ms, checksum, applied_by
+ )
+
+
+with suppress(TypeError):
+ setattr(BaseMigrationTracker, "_record_statement", _build_record_statement)
+
def _validate_squash_applied_state(result: Any, squashed_version: str, replaced_versions: "list[str]") -> bool:
"""Validate that either all or none of the replaced versions are applied."""
diff --git a/sqlspec/utils/fixtures.py b/sqlspec/utils/fixtures.py
index a1e0b378c..ff1d18513 100644
--- a/sqlspec/utils/fixtures.py
+++ b/sqlspec/utils/fixtures.py
@@ -67,7 +67,6 @@
)
_INSERT_VALUES_CLAUSE_PATTERN: Final["re.Pattern[str]"] = re.compile(r"\)\s+VALUES\s+\(")
_POSTGRES_DIALECTS: Final["frozenset[str]"] = frozenset({"cockroach", "cockroachdb", "postgres", "postgresql"})
-_PG_SEQUENCE_DIALECTS: Final["frozenset[str]"] = frozenset({"postgres", "postgresql"})
_SEQUENCE_RESYNC_DIALECTS: Final["frozenset[str]"] = frozenset({
"cockroach",
"cockroachdb",
@@ -345,8 +344,11 @@ def load_table_fixtures_sync(
sequence, identity column, or auto-increment counter behind each loaded
table to follow the column's maximum value, or back to its initial
minimum/seed value for an empty table, so subsequent generated values follow
- the loaded rows. On dialects where monotonic sequence resynchronization does
- not apply (Cloud Spanner, BigQuery), the option is skipped with a debug log.
+ the loaded rows. On MySQL, MariaDB, and Oracle, sequence and identity
+ resynchronization executes ``ALTER TABLE`` or ``ALTER SEQUENCE`` DDL, which
+ issues an implicit commit of the active transaction. On dialects where
+ monotonic sequence resynchronization does not apply (Cloud Spanner,
+ BigQuery), the option is skipped with a debug log.
ignore_unknown_columns: When ``True`` and table column metadata is available,
strip fixture columns not present in the target table before inserting.
exclude_update_columns: Column names (or per-table mapping of column names) to
@@ -459,8 +461,11 @@ async def load_table_fixtures_async(
sequence, identity column, or auto-increment counter behind each loaded
table to follow the column's maximum value, or back to its initial
minimum/seed value for an empty table, so subsequent generated values follow
- the loaded rows. On dialects where monotonic sequence resynchronization does
- not apply (Cloud Spanner, BigQuery), the option is skipped with a debug log.
+ the loaded rows. On MySQL, MariaDB, and Oracle, sequence and identity
+ resynchronization executes ``ALTER TABLE`` or ``ALTER SEQUENCE`` DDL, which
+ issues an implicit commit of the active transaction. On dialects where
+ monotonic sequence resynchronization does not apply (Cloud Spanner,
+ BigQuery), the option is skipped with a debug log.
ignore_unknown_columns: When ``True`` and table column metadata is available,
strip fixture columns not present in the target table before inserting.
exclude_update_columns: Column names (or per-table mapping of column names) to
@@ -537,8 +542,9 @@ def export_table_fixtures_sync(
columns.
Each file is written to a temporary file in the directory, created with the
- process umask, and then moved into place; after that, stale table fixture files for the
- same table in the directory are removed, so the next load reads the exported files.
+ process umask, and stale table fixture files for the same table in the directory
+ are removed before moving the new files into place, so the next load reads the
+ exported files.
The directory is created when missing. Table names are quoted and must match the database
spelling exactly.
@@ -569,7 +575,9 @@ def export_table_fixtures_sync(
counts: dict[str, int] = {}
for table in table_names:
columns = _table_columns_sync(driver, family, table)
- rows = _without_generated_values(driver.select(_table_export_query(dialect, family, table, columns)), columns)
+ rows = _without_generated_values(
+ driver.select(_table_export_query(dialect, family, table, columns)), columns, family
+ )
_write_table_fixture(
Path(fixtures_path),
table,
@@ -607,8 +615,9 @@ async def export_table_fixtures_async(
columns.
Each file is written to a temporary file in the directory, created with the
- process umask, and then moved into place; after that, stale table fixture files for the
- same table in the directory are removed, so the next load reads the exported files.
+ process umask, and stale table fixture files for the same table in the directory
+ are removed before moving the new files into place, so the next load reads the
+ exported files.
The directory is created when missing, and file writes run in a worker thread.
Table names are quoted and must match the database spelling exactly.
@@ -640,7 +649,7 @@ async def export_table_fixtures_async(
for table in table_names:
columns = await _table_columns_async(driver, family, table)
rows = _without_generated_values(
- await driver.select(_table_export_query(dialect, family, table, columns)), columns
+ await driver.select(_table_export_query(dialect, family, table, columns)), columns, family
)
await _async_write_table_fixture(
Path(fixtures_path), table, rows, compress, jsonl, max_shard_bytes, max_rows_per_shard
@@ -673,6 +682,26 @@ class _TableColumn(NamedTuple):
identity_on_null: bool = False
+class _ResyncStep(NamedTuple):
+ """Planned SQL step for resynchronizing a table sequence, identity column, or auto-increment counter.
+
+ Attributes:
+ statement: SQL statement or DDL template containing ``{next_val}`` when ``max_query`` is set.
+ parameters: Bind parameters for direct statement execution.
+ max_query: Optional ``SELECT`` query returning the next or maximum value to format into ``statement``.
+ default_next: Fallback integer value when ``max_query`` returns ``None`` or ``0``.
+ skip_when_zero: Whether to skip executing ``statement`` when the resolved integer value is ``<= 0``.
+ ignore_missing_sqlite_sequence: Whether to suppress ``no such table: sqlite_sequence`` errors.
+ """
+
+ statement: str
+ parameters: "dict[str, Any]"
+ max_query: "str | None" = None
+ default_next: int = 1
+ skip_when_zero: bool = False
+ ignore_missing_sqlite_sequence: bool = False
+
+
def _sqlglot_dialect(dialect: "DialectType", family: str) -> "DialectType":
"""Return a SQLGlot-compatible dialect string or object for the driver family.
@@ -1335,27 +1364,47 @@ def _decode_exported_json_value(value: Any) -> Any:
return value
-def _decode_exported_vector_value(value: Any, data_type: str) -> Any:
- """Normalize vector representations to a JSON-serializable list of floats."""
- if value is None:
- return None
- if isinstance(value, (list, tuple)):
- return [float(x) for x in value]
- if isinstance(value, array.array):
+def _vector_items(value: Any) -> "list[float] | None":
+ """Extract a list of floats from a sequence, array, vector object, or bracketed/sparse string."""
+ if isinstance(value, (list, tuple, array.array)):
return [float(x) for x in value]
if hasattr(value, "tolist"):
return [float(x) for x in value.tolist()]
+ if hasattr(value, "to_list"):
+ return [float(x) for x in value.to_list()]
if isinstance(value, str):
v = value.strip()
if v.startswith("[") and v.endswith("]"):
inner = v[1:-1].strip()
- if not inner:
- return []
- return [float(x.strip()) for x in inner.split(",") if x.strip()]
- return value
+ return [float(x.strip()) for x in inner.split(",") if x.strip()] if inner else []
+ if v.startswith("{") and "/" in v:
+ body, _, dim_str = v.rpartition("/")
+ if body.startswith("{") and body.endswith("}") and dim_str.strip().isdigit():
+ dim = int(dim_str.strip())
+ dense = [0.0] * dim
+ inner = body[1:-1].strip()
+ if inner:
+ for entry in inner.split(","):
+ idx_str, sep, val_str = entry.partition(":")
+ if not sep or not idx_str.strip().isdigit():
+ return None
+ idx = int(idx_str.strip()) - 1
+ if 0 <= idx < dim:
+ dense[idx] = float(val_str.strip())
+ return dense
+ return None
+
+
+def _decode_exported_vector_value(value: Any, data_type: str, family: str = "") -> Any:
+ """Normalize vector representations to a JSON-serializable list of floats."""
+ if value is None:
+ return None
+ items = _vector_items(value)
+ if items is not None:
+ return items
if isinstance(value, (bytes, bytearray, memoryview)):
b = bytes(value)
- if "f32" in data_type or "float32" in data_type:
+ if family == "mysql" or "f32" in data_type or "float32" in data_type:
n = len(b) // _FLOAT32_BYTES
return list(struct.unpack(f"<{n}f", b[: n * _FLOAT32_BYTES]))
if len(b) >= _PG_VECTOR_HEADER_BYTES:
@@ -1365,7 +1414,9 @@ def _decode_exported_vector_value(value: Any, data_type: str) -> Any:
return value
-def _without_generated_values(rows: "list[dict[str, Any]]", columns: "list[_TableColumn]") -> "list[dict[str, Any]]":
+def _without_generated_values(
+ rows: "list[dict[str, Any]]", columns: "list[_TableColumn]", family: str = ""
+) -> "list[dict[str, Any]]":
"""Return exported rows without generated columns and with string JSON/vector cells normalized."""
generated = _generated_column_names(columns)
json_columns = {column.name for column in columns if column.data_type in _JSON_TYPE_NAMES}
@@ -1381,7 +1432,7 @@ def _without_generated_values(rows: "list[dict[str, Any]]", columns: "list[_Tabl
if key in json_columns:
row_dict[key] = _decode_exported_json_value(value)
elif key in vector_columns:
- row_dict[key] = _decode_exported_vector_value(value, vector_columns[key])
+ row_dict[key] = _decode_exported_vector_value(value, vector_columns[key], family)
else:
row_dict[key] = value
result.append(row_dict)
@@ -1462,49 +1513,70 @@ def _decode_uuid(value: Any) -> Any:
return convert_uuid(value) if isinstance(value, str) else value
-def _decode_pg_vector(value: Any) -> Any:
- """Format vector values as PostgreSQL/pgvector string literal."""
- if value is None or isinstance(value, str):
+def _format_sparsevec(items: "Sequence[float]", data_type: str = "") -> str:
+ """Format a dense float sequence as a pgvector ``sparsevec`` literal ``{i:v}/dim``."""
+ dim = len(items)
+ if not dim and "(" in data_type and ")" in data_type:
+ dim_text = data_type.partition("(")[2].partition(")")[0].strip()
+ if dim_text.isdigit():
+ dim = int(dim_text)
+ entries = ",".join(f"{index}:{float(val)}" for index, val in enumerate(items, start=1) if float(val) != 0.0)
+ return f"{{{entries}}}/{dim}"
+
+
+def _decode_pg_vector(value: Any, data_type: str = "vector") -> Any:
+ """Format vector values as a PostgreSQL/pgvector string literal."""
+ if value is None:
+ return None
+ is_sparse = _postgres_vector_kind(data_type) == "sparsevec"
+ if isinstance(value, str):
+ v = value.strip()
+ if is_sparse and v.startswith("[") and v.endswith("]"):
+ items = _vector_items(v)
+ if items is not None:
+ return _format_sparsevec(items, data_type)
return value
- if isinstance(value, (list, tuple)):
- return f"[{','.join(str(float(x)) for x in value)}]"
- if hasattr(value, "tolist"):
- return f"[{','.join(str(float(x)) for x in value.tolist())}]"
+ if hasattr(value, "to_text"):
+ text = str(value.to_text())
+ if is_sparse and text.startswith("{"):
+ return text
+ if not is_sparse and text.startswith("["):
+ return text
+ items = _vector_items(value)
+ if items is not None:
+ if is_sparse:
+ return _format_sparsevec(items, data_type)
+ return f"[{','.join(str(x) for x in items)}]"
return str(value)
+def _pg_vector_decoder(data_type: str) -> "Callable[[Any], Any]":
+ """Return a decoder formatting vector values for a PostgreSQL pgvector column."""
+ if _postgres_vector_kind(data_type) == "sparsevec":
+
+ def _sparse_converter(value: Any) -> Any:
+ return _decode_pg_vector(value, data_type)
+
+ return _sparse_converter
+ return _decode_pg_vector
+
+
def _decode_mysql_vector(value: Any) -> Any:
"""Format vector values as MySQL string literal."""
- if value is None or isinstance(value, str):
- return value
- if isinstance(value, (list, tuple)):
- return f"[{','.join(str(float(x)) for x in value)}]"
- if hasattr(value, "tolist"):
- return f"[{','.join(str(float(x)) for x in value.tolist())}]"
- return str(value)
+ return _decode_pg_vector(value)
def _decode_oracle_vector(data_type: str) -> "Callable[[Any], Any]":
"""Return decoder converting vector values to array.array for Oracle."""
- typecode = "d" if "64" in data_type else "f"
+ typecode = "f" if "32" in data_type else "d"
def _oracle_vector_converter(value: Any) -> Any:
- if value is None:
- return None
- if isinstance(value, array.array):
+ if value is None or isinstance(value, array.array):
return value
if hasattr(value, "indices") and hasattr(value, "values"):
return value
- if isinstance(value, (list, tuple)):
- return array.array(typecode, [float(x) for x in value])
- if hasattr(value, "tolist"):
- return array.array(typecode, [float(x) for x in value.tolist()])
- if isinstance(value, str):
- v = value.strip()
- if v.startswith("[") and v.endswith("]"):
- items = [float(x.strip()) for x in v[1:-1].split(",") if x.strip()]
- return array.array(typecode, items)
- return value
+ items = _vector_items(value)
+ return array.array(typecode, items) if items is not None else value
return _oracle_vector_converter
@@ -1516,19 +1588,12 @@ def _decode_spanner_vector(data_type: str) -> "Callable[[Any], Any]":
def _spanner_vector_converter(value: Any) -> Any:
if value is None:
return None
- items: list[float]
- if isinstance(value, (list, tuple)):
- items = [float(x) for x in value]
- elif hasattr(value, "tolist"):
- items = [float(x) for x in value.tolist()]
- elif isinstance(value, str):
- v = value.strip()
- if v.startswith("[") and v.endswith("]"):
- items = [float(x.strip()) for x in v[1:-1].split(",") if x.strip()]
- else:
+ items = _vector_items(value)
+ if items is None:
+ if isinstance(value, str):
items = []
- else:
- return value
+ else:
+ return value
if is_float32:
return TypedParameter(items, list, semantic_name="ARRAY")
return items
@@ -1543,20 +1608,10 @@ def _decode_sqlite_vector(data_type: str) -> "Callable[[Any], Any]":
def _sqlite_vector_converter(value: Any) -> Any:
if value is None:
return None
- items: list[float]
- if isinstance(value, (list, tuple)):
- items = [float(x) for x in value]
- elif hasattr(value, "tolist"):
- items = [float(x) for x in value.tolist()]
- elif isinstance(value, str):
- v = value.strip()
- if v.startswith("[") and v.endswith("]"):
- items = [float(x.strip()) for x in v[1:-1].split(",") if x.strip()]
- else:
- return value
- elif isinstance(value, (bytes, bytearray, memoryview)):
+ if isinstance(value, (bytes, bytearray, memoryview)):
return bytes(value)
- else:
+ items = _vector_items(value)
+ if items is None:
return value
if is_blob:
return struct.pack(f"<{len(items)}f", *items)
@@ -1569,17 +1624,8 @@ def _decode_generic_vector(value: Any) -> Any:
"""Normalize vector values to a list of floats."""
if value is None:
return None
- if isinstance(value, list):
- return [float(x) for x in value]
- if isinstance(value, tuple):
- return [float(x) for x in value]
- if hasattr(value, "tolist"):
- return [float(x) for x in value.tolist()]
- if isinstance(value, str):
- v = value.strip()
- if v.startswith("[") and v.endswith("]"):
- return [float(x.strip()) for x in v[1:-1].split(",") if x.strip()]
- return value
+ items = _vector_items(value)
+ return items if items is not None else value
def _column_value_decoder(family: str, data_type: str) -> "Callable[[Any], Any] | None":
@@ -1590,7 +1636,7 @@ def _column_value_decoder(family: str, data_type: str) -> "Callable[[Any], Any]
normalized_type = data_type.lower()
if _is_vector_column(normalized_type):
if family == "postgres":
- return _decode_pg_vector
+ return _pg_vector_decoder(normalized_type)
if family == "mysql":
return _decode_mysql_vector
if family == "oracle":
@@ -2006,108 +2052,116 @@ def _sequence_resync_statements(
return statements
-def _resync_sybase_sequences_sync(driver: "SyncDriverAdapterBase", table: str, columns: "list[_TableColumn]") -> None:
- """Resynchronize Sybase ASE identity columns for a table synchronously."""
- for column in columns:
- if column.is_identity:
- quoted_col = _quoted_identifier_sql(column.name)
- query = f"SELECT coalesce(max({quoted_col}), 0) FROM {_quoted_table_name(table)}"
- max_val = driver.select_value(query)
- next_val = int(max_val or 0)
- if next_val > 0:
- escaped_table = table.replace("'", "''")
- driver.execute(f"EXEC sp_chgattribute '{escaped_table}', 'identity_burn_max', 0, '{next_val}'")
-
-
-def _resync_sqlanywhere_sequences_sync(
- driver: "SyncDriverAdapterBase", table: str, columns: "list[_TableColumn]"
-) -> None:
- """Resynchronize SAP SQL Anywhere identity columns for a table synchronously."""
- for column in columns:
- if column.is_identity:
- quoted_col = _quoted_identifier_sql(column.name)
- query = f"SELECT coalesce(max({quoted_col}), 0) FROM {_quoted_table_name(table)}"
- max_val = driver.select_value(query)
- next_val = int(max_val or 0)
- schema_part, _, table_part = table.rpartition(".")
- escaped_table = table_part.replace("'", "''")
- escaped_schema = schema_part.replace("'", "''")
- schema_sql = f"'{escaped_schema}'" if schema_part else "NULL"
- driver.execute(f"CALL sa_reset_identity('{escaped_table}', {schema_sql}, {next_val})")
-
-
-def _resync_table_sequences_sync(
- driver: "SyncDriverAdapterBase", family: str, dialect_name: str, table: str, columns: "list[_TableColumn]"
-) -> None:
- """Resynchronize sequences, identity columns, or auto-increment counters for a table synchronously."""
+def _plan_sequence_resync_steps(
+ table: str, columns: "list[_TableColumn]", dialect_name: str = "postgres", family: str = "postgres"
+) -> "list[_ResyncStep]":
+ """Plan the ordered SQL execution steps to resynchronize sequences or identity columns for a table."""
if family == "sybase":
- _resync_sybase_sequences_sync(driver, table, columns)
- return
+ escaped_table = table.replace("'", "''")
+ return [
+ _ResyncStep(
+ statement=f"EXEC sp_chgattribute '{escaped_table}', 'identity_burn_max', 0, '{{next_val}}'",
+ parameters={},
+ max_query=f"SELECT coalesce(max({_quoted_identifier_sql(column.name)}), 0) FROM {_quoted_table_name(table)}",
+ default_next=0,
+ skip_when_zero=True,
+ )
+ for column in columns
+ if column.is_identity
+ ]
if family == "sqlanywhere":
- _resync_sqlanywhere_sequences_sync(driver, table, columns)
- return
+ schema_part, _, table_part = table.rpartition(".")
+ escaped_table = table_part.replace("'", "''")
+ escaped_schema = schema_part.replace("'", "''")
+ schema_sql = f"'{escaped_schema}'" if schema_part else "NULL"
+ return [
+ _ResyncStep(
+ statement=f"CALL sa_reset_identity('{escaped_table}', {schema_sql}, {{next_val}})",
+ parameters={},
+ max_query=f"SELECT coalesce(max({_quoted_identifier_sql(column.name)}), 0) FROM {_quoted_table_name(table)}",
+ default_next=0,
+ )
+ for column in columns
+ if column.is_identity
+ ]
if family == "db2":
quoted_table = _quoted_table_name(table)
+ steps: list[_ResyncStep] = []
for column in columns:
+ quoted_col = _quoted_identifier_sql(column.name)
if column.is_identity:
- quoted_col = _quoted_identifier_sql(column.name)
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = driver.select_value(query)
- next_val = int(max_val or 1)
- driver.execute(f"ALTER TABLE {quoted_table} ALTER COLUMN {quoted_col} RESTART WITH {next_val}")
+ steps.append(
+ _ResyncStep(
+ statement=f"ALTER TABLE {quoted_table} ALTER COLUMN {quoted_col} RESTART WITH {{next_val}}",
+ parameters={},
+ max_query=f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}",
+ )
+ )
elif column.sequence_name is not None:
- quoted_col = _quoted_identifier_sql(column.name)
quoted_seq = _quoted_table_name(_validated_sequence_identifier(column.sequence_name))
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = driver.select_value(query)
- next_val = int(max_val or 1)
- driver.execute(f"ALTER SEQUENCE {quoted_seq} RESTART WITH {next_val}")
- return
+ steps.append(
+ _ResyncStep(
+ statement=f"ALTER SEQUENCE {quoted_seq} RESTART WITH {{next_val}}",
+ parameters={},
+ max_query=f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}",
+ )
+ )
+ return steps
if family == "oracle":
- for statement, parameters in _sequence_resync_statements(
- table, columns, dialect_name=dialect_name, family=family
- ):
- driver.execute(statement, parameters)
+ steps = [
+ _ResyncStep(statement=stmt, parameters=params)
+ for stmt, params in _sequence_resync_statements(table, columns, dialect_name=dialect_name, family=family)
+ ]
quoted_table = _quoted_table_name(table)
for column in columns:
if not column.is_identity and column.sequence_name is not None:
quoted_col = _quoted_identifier_sql(column.name)
quoted_seq = _quoted_table_name(_validated_sequence_identifier(column.sequence_name))
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = driver.select_value(query)
- next_val = int(max_val or 1)
- driver.execute(f"ALTER SEQUENCE {quoted_seq} RESTART START WITH {next_val}")
- return
+ steps.append(
+ _ResyncStep(
+ statement=f"ALTER SEQUENCE {quoted_seq} RESTART START WITH {{next_val}}",
+ parameters={},
+ max_query=f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}",
+ )
+ )
+ return steps
if family == "tsql":
- for statement, parameters in _sequence_resync_statements(
- table, columns, dialect_name=dialect_name, family=family
- ):
- driver.execute(statement, parameters)
+ steps = [
+ _ResyncStep(statement=stmt, parameters=params)
+ for stmt, params in _sequence_resync_statements(table, columns, dialect_name=dialect_name, family=family)
+ ]
quoted_table = _quoted_table(table).sql(dialect="tsql")
for column in columns:
if not column.is_identity and column.sequence_name is not None:
quoted_col = exp.column(_quoted_identifier(column.name)).sql(dialect="tsql")
quoted_seq = _quoted_table(_validated_sequence_identifier(column.sequence_name)).sql(dialect="tsql")
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = driver.select_value(query)
- next_val = int(max_val or 1)
- driver.execute(f"ALTER SEQUENCE {quoted_seq} RESTART WITH {next_val}")
- return
+ steps.append(
+ _ResyncStep(
+ statement=f"ALTER SEQUENCE {quoted_seq} RESTART WITH {{next_val}}",
+ parameters={},
+ max_query=f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}",
+ )
+ )
+ return steps
if family == "mysql":
quoted_table = _quoted_table(table).sql(dialect="mysql")
+ steps = []
for column in columns:
if not column.is_identity and column.sequence_name is not None:
quoted_col = exp.column(_quoted_identifier(column.name)).sql(dialect="mysql")
quoted_seq = _quoted_table(_validated_sequence_identifier(column.sequence_name)).sql(dialect="mysql")
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = driver.select_value(query)
- next_val = int(max_val or 1)
- driver.execute(f"ALTER SEQUENCE {quoted_seq} RESTART WITH {next_val}")
+ steps.append(
+ _ResyncStep(
+ statement=f"ALTER SEQUENCE {quoted_seq} RESTART WITH {{next_val}}",
+ parameters={},
+ max_query=f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}",
+ )
+ )
auto_columns = [
col
for col in columns
@@ -2117,171 +2171,76 @@ def _resync_table_sequences_sync(
if auto_columns:
column = auto_columns[0]
quoted_col = exp.column(_quoted_identifier(column.name)).sql(dialect="mysql")
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = driver.select_value(query)
- next_val = int(max_val or 1)
- driver.execute(f"ALTER TABLE {quoted_table} AUTO_INCREMENT = {next_val}")
- return
+ steps.append(
+ _ResyncStep(
+ statement=f"ALTER TABLE {quoted_table} AUTO_INCREMENT = {{next_val}}",
+ parameters={},
+ max_query=f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}",
+ )
+ )
+ return steps
- if family == "sqlite":
- for statement, parameters in _sequence_resync_statements(
- table, columns, dialect_name=dialect_name, family=family
- ):
+ is_sqlite = family == "sqlite"
+ return [
+ _ResyncStep(statement=stmt, parameters=params, ignore_missing_sqlite_sequence=is_sqlite)
+ for stmt, params in _sequence_resync_statements(table, columns, dialect_name=dialect_name, family=family)
+ ]
+
+
+def _resync_table_sequences_sync(
+ driver: "SyncDriverAdapterBase", family: str, dialect_name: str, table: str, columns: "list[_TableColumn]"
+) -> None:
+ """Resynchronize sequences, identity columns, or auto-increment counters for a table synchronously."""
+ steps = _plan_sequence_resync_steps(table, columns, dialect_name=dialect_name, family=family)
+ if not steps:
+ if family not in {"sybase", "sqlanywhere", "db2", "oracle", "tsql", "mysql", "sqlite"}:
+ logger.debug("Skipping table fixture sequence resync for table %r: no sequence or identity columns", table)
+ return
+ for step in steps:
+ if step.max_query is not None:
+ max_val = driver.select_value(step.max_query)
+ next_val = int(max_val or step.default_next)
+ if step.skip_when_zero and next_val <= 0:
+ continue
+ driver.execute(step.statement.format(next_val=next_val))
+ elif step.ignore_missing_sqlite_sequence:
try:
- driver.execute(statement, parameters)
+ driver.execute(step.statement, step.parameters)
except Exception as exc:
if "no such table: sqlite_sequence" in str(exc).lower():
logger.debug("Skipping sqlite_sequence update: sqlite_sequence table does not exist")
else:
raise
- return
-
- statements = _sequence_resync_statements(table, columns, dialect_name=dialect_name, family=family)
- if not statements:
- logger.debug("Skipping table fixture sequence resync for table %r: no sequence or identity columns", table)
- return
- for statement, parameters in statements:
- driver.execute(statement, parameters)
-
-
-async def _resync_sybase_sequences_async(
- driver: "AsyncDriverAdapterBase", table: str, columns: "list[_TableColumn]"
-) -> None:
- """Resynchronize Sybase ASE identity columns for a table asynchronously."""
- for column in columns:
- if column.is_identity:
- quoted_col = _quoted_identifier_sql(column.name)
- query = f"SELECT coalesce(max({quoted_col}), 0) FROM {_quoted_table_name(table)}"
- max_val = await driver.select_value(query)
- next_val = int(max_val or 0)
- if next_val > 0:
- escaped_table = table.replace("'", "''")
- await driver.execute(f"EXEC sp_chgattribute '{escaped_table}', 'identity_burn_max', 0, '{next_val}'")
-
-
-async def _resync_sqlanywhere_sequences_async(
- driver: "AsyncDriverAdapterBase", table: str, columns: "list[_TableColumn]"
-) -> None:
- """Resynchronize SAP SQL Anywhere identity columns for a table asynchronously."""
- for column in columns:
- if column.is_identity:
- quoted_col = _quoted_identifier_sql(column.name)
- query = f"SELECT coalesce(max({quoted_col}), 0) FROM {_quoted_table_name(table)}"
- max_val = await driver.select_value(query)
- next_val = int(max_val or 0)
- schema_part, _, table_part = table.rpartition(".")
- escaped_table = table_part.replace("'", "''")
- escaped_schema = schema_part.replace("'", "''")
- schema_sql = f"'{escaped_schema}'" if schema_part else "NULL"
- await driver.execute(f"CALL sa_reset_identity('{escaped_table}', {schema_sql}, {next_val})")
+ else:
+ driver.execute(step.statement, step.parameters)
async def _resync_table_sequences_async(
driver: "AsyncDriverAdapterBase", family: str, dialect_name: str, table: str, columns: "list[_TableColumn]"
) -> None:
"""Resynchronize sequences, identity columns, or auto-increment counters for a table asynchronously."""
- if family == "sybase":
- await _resync_sybase_sequences_async(driver, table, columns)
- return
-
- if family == "sqlanywhere":
- await _resync_sqlanywhere_sequences_async(driver, table, columns)
+ steps = _plan_sequence_resync_steps(table, columns, dialect_name=dialect_name, family=family)
+ if not steps:
+ if family not in {"sybase", "sqlanywhere", "db2", "oracle", "tsql", "mysql", "sqlite"}:
+ logger.debug("Skipping table fixture sequence resync for table %r: no sequence or identity columns", table)
return
-
- if family == "db2":
- quoted_table = _quoted_table_name(table)
- for column in columns:
- if column.is_identity:
- quoted_col = _quoted_identifier_sql(column.name)
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = await driver.select_value(query)
- next_val = int(max_val or 1)
- await driver.execute(f"ALTER TABLE {quoted_table} ALTER COLUMN {quoted_col} RESTART WITH {next_val}")
- elif column.sequence_name is not None:
- quoted_col = _quoted_identifier_sql(column.name)
- quoted_seq = _quoted_table_name(_validated_sequence_identifier(column.sequence_name))
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = await driver.select_value(query)
- next_val = int(max_val or 1)
- await driver.execute(f"ALTER SEQUENCE {quoted_seq} RESTART WITH {next_val}")
- return
-
- if family == "oracle":
- for statement, parameters in _sequence_resync_statements(
- table, columns, dialect_name=dialect_name, family=family
- ):
- await driver.execute(statement, parameters)
- quoted_table = _quoted_table_name(table)
- for column in columns:
- if not column.is_identity and column.sequence_name is not None:
- quoted_col = _quoted_identifier_sql(column.name)
- quoted_seq = _quoted_table_name(_validated_sequence_identifier(column.sequence_name))
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = await driver.select_value(query)
- next_val = int(max_val or 1)
- await driver.execute(f"ALTER SEQUENCE {quoted_seq} RESTART START WITH {next_val}")
- return
-
- if family == "tsql":
- for statement, parameters in _sequence_resync_statements(
- table, columns, dialect_name=dialect_name, family=family
- ):
- await driver.execute(statement, parameters)
- quoted_table = _quoted_table(table).sql(dialect="tsql")
- for column in columns:
- if not column.is_identity and column.sequence_name is not None:
- quoted_col = exp.column(_quoted_identifier(column.name)).sql(dialect="tsql")
- quoted_seq = _quoted_table(_validated_sequence_identifier(column.sequence_name)).sql(dialect="tsql")
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = await driver.select_value(query)
- next_val = int(max_val or 1)
- await driver.execute(f"ALTER SEQUENCE {quoted_seq} RESTART WITH {next_val}")
- return
-
- if family == "mysql":
- quoted_table = _quoted_table(table).sql(dialect="mysql")
- for column in columns:
- if not column.is_identity and column.sequence_name is not None:
- quoted_col = exp.column(_quoted_identifier(column.name)).sql(dialect="mysql")
- quoted_seq = _quoted_table(_validated_sequence_identifier(column.sequence_name)).sql(dialect="mysql")
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = await driver.select_value(query)
- next_val = int(max_val or 1)
- await driver.execute(f"ALTER SEQUENCE {quoted_seq} RESTART WITH {next_val}")
- auto_columns = [
- col
- for col in columns
- if col.is_identity
- or (col.is_primary and col.sequence_name is None and ("int" in col.data_type or col.data_type == ""))
- ]
- if auto_columns:
- column = auto_columns[0]
- quoted_col = exp.column(_quoted_identifier(column.name)).sql(dialect="mysql")
- query = f"SELECT coalesce(max({quoted_col}), 0) + 1 FROM {quoted_table}"
- max_val = await driver.select_value(query)
- next_val = int(max_val or 1)
- await driver.execute(f"ALTER TABLE {quoted_table} AUTO_INCREMENT = {next_val}")
- return
-
- if family == "sqlite":
- for statement, parameters in _sequence_resync_statements(
- table, columns, dialect_name=dialect_name, family=family
- ):
+ for step in steps:
+ if step.max_query is not None:
+ max_val = await driver.select_value(step.max_query)
+ next_val = int(max_val or step.default_next)
+ if step.skip_when_zero and next_val <= 0:
+ continue
+ await driver.execute(step.statement.format(next_val=next_val))
+ elif step.ignore_missing_sqlite_sequence:
try:
- await driver.execute(statement, parameters)
+ await driver.execute(step.statement, step.parameters)
except Exception as exc:
if "no such table: sqlite_sequence" in str(exc).lower():
logger.debug("Skipping sqlite_sequence update: sqlite_sequence table does not exist")
else:
raise
- return
-
- statements = _sequence_resync_statements(table, columns, dialect_name=dialect_name, family=family)
- if not statements:
- logger.debug("Skipping table fixture sequence resync for table %r: no sequence or identity columns", table)
- return
- for statement, parameters in statements:
- await driver.execute(statement, parameters)
+ else:
+ await driver.execute(step.statement, step.parameters)
def _exportable_value(value: Any) -> Any:
@@ -2292,6 +2251,8 @@ def _exportable_value(value: Any) -> Any:
return [float(x) for x in value]
if hasattr(value, "tolist"):
return [float(x) for x in value.tolist()]
+ if hasattr(value, "to_list"):
+ return [float(x) for x in value.to_list()]
return value
@@ -2376,25 +2337,32 @@ def _write_table_fixture(
temp_paths: list[Path] = []
final_paths: list[Path] = []
try:
- if len(shards) == 1:
- target_path = base_path / f"{table}{extension}"
- final_paths.append(target_path)
+ target_names = (
+ [f"{table}{extension}"]
+ if len(shards) == 1
+ else [f"{table}.{index:03d}{extension}" for index in range(1, len(shards) + 1)]
+ )
+ for target_name, shard_text in zip(target_names, shards, strict=True):
+ final_paths.append(base_path / target_name)
temp_path = base_path / f".{table}.{secrets.token_hex(8)}.tmp"
temp_paths.append(temp_path)
- payload = _compress_text(shards[0]) if compress else shards[0].encode("utf-8")
+ payload = _compress_text(shard_text) if compress else shard_text.encode("utf-8")
descriptor = os.open(temp_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, _TEMPORARY_FILE_MODE)
with os.fdopen(descriptor, "wb") as handle:
handle.write(payload)
- else:
- for index, shard_text in enumerate(shards, start=1):
- target_path = base_path / f"{table}.{index:03d}{extension}"
- final_paths.append(target_path)
- temp_path = base_path / f".{table}.{secrets.token_hex(8)}.tmp"
- temp_paths.append(temp_path)
- payload = _compress_text(shard_text) if compress else shard_text.encode("utf-8")
- descriptor = os.open(temp_path, os.O_WRONLY | os.O_CREAT | os.O_EXCL, _TEMPORARY_FILE_MODE)
- with os.fdopen(descriptor, "wb") as handle:
- handle.write(payload)
+
+ written_paths = set(final_paths)
+ for entry in base_path.iterdir():
+ if not entry.is_file():
+ continue
+ parsed = _parse_table_fixture_filename(entry.name)
+ if (
+ parsed is not None
+ and parsed[0] == table
+ and entry not in written_paths
+ and (parsed[1] is not None or len(shards) > 1)
+ ):
+ entry.unlink(missing_ok=True)
for temp_path, final_path in zip(temp_paths, final_paths, strict=True):
temp_path.replace(final_path)
@@ -2403,7 +2371,6 @@ def _write_table_fixture(
temp_path.unlink(missing_ok=True)
raise
- written_paths = set(final_paths)
for entry in base_path.iterdir():
if not entry.is_file():
continue
diff --git a/tests/unit/adapters/test_duckdb/test_driver.py b/tests/unit/adapters/test_duckdb/test_driver.py
index cb73ffc7e..9f03dc6ee 100644
--- a/tests/unit/adapters/test_duckdb/test_driver.py
+++ b/tests/unit/adapters/test_duckdb/test_driver.py
@@ -165,6 +165,28 @@ def fail_sql(*_args: Any, **_kwargs: Any) -> str:
config.close_pool()
+class _SpyDuckDBRelation:
+ """Proxy relation for counting to_arrow_table and fetchall calls."""
+
+ __slots__ = ("_counts", "_relation")
+
+ def __init__(self, relation: Any, counts: dict[str, int]) -> None:
+ self._relation = relation
+ self._counts = counts
+
+ @property
+ def description(self) -> Any:
+ return self._relation.description
+
+ def fetchall(self) -> list[Any]:
+ self._counts["fetchall"] += 1
+ return list(self._relation.fetchall())
+
+ def to_arrow_table(self, *args: Any, **kwargs: Any) -> Any:
+ self._counts["arrow"] += 1
+ return self._relation.to_arrow_table(*args, **kwargs)
+
+
class _SpyDuckDBCursor:
"""Proxy cursor for counting to_arrow_table and fetchall calls."""
@@ -184,6 +206,12 @@ def __exit__(self, *_: Any) -> None:
def description(self) -> Any:
return self._connection.description
+ def sql(self, query: str, *, params: Any = None) -> "_SpyDuckDBRelation | None":
+ relation = self._connection.sql(query, params=params)
+ if relation is None:
+ return None
+ return _SpyDuckDBRelation(relation, self._counts)
+
def execute(self, sql: str, parameters: Any = None) -> Self:
if parameters is not None:
self._connection.execute(sql, parameters)
diff --git a/tests/unit/builder/test_ddl_metadata_compiler.py b/tests/unit/builder/test_ddl_metadata_compiler.py
index fbfaafbfd..c6693994e 100644
--- a/tests/unit/builder/test_ddl_metadata_compiler.py
+++ b/tests/unit/builder/test_ddl_metadata_compiler.py
@@ -269,37 +269,89 @@ def test_create_index_from_metadata_skips_primary_and_builds_secondary_indexes()
@pytest.mark.parametrize(
- ("dialect", "expected_table_fragment", "expected_index_fragment"),
+ (
+ "dialect",
+ "expected_table_fragment",
+ "expected_index_fragment",
+ "expected_fidelity",
+ "expects_comment_on",
+ "expects_inline_comment",
+ ),
[
(
"postgres",
'CREATE TABLE "app"."invoices"',
'CREATE UNIQUE INDEX "idx_invoices_code" ON "app"."invoices"("code") INCLUDE ("total")',
+ MetadataFidelity.GENERATED,
+ True,
+ False,
),
(
"tsql",
"CREATE TABLE [app].[invoices]",
"CREATE UNIQUE INDEX [idx_invoices_code] ON [app].[invoices]([code]) INCLUDE ([total])",
+ MetadataFidelity.LOSSY,
+ False,
+ False,
),
(
"duckdb",
'CREATE TABLE "app"."invoices"',
- 'CREATE UNIQUE INDEX "idx_invoices_code" ON "app"."invoices"("code") INCLUDE ("total")',
+ 'CREATE UNIQUE INDEX "idx_invoices_code" ON "app"."invoices"("code")',
+ MetadataFidelity.LOSSY,
+ True,
+ False,
),
(
"sqlite",
'CREATE TABLE "app"."invoices"',
- 'CREATE UNIQUE INDEX "idx_invoices_code" ON "app"."invoices"("code") INCLUDE ("total")',
+ 'CREATE UNIQUE INDEX "idx_invoices_code" ON "app"."invoices"("code")',
+ MetadataFidelity.LOSSY,
+ False,
+ False,
),
(
"db2",
"CREATE TABLE app.invoices",
"CREATE UNIQUE INDEX idx_invoices_code ON app.invoices(code) INCLUDE (total)",
+ MetadataFidelity.GENERATED,
+ True,
+ False,
+ ),
+ (
+ "mysql",
+ "CREATE TABLE `app`.`invoices`",
+ "CREATE UNIQUE INDEX `idx_invoices_code` ON `app`.`invoices`(`code`)",
+ MetadataFidelity.LOSSY,
+ False,
+ True,
+ ),
+ (
+ "mariadb",
+ "CREATE TABLE `app`.`invoices`",
+ "CREATE UNIQUE INDEX `idx_invoices_code` ON `app`.`invoices`(`code`)",
+ MetadataFidelity.LOSSY,
+ False,
+ True,
+ ),
+ (
+ "spanner",
+ "CREATE TABLE `app`.`invoices`",
+ "CREATE UNIQUE INDEX `idx_invoices_code` ON `app`.`invoices`(`code`) STORING (`total`)",
+ MetadataFidelity.LOSSY,
+ False,
+ False,
),
+ ("bigquery", "CREATE TABLE `app`.`invoices`", None, MetadataFidelity.LOSSY, False, False),
],
)
def test_compile_table_ddl_results_cross_dialect(
- dialect: str, expected_table_fragment: str, expected_index_fragment: str
+ dialect: str,
+ expected_table_fragment: str,
+ expected_index_fragment: str | None,
+ expected_fidelity: MetadataFidelity,
+ expects_comment_on: bool,
+ expects_inline_comment: bool,
) -> None:
"""Test compile_table_ddl_results renders table, index, and comment DDL across dialects from one payload."""
columns = [
@@ -353,20 +405,58 @@ def test_compile_table_ddl_results_cross_dialect(
assert isinstance(result, DDLResult)
assert result.status == MetadataSupport.SUPPORTED
- assert result.fidelity == MetadataFidelity.GENERATED
+ assert result.fidelity == expected_fidelity
assert result.source == MetadataSource.GENERATED
assert result.identity.name == "invoices"
assert result.identity.schema == "app"
assert result.ddl is not None
assert expected_table_fragment in result.ddl
- assert expected_index_fragment in result.ddl
+ if expected_index_fragment is not None:
+ assert expected_index_fragment in result.ddl
+ else:
+ assert "idx_invoices_code" not in result.ddl
assert "pk_invoices" not in result.ddl
- assert "COMMENT ON TABLE" in result.ddl
- assert "Customer invoices" in result.ddl
- assert "Primary invoice identifier" in result.ddl
- assert "External invoice code" in result.ddl
+ if expects_comment_on:
+ assert "COMMENT ON TABLE" in result.ddl
+ assert "COMMENT ON COLUMN" in result.ddl
+ assert "Customer invoices" in result.ddl
+ assert "Primary invoice identifier" in result.ddl
+ assert "External invoice code" in result.ddl
+ elif expects_inline_comment:
+ assert "COMMENT ON TABLE" not in result.ddl
+ assert "COMMENT 'Primary invoice identifier'" in result.ddl
+ assert "COMMENT 'External invoice code'" in result.ddl
+ assert "COMMENT='Customer invoices'" in result.ddl
+ else:
+ assert "COMMENT ON TABLE" not in result.ddl
+ assert "Customer invoices" not in result.ddl
+ assert "Primary invoice identifier" not in result.ddl
+ assert "External invoice code" not in result.ddl
+ assert any("Table comment is not supported" in warning for warning in result.warnings)
assert result.ddl.endswith(";")
+ if dialect == "duckdb":
+ duck_conn = duckdb.connect(":memory:")
+ duck_conn.execute('CREATE SCHEMA "app"')
+ _execute_script(duck_conn, result.ddl)
+ duck_conn.execute('INSERT INTO "app"."invoices" ("id", "code") VALUES (1, \'INV-1\')')
+ assert duck_conn.execute('SELECT "id", "code", "total" FROM "app"."invoices"').fetchall() == [(1, "INV-1", 0)]
+ elif dialect == "sqlite":
+ unqualified = compile_table_ddl_results(
+ "invoices",
+ columns,
+ indexes=indexes,
+ table_comment="Customer invoices",
+ column_comments={"code": "External invoice code"},
+ dialect="sqlite",
+ source_dialect="postgres",
+ )
+ assert unqualified.ddl is not None
+ sqlite_conn = sqlite3.connect(":memory:")
+ _execute_script(sqlite_conn, unqualified.ddl)
+ sqlite_conn.execute('INSERT INTO "invoices" ("id", "code") VALUES (1, \'INV-1\')')
+ assert sqlite_conn.execute('SELECT "id", "code", "total" FROM "invoices"').fetchall() == [(1, "INV-1", 0)]
+
def test_compile_table_ddl_results_empty_columns_returns_unsupported() -> None:
"""Test compile_table_ddl_results returns an unsupported DDLResult when columns are empty."""
@@ -1165,3 +1255,240 @@ def test_compile_table_ddl_results_deduplicates_backing_indexes_and_preserves_co
assert 'CONSTRAINT "uq_accounts_email" UNIQUE ("email")' in result.ddl
assert "CREATE UNIQUE INDEX" not in result.ddl
assert "idx_accounts_email_dup_unique" not in result.ddl
+
+
+@pytest.mark.parametrize("dialect", ["postgres", "oracle", "db2", "duckdb", "snowflake"])
+def test_compile_table_ddl_results_emits_comment_on_only_for_supported_dialects(dialect: str) -> None:
+ """COMMENT ON TABLE/COLUMN statements are emitted only for dialects that support COMMENT ON."""
+ columns = [{"column_name": "id", "data_type": "BIGINT", "is_nullable": "NO", "comment": "Row id"}]
+ result = compile_table_ddl_results("items", columns, table_comment="All items", dialect=dialect)
+ assert result.ddl is not None
+ assert "COMMENT ON TABLE" in result.ddl
+ assert "COMMENT ON COLUMN" in result.ddl
+ assert result.fidelity == MetadataFidelity.GENERATED
+ assert result.warnings == ()
+
+
+@pytest.mark.parametrize("dialect", ["mysql", "mariadb"])
+def test_compile_table_ddl_results_inlines_comments_on_mysql_and_mariadb(dialect: str) -> None:
+ """MySQL and MariaDB receive inline column and table COMMENT clauses instead of COMMENT ON."""
+ columns = [
+ {"column_name": "id", "data_type": "BIGINT", "is_nullable": "NO", "comment": "Row's id"},
+ {"column_name": "code", "data_type": "VARCHAR", "max_length": 40, "is_nullable": "NO"},
+ ]
+ result = compile_table_ddl_results(
+ "items", columns, column_comments={"code": "External code"}, table_comment="Item catalog", dialect=dialect
+ )
+ assert result.ddl == (
+ "CREATE TABLE `items` (\n"
+ " `id` BIGINT NOT NULL COMMENT 'Row''s id',\n"
+ " `code` VARCHAR(40) NOT NULL COMMENT 'External code'\n"
+ ")\n"
+ "COMMENT='Item catalog';"
+ )
+ assert "COMMENT ON" not in result.ddl
+ assert result.fidelity == MetadataFidelity.GENERATED
+ assert result.warnings == ()
+
+
+@pytest.mark.parametrize("dialect", ["tsql", "sqlite", "spanner", "bigquery"])
+def test_compile_table_ddl_results_omits_comments_with_lossy_warning_on_unsupported_dialects(dialect: str) -> None:
+ """Dialects without COMMENT ON or inline COMMENT omit comments and record lossy warnings."""
+ columns = [{"column_name": "id", "data_type": "BIGINT", "is_nullable": "NO", "comment": "Row id"}]
+ result = compile_table_ddl_results("items", columns, table_comment="Item catalog", dialect=dialect)
+ assert result.ddl is not None
+ assert "COMMENT" not in result.ddl
+ assert result.fidelity == MetadataFidelity.LOSSY
+ assert result.warnings == (
+ f"Table comment is not supported by {dialect}; comment omitted",
+ f"Column comment on 'id' is not supported by {dialect}; comment omitted",
+ )
+
+
+def test_create_index_storing_on_spanner_and_include_validation() -> None:
+ """Spanner renders STORING (...), Postgres/TSQL/DB2 keep INCLUDE (...), and other dialects reject or warn."""
+ spanner_idx = (
+ CreateIndex("idx_orders_code", dialect="spanner")
+ .on("orders", "code")
+ .include("total", "created_at")
+ .build()
+ .sql
+ )
+ assert spanner_idx == "CREATE INDEX `idx_orders_code` ON `orders`(`code`) STORING (`total`, `created_at`)"
+
+ spangres_meta = CreateIndex.from_metadata(
+ {"index_name": "idx_orders_code", "table_name": "orders", "columns": ["code"], "included_columns": ["total"]},
+ dialect="spangres",
+ )
+ assert spangres_meta is not None
+ assert spangres_meta.build().sql == 'CREATE INDEX "idx_orders_code" ON "orders"("code") STORING ("total")'
+
+ warnings: list[str] = []
+ duck_idx = CreateIndex.from_metadata(
+ {"index_name": "idx_orders_code", "table_name": "orders", "columns": ["code"], "included_columns": ["total"]},
+ dialect="duckdb",
+ warnings=warnings,
+ )
+ assert duck_idx is not None
+ assert duck_idx.build().sql == 'CREATE INDEX "idx_orders_code" ON "orders"("code")'
+ assert warnings == ["Index idx_orders_code: covering INCLUDE columns are not supported on duckdb and were omitted"]
+
+
+def test_catalog_name_folds_unquoted_source_identifier_before_target_reserved_quoting() -> None:
+ """Unquoted source identifiers that are reserved only in the target dialect fold to target case before quoting."""
+ oracle_cols = [
+ {"column_name": "ID", "data_type": "NUMBER", "numeric_precision": 10, "numeric_scale": 0, "is_nullable": "N"},
+ {
+ "column_name": "START",
+ "data_type": "NUMBER",
+ "numeric_precision": 10,
+ "numeric_scale": 0,
+ "is_nullable": "Y",
+ },
+ ]
+ pg_from_oracle = compile_table_ddl_results("EVENTS", oracle_cols, dialect="postgres", source_dialect="oracle")
+ assert (
+ pg_from_oracle.ddl == 'CREATE TABLE "events" (\n "id" DECIMAL(10, 0) NOT NULL,\n "start" DECIMAL(10, 0)\n);'
+ )
+
+ pg_cols = [
+ {"column_name": "id", "data_type": "integer", "is_nullable": "NO"},
+ {"column_name": "rownum", "data_type": "integer", "is_nullable": "YES"},
+ ]
+ oracle_from_pg = compile_table_ddl_results("events", pg_cols, dialect="oracle", source_dialect="postgres")
+ assert oracle_from_pg.ddl == 'CREATE TABLE events (\n id INT NOT NULL,\n "ROWNUM" INT\n);'
+
+
+def test_compile_table_ddl_results_mariadb_quoted_defaults_not_double_quoted() -> None:
+ """MariaDB information_schema string defaults remain single-quoted when source_dialect is mariadb."""
+ result = compile_table_ddl_results(
+ "accounts",
+ [
+ {
+ "column_name": "status",
+ "data_type": "varchar",
+ "max_length": 20,
+ "is_nullable": "NO",
+ "column_default": "'active'",
+ "extra": "",
+ }
+ ],
+ dialect="mariadb",
+ source_dialect="mariadb",
+ )
+ assert result.ddl == "CREATE TABLE `accounts` (\n `status` VARCHAR(20) NOT NULL DEFAULT 'active'\n);"
+
+
+def test_compile_table_ddl_results_bigquery_branch_records_lossy_warnings() -> None:
+ """BigQuery table compilation omits identity generation, FK actions, secondary indexes, and comments."""
+ columns = [
+ {
+ "column_name": "id",
+ "data_type": "BIGINT",
+ "is_nullable": "NO",
+ "is_primary": True,
+ "is_identity": True,
+ "comment": "Primary key",
+ },
+ {"column_name": "parent_id", "data_type": "BIGINT", "is_nullable": "YES"},
+ ]
+ foreign_keys = [
+ {
+ "constraint_name": "fk_parent",
+ "column_name": "parent_id",
+ "referenced_table": "parents",
+ "referenced_column": "id",
+ "on_delete": "CASCADE",
+ }
+ ]
+ indexes = [
+ {
+ "index_name": "idx_parent_id",
+ "table_name": "children",
+ "columns": ["parent_id"],
+ "is_primary": False,
+ "is_unique": False,
+ }
+ ]
+ result = compile_table_ddl_results(
+ "children",
+ columns,
+ foreign_keys=foreign_keys,
+ indexes=indexes,
+ table_comment="Child rows",
+ dialect="bigquery",
+ source_dialect="postgres",
+ )
+ assert result.ddl is not None
+ assert "CREATE TABLE `children`" in result.ddl
+ assert "CREATE INDEX" not in result.ddl
+ assert "ON DELETE" not in result.ddl
+ assert result.fidelity == MetadataFidelity.LOSSY
+ assert (
+ "Column 'id' is an identity/auto-increment column; bigquery cannot express it on this column, "
+ "so it is emitted without automatic value generation"
+ ) in result.warnings
+ assert "Foreign key fk_parent: BigQuery does not support ON DELETE CASCADE; action omitted" in result.warnings
+ assert (
+ "BigQuery does not support secondary CREATE INDEX statements; index 'idx_parent_id' omitted" in result.warnings
+ )
+ assert "Table comment is not supported by bigquery; comment omitted" in result.warnings
+ assert "Column comment on 'id' is not supported by bigquery; comment omitted" in result.warnings
+
+
+def test_translate_catalog_type_preserves_sqlite_type_affinity_and_executes() -> None:
+ """Cross-dialect compilation into SQLite preserves NUMERIC affinity instead of rendering REAL."""
+ columns = [
+ {"column_name": "id", "data_type": "INTEGER", "is_nullable": "NO", "is_primary": True},
+ {
+ "column_name": "amount",
+ "data_type": "NUMERIC",
+ "numeric_precision": 12,
+ "numeric_scale": 2,
+ "is_nullable": "NO",
+ },
+ ]
+ result = compile_table_ddl_results("ledger", columns, dialect="sqlite", source_dialect="postgres")
+ assert (
+ result.ddl
+ == 'CREATE TABLE "ledger" (\n "id" INTEGER NOT NULL PRIMARY KEY,\n "amount" NUMERIC(12,2) NOT NULL\n);'
+ )
+ connection = sqlite3.connect(":memory:")
+ _execute_script(connection, result.ddl)
+ col_types = dict(connection.execute("SELECT name, type FROM pragma_table_info('ledger')").fetchall())
+ assert col_types["amount"] == "NUMERIC(12,2)"
+
+
+def test_sqlite_composite_foreign_keys_group_without_synthetic_constraint_names() -> None:
+ """SQLite composite foreign keys group into a single FOREIGN KEY and execute on in-memory SQLite."""
+ with SqliteConfig(connection_config={"database": ":memory:"}).provide_session() as source:
+ source.execute_script(
+ "CREATE TABLE parent (tenant_id INTEGER, item_id INTEGER, PRIMARY KEY (tenant_id, item_id)); "
+ "CREATE TABLE child ("
+ " id INTEGER PRIMARY KEY, "
+ " tenant_id INTEGER, "
+ " item_id INTEGER, "
+ " FOREIGN KEY (tenant_id, item_id) REFERENCES parent(tenant_id, item_id) ON DELETE CASCADE"
+ ")"
+ )
+ fks = source.data_dictionary.get_foreign_keys(source, table="child")
+ assert all(fk.constraint_name == "child_fk_0" for fk in fks)
+ ddl_result = source.data_dictionary.get_schema_ddl(source, prefer_native=False)
+
+ child_ddl = next(item for item in _ddl_items(ddl_result) if item.identity.name == "child")
+ assert child_ddl.ddl is not None
+ assert "child_fk_0" in child_ddl.ddl
+ assert (
+ 'FOREIGN KEY ("tenant_id", "item_id") REFERENCES "parent"("tenant_id", "item_id") ON DELETE CASCADE'
+ in child_ddl.ddl
+ )
+
+ replay = sqlite3.connect(":memory:")
+ for item in _ddl_items(ddl_result):
+ assert item.ddl is not None
+ _execute_script(replay, item.ddl)
+ replay.execute("PRAGMA foreign_keys = ON")
+ replay.execute("INSERT INTO parent VALUES (1, 10)")
+ replay.execute("INSERT INTO child VALUES (100, 1, 10)")
+ with pytest.raises(sqlite3.IntegrityError):
+ replay.execute("INSERT INTO child VALUES (101, 1, 99)")
diff --git a/tests/unit/cli/test_cli.py b/tests/unit/cli/test_cli.py
new file mode 100644
index 000000000..9650d4023
--- /dev/null
+++ b/tests/unit/cli/test_cli.py
@@ -0,0 +1,169 @@
+"""Unit tests for SQLSpec CLI squash, dump-schema, and create-migration options."""
+
+import re
+from pathlib import Path
+from unittest.mock import MagicMock, patch
+
+import pytest
+from click.testing import CliRunner
+
+from sqlspec.cli import add_migration_commands
+
+
+def test_squash_cli_forwards_single_file_and_split_flags(tmp_path: Path, monkeypatch: pytest.MonkeyPatch) -> None:
+ """sqlspec squash forwards --single-file, --split, and omits single_file when unspecified."""
+ monkeypatch.chdir(tmp_path)
+ config_file = tmp_path / "config.py"
+ config_file.write_text(
+ """
+from sqlspec.adapters.sqlite import SqliteConfig
+
+def get_config():
+ return SqliteConfig(
+ connection_config={"database": ":memory:"},
+ migration_config={"script_location": "migrations", "enabled": True},
+ )
+""",
+ encoding="utf-8",
+ )
+
+ runner = CliRunner()
+ cli_group = add_migration_commands()
+
+ for extra_args, expected_single_file in (([], None), (["--single-file"], True), (["--split"], False)):
+ mock_commands = MagicMock()
+ with patch("sqlspec.migrations.commands.create_migration_commands", return_value=mock_commands):
+ result = runner.invoke(
+ cli_group, ["--config", "config.get_config", "squash", "-m", "release", "--yes", *extra_args]
+ )
+ assert result.exit_code == 0, f"Output: {result.output}"
+ mock_commands.squash.assert_called_once()
+ _, kwargs = mock_commands.squash.call_args
+ assert kwargs.get("single_file") is expected_single_file
+
+
+def test_create_migration_from_data_table_requires_target_table(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ """create-migration --from-data with a source table name requires --target-table."""
+ monkeypatch.chdir(tmp_path)
+ config_file = tmp_path / "config.py"
+ config_file.write_text(
+ """
+from sqlspec.adapters.sqlite import SqliteConfig
+
+def get_config():
+ return SqliteConfig(
+ connection_config={"database": ":memory:"},
+ migration_config={"script_location": "migrations", "enabled": True},
+ )
+""",
+ encoding="utf-8",
+ )
+
+ runner = CliRunner()
+ cli_group = add_migration_commands()
+ mock_commands = MagicMock()
+
+ with patch("sqlspec.migrations.commands.create_migration_commands", return_value=mock_commands):
+ missing_target = runner.invoke(
+ cli_group,
+ ["--config", "config.get_config", "create-migration", "-m", "seed_users", "--from-data", "staging_users"],
+ )
+ assert missing_target.exit_code != 0
+ cleaned_output = _strip_ansi(missing_target.output)
+ assert "--target-table is required when --from-data specifies a source table name" in cleaned_output
+ mock_commands.revision.assert_not_called()
+
+ with_target = runner.invoke(
+ cli_group,
+ [
+ "--config",
+ "config.get_config",
+ "create-migration",
+ "-m",
+ "seed_users",
+ "--from-data",
+ "staging_users",
+ "--target-table",
+ "users",
+ ],
+ )
+ assert with_target.exit_code == 0, f"Output: {with_target.output}"
+ mock_commands.revision.assert_called_once()
+
+ mock_commands.reset_mock()
+ with patch("sqlspec.migrations.commands.create_migration_commands", return_value=mock_commands):
+ from_file = runner.invoke(
+ cli_group,
+ ["--config", "config.get_config", "create-migration", "-m", "seed_users", "--from-data", "users.jsonl"],
+ )
+ assert from_file.exit_code == 0, f"Output: {from_file.output}"
+ mock_commands.revision.assert_called_once()
+
+
+def test_dump_schema_multi_config_rejects_output_file_and_keeps_stdout_clean(
+ tmp_path: Path, monkeypatch: pytest.MonkeyPatch
+) -> None:
+ """dump-schema across multiple configs rejects -o FILE and routes status output to stderr."""
+ monkeypatch.chdir(tmp_path)
+ config_file = tmp_path / "multi_config.py"
+ config_file.write_text(
+ """
+from sqlspec.adapters.sqlite import SqliteConfig
+
+def get_configs():
+ return [
+ SqliteConfig(
+ bind_key="primary",
+ connection_config={"database": ":memory:"},
+ migration_config={"script_location": "migrations_primary", "enabled": True},
+ ),
+ SqliteConfig(
+ bind_key="analytics",
+ connection_config={"database": ":memory:"},
+ migration_config={"script_location": "migrations_analytics", "enabled": True},
+ ),
+ ]
+""",
+ encoding="utf-8",
+ )
+
+ runner = CliRunner()
+ cli_group = add_migration_commands()
+
+ mock_commands = MagicMock()
+ mock_commands.dump_schema.return_value = "CREATE TABLE items (id INTEGER PRIMARY KEY);\n"
+
+ with patch("sqlspec.migrations.commands.create_migration_commands", return_value=mock_commands):
+ rejected = runner.invoke(
+ cli_group, ["--config", "multi_config.get_configs", "dump-schema", "-o", str(tmp_path / "schema.sql")]
+ )
+ assert rejected.exit_code != 0
+ cleaned_output = _strip_ansi(rejected.output)
+ assert "Cannot use a single --output file" in cleaned_output
+ mock_commands.dump_schema.assert_not_called()
+
+ succeeded = runner.invoke(cli_group, ["--config", "multi_config.get_configs", "dump-schema"])
+ assert succeeded.exit_code == 0, f"Output: {succeeded.output}"
+ assert succeeded.stdout == (
+ "CREATE TABLE items (id INTEGER PRIMARY KEY);\nCREATE TABLE items (id INTEGER PRIMARY KEY);\n"
+ )
+ cleaned_stderr = _strip_ansi(succeeded.stderr)
+ assert "Dumping schema for configuration: primary" in cleaned_stderr
+ assert "Dumping schema for configuration: analytics" in cleaned_stderr
+
+
+ANSI_ESCAPE_PATTERN = re.compile(r"\x1b\[[0-9;]*[a-zA-Z]")
+
+
+def _strip_ansi(text: str) -> str:
+ """Remove ANSI escape codes from output text.
+
+ Args:
+ text: Input string with ANSI escape codes.
+
+ Returns:
+ String with ANSI escape codes removed.
+ """
+ return ANSI_ESCAPE_PATTERN.sub("", text)
diff --git a/tests/unit/data_dictionary/test_mssql_metadata.py b/tests/unit/data_dictionary/test_mssql_metadata.py
index 13f48e060..7f6679551 100644
--- a/tests/unit/data_dictionary/test_mssql_metadata.py
+++ b/tests/unit/data_dictionary/test_mssql_metadata.py
@@ -87,7 +87,7 @@ def test_mssql_column_and_index_query_shapes_cover_sql_server_specific_metadata(
"is_sparse",
"is_masked",
"encryption_type_desc",
- "generated_always_type_desc",
+ "generated_always_type",
"COALESCE(:schema_name, SCHEMA_NAME())",
):
assert required_fragment in columns_text
diff --git a/tests/unit/data_dictionary/test_schema_ddl_synthesis.py b/tests/unit/data_dictionary/test_schema_ddl_synthesis.py
index 9ddd5ddbe..c772d0316 100644
--- a/tests/unit/data_dictionary/test_schema_ddl_synthesis.py
+++ b/tests/unit/data_dictionary/test_schema_ddl_synthesis.py
@@ -1,5 +1,7 @@
"""Builder-backed table DDL synthesis, schema DDL ordering, exact catalog names, and profiling SQL."""
+import sqlite3
+from pathlib import Path
from typing import Any, cast
import pytest
@@ -7,8 +9,10 @@
from sqlspec.adapters.adbc.data_dictionary import AdbcDataDictionary
from sqlspec.adapters.asyncpg.data_dictionary import AsyncpgDataDictionary
from sqlspec.adapters.db2.data_dictionary import Db2AsyncDataDictionary, Db2SyncDataDictionary
+from sqlspec.adapters.duckdb import DuckDBConfig
from sqlspec.adapters.psqlpy.data_dictionary import PsqlpyDataDictionary
from sqlspec.adapters.psycopg.data_dictionary import PsycopgAsyncDataDictionary, PsycopgSyncDataDictionary
+from sqlspec.adapters.pymysql.data_dictionary import PyMysqlDataDictionary
from sqlspec.adapters.sqlite import SqliteConfig
from sqlspec.data_dictionary import (
DDLResult,
@@ -545,3 +549,94 @@ def select(self, statement: Any, **kwargs: Any) -> list[Any]:
with pytest.raises(PermissionError, match="permission denied"):
PsycopgSyncDataDictionary().get_table_profile(cast("Any", DeniedDriver()), "orders", schema="public")
+
+
+class _StubMariaDBDriver:
+ """Stub driver exposing engine_family for MariaDB/MySQL resolution checks."""
+
+ def __init__(self, engine_family: str) -> None:
+ self.engine_family = engine_family
+ self.dialect = "mysql"
+
+
+def test_mysql_data_dictionary_resolve_ddl_dialect_returns_mariadb_when_engine_family_is_mariadb() -> None:
+ """MySQL data dictionaries return 'mariadb' from resolve_ddl_dialect when engine_family is mariadb."""
+ dictionary = PyMysqlDataDictionary()
+ assert dictionary.resolve_ddl_dialect(_StubMariaDBDriver("mariadb")) == "mariadb"
+ assert dictionary.resolve_ddl_dialect(_StubMariaDBDriver("mysql")) == "mysql"
+
+
+def test_duckdb_native_ddl_restricts_create_type_to_enum_and_executes(tmp_path: Path) -> None:
+ """DuckDB native DDL queries emit CREATE TYPE only for ENUM types with non-null SQL and replay cleanly."""
+ with DuckDBConfig(connection_config={"database": str(tmp_path / "source.duckdb")}).provide_session() as source:
+ source.execute_script(
+ "CREATE TYPE status_enum AS ENUM ('pending', 'paid'); "
+ "CREATE TYPE custom_int AS INTEGER; "
+ "CREATE TABLE invoices (id INTEGER PRIMARY KEY, status status_enum);"
+ )
+ schema_ddl = source.data_dictionary.get_schema_ddl(source, prefer_native=True)
+ alias_ddl = source.data_dictionary.get_ddl(source, "custom_int", object_type="type")
+
+ assert alias_ddl.status == MetadataSupport.UNSUPPORTED
+ type_items = [
+ item for item in schema_ddl.items if isinstance(item, DDLResult) and item.identity.object_type == "type"
+ ]
+ assert [item.identity.name for item in type_items] == ["status_enum"]
+ assert type_items[0].ddl == "CREATE TYPE \"status_enum\" AS ENUM ('pending', 'paid');"
+
+ with DuckDBConfig(connection_config={"database": str(tmp_path / "replay.duckdb")}).provide_session() as replay:
+ for item in schema_ddl.items:
+ if isinstance(item, DDLResult) and item.ddl:
+ replay.execute_script(item.ddl)
+ replay.execute_script("INSERT INTO invoices VALUES (1, 'paid')")
+ assert replay.select_value("SELECT status FROM invoices WHERE id = 1") == "paid"
+
+
+def test_ddl_and_sequence_queries_use_upfront_ctes_and_left_joins() -> None:
+ """DuckDB, Postgres, and MSSQL DDL/sequence catalog queries use upfront CTEs with LEFT JOINs."""
+ loader = get_data_dictionary_loader()
+ duck_by_object = loader.get_domain_query_text("duckdb", "ddl", "by_object") or ""
+ duck_by_schema = loader.get_domain_query_text("duckdb", "ddl", "by_schema") or ""
+ pg_ddl = loader.get_domain_query_text("postgres", "ddl", "by_object") or ""
+ pg_sequences = loader.get_domain_query_text("postgres", "sequences", "by_schema") or ""
+ mssql_indexes = loader.get_domain_query_text("mssql", "ddl", "index_inputs_by_table") or ""
+ mssql_tables = loader.get_domain_query_text("mssql", "ddl", "table_inputs_by_table") or ""
+
+ for query_text in (duck_by_object, duck_by_schema, pg_ddl, pg_sequences, mssql_indexes, mssql_tables):
+ assert "WITH " in query_text
+ assert "LEFT JOIN " in query_text
+ assert "EXISTS (" not in query_text
+
+
+def test_sqlite_schema_ddl_synthesis_replays_multi_column_fks_in_memory() -> None:
+ """SQLite synthesized schema DDL groups composite foreign keys with synthetic names and executes in memory."""
+ config = SqliteConfig(connection_config={"database": ":memory:"})
+ try:
+ with config.provide_session() as session:
+ session.execute_script(
+ "CREATE TABLE accounts (tenant_id INTEGER, account_id INTEGER, PRIMARY KEY (tenant_id, account_id));"
+ "CREATE TABLE entries ("
+ " id INTEGER PRIMARY KEY,"
+ " tenant_id INTEGER NOT NULL,"
+ " account_id INTEGER NOT NULL,"
+ " amount NUMERIC(12, 2) NOT NULL,"
+ " FOREIGN KEY (tenant_id, account_id) REFERENCES accounts(tenant_id, account_id) ON DELETE CASCADE"
+ ");"
+ )
+ schema_ddl = session.data_dictionary.get_schema_ddl(session, prefer_native=False)
+ finally:
+ config.close_pool()
+
+ statements = _statements(schema_ddl)
+ assert len(statements) == 2
+ assert "entries_fk_0" in statements[1]
+ assert 'FOREIGN KEY ("tenant_id", "account_id") REFERENCES "accounts"("tenant_id", "account_id")' in statements[1]
+
+ conn = sqlite3.connect(":memory:")
+ for stmt in statements:
+ conn.executescript(stmt)
+ conn.execute("PRAGMA foreign_keys = ON")
+ conn.execute("INSERT INTO accounts VALUES (1, 42)")
+ conn.execute("INSERT INTO entries VALUES (1, 1, 42, 99.95)")
+ with pytest.raises(sqlite3.IntegrityError):
+ conn.execute("INSERT INTO entries VALUES (2, 1, 404, 10.00)")
diff --git a/tests/unit/docs/test_examples.py b/tests/unit/docs/test_examples.py
index 6bf2dee21..92d62d3b6 100644
--- a/tests/unit/docs/test_examples.py
+++ b/tests/unit/docs/test_examples.py
@@ -220,7 +220,11 @@ def test_markdown_code_blocks_and_snippets_fit_viewport_width() -> None:
if in_fence:
fence_lines.append((line_num, line))
- if "mermaid" not in fence_lang and len(line) > 84:
+ if (
+ "mermaid" not in fence_lang
+ and not rel.startswith("docs/reference/data-dictionary/dialects/")
+ and len(line) > 84
+ ):
violations.append(
f"{rel}:{line_num}: fenced code line exceeds 84 chars ({len(line)}): {line[:60]}..."
)
@@ -289,6 +293,8 @@ def test_standalone_example_executes(example_path: Path) -> None:
try:
module = _load_example_module(example_path)
except (ModuleNotFoundError, MissingDependencyError) as exc:
+ if isinstance(exc, ModuleNotFoundError) and (exc.name or "").startswith(("sqlspec", "docs")):
+ raise
pytest.skip(f"Optional dependency not installed for {rel}: {exc}")
if rel in CONFIG_ONLY_MODULES:
@@ -310,6 +316,8 @@ def test_standalone_example_executes(example_path: Path) -> None:
if inspect.iscoroutine(outcome):
asyncio.run(outcome)
except (ModuleNotFoundError, MissingDependencyError) as exc:
+ if isinstance(exc, ModuleNotFoundError) and (exc.name or "").startswith(("sqlspec", "docs")):
+ raise
pytest.skip(f"Optional dependency not installed for {rel}: {exc}")
diff --git a/tests/unit/migrations/test_commands.py b/tests/unit/migrations/test_commands.py
new file mode 100644
index 000000000..f9f206ee9
--- /dev/null
+++ b/tests/unit/migrations/test_commands.py
@@ -0,0 +1,305 @@
+"""Unit tests for migration squash validation, tracker recording, and command orchestration."""
+
+from pathlib import Path
+from unittest.mock import AsyncMock, MagicMock, patch
+
+import pytest
+
+from sqlspec.adapters.aiosqlite import AiosqliteConfig
+from sqlspec.adapters.db2.migrations import Db2SyncMigrationTracker
+from sqlspec.adapters.mssql_python.migrations import MssqlPythonSyncMigrationTracker
+from sqlspec.adapters.oracledb.migrations import OracleAsyncMigrationTracker, OracleSyncMigrationTracker
+from sqlspec.adapters.pymssql.migrations import PymssqlSyncMigrationTracker
+from sqlspec.adapters.sqlite import SqliteConfig
+from sqlspec.exceptions import SquashValidationError
+from sqlspec.migrations.commands import AsyncMigrationCommands, SyncMigrationCommands, create_migration_commands
+from sqlspec.migrations.runner import SyncMigrationRunner
+from sqlspec.migrations.squash import MigrationSquasher
+from sqlspec.migrations.tracker import AsyncMigrationTracker, SyncMigrationTracker
+
+
+def _write_sql_migration(
+ migrations_dir: Path,
+ filename: str,
+ version: str,
+ up_sql: str,
+ down_sql: str | None = None,
+ *,
+ header_lines: list[str] | None = None,
+) -> Path:
+ prefix = "\n".join(header_lines) + "\n" if header_lines else ""
+ content = f"{prefix}-- name: migrate-{version}-up\n{up_sql}\n"
+ if down_sql is not None:
+ content += f"\n-- name: migrate-{version}-down\n{down_sql}\n"
+ path = migrations_dir / filename
+ path.write_text(content, encoding="utf-8")
+ return path
+
+
+def test_schema_strategy_rejects_partial_range_in_plan_squash(tmp_path: Path) -> None:
+ """The schema squash strategy requires squashing from the first migration through head."""
+ _write_sql_migration(tmp_path, "0001_users.sql", "0001", "CREATE TABLE users (id INTEGER PRIMARY KEY);")
+ _write_sql_migration(tmp_path, "0002_posts.sql", "0002", "CREATE TABLE posts (id INTEGER PRIMARY KEY);")
+ _write_sql_migration(tmp_path, "0003_tags.sql", "0003", "CREATE TABLE tags (id INTEGER PRIMARY KEY);")
+
+ runner = SyncMigrationRunner(tmp_path)
+ squasher = MigrationSquasher(tmp_path, runner, strategy="schema")
+
+ with pytest.raises(
+ SquashValidationError, match=r"must squash from the first migration \(0001\) through head \(0003\)"
+ ):
+ squasher.plan_squash(
+ "0002",
+ "0003",
+ "partial_tail",
+ schema_ddl=(["CREATE TABLE users (id INTEGER PRIMARY KEY);"], ["DROP TABLE IF EXISTS users;"]),
+ )
+
+ with pytest.raises(
+ SquashValidationError, match=r"must squash from the first migration \(0001\) through head \(0003\)"
+ ):
+ squasher.plan_squash(
+ "0001",
+ "0002",
+ "partial_head",
+ schema_ddl=(["CREATE TABLE users (id INTEGER PRIMARY KEY);"], ["DROP TABLE IF EXISTS users;"]),
+ )
+
+
+def test_schema_strategy_rejects_unapplied_migrations_in_commands(tmp_path: Path) -> None:
+ """SyncMigrationCommands.squash(--strategy schema) requires all migrations through head to be applied."""
+ migrations_dir = tmp_path / "migrations"
+ migrations_dir.mkdir()
+ _write_sql_migration(migrations_dir, "0001_users.sql", "0001", "CREATE TABLE users (id INTEGER PRIMARY KEY);")
+ _write_sql_migration(migrations_dir, "0002_posts.sql", "0002", "CREATE TABLE posts (id INTEGER PRIMARY KEY);")
+
+ config = SqliteConfig(
+ connection_config={"database": str(tmp_path / "test.db")},
+ migration_config={"script_location": str(migrations_dir)},
+ )
+ commands: SyncMigrationCommands = create_migration_commands(config)
+ assert isinstance(commands, SyncMigrationCommands)
+
+ with pytest.raises(SquashValidationError, match="database has unapplied migrations"):
+ commands.squash("0001", "0002", "baseline", strategy="schema", yes=True)
+
+ commands.upgrade("0001")
+ with pytest.raises(SquashValidationError, match="database has unapplied migrations"):
+ commands.squash("0001", "0002", "baseline", strategy="schema", yes=True)
+
+ commands.upgrade()
+ commands.squash("0001", "0002", "baseline", strategy="schema", yes=True)
+ squashed = (migrations_dir / "0001_baseline.sql").read_text(encoding="utf-8")
+ assert "CREATE TABLE users" in squashed
+ assert "CREATE TABLE posts" in squashed
+
+
+async def test_async_schema_strategy_rejects_unapplied_migrations(tmp_path: Path) -> None:
+ """AsyncMigrationCommands.squash(--strategy schema) requires all migrations through head to be applied."""
+ migrations_dir = tmp_path / "migrations"
+ migrations_dir.mkdir()
+ _write_sql_migration(migrations_dir, "0001_users.sql", "0001", "CREATE TABLE users (id INTEGER PRIMARY KEY);")
+ _write_sql_migration(migrations_dir, "0002_posts.sql", "0002", "CREATE TABLE posts (id INTEGER PRIMARY KEY);")
+
+ config = AiosqliteConfig(
+ connection_config={"database": str(tmp_path / "async_test.db")},
+ migration_config={"script_location": str(migrations_dir)},
+ )
+ commands: AsyncMigrationCommands = create_migration_commands(config)
+ assert isinstance(commands, AsyncMigrationCommands)
+
+ try:
+ with pytest.raises(SquashValidationError, match="database has unapplied migrations"):
+ await commands.squash("0001", "0002", "baseline", strategy="schema", yes=True)
+
+ await commands.upgrade()
+ await commands.squash("0001", "0002", "baseline", strategy="schema", yes=True)
+ assert (migrations_dir / "0001_baseline.sql").exists()
+ finally:
+ await config.close_pool()
+
+
+def test_single_file_auto_splits_on_sql_directive_changes_and_rejects_forced_single_file(tmp_path: Path) -> None:
+ """Default single_file=None splits when SQL directives change while single_file=True raises an error."""
+ _write_sql_migration(
+ tmp_path, "0001_create_users.sql", "0001", "CREATE TABLE users (id INTEGER PRIMARY KEY);", "DROP TABLE users;"
+ )
+ _write_sql_migration(
+ tmp_path,
+ "0002_concurrent_idx.sql",
+ "0002",
+ "CREATE INDEX idx_users_id ON users (id);",
+ "DROP INDEX idx_users_id;",
+ header_lines=["-- transactional: false"],
+ )
+ _write_sql_migration(
+ tmp_path, "0003_create_posts.sql", "0003", "CREATE TABLE posts (id INTEGER PRIMARY KEY);", "DROP TABLE posts;"
+ )
+
+ runner = SyncMigrationRunner(tmp_path)
+ squasher = MigrationSquasher(tmp_path, runner)
+
+ auto_plans = squasher.plan_squash("0001", "0003", "release")
+ assert len(auto_plans) == 3
+ assert [plan.target_version for plan in auto_plans] == ["0001", "0002", "0003"]
+
+ split_plans = squasher.plan_squash("0001", "0003", "release", single_file=False)
+ assert len(split_plans) == 3
+
+ with pytest.raises(SquashValidationError, match="conflicting '-- transactional:' or '-- schema:' directives"):
+ squasher.plan_squash("0001", "0003", "release", single_file=True)
+
+
+def test_single_file_rejects_conflicting_schema_directives_and_python_output_with_directives(tmp_path: Path) -> None:
+ """Conflicting -- schema: directives or Python output on directive-bearing SQL migrations fail closed."""
+ _write_sql_migration(
+ tmp_path,
+ "0001_tenant_a.sql",
+ "0001",
+ "CREATE TABLE users (id INTEGER PRIMARY KEY);",
+ header_lines=["-- schema: tenant_a"],
+ )
+ _write_sql_migration(
+ tmp_path,
+ "0002_tenant_b.sql",
+ "0002",
+ "CREATE TABLE posts (id INTEGER PRIMARY KEY);",
+ header_lines=["-- schema: tenant_b"],
+ )
+
+ runner = SyncMigrationRunner(tmp_path)
+ squasher = MigrationSquasher(tmp_path, runner)
+
+ plans = squasher.plan_squash("0001", "0002", "release")
+ assert len(plans) == 2
+
+ with pytest.raises(SquashValidationError, match="conflicting '-- transactional:' or '-- schema:' directives"):
+ squasher.plan_squash("0001", "0002", "release", single_file=True)
+
+ with pytest.raises(SquashValidationError, match="Python migrations do not support SQL header directives"):
+ squasher.plan_squash("0001", "0001", "release", output_format="py")
+
+
+def test_squash_dry_run_extracts_and_prints_sql_without_modifying_files(tmp_path: Path) -> None:
+ """squash(dry_run=True) calls extract_plan_sql, prints SQL preview, and validates DDL folding."""
+ migrations_dir = tmp_path / "migrations"
+ migrations_dir.mkdir()
+ _write_sql_migration(
+ migrations_dir, "0001_users.sql", "0001", "CREATE TABLE users (id INTEGER PRIMARY KEY);", "DROP TABLE users;"
+ )
+ _write_sql_migration(migrations_dir, "0002_add_email.sql", "0002", "ALTER TABLE users ADD COLUMN email TEXT;")
+
+ config = SqliteConfig(
+ connection_config={"database": str(tmp_path / "test.db")},
+ migration_config={"script_location": str(migrations_dir)},
+ )
+ commands: SyncMigrationCommands = create_migration_commands(config)
+ assert isinstance(commands, SyncMigrationCommands)
+
+ with patch("sqlspec.migrations.commands.console.print") as mock_print:
+ commands.squash("0001", "0002", "release", strategy="ddl", dry_run=True)
+
+ printed = "\n".join(str(call.args[0]) for call in mock_print.call_args_list if call.args)
+ assert "[Preview Mode - No changes made]" in printed
+ assert "CREATE TABLE users (id INTEGER PRIMARY KEY, email TEXT)" in printed
+ assert (migrations_dir / "0001_users.sql").exists()
+ assert (migrations_dir / "0002_add_email.sql").exists()
+ assert not (migrations_dir / "0001_release.sql").exists()
+
+
+def test_squash_dry_run_surfaces_ddl_validation_errors(tmp_path: Path) -> None:
+ """squash(dry_run=True, strategy='ddl') raises SquashValidationError when DDL cannot be folded."""
+ migrations_dir = tmp_path / "migrations"
+ migrations_dir.mkdir()
+ _write_sql_migration(
+ migrations_dir, "0001_alter_missing.sql", "0001", "ALTER TABLE missing_table ADD COLUMN email TEXT;"
+ )
+
+ config = SqliteConfig(
+ connection_config={"database": str(tmp_path / "test.db")},
+ migration_config={"script_location": str(migrations_dir)},
+ )
+ commands: SyncMigrationCommands = create_migration_commands(config)
+ assert isinstance(commands, SyncMigrationCommands)
+
+ with pytest.raises(SquashValidationError, match="not a table created within"):
+ commands.squash("0001", "0001", "release", strategy="ddl", dry_run=True)
+
+
+def test_squash_prechecks_all_plans_before_writing_files(tmp_path: Path) -> None:
+ """If any split plan fails database pre-validation, no migration files on disk are modified."""
+ migrations_dir = tmp_path / "migrations"
+ migrations_dir.mkdir()
+ _write_sql_migration(migrations_dir, "0001_users.sql", "0001", "CREATE TABLE users (id INTEGER PRIMARY KEY);")
+ _write_sql_migration(
+ migrations_dir,
+ "0002_idx.sql",
+ "0002",
+ "CREATE INDEX idx_users_id ON users (id);",
+ header_lines=["-- transactional: false"],
+ )
+ before = {path.name: path.read_text(encoding="utf-8") for path in migrations_dir.iterdir()}
+
+ config = SqliteConfig(
+ connection_config={"database": str(tmp_path / "test.db")},
+ migration_config={"script_location": str(migrations_dir)},
+ )
+ commands: SyncMigrationCommands = create_migration_commands(config)
+ assert isinstance(commands, SyncMigrationCommands)
+
+ with (
+ patch.object(
+ commands.tracker,
+ "is_squash_already_applied",
+ side_effect=[False, SquashValidationError("partial squash state")],
+ ),
+ pytest.raises(SquashValidationError, match="partial squash state"),
+ ):
+ commands.squash("0001", "0002", "release", update_database=True, yes=True)
+
+ after = {path.name: path.read_text(encoding="utf-8") for path in migrations_dir.iterdir()}
+ assert after == before
+
+
+def test_tracker_record_statement_shared_across_sync_async_and_adapter_trackers() -> None:
+ """Sync, async, and adapter-specific trackers build record statements via _record_statement."""
+ sync_tracker = SyncMigrationTracker()
+ async_tracker = AsyncMigrationTracker()
+
+ stmt = sync_tracker._record_statement("0001", "sequential", 1, "initial", 12, "abc123", "tester", ["0001", "0002"])
+ async_stmt = async_tracker._record_statement(
+ "0001", "sequential", 1, "initial", 12, "abc123", "tester", ["0001", "0002"]
+ )
+ built_sync = stmt.build()
+ built_async = async_stmt.build()
+ assert "INSERT INTO" in built_sync.sql
+ assert built_sync.sql == built_async.sql
+
+ for tracker_cls in (PymssqlSyncMigrationTracker, MssqlPythonSyncMigrationTracker, OracleSyncMigrationTracker):
+ tracker = tracker_cls()
+ driver = MagicMock()
+ seq_result = MagicMock()
+ seq_result.get_data.return_value = [{"next_seq": 1}]
+ driver.execute.return_value = seq_result
+ with patch.object(tracker, "_record_statement", wraps=tracker._record_statement) as spy_stmt:
+ tracker.record_migration(driver, "0001", "init", 5, "chk")
+ spy_stmt.assert_called_once()
+ assert driver.execute.call_count == 2
+
+ db2_tracker = Db2SyncMigrationTracker()
+ db2_stmt = db2_tracker._record_statement("0001", "sequential", 1, "init", 5, "chk", "tester", ["0001"])
+ assert "INSERT INTO" in db2_stmt.build().sql
+
+
+async def test_oracle_async_tracker_uses_record_statement() -> None:
+ """OracleAsyncMigrationTracker.record_migration delegates statement construction to _record_statement."""
+ tracker = OracleAsyncMigrationTracker()
+ driver = MagicMock()
+ seq_result = MagicMock()
+ seq_result.get_data.return_value = [{"next_seq": 1}]
+ driver.execute = AsyncMock(return_value=seq_result)
+ driver.commit = AsyncMock()
+ with patch.object(tracker, "_record_statement", wraps=tracker._record_statement) as spy_stmt:
+ await tracker.record_migration(driver, "0001", "init", 5, "chk")
+ spy_stmt.assert_called_once()
+ assert driver.execute.await_count == 2
diff --git a/tests/unit/migrations/test_squash_ddl.py b/tests/unit/migrations/test_squash_ddl.py
index d81162a22..ee38c6abf 100644
--- a/tests/unit/migrations/test_squash_ddl.py
+++ b/tests/unit/migrations/test_squash_ddl.py
@@ -484,6 +484,34 @@ def test_fold_replaced_view_is_ordered_after_its_new_dependency_duckdb() -> None
],
"leaves behind the index MySQL created",
),
+ (
+ "mysql_drop_column_in_multi_column_index",
+ "mysql",
+ [
+ "CREATE TABLE items (id INT PRIMARY KEY, org_id INT, slug VARCHAR(64));",
+ "CREATE INDEX idx_items_org_slug ON items (org_id, slug);",
+ "ALTER TABLE items DROP COLUMN org_id;",
+ ],
+ "narrows a multi-column index or unique constraint",
+ ),
+ (
+ "mariadb_drop_column_in_multi_column_unique",
+ "mariadb",
+ [
+ "CREATE TABLE items (id INT PRIMARY KEY, org_id INT, slug VARCHAR(64), CONSTRAINT uq_org_slug UNIQUE (org_id, slug));",
+ "ALTER TABLE items DROP COLUMN org_id;",
+ ],
+ "narrows a multi-column index or unique constraint",
+ ),
+ (
+ "mysql_drop_column_in_inline_multi_column_index",
+ "mysql",
+ [
+ "CREATE TABLE items (id INT PRIMARY KEY, org_id INT, slug VARCHAR(64), INDEX idx_org_slug (org_id, slug));",
+ "ALTER TABLE items DROP COLUMN org_id;",
+ ],
+ "narrows a multi-column index or unique constraint",
+ ),
(
"drop_named_primary_key_constraint",
"postgres",
@@ -691,6 +719,23 @@ def test_mysql_modify_column_preserves_inline_primary_key_and_unique() -> None:
assert up_sql == ["CREATE TABLE users (id BIGINT NOT NULL PRIMARY KEY, code VARCHAR(32) NOT NULL UNIQUE);"]
+def test_mysql_drop_column_removes_single_column_index() -> None:
+ """MySQL DROP COLUMN removes single-column indexes on that column while refusing multi-column indexes."""
+ up_sql, _ = _fold(
+ [
+ "CREATE TABLE items (id INT PRIMARY KEY, legacy VARCHAR(32), active TINYINT);",
+ "CREATE INDEX idx_items_legacy ON items (legacy);",
+ "CREATE INDEX idx_items_active ON items (active);",
+ "ALTER TABLE items DROP COLUMN legacy;",
+ ],
+ "mysql",
+ )
+ assert up_sql == [
+ "CREATE TABLE items (id INT PRIMARY KEY, active TINYINT);",
+ "CREATE INDEX idx_items_active ON items (active);",
+ ]
+
+
def test_cross_schema_index_and_sequence_operations_stay_isolated() -> None:
"""Schema-qualified indexes and sequences do not collide across schemas."""
up_sql, _ = _fold(
diff --git a/tests/unit/test_import_time.py b/tests/unit/test_import_time.py
index 2494226d1..4b3a1b3df 100644
--- a/tests/unit/test_import_time.py
+++ b/tests/unit/test_import_time.py
@@ -153,14 +153,7 @@ def test_no_unintended_cross_adapter_imports() -> None:
adapters_root = Path("sqlspec/adapters")
assert adapters_root.is_dir()
- allowed_cross_imports: dict[str, set[str]] = {
- "aiomysql": {"mysql_common"},
- "asyncmy": {"mysql_common"},
- "cockroach_asyncpg": {"asyncpg"},
- "cockroach_psycopg": {"psycopg"},
- "mysqlconnector": {"mysql_common"},
- "pymysql": {"mysql_common"},
- }
+ allowed_cross_imports: dict[str, set[str]] = {"cockroach_asyncpg": {"asyncpg"}, "cockroach_psycopg": {"psycopg"}}
violations: list[str] = []
for adapter_dir in sorted(adapters_root.iterdir()):
@@ -171,21 +164,33 @@ def test_no_unintended_cross_adapter_imports() -> None:
for py_file in sorted(adapter_dir.rglob("*.py")):
tree = ast.parse(py_file.read_text(encoding="utf-8"), filename=str(py_file))
+ pkg_parts = list(py_file.parent.parts)
for node in ast.walk(tree):
- imported_module: str | None = None
- if isinstance(node, ast.ImportFrom) and node.module:
- if node.module.startswith("sqlspec.adapters."):
- imported_module = node.module.split(".")[2]
+ imported_modules: list[str] = []
+ if isinstance(node, ast.ImportFrom):
+ if node.level > 0:
+ base_parts = pkg_parts[: -(node.level - 1)] if node.level > 1 else pkg_parts
+ target_parts = [*base_parts, *node.module.split(".")] if node.module else base_parts
+ if target_parts[:2] == ["sqlspec", "adapters"]:
+ if len(target_parts) >= 3:
+ imported_modules.append(target_parts[2])
+ else:
+ imported_modules.extend(alias.name.split(".")[0] for alias in node.names)
+ elif node.module == "sqlspec.adapters":
+ imported_modules.extend(alias.name.split(".")[0] for alias in node.names)
+ elif node.module and node.module.startswith("sqlspec.adapters."):
+ imported_modules.append(node.module.split(".")[2])
elif isinstance(node, ast.Import):
- for alias in node.names:
- if alias.name.startswith("sqlspec.adapters."):
- imported_module = alias.name.split(".")[2]
-
- if imported_module and imported_module != adapter_name and imported_module not in allowed:
- violations.append(
- f"{py_file}:{getattr(node, 'lineno', '?')}: {adapter_name} imports sqlspec.adapters.{imported_module}"
+ imported_modules.extend(
+ alias.name.split(".")[2] for alias in node.names if alias.name.startswith("sqlspec.adapters.")
)
+ violations.extend(
+ f"{py_file}:{getattr(node, 'lineno', '?')}: {adapter_name} imports sqlspec.adapters.{imported_module}"
+ for imported_module in imported_modules
+ if imported_module != adapter_name and imported_module not in allowed
+ )
+
assert not violations, "Found cross-adapter imports:\n" + "\n".join(violations)
diff --git a/tests/unit/tools/test_cli_docs.py b/tests/unit/tools/test_cli_docs.py
index 7eb4f6e22..10a2caa33 100644
--- a/tests/unit/tools/test_cli_docs.py
+++ b/tests/unit/tools/test_cli_docs.py
@@ -6,11 +6,14 @@
import pytest
from tools.docs.generate_cli_docs import main as cli_docs_main
from tools.docs.generate_cli_docs import render_cli_reference
+from tools.docs.generate_data_dictionary_docs import DIALECT_SPECS, render_all_dialect_docs
+from tools.docs.generate_data_dictionary_docs import main as dialect_docs_main
import sqlspec.cli as cli_module
__all__ = (
"test_cli_docs_main_writes_and_checks_staleness",
+ "test_dialect_docs_generator_renders_all_dialects_and_checks_staleness",
"test_lazy_cli_attribute_resolves_click_group",
"test_render_cli_reference_renders_all_commands_arguments_and_options_cleanly",
)
@@ -90,3 +93,33 @@ def test_cli_docs_main_writes_and_checks_staleness(tmp_path: Path, capsys: pytes
stale_rc = cli_docs_main(["--check", "--output", str(output_file)])
assert stale_rc == 1
assert "out of date" in capsys.readouterr().err.lower()
+
+
+def test_dialect_docs_generator_renders_all_dialects_and_checks_staleness(
+ tmp_path: Path, capsys: pytest.CaptureFixture[str]
+) -> None:
+ rendered = render_all_dialect_docs()
+ assert "index.md" in rendered
+ assert len(rendered) == len(DIALECT_SPECS) + 1
+ assert "spanner" in rendered["index.md"]
+ assert "columns.sql" in rendered["postgres.md"]
+ assert "SYS.SYSTABCOL" in rendered["sqlanywhere.md"]
+ assert "sysobjects" in rendered["sybase.md"]
+
+ out_dir = tmp_path / "dialects"
+ missing_rc = dialect_docs_main(["--check", "--output-dir", str(out_dir)])
+ assert missing_rc == 1
+ assert "missing" in capsys.readouterr().err.lower()
+
+ write_rc = dialect_docs_main(["--output-dir", str(out_dir)])
+ assert write_rc == 0
+ assert (out_dir / "index.md").is_file()
+ assert (out_dir / "postgres.md").is_file()
+
+ clean_rc = dialect_docs_main(["--check", "--output-dir", str(out_dir)])
+ assert clean_rc == 0
+
+ (out_dir / "postgres.md").write_text("# stale\n", encoding="utf-8")
+ stale_rc = dialect_docs_main(["--check", "--output-dir", str(out_dir)])
+ assert stale_rc == 1
+ assert "out of date" in capsys.readouterr().err.lower()
diff --git a/tests/unit/tools/test_docs_tooling.py b/tests/unit/tools/test_docs_tooling.py
index aaa5979ae..4278095b7 100644
--- a/tests/unit/tools/test_docs_tooling.py
+++ b/tests/unit/tools/test_docs_tooling.py
@@ -183,7 +183,7 @@ def test_verify_site_artifacts_rejects_oversized_pages(tmp_path: Path) -> None:
site_dir = tmp_path / "site"
_write_clean_site(site_dir)
bloated_ref = site_dir / "reference" / "bloated.html"
- bloated_ref.write_text("" + ("a" * 125_000) + "", encoding="utf-8")
+ bloated_ref.write_text("" + ("a" * 660_000) + "", encoding="utf-8")
with pytest.raises(DocsVerificationError, match=r"bloated\.html"):
verify_site_artifacts(site_dir)
@@ -192,8 +192,8 @@ def test_verify_site_artifacts_rejects_oversized_pages(tmp_path: Path) -> None:
def test_verify_site_artifacts_rejects_bloated_secondary_toc(tmp_path: Path) -> None:
site_dir = tmp_path / "site"
_write_clean_site(site_dir)
- toc_links = "".join(f'm_{idx} ' for idx in range(15))
- headings = "".join(f'm_{idx} ' for idx in range(15))
+ toc_links = "".join(f'm_{idx} ' for idx in range(17))
+ headings = "".join(f'm_{idx} ' for idx in range(17))
bloated_toc = site_dir / "reference" / "methods.html"
bloated_toc.write_text(
f' {headings}',
diff --git a/tests/unit/utils/test_fixtures.py b/tests/unit/utils/test_fixtures.py
index 0890b8eb9..14e53d27d 100644
--- a/tests/unit/utils/test_fixtures.py
+++ b/tests/unit/utils/test_fixtures.py
@@ -26,6 +26,7 @@
import sqlspec.utils.fixtures as fixture_module
from sqlspec.adapters.aiosqlite import AiosqliteConfig
+from sqlspec.adapters.db2.data_dictionary import build_column_metadata as build_db2_column_metadata
from sqlspec.adapters.duckdb import DuckDBConfig
from sqlspec.adapters.sqlite import SqliteConfig
from sqlspec.core.parameters import TypedParameter
@@ -2917,3 +2918,256 @@ def test_sybase_and_sqlanywhere_value_decoders(tmp_path: Path) -> None:
assert loaded_row["ext_id"] == UUID("7f3c1d2e-9a4b-4c5d-8e6f-0a1b2c3d4e5f")
assert loaded_row["raw_image"] == b"\x00\xff"
assert loaded_row["raw_long_bin"] == b"\x00\xff"
+
+
+def test_mysql_vector_export_unpacks_float32_bytes(tmp_path: Path) -> None:
+ """MySQL binary VECTOR column bytes unpack into float lists during fixture export."""
+ packed = struct.pack("<3f", 1.5, -2.0, 3.25)
+ cols = [
+ fixture_module._TableColumn(
+ name="id",
+ data_type="int",
+ is_primary=True,
+ is_identity=False,
+ identity_always=False,
+ sequence_name=None,
+ is_generated=False,
+ ),
+ fixture_module._TableColumn(
+ name="embedding",
+ data_type="vector(3)",
+ is_primary=False,
+ is_identity=False,
+ identity_always=False,
+ sequence_name=None,
+ is_generated=False,
+ ),
+ ]
+ rows = fixture_module._without_generated_values([{"id": 1, "embedding": packed}], cols, family="mysql")
+ assert rows == [{"id": 1, "embedding": [1.5, -2.0, 3.25]}]
+
+ driver = MagicMock()
+ driver.statement_config.dialect = "mysql"
+ driver.data_dictionary.get_columns.return_value = [
+ {"column_name": "id", "data_type": "int", "is_primary": True},
+ {"column_name": "embedding", "data_type": "vector(3)", "is_primary": False},
+ ]
+ driver.select.return_value = [{"id": 1, "embedding": packed}]
+
+ export_table_fixtures_sync(driver, tmp_path, ["items"], compress=False)
+ exported = json.loads((tmp_path / "items.json").read_text(encoding="utf-8"))
+ assert exported == [{"id": 1, "embedding": [1.5, -2.0, 3.25]}]
+
+
+def test_pgvector_halfvec_and_sparsevec_export_and_decode() -> None:
+ """pgvector HalfVector and SparseVector round-trip through export and sparsevec formatting."""
+ half = _FakeHalfVector([0.5, 1.5, 2.5])
+ sparse = _FakeSparseVector([0.0, 1.25, 0.0, -3.5])
+ cols = [
+ fixture_module._TableColumn(
+ name="id",
+ data_type="integer",
+ is_primary=True,
+ is_identity=False,
+ identity_always=False,
+ sequence_name=None,
+ is_generated=False,
+ ),
+ fixture_module._TableColumn(
+ name="half_col",
+ data_type="halfvec(3)",
+ is_primary=False,
+ is_identity=False,
+ identity_always=False,
+ sequence_name=None,
+ is_generated=False,
+ ),
+ fixture_module._TableColumn(
+ name="sparse_col",
+ data_type="sparsevec(4)",
+ is_primary=False,
+ is_identity=False,
+ identity_always=False,
+ sequence_name=None,
+ is_generated=False,
+ ),
+ ]
+ exported_rows = fixture_module._without_generated_values(
+ [{"id": 1, "half_col": half, "sparse_col": sparse}], cols, family="postgres"
+ )
+ assert exported_rows == [{"id": 1, "half_col": [0.5, 1.5, 2.5], "sparse_col": [0.0, 1.25, 0.0, -3.5]}]
+ assert fixture_module._exportable_value(half) == [0.5, 1.5, 2.5]
+ assert fixture_module._exportable_value(sparse) == [0.0, 1.25, 0.0, -3.5]
+
+ half_decoder = fixture_module._column_value_decoder("postgres", "halfvec(3)")
+ sparse_decoder = fixture_module._column_value_decoder("postgres", "sparsevec(4)")
+ assert half_decoder is not None
+ assert sparse_decoder is not None
+ assert half_decoder(half) == "[0.5,1.5,2.5]"
+ assert half_decoder([0.5, 1.5, 2.5]) == "[0.5,1.5,2.5]"
+ assert sparse_decoder(sparse) == "{2:1.25,4:-3.5}/4"
+ assert sparse_decoder([0.0, 1.25, 0.0, -3.5]) == "{2:1.25,4:-3.5}/4"
+ assert sparse_decoder("{2:1.25,4:-3.5}/4") == "{2:1.25,4:-3.5}/4"
+ assert fixture_module._vector_items("{2:1.25,4:-3.5}/4") == [0.0, 1.25, 0.0, -3.5]
+
+
+def test_oracle_vector_decoder_uses_double_for_float64_and_unannotated() -> None:
+ """Oracle vector decoder returns array('d') for FLOAT64 or unannotated VECTOR and array('f') for FLOAT32."""
+ dec_f32 = fixture_module._column_value_decoder("oracle", "vector(3, float32)")
+ dec_f64 = fixture_module._column_value_decoder("oracle", "vector(3, float64)")
+ dec_unannotated = fixture_module._column_value_decoder("oracle", "vector(3)")
+ dec_bare = fixture_module._column_value_decoder("oracle", "vector")
+
+ assert dec_f32 is not None
+ assert dec_f64 is not None
+ assert dec_unannotated is not None
+ assert dec_bare is not None
+
+ arr_f32 = dec_f32([1.0, 2.0, 3.0])
+ arr_f64 = dec_f64([1.0, 2.0, 3.0])
+ arr_unannotated = dec_unannotated([1.0, 2.0, 3.0])
+ arr_bare = dec_bare([1.0, 2.0, 3.0])
+
+ assert isinstance(arr_f32, array.array) and arr_f32.typecode == "f"
+ assert isinstance(arr_f64, array.array) and arr_f64.typecode == "d"
+ assert isinstance(arr_unannotated, array.array) and arr_unannotated.typecode == "d"
+ assert isinstance(arr_bare, array.array) and arr_bare.typecode == "d"
+
+
+def test_catalog_columns_sql_and_db2_metadata_completeness() -> None:
+ """Dialect columns.sql queries and Db2 build_column_metadata preserve identity and generated metadata."""
+ repo_root = Path(fixture_module.__file__).resolve().parents[2]
+ mssql_sql = (repo_root / "sqlspec/data_dictionary/dialects/mssql/sql/columns.sql").read_text(encoding="utf-8")
+ assert "CASE WHEN cc.object_id IS NOT NULL OR c.generated_always_type <> 0 THEN 1 ELSE 0 END" in mssql_sql
+ assert "generated_always_type_desc" not in mssql_sql
+
+ spanner_sql = (repo_root / "sqlspec/data_dictionary/dialects/spanner/sql/columns.sql").read_text(encoding="utf-8")
+ assert "WITH pk_cols AS" in spanner_sql
+ assert "LEFT JOIN pk_cols" in spanner_sql
+ assert "AS is_primary" in spanner_sql
+ assert "AS is_identity" in spanner_sql
+ assert "AS is_generated" in spanner_sql
+ assert "c.generation_expression" in spanner_sql
+
+ sqlite_config = SqliteConfig(connection_config={"database": ":memory:"})
+ with sqlite_config.provide_session() as driver:
+ driver.execute("CREATE TABLE items (id INTEGER PRIMARY KEY AUTOINCREMENT, code INTEGER, label TEXT)")
+ columns = driver.data_dictionary.get_columns(driver, "items")
+ col_map = {col["column_name"]: col for col in columns}
+ assert bool(col_map["id"].get("is_identity")) is True
+ assert bool(col_map["code"].get("is_identity")) is False
+
+ table_cols = fixture_module._table_columns_sync(driver, "sqlite", "items")
+ id_col = next(col for col in table_cols if col.name == "id")
+ assert id_col.is_identity is True
+
+ db2_rows = build_db2_column_metadata([
+ {
+ "column_name": "ID",
+ "data_type": "INTEGER",
+ "is_nullable": "NO",
+ "is_primary": 1,
+ "is_identity": 1,
+ "identity_generation": "ALWAYS",
+ "is_generated": 0,
+ "generation_expression": None,
+ "column_comment": "Primary identifier",
+ },
+ {
+ "column_name": "TOTAL",
+ "data_type": "INTEGER",
+ "is_nullable": "YES",
+ "is_primary": 0,
+ "is_identity": 0,
+ "identity_generation": None,
+ "is_generated": 1,
+ "generation_expression": "GENERATED ALWAYS AS (A + B)",
+ "column_comment": "Computed sum",
+ },
+ ])
+ assert db2_rows[0].get("is_identity") is True
+ assert db2_rows[0].get("column_comment") == "Primary identifier"
+ assert db2_rows[1].get("is_identity") is False
+ assert db2_rows[1].get("generation_expression") == "GENERATED ALWAYS AS (A + B)"
+ assert db2_rows[1].get("column_comment") == "Computed sum"
+
+ parsed_id_col = fixture_module._table_column(dict(db2_rows[0]))
+ parsed_gen_col = fixture_module._table_column(dict(db2_rows[1]))
+ assert parsed_id_col.is_identity is True
+ assert parsed_id_col.is_generated is False
+ assert parsed_gen_col.is_identity is False
+ assert parsed_gen_col.is_generated is True
+
+
+def test_write_table_fixture_unlinks_stale_shards_before_renaming_tmp(tmp_path: Path) -> None:
+ """Stale shards are unlinked before renaming temporary shard files into place."""
+ stale_shard = tmp_path / "events.002.jsonl"
+ stale_shard.write_text('{"id": 99}\n', encoding="utf-8")
+
+ observed_stale_during_replace: list[bool] = []
+ original_replace = Path.replace
+
+ def tracking_replace(self: Path, target: Path) -> Path:
+ observed_stale_during_replace.append(stale_shard.exists())
+ return original_replace(self, target)
+
+ with patch.object(Path, "replace", tracking_replace):
+ fixture_module._write_table_fixture(
+ tmp_path, "events", [{"id": 1}, {"id": 2}], compress=False, jsonl=True, max_rows_per_shard=10
+ )
+
+ assert observed_stale_during_replace == [False]
+ assert (tmp_path / "events.jsonl").exists()
+ assert not stale_shard.exists()
+
+
+def test_sequence_resync_step_planner_is_shared_and_debloated() -> None:
+ """Sync and async sequence resync share _plan_sequence_resync_steps without dead _PG_SEQUENCE_DIALECTS."""
+ assert not hasattr(fixture_module, "_PG_SEQUENCE_DIALECTS")
+ col = fixture_module._TableColumn(
+ name="id",
+ data_type="integer",
+ is_primary=True,
+ is_identity=True,
+ identity_always=True,
+ sequence_name=None,
+ is_generated=False,
+ )
+ sybase_steps = fixture_module._plan_sequence_resync_steps("users", [col], dialect_name="sybase", family="sybase")
+ assert len(sybase_steps) == 1
+ assert sybase_steps[0].skip_when_zero is True
+ assert sybase_steps[0].statement.format(next_val=42) == "EXEC sp_chgattribute 'users', 'identity_burn_max', 0, '42'"
+
+ sa_steps = fixture_module._plan_sequence_resync_steps(
+ "DBA.users", [col], dialect_name="sqlanywhere", family="sqlanywhere"
+ )
+ assert len(sa_steps) == 1
+ assert sa_steps[0].skip_when_zero is False
+ assert sa_steps[0].statement.format(next_val=15) == "CALL sa_reset_identity('users', 'DBA', 15)"
+
+
+class _FakeHalfVector:
+ """Stub representing pgvector.HalfVector without a numpy tolist() method."""
+
+ def __init__(self, values: list[float]) -> None:
+ self._values = values
+
+ def to_list(self) -> list[float]:
+ return list(self._values)
+
+ def to_text(self) -> str:
+ return "[" + ",".join(str(v) for v in self._values) + "]"
+
+
+class _FakeSparseVector:
+ """Stub representing pgvector.SparseVector with to_list() and to_text()."""
+
+ def __init__(self, values: list[float]) -> None:
+ self._values = values
+
+ def to_list(self) -> list[float]:
+ return list(self._values)
+
+ def to_text(self) -> str:
+ nonzero = [f"{idx}:{val}" for idx, val in enumerate(self._values, start=1) if val != 0.0]
+ return f"{{{','.join(nonzero)}}}/{len(self._values)}"
diff --git a/tools/docs/generate_data_dictionary_docs.py b/tools/docs/generate_data_dictionary_docs.py
new file mode 100644
index 000000000..626c8c08d
--- /dev/null
+++ b/tools/docs/generate_data_dictionary_docs.py
@@ -0,0 +1,479 @@
+"""Generate Markdown reference documentation for all SQLSpec data dictionary dialects and SQL catalogs."""
+
+import argparse
+import re
+import sys
+from pathlib import Path
+
+import msgspec
+
+from sqlspec.data_dictionary._registry import get_dialect_config
+from sqlspec.data_dictionary._types import DialectConfig
+
+__all__ = (
+ "DEFAULT_OUTPUT_DIR",
+ "DIALECT_SPECS",
+ "DialectDocSpec",
+ "main",
+ "render_all_dialect_docs",
+ "render_dialect_index",
+ "render_dialect_page",
+)
+
+DEFAULT_OUTPUT_DIR = Path("docs/reference/data-dictionary/dialects")
+DIALECTS_SOURCE_DIR = Path("sqlspec/data_dictionary/dialects")
+
+_QUERY_NAME_PATTERN = re.compile(r"^--\s*name:\s*([A-Za-z0-9_-]+)\s*$", re.MULTILINE)
+_COLON_PARAM_PATTERN = re.compile(r"(? dict[str, str]:
+ """Render the dialect index and all 13 per-dialect Markdown pages.
+
+ Returns:
+ Mapping of filename (such as ``"index.md"`` or ``"postgres.md"``) to Markdown content.
+ """
+ rendered: dict[str, str] = {"index.md": render_dialect_index()}
+ for spec in DIALECT_SPECS:
+ rendered[f"{spec.dialect}.md"] = render_dialect_page(spec)
+ return rendered
+
+
+def render_dialect_index() -> str:
+ """Render the cross-dialect overview and SQL catalog matrix page.
+
+ Returns:
+ Markdown string for ``docs/reference/data-dictionary/dialects/index.md``.
+ """
+ lines: list[str] = [
+ "# Dialect SQL Catalogs",
+ "",
+ "```python",
+ "from sqlspec.data_dictionary import DialectConfig, get_data_dictionary_loader, get_dialect_config",
+ "```",
+ "",
+ "SQLSpec ships 13 dialect-specific SQL catalog packs under `sqlspec/data_dictionary/dialects/` containing named [`SQLFileLoader`](../../loader.md) queries (`-- name: ...`), version-gated feature profiles (`DialectConfig`), and logical-to-physical type mappings. See the [Data Dictionary Guide](../../../learn/data-dictionary.md) and [Data Dictionary Reference](../../data-dictionary.md) for programmatic usage.",
+ "",
+ "## Dialect Catalog Matrix",
+ "",
+ "| Dialect | Default Schema | DDL Resolution | Domain `.sql` Files | Named Queries | Reference |",
+ "| --- | --- | --- | --- | --- | --- |",
+ ]
+
+ total_files = 0
+ total_queries = 0
+ for spec in DIALECT_SPECS:
+ config = get_dialect_config(spec.dialect)
+ sql_files = _collect_sql_files(spec.dialect)
+ file_count = len(sql_files)
+ query_count = sum(len(_extract_query_names(path.read_text(encoding="utf-8"))) for _, path in sql_files)
+ total_files += file_count
+ total_queries += query_count
+ default_schema = f"`{config.default_schema}`" if config.default_schema else "`None`"
+ lines.append(
+ f"| **{spec.title}** (`{spec.dialect}`) | {default_schema} | {spec.ddl_strategy} | `{file_count}` | `{query_count}` | [{spec.title} Catalog]({spec.dialect}.md) |"
+ )
+
+ lines.extend([
+ f"| **Total (`{len(DIALECT_SPECS)}` dialects)** | - | - | **`{total_files}`** | **`{total_queries}`** | - |",
+ "",
+ "## Cross-Dialect Capability Matrix",
+ "",
+ "Each dialect pack declares boolean `feature_flags` and version-gated `feature_versions` inside its `DialectConfig`:",
+ "",
+ "| Dialect | Schemas | Returning | Upsert | Window | CTE | JSON | Arrays | Vectors | Transactions |",
+ "| --- | --- | --- | --- | --- | --- | --- | --- | --- | --- |",
+ ])
+
+ feature_cols = (
+ "supports_schemas",
+ "supports_returning",
+ "supports_upsert",
+ "supports_window_functions",
+ "supports_cte",
+ "supports_json",
+ "supports_arrays",
+ "supports_vectors",
+ "supports_transactions",
+ )
+ for spec in DIALECT_SPECS:
+ config = get_dialect_config(spec.dialect)
+ cells = [_format_capability_cell(config, feat) for feat in feature_cols]
+ lines.append(f"| [{spec.title}]({spec.dialect}.md) | " + " | ".join(cells) + " |")
+
+ lines.extend([
+ "",
+ "## Logical-to-Physical Type Mappings",
+ "",
+ "`driver.data_dictionary.get_optimal_type(driver, type_category)` resolves portable logical type categories using each dialect's `DialectConfig.type_mappings` and active engine version:",
+ "",
+ "| Dialect | `json` | `uuid` | `boolean` | `timestamp` | `text` | `blob` | `vector` |",
+ "| --- | --- | --- | --- | --- | --- | --- | --- |",
+ ])
+
+ type_cols = ("json", "uuid", "boolean", "timestamp", "text", "blob", "vector")
+ for spec in DIALECT_SPECS:
+ config = get_dialect_config(spec.dialect)
+ cells = [f"`{config.type_mappings.get(col, '-')}`" for col in type_cols]
+ lines.append(f"| [{spec.title}]({spec.dialect}.md) | " + " | ".join(cells) + " |")
+
+ return "\n".join(lines).rstrip() + "\n"
+
+
+def render_dialect_page(spec: DialectDocSpec) -> str:
+ """Render a single dialect's configuration, adapter classes, and SQL catalog queries.
+
+ Args:
+ spec: Dialect documentation specification.
+
+ Returns:
+ Markdown string for ``docs/reference/data-dictionary/dialects/.md``.
+ """
+ config = get_dialect_config(spec.dialect)
+ sql_files = _collect_sql_files(spec.dialect)
+ total_queries = sum(len(_extract_query_names(path.read_text(encoding="utf-8"))) for _, path in sql_files)
+ default_schema = f'`"{config.default_schema}"`' if config.default_schema else "`None`"
+
+ lines: list[str] = [
+ f"# {spec.title} Data Dictionary & SQL Catalog",
+ "",
+ "```python",
+ f'from sqlspec.data_dictionary import get_data_dictionary_loader, get_dialect_config\n\nconfig = get_dialect_config("{spec.dialect}")',
+ "```",
+ "",
+ f"{spec.summary} See the [{spec.title} Guide]({spec.guide_link}) and [Data Dictionary Guide](../../../learn/data-dictionary.md) for usage examples.",
+ "",
+ "## Dialect Configuration",
+ "",
+ "| Setting | Value |",
+ "| --- | --- |",
+ f"| **Dialect Name** | `{config.name}` |",
+ f"| **Default Schema** | {default_schema} |",
+ f"| **Version Pattern** | `{config.version_pattern.pattern}` |",
+ f"| **DDL Strategy** | {spec.ddl_strategy} |",
+ f"| **Catalog `.sql` Files** | `{len(sql_files)}` files (`{total_queries}` named queries) |",
+ "",
+ ]
+
+ lines.extend(_render_feature_tables(config))
+
+ if spec.adapter_classes:
+ lines.extend(["## Adapter Data Dictionary Classes", ""])
+ for cls_path in spec.adapter_classes:
+ lines.extend([f"::: {cls_path}", " options:", " heading_level: 3", ""])
+
+ lines.extend([
+ "## Catalog SQL Queries",
+ "",
+ f"The `{spec.dialect}` dialect pack defines `{total_queries}` named queries across `{len(sql_files)}` domain `.sql` files loaded via `DataDictionaryLoader` (`sqlspec/data_dictionary/dialects/{spec.dialect}/`):",
+ "",
+ "| Domain Pack | Named Queries | Bind Parameters |",
+ "| --- | --- | --- |",
+ ])
+
+ for label, path in sql_files:
+ text = path.read_text(encoding="utf-8")
+ query_names = _extract_query_names(text)
+ params = _extract_bind_params(text)
+ queries_cell = ", ".join(f"`{name}`" for name in query_names) if query_names else "-"
+ params_cell = ", ".join(f"`{p}`" for p in params) if params else "*none*"
+ lines.append(f"| `{label}` | {queries_cell} | {params_cell} |")
+
+ lines.append("")
+
+ for label, path in sql_files:
+ text = path.read_text(encoding="utf-8").strip()
+ query_names = _extract_query_names(text)
+ lines.extend([f'??? code "`{label}` ({len(query_names)} named queries)"', "", " ```sql"])
+ lines.extend(f" {sql_line}" if sql_line else "" for sql_line in text.splitlines())
+ lines.extend([" ```", ""])
+
+ return "\n".join(lines).rstrip() + "\n"
+
+
+def main(argv: list[str] | None = None) -> int:
+ """CLI entry point for generating or checking Data Dictionary dialect reference pages.
+
+ Args:
+ argv: Optional command-line arguments.
+
+ Returns:
+ Process exit code (0 on success, 1 when stale or missing under ``--check``).
+ """
+ parser = argparse.ArgumentParser(
+ description="Generate or check docs/reference/data-dictionary/dialects/*.md reference pages."
+ )
+ parser.add_argument(
+ "--output-dir",
+ type=Path,
+ default=DEFAULT_OUTPUT_DIR,
+ help="Target directory for generated dialect Markdown pages.",
+ )
+ parser.add_argument(
+ "--check",
+ action="store_true",
+ help="Verify that all generated dialect Markdown files are up to date without writing.",
+ )
+ args = parser.parse_args(argv)
+ output_dir: Path = args.output_dir
+ expected_files = render_all_dialect_docs()
+
+ if args.check:
+ stale: list[str] = []
+ for filename, expected in expected_files.items():
+ target = output_dir / filename
+ if not target.is_file():
+ stale.append(f"missing: {target}")
+ continue
+ actual = target.read_text(encoding="utf-8")
+ if actual != expected:
+ stale.append(f"out of date: {target}")
+ if stale:
+ for item in stale:
+ sys.stderr.write(f"Data dictionary dialect docs {item}\n")
+ sys.stderr.write("Run 'uv run python -m tools.docs.generate_data_dictionary_docs' to regenerate.\n")
+ return 1
+ sys.stdout.write(
+ f"Data dictionary dialect docs are up to date ({len(expected_files)} files in {output_dir}).\n"
+ )
+ return 0
+
+ output_dir.mkdir(parents=True, exist_ok=True)
+ for filename, content in expected_files.items():
+ (output_dir / filename).write_text(content, encoding="utf-8")
+ sys.stdout.write(f"Wrote {len(expected_files)} data dictionary dialect docs to {output_dir}\n")
+ return 0
+
+
+def _collect_sql_files(dialect: str) -> list[tuple[str, Path]]:
+ """Collect all domain .sql files for a dialect in deterministic order."""
+ base_dir = DIALECTS_SOURCE_DIR / dialect
+ sql_dir = base_dir / "sql"
+ if sql_dir.is_dir():
+ return [(path.name, path) for path in sorted(sql_dir.glob("*.sql"))]
+
+ collected: list[tuple[str, Path]] = []
+ for submode_dir in sorted(p for p in base_dir.iterdir() if p.is_dir() and (p / "sql").is_dir()):
+ collected.extend(
+ (f"{submode_dir.name}/{path.name}", path) for path in sorted((submode_dir / "sql").glob("*.sql"))
+ )
+ return collected
+
+
+def _extract_query_names(sql_text: str) -> list[str]:
+ """Extract all -- name: identifiers from a SQL catalog file."""
+ return _QUERY_NAME_PATTERN.findall(sql_text)
+
+
+def _extract_bind_params(sql_text: str) -> list[str]:
+ """Extract sorted bind parameter placeholders (:param or @param) from SQL text."""
+ lines_without_comments = [line for line in sql_text.splitlines() if not line.lstrip().startswith("--")]
+ body = "\n".join(lines_without_comments)
+ params: set[str] = set()
+ for match in _COLON_PARAM_PATTERN.findall(body):
+ params.add(f":{match}")
+ for match in _AT_PARAM_PATTERN.findall(body):
+ params.add(f"@{match}")
+ return sorted(params)
+
+
+def _format_capability_cell(config: DialectConfig, feature: str) -> str:
+ """Format a capability matrix cell combining static feature_flags and version gates."""
+ if feature in config.feature_versions:
+ return f"`>={config.feature_versions[feature]}`"
+ if feature in config.feature_flags:
+ return "`Yes`" if config.feature_flags[feature] else "`No`"
+ return "`No`"
+
+
+def _render_feature_tables(config: DialectConfig) -> list[str]:
+ """Render Markdown tables for feature flags, version gates, and type mappings."""
+ lines: list[str] = [
+ "### Capabilities & Type Mappings",
+ "",
+ "| Capability | Status / Minimum Version | Logical Type | Target Physical Type |",
+ "| --- | --- | --- | --- |",
+ ]
+ all_features: list[tuple[str, str]] = []
+ for key, flag in sorted(config.feature_flags.items()):
+ all_features.append((f"`{key}`", "`True`" if flag else "`False`"))
+ for key, ver in sorted(config.feature_versions.items()):
+ all_features.append((f"`{key}`", f"`>= {ver}`"))
+
+ all_types = [(f"`{k}`", f"`{v}`") for k, v in sorted(config.type_mappings.items())]
+ max_rows = max(len(all_features), len(all_types), 1)
+ for idx in range(max_rows):
+ feat_name, feat_val = all_features[idx] if idx < len(all_features) else ("-", "-")
+ type_name, type_val = all_types[idx] if idx < len(all_types) else ("-", "-")
+ lines.append(f"| {feat_name} | {feat_val} | {type_name} | {type_val} |")
+ lines.append("")
+ return lines
+
+
+if __name__ == "__main__":
+ raise SystemExit(main())
diff --git a/tools/docs/templates/python/material/attribute.html.jinja b/tools/docs/templates/python/material/attribute.html.jinja
index d030c450f..d0e7d8dfb 100644
--- a/tools/docs/templates/python/material/attribute.html.jinja
+++ b/tools/docs/templates/python/material/attribute.html.jinja
@@ -1,5 +1,4 @@
-{#- Skip individual attribute blocks on classes when summary/TypedDict renders the Attributes table. -#}
-{% set parent_is_typed_dict = attribute.parent and attribute.parent.is_class and ('TypedDict' in (attribute.parent.bases | map('string') | join(' ')) or 'Params' in attribute.parent.name or 'Features' in attribute.parent.name or (attribute.parent.name.endswith('Config') and not attribute.parent.functions)) %}
-{% if root or not (attribute.parent and attribute.parent.is_class and (config.summary.attributes or parent_is_typed_dict)) %}
+{#- Skip individual attribute blocks on classes since summary.html.jinja renders the compact Attributes table. -#}
+{% if root or not (attribute.parent and attribute.parent.is_class) %}
{% include "_base/attribute.html.jinja" with context %}
{% endif %}
diff --git a/tools/docs/templates/python/material/expression.html.jinja b/tools/docs/templates/python/material/expression.html.jinja
index 544c9b41e..5f396f955 100644
--- a/tools/docs/templates/python/material/expression.html.jinja
+++ b/tools/docs/templates/python/material/expression.html.jinja
@@ -20,7 +20,7 @@
{{ prefix }}
{%- if not signature and (backlink_type or path.startswith("sqlspec.")) -%}
{{ title }}
- {%- elif signature and config.signature_crossrefs -%}
+ {%- elif signature and config.signature_crossrefs and path.startswith("sqlspec.") -%}
{%- filter stash_crossref(length=title|length) -%}
{{ title }}
{%- endfilter -%}
diff --git a/tools/docs/templates/python/material/summary.html.jinja b/tools/docs/templates/python/material/summary.html.jinja
index 5ea44e5e8..5021890d3 100644
--- a/tools/docs/templates/python/material/summary.html.jinja
+++ b/tools/docs/templates/python/material/summary.html.jinja
@@ -1,4 +1,4 @@
-{#- Template for auto-summaries, automatically rendering Attributes tables on TypedDict schemas. -#}
+{#- Template for auto-summaries, rendering compact Attributes and Methods tables on classes. -#}
{% with members_list = config.members if root_members else None %}
{% if config.summary.modules %}
{% include "summary/modules.html.jinja" with context %}
@@ -12,12 +12,11 @@
{% include "summary/classes.html.jinja" with context %}
{% endif %}
- {% if config.summary.functions %}
+ {% if config.summary.functions or (obj.is_class and (obj.functions | length) >= 3) %}
{% include "summary/functions.html.jinja" with context %}
{% endif %}
- {% set is_typed_dict = obj.is_class and ('TypedDict' in (obj.bases | map('string') | join(' ')) or 'Params' in obj.name or 'Features' in obj.name or (obj.name.endswith('Config') and not obj.functions)) %}
- {% if config.summary.attributes or is_typed_dict %}
+ {% if config.summary.attributes or obj.is_class %}
{% include "summary/attributes.html.jinja" with context %}
{% endif %}
{% endwith %}
diff --git a/tools/docs/verify_docs_artifacts.py b/tools/docs/verify_docs_artifacts.py
index d8c8da130..2cdf6e1ad 100644
--- a/tools/docs/verify_docs_artifacts.py
+++ b/tools/docs/verify_docs_artifacts.py
@@ -1,6 +1,7 @@
"""HTML quality, inventory, TOC budget, and anchor integrity verifier for built Zensical docs."""
import argparse
+import html
import sys
from html.parser import HTMLParser
from pathlib import Path
@@ -31,7 +32,7 @@ def __init__(self, violations: list[str]) -> None:
def verify_site_artifacts(
- site_dir: Path, *, max_page_bytes: int = 180_000, max_reference_bytes: int = 120_000, max_toc_items: int = 14
+ site_dir: Path, *, max_page_bytes: int = 180_000, max_reference_bytes: int = 650_000, max_toc_items: int = 16
) -> list[str]:
"""Verify built Zensical site artifacts and raise DocsVerificationError on violations."""
violations: list[str] = []
@@ -53,6 +54,7 @@ def verify_site_artifacts(
raw_bytes = html_file.read_bytes()
byte_len = len(raw_bytes)
text = raw_bytes.decode("utf-8", errors="replace")
+ unescaped_text = html.unescape(text)
if not _is_size_exempt(rel_posix):
limit = max_reference_bytes if rel_posix.startswith("reference/") else max_page_bytes
@@ -62,7 +64,7 @@ def verify_site_artifacts(
violations.extend(
f"{rel_posix}: contains banned documentation leak phrase {phrase!r}"
for phrase in BANNED_PHRASES
- if phrase in text
+ if phrase in unescaped_text
)
parser = _PageArtifactParser()
@@ -108,10 +110,10 @@ def main(argv: list[str] | None = None) -> int:
parser.add_argument("--site-dir", type=Path, default=Path("site"), help="Built site directory.")
parser.add_argument("--max-page-bytes", type=int, default=180_000, help="Max HTML byte budget for guide pages.")
parser.add_argument(
- "--max-reference-bytes", type=int, default=120_000, help="Max HTML byte budget for API reference pages."
+ "--max-reference-bytes", type=int, default=650_000, help="Max HTML byte budget for API reference pages."
)
parser.add_argument(
- "--max-toc-items", type=int, default=14, help="Max links allowed in the secondary On-this-page TOC."
+ "--max-toc-items", type=int, default=16, help="Max links allowed in the secondary On-this-page TOC."
)
args = parser.parse_args(argv)
diff --git a/tools/scripts/mypyc_inventory.py b/tools/scripts/mypyc_inventory.py
index 0efd5cc60..0d7886e52 100644
--- a/tools/scripts/mypyc_inventory.py
+++ b/tools/scripts/mypyc_inventory.py
@@ -181,10 +181,6 @@
"classification": "compile_now",
"reason": "Sybase and SQL Anywhere render helper functions.",
},
- "sqlspec/adapters/mysql_common.py": {
- "classification": "compile_now",
- "reason": "Shared MySQL-family adapter helpers compile with the adapter core surface.",
- },
"sqlspec/extensions/events/_models.py": {
"classification": "compile_now",
"reason": "EventMessage has concrete datetime annotations and slot dataclass layout compatible with mypyc.",
diff --git a/uv.lock b/uv.lock
index 1098d063a..d5a2a3e77 100644
--- a/uv.lock
+++ b/uv.lock
@@ -19,6 +19,32 @@ resolution-markers = [
"python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'",
]
+[options]
+exclude-newer = "0001-01-01T00:00:00Z" # This has no effect and is included for backwards compatibility when using relative exclude-newer values.
+exclude-newer-span = "P7D"
+
+[options.exclude-newer-package]
+advanced-alchemy = false
+litestar = false
+litestar-asyncapi = false
+litestar-asyncpg = false
+litestar-autowire = false
+litestar-email = false
+litestar-granian = false
+litestar-htmx = false
+litestar-mcp = false
+litestar-oracledb = false
+litestar-queues = false
+litestar-saq = false
+litestar-security = false
+litestar-vite = false
+mypy-extensions = false
+polyfactory = false
+pytest-databases = false
+rich-click = false
+sqlglot = false
+sqlglotc = false
+
[manifest]
overrides = [
{ name = "mysql-connector-python", marker = "python_full_version < '3.12'" },
@@ -45,7 +71,7 @@ wheels = [
[[package]]
name = "adbc-driver-db2"
-version = "0.2.3"
+version = "0.2.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adbc-driver-manager" },
@@ -53,27 +79,27 @@ dependencies = [
{ name = "pyarrow" },
]
wheels = [
- { url = "https://files.pythonhosted.org/packages/95/85/cdd32e94bcb2a84e332860ec53cb28de53f19ec1a21eebc6955cfb8fca7c/adbc_driver_db2-0.2.3-py3-none-macosx_12_0_universal2.whl", hash = "sha256:865705599ddd5bb8bdedfbc6fa140767578d801770a057dd4d4c2da5f44718b1", size = 4198549, upload-time = "2026-10-27T13:52:52.496Z" },
- { url = "https://files.pythonhosted.org/packages/76/5e/ad54c5b2c987969f8817b4324214608df6e17e52b8b42215937015a785a5/adbc_driver_db2-0.2.3-py3-none-manylinux2014_aarch64.whl", hash = "sha256:6466a9477a821db9c9e8a0cb1651cb8176c69aa6af68f35d51278d226cbfb12f", size = 7961304, upload-time = "2026-10-27T13:52:54.501Z" },
- { url = "https://files.pythonhosted.org/packages/e7/6e/d9ad4c99b4a00371ccf42fc69d8bcce675628b49f636fe7ba45eff7d1661/adbc_driver_db2-0.2.3-py3-none-manylinux2014_x86_64.whl", hash = "sha256:9477387261211d64ece7531fc15965243220e3119ed3a0faaa506d43db46d000", size = 8862031, upload-time = "2026-10-27T13:52:56.314Z" },
- { url = "https://files.pythonhosted.org/packages/82/ba/0ad08234f3cf70264d659fff5638e5609095f94a9d06197f980cf62d1b90/adbc_driver_db2-0.2.3-py3-none-win_amd64.whl", hash = "sha256:0d601bea536d6fa2d084a3bd94a9414d9721b884d858f4da6246d5575b0e5336", size = 8822996, upload-time = "2026-10-27T13:52:58.345Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/4b/48c9619cc81c8cbef39cb9c3253bd91005c84067078d7508ee1047daaf7d/adbc_driver_db2-0.2.1-py3-none-macosx_12_0_universal2.whl", hash = "sha256:3573a21ef6f22ab8b66062d982bbafef99a16ca19fb7069e85dfa3c7a79b4a87", size = 3979102, upload-time = "2026-09-30T16:12:32.278Z" },
+ { url = "https://files.pythonhosted.org/packages/da/ff/673cede5fe0a4942526139829e29f1338bdcbb75381989e48c03e7296bda/adbc_driver_db2-0.2.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:286da0af1d0c64676986792f6331592e9e8c2bde5a151877c46e7961c54204de", size = 7507117, upload-time = "2026-09-30T16:12:34.021Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/af/6b70086e8873d5d64e961bdcf842d9b64bed2046580b59d56826d789909c/adbc_driver_db2-0.2.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:ccd8eb2ffe39a439ca36e186c38d515defe64227c8e5a295f2ee87f5a3f26b37", size = 8280573, upload-time = "2026-09-30T16:12:36.045Z" },
+ { url = "https://files.pythonhosted.org/packages/60/80/6679772b54d968226452cd700b18fa7d09d54fe0e0d254c5615396be9ce4/adbc_driver_db2-0.2.1-py3-none-win_amd64.whl", hash = "sha256:9eed1d3de68d868cef7df835cfd1f5930a882b2455222b6b178e13ebbbdd8aab", size = 8134110, upload-time = "2026-09-30T16:12:38.749Z" },
]
[[package]]
name = "adbc-driver-flightsql"
-version = "1.13.0"
+version = "1.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adbc-driver-manager" },
+ { name = "importlib-resources" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/e2/9c/a2dfca043e473cb95d4db32d6f37ee4c1c65bf22c84fb77526c4b698a47a/adbc_driver_flightsql-1.13.0.tar.gz", hash = "sha256:b067abf364e5253c033ce86dd1f8319eb4780d266eddc9b366cb1a1149daf9c1", size = 21808, upload-time = "2026-10-26T06:33:34.782Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/65/91/605b35b97aa5972a8026c0687e9996658d1d10ff7a0624173a0d0c7c5b93/adbc_driver_flightsql-1.12.0.tar.gz", hash = "sha256:300f67801ea016578e0c4bb798fc2b9024e405cfdd3f79fd77e37481a5767e45", size = 22097, upload-time = "2026-07-28T00:43:02.432Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2b/2c/8de9331757d5bb8c5bdaf8cb3221d4adf34c2f56d593dcaeb399c012d972/adbc_driver_flightsql-1.13.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:e73d8e456805b8fae8274e84772864d06481b41a9969c5ea2aed74893f31fc8d", size = 8326058, upload-time = "2026-10-26T06:31:19.469Z" },
- { url = "https://files.pythonhosted.org/packages/4f/3d/217cec4923d82310dde0dfc3b9cb853a1178503b5ac690190ac8450f5236/adbc_driver_flightsql-1.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:0a6e7edee439e35490d2ec49dea7463635a9985e0dba6b61fd78f42cda776fa1", size = 7715852, upload-time = "2026-10-26T06:31:25.073Z" },
- { url = "https://files.pythonhosted.org/packages/9c/af/5d0ec5ebf52478728e775be4e36ccf3c605cce719b2478facad4e92663d0/adbc_driver_flightsql-1.13.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:1199552b5cfe3349554a972c372fcaa2ecfdfac27b942c8ddf8c3bf798690c31", size = 15275335, upload-time = "2026-10-26T06:31:31.03Z" },
- { url = "https://files.pythonhosted.org/packages/13/2e/89d9c4fa900bd7f8d1ab0443e24fad1e301820f4219ed7eb5277fa202181/adbc_driver_flightsql-1.13.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:38886f49e3504c0b226796bcae914cf32293b79104c273459510c36592880749", size = 13974565, upload-time = "2026-10-26T06:31:37.661Z" },
- { url = "https://files.pythonhosted.org/packages/d5/d6/5e5606bd95e6c87ba4c86e0dfcf41e1ad2300abd7b9e7cf2501e51d4ad1c/adbc_driver_flightsql-1.13.0-py3-none-win_amd64.whl", hash = "sha256:6f11164214d979abb61600d96479781c7c47ea79f4f1c843837faeb772724f25", size = 15019143, upload-time = "2026-10-26T06:31:43.191Z" },
- { url = "https://files.pythonhosted.org/packages/4b/a0/3e792af07fe018f0cf640b7b036e54eb5133d81a0c08b554d6feb3c57734/adbc_driver_flightsql-1.13.0-py3-none-win_arm64.whl", hash = "sha256:ea5e26a4a9df4da69c9a39cd90a5475eaf084a5f2b52fc3e8fe68b1d54a290dc", size = 13686797, upload-time = "2026-10-26T06:31:48.625Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/a4/5ad52abf0c4830585b86d68cff8ce07779cdee9b8cf390dff0ee21a29fa7/adbc_driver_flightsql-1.12.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:c2c7209407c180d9d0dd94846203f184ab91e116782585bf29cb968c2686ba75", size = 8146345, upload-time = "2026-07-28T00:41:22.824Z" },
+ { url = "https://files.pythonhosted.org/packages/61/36/49267a4fc8c07c066dd8c36b9c3d27265a4dc0fc478cb5acb7bd6a551d80/adbc_driver_flightsql-1.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:fb37efefceb7ceca64840c76c55e6280f3811b54dca305ec1781f9cc1a6da21e", size = 7552045, upload-time = "2026-07-28T00:41:27.147Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/5b/f8566aa6d05eb40225f5874f8f5f5e8b8b4a4ce866d817a5ff352f326302/adbc_driver_flightsql-1.12.0-py3-none-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:69742e4f8fcc254490aa7f8e254a3b704c24bde9d5d827a0926ea90a6b4c3a6d", size = 14981635, upload-time = "2026-07-28T00:41:33.2Z" },
+ { url = "https://files.pythonhosted.org/packages/57/45/6468fb425f3c0ae868c1ca9ab8e15cba76b59e941edd7e80e892e5476728/adbc_driver_flightsql-1.12.0-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9adfb5e46abc77347476d09ee76dfc835fef2b4dcb0c76d08d6d35aa3a4cce17", size = 13699634, upload-time = "2026-07-28T00:41:38.232Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/c7/6773dedc54fad73277ee2d767dd817e203e949972b0cfb197df99e00b784/adbc_driver_flightsql-1.12.0-py3-none-win_amd64.whl", hash = "sha256:098f2498778fd3efd671fbeacba80987de98ebfeb0291da77fe0a26c6fa78532", size = 15033803, upload-time = "2026-07-28T00:41:42.483Z" },
]
[[package]]
@@ -95,60 +121,43 @@ wheels = [
[[package]]
name = "adbc-driver-manager"
-version = "1.13.0"
+version = "1.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/db/73/114b15fff653d53a5588826152e33b009c141edcd45678506f9cbdf06c19/adbc_driver_manager-1.13.0.tar.gz", hash = "sha256:72d616e43d03b735e4000b653307531a2d1820f1f7e0ce949656f0ab939bd53e", size = 220657, upload-time = "2026-10-26T06:33:35.816Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ed/ec/48d773b8af10a028010bd66ee72c7f5bbe4773771e388bff63bd9c223493/adbc_driver_manager-1.13.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:7e76996b5adb33f918d2fb8480f2f8da333fe2f6d99c2ade5b2d6deb63ecf1eb", size = 584036, upload-time = "2026-10-26T06:31:50.853Z" },
- { url = "https://files.pythonhosted.org/packages/d0/54/89e374dab257558582beb53ef6569f30d761f5aafaf59c34ad5562c9bd22/adbc_driver_manager-1.13.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:d0c0dbd225cd670722929fd72178498d4104f16b95db390a372a7bea9ada39a0", size = 597785, upload-time = "2026-10-26T06:31:52.594Z" },
- { url = "https://files.pythonhosted.org/packages/f3/7a/2dcd5cd355c917b1436e94f4bfd6f6aab960993dfeda36cd4723d8c76236/adbc_driver_manager-1.13.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2da4174cec7d52269d144a847e60e6c80d7b28317764d204b436be4ed7fd294e", size = 4150783, upload-time = "2026-10-26T06:31:55.281Z" },
- { url = "https://files.pythonhosted.org/packages/a2/2d/97675f53a3dec3ed0745d087f7eb26a4601a2879d9918f8ab24bd9c49b2a/adbc_driver_manager-1.13.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2e5444a944e3c17c778601e35e83902e26d9571e77ca132b61f264c78346a06c", size = 4219333, upload-time = "2026-10-26T06:31:57.658Z" },
- { url = "https://files.pythonhosted.org/packages/8a/46/9026fa768c47860a38fd81446eab768cc093fc49ff8f202c9f2a2a2a3a0f/adbc_driver_manager-1.13.0-cp311-cp311-win_amd64.whl", hash = "sha256:26c6c125ba569a4f269d4d2c9e462b9de08b7c3d7fec931fe17e0b750ace3b6d", size = 749255, upload-time = "2026-10-26T06:31:59.108Z" },
- { url = "https://files.pythonhosted.org/packages/c0/ef/cc43bf835198307daed40cb38f32c12c120617a17ab800b16334d56b8c67/adbc_driver_manager-1.13.0-cp311-cp311-win_arm64.whl", hash = "sha256:7f019e7cf4fdd85d890d94934de3912416b23028fcd256cac54d34a81ad2f2a6", size = 902200, upload-time = "2026-10-26T06:32:00.67Z" },
- { url = "https://files.pythonhosted.org/packages/b1/da/32a87b4c3bf7374789ee9cd4e30af36f6197dd909a1d41d8c37b0abb3130/adbc_driver_manager-1.13.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:7a4caf0a2edcee471f3bf2e92f31c8df62a6facf6ceee51d9d6d3ea9b6f10aec", size = 586271, upload-time = "2026-10-26T06:32:02.111Z" },
- { url = "https://files.pythonhosted.org/packages/8a/b9/16a51949215a7d1d2d0212796758ba65cc3c02870c9b9a5f8a2f417e2dfa/adbc_driver_manager-1.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:fa07d0453c0a1022f4ab5490dc1795372799f994424cb8fd6fb274eda8fffb2e", size = 598566, upload-time = "2026-10-26T06:32:03.453Z" },
- { url = "https://files.pythonhosted.org/packages/31/ac/02fbdb2552f23794b62dfb6d9ecb5391767f018d28c180c917226b118c4e/adbc_driver_manager-1.13.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46776b55bddb98d37908bc4809a4567f6f897f97d1a79bf4ab0889bcab4ef585", size = 4148672, upload-time = "2026-10-26T06:32:05.83Z" },
- { url = "https://files.pythonhosted.org/packages/d3/0d/ae1ca761707904fe502cb63204c40d6baae7589c4181123cdda3aa03663f/adbc_driver_manager-1.13.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:47e225d206d0d5f82d32d1ad0985ddf4984168479ecfcff21cb82fc5bb36ec5a", size = 4217627, upload-time = "2026-10-26T06:32:08.134Z" },
- { url = "https://files.pythonhosted.org/packages/5e/56/7f2cb5ff0554f33bc2d4ef5871f739dc584fb3d1361f22cd8cb4228718b1/adbc_driver_manager-1.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:cf0ebd3d6082399f904863c88c5b49f5f8e6026f057bac2ebb1a9a2206325569", size = 746315, upload-time = "2026-10-26T06:32:09.5Z" },
- { url = "https://files.pythonhosted.org/packages/ec/75/c0c3e23487817552fffe63ac62c3bb36dfd258e77c53ece97fd675f2e2c0/adbc_driver_manager-1.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:19bf47dbeaa654c694831d7ef538a027fef7785d62d8860eebd5f24eeeae7463", size = 899028, upload-time = "2026-10-26T06:32:10.948Z" },
- { url = "https://files.pythonhosted.org/packages/e1/e5/710220a20b0966e20205fe18f5a45b69d98c6413e74546143c83abc33105/adbc_driver_manager-1.13.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:624bef3b9949e7038ff451ce3c08df20e0738da23c0d1037834c0ee4426ea305", size = 584189, upload-time = "2026-10-26T06:32:12.653Z" },
- { url = "https://files.pythonhosted.org/packages/3a/38/0b4ab4c4dad3de34a19c3fed0833a244355da8ee4800df00b271372051f5/adbc_driver_manager-1.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c1420b860c87679757058bda49b64a56e7f61e968eb6568a853e1d52356d44b3", size = 596254, upload-time = "2026-10-26T06:32:14.194Z" },
- { url = "https://files.pythonhosted.org/packages/97/11/5d732670417f9d8e2b3d201be366f6ed716041d490008a21469c1baf472e/adbc_driver_manager-1.13.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:82c90861896653c2cbc39f74f0261c0bb0c2862a2ceee60f29744df6e18da175", size = 4133894, upload-time = "2026-10-26T06:32:16.578Z" },
- { url = "https://files.pythonhosted.org/packages/9f/4f/e9098132e7892ba95582ab62880730ed3070a6d0218a04b56ef54a5211ae/adbc_driver_manager-1.13.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b81819bd35fb47b59757ab075041e326ec278ddaec33f75b6baadf11be1bc066", size = 4206979, upload-time = "2026-10-26T06:32:18.544Z" },
- { url = "https://files.pythonhosted.org/packages/b6/4f/726bf4db51fa2e9ac1407ea3c1468306a8c015746c5e04ec0687ff404127/adbc_driver_manager-1.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:76f3addf8f4a25937b4d4dd3aa581b436c60c248123a094353d3293ee3405454", size = 744877, upload-time = "2026-10-26T06:32:20.232Z" },
- { url = "https://files.pythonhosted.org/packages/ca/33/33522fc3cfcc8072cd00d408b74375a06973420b0b24e49d754cabbbc637/adbc_driver_manager-1.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:d7c8d9a07c99235475c68581cacaa03ee04b31d50440930321dde69c9d47a1bb", size = 897940, upload-time = "2026-10-26T06:32:21.792Z" },
- { url = "https://files.pythonhosted.org/packages/8e/d2/12972831ad00ba4c42c6b14bd009e196443ff98e9466c6239d41d68af05a/adbc_driver_manager-1.13.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:685cc3b2689a7f16f1c79f9423a6ec1694d1663cb0257aec2006546f294379ce", size = 584835, upload-time = "2026-10-26T06:32:23.914Z" },
- { url = "https://files.pythonhosted.org/packages/47/a0/596e90687f39982443bb5353e68d5b483169423d5c25bd6d0463835ed59c/adbc_driver_manager-1.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:18f03c51b73ffcafe16d98fd537a6417df5171043147e59fc781373d2290ffa9", size = 597713, upload-time = "2026-10-26T06:32:25.426Z" },
- { url = "https://files.pythonhosted.org/packages/07/dd/5363a6103751fa0ac644c0db1875b495e3297b9f15fa04fe5ce4cf2da1ed/adbc_driver_manager-1.13.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7699696b6388fb7e0e69b84627a28bdfe988d144d45649102859a0f07aeb0223", size = 4131883, upload-time = "2026-10-26T06:32:28.05Z" },
- { url = "https://files.pythonhosted.org/packages/2b/2c/1759a417eae4d90c390737eccf1098033779f2a8d6d8952d2f208cd236fd/adbc_driver_manager-1.13.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3ce8256a35e68420fab5e19830b6051578828c7774f10500abae7edc61ec0d3a", size = 4190903, upload-time = "2026-10-26T06:32:30.221Z" },
- { url = "https://files.pythonhosted.org/packages/c9/22/96f0c65ff4c9417da9c7056cebc2e9b08768c9b26f0123896c7b2172c251/adbc_driver_manager-1.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:5d1b71b6faa58ab970c67e6992eee0a0e617d1a2f35d1bd1095d6c4c4dea1e3b", size = 762138, upload-time = "2026-10-26T06:32:43.077Z" },
- { url = "https://files.pythonhosted.org/packages/8c/0a/1b53bb18f406f2c808b2c3757318f905d61901254c83071d3869648e0c3c/adbc_driver_manager-1.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:051147056936bcec489c59c859162a4bf17cb6280efc5b75f63e70b2ac4250bf", size = 921713, upload-time = "2026-10-26T06:32:45.114Z" },
- { url = "https://files.pythonhosted.org/packages/cd/dc/7151b3fedcb5d1ae0301eb5c1389200616a42f3138d1d55f142603a4001b/adbc_driver_manager-1.13.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:487ae59fa53521dd040f78449032a92f460349a67c2daf3606a80c2410487d42", size = 603338, upload-time = "2026-10-26T06:32:31.9Z" },
- { url = "https://files.pythonhosted.org/packages/13/e8/12db91dcc1b865d70556ba0b1a8128e850608612b3c7359b428e7118b177/adbc_driver_manager-1.13.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:ca595a044fabbb083d82c61c20ffea8614ac7568ed3d6978ac1c48f026e7789c", size = 616567, upload-time = "2026-10-26T06:32:33.49Z" },
- { url = "https://files.pythonhosted.org/packages/f4/4a/60c6f39b9e66bf192efb3e2f593d8388bc056efd19288933494f7bc55a39/adbc_driver_manager-1.13.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:17a0552d198105dd15b39a93d20e8eda79916e747d74cd49868282f447c8a88f", size = 4198546, upload-time = "2026-10-26T06:32:36.122Z" },
- { url = "https://files.pythonhosted.org/packages/fa/69/d1796f2b18b4777b5571f102be9b7d9aa3371aa3253e8a67d79bb8d20d6c/adbc_driver_manager-1.13.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f851a4244edd2bf0fd33167cdb0d142963c089e53a720f42e550752b38c328c6", size = 4234979, upload-time = "2026-10-26T06:32:38.28Z" },
- { url = "https://files.pythonhosted.org/packages/c1/e4/4e5bcf48779f9ea999d20ed3b622ce4f2481c144ae86d7a2a15dc4c883b2/adbc_driver_manager-1.13.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0cec917f7b46667e255842dc8d1f174125faac85951a7777527befd545fb4b6c", size = 778561, upload-time = "2026-10-26T06:32:39.835Z" },
- { url = "https://files.pythonhosted.org/packages/68/9c/cdf0e92e23bb5cf8f24ad0ebaa771d6d4881ed9d1f0f3090faafdd562149/adbc_driver_manager-1.13.0-cp314-cp314t-win_arm64.whl", hash = "sha256:914e2dbb42d31763af97b472e23b99b5888681e8485d8931a776a4662dfae049", size = 934897, upload-time = "2026-10-26T06:32:41.345Z" },
- { url = "https://files.pythonhosted.org/packages/4f/60/b3d4c3680736f14e16f3511ee9306aad55349ee7d716b30e92e7205ca42f/adbc_driver_manager-1.13.0-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:6c34be6fb94c81614ae2eafbf984428657cc8d694ee73503aa5c74f88a44e776", size = 583902, upload-time = "2026-10-26T06:32:46.957Z" },
- { url = "https://files.pythonhosted.org/packages/4e/f0/3e5f67764da561a6f4389eb2a79d41bf3b3486361c47982fe0e50d0b39b2/adbc_driver_manager-1.13.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:cad5bfa4f930c737cb2505c3888e091fa64bc2d628a3e2aadef307e5d50d47f7", size = 597158, upload-time = "2026-10-26T06:32:48.852Z" },
- { url = "https://files.pythonhosted.org/packages/6c/52/d488a47c4c489019c042f5622ed1f5fe798a96bd1db4e67c6dde1e326b5e/adbc_driver_manager-1.13.0-cp315-cp315-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:68fcb9e8b12bb18171b775e389fea8228b315b344d7b9454a9c350e1cd17e28d", size = 4122336, upload-time = "2026-10-26T06:32:51.611Z" },
- { url = "https://files.pythonhosted.org/packages/39/61/5a1b64bb95d8712a1818e1e3fc67415ec8f930f7daa1f9f61c663f0b5bab/adbc_driver_manager-1.13.0-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fbb20b36d7dd23913e3bebd4b37499f4130c38a291150ba57205c25531782775", size = 4194847, upload-time = "2026-10-26T06:32:53.998Z" },
- { url = "https://files.pythonhosted.org/packages/9c/12/5601ebe623182360d0fede83b85d5fd3ec1cc1436f8bc566586d6984e0a3/adbc_driver_manager-1.13.0-cp315-cp315-win_amd64.whl", hash = "sha256:d961fc26536092500ef9f2333d1113e8954a67fcffb08148296b0238d35b4206", size = 762591, upload-time = "2026-10-26T06:33:06.227Z" },
- { url = "https://files.pythonhosted.org/packages/2c/10/4de47851ff5be7e20d590a89d667ad9a45709f70d2d95652835b9253b922/adbc_driver_manager-1.13.0-cp315-cp315-win_arm64.whl", hash = "sha256:c5c59cb30aaff7bb1e4da95f6d92c4949532233174ba8d59b1b96102facddbfb", size = 921291, upload-time = "2026-10-26T06:33:08.052Z" },
- { url = "https://files.pythonhosted.org/packages/c3/6b/53eb3b0df2735a7ef4d225589a0a4b59bb81f9b31728c623667dc3e1a234/adbc_driver_manager-1.13.0-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:1bb5a9787d3cfe88193b5d82bd866c3060c198fea3f36918797f4af80561920d", size = 601116, upload-time = "2026-10-26T06:32:55.66Z" },
- { url = "https://files.pythonhosted.org/packages/69/c8/4e91dc6dcf38c44e406efc47546e956e1d804a813de81d0a66626b89de3c/adbc_driver_manager-1.13.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:20231b8c38fef43719e3fc901a16f7dcf1cebe5869618242b31dca44879b78fd", size = 613341, upload-time = "2026-10-26T06:32:57.042Z" },
- { url = "https://files.pythonhosted.org/packages/9d/53/42a5738a5b23fd6629eb24e3410914b336c1e381858c88b7fb51bb1b0737/adbc_driver_manager-1.13.0-cp315-cp315t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bd1d240663870aa4bf15efb4c789d8a71faa8a81fa8ee704975b0bfb2cad4f1a", size = 4189350, upload-time = "2026-10-26T06:32:59.066Z" },
- { url = "https://files.pythonhosted.org/packages/44/3e/081d2cd91b69ee3ff81a37c2a1036a2a7956586c8d784760d793892e7303/adbc_driver_manager-1.13.0-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f5e85accd23f8665ba5381bbb99d655fa3728f4e998e1f3c8821b8f1b5476211", size = 4243900, upload-time = "2026-10-26T06:33:01.332Z" },
- { url = "https://files.pythonhosted.org/packages/2c/ab/3a9377a8b7a649f06a97d0c657dfbf0c6bd7550cd608991ef7f7a7456895/adbc_driver_manager-1.13.0-cp315-cp315t-win_amd64.whl", hash = "sha256:424a902361f0ad8833437a726407f91c849173aa8dcf4b04de5a72602293bf30", size = 777474, upload-time = "2026-10-26T06:33:02.754Z" },
- { url = "https://files.pythonhosted.org/packages/e3/1b/f152c3f58106370cd50ac6993109d0b265e93ce90b644d99c47e734d5e69/adbc_driver_manager-1.13.0-cp315-cp315t-win_arm64.whl", hash = "sha256:7b25f59a849b53391e22af1fd2a83d7ca36e171a89f52c88a240445764b3d694", size = 932449, upload-time = "2026-10-26T06:33:04.483Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/9c/f8/ed6475b49a7cf35ea888d5c95e7d4bc9dc6568f9d741f14c0573d622cc1e/adbc_driver_manager-1.12.0.tar.gz", hash = "sha256:45991f0c2de369d330c6a211ca2edbcce6389c5dc81cde70461bdeb6f8f7b268", size = 217579, upload-time = "2026-07-28T00:43:03.512Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/73/2d/e41ea911f9486c497534ae181dfdab19adca21f71abc8a1fcaf2c27251a8/adbc_driver_manager-1.12.0-cp311-cp311-macosx_10_15_x86_64.whl", hash = "sha256:3c0c73670c8aa6fe42de1d5e71a0b329c4b37f7c55c560c23f6f3a1609200c1f", size = 600030, upload-time = "2026-07-28T00:41:54.753Z" },
+ { url = "https://files.pythonhosted.org/packages/10/ea/1a8b51999785d7dce17079dd635c9ee2372c75ec7328423ce862741cb503/adbc_driver_manager-1.12.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:6943c7adcf3c7c9f7c4b5bdb7589c331027a347e3c77471eb3f656b1a881e351", size = 611058, upload-time = "2026-07-28T00:41:56.306Z" },
+ { url = "https://files.pythonhosted.org/packages/85/a2/5ede53173a420742fa71d6c26792e2295fc73f25cf39055f912a5385b1f0/adbc_driver_manager-1.12.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:78c9936adb280e2c10e90632e41b58aa23be358e1136d8fb3c52862b72818a95", size = 4663735, upload-time = "2026-07-28T00:41:58.793Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/1a/781561d0f55e05a0b884244ed563dab14b165b4dd74abd8af3f8efd95e3d/adbc_driver_manager-1.12.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:30d96ab4a2594b4109496fb4913646f41a5bf1ecce79b4313847d240a2a62db3", size = 4747090, upload-time = "2026-07-28T00:42:00.746Z" },
+ { url = "https://files.pythonhosted.org/packages/c6/fa/47c755a74ea4887968c52a968e02736007da4042fc0820292dc8c6827a94/adbc_driver_manager-1.12.0-cp311-cp311-win_amd64.whl", hash = "sha256:67419b92c286646944426992069f56fed90c2ceac83521f6d66d7d3cbf6c17ea", size = 760952, upload-time = "2026-07-28T00:42:02.432Z" },
+ { url = "https://files.pythonhosted.org/packages/de/8c/cd3fe16df716719116a6c79e64a768fe994f6ded55d5a8f091bb4f42d6f0/adbc_driver_manager-1.12.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:fd02364c65b8b376c5627e3b77410f457fcbbf983e52e8d15ca099da3a7ae314", size = 599054, upload-time = "2026-07-28T00:42:04.072Z" },
+ { url = "https://files.pythonhosted.org/packages/49/4a/2f060ff6bd61420ea1613670e1f85a22a8714934c235186dc3803de8ddac/adbc_driver_manager-1.12.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d8dcf62621090e8d9c8216e08dfc4043f16331872522186af61a5de9478e9c63", size = 609964, upload-time = "2026-07-28T00:42:05.82Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/f1/0746db149828ae91e4a6cf49f8d0e49210eec20c03ad80044454139c8240/adbc_driver_manager-1.12.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:efa5dbbf101962d212b176f25e6fc509dacf07afd4cf70b5027d81ec6871bdec", size = 4685726, upload-time = "2026-07-28T00:42:08.1Z" },
+ { url = "https://files.pythonhosted.org/packages/b9/c3/f8e9c5157b19e986df719259eb3502dad1268df9f7a1034f65ca220ab2ea/adbc_driver_manager-1.12.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8b340679a005a8adf6b0b58754dbc638dff00db7b2559c140406a1d92678b48c", size = 4768774, upload-time = "2026-07-28T00:42:10.359Z" },
+ { url = "https://files.pythonhosted.org/packages/92/51/f8e625af691e6b4c54945790854524356a02a0a69063e888f7cfee1b2e50/adbc_driver_manager-1.12.0-cp312-cp312-win_amd64.whl", hash = "sha256:47f428a922d224fd486b661deeaf9520e5faec558b3d144832bed09a080cac88", size = 760087, upload-time = "2026-07-28T00:42:11.871Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/f9/674c5bbc5093617d72c4f58a5dab67982710b2320cc9aa826050a6aaa131/adbc_driver_manager-1.12.0-cp313-cp313-macosx_10_15_x86_64.whl", hash = "sha256:c42ca4d9caa22b3a5ce76bde8729169f403bb7393e3671734b9416634c207125", size = 596815, upload-time = "2026-07-28T00:42:13.64Z" },
+ { url = "https://files.pythonhosted.org/packages/56/5f/c1d888d787330801edae282d2a9def3765e8157547cc20e71154ff38c1bb/adbc_driver_manager-1.12.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c894117c8f5c484b902c8b070bcfd9d31d90efe0288b2b58a3ddab97c80f66e7", size = 608277, upload-time = "2026-07-28T00:42:15.643Z" },
+ { url = "https://files.pythonhosted.org/packages/06/4b/ee799babf171e39690ef45560451096f869d9e7387bc0e5a754bb243ed2a/adbc_driver_manager-1.12.0-cp313-cp313-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:214f80f9b65562f08b4d1c52a756b5db557530e3c0652f587c43aaa80039579a", size = 4667230, upload-time = "2026-07-28T00:42:17.97Z" },
+ { url = "https://files.pythonhosted.org/packages/00/c6/a35e38ef5e0db391be79e0e14c019ce378b87d9d7e31d1dfcd451e9d291f/adbc_driver_manager-1.12.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:532ab290b3d923ce0a75bca21dc6e13f55835625f78808e1664755939f3ebdf6", size = 4745299, upload-time = "2026-07-28T00:42:20.189Z" },
+ { url = "https://files.pythonhosted.org/packages/16/e2/62bacd6844859036d79ea229401b5200056fb5050c82dc3a2e28b08ff49b/adbc_driver_manager-1.12.0-cp313-cp313-win_amd64.whl", hash = "sha256:034da82c1a6e195d67ca1f0c97a1a517046037ec3029ab9a0ea8f7ccb14056e4", size = 758878, upload-time = "2026-07-28T00:42:21.598Z" },
+ { url = "https://files.pythonhosted.org/packages/50/ea/f53b434fe36d0f138d147fc10a95784c8c0eeea1bec1f3f31eee5ec8bdb5/adbc_driver_manager-1.12.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:a740d634118722f42af31176374fddbad3846fa2e6536f497bac145e9511cecc", size = 597579, upload-time = "2026-07-28T00:42:23.216Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/57/6208e66d9256550c2aff75db4a323a855a0d5d2d1bd639526f825d3e08b4/adbc_driver_manager-1.12.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:8a77ae39832e67946009816d83c321e540a3024aad1419ccba24ddeb7b6a01f4", size = 610337, upload-time = "2026-07-28T00:42:25.051Z" },
+ { url = "https://files.pythonhosted.org/packages/1d/cd/f5ea3f08191af5ae15041821fcb52bf35837dce1a9ac16fa039b3bfe308c/adbc_driver_manager-1.12.0-cp314-cp314-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:690f140ca67d49f995afac59f85441c3d5e896cd2fc8fd381423fe900e51f1f7", size = 4664297, upload-time = "2026-07-28T00:42:27.474Z" },
+ { url = "https://files.pythonhosted.org/packages/df/81/823a71a515078545eab8a4be8381206887129e11b91e9bf51ca2a9eea44d/adbc_driver_manager-1.12.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:fd568c94874c0586d82f99de2bb5d2c02b4fa9c5bafe3d0d8ab353bddf9d2fd6", size = 4733739, upload-time = "2026-07-28T00:42:29.814Z" },
+ { url = "https://files.pythonhosted.org/packages/cf/f7/7612d078d935344aee679a44a6283de6aae9008eb8e0ef80e475dd12dffa/adbc_driver_manager-1.12.0-cp314-cp314-win_amd64.whl", hash = "sha256:57f5101fb2a853b1ffb81ff807b5e29a51ba14c64032eb0038b8dfd433b6d533", size = 777952, upload-time = "2026-07-28T00:42:40.881Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/ad/2478338aaece38b8b72259dbfd4d4c84d9a038421e25bbc283e510d47555/adbc_driver_manager-1.12.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:bb9db6e4a3bcd73153435a900b5ae40ad36f5875df93a8faf784d9fcf6833983", size = 615694, upload-time = "2026-07-28T00:42:31.932Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/a0/0592c85e653f005aa28de7733b3c3c4f0282238301694f76806e5f3cc1e1/adbc_driver_manager-1.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:07cae26bd5ccee6caa4227f817c0fd57f9ac131c2dd98e0c5d7fecfef61819c7", size = 628341, upload-time = "2026-07-28T00:42:33.481Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/00/65705a72f768bc2dda82623a74cf816609dfdff56f3ad22b073d4a1ea7f8/adbc_driver_manager-1.12.0-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:442ed2ee8ea62c475bf3478385555bb4f0b25d9d551087ffe40c73b91bf5431e", size = 4730268, upload-time = "2026-07-28T00:42:35.661Z" },
+ { url = "https://files.pythonhosted.org/packages/44/b9/60ecde5d9dde5acc5576cb0ba5ffa34e154464e07fa295c57cd975ea27c7/adbc_driver_manager-1.12.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9c2aa05c5dc52164692284b2df27fba5680dbc967b8e3ca704aabf5399667996", size = 4777527, upload-time = "2026-07-28T00:42:37.709Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/76/6749e0c0c437219780c65487cff67dc09a556c1fccf577a2b27f7b92a704/adbc_driver_manager-1.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:cfa08f8c7c63e3fa92eb4e26ef4d8a9520cf92a39281cd011821f6f16a963080", size = 793451, upload-time = "2026-07-28T00:42:39.222Z" },
]
[[package]]
name = "adbc-driver-oracle"
-version = "1.0.2"
+version = "1.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adbc-driver-manager" },
@@ -156,27 +165,27 @@ dependencies = [
{ name = "pyarrow" },
]
wheels = [
- { url = "https://files.pythonhosted.org/packages/0c/9a/5febd3e8acdfb7b31110d3ab6b0bc7d349282b2dc7256e1867cf8d02f2e8/adbc_driver_oracle-1.0.2-py3-none-macosx_12_0_universal2.whl", hash = "sha256:12e2835459ec55d7f1705354d502b56c5f0153a5c367770b01621e53e3dfed30", size = 4351310, upload-time = "2026-10-27T13:58:10.375Z" },
- { url = "https://files.pythonhosted.org/packages/2d/8e/209c8463211060c2cbc3e35b1629cb4ad32b83b4d1fd7099f22736beffc9/adbc_driver_oracle-1.0.2-py3-none-manylinux2014_aarch64.whl", hash = "sha256:373456f2d4fd41013e4c339b7cab5d5bdecec214312520415c2ba703579a2291", size = 8199440, upload-time = "2026-10-27T13:58:12.924Z" },
- { url = "https://files.pythonhosted.org/packages/b5/fd/a463fd35656f587b1317cd2946795ce6fd7d0679fc9d58e35a3b91eeae78/adbc_driver_oracle-1.0.2-py3-none-manylinux2014_x86_64.whl", hash = "sha256:65b85a8a318ce9238dfb52e64f4aa68d8c475949f3426f27ab7c21ee6c2a5627", size = 9052381, upload-time = "2026-10-27T13:58:15.758Z" },
- { url = "https://files.pythonhosted.org/packages/dc/ec/616046322ab021e2504d608c9867a13f6c674cd6d85470d8992184ba8fc9/adbc_driver_oracle-1.0.2-py3-none-win_amd64.whl", hash = "sha256:d47c9dc6fb7e7140e7d3c06975befda5d3f9f57ba94030e66364b5a81c137ab2", size = 9107033, upload-time = "2026-10-27T13:58:18.291Z" },
+ { url = "https://files.pythonhosted.org/packages/63/b4/1c0a418a4cd99a32ecf6d054d9ef71de869f7c4d83ff10fb6e953281f4fd/adbc_driver_oracle-1.0.1-py3-none-macosx_12_0_universal2.whl", hash = "sha256:4bfaedbb38803893791842601504e45d583ac5e94a75d3ca867e300f9322cf77", size = 4733798, upload-time = "2026-10-15T16:11:06.957Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/e2/169358ad85ac78a2558bf9f458b70f1a6cb2397827f3f952e4a5016dff89/adbc_driver_oracle-1.0.1-py3-none-manylinux2014_aarch64.whl", hash = "sha256:b9baeca7fdfdd04d1250f0dc2dd2932cad4fc2841bc737d18b5c04d6049c91a4", size = 8833896, upload-time = "2026-10-15T16:11:09.316Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/a5/747b2bf253c3988a094f3619c7a54a77016a83964dfcabba085c3054e6f3/adbc_driver_oracle-1.0.1-py3-none-manylinux2014_x86_64.whl", hash = "sha256:b1008012fc1bcd9efc11a593c06df9fa5fee4bf059ab7bacddb39e96ac3f24d9", size = 9641676, upload-time = "2026-10-15T16:11:11.888Z" },
+ { url = "https://files.pythonhosted.org/packages/24/a8/c7a6ba0cac3da4c2a0fdb12ed37105b64c789b20aaafb890408663920ca1/adbc_driver_oracle-1.0.1-py3-none-win_amd64.whl", hash = "sha256:7abc29e759119fafbcd6f59ab37175fcafd767c8ee9835a8fda73ee927a65e53", size = 9598563, upload-time = "2026-10-15T16:11:15.301Z" },
]
[[package]]
name = "adbc-driver-postgresql"
-version = "1.13.0"
+version = "1.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adbc-driver-manager" },
+ { name = "importlib-resources" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/e3/8c/34d8bb83444f7ee193ed8d90a59f263ffa1dffa65e4b49c64ebc556b0455/adbc_driver_postgresql-1.13.0.tar.gz", hash = "sha256:ecebbf32e4841d4874229614c98f7dcf60ec657882641d6cb20cd54b2a7423bf", size = 18532, upload-time = "2026-10-26T06:33:37.032Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ef/9e/cc757dfc1bb5472e35bf066ee4044f6b101bef61036512e8dfb4e97e7e08/adbc_driver_postgresql-1.12.0.tar.gz", hash = "sha256:766a002531bb99b691d2b92e7d928dea21c24ea567c03a6ee1edb61fe95b9187", size = 17793, upload-time = "2026-07-28T00:43:04.481Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/25/3c/9f62b28e0a61e0af885e5e7271a78fc899dac3e9641c42aab01f7fe716bb/adbc_driver_postgresql-1.13.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:12fd64bf6ec16705d30d26c38ac2cc47fe8c7006ac2902c43234d61cb6ae8a43", size = 3076900, upload-time = "2026-10-26T06:33:10.567Z" },
- { url = "https://files.pythonhosted.org/packages/04/2f/79e0e9b471293320a79805a6dd77199ceaf8d73789dce35af5959b2db778/adbc_driver_postgresql-1.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:7981ae7c5ecd7ba4c649cd00a96e81cf0e39475d3fa472901c5fd19b4e9de4a8", size = 3348673, upload-time = "2026-10-26T06:33:12.998Z" },
- { url = "https://files.pythonhosted.org/packages/a8/07/3c6c59a60fb423df8e5912f6442c1c2a0a966d5a0b8dc82de0807b8256d2/adbc_driver_postgresql-1.13.0-py3-none-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f2f3c51076fc27ba768bad2dc4cc3888655ac719473aa7d933b2813860491014", size = 3871372, upload-time = "2026-10-26T06:33:16.163Z" },
- { url = "https://files.pythonhosted.org/packages/37/6b/dd22c016305e84b8b9959784e77bbd20c454bd82463dc8de5a839abde206/adbc_driver_postgresql-1.13.0-py3-none-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a86819ba7c79b1d067c4a5344358b4d346bab58e888d7879ed2b25d97cc59ba1", size = 3560463, upload-time = "2026-10-26T06:33:18.309Z" },
- { url = "https://files.pythonhosted.org/packages/82/8f/519def2e78e435139de9de645933cfcfe38ca997193d5d23365406d228b8/adbc_driver_postgresql-1.13.0-py3-none-win_amd64.whl", hash = "sha256:c97a0b71e7205b7aac7f8bcd85a41280ef56e2d788110d2572c52ac3d447e25d", size = 3111598, upload-time = "2026-10-26T06:33:20.149Z" },
- { url = "https://files.pythonhosted.org/packages/21/d6/7e0176a984252e22f1428b8e9d5cce9b201ad521f424be40f2ac048fb37c/adbc_driver_postgresql-1.13.0-py3-none-win_arm64.whl", hash = "sha256:ed770fddf8b5fedf98e36b728224f1a5df3c0e1daaea1abc6feb41a765c7aa79", size = 3414650, upload-time = "2026-10-26T06:33:22.199Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/ba/152bbe1d4a1cc13e2da72e76a5045ee25338bc75294f1ebf04c90b787287/adbc_driver_postgresql-1.12.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:28548d9e16497d2cb4750bc8e9e1abad3d0f981c7c0ff7afe70323f4b71c70aa", size = 3068434, upload-time = "2026-07-28T00:42:43.495Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/d3/f17e69423ed7217b70155d8e531c5cf6fb74f3b598a583e6cfe541dc3e7e/adbc_driver_postgresql-1.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:03c617aee8796f38a0a2f1af50ceae92d40f0974f3abbe7eefbaf009fecdc5ce", size = 3337707, upload-time = "2026-07-28T00:42:45.568Z" },
+ { url = "https://files.pythonhosted.org/packages/93/60/3b018e75661ac14a7aab7bb5cc1a95d72ddb9684abaee1e88a685406df81/adbc_driver_postgresql-1.12.0-py3-none-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b523f15051b27eef18c3a822296c2d94b894be552a0dbe49fe14059e2c706155", size = 3822540, upload-time = "2026-07-28T00:42:47.619Z" },
+ { url = "https://files.pythonhosted.org/packages/00/bb/ee19e7d56824c05892f82a3a2abca94fd2345b7de3dc22baac9e65a46acc/adbc_driver_postgresql-1.12.0-py3-none-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:2c2dc9c29db07ba3e0caf293c57a7ab1259dd772d3725ff1f1aeedb7a1895dd4", size = 3512701, upload-time = "2026-07-28T00:42:49.519Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/02/7aa782cbb0134b09d1e67757c81332e0cd5e5697beecc8852468482193b0/adbc_driver_postgresql-1.12.0-py3-none-win_amd64.whl", hash = "sha256:5a3b5262eed6f28fb4c782b532e6a65caed1f2268fab7be736335ead49eed9dc", size = 3207946, upload-time = "2026-07-28T00:42:51.543Z" },
]
[[package]]
@@ -198,19 +207,19 @@ wheels = [
[[package]]
name = "adbc-driver-sqlite"
-version = "1.13.0"
+version = "1.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "adbc-driver-manager" },
+ { name = "importlib-resources" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/20/37/539306d53a93c8ca2085daf211f21758e7ac9d96376b2324f14347e27e96/adbc_driver_sqlite-1.13.0.tar.gz", hash = "sha256:6c186aa0449eed95c361a97410a02c78c10619c4d209dc4070e77e769f8e779c", size = 13328, upload-time = "2026-10-26T06:33:37.954Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f5/02/2dc143bdd2a62c52d103d4b0ae491a347944aaed25b3d40fb11797750c70/adbc_driver_sqlite-1.12.0.tar.gz", hash = "sha256:18466a2f0c14f94cb0b17818157cc14ed6b93aef0a48ef648de945e9bac1540d", size = 12849, upload-time = "2026-07-28T00:43:05.408Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ad/e9/0dcdee1f5438c514cd7846398073e271a008b927f8337e273027482b2225/adbc_driver_sqlite-1.13.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:7044692c252985411f6ec4bf7947ff4e1b20f77c048f1e15438b2a4455941b79", size = 1422033, upload-time = "2026-10-26T06:33:23.735Z" },
- { url = "https://files.pythonhosted.org/packages/c9/93/a5ce6d47b993336fde99178f2cb8079e18d0c0edfacf9c68af5c34957643/adbc_driver_sqlite-1.13.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e2003654d198b00cf3b9e72f0d7c2414afab2511dd00bbe0ec9a1980bf2ef0eb", size = 1366378, upload-time = "2026-10-26T06:33:25.877Z" },
- { url = "https://files.pythonhosted.org/packages/8e/eb/974d9af8b401b141f00431bde238a36359d33e706edf8dbcf6b17dbababc/adbc_driver_sqlite-1.13.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:9fb6ff7adf05c01a9cd5ef5c62c926f6af6720f774e824829c7f648b801848af", size = 1525647, upload-time = "2026-10-26T06:33:27.717Z" },
- { url = "https://files.pythonhosted.org/packages/57/57/a4433a1c5e52d52358d390f5c99f5878d3fa1c7a1ff6618a40e13918901f/adbc_driver_sqlite-1.13.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:fb51f307cb3c45aa5476c84eae83781079939832f35dd54611dbe6095627ca3d", size = 1573387, upload-time = "2026-10-26T06:33:29.885Z" },
- { url = "https://files.pythonhosted.org/packages/1f/07/4e5551f023ad741cd6ba959bbd51d50dc224936cc1f20ca82560820643ec/adbc_driver_sqlite-1.13.0-py3-none-win_amd64.whl", hash = "sha256:87396ae95c1d0a10d95ea644afb6ae9843a64148a09c3e56fa7870ee03944a22", size = 1467059, upload-time = "2026-10-26T06:33:31.603Z" },
- { url = "https://files.pythonhosted.org/packages/03/ef/cabba36d8cb660689fd1d043258fc8ef9d0e2d73721ebfddbb50b56b9847/adbc_driver_sqlite-1.13.0-py3-none-win_arm64.whl", hash = "sha256:94e0348eca2813a866bf553e2fd6121c179145f15817c8d18bff2752307378be", size = 1301730, upload-time = "2026-10-26T06:33:33.272Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/f7/c35740269d3a5e3aa07b9ab155d4e943a7f5267f64d8f7396d5e14184a02/adbc_driver_sqlite-1.12.0-py3-none-macosx_10_15_x86_64.whl", hash = "sha256:2d5b3e9d0b5dbc66324b0ccf2ded886e3781f901be986892d319529b05536d3b", size = 1413592, upload-time = "2026-07-28T00:42:53.523Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/31/5d1d637e6ae76fcc57d5116d537aa78d2ab687354d5a0b2d527e085b61d4/adbc_driver_sqlite-1.12.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:5a81f53791e4aec69afbf8f77dac6acf48749fd84684e86601eafdd36d2eb7c3", size = 1357479, upload-time = "2026-07-28T00:42:55.194Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/99/415bf90eb912403d2d5d0c31baa1cedf200bd510f40027ee8fd3421c4c02/adbc_driver_sqlite-1.12.0-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:c987d03e3f4850e57f218c8a0b9d224209123af642469ee1f36901c5a51725bd", size = 1501753, upload-time = "2026-07-28T00:42:57.442Z" },
+ { url = "https://files.pythonhosted.org/packages/69/10/a3156f19fadd254a4f58a328a8aa9472c981ff93bb4d23f3c22a4341796e/adbc_driver_sqlite-1.12.0-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:3005a80bedf6624c6856da98037ea943a791aa8e82dad458259e0558be32912c", size = 1548999, upload-time = "2026-07-28T00:42:59.163Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/d9/3245d741936100365ea77c434f84a0985523467bd812e5e11bb9b36d7152/adbc_driver_sqlite-1.12.0-py3-none-win_amd64.whl", hash = "sha256:0982bfc06158c2140b5c490b1a1325019c827b158f8432a30d49c8a0c18533ad", size = 1522964, upload-time = "2026-07-28T00:43:00.917Z" },
]
[[package]]
@@ -500,19 +509,19 @@ wheels = [
[[package]]
name = "arrow-odbc"
-version = "10.6.3"
+version = "10.6.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi" },
{ name = "pyarrow" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/7c/0b/40ff0d4bdea53cfde441fd5d6d9b1d0e951ac8d862c4686c9e9a88d825db/arrow_odbc-10.6.3.tar.gz", hash = "sha256:056b15c47caee60c18827ad147e0c99a7524cf2e5c0887eed0fa32643d097716", size = 108856, upload-time = "2026-10-28T10:46:45.376Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/12/f8/7d0d0b88cadd9d28050ad7bce851af086857eb1401d09b54b26045bfbe99/arrow_odbc-10.6.1.tar.gz", hash = "sha256:f7449d292632f455f72e7ad8bb6b4fe386ac5602738225526294e1ffee0374d5", size = 107639, upload-time = "2026-10-19T20:22:59.75Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/85/34/c834ef7c41956fa0157e6d5db53ee46667b1b383e90007cfe788a8ee3c9d/arrow_odbc-10.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b1c828a9378ad2711b876b030c029a1f4503932532530b12c52e16346d63ea7", size = 761584, upload-time = "2026-10-28T10:52:19.614Z" },
- { url = "https://files.pythonhosted.org/packages/5d/e8/5fa28427d377fd6fcf57595c044186cb6c2f180e19d0256265e0d3e09dd7/arrow_odbc-10.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:a10f461776a3b0d38af0f8fc20035053534786912e697380180f89835e9b8ef8", size = 744531, upload-time = "2026-10-28T10:46:44.051Z" },
- { url = "https://files.pythonhosted.org/packages/56/22/67d6b8f32ca3f5fcaf72fbb256b8e9c9802e7c77eafe3c83861c46387fb9/arrow_odbc-10.6.3-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:6c2414e523ce8e157f3239e931467db8ab9597cc5a555ee81cd0fa0f933f6e20", size = 849241, upload-time = "2026-10-28T10:47:21.448Z" },
- { url = "https://files.pythonhosted.org/packages/63/05/5f61989017db4184bcaf813edfb4479fa1c79e82b6c6c6cc5b913c6b4a11/arrow_odbc-10.6.3-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:6f9ed02f5ea72d7c5c9630816991ae297575ff7c54bd50cf556c91e1fb45be6d", size = 873569, upload-time = "2026-10-28T10:48:00.33Z" },
- { url = "https://files.pythonhosted.org/packages/80/4d/0648226a6e7d702b23cb6bbe585a68a6e251e2fbbc9814aba2858c0b972d/arrow_odbc-10.6.3-py3-none-win_amd64.whl", hash = "sha256:fc01d3e974329d88614b282249f40ea70df0d493cc82695991e086e374eee50a", size = 661175, upload-time = "2026-10-28T10:48:30.009Z" },
+ { url = "https://files.pythonhosted.org/packages/7d/17/8c32bd7afa88b6cb8ee120e0051218d2bab52dd7e1e60387230ac5b00ef1/arrow_odbc-10.6.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:4de744a4f20ebe574030bb1943f9aa10cb242b77ac7128a1efd1539d943cee6e", size = 753105, upload-time = "2026-10-19T20:25:14.844Z" },
+ { url = "https://files.pythonhosted.org/packages/88/7f/e7c884fe16844f38899053f0e2e9f5ad66a984bb79823b1aef6d17b743d5/arrow_odbc-10.6.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:eafdf71851aac18c37822a67b57d704f2b9a7b133205da253445d374ca6c90c2", size = 735430, upload-time = "2026-10-19T20:22:58.19Z" },
+ { url = "https://files.pythonhosted.org/packages/67/54/0deab13f42cbdd8aaeec9b957164b49b54d805fb3c94ca27d3857a07ee09/arrow_odbc-10.6.1-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:72d97c33cc41bde4b071bad8b20932e360d11f4dad562bc0687cd59b8a11a3cc", size = 843468, upload-time = "2026-10-19T20:28:59.043Z" },
+ { url = "https://files.pythonhosted.org/packages/94/bb/920f8e03a0340b8c8ad945c6fde73df07519596def2e40ff6ef70358ec8f/arrow_odbc-10.6.1-py3-none-manylinux_2_28_x86_64.whl", hash = "sha256:78d48b9fe0291b32f50e8b1fb81c5bb2cd74fe7f636b34052551070a51ae2a5a", size = 869855, upload-time = "2026-10-19T20:25:20.298Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/12/311cd3f26939ead9b7f276988b9bd9d7b049c47c84af6b71cd35bd3d24c0/arrow_odbc-10.6.1-py3-none-win_amd64.whl", hash = "sha256:6226df0d91af94bdb47f575933e6c704e46d525d8b37348c43f7207f4b7d5e9b", size = 654895, upload-time = "2026-10-19T20:25:03.548Z" },
]
[[package]]
@@ -769,7 +778,7 @@ aio = [
[[package]]
name = "azure-identity"
-version = "1.27.0"
+version = "1.26.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "azure-core" },
@@ -778,14 +787,14 @@ dependencies = [
{ name = "msal-extensions" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/cf/33/0b881d0093d64c965bb20fcf71b806c7097881b544a2a55d519cdcf3bab9/azure_identity-1.27.0.tar.gz", hash = "sha256:6a4c50e25e8e9a47b324a95ad024897e56cb3c6241e0094e93b126a2bf71edc5", size = 331360, upload-time = "2026-10-26T21:17:39.095Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/e6/87/e88098a024d74b7434de5b110c261da024fe5d6c139f1847f6f8a2b76678/azure_identity-1.26.0.tar.gz", hash = "sha256:61b4538b87df10d2d6d44b77cd5ea28a8929fd4929d58b45bf3f230f7daba02e", size = 310151, upload-time = "2026-10-01T14:17:47.825Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/29/1e/6a2fe486576f41884a120ef7b2c3774f334f9143402fb73650c67db94ddd/azure_identity-1.27.0-py3-none-any.whl", hash = "sha256:92fd2a93c0d2f777dac6c04e5fd7d5734f6643582b983544ef66b4575844e46d", size = 203474, upload-time = "2026-10-26T21:17:40.92Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/d5/ae45ce8009d0d015304b206c7c4a6cbab02c03f8f07eedefb32b099ccee3/azure_identity-1.26.0-py3-none-any.whl", hash = "sha256:3c60d9682b3ac01507388d474cb5a5ffbe54152a96b6cdcaa7013da9315cf186", size = 195958, upload-time = "2026-10-01T14:17:49.705Z" },
]
[[package]]
name = "azure-storage-blob"
-version = "12.31.1"
+version = "12.31.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "azure-core" },
@@ -793,9 +802,9 @@ dependencies = [
{ name = "isodate" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/6f/18/eb2ade6eede419d33493016ec3243ca9860f67d0bc71aa0461cda2cad0c4/azure_storage_blob-12.31.1.tar.gz", hash = "sha256:0d6e5b860a1357efc602db04cfa7c9950ac86126edcf7db89320cde281555fe5", size = 672417, upload-time = "2026-10-29T23:34:05.69Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/26/ca/5299cedef5957dd838d4dc46f97bea37335bb39f6610d72b27e1a3317650/azure_storage_blob-12.31.0.tar.gz", hash = "sha256:997b393cfcbdc4b186d5911790d91f80387f7edc12c4d73eab963a2d26e5b2a9", size = 670868, upload-time = "2026-09-30T21:23:22.837Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/1a/93/b9f0dc477827887aef64012946bdba65e9aad67084ad38044661a72fe47d/azure_storage_blob-12.31.1-py3-none-any.whl", hash = "sha256:9e012d5f4ab658b7cb8a4581329d7a7aa2bf5f39b4d63baeefaae7f9e90fe6f0", size = 455904, upload-time = "2026-10-29T23:34:07.459Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/57/d1f45fbccc0dfbe6b6db7e5fa06199e35219c712f3743677bec1b4e7d78b/azure_storage_blob-12.31.0-py3-none-any.whl", hash = "sha256:0c0cb601d3462491d09ea96023cd791bb9dd4b173bf950daf3cff34ff47ba5b5", size = 455738, upload-time = "2026-09-30T21:23:24.944Z" },
]
[package.optional-dependencies]
@@ -837,7 +846,7 @@ wheels = [
[[package]]
name = "bump-my-version"
-version = "1.6.7"
+version = "1.6.5"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
@@ -850,9 +859,9 @@ dependencies = [
{ name = "tomlkit" },
{ name = "wcmatch" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/2f/ea/5ce9d814fbd532d8a5d8344bff688170c031f16ef11370b14b42fd3f8e7f/bump_my_version-1.6.7.tar.gz", hash = "sha256:ca21d720a4d951eb26354c9b62776a5481468c2319bd253c3494faf489f49c34", size = 1161983, upload-time = "2026-10-25T20:21:34.979Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/a9/65/338597a14c53ae2b9d004031dbcb61a0641484b20469b5b7bd5250a37a34/bump_my_version-1.6.5.tar.gz", hash = "sha256:0fd40347bca359bdee9999c93e101d2ad4612cf10319bc5e81181e57fc313e0d", size = 1158255, upload-time = "2026-10-24T14:02:28.037Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/50/b6/8a0bca33e613912312020e0a57a62e024a9fbc97a72dbc302fe7e3a6696c/bump_my_version-1.6.7-py3-none-any.whl", hash = "sha256:44e9f4fb733ff5d77f832249b7974d49a2f7acf3b566960265b5928788ab2f75", size = 68332, upload-time = "2026-10-25T20:21:33.587Z" },
+ { url = "https://files.pythonhosted.org/packages/15/bd/a8698a2143cccd7d5a5f23e7ec8520c2133e4083ccda1799531490fad17d/bump_my_version-1.6.5-py3-none-any.whl", hash = "sha256:7fc77cfa499edfa18072aaf7cc9e6697b8a66c7ba76f467c3c4fba4c554f2521", size = 67396, upload-time = "2026-10-24T14:02:26.214Z" },
]
[[package]]
@@ -1375,11 +1384,11 @@ wheels = [
[[package]]
name = "deepmerge"
-version = "4.0.0"
+version = "3.0.1"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ca/e9/5e99e56e03f9461858e686fdaf82e2516671bc1bd46f8b7ad7b9a8375f6f/deepmerge-4.0.0.tar.gz", hash = "sha256:948565f2bc9cdb41259a4a23d26638e5840c9e084a343ab279ffd3edcfdf74d3", size = 22792, upload-time = "2026-10-25T19:25:28.967Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/38/6e/5cb3548b4d3112fea529375e55e6f3cdc52b8054e3a66f203b1f888ba885/deepmerge-3.0.1.tar.gz", hash = "sha256:35b39a4cb92cf328d6eca61cbbf65f68a37c2ceb3085f0f853cbb2e52a59fc23", size = 22328, upload-time = "2026-09-01T14:09:44.383Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/c2/89/362146b2ec7d0d503f824ac8496c88f0408ee0d39203b02cc10feac08146/deepmerge-4.0.0-py3-none-any.whl", hash = "sha256:a36dd5810fc3b90bd3e0b681158221361d6a31a6109411b27b211b371fed6d5b", size = 15429, upload-time = "2026-10-25T19:25:28.012Z" },
+ { url = "https://files.pythonhosted.org/packages/20/91/600003aaad107e27553fbc9cbfc57e96fa37e0223d2ef9d7d3a0e8d8d070/deepmerge-3.0.1-py3-none-any.whl", hash = "sha256:35c96f6a68fcf90719a5b31d9f8042ecef6c00fb56836660d33455d0f5cfda65", size = 14909, upload-time = "2026-09-01T14:09:43.364Z" },
]
[[package]]
@@ -1402,11 +1411,11 @@ wheels = [
[[package]]
name = "dnspython"
-version = "2.9.1"
+version = "2.9.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/91/21/7fcd6780f61e9756aafaea760560509bb4111375daa2e23c1be7a89f33f1/dnspython-2.9.1.tar.gz", hash = "sha256:9a6d30e55f8dc63e6052597666112bff0586b95a2474a9deda03afbbf820d37a", size = 455302, upload-time = "2026-10-29T18:05:12.156Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/ef/4a/50822184bd67cc6493f0fb6a880749158fcd31ab3fa07409acfd91f9fc85/dnspython-2.9.0.tar.gz", hash = "sha256:b44dc6b18f07a8b1c56676a19fbfdb5209415b046a9cece286baafa87ff3f7f1", size = 423560, upload-time = "2026-10-09T00:07:24.352Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/61/55/f90405bbbbd3dd8f788e92cec2d39964f5c09783f0570fc35f3bb3381485/dnspython-2.9.1-py3-none-any.whl", hash = "sha256:7c81caf8de1ed57b8cfc7e0ad4f528a0b8ceace473c7eb946df195dc9def751f", size = 360364, upload-time = "2026-10-29T18:05:10.206Z" },
+ { url = "https://files.pythonhosted.org/packages/10/02/cdcc9b7c051786a103c3b09e1003a82fa0c66bcb91ffbdabcfbf7b4163b9/dnspython-2.9.0-py3-none-any.whl", hash = "sha256:9a4aedb833c3c1b49214d04d44d3032ab7a9135f7c1d29a549b4ff78fd82fda9", size = 354822, upload-time = "2026-10-09T00:07:22.622Z" },
]
[[package]]
@@ -1470,14 +1479,14 @@ wheels = [
[[package]]
name = "faker"
-version = "40.44.0"
+version = "40.43.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "tzdata", marker = "sys_platform == 'win32'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/87/22/965c7b822f4c80f6e003c696d5fa01b48eea6cc7852d61aec8779519b346/faker-40.44.0.tar.gz", hash = "sha256:97245a3756dcaf35418b89bea9f03ae6881557db0032bf048744c2aaee2a9108", size = 2034259, upload-time = "2026-10-28T17:06:41.048Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/33/6c/b8793efc2f00a912ef17cf0b61b717cddf34607499efcb8a32238c119368/faker-40.43.0.tar.gz", hash = "sha256:02fae4327c03a4a6315e1b428a3878f435bfc276c93435ea349b95c0c9372361", size = 2032713, upload-time = "2026-10-09T20:06:29.652Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/58/9e/3da962809aefa00ddf457d1b11bba41d7766373a0ad9e537fa0804604361/faker-40.44.0-py3-none-any.whl", hash = "sha256:db808a3ec33fe9ce5caf40b9fb61f85f46e429a56966f765209272adec83e4c6", size = 2069747, upload-time = "2026-10-28T17:06:39.109Z" },
+ { url = "https://files.pythonhosted.org/packages/81/ed/0d6d0d6467ae3d009fb82fb411867c7fcfeadbdd25602cab0d7a7963f400/faker-40.43.0-py3-none-any.whl", hash = "sha256:9dd7c0ddfaf30c842b05502d3cf641c135e0120a3a19047008ba8525b72953ed", size = 2069082, upload-time = "2026-10-09T20:06:27.166Z" },
]
[[package]]
@@ -1569,11 +1578,11 @@ wheels = [
[[package]]
name = "filelock"
-version = "4.2.5"
+version = "4.2.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/aa/4b/2320d9fa03b65b4beb43b521fe1b12b7f87ab91ee42de3a286075016d205/filelock-4.2.5.tar.gz", hash = "sha256:37d8fa4f8b5e9233612e68ab23bf64cae41771322c8797361088ae2895b12894", size = 659943, upload-time = "2026-10-30T19:27:22.237Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d8/28/3893324bcadf8f89b1616637468d766af3f625008c0735bf704fdfa86617/filelock-4.2.4.tar.gz", hash = "sha256:c6d8663535e1650ee430507b4ffbac7b464dd64b7647ea0112a9f406499d163d", size = 652444, upload-time = "2026-10-19T01:57:51.001Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/14/2d/2cf36eca330b7a97c0e9076e8fc4272127c29398fa2bb7b24d4321b8b476/filelock-4.2.5-py3-none-any.whl", hash = "sha256:46ce42d27e5cdde09ee7197349663e97cb106679bd9e75db3a09679fbc7aa1a8", size = 163877, upload-time = "2026-10-30T19:27:20.409Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/1b/5e74289a1878e666bb4b65fe5805a04cafcac2aa5f519ef1ead67ef91c56/filelock-4.2.4-py3-none-any.whl", hash = "sha256:301241438c57936ef93f4243a94f2559022268bfcf9b375a9a72eb0f8db8358e", size = 162475, upload-time = "2026-10-19T01:57:49.419Z" },
]
[[package]]
@@ -1780,7 +1789,7 @@ wheels = [
[[package]]
name = "google-api-core"
-version = "2.43.2"
+version = "2.43.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "google-auth" },
@@ -1790,9 +1799,9 @@ dependencies = [
{ name = "protobuf" },
{ name = "requests" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/a4/74/307919972c4dc84598f4128a8d6ae05ac3d9e6ca73fe60a7bb93c670931c/google_api_core-2.43.2.tar.gz", hash = "sha256:5dfc5e9d52982bcc03bd6af380a893365b5fdf907dc527f63d3a372833c33329", size = 286250, upload-time = "2026-10-29T17:20:40.193Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/bf/1c/83011225511b8a0aeee9633d58d8e6b5d858ea5502e76117bcefb620245d/google_api_core-2.43.0.tar.gz", hash = "sha256:d67355a22d995a009b6c5ca98744ea35fbf4f9c2c006aa0588c0941d1ceb5216", size = 282948, upload-time = "2026-10-22T23:43:15.125Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b0/1d/a0120c7d937597293fce74510c80fe0cec8e45b1d702a816b567b6754c25/google_api_core-2.43.2-py3-none-any.whl", hash = "sha256:5a9d21baab97fd3c0547ccfc186a9e64f4227d8d7026f393835316400b29a6d4", size = 224304, upload-time = "2026-10-29T17:20:17.733Z" },
+ { url = "https://files.pythonhosted.org/packages/63/5e/ea85dbdb29e05a1dab94be7e4c07e5099b7048074f9ededf7d39636a878a/google_api_core-2.43.0-py3-none-any.whl", hash = "sha256:dd588ae9de5e372be89b87c68b1e1555fe89add1809d623182b773d14f96859a", size = 223488, upload-time = "2026-10-22T23:42:44.924Z" },
]
[package.optional-dependencies]
@@ -1872,7 +1881,7 @@ wheels = [
[[package]]
name = "google-cloud-bigquery"
-version = "3.47.0"
+version = "3.46.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "google-api-core", extra = ["grpc"] },
@@ -1883,9 +1892,9 @@ dependencies = [
{ name = "python-dateutil" },
{ name = "requests" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/b0/96/482bde8e8b87b65f80aa9c0a4fa86e6e92ae407b6f50da574f264b923b64/google_cloud_bigquery-3.47.0.tar.gz", hash = "sha256:bca7a5ff52326172dc8c292e4592a5b46b491b6872010d242d7663d8ea89b12f", size = 553095, upload-time = "2026-10-29T17:35:33.836Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/df/1b6f6fa4c119babcbbd002826a0a24ac287066808fbc873e7733b5ecc2bf/google_cloud_bigquery-3.46.2.tar.gz", hash = "sha256:a87e2e8026133e709deab9ebf537a6a2eee3fbcf4befa334a2f9f6164435b331", size = 530538, upload-time = "2026-10-15T17:49:37.245Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/4b/48/79129b85526710828e84e83cb66efad3680587ac63dfc30ae6eb7ab2fb7b/google_cloud_bigquery-3.47.0-py3-none-any.whl", hash = "sha256:4e42781c3aa7acfc1d3a91ac4052aafcbc05288a4cc1796550f4f94cb9ee3aba", size = 275370, upload-time = "2026-10-29T17:35:31.888Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/ba/e89552c32a1cd5483261483c303da560e6504365d7314ecd88216ff9e119/google_cloud_bigquery-3.46.2-py3-none-any.whl", hash = "sha256:5e9a4e15f778cc00ee88167de9934a4c1bbb7fc20058e8f573f2bfdaf0d99052", size = 269195, upload-time = "2026-10-15T17:49:06.152Z" },
]
[[package]]
@@ -1961,7 +1970,7 @@ wheels = [
[[package]]
name = "google-cloud-sql"
-version = "0.1.3"
+version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "google-api-core", extra = ["grpc"] },
@@ -1970,9 +1979,9 @@ dependencies = [
{ name = "proto-plus" },
{ name = "protobuf" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/d9/8d/15f6c63fe83d53b6f1a0108d1e41c68c7ea42cb9c85bd7ad3e3a222afb26/google_cloud_sql-0.1.3.tar.gz", hash = "sha256:3140a6fd82b002cd2c1605d4985de8d0a1a42013d358e87e8d21b2b0a5b3a7cc", size = 1260732, upload-time = "2026-10-29T17:20:51.36Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/dc/02/bb267b1a9917eb95f069da6116c5928fe9fba82f315271228fc0d1bdbf7b/google_cloud_sql-0.1.2.tar.gz", hash = "sha256:c42bcd1b30f5d481463386da3713e35e3f326d80a41fbe89c5836a2a3dcd09ba", size = 1260461, upload-time = "2026-10-22T23:43:30.466Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/54/f5/df647f08a3e06f9beccbf5a92fb2553350455d0cb0f883afda169c68b39c/google_cloud_sql-0.1.3-py3-none-any.whl", hash = "sha256:c0bdb1e4aa660f00860272131f73bc1089a52fcdb8a7660a7e02235598976fc7", size = 1263464, upload-time = "2026-10-29T17:20:32.421Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/31/b35992f23a20b7858261ff2eeaf4279b9b9adf09d0312bda4b4c3ef56081/google_cloud_sql-0.1.2-py3-none-any.whl", hash = "sha256:220b4f6433dea8c8e421e9d51b683faa6ddc87b0b9078b2a1dde1f391b6f46dc", size = 1263305, upload-time = "2026-10-22T23:43:02.113Z" },
]
[[package]]
@@ -2044,7 +2053,7 @@ wheels = [
[[package]]
name = "google-genai"
-version = "2.34.0"
+version = "2.33.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
@@ -2058,9 +2067,9 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "websockets" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/22/89/4621aee9cb183efa5b8a187f0e315c49fddcfb8cd7f238b4a3bf4946dcd0/google_genai-2.34.0.tar.gz", hash = "sha256:8e182cf3f73384167b96861cf0aef5102b5070f2db1ea11002879e913806ef37", size = 791853, upload-time = "2026-10-29T06:43:23.706Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/83/a4/b83b378b4a2fbbfb300c3fde0ff9f443f049c33e2accedf06bc0ad9bf393/google_genai-2.33.0.tar.gz", hash = "sha256:fced7f746da45264b34bcf6044a6fcdb91ebad1825e95c324ee48addb6e557ae", size = 758627, upload-time = "2026-10-23T00:06:28.332Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/2e/42/c24db865d9231b1c672ea26242a4dcf340e200bb1a9823dce4aa9c4c07a4/google_genai-2.34.0-py3-none-any.whl", hash = "sha256:a4cb02aebc4c357f863d2938132f0e3f7967fc5e00b8a4078673f68aad340665", size = 1258155, upload-time = "2026-10-29T06:43:21.121Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/3b/9ae8e16c5b2177bfdec1c64769a93eea9093de684e0bbb95c10c77645fef/google_genai-2.33.0-py3-none-any.whl", hash = "sha256:f3f8ab696b35caf95c222e05268740c1ccd6934257890506be73b126150d3b14", size = 1222487, upload-time = "2026-10-23T00:06:26.528Z" },
]
[[package]]
@@ -2215,77 +2224,77 @@ wheels = [
[[package]]
name = "grpcio"
-version = "1.85.1"
+version = "1.84.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/f0/03/f9e805772d4d01da2a1754a5f63c58601eacaf8f1e878729217188de7d16/grpcio-1.85.1.tar.gz", hash = "sha256:5f530287d5de5f7f66089ead81de44f6787162944dab3922efa65dfdff108a1d", size = 14091456, upload-time = "2026-10-30T16:01:16.909Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/47/3c/ab73cb485967ac57f00350fef66c8180400c6a832436cf39811cc7d41f8d/grpcio-1.85.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:d2a776b113e7bd6d139e679f5da281193d6410cce361e4180c9da0d553e01927", size = 6823898, upload-time = "2026-10-30T15:59:12.079Z" },
- { url = "https://files.pythonhosted.org/packages/81/0f/80bd2a767a8cb78e9c257b9062f9786b67c3ce26d403825545ba2e9a79ad/grpcio-1.85.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:cfa9bf4daed57dd5bd5fac4be3276f5dc1703f41abf4df0737f4e7952705cbc6", size = 12654220, upload-time = "2026-10-30T15:59:14.324Z" },
- { url = "https://files.pythonhosted.org/packages/f8/a0/efb97c6639f59907fe3c17270904918b37cdc406eba2dbe97feacf309a86/grpcio-1.85.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9885436e83eab92dd0d476642baf08a17c55ef2b1db430adcba3e89bb6938a5d", size = 7215807, upload-time = "2026-10-30T15:59:17.395Z" },
- { url = "https://files.pythonhosted.org/packages/f7/f2/2b0900585deed45e126a1d5939acbaba94f6f4c62a787949cdc03329979d/grpcio-1.85.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3bbf7ae798e38eccb1b7abf169d9e228514418da6f68caec69a295fde899ec17", size = 8200549, upload-time = "2026-10-30T15:59:19.657Z" },
- { url = "https://files.pythonhosted.org/packages/1a/4f/30f66cedfec6c03bc304b28fbe073ed53179cf37a511b8ee8e8e7e2d3f17/grpcio-1.85.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:83aced2a1c05648cee0de34e531351118b14c58238f0a61b43559ac8851647a8", size = 7392335, upload-time = "2026-10-30T15:59:21.917Z" },
- { url = "https://files.pythonhosted.org/packages/4a/ad/5dee0e6e5a57f88379bc2820aa6e3098a2174e21a40bfb961c6d5f5b28d0/grpcio-1.85.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:b364bc3daa601eb1cb0023e90b1309cbf6da0de6ca40a19f2d5a31edeb9990e3", size = 7955183, upload-time = "2026-10-30T15:59:24.197Z" },
- { url = "https://files.pythonhosted.org/packages/43/d8/93b729c8fc1aee6232906cffd44a9dfcb6f8387e6279a1238cd972ff468a/grpcio-1.85.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:c9a4a4e504bd914e10c0d6190ffac59b9e21f1c962366b72293075ade097793d", size = 9212733, upload-time = "2026-10-30T15:59:26.388Z" },
- { url = "https://files.pythonhosted.org/packages/08/4d/d6339c51032649a90c347a67ad41b56f2f1a564f4960efcc72409fe03eba/grpcio-1.85.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:83b09ca3f9602caf4f1b4295b4bb002b10477789ff9797feeedc811b9a6be0b7", size = 8332651, upload-time = "2026-10-30T15:59:29.113Z" },
- { url = "https://files.pythonhosted.org/packages/b4/bf/085116e89db4f758af02eca2da9ad3a1890fb1691f5e2fc882c402ed9622/grpcio-1.85.1-cp311-cp311-win32.whl", hash = "sha256:bbc678a2b8d10e0dea666d948f36237e681be3686079564937df689bac386bc5", size = 4779959, upload-time = "2026-10-30T15:59:31.001Z" },
- { url = "https://files.pythonhosted.org/packages/9b/b8/fb80401f9eaf51eb2fc8c5dbf8e4f8cbfeebc21bf837f7c6576790ce433e/grpcio-1.85.1-cp311-cp311-win_amd64.whl", hash = "sha256:27b519f6594da04cf2dda1378f46811b2b6160d60a01f88a0ec4de009bbbfa46", size = 5615553, upload-time = "2026-10-30T15:59:33.252Z" },
- { url = "https://files.pythonhosted.org/packages/31/8c/b6d9ea0bf02a6b47ba34c5f936da2921d76f5e40cbb411e9abb246b70d2d/grpcio-1.85.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:1a10d337967ec2be049c6a2345320f6a59f71bf9f54febfd80e5a966cffaa945", size = 6797195, upload-time = "2026-10-30T15:59:35.364Z" },
- { url = "https://files.pythonhosted.org/packages/c6/ee/f60cfacd679fe811944be391c899955c3d9c7e07db922b47e535ddda550c/grpcio-1.85.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:658a9d89c5736bf4aa3ef4ecbc79528e26d8e23c5e1c83417d2870a1a06063e5", size = 12633025, upload-time = "2026-10-30T15:59:37.66Z" },
- { url = "https://files.pythonhosted.org/packages/f1/0d/5dbe225f0ceb208538dc8c16c5f52b0c402b91611f981604b5e280439dfb/grpcio-1.85.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:843aa281a48188621f50880496d35defed51298bcb66d5345d5f282fd3f87354", size = 7170616, upload-time = "2026-10-30T15:59:40.138Z" },
- { url = "https://files.pythonhosted.org/packages/54/35/ce972a4e5bb721ba85f5c9e20a0029253ae0b2deb5276dafa3383e025133/grpcio-1.85.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:e768556a2346d1d2921f511f3145e2462e4fe68095c31e4db407558bcd3f1d4f", size = 8180786, upload-time = "2026-10-30T15:59:42.7Z" },
- { url = "https://files.pythonhosted.org/packages/00/8d/74a8649d2787dae0a661855cdf09a8e2238fa741689bb368ca1b4f6d5b6f/grpcio-1.85.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a1a72d19c0ab84b4b73b6c47eeaade76d84b1973a51593ff1160a73df33d03d9", size = 7362744, upload-time = "2026-10-30T15:59:45.076Z" },
- { url = "https://files.pythonhosted.org/packages/03/72/87ef13aba6bd2c5c6d4c5117238b17b1e3c1c1efa26c66cb377053124eb7/grpcio-1.85.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0e07f477e1d1a05bdbe71d63add964c5457e7ab2e3f5fd32859639ed8ecb976e", size = 7929643, upload-time = "2026-10-30T15:59:47.294Z" },
- { url = "https://files.pythonhosted.org/packages/09/f1/2e75b2906c9643e0be81a74bf23ae10c29661e0199c8f3a6e2c23707e882/grpcio-1.85.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:b2846c70ed58c8e149e8e5d867af40805a13b151d84157d8272952d711a77c5c", size = 9181161, upload-time = "2026-10-30T15:59:49.399Z" },
- { url = "https://files.pythonhosted.org/packages/c1/d9/6b9127e8fa3ef14a3ec199436bec6c1a54a0bb429ca261b1a2fb518938d1/grpcio-1.85.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9998f03ac0dfed226eead6e93cee1b547d7a32e47e1915dee94eff0e113b167e", size = 8318829, upload-time = "2026-10-30T15:59:52.09Z" },
- { url = "https://files.pythonhosted.org/packages/21/46/ff3374d33efee490a0e50e3c3fc59280b7f7fc2ed3ab1ee8c4e9873dc83d/grpcio-1.85.1-cp312-cp312-win32.whl", hash = "sha256:2670c03d154f6bec66a0fef97c607116761b032183187dfdfe229b5975820a4f", size = 4768056, upload-time = "2026-10-30T15:59:54.132Z" },
- { url = "https://files.pythonhosted.org/packages/dd/48/4030a63540089d00a36cea15282623bcf084a5ea4c0ffc4685a42ef0f342/grpcio-1.85.1-cp312-cp312-win_amd64.whl", hash = "sha256:eaad7e6aa9ac3aded4f6bc86a18bf2cff3de356f972bf32a5558df9788085b85", size = 5607218, upload-time = "2026-10-30T15:59:56.721Z" },
- { url = "https://files.pythonhosted.org/packages/c1/05/2d40be7dbfd8e66e8e5675ec695fafacc69823dcd2ef5d387ad7431c7af7/grpcio-1.85.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:e117b074d707d418ff411e0ad6fc86e97e83047b742488b41d7a5b7a62953cd3", size = 6801759, upload-time = "2026-10-30T15:59:58.834Z" },
- { url = "https://files.pythonhosted.org/packages/b0/98/530af74d704dc8e79c79b26888af44ccbdd1827bfabc9cb405b3d4cc184e/grpcio-1.85.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:6bb4bbb5cb46010687db552fd88852c405595814fb7d435b072124bd6b93f827", size = 12629341, upload-time = "2026-10-30T16:00:01.159Z" },
- { url = "https://files.pythonhosted.org/packages/21/cb/bac12e6c7ab3b1c6b50816fdcbf7f97aec1bd0992cc0d193e332aff716a5/grpcio-1.85.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:0bde4264ca71aaf3bc56c587380c426b1c959d1db0ab0a3f632bc66515f9726f", size = 7174387, upload-time = "2026-10-30T16:00:04.847Z" },
- { url = "https://files.pythonhosted.org/packages/90/0a/7770a652508a3a6c7d16fbb81db0f0b2be8a73a469af88a6cdfdc3d29cf2/grpcio-1.85.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:7b931335c163510fb054c30007fe3556ef3bf354a1c37b5da403f2e52c6bbd95", size = 8185778, upload-time = "2026-10-30T16:00:07.009Z" },
- { url = "https://files.pythonhosted.org/packages/bf/cb/52417fb272b7603d13ec8688e843dfd8df64584da1951ad954b9120d7c80/grpcio-1.85.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:de520b1555ae445cdc4f8f1cc7ccba12a19992bfafa7c292ebe7fe1fe5da6c53", size = 7364062, upload-time = "2026-10-30T16:00:09.777Z" },
- { url = "https://files.pythonhosted.org/packages/ba/86/9329799fa9defe9327084b9ca7b9a9745e83e7174bb90f2bfb2f37063967/grpcio-1.85.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:2b20ba567ca818b8393fd4668878b5688cab32a46e8e7a4d1a58641fc0072df9", size = 7932775, upload-time = "2026-10-30T16:00:11.956Z" },
- { url = "https://files.pythonhosted.org/packages/48/e4/826e0ae8f89a3d2ab3a15b91ee4ef486bdd7a09fd1ff72e12d01505341dc/grpcio-1.85.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f0ebfbd8ba42497e0ff7229c8c5e166e07c2b348462c1481dbd90faa978ad8af", size = 9173461, upload-time = "2026-10-30T16:00:15.696Z" },
- { url = "https://files.pythonhosted.org/packages/88/2d/d4baf37bf1a9ba8222dcb133c6aeb09119b183bd7b471061762a1198461c/grpcio-1.85.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:000999ea9bba80217b1625782d638e7a688790f6154e845e18826f7da859bfc7", size = 8311104, upload-time = "2026-10-30T16:00:18.533Z" },
- { url = "https://files.pythonhosted.org/packages/e4/02/b07af232a583727fa84665d11165bbd7be6ddd31d84c2b8cb62a7b69062f/grpcio-1.85.1-cp313-cp313-win32.whl", hash = "sha256:b3722f4e01820f4d2422679a567fe33f31b6f14eba4352657a1078f4b4ed323e", size = 4769351, upload-time = "2026-10-30T16:00:21.165Z" },
- { url = "https://files.pythonhosted.org/packages/f9/f0/d2448e860d884e8c89dd9135cd9373df064c10227d90973d822c2125428d/grpcio-1.85.1-cp313-cp313-win_amd64.whl", hash = "sha256:c17d61af28b322c48ab3881c685e5f6e7e367c56844cf589ee7707be9477b969", size = 5606831, upload-time = "2026-10-30T16:00:23.621Z" },
- { url = "https://files.pythonhosted.org/packages/39/cc/da49c22b9c571b5315acc507e2ff6568e5ff9d4c99895285910283bf87e3/grpcio-1.85.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:2a378eb60ea42996be5a8c7167302fb30d968fcfcf9edd7b3fa6c985906c4500", size = 6803626, upload-time = "2026-10-30T16:00:26.587Z" },
- { url = "https://files.pythonhosted.org/packages/12/92/35bcee92bcfc2b0f66290a5dd6dd23ced7b9423fe269cf4495136e73d55b/grpcio-1.85.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:90a7e0f26de39d15f5c741b8848bb2065c9ab7351c57d4fc973ff5cbda0cb9c7", size = 12640307, upload-time = "2026-10-30T16:00:28.969Z" },
- { url = "https://files.pythonhosted.org/packages/f2/b5/469b69dadb2c413706f16e0c3e7a2749cc70abaab10d718873603bec29b2/grpcio-1.85.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:421925365f9a665830eded01b8053033abbc2fbabc9bb4e9c59c3b5b12acbab9", size = 7187308, upload-time = "2026-10-30T16:00:31.652Z" },
- { url = "https://files.pythonhosted.org/packages/c4/df/2fcb1567d37e91ce26bddfca30f39fe6c39cbc5a2f4a210cfb1246ced1b2/grpcio-1.85.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:8a921a98ed6142a29651b4a6f4c7b8e051d115f1e78c31655a9642dcf6356dd9", size = 8188002, upload-time = "2026-10-30T16:00:33.893Z" },
- { url = "https://files.pythonhosted.org/packages/7b/b2/c67c1d8c7b70a094d404b28233458887bc1ca1319daa95d8952621216717/grpcio-1.85.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:e0e6d5829b50a2ffcfcd2dbf08d02ac0d6919051a2e97c2ee27c99bc9916a245", size = 7368045, upload-time = "2026-10-30T16:00:36.675Z" },
- { url = "https://files.pythonhosted.org/packages/80/48/5cf6c704be4b059d508e8f6c981154223533595ba7a3ee531211ef8be62e/grpcio-1.85.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:58b5ebe509da292bec5a1913c1e4c14d842a33f6cb330a87d3713c5089dd0f55", size = 7941330, upload-time = "2026-10-30T16:00:39.173Z" },
- { url = "https://files.pythonhosted.org/packages/67/96/5ef14edb784ea2053610753993601e98a76a18b541a8a74103dc390540ab/grpcio-1.85.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:016982ebd21d2b48e955aa808e9db8d628708a35c6eba852cd0e70765826ab06", size = 9176909, upload-time = "2026-10-30T16:00:41.335Z" },
- { url = "https://files.pythonhosted.org/packages/50/01/4264ae95364bdeede4c8a5f0d83df83a21b477935f9ab8c829ae5606e1d4/grpcio-1.85.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:0fa3d7b8b95e8a609967991c3b0a302b2f9d510fe417efadfa7c7d2d044a99ba", size = 8316226, upload-time = "2026-10-30T16:00:44.199Z" },
- { url = "https://files.pythonhosted.org/packages/75/b8/79dace38eb4bb904751b804cfdc71609a3840f34084af7475169059dce20/grpcio-1.85.1-cp314-cp314-win32.whl", hash = "sha256:39b1965266b579f3758db20d74f9075b130d1b1edcf0464cda316e403a15da9b", size = 4853260, upload-time = "2026-10-30T16:00:47.098Z" },
- { url = "https://files.pythonhosted.org/packages/48/c9/68a6bbd2450f08942c3056acd8995ac28af2b4eeb0574c0a8e59fa59b140/grpcio-1.85.1-cp314-cp314-win_amd64.whl", hash = "sha256:5f015600190124813032da0560ec950c13c257f77c1307abd77c393c0994f76f", size = 5758958, upload-time = "2026-10-30T16:00:49.145Z" },
- { url = "https://files.pythonhosted.org/packages/1b/bd/1a2481b354ab4528eb8b3462937904c9206e2a54748f3f74a2576205ef3e/grpcio-1.85.1-cp315-cp315-linux_armv7l.whl", hash = "sha256:07bfef5df75875d8481d3403f864785e30096594162e8041c53a84fb11ee4f8e", size = 6803392, upload-time = "2026-10-30T16:00:51.782Z" },
- { url = "https://files.pythonhosted.org/packages/40/45/1868c12c18058d778fcecee0e04c867891a82d983d2dd0de2639158aa960/grpcio-1.85.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:e909e0f9145a555d3968dd2a448616b0d3cb58748b9e2053de3e7974b3e429bc", size = 12657793, upload-time = "2026-10-30T16:00:55.335Z" },
- { url = "https://files.pythonhosted.org/packages/ed/af/c283032a42df5217913b98bd0113738b68dd94b91cea16b2209565fefe67/grpcio-1.85.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1f3064e209167e5fcb75a72cbc18918c06027b92b29fa06eaff0126f9a2aab5a", size = 7209146, upload-time = "2026-10-30T16:00:58.279Z" },
- { url = "https://files.pythonhosted.org/packages/45/64/8a72fd6c20c206cff07eaa49b2ca63bb15d7ed9b6bf7c653ea10ec578e4b/grpcio-1.85.1-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:07675c635a2da0c772e58ac736a7fb10c7c7c1613157766441e6866ad97d25b4", size = 7511067, upload-time = "2026-10-30T16:01:00.806Z" },
- { url = "https://files.pythonhosted.org/packages/5d/b8/32afa404288c7eea4c6034d031f1a07eff772a8cdbfc7751314382e7b724/grpcio-1.85.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:759f7df78954e3ac0ed4c87612eff9125021b9271be37be8c88167aa533e29ee", size = 7410227, upload-time = "2026-10-30T16:01:02.966Z" },
- { url = "https://files.pythonhosted.org/packages/89/a4/1c51e2c2d0d9e4d37e8e8a7be412c5dfb3fd5c80ab100dd531fd81b44d43/grpcio-1.85.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:7cd08bd5ad063ad9639dbebf95d3a6b8c8aa2c9d49049bc0e8f47e73ecf1c1a2", size = 7964886, upload-time = "2026-10-30T16:01:05.208Z" },
- { url = "https://files.pythonhosted.org/packages/07/f3/e26f570129c162d8f15a02af6edb49e592ac1c88a63490cd3ec9142d8c67/grpcio-1.85.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:2198269c8b69cb1e9d5d20628029245e40b7227d5f6fc6f298a420701d8fb652", size = 8504060, upload-time = "2026-10-30T16:01:07.82Z" },
- { url = "https://files.pythonhosted.org/packages/16/e1/e334cf7442c01892ed813563b21b2a8ab5b042e34f33ad76cf64526d1de8/grpcio-1.85.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:7bc5de50374416223bc35332aa8185567ec065f86190ff0ca6e13176ed39a3b4", size = 8346563, upload-time = "2026-10-30T16:01:10.394Z" },
- { url = "https://files.pythonhosted.org/packages/66/bb/cf72913ce0baae1fed95445f1ba8ac309184ac16b4f20fb5f88219eff72c/grpcio-1.85.1-cp315-cp315-win32.whl", hash = "sha256:5740aedf1e6acde538d7b80a2507eb6ee0b8983c577a36890d27a0e2b62cc1eb", size = 4853404, upload-time = "2026-10-30T16:01:12.239Z" },
- { url = "https://files.pythonhosted.org/packages/ea/b8/b7cff7b9e1ff7b6a8d36a19ce8b9c575963e71fcbb3878b276cff6748b05/grpcio-1.85.1-cp315-cp315-win_amd64.whl", hash = "sha256:b32f39c56d22d2e8722eececd4376074d94e01595a29f4f944ae0b6cea1caa70", size = 5758584, upload-time = "2026-10-30T16:01:14.325Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/c0/3a/5c8e0eea2af3d30cb20d33ac325a98d9b799c069d63101ca4da20bcc776a/grpcio-1.84.1.tar.gz", hash = "sha256:446e7876b9b991fa124c1d414899fa7cf95f89a3e71237ea0161863dc0b2c09f", size = 13421202, upload-time = "2026-10-22T14:59:43.986Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/b6/99/8c0a3739dbc75bf509cf2a79a35ebb27e73cd7a1f9be25af842ae66e4749/grpcio-1.84.1-cp311-cp311-linux_armv7l.whl", hash = "sha256:a5ea4e8d1e9745ba6c2c9d14ed0861e9496a8051bf9fd8a17b667d9b9a0c1019", size = 6420007, upload-time = "2026-10-22T14:57:39.037Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/00/c8261b1d56715d09c922145d12427abc1b1be5afad031336d7b00bad7d8c/grpcio-1.84.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:11c1d07c58922f5dad022b422d3f7998755c3e35e2f1949bac47cfd7a5eb31fd", size = 12325248, upload-time = "2026-10-22T14:57:41.384Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/4a/30f23e49f1b8ab11ccd24cfa146524df5478f45db7167006580cdfeaa321/grpcio-1.84.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:6a22342e59b5b2f4c5ed276ef72a3668ec84f38d2f5fad8a64af0b3618e510eb", size = 6995366, upload-time = "2026-10-22T14:57:44.399Z" },
+ { url = "https://files.pythonhosted.org/packages/c1/d8/fd238758e4725f11d509c440de1b03a77c53d5f213fd45ccfb5f4f775aff/grpcio-1.84.1-cp311-cp311-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:bdd1181edaf42b6e79a457789fd43e85858db1df3861e4e5128ff4d6a49cb638", size = 7747707, upload-time = "2026-10-22T14:57:47.241Z" },
+ { url = "https://files.pythonhosted.org/packages/47/54/d6544884ef836bea968d3ed7abf179c4d33a794dd8a8b1c5cf002fff9620/grpcio-1.84.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:eb881326607275ad2d2d17dce09b823472414dcc559e8f694d5607fe8af3cbc1", size = 7153395, upload-time = "2026-10-22T14:57:50.28Z" },
+ { url = "https://files.pythonhosted.org/packages/06/9c/094cd0afd21348e65d5750a7fa4cfa5f8ba6ca354fe6aff9f1163f4c5d0e/grpcio-1.84.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:772ff116d0d31b2817e23fb9a1dfa52fa9fed08be0cd87b593450f034e2968b1", size = 7702992, upload-time = "2026-10-22T14:57:53.602Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/54/254432337e14beb08bc7f29fc10a86ef149f672124073f2e0d446dbf8538/grpcio-1.84.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:0225c1e29d1506594e75ac46fd7b327bbfd24c6306a3c479b2164c1c4e87901f", size = 8758120, upload-time = "2026-10-22T14:57:55.728Z" },
+ { url = "https://files.pythonhosted.org/packages/04/29/528ccb437076233d524e09e59320625b8ca9d94f0fb0198703e7100f3bff/grpcio-1.84.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a1ce9e360d0a4a329b925ae8b0bc49d97d663bcce50822eb862873798a86eb10", size = 8110925, upload-time = "2026-10-22T14:57:58.639Z" },
+ { url = "https://files.pythonhosted.org/packages/24/5a/e4005d02dd65884a525f49ba0aa0371967b7fb40f00d35818ab397d91a93/grpcio-1.84.1-cp311-cp311-win32.whl", hash = "sha256:b87f8696bb78a31328e815629d320304c3d5cca1c654d3fd7e5518685bcfb8d2", size = 4460566, upload-time = "2026-10-22T14:58:01.077Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/fc/fc040230b60529a5df349945eb1e4f8a201607a6e3d70f3465d80e801393/grpcio-1.84.1-cp311-cp311-win_amd64.whl", hash = "sha256:e44786d5c166944a53fb1fa3384d03f1dbade6f68007be6d0c5a1a0971cbaca6", size = 5225017, upload-time = "2026-10-22T14:58:03.113Z" },
+ { url = "https://files.pythonhosted.org/packages/69/39/34a3e64818b1fdaf73b6c873f7d085969f20c2aff114c8909cc7ee0cb2b4/grpcio-1.84.1-cp312-cp312-linux_armv7l.whl", hash = "sha256:db2dbe5380dccc2f5df967f31308a744e65d46e21b473a9c3ca033c4299ec3f4", size = 6381194, upload-time = "2026-10-22T14:58:05.289Z" },
+ { url = "https://files.pythonhosted.org/packages/77/bf/5f1922cafaaaa7b2b6a0f6b0614783efa8502f16c8dd9c7b8b131a3dc9d2/grpcio-1.84.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:f7533921b52ef94210437dedd3911e00b7642ca8d11eda64eb1715778630715e", size = 12304970, upload-time = "2026-10-22T14:58:07.714Z" },
+ { url = "https://files.pythonhosted.org/packages/af/f9/77e9991fd5ae7d754b78b3c8e8eb028ddc886a480b86b934ece85f727f95/grpcio-1.84.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e0dcc7b588e7b7dd3363317296983db23a26af8baa5884db15a10fc0c8534186", size = 6949905, upload-time = "2026-10-22T14:58:10.92Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/a2/af693a3bd3fb43cd97e4ad054ad5b117814c1a24cad048130e982a189f0f/grpcio-1.84.1-cp312-cp312-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:a92bb13dd1b31a16f631ab415710de59f404c37364c018e7486496fb449a89da", size = 7714870, upload-time = "2026-10-22T14:58:13.622Z" },
+ { url = "https://files.pythonhosted.org/packages/00/98/456ef643a796ba04ad737e39a820c27291c238db87f7d168355dbdaf7b4b/grpcio-1.84.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:474408c92b27c823ea6aab2315e2ea52d5a4e0040b37ce1e65e89d9149c48205", size = 7121552, upload-time = "2026-10-22T14:58:16.454Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/bc/c98f29a74b772ba9025f13243dc9547807583b1d111282bdcc53bb4f8db6/grpcio-1.84.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:912694cac76adb4f45563b38ae4d06d77a2081e8ed5cb20a3918c1affba83c63", size = 7673450, upload-time = "2026-10-22T14:58:19.009Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/2f/713bbfaf1cd7ce3a4626b8c0e93ec3f7493d8f52ccdb1ddb02fc98fc7d7e/grpcio-1.84.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:f821954e4d89e97f7d51c03073d6964e177f2d11181550a5a22c082fac614f43", size = 8725415, upload-time = "2026-10-22T14:58:22.23Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/ef/21db8160edde5646a0f688c9f7e97bf6106965e4e2ae1dcfe9649fe2db01/grpcio-1.84.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:02612f3c19ed3dd885e2fff1236bf5629e5b43b5aa84c3290b0d5f62f26b0ef7", size = 8090355, upload-time = "2026-10-22T14:58:25.039Z" },
+ { url = "https://files.pythonhosted.org/packages/34/0e/a762461094659b13a86a027461a6acdf4734dbbe17b19bb193191801b570/grpcio-1.84.1-cp312-cp312-win32.whl", hash = "sha256:380b58bf32707b4e3019c9567dab30375bd21f9a5c98aa24401aff1e73f3b262", size = 4444024, upload-time = "2026-10-22T14:58:27.015Z" },
+ { url = "https://files.pythonhosted.org/packages/51/b5/2dbcb95b7b3622253bbb09b79dd87787dd05709303e571cc0eb150f860d1/grpcio-1.84.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a221fe42e444cd0099752028810a289efafce1b5690dfe19a831d2a2fe6ea62", size = 5218662, upload-time = "2026-10-22T14:58:29.124Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/57/f25d35af33566daec7c9eec09c65e06f510cea2cc392bc2988af26250a4e/grpcio-1.84.1-cp313-cp313-linux_armv7l.whl", hash = "sha256:9c24b1ebbdf867b33d74868044fed5c7c5a6a6e00787140b5f582572f7a7e633", size = 6393057, upload-time = "2026-10-22T14:58:31.938Z" },
+ { url = "https://files.pythonhosted.org/packages/1f/d5/68c3c5804bc7b05a0a544ac68439ff7eb493d5d394e5361bc94e60b6be0b/grpcio-1.84.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:af0f0c10281c2b62c68868e0a98de24f89c31308c68dbf19320d30c3a329aba5", size = 12302330, upload-time = "2026-10-22T14:58:34.375Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/8f/d25a5d4d4f34e99ec31869ad3b988893f15d5965625cfc35a5faffb9889f/grpcio-1.84.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c1c04d2876ccb063507a976602bc88b5c31330982e650c620bf8e730b120c9d5", size = 6954893, upload-time = "2026-10-22T14:58:37.408Z" },
+ { url = "https://files.pythonhosted.org/packages/88/f8/fae50f0e28281be328614ad772118290d0f804d9be5be2d5708e7ea8d9aa/grpcio-1.84.1-cp313-cp313-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:3cf14e9cf02d12628f20ec205d699838c47e2d3741690fbf14ae626bfa32ac0b", size = 7723209, upload-time = "2026-10-22T14:58:39.537Z" },
+ { url = "https://files.pythonhosted.org/packages/59/4d/99dfd9cfab7f748d52cfb9177e8967d5cec0a50f148d4e3b9a0bf0b1220c/grpcio-1.84.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2ce575e400e0dec9484f5d038fa12d6b3919d493c2beb897a23b9a32fa3bfd12", size = 7125010, upload-time = "2026-10-22T14:58:42.037Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/cc/7354ebd3d4c1bef08b062b9f07d154141c2b9043f677a219bc7943070867/grpcio-1.84.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6e9be93efe763bb2b12a4c0d5fc13082f24de6cbb53a9d11078c9d0c8cdb2b8", size = 7675985, upload-time = "2026-10-22T14:58:44.564Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/67/37ccd26e1f7a7576f53627b45e222ac89ea3465b3f02836f480ac26e4dce/grpcio-1.84.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:f216832aa84ad7e697ae1ed993c33905d27e27cd1b02016af1441be2091e287d", size = 8726955, upload-time = "2026-10-22T14:58:47.051Z" },
+ { url = "https://files.pythonhosted.org/packages/88/cf/4887c033734182cecd12d3a1f6f9e7f84e9a5589a1ca2f564ff2ce8f2a29/grpcio-1.84.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:6a4a06d757e7ea5ddf636a4c40914aa36fb4f7e2a03abf727a16e61822331343", size = 8086854, upload-time = "2026-10-22T14:58:49.831Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/2c/34aca5188055d8e5342a5a672f6d321f331c8e1e944ea7d3650ee71f4943/grpcio-1.84.1-cp313-cp313-win32.whl", hash = "sha256:4052f86fdb5105ac8b02c77d17cd9beff78790a0c3117afdd5316154cf4348a5", size = 4443070, upload-time = "2026-10-22T14:58:51.851Z" },
+ { url = "https://files.pythonhosted.org/packages/68/25/5c315847ab3bd6d3b9f3caad178596eb1affd5aafd02374813b27bddf708/grpcio-1.84.1-cp313-cp313-win_amd64.whl", hash = "sha256:b7452ccc592e40e4167adec46195d85387c8036f0aadf6f96ab6ed931205d0ce", size = 5220505, upload-time = "2026-10-22T14:58:54.464Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/d8/bbc616bd123ddc2fd1f53f49e40cf537f7dd9c483f47bb21be61c4aa0ae1/grpcio-1.84.1-cp314-cp314-linux_armv7l.whl", hash = "sha256:42ffbbc11fe2e4b02a4b51b090f8ca6727414748cf551539665b5592372263e8", size = 6393649, upload-time = "2026-10-22T14:58:56.534Z" },
+ { url = "https://files.pythonhosted.org/packages/80/25/71e0903ca23c483aad588daa7744f9b01c149db9cbfa13eb24ef5f813a2e/grpcio-1.84.1-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:f2482ebe5e3a9c6d1f99859e135cc0ca370362e3b2a94cb6304f64943997bfcb", size = 12308221, upload-time = "2026-10-22T14:58:58.92Z" },
+ { url = "https://files.pythonhosted.org/packages/28/01/a0207b967ab991bc009030763e3c7e6280cbeda1de06351e16034d34547e/grpcio-1.84.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b8fb04fe0ded6fb1c5edd4c90ed39193d7b6226214685111b25a153ccb33a52a", size = 6963837, upload-time = "2026-10-22T14:59:01.562Z" },
+ { url = "https://files.pythonhosted.org/packages/38/74/a4171ad8c4b0e80371afb27503cad580d4cae2f116b56adbb9dafa7e3c76/grpcio-1.84.1-cp314-cp314-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:ffc0be893d8df55f0d23afbd3b776ede444847260a4a00ac80326a32dac91ad3", size = 7722977, upload-time = "2026-10-22T14:59:03.784Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/b8/73e85d67437fa4324933cc625a6a8e8e5f22fbdfe6757d949a6958b87479/grpcio-1.84.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ccd8be89a73d276336cbf223b22bd5c677d9fd15233100f208845c6a0c7cbc72", size = 7126919, upload-time = "2026-10-22T14:59:05.982Z" },
+ { url = "https://files.pythonhosted.org/packages/03/60/734b781ca01f562e6e01c84ef8e762f1ebcfccd02b29be0b77513fc26da0/grpcio-1.84.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:ced0c5ef2709964aca16e6af6f74f682b57872d2f7c7c553916c6b86dac64fa4", size = 7685628, upload-time = "2026-10-22T14:59:08.495Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/c8/03051aa012430b2dbd15fbf9af42482d945197b7886c77c557475d1bb4bb/grpcio-1.84.1-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:28caf3df58096e2cb06f0f7e672160e65e9df5e053f392594d93d33a15fe1ef1", size = 8728232, upload-time = "2026-10-22T14:59:10.709Z" },
+ { url = "https://files.pythonhosted.org/packages/58/83/c87911b98ed766d7084d64053dc06f7855267f686b673dc0bb1ac8c07aee/grpcio-1.84.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:f4b64099afaccdeeb987240af47aa1fc14f57285d3d2ff000c7327228624999a", size = 8088730, upload-time = "2026-10-22T14:59:13.431Z" },
+ { url = "https://files.pythonhosted.org/packages/88/bf/6b18690790c0c4d7062749785bc8e515881a8a4058f46cb2925c86adc9e0/grpcio-1.84.1-cp314-cp314-win32.whl", hash = "sha256:116b4796da0e7fca3fdf6edf422ea7521c25d8c4be64ab7e3b5e2a77c832b0ed", size = 4527749, upload-time = "2026-10-22T14:59:15.712Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/d9/ee50fe67481254c45c9b709b31ba468e4c90925018f4507c424369a9abd5/grpcio-1.84.1-cp314-cp314-win_amd64.whl", hash = "sha256:3875f4c4caf0c54ac370b66322f0bf2d46e9d21414be36ea8b7f618badb3b87c", size = 5359432, upload-time = "2026-10-22T14:59:18.331Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/c0/fc3341990f75cba2096d07ea5e7a8f78af8cb36f6b1fd78660b8c1220e11/grpcio-1.84.1-cp315-cp315-linux_armv7l.whl", hash = "sha256:775f98cb476625936061b91e3c1da229112b212a6b2a70fefc9c87e5aaf9fba6", size = 6393689, upload-time = "2026-10-22T14:59:20.402Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/f4/6b9a70760bbd8e927f31452ed2ba33cc3dc1a327f1b46c44f65ddb6dbf0a/grpcio-1.84.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:cfdc7530e5707ab6e5c80fc624f89e26ddf8f38d3e89e5226dd7e55c080e9b65", size = 12325454, upload-time = "2026-10-22T14:59:22.805Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/87/e44f1c73d4c003a10a8c9b348b44296e99ec5623b70fb441ad250f4b532b/grpcio-1.84.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c61a0d5c60506b2371444716b56d6c633d3c56a17c703566bcca39604c02c12c", size = 6984778, upload-time = "2026-10-22T14:59:25.385Z" },
+ { url = "https://files.pythonhosted.org/packages/19/9f/efd7828266b9e5ff0764af8420d52add5acfc13382d073596f15d7dfc183/grpcio-1.84.1-cp315-cp315-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:79b3ee4683290b3cfabadc718a008c3474073b585bc450231a842c21c2ef1f27", size = 7073415, upload-time = "2026-10-22T14:59:27.503Z" },
+ { url = "https://files.pythonhosted.org/packages/80/5e/de257fc887b5a4e955173fb28a76a39fdb69f12e0d99987ed723c937c419/grpcio-1.84.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:c8ae49802f09cc272546592b3656207de6f719e50a6a0d1654e613aaba6336ac", size = 7165478, upload-time = "2026-10-22T14:59:30.083Z" },
+ { url = "https://files.pythonhosted.org/packages/75/b0/66840129d46b06a5159277206a1f706ff755d725fdd0652e7acf6961a930/grpcio-1.84.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:1e25f45414b3a53bfc1fe43b980f9d0cae9e194a62541d5f6bef0846758162ac", size = 7707719, upload-time = "2026-10-22T14:59:32.284Z" },
+ { url = "https://files.pythonhosted.org/packages/10/0d/92682af96b05c1d5e5ce1ea82a3969b221c9d58112a097ff637946e395c6/grpcio-1.84.1-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:de3c31abc0d052511744a817506e03534f14e32dadab8ec19f63f2d4facd05f3", size = 8079074, upload-time = "2026-10-22T14:59:34.507Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/69/1e426aa52c23640430eed1728c0f7c1167bb64eb7b0a31bb5ce9a500f73c/grpcio-1.84.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:6ce0835704f30d4954aa0a68352fc9cc30242cc85f39d7b6cd20c0790e05925b", size = 8118408, upload-time = "2026-10-22T14:59:37.082Z" },
+ { url = "https://files.pythonhosted.org/packages/72/66/037c29a3668a32f2dbd287ceac43286797ffe14c95e7234db75c42ea10dc/grpcio-1.84.1-cp315-cp315-win32.whl", hash = "sha256:43d8b84b3e2b7505e4902ab6288ceee699cfec5435ed879bb53f876b2966ee33", size = 4527284, upload-time = "2026-10-22T14:59:39.362Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/87/4dea740311428c40fa0da674e0ecfa28f572809cba4cbc9c568918c82d2e/grpcio-1.84.1-cp315-cp315-win_amd64.whl", hash = "sha256:c8e1c15e81f65efb317906754fdba4a85d2eac9cff250d8a3fb3ccb3a4e52db9", size = 5360072, upload-time = "2026-10-22T14:59:41.328Z" },
]
[[package]]
name = "grpcio-status"
-version = "1.85.1"
+version = "1.84.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "googleapis-common-protos" },
{ name = "grpcio" },
{ name = "protobuf" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/fe/16/0a161d0016f6e318ebf217780a91e007a67d9d244a06f79caeef689f5f02/grpcio_status-1.85.1.tar.gz", hash = "sha256:3f6ba6090d794b7148b23f03355addd56bee2b78174a12784c4d9b4e28c8928e", size = 13982, upload-time = "2026-10-30T16:05:25.631Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/23/18/53459706d79cb7c144808d941cb48f099764776d483a85922129662070fc/grpcio_status-1.84.1.tar.gz", hash = "sha256:690ecf476c82d6d98703a80578cc0ec6d0b5222187f783bd0158abd28396cb42", size = 13993, upload-time = "2026-10-22T15:04:10.36Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/f1/e9/5e34e2543b586ffddf90721ab242e7b662c063484ee3978bf61ca90d7579/grpcio_status-1.85.1-py3-none-any.whl", hash = "sha256:ef84cb262c7a05fd7c27e61bb70c9f63bce4484ce8b3366585a381fed738fcb0", size = 14640, upload-time = "2026-10-30T16:05:11.252Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/e2/b9a35118c6c231a3710a0b4f2174da87f2c09045e743c1edc221b4659900/grpcio_status-1.84.1-py3-none-any.whl", hash = "sha256:5a1455bf5b39fe2a3d09d71661e59c97622c73888ad11f13109ce82220b88218", size = 14639, upload-time = "2026-10-22T15:03:56.16Z" },
]
[[package]]
@@ -2314,7 +2323,7 @@ wheels = [
[[package]]
name = "hatchling"
-version = "1.33.0"
+version = "1.32.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "packaging" },
@@ -2323,9 +2332,9 @@ dependencies = [
{ name = "tomlkit" },
{ name = "trove-classifiers" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/7a/f1/e47b937226ae3b093bbd0132eb0b7ee5bfad12c2b5324afd10ae49946b05/hatchling-1.33.0.tar.gz", hash = "sha256:4c422bd4d554fcff5de5b73e9596f85e41e9f97f7e553d8f7595ecb31a627bef", size = 58079, upload-time = "2026-10-28T02:27:30.694Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/f6/97/b5312f01a8c6daf729a9d272dd442e0c546dbcc630495788786c4b567ed0/hatchling-1.32.4.tar.gz", hash = "sha256:c4468f73144c054d2aab4ef0f0378c43b9878bf07f8ffd6b79690e970d375f07", size = 58065, upload-time = "2026-09-20T22:48:45.398Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/32/64/4afe5dd77b2939a93e8c9513f8082dbd049ecba9c1484a379a0f6ae29d6b/hatchling-1.33.0-py3-none-any.whl", hash = "sha256:415290b84f464d2884303fca8974d38e7b85579069fab02a15db0828fbb1446d", size = 78750, upload-time = "2026-10-28T02:27:29.033Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/80/91f51f439c05d4ec4623c22928ce16a938d6d793bf709477830823497859/hatchling-1.32.4-py3-none-any.whl", hash = "sha256:08ecf7548fb48205e7f213d70c71e67b8271b7242093dc3f1da578b42c734a2c", size = 78748, upload-time = "2026-09-20T22:48:44.19Z" },
]
[[package]]
@@ -3539,7 +3548,7 @@ wheels = [
[[package]]
name = "mypy"
-version = "2.4.1"
+version = "2.4.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "ast-serialize" },
@@ -3548,53 +3557,53 @@ dependencies = [
{ name = "pathspec" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/4a/3c/78ecece71ee4b5445389a4ccd11627cfb9c69e4de554d11060171f376305/mypy-2.4.1.tar.gz", hash = "sha256:63b590965691e540a335fa74dadf31a7069142f6b72e234689bbd244edf55623", size = 4066728, upload-time = "2026-10-28T19:23:13.286Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4b/8b/2bc7f489528db504221ad7eff6b3f83979ee57d066103c72a85d09046a30/mypy-2.4.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:79a1e79199e980fb41f219cb3d4746d25c64b0b623840d3ed57df1cdc13a6502", size = 14321495, upload-time = "2026-10-28T19:21:16.612Z" },
- { url = "https://files.pythonhosted.org/packages/18/9a/b2f037b4982dce33645296e4394b031ade60f697f468761fa3ea8e2f8ece/mypy-2.4.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4e18d0da3898b3bcd16192f15a206e7b8b8dd0f4a0b3e51aa5bd5c2afe749e2c", size = 14467468, upload-time = "2026-10-28T19:22:10.629Z" },
- { url = "https://files.pythonhosted.org/packages/c4/5a/379f9bfa18bf3f8d43668fbd9ae4f2922dd027a559c461ca9d41cde1d32e/mypy-2.4.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b69a15fc2e75858d6b15e91ed3a0dcff1a533265183b64734138791fe0f49aa5", size = 15421496, upload-time = "2026-10-28T19:22:48.005Z" },
- { url = "https://files.pythonhosted.org/packages/83/50/3bde18a2824ec296432cebe01752ccd0ab8a2ad784abe4390040d019b3bf/mypy-2.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:70971cc0bcb1d3f03d2eb68fa430a6481ccc03558dc001d9534d7e5fa274c392", size = 15670425, upload-time = "2026-10-28T19:21:57.026Z" },
- { url = "https://files.pythonhosted.org/packages/6a/36/8b722b88c325d317354de7d10d8fb9d4dc0f958433e873a91fc4ed21a06e/mypy-2.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:6f7225951a277dd0d6add900731d487174b7d24ac133c09d670ed63b4d8cb90d", size = 11300972, upload-time = "2026-10-28T19:21:48.724Z" },
- { url = "https://files.pythonhosted.org/packages/c2/d6/fb2bab1245658d68af8abfe776362a780c621fe0f364f2c7b53039a945ee/mypy-2.4.1-cp311-cp311-win_arm64.whl", hash = "sha256:be4c03f33c55500191df2ed4a9b5e7edbdd1fff067fe361a5a9b1b5d3262da6e", size = 10873349, upload-time = "2026-10-28T19:21:32.104Z" },
- { url = "https://files.pythonhosted.org/packages/c0/1a/0be9dc6af5e0e04128f18a5e36b4ca44bfb0085857d37aa6a7de1ac2b0cb/mypy-2.4.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:d63737426a048a27705a4eedb828669f4bc0873612a1b46b0b9fe892b2ebaaaf", size = 14381321, upload-time = "2026-10-28T19:21:54.223Z" },
- { url = "https://files.pythonhosted.org/packages/0d/a6/7907e36711a8a8c703f961f076fab61793383694140bd1fd87589f3f68d4/mypy-2.4.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7344869cad05f073b8cae2826774fa0161e3eb2f5c685b76813574008d77d750", size = 14466373, upload-time = "2026-10-28T19:22:25.211Z" },
- { url = "https://files.pythonhosted.org/packages/5d/b8/7313212b29de570c25c9859d7c8e4ef2251a6d34338a1e20031d94297721/mypy-2.4.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a20ff6cd2af7a670c64f089bfa5fbcebe8ff3bf896c5e6ac7c9862c7960e4494", size = 15553666, upload-time = "2026-10-28T19:22:36.511Z" },
- { url = "https://files.pythonhosted.org/packages/23/8f/92966b6082ff49da9142e1e527eb9af4359a9a61632a1cfc313da96b37fe/mypy-2.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:9ed1195502cca32be5ef756b1693477f4991db2f2e34eaa90cc4ad6bea4f9531", size = 15802641, upload-time = "2026-10-28T19:21:13.421Z" },
- { url = "https://files.pythonhosted.org/packages/b6/b4/874f6fd54b005020ad63e2cf7a59b178d16a771d478849b29b842cb0cf14/mypy-2.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:4c194c5d878130fca1ae662dfb1feb6274afbab0b40032c80147377e0ea803d5", size = 11383067, upload-time = "2026-10-28T19:22:03.516Z" },
- { url = "https://files.pythonhosted.org/packages/b0/41/5ae8e6528c2faa96e4a921a999d2c7ac7c565bee184b4282bf62ffa76b5d/mypy-2.4.1-cp312-cp312-win_arm64.whl", hash = "sha256:71eda06e5bae51976ce869fda429159c9cec678bd02f25bb197f0265c8f991be", size = 10946882, upload-time = "2026-10-28T19:21:39.51Z" },
- { url = "https://files.pythonhosted.org/packages/6a/65/cd19bc06b67d2df4828220245fd98569ba40fac00c60ba48bef1817d8ac3/mypy-2.4.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:3f1613bfe6726169a5680ec3dad5fe1f6d96285c12aded51ad432eadedd39d45", size = 14214179, upload-time = "2026-10-28T19:22:27.49Z" },
- { url = "https://files.pythonhosted.org/packages/c9/db/4d54ca4f49a0113d0c956980272aefdc6eec5458f7a72b350785b9ef2249/mypy-2.4.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:680ba5d9f0e3fe0c746b3d14df15f2a718e4bc308a1145616a1ea691ce7c354b", size = 14376217, upload-time = "2026-10-28T19:21:08.019Z" },
- { url = "https://files.pythonhosted.org/packages/a2/45/089b4724b85f0f408941be5298f817ee89181c1ce031a73d60ebb150c2c7/mypy-2.4.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fb70621535c22fd4b1a0450d37ed1d6c5eb39656a1cebfb189084c12de54ba7", size = 15467623, upload-time = "2026-10-28T19:21:21.555Z" },
- { url = "https://files.pythonhosted.org/packages/27/4c/15edbc64b0b1412bfe94c74192b3b56554ab642b85a31bb06cef6313101f/mypy-2.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4af5ad558033bf71a44a1be149ed61633522824f84c1b6bc2445d3fe2d2a5296", size = 15729867, upload-time = "2026-10-28T19:21:37.557Z" },
- { url = "https://files.pythonhosted.org/packages/76/5b/7d160fb685994b65b0ca86d352f3afb5424f96a9e6914719f7b861fb6701/mypy-2.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:acdd82cb3a2eb10720e87706a128d75785d005a8c47e36a4973221524f41fc56", size = 11390802, upload-time = "2026-10-28T19:22:18.081Z" },
- { url = "https://files.pythonhosted.org/packages/9a/ea/453d27f8d531f57c5f14aa73094eaa48e3fb77f680bdede2045cb64fa58b/mypy-2.4.1-cp313-cp313-win_arm64.whl", hash = "sha256:133886ef1a7a8cf673df55d865e41580c6c46aa9b8e31e1325dfe315c038f745", size = 10950427, upload-time = "2026-10-28T19:22:59.003Z" },
- { url = "https://files.pythonhosted.org/packages/7d/75/282d43d6fea7be984ac63e75ffd8eee146ba2f3b077bb1ca5bf3f973e0c4/mypy-2.4.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c45485061ba08f700acc12cfc8b177e6997e6bc353605babfc0abb081e079456", size = 14233251, upload-time = "2026-10-28T19:22:44.977Z" },
- { url = "https://files.pythonhosted.org/packages/ac/a8/9021263cc7e761451c7d8131bdfab3c0a5c2d67aa2d694670019e1ab86a7/mypy-2.4.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:727890a32f1858c09549eb55a8b234fe990d460450b28f03e8213ef925f1247c", size = 14394953, upload-time = "2026-10-28T19:21:29.492Z" },
- { url = "https://files.pythonhosted.org/packages/16/10/4192a5cea09ebbc41c534f57bd0d8f0fb42a8e810632cda6e37f072c59fd/mypy-2.4.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:beb254fec368c4e7f7f82a5f310c6470b893ec88a90b66426da971c6826b0453", size = 15442824, upload-time = "2026-10-28T19:22:42.068Z" },
- { url = "https://files.pythonhosted.org/packages/99/e6/d3b0a7474aa4f696594a47a218b6796d9c4346fc81ed288904278cddd9bd/mypy-2.4.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:ec416d5d2ae689e681f5727e328723f18d8b5dc8c903a146b7851dc5727aad8a", size = 15717227, upload-time = "2026-10-28T19:22:54.026Z" },
- { url = "https://files.pythonhosted.org/packages/48/6a/2ec530dacd4f77ca50d0fa05b854098f2d75e5d18c587d2612b44f10d68c/mypy-2.4.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:0e95ad0bec03ced9a73b42514208abd24c789e67f0e1eba476e276dd7a2dea3a", size = 7898637, upload-time = "2026-10-28T19:21:41.709Z" },
- { url = "https://files.pythonhosted.org/packages/cc/56/de9d7bad12e3868d5f1f4279aed20b1b568b2c5e166f2d3a05b211dca0b1/mypy-2.4.1-cp314-cp314-win_amd64.whl", hash = "sha256:51f742d4154824837ca6e92aa81f00eec222700a3c337bd258e5562a3880d75a", size = 11576656, upload-time = "2026-10-28T19:22:13.232Z" },
- { url = "https://files.pythonhosted.org/packages/b8/12/4fa9883b9b59e5c96ff62845ad019dc47786031c45d67eaccf452cffffd8/mypy-2.4.1-cp314-cp314-win_arm64.whl", hash = "sha256:f44dd4c5ab22df21206dd926c98381ebdbf5bfd696df37dd38ded2d23143fcb6", size = 11174403, upload-time = "2026-10-28T19:23:01.346Z" },
- { url = "https://files.pythonhosted.org/packages/3f/f2/827e356fc4f74c60b8da42663336e035b49d948a3c11224b0fc7543086b1/mypy-2.4.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2875280687e64740e6ddcef43bd4c5852ad45ff34549740f6aea3f0373870b93", size = 15713427, upload-time = "2026-10-28T19:21:26.757Z" },
- { url = "https://files.pythonhosted.org/packages/a2/fa/e58a4fcae1d238189eac758036c0921992a86e3e6209190efd96d61fc5d4/mypy-2.4.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ea0714d2bccaf6d010c970e1d3ba2c9c2add79d2030aad2ef03167d788948276", size = 15938481, upload-time = "2026-10-28T19:22:34.371Z" },
- { url = "https://files.pythonhosted.org/packages/a1/54/a20f881801b88900923f2be1541a4b438ad0f0883e03836641fdc3d7adf4/mypy-2.4.1-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63c6ff0e7ea546943001a91639c9f0c64b60d239268fa5d29e4a6c87f82c2373", size = 17243006, upload-time = "2026-10-28T19:22:39.425Z" },
- { url = "https://files.pythonhosted.org/packages/de/1c/ff84a6133a6e89c390bff6b25d9bad6598e7891b205f4b30b496906d636d/mypy-2.4.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a5b5da107dc2c93fb0a22f7a45dbaea5dc131afc7056b5468d78e819f9a138e6", size = 17557728, upload-time = "2026-10-28T19:21:43.607Z" },
- { url = "https://files.pythonhosted.org/packages/15/4a/effa47d39b8fadf679c08e7c78559e523f9769a42a75662d2d15b2b9cdd6/mypy-2.4.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c3a4d0c47053cc90035dd3a2c321112b593ad42935d0cbfd747288e1564fa8ca", size = 12370826, upload-time = "2026-10-28T19:22:29.779Z" },
- { url = "https://files.pythonhosted.org/packages/46/9f/b214ececb1a97fbb61ab3dfd48ae3cffd92ae2c148af4c9dc94c6c56772b/mypy-2.4.1-cp314-cp314t-win_arm64.whl", hash = "sha256:0c5bf267645351887b72903fd2f5fbddcf85234e0e6c4ad94e2fcfb4f0c3f971", size = 11851083, upload-time = "2026-10-28T19:23:11.288Z" },
- { url = "https://files.pythonhosted.org/packages/57/5b/e44a2edb0b25a9f1429de770e1d0238af23302671dd40c77ac5efeb0c559/mypy-2.4.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:db599d03e7d60d15f2e0a11665c18bc52a88699fcab990fdf8cecd5be8ac801c", size = 14243144, upload-time = "2026-10-28T19:21:10.975Z" },
- { url = "https://files.pythonhosted.org/packages/59/d4/5c99dc3a28e11d2f0fcbb02df63d2d3e574dedc0f9f69d50901df305a7a6/mypy-2.4.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:c6c7210e40dd423af7d65cbe428b5906d11dc60659337bdae4e5fdf02ce32ed7", size = 14409316, upload-time = "2026-10-28T19:22:15.515Z" },
- { url = "https://files.pythonhosted.org/packages/c3/c9/3108b38f4c30eabd80194f5d9771a87e27196c5e0ddb5bf4b99c6ed1e97e/mypy-2.4.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a2d4a51db92059eecc2c33b2a5194b28585ebfd917292c015fe87a77dbff6cd2", size = 15445573, upload-time = "2026-10-28T19:22:05.635Z" },
- { url = "https://files.pythonhosted.org/packages/dd/12/84a53eceaf11c46dfd7931fb6b8902d01fb33787d3a991e81c461458150b/mypy-2.4.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:01b7718552d017f88d1151f3cc9d6556d778a90bd58060a2c3d87d070f9099fb", size = 15732989, upload-time = "2026-10-28T19:23:06.512Z" },
- { url = "https://files.pythonhosted.org/packages/65/ba/bf017dc2abdf83b2f74e29c43834b7af67dc1f1b20202f02dae60b4781a0/mypy-2.4.1-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:3467b1d13000812ab85dc2a3834b9379ec77798ca1bcabe434e23fb21fead692", size = 7898932, upload-time = "2026-10-28T19:21:59.366Z" },
- { url = "https://files.pythonhosted.org/packages/76/05/0c22b1f0390120a8592d31209cb6b9f932b6a0b426fe643e550de15139e3/mypy-2.4.1-cp315-cp315-win_amd64.whl", hash = "sha256:080cf64a60b90a73435542feecf4171904cc5710658165b40c6869115d4aa100", size = 11577577, upload-time = "2026-10-28T19:22:01.302Z" },
- { url = "https://files.pythonhosted.org/packages/73/fb/ac01e173629697ddac2f11e51efd10347d673f9a7311b03f4aba710ebccd/mypy-2.4.1-cp315-cp315-win_arm64.whl", hash = "sha256:f9118a3f50de264b614861bb11814833a742138ac3c8e2e49a860b6977fb2704", size = 11173540, upload-time = "2026-10-28T19:21:46.444Z" },
- { url = "https://files.pythonhosted.org/packages/5b/e4/b9b9c8eb09e70927ee65b1456cfed35710b6e4ff0b07f364ceaf2202307c/mypy-2.4.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:9d7dafd56179d296651df9e1844b91c9517c379398f017a2a435d077a28943c1", size = 15706623, upload-time = "2026-10-28T19:22:20.528Z" },
- { url = "https://files.pythonhosted.org/packages/26/74/1ceb1ca884add0565e618d013dd24333039100dc0fcc42661561f2e1cae5/mypy-2.4.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d923cec43316f0e6736b8298fd00abeaaf348a091c20e7296b3aebceecb63eca", size = 15944095, upload-time = "2026-10-28T19:21:04.779Z" },
- { url = "https://files.pythonhosted.org/packages/2b/68/27bcf9b4efa22733b6050485ae37845b94c9b8851abe4fc60632d905c32d/mypy-2.4.1-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d6e081b466e88d4343adffe11aff0f02df34b5b9070b4b700053a0b7294c0be1", size = 17223065, upload-time = "2026-10-28T19:23:03.724Z" },
- { url = "https://files.pythonhosted.org/packages/1a/a3/ed5724b317bf585c29f3c8be2792e97d45925d65a9c8af40ceeb267072e6/mypy-2.4.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:70196470ac300f444a302a738541101893c2c57804078b33690286a7899149e9", size = 17560589, upload-time = "2026-10-28T19:21:34.539Z" },
- { url = "https://files.pythonhosted.org/packages/42/2f/005185e6ab4752fd6cc2d1a6d1421fe21979770afe2490c00c555f34a913/mypy-2.4.1-cp315-cp315t-win_amd64.whl", hash = "sha256:de95648e0a6d46fa9929b956ed2c87c5dc9933d4d59945e6390e9d753c74e7c1", size = 12353324, upload-time = "2026-10-28T19:22:51.481Z" },
- { url = "https://files.pythonhosted.org/packages/a1/d6/20420cc9c780087139662534d128c2f3274361f8bb7e9cf0d5495a846ed5/mypy-2.4.1-cp315-cp315t-win_arm64.whl", hash = "sha256:ef8d5be90032261197ac388d945d8ff4f476c95880798a1911760af64ddfe71f", size = 11845599, upload-time = "2026-10-28T19:22:23.136Z" },
- { url = "https://files.pythonhosted.org/packages/21/5c/6e8010f8f2c4ad817364592d4b3883633417269b913fc9d5174fdeb7e565/mypy-2.4.1-py3-none-any.whl", hash = "sha256:1784648d6ec8c2b5465e3f26241b845a0b3cb4e56c993c64abe16989e7698999", size = 2801286, upload-time = "2026-10-28T19:21:19.516Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/34/4e/64300736cf0a0373a27b94a91b664ee7382e36f77b0621bae6381da3e180/mypy-2.4.0.tar.gz", hash = "sha256:77bdaebd452f43fcfc4cc3ba94352a3ea537cd01e3f2d0879f48673d2ec00d6e", size = 4064425, upload-time = "2026-10-01T20:40:39.229Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/26/67/e53965e67d23d48e78dd603aa7525a8d77e7818c23954cc217ccde5cd5c0/mypy-2.4.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:5d20e6c7c35fcbf2a0ebdd0eaeacfbc243009dfd33ab7822d54e213912e6dbbd", size = 14315745, upload-time = "2026-10-01T20:39:25.03Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/5d/a588ccd57625822d8484d7aff4e9c70f88d042a91c2ec84324a2bc14b3f3/mypy-2.4.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3011537be6cf1de4511c0255a324362a812b58184bbe61e15f59c8b31033bd74", size = 14465652, upload-time = "2026-10-01T20:38:57.19Z" },
+ { url = "https://files.pythonhosted.org/packages/05/af/b4978a5566cb829ca563dc08e3ad8d944f116078786ad54ba3dff2bcb64d/mypy-2.4.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a96b07a49b7b1d025ce59c1b3acbcf24bead9a83da4523c4a6bde1bb94e7a0e1", size = 15419180, upload-time = "2026-10-01T20:40:27.808Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/8c/52c71f31c54d0a749d832cc73b933c6e38c52d9b270d5503e031c73723dc/mypy-2.4.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7c4f8f8d1d1c0e2832d8ee7113dd08f6df6c7aad9e863fcbed9f25832be0b8c4", size = 15669212, upload-time = "2026-10-01T20:39:20.443Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/55/ba63121494404fd7d3787ec176ccd02820d6ec125ac4e7c4c0b79f2cc6c7/mypy-2.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:ba05652540bf12828e52abae807b024b09ca144ff4f75e2450a81d69c376425b", size = 11299572, upload-time = "2026-10-01T20:40:03.627Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/9c/385c81b5c0d3a2917f077c4da660d0493f9521ccfd4b058e33b860f72d02/mypy-2.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:6306086b87cf7f8a29aa618d9fd9bffb56c59247166b9660fdb54d86d7714ecd", size = 10869751, upload-time = "2026-10-01T20:40:25.483Z" },
+ { url = "https://files.pythonhosted.org/packages/68/ed/e5d7cf4017e74a1c1e1c4058ce8f614fc1e3e7606564f47166e22bfc9f95/mypy-2.4.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e05ff2925d8b37ad26c80c1b9dc43ae5d455da2df1e23c24c095a6425917c57e", size = 14378593, upload-time = "2026-10-01T20:38:48.222Z" },
+ { url = "https://files.pythonhosted.org/packages/30/7d/12d994886a922f0f1997becc9c6625198d61eeb5962558a886af7dd38d54/mypy-2.4.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:29243242cf72582b65f9582ad9e56e8cb281566ed3519f4cd70bb8b9f2977e90", size = 14464697, upload-time = "2026-10-01T20:39:11.059Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/9e/bcc9af755425ad17790bf11d73c2ea7592914cf309ee1340997670f9d57a/mypy-2.4.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:29eb0b9427a6b11b992e452f6cceb8af724f4dceb47e779d0b35e405e996ea5e", size = 15552257, upload-time = "2026-10-01T20:39:56.269Z" },
+ { url = "https://files.pythonhosted.org/packages/af/0c/3343fc4525d6f00d75ad17a93a9f052d5163641cb8911840d4a12cb59ff2/mypy-2.4.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e3ebe2f72a2a1156065a9851570ffbf50c0a93cdccadef9c6e05c508a4fd10b1", size = 15800443, upload-time = "2026-10-01T20:38:54.815Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/0e/69aac6b8159da7c53e6115a2be1bf48e85503402cadbf506dd708e8b2ad7/mypy-2.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:236e0d68f6941992b0811128e652590f590db444ab29ad8f1324765b9298b946", size = 11381574, upload-time = "2026-10-01T20:39:49.406Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/d3/d32ce4feb5993eec09d2024bef16cedc9b93701b1446b86092f08b24491b/mypy-2.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:82d0f94c8587ccb472622ee7795280aaa38a06640d5f45b3f16909d6dd86a989", size = 10944729, upload-time = "2026-10-01T20:40:15.203Z" },
+ { url = "https://files.pythonhosted.org/packages/44/f2/eb15183c97c69d7cbfac990a6efd33a19ecfd97dab9e714c742fa78a784f/mypy-2.4.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7da85fbcff6dac1abcc636707bed38b45598131fb7a605d9719c70b5cc733af8", size = 14213395, upload-time = "2026-10-01T20:40:21.178Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/b5/ba91b6ff65e4d6b6ff53b2b3b3ac5f1babf0c7c27d0b43a0196b1c967926/mypy-2.4.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:4209da39d85cf240f762af622d8180fcdfcb4727d021f44ade62d613a1a43324", size = 14373009, upload-time = "2026-10-01T20:39:58.86Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/c4/484275efc935c0003e55e4e8a33e4b8e99528ee956c12256ece4708f903f/mypy-2.4.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6be721bd4bd57576193653b75b4af3461c9d0bf7dd8b528f782e9be210dc75bb", size = 15465557, upload-time = "2026-10-01T20:39:01.794Z" },
+ { url = "https://files.pythonhosted.org/packages/50/30/66eb6fdd0875e3c9025a02f0bb0ea2e524b74274b658c37fde0068c4939d/mypy-2.4.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e1fde197ae65be856a034a91b70ed747a16562ca69577785f06c661548424bf1", size = 15728673, upload-time = "2026-10-01T20:38:43.724Z" },
+ { url = "https://files.pythonhosted.org/packages/58/bc/2aa98fd7f49c42dba8e9c065886fadffdd00f3c654cff3f2a7103a797ac8/mypy-2.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:295ecf2e57542cd836ca537486951289678c8c7d1ee6ad74ebe29b2168a003cf", size = 11389803, upload-time = "2026-10-01T20:39:37.547Z" },
+ { url = "https://files.pythonhosted.org/packages/49/41/17b60df2d946792ef6af43b89351f5c9ddabc69053206b2304945572a744/mypy-2.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:bc378bdad4e9f12b5bd96466083d1e71acf00594ec9c7b2bdb5e02816f77f303", size = 10951060, upload-time = "2026-10-01T20:39:04.015Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/66/924be0b653372ed31ad5c48e26044cc00840e591943a56e61621cf05b60e/mypy-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:058165f564ccf559c68c70fec2091fca5891110480210c22594635e3f6683437", size = 14234230, upload-time = "2026-10-01T20:39:44.535Z" },
+ { url = "https://files.pythonhosted.org/packages/56/39/c4f176880a4177123576de6cec6309feea8f42fca2bf2f6584e88054f656/mypy-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9fa247e02b505a45a2775f69df38d360d197e3790bc60f717595db9eda358b6e", size = 14392944, upload-time = "2026-10-01T20:40:05.78Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/1c/26e16977e25ef2494a74f8ffc872a76ec2c3aa43c3156057cbdf88f352c5/mypy-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:20e9a5cd875837520c43db98dea0b6d0c2197833d95c30127d8f570fb9b1f00b", size = 15441463, upload-time = "2026-10-01T20:40:32.412Z" },
+ { url = "https://files.pythonhosted.org/packages/31/9c/9e4b049f0ecefbfd6817ca2a16eea55dd74a07950268edc6cffd29ccfe98/mypy-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:9f03a7828cca2b0adcd6662aee8f2711ff8830e1027641fdea3ab0b787483566", size = 15705406, upload-time = "2026-10-01T20:40:34.844Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/5e/e861b5f6c5ef9ee6cd24683aed1edecf82a26dbd536049f9b7850b586267/mypy-2.4.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:9279488933040b638c0ab739084c0ca100efeea6db581bf5d7628d8e89de53fe", size = 7897523, upload-time = "2026-10-01T20:39:39.861Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/87/61ffee58b25a956532a6006fae84918a2de849ed25f397492b2e815fe7b3/mypy-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:2106b55105ba5ea9be4f53a24517fc5fa927ff1585edc9bc1a975abb72caef89", size = 11576345, upload-time = "2026-10-01T20:39:41.553Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/88/a331c20698971c2ce8d1c30f317fd61b5be13a85b1f053e12dfa22ac6568/mypy-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:528c8744b8b5e3ecb8774f86af38d2376216816e9908317ad055f3c9c2d74799", size = 11172062, upload-time = "2026-10-01T20:38:59.534Z" },
+ { url = "https://files.pythonhosted.org/packages/47/c0/4f7daa73270dced8e86c6f4a911c68082d03a84e6c1ec9bd916028d66131/mypy-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:0bb95cf34899e4619c61ab0a8667804e139e580b30d5df12af2102dfe44d0c97", size = 15711208, upload-time = "2026-10-01T20:39:30.597Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/05/f1afa303c678be24cf7a266d38fb24b3de4599a024c2f9ba0d5905a3efa3/mypy-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:86d616fe84c6eab8026f8c50ab5bcb90db780d2ccd233d971e34e92bede9b359", size = 15935442, upload-time = "2026-10-01T20:39:22.781Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/d6/6a1a45459b63716e0d035f4892a926d3054f0a8cbfa02551d228a9946083/mypy-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a3f86fd1313dd69d013e265f1fdcd12ea7a9d606f9875b2a3db946cd334555f3", size = 17240044, upload-time = "2026-10-01T20:39:32.95Z" },
+ { url = "https://files.pythonhosted.org/packages/87/85/ae33bee66c13f98d421964d87bf0888be941063bc75c1204a4cf142cf1cc/mypy-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:720434d48542ecfe84d32d287b727569d3fc8f5769acd39051130e490a5c295c", size = 17545856, upload-time = "2026-10-01T20:39:08.827Z" },
+ { url = "https://files.pythonhosted.org/packages/df/de/eeff209b65c3e267818d79333bb9f058da6e4e73761461bd94748a2eb628/mypy-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:a6e851b82c0661f69f1630fc16172c68787a6a9cf0991e7c6437d60976cdcd76", size = 12369342, upload-time = "2026-10-01T20:40:10.516Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/43/e62d8d5c1dd737aa248968302ff7d6eabf997c3301773ed8bcb64932ca77/mypy-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:3bd0e340f0ebe65c548210f53be3fd8192e83964760caf0c28bef368e68b0d37", size = 11849099, upload-time = "2026-10-01T20:39:51.834Z" },
+ { url = "https://files.pythonhosted.org/packages/2e/5f/335b8980055118dc131355155883fb676bb2161a0d97e08658e479c776fb/mypy-2.4.0-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:afa89837d9be67e0cadfa33bca3bb7efdda98c3b07e74dc3b635ebfb1c8a926a", size = 14241302, upload-time = "2026-10-01T20:39:13.227Z" },
+ { url = "https://files.pythonhosted.org/packages/18/37/1482fdc49332b145828912b15f16eee0c4ca70a8bce0f6514ece79b14680/mypy-2.4.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb443e81057896132d3642d6be219e6efd158691ac7883e3ba8fcb469865f05d", size = 14408200, upload-time = "2026-10-01T20:38:52.576Z" },
+ { url = "https://files.pythonhosted.org/packages/04/09/dce2e8f6c1b31053c430ef6963f6f7a38ccb49b90c5e01e37ed0129b7d5a/mypy-2.4.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:7f38f57d344f8b6accb40e01c3d83cfc590498231724d16c07ffb7940f157818", size = 15444409, upload-time = "2026-10-01T20:39:18.1Z" },
+ { url = "https://files.pythonhosted.org/packages/43/c5/91b68306da4cd280cb15be35dbb5ffc343cabd67c4b121b6625bf2d13177/mypy-2.4.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:e76172710bd4e5eeae061abfd68347e5264632e02778be61784671ae3a2132f5", size = 15731186, upload-time = "2026-10-01T20:40:23.426Z" },
+ { url = "https://files.pythonhosted.org/packages/64/71/2d0340182a8f27352fb1e25531355951108b506097c433937e9eec452ffd/mypy-2.4.0-cp315-cp315-pyemscripten_2026_5_wasm32.whl", hash = "sha256:f83353e47ab520bf6fd4df8f5897d9fe081211f2fbc4b7d37736a3e3c166cbcf", size = 7897838, upload-time = "2026-10-01T20:38:50.779Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/08/32703c117e134c02efa2a82705bb40cee91eaedea10b6530e20eba651b1f/mypy-2.4.0-cp315-cp315-win_amd64.whl", hash = "sha256:970b221ed5842213d98e3c480c08f795ace4b1f81fb21e1b126bd0476bce1c34", size = 11575700, upload-time = "2026-10-01T20:39:53.982Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/08/08bb269feafdaad031046ee2d771528a64467e6a180f962afc28c4a3ccd8/mypy-2.4.0-cp315-cp315-win_arm64.whl", hash = "sha256:502b94b0b331f7dafe32fd6b151797ddbb4f32385b362e722c783a025e5954a3", size = 11172908, upload-time = "2026-10-01T20:40:12.91Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/3f/c5c92626006ca92c7686adfbece47fc6a0daa0e54753952a9ad13ce0561c/mypy-2.4.0-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:c9de622fd397495695d0598ddc789222bfcfec9d7c9ec3a1e385c855e3bc5e01", size = 15705970, upload-time = "2026-10-01T20:40:17.79Z" },
+ { url = "https://files.pythonhosted.org/packages/10/f3/863365f7997a76a5a1dd42d7902ab05afa4124e8419089cbca1ce2554db1/mypy-2.4.0-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9f459f0b4f0596d9d51fe7716b404b35287b99e77da98a7af90a65dd5fd61141", size = 15942234, upload-time = "2026-10-01T20:40:37.243Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/30/2f45b1f425a2c95dbe1a3f4d076bfd42b76e9615dba5906230703b14e4eb/mypy-2.4.0-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f9b028548b3af480e2b1ed8df14ccaac86f99c9f600d1580770ab7ba3dcd40f0", size = 17218364, upload-time = "2026-10-01T20:39:06.497Z" },
+ { url = "https://files.pythonhosted.org/packages/43/8b/5b2bbfc69e84800b78fa2dba16d995b003f93b573558e412c5490d6e6c37/mypy-2.4.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:cb734b2668c1f40d07ce093bbeb4407e9527c67901627b0e1679825be3f09975", size = 17557934, upload-time = "2026-10-01T20:38:46.077Z" },
+ { url = "https://files.pythonhosted.org/packages/df/c0/1a5dc601c22041a7bbfe1ff71cf097fbbd4fb49930867c6789baf5102106/mypy-2.4.0-cp315-cp315t-win_amd64.whl", hash = "sha256:172e30b8fea631fe310f0c665477f52d9ea40bb4e99e0c81dc30118563b13710", size = 12350955, upload-time = "2026-10-01T20:40:01.52Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/bc/697e9e26fc2a86c094ad67ee1e419971b7f01ded66ee66231b09e9f3e12e/mypy-2.4.0-cp315-cp315t-win_arm64.whl", hash = "sha256:5786ef987b3767e51aaa53f20aec104c0252b42ecda7aef8e8b4cbae279b05c5", size = 11842738, upload-time = "2026-10-01T20:40:30.026Z" },
+ { url = "https://files.pythonhosted.org/packages/81/12/46ae8670c98a3cd0286ca5645c2f918f8f6be65edfed81b916010619f668/mypy-2.4.0-py3-none-any.whl", hash = "sha256:d01c5d26a352acc6d5cf3128225477e1e8465e8d3029d4c345807fbf7f3cf093", size = 2800734, upload-time = "2026-10-01T20:39:26.837Z" },
]
[[package]]
@@ -3637,11 +3646,11 @@ wheels = [
[[package]]
name = "nodeenv"
-version = "1.11.1"
+version = "1.11.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/ab/27/79acfd3d6f526a4a16f3d209dedcf7b5bc1ea15c5bf2bbb82f2c2285057e/nodeenv-1.11.1.tar.gz", hash = "sha256:d7fa027c1df437bdd974b0502a8dc76fa1b3183e369ac9539602bc6bab9100ba", size = 99853, upload-time = "2026-10-29T20:54:04.261Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/105de02c1322cfada6d9710d9146ef8026419d433c9d08359a2d35805811/nodeenv-1.11.0.tar.gz", hash = "sha256:3ce8fe5b71d16e8af7039ca65257354100bc772965d6bc549070649e53b1b146", size = 99301, upload-time = "2026-09-26T11:29:21.367Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/40/35/a09e0b1dac4027e35b872e4f123a9f9e7bbc39e6424bdc9776920d3a6491/nodeenv-1.11.1-py2.py3-none-any.whl", hash = "sha256:bc0fb085d8ed0d67a6471892535f3d9b795eb911b7cd129d3a24967aab7a538f", size = 34473, upload-time = "2026-10-29T20:54:02.752Z" },
+ { url = "https://files.pythonhosted.org/packages/54/c8/12811c9b48fde162bb72b6f2e78fada9a247a09d7bb5be2050a5d099c77b/nodeenv-1.11.0-py2.py3-none-any.whl", hash = "sha256:edaa16e6c14d7cf395d75d4bbd5a26390f4dc06501a33b4e76282b02cc688a25", size = 34277, upload-time = "2026-09-26T11:29:19.933Z" },
]
[[package]]
@@ -3826,63 +3835,63 @@ wheels = [
[[package]]
name = "obstore"
-version = "0.12.1"
+version = "0.12.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/76/c5/9fbb70a0914f4cd5ee7f0fd3e39e0812c29cdb4b4a8120ac7870f379e8a0/obstore-0.12.1.tar.gz", hash = "sha256:80549c9ea5478fd5d5bd75a3c9178841c7108a76d8139db05407a25a55a6a476", size = 138207, upload-time = "2026-10-27T22:51:02.613Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/f7/ed/9667a1b1c6022b212460210929cc2b89bd72883c1182c9ea513a228158ad/obstore-0.12.1-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:8c97afecce59224b82fcb4588a3d6fbef479bd6cc2616b8f4d3d3ddba43fa6b7", size = 5431853, upload-time = "2026-10-27T22:49:38.938Z" },
- { url = "https://files.pythonhosted.org/packages/c9/bf/78437ef20196862b6f77f112184ca6c14783ddb98eb601fea88ee07c7acb/obstore-0.12.1-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:60af5d92da9332014bba8d34f985b9dc692e73d1141d1775dd3a328db476f219", size = 4592428, upload-time = "2026-10-27T22:49:40.799Z" },
- { url = "https://files.pythonhosted.org/packages/f5/30/80f63a19ace4d55ee94b7572dacf5435aeaa835e32550277c9b630fa6974/obstore-0.12.1-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cf96279d243efd65d005c992e20476b3f343a54f71416d04af5d29d9998e739b", size = 5029909, upload-time = "2026-10-27T22:49:42.497Z" },
- { url = "https://files.pythonhosted.org/packages/07/43/4943fd0e5f89db060511a7e96ac887502f8d682b044e70b8e96f7f5bbf41/obstore-0.12.1-cp311-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5b22ca0d66af4c3dd130ed492f966eb7d775529ea129bf379d002dd2c4093971", size = 5276990, upload-time = "2026-10-27T22:49:44.028Z" },
- { url = "https://files.pythonhosted.org/packages/39/85/374b2f8356f052c058be845dbd4e5930d952636bb54dde67e3219ad52689/obstore-0.12.1-cp311-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b9ad04de828d3726da251b95ac7cf5bf4397c026243bb88b2d087e4ca1d7506c", size = 5445620, upload-time = "2026-10-27T22:49:45.634Z" },
- { url = "https://files.pythonhosted.org/packages/df/b7/1b8c950cc7cdadb65eace8d8c7a3e53b45341b726a28a3f93d2e2db98e75/obstore-0.12.1-cp311-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:437831889a617deb632b54be8e978b59db5c34e57f7329dd5ced1f2ee2895473", size = 5355053, upload-time = "2026-10-27T22:49:47.206Z" },
- { url = "https://files.pythonhosted.org/packages/9f/de/dc6eb84ef8d1f96af626c83b3da436b1a4d6b89aa52b560d4451f9ca8ef8/obstore-0.12.1-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d31b255aec88d37636d374a651f5efc684c89716ab64dea6ac89d75801933af2", size = 5591012, upload-time = "2026-10-27T22:49:48.912Z" },
- { url = "https://files.pythonhosted.org/packages/81/b5/f327b606c3aa1c7cc78f7595da7df96d8751e69912afa6331b48234098b6/obstore-0.12.1-cp311-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:d7ee9cde74dbe11a4630c90d7245543993f3cebc38a70aceaedcd3b3d6192807", size = 5376219, upload-time = "2026-10-27T22:49:50.393Z" },
- { url = "https://files.pythonhosted.org/packages/22/f2/54890109d9c1c8ed16d3ad8b17f53953e04006d6b1bf013e13c56c7ef979/obstore-0.12.1-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4240e5cb60d091e4a612499630eebede5164c821196322bc8a5ecb4861dc7b1f", size = 5570174, upload-time = "2026-10-27T22:49:51.889Z" },
- { url = "https://files.pythonhosted.org/packages/1a/e4/8573194930c7a8cf4354b894dfd3a40ad94e9c33b5e6c88213da7038a630/obstore-0.12.1-cp311-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:7b8e376fb425cd3963bbc2d51c0e0f19bebdaf3f824ffdac9bfc8dc13a782b5b", size = 5257775, upload-time = "2026-10-27T22:49:53.573Z" },
- { url = "https://files.pythonhosted.org/packages/6d/0d/9b3ec1f0c054b104ebf00333dc8f69f56c023c4f466fb09c52ccd25501a7/obstore-0.12.1-cp311-abi3-musllinux_1_2_i686.whl", hash = "sha256:2fcec4ccfeac0c5ad43c184692f60d15b79e8db594b075c45a40c5f7eeab9276", size = 5403147, upload-time = "2026-10-27T22:49:55.245Z" },
- { url = "https://files.pythonhosted.org/packages/b9/57/834fe762603c42d70957699d5fa84ccb51c812abc60f22a70cd56ac1a0fb/obstore-0.12.1-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:656547972c57aee8054fedd2585f4280d50bc8fee192269367547c4a01759d2f", size = 5821292, upload-time = "2026-10-27T22:49:56.895Z" },
- { url = "https://files.pythonhosted.org/packages/6d/a8/1e71da6cc12d4df8da701defd2ee78edfa5298f8eddfceb6b491a9c7b4aa/obstore-0.12.1-cp311-abi3-win_amd64.whl", hash = "sha256:d84cdaeeb5f45bfa685fef3ec87265b48af13f8d006ca7c5ed1424c91e448dd5", size = 5343080, upload-time = "2026-10-27T22:49:58.631Z" },
- { url = "https://files.pythonhosted.org/packages/3e/39/6bb0099a1002498743a9aff82b6e5abbf07d0ac8ed672080374a3528b16a/obstore-0.12.1-cp311-abi3-win_arm64.whl", hash = "sha256:6eaf07332b1121dcb3b401da9220b22f94d8e3823f2cdc69e4d8b13c4adba0a6", size = 5155426, upload-time = "2026-10-27T22:50:00.462Z" },
- { url = "https://files.pythonhosted.org/packages/b7/cd/ca597f403114ca8cf0f46fb15db1db68b2c04bd14203a21d8eb5491688d5/obstore-0.12.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:5a8dec8c89d710ba0debc7a9e7a3d56bb207f32d6acb0dc95f786c19aa4a811f", size = 5443755, upload-time = "2026-10-27T22:50:02.018Z" },
- { url = "https://files.pythonhosted.org/packages/d4/36/42c26efb568b1a386f279dcb26d05df04243dacc5f4123108790390a3c00/obstore-0.12.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:850062a27e9162c6bb9c4e18f539a693531df741b6c5fd5d8b2a17a8c4681dda", size = 4562659, upload-time = "2026-10-27T22:50:04.262Z" },
- { url = "https://files.pythonhosted.org/packages/d2/29/341473d040251cd4918204315cbc00b0bdc9ed94e89eb3ceacd4b4b75ad9/obstore-0.12.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:de9eb44136babc4eccdb96e5d04f195612b976f6e708573a127b2ac46eeb0b49", size = 5423989, upload-time = "2026-10-27T22:50:05.848Z" },
- { url = "https://files.pythonhosted.org/packages/13/5e/febe7b32fc416128d61e18c86fd5da7adf8be22b1bf0be1d94e8622a197e/obstore-0.12.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ee8a1d6619ad9db9a84b1e7b769f639d16f7122255e48fec475d93f00d1206f3", size = 5353082, upload-time = "2026-10-27T22:50:07.416Z" },
- { url = "https://files.pythonhosted.org/packages/47/55/926becb2372bc22af0aa0c9aad61dcdd9af29bae942a36951c622f167c63/obstore-0.12.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8806b2e4a46a8ae69b1af7187d5bb9a0f1c717583c5525e7eccdf9b7e76da475", size = 5575137, upload-time = "2026-10-27T22:50:09.169Z" },
- { url = "https://files.pythonhosted.org/packages/f6/94/c7050bf30060fe8ea9523dc55a2d57b73dd5567abe62becaac7deba6b102/obstore-0.12.1-cp314-cp314t-manylinux_2_24_aarch64.whl", hash = "sha256:7880d391c3edf7abd6b1cb4c9071acd2c4ea108e4af83f57df4f351f4a5816bc", size = 5363584, upload-time = "2026-10-27T22:50:10.833Z" },
- { url = "https://files.pythonhosted.org/packages/e0/85/868d3589d761d5250724566f0dd942ac4b341279821cb8bddd62fe87416f/obstore-0.12.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:bf0131b338f5e23224d9009dd6f67b6088aeeeac80259ae32543f5653bce9ab1", size = 5561341, upload-time = "2026-10-27T22:50:12.609Z" },
- { url = "https://files.pythonhosted.org/packages/cb/78/ee95ddd84ecd492f132bd657f6d23761373eef496247fc1806bebfa02c3f/obstore-0.12.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:8378a242379dc8f3955bf17847f22987348f2d978d0e5a43c15fff97a3c60989", size = 5804292, upload-time = "2026-10-27T22:50:14.14Z" },
- { url = "https://files.pythonhosted.org/packages/35/b1/1197446a6376c4297e162c6242dd01de7ea3419fa627cce6239cb01a8f6b/obstore-0.12.1-cp314-cp314t-win_amd64.whl", hash = "sha256:13f5a95b9722e4d489b9ac0a9d75d3e9d097ccde241cdfd6ca0150a3dd742619", size = 5320383, upload-time = "2026-10-27T22:50:16.004Z" },
- { url = "https://files.pythonhosted.org/packages/0e/da/34f86122a19ae305e58d2913b0eee7e777cc69283d5bc068590b6a8f68c6/obstore-0.12.1-cp314-cp314t-win_arm64.whl", hash = "sha256:31b5d33132e28f0933c3d35e9a1436a3b32ed68fd8bfa6e1d1b8ad69f7aa8d82", size = 5136428, upload-time = "2026-10-27T22:50:17.72Z" },
- { url = "https://files.pythonhosted.org/packages/00/2d/8c472ebe021c474c55ad79a8dfe3719281d1ac0592870d7fca8d92361461/obstore-0.12.1-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:2da906fb0b5c28f6389a9561b96a5cc4da92f5eb9c2aeb3e2bd3768ab87e3717", size = 5491955, upload-time = "2026-10-27T22:50:19.814Z" },
- { url = "https://files.pythonhosted.org/packages/ce/c5/7a6cce169714f2a4c4aa90831f2c4015b07727eb8e17d858820ba70e4a81/obstore-0.12.1-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:4095f4e70ca24e8e7b1efeb87912cfc5142e2ec82888b1a63c0caa94fe875d22", size = 4608411, upload-time = "2026-10-27T22:50:21.49Z" },
- { url = "https://files.pythonhosted.org/packages/ab/9e/c0a2ab434103f89cfb0dcb3218e489273295936fdaa0b58fdef0d932aa77/obstore-0.12.1-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ab20d8a422a11b4236d4041861f52fa2e7e5b53fc80f2629c21fa3dd1e7fd8a1", size = 5059569, upload-time = "2026-10-27T22:50:23.035Z" },
- { url = "https://files.pythonhosted.org/packages/58/e9/d0c6db93119f0b18730d11f43ee46730c1c1031f35a742d99955fe8114c5/obstore-0.12.1-cp315-abi3.abi3t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf2c08fca6766f70a4d693e752fc279b57481e0a9fbb6d86770f3eeea67ee7e8", size = 5289087, upload-time = "2026-10-27T22:50:24.627Z" },
- { url = "https://files.pythonhosted.org/packages/8a/13/f8ecff794eb93e13a749c4738cc39ffde2a227780676fc6b3e3e1874b953/obstore-0.12.1-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8a357ef92897cec37c6c4f630c71a8b5c3b602439bfeb7201c00682527526434", size = 5469409, upload-time = "2026-10-27T22:50:26.095Z" },
- { url = "https://files.pythonhosted.org/packages/1e/26/ad477c01bb505ba6ad648286e8ec89aab7290a089fc761d600c65609de33/obstore-0.12.1-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e422bf5821e5ceb8577154b62389b5b44136718f345db663bdd592d301aab1c0", size = 5390957, upload-time = "2026-10-27T22:50:28.009Z" },
- { url = "https://files.pythonhosted.org/packages/ab/cc/54242ed5d9f0b94631f686118292c639ba5824c7eb6fed7321fb52267523/obstore-0.12.1-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd80719105c062b941b172ffe758b8cebee7ab0526562793fb2e9d57961f8072", size = 5625205, upload-time = "2026-10-27T22:50:29.604Z" },
- { url = "https://files.pythonhosted.org/packages/a5/18/94263560896c75216c24295d1f6292180d9a006b94df9cfd45640af9fab5/obstore-0.12.1-cp315-abi3.abi3t-manylinux_2_24_aarch64.whl", hash = "sha256:fc673c165955d3b91fac276c8720daa10276f31d685f1e12fa16e4bb0144e82f", size = 5407054, upload-time = "2026-10-27T22:50:31.226Z" },
- { url = "https://files.pythonhosted.org/packages/fc/45/497e3a6de4cf78148fcffbd3bd3bc1272d9229feeece22e46db7927debfe/obstore-0.12.1-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:6a019c276c606d930f5a7c538865186f9baa102019be0e24bfc94a8289a571eb", size = 5601269, upload-time = "2026-10-27T22:50:32.738Z" },
- { url = "https://files.pythonhosted.org/packages/4e/4f/6c0bc2a08bf6615417ad03cc3bf6a36da2e4e456ec7a0577803a8e934189/obstore-0.12.1-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:a83b1ee193c32f4da0473606ef32b9e4de924e00754c404e0ce9ff82f3076797", size = 5285684, upload-time = "2026-10-27T22:50:34.304Z" },
- { url = "https://files.pythonhosted.org/packages/87/1f/b21fa2caaed5050c0622dc0ae5ccf5dc907a4ea33cf03d1f921734cc5828/obstore-0.12.1-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:8ed7826686178130f5f131e7b2d435f24f1256bf44029d4fb29ff1fca441c008", size = 5412047, upload-time = "2026-10-27T22:50:35.768Z" },
- { url = "https://files.pythonhosted.org/packages/ab/a0/989bb53ab9371959aa8923598520ff17f30e43b2e790d0a1e0cc5afb50f0/obstore-0.12.1-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:6e8b45d1ceed62706c0e19680a6cb705d9094d550e7f1287e230a86694a7c5c6", size = 5853753, upload-time = "2026-10-27T22:50:37.568Z" },
- { url = "https://files.pythonhosted.org/packages/4a/95/86c53a2d63dd3521fd895bf02a312893517f1489eaee837e7c1919b92f12/obstore-0.12.1-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:1b902e538809fd01e938707cc785e2efdfedc17600223323ef7b6a3d5c0874d9", size = 5365259, upload-time = "2026-10-27T22:50:39.099Z" },
- { url = "https://files.pythonhosted.org/packages/f1/6c/8a464c4d4a8ebce693e549777be90625cf01d0ba75daea99330d909d54a0/obstore-0.12.1-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:7c0497355e6fe55d98ef3fa94429488be9f2c4d26a786969f90a1e179efaa93a", size = 5179055, upload-time = "2026-10-27T22:50:41.339Z" },
- { url = "https://files.pythonhosted.org/packages/0f/46/db83f5e902acc4d7188e828b52ff72e1072fcc92d56af1849fc3e3894350/obstore-0.12.1-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:019af6060d0143975594a3f1efda0d15c0098702b889d048a2c085018a5c65e8", size = 5027315, upload-time = "2026-10-27T22:50:42.851Z" },
- { url = "https://files.pythonhosted.org/packages/ee/37/335aa42c62ce365b2ec37355f06eb6f57a8b53aa99160f0babec7c593e63/obstore-0.12.1-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8f7aeba8901b506056b01765e72d13af8a295ef1b64ad73251895364a843afef", size = 5278226, upload-time = "2026-10-27T22:50:44.413Z" },
- { url = "https://files.pythonhosted.org/packages/a4/e0/b3d447736a8e985ef10ffec4061bc895f2b410fe983b683213fec404ff0f/obstore-0.12.1-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bdbd3db63d7f97ada70ff9351948db6e4ba883ad189e422ffa03ff02dc4c34c4", size = 5440705, upload-time = "2026-10-27T22:50:45.952Z" },
- { url = "https://files.pythonhosted.org/packages/db/7b/cb10055c1f4cd78e3c52c5a43d912b3c1f5a6601d2989ea3656515061057/obstore-0.12.1-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96ce72f778133851d27602a429e744620ea6e762630a5f58140bb4f71fdaf8be", size = 5353724, upload-time = "2026-10-27T22:50:47.409Z" },
- { url = "https://files.pythonhosted.org/packages/6c/bd/bb2a98c22191b29f02c042e98be026a13e7eb284531f64d1a25a119ea6b4/obstore-0.12.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ca9a0399a5b43de54a31777cc56f8f2f7893ac444cac2b7d834f2bea299cad61", size = 5586399, upload-time = "2026-10-27T22:50:48.973Z" },
- { url = "https://files.pythonhosted.org/packages/e7/48/e0d510abfba83e571529b892e0d54280828b2761e6409af34b811cca58aa/obstore-0.12.1-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl", hash = "sha256:5c1fe53af794af798ac1e0ec509a0cbfc66fe68df98d3cc1b557d4602dd39b50", size = 5375790, upload-time = "2026-10-27T22:50:50.884Z" },
- { url = "https://files.pythonhosted.org/packages/0f/0a/c5999ce2d5d70071d2e6b45b10c98bc22bb5427d84120f02ca94b1716c7d/obstore-0.12.1-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:f7e02534dfcc2fd6188a4aa5901bafcf032142fbf8836caab80db7b6ad25e369", size = 5569722, upload-time = "2026-10-27T22:50:52.507Z" },
- { url = "https://files.pythonhosted.org/packages/f6/61/b899283be0df446f3f9cb2815a64d9faf8a8b30487954795b92471d3e90b/obstore-0.12.1-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:eea60451265e609c48510787080a403b79144a45e69f59b330f7e3901e11236f", size = 5252932, upload-time = "2026-10-27T22:50:54.126Z" },
- { url = "https://files.pythonhosted.org/packages/04/36/daed807520fdab5ac0031e0d7a459b3ce5aedde21f2f315b278c369599dc/obstore-0.12.1-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:f4cedcc164644f2923b19a763aa07a04134072bbf7a190637c267072cd8f6e4d", size = 5398228, upload-time = "2026-10-27T22:50:55.92Z" },
- { url = "https://files.pythonhosted.org/packages/b5/e0/6e38c19d1fd4dc40f087ad953c2c0dea1203994d359f253e1123ae175351/obstore-0.12.1-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:94fba14cd7d64f7a0b4da0224dcaec9c379ba7a1ddcad2c5e4c35a0b56977b61", size = 5815556, upload-time = "2026-10-27T22:50:57.387Z" },
- { url = "https://files.pythonhosted.org/packages/d8/c6/f0b45052e07d8eb2f5a6836b45babf18b06dae2019404f9af4662db6666c/obstore-0.12.1-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:2edaecb8b06e6f1674f8e0375f0e25df00c4a2162c489769cd4ffc182e8069ba", size = 5424080, upload-time = "2026-10-27T22:50:58.899Z" },
- { url = "https://files.pythonhosted.org/packages/d3/b5/91046acc476a58c02731140d987cc3faed8a4dd073154b9c3eaf34965107/obstore-0.12.1-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:eb4fcec7658aa6929f77502edef424213db8dfe173276ae6bff67df8f95e5aff", size = 4591674, upload-time = "2026-10-27T22:51:00.972Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/23/65/e14b7dba4ed71c3042fb9605fbfe3a8681bade1f5f90f18adbdc635b7c65/obstore-0.12.0.tar.gz", hash = "sha256:3f355f3333084172a8e8da2f62f929e2dd5d0909f6ba714a0b42cb1774a5be3c", size = 138191, upload-time = "2026-10-15T16:41:38.735Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/bc/18/8c236dc6c93c57f3796afaa3a11c7ce943da2cf268bb97cfe65ebd6db9e8/obstore-0.12.0-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:6fddc673fee8ddade9d49bff32de4815afd30b28ba25cbdc29d87639149cf62a", size = 5417987, upload-time = "2026-10-15T16:39:52.308Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/a7/f8da19362e555e1b826509c6197ba2f98f8a700c4fa4dc0761c2401453f5/obstore-0.12.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:656b9ae76280951f54503e883acb693f0fb4a3778d559733bdc4b996b2078715", size = 4615162, upload-time = "2026-10-15T16:39:54.81Z" },
+ { url = "https://files.pythonhosted.org/packages/11/b6/e342f399a8c664a6f3ad61fb1a4a1f77e935d14bc7aaba3525b1a9bcc93d/obstore-0.12.0-cp311-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:76b942f2cadbdd35d1a37df8c56a51a00e8279feafb9b3fde4ab161b5ca7a114", size = 5025558, upload-time = "2026-10-15T16:39:56.61Z" },
+ { url = "https://files.pythonhosted.org/packages/14/0c/4612546a8701c9d951920e0087be25d74865faed852731de69b2d9d63f51/obstore-0.12.0-cp311-abi3-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d87a362bfb078e015c2ab588ac38274db917c5563be2cd0306b51347db7d6edf", size = 5268378, upload-time = "2026-10-15T16:39:58.287Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/90/059ea17c30eef9f1025cd171d47360ea701ba8ca619302a75cf2a176bfdc/obstore-0.12.0-cp311-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a8d00b374ee368db3e8f537b4bab7c45bf6077d3f6c07ec93281f1cfd087271b", size = 5438991, upload-time = "2026-10-15T16:40:00.439Z" },
+ { url = "https://files.pythonhosted.org/packages/fe/6a/fe36b87c170b7bafd6ec6162f923eed213a503e621bc699ee2fc296a0e93/obstore-0.12.0-cp311-abi3-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7789fda63972d2fac0107afe5aac8d362b4d248971217f26a29bb15ac21cbdf5", size = 5348697, upload-time = "2026-10-15T16:40:02.618Z" },
+ { url = "https://files.pythonhosted.org/packages/70/3f/57e4173cbd61f1354d4f3f289a61a4271d8b3afa3a7f2efe519014697043/obstore-0.12.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fa53318c8892c612a58d0ac6c1cf26d9f5b697ca880de4133e9bd4868e90e5be", size = 5575588, upload-time = "2026-10-15T16:40:04.29Z" },
+ { url = "https://files.pythonhosted.org/packages/89/cb/ec80f278d834330b40834e34cf1332444fcdebe21c43f67e34b323ca2ece/obstore-0.12.0-cp311-abi3-manylinux_2_24_aarch64.whl", hash = "sha256:bf6b48324a6fec42088a9918c63ab064567d9544414aac086f65adc6c16ad2e1", size = 5340287, upload-time = "2026-10-15T16:40:06.166Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/01/bc007efe8a55ba4131a331fd47858d188ecaa3e23cf59bccb33bac2bd1c8/obstore-0.12.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9980becd715aa1cd7bd7ca13eeabae176e6713b6e39577622d964e31540a65e1", size = 5547826, upload-time = "2026-10-15T16:40:08.019Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/ae/57793c16df57064f062747d35a31167add5b68a7bfa1cf6f8ed929701b31/obstore-0.12.0-cp311-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:4b5fb3ef86cba98b280c6f02021274060787d5984ffe3791fdea9cdcea3f4bb6", size = 5249557, upload-time = "2026-10-15T16:40:10.148Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/37/878d4cc92c55114ee513bb77d4194ccbb99a87920e6345a1e972057383ec/obstore-0.12.0-cp311-abi3-musllinux_1_2_i686.whl", hash = "sha256:ac553dbe800be6b64cac1a4725c620ff5d474fd543deca90082dbd1fbfad2f1d", size = 5393052, upload-time = "2026-10-15T16:40:12.118Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/6d/a9e560f1232446cf1fc3cc1f03b5b7a9786e8312b6e543348d75a879e608/obstore-0.12.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:2956f243e459413f1f4bbd1195c721ca5a6c16d7de8bcd8241c2545e0bb87c18", size = 5809599, upload-time = "2026-10-15T16:40:14.398Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/1a/7586a93446b8ffff7bf9a1b41b1977ab125f4ef45e96d9b7b3f468961ffa/obstore-0.12.0-cp311-abi3-win_amd64.whl", hash = "sha256:7aef72fbe4631dd5462714309e55a3ceacfe15c5f31f518ddac41cc33a8cfcb0", size = 5341295, upload-time = "2026-10-15T16:40:16.498Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/97/8e68f15b93529939c6390bbbf59114e11017580629b8d14c4046dadb60d6/obstore-0.12.0-cp311-abi3-win_arm64.whl", hash = "sha256:e64494202759211cc42fa381d8959982473c9080a21aefcbf20121bdfb49a2cc", size = 5212128, upload-time = "2026-10-15T16:40:18.659Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/da/e51e62cb95c6c5531e49ea3aaf15df0e9fb34be0101bf4596d55afa6f820/obstore-0.12.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:728347d647edf981b7129899981a73ff0f0467225896d3747a6d4e993b7ee441", size = 5434598, upload-time = "2026-10-15T16:40:20.519Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/ba/1c2b8afedea56950785e43bcb0efb7e06e449ca147845e267e2e84fc317b/obstore-0.12.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2982abef7caa064dd412236d41e630a9b49dfb8265f62ca004059446624e9a73", size = 4584517, upload-time = "2026-10-15T16:40:22.326Z" },
+ { url = "https://files.pythonhosted.org/packages/29/10/9607706283e00a4f3a8370a3d77486fd72e53e089734720054755846bf29/obstore-0.12.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fa8e979499741c57798d2daa048e4f42835ac9a8497f9255796db478e9a38d1b", size = 5419035, upload-time = "2026-10-15T16:40:24.283Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/bf/57f52ca4a48bdb469a84cf4f3334e60324a5dfe24f7bfb180e3a7b1a57ca/obstore-0.12.0-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:58a3da66f68a98c559011f0099f9f3961db27df84218576cc1f95f4a88b5b8a9", size = 5346814, upload-time = "2026-10-15T16:40:27.197Z" },
+ { url = "https://files.pythonhosted.org/packages/26/ba/ecfc87519c17b2bba6c7275d754631b6ed1232f3f35e156b7a6749ba37cf/obstore-0.12.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:78c1cd6498e52410f9663134c277b07234960ef2a78eb4988327c2d5d9345986", size = 5558323, upload-time = "2026-10-15T16:40:29.812Z" },
+ { url = "https://files.pythonhosted.org/packages/56/49/4943918d58eff01789420f399484f867cef172822a7b77ec6087a3d4f6ab/obstore-0.12.0-cp314-cp314t-manylinux_2_24_aarch64.whl", hash = "sha256:a30dd09e62cb59f4c181f69b4e76780d6eaf0b3abb771fee3ac03f0e63109d1f", size = 5326762, upload-time = "2026-10-15T16:40:32.021Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/86/1eee013a94be33eea2ea618af4f3777b620dea7112e0c1b5356f082622b5/obstore-0.12.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:2622dc12100ca4b870a987ab9c0f4c3c1ca7122f14459bf66f834774790c9673", size = 5537435, upload-time = "2026-10-15T16:40:34.244Z" },
+ { url = "https://files.pythonhosted.org/packages/45/b2/df7567ce2e689999f8ec455a4f825b83e6c4c3935213696a80050f4fe402/obstore-0.12.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:f89ba827fe466d31cf7977525ce9ce7072e1395c98920b4846ae84a24c0b21e0", size = 5787179, upload-time = "2026-10-15T16:40:36.886Z" },
+ { url = "https://files.pythonhosted.org/packages/17/09/f0ccf26d95193c7f0e75183b52926a4b30f5af69b1539f6f44b27520a43b/obstore-0.12.0-cp314-cp314t-win_amd64.whl", hash = "sha256:75dabd1c785cee1d1cdb7fed4906eebcaf7dbb3ed7faaa862b6f54af59444a22", size = 5319434, upload-time = "2026-10-15T16:40:38.857Z" },
+ { url = "https://files.pythonhosted.org/packages/13/54/f80372e4773977c8f83f74ecd234c53273db32bb2f173aa7c49d5dbc805e/obstore-0.12.0-cp314-cp314t-win_arm64.whl", hash = "sha256:09f6f78e888a4b5a037b2c405456868102e3478a3fedcb3096afc51c26028f67", size = 5190455, upload-time = "2026-10-15T16:40:40.781Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/39/b80c381a82819f226219a588abdaed8bd30f27deb3afea99845a32c357f8/obstore-0.12.0-cp315-abi3.abi3t-macosx_10_12_x86_64.whl", hash = "sha256:0b8c8932d5234ec718e31ddcf9c4776c0cd8c694ea54baf555bffbbb8ca297e9", size = 5481244, upload-time = "2026-10-15T16:40:42.569Z" },
+ { url = "https://files.pythonhosted.org/packages/89/4a/b07c3964c48e39bc8dc704254b47c850b75c22ba3ab4a392f77903ab6503/obstore-0.12.0-cp315-abi3.abi3t-macosx_11_0_arm64.whl", hash = "sha256:d35bf87c228466d3920e7f98ca1ba1471ca864c5701f3be38aa442eb51ed290f", size = 4628787, upload-time = "2026-10-15T16:40:44.506Z" },
+ { url = "https://files.pythonhosted.org/packages/64/86/e190c535ed9d951dbbf28d716f1dc26a82f04ba597b59043f993b51a0ab2/obstore-0.12.0-cp315-abi3.abi3t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9a4c300fa1ab96f074df959e70cbdb37c0bb17d81d0b296b0d7fdd7a6fca4307", size = 5053980, upload-time = "2026-10-15T16:40:46.576Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/e3/5ea350a22070352859a9ccbc007061ffb9c3393c1c37de9cdbccbdc46eb9/obstore-0.12.0-cp315-abi3.abi3t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ac9909a21eea8ffb25dab9dad6491bac6b3234f93164cfa4b8638efb0fe248e6", size = 5282271, upload-time = "2026-10-15T16:40:48.615Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/8c/c3c87eeff228dea2dba146c20c0d94c3cd4a3c2d29027ed755bc2e8e1cea/obstore-0.12.0-cp315-abi3.abi3t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:000677a8bd10f2752cfea37a5f5f2a81414c0df07e9c5e3af4f1d1396e90250c", size = 5462575, upload-time = "2026-10-15T16:40:50.695Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/73/ffbda3894e7ee0ff42ae7e58e2c6007ee0433df261f62db31fabca7186dc/obstore-0.12.0-cp315-abi3.abi3t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3cf58ff29e0bffacfde8dd1858b57cc38f6a23e49f9357eb3c2ace9d741b3bbc", size = 5388817, upload-time = "2026-10-15T16:40:53.045Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/ce/cb0660726e2600d8fde9997cf5ddd90773379f5dd287a9411b83a7a6a29a/obstore-0.12.0-cp315-abi3.abi3t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:38acd564329c8732ea61a4c1f4dcb1ce34c4394371c42f4e075ee104268ddb7c", size = 5603974, upload-time = "2026-10-15T16:40:55.547Z" },
+ { url = "https://files.pythonhosted.org/packages/4c/fa/6471d396c57d67ee09343ddc64a6723aa9bbe95039409981858ad708abea/obstore-0.12.0-cp315-abi3.abi3t-manylinux_2_24_aarch64.whl", hash = "sha256:c0c7d5d9735bbb06e0f824d2af64e0335ff63a18fac8cbd2f83943814779a2de", size = 5369797, upload-time = "2026-10-15T16:40:57.788Z" },
+ { url = "https://files.pythonhosted.org/packages/12/81/0db46c02ffaf3a2db0a8db5a593e1d3a9c29bf7192bbb75fe6deaf79a7d8/obstore-0.12.0-cp315-abi3.abi3t-musllinux_1_2_aarch64.whl", hash = "sha256:cfdd2c7d04c9f81d3e110ba1e6690eddbeb9f9dfb932076624fad9076a970583", size = 5579688, upload-time = "2026-10-15T16:40:59.786Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/ef/8e606cf18bc8c9c0ce2523f27b9cc7888b329bd8a5bdd6ee5cd8d7a880bf/obstore-0.12.0-cp315-abi3.abi3t-musllinux_1_2_armv7l.whl", hash = "sha256:0fa81ce46e1d346c79e1a183a676d4511523079789c79edcf281b394c554303d", size = 5278268, upload-time = "2026-10-15T16:41:01.68Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/46/c967dac44c7c9cd2a07c75d32902b04a66422ff518002700aba5b8239198/obstore-0.12.0-cp315-abi3.abi3t-musllinux_1_2_i686.whl", hash = "sha256:bb944a435dff68147e462950119e9ab92392150cad91749c668cd10fc2bde919", size = 5402119, upload-time = "2026-10-15T16:41:03.497Z" },
+ { url = "https://files.pythonhosted.org/packages/4e/53/c2261822b995c8dbc71f65e4a9914f6c63c1f15befb785d6d9b77037d13d/obstore-0.12.0-cp315-abi3.abi3t-musllinux_1_2_x86_64.whl", hash = "sha256:74bc425a675eeafc0ce1db29f38807126253ed377c0518569e61132341f7da7a", size = 5839610, upload-time = "2026-10-15T16:41:06Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/06/116c124a0fad9104f83f854296483c759ead70ba2229f1df2a445cf92a4d/obstore-0.12.0-cp315-abi3.abi3t-win_amd64.whl", hash = "sha256:690018140a0ca0e4346ec5afc2c20608555f5f5e447d0bd5ca8b99b6cb1ec198", size = 5362445, upload-time = "2026-10-15T16:41:08.488Z" },
+ { url = "https://files.pythonhosted.org/packages/04/88/20151b32d7a0d1e15fd8f0b9a9c7439f67946f68071a2f63306b1e7fe946/obstore-0.12.0-cp315-abi3.abi3t-win_arm64.whl", hash = "sha256:3037ae42e0f036257a442d16ac49948a3759df22571213026d1c32d6045302dd", size = 5237094, upload-time = "2026-10-15T16:41:10.49Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/ff/3e417abec0bdfae2fe64b384b6cabe6417f33328fe239d155069f9924cdd/obstore-0.12.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:098c8e2a584dc991e33f3047143e7bb5c2bab1d3efc20f076fd1fdcda4fc92a5", size = 5020729, upload-time = "2026-10-15T16:41:12.931Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/60/0348ed2f2a00d1c7f380dfebdee049641f457e472a4afa328886db59c9ec/obstore-0.12.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4d9cb87386e464a92c69ef6f3168ee2dbe21dbc0d738c7cda646e79de1e16a80", size = 5264660, upload-time = "2026-10-15T16:41:15.531Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/09/f724a30c06211dcb1e899149e095d00179a3cf6cff8de232735d941a823a/obstore-0.12.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:dfbe400a02eea67104f5ae8fcad52fefe9571c042ee1257ce2059f0be1e47eaf", size = 5435452, upload-time = "2026-10-15T16:41:17.566Z" },
+ { url = "https://files.pythonhosted.org/packages/16/e9/3563a37041b4186863b98343c53801b944d3d9a1ff8e8bca6f9e3ad3f7c9/obstore-0.12.0-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1642e69f80e95c0a2d40d87239228145d2a99cb7720a19343301ce1df0a963d0", size = 5345777, upload-time = "2026-10-15T16:41:19.983Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/ee/ff4f8179d03b59f952c26247b80e89f9cc660573dede6f38c7fc1a370fa3/obstore-0.12.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:25743269e0f2b49b2bdc4068a912fdd3fc811acdff79dd886429686364c12a27", size = 5570602, upload-time = "2026-10-15T16:41:22.024Z" },
+ { url = "https://files.pythonhosted.org/packages/f8/7a/c0da11a5a8f0f6a4413747718dcaf5adc9d15da1375eaacc3eace5fdd31d/obstore-0.12.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.whl", hash = "sha256:e6612a88cdde54db1aeea432fa68477edcc43423baa8c46fd19109fa4d8fea1e", size = 5338889, upload-time = "2026-10-15T16:41:24.231Z" },
+ { url = "https://files.pythonhosted.org/packages/01/b5/ed40d03a77bfcaf3d65f260ea397fe1d50fe05e22fd560f944c15986c85b/obstore-0.12.0-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:3171dc1e0baac61725f7722219a92082ac65002af3be6dca0bda09f9049de624", size = 5547960, upload-time = "2026-10-15T16:41:26.279Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/62/9f316c412975f9a43f4ba8141fe53b72a35561e6825c7d3c282255153d2c/obstore-0.12.0-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:777678604e9658f7edfe7cbf0c0316953cc1511dd957a6f0a513866c4161ce43", size = 5246652, upload-time = "2026-10-15T16:41:28.582Z" },
+ { url = "https://files.pythonhosted.org/packages/dc/3e/27ffabf8b3d7a9115f99b7374bccdcb5b5ae106897c3e5733e9c88ce174c/obstore-0.12.0-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:08e9009e4996d5a57e924324fbb7fb3d5d9a41dc1645e82d87268098f40c5bd9", size = 5389579, upload-time = "2026-10-15T16:41:30.615Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/5e/390c745f7e7e5db8db3e6cc9344b8abaf25baf14925469cf34e3b76fb02e/obstore-0.12.0-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:c45fa5a7200c2c1ef72cd195ce8c621de9d3cc819880c120b80610da2598d0aa", size = 5799158, upload-time = "2026-10-15T16:41:32.585Z" },
+ { url = "https://files.pythonhosted.org/packages/25/bc/ac2409626548e681daa1d651bf70b79f5af49c7ab4ae5c0ea252809ef4d1/obstore-0.12.0-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:3feb6b4cb17f8e0d4569730835c3628f0a5818ea63b42fbd04b7aec98549bd50", size = 5415927, upload-time = "2026-10-15T16:41:34.771Z" },
+ { url = "https://files.pythonhosted.org/packages/8b/b3/88b609eeeacfec32ade070f2782570230a58a7f844cc1e3180e9aea39326/obstore-0.12.0-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:91d0635f8e714b3a3fccb3d4cc96e519decdd1eab4728876bcc6243e378e8363", size = 4613967, upload-time = "2026-10-15T16:41:36.827Z" },
]
[[package]]
@@ -3955,54 +3964,54 @@ wheels = [
[[package]]
name = "oracledb"
-version = "26.1.0"
+version = "26.0.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cryptography" },
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/db/11/96b4a08808217a230a662379b72140ad292f117b213a0274d8036aabf3ad/oracledb-26.1.0.tar.gz", hash = "sha256:18e1e78073a1479326e8766deb8b9358ae7880d80b65f562580b010da07c73ee", size = 943791, upload-time = "2026-10-27T17:16:45.035Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4f/77/f953a47ec9944f36f9e9b1d91a48c4e8025c38bd8c3d9d05820e20fed8eb/oracledb-26.1.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2f5d798b43136bc9d33cec05abb48d46d228fa71c42581ad59acaf2911aa607a", size = 4896939, upload-time = "2026-10-27T17:17:02.612Z" },
- { url = "https://files.pythonhosted.org/packages/cb/b5/1eb42dc2c753a1cbc61db0c922c69b28bad79cf8fd04aceeca2088eb4e97/oracledb-26.1.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:76929b0bf9be6379a7e9cde9578f6526e2b0a480c51570205d39637dccd3a377", size = 2695172, upload-time = "2026-10-27T17:17:04.002Z" },
- { url = "https://files.pythonhosted.org/packages/b1/8b/745b52b661c4c6131e66a895572367d707b7023fec6e3b9d5c78f43bfc33/oracledb-26.1.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4ca386a8e6dc9de6b4f4d3fcf4a82e1e44ba8e3471ccb6d5c3dd9b827416bcf8", size = 2896418, upload-time = "2026-10-27T17:17:05.508Z" },
- { url = "https://files.pythonhosted.org/packages/69/f5/36485ff11cafa2be93c54d927d8ceb3f7637cedc94ee16b99dec2aed0df8/oracledb-26.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:49267964f748dfb5ffdfc983afc9eb458dcded731713cff7bf1f5eb4c38749dd", size = 2750272, upload-time = "2026-10-27T17:17:06.762Z" },
- { url = "https://files.pythonhosted.org/packages/47/5c/f2a053b267262914647677d9e6aaaf2c2da296189c7f5ab5d7bb85a598c2/oracledb-26.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:9bc42b8686972072533b180a95d63b70373734eb9ff20ce4d3d290ea55e14251", size = 2925880, upload-time = "2026-10-27T17:17:08.028Z" },
- { url = "https://files.pythonhosted.org/packages/05/7e/48933a192a4431596d1a781a580df1bef74832fac4981f2601210f3f8059/oracledb-26.1.0-cp311-cp311-win32.whl", hash = "sha256:14dde5576cfb0ed0413cd632327cf02b210d449ba1720d1918807705bfd96b08", size = 1653578, upload-time = "2026-10-27T17:17:09.408Z" },
- { url = "https://files.pythonhosted.org/packages/e2/d2/c05de9942a69f19dfc43707eaeed90bcebbcf2eaaed319fefa75f6d062de/oracledb-26.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:a10f903086b940c2f1515dca6368316b7552a686bf561494ae339a62993b0173", size = 2119773, upload-time = "2026-10-27T17:17:10.636Z" },
- { url = "https://files.pythonhosted.org/packages/f7/74/735b6825837efc57ed20e65f12563b2e4aa520568c70cfb77213c12514e6/oracledb-26.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:fcd92d25fe3882958fd01302d3ea6c8d69c89627e0ce969396a3650fca8fc96f", size = 1848054, upload-time = "2026-10-27T17:17:11.977Z" },
- { url = "https://files.pythonhosted.org/packages/35/33/ef3f5ba74048111f2774f098b8362d3188268980113f3b076a8c699a0347/oracledb-26.1.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:82ef76d5c6ca00d8c914d6a84513e4508de53d88030e6abe19a8d7b403709361", size = 4998984, upload-time = "2026-10-27T17:17:14.062Z" },
- { url = "https://files.pythonhosted.org/packages/05/97/2e19d7184d85867f3862a368218e59244aaff0f4bf382d30b4f3788b58b9/oracledb-26.1.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fccc3a99ab060addc60d8560e765bb52b752f63ac80ce42e480d0d2bb10910ca", size = 2507588, upload-time = "2026-10-27T17:17:15.531Z" },
- { url = "https://files.pythonhosted.org/packages/86/cd/6d1b47372ad75ff61c96d4949066224f038f15ac25bfc63eade528dfb32c/oracledb-26.1.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a10dc4e094d9ffc5deb68499e8f6a4a33aa95656f1e735aadc1cfd2f9e7aac0c", size = 2726296, upload-time = "2026-10-27T17:17:16.888Z" },
- { url = "https://files.pythonhosted.org/packages/7f/f1/8566c4ed3f12b6cf94330595fc137d9d790c39830c42591a553607ed1508/oracledb-26.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:c7d563c30ef62f55e53c09ccd0036d83935a9621ab1476b4ac16d3dcc1668d07", size = 2555430, upload-time = "2026-10-27T17:17:18.507Z" },
- { url = "https://files.pythonhosted.org/packages/4e/0a/a72bd37e23aa064c32e2d48dccef0d671ec43ab85cc683a9e8937bbdcb2e/oracledb-26.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:fbc698fa8d8f044d98ac0a2ae765da63688eddd3fd0083d18a21839111e94484", size = 2755767, upload-time = "2026-10-27T17:17:19.816Z" },
- { url = "https://files.pythonhosted.org/packages/3f/18/54692e4468494cd0ba3a7fe8d0150d6c6a08bb7c48ff31f2a70c2f184db2/oracledb-26.1.0-cp312-cp312-win32.whl", hash = "sha256:f36a54fcfa6040f8a172314de35cfb1de7e7078f475ce33448b68e989ab9a786", size = 1622525, upload-time = "2026-10-27T17:17:21.149Z" },
- { url = "https://files.pythonhosted.org/packages/2a/ef/6408f374f00501801dc2dcc19e82b15d6378905fe369638c71f397901ef2/oracledb-26.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:e7340527d314907d04e909825e3cfbcb16228c04ceff01458d1c359ab4741a3a", size = 2020892, upload-time = "2026-10-27T17:17:22.491Z" },
- { url = "https://files.pythonhosted.org/packages/6a/9c/5956f7527510ef8f5f41b5c04844909143da5f5c8a980212d4da346b1977/oracledb-26.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:1421356bb391e23f3d3514b6e5109aae8715a1b97b26ba768bd22f907ba0f74b", size = 1744275, upload-time = "2026-10-27T17:17:23.793Z" },
- { url = "https://files.pythonhosted.org/packages/62/7b/8682086ae0399907451726d870f59a76453e77155bb97e2bf8763734d3a7/oracledb-26.1.0-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:26f1b210cd02c7a24753ac7981c0827a1bb8572c5f7f26e7b4394b948f6d3937", size = 4999909, upload-time = "2026-10-27T17:17:25.036Z" },
- { url = "https://files.pythonhosted.org/packages/03/6b/87d78ec1f2df8d5530680e4dd153d314b6bee8556ff9c21ccfa7ed5d83fe/oracledb-26.1.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fb5ae69a25c45d6fdff7ed52dc2110b59ef156beee6f82f78c8966d8d3c35a4e", size = 2501297, upload-time = "2026-10-27T17:17:26.337Z" },
- { url = "https://files.pythonhosted.org/packages/83/76/e6822285875816c901626fbe30ac29b4220ebc604e4f4dfbe8920db546c6/oracledb-26.1.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:40077ea15c242e8a05195b1c317c0ff82200d5acf304a387283ea90ab8af6e92", size = 2702448, upload-time = "2026-10-27T17:17:27.835Z" },
- { url = "https://files.pythonhosted.org/packages/52/42/485c977ff9ec44933d295facea5d408651c8d532a9e99dbd1cb4efd63785/oracledb-26.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:bff0cb102bd147c9f7546aeab64300f970695fe3f3da4bb861bde58293a12dee", size = 2555181, upload-time = "2026-10-27T17:17:29.171Z" },
- { url = "https://files.pythonhosted.org/packages/ff/9d/b15cb2a0b90a7b61bf6e6cc26a3a8a3091ae8525a281f96f1a1c44e312c6/oracledb-26.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:3472ce37ba7f2be4702bef0f0f655def77447287a070a64994f22fb45273c4a5", size = 2735989, upload-time = "2026-10-27T17:17:30.498Z" },
- { url = "https://files.pythonhosted.org/packages/f6/21/7e485c279dd062f2c9d196ff4a5f18cfe10029945ea995c858280bd32cef/oracledb-26.1.0-cp313-cp313-win32.whl", hash = "sha256:a280b65d55ddeeb39f259d48dd99b81b5385bce5de2d9a517ba1bedacead7abf", size = 1619889, upload-time = "2026-10-27T17:17:32.079Z" },
- { url = "https://files.pythonhosted.org/packages/e7/3f/a2ded12389b2c668776deee582b0f82eb13e58f00e707d49a6d9d9388765/oracledb-26.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:9f34e305e9eff7dc9c4b7bd3a19a78d4932568e60070855cc057ab8d0a18a16f", size = 2020219, upload-time = "2026-10-27T17:17:33.605Z" },
- { url = "https://files.pythonhosted.org/packages/ed/4e/27f2cb2faaadd0c24eab5f186fd403af97a1e027c6854af42fbb395a2770/oracledb-26.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:77f8f6c2b4dc5861f58c0b448fc1da5ebba320da8c457608b8ecf4b8d93f8ed8", size = 1743359, upload-time = "2026-10-27T17:17:35.122Z" },
- { url = "https://files.pythonhosted.org/packages/3c/e8/3f60c5f6e30e3b36560967689cb36885468ed290e9f6d7fa2bb715a3c90b/oracledb-26.1.0-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:e0aecc554558d739dab0c346bf18cfdb079aff1bd24fa1f069df8a93123aec47", size = 5054788, upload-time = "2026-10-27T17:17:36.754Z" },
- { url = "https://files.pythonhosted.org/packages/b5/87/e00707a669af0f7396a7784bb8819a0dbaa5ca35b4ae1f1d2bb862de6244/oracledb-26.1.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0cf48cc91399b300c9b430e28d91dca858fa4da4d0bb9b3887302ef477d8d562", size = 2549021, upload-time = "2026-10-27T17:17:38.197Z" },
- { url = "https://files.pythonhosted.org/packages/a5/98/5a4ad92c0564fc5f5be9a77fc14f0f33d55326910b14687788cbcb126c31/oracledb-26.1.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6993fa6d9582864eff72e9909c43666e30b83b5b3c51acb5f013c25175944c82", size = 2725259, upload-time = "2026-10-27T17:17:39.589Z" },
- { url = "https://files.pythonhosted.org/packages/98/3f/dfd1dc5cdcd328e472c1855eda407078e8bce1cc41ad105d679b7424b93e/oracledb-26.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:b96d28031f87a2df422730ba730fc49739844c01da951b762693cde96a95fa5e", size = 2603235, upload-time = "2026-10-27T17:17:41.159Z" },
- { url = "https://files.pythonhosted.org/packages/f0/9a/b308177a8b3fe38d337043623ab8e1cb8857786cbce3a5f4f68be3ed1bbf/oracledb-26.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:b163bd218caf65309b8513d6d6986c28afd0a3c9d697fb0bd6774e067110c2a9", size = 2752500, upload-time = "2026-10-27T17:17:42.649Z" },
- { url = "https://files.pythonhosted.org/packages/3c/b1/f74cfa64cf481540e8fb7a0e428dfe2fefc14370b907719242e19d9ab409/oracledb-26.1.0-cp314-cp314-win32.whl", hash = "sha256:ae4f1d7e32e71f4ff5c67aa8aa38cb2a204c1ce4a1b6bd83114473344af12fcf", size = 1644951, upload-time = "2026-10-27T17:17:44.074Z" },
- { url = "https://files.pythonhosted.org/packages/7b/1e/5efe8d52f7b1d50b6e8a40bcf43ce8dd4db426bee7a481246a63bc55a96e/oracledb-26.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:87c6ebb0cf4b1b72c0b4c22327bc9d29fa13052057adaa660e64d8eb242d4c23", size = 2077032, upload-time = "2026-10-27T17:17:45.438Z" },
- { url = "https://files.pythonhosted.org/packages/bb/6a/0cfa6eda47789857fa39fb1023b17bef32e0c06355bb21c87796a9d6a82e/oracledb-26.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:103dd657946244b0eda1056d11f6f425301240b204e107c1d1763024a6207328", size = 1798878, upload-time = "2026-10-27T17:17:46.954Z" },
- { url = "https://files.pythonhosted.org/packages/e3/22/74400e108fa596837bb2eb981a78472043acd044b0553f70074bb6d3b8d5/oracledb-26.1.0-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:950e5fd97a9b7b31218344d07805eb846e1a05d206a90e0faa63428a432fadc3", size = 5052940, upload-time = "2026-10-27T17:17:48.872Z" },
- { url = "https://files.pythonhosted.org/packages/41/9b/ba1abcc0513e9d0b51c49cc674831bfeecaa230cd6781d7f6f5dec620fa6/oracledb-26.1.0-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5434344431fb13d71f06e428a894f08c65622ce94f1ebdc9d2fba982515941a8", size = 2550183, upload-time = "2026-10-27T17:17:50.277Z" },
- { url = "https://files.pythonhosted.org/packages/04/84/812c5dd5dcde8759e217e0be33a966a2db115a1e1ee5352f800893557cc3/oracledb-26.1.0-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3b16e809a5409f9e4e6552fe7d7f6871e52ec4bf749c1762ce3ea57c2e6e7ebb", size = 2744432, upload-time = "2026-10-27T17:17:52.147Z" },
- { url = "https://files.pythonhosted.org/packages/31/49/97db788bbcd1edb0aef48e5bb466162b1496aa14e85e4aff92180dcd4e4b/oracledb-26.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:27921624f0fa0fad58b31c8c0c32981708e81b75c381457eef15b1ad1c711126", size = 2603456, upload-time = "2026-10-27T17:17:53.353Z" },
- { url = "https://files.pythonhosted.org/packages/0c/f3/195e97170be594ce3a71924a11d5d304289ccd2ea5503fe18e0ca460c3d6/oracledb-26.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:8a32e29e9588a886712c48a0bfa48b0f61180b620b723aa5954fa8f915a5cf22", size = 2773979, upload-time = "2026-10-27T17:17:54.81Z" },
- { url = "https://files.pythonhosted.org/packages/9d/55/56bb2bea611da121b3e99a1c9f95d4bada5638152eb36b52be41c98f070b/oracledb-26.1.0-cp315-cp315-win32.whl", hash = "sha256:31d42cadf3a15b41bd04efef20bdca55e2ce93cf39bd23cad13023c917c2188d", size = 1644343, upload-time = "2026-10-27T17:17:56.116Z" },
- { url = "https://files.pythonhosted.org/packages/22/72/78771b0b595b9146bda0b89fa9350662138145186c0574ad205ba60957ff/oracledb-26.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:6cdaecd494b1d02d440f2099bc6b1492dae300f13377d09da6cc9255755833bb", size = 2075952, upload-time = "2026-10-27T17:17:57.317Z" },
- { url = "https://files.pythonhosted.org/packages/40/53/afe3ad0eb706f5988235fd27b36817c89b899d5e47288ca523bdb5670d3c/oracledb-26.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:6c87de373f330ca1bdcf6b1b5f41eeee5a522fc566eb17f21f8e9d0ee53e48db", size = 1799709, upload-time = "2026-10-27T17:17:58.618Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/8c/f3/22113415f48b6608ada31ebb46047b3b8ac361dba393d1b5ed1f768d8f6a/oracledb-26.0.1.tar.gz", hash = "sha256:786397a6b37e94ebfa6c1c6cd026755eb8fccfd39e15bcb64cb5879491882aef", size = 910463, upload-time = "2026-09-22T21:23:20.378Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/ce/91/e63f777028a560908bc3662b1cbbaf8aba3a27dd57304655349fcbd128fe/oracledb-26.0.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:c8635ed9f8d659421679e16d7da56a0d50078d91b40782fb9c27c85f8d6c3075", size = 4806896, upload-time = "2026-09-22T21:23:36.916Z" },
+ { url = "https://files.pythonhosted.org/packages/0e/59/149845af7f23cac1138f76ff1f68d0e6e03fe65e67da8084251c98bc4fa7/oracledb-26.0.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:f1872361b806678c8865e375b8a46b59e2b4923191fb49af37d1dabc9438189f", size = 2633349, upload-time = "2026-09-22T21:23:38.505Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/58/caf7db67e8c66e0e099c30b58d257e3a60b1b33c88b331dd2802c9e356df/oracledb-26.0.1-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5b6949975dcd283dd4904c79c2263bbb32843116eb07f16b266b07b069ca2593", size = 2829714, upload-time = "2026-09-22T21:23:39.971Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/e8/979beffc989f146ae5e37cd5b53ebe08231306afcd5ffe245368a2710a39/oracledb-26.0.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:7d1fecfa6e7c39a3ee8aedeca7be2e48f4008d90cdcfa1b49ae9f825827b8156", size = 2685600, upload-time = "2026-09-22T21:23:41.73Z" },
+ { url = "https://files.pythonhosted.org/packages/29/57/5eb64f683afa2b56ece4f8d81bd09d60db19ff487c9a92911bff2be6c795/oracledb-26.0.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7d407685928a87240c54d2ca8cc5e0a89fa44639b459030519f120db461004a0", size = 2856509, upload-time = "2026-09-22T21:23:43.229Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/c9/49e89422cfbc04fb93aabaca1008892322ea8c177136e14e86c50131270f/oracledb-26.0.1-cp311-cp311-win32.whl", hash = "sha256:391b61d248ade2f988581a2acadce20f3b3b21886481fc1e5bc57aceab7ab376", size = 1608982, upload-time = "2026-09-22T21:23:44.594Z" },
+ { url = "https://files.pythonhosted.org/packages/a3/2d/12b184980a87ed89cf6b23816b3aa6e4ed720a5708ee7af5960f56eee9c6/oracledb-26.0.1-cp311-cp311-win_amd64.whl", hash = "sha256:582d629ca1282653f575a74b57e3c146175a55ea43c1470ddca44c3eef04dddc", size = 2068149, upload-time = "2026-09-22T21:23:46.024Z" },
+ { url = "https://files.pythonhosted.org/packages/47/f2/61869d47cf3d74099d9398c56c839a21394b35dd19d05e91a60abb6e7881/oracledb-26.0.1-cp311-cp311-win_arm64.whl", hash = "sha256:b857ecc33ed1ec8141f4e45a7420a4df4fc625dcaa35e32b01dca62400376d5d", size = 1709535, upload-time = "2026-09-22T21:23:47.737Z" },
+ { url = "https://files.pythonhosted.org/packages/83/fa/349ee179b3e106f210ac070d199dd341f37e2af9f5faf2563b80d8aa6ab1/oracledb-26.0.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eea4d0824fa82b03c0d90aad81f10dbbacdaf351979c0130190a362a539e63ae", size = 4908265, upload-time = "2026-09-22T21:23:49.524Z" },
+ { url = "https://files.pythonhosted.org/packages/60/68/2dc6a99d385835290825c0d6689097af29637db8754197040b95814bb5cd/oracledb-26.0.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c05707b2ba57d8210bdf062ba3419900591f3369d1532fb116af51445c2993d", size = 2448388, upload-time = "2026-09-22T21:23:50.993Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4b/ce48ed072c2328b429a6bc87a35ddd735945151145d9fe49c1e177b0da83/oracledb-26.0.1-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:36cbf2be185412fee95be96aa6f187a73fd5181bb03186b53d5b70ac18fa354b", size = 2663296, upload-time = "2026-09-22T21:23:52.426Z" },
+ { url = "https://files.pythonhosted.org/packages/f2/c8/73166ff29389c5ce3272fcc534360ab65248f90fcdf175a99b5fa2bb8466/oracledb-26.0.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:d5ad1b6f4db3d8a43182e7734922342fac5901456b34acead92f6fc60cc59882", size = 2493289, upload-time = "2026-09-22T21:23:53.949Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/c0/f6b4f82d6dc7f7d7654c0e4889caa1abbe019ec76daa1b4b4ee80859172b/oracledb-26.0.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aecc4cdd03cb5a5ad51f43680d16d74132ae7df65c991e7bd69bbe2ccc0fd209", size = 2694730, upload-time = "2026-09-22T21:23:55.609Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/da/a3ad79e18c5b27b357de005ce9d79dff1ef1646b7c55b6e6557b238f03bd/oracledb-26.0.1-cp312-cp312-win32.whl", hash = "sha256:f55d6fd9ebc3e8bf3bf4828fcd6ae152b363859628d27f92a4764111adfb1001", size = 1578393, upload-time = "2026-09-22T21:23:56.975Z" },
+ { url = "https://files.pythonhosted.org/packages/06/c6/8e2ad66bda2cda6d085f3516ad8f7834b1d7d4c9da21f96834467525b551/oracledb-26.0.1-cp312-cp312-win_amd64.whl", hash = "sha256:1c6f2f0d7290b15f1a166c0d24187e7746a6f08ea6e886e521110f1bf8d2002c", size = 1970972, upload-time = "2026-09-22T21:23:58.352Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/ce/98c3a83142a562e40135abb8503b59eba7af1135c11c8077986a289f3a35/oracledb-26.0.1-cp312-cp312-win_arm64.whl", hash = "sha256:c31b6f7440047c867a1b41cbaace7867db0750ecab4dc6454c9f501991152deb", size = 1622016, upload-time = "2026-09-22T21:23:59.996Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/d7/2a911476339d9fa6eb83e6a4639b007c13af26d7261907ecc2d8722a6712/oracledb-26.0.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:339dd6c4f0d50dab4026a36ae89d902240b2a879a17cdf18e3feadc113957b4e", size = 4911060, upload-time = "2026-09-22T21:24:01.757Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/e8/97e3b2c283ab996efe1d12b6afa86042c746b0ce4e408942bb4b7c35e39a/oracledb-26.0.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5907bb5c9df123e417ddf358f540554dfc6766064c2feb84121c099bc4e039a3", size = 2445641, upload-time = "2026-09-22T21:24:03.587Z" },
+ { url = "https://files.pythonhosted.org/packages/03/f7/bbf75b91248ea489e8393ae84e9985b37c895781c658b6d9e5d83d02c1c1/oracledb-26.0.1-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:16d08328bf9b02e980ad4d5f3e0e99596f9a42aaf6bd1e244ea85be753f2c292", size = 2646590, upload-time = "2026-09-22T21:24:05.22Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/32/cbdacb302a2ca047e36e0f804c493391844aad83822d428184fa1d4203d5/oracledb-26.0.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:50e42111d5c620f60478e4571a5847c335286e8650e63a8789f8bd51035f90da", size = 2499129, upload-time = "2026-09-22T21:24:06.801Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/1e/ce86b581a4f1c8d5844019f84eacc9f4084c882ba676a90c94d7eaeb749e/oracledb-26.0.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0453f8f1a6748c982dcdac8af09bcd81971979c7ec9d1d151e15595f58d9164b", size = 2675979, upload-time = "2026-09-22T21:24:08.153Z" },
+ { url = "https://files.pythonhosted.org/packages/82/7c/49e6af631b84db68d801b6d03ff58c4fa7b767ffa8a8402de643584e9d63/oracledb-26.0.1-cp313-cp313-win32.whl", hash = "sha256:93e6a7cda6ad7b14e2ce8689c9694872b3d69d57015aab070028c9a93faf3bf1", size = 1576622, upload-time = "2026-09-22T21:24:09.492Z" },
+ { url = "https://files.pythonhosted.org/packages/59/10/ba7a5ce82fda9bbfcf222930b9a6f9a967c2b3d49f787ba7f4f99b598c6f/oracledb-26.0.1-cp313-cp313-win_amd64.whl", hash = "sha256:6cf0c4d8662072917e2f98598cb97a18e157785bd0f294fbcf770d822ca239a9", size = 1970210, upload-time = "2026-09-22T21:24:10.806Z" },
+ { url = "https://files.pythonhosted.org/packages/24/dc/96ce57dad3b1bb3d3c30be936d0f6daea0a02649d8936b878921004b15e1/oracledb-26.0.1-cp313-cp313-win_arm64.whl", hash = "sha256:22bb1e47a01a5259eead9202be8f631682b69be08ee29d8e532ed9b9364bd7bd", size = 1621513, upload-time = "2026-09-22T21:24:12.246Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/e6/7cf147ea1daf0c7c86fcd77acce1a09b0d4ab75539a4677a2034f112c860/oracledb-26.0.1-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c2201184b46bbec476a7ac29a72f14ee4ed75d692ade3b6c5d672fdbca71e6b2", size = 4961776, upload-time = "2026-09-22T21:24:13.967Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/7e/1ada40fce6ec550718e6eb1b8be9bfcc6e9697f19b6244ed6e0b89aa8da8/oracledb-26.0.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:8bddb6bd7156df750045b419ffc400cfe429276f1a17d83913f10adefd0baa9a", size = 2486660, upload-time = "2026-09-22T21:24:16.397Z" },
+ { url = "https://files.pythonhosted.org/packages/65/2a/a4cf6f8081b1ab3498c82e3917b2174843bf22e2fa4c41e98ad1e0b66d47/oracledb-26.0.1-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b33b22800a9b02ee0ead00e6f3073b810f7dc51c1e82fc9fc5475c09812420df", size = 2664849, upload-time = "2026-09-22T21:24:18.16Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/f2/50a32fa2d8c6cbb0e5fb69c262b01e7412ad08e430192404af9d2b9f4bea/oracledb-26.0.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:124f0072e6c7df277c561ec3a4c42cb2c02c30e9ce15e75a460b4abcfee41046", size = 2544330, upload-time = "2026-09-22T21:24:19.627Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/90/9b6dd9bbc1899ca3a982e9012f25be2a70e4795eacc6f8582c340c64fe1c/oracledb-26.0.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:4c5094d5ba60b0f67ce7c74eb0fbc738d827103c0ba2db5f8483db405ab4e9c4", size = 2693190, upload-time = "2026-09-22T21:24:21.228Z" },
+ { url = "https://files.pythonhosted.org/packages/92/42/0df9265f2f562283c88590a6bcd442084cef80c832ff598bdbab994a3cb8/oracledb-26.0.1-cp314-cp314-win32.whl", hash = "sha256:03424ccfbd25c402133c2a23c5c1a630e57effda7b962085d91c41d765af1b25", size = 1602133, upload-time = "2026-09-22T21:24:22.733Z" },
+ { url = "https://files.pythonhosted.org/packages/db/d4/1b90c5e252a53dbc71833217746f2673e9e2ce0e06115bac1dc42ae347d0/oracledb-26.0.1-cp314-cp314-win_amd64.whl", hash = "sha256:ee8f4736b72f38ade26f9770f7b40e8662bfa825ef48cefcc55cef0c30525d4b", size = 2027835, upload-time = "2026-09-22T21:24:24.105Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/4e/9febea61cbf476c2ae5d290fb3f823f1e46668e3ac15b8af5307531e33ad/oracledb-26.0.1-cp314-cp314-win_arm64.whl", hash = "sha256:13b0dfbbc503c6107d6c08b1658df260c2c077bc5574d9545159df0a0301fe7d", size = 1680743, upload-time = "2026-09-22T21:24:25.475Z" },
+ { url = "https://files.pythonhosted.org/packages/77/bb/71f7861bfbf586757873259a858f370030a137b4172865992c3cc9a462b1/oracledb-26.0.1-cp315-cp315-macosx_10_15_universal2.whl", hash = "sha256:9e77a6b3b01a3a2f4b786939f59d862fbe73fbc4c68cdf87aa45a5398725a16c", size = 4960764, upload-time = "2026-09-22T21:24:27.244Z" },
+ { url = "https://files.pythonhosted.org/packages/14/c0/86aa68f08e5c1d25b864ac5777c559bf0bc8911e5d8262b70bec5e05cc21/oracledb-26.0.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:3d722f185f9c05ecccb2702a58a1e5ecfd309ae92cad0c38373e0969e83bf2bd", size = 2488466, upload-time = "2026-09-22T21:24:29.177Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/f4/af94230a3c2c6679323f3578a0a5281cfd87f72994f66e6044b5ff6c95e9/oracledb-26.0.1-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bfcf6382bc48e21e448ca2b71644cc75626056a638bca50181acdc8adc69dd3c", size = 2685915, upload-time = "2026-09-22T21:24:30.823Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/a7/aa99f8e60961e80795ca926ead66c66d98c3aee78d970320eacdfa843614/oracledb-26.0.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:d42a2e0caea9bcf7d260d430b225d72cff6dffa0d6729be7a8823db2678e0b74", size = 2544920, upload-time = "2026-09-22T21:24:32.667Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/65/b636a18a578cffd5819df08ae80deeb931f1215d2d2b18112a37e9d2bbea/oracledb-26.0.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:2a073cc8bd74a7ce32d12f4bc898dd143971981b52ecd2f72c37b4474bff76cb", size = 2713492, upload-time = "2026-09-22T21:24:34.22Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/4a/e758838b14540a94599487d0d15ce4390edba5a257e53f2cea66f94bd138/oracledb-26.0.1-cp315-cp315-win32.whl", hash = "sha256:f4188bf8eedfc05ef266835e98ce304fda92d6bc87dd776d5f9a8b510d8b54ba", size = 1601872, upload-time = "2026-09-22T21:24:35.591Z" },
+ { url = "https://files.pythonhosted.org/packages/62/b1/51b2a51abd89ba43dc0f9ee14d456a9b002d0b9c31840c7de55387e555eb/oracledb-26.0.1-cp315-cp315-win_amd64.whl", hash = "sha256:9d508d2df7bb9a4802247ec95d06064e2babf8ae9e1f91d89c349bcd30df44ed", size = 2026829, upload-time = "2026-09-22T21:24:37.15Z" },
+ { url = "https://files.pythonhosted.org/packages/62/6b/b099ff2c51447cbf0d2f1c33ea7de1abf96d9bf4c3d93b2a5dff48728965/oracledb-26.0.1-cp315-cp315-win_arm64.whl", hash = "sha256:7c9541d7cf2d324e9516c955c033235bbf25b7d96ce41a856b3bff2eab3494ed", size = 1680535, upload-time = "2026-09-22T21:24:38.551Z" },
]
[[package]]
@@ -4177,11 +4186,11 @@ wheels = [
[[package]]
name = "platformdirs"
-version = "4.14.0"
+version = "4.13.5"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/40/28/1c058a9143d2304b3078f8dadcbb9dc27f7d3581b5d05157c84869c4cc0d/platformdirs-4.14.0.tar.gz", hash = "sha256:83f009c1e8c5fc36869b5171755b291887bd2cade7965233cdfabd23d754720b", size = 70387, upload-time = "2026-10-28T18:27:30.973Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/58/e6/d0abbb256b44e6a05748c6942511ff67db8dbb068de0c35b24bf1b395b09/platformdirs-4.13.5.tar.gz", hash = "sha256:05831adf87d392760f4b5c3a0a95579ab5a10b18b9b9bd43680dcb47544788a0", size = 62362, upload-time = "2026-10-19T01:02:11.778Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/69/2b/887969ef4bb53e0cb271dc7c3f4a73d50c5d58bb88aeaae311e3340cd57e/platformdirs-4.14.0-py3-none-any.whl", hash = "sha256:1dadfc59a8a5d7565b3d9270ae8fa71c065595352c4277b41c11ee6c222c4b91", size = 35259, upload-time = "2026-10-28T18:27:29.659Z" },
+ { url = "https://files.pythonhosted.org/packages/6c/8a/1d8cb3842909dc193a73835adedf412e32e18f8ffd59f43983949622fe6f/platformdirs-4.13.5-py3-none-any.whl", hash = "sha256:53d3ec4280b815c1e0b02edad7f9ee8fda1ba9c0865dd9c2e6e63849ce82fc1e", size = 32779, upload-time = "2026-10-19T01:02:10.17Z" },
]
[[package]]
@@ -4195,30 +4204,30 @@ wheels = [
[[package]]
name = "polars"
-version = "2.1.0"
+version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "polars-runtime-32" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/b9/da/c27daf205a824f89f71172a75ad0098c5837a5a31ddeaac7f8737ea8574b/polars-2.1.0.tar.gz", hash = "sha256:9eee488d6e10773c028753dfc8dac647fd60a708108dcb818a733e85d33df253", size = 811508, upload-time = "2026-10-25T18:00:33.43Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/8e/e9/001f371ec6a1bb54893f599ceebd56e6144fed4091f09f09fec0021a9276/polars-2.0.0.tar.gz", hash = "sha256:62da109e27a19a9d36657ee25dc035c9d3f87e7bd610526fe467dc37ea7dc115", size = 778215, upload-time = "2026-10-06T11:51:29.679Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/d5/d4/e696a46c74ec8cac6434764deaf72f7edcc2e91c2145a9b3f61900a7aa7a/polars-2.1.0-py3-none-any.whl", hash = "sha256:63c50ce3533f5a03d17c3a2a24f1d0e0c7eda98fbde4268a3cba900d34060e67", size = 911474, upload-time = "2026-10-25T17:58:26.863Z" },
+ { url = "https://files.pythonhosted.org/packages/ac/09/cc33bbd5463749c116b62c204d88bed6c02a6cb901eac7adab0d38651b07/polars-2.0.0-py3-none-any.whl", hash = "sha256:35d62f3541b7a6d4c360a2e2f07fccc0c2bcbd33b0ea51c83a25417a47a3f3ad", size = 876611, upload-time = "2026-10-06T11:44:04.327Z" },
]
[[package]]
name = "polars-runtime-32"
-version = "2.1.0"
+version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/e0/a6/46e09af693326735b1fba3a8bf5a725b57136fb1342e707e75d3113c826b/polars_runtime_32-2.1.0.tar.gz", hash = "sha256:c575f5f3cdb8a5f9be65477b9b4202b062593687fe62a0082add87fbd4571067", size = 3878004, upload-time = "2026-10-25T18:00:34.932Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/34/ad/dbb6f6d7070867951532bcfe5e6a648d8777b416b18cddabc07030404e8c/polars_runtime_32-2.0.0.tar.gz", hash = "sha256:b5f9afcc742b4a67eabd2c680ff0f12eb02ede9b4bf807bffabd6dbb9a58d5c7", size = 3591339, upload-time = "2026-10-06T11:51:31.076Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/7a/40/b424f3946c702ff48eef14e44d35df81a25a784f20f77355f39c746528fc/polars_runtime_32-2.1.0-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:de243b6e5a0b9f3abeaefe542136fcdc6dd8df5be3c988a40838d075314e246c", size = 52169672, upload-time = "2026-10-25T17:58:32.437Z" },
- { url = "https://files.pythonhosted.org/packages/fe/38/07066430ac655a8cab2f9e96ea1fec6ca1f20ec47844afe117662d43ace0/polars_runtime_32-2.1.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:317314d65949f1d58c4939adf83aa0d2cfaf8da50fd4a725a2de27bde2c10018", size = 47626883, upload-time = "2026-10-25T17:58:38.688Z" },
- { url = "https://files.pythonhosted.org/packages/49/a8/a773e20b451bbaab4d8267867b7c43a0c3aa67ebb6f781423a42a5a1af37/polars_runtime_32-2.1.0-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:777228cc8bf9cda36dee85ba74d13a25974ad4caffc5da93e10b1da9d810ce2d", size = 50033514, upload-time = "2026-10-25T17:58:44.261Z" },
- { url = "https://files.pythonhosted.org/packages/d2/e6/649c524252d32b24d0ba92278033823fa8e0040ee3748ce5afbeda107403/polars_runtime_32-2.1.0-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0b838056cf365d7497971d9d9825c547766923338771c48c545b86ead2ebc572", size = 53863954, upload-time = "2026-10-25T17:58:48.737Z" },
- { url = "https://files.pythonhosted.org/packages/90/f7/2eb405bf5ead6373a33fa5dd4637fe996565a1f07ccce8911ed167673740/polars_runtime_32-2.1.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:04cfc3db1571b74ea4145944de7feceed4bfc9b26e5b2c265e27ad3b2d3866a9", size = 50204290, upload-time = "2026-10-25T17:58:53.33Z" },
- { url = "https://files.pythonhosted.org/packages/33/4d/d76cd42042605e079d6df91cc4fea09ed5f4cb836c72c952a17f585860a9/polars_runtime_32-2.1.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:af58b5f6e5cc44b578f2a93d752f87c135c3a53205b4360301bc3a9b89dfbfd1", size = 53794253, upload-time = "2026-10-25T17:58:57.99Z" },
- { url = "https://files.pythonhosted.org/packages/4d/4e/2a419d0def2908255dd1b7318c48430319859dc92ca156e2f81ff7bcd78b/polars_runtime_32-2.1.0-cp311-abi3-win_amd64.whl", hash = "sha256:f2fe32f48af2395cd6ec52a433a03970a69aab1dbb66a613729fd549ce92c904", size = 53926265, upload-time = "2026-10-25T17:59:03.13Z" },
- { url = "https://files.pythonhosted.org/packages/c8/1b/15ffba3264baf21d2f04c51f54b01159cb6f4fad4e84de1404be552d601f/polars_runtime_32-2.1.0-cp311-abi3-win_arm64.whl", hash = "sha256:0af30dd53a7725f703d0d21dd421a39e0165378bfaa41431008f286608376d71", size = 48636436, upload-time = "2026-10-25T17:59:08.476Z" },
+ { url = "https://files.pythonhosted.org/packages/82/88/d35dec6c8928dfbaa1cccf9b626a1067da906e792c92d9f994ca825ab2b5/polars_runtime_32-2.0.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:ffb7ac6cf4e8c4a652df1951e3c3840c7c23a033603d5a9efd422fa8dd699d82", size = 52494314, upload-time = "2026-10-06T11:44:07.768Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/fd/2237bf53ffaff47cdf1edc6c10587a7a6444d4951150eeb08d84f3493ff8/polars_runtime_32-2.0.0-cp310-abi3-macosx_11_0_arm64.whl", hash = "sha256:7012d8a0201bd95638545ce8f256c0efe2c5cab0f806eb043021dddde5a9498b", size = 47930083, upload-time = "2026-10-06T11:44:11.592Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/0d/85e3ed90417996fc09770be91b39979074fe2978fc15b431bf8a9459760d/polars_runtime_32-2.0.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b85bb42e6009acc9629afcc70a83473fd468694d6a30ffb0ab376c8dd1a0a17", size = 50417889, upload-time = "2026-10-06T11:50:20.774Z" },
+ { url = "https://files.pythonhosted.org/packages/83/88/e9fecfd49159da92f54ff2445883577a0f1bc195da53ecc9535c458d55dd/polars_runtime_32-2.0.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0d6ac584ea2b38913784db943879412380d92e28ab9cb88e20a77ba71ba3f911", size = 54475036, upload-time = "2026-10-06T11:50:24.411Z" },
+ { url = "https://files.pythonhosted.org/packages/48/ad/b2abf732697b21467aaaeaac0f3bf7eee0d89c59ce8125f1ed41b28a2d97/polars_runtime_32-2.0.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a6bf5e260e0a6f00d0f9181438fe9e45776df8c66cee9cba16e3675cc3888488", size = 50579474, upload-time = "2026-10-06T11:50:28.377Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/05/304deee59a95865e1b5e9ec7b066069b49093b81b768f473d9d3b165c686/polars_runtime_32-2.0.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:55c26eef325b6840584d91aac232e9cf3ac19e1b904594b9b54131be1edeab4d", size = 54413293, upload-time = "2026-10-06T11:50:31.828Z" },
+ { url = "https://files.pythonhosted.org/packages/61/59/8c9fd7199f7c4eb1b64e640306a946a2e4a46337b3bbb33b840972c7d84b/polars_runtime_32-2.0.0-cp310-abi3-win_amd64.whl", hash = "sha256:7da1caf3c7b4f397fb213c984013a0c755557619a2d511899a1ff74392484078", size = 54229989, upload-time = "2026-10-06T11:50:35.206Z" },
+ { url = "https://files.pythonhosted.org/packages/e2/93/43608026f38aa6ed4d22da8597706a61682ee403caef0021ce8e6dc73227/polars_runtime_32-2.0.0-cp310-abi3-win_arm64.whl", hash = "sha256:c30ba698c8904048df4a9bc3d6c5033cc2d0a7cbb0e13f4fd2de5a1947b61994", size = 48730655, upload-time = "2026-10-06T11:50:38.756Z" },
]
[[package]]
@@ -4236,19 +4245,19 @@ wheels = [
[[package]]
name = "prek"
-version = "0.5.9"
+version = "0.5.7"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f2/c6/89312171d4b87b7d2419a077f84e638bee69d1b7943598c72685655f86a7/prek-0.5.9.tar.gz", hash = "sha256:47bc660a7e959860a44afb5ff3dc167d96f04d929b405020a0f2c7683d3d00d8", size = 603716, upload-time = "2026-10-30T10:32:01.272Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/e0/0c/985c6f0fe1671e639fb97bf5a60f3945369f26d4ccf5a8a1b063d357e6da/prek-0.5.7.tar.gz", hash = "sha256:91049700f0f13b4575deb44045dd6dd51f7c4a70c53ec5d452b09925962e3b35", size = 580753, upload-time = "2026-10-21T00:46:48.231Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/4c/d1/318ff67aae2307cf1db65a5eea46ed891b340efaa75d9fba3c4307247ee3/prek-0.5.9-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:007e406e38a247fa52edddb7f7381a5c19058872c7999ee0ba92a112d768a2b3", size = 5861967, upload-time = "2026-10-30T10:31:43.735Z" },
- { url = "https://files.pythonhosted.org/packages/5d/17/cef855f5a9c40d588557bf7a703bce0420d9592fa8407d2320df9a48135a/prek-0.5.9-py3-none-macosx_11_0_arm64.whl", hash = "sha256:db03274472abdc033fce78ff28d6abe221eebd2388f0c6d293376b1b09df0978", size = 5511607, upload-time = "2026-10-30T10:31:46.06Z" },
- { url = "https://files.pythonhosted.org/packages/34/b6/ef0bfb91af9c1485775087108810eb7ea28152de2e7ecb7485310f259a4d/prek-0.5.9-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:cc050edef6217dc0a12bd3202258b1449f24efcfa227cbf8cee7cac0a2b7f8df", size = 5625679, upload-time = "2026-10-30T10:31:47.884Z" },
- { url = "https://files.pythonhosted.org/packages/03/01/92719c905cf39683fe3db9664f1520ed6a0ee21e899fd9862ea6f72351c8/prek-0.5.9-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:da36f09983a5ca9c36afbda2c8b45e567b3f597acac38de9636850cbf5778570", size = 6058394, upload-time = "2026-10-30T10:31:49.995Z" },
- { url = "https://files.pythonhosted.org/packages/7c/de/9f538470c56f0524215d5ede21fdb092b5a5a55ee8b4ccc88a35487d57e0/prek-0.5.9-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1ef04fd7b4bae3a31e1180bc87e877a3a5584063fe79320a125267afee47090d", size = 5942047, upload-time = "2026-10-30T10:31:52.132Z" },
- { url = "https://files.pythonhosted.org/packages/27/ba/f4ed6bd2624f0889d4b557c72355e69636ac141c4b6c91b6af6999686b55/prek-0.5.9-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:db754a188464f4f3ea2101c74593ed0a5c371132c2da42daa8b94a67a9f15331", size = 5625016, upload-time = "2026-10-30T10:31:53.846Z" },
- { url = "https://files.pythonhosted.org/packages/2e/f2/469eb2cd0e2202117104aeef5e4430c05ad3442f8a7616423e204e58214b/prek-0.5.9-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:7c4acbfded9dfdba5bac0690c92b1aaa5fbeedceb2172eff2c4f38a750a68075", size = 6092336, upload-time = "2026-10-30T10:31:55.634Z" },
- { url = "https://files.pythonhosted.org/packages/cf/1e/2c35ff3b059b6fbb43273c3ebe996b6d9d37c1889607c5ba6b9eea66530c/prek-0.5.9-py3-none-win_amd64.whl", hash = "sha256:b8fe1a01273203932b46318ebf304304838b6b73ff99b89531f42c86b63b86d5", size = 5501419, upload-time = "2026-10-30T10:31:57.93Z" },
- { url = "https://files.pythonhosted.org/packages/a3/a6/672ef3834b367ec376261fe269161851377e32597a52b447e3f1534b6f3e/prek-0.5.9-py3-none-win_arm64.whl", hash = "sha256:a548c0744311bd5ca521ea2cd845ced65b4f0de0c0a59dc87f0e1669a1c2dede", size = 5444009, upload-time = "2026-10-30T10:31:59.685Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/36/56c107d100cabfb7e7c7f2e62d363052c77c3fcf664707b66ef9e3b865a4/prek-0.5.7-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:621835811891d11d3feb5ca47102ca97973c5b4e6fbd1b35b1293d412d29d9cc", size = 6062121, upload-time = "2026-10-21T00:46:31.418Z" },
+ { url = "https://files.pythonhosted.org/packages/51/59/1868e2d6b3581eb9f327cb7db4270c4f38e80c5b175517e439e106eac704/prek-0.5.7-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ef10cf46c0660b011fc067b7ce20dc78f2c44b6f905bf8dc49402353f67283b1", size = 5686422, upload-time = "2026-10-21T00:46:34.006Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/e3/90901432ee7ca288b46872552ca8f658b2262cf56d9af3c1eb0416f206da/prek-0.5.7-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:b38075f9363066d72d1cd887b5a260ec938516fea9ea44def53e592ee75090d5", size = 5817816, upload-time = "2026-10-21T00:46:35.869Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/d1/f8e24b15906e433850a2c8409ae3a9270724b2a5d4a71b8c4eadafeb201b/prek-0.5.7-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6a8f7734931c4b6a5f2efca121fabed7979408df233ce6d9b043f4148873021c", size = 6276177, upload-time = "2026-10-21T00:46:37.511Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/15/c83456872211706839ff326474aa9f2d870eef7ee5ec2c436446519779d2/prek-0.5.7-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c139c494fb1d9826688f8107d1514fa2a58f8f4a0f0527b68e7970fa50a8b723", size = 6140221, upload-time = "2026-10-21T00:46:39.29Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/b5/996b05447d4c2a4399947a7c5c910b2cd2c544b3074c11153ae3e21b4112/prek-0.5.7-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:e86139ab5662124818dc1957854d8281f635414016e545ab72e63365c995824b", size = 5812638, upload-time = "2026-10-21T00:46:41.006Z" },
+ { url = "https://files.pythonhosted.org/packages/83/e5/60e0e76cd6819d75026b3a8147ddbd2b3fcab27c02236bce8b3a559e0e64/prek-0.5.7-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:94bd6a93ccf8ebb1d30b822161515e3e31ea916ce7411febe8ceb5f607eb840d", size = 6298681, upload-time = "2026-10-21T00:46:42.618Z" },
+ { url = "https://files.pythonhosted.org/packages/54/97/e14b12e7d8fbdb468581d778fb892c855fbca325d4344cc535c12e2851a5/prek-0.5.7-py3-none-win_amd64.whl", hash = "sha256:fa69837b6d1d2b6be5fe7fe4cd27fd5606f2155e784fec55ee7df7ed9f9bf211", size = 5692294, upload-time = "2026-10-21T00:46:44.385Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/28/9dc15c0644cc6019c16c5cdd44e5004b641f5e910488f830e80dfdd11042/prek-0.5.7-py3-none-win_arm64.whl", hash = "sha256:239ccb081e3766d83a6a9a5ebee1d5cbb7986fc8e4f230e785423d409cff95e3", size = 5626111, upload-time = "2026-10-21T00:46:46.341Z" },
]
[[package]]
@@ -4699,11 +4708,11 @@ wheels = [
[[package]]
name = "pyasn1"
-version = "0.6.5"
+version = "0.6.4"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/88/7e/3c0da41061a2021cac7cd524fd57092adbe5b515e8bf9005702e366802e2/pyasn1-0.6.5.tar.gz", hash = "sha256:6b11616a6bcf3a40e635f082bb232fa57b5e0dd7f517bd7ddd69ec3794508a47", size = 155152, upload-time = "2026-10-27T06:32:49.125Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/a4/9a/23310166d960def5897e91fe20e5b724601b02a22e84ba1f94232c0b7f67/pyasn1-0.6.4.tar.gz", hash = "sha256:9c447d8431c947fe4c8febc4ed9e760bc29011a5b01e5c74b67025bd9fb8ce81", size = 151262, upload-time = "2026-07-09T01:12:33.988Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/19/1f/3eefd5c84c35cae44549ce1c7f7e58f7945b34f5a6bce07a135438522073/pyasn1-0.6.5-py3-none-any.whl", hash = "sha256:4017b0fce125096b8573b39c989fdb1a4b19cf761e683398184550fea0e66587", size = 84807, upload-time = "2026-10-27T06:32:48.045Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/3b/6163796d69c3977d1e4287bea4a6979161cbbdd170ebb430511e8e1999ce/pyasn1-0.6.4-py3-none-any.whl", hash = "sha256:deda9277cfd454080ec40b207fb6df82206a3a2688735233cdcd8d3d565f088b", size = 84410, upload-time = "2026-07-09T01:12:32.92Z" },
]
[[package]]
@@ -4729,7 +4738,7 @@ wheels = [
[[package]]
name = "pydantic"
-version = "2.14.2"
+version = "2.14.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
@@ -4737,146 +4746,142 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/9f/d5/b4867fa7c7f04c34997d7d8aad744f64d210bb99d67cf18bf3fb1eea1aed/pydantic-2.14.2.tar.gz", hash = "sha256:fb7b2db6cefe8414dd612edeede49c4c433acb0432b0f92b95024e45f66c505d", size = 911596, upload-time = "2026-10-29T16:37:12.476Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/7c/0b/8e10b2e693af8ec54346a14caf36221334775975a747c9ceaa3f8371d96d/pydantic-2.14.1.tar.gz", hash = "sha256:94f478203dd03404682a1ada216965651dd74b1d2d5ffd62e00e0837caab5c26", size = 900362, upload-time = "2026-10-11T18:37:55.396Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/97/01/c71a71258b58659c72504e5fd65b451f6e36e34dc931d0b237ded213c814/pydantic-2.14.2-py3-none-any.whl", hash = "sha256:7efbf7507b937931cd32a5755acb00ea1cf5ccb1d4e8067a5850d5069e382118", size = 490403, upload-time = "2026-10-29T16:37:10.181Z" },
+ { url = "https://files.pythonhosted.org/packages/35/ea/a56b9fe5066f3537b7882f77e9c5dfb26d8c2eefdaed9b5fc73d57b4dc22/pydantic-2.14.1-py3-none-any.whl", hash = "sha256:9195d967ec791692a04438115466764fb8b9a27b31f14a760437694f40d6b454", size = 488106, upload-time = "2026-10-11T18:37:53.437Z" },
]
[[package]]
name = "pydantic-core"
-version = "2.50.2"
+version = "2.50.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/af/8e/23ec1021834e35041fbb2f10946141e6acdfd39379c45cc4d65a4e8a1d98/pydantic_core-2.50.2.tar.gz", hash = "sha256:1b0e8dc3932cf4e9ed5727570325cacc716b9409ffc794a221c0ccc72e8eb775", size = 518711, upload-time = "2026-10-29T16:34:57.465Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/8d/0f/1043d3840b7859cc19abede795e058a785ebc1fa011b022b0a69514dd5c5/pydantic_core-2.50.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:2115d7ca6d58554af8a14da3dd32d361a4fe999fae52f115a6155679b5b00e72", size = 2160886, upload-time = "2026-10-29T16:31:48.009Z" },
- { url = "https://files.pythonhosted.org/packages/d8/95/6211dfe1794bde5ce14f3de2212551196784c67411ac5249d8aa627e3e23/pydantic_core-2.50.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b2dd65864aad38dbcc57c3d0e2c978d4286e063378137d8fc0c9fcd558943285", size = 2115730, upload-time = "2026-10-29T16:31:49.449Z" },
- { url = "https://files.pythonhosted.org/packages/67/42/50a1a9721cfc21d360916754096c90467fa21386ef16875445ea06a4cf6e/pydantic_core-2.50.2-cp311-cp311-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:26b778d4ebb75f181d36220ff0f54a513fc46f053d9b09ce79993c2f00bc45cc", size = 2274234, upload-time = "2026-10-29T16:31:50.668Z" },
- { url = "https://files.pythonhosted.org/packages/69/0b/22349c3114a3b3c400333ba6ec1d2e127fc9a4e4682440d8e23d954df0d1/pydantic_core-2.50.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:e80c47314c7233e7105a7efcfffd89d56e479e558189d1959eeeaf2639a27680", size = 2116001, upload-time = "2026-10-29T16:31:51.887Z" },
- { url = "https://files.pythonhosted.org/packages/d8/2e/3b88612239e87cfddd370362e93b18a50d5075883817d6b35e8e3871af75/pydantic_core-2.50.2-cp311-cp311-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:3bfe12243b3ded517c6734d91f127489e2f51bf95cb533b53130fe360333db80", size = 2204318, upload-time = "2026-10-29T16:31:53.342Z" },
- { url = "https://files.pythonhosted.org/packages/3b/3b/a735bf3967d0fd73b512705d6f80fbe179d3b6208779721e86613cdf736d/pydantic_core-2.50.2-cp311-cp311-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:01eb0282d0b3a065d55956d1be9ce398c46db3dd6d277a6cb2358c8aa8c53179", size = 2392365, upload-time = "2026-10-29T16:31:54.721Z" },
- { url = "https://files.pythonhosted.org/packages/9d/16/0a0febc06b311d6ca43dd7fa068273d2c4d3c4ad86ac4a6c2f2cbe924c23/pydantic_core-2.50.2-cp311-cp311-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:196b87b48b2695426920e8470d20f9e99f019ebd86f15808aa018b68086840cd", size = 2443963, upload-time = "2026-10-29T16:31:56.455Z" },
- { url = "https://files.pythonhosted.org/packages/70/7d/22bd04c22adae266ae5e99e4c34bd882daf6c93c35bda2f9674421c1e7c3/pydantic_core-2.50.2-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:86e7abd24a67f61a4bfe8df059b67a2bdd8bf33705178e98463195d140bd3780", size = 2236349, upload-time = "2026-10-29T16:31:57.837Z" },
- { url = "https://files.pythonhosted.org/packages/2b/c4/77b12031179047a292128eee12cd7aad899d82391157356a581117e232e7/pydantic_core-2.50.2-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:aff31602e8518594e6f6bc670cdb27e37cb7dbeb2d5d0b76a7707b70fe951ddf", size = 2272702, upload-time = "2026-10-29T16:31:59.287Z" },
- { url = "https://files.pythonhosted.org/packages/98/58/41f674077b08b79bf2f2ae3c777a5b7b8758647433c6c621580b612e8c1e/pydantic_core-2.50.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a88851e8e332588cb8cced8b5318d1163c7d0bb87830aa8dcafbc4ae9602b73c", size = 2311296, upload-time = "2026-10-29T16:32:00.722Z" },
- { url = "https://files.pythonhosted.org/packages/61/af/527d1d29cfb8efc7c7f9ed9f35ab5b16a33b1837b186c6e42ecbca699817/pydantic_core-2.50.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:3b1bb3950e0794a795c9344d818d5b476189b0c9c9755e8eb8fde87c3acee17a", size = 2481042, upload-time = "2026-10-29T16:32:02.423Z" },
- { url = "https://files.pythonhosted.org/packages/57/35/79877aaf083424e67c870ba07c0b5ed36a2096a0cfc8b058c1d921797723/pydantic_core-2.50.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:928a44ad1e21a7e923eb924d601605f0694cd187d2ce5d40bf7ad76650f4627d", size = 2488007, upload-time = "2026-10-29T16:32:03.819Z" },
- { url = "https://files.pythonhosted.org/packages/aa/45/5a627e40690c21be388c4abfddaefbf589e359614456156c90432ac0a76a/pydantic_core-2.50.2-cp311-cp311-win32.whl", hash = "sha256:9a87744e683d9a13f60b7fa5242c17f8d283ffa94ad727cb19063b48c3d34960", size = 2121376, upload-time = "2026-10-29T16:32:05.053Z" },
- { url = "https://files.pythonhosted.org/packages/1f/9b/d21478003682505aad9619ff9e086bd29aced2615b23d5315cb37de14cad/pydantic_core-2.50.2-cp311-cp311-win_amd64.whl", hash = "sha256:b958c217c6e56bef261f516e4caf5d490b079ad6a428752afada5a764cca6c71", size = 2238388, upload-time = "2026-10-29T16:32:06.811Z" },
- { url = "https://files.pythonhosted.org/packages/e7/26/670a52ea740fd60f5e0a873b74505714c4bfd04c4344f789310005c4cbbd/pydantic_core-2.50.2-cp311-cp311-win_arm64.whl", hash = "sha256:b0e6a2152706d84f7da8b9d34dcecc7e9bde678af32c40bd5b405f427ae0fbdb", size = 2185763, upload-time = "2026-10-29T16:32:08.441Z" },
- { url = "https://files.pythonhosted.org/packages/d6/65/6609a66cda909d414c58438f9bd70b1e947fe12c25f6bd40c93121afd7ef/pydantic_core-2.50.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e3d824b6152e1c0fd58dc6266f2de470eb7b2286135294f5f7cd26a0f0693738", size = 2220341, upload-time = "2026-10-29T16:32:10.193Z" },
- { url = "https://files.pythonhosted.org/packages/f9/0b/92463ae7be78d73885487f72c4599902c2cc190378bccaad5718b7495250/pydantic_core-2.50.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:596921489390a002d6a5e303384a0f1f98384f8bb550fbf38a1aafa6093da0f9", size = 2093701, upload-time = "2026-10-29T16:32:11.502Z" },
- { url = "https://files.pythonhosted.org/packages/62/d3/24147a1aa22f7eade546172750e1110fd7b2793998057809800d3ebeae4e/pydantic_core-2.50.2-cp312-cp312-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:36d8b9725684ca3bf658b90a101d335fc8cae405bb22196b7b5ef41344913756", size = 2253622, upload-time = "2026-10-29T16:32:13.051Z" },
- { url = "https://files.pythonhosted.org/packages/f6/50/2628ca3c83a0d9a6fda3305fe59b1b28e40c0221e8f234cd1ef86fb64745/pydantic_core-2.50.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:43a8fd56f3c681f3cda2d5c842ad28e88241df742dfcbe8fd1dd437a3056e1f6", size = 2114101, upload-time = "2026-10-29T16:32:14.702Z" },
- { url = "https://files.pythonhosted.org/packages/21/c8/cc6d7304a93f91f5e06260ccad71cc7d882489663a37dec009532379fc46/pydantic_core-2.50.2-cp312-cp312-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:6f5a432c6c1ef18a1a02a04b3616b8d3f2f88b13faa46caae1ac6559ab202234", size = 2199541, upload-time = "2026-10-29T16:32:16.153Z" },
- { url = "https://files.pythonhosted.org/packages/65/d9/18f44ac5696b3e2b2384cb89c9fbe6ed69860f528e232b9fb46cc5b45a17/pydantic_core-2.50.2-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:5d3cd8be71c4da3c9b42cc92f17e5e3036bdca98bf18555a0e072c3fe87abbb4", size = 2368597, upload-time = "2026-10-29T16:32:17.544Z" },
- { url = "https://files.pythonhosted.org/packages/1d/22/5f2a48ef819a7e1b74fb003a85d27576ad2f9ddf3901b88998ac63ce1e00/pydantic_core-2.50.2-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6a438cb8a3f87f2c49e82e43fb19511d4d5043f53e0787dc4df937ccb904373a", size = 2451701, upload-time = "2026-10-29T16:32:18.956Z" },
- { url = "https://files.pythonhosted.org/packages/08/93/2a6556bbdb8e918175af9d849a5d0b367b5644e41939d040f3a7215b1339/pydantic_core-2.50.2-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9667637665acba166a7dbbc786f14c50512d3a01f7199e0e1a13b929e92d7c7a", size = 2242782, upload-time = "2026-10-29T16:32:20.381Z" },
- { url = "https://files.pythonhosted.org/packages/90/ed/dd9f29a92257f32eb5b99d5120826186ee0632ecc3aaf6c23e2fab89ed6d/pydantic_core-2.50.2-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:b3e681208e2d39fde13fbea449ef2736a85ec1ea2afa41e9193a8c536e71dafe", size = 2253706, upload-time = "2026-10-29T16:32:22.293Z" },
- { url = "https://files.pythonhosted.org/packages/11/19/1ccf3bf38eb4ed97d8aeefc558458834ae0ad53fdbb1edf61af7228ea500/pydantic_core-2.50.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:6eb6ff07964f39e33c6f57fefe939514e76b8c0d4a694e5663e2e04e80cc1798", size = 2288603, upload-time = "2026-10-29T16:32:23.465Z" },
- { url = "https://files.pythonhosted.org/packages/ea/cd/bae2320fc89a65bcd87bf66ec83aac51b3b43b970a2edece15feb8af64dd/pydantic_core-2.50.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:4eb54c25e3463d73e1e4ed439ffc45d1d7e590df40bc7bb06f11a906afd996c3", size = 2476597, upload-time = "2026-10-29T16:32:24.824Z" },
- { url = "https://files.pythonhosted.org/packages/db/35/1bdb8d52c8e562e95c5e6ebb5784792af568ac6a81470dd546d95f392b60/pydantic_core-2.50.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:91238117cd296352f54f24117ff5a227bdea428865dba1244aa0cdd1424c0bc2", size = 2478664, upload-time = "2026-10-29T16:32:26.197Z" },
- { url = "https://files.pythonhosted.org/packages/12/72/0179b40ac6a6b0899c61826f2e3e80f88063f6d821e0dd9641927de46700/pydantic_core-2.50.2-cp312-cp312-win32.whl", hash = "sha256:98722314e12a8e4fa346e0afd3fff2098b5732875ff45592b450a7b589b43c4f", size = 2108820, upload-time = "2026-10-29T16:32:27.736Z" },
- { url = "https://files.pythonhosted.org/packages/b4/fb/679144362d7e5162519d9f6631ec982521417de3196351c6c56485f5b29f/pydantic_core-2.50.2-cp312-cp312-win_amd64.whl", hash = "sha256:b381574614245f18fdb2d6d1aa8f149e225869c47b5ee7f81812de8977817314", size = 2252238, upload-time = "2026-10-29T16:32:29.572Z" },
- { url = "https://files.pythonhosted.org/packages/0f/68/e3ccc885c3e2086ff84d7ca496f45b974a9f0a49ce2da8973aaa3a369a4b/pydantic_core-2.50.2-cp312-cp312-win_arm64.whl", hash = "sha256:f07c965a8aa6d62145f122641f292129984f39a3e3697585b19298a1488287db", size = 2162565, upload-time = "2026-10-29T16:32:30.853Z" },
- { url = "https://files.pythonhosted.org/packages/c2/fc/ac3ae9d934932ce3c380e7fe53e01372b49ef5dcb49db56f1a1f005b4d8c/pydantic_core-2.50.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:93eb41d626d9a2749a678823c70bba8c25033cef474ae8001fd7621adbbd8437", size = 2222463, upload-time = "2026-10-29T16:32:32.125Z" },
- { url = "https://files.pythonhosted.org/packages/f7/0b/7605223654ed4e777d05668e914d32b476b1fa2bb756bff9820fe270456b/pydantic_core-2.50.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:7c539d452cba22f250103c7d1c00cbe9038eb705e657476e701c43374efd9f2e", size = 2093995, upload-time = "2026-10-29T16:32:33.496Z" },
- { url = "https://files.pythonhosted.org/packages/b4/29/4a1a653cfedd3f5e1ccd3b1e919fe324fb45607d12c30fdd7ca5af16ba8d/pydantic_core-2.50.2-cp313-cp313-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:53bd25e8ab54c02bb4c55d4848c1d36f75f3b9a7356e83179fe27794d9e9e6c4", size = 2253178, upload-time = "2026-10-29T16:32:34.77Z" },
- { url = "https://files.pythonhosted.org/packages/6c/f5/03ce5ed01c98b60272cdee8ac1abb819b64ef347717c11d96215e32d9952/pydantic_core-2.50.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f142cd663d1148ed32993ed9a58e5c67ea312f446a01a6c13a956945aafc9f23", size = 2113894, upload-time = "2026-10-29T16:32:39.241Z" },
- { url = "https://files.pythonhosted.org/packages/d3/bc/f7644474ac0faad9a7fdf5179239893b21339169b8b4f89e3075c4be16b2/pydantic_core-2.50.2-cp313-cp313-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:126f158474346f457c0759c29c1a6334acd5f7f99dd272bb27d19314f3d81b4d", size = 2198248, upload-time = "2026-10-29T16:32:42.357Z" },
- { url = "https://files.pythonhosted.org/packages/c2/8f/3d3a1c1d313e02774f6c034eb1910eb6e795922e66e81f0f053f98afe866/pydantic_core-2.50.2-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:0b94a515f44f1ebcb25395bcdba506c4cdaf6d3fcb0466b9658521b41e5ff057", size = 2368505, upload-time = "2026-10-29T16:32:43.755Z" },
- { url = "https://files.pythonhosted.org/packages/03/33/38fb4545c00ce2bfebc2a0ffd99f7f580f1e1efb8db3b2faf88def640435/pydantic_core-2.50.2-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:a55c1163e829a08707b4a92c8ee02a5a3a3847ced74aa4da94e3e975ffe1ec63", size = 2451704, upload-time = "2026-10-29T16:32:45.075Z" },
- { url = "https://files.pythonhosted.org/packages/79/be/b559a1ceaff8b1d4581733d47d2ffa785635ac05716eae4c964466aac6e8/pydantic_core-2.50.2-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:31a1fb3ffbd1195b24490f671d12623dd4c9bfa56d6af0a299fa43b516134f5b", size = 2241760, upload-time = "2026-10-29T16:32:46.382Z" },
- { url = "https://files.pythonhosted.org/packages/2d/7c/3c8ab7cb45e4dc7687a4f16a953fee14d2eca18dd90d6be8bd470ef06962/pydantic_core-2.50.2-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:83d6f42ae6eba44548a1a3d4f900a5b1500fdd14f09f04754a8800ab0d544432", size = 2254115, upload-time = "2026-10-29T16:32:47.818Z" },
- { url = "https://files.pythonhosted.org/packages/5c/ad/3f3471206224cdac2af3492a3f7c724c47a82f5f8850ff364c2e36023497/pydantic_core-2.50.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:9fcde11462e0faab4c2a2ae8cd4516f7dd8239eec583bd88c5524d8064ad2d0c", size = 2288598, upload-time = "2026-10-29T16:32:49.404Z" },
- { url = "https://files.pythonhosted.org/packages/a9/1e/2070b388b1d8633cfb72e3964ac3c95aaeb195c240209894ff386847b494/pydantic_core-2.50.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:848984e231190ae49bc8b5229e64d2a6fba4231dc6046e1b6b28ddc060bc7601", size = 2475116, upload-time = "2026-10-29T16:32:51.414Z" },
- { url = "https://files.pythonhosted.org/packages/3d/96/e96ff19527c73b9aaddce2a4424006dc15439fd89ef2d39641048c5ee85a/pydantic_core-2.50.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:8772c2c0091e6fc2d0499151793d31bf811826986b0727052935a95104845544", size = 2478926, upload-time = "2026-10-29T16:32:52.856Z" },
- { url = "https://files.pythonhosted.org/packages/35/10/2fefc082eb46f959e80735e859161305c0c2fbb3d5d20ac5547c49a84a3b/pydantic_core-2.50.2-cp313-cp313-win32.whl", hash = "sha256:bd6ecae08c0d871cf93b6f0d4ee76b06c65be44ea22311981a022b2f1948cf9d", size = 2108354, upload-time = "2026-10-29T16:32:54.276Z" },
- { url = "https://files.pythonhosted.org/packages/6f/dd/14eeec394ec1422f9c4e9743cd746f4502232a7c65eb235d1a03729d94ab/pydantic_core-2.50.2-cp313-cp313-win_amd64.whl", hash = "sha256:e5c801bdf3cc92274b8fc2c7023c8751e5ae81260c009ac558ff55ce712a2d45", size = 2250556, upload-time = "2026-10-29T16:32:55.396Z" },
- { url = "https://files.pythonhosted.org/packages/75/b7/a014acf1ae4b4aa7d32ff957e2250698637daa3a29448444180360b92284/pydantic_core-2.50.2-cp313-cp313-win_arm64.whl", hash = "sha256:fd9aa71a800c87c0f7ab53fa38009169a23a82c685989626ca74a4bd483e1b67", size = 2162633, upload-time = "2026-10-29T16:32:56.792Z" },
- { url = "https://files.pythonhosted.org/packages/f2/64/fc525bb981a9f287cfa34346e7ed1d5ef7c043c2b9b517f54c6136032d10/pydantic_core-2.50.2-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:efad0bdc67d3b119c3bab6b94d341a27333c2bb89ed75dfa182a4a06cd0ea329", size = 2226414, upload-time = "2026-10-29T16:32:58.559Z" },
- { url = "https://files.pythonhosted.org/packages/db/68/315701095f25687938ab79d263a6d939337456c2840fe69038fd38000338/pydantic_core-2.50.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:6ffb4dce3ecb2cd563aa30f9eb5088a1cf1f4f688b90cbb374fa96e49fc50b02", size = 2095041, upload-time = "2026-10-29T16:32:59.873Z" },
- { url = "https://files.pythonhosted.org/packages/b8/4b/efd3abf31aa24bc97b311735f418886f947b4f193df02a1977643963b03e/pydantic_core-2.50.2-cp314-cp314-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:150f6e101f575c21a4ecfed1be1c05bb89a0ad49cb9c846ed377583bd71b32c2", size = 2257635, upload-time = "2026-10-29T16:33:01.096Z" },
- { url = "https://files.pythonhosted.org/packages/2d/6c/146b36eed4de8115a0fda76840703e75f5f8b36aeb3af86976bf094d1a17/pydantic_core-2.50.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1db9cab6197e797824f0479dd2ae5fd2e272aa61c61b7faa5a6e450ca7a0d3b7", size = 2116807, upload-time = "2026-10-29T16:33:02.692Z" },
- { url = "https://files.pythonhosted.org/packages/fb/94/db006ea79bbe405cc38b25899319c7d461ff1813cbdfe6aa90a65ed1dfff/pydantic_core-2.50.2-cp314-cp314-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:1b9f652e17a13bbc09b45f74c3c326cca72059ce4dcdef687fcb04389db413a9", size = 2201037, upload-time = "2026-10-29T16:33:03.942Z" },
- { url = "https://files.pythonhosted.org/packages/41/f9/ea2bc1ec13a67af4a63c9a887fa0aa6ef4ade9018e2bb412d4fc80110e19/pydantic_core-2.50.2-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:823b222c4fc861a82c650998a6117ecbd150cc71bf8b78f9a31bf3f48c456108", size = 2374904, upload-time = "2026-10-29T16:33:05.458Z" },
- { url = "https://files.pythonhosted.org/packages/44/8e/26a6a075301f0e563646037ea69bef8c63911d38fbd257f94cec69cdf985/pydantic_core-2.50.2-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:783abdfac21247d33bec40aa683d26bba30d61655c40de38b2decc4797aabf92", size = 2457626, upload-time = "2026-10-29T16:33:07.271Z" },
- { url = "https://files.pythonhosted.org/packages/9d/a2/8eeabaf1d394f0d3b9a27129ac21556eabee6f2ddcb418069ef4037e00b0/pydantic_core-2.50.2-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:b29b7c5adf4b2be7c941f7f76432aa3269cd5052ff8610376b8ece24d7b02c51", size = 2245505, upload-time = "2026-10-29T16:33:08.824Z" },
- { url = "https://files.pythonhosted.org/packages/bd/54/449778cd397e2682b30d1bcfab0872f6ac36702b59cc326296b7f4f14928/pydantic_core-2.50.2-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:4ac996e42a93d80122a23edce44e02c6b9c4913f5c6198a077da31475341a39a", size = 2259563, upload-time = "2026-10-29T16:33:10.107Z" },
- { url = "https://files.pythonhosted.org/packages/26/c2/2f65691d25488ee0f3c17861d079e8ccb7817955522d07cd3c3970d30af5/pydantic_core-2.50.2-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:99e1d482138a71902415ba375bb4627679a5fefd3bd07803c3a8f37632f4981d", size = 2290470, upload-time = "2026-10-29T16:33:11.273Z" },
- { url = "https://files.pythonhosted.org/packages/9f/32/aa7c40234cd24536afa0895ff1d26f9f15638004e25242c5193ce053b96b/pydantic_core-2.50.2-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:a53d5f11cd6d15569ba3e53f3498077f0d5e633f6ec604d2ea613e895017ebee", size = 2477272, upload-time = "2026-10-29T16:33:12.771Z" },
- { url = "https://files.pythonhosted.org/packages/c4/43/c347c68e040cf93d8487a8f4fc28f2768312834c7d21c301538f416f9955/pydantic_core-2.50.2-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:23385c4626f8b5a7792aa381ea573d9432cfbf4c21375a0a5eed2a4fb64e0e58", size = 2484352, upload-time = "2026-10-29T16:33:14.485Z" },
- { url = "https://files.pythonhosted.org/packages/b8/8e/72556126c8b6154fe7acaf757b96b2daa37aeffc3a517deb76e272f5ad09/pydantic_core-2.50.2-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:9a8a62281fd9c6a373b3fd3d473685e6838d43341eecde5f47729748e2c11ee3", size = 1491226, upload-time = "2026-10-29T16:33:15.603Z" },
- { url = "https://files.pythonhosted.org/packages/5b/66/004c85511812964a36b3535af022c5c0dfcb760a31e4905b96310e8dd6c8/pydantic_core-2.50.2-cp314-cp314-win32.whl", hash = "sha256:18f008ef58cd30782f81b6e0119e20b7e884edcefbc2d370162ebe033f6e5c99", size = 2113134, upload-time = "2026-10-29T16:33:16.87Z" },
- { url = "https://files.pythonhosted.org/packages/a3/5f/dcd3df20ab2a7279d42a8415fc184e8e77bcf9aa3c9ac3e938c1300a242c/pydantic_core-2.50.2-cp314-cp314-win_amd64.whl", hash = "sha256:b62c89545d21d930c333208fd548e7b4f262228ba3a322fd75c987a75ab11f31", size = 2253407, upload-time = "2026-10-29T16:33:18.47Z" },
- { url = "https://files.pythonhosted.org/packages/4e/fb/0685895f5d199b215011dc4eb8ccea032a64ec23dfa88eeb5acca939c2d2/pydantic_core-2.50.2-cp314-cp314-win_arm64.whl", hash = "sha256:6e731bf5a5e8e25398544d7709e39068ec4230b0177e4db7fa955692fd415ba3", size = 2160456, upload-time = "2026-10-29T16:33:19.782Z" },
- { url = "https://files.pythonhosted.org/packages/84/2f/4c8f67efe62837a508b74cf910904f73029cb211371351c5ceb36d56e85e/pydantic_core-2.50.2-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:33c6800ab9563adaa0d7893e9cd8f5b972ef1e6a23df70498e05c24f3fcec5e1", size = 2217558, upload-time = "2026-10-29T16:33:21.323Z" },
- { url = "https://files.pythonhosted.org/packages/ce/73/e0ef88cfbcca5409f57751289bc20be63a58af596a36e94b8cd795e59747/pydantic_core-2.50.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:f81df206b937d0dca5efb38c25fc99037d82dbcd3740e34c3f81997d2322ac1c", size = 2074335, upload-time = "2026-10-29T16:33:22.658Z" },
- { url = "https://files.pythonhosted.org/packages/7b/cc/47844a3179ebce27064e13619594b3fc1a9b4509d8dec1e3960c5f87f5bb/pydantic_core-2.50.2-cp314-cp314t-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:21ea3b00f1e87c66f3f9e92f9f6fec20495ac0e60d7d2e68ba1fc49739d7cf40", size = 2264201, upload-time = "2026-10-29T16:33:24.953Z" },
- { url = "https://files.pythonhosted.org/packages/bc/d1/30e9d3c86d81884ee3199560ddfa2c1a66a8dc0736441748eae43064abbc/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:9eff570402175c7d5fff8497d5a9c05580664a79bb66c07b8f205d17f69e580b", size = 2094353, upload-time = "2026-10-29T16:33:26.739Z" },
- { url = "https://files.pythonhosted.org/packages/d4/8f/392a5999480d0bcab4539db0dafa93265152396fd63e87be4d24e13d6d82/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:240fec32a74af0979f7253532859b3fd209ad5eea822416bb010929dba445c3d", size = 2203119, upload-time = "2026-10-29T16:33:28.154Z" },
- { url = "https://files.pythonhosted.org/packages/cb/80/6e4e276a3fde5500224a39150e06a4f134f136165908be69402720718a36/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92acf02c770b9d5c4e9a3e8d64e9d95ae26456a86ebb7b7cf64a97f899e5871b", size = 2392881, upload-time = "2026-10-29T16:33:29.305Z" },
- { url = "https://files.pythonhosted.org/packages/70/d5/5eb56c6317ab4abf33ef9519a50bfac0c96f7f408bad1bc36da6f27d43e4/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:1ac5b99bc7b0a68ec64f5f75a6828a7bab5535c8df0cdc07901deb50e0815910", size = 2457290, upload-time = "2026-10-29T16:33:30.587Z" },
- { url = "https://files.pythonhosted.org/packages/7b/9e/f1fe9384bb37e4e728c51c79151808deacf7b2502e245f50b57c85eeb199/pydantic_core-2.50.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:713d7c33c0e231e3786eb0ec95885814dd65d5f3a09754399b6acbf5e9365f11", size = 2241568, upload-time = "2026-10-29T16:33:32.04Z" },
- { url = "https://files.pythonhosted.org/packages/9d/f1/cffc4f8456c35690bcb65a8f2304afbf2f519766345a28aba553d5a55cc8/pydantic_core-2.50.2-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:61e13100b30d8d25fc0d1f0297014b32941bf0975a3584bd08f3adebb68d1ead", size = 2257185, upload-time = "2026-10-29T16:33:33.36Z" },
- { url = "https://files.pythonhosted.org/packages/84/34/ccd4df1cc39cb1e42fb7ec86a0dc0a23e8479de04d3012ca6b0bf2279a03/pydantic_core-2.50.2-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:523fe6574aea50f456cbc301c1cdddcbf237939f7657f9af99b4f0f71942b59c", size = 2299384, upload-time = "2026-10-29T16:33:34.799Z" },
- { url = "https://files.pythonhosted.org/packages/83/60/404b4b0c416a61867ee642cdc1450bca4d8a9a10e5f5f21e1ecaad05fdc8/pydantic_core-2.50.2-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:6c25b8826be85714b27dafe908d3461e1b09e3e31c0279a7029040c832c781ef", size = 2479384, upload-time = "2026-10-29T16:33:36.106Z" },
- { url = "https://files.pythonhosted.org/packages/ca/0d/57c81418a2fdfde0578c71396df773965adebd4421d9985a0f8dcbe9ec4d/pydantic_core-2.50.2-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:a59db03b80a156a6fbeae41096866d1cb89cdf31031ba44c768a804538880636", size = 2504616, upload-time = "2026-10-29T16:33:37.423Z" },
- { url = "https://files.pythonhosted.org/packages/08/66/dec78e28a604f3f4985f5a795d4da3e5fef78b0921aeb7f614ed7c780a32/pydantic_core-2.50.2-cp314-cp314t-win32.whl", hash = "sha256:0d090ae76d5b09c003600c7779b3adcf3d1b49268f616c5c51d844e54c8e318d", size = 2093179, upload-time = "2026-10-29T16:33:38.705Z" },
- { url = "https://files.pythonhosted.org/packages/d2/ea/79d9ee60648463804145d44b08f1e7e2474427d9704e3331d814483908b3/pydantic_core-2.50.2-cp314-cp314t-win_amd64.whl", hash = "sha256:9b01a092955c1c7530196ad526755cff4f2d11f37ca9a6fc9ebe37ca49a48977", size = 2220575, upload-time = "2026-10-29T16:33:39.929Z" },
- { url = "https://files.pythonhosted.org/packages/eb/01/aa6a7bd017c3228a41942da4fd788b86fb4aad9677212a4737a0c157fe74/pydantic_core-2.50.2-cp314-cp314t-win_arm64.whl", hash = "sha256:4ea01f30e119f1ec7a2034e85c40f491d469896aef02cd6436fd9292fe1be3fb", size = 2160866, upload-time = "2026-10-29T16:33:41.237Z" },
- { url = "https://files.pythonhosted.org/packages/4b/7f/ec2974015b4be5e50266ce732b8942d3d0b3bff751ace8900999c6577bce/pydantic_core-2.50.2-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:a3317630e2791f56071b82f89bb71624146125e4311e6643f708c6ba744b5ac5", size = 2230936, upload-time = "2026-10-29T16:33:43.656Z" },
- { url = "https://files.pythonhosted.org/packages/ae/ff/af99a09eddc348c1d0744c0df28dfcd4839ca497067f0fa2168208b41b1b/pydantic_core-2.50.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:f73930dacad5e611fe019e18df6f53c30629b1843d038d303b8dec86a32493bd", size = 2100409, upload-time = "2026-10-29T16:33:44.946Z" },
- { url = "https://files.pythonhosted.org/packages/84/75/8db3c46f02d6005277e5c2ff5933c85e7e1eb8798aa334c676c828650c3f/pydantic_core-2.50.2-cp315-cp315-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:e2e6240a0a5ad847017b3d65187877fdb7a11d5a58d43c30736f9f523478913b", size = 2259728, upload-time = "2026-10-29T16:33:46.461Z" },
- { url = "https://files.pythonhosted.org/packages/ee/9d/98c8d524921ca24e5c8a81a4b8f12209ac6ab05d0dd58b374e357da0ffc5/pydantic_core-2.50.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:1fdf5b52ed22f1404c2433d0b46df2d86d9befb742d0b9b4042a2325c43cdb97", size = 2121771, upload-time = "2026-10-29T16:33:47.952Z" },
- { url = "https://files.pythonhosted.org/packages/c4/ea/68c3b1fa919946012c940dea88e94e33e7dde46abe3e3d57053793c9bab8/pydantic_core-2.50.2-cp315-cp315-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:4e9392ddf67fe97ab9889f796d468c9908a8754b8b6c7520eb03a65012b9a1e7", size = 2196328, upload-time = "2026-10-29T16:33:49.131Z" },
- { url = "https://files.pythonhosted.org/packages/eb/9b/cfb6312a5b65b02eab6bc00fb0d258d0e2e49468dab29b430e7cb49b2ceb/pydantic_core-2.50.2-cp315-cp315-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:48e944091b7bff233920bed8a60b0ed6c96c73d3c5836bf51c4b9a4bfb58e4ca", size = 2380152, upload-time = "2026-10-29T16:33:50.503Z" },
- { url = "https://files.pythonhosted.org/packages/41/5a/7fcced1d84ea294200abfa93e488f36d00732101d17bf5e1f1389e66de26/pydantic_core-2.50.2-cp315-cp315-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:91c92856b846aa690ea6239ffd6c5003c5e3e296a69095570bf4efee5bfa0bdc", size = 2462299, upload-time = "2026-10-29T16:33:51.884Z" },
- { url = "https://files.pythonhosted.org/packages/e6/d0/dd0e355694094f0045922f4da60ac9bc7a314babc494e46089fb00640fd3/pydantic_core-2.50.2-cp315-cp315-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:5299296ff5cafcb69851fd652de8f9268a0bb036bcbedbfcaa94cf743baf3986", size = 2249999, upload-time = "2026-10-29T16:33:53.333Z" },
- { url = "https://files.pythonhosted.org/packages/3a/79/f5f4b36434604d22aacb7152667f87a470e68847c89b136c68a278cf409d/pydantic_core-2.50.2-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:d3d049b557875f9d15230eec8a731749a7a873d8ed6a051903b05bdf46e0e65f", size = 2262745, upload-time = "2026-10-29T16:33:54.79Z" },
- { url = "https://files.pythonhosted.org/packages/14/21/a4c47a286745857758a4bb8d4d5e260c736a7bd6fab5da618f5711d1a3f1/pydantic_core-2.50.2-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:b0b25d647036972c2951059046cb498f72b7af5a2def27bf8e5f44c3067ab7dd", size = 2293663, upload-time = "2026-10-29T16:33:56.132Z" },
- { url = "https://files.pythonhosted.org/packages/0d/db/25330e609fd219ce16d0a089af1b4b62095f01595dbe6972f7a7dfc313b9/pydantic_core-2.50.2-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:8835566b3e94026da3ce2249f9ee35c9d2ebe4cd131f6a39bfc82708d3010a46", size = 2473247, upload-time = "2026-10-29T16:33:57.607Z" },
- { url = "https://files.pythonhosted.org/packages/53/16/704ca88b82156dc485da2dfb11d5d52a10b9ef15a294639c0aa5ebd1643e/pydantic_core-2.50.2-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:3fbcd402e4b607d4b8de5d4591215f31f042af03f9d09e58f693a6221e32833f", size = 2490536, upload-time = "2026-10-29T16:33:59.297Z" },
- { url = "https://files.pythonhosted.org/packages/50/34/1fdd0eebee9f8b6c4ec3c2490f49dc59a652c8c5a7fa0cbcaada0d91cda9/pydantic_core-2.50.2-cp315-cp315-win32.whl", hash = "sha256:8431ecb131489cbdaf6f51f7ade47475f73aff4f1912552f75ba19a6cfe233a8", size = 2117204, upload-time = "2026-10-29T16:34:00.514Z" },
- { url = "https://files.pythonhosted.org/packages/24/6b/702adc91486fb96ba55675d58ee15ddc38fb48553fb35e26fd005e303206/pydantic_core-2.50.2-cp315-cp315-win_amd64.whl", hash = "sha256:04cf672960b8c4fc28bb159c8cdd101fbd311c520237a5c0360448b851adfb41", size = 2261064, upload-time = "2026-10-29T16:34:01.773Z" },
- { url = "https://files.pythonhosted.org/packages/36/0d/7f117432b8614526aa0d96fadd2b7fb7393156ddf2caf26a6fe606e6cfb6/pydantic_core-2.50.2-cp315-cp315-win_arm64.whl", hash = "sha256:36917e29c0b815d6b940bc91560fcf453b5b85d55c579796373f0ea11d1c111a", size = 2161783, upload-time = "2026-10-29T16:34:02.966Z" },
- { url = "https://files.pythonhosted.org/packages/45/1c/2ba7a34fe8da4c83ec200fb9f85ab4d54b961be08367922743623f987849/pydantic_core-2.50.2-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:bd1fe69056a19c377a6c6de2c1240fb8676ba0512a969965efb486510c5e0f1f", size = 2223184, upload-time = "2026-10-29T16:34:04.214Z" },
- { url = "https://files.pythonhosted.org/packages/47/01/f71a8da1f53a5be1787d2a97ba6fd9adba115305c7f15ea0bf733ed8336c/pydantic_core-2.50.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:9b2383933b1dd7a8c93e02f99ebfd305f1286057172483b8e6898bed3fef69d3", size = 2078174, upload-time = "2026-10-29T16:34:05.426Z" },
- { url = "https://files.pythonhosted.org/packages/f6/0a/847e1f5427372692f6eb5c38261fed6b8cedf379e37d9f6134d48b9c8ee9/pydantic_core-2.50.2-cp315-cp315t-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:a1b4d60bad57d6e0e7cd30cc4d1dc646d258dd6dfb7935ce76c7dd72dd1e98b0", size = 2268813, upload-time = "2026-10-29T16:34:06.733Z" },
- { url = "https://files.pythonhosted.org/packages/f5/bb/12966dc82b5e9c347981658b52e24349b6242902b644725c00ee7bdf4267/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:93bedc68c4138088ea3c042189c01d2945f530f0dcc36e1ea02df8418bd4f1cd", size = 2098331, upload-time = "2026-10-29T16:34:08.69Z" },
- { url = "https://files.pythonhosted.org/packages/d9/fe/340416b7ebe664524e7ada16ad2e3e069888b168010d75bda6a502bf2bf8/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_armv7l.manylinux_2_17_armv7l.whl", hash = "sha256:d684ff87d00de2f6abcbbfa9b0fdce8b513dd799a900809dcfa7eb62ad69832a", size = 2197432, upload-time = "2026-10-29T16:34:10.091Z" },
- { url = "https://files.pythonhosted.org/packages/c9/4d/425162c3d88af0d190c4dc4f940402cc6dd44d934359a37f50da6b7e4672/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:bb6d49f00e2babd40af4889d6b9f4bf50879f522dd4e2e22cb705cac5e0c5e8e", size = 2399300, upload-time = "2026-10-29T16:34:11.462Z" },
- { url = "https://files.pythonhosted.org/packages/2b/fe/2882bddf2e2038c8f41fa69f16ddeb6eeb1c002cc0cc0eb2157f73bb34e9/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:6d47b2d0e00e030e0ebdb08115a37da7294e7a6d196d134a77ed7ee6c142284b", size = 2462672, upload-time = "2026-10-29T16:34:12.79Z" },
- { url = "https://files.pythonhosted.org/packages/6e/ac/d7e6bfc6a95573647d1a699e19228dd99914ae6aab79a2bec293a038e980/pydantic_core-2.50.2-cp315-cp315t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:95838be125133fa314753f8cc5168f7c41e3a9dd5eef5383dbbbeed511bd4853", size = 2246293, upload-time = "2026-10-29T16:34:14.065Z" },
- { url = "https://files.pythonhosted.org/packages/74/64/97473ccaeb4ae5eb338162bd8479af121484e67f7078499730e3ee0a5592/pydantic_core-2.50.2-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:2d4a043c820363359e40ac96270e1915e5376e116be1f59cee0701775de3e376", size = 2259507, upload-time = "2026-10-29T16:34:15.391Z" },
- { url = "https://files.pythonhosted.org/packages/48/15/78a9b7a6a0d0679ce1e46cdd38ff95afd18dd2d6a01ed87c80a01bc003f7/pydantic_core-2.50.2-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:57bccd3e1128736c792094da1feac6da46ec3feb1957ef649a9b3e4f8de6fa04", size = 2303550, upload-time = "2026-10-29T16:34:17.162Z" },
- { url = "https://files.pythonhosted.org/packages/39/ba/b697173f82e99cdcf5ec6654b2edb66a00f2f92ef9d9a168861fca3fcded/pydantic_core-2.50.2-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:24120c644322a35b5eb765416ac8442214c45689e255f5fc6e761e7e7c743c8b", size = 2474919, upload-time = "2026-10-29T16:34:18.841Z" },
- { url = "https://files.pythonhosted.org/packages/71/10/986be76723784ae87f9664658fae39df340eaa21681cea95b6339d2e1b3e/pydantic_core-2.50.2-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:b1adf2f37521b578c438f6ec61d1c1d0c20b076863ddd5d8b6f3ab658857db3f", size = 2509095, upload-time = "2026-10-29T16:34:20.16Z" },
- { url = "https://files.pythonhosted.org/packages/5f/0b/38dbd38c67de7642f87348da70122b89ca4c5fe8227cfdd5cc00c1cdb454/pydantic_core-2.50.2-cp315-cp315t-win32.whl", hash = "sha256:d79aaa468193c1bed6b0560d3c5c9f9e369e3efb0b128b38a2537e2d51d42773", size = 2096972, upload-time = "2026-10-29T16:34:21.715Z" },
- { url = "https://files.pythonhosted.org/packages/7a/df/154baf8413b08d6daf2f5dec211c303ec6bed50c032717f354f8dd85c994/pydantic_core-2.50.2-cp315-cp315t-win_amd64.whl", hash = "sha256:cc488daac19e6f5292180fac5e0ef7efffabeef4e096af9a17a75a7d3d6781f6", size = 2226659, upload-time = "2026-10-29T16:34:23.177Z" },
- { url = "https://files.pythonhosted.org/packages/b5/17/eea7a1e7d98a79ef11b6d31748b70c58f8723f87d7ee42edbb6a101f6245/pydantic_core-2.50.2-cp315-cp315t-win_arm64.whl", hash = "sha256:91315aea7adb51821d4a3378ede953bfdd76e3bf71551de4a82d7638cef7dc02", size = 2166387, upload-time = "2026-10-29T16:34:24.433Z" },
- { url = "https://files.pythonhosted.org/packages/f6/82/ec0384d2a74415aeef7fcd032d29af84a98da0ca25cfb4937b10a2ac78eb/pydantic_core-2.50.2-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:adec010c824290d4ca87047e8d8bfa4f85ac29f545f326cd5321696805d311ed", size = 2154528, upload-time = "2026-10-29T16:34:28.022Z" },
- { url = "https://files.pythonhosted.org/packages/8c/b4/46483477921934aa31eec9cb86ba95d2f620f4c71da749024c2e3b6e8203/pydantic_core-2.50.2-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:f47c1349c3b4274aeb18459d54244881a5c0dee0fdccedffca45704ba5c9e0a0", size = 2072173, upload-time = "2026-10-29T16:34:29.771Z" },
- { url = "https://files.pythonhosted.org/packages/4a/16/c1eb6bdd67c9d5f42b290abb86948d8de288bdf4aeca06c588c3cfe6100b/pydantic_core-2.50.2-graalpy311-graalpy242_311_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:4ee64baeab403b9de9c92846c5c1abec28ffb046cbc023bfae059897841f88af", size = 2116695, upload-time = "2026-10-29T16:34:31.103Z" },
- { url = "https://files.pythonhosted.org/packages/ae/67/ccd8294730d9335c687facbdfd77515fa3b323e9bbb042c5d63addc6f5f8/pydantic_core-2.50.2-graalpy311-graalpy242_311_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:16ec8e07c37f02dcf68939cc7f202ba9a148cc155ee2acf22d337e10b24c2cb7", size = 2263561, upload-time = "2026-10-29T16:34:32.901Z" },
- { url = "https://files.pythonhosted.org/packages/64/4d/23560b11366b547e3e68a3b8f9c02557cee707effa54fd39dbb3c4fff1fa/pydantic_core-2.50.2-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:d18d56c1ec63b304f4bb3c7db7e0d311e61cca1b1c944590a4b13ac39b2ca92b", size = 2204868, upload-time = "2026-10-29T16:34:34.745Z" },
- { url = "https://files.pythonhosted.org/packages/06/58/b9ddfaad189143d6568627166a730b6624ca90bd9a9a4af4854df88d444b/pydantic_core-2.50.2-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:41ed8abeffda6a880207fdc0cd1343a448cec3cc6b38b53f730c0745f745292d", size = 2049162, upload-time = "2026-10-29T16:34:36.186Z" },
- { url = "https://files.pythonhosted.org/packages/8b/20/99d259bf7835b2f13c6d0b3eb7bcf669fb6349986736fa7a8308a9e620b0/pydantic_core-2.50.2-graalpy312-graalpy250_312_native-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:b7abcfaf66c1dc14c5ed928c349614f223c972a8b021b4be9548dbea6c817371", size = 2112135, upload-time = "2026-10-29T16:34:37.777Z" },
- { url = "https://files.pythonhosted.org/packages/9b/ca/652c8fe599abacafd852479059d209e608596ad3de82f1bff22e9f0f02fe/pydantic_core-2.50.2-graalpy312-graalpy250_312_native-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9e7460abce637f90e37f8c443559fbd687c38884d05cb69ef265e634945f4324", size = 2270339, upload-time = "2026-10-29T16:34:39.039Z" },
- { url = "https://files.pythonhosted.org/packages/18/9b/d240307fd6882a93a88bf038ff07e1a6dcac0e88f41951fe92c9b614f0b6/pydantic_core-2.50.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b583ddeee6a6bb5bbc04462167c68034913221139d52d9f155ca6d1637a2fdcf", size = 2157909, upload-time = "2026-10-29T16:34:40.355Z" },
- { url = "https://files.pythonhosted.org/packages/30/e5/d336fb654f8f1584b4a20d71f92c2a966419bc5eafffd36848d8cb1a916d/pydantic_core-2.50.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:5ab5738c39b25b5dc5a721270e2269a2f5e86e614375e6c07ee6caab8cbc0f2f", size = 2075773, upload-time = "2026-10-29T16:34:41.761Z" },
- { url = "https://files.pythonhosted.org/packages/38/08/18203f64a405f11c3e4d569c9f49ad7a0b5901cd5fa2dff2436d28531c60/pydantic_core-2.50.2-pp311-pypy311_pp73-manylinux1_i686.manylinux_2_5_i686.whl", hash = "sha256:21a902e546032c82b7275552345a2342d862e0541aea7226c91067c396e68b3f", size = 2277290, upload-time = "2026-10-29T16:34:42.952Z" },
- { url = "https://files.pythonhosted.org/packages/9e/c4/a4e0ae45ad1554c38caa93e200fdd08e74d2c0149b4242c78b2475903379/pydantic_core-2.50.2-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:92ada606285dab054e40848a951f583c537cde1e54701ea1305ec0096877012d", size = 2265597, upload-time = "2026-10-29T16:34:44.244Z" },
- { url = "https://files.pythonhosted.org/packages/99/20/304baf7724ac137b921ce793e4b686cf4cddc431da4888e5419e7bc7374f/pydantic_core-2.50.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:c1b65a42afc73b39e6ae908ec7b9c3b9898676a7b15ce4c66d4965f7071007c8", size = 2289092, upload-time = "2026-10-29T16:34:45.55Z" },
- { url = "https://files.pythonhosted.org/packages/77/6f/0fe5ac160718d75f2f9729bb31aa9a349dadd0d32886b8c0ecc253629c46/pydantic_core-2.50.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:c324a7e0c0012e3974053f81a270d8f1aa23227571f4ddd3254c911bb356459b", size = 2458417, upload-time = "2026-10-29T16:34:46.827Z" },
- { url = "https://files.pythonhosted.org/packages/43/44/042cbd7e46f00f45aea36496ca8bdb6b1136c2ec4063875a25286c2ba8fc/pydantic_core-2.50.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:cb8d2d7b07cbe2d7488de7e53671f836c4f4a8032dcda079df33dc1b7e2397c0", size = 2492296, upload-time = "2026-10-29T16:34:48.374Z" },
- { url = "https://files.pythonhosted.org/packages/cb/8e/e8aef16e587215a1795dbe991e41006003aef1f1b6f554361f3a6edd585a/pydantic_core-2.50.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7c6e1bd6cadae13958e52897ef020e85c9916a95917149fca463bb0c0421a7c6", size = 2323090, upload-time = "2026-10-29T16:34:50.008Z" },
- { url = "https://files.pythonhosted.org/packages/21/36/d083f973e28c5718f9d9d819c0b8171df1526124e8c3709bd55ddf1d4244/pydantic_core-2.50.2-pp311-pypy311_pp80-macosx_10_12_x86_64.whl", hash = "sha256:f078e276848bc23d1f92f46fc88d5b9cbb824b9acc60d9e07116620e502e16a9", size = 2157909, upload-time = "2026-10-29T16:34:51.579Z" },
- { url = "https://files.pythonhosted.org/packages/0c/4b/e2eb5b51f46411ceb80451a7bf4aee215fd84012eeba22a183b4699006b5/pydantic_core-2.50.2-pp311-pypy311_pp80-macosx_11_0_arm64.whl", hash = "sha256:3435c443da7510a6de1a30c68beae338852a11f0440cc8320ea756a162f38dbe", size = 2075771, upload-time = "2026-10-29T16:34:53.469Z" },
- { url = "https://files.pythonhosted.org/packages/d5/99/e9703785468d1e644f19d602985b722ff97cc8ca67389e2d79f8fb562cfa/pydantic_core-2.50.2-pp311-pypy311_pp80-manylinux_2_28_x86_64.whl", hash = "sha256:b5675a12a7b67063fe3ca187e735b0463cf13fabc10166ae4d4b2f775fd0632d", size = 2265708, upload-time = "2026-10-29T16:34:54.662Z" },
- { url = "https://files.pythonhosted.org/packages/bd/d7/079d3d36aa6ee0333847d968d91ff185837541741d3c58ed0316103309bf/pydantic_core-2.50.2-pp311-pypy311_pp80-win_amd64.whl", hash = "sha256:f5ba7ef10ce82737e173fa95cafa1c8210918f7e653715b2c7a7a31877a74848", size = 2323087, upload-time = "2026-10-29T16:34:56.176Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/a6/24/af4ec4be49fbc810f35b0bdcacc3d433b56bbfa469fd3bfd4ae116cd9bf1/pydantic_core-2.50.1.tar.gz", hash = "sha256:e50d7b94baac6c7d09927fa5ca5800a0c7ee5015c7fcff65beb3a1931b5a6e09", size = 513515, upload-time = "2026-10-11T18:35:44.82Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/45/1b/ca81811da3d2aea724fb3c4ba6b83f50ab8f210576306ccd601973142d35/pydantic_core-2.50.1-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:c531166c42ea7bdfecc8c50049581f05dd1993b09cc7c52bb36a14e96deaec7d", size = 2152145, upload-time = "2026-10-11T18:31:58.046Z" },
+ { url = "https://files.pythonhosted.org/packages/59/20/938ef7e0541af0a314e2f9d63848dbd1656ba2ebfe5ca0469f7ebee00d5e/pydantic_core-2.50.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b6d0c2183008c188e19f4906d426b293bdc4f67ab17df8e180fe16cda208fa71", size = 2045761, upload-time = "2026-10-11T18:31:59.35Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/4b/456497fe2affa18f49b226e89bde31e8a204751ee343dd9beaac5438123e/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94be440c03fede26969a5ce75468e0e6a9927a1b46d9b679ee8adc1b057b0350", size = 2049557, upload-time = "2026-10-11T18:32:01.121Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/9d/cd5dfe13928f55e236dff3a1bee06721d318ca6c955d9ca993b0a5d09f39/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36c426eac0af8d1529ff8467e612b933346caec1fdc0d774f78f67a1a11e16c1", size = 2188153, upload-time = "2026-10-11T18:32:02.442Z" },
+ { url = "https://files.pythonhosted.org/packages/fb/8d/d0b25bc484f8d9136ea76ece69e0b2d24ab370cc2e7bbd1b79e61723b763/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:028e2f212273d4a39b1ec1e0de8166b1165a65fc0f1111452a9d94fc7c625c63", size = 2388308, upload-time = "2026-10-11T18:32:03.767Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/64/6513cac7c9f78492abafa01d57a69de1d246bf3085fd7b0330bf14b025b3/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e6f0cc1bb9900dc558960894adeb30b0c083366fc1d69b856209fb2ca5c36fe5", size = 2431187, upload-time = "2026-10-11T18:32:05.159Z" },
+ { url = "https://files.pythonhosted.org/packages/ed/60/7f2ae89c37e69a3dc0da9ff57b0388c285542c688d05f36767770429de85/pydantic_core-2.50.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8812592c85d0edf423f10eadcef42716d71e8219085ad9e85b775057b7306133", size = 2157623, upload-time = "2026-10-11T18:32:06.616Z" },
+ { url = "https://files.pythonhosted.org/packages/87/02/e3a71373b85a7973099b9a229a4302c7792262717e0b4997e41c01f7dd16/pydantic_core-2.50.1-cp311-cp311-manylinux_2_31_riscv64.whl", hash = "sha256:bbce99252ba3167b2b6277f1829d5bf4b43b754524bddf7f944707c3db7d2253", size = 2254539, upload-time = "2026-10-11T18:32:07.968Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/d5/5b620aaf40dc631d911565b64d49d3fa8422027c046a7a96dc4e4921ae98/pydantic_core-2.50.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:476f6ed8e43cd1e0b460920e23571700872b284e77331cb30c4faf459cf48a4b", size = 2267344, upload-time = "2026-10-11T18:32:09.399Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/48/f46baf4580d8a56ca4782aa65842e16d3cf39736b29fcce8e9c0521b3422/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:2cbd1b75b09e976ed0d6b6ca297675632ca35df86130088457cdc60ef36970ae", size = 2300877, upload-time = "2026-10-11T18:32:10.903Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/9d/e7d28505e6092c1fe075838a4f793ccbe6dd676e44ab16175815ee06cfe0/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:5958c72adb417c39b12ac87525ac60b0d73315fcdc59e21f44ee4a5e2512c9ef", size = 2464270, upload-time = "2026-10-11T18:32:12.329Z" },
+ { url = "https://files.pythonhosted.org/packages/22/b1/3bf63b82efd93ebc9968f9c12fc46037a28bcb452c88e4688f33e5ff7dca/pydantic_core-2.50.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:d8f9e8a6c4ab04b78d61f78627370d834eb004b2869dcb28cfffa647b4ea1980", size = 2478653, upload-time = "2026-10-11T18:32:13.735Z" },
+ { url = "https://files.pythonhosted.org/packages/28/17/26d1e4b9c2ba6a92b7dc75bbcb99ef44456b7e3af1bced868403667ea7d3/pydantic_core-2.50.1-cp311-cp311-win32.whl", hash = "sha256:4be846f55c9477f5f3ddde8f2ce941137e16862a56d018ed885d422bb6ae02f2", size = 2120246, upload-time = "2026-10-11T18:32:15.591Z" },
+ { url = "https://files.pythonhosted.org/packages/24/65/3e875e7991eaecd4d70686293ff0d8a99aa8f93dc295e6a21c3d702912fb/pydantic_core-2.50.1-cp311-cp311-win_amd64.whl", hash = "sha256:0048b6dddc8ef4b64fccaad878bd143b0c3882ea9936279dc11d613f6b7dd1bc", size = 2176332, upload-time = "2026-10-11T18:32:17.153Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/5c/58dbd17c61f209d91c70f549eb7a3bbec7023e89ea12dead091462d6d658/pydantic_core-2.50.1-cp311-cp311-win_arm64.whl", hash = "sha256:6a733778df2f7087ec1100ed0b41533e4f3001976e99570fa34f57c66e7f8e3e", size = 2182471, upload-time = "2026-10-11T18:32:18.49Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/94/101b49a6c63f99586755690696eae14532772587d9e39e93f7b8d28a49da/pydantic_core-2.50.1-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:704075d10b74f2f3c6e15407c696d88701df35fc8953f434a431add0d0074db0", size = 2222428, upload-time = "2026-10-11T18:32:20.228Z" },
+ { url = "https://files.pythonhosted.org/packages/db/a3/f8b1b39349c33037c73d7a1b794c9c29d8f07d5bd2b74399c1375a529cc6/pydantic_core-2.50.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e8e1d6ce820aa23317e8209a86bd65a540973c12dc7552b48a4f6c8e9926815e", size = 2025779, upload-time = "2026-10-11T18:32:21.723Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/2d/862a7d115305cc621358e51ba13f296e0ffdf4fbd984be9f63bc7862730b/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c18db21573bd2c6489f9a544b7499f0df2853958c568e5e783536ee1f690af41", size = 2048429, upload-time = "2026-10-11T18:32:23.3Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/7d/fed95d18bf2b5abe2d5ab2bbec4184bfc22b6805994a33ccdae8edffd25a/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:cb57f304525a5e3c13333b772bf9a473f36326e9c821b2e8e1b2fd36f80ae2c3", size = 2173274, upload-time = "2026-10-11T18:32:24.697Z" },
+ { url = "https://files.pythonhosted.org/packages/93/1e/c6f22ceac65dc3d291ff79bea0eb74cbd1de8b128f01fee48af03bcea1e1/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a27c09d86600f1bf2fe3f37e1ae697faf3143931c09322cd799da94deee923b5", size = 2360403, upload-time = "2026-10-11T18:32:26.281Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/97/190b5b6bfe4c72f265feb657913eb30749193927a5ad7b83cfa7866a25a2/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:46b3301d3b5c886f77de7546e47274a5842c622ea2020b8c6524c6b66913b4a6", size = 2439799, upload-time = "2026-10-11T18:32:27.844Z" },
+ { url = "https://files.pythonhosted.org/packages/90/24/48cd98388e4ac2c08d0800af36d15413db15e20387f1038123a2c86cb2f6/pydantic_core-2.50.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93ba4e9d8210d941c200431a56b2c0400b131865947903937ed3ec5404307d2e", size = 2160157, upload-time = "2026-10-11T18:32:29.553Z" },
+ { url = "https://files.pythonhosted.org/packages/00/86/d909133d0b9979ed14c20f110f21124a0e346caf071bd66ac5af3009062b/pydantic_core-2.50.1-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:e5faeaee74a57d32b3ab3aebad2e348f06d3ba946fc5d28c1728455f00a3d13a", size = 2229523, upload-time = "2026-10-11T18:32:31.027Z" },
+ { url = "https://files.pythonhosted.org/packages/90/31/4b43f08131b2098e73a852fd932063385f3103b33839fd9d1abdb89e673d/pydantic_core-2.50.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3cda0e538208e5d722bbf3698b24f19c0a7d05bc8d5f8a7f9b121ea7fa243d9", size = 2242700, upload-time = "2026-10-11T18:32:32.622Z" },
+ { url = "https://files.pythonhosted.org/packages/88/77/de39658569dc7a4c293d110a89629f61145e7538f00d1e4280c28debefd3/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:57f51b31ff826e2859120cf4737c5a758a48d96f3e97da40ccee1796d58078ff", size = 2278452, upload-time = "2026-10-11T18:32:34.165Z" },
+ { url = "https://files.pythonhosted.org/packages/46/bd/1eeb9e4e791e81210ef9096f58080b32ba15b5b13c108d50aaa3f8ce9f73/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8daa7ee75245d43ad7d747e5c9ecc1b1d06552f72b14887e9276f787d57375f4", size = 2450098, upload-time = "2026-10-11T18:32:35.547Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/e6/b2accfdff17e5a88d6358536702146de00f9339adaa4d5bbe1ee93cb4832/pydantic_core-2.50.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:acbf31f37c53a5ac0c34706c80b4f5107ba20b05fdd3816124bf236ef0c57dd2", size = 2476839, upload-time = "2026-10-11T18:32:37.119Z" },
+ { url = "https://files.pythonhosted.org/packages/3b/7e/c8a83eb5d2cd42c2e13507150fb777b1edea5a018adc9cbf2858a1d08285/pydantic_core-2.50.1-cp312-cp312-win32.whl", hash = "sha256:45b11cac094aa25725581d9304eee93c9028516b9ea80dd9e175e13a5a2c840e", size = 2101425, upload-time = "2026-10-11T18:32:38.593Z" },
+ { url = "https://files.pythonhosted.org/packages/c5/17/8bbd530b8659e9d963f5b16f6cb8e159f4cd174b4d6304e48969ab12dba8/pydantic_core-2.50.1-cp312-cp312-win_amd64.whl", hash = "sha256:132529c83901437ff642f585216831bf5fd7a91df66829907e155192ead62498", size = 2172522, upload-time = "2026-10-11T18:32:40.112Z" },
+ { url = "https://files.pythonhosted.org/packages/af/9f/a79d690b127e4e3cff143d031538ab9aa715f6b2fbeecbd07cd6f2bf9fb7/pydantic_core-2.50.1-cp312-cp312-win_arm64.whl", hash = "sha256:4e834f6a8e4ff772dcc34f58ef5504147a3ea5b0f4eeb13b0f8eb2ca75ac57f1", size = 2161366, upload-time = "2026-10-11T18:32:41.606Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/57/0e237d7091d2cd44a35d243b7344227f90440166860469cd036afc23a04d/pydantic_core-2.50.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:d5e062c01286d861fd6a1c4ff6e063547b3e713067f2df033c0ff97ac2ca006b", size = 2222774, upload-time = "2026-10-11T18:32:43.103Z" },
+ { url = "https://files.pythonhosted.org/packages/f7/b9/c720e56858d4e1539503297ed37063e0c08e0f3541c41b777f6a800f75fa/pydantic_core-2.50.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:0c003c3b7f49debb893d2d85ae099ac5959c9839e2f330fadb1fcdf7a6594482", size = 2024738, upload-time = "2026-10-11T18:32:44.587Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/b8/fbfc25875219cc060e613170ff10e850c6d8924beb4910da57c2ee3ba1d2/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:409e0ea40ec30d9158f33574fd758e689f6045a0f2596701828c27816ca9687d", size = 2047049, upload-time = "2026-10-11T18:32:46.164Z" },
+ { url = "https://files.pythonhosted.org/packages/d7/43/34210124d504c553688f2f04b48500b131237528ac545b445e0d6d30e0d5/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:131059670f1d2444269b8585cb888963994871932447c08b39ac6a51fcfef658", size = 2173415, upload-time = "2026-10-11T18:32:47.722Z" },
+ { url = "https://files.pythonhosted.org/packages/65/cf/6e178e8fdc11da5965bef980983bf46326a42f436871dd51ba0a57f39df1/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dbcbee53bf17196a7f745aa9bf5a9603953a1e365b1f020be3207c676a3e7c4", size = 2361719, upload-time = "2026-10-11T18:32:49.217Z" },
+ { url = "https://files.pythonhosted.org/packages/11/14/bd5169d356aa91bf777e28ba0b281c192d286d03c2812c9c9db023a2f00e/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:325c23f3e35cfbf0fe3486fa5f7260d1e45885173002d30a28ca019994124255", size = 2439803, upload-time = "2026-10-11T18:32:51.025Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/bc/d79d000e5203ebef39af839f2ce77a777fcad6e08ad26af9a2fcd114ffc8/pydantic_core-2.50.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:17e722e156d0444ecaefbe640bdb60928752bf2013e2b7a11cdb099aaae19bec", size = 2158402, upload-time = "2026-10-11T18:32:52.714Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/a5/4902cb5fd599422c130bd3124ab31ee8b771199bd56662702eed07d3fec7/pydantic_core-2.50.1-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:aa8224f10880d9bf1b5993988ba153d42a8b4f3f4f511f93b1f09c93ff613c72", size = 2229755, upload-time = "2026-10-11T18:32:54.126Z" },
+ { url = "https://files.pythonhosted.org/packages/1a/f5/c1481f8669f6060d89110c9b1374173fc8767ca276b84f4870bd8acd5e3f/pydantic_core-2.50.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:41bc8237121bd8dc8d888dfd6279fc166ffc88c1f1bf3a8bf00869680533ca4c", size = 2242303, upload-time = "2026-10-11T18:32:55.641Z" },
+ { url = "https://files.pythonhosted.org/packages/04/f9/77fc3c7653ba9b6e42049e17e96b25388187d4a7c274ab1cb07f58ac8419/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:45c6266d071c241f2a168d45bf8c54344f0effce35e7e6b73afdec11f3687568", size = 2278345, upload-time = "2026-10-11T18:32:57.38Z" },
+ { url = "https://files.pythonhosted.org/packages/95/9b/0579c5d12e7f2b16b27e6782427987341fcce07b0725e02ddb0b74add0c4/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:1deeacb112d14d3f4fcb16b165f7dbaf76c70ba6e82f37ba042bdab51970a0b8", size = 2449947, upload-time = "2026-10-11T18:32:58.896Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/ac/1b677db91eba54cc5922f4de6edc46ba45c2bd712dfdc0130382d158e214/pydantic_core-2.50.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c96fd793b73d1b92e65570132505498fe7b21eaef73cdf74e67e5dfba7ac9e4", size = 2476889, upload-time = "2026-10-11T18:33:00.665Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/2a/3a9f6624ee3ea9ccba5249dde11418bb4c35780a7a92608f8768bd3fea39/pydantic_core-2.50.1-cp313-cp313-win32.whl", hash = "sha256:06ead20d39ffd6f2f6f2a8f8a6de67ff8bb1b4f14a8a30e058502514ee2ac685", size = 2101118, upload-time = "2026-10-11T18:33:02.329Z" },
+ { url = "https://files.pythonhosted.org/packages/2d/1f/323f78ddd9d9938aac420c1abb4e8ba799fc8bdab0acc67c0593b837c979/pydantic_core-2.50.1-cp313-cp313-win_amd64.whl", hash = "sha256:7816e98acc08119dc0f340ab167048ecc54126316330c1f0caf7c6756c88e28f", size = 2170840, upload-time = "2026-10-11T18:33:03.919Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/09/8497c52a739ae425c3ac2f7f56414cbc711c67d374346174f40fe2062644/pydantic_core-2.50.1-cp313-cp313-win_arm64.whl", hash = "sha256:c17799a62c142d61b8a3c51752a7cbc87fe2ad4ccfab10e628a77b405075c662", size = 2161633, upload-time = "2026-10-11T18:33:05.518Z" },
+ { url = "https://files.pythonhosted.org/packages/49/33/28b96e81677153715e3eafb9f26663a80841e859fde282a380359b0d3fa1/pydantic_core-2.50.1-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:1cf41f1ae3fa155cf167a72689ad044bcc1e3c97e064123677149bdfb5dafc4a", size = 2224428, upload-time = "2026-10-11T18:33:07.153Z" },
+ { url = "https://files.pythonhosted.org/packages/94/40/15c06410c9b7b8da5805d27b64e09bd3f900e986728106db2689dbc51513/pydantic_core-2.50.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:4df197990c15b5a37c5a277d131d9f2c67de6133f2e5dafd80d9bba4b99f46f9", size = 2023740, upload-time = "2026-10-11T18:33:08.763Z" },
+ { url = "https://files.pythonhosted.org/packages/a1/4e/5eb629f6efc2a27e421d789dd4bfacbb5f6d09c80c28b13d1e87d73163d5/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0036473f5583e6a60e50b8b21651511564277a3f05cc5dab8cf579f552cd5f6c", size = 2049435, upload-time = "2026-10-11T18:33:10.366Z" },
+ { url = "https://files.pythonhosted.org/packages/ba/8e/f195aebec49ad12318876ac2368c197a7939f5d52f8e156d2238ef8a4588/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:992c3514ec891fa7858099183e4d64e6bd5a5d4ff452fae29df22faa77a006bb", size = 2172975, upload-time = "2026-10-11T18:33:12.368Z" },
+ { url = "https://files.pythonhosted.org/packages/e0/f5/7ad9fb83010cd5ea0948409db105676ed779c4e709e2d3d89b9fe2558794/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:739dc730e6be3bd5ec2f4ab5cfc7eb047cc45fc1497b3bafec74ff2ed07df597", size = 2364197, upload-time = "2026-10-11T18:33:14.244Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/fb/bf0aab3e78301d202b82a0322ec968cd703b11fc0623fde9a28708936f62/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:32fad3a91e51b6d2039c572db04a5a873260b399f6bd62c3552671fa7a4a2899", size = 2444259, upload-time = "2026-10-11T18:33:15.79Z" },
+ { url = "https://files.pythonhosted.org/packages/45/35/38f6d6564fae57d9b12e5676dfa947e0e7d5c46dbeaa7eb3352261d6d299/pydantic_core-2.50.1-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42b54c2c90ad348b5e3a85e03e715d572c1fde357ef104cdfe3b03b697a404ea", size = 2159857, upload-time = "2026-10-11T18:33:17.51Z" },
+ { url = "https://files.pythonhosted.org/packages/65/a0/fb0a3ca10f139dcf765b2d312d10cf0f65c57c59993229d00ad12b14ceff/pydantic_core-2.50.1-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:2df1ff41884de2bc4b307bafd7c40a691094fad2ff8e767e5b45a319257bcf4e", size = 2233954, upload-time = "2026-10-11T18:33:19.591Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/6a/e63842252702aa4ec6e6b7178ca85e541a77459076592c23ebe2d9840b33/pydantic_core-2.50.1-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fe90228920fd8ff2be62622b6bb8a2b11acd65046d50c6b130614b5879605a20", size = 2243876, upload-time = "2026-10-11T18:33:21.393Z" },
+ { url = "https://files.pythonhosted.org/packages/39/25/5991cf8318b37e0dfab47b87541619a1df8501a793cba0d978846cba37a7/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:844b869f118e22a41a091bdcedda8a71bc1b0f62c38d1a0c3211cece47e1d8fc", size = 2277908, upload-time = "2026-10-11T18:33:23.324Z" },
+ { url = "https://files.pythonhosted.org/packages/a7/3e/3ee8baaa6cc25a6961c69168cf9ff0f002d56f4e0d541ad6724c18fb61f3/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:2eb75304506894a281d346220a4f7481a1b8729577c5ed2a05395991966a8396", size = 2449285, upload-time = "2026-10-11T18:33:24.942Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/c7/acbec6deac13fe697a80c275a9b6661db62c4d323bac8a47347b1f39c7cd/pydantic_core-2.50.1-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:6b20a4bffabdad0db2927ac034ae3b8a681b1f7a0182f3e60b479ad2fde21ebb", size = 2479013, upload-time = "2026-10-11T18:33:26.925Z" },
+ { url = "https://files.pythonhosted.org/packages/10/08/21a3f237b264389f6219d053ee78fc1b5c2fd402c1b1cb2b6cb8b85f9834/pydantic_core-2.50.1-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:99ba9bc2b8062ea0c326a990f7f00e6530c23579de66dd246e72c4cafef950a5", size = 1484400, upload-time = "2026-10-11T18:33:28.693Z" },
+ { url = "https://files.pythonhosted.org/packages/09/ce/077a6d262d12ef09108377ac0717f029f420d773ace63cafd6143af75568/pydantic_core-2.50.1-cp314-cp314-win32.whl", hash = "sha256:cf356f70551d40374eaffb1aa63f1eb6d2006681cbd7a9faea173ce0f4dd7cd2", size = 2103222, upload-time = "2026-10-11T18:33:30.326Z" },
+ { url = "https://files.pythonhosted.org/packages/14/4c/350a2415209c43d670eb71d3c040f332c04a30583d39e311b05c7ac15762/pydantic_core-2.50.1-cp314-cp314-win_amd64.whl", hash = "sha256:d32f3acc081cc3923386d88f422cde8892335e95f034e0104bb4cf9310d9915f", size = 2171586, upload-time = "2026-10-11T18:33:32.139Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/92/9bea6ca96580a0902fed366f064f0829e404f41889b34543279a1162b888/pydantic_core-2.50.1-cp314-cp314-win_arm64.whl", hash = "sha256:bed5163e03b98bc1fa2eb05d74c63d9c5c95d8ed6254985481640fbf5e237dea", size = 2155773, upload-time = "2026-10-11T18:33:33.896Z" },
+ { url = "https://files.pythonhosted.org/packages/86/8c/f121f073cf32bdd5cba7e6230b1ce0ac845b0cf43e44dd597d95af272db2/pydantic_core-2.50.1-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:9572c1369e9c9da2d64a7b7992c786d90ff295abc93964cfe3125e4290768070", size = 2234017, upload-time = "2026-10-11T18:33:35.588Z" },
+ { url = "https://files.pythonhosted.org/packages/a4/69/2bb2bcd6146cffe98d760a46c42ae71efd5151d9b2f9c9bf6619a3b32083/pydantic_core-2.50.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:2005207aafe1231315718bf6ed5d064a7300fb4772754af35ee72fc68159492e", size = 2001016, upload-time = "2026-10-11T18:33:37.57Z" },
+ { url = "https://files.pythonhosted.org/packages/20/b3/fbf854c7d07ec114260c26e9e2071a4381740f9ae09641dbfcbdf2a18c45/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64f6047f62a6c5ae08d0a6afb035667aa2d97c3d20d69762e034c5ea144d92a5", size = 2023015, upload-time = "2026-10-11T18:33:39.433Z" },
+ { url = "https://files.pythonhosted.org/packages/65/20/6de55b2f92cdb614b745c6e9ced639fc4fd7e1e77604825a88bdece6fcbd/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1ef800dd7d85bcdadf4c3076e4c94e43939493558a3b69a1ea830c706d4617bb", size = 2192543, upload-time = "2026-10-11T18:33:41.381Z" },
+ { url = "https://files.pythonhosted.org/packages/0c/d9/19e91c94bd5c405945ce2f15526808aea37e162c253160de8ed7bf70b406/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b0135bcdcaa0f23573f286e4cb5e0fd2962700964ed13df085b85f2b97aeab9e", size = 2388704, upload-time = "2026-10-11T18:33:43.167Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/bc/2e24c8415eae1a25ee5a5484946a917123bad2e9d01689a759236928175a/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0b3a6f334c6a2345ca15318ff894502a90012536404b37c844a976c76c846e0b", size = 2464118, upload-time = "2026-10-11T18:33:44.856Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/1f/945b8053cb061c64e102bcaf7bfb9ed740c0bd4349f9bb978a7d4ddff4ab/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06e01fbbfdb9be777b316a71b6c49efaf4a08b615d0a98d678cda3023f79d019", size = 2150142, upload-time = "2026-10-11T18:33:46.661Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/78/96a3e50bf0d64aaae781107eb9335b7529d05a85793ed6e7241c4cd1d931/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:a29a061fec0b4e2d714f277e70a3a18125ecff803f2fea6eade2f2e53711d112", size = 2245577, upload-time = "2026-10-11T18:33:48.574Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/5d/a6038a0322232758a6ebfa709f4ca14a60bf5b93940cd0b345056a557da4/pydantic_core-2.50.1-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f5187624823423e1d1b82b1072ac41dc837389e18d3d0572cc19bbee46cd550a", size = 2263728, upload-time = "2026-10-11T18:33:50.527Z" },
+ { url = "https://files.pythonhosted.org/packages/0d/4b/76ded3333a457a9344c4f2d63f2d82d0101654b05178fa4ddfe7d3627674/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:3e46a9eb0a0901dd6275e6b06ac3a464885ef350ec4121fe486869de8053e4bb", size = 2293630, upload-time = "2026-10-11T18:33:52.362Z" },
+ { url = "https://files.pythonhosted.org/packages/a9/00/9eca378335c9c1b72bc779bf6e4c2a82ce784f14ec48a0e87102c9870e05/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:756d669f04e62ec4148ecfe22be6a4484d9b1181a6ef32e205ebfd200540858b", size = 2468048, upload-time = "2026-10-11T18:33:54.118Z" },
+ { url = "https://files.pythonhosted.org/packages/35/ca/e3832e9cf93651251de43c8c5c029ed680a3c1a0a1b17ee26c81bed08cbd/pydantic_core-2.50.1-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:c516cc5367ca3448995d42cb994bf3f4c9002d2a7c22eac9622551269ad1b807", size = 2512159, upload-time = "2026-10-11T18:33:55.99Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/f2/773469b5a10a39116a2cb17edaf6d722f017dd8da07161b371465311c542/pydantic_core-2.50.1-cp314-cp314t-win32.whl", hash = "sha256:9d1bed94af6a63835461f3cf7502058eb166c58c4778e11d0f433cfb1bd69e19", size = 2098406, upload-time = "2026-10-11T18:33:58.043Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/42/0bb74f8f25204b259b11ab7c12dc7f180893b6bd706118b385147fb6efd5/pydantic_core-2.50.1-cp314-cp314t-win_amd64.whl", hash = "sha256:c8dce1f1e0e5358b682a6ad3fa5e31b31d4560997b8e61417e9217c8d60f8a0c", size = 2148481, upload-time = "2026-10-11T18:33:59.913Z" },
+ { url = "https://files.pythonhosted.org/packages/47/0d/d801646c9679a4e630e15cf521d4108b93854b42a6e6e02391bd4c6b1095/pydantic_core-2.50.1-cp314-cp314t-win_arm64.whl", hash = "sha256:ceff0acc940be2715bd6ad17b24c0e5304abf44f6efd0f81ee8499e640f9dc86", size = 2174972, upload-time = "2026-10-11T18:34:01.875Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/84/23984b763d8862a02a13d27a44b6e8169428fd85ecfed88f54c29108604d/pydantic_core-2.50.1-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:8a6791afa2245e6c6b180122d105941644f5bd410bb18623b408808cc41a3102", size = 2224599, upload-time = "2026-10-11T18:34:04.016Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/90/a63cf8586abc1d1a3f6d9b18f0224789ebf003851f092ebda3c7c863fd32/pydantic_core-2.50.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:84f34323a61a365b4e9295de6028474754829aaddd59c7bf1a040e7487ef8f3c", size = 2024191, upload-time = "2026-10-11T18:34:05.901Z" },
+ { url = "https://files.pythonhosted.org/packages/a6/6a/f34bff9808ffb4907fbf5f5040457d253cacf2da7fce9efbc99ca1b1a44d/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:23edad659e8dbd8ca7e4e877fe6c81573abbdf215bd25a68b53e1272f58b80c7", size = 2048252, upload-time = "2026-10-11T18:34:07.757Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/54/13f419bf1eb59852003818e25935aaf175687d978f4c4bca70e08fe40a3b/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a5fce22f1e87d181e924e12da7d81cfe031fb3881a5ddf26ad28f141756ca43", size = 2173376, upload-time = "2026-10-11T18:34:09.592Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/6c/b5a34d24cd0c81669d8f8339d74e6815abcf2f8fb48ab4b49b84c09be1d5/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c73622ef819328873b53109ee4f77ceb598bffedd02daf916102be3228866b78", size = 2364559, upload-time = "2026-10-11T18:34:11.534Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/8d/d64d6216a8df365082665927ff923f183056f9049fee08e9777c9ac05296/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ce8c25ca38cc0e3d7753ba180808de2c0c8cb24eae0df64491e40921454e9831", size = 2444697, upload-time = "2026-10-11T18:34:13.535Z" },
+ { url = "https://files.pythonhosted.org/packages/b8/0d/1b1149f60a00ea21ba5f70e28acbd40feb4598af414f80f53c921fac07c9/pydantic_core-2.50.1-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7689580e72a642ab5ec64d5f55b2e33636fa43b4ebe63c0c2c965ef307c7d1aa", size = 2159961, upload-time = "2026-10-11T18:34:15.56Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/35/f236549299dcc78e71e945495d7ec67e78d20844d0999c60601685003031/pydantic_core-2.50.1-cp315-cp315-manylinux_2_31_riscv64.whl", hash = "sha256:d5c0e32fdbce7f1e8ef4d11f655694bf5f4175c757a9f1dc2be09b8864e5bcf5", size = 2234581, upload-time = "2026-10-11T18:34:17.502Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/85/26901a490522b7f75ef9bb9a7afb73e5bb550f0e1cb5b99a0069183e2eb9/pydantic_core-2.50.1-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:40f523349960fa30f3ea51404308ff50f9997a90df639590f47a057c1f32b415", size = 2244090, upload-time = "2026-10-11T18:34:19.402Z" },
+ { url = "https://files.pythonhosted.org/packages/ca/2c/481bcfc70ceeb77a778ca6e5b705fe592cb64735c108157f81b7dd9c280e/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_aarch64.whl", hash = "sha256:d4193206b6587047437f6f11d7e776df23e1c1e23af2a54d9347275614791e10", size = 2277948, upload-time = "2026-10-11T18:34:21.317Z" },
+ { url = "https://files.pythonhosted.org/packages/24/eb/f1e09333faa7ba447cde967310f758430cc7c5e987bdab5228816c70030d/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_armv7l.whl", hash = "sha256:84bc765b282a9d5b7fe0348b8648904f25a6a04b2139da52b1dd30c8ac3a2c8f", size = 2449688, upload-time = "2026-10-11T18:34:23.321Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/b3/036bde636db8f76d92996e81aefc75678ab5cec4a07eea1ad0c72a893fc3/pydantic_core-2.50.1-cp315-cp315-musllinux_1_1_x86_64.whl", hash = "sha256:ed1e728b39a383c81035b2459cfcb35d99dfb01f7d6ebe3a913bc1cc5b81e459", size = 2479286, upload-time = "2026-10-11T18:34:25.214Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/a3/07f018294ee18d144afeb6df1a47d9e92960f014be45c5ed13519fa5af95/pydantic_core-2.50.1-cp315-cp315-win32.whl", hash = "sha256:bc94f474417604bd383d2cd445d071b07dd55fedceed3ce33407bf1fcc107290", size = 2103683, upload-time = "2026-10-11T18:34:27.42Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/98/f9bd7e1f9b6709f155acb9ef826d9c3884fe925f811fd8e55b9b52280bad/pydantic_core-2.50.1-cp315-cp315-win_amd64.whl", hash = "sha256:983a662de2571cb2502fc8ff47b6770b03d025d2eb314c92f77b3f07c74720ed", size = 2171681, upload-time = "2026-10-11T18:34:29.508Z" },
+ { url = "https://files.pythonhosted.org/packages/10/87/4bb3e1e7f385c076ab5af4d6dd0571b22042cd8207eb810d5b9fef15ae31/pydantic_core-2.50.1-cp315-cp315-win_arm64.whl", hash = "sha256:94845ff54dc5193f228cab81b2662a04bfbb892e95bdc15edf7399000ce57d54", size = 2156251, upload-time = "2026-10-11T18:34:31.455Z" },
+ { url = "https://files.pythonhosted.org/packages/47/47/83643225b08f2aef6c8cc4bbe6e3f79c5e139c4364a6e450d0f399d87774/pydantic_core-2.50.1-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:4a53d13cdfbedbfa87f08b83c1a0a5efcc767d785a4b41934fa9cb672670493a", size = 2234363, upload-time = "2026-10-11T18:34:33.65Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/66/127ca649ba2f2462039dc1e694c6c01e00a3689f023a617364d3507e6d97/pydantic_core-2.50.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:efbecf43d321f7b9281441f1f213f7c21c66988b0e06c2730ba13ed47a46bb08", size = 2000972, upload-time = "2026-10-11T18:34:36.037Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/4f/e421e0a5d653b1203b090b2e48745976988d7338a647940704b5b9c2b399/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bc1f08f68dac9f9e83845a8039880aba2ab553eb9b2259c3243a313182c253fe", size = 2022925, upload-time = "2026-10-11T18:34:37.972Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/4e/ea5568e2491e1a71100f15ae8c2d01ef52db42a184a2d4e716bc79e5eb8f/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5dfe41f232befddb9c4377f6cfc702b51595e2d78ed082672adf8758d2c4619f", size = 2192867, upload-time = "2026-10-11T18:34:39.921Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/5e/3b8c3a35acbe219909ada5defad5d7d9fed845fb2b37bd5ec518f453c119/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:adc06d218a1cadfd2ec4628424d7d79ce4eba69c2965e7e7b55106f0da5208c8", size = 2388687, upload-time = "2026-10-11T18:34:42.184Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/aa/7889b4e515f91a2e8c0ae6b5081fec0feb30c4398d1434e14793c60f173a/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2cf91809d0721ab81592ba67bea7694821679c10b1a2e3c3460082b286c1918a", size = 2464415, upload-time = "2026-10-11T18:34:44.384Z" },
+ { url = "https://files.pythonhosted.org/packages/08/78/93449e628eb8a6fdcce3eff9043081179d1bc7ce6f1bff32dc5006f41e00/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:23923ab9292c40da026330b1ecf4dc2618c8e86e0422e5d1fbf50d94d64ca4f8", size = 2150823, upload-time = "2026-10-11T18:34:46.392Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/f1/72c5bc129fceb0d00f05dc1e67f518c1728de1928c55f81fc13d7690de39/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_31_riscv64.whl", hash = "sha256:f3377c8c2b3ce898423c5e5dd94c7982e30aa7717a7e6ab2470b9de364963709", size = 2245806, upload-time = "2026-10-11T18:34:48.805Z" },
+ { url = "https://files.pythonhosted.org/packages/28/2a/922a0e78f3aa6ab837b59f88190233fb8dad546c17995fde9bb3ed3b9b49/pydantic_core-2.50.1-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:455a773617b5913bf5c20d0692e5787b119e52c4d40ea644ca31f5758fd31be2", size = 2264160, upload-time = "2026-10-11T18:34:50.876Z" },
+ { url = "https://files.pythonhosted.org/packages/52/a8/0f1449e3e1b20941c9372faa18e7b6a092e30cdfa841902473f7989532ac/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_aarch64.whl", hash = "sha256:1a9006395dece0e32e704c315eff8a00bede494f6108546cfc5539c89fef4f9a", size = 2294031, upload-time = "2026-10-11T18:34:52.876Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/d0/1031f492857de70355fb16524bbb03efce5fd34c93ed4a1ec60be07e0d4b/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_armv7l.whl", hash = "sha256:d2d82aa62521c55ddfb000ae70f88cdd8de974078f6024e821dfe5addd0c818f", size = 2468423, upload-time = "2026-10-11T18:34:54.995Z" },
+ { url = "https://files.pythonhosted.org/packages/46/52/269ffffa645b8e47906395a39cf9db1151ae9fa4bcd7f47b960bc31baf37/pydantic_core-2.50.1-cp315-cp315t-musllinux_1_1_x86_64.whl", hash = "sha256:009634b83993777ddcd69cad0ffcace43dabde692109528e35f0fde91e386a8b", size = 2512598, upload-time = "2026-10-11T18:34:57.149Z" },
+ { url = "https://files.pythonhosted.org/packages/31/5c/e47e28281f20326ff6f3c31d626f0a83e615d2d94ba41bb0ad6ec237184a/pydantic_core-2.50.1-cp315-cp315t-win32.whl", hash = "sha256:3fde4fdc6487a58d944ca87cf5adc95d5f266e872c19599f5f4c0a8a1b1f9f9f", size = 2098535, upload-time = "2026-10-11T18:34:59.313Z" },
+ { url = "https://files.pythonhosted.org/packages/03/ad/759e181e69c1b472c60b2049e5f61d5da1deefdd5ce1df4bb2ebcf771f25/pydantic_core-2.50.1-cp315-cp315t-win_amd64.whl", hash = "sha256:1c8632d4ac04e6f91128fca584b3a8a507d81604c24eeaaad00d4be42765c32b", size = 2148645, upload-time = "2026-10-11T18:35:01.571Z" },
+ { url = "https://files.pythonhosted.org/packages/6d/56/8a702c27e5be9f47e5f19d8669227424290e4c024e7c370279cbaf244b4e/pydantic_core-2.50.1-cp315-cp315t-win_arm64.whl", hash = "sha256:c3ede305158e75510be50869b319550ab072008c13d64d4ab1e094fb286b6f44", size = 2175297, upload-time = "2026-10-11T18:35:04.079Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/73/a23a327237d9bb985298ce47702e196f995ce850224c12c70886bd66bb5c/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-macosx_10_12_x86_64.whl", hash = "sha256:062e891facce5ca296a1c37098e5e466780457f86413894b399f0cf22934f769", size = 2162560, upload-time = "2026-10-11T18:35:06.274Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b2/33b37b5e82408f402a5e82649a43e7c95cd5dab7beaeef2c35cedbfd18f2/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-macosx_11_0_arm64.whl", hash = "sha256:49c2cbb2397fe4d0987e84606e691af6cb87bc0ee1bd3e7b737f7e10b4c142f9", size = 2075250, upload-time = "2026-10-11T18:35:08.455Z" },
+ { url = "https://files.pythonhosted.org/packages/04/62/c7d5b9249466ccab90f3ce4790c2399dbfb8172ae9e74a7753dd295fe65a/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9e4472072de0137ee0d8e72d6620e85939c271d2f90f6bbb4b15c24638b79f92", size = 2107891, upload-time = "2026-10-11T18:35:10.918Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/7e/28d29bc1657195933147f772dee0558d9e0f045bbc11db7f992f40c37bb5/pydantic_core-2.50.1-graalpy311-graalpy242_311_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ed4f3cef55164b026fefb41341b7754cc6b624c75dfe7142d2ecceb5ad21c87", size = 2268026, upload-time = "2026-10-11T18:35:13.336Z" },
+ { url = "https://files.pythonhosted.org/packages/a5/09/1bcf160f3cd6333e2a76982fae5963e5032de0bcaaba38464223dc266bfe/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:76e2e83fa6ec8cdc972d438dafc2522b3a47bee4ec0ae668b29cfb1977ab5242", size = 2217070, upload-time = "2026-10-11T18:35:15.688Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/81/f02de95eec7794dfc7a5bc69a834f0e834b31b3009014ce6de903855f775/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:a51eee75939cf811ac09b278745a6cee7dc873ccfbc8b9af3cc88fe4b7ce25b5", size = 2039027, upload-time = "2026-10-11T18:35:18.07Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/16/227746ed3771d9bf2304568b55134849ece34e13b38b1d76de763015c77f/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ae28183297fb0d2b8dc46a1f01d51f5e45825fc5afe76a835a6cb7fb34821295", size = 2109009, upload-time = "2026-10-11T18:35:20.218Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/b9/7664d1592a0e5a74903f357cd5b28ceb7336f3473256c5a4e8c8432f1881/pydantic_core-2.50.1-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:88e492e8b9d0312e7dc13667c30222abf284dc3b79b5302b3607b41a5784ce61", size = 2269883, upload-time = "2026-10-11T18:35:22.48Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/35/002a378302ff91d2a7f149bc8d22363faf581ba40ac39d0123444315a32d/pydantic_core-2.50.1-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:7456d699b13954e9c0164dcb267250a10ae0dfb03e6e26d6796ab0d46e189c84", size = 2154076, upload-time = "2026-10-11T18:35:24.743Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/5c/7a034330082c00f3c9795052bd9b161aaaf6e1951e897ac00a094823ef7e/pydantic_core-2.50.1-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:b0d955195bbbe489ad343fcc956eacea9357b79cb22192c66cacdefcbc14b32f", size = 2085850, upload-time = "2026-10-11T18:35:27.009Z" },
+ { url = "https://files.pythonhosted.org/packages/57/fa/d5b44a7d3d4eceed6ef8713a0003af56fefde6ec75915513bcdd5d730cd9/pydantic_core-2.50.1-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2c634642694e6a0dad2ab1d375589fa671fd442edd5caf7d9737b8f6ca22906", size = 2259754, upload-time = "2026-10-11T18:35:29.763Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/88/e01dd37301710bd8b3b2daf34ec9e2a6e83a333130831c0d468b0292a48c/pydantic_core-2.50.1-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:ee6db2fbed51a7991302e8fac498cd67e336246026d0dfa84cf5166ce1412760", size = 2268712, upload-time = "2026-10-11T18:35:32.654Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/43/5bb80d4a4d2b2d84612a30205662cc521a20d209c94abc079ea48b4e0df2/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:79490e33c4c0fcb933bbbcfc3a62184d8803b99f535863dfbb925e1bcb6945ad", size = 2305049, upload-time = "2026-10-11T18:35:35.392Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/7b/e3ef76b269cfe4a5bd3bdcf5d7124cf05e5a8909d443b91639307116bdae/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:48569b0ade9edfbe065cad1d700175546592aebbb42f02adcebcc26e75b896fe", size = 2466001, upload-time = "2026-10-11T18:35:37.791Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/7a/ea0947aadc1d9f72d4be00d2e19939611ec17363bcb5e6eb9ec7d8221c34/pydantic_core-2.50.1-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:5f3cae32fc46121f787cb2486de9cf95a8bf72aec5cc78f64c606fa1735a6ef5", size = 2481777, upload-time = "2026-10-11T18:35:40.287Z" },
+ { url = "https://files.pythonhosted.org/packages/24/f3/d15bc0b1fb0c4f1e07b3326ffb7489f7ae70da2b56a0884d22641b0eb47c/pydantic_core-2.50.1-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:7f476456ac2bb0d937f75191494a09c83a30765fea4f70f3b404942fe25f6cdf", size = 2336787, upload-time = "2026-10-11T18:35:42.558Z" },
]
[[package]]
@@ -4894,7 +4899,7 @@ wheels = [
[[package]]
name = "pydantic-settings"
-version = "2.16.2"
+version = "2.16.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pydantic" },
@@ -4902,9 +4907,9 @@ dependencies = [
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/d0/31/21d21e0e38a8f5f7a7f0cde22820117b6d17d6bc36f348129527f915d7f4/pydantic_settings-2.16.2.tar.gz", hash = "sha256:3dc08ffabed2bbd5dc2dd55ead7b78b4e4c5cd776aecd04310db5f1224caaf46", size = 309225, upload-time = "2026-10-28T11:14:28.066Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/05/ca/7817b4bf0c4b15c6a6ab8cdf4cb96c268335a8ca2f525de93b6ebe4c7d28/pydantic_settings-2.16.1.tar.gz", hash = "sha256:aff602a1886338c2df3d1e958e2193a8dc33cec75b91f202e83fd98484b4b0da", size = 303436, upload-time = "2026-10-20T11:37:19.024Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/39/88/4131c51ff87b7cd7f82a1194d0c0a2b7ca68c72d9da04c3b7e69ab91a79f/pydantic_settings-2.16.2-py3-none-any.whl", hash = "sha256:8d523e5c129b097686bde019bd6109bff53f4c8c3f63f1ddf9933ffa42e5ab4f", size = 81994, upload-time = "2026-10-28T11:14:26.716Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/74/fdf926afe145d8629c4b2ff57f4ea1d64ed7abc4d84d97463b2517a2547e/pydantic_settings-2.16.1-py3-none-any.whl", hash = "sha256:30f5313340da4e8f1b440d1910b92a5bc3ccbb6b4ba70ec2b8395bc921f418f5", size = 80771, upload-time = "2026-10-20T11:37:17.298Z" },
]
[[package]]
@@ -4945,66 +4950,66 @@ wheels = [
[[package]]
name = "pymssql"
-version = "2.4.6"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/70/86/ec7658cf5ff34dbe6f9e631db252b6f22bd39129af105c489f64754836c1/pymssql-2.4.6.tar.gz", hash = "sha256:2399f7dabcbacd56f10337f353a885a0954be611f6ec8da0e9a5d0f6f0311e0f", size = 213011, upload-time = "2026-10-27T02:53:50.798Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/c7/79/47ed1789fcf03a7fd2262f2f6d4a1425c73bcc5b5451ece7c7047a805b9f/pymssql-2.4.6-cp311-cp311-macosx_15_0_arm64.whl", hash = "sha256:7e3c4c2b7052c65cfa619491ac870828e0efa6eead1a086cd36f043da25e3634", size = 3244488, upload-time = "2026-10-27T02:52:23.827Z" },
- { url = "https://files.pythonhosted.org/packages/4a/77/60ace5e7049e68b23f678fbcef5a3d190603918277c84ab9c8599ca8e138/pymssql-2.4.6-cp311-cp311-macosx_15_0_x86_64.whl", hash = "sha256:a739b6087ccb10a3580f5eb464410437f9962acd635d0e45df65b80ef08ece9d", size = 3017947, upload-time = "2026-10-27T02:52:25.358Z" },
- { url = "https://files.pythonhosted.org/packages/ac/90/71e2bd433b642d6c04207db235efe70b8085d7fc438a1e2750bf964a382e/pymssql-2.4.6-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2ea0bdfa45b7acd57c15c4184435f7595810ec01190557277c210eb1c8dfd69a", size = 3071283, upload-time = "2026-10-27T02:52:27.054Z" },
- { url = "https://files.pythonhosted.org/packages/d6/a5/de6c8f98f1c46329da2b5b917348ebd725e5d7593d7c62c1097da6dc3b55/pymssql-2.4.6-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3369b93e35d99cbdb428dd6a237b407abe825c889a7d983494e778c906d474b8", size = 3249185, upload-time = "2026-10-27T02:52:28.733Z" },
- { url = "https://files.pythonhosted.org/packages/dc/21/1e80a7996aafca4e163bbefee4c45829e307a31a1bdc51c5518eec1e9fe2/pymssql-2.4.6-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:9f7e052230137112b92b5b37282c5ad7caa9cef7af286c5db3876cb4539d4454", size = 4462767, upload-time = "2026-10-27T02:52:30.054Z" },
- { url = "https://files.pythonhosted.org/packages/66/fd/b1feeba236282673b286556c855f0d6ecb18ecc9f0b0f97da8c62fa5e7d0/pymssql-2.4.6-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a0109c9be7ef18adb27252b72227b420f3cef03db4d492417be156bfced38080", size = 4199943, upload-time = "2026-10-27T02:52:31.192Z" },
- { url = "https://files.pythonhosted.org/packages/ea/15/d59efcbf072b4f93bdf2b47920ed08bbb5b5aca69dd7e947b520e5f84266/pymssql-2.4.6-cp311-cp311-win_amd64.whl", hash = "sha256:76fccef34fced00b5b9bbe5e30bc6735f13add5a490f10228c2da804a62b7455", size = 2328615, upload-time = "2026-10-27T02:52:32.517Z" },
- { url = "https://files.pythonhosted.org/packages/36/c6/6415d161fdde234b4f065b61505234e7556b07d34edf12a6781a3066b8e1/pymssql-2.4.6-cp311-cp311-win_arm64.whl", hash = "sha256:b2463bd5840977eaa18a1fd4f353defbe7cb5b6026ac497aac30d5506565fa57", size = 1855993, upload-time = "2026-10-27T02:52:33.836Z" },
- { url = "https://files.pythonhosted.org/packages/a6/ee/0f7da0b75041f3700839499a7b579688d5569f2e93fbf88712addf4917d0/pymssql-2.4.6-cp312-cp312-macosx_15_0_arm64.whl", hash = "sha256:c5d8ccef171b335e6ed3da4689f113a5cdbcce738ea5f1870cc7c71f763a0f8d", size = 3230920, upload-time = "2026-10-27T02:52:35.016Z" },
- { url = "https://files.pythonhosted.org/packages/4e/69/20fa7c64b5f826919381e47af5e4539f1581d592707bf43b3bf81c81f6ad/pymssql-2.4.6-cp312-cp312-macosx_15_0_x86_64.whl", hash = "sha256:ed10c66ed9c1ec2964480c0ab2daeb4f6b6a6ee261a0e42abe45a74465cdad67", size = 2998868, upload-time = "2026-10-27T02:52:36.256Z" },
- { url = "https://files.pythonhosted.org/packages/65/60/d0cb02444abd419268a226769d66398834ea635e8443508d736a4bf03a15/pymssql-2.4.6-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6ea24c9983543db3dd33c5806ce0425d741a3e2104090e8962c9901803ef653", size = 3102143, upload-time = "2026-10-27T02:52:37.773Z" },
- { url = "https://files.pythonhosted.org/packages/42/f9/5c561c35a0be1e234f16658b9a76e5adacbe0a77c91d93390ba5ab3e5ca5/pymssql-2.4.6-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dd1e5a1af9508033ef14bb8d074cfbf6cf2a4e04aab09207e63c9d4b37e3c485", size = 3278701, upload-time = "2026-10-27T02:52:38.988Z" },
- { url = "https://files.pythonhosted.org/packages/40/55/c9c6f537863399437d22a13bc3472da82638dcde9ac554ee99fb0545593e/pymssql-2.4.6-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:e31e9a10196b39d9b0645009b122dc1e18458b84e93e3789febd9f3720e11054", size = 4492333, upload-time = "2026-10-27T02:52:40.49Z" },
- { url = "https://files.pythonhosted.org/packages/d5/ad/d3870696da44fb126fdd473f890c048ae4f2cf6e46df5abebb9fe4c8d3a4/pymssql-2.4.6-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:afee0c02cf275cda2b7498e1019a938a00e8765b08cec71634a1fd45757b900e", size = 4228007, upload-time = "2026-10-27T02:52:42.229Z" },
- { url = "https://files.pythonhosted.org/packages/bc/70/cce95b45f6f0d214ebc1a368e7088ac86e86cae638e51210bbb03f3c92dd/pymssql-2.4.6-cp312-cp312-win_amd64.whl", hash = "sha256:f1b1d1a67a447e995f9588018170fe3c940f74dd8703b829edaf7dc332debd32", size = 2316243, upload-time = "2026-10-27T02:52:43.792Z" },
- { url = "https://files.pythonhosted.org/packages/31/b2/1b4556be296c5fd2aafaec2f2df53479c22c718db2ce9e956c983fddb0a7/pymssql-2.4.6-cp312-cp312-win_arm64.whl", hash = "sha256:786eedbef791a9faf445a83020826254f7d12fcf2cd453328399867de1fa162a", size = 1839928, upload-time = "2026-10-27T02:52:45.237Z" },
- { url = "https://files.pythonhosted.org/packages/9e/76/710e8cd697871ea43da3cafac4fc7e86ee3da9ba3d80063947ca8565adf0/pymssql-2.4.6-cp313-cp313-macosx_15_0_arm64.whl", hash = "sha256:ce1b67bce86d0572f7be8e5e63520384128b3372d33f68995415aa46f08b7ef0", size = 3235014, upload-time = "2026-10-27T02:52:46.565Z" },
- { url = "https://files.pythonhosted.org/packages/88/20/76c381d2edfa87b2a83e64b93891a0fa496f521727a85961b9218d6e17d8/pymssql-2.4.6-cp313-cp313-macosx_15_0_x86_64.whl", hash = "sha256:58bc76bfa786042ed82ccb66f9032cfc3c28ebb99ce5c366b93b232577c2adb2", size = 3004184, upload-time = "2026-10-27T02:52:47.984Z" },
- { url = "https://files.pythonhosted.org/packages/f7/da/6ea8b768c9fa4050c7a5f4deb52076a8f7042cb422279e7b8f1dfd238ef7/pymssql-2.4.6-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aef754ebb8c08ddfde02e67f5af04a186bccd10351ab772de8cefa75df68dcbb", size = 3101530, upload-time = "2026-10-27T02:52:49.161Z" },
- { url = "https://files.pythonhosted.org/packages/4d/25/1abe10b22255df537786227a3e3753775b0af77a858d499ab3a40b5f1f7d/pymssql-2.4.6-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4bfb91d65576ac7a7df91c62d976357827dadd9894fd4ba62d78f8cc040a5540", size = 3278294, upload-time = "2026-10-27T02:52:50.441Z" },
- { url = "https://files.pythonhosted.org/packages/a9/61/3f50631191d73515921eba30daaef8e2ea64a7c5d286ca5f03ee9cf108f5/pymssql-2.4.6-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e2f8782c4320ffc28bd3abdb14b9bf968eea26f3ebfba1397023d9f3dec20549", size = 4491947, upload-time = "2026-10-27T02:52:51.773Z" },
- { url = "https://files.pythonhosted.org/packages/35/44/faeb38fa0d11adaf6ee80e818d34059d62b3ce326500521bbd3f9ef8c238/pymssql-2.4.6-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:44bd7fdacba391c9938588c24f9190c083a04ce47fa21cb32531dad6dc303a00", size = 4227600, upload-time = "2026-10-27T02:52:53.025Z" },
- { url = "https://files.pythonhosted.org/packages/4e/35/556c317f18f36ae714efffc94dc972e003088158890e5deb3e1c908d3f6f/pymssql-2.4.6-cp313-cp313-win_amd64.whl", hash = "sha256:e4ff7d8c1adbbace3591c021149ddb8fcd82697eb2cf75dfab627b8df846b06a", size = 2315770, upload-time = "2026-10-27T02:52:54.717Z" },
- { url = "https://files.pythonhosted.org/packages/99/0f/94638bb0ee7064250abe0d7f148e74cd8c759f4108ffbd663a189814616b/pymssql-2.4.6-cp313-cp313-win_arm64.whl", hash = "sha256:a9d66b0c6a4c0210c3e1b6c2054be71e0b85f2dc673eda27602fe6856a401c20", size = 1839188, upload-time = "2026-10-27T02:52:56.139Z" },
- { url = "https://files.pythonhosted.org/packages/22/4e/d45bed050927a35e8e96fa3068517cb6de001c80892e6766bdd3e6b1902e/pymssql-2.4.6-cp314-cp314-macosx_15_0_arm64.whl", hash = "sha256:7b33b31e1f0e141d98c38e2a7c028ce8d8edaab23418bbd8558240426f73fa35", size = 3239406, upload-time = "2026-10-27T02:52:57.471Z" },
- { url = "https://files.pythonhosted.org/packages/75/67/7a7664711669de9195fc4b91b87a1853f8617e8e6788df0570869da4dbb6/pymssql-2.4.6-cp314-cp314-macosx_15_0_x86_64.whl", hash = "sha256:258332d757744772a467ea982a2c9cb79c27ab7d93e346ffb39c34a9c5044aba", size = 3006181, upload-time = "2026-10-27T02:52:58.725Z" },
- { url = "https://files.pythonhosted.org/packages/93/be/00e73d5e9f651e949b23225c082378e434bf03923863cfddbeb726a1f341/pymssql-2.4.6-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:277c5a1262949a51a66bdfa62da86b2b81d11186ae4e7ecf7b1c627931262a48", size = 3099917, upload-time = "2026-10-27T02:53:00.486Z" },
- { url = "https://files.pythonhosted.org/packages/19/f3/fbfc3dc49a717d753532148504c56f74c2bb630733e6e25eec8be6a604ad/pymssql-2.4.6-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9a195e7b84ecb9436e5f49e4ad26dd0dd2ed4775512e649762c29fb05ea13da3", size = 3276344, upload-time = "2026-10-27T02:53:01.671Z" },
- { url = "https://files.pythonhosted.org/packages/bf/a4/bd7226ca9a24fbf5118493391f6e6bc20994650b07d6bead6282fd21896a/pymssql-2.4.6-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:65d3e2ca8b6aa56cb45b216e7db420d43b7b8a7fa155eeffe3eccfbd61d86777", size = 4490503, upload-time = "2026-10-27T02:53:02.784Z" },
- { url = "https://files.pythonhosted.org/packages/02/44/75ede4d8f7fad21d409d497926a160536a44c91994a34bb967459dc12a3b/pymssql-2.4.6-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7177bc90667a9d89bafbd9ccc44b5db20baea47f4b0737556b501aa489787f74", size = 4225591, upload-time = "2026-10-27T02:53:04.02Z" },
- { url = "https://files.pythonhosted.org/packages/d7/08/e55095d93eeed2ea3a3d90375d1dba48d19f0bc4f096121d2b98055aa723/pymssql-2.4.6-cp314-cp314-win_amd64.whl", hash = "sha256:4a3b54b881edc5f051f17fd897a1617ec2572f194ab224007a17495c8d03f1ae", size = 2378302, upload-time = "2026-10-27T02:53:05.576Z" },
- { url = "https://files.pythonhosted.org/packages/55/30/b470d11026bebbdd843f9bb55cadce739d72afaa16749b3793e1a4ebfbb6/pymssql-2.4.6-cp314-cp314-win_arm64.whl", hash = "sha256:662d2044af8649df6357723dcf5e85a16a662ccb2669427f4a5cee5b18366193", size = 1908943, upload-time = "2026-10-27T02:53:06.867Z" },
- { url = "https://files.pythonhosted.org/packages/9c/1a/c3e4b5366e5c1e66a1cf357a795d2eb98134ab81144e9f0a83b1dae659db/pymssql-2.4.6-cp314-cp314t-macosx_15_0_arm64.whl", hash = "sha256:555c2804c85a4f93b07ddd77a545d920f7a194eefebd8860acdd83f1f9a7818f", size = 3259390, upload-time = "2026-10-27T02:53:07.999Z" },
- { url = "https://files.pythonhosted.org/packages/f7/37/3547af6506a3eef3415cc726e0fdddc26ff04afbe8b3862eb9c5059a10fb/pymssql-2.4.6-cp314-cp314t-macosx_15_0_x86_64.whl", hash = "sha256:5773c7994b54ba0e55fdac1cd4f8ff779d9fcf0fc664a4a28b7adec6980af98a", size = 3020987, upload-time = "2026-10-27T02:53:09.297Z" },
- { url = "https://files.pythonhosted.org/packages/94/fb/fecac21e4b815fb68317bc30d397412958eb5a935353670ddc20c920b579/pymssql-2.4.6-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:335c906adaf0ebc9dc0583cddf818304f5b9efc183f3b342845b86ca8428358a", size = 3093843, upload-time = "2026-10-27T02:53:10.87Z" },
- { url = "https://files.pythonhosted.org/packages/bc/d5/fb461ab7a61c9447f3b3f6b0d35cc09aeecc9bbbf8b1b19c5f36df63dba2/pymssql-2.4.6-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d5a07b13f200d5d94a489bbca5ba0af27de48e529e0a08447d339300d9c3ecde", size = 3272634, upload-time = "2026-10-27T02:53:12.404Z" },
- { url = "https://files.pythonhosted.org/packages/47/96/22693e25c0f10cca54e4b13891b60fdf983b2c974e5eba237c2f394a0498/pymssql-2.4.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:e18373991d65852f49a61141ed9c37d955bf9c923e7ff672f32696760d3c07fe", size = 4485517, upload-time = "2026-10-27T02:53:13.776Z" },
- { url = "https://files.pythonhosted.org/packages/66/02/fba1cae31e32cc6a08a6c7b0d87f9bbce53274ec9a36ffd0162b3a39bc5f/pymssql-2.4.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2b9c16b49e60c5f24814331943013df9b10fd0e377266399b03c8238ea418c38", size = 4223855, upload-time = "2026-10-27T02:53:15.462Z" },
- { url = "https://files.pythonhosted.org/packages/29/df/85503393fb196a00b85eb3188522a7ddb94ae99da9d665d35083f4cfc663/pymssql-2.4.6-cp314-cp314t-win_amd64.whl", hash = "sha256:5efc2c9d6ffe6e922140db513bf0094b6b10d6a04e259fd3b9203c5a032048d7", size = 2397136, upload-time = "2026-10-27T02:53:16.781Z" },
- { url = "https://files.pythonhosted.org/packages/55/84/fd08b664b5152adb72de493613408be37447a3a7badbd7c732b26ee5b331/pymssql-2.4.6-cp314-cp314t-win_arm64.whl", hash = "sha256:29f8a8a1f02928262549c6bc9f35c58bb42197829bdc0fd3a107cc9a9906f727", size = 1923884, upload-time = "2026-10-27T02:53:18.044Z" },
- { url = "https://files.pythonhosted.org/packages/a6/c1/cfc01cb4e58b99abbebc65cdfd5c0cda81d6277217ce7bde83af4e718448/pymssql-2.4.6-cp315-cp315-macosx_15_0_arm64.whl", hash = "sha256:10888784ef1d8b8ceeef48db3fd7624c45b5daf0053c1a8c9efac29ea322b65c", size = 3237778, upload-time = "2026-10-27T02:53:19.351Z" },
- { url = "https://files.pythonhosted.org/packages/69/e0/d77d92560215192c6f36ddbf4abf6d6ef0d150a8a6907b3fdf6860538532/pymssql-2.4.6-cp315-cp315-macosx_15_0_x86_64.whl", hash = "sha256:6bd1bd6b15ebac0b990fb54241cb98c0c438f7d4c8d455a5d023789a3e4c2a58", size = 3004893, upload-time = "2026-10-27T02:53:20.585Z" },
- { url = "https://files.pythonhosted.org/packages/5a/59/275d30d325e7c1b12c48802b8407f3105bcda87d493f447d480f4f1ac105/pymssql-2.4.6-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a6688e398ff56713e4fa7e662b1e2d4706fe27338622c963cfb779d84753b228", size = 3100246, upload-time = "2026-10-27T02:53:21.814Z" },
- { url = "https://files.pythonhosted.org/packages/cd/cd/01336858c679a5e0ad6ecd14025fa04c30392d57d966613b0c9d1c438dbe/pymssql-2.4.6-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:808cc5094153f326c96d1bf392baeee82cc98a24a65a556799be40645d81d473", size = 3276460, upload-time = "2026-10-27T02:53:23.378Z" },
- { url = "https://files.pythonhosted.org/packages/b1/37/9a39a7b8ebba910c6541d0e8b44fc5fc35422321af248000fd733f7732b1/pymssql-2.4.6-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:216f754182a223b37c284925b56cc4dd4b14f9b9249bd67dad0ae476e5c80c0d", size = 4490837, upload-time = "2026-10-27T02:53:24.71Z" },
- { url = "https://files.pythonhosted.org/packages/8b/b5/c3687a05791b05753255df0a84d292cc0cce50c751e3f10de30b22992c9d/pymssql-2.4.6-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:9424d018537bda58f28d57ecee955b3becc7617bbcce94108c4578a44093c4a1", size = 4225821, upload-time = "2026-10-27T02:53:26.083Z" },
- { url = "https://files.pythonhosted.org/packages/d3/31/7d7e2265c7ad600e27afcf396551ba5710571a49b149d9afa793a85a1ed9/pymssql-2.4.6-cp315-cp315-win_amd64.whl", hash = "sha256:ebceea523fa483a2a13db888f282d609e557c6766c8ed90519d044a1bce6cd56", size = 2378073, upload-time = "2026-10-27T02:53:27.403Z" },
- { url = "https://files.pythonhosted.org/packages/ea/e3/7eb949d0a80a50570d926251644cab52bf4a8f37e8a48aa9e27e3a49c7d0/pymssql-2.4.6-cp315-cp315-win_arm64.whl", hash = "sha256:1bf080fa58d5bda9b963fdce8d62bd76f234e8d45f716895ea51f794bb161284", size = 1908506, upload-time = "2026-10-27T02:53:28.633Z" },
- { url = "https://files.pythonhosted.org/packages/10/82/7227c27bb701ea6ac258a9b7894089dd3d6aab5c5df27254d42409bb4b8e/pymssql-2.4.6-cp315-cp315t-macosx_15_0_arm64.whl", hash = "sha256:e5b1126ee2fa39816aec275868c9ef2fa2dcde04156c0e77629c33c782f7a37a", size = 3258074, upload-time = "2026-10-27T02:53:29.892Z" },
- { url = "https://files.pythonhosted.org/packages/b7/28/d3354a43646884ec6f20cb89b123fc04da781f943ebb0fbb5e81e1082751/pymssql-2.4.6-cp315-cp315t-macosx_15_0_x86_64.whl", hash = "sha256:adc59a5c3ba6520ee52c6290c7c91797423ddfdf46336c1a470b209c9f97fa2e", size = 3020095, upload-time = "2026-10-27T02:53:31.264Z" },
- { url = "https://files.pythonhosted.org/packages/ec/67/696b8e7765b8a881fb08d36aae489cd6c1ce628ad430102342848ead4abe/pymssql-2.4.6-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7473ff8756717ae869e49ec7aba2156ee480ed91369109f69b27e06da80c4ff0", size = 3093373, upload-time = "2026-10-27T02:53:32.466Z" },
- { url = "https://files.pythonhosted.org/packages/31/08/62596da0ac775d4a209793898d1896a2cfebe68bc1fc65cb09c47d64a64e/pymssql-2.4.6-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:465e9e3227e5f96f47cf30fa974ecf3371178e07caa5ed8ae9cc3302c4dd93bc", size = 3272143, upload-time = "2026-10-27T02:53:33.779Z" },
- { url = "https://files.pythonhosted.org/packages/f5/41/67c5827e9e6c83554001aa4fd682278703bdfa9da4ed0440ec9cad102f45/pymssql-2.4.6-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:d04cf86a39f3c49904a156a01650a2a62348585fb88134980cbd2a458b263dc2", size = 4484746, upload-time = "2026-10-27T02:53:35.223Z" },
- { url = "https://files.pythonhosted.org/packages/a2/68/e2da61131c47605485b7c85976ee4be3b434a8e07ffdd2c00f2f0f47c17e/pymssql-2.4.6-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:d8d34d384cd5a5dbb78084ed6473f9a4b0f9eb99b4291cb53cf2eb830156fcc5", size = 4223242, upload-time = "2026-10-27T02:53:36.603Z" },
- { url = "https://files.pythonhosted.org/packages/17/f5/387ff4ace0320f2ee8f3e262e48c65a0fd4186bf889fd86f9d5a33b894d1/pymssql-2.4.6-cp315-cp315t-win_amd64.whl", hash = "sha256:9b9d195f4deb772bb012d35d6b79e365d7209d23e68801bb8b64449948ae3614", size = 2394827, upload-time = "2026-10-27T02:53:37.932Z" },
- { url = "https://files.pythonhosted.org/packages/10/c4/54c9d83f609f57f305866ed29cf5225f69ebc3900c0dc1ed4177b9c36345/pymssql-2.4.6-cp315-cp315t-win_arm64.whl", hash = "sha256:6e8cf9bffefd3a211c7dd555bd1a33c24ac2050b80a83e6a4e845b2c838444e0", size = 1921745, upload-time = "2026-10-27T02:53:39.099Z" },
+version = "2.4.5"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/36/f9/b56dfde2a0edf51a298b67ffaea62aa8c9459fb32c44b4f4701258debfd9/pymssql-2.4.5.tar.gz", hash = "sha256:ada0e747bab96b6b7ee0d21ca019db6e099cda0593080f59089e182a0a14b42a", size = 212797, upload-time = "2026-10-20T00:19:42.067Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/d6/56/85067bd635017f8de62dd377db9f9966e8b6b725050808b271f0fab2b9f2/pymssql-2.4.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:29e9a463fc19a1029a33df49f29e2f736bf97e511de48e690864c4572a9f8979", size = 3213173, upload-time = "2026-10-20T00:18:07.039Z" },
+ { url = "https://files.pythonhosted.org/packages/89/db/a3dc723564ef6143d6a276188c234152758ba8bcd1799ad84246e12f04db/pymssql-2.4.5-cp311-cp311-macosx_15_0_x86_64.whl", hash = "sha256:d8c1fd28defbab9bb4144b86a319924f73bc24ccdfa715f76105fbac35035ef8", size = 3017602, upload-time = "2026-10-20T00:18:08.697Z" },
+ { url = "https://files.pythonhosted.org/packages/30/95/769700844802c37cafb679d7f61092d82d5df5ae8ab18837e74e3a64d6c4/pymssql-2.4.5-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:9d6f83ba9e9095b6b27e3c72ea99c76b29c1d7f7011c77411b5b6fd3c18b5633", size = 3070534, upload-time = "2026-10-20T00:18:10.023Z" },
+ { url = "https://files.pythonhosted.org/packages/33/3a/c7ec2db346f809c3cda1741e90e7f4b9af9ff4856f74dc34274908c9ecba/pymssql-2.4.5-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:b428cdab437eb1cb3d8298bd4fcc5eb58c359c5915949f7f418374e29b79114d", size = 3249743, upload-time = "2026-10-20T00:18:11.358Z" },
+ { url = "https://files.pythonhosted.org/packages/19/66/64fd381ee64a4c898a9957f0da8bcbd7d8b8dc871abd37fb30de7f0b8e85/pymssql-2.4.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:5a591f363ed0de2c84e988ce50150649b3c325e7609f11a240016db02f52a937", size = 4462361, upload-time = "2026-10-20T00:18:12.605Z" },
+ { url = "https://files.pythonhosted.org/packages/7a/1d/178f3864083f722fc4b9234c1008a290e917acede1556bb5a6136c028eed/pymssql-2.4.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:45b1c3989d656ee806911474d9c1a20ced2970db6364bbf35046ea8599ddcb1d", size = 4199687, upload-time = "2026-10-20T00:18:14.228Z" },
+ { url = "https://files.pythonhosted.org/packages/54/4c/284adab7f871d2ed4b93f6e02bbf87cf5c78dd6dfaa664f7df48a6441c1a/pymssql-2.4.5-cp311-cp311-win_amd64.whl", hash = "sha256:56763b89ed308fc9e47644bbe8eba6ca19a18584b36cff53160394bc3878b0b9", size = 2328148, upload-time = "2026-10-20T00:18:15.902Z" },
+ { url = "https://files.pythonhosted.org/packages/94/0e/87491f22bf656ced8adb59cdd614f744438b1460c94275eb42cab064faef/pymssql-2.4.5-cp311-cp311-win_arm64.whl", hash = "sha256:984237bec08410e09c28b4fc380d48bd1c4b72d6de56dc80f3e8cffab5aa55ec", size = 1855894, upload-time = "2026-10-20T00:18:17.22Z" },
+ { url = "https://files.pythonhosted.org/packages/59/bd/2bfd56916b902febfd9e797d698fa9a3672bf250582ffa8b4c53d1e25473/pymssql-2.4.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:e316c829ba6cbec50a66fd37cc34a7c63a87777578bbb28e9de1519aad144d3c", size = 3195489, upload-time = "2026-10-20T00:18:18.42Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/0d/40e7f691f66e5583ec46958129fab37a85a8d58edf62022c1be9fd564f89/pymssql-2.4.5-cp312-cp312-macosx_15_0_x86_64.whl", hash = "sha256:4cea4cd70bba994fdadc577c758fc18c43d22a672c01c5bf781f74c2545719ec", size = 2998450, upload-time = "2026-10-20T00:18:19.732Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/32/9308ebfe76c4ac9eedeeb0d49cbf449e8cf807b799497564dc0704f1fd02/pymssql-2.4.5-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1055176a5f1dda28d90ae7c10db7b3fa785c824e884ca1edb7d24d5a080b91ac", size = 3101331, upload-time = "2026-10-20T00:18:21.143Z" },
+ { url = "https://files.pythonhosted.org/packages/09/fc/358f9189f27ce60b07c1a525dbbffc20c944df09e60e116c48e0ec3e270f/pymssql-2.4.5-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4cf2fdd1f626053b804928eec74418e2d5fd70de02e2bfa4ed04cf34e1759c99", size = 3279411, upload-time = "2026-10-20T00:18:22.422Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/d9/6e6ef2f089096926f68f0905365fc611f3bd9715c17868393b16a028c1fb/pymssql-2.4.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:72a74d0a3ee2d5a5810b42896bd808d7b93027b3b5b44a245cf0f3a3d0ddfd28", size = 4491849, upload-time = "2026-10-20T00:18:23.836Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/3e/d2c18276f8ea90f57ecf3a95681986847c5c5290b361704b578a06e92191/pymssql-2.4.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:c390159a7d662401f6347d4c2210ef3ec8798f5e1628b6caa0bdd9bcedc5b65b", size = 4227456, upload-time = "2026-10-20T00:18:25.494Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/6e/f06efea0e9072312fec21cabe1480743763278b9d8193bbbef962a1b614a/pymssql-2.4.5-cp312-cp312-win_amd64.whl", hash = "sha256:e4a225585dac65c8e354a6cc12cdd69aa9c421d0ad2fd2feedc58d28af9b2923", size = 2315806, upload-time = "2026-10-20T00:18:27.2Z" },
+ { url = "https://files.pythonhosted.org/packages/d0/b2/5081c4e7bb13eca469da6259006def2f58d35744bde79884cd6139daffaa/pymssql-2.4.5-cp312-cp312-win_arm64.whl", hash = "sha256:0358f787afa508237c3ed56ed9a6f0134aeed7aa8dafa1fceb43863c77b92ebc", size = 1839736, upload-time = "2026-10-20T00:18:28.492Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/8f/c3dc712661c0cacba464c8fbaee865e5ebf39667f7a4c2ae773d6f7630c8/pymssql-2.4.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:a0c154cd8d803265671136dba873206fcdb87b70b0c319b01ef095a173aead05", size = 3195529, upload-time = "2026-10-20T00:18:30.316Z" },
+ { url = "https://files.pythonhosted.org/packages/26/2d/d53796f3de0b0fa32667c8ea547273930b099ed22dd3b2e427c8746ac91d/pymssql-2.4.5-cp313-cp313-macosx_15_0_x86_64.whl", hash = "sha256:81071a47a9f026c6b154c02a7a7b842f44c9d23ec461c8a0d4ecd9d09485808e", size = 3003695, upload-time = "2026-10-20T00:18:31.894Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/02/0a194ea53f44f60a9800cf9fe6acdfaaa264247de9ac26e63c2376a8e62a/pymssql-2.4.5-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b8ec1b6ca9d74f9d9280a84dfd7d2c7dea05651ea3108620b91dfaa4ec8d1bcf", size = 3100808, upload-time = "2026-10-20T00:18:33.445Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/b8/af4fb12dc0e3ca21acc7686987a24d1f8cd8e3d69fb9a4f66729cab339db/pymssql-2.4.5-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9ee37e118fbcddca6ba0d30e227c40cef5606c6ff182653304fb13de66e9883c", size = 3278947, upload-time = "2026-10-20T00:18:35.04Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/b3/beceb56d905380fafddeb7466d22aff6ffb26603d8458256030a0cccf108/pymssql-2.4.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:441c85480d00c8e2d4931007739a5f87c030cc5f650ff531740a240a0ff529ff", size = 4491325, upload-time = "2026-10-20T00:18:36.514Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/ce/0984555c992a2695d01550282a88ef3951cc19b1cf3d5d1e8f0b501d6400/pymssql-2.4.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:cd434c8aad33b46a71509b8071e290e8c0a5cc16e061b4f16e1f7fc38c451034", size = 4227038, upload-time = "2026-10-20T00:18:38.225Z" },
+ { url = "https://files.pythonhosted.org/packages/ee/57/880e92954c2dba28ae12a5f1f05c1b7269df02aab35327a210ed00221de4/pymssql-2.4.5-cp313-cp313-win_amd64.whl", hash = "sha256:4f6eb7a883ac0a81fc6bd62b5b855627c539070c7731683f00df80fd344372ee", size = 2315376, upload-time = "2026-10-20T00:18:39.574Z" },
+ { url = "https://files.pythonhosted.org/packages/b1/e2/6032ab12af2e7cb71adcc98fe0b4241f0fd4cf5ff94cded3756faebbe66b/pymssql-2.4.5-cp313-cp313-win_arm64.whl", hash = "sha256:0122993af8d8ace54bff20f5b8e3c911b2cc5682331f9f952a5bd5d88f578ad9", size = 1839051, upload-time = "2026-10-20T00:18:41.284Z" },
+ { url = "https://files.pythonhosted.org/packages/04/f6/39c90b800b19ad2073861dd1bc5fcbb255ac2651bee3d54dc74de4b20c5e/pymssql-2.4.5-cp314-cp314-macosx_14_0_arm64.whl", hash = "sha256:515a5866a8feb0a3f8d518af6fd32240ce2d44d580e80027eb934d2d320b87d6", size = 3199285, upload-time = "2026-10-20T00:18:42.654Z" },
+ { url = "https://files.pythonhosted.org/packages/cd/b1/4144cb4b7034283b26b5035448983c0fead4c734176e4573de7288889ea2/pymssql-2.4.5-cp314-cp314-macosx_15_0_x86_64.whl", hash = "sha256:763b1a36b0aa230ffaf6625647fa289bee220a554a058876d2f308bc9945e725", size = 3005654, upload-time = "2026-10-20T00:18:44.269Z" },
+ { url = "https://files.pythonhosted.org/packages/08/7d/848e48879f9152e758ea7f51f4ca89e81cb7a5809a538ee318051b129356/pymssql-2.4.5-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:00665fc7457224c3ce0968742120118aeb73095741a1bc8e7e2b6eab0428b40a", size = 3098945, upload-time = "2026-10-20T00:18:45.554Z" },
+ { url = "https://files.pythonhosted.org/packages/dd/f3/2555a350f724d19d78177e9b30cad008a0a1b834d92e0ade17abfb4159c1/pymssql-2.4.5-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:84384770e186b8a815083d49b95881ccb594bfd1637f9977d209355f65546569", size = 3276985, upload-time = "2026-10-20T00:18:47Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/fc/3218bf5aec92695b96b1a4d7257ec5b3587621db4b958f305b7e57755356/pymssql-2.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9d7b7aa9a1344109657839ede7b7ba7f32d439ff3a4dbaf92dbf9b7512fa2054", size = 4490170, upload-time = "2026-10-20T00:18:48.381Z" },
+ { url = "https://files.pythonhosted.org/packages/74/04/afd87e617eda6664411452a6116fe910ea0275e396af9790099b689f08ad/pymssql-2.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:fc169c52e55e94321696a79f9a664480d908b917364f04d4a9f7f7f85892146f", size = 4225176, upload-time = "2026-10-20T00:18:50.94Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/b8/32c81774d55633726bd5769370b30e717921e34f82bf583bfc8cec821e37/pymssql-2.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:71d8207eb4d4fc887ceabd0bcd325121b8563d76bdf0710b80f0ed9ea44aef06", size = 2377940, upload-time = "2026-10-20T00:18:52.273Z" },
+ { url = "https://files.pythonhosted.org/packages/df/88/08f429c4221c8723adc0d9945f34c1eecd1ec9889e6750c9aa1ace608b61/pymssql-2.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:d802451b025fca7856faab16591de54e59fbeb4d9d8ffd1f940c18c916fac9b8", size = 1908759, upload-time = "2026-10-20T00:18:53.583Z" },
+ { url = "https://files.pythonhosted.org/packages/db/19/abb08f7d5539d2f7211cf8f98091d430e4b0eebb7029bf1bb81572034afa/pymssql-2.4.5-cp314-cp314t-macosx_14_0_arm64.whl", hash = "sha256:4095d1b6a230940dd34b1c2db870c1b1051d893e7c7a62c832e23bf2d18b5ceb", size = 3227283, upload-time = "2026-10-20T00:18:54.897Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/d7/bc8d98491c42d13dae0b78bf72d0a62d1a563b0c363524fb4b72c1489386/pymssql-2.4.5-cp314-cp314t-macosx_15_0_x86_64.whl", hash = "sha256:7148cacb61e4faffd115a946d2e9ee2e763b821c49277350fcc44703bc78549d", size = 3020500, upload-time = "2026-10-20T00:18:56.571Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/53/31647de8e1f7ba10ec60d7ada60aa09e8e224eb9f0b2e1e1a40a27b92d90/pymssql-2.4.5-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:61d0ff45ad3b5dc204d0fca928e654fc99ce50ef5ac65c33ae98ceb3ee8173a3", size = 3093132, upload-time = "2026-10-20T00:18:58.269Z" },
+ { url = "https://files.pythonhosted.org/packages/26/97/90a4d8c785f1b055f306db601076022fde2389bd9170642a8a08bb53770b/pymssql-2.4.5-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ca142acd30fdf84bb4e19adf4fe732e31dd7f34557fcc8fdc11d001cf42e3d60", size = 3273179, upload-time = "2026-10-20T00:18:59.838Z" },
+ { url = "https://files.pythonhosted.org/packages/a0/2f/52b93a66a51558c0b523b3e1c67eae08e7e91cde8048b4c7fd05c6744318/pymssql-2.4.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:fc0163a6f89fc53b2dbd2a11c2c500f52266a38257cac0a8e8dc7f562a4c9221", size = 4485244, upload-time = "2026-10-20T00:19:01.104Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/3c/f557765e794846c1d5065b6e2e3fe96df08df2b02aee03cb1f89f1437bd4/pymssql-2.4.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:04050fa1d5f68ff37ead859f877bc4f217240d20f8f770d5b694ac09cf664c3a", size = 4223450, upload-time = "2026-10-20T00:19:02.547Z" },
+ { url = "https://files.pythonhosted.org/packages/a8/cc/9c7640d39a5aed4bd6cd83a594ad8b282174ab789edfd9253574c03e1f78/pymssql-2.4.5-cp314-cp314t-win_amd64.whl", hash = "sha256:2f8441656e016a0f3cf0e4181efc054f41056b11ffd54cb774ee0e8dbdbf5a2f", size = 2396761, upload-time = "2026-10-20T00:19:03.954Z" },
+ { url = "https://files.pythonhosted.org/packages/d5/50/cc975d4aa3adcbba3a0610b59fe86f01b0094025f4c91deb6d263a5fbf8a/pymssql-2.4.5-cp314-cp314t-win_arm64.whl", hash = "sha256:1e5369709fbb82fa710a5e1f7ea67a4acfa17ba61f86c7be6384d3cbd9a35350", size = 1923588, upload-time = "2026-10-20T00:19:05.407Z" },
+ { url = "https://files.pythonhosted.org/packages/01/3a/2c31ea8f700254d86cd040d5373001c82339f798c589cc58a1415a3fea38/pymssql-2.4.5-cp315-cp315-macosx_14_0_arm64.whl", hash = "sha256:f723cfada20b6679e48cc8fc8ad5b9caaf3467168d622ba83e3ec8dbee239431", size = 3198417, upload-time = "2026-10-20T00:19:06.682Z" },
+ { url = "https://files.pythonhosted.org/packages/33/f5/28e66eae4fe417b976b894d8fdc4afca72cc8ae19da3e638433c14682e6f/pymssql-2.4.5-cp315-cp315-macosx_15_0_x86_64.whl", hash = "sha256:dab1cde0a1ccba53eea96d0156528012d06e442c70bafe9605828ec41ed9bc66", size = 3004519, upload-time = "2026-10-20T00:19:08.048Z" },
+ { url = "https://files.pythonhosted.org/packages/ec/54/49b39f6024924db86388d0da46c3335d24cfef88acac971b61a462b01494/pymssql-2.4.5-cp315-cp315-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:372746a18767b017715fee03da4151978087841edf04e9b45d7823e7dae5e8a9", size = 3099426, upload-time = "2026-10-20T00:19:09.581Z" },
+ { url = "https://files.pythonhosted.org/packages/49/17/413861f098dca28193fb1a9b0f42777e7d94dd34384968d02896000e06a0/pymssql-2.4.5-cp315-cp315-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aa7463e48274d1b5cdaf882a6e9ba3ae06807d451819351373398a2f56e218f7", size = 3277041, upload-time = "2026-10-20T00:19:10.945Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/14/9a35456242fafabb07c33dab7a39cb8d69ff73296bb3c7947abed717f11f/pymssql-2.4.5-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:09dc94f8a13c2b332e5aab768682ea64ef05c50434498274812d8a94b0a80c1f", size = 4490455, upload-time = "2026-10-20T00:19:12.429Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/56/bf6888c70270862302f4d186e83995261e1072fecd60dd1c84bdc25b5cd4/pymssql-2.4.5-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:5818445136cc3180f0e92f3ab081226344694e247de4ae57b38ba0126a6d760c", size = 4225373, upload-time = "2026-10-20T00:19:13.853Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/f5/f859311836c84c79138646fb0ebaac359644318eead67db03505c6e736c3/pymssql-2.4.5-cp315-cp315-win_amd64.whl", hash = "sha256:357a7925d2a1f3aeede56225c177b1aa9685f346afae0bdab0a2b794951f3245", size = 2377625, upload-time = "2026-10-20T00:19:15.223Z" },
+ { url = "https://files.pythonhosted.org/packages/c0/13/f7bde703726eb7168fd53f1308e4e179dd18d1fefb5393862880345b0eae/pymssql-2.4.5-cp315-cp315-win_arm64.whl", hash = "sha256:93e68273e3e2807740dfe6a99bc4bf3876d5417b6ca2dd95632839d0c73f00b4", size = 1908218, upload-time = "2026-10-20T00:19:16.89Z" },
+ { url = "https://files.pythonhosted.org/packages/73/67/dcd7e6395cccc9d3a148667248b589dc256a583058d81febcebeedf2f9b8/pymssql-2.4.5-cp315-cp315t-macosx_14_0_arm64.whl", hash = "sha256:a4d5e230468bc882388ec9a37e185a43de45d5182940c2b85e7ca25bad516828", size = 3226657, upload-time = "2026-10-20T00:19:18.129Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/f4/0835bb557210f16ee5eb01bda713891f077c1f730d0a241114c54d09c79d/pymssql-2.4.5-cp315-cp315t-macosx_15_0_x86_64.whl", hash = "sha256:49ed1b078760c41f7ff632174c0531e825dcfca2b2d541236c5547a9266ada63", size = 3019612, upload-time = "2026-10-20T00:19:19.454Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/ab/a60984fe167f2a96855095cb51c1764398c7b60f822e7c1251d4fb837be8/pymssql-2.4.5-cp315-cp315t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:aa63dbddddc73a2487cabea1c7e18a32bd7043f4ae5af2ade414b580c8427c2b", size = 3092609, upload-time = "2026-10-20T00:19:20.997Z" },
+ { url = "https://files.pythonhosted.org/packages/1b/42/69c782c0512d90421454ffc22a6c7a9b3633ede160977c7313cf56395e70/pymssql-2.4.5-cp315-cp315t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8ea3f28dcc8e54beee478c0d4c2d92227ac279a46f60f4f1dafe7abf84b82141", size = 3272590, upload-time = "2026-10-20T00:19:22.41Z" },
+ { url = "https://files.pythonhosted.org/packages/01/b4/f7fc61acb4e8b9b3cb334ea5fdf9ed614282de2f7fbc3c502bb5171afaa6/pymssql-2.4.5-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:2c91ac0938f2bf6702e36e00791b40535c6ef13f870407472ce1b0583666b3c0", size = 4484295, upload-time = "2026-10-20T00:19:23.81Z" },
+ { url = "https://files.pythonhosted.org/packages/18/63/d22117e3d5a00b3190e324ea71655af9948041f6c20f3d412bfa84bc0994/pymssql-2.4.5-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:c30fe08a7ae5f412758bff2f256bcb92bde9bb2d06f44eabee9b7780fb5119ff", size = 4222807, upload-time = "2026-10-20T00:19:25.496Z" },
+ { url = "https://files.pythonhosted.org/packages/36/1c/475e602ea569836312d2e8aa9934e9a9123b47b987a4aa2893ff6e09038c/pymssql-2.4.5-cp315-cp315t-win_amd64.whl", hash = "sha256:d0b56fe1dc3925487d47a74dcf02a28ed0b58ae19ee9dc2a0b1c5e93f95266c5", size = 2394303, upload-time = "2026-10-20T00:19:27.756Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/d8/b2b2cfa13e80efbeb7789fdedb55941df51c56ff46a85add7bfa9df80d2b/pymssql-2.4.5-cp315-cp315t-win_arm64.whl", hash = "sha256:9e7de3a928d495b629f8f0b341c90746ba5e8e8c7b8d45d46e81a8e2c6e3db0a", size = 1921599, upload-time = "2026-10-20T00:19:29.296Z" },
]
[[package]]
@@ -5031,7 +5036,7 @@ wheels = [
[[package]]
name = "pytest"
-version = "9.2.0"
+version = "9.1.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
@@ -5040,9 +5045,9 @@ dependencies = [
{ name = "pluggy" },
{ name = "pygments" },
]
-sdist = { url = "https://files.pythonhosted.org/packages/79/42/bb4641652e58dedeac2ec51abc1f8cae4fee4ec63899a0999016229a0f2a/pytest-9.2.0.tar.gz", hash = "sha256:97c4f3b1f3c7414aa36465e09b8220f1d03627993bd51b1282e193753c80557b", size = 1797555, upload-time = "2026-10-27T21:30:20.728Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/e4/47/b9efed96c114afcfa3c9d3fe98a76a1d14c74a9e266d397cf6eb64be5e01/pytest-9.1.1.tar.gz", hash = "sha256:1088fbde8f2b49d95a549a195707afa7a76a3ce9bcadc26b6d71f0ffda5fe313", size = 1636369, upload-time = "2026-06-19T10:58:32.857Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/51/39/1f5d8c8877a0fa327db959c19341d4be61660de1ab6b7391f9eb805bcd67/pytest-9.2.0-py3-none-any.whl", hash = "sha256:07acdcf88ec174f907dc01af70bf52d49b014bbab03faeeade31c96b18c2e588", size = 411776, upload-time = "2026-10-27T21:30:19.131Z" },
+ { url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
]
[[package]]
@@ -5151,11 +5156,11 @@ wheels = [
[[package]]
name = "pytz"
-version = "2026.6"
+version = "2026.5"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/f5/b0/168030b0bfcc334cf09fa3313afdb888d892c8a0027324683d1de0ec277e/pytz-2026.6.tar.gz", hash = "sha256:3b0fc2727707663d6e517936f444fd8a8b240f1fc32f4bf7ee03274245945284", size = 318520, upload-time = "2026-10-29T09:05:47.011Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/14/21/d83d6ef28c4c912c4bb4d1dcf591f7b8c6bde87b9c66f9f454677314e16d/pytz-2026.5.tar.gz", hash = "sha256:fa23724b9c486543b9ff54a327ee7569ac83ade54bb9afd0fc18676620401c86", size = 318572, upload-time = "2026-10-04T02:37:58.719Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/b6/25/564f3fc5b70c1398d706c51f2b1470d0ea0b1b2f96fee67877b6f5bdf3b6/pytz-2026.6-py2.py3-none-any.whl", hash = "sha256:7fed3fee8fd6bd4a2b7b75a8d56021d0624127bf69bd5691f115db929ac570b3", size = 506321, upload-time = "2026-10-29T09:05:45.14Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/ef/c66110d46fb800dda0bf33164182dfadabe26a90e4476844d502a23dca8e/pytz-2026.5-py2.py3-none-any.whl", hash = "sha256:e658af3757f9e26a9d25dd2aff38335acd92bc9104f890a894b2c1ba28311b03", size = 506342, upload-time = "2026-10-04T02:37:56.814Z" },
]
[[package]]
@@ -5465,27 +5470,27 @@ wheels = [
[[package]]
name = "ruff"
-version = "0.17.3"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/59/73/81f140c4edd9cc323de5478e6bbd3bbcfae9a82692df1a258de8cec6a6d2/ruff-0.17.3.tar.gz", hash = "sha256:a57049c8addb3662073859bb55a7ad35aa54f8a7c9ed268911d1333c5aadaaa9", size = 5339444, upload-time = "2026-10-27T00:34:05.914Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/9e/8c/083365310c4e79c842f25bf5a842facbd9b0c0ec336f96dffa5074fe723d/ruff-0.17.3-py3-none-linux_armv6l.whl", hash = "sha256:ab86b84cd9d05e56d329742c0cabdcbe2d1d56c98f4a04aab9b5b66196febfb4", size = 10216522, upload-time = "2026-10-27T00:33:29.822Z" },
- { url = "https://files.pythonhosted.org/packages/72/26/4b2edf31cfebe9605d8f6ed8f1d6f376d3a53d4c9a123e62661c64c440f0/ruff-0.17.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e407b4a6852853a9a2cdce96963e94ad9775578bb44e7561a0f2829f3cc04b11", size = 10217786, upload-time = "2026-10-27T00:33:33.949Z" },
- { url = "https://files.pythonhosted.org/packages/a2/7e/537aba4b7f54397a374d2f6a6fc93e9c8015e18553f31a4a7b7dbced48b5/ruff-0.17.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:b75cc113b3aca388933d8296b667b1ac401a80317ea3074c9b0a66541199da29", size = 9440362, upload-time = "2026-10-27T00:33:35.784Z" },
- { url = "https://files.pythonhosted.org/packages/3d/66/283e39734f66c8a5ebaa93c22eb7b7515779a0b1d5a5c0ad20e05c0edb41/ruff-0.17.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ab5fa7ed5ce18554ea69d850ad162cbaa0c85fc47c08d83a569f806681c9a6ad", size = 10094281, upload-time = "2026-10-27T00:33:38.216Z" },
- { url = "https://files.pythonhosted.org/packages/21/4c/1ffc768497bd025a32c72152771e40c0b99bddc5cc6e4f50c7949a395346/ruff-0.17.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c673c170e27834d1262e3a9c1e97bdc9b9c3b180c63d89173ce69747232474c1", size = 10022543, upload-time = "2026-10-27T00:33:40.181Z" },
- { url = "https://files.pythonhosted.org/packages/73/c3/f90db547301080fcb82c92abf914c8bd57b89911d9b65a6c4cc6b52d0635/ruff-0.17.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6a754c0341bc9ca151b7b335988b3263921c08859f84ca219b70cee825571b6f", size = 10683921, upload-time = "2026-10-27T00:33:42.182Z" },
- { url = "https://files.pythonhosted.org/packages/e5/98/92d4a8a26c6c96cf1a5b34dd59036d8a71e52d650aab065c58867e964e63/ruff-0.17.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4590b9d86c0b1543ebb09094254e191cd43fe8edaf330baa035aab03da251532", size = 11373408, upload-time = "2026-10-27T00:33:44.19Z" },
- { url = "https://files.pythonhosted.org/packages/43/02/1f24d2e3064c9753926deeb901afa57777b37ee46799b09ddd7aee6e5145/ruff-0.17.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:bbde18beccd98c29a227c63c99022e3443b99258e08326df6425dfe0443705ef", size = 10977469, upload-time = "2026-10-27T00:33:46.179Z" },
- { url = "https://files.pythonhosted.org/packages/f0/20/fecedea2209f574ab92033b344e01a7f0c2dc76c6d6d19a14c9f8141173f/ruff-0.17.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7fa4b0f931cdbcb8a52044f370a3eb429fb42eb88807b0e800fcaa98dd42b24e", size = 10577491, upload-time = "2026-10-27T00:33:48.233Z" },
- { url = "https://files.pythonhosted.org/packages/33/36/b93fdf0bc6f84f0cd487aadfb0354deb40013669ea35ac3d26855de1e959/ruff-0.17.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:39405f341deec934b4289634324ad695f72df2970f9121582872eaf5b722f0f5", size = 10608748, upload-time = "2026-10-27T00:33:50.284Z" },
- { url = "https://files.pythonhosted.org/packages/5d/b7/7d3f5f14145daead1efb0b9a8289ed1daa100f419d60774375951723ce00/ruff-0.17.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:68074094f55ca3d29ab19491f640282f7a84061c4c7977aa5094b968ca457e9a", size = 10075057, upload-time = "2026-10-27T00:33:52.074Z" },
- { url = "https://files.pythonhosted.org/packages/b0/ed/93ff7a5909ee56e2bd4b982deae9e0efb2724417874a90c9e41ad66c6dd7/ruff-0.17.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:27bb571ac78fef178fe5118d2827df95c2aae74a48fff2b15f6acd03af5ff5bb", size = 10022431, upload-time = "2026-10-27T00:33:54.061Z" },
- { url = "https://files.pythonhosted.org/packages/9b/07/a83b9eeec8c1c3c0e4eda15f06c3a91001bfd2687ea2f07b750719bf9712/ruff-0.17.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:029c37cbd3de48ca37aa12117bead2d065f15da17d66bf84e0d4274013705db2", size = 10394903, upload-time = "2026-10-27T00:33:56.158Z" },
- { url = "https://files.pythonhosted.org/packages/af/b2/05c39857c2526fcbebe46ad93a8dc97424656dfef14d3ef933abe1ffd331/ruff-0.17.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:692fdb46d0fd9c71e380947dc5fc72f2c3f881073bc1f739e4579376231affbe", size = 10799780, upload-time = "2026-10-27T00:33:58.204Z" },
- { url = "https://files.pythonhosted.org/packages/ec/d9/47171a16739eb7a353936b31c20cf54340531e68c9af7d72fd7a784e235d/ruff-0.17.3-py3-none-win32.whl", hash = "sha256:46539cb031b01fd29ae0a02142483c46640b233ee12bb1c1b690bffa1555d5ca", size = 9908481, upload-time = "2026-10-27T00:34:00.087Z" },
- { url = "https://files.pythonhosted.org/packages/d0/a0/f08c1631bfd81faa6a90eaf70c37c786c11bed2046302b56bb506c25e1a0/ruff-0.17.3-py3-none-win_amd64.whl", hash = "sha256:56b8ffb3f3b3cbccb8b76b71b38261b7ed982ef5d20f5b447b1b61a7f4949b24", size = 10341333, upload-time = "2026-10-27T00:34:02.099Z" },
- { url = "https://files.pythonhosted.org/packages/99/d5/4ad4ed31413c301430918e70d3d38ae6f097af0766cb9ab8c9d502b2a4d6/ruff-0.17.3-py3-none-win_arm64.whl", hash = "sha256:81fce74bcd6be0e9da7c67c7553fd0f1f12964a2052cd728ef917b937a5ca1d9", size = 9854332, upload-time = "2026-10-27T00:34:03.847Z" },
+version = "0.17.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/ae/58/690cf56da80c8f2c8bf12042b6017beac2c34fe961f65420bcb3fb7fac38/ruff-0.17.2.tar.gz", hash = "sha256:84d632402d14c23b488f941db23712d6736a013225162062d34558c3f3c780eb", size = 5333400, upload-time = "2026-10-22T18:38:00.181Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/98/b8/8b6d69c57e8fe1cc6af1aa7493b72a1a5b5a8afe34694e302025441729d4/ruff-0.17.2-py3-none-linux_armv6l.whl", hash = "sha256:96a65e755d8dba094707fb23ab602111d9a31ec2835b3a6992a1914a8fcb211e", size = 10183857, upload-time = "2026-10-22T18:37:14.956Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/61/12cc2ff7b2fa90a268ce1e2162c23aaf76e601d22623f6a31b0eeaa764b0/ruff-0.17.2-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:39c5b55c960515cdb32be16e69efb5734c589c06a0eb38a22694524a336b889d", size = 10210904, upload-time = "2026-10-22T18:37:17.985Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/7c/b8850a8e4ad29f64a827dd6c6902db4a9aa538fb2b9eef4e26084001a3bb/ruff-0.17.2-py3-none-macosx_11_0_arm64.whl", hash = "sha256:92a80d2f44584dabb0189b3a6c393e37e525e04942838482010575cb81945be7", size = 9408439, upload-time = "2026-10-22T18:37:20.964Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/b6/6a64fae86ca5e5e29d5adc311e5481de726cedf9aee607435a16c31c442a/ruff-0.17.2-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7d0906677d89bbf3cf73cde64b5bbe2ab35df3ae43e49ee2627301e91265bac", size = 10047078, upload-time = "2026-10-22T18:37:23.717Z" },
+ { url = "https://files.pythonhosted.org/packages/10/19/23d376547715175aa1bd46df48df6aacc7c6439d9daf19cac15975180aa5/ruff-0.17.2-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7cfd783590edf2b77317882631900f6a13ea2e8eacefe6ebf2370d2715299160", size = 9990478, upload-time = "2026-10-22T18:37:26.584Z" },
+ { url = "https://files.pythonhosted.org/packages/93/6c/34cda32cbe13adac5c6b291a307a5570d8ebd37c0c86339f0fe628e55632/ruff-0.17.2-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:122258f6ecde0e0dd1bb60b24263c3edde4338caf723fca711cc729f9bfd414a", size = 10673624, upload-time = "2026-10-22T18:37:29.767Z" },
+ { url = "https://files.pythonhosted.org/packages/bf/10/5d762a4dbc851d8bb47863cf3a28a61bf31c804d393b70d891fac899f5d1/ruff-0.17.2-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7f8fba429d6ac4128ab29cfb8fa6240d2fa671d04adf7f84d3b22fc7b6b6bea4", size = 11351797, upload-time = "2026-10-22T18:37:32.535Z" },
+ { url = "https://files.pythonhosted.org/packages/3f/04/a0e92a81faf5c81c1c98d4374c8401734ccf2b8cca7d0170cb0fec08c6e2/ruff-0.17.2-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:583bd67bb9e7061dd09bc46a0a884bae2bbd214dbcd48e509813612619683869", size = 10949414, upload-time = "2026-10-22T18:37:35.501Z" },
+ { url = "https://files.pythonhosted.org/packages/c3/d8/59894cf630d2d86882457a2a6a0a1533410c5c805bfbac6f6eb4b5aef69d/ruff-0.17.2-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c60f93fec7142dba5b8f487f94bf2f6231dae24dc11e4c48648347c8b0720004", size = 10528000, upload-time = "2026-10-22T18:37:37.88Z" },
+ { url = "https://files.pythonhosted.org/packages/ab/95/d6e30c51e851d37e10762ad91fa1e76ddaad998589b5667962a3cfbc9edd/ruff-0.17.2-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:c555a877f8f41447e318101cfefea9bf12dfa9f82eedf90c376e57e069f917d0", size = 10571779, upload-time = "2026-10-22T18:37:40.444Z" },
+ { url = "https://files.pythonhosted.org/packages/f4/5a/a7e1d74fb464250137d3d4eca645d323536533b672004d04ee0e46e0caf3/ruff-0.17.2-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:ba405cbd5ec7bda1f555d5e52be1fd68d02608d4d95d24591bdac92cfacb579f", size = 10051707, upload-time = "2026-10-22T18:37:42.812Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/f2/02fca29ca7999380f0350a7212fe755c11ab9c22d61327356cce8decdbc6/ruff-0.17.2-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:8c21b4ab24485358f17291a4f8dc5399b834e963b8efedd920fe4fdb02081bb4", size = 9998227, upload-time = "2026-10-22T18:37:45.224Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/00/55aa604a093dfdc1526e570fae0bb7d37a2221c7b4728d5f080b2add16ba/ruff-0.17.2-py3-none-musllinux_1_2_i686.whl", hash = "sha256:5875a16b0de70b43cb81eabea5ae5e8f5d1b2fd1b5e6ed07f80cfe5019d0bd5a", size = 10377692, upload-time = "2026-10-22T18:37:47.704Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/5a/a7a640f47bb906ec8fa233ea84319d503e6dee02b59e3901bd312ebd1f9b/ruff-0.17.2-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:2d086be452eed686aa51271ecf2cbe5037956517cc6ee39a17b4489ac12f8bbe", size = 10790587, upload-time = "2026-10-22T18:37:50.191Z" },
+ { url = "https://files.pythonhosted.org/packages/2c/d4/c3b74e1b4808438ea7f14bb914478025e2d51261f60774a54a0fa222ec46/ruff-0.17.2-py3-none-win32.whl", hash = "sha256:6bce7d1f5165701ae18f5df85c65afe78cddfa6c5fb4b9bdb0014d60388f3edd", size = 9870537, upload-time = "2026-10-22T18:37:52.486Z" },
+ { url = "https://files.pythonhosted.org/packages/1c/9a/c7da96c2c96d20704cf9027f7a5121da272a8c05f1d1531ba949e3b3df04/ruff-0.17.2-py3-none-win_amd64.whl", hash = "sha256:1b59d447f197bef9ef48a22ba1a6016137753c6099e9bac9c36eb566e422424c", size = 10282826, upload-time = "2026-10-22T18:37:55.2Z" },
+ { url = "https://files.pythonhosted.org/packages/81/60/a3604c79cb02777b6cb84da894104021986f06c4bc41375102cc95049a32/ruff-0.17.2-py3-none-win_arm64.whl", hash = "sha256:195ad918027f817c119310ca89a42d347887dfc28934aaa869325e9c5dc9d962", size = 9827036, upload-time = "2026-10-22T18:37:58.031Z" },
]
[[package]]
@@ -5668,6 +5673,12 @@ asyncio = [
{ name = "greenlet" },
]
+[[package]]
+name = "sqlanydb"
+version = "1.0.14"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/94/17/23328823ebba935891957a2f40a36fd54bee570ff2c0da6df17a17041b0d/sqlanydb-1.0.14.tar.gz", hash = "sha256:febda78d52c6828d9641238bdd78361cb0e6b48040a15926b737429e31c32637", size = 21456, upload-time = "2024-03-11T20:55:22.619Z" }
+
[[package]]
name = "sqlglot"
version = "30.27.0"
@@ -5724,7 +5735,7 @@ wheels = [
[[package]]
name = "sqlspec"
-version = "0.69.0"
+version = "0.70.0"
source = { editable = "." }
dependencies = [
{ name = "mypy-extensions" },
@@ -5860,9 +5871,15 @@ sanic = [
spanner = [
{ name = "google-cloud-spanner" },
]
+sqlanywhere = [
+ { name = "sqlanydb" },
+]
starlette = [
{ name = "starlette" },
]
+sybase = [
+ { name = "pymssql" },
+]
uuid = [
{ name = "uuid-utils" },
]
@@ -6045,17 +6062,19 @@ requires-dist = [
{ name = "pydantic", marker = "extra == 'pydantic'" },
{ name = "pydantic-extra-types", marker = "extra == 'pydantic'" },
{ name = "pymssql", marker = "extra == 'pymssql'" },
+ { name = "pymssql", marker = "extra == 'sybase'" },
{ name = "pymysql", marker = "extra == 'pymysql'" },
{ name = "pytz", marker = "extra == 'duckdb'" },
{ name = "rich-click", specifier = ">=1.9.0" },
{ name = "sanic", marker = "extra == 'sanic'" },
+ { name = "sqlanydb", marker = "extra == 'sqlanywhere'", specifier = ">=1.0.14" },
{ name = "sqlglot", specifier = ">=30.13.0" },
{ name = "sqlglot", extras = ["c"], marker = "extra == 'mypyc'", specifier = ">=30.13.0" },
{ name = "starlette", marker = "extra == 'starlette'" },
{ name = "typing-extensions" },
{ name = "uuid-utils", marker = "extra == 'uuid'", specifier = ">=0.17.0" },
]
-provides-extras = ["adbc", "adk", "aiomysql", "aiosqlite", "alloydb", "arrow-odbc", "asyncmy", "asyncpg", "attrs", "bigquery", "cloud-sql", "cockroachdb", "db2", "duckdb", "fastapi", "flask", "fsspec", "litestar", "msgspec", "mssql-python", "mypyc", "mysql-connector", "nanoid", "obstore", "opentelemetry", "oracledb", "orjson", "pandas", "performance", "polars", "prometheus", "psqlpy", "psycopg", "pydantic", "pymssql", "pymysql", "sanic", "spanner", "starlette", "uuid"]
+provides-extras = ["adbc", "adk", "aiomysql", "aiosqlite", "alloydb", "arrow-odbc", "asyncmy", "asyncpg", "attrs", "bigquery", "cloud-sql", "cockroachdb", "db2", "duckdb", "fastapi", "flask", "fsspec", "litestar", "msgspec", "mssql-python", "mypyc", "mysql-connector", "nanoid", "obstore", "opentelemetry", "oracledb", "orjson", "pandas", "performance", "polars", "prometheus", "psqlpy", "psycopg", "pydantic", "pymssql", "pymysql", "sanic", "spanner", "sqlanywhere", "starlette", "sybase", "uuid"]
[package.metadata.requires-dev]
benchmarks = [
@@ -6425,11 +6444,11 @@ wheels = [
[[package]]
name = "tzdata"
-version = "2026.6"
+version = "2026.5"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7a/29/6a1391d31cd00ff9e927215a224a880e1e84e826b64ba80a499e51c074d8/tzdata-2026.6.tar.gz", hash = "sha256:85d2cd9c09f81f0bb29db27759fc83af4f1e620efb814f8c5bf8b4cc4feece6d", size = 201017, upload-time = "2026-10-28T09:57:09.137Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/d9/68/f1b440335057bfce71b6e50a9d09445aa2ecbd08359a337976627b8409e7/tzdata-2026.5.tar.gz", hash = "sha256:8cc73c0a0bfca7dbfa59235d60b2eff82231dee33f53d206db1acd9173cfc0a7", size = 200404, upload-time = "2026-10-03T09:23:14.143Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/0a/66/9a06c2227fedfd4919f0eaaa8b94377d9730882a7e4f95823c9a331ab5e1/tzdata-2026.6-py2.py3-none-any.whl", hash = "sha256:0a10eec6e13ef16b627fabed9a5f2ba61a62b1c67f327305f4e7e412a70b4aec", size = 348210, upload-time = "2026-10-28T09:57:07.276Z" },
+ { url = "https://files.pythonhosted.org/packages/94/21/1e5995a1c920cce14e4bffae20c665ec10e7ed03ab25e006cd741092b718/tzdata-2026.5-py2.py3-none-any.whl", hash = "sha256:b683bd1b6659ddcd810ff02ad09ba821d4bf1065072805063eb35c49617905ac", size = 347996, upload-time = "2026-10-03T09:23:12.535Z" },
]
[[package]]
@@ -6533,62 +6552,118 @@ wheels = [
[[package]]
name = "uuid-utils"
-version = "1.2.0"
+version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/fd/01/baff7ef80f30885cca5ecfc0d9ea660b3d20c34dfaf0d17a79a95ec342d7/uuid_utils-1.2.0.tar.gz", hash = "sha256:8a8bd1120a8bc5a6656a1c580d66a050af430b05d017da526ec0b0a06715bf70", size = 43765, upload-time = "2026-10-26T11:08:50.423Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/e9/12/ea68ace50014a047a1fb08b66d9458e48ef132a36b9664101365fe262337/uuid_utils-1.2.0-cp310-abi3-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:325432c5db7cdf0fc94fe30265ffd8c8705d468b922e02d8d92b4bef91ab99ce", size = 576108, upload-time = "2026-10-26T11:07:31.114Z" },
- { url = "https://files.pythonhosted.org/packages/e9/cc/7e1cabaccad7bf6d4fabac10dab493c3ea72de6bd241331972ffaa692f21/uuid_utils-1.2.0-cp310-abi3-macosx_10_12_x86_64.whl", hash = "sha256:3ca80839df75b10b9d89a3ca9193e6df5500095c8efc80e5286d20ccc03746ac", size = 295123, upload-time = "2026-10-26T11:07:32.796Z" },
- { url = "https://files.pythonhosted.org/packages/b4/8a/00c19434487c8066eda5902a8456737f096d3fb9905bff8dd496afc80043/uuid_utils-1.2.0-cp310-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7637fe5b6c4bb77a3a973bc0abae32b566ef8415c92cebcc91492b186350fbb1", size = 329552, upload-time = "2026-10-26T11:07:34.281Z" },
- { url = "https://files.pythonhosted.org/packages/fc/72/ad0f4ce574b3535d22ceccdeb3e2bce165276319e52777a186b73f2de059/uuid_utils-1.2.0-cp310-abi3-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:37359f40ad4a143b80a71887a6e60d9c6d7841915585fcab04be7f0fe1bbc5af", size = 337553, upload-time = "2026-10-26T11:07:35.964Z" },
- { url = "https://files.pythonhosted.org/packages/26/51/59536f85f57870d4a5b02faadfd326e0a0b32b9ea2f8a8a4026868a21012/uuid_utils-1.2.0-cp310-abi3-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:64f4e98e1220a1323db656535309385fbcd9bd0e65a4c43d1fcc41b4b5b2e8b2", size = 442496, upload-time = "2026-10-26T11:07:37.389Z" },
- { url = "https://files.pythonhosted.org/packages/00/23/2b68f9ffa7da1b344140b4f013fc56270e1fe43310da815104e6db886e58/uuid_utils-1.2.0-cp310-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0a1ed18b454295be2a8990bea89b3385238356ff6790e4fc256731986f413b87", size = 331264, upload-time = "2026-10-26T11:07:39.126Z" },
- { url = "https://files.pythonhosted.org/packages/22/1c/8990fdf8bdc9d189f2ce30de0522ad085e2f3d4e5335044000e6199959a9/uuid_utils-1.2.0-cp310-abi3-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:180040a8f38c588bd50c0178736ddbb8c18cfd25d3dc4ed19ea6ede45ee8059c", size = 356084, upload-time = "2026-10-26T11:07:40.534Z" },
- { url = "https://files.pythonhosted.org/packages/9c/09/5de2448d6e7b113c0471055491c21841a94680811c571270ca2d89b6e3b1/uuid_utils-1.2.0-cp310-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:7b3ba967ac955414ca0ee1231de0b9693b6c454d72cbf43da05812bf18d71c7d", size = 505643, upload-time = "2026-10-26T11:07:42.013Z" },
- { url = "https://files.pythonhosted.org/packages/13/d1/fe68de666a3148b57e3736b04eb1997ceeb0b82cec4eb657676f393e1116/uuid_utils-1.2.0-cp310-abi3-musllinux_1_2_armv7l.whl", hash = "sha256:97134fdd3cf32f5432f8249630d2b7059caf8ac815e3316a05d2a1b7e1704144", size = 612085, upload-time = "2026-10-26T11:07:43.8Z" },
- { url = "https://files.pythonhosted.org/packages/e6/11/26b3c9dc8224e87e1c0581a190d130239f3a798287ad791dddbfd1437392/uuid_utils-1.2.0-cp310-abi3-musllinux_1_2_i686.whl", hash = "sha256:9939d3a0e94a34b5d3ff81293eff6d2fddc7704486ddb11e3d6251152fe1be31", size = 572063, upload-time = "2026-10-26T11:07:45.205Z" },
- { url = "https://files.pythonhosted.org/packages/7d/b2/29f5db24b4b0cb2c4e9e1472ae5969f9263c558e6abcbb6032334f3db978/uuid_utils-1.2.0-cp310-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:22bb149628ef34837c95f718657ad227221e2e8729267b99ae942ac6af22ab6e", size = 536932, upload-time = "2026-10-26T11:07:46.654Z" },
- { url = "https://files.pythonhosted.org/packages/d4/cf/fe1806c104a374158511647b0ba8cc2449a9629bf6925128f82e2b3266af/uuid_utils-1.2.0-cp310-abi3-pyemscripten_2025_0_wasm32.whl", hash = "sha256:a1b6725a9a0368f55f819e145b0f943e5491f7d5c4cb6089d62b240f4e3baab9", size = 104061, upload-time = "2026-10-26T11:07:48.153Z" },
- { url = "https://files.pythonhosted.org/packages/de/59/933b236fcb84c80d8095c31bd2a71528468ef8876c6749084045b81a1bfc/uuid_utils-1.2.0-cp310-abi3-pyemscripten_2026_0_wasm32.whl", hash = "sha256:10bf3925327dbc77a6bc4750bf6e117661335de3322df59a96d5c6118442e460", size = 103999, upload-time = "2026-10-26T11:07:49.494Z" },
- { url = "https://files.pythonhosted.org/packages/8b/03/e036bb6b577e25182bb1361a4e627f60e79cea48251d40d30d9952f11f29/uuid_utils-1.2.0-cp310-abi3-win32.whl", hash = "sha256:816b02212e09ddee97b42bb3e6f5926b238a46d49ef18d907062468d3ec56faa", size = 177530, upload-time = "2026-10-26T11:07:51.122Z" },
- { url = "https://files.pythonhosted.org/packages/01/26/4698de01c11b3fcf784e847f5245f52224cea4db37cbfa0a08ee76a1d38e/uuid_utils-1.2.0-cp310-abi3-win_amd64.whl", hash = "sha256:5d66aee34594f9bda7bfcb68f7db7ba9b3a89d5f221f8d42a8f538737ae1164d", size = 184093, upload-time = "2026-10-26T11:07:52.455Z" },
- { url = "https://files.pythonhosted.org/packages/ca/52/8ddd5a08f97ea254a1d2241dd7c2e6a826fd4010ae7cc35bfa3b5c9db94f/uuid_utils-1.2.0-cp310-abi3-win_arm64.whl", hash = "sha256:2819a38f3f0bb232edbc3ab134551bbefd6a5d6ec59084acdc1a6572f231cab1", size = 182004, upload-time = "2026-10-26T11:07:53.853Z" },
- { url = "https://files.pythonhosted.org/packages/32/7a/7462b83f5fe77429bffbb38f314b09d57730e54156c13a73a1cbc39aecd4/uuid_utils-1.2.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:e011e1b992eca52110f8489b82530a59ad8d97439e4483ed65e125f147959eba", size = 567239, upload-time = "2026-10-26T11:07:55.539Z" },
- { url = "https://files.pythonhosted.org/packages/b7/ae/f5ceb473d75080dc461914a58de790f1e8aff0c6b3f355fa1e8c837b6bdc/uuid_utils-1.2.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:806e1e37e77d95406699ac154ece22bedd2eeda41f5a5d45ab7c3b2f79bf9285", size = 292729, upload-time = "2026-10-26T11:07:57.128Z" },
- { url = "https://files.pythonhosted.org/packages/54/af/18acf6463802b403d1faeb8319a58e7daaec67fe136eb44979b69fbfa77a/uuid_utils-1.2.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:96b5e52ca1722ae0d3f03effd4b8063f0725a7cc9f760954e0f1c36ab52e9444", size = 324560, upload-time = "2026-10-26T11:07:58.684Z" },
- { url = "https://files.pythonhosted.org/packages/14/24/f0adc745da334caba87a5041bb1fe598347d8eaf35de89aa21b14fc57ab5/uuid_utils-1.2.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d55790277e8f9f6cba68bdff2be701a68987b1622cde56e50317c366fb8ae994", size = 329667, upload-time = "2026-10-26T11:08:00.152Z" },
- { url = "https://files.pythonhosted.org/packages/a7/f6/a45af3098af0e43c811212d8f2634f6b5959adfedd807e6086cdc2d54836/uuid_utils-1.2.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:71743b880787cf97934d609fe4561022bfb22713dd1e356622da3e7c06e348b9", size = 436256, upload-time = "2026-10-26T11:08:01.682Z" },
- { url = "https://files.pythonhosted.org/packages/cf/7f/1ca1cca384d65d5533fa2fe8bec9e5f6bc4e5de634582eb3b5a62e6dab33/uuid_utils-1.2.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdf993b4a92b0edd64f7ef46799d5ab07755c84781f2e5700a49a112f1dc1092", size = 326366, upload-time = "2026-10-26T11:08:03.104Z" },
- { url = "https://files.pythonhosted.org/packages/e1/59/537100eaf21796237afee5523823883e991b69b157e9a1ba224ac9d60298/uuid_utils-1.2.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:83af4a16175aa98f19e661d3eb9c210f4441723cdf477b959d7015111b653a54", size = 347030, upload-time = "2026-10-26T11:08:04.794Z" },
- { url = "https://files.pythonhosted.org/packages/0d/84/de0aac9162713c233cbc0cdcd5198e2e342373eab3c18743fbc71c4e5283/uuid_utils-1.2.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6dedd223c46187017a02d64df9734c78fe5460b6a6e21912b48abcf6388f2934", size = 502312, upload-time = "2026-10-26T11:08:06.234Z" },
- { url = "https://files.pythonhosted.org/packages/ce/9d/fb3624fd335d97e1a99be8498b4ec6784f35ce6f5f9f6d96e9e12467afea/uuid_utils-1.2.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:79cd62145b2575c08946c9f3dafed7418ab590bb847b50d8c60acd109e0bbd6c", size = 605106, upload-time = "2026-10-26T11:08:07.847Z" },
- { url = "https://files.pythonhosted.org/packages/3b/3a/c5e80c63ae7330c541312c282b879da8a89e269cf91a9923771aa22e52d8/uuid_utils-1.2.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:5632d40fadae4a0856d5f6bc30612833b807589e8c804e3d479110fb284493ff", size = 564964, upload-time = "2026-10-26T11:08:09.467Z" },
- { url = "https://files.pythonhosted.org/packages/fd/9e/b977028fe182e797f196dd0f0a9fd62ff2a2c21a61011bcf9009f6137e59/uuid_utils-1.2.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:555933b63e520aa52d092aef7a0c43577da1879568b35954a977f86f5eb741cb", size = 531699, upload-time = "2026-10-26T11:08:10.955Z" },
- { url = "https://files.pythonhosted.org/packages/b4/4a/676338df4204eb88658a316b7a492fbce9866d0dc5f85f573197505886b3/uuid_utils-1.2.0-cp314-cp314t-win32.whl", hash = "sha256:1941c938ed88406b3866361c0069adb712b8b54782600f6416f759d30ab09ad7", size = 173619, upload-time = "2026-10-26T11:08:12.865Z" },
- { url = "https://files.pythonhosted.org/packages/7f/2f/1c76cd5ab1a7c4327bbd1e7e26c42d8f4cc3ab1267691f5f373c26cdd653/uuid_utils-1.2.0-cp314-cp314t-win_amd64.whl", hash = "sha256:0610e92b6b8b4753803c1828e2341627d000c63cdf575259a25b2346a7a8545c", size = 179326, upload-time = "2026-10-26T11:08:14.398Z" },
- { url = "https://files.pythonhosted.org/packages/7f/1a/bc306695a03dcfa5f1584dc8dd43b7c74ac991a654cce21a435f45603fb9/uuid_utils-1.2.0-cp314-cp314t-win_arm64.whl", hash = "sha256:c55216f949fffbf684c6f8f8ea4c882f6dc6766c81a6a5cb42a0e7ca09c4b234", size = 177336, upload-time = "2026-10-26T11:08:15.85Z" },
- { url = "https://files.pythonhosted.org/packages/03/a0/3f202d555461635c879c032cfd26990d78b5cfd7b4273409f94ee06d4d59/uuid_utils-1.2.0-cp315-cp315t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c7d3749a071889be8610f02fd4bbbfa1eb24f5d7f32b6ae1fb653c1b4c09a39e", size = 568287, upload-time = "2026-10-26T11:08:17.245Z" },
- { url = "https://files.pythonhosted.org/packages/e5/89/239c462fd5ee61b56f2c7d18af093a5f5a0cde09194286f694797cb3e85d/uuid_utils-1.2.0-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:4532e404f60e2ebe5d75104ae7d1f79f01cda6b0ba169170b11c02446012395a", size = 293016, upload-time = "2026-10-26T11:08:18.86Z" },
- { url = "https://files.pythonhosted.org/packages/43/63/56d0ed7386ffea5e25a98abdf88f01f1f933530ad24726010396956a0edf/uuid_utils-1.2.0-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6bafad0526ceb1901252f38509609ee6e8b7cd358e7b5eed4e3cd30dcb2d2f41", size = 324511, upload-time = "2026-10-26T11:08:20.441Z" },
- { url = "https://files.pythonhosted.org/packages/0d/f1/b7b6859d338ea36014d0b77b9de41fa803757040db7598cd278d9f4e6586/uuid_utils-1.2.0-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d1d327972b03f44d5c5f25bc479ed6b3672eb749d56d78738bbdb8b849df684a", size = 330148, upload-time = "2026-10-26T11:08:22.182Z" },
- { url = "https://files.pythonhosted.org/packages/51/b5/f689bae190a2b04d0f3c7cab7cf040e02f19729fddaa2acbeef67a13e397/uuid_utils-1.2.0-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:89983195942a3d8469df4ef79a4ad1f5d2f5ea26c518206daae8c42a85ff781a", size = 435497, upload-time = "2026-10-26T11:08:23.671Z" },
- { url = "https://files.pythonhosted.org/packages/4c/e2/279f01c7fe040df3e5d6329abd4f2d31570f612d79b4003c8edaec9a2129/uuid_utils-1.2.0-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfdafb0f430718f1fb56695e3a5b6bec06c5305797027653d568135be89f3109", size = 326704, upload-time = "2026-10-26T11:08:25.09Z" },
- { url = "https://files.pythonhosted.org/packages/13/64/61653bb35b673d5509838c804656a00f3fae17c1869d35ab000183bf136b/uuid_utils-1.2.0-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:71fe514ba633707496d7ba21e2ecf87eb3c54787094eddc36227e661ad697823", size = 347816, upload-time = "2026-10-26T11:08:26.554Z" },
- { url = "https://files.pythonhosted.org/packages/50/21/48a7ef79de05bc343cc3f5b1ce5fd27036a04a1e7bf6366427623a5e91bd/uuid_utils-1.2.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:605f05c783cf9a18dea7cb65fd6028748de7eab355979988af034e93c035486e", size = 502417, upload-time = "2026-10-26T11:08:28.407Z" },
- { url = "https://files.pythonhosted.org/packages/63/38/44838b437142cd443be9f51c4871f2075ace06bae8916890285327972142/uuid_utils-1.2.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:424b7bc4d0f59b8a5028db97b44ca4693c08da2aeb1a83f27742804a9f2c43eb", size = 605752, upload-time = "2026-10-26T11:08:29.899Z" },
- { url = "https://files.pythonhosted.org/packages/60/6e/29adbf56b65c2c93b9bab6316ca234fb7ced751aa64352edd5d35cb29eac/uuid_utils-1.2.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:e1fa69a90aa1c722a778f08d9c358cabbd2be6ae9c000c300c93540ed3455f74", size = 565349, upload-time = "2026-10-26T11:08:31.33Z" },
- { url = "https://files.pythonhosted.org/packages/77/31/c75755cbfc4955ef019c6eb16feefff22139c59b9a4093d51ae75704847d/uuid_utils-1.2.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:0a49728a9b9242bb6e0298b1cae6ab132befa88115d98b492e0ac68b9db41dba", size = 531585, upload-time = "2026-10-26T11:08:33.071Z" },
- { url = "https://files.pythonhosted.org/packages/e9/7d/a8e91b157787b4ed4b498a7ed09d18baa8fc4e6b1828662db188b7f3276e/uuid_utils-1.2.0-cp315-cp315t-win32.whl", hash = "sha256:c52cfc3388069a6dc787d9100815d298a9d8be8c37835e4d039af5e3cc048409", size = 174033, upload-time = "2026-10-26T11:08:34.49Z" },
- { url = "https://files.pythonhosted.org/packages/31/a0/35786696cf1abb7fdb2711e2421bdf650b075eee1a971040d62148a0f0a2/uuid_utils-1.2.0-cp315-cp315t-win_amd64.whl", hash = "sha256:0ccace291105dadedb78bba2383ff49ee51138473507eda3ec274fb654451916", size = 179593, upload-time = "2026-10-26T11:08:35.896Z" },
- { url = "https://files.pythonhosted.org/packages/65/d4/e3f6ab681754dd0181c61193b8f94c77e0b798a0d181feccb928a5a43155/uuid_utils-1.2.0-cp315-cp315t-win_arm64.whl", hash = "sha256:ef4b07203bff2511deba54e36589141aed99a7a4831d7f73a3e357dade624bd3", size = 177283, upload-time = "2026-10-26T11:08:37.128Z" },
- { url = "https://files.pythonhosted.org/packages/02/c7/1e655b92c3cb35103d1f38a4b113a65393277208297587b33450d984ed06/uuid_utils-1.2.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5987ef57e4c0bd5284a374abea49f1bf2f80e145ff2ad8d7f1a97590788ae86c", size = 571810, upload-time = "2026-10-26T11:08:38.591Z" },
- { url = "https://files.pythonhosted.org/packages/e5/47/e08716f0c276e73045a81c68bfd89cc593fedd963c5b8a45ebf93a62b7d9/uuid_utils-1.2.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:a11c7eaf24bd4efaa2d50c81ec373b2673c3e8fc700d8b1538e5278966f34c7f", size = 292508, upload-time = "2026-10-26T11:08:39.986Z" },
- { url = "https://files.pythonhosted.org/packages/d4/82/e97b8b05c9d769eb4f4ced71d7f61e2a5ffceb501fcd14810ac9ab863fe2/uuid_utils-1.2.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94156c3383fa92b6171ae34fd16227445a41c1efee09ac532878445400c16e23", size = 327607, upload-time = "2026-10-26T11:08:41.515Z" },
- { url = "https://files.pythonhosted.org/packages/b5/b7/7048c864d81ced8d893c2807a60c4d64f4c807d31f76819d60974c637e01/uuid_utils-1.2.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:bf3c9a7ae482c019e791dfe19c97f6b46541a061de3faf4d1eefb58c4152b8ff", size = 334924, upload-time = "2026-10-26T11:08:43.074Z" },
- { url = "https://files.pythonhosted.org/packages/2d/0b/0218ef04fc2fcf2d848bfc752e421e0dfd5042a317f64f32ed1c8938820b/uuid_utils-1.2.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9617882972837dd99a4fd2826bd27a1de062389447455965a58db37db06d0721", size = 440247, upload-time = "2026-10-26T11:08:44.755Z" },
- { url = "https://files.pythonhosted.org/packages/4a/66/b3d2ed0ef738efaec6ee3eb66162959a77d3859e2cc27a472657bd5563e4/uuid_utils-1.2.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3142fb087b67d95a005732469abd6f5032e51a5a93b23b2818e2c9624356ad1f", size = 329546, upload-time = "2026-10-26T11:08:46.199Z" },
- { url = "https://files.pythonhosted.org/packages/ce/32/b1be67ed5de6d19eb10d153c94b9320cce56c2df3fab62eed0a194cab702/uuid_utils-1.2.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1909c04f2e5c813c625873621aea3b3f2303a6bc7a6231da6deb69dc4df350d3", size = 354068, upload-time = "2026-10-26T11:08:47.763Z" },
- { url = "https://files.pythonhosted.org/packages/c9/54/5eebf7cd2282f81df1db7864f3b76314ad85b603566927281659406807d1/uuid_utils-1.2.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:16d9e7b719ac3e018519664407edb5e583f343e8a8e3941979f26a02bdfc6c28", size = 182385, upload-time = "2026-10-26T11:08:49.222Z" },
+sdist = { url = "https://files.pythonhosted.org/packages/ab/02/66f6073ed29a39d546700678238268125cd5c712a0e9b28d5e6a6faa0b4e/uuid_utils-1.1.0.tar.gz", hash = "sha256:8c9c2a6714aab31da915e7b85b376cc21f1f7379db9f1d77828ec72662e60555", size = 43769, upload-time = "2026-10-19T11:23:51.676Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/05/bd/6b9f538a0ea7c87ed1ea218cbf699f890751001b90fd58544770e81d80f9/uuid_utils-1.1.0-cp311-cp311-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:7425ea6bdd4edaf062cc4ef69ab5b7ae97ede1ea541e8d9d199edf2ee35e2f6c", size = 569226, upload-time = "2026-10-19T11:20:42.607Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/16/99f8df1d2213865ef208c18b833803c4ec532ebd8be78c2b814f11d43461/uuid_utils-1.1.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:afbbef5e19dce604ee4084b287d7633f4ae3f798391403106e976cc565014495", size = 290915, upload-time = "2026-10-19T11:20:44.272Z" },
+ { url = "https://files.pythonhosted.org/packages/3a/a6/ff7e16d04162d2e23dff1f13f4ce556407af2934ad0c13535b5a4ade58e9/uuid_utils-1.1.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a0cfa71f1026ec800206a517905666fe2acbc41dba4c8a6c51e03b17bd242541", size = 326927, upload-time = "2026-10-19T11:20:46.001Z" },
+ { url = "https://files.pythonhosted.org/packages/3e/74/35a63f788b94704e8ceabdb3122de29a59b06e38a09484cc6725348c566b/uuid_utils-1.1.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:36223ba9eea350ae41da2fe86f3b9a8399fd8420d02ee4cef4192356fd16c026", size = 333433, upload-time = "2026-10-19T11:20:47.711Z" },
+ { url = "https://files.pythonhosted.org/packages/1e/fa/5ef4f20e556dc4a86a5212636cf1694ce37c2882e17b0f7fb2406592426f/uuid_utils-1.1.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:bfe635129e607140e583099ba5da730a3cd8142a076660ac995a37b334dfab52", size = 439842, upload-time = "2026-10-19T11:20:49.493Z" },
+ { url = "https://files.pythonhosted.org/packages/ad/47/be79efbae8e499bee2626fe4b2b7ebe17763b25dc894a79753dd3ce0a07c/uuid_utils-1.1.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdd40b58fc284badd5cc75620c61512e482957f884ba7824b5abb7081b871149", size = 327441, upload-time = "2026-10-19T11:20:51.157Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/80/ff80a4907e9cc12a8ae80679461f7e2717886f43fe6f1c8b054b7607aae0/uuid_utils-1.1.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:815b81d226e5036cb8f3fbc44ac19ef2d8a454e557d6a9c1140b2173544c0514", size = 351434, upload-time = "2026-10-19T11:20:52.795Z" },
+ { url = "https://files.pythonhosted.org/packages/55/a5/140455d246fbf195f5c689a14ea6975f6a89262edcf28e0904094d3af0e1/uuid_utils-1.1.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:6b600f0de348beae5e7cded0de63cb3878725a2daa6be58b1309a81b4ec47be8", size = 503728, upload-time = "2026-10-19T11:20:54.686Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/41/eae8b1f19d4065a8acfcfb62ec3a1730c949f86914e00c202cf388fef8e6/uuid_utils-1.1.0-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:73aac44a18b21320869a58535adfeb614dfa4c5bd00deb7707918795eaace87b", size = 608130, upload-time = "2026-10-19T11:20:56.577Z" },
+ { url = "https://files.pythonhosted.org/packages/05/2d/08bb9ee241dbf2d04164210653cb61025ca49d71fa5ffba88f04a5676c86/uuid_utils-1.1.0-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:48d225adeb56f8a5953c2cba5db1a0af3e65092c63d6d6bd458dea3be6a408b1", size = 567998, upload-time = "2026-10-19T11:20:58.374Z" },
+ { url = "https://files.pythonhosted.org/packages/93/52/2d57a327da74605218c8e76a2cc7e84019aa71361cd3f48d137fe002c03b/uuid_utils-1.1.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a7e5e9a7c4ef7120b83eabd2d97692498ef238694f3b78782fb90c8a9214f400", size = 532556, upload-time = "2026-10-19T11:21:00.253Z" },
+ { url = "https://files.pythonhosted.org/packages/68/57/6c5d4fb35f36a18db78c279dfb357cc4881c252207ece15992aa4b3b2095/uuid_utils-1.1.0-cp311-cp311-win32.whl", hash = "sha256:08a5b7832a09d0e31438ca2adfe0eb1abc8388018c52e431d8ac113077cf19b9", size = 174300, upload-time = "2026-10-19T11:21:01.838Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/65/b1192ce7ac423f6be6533bb651b72120250036d60157fdd960fc4787df71/uuid_utils-1.1.0-cp311-cp311-win_amd64.whl", hash = "sha256:115fa696d1c9bff28648c9588680b681af391ac9cf95789b3297625db47925bf", size = 181040, upload-time = "2026-10-19T11:21:03.362Z" },
+ { url = "https://files.pythonhosted.org/packages/43/6b/fd57b669af524715a3b1c01caea4530dfcc6f8c032369f504fe9fdb68877/uuid_utils-1.1.0-cp311-cp311-win_arm64.whl", hash = "sha256:28e7c3ae445753587c83b2128a67a0fb62b610df12c0495794097fd1bbd0ef88", size = 179516, upload-time = "2026-10-19T11:21:05.031Z" },
+ { url = "https://files.pythonhosted.org/packages/16/71/310e4aeb7bf7d40524ecf5cabc1f6089c8f791bf013a8684b43437116d45/uuid_utils-1.1.0-cp312-cp312-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:949baf215402379b958d919740c11f5d07ec80a2a76275d51dcd9564c97f22a1", size = 559048, upload-time = "2026-10-19T11:21:06.722Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/49/884e4da3eb23aa47d78af0e77d7f5535235023d415083e73615089d1f5dd/uuid_utils-1.1.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:af4ee2bb189a701c925ff952f76660340549e2bc1dcc33d4c30126f5d4aa5b98", size = 288928, upload-time = "2026-10-19T11:21:08.513Z" },
+ { url = "https://files.pythonhosted.org/packages/51/26/1db0ed16af735beb1726be5de196583921fecbb35560f150d2138fb0deb2/uuid_utils-1.1.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:64262199314b709a362668937a95ec060fb9adf463e8d784cbce1016ed1180dc", size = 322257, upload-time = "2026-10-19T11:21:10.244Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/e8/5416eb6232de595ce41219738e58b84d5c832e4458eb69c1e0df69aa5506/uuid_utils-1.1.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:408a8dee1d4b60fa7529c88c6951c4b64653401450cd23d06703ef26c1dba820", size = 329266, upload-time = "2026-10-19T11:21:11.866Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/47/cf0d69c9ce5bf649bfcbe7f518b0d19905ad3aadacadcc0ce7097deba9d8/uuid_utils-1.1.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a5095ba70c03b8d0957e1986c48d6e05536bb264812c7360577d996b6c416d81", size = 432891, upload-time = "2026-10-19T11:21:13.54Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/ce/ae337d441c789041a916c1f9a8489121beb6f98ed7c505c537c79e0fea73/uuid_utils-1.1.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ef30b84f1f1729b59d076e9b270837b4716b7cecf07cce5a364ae2e0a01ceb7", size = 322893, upload-time = "2026-10-19T11:21:15.196Z" },
+ { url = "https://files.pythonhosted.org/packages/75/66/61e58e51302a1a628e063701cb9ebf67b614ed2e4511c816cbcfebd361a4/uuid_utils-1.1.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:933a546d331005fdc96bf5246eba69079f0bafef438238e67094ee35a7522980", size = 344408, upload-time = "2026-10-19T11:21:17.197Z" },
+ { url = "https://files.pythonhosted.org/packages/57/a6/fcb9e47d98ba7896feb11aa5ffa7257a6ca606589178494090d69130acd1/uuid_utils-1.1.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:2f661a1596ac5bf6e134a4179c1793c5e23f38909767ddb63fe5f072c43788ba", size = 500268, upload-time = "2026-10-19T11:21:19.156Z" },
+ { url = "https://files.pythonhosted.org/packages/c2/0c/6f009764e6e44575d33869f7156822b18991d2a9b40de38ae7eec4d7288a/uuid_utils-1.1.0-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:ff56adad65ee9e100a00176b74cf97961217cae9df86f114f311741cc1edac90", size = 604776, upload-time = "2026-10-19T11:21:21.065Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/c7/06d283872b36893b71c7d636a12ad1a2163e0d1d2aaea846d1d5488eb962/uuid_utils-1.1.0-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:7220e61d39bf58e1f2a3a58e921109bb12857111ac533eaff5840998d4755773", size = 562133, upload-time = "2026-10-19T11:21:23.105Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/07/93016f7178524b5ab16a19e1114ab5f192ba984d2f2db8078061cf47a47d/uuid_utils-1.1.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:6f579d21cbd1e6fd7d40011919fe6243868d77c0381d72ca9b4f3b294703bb13", size = 527588, upload-time = "2026-10-19T11:21:24.751Z" },
+ { url = "https://files.pythonhosted.org/packages/eb/76/a76ec9e1561e4a0265ae8f3292fa601263ae8519b273cdf8297d36552395/uuid_utils-1.1.0-cp312-cp312-win32.whl", hash = "sha256:9b0724915ce756ce137ff14be77b7f74bd8042112bb84f937288cad4eed47a86", size = 170780, upload-time = "2026-10-19T11:21:26.382Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/4c/ef53c00298dc1f73c7722a4b550074020e8ee05fcbaf08f57c4d3263065a/uuid_utils-1.1.0-cp312-cp312-win_amd64.whl", hash = "sha256:06d658535386df4f31769b13092e30d8cea03799a5874e9fa755847e299c1323", size = 176925, upload-time = "2026-10-19T11:21:27.921Z" },
+ { url = "https://files.pythonhosted.org/packages/9a/2e/9ba28f6b8fd0ff855890cf1a0e8ed8929e598428255682c9f72467565d3a/uuid_utils-1.1.0-cp312-cp312-win_arm64.whl", hash = "sha256:f8db3f53d51c38b7ba4afeac10cdcdf4d0048ab6ea63d50a3c19d965c13f920f", size = 175402, upload-time = "2026-10-19T11:21:29.529Z" },
+ { url = "https://files.pythonhosted.org/packages/de/78/5dbe559ec503ccc1e90f604543018a7a04047fb4b7e576f6b5b0a7f7d9c2/uuid_utils-1.1.0-cp313-cp313-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:c63b56ba5fa87b3c9f83096ddf36536c270ba1021c65821ed0ba2972f10a6578", size = 562784, upload-time = "2026-10-19T11:21:31.252Z" },
+ { url = "https://files.pythonhosted.org/packages/c9/b8/d6161ff91777a2e22de3c40d81136a76ef1da8714dc9ad473a9df204b20f/uuid_utils-1.1.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:cdea92a41bf3acd3b11a95585bb0f80fc798d8b48c89771e6be535bc268eeeac", size = 290517, upload-time = "2026-10-19T11:21:32.845Z" },
+ { url = "https://files.pythonhosted.org/packages/6e/0d/7ac2979c06e0092e94ea2ccc37c7298628b7e2998310e53e12a4a36b9c23/uuid_utils-1.1.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1de74bb1d45eab119dfd62bd4383fc5f0b9a5cd652ac1655d66e4b87d2d53184", size = 322563, upload-time = "2026-10-19T11:21:34.723Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/5a/5bf030a351764832a7b794895ba5b8d5edeed93635de9f945d1e59654f0b/uuid_utils-1.1.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9e5d6ca1133ec922498a423e1fa2fe1b0899b2294761250aeab098dcb6219fba", size = 329111, upload-time = "2026-10-19T11:21:36.341Z" },
+ { url = "https://files.pythonhosted.org/packages/66/ff/2e5f293c9238715285b6a20265bb6fe24bc85dbf30b843464cca20b07d91/uuid_utils-1.1.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:54a2a6b0c377202610415c6332733d517caf2d372a0fb26de28400d70e99febc", size = 434137, upload-time = "2026-10-19T11:21:38.145Z" },
+ { url = "https://files.pythonhosted.org/packages/b6/8b/c757327f35e9ed9d8113753465b1a920ec0df384a5e4d8c7dbb2246d1d07/uuid_utils-1.1.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40cf778e0b2bcdc3aa6235f18e883315a12c47b64c77a8006140571cd2a7bd59", size = 322680, upload-time = "2026-10-19T11:21:39.862Z" },
+ { url = "https://files.pythonhosted.org/packages/f5/e0/d84c15acf4dc4e8e0f2e822ed1fa20f780de020eb9b1023424144b6267fb/uuid_utils-1.1.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7b524865d84c36e2705c78e295210ed2cc8d9030ed85fc723e81526294a331c1", size = 344102, upload-time = "2026-10-19T11:21:41.582Z" },
+ { url = "https://files.pythonhosted.org/packages/b0/3f/bff84d94a0bd4119a1eb3948459cf48d8c659c91b582411c755091a8edc2/uuid_utils-1.1.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6554d6ee237ff2f96de146bfa1d3f2603542683efe7c3ec013b2b00ce9952756", size = 500707, upload-time = "2026-10-19T11:21:43.452Z" },
+ { url = "https://files.pythonhosted.org/packages/2a/0c/2d901d58d8e1db1e898c033b88e310fbace3575b4d0ac18b3a20cd4a7776/uuid_utils-1.1.0-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:f397ccdff523b700238a539249837ca9773195d4f64a76a7ca77da25eafc18fe", size = 604743, upload-time = "2026-10-19T11:21:45.225Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/d9/95e74faddc26f3862b191b54957e76aaa9ba9503c912648ca83cdc04dfde/uuid_utils-1.1.0-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:7e2cadb57502461ec33a667888b6bcfa30ef34f522f182c752e74ffee781ddcc", size = 561361, upload-time = "2026-10-19T11:21:46.963Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/5d/a7332f64b5cc63f993cbeeb0cf2aa02bf0722b2c038312563cd0a548216b/uuid_utils-1.1.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:70b5e208a339ad8a01231b2531846e598070efb2a126b0529191a084a4dfcd0e", size = 527665, upload-time = "2026-10-19T11:21:48.562Z" },
+ { url = "https://files.pythonhosted.org/packages/10/b8/6ce880dd3ddf3a575d56e77e9337adc36117ea1ada850b39c0d50e682ff4/uuid_utils-1.1.0-cp313-cp313-pyemscripten_2025_0_wasm32.whl", hash = "sha256:001cfb0b3183164a23b3a551101ad0f22981c44f8486e63220c512e44b283fda", size = 99632, upload-time = "2026-10-19T11:21:50.113Z" },
+ { url = "https://files.pythonhosted.org/packages/74/61/60d34c78594cbaf6b0459b9411bfd586540c640e66ee75ddd0cb2d2f0074/uuid_utils-1.1.0-cp313-cp313-win32.whl", hash = "sha256:c9725e8e371472c03d7fb7669abf9d855c1a6d3347f1a425e55434c946aae94a", size = 171266, upload-time = "2026-10-19T11:21:51.638Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/34/d88702d2d2173dac1d4b39c85dd8dbb990321578aa4da96b31c362b7dea7/uuid_utils-1.1.0-cp313-cp313-win_amd64.whl", hash = "sha256:fb3047b35012d8623814605411cf86f4d08de3043d0405dd8ac64d139539335c", size = 176665, upload-time = "2026-10-19T11:21:53.486Z" },
+ { url = "https://files.pythonhosted.org/packages/87/75/1e8444fc4d62b64c4c425f885693ec4f42a497c8fb026be965ccbbeb04f9/uuid_utils-1.1.0-cp313-cp313-win_arm64.whl", hash = "sha256:ea12a2ba99f9bb9f3e761b79ade863635c8f467c6e6f40ae24269c5dbe2bad2a", size = 175333, upload-time = "2026-10-19T11:21:55.555Z" },
+ { url = "https://files.pythonhosted.org/packages/81/e5/53401292e8850ba7b3b5ef3fcc7de6a61ca34e804898cae00961af16ba8d/uuid_utils-1.1.0-cp314-cp314-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:d8e8f28ca5a80563d9ac681098416718633c80a1f1ed955c4d30e18e4a9e8075", size = 566662, upload-time = "2026-10-19T11:21:57.483Z" },
+ { url = "https://files.pythonhosted.org/packages/5b/2a/bd1f292edd4175a4c2f2c6508e687ca5a057d2fd23b79ea3fe070a56938a/uuid_utils-1.1.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:5e2c3f3c3e969b2f900543e27514c354eb145ae8ca0af1fda4ce4d006746fcb6", size = 292298, upload-time = "2026-10-19T11:21:59.313Z" },
+ { url = "https://files.pythonhosted.org/packages/87/b8/92c25a0c7d756cabcc6f90aa34da84d7dfae801be9c2b15bf7b8a35f54aa/uuid_utils-1.1.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b366c721af0fdfd08bd9ec8242a97d07d8e3569262bab5d6152ec222623b43c6", size = 324825, upload-time = "2026-10-19T11:22:00.8Z" },
+ { url = "https://files.pythonhosted.org/packages/51/b0/80e5401c7fb444b0e5bacc68226cee86d09c22e09fb11156e99dbfb73ef9/uuid_utils-1.1.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:5c465dcc77dd101053251b7f77fcbb31c6468135e93d478d521e745b441d8da8", size = 330975, upload-time = "2026-10-19T11:22:02.607Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/87/a96615d5d80740cba5e91d730bad37ccee027aed8ace5b773131e5183544/uuid_utils-1.1.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:036d204dc1aa20a4a1c10305c4b6da5b8f56bf98b56997409c0c92745213ce83", size = 436353, upload-time = "2026-10-19T11:22:04.437Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/62/ceb21bbeb93af570e0ae169f0373295062726f81a8c4a6ef953370784d7c/uuid_utils-1.1.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:698f2f078dad63375b26badd3c792d0f7b81f515b99091018f31166a0d14cab7", size = 325424, upload-time = "2026-10-19T11:22:06.152Z" },
+ { url = "https://files.pythonhosted.org/packages/90/99/81fee7a0e9758b87daf8727ae173c6c51d110d4d70364cc1ae003d2c85c6/uuid_utils-1.1.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e2151a82e4374ea2b88e8b173e20b676a62abb013a807f832758b2bcc9997874", size = 346584, upload-time = "2026-10-19T11:22:07.769Z" },
+ { url = "https://files.pythonhosted.org/packages/90/10/fbe1ae3911220adaca2a7aa1e7a308b5d048cf634fff4c65f42b56a1dc23/uuid_utils-1.1.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:cb085fdfdbab532351e45a4957387652407dab1b145be9be00836c09be6fc3ec", size = 502761, upload-time = "2026-10-19T11:22:09.65Z" },
+ { url = "https://files.pythonhosted.org/packages/8c/22/4d9da0e3e8a94d7c70b15dea17eb1c7377a3e3390f91059efae22ba83551/uuid_utils-1.1.0-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d018cd1e3e71e215fb5a52a1dbe1d01f315c081158abfd92301611a091ec55d5", size = 606432, upload-time = "2026-10-19T11:22:11.367Z" },
+ { url = "https://files.pythonhosted.org/packages/ea/98/8b955bd7f984146dd332072c3869274f386de4a760bcc72e104d020aa941/uuid_utils-1.1.0-cp314-cp314-musllinux_1_2_i686.whl", hash = "sha256:487ce24cc7455b8f1e9080e9cf0699fd133f08d169ddbacd73e42f6f483e5663", size = 564644, upload-time = "2026-10-19T11:22:12.987Z" },
+ { url = "https://files.pythonhosted.org/packages/36/5e/94fca160ca6b80c6b3de63d3056c76add0bb852519519e5e96225faa823e/uuid_utils-1.1.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:bcff122d2781ff7dceaa1a79ee9b4ca4ba9dec3fcb3c1bc1a176da8a52789227", size = 530534, upload-time = "2026-10-19T11:22:14.549Z" },
+ { url = "https://files.pythonhosted.org/packages/d4/ac/93f18f4d9de9eccff8042fe1e054002b8d3307ee80ff6337e85076b8aaec/uuid_utils-1.1.0-cp314-cp314-pyemscripten_2026_0_wasm32.whl", hash = "sha256:da28d6a0c676f1fa18dbb05bc521ba3a8b5843851fa3f4a07e6fe01bdfad2ff8", size = 100551, upload-time = "2026-10-19T11:22:16.152Z" },
+ { url = "https://files.pythonhosted.org/packages/33/7b/f48a6bb58528a8f24d54c92e62f01a08781c522490339631e2eb7f37b624/uuid_utils-1.1.0-cp314-cp314-win32.whl", hash = "sha256:0512c36dbd169ece6f2bb99b90c23a87983e3b477bb2ae95b6ddb8517d59b8e2", size = 174077, upload-time = "2026-10-19T11:22:18.15Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/1a/63025c72422a26a7f858357cc15757390f546f2634e2a2a98eccc995e4c2/uuid_utils-1.1.0-cp314-cp314-win_amd64.whl", hash = "sha256:5ea4c693b2a6c27aa9c53216d5fdaf159ad667e56028e3f5c8e64b64944901ad", size = 179753, upload-time = "2026-10-19T11:22:19.91Z" },
+ { url = "https://files.pythonhosted.org/packages/53/b4/7b20acceaae588cd13af524f0a60244f461e0fe6161910b13df141e4dcbb/uuid_utils-1.1.0-cp314-cp314-win_arm64.whl", hash = "sha256:607a35b2b6dbd9109bca42af3d52378f812cb014d8c0c9ec40d32eeb5cbf656c", size = 177607, upload-time = "2026-10-19T11:22:21.428Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/21/11459ab6cdd1b0e8c3f1dead90b4ba036d67c69fe74b5de65356e49456d8/uuid_utils-1.1.0-cp314-cp314t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:4e7b534b72bedb3efe03d2f71631c4e1028952398380c83e75c3f372a3b3d43a", size = 567221, upload-time = "2026-10-19T11:22:23.079Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/29/04ba922c99a1fc823c800bb7e7e81261982b7075b8af0314b8c114d66e73/uuid_utils-1.1.0-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:162ee2e2ec3f1ad43fb071377fa9d304b7968775072f5766e0992832a8babba5", size = 292732, upload-time = "2026-10-19T11:22:25.014Z" },
+ { url = "https://files.pythonhosted.org/packages/be/a9/0ec005f2c53d14e299bb638ba7e1d1c85c2a98d578d4db32c85c06ada869/uuid_utils-1.1.0-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b65aaf9c39935a4e609d00ea77604c93bb32ed0dc7255966f8cb5b1bbd0591d", size = 324460, upload-time = "2026-10-19T11:22:26.895Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/75/1aa9c440d4f1f7b7e38624d8abd59f7dc3ad75bad8980765255f23919c83/uuid_utils-1.1.0-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:358a7de0ff8f47bf0552d6ae780f95854c3b048ae00ecb3bbcff3da93efb4041", size = 329524, upload-time = "2026-10-19T11:22:28.747Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/13/3dc711f731481ac66f7b597e4b30b3dc84de238032760d6d70e26078038e/uuid_utils-1.1.0-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a9316d4d009b3881388bf53f026aadb890c459c259f107e945067ca3f3100698", size = 436207, upload-time = "2026-10-19T11:22:30.58Z" },
+ { url = "https://files.pythonhosted.org/packages/19/8a/6cab173e1f6c43a0ad09232968d2185fc0298e1d656336dc23558ea93ee0/uuid_utils-1.1.0-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:402c619b7e934c48c8950bb8542808e9b0328da8f427c08295efe949cf497bd2", size = 326382, upload-time = "2026-10-19T11:22:32.293Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/7b/16950cf22fbc47fe045cec6b43d7ecd21063775304920123d92f76f64976/uuid_utils-1.1.0-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:094d4e4b8f64f1aa5e22e59ad10432ddcf63e01d2ef3ff1489b878feaefd748e", size = 347139, upload-time = "2026-10-19T11:22:34.349Z" },
+ { url = "https://files.pythonhosted.org/packages/fa/3a/6baedc1aa6c0446d8c0a8961aed7e3e704757c1c4865afd91e996b1de80d/uuid_utils-1.1.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:28d36962be414c3543c1dc39a76caceb0f697856d0f83fe149789688218a5437", size = 502078, upload-time = "2026-10-19T11:22:36.293Z" },
+ { url = "https://files.pythonhosted.org/packages/71/3b/ad3ef3c100c79a07655d919368f86afc8e627be43564267b6bfd91f3ff29/uuid_utils-1.1.0-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:f836bbaee4e704b780dd64f874386ecefc1083b5b608e92128afa2a6b236e376", size = 606179, upload-time = "2026-10-19T11:22:38.358Z" },
+ { url = "https://files.pythonhosted.org/packages/d3/39/20b279df56f9f24937be18abf9cd2bba13bca932590eff7dac55d4c9040e/uuid_utils-1.1.0-cp314-cp314t-musllinux_1_2_i686.whl", hash = "sha256:8ef69a74258b265894e5bf9bf2bbab488fdee6e053fbe863bd084f062ff007c0", size = 564949, upload-time = "2026-10-19T11:22:40.28Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/9c/4d1ba32ee94caddaba0077660e781734ffbc80084511fd8d542ca918a7b2/uuid_utils-1.1.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:a64838ceea2b32154703235788cecb2728815b17eda61843bb2a136f0ee3fd16", size = 531382, upload-time = "2026-10-19T11:22:41.899Z" },
+ { url = "https://files.pythonhosted.org/packages/c4/eb/3c5ee173a1c3f50a88be996bb36f2241f6a17e100cd27c3c0767bb6a7d5c/uuid_utils-1.1.0-cp314-cp314t-win32.whl", hash = "sha256:e8530849a955861eeda845851dffdb6cef9146aafa67a484ba8b3890e337ef08", size = 173614, upload-time = "2026-10-19T11:22:43.63Z" },
+ { url = "https://files.pythonhosted.org/packages/42/d7/617d2b6042d242e52dd0d7b05cedd2ee28f7d17bda12846951381021d961/uuid_utils-1.1.0-cp314-cp314t-win_amd64.whl", hash = "sha256:deb78d38ddc98d99dbd269cd3379fb891300e4034d47dce8999f62742f0e229d", size = 179325, upload-time = "2026-10-19T11:22:45.289Z" },
+ { url = "https://files.pythonhosted.org/packages/d8/4f/3498f81f4071019b25f930f1c3ae60112adf5e0facccd69043c7b50b882e/uuid_utils-1.1.0-cp314-cp314t-win_arm64.whl", hash = "sha256:cba5571c253bf2eb0c9711616957ce0c500e31cdcca4f4323d5ffc2792eb092f", size = 177344, upload-time = "2026-10-19T11:22:47.081Z" },
+ { url = "https://files.pythonhosted.org/packages/88/c8/680ce527c486ce94c257ec9896acd23b9fbb6599b53de828ba43c815e63d/uuid_utils-1.1.0-cp315-cp315-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:71595f8cc7b16ffc909ceb97d552e63d7f3c968cc43029c548d7c9c4a883f154", size = 567374, upload-time = "2026-10-19T11:22:48.822Z" },
+ { url = "https://files.pythonhosted.org/packages/56/c1/393c5f63869c6f16a707fcf5dafc5185848f69efea84591897f40a7579a2/uuid_utils-1.1.0-cp315-cp315-macosx_10_12_x86_64.whl", hash = "sha256:2ea7484d9c224d00f331caa17babf3d3e5baebfdc32317b4911411d03c2f7de9", size = 292869, upload-time = "2026-10-19T11:22:50.397Z" },
+ { url = "https://files.pythonhosted.org/packages/13/b3/1f9f666729309f773d19226d12cfecc07fa512765489ba69b91a46375810/uuid_utils-1.1.0-cp315-cp315-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7a2ecd639e80b03018fbc37857a007ffdfe1d2c82c3628706d84b6047b80f781", size = 325049, upload-time = "2026-10-19T11:22:52.463Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/06/18f8e143d5c805be1c1f85753edf24bf871c7dc7359df17ae26f9ac25eb1/uuid_utils-1.1.0-cp315-cp315-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d7efcd15d845a7d9b7d1f40b094094e9f0378d4d6f130bd1ab6151597c4a3684", size = 331997, upload-time = "2026-10-19T11:22:54.293Z" },
+ { url = "https://files.pythonhosted.org/packages/9b/ce/4e14563231d40e81fe88424ee09f7903435e3e0d5bb81616c9a34e7575a5/uuid_utils-1.1.0-cp315-cp315-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:06e94f1f8d33681c0240b9ed971e8393432f02f6e5254c5d8e1821ed1bafc748", size = 437362, upload-time = "2026-10-19T11:22:55.997Z" },
+ { url = "https://files.pythonhosted.org/packages/88/7a/3182b8f231baaedf64240e6b0b18712ce9036ccf5e4a9104b9ab8b928d09/uuid_utils-1.1.0-cp315-cp315-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:963b812f0b06291c97e20a14b14ffdb519644fb9856e0a299e8722fba8fb099a", size = 325630, upload-time = "2026-10-19T11:22:57.698Z" },
+ { url = "https://files.pythonhosted.org/packages/d1/39/bb3b5b72939894a292d34b1ec499e0390ca16738a8d03a60f7bbec851f99/uuid_utils-1.1.0-cp315-cp315-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7d73586cbe8a70b16141a92a3fe81102ffe9e5c0b8970a8be7720526d72098d5", size = 348301, upload-time = "2026-10-19T11:22:59.507Z" },
+ { url = "https://files.pythonhosted.org/packages/8f/5f/99d9c8f066aad104f0a157d5048b2e469ad25539d3a60b3b362b54fc1b10/uuid_utils-1.1.0-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:24c399612223410ec1b4a924da3011f4c8aeb333cadf44762258f687779448b4", size = 502848, upload-time = "2026-10-19T11:23:01.699Z" },
+ { url = "https://files.pythonhosted.org/packages/7c/59/0a771b2248503807cb0d34d49e0a2f5d37570fb0c0a4f7b659f7261453a1/uuid_utils-1.1.0-cp315-cp315-musllinux_1_2_armv7l.whl", hash = "sha256:1c46bf2fc2b84a7e1b305191a42671f7827fcc0d6b864939dc03136c6f095fe9", size = 607067, upload-time = "2026-10-19T11:23:03.39Z" },
+ { url = "https://files.pythonhosted.org/packages/bd/68/3dcf95eac42adf9012c3f8d48de2bf3ea1dc4b07961bb728b280514c2713/uuid_utils-1.1.0-cp315-cp315-musllinux_1_2_i686.whl", hash = "sha256:8628fe3da953923e9a0d932a9b6cfed8c96e6b5586ca3dc87d9d3ab685ee7bf2", size = 565855, upload-time = "2026-10-19T11:23:05.508Z" },
+ { url = "https://files.pythonhosted.org/packages/6f/f5/5a0c60e658cd59d9fd20bb396c7990b0750c3e8914a3160ce79baf0a7a4a/uuid_utils-1.1.0-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:c295111c8be5cf85fa06fcfa1e145a8242470a6852b38560780e133f0a842f09", size = 530734, upload-time = "2026-10-19T11:23:06.991Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/ae/fea64495f955bb624884404454ceb99d9cc134696c06dbeab6ed011711c5/uuid_utils-1.1.0-cp315-cp315-win32.whl", hash = "sha256:ddc784b3b902df461ede85ce52ea825e7401d1ab5cd2efe34f0a81e5c7664a04", size = 174553, upload-time = "2026-10-19T11:23:08.502Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/14/8478bf1009f3aeeeb86461296fb6cf999bacb3d937b7ecf2e65d38bd07a7/uuid_utils-1.1.0-cp315-cp315-win_amd64.whl", hash = "sha256:065acf8b353f5eb94170f63fb11f08b0e4547cdee2d5d2a04586731cd88c1d73", size = 179363, upload-time = "2026-10-19T11:23:10.082Z" },
+ { url = "https://files.pythonhosted.org/packages/0b/84/5de94ef688abfe1943a81be0c5180221c8723d798d8e09abf943b6dd768c/uuid_utils-1.1.0-cp315-cp315-win_arm64.whl", hash = "sha256:a864590871b42e32e8729f315e324faeb440381ba34efd9f1ffbab01ee9ef325", size = 177424, upload-time = "2026-10-19T11:23:11.675Z" },
+ { url = "https://files.pythonhosted.org/packages/99/e2/768633dac9c3774de5ad9e6ab401fe2a2435cdeff26102f4680054027f2f/uuid_utils-1.1.0-cp315-cp315t-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:55c0098390cba6e5bbd9cc01ba15d56027420dcf8a5b63ca0f4dd85c2c286aab", size = 568306, upload-time = "2026-10-19T11:23:13.342Z" },
+ { url = "https://files.pythonhosted.org/packages/ce/3f/4aca8f432f7bb823df10a451c1b8bd4ddb9be7ee149f5e6be1be0b869c23/uuid_utils-1.1.0-cp315-cp315t-macosx_10_12_x86_64.whl", hash = "sha256:d7d9f00b244434332e9fecba615e221277a46eaeef932abf4f784ad6595e25b6", size = 293017, upload-time = "2026-10-19T11:23:15.054Z" },
+ { url = "https://files.pythonhosted.org/packages/d9/c7/64539a51b50b0c91dcfe556a9f5552891be86cf9dec1c1bf791f94985168/uuid_utils-1.1.0-cp315-cp315t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6727381e7d22e44ef749e2ce3cfb1ef6c6b2934556a5ec09f3b4b86b14752644", size = 324337, upload-time = "2026-10-19T11:23:16.608Z" },
+ { url = "https://files.pythonhosted.org/packages/94/4a/1910e168bdcef00a4a2b5542e03fa37d535c8b14f5c729fcac9317fffbc2/uuid_utils-1.1.0-cp315-cp315t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:75416b7e93e665524fafe0cdd26ce3aa7a1d82fcc1eebcab16d75de507ae28c0", size = 330108, upload-time = "2026-10-19T11:23:18.374Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/ab/9d4bfb49b11095eb004b275cccf52cdf8232a6d380f3d21a51ac452b2eb5/uuid_utils-1.1.0-cp315-cp315t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:325f744a6b616cd1c84b7dfcea234f772a61801d95e0e7b30006e4488df1faca", size = 435646, upload-time = "2026-10-19T11:23:20.132Z" },
+ { url = "https://files.pythonhosted.org/packages/57/75/7a1aeb280eee7e796d4823228a429f9cae52dd9b54510ccac8919c973a21/uuid_utils-1.1.0-cp315-cp315t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:13de6fdb1aa02b0c1a2959292c2bf97afca3ead0412bcbc239ea196b0f03877c", size = 326692, upload-time = "2026-10-19T11:23:21.748Z" },
+ { url = "https://files.pythonhosted.org/packages/73/24/f2ec38c6446719e03964c9559f30ecee919f4adc9ea6f7ec351ab5589f14/uuid_utils-1.1.0-cp315-cp315t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:d6d4c4e11597b6af7bbefd26e7574765f6ba488df946ef38bf2c6c8cfdd9d045", size = 347615, upload-time = "2026-10-19T11:23:23.701Z" },
+ { url = "https://files.pythonhosted.org/packages/95/e3/8bbb8105d70d73f0a4f8d4a02135a20be911e5ae7268ff618bf0d271233e/uuid_utils-1.1.0-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:3faa2e0852da89962f20e8a4cee49777fd83b651095ff10f87a2349c7d94e655", size = 502377, upload-time = "2026-10-19T11:23:25.513Z" },
+ { url = "https://files.pythonhosted.org/packages/7e/41/79e1ccb5d25cddf17f38c6bca2a437f336abfd7b165b21c334dd6148b916/uuid_utils-1.1.0-cp315-cp315t-musllinux_1_2_armv7l.whl", hash = "sha256:8bc9c328bf78128bcd3fa040394c4b986c4ef13baffef0ca75fc66a5d44d8628", size = 606900, upload-time = "2026-10-19T11:23:27.168Z" },
+ { url = "https://files.pythonhosted.org/packages/cb/1b/4bfebece1cf1bdbbd3600f8e6dc84a26a7ec27457db82366b2450090816a/uuid_utils-1.1.0-cp315-cp315t-musllinux_1_2_i686.whl", hash = "sha256:3882e0bc4d82655bfe504918a9d4e0aa780122350653b6d6edbebac327a51208", size = 565320, upload-time = "2026-10-19T11:23:29.263Z" },
+ { url = "https://files.pythonhosted.org/packages/34/ef/2eb1adab4456145a1297eaea41f8706d33d922f0f93d1e8070c1a18ecb8a/uuid_utils-1.1.0-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:fd42a5ab5ccdaa58ea0ba74c36af2d2ad9df9faf9bd0cffda9c6c453acf1c4a2", size = 531494, upload-time = "2026-10-19T11:23:30.966Z" },
+ { url = "https://files.pythonhosted.org/packages/f9/aa/bf056eb703c4ed8d73b4053cf031c54c09b13af73846f3c591b2f29f8358/uuid_utils-1.1.0-cp315-cp315t-win32.whl", hash = "sha256:1b02672c94234bbbfac40aa6bab593f0a14b2eaa7d0f720420270d001bad8777", size = 174031, upload-time = "2026-10-19T11:23:33.075Z" },
+ { url = "https://files.pythonhosted.org/packages/24/c8/941467babae8e58e765b63669c9d79519151ce2be22d95d244d352e66fe9/uuid_utils-1.1.0-cp315-cp315t-win_amd64.whl", hash = "sha256:40185e2a4a033c4c17c33c93701ad79b2c0d8653b7dc7d5d7b3a0006e78dd7fa", size = 179531, upload-time = "2026-10-19T11:23:34.521Z" },
+ { url = "https://files.pythonhosted.org/packages/07/bd/b1057910536d9b80fa9fe5ce9327acb5b01fd896e2ce51b132e5cf76fbb0/uuid_utils-1.1.0-cp315-cp315t-win_arm64.whl", hash = "sha256:85059434bebb24c460bea1fd5bae481439465ba94b09a770cd3c265409de7f2d", size = 177281, upload-time = "2026-10-19T11:23:36.427Z" },
+ { url = "https://files.pythonhosted.org/packages/5a/b9/18ba310a5b330e100e57ad290a3ecf8849eff37fbd14be428cf9746abbd2/uuid_utils-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.macosx_11_0_arm64.macosx_10_12_universal2.whl", hash = "sha256:5cc7bce5b972074b707515747d27d681f64a1fb571f50bdbc710a9b1ea3e9479", size = 571793, upload-time = "2026-10-19T11:23:38.176Z" },
+ { url = "https://files.pythonhosted.org/packages/59/57/f5f1357c91c9da4e589989f34be49f26c76fd04bc270a059d609d15e0490/uuid_utils-1.1.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:b4159199b6a7879ad13843acd05b636c4c390bf8df7946ee567481c6693321b6", size = 292091, upload-time = "2026-10-19T11:23:39.635Z" },
+ { url = "https://files.pythonhosted.org/packages/ff/2f/7b7824c7cf9127b47bd72325f35b5ef8841dac37880001d9dea3f9a00b17/uuid_utils-1.1.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ea48336100e8c91309b4d8ca584ba88aca846a17f2799466a28160a5b069601a", size = 328475, upload-time = "2026-10-19T11:23:41.418Z" },
+ { url = "https://files.pythonhosted.org/packages/b2/6c/9be0651439525d8ca77718b30e7216524c282432c08e1421db7dbbd56209/uuid_utils-1.1.0-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ec2eec518cc0c45d42756f14072a0eab6323f1f6d1ee57626a43ce071e180415", size = 335624, upload-time = "2026-10-19T11:23:43.152Z" },
+ { url = "https://files.pythonhosted.org/packages/83/66/433d85f1c7f20e388bce690eadbec4a543bde09042fb38bebf4e14634b1b/uuid_utils-1.1.0-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:67fd31785e9b012844500b77b7bd7054a96e9bbfa6b7039f323653d83890a414", size = 439653, upload-time = "2026-10-19T11:23:44.918Z" },
+ { url = "https://files.pythonhosted.org/packages/df/6e/f4af18dbaf8a9b689e17788f8b06b0152b4a85e135658f286edc7309974a/uuid_utils-1.1.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2a311dfb8d870dabc7d108b91c187b2701537133e0151a1c112570d4eb2a187b", size = 328513, upload-time = "2026-10-19T11:23:46.661Z" },
+ { url = "https://files.pythonhosted.org/packages/09/78/8c7e51c5c08e50ba60f97e002dfb6272866ef15efd8659defea30a4938ee/uuid_utils-1.1.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:97cf6a4ba2b55779efae448b01e01ab7af182ee090fa82ea09a17721cdd01701", size = 353620, upload-time = "2026-10-19T11:23:48.405Z" },
+ { url = "https://files.pythonhosted.org/packages/f1/1b/45862dd20ac4ae3547f07ad395484cfc816cd39c345fda7079a4da9aa45a/uuid_utils-1.1.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:43690470a0f0ebe30a369aa32093ce9092397088cf3e177ac4112c6b95d5debf", size = 182168, upload-time = "2026-10-19T11:23:50.009Z" },
]
[[package]]
@@ -6785,88 +6860,88 @@ wheels = [
[[package]]
name = "wrapt"
-version = "2.5.2"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/9c/b0/f752d19e1e83ab8bd64aeccfde32ff086aee3ef08520f7340b69a8d0a4d2/wrapt-2.5.2.tar.gz", hash = "sha256:551cb49e88b3722faad780303833bc4fa70685fb1773270fb98b40d39d9fddbf", size = 186066, upload-time = "2026-10-29T23:06:39.868Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/4d/c2/b4bbaabf73ea9ccc2e322a11455a95d7d9dcae5bbcc6fd836c5f676a082f/wrapt-2.5.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8b240a879405591da10e2d20c2d1ad29b978d40ec37e24801296cfee1d4c04f9", size = 105761, upload-time = "2026-10-29T23:04:28.866Z" },
- { url = "https://files.pythonhosted.org/packages/f7/d4/57db9384ffb339f29b6712cc1d7d4ace29d07670756becf9ad336728ca42/wrapt-2.5.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:b632d90ac0d04e3ba8821b19954d6c36ea8f714b75db53c58015025fefccb1ac", size = 105933, upload-time = "2026-10-29T23:04:30.45Z" },
- { url = "https://files.pythonhosted.org/packages/98/00/a0effd5872aed3271b28b4740a71bc2d7406d33b4e03eac01ae099ac8569/wrapt-2.5.2-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:0743950d3dda9940e56f81f5334ba051c4fe2e1421e4e4a4ef8eb8fb86a90894", size = 240286, upload-time = "2026-10-29T23:04:31.855Z" },
- { url = "https://files.pythonhosted.org/packages/69/22/8e9ef3a15115f3cc01f2460aaf9aeb04819704fefd218086642c7182be95/wrapt-2.5.2-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:594c6d10b9cfb951a09f7338531a41d31194e7eee9dee206c0a8992421682719", size = 241793, upload-time = "2026-10-29T23:04:33.501Z" },
- { url = "https://files.pythonhosted.org/packages/15/7f/b2912f3d045fd9a5f30d9d58f3b4df8921c87e902524a32d7eb0e0e315cb/wrapt-2.5.2-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e4362b3b4e4b050b8f1f518e0c71a09f2e938a9f62ee559ea1743a13b3c9e8f1", size = 226781, upload-time = "2026-10-29T23:04:34.862Z" },
- { url = "https://files.pythonhosted.org/packages/ab/06/cdd488014d5010b052fdd9c368d8598accd9eee766d98e698cc8510b364d/wrapt-2.5.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:81e685c7662fab9958a852b9c86f3b5b8eee45d047a2766d6c23b63ad1eee423", size = 239441, upload-time = "2026-10-29T23:04:36.296Z" },
- { url = "https://files.pythonhosted.org/packages/dd/b9/6605b2fd612ea1635d0a7cebbdc05cdb59c97a07ee9c418a5f43a49cee53/wrapt-2.5.2-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:c6b4bd8c662c333518a9aedb35bb9f4f7d1584fbef4bc1507a6c6a47357c628e", size = 224041, upload-time = "2026-10-29T23:04:38.008Z" },
- { url = "https://files.pythonhosted.org/packages/42/9b/6a8acfd407991b50b9bdbaaae5551cc12ba487a8a0973c87793dac621f1f/wrapt-2.5.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7a7efde469ad904b8e05f98c76d63b437f4756c30ec7b9136bef02bdb5d1b14c", size = 229602, upload-time = "2026-10-29T23:04:39.463Z" },
- { url = "https://files.pythonhosted.org/packages/81/6c/9a975e802393c74af9879ca938b298a00fd59074ee90abf4ec4bedcdd109/wrapt-2.5.2-cp311-cp311-win32.whl", hash = "sha256:73d432588d1e0a8fcb88d46ef2733e848ff0c3e14111a0420c77199fd2a8cd30", size = 100856, upload-time = "2026-10-29T23:04:40.884Z" },
- { url = "https://files.pythonhosted.org/packages/46/aa/196ab4e3584e547fcb6c4a622934aae14077c367b86f57a5ab5e76a47203/wrapt-2.5.2-cp311-cp311-win_amd64.whl", hash = "sha256:04703a2a4eea122c3a6b3af89e72bbbaa20779e5c7199c3d06e3fcb6601bc7d4", size = 106052, upload-time = "2026-10-29T23:04:42.232Z" },
- { url = "https://files.pythonhosted.org/packages/e0/5d/7a699446d344bc74bcd607a0cf09087285a253eefc6449048c0df7b5a3e5/wrapt-2.5.2-cp311-cp311-win_arm64.whl", hash = "sha256:53cf804402d49fa2c920a90dfab971a82bca67fb43e54adc7c3b24c054485b4d", size = 103001, upload-time = "2026-10-29T23:04:43.529Z" },
- { url = "https://files.pythonhosted.org/packages/4b/fe/9ccae0a147d0732397f54ff232b1a0da148a4099f17c1b156b9d3e79127e/wrapt-2.5.2-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:bec3b6b1ae2f7ed763fb0ba1776b9b5f11283e6ff40c9cbe3a855ea652c326cf", size = 106628, upload-time = "2026-10-29T23:04:44.962Z" },
- { url = "https://files.pythonhosted.org/packages/d5/ca/a61d994df49fa422abd859e525e04b95ef2888e3818a0f451aa7d4d3527f/wrapt-2.5.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:e57d79e3338b3cecee1dd80d6ef8f945f8ea7b92f0d8d5591f840ffa0afc14dc", size = 106264, upload-time = "2026-10-29T23:04:46.307Z" },
- { url = "https://files.pythonhosted.org/packages/dd/26/a6caa9a84ef5db4a273787949cea7ab51f6001d2548768af4c8e3c49debd/wrapt-2.5.2-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:092472b7b1494deef40dac5e9f6295013a9d51b112b5fbc4350f64dc2574e46a", size = 250614, upload-time = "2026-10-29T23:04:47.735Z" },
- { url = "https://files.pythonhosted.org/packages/c7/28/2189571e711ab31cb7f682c739d05f033ae0e83e12eb4d5d21473ca2cd1a/wrapt-2.5.2-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d108bd98ab2c5ccbf55cf2154e0a73341df930030df2e01ee35d71a44a99d3ad", size = 250770, upload-time = "2026-10-29T23:04:49.183Z" },
- { url = "https://files.pythonhosted.org/packages/80/8e/46a6cc2e8215df7fae292178a355df729547ff9b912686823201d38be269/wrapt-2.5.2-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:858511731d6ede4217960c5fecdb5ecbf90e306dfd93b837395658d61374a498", size = 230031, upload-time = "2026-10-29T23:04:50.641Z" },
- { url = "https://files.pythonhosted.org/packages/97/38/dff531d0125fe880e05fe5fcb89f144d25ffbe95aa9a2c50539eabde7567/wrapt-2.5.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:3b6885250b809532a634acd56e360d57c201c1cdc75cbf2b5e106241ece6cbc3", size = 247325, upload-time = "2026-10-29T23:04:52.16Z" },
- { url = "https://files.pythonhosted.org/packages/f0/5b/f4a78df4c52175d53ff095f2f5a54206a2d16e542b1dd2cdcaed901fe432/wrapt-2.5.2-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:c60a5cd8b7b510056f2643d94f1c144533a568ade2891a9dd35733f5e0055777", size = 226900, upload-time = "2026-10-29T23:04:53.559Z" },
- { url = "https://files.pythonhosted.org/packages/20/6f/8eab144c717300f76f1337ba58091f4de5da0f4d772727c9f41135b325c1/wrapt-2.5.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:40e03506f09dbf4854fd69848e843dadd9b7fbbdd217837cb27b37fb8f0b5bc3", size = 239017, upload-time = "2026-10-29T23:04:55.293Z" },
- { url = "https://files.pythonhosted.org/packages/59/1a/98e278ef2f8952a34f847a91f75899f6385762b07577c51a5ef82c768dfb/wrapt-2.5.2-cp312-cp312-win32.whl", hash = "sha256:ac00ba7d22d55d3c8d085005ce4942ff3bb118d480f889b5984f1d3454052917", size = 100660, upload-time = "2026-10-29T23:04:56.703Z" },
- { url = "https://files.pythonhosted.org/packages/7d/0a/ab645f8b4c594cac2db49bbc805bff97a9d3f2944061b865f67e0351b6c8/wrapt-2.5.2-cp312-cp312-win_amd64.whl", hash = "sha256:36021ef761856bdbc10d5f3da9a95af93ef60d6acf9c72c8f849937ba2b902aa", size = 106220, upload-time = "2026-10-29T23:04:58.081Z" },
- { url = "https://files.pythonhosted.org/packages/37/1e/232868b68b925ab29ca5acbec4ad54ee42a853fcd8f596bf7e0e0e3850f1/wrapt-2.5.2-cp312-cp312-win_arm64.whl", hash = "sha256:81bdf0bd141d9b1706a0751b2ee068c7afe6be4042712d427b32b3072d5c40b9", size = 103377, upload-time = "2026-10-29T23:04:59.366Z" },
- { url = "https://files.pythonhosted.org/packages/1b/7a/38f08a89ca8faa9b788a857f109b5e4cd1f25671a5157e2218072f64aa76/wrapt-2.5.2-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:88f982c2bb306732271aa15cded4002ce28b5d9023de9d11e927da4dcf65e441", size = 106242, upload-time = "2026-10-29T23:05:00.746Z" },
- { url = "https://files.pythonhosted.org/packages/e9/8b/2fb9b2522b7e1e7534a72fec0040bbf3d273129ec0a98316e7d37b09aa35/wrapt-2.5.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:269321b17467a10997cc6e14793bf1816e6ce488f9711c74b72552466d206739", size = 106366, upload-time = "2026-10-29T23:05:02.191Z" },
- { url = "https://files.pythonhosted.org/packages/c2/1e/770d73e0cc82a41602cb2cb184769c28bdbaa9b998944d577a1097c18da3/wrapt-2.5.2-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:8b039b3a296762db1908d6108365225393c4fb33c1dbc5f00aa36f8302eaeaa0", size = 248696, upload-time = "2026-10-29T23:05:03.593Z" },
- { url = "https://files.pythonhosted.org/packages/ac/2b/b4362e803d870451b5e701df5e30ded2d6fb1d9e0c01eec4dc7fa8332b57/wrapt-2.5.2-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e1af6136ac5bcfab17190a70b25d8437eb8d8f9ca2b821a0655d39275a8cb7e7", size = 248663, upload-time = "2026-10-29T23:05:05.084Z" },
- { url = "https://files.pythonhosted.org/packages/87/15/244dbd2d86285857898be5f7632e8375161953e645f250ac9778c058ab9b/wrapt-2.5.2-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:9d4e1cdf2efdc7181c8177f6090886e22278e70f7257df12863d2f52a9dcbbd7", size = 227568, upload-time = "2026-10-29T23:05:06.836Z" },
- { url = "https://files.pythonhosted.org/packages/62/0e/15ae5812d510fbfb65acb3a63aecbd8ffb4b944b2fd0131d968e31df89d6/wrapt-2.5.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:e33578755e07edf3a70df3241f4cf376dad507e2cc73e737311bb00b5067f4d7", size = 245147, upload-time = "2026-10-29T23:05:08.322Z" },
- { url = "https://files.pythonhosted.org/packages/f6/c2/e611b881c05f7f2a5c88f9d89236afb091b8adba3ac89ff9040b6a837be4/wrapt-2.5.2-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:2bf23cde96e0f771a6a2c68973c1ded45c9707f8bd519ede30ecf075f3c720f6", size = 225491, upload-time = "2026-10-29T23:05:10.102Z" },
- { url = "https://files.pythonhosted.org/packages/f3/f4/bdb253208ec907097809e34c25d199cd66686a7974257e08b57d96e3176c/wrapt-2.5.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:eff91af619ad5d587f8855f6c02164e12d33d205c702eb73d487deb499520265", size = 237319, upload-time = "2026-10-29T23:05:11.625Z" },
- { url = "https://files.pythonhosted.org/packages/67/86/74205217834201f27bc73a76b4db67f488fd07787e12a045fb2b39f09c29/wrapt-2.5.2-cp313-cp313-win32.whl", hash = "sha256:11655df8e17658a67628cdc97fe95d6b263e023213cf18067f21e5ec0b0285f3", size = 100634, upload-time = "2026-10-29T23:05:13.073Z" },
- { url = "https://files.pythonhosted.org/packages/e0/56/7c732aed39116853d74485b4431a0ea37b05bd2bf250bdddc55ceb64f8c0/wrapt-2.5.2-cp313-cp313-win_amd64.whl", hash = "sha256:5726faa7342ba473041bc1a276486d232b42e3927d35427729b266b1b5ecaa42", size = 106044, upload-time = "2026-10-29T23:05:14.372Z" },
- { url = "https://files.pythonhosted.org/packages/0c/a7/c4f7ae891bbfef566aa63effe846f6804dac4c0461601993f4c59deaf955/wrapt-2.5.2-cp313-cp313-win_arm64.whl", hash = "sha256:e1fb4af8aac3573770ed3e64efc39d953342b12bcb6e6479512a945eb5557523", size = 103077, upload-time = "2026-10-29T23:05:15.718Z" },
- { url = "https://files.pythonhosted.org/packages/ac/dc/a259acf2b59eefe400b2a1c6b45bc19b0d0b583722426c9dabb276cc0d19/wrapt-2.5.2-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:229859aebfc909626ea6b5a3ae9523aba43c63fd828f876f2a813f4a5767726f", size = 106620, upload-time = "2026-10-29T23:05:17.256Z" },
- { url = "https://files.pythonhosted.org/packages/f8/c5/b34ca079b7906eb043c53c06896b4e42f3ca9efa97ac10e4e254c88e2ca6/wrapt-2.5.2-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c1ea16d7dfe7d235d29435307b1539495b4925e88b135d532892973a04569245", size = 106555, upload-time = "2026-10-29T23:05:18.656Z" },
- { url = "https://files.pythonhosted.org/packages/e2/46/f66042c9ed3626cdcdf641105f091964f6d38cd406cec96479c4a3f70640/wrapt-2.5.2-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:59b7e6d1dea7c3f38bab21954f587499c27c16ea537058eee4add6e4da1780ea", size = 251681, upload-time = "2026-10-29T23:05:20.035Z" },
- { url = "https://files.pythonhosted.org/packages/70/95/1ebe33ff8053a94b3e1a0bafe31ea94b5e9296f9c5098cbcafc4ee2bcdc9/wrapt-2.5.2-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:cfd508866c421687a99bed9ce5b7b4f26fd9b93d3753685d81947ab37d54c496", size = 250919, upload-time = "2026-10-29T23:05:21.501Z" },
- { url = "https://files.pythonhosted.org/packages/1c/bf/ee90a8674c8f2179ad7aeaf8c5b875fa33a12fec7bf1ccf6cbfcc31fbbf0/wrapt-2.5.2-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:459bb66cd4bc43e35cba1f1ab2bdfb9b507e0d8dc0ec40c93a060c9cb0c5415e", size = 231773, upload-time = "2026-10-29T23:05:22.996Z" },
- { url = "https://files.pythonhosted.org/packages/e8/ee/610ef6bf4f35e4719a5edea6bd0c4d948921d028950bc9669d1fa9234af9/wrapt-2.5.2-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:a8626135ecbb0b892ca79c388581ab0ef57e087f09dc0e51a2f34185d3682055", size = 248171, upload-time = "2026-10-29T23:05:24.481Z" },
- { url = "https://files.pythonhosted.org/packages/c6/10/6f12cf42bd9a34829c902c8a617c8151a4e16e7d7cb015289eede5813f39/wrapt-2.5.2-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:7ffa0be31f651da63ee51e5b74701339feac1860b561a985c7b4869d4600e800", size = 228970, upload-time = "2026-10-29T23:05:25.904Z" },
- { url = "https://files.pythonhosted.org/packages/b1/f1/79417514032b9015b772bf5bf006ca542937b5f6c483b9ab092f9ceff8e9/wrapt-2.5.2-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5ab31d929ca8b2b7bf9320ad2a927edfb1841609951a869a9b98723b285ffbc6", size = 239267, upload-time = "2026-10-29T23:05:27.502Z" },
- { url = "https://files.pythonhosted.org/packages/a1/ce/092f2723758586973c94afa8c38a206ce3720ec5cd3edb64ee9c8b27a1c4/wrapt-2.5.2-cp314-cp314-win32.whl", hash = "sha256:afd99530994449aaac9a58e1e268eb080bb1ffd758be43ab2f8a249ce8970c9c", size = 101091, upload-time = "2026-10-29T23:05:28.955Z" },
- { url = "https://files.pythonhosted.org/packages/23/86/201fb9180290373b7fdff18e8d600151694fdb0b10892cdee77c13eb7e6b/wrapt-2.5.2-cp314-cp314-win_amd64.whl", hash = "sha256:fabc60720d1e4a36bbe8aaf1c32024f04c6674991313f5acaca5eeadb564c600", size = 106417, upload-time = "2026-10-29T23:05:30.395Z" },
- { url = "https://files.pythonhosted.org/packages/73/a8/cc313dc9230f4b5029e24867a1fc1dbcb929b792b02ac5fb2e0d79df7401/wrapt-2.5.2-cp314-cp314-win_arm64.whl", hash = "sha256:1c17ca5ce83c7145eddaa20073398ec2192dad65c8d8ff4d9b78fd2917c46a75", size = 104145, upload-time = "2026-10-29T23:05:31.788Z" },
- { url = "https://files.pythonhosted.org/packages/62/04/15e0c465088fbfdab321a672c88739f68429a18aeb81ad7995a0a5cf2065/wrapt-2.5.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:a60863d874cc6ac917bbf44324e3f195564064dee15c84e83b19e1fbbccd3169", size = 109731, upload-time = "2026-10-29T23:05:33.131Z" },
- { url = "https://files.pythonhosted.org/packages/b1/b6/91d6f77257afabafe19bca698b41d60dde7cf49b59b0126eb25ed6a3a90e/wrapt-2.5.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:d41c45517912610de55546188695e2bd0fbcd70b585087cd759f85a96f47be7e", size = 110032, upload-time = "2026-10-29T23:05:34.589Z" },
- { url = "https://files.pythonhosted.org/packages/9c/84/40901e8ba08f6f18f213fea51daa41a8107ccaae4e0b6e13b842f02f5feb/wrapt-2.5.2-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:48fd5026977adb3e2fda292a6a796967b511935d23f97dc2ae1f127a3a8639d5", size = 284278, upload-time = "2026-10-29T23:05:36.033Z" },
- { url = "https://files.pythonhosted.org/packages/89/af/6bfdffc2688a78019762f0b14171ca47c73fae1421c3324c0850135b212c/wrapt-2.5.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0d7639c3287d4efcdc2c8837f1854d1ecf27aae761714ba3d95e61fe8eb48db3", size = 294060, upload-time = "2026-10-29T23:05:37.515Z" },
- { url = "https://files.pythonhosted.org/packages/56/d1/9a4e3aed00d6d877d2646072d9b6b4c9a558a8ad79a18d3abafc1079cd07/wrapt-2.5.2-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:eeaa40251c40e18c5c97ef11d5dbbad28a7b7b99fdd7a7469662799100d74dc7", size = 263272, upload-time = "2026-10-29T23:05:39.008Z" },
- { url = "https://files.pythonhosted.org/packages/5f/6f/0086e02aae9144a0fa029fb0351a8b2f91fbd28eb191c8f2ced3831cb1ba/wrapt-2.5.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:b9ce67ec8803e6c56e78c9ebf726f61116516212c28dfa3683d0962e15cf9a62", size = 289219, upload-time = "2026-10-29T23:05:40.459Z" },
- { url = "https://files.pythonhosted.org/packages/d2/ac/e342f997166b7d433abc12720b6e30793fcf3a36c5457d22261558b61411/wrapt-2.5.2-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:4d5faf5c3e435207b3a51294ed5a00e5e17e3842442ccbbd864ee9bc0efc63fb", size = 260554, upload-time = "2026-10-29T23:05:41.815Z" },
- { url = "https://files.pythonhosted.org/packages/65/64/48ddff54611f6e08ba70adf1b3dbf36c7b08b7aa5dbf7c65a03672b0eed1/wrapt-2.5.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2861d50a4f330fa140ddc2a9ddee33f7e4f9bdb649a23dc9be8ed6310ffa47fc", size = 278921, upload-time = "2026-10-29T23:05:43.489Z" },
- { url = "https://files.pythonhosted.org/packages/c0/bf/0ca422495491f22656b401832184a87b1e67d1dfb7f31ac8d60525c572ce/wrapt-2.5.2-cp314-cp314t-win32.whl", hash = "sha256:46cee4400437c9f33e755522be9e7ce6ebd097273f464eb6f57bee09787f0c59", size = 103586, upload-time = "2026-10-29T23:05:45.046Z" },
- { url = "https://files.pythonhosted.org/packages/69/17/922d99009a228907cd0a5f5d590bbc176dd70527d3eb06f8b4d74889edf8/wrapt-2.5.2-cp314-cp314t-win_amd64.whl", hash = "sha256:1ad706a42b139ad7f03f32aed57da5e10fe8ed707e29849cfa94f1d49cba1405", size = 110111, upload-time = "2026-10-29T23:05:46.458Z" },
- { url = "https://files.pythonhosted.org/packages/68/45/d168c09643e3207c5618b33a5636137f298b9ded38b3a33fad67dc343403/wrapt-2.5.2-cp314-cp314t-win_arm64.whl", hash = "sha256:450cc93f5a2d58d4a74d384d52db4b41d5728065c0057c9bb4666c6115f2a2c9", size = 106384, upload-time = "2026-10-29T23:05:48.051Z" },
- { url = "https://files.pythonhosted.org/packages/67/8c/3b2b95cd60b78f9450d748854e421fcf48bc350d76ca9e0fe048b87ad796/wrapt-2.5.2-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:2c17ec4310cc06f864799a4c37c1c716d5dcf05ceead2e63ca992a9466903b73", size = 106621, upload-time = "2026-10-29T23:05:49.433Z" },
- { url = "https://files.pythonhosted.org/packages/16/12/c430cd1289711cdf2b6b1c469d5efa527f4815fabab32bff3249545344da/wrapt-2.5.2-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:430070776fb30d925a7a1e13d68086e4731b63eedf4c43ee62fc4c8108920cce", size = 106590, upload-time = "2026-10-29T23:05:50.967Z" },
- { url = "https://files.pythonhosted.org/packages/69/4b/d7230c57ca30023a5630bf5cc0e092ad1cfce4d999a309b8377a36b3c249/wrapt-2.5.2-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:99978d196df45c115273c6119e4d0dacbc67bc267a196506625bf7084b741f0b", size = 252192, upload-time = "2026-10-29T23:05:52.344Z" },
- { url = "https://files.pythonhosted.org/packages/fa/74/6dc841c96b9906e0c466aaeed7f51162129cc8745a91bb28168aa3b45194/wrapt-2.5.2-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:217798fb0cfb31479a264f841b0242bc1bed1ffc70d233e8444a876cec57928c", size = 251207, upload-time = "2026-10-29T23:05:54.011Z" },
- { url = "https://files.pythonhosted.org/packages/52/bb/25e710cc9d3bd25d81d46791dd25029d6f9b9d44b990e3d81755b549ff71/wrapt-2.5.2-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:62b339565cfdfcc0ba71c527b4ad13ac9de25504d474fa4ae5db95d1c7e4c6a2", size = 233048, upload-time = "2026-10-29T23:05:55.796Z" },
- { url = "https://files.pythonhosted.org/packages/4e/80/672640d799862a36abae740e778146dbe155ec8ffd6211b3a99240f599ca/wrapt-2.5.2-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:84619ca6b6f67a7dd53754ad402cafceb4ddc9d8981f144f62718b783f05786e", size = 248479, upload-time = "2026-10-29T23:05:57.346Z" },
- { url = "https://files.pythonhosted.org/packages/f6/34/91eb6724eb7cf64b77637d720ef71cad1ec6e4ce2d15eb548670c47078fa/wrapt-2.5.2-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:08d1d7bcb7dd277b7b976fd914455c3c1088f5c101ac7469ae5a5b8c03be0a0a", size = 230013, upload-time = "2026-10-29T23:05:58.745Z" },
- { url = "https://files.pythonhosted.org/packages/bf/f8/f1fc0c3394819c9a6a3c0d0055b06614a3bef7ad133cf18f9c09a2b2141f/wrapt-2.5.2-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:4e86f3543c67d72b4d4c08c3e351523c2d69cd05932df65937d79bc472e9d62f", size = 239808, upload-time = "2026-10-29T23:06:00.266Z" },
- { url = "https://files.pythonhosted.org/packages/56/d1/70f9e71674d7b6c21c8e8cafc3016f773dc3acb31a131f23ab4af8c1effb/wrapt-2.5.2-cp315-cp315-win32.whl", hash = "sha256:035cd71e2aadfc0ad366ab4785b49e62093370885ea4b527b61b308b7a92ff14", size = 101099, upload-time = "2026-10-29T23:06:01.777Z" },
- { url = "https://files.pythonhosted.org/packages/7c/e6/fdea9e35605a6e7fc71556068ea184fb3e9cc09d1e3be8ca8f26d16339c9/wrapt-2.5.2-cp315-cp315-win_amd64.whl", hash = "sha256:b9f41029f9bc6ba80755b9c07f5e77e9e7aafb71c625b9f67614e4ebfb24302a", size = 106423, upload-time = "2026-10-29T23:06:03.076Z" },
- { url = "https://files.pythonhosted.org/packages/d5/50/8c41d8ba868cde9afd5b68e18f19b94300d091c409ef081b80c2aecda653/wrapt-2.5.2-cp315-cp315-win_arm64.whl", hash = "sha256:ae84013e89ec1874415549a46cfea373e28ddecbb72e1f5456cddfa2b76a28e6", size = 104152, upload-time = "2026-10-29T23:06:04.356Z" },
- { url = "https://files.pythonhosted.org/packages/8a/e7/b43d341b036e2862550eb7fffe2b4130015c39c861f30eb34014a2f032d2/wrapt-2.5.2-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:b680bef5200899524667f813fa34dcfde9313fef9f9fa432985d4bc7b4195ba3", size = 109711, upload-time = "2026-10-29T23:06:05.768Z" },
- { url = "https://files.pythonhosted.org/packages/be/52/8012aaf046264e27969f79bc94cd2afb4729338f5434b90edd3d4bb60b8f/wrapt-2.5.2-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:da51f5ac290f2e024ee842799c6592f68cd880c20f6b3a54305d1b6aeab459a4", size = 110047, upload-time = "2026-10-29T23:06:07.309Z" },
- { url = "https://files.pythonhosted.org/packages/54/a7/628c315d19c6027e8b0e700c0dd7d5c3fd662329c7aa1294315064b87cdd/wrapt-2.5.2-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:b3ebc07822649eda442382c1d20983bc6ec5aa2bf79a600da11b29ecb62a0fb2", size = 285041, upload-time = "2026-10-29T23:06:08.777Z" },
- { url = "https://files.pythonhosted.org/packages/7a/fd/4b418c9655bacb5ce8b0aed894f8355e12940bde74237ec45d9396e1f87f/wrapt-2.5.2-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bbb6e959c1e2c32bbad291f83af6320c8c46bb64e256801e85f55d88a09522d5", size = 295329, upload-time = "2026-10-29T23:06:10.489Z" },
- { url = "https://files.pythonhosted.org/packages/92/24/dbe9776fded6dd5f2bfa1446ec04a33afe5afac1d5fb89cd7f5e96fbc61d/wrapt-2.5.2-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:a0541e62f7b9c0d42130e3469d6ac3b1767f019314ae795646a2850a75c64412", size = 265088, upload-time = "2026-10-29T23:06:11.963Z" },
- { url = "https://files.pythonhosted.org/packages/9d/ee/4103576d31bed902224fed8b1d06b94313491504d1d644e42bceba4de880/wrapt-2.5.2-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:18819c05409a161999be68da8e451636470c35f77e25c8201825a33a7d88c89c", size = 290413, upload-time = "2026-10-29T23:06:13.536Z" },
- { url = "https://files.pythonhosted.org/packages/bc/7c/944e95c25232a5805ea9f25cf2ce90386262db025ba04fbb61f451d9523f/wrapt-2.5.2-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:2594e0be4ee09b2f19828265a39df90e591def44215fe8c5b8d6b83aa4cc18b5", size = 262383, upload-time = "2026-10-29T23:06:14.887Z" },
- { url = "https://files.pythonhosted.org/packages/5d/b3/a4b66497e3bb63d2e94ea1605f4beaf5b6b5dafbb6ce41abd727a6fcb7b5/wrapt-2.5.2-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:32c0387872e67b9dfdd2b599bf31fe127e597ed8526c5f192cdaca57a491e9dc", size = 280180, upload-time = "2026-10-29T23:06:16.314Z" },
- { url = "https://files.pythonhosted.org/packages/91/bd/28d046dccfcade6ad62ad19f2f805e5b2e3f3463431307c3b8bd54e24e7f/wrapt-2.5.2-cp315-cp315t-win32.whl", hash = "sha256:9cb33f293e3aa04b36f7d165dc05b42db85e0a88e20a8fd81f60426d1f17d535", size = 103551, upload-time = "2026-10-29T23:06:17.732Z" },
- { url = "https://files.pythonhosted.org/packages/0d/d9/aef3035a3e3d918eb51e7db454af9e06919f0c5208fa0a9315548fef7734/wrapt-2.5.2-cp315-cp315t-win_amd64.whl", hash = "sha256:7f89423965b23aa62a90bf21c20353056a829ca9a60c01fde9aec9876c99392e", size = 110099, upload-time = "2026-10-29T23:06:19.293Z" },
- { url = "https://files.pythonhosted.org/packages/5f/ea/56eddba6249fae5361b53eb62e2e25ad86cabec4632d943d356aba8dea1b/wrapt-2.5.2-cp315-cp315t-win_arm64.whl", hash = "sha256:fa642d3781c061579cb138773add4a55b16f99de9ebc41603e89a756cf9e62c6", size = 106397, upload-time = "2026-10-29T23:06:20.826Z" },
- { url = "https://files.pythonhosted.org/packages/77/8b/1ea57b936b70f98e8e7620a352578096d778dbee95b262f73c066d210ffb/wrapt-2.5.2-py3-none-any.whl", hash = "sha256:61eb54e9540ea522c2c4752a57549a83d3c67e6704ed708403d28e067aaf90f0", size = 82025, upload-time = "2026-10-29T23:06:38.56Z" },
+version = "2.5.1"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/04/22/89e2f3bdae5cb34e0cab0cd86d7172dbf418de4b46c9b17b9c7a560dfa44/wrapt-2.5.1.tar.gz", hash = "sha256:f595bb0185aab3e9dc31950c95d914f56ea8278810c3b928f3426e12ed6d27bc", size = 184455, upload-time = "2026-10-14T00:39:39.24Z" }
+wheels = [
+ { url = "https://files.pythonhosted.org/packages/c4/2b/0f2ae9e355a0491c202a1331ec405d794c249f4fe9b4db952c0246909786/wrapt-2.5.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:aed178902c2386d7c5d3d23eb96d32c100e34cb8c2390e7ece0e4901ae43f0e7", size = 105587, upload-time = "2026-10-14T00:37:01.614Z" },
+ { url = "https://files.pythonhosted.org/packages/0a/54/a5b9904d341ae255bc5618ac43830ab68ed6420dac62af9a305a6191062a/wrapt-2.5.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:1910be5adc0232cc6e8c0673bf3f41c2ee724547543526bed8d00734458e7bc5", size = 105759, upload-time = "2026-10-14T00:37:03.02Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/bf/8edaec939d7411a58bccb4dc4310246caea5050576ff91b5f5abf6079a9c/wrapt-2.5.1-cp311-cp311-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:c25c594f58ecb676358d6d6b0ff068b8bbbc506dc831c6d17876460c66ce39c2", size = 240110, upload-time = "2026-10-14T00:37:04.448Z" },
+ { url = "https://files.pythonhosted.org/packages/9e/8c/18ad7f24c82cbf689324521abe4dc90078d20a5498d991ec83e03dc60c85/wrapt-2.5.1-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e85a9db9e5a5ccc326edb19e35a5106ba16e451d570a2ec8ea9deb1ea52a3c42", size = 241618, upload-time = "2026-10-14T00:37:06.1Z" },
+ { url = "https://files.pythonhosted.org/packages/d2/1b/607e1fc9a8e8838f1a8516f5b87565c9dc415b474ec09479f9443a5a7305/wrapt-2.5.1-cp311-cp311-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:2c642a83b6703804b571caa3b8b205aacd341b1b37e2b2d89cd70e03e0e9caa6", size = 226604, upload-time = "2026-10-14T00:37:07.899Z" },
+ { url = "https://files.pythonhosted.org/packages/b4/fb/6f637ca3e71ea046148dd17622e023df159a16d99762aad4bbdbac11c76e/wrapt-2.5.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:920f700ef41ee774a1e4778c1f4295e117f1ff3435a7e0cd3e997d10da819d32", size = 239265, upload-time = "2026-10-14T00:37:09.45Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/fc/b746f3a72ee7f56d238debeb3842b2569511cf357f5a35f455e24258dc4f/wrapt-2.5.1-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:3f93ceb0ac4896de45d5a45a8f4e69474da583440589de10b362ddc1db4691ed", size = 223864, upload-time = "2026-10-14T00:37:11.363Z" },
+ { url = "https://files.pythonhosted.org/packages/97/12/290a6385393fddcb01bcc83d78c16a24c50a392fe72fb4936999bfe573ec/wrapt-2.5.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:a88370a7d89fcb1c4953a87673fdd7b4a0eb14a1a4dfce49771f0c827ef44893", size = 229426, upload-time = "2026-10-14T00:37:12.868Z" },
+ { url = "https://files.pythonhosted.org/packages/29/af/b52bb81d4217ed7f0fe285de6754e44cb5e6a8b316fc78c605743f463709/wrapt-2.5.1-cp311-cp311-win32.whl", hash = "sha256:12bee472452019706fa1d4ead093f52a9683b4fe6617953e15bab9acdfdc013f", size = 100684, upload-time = "2026-10-14T00:37:14.59Z" },
+ { url = "https://files.pythonhosted.org/packages/e6/d4/ae9ca837038a7a9aadb906eeca76b42df54e299edb29f8ac2cbad1d55b4f/wrapt-2.5.1-cp311-cp311-win_amd64.whl", hash = "sha256:ce3889e3815f97d46414eb574bffdd9bdb41ff70f503097e2707615a87d4e92c", size = 105878, upload-time = "2026-10-14T00:37:16.057Z" },
+ { url = "https://files.pythonhosted.org/packages/15/5e/0605567a81c7105446cec682bc2235172a37e2bc817e2de3f5bad95ee969/wrapt-2.5.1-cp311-cp311-win_arm64.whl", hash = "sha256:ca7b967e96384abdf7e7182c79f71529997981ece8169f8a8ddb31bc5b57cbec", size = 102825, upload-time = "2026-10-14T00:37:17.492Z" },
+ { url = "https://files.pythonhosted.org/packages/66/9b/c7f97d5493a33b5ed01d3c85745f9bdfdd2e5c2785471b8d8b55a3c273d6/wrapt-2.5.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:6e3eff05ae616671b40d7ad0a504210329e4adc9fb91415663570aca93c5f5cc", size = 106452, upload-time = "2026-10-14T00:37:18.951Z" },
+ { url = "https://files.pythonhosted.org/packages/7f/b0/335b0af2930938678fcde954b29780b26308961b93df5e0192fc182e8b7e/wrapt-2.5.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:c44dd9881626da7d621c23805f26726f6b023cf3e9755f48d092bc9cbef4a8e7", size = 106087, upload-time = "2026-10-14T00:37:20.392Z" },
+ { url = "https://files.pythonhosted.org/packages/4a/5a/2a34ba5a468e9d3d6e5b0733280e1ae3c850bfc5f1d681fc0e97f564d1f2/wrapt-2.5.1-cp312-cp312-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:bfaa998ceeea4d0aa72b40cdd0023d19409504e244b439ff2aa9f01729341c5f", size = 250438, upload-time = "2026-10-14T00:37:21.882Z" },
+ { url = "https://files.pythonhosted.org/packages/b3/d5/3d4ad322af74d3ab2a14f69ba844cdd3edefb555edfc1c1976ec0112d5c4/wrapt-2.5.1-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d6d274ec50a5b208be75596dc44ea253e65deaa6ee3a600babc86dafbb957dfc", size = 250594, upload-time = "2026-10-14T00:37:23.497Z" },
+ { url = "https://files.pythonhosted.org/packages/37/1a/3cbf48425ec2c66aa9645218458da1e19e315abb9766604d3c49e579076c/wrapt-2.5.1-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:1a96e2671c60f9f09ae547b5a815cecb29af16caa68d73693387d0028788cb32", size = 229856, upload-time = "2026-10-14T00:37:25.029Z" },
+ { url = "https://files.pythonhosted.org/packages/cc/e7/b2ea57f4c51258659200565af8617d76992b0fe65e6aad7162dd5720ef05/wrapt-2.5.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:729d644b6acaf4846a4ef81b037857b66a01dea6d227f827c6d71c0b6d656d6c", size = 247153, upload-time = "2026-10-14T00:37:26.67Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/c1/4714743e672ed1084a035a2a4f0edeef7838399753b4856a0dc46ef9487d/wrapt-2.5.1-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:859f67bfc31eb7ab55f237b629cd4ab0441b075912446481f910f7d02066811e", size = 226726, upload-time = "2026-10-14T00:37:28.425Z" },
+ { url = "https://files.pythonhosted.org/packages/91/e3/c00401bcc3485eb9937c3fe4a1cc8fc3b61800b1378ea3a143ea1c30f6f6/wrapt-2.5.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:29b62e87fcd6a1893f669abfd02a596a7fc5cfa79fa57e42c4e650a6c170c67b", size = 238843, upload-time = "2026-10-14T00:37:30.075Z" },
+ { url = "https://files.pythonhosted.org/packages/76/b5/c16759fb0721e63df92b576c2222ce1f11690a8b300fd91b49c55436865c/wrapt-2.5.1-cp312-cp312-win32.whl", hash = "sha256:f1c911818fb076910ef509f2298dfcb966a54a6ff068eebd459632102cf589fb", size = 100488, upload-time = "2026-10-14T00:37:31.625Z" },
+ { url = "https://files.pythonhosted.org/packages/22/d5/39d5a704650f18799f37841442b464edb81cf2015f006eaef26068acc6ea/wrapt-2.5.1-cp312-cp312-win_amd64.whl", hash = "sha256:c39c7130ea0702c4ab0faf12da1df1e02d5174305c17edf02309e2f058c4114f", size = 106048, upload-time = "2026-10-14T00:37:33.188Z" },
+ { url = "https://files.pythonhosted.org/packages/21/bf/65743adeeb5476920c62dad6cded7bc8789e19bd4f9a336d4ac812adb8de/wrapt-2.5.1-cp312-cp312-win_arm64.whl", hash = "sha256:e089a22ff5af1290b8c759a610830bdb2a829ef9c3d7797e4ee32c2f795ed482", size = 103200, upload-time = "2026-10-14T00:37:34.673Z" },
+ { url = "https://files.pythonhosted.org/packages/e4/6d/cfe55762435f36107815d56a2cfbebe7e3129b593c47a670c6eb1d7917d3/wrapt-2.5.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:f98eaf784cd12bc69c77af398084174531007cd81849c962163ccfc6e791f3ea", size = 106068, upload-time = "2026-10-14T00:37:36.087Z" },
+ { url = "https://files.pythonhosted.org/packages/01/b9/41642877fe741db56d240833c8822188b663c4c5d52beb087964774035d4/wrapt-2.5.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:ab6db7d2a18d366cc57c2228253cf26443190aba0a6dd0939b3c1e8ac6e29e2c", size = 106190, upload-time = "2026-10-14T00:37:37.768Z" },
+ { url = "https://files.pythonhosted.org/packages/37/62/20edad100b93552ec5c172e509a9db898a73d5043ae701fcb6e9986f9d33/wrapt-2.5.1-cp313-cp313-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:f1630201b0e2a96bb26304b7adfbd91a4ef486abb5a4c48377444a0bed749f37", size = 248519, upload-time = "2026-10-14T00:37:39.321Z" },
+ { url = "https://files.pythonhosted.org/packages/3d/e9/8d81185bc9a40cfb43d91fc70a1e80ecde752c95dc98f5452cae82037976/wrapt-2.5.1-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:d800c7689154622b0ba2922ceca44a3cf2ef61c3b9a4c4eeb1d8b3050d7ededa", size = 248488, upload-time = "2026-10-14T00:37:40.96Z" },
+ { url = "https://files.pythonhosted.org/packages/8a/88/8431df4fd81f0dfa83e8ede463eed311d083c5a279a56891dc0396b07b0e/wrapt-2.5.1-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:5b53000b424dc2133eaaf22838a2352d3497f5d7c2e7d9a2acfe675ab7225bb1", size = 227392, upload-time = "2026-10-14T00:37:42.599Z" },
+ { url = "https://files.pythonhosted.org/packages/db/8a/ee6f8542eeccad6874faf0b7b2e129952c527a482f1d28940e2111fec2d6/wrapt-2.5.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:76f230a9b07e3cb66646d265398f579abb6128b1bb4cb97c74b1ae5d09e96f31", size = 244972, upload-time = "2026-10-14T00:37:44.209Z" },
+ { url = "https://files.pythonhosted.org/packages/4d/1f/32c59e7fd522409f3863dfecdab5315ee9ba37f96020b6f0adee9d223310/wrapt-2.5.1-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:fd3f878a4aac3c262447ddf43c5f4c18fc67dfc3ba69c4fb1c7a4c4af96abe7e", size = 225316, upload-time = "2026-10-14T00:37:45.948Z" },
+ { url = "https://files.pythonhosted.org/packages/ae/d6/1b9abc1244592034c5db744571e17d663f0f1b0ce6c8ba279c60f6f9c3a8/wrapt-2.5.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:0c9480bdee340a1602cae5a777146ab4be3e384fdcb569fffdf8721032314645", size = 237145, upload-time = "2026-10-14T00:37:47.535Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/ce/8f3b5482f768c1d60fd2557d049c766543fef5ec707037cb410a57eb65ee/wrapt-2.5.1-cp313-cp313-win32.whl", hash = "sha256:dc401274fcc7b15b3b2c12df2ff34024a11925243a7d3daee91c6d7d14f9addf", size = 100462, upload-time = "2026-10-14T00:37:49.21Z" },
+ { url = "https://files.pythonhosted.org/packages/7b/dc/6a5735874ea79816f85c1ec9d92139d7073c20d1881c15ff2108c211354b/wrapt-2.5.1-cp313-cp313-win_amd64.whl", hash = "sha256:09b1893ee4063706574c1813abf479b8b51926633fbdb6f96aab8dc7b0976668", size = 105870, upload-time = "2026-10-14T00:37:50.745Z" },
+ { url = "https://files.pythonhosted.org/packages/08/83/a4e8b5a5a32f8dfc5dad8344f1e2b908f7d8d84b11c3c336bf7f79a5144a/wrapt-2.5.1-cp313-cp313-win_arm64.whl", hash = "sha256:f280c115ea64eff3dcbd68a668ce3f63476a4ba386bbabb318017e286196ea2c", size = 102903, upload-time = "2026-10-14T00:37:52.323Z" },
+ { url = "https://files.pythonhosted.org/packages/25/3d/ec1937283863bbe0d90528e09f2b27cfc0dd7e608fc2b65c804967dee369/wrapt-2.5.1-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:cf63fffcdcd8c60f223d3967bb92cc4fc2e8b46f09e75b67a6a75e6f47c0fc43", size = 106439, upload-time = "2026-10-14T00:37:53.853Z" },
+ { url = "https://files.pythonhosted.org/packages/93/39/cca8afb80dbb9fce6103e59db507a9415291c4dbe97ea055875ff62901fb/wrapt-2.5.1-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:9f0750cbc2e29e4f3c9529d3587d4e7ed8f60638ceafb80b87a95833b0c5acd9", size = 106378, upload-time = "2026-10-14T00:37:55.386Z" },
+ { url = "https://files.pythonhosted.org/packages/aa/a0/e784d7a9fd277a2ee395490ec4df96608b7fe218bb1ef7dced2a1caea490/wrapt-2.5.1-cp314-cp314-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:3cf273b7e8d2038abb7f0a8c6550aff4f617b9d486a9965c8e8acc96a3a04de9", size = 251505, upload-time = "2026-10-14T00:37:57.022Z" },
+ { url = "https://files.pythonhosted.org/packages/81/56/01ebc86b88056f5782b9d50f962fb398c6b82aa11efc98f50c64896d94e3/wrapt-2.5.1-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:380f72610181883f66b41442cfc7c0f7552b42169efb2113def26e6380013d37", size = 250744, upload-time = "2026-10-14T00:37:58.652Z" },
+ { url = "https://files.pythonhosted.org/packages/5f/5c/0e8eaaf31e2d6e7bf13c6eae2fd5b85eaa24e21e06466e6e7a0f35532689/wrapt-2.5.1-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:cef2a8f006410b6134a0d273ec037fea8cc7a6a914f1bd7555ad9788ad788c6e", size = 231598, upload-time = "2026-10-14T00:38:00.505Z" },
+ { url = "https://files.pythonhosted.org/packages/6a/6b/6a3e257e65de6cc0027e78b423942697c74451520ba3797fd86455ec4df8/wrapt-2.5.1-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:9bad4dbb4e61624fcce5f301e37f9e743ecae4f1259a3777b3207eb7eba3dccd", size = 247998, upload-time = "2026-10-14T00:38:02.63Z" },
+ { url = "https://files.pythonhosted.org/packages/22/38/b2b8f3ee22b05f33f5aefb052844a36a0d7edd1eaff2ff4249f97792bea8/wrapt-2.5.1-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:9a34640eb6295f33ca23462977de275fe8f3a50ab339b8918b96d69a7451e2e1", size = 228795, upload-time = "2026-10-14T00:38:04.317Z" },
+ { url = "https://files.pythonhosted.org/packages/29/39/e6c86552286ac27b855042fb9c229c580ed2d4c3ced5d0a1dad5f5ee8c11/wrapt-2.5.1-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:26313f38d18d40a9975123a4ebff9da125ec63ab9ece4f05320a3d8d37d2c1fe", size = 239090, upload-time = "2026-10-14T00:38:06.119Z" },
+ { url = "https://files.pythonhosted.org/packages/e7/7a/aea209f64e894573935b17de26ecfa0efe139b63f170a60be9f13734e0f5/wrapt-2.5.1-cp314-cp314-win32.whl", hash = "sha256:0591e6eace0d186c9ef1ecd1244be5a04e98041424cfca425b684ffe4f0d8030", size = 100919, upload-time = "2026-10-14T00:38:07.874Z" },
+ { url = "https://files.pythonhosted.org/packages/57/24/847096aa49d42990137ed3b940743c8a6da806d39f6c455317114e8ebfde/wrapt-2.5.1-cp314-cp314-win_amd64.whl", hash = "sha256:25ed8b1b39234140d5b5c6a273130c7595e0abece417c3ca3cb378fcea5cd0fe", size = 106243, upload-time = "2026-10-14T00:38:09.49Z" },
+ { url = "https://files.pythonhosted.org/packages/9c/ff/1cdc742133b9fb8558cdf42b2a6c2699bd7c72f7d0606286ec2f9142e20a/wrapt-2.5.1-cp314-cp314-win_arm64.whl", hash = "sha256:6201c7e122f40060a9b50696d80deec8f93b1a235ec0443f51d7a8a42f7044a6", size = 103974, upload-time = "2026-10-14T00:38:11.354Z" },
+ { url = "https://files.pythonhosted.org/packages/fc/6f/c32dc64900f1970a7f991ff5d06788cd636ca2f3ee2f99709d82577ca198/wrapt-2.5.1-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:da847332447db5505162759a4cd5ac374eb8b74841fe97a98ef3de14edd2586d", size = 109552, upload-time = "2026-10-14T00:38:12.965Z" },
+ { url = "https://files.pythonhosted.org/packages/e9/73/a9c8cc82b166e3de42f5fbd88089d2ef9b72e87aac7d6cdddb070335c20b/wrapt-2.5.1-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:9f437dd704abc4ee1bd03bb2d796d362d0e75915e8f3113a7900b3b7ec5f8b47", size = 109853, upload-time = "2026-10-14T00:38:14.565Z" },
+ { url = "https://files.pythonhosted.org/packages/f6/48/f341d82e69ae47df2755847af1c744ff0732543482dbc2373e5e57676621/wrapt-2.5.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:03aa7d2256309b57ddbf317bff2cae5f47e50ea9ae8d582780ebe0b554347b42", size = 284103, upload-time = "2026-10-14T00:38:16.23Z" },
+ { url = "https://files.pythonhosted.org/packages/fd/50/b87c6374377b08ee0783b6c5c31cd41a5e103bc54e7e857e800a0a965550/wrapt-2.5.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fcccaa1484f7dd1091602970988ab741491f9f974013c844f70e45ac1196b80d", size = 293883, upload-time = "2026-10-14T00:38:17.986Z" },
+ { url = "https://files.pythonhosted.org/packages/38/e0/6d0810ae73f7a5180ec366577588ab3ad55fc1bc7e3623e82d5f8528dd2a/wrapt-2.5.1-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:8078186f719a92693199f1e06c4ec72e1e6d374c2e459da18ed5c39d6966d727", size = 263097, upload-time = "2026-10-14T00:38:20.107Z" },
+ { url = "https://files.pythonhosted.org/packages/d6/d3/c890a46f4d395a7935e5a7436f374ceefa362eb612fdd39376dd775e0283/wrapt-2.5.1-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:1425fcf0e70b27053bd610d57bae975856e7897e3f6ba1456d2b80b9d7fd15d1", size = 289045, upload-time = "2026-10-14T00:38:21.913Z" },
+ { url = "https://files.pythonhosted.org/packages/e5/c6/042e30e0d851ca6ea743e6978902527f9166da42d736f074245d1cc54c8f/wrapt-2.5.1-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:b238e955ba34ef2b8897f358b7b868b41b9a02ffd338014b62985fa91898cc4a", size = 260378, upload-time = "2026-10-14T00:38:23.662Z" },
+ { url = "https://files.pythonhosted.org/packages/31/a4/5e65f90bf414c2f1c7eefc3d26c33af01d87ba33c4b879db4e1d4ba7fc3b/wrapt-2.5.1-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:25eb4d928a9abeaf70ca786a35861b46d1ab37cc4ce49ea70a070dacdead4dfe", size = 278746, upload-time = "2026-10-14T00:38:25.523Z" },
+ { url = "https://files.pythonhosted.org/packages/4b/86/17a85475e218e05225a4f6d65237b12b280596e04520df0e3d8841c4eae3/wrapt-2.5.1-cp314-cp314t-win32.whl", hash = "sha256:df6e3a36170cda0d313be50fe5065948e7f12f3a181b38cbc262e9f2ee4824e1", size = 103415, upload-time = "2026-10-14T00:38:27.409Z" },
+ { url = "https://files.pythonhosted.org/packages/27/1c/495b3aebbbe5aebf52ae5f9e8ddd0e072a412b9f5a9bc68e5eba43fa26ba/wrapt-2.5.1-cp314-cp314t-win_amd64.whl", hash = "sha256:bc5c0203d383403043fb86c964bd0bab4fcbfb26004ff4bb9c6d02ebc1d608ae", size = 109940, upload-time = "2026-10-14T00:38:29.145Z" },
+ { url = "https://files.pythonhosted.org/packages/28/4d/030ecd98da4d052c264290c4fb9f984706c9a19026154c833580e8624a05/wrapt-2.5.1-cp314-cp314t-win_arm64.whl", hash = "sha256:a424e8a9776c06aef6313af1d0e3fe6e0838af4241d0c09eb0a3b46f2c9a5ff3", size = 106214, upload-time = "2026-10-14T00:38:30.795Z" },
+ { url = "https://files.pythonhosted.org/packages/90/2b/eec5745baaad284fa19232f47796914134e1ea2dd21e289b012aa7981323/wrapt-2.5.1-cp315-cp315-macosx_10_15_x86_64.whl", hash = "sha256:a18e63910252eb75d8806b4baefbc3a03612502f63eab042e3741b00b719f043", size = 106441, upload-time = "2026-10-14T00:38:32.504Z" },
+ { url = "https://files.pythonhosted.org/packages/29/f3/976b0f014a08654289358d41a799c2d24642151b091cfe18a8b91766ee28/wrapt-2.5.1-cp315-cp315-macosx_11_0_arm64.whl", hash = "sha256:183bf0bb893f783c9d22f953cb01fababb9f618e098763f8e66337b575b0647a", size = 106411, upload-time = "2026-10-14T00:38:34.338Z" },
+ { url = "https://files.pythonhosted.org/packages/c7/f4/4b94583d9bec0ff0573a5f10fab295675e3b9a64b701c4609b1a1982c390/wrapt-2.5.1-cp315-cp315-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:a1e823aecb3746b8f9e0aee2e1413887871ee2f5c502a3e0ef8d466dbd4adde1", size = 252017, upload-time = "2026-10-14T00:38:36.142Z" },
+ { url = "https://files.pythonhosted.org/packages/5e/3c/4f9ba033343b2935a453188f97866f0bd4f7748748ab307aaefb18be3a0a/wrapt-2.5.1-cp315-cp315-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:bde5d1b37101b1e9dd3da1f35072e2e7028e9c5e3511f7d76d3fdd4d071b7663", size = 251030, upload-time = "2026-10-14T00:38:38.003Z" },
+ { url = "https://files.pythonhosted.org/packages/69/a1/704c761913be404ed893d05702eeda5ff96c5d8448271c28b80101202bcb/wrapt-2.5.1-cp315-cp315-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:12d3d2b9d6553df6e2421ab99e1cc5413509076788f57fcb3169f5ce100a19d1", size = 232875, upload-time = "2026-10-14T00:38:40.124Z" },
+ { url = "https://files.pythonhosted.org/packages/f0/3a/779ca20fb8c70238069efd0a2b60ea3da450e57c4f103748db0247f52f3a/wrapt-2.5.1-cp315-cp315-musllinux_1_2_aarch64.whl", hash = "sha256:521bd5ef2a33171fac08a0a302d51a983c19c3519406c1ee8da7ce29285488da", size = 248302, upload-time = "2026-10-14T00:38:42.138Z" },
+ { url = "https://files.pythonhosted.org/packages/03/02/80e13786204ce8e1002edb66d06a3194906c0dbbc038bce7bbda426d0f2b/wrapt-2.5.1-cp315-cp315-musllinux_1_2_riscv64.whl", hash = "sha256:129cab3c7b21e68e693c2819a95c47f3b1c41a834b931154688c83b6aef6bdab", size = 229839, upload-time = "2026-10-14T00:38:44.039Z" },
+ { url = "https://files.pythonhosted.org/packages/9d/d1/14c0d041375ae5d0a12445b5c5bc61b109df954cd5bfb852736cd4281cbd/wrapt-2.5.1-cp315-cp315-musllinux_1_2_x86_64.whl", hash = "sha256:8a7c078323e6e1534968cb85488c5eb7ee2b9bbd0f8a291095213a763da40dab", size = 239634, upload-time = "2026-10-14T00:38:45.896Z" },
+ { url = "https://files.pythonhosted.org/packages/97/6e/dacc92526fbed1013eb903406ce961da6ff2a1e66b7349a253f439b979aa/wrapt-2.5.1-cp315-cp315-win32.whl", hash = "sha256:736c1de0230c6d24327b14684794214167b2c5ebb6332e28a10f504641b600df", size = 100931, upload-time = "2026-10-14T00:38:47.718Z" },
+ { url = "https://files.pythonhosted.org/packages/e8/e4/84bbd88554052958ecbbb481a75559b1e40753aba52ec86e3e1efb50ccf0/wrapt-2.5.1-cp315-cp315-win_amd64.whl", hash = "sha256:69fd0fbb3daf7c8c6f5e062847a0061f880f347374d74cf1daba57220fb64cd0", size = 106252, upload-time = "2026-10-14T00:38:49.511Z" },
+ { url = "https://files.pythonhosted.org/packages/b7/98/98d4c4524e8af70ccf35b66864be29ea9d232e5a918efc1dbcf5c87a039d/wrapt-2.5.1-cp315-cp315-win_arm64.whl", hash = "sha256:051220e5071fdfb1a6678707c8abb7bbf4824d40f99758394b2b4d64855fb284", size = 103980, upload-time = "2026-10-14T00:38:51.384Z" },
+ { url = "https://files.pythonhosted.org/packages/75/d9/4b242519d6d29eabb73cb9e50e645e014eb2c13f022601151953b3e81946/wrapt-2.5.1-cp315-cp315t-macosx_10_15_x86_64.whl", hash = "sha256:711e73da3d7983547fc9dd208973b6b0c52640822f5d477910ba24622df6ba64", size = 109538, upload-time = "2026-10-14T00:38:53.124Z" },
+ { url = "https://files.pythonhosted.org/packages/58/05/e434f56fcceaafb251cc56c03107ae278e99158466b49f4146f7b33a2532/wrapt-2.5.1-cp315-cp315t-macosx_11_0_arm64.whl", hash = "sha256:5be9816d9de88f02fce23cf55f392403411d9bd9c7ae57fdc965a43b22e2de5e", size = 109874, upload-time = "2026-10-14T00:38:54.941Z" },
+ { url = "https://files.pythonhosted.org/packages/23/09/d2c0b34d02804018225279a157307b873c8d0f8452790efdd7f0004387a8/wrapt-2.5.1-cp315-cp315t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl", hash = "sha256:4b3f410c416752e1dba53d361e2e6562f22c2c3ec855740dfa5836e061b22571", size = 284866, upload-time = "2026-10-14T00:38:57.081Z" },
+ { url = "https://files.pythonhosted.org/packages/71/6f/2b56319c0565d9a6b63eeba2f11f3e699dc324aaa460f1aae079bd4507b0/wrapt-2.5.1-cp315-cp315t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:094b847491b813b6e6c1775e03770930d75078c0821adf929ac712830951ef25", size = 295154, upload-time = "2026-10-14T00:38:59.007Z" },
+ { url = "https://files.pythonhosted.org/packages/3c/1b/9ac4238a1a839457d6b687b9e9c35d57ba1b2050a42d6bf8c93176bc1bdf/wrapt-2.5.1-cp315-cp315t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:26d8ea2ec6818aeb656bd8a9e745a6f1fb0edfcd8f54291ccd94f62eb5f5e3bd", size = 264914, upload-time = "2026-10-14T00:39:01.154Z" },
+ { url = "https://files.pythonhosted.org/packages/4f/95/9faed8e5f6e5431edd36b2cfb4f305df520c197ba3639e1c78d11c70a068/wrapt-2.5.1-cp315-cp315t-musllinux_1_2_aarch64.whl", hash = "sha256:0a526227efe17dd94bd16b123d170f879bce42c15f10eb92495a745f54caa943", size = 290238, upload-time = "2026-10-14T00:39:03.052Z" },
+ { url = "https://files.pythonhosted.org/packages/b5/52/cae26590ef8ee46b55aa8b211c507f6e5ec0fbd241a6730bf7da024b5dab/wrapt-2.5.1-cp315-cp315t-musllinux_1_2_riscv64.whl", hash = "sha256:36d7d0ad593c4f1a651e4032de834db59aee1a929ee396cd483895b673328e51", size = 262208, upload-time = "2026-10-14T00:39:05.008Z" },
+ { url = "https://files.pythonhosted.org/packages/00/f3/34e5008307be4169592e99de52946cd8800b98097e2a145da11484c7b3e4/wrapt-2.5.1-cp315-cp315t-musllinux_1_2_x86_64.whl", hash = "sha256:89d9a8607b7028054bb6fd01d437f205534a5d59d53c3665d15949a99a2fce0d", size = 280005, upload-time = "2026-10-14T00:39:07.048Z" },
+ { url = "https://files.pythonhosted.org/packages/f3/f9/64e000aa84a88c52a481c7c8b011c80a8ae60bd5f68598e567a918a624a0/wrapt-2.5.1-cp315-cp315t-win32.whl", hash = "sha256:ad81bf81b0a0b6c6ec74169638202851962843e86749570c463eecc55072f93b", size = 103378, upload-time = "2026-10-14T00:39:09.336Z" },
+ { url = "https://files.pythonhosted.org/packages/2f/e4/69efa7c6e8535c5188e041ac278079949fb2daaa97e6f08beb91cf31b3d1/wrapt-2.5.1-cp315-cp315t-win_amd64.whl", hash = "sha256:d5b665a43fe0d3b390cbdd3c003d61c92fa07bd5e3fb1ed3f47920c2d03cd9fd", size = 109928, upload-time = "2026-10-14T00:39:11.156Z" },
+ { url = "https://files.pythonhosted.org/packages/13/77/6e414b3388b9f1ecb76107ef4a2aae501f1bdfcab85c8e34ef78f7db22db/wrapt-2.5.1-cp315-cp315t-win_arm64.whl", hash = "sha256:6405ff2160af9d59132ebb076eda0304db44d9d09809582932412ef7c0788a36", size = 106226, upload-time = "2026-10-14T00:39:13.061Z" },
+ { url = "https://files.pythonhosted.org/packages/bc/0c/7da7513ddcc8f1d831ec4bfbedc9f7f174ecb91042bc16916fc1e0d06b22/wrapt-2.5.1-py3-none-any.whl", hash = "sha256:c6e6c226b1ca5402d7ae5fb34a0d21f1b49124fe4200e5884d1e19e53c47ac1d", size = 81849, upload-time = "2026-10-14T00:39:37.441Z" },
]
[[package]]
@@ -7004,7 +7079,7 @@ wheels = [
[[package]]
name = "zensical"
-version = "0.0.74"
+version = "0.0.72"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
@@ -7016,27 +7091,17 @@ dependencies = [
{ name = "pymdown-extensions" },
{ name = "pyyaml" },
{ name = "tomli" },
- { name = "zensical-ui" },
-]
-sdist = { url = "https://files.pythonhosted.org/packages/06/41/147eed1f072917de5d91e2aa0c2204edf87e5e9c7cd34c314177969d5281/zensical-0.0.74.tar.gz", hash = "sha256:f2ddd17daead93fdbcaa42b5bdc367f9a4808bad282fe706763cb52dba29d96b", size = 514731, upload-time = "2026-10-30T22:09:31.956Z" }
-wheels = [
- { url = "https://files.pythonhosted.org/packages/ee/a3/4031992e04a323f09b3fb89531fdce6c13f83650533a39809ddab9b4a4ef/zensical-0.0.74-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:44f5afe36d1fda5a80a2e9b24cecd25e2d6e6f1f05845299284108d7be40a48b", size = 8621183, upload-time = "2026-10-30T22:09:09.263Z" },
- { url = "https://files.pythonhosted.org/packages/9c/0a/6d56d6886637927204180ddf8480483f9ba4f838cf23f871c02845e8b1ef/zensical-0.0.74-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:912a6a5eb834b283e31ca89341026ccb0bba260351f9123139dbb476a3daf505", size = 8217741, upload-time = "2026-10-30T22:09:12.401Z" },
- { url = "https://files.pythonhosted.org/packages/e3/88/deedbf55d985629af31cdbd126a83d60c89440d32d94fd86c9ab7b13a215/zensical-0.0.74-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7722e1c2505925a477719b79fdc25defe53ce032bab1d769bd4861b130cf149c", size = 8661389, upload-time = "2026-10-30T22:09:14.707Z" },
- { url = "https://files.pythonhosted.org/packages/92/48/77a87b588801a6945a274415bffd0ffe7d7781424bec449d54422780784f/zensical-0.0.74-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9da81ad4bb42bdd92a52d9e42623eaa25c936fa0df0d26e507dcd282e8bf85b2", size = 9151632, upload-time = "2026-10-30T22:09:17.611Z" },
- { url = "https://files.pythonhosted.org/packages/20/80/c8ffff0de58e6f8810b1d7609442efa2919058c8d1035d266db4b2aad7d9/zensical-0.0.74-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:4df8506f0c13226436c6955203b3332e5334b579a1daeeb3c9e7749c726e5327", size = 8830888, upload-time = "2026-10-30T22:09:20.736Z" },
- { url = "https://files.pythonhosted.org/packages/4a/c6/3eff7d34d94fe1978637d60094db4c407e9f3779a41f37fa678a6d8e7356/zensical-0.0.74-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6a6fad6241a6fb04012ca87bf31e11f7cc3b3248a84fe4bba3e3b86a3506cb09", size = 9422666, upload-time = "2026-10-30T22:09:23.882Z" },
- { url = "https://files.pythonhosted.org/packages/cf/91/dc6a6783b62d82ad872beebb14339d0e9b8372dd8b74bd5c4b388d8dab1d/zensical-0.0.74-cp311-abi3-win_amd64.whl", hash = "sha256:491ee04190f8fc0b731a40d99062f979a508b522265c5042cbd79eb7a59e250d", size = 9356286, upload-time = "2026-10-30T22:09:26.574Z" },
- { url = "https://files.pythonhosted.org/packages/9f/f2/fa31d226655699c974137499a98dd5a188882f0c9460ba584107e072ad2d/zensical-0.0.74-cp311-abi3-win_arm64.whl", hash = "sha256:a43b3801fb6e90f774d32b4e485a81cdf028a5765d0d5398d28cf406be888731", size = 8750302, upload-time = "2026-10-30T22:09:29.718Z" },
]
-
-[[package]]
-name = "zensical-ui"
-version = "0.0.41"
-source = { registry = "https://pypi.org/simple" }
-sdist = { url = "https://files.pythonhosted.org/packages/7e/a7/77f1d045900f37f703bef9f64af8c8ca3e9419ca6dbfbc7885e33ca8f2a1/zensical_ui-0.0.41.tar.gz", hash = "sha256:2268e3533a2e186eb7b67dcc8ccdbfc5994fe114cda9382a505f306256df3d88", size = 4063232, upload-time = "2026-10-30T19:06:00.915Z" }
+sdist = { url = "https://files.pythonhosted.org/packages/bf/02/e10dc98fc5e204500a2b208624696bba28afdac5a3aaa399e4252f4fa1fd/zensical-0.0.72.tar.gz", hash = "sha256:2801fcd8baec6e4d14ada113e03a6fb4c6ec2efa2518dc0ae94ee54ed71c94f1", size = 4348165, upload-time = "2026-10-23T14:52:27.771Z" }
wheels = [
- { url = "https://files.pythonhosted.org/packages/ce/84/bb9f8022f2a4fc57c53e135d24318c880205038da644432d443598e752d1/zensical_ui-0.0.41-py3-none-any.whl", hash = "sha256:f0cd5b4351c130257df65b2100fc7116e72c777b1fbc596fee64a9f5de4ccdad", size = 9961148, upload-time = "2026-10-30T19:05:58.612Z" },
+ { url = "https://files.pythonhosted.org/packages/df/b2/30d59a91ed2828fbe6d9a297990dd6de1fd3c5d722354421d80352bba322/zensical-0.0.72-cp311-abi3-macosx_10_12_x86_64.whl", hash = "sha256:9afa0bdb357e0197f5d65cb882e2ad1652d7a3790e2fc3d243f0669092aab536", size = 18269323, upload-time = "2026-10-23T14:52:02.987Z" },
+ { url = "https://files.pythonhosted.org/packages/2b/46/33d705f40d8fb3c2291fff63fce8b0688d11e11b4729c7bd11491ead3649/zensical-0.0.72-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:2103dabfb6fcbea16ffbf226b5405389f3ac67370293eb649f949f03314dd64c", size = 17790144, upload-time = "2026-10-23T14:52:06.092Z" },
+ { url = "https://files.pythonhosted.org/packages/45/02/9d4ed7fa43f0cc8464e4ba4e844201c83abcfce9043073c06acf3518f4ba/zensical-0.0.72-cp311-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3631d44074d4b28448e710d7b54a506caacb6156b38bd0d91d6a033d65da6ad3", size = 18290933, upload-time = "2026-10-23T14:52:09.003Z" },
+ { url = "https://files.pythonhosted.org/packages/98/d8/d176cc7515465c5347c20b412f7e52313f051dc4bcf585b18060044d0d61/zensical-0.0.72-cp311-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3513bf501ff5e28b9e8b7255f225bb117d5651c5d50af39a1eb7bb3bbd7251b0", size = 18789079, upload-time = "2026-10-23T14:52:13.896Z" },
+ { url = "https://files.pythonhosted.org/packages/07/01/e378f3cb316bd0f3d1e7c2610d4eb8e6a4adf8797fdfebcaf6cb7e15f85f/zensical-0.0.72-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:a0603827ea0a04f920cf2a5be555f9e81818beaac1ff5a92399f19f79e330b2e", size = 18467423, upload-time = "2026-10-23T14:52:16.554Z" },
+ { url = "https://files.pythonhosted.org/packages/95/b3/979d2344d0efcb3d43bdcf613fa9846daae174993ba79ba5bab6e02aad45/zensical-0.0.72-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:042795a983823efd2478630164afa708b04b6aff7708a04177657e57292bc1b8", size = 19047304, upload-time = "2026-10-23T14:52:19.283Z" },
+ { url = "https://files.pythonhosted.org/packages/9f/aa/0bac4dc630d77048e2adb9ba7694243f0dba247c3f7400898f96c69b74c3/zensical-0.0.72-cp311-abi3-win_amd64.whl", hash = "sha256:40cd37e4525a24d0231635dafb95fc6e431fa021f26b0a1be2b049ea33698b80", size = 18996397, upload-time = "2026-10-23T14:52:21.987Z" },
+ { url = "https://files.pythonhosted.org/packages/30/a9/97efa66e614383849d0d926bdf34599a7698284e08318839035e55a56fed/zensical-0.0.72-cp311-abi3-win_arm64.whl", hash = "sha256:23514bbf0b3f297d3473b25bee513a133722d020b363d668ef15a4f713d76cde", size = 18400138, upload-time = "2026-10-23T14:52:24.968Z" },
]
[[package]]
diff --git a/zensical.toml b/zensical.toml
index 25aa22156..510127b02 100644
--- a/zensical.toml
+++ b/zensical.toml
@@ -70,6 +70,8 @@ nav = [
{ "MySQL & MariaDB" = "databases/mysql.md" },
{ "Oracle Database" = "databases/oracledb.md" },
{ "SQL Server" = "databases/mssql.md" },
+ { "Sybase ASE" = "databases/sybase.md" },
+ { "SAP SQL Anywhere" = "databases/sqlanywhere.md" },
{ "IBM Db2" = "databases/db2.md" },
{ "DuckDB" = "databases/duckdb.md" },
{ "BigQuery" = "databases/bigquery.md" },
@@ -128,6 +130,22 @@ nav = [
{ "Data Dictionary" = [
"reference/data-dictionary.md",
{ "Runtime & DDL" = "reference/data-dictionary/runtime.md" },
+ { "Dialect SQL Catalogs" = [
+ "reference/data-dictionary/dialects/index.md",
+ { "PostgreSQL" = "reference/data-dictionary/dialects/postgres.md" },
+ { "CockroachDB" = "reference/data-dictionary/dialects/cockroachdb.md" },
+ { "SQLite" = "reference/data-dictionary/dialects/sqlite.md" },
+ { "DuckDB" = "reference/data-dictionary/dialects/duckdb.md" },
+ { "MySQL" = "reference/data-dictionary/dialects/mysql.md" },
+ { "MariaDB" = "reference/data-dictionary/dialects/mariadb.md" },
+ { "Oracle" = "reference/data-dictionary/dialects/oracle.md" },
+ { "SQL Server" = "reference/data-dictionary/dialects/mssql.md" },
+ { "Sybase ASE" = "reference/data-dictionary/dialects/sybase.md" },
+ { "SAP SQL Anywhere" = "reference/data-dictionary/dialects/sqlanywhere.md" },
+ { "IBM Db2" = "reference/data-dictionary/dialects/db2.md" },
+ { "BigQuery" = "reference/data-dictionary/dialects/bigquery.md" },
+ { "Cloud Spanner" = "reference/data-dictionary/dialects/spanner.md" },
+ ] },
] },
{ "Observability" = "reference/observability.md" },
{ "Service Layer" = "reference/service.md" },
@@ -162,6 +180,8 @@ nav = [
{ "oracledb" = "reference/adapters/oracledb.md" },
{ "mssql-python" = "reference/adapters/mssql-python.md" },
{ "pymssql" = "reference/adapters/pymssql.md" },
+ { "sybase" = "reference/adapters/sybase.md" },
+ { "sqlanywhere" = "reference/adapters/sqlanywhere.md" },
{ "db2" = "reference/adapters/db2.md" },
{ "duckdb" = "reference/adapters/duckdb.md" },
{ "bigquery" = "reference/adapters/bigquery.md" },
@@ -322,7 +342,7 @@ heading_level = 3
inherited_members = false
line_length = 80
members_order = "source"
-merge_init_into_class = false
+merge_init_into_class = true
modernize_annotations = true
annotations_path = "brief"
preload_modules = ["sqlspec"]
@@ -330,17 +350,17 @@ separate_signature = true
show_bases = true
show_category_heading = false
show_if_no_docstring = true
-show_labels = false
+show_labels = true
show_object_full_path = false
show_overloads = false
show_root_full_path = false
show_root_heading = true
show_root_toc_entry = true
-show_signature_annotations = false
+show_signature_annotations = true
show_source = false
show_symbol_type_heading = true
show_symbol_type_toc = false
-signature_crossrefs = false
+signature_crossrefs = true
summary = false
unwrap_annotated = true
filters = ["!^_"]