sqlspec 0.47.0__cp314-cp314-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- f68e0789eb443ecb1c2c__mypyc.cp314-win_amd64.pyd +0 -0
- sqlspec/__init__.py +167 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_typing.py +714 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +13 -0
- sqlspec/adapters/adbc/_typing.py +106 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +1280 -0
- sqlspec/adapters/adbc/config.py +378 -0
- sqlspec/adapters/adbc/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/adbc/core.py +922 -0
- sqlspec/adapters/adbc/data_dictionary.py +339 -0
- sqlspec/adapters/adbc/driver.py +534 -0
- sqlspec/adapters/adbc/events/__init__.py +5 -0
- sqlspec/adapters/adbc/events/store.py +285 -0
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +534 -0
- sqlspec/adapters/adbc/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/adbc/type_converter.py +142 -0
- sqlspec/adapters/aiomysql/__init__.py +21 -0
- sqlspec/adapters/aiomysql/_typing.py +137 -0
- sqlspec/adapters/aiomysql/adk/__init__.py +5 -0
- sqlspec/adapters/aiomysql/adk/store.py +678 -0
- sqlspec/adapters/aiomysql/config.py +305 -0
- sqlspec/adapters/aiomysql/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiomysql/core.py +536 -0
- sqlspec/adapters/aiomysql/data_dictionary.py +121 -0
- sqlspec/adapters/aiomysql/driver.py +386 -0
- sqlspec/adapters/aiomysql/events/__init__.py +5 -0
- sqlspec/adapters/aiomysql/events/store.py +104 -0
- sqlspec/adapters/aiomysql/litestar/__init__.py +5 -0
- sqlspec/adapters/aiomysql/litestar/store.py +314 -0
- sqlspec/adapters/aiosqlite/__init__.py +26 -0
- sqlspec/adapters/aiosqlite/_typing.py +109 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +829 -0
- sqlspec/adapters/aiosqlite/config.py +315 -0
- sqlspec/adapters/aiosqlite/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiosqlite/core.py +315 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +202 -0
- sqlspec/adapters/aiosqlite/driver.py +311 -0
- sqlspec/adapters/aiosqlite/events/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/events/store.py +20 -0
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +279 -0
- sqlspec/adapters/aiosqlite/pool.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiosqlite/pool.py +734 -0
- sqlspec/adapters/asyncmy/__init__.py +21 -0
- sqlspec/adapters/asyncmy/_typing.py +113 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +644 -0
- sqlspec/adapters/asyncmy/config.py +307 -0
- sqlspec/adapters/asyncmy/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/asyncmy/core.py +538 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +122 -0
- sqlspec/adapters/asyncmy/driver.py +391 -0
- sqlspec/adapters/asyncmy/events/__init__.py +5 -0
- sqlspec/adapters/asyncmy/events/store.py +104 -0
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +26 -0
- sqlspec/adapters/asyncpg/_typing.py +103 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +483 -0
- sqlspec/adapters/asyncpg/config.py +575 -0
- sqlspec/adapters/asyncpg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/asyncpg/core.py +480 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +157 -0
- sqlspec/adapters/asyncpg/driver.py +487 -0
- sqlspec/adapters/asyncpg/events/__init__.py +6 -0
- sqlspec/adapters/asyncpg/events/_hub.py +181 -0
- sqlspec/adapters/asyncpg/events/backend.py +210 -0
- sqlspec/adapters/asyncpg/events/store.py +40 -0
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +251 -0
- sqlspec/adapters/bigquery/__init__.py +15 -0
- sqlspec/adapters/bigquery/_typing.py +108 -0
- sqlspec/adapters/bigquery/config.py +362 -0
- sqlspec/adapters/bigquery/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/bigquery/core.py +768 -0
- sqlspec/adapters/bigquery/data_dictionary.py +120 -0
- sqlspec/adapters/bigquery/driver.py +542 -0
- sqlspec/adapters/bigquery/events/__init__.py +5 -0
- sqlspec/adapters/bigquery/events/store.py +139 -0
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +325 -0
- sqlspec/adapters/bigquery/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/bigquery/type_converter.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/__init__.py +26 -0
- sqlspec/adapters/cockroach_asyncpg/_typing.py +73 -0
- sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/adk/store.py +465 -0
- sqlspec/adapters/cockroach_asyncpg/config.py +248 -0
- sqlspec/adapters/cockroach_asyncpg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
- sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +110 -0
- sqlspec/adapters/cockroach_asyncpg/driver.py +142 -0
- sqlspec/adapters/cockroach_asyncpg/events/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/events/store.py +20 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/store.py +142 -0
- sqlspec/adapters/cockroach_psycopg/__init__.py +39 -0
- sqlspec/adapters/cockroach_psycopg/_typing.py +137 -0
- sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
- sqlspec/adapters/cockroach_psycopg/adk/store.py +1039 -0
- sqlspec/adapters/cockroach_psycopg/config.py +511 -0
- sqlspec/adapters/cockroach_psycopg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/cockroach_psycopg/core.py +63 -0
- sqlspec/adapters/cockroach_psycopg/data_dictionary.py +220 -0
- sqlspec/adapters/cockroach_psycopg/driver.py +273 -0
- sqlspec/adapters/cockroach_psycopg/events/__init__.py +6 -0
- sqlspec/adapters/cockroach_psycopg/events/store.py +34 -0
- sqlspec/adapters/cockroach_psycopg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_psycopg/litestar/store.py +327 -0
- sqlspec/adapters/duckdb/__init__.py +29 -0
- sqlspec/adapters/duckdb/_typing.py +104 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +935 -0
- sqlspec/adapters/duckdb/config.py +386 -0
- sqlspec/adapters/duckdb/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/core.py +332 -0
- sqlspec/adapters/duckdb/data_dictionary.py +140 -0
- sqlspec/adapters/duckdb/driver.py +426 -0
- sqlspec/adapters/duckdb/events/__init__.py +5 -0
- sqlspec/adapters/duckdb/events/store.py +57 -0
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +330 -0
- sqlspec/adapters/duckdb/pool.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/pool.py +350 -0
- sqlspec/adapters/duckdb/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/type_converter.py +118 -0
- sqlspec/adapters/mysqlconnector/__init__.py +39 -0
- sqlspec/adapters/mysqlconnector/_typing.py +186 -0
- sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
- sqlspec/adapters/mysqlconnector/adk/store.py +1183 -0
- sqlspec/adapters/mysqlconnector/config.py +421 -0
- sqlspec/adapters/mysqlconnector/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/mysqlconnector/core.py +472 -0
- sqlspec/adapters/mysqlconnector/data_dictionary.py +230 -0
- sqlspec/adapters/mysqlconnector/driver.py +516 -0
- sqlspec/adapters/mysqlconnector/events/__init__.py +8 -0
- sqlspec/adapters/mysqlconnector/events/store.py +98 -0
- sqlspec/adapters/mysqlconnector/litestar/__init__.py +5 -0
- sqlspec/adapters/mysqlconnector/litestar/store.py +426 -0
- sqlspec/adapters/oracledb/__init__.py +39 -0
- sqlspec/adapters/oracledb/_json_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_json_handlers.py +196 -0
- sqlspec/adapters/oracledb/_param_types.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_param_types.py +46 -0
- sqlspec/adapters/oracledb/_typing.py +258 -0
- sqlspec/adapters/oracledb/_uuid_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +163 -0
- sqlspec/adapters/oracledb/_vector_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_vector_handlers.py +228 -0
- sqlspec/adapters/oracledb/adk/__init__.py +21 -0
- sqlspec/adapters/oracledb/adk/store.py +2453 -0
- sqlspec/adapters/oracledb/config.py +575 -0
- sqlspec/adapters/oracledb/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/core.py +820 -0
- sqlspec/adapters/oracledb/data_dictionary.py +404 -0
- sqlspec/adapters/oracledb/driver.py +1277 -0
- sqlspec/adapters/oracledb/events/__init__.py +16 -0
- sqlspec/adapters/oracledb/events/_hub.py +345 -0
- sqlspec/adapters/oracledb/events/backend.py +300 -0
- sqlspec/adapters/oracledb/events/store.py +420 -0
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +781 -0
- sqlspec/adapters/oracledb/migrations.py +539 -0
- sqlspec/adapters/oracledb/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/type_converter.py +211 -0
- sqlspec/adapters/psqlpy/__init__.py +18 -0
- sqlspec/adapters/psqlpy/_typing.py +121 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +591 -0
- sqlspec/adapters/psqlpy/config.py +376 -0
- sqlspec/adapters/psqlpy/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psqlpy/core.py +694 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +121 -0
- sqlspec/adapters/psqlpy/driver.py +411 -0
- sqlspec/adapters/psqlpy/events/__init__.py +6 -0
- sqlspec/adapters/psqlpy/events/_hub.py +204 -0
- sqlspec/adapters/psqlpy/events/backend.py +210 -0
- sqlspec/adapters/psqlpy/events/store.py +20 -0
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +270 -0
- sqlspec/adapters/psqlpy/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psqlpy/type_converter.py +113 -0
- sqlspec/adapters/psycopg/__init__.py +38 -0
- sqlspec/adapters/psycopg/_typing.py +218 -0
- sqlspec/adapters/psycopg/adk/__init__.py +10 -0
- sqlspec/adapters/psycopg/adk/store.py +1106 -0
- sqlspec/adapters/psycopg/config.py +695 -0
- sqlspec/adapters/psycopg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psycopg/core.py +520 -0
- sqlspec/adapters/psycopg/data_dictionary.py +278 -0
- sqlspec/adapters/psycopg/driver.py +1033 -0
- sqlspec/adapters/psycopg/events/__init__.py +20 -0
- sqlspec/adapters/psycopg/events/_hub.py +388 -0
- sqlspec/adapters/psycopg/events/backend.py +398 -0
- sqlspec/adapters/psycopg/events/store.py +42 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/psycopg/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psycopg/type_converter.py +93 -0
- sqlspec/adapters/pymysql/__init__.py +21 -0
- sqlspec/adapters/pymysql/_typing.py +92 -0
- sqlspec/adapters/pymysql/adk/__init__.py +5 -0
- sqlspec/adapters/pymysql/adk/store.py +657 -0
- sqlspec/adapters/pymysql/config.py +176 -0
- sqlspec/adapters/pymysql/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/pymysql/core.py +469 -0
- sqlspec/adapters/pymysql/data_dictionary.py +120 -0
- sqlspec/adapters/pymysql/driver.py +271 -0
- sqlspec/adapters/pymysql/events/__init__.py +5 -0
- sqlspec/adapters/pymysql/events/store.py +50 -0
- sqlspec/adapters/pymysql/litestar/__init__.py +5 -0
- sqlspec/adapters/pymysql/litestar/store.py +232 -0
- sqlspec/adapters/pymysql/pool.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/pymysql/pool.py +184 -0
- sqlspec/adapters/spanner/__init__.py +33 -0
- sqlspec/adapters/spanner/_typing.py +102 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +758 -0
- sqlspec/adapters/spanner/config.py +355 -0
- sqlspec/adapters/spanner/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/spanner/core.py +263 -0
- sqlspec/adapters/spanner/data_dictionary.py +120 -0
- sqlspec/adapters/spanner/driver.py +407 -0
- sqlspec/adapters/spanner/events/__init__.py +5 -0
- sqlspec/adapters/spanner/events/store.py +187 -0
- sqlspec/adapters/spanner/litestar/__init__.py +5 -0
- sqlspec/adapters/spanner/litestar/store.py +291 -0
- sqlspec/adapters/spanner/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/spanner/type_converter.py +342 -0
- sqlspec/adapters/sqlite/__init__.py +19 -0
- sqlspec/adapters/sqlite/_typing.py +123 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +992 -0
- sqlspec/adapters/sqlite/config.py +240 -0
- sqlspec/adapters/sqlite/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/core.py +357 -0
- sqlspec/adapters/sqlite/data_dictionary.py +198 -0
- sqlspec/adapters/sqlite/driver.py +527 -0
- sqlspec/adapters/sqlite/events/__init__.py +5 -0
- sqlspec/adapters/sqlite/events/store.py +20 -0
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +316 -0
- sqlspec/adapters/sqlite/pool.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/pool.py +237 -0
- sqlspec/adapters/sqlite/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/type_converter.py +114 -0
- sqlspec/base.py +832 -0
- sqlspec/builder/__init__.py +181 -0
- sqlspec/builder/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_base.py +1071 -0
- sqlspec/builder/_column.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_column.py +521 -0
- sqlspec/builder/_ddl.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_ddl.py +1691 -0
- sqlspec/builder/_delete.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_delete.py +95 -0
- sqlspec/builder/_dml.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_dml.py +386 -0
- sqlspec/builder/_explain.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_explain.py +579 -0
- sqlspec/builder/_expression_wrappers.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_factory.py +1884 -0
- sqlspec/builder/_insert.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_insert.py +405 -0
- sqlspec/builder/_join.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_join.py +489 -0
- sqlspec/builder/_merge.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_merge.py +823 -0
- sqlspec/builder/_parsing_utils.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_parsing_utils.py +295 -0
- sqlspec/builder/_select.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_select.py +1666 -0
- sqlspec/builder/_temporal.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_temporal.py +167 -0
- sqlspec/builder/_update.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_update.py +173 -0
- sqlspec/builder/_vector_distance.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_vector_distance.py +330 -0
- sqlspec/cli.py +1095 -0
- sqlspec/config.py +2383 -0
- sqlspec/core/__init__.py +372 -0
- sqlspec/core/_correlation.cp314-win_amd64.pyd +0 -0
- sqlspec/core/_correlation.py +176 -0
- sqlspec/core/_pagination.py +42 -0
- sqlspec/core/_pool.cp314-win_amd64.pyd +0 -0
- sqlspec/core/_pool.py +76 -0
- sqlspec/core/cache.cp314-win_amd64.pyd +0 -0
- sqlspec/core/cache.py +1085 -0
- sqlspec/core/compiler.cp314-win_amd64.pyd +0 -0
- sqlspec/core/compiler.py +1090 -0
- sqlspec/core/config_runtime.cp314-win_amd64.pyd +0 -0
- sqlspec/core/config_runtime.py +174 -0
- sqlspec/core/explain.cp314-win_amd64.pyd +0 -0
- sqlspec/core/explain.py +275 -0
- sqlspec/core/filters.cp314-win_amd64.pyd +0 -0
- sqlspec/core/filters.py +969 -0
- sqlspec/core/hashing.cp314-win_amd64.pyd +0 -0
- sqlspec/core/hashing.py +266 -0
- sqlspec/core/metrics.cp314-win_amd64.pyd +0 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +72 -0
- sqlspec/core/parameters/_alignment.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_alignment.py +283 -0
- sqlspec/core/parameters/_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_converter.py +554 -0
- sqlspec/core/parameters/_processor.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_processor.py +1182 -0
- sqlspec/core/parameters/_registry.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_registry.py +206 -0
- sqlspec/core/parameters/_transformers.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_transformers.py +324 -0
- sqlspec/core/parameters/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_types.py +536 -0
- sqlspec/core/parameters/_validator.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_validator.py +171 -0
- sqlspec/core/pipeline.cp314-win_amd64.pyd +0 -0
- sqlspec/core/pipeline.py +333 -0
- sqlspec/core/query_modifiers.cp314-win_amd64.pyd +0 -0
- sqlspec/core/query_modifiers.py +508 -0
- sqlspec/core/result/__init__.py +25 -0
- sqlspec/core/result/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/core/result/_base.py +1232 -0
- sqlspec/core/result/_io.cp314-win_amd64.pyd +0 -0
- sqlspec/core/result/_io.py +28 -0
- sqlspec/core/splitter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/splitter.py +1021 -0
- sqlspec/core/sqlcommenter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/sqlcommenter.py +249 -0
- sqlspec/core/stack.cp314-win_amd64.pyd +0 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.cp314-win_amd64.pyd +0 -0
- sqlspec/core/statement.py +1865 -0
- sqlspec/core/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/type_converter.py +340 -0
- sqlspec/data_dictionary/__init__.py +22 -0
- sqlspec/data_dictionary/_loader.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_loader.py +138 -0
- sqlspec/data_dictionary/_registry.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_registry.py +74 -0
- sqlspec/data_dictionary/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_types.py +121 -0
- sqlspec/data_dictionary/dialects/__init__.py +21 -0
- sqlspec/data_dictionary/dialects/bigquery.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/bigquery.py +81 -0
- sqlspec/data_dictionary/dialects/cockroachdb.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/cockroachdb.py +54 -0
- sqlspec/data_dictionary/dialects/duckdb.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/duckdb.py +47 -0
- sqlspec/data_dictionary/dialects/mysql.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/mysql.py +53 -0
- sqlspec/data_dictionary/dialects/oracle.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/oracle.py +197 -0
- sqlspec/data_dictionary/dialects/postgres.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/postgres.py +69 -0
- sqlspec/data_dictionary/dialects/spanner.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/spanner.py +37 -0
- sqlspec/data_dictionary/dialects/sqlite.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/sqlite.py +59 -0
- sqlspec/data_dictionary/sql/.gitkeep +0 -0
- sqlspec/data_dictionary/sql/bigquery/columns.sql +23 -0
- sqlspec/data_dictionary/sql/bigquery/foreign_keys.sql +34 -0
- sqlspec/data_dictionary/sql/bigquery/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/bigquery/tables.sql +33 -0
- sqlspec/data_dictionary/sql/bigquery/version.sql +3 -0
- sqlspec/data_dictionary/sql/cockroachdb/columns.sql +34 -0
- sqlspec/data_dictionary/sql/cockroachdb/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/cockroachdb/indexes.sql +32 -0
- sqlspec/data_dictionary/sql/cockroachdb/tables.sql +44 -0
- sqlspec/data_dictionary/sql/cockroachdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/duckdb/columns.sql +23 -0
- sqlspec/data_dictionary/sql/duckdb/foreign_keys.sql +36 -0
- sqlspec/data_dictionary/sql/duckdb/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/duckdb/tables.sql +38 -0
- sqlspec/data_dictionary/sql/duckdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/mysql/columns.sql +23 -0
- sqlspec/data_dictionary/sql/mysql/foreign_keys.sql +28 -0
- sqlspec/data_dictionary/sql/mysql/indexes.sql +26 -0
- sqlspec/data_dictionary/sql/mysql/tables.sql +33 -0
- sqlspec/data_dictionary/sql/mysql/version.sql +3 -0
- sqlspec/data_dictionary/sql/oracle/columns.sql +23 -0
- sqlspec/data_dictionary/sql/oracle/foreign_keys.sql +48 -0
- sqlspec/data_dictionary/sql/oracle/indexes.sql +44 -0
- sqlspec/data_dictionary/sql/oracle/tables.sql +25 -0
- sqlspec/data_dictionary/sql/oracle/version.sql +20 -0
- sqlspec/data_dictionary/sql/postgres/columns.sql +34 -0
- sqlspec/data_dictionary/sql/postgres/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/postgres/indexes.sql +56 -0
- sqlspec/data_dictionary/sql/postgres/tables.sql +44 -0
- sqlspec/data_dictionary/sql/postgres/version.sql +3 -0
- sqlspec/data_dictionary/sql/spanner/columns.sql +23 -0
- sqlspec/data_dictionary/sql/spanner/foreign_keys.sql +70 -0
- sqlspec/data_dictionary/sql/spanner/indexes.sql +30 -0
- sqlspec/data_dictionary/sql/spanner/tables.sql +9 -0
- sqlspec/data_dictionary/sql/spanner/version.sql +3 -0
- sqlspec/data_dictionary/sql/sqlite/columns.sql +23 -0
- sqlspec/data_dictionary/sql/sqlite/foreign_keys.sql +22 -0
- sqlspec/data_dictionary/sql/sqlite/indexes.sql +7 -0
- sqlspec/data_dictionary/sql/sqlite/tables.sql +28 -0
- sqlspec/data_dictionary/sql/sqlite/version.sql +3 -0
- sqlspec/dialects/__init__.py +22 -0
- sqlspec/dialects/_compat.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/_compat.py +14 -0
- sqlspec/dialects/postgres/__init__.py +9 -0
- sqlspec/dialects/postgres/_generators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/postgres/_generators.py +57 -0
- sqlspec/dialects/postgres/_operators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/postgres/_operators.py +81 -0
- sqlspec/dialects/postgres/_paradedb.py +50 -0
- sqlspec/dialects/postgres/_pgvector.py +36 -0
- sqlspec/dialects/spanner/__init__.py +6 -0
- sqlspec/dialects/spanner/_generators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/spanner/_generators.py +206 -0
- sqlspec/dialects/spanner/_spangres.py +77 -0
- sqlspec/dialects/spanner/_spanner.py +179 -0
- sqlspec/driver/__init__.py +49 -0
- sqlspec/driver/_async.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_async.py +1830 -0
- sqlspec/driver/_common.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_common.py +2292 -0
- sqlspec/driver/_exception_handler.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_exception_handler.py +108 -0
- sqlspec/driver/_query_cache.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_query_cache.py +96 -0
- sqlspec/driver/_sql_helpers.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_sql_helpers.py +139 -0
- sqlspec/driver/_storage_helpers.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_storage_helpers.py +153 -0
- sqlspec/driver/_sync.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_sync.py +1817 -0
- sqlspec/exceptions.cp314-win_amd64.pyd +0 -0
- sqlspec/exceptions.py +480 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +84 -0
- sqlspec/extensions/adk/_config_utils.py +199 -0
- sqlspec/extensions/adk/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/_types.py +41 -0
- sqlspec/extensions/adk/artifact/__init__.py +57 -0
- sqlspec/extensions/adk/artifact/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/artifact/_types.py +32 -0
- sqlspec/extensions/adk/artifact/service.py +508 -0
- sqlspec/extensions/adk/artifact/store.py +361 -0
- sqlspec/extensions/adk/converters.py +212 -0
- sqlspec/extensions/adk/memory/__init__.py +69 -0
- sqlspec/extensions/adk/memory/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/memory/_types.py +30 -0
- sqlspec/extensions/adk/memory/converters.py +225 -0
- sqlspec/extensions/adk/memory/service.py +316 -0
- sqlspec/extensions/adk/memory/store.py +525 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +184 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +279 -0
- sqlspec/extensions/adk/store.py +590 -0
- sqlspec/extensions/events/__init__.py +51 -0
- sqlspec/extensions/events/_channel.py +703 -0
- sqlspec/extensions/events/_hints.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/events/_hints.py +45 -0
- sqlspec/extensions/events/_models.py +23 -0
- sqlspec/extensions/events/_payload.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/events/_payload.py +69 -0
- sqlspec/extensions/events/_protocols.py +134 -0
- sqlspec/extensions/events/_queue.py +462 -0
- sqlspec/extensions/events/_store.py +209 -0
- sqlspec/extensions/events/migrations/0001_create_event_queue.py +59 -0
- sqlspec/extensions/events/migrations/__init__.py +3 -0
- sqlspec/extensions/fastapi/__init__.py +22 -0
- sqlspec/extensions/fastapi/extension.py +391 -0
- sqlspec/extensions/fastapi/providers.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/fastapi/providers.py +712 -0
- sqlspec/extensions/flask/__init__.py +38 -0
- sqlspec/extensions/flask/_state.py +87 -0
- sqlspec/extensions/flask/_utils.py +71 -0
- sqlspec/extensions/flask/extension.py +539 -0
- sqlspec/extensions/litestar/__init__.py +31 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/channels.py +165 -0
- sqlspec/extensions/litestar/cli.py +102 -0
- sqlspec/extensions/litestar/config.py +90 -0
- sqlspec/extensions/litestar/handlers.py +316 -0
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +1066 -0
- sqlspec/extensions/litestar/providers.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/litestar/providers.py +784 -0
- sqlspec/extensions/litestar/store.py +298 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +113 -0
- sqlspec/extensions/sanic/__init__.py +19 -0
- sqlspec/extensions/sanic/_state.py +43 -0
- sqlspec/extensions/sanic/_utils.py +127 -0
- sqlspec/extensions/sanic/extension.py +647 -0
- sqlspec/extensions/starlette/__init__.py +22 -0
- sqlspec/extensions/starlette/_state.py +42 -0
- sqlspec/extensions/starlette/_utils.py +96 -0
- sqlspec/extensions/starlette/extension.py +374 -0
- sqlspec/extensions/starlette/middleware.py +281 -0
- sqlspec/loader.cp314-win_amd64.pyd +0 -0
- sqlspec/loader.py +727 -0
- sqlspec/migrations/__init__.py +39 -0
- sqlspec/migrations/base.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/base.py +862 -0
- sqlspec/migrations/commands.py +2151 -0
- sqlspec/migrations/context.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/context.py +157 -0
- sqlspec/migrations/fix.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/fix.py +204 -0
- sqlspec/migrations/loaders.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/loaders.py +443 -0
- sqlspec/migrations/runner.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/runner.py +1195 -0
- sqlspec/migrations/squash.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/squash.py +490 -0
- sqlspec/migrations/templates.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/tracker.py +792 -0
- sqlspec/migrations/utils.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/validation.py +359 -0
- sqlspec/migrations/version.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/version.py +446 -0
- sqlspec/observability/__init__.py +57 -0
- sqlspec/observability/_common.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_common.py +77 -0
- sqlspec/observability/_config.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_config.py +364 -0
- sqlspec/observability/_diagnostics.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_diagnostics.py +74 -0
- sqlspec/observability/_dispatcher.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_dispatcher.py +200 -0
- sqlspec/observability/_formatters/__init__.py +13 -0
- sqlspec/observability/_formatters/_aws.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_aws.py +102 -0
- sqlspec/observability/_formatters/_azure.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_azure.py +96 -0
- sqlspec/observability/_formatters/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_base.py +57 -0
- sqlspec/observability/_formatters/_gcp.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_gcp.py +131 -0
- sqlspec/observability/_formatting.py +58 -0
- sqlspec/observability/_observer.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_observer.py +361 -0
- sqlspec/observability/_runtime.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_runtime.py +461 -0
- sqlspec/observability/_sampling.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_sampling.py +188 -0
- sqlspec/observability/_spans.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_spans.py +161 -0
- sqlspec/protocols.py +955 -0
- sqlspec/py.typed +0 -0
- sqlspec/service.py +433 -0
- sqlspec/storage/__init__.py +48 -0
- sqlspec/storage/_arrow_payload.py +68 -0
- sqlspec/storage/_paths.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/_paths.py +58 -0
- sqlspec/storage/_utils.py +46 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/backends/base.py +374 -0
- sqlspec/storage/backends/fsspec.py +574 -0
- sqlspec/storage/backends/local.py +468 -0
- sqlspec/storage/backends/obstore.py +956 -0
- sqlspec/storage/errors.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/errors.py +102 -0
- sqlspec/storage/pipeline.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/pipeline.py +628 -0
- sqlspec/storage/registry.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/registry.py +329 -0
- sqlspec/typing.py +405 -0
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/arrow_helpers.py +384 -0
- sqlspec/utils/config_tools.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/config_tools.py +314 -0
- sqlspec/utils/correlation.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/correlation.py +134 -0
- sqlspec/utils/deprecation.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/deprecation.py +157 -0
- sqlspec/utils/dispatch.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/dispatch.py +101 -0
- sqlspec/utils/fixtures.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/fixtures.py +260 -0
- sqlspec/utils/logging.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/logging.py +251 -0
- sqlspec/utils/module_loader.py +306 -0
- sqlspec/utils/portal.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/portal.py +377 -0
- sqlspec/utils/schema.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/schema.py +1040 -0
- sqlspec/utils/serializers/__init__.py +30 -0
- sqlspec/utils/serializers/_json.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_json.py +415 -0
- sqlspec/utils/serializers/_numpy.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_numpy.py +65 -0
- sqlspec/utils/serializers/_schema.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_schema.py +285 -0
- sqlspec/utils/singleton.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/sync_tools.py +316 -0
- sqlspec/utils/text.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/text.py +109 -0
- sqlspec/utils/type_converters.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/type_converters.py +216 -0
- sqlspec/utils/type_guards.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/type_guards.py +1508 -0
- sqlspec/utils/uuids.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/uuids.py +241 -0
- sqlspec-0.47.0.dist-info/METADATA +202 -0
- sqlspec-0.47.0.dist-info/RECORD +621 -0
- sqlspec-0.47.0.dist-info/WHEEL +4 -0
- sqlspec-0.47.0.dist-info/entry_points.txt +6 -0
- sqlspec-0.47.0.dist-info/licenses/LICENSE +21 -0
sqlspec/driver/_async.py
ADDED
|
@@ -0,0 +1,1830 @@
|
|
|
1
|
+
"""Asynchronous driver protocol implementation."""
|
|
2
|
+
|
|
3
|
+
from abc import abstractmethod
|
|
4
|
+
from time import perf_counter
|
|
5
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Final, cast, final, overload
|
|
6
|
+
|
|
7
|
+
from mypy_extensions import mypyc_attr
|
|
8
|
+
|
|
9
|
+
from sqlspec.core import SQL, ProcessedState, StackResult, Statement, create_arrow_result
|
|
10
|
+
from sqlspec.core.result import DMLResult
|
|
11
|
+
from sqlspec.core.stack import StackOperation, StatementStack
|
|
12
|
+
from sqlspec.driver._common import (
|
|
13
|
+
AsyncExceptionHandler,
|
|
14
|
+
CommonDriverAttributesMixin,
|
|
15
|
+
DataDictionaryDialectMixin,
|
|
16
|
+
DataDictionaryMixin,
|
|
17
|
+
ExecutionResult,
|
|
18
|
+
StackExecutionObserver,
|
|
19
|
+
_raise_database_exception,
|
|
20
|
+
describe_stack_statement,
|
|
21
|
+
handle_single_row_error,
|
|
22
|
+
)
|
|
23
|
+
from sqlspec.driver._query_cache import CachedQuery
|
|
24
|
+
from sqlspec.driver._sql_helpers import DEFAULT_PRETTY
|
|
25
|
+
from sqlspec.driver._sql_helpers import convert_to_dialect as _convert_to_dialect_impl
|
|
26
|
+
from sqlspec.driver._storage_helpers import stringify_storage_target
|
|
27
|
+
from sqlspec.exceptions import ImproperConfigurationError, StackExecutionError
|
|
28
|
+
from sqlspec.storage import AsyncStoragePipeline, StorageBridgeJob, StorageDestination, StorageFormat, StorageTelemetry
|
|
29
|
+
from sqlspec.utils.arrow_helpers import convert_dict_to_arrow_with_schema
|
|
30
|
+
from sqlspec.utils.logging import get_logger
|
|
31
|
+
from sqlspec.utils.schema import ValueT, to_value_type
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from collections.abc import Awaitable, Sequence
|
|
35
|
+
|
|
36
|
+
from sqlglot.dialects.dialect import DialectType
|
|
37
|
+
|
|
38
|
+
from sqlspec.builder import QueryBuilder
|
|
39
|
+
from sqlspec.core import ArrowResult, SQLResult, StatementConfig, StatementFilter
|
|
40
|
+
from sqlspec.typing import (
|
|
41
|
+
ArrowReturnFormat,
|
|
42
|
+
ArrowTable,
|
|
43
|
+
ColumnMetadata,
|
|
44
|
+
ForeignKeyMetadata,
|
|
45
|
+
IndexMetadata,
|
|
46
|
+
SchemaT,
|
|
47
|
+
StatementParameters,
|
|
48
|
+
TableMetadata,
|
|
49
|
+
VersionInfo,
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
__all__ = ("AsyncDataDictionaryBase", "AsyncDriverAdapterBase", "AsyncPoolConnectionContext", "AsyncPoolSessionFactory")
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
EMPTY_FILTERS: Final["list[StatementFilter]"] = []
|
|
57
|
+
_LOGGER_NAME: Final[str] = "sqlspec.driver"
|
|
58
|
+
logger = get_logger(_LOGGER_NAME)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@mypyc_attr(allow_interpreted_subclasses=True)
|
|
62
|
+
class AsyncPoolConnectionContext:
|
|
63
|
+
"""Base async connection context using pool acquire/release pattern.
|
|
64
|
+
|
|
65
|
+
Subclass per adapter and override ``__aenter__``/``__aexit__`` for
|
|
66
|
+
adapter-specific pool acquisition and release logic.
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
__slots__ = ("_config", "_connection")
|
|
70
|
+
|
|
71
|
+
def __init__(self, config: Any) -> None:
|
|
72
|
+
self._config = config
|
|
73
|
+
self._connection: Any = None
|
|
74
|
+
|
|
75
|
+
async def __aenter__(self) -> Any:
|
|
76
|
+
pool = self._config.connection_instance
|
|
77
|
+
if pool is None:
|
|
78
|
+
pool = await self._config.create_pool()
|
|
79
|
+
self._config.connection_instance = pool
|
|
80
|
+
self._connection = await pool.acquire()
|
|
81
|
+
return self._connection
|
|
82
|
+
|
|
83
|
+
async def __aexit__(
|
|
84
|
+
self, exc_type: "type[BaseException] | None", exc_val: "BaseException | None", exc_tb: Any
|
|
85
|
+
) -> "bool | None":
|
|
86
|
+
if self._connection is not None:
|
|
87
|
+
if self._config.connection_instance:
|
|
88
|
+
await self._config.connection_instance.release(self._connection)
|
|
89
|
+
self._connection = None
|
|
90
|
+
return None
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@mypyc_attr(allow_interpreted_subclasses=True)
|
|
94
|
+
class AsyncPoolSessionFactory:
|
|
95
|
+
"""Base async session factory using pool acquire/release pattern.
|
|
96
|
+
|
|
97
|
+
Subclass per adapter and override ``acquire_connection``/``release_connection``
|
|
98
|
+
for adapter-specific pool acquisition and release logic.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
__slots__ = ("_config", "_connection")
|
|
102
|
+
|
|
103
|
+
def __init__(self, config: Any) -> None:
|
|
104
|
+
self._config = config
|
|
105
|
+
self._connection: Any = None
|
|
106
|
+
|
|
107
|
+
async def acquire_connection(self) -> Any:
|
|
108
|
+
pool = self._config.connection_instance
|
|
109
|
+
if pool is None:
|
|
110
|
+
pool = await self._config.create_pool()
|
|
111
|
+
self._config.connection_instance = pool
|
|
112
|
+
self._connection = await pool.acquire()
|
|
113
|
+
return self._connection
|
|
114
|
+
|
|
115
|
+
async def release_connection(self, _conn: Any, **kwargs: Any) -> None:
|
|
116
|
+
if self._connection is not None and self._config.connection_instance is not None:
|
|
117
|
+
await self._config.connection_instance.release(self._connection)
|
|
118
|
+
self._connection = None
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
@mypyc_attr(allow_interpreted_subclasses=True)
|
|
122
|
+
class AsyncDriverAdapterBase(CommonDriverAttributesMixin):
|
|
123
|
+
"""Base class for asynchronous database drivers.
|
|
124
|
+
|
|
125
|
+
This class includes flattened storage and SQL translation methods that were
|
|
126
|
+
previously in StorageDriverMixin and SQLTranslatorMixin. The flattening
|
|
127
|
+
eliminates cross-trait attribute access that caused mypyc segmentation faults.
|
|
128
|
+
|
|
129
|
+
Method Organization:
|
|
130
|
+
1. Core dispatch methods (the execution engine)
|
|
131
|
+
2. Transaction management (abstract methods)
|
|
132
|
+
3. Public API - execution methods
|
|
133
|
+
4. Public API - query methods (select/fetch variants)
|
|
134
|
+
5. Arrow API methods
|
|
135
|
+
6. Stack execution
|
|
136
|
+
7. Storage API methods
|
|
137
|
+
8. Utility methods
|
|
138
|
+
9. Private/internal methods
|
|
139
|
+
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
__slots__ = ()
|
|
143
|
+
|
|
144
|
+
dialect: "DialectType | None" = None
|
|
145
|
+
|
|
146
|
+
@property
|
|
147
|
+
def is_async(self) -> bool:
|
|
148
|
+
"""Return whether the driver executes asynchronously.
|
|
149
|
+
|
|
150
|
+
Returns:
|
|
151
|
+
True for async drivers.
|
|
152
|
+
|
|
153
|
+
"""
|
|
154
|
+
return True
|
|
155
|
+
|
|
156
|
+
@property
|
|
157
|
+
@abstractmethod
|
|
158
|
+
def data_dictionary(self) -> "AsyncDataDictionaryBase":
|
|
159
|
+
"""Get the data dictionary for this driver.
|
|
160
|
+
|
|
161
|
+
Returns:
|
|
162
|
+
Data dictionary instance for metadata queries
|
|
163
|
+
|
|
164
|
+
"""
|
|
165
|
+
|
|
166
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
167
|
+
# CORE DISPATCH METHODS - The Execution Engine
|
|
168
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
169
|
+
|
|
170
|
+
@staticmethod
|
|
171
|
+
def _check_pending_exception(exc_handler: AsyncExceptionHandler) -> None:
|
|
172
|
+
"""Raise any pending mapped exception after context manager exit."""
|
|
173
|
+
if exc_handler.pending_exception is not None:
|
|
174
|
+
raise exc_handler.pending_exception from None
|
|
175
|
+
|
|
176
|
+
@final
|
|
177
|
+
async def dispatch_statement_execution(self, statement: "SQL", connection: "Any") -> "SQLResult":
|
|
178
|
+
"""Central execution dispatcher using the Template Method Pattern.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
statement: The SQL statement to execute
|
|
182
|
+
connection: The database connection to use
|
|
183
|
+
|
|
184
|
+
Returns:
|
|
185
|
+
The result of the SQL execution
|
|
186
|
+
|
|
187
|
+
"""
|
|
188
|
+
try:
|
|
189
|
+
runtime = self._observability
|
|
190
|
+
compiled_sql, execution_parameters = statement.compile()
|
|
191
|
+
_ = cast("ProcessedState", statement.get_processed_state())
|
|
192
|
+
result: SQLResult | None = None
|
|
193
|
+
|
|
194
|
+
# FAST PATH: Skip all instrumentation if runtime is absent or idle.
|
|
195
|
+
if runtime is None or runtime.is_idle:
|
|
196
|
+
exc_handler = self.handle_database_exceptions()
|
|
197
|
+
async with exc_handler, self.with_cursor(connection) as cursor:
|
|
198
|
+
special_result = await self.dispatch_special_handling(cursor, statement)
|
|
199
|
+
if special_result is not None:
|
|
200
|
+
result = special_result
|
|
201
|
+
elif statement.is_script:
|
|
202
|
+
execution_result = await self.dispatch_execute_script(cursor, statement)
|
|
203
|
+
result = self.build_statement_result(statement, execution_result)
|
|
204
|
+
elif statement.is_many:
|
|
205
|
+
execution_result = await self.dispatch_execute_many(cursor, statement)
|
|
206
|
+
result = self.build_statement_result(statement, execution_result)
|
|
207
|
+
else:
|
|
208
|
+
execution_result = await self.dispatch_execute(cursor, statement)
|
|
209
|
+
result = self.build_statement_result(statement, execution_result)
|
|
210
|
+
self._check_pending_exception(exc_handler)
|
|
211
|
+
assert result is not None
|
|
212
|
+
return result
|
|
213
|
+
|
|
214
|
+
operation = statement.operation_type
|
|
215
|
+
query_context = {
|
|
216
|
+
"sql": compiled_sql,
|
|
217
|
+
"parameters": execution_parameters,
|
|
218
|
+
"driver": type(self).__name__,
|
|
219
|
+
"operation": operation,
|
|
220
|
+
"is_many": statement.is_many,
|
|
221
|
+
"is_script": statement.is_script,
|
|
222
|
+
}
|
|
223
|
+
runtime.emit_query_start(**query_context)
|
|
224
|
+
span = runtime.start_query_span(compiled_sql, operation, type(self).__name__)
|
|
225
|
+
started = perf_counter()
|
|
226
|
+
|
|
227
|
+
exc_handler = self.handle_database_exceptions()
|
|
228
|
+
try:
|
|
229
|
+
async with exc_handler, self.with_cursor(connection) as cursor:
|
|
230
|
+
special_result = await self.dispatch_special_handling(cursor, statement)
|
|
231
|
+
if special_result is not None:
|
|
232
|
+
result = special_result
|
|
233
|
+
elif statement.is_script:
|
|
234
|
+
execution_result = await self.dispatch_execute_script(cursor, statement)
|
|
235
|
+
result = self.build_statement_result(statement, execution_result)
|
|
236
|
+
elif statement.is_many:
|
|
237
|
+
execution_result = await self.dispatch_execute_many(cursor, statement)
|
|
238
|
+
result = self.build_statement_result(statement, execution_result)
|
|
239
|
+
else:
|
|
240
|
+
execution_result = await self.dispatch_execute(cursor, statement)
|
|
241
|
+
result = self.build_statement_result(statement, execution_result)
|
|
242
|
+
except Exception as exc: # pragma: no cover - instrumentation path
|
|
243
|
+
pending_exception = exc_handler.pending_exception
|
|
244
|
+
if pending_exception is not None:
|
|
245
|
+
mapped_exc = pending_exception
|
|
246
|
+
runtime.span_manager.end_span(span, error=mapped_exc)
|
|
247
|
+
runtime.emit_error(mapped_exc, **query_context)
|
|
248
|
+
_raise_database_exception(exc_handler, exc)
|
|
249
|
+
runtime.span_manager.end_span(span, error=exc)
|
|
250
|
+
runtime.emit_error(exc, **query_context)
|
|
251
|
+
_raise_database_exception(exc_handler, exc)
|
|
252
|
+
|
|
253
|
+
pending_exception = exc_handler.pending_exception
|
|
254
|
+
if pending_exception is not None:
|
|
255
|
+
mapped_exc = pending_exception
|
|
256
|
+
runtime.span_manager.end_span(span, error=mapped_exc)
|
|
257
|
+
runtime.emit_error(mapped_exc, **query_context)
|
|
258
|
+
_raise_database_exception(exc_handler, None)
|
|
259
|
+
|
|
260
|
+
assert result is not None # Guaranteed: no exception means result was assigned
|
|
261
|
+
|
|
262
|
+
runtime.span_manager.end_span(span)
|
|
263
|
+
duration = perf_counter() - started
|
|
264
|
+
runtime.emit_query_complete(**{**query_context, "rows_affected": result.rows_affected})
|
|
265
|
+
runtime.emit_statement_event(
|
|
266
|
+
sql=compiled_sql,
|
|
267
|
+
parameters=execution_parameters,
|
|
268
|
+
driver=type(self).__name__,
|
|
269
|
+
operation=operation,
|
|
270
|
+
execution_mode=self.statement_config.execution_mode,
|
|
271
|
+
is_many=statement.is_many,
|
|
272
|
+
is_script=statement.is_script,
|
|
273
|
+
rows_affected=result.rows_affected,
|
|
274
|
+
duration_s=duration,
|
|
275
|
+
storage_backend=(result.metadata or {}).get("storage_backend"),
|
|
276
|
+
started_at=started,
|
|
277
|
+
)
|
|
278
|
+
return result
|
|
279
|
+
finally:
|
|
280
|
+
self._release_pooled_statement(statement)
|
|
281
|
+
|
|
282
|
+
@abstractmethod
|
|
283
|
+
async def dispatch_execute(self, cursor: Any, statement: "SQL") -> ExecutionResult:
|
|
284
|
+
"""Execute a single SQL statement.
|
|
285
|
+
|
|
286
|
+
Must be implemented by each driver for database-specific execution logic.
|
|
287
|
+
|
|
288
|
+
Args:
|
|
289
|
+
cursor: Database cursor/connection object
|
|
290
|
+
statement: SQL statement object with all necessary data and configuration
|
|
291
|
+
|
|
292
|
+
Returns:
|
|
293
|
+
ExecutionResult with execution data
|
|
294
|
+
|
|
295
|
+
"""
|
|
296
|
+
|
|
297
|
+
@abstractmethod
|
|
298
|
+
async def dispatch_execute_many(self, cursor: Any, statement: "SQL") -> ExecutionResult:
|
|
299
|
+
"""Execute SQL with multiple parameter sets (executemany).
|
|
300
|
+
|
|
301
|
+
Must be implemented by each driver for database-specific executemany logic.
|
|
302
|
+
|
|
303
|
+
Args:
|
|
304
|
+
cursor: Database cursor/connection object
|
|
305
|
+
statement: SQL statement object with all necessary data and configuration
|
|
306
|
+
|
|
307
|
+
Returns:
|
|
308
|
+
ExecutionResult with execution data for the many operation
|
|
309
|
+
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
async def dispatch_execute_script(self, cursor: Any, statement: "SQL") -> ExecutionResult:
|
|
313
|
+
"""Execute a SQL script containing multiple statements.
|
|
314
|
+
|
|
315
|
+
Default implementation splits the script and executes statements individually.
|
|
316
|
+
Drivers can override for database-specific script execution methods.
|
|
317
|
+
|
|
318
|
+
Args:
|
|
319
|
+
cursor: Database cursor/connection object
|
|
320
|
+
statement: SQL statement object with all necessary data and configuration
|
|
321
|
+
|
|
322
|
+
Returns:
|
|
323
|
+
ExecutionResult with script execution data including statement counts
|
|
324
|
+
|
|
325
|
+
"""
|
|
326
|
+
sql, prepared_parameters = self._get_compiled_sql(statement, self.statement_config)
|
|
327
|
+
statements = self.split_script_statements(sql, self.statement_config, strip_trailing_semicolon=True)
|
|
328
|
+
|
|
329
|
+
statement_count: int = len(statements)
|
|
330
|
+
successful_count: int = 0
|
|
331
|
+
|
|
332
|
+
for stmt in statements:
|
|
333
|
+
single_stmt = statement.copy(statement=stmt, parameters=prepared_parameters)
|
|
334
|
+
await self.dispatch_execute(cursor, single_stmt)
|
|
335
|
+
successful_count += 1
|
|
336
|
+
|
|
337
|
+
return self.create_execution_result(
|
|
338
|
+
cursor, statement_count=statement_count, successful_statements=successful_count, is_script_result=True
|
|
339
|
+
)
|
|
340
|
+
|
|
341
|
+
async def dispatch_special_handling(self, cursor: Any, statement: "SQL") -> "SQLResult | None":
|
|
342
|
+
"""Hook for database-specific special operations (e.g., PostgreSQL COPY, bulk operations).
|
|
343
|
+
|
|
344
|
+
This method is called first in dispatch_statement_execution() to allow drivers to handle
|
|
345
|
+
special operations that don't follow the standard SQL execution pattern.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
cursor: Database cursor/connection object
|
|
349
|
+
statement: SQL statement to analyze
|
|
350
|
+
|
|
351
|
+
Returns:
|
|
352
|
+
SQLResult if the special operation was handled and completed,
|
|
353
|
+
None if standard execution should proceed
|
|
354
|
+
|
|
355
|
+
"""
|
|
356
|
+
_ = (cursor, statement)
|
|
357
|
+
return None
|
|
358
|
+
|
|
359
|
+
def collect_rows(self, cursor: Any, fetched: "list[Any]") -> "tuple[list[Any], list[str], int]":
|
|
360
|
+
"""Collect rows from cursor after fetchall for the direct execution path.
|
|
361
|
+
|
|
362
|
+
Adapters should override this method to provide optimized row collection
|
|
363
|
+
that bypasses full dispatch_execute overhead.
|
|
364
|
+
|
|
365
|
+
Args:
|
|
366
|
+
cursor: Database cursor with description metadata.
|
|
367
|
+
fetched: Rows returned from cursor.fetchall().
|
|
368
|
+
|
|
369
|
+
Returns:
|
|
370
|
+
Tuple of (data, column_names, row_count).
|
|
371
|
+
|
|
372
|
+
Raises:
|
|
373
|
+
NotImplementedError: If the adapter does not implement this method.
|
|
374
|
+
|
|
375
|
+
"""
|
|
376
|
+
msg = "Adapter must implement collect_rows() for direct execution path"
|
|
377
|
+
raise NotImplementedError(msg)
|
|
378
|
+
|
|
379
|
+
def resolve_rowcount(self, cursor: Any) -> int:
|
|
380
|
+
"""Resolve the number of affected rows from cursor for the direct execution path.
|
|
381
|
+
|
|
382
|
+
Adapters should override this method to provide optimized rowcount resolution
|
|
383
|
+
that bypasses full dispatch_execute overhead.
|
|
384
|
+
|
|
385
|
+
Args:
|
|
386
|
+
cursor: Database cursor with rowcount metadata.
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
Number of affected rows, or 0 when unknown.
|
|
390
|
+
|
|
391
|
+
Raises:
|
|
392
|
+
NotImplementedError: If the adapter does not implement this method.
|
|
393
|
+
|
|
394
|
+
"""
|
|
395
|
+
msg = "Adapter must implement resolve_rowcount() for direct execution path"
|
|
396
|
+
raise NotImplementedError(msg)
|
|
397
|
+
|
|
398
|
+
async def _stmt_cache_execute_direct(
|
|
399
|
+
self, sql: str, params: "tuple[Any, ...] | list[Any]", cached: CachedQuery
|
|
400
|
+
) -> "SQLResult":
|
|
401
|
+
"""Execute pre-compiled query via ultra-fast path (async).
|
|
402
|
+
|
|
403
|
+
Uses _stmt_cache_build_direct + dispatch_execute since async drivers can't
|
|
404
|
+
call cursor.execute directly. For DML operations, returns DMLResult
|
|
405
|
+
to bypass full SQLResult construction.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
sql: Raw SQL string (original, not compiled).
|
|
409
|
+
params: Query parameters.
|
|
410
|
+
cached: Cached query metadata.
|
|
411
|
+
|
|
412
|
+
Returns:
|
|
413
|
+
SQLResult or DMLResult.
|
|
414
|
+
"""
|
|
415
|
+
compiled_sql = cached.compiled_sql
|
|
416
|
+
direct_statement = self._stmt_cache_build_direct(
|
|
417
|
+
sql, params, cached, params, params_are_simple=True, compiled_sql=compiled_sql
|
|
418
|
+
)
|
|
419
|
+
|
|
420
|
+
exc_handler = self.handle_database_exceptions()
|
|
421
|
+
result: SQLResult | None = None
|
|
422
|
+
try:
|
|
423
|
+
async with exc_handler, self.with_cursor(self.connection) as cursor:
|
|
424
|
+
execution_result = await self.dispatch_execute(cursor, direct_statement)
|
|
425
|
+
|
|
426
|
+
if cached.operation_profile.returns_rows:
|
|
427
|
+
result = self.build_statement_result(direct_statement, execution_result)
|
|
428
|
+
else:
|
|
429
|
+
# DML path: use DMLResult to bypass full SQLResult construction
|
|
430
|
+
affected_rows = (
|
|
431
|
+
execution_result.rowcount_override
|
|
432
|
+
if execution_result.rowcount_override is not None and execution_result.rowcount_override >= 0
|
|
433
|
+
else 0
|
|
434
|
+
)
|
|
435
|
+
result = DMLResult(cached.operation_type, affected_rows)
|
|
436
|
+
|
|
437
|
+
self._check_pending_exception(exc_handler)
|
|
438
|
+
assert result is not None
|
|
439
|
+
return result
|
|
440
|
+
finally:
|
|
441
|
+
self._release_pooled_statement(direct_statement)
|
|
442
|
+
|
|
443
|
+
async def _stmt_cache_lookup(self, statement: str, params: "tuple[Any, ...] | list[Any]") -> "SQLResult | None":
|
|
444
|
+
"""Attempt fast-path execution for cached query (async).
|
|
445
|
+
|
|
446
|
+
Args:
|
|
447
|
+
statement: Raw SQL string.
|
|
448
|
+
params: Query parameters.
|
|
449
|
+
|
|
450
|
+
Returns:
|
|
451
|
+
SQLResult if cache hit and execution succeeds, None otherwise.
|
|
452
|
+
"""
|
|
453
|
+
result = super()._stmt_cache_lookup(statement, params)
|
|
454
|
+
if result is None:
|
|
455
|
+
return None
|
|
456
|
+
return await cast("Awaitable[SQLResult | None]", result)
|
|
457
|
+
|
|
458
|
+
async def _stmt_cache_execute(self, statement: "SQL") -> "SQLResult":
|
|
459
|
+
"""Execute pre-compiled query via fast path (async).
|
|
460
|
+
|
|
461
|
+
The statement is already compiled by _stmt_cache_prepare_direct, so dispatch_execute
|
|
462
|
+
will hit the fast path in _get_compiled_statement (is_processed check).
|
|
463
|
+
"""
|
|
464
|
+
exc_handler = self.handle_database_exceptions()
|
|
465
|
+
result: SQLResult | None = None
|
|
466
|
+
try:
|
|
467
|
+
async with exc_handler, self.with_cursor(self.connection) as cursor:
|
|
468
|
+
execution_result = await self.dispatch_execute(cursor, statement)
|
|
469
|
+
result = self.build_statement_result(statement, execution_result)
|
|
470
|
+
|
|
471
|
+
self._check_pending_exception(exc_handler)
|
|
472
|
+
assert result is not None
|
|
473
|
+
return result
|
|
474
|
+
finally:
|
|
475
|
+
self._release_pooled_statement(statement)
|
|
476
|
+
|
|
477
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
478
|
+
# TRANSACTION MANAGEMENT - Required Abstract Methods
|
|
479
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
480
|
+
|
|
481
|
+
@abstractmethod
|
|
482
|
+
async def begin(self) -> None:
|
|
483
|
+
"""Begin a database transaction on the current connection."""
|
|
484
|
+
|
|
485
|
+
@abstractmethod
|
|
486
|
+
async def commit(self) -> None:
|
|
487
|
+
"""Commit the current transaction on the current connection."""
|
|
488
|
+
|
|
489
|
+
@abstractmethod
|
|
490
|
+
async def rollback(self) -> None:
|
|
491
|
+
"""Rollback the current transaction on the current connection."""
|
|
492
|
+
|
|
493
|
+
@abstractmethod
|
|
494
|
+
def with_cursor(self, connection: Any) -> Any:
|
|
495
|
+
"""Create and return an async context manager for cursor acquisition and cleanup.
|
|
496
|
+
|
|
497
|
+
Returns an async context manager that yields a cursor for database operations.
|
|
498
|
+
Concrete implementations handle database-specific cursor creation and cleanup.
|
|
499
|
+
"""
|
|
500
|
+
|
|
501
|
+
@abstractmethod
|
|
502
|
+
def handle_database_exceptions(self) -> "AsyncExceptionHandler":
|
|
503
|
+
"""Handle database-specific exceptions and wrap them appropriately.
|
|
504
|
+
|
|
505
|
+
Returns:
|
|
506
|
+
Exception handler with deferred exception pattern for mypyc compatibility.
|
|
507
|
+
The handler stores mapped exceptions in pending_exception rather than
|
|
508
|
+
raising from __aexit__ to avoid ABI boundary violations.
|
|
509
|
+
|
|
510
|
+
"""
|
|
511
|
+
|
|
512
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
513
|
+
# PUBLIC API - Core Execution Methods
|
|
514
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
515
|
+
|
|
516
|
+
async def execute(
|
|
517
|
+
self,
|
|
518
|
+
statement: "SQL | Statement | QueryBuilder",
|
|
519
|
+
/,
|
|
520
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
521
|
+
statement_config: "StatementConfig | None" = None,
|
|
522
|
+
**kwargs: Any,
|
|
523
|
+
) -> "SQLResult":
|
|
524
|
+
"""Execute a statement with parameter handling."""
|
|
525
|
+
exc_handler = self.handle_database_exceptions()
|
|
526
|
+
result: SQLResult | None = None
|
|
527
|
+
async with exc_handler:
|
|
528
|
+
if (
|
|
529
|
+
self._stmt_cache_enabled
|
|
530
|
+
and (statement_config is None or statement_config is self.statement_config)
|
|
531
|
+
and isinstance(statement, str)
|
|
532
|
+
and len(parameters) == 1
|
|
533
|
+
and isinstance(parameters[0], (tuple, list))
|
|
534
|
+
and not kwargs
|
|
535
|
+
):
|
|
536
|
+
fast_result = await self._stmt_cache_lookup(statement, parameters[0])
|
|
537
|
+
if fast_result is not None:
|
|
538
|
+
result = fast_result
|
|
539
|
+
if result is None:
|
|
540
|
+
sql_statement = self.prepare_statement(
|
|
541
|
+
statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs
|
|
542
|
+
)
|
|
543
|
+
result = await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection)
|
|
544
|
+
self._check_pending_exception(exc_handler)
|
|
545
|
+
assert result is not None
|
|
546
|
+
return result
|
|
547
|
+
|
|
548
|
+
async def execute_many(
|
|
549
|
+
self,
|
|
550
|
+
statement: "SQL | Statement | QueryBuilder",
|
|
551
|
+
/,
|
|
552
|
+
parameters: "Sequence[StatementParameters]",
|
|
553
|
+
*filters: "StatementParameters | StatementFilter",
|
|
554
|
+
statement_config: "StatementConfig | None" = None,
|
|
555
|
+
**kwargs: Any,
|
|
556
|
+
) -> "SQLResult":
|
|
557
|
+
"""Execute statement multiple times with different parameters.
|
|
558
|
+
|
|
559
|
+
Parameters passed will be used as the batch execution sequence.
|
|
560
|
+
"""
|
|
561
|
+
exc_handler = self.handle_database_exceptions()
|
|
562
|
+
result: SQLResult | None = None
|
|
563
|
+
async with exc_handler:
|
|
564
|
+
config = statement_config or self.statement_config
|
|
565
|
+
|
|
566
|
+
if isinstance(statement, str) and not filters and not kwargs:
|
|
567
|
+
sql_statement = SQL(statement, parameters, statement_config=config, is_many=True)
|
|
568
|
+
elif isinstance(statement, SQL):
|
|
569
|
+
statement_seed = statement.raw_expression or statement.raw_sql
|
|
570
|
+
sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs)
|
|
571
|
+
else:
|
|
572
|
+
base_statement = self.prepare_statement(statement, filters, statement_config=config, kwargs=kwargs)
|
|
573
|
+
statement_seed = base_statement.raw_expression or base_statement.raw_sql
|
|
574
|
+
sql_statement = SQL(statement_seed, parameters, statement_config=config, is_many=True, **kwargs)
|
|
575
|
+
|
|
576
|
+
result = await self.dispatch_statement_execution(statement=sql_statement, connection=self.connection)
|
|
577
|
+
self._check_pending_exception(exc_handler)
|
|
578
|
+
assert result is not None
|
|
579
|
+
return result
|
|
580
|
+
|
|
581
|
+
async def execute_script(
|
|
582
|
+
self,
|
|
583
|
+
statement: "str | SQL",
|
|
584
|
+
/,
|
|
585
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
586
|
+
statement_config: "StatementConfig | None" = None,
|
|
587
|
+
**kwargs: Any,
|
|
588
|
+
) -> "SQLResult":
|
|
589
|
+
"""Execute a multi-statement script.
|
|
590
|
+
|
|
591
|
+
By default, validates each statement and logs warnings for dangerous
|
|
592
|
+
operations. Use suppress_warnings=True for migrations and admin scripts.
|
|
593
|
+
"""
|
|
594
|
+
exc_handler = self.handle_database_exceptions()
|
|
595
|
+
result: SQLResult | None = None
|
|
596
|
+
async with exc_handler:
|
|
597
|
+
config = statement_config or self.statement_config
|
|
598
|
+
sql_statement = self.prepare_statement(statement, parameters, statement_config=config, kwargs=kwargs)
|
|
599
|
+
result = await self.dispatch_statement_execution(
|
|
600
|
+
statement=sql_statement.as_script(), connection=self.connection
|
|
601
|
+
)
|
|
602
|
+
self._check_pending_exception(exc_handler)
|
|
603
|
+
assert result is not None
|
|
604
|
+
return result
|
|
605
|
+
|
|
606
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
607
|
+
# PUBLIC API - Query Methods (select/fetch variants)
|
|
608
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
609
|
+
|
|
610
|
+
@overload
|
|
611
|
+
async def select(
|
|
612
|
+
self,
|
|
613
|
+
statement: "Statement | QueryBuilder",
|
|
614
|
+
/,
|
|
615
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
616
|
+
schema_type: "type[SchemaT]",
|
|
617
|
+
statement_config: "StatementConfig | None" = None,
|
|
618
|
+
**kwargs: Any,
|
|
619
|
+
) -> "list[SchemaT]": ...
|
|
620
|
+
|
|
621
|
+
@overload
|
|
622
|
+
async def select(
|
|
623
|
+
self,
|
|
624
|
+
statement: "Statement | QueryBuilder",
|
|
625
|
+
/,
|
|
626
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
627
|
+
schema_type: None = None,
|
|
628
|
+
statement_config: "StatementConfig | None" = None,
|
|
629
|
+
**kwargs: Any,
|
|
630
|
+
) -> "list[dict[str, Any]]": ...
|
|
631
|
+
|
|
632
|
+
async def select(
|
|
633
|
+
self,
|
|
634
|
+
statement: "Statement | QueryBuilder",
|
|
635
|
+
/,
|
|
636
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
637
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
638
|
+
statement_config: "StatementConfig | None" = None,
|
|
639
|
+
**kwargs: Any,
|
|
640
|
+
) -> "list[SchemaT] | list[dict[str, Any]]":
|
|
641
|
+
"""Execute a select statement and return all rows."""
|
|
642
|
+
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
643
|
+
return result.get_data(schema_type=schema_type)
|
|
644
|
+
|
|
645
|
+
@overload
|
|
646
|
+
async def fetch(
|
|
647
|
+
self,
|
|
648
|
+
statement: "Statement | QueryBuilder",
|
|
649
|
+
/,
|
|
650
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
651
|
+
schema_type: "type[SchemaT]",
|
|
652
|
+
statement_config: "StatementConfig | None" = None,
|
|
653
|
+
**kwargs: Any,
|
|
654
|
+
) -> "list[SchemaT]": ...
|
|
655
|
+
|
|
656
|
+
@overload
|
|
657
|
+
async def fetch(
|
|
658
|
+
self,
|
|
659
|
+
statement: "Statement | QueryBuilder",
|
|
660
|
+
/,
|
|
661
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
662
|
+
schema_type: None = None,
|
|
663
|
+
statement_config: "StatementConfig | None" = None,
|
|
664
|
+
**kwargs: Any,
|
|
665
|
+
) -> "list[dict[str, Any]]": ...
|
|
666
|
+
|
|
667
|
+
async def fetch(
|
|
668
|
+
self,
|
|
669
|
+
statement: "Statement | QueryBuilder",
|
|
670
|
+
/,
|
|
671
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
672
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
673
|
+
statement_config: "StatementConfig | None" = None,
|
|
674
|
+
**kwargs: Any,
|
|
675
|
+
) -> "list[SchemaT] | list[dict[str, Any]]":
|
|
676
|
+
"""Execute a select statement and return all rows.
|
|
677
|
+
|
|
678
|
+
This is an alias for :meth:`select` provided for users familiar
|
|
679
|
+
with asyncpg's fetch() naming convention.
|
|
680
|
+
|
|
681
|
+
See Also:
|
|
682
|
+
select(): Primary method with identical behavior
|
|
683
|
+
|
|
684
|
+
"""
|
|
685
|
+
return await self.select(
|
|
686
|
+
statement, *parameters, schema_type=schema_type, statement_config=statement_config, **kwargs
|
|
687
|
+
)
|
|
688
|
+
|
|
689
|
+
@overload
|
|
690
|
+
async def select_one(
|
|
691
|
+
self,
|
|
692
|
+
statement: "Statement | QueryBuilder",
|
|
693
|
+
/,
|
|
694
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
695
|
+
schema_type: "type[SchemaT]",
|
|
696
|
+
statement_config: "StatementConfig | None" = None,
|
|
697
|
+
**kwargs: Any,
|
|
698
|
+
) -> "SchemaT": ...
|
|
699
|
+
|
|
700
|
+
@overload
|
|
701
|
+
async def select_one(
|
|
702
|
+
self,
|
|
703
|
+
statement: "Statement | QueryBuilder",
|
|
704
|
+
/,
|
|
705
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
706
|
+
schema_type: None = None,
|
|
707
|
+
statement_config: "StatementConfig | None" = None,
|
|
708
|
+
**kwargs: Any,
|
|
709
|
+
) -> "dict[str, Any]": ...
|
|
710
|
+
|
|
711
|
+
async def select_one(
|
|
712
|
+
self,
|
|
713
|
+
statement: "Statement | QueryBuilder",
|
|
714
|
+
/,
|
|
715
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
716
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
717
|
+
statement_config: "StatementConfig | None" = None,
|
|
718
|
+
**kwargs: Any,
|
|
719
|
+
) -> "SchemaT | dict[str, Any]":
|
|
720
|
+
"""Execute a select statement and return exactly one row.
|
|
721
|
+
|
|
722
|
+
Raises an exception if no rows or more than one row is returned.
|
|
723
|
+
"""
|
|
724
|
+
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
725
|
+
try:
|
|
726
|
+
return result.one(schema_type=schema_type)
|
|
727
|
+
except ValueError as error:
|
|
728
|
+
handle_single_row_error(error)
|
|
729
|
+
|
|
730
|
+
@overload
|
|
731
|
+
async def fetch_one(
|
|
732
|
+
self,
|
|
733
|
+
statement: "Statement | QueryBuilder",
|
|
734
|
+
/,
|
|
735
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
736
|
+
schema_type: "type[SchemaT]",
|
|
737
|
+
statement_config: "StatementConfig | None" = None,
|
|
738
|
+
**kwargs: Any,
|
|
739
|
+
) -> "SchemaT": ...
|
|
740
|
+
|
|
741
|
+
@overload
|
|
742
|
+
async def fetch_one(
|
|
743
|
+
self,
|
|
744
|
+
statement: "Statement | QueryBuilder",
|
|
745
|
+
/,
|
|
746
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
747
|
+
schema_type: None = None,
|
|
748
|
+
statement_config: "StatementConfig | None" = None,
|
|
749
|
+
**kwargs: Any,
|
|
750
|
+
) -> "dict[str, Any]": ...
|
|
751
|
+
|
|
752
|
+
async def fetch_one(
|
|
753
|
+
self,
|
|
754
|
+
statement: "Statement | QueryBuilder",
|
|
755
|
+
/,
|
|
756
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
757
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
758
|
+
statement_config: "StatementConfig | None" = None,
|
|
759
|
+
**kwargs: Any,
|
|
760
|
+
) -> "SchemaT | dict[str, Any]":
|
|
761
|
+
"""Execute a select statement and return exactly one row.
|
|
762
|
+
|
|
763
|
+
This is an alias for :meth:`select_one` provided for users familiar
|
|
764
|
+
with asyncpg's fetch_one() naming convention.
|
|
765
|
+
|
|
766
|
+
Raises an exception if no rows or more than one row is returned.
|
|
767
|
+
|
|
768
|
+
See Also:
|
|
769
|
+
select_one(): Primary method with identical behavior
|
|
770
|
+
|
|
771
|
+
"""
|
|
772
|
+
return await self.select_one(
|
|
773
|
+
statement, *parameters, schema_type=schema_type, statement_config=statement_config, **kwargs
|
|
774
|
+
)
|
|
775
|
+
|
|
776
|
+
@overload
|
|
777
|
+
async def select_one_or_none(
|
|
778
|
+
self,
|
|
779
|
+
statement: "Statement | QueryBuilder",
|
|
780
|
+
/,
|
|
781
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
782
|
+
schema_type: "type[SchemaT]",
|
|
783
|
+
statement_config: "StatementConfig | None" = None,
|
|
784
|
+
**kwargs: Any,
|
|
785
|
+
) -> "SchemaT | None": ...
|
|
786
|
+
|
|
787
|
+
@overload
|
|
788
|
+
async def select_one_or_none(
|
|
789
|
+
self,
|
|
790
|
+
statement: "Statement | QueryBuilder",
|
|
791
|
+
/,
|
|
792
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
793
|
+
schema_type: None = None,
|
|
794
|
+
statement_config: "StatementConfig | None" = None,
|
|
795
|
+
**kwargs: Any,
|
|
796
|
+
) -> "dict[str, Any] | None": ...
|
|
797
|
+
|
|
798
|
+
async def select_one_or_none(
|
|
799
|
+
self,
|
|
800
|
+
statement: "Statement | QueryBuilder",
|
|
801
|
+
/,
|
|
802
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
803
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
804
|
+
statement_config: "StatementConfig | None" = None,
|
|
805
|
+
**kwargs: Any,
|
|
806
|
+
) -> "SchemaT | dict[str, Any] | None":
|
|
807
|
+
"""Execute a select statement and return at most one row.
|
|
808
|
+
|
|
809
|
+
Returns None if no rows are found. Raises ``ValueError`` if more than one
|
|
810
|
+
row is returned. Any database or SQL execution errors raised by the driver
|
|
811
|
+
are propagated unchanged.
|
|
812
|
+
"""
|
|
813
|
+
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
814
|
+
return result.one_or_none(schema_type=schema_type)
|
|
815
|
+
|
|
816
|
+
@overload
|
|
817
|
+
async def fetch_one_or_none(
|
|
818
|
+
self,
|
|
819
|
+
statement: "Statement | QueryBuilder",
|
|
820
|
+
/,
|
|
821
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
822
|
+
schema_type: "type[SchemaT]",
|
|
823
|
+
statement_config: "StatementConfig | None" = None,
|
|
824
|
+
**kwargs: Any,
|
|
825
|
+
) -> "SchemaT | None": ...
|
|
826
|
+
|
|
827
|
+
@overload
|
|
828
|
+
async def fetch_one_or_none(
|
|
829
|
+
self,
|
|
830
|
+
statement: "Statement | QueryBuilder",
|
|
831
|
+
/,
|
|
832
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
833
|
+
schema_type: None = None,
|
|
834
|
+
statement_config: "StatementConfig | None" = None,
|
|
835
|
+
**kwargs: Any,
|
|
836
|
+
) -> "dict[str, Any] | None": ...
|
|
837
|
+
|
|
838
|
+
async def fetch_one_or_none(
|
|
839
|
+
self,
|
|
840
|
+
statement: "Statement | QueryBuilder",
|
|
841
|
+
/,
|
|
842
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
843
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
844
|
+
statement_config: "StatementConfig | None" = None,
|
|
845
|
+
**kwargs: Any,
|
|
846
|
+
) -> "SchemaT | dict[str, Any] | None":
|
|
847
|
+
"""Execute a select statement and return at most one row.
|
|
848
|
+
|
|
849
|
+
This is an alias for :meth:`select_one_or_none` provided for users familiar
|
|
850
|
+
with asyncpg's fetch_one_or_none() naming convention.
|
|
851
|
+
|
|
852
|
+
Returns None if no rows are found.
|
|
853
|
+
Raises an exception if more than one row is returned.
|
|
854
|
+
|
|
855
|
+
See Also:
|
|
856
|
+
select_one_or_none(): Primary method with identical behavior
|
|
857
|
+
|
|
858
|
+
"""
|
|
859
|
+
return await self.select_one_or_none(
|
|
860
|
+
statement, *parameters, schema_type=schema_type, statement_config=statement_config, **kwargs
|
|
861
|
+
)
|
|
862
|
+
|
|
863
|
+
@overload
|
|
864
|
+
async def select_value(
|
|
865
|
+
self,
|
|
866
|
+
statement: "Statement | QueryBuilder",
|
|
867
|
+
/,
|
|
868
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
869
|
+
value_type: "type[ValueT]",
|
|
870
|
+
statement_config: "StatementConfig | None" = None,
|
|
871
|
+
**kwargs: Any,
|
|
872
|
+
) -> "ValueT": ...
|
|
873
|
+
|
|
874
|
+
@overload
|
|
875
|
+
async def select_value(
|
|
876
|
+
self,
|
|
877
|
+
statement: "Statement | QueryBuilder",
|
|
878
|
+
/,
|
|
879
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
880
|
+
value_type: None = None,
|
|
881
|
+
statement_config: "StatementConfig | None" = None,
|
|
882
|
+
**kwargs: Any,
|
|
883
|
+
) -> Any: ...
|
|
884
|
+
|
|
885
|
+
async def select_value(
|
|
886
|
+
self,
|
|
887
|
+
statement: "Statement | QueryBuilder",
|
|
888
|
+
/,
|
|
889
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
890
|
+
value_type: "type[ValueT] | None" = None,
|
|
891
|
+
statement_config: "StatementConfig | None" = None,
|
|
892
|
+
**kwargs: Any,
|
|
893
|
+
) -> "ValueT | Any":
|
|
894
|
+
"""Execute a select statement and return a single scalar value.
|
|
895
|
+
|
|
896
|
+
Expects exactly one row with one column.
|
|
897
|
+
Raises an exception if no rows or more than one row/column is returned.
|
|
898
|
+
|
|
899
|
+
Args:
|
|
900
|
+
statement: SQL statement or query builder to execute.
|
|
901
|
+
*parameters: Positional parameters for the statement.
|
|
902
|
+
value_type: Optional type to convert the result to. When provided,
|
|
903
|
+
the return value is converted to this type and the return type
|
|
904
|
+
is narrowed for type checkers. Supports int, float, str, bool,
|
|
905
|
+
datetime, date, time, Decimal, UUID, Path, dict, and list.
|
|
906
|
+
statement_config: Optional statement configuration.
|
|
907
|
+
**kwargs: Additional keyword arguments.
|
|
908
|
+
|
|
909
|
+
Returns:
|
|
910
|
+
The scalar value, optionally converted to the specified type
|
|
911
|
+
|
|
912
|
+
Examples:
|
|
913
|
+
Basic usage (returns Any):
|
|
914
|
+
|
|
915
|
+
>>> count = await driver.select_value(
|
|
916
|
+
... "SELECT COUNT(*) FROM users"
|
|
917
|
+
... )
|
|
918
|
+
|
|
919
|
+
With type hint (returns int):
|
|
920
|
+
|
|
921
|
+
>>> count = await driver.select_value(
|
|
922
|
+
... "SELECT COUNT(*) FROM users", value_type=int
|
|
923
|
+
... )
|
|
924
|
+
|
|
925
|
+
With UUID conversion:
|
|
926
|
+
|
|
927
|
+
>>> user_id = await driver.select_value(
|
|
928
|
+
... "SELECT id FROM users WHERE name = :name",
|
|
929
|
+
... {"name": "alice"},
|
|
930
|
+
... value_type=UUID,
|
|
931
|
+
... )
|
|
932
|
+
"""
|
|
933
|
+
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
934
|
+
try:
|
|
935
|
+
value = result.scalar()
|
|
936
|
+
except ValueError as error:
|
|
937
|
+
handle_single_row_error(error)
|
|
938
|
+
|
|
939
|
+
if value_type is not None:
|
|
940
|
+
return to_value_type(value, value_type)
|
|
941
|
+
return value
|
|
942
|
+
|
|
943
|
+
@overload
|
|
944
|
+
async def fetch_value(
|
|
945
|
+
self,
|
|
946
|
+
statement: "Statement | QueryBuilder",
|
|
947
|
+
/,
|
|
948
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
949
|
+
value_type: "type[ValueT]",
|
|
950
|
+
statement_config: "StatementConfig | None" = None,
|
|
951
|
+
**kwargs: Any,
|
|
952
|
+
) -> "ValueT": ...
|
|
953
|
+
|
|
954
|
+
@overload
|
|
955
|
+
async def fetch_value(
|
|
956
|
+
self,
|
|
957
|
+
statement: "Statement | QueryBuilder",
|
|
958
|
+
/,
|
|
959
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
960
|
+
value_type: None = None,
|
|
961
|
+
statement_config: "StatementConfig | None" = None,
|
|
962
|
+
**kwargs: Any,
|
|
963
|
+
) -> Any: ...
|
|
964
|
+
|
|
965
|
+
async def fetch_value(
|
|
966
|
+
self,
|
|
967
|
+
statement: "Statement | QueryBuilder",
|
|
968
|
+
/,
|
|
969
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
970
|
+
value_type: "type[ValueT] | None" = None,
|
|
971
|
+
statement_config: "StatementConfig | None" = None,
|
|
972
|
+
**kwargs: Any,
|
|
973
|
+
) -> "ValueT | Any":
|
|
974
|
+
"""Execute a select statement and return a single scalar value.
|
|
975
|
+
|
|
976
|
+
This is an alias for :meth:`select_value` provided for users familiar
|
|
977
|
+
with asyncpg's fetch_value() naming convention.
|
|
978
|
+
|
|
979
|
+
Expects exactly one row with one column.
|
|
980
|
+
Raises an exception if no rows or more than one row/column is returned.
|
|
981
|
+
|
|
982
|
+
See Also:
|
|
983
|
+
select_value(): Primary method with identical behavior
|
|
984
|
+
|
|
985
|
+
"""
|
|
986
|
+
return await self.select_value(
|
|
987
|
+
statement, *parameters, value_type=value_type, statement_config=statement_config, **kwargs
|
|
988
|
+
)
|
|
989
|
+
|
|
990
|
+
@overload
|
|
991
|
+
async def select_value_or_none(
|
|
992
|
+
self,
|
|
993
|
+
statement: "Statement | QueryBuilder",
|
|
994
|
+
/,
|
|
995
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
996
|
+
value_type: "type[ValueT]",
|
|
997
|
+
statement_config: "StatementConfig | None" = None,
|
|
998
|
+
**kwargs: Any,
|
|
999
|
+
) -> "ValueT | None": ...
|
|
1000
|
+
|
|
1001
|
+
@overload
|
|
1002
|
+
async def select_value_or_none(
|
|
1003
|
+
self,
|
|
1004
|
+
statement: "Statement | QueryBuilder",
|
|
1005
|
+
/,
|
|
1006
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1007
|
+
value_type: None = None,
|
|
1008
|
+
statement_config: "StatementConfig | None" = None,
|
|
1009
|
+
**kwargs: Any,
|
|
1010
|
+
) -> Any: ...
|
|
1011
|
+
|
|
1012
|
+
async def select_value_or_none(
|
|
1013
|
+
self,
|
|
1014
|
+
statement: "Statement | QueryBuilder",
|
|
1015
|
+
/,
|
|
1016
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1017
|
+
value_type: "type[ValueT] | None" = None,
|
|
1018
|
+
statement_config: "StatementConfig | None" = None,
|
|
1019
|
+
**kwargs: Any,
|
|
1020
|
+
) -> "ValueT | None | Any":
|
|
1021
|
+
"""Execute a select statement and return a single scalar value or None.
|
|
1022
|
+
|
|
1023
|
+
Returns None if no rows are found.
|
|
1024
|
+
Expects at most one row with one column.
|
|
1025
|
+
Raises an exception if more than one row is returned.
|
|
1026
|
+
|
|
1027
|
+
Args:
|
|
1028
|
+
statement: SQL statement or query builder to execute.
|
|
1029
|
+
*parameters: Positional parameters for the statement.
|
|
1030
|
+
value_type: Optional type to convert the result to. When provided,
|
|
1031
|
+
the return value is converted to this type and the return type
|
|
1032
|
+
is narrowed to ``T | None`` for type checkers. Supports int, float,
|
|
1033
|
+
str, bool, datetime, date, time, Decimal, UUID, Path, dict, and list.
|
|
1034
|
+
statement_config: Optional statement configuration.
|
|
1035
|
+
**kwargs: Additional keyword arguments.
|
|
1036
|
+
|
|
1037
|
+
Returns:
|
|
1038
|
+
The scalar value (optionally converted), or None if no rows found.
|
|
1039
|
+
|
|
1040
|
+
Examples:
|
|
1041
|
+
Basic usage:
|
|
1042
|
+
|
|
1043
|
+
>>> email = await driver.select_value_or_none(
|
|
1044
|
+
... "SELECT email FROM users WHERE id = :id", {"id": 123}
|
|
1045
|
+
... )
|
|
1046
|
+
>>> if email is not None:
|
|
1047
|
+
... print(email)
|
|
1048
|
+
|
|
1049
|
+
With type hint:
|
|
1050
|
+
|
|
1051
|
+
>>> count = await driver.select_value_or_none(
|
|
1052
|
+
... "SELECT COUNT(*) FROM users WHERE active = true",
|
|
1053
|
+
... value_type=int,
|
|
1054
|
+
... )
|
|
1055
|
+
>>> if count is not None and count > 0:
|
|
1056
|
+
... print(f"Found {count} active users")
|
|
1057
|
+
"""
|
|
1058
|
+
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
1059
|
+
value = result.scalar_or_none()
|
|
1060
|
+
|
|
1061
|
+
if value is None:
|
|
1062
|
+
return None
|
|
1063
|
+
if value_type is not None:
|
|
1064
|
+
return to_value_type(value, value_type)
|
|
1065
|
+
return value
|
|
1066
|
+
|
|
1067
|
+
@overload
|
|
1068
|
+
async def fetch_value_or_none(
|
|
1069
|
+
self,
|
|
1070
|
+
statement: "Statement | QueryBuilder",
|
|
1071
|
+
/,
|
|
1072
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1073
|
+
value_type: "type[ValueT]",
|
|
1074
|
+
statement_config: "StatementConfig | None" = None,
|
|
1075
|
+
**kwargs: Any,
|
|
1076
|
+
) -> "ValueT | None": ...
|
|
1077
|
+
|
|
1078
|
+
@overload
|
|
1079
|
+
async def fetch_value_or_none(
|
|
1080
|
+
self,
|
|
1081
|
+
statement: "Statement | QueryBuilder",
|
|
1082
|
+
/,
|
|
1083
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1084
|
+
value_type: None = None,
|
|
1085
|
+
statement_config: "StatementConfig | None" = None,
|
|
1086
|
+
**kwargs: Any,
|
|
1087
|
+
) -> Any: ...
|
|
1088
|
+
|
|
1089
|
+
async def fetch_value_or_none(
|
|
1090
|
+
self,
|
|
1091
|
+
statement: "Statement | QueryBuilder",
|
|
1092
|
+
/,
|
|
1093
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1094
|
+
value_type: "type[ValueT] | None" = None,
|
|
1095
|
+
statement_config: "StatementConfig | None" = None,
|
|
1096
|
+
**kwargs: Any,
|
|
1097
|
+
) -> "ValueT | None | Any":
|
|
1098
|
+
"""Execute a select statement and return a single scalar value or None.
|
|
1099
|
+
|
|
1100
|
+
This is an alias for :meth:`select_value_or_none` provided for users familiar
|
|
1101
|
+
with asyncpg's fetch_value_or_none() naming convention.
|
|
1102
|
+
|
|
1103
|
+
Returns None if no rows are found.
|
|
1104
|
+
Expects at most one row with one column.
|
|
1105
|
+
Raises an exception if more than one row is returned.
|
|
1106
|
+
|
|
1107
|
+
See Also:
|
|
1108
|
+
select_value_or_none(): Primary method with identical behavior
|
|
1109
|
+
|
|
1110
|
+
"""
|
|
1111
|
+
return await self.select_value_or_none(
|
|
1112
|
+
statement, *parameters, value_type=value_type, statement_config=statement_config, **kwargs
|
|
1113
|
+
)
|
|
1114
|
+
|
|
1115
|
+
@overload
|
|
1116
|
+
async def select_with_total(
|
|
1117
|
+
self,
|
|
1118
|
+
statement: "Statement | QueryBuilder",
|
|
1119
|
+
/,
|
|
1120
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1121
|
+
schema_type: "type[SchemaT]",
|
|
1122
|
+
statement_config: "StatementConfig | None" = None,
|
|
1123
|
+
count_with_window: bool = False,
|
|
1124
|
+
**kwargs: Any,
|
|
1125
|
+
) -> "tuple[list[SchemaT], int]": ...
|
|
1126
|
+
|
|
1127
|
+
@overload
|
|
1128
|
+
async def select_with_total(
|
|
1129
|
+
self,
|
|
1130
|
+
statement: "Statement | QueryBuilder",
|
|
1131
|
+
/,
|
|
1132
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1133
|
+
schema_type: None = None,
|
|
1134
|
+
statement_config: "StatementConfig | None" = None,
|
|
1135
|
+
count_with_window: bool = False,
|
|
1136
|
+
**kwargs: Any,
|
|
1137
|
+
) -> "tuple[list[dict[str, Any]], int]": ...
|
|
1138
|
+
|
|
1139
|
+
async def select_with_total(
|
|
1140
|
+
self,
|
|
1141
|
+
statement: "Statement | QueryBuilder",
|
|
1142
|
+
/,
|
|
1143
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1144
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
1145
|
+
statement_config: "StatementConfig | None" = None,
|
|
1146
|
+
count_with_window: bool = False,
|
|
1147
|
+
**kwargs: Any,
|
|
1148
|
+
) -> "tuple[list[SchemaT] | list[dict[str, Any]], int]":
|
|
1149
|
+
"""Execute a select statement and return both the data and total count.
|
|
1150
|
+
|
|
1151
|
+
This method is designed for pagination scenarios where you need both
|
|
1152
|
+
the current page of data and the total number of rows that match the query.
|
|
1153
|
+
|
|
1154
|
+
Args:
|
|
1155
|
+
statement: The SQL statement, QueryBuilder, or raw SQL string
|
|
1156
|
+
*parameters: Parameters for the SQL statement
|
|
1157
|
+
schema_type: Optional schema type for data transformation
|
|
1158
|
+
statement_config: Optional SQL configuration
|
|
1159
|
+
count_with_window: If True, use a single query with COUNT(*) OVER() window
|
|
1160
|
+
function instead of two separate queries. This can be more efficient
|
|
1161
|
+
for some databases but adds a column to each row. Default False.
|
|
1162
|
+
**kwargs: Additional keyword arguments
|
|
1163
|
+
|
|
1164
|
+
Returns:
|
|
1165
|
+
A tuple containing:
|
|
1166
|
+
- List of data rows (transformed by schema_type if provided)
|
|
1167
|
+
- Total count of rows matching the query (ignoring LIMIT/OFFSET)
|
|
1168
|
+
|
|
1169
|
+
Example:
|
|
1170
|
+
>>> # Two-query approach (default):
|
|
1171
|
+
>>> data, total = await driver.select_with_total(
|
|
1172
|
+
... sql
|
|
1173
|
+
... .select("*")
|
|
1174
|
+
... .from_("users")
|
|
1175
|
+
... .where_eq("status", "active")
|
|
1176
|
+
... .limit(10)
|
|
1177
|
+
... )
|
|
1178
|
+
|
|
1179
|
+
>>> # Single-query with window function:
|
|
1180
|
+
>>> data, total = await driver.select_with_total(
|
|
1181
|
+
... sql
|
|
1182
|
+
... .select("*")
|
|
1183
|
+
... .from_("users")
|
|
1184
|
+
... .where_eq("status", "active")
|
|
1185
|
+
... .limit(10),
|
|
1186
|
+
... count_with_window=True,
|
|
1187
|
+
... )
|
|
1188
|
+
|
|
1189
|
+
"""
|
|
1190
|
+
sql_statement = self.prepare_statement(
|
|
1191
|
+
statement, parameters, statement_config=statement_config or self.statement_config, kwargs=kwargs
|
|
1192
|
+
)
|
|
1193
|
+
|
|
1194
|
+
if count_with_window:
|
|
1195
|
+
modified_sql = self._add_count_over_column(sql_statement)
|
|
1196
|
+
result = await self.dispatch_statement_execution(modified_sql, self.connection)
|
|
1197
|
+
rows = result.all()
|
|
1198
|
+
data, total = self._extract_total_from_rows(rows)
|
|
1199
|
+
|
|
1200
|
+
if schema_type is not None:
|
|
1201
|
+
return ([schema_type(**r) for r in data], total)
|
|
1202
|
+
return (data, total)
|
|
1203
|
+
|
|
1204
|
+
count_result = await self.dispatch_statement_execution(self._create_count_query(sql_statement), self.connection)
|
|
1205
|
+
select_result = await self.dispatch_statement_execution(sql_statement, self.connection)
|
|
1206
|
+
|
|
1207
|
+
return (select_result.get_data(schema_type=schema_type), count_result.scalar())
|
|
1208
|
+
|
|
1209
|
+
@overload
|
|
1210
|
+
async def fetch_with_total(
|
|
1211
|
+
self,
|
|
1212
|
+
statement: "Statement | QueryBuilder",
|
|
1213
|
+
/,
|
|
1214
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1215
|
+
schema_type: "type[SchemaT]",
|
|
1216
|
+
statement_config: "StatementConfig | None" = None,
|
|
1217
|
+
count_with_window: bool = False,
|
|
1218
|
+
**kwargs: Any,
|
|
1219
|
+
) -> "tuple[list[SchemaT], int]": ...
|
|
1220
|
+
|
|
1221
|
+
@overload
|
|
1222
|
+
async def fetch_with_total(
|
|
1223
|
+
self,
|
|
1224
|
+
statement: "Statement | QueryBuilder",
|
|
1225
|
+
/,
|
|
1226
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1227
|
+
schema_type: None = None,
|
|
1228
|
+
statement_config: "StatementConfig | None" = None,
|
|
1229
|
+
count_with_window: bool = False,
|
|
1230
|
+
**kwargs: Any,
|
|
1231
|
+
) -> "tuple[list[dict[str, Any]], int]": ...
|
|
1232
|
+
|
|
1233
|
+
async def fetch_with_total(
|
|
1234
|
+
self,
|
|
1235
|
+
statement: "Statement | QueryBuilder",
|
|
1236
|
+
/,
|
|
1237
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1238
|
+
schema_type: "type[SchemaT] | None" = None,
|
|
1239
|
+
statement_config: "StatementConfig | None" = None,
|
|
1240
|
+
count_with_window: bool = False,
|
|
1241
|
+
**kwargs: Any,
|
|
1242
|
+
) -> "tuple[list[SchemaT] | list[dict[str, Any]], int]":
|
|
1243
|
+
"""Execute a select statement and return both the data and total count.
|
|
1244
|
+
|
|
1245
|
+
This is an alias for :meth:`select_with_total` provided for users familiar
|
|
1246
|
+
with asyncpg's fetch() naming convention.
|
|
1247
|
+
|
|
1248
|
+
This method is designed for pagination scenarios where you need both
|
|
1249
|
+
the current page of data and the total number of rows that match the query.
|
|
1250
|
+
|
|
1251
|
+
See Also:
|
|
1252
|
+
select_with_total(): Primary method with identical behavior and full documentation
|
|
1253
|
+
|
|
1254
|
+
"""
|
|
1255
|
+
return await self.select_with_total(
|
|
1256
|
+
statement,
|
|
1257
|
+
*parameters,
|
|
1258
|
+
schema_type=schema_type,
|
|
1259
|
+
statement_config=statement_config,
|
|
1260
|
+
count_with_window=count_with_window,
|
|
1261
|
+
**kwargs,
|
|
1262
|
+
)
|
|
1263
|
+
|
|
1264
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1265
|
+
# ARROW API METHODS
|
|
1266
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1267
|
+
|
|
1268
|
+
async def select_to_arrow(
|
|
1269
|
+
self,
|
|
1270
|
+
statement: "Statement | QueryBuilder",
|
|
1271
|
+
/,
|
|
1272
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1273
|
+
statement_config: "StatementConfig | None" = None,
|
|
1274
|
+
return_format: "ArrowReturnFormat" = "table",
|
|
1275
|
+
native_only: bool = False,
|
|
1276
|
+
batch_size: int | None = None,
|
|
1277
|
+
arrow_schema: Any = None,
|
|
1278
|
+
**kwargs: Any,
|
|
1279
|
+
) -> "ArrowResult":
|
|
1280
|
+
"""Execute query and return results as Apache Arrow format (async).
|
|
1281
|
+
|
|
1282
|
+
This base implementation uses the conversion path: execute() → dict → Arrow.
|
|
1283
|
+
Adapters with native Arrow support (ADBC, DuckDB, BigQuery) override this
|
|
1284
|
+
method to use zero-copy native paths for 5-10x performance improvement.
|
|
1285
|
+
|
|
1286
|
+
Args:
|
|
1287
|
+
statement: SQL query string, Statement, or QueryBuilder
|
|
1288
|
+
*parameters: Query parameters (same format as execute()/select())
|
|
1289
|
+
statement_config: Optional statement configuration override
|
|
1290
|
+
return_format: "table" for pyarrow.Table (default), "batch" for single RecordBatch,
|
|
1291
|
+
"batches" for iterator of RecordBatches, "reader" for RecordBatchReader
|
|
1292
|
+
native_only: If True, raise error if native Arrow unavailable (default: False)
|
|
1293
|
+
batch_size: Rows per batch for "batch"/"batches" format (default: None = all rows)
|
|
1294
|
+
arrow_schema: Optional pyarrow.Schema for type casting
|
|
1295
|
+
**kwargs: Additional keyword arguments
|
|
1296
|
+
|
|
1297
|
+
Returns:
|
|
1298
|
+
ArrowResult containing pyarrow.Table, RecordBatchReader, or RecordBatches
|
|
1299
|
+
|
|
1300
|
+
Raises:
|
|
1301
|
+
ImproperConfigurationError: If native_only=True and adapter doesn't support native Arrow
|
|
1302
|
+
|
|
1303
|
+
Examples:
|
|
1304
|
+
>>> result = await driver.select_to_arrow(
|
|
1305
|
+
... "SELECT * FROM users WHERE age > ?", 18
|
|
1306
|
+
... )
|
|
1307
|
+
>>> df = result.to_pandas()
|
|
1308
|
+
>>> print(df.head())
|
|
1309
|
+
|
|
1310
|
+
>>> # Force native Arrow path (raises error if unavailable)
|
|
1311
|
+
>>> result = await driver.select_to_arrow(
|
|
1312
|
+
... "SELECT * FROM users", native_only=True
|
|
1313
|
+
... )
|
|
1314
|
+
|
|
1315
|
+
"""
|
|
1316
|
+
if native_only:
|
|
1317
|
+
msg = (
|
|
1318
|
+
f"Adapter '{self.__class__.__name__}' does not support native Arrow results. "
|
|
1319
|
+
f"Use native_only=False to allow conversion path, or switch to an adapter "
|
|
1320
|
+
f"with native Arrow support (ADBC, DuckDB, BigQuery)."
|
|
1321
|
+
)
|
|
1322
|
+
raise ImproperConfigurationError(msg)
|
|
1323
|
+
|
|
1324
|
+
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
|
|
1325
|
+
|
|
1326
|
+
arrow_data = convert_dict_to_arrow_with_schema(
|
|
1327
|
+
result.get_data(), return_format=return_format, batch_size=batch_size, arrow_schema=arrow_schema
|
|
1328
|
+
)
|
|
1329
|
+
return create_arrow_result(
|
|
1330
|
+
statement=result.statement,
|
|
1331
|
+
data=arrow_data,
|
|
1332
|
+
rows_affected=result.rows_affected,
|
|
1333
|
+
last_inserted_id=result.last_inserted_id,
|
|
1334
|
+
execution_time=result.execution_time,
|
|
1335
|
+
metadata=result.metadata,
|
|
1336
|
+
)
|
|
1337
|
+
|
|
1338
|
+
async def fetch_to_arrow(
|
|
1339
|
+
self,
|
|
1340
|
+
statement: "Statement | QueryBuilder",
|
|
1341
|
+
/,
|
|
1342
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1343
|
+
statement_config: "StatementConfig | None" = None,
|
|
1344
|
+
return_format: "ArrowReturnFormat" = "table",
|
|
1345
|
+
native_only: bool = False,
|
|
1346
|
+
batch_size: int | None = None,
|
|
1347
|
+
arrow_schema: Any = None,
|
|
1348
|
+
**kwargs: Any,
|
|
1349
|
+
) -> "ArrowResult":
|
|
1350
|
+
"""Execute query and return results as Apache Arrow format (async).
|
|
1351
|
+
|
|
1352
|
+
This is an alias for :meth:`select_to_arrow` provided for users familiar
|
|
1353
|
+
with asyncpg's fetch() naming convention.
|
|
1354
|
+
|
|
1355
|
+
See Also:
|
|
1356
|
+
select_to_arrow(): Primary method with identical behavior and full documentation
|
|
1357
|
+
|
|
1358
|
+
"""
|
|
1359
|
+
return await self.select_to_arrow(
|
|
1360
|
+
statement,
|
|
1361
|
+
*parameters,
|
|
1362
|
+
statement_config=statement_config,
|
|
1363
|
+
return_format=return_format,
|
|
1364
|
+
native_only=native_only,
|
|
1365
|
+
batch_size=batch_size,
|
|
1366
|
+
arrow_schema=arrow_schema,
|
|
1367
|
+
**kwargs,
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1370
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1371
|
+
# STACK EXECUTION
|
|
1372
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1373
|
+
|
|
1374
|
+
async def execute_stack(
|
|
1375
|
+
self, stack: "StatementStack", *, continue_on_error: bool = False
|
|
1376
|
+
) -> "tuple[StackResult, ...]":
|
|
1377
|
+
"""Execute a StatementStack sequentially using the adapter's primitives."""
|
|
1378
|
+
if not isinstance(stack, StatementStack):
|
|
1379
|
+
msg = "execute_stack expects a StatementStack instance"
|
|
1380
|
+
raise TypeError(msg)
|
|
1381
|
+
if not stack:
|
|
1382
|
+
msg = "Cannot execute an empty StatementStack"
|
|
1383
|
+
raise ValueError(msg)
|
|
1384
|
+
|
|
1385
|
+
results: list[StackResult] = []
|
|
1386
|
+
single_transaction = not continue_on_error
|
|
1387
|
+
|
|
1388
|
+
with StackExecutionObserver(self, stack, continue_on_error, native_pipeline=False) as observer:
|
|
1389
|
+
started_transaction = False
|
|
1390
|
+
|
|
1391
|
+
try:
|
|
1392
|
+
if single_transaction and not self._connection_in_transaction():
|
|
1393
|
+
await self.begin()
|
|
1394
|
+
started_transaction = True
|
|
1395
|
+
|
|
1396
|
+
for index, operation in enumerate(stack.operations):
|
|
1397
|
+
try:
|
|
1398
|
+
result = await self._execute_stack_operation(operation)
|
|
1399
|
+
except Exception as exc: # pragma: no cover - exercised via tests
|
|
1400
|
+
stack_error = StackExecutionError(
|
|
1401
|
+
index,
|
|
1402
|
+
describe_stack_statement(operation.statement),
|
|
1403
|
+
exc,
|
|
1404
|
+
adapter=type(self).__name__,
|
|
1405
|
+
mode="continue-on-error" if continue_on_error else "fail-fast",
|
|
1406
|
+
)
|
|
1407
|
+
|
|
1408
|
+
if started_transaction and not continue_on_error:
|
|
1409
|
+
try:
|
|
1410
|
+
await self.rollback()
|
|
1411
|
+
except Exception as rollback_error: # pragma: no cover - diagnostics only
|
|
1412
|
+
logger.debug("Rollback after stack failure failed: %s", rollback_error)
|
|
1413
|
+
started_transaction = False
|
|
1414
|
+
|
|
1415
|
+
if continue_on_error:
|
|
1416
|
+
await self._rollback_after_stack_error_async()
|
|
1417
|
+
observer.record_operation_error(stack_error)
|
|
1418
|
+
results.append(StackResult.from_error(stack_error))
|
|
1419
|
+
continue
|
|
1420
|
+
|
|
1421
|
+
raise stack_error from exc
|
|
1422
|
+
|
|
1423
|
+
results.append(StackResult(result=result))
|
|
1424
|
+
|
|
1425
|
+
if continue_on_error:
|
|
1426
|
+
await self._commit_after_stack_operation_async()
|
|
1427
|
+
|
|
1428
|
+
if started_transaction:
|
|
1429
|
+
await self.commit()
|
|
1430
|
+
except Exception:
|
|
1431
|
+
if started_transaction:
|
|
1432
|
+
try:
|
|
1433
|
+
await self.rollback()
|
|
1434
|
+
except Exception as rollback_error: # pragma: no cover - diagnostics only
|
|
1435
|
+
logger.debug("Rollback after stack failure failed: %s", rollback_error)
|
|
1436
|
+
raise
|
|
1437
|
+
|
|
1438
|
+
return tuple(results)
|
|
1439
|
+
|
|
1440
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1441
|
+
# STORAGE API METHODS
|
|
1442
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1443
|
+
|
|
1444
|
+
async def select_to_storage(
|
|
1445
|
+
self,
|
|
1446
|
+
statement: "SQL | str",
|
|
1447
|
+
destination: "StorageDestination",
|
|
1448
|
+
/,
|
|
1449
|
+
*parameters: "StatementParameters | StatementFilter",
|
|
1450
|
+
statement_config: "StatementConfig | None" = None,
|
|
1451
|
+
partitioner: "dict[str, object] | None" = None,
|
|
1452
|
+
format_hint: "StorageFormat | None" = None,
|
|
1453
|
+
telemetry: "StorageTelemetry | None" = None,
|
|
1454
|
+
) -> "StorageBridgeJob":
|
|
1455
|
+
"""Stream a SELECT statement directly into storage.
|
|
1456
|
+
|
|
1457
|
+
Args:
|
|
1458
|
+
statement: SQL statement to execute.
|
|
1459
|
+
destination: Storage destination path.
|
|
1460
|
+
parameters: Query parameters.
|
|
1461
|
+
statement_config: Optional statement configuration.
|
|
1462
|
+
partitioner: Optional partitioner configuration.
|
|
1463
|
+
format_hint: Optional format hint for storage.
|
|
1464
|
+
telemetry: Optional telemetry dict to merge.
|
|
1465
|
+
|
|
1466
|
+
Returns:
|
|
1467
|
+
StorageBridgeJob with execution telemetry.
|
|
1468
|
+
|
|
1469
|
+
"""
|
|
1470
|
+
self._raise_storage_not_implemented("select_to_storage")
|
|
1471
|
+
raise NotImplementedError
|
|
1472
|
+
|
|
1473
|
+
async def load_from_arrow(
|
|
1474
|
+
self,
|
|
1475
|
+
table: str,
|
|
1476
|
+
source: "ArrowResult | Any",
|
|
1477
|
+
*,
|
|
1478
|
+
partitioner: "dict[str, object] | None" = None,
|
|
1479
|
+
overwrite: bool = False,
|
|
1480
|
+
) -> "StorageBridgeJob":
|
|
1481
|
+
"""Load Arrow data into the target table.
|
|
1482
|
+
|
|
1483
|
+
Args:
|
|
1484
|
+
table: Target table name.
|
|
1485
|
+
source: Arrow data source.
|
|
1486
|
+
partitioner: Optional partitioner configuration.
|
|
1487
|
+
overwrite: Whether to overwrite existing data.
|
|
1488
|
+
|
|
1489
|
+
Returns:
|
|
1490
|
+
StorageBridgeJob with execution telemetry.
|
|
1491
|
+
|
|
1492
|
+
Raises:
|
|
1493
|
+
NotImplementedError: If not implemented.
|
|
1494
|
+
|
|
1495
|
+
"""
|
|
1496
|
+
self._raise_storage_not_implemented("load_from_arrow")
|
|
1497
|
+
raise NotImplementedError
|
|
1498
|
+
|
|
1499
|
+
async def load_from_storage(
|
|
1500
|
+
self,
|
|
1501
|
+
table: str,
|
|
1502
|
+
source: "StorageDestination",
|
|
1503
|
+
*,
|
|
1504
|
+
file_format: "StorageFormat",
|
|
1505
|
+
partitioner: "dict[str, object] | None" = None,
|
|
1506
|
+
overwrite: bool = False,
|
|
1507
|
+
) -> "StorageBridgeJob":
|
|
1508
|
+
"""Load artifacts from storage into the target table.
|
|
1509
|
+
|
|
1510
|
+
Args:
|
|
1511
|
+
table: Target table name.
|
|
1512
|
+
source: Storage source path.
|
|
1513
|
+
file_format: File format of source.
|
|
1514
|
+
partitioner: Optional partitioner configuration.
|
|
1515
|
+
overwrite: Whether to overwrite existing data.
|
|
1516
|
+
|
|
1517
|
+
Returns:
|
|
1518
|
+
StorageBridgeJob with execution telemetry.
|
|
1519
|
+
|
|
1520
|
+
"""
|
|
1521
|
+
self._raise_storage_not_implemented("load_from_storage")
|
|
1522
|
+
raise NotImplementedError
|
|
1523
|
+
|
|
1524
|
+
def stage_artifact(self, request: "dict[str, Any]") -> "dict[str, Any]":
|
|
1525
|
+
"""Provision staging metadata for adapters that require remote URIs.
|
|
1526
|
+
|
|
1527
|
+
Args:
|
|
1528
|
+
request: Staging request configuration.
|
|
1529
|
+
|
|
1530
|
+
Returns:
|
|
1531
|
+
Staging metadata dict.
|
|
1532
|
+
|
|
1533
|
+
"""
|
|
1534
|
+
self._raise_storage_not_implemented("stage_artifact")
|
|
1535
|
+
raise NotImplementedError
|
|
1536
|
+
|
|
1537
|
+
def flush_staging_artifacts(self, artifacts: "list[dict[str, Any]]", *, error: Exception | None = None) -> None:
|
|
1538
|
+
"""Clean up staged artifacts after a job completes.
|
|
1539
|
+
|
|
1540
|
+
Args:
|
|
1541
|
+
artifacts: List of staging artifacts to clean up.
|
|
1542
|
+
error: Optional error that triggered cleanup.
|
|
1543
|
+
|
|
1544
|
+
"""
|
|
1545
|
+
if artifacts:
|
|
1546
|
+
self._raise_storage_not_implemented("flush_staging_artifacts")
|
|
1547
|
+
|
|
1548
|
+
def get_storage_job(self, job_id: str) -> "StorageBridgeJob | None":
|
|
1549
|
+
"""Fetch a previously created job handle.
|
|
1550
|
+
|
|
1551
|
+
Args:
|
|
1552
|
+
job_id: Job identifier.
|
|
1553
|
+
|
|
1554
|
+
Returns:
|
|
1555
|
+
StorageBridgeJob if found, None otherwise.
|
|
1556
|
+
|
|
1557
|
+
"""
|
|
1558
|
+
return None
|
|
1559
|
+
|
|
1560
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1561
|
+
# UTILITY METHODS
|
|
1562
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1563
|
+
|
|
1564
|
+
def convert_to_dialect(
|
|
1565
|
+
self, statement: "Statement", to_dialect: "DialectType | None" = None, pretty: bool = DEFAULT_PRETTY
|
|
1566
|
+
) -> str:
|
|
1567
|
+
"""Convert a statement to a target SQL dialect.
|
|
1568
|
+
|
|
1569
|
+
Args:
|
|
1570
|
+
statement: SQL statement to convert.
|
|
1571
|
+
to_dialect: Target dialect (defaults to current dialect).
|
|
1572
|
+
pretty: Whether to format the output SQL.
|
|
1573
|
+
|
|
1574
|
+
Returns:
|
|
1575
|
+
SQL string in target dialect.
|
|
1576
|
+
|
|
1577
|
+
"""
|
|
1578
|
+
return _convert_to_dialect_impl(statement, self.dialect, to_dialect, pretty)
|
|
1579
|
+
|
|
1580
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1581
|
+
# PRIVATE/INTERNAL METHODS
|
|
1582
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
1583
|
+
|
|
1584
|
+
def _connection_in_transaction(self) -> bool:
|
|
1585
|
+
"""Check if the connection is inside a transaction.
|
|
1586
|
+
|
|
1587
|
+
Each adapter MUST override this method with direct attribute access
|
|
1588
|
+
for optimal mypyc performance. Do not use getattr chains.
|
|
1589
|
+
|
|
1590
|
+
Raises:
|
|
1591
|
+
NotImplementedError: Always - subclasses must override.
|
|
1592
|
+
|
|
1593
|
+
"""
|
|
1594
|
+
msg = "Adapters must override _connection_in_transaction()"
|
|
1595
|
+
raise NotImplementedError(msg)
|
|
1596
|
+
|
|
1597
|
+
async def _execute_stack_operation(self, operation: "StackOperation") -> "SQLResult | ArrowResult | None":
|
|
1598
|
+
kwargs = dict(operation.keyword_arguments) if operation.keyword_arguments else {}
|
|
1599
|
+
|
|
1600
|
+
if operation.method == "execute":
|
|
1601
|
+
return await self.execute(operation.statement, *operation.arguments, **kwargs)
|
|
1602
|
+
|
|
1603
|
+
if operation.method == "execute_many":
|
|
1604
|
+
if not operation.arguments:
|
|
1605
|
+
msg = "execute_many stack operation requires parameter sets"
|
|
1606
|
+
raise ValueError(msg)
|
|
1607
|
+
parameter_sets = operation.arguments[0]
|
|
1608
|
+
filters = operation.arguments[1:]
|
|
1609
|
+
return await self.execute_many(operation.statement, parameter_sets, *filters, **kwargs)
|
|
1610
|
+
|
|
1611
|
+
if operation.method == "execute_script":
|
|
1612
|
+
return await self.execute_script(operation.statement, *operation.arguments, **kwargs)
|
|
1613
|
+
|
|
1614
|
+
if operation.method == "execute_arrow":
|
|
1615
|
+
return await self.select_to_arrow(operation.statement, *operation.arguments, **kwargs)
|
|
1616
|
+
|
|
1617
|
+
msg = f"Unsupported stack operation method: {operation.method}"
|
|
1618
|
+
raise ValueError(msg)
|
|
1619
|
+
|
|
1620
|
+
async def _rollback_after_stack_error_async(self) -> None:
|
|
1621
|
+
"""Attempt to rollback after a stack operation error (async)."""
|
|
1622
|
+
try:
|
|
1623
|
+
await self.rollback()
|
|
1624
|
+
except Exception as rollback_error: # pragma: no cover - driver-specific cleanup
|
|
1625
|
+
logger.debug("Rollback after stack error failed: %s", rollback_error)
|
|
1626
|
+
|
|
1627
|
+
async def _commit_after_stack_operation_async(self) -> None:
|
|
1628
|
+
"""Attempt to commit after a successful stack operation when not batching (async)."""
|
|
1629
|
+
try:
|
|
1630
|
+
await self.commit()
|
|
1631
|
+
except Exception as commit_error: # pragma: no cover - driver-specific cleanup
|
|
1632
|
+
logger.debug("Commit after stack operation failed: %s", commit_error)
|
|
1633
|
+
|
|
1634
|
+
def _storage_pipeline(self) -> "AsyncStoragePipeline":
|
|
1635
|
+
"""Get or create an async storage pipeline.
|
|
1636
|
+
|
|
1637
|
+
Returns:
|
|
1638
|
+
AsyncStoragePipeline instance.
|
|
1639
|
+
|
|
1640
|
+
"""
|
|
1641
|
+
factory = self.storage_pipeline_factory
|
|
1642
|
+
if factory is None:
|
|
1643
|
+
return AsyncStoragePipeline()
|
|
1644
|
+
return cast("AsyncStoragePipeline", factory())
|
|
1645
|
+
|
|
1646
|
+
async def _write_result_to_storage_async(
|
|
1647
|
+
self,
|
|
1648
|
+
result: "ArrowResult",
|
|
1649
|
+
destination: "StorageDestination",
|
|
1650
|
+
*,
|
|
1651
|
+
format_hint: "StorageFormat | None" = None,
|
|
1652
|
+
storage_options: "dict[str, Any] | None" = None,
|
|
1653
|
+
pipeline: "AsyncStoragePipeline | None" = None,
|
|
1654
|
+
) -> "StorageTelemetry":
|
|
1655
|
+
"""Write Arrow result to storage with telemetry.
|
|
1656
|
+
|
|
1657
|
+
Args:
|
|
1658
|
+
result: Arrow result to write.
|
|
1659
|
+
destination: Storage destination.
|
|
1660
|
+
format_hint: Optional format hint.
|
|
1661
|
+
storage_options: Optional storage options.
|
|
1662
|
+
pipeline: Optional storage pipeline.
|
|
1663
|
+
|
|
1664
|
+
Returns:
|
|
1665
|
+
StorageTelemetry with write metrics.
|
|
1666
|
+
|
|
1667
|
+
"""
|
|
1668
|
+
runtime = self.observability
|
|
1669
|
+
span = runtime.start_storage_span(
|
|
1670
|
+
"write", destination=stringify_storage_target(destination), format_label=format_hint
|
|
1671
|
+
)
|
|
1672
|
+
try:
|
|
1673
|
+
telemetry = await result.write_to_storage_async(
|
|
1674
|
+
destination, format_hint=format_hint, storage_options=storage_options, pipeline=pipeline
|
|
1675
|
+
)
|
|
1676
|
+
except Exception as exc:
|
|
1677
|
+
runtime.end_storage_span(span, error=exc)
|
|
1678
|
+
raise
|
|
1679
|
+
telemetry = runtime.annotate_storage_telemetry(telemetry)
|
|
1680
|
+
runtime.end_storage_span(span, telemetry=telemetry)
|
|
1681
|
+
return telemetry
|
|
1682
|
+
|
|
1683
|
+
async def _read_arrow_from_storage_async(
|
|
1684
|
+
self,
|
|
1685
|
+
source: "StorageDestination",
|
|
1686
|
+
*,
|
|
1687
|
+
file_format: "StorageFormat",
|
|
1688
|
+
storage_options: "dict[str, Any] | None" = None,
|
|
1689
|
+
) -> "tuple[ArrowTable, StorageTelemetry]":
|
|
1690
|
+
"""Read Arrow table from storage with telemetry.
|
|
1691
|
+
|
|
1692
|
+
Args:
|
|
1693
|
+
source: Storage source path.
|
|
1694
|
+
file_format: File format to read.
|
|
1695
|
+
storage_options: Optional storage options.
|
|
1696
|
+
|
|
1697
|
+
Returns:
|
|
1698
|
+
Tuple of (ArrowTable, StorageTelemetry).
|
|
1699
|
+
|
|
1700
|
+
"""
|
|
1701
|
+
runtime = self.observability
|
|
1702
|
+
span = runtime.start_storage_span(
|
|
1703
|
+
"read", destination=stringify_storage_target(source), format_label=file_format
|
|
1704
|
+
)
|
|
1705
|
+
pipeline = self._storage_pipeline()
|
|
1706
|
+
try:
|
|
1707
|
+
table, telemetry = await pipeline.read_arrow_async(
|
|
1708
|
+
source, file_format=file_format, storage_options=storage_options
|
|
1709
|
+
)
|
|
1710
|
+
except Exception as exc:
|
|
1711
|
+
runtime.end_storage_span(span, error=exc)
|
|
1712
|
+
raise
|
|
1713
|
+
telemetry = runtime.annotate_storage_telemetry(telemetry)
|
|
1714
|
+
runtime.end_storage_span(span, telemetry=telemetry)
|
|
1715
|
+
return table, telemetry
|
|
1716
|
+
|
|
1717
|
+
|
|
1718
|
+
@mypyc_attr(allow_interpreted_subclasses=True)
|
|
1719
|
+
class AsyncDataDictionaryBase(DataDictionaryDialectMixin, DataDictionaryMixin):
|
|
1720
|
+
"""Base class for asynchronous data dictionary implementations.
|
|
1721
|
+
|
|
1722
|
+
Uses Python-compatible class layouts for cross-module inheritance.
|
|
1723
|
+
Child classes define dialect as a class attribute.
|
|
1724
|
+
"""
|
|
1725
|
+
|
|
1726
|
+
dialect: "ClassVar[str]"
|
|
1727
|
+
"""Dialect identifier. Must be defined by subclasses as a class attribute."""
|
|
1728
|
+
|
|
1729
|
+
def __init__(self) -> None:
|
|
1730
|
+
self._version_cache: dict[int, VersionInfo | None] = {}
|
|
1731
|
+
self._version_fetch_attempted: set[int] = set()
|
|
1732
|
+
|
|
1733
|
+
@abstractmethod
|
|
1734
|
+
async def get_version(self, driver: Any) -> "VersionInfo | None":
|
|
1735
|
+
"""Get database version information.
|
|
1736
|
+
|
|
1737
|
+
Args:
|
|
1738
|
+
driver: Async database driver instance
|
|
1739
|
+
|
|
1740
|
+
Returns:
|
|
1741
|
+
Version information or None if detection fails
|
|
1742
|
+
|
|
1743
|
+
"""
|
|
1744
|
+
|
|
1745
|
+
@abstractmethod
|
|
1746
|
+
async def get_feature_flag(self, driver: Any, feature: str) -> bool:
|
|
1747
|
+
"""Check if database supports a specific feature.
|
|
1748
|
+
|
|
1749
|
+
Args:
|
|
1750
|
+
driver: Async database driver instance
|
|
1751
|
+
feature: Feature name to check
|
|
1752
|
+
|
|
1753
|
+
Returns:
|
|
1754
|
+
True if feature is supported, False otherwise
|
|
1755
|
+
|
|
1756
|
+
"""
|
|
1757
|
+
|
|
1758
|
+
@abstractmethod
|
|
1759
|
+
async def get_optimal_type(self, driver: Any, type_category: str) -> str:
|
|
1760
|
+
"""Get optimal database type for a category.
|
|
1761
|
+
|
|
1762
|
+
Args:
|
|
1763
|
+
driver: Async database driver instance
|
|
1764
|
+
type_category: Type category (e.g., 'json', 'uuid', 'boolean')
|
|
1765
|
+
|
|
1766
|
+
Returns:
|
|
1767
|
+
Database-specific type name
|
|
1768
|
+
|
|
1769
|
+
"""
|
|
1770
|
+
|
|
1771
|
+
@abstractmethod
|
|
1772
|
+
async def get_tables(self, driver: Any, schema: "str | None" = None) -> "list[TableMetadata]":
|
|
1773
|
+
"""Get list of tables in schema.
|
|
1774
|
+
|
|
1775
|
+
Args:
|
|
1776
|
+
driver: Async database driver instance
|
|
1777
|
+
schema: Schema name (None for default)
|
|
1778
|
+
|
|
1779
|
+
Returns:
|
|
1780
|
+
List of table metadata dictionaries
|
|
1781
|
+
|
|
1782
|
+
"""
|
|
1783
|
+
|
|
1784
|
+
@abstractmethod
|
|
1785
|
+
async def get_columns(
|
|
1786
|
+
self, driver: Any, table: "str | None" = None, schema: "str | None" = None
|
|
1787
|
+
) -> "list[ColumnMetadata]":
|
|
1788
|
+
"""Get column information for a table or schema.
|
|
1789
|
+
|
|
1790
|
+
Args:
|
|
1791
|
+
driver: Async database driver instance
|
|
1792
|
+
table: Table name (None to fetch columns for all tables in schema)
|
|
1793
|
+
schema: Schema name (None for default)
|
|
1794
|
+
|
|
1795
|
+
Returns:
|
|
1796
|
+
List of column metadata dictionaries
|
|
1797
|
+
|
|
1798
|
+
"""
|
|
1799
|
+
|
|
1800
|
+
@abstractmethod
|
|
1801
|
+
async def get_indexes(
|
|
1802
|
+
self, driver: Any, table: "str | None" = None, schema: "str | None" = None
|
|
1803
|
+
) -> "list[IndexMetadata]":
|
|
1804
|
+
"""Get index information for a table or schema.
|
|
1805
|
+
|
|
1806
|
+
Args:
|
|
1807
|
+
driver: Async database driver instance
|
|
1808
|
+
table: Table name (None to fetch indexes for all tables in schema)
|
|
1809
|
+
schema: Schema name (None for default)
|
|
1810
|
+
|
|
1811
|
+
Returns:
|
|
1812
|
+
List of index metadata dictionaries
|
|
1813
|
+
|
|
1814
|
+
"""
|
|
1815
|
+
|
|
1816
|
+
@abstractmethod
|
|
1817
|
+
async def get_foreign_keys(
|
|
1818
|
+
self, driver: Any, table: "str | None" = None, schema: "str | None" = None
|
|
1819
|
+
) -> "list[ForeignKeyMetadata]":
|
|
1820
|
+
"""Get foreign key metadata.
|
|
1821
|
+
|
|
1822
|
+
Args:
|
|
1823
|
+
driver: Async database driver instance
|
|
1824
|
+
table: Optional table name filter
|
|
1825
|
+
schema: Optional schema name filter
|
|
1826
|
+
|
|
1827
|
+
Returns:
|
|
1828
|
+
List of foreign key metadata
|
|
1829
|
+
|
|
1830
|
+
"""
|