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.
Files changed (621) hide show
  1. f68e0789eb443ecb1c2c__mypyc.cp314-win_amd64.pyd +0 -0
  2. sqlspec/__init__.py +167 -0
  3. sqlspec/__main__.py +12 -0
  4. sqlspec/__metadata__.py +14 -0
  5. sqlspec/_typing.py +714 -0
  6. sqlspec/adapters/__init__.py +0 -0
  7. sqlspec/adapters/adbc/__init__.py +13 -0
  8. sqlspec/adapters/adbc/_typing.py +106 -0
  9. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  10. sqlspec/adapters/adbc/adk/store.py +1280 -0
  11. sqlspec/adapters/adbc/config.py +378 -0
  12. sqlspec/adapters/adbc/core.cp314-win_amd64.pyd +0 -0
  13. sqlspec/adapters/adbc/core.py +922 -0
  14. sqlspec/adapters/adbc/data_dictionary.py +339 -0
  15. sqlspec/adapters/adbc/driver.py +534 -0
  16. sqlspec/adapters/adbc/events/__init__.py +5 -0
  17. sqlspec/adapters/adbc/events/store.py +285 -0
  18. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  19. sqlspec/adapters/adbc/litestar/store.py +534 -0
  20. sqlspec/adapters/adbc/type_converter.cp314-win_amd64.pyd +0 -0
  21. sqlspec/adapters/adbc/type_converter.py +142 -0
  22. sqlspec/adapters/aiomysql/__init__.py +21 -0
  23. sqlspec/adapters/aiomysql/_typing.py +137 -0
  24. sqlspec/adapters/aiomysql/adk/__init__.py +5 -0
  25. sqlspec/adapters/aiomysql/adk/store.py +678 -0
  26. sqlspec/adapters/aiomysql/config.py +305 -0
  27. sqlspec/adapters/aiomysql/core.cp314-win_amd64.pyd +0 -0
  28. sqlspec/adapters/aiomysql/core.py +536 -0
  29. sqlspec/adapters/aiomysql/data_dictionary.py +121 -0
  30. sqlspec/adapters/aiomysql/driver.py +386 -0
  31. sqlspec/adapters/aiomysql/events/__init__.py +5 -0
  32. sqlspec/adapters/aiomysql/events/store.py +104 -0
  33. sqlspec/adapters/aiomysql/litestar/__init__.py +5 -0
  34. sqlspec/adapters/aiomysql/litestar/store.py +314 -0
  35. sqlspec/adapters/aiosqlite/__init__.py +26 -0
  36. sqlspec/adapters/aiosqlite/_typing.py +109 -0
  37. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  38. sqlspec/adapters/aiosqlite/adk/store.py +829 -0
  39. sqlspec/adapters/aiosqlite/config.py +315 -0
  40. sqlspec/adapters/aiosqlite/core.cp314-win_amd64.pyd +0 -0
  41. sqlspec/adapters/aiosqlite/core.py +315 -0
  42. sqlspec/adapters/aiosqlite/data_dictionary.py +202 -0
  43. sqlspec/adapters/aiosqlite/driver.py +311 -0
  44. sqlspec/adapters/aiosqlite/events/__init__.py +5 -0
  45. sqlspec/adapters/aiosqlite/events/store.py +20 -0
  46. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  47. sqlspec/adapters/aiosqlite/litestar/store.py +279 -0
  48. sqlspec/adapters/aiosqlite/pool.cp314-win_amd64.pyd +0 -0
  49. sqlspec/adapters/aiosqlite/pool.py +734 -0
  50. sqlspec/adapters/asyncmy/__init__.py +21 -0
  51. sqlspec/adapters/asyncmy/_typing.py +113 -0
  52. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  53. sqlspec/adapters/asyncmy/adk/store.py +644 -0
  54. sqlspec/adapters/asyncmy/config.py +307 -0
  55. sqlspec/adapters/asyncmy/core.cp314-win_amd64.pyd +0 -0
  56. sqlspec/adapters/asyncmy/core.py +538 -0
  57. sqlspec/adapters/asyncmy/data_dictionary.py +122 -0
  58. sqlspec/adapters/asyncmy/driver.py +391 -0
  59. sqlspec/adapters/asyncmy/events/__init__.py +5 -0
  60. sqlspec/adapters/asyncmy/events/store.py +104 -0
  61. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  62. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  63. sqlspec/adapters/asyncpg/__init__.py +26 -0
  64. sqlspec/adapters/asyncpg/_typing.py +103 -0
  65. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  66. sqlspec/adapters/asyncpg/adk/store.py +483 -0
  67. sqlspec/adapters/asyncpg/config.py +575 -0
  68. sqlspec/adapters/asyncpg/core.cp314-win_amd64.pyd +0 -0
  69. sqlspec/adapters/asyncpg/core.py +480 -0
  70. sqlspec/adapters/asyncpg/data_dictionary.py +157 -0
  71. sqlspec/adapters/asyncpg/driver.py +487 -0
  72. sqlspec/adapters/asyncpg/events/__init__.py +6 -0
  73. sqlspec/adapters/asyncpg/events/_hub.py +181 -0
  74. sqlspec/adapters/asyncpg/events/backend.py +210 -0
  75. sqlspec/adapters/asyncpg/events/store.py +40 -0
  76. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  77. sqlspec/adapters/asyncpg/litestar/store.py +251 -0
  78. sqlspec/adapters/bigquery/__init__.py +15 -0
  79. sqlspec/adapters/bigquery/_typing.py +108 -0
  80. sqlspec/adapters/bigquery/config.py +362 -0
  81. sqlspec/adapters/bigquery/core.cp314-win_amd64.pyd +0 -0
  82. sqlspec/adapters/bigquery/core.py +768 -0
  83. sqlspec/adapters/bigquery/data_dictionary.py +120 -0
  84. sqlspec/adapters/bigquery/driver.py +542 -0
  85. sqlspec/adapters/bigquery/events/__init__.py +5 -0
  86. sqlspec/adapters/bigquery/events/store.py +139 -0
  87. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  88. sqlspec/adapters/bigquery/litestar/store.py +325 -0
  89. sqlspec/adapters/bigquery/type_converter.cp314-win_amd64.pyd +0 -0
  90. sqlspec/adapters/bigquery/type_converter.py +107 -0
  91. sqlspec/adapters/cockroach_asyncpg/__init__.py +26 -0
  92. sqlspec/adapters/cockroach_asyncpg/_typing.py +73 -0
  93. sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
  94. sqlspec/adapters/cockroach_asyncpg/adk/store.py +465 -0
  95. sqlspec/adapters/cockroach_asyncpg/config.py +248 -0
  96. sqlspec/adapters/cockroach_asyncpg/core.cp314-win_amd64.pyd +0 -0
  97. sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
  98. sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +110 -0
  99. sqlspec/adapters/cockroach_asyncpg/driver.py +142 -0
  100. sqlspec/adapters/cockroach_asyncpg/events/__init__.py +3 -0
  101. sqlspec/adapters/cockroach_asyncpg/events/store.py +20 -0
  102. sqlspec/adapters/cockroach_asyncpg/litestar/__init__.py +3 -0
  103. sqlspec/adapters/cockroach_asyncpg/litestar/store.py +142 -0
  104. sqlspec/adapters/cockroach_psycopg/__init__.py +39 -0
  105. sqlspec/adapters/cockroach_psycopg/_typing.py +137 -0
  106. sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
  107. sqlspec/adapters/cockroach_psycopg/adk/store.py +1039 -0
  108. sqlspec/adapters/cockroach_psycopg/config.py +511 -0
  109. sqlspec/adapters/cockroach_psycopg/core.cp314-win_amd64.pyd +0 -0
  110. sqlspec/adapters/cockroach_psycopg/core.py +63 -0
  111. sqlspec/adapters/cockroach_psycopg/data_dictionary.py +220 -0
  112. sqlspec/adapters/cockroach_psycopg/driver.py +273 -0
  113. sqlspec/adapters/cockroach_psycopg/events/__init__.py +6 -0
  114. sqlspec/adapters/cockroach_psycopg/events/store.py +34 -0
  115. sqlspec/adapters/cockroach_psycopg/litestar/__init__.py +3 -0
  116. sqlspec/adapters/cockroach_psycopg/litestar/store.py +327 -0
  117. sqlspec/adapters/duckdb/__init__.py +29 -0
  118. sqlspec/adapters/duckdb/_typing.py +104 -0
  119. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  120. sqlspec/adapters/duckdb/adk/store.py +935 -0
  121. sqlspec/adapters/duckdb/config.py +386 -0
  122. sqlspec/adapters/duckdb/core.cp314-win_amd64.pyd +0 -0
  123. sqlspec/adapters/duckdb/core.py +332 -0
  124. sqlspec/adapters/duckdb/data_dictionary.py +140 -0
  125. sqlspec/adapters/duckdb/driver.py +426 -0
  126. sqlspec/adapters/duckdb/events/__init__.py +5 -0
  127. sqlspec/adapters/duckdb/events/store.py +57 -0
  128. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  129. sqlspec/adapters/duckdb/litestar/store.py +330 -0
  130. sqlspec/adapters/duckdb/pool.cp314-win_amd64.pyd +0 -0
  131. sqlspec/adapters/duckdb/pool.py +350 -0
  132. sqlspec/adapters/duckdb/type_converter.cp314-win_amd64.pyd +0 -0
  133. sqlspec/adapters/duckdb/type_converter.py +118 -0
  134. sqlspec/adapters/mysqlconnector/__init__.py +39 -0
  135. sqlspec/adapters/mysqlconnector/_typing.py +186 -0
  136. sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
  137. sqlspec/adapters/mysqlconnector/adk/store.py +1183 -0
  138. sqlspec/adapters/mysqlconnector/config.py +421 -0
  139. sqlspec/adapters/mysqlconnector/core.cp314-win_amd64.pyd +0 -0
  140. sqlspec/adapters/mysqlconnector/core.py +472 -0
  141. sqlspec/adapters/mysqlconnector/data_dictionary.py +230 -0
  142. sqlspec/adapters/mysqlconnector/driver.py +516 -0
  143. sqlspec/adapters/mysqlconnector/events/__init__.py +8 -0
  144. sqlspec/adapters/mysqlconnector/events/store.py +98 -0
  145. sqlspec/adapters/mysqlconnector/litestar/__init__.py +5 -0
  146. sqlspec/adapters/mysqlconnector/litestar/store.py +426 -0
  147. sqlspec/adapters/oracledb/__init__.py +39 -0
  148. sqlspec/adapters/oracledb/_json_handlers.cp314-win_amd64.pyd +0 -0
  149. sqlspec/adapters/oracledb/_json_handlers.py +196 -0
  150. sqlspec/adapters/oracledb/_param_types.cp314-win_amd64.pyd +0 -0
  151. sqlspec/adapters/oracledb/_param_types.py +46 -0
  152. sqlspec/adapters/oracledb/_typing.py +258 -0
  153. sqlspec/adapters/oracledb/_uuid_handlers.cp314-win_amd64.pyd +0 -0
  154. sqlspec/adapters/oracledb/_uuid_handlers.py +163 -0
  155. sqlspec/adapters/oracledb/_vector_handlers.cp314-win_amd64.pyd +0 -0
  156. sqlspec/adapters/oracledb/_vector_handlers.py +228 -0
  157. sqlspec/adapters/oracledb/adk/__init__.py +21 -0
  158. sqlspec/adapters/oracledb/adk/store.py +2453 -0
  159. sqlspec/adapters/oracledb/config.py +575 -0
  160. sqlspec/adapters/oracledb/core.cp314-win_amd64.pyd +0 -0
  161. sqlspec/adapters/oracledb/core.py +820 -0
  162. sqlspec/adapters/oracledb/data_dictionary.py +404 -0
  163. sqlspec/adapters/oracledb/driver.py +1277 -0
  164. sqlspec/adapters/oracledb/events/__init__.py +16 -0
  165. sqlspec/adapters/oracledb/events/_hub.py +345 -0
  166. sqlspec/adapters/oracledb/events/backend.py +300 -0
  167. sqlspec/adapters/oracledb/events/store.py +420 -0
  168. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  169. sqlspec/adapters/oracledb/litestar/store.py +781 -0
  170. sqlspec/adapters/oracledb/migrations.py +539 -0
  171. sqlspec/adapters/oracledb/type_converter.cp314-win_amd64.pyd +0 -0
  172. sqlspec/adapters/oracledb/type_converter.py +211 -0
  173. sqlspec/adapters/psqlpy/__init__.py +18 -0
  174. sqlspec/adapters/psqlpy/_typing.py +121 -0
  175. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  176. sqlspec/adapters/psqlpy/adk/store.py +591 -0
  177. sqlspec/adapters/psqlpy/config.py +376 -0
  178. sqlspec/adapters/psqlpy/core.cp314-win_amd64.pyd +0 -0
  179. sqlspec/adapters/psqlpy/core.py +694 -0
  180. sqlspec/adapters/psqlpy/data_dictionary.py +121 -0
  181. sqlspec/adapters/psqlpy/driver.py +411 -0
  182. sqlspec/adapters/psqlpy/events/__init__.py +6 -0
  183. sqlspec/adapters/psqlpy/events/_hub.py +204 -0
  184. sqlspec/adapters/psqlpy/events/backend.py +210 -0
  185. sqlspec/adapters/psqlpy/events/store.py +20 -0
  186. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  187. sqlspec/adapters/psqlpy/litestar/store.py +270 -0
  188. sqlspec/adapters/psqlpy/type_converter.cp314-win_amd64.pyd +0 -0
  189. sqlspec/adapters/psqlpy/type_converter.py +113 -0
  190. sqlspec/adapters/psycopg/__init__.py +38 -0
  191. sqlspec/adapters/psycopg/_typing.py +218 -0
  192. sqlspec/adapters/psycopg/adk/__init__.py +10 -0
  193. sqlspec/adapters/psycopg/adk/store.py +1106 -0
  194. sqlspec/adapters/psycopg/config.py +695 -0
  195. sqlspec/adapters/psycopg/core.cp314-win_amd64.pyd +0 -0
  196. sqlspec/adapters/psycopg/core.py +520 -0
  197. sqlspec/adapters/psycopg/data_dictionary.py +278 -0
  198. sqlspec/adapters/psycopg/driver.py +1033 -0
  199. sqlspec/adapters/psycopg/events/__init__.py +20 -0
  200. sqlspec/adapters/psycopg/events/_hub.py +388 -0
  201. sqlspec/adapters/psycopg/events/backend.py +398 -0
  202. sqlspec/adapters/psycopg/events/store.py +42 -0
  203. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  204. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  205. sqlspec/adapters/psycopg/type_converter.cp314-win_amd64.pyd +0 -0
  206. sqlspec/adapters/psycopg/type_converter.py +93 -0
  207. sqlspec/adapters/pymysql/__init__.py +21 -0
  208. sqlspec/adapters/pymysql/_typing.py +92 -0
  209. sqlspec/adapters/pymysql/adk/__init__.py +5 -0
  210. sqlspec/adapters/pymysql/adk/store.py +657 -0
  211. sqlspec/adapters/pymysql/config.py +176 -0
  212. sqlspec/adapters/pymysql/core.cp314-win_amd64.pyd +0 -0
  213. sqlspec/adapters/pymysql/core.py +469 -0
  214. sqlspec/adapters/pymysql/data_dictionary.py +120 -0
  215. sqlspec/adapters/pymysql/driver.py +271 -0
  216. sqlspec/adapters/pymysql/events/__init__.py +5 -0
  217. sqlspec/adapters/pymysql/events/store.py +50 -0
  218. sqlspec/adapters/pymysql/litestar/__init__.py +5 -0
  219. sqlspec/adapters/pymysql/litestar/store.py +232 -0
  220. sqlspec/adapters/pymysql/pool.cp314-win_amd64.pyd +0 -0
  221. sqlspec/adapters/pymysql/pool.py +184 -0
  222. sqlspec/adapters/spanner/__init__.py +33 -0
  223. sqlspec/adapters/spanner/_typing.py +102 -0
  224. sqlspec/adapters/spanner/adk/__init__.py +5 -0
  225. sqlspec/adapters/spanner/adk/store.py +758 -0
  226. sqlspec/adapters/spanner/config.py +355 -0
  227. sqlspec/adapters/spanner/core.cp314-win_amd64.pyd +0 -0
  228. sqlspec/adapters/spanner/core.py +263 -0
  229. sqlspec/adapters/spanner/data_dictionary.py +120 -0
  230. sqlspec/adapters/spanner/driver.py +407 -0
  231. sqlspec/adapters/spanner/events/__init__.py +5 -0
  232. sqlspec/adapters/spanner/events/store.py +187 -0
  233. sqlspec/adapters/spanner/litestar/__init__.py +5 -0
  234. sqlspec/adapters/spanner/litestar/store.py +291 -0
  235. sqlspec/adapters/spanner/type_converter.cp314-win_amd64.pyd +0 -0
  236. sqlspec/adapters/spanner/type_converter.py +342 -0
  237. sqlspec/adapters/sqlite/__init__.py +19 -0
  238. sqlspec/adapters/sqlite/_typing.py +123 -0
  239. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  240. sqlspec/adapters/sqlite/adk/store.py +992 -0
  241. sqlspec/adapters/sqlite/config.py +240 -0
  242. sqlspec/adapters/sqlite/core.cp314-win_amd64.pyd +0 -0
  243. sqlspec/adapters/sqlite/core.py +357 -0
  244. sqlspec/adapters/sqlite/data_dictionary.py +198 -0
  245. sqlspec/adapters/sqlite/driver.py +527 -0
  246. sqlspec/adapters/sqlite/events/__init__.py +5 -0
  247. sqlspec/adapters/sqlite/events/store.py +20 -0
  248. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  249. sqlspec/adapters/sqlite/litestar/store.py +316 -0
  250. sqlspec/adapters/sqlite/pool.cp314-win_amd64.pyd +0 -0
  251. sqlspec/adapters/sqlite/pool.py +237 -0
  252. sqlspec/adapters/sqlite/type_converter.cp314-win_amd64.pyd +0 -0
  253. sqlspec/adapters/sqlite/type_converter.py +114 -0
  254. sqlspec/base.py +832 -0
  255. sqlspec/builder/__init__.py +181 -0
  256. sqlspec/builder/_base.cp314-win_amd64.pyd +0 -0
  257. sqlspec/builder/_base.py +1071 -0
  258. sqlspec/builder/_column.cp314-win_amd64.pyd +0 -0
  259. sqlspec/builder/_column.py +521 -0
  260. sqlspec/builder/_ddl.cp314-win_amd64.pyd +0 -0
  261. sqlspec/builder/_ddl.py +1691 -0
  262. sqlspec/builder/_delete.cp314-win_amd64.pyd +0 -0
  263. sqlspec/builder/_delete.py +95 -0
  264. sqlspec/builder/_dml.cp314-win_amd64.pyd +0 -0
  265. sqlspec/builder/_dml.py +386 -0
  266. sqlspec/builder/_explain.cp314-win_amd64.pyd +0 -0
  267. sqlspec/builder/_explain.py +579 -0
  268. sqlspec/builder/_expression_wrappers.cp314-win_amd64.pyd +0 -0
  269. sqlspec/builder/_expression_wrappers.py +46 -0
  270. sqlspec/builder/_factory.cp314-win_amd64.pyd +0 -0
  271. sqlspec/builder/_factory.py +1884 -0
  272. sqlspec/builder/_insert.cp314-win_amd64.pyd +0 -0
  273. sqlspec/builder/_insert.py +405 -0
  274. sqlspec/builder/_join.cp314-win_amd64.pyd +0 -0
  275. sqlspec/builder/_join.py +489 -0
  276. sqlspec/builder/_merge.cp314-win_amd64.pyd +0 -0
  277. sqlspec/builder/_merge.py +823 -0
  278. sqlspec/builder/_parsing_utils.cp314-win_amd64.pyd +0 -0
  279. sqlspec/builder/_parsing_utils.py +295 -0
  280. sqlspec/builder/_select.cp314-win_amd64.pyd +0 -0
  281. sqlspec/builder/_select.py +1666 -0
  282. sqlspec/builder/_temporal.cp314-win_amd64.pyd +0 -0
  283. sqlspec/builder/_temporal.py +167 -0
  284. sqlspec/builder/_update.cp314-win_amd64.pyd +0 -0
  285. sqlspec/builder/_update.py +173 -0
  286. sqlspec/builder/_vector_distance.cp314-win_amd64.pyd +0 -0
  287. sqlspec/builder/_vector_distance.py +330 -0
  288. sqlspec/cli.py +1095 -0
  289. sqlspec/config.py +2383 -0
  290. sqlspec/core/__init__.py +372 -0
  291. sqlspec/core/_correlation.cp314-win_amd64.pyd +0 -0
  292. sqlspec/core/_correlation.py +176 -0
  293. sqlspec/core/_pagination.py +42 -0
  294. sqlspec/core/_pool.cp314-win_amd64.pyd +0 -0
  295. sqlspec/core/_pool.py +76 -0
  296. sqlspec/core/cache.cp314-win_amd64.pyd +0 -0
  297. sqlspec/core/cache.py +1085 -0
  298. sqlspec/core/compiler.cp314-win_amd64.pyd +0 -0
  299. sqlspec/core/compiler.py +1090 -0
  300. sqlspec/core/config_runtime.cp314-win_amd64.pyd +0 -0
  301. sqlspec/core/config_runtime.py +174 -0
  302. sqlspec/core/explain.cp314-win_amd64.pyd +0 -0
  303. sqlspec/core/explain.py +275 -0
  304. sqlspec/core/filters.cp314-win_amd64.pyd +0 -0
  305. sqlspec/core/filters.py +969 -0
  306. sqlspec/core/hashing.cp314-win_amd64.pyd +0 -0
  307. sqlspec/core/hashing.py +266 -0
  308. sqlspec/core/metrics.cp314-win_amd64.pyd +0 -0
  309. sqlspec/core/metrics.py +83 -0
  310. sqlspec/core/parameters/__init__.py +72 -0
  311. sqlspec/core/parameters/_alignment.cp314-win_amd64.pyd +0 -0
  312. sqlspec/core/parameters/_alignment.py +283 -0
  313. sqlspec/core/parameters/_converter.cp314-win_amd64.pyd +0 -0
  314. sqlspec/core/parameters/_converter.py +554 -0
  315. sqlspec/core/parameters/_processor.cp314-win_amd64.pyd +0 -0
  316. sqlspec/core/parameters/_processor.py +1182 -0
  317. sqlspec/core/parameters/_registry.cp314-win_amd64.pyd +0 -0
  318. sqlspec/core/parameters/_registry.py +206 -0
  319. sqlspec/core/parameters/_transformers.cp314-win_amd64.pyd +0 -0
  320. sqlspec/core/parameters/_transformers.py +324 -0
  321. sqlspec/core/parameters/_types.cp314-win_amd64.pyd +0 -0
  322. sqlspec/core/parameters/_types.py +536 -0
  323. sqlspec/core/parameters/_validator.cp314-win_amd64.pyd +0 -0
  324. sqlspec/core/parameters/_validator.py +171 -0
  325. sqlspec/core/pipeline.cp314-win_amd64.pyd +0 -0
  326. sqlspec/core/pipeline.py +333 -0
  327. sqlspec/core/query_modifiers.cp314-win_amd64.pyd +0 -0
  328. sqlspec/core/query_modifiers.py +508 -0
  329. sqlspec/core/result/__init__.py +25 -0
  330. sqlspec/core/result/_base.cp314-win_amd64.pyd +0 -0
  331. sqlspec/core/result/_base.py +1232 -0
  332. sqlspec/core/result/_io.cp314-win_amd64.pyd +0 -0
  333. sqlspec/core/result/_io.py +28 -0
  334. sqlspec/core/splitter.cp314-win_amd64.pyd +0 -0
  335. sqlspec/core/splitter.py +1021 -0
  336. sqlspec/core/sqlcommenter.cp314-win_amd64.pyd +0 -0
  337. sqlspec/core/sqlcommenter.py +249 -0
  338. sqlspec/core/stack.cp314-win_amd64.pyd +0 -0
  339. sqlspec/core/stack.py +163 -0
  340. sqlspec/core/statement.cp314-win_amd64.pyd +0 -0
  341. sqlspec/core/statement.py +1865 -0
  342. sqlspec/core/type_converter.cp314-win_amd64.pyd +0 -0
  343. sqlspec/core/type_converter.py +340 -0
  344. sqlspec/data_dictionary/__init__.py +22 -0
  345. sqlspec/data_dictionary/_loader.cp314-win_amd64.pyd +0 -0
  346. sqlspec/data_dictionary/_loader.py +138 -0
  347. sqlspec/data_dictionary/_registry.cp314-win_amd64.pyd +0 -0
  348. sqlspec/data_dictionary/_registry.py +74 -0
  349. sqlspec/data_dictionary/_types.cp314-win_amd64.pyd +0 -0
  350. sqlspec/data_dictionary/_types.py +121 -0
  351. sqlspec/data_dictionary/dialects/__init__.py +21 -0
  352. sqlspec/data_dictionary/dialects/bigquery.cp314-win_amd64.pyd +0 -0
  353. sqlspec/data_dictionary/dialects/bigquery.py +81 -0
  354. sqlspec/data_dictionary/dialects/cockroachdb.cp314-win_amd64.pyd +0 -0
  355. sqlspec/data_dictionary/dialects/cockroachdb.py +54 -0
  356. sqlspec/data_dictionary/dialects/duckdb.cp314-win_amd64.pyd +0 -0
  357. sqlspec/data_dictionary/dialects/duckdb.py +47 -0
  358. sqlspec/data_dictionary/dialects/mysql.cp314-win_amd64.pyd +0 -0
  359. sqlspec/data_dictionary/dialects/mysql.py +53 -0
  360. sqlspec/data_dictionary/dialects/oracle.cp314-win_amd64.pyd +0 -0
  361. sqlspec/data_dictionary/dialects/oracle.py +197 -0
  362. sqlspec/data_dictionary/dialects/postgres.cp314-win_amd64.pyd +0 -0
  363. sqlspec/data_dictionary/dialects/postgres.py +69 -0
  364. sqlspec/data_dictionary/dialects/spanner.cp314-win_amd64.pyd +0 -0
  365. sqlspec/data_dictionary/dialects/spanner.py +37 -0
  366. sqlspec/data_dictionary/dialects/sqlite.cp314-win_amd64.pyd +0 -0
  367. sqlspec/data_dictionary/dialects/sqlite.py +59 -0
  368. sqlspec/data_dictionary/sql/.gitkeep +0 -0
  369. sqlspec/data_dictionary/sql/bigquery/columns.sql +23 -0
  370. sqlspec/data_dictionary/sql/bigquery/foreign_keys.sql +34 -0
  371. sqlspec/data_dictionary/sql/bigquery/indexes.sql +19 -0
  372. sqlspec/data_dictionary/sql/bigquery/tables.sql +33 -0
  373. sqlspec/data_dictionary/sql/bigquery/version.sql +3 -0
  374. sqlspec/data_dictionary/sql/cockroachdb/columns.sql +34 -0
  375. sqlspec/data_dictionary/sql/cockroachdb/foreign_keys.sql +40 -0
  376. sqlspec/data_dictionary/sql/cockroachdb/indexes.sql +32 -0
  377. sqlspec/data_dictionary/sql/cockroachdb/tables.sql +44 -0
  378. sqlspec/data_dictionary/sql/cockroachdb/version.sql +3 -0
  379. sqlspec/data_dictionary/sql/duckdb/columns.sql +23 -0
  380. sqlspec/data_dictionary/sql/duckdb/foreign_keys.sql +36 -0
  381. sqlspec/data_dictionary/sql/duckdb/indexes.sql +19 -0
  382. sqlspec/data_dictionary/sql/duckdb/tables.sql +38 -0
  383. sqlspec/data_dictionary/sql/duckdb/version.sql +3 -0
  384. sqlspec/data_dictionary/sql/mysql/columns.sql +23 -0
  385. sqlspec/data_dictionary/sql/mysql/foreign_keys.sql +28 -0
  386. sqlspec/data_dictionary/sql/mysql/indexes.sql +26 -0
  387. sqlspec/data_dictionary/sql/mysql/tables.sql +33 -0
  388. sqlspec/data_dictionary/sql/mysql/version.sql +3 -0
  389. sqlspec/data_dictionary/sql/oracle/columns.sql +23 -0
  390. sqlspec/data_dictionary/sql/oracle/foreign_keys.sql +48 -0
  391. sqlspec/data_dictionary/sql/oracle/indexes.sql +44 -0
  392. sqlspec/data_dictionary/sql/oracle/tables.sql +25 -0
  393. sqlspec/data_dictionary/sql/oracle/version.sql +20 -0
  394. sqlspec/data_dictionary/sql/postgres/columns.sql +34 -0
  395. sqlspec/data_dictionary/sql/postgres/foreign_keys.sql +40 -0
  396. sqlspec/data_dictionary/sql/postgres/indexes.sql +56 -0
  397. sqlspec/data_dictionary/sql/postgres/tables.sql +44 -0
  398. sqlspec/data_dictionary/sql/postgres/version.sql +3 -0
  399. sqlspec/data_dictionary/sql/spanner/columns.sql +23 -0
  400. sqlspec/data_dictionary/sql/spanner/foreign_keys.sql +70 -0
  401. sqlspec/data_dictionary/sql/spanner/indexes.sql +30 -0
  402. sqlspec/data_dictionary/sql/spanner/tables.sql +9 -0
  403. sqlspec/data_dictionary/sql/spanner/version.sql +3 -0
  404. sqlspec/data_dictionary/sql/sqlite/columns.sql +23 -0
  405. sqlspec/data_dictionary/sql/sqlite/foreign_keys.sql +22 -0
  406. sqlspec/data_dictionary/sql/sqlite/indexes.sql +7 -0
  407. sqlspec/data_dictionary/sql/sqlite/tables.sql +28 -0
  408. sqlspec/data_dictionary/sql/sqlite/version.sql +3 -0
  409. sqlspec/dialects/__init__.py +22 -0
  410. sqlspec/dialects/_compat.cp314-win_amd64.pyd +0 -0
  411. sqlspec/dialects/_compat.py +14 -0
  412. sqlspec/dialects/postgres/__init__.py +9 -0
  413. sqlspec/dialects/postgres/_generators.cp314-win_amd64.pyd +0 -0
  414. sqlspec/dialects/postgres/_generators.py +57 -0
  415. sqlspec/dialects/postgres/_operators.cp314-win_amd64.pyd +0 -0
  416. sqlspec/dialects/postgres/_operators.py +81 -0
  417. sqlspec/dialects/postgres/_paradedb.py +50 -0
  418. sqlspec/dialects/postgres/_pgvector.py +36 -0
  419. sqlspec/dialects/spanner/__init__.py +6 -0
  420. sqlspec/dialects/spanner/_generators.cp314-win_amd64.pyd +0 -0
  421. sqlspec/dialects/spanner/_generators.py +206 -0
  422. sqlspec/dialects/spanner/_spangres.py +77 -0
  423. sqlspec/dialects/spanner/_spanner.py +179 -0
  424. sqlspec/driver/__init__.py +49 -0
  425. sqlspec/driver/_async.cp314-win_amd64.pyd +0 -0
  426. sqlspec/driver/_async.py +1830 -0
  427. sqlspec/driver/_common.cp314-win_amd64.pyd +0 -0
  428. sqlspec/driver/_common.py +2292 -0
  429. sqlspec/driver/_exception_handler.cp314-win_amd64.pyd +0 -0
  430. sqlspec/driver/_exception_handler.py +108 -0
  431. sqlspec/driver/_query_cache.cp314-win_amd64.pyd +0 -0
  432. sqlspec/driver/_query_cache.py +96 -0
  433. sqlspec/driver/_sql_helpers.cp314-win_amd64.pyd +0 -0
  434. sqlspec/driver/_sql_helpers.py +139 -0
  435. sqlspec/driver/_storage_helpers.cp314-win_amd64.pyd +0 -0
  436. sqlspec/driver/_storage_helpers.py +153 -0
  437. sqlspec/driver/_sync.cp314-win_amd64.pyd +0 -0
  438. sqlspec/driver/_sync.py +1817 -0
  439. sqlspec/exceptions.cp314-win_amd64.pyd +0 -0
  440. sqlspec/exceptions.py +480 -0
  441. sqlspec/extensions/__init__.py +0 -0
  442. sqlspec/extensions/adk/__init__.py +84 -0
  443. sqlspec/extensions/adk/_config_utils.py +199 -0
  444. sqlspec/extensions/adk/_types.cp314-win_amd64.pyd +0 -0
  445. sqlspec/extensions/adk/_types.py +41 -0
  446. sqlspec/extensions/adk/artifact/__init__.py +57 -0
  447. sqlspec/extensions/adk/artifact/_types.cp314-win_amd64.pyd +0 -0
  448. sqlspec/extensions/adk/artifact/_types.py +32 -0
  449. sqlspec/extensions/adk/artifact/service.py +508 -0
  450. sqlspec/extensions/adk/artifact/store.py +361 -0
  451. sqlspec/extensions/adk/converters.py +212 -0
  452. sqlspec/extensions/adk/memory/__init__.py +69 -0
  453. sqlspec/extensions/adk/memory/_types.cp314-win_amd64.pyd +0 -0
  454. sqlspec/extensions/adk/memory/_types.py +30 -0
  455. sqlspec/extensions/adk/memory/converters.py +225 -0
  456. sqlspec/extensions/adk/memory/service.py +316 -0
  457. sqlspec/extensions/adk/memory/store.py +525 -0
  458. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +184 -0
  459. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  460. sqlspec/extensions/adk/service.py +279 -0
  461. sqlspec/extensions/adk/store.py +590 -0
  462. sqlspec/extensions/events/__init__.py +51 -0
  463. sqlspec/extensions/events/_channel.py +703 -0
  464. sqlspec/extensions/events/_hints.cp314-win_amd64.pyd +0 -0
  465. sqlspec/extensions/events/_hints.py +45 -0
  466. sqlspec/extensions/events/_models.py +23 -0
  467. sqlspec/extensions/events/_payload.cp314-win_amd64.pyd +0 -0
  468. sqlspec/extensions/events/_payload.py +69 -0
  469. sqlspec/extensions/events/_protocols.py +134 -0
  470. sqlspec/extensions/events/_queue.py +462 -0
  471. sqlspec/extensions/events/_store.py +209 -0
  472. sqlspec/extensions/events/migrations/0001_create_event_queue.py +59 -0
  473. sqlspec/extensions/events/migrations/__init__.py +3 -0
  474. sqlspec/extensions/fastapi/__init__.py +22 -0
  475. sqlspec/extensions/fastapi/extension.py +391 -0
  476. sqlspec/extensions/fastapi/providers.cp314-win_amd64.pyd +0 -0
  477. sqlspec/extensions/fastapi/providers.py +712 -0
  478. sqlspec/extensions/flask/__init__.py +38 -0
  479. sqlspec/extensions/flask/_state.py +87 -0
  480. sqlspec/extensions/flask/_utils.py +71 -0
  481. sqlspec/extensions/flask/extension.py +539 -0
  482. sqlspec/extensions/litestar/__init__.py +31 -0
  483. sqlspec/extensions/litestar/_utils.py +52 -0
  484. sqlspec/extensions/litestar/channels.py +165 -0
  485. sqlspec/extensions/litestar/cli.py +102 -0
  486. sqlspec/extensions/litestar/config.py +90 -0
  487. sqlspec/extensions/litestar/handlers.py +316 -0
  488. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  489. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  490. sqlspec/extensions/litestar/plugin.py +1066 -0
  491. sqlspec/extensions/litestar/providers.cp314-win_amd64.pyd +0 -0
  492. sqlspec/extensions/litestar/providers.py +784 -0
  493. sqlspec/extensions/litestar/store.py +298 -0
  494. sqlspec/extensions/otel/__init__.py +58 -0
  495. sqlspec/extensions/prometheus/__init__.py +113 -0
  496. sqlspec/extensions/sanic/__init__.py +19 -0
  497. sqlspec/extensions/sanic/_state.py +43 -0
  498. sqlspec/extensions/sanic/_utils.py +127 -0
  499. sqlspec/extensions/sanic/extension.py +647 -0
  500. sqlspec/extensions/starlette/__init__.py +22 -0
  501. sqlspec/extensions/starlette/_state.py +42 -0
  502. sqlspec/extensions/starlette/_utils.py +96 -0
  503. sqlspec/extensions/starlette/extension.py +374 -0
  504. sqlspec/extensions/starlette/middleware.py +281 -0
  505. sqlspec/loader.cp314-win_amd64.pyd +0 -0
  506. sqlspec/loader.py +727 -0
  507. sqlspec/migrations/__init__.py +39 -0
  508. sqlspec/migrations/base.cp314-win_amd64.pyd +0 -0
  509. sqlspec/migrations/base.py +862 -0
  510. sqlspec/migrations/commands.py +2151 -0
  511. sqlspec/migrations/context.cp314-win_amd64.pyd +0 -0
  512. sqlspec/migrations/context.py +157 -0
  513. sqlspec/migrations/fix.cp314-win_amd64.pyd +0 -0
  514. sqlspec/migrations/fix.py +204 -0
  515. sqlspec/migrations/loaders.cp314-win_amd64.pyd +0 -0
  516. sqlspec/migrations/loaders.py +443 -0
  517. sqlspec/migrations/runner.cp314-win_amd64.pyd +0 -0
  518. sqlspec/migrations/runner.py +1195 -0
  519. sqlspec/migrations/squash.cp314-win_amd64.pyd +0 -0
  520. sqlspec/migrations/squash.py +490 -0
  521. sqlspec/migrations/templates.cp314-win_amd64.pyd +0 -0
  522. sqlspec/migrations/templates.py +234 -0
  523. sqlspec/migrations/tracker.cp314-win_amd64.pyd +0 -0
  524. sqlspec/migrations/tracker.py +792 -0
  525. sqlspec/migrations/utils.cp314-win_amd64.pyd +0 -0
  526. sqlspec/migrations/utils.py +256 -0
  527. sqlspec/migrations/validation.cp314-win_amd64.pyd +0 -0
  528. sqlspec/migrations/validation.py +359 -0
  529. sqlspec/migrations/version.cp314-win_amd64.pyd +0 -0
  530. sqlspec/migrations/version.py +446 -0
  531. sqlspec/observability/__init__.py +57 -0
  532. sqlspec/observability/_common.cp314-win_amd64.pyd +0 -0
  533. sqlspec/observability/_common.py +77 -0
  534. sqlspec/observability/_config.cp314-win_amd64.pyd +0 -0
  535. sqlspec/observability/_config.py +364 -0
  536. sqlspec/observability/_diagnostics.cp314-win_amd64.pyd +0 -0
  537. sqlspec/observability/_diagnostics.py +74 -0
  538. sqlspec/observability/_dispatcher.cp314-win_amd64.pyd +0 -0
  539. sqlspec/observability/_dispatcher.py +200 -0
  540. sqlspec/observability/_formatters/__init__.py +13 -0
  541. sqlspec/observability/_formatters/_aws.cp314-win_amd64.pyd +0 -0
  542. sqlspec/observability/_formatters/_aws.py +102 -0
  543. sqlspec/observability/_formatters/_azure.cp314-win_amd64.pyd +0 -0
  544. sqlspec/observability/_formatters/_azure.py +96 -0
  545. sqlspec/observability/_formatters/_base.cp314-win_amd64.pyd +0 -0
  546. sqlspec/observability/_formatters/_base.py +57 -0
  547. sqlspec/observability/_formatters/_gcp.cp314-win_amd64.pyd +0 -0
  548. sqlspec/observability/_formatters/_gcp.py +131 -0
  549. sqlspec/observability/_formatting.py +58 -0
  550. sqlspec/observability/_observer.cp314-win_amd64.pyd +0 -0
  551. sqlspec/observability/_observer.py +361 -0
  552. sqlspec/observability/_runtime.cp314-win_amd64.pyd +0 -0
  553. sqlspec/observability/_runtime.py +461 -0
  554. sqlspec/observability/_sampling.cp314-win_amd64.pyd +0 -0
  555. sqlspec/observability/_sampling.py +188 -0
  556. sqlspec/observability/_spans.cp314-win_amd64.pyd +0 -0
  557. sqlspec/observability/_spans.py +161 -0
  558. sqlspec/protocols.py +955 -0
  559. sqlspec/py.typed +0 -0
  560. sqlspec/service.py +433 -0
  561. sqlspec/storage/__init__.py +48 -0
  562. sqlspec/storage/_arrow_payload.py +68 -0
  563. sqlspec/storage/_paths.cp314-win_amd64.pyd +0 -0
  564. sqlspec/storage/_paths.py +58 -0
  565. sqlspec/storage/_utils.py +46 -0
  566. sqlspec/storage/backends/__init__.py +1 -0
  567. sqlspec/storage/backends/base.cp314-win_amd64.pyd +0 -0
  568. sqlspec/storage/backends/base.py +374 -0
  569. sqlspec/storage/backends/fsspec.py +574 -0
  570. sqlspec/storage/backends/local.py +468 -0
  571. sqlspec/storage/backends/obstore.py +956 -0
  572. sqlspec/storage/errors.cp314-win_amd64.pyd +0 -0
  573. sqlspec/storage/errors.py +102 -0
  574. sqlspec/storage/pipeline.cp314-win_amd64.pyd +0 -0
  575. sqlspec/storage/pipeline.py +628 -0
  576. sqlspec/storage/registry.cp314-win_amd64.pyd +0 -0
  577. sqlspec/storage/registry.py +329 -0
  578. sqlspec/typing.py +405 -0
  579. sqlspec/utils/__init__.py +7 -0
  580. sqlspec/utils/arrow_helpers.py +384 -0
  581. sqlspec/utils/config_tools.cp314-win_amd64.pyd +0 -0
  582. sqlspec/utils/config_tools.py +314 -0
  583. sqlspec/utils/correlation.cp314-win_amd64.pyd +0 -0
  584. sqlspec/utils/correlation.py +134 -0
  585. sqlspec/utils/deprecation.cp314-win_amd64.pyd +0 -0
  586. sqlspec/utils/deprecation.py +157 -0
  587. sqlspec/utils/dispatch.cp314-win_amd64.pyd +0 -0
  588. sqlspec/utils/dispatch.py +101 -0
  589. sqlspec/utils/fixtures.cp314-win_amd64.pyd +0 -0
  590. sqlspec/utils/fixtures.py +260 -0
  591. sqlspec/utils/logging.cp314-win_amd64.pyd +0 -0
  592. sqlspec/utils/logging.py +251 -0
  593. sqlspec/utils/module_loader.py +306 -0
  594. sqlspec/utils/portal.cp314-win_amd64.pyd +0 -0
  595. sqlspec/utils/portal.py +377 -0
  596. sqlspec/utils/schema.cp314-win_amd64.pyd +0 -0
  597. sqlspec/utils/schema.py +1040 -0
  598. sqlspec/utils/serializers/__init__.py +30 -0
  599. sqlspec/utils/serializers/_json.cp314-win_amd64.pyd +0 -0
  600. sqlspec/utils/serializers/_json.py +415 -0
  601. sqlspec/utils/serializers/_numpy.cp314-win_amd64.pyd +0 -0
  602. sqlspec/utils/serializers/_numpy.py +65 -0
  603. sqlspec/utils/serializers/_schema.cp314-win_amd64.pyd +0 -0
  604. sqlspec/utils/serializers/_schema.py +285 -0
  605. sqlspec/utils/singleton.cp314-win_amd64.pyd +0 -0
  606. sqlspec/utils/singleton.py +41 -0
  607. sqlspec/utils/sync_tools.cp314-win_amd64.pyd +0 -0
  608. sqlspec/utils/sync_tools.py +316 -0
  609. sqlspec/utils/text.cp314-win_amd64.pyd +0 -0
  610. sqlspec/utils/text.py +109 -0
  611. sqlspec/utils/type_converters.cp314-win_amd64.pyd +0 -0
  612. sqlspec/utils/type_converters.py +216 -0
  613. sqlspec/utils/type_guards.cp314-win_amd64.pyd +0 -0
  614. sqlspec/utils/type_guards.py +1508 -0
  615. sqlspec/utils/uuids.cp314-win_amd64.pyd +0 -0
  616. sqlspec/utils/uuids.py +241 -0
  617. sqlspec-0.47.0.dist-info/METADATA +202 -0
  618. sqlspec-0.47.0.dist-info/RECORD +621 -0
  619. sqlspec-0.47.0.dist-info/WHEEL +4 -0
  620. sqlspec-0.47.0.dist-info/entry_points.txt +6 -0
  621. sqlspec-0.47.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,1195 @@
1
+ """Migration execution engine for SQLSpec."""
2
+
3
+ import ast
4
+ import hashlib
5
+ import inspect
6
+ import logging
7
+ import re
8
+ import time
9
+ from abc import ABC, abstractmethod
10
+ from pathlib import Path
11
+ from typing import TYPE_CHECKING, Any, Literal, cast, overload
12
+
13
+ from sqlspec.core import SQL
14
+ from sqlspec.loader import SQLFileLoader
15
+ from sqlspec.migrations.context import MigrationContext
16
+ from sqlspec.migrations.loaders import get_migration_loader
17
+ from sqlspec.migrations.templates import TemplateDescriptionHints
18
+ from sqlspec.migrations.version import parse_version
19
+ from sqlspec.observability import resolve_db_system
20
+ from sqlspec.utils.logging import get_logger, log_with_context
21
+ from sqlspec.utils.sync_tools import async_, await_
22
+
23
+ if TYPE_CHECKING:
24
+ from collections.abc import Awaitable, Callable
25
+
26
+ from sqlspec.config import DatabaseConfigProtocol
27
+ from sqlspec.driver import AsyncDriverAdapterBase, SyncDriverAdapterBase
28
+ from sqlspec.observability import ObservabilityRuntime
29
+
30
+ __all__ = ("AsyncMigrationRunner", "SyncMigrationRunner", "create_migration_runner")
31
+
32
+ logger = get_logger("sqlspec.migrations.runner")
33
+
34
+
35
+ class _CachedMigrationMetadata:
36
+ """Cached migration metadata keyed by file path."""
37
+
38
+ __slots__ = ("metadata", "mtime_ns", "size")
39
+
40
+ def __init__(self, metadata: "dict[str, Any]", mtime_ns: int, size: int) -> None:
41
+ self.metadata = metadata
42
+ self.mtime_ns = mtime_ns
43
+ self.size = size
44
+
45
+ def clone(self) -> "dict[str, Any]":
46
+ return dict(self.metadata)
47
+
48
+
49
+ class _MigrationFileEntry:
50
+ """Represents a migration file discovered during directory scanning."""
51
+
52
+ __slots__ = ("extension_name", "path")
53
+
54
+ def __init__(self, path: Path, extension_name: "str | None") -> None:
55
+ self.path = path
56
+ self.extension_name = extension_name
57
+
58
+
59
+ class BaseMigrationRunner(ABC):
60
+ """Base migration runner with common functionality shared between sync and async implementations."""
61
+
62
+ def __init__(
63
+ self,
64
+ migrations_path: Path,
65
+ extension_migrations: "dict[str, Path] | None" = None,
66
+ context: "MigrationContext | None" = None,
67
+ extension_configs: "dict[str, dict[str, Any]] | None" = None,
68
+ runtime: "ObservabilityRuntime | None" = None,
69
+ description_hints: "TemplateDescriptionHints | None" = None,
70
+ summary_only: bool = False,
71
+ use_logger: bool = False,
72
+ ) -> None:
73
+ """Initialize the migration runner.
74
+
75
+ Args:
76
+ migrations_path: Path to the directory containing migration files.
77
+ extension_migrations: Optional mapping of extension names to their migration paths.
78
+ context: Optional migration context for Python migrations.
79
+ extension_configs: Optional mapping of extension names to their configurations.
80
+ runtime: Observability runtime shared with command/context consumers.
81
+ description_hints: Hints for extracting migration descriptions.
82
+ summary_only: Whether summary-only logging is enabled.
83
+ use_logger: Whether to emit log output. Defaults to False (CLI mode).
84
+ """
85
+ self.migrations_path = migrations_path
86
+ self.extension_migrations = extension_migrations or {}
87
+ self.runtime = runtime
88
+ self.loader = SQLFileLoader(runtime=runtime)
89
+ self.project_root: Path | None = None
90
+ self.context = context
91
+ self.extension_configs = extension_configs or {}
92
+ self._listing_digest: str | None = None
93
+ self._listing_cache: list[tuple[str, Path]] | None = None
94
+ self._listing_signatures: dict[str, tuple[int, int]] = {}
95
+ self._metadata_cache: dict[str, _CachedMigrationMetadata] = {}
96
+ self.description_hints = description_hints or TemplateDescriptionHints()
97
+ self.summary_only = summary_only
98
+ self.use_logger = use_logger
99
+
100
+ def set_summary_only(self, value: bool) -> None:
101
+ """Set summary-only logging behavior for migration runner."""
102
+ self.summary_only = value
103
+
104
+ def set_use_logger(self, value: bool) -> None:
105
+ """Set whether to emit log output.
106
+
107
+ Args:
108
+ value: True to enable logging, False for silent mode (CLI default).
109
+ """
110
+ self.use_logger = value
111
+
112
+ def _log_migration_event(self, level: int, event: str, **extra_fields: Any) -> None:
113
+ """Log migration events, respecting use_logger and summary_only settings."""
114
+ if not self.use_logger:
115
+ return
116
+ if self.summary_only and level == logging.INFO:
117
+ return
118
+ log_with_context(logger, level, event, **extra_fields)
119
+
120
+ def _metric(self, name: str, amount: float = 1.0) -> None:
121
+ if self.runtime is None:
122
+ return
123
+ self.runtime.increment_metric(name, amount)
124
+
125
+ def _iter_directory_entries(self, base_path: Path, extension_name: "str | None") -> "list[_MigrationFileEntry]":
126
+ """Collect migration files discovered under a base path."""
127
+
128
+ if not base_path.exists():
129
+ return []
130
+
131
+ entries: list[_MigrationFileEntry] = []
132
+ for pattern in ("*.sql", "*.py"):
133
+ for file_path in sorted(base_path.glob(pattern)):
134
+ if file_path.name.startswith("."):
135
+ continue
136
+ entries.append(_MigrationFileEntry(path=file_path, extension_name=extension_name))
137
+ return entries
138
+
139
+ def _collect_listing_entries(self) -> "tuple[list[_MigrationFileEntry], dict[str, tuple[int, int]], str]":
140
+ """Gather migration files, stat signatures, and digest for cache validation."""
141
+
142
+ entries: list[_MigrationFileEntry] = []
143
+ signatures: dict[str, tuple[int, int]] = {}
144
+ digest_source = hashlib.md5(usedforsecurity=False)
145
+
146
+ for entry in self._iter_directory_entries(self.migrations_path, None):
147
+ self._record_entry(entry, entries, signatures, digest_source)
148
+
149
+ for ext_name, ext_path in self.extension_migrations.items():
150
+ for entry in self._iter_directory_entries(ext_path, ext_name):
151
+ self._record_entry(entry, entries, signatures, digest_source)
152
+
153
+ return entries, signatures, digest_source.hexdigest()
154
+
155
+ def _record_entry(
156
+ self,
157
+ entry: _MigrationFileEntry,
158
+ entries: "list[_MigrationFileEntry]",
159
+ signatures: "dict[str, tuple[int, int]]",
160
+ digest_source: Any,
161
+ ) -> None:
162
+ """Record entry metadata for cache decisions."""
163
+
164
+ try:
165
+ stat_result = entry.path.stat()
166
+ except FileNotFoundError:
167
+ return
168
+
169
+ path_str = str(entry.path)
170
+ token = (stat_result.st_mtime_ns, stat_result.st_size)
171
+ signatures[path_str] = token
172
+ digest_source.update(path_str.encode("utf-8"))
173
+ digest_source.update(f"{token[0]}:{token[1]}".encode())
174
+ entries.append(entry)
175
+
176
+ def _build_sorted_listing(self, entries: "list[_MigrationFileEntry]") -> "list[tuple[str, Path]]":
177
+ """Construct sorted migration listing from directory entries."""
178
+
179
+ migrations: list[tuple[str, Path]] = []
180
+
181
+ for entry in entries:
182
+ version = self._extract_version(entry.path.name)
183
+ if not version:
184
+ continue
185
+ if entry.extension_name:
186
+ version = f"ext_{entry.extension_name}_{version}"
187
+ migrations.append((version, entry.path))
188
+
189
+ def version_sort_key(migration_tuple: "tuple[str, Path]") -> "Any":
190
+ version_str = migration_tuple[0]
191
+ try:
192
+ return parse_version(version_str)
193
+ except ValueError:
194
+ return version_str
195
+
196
+ return sorted(migrations, key=version_sort_key)
197
+
198
+ def _log_listing_invalidation(
199
+ self, previous: "dict[str, tuple[int, int]]", current: "dict[str, tuple[int, int]]"
200
+ ) -> None:
201
+ """Log cache invalidation details at INFO level."""
202
+
203
+ prev_keys = set(previous)
204
+ curr_keys = set(current)
205
+ added = curr_keys - prev_keys
206
+ removed = prev_keys - curr_keys
207
+ modified = {key for key in prev_keys & curr_keys if previous[key] != current[key]}
208
+ self._log_migration_event(
209
+ logging.INFO,
210
+ "migration.listing.invalidated",
211
+ added_count=len(added),
212
+ removed_count=len(removed),
213
+ modified_count=len(modified),
214
+ )
215
+ self._metric("migrations.listing.cache_invalidations")
216
+ if added:
217
+ self._metric("migrations.listing.added", float(len(added)))
218
+ if removed:
219
+ self._metric("migrations.listing.removed", float(len(removed)))
220
+ if modified:
221
+ self._metric("migrations.listing.modified", float(len(modified)))
222
+
223
+ def _extract_version(self, filename: str) -> "str | None":
224
+ """Extract version from filename.
225
+
226
+ Supports sequential (0001), timestamp (20251011120000), and extension-prefixed
227
+ (ext_litestar_0001) version formats.
228
+
229
+ Args:
230
+ filename: The migration filename.
231
+
232
+ Returns:
233
+ The extracted version string or None.
234
+ """
235
+ extension_version_parts = 3
236
+ timestamp_min_length = 4
237
+
238
+ name_without_ext = filename.rsplit(".", 1)[0]
239
+
240
+ if name_without_ext.startswith("ext_"):
241
+ parts = name_without_ext.split("_", 3)
242
+ if len(parts) >= extension_version_parts:
243
+ return f"{parts[0]}_{parts[1]}_{parts[2]}"
244
+ return None
245
+
246
+ parts = name_without_ext.split("_", 1)
247
+ if parts and parts[0].isdigit():
248
+ return parts[0] if len(parts[0]) > timestamp_min_length else parts[0].zfill(4)
249
+
250
+ return None
251
+
252
+ def calculate_checksum(self, content: str) -> str:
253
+ """Calculate MD5 checksum of migration content.
254
+
255
+ Canonicalizes content by excluding query name headers that change during
256
+ fix command (migrate-{version}-up/down). This ensures checksums remain
257
+ stable when converting timestamp versions to sequential format.
258
+
259
+ Args:
260
+ content: The migration file content.
261
+
262
+ Returns:
263
+ MD5 checksum hex string.
264
+ """
265
+ canonical_content = re.sub(r"^--\s*name:\s*migrate-[^-]+-(?:up|down)\s*$", "", content, flags=re.MULTILINE)
266
+
267
+ return hashlib.md5(canonical_content.encode()).hexdigest() # noqa: S324
268
+
269
+ @abstractmethod
270
+ def load_migration(self, file_path: Path) -> "dict[str, Any] | Awaitable[dict[str, Any]]":
271
+ """Load a migration file and extract its components.
272
+
273
+ Args:
274
+ file_path: Path to the migration file.
275
+
276
+ Returns:
277
+ Dictionary containing migration metadata and queries.
278
+ For async implementations, returns a coroutine.
279
+ """
280
+
281
+ def _load_migration_listing(self) -> "list[tuple[str, Path]]":
282
+ """Build the cached migration listing shared by sync/async runners."""
283
+ entries, signatures, digest = self._collect_listing_entries()
284
+ cached_listing = self._listing_cache
285
+
286
+ if cached_listing is not None and self._listing_digest == digest:
287
+ self._metric("migrations.listing.cache_hit")
288
+ self._metric("migrations.listing.files_cached", float(len(cached_listing)))
289
+ self._log_migration_event(logging.DEBUG, "migration.listing.cache_hit", file_count=len(cached_listing))
290
+ return cached_listing
291
+
292
+ files = self._build_sorted_listing(entries)
293
+ previous_digest = self._listing_digest
294
+ previous_signatures = self._listing_signatures
295
+
296
+ self._metric("migrations.listing.cache_miss")
297
+ self._metric("migrations.listing.files_scanned", float(len(files)))
298
+
299
+ self._listing_cache = files
300
+ self._listing_signatures = signatures
301
+ self._listing_digest = digest
302
+
303
+ if previous_digest is None:
304
+ self._log_migration_event(logging.DEBUG, "migration.listing.cache_primed", file_count=len(files))
305
+ else:
306
+ self._log_listing_invalidation(previous_signatures, signatures)
307
+
308
+ return files
309
+
310
+ @abstractmethod
311
+ def get_migration_files(self) -> "list[tuple[str, Path]] | Awaitable[list[tuple[str, Path]]]":
312
+ """Get all migration files sorted by version."""
313
+
314
+ def _load_migration_metadata_common(self, file_path: Path, version: "str | None" = None) -> "dict[str, Any]":
315
+ """Load common migration metadata that doesn't require async operations.
316
+
317
+ Args:
318
+ file_path: Path to the migration file.
319
+ version: Optional pre-extracted version (preserves prefixes like ext_adk_0001).
320
+
321
+ Returns:
322
+ Partial migration metadata dictionary.
323
+ """
324
+ cache_key = str(file_path)
325
+ stat_result = file_path.stat()
326
+ cached_metadata = self._metadata_cache.get(cache_key)
327
+ if (
328
+ cached_metadata
329
+ and cached_metadata.mtime_ns == stat_result.st_mtime_ns
330
+ and cached_metadata.size == stat_result.st_size
331
+ ):
332
+ self._metric("migrations.metadata.cache_hit")
333
+ self._log_migration_event(logging.DEBUG, "migration.metadata.cache_hit", file_path=cache_key)
334
+ metadata = cached_metadata.clone()
335
+ metadata["file_path"] = file_path
336
+ return metadata
337
+
338
+ self._metric("migrations.metadata.cache_miss")
339
+ self._metric("migrations.metadata.bytes", float(stat_result.st_size))
340
+
341
+ content = file_path.read_text(encoding="utf-8")
342
+ checksum = self.calculate_checksum(content)
343
+ if version is None:
344
+ version = self._extract_version(file_path.name)
345
+ description = self._extract_description(content, file_path)
346
+ if not description:
347
+ description = file_path.stem.split("_", 1)[1] if "_" in file_path.stem else ""
348
+
349
+ transactional_match = re.search(
350
+ r"^--\s*transactional:\s*(true|false)\s*$", content, re.MULTILINE | re.IGNORECASE
351
+ )
352
+ transactional = None
353
+ if transactional_match:
354
+ transactional = transactional_match.group(1).lower() == "true"
355
+
356
+ metadata = {
357
+ "version": version,
358
+ "description": description,
359
+ "file_path": file_path,
360
+ "checksum": checksum,
361
+ "content": content,
362
+ "transactional": transactional,
363
+ }
364
+ self._metadata_cache[cache_key] = _CachedMigrationMetadata(
365
+ metadata=dict(metadata), mtime_ns=stat_result.st_mtime_ns, size=stat_result.st_size
366
+ )
367
+ if cached_metadata:
368
+ self._log_migration_event(logging.DEBUG, "migration.metadata.cache_invalidated", file_path=cache_key)
369
+ else:
370
+ self._log_migration_event(logging.DEBUG, "migration.metadata.cached", file_path=cache_key)
371
+ return metadata
372
+
373
+ def _extract_description(self, content: str, file_path: Path) -> str:
374
+ if file_path.suffix == ".sql":
375
+ return self._extract_sql_description(content)
376
+ if file_path.suffix == ".py":
377
+ return self._extract_python_description(content)
378
+ return ""
379
+
380
+ def _extract_sql_description(self, content: str) -> str:
381
+ keys = self.description_hints.sql_keys
382
+ for line in content.splitlines():
383
+ stripped = line.strip()
384
+ if not stripped:
385
+ continue
386
+ if stripped.startswith("--"):
387
+ body = stripped.lstrip("-").strip()
388
+ if not body:
389
+ continue
390
+ if ":" in body:
391
+ key, value = body.split(":", 1)
392
+ if key.strip() in keys:
393
+ return value.strip()
394
+ continue
395
+ break
396
+ return ""
397
+
398
+ def _extract_python_description(self, content: str) -> str:
399
+ try:
400
+ module = ast.parse(content)
401
+ except SyntaxError:
402
+ return ""
403
+ docstring = ast.get_docstring(module) or ""
404
+ keys = self.description_hints.python_keys
405
+ for line in docstring.splitlines():
406
+ stripped = line.strip()
407
+ if not stripped:
408
+ continue
409
+ if ":" in stripped:
410
+ key, value = stripped.split(":", 1)
411
+ if key.strip() in keys:
412
+ return value.strip()
413
+ return stripped
414
+ return ""
415
+
416
+ def _get_context_for_migration(self, file_path: Path) -> "MigrationContext | None":
417
+ """Get the appropriate context for a migration file.
418
+
419
+ Args:
420
+ file_path: Path to the migration file.
421
+
422
+ Returns:
423
+ Migration context to use, or None to use default.
424
+ """
425
+ context_to_use = self.context
426
+ if context_to_use and file_path.name.startswith("ext_"):
427
+ version = self._extract_version(file_path.name)
428
+ if version and version.startswith("ext_"):
429
+ min_extension_version_parts = 3
430
+ parts = version.split("_", 2)
431
+ if len(parts) >= min_extension_version_parts:
432
+ ext_name = parts[1]
433
+ if ext_name in self.extension_configs:
434
+ context_to_use = MigrationContext(
435
+ dialect=self.context.dialect if self.context else None,
436
+ config=self.context.config if self.context else None,
437
+ driver=self.context.driver if self.context else None,
438
+ metadata=self.context.metadata.copy() if self.context and self.context.metadata else {},
439
+ extension_config=self.extension_configs[ext_name],
440
+ )
441
+
442
+ for ext_name, ext_path in self.extension_migrations.items():
443
+ if file_path.parent == ext_path:
444
+ if ext_name in self.extension_configs and self.context:
445
+ context_to_use = MigrationContext(
446
+ config=self.context.config,
447
+ dialect=self.context.dialect,
448
+ driver=self.context.driver,
449
+ metadata=self.context.metadata.copy() if self.context.metadata else {},
450
+ extension_config=self.extension_configs[ext_name],
451
+ )
452
+ break
453
+
454
+ return context_to_use
455
+
456
+ def should_use_transaction(
457
+ self, migration: "dict[str, Any]", config: "DatabaseConfigProtocol[Any, Any, Any]"
458
+ ) -> bool:
459
+ """Determine if migration should run in a transaction.
460
+
461
+ Args:
462
+ migration: Migration metadata dictionary.
463
+ config: The database configuration instance.
464
+
465
+ Returns:
466
+ True if migration should be wrapped in a transaction.
467
+ """
468
+ if not config.supports_transactional_ddl:
469
+ return False
470
+
471
+ if migration.get("transactional") is not None:
472
+ return bool(migration["transactional"])
473
+
474
+ migration_config = cast("dict[str, Any]", config.migration_config) or {}
475
+ return bool(migration_config.get("transactional", True))
476
+
477
+
478
+ class SyncMigrationRunner(BaseMigrationRunner):
479
+ """Synchronous migration runner with pure sync methods."""
480
+
481
+ def get_migration_files(self) -> "list[tuple[str, Path]]":
482
+ """Get all migration files sorted by version.
483
+
484
+ Returns:
485
+ List of (version, path) tuples sorted by version.
486
+ """
487
+ return self._load_migration_listing()
488
+
489
+ def load_migration(self, file_path: Path, version: "str | None" = None) -> "dict[str, Any]":
490
+ """Load a migration file and extract its components.
491
+
492
+ Args:
493
+ file_path: Path to the migration file.
494
+ version: Optional pre-extracted version (preserves prefixes like ext_adk_0001).
495
+
496
+ Returns:
497
+ Dictionary containing migration metadata and queries.
498
+ """
499
+ metadata = self._load_migration_metadata_common(file_path, version)
500
+ context_to_use = self._get_context_for_migration(file_path)
501
+
502
+ loader = get_migration_loader(file_path, self.migrations_path, self.project_root, context_to_use, self.loader)
503
+ loader.validate_migration_file(file_path)
504
+
505
+ has_upgrade, has_downgrade = True, False
506
+
507
+ if file_path.suffix == ".sql":
508
+ version = metadata["version"]
509
+ up_query, down_query = f"migrate-{version}-up", f"migrate-{version}-down"
510
+ has_upgrade, has_downgrade = self.loader.has_query(up_query), self.loader.has_query(down_query)
511
+ else:
512
+ try:
513
+ has_downgrade = bool(self._get_migration_sql({"loader": loader, "file_path": file_path}, "down"))
514
+ except Exception:
515
+ has_downgrade = False
516
+
517
+ metadata.update({"has_upgrade": has_upgrade, "has_downgrade": has_downgrade, "loader": loader})
518
+ return metadata
519
+
520
+ def execute_upgrade(
521
+ self,
522
+ driver: "SyncDriverAdapterBase",
523
+ migration: "dict[str, Any]",
524
+ *,
525
+ use_transaction: "bool | None" = None,
526
+ on_success: "Callable[[int], None] | None" = None,
527
+ ) -> "tuple[str | None, int]":
528
+ """Execute an upgrade migration.
529
+
530
+ Args:
531
+ driver: The sync database driver to use.
532
+ migration: Migration metadata dictionary.
533
+ use_transaction: Override transaction behavior. If None, uses should_use_transaction logic.
534
+ on_success: Callback invoked with execution_time_ms before commit (for version tracking).
535
+
536
+ Returns:
537
+ Tuple of (sql_content, execution_time_ms).
538
+ """
539
+ upgrade_sql_list = self._get_migration_sql(migration, "up")
540
+ if upgrade_sql_list is None:
541
+ self._metric("migrations.upgrade.skipped")
542
+ self._log_migration_event(
543
+ logging.WARNING,
544
+ "migration.apply",
545
+ db_system=resolve_db_system(type(driver).__name__),
546
+ version=migration.get("version"),
547
+ status="missing",
548
+ )
549
+ return None, 0
550
+
551
+ if use_transaction is None:
552
+ config = self.context.config if self.context else None
553
+ use_transaction = self.should_use_transaction(migration, config) if config else False
554
+
555
+ runtime = self.runtime
556
+ span = None
557
+ if runtime is not None:
558
+ version = cast("str | None", migration.get("version"))
559
+ span = runtime.start_migration_span("upgrade", version=version)
560
+ runtime.increment_metric("migrations.upgrade.invocations")
561
+ self._log_migration_event(
562
+ logging.INFO,
563
+ "migration.apply",
564
+ db_system=resolve_db_system(type(driver).__name__),
565
+ version=migration.get("version"),
566
+ use_transaction=use_transaction,
567
+ status="start",
568
+ )
569
+
570
+ start_time = time.perf_counter()
571
+ execution_time = 0
572
+
573
+ try:
574
+ if use_transaction:
575
+ driver.begin()
576
+ for sql_statement in upgrade_sql_list:
577
+ if sql_statement.strip():
578
+ driver.execute_script(sql_statement)
579
+ execution_time = int((time.perf_counter() - start_time) * 1000)
580
+ if on_success:
581
+ on_success(execution_time)
582
+ driver.commit()
583
+ else:
584
+ for sql_statement in upgrade_sql_list:
585
+ if sql_statement.strip():
586
+ driver.execute_script(sql_statement)
587
+ execution_time = int((time.perf_counter() - start_time) * 1000)
588
+ if on_success:
589
+ on_success(execution_time)
590
+ except Exception as exc:
591
+ if use_transaction:
592
+ driver.rollback()
593
+ if runtime is not None:
594
+ duration_ms = int((time.perf_counter() - start_time) * 1000)
595
+ runtime.increment_metric("migrations.upgrade.errors")
596
+ runtime.end_migration_span(span, duration_ms=duration_ms, error=exc)
597
+ self._log_migration_event(
598
+ logging.ERROR,
599
+ "migration.apply",
600
+ db_system=resolve_db_system(type(driver).__name__),
601
+ version=migration.get("version"),
602
+ duration_ms=int((time.perf_counter() - start_time) * 1000),
603
+ error_type=type(exc).__name__,
604
+ status="failed",
605
+ )
606
+ raise
607
+
608
+ if runtime is not None:
609
+ runtime.increment_metric("migrations.upgrade.applied")
610
+ runtime.increment_metric("migrations.upgrade.duration_ms", float(execution_time))
611
+ runtime.end_migration_span(span, duration_ms=execution_time)
612
+ self._log_migration_event(
613
+ logging.INFO,
614
+ "migration.apply",
615
+ db_system=resolve_db_system(type(driver).__name__),
616
+ version=migration.get("version"),
617
+ duration_ms=execution_time,
618
+ status="complete",
619
+ )
620
+
621
+ return None, execution_time
622
+
623
+ def execute_downgrade(
624
+ self,
625
+ driver: "SyncDriverAdapterBase",
626
+ migration: "dict[str, Any]",
627
+ *,
628
+ use_transaction: "bool | None" = None,
629
+ on_success: "Callable[[int], None] | None" = None,
630
+ ) -> "tuple[str | None, int]":
631
+ """Execute a downgrade migration.
632
+
633
+ Args:
634
+ driver: The sync database driver to use.
635
+ migration: Migration metadata dictionary.
636
+ use_transaction: Override transaction behavior. If None, uses should_use_transaction logic.
637
+ on_success: Callback invoked with execution_time_ms before commit (for version tracking).
638
+
639
+ Returns:
640
+ Tuple of (sql_content, execution_time_ms).
641
+ """
642
+ downgrade_sql_list = self._get_migration_sql(migration, "down")
643
+ if downgrade_sql_list is None:
644
+ self._metric("migrations.downgrade.skipped")
645
+ self._log_migration_event(
646
+ logging.WARNING,
647
+ "migration.rollback",
648
+ db_system=resolve_db_system(type(driver).__name__),
649
+ version=migration.get("version"),
650
+ status="missing",
651
+ )
652
+ return None, 0
653
+
654
+ if use_transaction is None:
655
+ config = self.context.config if self.context else None
656
+ use_transaction = self.should_use_transaction(migration, config) if config else False
657
+
658
+ runtime = self.runtime
659
+ span = None
660
+ if runtime is not None:
661
+ version = cast("str | None", migration.get("version"))
662
+ span = runtime.start_migration_span("downgrade", version=version)
663
+ runtime.increment_metric("migrations.downgrade.invocations")
664
+ self._log_migration_event(
665
+ logging.INFO,
666
+ "migration.rollback",
667
+ db_system=resolve_db_system(type(driver).__name__),
668
+ version=migration.get("version"),
669
+ use_transaction=use_transaction,
670
+ status="start",
671
+ )
672
+
673
+ start_time = time.perf_counter()
674
+ execution_time = 0
675
+
676
+ try:
677
+ if use_transaction:
678
+ driver.begin()
679
+ for sql_statement in downgrade_sql_list:
680
+ if sql_statement.strip():
681
+ driver.execute_script(sql_statement)
682
+ execution_time = int((time.perf_counter() - start_time) * 1000)
683
+ if on_success:
684
+ on_success(execution_time)
685
+ driver.commit()
686
+ else:
687
+ for sql_statement in downgrade_sql_list:
688
+ if sql_statement.strip():
689
+ driver.execute_script(sql_statement)
690
+ execution_time = int((time.perf_counter() - start_time) * 1000)
691
+ if on_success:
692
+ on_success(execution_time)
693
+ except Exception as exc:
694
+ if use_transaction:
695
+ driver.rollback()
696
+ if runtime is not None:
697
+ duration_ms = int((time.perf_counter() - start_time) * 1000)
698
+ runtime.increment_metric("migrations.downgrade.errors")
699
+ runtime.end_migration_span(span, duration_ms=duration_ms, error=exc)
700
+ self._log_migration_event(
701
+ logging.ERROR,
702
+ "migration.rollback",
703
+ db_system=resolve_db_system(type(driver).__name__),
704
+ version=migration.get("version"),
705
+ duration_ms=int((time.perf_counter() - start_time) * 1000),
706
+ error_type=type(exc).__name__,
707
+ status="failed",
708
+ )
709
+ raise
710
+
711
+ if runtime is not None:
712
+ runtime.increment_metric("migrations.downgrade.applied")
713
+ runtime.increment_metric("migrations.downgrade.duration_ms", float(execution_time))
714
+ runtime.end_migration_span(span, duration_ms=execution_time)
715
+ self._log_migration_event(
716
+ logging.INFO,
717
+ "migration.rollback",
718
+ db_system=resolve_db_system(type(driver).__name__),
719
+ version=migration.get("version"),
720
+ duration_ms=execution_time,
721
+ status="complete",
722
+ )
723
+
724
+ return None, execution_time
725
+
726
+ def _get_migration_sql(self, migration: "dict[str, Any]", direction: str) -> "list[str] | None":
727
+ """Get migration SQL for given direction (sync version).
728
+
729
+ Args:
730
+ migration: Migration metadata.
731
+ direction: Either 'up' or 'down'.
732
+
733
+ Returns:
734
+ SQL statements for the migration.
735
+ """
736
+ # If this is being called during migration loading (no has_*grade field yet),
737
+ # don't raise/warn - just proceed to check if the method exists
738
+ if f"has_{direction}grade" in migration and not migration.get(f"has_{direction}grade"):
739
+ if direction == "down":
740
+ self._log_migration_event(
741
+ logging.WARNING, "migration.downgrade.missing", version=migration.get("version")
742
+ )
743
+ return None
744
+ msg = f"Migration {migration.get('version')} has no upgrade query"
745
+ raise ValueError(msg)
746
+
747
+ file_path, loader = migration["file_path"], migration["loader"]
748
+
749
+ try:
750
+ method = loader.get_up_sql if direction == "up" else loader.get_down_sql
751
+ sql_statements = (
752
+ await_(method, raise_sync_error=False)(file_path)
753
+ if inspect.iscoroutinefunction(method)
754
+ else method(file_path)
755
+ )
756
+
757
+ except Exception as e:
758
+ if direction == "down":
759
+ self._log_migration_event(
760
+ logging.WARNING, "migration.downgrade.load_failed", version=migration.get("version"), error=str(e)
761
+ )
762
+ return None
763
+ msg = f"Failed to load upgrade for migration {migration.get('version')}: {e}"
764
+ raise ValueError(msg) from e
765
+ else:
766
+ if sql_statements:
767
+ return cast("list[str]", sql_statements)
768
+ return None
769
+
770
+ def load_all_migrations(self) -> "dict[str, SQL]":
771
+ """Load all migrations into a single namespace for bulk operations.
772
+
773
+ Returns:
774
+ Dictionary mapping query names to SQL objects.
775
+ """
776
+ all_queries = {}
777
+ migrations = self.get_migration_files()
778
+
779
+ for version, file_path in migrations:
780
+ if file_path.suffix == ".sql":
781
+ self.loader.load_sql(file_path)
782
+ for query_name in self.loader.list_queries():
783
+ all_queries[query_name] = self.loader.get_sql(query_name)
784
+ else:
785
+ loader = get_migration_loader(
786
+ file_path, self.migrations_path, self.project_root, self.context, self.loader
787
+ )
788
+
789
+ try:
790
+ up_sql = await_(loader.get_up_sql, raise_sync_error=False)(file_path)
791
+ down_sql = await_(loader.get_down_sql, raise_sync_error=False)(file_path)
792
+
793
+ if up_sql:
794
+ all_queries[f"migrate-{version}-up"] = SQL(up_sql[0])
795
+ if down_sql:
796
+ all_queries[f"migrate-{version}-down"] = SQL(down_sql[0])
797
+
798
+ except Exception as e:
799
+ self._log_migration_event(
800
+ logging.DEBUG, "migration.python.load_failed", file_path=str(file_path), error=str(e)
801
+ )
802
+
803
+ return all_queries
804
+
805
+
806
+ class AsyncMigrationRunner(BaseMigrationRunner):
807
+ """Asynchronous migration runner with pure async methods."""
808
+
809
+ async def get_migration_files(self) -> "list[tuple[str, Path]]":
810
+ """Get all migration files sorted by version.
811
+
812
+ Returns:
813
+ List of (version, path) tuples sorted by version.
814
+ """
815
+ return await async_(self._load_migration_listing)()
816
+
817
+ async def load_migration(self, file_path: Path, version: "str | None" = None) -> "dict[str, Any]":
818
+ """Load a migration file and extract its components.
819
+
820
+ Args:
821
+ file_path: Path to the migration file.
822
+ version: Optional pre-extracted version (preserves prefixes like ext_adk_0001).
823
+
824
+ Returns:
825
+ Dictionary containing migration metadata and queries.
826
+ """
827
+ metadata = self._load_migration_metadata_common(file_path, version)
828
+ context_to_use = self._get_context_for_migration(file_path)
829
+
830
+ loader = get_migration_loader(file_path, self.migrations_path, self.project_root, context_to_use, self.loader)
831
+ loader.validate_migration_file(file_path)
832
+
833
+ has_upgrade, has_downgrade = True, False
834
+
835
+ if file_path.suffix == ".sql":
836
+ version = metadata["version"]
837
+ up_query, down_query = f"migrate-{version}-up", f"migrate-{version}-down"
838
+ has_upgrade, has_downgrade = self.loader.has_query(up_query), self.loader.has_query(down_query)
839
+ else:
840
+ try:
841
+ has_downgrade = bool(
842
+ await self._get_migration_sql_async({"loader": loader, "file_path": file_path}, "down")
843
+ )
844
+ except Exception:
845
+ has_downgrade = False
846
+
847
+ metadata.update({"has_upgrade": has_upgrade, "has_downgrade": has_downgrade, "loader": loader})
848
+ return metadata
849
+
850
+ async def execute_upgrade(
851
+ self,
852
+ driver: "AsyncDriverAdapterBase",
853
+ migration: "dict[str, Any]",
854
+ *,
855
+ use_transaction: "bool | None" = None,
856
+ on_success: "Callable[[int], Awaitable[None]] | None" = None,
857
+ ) -> "tuple[str | None, int]":
858
+ """Execute an upgrade migration.
859
+
860
+ Args:
861
+ driver: The async database driver to use.
862
+ migration: Migration metadata dictionary.
863
+ use_transaction: Override transaction behavior. If None, uses should_use_transaction logic.
864
+ on_success: Async callback invoked with execution_time_ms before commit (for version tracking).
865
+
866
+ Returns:
867
+ Tuple of (sql_content, execution_time_ms).
868
+ """
869
+ upgrade_sql_list = await self._get_migration_sql_async(migration, "up")
870
+ if upgrade_sql_list is None:
871
+ self._metric("migrations.upgrade.skipped")
872
+ self._log_migration_event(
873
+ logging.WARNING,
874
+ "migration.apply",
875
+ db_system=resolve_db_system(type(driver).__name__),
876
+ version=migration.get("version"),
877
+ status="missing",
878
+ )
879
+ return None, 0
880
+
881
+ if use_transaction is None:
882
+ config = self.context.config if self.context else None
883
+ use_transaction = self.should_use_transaction(migration, config) if config else False
884
+
885
+ runtime = self.runtime
886
+ span = None
887
+ if runtime is not None:
888
+ version = cast("str | None", migration.get("version"))
889
+ span = runtime.start_migration_span("upgrade", version=version)
890
+ runtime.increment_metric("migrations.upgrade.invocations")
891
+ self._log_migration_event(
892
+ logging.INFO,
893
+ "migration.apply",
894
+ db_system=resolve_db_system(type(driver).__name__),
895
+ version=migration.get("version"),
896
+ use_transaction=use_transaction,
897
+ status="start",
898
+ )
899
+
900
+ start_time = time.perf_counter()
901
+ execution_time = 0
902
+
903
+ try:
904
+ if use_transaction:
905
+ await driver.begin()
906
+ for sql_statement in upgrade_sql_list:
907
+ if sql_statement.strip():
908
+ await driver.execute_script(sql_statement)
909
+ execution_time = int((time.perf_counter() - start_time) * 1000)
910
+ if on_success:
911
+ await on_success(execution_time)
912
+ await driver.commit()
913
+ else:
914
+ for sql_statement in upgrade_sql_list:
915
+ if sql_statement.strip():
916
+ await driver.execute_script(sql_statement)
917
+ execution_time = int((time.perf_counter() - start_time) * 1000)
918
+ if on_success:
919
+ await on_success(execution_time)
920
+ except Exception as exc:
921
+ if use_transaction:
922
+ await driver.rollback()
923
+ if runtime is not None:
924
+ duration_ms = int((time.perf_counter() - start_time) * 1000)
925
+ runtime.increment_metric("migrations.upgrade.errors")
926
+ runtime.end_migration_span(span, duration_ms=duration_ms, error=exc)
927
+ self._log_migration_event(
928
+ logging.ERROR,
929
+ "migration.apply",
930
+ db_system=resolve_db_system(type(driver).__name__),
931
+ version=migration.get("version"),
932
+ duration_ms=int((time.perf_counter() - start_time) * 1000),
933
+ error_type=type(exc).__name__,
934
+ status="failed",
935
+ )
936
+ raise
937
+
938
+ if runtime is not None:
939
+ runtime.increment_metric("migrations.upgrade.applied")
940
+ runtime.increment_metric("migrations.upgrade.duration_ms", float(execution_time))
941
+ runtime.end_migration_span(span, duration_ms=execution_time)
942
+ self._log_migration_event(
943
+ logging.INFO,
944
+ "migration.apply",
945
+ db_system=resolve_db_system(type(driver).__name__),
946
+ version=migration.get("version"),
947
+ duration_ms=execution_time,
948
+ status="complete",
949
+ )
950
+
951
+ return None, execution_time
952
+
953
+ async def execute_downgrade(
954
+ self,
955
+ driver: "AsyncDriverAdapterBase",
956
+ migration: "dict[str, Any]",
957
+ *,
958
+ use_transaction: "bool | None" = None,
959
+ on_success: "Callable[[int], Awaitable[None]] | None" = None,
960
+ ) -> "tuple[str | None, int]":
961
+ """Execute a downgrade migration.
962
+
963
+ Args:
964
+ driver: The async database driver to use.
965
+ migration: Migration metadata dictionary.
966
+ use_transaction: Override transaction behavior. If None, uses should_use_transaction logic.
967
+ on_success: Async callback invoked with execution_time_ms before commit (for version tracking).
968
+
969
+ Returns:
970
+ Tuple of (sql_content, execution_time_ms).
971
+ """
972
+ downgrade_sql_list = await self._get_migration_sql_async(migration, "down")
973
+ if downgrade_sql_list is None:
974
+ self._metric("migrations.downgrade.skipped")
975
+ self._log_migration_event(
976
+ logging.WARNING,
977
+ "migration.rollback",
978
+ db_system=resolve_db_system(type(driver).__name__),
979
+ version=migration.get("version"),
980
+ status="missing",
981
+ )
982
+ return None, 0
983
+
984
+ if use_transaction is None:
985
+ config = self.context.config if self.context else None
986
+ use_transaction = self.should_use_transaction(migration, config) if config else False
987
+
988
+ runtime = self.runtime
989
+ span = None
990
+ if runtime is not None:
991
+ version = cast("str | None", migration.get("version"))
992
+ span = runtime.start_migration_span("downgrade", version=version)
993
+ runtime.increment_metric("migrations.downgrade.invocations")
994
+ self._log_migration_event(
995
+ logging.INFO,
996
+ "migration.rollback",
997
+ db_system=resolve_db_system(type(driver).__name__),
998
+ version=migration.get("version"),
999
+ use_transaction=use_transaction,
1000
+ status="start",
1001
+ )
1002
+
1003
+ start_time = time.perf_counter()
1004
+ execution_time = 0
1005
+
1006
+ try:
1007
+ if use_transaction:
1008
+ await driver.begin()
1009
+ for sql_statement in downgrade_sql_list:
1010
+ if sql_statement.strip():
1011
+ await driver.execute_script(sql_statement)
1012
+ execution_time = int((time.perf_counter() - start_time) * 1000)
1013
+ if on_success:
1014
+ await on_success(execution_time)
1015
+ await driver.commit()
1016
+ else:
1017
+ for sql_statement in downgrade_sql_list:
1018
+ if sql_statement.strip():
1019
+ await driver.execute_script(sql_statement)
1020
+ execution_time = int((time.perf_counter() - start_time) * 1000)
1021
+ if on_success:
1022
+ await on_success(execution_time)
1023
+ except Exception as exc:
1024
+ if use_transaction:
1025
+ await driver.rollback()
1026
+ if runtime is not None:
1027
+ duration_ms = int((time.perf_counter() - start_time) * 1000)
1028
+ runtime.increment_metric("migrations.downgrade.errors")
1029
+ runtime.end_migration_span(span, duration_ms=duration_ms, error=exc)
1030
+ self._log_migration_event(
1031
+ logging.ERROR,
1032
+ "migration.rollback",
1033
+ db_system=resolve_db_system(type(driver).__name__),
1034
+ version=migration.get("version"),
1035
+ duration_ms=int((time.perf_counter() - start_time) * 1000),
1036
+ error_type=type(exc).__name__,
1037
+ status="failed",
1038
+ )
1039
+ raise
1040
+
1041
+ if runtime is not None:
1042
+ runtime.increment_metric("migrations.downgrade.applied")
1043
+ runtime.increment_metric("migrations.downgrade.duration_ms", float(execution_time))
1044
+ runtime.end_migration_span(span, duration_ms=execution_time)
1045
+ self._log_migration_event(
1046
+ logging.INFO,
1047
+ "migration.rollback",
1048
+ db_system=resolve_db_system(type(driver).__name__),
1049
+ version=migration.get("version"),
1050
+ duration_ms=execution_time,
1051
+ status="complete",
1052
+ )
1053
+
1054
+ return None, execution_time
1055
+
1056
+ async def _get_migration_sql_async(self, migration: "dict[str, Any]", direction: str) -> "list[str] | None":
1057
+ """Get migration SQL for given direction (async version).
1058
+
1059
+ Args:
1060
+ migration: Migration metadata.
1061
+ direction: Either 'up' or 'down'.
1062
+
1063
+ Returns:
1064
+ SQL statements for the migration.
1065
+ """
1066
+ # If this is being called during migration loading (no has_*grade field yet),
1067
+ # don't raise/warn - just proceed to check if the method exists
1068
+ if f"has_{direction}grade" in migration and not migration.get(f"has_{direction}grade"):
1069
+ if direction == "down":
1070
+ self._log_migration_event(
1071
+ logging.WARNING, "migration.downgrade.missing", version=migration.get("version")
1072
+ )
1073
+ return None
1074
+ msg = f"Migration {migration.get('version')} has no upgrade query"
1075
+ raise ValueError(msg)
1076
+
1077
+ file_path, loader = migration["file_path"], migration["loader"]
1078
+
1079
+ try:
1080
+ method = loader.get_up_sql if direction == "up" else loader.get_down_sql
1081
+ sql_statements = await method(file_path)
1082
+
1083
+ except Exception as e:
1084
+ if direction == "down":
1085
+ self._log_migration_event(
1086
+ logging.WARNING, "migration.downgrade.load_failed", version=migration.get("version"), error=str(e)
1087
+ )
1088
+ return None
1089
+ msg = f"Failed to load upgrade for migration {migration.get('version')}: {e}"
1090
+ raise ValueError(msg) from e
1091
+ else:
1092
+ if sql_statements:
1093
+ return cast("list[str]", sql_statements)
1094
+ return None
1095
+
1096
+ async def load_all_migrations(self) -> "dict[str, SQL]":
1097
+ """Load all migrations into a single namespace for bulk operations.
1098
+
1099
+ Returns:
1100
+ Dictionary mapping query names to SQL objects.
1101
+ """
1102
+ all_queries = {}
1103
+ migrations = await self.get_migration_files()
1104
+
1105
+ for version, file_path in migrations:
1106
+ if file_path.suffix == ".sql":
1107
+ await async_(self.loader.load_sql)(file_path)
1108
+ for query_name in self.loader.list_queries():
1109
+ all_queries[query_name] = self.loader.get_sql(query_name)
1110
+ else:
1111
+ loader = get_migration_loader(
1112
+ file_path, self.migrations_path, self.project_root, self.context, self.loader
1113
+ )
1114
+
1115
+ try:
1116
+ up_sql = await loader.get_up_sql(file_path)
1117
+ down_sql = await loader.get_down_sql(file_path)
1118
+
1119
+ if up_sql:
1120
+ all_queries[f"migrate-{version}-up"] = SQL(up_sql[0])
1121
+ if down_sql:
1122
+ all_queries[f"migrate-{version}-down"] = SQL(down_sql[0])
1123
+
1124
+ except Exception as e:
1125
+ self._log_migration_event(
1126
+ logging.DEBUG, "migration.python.load_failed", file_path=str(file_path), error=str(e)
1127
+ )
1128
+
1129
+ return all_queries
1130
+
1131
+
1132
+ @overload
1133
+ def create_migration_runner(
1134
+ migrations_path: Path,
1135
+ extension_migrations: "dict[str, Path]",
1136
+ context: "MigrationContext | None",
1137
+ extension_configs: "dict[str, Any]",
1138
+ is_async: "Literal[False]" = False,
1139
+ runtime: "ObservabilityRuntime | None" = None,
1140
+ description_hints: "TemplateDescriptionHints | None" = None,
1141
+ ) -> SyncMigrationRunner: ...
1142
+
1143
+
1144
+ @overload
1145
+ def create_migration_runner(
1146
+ migrations_path: Path,
1147
+ extension_migrations: "dict[str, Path]",
1148
+ context: "MigrationContext | None",
1149
+ extension_configs: "dict[str, Any]",
1150
+ is_async: "Literal[True]",
1151
+ runtime: "ObservabilityRuntime | None" = None,
1152
+ description_hints: "TemplateDescriptionHints | None" = None,
1153
+ ) -> AsyncMigrationRunner: ...
1154
+
1155
+
1156
+ def create_migration_runner(
1157
+ migrations_path: Path,
1158
+ extension_migrations: "dict[str, Path]",
1159
+ context: "MigrationContext | None",
1160
+ extension_configs: "dict[str, Any]",
1161
+ is_async: bool = False,
1162
+ runtime: "ObservabilityRuntime | None" = None,
1163
+ description_hints: "TemplateDescriptionHints | None" = None,
1164
+ ) -> "SyncMigrationRunner | AsyncMigrationRunner":
1165
+ """Factory function to create the appropriate migration runner.
1166
+
1167
+ Args:
1168
+ migrations_path: Path to migrations directory.
1169
+ extension_migrations: Extension migration paths.
1170
+ context: Migration context.
1171
+ extension_configs: Extension configurations.
1172
+ is_async: Whether to create async or sync runner.
1173
+ runtime: Observability runtime shared with loaders and execution steps.
1174
+ description_hints: Optional description extraction hints from template profiles.
1175
+
1176
+ Returns:
1177
+ Appropriate migration runner instance.
1178
+ """
1179
+ if is_async:
1180
+ return AsyncMigrationRunner(
1181
+ migrations_path,
1182
+ extension_migrations,
1183
+ context,
1184
+ extension_configs,
1185
+ runtime=runtime,
1186
+ description_hints=description_hints,
1187
+ )
1188
+ return SyncMigrationRunner(
1189
+ migrations_path,
1190
+ extension_migrations,
1191
+ context,
1192
+ extension_configs,
1193
+ runtime=runtime,
1194
+ description_hints=description_hints,
1195
+ )