sqlspec 0.36.0__cp310-cp310-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl

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