sqlspec 0.36.0__cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ac8f31065839703b4e70__mypyc.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/__init__.py +140 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_serialization.py +315 -0
- sqlspec/_typing.py +700 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +5 -0
- sqlspec/adapters/adbc/_typing.py +82 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +1273 -0
- sqlspec/adapters/adbc/config.py +295 -0
- sqlspec/adapters/adbc/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/core.py +735 -0
- sqlspec/adapters/adbc/data_dictionary.py +334 -0
- sqlspec/adapters/adbc/driver.py +529 -0
- sqlspec/adapters/adbc/events/__init__.py +5 -0
- sqlspec/adapters/adbc/events/store.py +285 -0
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +502 -0
- sqlspec/adapters/adbc/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/adbc/type_converter.py +140 -0
- sqlspec/adapters/aiosqlite/__init__.py +25 -0
- sqlspec/adapters/aiosqlite/_typing.py +82 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +818 -0
- sqlspec/adapters/aiosqlite/config.py +334 -0
- sqlspec/adapters/aiosqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/aiosqlite/core.py +315 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +208 -0
- sqlspec/adapters/aiosqlite/driver.py +313 -0
- sqlspec/adapters/aiosqlite/events/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/events/store.py +20 -0
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +279 -0
- sqlspec/adapters/aiosqlite/pool.py +533 -0
- sqlspec/adapters/asyncmy/__init__.py +21 -0
- sqlspec/adapters/asyncmy/_typing.py +87 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +703 -0
- sqlspec/adapters/asyncmy/config.py +302 -0
- sqlspec/adapters/asyncmy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncmy/core.py +360 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +124 -0
- sqlspec/adapters/asyncmy/driver.py +383 -0
- sqlspec/adapters/asyncmy/events/__init__.py +5 -0
- sqlspec/adapters/asyncmy/events/store.py +104 -0
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +19 -0
- sqlspec/adapters/asyncpg/_typing.py +88 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +748 -0
- sqlspec/adapters/asyncpg/config.py +569 -0
- sqlspec/adapters/asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/asyncpg/core.py +367 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +162 -0
- sqlspec/adapters/asyncpg/driver.py +487 -0
- sqlspec/adapters/asyncpg/events/__init__.py +6 -0
- sqlspec/adapters/asyncpg/events/backend.py +286 -0
- sqlspec/adapters/asyncpg/events/store.py +40 -0
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +251 -0
- sqlspec/adapters/bigquery/__init__.py +14 -0
- sqlspec/adapters/bigquery/_typing.py +86 -0
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +827 -0
- sqlspec/adapters/bigquery/config.py +353 -0
- sqlspec/adapters/bigquery/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/core.py +715 -0
- sqlspec/adapters/bigquery/data_dictionary.py +128 -0
- sqlspec/adapters/bigquery/driver.py +548 -0
- sqlspec/adapters/bigquery/events/__init__.py +5 -0
- sqlspec/adapters/bigquery/events/store.py +139 -0
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +325 -0
- sqlspec/adapters/bigquery/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/bigquery/type_converter.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/__init__.py +24 -0
- sqlspec/adapters/cockroach_asyncpg/_typing.py +72 -0
- sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/adk/store.py +410 -0
- sqlspec/adapters/cockroach_asyncpg/config.py +238 -0
- sqlspec/adapters/cockroach_asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
- sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/driver.py +144 -0
- sqlspec/adapters/cockroach_asyncpg/events/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/events/store.py +20 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/litestar/store.py +142 -0
- sqlspec/adapters/cockroach_psycopg/__init__.py +38 -0
- sqlspec/adapters/cockroach_psycopg/_typing.py +129 -0
- sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
- sqlspec/adapters/cockroach_psycopg/adk/store.py +868 -0
- sqlspec/adapters/cockroach_psycopg/config.py +484 -0
- sqlspec/adapters/cockroach_psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/cockroach_psycopg/core.py +63 -0
- sqlspec/adapters/cockroach_psycopg/data_dictionary.py +215 -0
- sqlspec/adapters/cockroach_psycopg/driver.py +284 -0
- sqlspec/adapters/cockroach_psycopg/events/__init__.py +6 -0
- sqlspec/adapters/cockroach_psycopg/events/store.py +34 -0
- sqlspec/adapters/cockroach_psycopg/litestar/__init__.py +3 -0
- sqlspec/adapters/cockroach_psycopg/litestar/store.py +325 -0
- sqlspec/adapters/duckdb/__init__.py +25 -0
- sqlspec/adapters/duckdb/_typing.py +81 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +850 -0
- sqlspec/adapters/duckdb/config.py +463 -0
- sqlspec/adapters/duckdb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/core.py +257 -0
- sqlspec/adapters/duckdb/data_dictionary.py +140 -0
- sqlspec/adapters/duckdb/driver.py +430 -0
- sqlspec/adapters/duckdb/events/__init__.py +5 -0
- sqlspec/adapters/duckdb/events/store.py +57 -0
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +330 -0
- sqlspec/adapters/duckdb/pool.py +293 -0
- sqlspec/adapters/duckdb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/duckdb/type_converter.py +118 -0
- sqlspec/adapters/mock/__init__.py +72 -0
- sqlspec/adapters/mock/_typing.py +147 -0
- sqlspec/adapters/mock/config.py +483 -0
- sqlspec/adapters/mock/core.py +319 -0
- sqlspec/adapters/mock/data_dictionary.py +366 -0
- sqlspec/adapters/mock/driver.py +721 -0
- sqlspec/adapters/mysqlconnector/__init__.py +36 -0
- sqlspec/adapters/mysqlconnector/_typing.py +141 -0
- sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
- sqlspec/adapters/mysqlconnector/adk/store.py +1060 -0
- sqlspec/adapters/mysqlconnector/config.py +394 -0
- sqlspec/adapters/mysqlconnector/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/mysqlconnector/core.py +303 -0
- sqlspec/adapters/mysqlconnector/data_dictionary.py +235 -0
- sqlspec/adapters/mysqlconnector/driver.py +483 -0
- sqlspec/adapters/mysqlconnector/events/__init__.py +8 -0
- sqlspec/adapters/mysqlconnector/events/store.py +98 -0
- sqlspec/adapters/mysqlconnector/litestar/__init__.py +5 -0
- sqlspec/adapters/mysqlconnector/litestar/store.py +426 -0
- sqlspec/adapters/oracledb/__init__.py +60 -0
- sqlspec/adapters/oracledb/_numpy_handlers.py +141 -0
- sqlspec/adapters/oracledb/_typing.py +182 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +166 -0
- sqlspec/adapters/oracledb/adk/__init__.py +10 -0
- sqlspec/adapters/oracledb/adk/store.py +2369 -0
- sqlspec/adapters/oracledb/config.py +550 -0
- sqlspec/adapters/oracledb/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/core.py +543 -0
- sqlspec/adapters/oracledb/data_dictionary.py +536 -0
- sqlspec/adapters/oracledb/driver.py +1229 -0
- sqlspec/adapters/oracledb/events/__init__.py +16 -0
- sqlspec/adapters/oracledb/events/backend.py +347 -0
- sqlspec/adapters/oracledb/events/store.py +420 -0
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +781 -0
- sqlspec/adapters/oracledb/migrations.py +535 -0
- sqlspec/adapters/oracledb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/oracledb/type_converter.py +211 -0
- sqlspec/adapters/psqlpy/__init__.py +17 -0
- sqlspec/adapters/psqlpy/_typing.py +79 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +766 -0
- sqlspec/adapters/psqlpy/config.py +304 -0
- sqlspec/adapters/psqlpy/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/core.py +480 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +126 -0
- sqlspec/adapters/psqlpy/driver.py +438 -0
- sqlspec/adapters/psqlpy/events/__init__.py +6 -0
- sqlspec/adapters/psqlpy/events/backend.py +310 -0
- sqlspec/adapters/psqlpy/events/store.py +20 -0
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +270 -0
- sqlspec/adapters/psqlpy/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psqlpy/type_converter.py +113 -0
- sqlspec/adapters/psycopg/__init__.py +32 -0
- sqlspec/adapters/psycopg/_typing.py +164 -0
- sqlspec/adapters/psycopg/adk/__init__.py +10 -0
- sqlspec/adapters/psycopg/adk/store.py +1387 -0
- sqlspec/adapters/psycopg/config.py +576 -0
- sqlspec/adapters/psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/core.py +450 -0
- sqlspec/adapters/psycopg/data_dictionary.py +289 -0
- sqlspec/adapters/psycopg/driver.py +975 -0
- sqlspec/adapters/psycopg/events/__init__.py +20 -0
- sqlspec/adapters/psycopg/events/backend.py +458 -0
- sqlspec/adapters/psycopg/events/store.py +42 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +552 -0
- sqlspec/adapters/psycopg/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/psycopg/type_converter.py +93 -0
- sqlspec/adapters/pymysql/__init__.py +21 -0
- sqlspec/adapters/pymysql/_typing.py +71 -0
- sqlspec/adapters/pymysql/adk/__init__.py +5 -0
- sqlspec/adapters/pymysql/adk/store.py +540 -0
- sqlspec/adapters/pymysql/config.py +195 -0
- sqlspec/adapters/pymysql/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/pymysql/core.py +299 -0
- sqlspec/adapters/pymysql/data_dictionary.py +122 -0
- sqlspec/adapters/pymysql/driver.py +259 -0
- sqlspec/adapters/pymysql/events/__init__.py +5 -0
- sqlspec/adapters/pymysql/events/store.py +50 -0
- sqlspec/adapters/pymysql/litestar/__init__.py +5 -0
- sqlspec/adapters/pymysql/litestar/store.py +232 -0
- sqlspec/adapters/pymysql/pool.py +137 -0
- sqlspec/adapters/spanner/__init__.py +40 -0
- sqlspec/adapters/spanner/_typing.py +86 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +732 -0
- sqlspec/adapters/spanner/config.py +352 -0
- sqlspec/adapters/spanner/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/core.py +188 -0
- sqlspec/adapters/spanner/data_dictionary.py +120 -0
- sqlspec/adapters/spanner/dialect/__init__.py +6 -0
- sqlspec/adapters/spanner/dialect/_spangres.py +57 -0
- sqlspec/adapters/spanner/dialect/_spanner.py +130 -0
- sqlspec/adapters/spanner/driver.py +373 -0
- sqlspec/adapters/spanner/events/__init__.py +5 -0
- sqlspec/adapters/spanner/events/store.py +187 -0
- sqlspec/adapters/spanner/litestar/__init__.py +5 -0
- sqlspec/adapters/spanner/litestar/store.py +291 -0
- sqlspec/adapters/spanner/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/spanner/type_converter.py +331 -0
- sqlspec/adapters/sqlite/__init__.py +19 -0
- sqlspec/adapters/sqlite/_typing.py +80 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +958 -0
- sqlspec/adapters/sqlite/config.py +280 -0
- sqlspec/adapters/sqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/core.py +312 -0
- sqlspec/adapters/sqlite/data_dictionary.py +202 -0
- sqlspec/adapters/sqlite/driver.py +359 -0
- sqlspec/adapters/sqlite/events/__init__.py +5 -0
- sqlspec/adapters/sqlite/events/store.py +20 -0
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +316 -0
- sqlspec/adapters/sqlite/pool.py +198 -0
- sqlspec/adapters/sqlite/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/adapters/sqlite/type_converter.py +114 -0
- sqlspec/base.py +747 -0
- sqlspec/builder/__init__.py +179 -0
- sqlspec/builder/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_base.py +1022 -0
- sqlspec/builder/_column.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_column.py +521 -0
- sqlspec/builder/_ddl.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_ddl.py +1642 -0
- sqlspec/builder/_delete.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_delete.py +95 -0
- sqlspec/builder/_dml.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_dml.py +365 -0
- sqlspec/builder/_explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_explain.py +579 -0
- sqlspec/builder/_expression_wrappers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_factory.py +1697 -0
- sqlspec/builder/_insert.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_insert.py +328 -0
- sqlspec/builder/_join.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_join.py +499 -0
- sqlspec/builder/_merge.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_merge.py +821 -0
- sqlspec/builder/_parsing_utils.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_parsing_utils.py +297 -0
- sqlspec/builder/_select.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_select.py +1660 -0
- sqlspec/builder/_temporal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_temporal.py +139 -0
- sqlspec/builder/_update.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/builder/_update.py +173 -0
- sqlspec/builder/_vector_expressions.py +267 -0
- sqlspec/cli.py +911 -0
- sqlspec/config.py +1755 -0
- sqlspec/core/__init__.py +374 -0
- sqlspec/core/_correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/_correlation.py +176 -0
- sqlspec/core/cache.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/cache.py +1069 -0
- sqlspec/core/compiler.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/compiler.py +954 -0
- sqlspec/core/explain.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/explain.py +275 -0
- sqlspec/core/filters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/filters.py +952 -0
- sqlspec/core/hashing.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/hashing.py +262 -0
- sqlspec/core/metrics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +71 -0
- sqlspec/core/parameters/_alignment.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_alignment.py +270 -0
- sqlspec/core/parameters/_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_converter.py +543 -0
- sqlspec/core/parameters/_processor.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_processor.py +505 -0
- sqlspec/core/parameters/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_registry.py +206 -0
- sqlspec/core/parameters/_transformers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_transformers.py +292 -0
- sqlspec/core/parameters/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_types.py +499 -0
- sqlspec/core/parameters/_validator.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/parameters/_validator.py +180 -0
- sqlspec/core/pipeline.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/pipeline.py +319 -0
- sqlspec/core/query_modifiers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/query_modifiers.py +437 -0
- sqlspec/core/result/__init__.py +23 -0
- sqlspec/core/result/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_base.py +1121 -0
- sqlspec/core/result/_io.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/result/_io.py +28 -0
- sqlspec/core/splitter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/splitter.py +966 -0
- sqlspec/core/stack.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/statement.py +1503 -0
- sqlspec/core/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/core/type_converter.py +339 -0
- sqlspec/data_dictionary/__init__.py +22 -0
- sqlspec/data_dictionary/_loader.py +123 -0
- sqlspec/data_dictionary/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_registry.py +74 -0
- sqlspec/data_dictionary/_types.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/_types.py +121 -0
- sqlspec/data_dictionary/dialects/__init__.py +21 -0
- sqlspec/data_dictionary/dialects/bigquery.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/bigquery.py +49 -0
- sqlspec/data_dictionary/dialects/cockroachdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/cockroachdb.py +43 -0
- sqlspec/data_dictionary/dialects/duckdb.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/duckdb.py +47 -0
- sqlspec/data_dictionary/dialects/mysql.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/mysql.py +42 -0
- sqlspec/data_dictionary/dialects/oracle.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/oracle.py +34 -0
- sqlspec/data_dictionary/dialects/postgres.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/postgres.py +46 -0
- sqlspec/data_dictionary/dialects/spanner.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/spanner.py +37 -0
- sqlspec/data_dictionary/dialects/sqlite.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/data_dictionary/dialects/sqlite.py +42 -0
- sqlspec/data_dictionary/sql/.gitkeep +0 -0
- sqlspec/data_dictionary/sql/bigquery/columns.sql +23 -0
- sqlspec/data_dictionary/sql/bigquery/foreign_keys.sql +34 -0
- sqlspec/data_dictionary/sql/bigquery/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/bigquery/tables.sql +33 -0
- sqlspec/data_dictionary/sql/bigquery/version.sql +3 -0
- sqlspec/data_dictionary/sql/cockroachdb/columns.sql +34 -0
- sqlspec/data_dictionary/sql/cockroachdb/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/cockroachdb/indexes.sql +32 -0
- sqlspec/data_dictionary/sql/cockroachdb/tables.sql +44 -0
- sqlspec/data_dictionary/sql/cockroachdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/duckdb/columns.sql +23 -0
- sqlspec/data_dictionary/sql/duckdb/foreign_keys.sql +36 -0
- sqlspec/data_dictionary/sql/duckdb/indexes.sql +19 -0
- sqlspec/data_dictionary/sql/duckdb/tables.sql +38 -0
- sqlspec/data_dictionary/sql/duckdb/version.sql +3 -0
- sqlspec/data_dictionary/sql/mysql/columns.sql +23 -0
- sqlspec/data_dictionary/sql/mysql/foreign_keys.sql +28 -0
- sqlspec/data_dictionary/sql/mysql/indexes.sql +26 -0
- sqlspec/data_dictionary/sql/mysql/tables.sql +33 -0
- sqlspec/data_dictionary/sql/mysql/version.sql +3 -0
- sqlspec/data_dictionary/sql/oracle/columns.sql +23 -0
- sqlspec/data_dictionary/sql/oracle/foreign_keys.sql +48 -0
- sqlspec/data_dictionary/sql/oracle/indexes.sql +44 -0
- sqlspec/data_dictionary/sql/oracle/tables.sql +25 -0
- sqlspec/data_dictionary/sql/oracle/version.sql +20 -0
- sqlspec/data_dictionary/sql/postgres/columns.sql +34 -0
- sqlspec/data_dictionary/sql/postgres/foreign_keys.sql +40 -0
- sqlspec/data_dictionary/sql/postgres/indexes.sql +56 -0
- sqlspec/data_dictionary/sql/postgres/tables.sql +44 -0
- sqlspec/data_dictionary/sql/postgres/version.sql +3 -0
- sqlspec/data_dictionary/sql/spanner/columns.sql +23 -0
- sqlspec/data_dictionary/sql/spanner/foreign_keys.sql +70 -0
- sqlspec/data_dictionary/sql/spanner/indexes.sql +30 -0
- sqlspec/data_dictionary/sql/spanner/tables.sql +9 -0
- sqlspec/data_dictionary/sql/spanner/version.sql +3 -0
- sqlspec/data_dictionary/sql/sqlite/columns.sql +23 -0
- sqlspec/data_dictionary/sql/sqlite/foreign_keys.sql +22 -0
- sqlspec/data_dictionary/sql/sqlite/indexes.sql +7 -0
- sqlspec/data_dictionary/sql/sqlite/tables.sql +28 -0
- sqlspec/data_dictionary/sql/sqlite/version.sql +3 -0
- sqlspec/driver/__init__.py +32 -0
- sqlspec/driver/_async.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_async.py +1737 -0
- sqlspec/driver/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_common.py +1478 -0
- sqlspec/driver/_sql_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sql_helpers.py +148 -0
- sqlspec/driver/_storage_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_storage_helpers.py +144 -0
- sqlspec/driver/_sync.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/driver/_sync.py +1710 -0
- sqlspec/exceptions.py +338 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +70 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/memory/__init__.py +69 -0
- sqlspec/extensions/adk/memory/_types.py +30 -0
- sqlspec/extensions/adk/memory/converters.py +149 -0
- sqlspec/extensions/adk/memory/service.py +217 -0
- sqlspec/extensions/adk/memory/store.py +569 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +246 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +225 -0
- sqlspec/extensions/adk/store.py +567 -0
- sqlspec/extensions/events/__init__.py +51 -0
- sqlspec/extensions/events/_channel.py +703 -0
- sqlspec/extensions/events/_hints.py +45 -0
- sqlspec/extensions/events/_models.py +23 -0
- sqlspec/extensions/events/_payload.py +69 -0
- sqlspec/extensions/events/_protocols.py +134 -0
- sqlspec/extensions/events/_queue.py +461 -0
- sqlspec/extensions/events/_store.py +209 -0
- sqlspec/extensions/events/migrations/0001_create_event_queue.py +59 -0
- sqlspec/extensions/events/migrations/__init__.py +3 -0
- sqlspec/extensions/fastapi/__init__.py +19 -0
- sqlspec/extensions/fastapi/extension.py +351 -0
- sqlspec/extensions/fastapi/providers.py +607 -0
- sqlspec/extensions/flask/__init__.py +37 -0
- sqlspec/extensions/flask/_state.py +76 -0
- sqlspec/extensions/flask/_utils.py +71 -0
- sqlspec/extensions/flask/extension.py +519 -0
- sqlspec/extensions/litestar/__init__.py +28 -0
- sqlspec/extensions/litestar/_utils.py +52 -0
- sqlspec/extensions/litestar/channels.py +165 -0
- sqlspec/extensions/litestar/cli.py +102 -0
- sqlspec/extensions/litestar/config.py +90 -0
- sqlspec/extensions/litestar/handlers.py +316 -0
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +671 -0
- sqlspec/extensions/litestar/providers.py +526 -0
- sqlspec/extensions/litestar/store.py +296 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +113 -0
- sqlspec/extensions/starlette/__init__.py +19 -0
- sqlspec/extensions/starlette/_state.py +30 -0
- sqlspec/extensions/starlette/_utils.py +96 -0
- sqlspec/extensions/starlette/extension.py +346 -0
- sqlspec/extensions/starlette/middleware.py +235 -0
- sqlspec/loader.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/loader.py +702 -0
- sqlspec/migrations/__init__.py +36 -0
- sqlspec/migrations/base.py +731 -0
- sqlspec/migrations/commands.py +1232 -0
- sqlspec/migrations/context.py +157 -0
- sqlspec/migrations/fix.py +204 -0
- sqlspec/migrations/loaders.py +443 -0
- sqlspec/migrations/runner.py +1172 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.py +611 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.py +207 -0
- sqlspec/migrations/version.py +446 -0
- sqlspec/observability/__init__.py +55 -0
- sqlspec/observability/_common.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_common.py +77 -0
- sqlspec/observability/_config.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_config.py +348 -0
- sqlspec/observability/_diagnostics.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_diagnostics.py +74 -0
- sqlspec/observability/_dispatcher.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_dispatcher.py +152 -0
- sqlspec/observability/_formatters/__init__.py +13 -0
- sqlspec/observability/_formatters/_aws.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_aws.py +102 -0
- sqlspec/observability/_formatters/_azure.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_azure.py +96 -0
- sqlspec/observability/_formatters/_base.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_base.py +57 -0
- sqlspec/observability/_formatters/_gcp.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_formatters/_gcp.py +131 -0
- sqlspec/observability/_formatting.py +58 -0
- sqlspec/observability/_observer.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_observer.py +357 -0
- sqlspec/observability/_runtime.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_runtime.py +420 -0
- sqlspec/observability/_sampling.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_sampling.py +188 -0
- sqlspec/observability/_spans.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/observability/_spans.py +161 -0
- sqlspec/protocols.py +916 -0
- sqlspec/py.typed +0 -0
- sqlspec/storage/__init__.py +48 -0
- sqlspec/storage/_utils.py +104 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.py +253 -0
- sqlspec/storage/backends/fsspec.py +529 -0
- sqlspec/storage/backends/local.py +441 -0
- sqlspec/storage/backends/obstore.py +916 -0
- sqlspec/storage/errors.py +104 -0
- sqlspec/storage/pipeline.py +582 -0
- sqlspec/storage/registry.py +301 -0
- sqlspec/typing.py +395 -0
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/arrow_helpers.py +318 -0
- sqlspec/utils/config_tools.py +332 -0
- sqlspec/utils/correlation.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/correlation.py +134 -0
- sqlspec/utils/deprecation.py +190 -0
- sqlspec/utils/fixtures.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/fixtures.py +258 -0
- sqlspec/utils/logging.py +222 -0
- sqlspec/utils/module_loader.py +306 -0
- sqlspec/utils/portal.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/portal.py +375 -0
- sqlspec/utils/schema.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/schema.py +485 -0
- sqlspec/utils/serializers.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/serializers.py +408 -0
- sqlspec/utils/singleton.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/sync_tools.py +311 -0
- sqlspec/utils/text.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/text.py +108 -0
- sqlspec/utils/type_converters.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_converters.py +128 -0
- sqlspec/utils/type_guards.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/type_guards.py +1360 -0
- sqlspec/utils/uuids.cpython-310-aarch64-linux-gnu.so +0 -0
- sqlspec/utils/uuids.py +225 -0
- sqlspec-0.36.0.dist-info/METADATA +205 -0
- sqlspec-0.36.0.dist-info/RECORD +531 -0
- sqlspec-0.36.0.dist-info/WHEEL +7 -0
- sqlspec-0.36.0.dist-info/entry_points.txt +2 -0
- sqlspec-0.36.0.dist-info/licenses/LICENSE +21 -0
sqlspec/builder/_base.py
ADDED
|
@@ -0,0 +1,1022 @@
|
|
|
1
|
+
"""Base query builder with validation and parameter binding.
|
|
2
|
+
|
|
3
|
+
Provides abstract base classes and core functionality for SQL query builders.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import hashlib
|
|
7
|
+
import re
|
|
8
|
+
import uuid
|
|
9
|
+
from abc import ABC, abstractmethod
|
|
10
|
+
from collections.abc import Callable, Iterable, Mapping
|
|
11
|
+
from typing import Any, NoReturn, cast
|
|
12
|
+
|
|
13
|
+
import sqlglot
|
|
14
|
+
from sqlglot import Dialect, exp
|
|
15
|
+
from sqlglot.dialects.dialect import DialectType
|
|
16
|
+
from sqlglot.errors import ParseError as SQLGlotParseError
|
|
17
|
+
from sqlglot.optimizer import optimize
|
|
18
|
+
from typing_extensions import Self
|
|
19
|
+
|
|
20
|
+
from sqlspec.builder._vector_expressions import VectorDistance
|
|
21
|
+
from sqlspec.core import (
|
|
22
|
+
SQL,
|
|
23
|
+
ParameterStyle,
|
|
24
|
+
ParameterStyleConfig,
|
|
25
|
+
SQLResult,
|
|
26
|
+
StatementConfig,
|
|
27
|
+
get_cache,
|
|
28
|
+
get_cache_config,
|
|
29
|
+
hash_optimized_expression,
|
|
30
|
+
)
|
|
31
|
+
from sqlspec.exceptions import SQLBuilderError
|
|
32
|
+
from sqlspec.utils.logging import get_logger
|
|
33
|
+
from sqlspec.utils.type_guards import has_expression_and_parameters, has_name, has_with_method, is_expression
|
|
34
|
+
|
|
35
|
+
__all__ = ("BuiltQuery", "ExpressionBuilder", "QueryBuilder")
|
|
36
|
+
|
|
37
|
+
MAX_PARAMETER_COLLISION_ATTEMPTS = 1000
|
|
38
|
+
PARAMETER_INDEX_PATTERN = re.compile(r"^param_(?P<index>\d+)$")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class _ExpressionParameterizer:
|
|
42
|
+
__slots__ = ("_builder",)
|
|
43
|
+
|
|
44
|
+
def __init__(self, builder: "QueryBuilder") -> None:
|
|
45
|
+
self._builder = builder
|
|
46
|
+
|
|
47
|
+
def __call__(self, node: exp.Expression) -> exp.Expression:
|
|
48
|
+
if isinstance(node, exp.Literal):
|
|
49
|
+
if node.this in {True, False, None}:
|
|
50
|
+
return node
|
|
51
|
+
|
|
52
|
+
parent = node.parent
|
|
53
|
+
if isinstance(parent, exp.Array) and node.find_ancestor(VectorDistance) is not None:
|
|
54
|
+
return node
|
|
55
|
+
|
|
56
|
+
value = node.this
|
|
57
|
+
if node.is_number and isinstance(node.this, str):
|
|
58
|
+
try:
|
|
59
|
+
value = float(node.this) if "." in node.this or "e" in node.this.lower() else int(node.this)
|
|
60
|
+
except ValueError:
|
|
61
|
+
value = node.this
|
|
62
|
+
|
|
63
|
+
param_name = self._builder.add_parameter_for_expression(value, context="where")
|
|
64
|
+
return exp.Placeholder(this=param_name)
|
|
65
|
+
return node
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class _PlaceholderReplacer:
|
|
69
|
+
__slots__ = ("_param_mapping",)
|
|
70
|
+
|
|
71
|
+
def __init__(self, param_mapping: dict[str, str]) -> None:
|
|
72
|
+
self._param_mapping = param_mapping
|
|
73
|
+
|
|
74
|
+
def __call__(self, node: exp.Expression) -> exp.Expression:
|
|
75
|
+
if isinstance(node, exp.Placeholder) and str(node.this) in self._param_mapping:
|
|
76
|
+
return exp.Placeholder(this=self._param_mapping[str(node.this)])
|
|
77
|
+
return node
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _unquote_identifier(node: exp.Expression) -> exp.Expression:
|
|
81
|
+
if isinstance(node, exp.Identifier):
|
|
82
|
+
node.set("quoted", False)
|
|
83
|
+
return node
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
logger = get_logger(__name__)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class BuiltQuery:
|
|
90
|
+
"""SQL query with bound parameters."""
|
|
91
|
+
|
|
92
|
+
__slots__ = ("dialect", "parameters", "sql")
|
|
93
|
+
|
|
94
|
+
def __init__(self, sql: str, parameters: dict[str, Any] | None = None, dialect: DialectType | None = None) -> None:
|
|
95
|
+
self.sql = sql
|
|
96
|
+
self.parameters = parameters if parameters is not None else {}
|
|
97
|
+
self.dialect = dialect
|
|
98
|
+
|
|
99
|
+
def __repr__(self) -> str:
|
|
100
|
+
parameter_keys = sorted(self.parameters.keys())
|
|
101
|
+
return f"BuiltQuery(sql={self.sql!r}, parameters={parameter_keys!r}, dialect={self.dialect!r})"
|
|
102
|
+
|
|
103
|
+
def __eq__(self, other: object) -> bool:
|
|
104
|
+
if not isinstance(other, BuiltQuery):
|
|
105
|
+
return NotImplemented
|
|
106
|
+
return self.sql == other.sql and self.parameters == other.parameters and self.dialect == other.dialect
|
|
107
|
+
|
|
108
|
+
def __hash__(self) -> int:
|
|
109
|
+
return hash((self.sql, frozenset(self.parameters.items()), self.dialect))
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
class QueryBuilder(ABC):
|
|
113
|
+
"""Abstract base class for SQL query builders.
|
|
114
|
+
|
|
115
|
+
Provides common functionality for dialect handling, parameter management,
|
|
116
|
+
and query construction using SQLGlot.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
__slots__ = (
|
|
120
|
+
"_expression",
|
|
121
|
+
"_lock_targets_quoted",
|
|
122
|
+
"_merge_target_quoted",
|
|
123
|
+
"_parameter_counter",
|
|
124
|
+
"_parameter_name_counters",
|
|
125
|
+
"_parameters",
|
|
126
|
+
"_with_ctes",
|
|
127
|
+
"dialect",
|
|
128
|
+
"enable_optimization",
|
|
129
|
+
"optimize_joins",
|
|
130
|
+
"optimize_predicates",
|
|
131
|
+
"schema",
|
|
132
|
+
"simplify_expressions",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
def __init__(
|
|
136
|
+
self,
|
|
137
|
+
dialect: DialectType | None = None,
|
|
138
|
+
schema: dict[str, dict[str, str]] | None = None,
|
|
139
|
+
enable_optimization: bool = True,
|
|
140
|
+
optimize_joins: bool = True,
|
|
141
|
+
optimize_predicates: bool = True,
|
|
142
|
+
simplify_expressions: bool = True,
|
|
143
|
+
) -> None:
|
|
144
|
+
self.dialect = dialect
|
|
145
|
+
self.schema = schema
|
|
146
|
+
self.enable_optimization = enable_optimization
|
|
147
|
+
self.optimize_joins = optimize_joins
|
|
148
|
+
self.optimize_predicates = optimize_predicates
|
|
149
|
+
self.simplify_expressions = simplify_expressions
|
|
150
|
+
|
|
151
|
+
self._expression: exp.Expression | None = None
|
|
152
|
+
self._parameter_name_counters: dict[str, int] = {}
|
|
153
|
+
self._parameters: dict[str, Any] = {}
|
|
154
|
+
self._parameter_counter: int = 0
|
|
155
|
+
self._with_ctes: dict[str, exp.CTE] = {}
|
|
156
|
+
self._lock_targets_quoted = False
|
|
157
|
+
self._merge_target_quoted = False
|
|
158
|
+
|
|
159
|
+
@classmethod
|
|
160
|
+
def _parse_query_builder_kwargs(
|
|
161
|
+
cls, kwargs: "dict[str, Any]"
|
|
162
|
+
) -> "tuple[DialectType | None, dict[str, dict[str, str]] | None, bool, bool, bool, bool]":
|
|
163
|
+
dialect = kwargs.pop("dialect", None)
|
|
164
|
+
schema = kwargs.pop("schema", None)
|
|
165
|
+
enable_optimization = kwargs.pop("enable_optimization", True)
|
|
166
|
+
optimize_joins = kwargs.pop("optimize_joins", True)
|
|
167
|
+
optimize_predicates = kwargs.pop("optimize_predicates", True)
|
|
168
|
+
simplify_expressions = kwargs.pop("simplify_expressions", True)
|
|
169
|
+
|
|
170
|
+
if kwargs:
|
|
171
|
+
unknown = ", ".join(sorted(kwargs.keys()))
|
|
172
|
+
cls._raise_sql_builder_error(f"Unexpected QueryBuilder arguments: {unknown}")
|
|
173
|
+
|
|
174
|
+
return (dialect, schema, enable_optimization, optimize_joins, optimize_predicates, simplify_expressions)
|
|
175
|
+
|
|
176
|
+
def _initialize_expression(self) -> None:
|
|
177
|
+
"""Initialize the base expression. Called after __init__."""
|
|
178
|
+
self._expression = self._create_base_expression()
|
|
179
|
+
if not self._expression:
|
|
180
|
+
self._raise_sql_builder_error(
|
|
181
|
+
"QueryBuilder._create_base_expression must return a valid sqlglot expression."
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
def get_expression(self) -> exp.Expression | None:
|
|
185
|
+
"""Get expression reference (no copy).
|
|
186
|
+
|
|
187
|
+
Returns:
|
|
188
|
+
The current SQLGlot expression or None if not set
|
|
189
|
+
"""
|
|
190
|
+
return self._expression
|
|
191
|
+
|
|
192
|
+
def set_expression(self, expression: exp.Expression) -> None:
|
|
193
|
+
"""Set expression with validation.
|
|
194
|
+
|
|
195
|
+
Args:
|
|
196
|
+
expression: SQLGlot expression to set
|
|
197
|
+
"""
|
|
198
|
+
if not is_expression(expression):
|
|
199
|
+
self._raise_invalid_expression_type(expression)
|
|
200
|
+
self._expression = expression
|
|
201
|
+
|
|
202
|
+
def has_expression(self) -> bool:
|
|
203
|
+
"""Check if expression exists.
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
True if expression is set, False otherwise
|
|
207
|
+
"""
|
|
208
|
+
return self._expression is not None
|
|
209
|
+
|
|
210
|
+
@abstractmethod
|
|
211
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
212
|
+
"""Create the base sqlglot expression for the specific query type.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
A new sqlglot expression appropriate for the query type.
|
|
216
|
+
"""
|
|
217
|
+
|
|
218
|
+
@property
|
|
219
|
+
@abstractmethod
|
|
220
|
+
def _expected_result_type(self) -> "type[SQLResult]":
|
|
221
|
+
"""The expected result type for the query being built.
|
|
222
|
+
|
|
223
|
+
Returns:
|
|
224
|
+
type[ResultT]: The type of the result.
|
|
225
|
+
"""
|
|
226
|
+
|
|
227
|
+
@staticmethod
|
|
228
|
+
def _raise_sql_builder_error(message: str, cause: BaseException | None = None) -> NoReturn:
|
|
229
|
+
"""Helper to raise SQLBuilderError, potentially with a cause.
|
|
230
|
+
|
|
231
|
+
Args:
|
|
232
|
+
message: The error message.
|
|
233
|
+
cause: The optional original exception to chain.
|
|
234
|
+
|
|
235
|
+
Raises:
|
|
236
|
+
SQLBuilderError: Always raises this exception.
|
|
237
|
+
"""
|
|
238
|
+
raise SQLBuilderError(message) from cause
|
|
239
|
+
|
|
240
|
+
@staticmethod
|
|
241
|
+
def _raise_invalid_expression_type(expression: Any) -> NoReturn:
|
|
242
|
+
"""Raise error for invalid expression type.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
expression: The invalid expression object
|
|
246
|
+
|
|
247
|
+
Raises:
|
|
248
|
+
TypeError: Always raised for type mismatch
|
|
249
|
+
"""
|
|
250
|
+
msg = f"Expected Expression, got {type(expression)}"
|
|
251
|
+
raise TypeError(msg)
|
|
252
|
+
|
|
253
|
+
@staticmethod
|
|
254
|
+
def _raise_cte_query_error(alias: str, message: str) -> NoReturn:
|
|
255
|
+
"""Raise error for CTE query issues.
|
|
256
|
+
|
|
257
|
+
Args:
|
|
258
|
+
alias: CTE alias name
|
|
259
|
+
message: Specific error message
|
|
260
|
+
|
|
261
|
+
Raises:
|
|
262
|
+
SQLBuilderError: Always raised for CTE errors
|
|
263
|
+
"""
|
|
264
|
+
msg = f"CTE '{alias}': {message}"
|
|
265
|
+
raise SQLBuilderError(msg)
|
|
266
|
+
|
|
267
|
+
@staticmethod
|
|
268
|
+
def _raise_cte_parse_error(cause: BaseException) -> NoReturn:
|
|
269
|
+
"""Raise error for CTE parsing failures.
|
|
270
|
+
|
|
271
|
+
Args:
|
|
272
|
+
cause: The original parsing exception
|
|
273
|
+
|
|
274
|
+
Raises:
|
|
275
|
+
SQLBuilderError: Always raised with chained cause
|
|
276
|
+
"""
|
|
277
|
+
msg = f"Failed to parse CTE query: {cause!s}"
|
|
278
|
+
raise SQLBuilderError(msg) from cause
|
|
279
|
+
|
|
280
|
+
def _build_final_expression(self, *, copy: bool = False) -> exp.Expression:
|
|
281
|
+
"""Construct the current expression with attached CTEs.
|
|
282
|
+
|
|
283
|
+
Args:
|
|
284
|
+
copy: Whether to copy the underlying expression tree before
|
|
285
|
+
applying transformations.
|
|
286
|
+
|
|
287
|
+
Returns:
|
|
288
|
+
Expression representing the current builder state with CTEs applied.
|
|
289
|
+
"""
|
|
290
|
+
if self._expression is None:
|
|
291
|
+
self._raise_sql_builder_error("QueryBuilder expression not initialized.")
|
|
292
|
+
|
|
293
|
+
base_expression = self._expression.copy() if copy or self._with_ctes else self._expression
|
|
294
|
+
|
|
295
|
+
if not self._with_ctes:
|
|
296
|
+
return base_expression
|
|
297
|
+
|
|
298
|
+
final_expression: exp.Expression = base_expression
|
|
299
|
+
if has_with_method(final_expression):
|
|
300
|
+
for alias, cte_node in self._with_ctes.items():
|
|
301
|
+
final_expression = cast("Any", final_expression).with_(cte_node.args["this"], as_=alias, copy=False)
|
|
302
|
+
return cast("exp.Expression", final_expression)
|
|
303
|
+
|
|
304
|
+
if isinstance(final_expression, (exp.Select, exp.Insert, exp.Update, exp.Delete, exp.Union)):
|
|
305
|
+
return exp.With(expressions=list(self._with_ctes.values()), this=final_expression)
|
|
306
|
+
|
|
307
|
+
return final_expression
|
|
308
|
+
|
|
309
|
+
def _spawn_like_self(self: Self) -> Self:
|
|
310
|
+
"""Create a new builder instance with matching configuration."""
|
|
311
|
+
return type(self)(
|
|
312
|
+
dialect=self.dialect,
|
|
313
|
+
schema=self.schema,
|
|
314
|
+
enable_optimization=self.enable_optimization,
|
|
315
|
+
optimize_joins=self.optimize_joins,
|
|
316
|
+
optimize_predicates=self.optimize_predicates,
|
|
317
|
+
simplify_expressions=self.simplify_expressions,
|
|
318
|
+
)
|
|
319
|
+
|
|
320
|
+
def _resolve_cte_query(self, alias: str, query: "QueryBuilder | exp.Select | str") -> exp.Select:
|
|
321
|
+
"""Resolve a CTE query into a Select expression with merged parameters."""
|
|
322
|
+
if isinstance(query, QueryBuilder):
|
|
323
|
+
query_expr = query.get_expression()
|
|
324
|
+
if query_expr is None:
|
|
325
|
+
self._raise_cte_query_error(alias, "query builder has no expression")
|
|
326
|
+
if not isinstance(query_expr, exp.Select):
|
|
327
|
+
self._raise_cte_query_error(alias, f"expression must be a Select, got {type(query_expr).__name__}")
|
|
328
|
+
cte_select_expression = query_expr.copy()
|
|
329
|
+
param_mapping = self._merge_cte_parameters(alias, query.parameters)
|
|
330
|
+
updated_expression = self._update_placeholders_in_expression(cte_select_expression, param_mapping)
|
|
331
|
+
if not isinstance(updated_expression, exp.Select): # pragma: no cover - defensive
|
|
332
|
+
msg = "CTE placeholder update produced non-select expression"
|
|
333
|
+
raise SQLBuilderError(msg)
|
|
334
|
+
return updated_expression
|
|
335
|
+
|
|
336
|
+
if isinstance(query, str):
|
|
337
|
+
try:
|
|
338
|
+
parsed_expression = sqlglot.parse_one(query, read=self.dialect_name)
|
|
339
|
+
except SQLGlotParseError as e: # pragma: no cover - defensive
|
|
340
|
+
self._raise_cte_parse_error(e)
|
|
341
|
+
if not isinstance(parsed_expression, exp.Select):
|
|
342
|
+
self._raise_cte_query_error(
|
|
343
|
+
alias, f"query string must parse to SELECT, got {type(parsed_expression).__name__}"
|
|
344
|
+
)
|
|
345
|
+
return parsed_expression
|
|
346
|
+
|
|
347
|
+
if isinstance(query, exp.Select):
|
|
348
|
+
return query
|
|
349
|
+
|
|
350
|
+
self._raise_cte_query_error(alias, f"invalid query type: {type(query).__name__}")
|
|
351
|
+
msg = "Unreachable"
|
|
352
|
+
raise AssertionError(msg)
|
|
353
|
+
|
|
354
|
+
def _add_parameter(self, value: Any, context: str | None = None) -> str:
|
|
355
|
+
"""Adds a parameter to the query and returns its placeholder name.
|
|
356
|
+
|
|
357
|
+
Args:
|
|
358
|
+
value: The value of the parameter.
|
|
359
|
+
context: Optional context hint for parameter naming (e.g., "where", "join")
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
str: The placeholder name for the parameter (e.g., :param_1 or :where_param_1).
|
|
363
|
+
"""
|
|
364
|
+
self._parameter_counter += 1
|
|
365
|
+
|
|
366
|
+
param_name = f"{context}_param_{self._parameter_counter}" if context else f"param_{self._parameter_counter}"
|
|
367
|
+
|
|
368
|
+
self._parameters[param_name] = value
|
|
369
|
+
return param_name
|
|
370
|
+
|
|
371
|
+
def add_parameter_for_expression(self, value: Any, context: str | None = None) -> str:
|
|
372
|
+
"""Add a parameter for expression parameterization.
|
|
373
|
+
|
|
374
|
+
Args:
|
|
375
|
+
value: The value of the parameter.
|
|
376
|
+
context: Optional context hint for parameter naming.
|
|
377
|
+
|
|
378
|
+
Returns:
|
|
379
|
+
Parameter placeholder name.
|
|
380
|
+
"""
|
|
381
|
+
return self._add_parameter(value, context=context)
|
|
382
|
+
|
|
383
|
+
def _parameterize_expression(self, expression: exp.Expression) -> exp.Expression:
|
|
384
|
+
"""Replace literal values in an expression with bound parameters.
|
|
385
|
+
|
|
386
|
+
This method traverses a SQLGlot expression tree and replaces literal
|
|
387
|
+
values with parameter placeholders, adding the values to the builder's
|
|
388
|
+
parameter collection.
|
|
389
|
+
|
|
390
|
+
Args:
|
|
391
|
+
expression: The SQLGlot expression to parameterize
|
|
392
|
+
|
|
393
|
+
Returns:
|
|
394
|
+
A new expression with literals replaced by parameter placeholders
|
|
395
|
+
"""
|
|
396
|
+
|
|
397
|
+
return expression.transform(_ExpressionParameterizer(self), copy=False)
|
|
398
|
+
|
|
399
|
+
def add_parameter(self: Self, value: Any, name: str | None = None) -> tuple[Self, str]:
|
|
400
|
+
"""Explicitly adds a parameter to the query.
|
|
401
|
+
|
|
402
|
+
This is useful for parameters that are not directly tied to a
|
|
403
|
+
builder method like `where` or `values`.
|
|
404
|
+
|
|
405
|
+
Args:
|
|
406
|
+
value: The value of the parameter.
|
|
407
|
+
name: Optional explicit name for the parameter. If None, a name
|
|
408
|
+
will be generated.
|
|
409
|
+
|
|
410
|
+
Returns:
|
|
411
|
+
tuple[Self, str]: The builder instance and the parameter name.
|
|
412
|
+
"""
|
|
413
|
+
if name:
|
|
414
|
+
if name in self._parameters:
|
|
415
|
+
self._raise_sql_builder_error(f"Parameter name '{name}' already exists.")
|
|
416
|
+
self._parameters[name] = value
|
|
417
|
+
return self, name
|
|
418
|
+
|
|
419
|
+
self._parameter_counter += 1
|
|
420
|
+
param_name = f"param_{self._parameter_counter}"
|
|
421
|
+
self._parameters[param_name] = value
|
|
422
|
+
return self, param_name
|
|
423
|
+
|
|
424
|
+
def load_parameters(self, parameters: "Mapping[str, Any]") -> None:
|
|
425
|
+
"""Load a parameter mapping into the builder.
|
|
426
|
+
|
|
427
|
+
Args:
|
|
428
|
+
parameters: Mapping of parameter names to values.
|
|
429
|
+
|
|
430
|
+
Raises:
|
|
431
|
+
SQLBuilderError: If a parameter name already exists on the builder.
|
|
432
|
+
"""
|
|
433
|
+
if not parameters:
|
|
434
|
+
return
|
|
435
|
+
|
|
436
|
+
for name, value in parameters.items():
|
|
437
|
+
if name in self._parameters:
|
|
438
|
+
self._raise_sql_builder_error(f"Parameter name '{name}' already exists.")
|
|
439
|
+
self._parameters[name] = value
|
|
440
|
+
self._update_parameter_counter(name)
|
|
441
|
+
|
|
442
|
+
def load_ctes(self, ctes: "Iterable[exp.CTE]") -> None:
|
|
443
|
+
"""Load SQLGlot CTE nodes into the builder.
|
|
444
|
+
|
|
445
|
+
Args:
|
|
446
|
+
ctes: Iterable of CTE expressions to register.
|
|
447
|
+
|
|
448
|
+
Raises:
|
|
449
|
+
SQLBuilderError: If a CTE alias is missing or duplicated.
|
|
450
|
+
"""
|
|
451
|
+
for cte in ctes:
|
|
452
|
+
alias = self._resolve_cte_alias(cte)
|
|
453
|
+
if alias in self._with_ctes:
|
|
454
|
+
self._raise_sql_builder_error(f"CTE '{alias}' already exists.")
|
|
455
|
+
self._with_ctes[alias] = cte
|
|
456
|
+
|
|
457
|
+
def _resolve_cte_alias(self, cte: exp.CTE) -> str:
|
|
458
|
+
alias_name = cte.alias_or_name
|
|
459
|
+
if not alias_name:
|
|
460
|
+
self._raise_sql_builder_error("CTE alias is required.")
|
|
461
|
+
return str(alias_name)
|
|
462
|
+
|
|
463
|
+
def _update_parameter_counter(self, name: str) -> None:
|
|
464
|
+
match = PARAMETER_INDEX_PATTERN.match(name)
|
|
465
|
+
if not match:
|
|
466
|
+
return
|
|
467
|
+
index = int(match.group("index"))
|
|
468
|
+
self._parameter_counter = max(self._parameter_counter, index)
|
|
469
|
+
|
|
470
|
+
def _generate_unique_parameter_name(self, base_name: str) -> str:
|
|
471
|
+
"""Generate unique parameter name when collision occurs.
|
|
472
|
+
|
|
473
|
+
Args:
|
|
474
|
+
base_name: The desired base name for the parameter
|
|
475
|
+
|
|
476
|
+
Returns:
|
|
477
|
+
A unique parameter name that doesn't exist in current parameters
|
|
478
|
+
"""
|
|
479
|
+
current_index = self._parameter_name_counters.get(base_name, 0)
|
|
480
|
+
|
|
481
|
+
if base_name not in self._parameters:
|
|
482
|
+
# First use keeps the base name, counter stays at 0
|
|
483
|
+
self._parameter_name_counters[base_name] = current_index
|
|
484
|
+
return base_name
|
|
485
|
+
|
|
486
|
+
next_index = current_index + 1
|
|
487
|
+
candidate = f"{base_name}_{next_index}"
|
|
488
|
+
|
|
489
|
+
while candidate in self._parameters:
|
|
490
|
+
next_index += 1
|
|
491
|
+
if next_index > MAX_PARAMETER_COLLISION_ATTEMPTS:
|
|
492
|
+
return f"{base_name}_{uuid.uuid4().hex[:8]}"
|
|
493
|
+
candidate = f"{base_name}_{next_index}"
|
|
494
|
+
|
|
495
|
+
self._parameter_name_counters[base_name] = next_index
|
|
496
|
+
return candidate
|
|
497
|
+
|
|
498
|
+
def _create_placeholder(self, value: Any, base_name: str) -> tuple[exp.Placeholder, str]:
|
|
499
|
+
"""Backwards-compatible placeholder helper (delegates to create_placeholder)."""
|
|
500
|
+
return self.create_placeholder(value, base_name)
|
|
501
|
+
|
|
502
|
+
def create_placeholder(self, value: Any, base_name: str) -> tuple[exp.Placeholder, str]:
|
|
503
|
+
"""Create placeholder expression with a unique parameter name.
|
|
504
|
+
|
|
505
|
+
Args:
|
|
506
|
+
value: Parameter value to bind.
|
|
507
|
+
base_name: Seed for parameter naming.
|
|
508
|
+
|
|
509
|
+
Returns:
|
|
510
|
+
Tuple of placeholder expression and the final parameter name.
|
|
511
|
+
"""
|
|
512
|
+
param_name = self._generate_unique_parameter_name(base_name)
|
|
513
|
+
_, param_name = self.add_parameter(value, name=param_name)
|
|
514
|
+
return exp.Placeholder(this=param_name), param_name
|
|
515
|
+
|
|
516
|
+
def _merge_cte_parameters(self, cte_name: str, parameters: dict[str, Any]) -> dict[str, str]:
|
|
517
|
+
"""Merge CTE parameters with unique naming to prevent collisions.
|
|
518
|
+
|
|
519
|
+
Args:
|
|
520
|
+
cte_name: The name of the CTE for parameter prefixing
|
|
521
|
+
parameters: The CTE's parameter dictionary
|
|
522
|
+
|
|
523
|
+
Returns:
|
|
524
|
+
Mapping of old parameter names to new unique names
|
|
525
|
+
"""
|
|
526
|
+
param_mapping = {}
|
|
527
|
+
for old_name, value in parameters.items():
|
|
528
|
+
new_name = self._generate_unique_parameter_name(f"{cte_name}_{old_name}")
|
|
529
|
+
param_mapping[old_name] = new_name
|
|
530
|
+
self.add_parameter(value, name=new_name)
|
|
531
|
+
return param_mapping
|
|
532
|
+
|
|
533
|
+
def _update_placeholders_in_expression(
|
|
534
|
+
self, expression: exp.Expression, param_mapping: dict[str, str]
|
|
535
|
+
) -> exp.Expression:
|
|
536
|
+
"""Update parameter placeholders in expression to use new names.
|
|
537
|
+
|
|
538
|
+
Args:
|
|
539
|
+
expression: The SQLGlot expression to update
|
|
540
|
+
param_mapping: Mapping of old parameter names to new names
|
|
541
|
+
|
|
542
|
+
Returns:
|
|
543
|
+
Updated expression with new placeholder names
|
|
544
|
+
"""
|
|
545
|
+
|
|
546
|
+
return expression.transform(_PlaceholderReplacer(param_mapping), copy=False)
|
|
547
|
+
|
|
548
|
+
def _generate_builder_cache_key(self, config: "StatementConfig | None" = None) -> str:
|
|
549
|
+
"""Generate cache key based on builder state and configuration.
|
|
550
|
+
|
|
551
|
+
Args:
|
|
552
|
+
config: Optional SQL configuration that affects the generated SQL
|
|
553
|
+
|
|
554
|
+
Returns:
|
|
555
|
+
A unique cache key representing the builder state and configuration
|
|
556
|
+
"""
|
|
557
|
+
dialect_name: str = self.dialect_name or "default"
|
|
558
|
+
|
|
559
|
+
if self._expression is None:
|
|
560
|
+
self._expression = self._create_base_expression()
|
|
561
|
+
|
|
562
|
+
expr_sql: str = self._expression.sql() if self._expression else "None"
|
|
563
|
+
parameters_snapshot = sorted(self._parameters.items())
|
|
564
|
+
parameters_hash = hashlib.sha256(str(parameters_snapshot).encode()).hexdigest()[:8]
|
|
565
|
+
|
|
566
|
+
state_parts = [
|
|
567
|
+
f"expression:{expr_sql}",
|
|
568
|
+
f"parameters_hash:{parameters_hash}",
|
|
569
|
+
f"ctes:{sorted(self._with_ctes.keys())}",
|
|
570
|
+
f"dialect:{dialect_name}",
|
|
571
|
+
f"schema_hash:{hashlib.sha256(str(self.schema).encode()).hexdigest()[:8]}",
|
|
572
|
+
f"optimization:{self.enable_optimization}",
|
|
573
|
+
f"optimize_joins:{self.optimize_joins}",
|
|
574
|
+
f"optimize_predicates:{self.optimize_predicates}",
|
|
575
|
+
f"simplify_expressions:{self.simplify_expressions}",
|
|
576
|
+
]
|
|
577
|
+
|
|
578
|
+
if config:
|
|
579
|
+
config_parts = [
|
|
580
|
+
f"config_dialect:{config.dialect or 'default'}",
|
|
581
|
+
f"enable_parsing:{config.enable_parsing}",
|
|
582
|
+
f"enable_validation:{config.enable_validation}",
|
|
583
|
+
f"enable_transformations:{config.enable_transformations}",
|
|
584
|
+
f"enable_analysis:{config.enable_analysis}",
|
|
585
|
+
f"enable_caching:{config.enable_caching}",
|
|
586
|
+
f"param_style:{config.parameter_config.default_parameter_style.value}",
|
|
587
|
+
]
|
|
588
|
+
state_parts.extend(config_parts)
|
|
589
|
+
|
|
590
|
+
state_string = "|".join(state_parts)
|
|
591
|
+
return f"builder:{hashlib.sha256(state_string.encode()).hexdigest()[:16]}"
|
|
592
|
+
|
|
593
|
+
def with_cte(self: Self, alias: str, query: "QueryBuilder | exp.Select | str") -> Self:
|
|
594
|
+
"""Adds a Common Table Expression (CTE) to the query.
|
|
595
|
+
|
|
596
|
+
Args:
|
|
597
|
+
alias: The alias for the CTE.
|
|
598
|
+
query: The CTE query, which can be another QueryBuilder instance,
|
|
599
|
+
a raw SQL string, or a sqlglot Select expression.
|
|
600
|
+
|
|
601
|
+
Returns:
|
|
602
|
+
Self: The current builder instance for method chaining.
|
|
603
|
+
"""
|
|
604
|
+
if alias in self._with_ctes:
|
|
605
|
+
self._raise_sql_builder_error(f"CTE with alias '{alias}' already exists.")
|
|
606
|
+
|
|
607
|
+
cte_select_expression = self._resolve_cte_query(alias, query)
|
|
608
|
+
self._with_ctes[alias] = exp.CTE(this=cte_select_expression, alias=exp.to_table(alias))
|
|
609
|
+
return self
|
|
610
|
+
|
|
611
|
+
def build(self, dialect: DialectType = None) -> "BuiltQuery":
|
|
612
|
+
"""Builds the SQL query string and parameters.
|
|
613
|
+
|
|
614
|
+
Args:
|
|
615
|
+
dialect: Optional dialect override. If provided, generates SQL for this dialect
|
|
616
|
+
instead of the builder's default dialect.
|
|
617
|
+
|
|
618
|
+
Returns:
|
|
619
|
+
BuiltQuery: A dataclass containing the SQL string and parameters.
|
|
620
|
+
|
|
621
|
+
Examples:
|
|
622
|
+
# Use builder's default dialect
|
|
623
|
+
query = sql.select("*").from_("products")
|
|
624
|
+
result = query.build()
|
|
625
|
+
|
|
626
|
+
# Override dialect at build time
|
|
627
|
+
postgres_sql = query.build(dialect="postgres")
|
|
628
|
+
mysql_sql = query.build(dialect="mysql")
|
|
629
|
+
"""
|
|
630
|
+
final_expression = self._build_final_expression()
|
|
631
|
+
|
|
632
|
+
if self.enable_optimization and isinstance(final_expression, exp.Expression):
|
|
633
|
+
final_expression = self._optimize_expression(final_expression)
|
|
634
|
+
|
|
635
|
+
target_dialect = str(dialect) if dialect else self.dialect_name
|
|
636
|
+
|
|
637
|
+
try:
|
|
638
|
+
if isinstance(final_expression, exp.Expression):
|
|
639
|
+
normalized_expression = (
|
|
640
|
+
self._unquote_identifiers_for_oracle(final_expression)
|
|
641
|
+
if self._is_oracle_dialect(target_dialect)
|
|
642
|
+
else final_expression
|
|
643
|
+
)
|
|
644
|
+
identify = self._should_identify(target_dialect)
|
|
645
|
+
sql_string = normalized_expression.sql(dialect=target_dialect, pretty=True, identify=identify)
|
|
646
|
+
sql_string = self._strip_lock_identifier_quotes(sql_string)
|
|
647
|
+
else:
|
|
648
|
+
sql_string = str(final_expression)
|
|
649
|
+
except Exception as e:
|
|
650
|
+
err_msg = f"Error generating SQL from expression: {e!s}"
|
|
651
|
+
self._raise_sql_builder_error(err_msg, e)
|
|
652
|
+
|
|
653
|
+
return BuiltQuery(sql=sql_string, parameters=self._parameters.copy(), dialect=dialect or self.dialect)
|
|
654
|
+
|
|
655
|
+
def to_sql(self, show_parameters: bool = False, dialect: DialectType = None) -> str:
|
|
656
|
+
"""Return SQL string with optional parameter substitution.
|
|
657
|
+
|
|
658
|
+
Args:
|
|
659
|
+
show_parameters: If True, replace parameter placeholders with actual values (for debugging).
|
|
660
|
+
If False (default), return SQL with parameter placeholders.
|
|
661
|
+
dialect: Optional dialect override. If provided, generates SQL for this dialect
|
|
662
|
+
instead of the builder's default dialect.
|
|
663
|
+
|
|
664
|
+
Returns:
|
|
665
|
+
SQL string with or without parameter values filled in
|
|
666
|
+
|
|
667
|
+
Examples:
|
|
668
|
+
Get SQL with placeholders (for execution):
|
|
669
|
+
sql_str = query.to_sql()
|
|
670
|
+
# "SELECT * FROM products WHERE id = :id"
|
|
671
|
+
|
|
672
|
+
Get SQL with values (for debugging):
|
|
673
|
+
sql_str = query.to_sql(show_parameters=True)
|
|
674
|
+
# "SELECT * FROM products WHERE id = 123"
|
|
675
|
+
|
|
676
|
+
Override dialect at output time:
|
|
677
|
+
postgres_sql = query.to_sql(dialect="postgres")
|
|
678
|
+
mysql_sql = query.to_sql(dialect="mysql")
|
|
679
|
+
|
|
680
|
+
Warning:
|
|
681
|
+
SQL with show_parameters=True is for debugging ONLY.
|
|
682
|
+
Never execute SQL with interpolated parameters directly - use parameterized queries.
|
|
683
|
+
"""
|
|
684
|
+
safe_query = self.build(dialect=dialect)
|
|
685
|
+
|
|
686
|
+
if not show_parameters:
|
|
687
|
+
return safe_query.sql
|
|
688
|
+
|
|
689
|
+
sql = safe_query.sql
|
|
690
|
+
parameters = safe_query.parameters
|
|
691
|
+
|
|
692
|
+
for param_name, param_value in parameters.items():
|
|
693
|
+
placeholder = f":{param_name}"
|
|
694
|
+
if isinstance(param_value, str):
|
|
695
|
+
replacement = f"'{param_value}'"
|
|
696
|
+
elif param_value is None:
|
|
697
|
+
replacement = "NULL"
|
|
698
|
+
elif isinstance(param_value, bool):
|
|
699
|
+
replacement = "TRUE" if param_value else "FALSE"
|
|
700
|
+
else:
|
|
701
|
+
replacement = str(param_value)
|
|
702
|
+
|
|
703
|
+
sql = sql.replace(placeholder, replacement)
|
|
704
|
+
|
|
705
|
+
return sql
|
|
706
|
+
|
|
707
|
+
def _optimize_expression(self, expression: exp.Expression) -> exp.Expression:
|
|
708
|
+
"""Apply SQLGlot optimizations to the expression.
|
|
709
|
+
|
|
710
|
+
Args:
|
|
711
|
+
expression: The expression to optimize
|
|
712
|
+
|
|
713
|
+
Returns:
|
|
714
|
+
The optimized expression
|
|
715
|
+
"""
|
|
716
|
+
if not self.enable_optimization:
|
|
717
|
+
return expression
|
|
718
|
+
|
|
719
|
+
if not self.optimize_joins and not self.optimize_predicates and not self.simplify_expressions:
|
|
720
|
+
return expression
|
|
721
|
+
|
|
722
|
+
optimizer_settings = {
|
|
723
|
+
"optimize_joins": self.optimize_joins,
|
|
724
|
+
"pushdown_predicates": self.optimize_predicates,
|
|
725
|
+
"simplify_expressions": self.simplify_expressions,
|
|
726
|
+
}
|
|
727
|
+
|
|
728
|
+
dialect_name = self.dialect_name or "default"
|
|
729
|
+
cache_key = hash_optimized_expression(
|
|
730
|
+
expression, dialect=dialect_name, schema=self.schema, optimizer_settings=optimizer_settings
|
|
731
|
+
)
|
|
732
|
+
|
|
733
|
+
cache = get_cache()
|
|
734
|
+
cached_optimized = cache.get_optimized(cache_key)
|
|
735
|
+
if cached_optimized:
|
|
736
|
+
return cast("exp.Expression", cached_optimized)
|
|
737
|
+
|
|
738
|
+
try:
|
|
739
|
+
optimized = optimize(
|
|
740
|
+
expression, schema=self.schema, dialect=self.dialect_name, optimizer_settings=optimizer_settings
|
|
741
|
+
)
|
|
742
|
+
cache.put_optimized(cache_key, optimized)
|
|
743
|
+
except Exception:
|
|
744
|
+
logger.debug("Expression optimization failed, using original expression")
|
|
745
|
+
return expression
|
|
746
|
+
else:
|
|
747
|
+
return optimized
|
|
748
|
+
|
|
749
|
+
def to_statement(self, config: "StatementConfig | None" = None) -> "SQL":
|
|
750
|
+
"""Converts the built query into a SQL statement object.
|
|
751
|
+
|
|
752
|
+
Args:
|
|
753
|
+
config: Optional SQL configuration.
|
|
754
|
+
|
|
755
|
+
Returns:
|
|
756
|
+
SQL: A SQL statement object.
|
|
757
|
+
"""
|
|
758
|
+
cache_config = get_cache_config()
|
|
759
|
+
if not cache_config.compiled_cache_enabled:
|
|
760
|
+
return self._to_statement(config)
|
|
761
|
+
|
|
762
|
+
cache_key_str = self._generate_builder_cache_key(config)
|
|
763
|
+
|
|
764
|
+
cache = get_cache()
|
|
765
|
+
cached_sql = cache.get_builder(cache_key_str)
|
|
766
|
+
if cached_sql is not None:
|
|
767
|
+
return cast("SQL", cached_sql)
|
|
768
|
+
|
|
769
|
+
sql_statement = self._to_statement(config)
|
|
770
|
+
cache.put_builder(cache_key_str, sql_statement)
|
|
771
|
+
|
|
772
|
+
return sql_statement
|
|
773
|
+
|
|
774
|
+
def _to_statement(self, config: "StatementConfig | None" = None) -> "SQL":
|
|
775
|
+
"""Internal method to create SQL statement.
|
|
776
|
+
|
|
777
|
+
Args:
|
|
778
|
+
config: Optional SQL configuration.
|
|
779
|
+
|
|
780
|
+
Returns:
|
|
781
|
+
SQL: A SQL statement object.
|
|
782
|
+
"""
|
|
783
|
+
dialect_override = config.dialect if config else None
|
|
784
|
+
safe_query = self.build(dialect=dialect_override)
|
|
785
|
+
|
|
786
|
+
kwargs, parameters = self._extract_statement_parameters(safe_query.parameters)
|
|
787
|
+
|
|
788
|
+
if config is None:
|
|
789
|
+
config = StatementConfig(
|
|
790
|
+
parameter_config=ParameterStyleConfig(
|
|
791
|
+
default_parameter_style=ParameterStyle.QMARK, supported_parameter_styles={ParameterStyle.QMARK}
|
|
792
|
+
),
|
|
793
|
+
dialect=safe_query.dialect,
|
|
794
|
+
)
|
|
795
|
+
|
|
796
|
+
sql_string = safe_query.sql
|
|
797
|
+
if (
|
|
798
|
+
config.dialect is not None
|
|
799
|
+
and config.dialect != safe_query.dialect
|
|
800
|
+
and isinstance(self._expression, exp.Expression)
|
|
801
|
+
):
|
|
802
|
+
try:
|
|
803
|
+
identify = self._should_identify(config.dialect)
|
|
804
|
+
sql_string = self._expression.sql(dialect=config.dialect, pretty=True, identify=identify)
|
|
805
|
+
except Exception:
|
|
806
|
+
sql_string = safe_query.sql
|
|
807
|
+
|
|
808
|
+
if kwargs:
|
|
809
|
+
return SQL(sql_string, statement_config=config, **kwargs)
|
|
810
|
+
if parameters:
|
|
811
|
+
return SQL(sql_string, *parameters, statement_config=config)
|
|
812
|
+
return SQL(sql_string, statement_config=config)
|
|
813
|
+
|
|
814
|
+
def _extract_statement_parameters(
|
|
815
|
+
self, raw_parameters: Any
|
|
816
|
+
) -> "tuple[dict[str, Any] | None, tuple[Any, ...] | None]":
|
|
817
|
+
"""Extract parameters for SQL statement creation.
|
|
818
|
+
|
|
819
|
+
Args:
|
|
820
|
+
raw_parameters: Raw parameter data from BuiltQuery
|
|
821
|
+
|
|
822
|
+
Returns:
|
|
823
|
+
Tuple of (kwargs, parameters) for SQL statement construction
|
|
824
|
+
"""
|
|
825
|
+
if isinstance(raw_parameters, dict):
|
|
826
|
+
return raw_parameters, None
|
|
827
|
+
|
|
828
|
+
if isinstance(raw_parameters, tuple):
|
|
829
|
+
return None, raw_parameters
|
|
830
|
+
|
|
831
|
+
if raw_parameters:
|
|
832
|
+
return None, tuple(raw_parameters)
|
|
833
|
+
|
|
834
|
+
return None, None
|
|
835
|
+
|
|
836
|
+
def __str__(self) -> str:
|
|
837
|
+
"""Return the SQL string representation of the query.
|
|
838
|
+
|
|
839
|
+
Returns:
|
|
840
|
+
str: The SQL string for this query.
|
|
841
|
+
"""
|
|
842
|
+
return self.build().sql
|
|
843
|
+
|
|
844
|
+
@property
|
|
845
|
+
def dialect_name(self) -> "str | None":
|
|
846
|
+
"""Returns the name of the dialect, if set."""
|
|
847
|
+
if isinstance(self.dialect, str):
|
|
848
|
+
return self.dialect
|
|
849
|
+
if self.dialect is None:
|
|
850
|
+
return None
|
|
851
|
+
if isinstance(self.dialect, type) and issubclass(self.dialect, Dialect):
|
|
852
|
+
return self.dialect.__name__.lower()
|
|
853
|
+
if isinstance(self.dialect, Dialect):
|
|
854
|
+
return type(self.dialect).__name__.lower()
|
|
855
|
+
if has_name(self.dialect):
|
|
856
|
+
return self.dialect.__name__.lower()
|
|
857
|
+
return str(self.dialect).lower()
|
|
858
|
+
|
|
859
|
+
def _merge_sql_object_parameters(self, sql_obj: Any) -> None:
|
|
860
|
+
"""Merge parameters from a SQL object into the builder.
|
|
861
|
+
|
|
862
|
+
Args:
|
|
863
|
+
sql_obj: Object with parameters attribute containing parameter mappings
|
|
864
|
+
"""
|
|
865
|
+
if not has_expression_and_parameters(sql_obj):
|
|
866
|
+
return
|
|
867
|
+
|
|
868
|
+
sql_parameters = sql_obj.parameters
|
|
869
|
+
for param_name, param_value in sql_parameters.items():
|
|
870
|
+
unique_name = self._generate_unique_parameter_name(param_name)
|
|
871
|
+
self.add_parameter(param_value, name=unique_name)
|
|
872
|
+
|
|
873
|
+
@property
|
|
874
|
+
def parameters(self) -> dict[str, Any]:
|
|
875
|
+
"""Public access to query parameters."""
|
|
876
|
+
return self._parameters
|
|
877
|
+
|
|
878
|
+
def set_parameters(self, parameters: dict[str, Any]) -> None:
|
|
879
|
+
"""Set query parameters (public API)."""
|
|
880
|
+
self._parameters = parameters.copy()
|
|
881
|
+
|
|
882
|
+
def _is_oracle_dialect(self, dialect: "DialectType | str | None") -> bool:
|
|
883
|
+
"""Check if target dialect is Oracle."""
|
|
884
|
+
if dialect is None:
|
|
885
|
+
return False
|
|
886
|
+
return str(dialect).lower() == "oracle"
|
|
887
|
+
|
|
888
|
+
def _unquote_identifiers_for_oracle(self, expression: exp.Expression) -> exp.Expression:
|
|
889
|
+
"""Remove identifier quoting to avoid Oracle case-sensitive lookup issues."""
|
|
890
|
+
|
|
891
|
+
return expression.copy().transform(_unquote_identifier, copy=False)
|
|
892
|
+
|
|
893
|
+
def _strip_lock_identifier_quotes(self, sql_string: str) -> str:
|
|
894
|
+
for keyword in ("FOR UPDATE OF ", "FOR SHARE OF "):
|
|
895
|
+
if keyword in sql_string and not self._lock_targets_quoted:
|
|
896
|
+
head, tail = sql_string.split(keyword, 1)
|
|
897
|
+
tail = tail.replace('"', "")
|
|
898
|
+
return f"{head}{keyword}{tail}"
|
|
899
|
+
if sql_string.startswith('MERGE INTO "') and not self._merge_target_quoted:
|
|
900
|
+
# Remove quotes around target table only, leave alias/rest intact
|
|
901
|
+
end_quote = sql_string.find('"', len('MERGE INTO "'))
|
|
902
|
+
if end_quote > 0:
|
|
903
|
+
table_name = sql_string[len('MERGE INTO "') : end_quote]
|
|
904
|
+
remainder = sql_string[end_quote + 1 :]
|
|
905
|
+
return f"MERGE INTO {table_name}{remainder}"
|
|
906
|
+
return sql_string
|
|
907
|
+
|
|
908
|
+
def _should_identify(self, dialect: "DialectType | str | None") -> bool:
|
|
909
|
+
"""Determine whether to quote identifiers for the given dialect."""
|
|
910
|
+
if dialect is None:
|
|
911
|
+
return True
|
|
912
|
+
dialect_name = str(dialect).lower()
|
|
913
|
+
# Oracle folds unquoted identifiers to uppercase; quoting lower-case breaks table lookup
|
|
914
|
+
return dialect_name != "oracle"
|
|
915
|
+
|
|
916
|
+
@property
|
|
917
|
+
def with_ctes(self) -> "dict[str, exp.CTE]":
|
|
918
|
+
"""Get WITH clause CTEs (public API)."""
|
|
919
|
+
return dict(self._with_ctes)
|
|
920
|
+
|
|
921
|
+
def generate_unique_parameter_name(self, base_name: str) -> str:
|
|
922
|
+
"""Generate unique parameter name (public API)."""
|
|
923
|
+
return self._generate_unique_parameter_name(base_name)
|
|
924
|
+
|
|
925
|
+
def build_static_expression(
|
|
926
|
+
self,
|
|
927
|
+
expression: exp.Expression | None = None,
|
|
928
|
+
parameters: dict[str, Any] | None = None,
|
|
929
|
+
*,
|
|
930
|
+
cache_key: str | None = None,
|
|
931
|
+
expression_factory: Callable[[], exp.Expression] | None = None,
|
|
932
|
+
copy: bool = True,
|
|
933
|
+
optimize_expression: bool | None = None,
|
|
934
|
+
dialect: DialectType | None = None,
|
|
935
|
+
) -> "BuiltQuery":
|
|
936
|
+
"""Compile a pre-built expression with optional caching and parameters.
|
|
937
|
+
|
|
938
|
+
Designed for hot paths that construct an AST once and reuse it with
|
|
939
|
+
different parameters, avoiding repeated parse/optimize cycles.
|
|
940
|
+
|
|
941
|
+
Args:
|
|
942
|
+
expression: Pre-built sqlglot expression to render (required when cache_key is not provided).
|
|
943
|
+
parameters: Optional parameter mapping to include in the result.
|
|
944
|
+
cache_key: When provided, the expression will be cached under this key.
|
|
945
|
+
expression_factory: Factory used to build the expression on cache miss.
|
|
946
|
+
copy: Copy the expression before rendering to avoid caller mutation.
|
|
947
|
+
optimize_expression: Override builder optimization toggle for this call.
|
|
948
|
+
dialect: Optional dialect override for SQL generation.
|
|
949
|
+
|
|
950
|
+
Returns:
|
|
951
|
+
BuiltQuery containing SQL and parameters.
|
|
952
|
+
"""
|
|
953
|
+
|
|
954
|
+
expr: exp.Expression | None = None
|
|
955
|
+
|
|
956
|
+
if cache_key is not None:
|
|
957
|
+
cache = get_cache()
|
|
958
|
+
cached_expr = cache.get_expression(cache_key)
|
|
959
|
+
if cached_expr is None:
|
|
960
|
+
if expression_factory is None:
|
|
961
|
+
msg = "expression_factory is required when cache_key is provided"
|
|
962
|
+
self._raise_sql_builder_error(msg)
|
|
963
|
+
expr_candidate = expression_factory()
|
|
964
|
+
if not is_expression(expr_candidate):
|
|
965
|
+
self._raise_invalid_expression_type(expr_candidate)
|
|
966
|
+
expr_to_store = expr_candidate.copy() if copy else expr_candidate
|
|
967
|
+
should_optimize = self.enable_optimization if optimize_expression is None else optimize_expression
|
|
968
|
+
if should_optimize:
|
|
969
|
+
expr_to_store = self._optimize_expression(expr_to_store)
|
|
970
|
+
cache.put_expression(cache_key, expr_to_store)
|
|
971
|
+
cached_expr = expr_to_store
|
|
972
|
+
expr = cached_expr.copy() if copy else cached_expr
|
|
973
|
+
else:
|
|
974
|
+
if expression is None:
|
|
975
|
+
msg = "expression must be provided when cache_key is not set"
|
|
976
|
+
self._raise_sql_builder_error(msg)
|
|
977
|
+
expr = expression.copy() if copy else expression
|
|
978
|
+
should_optimize = self.enable_optimization if optimize_expression is None else optimize_expression
|
|
979
|
+
if should_optimize:
|
|
980
|
+
expr = self._optimize_expression(expr)
|
|
981
|
+
|
|
982
|
+
if expr is None:
|
|
983
|
+
self._raise_sql_builder_error("Static expression could not be resolved.")
|
|
984
|
+
|
|
985
|
+
target_dialect = str(dialect) if dialect else self.dialect_name
|
|
986
|
+
identify = self._should_identify(target_dialect)
|
|
987
|
+
sql_string = expr.sql(dialect=target_dialect, pretty=True, identify=identify)
|
|
988
|
+
return BuiltQuery(
|
|
989
|
+
sql=sql_string, parameters=parameters.copy() if parameters else {}, dialect=dialect or self.dialect
|
|
990
|
+
)
|
|
991
|
+
|
|
992
|
+
|
|
993
|
+
class ExpressionBuilder(QueryBuilder):
|
|
994
|
+
"""Builder wrapper for a pre-parsed SQLGlot expression."""
|
|
995
|
+
|
|
996
|
+
__slots__ = ()
|
|
997
|
+
|
|
998
|
+
def __init__(self, expression: exp.Expression, **kwargs: Any) -> None:
|
|
999
|
+
(dialect, schema, enable_optimization, optimize_joins, optimize_predicates, simplify_expressions) = (
|
|
1000
|
+
self._parse_query_builder_kwargs(kwargs)
|
|
1001
|
+
)
|
|
1002
|
+
super().__init__(
|
|
1003
|
+
dialect=dialect,
|
|
1004
|
+
schema=schema,
|
|
1005
|
+
enable_optimization=enable_optimization,
|
|
1006
|
+
optimize_joins=optimize_joins,
|
|
1007
|
+
optimize_predicates=optimize_predicates,
|
|
1008
|
+
simplify_expressions=simplify_expressions,
|
|
1009
|
+
)
|
|
1010
|
+
if not is_expression(expression):
|
|
1011
|
+
self._raise_invalid_expression_type(expression)
|
|
1012
|
+
self._expression = expression
|
|
1013
|
+
|
|
1014
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
1015
|
+
if self._expression is None:
|
|
1016
|
+
msg = "ExpressionBuilder requires an expression at construction."
|
|
1017
|
+
self._raise_sql_builder_error(msg)
|
|
1018
|
+
return self._expression
|
|
1019
|
+
|
|
1020
|
+
@property
|
|
1021
|
+
def _expected_result_type(self) -> "type[SQLResult]":
|
|
1022
|
+
return SQLResult
|