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/compiler.py
ADDED
|
@@ -0,0 +1,954 @@
|
|
|
1
|
+
"""SQL compilation and caching.
|
|
2
|
+
|
|
3
|
+
Components:
|
|
4
|
+
- CompiledSQL: Immutable compilation result
|
|
5
|
+
- SQLProcessor: SQL compiler with caching
|
|
6
|
+
- Parameter processing via ParameterProcessor
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import hashlib
|
|
10
|
+
import logging
|
|
11
|
+
from collections import OrderedDict
|
|
12
|
+
from collections.abc import Mapping
|
|
13
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
14
|
+
|
|
15
|
+
import sqlglot
|
|
16
|
+
from mypy_extensions import mypyc_attr
|
|
17
|
+
from sqlglot import expressions as exp
|
|
18
|
+
from sqlglot.errors import ParseError
|
|
19
|
+
|
|
20
|
+
import sqlspec.exceptions
|
|
21
|
+
from sqlspec.core.parameters import (
|
|
22
|
+
ParameterProcessor,
|
|
23
|
+
ParameterProfile,
|
|
24
|
+
fingerprint_parameters,
|
|
25
|
+
validate_parameter_alignment,
|
|
26
|
+
)
|
|
27
|
+
from sqlspec.utils.logging import get_logger, log_with_context
|
|
28
|
+
from sqlspec.utils.type_guards import get_value_attribute
|
|
29
|
+
|
|
30
|
+
if TYPE_CHECKING:
|
|
31
|
+
from sqlspec.core.statement import StatementConfig
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
__all__ = (
|
|
35
|
+
"CompiledSQL",
|
|
36
|
+
"OperationProfile",
|
|
37
|
+
"OperationType",
|
|
38
|
+
"SQLProcessor",
|
|
39
|
+
"is_copy_from_operation",
|
|
40
|
+
"is_copy_operation",
|
|
41
|
+
"is_copy_to_operation",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
logger: "logging.Logger" = get_logger("sqlspec.core.compiler")
|
|
45
|
+
OperationType = Literal[
|
|
46
|
+
"SELECT",
|
|
47
|
+
"INSERT",
|
|
48
|
+
"UPDATE",
|
|
49
|
+
"DELETE",
|
|
50
|
+
"COPY",
|
|
51
|
+
"COPY_FROM",
|
|
52
|
+
"COPY_TO",
|
|
53
|
+
"EXECUTE",
|
|
54
|
+
"SCRIPT",
|
|
55
|
+
"DDL",
|
|
56
|
+
"PRAGMA",
|
|
57
|
+
"MERGE",
|
|
58
|
+
"UNKNOWN",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
OPERATION_TYPE_MAP: "dict[type[exp.Expression], OperationType]" = {
|
|
62
|
+
exp.Select: "SELECT",
|
|
63
|
+
exp.Union: "SELECT",
|
|
64
|
+
exp.Except: "SELECT",
|
|
65
|
+
exp.Intersect: "SELECT",
|
|
66
|
+
exp.With: "SELECT",
|
|
67
|
+
exp.Insert: "INSERT",
|
|
68
|
+
exp.Update: "UPDATE",
|
|
69
|
+
exp.Delete: "DELETE",
|
|
70
|
+
exp.Pragma: "PRAGMA",
|
|
71
|
+
exp.Command: "EXECUTE",
|
|
72
|
+
exp.Create: "DDL",
|
|
73
|
+
exp.Drop: "DDL",
|
|
74
|
+
exp.Alter: "DDL",
|
|
75
|
+
exp.Merge: "MERGE",
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
COPY_OPERATION_TYPES: "tuple[OperationType, ...]" = ("COPY", "COPY_FROM", "COPY_TO")
|
|
79
|
+
|
|
80
|
+
COPY_FROM_OPERATION_TYPES: "tuple[OperationType, ...]" = ("COPY", "COPY_FROM")
|
|
81
|
+
|
|
82
|
+
COPY_TO_OPERATION_TYPES: "tuple[OperationType, ...]" = ("COPY_TO",)
|
|
83
|
+
|
|
84
|
+
ParseCacheEntry = tuple[exp.Expression | None, OperationType, dict[int, str], tuple[bool, bool]]
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def is_copy_operation(operation_type: "OperationType") -> bool:
|
|
88
|
+
"""Determine if the operation corresponds to any PostgreSQL COPY variant.
|
|
89
|
+
|
|
90
|
+
Args:
|
|
91
|
+
operation_type: Operation type detected by the compiler.
|
|
92
|
+
|
|
93
|
+
Returns:
|
|
94
|
+
True when the operation type represents COPY, COPY FROM, or COPY TO.
|
|
95
|
+
"""
|
|
96
|
+
|
|
97
|
+
return operation_type in COPY_OPERATION_TYPES
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def is_copy_from_operation(operation_type: "OperationType") -> bool:
|
|
101
|
+
"""Check if the operation streams data into the database using COPY.
|
|
102
|
+
|
|
103
|
+
Args:
|
|
104
|
+
operation_type: Operation type detected by the compiler.
|
|
105
|
+
|
|
106
|
+
Returns:
|
|
107
|
+
True for COPY operations that read from client input (COPY FROM).
|
|
108
|
+
"""
|
|
109
|
+
|
|
110
|
+
return operation_type in COPY_FROM_OPERATION_TYPES
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def is_copy_to_operation(operation_type: "OperationType") -> bool:
|
|
114
|
+
"""Check if the operation streams data out from the database using COPY.
|
|
115
|
+
|
|
116
|
+
Args:
|
|
117
|
+
operation_type: Operation type detected by the compiler.
|
|
118
|
+
|
|
119
|
+
Returns:
|
|
120
|
+
True for COPY operations that write to client output (COPY TO).
|
|
121
|
+
"""
|
|
122
|
+
|
|
123
|
+
return operation_type in COPY_TO_OPERATION_TYPES
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def _assign_placeholder_position(
|
|
127
|
+
placeholder: "exp.Placeholder", placeholder_positions: "dict[str, int]", placeholder_counter: "list[int]"
|
|
128
|
+
) -> "int | None":
|
|
129
|
+
name_expr = placeholder.name if placeholder.name is not None else None
|
|
130
|
+
if name_expr is not None:
|
|
131
|
+
placeholder_key = str(name_expr)
|
|
132
|
+
else:
|
|
133
|
+
value = placeholder.args.get("this")
|
|
134
|
+
placeholder_key = str(value) if value is not None else placeholder.sql()
|
|
135
|
+
|
|
136
|
+
if not placeholder_key:
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
if placeholder_key not in placeholder_positions:
|
|
140
|
+
placeholder_counter[0] += 1
|
|
141
|
+
placeholder_positions[placeholder_key] = placeholder_counter[0]
|
|
142
|
+
|
|
143
|
+
return placeholder_positions[placeholder_key]
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
147
|
+
class OperationProfile:
|
|
148
|
+
"""Semantic characteristics derived from the parsed SQL expression."""
|
|
149
|
+
|
|
150
|
+
__slots__ = ("modifies_rows", "returns_rows")
|
|
151
|
+
|
|
152
|
+
def __init__(self, returns_rows: bool = False, modifies_rows: bool = False) -> None:
|
|
153
|
+
self.returns_rows = returns_rows
|
|
154
|
+
self.modifies_rows = modifies_rows
|
|
155
|
+
|
|
156
|
+
@classmethod
|
|
157
|
+
def empty(cls) -> "OperationProfile":
|
|
158
|
+
return cls(returns_rows=False, modifies_rows=False)
|
|
159
|
+
|
|
160
|
+
def __repr__(self) -> str:
|
|
161
|
+
return f"OperationProfile(returns_rows={self.returns_rows!r}, modifies_rows={self.modifies_rows!r})"
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
def _is_effectively_empty_parameters(value: Any) -> bool:
|
|
165
|
+
if value is None:
|
|
166
|
+
return True
|
|
167
|
+
if isinstance(value, Mapping):
|
|
168
|
+
return len(value) == 0
|
|
169
|
+
if isinstance(value, (list, tuple, set, frozenset)):
|
|
170
|
+
return len(value) == 0
|
|
171
|
+
return False
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
175
|
+
class CompiledSQL:
|
|
176
|
+
"""Compiled SQL result.
|
|
177
|
+
|
|
178
|
+
Contains the result of SQL compilation with information needed for execution.
|
|
179
|
+
Immutable container holding compiled SQL text, processed parameters, operation
|
|
180
|
+
type, and execution metadata.
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
__slots__ = (
|
|
184
|
+
"_hash",
|
|
185
|
+
"compiled_sql",
|
|
186
|
+
"execution_parameters",
|
|
187
|
+
"expression",
|
|
188
|
+
"operation_profile",
|
|
189
|
+
"operation_type",
|
|
190
|
+
"parameter_casts",
|
|
191
|
+
"parameter_profile",
|
|
192
|
+
"parameter_style",
|
|
193
|
+
"supports_many",
|
|
194
|
+
)
|
|
195
|
+
|
|
196
|
+
operation_type: "OperationType"
|
|
197
|
+
|
|
198
|
+
def __init__(
|
|
199
|
+
self,
|
|
200
|
+
compiled_sql: str,
|
|
201
|
+
execution_parameters: Any,
|
|
202
|
+
operation_type: "OperationType",
|
|
203
|
+
expression: "exp.Expression | None" = None,
|
|
204
|
+
parameter_style: str | None = None,
|
|
205
|
+
supports_many: bool = False,
|
|
206
|
+
parameter_casts: "dict[int, str] | None" = None,
|
|
207
|
+
parameter_profile: "ParameterProfile | None" = None,
|
|
208
|
+
operation_profile: "OperationProfile | None" = None,
|
|
209
|
+
) -> None:
|
|
210
|
+
"""Initialize compiled result.
|
|
211
|
+
|
|
212
|
+
Args:
|
|
213
|
+
compiled_sql: SQL string ready for execution
|
|
214
|
+
execution_parameters: Parameters in driver-specific format
|
|
215
|
+
operation_type: SQL operation type (SELECT, INSERT, etc.)
|
|
216
|
+
expression: SQLGlot AST expression
|
|
217
|
+
parameter_style: Parameter style used in compilation
|
|
218
|
+
supports_many: Whether this supports execute_many operations
|
|
219
|
+
parameter_casts: Mapping of parameter positions to cast types
|
|
220
|
+
parameter_profile: Profile describing detected placeholders
|
|
221
|
+
operation_profile: Profile describing semantic characteristics
|
|
222
|
+
"""
|
|
223
|
+
self.compiled_sql = compiled_sql
|
|
224
|
+
self.execution_parameters = execution_parameters
|
|
225
|
+
self.operation_type = operation_type
|
|
226
|
+
self.expression = expression
|
|
227
|
+
self.parameter_style = parameter_style
|
|
228
|
+
self.supports_many = supports_many
|
|
229
|
+
self.parameter_casts = parameter_casts or {}
|
|
230
|
+
self.parameter_profile = parameter_profile
|
|
231
|
+
self.operation_profile = operation_profile or OperationProfile.empty()
|
|
232
|
+
self._hash: int | None = None
|
|
233
|
+
|
|
234
|
+
def __hash__(self) -> int:
|
|
235
|
+
"""Cached hash value."""
|
|
236
|
+
if self._hash is None:
|
|
237
|
+
param_str = str(self.execution_parameters)
|
|
238
|
+
self._hash = hash((self.compiled_sql, param_str, self.operation_type, self.parameter_style))
|
|
239
|
+
return self._hash
|
|
240
|
+
|
|
241
|
+
def __eq__(self, other: object) -> bool:
|
|
242
|
+
"""Equality comparison."""
|
|
243
|
+
if not isinstance(other, CompiledSQL):
|
|
244
|
+
return False
|
|
245
|
+
return (
|
|
246
|
+
self.compiled_sql == other.compiled_sql
|
|
247
|
+
and self.execution_parameters == other.execution_parameters
|
|
248
|
+
and self.operation_type == other.operation_type
|
|
249
|
+
and self.parameter_style == other.parameter_style
|
|
250
|
+
)
|
|
251
|
+
|
|
252
|
+
def __repr__(self) -> str:
|
|
253
|
+
"""String representation."""
|
|
254
|
+
return (
|
|
255
|
+
f"CompiledSQL(sql={self.compiled_sql!r}, "
|
|
256
|
+
f"params={self.execution_parameters!r}, "
|
|
257
|
+
f"type={self.operation_type!r})"
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
262
|
+
class SQLProcessor:
|
|
263
|
+
"""SQL processor with compilation and caching.
|
|
264
|
+
|
|
265
|
+
Processes SQL statements by compiling them into executable format with
|
|
266
|
+
parameter substitution. Includes LRU-style caching for compilation results
|
|
267
|
+
to avoid re-processing identical statements.
|
|
268
|
+
"""
|
|
269
|
+
|
|
270
|
+
__slots__ = (
|
|
271
|
+
"_cache",
|
|
272
|
+
"_cache_enabled",
|
|
273
|
+
"_cache_hits",
|
|
274
|
+
"_cache_misses",
|
|
275
|
+
"_config",
|
|
276
|
+
"_max_cache_size",
|
|
277
|
+
"_parameter_processor",
|
|
278
|
+
"_parse_cache",
|
|
279
|
+
"_parse_cache_hits",
|
|
280
|
+
"_parse_cache_max_size",
|
|
281
|
+
"_parse_cache_misses",
|
|
282
|
+
)
|
|
283
|
+
|
|
284
|
+
def __init__(
|
|
285
|
+
self,
|
|
286
|
+
config: "StatementConfig",
|
|
287
|
+
max_cache_size: int = 1000,
|
|
288
|
+
parse_cache_size: int | None = None,
|
|
289
|
+
parameter_cache_size: int | None = None,
|
|
290
|
+
validator_cache_size: int | None = None,
|
|
291
|
+
cache_enabled: bool = True,
|
|
292
|
+
) -> None:
|
|
293
|
+
"""Initialize processor.
|
|
294
|
+
|
|
295
|
+
Args:
|
|
296
|
+
config: Statement configuration
|
|
297
|
+
max_cache_size: Maximum number of compilation results to cache
|
|
298
|
+
parse_cache_size: Maximum number of parsed expressions to cache
|
|
299
|
+
parameter_cache_size: Maximum parameter conversion cache entries
|
|
300
|
+
validator_cache_size: Maximum cached parameter metadata entries
|
|
301
|
+
cache_enabled: Toggle compiled SQL caching (parse/parameter caches remain size-driven)
|
|
302
|
+
"""
|
|
303
|
+
self._config = config
|
|
304
|
+
self._cache: OrderedDict[str, CompiledSQL] = OrderedDict()
|
|
305
|
+
self._max_cache_size = max(max_cache_size, 0)
|
|
306
|
+
compiled_cache_active = cache_enabled and config.enable_caching and self._max_cache_size > 0
|
|
307
|
+
self._cache_enabled = compiled_cache_active
|
|
308
|
+
parse_cache_max_size = self._max_cache_size if parse_cache_size is None else parse_cache_size
|
|
309
|
+
self._parse_cache_max_size = max(parse_cache_max_size, 0)
|
|
310
|
+
if not config.enable_caching:
|
|
311
|
+
self._parse_cache_max_size = 0
|
|
312
|
+
parameter_cache = parameter_cache_size if parameter_cache_size is not None else self._parse_cache_max_size
|
|
313
|
+
validator_cache = validator_cache_size if validator_cache_size is not None else parameter_cache
|
|
314
|
+
if not config.enable_caching:
|
|
315
|
+
parameter_cache = 0
|
|
316
|
+
validator_cache = 0
|
|
317
|
+
self._parameter_processor = ParameterProcessor(
|
|
318
|
+
converter=config.parameter_converter,
|
|
319
|
+
validator=config.parameter_validator,
|
|
320
|
+
cache_max_size=parameter_cache,
|
|
321
|
+
validator_cache_max_size=validator_cache,
|
|
322
|
+
)
|
|
323
|
+
self._cache_hits = 0
|
|
324
|
+
self._cache_misses = 0
|
|
325
|
+
self._parse_cache: OrderedDict[
|
|
326
|
+
str, tuple[exp.Expression | None, OperationType, dict[int, str], tuple[bool, bool]]
|
|
327
|
+
] = OrderedDict()
|
|
328
|
+
self._parse_cache_hits = 0
|
|
329
|
+
self._parse_cache_misses = 0
|
|
330
|
+
|
|
331
|
+
def compile(
|
|
332
|
+
self, sql: str, parameters: Any = None, is_many: bool = False, expression: "exp.Expression | None" = None
|
|
333
|
+
) -> CompiledSQL:
|
|
334
|
+
"""Compile SQL statement.
|
|
335
|
+
|
|
336
|
+
Args:
|
|
337
|
+
sql: SQL string for compilation
|
|
338
|
+
parameters: Parameter values for substitution
|
|
339
|
+
is_many: Whether this is for execute_many operation
|
|
340
|
+
expression: Pre-parsed SQLGlot expression to reuse
|
|
341
|
+
|
|
342
|
+
Returns:
|
|
343
|
+
CompiledSQL with execution information
|
|
344
|
+
"""
|
|
345
|
+
if not self._config.enable_caching or not self._cache_enabled:
|
|
346
|
+
return self._compile_uncached(sql, parameters, is_many, expression)
|
|
347
|
+
|
|
348
|
+
cache_key = self._make_cache_key(sql, parameters, is_many)
|
|
349
|
+
|
|
350
|
+
if cache_key in self._cache:
|
|
351
|
+
result = self._cache[cache_key]
|
|
352
|
+
del self._cache[cache_key]
|
|
353
|
+
self._cache[cache_key] = result
|
|
354
|
+
self._cache_hits += 1
|
|
355
|
+
return result
|
|
356
|
+
|
|
357
|
+
self._cache_misses += 1
|
|
358
|
+
result = self._compile_uncached(sql, parameters, is_many, expression)
|
|
359
|
+
|
|
360
|
+
if len(self._cache) >= self._max_cache_size:
|
|
361
|
+
self._cache.popitem(last=False)
|
|
362
|
+
|
|
363
|
+
self._cache[cache_key] = result
|
|
364
|
+
return result
|
|
365
|
+
|
|
366
|
+
def _prepare_parameters(
|
|
367
|
+
self, sql: str, parameters: Any, is_many: bool, dialect_str: "str | None"
|
|
368
|
+
) -> "tuple[str, Any, ParameterProfile, str]":
|
|
369
|
+
"""Process SQL parameters for compilation.
|
|
370
|
+
|
|
371
|
+
Args:
|
|
372
|
+
sql: SQL string.
|
|
373
|
+
parameters: Raw parameters.
|
|
374
|
+
is_many: Whether this is for execute_many.
|
|
375
|
+
dialect_str: Dialect name.
|
|
376
|
+
|
|
377
|
+
Returns:
|
|
378
|
+
Tuple of processed SQL, processed parameters, parameter profile, and SQLGlot SQL.
|
|
379
|
+
"""
|
|
380
|
+
process_result = self._parameter_processor.process(
|
|
381
|
+
sql=sql,
|
|
382
|
+
parameters=parameters,
|
|
383
|
+
config=self._config.parameter_config,
|
|
384
|
+
dialect=dialect_str,
|
|
385
|
+
is_many=is_many,
|
|
386
|
+
wrap_types=self._config.enable_parameter_type_wrapping,
|
|
387
|
+
)
|
|
388
|
+
return (
|
|
389
|
+
process_result.sql,
|
|
390
|
+
process_result.parameters,
|
|
391
|
+
process_result.parameter_profile,
|
|
392
|
+
process_result.sqlglot_sql,
|
|
393
|
+
)
|
|
394
|
+
|
|
395
|
+
def _normalize_expression_override(
|
|
396
|
+
self, expression_override: "exp.Expression | None", sqlglot_sql: str, sql: str
|
|
397
|
+
) -> "exp.Expression | None":
|
|
398
|
+
"""Validate expression overrides against the input SQL.
|
|
399
|
+
|
|
400
|
+
Args:
|
|
401
|
+
expression_override: Pre-parsed SQLGlot expression.
|
|
402
|
+
sqlglot_sql: SQL passed to SQLGlot.
|
|
403
|
+
sql: Original SQL string.
|
|
404
|
+
|
|
405
|
+
Returns:
|
|
406
|
+
Expression override when it is safe to reuse.
|
|
407
|
+
"""
|
|
408
|
+
if expression_override is None:
|
|
409
|
+
return None
|
|
410
|
+
if sqlglot_sql != sql:
|
|
411
|
+
return None
|
|
412
|
+
return expression_override
|
|
413
|
+
|
|
414
|
+
def _parse_expression_uncached(
|
|
415
|
+
self, sqlglot_sql: str, dialect_str: "str | None", expression_override: "exp.Expression | None"
|
|
416
|
+
) -> "tuple[exp.Expression | None, OperationType, dict[int, str], OperationProfile]":
|
|
417
|
+
"""Parse SQL into an expression without cache.
|
|
418
|
+
|
|
419
|
+
Args:
|
|
420
|
+
sqlglot_sql: SQL string for SQLGlot.
|
|
421
|
+
dialect_str: Dialect name.
|
|
422
|
+
expression_override: Pre-parsed SQLGlot expression.
|
|
423
|
+
|
|
424
|
+
Returns:
|
|
425
|
+
Expression details and derived metadata.
|
|
426
|
+
"""
|
|
427
|
+
try:
|
|
428
|
+
if expression_override is not None:
|
|
429
|
+
expression = expression_override
|
|
430
|
+
else:
|
|
431
|
+
expression = sqlglot.parse_one(sqlglot_sql, dialect=dialect_str)
|
|
432
|
+
except ParseError:
|
|
433
|
+
return None, "EXECUTE", {}, OperationProfile.empty()
|
|
434
|
+
else:
|
|
435
|
+
operation_type = self._detect_operation_type(expression)
|
|
436
|
+
parameter_casts = self._detect_parameter_casts(expression)
|
|
437
|
+
operation_profile = self._build_operation_profile(expression, operation_type)
|
|
438
|
+
return expression, operation_type, parameter_casts, operation_profile
|
|
439
|
+
|
|
440
|
+
def _store_parse_cache(
|
|
441
|
+
self,
|
|
442
|
+
parse_cache_key: str,
|
|
443
|
+
expression: "exp.Expression | None",
|
|
444
|
+
operation_type: "OperationType",
|
|
445
|
+
parameter_casts: "dict[int, str]",
|
|
446
|
+
operation_profile: "OperationProfile",
|
|
447
|
+
) -> None:
|
|
448
|
+
"""Store parsed expression details in cache.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
parse_cache_key: Cache key for the parsed SQL.
|
|
452
|
+
expression: Parsed SQLGlot expression.
|
|
453
|
+
operation_type: Detected operation type.
|
|
454
|
+
parameter_casts: Parameter cast mappings.
|
|
455
|
+
operation_profile: Operation metadata.
|
|
456
|
+
"""
|
|
457
|
+
if len(self._parse_cache) >= self._parse_cache_max_size:
|
|
458
|
+
self._parse_cache.popitem(last=False)
|
|
459
|
+
cache_expression = expression.copy() if expression is not None else None
|
|
460
|
+
self._parse_cache[parse_cache_key] = (
|
|
461
|
+
cache_expression,
|
|
462
|
+
operation_type,
|
|
463
|
+
parameter_casts,
|
|
464
|
+
(operation_profile.returns_rows, operation_profile.modifies_rows),
|
|
465
|
+
)
|
|
466
|
+
|
|
467
|
+
def _unpack_parse_cache_entry(
|
|
468
|
+
self, parse_cache_entry: "ParseCacheEntry"
|
|
469
|
+
) -> "tuple[exp.Expression | None, OperationType, dict[int, str], OperationProfile]":
|
|
470
|
+
"""Expand cached parse results into runtime objects.
|
|
471
|
+
|
|
472
|
+
Args:
|
|
473
|
+
parse_cache_entry: Cached parse entry.
|
|
474
|
+
|
|
475
|
+
Returns:
|
|
476
|
+
Parsed expression metadata.
|
|
477
|
+
"""
|
|
478
|
+
cached_expression, cached_operation, cached_casts, cached_profile = parse_cache_entry
|
|
479
|
+
expression = cached_expression.copy() if cached_expression is not None else None
|
|
480
|
+
operation_profile = OperationProfile(returns_rows=cached_profile[0], modifies_rows=cached_profile[1])
|
|
481
|
+
return expression, cached_operation, dict(cached_casts), operation_profile
|
|
482
|
+
|
|
483
|
+
def _resolve_expression(
|
|
484
|
+
self, sqlglot_sql: str, dialect_str: "str | None", expression_override: "exp.Expression | None"
|
|
485
|
+
) -> "tuple[exp.Expression | None, OperationType, dict[int, str], OperationProfile, str | None, ParseCacheEntry | None]":
|
|
486
|
+
"""Resolve an SQLGlot expression with caching.
|
|
487
|
+
|
|
488
|
+
Args:
|
|
489
|
+
sqlglot_sql: SQL string for SQLGlot.
|
|
490
|
+
dialect_str: Dialect name.
|
|
491
|
+
expression_override: Pre-parsed SQLGlot expression.
|
|
492
|
+
|
|
493
|
+
Returns:
|
|
494
|
+
Expression metadata and parse cache information.
|
|
495
|
+
"""
|
|
496
|
+
parse_cache_key = None
|
|
497
|
+
parse_cache_entry = None
|
|
498
|
+
if self._config.enable_caching and self._parse_cache_max_size > 0:
|
|
499
|
+
parse_cache_key = self._make_parse_cache_key(sqlglot_sql, dialect_str)
|
|
500
|
+
parse_cache_entry = self._parse_cache.get(parse_cache_key)
|
|
501
|
+
if parse_cache_entry is not None:
|
|
502
|
+
self._parse_cache_hits += 1
|
|
503
|
+
self._parse_cache.move_to_end(parse_cache_key)
|
|
504
|
+
if parse_cache_entry is None:
|
|
505
|
+
self._parse_cache_misses += 1
|
|
506
|
+
expression, operation_type, parameter_casts, operation_profile = self._parse_expression_uncached(
|
|
507
|
+
sqlglot_sql, dialect_str, expression_override
|
|
508
|
+
)
|
|
509
|
+
if parse_cache_key is not None:
|
|
510
|
+
self._store_parse_cache(parse_cache_key, expression, operation_type, parameter_casts, operation_profile)
|
|
511
|
+
else:
|
|
512
|
+
expression, operation_type, parameter_casts, operation_profile = self._unpack_parse_cache_entry(
|
|
513
|
+
parse_cache_entry
|
|
514
|
+
)
|
|
515
|
+
return expression, operation_type, parameter_casts, operation_profile, parse_cache_key, parse_cache_entry
|
|
516
|
+
|
|
517
|
+
def _apply_ast_transformers(
|
|
518
|
+
self,
|
|
519
|
+
expression: "exp.Expression | None",
|
|
520
|
+
parameters: Any,
|
|
521
|
+
parameter_profile: "ParameterProfile",
|
|
522
|
+
operation_type: "OperationType",
|
|
523
|
+
parameter_casts: "dict[int, str]",
|
|
524
|
+
operation_profile: "OperationProfile",
|
|
525
|
+
parse_cache_key: "str | None",
|
|
526
|
+
parse_cache_entry: "ParseCacheEntry | None",
|
|
527
|
+
expression_override: "exp.Expression | None",
|
|
528
|
+
) -> "tuple[exp.Expression | None, Any, bool, OperationType, dict[int, str], OperationProfile]":
|
|
529
|
+
"""Apply AST transformers and update metadata.
|
|
530
|
+
|
|
531
|
+
Args:
|
|
532
|
+
expression: SQLGlot expression to transform.
|
|
533
|
+
parameters: Execution parameters.
|
|
534
|
+
parameter_profile: Parameter profile metadata.
|
|
535
|
+
operation_type: Current operation type.
|
|
536
|
+
parameter_casts: Current parameter cast mapping.
|
|
537
|
+
operation_profile: Current operation profile.
|
|
538
|
+
parse_cache_key: Parse cache key when used.
|
|
539
|
+
parse_cache_entry: Cached parse entry when available.
|
|
540
|
+
expression_override: Expression override reference.
|
|
541
|
+
|
|
542
|
+
Returns:
|
|
543
|
+
Updated expression metadata and transformation state.
|
|
544
|
+
"""
|
|
545
|
+
statement_transformers = self._config.statement_transformers
|
|
546
|
+
ast_transformer = self._config.parameter_config.ast_transformer
|
|
547
|
+
if expression is None or (not statement_transformers and not ast_transformer):
|
|
548
|
+
return expression, parameters, False, operation_type, parameter_casts, operation_profile
|
|
549
|
+
|
|
550
|
+
should_copy = False
|
|
551
|
+
if parse_cache_key is not None and parse_cache_entry is None:
|
|
552
|
+
should_copy = True
|
|
553
|
+
if expression_override is not None and expression is expression_override:
|
|
554
|
+
should_copy = True
|
|
555
|
+
if should_copy:
|
|
556
|
+
expression = expression.copy()
|
|
557
|
+
|
|
558
|
+
ast_was_transformed = False
|
|
559
|
+
if statement_transformers:
|
|
560
|
+
for transformer in statement_transformers:
|
|
561
|
+
expression, parameters = transformer(expression, parameters)
|
|
562
|
+
ast_was_transformed = True
|
|
563
|
+
if ast_transformer:
|
|
564
|
+
expression, parameters = ast_transformer(expression, parameters, parameter_profile)
|
|
565
|
+
ast_was_transformed = True
|
|
566
|
+
if ast_was_transformed:
|
|
567
|
+
if expression is None:
|
|
568
|
+
return expression, parameters, ast_was_transformed, operation_type, parameter_casts, operation_profile
|
|
569
|
+
operation_type = self._detect_operation_type(expression)
|
|
570
|
+
parameter_casts = self._detect_parameter_casts(expression)
|
|
571
|
+
operation_profile = self._build_operation_profile(expression, operation_type)
|
|
572
|
+
|
|
573
|
+
return expression, parameters, ast_was_transformed, operation_type, parameter_casts, operation_profile
|
|
574
|
+
|
|
575
|
+
def _finalize_compilation(
|
|
576
|
+
self,
|
|
577
|
+
processed_sql: str,
|
|
578
|
+
processed_params: Any,
|
|
579
|
+
expression: "exp.Expression | None",
|
|
580
|
+
parameters: Any,
|
|
581
|
+
parameter_profile: "ParameterProfile",
|
|
582
|
+
is_many: bool,
|
|
583
|
+
dialect_str: "str | None",
|
|
584
|
+
ast_was_transformed: bool,
|
|
585
|
+
) -> "tuple[str, Any, ParameterProfile]":
|
|
586
|
+
"""Finalize SQL and parameter conversion for execution.
|
|
587
|
+
|
|
588
|
+
Args:
|
|
589
|
+
processed_sql: SQL after parameter processing.
|
|
590
|
+
processed_params: Parameters after initial processing.
|
|
591
|
+
expression: SQLGlot expression if available.
|
|
592
|
+
parameters: Parameters to compile for execution.
|
|
593
|
+
parameter_profile: Parameter profile metadata.
|
|
594
|
+
is_many: Whether this is for execute_many.
|
|
595
|
+
dialect_str: Dialect name.
|
|
596
|
+
ast_was_transformed: Whether AST transformations ran.
|
|
597
|
+
|
|
598
|
+
Returns:
|
|
599
|
+
Final SQL, execution parameters, and parameter profile.
|
|
600
|
+
"""
|
|
601
|
+
if self._config.parameter_config.needs_static_script_compilation and processed_params is None:
|
|
602
|
+
return processed_sql, processed_params, parameter_profile
|
|
603
|
+
if ast_was_transformed and expression is not None:
|
|
604
|
+
transformed_result = self._parameter_processor.process_for_execution(
|
|
605
|
+
sql=expression.sql(dialect=dialect_str),
|
|
606
|
+
parameters=parameters,
|
|
607
|
+
config=self._config.parameter_config,
|
|
608
|
+
dialect=dialect_str,
|
|
609
|
+
is_many=is_many,
|
|
610
|
+
wrap_types=self._config.enable_parameter_type_wrapping,
|
|
611
|
+
)
|
|
612
|
+
final_sql = transformed_result.sql
|
|
613
|
+
final_params = transformed_result.parameters
|
|
614
|
+
parameter_profile = transformed_result.parameter_profile
|
|
615
|
+
output_transformer = self._config.output_transformer
|
|
616
|
+
if output_transformer:
|
|
617
|
+
final_sql, final_params = output_transformer(final_sql, final_params)
|
|
618
|
+
return final_sql, final_params, parameter_profile
|
|
619
|
+
|
|
620
|
+
final_sql, final_params = self._apply_final_transformations(expression, processed_sql, parameters, dialect_str)
|
|
621
|
+
return final_sql, final_params, parameter_profile
|
|
622
|
+
|
|
623
|
+
def _should_validate_parameters(self, final_params: Any, raw_parameters: Any, is_many: bool) -> bool:
|
|
624
|
+
"""Determine if parameter alignment should be validated.
|
|
625
|
+
|
|
626
|
+
Args:
|
|
627
|
+
final_params: Parameters after compilation.
|
|
628
|
+
raw_parameters: Original parameters.
|
|
629
|
+
is_many: Whether this is for execute_many.
|
|
630
|
+
|
|
631
|
+
Returns:
|
|
632
|
+
True when validation should run.
|
|
633
|
+
"""
|
|
634
|
+
if not self._config.enable_validation:
|
|
635
|
+
return False
|
|
636
|
+
return not (
|
|
637
|
+
_is_effectively_empty_parameters(final_params)
|
|
638
|
+
and _is_effectively_empty_parameters(raw_parameters)
|
|
639
|
+
and not is_many
|
|
640
|
+
)
|
|
641
|
+
|
|
642
|
+
def _validate_parameters(self, parameter_profile: "ParameterProfile", final_params: Any, is_many: bool) -> None:
|
|
643
|
+
"""Validate parameter alignment and log failures.
|
|
644
|
+
|
|
645
|
+
Args:
|
|
646
|
+
parameter_profile: Parameter metadata.
|
|
647
|
+
final_params: Execution parameters.
|
|
648
|
+
is_many: Whether this is for execute_many.
|
|
649
|
+
|
|
650
|
+
Raises:
|
|
651
|
+
Exception: Re-raises validation errors from parameter alignment.
|
|
652
|
+
"""
|
|
653
|
+
try:
|
|
654
|
+
validate_parameter_alignment(parameter_profile, final_params, is_many=is_many)
|
|
655
|
+
except Exception as exc:
|
|
656
|
+
log_with_context(logger, logging.ERROR, "sql.validate", error_type=type(exc).__name__)
|
|
657
|
+
raise
|
|
658
|
+
|
|
659
|
+
def _compile_uncached(
|
|
660
|
+
self, sql: str, parameters: Any, is_many: bool = False, expression_override: "exp.Expression | None" = None
|
|
661
|
+
) -> CompiledSQL:
|
|
662
|
+
"""Compile SQL without caching.
|
|
663
|
+
|
|
664
|
+
Args:
|
|
665
|
+
sql: SQL string
|
|
666
|
+
parameters: Parameter values
|
|
667
|
+
is_many: Whether this is for execute_many operation
|
|
668
|
+
expression_override: Pre-parsed SQLGlot expression to reuse
|
|
669
|
+
|
|
670
|
+
Returns:
|
|
671
|
+
CompiledSQL result
|
|
672
|
+
"""
|
|
673
|
+
parameter_profile = ParameterProfile.empty()
|
|
674
|
+
operation_profile = OperationProfile.empty()
|
|
675
|
+
|
|
676
|
+
try:
|
|
677
|
+
dialect_str = str(self._config.dialect) if self._config.dialect else None
|
|
678
|
+
processed_sql, processed_params, parameter_profile, sqlglot_sql = self._prepare_parameters(
|
|
679
|
+
sql, parameters, is_many, dialect_str
|
|
680
|
+
)
|
|
681
|
+
expression_override = self._normalize_expression_override(expression_override, sqlglot_sql, sql)
|
|
682
|
+
|
|
683
|
+
final_parameters = processed_params
|
|
684
|
+
ast_was_transformed = False
|
|
685
|
+
expression = None
|
|
686
|
+
operation_type: OperationType = "EXECUTE"
|
|
687
|
+
parameter_casts: dict[int, str] = {}
|
|
688
|
+
parse_cache_key = None
|
|
689
|
+
parse_cache_entry = None
|
|
690
|
+
|
|
691
|
+
if self._config.enable_parsing:
|
|
692
|
+
(expression, operation_type, parameter_casts, operation_profile, parse_cache_key, parse_cache_entry) = (
|
|
693
|
+
self._resolve_expression(sqlglot_sql, dialect_str, expression_override)
|
|
694
|
+
)
|
|
695
|
+
(
|
|
696
|
+
expression,
|
|
697
|
+
final_parameters,
|
|
698
|
+
ast_was_transformed,
|
|
699
|
+
operation_type,
|
|
700
|
+
parameter_casts,
|
|
701
|
+
operation_profile,
|
|
702
|
+
) = self._apply_ast_transformers(
|
|
703
|
+
expression,
|
|
704
|
+
final_parameters,
|
|
705
|
+
parameter_profile,
|
|
706
|
+
operation_type,
|
|
707
|
+
parameter_casts,
|
|
708
|
+
operation_profile,
|
|
709
|
+
parse_cache_key,
|
|
710
|
+
parse_cache_entry,
|
|
711
|
+
expression_override,
|
|
712
|
+
)
|
|
713
|
+
|
|
714
|
+
final_sql, final_params, parameter_profile = self._finalize_compilation(
|
|
715
|
+
processed_sql,
|
|
716
|
+
processed_params,
|
|
717
|
+
expression,
|
|
718
|
+
final_parameters,
|
|
719
|
+
parameter_profile,
|
|
720
|
+
is_many,
|
|
721
|
+
dialect_str,
|
|
722
|
+
ast_was_transformed,
|
|
723
|
+
)
|
|
724
|
+
|
|
725
|
+
if self._should_validate_parameters(final_params, parameters, is_many):
|
|
726
|
+
self._validate_parameters(parameter_profile, final_params, is_many)
|
|
727
|
+
|
|
728
|
+
return CompiledSQL(
|
|
729
|
+
compiled_sql=final_sql,
|
|
730
|
+
execution_parameters=final_params,
|
|
731
|
+
operation_type=operation_type,
|
|
732
|
+
expression=expression,
|
|
733
|
+
parameter_style=self._config.parameter_config.default_parameter_style.value,
|
|
734
|
+
supports_many=isinstance(final_params, list) and len(final_params) > 0,
|
|
735
|
+
parameter_casts=parameter_casts,
|
|
736
|
+
parameter_profile=parameter_profile,
|
|
737
|
+
operation_profile=operation_profile,
|
|
738
|
+
)
|
|
739
|
+
|
|
740
|
+
except sqlspec.exceptions.SQLSpecError:
|
|
741
|
+
raise
|
|
742
|
+
except Exception as exc:
|
|
743
|
+
log_with_context(logger, logging.DEBUG, "sql.compile", error_type=type(exc).__name__, status="fallback")
|
|
744
|
+
return CompiledSQL(
|
|
745
|
+
compiled_sql=sql,
|
|
746
|
+
execution_parameters=parameters,
|
|
747
|
+
operation_type="UNKNOWN",
|
|
748
|
+
parameter_casts={},
|
|
749
|
+
parameter_profile=parameter_profile,
|
|
750
|
+
operation_profile=operation_profile,
|
|
751
|
+
)
|
|
752
|
+
|
|
753
|
+
def _make_cache_key(self, sql: str, parameters: Any, is_many: bool = False) -> str:
|
|
754
|
+
"""Generate cache key.
|
|
755
|
+
|
|
756
|
+
Args:
|
|
757
|
+
sql: SQL string
|
|
758
|
+
parameters: Parameter values
|
|
759
|
+
is_many: Whether this is for execute_many operation
|
|
760
|
+
|
|
761
|
+
Returns:
|
|
762
|
+
Cache key string
|
|
763
|
+
"""
|
|
764
|
+
|
|
765
|
+
param_fingerprint = fingerprint_parameters(parameters)
|
|
766
|
+
dialect_str = str(self._config.dialect) if self._config.dialect else None
|
|
767
|
+
param_style = self._config.parameter_config.default_parameter_style.value
|
|
768
|
+
|
|
769
|
+
hash_data = (
|
|
770
|
+
sql,
|
|
771
|
+
param_fingerprint,
|
|
772
|
+
param_style,
|
|
773
|
+
dialect_str,
|
|
774
|
+
self._config.enable_parsing,
|
|
775
|
+
self._config.enable_transformations,
|
|
776
|
+
is_many,
|
|
777
|
+
)
|
|
778
|
+
|
|
779
|
+
hash_str = hashlib.blake2b(repr(hash_data).encode("utf-8"), digest_size=8).hexdigest()
|
|
780
|
+
return f"sql_{hash_str}"
|
|
781
|
+
|
|
782
|
+
def _detect_operation_type(self, expression: "exp.Expression") -> "OperationType":
|
|
783
|
+
"""Detect operation type from AST.
|
|
784
|
+
|
|
785
|
+
Args:
|
|
786
|
+
expression: AST expression
|
|
787
|
+
|
|
788
|
+
Returns:
|
|
789
|
+
Operation type literal
|
|
790
|
+
"""
|
|
791
|
+
|
|
792
|
+
expr_type = type(expression)
|
|
793
|
+
if expr_type in OPERATION_TYPE_MAP:
|
|
794
|
+
return OPERATION_TYPE_MAP[expr_type] # pyright: ignore
|
|
795
|
+
|
|
796
|
+
if isinstance(expression, exp.Copy):
|
|
797
|
+
copy_kind = expression.args.get("kind")
|
|
798
|
+
if copy_kind is True:
|
|
799
|
+
return "COPY_FROM"
|
|
800
|
+
if copy_kind is False:
|
|
801
|
+
return "COPY_TO"
|
|
802
|
+
return "COPY"
|
|
803
|
+
|
|
804
|
+
return "UNKNOWN"
|
|
805
|
+
|
|
806
|
+
def _detect_parameter_casts(self, expression: "exp.Expression | None") -> "dict[int, str]":
|
|
807
|
+
"""Detect explicit type casts on parameters in the AST.
|
|
808
|
+
|
|
809
|
+
Args:
|
|
810
|
+
expression: SQLGlot AST expression to analyze
|
|
811
|
+
|
|
812
|
+
Returns:
|
|
813
|
+
Dict mapping parameter positions (1-based) to cast type names
|
|
814
|
+
"""
|
|
815
|
+
if not expression:
|
|
816
|
+
return {}
|
|
817
|
+
|
|
818
|
+
cast_positions: dict[int, str] = {}
|
|
819
|
+
placeholder_positions: dict[str, int] = {}
|
|
820
|
+
placeholder_counter = [0]
|
|
821
|
+
|
|
822
|
+
# Walk all nodes in order to track parameter positions
|
|
823
|
+
for node in expression.walk():
|
|
824
|
+
if isinstance(node, exp.Placeholder):
|
|
825
|
+
_assign_placeholder_position(node, placeholder_positions, placeholder_counter)
|
|
826
|
+
# Check for cast nodes with parameter children
|
|
827
|
+
if isinstance(node, exp.Cast):
|
|
828
|
+
cast_target = node.this
|
|
829
|
+
position = None
|
|
830
|
+
|
|
831
|
+
if isinstance(cast_target, exp.Parameter):
|
|
832
|
+
# Handle $1, $2 style parameters
|
|
833
|
+
param_value = cast_target.this
|
|
834
|
+
if isinstance(param_value, exp.Literal):
|
|
835
|
+
position = int(param_value.this)
|
|
836
|
+
elif isinstance(cast_target, exp.Placeholder):
|
|
837
|
+
position = _assign_placeholder_position(cast_target, placeholder_positions, placeholder_counter)
|
|
838
|
+
elif isinstance(cast_target, exp.Column):
|
|
839
|
+
# Handle cases where $1 gets parsed as a column
|
|
840
|
+
column_name = str(cast_target.this) if cast_target.this else str(cast_target)
|
|
841
|
+
if column_name.startswith("$") and column_name[1:].isdigit():
|
|
842
|
+
position = int(column_name[1:])
|
|
843
|
+
|
|
844
|
+
if position is not None:
|
|
845
|
+
# Extract cast type
|
|
846
|
+
if isinstance(node.to, exp.DataType):
|
|
847
|
+
cast_type = str(get_value_attribute(node.to.this))
|
|
848
|
+
else:
|
|
849
|
+
cast_type = str(node.to)
|
|
850
|
+
cast_positions[position] = cast_type.upper()
|
|
851
|
+
|
|
852
|
+
return cast_positions
|
|
853
|
+
|
|
854
|
+
def _apply_final_transformations(
|
|
855
|
+
self, expression: "exp.Expression | None", sql: str, parameters: Any, dialect_str: "str | None"
|
|
856
|
+
) -> "tuple[str, Any]":
|
|
857
|
+
"""Apply final transformations.
|
|
858
|
+
|
|
859
|
+
Args:
|
|
860
|
+
expression: SQLGlot AST expression
|
|
861
|
+
sql: SQL string
|
|
862
|
+
parameters: Execution parameters
|
|
863
|
+
dialect_str: SQL dialect
|
|
864
|
+
|
|
865
|
+
Returns:
|
|
866
|
+
Tuple of (final_sql, final_parameters)
|
|
867
|
+
"""
|
|
868
|
+
output_transformer = self._config.output_transformer
|
|
869
|
+
if output_transformer:
|
|
870
|
+
if expression is not None:
|
|
871
|
+
ast_sql = expression.sql(dialect=dialect_str)
|
|
872
|
+
return output_transformer(ast_sql, parameters)
|
|
873
|
+
return output_transformer(sql, parameters)
|
|
874
|
+
|
|
875
|
+
return sql, parameters
|
|
876
|
+
|
|
877
|
+
def _build_operation_profile(
|
|
878
|
+
self, expression: "exp.Expression | None", operation_type: "OperationType"
|
|
879
|
+
) -> "OperationProfile":
|
|
880
|
+
if expression is None:
|
|
881
|
+
return OperationProfile.empty()
|
|
882
|
+
|
|
883
|
+
returns_rows = False
|
|
884
|
+
modifies_rows = False
|
|
885
|
+
|
|
886
|
+
expr = expression
|
|
887
|
+
if isinstance(
|
|
888
|
+
expr, (exp.Select, exp.Union, exp.Except, exp.Intersect, exp.Values, exp.Table, exp.TableSample, exp.With)
|
|
889
|
+
):
|
|
890
|
+
returns_rows = True
|
|
891
|
+
elif isinstance(expr, (exp.Insert, exp.Update, exp.Delete, exp.Merge)):
|
|
892
|
+
modifies_rows = True
|
|
893
|
+
returns_rows = bool(expr.args.get("returning"))
|
|
894
|
+
elif isinstance(expr, exp.Copy):
|
|
895
|
+
copy_kind = expr.args.get("kind")
|
|
896
|
+
modifies_rows = copy_kind is True
|
|
897
|
+
returns_rows = copy_kind is False
|
|
898
|
+
|
|
899
|
+
if not returns_rows and operation_type in {"SELECT", "WITH", "VALUES", "TABLE"}:
|
|
900
|
+
returns_rows = True
|
|
901
|
+
|
|
902
|
+
if not modifies_rows and operation_type in {"INSERT", "UPDATE", "DELETE", "MERGE"}:
|
|
903
|
+
modifies_rows = True
|
|
904
|
+
|
|
905
|
+
return OperationProfile(returns_rows=returns_rows, modifies_rows=modifies_rows)
|
|
906
|
+
|
|
907
|
+
def clear_cache(self) -> None:
|
|
908
|
+
"""Clear compilation cache and reset statistics."""
|
|
909
|
+
self._cache.clear()
|
|
910
|
+
self._cache_hits = 0
|
|
911
|
+
self._cache_misses = 0
|
|
912
|
+
self._parse_cache.clear()
|
|
913
|
+
self._parse_cache_hits = 0
|
|
914
|
+
self._parse_cache_misses = 0
|
|
915
|
+
self._parameter_processor.clear_cache()
|
|
916
|
+
|
|
917
|
+
def _make_parse_cache_key(self, sql: str, dialect: "str | None") -> str:
|
|
918
|
+
dialect_marker = dialect or "default"
|
|
919
|
+
hash_str = hashlib.sha256(f"{dialect_marker}:{sql}".encode()).hexdigest()[:16]
|
|
920
|
+
return f"parse_{hash_str}"
|
|
921
|
+
|
|
922
|
+
@property
|
|
923
|
+
def cache_stats(self) -> "dict[str, int]":
|
|
924
|
+
"""Get cache statistics.
|
|
925
|
+
|
|
926
|
+
Returns:
|
|
927
|
+
Dictionary with cache statistics
|
|
928
|
+
"""
|
|
929
|
+
total_requests = self._cache_hits + self._cache_misses
|
|
930
|
+
hit_rate_pct = int((self._cache_hits / total_requests) * 100) if total_requests > 0 else 0
|
|
931
|
+
parse_total = self._parse_cache_hits + self._parse_cache_misses
|
|
932
|
+
parse_hit_rate_pct = int((self._parse_cache_hits / parse_total) * 100) if parse_total > 0 else 0
|
|
933
|
+
parameter_stats = self._parameter_processor.cache_stats()
|
|
934
|
+
|
|
935
|
+
return {
|
|
936
|
+
"hits": self._cache_hits,
|
|
937
|
+
"misses": self._cache_misses,
|
|
938
|
+
"size": len(self._cache),
|
|
939
|
+
"max_size": self._max_cache_size,
|
|
940
|
+
"hit_rate_percent": hit_rate_pct,
|
|
941
|
+
"parse_hits": self._parse_cache_hits,
|
|
942
|
+
"parse_misses": self._parse_cache_misses,
|
|
943
|
+
"parse_size": len(self._parse_cache),
|
|
944
|
+
"parse_max_size": self._parse_cache_max_size,
|
|
945
|
+
"parse_hit_rate_percent": parse_hit_rate_pct,
|
|
946
|
+
"parameter_hits": parameter_stats["hits"],
|
|
947
|
+
"parameter_misses": parameter_stats["misses"],
|
|
948
|
+
"parameter_size": parameter_stats["size"],
|
|
949
|
+
"parameter_max_size": parameter_stats["max_size"],
|
|
950
|
+
"validator_hits": parameter_stats["validator_hits"],
|
|
951
|
+
"validator_misses": parameter_stats["validator_misses"],
|
|
952
|
+
"validator_size": parameter_stats["validator_size"],
|
|
953
|
+
"validator_max_size": parameter_stats["validator_max_size"],
|
|
954
|
+
}
|