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
sqlspec/core/cache.py
ADDED
|
@@ -0,0 +1,1069 @@
|
|
|
1
|
+
"""Caching system for SQL statement processing.
|
|
2
|
+
|
|
3
|
+
This module provides a caching system with LRU eviction and TTL support for
|
|
4
|
+
SQL statement processing and SQLGlot expression caching.
|
|
5
|
+
|
|
6
|
+
Components:
|
|
7
|
+
- CacheKey: Immutable cache key
|
|
8
|
+
- LRUCache: LRU + TTL cache implementation
|
|
9
|
+
- NamespacedCache: Namespace-aware cache wrapper for statement processing
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
import threading
|
|
14
|
+
import time
|
|
15
|
+
from typing import TYPE_CHECKING, Any, Final
|
|
16
|
+
|
|
17
|
+
from mypy_extensions import mypyc_attr
|
|
18
|
+
from typing_extensions import TypeVar
|
|
19
|
+
|
|
20
|
+
from sqlspec.core.pipeline import (
|
|
21
|
+
configure_statement_pipeline_cache,
|
|
22
|
+
get_statement_pipeline_metrics,
|
|
23
|
+
reset_statement_pipeline_cache,
|
|
24
|
+
)
|
|
25
|
+
from sqlspec.utils.logging import get_logger, log_with_context
|
|
26
|
+
from sqlspec.utils.type_guards import has_field_name, has_filter_attributes
|
|
27
|
+
|
|
28
|
+
if TYPE_CHECKING:
|
|
29
|
+
from collections.abc import Callable, Iterator
|
|
30
|
+
|
|
31
|
+
import sqlglot.expressions as exp
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
__all__ = (
|
|
35
|
+
"CacheKey",
|
|
36
|
+
"CacheStats",
|
|
37
|
+
"CachedStatement",
|
|
38
|
+
"FiltersView",
|
|
39
|
+
"LRUCache",
|
|
40
|
+
"NamespacedCache",
|
|
41
|
+
"canonicalize_filters",
|
|
42
|
+
"create_cache_key",
|
|
43
|
+
"get_cache",
|
|
44
|
+
"get_cache_config",
|
|
45
|
+
"get_cache_instances",
|
|
46
|
+
"get_default_cache",
|
|
47
|
+
"get_pipeline_metrics",
|
|
48
|
+
"reset_pipeline_registry",
|
|
49
|
+
"set_cache_instances",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
logger = get_logger("sqlspec.cache")
|
|
53
|
+
|
|
54
|
+
T = TypeVar("T")
|
|
55
|
+
CacheValueT = TypeVar("CacheValueT")
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
DEFAULT_MAX_SIZE: Final = 10000
|
|
59
|
+
DEFAULT_TTL_SECONDS: Final = 3600
|
|
60
|
+
CACHE_STATS_UPDATE_INTERVAL: Final = 100
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
CACHE_KEY_SLOTS: Final = ("_hash", "_key_data")
|
|
64
|
+
CACHE_NODE_SLOTS: Final = ("key", "value", "prev", "next", "timestamp", "access_count")
|
|
65
|
+
LRU_CACHE_SLOTS: Final = ("_cache", "_lock", "_max_size", "_ttl", "_head", "_tail", "_stats")
|
|
66
|
+
CACHE_STATS_SLOTS: Final = ("hits", "misses", "evictions", "total_operations", "memory_usage")
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
70
|
+
class CacheKey:
|
|
71
|
+
"""Immutable cache key.
|
|
72
|
+
|
|
73
|
+
Args:
|
|
74
|
+
key_data: Tuple of hashable values that uniquely identify the cached item
|
|
75
|
+
"""
|
|
76
|
+
|
|
77
|
+
__slots__ = ("_hash", "_key_data")
|
|
78
|
+
|
|
79
|
+
def __init__(self, key_data: "tuple[Any, ...]") -> None:
|
|
80
|
+
"""Initialize cache key.
|
|
81
|
+
|
|
82
|
+
Args:
|
|
83
|
+
key_data: Tuple of hashable values for the cache key
|
|
84
|
+
"""
|
|
85
|
+
self._key_data = key_data
|
|
86
|
+
self._hash = hash(key_data)
|
|
87
|
+
|
|
88
|
+
@property
|
|
89
|
+
def key_data(self) -> "tuple[Any, ...]":
|
|
90
|
+
"""Get the key data tuple."""
|
|
91
|
+
return self._key_data
|
|
92
|
+
|
|
93
|
+
def __hash__(self) -> int:
|
|
94
|
+
"""Return cached hash value."""
|
|
95
|
+
return self._hash
|
|
96
|
+
|
|
97
|
+
def __eq__(self, other: object) -> bool:
|
|
98
|
+
"""Equality comparison."""
|
|
99
|
+
if type(other) is not CacheKey:
|
|
100
|
+
return False
|
|
101
|
+
other_key = other
|
|
102
|
+
if self._hash != other_key._hash:
|
|
103
|
+
return False
|
|
104
|
+
return self._key_data == other_key._key_data
|
|
105
|
+
|
|
106
|
+
def __repr__(self) -> str:
|
|
107
|
+
"""String representation of the cache key."""
|
|
108
|
+
return f"CacheKey({self._key_data!r})"
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
112
|
+
class CacheStats:
|
|
113
|
+
"""Cache statistics tracking.
|
|
114
|
+
|
|
115
|
+
Tracks cache metrics including hit rates, evictions, and memory usage.
|
|
116
|
+
"""
|
|
117
|
+
|
|
118
|
+
__slots__ = CACHE_STATS_SLOTS
|
|
119
|
+
|
|
120
|
+
def __init__(self) -> None:
|
|
121
|
+
"""Initialize cache statistics."""
|
|
122
|
+
self.hits = 0
|
|
123
|
+
self.misses = 0
|
|
124
|
+
self.evictions = 0
|
|
125
|
+
self.total_operations = 0
|
|
126
|
+
self.memory_usage = 0
|
|
127
|
+
|
|
128
|
+
@property
|
|
129
|
+
def hit_rate(self) -> float:
|
|
130
|
+
"""Calculate cache hit rate as percentage."""
|
|
131
|
+
total = self.hits + self.misses
|
|
132
|
+
return (self.hits / total * 100) if total > 0 else 0.0
|
|
133
|
+
|
|
134
|
+
@property
|
|
135
|
+
def miss_rate(self) -> float:
|
|
136
|
+
"""Calculate cache miss rate as percentage."""
|
|
137
|
+
return 100.0 - self.hit_rate
|
|
138
|
+
|
|
139
|
+
def record_hit(self) -> None:
|
|
140
|
+
"""Record a cache hit."""
|
|
141
|
+
self.hits += 1
|
|
142
|
+
self.total_operations += 1
|
|
143
|
+
|
|
144
|
+
def record_miss(self) -> None:
|
|
145
|
+
"""Record a cache miss."""
|
|
146
|
+
self.misses += 1
|
|
147
|
+
self.total_operations += 1
|
|
148
|
+
|
|
149
|
+
def record_eviction(self) -> None:
|
|
150
|
+
"""Record a cache eviction."""
|
|
151
|
+
self.evictions += 1
|
|
152
|
+
|
|
153
|
+
def reset(self) -> None:
|
|
154
|
+
"""Reset all statistics."""
|
|
155
|
+
self.hits = 0
|
|
156
|
+
self.misses = 0
|
|
157
|
+
self.evictions = 0
|
|
158
|
+
self.total_operations = 0
|
|
159
|
+
self.memory_usage = 0
|
|
160
|
+
|
|
161
|
+
def __repr__(self) -> str:
|
|
162
|
+
"""String representation of cache statistics."""
|
|
163
|
+
return (
|
|
164
|
+
f"CacheStats(hit_rate={self.hit_rate:.1f}%, "
|
|
165
|
+
f"hits={self.hits}, misses={self.misses}, "
|
|
166
|
+
f"evictions={self.evictions}, ops={self.total_operations})"
|
|
167
|
+
)
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
171
|
+
class CacheNode:
|
|
172
|
+
"""Internal cache node for LRU linked list implementation."""
|
|
173
|
+
|
|
174
|
+
__slots__ = CACHE_NODE_SLOTS
|
|
175
|
+
|
|
176
|
+
def __init__(self, key: CacheKey, value: Any) -> None:
|
|
177
|
+
"""Initialize cache node.
|
|
178
|
+
|
|
179
|
+
Args:
|
|
180
|
+
key: Cache key for this node
|
|
181
|
+
value: Cached value
|
|
182
|
+
"""
|
|
183
|
+
self.key = key
|
|
184
|
+
self.value = value
|
|
185
|
+
self.prev: CacheNode | None = None
|
|
186
|
+
self.next: CacheNode | None = None
|
|
187
|
+
self.timestamp = time.time()
|
|
188
|
+
self.access_count = 1
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
192
|
+
class LRUCache:
|
|
193
|
+
"""Cache with LRU eviction and TTL support.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
max_size: Maximum number of items to cache (LRU eviction when exceeded)
|
|
197
|
+
ttl_seconds: Time-to-live in seconds (None for no expiration)
|
|
198
|
+
"""
|
|
199
|
+
|
|
200
|
+
__slots__ = LRU_CACHE_SLOTS
|
|
201
|
+
|
|
202
|
+
def __init__(self, max_size: int = DEFAULT_MAX_SIZE, ttl_seconds: int | None = DEFAULT_TTL_SECONDS) -> None:
|
|
203
|
+
"""Initialize LRU cache.
|
|
204
|
+
|
|
205
|
+
Args:
|
|
206
|
+
max_size: Maximum number of cache entries
|
|
207
|
+
ttl_seconds: Time-to-live in seconds (None for no expiration)
|
|
208
|
+
"""
|
|
209
|
+
self._cache: dict[CacheKey, CacheNode] = {}
|
|
210
|
+
self._lock = threading.RLock()
|
|
211
|
+
self._max_size = max_size
|
|
212
|
+
self._ttl = ttl_seconds
|
|
213
|
+
self._stats = CacheStats()
|
|
214
|
+
|
|
215
|
+
self._head = CacheNode(CacheKey(()), None)
|
|
216
|
+
self._tail = CacheNode(CacheKey(()), None)
|
|
217
|
+
self._head.next = self._tail
|
|
218
|
+
self._tail.prev = self._head
|
|
219
|
+
|
|
220
|
+
def get(self, key: CacheKey) -> Any | None:
|
|
221
|
+
"""Get value from cache.
|
|
222
|
+
|
|
223
|
+
Args:
|
|
224
|
+
key: Cache key to lookup
|
|
225
|
+
|
|
226
|
+
Returns:
|
|
227
|
+
Cached value or None if not found or expired
|
|
228
|
+
"""
|
|
229
|
+
with self._lock:
|
|
230
|
+
node = self._cache.get(key)
|
|
231
|
+
if node is None:
|
|
232
|
+
self._stats.record_miss()
|
|
233
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
234
|
+
log_with_context(logger, logging.DEBUG, "cache.miss", cache_size=len(self._cache))
|
|
235
|
+
return None
|
|
236
|
+
|
|
237
|
+
ttl = self._ttl
|
|
238
|
+
if ttl is not None:
|
|
239
|
+
current_time = time.time()
|
|
240
|
+
if (current_time - node.timestamp) > ttl:
|
|
241
|
+
self._remove_node(node)
|
|
242
|
+
del self._cache[key]
|
|
243
|
+
self._stats.record_miss()
|
|
244
|
+
self._stats.record_eviction()
|
|
245
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
246
|
+
log_with_context(
|
|
247
|
+
logger, logging.DEBUG, "cache.evict", cache_size=len(self._cache), reason="expired"
|
|
248
|
+
)
|
|
249
|
+
return None
|
|
250
|
+
|
|
251
|
+
self._move_to_head(node)
|
|
252
|
+
node.access_count += 1
|
|
253
|
+
self._stats.record_hit()
|
|
254
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
255
|
+
log_with_context(logger, logging.DEBUG, "cache.hit", cache_size=len(self._cache))
|
|
256
|
+
return node.value
|
|
257
|
+
|
|
258
|
+
def put(self, key: CacheKey, value: Any) -> None:
|
|
259
|
+
"""Put value in cache.
|
|
260
|
+
|
|
261
|
+
Args:
|
|
262
|
+
key: Cache key
|
|
263
|
+
value: Value to cache
|
|
264
|
+
"""
|
|
265
|
+
with self._lock:
|
|
266
|
+
existing_node = self._cache.get(key)
|
|
267
|
+
if existing_node is not None:
|
|
268
|
+
existing_node.value = value
|
|
269
|
+
existing_node.timestamp = time.time()
|
|
270
|
+
existing_node.access_count += 1
|
|
271
|
+
self._move_to_head(existing_node)
|
|
272
|
+
return
|
|
273
|
+
|
|
274
|
+
new_node = CacheNode(key, value)
|
|
275
|
+
self._cache[key] = new_node
|
|
276
|
+
self._add_to_head(new_node)
|
|
277
|
+
|
|
278
|
+
if len(self._cache) > self._max_size:
|
|
279
|
+
tail_node = self._tail.prev
|
|
280
|
+
if tail_node is not None and tail_node is not self._head:
|
|
281
|
+
self._remove_node(tail_node)
|
|
282
|
+
del self._cache[tail_node.key]
|
|
283
|
+
self._stats.record_eviction()
|
|
284
|
+
if logger.isEnabledFor(logging.DEBUG):
|
|
285
|
+
log_with_context(
|
|
286
|
+
logger, logging.DEBUG, "cache.evict", cache_size=len(self._cache), reason="max_size"
|
|
287
|
+
)
|
|
288
|
+
|
|
289
|
+
def delete(self, key: CacheKey) -> bool:
|
|
290
|
+
"""Delete entry from cache.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
key: Cache key to delete
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
True if key was found and deleted, False otherwise
|
|
297
|
+
"""
|
|
298
|
+
with self._lock:
|
|
299
|
+
node: CacheNode | None = self._cache.get(key)
|
|
300
|
+
if node is None:
|
|
301
|
+
return False
|
|
302
|
+
|
|
303
|
+
self._remove_node(node)
|
|
304
|
+
del self._cache[key]
|
|
305
|
+
return True
|
|
306
|
+
|
|
307
|
+
def clear(self) -> None:
|
|
308
|
+
"""Clear all cache entries."""
|
|
309
|
+
with self._lock:
|
|
310
|
+
self._cache.clear()
|
|
311
|
+
self._head.next = self._tail
|
|
312
|
+
self._tail.prev = self._head
|
|
313
|
+
self._stats.reset()
|
|
314
|
+
|
|
315
|
+
def size(self) -> int:
|
|
316
|
+
"""Get current cache size."""
|
|
317
|
+
return len(self._cache)
|
|
318
|
+
|
|
319
|
+
def is_empty(self) -> bool:
|
|
320
|
+
"""Check if cache is empty."""
|
|
321
|
+
return not self._cache
|
|
322
|
+
|
|
323
|
+
def get_stats(self) -> CacheStats:
|
|
324
|
+
"""Get cache statistics."""
|
|
325
|
+
return self._stats
|
|
326
|
+
|
|
327
|
+
def _add_to_head(self, node: CacheNode) -> None:
|
|
328
|
+
"""Add node to head of list."""
|
|
329
|
+
node.prev = self._head
|
|
330
|
+
head_next: CacheNode | None = self._head.next
|
|
331
|
+
node.next = head_next
|
|
332
|
+
if head_next is not None:
|
|
333
|
+
head_next.prev = node
|
|
334
|
+
self._head.next = node
|
|
335
|
+
|
|
336
|
+
def _remove_node(self, node: CacheNode) -> None:
|
|
337
|
+
"""Remove node from linked list."""
|
|
338
|
+
node_prev: CacheNode | None = node.prev
|
|
339
|
+
node_next: CacheNode | None = node.next
|
|
340
|
+
if node_prev is not None:
|
|
341
|
+
node_prev.next = node_next
|
|
342
|
+
if node_next is not None:
|
|
343
|
+
node_next.prev = node_prev
|
|
344
|
+
|
|
345
|
+
def _move_to_head(self, node: CacheNode) -> None:
|
|
346
|
+
"""Move node to head of list."""
|
|
347
|
+
self._remove_node(node)
|
|
348
|
+
self._add_to_head(node)
|
|
349
|
+
|
|
350
|
+
def __len__(self) -> int:
|
|
351
|
+
"""Get current cache size."""
|
|
352
|
+
return len(self._cache)
|
|
353
|
+
|
|
354
|
+
def __contains__(self, key: CacheKey) -> bool:
|
|
355
|
+
"""Check if key exists in cache."""
|
|
356
|
+
with self._lock:
|
|
357
|
+
node = self._cache.get(key)
|
|
358
|
+
if node is None:
|
|
359
|
+
return False
|
|
360
|
+
|
|
361
|
+
ttl = self._ttl
|
|
362
|
+
return not (ttl is not None and time.time() - node.timestamp > ttl)
|
|
363
|
+
|
|
364
|
+
|
|
365
|
+
_default_cache: LRUCache | None = None
|
|
366
|
+
_cache_lock = threading.Lock()
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def get_default_cache() -> LRUCache:
|
|
370
|
+
"""Get the default LRU cache instance.
|
|
371
|
+
|
|
372
|
+
Returns:
|
|
373
|
+
Singleton default cache instance
|
|
374
|
+
"""
|
|
375
|
+
global _default_cache
|
|
376
|
+
if _default_cache is None:
|
|
377
|
+
with _cache_lock:
|
|
378
|
+
if _default_cache is None:
|
|
379
|
+
config = get_cache_config()
|
|
380
|
+
_default_cache = LRUCache(config.sql_cache_size)
|
|
381
|
+
return _default_cache
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def get_cache_instances() -> "tuple[LRUCache | None, NamespacedCache | None]":
|
|
385
|
+
"""Return the current cache instances.
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
Tuple of (default_cache, namespaced_cache).
|
|
389
|
+
"""
|
|
390
|
+
return _default_cache, _namespaced_cache
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
def set_cache_instances(default_cache: "LRUCache | None", namespaced_cache: "NamespacedCache | None") -> None:
|
|
394
|
+
"""Replace cache instances (used by tests and diagnostics).
|
|
395
|
+
|
|
396
|
+
Args:
|
|
397
|
+
default_cache: Default cache instance or None.
|
|
398
|
+
namespaced_cache: Namespaced cache instance or None.
|
|
399
|
+
"""
|
|
400
|
+
global _default_cache, _namespaced_cache
|
|
401
|
+
_default_cache = default_cache
|
|
402
|
+
_namespaced_cache = namespaced_cache
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def clear_all_caches() -> None:
|
|
406
|
+
"""Clear all cache instances."""
|
|
407
|
+
if _default_cache is not None:
|
|
408
|
+
_default_cache.clear()
|
|
409
|
+
cache = get_cache()
|
|
410
|
+
cache.clear()
|
|
411
|
+
reset_statement_pipeline_cache()
|
|
412
|
+
|
|
413
|
+
|
|
414
|
+
def get_cache_statistics() -> "dict[str, CacheStats]":
|
|
415
|
+
"""Get statistics from all cache instances.
|
|
416
|
+
|
|
417
|
+
Returns:
|
|
418
|
+
Dictionary mapping cache type to statistics
|
|
419
|
+
"""
|
|
420
|
+
stats: dict[str, CacheStats] = {}
|
|
421
|
+
default_cache = get_default_cache()
|
|
422
|
+
stats["default"] = default_cache.get_stats()
|
|
423
|
+
cache = get_cache()
|
|
424
|
+
stats["namespaced"] = cache.get_stats()
|
|
425
|
+
return stats
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
_global_cache_config: "CacheConfig | None" = None
|
|
429
|
+
|
|
430
|
+
|
|
431
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
432
|
+
class CacheConfig:
|
|
433
|
+
"""Global cache configuration for SQLSpec."""
|
|
434
|
+
|
|
435
|
+
def __init__(
|
|
436
|
+
self,
|
|
437
|
+
*,
|
|
438
|
+
compiled_cache_enabled: bool = True,
|
|
439
|
+
sql_cache_enabled: bool = True,
|
|
440
|
+
fragment_cache_enabled: bool = True,
|
|
441
|
+
optimized_cache_enabled: bool = True,
|
|
442
|
+
sql_cache_size: int = 1000,
|
|
443
|
+
fragment_cache_size: int = 5000,
|
|
444
|
+
optimized_cache_size: int = 2000,
|
|
445
|
+
) -> None:
|
|
446
|
+
"""Initialize cache configuration.
|
|
447
|
+
|
|
448
|
+
Args:
|
|
449
|
+
compiled_cache_enabled: Master switch for namespaced caches and compiled SQL caching.
|
|
450
|
+
sql_cache_enabled: Enable statement and builder caching.
|
|
451
|
+
fragment_cache_enabled: Enable expression, parameter, and file caching.
|
|
452
|
+
optimized_cache_enabled: Enable optimized expression caching.
|
|
453
|
+
sql_cache_size: Maximum statement/builder cache entries.
|
|
454
|
+
fragment_cache_size: Maximum expression/parameter/file cache entries.
|
|
455
|
+
optimized_cache_size: Maximum optimized cache entries.
|
|
456
|
+
"""
|
|
457
|
+
self.compiled_cache_enabled = compiled_cache_enabled
|
|
458
|
+
self.sql_cache_enabled = sql_cache_enabled
|
|
459
|
+
self.fragment_cache_enabled = fragment_cache_enabled
|
|
460
|
+
self.optimized_cache_enabled = optimized_cache_enabled
|
|
461
|
+
self.sql_cache_size = sql_cache_size
|
|
462
|
+
self.fragment_cache_size = fragment_cache_size
|
|
463
|
+
self.optimized_cache_size = optimized_cache_size
|
|
464
|
+
|
|
465
|
+
|
|
466
|
+
def get_cache_config() -> CacheConfig:
|
|
467
|
+
"""Get the global cache configuration.
|
|
468
|
+
|
|
469
|
+
Returns:
|
|
470
|
+
Current global cache configuration instance
|
|
471
|
+
"""
|
|
472
|
+
global _global_cache_config
|
|
473
|
+
if _global_cache_config is None:
|
|
474
|
+
_global_cache_config = CacheConfig()
|
|
475
|
+
_configure_pipeline_cache(_global_cache_config)
|
|
476
|
+
return _global_cache_config
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
def _configure_pipeline_cache(config: "CacheConfig") -> None:
|
|
480
|
+
compiled_cache_enabled = config.compiled_cache_enabled and config.sql_cache_enabled
|
|
481
|
+
fragment_cache_enabled = config.compiled_cache_enabled and config.fragment_cache_enabled
|
|
482
|
+
cache_size = config.sql_cache_size if compiled_cache_enabled else 0
|
|
483
|
+
parse_cache_size = config.fragment_cache_size if fragment_cache_enabled else 0
|
|
484
|
+
configure_statement_pipeline_cache(
|
|
485
|
+
cache_size=cache_size, parse_cache_size=parse_cache_size, cache_enabled=compiled_cache_enabled
|
|
486
|
+
)
|
|
487
|
+
|
|
488
|
+
|
|
489
|
+
def update_cache_config(config: CacheConfig) -> None:
|
|
490
|
+
"""Update the global cache configuration.
|
|
491
|
+
|
|
492
|
+
Clears all existing caches when configuration changes.
|
|
493
|
+
|
|
494
|
+
Args:
|
|
495
|
+
config: New cache configuration to apply globally
|
|
496
|
+
"""
|
|
497
|
+
logger = get_logger("sqlspec.cache")
|
|
498
|
+
log_with_context(
|
|
499
|
+
logger,
|
|
500
|
+
logging.DEBUG,
|
|
501
|
+
"cache.config.updated",
|
|
502
|
+
compiled_cache_enabled=config.compiled_cache_enabled,
|
|
503
|
+
sql_cache_enabled=config.sql_cache_enabled,
|
|
504
|
+
fragment_cache_enabled=config.fragment_cache_enabled,
|
|
505
|
+
optimized_cache_enabled=config.optimized_cache_enabled,
|
|
506
|
+
sql_cache_size=config.sql_cache_size,
|
|
507
|
+
fragment_cache_size=config.fragment_cache_size,
|
|
508
|
+
optimized_cache_size=config.optimized_cache_size,
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
global _default_cache, _global_cache_config, _namespaced_cache
|
|
512
|
+
_global_cache_config = config
|
|
513
|
+
|
|
514
|
+
_configure_pipeline_cache(config)
|
|
515
|
+
|
|
516
|
+
if _default_cache is not None:
|
|
517
|
+
_default_cache.clear()
|
|
518
|
+
if _namespaced_cache is not None:
|
|
519
|
+
_namespaced_cache.clear()
|
|
520
|
+
_default_cache = None
|
|
521
|
+
_namespaced_cache = None
|
|
522
|
+
|
|
523
|
+
log_with_context(
|
|
524
|
+
logger,
|
|
525
|
+
logging.DEBUG,
|
|
526
|
+
"cache.config.cleared",
|
|
527
|
+
compiled_cache_enabled=config.compiled_cache_enabled,
|
|
528
|
+
sql_cache_enabled=config.sql_cache_enabled,
|
|
529
|
+
fragment_cache_enabled=config.fragment_cache_enabled,
|
|
530
|
+
optimized_cache_enabled=config.optimized_cache_enabled,
|
|
531
|
+
)
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
def get_cache_stats() -> "dict[str, CacheStats]":
|
|
535
|
+
"""Get cache statistics from all caches.
|
|
536
|
+
|
|
537
|
+
Returns:
|
|
538
|
+
Dictionary of cache statistics
|
|
539
|
+
"""
|
|
540
|
+
return get_cache_statistics()
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def reset_cache_stats() -> None:
|
|
544
|
+
"""Reset all cache statistics."""
|
|
545
|
+
clear_all_caches()
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def log_cache_stats() -> None:
|
|
549
|
+
"""Log cache statistics."""
|
|
550
|
+
logger = get_logger("sqlspec.cache")
|
|
551
|
+
stats = get_cache_stats()
|
|
552
|
+
stats_summary = {
|
|
553
|
+
name: {
|
|
554
|
+
"hits": stat.hits,
|
|
555
|
+
"misses": stat.misses,
|
|
556
|
+
"evictions": stat.evictions,
|
|
557
|
+
"total_operations": stat.total_operations,
|
|
558
|
+
"memory_usage": stat.memory_usage,
|
|
559
|
+
}
|
|
560
|
+
for name, stat in stats.items()
|
|
561
|
+
}
|
|
562
|
+
log_with_context(logger, logging.DEBUG, "cache.stats", stats=stats_summary)
|
|
563
|
+
|
|
564
|
+
|
|
565
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
566
|
+
class CachedStatement:
|
|
567
|
+
"""Immutable cached statement result.
|
|
568
|
+
|
|
569
|
+
This class stores compiled SQL and parameters in an immutable format
|
|
570
|
+
that can be safely shared between different parts of the system without
|
|
571
|
+
risk of mutation. Tuple parameters ensure no copying is needed.
|
|
572
|
+
"""
|
|
573
|
+
|
|
574
|
+
__slots__ = ("compiled_sql", "expression", "parameters")
|
|
575
|
+
|
|
576
|
+
def __init__(
|
|
577
|
+
self,
|
|
578
|
+
compiled_sql: str,
|
|
579
|
+
parameters: "tuple[Any, ...] | dict[str, Any] | None",
|
|
580
|
+
expression: "exp.Expression | None",
|
|
581
|
+
) -> None:
|
|
582
|
+
self.compiled_sql = compiled_sql
|
|
583
|
+
self.parameters = parameters
|
|
584
|
+
self.expression = expression
|
|
585
|
+
|
|
586
|
+
def __repr__(self) -> str:
|
|
587
|
+
return (
|
|
588
|
+
"CachedStatement("
|
|
589
|
+
f"compiled_sql={self.compiled_sql!r}, "
|
|
590
|
+
f"parameters={self.parameters!r}, "
|
|
591
|
+
f"expression={self.expression!r})"
|
|
592
|
+
)
|
|
593
|
+
|
|
594
|
+
def __eq__(self, other: object) -> bool:
|
|
595
|
+
if not isinstance(other, CachedStatement):
|
|
596
|
+
return False
|
|
597
|
+
return (
|
|
598
|
+
self.compiled_sql == other.compiled_sql
|
|
599
|
+
and self.parameters == other.parameters
|
|
600
|
+
and self.expression == other.expression
|
|
601
|
+
)
|
|
602
|
+
|
|
603
|
+
def __hash__(self) -> int:
|
|
604
|
+
return hash((self.compiled_sql, self.parameters, self.expression))
|
|
605
|
+
|
|
606
|
+
|
|
607
|
+
def create_cache_key(namespace: str, key: str, dialect: str | None = None) -> str:
|
|
608
|
+
"""Create optimized cache key using string concatenation.
|
|
609
|
+
|
|
610
|
+
Args:
|
|
611
|
+
namespace: Cache namespace name.
|
|
612
|
+
key: Base cache key.
|
|
613
|
+
dialect: SQL dialect (optional).
|
|
614
|
+
|
|
615
|
+
Returns:
|
|
616
|
+
Optimized cache key string.
|
|
617
|
+
"""
|
|
618
|
+
return f"{namespace}:{dialect or 'default'}:{key}"
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
def _sql_cache_enabled(config: "CacheConfig") -> bool:
|
|
622
|
+
return config.sql_cache_enabled
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def _sql_cache_size(config: "CacheConfig") -> int:
|
|
626
|
+
return config.sql_cache_size
|
|
627
|
+
|
|
628
|
+
|
|
629
|
+
def _fragment_cache_enabled(config: "CacheConfig") -> bool:
|
|
630
|
+
return config.fragment_cache_enabled
|
|
631
|
+
|
|
632
|
+
|
|
633
|
+
def _fragment_cache_size(config: "CacheConfig") -> int:
|
|
634
|
+
return config.fragment_cache_size
|
|
635
|
+
|
|
636
|
+
|
|
637
|
+
def _optimized_cache_enabled(config: "CacheConfig") -> bool:
|
|
638
|
+
return config.optimized_cache_enabled
|
|
639
|
+
|
|
640
|
+
|
|
641
|
+
def _optimized_cache_size(config: "CacheConfig") -> int:
|
|
642
|
+
return config.optimized_cache_size
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
NAMESPACED_CACHE_CONFIG: "dict[str, tuple[Callable[[CacheConfig], bool], Callable[[CacheConfig], int]]]" = {
|
|
646
|
+
"statement": (_sql_cache_enabled, _sql_cache_size),
|
|
647
|
+
"builder": (_sql_cache_enabled, _sql_cache_size),
|
|
648
|
+
"expression": (_fragment_cache_enabled, _fragment_cache_size),
|
|
649
|
+
"file": (_fragment_cache_enabled, _fragment_cache_size),
|
|
650
|
+
"optimized": (_optimized_cache_enabled, _optimized_cache_size),
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
655
|
+
class NamespacedCache:
|
|
656
|
+
"""Single cache with namespace isolation.
|
|
657
|
+
|
|
658
|
+
Uses per-namespace LRU caches sized by CacheConfig to keep memory usage
|
|
659
|
+
predictable while avoiding stringly-typed cache access.
|
|
660
|
+
"""
|
|
661
|
+
|
|
662
|
+
__slots__ = ("_caches", "_config")
|
|
663
|
+
|
|
664
|
+
def __init__(self, config: "CacheConfig | None" = None, ttl_seconds: int | None = DEFAULT_TTL_SECONDS) -> None:
|
|
665
|
+
"""Initialize namespaced cache.
|
|
666
|
+
|
|
667
|
+
Args:
|
|
668
|
+
config: Cache configuration to apply.
|
|
669
|
+
ttl_seconds: Time-to-live in seconds (None for no expiration).
|
|
670
|
+
"""
|
|
671
|
+
self._config = config or get_cache_config()
|
|
672
|
+
self._caches = self._build_caches(self._config, ttl_seconds)
|
|
673
|
+
|
|
674
|
+
@staticmethod
|
|
675
|
+
def _build_caches(config: "CacheConfig", ttl_seconds: int | None) -> "dict[str, LRUCache]":
|
|
676
|
+
caches: dict[str, LRUCache] = {}
|
|
677
|
+
for namespace, (_, size_getter) in NAMESPACED_CACHE_CONFIG.items():
|
|
678
|
+
size = size_getter(config)
|
|
679
|
+
caches[namespace] = LRUCache(size, ttl_seconds)
|
|
680
|
+
return caches
|
|
681
|
+
|
|
682
|
+
def _is_enabled(self, namespace: str) -> bool:
|
|
683
|
+
if not self._config.compiled_cache_enabled:
|
|
684
|
+
return False
|
|
685
|
+
enabled_getter = NAMESPACED_CACHE_CONFIG[namespace][0]
|
|
686
|
+
return bool(enabled_getter(self._config))
|
|
687
|
+
|
|
688
|
+
def _get(self, namespace: str, key: str, dialect: str | None = None) -> Any | None:
|
|
689
|
+
"""Get cached value by namespace.
|
|
690
|
+
|
|
691
|
+
Args:
|
|
692
|
+
namespace: Cache namespace.
|
|
693
|
+
key: Cache key.
|
|
694
|
+
dialect: Optional SQL dialect.
|
|
695
|
+
|
|
696
|
+
Returns:
|
|
697
|
+
Cached value or None if not found.
|
|
698
|
+
"""
|
|
699
|
+
if not self._is_enabled(namespace):
|
|
700
|
+
return None
|
|
701
|
+
cache = self._caches[namespace]
|
|
702
|
+
full_key = create_cache_key(namespace, key, dialect)
|
|
703
|
+
cache_key = CacheKey((full_key,))
|
|
704
|
+
return cache.get(cache_key)
|
|
705
|
+
|
|
706
|
+
def _put(self, namespace: str, key: str, value: Any, dialect: str | None = None) -> None:
|
|
707
|
+
"""Put cached value by namespace.
|
|
708
|
+
|
|
709
|
+
Args:
|
|
710
|
+
namespace: Cache namespace.
|
|
711
|
+
key: Cache key.
|
|
712
|
+
value: Value to cache.
|
|
713
|
+
dialect: Optional SQL dialect.
|
|
714
|
+
"""
|
|
715
|
+
if not self._is_enabled(namespace):
|
|
716
|
+
return
|
|
717
|
+
cache = self._caches[namespace]
|
|
718
|
+
full_key = create_cache_key(namespace, key, dialect)
|
|
719
|
+
cache_key = CacheKey((full_key,))
|
|
720
|
+
cache.put(cache_key, value)
|
|
721
|
+
|
|
722
|
+
def _delete(self, namespace: str, key: str, dialect: str | None = None) -> bool:
|
|
723
|
+
"""Delete cached value by namespace.
|
|
724
|
+
|
|
725
|
+
Args:
|
|
726
|
+
namespace: Cache namespace.
|
|
727
|
+
key: Cache key.
|
|
728
|
+
dialect: Optional SQL dialect.
|
|
729
|
+
|
|
730
|
+
Returns:
|
|
731
|
+
True when the key was found and deleted.
|
|
732
|
+
"""
|
|
733
|
+
if not self._is_enabled(namespace):
|
|
734
|
+
return False
|
|
735
|
+
cache = self._caches[namespace]
|
|
736
|
+
full_key = create_cache_key(namespace, key, dialect)
|
|
737
|
+
cache_key = CacheKey((full_key,))
|
|
738
|
+
return cache.delete(cache_key)
|
|
739
|
+
|
|
740
|
+
def get_statement(self, key: str, dialect: str | None = None) -> Any | None:
|
|
741
|
+
"""Get cached statement data.
|
|
742
|
+
|
|
743
|
+
Args:
|
|
744
|
+
key: Cache key.
|
|
745
|
+
dialect: Optional SQL dialect.
|
|
746
|
+
|
|
747
|
+
Returns:
|
|
748
|
+
Cached value or None if not found.
|
|
749
|
+
"""
|
|
750
|
+
return self._get("statement", key, dialect)
|
|
751
|
+
|
|
752
|
+
def put_statement(self, key: str, value: Any, dialect: str | None = None) -> None:
|
|
753
|
+
"""Cache compiled statement data.
|
|
754
|
+
|
|
755
|
+
Args:
|
|
756
|
+
key: Cache key.
|
|
757
|
+
value: Value to cache.
|
|
758
|
+
dialect: Optional SQL dialect.
|
|
759
|
+
"""
|
|
760
|
+
self._put("statement", key, value, dialect)
|
|
761
|
+
|
|
762
|
+
def delete_statement(self, key: str, dialect: str | None = None) -> bool:
|
|
763
|
+
"""Delete cached statement data.
|
|
764
|
+
|
|
765
|
+
Args:
|
|
766
|
+
key: Cache key.
|
|
767
|
+
dialect: Optional SQL dialect.
|
|
768
|
+
|
|
769
|
+
Returns:
|
|
770
|
+
True when the key was found and deleted.
|
|
771
|
+
"""
|
|
772
|
+
return self._delete("statement", key, dialect)
|
|
773
|
+
|
|
774
|
+
def get_expression(self, key: str, dialect: str | None = None) -> Any | None:
|
|
775
|
+
"""Get cached expression data.
|
|
776
|
+
|
|
777
|
+
Args:
|
|
778
|
+
key: Cache key.
|
|
779
|
+
dialect: Optional SQL dialect.
|
|
780
|
+
|
|
781
|
+
Returns:
|
|
782
|
+
Cached value or None if not found.
|
|
783
|
+
"""
|
|
784
|
+
return self._get("expression", key, dialect)
|
|
785
|
+
|
|
786
|
+
def put_expression(self, key: str, value: Any, dialect: str | None = None) -> None:
|
|
787
|
+
"""Cache parsed expression data.
|
|
788
|
+
|
|
789
|
+
Args:
|
|
790
|
+
key: Cache key.
|
|
791
|
+
value: Value to cache.
|
|
792
|
+
dialect: Optional SQL dialect.
|
|
793
|
+
"""
|
|
794
|
+
self._put("expression", key, value, dialect)
|
|
795
|
+
|
|
796
|
+
def delete_expression(self, key: str, dialect: str | None = None) -> bool:
|
|
797
|
+
"""Delete cached expression data.
|
|
798
|
+
|
|
799
|
+
Args:
|
|
800
|
+
key: Cache key.
|
|
801
|
+
dialect: Optional SQL dialect.
|
|
802
|
+
|
|
803
|
+
Returns:
|
|
804
|
+
True when the key was found and deleted.
|
|
805
|
+
"""
|
|
806
|
+
return self._delete("expression", key, dialect)
|
|
807
|
+
|
|
808
|
+
def get_optimized(self, key: str, dialect: str | None = None) -> Any | None:
|
|
809
|
+
"""Get cached optimized expression data.
|
|
810
|
+
|
|
811
|
+
Args:
|
|
812
|
+
key: Cache key.
|
|
813
|
+
dialect: Optional SQL dialect.
|
|
814
|
+
|
|
815
|
+
Returns:
|
|
816
|
+
Cached value or None if not found.
|
|
817
|
+
"""
|
|
818
|
+
return self._get("optimized", key, dialect)
|
|
819
|
+
|
|
820
|
+
def put_optimized(self, key: str, value: Any, dialect: str | None = None) -> None:
|
|
821
|
+
"""Cache optimized expression data.
|
|
822
|
+
|
|
823
|
+
Args:
|
|
824
|
+
key: Cache key.
|
|
825
|
+
value: Value to cache.
|
|
826
|
+
dialect: Optional SQL dialect.
|
|
827
|
+
"""
|
|
828
|
+
self._put("optimized", key, value, dialect)
|
|
829
|
+
|
|
830
|
+
def delete_optimized(self, key: str, dialect: str | None = None) -> bool:
|
|
831
|
+
"""Delete cached optimized expression data.
|
|
832
|
+
|
|
833
|
+
Args:
|
|
834
|
+
key: Cache key.
|
|
835
|
+
dialect: Optional SQL dialect.
|
|
836
|
+
|
|
837
|
+
Returns:
|
|
838
|
+
True when the key was found and deleted.
|
|
839
|
+
"""
|
|
840
|
+
return self._delete("optimized", key, dialect)
|
|
841
|
+
|
|
842
|
+
def get_builder(self, key: str, dialect: str | None = None) -> Any | None:
|
|
843
|
+
"""Get cached builder statement data.
|
|
844
|
+
|
|
845
|
+
Args:
|
|
846
|
+
key: Cache key.
|
|
847
|
+
dialect: Optional SQL dialect.
|
|
848
|
+
|
|
849
|
+
Returns:
|
|
850
|
+
Cached value or None if not found.
|
|
851
|
+
"""
|
|
852
|
+
return self._get("builder", key, dialect)
|
|
853
|
+
|
|
854
|
+
def put_builder(self, key: str, value: Any, dialect: str | None = None) -> None:
|
|
855
|
+
"""Cache builder statement data.
|
|
856
|
+
|
|
857
|
+
Args:
|
|
858
|
+
key: Cache key.
|
|
859
|
+
value: Value to cache.
|
|
860
|
+
dialect: Optional SQL dialect.
|
|
861
|
+
"""
|
|
862
|
+
self._put("builder", key, value, dialect)
|
|
863
|
+
|
|
864
|
+
def delete_builder(self, key: str, dialect: str | None = None) -> bool:
|
|
865
|
+
"""Delete cached builder statement data.
|
|
866
|
+
|
|
867
|
+
Args:
|
|
868
|
+
key: Cache key.
|
|
869
|
+
dialect: Optional SQL dialect.
|
|
870
|
+
|
|
871
|
+
Returns:
|
|
872
|
+
True when the key was found and deleted.
|
|
873
|
+
"""
|
|
874
|
+
return self._delete("builder", key, dialect)
|
|
875
|
+
|
|
876
|
+
def get_file(self, key: str, dialect: str | None = None) -> Any | None:
|
|
877
|
+
"""Get cached SQL file data.
|
|
878
|
+
|
|
879
|
+
Args:
|
|
880
|
+
key: Cache key.
|
|
881
|
+
dialect: Optional SQL dialect.
|
|
882
|
+
|
|
883
|
+
Returns:
|
|
884
|
+
Cached value or None if not found.
|
|
885
|
+
"""
|
|
886
|
+
return self._get("file", key, dialect)
|
|
887
|
+
|
|
888
|
+
def put_file(self, key: str, value: Any, dialect: str | None = None) -> None:
|
|
889
|
+
"""Cache SQL file data.
|
|
890
|
+
|
|
891
|
+
Args:
|
|
892
|
+
key: Cache key.
|
|
893
|
+
value: Value to cache.
|
|
894
|
+
dialect: Optional SQL dialect.
|
|
895
|
+
"""
|
|
896
|
+
self._put("file", key, value, dialect)
|
|
897
|
+
|
|
898
|
+
def delete_file(self, key: str, dialect: str | None = None) -> bool:
|
|
899
|
+
"""Delete cached SQL file data.
|
|
900
|
+
|
|
901
|
+
Args:
|
|
902
|
+
key: Cache key.
|
|
903
|
+
dialect: Optional SQL dialect.
|
|
904
|
+
|
|
905
|
+
Returns:
|
|
906
|
+
True when the key was found and deleted.
|
|
907
|
+
"""
|
|
908
|
+
return self._delete("file", key, dialect)
|
|
909
|
+
|
|
910
|
+
def clear(self) -> None:
|
|
911
|
+
"""Clear all cache entries."""
|
|
912
|
+
for cache in self._caches.values():
|
|
913
|
+
cache.clear()
|
|
914
|
+
|
|
915
|
+
def get_stats(self) -> CacheStats:
|
|
916
|
+
"""Get cache statistics."""
|
|
917
|
+
aggregated = CacheStats()
|
|
918
|
+
for cache in self._caches.values():
|
|
919
|
+
stats = cache.get_stats()
|
|
920
|
+
aggregated.hits += stats.hits
|
|
921
|
+
aggregated.misses += stats.misses
|
|
922
|
+
aggregated.evictions += stats.evictions
|
|
923
|
+
aggregated.total_operations += stats.total_operations
|
|
924
|
+
aggregated.memory_usage += stats.memory_usage
|
|
925
|
+
return aggregated
|
|
926
|
+
|
|
927
|
+
|
|
928
|
+
_namespaced_cache: NamespacedCache | None = None
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
def get_cache() -> NamespacedCache:
|
|
932
|
+
"""Get the namespaced cache instance.
|
|
933
|
+
|
|
934
|
+
Returns:
|
|
935
|
+
Singleton namespaced cache instance
|
|
936
|
+
"""
|
|
937
|
+
global _namespaced_cache
|
|
938
|
+
if _namespaced_cache is None:
|
|
939
|
+
with _cache_lock:
|
|
940
|
+
if _namespaced_cache is None:
|
|
941
|
+
_namespaced_cache = NamespacedCache(get_cache_config())
|
|
942
|
+
return _namespaced_cache
|
|
943
|
+
|
|
944
|
+
|
|
945
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
946
|
+
class Filter:
|
|
947
|
+
"""Immutable filter that can be safely shared."""
|
|
948
|
+
|
|
949
|
+
__slots__ = ("field_name", "operation", "value")
|
|
950
|
+
|
|
951
|
+
def __init__(self, field_name: str, operation: str, value: Any) -> None:
|
|
952
|
+
if not field_name:
|
|
953
|
+
msg = "Field name cannot be empty"
|
|
954
|
+
raise ValueError(msg)
|
|
955
|
+
if not operation:
|
|
956
|
+
msg = "Operation cannot be empty"
|
|
957
|
+
raise ValueError(msg)
|
|
958
|
+
self.field_name = field_name
|
|
959
|
+
self.operation = operation
|
|
960
|
+
self.value = value
|
|
961
|
+
|
|
962
|
+
def __repr__(self) -> str:
|
|
963
|
+
return f"Filter(field_name={self.field_name!r}, operation={self.operation!r}, value={self.value!r})"
|
|
964
|
+
|
|
965
|
+
def __eq__(self, other: object) -> bool:
|
|
966
|
+
if not isinstance(other, Filter):
|
|
967
|
+
return False
|
|
968
|
+
return self.field_name == other.field_name and self.operation == other.operation and self.value == other.value
|
|
969
|
+
|
|
970
|
+
def __hash__(self) -> int:
|
|
971
|
+
return hash((self.field_name, self.operation, self.value))
|
|
972
|
+
|
|
973
|
+
|
|
974
|
+
def canonicalize_filters(filters: "list[Filter]") -> "tuple[Filter, ...]":
|
|
975
|
+
"""Create canonical representation of filters for cache keys.
|
|
976
|
+
|
|
977
|
+
Args:
|
|
978
|
+
filters: List of filters to canonicalize
|
|
979
|
+
|
|
980
|
+
Returns:
|
|
981
|
+
Tuple of unique filters sorted by field_name, operation, then value
|
|
982
|
+
"""
|
|
983
|
+
if not filters:
|
|
984
|
+
return ()
|
|
985
|
+
|
|
986
|
+
# Deduplicate and sort for canonical representation
|
|
987
|
+
unique_filters = set(filters)
|
|
988
|
+
return tuple(sorted(unique_filters, key=_filter_sort_key))
|
|
989
|
+
|
|
990
|
+
|
|
991
|
+
def _filter_sort_key(filter_obj: "Filter") -> "tuple[str, str, str]":
|
|
992
|
+
return filter_obj.field_name, filter_obj.operation, str(filter_obj.value)
|
|
993
|
+
|
|
994
|
+
|
|
995
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
996
|
+
class FiltersView:
|
|
997
|
+
"""Read-only view of filters without copying.
|
|
998
|
+
|
|
999
|
+
Provides zero-copy access to filters with methods for querying,
|
|
1000
|
+
iteration, and canonical representation generation.
|
|
1001
|
+
"""
|
|
1002
|
+
|
|
1003
|
+
__slots__ = ("_filters_ref",)
|
|
1004
|
+
|
|
1005
|
+
def __init__(self, filters: "list[Any]") -> None:
|
|
1006
|
+
"""Initialize filters view.
|
|
1007
|
+
|
|
1008
|
+
Args:
|
|
1009
|
+
filters: List of filters (will be referenced, not copied)
|
|
1010
|
+
"""
|
|
1011
|
+
self._filters_ref = filters
|
|
1012
|
+
|
|
1013
|
+
def __len__(self) -> int:
|
|
1014
|
+
"""Get number of filters."""
|
|
1015
|
+
return len(self._filters_ref)
|
|
1016
|
+
|
|
1017
|
+
def __iter__(self) -> "Iterator[Any]":
|
|
1018
|
+
"""Iterate over filters."""
|
|
1019
|
+
return iter(self._filters_ref)
|
|
1020
|
+
|
|
1021
|
+
def get_by_field(self, field_name: str) -> "list[Any]":
|
|
1022
|
+
"""Get all filters for a specific field.
|
|
1023
|
+
|
|
1024
|
+
Args:
|
|
1025
|
+
field_name: Field name to filter by
|
|
1026
|
+
|
|
1027
|
+
Returns:
|
|
1028
|
+
List of filters matching the field name
|
|
1029
|
+
"""
|
|
1030
|
+
return [f for f in self._filters_ref if has_field_name(f) and f.field_name == field_name]
|
|
1031
|
+
|
|
1032
|
+
def has_field(self, field_name: str) -> bool:
|
|
1033
|
+
"""Check if any filter exists for a field.
|
|
1034
|
+
|
|
1035
|
+
Args:
|
|
1036
|
+
field_name: Field name to check
|
|
1037
|
+
|
|
1038
|
+
Returns:
|
|
1039
|
+
True if field has filters
|
|
1040
|
+
"""
|
|
1041
|
+
return any(has_field_name(f) and f.field_name == field_name for f in self._filters_ref)
|
|
1042
|
+
|
|
1043
|
+
def to_canonical(self) -> "tuple[Any, ...]":
|
|
1044
|
+
"""Create canonical representation for cache keys.
|
|
1045
|
+
|
|
1046
|
+
Returns:
|
|
1047
|
+
Canonical tuple representation of filters
|
|
1048
|
+
"""
|
|
1049
|
+
# Convert to Filter objects if needed, then canonicalize
|
|
1050
|
+
filter_objects = []
|
|
1051
|
+
for f in self._filters_ref:
|
|
1052
|
+
if isinstance(f, Filter):
|
|
1053
|
+
filter_objects.append(f)
|
|
1054
|
+
elif has_filter_attributes(f):
|
|
1055
|
+
filter_objects.append(Filter(f.field_name, f.operation, f.value))
|
|
1056
|
+
|
|
1057
|
+
return canonicalize_filters(filter_objects)
|
|
1058
|
+
|
|
1059
|
+
|
|
1060
|
+
def get_pipeline_metrics() -> "list[dict[str, Any]]":
|
|
1061
|
+
"""Return metrics for the shared statement pipeline cache when enabled."""
|
|
1062
|
+
|
|
1063
|
+
return get_statement_pipeline_metrics()
|
|
1064
|
+
|
|
1065
|
+
|
|
1066
|
+
def reset_pipeline_registry() -> None:
|
|
1067
|
+
"""Clear shared statement pipeline caches and metrics."""
|
|
1068
|
+
|
|
1069
|
+
reset_statement_pipeline_cache()
|