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
|
@@ -0,0 +1,734 @@
|
|
|
1
|
+
"""Multi-connection pool for aiosqlite."""
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
import logging
|
|
5
|
+
import time
|
|
6
|
+
from contextlib import suppress
|
|
7
|
+
from inspect import isawaitable
|
|
8
|
+
from threading import Thread
|
|
9
|
+
from typing import TYPE_CHECKING, Any
|
|
10
|
+
|
|
11
|
+
import aiosqlite
|
|
12
|
+
|
|
13
|
+
from sqlspec.exceptions import SQLSpecError
|
|
14
|
+
from sqlspec.utils.logging import POOL_LOGGER_NAME, get_logger, log_with_context
|
|
15
|
+
from sqlspec.utils.uuids import uuid4
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Awaitable, Callable
|
|
19
|
+
from types import TracebackType
|
|
20
|
+
|
|
21
|
+
from sqlspec.adapters.aiosqlite._typing import AiosqliteConnection
|
|
22
|
+
|
|
23
|
+
__all__ = (
|
|
24
|
+
"AiosqliteConnectTimeoutError",
|
|
25
|
+
"AiosqliteConnectionPool",
|
|
26
|
+
"AiosqlitePoolClosedError",
|
|
27
|
+
"AiosqlitePoolConnection",
|
|
28
|
+
"AiosqlitePoolConnectionContext",
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
logger = get_logger(POOL_LOGGER_NAME)
|
|
32
|
+
|
|
33
|
+
_ADAPTER_NAME = "aiosqlite"
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class AiosqlitePoolClosedError(SQLSpecError):
|
|
37
|
+
"""Pool has been closed and cannot accept new operations."""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class AiosqliteConnectTimeoutError(SQLSpecError):
|
|
41
|
+
"""Connection could not be established within the specified timeout period."""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class AiosqlitePoolConnection:
|
|
45
|
+
"""Wrapper for database connections in the pool."""
|
|
46
|
+
|
|
47
|
+
__slots__ = ("_closed", "_healthy", "connection", "id", "idle_since")
|
|
48
|
+
|
|
49
|
+
def __init__(self, connection: "AiosqliteConnection") -> None:
|
|
50
|
+
"""Initialize pool connection wrapper.
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
connection: The raw aiosqlite connection
|
|
54
|
+
"""
|
|
55
|
+
self.id = uuid4().hex
|
|
56
|
+
self.connection = connection
|
|
57
|
+
self.idle_since: float | None = None
|
|
58
|
+
self._closed = False
|
|
59
|
+
self._healthy = True
|
|
60
|
+
|
|
61
|
+
@property
|
|
62
|
+
def idle_time(self) -> float:
|
|
63
|
+
"""Get idle time in seconds.
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
Idle time in seconds, 0.0 if connection is in use
|
|
67
|
+
"""
|
|
68
|
+
if self.idle_since is None:
|
|
69
|
+
return 0.0
|
|
70
|
+
return time.time() - self.idle_since
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def is_closed(self) -> bool:
|
|
74
|
+
"""Check if connection is closed.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
True if connection is closed
|
|
78
|
+
"""
|
|
79
|
+
return self._closed
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def is_healthy(self) -> bool:
|
|
83
|
+
"""Check if connection was healthy on last check.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
True if connection is presumed healthy
|
|
87
|
+
"""
|
|
88
|
+
return self._healthy and not self._closed
|
|
89
|
+
|
|
90
|
+
def mark_as_in_use(self) -> None:
|
|
91
|
+
"""Mark connection as in use."""
|
|
92
|
+
self.idle_since = None
|
|
93
|
+
|
|
94
|
+
def mark_as_idle(self) -> None:
|
|
95
|
+
"""Mark connection as idle."""
|
|
96
|
+
self.idle_since = time.time()
|
|
97
|
+
|
|
98
|
+
def mark_unhealthy(self) -> None:
|
|
99
|
+
"""Mark connection as unhealthy."""
|
|
100
|
+
self._healthy = False
|
|
101
|
+
|
|
102
|
+
async def is_alive(self) -> bool:
|
|
103
|
+
"""Check if connection is alive and functional.
|
|
104
|
+
|
|
105
|
+
Returns:
|
|
106
|
+
True if connection is healthy
|
|
107
|
+
"""
|
|
108
|
+
if self._closed:
|
|
109
|
+
self._healthy = False
|
|
110
|
+
return False
|
|
111
|
+
try:
|
|
112
|
+
await self.connection.execute("SELECT 1")
|
|
113
|
+
except Exception:
|
|
114
|
+
self._healthy = False
|
|
115
|
+
return False
|
|
116
|
+
else:
|
|
117
|
+
self._healthy = True
|
|
118
|
+
return True
|
|
119
|
+
|
|
120
|
+
async def reset(self) -> None:
|
|
121
|
+
"""Reset connection to clean state."""
|
|
122
|
+
if self._closed:
|
|
123
|
+
return
|
|
124
|
+
with suppress(Exception):
|
|
125
|
+
await self.connection.rollback()
|
|
126
|
+
|
|
127
|
+
async def close(self) -> None:
|
|
128
|
+
"""Close the connection."""
|
|
129
|
+
if self._closed:
|
|
130
|
+
return
|
|
131
|
+
try:
|
|
132
|
+
with suppress(Exception):
|
|
133
|
+
await self.connection.rollback()
|
|
134
|
+
await self.connection.close()
|
|
135
|
+
except Exception:
|
|
136
|
+
# Note: No pool context available at connection level
|
|
137
|
+
log_with_context(
|
|
138
|
+
logger, logging.DEBUG, "pool.connection.close.error", adapter=_ADAPTER_NAME, connection_id=self.id
|
|
139
|
+
)
|
|
140
|
+
finally:
|
|
141
|
+
self._closed = True
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
class AiosqlitePoolConnectionContext:
|
|
145
|
+
"""Async context manager for pooled aiosqlite connections."""
|
|
146
|
+
|
|
147
|
+
__slots__ = ("_connection", "_pool")
|
|
148
|
+
|
|
149
|
+
def __init__(self, pool: "AiosqliteConnectionPool") -> None:
|
|
150
|
+
"""Initialize the context manager.
|
|
151
|
+
|
|
152
|
+
Args:
|
|
153
|
+
pool: Connection pool instance.
|
|
154
|
+
"""
|
|
155
|
+
self._pool = pool
|
|
156
|
+
self._connection: AiosqlitePoolConnection | None = None
|
|
157
|
+
|
|
158
|
+
async def __aenter__(self) -> "AiosqliteConnection":
|
|
159
|
+
self._connection = await self._pool.acquire()
|
|
160
|
+
return self._connection.connection
|
|
161
|
+
|
|
162
|
+
async def __aexit__(
|
|
163
|
+
self, exc_type: "type[BaseException] | None", exc_val: "BaseException | None", exc_tb: "TracebackType | None"
|
|
164
|
+
) -> "bool | None":
|
|
165
|
+
if self._connection is None:
|
|
166
|
+
return False
|
|
167
|
+
await self._pool.release(self._connection)
|
|
168
|
+
self._connection = None
|
|
169
|
+
return False
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class AiosqliteConnectionPool:
|
|
173
|
+
"""Multi-connection pool for aiosqlite."""
|
|
174
|
+
|
|
175
|
+
__slots__ = (
|
|
176
|
+
"_closed_event_instance",
|
|
177
|
+
"_connect_timeout",
|
|
178
|
+
"_connection_parameters",
|
|
179
|
+
"_connection_registry",
|
|
180
|
+
"_health_check_interval",
|
|
181
|
+
"_idle_timeout",
|
|
182
|
+
"_lock_instance",
|
|
183
|
+
"_min_size",
|
|
184
|
+
"_on_connection_create",
|
|
185
|
+
"_operation_timeout",
|
|
186
|
+
"_pool_id",
|
|
187
|
+
"_pool_size",
|
|
188
|
+
"_queue_instance",
|
|
189
|
+
"_wal_initialized",
|
|
190
|
+
"_warmed",
|
|
191
|
+
)
|
|
192
|
+
|
|
193
|
+
def __init__(
|
|
194
|
+
self,
|
|
195
|
+
connection_parameters: "dict[str, Any]",
|
|
196
|
+
pool_size: int = 5,
|
|
197
|
+
min_size: int = 0,
|
|
198
|
+
connect_timeout: float = 30.0,
|
|
199
|
+
idle_timeout: float = 24 * 60 * 60,
|
|
200
|
+
operation_timeout: float = 10.0,
|
|
201
|
+
health_check_interval: float = 30.0,
|
|
202
|
+
on_connection_create: "Callable[[AiosqliteConnection], Awaitable[None]] | None" = None,
|
|
203
|
+
) -> None:
|
|
204
|
+
"""Initialize connection pool.
|
|
205
|
+
|
|
206
|
+
Args:
|
|
207
|
+
connection_parameters: SQLite connection parameters
|
|
208
|
+
pool_size: Maximum number of connections in the pool
|
|
209
|
+
min_size: Minimum connections to pre-create (pool warming)
|
|
210
|
+
connect_timeout: Maximum time to wait for connection acquisition
|
|
211
|
+
idle_timeout: Maximum time a connection can remain idle
|
|
212
|
+
operation_timeout: Maximum time for connection operations
|
|
213
|
+
health_check_interval: Seconds of idle time before running health check
|
|
214
|
+
on_connection_create: Async callback executed when connection is created
|
|
215
|
+
"""
|
|
216
|
+
self._connection_parameters = connection_parameters
|
|
217
|
+
self._pool_size = pool_size
|
|
218
|
+
self._min_size = min(min_size, pool_size)
|
|
219
|
+
self._connect_timeout = connect_timeout
|
|
220
|
+
self._idle_timeout = idle_timeout
|
|
221
|
+
self._operation_timeout = operation_timeout
|
|
222
|
+
self._health_check_interval = health_check_interval
|
|
223
|
+
self._on_connection_create = on_connection_create
|
|
224
|
+
|
|
225
|
+
self._connection_registry: dict[str, AiosqlitePoolConnection] = {}
|
|
226
|
+
self._wal_initialized = False
|
|
227
|
+
self._warmed = False
|
|
228
|
+
self._pool_id = uuid4().hex[:8] # Short ID for logging
|
|
229
|
+
|
|
230
|
+
self._queue_instance: asyncio.Queue[AiosqlitePoolConnection] | None = None
|
|
231
|
+
self._lock_instance: asyncio.Lock | None = None
|
|
232
|
+
self._closed_event_instance: asyncio.Event | None = None
|
|
233
|
+
|
|
234
|
+
@property
|
|
235
|
+
def _queue(self) -> "asyncio.Queue[AiosqlitePoolConnection]":
|
|
236
|
+
"""Lazy initialization of asyncio.Queue for Python 3.9 compatibility."""
|
|
237
|
+
if self._queue_instance is None:
|
|
238
|
+
self._queue_instance = asyncio.Queue(maxsize=self._pool_size)
|
|
239
|
+
return self._queue_instance
|
|
240
|
+
|
|
241
|
+
@property
|
|
242
|
+
def _lock(self) -> asyncio.Lock:
|
|
243
|
+
"""Lazy initialization of asyncio.Lock for Python 3.9 compatibility."""
|
|
244
|
+
if self._lock_instance is None:
|
|
245
|
+
self._lock_instance = asyncio.Lock()
|
|
246
|
+
return self._lock_instance
|
|
247
|
+
|
|
248
|
+
@property
|
|
249
|
+
def _closed_event(self) -> asyncio.Event:
|
|
250
|
+
"""Lazy initialization of asyncio.Event for Python 3.9 compatibility."""
|
|
251
|
+
if self._closed_event_instance is None:
|
|
252
|
+
self._closed_event_instance = asyncio.Event()
|
|
253
|
+
return self._closed_event_instance
|
|
254
|
+
|
|
255
|
+
@property
|
|
256
|
+
def is_closed(self) -> bool:
|
|
257
|
+
"""Check if pool is closed.
|
|
258
|
+
|
|
259
|
+
Returns:
|
|
260
|
+
True if pool is closed
|
|
261
|
+
"""
|
|
262
|
+
return self._closed_event_instance is not None and self._closed_event.is_set()
|
|
263
|
+
|
|
264
|
+
@property
|
|
265
|
+
def _database_name(self) -> str:
|
|
266
|
+
"""Get sanitized database name for logging."""
|
|
267
|
+
db = self._connection_parameters.get("database", "unknown")
|
|
268
|
+
return str(db).split("/")[-1] if db else "unknown"
|
|
269
|
+
|
|
270
|
+
def _set_connect_proxy_daemon(self, connect_proxy: Any) -> None:
|
|
271
|
+
"""Set daemon mode on aiosqlite worker thread before await.
|
|
272
|
+
|
|
273
|
+
aiosqlite <=0.21 used Connection as a Thread subclass.
|
|
274
|
+
aiosqlite >=0.22 stores an internal ``_thread`` attribute instead.
|
|
275
|
+
"""
|
|
276
|
+
try:
|
|
277
|
+
if isinstance(connect_proxy, Thread):
|
|
278
|
+
connect_proxy.daemon = True
|
|
279
|
+
return
|
|
280
|
+
|
|
281
|
+
worker_thread = connect_proxy._thread # pyright: ignore[reportAttributeAccessIssue]
|
|
282
|
+
if isinstance(worker_thread, Thread):
|
|
283
|
+
worker_thread.daemon = True
|
|
284
|
+
except Exception:
|
|
285
|
+
log_with_context(
|
|
286
|
+
logger,
|
|
287
|
+
logging.DEBUG,
|
|
288
|
+
"pool.connection.daemon.configure.error",
|
|
289
|
+
adapter=_ADAPTER_NAME,
|
|
290
|
+
pool_id=self._pool_id,
|
|
291
|
+
database=self._database_name,
|
|
292
|
+
)
|
|
293
|
+
|
|
294
|
+
async def _force_stop_connection(self, connection: AiosqlitePoolConnection, *, reason: str) -> None:
|
|
295
|
+
"""Force-stop aiosqlite worker thread when graceful close times out."""
|
|
296
|
+
try:
|
|
297
|
+
stop_method = connection.connection.stop # pyright: ignore[reportAttributeAccessIssue]
|
|
298
|
+
except Exception:
|
|
299
|
+
log_with_context(
|
|
300
|
+
logger,
|
|
301
|
+
logging.DEBUG,
|
|
302
|
+
"pool.connection.force_stop.unavailable",
|
|
303
|
+
adapter=_ADAPTER_NAME,
|
|
304
|
+
pool_id=self._pool_id,
|
|
305
|
+
connection_id=connection.id,
|
|
306
|
+
reason=reason,
|
|
307
|
+
)
|
|
308
|
+
return
|
|
309
|
+
|
|
310
|
+
try:
|
|
311
|
+
stop_result = stop_method()
|
|
312
|
+
if isawaitable(stop_result):
|
|
313
|
+
await asyncio.wait_for(stop_result, timeout=self._operation_timeout)
|
|
314
|
+
log_with_context(
|
|
315
|
+
logger,
|
|
316
|
+
logging.DEBUG,
|
|
317
|
+
"pool.connection.force_stop.success",
|
|
318
|
+
adapter=_ADAPTER_NAME,
|
|
319
|
+
pool_id=self._pool_id,
|
|
320
|
+
connection_id=connection.id,
|
|
321
|
+
reason=reason,
|
|
322
|
+
)
|
|
323
|
+
except asyncio.TimeoutError:
|
|
324
|
+
log_with_context(
|
|
325
|
+
logger,
|
|
326
|
+
logging.WARNING,
|
|
327
|
+
"pool.connection.force_stop.timeout",
|
|
328
|
+
adapter=_ADAPTER_NAME,
|
|
329
|
+
pool_id=self._pool_id,
|
|
330
|
+
connection_id=connection.id,
|
|
331
|
+
timeout_seconds=self._operation_timeout,
|
|
332
|
+
reason=reason,
|
|
333
|
+
)
|
|
334
|
+
except Exception as e:
|
|
335
|
+
log_with_context(
|
|
336
|
+
logger,
|
|
337
|
+
logging.WARNING,
|
|
338
|
+
"pool.connection.force_stop.error",
|
|
339
|
+
adapter=_ADAPTER_NAME,
|
|
340
|
+
pool_id=self._pool_id,
|
|
341
|
+
connection_id=connection.id,
|
|
342
|
+
reason=reason,
|
|
343
|
+
error=str(e),
|
|
344
|
+
)
|
|
345
|
+
|
|
346
|
+
def size(self) -> int:
|
|
347
|
+
"""Get total number of connections in pool.
|
|
348
|
+
|
|
349
|
+
Returns:
|
|
350
|
+
Total connection count
|
|
351
|
+
"""
|
|
352
|
+
return len(self._connection_registry)
|
|
353
|
+
|
|
354
|
+
def checked_out(self) -> int:
|
|
355
|
+
"""Get number of checked out connections.
|
|
356
|
+
|
|
357
|
+
Returns:
|
|
358
|
+
Number of connections currently in use
|
|
359
|
+
"""
|
|
360
|
+
if self._queue_instance is None:
|
|
361
|
+
return len(self._connection_registry)
|
|
362
|
+
return len(self._connection_registry) - self._queue.qsize()
|
|
363
|
+
|
|
364
|
+
async def _create_connection(self) -> AiosqlitePoolConnection:
|
|
365
|
+
"""Create a new connection.
|
|
366
|
+
|
|
367
|
+
Returns:
|
|
368
|
+
New pool connection instance
|
|
369
|
+
"""
|
|
370
|
+
connect_proxy = aiosqlite.connect(**self._connection_parameters)
|
|
371
|
+
self._set_connect_proxy_daemon(connect_proxy)
|
|
372
|
+
connection = await connect_proxy
|
|
373
|
+
|
|
374
|
+
database_path = str(self._connection_parameters.get("database", ""))
|
|
375
|
+
is_shared_cache = "cache=shared" in database_path
|
|
376
|
+
is_memory_db = ":memory:" in database_path or "mode=memory" in database_path
|
|
377
|
+
|
|
378
|
+
try:
|
|
379
|
+
if is_memory_db:
|
|
380
|
+
await connection.execute("PRAGMA journal_mode = MEMORY")
|
|
381
|
+
await connection.execute("PRAGMA synchronous = OFF")
|
|
382
|
+
await connection.execute("PRAGMA temp_store = MEMORY")
|
|
383
|
+
await connection.execute("PRAGMA cache_size = -16000")
|
|
384
|
+
else:
|
|
385
|
+
await connection.execute("PRAGMA journal_mode = WAL")
|
|
386
|
+
await connection.execute("PRAGMA synchronous = NORMAL")
|
|
387
|
+
|
|
388
|
+
await connection.execute("PRAGMA foreign_keys = ON")
|
|
389
|
+
await connection.execute("PRAGMA busy_timeout = 30000")
|
|
390
|
+
|
|
391
|
+
if is_shared_cache and is_memory_db:
|
|
392
|
+
await connection.execute("PRAGMA read_uncommitted = ON")
|
|
393
|
+
|
|
394
|
+
await connection.commit()
|
|
395
|
+
|
|
396
|
+
if is_shared_cache:
|
|
397
|
+
self._wal_initialized = True
|
|
398
|
+
|
|
399
|
+
except Exception:
|
|
400
|
+
log_with_context(
|
|
401
|
+
logger,
|
|
402
|
+
logging.WARNING,
|
|
403
|
+
"pool.connection.configure.error",
|
|
404
|
+
adapter=_ADAPTER_NAME,
|
|
405
|
+
pool_id=self._pool_id,
|
|
406
|
+
database=self._database_name,
|
|
407
|
+
)
|
|
408
|
+
await connection.execute("PRAGMA foreign_keys = ON")
|
|
409
|
+
await connection.execute("PRAGMA busy_timeout = 30000")
|
|
410
|
+
await connection.commit()
|
|
411
|
+
|
|
412
|
+
# Call user-provided callback after internal setup
|
|
413
|
+
if self._on_connection_create is not None:
|
|
414
|
+
await self._on_connection_create(connection)
|
|
415
|
+
|
|
416
|
+
pool_connection = AiosqlitePoolConnection(connection)
|
|
417
|
+
pool_connection.mark_as_idle()
|
|
418
|
+
|
|
419
|
+
async with self._lock:
|
|
420
|
+
self._connection_registry[pool_connection.id] = pool_connection
|
|
421
|
+
|
|
422
|
+
return pool_connection
|
|
423
|
+
|
|
424
|
+
async def _claim_if_healthy(self, connection: AiosqlitePoolConnection) -> bool:
|
|
425
|
+
"""Check if connection is healthy and claim it.
|
|
426
|
+
|
|
427
|
+
Uses passive health checks: connections idle less than health_check_interval
|
|
428
|
+
are assumed healthy based on their last known state. Active health checks
|
|
429
|
+
(SELECT 1) are only performed on long-idle connections.
|
|
430
|
+
|
|
431
|
+
Args:
|
|
432
|
+
connection: Connection to check and claim
|
|
433
|
+
|
|
434
|
+
Returns:
|
|
435
|
+
True if connection was claimed
|
|
436
|
+
"""
|
|
437
|
+
if connection.idle_time > self._idle_timeout:
|
|
438
|
+
await self._retire_connection(connection, reason="idle_timeout")
|
|
439
|
+
return False
|
|
440
|
+
|
|
441
|
+
if not connection.is_healthy:
|
|
442
|
+
await self._retire_connection(connection, reason="unhealthy")
|
|
443
|
+
return False
|
|
444
|
+
|
|
445
|
+
if connection.idle_time > self._health_check_interval:
|
|
446
|
+
try:
|
|
447
|
+
is_alive = await asyncio.wait_for(connection.is_alive(), timeout=self._operation_timeout)
|
|
448
|
+
if not is_alive:
|
|
449
|
+
await self._retire_connection(connection, reason="health_check_failed")
|
|
450
|
+
return False
|
|
451
|
+
except asyncio.TimeoutError:
|
|
452
|
+
await self._retire_connection(connection, reason="health_check_timeout")
|
|
453
|
+
return False
|
|
454
|
+
|
|
455
|
+
connection.mark_as_in_use()
|
|
456
|
+
return True
|
|
457
|
+
|
|
458
|
+
async def _retire_connection(self, connection: AiosqlitePoolConnection, *, reason: str | None = None) -> None:
|
|
459
|
+
"""Retire a connection from the pool.
|
|
460
|
+
|
|
461
|
+
Args:
|
|
462
|
+
connection: Connection to retire
|
|
463
|
+
reason: Optional reason for retirement
|
|
464
|
+
"""
|
|
465
|
+
if reason:
|
|
466
|
+
log_with_context(
|
|
467
|
+
logger,
|
|
468
|
+
logging.DEBUG,
|
|
469
|
+
"pool.connection.retire",
|
|
470
|
+
adapter=_ADAPTER_NAME,
|
|
471
|
+
pool_id=self._pool_id,
|
|
472
|
+
connection_id=connection.id,
|
|
473
|
+
reason=reason,
|
|
474
|
+
)
|
|
475
|
+
async with self._lock:
|
|
476
|
+
self._connection_registry.pop(connection.id, None)
|
|
477
|
+
|
|
478
|
+
try:
|
|
479
|
+
await asyncio.wait_for(connection.close(), timeout=self._operation_timeout)
|
|
480
|
+
except asyncio.TimeoutError:
|
|
481
|
+
log_with_context(
|
|
482
|
+
logger,
|
|
483
|
+
logging.WARNING,
|
|
484
|
+
"pool.connection.close.timeout",
|
|
485
|
+
adapter=_ADAPTER_NAME,
|
|
486
|
+
pool_id=self._pool_id,
|
|
487
|
+
connection_id=connection.id,
|
|
488
|
+
timeout_seconds=self._operation_timeout,
|
|
489
|
+
)
|
|
490
|
+
await self._force_stop_connection(connection, reason="retire_close_timeout")
|
|
491
|
+
|
|
492
|
+
async def _try_provision_new_connection(self) -> "AiosqlitePoolConnection | None":
|
|
493
|
+
"""Try to create a new connection if under capacity.
|
|
494
|
+
|
|
495
|
+
Returns:
|
|
496
|
+
New connection if successful, None if at capacity
|
|
497
|
+
"""
|
|
498
|
+
async with self._lock:
|
|
499
|
+
if len(self._connection_registry) >= self._pool_size:
|
|
500
|
+
return None
|
|
501
|
+
|
|
502
|
+
try:
|
|
503
|
+
connection = await self._create_connection()
|
|
504
|
+
except Exception:
|
|
505
|
+
log_with_context(
|
|
506
|
+
logger,
|
|
507
|
+
logging.WARNING,
|
|
508
|
+
"pool.connection.create.error",
|
|
509
|
+
adapter=_ADAPTER_NAME,
|
|
510
|
+
pool_id=self._pool_id,
|
|
511
|
+
database=self._database_name,
|
|
512
|
+
pool_size=len(self._connection_registry),
|
|
513
|
+
max_size=self._pool_size,
|
|
514
|
+
)
|
|
515
|
+
return None
|
|
516
|
+
else:
|
|
517
|
+
connection.mark_as_in_use()
|
|
518
|
+
return connection
|
|
519
|
+
|
|
520
|
+
async def _wait_for_healthy_connection(self) -> AiosqlitePoolConnection:
|
|
521
|
+
"""Wait for a healthy connection to become available.
|
|
522
|
+
|
|
523
|
+
Returns:
|
|
524
|
+
Available healthy connection
|
|
525
|
+
|
|
526
|
+
Raises:
|
|
527
|
+
AiosqlitePoolClosedError: If pool is closed while waiting
|
|
528
|
+
"""
|
|
529
|
+
while True:
|
|
530
|
+
get_connection_task = asyncio.create_task(self._queue.get())
|
|
531
|
+
pool_closed_task = asyncio.create_task(self._closed_event.wait())
|
|
532
|
+
|
|
533
|
+
done, pending = await asyncio.wait(
|
|
534
|
+
{get_connection_task, pool_closed_task}, return_when=asyncio.FIRST_COMPLETED
|
|
535
|
+
)
|
|
536
|
+
|
|
537
|
+
try:
|
|
538
|
+
if pool_closed_task in done:
|
|
539
|
+
msg = "Pool closed during connection acquisition"
|
|
540
|
+
raise AiosqlitePoolClosedError(msg)
|
|
541
|
+
|
|
542
|
+
connection = get_connection_task.result()
|
|
543
|
+
if await self._claim_if_healthy(connection):
|
|
544
|
+
return connection
|
|
545
|
+
|
|
546
|
+
finally:
|
|
547
|
+
for task in pending:
|
|
548
|
+
task.cancel()
|
|
549
|
+
with suppress(asyncio.CancelledError):
|
|
550
|
+
await task
|
|
551
|
+
|
|
552
|
+
async def _warm_pool(self) -> None:
|
|
553
|
+
"""Pre-create minimum connections for pool warming.
|
|
554
|
+
|
|
555
|
+
Creates connections up to min_size to avoid cold-start latency
|
|
556
|
+
on first requests.
|
|
557
|
+
"""
|
|
558
|
+
if self._warmed or self._min_size <= 0:
|
|
559
|
+
return
|
|
560
|
+
|
|
561
|
+
self._warmed = True
|
|
562
|
+
connections_needed = self._min_size - len(self._connection_registry)
|
|
563
|
+
|
|
564
|
+
if connections_needed <= 0:
|
|
565
|
+
return
|
|
566
|
+
|
|
567
|
+
log_with_context(
|
|
568
|
+
logger,
|
|
569
|
+
logging.DEBUG,
|
|
570
|
+
"pool.warmup.start",
|
|
571
|
+
adapter=_ADAPTER_NAME,
|
|
572
|
+
pool_id=self._pool_id,
|
|
573
|
+
database=self._database_name,
|
|
574
|
+
connections_needed=connections_needed,
|
|
575
|
+
min_size=self._min_size,
|
|
576
|
+
)
|
|
577
|
+
tasks = [self._create_connection() for _ in range(connections_needed)]
|
|
578
|
+
results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
579
|
+
|
|
580
|
+
for result in results:
|
|
581
|
+
if isinstance(result, AiosqlitePoolConnection):
|
|
582
|
+
self._queue.put_nowait(result)
|
|
583
|
+
elif isinstance(result, Exception):
|
|
584
|
+
log_with_context(
|
|
585
|
+
logger,
|
|
586
|
+
logging.WARNING,
|
|
587
|
+
"pool.warmup.connection.error",
|
|
588
|
+
adapter=_ADAPTER_NAME,
|
|
589
|
+
pool_id=self._pool_id,
|
|
590
|
+
error=str(result),
|
|
591
|
+
)
|
|
592
|
+
|
|
593
|
+
async def _get_connection(self) -> AiosqlitePoolConnection:
|
|
594
|
+
"""Run the three-phase connection acquisition cycle.
|
|
595
|
+
|
|
596
|
+
Returns:
|
|
597
|
+
Available connection
|
|
598
|
+
|
|
599
|
+
Raises:
|
|
600
|
+
AiosqlitePoolClosedError: If pool is closed
|
|
601
|
+
"""
|
|
602
|
+
# Fast path: check closed state directly to avoid property overhead
|
|
603
|
+
if self._closed_event_instance is not None and self._closed_event_instance.is_set():
|
|
604
|
+
msg = "Cannot acquire connection from closed pool"
|
|
605
|
+
raise AiosqlitePoolClosedError(msg)
|
|
606
|
+
|
|
607
|
+
if not self._warmed and self._min_size > 0:
|
|
608
|
+
await self._warm_pool()
|
|
609
|
+
|
|
610
|
+
# Fast path: try to get from queue without health check overhead for fresh connections
|
|
611
|
+
while not self._queue.empty():
|
|
612
|
+
connection = self._queue.get_nowait()
|
|
613
|
+
# Fast claim for recently-used connections (idle < health_check_interval)
|
|
614
|
+
if connection.idle_since is not None:
|
|
615
|
+
idle_time = time.time() - connection.idle_since
|
|
616
|
+
if idle_time <= self._health_check_interval and connection.is_healthy:
|
|
617
|
+
connection.idle_since = None # mark_as_in_use inline
|
|
618
|
+
return connection
|
|
619
|
+
# Fall back to full health check for older connections
|
|
620
|
+
if await self._claim_if_healthy(connection):
|
|
621
|
+
return connection
|
|
622
|
+
|
|
623
|
+
# Try to create new connection if under capacity
|
|
624
|
+
# Fast path: check capacity without lock first
|
|
625
|
+
if len(self._connection_registry) < self._pool_size:
|
|
626
|
+
new_connection = await self._try_provision_new_connection()
|
|
627
|
+
if new_connection is not None:
|
|
628
|
+
return new_connection
|
|
629
|
+
|
|
630
|
+
return await self._wait_for_healthy_connection()
|
|
631
|
+
|
|
632
|
+
async def acquire(self) -> AiosqlitePoolConnection:
|
|
633
|
+
"""Acquire a connection from the pool.
|
|
634
|
+
|
|
635
|
+
Returns:
|
|
636
|
+
Available connection
|
|
637
|
+
|
|
638
|
+
Raises:
|
|
639
|
+
AiosqliteConnectTimeoutError: If acquisition times out
|
|
640
|
+
"""
|
|
641
|
+
# Fast path: try to get connection without timeout wrapper
|
|
642
|
+
# Only use timeout when we need to wait for a connection
|
|
643
|
+
try:
|
|
644
|
+
connection = await self._get_connection()
|
|
645
|
+
except AiosqlitePoolClosedError:
|
|
646
|
+
raise
|
|
647
|
+
except Exception:
|
|
648
|
+
# If fast path fails, fall back to timeout-wrapped acquisition
|
|
649
|
+
try:
|
|
650
|
+
connection = await asyncio.wait_for(self._get_connection(), timeout=self._connect_timeout)
|
|
651
|
+
except asyncio.TimeoutError as e:
|
|
652
|
+
msg = f"Connection acquisition timed out after {self._connect_timeout}s"
|
|
653
|
+
raise AiosqliteConnectTimeoutError(msg) from e
|
|
654
|
+
|
|
655
|
+
if not self._wal_initialized and "cache=shared" in str(self._connection_parameters.get("database", "")):
|
|
656
|
+
await asyncio.sleep(0.01)
|
|
657
|
+
return connection
|
|
658
|
+
|
|
659
|
+
async def release(self, connection: AiosqlitePoolConnection) -> None:
|
|
660
|
+
"""Release a connection back to the pool.
|
|
661
|
+
|
|
662
|
+
Args:
|
|
663
|
+
connection: Connection to release
|
|
664
|
+
"""
|
|
665
|
+
# Fast path: check closed state directly
|
|
666
|
+
if self._closed_event_instance is not None and self._closed_event_instance.is_set():
|
|
667
|
+
await self._retire_connection(connection)
|
|
668
|
+
return
|
|
669
|
+
|
|
670
|
+
if connection.id not in self._connection_registry:
|
|
671
|
+
log_with_context(
|
|
672
|
+
logger,
|
|
673
|
+
logging.WARNING,
|
|
674
|
+
"pool.connection.release.unknown",
|
|
675
|
+
adapter=_ADAPTER_NAME,
|
|
676
|
+
pool_id=self._pool_id,
|
|
677
|
+
connection_id=connection.id,
|
|
678
|
+
)
|
|
679
|
+
return
|
|
680
|
+
|
|
681
|
+
try:
|
|
682
|
+
# Fast path: skip timeout wrapper for reset, just do the rollback directly
|
|
683
|
+
# The rollback itself is fast for SQLite; timeout is overkill for hot path
|
|
684
|
+
with suppress(Exception):
|
|
685
|
+
await connection.connection.rollback()
|
|
686
|
+
connection.idle_since = time.time() # mark_as_idle inline
|
|
687
|
+
self._queue.put_nowait(connection)
|
|
688
|
+
except Exception as e:
|
|
689
|
+
log_with_context(
|
|
690
|
+
logger,
|
|
691
|
+
logging.WARNING,
|
|
692
|
+
"pool.connection.reset.error",
|
|
693
|
+
adapter=_ADAPTER_NAME,
|
|
694
|
+
pool_id=self._pool_id,
|
|
695
|
+
connection_id=connection.id,
|
|
696
|
+
error=str(e),
|
|
697
|
+
)
|
|
698
|
+
connection.mark_unhealthy()
|
|
699
|
+
await self._retire_connection(connection)
|
|
700
|
+
|
|
701
|
+
def get_connection(self) -> "AiosqlitePoolConnectionContext":
|
|
702
|
+
"""Get a connection with automatic release."""
|
|
703
|
+
return AiosqlitePoolConnectionContext(self)
|
|
704
|
+
|
|
705
|
+
async def close(self) -> None:
|
|
706
|
+
"""Close the connection pool."""
|
|
707
|
+
if self.is_closed:
|
|
708
|
+
return
|
|
709
|
+
self._closed_event.set()
|
|
710
|
+
|
|
711
|
+
while not self._queue.empty():
|
|
712
|
+
self._queue.get_nowait()
|
|
713
|
+
|
|
714
|
+
async with self._lock:
|
|
715
|
+
connections = list(self._connection_registry.values())
|
|
716
|
+
self._connection_registry.clear()
|
|
717
|
+
|
|
718
|
+
if connections:
|
|
719
|
+
close_tasks = [asyncio.wait_for(conn.close(), timeout=self._operation_timeout) for conn in connections]
|
|
720
|
+
results = await asyncio.gather(*close_tasks, return_exceptions=True)
|
|
721
|
+
|
|
722
|
+
for i, result in enumerate(results):
|
|
723
|
+
if isinstance(result, Exception):
|
|
724
|
+
if isinstance(result, asyncio.TimeoutError):
|
|
725
|
+
await self._force_stop_connection(connections[i], reason="pool_close_timeout")
|
|
726
|
+
log_with_context(
|
|
727
|
+
logger,
|
|
728
|
+
logging.WARNING,
|
|
729
|
+
"pool.close.connection.error",
|
|
730
|
+
adapter=_ADAPTER_NAME,
|
|
731
|
+
pool_id=self._pool_id,
|
|
732
|
+
connection_id=connections[i].id,
|
|
733
|
+
error=str(result),
|
|
734
|
+
)
|