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/filters.py
ADDED
|
@@ -0,0 +1,952 @@
|
|
|
1
|
+
"""Filter system for SQL statement manipulation.
|
|
2
|
+
|
|
3
|
+
This module provides filters that can be applied to SQL statements to add
|
|
4
|
+
WHERE clauses, ORDER BY clauses, LIMIT/OFFSET, and other modifications.
|
|
5
|
+
|
|
6
|
+
Components:
|
|
7
|
+
- StatementFilter: Abstract base class for all filters
|
|
8
|
+
- BeforeAfterFilter: Date range filtering
|
|
9
|
+
- InCollectionFilter: IN clause filtering
|
|
10
|
+
- LimitOffsetFilter: Pagination support
|
|
11
|
+
- OrderByFilter: Sorting support
|
|
12
|
+
- SearchFilter: Text search filtering
|
|
13
|
+
- Various collection and negation filters
|
|
14
|
+
|
|
15
|
+
Features:
|
|
16
|
+
- Parameter conflict resolution
|
|
17
|
+
- Type-safe filter application
|
|
18
|
+
- Cacheable filter configurations
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
import uuid
|
|
22
|
+
from abc import ABC, abstractmethod
|
|
23
|
+
from collections import abc
|
|
24
|
+
from collections.abc import Sequence
|
|
25
|
+
from datetime import datetime
|
|
26
|
+
from typing import TYPE_CHECKING, Any, Generic, Literal, TypeAlias
|
|
27
|
+
|
|
28
|
+
import sqlglot
|
|
29
|
+
from mypy_extensions import mypyc_attr
|
|
30
|
+
from sqlglot import exp
|
|
31
|
+
from typing_extensions import TypeVar
|
|
32
|
+
|
|
33
|
+
from sqlspec.utils.type_guards import has_field_name
|
|
34
|
+
|
|
35
|
+
if TYPE_CHECKING:
|
|
36
|
+
from sqlglot.expressions import Condition
|
|
37
|
+
|
|
38
|
+
from sqlspec.core.statement import SQL
|
|
39
|
+
|
|
40
|
+
__all__ = (
|
|
41
|
+
"AnyCollectionFilter",
|
|
42
|
+
"BeforeAfterFilter",
|
|
43
|
+
"FilterTypeT",
|
|
44
|
+
"FilterTypes",
|
|
45
|
+
"InAnyFilter",
|
|
46
|
+
"InCollectionFilter",
|
|
47
|
+
"LimitOffsetFilter",
|
|
48
|
+
"NotAnyCollectionFilter",
|
|
49
|
+
"NotInCollectionFilter",
|
|
50
|
+
"NotInSearchFilter",
|
|
51
|
+
"NotNullFilter",
|
|
52
|
+
"NullFilter",
|
|
53
|
+
"OffsetPagination",
|
|
54
|
+
"OnBeforeAfterFilter",
|
|
55
|
+
"OrderByFilter",
|
|
56
|
+
"PaginationFilter",
|
|
57
|
+
"SearchFilter",
|
|
58
|
+
"StatementFilter",
|
|
59
|
+
"apply_filter",
|
|
60
|
+
"canonicalize_filters",
|
|
61
|
+
"create_filters",
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
T = TypeVar("T")
|
|
65
|
+
FilterTypeT = TypeVar("FilterTypeT", bound="StatementFilter")
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
@mypyc_attr(allow_interpreted_subclasses=True)
|
|
69
|
+
class StatementFilter(ABC):
|
|
70
|
+
"""Abstract base class for filters that can be appended to a statement."""
|
|
71
|
+
|
|
72
|
+
__slots__ = ()
|
|
73
|
+
|
|
74
|
+
@abstractmethod
|
|
75
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
76
|
+
"""Append the filter to the statement.
|
|
77
|
+
|
|
78
|
+
This method should modify the SQL expression only, not the parameters.
|
|
79
|
+
Parameters should be provided via extract_parameters().
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
83
|
+
"""Extract parameters that this filter contributes.
|
|
84
|
+
|
|
85
|
+
Returns:
|
|
86
|
+
Tuple of (positional_parameters, named_parameters) where:
|
|
87
|
+
- positional_parameters: List of positional parameter values
|
|
88
|
+
- named_parameters: Dict of parameter name to value
|
|
89
|
+
"""
|
|
90
|
+
return [], {}
|
|
91
|
+
|
|
92
|
+
def _resolve_parameter_conflicts(self, statement: "SQL", proposed_names: "list[str]") -> "list[str]":
|
|
93
|
+
"""Resolve parameter name conflicts.
|
|
94
|
+
|
|
95
|
+
Args:
|
|
96
|
+
statement: The SQL statement to check for existing parameters
|
|
97
|
+
proposed_names: List of proposed parameter names
|
|
98
|
+
|
|
99
|
+
Returns:
|
|
100
|
+
List of resolved parameter names (same length as proposed_names)
|
|
101
|
+
"""
|
|
102
|
+
existing_params = set(statement.named_parameters.keys())
|
|
103
|
+
existing_params.update(statement.parameters.keys() if isinstance(statement.parameters, dict) else [])
|
|
104
|
+
|
|
105
|
+
resolved_names = []
|
|
106
|
+
for name in proposed_names:
|
|
107
|
+
if name in existing_params:
|
|
108
|
+
unique_suffix = str(uuid.uuid4()).replace("-", "")[:8]
|
|
109
|
+
resolved_name = f"{name}_{unique_suffix}"
|
|
110
|
+
else:
|
|
111
|
+
resolved_name = name
|
|
112
|
+
resolved_names.append(resolved_name)
|
|
113
|
+
existing_params.add(resolved_name)
|
|
114
|
+
|
|
115
|
+
return resolved_names
|
|
116
|
+
|
|
117
|
+
@abstractmethod
|
|
118
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
119
|
+
"""Return a cache key for this filter's configuration.
|
|
120
|
+
|
|
121
|
+
Returns:
|
|
122
|
+
Tuple of hashable values representing the filter's configuration
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class BeforeAfterFilter(StatementFilter):
|
|
127
|
+
"""Filter for datetime range queries.
|
|
128
|
+
|
|
129
|
+
Applies WHERE clauses for before/after datetime filtering.
|
|
130
|
+
"""
|
|
131
|
+
|
|
132
|
+
__slots__ = ("_after", "_before", "_field_name")
|
|
133
|
+
|
|
134
|
+
def __init__(self, field_name: str, before: datetime | None = None, after: datetime | None = None) -> None:
|
|
135
|
+
self._field_name = field_name
|
|
136
|
+
self._before = before
|
|
137
|
+
self._after = after
|
|
138
|
+
|
|
139
|
+
@property
|
|
140
|
+
def field_name(self) -> str:
|
|
141
|
+
return self._field_name
|
|
142
|
+
|
|
143
|
+
@property
|
|
144
|
+
def before(self) -> datetime | None:
|
|
145
|
+
return self._before
|
|
146
|
+
|
|
147
|
+
@property
|
|
148
|
+
def after(self) -> datetime | None:
|
|
149
|
+
return self._after
|
|
150
|
+
|
|
151
|
+
def get_param_names(self) -> "list[str]":
|
|
152
|
+
"""Get parameter names without storing them."""
|
|
153
|
+
names = []
|
|
154
|
+
if self.before:
|
|
155
|
+
names.append(f"{self.field_name}_before")
|
|
156
|
+
if self.after:
|
|
157
|
+
names.append(f"{self.field_name}_after")
|
|
158
|
+
return names
|
|
159
|
+
|
|
160
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
161
|
+
"""Extract filter parameters."""
|
|
162
|
+
named_parameters = {}
|
|
163
|
+
param_names = self.get_param_names()
|
|
164
|
+
param_idx = 0
|
|
165
|
+
if self.before:
|
|
166
|
+
named_parameters[param_names[param_idx]] = self.before
|
|
167
|
+
param_idx += 1
|
|
168
|
+
if self.after:
|
|
169
|
+
named_parameters[param_names[param_idx]] = self.after
|
|
170
|
+
return [], named_parameters
|
|
171
|
+
|
|
172
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
173
|
+
"""Apply filter to SQL expression only."""
|
|
174
|
+
conditions: list[Condition] = []
|
|
175
|
+
col_expr = exp.column(self.field_name)
|
|
176
|
+
|
|
177
|
+
proposed_names = self.get_param_names()
|
|
178
|
+
if not proposed_names:
|
|
179
|
+
return statement
|
|
180
|
+
|
|
181
|
+
resolved_names = self._resolve_parameter_conflicts(statement, proposed_names)
|
|
182
|
+
|
|
183
|
+
param_idx = 0
|
|
184
|
+
result = statement
|
|
185
|
+
if self.before:
|
|
186
|
+
before_param_name = resolved_names[param_idx]
|
|
187
|
+
param_idx += 1
|
|
188
|
+
conditions.append(exp.LT(this=col_expr, expression=exp.Placeholder(this=before_param_name)))
|
|
189
|
+
result = result.add_named_parameter(before_param_name, self.before)
|
|
190
|
+
|
|
191
|
+
if self.after:
|
|
192
|
+
after_param_name = resolved_names[param_idx]
|
|
193
|
+
conditions.append(exp.GT(this=col_expr, expression=exp.Placeholder(this=after_param_name)))
|
|
194
|
+
result = result.add_named_parameter(after_param_name, self.after)
|
|
195
|
+
|
|
196
|
+
final_condition = conditions[0]
|
|
197
|
+
for cond in conditions[1:]:
|
|
198
|
+
final_condition = exp.And(this=final_condition, expression=cond)
|
|
199
|
+
return result.where(final_condition)
|
|
200
|
+
|
|
201
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
202
|
+
"""Return cache key for this filter configuration."""
|
|
203
|
+
return ("BeforeAfterFilter", self.field_name, self.before, self.after)
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class OnBeforeAfterFilter(StatementFilter):
|
|
207
|
+
"""Filter for inclusive datetime range queries.
|
|
208
|
+
|
|
209
|
+
Applies WHERE clauses for on-or-before/on-or-after datetime filtering.
|
|
210
|
+
"""
|
|
211
|
+
|
|
212
|
+
__slots__ = ("_field_name", "_on_or_after", "_on_or_before")
|
|
213
|
+
|
|
214
|
+
def __init__(
|
|
215
|
+
self, field_name: str, on_or_before: datetime | None = None, on_or_after: datetime | None = None
|
|
216
|
+
) -> None:
|
|
217
|
+
self._field_name = field_name
|
|
218
|
+
self._on_or_before = on_or_before
|
|
219
|
+
self._on_or_after = on_or_after
|
|
220
|
+
|
|
221
|
+
@property
|
|
222
|
+
def field_name(self) -> str:
|
|
223
|
+
return self._field_name
|
|
224
|
+
|
|
225
|
+
@property
|
|
226
|
+
def on_or_before(self) -> datetime | None:
|
|
227
|
+
return self._on_or_before
|
|
228
|
+
|
|
229
|
+
@property
|
|
230
|
+
def on_or_after(self) -> datetime | None:
|
|
231
|
+
return self._on_or_after
|
|
232
|
+
|
|
233
|
+
def get_param_names(self) -> "list[str]":
|
|
234
|
+
"""Get parameter names without storing them."""
|
|
235
|
+
names = []
|
|
236
|
+
if self.on_or_before:
|
|
237
|
+
names.append(f"{self.field_name}_on_or_before")
|
|
238
|
+
if self.on_or_after:
|
|
239
|
+
names.append(f"{self.field_name}_on_or_after")
|
|
240
|
+
return names
|
|
241
|
+
|
|
242
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
243
|
+
"""Extract filter parameters."""
|
|
244
|
+
named_parameters = {}
|
|
245
|
+
param_names = self.get_param_names()
|
|
246
|
+
param_idx = 0
|
|
247
|
+
if self.on_or_before:
|
|
248
|
+
named_parameters[param_names[param_idx]] = self.on_or_before
|
|
249
|
+
param_idx += 1
|
|
250
|
+
if self.on_or_after:
|
|
251
|
+
named_parameters[param_names[param_idx]] = self.on_or_after
|
|
252
|
+
return [], named_parameters
|
|
253
|
+
|
|
254
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
255
|
+
conditions: list[Condition] = []
|
|
256
|
+
|
|
257
|
+
proposed_names = self.get_param_names()
|
|
258
|
+
if not proposed_names:
|
|
259
|
+
return statement
|
|
260
|
+
|
|
261
|
+
resolved_names = self._resolve_parameter_conflicts(statement, proposed_names)
|
|
262
|
+
|
|
263
|
+
param_idx = 0
|
|
264
|
+
result = statement
|
|
265
|
+
if self.on_or_before:
|
|
266
|
+
before_param_name = resolved_names[param_idx]
|
|
267
|
+
param_idx += 1
|
|
268
|
+
conditions.append(
|
|
269
|
+
exp.LTE(this=exp.column(self.field_name), expression=exp.Placeholder(this=before_param_name))
|
|
270
|
+
)
|
|
271
|
+
result = result.add_named_parameter(before_param_name, self.on_or_before)
|
|
272
|
+
|
|
273
|
+
if self.on_or_after:
|
|
274
|
+
after_param_name = resolved_names[param_idx]
|
|
275
|
+
conditions.append(
|
|
276
|
+
exp.GTE(this=exp.column(self.field_name), expression=exp.Placeholder(this=after_param_name))
|
|
277
|
+
)
|
|
278
|
+
result = result.add_named_parameter(after_param_name, self.on_or_after)
|
|
279
|
+
|
|
280
|
+
final_condition = conditions[0]
|
|
281
|
+
for cond in conditions[1:]:
|
|
282
|
+
final_condition = exp.And(this=final_condition, expression=cond)
|
|
283
|
+
return result.where(final_condition)
|
|
284
|
+
|
|
285
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
286
|
+
"""Return cache key for this filter configuration."""
|
|
287
|
+
return ("OnBeforeAfterFilter", self.field_name, self.on_or_before, self.on_or_after)
|
|
288
|
+
|
|
289
|
+
|
|
290
|
+
class InAnyFilter(StatementFilter, ABC, Generic[T]):
|
|
291
|
+
"""Base class for collection-based filters that support ANY operations."""
|
|
292
|
+
|
|
293
|
+
__slots__ = ()
|
|
294
|
+
|
|
295
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
296
|
+
raise NotImplementedError
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
class InCollectionFilter(InAnyFilter[T]):
|
|
300
|
+
"""Filter for IN clause queries.
|
|
301
|
+
|
|
302
|
+
Constructs WHERE ... IN (...) clauses.
|
|
303
|
+
"""
|
|
304
|
+
|
|
305
|
+
__slots__ = ("_field_name", "_values")
|
|
306
|
+
|
|
307
|
+
def __init__(self, field_name: str, values: abc.Collection[T] | None = None) -> None:
|
|
308
|
+
self._field_name = field_name
|
|
309
|
+
self._values = values
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def field_name(self) -> str:
|
|
313
|
+
return self._field_name
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def values(self) -> abc.Collection[T] | None:
|
|
317
|
+
return self._values
|
|
318
|
+
|
|
319
|
+
def get_param_names(self) -> "list[str]":
|
|
320
|
+
"""Get parameter names without storing them."""
|
|
321
|
+
if not self.values:
|
|
322
|
+
return []
|
|
323
|
+
return [f"{self.field_name}_in_{i}" for i, _ in enumerate(self.values)]
|
|
324
|
+
|
|
325
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
326
|
+
"""Extract filter parameters."""
|
|
327
|
+
named_parameters = {}
|
|
328
|
+
if self.values:
|
|
329
|
+
param_names = self.get_param_names()
|
|
330
|
+
for i, value in enumerate(self.values):
|
|
331
|
+
named_parameters[param_names[i]] = value
|
|
332
|
+
return [], named_parameters
|
|
333
|
+
|
|
334
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
335
|
+
if self.values is None:
|
|
336
|
+
return statement
|
|
337
|
+
|
|
338
|
+
if not self.values:
|
|
339
|
+
return statement.where(exp.false())
|
|
340
|
+
|
|
341
|
+
resolved_names = self._resolve_parameter_conflicts(statement, self.get_param_names())
|
|
342
|
+
|
|
343
|
+
placeholder_expressions: list[exp.Placeholder] = [
|
|
344
|
+
exp.Placeholder(this=param_name) for param_name in resolved_names
|
|
345
|
+
]
|
|
346
|
+
|
|
347
|
+
result = statement.where(exp.In(this=exp.column(self.field_name), expressions=placeholder_expressions))
|
|
348
|
+
|
|
349
|
+
for resolved_name, value in zip(resolved_names, self.values, strict=False):
|
|
350
|
+
result = result.add_named_parameter(resolved_name, value)
|
|
351
|
+
return result
|
|
352
|
+
|
|
353
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
354
|
+
"""Return cache key for this filter configuration."""
|
|
355
|
+
values_tuple = tuple(self.values) if self.values is not None else None
|
|
356
|
+
return ("InCollectionFilter", self.field_name, values_tuple)
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
class NotInCollectionFilter(InAnyFilter[T]):
|
|
360
|
+
"""Filter for NOT IN clause queries.
|
|
361
|
+
|
|
362
|
+
Constructs WHERE ... NOT IN (...) clauses.
|
|
363
|
+
"""
|
|
364
|
+
|
|
365
|
+
__slots__ = ("_field_name", "_values")
|
|
366
|
+
|
|
367
|
+
def __init__(self, field_name: str, values: abc.Collection[T] | None = None) -> None:
|
|
368
|
+
self._field_name = field_name
|
|
369
|
+
self._values = values
|
|
370
|
+
|
|
371
|
+
@property
|
|
372
|
+
def field_name(self) -> str:
|
|
373
|
+
return self._field_name
|
|
374
|
+
|
|
375
|
+
@property
|
|
376
|
+
def values(self) -> abc.Collection[T] | None:
|
|
377
|
+
return self._values
|
|
378
|
+
|
|
379
|
+
def get_param_names(self) -> "list[str]":
|
|
380
|
+
"""Get parameter names without storing them."""
|
|
381
|
+
if not self.values:
|
|
382
|
+
return []
|
|
383
|
+
# Use object id to ensure uniqueness between instances
|
|
384
|
+
return [f"{self.field_name}_notin_{i}_{id(self)}" for i, _ in enumerate(self.values)]
|
|
385
|
+
|
|
386
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
387
|
+
"""Extract filter parameters."""
|
|
388
|
+
named_parameters = {}
|
|
389
|
+
if self.values:
|
|
390
|
+
param_names = self.get_param_names()
|
|
391
|
+
for i, value in enumerate(self.values):
|
|
392
|
+
named_parameters[param_names[i]] = value
|
|
393
|
+
return [], named_parameters
|
|
394
|
+
|
|
395
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
396
|
+
if self.values is None or not self.values:
|
|
397
|
+
return statement
|
|
398
|
+
|
|
399
|
+
resolved_names = self._resolve_parameter_conflicts(statement, self.get_param_names())
|
|
400
|
+
|
|
401
|
+
placeholder_expressions: list[exp.Placeholder] = [
|
|
402
|
+
exp.Placeholder(this=param_name) for param_name in resolved_names
|
|
403
|
+
]
|
|
404
|
+
|
|
405
|
+
result = statement.where(
|
|
406
|
+
exp.Not(this=exp.In(this=exp.column(self.field_name), expressions=placeholder_expressions))
|
|
407
|
+
)
|
|
408
|
+
|
|
409
|
+
for resolved_name, value in zip(resolved_names, self.values, strict=False):
|
|
410
|
+
result = result.add_named_parameter(resolved_name, value)
|
|
411
|
+
return result
|
|
412
|
+
|
|
413
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
414
|
+
"""Return cache key for this filter configuration."""
|
|
415
|
+
values_tuple = tuple(self.values) if self.values is not None else None
|
|
416
|
+
return ("NotInCollectionFilter", self.field_name, values_tuple)
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
class AnyCollectionFilter(InAnyFilter[T]):
|
|
420
|
+
"""Filter for PostgreSQL-style ANY clause queries.
|
|
421
|
+
|
|
422
|
+
Constructs WHERE column_name = ANY (array_expression) clauses.
|
|
423
|
+
"""
|
|
424
|
+
|
|
425
|
+
__slots__ = ("_field_name", "_values")
|
|
426
|
+
|
|
427
|
+
def __init__(self, field_name: str, values: abc.Collection[T] | None = None) -> None:
|
|
428
|
+
self._field_name = field_name
|
|
429
|
+
self._values = values
|
|
430
|
+
|
|
431
|
+
@property
|
|
432
|
+
def field_name(self) -> str:
|
|
433
|
+
return self._field_name
|
|
434
|
+
|
|
435
|
+
@property
|
|
436
|
+
def values(self) -> abc.Collection[T] | None:
|
|
437
|
+
return self._values
|
|
438
|
+
|
|
439
|
+
def get_param_names(self) -> "list[str]":
|
|
440
|
+
"""Get parameter names without storing them."""
|
|
441
|
+
if not self.values:
|
|
442
|
+
return []
|
|
443
|
+
return [f"{self.field_name}_any_{i}" for i, _ in enumerate(self.values)]
|
|
444
|
+
|
|
445
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
446
|
+
"""Extract filter parameters."""
|
|
447
|
+
named_parameters = {}
|
|
448
|
+
if self.values:
|
|
449
|
+
param_names = self.get_param_names()
|
|
450
|
+
for i, value in enumerate(self.values):
|
|
451
|
+
named_parameters[param_names[i]] = value
|
|
452
|
+
return [], named_parameters
|
|
453
|
+
|
|
454
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
455
|
+
if self.values is None:
|
|
456
|
+
return statement
|
|
457
|
+
|
|
458
|
+
if not self.values:
|
|
459
|
+
return statement.where(exp.false())
|
|
460
|
+
|
|
461
|
+
resolved_names = self._resolve_parameter_conflicts(statement, self.get_param_names())
|
|
462
|
+
|
|
463
|
+
placeholder_expressions: list[exp.Expression] = [
|
|
464
|
+
exp.Placeholder(this=param_name) for param_name in resolved_names
|
|
465
|
+
]
|
|
466
|
+
|
|
467
|
+
array_expr = exp.Array(expressions=placeholder_expressions)
|
|
468
|
+
result = statement.where(exp.EQ(this=exp.column(self.field_name), expression=exp.Any(this=array_expr)))
|
|
469
|
+
|
|
470
|
+
for resolved_name, value in zip(resolved_names, self.values, strict=False):
|
|
471
|
+
result = result.add_named_parameter(resolved_name, value)
|
|
472
|
+
return result
|
|
473
|
+
|
|
474
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
475
|
+
"""Return cache key for this filter configuration."""
|
|
476
|
+
values_tuple = tuple(self.values) if self.values is not None else None
|
|
477
|
+
return ("AnyCollectionFilter", self.field_name, values_tuple)
|
|
478
|
+
|
|
479
|
+
|
|
480
|
+
class NotAnyCollectionFilter(InAnyFilter[T]):
|
|
481
|
+
"""Filter for PostgreSQL-style NOT ANY clause queries.
|
|
482
|
+
|
|
483
|
+
Constructs WHERE NOT (column_name = ANY (array_expression)) clauses.
|
|
484
|
+
"""
|
|
485
|
+
|
|
486
|
+
__slots__ = ("_field_name", "_values")
|
|
487
|
+
|
|
488
|
+
def __init__(self, field_name: str, values: abc.Collection[T] | None = None) -> None:
|
|
489
|
+
self._field_name = field_name
|
|
490
|
+
self._values = values
|
|
491
|
+
|
|
492
|
+
@property
|
|
493
|
+
def field_name(self) -> str:
|
|
494
|
+
return self._field_name
|
|
495
|
+
|
|
496
|
+
@property
|
|
497
|
+
def values(self) -> abc.Collection[T] | None:
|
|
498
|
+
return self._values
|
|
499
|
+
|
|
500
|
+
def get_param_names(self) -> "list[str]":
|
|
501
|
+
"""Get parameter names without storing them."""
|
|
502
|
+
if not self.values:
|
|
503
|
+
return []
|
|
504
|
+
return [f"{self.field_name}_not_any_{i}" for i, _ in enumerate(self.values)]
|
|
505
|
+
|
|
506
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
507
|
+
"""Extract filter parameters."""
|
|
508
|
+
named_parameters = {}
|
|
509
|
+
if self.values:
|
|
510
|
+
param_names = self.get_param_names()
|
|
511
|
+
for i, value in enumerate(self.values):
|
|
512
|
+
named_parameters[param_names[i]] = value
|
|
513
|
+
return [], named_parameters
|
|
514
|
+
|
|
515
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
516
|
+
if self.values is None or not self.values:
|
|
517
|
+
return statement
|
|
518
|
+
|
|
519
|
+
resolved_names = self._resolve_parameter_conflicts(statement, self.get_param_names())
|
|
520
|
+
|
|
521
|
+
placeholder_expressions: list[exp.Expression] = [
|
|
522
|
+
exp.Placeholder(this=param_name) for param_name in resolved_names
|
|
523
|
+
]
|
|
524
|
+
|
|
525
|
+
array_expr = exp.Array(expressions=placeholder_expressions)
|
|
526
|
+
condition = exp.EQ(this=exp.column(self.field_name), expression=exp.Any(this=array_expr))
|
|
527
|
+
result = statement.where(exp.Not(this=condition))
|
|
528
|
+
|
|
529
|
+
for resolved_name, value in zip(resolved_names, self.values, strict=False):
|
|
530
|
+
result = result.add_named_parameter(resolved_name, value)
|
|
531
|
+
return result
|
|
532
|
+
|
|
533
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
534
|
+
"""Return cache key for this filter configuration."""
|
|
535
|
+
values_tuple = tuple(self.values) if self.values is not None else None
|
|
536
|
+
return ("NotAnyCollectionFilter", self.field_name, values_tuple)
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
class PaginationFilter(StatementFilter, ABC):
|
|
540
|
+
"""Base class for pagination-related filters."""
|
|
541
|
+
|
|
542
|
+
__slots__ = ()
|
|
543
|
+
|
|
544
|
+
@abstractmethod
|
|
545
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
546
|
+
raise NotImplementedError
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
class LimitOffsetFilter(PaginationFilter):
|
|
550
|
+
"""Filter for LIMIT and OFFSET clauses.
|
|
551
|
+
|
|
552
|
+
Adds pagination support through LIMIT/OFFSET SQL clauses.
|
|
553
|
+
"""
|
|
554
|
+
|
|
555
|
+
__slots__ = ("_limit", "_offset")
|
|
556
|
+
|
|
557
|
+
def __init__(self, limit: int, offset: int) -> None:
|
|
558
|
+
self._limit = limit
|
|
559
|
+
self._offset = offset
|
|
560
|
+
|
|
561
|
+
@property
|
|
562
|
+
def limit(self) -> int:
|
|
563
|
+
return self._limit
|
|
564
|
+
|
|
565
|
+
@property
|
|
566
|
+
def offset(self) -> int:
|
|
567
|
+
return self._offset
|
|
568
|
+
|
|
569
|
+
def get_param_names(self) -> "list[str]":
|
|
570
|
+
"""Get parameter names without storing them."""
|
|
571
|
+
return ["limit", "offset"]
|
|
572
|
+
|
|
573
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
574
|
+
"""Extract filter parameters."""
|
|
575
|
+
param_names = self.get_param_names()
|
|
576
|
+
return [], {param_names[0]: self.limit, param_names[1]: self.offset}
|
|
577
|
+
|
|
578
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
579
|
+
resolved_names = self._resolve_parameter_conflicts(statement, self.get_param_names())
|
|
580
|
+
limit_param_name, offset_param_name = resolved_names
|
|
581
|
+
|
|
582
|
+
limit_placeholder = exp.Placeholder(this=limit_param_name)
|
|
583
|
+
offset_placeholder = exp.Placeholder(this=offset_param_name)
|
|
584
|
+
|
|
585
|
+
if statement.statement_expression is not None:
|
|
586
|
+
current_statement = statement.statement_expression.copy()
|
|
587
|
+
elif not statement.statement_config.enable_parsing:
|
|
588
|
+
current_statement = exp.Select().from_(f"({statement.raw_sql})")
|
|
589
|
+
else:
|
|
590
|
+
try:
|
|
591
|
+
current_statement = sqlglot.parse_one(statement.raw_sql, dialect=statement.dialect)
|
|
592
|
+
except Exception:
|
|
593
|
+
current_statement = exp.Select().from_(f"({statement.raw_sql})")
|
|
594
|
+
|
|
595
|
+
if isinstance(current_statement, exp.Select):
|
|
596
|
+
new_statement = current_statement.limit(limit_placeholder).offset(offset_placeholder)
|
|
597
|
+
else:
|
|
598
|
+
new_statement = exp.Select().from_(current_statement).limit(limit_placeholder).offset(offset_placeholder)
|
|
599
|
+
|
|
600
|
+
result = statement.copy(statement=new_statement)
|
|
601
|
+
result = result.add_named_parameter(limit_param_name, self.limit)
|
|
602
|
+
return result.add_named_parameter(offset_param_name, self.offset)
|
|
603
|
+
|
|
604
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
605
|
+
"""Return cache key for this filter configuration."""
|
|
606
|
+
return ("LimitOffsetFilter", self.limit, self.offset)
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
class OrderByFilter(StatementFilter):
|
|
610
|
+
"""Filter for ORDER BY clauses.
|
|
611
|
+
|
|
612
|
+
Adds sorting capability to SQL queries.
|
|
613
|
+
"""
|
|
614
|
+
|
|
615
|
+
__slots__ = ("_field_name", "_sort_order")
|
|
616
|
+
|
|
617
|
+
def __init__(self, field_name: str, sort_order: Literal["asc", "desc"] = "asc") -> None:
|
|
618
|
+
self._field_name = field_name
|
|
619
|
+
self._sort_order = sort_order
|
|
620
|
+
|
|
621
|
+
@property
|
|
622
|
+
def field_name(self) -> str:
|
|
623
|
+
return self._field_name
|
|
624
|
+
|
|
625
|
+
@property
|
|
626
|
+
def sort_order(self) -> Literal["asc", "desc"]:
|
|
627
|
+
return self._sort_order # pyright: ignore
|
|
628
|
+
|
|
629
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
630
|
+
"""Extract filter parameters."""
|
|
631
|
+
return [], {}
|
|
632
|
+
|
|
633
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
634
|
+
converted_sort_order = self.sort_order.lower()
|
|
635
|
+
if converted_sort_order not in {"asc", "desc"}:
|
|
636
|
+
converted_sort_order = "asc"
|
|
637
|
+
|
|
638
|
+
col_expr = exp.column(self.field_name)
|
|
639
|
+
order_expr = col_expr.desc() if converted_sort_order == "desc" else col_expr.asc()
|
|
640
|
+
|
|
641
|
+
if statement.statement_expression is not None:
|
|
642
|
+
current_statement = statement.statement_expression.copy()
|
|
643
|
+
elif not statement.statement_config.enable_parsing:
|
|
644
|
+
current_statement = exp.Select().from_(f"({statement.raw_sql})")
|
|
645
|
+
else:
|
|
646
|
+
try:
|
|
647
|
+
current_statement = sqlglot.parse_one(statement.raw_sql, dialect=statement.dialect)
|
|
648
|
+
except Exception:
|
|
649
|
+
current_statement = exp.Select().from_(f"({statement.raw_sql})")
|
|
650
|
+
|
|
651
|
+
if isinstance(current_statement, exp.Select):
|
|
652
|
+
new_statement = current_statement.order_by(order_expr)
|
|
653
|
+
else:
|
|
654
|
+
new_statement = exp.Select().from_(current_statement).order_by(order_expr)
|
|
655
|
+
|
|
656
|
+
return statement.copy(statement=new_statement)
|
|
657
|
+
|
|
658
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
659
|
+
"""Return cache key for this filter configuration."""
|
|
660
|
+
return ("OrderByFilter", self.field_name, self.sort_order)
|
|
661
|
+
|
|
662
|
+
|
|
663
|
+
class SearchFilter(StatementFilter):
|
|
664
|
+
"""Filter for text search queries.
|
|
665
|
+
|
|
666
|
+
Constructs WHERE field_name LIKE '%value%' clauses.
|
|
667
|
+
"""
|
|
668
|
+
|
|
669
|
+
__slots__ = ("_field_name", "_ignore_case", "_value")
|
|
670
|
+
|
|
671
|
+
def __init__(self, field_name: str | set[str], value: str | None, ignore_case: bool | None = False) -> None:
|
|
672
|
+
self._field_name = field_name
|
|
673
|
+
self._value = value
|
|
674
|
+
self._ignore_case = ignore_case
|
|
675
|
+
|
|
676
|
+
@property
|
|
677
|
+
def field_name(self) -> "str | set[str]":
|
|
678
|
+
return self._field_name
|
|
679
|
+
|
|
680
|
+
@property
|
|
681
|
+
def value(self) -> str | None:
|
|
682
|
+
return self._value
|
|
683
|
+
|
|
684
|
+
@property
|
|
685
|
+
def ignore_case(self) -> bool | None:
|
|
686
|
+
return self._ignore_case
|
|
687
|
+
|
|
688
|
+
def get_param_name(self) -> str | None:
|
|
689
|
+
"""Get parameter name without storing it."""
|
|
690
|
+
if not self.value:
|
|
691
|
+
return None
|
|
692
|
+
if isinstance(self.field_name, str):
|
|
693
|
+
return f"{self.field_name}_search"
|
|
694
|
+
return "search_value"
|
|
695
|
+
|
|
696
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
697
|
+
"""Extract filter parameters."""
|
|
698
|
+
named_parameters = {}
|
|
699
|
+
param_name = self.get_param_name()
|
|
700
|
+
if self.value and param_name:
|
|
701
|
+
search_value_with_wildcards = f"%{self.value}%"
|
|
702
|
+
named_parameters[param_name] = search_value_with_wildcards
|
|
703
|
+
return [], named_parameters
|
|
704
|
+
|
|
705
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
706
|
+
param_name = self.get_param_name()
|
|
707
|
+
if not self.value or not param_name:
|
|
708
|
+
return statement
|
|
709
|
+
|
|
710
|
+
resolved_names = self._resolve_parameter_conflicts(statement, [param_name])
|
|
711
|
+
param_name = resolved_names[0]
|
|
712
|
+
|
|
713
|
+
pattern_expr = exp.Placeholder(this=param_name)
|
|
714
|
+
like_op = exp.ILike if self.ignore_case else exp.Like
|
|
715
|
+
|
|
716
|
+
if isinstance(self.field_name, str):
|
|
717
|
+
result = statement.where(like_op(this=exp.column(self.field_name), expression=pattern_expr))
|
|
718
|
+
elif isinstance(self.field_name, set) and self.field_name:
|
|
719
|
+
field_conditions: list[Condition] = [
|
|
720
|
+
like_op(this=exp.column(field), expression=pattern_expr) for field in self.field_name
|
|
721
|
+
]
|
|
722
|
+
if not field_conditions:
|
|
723
|
+
return statement
|
|
724
|
+
|
|
725
|
+
final_condition: Condition = field_conditions[0]
|
|
726
|
+
for cond in field_conditions[1:]:
|
|
727
|
+
final_condition = exp.Or(this=final_condition, expression=cond)
|
|
728
|
+
result = statement.where(final_condition)
|
|
729
|
+
else:
|
|
730
|
+
result = statement
|
|
731
|
+
|
|
732
|
+
search_value_with_wildcards = f"%{self.value}%"
|
|
733
|
+
return result.add_named_parameter(param_name, search_value_with_wildcards)
|
|
734
|
+
|
|
735
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
736
|
+
"""Return cache key for this filter configuration."""
|
|
737
|
+
field_names = tuple(sorted(self.field_name)) if isinstance(self.field_name, set) else self.field_name
|
|
738
|
+
return ("SearchFilter", field_names, self.value, self.ignore_case)
|
|
739
|
+
|
|
740
|
+
|
|
741
|
+
class NullFilter(StatementFilter):
|
|
742
|
+
"""Filter for IS NULL queries.
|
|
743
|
+
|
|
744
|
+
Constructs WHERE field_name IS NULL clauses.
|
|
745
|
+
"""
|
|
746
|
+
|
|
747
|
+
__slots__ = ("_field_name",)
|
|
748
|
+
|
|
749
|
+
def __init__(self, field_name: str) -> None:
|
|
750
|
+
self._field_name = field_name
|
|
751
|
+
|
|
752
|
+
@property
|
|
753
|
+
def field_name(self) -> str:
|
|
754
|
+
return self._field_name
|
|
755
|
+
|
|
756
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
757
|
+
"""Extract filter parameters.
|
|
758
|
+
|
|
759
|
+
Returns empty parameters since IS NULL requires no values.
|
|
760
|
+
"""
|
|
761
|
+
return [], {}
|
|
762
|
+
|
|
763
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
764
|
+
"""Apply IS NULL filter to SQL expression."""
|
|
765
|
+
col_expr = exp.column(self.field_name)
|
|
766
|
+
is_null_condition = exp.Is(this=col_expr, expression=exp.Null())
|
|
767
|
+
return statement.where(is_null_condition)
|
|
768
|
+
|
|
769
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
770
|
+
"""Return cache key for this filter configuration."""
|
|
771
|
+
return ("NullFilter", self.field_name)
|
|
772
|
+
|
|
773
|
+
|
|
774
|
+
class NotNullFilter(StatementFilter):
|
|
775
|
+
"""Filter for IS NOT NULL queries.
|
|
776
|
+
|
|
777
|
+
Constructs WHERE field_name IS NOT NULL clauses.
|
|
778
|
+
"""
|
|
779
|
+
|
|
780
|
+
__slots__ = ("_field_name",)
|
|
781
|
+
|
|
782
|
+
def __init__(self, field_name: str) -> None:
|
|
783
|
+
self._field_name = field_name
|
|
784
|
+
|
|
785
|
+
@property
|
|
786
|
+
def field_name(self) -> str:
|
|
787
|
+
return self._field_name
|
|
788
|
+
|
|
789
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
790
|
+
"""Extract filter parameters.
|
|
791
|
+
|
|
792
|
+
Returns empty parameters since IS NOT NULL requires no values.
|
|
793
|
+
"""
|
|
794
|
+
return [], {}
|
|
795
|
+
|
|
796
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
797
|
+
"""Apply IS NOT NULL filter to SQL expression."""
|
|
798
|
+
col_expr = exp.column(self.field_name)
|
|
799
|
+
is_null_condition = exp.Is(this=col_expr, expression=exp.Null())
|
|
800
|
+
is_not_null_condition = exp.Not(this=is_null_condition)
|
|
801
|
+
return statement.where(is_not_null_condition)
|
|
802
|
+
|
|
803
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
804
|
+
"""Return cache key for this filter configuration."""
|
|
805
|
+
return ("NotNullFilter", self.field_name)
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
class NotInSearchFilter(SearchFilter):
|
|
809
|
+
"""Filter for negated text search queries.
|
|
810
|
+
|
|
811
|
+
Constructs WHERE field_name NOT LIKE '%value%' clauses.
|
|
812
|
+
"""
|
|
813
|
+
|
|
814
|
+
def get_param_name(self) -> str | None:
|
|
815
|
+
"""Get parameter name without storing it."""
|
|
816
|
+
if not self.value:
|
|
817
|
+
return None
|
|
818
|
+
if isinstance(self.field_name, str):
|
|
819
|
+
return f"{self.field_name}_not_search"
|
|
820
|
+
return "not_search_value"
|
|
821
|
+
|
|
822
|
+
def extract_parameters(self) -> "tuple[list[Any], dict[str, Any]]":
|
|
823
|
+
"""Extract filter parameters."""
|
|
824
|
+
named_parameters = {}
|
|
825
|
+
param_name = self.get_param_name()
|
|
826
|
+
if self.value and param_name:
|
|
827
|
+
search_value_with_wildcards = f"%{self.value}%"
|
|
828
|
+
named_parameters[param_name] = search_value_with_wildcards
|
|
829
|
+
return [], named_parameters
|
|
830
|
+
|
|
831
|
+
def append_to_statement(self, statement: "SQL") -> "SQL":
|
|
832
|
+
param_name = self.get_param_name()
|
|
833
|
+
if not self.value or not param_name:
|
|
834
|
+
return statement
|
|
835
|
+
|
|
836
|
+
resolved_names = self._resolve_parameter_conflicts(statement, [param_name])
|
|
837
|
+
param_name = resolved_names[0]
|
|
838
|
+
|
|
839
|
+
pattern_expr = exp.Placeholder(this=param_name)
|
|
840
|
+
like_op = exp.ILike if self.ignore_case else exp.Like
|
|
841
|
+
|
|
842
|
+
result = statement
|
|
843
|
+
if isinstance(self.field_name, str):
|
|
844
|
+
result = statement.where(exp.Not(this=like_op(this=exp.column(self.field_name), expression=pattern_expr)))
|
|
845
|
+
elif isinstance(self.field_name, set) and self.field_name:
|
|
846
|
+
field_conditions: list[Condition] = [
|
|
847
|
+
exp.Not(this=like_op(this=exp.column(field), expression=pattern_expr)) for field in self.field_name
|
|
848
|
+
]
|
|
849
|
+
if not field_conditions:
|
|
850
|
+
return statement
|
|
851
|
+
|
|
852
|
+
final_condition: Condition = field_conditions[0]
|
|
853
|
+
if len(field_conditions) > 1:
|
|
854
|
+
for cond in field_conditions[1:]:
|
|
855
|
+
final_condition = exp.And(this=final_condition, expression=cond)
|
|
856
|
+
result = statement.where(final_condition)
|
|
857
|
+
|
|
858
|
+
search_value_with_wildcards = f"%{self.value}%"
|
|
859
|
+
return result.add_named_parameter(param_name, search_value_with_wildcards)
|
|
860
|
+
|
|
861
|
+
def get_cache_key(self) -> "tuple[Any, ...]":
|
|
862
|
+
"""Return cache key for this filter configuration."""
|
|
863
|
+
field_names = tuple(sorted(self.field_name)) if isinstance(self.field_name, set) else self.field_name
|
|
864
|
+
return ("NotInSearchFilter", field_names, self.value, self.ignore_case)
|
|
865
|
+
|
|
866
|
+
|
|
867
|
+
class OffsetPagination(Generic[T]):
|
|
868
|
+
"""Container for data returned using limit/offset pagination."""
|
|
869
|
+
|
|
870
|
+
__slots__ = ("items", "limit", "offset", "total")
|
|
871
|
+
|
|
872
|
+
items: Sequence[T]
|
|
873
|
+
limit: int
|
|
874
|
+
offset: int
|
|
875
|
+
total: int
|
|
876
|
+
|
|
877
|
+
def __init__(self, items: Sequence[T], limit: int, offset: int, total: int) -> None:
|
|
878
|
+
"""Initialize OffsetPagination.
|
|
879
|
+
|
|
880
|
+
Args:
|
|
881
|
+
items: List of data being sent as part of the response.
|
|
882
|
+
limit: Maximal number of items to send.
|
|
883
|
+
offset: Offset from the beginning of the query. Identical to an index.
|
|
884
|
+
total: Total number of items.
|
|
885
|
+
"""
|
|
886
|
+
self.items = items
|
|
887
|
+
self.limit = limit
|
|
888
|
+
self.offset = offset
|
|
889
|
+
self.total = total
|
|
890
|
+
|
|
891
|
+
|
|
892
|
+
def apply_filter(statement: "SQL", filter_obj: StatementFilter) -> "SQL":
|
|
893
|
+
"""Apply a statement filter to a SQL query object.
|
|
894
|
+
|
|
895
|
+
Args:
|
|
896
|
+
statement: The SQL query object to modify.
|
|
897
|
+
filter_obj: The filter to apply.
|
|
898
|
+
|
|
899
|
+
Returns:
|
|
900
|
+
The modified query object.
|
|
901
|
+
"""
|
|
902
|
+
return filter_obj.append_to_statement(statement)
|
|
903
|
+
|
|
904
|
+
|
|
905
|
+
FilterTypes: TypeAlias = (
|
|
906
|
+
BeforeAfterFilter
|
|
907
|
+
| OnBeforeAfterFilter
|
|
908
|
+
| InCollectionFilter[Any]
|
|
909
|
+
| LimitOffsetFilter
|
|
910
|
+
| OrderByFilter
|
|
911
|
+
| SearchFilter
|
|
912
|
+
| NotInCollectionFilter[Any]
|
|
913
|
+
| NotInSearchFilter
|
|
914
|
+
| AnyCollectionFilter[Any]
|
|
915
|
+
| NotAnyCollectionFilter[Any]
|
|
916
|
+
| NullFilter
|
|
917
|
+
| NotNullFilter
|
|
918
|
+
)
|
|
919
|
+
|
|
920
|
+
|
|
921
|
+
def create_filters(filters: "list[StatementFilter]") -> "tuple[StatementFilter, ...]":
|
|
922
|
+
"""Convert mutable filters to immutable tuple.
|
|
923
|
+
|
|
924
|
+
Since StatementFilter classes are now immutable (with read-only properties),
|
|
925
|
+
we just need to convert to a tuple for consistent sharing.
|
|
926
|
+
|
|
927
|
+
Args:
|
|
928
|
+
filters: List of StatementFilter objects (already immutable)
|
|
929
|
+
|
|
930
|
+
Returns:
|
|
931
|
+
Tuple of StatementFilter objects
|
|
932
|
+
"""
|
|
933
|
+
return tuple(filters)
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
def _filter_sort_key(f: "StatementFilter") -> "tuple[str, str]":
|
|
937
|
+
"""Sort key for canonicalizing filters by type and field_name."""
|
|
938
|
+
class_name = type(f).__name__
|
|
939
|
+
field_name = str(f.field_name) if has_field_name(f) else ""
|
|
940
|
+
return (class_name, field_name)
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def canonicalize_filters(filters: "list[StatementFilter]") -> "tuple[StatementFilter, ...]":
|
|
944
|
+
"""Sort filters by type and field_name for consistent hashing.
|
|
945
|
+
|
|
946
|
+
Args:
|
|
947
|
+
filters: List of StatementFilter objects
|
|
948
|
+
|
|
949
|
+
Returns:
|
|
950
|
+
Canonically sorted tuple of filters
|
|
951
|
+
"""
|
|
952
|
+
return tuple(sorted(filters, key=_filter_sort_key))
|