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,1884 @@
1
+ """SQL factory for creating SQL builders and column expressions.
2
+
3
+ Provides statement builders (select, insert, update, etc.) and column expressions.
4
+ """
5
+
6
+ import hashlib
7
+ import logging
8
+ from collections.abc import Mapping, Sequence
9
+ from typing import TYPE_CHECKING, Any, TypeVar, Union, cast
10
+
11
+ import sqlglot
12
+ from sqlglot import exp
13
+ from sqlglot.dialects.dialect import DialectType
14
+ from sqlglot.errors import ParseError as SQLGlotParseError
15
+
16
+ from sqlspec.builder._base import QueryBuilder
17
+ from sqlspec.builder._column import Column
18
+ from sqlspec.builder._ddl import (
19
+ AlterTable,
20
+ CommentOn,
21
+ CreateIndex,
22
+ CreateMaterializedView,
23
+ CreateSchema,
24
+ CreateTable,
25
+ CreateTableAsSelect,
26
+ CreateView,
27
+ DropIndex,
28
+ DropMaterializedView,
29
+ DropSchema,
30
+ DropTable,
31
+ DropView,
32
+ RenameTable,
33
+ Truncate,
34
+ )
35
+ from sqlspec.builder._delete import Delete
36
+ from sqlspec.builder._explain import Explain
37
+ from sqlspec.builder._expression_wrappers import (
38
+ AggregateExpression,
39
+ ConversionExpression,
40
+ FunctionExpression,
41
+ MathExpression,
42
+ StringExpression,
43
+ )
44
+ from sqlspec.builder._insert import Insert
45
+ from sqlspec.builder._join import JoinBuilder, create_join_builder
46
+ from sqlspec.builder._merge import Merge
47
+ from sqlspec.builder._parsing_utils import extract_expression, to_expression
48
+ from sqlspec.builder._select import Case, Select, SubqueryBuilder, WindowFunctionBuilder
49
+ from sqlspec.builder._update import Update
50
+ from sqlspec.core import SQL
51
+ from sqlspec.core.explain import ExplainFormat, ExplainOptions
52
+ from sqlspec.exceptions import SQLBuilderError
53
+ from sqlspec.utils.logging import get_logger
54
+
55
+ if TYPE_CHECKING:
56
+ from collections.abc import Mapping, Sequence
57
+
58
+ from sqlspec.builder._expression_wrappers import ExpressionWrapper
59
+ from sqlspec.protocols import SQLBuilderProtocol
60
+
61
+
62
+ __all__ = (
63
+ "AlterTable",
64
+ "Case",
65
+ "Column",
66
+ "CommentOn",
67
+ "CreateIndex",
68
+ "CreateMaterializedView",
69
+ "CreateSchema",
70
+ "CreateTable",
71
+ "CreateTableAsSelect",
72
+ "CreateView",
73
+ "Delete",
74
+ "DropIndex",
75
+ "DropMaterializedView",
76
+ "DropSchema",
77
+ "DropTable",
78
+ "DropView",
79
+ "Explain",
80
+ "Insert",
81
+ "Merge",
82
+ "RenameTable",
83
+ "SQLFactory",
84
+ "Select",
85
+ "Truncate",
86
+ "Update",
87
+ "WindowFunctionBuilder",
88
+ "build_copy_from_statement",
89
+ "build_copy_statement",
90
+ "build_copy_to_statement",
91
+ "sql",
92
+ )
93
+
94
+ logger = get_logger("sqlspec.builder.factory")
95
+
96
+ BuilderT = TypeVar("BuilderT", bound=QueryBuilder)
97
+
98
+ MIN_SQL_LIKE_STRING_LENGTH = 6
99
+ MIN_DECODE_ARGS = 2
100
+ SQL_STARTERS = {
101
+ "SELECT",
102
+ "INSERT",
103
+ "UPDATE",
104
+ "DELETE",
105
+ "MERGE",
106
+ "WITH",
107
+ "CALL",
108
+ "DECLARE",
109
+ "BEGIN",
110
+ "END",
111
+ "CREATE",
112
+ "DROP",
113
+ "ALTER",
114
+ "TRUNCATE",
115
+ "RENAME",
116
+ "GRANT",
117
+ "REVOKE",
118
+ "SET",
119
+ "SHOW",
120
+ "USE",
121
+ "EXPLAIN",
122
+ "OPTIMIZE",
123
+ "VACUUM",
124
+ "COPY",
125
+ }
126
+
127
+
128
+ def _fingerprint_sql(sql: str) -> str:
129
+ digest = hashlib.sha256(sql.encode("utf-8", errors="replace")).hexdigest()
130
+ return digest[:12]
131
+
132
+
133
+ def _normalize_copy_dialect(dialect: DialectType | None) -> str:
134
+ if dialect is None:
135
+ return "postgres"
136
+ if isinstance(dialect, str):
137
+ return dialect
138
+ return str(dialect)
139
+
140
+
141
+ def _to_copy_schema(table: str, columns: "Sequence[str] | None") -> exp.Expr:
142
+ base = exp.table_(table)
143
+ if not columns:
144
+ return base
145
+ column_nodes = [exp.column(column_name) for column_name in columns]
146
+ return exp.Schema(this=base, expressions=column_nodes)
147
+
148
+
149
+ def _build_copy_expression(
150
+ *, direction: str, table: str, location: str, columns: "Sequence[str] | None", options: "Mapping[str, Any] | None"
151
+ ) -> exp.Copy:
152
+ copy_args: dict[str, Any] = {"this": _to_copy_schema(table, columns), "files": [exp.Literal.string(location)]}
153
+
154
+ if direction == "from":
155
+ copy_args["kind"] = True
156
+ elif direction == "to":
157
+ copy_args["kind"] = False
158
+
159
+ if options:
160
+ params: list[exp.CopyParameter] = []
161
+ for key, value in options.items():
162
+ identifier = exp.Var(this=str(key).upper())
163
+ value_expression: exp.Expr
164
+ if isinstance(value, bool):
165
+ value_expression = exp.Boolean(this=value)
166
+ elif value is None:
167
+ value_expression = exp.null()
168
+ elif isinstance(value, (int, float)):
169
+ value_expression = exp.Literal.number(value)
170
+ elif isinstance(value, (list, tuple)):
171
+ elements = [exp.Literal.string(str(item)) for item in value]
172
+ value_expression = exp.Array(expressions=elements)
173
+ else:
174
+ value_expression = exp.Literal.string(str(value))
175
+ params.append(exp.CopyParameter(this=identifier, expression=value_expression))
176
+ copy_args["params"] = params
177
+
178
+ return exp.Copy(**copy_args)
179
+
180
+
181
+ def build_copy_statement(
182
+ *,
183
+ direction: str,
184
+ table: str,
185
+ location: str,
186
+ columns: "Sequence[str] | None" = None,
187
+ options: "Mapping[str, Any] | None" = None,
188
+ dialect: DialectType | None = None,
189
+ ) -> SQL:
190
+ expression = _build_copy_expression(
191
+ direction=direction, table=table, location=location, columns=columns, options=options
192
+ )
193
+ rendered = expression.sql(dialect=_normalize_copy_dialect(dialect))
194
+ return SQL(rendered)
195
+
196
+
197
+ def build_copy_from_statement(
198
+ table: str,
199
+ source: str,
200
+ *,
201
+ columns: "Sequence[str] | None" = None,
202
+ options: "Mapping[str, Any] | None" = None,
203
+ dialect: DialectType | None = None,
204
+ ) -> SQL:
205
+ return build_copy_statement(
206
+ direction="from", table=table, location=source, columns=columns, options=options, dialect=dialect
207
+ )
208
+
209
+
210
+ def build_copy_to_statement(
211
+ table: str,
212
+ target: str,
213
+ *,
214
+ columns: "Sequence[str] | None" = None,
215
+ options: "Mapping[str, Any] | None" = None,
216
+ dialect: DialectType | None = None,
217
+ ) -> SQL:
218
+ return build_copy_statement(
219
+ direction="to", table=table, location=target, columns=columns, options=options, dialect=dialect
220
+ )
221
+
222
+
223
+ class SQLFactory:
224
+ """Factory for creating SQL builders and column expressions."""
225
+
226
+ @staticmethod
227
+ def _detect_type_from_expression(parsed_expr: exp.Expr) -> str:
228
+ if parsed_expr.key:
229
+ return parsed_expr.key.upper()
230
+ command_type = type(parsed_expr).__name__.upper()
231
+ if command_type == "COMMAND" and parsed_expr.this:
232
+ return str(parsed_expr.this).upper()
233
+ return command_type
234
+
235
+ @staticmethod
236
+ def _parse_sql_expression(sql: str, dialect: DialectType | None) -> "exp.Expr | None":
237
+ try:
238
+ return sqlglot.parse_one(sql, read=dialect)
239
+ except SQLGlotParseError:
240
+ if logger.isEnabledFor(logging.DEBUG):
241
+ logger.debug(
242
+ "Failed to parse SQL for type detection",
243
+ extra={"sql_length": len(sql), "sql_hash": _fingerprint_sql(sql)},
244
+ )
245
+ except (ValueError, TypeError, AttributeError):
246
+ if logger.isEnabledFor(logging.DEBUG):
247
+ logger.debug(
248
+ "Unexpected error during SQL type detection",
249
+ exc_info=True,
250
+ extra={"sql_length": len(sql), "sql_hash": _fingerprint_sql(sql)},
251
+ )
252
+ return None
253
+
254
+ @classmethod
255
+ def detect_sql_type(cls, sql: str, dialect: DialectType = None) -> str:
256
+ parsed_expr = cls._parse_sql_expression(sql, dialect)
257
+ if parsed_expr is None:
258
+ return "COMMAND"
259
+ return cls._detect_type_from_expression(parsed_expr)
260
+
261
+ def __init__(self, dialect: DialectType = None) -> None:
262
+ """Initialize the SQL factory.
263
+
264
+ Args:
265
+ dialect: Default SQL dialect to use for all builders.
266
+ """
267
+ self.dialect = dialect
268
+
269
+ def __call__(self, statement: str, dialect: DialectType = None) -> "Any":
270
+ """Create a SelectBuilder from a SQL string, or SQL object for DML with RETURNING.
271
+
272
+ Args:
273
+ statement: The SQL statement string.
274
+ dialect: Optional SQL dialect.
275
+
276
+ Returns:
277
+ SelectBuilder instance for SELECT/WITH statements,
278
+ SQL object for DML statements with RETURNING clause.
279
+
280
+ Raises:
281
+ SQLBuilderError: If the SQL is not a SELECT/CTE/DML+RETURNING statement.
282
+ """
283
+
284
+ try:
285
+ parsed_expr = sqlglot.parse_one(statement, read=dialect or self.dialect)
286
+ except Exception as e:
287
+ msg = f"Failed to parse SQL: {e}"
288
+ raise SQLBuilderError(msg) from e
289
+ actual_type = type(parsed_expr).__name__.upper()
290
+ expr_type_map = {
291
+ "SELECT": "SELECT",
292
+ "INSERT": "INSERT",
293
+ "UPDATE": "UPDATE",
294
+ "DELETE": "DELETE",
295
+ "MERGE": "MERGE",
296
+ "WITH": "WITH",
297
+ }
298
+ actual_type_str = expr_type_map.get(actual_type, actual_type)
299
+ if actual_type_str == "SELECT" or (
300
+ actual_type_str == "WITH" and parsed_expr.this and isinstance(parsed_expr.this, exp.Select)
301
+ ):
302
+ builder = Select(dialect=dialect or self.dialect)
303
+ builder.set_expression(parsed_expr)
304
+ return builder
305
+
306
+ if actual_type_str in {"INSERT", "UPDATE", "DELETE"} and parsed_expr.args.get("returning") is not None:
307
+ return SQL(statement)
308
+
309
+ msg = (
310
+ f"sql(...) only supports SELECT statements or DML statements with RETURNING clause. "
311
+ f"Detected type: {actual_type_str}. "
312
+ f"Use sql.{actual_type_str.lower()}() instead."
313
+ )
314
+ raise SQLBuilderError(msg)
315
+
316
+ def select(
317
+ self, *columns_or_sql: Union[str, exp.Expr, Column, "SQL", "Case"], dialect: DialectType = None
318
+ ) -> "Select":
319
+ builder_dialect = dialect or self.dialect
320
+ if len(columns_or_sql) == 1 and isinstance(columns_or_sql[0], str):
321
+ sql_candidate = columns_or_sql[0].strip()
322
+ if self._looks_like_sql(sql_candidate):
323
+ parsed_expr = self._parse_sql_expression(sql_candidate, builder_dialect)
324
+ detected = "COMMAND" if parsed_expr is None else self._detect_type_from_expression(parsed_expr)
325
+ if detected not in {"SELECT", "WITH"}:
326
+ msg = (
327
+ f"sql.select() expects a SELECT or WITH statement, got {detected}. "
328
+ f"Use sql.{detected.lower()}() if a dedicated builder exists, or ensure the SQL is SELECT/WITH."
329
+ )
330
+ raise SQLBuilderError(msg)
331
+ select_builder = Select(dialect=builder_dialect)
332
+ return self._populate_select_from_sql(select_builder, sql_candidate, parsed_expr)
333
+ select_builder = Select(dialect=builder_dialect)
334
+ if columns_or_sql:
335
+ select_builder.select(*columns_or_sql)
336
+ return select_builder
337
+
338
+ def insert(self, table_or_sql: str | None = None, dialect: DialectType = None) -> "Insert":
339
+ builder_dialect = dialect or self.dialect
340
+ builder = Insert(dialect=builder_dialect)
341
+ if table_or_sql:
342
+ if self._looks_like_sql(table_or_sql):
343
+ parsed_expr = self._parse_sql_expression(table_or_sql, builder_dialect)
344
+ detected = "COMMAND" if parsed_expr is None else self._detect_type_from_expression(parsed_expr)
345
+ if detected not in {"INSERT", "SELECT"}:
346
+ msg = (
347
+ f"sql.insert() expects INSERT or SELECT (for insert-from-select), got {detected}. "
348
+ f"Use sql.{detected.lower()}() if a dedicated builder exists, "
349
+ f"or ensure the SQL is INSERT/SELECT."
350
+ )
351
+ raise SQLBuilderError(msg)
352
+ return self._populate_insert_from_sql(builder, table_or_sql, parsed_expr)
353
+ return builder.into(table_or_sql)
354
+ return builder
355
+
356
+ def update(self, table_or_sql: str | None = None, dialect: DialectType = None) -> "Update":
357
+ builder_dialect = dialect or self.dialect
358
+ builder = Update(dialect=builder_dialect)
359
+ if table_or_sql:
360
+ if self._looks_like_sql(table_or_sql):
361
+ parsed_expr = self._parse_sql_expression(table_or_sql, builder_dialect)
362
+ detected = "COMMAND" if parsed_expr is None else self._detect_type_from_expression(parsed_expr)
363
+ if detected != "UPDATE":
364
+ msg = (
365
+ f"sql.update() expects UPDATE statement, got {detected}. "
366
+ f"Use sql.{detected.lower()}() if a dedicated builder exists."
367
+ )
368
+ raise SQLBuilderError(msg)
369
+ return self._populate_update_from_sql(builder, table_or_sql, parsed_expr)
370
+ return builder.table(table_or_sql)
371
+ return builder
372
+
373
+ def delete(self, table_or_sql: str | None = None, dialect: DialectType = None) -> "Delete":
374
+ builder_dialect = dialect or self.dialect
375
+ if table_or_sql and self._looks_like_sql(table_or_sql):
376
+ builder = Delete(dialect=builder_dialect)
377
+ parsed_expr = self._parse_sql_expression(table_or_sql, builder_dialect)
378
+ detected = "COMMAND" if parsed_expr is None else self._detect_type_from_expression(parsed_expr)
379
+ if detected != "DELETE":
380
+ msg = (
381
+ f"sql.delete() expects DELETE statement, got {detected}. "
382
+ f"Use sql.{detected.lower()}() if a dedicated builder exists."
383
+ )
384
+ raise SQLBuilderError(msg)
385
+ return self._populate_delete_from_sql(builder, table_or_sql, parsed_expr)
386
+
387
+ return Delete(table_or_sql, dialect=builder_dialect) if table_or_sql else Delete(dialect=builder_dialect)
388
+
389
+ def merge(self, table_or_sql: str | None = None, dialect: DialectType = None) -> "Merge":
390
+ builder_dialect = dialect or self.dialect
391
+ if table_or_sql and self._looks_like_sql(table_or_sql):
392
+ builder = Merge(dialect=builder_dialect)
393
+ parsed_expr = self._parse_sql_expression(table_or_sql, builder_dialect)
394
+ detected = "COMMAND" if parsed_expr is None else self._detect_type_from_expression(parsed_expr)
395
+ if detected != "MERGE":
396
+ msg = (
397
+ f"sql.merge() expects MERGE statement, got {detected}. "
398
+ f"Use sql.{detected.lower()}() if a dedicated builder exists."
399
+ )
400
+ raise SQLBuilderError(msg)
401
+ return self._populate_merge_from_sql(builder, table_or_sql, parsed_expr)
402
+
403
+ return Merge(table_or_sql, dialect=builder_dialect) if table_or_sql else Merge(dialect=builder_dialect)
404
+
405
+ def explain(
406
+ self,
407
+ statement: "str | exp.Expr | SQL | SQLBuilderProtocol",
408
+ *,
409
+ analyze: bool = False,
410
+ verbose: bool = False,
411
+ format: "ExplainFormat | str | None" = None,
412
+ dialect: DialectType = None,
413
+ ) -> "Explain":
414
+ """Create an EXPLAIN builder for a SQL statement.
415
+
416
+ Wraps any SQL statement in an EXPLAIN clause with dialect-aware
417
+ syntax generation.
418
+
419
+ Args:
420
+ statement: SQL statement to explain (string, expression, SQL object, or builder)
421
+ analyze: Execute the statement and show actual runtime statistics
422
+ verbose: Show additional information
423
+ format: Output format (TEXT, JSON, XML, YAML, TREE, TRADITIONAL)
424
+ dialect: Optional SQL dialect override
425
+
426
+ Returns:
427
+ Explain builder for further configuration
428
+
429
+ Examples:
430
+ Basic EXPLAIN:
431
+ plan = sql.explain("SELECT * FROM users").build()
432
+
433
+ With options:
434
+ plan = (
435
+ sql.explain("SELECT * FROM users", analyze=True, format="json")
436
+ .buffers()
437
+ .timing()
438
+ .build()
439
+ )
440
+
441
+ From QueryBuilder:
442
+ query = sql.select("*").from_("users").where("id = :id", id=1)
443
+ plan = sql.explain(query, analyze=True).build()
444
+
445
+ Chained configuration:
446
+ plan = (
447
+ sql.explain(sql.select("*").from_("large_table"))
448
+ .analyze()
449
+ .format("json")
450
+ .buffers()
451
+ .timing()
452
+ .build()
453
+ )
454
+ """
455
+ builder_dialect = dialect or self.dialect
456
+
457
+ fmt = None
458
+ if format is not None:
459
+ fmt = ExplainFormat(format.lower()) if isinstance(format, str) else format
460
+
461
+ options = ExplainOptions(analyze=analyze, verbose=verbose, format=fmt)
462
+
463
+ return Explain(statement, dialect=builder_dialect, options=options)
464
+
465
+ @property
466
+ def merge_(self) -> "Merge":
467
+ """Create a new MERGE builder (property shorthand).
468
+
469
+ Property that returns a new Merge builder instance using the factory's
470
+ default dialect. Cleaner syntax alternative to merge() method.
471
+
472
+ Examples:
473
+ query = sql.merge_.into("products").using(data, alias="src")
474
+ query = sql.merge_.into("products", alias="t").on("t.id = src.id")
475
+
476
+ Returns:
477
+ New Merge builder instance
478
+ """
479
+ return Merge(dialect=self.dialect)
480
+
481
+ def upsert(self, table: str, dialect: DialectType = None) -> "Merge | Insert":
482
+ """Create an upsert builder (MERGE or INSERT ON CONFLICT).
483
+
484
+ Automatically selects the appropriate builder based on database dialect:
485
+ - PostgreSQL 15+, Oracle, BigQuery: Returns MERGE builder
486
+ - SQLite, DuckDB, MySQL: Returns INSERT builder with ON CONFLICT support
487
+
488
+ Args:
489
+ table: Target table name
490
+ dialect: Optional SQL dialect (uses factory default if not provided)
491
+
492
+ Returns:
493
+ MERGE builder for supported databases, INSERT builder otherwise
494
+
495
+ Examples:
496
+ PostgreSQL/Oracle/BigQuery (uses MERGE):
497
+ upsert_query = (
498
+ sql.upsert("products", dialect="postgres")
499
+ .using([{"id": 1, "name": "Product 1"}], alias="src")
500
+ .on("t.id = src.id")
501
+ .when_matched_then_update(name="src.name")
502
+ .when_not_matched_then_insert(id="src.id", name="src.name")
503
+ )
504
+
505
+ SQLite/DuckDB/MySQL (uses INSERT ON CONFLICT):
506
+ upsert_query = (
507
+ sql.upsert("products", dialect="sqlite")
508
+ .values(id=1, name="Product 1")
509
+ .on_conflict("id")
510
+ .do_update(name="EXCLUDED.name")
511
+ )
512
+ """
513
+ builder_dialect = dialect or self.dialect
514
+ dialect_str = str(builder_dialect).lower() if builder_dialect else None
515
+
516
+ merge_supported = {"postgres", "postgresql", "oracle", "bigquery"}
517
+
518
+ if dialect_str in merge_supported:
519
+ return self.merge(table, dialect=builder_dialect)
520
+
521
+ return self.insert(table, dialect=builder_dialect)
522
+
523
+ def create_table(self, table_name: str, dialect: DialectType = None) -> "CreateTable":
524
+ """Create a CREATE TABLE builder.
525
+
526
+ Args:
527
+ table_name: Name of the table to create
528
+ dialect: Optional SQL dialect
529
+
530
+ Returns:
531
+ CreateTable builder instance
532
+ """
533
+ return CreateTable(table_name, dialect=dialect or self.dialect)
534
+
535
+ def create_table_as_select(self, dialect: DialectType = None) -> "CreateTableAsSelect":
536
+ """Create a CREATE TABLE AS SELECT builder.
537
+
538
+ Args:
539
+ dialect: Optional SQL dialect
540
+
541
+ Returns:
542
+ CreateTableAsSelect builder instance
543
+ """
544
+ return CreateTableAsSelect(dialect=dialect or self.dialect)
545
+
546
+ def create_view(self, view_name: str, dialect: DialectType = None) -> "CreateView":
547
+ """Create a CREATE VIEW builder.
548
+
549
+ Args:
550
+ view_name: Name of the view to create
551
+ dialect: Optional SQL dialect
552
+
553
+ Returns:
554
+ CreateView builder instance
555
+ """
556
+ return CreateView(view_name, dialect=dialect or self.dialect)
557
+
558
+ def create_materialized_view(self, view_name: str, dialect: DialectType = None) -> "CreateMaterializedView":
559
+ """Create a CREATE MATERIALIZED VIEW builder.
560
+
561
+ Args:
562
+ view_name: Name of the materialized view to create
563
+ dialect: Optional SQL dialect
564
+
565
+ Returns:
566
+ CreateMaterializedView builder instance
567
+ """
568
+ return CreateMaterializedView(view_name, dialect=dialect or self.dialect)
569
+
570
+ def create_index(self, index_name: str, dialect: DialectType = None) -> "CreateIndex":
571
+ """Create a CREATE INDEX builder.
572
+
573
+ Args:
574
+ index_name: Name of the index to create
575
+ dialect: Optional SQL dialect
576
+
577
+ Returns:
578
+ CreateIndex builder instance
579
+ """
580
+ return CreateIndex(index_name, dialect=dialect or self.dialect)
581
+
582
+ def create_schema(self, schema_name: str, dialect: DialectType = None) -> "CreateSchema":
583
+ """Create a CREATE SCHEMA builder.
584
+
585
+ Args:
586
+ schema_name: Name of the schema to create
587
+ dialect: Optional SQL dialect
588
+
589
+ Returns:
590
+ CreateSchema builder instance
591
+ """
592
+ return CreateSchema(schema_name, dialect=dialect or self.dialect)
593
+
594
+ def drop_table(self, table_name: str, dialect: DialectType = None) -> "DropTable":
595
+ """Create a DROP TABLE builder.
596
+
597
+ Args:
598
+ table_name: Name of the table to drop
599
+ dialect: Optional SQL dialect
600
+
601
+ Returns:
602
+ DropTable builder instance
603
+ """
604
+ return DropTable(table_name, dialect=dialect or self.dialect)
605
+
606
+ def drop_view(self, view_name: str, dialect: DialectType = None) -> "DropView":
607
+ """Create a DROP VIEW builder.
608
+
609
+ Args:
610
+ view_name: Name of the view to drop
611
+ dialect: Optional SQL dialect
612
+
613
+ Returns:
614
+ DropView builder instance
615
+ """
616
+ return DropView(view_name, dialect=dialect or self.dialect)
617
+
618
+ def drop_materialized_view(self, view_name: str, dialect: DialectType = None) -> "DropMaterializedView":
619
+ """Create a DROP MATERIALIZED VIEW builder.
620
+
621
+ Args:
622
+ view_name: Name of the materialized view to drop
623
+ dialect: Optional SQL dialect
624
+
625
+ Returns:
626
+ DropMaterializedView builder instance
627
+ """
628
+ return DropMaterializedView(view_name, dialect=dialect or self.dialect)
629
+
630
+ def drop_index(self, index_name: str, dialect: DialectType = None) -> "DropIndex":
631
+ """Create a DROP INDEX builder.
632
+
633
+ Args:
634
+ index_name: Name of the index to drop
635
+ dialect: Optional SQL dialect
636
+
637
+ Returns:
638
+ DropIndex builder instance
639
+ """
640
+ return DropIndex(index_name, dialect=dialect or self.dialect)
641
+
642
+ def drop_schema(self, schema_name: str, dialect: DialectType = None) -> "DropSchema":
643
+ """Create a DROP SCHEMA builder.
644
+
645
+ Args:
646
+ schema_name: Name of the schema to drop
647
+ dialect: Optional SQL dialect
648
+
649
+ Returns:
650
+ DropSchema builder instance
651
+ """
652
+ return DropSchema(schema_name, dialect=dialect or self.dialect)
653
+
654
+ def alter_table(self, table_name: str, dialect: DialectType = None) -> "AlterTable":
655
+ """Create an ALTER TABLE builder.
656
+
657
+ Args:
658
+ table_name: Name of the table to alter
659
+ dialect: Optional SQL dialect
660
+
661
+ Returns:
662
+ AlterTable builder instance
663
+ """
664
+ return AlterTable(table_name, dialect=dialect or self.dialect)
665
+
666
+ def rename_table(self, old_name: str, dialect: DialectType = None) -> "RenameTable":
667
+ """Create a RENAME TABLE builder.
668
+
669
+ Args:
670
+ old_name: Current name of the table
671
+ dialect: Optional SQL dialect
672
+
673
+ Returns:
674
+ RenameTable builder instance
675
+ """
676
+ return RenameTable(old_name, dialect=dialect or self.dialect)
677
+
678
+ def comment_on(self, dialect: DialectType = None) -> "CommentOn":
679
+ """Create a COMMENT ON builder.
680
+
681
+ Args:
682
+ dialect: Optional SQL dialect
683
+
684
+ Returns:
685
+ CommentOn builder instance
686
+ """
687
+ return CommentOn(dialect=dialect or self.dialect)
688
+
689
+ def copy_from(
690
+ self,
691
+ table: str,
692
+ source: str,
693
+ *,
694
+ columns: "Sequence[str] | None" = None,
695
+ options: "Mapping[str, Any] | None" = None,
696
+ dialect: DialectType | None = None,
697
+ ) -> SQL:
698
+ """Build a COPY ... FROM statement."""
699
+
700
+ effective_dialect = dialect or self.dialect
701
+ return build_copy_from_statement(table, source, columns=columns, options=options, dialect=effective_dialect)
702
+
703
+ def copy_to(
704
+ self,
705
+ table: str,
706
+ target: str,
707
+ *,
708
+ columns: "Sequence[str] | None" = None,
709
+ options: "Mapping[str, Any] | None" = None,
710
+ dialect: DialectType | None = None,
711
+ ) -> SQL:
712
+ """Build a COPY ... TO statement."""
713
+
714
+ effective_dialect = dialect or self.dialect
715
+ return build_copy_to_statement(table, target, columns=columns, options=options, dialect=effective_dialect)
716
+
717
+ def copy(
718
+ self,
719
+ table: str,
720
+ *,
721
+ source: str | None = None,
722
+ target: str | None = None,
723
+ columns: "Sequence[str] | None" = None,
724
+ options: "Mapping[str, Any] | None" = None,
725
+ dialect: DialectType | None = None,
726
+ ) -> SQL:
727
+ """Build a COPY statement, inferring direction from provided arguments."""
728
+
729
+ if (source is None and target is None) or (source is not None and target is not None):
730
+ msg = "Provide either 'source' or 'target' (but not both) to sql.copy()."
731
+ raise SQLBuilderError(msg)
732
+
733
+ if source is not None:
734
+ return self.copy_from(table, source, columns=columns, options=options, dialect=dialect)
735
+
736
+ target_value = cast("str", target)
737
+ return self.copy_to(table, target_value, columns=columns, options=options, dialect=dialect)
738
+
739
+ @staticmethod
740
+ def _looks_like_sql(candidate: str, expected_type: str | None = None) -> bool:
741
+ """Determine if a string looks like SQL.
742
+
743
+ Args:
744
+ candidate: String to check
745
+ expected_type: Expected SQL statement type (SELECT, INSERT, etc.)
746
+
747
+ Returns:
748
+ True if the string appears to be SQL
749
+ """
750
+ if not candidate or len(candidate.strip()) < MIN_SQL_LIKE_STRING_LENGTH:
751
+ return False
752
+
753
+ candidate_upper = candidate.strip().upper()
754
+
755
+ if expected_type:
756
+ return candidate_upper.startswith(expected_type.upper())
757
+
758
+ if any(candidate_upper.startswith(starter) for starter in SQL_STARTERS):
759
+ return " " in candidate
760
+
761
+ return False
762
+
763
+ def _populate_builder_from_sql(
764
+ self, builder: BuilderT, sql_string: str, expected_type: type[exp.Expr], parsed_expr: "exp.Expr | None" = None
765
+ ) -> BuilderT:
766
+ """Parse SQL string and populate a builder using SQLGlot directly."""
767
+ builder_name = expected_type.__name__.lower()
768
+ try:
769
+ if parsed_expr is None:
770
+ parsed_expr = exp.maybe_parse(sql_string, dialect=self.dialect)
771
+
772
+ if expected_type is exp.Select and isinstance(parsed_expr, exp.With):
773
+ base_expression = parsed_expr.this
774
+ if isinstance(builder, Select) and isinstance(base_expression, exp.Select):
775
+ builder.set_expression(base_expression)
776
+ builder.load_ctes(list(parsed_expr.expressions))
777
+ return builder
778
+
779
+ if isinstance(parsed_expr, expected_type):
780
+ builder.set_expression(parsed_expr)
781
+ return builder
782
+
783
+ if expected_type is exp.Insert and isinstance(parsed_expr, exp.Select):
784
+ logger.debug(
785
+ "Detected SELECT statement for INSERT; builder requires explicit target table",
786
+ extra={"builder": "insert"},
787
+ )
788
+ return builder
789
+
790
+ logger.debug(
791
+ "Cannot create %s from parsed statement type",
792
+ builder_name.upper(),
793
+ extra={"builder": builder_name, "parsed_type": type(parsed_expr).__name__},
794
+ )
795
+
796
+ except Exception:
797
+ logger.debug(
798
+ "Failed to parse %s SQL; falling back to traditional mode",
799
+ builder_name.upper(),
800
+ exc_info=True,
801
+ extra={"builder": builder_name},
802
+ )
803
+ return builder
804
+
805
+ def _populate_insert_from_sql(
806
+ self, builder: "Insert", sql_string: str, parsed_expr: "exp.Expr | None" = None
807
+ ) -> "Insert":
808
+ """Parse SQL string and populate INSERT builder using SQLGlot directly."""
809
+ return self._populate_builder_from_sql(builder, sql_string, exp.Insert, parsed_expr)
810
+
811
+ def _populate_select_from_sql(
812
+ self, builder: "Select", sql_string: str, parsed_expr: "exp.Expr | None" = None
813
+ ) -> "Select":
814
+ """Parse SQL string and populate SELECT builder using SQLGlot directly."""
815
+ return self._populate_builder_from_sql(builder, sql_string, exp.Select, parsed_expr)
816
+
817
+ def _populate_update_from_sql(
818
+ self, builder: "Update", sql_string: str, parsed_expr: "exp.Expr | None" = None
819
+ ) -> "Update":
820
+ """Parse SQL string and populate UPDATE builder using SQLGlot directly."""
821
+ return self._populate_builder_from_sql(builder, sql_string, exp.Update, parsed_expr)
822
+
823
+ def _populate_delete_from_sql(
824
+ self, builder: "Delete", sql_string: str, parsed_expr: "exp.Expr | None" = None
825
+ ) -> "Delete":
826
+ """Parse SQL string and populate DELETE builder using SQLGlot directly."""
827
+ return self._populate_builder_from_sql(builder, sql_string, exp.Delete, parsed_expr)
828
+
829
+ def _populate_merge_from_sql(
830
+ self, builder: "Merge", sql_string: str, parsed_expr: "exp.Expr | None" = None
831
+ ) -> "Merge":
832
+ """Parse SQL string and populate MERGE builder using SQLGlot directly."""
833
+ return self._populate_builder_from_sql(builder, sql_string, exp.Merge, parsed_expr)
834
+
835
+ def column(self, name: str, table: str | None = None) -> Column:
836
+ """Create a column reference.
837
+
838
+ Args:
839
+ name: Column name.
840
+ table: Optional table name.
841
+
842
+ Returns:
843
+ Column object that supports method chaining and operator overloading.
844
+ """
845
+ return Column(name, table)
846
+
847
+ @property
848
+ def case_(self) -> "Case":
849
+ """Create a CASE expression builder.
850
+
851
+ Returns:
852
+ Case builder instance for CASE expression building.
853
+
854
+ Example:
855
+ ```python
856
+ case_expr = (
857
+ sql.case_
858
+ .when("x = 1", "one")
859
+ .when("x = 2", "two")
860
+ .else_("other")
861
+ .end()
862
+ )
863
+ aliased_case = (
864
+ sql.case_
865
+ .when("status = 'active'", 1)
866
+ .else_(0)
867
+ .as_("is_active")
868
+ )
869
+ ```
870
+ """
871
+ return Case()
872
+
873
+ @property
874
+ def row_number_(self) -> "WindowFunctionBuilder":
875
+ """Create a ROW_NUMBER() window function builder."""
876
+ return WindowFunctionBuilder("row_number")
877
+
878
+ @property
879
+ def rank_(self) -> "WindowFunctionBuilder":
880
+ """Create a RANK() window function builder."""
881
+ return WindowFunctionBuilder("rank")
882
+
883
+ @property
884
+ def dense_rank_(self) -> "WindowFunctionBuilder":
885
+ """Create a DENSE_RANK() window function builder."""
886
+ return WindowFunctionBuilder("dense_rank")
887
+
888
+ @property
889
+ def lag_(self) -> "WindowFunctionBuilder":
890
+ """Create a LAG() window function builder."""
891
+ return WindowFunctionBuilder("lag")
892
+
893
+ @property
894
+ def lead_(self) -> "WindowFunctionBuilder":
895
+ """Create a LEAD() window function builder."""
896
+ return WindowFunctionBuilder("lead")
897
+
898
+ @property
899
+ def count_over_(self) -> "WindowFunctionBuilder":
900
+ """Create a COUNT(*) OVER() window function builder.
901
+
902
+ Returns a WindowFunctionBuilder pre-configured with COUNT(*) for fluent chaining.
903
+ Useful for pagination queries where you want to get the total count in the same query.
904
+
905
+ Returns:
906
+ WindowFunctionBuilder configured for COUNT(*) OVER()
907
+
908
+ Example:
909
+ >>> query = (
910
+ ... sql
911
+ ... .select("*", sql.count_over_.as_("total"))
912
+ ... .from_("users")
913
+ ... .where_eq("status", "active")
914
+ ... .limit(10)
915
+ ... )
916
+ # Produces: SELECT *, COUNT(*) OVER() AS total FROM users WHERE status = :status LIMIT 10
917
+
918
+ >>> # With partition:
919
+ >>> query = sql.select(
920
+ ... "*",
921
+ ... sql.count_over_.partition_by("department").as_(
922
+ ... "dept_count"
923
+ ... ),
924
+ ... )
925
+ """
926
+ return WindowFunctionBuilder("count", exp.Star())
927
+
928
+ @property
929
+ def sum_over_(self) -> "WindowFunctionBuilder":
930
+ """Create a SUM() OVER() window function builder."""
931
+ return WindowFunctionBuilder("sum")
932
+
933
+ @property
934
+ def avg_over_(self) -> "WindowFunctionBuilder":
935
+ """Create an AVG() OVER() window function builder."""
936
+ return WindowFunctionBuilder("avg")
937
+
938
+ @property
939
+ def max_over_(self) -> "WindowFunctionBuilder":
940
+ """Create a MAX() OVER() window function builder."""
941
+ return WindowFunctionBuilder("max")
942
+
943
+ @property
944
+ def min_over_(self) -> "WindowFunctionBuilder":
945
+ """Create a MIN() OVER() window function builder."""
946
+ return WindowFunctionBuilder("min")
947
+
948
+ @property
949
+ def exists_(self) -> "SubqueryBuilder":
950
+ """Create an EXISTS subquery builder."""
951
+ return SubqueryBuilder("exists")
952
+
953
+ @property
954
+ def in_(self) -> "SubqueryBuilder":
955
+ """Create an IN subquery builder."""
956
+ return SubqueryBuilder("in")
957
+
958
+ @property
959
+ def any_(self) -> "SubqueryBuilder":
960
+ """Create an ANY subquery builder."""
961
+ return SubqueryBuilder("any")
962
+
963
+ @property
964
+ def all_(self) -> "SubqueryBuilder":
965
+ """Create an ALL subquery builder."""
966
+ return SubqueryBuilder("all")
967
+
968
+ @property
969
+ def inner_join_(self) -> "JoinBuilder":
970
+ """Create an INNER JOIN builder."""
971
+ return create_join_builder("inner join")
972
+
973
+ @property
974
+ def left_join_(self) -> "JoinBuilder":
975
+ """Create a LEFT JOIN builder."""
976
+ return create_join_builder("left join")
977
+
978
+ @property
979
+ def right_join_(self) -> "JoinBuilder":
980
+ """Create a RIGHT JOIN builder."""
981
+ return create_join_builder("right join")
982
+
983
+ @property
984
+ def full_join_(self) -> "JoinBuilder":
985
+ """Create a FULL OUTER JOIN builder."""
986
+ return create_join_builder("full join")
987
+
988
+ @property
989
+ def cross_join_(self) -> "JoinBuilder":
990
+ """Create a CROSS JOIN builder."""
991
+ return create_join_builder("cross join")
992
+
993
+ @property
994
+ def lateral_join_(self) -> "JoinBuilder":
995
+ """Create a LATERAL JOIN builder.
996
+
997
+ Returns:
998
+ JoinBuilder configured for LATERAL JOIN
999
+
1000
+ Example:
1001
+ ```python
1002
+ query = (
1003
+ sql
1004
+ .select("u.name", "arr.value")
1005
+ .from_("users u")
1006
+ .join(sql.lateral_join_("UNNEST(u.tags)").on("true"))
1007
+ )
1008
+ ```
1009
+ """
1010
+ return create_join_builder("lateral join", lateral=True)
1011
+
1012
+ @property
1013
+ def left_lateral_join_(self) -> "JoinBuilder":
1014
+ """Create a LEFT LATERAL JOIN builder.
1015
+
1016
+ Returns:
1017
+ JoinBuilder configured for LEFT LATERAL JOIN
1018
+ """
1019
+ return create_join_builder("left join", lateral=True)
1020
+
1021
+ @property
1022
+ def cross_lateral_join_(self) -> "JoinBuilder":
1023
+ """Create a CROSS LATERAL JOIN builder.
1024
+
1025
+ Returns:
1026
+ JoinBuilder configured for CROSS LATERAL JOIN
1027
+ """
1028
+ return create_join_builder("cross join", lateral=True)
1029
+
1030
+ def __getattr__(self, name: str) -> "Column":
1031
+ """Dynamically create column references.
1032
+
1033
+ Args:
1034
+ name: Column name.
1035
+
1036
+ Returns:
1037
+ Column object for the given name.
1038
+
1039
+ Note:
1040
+ Special SQL constructs like case_, row_number_, etc. are
1041
+ handled as properties for type safety.
1042
+ """
1043
+ return Column(name)
1044
+
1045
+ @staticmethod
1046
+ def raw(sql_fragment: str, **parameters: Any) -> "exp.Expr | SQL":
1047
+ """Create a raw SQL expression from a string fragment with optional parameters.
1048
+
1049
+ Args:
1050
+ sql_fragment: Raw SQL string to parse into an expression.
1051
+ **parameters: Named parameters for parameter binding.
1052
+
1053
+ Returns:
1054
+ SQLGlot expression from the parsed SQL fragment (if no parameters).
1055
+ SQL statement object (if parameters provided).
1056
+
1057
+ Raises:
1058
+ SQLBuilderError: If the SQL fragment cannot be parsed.
1059
+
1060
+ Example:
1061
+ ```python
1062
+ expr = sql.raw("COALESCE(name, 'Unknown')")
1063
+
1064
+
1065
+ stmt = sql.raw(
1066
+ "LOWER(name) LIKE LOWER(:pattern)", pattern=f"%{query}%"
1067
+ )
1068
+
1069
+
1070
+ expr = sql.raw(
1071
+ "price BETWEEN :min_price AND :max_price",
1072
+ min_price=100,
1073
+ max_price=500,
1074
+ )
1075
+
1076
+
1077
+ query = sql.select(
1078
+ "name",
1079
+ sql.raw(
1080
+ "ROW_NUMBER() OVER (PARTITION BY department ORDER BY salary DESC)"
1081
+ ),
1082
+ ).from_("employees")
1083
+ ```
1084
+ """
1085
+ if not parameters:
1086
+ try:
1087
+ parsed: exp.Expr = exp.maybe_parse(sql_fragment)
1088
+ except Exception as e:
1089
+ msg = f"Failed to parse raw SQL fragment '{sql_fragment}': {e}"
1090
+ raise SQLBuilderError(msg) from e
1091
+ return parsed
1092
+
1093
+ return SQL(sql_fragment, parameters)
1094
+
1095
+ def count(
1096
+ self, column: Union[str, exp.Expr, "ExpressionWrapper", "Case", "Column"] = "*", distinct: bool = False
1097
+ ) -> AggregateExpression:
1098
+ """Create a COUNT expression.
1099
+
1100
+ Args:
1101
+ column: Column to count (default "*").
1102
+ distinct: Whether to use COUNT DISTINCT.
1103
+
1104
+ Returns:
1105
+ COUNT expression.
1106
+ """
1107
+ if isinstance(column, str) and column == "*":
1108
+ expr = exp.Count(this=exp.Star(), distinct=distinct)
1109
+ else:
1110
+ col_expr = extract_expression(column)
1111
+ expr = exp.Count(this=col_expr, distinct=distinct)
1112
+ return AggregateExpression(expr)
1113
+
1114
+ def count_distinct(self, column: Union[str, exp.Expr, "ExpressionWrapper", "Case"]) -> AggregateExpression:
1115
+ """Create a COUNT(DISTINCT column) expression.
1116
+
1117
+ Args:
1118
+ column: Column to count distinct values.
1119
+
1120
+ Returns:
1121
+ COUNT DISTINCT expression.
1122
+ """
1123
+ return self.count(column, distinct=True)
1124
+
1125
+ def count_over(
1126
+ self,
1127
+ column: Union[str, exp.Expr, "ExpressionWrapper", "Case", "Column"] = "*",
1128
+ partition_by: str | list[str] | exp.Expr | None = None,
1129
+ ) -> FunctionExpression:
1130
+ """Create a COUNT() OVER() window function for inline total counts.
1131
+
1132
+ This is particularly useful for pagination queries where you want to get
1133
+ the total count in the same query as the paginated results.
1134
+
1135
+ Args:
1136
+ column: Column to count (default "*" for COUNT(*)).
1137
+ partition_by: Optional columns to partition by.
1138
+
1139
+ Returns:
1140
+ COUNT() OVER() window function expression.
1141
+
1142
+ Example:
1143
+ >>> # Simple total count alongside paginated results:
1144
+ >>> query = (
1145
+ ... sql
1146
+ ... .select("id", "name", sql.count_over().as_("total"))
1147
+ ... .from_("users")
1148
+ ... .where_eq("status", "active")
1149
+ ... .limit(10)
1150
+ ... .offset(0)
1151
+ ... )
1152
+ # Produces: SELECT id, name, COUNT(*) OVER() AS total FROM users WHERE status = :status LIMIT 10
1153
+
1154
+ >>> # With partition (count per group):
1155
+ >>> query = sql.select(
1156
+ ... "*",
1157
+ ... sql.count_over(partition_by="department").as_(
1158
+ ... "dept_count"
1159
+ ... ),
1160
+ ... )
1161
+ """
1162
+ if isinstance(column, str) and column == "*":
1163
+ count_expr = exp.Count(this=exp.Star())
1164
+ else:
1165
+ col_expr = extract_expression(column)
1166
+ count_expr = exp.Count(this=col_expr)
1167
+
1168
+ over_args: dict[str, Any] = {}
1169
+ if partition_by:
1170
+ if isinstance(partition_by, str):
1171
+ over_args["partition_by"] = [exp.column(partition_by)]
1172
+ elif isinstance(partition_by, list):
1173
+ over_args["partition_by"] = [exp.column(col) for col in partition_by]
1174
+ elif isinstance(partition_by, exp.Expr):
1175
+ over_args["partition_by"] = [partition_by]
1176
+
1177
+ return FunctionExpression(exp.Window(this=count_expr, **over_args))
1178
+
1179
+ def sum_over(
1180
+ self,
1181
+ column: Union[str, exp.Expr, "ExpressionWrapper", "Case"],
1182
+ partition_by: str | list[str] | exp.Expr | None = None,
1183
+ order_by: str | list[str] | exp.Expr | None = None,
1184
+ ) -> FunctionExpression:
1185
+ """Create a SUM() OVER() window function.
1186
+
1187
+ Args:
1188
+ column: Column to sum.
1189
+ partition_by: Columns to partition by.
1190
+ order_by: Columns to order by.
1191
+
1192
+ Returns:
1193
+ SUM() OVER() window function expression.
1194
+ """
1195
+ col_expr = extract_expression(column)
1196
+ return self._create_window_function("SUM", [col_expr], partition_by, order_by)
1197
+
1198
+ def avg_over(
1199
+ self,
1200
+ column: Union[str, exp.Expr, "ExpressionWrapper", "Case"],
1201
+ partition_by: str | list[str] | exp.Expr | None = None,
1202
+ order_by: str | list[str] | exp.Expr | None = None,
1203
+ ) -> FunctionExpression:
1204
+ """Create an AVG() OVER() window function.
1205
+
1206
+ Args:
1207
+ column: Column to average.
1208
+ partition_by: Columns to partition by.
1209
+ order_by: Columns to order by.
1210
+
1211
+ Returns:
1212
+ AVG() OVER() window function expression.
1213
+ """
1214
+ col_expr = extract_expression(column)
1215
+ return self._create_window_function("AVG", [col_expr], partition_by, order_by)
1216
+
1217
+ def max_over(
1218
+ self,
1219
+ column: Union[str, exp.Expr, "ExpressionWrapper", "Case"],
1220
+ partition_by: str | list[str] | exp.Expr | None = None,
1221
+ order_by: str | list[str] | exp.Expr | None = None,
1222
+ ) -> FunctionExpression:
1223
+ """Create a MAX() OVER() window function.
1224
+
1225
+ Args:
1226
+ column: Column to find maximum.
1227
+ partition_by: Columns to partition by.
1228
+ order_by: Columns to order by.
1229
+
1230
+ Returns:
1231
+ MAX() OVER() window function expression.
1232
+ """
1233
+ col_expr = extract_expression(column)
1234
+ return self._create_window_function("MAX", [col_expr], partition_by, order_by)
1235
+
1236
+ def min_over(
1237
+ self,
1238
+ column: Union[str, exp.Expr, "ExpressionWrapper", "Case"],
1239
+ partition_by: str | list[str] | exp.Expr | None = None,
1240
+ order_by: str | list[str] | exp.Expr | None = None,
1241
+ ) -> FunctionExpression:
1242
+ """Create a MIN() OVER() window function.
1243
+
1244
+ Args:
1245
+ column: Column to find minimum.
1246
+ partition_by: Columns to partition by.
1247
+ order_by: Columns to order by.
1248
+
1249
+ Returns:
1250
+ MIN() OVER() window function expression.
1251
+ """
1252
+ col_expr = extract_expression(column)
1253
+ return self._create_window_function("MIN", [col_expr], partition_by, order_by)
1254
+
1255
+ @staticmethod
1256
+ def sum(column: Union[str, exp.Expr, "ExpressionWrapper", "Case"], distinct: bool = False) -> AggregateExpression:
1257
+ """Create a SUM expression.
1258
+
1259
+ Args:
1260
+ column: Column to sum.
1261
+ distinct: Whether to use SUM DISTINCT.
1262
+
1263
+ Returns:
1264
+ SUM expression.
1265
+ """
1266
+ col_expr = extract_expression(column)
1267
+ return AggregateExpression(exp.Sum(this=col_expr, distinct=distinct))
1268
+
1269
+ @staticmethod
1270
+ def avg(column: Union[str, exp.Expr, "ExpressionWrapper", "Case"]) -> AggregateExpression:
1271
+ """Create an AVG expression.
1272
+
1273
+ Args:
1274
+ column: Column to average.
1275
+
1276
+ Returns:
1277
+ AVG expression.
1278
+ """
1279
+ col_expr = extract_expression(column)
1280
+ return AggregateExpression(exp.Avg(this=col_expr))
1281
+
1282
+ @staticmethod
1283
+ def max(column: Union[str, exp.Expr, "ExpressionWrapper", "Case"]) -> AggregateExpression:
1284
+ """Create a MAX expression.
1285
+
1286
+ Args:
1287
+ column: Column to find maximum.
1288
+
1289
+ Returns:
1290
+ MAX expression.
1291
+ """
1292
+ col_expr = extract_expression(column)
1293
+ return AggregateExpression(exp.Max(this=col_expr))
1294
+
1295
+ @staticmethod
1296
+ def min(column: Union[str, exp.Expr, "ExpressionWrapper", "Case"]) -> AggregateExpression:
1297
+ """Create a MIN expression.
1298
+
1299
+ Args:
1300
+ column: Column to find minimum.
1301
+
1302
+ Returns:
1303
+ MIN expression.
1304
+ """
1305
+ col_expr = extract_expression(column)
1306
+ return AggregateExpression(exp.Min(this=col_expr))
1307
+
1308
+ @staticmethod
1309
+ def rollup(*columns: str | exp.Expr) -> FunctionExpression:
1310
+ """Create a ROLLUP expression for GROUP BY clauses.
1311
+
1312
+ Args:
1313
+ *columns: Columns to include in the rollup.
1314
+
1315
+ Returns:
1316
+ ROLLUP expression.
1317
+
1318
+ Example:
1319
+ ```python
1320
+ query = (
1321
+ sql
1322
+ .select("product", "region", sql.sum("sales"))
1323
+ .from_("sales_data")
1324
+ .group_by(sql.rollup("product", "region"))
1325
+ )
1326
+ ```
1327
+ """
1328
+ column_exprs = [exp.column(col) if isinstance(col, str) else col for col in columns]
1329
+ return FunctionExpression(exp.Rollup(expressions=column_exprs))
1330
+
1331
+ @staticmethod
1332
+ def cube(*columns: str | exp.Expr) -> FunctionExpression:
1333
+ """Create a CUBE expression for GROUP BY clauses.
1334
+
1335
+ Args:
1336
+ *columns: Columns to include in the cube.
1337
+
1338
+ Returns:
1339
+ CUBE expression.
1340
+
1341
+ Example:
1342
+ ```python
1343
+ query = (
1344
+ sql
1345
+ .select("product", "region", sql.sum("sales"))
1346
+ .from_("sales_data")
1347
+ .group_by(sql.cube("product", "region"))
1348
+ )
1349
+ ```
1350
+ """
1351
+ column_exprs = [exp.column(col) if isinstance(col, str) else col for col in columns]
1352
+ return FunctionExpression(exp.Cube(expressions=column_exprs))
1353
+
1354
+ @staticmethod
1355
+ def grouping_sets(*column_sets: tuple[str, ...] | list[str]) -> FunctionExpression:
1356
+ """Create a GROUPING SETS expression for GROUP BY clauses.
1357
+
1358
+ Args:
1359
+ *column_sets: Sets of columns to group by.
1360
+
1361
+ Returns:
1362
+ GROUPING SETS expression.
1363
+
1364
+ Example:
1365
+ ```python
1366
+ query = (
1367
+ sql
1368
+ .select("product", "region", sql.sum("sales"))
1369
+ .from_("sales_data")
1370
+ .group_by(
1371
+ sql.grouping_sets(("product",), ("region",), ())
1372
+ )
1373
+ )
1374
+ ```
1375
+ """
1376
+ set_expressions = []
1377
+ for column_set in column_sets:
1378
+ if isinstance(column_set, (tuple, list)):
1379
+ if len(column_set) == 0:
1380
+ set_expressions.append(exp.Tuple(expressions=[]))
1381
+ else:
1382
+ columns = [exp.column(col) for col in column_set]
1383
+ set_expressions.append(exp.Tuple(expressions=columns))
1384
+ else:
1385
+ set_expressions.append(exp.column(column_set))
1386
+
1387
+ return FunctionExpression(exp.GroupingSets(expressions=set_expressions))
1388
+
1389
+ @staticmethod
1390
+ def any(values: list[Any] | exp.Expr | str) -> FunctionExpression:
1391
+ """Create an ANY expression for use with comparison operators.
1392
+
1393
+ Args:
1394
+ values: Values, expression, or subquery for the ANY clause.
1395
+
1396
+ Returns:
1397
+ ANY expression.
1398
+
1399
+ Example:
1400
+ ```python
1401
+ subquery = sql.select("user_id").from_("active_users")
1402
+ query = (
1403
+ sql
1404
+ .select("*")
1405
+ .from_("users")
1406
+ .where(sql.id.eq(sql.any(subquery)))
1407
+ )
1408
+ ```
1409
+ """
1410
+ if isinstance(values, list):
1411
+ literals = [SQLFactory.to_literal(v) for v in values]
1412
+ return FunctionExpression(exp.Any(this=exp.Array(expressions=literals)))
1413
+ if isinstance(values, str):
1414
+ parsed: exp.Expr = exp.maybe_parse(values)
1415
+ return FunctionExpression(exp.Any(this=parsed))
1416
+ return FunctionExpression(exp.Any(this=values))
1417
+
1418
+ @staticmethod
1419
+ def not_any_(values: list[Any] | exp.Expr | str) -> FunctionExpression:
1420
+ """Create a NOT ANY expression for use with comparison operators.
1421
+
1422
+ Args:
1423
+ values: Values, expression, or subquery for the NOT ANY clause.
1424
+
1425
+ Returns:
1426
+ NOT ANY expression.
1427
+
1428
+ Example:
1429
+ ```python
1430
+ subquery = sql.select("user_id").from_("blocked_users")
1431
+ query = (
1432
+ sql
1433
+ .select("*")
1434
+ .from_("users")
1435
+ .where(sql.id.neq(sql.not_any(subquery)))
1436
+ )
1437
+ ```
1438
+ """
1439
+ return SQLFactory.any(values)
1440
+
1441
+ @staticmethod
1442
+ def concat(*expressions: str | exp.Expr) -> StringExpression:
1443
+ """Create a CONCAT expression.
1444
+
1445
+ Args:
1446
+ *expressions: Expressions to concatenate.
1447
+
1448
+ Returns:
1449
+ CONCAT expression.
1450
+ """
1451
+ exprs = [exp.column(expr) if isinstance(expr, str) else expr for expr in expressions]
1452
+ return StringExpression(exp.Concat(expressions=exprs))
1453
+
1454
+ @staticmethod
1455
+ def upper(column: str | exp.Expr) -> StringExpression:
1456
+ """Create an UPPER expression.
1457
+
1458
+ Args:
1459
+ column: Column to convert to uppercase.
1460
+
1461
+ Returns:
1462
+ UPPER expression.
1463
+ """
1464
+ col_expr = exp.column(column) if isinstance(column, str) else column
1465
+ return StringExpression(exp.Upper(this=col_expr))
1466
+
1467
+ @staticmethod
1468
+ def lower(column: str | exp.Expr) -> StringExpression:
1469
+ """Create a LOWER expression.
1470
+
1471
+ Args:
1472
+ column: Column to convert to lowercase.
1473
+
1474
+ Returns:
1475
+ LOWER expression.
1476
+ """
1477
+ col_expr = exp.column(column) if isinstance(column, str) else column
1478
+ return StringExpression(exp.Lower(this=col_expr))
1479
+
1480
+ @staticmethod
1481
+ def length(column: str | exp.Expr) -> StringExpression:
1482
+ """Create a LENGTH expression.
1483
+
1484
+ Args:
1485
+ column: Column to get length of.
1486
+
1487
+ Returns:
1488
+ LENGTH expression.
1489
+ """
1490
+ col_expr = exp.column(column) if isinstance(column, str) else column
1491
+ return StringExpression(exp.Length(this=col_expr))
1492
+
1493
+ @staticmethod
1494
+ def round(column: str | exp.Expr, decimals: int = 0) -> MathExpression:
1495
+ """Create a ROUND expression.
1496
+
1497
+ Args:
1498
+ column: Column to round.
1499
+ decimals: Number of decimal places.
1500
+
1501
+ Returns:
1502
+ ROUND expression.
1503
+ """
1504
+ col_expr = exp.column(column) if isinstance(column, str) else column
1505
+ if decimals == 0:
1506
+ return MathExpression(exp.Round(this=col_expr))
1507
+ return MathExpression(exp.Round(this=col_expr, expression=exp.Literal.number(decimals)))
1508
+
1509
+ @staticmethod
1510
+ def to_literal(value: Any) -> FunctionExpression:
1511
+ """Convert a Python value to a SQLGlot literal expression.
1512
+
1513
+ Uses SQLGlot's built-in exp.convert() function for literal creation.
1514
+ Handles all Python primitive types:
1515
+ - None -> exp.Null (renders as NULL)
1516
+ - bool -> exp.Boolean (renders as TRUE/FALSE or 1/0 based on dialect)
1517
+ - int/float -> exp.Literal with is_number=True
1518
+ - str -> exp.Literal with is_string=True
1519
+ - exp.Expr -> returned as-is (passthrough)
1520
+
1521
+ Args:
1522
+ value: Python value or SQLGlot expression to convert.
1523
+
1524
+ Returns:
1525
+ SQLGlot expression representing the literal value.
1526
+ """
1527
+ if isinstance(value, exp.Expr):
1528
+ return FunctionExpression(value)
1529
+ return FunctionExpression(exp.convert(value))
1530
+
1531
+ @staticmethod
1532
+ def decode(column: str | exp.Expr, *args: str | exp.Expr | Any) -> FunctionExpression:
1533
+ """Create a DECODE expression (Oracle-style conditional logic).
1534
+
1535
+ DECODE compares column to each search value and returns the corresponding result.
1536
+ If no match is found, returns the default value (if provided) or NULL.
1537
+
1538
+ Args:
1539
+ column: Column to compare.
1540
+ *args: Alternating search values and results, with optional default at the end.
1541
+ Format: search1, result1, search2, result2, ..., [default]
1542
+
1543
+ Raises:
1544
+ ValueError: If fewer than two search/result pairs are provided.
1545
+
1546
+ Returns:
1547
+ CASE expression equivalent to DECODE.
1548
+
1549
+ Example:
1550
+ ```python
1551
+ sql.decode(
1552
+ "status", "A", "Active", "I", "Inactive", "Unknown"
1553
+ )
1554
+ ```
1555
+ """
1556
+ col_expr = exp.column(column) if isinstance(column, str) else column
1557
+
1558
+ if len(args) < MIN_DECODE_ARGS:
1559
+ msg = "DECODE requires at least one search/result pair"
1560
+ raise ValueError(msg)
1561
+
1562
+ conditions = []
1563
+ default = None
1564
+
1565
+ for i in range(0, len(args) - 1, 2):
1566
+ if i + 1 >= len(args):
1567
+ default = to_expression(args[i])
1568
+ break
1569
+
1570
+ search_val = args[i]
1571
+ result_val = args[i + 1]
1572
+
1573
+ search_expr = to_expression(search_val)
1574
+ result_expr = to_expression(result_val)
1575
+
1576
+ condition = exp.EQ(this=col_expr, expression=search_expr)
1577
+ conditions.append(exp.If(this=condition, true=result_expr))
1578
+
1579
+ return FunctionExpression(exp.Case(ifs=conditions, default=default))
1580
+
1581
+ @staticmethod
1582
+ def cast(column: str | exp.Expr, data_type: str) -> ConversionExpression:
1583
+ """Create a CAST expression for type conversion.
1584
+
1585
+ Args:
1586
+ column: Column or expression to cast.
1587
+ data_type: Target data type (e.g., 'INT', 'VARCHAR(100)', 'DECIMAL(10,2)').
1588
+
1589
+ Returns:
1590
+ CAST expression.
1591
+ """
1592
+ col_expr = exp.column(column) if isinstance(column, str) else column
1593
+ return ConversionExpression(exp.Cast(this=col_expr, to=exp.DataType.build(data_type)))
1594
+
1595
+ @staticmethod
1596
+ def coalesce(*expressions: str | exp.Expr) -> ConversionExpression:
1597
+ """Create a COALESCE expression.
1598
+
1599
+ Args:
1600
+ *expressions: Expressions to coalesce.
1601
+
1602
+ Returns:
1603
+ COALESCE expression.
1604
+ """
1605
+ exprs = [exp.column(expr) if isinstance(expr, str) else expr for expr in expressions]
1606
+ return ConversionExpression(exp.Coalesce(expressions=exprs))
1607
+
1608
+ @staticmethod
1609
+ def nvl(column: str | exp.Expr, substitute_value: str | exp.Expr | Any) -> ConversionExpression:
1610
+ """Create an NVL (Oracle-style) expression using COALESCE.
1611
+
1612
+ Args:
1613
+ column: Column to check for NULL.
1614
+ substitute_value: Value to use if column is NULL.
1615
+
1616
+ Returns:
1617
+ COALESCE expression equivalent to NVL.
1618
+ """
1619
+ col_expr = exp.column(column) if isinstance(column, str) else column
1620
+ sub_expr = to_expression(substitute_value)
1621
+ return ConversionExpression(exp.Coalesce(expressions=[col_expr, sub_expr]))
1622
+
1623
+ @staticmethod
1624
+ def nvl2(
1625
+ column: str | exp.Expr, value_if_not_null: str | exp.Expr | Any, value_if_null: str | exp.Expr | Any
1626
+ ) -> ConversionExpression:
1627
+ """Create an NVL2 (Oracle-style) expression using CASE.
1628
+
1629
+ NVL2 returns value_if_not_null if column is not NULL,
1630
+ otherwise returns value_if_null.
1631
+
1632
+ Args:
1633
+ column: Column to check for NULL.
1634
+ value_if_not_null: Value to use if column is NOT NULL.
1635
+ value_if_null: Value to use if column is NULL.
1636
+
1637
+ Returns:
1638
+ CASE expression equivalent to NVL2.
1639
+
1640
+ Example:
1641
+ ```python
1642
+ sql.nvl2("salary", "Has Salary", "No Salary")
1643
+ ```
1644
+ """
1645
+ col_expr = exp.column(column) if isinstance(column, str) else column
1646
+ not_null_expr = to_expression(value_if_not_null)
1647
+ null_expr = to_expression(value_if_null)
1648
+
1649
+ is_null = exp.Is(this=col_expr, expression=exp.Null())
1650
+ condition = exp.Not(this=is_null)
1651
+ when_clause = exp.If(this=condition, true=not_null_expr)
1652
+
1653
+ return ConversionExpression(exp.Case(ifs=[when_clause], default=null_expr))
1654
+
1655
+ @staticmethod
1656
+ def bulk_insert(table_name: str, column_count: int, placeholder_style: str = "?") -> FunctionExpression:
1657
+ """Create bulk INSERT expression for executemany operations.
1658
+
1659
+ For bulk loading operations like CSV ingestion where
1660
+ an INSERT expression with placeholders for executemany() is needed.
1661
+
1662
+ Args:
1663
+ table_name: Name of the table to insert into
1664
+ column_count: Number of columns (for placeholder generation)
1665
+ placeholder_style: Placeholder style ("?" for SQLite/PostgreSQL, "%s" for MySQL, ":1" for Oracle)
1666
+
1667
+ Returns:
1668
+ INSERT expression with placeholders for bulk operations
1669
+
1670
+ Example:
1671
+ ```python
1672
+ from sqlspec import sql
1673
+
1674
+
1675
+ insert_expr = sql.bulk_insert("my_table", 3)
1676
+
1677
+
1678
+ insert_expr = sql.bulk_insert(
1679
+ "my_table", 3, placeholder_style="%s"
1680
+ )
1681
+
1682
+
1683
+ insert_expr = sql.bulk_insert(
1684
+ "my_table", 3, placeholder_style=":1"
1685
+ )
1686
+ ```
1687
+ """
1688
+ return FunctionExpression(
1689
+ exp.Insert(
1690
+ this=exp.Table(this=exp.to_identifier(table_name)),
1691
+ expression=exp.Values(
1692
+ expressions=[
1693
+ exp.Tuple(expressions=[exp.Placeholder(this=placeholder_style) for _ in range(column_count)])
1694
+ ]
1695
+ ),
1696
+ )
1697
+ )
1698
+
1699
+ def truncate(self, table_name: str) -> "Truncate":
1700
+ """Create a TRUNCATE TABLE builder.
1701
+
1702
+ Args:
1703
+ table_name: Name of the table to truncate
1704
+
1705
+ Returns:
1706
+ TruncateTable builder instance
1707
+
1708
+ Example:
1709
+ ```python
1710
+ from sqlspec import sql
1711
+
1712
+
1713
+ truncate_sql = sql.truncate_table("my_table").build().sql
1714
+
1715
+
1716
+ truncate_sql = (
1717
+ sql
1718
+ .truncate_table("my_table")
1719
+ .cascade()
1720
+ .restart_identity()
1721
+ .build()
1722
+ .sql
1723
+ )
1724
+ ```
1725
+ """
1726
+ return Truncate(table_name, dialect=self.dialect)
1727
+
1728
+ @staticmethod
1729
+ def case() -> "Case":
1730
+ """Create a CASE expression builder.
1731
+
1732
+ Returns:
1733
+ CaseExpressionBuilder for building CASE expressions.
1734
+ """
1735
+ return Case()
1736
+
1737
+ def row_number(
1738
+ self, partition_by: str | list[str] | exp.Expr | None = None, order_by: str | list[str] | exp.Expr | None = None
1739
+ ) -> FunctionExpression:
1740
+ """Create a ROW_NUMBER() window function.
1741
+
1742
+ Args:
1743
+ partition_by: Columns to partition by.
1744
+ order_by: Columns to order by.
1745
+
1746
+ Returns:
1747
+ ROW_NUMBER window function expression.
1748
+ """
1749
+ return self._create_window_function("ROW_NUMBER", [], partition_by, order_by)
1750
+
1751
+ def rank(
1752
+ self, partition_by: str | list[str] | exp.Expr | None = None, order_by: str | list[str] | exp.Expr | None = None
1753
+ ) -> FunctionExpression:
1754
+ """Create a RANK() window function.
1755
+
1756
+ Args:
1757
+ partition_by: Columns to partition by.
1758
+ order_by: Columns to order by.
1759
+
1760
+ Returns:
1761
+ RANK window function expression.
1762
+ """
1763
+ return self._create_window_function("RANK", [], partition_by, order_by)
1764
+
1765
+ def dense_rank(
1766
+ self, partition_by: str | list[str] | exp.Expr | None = None, order_by: str | list[str] | exp.Expr | None = None
1767
+ ) -> FunctionExpression:
1768
+ """Create a DENSE_RANK() window function.
1769
+
1770
+ Args:
1771
+ partition_by: Columns to partition by.
1772
+ order_by: Columns to order by.
1773
+
1774
+ Returns:
1775
+ DENSE_RANK window function expression.
1776
+ """
1777
+ return self._create_window_function("DENSE_RANK", [], partition_by, order_by)
1778
+
1779
+ def lag(
1780
+ self,
1781
+ column: str | exp.Expr,
1782
+ offset: int = 1,
1783
+ default: Any = None,
1784
+ partition_by: str | list[str] | exp.Expr | None = None,
1785
+ order_by: str | list[str] | exp.Expr | None = None,
1786
+ ) -> FunctionExpression:
1787
+ """Create a LAG() window function.
1788
+
1789
+ LAG accesses data from a previous row in the same result set without using a self-join.
1790
+
1791
+ Args:
1792
+ column: The column to get the lagged value from.
1793
+ offset: Number of rows to look back (default 1).
1794
+ default: Value to return when there is no row at the offset.
1795
+ partition_by: Columns to partition by.
1796
+ order_by: Columns to order by.
1797
+
1798
+ Returns:
1799
+ LAG window function expression.
1800
+
1801
+ Example:
1802
+ >>> sql.lag("salary", offset=1, order_by="hire_date")
1803
+ >>> sql.lag("price", partition_by="category", order_by="date")
1804
+ """
1805
+ col_expr = exp.column(column) if isinstance(column, str) else column
1806
+ func_args: list[exp.Expr] = [col_expr, exp.Literal.number(offset)]
1807
+ if default is not None:
1808
+ func_args.append(exp.convert(default))
1809
+ return self._create_window_function("LAG", func_args, partition_by, order_by)
1810
+
1811
+ def lead(
1812
+ self,
1813
+ column: str | exp.Expr,
1814
+ offset: int = 1,
1815
+ default: Any = None,
1816
+ partition_by: str | list[str] | exp.Expr | None = None,
1817
+ order_by: str | list[str] | exp.Expr | None = None,
1818
+ ) -> FunctionExpression:
1819
+ """Create a LEAD() window function.
1820
+
1821
+ LEAD accesses data from a subsequent row in the same result set without using a self-join.
1822
+
1823
+ Args:
1824
+ column: The column to get the lead value from.
1825
+ offset: Number of rows to look forward (default 1).
1826
+ default: Value to return when there is no row at the offset.
1827
+ partition_by: Columns to partition by.
1828
+ order_by: Columns to order by.
1829
+
1830
+ Returns:
1831
+ LEAD window function expression.
1832
+
1833
+ Example:
1834
+ >>> sql.lead("salary", offset=1, order_by="hire_date")
1835
+ >>> sql.lead("price", partition_by="category", order_by="date")
1836
+ """
1837
+ col_expr = exp.column(column) if isinstance(column, str) else column
1838
+ func_args: list[exp.Expr] = [col_expr, exp.Literal.number(offset)]
1839
+ if default is not None:
1840
+ func_args.append(exp.convert(default))
1841
+ return self._create_window_function("LEAD", func_args, partition_by, order_by)
1842
+
1843
+ @staticmethod
1844
+ def _create_window_function(
1845
+ func_name: str,
1846
+ func_args: list[exp.Expr],
1847
+ partition_by: str | list[str] | exp.Expr | None = None,
1848
+ order_by: str | list[str] | exp.Expr | None = None,
1849
+ ) -> FunctionExpression:
1850
+ """Helper to create window function expressions.
1851
+
1852
+ Args:
1853
+ func_name: Name of the window function.
1854
+ func_args: Arguments to the function.
1855
+ partition_by: Columns to partition by.
1856
+ order_by: Columns to order by.
1857
+
1858
+ Returns:
1859
+ Window function expression.
1860
+ """
1861
+ func_expr = exp.Anonymous(this=func_name, expressions=func_args)
1862
+
1863
+ over_args: dict[str, Any] = {}
1864
+
1865
+ if partition_by:
1866
+ if isinstance(partition_by, str):
1867
+ over_args["partition_by"] = [exp.column(partition_by)]
1868
+ elif isinstance(partition_by, list):
1869
+ over_args["partition_by"] = [exp.column(col) for col in partition_by]
1870
+ elif isinstance(partition_by, exp.Expr):
1871
+ over_args["partition_by"] = [partition_by]
1872
+
1873
+ if order_by:
1874
+ if isinstance(order_by, str):
1875
+ over_args["order"] = exp.Order(expressions=[exp.column(order_by).asc()])
1876
+ elif isinstance(order_by, list):
1877
+ over_args["order"] = exp.Order(expressions=[exp.column(col).asc() for col in order_by])
1878
+ elif isinstance(order_by, exp.Expr):
1879
+ over_args["order"] = exp.Order(expressions=[order_by])
1880
+
1881
+ return FunctionExpression(exp.Window(this=func_expr, **over_args))
1882
+
1883
+
1884
+ sql = SQLFactory()