sqlspec 0.36.0__cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ac8f31065839703b4e70__mypyc.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/__init__.py +140 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +315 -0
- sqlspec/_typing.py +700 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_typing.py +82 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +1273 -0
- sqlspec/adapters/adbc/config.py +295 -0
- sqlspec/adapters/adbc/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/core.py +735 -0
- sqlspec/adapters/adbc/data_dictionary.py +334 -0
- sqlspec/adapters/adbc/driver.py +529 -0
- sqlspec/adapters/adbc/events/__init__.py +5 -0
- sqlspec/adapters/adbc/events/store.py +285 -0
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +502 -0
- sqlspec/adapters/adbc/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/type_converter.py +140 -0
- sqlspec/adapters/aiosqlite/__init__.py +25 -0
- sqlspec/adapters/aiosqlite/_typing.py +82 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +818 -0
- sqlspec/adapters/aiosqlite/config.py +334 -0
- sqlspec/adapters/aiosqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/aiosqlite/core.py +315 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +208 -0
- sqlspec/adapters/aiosqlite/driver.py +313 -0
- sqlspec/adapters/aiosqlite/events/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/events/store.py +20 -0
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +279 -0
- sqlspec/adapters/aiosqlite/pool.py +533 -0
- sqlspec/adapters/asyncmy/__init__.py +21 -0
- sqlspec/adapters/asyncmy/_typing.py +87 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +703 -0
- sqlspec/adapters/asyncmy/config.py +302 -0
- sqlspec/adapters/asyncmy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncmy/core.py +360 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +124 -0
- sqlspec/adapters/asyncmy/driver.py +383 -0
- sqlspec/adapters/asyncmy/events/__init__.py +5 -0
- sqlspec/adapters/asyncmy/events/store.py +104 -0
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +19 -0
- sqlspec/adapters/asyncpg/_typing.py +88 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +748 -0
- sqlspec/adapters/asyncpg/config.py +569 -0
- sqlspec/adapters/asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncpg/core.py +367 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +162 -0
- sqlspec/adapters/asyncpg/driver.py +487 -0
- sqlspec/adapters/asyncpg/events/__init__.py +6 -0
- sqlspec/adapters/asyncpg/events/backend.py +286 -0
- sqlspec/adapters/asyncpg/events/store.py +40 -0
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +251 -0
- sqlspec/adapters/bigquery/__init__.py +14 -0
- sqlspec/adapters/bigquery/_typing.py +86 -0
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +827 -0
- sqlspec/adapters/bigquery/config.py +353 -0
- sqlspec/adapters/bigquery/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/core.py +715 -0
- sqlspec/adapters/bigquery/data_dictionary.py +128 -0
- sqlspec/adapters/bigquery/driver.py +548 -0
- sqlspec/adapters/bigquery/events/__init__.py +5 -0
- sqlspec/adapters/bigquery/events/store.py +139 -0
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +325 -0
- sqlspec/adapters/bigquery/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/type_converter.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/__init__.py +24 -0
- sqlspec/adapters/cockroach_asyncpg/_typing.py +72 -0
- sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/adk/store.py +410 -0
- sqlspec/adapters/cockroach_asyncpg/config.py +238 -0
- sqlspec/adapters/cockroach_asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
- sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/driver.py +144 -0
- sqlspec/adapters/cockroach_asyncpg/events/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/events/store.py +20 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/store.py +142 -0
- sqlspec/adapters/cockroach_psycopg/__init__.py +38 -0
- sqlspec/adapters/cockroach_psycopg/_typing.py +129 -0
- sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
- sqlspec/adapters/cockroach_psycopg/adk/store.py +868 -0
- sqlspec/adapters/cockroach_psycopg/config.py +484 -0
- sqlspec/adapters/cockroach_psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_psycopg/core.py +63 -0
- sqlspec/adapters/cockroach_psycopg/data_dictionary.py +215 -0
- sqlspec/adapters/cockroach_psycopg/driver.py +284 -0
- sqlspec/adapters/cockroach_psycopg/events/__init__.py +6 -0
- sqlspec/adapters/cockroach_psycopg/events/store.py +34 -0
- sqlspec/adapters/cockroach_psycopg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_psycopg/litestar/store.py +325 -0
- sqlspec/adapters/duckdb/__init__.py +25 -0
- sqlspec/adapters/duckdb/_typing.py +81 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +850 -0
- sqlspec/adapters/duckdb/config.py +463 -0
- sqlspec/adapters/duckdb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/core.py +257 -0
- sqlspec/adapters/duckdb/data_dictionary.py +140 -0
- sqlspec/adapters/duckdb/driver.py +430 -0
- sqlspec/adapters/duckdb/events/__init__.py +5 -0
- sqlspec/adapters/duckdb/events/store.py +57 -0
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +330 -0
- sqlspec/adapters/duckdb/pool.py +293 -0
- sqlspec/adapters/duckdb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/type_converter.py +118 -0
- sqlspec/adapters/mock/__init__.py +72 -0
- sqlspec/adapters/mock/_typing.py +147 -0
- sqlspec/adapters/mock/config.py +483 -0
- sqlspec/adapters/mock/core.py +319 -0
- sqlspec/adapters/mock/data_dictionary.py +366 -0
- sqlspec/adapters/mock/driver.py +721 -0
- sqlspec/adapters/mysqlconnector/__init__.py +36 -0
- sqlspec/adapters/mysqlconnector/_typing.py +141 -0
- sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
- sqlspec/adapters/mysqlconnector/adk/store.py +1060 -0
- sqlspec/adapters/mysqlconnector/config.py +394 -0
- sqlspec/adapters/mysqlconnector/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/mysqlconnector/core.py +303 -0
- sqlspec/adapters/mysqlconnector/data_dictionary.py +235 -0
- sqlspec/adapters/mysqlconnector/driver.py +483 -0
- sqlspec/adapters/mysqlconnector/events/__init__.py +8 -0
- sqlspec/adapters/mysqlconnector/events/store.py +98 -0
- sqlspec/adapters/mysqlconnector/litestar/__init__.py +5 -0
- sqlspec/adapters/mysqlconnector/litestar/store.py +426 -0
- sqlspec/adapters/oracledb/__init__.py +60 -0
- sqlspec/adapters/oracledb/_numpy_handlers.py +141 -0
- sqlspec/adapters/oracledb/_typing.py +182 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +166 -0
- sqlspec/adapters/oracledb/adk/__init__.py +10 -0
- sqlspec/adapters/oracledb/adk/store.py +2369 -0
- sqlspec/adapters/oracledb/config.py +550 -0
- sqlspec/adapters/oracledb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/core.py +543 -0
- sqlspec/adapters/oracledb/data_dictionary.py +536 -0
- sqlspec/adapters/oracledb/driver.py +1229 -0
- sqlspec/adapters/oracledb/events/__init__.py +16 -0
- sqlspec/adapters/oracledb/events/backend.py +347 -0
- sqlspec/adapters/oracledb/events/store.py +420 -0
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +781 -0
- sqlspec/adapters/oracledb/migrations.py +535 -0
- sqlspec/adapters/oracledb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/type_converter.py +211 -0
- sqlspec/adapters/psqlpy/__init__.py +17 -0
- sqlspec/adapters/psqlpy/_typing.py +79 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +766 -0
- sqlspec/adapters/psqlpy/config.py +304 -0
- sqlspec/adapters/psqlpy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/core.py +480 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +126 -0
- sqlspec/adapters/psqlpy/driver.py +438 -0
- sqlspec/adapters/psqlpy/events/__init__.py +6 -0
- sqlspec/adapters/psqlpy/events/backend.py +310 -0
- sqlspec/adapters/psqlpy/events/store.py +20 -0
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +270 -0
- sqlspec/adapters/psqlpy/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/type_converter.py +113 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_typing.py +164 -0
- sqlspec/adapters/psycopg/adk/__init__.py +10 -0
- sqlspec/adapters/psycopg/adk/store.py +1387 -0
- sqlspec/adapters/psycopg/config.py +576 -0
- sqlspec/adapters/psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/core.py +450 -0
- sqlspec/adapters/psycopg/data_dictionary.py +289 -0
- sqlspec/adapters/psycopg/driver.py +975 -0
- sqlspec/adapters/psycopg/events/__init__.py +20 -0
- sqlspec/adapters/psycopg/events/backend.py +458 -0
- sqlspec/adapters/psycopg/events/store.py +42 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +552 -0
- sqlspec/adapters/psycopg/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/type_converter.py +93 -0
- sqlspec/adapters/pymysql/__init__.py +21 -0
- sqlspec/adapters/pymysql/_typing.py +71 -0
- sqlspec/adapters/pymysql/adk/__init__.py +5 -0
- sqlspec/adapters/pymysql/adk/store.py +540 -0
- sqlspec/adapters/pymysql/config.py +195 -0
- sqlspec/adapters/pymysql/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/pymysql/core.py +299 -0
- sqlspec/adapters/pymysql/data_dictionary.py +122 -0
- sqlspec/adapters/pymysql/driver.py +259 -0
- sqlspec/adapters/pymysql/events/__init__.py +5 -0
- sqlspec/adapters/pymysql/events/store.py +50 -0
- sqlspec/adapters/pymysql/litestar/__init__.py +5 -0
- sqlspec/adapters/pymysql/litestar/store.py +232 -0
- sqlspec/adapters/pymysql/pool.py +137 -0
- sqlspec/adapters/spanner/__init__.py +40 -0
- sqlspec/adapters/spanner/_typing.py +86 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +732 -0
- sqlspec/adapters/spanner/config.py +352 -0
- sqlspec/adapters/spanner/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/core.py +188 -0
- sqlspec/adapters/spanner/data_dictionary.py +120 -0
- sqlspec/adapters/spanner/dialect/__init__.py +6 -0
- sqlspec/adapters/spanner/dialect/_spangres.py +57 -0
- sqlspec/adapters/spanner/dialect/_spanner.py +130 -0
- sqlspec/adapters/spanner/driver.py +373 -0
- sqlspec/adapters/spanner/events/__init__.py +5 -0
- sqlspec/adapters/spanner/events/store.py +187 -0
- sqlspec/adapters/spanner/litestar/__init__.py +5 -0
- sqlspec/adapters/spanner/litestar/store.py +291 -0
- sqlspec/adapters/spanner/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/type_converter.py +331 -0
- sqlspec/adapters/sqlite/__init__.py +19 -0
- sqlspec/adapters/sqlite/_typing.py +80 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +958 -0
- sqlspec/adapters/sqlite/config.py +280 -0
- sqlspec/adapters/sqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/core.py +312 -0
- sqlspec/adapters/sqlite/data_dictionary.py +202 -0
- sqlspec/adapters/sqlite/driver.py +359 -0
- sqlspec/adapters/sqlite/events/__init__.py +5 -0
- sqlspec/adapters/sqlite/events/store.py +20 -0
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +316 -0
- sqlspec/adapters/sqlite/pool.py +198 -0
- sqlspec/adapters/sqlite/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/type_converter.py +114 -0
- sqlspec/base.py +747 -0
- sqlspec/builder/__init__.py +179 -0
- sqlspec/builder/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_base.py +1022 -0
- sqlspec/builder/_column.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_column.py +521 -0
- sqlspec/builder/_ddl.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_ddl.py +1642 -0
- sqlspec/builder/_delete.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_delete.py +95 -0
- sqlspec/builder/_dml.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_dml.py +365 -0
- sqlspec/builder/_explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_explain.py +579 -0
- sqlspec/builder/_expression_wrappers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_factory.py +1697 -0
- sqlspec/builder/_insert.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_insert.py +328 -0
- sqlspec/builder/_join.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_join.py +499 -0
- sqlspec/builder/_merge.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_merge.py +821 -0
- sqlspec/builder/_parsing_utils.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_parsing_utils.py +297 -0
- sqlspec/builder/_select.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_select.py +1660 -0
- sqlspec/builder/_temporal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_temporal.py +139 -0
- sqlspec/builder/_update.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_update.py +173 -0
- sqlspec/builder/_vector_expressions.py +267 -0
- sqlspec/cli.py +911 -0
- sqlspec/config.py +1755 -0
- sqlspec/core/__init__.py +374 -0
- sqlspec/core/_correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/_correlation.py +176 -0
- sqlspec/core/cache.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +1069 -0
- sqlspec/core/compiler.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +954 -0
- sqlspec/core/explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/explain.py +275 -0
- sqlspec/core/filters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +952 -0
- sqlspec/core/hashing.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +262 -0
- sqlspec/core/metrics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +71 -0
- sqlspec/core/parameters/_alignment.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_alignment.py +270 -0
- sqlspec/core/parameters/_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_converter.py +543 -0
- sqlspec/core/parameters/_processor.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_processor.py +505 -0
- sqlspec/core/parameters/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_registry.py +206 -0
- sqlspec/core/parameters/_transformers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_transformers.py +292 -0
- sqlspec/core/parameters/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_types.py +499 -0
- sqlspec/core/parameters/_validator.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_validator.py +180 -0
- sqlspec/core/pipeline.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/pipeline.py +319 -0
- sqlspec/core/query_modifiers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/query_modifiers.py +437 -0
- sqlspec/core/result/__init__.py +23 -0
- sqlspec/core/result/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_base.py +1121 -0
- sqlspec/core/result/_io.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_io.py +28 -0
- sqlspec/core/splitter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +966 -0
- sqlspec/core/stack.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/statement.py +1503 -0
- sqlspec/core/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/type_converter.py +339 -0
- sqlspec/data_dictionary/__init__.py +22 -0
- sqlspec/data_dictionary/_loader.py +123 -0
- sqlspec/data_dictionary/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_registry.py +74 -0
- sqlspec/data_dictionary/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_types.py +121 -0
- sqlspec/data_dictionary/dialects/__init__.py +21 -0
- sqlspec/data_dictionary/dialects/bigquery.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/bigquery.py +49 -0
- sqlspec/data_dictionary/dialects/cockroachdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/cockroachdb.py +43 -0
- sqlspec/data_dictionary/dialects/duckdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/duckdb.py +47 -0
- sqlspec/data_dictionary/dialects/mysql.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/mysql.py +42 -0
- sqlspec/data_dictionary/dialects/oracle.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/oracle.py +34 -0
- sqlspec/data_dictionary/dialects/postgres.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/postgres.py +46 -0
- sqlspec/data_dictionary/dialects/spanner.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/spanner.py +37 -0
- sqlspec/data_dictionary/dialects/sqlite.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/sqlite.py +42 -0
- sqlspec/data_dictionary/sql/.gitkeep +0 -0
- sqlspec/data_dictionary/sql/bigquery/columns.sql +23 -0
- sqlspec/data_dictionary/sql/bigquery/foreign_keys.sql +34 -0
- sqlspec/data_dictionary/sql/bigquery/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/bigquery/tables.sql +33 -0
- sqlspec/data_dictionary/sql/bigquery/version.sql +3 -0
- sqlspec/data_dictionary/sql/cockroachdb/columns.sql +34 -0
- sqlspec/data_dictionary/sql/cockroachdb/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/cockroachdb/indexes.sql +32 -0
- sqlspec/data_dictionary/sql/cockroachdb/tables.sql +44 -0
- sqlspec/data_dictionary/sql/cockroachdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/duckdb/columns.sql +23 -0
- sqlspec/data_dictionary/sql/duckdb/foreign_keys.sql +36 -0
- sqlspec/data_dictionary/sql/duckdb/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/duckdb/tables.sql +38 -0
- sqlspec/data_dictionary/sql/duckdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/mysql/columns.sql +23 -0
- sqlspec/data_dictionary/sql/mysql/foreign_keys.sql +28 -0
- sqlspec/data_dictionary/sql/mysql/indexes.sql +26 -0
- sqlspec/data_dictionary/sql/mysql/tables.sql +33 -0
- sqlspec/data_dictionary/sql/mysql/version.sql +3 -0
- sqlspec/data_dictionary/sql/oracle/columns.sql +23 -0
- sqlspec/data_dictionary/sql/oracle/foreign_keys.sql +48 -0
- sqlspec/data_dictionary/sql/oracle/indexes.sql +44 -0
- sqlspec/data_dictionary/sql/oracle/tables.sql +25 -0
- sqlspec/data_dictionary/sql/oracle/version.sql +20 -0
- sqlspec/data_dictionary/sql/postgres/columns.sql +34 -0
- sqlspec/data_dictionary/sql/postgres/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/postgres/indexes.sql +56 -0
- sqlspec/data_dictionary/sql/postgres/tables.sql +44 -0
- sqlspec/data_dictionary/sql/postgres/version.sql +3 -0
- sqlspec/data_dictionary/sql/spanner/columns.sql +23 -0
- sqlspec/data_dictionary/sql/spanner/foreign_keys.sql +70 -0
- sqlspec/data_dictionary/sql/spanner/indexes.sql +30 -0
- sqlspec/data_dictionary/sql/spanner/tables.sql +9 -0
- sqlspec/data_dictionary/sql/spanner/version.sql +3 -0
- sqlspec/data_dictionary/sql/sqlite/columns.sql +23 -0
- sqlspec/data_dictionary/sql/sqlite/foreign_keys.sql +22 -0
- sqlspec/data_dictionary/sql/sqlite/indexes.sql +7 -0
- sqlspec/data_dictionary/sql/sqlite/tables.sql +28 -0
- sqlspec/data_dictionary/sql/sqlite/version.sql +3 -0
- sqlspec/driver/__init__.py +32 -0
- sqlspec/driver/_async.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_async.py +1737 -0
- sqlspec/driver/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_common.py +1478 -0
- sqlspec/driver/_sql_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sql_helpers.py +148 -0
- sqlspec/driver/_storage_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_storage_helpers.py +144 -0
- sqlspec/driver/_sync.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sync.py +1710 -0
- sqlspec/exceptions.py +338 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +70 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/memory/__init__.py +69 -0
- sqlspec/extensions/adk/memory/_types.py +30 -0
- sqlspec/extensions/adk/memory/converters.py +149 -0
- sqlspec/extensions/adk/memory/service.py +217 -0
- sqlspec/extensions/adk/memory/store.py +569 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +246 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +225 -0
- sqlspec/extensions/adk/store.py +567 -0
- sqlspec/extensions/events/__init__.py +51 -0
- sqlspec/extensions/events/_channel.py +703 -0
- sqlspec/extensions/events/_hints.py +45 -0
- sqlspec/extensions/events/_models.py +23 -0
- sqlspec/extensions/events/_payload.py +69 -0
- sqlspec/extensions/events/_protocols.py +134 -0
- sqlspec/extensions/events/_queue.py +461 -0
- sqlspec/extensions/events/_store.py +209 -0
- sqlspec/extensions/events/migrations/0001_create_event_queue.py +59 -0
- sqlspec/extensions/events/migrations/__init__.py +3 -0
- sqlspec/extensions/fastapi/__init__.py +19 -0
- sqlspec/extensions/fastapi/extension.py +351 -0
- sqlspec/extensions/fastapi/providers.py +607 -0
- sqlspec/extensions/flask/__init__.py +37 -0
- sqlspec/extensions/flask/_state.py +76 -0
- sqlspec/extensions/flask/_utils.py +71 -0
- sqlspec/extensions/flask/extension.py +519 -0
- sqlspec/extensions/litestar/__init__.py +28 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/channels.py +165 -0
- sqlspec/extensions/litestar/cli.py +102 -0
- sqlspec/extensions/litestar/config.py +90 -0
- sqlspec/extensions/litestar/handlers.py +316 -0
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +671 -0
- sqlspec/extensions/litestar/providers.py +526 -0
- sqlspec/extensions/litestar/store.py +296 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +113 -0
- sqlspec/extensions/starlette/__init__.py +19 -0
- sqlspec/extensions/starlette/_state.py +30 -0
- sqlspec/extensions/starlette/_utils.py +96 -0
- sqlspec/extensions/starlette/extension.py +346 -0
- sqlspec/extensions/starlette/middleware.py +235 -0
- sqlspec/loader.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/loader.py +702 -0
- sqlspec/migrations/__init__.py +36 -0
- sqlspec/migrations/base.py +731 -0
- sqlspec/migrations/commands.py +1232 -0
- sqlspec/migrations/context.py +157 -0
- sqlspec/migrations/fix.py +204 -0
- sqlspec/migrations/loaders.py +443 -0
- sqlspec/migrations/runner.py +1172 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.py +611 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.py +207 -0
- sqlspec/migrations/version.py +446 -0
- sqlspec/observability/__init__.py +55 -0
- sqlspec/observability/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_common.py +77 -0
- sqlspec/observability/_config.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_config.py +348 -0
- sqlspec/observability/_diagnostics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_diagnostics.py +74 -0
- sqlspec/observability/_dispatcher.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_dispatcher.py +152 -0
- sqlspec/observability/_formatters/__init__.py +13 -0
- sqlspec/observability/_formatters/_aws.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_aws.py +102 -0
- sqlspec/observability/_formatters/_azure.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_azure.py +96 -0
- sqlspec/observability/_formatters/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_base.py +57 -0
- sqlspec/observability/_formatters/_gcp.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_gcp.py +131 -0
- sqlspec/observability/_formatting.py +58 -0
- sqlspec/observability/_observer.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_observer.py +357 -0
- sqlspec/observability/_runtime.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_runtime.py +420 -0
- sqlspec/observability/_sampling.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_sampling.py +188 -0
- sqlspec/observability/_spans.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_spans.py +161 -0
- sqlspec/protocols.py +916 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +48 -0
- sqlspec/storage/_utils.py +104 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.py +253 -0
- sqlspec/storage/backends/fsspec.py +529 -0
- sqlspec/storage/backends/local.py +441 -0
- sqlspec/storage/backends/obstore.py +916 -0
- sqlspec/storage/errors.py +104 -0
- sqlspec/storage/pipeline.py +582 -0
- sqlspec/storage/registry.py +301 -0
- sqlspec/typing.py +395 -0
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/arrow_helpers.py +318 -0
- sqlspec/utils/config_tools.py +332 -0
- sqlspec/utils/correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/correlation.py +134 -0
- sqlspec/utils/deprecation.py +190 -0
- sqlspec/utils/fixtures.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/fixtures.py +258 -0
- sqlspec/utils/logging.py +222 -0
- sqlspec/utils/module_loader.py +306 -0
- sqlspec/utils/portal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/portal.py +375 -0
- sqlspec/utils/schema.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/schema.py +485 -0
- sqlspec/utils/serializers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/serializers.py +408 -0
- sqlspec/utils/singleton.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +311 -0
- sqlspec/utils/text.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +108 -0
- sqlspec/utils/type_converters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_converters.py +128 -0
- sqlspec/utils/type_guards.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_guards.py +1360 -0
- sqlspec/utils/uuids.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/uuids.py +225 -0
- sqlspec-0.36.0.dist-info/METADATA +205 -0
- sqlspec-0.36.0.dist-info/RECORD +531 -0
- sqlspec-0.36.0.dist-info/WHEEL +7 -0
- sqlspec-0.36.0.dist-info/entry_points.txt +2 -0
- sqlspec-0.36.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1503 @@
|
|
|
1
|
+
"""SQL statement and configuration management."""
|
|
2
|
+
|
|
3
|
+
import uuid
|
|
4
|
+
from collections.abc import Mapping, Sequence
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Final, TypeAlias
|
|
6
|
+
|
|
7
|
+
import sqlglot
|
|
8
|
+
from mypy_extensions import mypyc_attr
|
|
9
|
+
from sqlglot import exp
|
|
10
|
+
from sqlglot.errors import ParseError
|
|
11
|
+
|
|
12
|
+
import sqlspec.exceptions
|
|
13
|
+
from sqlspec.core import pipeline
|
|
14
|
+
from sqlspec.core.cache import FiltersView
|
|
15
|
+
from sqlspec.core.compiler import OperationProfile, OperationType
|
|
16
|
+
from sqlspec.core.explain import ExplainFormat, ExplainOptions
|
|
17
|
+
from sqlspec.core.parameters import (
|
|
18
|
+
ParameterConverter,
|
|
19
|
+
ParameterProfile,
|
|
20
|
+
ParameterStyle,
|
|
21
|
+
ParameterStyleConfig,
|
|
22
|
+
ParameterValidator,
|
|
23
|
+
)
|
|
24
|
+
from sqlspec.core.query_modifiers import (
|
|
25
|
+
apply_limit,
|
|
26
|
+
apply_offset,
|
|
27
|
+
apply_select_only,
|
|
28
|
+
apply_where,
|
|
29
|
+
create_between_condition,
|
|
30
|
+
create_condition,
|
|
31
|
+
create_in_condition,
|
|
32
|
+
create_not_in_condition,
|
|
33
|
+
expr_eq,
|
|
34
|
+
expr_gt,
|
|
35
|
+
expr_gte,
|
|
36
|
+
expr_ilike,
|
|
37
|
+
expr_is_not_null,
|
|
38
|
+
expr_is_null,
|
|
39
|
+
expr_like,
|
|
40
|
+
expr_lt,
|
|
41
|
+
expr_lte,
|
|
42
|
+
expr_neq,
|
|
43
|
+
extract_column_name,
|
|
44
|
+
safe_modify_with_cte,
|
|
45
|
+
)
|
|
46
|
+
from sqlspec.typing import Empty, EmptyEnum
|
|
47
|
+
from sqlspec.utils.logging import get_logger
|
|
48
|
+
from sqlspec.utils.type_guards import is_statement_filter, supports_where
|
|
49
|
+
|
|
50
|
+
if TYPE_CHECKING:
|
|
51
|
+
from collections.abc import Callable
|
|
52
|
+
|
|
53
|
+
from sqlglot.dialects.dialect import DialectType
|
|
54
|
+
|
|
55
|
+
from sqlspec.builder import QueryBuilder
|
|
56
|
+
from sqlspec.core.filters import StatementFilter
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
__all__ = (
|
|
60
|
+
"SQL",
|
|
61
|
+
"ProcessedState",
|
|
62
|
+
"Statement",
|
|
63
|
+
"StatementConfig",
|
|
64
|
+
"get_default_config",
|
|
65
|
+
"get_default_parameter_config",
|
|
66
|
+
)
|
|
67
|
+
logger = get_logger("sqlspec.core.statement")
|
|
68
|
+
|
|
69
|
+
RETURNS_ROWS_OPERATIONS: Final = {"SELECT", "WITH", "VALUES", "TABLE", "SHOW", "DESCRIBE", "PRAGMA"}
|
|
70
|
+
MODIFYING_OPERATIONS: Final = {"INSERT", "UPDATE", "DELETE", "MERGE", "UPSERT"}
|
|
71
|
+
_ORDER_PARTS_COUNT: Final = 2
|
|
72
|
+
_MAX_PARAM_COLLISION_ATTEMPTS: Final = 1000
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
SQL_CONFIG_SLOTS: Final = (
|
|
76
|
+
"dialect",
|
|
77
|
+
"enable_analysis",
|
|
78
|
+
"enable_caching",
|
|
79
|
+
"enable_expression_simplification",
|
|
80
|
+
"enable_parameter_type_wrapping",
|
|
81
|
+
"enable_parsing",
|
|
82
|
+
"enable_transformations",
|
|
83
|
+
"enable_validation",
|
|
84
|
+
"execution_mode",
|
|
85
|
+
"execution_args",
|
|
86
|
+
"output_transformer",
|
|
87
|
+
"statement_transformers",
|
|
88
|
+
"parameter_config",
|
|
89
|
+
"parameter_converter",
|
|
90
|
+
"parameter_validator",
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
PROCESSED_STATE_SLOTS: Final = (
|
|
94
|
+
"compiled_sql",
|
|
95
|
+
"execution_parameters",
|
|
96
|
+
"parsed_expression",
|
|
97
|
+
"operation_type",
|
|
98
|
+
"parameter_casts",
|
|
99
|
+
"parameter_profile",
|
|
100
|
+
"operation_profile",
|
|
101
|
+
"validation_errors",
|
|
102
|
+
"is_many",
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
107
|
+
class ProcessedState:
|
|
108
|
+
"""Processing results for SQL statements.
|
|
109
|
+
|
|
110
|
+
Contains the compiled SQL, execution parameters, parsed expression,
|
|
111
|
+
operation type, and validation errors for a processed SQL statement.
|
|
112
|
+
"""
|
|
113
|
+
|
|
114
|
+
__slots__ = PROCESSED_STATE_SLOTS
|
|
115
|
+
operation_type: "OperationType"
|
|
116
|
+
|
|
117
|
+
def __init__(
|
|
118
|
+
self,
|
|
119
|
+
compiled_sql: str,
|
|
120
|
+
execution_parameters: Any,
|
|
121
|
+
parsed_expression: "exp.Expression | None" = None,
|
|
122
|
+
operation_type: "OperationType" = "UNKNOWN",
|
|
123
|
+
parameter_casts: "dict[int, str] | None" = None,
|
|
124
|
+
validation_errors: "list[str] | None" = None,
|
|
125
|
+
parameter_profile: "ParameterProfile | None" = None,
|
|
126
|
+
operation_profile: "OperationProfile | None" = None,
|
|
127
|
+
is_many: bool = False,
|
|
128
|
+
) -> None:
|
|
129
|
+
self.compiled_sql = compiled_sql
|
|
130
|
+
self.execution_parameters = execution_parameters
|
|
131
|
+
self.parsed_expression = parsed_expression
|
|
132
|
+
self.operation_type = operation_type
|
|
133
|
+
self.parameter_casts = parameter_casts or {}
|
|
134
|
+
self.validation_errors = validation_errors or []
|
|
135
|
+
self.parameter_profile = parameter_profile or ParameterProfile.empty()
|
|
136
|
+
self.operation_profile = operation_profile or OperationProfile.empty()
|
|
137
|
+
self.is_many = is_many
|
|
138
|
+
|
|
139
|
+
def __hash__(self) -> int:
|
|
140
|
+
return hash((self.compiled_sql, str(self.execution_parameters), self.operation_type))
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
144
|
+
class SQL:
|
|
145
|
+
"""SQL statement with parameter and filter support.
|
|
146
|
+
|
|
147
|
+
Represents a SQL statement that can be compiled with parameters and filters.
|
|
148
|
+
Supports both positional and named parameters, statement filtering,
|
|
149
|
+
and various execution modes including batch operations.
|
|
150
|
+
"""
|
|
151
|
+
|
|
152
|
+
__slots__ = (
|
|
153
|
+
"_dialect",
|
|
154
|
+
"_filters",
|
|
155
|
+
"_hash",
|
|
156
|
+
"_is_many",
|
|
157
|
+
"_is_script",
|
|
158
|
+
"_named_parameters",
|
|
159
|
+
"_original_parameters",
|
|
160
|
+
"_positional_parameters",
|
|
161
|
+
"_processed_state",
|
|
162
|
+
"_raw_expression",
|
|
163
|
+
"_raw_sql",
|
|
164
|
+
"_sql_param_counters",
|
|
165
|
+
"_statement_config",
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# Type annotation for mypyc compatibility
|
|
169
|
+
_sql_param_counters: "dict[str, int]"
|
|
170
|
+
|
|
171
|
+
def __init__(
|
|
172
|
+
self,
|
|
173
|
+
statement: "str | exp.Expression | 'SQL'",
|
|
174
|
+
*parameters: "Any | StatementFilter | list[Any | StatementFilter]",
|
|
175
|
+
statement_config: "StatementConfig | None" = None,
|
|
176
|
+
is_many: bool | None = None,
|
|
177
|
+
**kwargs: Any,
|
|
178
|
+
) -> None:
|
|
179
|
+
"""Initialize SQL statement.
|
|
180
|
+
|
|
181
|
+
Args:
|
|
182
|
+
statement: SQL string, expression, or existing SQL object
|
|
183
|
+
*parameters: Parameters and filters
|
|
184
|
+
statement_config: Configuration
|
|
185
|
+
is_many: Mark as execute_many operation
|
|
186
|
+
**kwargs: Additional parameters
|
|
187
|
+
"""
|
|
188
|
+
config = statement_config or self._create_auto_config(statement, parameters, kwargs)
|
|
189
|
+
self._statement_config = config
|
|
190
|
+
self._dialect = self._normalize_dialect(config.dialect)
|
|
191
|
+
self._processed_state: EmptyEnum | ProcessedState = Empty
|
|
192
|
+
self._hash: int | None = None
|
|
193
|
+
self._filters: list[StatementFilter] = []
|
|
194
|
+
self._named_parameters: dict[str, Any] = {}
|
|
195
|
+
self._positional_parameters: list[Any] = []
|
|
196
|
+
self._sql_param_counters = {}
|
|
197
|
+
self._is_script = False
|
|
198
|
+
self._raw_expression: exp.Expression | None = None
|
|
199
|
+
|
|
200
|
+
if isinstance(statement, SQL):
|
|
201
|
+
self._init_from_sql_object(statement)
|
|
202
|
+
if is_many is not None:
|
|
203
|
+
self._is_many = is_many
|
|
204
|
+
else:
|
|
205
|
+
if isinstance(statement, str):
|
|
206
|
+
self._raw_sql = statement
|
|
207
|
+
else:
|
|
208
|
+
dialect = self._dialect
|
|
209
|
+
self._raw_sql = statement.sql(dialect=str(dialect) if dialect else None)
|
|
210
|
+
self._raw_expression = statement
|
|
211
|
+
|
|
212
|
+
self._is_many = is_many if is_many is not None else self._should_auto_detect_many(parameters)
|
|
213
|
+
|
|
214
|
+
self._original_parameters = parameters
|
|
215
|
+
self._process_parameters(*parameters, **kwargs)
|
|
216
|
+
|
|
217
|
+
def _create_auto_config(
|
|
218
|
+
self, _statement: "str | exp.Expression | 'SQL'", _parameters: tuple, _kwargs: "dict[str, Any]"
|
|
219
|
+
) -> "StatementConfig":
|
|
220
|
+
"""Create default StatementConfig when none provided.
|
|
221
|
+
|
|
222
|
+
Args:
|
|
223
|
+
_statement: The SQL statement (unused)
|
|
224
|
+
_parameters: Statement parameters (unused)
|
|
225
|
+
_kwargs: Additional keyword arguments (unused)
|
|
226
|
+
|
|
227
|
+
Returns:
|
|
228
|
+
Default StatementConfig instance
|
|
229
|
+
"""
|
|
230
|
+
return get_default_config()
|
|
231
|
+
|
|
232
|
+
def _normalize_dialect(self, dialect: "DialectType") -> "str | None":
|
|
233
|
+
"""Convert dialect to string representation.
|
|
234
|
+
|
|
235
|
+
Args:
|
|
236
|
+
dialect: Dialect type, string, or None
|
|
237
|
+
|
|
238
|
+
Returns:
|
|
239
|
+
String representation of the dialect or None
|
|
240
|
+
"""
|
|
241
|
+
if dialect is None:
|
|
242
|
+
return None
|
|
243
|
+
if isinstance(dialect, str):
|
|
244
|
+
return dialect
|
|
245
|
+
return dialect.__class__.__name__.lower()
|
|
246
|
+
|
|
247
|
+
def _init_from_sql_object(self, sql_obj: "SQL") -> None:
|
|
248
|
+
"""Initialize instance attributes from existing SQL object.
|
|
249
|
+
|
|
250
|
+
Args:
|
|
251
|
+
sql_obj: Existing SQL object to copy from
|
|
252
|
+
"""
|
|
253
|
+
self._raw_sql = sql_obj.raw_sql
|
|
254
|
+
self._raw_expression = sql_obj.raw_expression
|
|
255
|
+
self._filters = sql_obj.filters.copy()
|
|
256
|
+
self._named_parameters = sql_obj.named_parameters.copy()
|
|
257
|
+
self._positional_parameters = sql_obj.positional_parameters.copy()
|
|
258
|
+
self._sql_param_counters = sql_obj._sql_param_counters.copy()
|
|
259
|
+
self._is_many = sql_obj.is_many
|
|
260
|
+
self._is_script = sql_obj.is_script
|
|
261
|
+
if sql_obj.is_processed:
|
|
262
|
+
self._processed_state = sql_obj.get_processed_state()
|
|
263
|
+
|
|
264
|
+
def _should_auto_detect_many(self, parameters: tuple) -> bool:
|
|
265
|
+
"""Detect execute_many mode from parameter structure.
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
parameters: Parameter tuple to analyze
|
|
269
|
+
|
|
270
|
+
Returns:
|
|
271
|
+
True if parameters indicate batch execution
|
|
272
|
+
"""
|
|
273
|
+
if len(parameters) == 1 and isinstance(parameters[0], list):
|
|
274
|
+
param_list = parameters[0]
|
|
275
|
+
if param_list and all(isinstance(item, (tuple, list)) for item in param_list):
|
|
276
|
+
return len(param_list) > 1
|
|
277
|
+
return False
|
|
278
|
+
|
|
279
|
+
def _process_parameters(self, *parameters: Any, dialect: str | None = None, **kwargs: Any) -> None:
|
|
280
|
+
"""Process and organize parameters and filters.
|
|
281
|
+
|
|
282
|
+
Args:
|
|
283
|
+
*parameters: Variable parameters and filters
|
|
284
|
+
dialect: SQL dialect override
|
|
285
|
+
**kwargs: Additional named parameters
|
|
286
|
+
"""
|
|
287
|
+
if dialect is not None:
|
|
288
|
+
self._dialect = self._normalize_dialect(dialect)
|
|
289
|
+
|
|
290
|
+
if "is_script" in kwargs:
|
|
291
|
+
self._is_script = bool(kwargs.pop("is_script"))
|
|
292
|
+
|
|
293
|
+
self._filters.extend(self._extract_filters(parameters))
|
|
294
|
+
self._normalize_parameters(parameters)
|
|
295
|
+
self._named_parameters.update(kwargs)
|
|
296
|
+
|
|
297
|
+
def _extract_filters(self, parameters: "tuple[Any, ...]") -> "list[StatementFilter]":
|
|
298
|
+
return [p for p in parameters if is_statement_filter(p)]
|
|
299
|
+
|
|
300
|
+
def _normalize_parameters(self, parameters: "tuple[Any, ...]") -> None:
|
|
301
|
+
actual_params = [p for p in parameters if not is_statement_filter(p)]
|
|
302
|
+
if not actual_params:
|
|
303
|
+
return
|
|
304
|
+
|
|
305
|
+
if len(actual_params) == 1:
|
|
306
|
+
param = actual_params[0]
|
|
307
|
+
if isinstance(param, dict):
|
|
308
|
+
self._named_parameters.update(param)
|
|
309
|
+
elif isinstance(param, (list, tuple)):
|
|
310
|
+
if self._is_many:
|
|
311
|
+
self._positional_parameters = list(param)
|
|
312
|
+
else:
|
|
313
|
+
self._positional_parameters.extend(param)
|
|
314
|
+
else:
|
|
315
|
+
self._positional_parameters.append(param)
|
|
316
|
+
else:
|
|
317
|
+
self._positional_parameters.extend(actual_params)
|
|
318
|
+
|
|
319
|
+
@property
|
|
320
|
+
def sql(self) -> str:
|
|
321
|
+
"""Get the raw SQL string."""
|
|
322
|
+
return self._raw_sql
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def raw_sql(self) -> str:
|
|
326
|
+
"""Get raw SQL string (public API).
|
|
327
|
+
|
|
328
|
+
Returns:
|
|
329
|
+
The raw SQL string
|
|
330
|
+
"""
|
|
331
|
+
return self._raw_sql
|
|
332
|
+
|
|
333
|
+
@property
|
|
334
|
+
def parameters(self) -> Any:
|
|
335
|
+
"""Get the original parameters."""
|
|
336
|
+
if self._named_parameters:
|
|
337
|
+
return self._named_parameters
|
|
338
|
+
return self._positional_parameters or []
|
|
339
|
+
|
|
340
|
+
@property
|
|
341
|
+
def positional_parameters(self) -> "list[Any]":
|
|
342
|
+
"""Get positional parameters (public API)."""
|
|
343
|
+
return self._positional_parameters or []
|
|
344
|
+
|
|
345
|
+
@property
|
|
346
|
+
def named_parameters(self) -> "dict[str, Any]":
|
|
347
|
+
"""Get named parameters (public API)."""
|
|
348
|
+
return self._named_parameters
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def original_parameters(self) -> Any:
|
|
352
|
+
"""Get original parameters (public API)."""
|
|
353
|
+
return self._original_parameters
|
|
354
|
+
|
|
355
|
+
@property
|
|
356
|
+
def operation_type(self) -> "OperationType":
|
|
357
|
+
"""SQL operation type."""
|
|
358
|
+
if self._processed_state is Empty:
|
|
359
|
+
return "UNKNOWN"
|
|
360
|
+
return self._processed_state.operation_type
|
|
361
|
+
|
|
362
|
+
@property
|
|
363
|
+
def statement_config(self) -> "StatementConfig":
|
|
364
|
+
"""Statement configuration."""
|
|
365
|
+
return self._statement_config
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def expression(self) -> "exp.Expression | None":
|
|
369
|
+
"""SQLGlot expression."""
|
|
370
|
+
if self._processed_state is not Empty:
|
|
371
|
+
return self._processed_state.parsed_expression
|
|
372
|
+
return self._raw_expression
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def raw_expression(self) -> "exp.Expression | None":
|
|
376
|
+
"""Original expression supplied at construction, if available."""
|
|
377
|
+
return self._raw_expression
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def filters(self) -> "list[StatementFilter]":
|
|
381
|
+
"""Applied filters."""
|
|
382
|
+
return self._filters.copy()
|
|
383
|
+
|
|
384
|
+
def get_filters_view(self) -> "FiltersView":
|
|
385
|
+
"""Get zero-copy filters view (public API).
|
|
386
|
+
|
|
387
|
+
Returns:
|
|
388
|
+
Read-only view of filters without copying
|
|
389
|
+
"""
|
|
390
|
+
return FiltersView(self._filters)
|
|
391
|
+
|
|
392
|
+
@property
|
|
393
|
+
def is_processed(self) -> bool:
|
|
394
|
+
"""Check if SQL has been processed (public API)."""
|
|
395
|
+
return self._processed_state is not Empty
|
|
396
|
+
|
|
397
|
+
def get_processed_state(self) -> Any:
|
|
398
|
+
"""Get processed state (public API)."""
|
|
399
|
+
return self._processed_state
|
|
400
|
+
|
|
401
|
+
@property
|
|
402
|
+
def dialect(self) -> "str | None":
|
|
403
|
+
"""SQL dialect."""
|
|
404
|
+
return self._dialect
|
|
405
|
+
|
|
406
|
+
@property
|
|
407
|
+
def statement_expression(self) -> "exp.Expression | None":
|
|
408
|
+
"""Get parsed statement expression (public API).
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
Parsed SQLGlot expression or None if not parsed
|
|
412
|
+
"""
|
|
413
|
+
if self._processed_state is not Empty:
|
|
414
|
+
return self._processed_state.parsed_expression
|
|
415
|
+
return self._raw_expression
|
|
416
|
+
|
|
417
|
+
@property
|
|
418
|
+
def is_many(self) -> bool:
|
|
419
|
+
"""Check if this is execute_many."""
|
|
420
|
+
return self._is_many
|
|
421
|
+
|
|
422
|
+
@property
|
|
423
|
+
def is_script(self) -> bool:
|
|
424
|
+
"""Check if this is script execution."""
|
|
425
|
+
return self._is_script
|
|
426
|
+
|
|
427
|
+
@property
|
|
428
|
+
def validation_errors(self) -> "list[str]":
|
|
429
|
+
"""Validation errors."""
|
|
430
|
+
if self._processed_state is Empty:
|
|
431
|
+
return []
|
|
432
|
+
return self._processed_state.validation_errors.copy()
|
|
433
|
+
|
|
434
|
+
@property
|
|
435
|
+
def has_errors(self) -> bool:
|
|
436
|
+
"""Check if there are validation errors."""
|
|
437
|
+
return len(self.validation_errors) > 0
|
|
438
|
+
|
|
439
|
+
def returns_rows(self) -> bool:
|
|
440
|
+
"""Check if statement returns rows.
|
|
441
|
+
|
|
442
|
+
Returns:
|
|
443
|
+
True if the SQL statement returns result rows
|
|
444
|
+
"""
|
|
445
|
+
if self._processed_state is Empty:
|
|
446
|
+
self.compile()
|
|
447
|
+
if self._processed_state is Empty:
|
|
448
|
+
return False
|
|
449
|
+
|
|
450
|
+
profile = self._processed_state.operation_profile
|
|
451
|
+
if profile.returns_rows:
|
|
452
|
+
return True
|
|
453
|
+
|
|
454
|
+
op_type = self._processed_state.operation_type
|
|
455
|
+
if op_type in RETURNS_ROWS_OPERATIONS:
|
|
456
|
+
return True
|
|
457
|
+
|
|
458
|
+
if self._processed_state.parsed_expression:
|
|
459
|
+
expr = self._processed_state.parsed_expression
|
|
460
|
+
if isinstance(expr, (exp.Insert, exp.Update, exp.Delete)) and expr.args.get("returning"):
|
|
461
|
+
return True
|
|
462
|
+
|
|
463
|
+
return False
|
|
464
|
+
|
|
465
|
+
def is_modifying_operation(self) -> bool:
|
|
466
|
+
"""Check if the SQL statement is a modifying operation.
|
|
467
|
+
|
|
468
|
+
Returns:
|
|
469
|
+
True if the operation modifies data (INSERT/UPDATE/DELETE)
|
|
470
|
+
"""
|
|
471
|
+
if self._processed_state is Empty:
|
|
472
|
+
return False
|
|
473
|
+
|
|
474
|
+
profile = self._processed_state.operation_profile
|
|
475
|
+
if profile.modifies_rows:
|
|
476
|
+
return True
|
|
477
|
+
|
|
478
|
+
op_type = self._processed_state.operation_type
|
|
479
|
+
if op_type in MODIFYING_OPERATIONS:
|
|
480
|
+
return True
|
|
481
|
+
|
|
482
|
+
if self._processed_state.parsed_expression:
|
|
483
|
+
return isinstance(self._processed_state.parsed_expression, (exp.Insert, exp.Update, exp.Delete, exp.Merge))
|
|
484
|
+
|
|
485
|
+
return False
|
|
486
|
+
|
|
487
|
+
def compile(self) -> "tuple[str, Any]":
|
|
488
|
+
"""Compile SQL statement with parameters.
|
|
489
|
+
|
|
490
|
+
Returns:
|
|
491
|
+
Tuple of compiled SQL string and execution parameters
|
|
492
|
+
"""
|
|
493
|
+
if self._processed_state is Empty:
|
|
494
|
+
try:
|
|
495
|
+
config = self._statement_config
|
|
496
|
+
raw_sql = self._raw_sql
|
|
497
|
+
params = self._named_parameters or self._positional_parameters
|
|
498
|
+
is_many = self._is_many
|
|
499
|
+
compiled_result = pipeline.compile_with_pipeline(
|
|
500
|
+
config, raw_sql, params, is_many=is_many, expression=self._raw_expression
|
|
501
|
+
)
|
|
502
|
+
|
|
503
|
+
self._processed_state = ProcessedState(
|
|
504
|
+
compiled_sql=compiled_result.compiled_sql,
|
|
505
|
+
execution_parameters=compiled_result.execution_parameters,
|
|
506
|
+
parsed_expression=compiled_result.expression,
|
|
507
|
+
operation_type=compiled_result.operation_type,
|
|
508
|
+
parameter_casts=compiled_result.parameter_casts,
|
|
509
|
+
parameter_profile=compiled_result.parameter_profile,
|
|
510
|
+
operation_profile=compiled_result.operation_profile,
|
|
511
|
+
validation_errors=[],
|
|
512
|
+
is_many=self._is_many,
|
|
513
|
+
)
|
|
514
|
+
except sqlspec.exceptions.SQLSpecError:
|
|
515
|
+
raise
|
|
516
|
+
except Exception as e:
|
|
517
|
+
self._processed_state = self._handle_compile_failure(e)
|
|
518
|
+
|
|
519
|
+
return self._processed_state.compiled_sql, self._processed_state.execution_parameters
|
|
520
|
+
|
|
521
|
+
def as_script(self) -> "SQL":
|
|
522
|
+
"""Create copy marked for script execution.
|
|
523
|
+
|
|
524
|
+
Returns:
|
|
525
|
+
New SQL instance configured for script execution
|
|
526
|
+
"""
|
|
527
|
+
original_params = self._original_parameters
|
|
528
|
+
config = self._statement_config
|
|
529
|
+
is_many = self._is_many
|
|
530
|
+
statement_seed = self._raw_expression or self._raw_sql
|
|
531
|
+
new_sql = SQL(statement_seed, *original_params, statement_config=config, is_many=is_many)
|
|
532
|
+
new_sql._named_parameters.update(self._named_parameters)
|
|
533
|
+
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
534
|
+
new_sql._filters = self._filters.copy()
|
|
535
|
+
new_sql._is_script = True
|
|
536
|
+
return new_sql
|
|
537
|
+
|
|
538
|
+
def copy(
|
|
539
|
+
self, statement: "str | exp.Expression | None" = None, parameters: Any | None = None, **kwargs: Any
|
|
540
|
+
) -> "SQL":
|
|
541
|
+
"""Create copy with modifications.
|
|
542
|
+
|
|
543
|
+
Args:
|
|
544
|
+
statement: New SQL statement to use
|
|
545
|
+
parameters: New parameters to use
|
|
546
|
+
**kwargs: Additional modifications
|
|
547
|
+
|
|
548
|
+
Returns:
|
|
549
|
+
New SQL instance with modifications applied
|
|
550
|
+
"""
|
|
551
|
+
statement_expression = self._raw_expression if statement is None else statement
|
|
552
|
+
new_sql = SQL(
|
|
553
|
+
statement_expression or self._raw_sql,
|
|
554
|
+
*(parameters if parameters is not None else self._original_parameters),
|
|
555
|
+
statement_config=self._statement_config,
|
|
556
|
+
is_many=self._is_many,
|
|
557
|
+
**kwargs,
|
|
558
|
+
)
|
|
559
|
+
if parameters is None:
|
|
560
|
+
new_sql._named_parameters.update(self._named_parameters)
|
|
561
|
+
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
562
|
+
new_sql._filters = self._filters.copy()
|
|
563
|
+
return new_sql
|
|
564
|
+
|
|
565
|
+
def _handle_compile_failure(self, error: Exception) -> ProcessedState:
|
|
566
|
+
logger.debug("Processing failed, using fallback: %s", error)
|
|
567
|
+
return ProcessedState(
|
|
568
|
+
compiled_sql=self._raw_sql,
|
|
569
|
+
execution_parameters=self._named_parameters or self._positional_parameters,
|
|
570
|
+
operation_type="UNKNOWN",
|
|
571
|
+
parameter_casts={},
|
|
572
|
+
parameter_profile=ParameterProfile.empty(),
|
|
573
|
+
operation_profile=OperationProfile.empty(),
|
|
574
|
+
is_many=self._is_many,
|
|
575
|
+
)
|
|
576
|
+
|
|
577
|
+
# ==========================================================================
|
|
578
|
+
# Parameter Generation Helpers
|
|
579
|
+
# ==========================================================================
|
|
580
|
+
|
|
581
|
+
def _generate_sql_param_name(self, base_name: str) -> str:
|
|
582
|
+
"""Generate unique parameter name with _sqlspec_ prefix.
|
|
583
|
+
|
|
584
|
+
Uses _sqlspec_ prefix to avoid collision with user-provided parameters.
|
|
585
|
+
Auto-generated parameters are namespaced to prevent conflicts.
|
|
586
|
+
|
|
587
|
+
Args:
|
|
588
|
+
base_name: The base name for the parameter (e.g., column name)
|
|
589
|
+
|
|
590
|
+
Returns:
|
|
591
|
+
A unique parameter name that doesn't exist in current parameters
|
|
592
|
+
"""
|
|
593
|
+
prefixed_base = f"_sqlspec_{base_name}"
|
|
594
|
+
current_index = self._sql_param_counters.get(prefixed_base, 0)
|
|
595
|
+
|
|
596
|
+
if prefixed_base not in self._named_parameters:
|
|
597
|
+
self._sql_param_counters[prefixed_base] = current_index
|
|
598
|
+
return prefixed_base
|
|
599
|
+
|
|
600
|
+
next_index = current_index + 1
|
|
601
|
+
candidate = f"{prefixed_base}_{next_index}"
|
|
602
|
+
|
|
603
|
+
while candidate in self._named_parameters:
|
|
604
|
+
next_index += 1
|
|
605
|
+
if next_index > _MAX_PARAM_COLLISION_ATTEMPTS:
|
|
606
|
+
return f"{prefixed_base}_{uuid.uuid4().hex[:8]}"
|
|
607
|
+
candidate = f"{prefixed_base}_{next_index}"
|
|
608
|
+
|
|
609
|
+
self._sql_param_counters[prefixed_base] = next_index
|
|
610
|
+
return candidate
|
|
611
|
+
|
|
612
|
+
def _get_or_parse_expression(self) -> exp.Expression:
|
|
613
|
+
"""Get the current expression or parse the raw SQL.
|
|
614
|
+
|
|
615
|
+
Returns:
|
|
616
|
+
The SQLGlot expression for this statement
|
|
617
|
+
"""
|
|
618
|
+
if self.statement_expression is not None:
|
|
619
|
+
return self.statement_expression.copy()
|
|
620
|
+
if not self._statement_config.enable_parsing:
|
|
621
|
+
return exp.Select().from_(f"({self._raw_sql})")
|
|
622
|
+
try:
|
|
623
|
+
return sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
|
|
624
|
+
except ParseError:
|
|
625
|
+
return exp.Select().from_(f"({self._raw_sql})")
|
|
626
|
+
|
|
627
|
+
def _create_modified_copy_with_expression(self, new_expr: exp.Expression) -> "SQL":
|
|
628
|
+
"""Create a new SQL instance with a modified expression.
|
|
629
|
+
|
|
630
|
+
Args:
|
|
631
|
+
new_expr: The new SQLGlot expression
|
|
632
|
+
|
|
633
|
+
Returns:
|
|
634
|
+
New SQL instance with the expression and copied state
|
|
635
|
+
"""
|
|
636
|
+
new_sql = SQL(
|
|
637
|
+
new_expr, *self._original_parameters, statement_config=self._statement_config, is_many=self._is_many
|
|
638
|
+
)
|
|
639
|
+
new_sql._named_parameters.update(self._named_parameters)
|
|
640
|
+
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
641
|
+
new_sql._filters = self._filters.copy()
|
|
642
|
+
new_sql._sql_param_counters = self._sql_param_counters.copy()
|
|
643
|
+
return new_sql
|
|
644
|
+
|
|
645
|
+
def add_named_parameter(self, name: str, value: Any) -> "SQL":
|
|
646
|
+
"""Add a named parameter and return a new SQL instance.
|
|
647
|
+
|
|
648
|
+
Args:
|
|
649
|
+
name: Parameter name
|
|
650
|
+
value: Parameter value
|
|
651
|
+
|
|
652
|
+
Returns:
|
|
653
|
+
New SQL instance with the added parameter
|
|
654
|
+
"""
|
|
655
|
+
original_params = self._original_parameters
|
|
656
|
+
config = self._statement_config
|
|
657
|
+
is_many = self._is_many
|
|
658
|
+
statement_seed = self._raw_expression or self._raw_sql
|
|
659
|
+
new_sql = SQL(statement_seed, *original_params, statement_config=config, is_many=is_many)
|
|
660
|
+
new_sql._named_parameters.update(self._named_parameters)
|
|
661
|
+
new_sql._named_parameters[name] = value
|
|
662
|
+
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
663
|
+
new_sql._filters = self._filters.copy()
|
|
664
|
+
return new_sql
|
|
665
|
+
|
|
666
|
+
def where(self, condition: "str | exp.Expression") -> "SQL":
|
|
667
|
+
"""Add WHERE condition to the SQL statement.
|
|
668
|
+
|
|
669
|
+
Args:
|
|
670
|
+
condition: WHERE condition as string or SQLGlot expression
|
|
671
|
+
|
|
672
|
+
Returns:
|
|
673
|
+
New SQL instance with the WHERE condition applied
|
|
674
|
+
"""
|
|
675
|
+
if self.statement_expression is not None:
|
|
676
|
+
current_expr = self.statement_expression.copy()
|
|
677
|
+
elif not self._statement_config.enable_parsing:
|
|
678
|
+
current_expr = exp.Select().from_(f"({self._raw_sql})")
|
|
679
|
+
else:
|
|
680
|
+
try:
|
|
681
|
+
current_expr = sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
|
|
682
|
+
except ParseError:
|
|
683
|
+
subquery_sql = f"SELECT * FROM ({self._raw_sql}) AS subquery"
|
|
684
|
+
current_expr = sqlglot.parse_one(subquery_sql, dialect=self._dialect)
|
|
685
|
+
|
|
686
|
+
condition_expr: exp.Expression
|
|
687
|
+
if isinstance(condition, str):
|
|
688
|
+
if not self._statement_config.enable_parsing:
|
|
689
|
+
condition_expr = exp.Condition(this=condition)
|
|
690
|
+
else:
|
|
691
|
+
try:
|
|
692
|
+
condition_expr = sqlglot.parse_one(condition, dialect=self._dialect, into=exp.Condition)
|
|
693
|
+
except ParseError:
|
|
694
|
+
condition_expr = exp.Condition(this=condition)
|
|
695
|
+
else:
|
|
696
|
+
condition_expr = condition
|
|
697
|
+
|
|
698
|
+
if isinstance(current_expr, exp.Select) or supports_where(current_expr):
|
|
699
|
+
new_expr = current_expr.where(condition_expr, copy=False)
|
|
700
|
+
else:
|
|
701
|
+
new_expr = exp.Select().from_(current_expr).where(condition_expr, copy=False)
|
|
702
|
+
|
|
703
|
+
original_params = self._original_parameters
|
|
704
|
+
config = self._statement_config
|
|
705
|
+
is_many = self._is_many
|
|
706
|
+
new_sql = SQL(new_expr, *original_params, statement_config=config, is_many=is_many)
|
|
707
|
+
|
|
708
|
+
new_sql._named_parameters.update(self._named_parameters)
|
|
709
|
+
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
710
|
+
new_sql._filters = self._filters.copy()
|
|
711
|
+
return new_sql
|
|
712
|
+
|
|
713
|
+
# ==========================================================================
|
|
714
|
+
# Parameterized WHERE Methods (using shared utilities)
|
|
715
|
+
# ==========================================================================
|
|
716
|
+
|
|
717
|
+
def where_eq(self, column: "str | exp.Column", value: Any) -> "SQL":
|
|
718
|
+
"""Add WHERE column = value condition.
|
|
719
|
+
|
|
720
|
+
Args:
|
|
721
|
+
column: Column name or expression
|
|
722
|
+
value: Value to compare against
|
|
723
|
+
|
|
724
|
+
Returns:
|
|
725
|
+
New SQL instance with WHERE condition applied
|
|
726
|
+
"""
|
|
727
|
+
expression = self._get_or_parse_expression()
|
|
728
|
+
col_name = extract_column_name(column)
|
|
729
|
+
param_name = self._generate_sql_param_name(col_name)
|
|
730
|
+
condition = create_condition(column, param_name, expr_eq)
|
|
731
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
732
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
733
|
+
new_sql._named_parameters[param_name] = value
|
|
734
|
+
return new_sql
|
|
735
|
+
|
|
736
|
+
def where_neq(self, column: "str | exp.Column", value: Any) -> "SQL":
|
|
737
|
+
"""Add WHERE column != value condition.
|
|
738
|
+
|
|
739
|
+
Args:
|
|
740
|
+
column: Column name or expression
|
|
741
|
+
value: Value to compare against
|
|
742
|
+
|
|
743
|
+
Returns:
|
|
744
|
+
New SQL instance with WHERE condition applied
|
|
745
|
+
"""
|
|
746
|
+
expression = self._get_or_parse_expression()
|
|
747
|
+
col_name = extract_column_name(column)
|
|
748
|
+
param_name = self._generate_sql_param_name(col_name)
|
|
749
|
+
condition = create_condition(column, param_name, expr_neq)
|
|
750
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
751
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
752
|
+
new_sql._named_parameters[param_name] = value
|
|
753
|
+
return new_sql
|
|
754
|
+
|
|
755
|
+
def where_lt(self, column: "str | exp.Column", value: Any) -> "SQL":
|
|
756
|
+
"""Add WHERE column < value condition.
|
|
757
|
+
|
|
758
|
+
Args:
|
|
759
|
+
column: Column name or expression
|
|
760
|
+
value: Value to compare against
|
|
761
|
+
|
|
762
|
+
Returns:
|
|
763
|
+
New SQL instance with WHERE condition applied
|
|
764
|
+
"""
|
|
765
|
+
expression = self._get_or_parse_expression()
|
|
766
|
+
col_name = extract_column_name(column)
|
|
767
|
+
param_name = self._generate_sql_param_name(col_name)
|
|
768
|
+
condition = create_condition(column, param_name, expr_lt)
|
|
769
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
770
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
771
|
+
new_sql._named_parameters[param_name] = value
|
|
772
|
+
return new_sql
|
|
773
|
+
|
|
774
|
+
def where_lte(self, column: "str | exp.Column", value: Any) -> "SQL":
|
|
775
|
+
"""Add WHERE column <= value condition.
|
|
776
|
+
|
|
777
|
+
Args:
|
|
778
|
+
column: Column name or expression
|
|
779
|
+
value: Value to compare against
|
|
780
|
+
|
|
781
|
+
Returns:
|
|
782
|
+
New SQL instance with WHERE condition applied
|
|
783
|
+
"""
|
|
784
|
+
expression = self._get_or_parse_expression()
|
|
785
|
+
col_name = extract_column_name(column)
|
|
786
|
+
param_name = self._generate_sql_param_name(col_name)
|
|
787
|
+
condition = create_condition(column, param_name, expr_lte)
|
|
788
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
789
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
790
|
+
new_sql._named_parameters[param_name] = value
|
|
791
|
+
return new_sql
|
|
792
|
+
|
|
793
|
+
def where_gt(self, column: "str | exp.Column", value: Any) -> "SQL":
|
|
794
|
+
"""Add WHERE column > value condition.
|
|
795
|
+
|
|
796
|
+
Args:
|
|
797
|
+
column: Column name or expression
|
|
798
|
+
value: Value to compare against
|
|
799
|
+
|
|
800
|
+
Returns:
|
|
801
|
+
New SQL instance with WHERE condition applied
|
|
802
|
+
"""
|
|
803
|
+
expression = self._get_or_parse_expression()
|
|
804
|
+
col_name = extract_column_name(column)
|
|
805
|
+
param_name = self._generate_sql_param_name(col_name)
|
|
806
|
+
condition = create_condition(column, param_name, expr_gt)
|
|
807
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
808
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
809
|
+
new_sql._named_parameters[param_name] = value
|
|
810
|
+
return new_sql
|
|
811
|
+
|
|
812
|
+
def where_gte(self, column: "str | exp.Column", value: Any) -> "SQL":
|
|
813
|
+
"""Add WHERE column >= value condition.
|
|
814
|
+
|
|
815
|
+
Args:
|
|
816
|
+
column: Column name or expression
|
|
817
|
+
value: Value to compare against
|
|
818
|
+
|
|
819
|
+
Returns:
|
|
820
|
+
New SQL instance with WHERE condition applied
|
|
821
|
+
"""
|
|
822
|
+
expression = self._get_or_parse_expression()
|
|
823
|
+
col_name = extract_column_name(column)
|
|
824
|
+
param_name = self._generate_sql_param_name(col_name)
|
|
825
|
+
condition = create_condition(column, param_name, expr_gte)
|
|
826
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
827
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
828
|
+
new_sql._named_parameters[param_name] = value
|
|
829
|
+
return new_sql
|
|
830
|
+
|
|
831
|
+
def where_like(self, column: "str | exp.Column", pattern: str) -> "SQL":
|
|
832
|
+
"""Add WHERE column LIKE pattern condition.
|
|
833
|
+
|
|
834
|
+
Args:
|
|
835
|
+
column: Column name or expression
|
|
836
|
+
pattern: LIKE pattern (e.g., '%search%')
|
|
837
|
+
|
|
838
|
+
Returns:
|
|
839
|
+
New SQL instance with WHERE condition applied
|
|
840
|
+
"""
|
|
841
|
+
expression = self._get_or_parse_expression()
|
|
842
|
+
col_name = extract_column_name(column)
|
|
843
|
+
param_name = self._generate_sql_param_name(col_name)
|
|
844
|
+
condition = create_condition(column, param_name, expr_like)
|
|
845
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
846
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
847
|
+
new_sql._named_parameters[param_name] = pattern
|
|
848
|
+
return new_sql
|
|
849
|
+
|
|
850
|
+
def where_ilike(self, column: "str | exp.Column", pattern: str) -> "SQL":
|
|
851
|
+
"""Add WHERE column ILIKE pattern condition (case-insensitive).
|
|
852
|
+
|
|
853
|
+
Args:
|
|
854
|
+
column: Column name or expression
|
|
855
|
+
pattern: ILIKE pattern (e.g., '%search%')
|
|
856
|
+
|
|
857
|
+
Returns:
|
|
858
|
+
New SQL instance with WHERE condition applied
|
|
859
|
+
"""
|
|
860
|
+
expression = self._get_or_parse_expression()
|
|
861
|
+
col_name = extract_column_name(column)
|
|
862
|
+
param_name = self._generate_sql_param_name(col_name)
|
|
863
|
+
condition = create_condition(column, param_name, expr_ilike)
|
|
864
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
865
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
866
|
+
new_sql._named_parameters[param_name] = pattern
|
|
867
|
+
return new_sql
|
|
868
|
+
|
|
869
|
+
def where_is_null(self, column: "str | exp.Column") -> "SQL":
|
|
870
|
+
"""Add WHERE column IS NULL condition.
|
|
871
|
+
|
|
872
|
+
Args:
|
|
873
|
+
column: Column name or expression
|
|
874
|
+
|
|
875
|
+
Returns:
|
|
876
|
+
New SQL instance with WHERE condition applied
|
|
877
|
+
"""
|
|
878
|
+
expression = self._get_or_parse_expression()
|
|
879
|
+
condition = create_condition(column, "_unused", expr_is_null)
|
|
880
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
881
|
+
return self._create_modified_copy_with_expression(new_expr)
|
|
882
|
+
|
|
883
|
+
def where_is_not_null(self, column: "str | exp.Column") -> "SQL":
|
|
884
|
+
"""Add WHERE column IS NOT NULL condition.
|
|
885
|
+
|
|
886
|
+
Args:
|
|
887
|
+
column: Column name or expression
|
|
888
|
+
|
|
889
|
+
Returns:
|
|
890
|
+
New SQL instance with WHERE condition applied
|
|
891
|
+
"""
|
|
892
|
+
expression = self._get_or_parse_expression()
|
|
893
|
+
condition = create_condition(column, "_unused", expr_is_not_null)
|
|
894
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
895
|
+
return self._create_modified_copy_with_expression(new_expr)
|
|
896
|
+
|
|
897
|
+
def where_in(self, column: "str | exp.Column", values: "Sequence[Any]") -> "SQL":
|
|
898
|
+
"""Add WHERE column IN (values) condition.
|
|
899
|
+
|
|
900
|
+
Args:
|
|
901
|
+
column: Column name or expression
|
|
902
|
+
values: Sequence of values for IN clause
|
|
903
|
+
|
|
904
|
+
Returns:
|
|
905
|
+
New SQL instance with WHERE condition applied
|
|
906
|
+
"""
|
|
907
|
+
if not values:
|
|
908
|
+
expression = self._get_or_parse_expression()
|
|
909
|
+
false_condition = exp.EQ(this=exp.Literal.number(1), expression=exp.Literal.number(0))
|
|
910
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, false_condition))
|
|
911
|
+
return self._create_modified_copy_with_expression(new_expr)
|
|
912
|
+
|
|
913
|
+
expression = self._get_or_parse_expression()
|
|
914
|
+
col_name = extract_column_name(column)
|
|
915
|
+
|
|
916
|
+
param_names: list[str] = []
|
|
917
|
+
param_values: dict[str, Any] = {}
|
|
918
|
+
for i, val in enumerate(values):
|
|
919
|
+
param_name = self._generate_sql_param_name(f"{col_name}_in_{i}")
|
|
920
|
+
param_names.append(param_name)
|
|
921
|
+
param_values[param_name] = val
|
|
922
|
+
|
|
923
|
+
condition = create_in_condition(column, param_names)
|
|
924
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
925
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
926
|
+
new_sql._named_parameters.update(param_values)
|
|
927
|
+
return new_sql
|
|
928
|
+
|
|
929
|
+
def where_not_in(self, column: "str | exp.Column", values: "Sequence[Any]") -> "SQL":
|
|
930
|
+
"""Add WHERE column NOT IN (values) condition.
|
|
931
|
+
|
|
932
|
+
Args:
|
|
933
|
+
column: Column name or expression
|
|
934
|
+
values: Sequence of values for NOT IN clause
|
|
935
|
+
|
|
936
|
+
Returns:
|
|
937
|
+
New SQL instance with WHERE condition applied
|
|
938
|
+
"""
|
|
939
|
+
if not values:
|
|
940
|
+
return self
|
|
941
|
+
|
|
942
|
+
expression = self._get_or_parse_expression()
|
|
943
|
+
col_name = extract_column_name(column)
|
|
944
|
+
|
|
945
|
+
param_names: list[str] = []
|
|
946
|
+
param_values: dict[str, Any] = {}
|
|
947
|
+
for i, val in enumerate(values):
|
|
948
|
+
param_name = self._generate_sql_param_name(f"{col_name}_not_in_{i}")
|
|
949
|
+
param_names.append(param_name)
|
|
950
|
+
param_values[param_name] = val
|
|
951
|
+
|
|
952
|
+
condition = create_not_in_condition(column, param_names)
|
|
953
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
954
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
955
|
+
new_sql._named_parameters.update(param_values)
|
|
956
|
+
return new_sql
|
|
957
|
+
|
|
958
|
+
def where_between(self, column: "str | exp.Column", low: Any, high: Any) -> "SQL":
|
|
959
|
+
"""Add WHERE column BETWEEN low AND high condition.
|
|
960
|
+
|
|
961
|
+
Args:
|
|
962
|
+
column: Column name or expression
|
|
963
|
+
low: Lower bound value
|
|
964
|
+
high: Upper bound value
|
|
965
|
+
|
|
966
|
+
Returns:
|
|
967
|
+
New SQL instance with WHERE condition applied
|
|
968
|
+
"""
|
|
969
|
+
expression = self._get_or_parse_expression()
|
|
970
|
+
col_name = extract_column_name(column)
|
|
971
|
+
low_param = self._generate_sql_param_name(f"{col_name}_low")
|
|
972
|
+
high_param = self._generate_sql_param_name(f"{col_name}_high")
|
|
973
|
+
condition = create_between_condition(column, low_param, high_param)
|
|
974
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
|
|
975
|
+
new_sql = self._create_modified_copy_with_expression(new_expr)
|
|
976
|
+
new_sql._named_parameters[low_param] = low
|
|
977
|
+
new_sql._named_parameters[high_param] = high
|
|
978
|
+
return new_sql
|
|
979
|
+
|
|
980
|
+
def order_by(self, *items: "str | exp.Expression", desc: bool = False) -> "SQL":
|
|
981
|
+
"""Add ORDER BY clause to the SQL statement.
|
|
982
|
+
|
|
983
|
+
Args:
|
|
984
|
+
*items: ORDER BY expressions as strings or SQLGlot expressions
|
|
985
|
+
desc: Apply descending order to each item
|
|
986
|
+
|
|
987
|
+
Returns:
|
|
988
|
+
New SQL instance with ORDER BY applied
|
|
989
|
+
"""
|
|
990
|
+
if not items:
|
|
991
|
+
return self
|
|
992
|
+
|
|
993
|
+
if self.statement_expression is not None:
|
|
994
|
+
current_expr = self.statement_expression.copy()
|
|
995
|
+
elif not self._statement_config.enable_parsing:
|
|
996
|
+
current_expr = exp.Select().from_(f"({self._raw_sql})")
|
|
997
|
+
else:
|
|
998
|
+
try:
|
|
999
|
+
current_expr = sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
|
|
1000
|
+
except ParseError:
|
|
1001
|
+
current_expr = exp.Select().from_(f"({self._raw_sql})")
|
|
1002
|
+
|
|
1003
|
+
def parse_order_item(order_item: str) -> exp.Expression:
|
|
1004
|
+
normalized = order_item.strip()
|
|
1005
|
+
if not normalized:
|
|
1006
|
+
return exp.column(order_item)
|
|
1007
|
+
|
|
1008
|
+
if self._statement_config.enable_parsing:
|
|
1009
|
+
try:
|
|
1010
|
+
parsed = sqlglot.parse_one(normalized, dialect=self._dialect, into=exp.Ordered)
|
|
1011
|
+
except ParseError:
|
|
1012
|
+
parsed = None
|
|
1013
|
+
if parsed is not None:
|
|
1014
|
+
return parsed
|
|
1015
|
+
|
|
1016
|
+
parts = normalized.rsplit(None, 1)
|
|
1017
|
+
if len(parts) == _ORDER_PARTS_COUNT and parts[1].lower() in {"asc", "desc"}:
|
|
1018
|
+
base_expr = exp.column(parts[0]) if parts[0] else exp.column(normalized)
|
|
1019
|
+
return base_expr.desc() if parts[1].lower() == "desc" else base_expr.asc()
|
|
1020
|
+
|
|
1021
|
+
return exp.column(normalized)
|
|
1022
|
+
|
|
1023
|
+
new_expr = current_expr
|
|
1024
|
+
for item in items:
|
|
1025
|
+
if isinstance(item, str):
|
|
1026
|
+
order_expr = parse_order_item(item)
|
|
1027
|
+
if desc and not isinstance(order_expr, exp.Ordered):
|
|
1028
|
+
order_expr = order_expr.desc()
|
|
1029
|
+
else:
|
|
1030
|
+
order_expr = item.desc() if desc and not isinstance(item, exp.Ordered) else item
|
|
1031
|
+
if isinstance(new_expr, exp.Select):
|
|
1032
|
+
new_expr = new_expr.order_by(order_expr, copy=False)
|
|
1033
|
+
else:
|
|
1034
|
+
new_expr = exp.Select().from_(new_expr).order_by(order_expr)
|
|
1035
|
+
|
|
1036
|
+
original_params = self._original_parameters
|
|
1037
|
+
config = self._statement_config
|
|
1038
|
+
is_many = self._is_many
|
|
1039
|
+
new_sql = SQL(new_expr, *original_params, statement_config=config, is_many=is_many)
|
|
1040
|
+
|
|
1041
|
+
new_sql._named_parameters.update(self._named_parameters)
|
|
1042
|
+
new_sql._positional_parameters = self._positional_parameters.copy()
|
|
1043
|
+
new_sql._filters = self._filters.copy()
|
|
1044
|
+
return new_sql
|
|
1045
|
+
|
|
1046
|
+
# ==========================================================================
|
|
1047
|
+
# Pagination Methods
|
|
1048
|
+
# ==========================================================================
|
|
1049
|
+
|
|
1050
|
+
def limit(self, value: int) -> "SQL":
|
|
1051
|
+
"""Add LIMIT clause to the SQL statement.
|
|
1052
|
+
|
|
1053
|
+
Args:
|
|
1054
|
+
value: Maximum number of rows to return
|
|
1055
|
+
|
|
1056
|
+
Returns:
|
|
1057
|
+
New SQL instance with LIMIT applied
|
|
1058
|
+
|
|
1059
|
+
Raises:
|
|
1060
|
+
SQLSpecError: If statement is not a SELECT
|
|
1061
|
+
"""
|
|
1062
|
+
expression = self._get_or_parse_expression()
|
|
1063
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_limit(e, value))
|
|
1064
|
+
return self._create_modified_copy_with_expression(new_expr)
|
|
1065
|
+
|
|
1066
|
+
def offset(self, value: int) -> "SQL":
|
|
1067
|
+
"""Add OFFSET clause to the SQL statement.
|
|
1068
|
+
|
|
1069
|
+
Args:
|
|
1070
|
+
value: Number of rows to skip
|
|
1071
|
+
|
|
1072
|
+
Returns:
|
|
1073
|
+
New SQL instance with OFFSET applied
|
|
1074
|
+
|
|
1075
|
+
Raises:
|
|
1076
|
+
SQLSpecError: If statement is not a SELECT
|
|
1077
|
+
"""
|
|
1078
|
+
expression = self._get_or_parse_expression()
|
|
1079
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_offset(e, value))
|
|
1080
|
+
return self._create_modified_copy_with_expression(new_expr)
|
|
1081
|
+
|
|
1082
|
+
def paginate(self, page: int, page_size: int) -> "SQL":
|
|
1083
|
+
"""Add LIMIT and OFFSET for pagination.
|
|
1084
|
+
|
|
1085
|
+
Args:
|
|
1086
|
+
page: Page number (1-indexed)
|
|
1087
|
+
page_size: Number of items per page
|
|
1088
|
+
|
|
1089
|
+
Returns:
|
|
1090
|
+
New SQL instance with LIMIT and OFFSET applied
|
|
1091
|
+
|
|
1092
|
+
Example:
|
|
1093
|
+
# Get page 3 with 20 items per page
|
|
1094
|
+
stmt = SQL("SELECT * FROM users").paginate(3, 20)
|
|
1095
|
+
# Results in: SELECT * FROM users LIMIT 20 OFFSET 40
|
|
1096
|
+
"""
|
|
1097
|
+
if page < 1:
|
|
1098
|
+
msg = "paginate page must be >= 1"
|
|
1099
|
+
raise sqlspec.exceptions.SQLSpecError(msg)
|
|
1100
|
+
if page_size < 1:
|
|
1101
|
+
msg = "paginate page_size must be >= 1"
|
|
1102
|
+
raise sqlspec.exceptions.SQLSpecError(msg)
|
|
1103
|
+
offset_value = (page - 1) * page_size
|
|
1104
|
+
return self.limit(page_size).offset(offset_value)
|
|
1105
|
+
|
|
1106
|
+
# ==========================================================================
|
|
1107
|
+
# Column Projection Methods
|
|
1108
|
+
# ==========================================================================
|
|
1109
|
+
|
|
1110
|
+
def select_only(self, *columns: "str | exp.Expression") -> "SQL":
|
|
1111
|
+
"""Replace SELECT columns with only the specified columns.
|
|
1112
|
+
|
|
1113
|
+
This is useful for narrowing down the columns returned by a query
|
|
1114
|
+
without modifying the FROM clause or WHERE conditions.
|
|
1115
|
+
|
|
1116
|
+
Args:
|
|
1117
|
+
*columns: Column names or expressions to select
|
|
1118
|
+
|
|
1119
|
+
Returns:
|
|
1120
|
+
New SQL instance with only the specified columns
|
|
1121
|
+
|
|
1122
|
+
Example:
|
|
1123
|
+
stmt = SQL("SELECT * FROM users WHERE active = 1")
|
|
1124
|
+
narrow = stmt.select_only("id", "name", "email")
|
|
1125
|
+
# Results in: SELECT id, name, email FROM users WHERE active = 1
|
|
1126
|
+
"""
|
|
1127
|
+
if not columns:
|
|
1128
|
+
return self
|
|
1129
|
+
|
|
1130
|
+
expression = self._get_or_parse_expression()
|
|
1131
|
+
new_expr = safe_modify_with_cte(expression, lambda e: apply_select_only(e, columns))
|
|
1132
|
+
return self._create_modified_copy_with_expression(new_expr)
|
|
1133
|
+
|
|
1134
|
+
def explain(self, analyze: bool = False, verbose: bool = False, format: "str | None" = None) -> "SQL":
|
|
1135
|
+
"""Create an EXPLAIN statement for this SQL.
|
|
1136
|
+
|
|
1137
|
+
Wraps the current SQL statement in an EXPLAIN clause with
|
|
1138
|
+
dialect-aware syntax generation.
|
|
1139
|
+
|
|
1140
|
+
Args:
|
|
1141
|
+
analyze: Execute the statement and show actual runtime statistics
|
|
1142
|
+
verbose: Show additional information
|
|
1143
|
+
format: Output format (TEXT, JSON, XML, YAML, TREE, TRADITIONAL)
|
|
1144
|
+
|
|
1145
|
+
Returns:
|
|
1146
|
+
New SQL instance containing the EXPLAIN statement
|
|
1147
|
+
|
|
1148
|
+
Examples:
|
|
1149
|
+
Basic EXPLAIN:
|
|
1150
|
+
stmt = SQL("SELECT * FROM users")
|
|
1151
|
+
explain_stmt = stmt.explain()
|
|
1152
|
+
|
|
1153
|
+
With options:
|
|
1154
|
+
explain_stmt = stmt.explain(analyze=True, format="json")
|
|
1155
|
+
"""
|
|
1156
|
+
from sqlspec.builder import Explain
|
|
1157
|
+
|
|
1158
|
+
fmt = None
|
|
1159
|
+
if format is not None:
|
|
1160
|
+
fmt = ExplainFormat(format.lower())
|
|
1161
|
+
|
|
1162
|
+
options = ExplainOptions(analyze=analyze, verbose=verbose, format=fmt)
|
|
1163
|
+
|
|
1164
|
+
explain_builder = Explain(self, dialect=self._dialect, options=options)
|
|
1165
|
+
return explain_builder.build()
|
|
1166
|
+
|
|
1167
|
+
def builder(self, dialect: "DialectType | None" = None) -> "QueryBuilder":
|
|
1168
|
+
"""Create a query builder seeded from this SQL statement.
|
|
1169
|
+
|
|
1170
|
+
Args:
|
|
1171
|
+
dialect: Optional SQL dialect override for parsing and rendering.
|
|
1172
|
+
|
|
1173
|
+
Returns:
|
|
1174
|
+
QueryBuilder instance initialized with the parsed statement.
|
|
1175
|
+
|
|
1176
|
+
Raises:
|
|
1177
|
+
SQLBuilderError: If the statement cannot be parsed.
|
|
1178
|
+
|
|
1179
|
+
Notes:
|
|
1180
|
+
Statements outside the DML set return an ExpressionBuilder without
|
|
1181
|
+
DML-specific helper methods.
|
|
1182
|
+
"""
|
|
1183
|
+
if self._is_many:
|
|
1184
|
+
msg = "QueryBuilder does not support execute_many SQL statements."
|
|
1185
|
+
raise sqlspec.exceptions.SQLBuilderError(msg)
|
|
1186
|
+
|
|
1187
|
+
from sqlspec.builder import Delete, ExpressionBuilder, Insert, Merge, Select, Update
|
|
1188
|
+
|
|
1189
|
+
builder_dialect = dialect or self._dialect
|
|
1190
|
+
converter = self._statement_config.parameter_converter or ParameterConverter(
|
|
1191
|
+
self._statement_config.parameter_validator
|
|
1192
|
+
)
|
|
1193
|
+
raw_params = self.parameters
|
|
1194
|
+
converted_sql, converted_params = converter.convert_placeholder_style(
|
|
1195
|
+
self._raw_sql, raw_params, ParameterStyle.NAMED_COLON, is_many=False
|
|
1196
|
+
)
|
|
1197
|
+
|
|
1198
|
+
if self._raw_expression is not None and converted_sql == self._raw_sql and (builder_dialect == self._dialect):
|
|
1199
|
+
expression = self._raw_expression.copy()
|
|
1200
|
+
else:
|
|
1201
|
+
try:
|
|
1202
|
+
expression = sqlglot.parse_one(converted_sql, dialect=builder_dialect)
|
|
1203
|
+
except ParseError as exc:
|
|
1204
|
+
msg = f"Failed to parse SQL for builder: {exc}"
|
|
1205
|
+
raise sqlspec.exceptions.SQLBuilderError(msg) from exc
|
|
1206
|
+
|
|
1207
|
+
base_expression = expression
|
|
1208
|
+
ctes: list[exp.CTE] | None = None
|
|
1209
|
+
if isinstance(expression, exp.With):
|
|
1210
|
+
if expression.this is None:
|
|
1211
|
+
msg = "WITH expression does not include a base statement."
|
|
1212
|
+
raise sqlspec.exceptions.SQLBuilderError(msg)
|
|
1213
|
+
base_expression = expression.this
|
|
1214
|
+
ctes = list(expression.expressions)
|
|
1215
|
+
|
|
1216
|
+
builder: QueryBuilder
|
|
1217
|
+
if isinstance(base_expression, (exp.Select, exp.Union, exp.Except, exp.Intersect, exp.Values)):
|
|
1218
|
+
builder = Select(dialect=builder_dialect)
|
|
1219
|
+
builder.set_expression(base_expression.copy())
|
|
1220
|
+
elif isinstance(base_expression, exp.Insert):
|
|
1221
|
+
builder = Insert(dialect=builder_dialect)
|
|
1222
|
+
builder.set_expression(base_expression.copy())
|
|
1223
|
+
elif isinstance(base_expression, exp.Update):
|
|
1224
|
+
builder = Update(dialect=builder_dialect)
|
|
1225
|
+
builder.set_expression(base_expression.copy())
|
|
1226
|
+
elif isinstance(base_expression, exp.Delete):
|
|
1227
|
+
builder = Delete(dialect=builder_dialect)
|
|
1228
|
+
builder.set_expression(base_expression.copy())
|
|
1229
|
+
elif isinstance(base_expression, exp.Merge):
|
|
1230
|
+
builder = Merge(dialect=builder_dialect)
|
|
1231
|
+
builder.set_expression(base_expression.copy())
|
|
1232
|
+
else:
|
|
1233
|
+
builder = ExpressionBuilder(base_expression.copy(), dialect=builder_dialect)
|
|
1234
|
+
|
|
1235
|
+
if ctes:
|
|
1236
|
+
builder.load_ctes(ctes)
|
|
1237
|
+
|
|
1238
|
+
if isinstance(converted_params, Mapping):
|
|
1239
|
+
builder.load_parameters(converted_params)
|
|
1240
|
+
return builder
|
|
1241
|
+
|
|
1242
|
+
if (
|
|
1243
|
+
converted_params
|
|
1244
|
+
and isinstance(converted_params, Sequence)
|
|
1245
|
+
and not isinstance(converted_params, (str, bytes, bytearray))
|
|
1246
|
+
):
|
|
1247
|
+
param_info = converter.validator.extract_parameters(converted_sql)
|
|
1248
|
+
param_map: dict[str, Any] = {}
|
|
1249
|
+
for index, param in enumerate(param_info):
|
|
1250
|
+
if index >= len(converted_params):
|
|
1251
|
+
break
|
|
1252
|
+
param_name = param.name or f"param_{param.ordinal}"
|
|
1253
|
+
param_map[param_name] = converted_params[index]
|
|
1254
|
+
builder.load_parameters(param_map)
|
|
1255
|
+
|
|
1256
|
+
return builder
|
|
1257
|
+
|
|
1258
|
+
def __hash__(self) -> int:
|
|
1259
|
+
"""Hash value computation."""
|
|
1260
|
+
if self._hash is None:
|
|
1261
|
+
positional_tuple = tuple(self._positional_parameters)
|
|
1262
|
+
named_tuple = tuple(sorted(self._named_parameters.items())) if self._named_parameters else ()
|
|
1263
|
+
raw_sql = self._raw_sql
|
|
1264
|
+
is_many = self._is_many
|
|
1265
|
+
is_script = self._is_script
|
|
1266
|
+
self._hash = hash((raw_sql, positional_tuple, named_tuple, is_many, is_script))
|
|
1267
|
+
return self._hash
|
|
1268
|
+
|
|
1269
|
+
def __eq__(self, other: object) -> bool:
|
|
1270
|
+
"""Equality comparison."""
|
|
1271
|
+
if not isinstance(other, SQL):
|
|
1272
|
+
return False
|
|
1273
|
+
return (
|
|
1274
|
+
self._raw_sql == other._raw_sql
|
|
1275
|
+
and self._positional_parameters == other._positional_parameters
|
|
1276
|
+
and self._named_parameters == other._named_parameters
|
|
1277
|
+
and self._is_many == other._is_many
|
|
1278
|
+
and self._is_script == other._is_script
|
|
1279
|
+
)
|
|
1280
|
+
|
|
1281
|
+
def __repr__(self) -> str:
|
|
1282
|
+
"""String representation."""
|
|
1283
|
+
params_parts = []
|
|
1284
|
+
if self._positional_parameters:
|
|
1285
|
+
params_parts.append(f"params={self._positional_parameters}")
|
|
1286
|
+
if self._named_parameters:
|
|
1287
|
+
params_parts.append(f"named_params={self._named_parameters}")
|
|
1288
|
+
params_str = f", {', '.join(params_parts)}" if params_parts else ""
|
|
1289
|
+
|
|
1290
|
+
flags = []
|
|
1291
|
+
if self._is_many:
|
|
1292
|
+
flags.append("is_many")
|
|
1293
|
+
if self._is_script:
|
|
1294
|
+
flags.append("is_script")
|
|
1295
|
+
flags_str = f", {', '.join(flags)}" if flags else ""
|
|
1296
|
+
|
|
1297
|
+
return f"SQL({self._raw_sql!r}{params_str}{flags_str})"
|
|
1298
|
+
|
|
1299
|
+
|
|
1300
|
+
@mypyc_attr(allow_interpreted_subclasses=False)
|
|
1301
|
+
class StatementConfig:
|
|
1302
|
+
"""Configuration for SQL statement processing.
|
|
1303
|
+
|
|
1304
|
+
Controls SQL parsing, validation, transformations, parameter handling,
|
|
1305
|
+
and other processing options for SQL statements.
|
|
1306
|
+
"""
|
|
1307
|
+
|
|
1308
|
+
__slots__ = SQL_CONFIG_SLOTS
|
|
1309
|
+
|
|
1310
|
+
def __init__(
|
|
1311
|
+
self,
|
|
1312
|
+
parameter_config: "ParameterStyleConfig | None" = None,
|
|
1313
|
+
enable_parsing: bool = True,
|
|
1314
|
+
enable_validation: bool = True,
|
|
1315
|
+
enable_transformations: bool = True,
|
|
1316
|
+
enable_analysis: bool = False,
|
|
1317
|
+
enable_expression_simplification: bool = False,
|
|
1318
|
+
enable_parameter_type_wrapping: bool = True,
|
|
1319
|
+
enable_caching: bool = True,
|
|
1320
|
+
parameter_converter: "ParameterConverter | None" = None,
|
|
1321
|
+
parameter_validator: "ParameterValidator | None" = None,
|
|
1322
|
+
dialect: "DialectType | None" = None,
|
|
1323
|
+
execution_mode: "str | None" = None,
|
|
1324
|
+
execution_args: "dict[str, Any] | None" = None,
|
|
1325
|
+
output_transformer: "Callable[[str, Any], tuple[str, Any]] | None" = None,
|
|
1326
|
+
statement_transformers: "Sequence[Callable[[exp.Expression, Any], tuple[exp.Expression, Any]]] | None" = None,
|
|
1327
|
+
) -> None:
|
|
1328
|
+
"""Initialize StatementConfig.
|
|
1329
|
+
|
|
1330
|
+
Args:
|
|
1331
|
+
parameter_config: Parameter style configuration
|
|
1332
|
+
enable_parsing: Enable SQL parsing
|
|
1333
|
+
enable_validation: Run SQL validators
|
|
1334
|
+
enable_transformations: Apply SQL transformers
|
|
1335
|
+
enable_analysis: Run SQL analyzers
|
|
1336
|
+
enable_expression_simplification: Apply expression simplification
|
|
1337
|
+
enable_parameter_type_wrapping: Wrap parameters with type information
|
|
1338
|
+
enable_caching: Cache processed SQL statements
|
|
1339
|
+
parameter_converter: Handles parameter style conversions
|
|
1340
|
+
parameter_validator: Validates parameter usage and styles
|
|
1341
|
+
dialect: SQL dialect
|
|
1342
|
+
execution_mode: Special execution mode
|
|
1343
|
+
execution_args: Arguments for special execution modes
|
|
1344
|
+
output_transformer: Optional output transformation function
|
|
1345
|
+
statement_transformers: Optional AST transformers executed during compilation
|
|
1346
|
+
"""
|
|
1347
|
+
self.enable_parsing = enable_parsing
|
|
1348
|
+
self.enable_validation = enable_validation
|
|
1349
|
+
self.enable_transformations = enable_transformations
|
|
1350
|
+
self.enable_analysis = enable_analysis
|
|
1351
|
+
self.enable_expression_simplification = enable_expression_simplification
|
|
1352
|
+
self.enable_parameter_type_wrapping = enable_parameter_type_wrapping
|
|
1353
|
+
self.enable_caching = enable_caching
|
|
1354
|
+
if parameter_converter is None:
|
|
1355
|
+
if parameter_validator is None:
|
|
1356
|
+
parameter_validator = ParameterValidator()
|
|
1357
|
+
self.parameter_converter = ParameterConverter(parameter_validator)
|
|
1358
|
+
else:
|
|
1359
|
+
self.parameter_converter = parameter_converter
|
|
1360
|
+
|
|
1361
|
+
if parameter_validator is None:
|
|
1362
|
+
self.parameter_validator = self.parameter_converter.validator
|
|
1363
|
+
else:
|
|
1364
|
+
self.parameter_validator = parameter_validator
|
|
1365
|
+
self.parameter_converter.validator = parameter_validator
|
|
1366
|
+
self.parameter_config = parameter_config or ParameterStyleConfig(
|
|
1367
|
+
default_parameter_style=ParameterStyle.QMARK, supported_parameter_styles={ParameterStyle.QMARK}
|
|
1368
|
+
)
|
|
1369
|
+
|
|
1370
|
+
self.dialect = dialect
|
|
1371
|
+
self.execution_mode = execution_mode
|
|
1372
|
+
self.execution_args = execution_args
|
|
1373
|
+
self.output_transformer = output_transformer
|
|
1374
|
+
if statement_transformers:
|
|
1375
|
+
self.statement_transformers = tuple(statement_transformers)
|
|
1376
|
+
else:
|
|
1377
|
+
self.statement_transformers = ()
|
|
1378
|
+
|
|
1379
|
+
def replace(self, **kwargs: Any) -> "StatementConfig":
|
|
1380
|
+
"""Immutable update pattern.
|
|
1381
|
+
|
|
1382
|
+
Args:
|
|
1383
|
+
**kwargs: Attributes to update
|
|
1384
|
+
|
|
1385
|
+
Returns:
|
|
1386
|
+
New StatementConfig instance with updated attributes
|
|
1387
|
+
"""
|
|
1388
|
+
for key in kwargs:
|
|
1389
|
+
if key not in SQL_CONFIG_SLOTS:
|
|
1390
|
+
msg = f"{key!r} is not a field in {type(self).__name__}"
|
|
1391
|
+
raise TypeError(msg)
|
|
1392
|
+
|
|
1393
|
+
current_kwargs: dict[str, Any] = {
|
|
1394
|
+
"parameter_config": self.parameter_config,
|
|
1395
|
+
"enable_parsing": self.enable_parsing,
|
|
1396
|
+
"enable_validation": self.enable_validation,
|
|
1397
|
+
"enable_transformations": self.enable_transformations,
|
|
1398
|
+
"enable_analysis": self.enable_analysis,
|
|
1399
|
+
"enable_expression_simplification": self.enable_expression_simplification,
|
|
1400
|
+
"enable_parameter_type_wrapping": self.enable_parameter_type_wrapping,
|
|
1401
|
+
"enable_caching": self.enable_caching,
|
|
1402
|
+
"parameter_converter": self.parameter_converter,
|
|
1403
|
+
"parameter_validator": self.parameter_validator,
|
|
1404
|
+
"dialect": self.dialect,
|
|
1405
|
+
"execution_mode": self.execution_mode,
|
|
1406
|
+
"execution_args": self.execution_args,
|
|
1407
|
+
"output_transformer": self.output_transformer,
|
|
1408
|
+
"statement_transformers": self.statement_transformers,
|
|
1409
|
+
}
|
|
1410
|
+
current_kwargs.update(kwargs)
|
|
1411
|
+
return type(self)(**current_kwargs)
|
|
1412
|
+
|
|
1413
|
+
def __hash__(self) -> int:
|
|
1414
|
+
"""Hash based on configuration settings."""
|
|
1415
|
+
return hash((
|
|
1416
|
+
self.enable_parsing,
|
|
1417
|
+
self.enable_validation,
|
|
1418
|
+
self.enable_transformations,
|
|
1419
|
+
self.enable_analysis,
|
|
1420
|
+
self.enable_expression_simplification,
|
|
1421
|
+
self.enable_parameter_type_wrapping,
|
|
1422
|
+
self.enable_caching,
|
|
1423
|
+
str(self.dialect),
|
|
1424
|
+
self.parameter_config.hash(),
|
|
1425
|
+
self.execution_mode,
|
|
1426
|
+
self.output_transformer,
|
|
1427
|
+
self.statement_transformers,
|
|
1428
|
+
))
|
|
1429
|
+
|
|
1430
|
+
def __repr__(self) -> str:
|
|
1431
|
+
"""String representation of the StatementConfig instance."""
|
|
1432
|
+
field_strs = [
|
|
1433
|
+
f"parameter_config={self.parameter_config!r}",
|
|
1434
|
+
f"enable_parsing={self.enable_parsing!r}",
|
|
1435
|
+
f"enable_validation={self.enable_validation!r}",
|
|
1436
|
+
f"enable_transformations={self.enable_transformations!r}",
|
|
1437
|
+
f"enable_analysis={self.enable_analysis!r}",
|
|
1438
|
+
f"enable_expression_simplification={self.enable_expression_simplification!r}",
|
|
1439
|
+
f"enable_parameter_type_wrapping={self.enable_parameter_type_wrapping!r}",
|
|
1440
|
+
f"enable_caching={self.enable_caching!r}",
|
|
1441
|
+
f"parameter_converter={self.parameter_converter!r}",
|
|
1442
|
+
f"parameter_validator={self.parameter_validator!r}",
|
|
1443
|
+
f"dialect={self.dialect!r}",
|
|
1444
|
+
f"execution_mode={self.execution_mode!r}",
|
|
1445
|
+
f"execution_args={self.execution_args!r}",
|
|
1446
|
+
f"output_transformer={self.output_transformer!r}",
|
|
1447
|
+
f"statement_transformers={self.statement_transformers!r}",
|
|
1448
|
+
]
|
|
1449
|
+
return f"{self.__class__.__name__}({', '.join(field_strs)})"
|
|
1450
|
+
|
|
1451
|
+
def __eq__(self, other: object) -> bool:
|
|
1452
|
+
"""Equality comparison."""
|
|
1453
|
+
if not isinstance(other, type(self)):
|
|
1454
|
+
return False
|
|
1455
|
+
|
|
1456
|
+
if not self._compare_parameter_configs(self.parameter_config, other.parameter_config):
|
|
1457
|
+
return False
|
|
1458
|
+
|
|
1459
|
+
return (
|
|
1460
|
+
self.enable_parsing == other.enable_parsing
|
|
1461
|
+
and self.enable_validation == other.enable_validation
|
|
1462
|
+
and self.enable_transformations == other.enable_transformations
|
|
1463
|
+
and self.enable_analysis == other.enable_analysis
|
|
1464
|
+
and self.enable_expression_simplification == other.enable_expression_simplification
|
|
1465
|
+
and self.enable_parameter_type_wrapping == other.enable_parameter_type_wrapping
|
|
1466
|
+
and self.enable_caching == other.enable_caching
|
|
1467
|
+
and self.dialect == other.dialect
|
|
1468
|
+
and self.execution_mode == other.execution_mode
|
|
1469
|
+
and self.execution_args == other.execution_args
|
|
1470
|
+
and self.output_transformer == other.output_transformer
|
|
1471
|
+
and self.statement_transformers == other.statement_transformers
|
|
1472
|
+
)
|
|
1473
|
+
|
|
1474
|
+
def _compare_parameter_configs(self, config1: Any, config2: Any) -> bool:
|
|
1475
|
+
"""Compare parameter configs."""
|
|
1476
|
+
return bool(
|
|
1477
|
+
config1.default_parameter_style == config2.default_parameter_style
|
|
1478
|
+
and config1.supported_parameter_styles == config2.supported_parameter_styles
|
|
1479
|
+
and config1.supported_execution_parameter_styles == config2.supported_execution_parameter_styles
|
|
1480
|
+
)
|
|
1481
|
+
|
|
1482
|
+
|
|
1483
|
+
def get_default_config() -> StatementConfig:
|
|
1484
|
+
"""Get default statement configuration.
|
|
1485
|
+
|
|
1486
|
+
Returns:
|
|
1487
|
+
StatementConfig with default settings
|
|
1488
|
+
"""
|
|
1489
|
+
return StatementConfig()
|
|
1490
|
+
|
|
1491
|
+
|
|
1492
|
+
def get_default_parameter_config() -> ParameterStyleConfig:
|
|
1493
|
+
"""Get default parameter configuration.
|
|
1494
|
+
|
|
1495
|
+
Returns:
|
|
1496
|
+
ParameterStyleConfig with QMARK style as default
|
|
1497
|
+
"""
|
|
1498
|
+
return ParameterStyleConfig(
|
|
1499
|
+
default_parameter_style=ParameterStyle.QMARK, supported_parameter_styles={ParameterStyle.QMARK}
|
|
1500
|
+
)
|
|
1501
|
+
|
|
1502
|
+
|
|
1503
|
+
Statement: TypeAlias = str | exp.Expression | SQL
|