sqlspec 0.47.0__cp314-cp314-win_amd64.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.
- f68e0789eb443ecb1c2c__mypyc.cp314-win_amd64.pyd +0 -0
- sqlspec/__init__.py +167 -0
- sqlspec/__main__.py +12 -0
- sqlspec/__metadata__.py +14 -0
- sqlspec/_typing.py +714 -0
- sqlspec/adapters/__init__.py +0 -0
- sqlspec/adapters/adbc/__init__.py +13 -0
- sqlspec/adapters/adbc/_typing.py +106 -0
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +1280 -0
- sqlspec/adapters/adbc/config.py +378 -0
- sqlspec/adapters/adbc/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/adbc/core.py +922 -0
- sqlspec/adapters/adbc/data_dictionary.py +339 -0
- sqlspec/adapters/adbc/driver.py +534 -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 +534 -0
- sqlspec/adapters/adbc/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/adbc/type_converter.py +142 -0
- sqlspec/adapters/aiomysql/__init__.py +21 -0
- sqlspec/adapters/aiomysql/_typing.py +137 -0
- sqlspec/adapters/aiomysql/adk/__init__.py +5 -0
- sqlspec/adapters/aiomysql/adk/store.py +678 -0
- sqlspec/adapters/aiomysql/config.py +305 -0
- sqlspec/adapters/aiomysql/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiomysql/core.py +536 -0
- sqlspec/adapters/aiomysql/data_dictionary.py +121 -0
- sqlspec/adapters/aiomysql/driver.py +386 -0
- sqlspec/adapters/aiomysql/events/__init__.py +5 -0
- sqlspec/adapters/aiomysql/events/store.py +104 -0
- sqlspec/adapters/aiomysql/litestar/__init__.py +5 -0
- sqlspec/adapters/aiomysql/litestar/store.py +314 -0
- sqlspec/adapters/aiosqlite/__init__.py +26 -0
- sqlspec/adapters/aiosqlite/_typing.py +109 -0
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +829 -0
- sqlspec/adapters/aiosqlite/config.py +315 -0
- sqlspec/adapters/aiosqlite/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiosqlite/core.py +315 -0
- sqlspec/adapters/aiosqlite/data_dictionary.py +202 -0
- sqlspec/adapters/aiosqlite/driver.py +311 -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.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/aiosqlite/pool.py +734 -0
- sqlspec/adapters/asyncmy/__init__.py +21 -0
- sqlspec/adapters/asyncmy/_typing.py +113 -0
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +644 -0
- sqlspec/adapters/asyncmy/config.py +307 -0
- sqlspec/adapters/asyncmy/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/asyncmy/core.py +538 -0
- sqlspec/adapters/asyncmy/data_dictionary.py +122 -0
- sqlspec/adapters/asyncmy/driver.py +391 -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 +26 -0
- sqlspec/adapters/asyncpg/_typing.py +103 -0
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +483 -0
- sqlspec/adapters/asyncpg/config.py +575 -0
- sqlspec/adapters/asyncpg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/asyncpg/core.py +480 -0
- sqlspec/adapters/asyncpg/data_dictionary.py +157 -0
- sqlspec/adapters/asyncpg/driver.py +487 -0
- sqlspec/adapters/asyncpg/events/__init__.py +6 -0
- sqlspec/adapters/asyncpg/events/_hub.py +181 -0
- sqlspec/adapters/asyncpg/events/backend.py +210 -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 +15 -0
- sqlspec/adapters/bigquery/_typing.py +108 -0
- sqlspec/adapters/bigquery/config.py +362 -0
- sqlspec/adapters/bigquery/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/bigquery/core.py +768 -0
- sqlspec/adapters/bigquery/data_dictionary.py +120 -0
- sqlspec/adapters/bigquery/driver.py +542 -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.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/bigquery/type_converter.py +107 -0
- sqlspec/adapters/cockroach_asyncpg/__init__.py +26 -0
- sqlspec/adapters/cockroach_asyncpg/_typing.py +73 -0
- sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
- sqlspec/adapters/cockroach_asyncpg/adk/store.py +465 -0
- sqlspec/adapters/cockroach_asyncpg/config.py +248 -0
- sqlspec/adapters/cockroach_asyncpg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
- sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +110 -0
- sqlspec/adapters/cockroach_asyncpg/driver.py +142 -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 +39 -0
- sqlspec/adapters/cockroach_psycopg/_typing.py +137 -0
- sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
- sqlspec/adapters/cockroach_psycopg/adk/store.py +1039 -0
- sqlspec/adapters/cockroach_psycopg/config.py +511 -0
- sqlspec/adapters/cockroach_psycopg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/cockroach_psycopg/core.py +63 -0
- sqlspec/adapters/cockroach_psycopg/data_dictionary.py +220 -0
- sqlspec/adapters/cockroach_psycopg/driver.py +273 -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 +327 -0
- sqlspec/adapters/duckdb/__init__.py +29 -0
- sqlspec/adapters/duckdb/_typing.py +104 -0
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +935 -0
- sqlspec/adapters/duckdb/config.py +386 -0
- sqlspec/adapters/duckdb/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/core.py +332 -0
- sqlspec/adapters/duckdb/data_dictionary.py +140 -0
- sqlspec/adapters/duckdb/driver.py +426 -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.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/pool.py +350 -0
- sqlspec/adapters/duckdb/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/duckdb/type_converter.py +118 -0
- sqlspec/adapters/mysqlconnector/__init__.py +39 -0
- sqlspec/adapters/mysqlconnector/_typing.py +186 -0
- sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
- sqlspec/adapters/mysqlconnector/adk/store.py +1183 -0
- sqlspec/adapters/mysqlconnector/config.py +421 -0
- sqlspec/adapters/mysqlconnector/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/mysqlconnector/core.py +472 -0
- sqlspec/adapters/mysqlconnector/data_dictionary.py +230 -0
- sqlspec/adapters/mysqlconnector/driver.py +516 -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 +39 -0
- sqlspec/adapters/oracledb/_json_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_json_handlers.py +196 -0
- sqlspec/adapters/oracledb/_param_types.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_param_types.py +46 -0
- sqlspec/adapters/oracledb/_typing.py +258 -0
- sqlspec/adapters/oracledb/_uuid_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_uuid_handlers.py +163 -0
- sqlspec/adapters/oracledb/_vector_handlers.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/_vector_handlers.py +228 -0
- sqlspec/adapters/oracledb/adk/__init__.py +21 -0
- sqlspec/adapters/oracledb/adk/store.py +2453 -0
- sqlspec/adapters/oracledb/config.py +575 -0
- sqlspec/adapters/oracledb/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/core.py +820 -0
- sqlspec/adapters/oracledb/data_dictionary.py +404 -0
- sqlspec/adapters/oracledb/driver.py +1277 -0
- sqlspec/adapters/oracledb/events/__init__.py +16 -0
- sqlspec/adapters/oracledb/events/_hub.py +345 -0
- sqlspec/adapters/oracledb/events/backend.py +300 -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 +539 -0
- sqlspec/adapters/oracledb/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/oracledb/type_converter.py +211 -0
- sqlspec/adapters/psqlpy/__init__.py +18 -0
- sqlspec/adapters/psqlpy/_typing.py +121 -0
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +591 -0
- sqlspec/adapters/psqlpy/config.py +376 -0
- sqlspec/adapters/psqlpy/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psqlpy/core.py +694 -0
- sqlspec/adapters/psqlpy/data_dictionary.py +121 -0
- sqlspec/adapters/psqlpy/driver.py +411 -0
- sqlspec/adapters/psqlpy/events/__init__.py +6 -0
- sqlspec/adapters/psqlpy/events/_hub.py +204 -0
- sqlspec/adapters/psqlpy/events/backend.py +210 -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.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psqlpy/type_converter.py +113 -0
- sqlspec/adapters/psycopg/__init__.py +38 -0
- sqlspec/adapters/psycopg/_typing.py +218 -0
- sqlspec/adapters/psycopg/adk/__init__.py +10 -0
- sqlspec/adapters/psycopg/adk/store.py +1106 -0
- sqlspec/adapters/psycopg/config.py +695 -0
- sqlspec/adapters/psycopg/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psycopg/core.py +520 -0
- sqlspec/adapters/psycopg/data_dictionary.py +278 -0
- sqlspec/adapters/psycopg/driver.py +1033 -0
- sqlspec/adapters/psycopg/events/__init__.py +20 -0
- sqlspec/adapters/psycopg/events/_hub.py +388 -0
- sqlspec/adapters/psycopg/events/backend.py +398 -0
- sqlspec/adapters/psycopg/events/store.py +42 -0
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/psycopg/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/psycopg/type_converter.py +93 -0
- sqlspec/adapters/pymysql/__init__.py +21 -0
- sqlspec/adapters/pymysql/_typing.py +92 -0
- sqlspec/adapters/pymysql/adk/__init__.py +5 -0
- sqlspec/adapters/pymysql/adk/store.py +657 -0
- sqlspec/adapters/pymysql/config.py +176 -0
- sqlspec/adapters/pymysql/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/pymysql/core.py +469 -0
- sqlspec/adapters/pymysql/data_dictionary.py +120 -0
- sqlspec/adapters/pymysql/driver.py +271 -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.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/pymysql/pool.py +184 -0
- sqlspec/adapters/spanner/__init__.py +33 -0
- sqlspec/adapters/spanner/_typing.py +102 -0
- sqlspec/adapters/spanner/adk/__init__.py +5 -0
- sqlspec/adapters/spanner/adk/store.py +758 -0
- sqlspec/adapters/spanner/config.py +355 -0
- sqlspec/adapters/spanner/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/spanner/core.py +263 -0
- sqlspec/adapters/spanner/data_dictionary.py +120 -0
- sqlspec/adapters/spanner/driver.py +407 -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.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/spanner/type_converter.py +342 -0
- sqlspec/adapters/sqlite/__init__.py +19 -0
- sqlspec/adapters/sqlite/_typing.py +123 -0
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +992 -0
- sqlspec/adapters/sqlite/config.py +240 -0
- sqlspec/adapters/sqlite/core.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/core.py +357 -0
- sqlspec/adapters/sqlite/data_dictionary.py +198 -0
- sqlspec/adapters/sqlite/driver.py +527 -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.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/pool.py +237 -0
- sqlspec/adapters/sqlite/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/adapters/sqlite/type_converter.py +114 -0
- sqlspec/base.py +832 -0
- sqlspec/builder/__init__.py +181 -0
- sqlspec/builder/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_base.py +1071 -0
- sqlspec/builder/_column.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_column.py +521 -0
- sqlspec/builder/_ddl.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_ddl.py +1691 -0
- sqlspec/builder/_delete.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_delete.py +95 -0
- sqlspec/builder/_dml.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_dml.py +386 -0
- sqlspec/builder/_explain.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_explain.py +579 -0
- sqlspec/builder/_expression_wrappers.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_expression_wrappers.py +46 -0
- sqlspec/builder/_factory.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_factory.py +1884 -0
- sqlspec/builder/_insert.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_insert.py +405 -0
- sqlspec/builder/_join.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_join.py +489 -0
- sqlspec/builder/_merge.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_merge.py +823 -0
- sqlspec/builder/_parsing_utils.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_parsing_utils.py +295 -0
- sqlspec/builder/_select.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_select.py +1666 -0
- sqlspec/builder/_temporal.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_temporal.py +167 -0
- sqlspec/builder/_update.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_update.py +173 -0
- sqlspec/builder/_vector_distance.cp314-win_amd64.pyd +0 -0
- sqlspec/builder/_vector_distance.py +330 -0
- sqlspec/cli.py +1095 -0
- sqlspec/config.py +2383 -0
- sqlspec/core/__init__.py +372 -0
- sqlspec/core/_correlation.cp314-win_amd64.pyd +0 -0
- sqlspec/core/_correlation.py +176 -0
- sqlspec/core/_pagination.py +42 -0
- sqlspec/core/_pool.cp314-win_amd64.pyd +0 -0
- sqlspec/core/_pool.py +76 -0
- sqlspec/core/cache.cp314-win_amd64.pyd +0 -0
- sqlspec/core/cache.py +1085 -0
- sqlspec/core/compiler.cp314-win_amd64.pyd +0 -0
- sqlspec/core/compiler.py +1090 -0
- sqlspec/core/config_runtime.cp314-win_amd64.pyd +0 -0
- sqlspec/core/config_runtime.py +174 -0
- sqlspec/core/explain.cp314-win_amd64.pyd +0 -0
- sqlspec/core/explain.py +275 -0
- sqlspec/core/filters.cp314-win_amd64.pyd +0 -0
- sqlspec/core/filters.py +969 -0
- sqlspec/core/hashing.cp314-win_amd64.pyd +0 -0
- sqlspec/core/hashing.py +266 -0
- sqlspec/core/metrics.cp314-win_amd64.pyd +0 -0
- sqlspec/core/metrics.py +83 -0
- sqlspec/core/parameters/__init__.py +72 -0
- sqlspec/core/parameters/_alignment.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_alignment.py +283 -0
- sqlspec/core/parameters/_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_converter.py +554 -0
- sqlspec/core/parameters/_processor.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_processor.py +1182 -0
- sqlspec/core/parameters/_registry.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_registry.py +206 -0
- sqlspec/core/parameters/_transformers.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_transformers.py +324 -0
- sqlspec/core/parameters/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_types.py +536 -0
- sqlspec/core/parameters/_validator.cp314-win_amd64.pyd +0 -0
- sqlspec/core/parameters/_validator.py +171 -0
- sqlspec/core/pipeline.cp314-win_amd64.pyd +0 -0
- sqlspec/core/pipeline.py +333 -0
- sqlspec/core/query_modifiers.cp314-win_amd64.pyd +0 -0
- sqlspec/core/query_modifiers.py +508 -0
- sqlspec/core/result/__init__.py +25 -0
- sqlspec/core/result/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/core/result/_base.py +1232 -0
- sqlspec/core/result/_io.cp314-win_amd64.pyd +0 -0
- sqlspec/core/result/_io.py +28 -0
- sqlspec/core/splitter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/splitter.py +1021 -0
- sqlspec/core/sqlcommenter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/sqlcommenter.py +249 -0
- sqlspec/core/stack.cp314-win_amd64.pyd +0 -0
- sqlspec/core/stack.py +163 -0
- sqlspec/core/statement.cp314-win_amd64.pyd +0 -0
- sqlspec/core/statement.py +1865 -0
- sqlspec/core/type_converter.cp314-win_amd64.pyd +0 -0
- sqlspec/core/type_converter.py +340 -0
- sqlspec/data_dictionary/__init__.py +22 -0
- sqlspec/data_dictionary/_loader.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_loader.py +138 -0
- sqlspec/data_dictionary/_registry.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_registry.py +74 -0
- sqlspec/data_dictionary/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/_types.py +121 -0
- sqlspec/data_dictionary/dialects/__init__.py +21 -0
- sqlspec/data_dictionary/dialects/bigquery.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/bigquery.py +81 -0
- sqlspec/data_dictionary/dialects/cockroachdb.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/cockroachdb.py +54 -0
- sqlspec/data_dictionary/dialects/duckdb.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/duckdb.py +47 -0
- sqlspec/data_dictionary/dialects/mysql.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/mysql.py +53 -0
- sqlspec/data_dictionary/dialects/oracle.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/oracle.py +197 -0
- sqlspec/data_dictionary/dialects/postgres.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/postgres.py +69 -0
- sqlspec/data_dictionary/dialects/spanner.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/spanner.py +37 -0
- sqlspec/data_dictionary/dialects/sqlite.cp314-win_amd64.pyd +0 -0
- sqlspec/data_dictionary/dialects/sqlite.py +59 -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/dialects/__init__.py +22 -0
- sqlspec/dialects/_compat.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/_compat.py +14 -0
- sqlspec/dialects/postgres/__init__.py +9 -0
- sqlspec/dialects/postgres/_generators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/postgres/_generators.py +57 -0
- sqlspec/dialects/postgres/_operators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/postgres/_operators.py +81 -0
- sqlspec/dialects/postgres/_paradedb.py +50 -0
- sqlspec/dialects/postgres/_pgvector.py +36 -0
- sqlspec/dialects/spanner/__init__.py +6 -0
- sqlspec/dialects/spanner/_generators.cp314-win_amd64.pyd +0 -0
- sqlspec/dialects/spanner/_generators.py +206 -0
- sqlspec/dialects/spanner/_spangres.py +77 -0
- sqlspec/dialects/spanner/_spanner.py +179 -0
- sqlspec/driver/__init__.py +49 -0
- sqlspec/driver/_async.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_async.py +1830 -0
- sqlspec/driver/_common.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_common.py +2292 -0
- sqlspec/driver/_exception_handler.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_exception_handler.py +108 -0
- sqlspec/driver/_query_cache.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_query_cache.py +96 -0
- sqlspec/driver/_sql_helpers.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_sql_helpers.py +139 -0
- sqlspec/driver/_storage_helpers.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_storage_helpers.py +153 -0
- sqlspec/driver/_sync.cp314-win_amd64.pyd +0 -0
- sqlspec/driver/_sync.py +1817 -0
- sqlspec/exceptions.cp314-win_amd64.pyd +0 -0
- sqlspec/exceptions.py +480 -0
- sqlspec/extensions/__init__.py +0 -0
- sqlspec/extensions/adk/__init__.py +84 -0
- sqlspec/extensions/adk/_config_utils.py +199 -0
- sqlspec/extensions/adk/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/_types.py +41 -0
- sqlspec/extensions/adk/artifact/__init__.py +57 -0
- sqlspec/extensions/adk/artifact/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/artifact/_types.py +32 -0
- sqlspec/extensions/adk/artifact/service.py +508 -0
- sqlspec/extensions/adk/artifact/store.py +361 -0
- sqlspec/extensions/adk/converters.py +212 -0
- sqlspec/extensions/adk/memory/__init__.py +69 -0
- sqlspec/extensions/adk/memory/_types.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/adk/memory/_types.py +30 -0
- sqlspec/extensions/adk/memory/converters.py +225 -0
- sqlspec/extensions/adk/memory/service.py +316 -0
- sqlspec/extensions/adk/memory/store.py +525 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +184 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +279 -0
- sqlspec/extensions/adk/store.py +590 -0
- sqlspec/extensions/events/__init__.py +51 -0
- sqlspec/extensions/events/_channel.py +703 -0
- sqlspec/extensions/events/_hints.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/events/_hints.py +45 -0
- sqlspec/extensions/events/_models.py +23 -0
- sqlspec/extensions/events/_payload.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/events/_payload.py +69 -0
- sqlspec/extensions/events/_protocols.py +134 -0
- sqlspec/extensions/events/_queue.py +462 -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 +22 -0
- sqlspec/extensions/fastapi/extension.py +391 -0
- sqlspec/extensions/fastapi/providers.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/fastapi/providers.py +712 -0
- sqlspec/extensions/flask/__init__.py +38 -0
- sqlspec/extensions/flask/_state.py +87 -0
- sqlspec/extensions/flask/_utils.py +71 -0
- sqlspec/extensions/flask/extension.py +539 -0
- sqlspec/extensions/litestar/__init__.py +31 -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 +1066 -0
- sqlspec/extensions/litestar/providers.cp314-win_amd64.pyd +0 -0
- sqlspec/extensions/litestar/providers.py +784 -0
- sqlspec/extensions/litestar/store.py +298 -0
- sqlspec/extensions/otel/__init__.py +58 -0
- sqlspec/extensions/prometheus/__init__.py +113 -0
- sqlspec/extensions/sanic/__init__.py +19 -0
- sqlspec/extensions/sanic/_state.py +43 -0
- sqlspec/extensions/sanic/_utils.py +127 -0
- sqlspec/extensions/sanic/extension.py +647 -0
- sqlspec/extensions/starlette/__init__.py +22 -0
- sqlspec/extensions/starlette/_state.py +42 -0
- sqlspec/extensions/starlette/_utils.py +96 -0
- sqlspec/extensions/starlette/extension.py +374 -0
- sqlspec/extensions/starlette/middleware.py +281 -0
- sqlspec/loader.cp314-win_amd64.pyd +0 -0
- sqlspec/loader.py +727 -0
- sqlspec/migrations/__init__.py +39 -0
- sqlspec/migrations/base.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/base.py +862 -0
- sqlspec/migrations/commands.py +2151 -0
- sqlspec/migrations/context.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/context.py +157 -0
- sqlspec/migrations/fix.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/fix.py +204 -0
- sqlspec/migrations/loaders.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/loaders.py +443 -0
- sqlspec/migrations/runner.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/runner.py +1195 -0
- sqlspec/migrations/squash.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/squash.py +490 -0
- sqlspec/migrations/templates.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/templates.py +234 -0
- sqlspec/migrations/tracker.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/tracker.py +792 -0
- sqlspec/migrations/utils.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/utils.py +256 -0
- sqlspec/migrations/validation.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/validation.py +359 -0
- sqlspec/migrations/version.cp314-win_amd64.pyd +0 -0
- sqlspec/migrations/version.py +446 -0
- sqlspec/observability/__init__.py +57 -0
- sqlspec/observability/_common.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_common.py +77 -0
- sqlspec/observability/_config.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_config.py +364 -0
- sqlspec/observability/_diagnostics.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_diagnostics.py +74 -0
- sqlspec/observability/_dispatcher.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_dispatcher.py +200 -0
- sqlspec/observability/_formatters/__init__.py +13 -0
- sqlspec/observability/_formatters/_aws.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_aws.py +102 -0
- sqlspec/observability/_formatters/_azure.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_azure.py +96 -0
- sqlspec/observability/_formatters/_base.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_base.py +57 -0
- sqlspec/observability/_formatters/_gcp.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_formatters/_gcp.py +131 -0
- sqlspec/observability/_formatting.py +58 -0
- sqlspec/observability/_observer.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_observer.py +361 -0
- sqlspec/observability/_runtime.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_runtime.py +461 -0
- sqlspec/observability/_sampling.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_sampling.py +188 -0
- sqlspec/observability/_spans.cp314-win_amd64.pyd +0 -0
- sqlspec/observability/_spans.py +161 -0
- sqlspec/protocols.py +955 -0
- sqlspec/py.typed +0 -0
- sqlspec/service.py +433 -0
- sqlspec/storage/__init__.py +48 -0
- sqlspec/storage/_arrow_payload.py +68 -0
- sqlspec/storage/_paths.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/_paths.py +58 -0
- sqlspec/storage/_utils.py +46 -0
- sqlspec/storage/backends/__init__.py +1 -0
- sqlspec/storage/backends/base.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/backends/base.py +374 -0
- sqlspec/storage/backends/fsspec.py +574 -0
- sqlspec/storage/backends/local.py +468 -0
- sqlspec/storage/backends/obstore.py +956 -0
- sqlspec/storage/errors.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/errors.py +102 -0
- sqlspec/storage/pipeline.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/pipeline.py +628 -0
- sqlspec/storage/registry.cp314-win_amd64.pyd +0 -0
- sqlspec/storage/registry.py +329 -0
- sqlspec/typing.py +405 -0
- sqlspec/utils/__init__.py +7 -0
- sqlspec/utils/arrow_helpers.py +384 -0
- sqlspec/utils/config_tools.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/config_tools.py +314 -0
- sqlspec/utils/correlation.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/correlation.py +134 -0
- sqlspec/utils/deprecation.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/deprecation.py +157 -0
- sqlspec/utils/dispatch.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/dispatch.py +101 -0
- sqlspec/utils/fixtures.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/fixtures.py +260 -0
- sqlspec/utils/logging.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/logging.py +251 -0
- sqlspec/utils/module_loader.py +306 -0
- sqlspec/utils/portal.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/portal.py +377 -0
- sqlspec/utils/schema.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/schema.py +1040 -0
- sqlspec/utils/serializers/__init__.py +30 -0
- sqlspec/utils/serializers/_json.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_json.py +415 -0
- sqlspec/utils/serializers/_numpy.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_numpy.py +65 -0
- sqlspec/utils/serializers/_schema.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/serializers/_schema.py +285 -0
- sqlspec/utils/singleton.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/singleton.py +41 -0
- sqlspec/utils/sync_tools.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/sync_tools.py +316 -0
- sqlspec/utils/text.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/text.py +109 -0
- sqlspec/utils/type_converters.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/type_converters.py +216 -0
- sqlspec/utils/type_guards.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/type_guards.py +1508 -0
- sqlspec/utils/uuids.cp314-win_amd64.pyd +0 -0
- sqlspec/utils/uuids.py +241 -0
- sqlspec-0.47.0.dist-info/METADATA +202 -0
- sqlspec-0.47.0.dist-info/RECORD +621 -0
- sqlspec-0.47.0.dist-info/WHEEL +4 -0
- sqlspec-0.47.0.dist-info/entry_points.txt +6 -0
- sqlspec-0.47.0.dist-info/licenses/LICENSE +21 -0
|
Binary file
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
"""Utility functions for SQLSpec migrations."""
|
|
2
|
+
|
|
3
|
+
import importlib
|
|
4
|
+
import inspect
|
|
5
|
+
import os
|
|
6
|
+
import subprocess
|
|
7
|
+
from datetime import datetime, timezone
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
from typing import TYPE_CHECKING, Any, cast
|
|
10
|
+
|
|
11
|
+
from sqlspec.migrations.templates import MigrationTemplateSettings, TemplateValidationError, build_template_settings
|
|
12
|
+
from sqlspec.utils.logging import get_logger
|
|
13
|
+
from sqlspec.utils.text import slugify
|
|
14
|
+
|
|
15
|
+
if TYPE_CHECKING:
|
|
16
|
+
from collections.abc import Callable
|
|
17
|
+
|
|
18
|
+
from sqlspec.config import DatabaseConfigProtocol
|
|
19
|
+
from sqlspec.driver import AsyncDriverAdapterBase
|
|
20
|
+
|
|
21
|
+
__all__ = ("create_migration_file", "drop_all", "get_author")
|
|
22
|
+
|
|
23
|
+
logger = get_logger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def create_migration_file(
|
|
27
|
+
migrations_dir: Path,
|
|
28
|
+
version: str,
|
|
29
|
+
message: str,
|
|
30
|
+
file_type: str | None = None,
|
|
31
|
+
*,
|
|
32
|
+
config: "DatabaseConfigProtocol[Any, Any, Any] | None" = None,
|
|
33
|
+
template_settings: "MigrationTemplateSettings | None" = None,
|
|
34
|
+
) -> Path:
|
|
35
|
+
"""Create a new migration file from template."""
|
|
36
|
+
|
|
37
|
+
migration_config = cast("dict[str, Any]", config.migration_config) if config is not None else {}
|
|
38
|
+
settings = template_settings or build_template_settings(migration_config)
|
|
39
|
+
author = get_author(migration_config.get("author"), config=config)
|
|
40
|
+
safe_message = _slugify_message(message)
|
|
41
|
+
file_format = settings.resolve_format(file_type)
|
|
42
|
+
extension = "py" if file_format == "py" else "sql"
|
|
43
|
+
filename = f"{version}_{safe_message or 'migration'}.{extension}"
|
|
44
|
+
file_path = migrations_dir / filename
|
|
45
|
+
context = _build_template_context(
|
|
46
|
+
settings=settings,
|
|
47
|
+
version=version,
|
|
48
|
+
message=message,
|
|
49
|
+
author=author,
|
|
50
|
+
adapter=_resolve_adapter_name(config),
|
|
51
|
+
project_slug=_derive_project_slug(config),
|
|
52
|
+
safe_message=safe_message,
|
|
53
|
+
)
|
|
54
|
+
renderer = settings.profile.python.render if file_format == "py" else settings.profile.sql.render
|
|
55
|
+
content = renderer(context)
|
|
56
|
+
file_path.write_text(content, encoding="utf-8")
|
|
57
|
+
return file_path
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def get_author(
|
|
61
|
+
author_config: Any | None = None, *, config: "DatabaseConfigProtocol[Any, Any, Any] | None" = None
|
|
62
|
+
) -> str:
|
|
63
|
+
"""Resolve author metadata for migration templates."""
|
|
64
|
+
|
|
65
|
+
if isinstance(author_config, str):
|
|
66
|
+
token = author_config.strip()
|
|
67
|
+
if not token:
|
|
68
|
+
return _resolve_git_author()
|
|
69
|
+
lowered = token.lower()
|
|
70
|
+
if lowered == "git":
|
|
71
|
+
return _resolve_git_author()
|
|
72
|
+
if lowered == "system":
|
|
73
|
+
return _get_system_username()
|
|
74
|
+
if lowered.startswith("env:"):
|
|
75
|
+
env_var = token.split(":", 1)[1].strip()
|
|
76
|
+
if not env_var:
|
|
77
|
+
msg = "Environment author token requires a variable name"
|
|
78
|
+
raise TemplateValidationError(msg)
|
|
79
|
+
return _resolve_author_from_env(env_var)
|
|
80
|
+
if lowered.startswith("callable:"):
|
|
81
|
+
import_path = token.split(":", 1)[1].strip()
|
|
82
|
+
if not import_path:
|
|
83
|
+
msg = "Callable author token requires an import path"
|
|
84
|
+
raise TemplateValidationError(msg)
|
|
85
|
+
return _resolve_author_callable(import_path, config)
|
|
86
|
+
if ":" in token and " " not in token:
|
|
87
|
+
return _resolve_author_callable(token, config)
|
|
88
|
+
return token
|
|
89
|
+
|
|
90
|
+
if isinstance(author_config, dict):
|
|
91
|
+
mode = str(author_config.get("mode") or "static").lower()
|
|
92
|
+
value = author_config.get("value")
|
|
93
|
+
if mode == "static":
|
|
94
|
+
if not isinstance(value, str) or not value.strip():
|
|
95
|
+
msg = "Static author value must be a non-empty string"
|
|
96
|
+
raise TemplateValidationError(msg)
|
|
97
|
+
return value.strip()
|
|
98
|
+
if mode == "env":
|
|
99
|
+
if not isinstance(value, str) or not value.strip():
|
|
100
|
+
msg = "Environment author mode requires an environment variable name"
|
|
101
|
+
raise TemplateValidationError(msg)
|
|
102
|
+
return _resolve_author_from_env(value.strip())
|
|
103
|
+
if mode == "callable":
|
|
104
|
+
if not isinstance(value, str) or not value.strip():
|
|
105
|
+
msg = "Callable author mode requires an import path"
|
|
106
|
+
raise TemplateValidationError(msg)
|
|
107
|
+
return _resolve_author_callable(value.strip(), config)
|
|
108
|
+
if mode == "system":
|
|
109
|
+
return _get_system_username()
|
|
110
|
+
if mode == "git":
|
|
111
|
+
return _resolve_git_author()
|
|
112
|
+
msg = f"Unsupported author mode '{mode}'"
|
|
113
|
+
raise TemplateValidationError(msg)
|
|
114
|
+
|
|
115
|
+
return _resolve_git_author()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def _get_git_config(config_key: str) -> str | None:
|
|
119
|
+
"""Retrieve git configuration value.
|
|
120
|
+
|
|
121
|
+
Args:
|
|
122
|
+
config_key: Git config key (e.g., 'user.name', 'user.email').
|
|
123
|
+
|
|
124
|
+
Returns:
|
|
125
|
+
Configuration value if found, None otherwise.
|
|
126
|
+
"""
|
|
127
|
+
try:
|
|
128
|
+
result = subprocess.run( # noqa: S603
|
|
129
|
+
["git", "config", config_key], # noqa: S607
|
|
130
|
+
capture_output=True,
|
|
131
|
+
text=True,
|
|
132
|
+
timeout=2,
|
|
133
|
+
check=False,
|
|
134
|
+
)
|
|
135
|
+
if result.returncode == 0 and result.stdout.strip():
|
|
136
|
+
return result.stdout.strip()
|
|
137
|
+
except (subprocess.SubprocessError, FileNotFoundError, OSError) as e:
|
|
138
|
+
logger.debug("Failed to get git config %s: %s", config_key, e)
|
|
139
|
+
|
|
140
|
+
return None
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def _get_system_username() -> str:
|
|
144
|
+
"""Get system username from environment.
|
|
145
|
+
|
|
146
|
+
Returns:
|
|
147
|
+
Username from USER environment variable, or 'unknown' if not set.
|
|
148
|
+
"""
|
|
149
|
+
return os.environ.get("USER", "unknown")
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _resolve_git_author() -> str:
|
|
153
|
+
git_name = _get_git_config("user.name")
|
|
154
|
+
git_email = _get_git_config("user.email")
|
|
155
|
+
if git_name and git_email:
|
|
156
|
+
return f"{git_name} <{git_email}>"
|
|
157
|
+
return _get_system_username()
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _resolve_author_from_env(env_var: str) -> str:
|
|
161
|
+
value = os.environ.get(env_var)
|
|
162
|
+
if value:
|
|
163
|
+
return value.strip()
|
|
164
|
+
msg = f"Environment variable '{env_var}' is not set for migration author"
|
|
165
|
+
raise TemplateValidationError(msg)
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _resolve_author_callable(import_path: str, config: "DatabaseConfigProtocol[Any, Any, Any] | None") -> str:
|
|
169
|
+
def _raise_callable_error(message: str) -> None:
|
|
170
|
+
msg = message
|
|
171
|
+
raise TemplateValidationError(msg)
|
|
172
|
+
|
|
173
|
+
module_name, _, attr_name = import_path.partition(":")
|
|
174
|
+
if not module_name or not attr_name:
|
|
175
|
+
_raise_callable_error("Callable author path must be in 'module:function' format")
|
|
176
|
+
module = importlib.import_module(module_name)
|
|
177
|
+
candidate_obj = module.__dict__.get(attr_name)
|
|
178
|
+
if candidate_obj is None or not callable(candidate_obj):
|
|
179
|
+
_raise_callable_error(f"Callable '{import_path}' is not callable")
|
|
180
|
+
candidate = cast("Callable[..., Any]", candidate_obj)
|
|
181
|
+
signature = inspect.signature(candidate)
|
|
182
|
+
param_count = len(signature.parameters)
|
|
183
|
+
if param_count > 1:
|
|
184
|
+
_raise_callable_error("Author callable must accept zero or one positional argument")
|
|
185
|
+
try:
|
|
186
|
+
result_value: object = candidate() if param_count == 0 else candidate(config)
|
|
187
|
+
except Exception as exc: # pragma: no cover - passthrough
|
|
188
|
+
msg = f"Author callable '{import_path}' raised an error: {exc}"
|
|
189
|
+
raise TemplateValidationError(msg) from exc
|
|
190
|
+
result_str: str = str(result_value)
|
|
191
|
+
return result_str
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
def _build_template_context(
|
|
195
|
+
*,
|
|
196
|
+
settings: "MigrationTemplateSettings",
|
|
197
|
+
version: str,
|
|
198
|
+
message: str,
|
|
199
|
+
author: str,
|
|
200
|
+
adapter: str,
|
|
201
|
+
project_slug: str,
|
|
202
|
+
safe_message: str,
|
|
203
|
+
) -> "dict[str, str]":
|
|
204
|
+
created_at = datetime.now(timezone.utc).isoformat()
|
|
205
|
+
display_message = message or "New migration"
|
|
206
|
+
description = display_message.strip() or safe_message or version
|
|
207
|
+
return {
|
|
208
|
+
"title": settings.profile.title,
|
|
209
|
+
"version": version,
|
|
210
|
+
"message": display_message,
|
|
211
|
+
"description": description,
|
|
212
|
+
"created_at": created_at,
|
|
213
|
+
"author": author,
|
|
214
|
+
"adapter": adapter,
|
|
215
|
+
"project_slug": project_slug,
|
|
216
|
+
"slug": safe_message,
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
def _derive_project_slug(config: "DatabaseConfigProtocol[Any, Any, Any] | None") -> str:
|
|
221
|
+
if config and config.bind_key:
|
|
222
|
+
source = config.bind_key
|
|
223
|
+
elif config:
|
|
224
|
+
source = config.__class__.__module__.split(".")[0]
|
|
225
|
+
else:
|
|
226
|
+
source = Path.cwd().name
|
|
227
|
+
return _slugify_message(source)
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
def _resolve_adapter_name(config: "DatabaseConfigProtocol[Any, Any, Any] | None") -> str:
|
|
231
|
+
if config is None:
|
|
232
|
+
return "UnknownAdapter"
|
|
233
|
+
driver_type = config.driver_type
|
|
234
|
+
if driver_type is not None:
|
|
235
|
+
return str(driver_type.__name__)
|
|
236
|
+
return type(config).__name__
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def _slugify_message(message: str) -> str:
|
|
240
|
+
slug = slugify(message or "", separator="_")
|
|
241
|
+
return slug[:50]
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
async def drop_all(engine: "AsyncDriverAdapterBase", version_table_name: str, metadata: Any | None = None) -> None:
|
|
245
|
+
"""Drop all tables from the database.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
engine: The database engine/driver.
|
|
249
|
+
version_table_name: Name of the version tracking table.
|
|
250
|
+
metadata: Optional metadata object.
|
|
251
|
+
|
|
252
|
+
Raises:
|
|
253
|
+
NotImplementedError: Always raised.
|
|
254
|
+
"""
|
|
255
|
+
msg = "drop_all functionality requires database-specific implementation"
|
|
256
|
+
raise NotImplementedError(msg)
|
|
Binary file
|
|
@@ -0,0 +1,359 @@
|
|
|
1
|
+
"""Migration validation and out-of-order detection for SQLSpec.
|
|
2
|
+
|
|
3
|
+
This module provides functionality to detect and handle out-of-order migrations,
|
|
4
|
+
which can occur when branches with migrations merge in different orders across
|
|
5
|
+
staging and production environments.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import logging
|
|
9
|
+
from typing import TYPE_CHECKING
|
|
10
|
+
|
|
11
|
+
from rich.console import Console
|
|
12
|
+
|
|
13
|
+
from sqlspec.exceptions import OutOfOrderMigrationError, SquashValidationError
|
|
14
|
+
from sqlspec.migrations.version import parse_version
|
|
15
|
+
from sqlspec.utils.logging import get_logger, log_with_context
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from collections.abc import Sequence
|
|
19
|
+
from pathlib import Path
|
|
20
|
+
|
|
21
|
+
from sqlspec.migrations.version import MigrationVersion
|
|
22
|
+
|
|
23
|
+
__all__ = (
|
|
24
|
+
"MigrationGap",
|
|
25
|
+
"detect_out_of_order_migrations",
|
|
26
|
+
"format_out_of_order_warning",
|
|
27
|
+
"validate_extension_consistency",
|
|
28
|
+
"validate_squash_idempotency",
|
|
29
|
+
"validate_squash_range",
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
console = Console()
|
|
33
|
+
logger = get_logger("sqlspec.migrations.validation")
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class MigrationGap:
|
|
37
|
+
"""Represents a migration that is out of order.
|
|
38
|
+
|
|
39
|
+
An out-of-order migration occurs when a pending migration has a timestamp
|
|
40
|
+
earlier than already-applied migrations, indicating it was created in a branch
|
|
41
|
+
that merged after other migrations were already applied.
|
|
42
|
+
|
|
43
|
+
Attributes:
|
|
44
|
+
missing_version: The out-of-order migration version.
|
|
45
|
+
applied_after: List of already-applied migrations with later timestamps.
|
|
46
|
+
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
__slots__ = ("_initialized", "applied_after", "missing_version")
|
|
50
|
+
applied_after: "list[MigrationVersion]"
|
|
51
|
+
missing_version: "MigrationVersion"
|
|
52
|
+
_initialized: bool
|
|
53
|
+
|
|
54
|
+
def __init__(self, missing_version: "MigrationVersion", applied_after: "list[MigrationVersion]") -> None:
|
|
55
|
+
object.__setattr__(self, "missing_version", missing_version)
|
|
56
|
+
object.__setattr__(self, "applied_after", list(applied_after))
|
|
57
|
+
object.__setattr__(self, "_initialized", True)
|
|
58
|
+
|
|
59
|
+
def __repr__(self) -> str:
|
|
60
|
+
return f"MigrationGap(missing_version={self.missing_version!r}, applied_after={self.applied_after!r})"
|
|
61
|
+
|
|
62
|
+
def __eq__(self, other: object) -> bool:
|
|
63
|
+
if not isinstance(other, MigrationGap):
|
|
64
|
+
return NotImplemented
|
|
65
|
+
return self.missing_version == other.missing_version and self.applied_after == other.applied_after
|
|
66
|
+
|
|
67
|
+
def __hash__(self) -> int:
|
|
68
|
+
return hash((self.missing_version, tuple(self.applied_after)))
|
|
69
|
+
|
|
70
|
+
def __setattr__(self, name: str, value: object) -> None:
|
|
71
|
+
if name == "_initialized":
|
|
72
|
+
object.__setattr__(self, name, value)
|
|
73
|
+
return
|
|
74
|
+
try:
|
|
75
|
+
initialized = self._initialized
|
|
76
|
+
except AttributeError:
|
|
77
|
+
initialized = False
|
|
78
|
+
if initialized:
|
|
79
|
+
msg = "MigrationGap is immutable"
|
|
80
|
+
raise AttributeError(msg)
|
|
81
|
+
object.__setattr__(self, name, value)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def detect_out_of_order_migrations(
|
|
85
|
+
pending_versions: "Sequence[str | None]", applied_versions: "Sequence[str | None]"
|
|
86
|
+
) -> "list[MigrationGap]":
|
|
87
|
+
"""Detect migrations created before already-applied migrations.
|
|
88
|
+
|
|
89
|
+
Identifies pending migrations with timestamps earlier than the latest applied
|
|
90
|
+
migration, which indicates they were created in branches that merged late or
|
|
91
|
+
were cherry-picked across environments.
|
|
92
|
+
|
|
93
|
+
Extension migrations are excluded from out-of-order detection as they maintain
|
|
94
|
+
independent sequences within their own namespaces.
|
|
95
|
+
|
|
96
|
+
Args:
|
|
97
|
+
pending_versions: List of migration versions not yet applied (may contain None).
|
|
98
|
+
applied_versions: List of migration versions already applied (may contain None).
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
List of migration gaps where pending versions are older than applied.
|
|
102
|
+
|
|
103
|
+
"""
|
|
104
|
+
if not applied_versions or not pending_versions:
|
|
105
|
+
return []
|
|
106
|
+
|
|
107
|
+
gaps: list[MigrationGap] = []
|
|
108
|
+
|
|
109
|
+
# Filter out None values, empty strings, and whitespace-only strings
|
|
110
|
+
valid_applied = [v for v in applied_versions if v is not None and v.strip()]
|
|
111
|
+
valid_pending = [v for v in pending_versions if v is not None and v.strip()]
|
|
112
|
+
|
|
113
|
+
if not valid_applied or not valid_pending:
|
|
114
|
+
return []
|
|
115
|
+
|
|
116
|
+
parsed_applied = [parse_version(v) for v in valid_applied]
|
|
117
|
+
parsed_pending = [parse_version(v) for v in valid_pending]
|
|
118
|
+
|
|
119
|
+
core_applied = [v for v in parsed_applied if v.extension is None]
|
|
120
|
+
core_pending = [v for v in parsed_pending if v.extension is None]
|
|
121
|
+
|
|
122
|
+
if not core_applied or not core_pending:
|
|
123
|
+
return []
|
|
124
|
+
|
|
125
|
+
latest_applied = max(core_applied)
|
|
126
|
+
|
|
127
|
+
for pending in core_pending:
|
|
128
|
+
if pending < latest_applied:
|
|
129
|
+
applied_after = [a for a in core_applied if a > pending]
|
|
130
|
+
if applied_after:
|
|
131
|
+
gaps.append(MigrationGap(missing_version=pending, applied_after=applied_after))
|
|
132
|
+
|
|
133
|
+
return gaps
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def format_out_of_order_warning(gaps: "list[MigrationGap]") -> str:
|
|
137
|
+
"""Create user-friendly warning message for out-of-order migrations.
|
|
138
|
+
|
|
139
|
+
Formats migration gaps into a clear warning message explaining which migrations
|
|
140
|
+
are out of order and what migrations were already applied after them.
|
|
141
|
+
|
|
142
|
+
Args:
|
|
143
|
+
gaps: List of migration gaps to format.
|
|
144
|
+
|
|
145
|
+
Returns:
|
|
146
|
+
Formatted warning message string.
|
|
147
|
+
|
|
148
|
+
Example:
|
|
149
|
+
>>> gaps = [MigrationGap(version1, [version2, version3])]
|
|
150
|
+
>>> print(format_out_of_order_warning(gaps))
|
|
151
|
+
Out-of-order migrations detected:
|
|
152
|
+
|
|
153
|
+
- 20251011130000 created before:
|
|
154
|
+
- 20251012140000
|
|
155
|
+
- 20251013090000
|
|
156
|
+
|
|
157
|
+
"""
|
|
158
|
+
if not gaps:
|
|
159
|
+
return ""
|
|
160
|
+
|
|
161
|
+
lines = ["Out-of-order migrations detected:", ""]
|
|
162
|
+
|
|
163
|
+
for gap in gaps:
|
|
164
|
+
lines.append(f"- {gap.missing_version.raw} created before:")
|
|
165
|
+
lines.extend(f" - {applied.raw}" for applied in gap.applied_after)
|
|
166
|
+
lines.append("")
|
|
167
|
+
|
|
168
|
+
lines.extend((
|
|
169
|
+
"These migrations will be applied but may cause issues if they",
|
|
170
|
+
"depend on schema changes from later migrations.",
|
|
171
|
+
"",
|
|
172
|
+
"To prevent this in the future, ensure migrations are merged in",
|
|
173
|
+
"chronological order or use strict_ordering mode in migration_config.",
|
|
174
|
+
))
|
|
175
|
+
|
|
176
|
+
return "\n".join(lines)
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
def validate_migration_order(
|
|
180
|
+
pending_versions: "list[str]",
|
|
181
|
+
applied_versions: "list[str]",
|
|
182
|
+
strict_ordering: bool = False,
|
|
183
|
+
*,
|
|
184
|
+
use_logger: bool = False,
|
|
185
|
+
echo: bool = True,
|
|
186
|
+
summary_only: bool = False,
|
|
187
|
+
) -> None:
|
|
188
|
+
"""Validate migration order and raise error if out-of-order in strict mode.
|
|
189
|
+
|
|
190
|
+
Checks for out-of-order migrations and either warns or raises an error
|
|
191
|
+
depending on the strict_ordering configuration.
|
|
192
|
+
|
|
193
|
+
Args:
|
|
194
|
+
pending_versions: List of migration versions not yet applied.
|
|
195
|
+
applied_versions: List of migration versions already applied.
|
|
196
|
+
strict_ordering: If True, raise error for out-of-order migrations.
|
|
197
|
+
If False (default), log warning but allow.
|
|
198
|
+
use_logger: If True, emit warning via logger instead of console.
|
|
199
|
+
echo: Whether to echo output to the console.
|
|
200
|
+
summary_only: Whether summary-only logging is enabled.
|
|
201
|
+
|
|
202
|
+
Raises:
|
|
203
|
+
OutOfOrderMigrationError: If out-of-order migrations detected and
|
|
204
|
+
strict_ordering is True.
|
|
205
|
+
|
|
206
|
+
Example:
|
|
207
|
+
>>> validate_migration_order(
|
|
208
|
+
... ["20251011130000"],
|
|
209
|
+
... ["20251012140000"],
|
|
210
|
+
... strict_ordering=True,
|
|
211
|
+
... )
|
|
212
|
+
OutOfOrderMigrationError: Out-of-order migrations detected...
|
|
213
|
+
|
|
214
|
+
"""
|
|
215
|
+
gaps = detect_out_of_order_migrations(pending_versions, applied_versions)
|
|
216
|
+
|
|
217
|
+
if not gaps:
|
|
218
|
+
return
|
|
219
|
+
|
|
220
|
+
warning_message = format_out_of_order_warning(gaps)
|
|
221
|
+
|
|
222
|
+
if strict_ordering:
|
|
223
|
+
msg = f"{warning_message}\n\nStrict ordering is enabled. Use --allow-missing to override."
|
|
224
|
+
raise OutOfOrderMigrationError(msg)
|
|
225
|
+
|
|
226
|
+
if use_logger:
|
|
227
|
+
if not summary_only:
|
|
228
|
+
log_with_context(logger, logging.WARNING, "migration.order.warning", warning_message=warning_message)
|
|
229
|
+
return
|
|
230
|
+
if not echo:
|
|
231
|
+
return
|
|
232
|
+
console.print("[yellow]Out-of-order migrations detected[/]")
|
|
233
|
+
console.print(f"[yellow]{warning_message}[/]")
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def validate_squash_range(
|
|
237
|
+
migrations: "list[tuple[str, Path]]", start_version: str, end_version: str, *, allow_gaps: bool = False
|
|
238
|
+
) -> "list[tuple[str, Path]]":
|
|
239
|
+
"""Validate and filter migrations within a squash range.
|
|
240
|
+
|
|
241
|
+
Filters migrations to those within [start, end] range (inclusive),
|
|
242
|
+
validates the range exists, and optionally checks for gaps.
|
|
243
|
+
|
|
244
|
+
Args:
|
|
245
|
+
migrations: List of (version, path) tuples for all available migrations.
|
|
246
|
+
start_version: First version in the range to squash (inclusive).
|
|
247
|
+
end_version: Last version in the range to squash (inclusive).
|
|
248
|
+
allow_gaps: If True, skip gap detection. If False (default), raise error on gaps.
|
|
249
|
+
|
|
250
|
+
Returns:
|
|
251
|
+
Sorted list of (version, path) tuples within the range.
|
|
252
|
+
|
|
253
|
+
Raises:
|
|
254
|
+
SquashValidationError: If validation fails (invalid range, missing versions, gaps).
|
|
255
|
+
|
|
256
|
+
"""
|
|
257
|
+
if int(start_version) > int(end_version):
|
|
258
|
+
msg = f"Invalid range: start version {start_version} is greater than end version {end_version}"
|
|
259
|
+
raise SquashValidationError(msg)
|
|
260
|
+
|
|
261
|
+
version_map: dict[str, Path] = dict(migrations)
|
|
262
|
+
|
|
263
|
+
if start_version not in version_map:
|
|
264
|
+
msg = f"Start version {start_version} not found in migrations"
|
|
265
|
+
raise SquashValidationError(msg)
|
|
266
|
+
if end_version not in version_map:
|
|
267
|
+
msg = f"End version {end_version} not found in migrations"
|
|
268
|
+
raise SquashValidationError(msg)
|
|
269
|
+
|
|
270
|
+
start_int = int(start_version)
|
|
271
|
+
end_int = int(end_version)
|
|
272
|
+
result: list[tuple[str, Path]] = []
|
|
273
|
+
|
|
274
|
+
for version, path in migrations:
|
|
275
|
+
try:
|
|
276
|
+
version_int = int(version)
|
|
277
|
+
except ValueError:
|
|
278
|
+
continue
|
|
279
|
+
if start_int <= version_int <= end_int:
|
|
280
|
+
result.append((version, path))
|
|
281
|
+
|
|
282
|
+
result.sort(key=lambda x: int(x[0]))
|
|
283
|
+
|
|
284
|
+
if not result:
|
|
285
|
+
msg = f"No migrations found in range {start_version} to {end_version}"
|
|
286
|
+
raise SquashValidationError(msg)
|
|
287
|
+
|
|
288
|
+
if not allow_gaps and len(result) > 1:
|
|
289
|
+
sorted_versions = [int(v) for v, _ in result]
|
|
290
|
+
for i in range(1, len(sorted_versions)):
|
|
291
|
+
if sorted_versions[i] - sorted_versions[i - 1] != 1:
|
|
292
|
+
msg = f"Gap detected in version sequence between {sorted_versions[i - 1]:04d} and {sorted_versions[i]:04d}"
|
|
293
|
+
raise SquashValidationError(msg)
|
|
294
|
+
|
|
295
|
+
return result
|
|
296
|
+
|
|
297
|
+
|
|
298
|
+
def validate_extension_consistency(migrations: "list[tuple[str, Path]]") -> None:
|
|
299
|
+
"""Validate all migrations belong to same namespace (core or single extension).
|
|
300
|
+
|
|
301
|
+
Ensures migrations can be safely squashed together by verifying they all
|
|
302
|
+
belong to either core migrations or the same extension namespace.
|
|
303
|
+
|
|
304
|
+
Args:
|
|
305
|
+
migrations: List of (version, path) tuples to validate.
|
|
306
|
+
|
|
307
|
+
Raises:
|
|
308
|
+
SquashValidationError: If migrations mix core and extension, or different extensions.
|
|
309
|
+
|
|
310
|
+
"""
|
|
311
|
+
if not migrations:
|
|
312
|
+
return
|
|
313
|
+
|
|
314
|
+
extensions: set[str | None] = set()
|
|
315
|
+
|
|
316
|
+
for version, _ in migrations:
|
|
317
|
+
parsed = parse_version(version)
|
|
318
|
+
extensions.add(parsed.extension)
|
|
319
|
+
|
|
320
|
+
if len(extensions) > 1:
|
|
321
|
+
has_core = None in extensions
|
|
322
|
+
ext_names = [e for e in extensions if e is not None]
|
|
323
|
+
|
|
324
|
+
if has_core:
|
|
325
|
+
msg = f"Cannot squash migrations mixing core and extension ({', '.join(ext_names)})"
|
|
326
|
+
raise SquashValidationError(msg)
|
|
327
|
+
|
|
328
|
+
msg = f"Cannot squash migrations from different extensions: {', '.join(sorted(ext_names))}"
|
|
329
|
+
raise SquashValidationError(msg)
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def validate_squash_idempotency(source_files: "list[Path]", target_file: "Path") -> str:
|
|
333
|
+
"""Check if a squash operation has already been performed.
|
|
334
|
+
|
|
335
|
+
Determines the current state of a squash operation based on file existence.
|
|
336
|
+
|
|
337
|
+
Args:
|
|
338
|
+
source_files: List of original migration file paths that would be squashed.
|
|
339
|
+
target_file: Path where the squashed migration would be written.
|
|
340
|
+
|
|
341
|
+
Returns:
|
|
342
|
+
Status string: "ready" (can squash), "already_squashed" (already done),
|
|
343
|
+
or "partial" (inconsistent state - target exists but some sources remain).
|
|
344
|
+
|
|
345
|
+
"""
|
|
346
|
+
target_exists = target_file.exists()
|
|
347
|
+
sources_exist = [f.exists() for f in source_files]
|
|
348
|
+
any_source_exists = any(sources_exist)
|
|
349
|
+
|
|
350
|
+
if not target_exists and any_source_exists:
|
|
351
|
+
return "ready"
|
|
352
|
+
|
|
353
|
+
if target_exists and not any_source_exists:
|
|
354
|
+
return "already_squashed"
|
|
355
|
+
|
|
356
|
+
if target_exists and any_source_exists:
|
|
357
|
+
return "partial"
|
|
358
|
+
|
|
359
|
+
return "ready"
|
|
Binary file
|