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/_ddl.py
ADDED
|
@@ -0,0 +1,1642 @@
|
|
|
1
|
+
"""DDL statement builders.
|
|
2
|
+
|
|
3
|
+
Provides builders for DDL operations including CREATE, DROP, ALTER,
|
|
4
|
+
TRUNCATE, and other schema manipulation statements.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Union
|
|
8
|
+
|
|
9
|
+
from sqlglot import exp
|
|
10
|
+
from typing_extensions import Self
|
|
11
|
+
|
|
12
|
+
from sqlspec.builder._base import BuiltQuery, QueryBuilder
|
|
13
|
+
from sqlspec.builder._select import Select
|
|
14
|
+
from sqlspec.core import SQL, SQLResult
|
|
15
|
+
from sqlspec.utils.type_guards import has_sqlglot_expression, has_with_method
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from sqlglot.dialects.dialect import DialectType
|
|
19
|
+
|
|
20
|
+
from sqlspec.builder._column import ColumnExpression
|
|
21
|
+
from sqlspec.core import StatementConfig
|
|
22
|
+
|
|
23
|
+
__all__ = (
|
|
24
|
+
"AlterOperation",
|
|
25
|
+
"AlterTable",
|
|
26
|
+
"ColumnDefinition",
|
|
27
|
+
"CommentOn",
|
|
28
|
+
"ConstraintDefinition",
|
|
29
|
+
"CreateIndex",
|
|
30
|
+
"CreateMaterializedView",
|
|
31
|
+
"CreateSchema",
|
|
32
|
+
"CreateTable",
|
|
33
|
+
"CreateTableAsSelect",
|
|
34
|
+
"CreateView",
|
|
35
|
+
"DDLBuilder",
|
|
36
|
+
"DropIndex",
|
|
37
|
+
"DropSchema",
|
|
38
|
+
"DropTable",
|
|
39
|
+
"DropView",
|
|
40
|
+
"RenameTable",
|
|
41
|
+
"Truncate",
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
CONSTRAINT_TYPE_PRIMARY_KEY = "PRIMARY KEY"
|
|
45
|
+
CONSTRAINT_TYPE_FOREIGN_KEY = "FOREIGN KEY"
|
|
46
|
+
CONSTRAINT_TYPE_UNIQUE = "UNIQUE"
|
|
47
|
+
CONSTRAINT_TYPE_CHECK = "CHECK"
|
|
48
|
+
|
|
49
|
+
FOREIGN_KEY_ACTION_CASCADE = "CASCADE"
|
|
50
|
+
FOREIGN_KEY_ACTION_SET_NULL = "SET NULL"
|
|
51
|
+
FOREIGN_KEY_ACTION_SET_DEFAULT = "SET DEFAULT"
|
|
52
|
+
FOREIGN_KEY_ACTION_RESTRICT = "RESTRICT"
|
|
53
|
+
FOREIGN_KEY_ACTION_NO_ACTION = "NO ACTION"
|
|
54
|
+
|
|
55
|
+
VALID_FOREIGN_KEY_ACTIONS = {
|
|
56
|
+
FOREIGN_KEY_ACTION_CASCADE,
|
|
57
|
+
FOREIGN_KEY_ACTION_SET_NULL,
|
|
58
|
+
FOREIGN_KEY_ACTION_SET_DEFAULT,
|
|
59
|
+
FOREIGN_KEY_ACTION_RESTRICT,
|
|
60
|
+
FOREIGN_KEY_ACTION_NO_ACTION,
|
|
61
|
+
None,
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
VALID_CONSTRAINT_TYPES = {
|
|
65
|
+
CONSTRAINT_TYPE_PRIMARY_KEY,
|
|
66
|
+
CONSTRAINT_TYPE_FOREIGN_KEY,
|
|
67
|
+
CONSTRAINT_TYPE_UNIQUE,
|
|
68
|
+
CONSTRAINT_TYPE_CHECK,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
CURRENT_TIMESTAMP_KEYWORD = "CURRENT_TIMESTAMP"
|
|
72
|
+
CURRENT_DATE_KEYWORD = "CURRENT_DATE"
|
|
73
|
+
CURRENT_TIME_KEYWORD = "CURRENT_TIME"
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def build_column_expression(col: "ColumnDefinition") -> "exp.Expression":
|
|
77
|
+
"""Build SQLGlot expression for a column definition."""
|
|
78
|
+
col_def = exp.ColumnDef(this=exp.to_identifier(col.name), kind=exp.DataType.build(col.dtype))
|
|
79
|
+
|
|
80
|
+
constraints: list[exp.ColumnConstraint] = []
|
|
81
|
+
|
|
82
|
+
if col.not_null:
|
|
83
|
+
constraints.append(exp.ColumnConstraint(kind=exp.NotNullColumnConstraint()))
|
|
84
|
+
|
|
85
|
+
if col.primary_key:
|
|
86
|
+
constraints.append(exp.ColumnConstraint(kind=exp.PrimaryKeyColumnConstraint()))
|
|
87
|
+
|
|
88
|
+
if col.unique:
|
|
89
|
+
constraints.append(exp.ColumnConstraint(kind=exp.UniqueColumnConstraint()))
|
|
90
|
+
|
|
91
|
+
if col.default is not None:
|
|
92
|
+
default_expr: exp.Expression | None = None
|
|
93
|
+
if isinstance(col.default, str):
|
|
94
|
+
default_upper = col.default.upper()
|
|
95
|
+
if default_upper == CURRENT_TIMESTAMP_KEYWORD:
|
|
96
|
+
default_expr = exp.CurrentTimestamp()
|
|
97
|
+
elif default_upper == CURRENT_DATE_KEYWORD:
|
|
98
|
+
default_expr = exp.CurrentDate()
|
|
99
|
+
elif default_upper == CURRENT_TIME_KEYWORD:
|
|
100
|
+
default_expr = exp.CurrentTime()
|
|
101
|
+
elif "(" in col.default:
|
|
102
|
+
default_expr = exp.maybe_parse(col.default)
|
|
103
|
+
else:
|
|
104
|
+
default_expr = exp.convert(col.default)
|
|
105
|
+
else:
|
|
106
|
+
default_expr = exp.convert(col.default)
|
|
107
|
+
|
|
108
|
+
constraints.append(exp.ColumnConstraint(kind=exp.DefaultColumnConstraint(this=default_expr)))
|
|
109
|
+
|
|
110
|
+
if col.check:
|
|
111
|
+
constraints.append(exp.ColumnConstraint(kind=exp.Check(this=exp.maybe_parse(col.check))))
|
|
112
|
+
|
|
113
|
+
if col.comment:
|
|
114
|
+
constraints.append(exp.ColumnConstraint(kind=exp.CommentColumnConstraint(this=exp.convert(col.comment))))
|
|
115
|
+
|
|
116
|
+
if col.generated:
|
|
117
|
+
constraints.append(
|
|
118
|
+
exp.ColumnConstraint(kind=exp.GeneratedAsIdentityColumnConstraint(this=exp.maybe_parse(col.generated)))
|
|
119
|
+
)
|
|
120
|
+
|
|
121
|
+
if col.collate:
|
|
122
|
+
constraints.append(exp.ColumnConstraint(kind=exp.CollateColumnConstraint(this=exp.to_identifier(col.collate))))
|
|
123
|
+
|
|
124
|
+
if constraints:
|
|
125
|
+
col_def.set("constraints", constraints)
|
|
126
|
+
|
|
127
|
+
return col_def
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def build_constraint_expression(constraint: "ConstraintDefinition") -> "exp.Expression | None":
|
|
131
|
+
"""Build SQLGlot expression for a table constraint."""
|
|
132
|
+
if constraint.constraint_type == CONSTRAINT_TYPE_PRIMARY_KEY:
|
|
133
|
+
pk_constraint = exp.PrimaryKey(expressions=[exp.to_identifier(col) for col in constraint.columns])
|
|
134
|
+
|
|
135
|
+
if constraint.name:
|
|
136
|
+
return exp.Constraint(this=exp.to_identifier(constraint.name), expression=pk_constraint)
|
|
137
|
+
return pk_constraint
|
|
138
|
+
|
|
139
|
+
if constraint.constraint_type == CONSTRAINT_TYPE_FOREIGN_KEY:
|
|
140
|
+
fk_constraint = exp.ForeignKey(
|
|
141
|
+
expressions=[exp.to_identifier(col) for col in constraint.columns],
|
|
142
|
+
reference=exp.Reference(
|
|
143
|
+
this=exp.to_table(constraint.references_table) if constraint.references_table else None,
|
|
144
|
+
expressions=[exp.to_identifier(col) for col in constraint.references_columns],
|
|
145
|
+
on_delete=constraint.on_delete,
|
|
146
|
+
on_update=constraint.on_update,
|
|
147
|
+
),
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
if constraint.name:
|
|
151
|
+
return exp.Constraint(this=exp.to_identifier(constraint.name), expression=fk_constraint)
|
|
152
|
+
return fk_constraint
|
|
153
|
+
|
|
154
|
+
if constraint.constraint_type == CONSTRAINT_TYPE_UNIQUE:
|
|
155
|
+
unique_constraint = exp.UniqueKeyProperty(expressions=[exp.to_identifier(col) for col in constraint.columns])
|
|
156
|
+
|
|
157
|
+
if constraint.name:
|
|
158
|
+
return exp.Constraint(this=exp.to_identifier(constraint.name), expression=unique_constraint)
|
|
159
|
+
return unique_constraint
|
|
160
|
+
|
|
161
|
+
if constraint.constraint_type == CONSTRAINT_TYPE_CHECK:
|
|
162
|
+
check_expr = exp.Check(this=exp.maybe_parse(constraint.condition) if constraint.condition else None)
|
|
163
|
+
|
|
164
|
+
if constraint.name:
|
|
165
|
+
return exp.Constraint(this=exp.to_identifier(constraint.name), expression=check_expr)
|
|
166
|
+
return check_expr
|
|
167
|
+
|
|
168
|
+
return None
|
|
169
|
+
|
|
170
|
+
|
|
171
|
+
class DDLBuilder(QueryBuilder):
|
|
172
|
+
"""Base class for DDL builders (CREATE, DROP, ALTER, etc)."""
|
|
173
|
+
|
|
174
|
+
__slots__ = ()
|
|
175
|
+
|
|
176
|
+
def __init__(self, dialect: "DialectType" = None) -> None:
|
|
177
|
+
super().__init__(dialect=dialect)
|
|
178
|
+
self._expression: exp.Expression | None = None
|
|
179
|
+
|
|
180
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
181
|
+
msg = "Subclasses must implement _create_base_expression."
|
|
182
|
+
raise NotImplementedError(msg)
|
|
183
|
+
|
|
184
|
+
@property
|
|
185
|
+
def _expected_result_type(self) -> "type[SQLResult]":
|
|
186
|
+
return SQLResult
|
|
187
|
+
|
|
188
|
+
def build(self, dialect: "DialectType" = None) -> "BuiltQuery":
|
|
189
|
+
if self._expression is None:
|
|
190
|
+
self._expression = self._create_base_expression()
|
|
191
|
+
return super().build(dialect=dialect)
|
|
192
|
+
|
|
193
|
+
def to_statement(self, config: "StatementConfig | None" = None) -> "SQL":
|
|
194
|
+
return super().to_statement(config=config)
|
|
195
|
+
|
|
196
|
+
|
|
197
|
+
class ColumnDefinition:
|
|
198
|
+
"""Column definition for CREATE TABLE."""
|
|
199
|
+
|
|
200
|
+
__slots__ = (
|
|
201
|
+
"auto_increment",
|
|
202
|
+
"check",
|
|
203
|
+
"collate",
|
|
204
|
+
"comment",
|
|
205
|
+
"default",
|
|
206
|
+
"dtype",
|
|
207
|
+
"generated",
|
|
208
|
+
"name",
|
|
209
|
+
"not_null",
|
|
210
|
+
"primary_key",
|
|
211
|
+
"unique",
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
def __init__(
|
|
215
|
+
self,
|
|
216
|
+
name: str,
|
|
217
|
+
dtype: str,
|
|
218
|
+
default: "Any | None" = None,
|
|
219
|
+
not_null: bool = False,
|
|
220
|
+
primary_key: bool = False,
|
|
221
|
+
unique: bool = False,
|
|
222
|
+
auto_increment: bool = False,
|
|
223
|
+
comment: "str | None" = None,
|
|
224
|
+
check: "str | None" = None,
|
|
225
|
+
generated: "str | None" = None,
|
|
226
|
+
collate: "str | None" = None,
|
|
227
|
+
) -> None:
|
|
228
|
+
self.name = name
|
|
229
|
+
self.dtype = dtype
|
|
230
|
+
self.default = default
|
|
231
|
+
self.not_null = not_null
|
|
232
|
+
self.primary_key = primary_key
|
|
233
|
+
self.unique = unique
|
|
234
|
+
self.auto_increment = auto_increment
|
|
235
|
+
self.comment = comment
|
|
236
|
+
self.check = check
|
|
237
|
+
self.generated = generated
|
|
238
|
+
self.collate = collate
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
class ConstraintDefinition:
|
|
242
|
+
"""Constraint definition for CREATE TABLE."""
|
|
243
|
+
|
|
244
|
+
__slots__ = (
|
|
245
|
+
"columns",
|
|
246
|
+
"condition",
|
|
247
|
+
"constraint_type",
|
|
248
|
+
"deferrable",
|
|
249
|
+
"initially_deferred",
|
|
250
|
+
"name",
|
|
251
|
+
"on_delete",
|
|
252
|
+
"on_update",
|
|
253
|
+
"references_columns",
|
|
254
|
+
"references_table",
|
|
255
|
+
)
|
|
256
|
+
|
|
257
|
+
def __init__(
|
|
258
|
+
self,
|
|
259
|
+
constraint_type: str,
|
|
260
|
+
name: "str | None" = None,
|
|
261
|
+
columns: "list[str] | None" = None,
|
|
262
|
+
references_table: "str | None" = None,
|
|
263
|
+
references_columns: "list[str] | None" = None,
|
|
264
|
+
condition: "str | None" = None,
|
|
265
|
+
on_delete: "str | None" = None,
|
|
266
|
+
on_update: "str | None" = None,
|
|
267
|
+
deferrable: bool = False,
|
|
268
|
+
initially_deferred: bool = False,
|
|
269
|
+
) -> None:
|
|
270
|
+
self.constraint_type = constraint_type
|
|
271
|
+
self.name = name
|
|
272
|
+
self.columns = columns or []
|
|
273
|
+
self.references_table = references_table
|
|
274
|
+
self.references_columns = references_columns or []
|
|
275
|
+
self.condition = condition
|
|
276
|
+
self.on_delete = on_delete
|
|
277
|
+
self.on_update = on_update
|
|
278
|
+
self.deferrable = deferrable
|
|
279
|
+
self.initially_deferred = initially_deferred
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
class CreateTable(DDLBuilder):
|
|
283
|
+
"""Builder for CREATE TABLE statements with columns and constraints.
|
|
284
|
+
|
|
285
|
+
Example:
|
|
286
|
+
builder = (
|
|
287
|
+
CreateTable("users")
|
|
288
|
+
.column("id", "SERIAL", primary_key=True)
|
|
289
|
+
.column("email", "VARCHAR(255)", not_null=True, unique=True)
|
|
290
|
+
.column("created_at", "TIMESTAMP", default="CURRENT_TIMESTAMP")
|
|
291
|
+
.foreign_key_constraint("org_id", "organizations", "id")
|
|
292
|
+
)
|
|
293
|
+
sql = builder.build().sql
|
|
294
|
+
"""
|
|
295
|
+
|
|
296
|
+
__slots__ = (
|
|
297
|
+
"_columns",
|
|
298
|
+
"_constraints",
|
|
299
|
+
"_if_not_exists",
|
|
300
|
+
"_like_table",
|
|
301
|
+
"_partition_by",
|
|
302
|
+
"_schema",
|
|
303
|
+
"_table_name",
|
|
304
|
+
"_table_options",
|
|
305
|
+
"_tablespace",
|
|
306
|
+
"_temporary",
|
|
307
|
+
)
|
|
308
|
+
|
|
309
|
+
def __init__(self, table_name: str, dialect: "DialectType" = None) -> None:
|
|
310
|
+
super().__init__(dialect=dialect)
|
|
311
|
+
self._table_name = table_name
|
|
312
|
+
self._if_not_exists = False
|
|
313
|
+
self._temporary = False
|
|
314
|
+
self._columns: list[ColumnDefinition] = []
|
|
315
|
+
self._constraints: list[ConstraintDefinition] = []
|
|
316
|
+
self._table_options: dict[str, Any] = {}
|
|
317
|
+
self._schema: str | None = None
|
|
318
|
+
self._tablespace: str | None = None
|
|
319
|
+
self._like_table: str | None = None
|
|
320
|
+
self._partition_by: str | None = None
|
|
321
|
+
|
|
322
|
+
def in_schema(self, schema_name: str) -> "Self":
|
|
323
|
+
"""Set the schema for the table."""
|
|
324
|
+
self._schema = schema_name
|
|
325
|
+
return self
|
|
326
|
+
|
|
327
|
+
def if_not_exists(self) -> "Self":
|
|
328
|
+
"""Add IF NOT EXISTS clause."""
|
|
329
|
+
self._if_not_exists = True
|
|
330
|
+
return self
|
|
331
|
+
|
|
332
|
+
def temporary(self) -> "Self":
|
|
333
|
+
"""Create a temporary table."""
|
|
334
|
+
self._temporary = True
|
|
335
|
+
return self
|
|
336
|
+
|
|
337
|
+
def like(self, source_table: str) -> "Self":
|
|
338
|
+
"""Create table LIKE another table."""
|
|
339
|
+
self._like_table = source_table
|
|
340
|
+
return self
|
|
341
|
+
|
|
342
|
+
def tablespace(self, name: str) -> "Self":
|
|
343
|
+
"""Set tablespace for the table."""
|
|
344
|
+
self._tablespace = name
|
|
345
|
+
return self
|
|
346
|
+
|
|
347
|
+
def partition_by(self, partition_spec: str) -> "Self":
|
|
348
|
+
"""Set partitioning specification."""
|
|
349
|
+
self._partition_by = partition_spec
|
|
350
|
+
return self
|
|
351
|
+
|
|
352
|
+
@property
|
|
353
|
+
def columns(self) -> "list[ColumnDefinition]":
|
|
354
|
+
"""Get the list of column definitions for this table.
|
|
355
|
+
|
|
356
|
+
Returns:
|
|
357
|
+
List of ColumnDefinition objects.
|
|
358
|
+
"""
|
|
359
|
+
return self._columns
|
|
360
|
+
|
|
361
|
+
def column(
|
|
362
|
+
self,
|
|
363
|
+
name: str,
|
|
364
|
+
dtype: str,
|
|
365
|
+
default: "Any | None" = None,
|
|
366
|
+
not_null: bool = False,
|
|
367
|
+
primary_key: bool = False,
|
|
368
|
+
unique: bool = False,
|
|
369
|
+
auto_increment: bool = False,
|
|
370
|
+
comment: "str | None" = None,
|
|
371
|
+
check: "str | None" = None,
|
|
372
|
+
generated: "str | None" = None,
|
|
373
|
+
collate: "str | None" = None,
|
|
374
|
+
) -> "Self":
|
|
375
|
+
"""Add a column definition to the table."""
|
|
376
|
+
if not name:
|
|
377
|
+
self._raise_sql_builder_error("Column name must be a non-empty string")
|
|
378
|
+
|
|
379
|
+
if not dtype:
|
|
380
|
+
self._raise_sql_builder_error("Column type must be a non-empty string")
|
|
381
|
+
|
|
382
|
+
if any(col.name == name for col in self._columns):
|
|
383
|
+
self._raise_sql_builder_error(f"Column '{name}' already defined")
|
|
384
|
+
|
|
385
|
+
column_def = ColumnDefinition(
|
|
386
|
+
name=name,
|
|
387
|
+
dtype=dtype,
|
|
388
|
+
default=default,
|
|
389
|
+
not_null=not_null,
|
|
390
|
+
primary_key=primary_key,
|
|
391
|
+
unique=unique,
|
|
392
|
+
auto_increment=auto_increment,
|
|
393
|
+
comment=comment,
|
|
394
|
+
check=check,
|
|
395
|
+
generated=generated,
|
|
396
|
+
collate=collate,
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
self._columns.append(column_def)
|
|
400
|
+
|
|
401
|
+
if primary_key and not self._has_primary_key_constraint():
|
|
402
|
+
self.primary_key_constraint([name])
|
|
403
|
+
|
|
404
|
+
return self
|
|
405
|
+
|
|
406
|
+
def primary_key_constraint(self, columns: "str | list[str]", name: "str | None" = None) -> "Self":
|
|
407
|
+
"""Add a primary key constraint."""
|
|
408
|
+
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
409
|
+
|
|
410
|
+
if not col_list:
|
|
411
|
+
self._raise_sql_builder_error("Primary key must include at least one column")
|
|
412
|
+
|
|
413
|
+
existing_pk = self._find_primary_key_constraint()
|
|
414
|
+
if existing_pk:
|
|
415
|
+
for col in col_list:
|
|
416
|
+
if col not in existing_pk.columns:
|
|
417
|
+
existing_pk.columns.append(col)
|
|
418
|
+
else:
|
|
419
|
+
constraint = ConstraintDefinition(constraint_type=CONSTRAINT_TYPE_PRIMARY_KEY, name=name, columns=col_list)
|
|
420
|
+
self._constraints.append(constraint)
|
|
421
|
+
|
|
422
|
+
return self
|
|
423
|
+
|
|
424
|
+
def foreign_key_constraint(
|
|
425
|
+
self,
|
|
426
|
+
columns: "str | list[str]",
|
|
427
|
+
references_table: str,
|
|
428
|
+
references_columns: "str | list[str]",
|
|
429
|
+
name: "str | None" = None,
|
|
430
|
+
on_delete: "str | None" = None,
|
|
431
|
+
on_update: "str | None" = None,
|
|
432
|
+
deferrable: bool = False,
|
|
433
|
+
initially_deferred: bool = False,
|
|
434
|
+
) -> "Self":
|
|
435
|
+
"""Add a foreign key constraint."""
|
|
436
|
+
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
437
|
+
|
|
438
|
+
ref_col_list = [references_columns] if isinstance(references_columns, str) else list(references_columns)
|
|
439
|
+
|
|
440
|
+
if len(col_list) != len(ref_col_list):
|
|
441
|
+
self._raise_sql_builder_error("Foreign key columns and referenced columns must have same length")
|
|
442
|
+
|
|
443
|
+
self._validate_foreign_key_action(on_delete, "ON DELETE")
|
|
444
|
+
self._validate_foreign_key_action(on_update, "ON UPDATE")
|
|
445
|
+
|
|
446
|
+
constraint = ConstraintDefinition(
|
|
447
|
+
constraint_type=CONSTRAINT_TYPE_FOREIGN_KEY,
|
|
448
|
+
name=name,
|
|
449
|
+
columns=col_list,
|
|
450
|
+
references_table=references_table,
|
|
451
|
+
references_columns=ref_col_list,
|
|
452
|
+
on_delete=on_delete.upper() if on_delete else None,
|
|
453
|
+
on_update=on_update.upper() if on_update else None,
|
|
454
|
+
deferrable=deferrable,
|
|
455
|
+
initially_deferred=initially_deferred,
|
|
456
|
+
)
|
|
457
|
+
|
|
458
|
+
self._constraints.append(constraint)
|
|
459
|
+
return self
|
|
460
|
+
|
|
461
|
+
def unique_constraint(self, columns: "str | list[str]", name: "str | None" = None) -> "Self":
|
|
462
|
+
"""Add a unique constraint."""
|
|
463
|
+
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
464
|
+
|
|
465
|
+
if not col_list:
|
|
466
|
+
self._raise_sql_builder_error("Unique constraint must include at least one column")
|
|
467
|
+
|
|
468
|
+
constraint = ConstraintDefinition(constraint_type=CONSTRAINT_TYPE_UNIQUE, name=name, columns=col_list)
|
|
469
|
+
|
|
470
|
+
self._constraints.append(constraint)
|
|
471
|
+
return self
|
|
472
|
+
|
|
473
|
+
def check_constraint(self, condition: Union[str, "ColumnExpression"], name: "str | None" = None) -> "Self":
|
|
474
|
+
"""Add a check constraint."""
|
|
475
|
+
if not condition:
|
|
476
|
+
self._raise_sql_builder_error("Check constraint must have a condition")
|
|
477
|
+
|
|
478
|
+
condition_str: str
|
|
479
|
+
if has_sqlglot_expression(condition):
|
|
480
|
+
sqlglot_expr = condition.sqlglot_expression
|
|
481
|
+
condition_str = sqlglot_expr.sql(dialect=self.dialect) if sqlglot_expr else str(condition)
|
|
482
|
+
else:
|
|
483
|
+
condition_str = str(condition)
|
|
484
|
+
|
|
485
|
+
constraint = ConstraintDefinition(constraint_type=CONSTRAINT_TYPE_CHECK, name=name, condition=condition_str)
|
|
486
|
+
|
|
487
|
+
self._constraints.append(constraint)
|
|
488
|
+
return self
|
|
489
|
+
|
|
490
|
+
def engine(self, engine_name: str) -> "Self":
|
|
491
|
+
"""Set storage engine (MySQL/MariaDB)."""
|
|
492
|
+
self._table_options["engine"] = engine_name
|
|
493
|
+
return self
|
|
494
|
+
|
|
495
|
+
def charset(self, charset_name: str) -> "Self":
|
|
496
|
+
"""Set character set."""
|
|
497
|
+
self._table_options["charset"] = charset_name
|
|
498
|
+
return self
|
|
499
|
+
|
|
500
|
+
def collate(self, collation: str) -> "Self":
|
|
501
|
+
"""Set table collation."""
|
|
502
|
+
self._table_options["collate"] = collation
|
|
503
|
+
return self
|
|
504
|
+
|
|
505
|
+
def comment(self, comment_text: str) -> "Self":
|
|
506
|
+
"""Set table comment."""
|
|
507
|
+
self._table_options["comment"] = comment_text
|
|
508
|
+
return self
|
|
509
|
+
|
|
510
|
+
def with_option(self, key: str, value: "Any") -> "Self":
|
|
511
|
+
"""Add custom table option."""
|
|
512
|
+
self._table_options[key] = value
|
|
513
|
+
return self
|
|
514
|
+
|
|
515
|
+
def _create_base_expression(self) -> "exp.Expression":
|
|
516
|
+
"""Create the SQLGlot expression for CREATE TABLE."""
|
|
517
|
+
if not self._columns and not self._like_table:
|
|
518
|
+
self._raise_sql_builder_error("Table must have at least one column or use LIKE clause")
|
|
519
|
+
|
|
520
|
+
column_defs: list[exp.Expression] = []
|
|
521
|
+
for col in self._columns:
|
|
522
|
+
col_expr = build_column_expression(col)
|
|
523
|
+
column_defs.append(col_expr)
|
|
524
|
+
|
|
525
|
+
for constraint in self._constraints:
|
|
526
|
+
if self._is_redundant_single_column_primary_key(constraint):
|
|
527
|
+
continue
|
|
528
|
+
|
|
529
|
+
constraint_expr = build_constraint_expression(constraint)
|
|
530
|
+
if constraint_expr:
|
|
531
|
+
column_defs.append(constraint_expr)
|
|
532
|
+
|
|
533
|
+
props: list[exp.Property] = []
|
|
534
|
+
if self._table_options.get("engine"):
|
|
535
|
+
props.append(
|
|
536
|
+
exp.Property(
|
|
537
|
+
this=exp.to_identifier("ENGINE"), value=exp.to_identifier(self._table_options.get("engine"))
|
|
538
|
+
)
|
|
539
|
+
)
|
|
540
|
+
if self._tablespace:
|
|
541
|
+
props.append(exp.Property(this=exp.to_identifier("TABLESPACE"), value=exp.to_identifier(self._tablespace)))
|
|
542
|
+
if self._partition_by:
|
|
543
|
+
props.append(exp.Property(this=exp.to_identifier("PARTITION BY"), value=exp.convert(self._partition_by)))
|
|
544
|
+
|
|
545
|
+
for key, value in self._table_options.items():
|
|
546
|
+
if key != "engine":
|
|
547
|
+
props.append(exp.Property(this=exp.to_identifier(key.upper()), value=exp.convert(value)))
|
|
548
|
+
|
|
549
|
+
properties_node = exp.Properties(expressions=props) if props else None
|
|
550
|
+
|
|
551
|
+
if self._schema:
|
|
552
|
+
table_identifier = exp.Table(this=exp.to_identifier(self._table_name), db=exp.to_identifier(self._schema))
|
|
553
|
+
else:
|
|
554
|
+
table_identifier = exp.Table(this=exp.to_identifier(self._table_name))
|
|
555
|
+
|
|
556
|
+
schema_expr = exp.Schema(this=table_identifier, expressions=column_defs)
|
|
557
|
+
|
|
558
|
+
like_expr = None
|
|
559
|
+
if self._like_table:
|
|
560
|
+
like_expr = exp.to_table(self._like_table)
|
|
561
|
+
|
|
562
|
+
return exp.Create(
|
|
563
|
+
kind="TABLE",
|
|
564
|
+
this=schema_expr,
|
|
565
|
+
exists=self._if_not_exists,
|
|
566
|
+
temporary=self._temporary,
|
|
567
|
+
properties=properties_node,
|
|
568
|
+
like=like_expr,
|
|
569
|
+
)
|
|
570
|
+
|
|
571
|
+
def _has_primary_key_constraint(self) -> bool:
|
|
572
|
+
"""Check if table already has a primary key constraint."""
|
|
573
|
+
return any(c.constraint_type == CONSTRAINT_TYPE_PRIMARY_KEY for c in self._constraints)
|
|
574
|
+
|
|
575
|
+
def _find_primary_key_constraint(self) -> "ConstraintDefinition | None":
|
|
576
|
+
"""Find existing primary key constraint."""
|
|
577
|
+
return next((c for c in self._constraints if c.constraint_type == CONSTRAINT_TYPE_PRIMARY_KEY), None)
|
|
578
|
+
|
|
579
|
+
def _validate_foreign_key_action(self, action: "str | None", action_type: str) -> None:
|
|
580
|
+
"""Validate foreign key action (ON DELETE or ON UPDATE)."""
|
|
581
|
+
if action and action.upper() not in VALID_FOREIGN_KEY_ACTIONS:
|
|
582
|
+
self._raise_sql_builder_error(f"Invalid {action_type} action: {action}")
|
|
583
|
+
|
|
584
|
+
def _is_redundant_single_column_primary_key(self, constraint: "ConstraintDefinition") -> bool:
|
|
585
|
+
"""Check if constraint is a redundant single-column primary key."""
|
|
586
|
+
if constraint.constraint_type != CONSTRAINT_TYPE_PRIMARY_KEY or len(constraint.columns) != 1:
|
|
587
|
+
return False
|
|
588
|
+
|
|
589
|
+
col_name = constraint.columns[0]
|
|
590
|
+
return any(c.name == col_name and c.primary_key for c in self._columns)
|
|
591
|
+
|
|
592
|
+
|
|
593
|
+
class DropTable(DDLBuilder):
|
|
594
|
+
"""Builder for DROP TABLE [IF EXISTS] ... [CASCADE|RESTRICT]."""
|
|
595
|
+
|
|
596
|
+
__slots__ = ("_cascade", "_if_exists", "_table_name")
|
|
597
|
+
|
|
598
|
+
def __init__(self, table_name: str, dialect: "DialectType" = None) -> None:
|
|
599
|
+
"""Initialize DROP TABLE with table name.
|
|
600
|
+
|
|
601
|
+
Args:
|
|
602
|
+
table_name: Name of the table to drop
|
|
603
|
+
dialect: SQL dialect to use
|
|
604
|
+
"""
|
|
605
|
+
super().__init__(dialect=dialect)
|
|
606
|
+
self._table_name = table_name
|
|
607
|
+
self._if_exists = False
|
|
608
|
+
self._cascade: bool | None = None
|
|
609
|
+
|
|
610
|
+
def table(self, name: str) -> Self:
|
|
611
|
+
self._table_name = name
|
|
612
|
+
return self
|
|
613
|
+
|
|
614
|
+
def if_exists(self) -> Self:
|
|
615
|
+
self._if_exists = True
|
|
616
|
+
return self
|
|
617
|
+
|
|
618
|
+
def cascade(self) -> Self:
|
|
619
|
+
self._cascade = True
|
|
620
|
+
return self
|
|
621
|
+
|
|
622
|
+
def restrict(self) -> Self:
|
|
623
|
+
self._cascade = False
|
|
624
|
+
return self
|
|
625
|
+
|
|
626
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
627
|
+
if not self._table_name:
|
|
628
|
+
self._raise_sql_builder_error("Table name must be set for DROP TABLE.")
|
|
629
|
+
return exp.Drop(
|
|
630
|
+
kind="TABLE", this=exp.to_table(self._table_name), exists=self._if_exists, cascade=self._cascade
|
|
631
|
+
)
|
|
632
|
+
|
|
633
|
+
|
|
634
|
+
class DropIndex(DDLBuilder):
|
|
635
|
+
"""Builder for DROP INDEX [IF EXISTS] ... [ON table] [CASCADE|RESTRICT]."""
|
|
636
|
+
|
|
637
|
+
__slots__ = ("_cascade", "_if_exists", "_index_name", "_table_name")
|
|
638
|
+
|
|
639
|
+
def __init__(self, index_name: str, dialect: "DialectType" = None) -> None:
|
|
640
|
+
"""Initialize DROP INDEX with index name.
|
|
641
|
+
|
|
642
|
+
Args:
|
|
643
|
+
index_name: Name of the index to drop
|
|
644
|
+
dialect: SQL dialect to use
|
|
645
|
+
"""
|
|
646
|
+
super().__init__(dialect=dialect)
|
|
647
|
+
self._index_name = index_name
|
|
648
|
+
self._table_name: str | None = None
|
|
649
|
+
self._if_exists = False
|
|
650
|
+
self._cascade: bool | None = None
|
|
651
|
+
|
|
652
|
+
def name(self, index_name: str) -> Self:
|
|
653
|
+
self._index_name = index_name
|
|
654
|
+
return self
|
|
655
|
+
|
|
656
|
+
def on_table(self, table_name: str) -> Self:
|
|
657
|
+
self._table_name = table_name
|
|
658
|
+
return self
|
|
659
|
+
|
|
660
|
+
def if_exists(self) -> Self:
|
|
661
|
+
self._if_exists = True
|
|
662
|
+
return self
|
|
663
|
+
|
|
664
|
+
def cascade(self) -> Self:
|
|
665
|
+
self._cascade = True
|
|
666
|
+
return self
|
|
667
|
+
|
|
668
|
+
def restrict(self) -> Self:
|
|
669
|
+
self._cascade = False
|
|
670
|
+
return self
|
|
671
|
+
|
|
672
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
673
|
+
if not self._index_name:
|
|
674
|
+
self._raise_sql_builder_error("Index name must be set for DROP INDEX.")
|
|
675
|
+
return exp.Drop(
|
|
676
|
+
kind="INDEX",
|
|
677
|
+
this=exp.to_identifier(self._index_name),
|
|
678
|
+
table=exp.to_table(self._table_name) if self._table_name else None,
|
|
679
|
+
exists=self._if_exists,
|
|
680
|
+
cascade=self._cascade,
|
|
681
|
+
)
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
class DropView(DDLBuilder):
|
|
685
|
+
"""Builder for DROP VIEW [IF EXISTS] ... [CASCADE|RESTRICT]."""
|
|
686
|
+
|
|
687
|
+
__slots__ = ("_cascade", "_if_exists", "_view_name")
|
|
688
|
+
|
|
689
|
+
def __init__(self, view_name: str, dialect: "DialectType" = None) -> None:
|
|
690
|
+
"""Initialize DROP VIEW with view name.
|
|
691
|
+
|
|
692
|
+
Args:
|
|
693
|
+
view_name: Name of the view to drop
|
|
694
|
+
dialect: SQL dialect to use
|
|
695
|
+
"""
|
|
696
|
+
super().__init__(dialect=dialect)
|
|
697
|
+
self._view_name = view_name
|
|
698
|
+
self._if_exists = False
|
|
699
|
+
self._cascade: bool | None = None
|
|
700
|
+
|
|
701
|
+
def name(self, view_name: str) -> Self:
|
|
702
|
+
self._view_name = view_name
|
|
703
|
+
return self
|
|
704
|
+
|
|
705
|
+
def if_exists(self) -> Self:
|
|
706
|
+
self._if_exists = True
|
|
707
|
+
return self
|
|
708
|
+
|
|
709
|
+
def cascade(self) -> Self:
|
|
710
|
+
self._cascade = True
|
|
711
|
+
return self
|
|
712
|
+
|
|
713
|
+
def restrict(self) -> Self:
|
|
714
|
+
self._cascade = False
|
|
715
|
+
return self
|
|
716
|
+
|
|
717
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
718
|
+
if not self._view_name:
|
|
719
|
+
self._raise_sql_builder_error("View name must be set for DROP VIEW.")
|
|
720
|
+
return exp.Drop(
|
|
721
|
+
kind="VIEW", this=exp.to_identifier(self._view_name), exists=self._if_exists, cascade=self._cascade
|
|
722
|
+
)
|
|
723
|
+
|
|
724
|
+
|
|
725
|
+
class DropSchema(DDLBuilder):
|
|
726
|
+
"""Builder for DROP SCHEMA [IF EXISTS] ... [CASCADE|RESTRICT]."""
|
|
727
|
+
|
|
728
|
+
__slots__ = ("_cascade", "_if_exists", "_schema_name")
|
|
729
|
+
|
|
730
|
+
def __init__(self, schema_name: str, dialect: "DialectType" = None) -> None:
|
|
731
|
+
"""Initialize DROP SCHEMA with schema name.
|
|
732
|
+
|
|
733
|
+
Args:
|
|
734
|
+
schema_name: Name of the schema to drop
|
|
735
|
+
dialect: SQL dialect to use
|
|
736
|
+
"""
|
|
737
|
+
super().__init__(dialect=dialect)
|
|
738
|
+
self._schema_name = schema_name
|
|
739
|
+
self._if_exists = False
|
|
740
|
+
self._cascade: bool | None = None
|
|
741
|
+
|
|
742
|
+
def name(self, schema_name: str) -> Self:
|
|
743
|
+
self._schema_name = schema_name
|
|
744
|
+
return self
|
|
745
|
+
|
|
746
|
+
def if_exists(self) -> Self:
|
|
747
|
+
self._if_exists = True
|
|
748
|
+
return self
|
|
749
|
+
|
|
750
|
+
def cascade(self) -> Self:
|
|
751
|
+
self._cascade = True
|
|
752
|
+
return self
|
|
753
|
+
|
|
754
|
+
def restrict(self) -> Self:
|
|
755
|
+
self._cascade = False
|
|
756
|
+
return self
|
|
757
|
+
|
|
758
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
759
|
+
if not self._schema_name:
|
|
760
|
+
self._raise_sql_builder_error("Schema name must be set for DROP SCHEMA.")
|
|
761
|
+
return exp.Drop(
|
|
762
|
+
kind="SCHEMA", this=exp.to_identifier(self._schema_name), exists=self._if_exists, cascade=self._cascade
|
|
763
|
+
)
|
|
764
|
+
|
|
765
|
+
|
|
766
|
+
class CreateIndex(DDLBuilder):
|
|
767
|
+
"""Builder for CREATE [UNIQUE] INDEX [IF NOT EXISTS] ... ON ... (...)."""
|
|
768
|
+
|
|
769
|
+
__slots__ = ("_columns", "_if_not_exists", "_index_name", "_table_name", "_unique", "_using", "_where")
|
|
770
|
+
|
|
771
|
+
def __init__(self, index_name: str, dialect: "DialectType" = None) -> None:
|
|
772
|
+
"""Initialize CREATE INDEX with index name.
|
|
773
|
+
|
|
774
|
+
Args:
|
|
775
|
+
index_name: Name of the index to create
|
|
776
|
+
dialect: SQL dialect to use
|
|
777
|
+
"""
|
|
778
|
+
super().__init__(dialect=dialect)
|
|
779
|
+
self._index_name = index_name
|
|
780
|
+
self._table_name: str | None = None
|
|
781
|
+
self._columns: list[str | exp.Ordered | exp.Expression] = []
|
|
782
|
+
self._unique = False
|
|
783
|
+
self._if_not_exists = False
|
|
784
|
+
self._using: str | None = None
|
|
785
|
+
self._where: str | exp.Expression | None = None
|
|
786
|
+
|
|
787
|
+
def name(self, index_name: str) -> Self:
|
|
788
|
+
self._index_name = index_name
|
|
789
|
+
return self
|
|
790
|
+
|
|
791
|
+
def on_table(self, table_name: str) -> Self:
|
|
792
|
+
self._table_name = table_name
|
|
793
|
+
return self
|
|
794
|
+
|
|
795
|
+
def columns(self, *cols: str | exp.Ordered | exp.Expression) -> Self:
|
|
796
|
+
self._columns.extend(cols)
|
|
797
|
+
return self
|
|
798
|
+
|
|
799
|
+
def expressions(self, *exprs: str | exp.Expression) -> Self:
|
|
800
|
+
self._columns.extend(exprs)
|
|
801
|
+
return self
|
|
802
|
+
|
|
803
|
+
def unique(self) -> Self:
|
|
804
|
+
self._unique = True
|
|
805
|
+
return self
|
|
806
|
+
|
|
807
|
+
def if_not_exists(self) -> Self:
|
|
808
|
+
self._if_not_exists = True
|
|
809
|
+
return self
|
|
810
|
+
|
|
811
|
+
def using(self, method: str) -> Self:
|
|
812
|
+
self._using = method
|
|
813
|
+
return self
|
|
814
|
+
|
|
815
|
+
def where(self, condition: str | exp.Expression) -> Self:
|
|
816
|
+
self._where = condition
|
|
817
|
+
return self
|
|
818
|
+
|
|
819
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
820
|
+
"""Build the CREATE INDEX expression used by this builder.
|
|
821
|
+
|
|
822
|
+
Columns are turned into raw expressions (not ``Ordered``) to preserve natural NULL ordering,
|
|
823
|
+
string ``where`` clauses become expressions, and the final ``exp.Index`` is wrapped in an ``exp.Create`` with the configured flags.
|
|
824
|
+
"""
|
|
825
|
+
if not self._index_name or not self._table_name:
|
|
826
|
+
self._raise_sql_builder_error("Index name and table name must be set for CREATE INDEX.")
|
|
827
|
+
|
|
828
|
+
cols: list[exp.Expression] = []
|
|
829
|
+
for col in self._columns:
|
|
830
|
+
if isinstance(col, str):
|
|
831
|
+
cols.append(exp.column(col))
|
|
832
|
+
else:
|
|
833
|
+
cols.append(col)
|
|
834
|
+
|
|
835
|
+
where_expr = None
|
|
836
|
+
if self._where:
|
|
837
|
+
where_expr = exp.condition(self._where) if isinstance(self._where, str) else self._where
|
|
838
|
+
|
|
839
|
+
index_params = exp.IndexParameters(columns=cols) if cols else None
|
|
840
|
+
|
|
841
|
+
index_expr = exp.Index(
|
|
842
|
+
this=exp.to_identifier(self._index_name), table=exp.to_table(self._table_name), params=index_params
|
|
843
|
+
)
|
|
844
|
+
|
|
845
|
+
if where_expr:
|
|
846
|
+
index_expr.set("where", where_expr)
|
|
847
|
+
|
|
848
|
+
return exp.Create(kind="INDEX", this=index_expr, unique=self._unique, exists=self._if_not_exists)
|
|
849
|
+
|
|
850
|
+
|
|
851
|
+
class Truncate(DDLBuilder):
|
|
852
|
+
"""Builder for TRUNCATE TABLE ... [CASCADE|RESTRICT] [RESTART IDENTITY|CONTINUE IDENTITY]."""
|
|
853
|
+
|
|
854
|
+
__slots__ = ("_cascade", "_identity", "_table_name")
|
|
855
|
+
|
|
856
|
+
def __init__(self, table_name: str, dialect: "DialectType" = None) -> None:
|
|
857
|
+
"""Initialize TRUNCATE with table name.
|
|
858
|
+
|
|
859
|
+
Args:
|
|
860
|
+
table_name: Name of the table to truncate
|
|
861
|
+
dialect: SQL dialect to use
|
|
862
|
+
"""
|
|
863
|
+
super().__init__(dialect=dialect)
|
|
864
|
+
self._table_name = table_name
|
|
865
|
+
self._cascade: bool | None = None
|
|
866
|
+
self._identity: str | None = None
|
|
867
|
+
|
|
868
|
+
def table(self, name: str) -> Self:
|
|
869
|
+
self._table_name = name
|
|
870
|
+
return self
|
|
871
|
+
|
|
872
|
+
def cascade(self) -> Self:
|
|
873
|
+
self._cascade = True
|
|
874
|
+
return self
|
|
875
|
+
|
|
876
|
+
def restrict(self) -> Self:
|
|
877
|
+
self._cascade = False
|
|
878
|
+
return self
|
|
879
|
+
|
|
880
|
+
def restart_identity(self) -> Self:
|
|
881
|
+
self._identity = "RESTART"
|
|
882
|
+
return self
|
|
883
|
+
|
|
884
|
+
def continue_identity(self) -> Self:
|
|
885
|
+
self._identity = "CONTINUE"
|
|
886
|
+
return self
|
|
887
|
+
|
|
888
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
889
|
+
if not self._table_name:
|
|
890
|
+
self._raise_sql_builder_error("Table name must be set for TRUNCATE TABLE.")
|
|
891
|
+
identity_expr = exp.Var(this=self._identity) if self._identity else None
|
|
892
|
+
return exp.TruncateTable(this=exp.to_table(self._table_name), cascade=self._cascade, identity=identity_expr)
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
class AlterOperation:
|
|
896
|
+
"""Represents a single ALTER TABLE operation."""
|
|
897
|
+
|
|
898
|
+
__slots__ = (
|
|
899
|
+
"after_column",
|
|
900
|
+
"column_definition",
|
|
901
|
+
"column_name",
|
|
902
|
+
"constraint_definition",
|
|
903
|
+
"constraint_name",
|
|
904
|
+
"first",
|
|
905
|
+
"new_name",
|
|
906
|
+
"new_type",
|
|
907
|
+
"operation_type",
|
|
908
|
+
"using_expression",
|
|
909
|
+
)
|
|
910
|
+
|
|
911
|
+
def __init__(
|
|
912
|
+
self,
|
|
913
|
+
operation_type: str,
|
|
914
|
+
column_name: "str | None" = None,
|
|
915
|
+
column_definition: "ColumnDefinition | None" = None,
|
|
916
|
+
constraint_name: "str | None" = None,
|
|
917
|
+
constraint_definition: "ConstraintDefinition | None" = None,
|
|
918
|
+
new_type: "str | None" = None,
|
|
919
|
+
new_name: "str | None" = None,
|
|
920
|
+
after_column: "str | None" = None,
|
|
921
|
+
first: bool = False,
|
|
922
|
+
using_expression: "str | None" = None,
|
|
923
|
+
) -> None:
|
|
924
|
+
self.operation_type = operation_type
|
|
925
|
+
self.column_name = column_name
|
|
926
|
+
self.column_definition = column_definition
|
|
927
|
+
self.constraint_name = constraint_name
|
|
928
|
+
self.constraint_definition = constraint_definition
|
|
929
|
+
self.new_type = new_type
|
|
930
|
+
self.new_name = new_name
|
|
931
|
+
self.after_column = after_column
|
|
932
|
+
self.first = first
|
|
933
|
+
self.using_expression = using_expression
|
|
934
|
+
|
|
935
|
+
|
|
936
|
+
class CreateSchema(DDLBuilder):
|
|
937
|
+
"""Builder for CREATE SCHEMA [IF NOT EXISTS] schema_name [AUTHORIZATION user_name]."""
|
|
938
|
+
|
|
939
|
+
__slots__ = ("_authorization", "_if_not_exists", "_schema_name")
|
|
940
|
+
|
|
941
|
+
def __init__(self, schema_name: str, dialect: "DialectType" = None) -> None:
|
|
942
|
+
"""Initialize CREATE SCHEMA with schema name.
|
|
943
|
+
|
|
944
|
+
Args:
|
|
945
|
+
schema_name: Name of the schema to create
|
|
946
|
+
dialect: SQL dialect to use
|
|
947
|
+
"""
|
|
948
|
+
super().__init__(dialect=dialect)
|
|
949
|
+
self._schema_name = schema_name
|
|
950
|
+
self._if_not_exists = False
|
|
951
|
+
self._authorization: str | None = None
|
|
952
|
+
|
|
953
|
+
def name(self, schema_name: str) -> Self:
|
|
954
|
+
self._schema_name = schema_name
|
|
955
|
+
return self
|
|
956
|
+
|
|
957
|
+
def if_not_exists(self) -> Self:
|
|
958
|
+
self._if_not_exists = True
|
|
959
|
+
return self
|
|
960
|
+
|
|
961
|
+
def authorization(self, user_name: str) -> Self:
|
|
962
|
+
self._authorization = user_name
|
|
963
|
+
return self
|
|
964
|
+
|
|
965
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
966
|
+
if not self._schema_name:
|
|
967
|
+
self._raise_sql_builder_error("Schema name must be set for CREATE SCHEMA.")
|
|
968
|
+
props: list[exp.Property] = []
|
|
969
|
+
if self._authorization:
|
|
970
|
+
props.append(
|
|
971
|
+
exp.Property(this=exp.to_identifier("AUTHORIZATION"), value=exp.to_identifier(self._authorization))
|
|
972
|
+
)
|
|
973
|
+
properties_node = exp.Properties(expressions=props) if props else None
|
|
974
|
+
return exp.Create(
|
|
975
|
+
kind="SCHEMA",
|
|
976
|
+
this=exp.to_identifier(self._schema_name),
|
|
977
|
+
exists=self._if_not_exists,
|
|
978
|
+
properties=properties_node,
|
|
979
|
+
)
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
class CreateTableAsSelect(DDLBuilder):
|
|
983
|
+
"""Builder for CREATE TABLE [IF NOT EXISTS] ... AS SELECT ... (CTAS).
|
|
984
|
+
|
|
985
|
+
Example:
|
|
986
|
+
builder = (
|
|
987
|
+
CreateTableAsSelectBuilder()
|
|
988
|
+
.name("my_table")
|
|
989
|
+
.if_not_exists()
|
|
990
|
+
.columns("id", "name")
|
|
991
|
+
.as_select(select_builder)
|
|
992
|
+
)
|
|
993
|
+
sql = builder.build().sql
|
|
994
|
+
|
|
995
|
+
Methods:
|
|
996
|
+
- name(table_name: str): Set the table name.
|
|
997
|
+
- if_not_exists(): Add IF NOT EXISTS.
|
|
998
|
+
- columns(*cols: str): Set explicit column list (optional).
|
|
999
|
+
- as_select(select_query): Set the SELECT source (SQL, SelectBuilder, or str).
|
|
1000
|
+
"""
|
|
1001
|
+
|
|
1002
|
+
__slots__ = ("_columns", "_if_not_exists", "_select_query", "_table_name")
|
|
1003
|
+
|
|
1004
|
+
def __init__(self, dialect: "DialectType" = None) -> None:
|
|
1005
|
+
super().__init__(dialect=dialect)
|
|
1006
|
+
self._table_name: str | None = None
|
|
1007
|
+
self._if_not_exists = False
|
|
1008
|
+
self._columns: list[str] = []
|
|
1009
|
+
self._select_query: object | None = None
|
|
1010
|
+
|
|
1011
|
+
def name(self, table_name: str) -> Self:
|
|
1012
|
+
self._table_name = table_name
|
|
1013
|
+
return self
|
|
1014
|
+
|
|
1015
|
+
def if_not_exists(self) -> Self:
|
|
1016
|
+
self._if_not_exists = True
|
|
1017
|
+
return self
|
|
1018
|
+
|
|
1019
|
+
def columns(self, *cols: str) -> Self:
|
|
1020
|
+
self._columns = list(cols)
|
|
1021
|
+
return self
|
|
1022
|
+
|
|
1023
|
+
def as_select(self, select_query: "str | exp.Expression") -> Self:
|
|
1024
|
+
self._select_query = select_query
|
|
1025
|
+
return self
|
|
1026
|
+
|
|
1027
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
1028
|
+
if not self._table_name:
|
|
1029
|
+
self._raise_sql_builder_error("Table name must be set for CREATE TABLE AS SELECT.")
|
|
1030
|
+
if self._select_query is None:
|
|
1031
|
+
self._raise_sql_builder_error("SELECT query must be set for CREATE TABLE AS SELECT.")
|
|
1032
|
+
|
|
1033
|
+
select_expr = None
|
|
1034
|
+
select_parameters = None
|
|
1035
|
+
|
|
1036
|
+
if isinstance(self._select_query, SQL):
|
|
1037
|
+
select_expr = self._select_query.expression
|
|
1038
|
+
select_parameters = self._select_query.parameters
|
|
1039
|
+
elif isinstance(self._select_query, Select):
|
|
1040
|
+
select_expr = self._select_query.get_expression()
|
|
1041
|
+
select_parameters = self._select_query.parameters
|
|
1042
|
+
|
|
1043
|
+
with_ctes = self._select_query.with_ctes
|
|
1044
|
+
if with_ctes and select_expr and isinstance(select_expr, exp.Select):
|
|
1045
|
+
for alias, cte in with_ctes.items():
|
|
1046
|
+
if has_with_method(select_expr):
|
|
1047
|
+
select_expr = select_expr.with_(cte.this, as_=alias, copy=False)
|
|
1048
|
+
elif isinstance(self._select_query, str):
|
|
1049
|
+
select_expr = exp.maybe_parse(self._select_query)
|
|
1050
|
+
select_parameters = None
|
|
1051
|
+
else:
|
|
1052
|
+
self._raise_sql_builder_error("Unsupported type for SELECT query in CTAS.")
|
|
1053
|
+
if select_expr is None:
|
|
1054
|
+
self._raise_sql_builder_error("SELECT query must be a valid SELECT expression.")
|
|
1055
|
+
|
|
1056
|
+
if select_parameters:
|
|
1057
|
+
for p_name, p_value in select_parameters.items():
|
|
1058
|
+
self._parameters[p_name] = p_value
|
|
1059
|
+
|
|
1060
|
+
schema_expr = None
|
|
1061
|
+
if self._columns:
|
|
1062
|
+
schema_expr = exp.Schema(expressions=[exp.column(c) for c in self._columns])
|
|
1063
|
+
|
|
1064
|
+
return exp.Create(
|
|
1065
|
+
kind="TABLE",
|
|
1066
|
+
this=exp.to_table(self._table_name),
|
|
1067
|
+
exists=self._if_not_exists,
|
|
1068
|
+
expression=select_expr,
|
|
1069
|
+
schema=schema_expr,
|
|
1070
|
+
)
|
|
1071
|
+
|
|
1072
|
+
|
|
1073
|
+
class CreateMaterializedView(DDLBuilder):
|
|
1074
|
+
"""Builder for CREATE MATERIALIZED VIEW [IF NOT EXISTS] ... AS SELECT ..."""
|
|
1075
|
+
|
|
1076
|
+
__slots__ = (
|
|
1077
|
+
"_columns",
|
|
1078
|
+
"_hints",
|
|
1079
|
+
"_if_not_exists",
|
|
1080
|
+
"_refresh_mode",
|
|
1081
|
+
"_select_query",
|
|
1082
|
+
"_storage_parameters",
|
|
1083
|
+
"_tablespace",
|
|
1084
|
+
"_using_index",
|
|
1085
|
+
"_view_name",
|
|
1086
|
+
"_with_data",
|
|
1087
|
+
)
|
|
1088
|
+
|
|
1089
|
+
def __init__(self, view_name: str, dialect: "DialectType" = None) -> None:
|
|
1090
|
+
"""Initialize CREATE MATERIALIZED VIEW with view name.
|
|
1091
|
+
|
|
1092
|
+
Args:
|
|
1093
|
+
view_name: Name of the materialized view to create
|
|
1094
|
+
dialect: SQL dialect to use
|
|
1095
|
+
"""
|
|
1096
|
+
super().__init__(dialect=dialect)
|
|
1097
|
+
self._view_name = view_name
|
|
1098
|
+
self._if_not_exists = False
|
|
1099
|
+
self._columns: list[str] = []
|
|
1100
|
+
self._select_query: str | exp.Expression | None = None
|
|
1101
|
+
self._with_data: bool | None = None
|
|
1102
|
+
self._refresh_mode: str | None = None
|
|
1103
|
+
self._storage_parameters: dict[str, Any] = {}
|
|
1104
|
+
self._tablespace: str | None = None
|
|
1105
|
+
self._using_index: str | None = None
|
|
1106
|
+
self._hints: list[str] = []
|
|
1107
|
+
|
|
1108
|
+
def name(self, view_name: str) -> Self:
|
|
1109
|
+
self._view_name = view_name
|
|
1110
|
+
return self
|
|
1111
|
+
|
|
1112
|
+
def if_not_exists(self) -> Self:
|
|
1113
|
+
self._if_not_exists = True
|
|
1114
|
+
return self
|
|
1115
|
+
|
|
1116
|
+
def columns(self, *cols: str) -> Self:
|
|
1117
|
+
self._columns = list(cols)
|
|
1118
|
+
return self
|
|
1119
|
+
|
|
1120
|
+
def as_select(self, select_query: "str | exp.Expression") -> Self:
|
|
1121
|
+
self._select_query = select_query
|
|
1122
|
+
return self
|
|
1123
|
+
|
|
1124
|
+
def with_data(self) -> Self:
|
|
1125
|
+
self._with_data = True
|
|
1126
|
+
return self
|
|
1127
|
+
|
|
1128
|
+
def no_data(self) -> Self:
|
|
1129
|
+
self._with_data = False
|
|
1130
|
+
return self
|
|
1131
|
+
|
|
1132
|
+
def refresh_mode(self, mode: str) -> Self:
|
|
1133
|
+
self._refresh_mode = mode
|
|
1134
|
+
return self
|
|
1135
|
+
|
|
1136
|
+
def storage_parameter(self, key: str, value: Any) -> Self:
|
|
1137
|
+
self._storage_parameters[key] = value
|
|
1138
|
+
return self
|
|
1139
|
+
|
|
1140
|
+
def tablespace(self, name: str) -> Self:
|
|
1141
|
+
self._tablespace = name
|
|
1142
|
+
return self
|
|
1143
|
+
|
|
1144
|
+
def using_index(self, index_name: str) -> Self:
|
|
1145
|
+
self._using_index = index_name
|
|
1146
|
+
return self
|
|
1147
|
+
|
|
1148
|
+
def with_hint(self, hint: str) -> Self:
|
|
1149
|
+
self._hints.append(hint)
|
|
1150
|
+
return self
|
|
1151
|
+
|
|
1152
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
1153
|
+
if not self._view_name:
|
|
1154
|
+
self._raise_sql_builder_error("View name must be set for CREATE MATERIALIZED VIEW.")
|
|
1155
|
+
if self._select_query is None:
|
|
1156
|
+
self._raise_sql_builder_error("SELECT query must be set for CREATE MATERIALIZED VIEW.")
|
|
1157
|
+
|
|
1158
|
+
select_expr: exp.Expression | None = None
|
|
1159
|
+
select_parameters: dict[str, Any] | None = None
|
|
1160
|
+
|
|
1161
|
+
if isinstance(self._select_query, SQL):
|
|
1162
|
+
select_expr = self._select_query.expression
|
|
1163
|
+
select_parameters = self._select_query.parameters
|
|
1164
|
+
elif isinstance(self._select_query, Select):
|
|
1165
|
+
select_expr = self._select_query.get_expression()
|
|
1166
|
+
select_parameters = self._select_query.parameters
|
|
1167
|
+
elif isinstance(self._select_query, str):
|
|
1168
|
+
select_expr = exp.maybe_parse(self._select_query)
|
|
1169
|
+
select_parameters = None
|
|
1170
|
+
else:
|
|
1171
|
+
self._raise_sql_builder_error("Unsupported type for SELECT query in materialized view.")
|
|
1172
|
+
if select_expr is None or not isinstance(select_expr, exp.Select):
|
|
1173
|
+
self._raise_sql_builder_error("SELECT query must be a valid SELECT expression.")
|
|
1174
|
+
|
|
1175
|
+
if select_parameters:
|
|
1176
|
+
for p_name, p_value in select_parameters.items():
|
|
1177
|
+
self._parameters[p_name] = p_value
|
|
1178
|
+
|
|
1179
|
+
schema_expr = None
|
|
1180
|
+
if self._columns:
|
|
1181
|
+
schema_expr = exp.Schema(expressions=[exp.column(c) for c in self._columns])
|
|
1182
|
+
|
|
1183
|
+
props: list[exp.Property] = []
|
|
1184
|
+
if self._refresh_mode:
|
|
1185
|
+
props.append(exp.Property(this=exp.to_identifier("REFRESH_MODE"), value=exp.convert(self._refresh_mode)))
|
|
1186
|
+
if self._tablespace:
|
|
1187
|
+
props.append(exp.Property(this=exp.to_identifier("TABLESPACE"), value=exp.to_identifier(self._tablespace)))
|
|
1188
|
+
if self._using_index:
|
|
1189
|
+
props.append(
|
|
1190
|
+
exp.Property(this=exp.to_identifier("USING_INDEX"), value=exp.to_identifier(self._using_index))
|
|
1191
|
+
)
|
|
1192
|
+
for k, v in self._storage_parameters.items():
|
|
1193
|
+
props.append(exp.Property(this=exp.to_identifier(k), value=exp.convert(str(v))))
|
|
1194
|
+
if self._with_data is not None:
|
|
1195
|
+
props.append(exp.Property(this=exp.to_identifier("WITH_DATA" if self._with_data else "NO_DATA")))
|
|
1196
|
+
props.extend(exp.Property(this=exp.to_identifier("HINT"), value=exp.convert(hint)) for hint in self._hints)
|
|
1197
|
+
properties_node = exp.Properties(expressions=props) if props else None
|
|
1198
|
+
|
|
1199
|
+
return exp.Create(
|
|
1200
|
+
kind="MATERIALIZED_VIEW",
|
|
1201
|
+
this=exp.to_identifier(self._view_name),
|
|
1202
|
+
exists=self._if_not_exists,
|
|
1203
|
+
expression=select_expr,
|
|
1204
|
+
schema=schema_expr,
|
|
1205
|
+
properties=properties_node,
|
|
1206
|
+
)
|
|
1207
|
+
|
|
1208
|
+
|
|
1209
|
+
class CreateView(DDLBuilder):
|
|
1210
|
+
"""Builder for CREATE VIEW [IF NOT EXISTS] ... AS SELECT ..."""
|
|
1211
|
+
|
|
1212
|
+
__slots__ = ("_columns", "_hints", "_if_not_exists", "_select_query", "_view_name")
|
|
1213
|
+
|
|
1214
|
+
def __init__(self, view_name: str, dialect: "DialectType" = None) -> None:
|
|
1215
|
+
"""Initialize CREATE VIEW with view name.
|
|
1216
|
+
|
|
1217
|
+
Args:
|
|
1218
|
+
view_name: Name of the view to create
|
|
1219
|
+
dialect: SQL dialect to use
|
|
1220
|
+
"""
|
|
1221
|
+
super().__init__(dialect=dialect)
|
|
1222
|
+
self._view_name = view_name
|
|
1223
|
+
self._if_not_exists = False
|
|
1224
|
+
self._columns: list[str] = []
|
|
1225
|
+
self._select_query: str | exp.Expression | None = None
|
|
1226
|
+
self._hints: list[str] = []
|
|
1227
|
+
|
|
1228
|
+
def name(self, view_name: str) -> Self:
|
|
1229
|
+
self._view_name = view_name
|
|
1230
|
+
return self
|
|
1231
|
+
|
|
1232
|
+
def if_not_exists(self) -> Self:
|
|
1233
|
+
self._if_not_exists = True
|
|
1234
|
+
return self
|
|
1235
|
+
|
|
1236
|
+
def columns(self, *cols: str) -> Self:
|
|
1237
|
+
self._columns = list(cols)
|
|
1238
|
+
return self
|
|
1239
|
+
|
|
1240
|
+
def as_select(self, select_query: "str | exp.Expression") -> Self:
|
|
1241
|
+
self._select_query = select_query
|
|
1242
|
+
return self
|
|
1243
|
+
|
|
1244
|
+
def with_hint(self, hint: str) -> Self:
|
|
1245
|
+
self._hints.append(hint)
|
|
1246
|
+
return self
|
|
1247
|
+
|
|
1248
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
1249
|
+
if not self._view_name:
|
|
1250
|
+
self._raise_sql_builder_error("View name must be set for CREATE VIEW.")
|
|
1251
|
+
if self._select_query is None:
|
|
1252
|
+
self._raise_sql_builder_error("SELECT query must be set for CREATE VIEW.")
|
|
1253
|
+
|
|
1254
|
+
select_expr: exp.Expression | None = None
|
|
1255
|
+
select_parameters: dict[str, Any] | None = None
|
|
1256
|
+
|
|
1257
|
+
if isinstance(self._select_query, SQL):
|
|
1258
|
+
select_expr = self._select_query.expression
|
|
1259
|
+
select_parameters = self._select_query.parameters
|
|
1260
|
+
elif isinstance(self._select_query, Select):
|
|
1261
|
+
select_expr = self._select_query.get_expression()
|
|
1262
|
+
select_parameters = self._select_query.parameters
|
|
1263
|
+
elif isinstance(self._select_query, str):
|
|
1264
|
+
select_expr = exp.maybe_parse(self._select_query)
|
|
1265
|
+
select_parameters = None
|
|
1266
|
+
else:
|
|
1267
|
+
self._raise_sql_builder_error("Unsupported type for SELECT query in view.")
|
|
1268
|
+
if select_expr is None or not isinstance(select_expr, exp.Select):
|
|
1269
|
+
self._raise_sql_builder_error("SELECT query must be a valid SELECT expression.")
|
|
1270
|
+
|
|
1271
|
+
if select_parameters:
|
|
1272
|
+
for p_name, p_value in select_parameters.items():
|
|
1273
|
+
self._parameters[p_name] = p_value
|
|
1274
|
+
|
|
1275
|
+
schema_expr = None
|
|
1276
|
+
if self._columns:
|
|
1277
|
+
schema_expr = exp.Schema(expressions=[exp.column(c) for c in self._columns])
|
|
1278
|
+
|
|
1279
|
+
props: list[exp.Property] = [
|
|
1280
|
+
exp.Property(this=exp.to_identifier("HINT"), value=exp.convert(h)) for h in self._hints
|
|
1281
|
+
]
|
|
1282
|
+
properties_node = exp.Properties(expressions=props) if props else None
|
|
1283
|
+
|
|
1284
|
+
return exp.Create(
|
|
1285
|
+
kind="VIEW",
|
|
1286
|
+
this=exp.to_identifier(self._view_name),
|
|
1287
|
+
exists=self._if_not_exists,
|
|
1288
|
+
expression=select_expr,
|
|
1289
|
+
schema=schema_expr,
|
|
1290
|
+
properties=properties_node,
|
|
1291
|
+
)
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
class AlterTable(DDLBuilder):
|
|
1295
|
+
"""Builder for ALTER TABLE operations.
|
|
1296
|
+
|
|
1297
|
+
Example:
|
|
1298
|
+
builder = (
|
|
1299
|
+
AlterTable("users")
|
|
1300
|
+
.add_column("email", "VARCHAR(255)", not_null=True)
|
|
1301
|
+
.drop_column("old_field")
|
|
1302
|
+
.add_constraint("check_age", "CHECK (age >= 18)")
|
|
1303
|
+
)
|
|
1304
|
+
"""
|
|
1305
|
+
|
|
1306
|
+
__slots__ = ("_if_exists", "_operations", "_schema", "_table_name")
|
|
1307
|
+
|
|
1308
|
+
def __init__(self, table_name: str, dialect: "DialectType" = None) -> None:
|
|
1309
|
+
super().__init__(dialect=dialect)
|
|
1310
|
+
self._table_name = table_name
|
|
1311
|
+
self._operations: list[AlterOperation] = []
|
|
1312
|
+
self._schema: str | None = None
|
|
1313
|
+
self._if_exists = False
|
|
1314
|
+
|
|
1315
|
+
def if_exists(self) -> "Self":
|
|
1316
|
+
"""Add IF EXISTS clause."""
|
|
1317
|
+
self._if_exists = True
|
|
1318
|
+
return self
|
|
1319
|
+
|
|
1320
|
+
def add_column(
|
|
1321
|
+
self,
|
|
1322
|
+
name: str,
|
|
1323
|
+
dtype: str,
|
|
1324
|
+
default: "Any | None" = None,
|
|
1325
|
+
not_null: bool = False,
|
|
1326
|
+
unique: bool = False,
|
|
1327
|
+
comment: "str | None" = None,
|
|
1328
|
+
after: "str | None" = None,
|
|
1329
|
+
first: bool = False,
|
|
1330
|
+
) -> "Self":
|
|
1331
|
+
"""Add a new column to the table."""
|
|
1332
|
+
if not name:
|
|
1333
|
+
self._raise_sql_builder_error("Column name must be a non-empty string")
|
|
1334
|
+
|
|
1335
|
+
if not dtype:
|
|
1336
|
+
self._raise_sql_builder_error("Column type must be a non-empty string")
|
|
1337
|
+
|
|
1338
|
+
column_def = ColumnDefinition(
|
|
1339
|
+
name=name, dtype=dtype, default=default, not_null=not_null, unique=unique, comment=comment
|
|
1340
|
+
)
|
|
1341
|
+
|
|
1342
|
+
operation = AlterOperation(
|
|
1343
|
+
operation_type="ADD COLUMN", column_definition=column_def, after_column=after, first=first
|
|
1344
|
+
)
|
|
1345
|
+
|
|
1346
|
+
self._operations.append(operation)
|
|
1347
|
+
return self
|
|
1348
|
+
|
|
1349
|
+
def drop_column(self, name: str, cascade: bool = False) -> "Self":
|
|
1350
|
+
"""Drop a column from the table."""
|
|
1351
|
+
if not name:
|
|
1352
|
+
self._raise_sql_builder_error("Column name must be a non-empty string")
|
|
1353
|
+
|
|
1354
|
+
operation = AlterOperation(operation_type="DROP COLUMN CASCADE" if cascade else "DROP COLUMN", column_name=name)
|
|
1355
|
+
|
|
1356
|
+
self._operations.append(operation)
|
|
1357
|
+
return self
|
|
1358
|
+
|
|
1359
|
+
def alter_column_type(self, name: str, new_type: str, using: "str | None" = None) -> "Self":
|
|
1360
|
+
"""Change the type of an existing column."""
|
|
1361
|
+
if not name:
|
|
1362
|
+
self._raise_sql_builder_error("Column name must be a non-empty string")
|
|
1363
|
+
|
|
1364
|
+
if not new_type:
|
|
1365
|
+
self._raise_sql_builder_error("New type must be a non-empty string")
|
|
1366
|
+
|
|
1367
|
+
operation = AlterOperation(
|
|
1368
|
+
operation_type="ALTER COLUMN TYPE", column_name=name, new_type=new_type, using_expression=using
|
|
1369
|
+
)
|
|
1370
|
+
|
|
1371
|
+
self._operations.append(operation)
|
|
1372
|
+
return self
|
|
1373
|
+
|
|
1374
|
+
def rename_column(self, old_name: str, new_name: str) -> "Self":
|
|
1375
|
+
"""Rename a column."""
|
|
1376
|
+
if not old_name:
|
|
1377
|
+
self._raise_sql_builder_error("Old column name must be a non-empty string")
|
|
1378
|
+
|
|
1379
|
+
if not new_name:
|
|
1380
|
+
self._raise_sql_builder_error("New column name must be a non-empty string")
|
|
1381
|
+
|
|
1382
|
+
operation = AlterOperation(operation_type="RENAME COLUMN", column_name=old_name, new_name=new_name)
|
|
1383
|
+
|
|
1384
|
+
self._operations.append(operation)
|
|
1385
|
+
return self
|
|
1386
|
+
|
|
1387
|
+
def add_constraint(
|
|
1388
|
+
self,
|
|
1389
|
+
constraint_type: str,
|
|
1390
|
+
columns: "str | list[str] | None" = None,
|
|
1391
|
+
name: "str | None" = None,
|
|
1392
|
+
references_table: "str | None" = None,
|
|
1393
|
+
references_columns: "str | list[str] | None" = None,
|
|
1394
|
+
condition: "str | ColumnExpression | None" = None,
|
|
1395
|
+
on_delete: "str | None" = None,
|
|
1396
|
+
on_update: "str | None" = None,
|
|
1397
|
+
) -> "Self":
|
|
1398
|
+
"""Add a constraint to the table.
|
|
1399
|
+
|
|
1400
|
+
Args:
|
|
1401
|
+
constraint_type: Type of constraint ('PRIMARY KEY', 'FOREIGN KEY', 'UNIQUE', 'CHECK')
|
|
1402
|
+
columns: Column(s) for the constraint (not needed for CHECK)
|
|
1403
|
+
name: Optional constraint name
|
|
1404
|
+
references_table: Table referenced by foreign key
|
|
1405
|
+
references_columns: Columns referenced by foreign key
|
|
1406
|
+
condition: CHECK constraint condition
|
|
1407
|
+
on_delete: Foreign key ON DELETE action
|
|
1408
|
+
on_update: Foreign key ON UPDATE action
|
|
1409
|
+
"""
|
|
1410
|
+
if constraint_type.upper() not in VALID_CONSTRAINT_TYPES:
|
|
1411
|
+
self._raise_sql_builder_error(f"Invalid constraint type: {constraint_type}")
|
|
1412
|
+
|
|
1413
|
+
col_list = None
|
|
1414
|
+
if columns is not None:
|
|
1415
|
+
col_list = [columns] if isinstance(columns, str) else list(columns)
|
|
1416
|
+
|
|
1417
|
+
ref_col_list = None
|
|
1418
|
+
if references_columns is not None:
|
|
1419
|
+
ref_col_list = [references_columns] if isinstance(references_columns, str) else list(references_columns)
|
|
1420
|
+
|
|
1421
|
+
condition_str: str | None = None
|
|
1422
|
+
if condition is not None:
|
|
1423
|
+
if has_sqlglot_expression(condition):
|
|
1424
|
+
sqlglot_expr = condition.sqlglot_expression
|
|
1425
|
+
condition_str = sqlglot_expr.sql(dialect=self.dialect) if sqlglot_expr else str(condition)
|
|
1426
|
+
else:
|
|
1427
|
+
condition_str = str(condition)
|
|
1428
|
+
|
|
1429
|
+
constraint_def = ConstraintDefinition(
|
|
1430
|
+
constraint_type=constraint_type.upper(),
|
|
1431
|
+
name=name,
|
|
1432
|
+
columns=col_list or [],
|
|
1433
|
+
references_table=references_table,
|
|
1434
|
+
references_columns=ref_col_list or [],
|
|
1435
|
+
condition=condition_str,
|
|
1436
|
+
on_delete=on_delete,
|
|
1437
|
+
on_update=on_update,
|
|
1438
|
+
)
|
|
1439
|
+
|
|
1440
|
+
operation = AlterOperation(operation_type="ADD CONSTRAINT", constraint_definition=constraint_def)
|
|
1441
|
+
|
|
1442
|
+
self._operations.append(operation)
|
|
1443
|
+
return self
|
|
1444
|
+
|
|
1445
|
+
def drop_constraint(self, name: str, cascade: bool = False) -> "Self":
|
|
1446
|
+
"""Drop a constraint from the table."""
|
|
1447
|
+
if not name:
|
|
1448
|
+
self._raise_sql_builder_error("Constraint name must be a non-empty string")
|
|
1449
|
+
|
|
1450
|
+
operation = AlterOperation(
|
|
1451
|
+
operation_type="DROP CONSTRAINT CASCADE" if cascade else "DROP CONSTRAINT", constraint_name=name
|
|
1452
|
+
)
|
|
1453
|
+
|
|
1454
|
+
self._operations.append(operation)
|
|
1455
|
+
return self
|
|
1456
|
+
|
|
1457
|
+
def set_not_null(self, column: str) -> "Self":
|
|
1458
|
+
"""Set a column to NOT NULL."""
|
|
1459
|
+
operation = AlterOperation(operation_type="ALTER COLUMN SET NOT NULL", column_name=column)
|
|
1460
|
+
|
|
1461
|
+
self._operations.append(operation)
|
|
1462
|
+
return self
|
|
1463
|
+
|
|
1464
|
+
def drop_not_null(self, column: str) -> "Self":
|
|
1465
|
+
"""Remove NOT NULL constraint from a column."""
|
|
1466
|
+
operation = AlterOperation(operation_type="ALTER COLUMN DROP NOT NULL", column_name=column)
|
|
1467
|
+
|
|
1468
|
+
self._operations.append(operation)
|
|
1469
|
+
return self
|
|
1470
|
+
|
|
1471
|
+
def _create_base_expression(self) -> "exp.Expression":
|
|
1472
|
+
"""Create the SQLGlot expression for ALTER TABLE."""
|
|
1473
|
+
if not self._operations:
|
|
1474
|
+
self._raise_sql_builder_error("At least one operation must be specified for ALTER TABLE")
|
|
1475
|
+
|
|
1476
|
+
if self._schema:
|
|
1477
|
+
table = exp.Table(this=exp.to_identifier(self._table_name), db=exp.to_identifier(self._schema))
|
|
1478
|
+
else:
|
|
1479
|
+
table = exp.to_table(self._table_name)
|
|
1480
|
+
|
|
1481
|
+
actions: list[exp.Expression] = [self._build_operation_expression(op) for op in self._operations]
|
|
1482
|
+
|
|
1483
|
+
return exp.Alter(this=table, kind="TABLE", actions=actions, exists=self._if_exists)
|
|
1484
|
+
|
|
1485
|
+
def _build_operation_expression(self, op: "AlterOperation") -> exp.Expression:
|
|
1486
|
+
"""Build a structured SQLGlot expression for a single alter operation."""
|
|
1487
|
+
op_type = op.operation_type.upper()
|
|
1488
|
+
|
|
1489
|
+
if op_type == "ADD COLUMN":
|
|
1490
|
+
if not op.column_definition:
|
|
1491
|
+
self._raise_sql_builder_error("Column definition required for ADD COLUMN")
|
|
1492
|
+
return build_column_expression(op.column_definition)
|
|
1493
|
+
|
|
1494
|
+
if op_type == "DROP COLUMN":
|
|
1495
|
+
return exp.Drop(this=exp.to_identifier(op.column_name), kind="COLUMN", exists=True)
|
|
1496
|
+
|
|
1497
|
+
if op_type == "DROP COLUMN CASCADE":
|
|
1498
|
+
return exp.Drop(this=exp.to_identifier(op.column_name), kind="COLUMN", cascade=True, exists=True)
|
|
1499
|
+
|
|
1500
|
+
if op_type == "ALTER COLUMN TYPE":
|
|
1501
|
+
if not op.new_type:
|
|
1502
|
+
self._raise_sql_builder_error("New type required for ALTER COLUMN TYPE")
|
|
1503
|
+
return exp.AlterColumn(
|
|
1504
|
+
this=exp.to_identifier(op.column_name),
|
|
1505
|
+
dtype=exp.DataType.build(op.new_type),
|
|
1506
|
+
using=exp.maybe_parse(op.using_expression) if op.using_expression else None,
|
|
1507
|
+
)
|
|
1508
|
+
|
|
1509
|
+
if op_type == "RENAME COLUMN":
|
|
1510
|
+
return exp.RenameColumn(this=exp.to_identifier(op.column_name), to=exp.to_identifier(op.new_name))
|
|
1511
|
+
|
|
1512
|
+
if op_type == "ADD CONSTRAINT":
|
|
1513
|
+
if not op.constraint_definition:
|
|
1514
|
+
self._raise_sql_builder_error("Constraint definition required for ADD CONSTRAINT")
|
|
1515
|
+
constraint_expr = build_constraint_expression(op.constraint_definition)
|
|
1516
|
+
return exp.AddConstraint(this=constraint_expr)
|
|
1517
|
+
|
|
1518
|
+
if op_type == "DROP CONSTRAINT":
|
|
1519
|
+
return exp.Drop(this=exp.to_identifier(op.constraint_name), kind="CONSTRAINT", exists=True)
|
|
1520
|
+
|
|
1521
|
+
if op_type == "DROP CONSTRAINT CASCADE":
|
|
1522
|
+
return exp.Drop(this=exp.to_identifier(op.constraint_name), kind="CONSTRAINT", cascade=True, exists=True)
|
|
1523
|
+
|
|
1524
|
+
if op_type == "ALTER COLUMN SET NOT NULL":
|
|
1525
|
+
return exp.AlterColumn(this=exp.to_identifier(op.column_name), allow_null=False)
|
|
1526
|
+
|
|
1527
|
+
if op_type == "ALTER COLUMN DROP NOT NULL":
|
|
1528
|
+
return exp.AlterColumn(this=exp.to_identifier(op.column_name), drop=True, allow_null=True)
|
|
1529
|
+
|
|
1530
|
+
if op_type == "ALTER COLUMN SET DEFAULT":
|
|
1531
|
+
if not op.column_definition or op.column_definition.default is None:
|
|
1532
|
+
self._raise_sql_builder_error("Default value required for SET DEFAULT")
|
|
1533
|
+
default_val = op.column_definition.default
|
|
1534
|
+
default_expr: exp.Expression | None
|
|
1535
|
+
if isinstance(default_val, str):
|
|
1536
|
+
if self._is_sql_function_default(default_val):
|
|
1537
|
+
default_expr = exp.maybe_parse(default_val)
|
|
1538
|
+
else:
|
|
1539
|
+
default_expr = exp.convert(default_val)
|
|
1540
|
+
elif isinstance(default_val, (int, float)):
|
|
1541
|
+
default_expr = exp.convert(default_val)
|
|
1542
|
+
elif default_val is True:
|
|
1543
|
+
default_expr = exp.true()
|
|
1544
|
+
elif default_val is False:
|
|
1545
|
+
default_expr = exp.false()
|
|
1546
|
+
else:
|
|
1547
|
+
default_expr = exp.convert(str(default_val))
|
|
1548
|
+
return exp.AlterColumn(this=exp.to_identifier(op.column_name), default=default_expr)
|
|
1549
|
+
|
|
1550
|
+
if op_type == "ALTER COLUMN DROP DEFAULT":
|
|
1551
|
+
return exp.AlterColumn(this=exp.to_identifier(op.column_name), kind="DROP DEFAULT")
|
|
1552
|
+
|
|
1553
|
+
self._raise_sql_builder_error(f"Unknown operation type: {op.operation_type}")
|
|
1554
|
+
raise AssertionError
|
|
1555
|
+
|
|
1556
|
+
def _is_sql_function_default(self, default_val: str) -> bool:
|
|
1557
|
+
"""Check if default value is a SQL function or expression."""
|
|
1558
|
+
default_upper = default_val.upper()
|
|
1559
|
+
return (
|
|
1560
|
+
default_upper in {CURRENT_TIMESTAMP_KEYWORD, CURRENT_DATE_KEYWORD, CURRENT_TIME_KEYWORD}
|
|
1561
|
+
or "(" in default_val
|
|
1562
|
+
)
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
class CommentOn(DDLBuilder):
|
|
1566
|
+
"""Builder for COMMENT ON ... IS ... statements."""
|
|
1567
|
+
|
|
1568
|
+
__slots__ = ("_column", "_comment", "_table", "_target_type")
|
|
1569
|
+
|
|
1570
|
+
def __init__(self, dialect: "DialectType" = None) -> None:
|
|
1571
|
+
"""Initialize COMMENT ON builder.
|
|
1572
|
+
|
|
1573
|
+
Args:
|
|
1574
|
+
dialect: SQL dialect to use
|
|
1575
|
+
"""
|
|
1576
|
+
super().__init__(dialect=dialect)
|
|
1577
|
+
self._target_type: str | None = None
|
|
1578
|
+
self._table: str | None = None
|
|
1579
|
+
self._column: str | None = None
|
|
1580
|
+
self._comment: str | None = None
|
|
1581
|
+
|
|
1582
|
+
def on_table(self, table: str) -> Self:
|
|
1583
|
+
self._target_type = "TABLE"
|
|
1584
|
+
self._table = table
|
|
1585
|
+
self._column = None
|
|
1586
|
+
return self
|
|
1587
|
+
|
|
1588
|
+
def on_column(self, table: str, column: str) -> Self:
|
|
1589
|
+
self._target_type = "COLUMN"
|
|
1590
|
+
self._table = table
|
|
1591
|
+
self._column = column
|
|
1592
|
+
return self
|
|
1593
|
+
|
|
1594
|
+
def is_(self, comment: str) -> Self:
|
|
1595
|
+
self._comment = comment
|
|
1596
|
+
return self
|
|
1597
|
+
|
|
1598
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
1599
|
+
if self._target_type == "TABLE" and self._table and self._comment is not None:
|
|
1600
|
+
return exp.Comment(this=exp.to_table(self._table), kind="TABLE", expression=exp.convert(self._comment))
|
|
1601
|
+
if self._target_type == "COLUMN" and self._table and self._column and self._comment is not None:
|
|
1602
|
+
return exp.Comment(
|
|
1603
|
+
this=exp.Column(table=self._table, this=self._column),
|
|
1604
|
+
kind="COLUMN",
|
|
1605
|
+
expression=exp.convert(self._comment),
|
|
1606
|
+
)
|
|
1607
|
+
self._raise_sql_builder_error("Must specify target and comment for COMMENT ON statement.")
|
|
1608
|
+
raise AssertionError
|
|
1609
|
+
|
|
1610
|
+
|
|
1611
|
+
class RenameTable(DDLBuilder):
|
|
1612
|
+
"""Builder for ALTER TABLE ... RENAME TO ... statements."""
|
|
1613
|
+
|
|
1614
|
+
__slots__ = ("_new_name", "_old_name")
|
|
1615
|
+
|
|
1616
|
+
def __init__(self, old_name: str, dialect: "DialectType" = None) -> None:
|
|
1617
|
+
"""Initialize RENAME TABLE with old name.
|
|
1618
|
+
|
|
1619
|
+
Args:
|
|
1620
|
+
old_name: Current name of the table
|
|
1621
|
+
dialect: SQL dialect to use
|
|
1622
|
+
"""
|
|
1623
|
+
super().__init__(dialect=dialect)
|
|
1624
|
+
self._old_name = old_name
|
|
1625
|
+
self._new_name: str | None = None
|
|
1626
|
+
|
|
1627
|
+
def table(self, old_name: str) -> Self:
|
|
1628
|
+
self._old_name = old_name
|
|
1629
|
+
return self
|
|
1630
|
+
|
|
1631
|
+
def to(self, new_name: str) -> Self:
|
|
1632
|
+
self._new_name = new_name
|
|
1633
|
+
return self
|
|
1634
|
+
|
|
1635
|
+
def _create_base_expression(self) -> exp.Expression:
|
|
1636
|
+
if not self._old_name or not self._new_name:
|
|
1637
|
+
self._raise_sql_builder_error("Both old and new table names must be set for RENAME TABLE.")
|
|
1638
|
+
return exp.Alter(
|
|
1639
|
+
this=exp.to_table(self._old_name),
|
|
1640
|
+
kind="TABLE",
|
|
1641
|
+
actions=[exp.AlterRename(this=exp.to_identifier(self._new_name))],
|
|
1642
|
+
)
|