sqlspec 0.36.0__cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.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.
- ac8f31065839703b4e70__mypyc.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/__init__.py +140 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +315 -0
- sqlspec/_typing.py +700 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_typing.py +82 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +1273 -0
- sqlspec/adapters/adbc/config.py +295 -0
- sqlspec/adapters/adbc/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/core.py +735 -0
- sqlspec/adapters/adbc/data_dictionary.py +334 -0
- sqlspec/adapters/adbc/driver.py +529 -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 +502 -0
- sqlspec/adapters/adbc/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/type_converter.py +140 -0
- sqlspec/adapters/aiosqlite/__init__.py +25 -0
- sqlspec/adapters/aiosqlite/_typing.py +82 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +818 -0
- sqlspec/adapters/aiosqlite/config.py +334 -0
- sqlspec/adapters/aiosqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/aiosqlite/core.py +315 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +208 -0
- sqlspec/adapters/aiosqlite/driver.py +313 -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.py +533 -0
- sqlspec/adapters/asyncmy/__init__.py +21 -0
- sqlspec/adapters/asyncmy/_typing.py +87 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +703 -0
- sqlspec/adapters/asyncmy/config.py +302 -0
- sqlspec/adapters/asyncmy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncmy/core.py +360 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +124 -0
- sqlspec/adapters/asyncmy/driver.py +383 -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 +19 -0
- sqlspec/adapters/asyncpg/_typing.py +88 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +748 -0
- sqlspec/adapters/asyncpg/config.py +569 -0
- sqlspec/adapters/asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncpg/core.py +367 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +162 -0
- sqlspec/adapters/asyncpg/driver.py +487 -0
- sqlspec/adapters/asyncpg/events/__init__.py +6 -0
- sqlspec/adapters/asyncpg/events/backend.py +286 -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 +14 -0
- sqlspec/adapters/bigquery/_typing.py +86 -0
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +827 -0
- sqlspec/adapters/bigquery/config.py +353 -0
- sqlspec/adapters/bigquery/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/core.py +715 -0
- sqlspec/adapters/bigquery/data_dictionary.py +128 -0
- sqlspec/adapters/bigquery/driver.py +548 -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.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/type_converter.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/__init__.py +24 -0
- sqlspec/adapters/cockroach_asyncpg/_typing.py +72 -0
- sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/adk/store.py +410 -0
- sqlspec/adapters/cockroach_asyncpg/config.py +238 -0
- sqlspec/adapters/cockroach_asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
- sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/driver.py +144 -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 +38 -0
- sqlspec/adapters/cockroach_psycopg/_typing.py +129 -0
- sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
- sqlspec/adapters/cockroach_psycopg/adk/store.py +868 -0
- sqlspec/adapters/cockroach_psycopg/config.py +484 -0
- sqlspec/adapters/cockroach_psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_psycopg/core.py +63 -0
- sqlspec/adapters/cockroach_psycopg/data_dictionary.py +215 -0
- sqlspec/adapters/cockroach_psycopg/driver.py +284 -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 +325 -0
- sqlspec/adapters/duckdb/__init__.py +25 -0
- sqlspec/adapters/duckdb/_typing.py +81 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +850 -0
- sqlspec/adapters/duckdb/config.py +463 -0
- sqlspec/adapters/duckdb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/core.py +257 -0
- sqlspec/adapters/duckdb/data_dictionary.py +140 -0
- sqlspec/adapters/duckdb/driver.py +430 -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.py +293 -0
- sqlspec/adapters/duckdb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/type_converter.py +118 -0
- sqlspec/adapters/mock/__init__.py +72 -0
- sqlspec/adapters/mock/_typing.py +147 -0
- sqlspec/adapters/mock/config.py +483 -0
- sqlspec/adapters/mock/core.py +319 -0
- sqlspec/adapters/mock/data_dictionary.py +366 -0
- sqlspec/adapters/mock/driver.py +721 -0
- sqlspec/adapters/mysqlconnector/__init__.py +36 -0
- sqlspec/adapters/mysqlconnector/_typing.py +141 -0
- sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
- sqlspec/adapters/mysqlconnector/adk/store.py +1060 -0
- sqlspec/adapters/mysqlconnector/config.py +394 -0
- sqlspec/adapters/mysqlconnector/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/mysqlconnector/core.py +303 -0
- sqlspec/adapters/mysqlconnector/data_dictionary.py +235 -0
- sqlspec/adapters/mysqlconnector/driver.py +483 -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 +60 -0
- sqlspec/adapters/oracledb/_numpy_handlers.py +141 -0
- sqlspec/adapters/oracledb/_typing.py +182 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +166 -0
- sqlspec/adapters/oracledb/adk/__init__.py +10 -0
- sqlspec/adapters/oracledb/adk/store.py +2369 -0
- sqlspec/adapters/oracledb/config.py +550 -0
- sqlspec/adapters/oracledb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/core.py +543 -0
- sqlspec/adapters/oracledb/data_dictionary.py +536 -0
- sqlspec/adapters/oracledb/driver.py +1229 -0
- sqlspec/adapters/oracledb/events/__init__.py +16 -0
- sqlspec/adapters/oracledb/events/backend.py +347 -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 +535 -0
- sqlspec/adapters/oracledb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/type_converter.py +211 -0
- sqlspec/adapters/psqlpy/__init__.py +17 -0
- sqlspec/adapters/psqlpy/_typing.py +79 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +766 -0
- sqlspec/adapters/psqlpy/config.py +304 -0
- sqlspec/adapters/psqlpy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/core.py +480 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +126 -0
- sqlspec/adapters/psqlpy/driver.py +438 -0
- sqlspec/adapters/psqlpy/events/__init__.py +6 -0
- sqlspec/adapters/psqlpy/events/backend.py +310 -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.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/type_converter.py +113 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_typing.py +164 -0
- sqlspec/adapters/psycopg/adk/__init__.py +10 -0
- sqlspec/adapters/psycopg/adk/store.py +1387 -0
- sqlspec/adapters/psycopg/config.py +576 -0
- sqlspec/adapters/psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/core.py +450 -0
- sqlspec/adapters/psycopg/data_dictionary.py +289 -0
- sqlspec/adapters/psycopg/driver.py +975 -0
- sqlspec/adapters/psycopg/events/__init__.py +20 -0
- sqlspec/adapters/psycopg/events/backend.py +458 -0
- sqlspec/adapters/psycopg/events/store.py +42 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +552 -0
- sqlspec/adapters/psycopg/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/type_converter.py +93 -0
- sqlspec/adapters/pymysql/__init__.py +21 -0
- sqlspec/adapters/pymysql/_typing.py +71 -0
- sqlspec/adapters/pymysql/adk/__init__.py +5 -0
- sqlspec/adapters/pymysql/adk/store.py +540 -0
- sqlspec/adapters/pymysql/config.py +195 -0
- sqlspec/adapters/pymysql/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/pymysql/core.py +299 -0
- sqlspec/adapters/pymysql/data_dictionary.py +122 -0
- sqlspec/adapters/pymysql/driver.py +259 -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.py +137 -0
- sqlspec/adapters/spanner/__init__.py +40 -0
- sqlspec/adapters/spanner/_typing.py +86 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +732 -0
- sqlspec/adapters/spanner/config.py +352 -0
- sqlspec/adapters/spanner/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/core.py +188 -0
- sqlspec/adapters/spanner/data_dictionary.py +120 -0
- sqlspec/adapters/spanner/dialect/__init__.py +6 -0
- sqlspec/adapters/spanner/dialect/_spangres.py +57 -0
- sqlspec/adapters/spanner/dialect/_spanner.py +130 -0
- sqlspec/adapters/spanner/driver.py +373 -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.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/type_converter.py +331 -0
- sqlspec/adapters/sqlite/__init__.py +19 -0
- sqlspec/adapters/sqlite/_typing.py +80 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +958 -0
- sqlspec/adapters/sqlite/config.py +280 -0
- sqlspec/adapters/sqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/core.py +312 -0
- sqlspec/adapters/sqlite/data_dictionary.py +202 -0
- sqlspec/adapters/sqlite/driver.py +359 -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.py +198 -0
- sqlspec/adapters/sqlite/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/type_converter.py +114 -0
- sqlspec/base.py +747 -0
- sqlspec/builder/__init__.py +179 -0
- sqlspec/builder/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_base.py +1022 -0
- sqlspec/builder/_column.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_column.py +521 -0
- sqlspec/builder/_ddl.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_ddl.py +1642 -0
- sqlspec/builder/_delete.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_delete.py +95 -0
- sqlspec/builder/_dml.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_dml.py +365 -0
- sqlspec/builder/_explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_explain.py +579 -0
- sqlspec/builder/_expression_wrappers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_factory.py +1697 -0
- sqlspec/builder/_insert.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_insert.py +328 -0
- sqlspec/builder/_join.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_join.py +499 -0
- sqlspec/builder/_merge.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_merge.py +821 -0
- sqlspec/builder/_parsing_utils.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_parsing_utils.py +297 -0
- sqlspec/builder/_select.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_select.py +1660 -0
- sqlspec/builder/_temporal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_temporal.py +139 -0
- sqlspec/builder/_update.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_update.py +173 -0
- sqlspec/builder/_vector_expressions.py +267 -0
- sqlspec/cli.py +911 -0
- sqlspec/config.py +1755 -0
- sqlspec/core/__init__.py +374 -0
- sqlspec/core/_correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/_correlation.py +176 -0
- sqlspec/core/cache.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +1069 -0
- sqlspec/core/compiler.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +954 -0
- sqlspec/core/explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/explain.py +275 -0
- sqlspec/core/filters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +952 -0
- sqlspec/core/hashing.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +262 -0
- sqlspec/core/metrics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +71 -0
- sqlspec/core/parameters/_alignment.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_alignment.py +270 -0
- sqlspec/core/parameters/_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_converter.py +543 -0
- sqlspec/core/parameters/_processor.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_processor.py +505 -0
- sqlspec/core/parameters/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_registry.py +206 -0
- sqlspec/core/parameters/_transformers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_transformers.py +292 -0
- sqlspec/core/parameters/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_types.py +499 -0
- sqlspec/core/parameters/_validator.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_validator.py +180 -0
- sqlspec/core/pipeline.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/pipeline.py +319 -0
- sqlspec/core/query_modifiers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/query_modifiers.py +437 -0
- sqlspec/core/result/__init__.py +23 -0
- sqlspec/core/result/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_base.py +1121 -0
- sqlspec/core/result/_io.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_io.py +28 -0
- sqlspec/core/splitter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +966 -0
- sqlspec/core/stack.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/statement.py +1503 -0
- sqlspec/core/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/type_converter.py +339 -0
- sqlspec/data_dictionary/__init__.py +22 -0
- sqlspec/data_dictionary/_loader.py +123 -0
- sqlspec/data_dictionary/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_registry.py +74 -0
- sqlspec/data_dictionary/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_types.py +121 -0
- sqlspec/data_dictionary/dialects/__init__.py +21 -0
- sqlspec/data_dictionary/dialects/bigquery.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/bigquery.py +49 -0
- sqlspec/data_dictionary/dialects/cockroachdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/cockroachdb.py +43 -0
- sqlspec/data_dictionary/dialects/duckdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/duckdb.py +47 -0
- sqlspec/data_dictionary/dialects/mysql.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/mysql.py +42 -0
- sqlspec/data_dictionary/dialects/oracle.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/oracle.py +34 -0
- sqlspec/data_dictionary/dialects/postgres.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/postgres.py +46 -0
- sqlspec/data_dictionary/dialects/spanner.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/spanner.py +37 -0
- sqlspec/data_dictionary/dialects/sqlite.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/sqlite.py +42 -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/driver/__init__.py +32 -0
- sqlspec/driver/_async.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_async.py +1737 -0
- sqlspec/driver/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_common.py +1478 -0
- sqlspec/driver/_sql_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sql_helpers.py +148 -0
- sqlspec/driver/_storage_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_storage_helpers.py +144 -0
- sqlspec/driver/_sync.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sync.py +1710 -0
- sqlspec/exceptions.py +338 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +70 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/memory/__init__.py +69 -0
- sqlspec/extensions/adk/memory/_types.py +30 -0
- sqlspec/extensions/adk/memory/converters.py +149 -0
- sqlspec/extensions/adk/memory/service.py +217 -0
- sqlspec/extensions/adk/memory/store.py +569 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +246 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +225 -0
- sqlspec/extensions/adk/store.py +567 -0
- sqlspec/extensions/events/__init__.py +51 -0
- sqlspec/extensions/events/_channel.py +703 -0
- sqlspec/extensions/events/_hints.py +45 -0
- sqlspec/extensions/events/_models.py +23 -0
- sqlspec/extensions/events/_payload.py +69 -0
- sqlspec/extensions/events/_protocols.py +134 -0
- sqlspec/extensions/events/_queue.py +461 -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 +19 -0
- sqlspec/extensions/fastapi/extension.py +351 -0
- sqlspec/extensions/fastapi/providers.py +607 -0
- sqlspec/extensions/flask/__init__.py +37 -0
- sqlspec/extensions/flask/_state.py +76 -0
- sqlspec/extensions/flask/_utils.py +71 -0
- sqlspec/extensions/flask/extension.py +519 -0
- sqlspec/extensions/litestar/__init__.py +28 -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 +671 -0
- sqlspec/extensions/litestar/providers.py +526 -0
- sqlspec/extensions/litestar/store.py +296 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +113 -0
- sqlspec/extensions/starlette/__init__.py +19 -0
- sqlspec/extensions/starlette/_state.py +30 -0
- sqlspec/extensions/starlette/_utils.py +96 -0
- sqlspec/extensions/starlette/extension.py +346 -0
- sqlspec/extensions/starlette/middleware.py +235 -0
- sqlspec/loader.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/loader.py +702 -0
- sqlspec/migrations/__init__.py +36 -0
- sqlspec/migrations/base.py +731 -0
- sqlspec/migrations/commands.py +1232 -0
- sqlspec/migrations/context.py +157 -0
- sqlspec/migrations/fix.py +204 -0
- sqlspec/migrations/loaders.py +443 -0
- sqlspec/migrations/runner.py +1172 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.py +611 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.py +207 -0
- sqlspec/migrations/version.py +446 -0
- sqlspec/observability/__init__.py +55 -0
- sqlspec/observability/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_common.py +77 -0
- sqlspec/observability/_config.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_config.py +348 -0
- sqlspec/observability/_diagnostics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_diagnostics.py +74 -0
- sqlspec/observability/_dispatcher.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_dispatcher.py +152 -0
- sqlspec/observability/_formatters/__init__.py +13 -0
- sqlspec/observability/_formatters/_aws.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_aws.py +102 -0
- sqlspec/observability/_formatters/_azure.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_azure.py +96 -0
- sqlspec/observability/_formatters/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_base.py +57 -0
- sqlspec/observability/_formatters/_gcp.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_gcp.py +131 -0
- sqlspec/observability/_formatting.py +58 -0
- sqlspec/observability/_observer.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_observer.py +357 -0
- sqlspec/observability/_runtime.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_runtime.py +420 -0
- sqlspec/observability/_sampling.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_sampling.py +188 -0
- sqlspec/observability/_spans.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_spans.py +161 -0
- sqlspec/protocols.py +916 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +48 -0
- sqlspec/storage/_utils.py +104 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.py +253 -0
- sqlspec/storage/backends/fsspec.py +529 -0
- sqlspec/storage/backends/local.py +441 -0
- sqlspec/storage/backends/obstore.py +916 -0
- sqlspec/storage/errors.py +104 -0
- sqlspec/storage/pipeline.py +582 -0
- sqlspec/storage/registry.py +301 -0
- sqlspec/typing.py +395 -0
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/arrow_helpers.py +318 -0
- sqlspec/utils/config_tools.py +332 -0
- sqlspec/utils/correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/correlation.py +134 -0
- sqlspec/utils/deprecation.py +190 -0
- sqlspec/utils/fixtures.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/fixtures.py +258 -0
- sqlspec/utils/logging.py +222 -0
- sqlspec/utils/module_loader.py +306 -0
- sqlspec/utils/portal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/portal.py +375 -0
- sqlspec/utils/schema.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/schema.py +485 -0
- sqlspec/utils/serializers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/serializers.py +408 -0
- sqlspec/utils/singleton.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +311 -0
- sqlspec/utils/text.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +108 -0
- sqlspec/utils/type_converters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_converters.py +128 -0
- sqlspec/utils/type_guards.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_guards.py +1360 -0
- sqlspec/utils/uuids.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/uuids.py +225 -0
- sqlspec-0.36.0.dist-info/METADATA +205 -0
- sqlspec-0.36.0.dist-info/RECORD +531 -0
- sqlspec-0.36.0.dist-info/WHEEL +7 -0
- sqlspec-0.36.0.dist-info/entry_points.txt +2 -0
- sqlspec-0.36.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,781 @@
|
|
|
1
|
+
"""Oracle session store for Litestar integration."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from sqlspec.extensions.litestar.store import BaseSQLSpecStore
|
|
7
|
+
from sqlspec.utils.sync_tools import async_
|
|
8
|
+
from sqlspec.utils.type_guards import is_async_readable, is_readable
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from sqlspec.adapters.oracledb.config import OracleAsyncConfig, OracleSyncConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
ORACLE_SMALL_BLOB_LIMIT = 32000
|
|
15
|
+
|
|
16
|
+
__all__ = ("OracleAsyncStore", "OracleSyncStore")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _coerce_bytes_payload(value: object) -> bytes:
|
|
20
|
+
"""Coerce a payload into bytes for session storage."""
|
|
21
|
+
if value is None:
|
|
22
|
+
return b""
|
|
23
|
+
if isinstance(value, bytes):
|
|
24
|
+
return value
|
|
25
|
+
if isinstance(value, str):
|
|
26
|
+
return value.encode("utf-8")
|
|
27
|
+
return str(value).encode("utf-8")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
async def _read_blob_async(value: object) -> bytes:
|
|
31
|
+
"""Read LOB values from async connections into bytes."""
|
|
32
|
+
if is_async_readable(value):
|
|
33
|
+
return _coerce_bytes_payload(await value.read())
|
|
34
|
+
if is_readable(value):
|
|
35
|
+
return _coerce_bytes_payload(value.read())
|
|
36
|
+
return _coerce_bytes_payload(value)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _read_blob_sync(value: object) -> bytes:
|
|
40
|
+
"""Read LOB values from sync connections into bytes."""
|
|
41
|
+
if is_readable(value):
|
|
42
|
+
return _coerce_bytes_payload(value.read())
|
|
43
|
+
return _coerce_bytes_payload(value)
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class OracleAsyncStore(BaseSQLSpecStore["OracleAsyncConfig"]):
|
|
47
|
+
"""Oracle session store using async OracleDB driver.
|
|
48
|
+
|
|
49
|
+
Implements server-side session storage for Litestar using Oracle Database
|
|
50
|
+
via the async python-oracledb driver. Provides efficient session management with:
|
|
51
|
+
- Native async Oracle operations
|
|
52
|
+
- MERGE statement for atomic UPSERT
|
|
53
|
+
- Automatic expiration handling
|
|
54
|
+
- Efficient cleanup of expired sessions
|
|
55
|
+
- Optional In-Memory Column Store support (requires Oracle Database In-Memory license)
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
config: OracleAsyncConfig with extension_config["litestar"] settings.
|
|
59
|
+
|
|
60
|
+
Example:
|
|
61
|
+
from sqlspec.adapters.oracledb import OracleAsyncConfig
|
|
62
|
+
from sqlspec.adapters.oracledb.litestar.store import OracleAsyncStore
|
|
63
|
+
|
|
64
|
+
config = OracleAsyncConfig(
|
|
65
|
+
connection_config={"dsn": "oracle://..."},
|
|
66
|
+
extension_config={
|
|
67
|
+
"litestar": {
|
|
68
|
+
"session_table": "my_sessions",
|
|
69
|
+
"in_memory": True
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
)
|
|
73
|
+
store = OracleAsyncStore(config)
|
|
74
|
+
await store.create_table()
|
|
75
|
+
|
|
76
|
+
Notes:
|
|
77
|
+
Configuration is read from config.extension_config["litestar"]:
|
|
78
|
+
- session_table: Session table name (default: "litestar_session")
|
|
79
|
+
- in_memory: Enable INMEMORY PRIORITY HIGH clause (default: False, Oracle-specific)
|
|
80
|
+
|
|
81
|
+
When in_memory=True, the table is created with INMEMORY PRIORITY HIGH clause for
|
|
82
|
+
faster read operations. PRIORITY HIGH ensures the table is populated into the
|
|
83
|
+
In-Memory column store at database startup for immediate performance benefits.
|
|
84
|
+
This requires Oracle Database 12.1.0.2+ with the Database In-Memory option licensed.
|
|
85
|
+
If In-Memory is not available, the table creation will fail with ORA-00439 or ORA-62142.
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
__slots__ = ("_in_memory",)
|
|
89
|
+
|
|
90
|
+
def __init__(self, config: "OracleAsyncConfig") -> None:
|
|
91
|
+
"""Initialize Oracle session store.
|
|
92
|
+
|
|
93
|
+
Args:
|
|
94
|
+
config: OracleAsyncConfig instance.
|
|
95
|
+
|
|
96
|
+
Notes:
|
|
97
|
+
Configuration is read from config.extension_config["litestar"]:
|
|
98
|
+
- session_table: Session table name (default: "litestar_session")
|
|
99
|
+
- in_memory: Enable INMEMORY clause (default: False)
|
|
100
|
+
"""
|
|
101
|
+
super().__init__(config)
|
|
102
|
+
|
|
103
|
+
litestar_config = config.extension_config.get("litestar", {})
|
|
104
|
+
self._in_memory = bool(litestar_config.get("in_memory", False))
|
|
105
|
+
|
|
106
|
+
def _get_create_table_sql(self) -> str:
|
|
107
|
+
"""Get Oracle CREATE TABLE SQL with optimized schema.
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
SQL statement to create the sessions table with proper indexes.
|
|
111
|
+
|
|
112
|
+
Notes:
|
|
113
|
+
- Uses TIMESTAMP WITH TIME ZONE for timezone-aware expiration timestamps
|
|
114
|
+
- Index on expires_at for efficient cleanup queries
|
|
115
|
+
- BLOB type for data storage (Oracle native binary type)
|
|
116
|
+
- Audit columns (created_at, updated_at) help with debugging
|
|
117
|
+
- Table name is internally controlled, not user input (S608 suppressed)
|
|
118
|
+
- INMEMORY PRIORITY HIGH clause added when in_memory=True for faster reads
|
|
119
|
+
- HIGH priority ensures table population at database startup
|
|
120
|
+
"""
|
|
121
|
+
inmemory_clause = "INMEMORY PRIORITY HIGH" if self._in_memory else ""
|
|
122
|
+
return f"""
|
|
123
|
+
BEGIN
|
|
124
|
+
EXECUTE IMMEDIATE 'CREATE TABLE {self._table_name} (
|
|
125
|
+
session_id VARCHAR2(255) PRIMARY KEY,
|
|
126
|
+
data BLOB NOT NULL,
|
|
127
|
+
expires_at TIMESTAMP WITH TIME ZONE,
|
|
128
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
|
|
129
|
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL
|
|
130
|
+
) {inmemory_clause}';
|
|
131
|
+
EXCEPTION
|
|
132
|
+
WHEN OTHERS THEN
|
|
133
|
+
IF SQLCODE != -955 THEN
|
|
134
|
+
RAISE;
|
|
135
|
+
END IF;
|
|
136
|
+
END;
|
|
137
|
+
|
|
138
|
+
BEGIN
|
|
139
|
+
EXECUTE IMMEDIATE 'CREATE INDEX idx_{self._table_name}_expires_at
|
|
140
|
+
ON {self._table_name}(expires_at)';
|
|
141
|
+
EXCEPTION
|
|
142
|
+
WHEN OTHERS THEN
|
|
143
|
+
IF SQLCODE != -955 THEN
|
|
144
|
+
RAISE;
|
|
145
|
+
END IF;
|
|
146
|
+
END;
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
def _get_drop_table_sql(self) -> "list[str]":
|
|
150
|
+
"""Get Oracle DROP TABLE SQL with PL/SQL error handling.
|
|
151
|
+
|
|
152
|
+
Returns:
|
|
153
|
+
List of SQL statements with exception handling for non-existent objects.
|
|
154
|
+
"""
|
|
155
|
+
return [
|
|
156
|
+
f"""
|
|
157
|
+
BEGIN
|
|
158
|
+
EXECUTE IMMEDIATE 'DROP INDEX idx_{self._table_name}_expires_at';
|
|
159
|
+
EXCEPTION
|
|
160
|
+
WHEN OTHERS THEN
|
|
161
|
+
IF SQLCODE != -1418 THEN
|
|
162
|
+
RAISE;
|
|
163
|
+
END IF;
|
|
164
|
+
END;
|
|
165
|
+
""",
|
|
166
|
+
f"""
|
|
167
|
+
BEGIN
|
|
168
|
+
EXECUTE IMMEDIATE 'DROP TABLE {self._table_name}';
|
|
169
|
+
EXCEPTION
|
|
170
|
+
WHEN OTHERS THEN
|
|
171
|
+
IF SQLCODE != -942 THEN
|
|
172
|
+
RAISE;
|
|
173
|
+
END IF;
|
|
174
|
+
END;
|
|
175
|
+
""",
|
|
176
|
+
]
|
|
177
|
+
|
|
178
|
+
async def create_table(self) -> None:
|
|
179
|
+
"""Create the session table if it doesn't exist."""
|
|
180
|
+
sql = self._get_create_table_sql()
|
|
181
|
+
async with self._config.provide_session() as driver:
|
|
182
|
+
await driver.execute_script(sql)
|
|
183
|
+
|
|
184
|
+
self._log_table_created()
|
|
185
|
+
|
|
186
|
+
async def get(self, key: str, renew_for: "int | timedelta | None" = None) -> "bytes | None":
|
|
187
|
+
"""Get a session value by key.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
key: Session ID to retrieve.
|
|
191
|
+
renew_for: If given, renew the expiry time for this duration.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
Session data as bytes if found and not expired, None otherwise.
|
|
195
|
+
|
|
196
|
+
Notes:
|
|
197
|
+
Uses SYSTIMESTAMP for Oracle current timestamp.
|
|
198
|
+
The query uses the index for expires_at > SYSTIMESTAMP.
|
|
199
|
+
"""
|
|
200
|
+
sql = f"""
|
|
201
|
+
SELECT data, expires_at FROM {self._table_name}
|
|
202
|
+
WHERE session_id = :session_id
|
|
203
|
+
AND (expires_at IS NULL OR expires_at > SYSTIMESTAMP)
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
conn_context = self._config.provide_connection()
|
|
207
|
+
async with conn_context as conn:
|
|
208
|
+
cursor = conn.cursor()
|
|
209
|
+
await cursor.execute(sql, {"session_id": key})
|
|
210
|
+
row = await cursor.fetchone()
|
|
211
|
+
|
|
212
|
+
if row is None:
|
|
213
|
+
return None
|
|
214
|
+
|
|
215
|
+
data_blob, expires_at = row
|
|
216
|
+
|
|
217
|
+
if renew_for is not None and expires_at is not None:
|
|
218
|
+
new_expires_at = self._calculate_expires_at(renew_for)
|
|
219
|
+
if new_expires_at is not None:
|
|
220
|
+
update_sql = f"""
|
|
221
|
+
UPDATE {self._table_name}
|
|
222
|
+
SET expires_at = :expires_at, updated_at = SYSTIMESTAMP
|
|
223
|
+
WHERE session_id = :session_id
|
|
224
|
+
"""
|
|
225
|
+
await cursor.execute(update_sql, {"expires_at": new_expires_at, "session_id": key})
|
|
226
|
+
await conn.commit()
|
|
227
|
+
|
|
228
|
+
return await _read_blob_async(data_blob)
|
|
229
|
+
|
|
230
|
+
async def set(self, key: str, value: "str | bytes", expires_in: "int | timedelta | None" = None) -> None:
|
|
231
|
+
"""Store a session value.
|
|
232
|
+
|
|
233
|
+
Args:
|
|
234
|
+
key: Session ID.
|
|
235
|
+
value: Session data.
|
|
236
|
+
expires_in: Time until expiration.
|
|
237
|
+
|
|
238
|
+
Notes:
|
|
239
|
+
Uses MERGE for atomic UPSERT operation in Oracle.
|
|
240
|
+
Updates updated_at timestamp on every write for audit trail.
|
|
241
|
+
For large BLOBs, uses empty_blob() and then writes data separately.
|
|
242
|
+
"""
|
|
243
|
+
data = self._value_to_bytes(value)
|
|
244
|
+
expires_at = self._calculate_expires_at(expires_in)
|
|
245
|
+
|
|
246
|
+
conn_context = self._config.provide_connection()
|
|
247
|
+
async with conn_context as conn:
|
|
248
|
+
cursor = conn.cursor()
|
|
249
|
+
|
|
250
|
+
if len(data) > ORACLE_SMALL_BLOB_LIMIT:
|
|
251
|
+
merge_sql = f"""
|
|
252
|
+
MERGE INTO {self._table_name} t
|
|
253
|
+
USING (SELECT :session_id AS session_id FROM DUAL) s
|
|
254
|
+
ON (t.session_id = s.session_id)
|
|
255
|
+
WHEN MATCHED THEN
|
|
256
|
+
UPDATE SET
|
|
257
|
+
data = EMPTY_BLOB(),
|
|
258
|
+
expires_at = :expires_at,
|
|
259
|
+
updated_at = SYSTIMESTAMP
|
|
260
|
+
WHEN NOT MATCHED THEN
|
|
261
|
+
INSERT (session_id, data, expires_at, created_at, updated_at)
|
|
262
|
+
VALUES (:session_id, EMPTY_BLOB(), :expires_at, SYSTIMESTAMP, SYSTIMESTAMP)
|
|
263
|
+
"""
|
|
264
|
+
await cursor.execute(merge_sql, {"session_id": key, "expires_at": expires_at})
|
|
265
|
+
|
|
266
|
+
select_sql = f"""
|
|
267
|
+
SELECT data FROM {self._table_name}
|
|
268
|
+
WHERE session_id = :session_id FOR UPDATE
|
|
269
|
+
"""
|
|
270
|
+
await cursor.execute(select_sql, {"session_id": key})
|
|
271
|
+
row = await cursor.fetchone()
|
|
272
|
+
if row:
|
|
273
|
+
blob = row[0]
|
|
274
|
+
await blob.write(data)
|
|
275
|
+
|
|
276
|
+
await conn.commit()
|
|
277
|
+
else:
|
|
278
|
+
sql = f"""
|
|
279
|
+
MERGE INTO {self._table_name} t
|
|
280
|
+
USING (SELECT :session_id AS session_id FROM DUAL) s
|
|
281
|
+
ON (t.session_id = s.session_id)
|
|
282
|
+
WHEN MATCHED THEN
|
|
283
|
+
UPDATE SET
|
|
284
|
+
data = :data,
|
|
285
|
+
expires_at = :expires_at,
|
|
286
|
+
updated_at = SYSTIMESTAMP
|
|
287
|
+
WHEN NOT MATCHED THEN
|
|
288
|
+
INSERT (session_id, data, expires_at, created_at, updated_at)
|
|
289
|
+
VALUES (:session_id, :data, :expires_at, SYSTIMESTAMP, SYSTIMESTAMP)
|
|
290
|
+
"""
|
|
291
|
+
await cursor.execute(sql, {"session_id": key, "data": data, "expires_at": expires_at})
|
|
292
|
+
await conn.commit()
|
|
293
|
+
|
|
294
|
+
async def delete(self, key: str) -> None:
|
|
295
|
+
"""Delete a session by key.
|
|
296
|
+
|
|
297
|
+
Args:
|
|
298
|
+
key: Session ID to delete.
|
|
299
|
+
"""
|
|
300
|
+
sql = f"DELETE FROM {self._table_name} WHERE session_id = :session_id"
|
|
301
|
+
|
|
302
|
+
conn_context = self._config.provide_connection()
|
|
303
|
+
async with conn_context as conn:
|
|
304
|
+
cursor = conn.cursor()
|
|
305
|
+
await cursor.execute(sql, {"session_id": key})
|
|
306
|
+
await conn.commit()
|
|
307
|
+
|
|
308
|
+
async def delete_all(self) -> None:
|
|
309
|
+
"""Delete all sessions from the store."""
|
|
310
|
+
sql = f"DELETE FROM {self._table_name}"
|
|
311
|
+
|
|
312
|
+
conn_context = self._config.provide_connection()
|
|
313
|
+
async with conn_context as conn:
|
|
314
|
+
cursor = conn.cursor()
|
|
315
|
+
await cursor.execute(sql)
|
|
316
|
+
await conn.commit()
|
|
317
|
+
self._log_delete_all()
|
|
318
|
+
|
|
319
|
+
async def exists(self, key: str) -> bool:
|
|
320
|
+
"""Check if a session key exists and is not expired.
|
|
321
|
+
|
|
322
|
+
Args:
|
|
323
|
+
key: Session ID to check.
|
|
324
|
+
|
|
325
|
+
Returns:
|
|
326
|
+
True if the session exists and is not expired.
|
|
327
|
+
|
|
328
|
+
Notes:
|
|
329
|
+
Uses SYSTIMESTAMP for consistency with get() method.
|
|
330
|
+
"""
|
|
331
|
+
sql = f"""
|
|
332
|
+
SELECT 1 FROM {self._table_name}
|
|
333
|
+
WHERE session_id = :session_id
|
|
334
|
+
AND (expires_at IS NULL OR expires_at > SYSTIMESTAMP)
|
|
335
|
+
"""
|
|
336
|
+
|
|
337
|
+
conn_context = self._config.provide_connection()
|
|
338
|
+
async with conn_context as conn:
|
|
339
|
+
cursor = conn.cursor()
|
|
340
|
+
await cursor.execute(sql, {"session_id": key})
|
|
341
|
+
result = await cursor.fetchone()
|
|
342
|
+
return result is not None
|
|
343
|
+
|
|
344
|
+
async def expires_in(self, key: str) -> "int | None":
|
|
345
|
+
"""Get the time in seconds until the session expires.
|
|
346
|
+
|
|
347
|
+
Args:
|
|
348
|
+
key: Session ID to check.
|
|
349
|
+
|
|
350
|
+
Returns:
|
|
351
|
+
Seconds until expiration, or None if no expiry or key doesn't exist.
|
|
352
|
+
"""
|
|
353
|
+
sql = f"""
|
|
354
|
+
SELECT expires_at FROM {self._table_name}
|
|
355
|
+
WHERE session_id = :session_id
|
|
356
|
+
"""
|
|
357
|
+
|
|
358
|
+
conn_context = self._config.provide_connection()
|
|
359
|
+
async with conn_context as conn:
|
|
360
|
+
cursor = conn.cursor()
|
|
361
|
+
await cursor.execute(sql, {"session_id": key})
|
|
362
|
+
row = await cursor.fetchone()
|
|
363
|
+
|
|
364
|
+
if row is None or row[0] is None:
|
|
365
|
+
return None
|
|
366
|
+
|
|
367
|
+
expires_at = row[0]
|
|
368
|
+
|
|
369
|
+
if expires_at.tzinfo is None:
|
|
370
|
+
expires_at = expires_at.replace(tzinfo=timezone.utc)
|
|
371
|
+
|
|
372
|
+
now = datetime.now(timezone.utc)
|
|
373
|
+
|
|
374
|
+
if expires_at <= now:
|
|
375
|
+
return 0
|
|
376
|
+
|
|
377
|
+
delta = expires_at - now
|
|
378
|
+
return int(delta.total_seconds())
|
|
379
|
+
|
|
380
|
+
async def delete_expired(self) -> int:
|
|
381
|
+
"""Delete all expired sessions.
|
|
382
|
+
|
|
383
|
+
Returns:
|
|
384
|
+
Number of sessions deleted.
|
|
385
|
+
|
|
386
|
+
Notes:
|
|
387
|
+
Uses SYSTIMESTAMP for consistency.
|
|
388
|
+
Oracle automatically commits DDL, so we explicitly commit for DML.
|
|
389
|
+
"""
|
|
390
|
+
sql = f"DELETE FROM {self._table_name} WHERE expires_at <= SYSTIMESTAMP"
|
|
391
|
+
|
|
392
|
+
conn_context = self._config.provide_connection()
|
|
393
|
+
async with conn_context as conn:
|
|
394
|
+
cursor = conn.cursor()
|
|
395
|
+
await cursor.execute(sql)
|
|
396
|
+
count = cursor.rowcount if cursor.rowcount is not None else 0
|
|
397
|
+
await conn.commit()
|
|
398
|
+
if count > 0:
|
|
399
|
+
self._log_delete_expired(count)
|
|
400
|
+
return count
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
class OracleSyncStore(BaseSQLSpecStore["OracleSyncConfig"]):
|
|
404
|
+
"""Oracle session store using sync OracleDB driver.
|
|
405
|
+
|
|
406
|
+
Implements server-side session storage for Litestar using Oracle Database
|
|
407
|
+
via the synchronous python-oracledb driver. Uses async_() wrapper to provide
|
|
408
|
+
an async interface compatible with the Store protocol.
|
|
409
|
+
|
|
410
|
+
Provides efficient session management with:
|
|
411
|
+
- Sync operations wrapped for async compatibility
|
|
412
|
+
- MERGE statement for atomic UPSERT
|
|
413
|
+
- Automatic expiration handling
|
|
414
|
+
- Efficient cleanup of expired sessions
|
|
415
|
+
- Optional In-Memory Column Store support (requires Oracle Database In-Memory license)
|
|
416
|
+
|
|
417
|
+
Note:
|
|
418
|
+
For high-concurrency applications, consider using OracleAsyncStore instead,
|
|
419
|
+
as it provides native async operations without threading overhead.
|
|
420
|
+
|
|
421
|
+
Args:
|
|
422
|
+
config: OracleSyncConfig with extension_config["litestar"] settings.
|
|
423
|
+
|
|
424
|
+
Example:
|
|
425
|
+
from sqlspec.adapters.oracledb import OracleSyncConfig
|
|
426
|
+
from sqlspec.adapters.oracledb.litestar.store import OracleSyncStore
|
|
427
|
+
|
|
428
|
+
config = OracleSyncConfig(
|
|
429
|
+
connection_config={"dsn": "oracle://..."},
|
|
430
|
+
extension_config={
|
|
431
|
+
"litestar": {
|
|
432
|
+
"session_table": "my_sessions",
|
|
433
|
+
"in_memory": True
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
)
|
|
437
|
+
store = OracleSyncStore(config)
|
|
438
|
+
await store.create_table()
|
|
439
|
+
|
|
440
|
+
Notes:
|
|
441
|
+
Configuration is read from config.extension_config["litestar"]:
|
|
442
|
+
- session_table: Session table name (default: "litestar_session")
|
|
443
|
+
- in_memory: Enable INMEMORY PRIORITY HIGH clause (default: False, Oracle-specific)
|
|
444
|
+
|
|
445
|
+
When in_memory=True, the table is created with INMEMORY PRIORITY HIGH clause for
|
|
446
|
+
faster read operations. PRIORITY HIGH ensures the table is populated into the
|
|
447
|
+
In-Memory column store at database startup for immediate performance benefits.
|
|
448
|
+
This requires Oracle Database 12.1.0.2+ with the Database In-Memory option licensed.
|
|
449
|
+
If In-Memory is not available, the table creation will fail with ORA-00439 or ORA-62142.
|
|
450
|
+
"""
|
|
451
|
+
|
|
452
|
+
__slots__ = ("_in_memory",)
|
|
453
|
+
|
|
454
|
+
def __init__(self, config: "OracleSyncConfig") -> None:
|
|
455
|
+
"""Initialize Oracle sync session store.
|
|
456
|
+
|
|
457
|
+
Args:
|
|
458
|
+
config: OracleSyncConfig instance.
|
|
459
|
+
|
|
460
|
+
Notes:
|
|
461
|
+
Configuration is read from config.extension_config["litestar"]:
|
|
462
|
+
- session_table: Session table name (default: "litestar_session")
|
|
463
|
+
- in_memory: Enable INMEMORY clause (default: False)
|
|
464
|
+
"""
|
|
465
|
+
super().__init__(config)
|
|
466
|
+
|
|
467
|
+
litestar_config = config.extension_config.get("litestar", {})
|
|
468
|
+
self._in_memory = bool(litestar_config.get("in_memory", False))
|
|
469
|
+
|
|
470
|
+
def _get_create_table_sql(self) -> str:
|
|
471
|
+
"""Get Oracle CREATE TABLE SQL with optimized schema.
|
|
472
|
+
|
|
473
|
+
Returns:
|
|
474
|
+
SQL statement to create the sessions table with proper indexes.
|
|
475
|
+
|
|
476
|
+
Notes:
|
|
477
|
+
- Uses TIMESTAMP WITH TIME ZONE for timezone-aware expiration timestamps
|
|
478
|
+
- Index on expires_at for efficient cleanup queries
|
|
479
|
+
- BLOB type for data storage (Oracle native binary type)
|
|
480
|
+
- Audit columns (created_at, updated_at) help with debugging
|
|
481
|
+
- Table name is internally controlled, not user input (S608 suppressed)
|
|
482
|
+
- INMEMORY PRIORITY HIGH clause added when in_memory=True for faster reads
|
|
483
|
+
- HIGH priority ensures table population at database startup
|
|
484
|
+
"""
|
|
485
|
+
inmemory_clause = "INMEMORY PRIORITY HIGH" if self._in_memory else ""
|
|
486
|
+
return f"""
|
|
487
|
+
BEGIN
|
|
488
|
+
EXECUTE IMMEDIATE 'CREATE TABLE {self._table_name} (
|
|
489
|
+
session_id VARCHAR2(255) PRIMARY KEY,
|
|
490
|
+
data BLOB NOT NULL,
|
|
491
|
+
expires_at TIMESTAMP WITH TIME ZONE,
|
|
492
|
+
created_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL,
|
|
493
|
+
updated_at TIMESTAMP WITH TIME ZONE DEFAULT SYSTIMESTAMP NOT NULL
|
|
494
|
+
) {inmemory_clause}';
|
|
495
|
+
EXCEPTION
|
|
496
|
+
WHEN OTHERS THEN
|
|
497
|
+
IF SQLCODE != -955 THEN
|
|
498
|
+
RAISE;
|
|
499
|
+
END IF;
|
|
500
|
+
END;
|
|
501
|
+
|
|
502
|
+
BEGIN
|
|
503
|
+
EXECUTE IMMEDIATE 'CREATE INDEX idx_{self._table_name}_expires_at
|
|
504
|
+
ON {self._table_name}(expires_at)';
|
|
505
|
+
EXCEPTION
|
|
506
|
+
WHEN OTHERS THEN
|
|
507
|
+
IF SQLCODE != -955 THEN
|
|
508
|
+
RAISE;
|
|
509
|
+
END IF;
|
|
510
|
+
END;
|
|
511
|
+
"""
|
|
512
|
+
|
|
513
|
+
def _get_drop_table_sql(self) -> "list[str]":
|
|
514
|
+
"""Get Oracle DROP TABLE SQL with PL/SQL error handling.
|
|
515
|
+
|
|
516
|
+
Returns:
|
|
517
|
+
List of SQL statements with exception handling for non-existent objects.
|
|
518
|
+
"""
|
|
519
|
+
return [
|
|
520
|
+
f"""
|
|
521
|
+
BEGIN
|
|
522
|
+
EXECUTE IMMEDIATE 'DROP INDEX idx_{self._table_name}_expires_at';
|
|
523
|
+
EXCEPTION
|
|
524
|
+
WHEN OTHERS THEN
|
|
525
|
+
IF SQLCODE != -1418 THEN
|
|
526
|
+
RAISE;
|
|
527
|
+
END IF;
|
|
528
|
+
END;
|
|
529
|
+
""",
|
|
530
|
+
f"""
|
|
531
|
+
BEGIN
|
|
532
|
+
EXECUTE IMMEDIATE 'DROP TABLE {self._table_name}';
|
|
533
|
+
EXCEPTION
|
|
534
|
+
WHEN OTHERS THEN
|
|
535
|
+
IF SQLCODE != -942 THEN
|
|
536
|
+
RAISE;
|
|
537
|
+
END IF;
|
|
538
|
+
END;
|
|
539
|
+
""",
|
|
540
|
+
]
|
|
541
|
+
|
|
542
|
+
def _create_table(self) -> None:
|
|
543
|
+
"""Synchronous implementation of create_table."""
|
|
544
|
+
sql = self._get_create_table_sql()
|
|
545
|
+
with self._config.provide_session() as driver:
|
|
546
|
+
driver.execute_script(sql)
|
|
547
|
+
|
|
548
|
+
self._log_table_created()
|
|
549
|
+
|
|
550
|
+
async def create_table(self) -> None:
|
|
551
|
+
"""Create the session table if it doesn't exist."""
|
|
552
|
+
await async_(self._create_table)()
|
|
553
|
+
|
|
554
|
+
def _get(self, key: str, renew_for: "int | timedelta | None" = None) -> "bytes | None":
|
|
555
|
+
"""Synchronous implementation of get.
|
|
556
|
+
|
|
557
|
+
Notes:
|
|
558
|
+
Uses SYSTIMESTAMP for Oracle current timestamp.
|
|
559
|
+
"""
|
|
560
|
+
sql = f"""
|
|
561
|
+
SELECT data, expires_at FROM {self._table_name}
|
|
562
|
+
WHERE session_id = :session_id
|
|
563
|
+
AND (expires_at IS NULL OR expires_at > SYSTIMESTAMP)
|
|
564
|
+
"""
|
|
565
|
+
|
|
566
|
+
with self._config.provide_connection() as conn:
|
|
567
|
+
cursor = conn.cursor()
|
|
568
|
+
cursor.execute(sql, {"session_id": key})
|
|
569
|
+
row = cursor.fetchone()
|
|
570
|
+
|
|
571
|
+
if row is None:
|
|
572
|
+
return None
|
|
573
|
+
|
|
574
|
+
data_blob, expires_at = row
|
|
575
|
+
|
|
576
|
+
if renew_for is not None and expires_at is not None:
|
|
577
|
+
new_expires_at = self._calculate_expires_at(renew_for)
|
|
578
|
+
if new_expires_at is not None:
|
|
579
|
+
update_sql = f"""
|
|
580
|
+
UPDATE {self._table_name}
|
|
581
|
+
SET expires_at = :expires_at, updated_at = SYSTIMESTAMP
|
|
582
|
+
WHERE session_id = :session_id
|
|
583
|
+
"""
|
|
584
|
+
cursor.execute(update_sql, {"expires_at": new_expires_at, "session_id": key})
|
|
585
|
+
conn.commit()
|
|
586
|
+
|
|
587
|
+
return _read_blob_sync(data_blob)
|
|
588
|
+
|
|
589
|
+
async def get(self, key: str, renew_for: "int | timedelta | None" = None) -> "bytes | None":
|
|
590
|
+
"""Get a session value by key.
|
|
591
|
+
|
|
592
|
+
Args:
|
|
593
|
+
key: Session ID to retrieve.
|
|
594
|
+
renew_for: If given, renew the expiry time for this duration.
|
|
595
|
+
|
|
596
|
+
Returns:
|
|
597
|
+
Session data as bytes if found and not expired, None otherwise.
|
|
598
|
+
"""
|
|
599
|
+
return await async_(self._get)(key, renew_for)
|
|
600
|
+
|
|
601
|
+
def _set(self, key: str, value: "str | bytes", expires_in: "int | timedelta | None" = None) -> None:
|
|
602
|
+
"""Synchronous implementation of set.
|
|
603
|
+
|
|
604
|
+
Notes:
|
|
605
|
+
Uses MERGE for atomic UPSERT operation in Oracle.
|
|
606
|
+
"""
|
|
607
|
+
data = self._value_to_bytes(value)
|
|
608
|
+
expires_at = self._calculate_expires_at(expires_in)
|
|
609
|
+
|
|
610
|
+
with self._config.provide_connection() as conn:
|
|
611
|
+
cursor = conn.cursor()
|
|
612
|
+
|
|
613
|
+
if len(data) > ORACLE_SMALL_BLOB_LIMIT:
|
|
614
|
+
merge_sql = f"""
|
|
615
|
+
MERGE INTO {self._table_name} t
|
|
616
|
+
USING (SELECT :session_id AS session_id FROM DUAL) s
|
|
617
|
+
ON (t.session_id = s.session_id)
|
|
618
|
+
WHEN MATCHED THEN
|
|
619
|
+
UPDATE SET
|
|
620
|
+
data = EMPTY_BLOB(),
|
|
621
|
+
expires_at = :expires_at,
|
|
622
|
+
updated_at = SYSTIMESTAMP
|
|
623
|
+
WHEN NOT MATCHED THEN
|
|
624
|
+
INSERT (session_id, data, expires_at, created_at, updated_at)
|
|
625
|
+
VALUES (:session_id, EMPTY_BLOB(), :expires_at, SYSTIMESTAMP, SYSTIMESTAMP)
|
|
626
|
+
"""
|
|
627
|
+
cursor.execute(merge_sql, {"session_id": key, "expires_at": expires_at})
|
|
628
|
+
|
|
629
|
+
select_sql = f"""
|
|
630
|
+
SELECT data FROM {self._table_name}
|
|
631
|
+
WHERE session_id = :session_id FOR UPDATE
|
|
632
|
+
"""
|
|
633
|
+
cursor.execute(select_sql, {"session_id": key})
|
|
634
|
+
row = cursor.fetchone()
|
|
635
|
+
if row:
|
|
636
|
+
blob = row[0]
|
|
637
|
+
blob.write(data)
|
|
638
|
+
|
|
639
|
+
conn.commit()
|
|
640
|
+
else:
|
|
641
|
+
sql = f"""
|
|
642
|
+
MERGE INTO {self._table_name} t
|
|
643
|
+
USING (SELECT :session_id AS session_id FROM DUAL) s
|
|
644
|
+
ON (t.session_id = s.session_id)
|
|
645
|
+
WHEN MATCHED THEN
|
|
646
|
+
UPDATE SET
|
|
647
|
+
data = :data,
|
|
648
|
+
expires_at = :expires_at,
|
|
649
|
+
updated_at = SYSTIMESTAMP
|
|
650
|
+
WHEN NOT MATCHED THEN
|
|
651
|
+
INSERT (session_id, data, expires_at, created_at, updated_at)
|
|
652
|
+
VALUES (:session_id, :data, :expires_at, SYSTIMESTAMP, SYSTIMESTAMP)
|
|
653
|
+
"""
|
|
654
|
+
cursor.execute(sql, {"session_id": key, "data": data, "expires_at": expires_at})
|
|
655
|
+
conn.commit()
|
|
656
|
+
|
|
657
|
+
async def set(self, key: str, value: "str | bytes", expires_in: "int | timedelta | None" = None) -> None:
|
|
658
|
+
"""Store a session value.
|
|
659
|
+
|
|
660
|
+
Args:
|
|
661
|
+
key: Session ID.
|
|
662
|
+
value: Session data.
|
|
663
|
+
expires_in: Time until expiration.
|
|
664
|
+
"""
|
|
665
|
+
await async_(self._set)(key, value, expires_in)
|
|
666
|
+
|
|
667
|
+
def _delete(self, key: str) -> None:
|
|
668
|
+
"""Synchronous implementation of delete."""
|
|
669
|
+
sql = f"DELETE FROM {self._table_name} WHERE session_id = :session_id"
|
|
670
|
+
|
|
671
|
+
with self._config.provide_connection() as conn:
|
|
672
|
+
cursor = conn.cursor()
|
|
673
|
+
cursor.execute(sql, {"session_id": key})
|
|
674
|
+
conn.commit()
|
|
675
|
+
|
|
676
|
+
async def delete(self, key: str) -> None:
|
|
677
|
+
"""Delete a session by key.
|
|
678
|
+
|
|
679
|
+
Args:
|
|
680
|
+
key: Session ID to delete.
|
|
681
|
+
"""
|
|
682
|
+
await async_(self._delete)(key)
|
|
683
|
+
|
|
684
|
+
def _delete_all(self) -> None:
|
|
685
|
+
"""Synchronous implementation of delete_all."""
|
|
686
|
+
sql = f"DELETE FROM {self._table_name}"
|
|
687
|
+
|
|
688
|
+
with self._config.provide_connection() as conn:
|
|
689
|
+
cursor = conn.cursor()
|
|
690
|
+
cursor.execute(sql)
|
|
691
|
+
conn.commit()
|
|
692
|
+
self._log_delete_all()
|
|
693
|
+
|
|
694
|
+
async def delete_all(self) -> None:
|
|
695
|
+
"""Delete all sessions from the store."""
|
|
696
|
+
await async_(self._delete_all)()
|
|
697
|
+
|
|
698
|
+
def _exists(self, key: str) -> bool:
|
|
699
|
+
"""Synchronous implementation of exists."""
|
|
700
|
+
sql = f"""
|
|
701
|
+
SELECT 1 FROM {self._table_name}
|
|
702
|
+
WHERE session_id = :session_id
|
|
703
|
+
AND (expires_at IS NULL OR expires_at > SYSTIMESTAMP)
|
|
704
|
+
"""
|
|
705
|
+
|
|
706
|
+
with self._config.provide_connection() as conn:
|
|
707
|
+
cursor = conn.cursor()
|
|
708
|
+
cursor.execute(sql, {"session_id": key})
|
|
709
|
+
result = cursor.fetchone()
|
|
710
|
+
return result is not None
|
|
711
|
+
|
|
712
|
+
async def exists(self, key: str) -> bool:
|
|
713
|
+
"""Check if a session key exists and is not expired.
|
|
714
|
+
|
|
715
|
+
Args:
|
|
716
|
+
key: Session ID to check.
|
|
717
|
+
|
|
718
|
+
Returns:
|
|
719
|
+
True if the session exists and is not expired.
|
|
720
|
+
"""
|
|
721
|
+
return await async_(self._exists)(key)
|
|
722
|
+
|
|
723
|
+
def _expires_in(self, key: str) -> "int | None":
|
|
724
|
+
"""Synchronous implementation of expires_in."""
|
|
725
|
+
sql = f"""
|
|
726
|
+
SELECT expires_at FROM {self._table_name}
|
|
727
|
+
WHERE session_id = :session_id
|
|
728
|
+
"""
|
|
729
|
+
|
|
730
|
+
with self._config.provide_connection() as conn:
|
|
731
|
+
cursor = conn.cursor()
|
|
732
|
+
cursor.execute(sql, {"session_id": key})
|
|
733
|
+
row = cursor.fetchone()
|
|
734
|
+
|
|
735
|
+
if row is None or row[0] is None:
|
|
736
|
+
return None
|
|
737
|
+
|
|
738
|
+
expires_at = row[0]
|
|
739
|
+
|
|
740
|
+
if expires_at.tzinfo is None:
|
|
741
|
+
expires_at = expires_at.replace(tzinfo=timezone.utc)
|
|
742
|
+
|
|
743
|
+
now = datetime.now(timezone.utc)
|
|
744
|
+
|
|
745
|
+
if expires_at <= now:
|
|
746
|
+
return 0
|
|
747
|
+
|
|
748
|
+
delta = expires_at - now
|
|
749
|
+
return int(delta.total_seconds())
|
|
750
|
+
|
|
751
|
+
async def expires_in(self, key: str) -> "int | None":
|
|
752
|
+
"""Get the time in seconds until the session expires.
|
|
753
|
+
|
|
754
|
+
Args:
|
|
755
|
+
key: Session ID to check.
|
|
756
|
+
|
|
757
|
+
Returns:
|
|
758
|
+
Seconds until expiration, or None if no expiry or key doesn't exist.
|
|
759
|
+
"""
|
|
760
|
+
return await async_(self._expires_in)(key)
|
|
761
|
+
|
|
762
|
+
def _delete_expired(self) -> int:
|
|
763
|
+
"""Synchronous implementation of delete_expired."""
|
|
764
|
+
sql = f"DELETE FROM {self._table_name} WHERE expires_at <= SYSTIMESTAMP"
|
|
765
|
+
|
|
766
|
+
with self._config.provide_connection() as conn:
|
|
767
|
+
cursor = conn.cursor()
|
|
768
|
+
cursor.execute(sql)
|
|
769
|
+
count = cursor.rowcount if cursor.rowcount is not None else 0
|
|
770
|
+
conn.commit()
|
|
771
|
+
if count > 0:
|
|
772
|
+
self._log_delete_expired(count)
|
|
773
|
+
return count
|
|
774
|
+
|
|
775
|
+
async def delete_expired(self) -> int:
|
|
776
|
+
"""Delete all expired sessions.
|
|
777
|
+
|
|
778
|
+
Returns:
|
|
779
|
+
Number of sessions deleted.
|
|
780
|
+
"""
|
|
781
|
+
return await async_(self._delete_expired)()
|