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,1865 @@
1
+ """SQL statement and configuration management."""
2
+
3
+ import hashlib
4
+ import uuid
5
+ from collections.abc import Mapping, Sequence
6
+ from typing import TYPE_CHECKING, Any, Final, TypeAlias
7
+
8
+ import sqlglot
9
+ from mypy_extensions import mypyc_attr
10
+ from sqlglot import exp
11
+ from sqlglot.errors import ParseError
12
+
13
+ import sqlspec.exceptions
14
+ from sqlspec.core import pipeline
15
+ from sqlspec.core._pool import get_processed_state_pool, get_sql_pool
16
+ from sqlspec.core.cache import FiltersView
17
+ from sqlspec.core.compiler import OperationProfile, OperationType
18
+ from sqlspec.core.explain import ExplainFormat, ExplainOptions
19
+ from sqlspec.core.hashing import hash_filters
20
+ from sqlspec.core.parameters import (
21
+ ParameterConverter,
22
+ ParameterProcessor,
23
+ ParameterProfile,
24
+ ParameterStyle,
25
+ ParameterStyleConfig,
26
+ ParameterValidator,
27
+ structural_fingerprint,
28
+ )
29
+ from sqlspec.core.query_modifiers import (
30
+ apply_column_pruning,
31
+ apply_limit,
32
+ apply_offset,
33
+ apply_select_only,
34
+ apply_where,
35
+ create_between_condition,
36
+ create_condition,
37
+ create_in_condition,
38
+ create_not_in_condition,
39
+ expr_eq,
40
+ expr_gt,
41
+ expr_gte,
42
+ expr_ilike,
43
+ expr_is_not_null,
44
+ expr_is_null,
45
+ expr_like,
46
+ expr_lt,
47
+ expr_lte,
48
+ expr_neq,
49
+ extract_column_name,
50
+ safe_modify_with_cte,
51
+ )
52
+ from sqlspec.core.sqlcommenter import create_sqlcommenter_statement_transformer
53
+ from sqlspec.observability import resolve_db_system
54
+ from sqlspec.typing import Empty, EmptyEnum
55
+ from sqlspec.utils.logging import get_logger
56
+ from sqlspec.utils.type_guards import is_statement_filter, supports_where
57
+
58
+ if TYPE_CHECKING:
59
+ from collections.abc import Callable
60
+
61
+ from sqlglot.dialects.dialect import DialectType
62
+
63
+ from sqlspec.builder import QueryBuilder
64
+ from sqlspec.core.filters import StatementFilter
65
+
66
+
67
+ __all__ = (
68
+ "SQL",
69
+ "ProcessedState",
70
+ "Statement",
71
+ "StatementConfig",
72
+ "get_default_config",
73
+ "get_default_parameter_config",
74
+ )
75
+ logger = get_logger("sqlspec.core.statement")
76
+
77
+ RETURNS_ROWS_OPERATIONS: Final = {"SELECT", "WITH", "VALUES", "TABLE", "SHOW", "DESCRIBE", "PRAGMA"}
78
+ MODIFYING_OPERATIONS: Final = {"INSERT", "UPDATE", "DELETE", "MERGE", "UPSERT"}
79
+ _ORDER_PARTS_COUNT: Final = 2
80
+ _MAX_PARAM_COLLISION_ATTEMPTS: Final = 1000
81
+
82
+
83
+ SQL_CONFIG_SLOTS: Final = (
84
+ "dialect",
85
+ "enable_analysis",
86
+ "enable_caching",
87
+ "enable_column_pruning",
88
+ "enable_expression_simplification",
89
+ "enable_parameter_type_wrapping",
90
+ "enable_parsing",
91
+ "enable_sqlcommenter",
92
+ "enable_transformations",
93
+ "enable_validation",
94
+ "execution_mode",
95
+ "execution_args",
96
+ "output_transformer",
97
+ "sqlcommenter_attributes",
98
+ "sqlcommenter_enable_context",
99
+ "sqlcommenter_enable_traceparent",
100
+ "statement_transformers",
101
+ "parameter_config",
102
+ "parameter_converter",
103
+ "parameter_validator",
104
+ "_fingerprint_cache",
105
+ "_hash_cache",
106
+ "_is_frozen",
107
+ )
108
+
109
+ PROCESSED_STATE_SLOTS: Final = (
110
+ "compiled_sql",
111
+ "execution_parameters",
112
+ "parsed_expression",
113
+ "operation_type",
114
+ "input_named_parameters",
115
+ "applied_wrap_types",
116
+ "filter_hash",
117
+ "parameter_fingerprint",
118
+ "parameter_casts",
119
+ "parameter_profile",
120
+ "operation_profile",
121
+ "validation_errors",
122
+ "is_many",
123
+ )
124
+
125
+
126
+ @mypyc_attr(allow_interpreted_subclasses=False)
127
+ class ProcessedState:
128
+ """Processing results for SQL statements.
129
+
130
+ Contains the compiled SQL, execution parameters, parsed expression,
131
+ operation type, and validation errors for a processed SQL statement.
132
+ """
133
+
134
+ __slots__ = PROCESSED_STATE_SLOTS
135
+ operation_type: "OperationType"
136
+
137
+ def __init__(
138
+ self,
139
+ compiled_sql: str,
140
+ execution_parameters: Any,
141
+ parsed_expression: "exp.Expr | None" = None,
142
+ operation_type: "OperationType" = "COMMAND",
143
+ input_named_parameters: "tuple[str, ...] | None" = None,
144
+ applied_wrap_types: bool = False,
145
+ filter_hash: int = 0,
146
+ parameter_fingerprint: Any | None = None,
147
+ parameter_casts: "dict[int, str] | None" = None,
148
+ validation_errors: "list[str] | None" = None,
149
+ parameter_profile: "ParameterProfile | None" = None,
150
+ operation_profile: "OperationProfile | None" = None,
151
+ is_many: bool = False,
152
+ ) -> None:
153
+ self.compiled_sql = compiled_sql
154
+ self.execution_parameters = execution_parameters
155
+ self.parsed_expression = parsed_expression
156
+ self.operation_type = operation_type
157
+ self.input_named_parameters = input_named_parameters or ()
158
+ self.applied_wrap_types = applied_wrap_types
159
+ self.filter_hash = filter_hash
160
+ self.parameter_fingerprint = parameter_fingerprint
161
+ self.parameter_casts = parameter_casts or {}
162
+ self.validation_errors = validation_errors or []
163
+ self.parameter_profile = parameter_profile or ParameterProfile.empty()
164
+ self.operation_profile = operation_profile or OperationProfile.empty()
165
+ self.is_many = is_many
166
+
167
+ def __hash__(self) -> int:
168
+ return hash((self.compiled_sql, str(self.execution_parameters), self.operation_type))
169
+
170
+ def reset(self) -> None:
171
+ """Reset processing state for reuse."""
172
+ self.compiled_sql = ""
173
+ self.execution_parameters = []
174
+ self.parsed_expression = None
175
+ self.operation_type = "COMMAND"
176
+ self.input_named_parameters = ()
177
+ self.applied_wrap_types = False
178
+ self.filter_hash = 0
179
+ self.parameter_fingerprint = None
180
+ self.parameter_casts.clear()
181
+ self.validation_errors.clear()
182
+ self.parameter_profile = ParameterProfile.empty()
183
+ self.operation_profile = OperationProfile.empty()
184
+ self.is_many = False
185
+
186
+
187
+ @mypyc_attr(allow_interpreted_subclasses=False)
188
+ class SQL:
189
+ """SQL statement with parameter and filter support.
190
+
191
+ Represents a SQL statement that can be compiled with parameters and filters.
192
+ Supports both positional and named parameters, statement filtering,
193
+ and various execution modes including batch operations.
194
+ """
195
+
196
+ __slots__ = (
197
+ "_compiled_from_cache",
198
+ "_dialect",
199
+ "_filters",
200
+ "_hash",
201
+ "_is_cache_direct",
202
+ "_is_many",
203
+ "_is_script",
204
+ "_named_parameters",
205
+ "_original_parameters",
206
+ "_pooled",
207
+ "_positional_parameters",
208
+ "_processed_state",
209
+ "_raw_expression",
210
+ "_raw_sql",
211
+ "_sql_param_counters",
212
+ "_statement_config",
213
+ )
214
+
215
+ # Type annotation for mypyc compatibility
216
+ _sql_param_counters: "dict[str, int]"
217
+
218
+ @classmethod
219
+ def _create_cached_direct(cls, sql: str, config: "StatementConfig", processed_state: "ProcessedState") -> "SQL":
220
+ """Create a minimal SQL object for direct (fast-path) execution.
221
+
222
+ Bypasses standard __init__ and parameter processing.
223
+ Only for use by direct execution paths.
224
+ """
225
+ stmt = get_sql_pool().acquire()
226
+ stmt._raw_sql = sql
227
+ stmt._statement_config = config
228
+ stmt._dialect = stmt._normalize_dialect(config.dialect)
229
+ stmt._processed_state = processed_state
230
+ stmt._pooled = True
231
+ stmt._is_cache_direct = True
232
+ stmt._is_many = False
233
+ stmt._is_script = False
234
+ return stmt
235
+
236
+ def __init__(
237
+ self,
238
+ statement: "str | exp.Expr | 'SQL'",
239
+ *parameters: "Any | StatementFilter | list[Any | StatementFilter]",
240
+ statement_config: "StatementConfig | None" = None,
241
+ is_many: bool | None = None,
242
+ **kwargs: Any,
243
+ ) -> None:
244
+ """Initialize SQL statement.
245
+
246
+ Args:
247
+ statement: SQL string, expression, or existing SQL object
248
+ *parameters: Parameters and filters
249
+ statement_config: Configuration
250
+ is_many: Mark as execute_many operation
251
+ **kwargs: Additional parameters
252
+ """
253
+ config = statement_config or self._create_auto_config(statement, parameters, kwargs)
254
+ self._statement_config = config
255
+ self._dialect = self._normalize_dialect(config.dialect)
256
+ self._compiled_from_cache = False
257
+ self._pooled = False
258
+ self._is_cache_direct = False
259
+ self._processed_state: EmptyEnum | ProcessedState = Empty
260
+ self._hash: int | None = None
261
+ self._filters: list[StatementFilter] = []
262
+ self._named_parameters: dict[str, Any] = {}
263
+ self._positional_parameters: list[Any] = []
264
+ self._sql_param_counters = {}
265
+ self._is_script = False
266
+ self._raw_expression: exp.Expr | None = None
267
+
268
+ if isinstance(statement, SQL):
269
+ self._init_from_sql_object(statement)
270
+ if is_many is not None:
271
+ self._is_many = is_many
272
+ else:
273
+ if isinstance(statement, str):
274
+ self._raw_sql = statement
275
+ else:
276
+ dialect = self._dialect
277
+ self._raw_sql = statement.sql(dialect=str(dialect) if dialect else None)
278
+ self._raw_expression = statement
279
+
280
+ self._is_many = is_many if is_many is not None else self._should_auto_detect_many(parameters)
281
+
282
+ self._original_parameters = parameters
283
+ self._process_parameters(*parameters, **kwargs)
284
+
285
+ def _create_auto_config(
286
+ self, _statement: "str | exp.Expr | 'SQL'", _parameters: tuple, _kwargs: "dict[str, Any]"
287
+ ) -> "StatementConfig":
288
+ """Create default StatementConfig when none provided.
289
+
290
+ Args:
291
+ _statement: The SQL statement (unused)
292
+ _parameters: Statement parameters (unused)
293
+ _kwargs: Additional keyword arguments (unused)
294
+
295
+ Returns:
296
+ Default StatementConfig instance
297
+ """
298
+ return get_default_config()
299
+
300
+ def reset(self) -> None:
301
+ """Reset SQL object for reuse in pooling scenarios."""
302
+ if self._pooled and not self._compiled_from_cache and self._processed_state is not Empty:
303
+ get_processed_state_pool().release(self._processed_state)
304
+ self._compiled_from_cache = False
305
+ self._is_cache_direct = False
306
+ self._processed_state = Empty
307
+ self._hash = None
308
+ self._filters.clear()
309
+ self._named_parameters.clear()
310
+ self._positional_parameters.clear()
311
+ self._sql_param_counters.clear()
312
+ self._original_parameters = ()
313
+ self._is_many = False
314
+ self._is_script = False
315
+ self._raw_expression = None
316
+ self._raw_sql = ""
317
+ self._statement_config = get_default_config()
318
+ self._dialect = self._normalize_dialect(self._statement_config.dialect)
319
+
320
+ def _normalize_dialect(self, dialect: "DialectType") -> "str | None":
321
+ """Convert dialect to string representation.
322
+
323
+ Args:
324
+ dialect: Dialect type, string, or None
325
+
326
+ Returns:
327
+ String representation of the dialect or None
328
+ """
329
+ if dialect is None:
330
+ return None
331
+ if isinstance(dialect, str):
332
+ return dialect
333
+ return dialect.__class__.__name__.lower()
334
+
335
+ def _init_from_sql_object(self, sql_obj: "SQL") -> None:
336
+ """Initialize instance attributes from existing SQL object.
337
+
338
+ Args:
339
+ sql_obj: Existing SQL object to copy from
340
+ """
341
+ self._raw_sql = sql_obj.raw_sql
342
+ self._raw_expression = sql_obj.raw_expression
343
+ self._filters = sql_obj.filters.copy()
344
+ self._named_parameters = sql_obj.named_parameters.copy()
345
+ self._positional_parameters = sql_obj.positional_parameters.copy()
346
+ self._sql_param_counters = sql_obj._sql_param_counters.copy()
347
+ self._is_many = sql_obj.is_many
348
+ self._is_script = sql_obj.is_script
349
+ if sql_obj.is_processed:
350
+ self._processed_state = sql_obj.get_processed_state()
351
+
352
+ def _should_auto_detect_many(self, parameters: tuple) -> bool:
353
+ """Detect execute_many mode from parameter structure.
354
+
355
+ Args:
356
+ parameters: Parameter tuple to analyze
357
+
358
+ Returns:
359
+ True if parameters indicate batch execution
360
+ """
361
+ if len(parameters) == 1 and isinstance(parameters[0], list):
362
+ param_list = parameters[0]
363
+ if not param_list:
364
+ return False
365
+ # Optimization: Check only the first element for batch structure
366
+ # O(1) check instead of O(N) scan
367
+ first_item = param_list[0]
368
+ if isinstance(first_item, (tuple, list, dict)):
369
+ return len(param_list) > 1
370
+ return False
371
+
372
+ def _process_parameters(self, *parameters: Any, dialect: str | None = None, **kwargs: Any) -> None:
373
+ """Process and organize parameters and filters.
374
+
375
+ Args:
376
+ *parameters: Variable parameters and filters
377
+ dialect: SQL dialect override
378
+ **kwargs: Additional named parameters
379
+ """
380
+ if dialect is not None:
381
+ self._dialect = self._normalize_dialect(dialect)
382
+
383
+ if "is_script" in kwargs:
384
+ self._is_script = bool(kwargs.pop("is_script"))
385
+
386
+ self._filters.extend(self._extract_filters(parameters))
387
+ self._normalize_parameters(parameters)
388
+ self._named_parameters.update(kwargs)
389
+
390
+ def _extract_filters(self, parameters: "tuple[Any, ...]") -> "list[StatementFilter]":
391
+ return [p for p in parameters if is_statement_filter(p)]
392
+
393
+ def _normalize_parameters(self, parameters: "tuple[Any, ...]") -> None:
394
+ if not parameters:
395
+ return
396
+
397
+ # Optimization: Fast path for single parameter (most common case)
398
+ if len(parameters) == 1:
399
+ param = parameters[0]
400
+ # Fast check for simple types before filter check
401
+ if isinstance(param, (str, int, float, bool)) or param is None:
402
+ self._positional_parameters.append(param)
403
+ return
404
+
405
+ if is_statement_filter(param):
406
+ return
407
+
408
+ if isinstance(param, dict):
409
+ self._named_parameters.update(param)
410
+ elif isinstance(param, (list, tuple)):
411
+ if self._is_many:
412
+ self._positional_parameters = list(param)
413
+ else:
414
+ self._positional_parameters.extend(param)
415
+ else:
416
+ self._positional_parameters.append(param)
417
+ return
418
+
419
+ # Multiple parameters: check for filters
420
+ # O(N) check only if we have more than 1 param
421
+ has_filter = any(is_statement_filter(p) for p in parameters)
422
+ if not has_filter:
423
+ self._positional_parameters.extend(parameters)
424
+ return
425
+
426
+ actual_params = [p for p in parameters if not is_statement_filter(p)]
427
+ if not actual_params:
428
+ return
429
+
430
+ if len(actual_params) == 1:
431
+ param = actual_params[0]
432
+ if isinstance(param, dict):
433
+ self._named_parameters.update(param)
434
+ elif isinstance(param, (list, tuple)):
435
+ if self._is_many:
436
+ self._positional_parameters = list(param)
437
+ else:
438
+ self._positional_parameters.extend(param)
439
+ else:
440
+ self._positional_parameters.append(param)
441
+ else:
442
+ self._positional_parameters.extend(actual_params)
443
+
444
+ @property
445
+ def sql(self) -> str:
446
+ """Get the raw SQL string."""
447
+ return self._raw_sql
448
+
449
+ @property
450
+ def raw_sql(self) -> str:
451
+ """Get raw SQL string (public API).
452
+
453
+ Returns:
454
+ The raw SQL string
455
+ """
456
+ return self._raw_sql
457
+
458
+ @property
459
+ def parameters(self) -> Any:
460
+ """Get the original parameters."""
461
+ if self._named_parameters:
462
+ return self._named_parameters
463
+ return self._positional_parameters or []
464
+
465
+ @property
466
+ def positional_parameters(self) -> "list[Any]":
467
+ """Get positional parameters (public API)."""
468
+ return self._positional_parameters or []
469
+
470
+ @property
471
+ def named_parameters(self) -> "dict[str, Any]":
472
+ """Get named parameters (public API)."""
473
+ return self._named_parameters
474
+
475
+ @property
476
+ def original_parameters(self) -> Any:
477
+ """Get original parameters (public API)."""
478
+ return self._original_parameters
479
+
480
+ @property
481
+ def operation_type(self) -> "OperationType":
482
+ """SQL operation type."""
483
+ if self._processed_state is Empty:
484
+ return "COMMAND"
485
+ return self._processed_state.operation_type
486
+
487
+ @property
488
+ def statement_config(self) -> "StatementConfig":
489
+ """Statement configuration."""
490
+ return self._statement_config
491
+
492
+ @property
493
+ def expression(self) -> "exp.Expr | None":
494
+ """SQLGlot expression."""
495
+ if self._processed_state is not Empty:
496
+ return self._processed_state.parsed_expression
497
+ return self._raw_expression
498
+
499
+ @property
500
+ def raw_expression(self) -> "exp.Expr | None":
501
+ """Original expression supplied at construction, if available."""
502
+ return self._raw_expression
503
+
504
+ @property
505
+ def filters(self) -> "list[StatementFilter]":
506
+ """Applied filters."""
507
+ return self._filters.copy()
508
+
509
+ def get_filters_view(self) -> "FiltersView":
510
+ """Get zero-copy filters view (public API).
511
+
512
+ Returns:
513
+ Read-only view of filters without copying
514
+ """
515
+ return FiltersView(self._filters)
516
+
517
+ @property
518
+ def is_processed(self) -> bool:
519
+ """Check if SQL has been processed (public API)."""
520
+ return self._processed_state is not Empty
521
+
522
+ def get_processed_state(self) -> Any:
523
+ """Get processed state (public API)."""
524
+ return self._processed_state
525
+
526
+ @property
527
+ def dialect(self) -> "str | None":
528
+ """SQL dialect."""
529
+ return self._dialect
530
+
531
+ @property
532
+ def statement_expression(self) -> "exp.Expr | None":
533
+ """Get parsed statement expression (public API).
534
+
535
+ Returns:
536
+ Parsed SQLGlot expression or None if not parsed
537
+ """
538
+ if self._processed_state is not Empty:
539
+ return self._processed_state.parsed_expression
540
+ return self._raw_expression
541
+
542
+ @property
543
+ def is_many(self) -> bool:
544
+ """Check if this is execute_many."""
545
+ return self._is_many
546
+
547
+ @property
548
+ def is_script(self) -> bool:
549
+ """Check if this is script execution."""
550
+ return self._is_script
551
+
552
+ @property
553
+ def validation_errors(self) -> "list[str]":
554
+ """Validation errors."""
555
+ if self._processed_state is Empty:
556
+ return []
557
+ return self._processed_state.validation_errors.copy()
558
+
559
+ @property
560
+ def has_errors(self) -> bool:
561
+ """Check if there are validation errors."""
562
+ return len(self.validation_errors) > 0
563
+
564
+ def returns_rows(self) -> bool:
565
+ """Check if statement returns rows.
566
+
567
+ Returns:
568
+ True if the SQL statement returns result rows
569
+ """
570
+ if self._processed_state is Empty:
571
+ self.compile()
572
+ if self._processed_state is Empty:
573
+ return False
574
+
575
+ profile = self._processed_state.operation_profile
576
+ if profile.returns_rows:
577
+ return True
578
+
579
+ op_type = self._processed_state.operation_type
580
+ if op_type in RETURNS_ROWS_OPERATIONS:
581
+ return True
582
+
583
+ if self._processed_state.parsed_expression:
584
+ expr = self._processed_state.parsed_expression
585
+ if isinstance(expr, (exp.Insert, exp.Update, exp.Delete)) and expr.args.get("returning"):
586
+ return True
587
+
588
+ return False
589
+
590
+ def is_modifying_operation(self) -> bool:
591
+ """Check if the SQL statement is a modifying operation.
592
+
593
+ Returns:
594
+ True if the operation modifies data (INSERT/UPDATE/DELETE)
595
+ """
596
+ if self._processed_state is Empty:
597
+ return False
598
+
599
+ profile = self._processed_state.operation_profile
600
+ if profile.modifies_rows:
601
+ return True
602
+
603
+ op_type = self._processed_state.operation_type
604
+ if op_type in MODIFYING_OPERATIONS:
605
+ return True
606
+
607
+ if self._processed_state.parsed_expression:
608
+ return isinstance(self._processed_state.parsed_expression, (exp.Insert, exp.Update, exp.Delete, exp.Merge))
609
+
610
+ return False
611
+
612
+ def compile(self) -> "tuple[str, Any]":
613
+ """Compile SQL statement with parameters.
614
+
615
+ Returns:
616
+ Tuple of compiled SQL string and execution parameters
617
+ """
618
+ if self._processed_state is not Empty:
619
+ if self._compiled_from_cache:
620
+ state = self._processed_state
621
+ if state.execution_parameters is None:
622
+ self._processed_state = Empty
623
+ self._compiled_from_cache = False
624
+ else:
625
+ can_reuse = (
626
+ not self._statement_config.parameter_config.needs_static_script_compilation
627
+ and self._can_reuse_cached_state(state)
628
+ )
629
+ if can_reuse:
630
+ return self._rebind_cached_parameters(state)
631
+ self._processed_state = Empty
632
+ self._compiled_from_cache = False
633
+ else:
634
+ return self._processed_state.compiled_sql, self._processed_state.execution_parameters
635
+ if self._processed_state is Empty:
636
+ try:
637
+ config = self._statement_config
638
+ raw_sql = self._raw_sql
639
+ params = self._named_parameters or self._positional_parameters
640
+ is_many = self._is_many
641
+ param_fingerprint = structural_fingerprint(params, is_many=is_many)
642
+ compiled_result = pipeline.compile_with_pipeline(
643
+ config, raw_sql, params, is_many=is_many, expression=self._raw_expression
644
+ )
645
+
646
+ self._processed_state = self._build_processed_state(
647
+ compiled_sql=compiled_result.compiled_sql,
648
+ execution_parameters=compiled_result.execution_parameters,
649
+ parsed_expression=compiled_result.expression,
650
+ operation_type=compiled_result.operation_type,
651
+ input_named_parameters=compiled_result.input_named_parameters,
652
+ applied_wrap_types=compiled_result.applied_wrap_types,
653
+ filter_hash=hash_filters(self._filters),
654
+ parameter_fingerprint=param_fingerprint,
655
+ parameter_casts=compiled_result.parameter_casts,
656
+ parameter_profile=compiled_result.parameter_profile,
657
+ operation_profile=compiled_result.operation_profile,
658
+ validation_errors=[],
659
+ is_many=self._is_many,
660
+ )
661
+ except sqlspec.exceptions.SQLSpecError:
662
+ raise
663
+ except Exception as e:
664
+ self._processed_state = self._handle_compile_failure(e)
665
+
666
+ return self._processed_state.compiled_sql, self._processed_state.execution_parameters
667
+
668
+ def _rebind_cached_parameters(self, state: "ProcessedState") -> "tuple[str, Any]":
669
+ params = self._named_parameters or self._positional_parameters
670
+ processor = ParameterProcessor(
671
+ converter=self._statement_config.parameter_converter,
672
+ validator=self._statement_config.parameter_validator,
673
+ cache_max_size=0,
674
+ validator_cache_max_size=0,
675
+ )
676
+ rebound_params = processor._transform_cached_parameters( # pyright: ignore[reportPrivateUsage]
677
+ params,
678
+ state.parameter_profile,
679
+ self._statement_config.parameter_config,
680
+ input_named_parameters=state.input_named_parameters,
681
+ is_many=self._is_many,
682
+ apply_wrap_types=state.applied_wrap_types,
683
+ )
684
+ compiled_sql = state.compiled_sql
685
+ output_transformer = self._statement_config.output_transformer
686
+ if output_transformer:
687
+ compiled_sql, rebound_params = output_transformer(compiled_sql, rebound_params)
688
+ self._processed_state = self._build_processed_state(
689
+ compiled_sql=compiled_sql,
690
+ execution_parameters=rebound_params,
691
+ parsed_expression=state.parsed_expression,
692
+ operation_type=state.operation_type,
693
+ input_named_parameters=state.input_named_parameters,
694
+ applied_wrap_types=state.applied_wrap_types,
695
+ filter_hash=state.filter_hash,
696
+ parameter_fingerprint=state.parameter_fingerprint,
697
+ parameter_casts=state.parameter_casts,
698
+ parameter_profile=state.parameter_profile,
699
+ operation_profile=state.operation_profile,
700
+ validation_errors=state.validation_errors.copy(),
701
+ is_many=state.is_many,
702
+ )
703
+ self._compiled_from_cache = False
704
+ return compiled_sql, rebound_params
705
+
706
+ def _can_reuse_cached_state(self, state: "ProcessedState") -> bool:
707
+ cached_fingerprint = state.parameter_fingerprint
708
+ if cached_fingerprint is None:
709
+ return False
710
+ if state.is_many != self._is_many:
711
+ return False
712
+ if state.filter_hash != hash_filters(self._filters):
713
+ return False
714
+ params = self._named_parameters or self._positional_parameters
715
+ return bool(structural_fingerprint(params, is_many=self._is_many) == cached_fingerprint)
716
+
717
+ def as_script(self) -> "SQL":
718
+ """Create copy marked for script execution.
719
+
720
+ Returns:
721
+ New SQL instance configured for script execution
722
+ """
723
+ original_params = self._original_parameters
724
+ config = self._statement_config
725
+ is_many = self._is_many
726
+ statement_seed = self._raw_expression or self._raw_sql
727
+ new_sql = SQL(statement_seed, *original_params, statement_config=config, is_many=is_many)
728
+ new_sql._named_parameters.update(self._named_parameters)
729
+ new_sql._positional_parameters = self._positional_parameters.copy()
730
+ new_sql._filters = self._filters.copy()
731
+ new_sql._is_script = True
732
+ return new_sql
733
+
734
+ def copy(self, statement: "str | exp.Expr | None" = None, parameters: Any | None = None, **kwargs: Any) -> "SQL":
735
+ """Create copy with modifications.
736
+
737
+ Args:
738
+ statement: New SQL statement to use
739
+ parameters: New parameters to use
740
+ **kwargs: Additional modifications
741
+
742
+ Returns:
743
+ New SQL instance with modifications applied
744
+ """
745
+ # FAST PATH: Only parameters are changing
746
+ if statement is None and not kwargs and parameters is not None and not isinstance(parameters, (str, bytes)):
747
+ new_sql = self._create_empty_copy()
748
+ new_sql._process_parameters(*(parameters if isinstance(parameters, tuple) else (parameters,)))
749
+ return new_sql
750
+
751
+ statement_expression = self._raw_expression if statement is None else statement
752
+ new_sql = SQL(
753
+ statement_expression or self._raw_sql,
754
+ *(parameters if parameters is not None else self._original_parameters),
755
+ statement_config=self._statement_config,
756
+ is_many=self._is_many,
757
+ **kwargs,
758
+ )
759
+ if parameters is None:
760
+ new_sql._named_parameters.update(self._named_parameters)
761
+ new_sql._positional_parameters = self._positional_parameters.copy()
762
+ new_sql._filters = self._filters.copy()
763
+ return new_sql
764
+
765
+ def _create_empty_copy(self) -> "SQL":
766
+ """Create a shell copy with shared immutable state but empty mutable state."""
767
+ new_sql = get_sql_pool().acquire()
768
+ new_sql._raw_sql = self._raw_sql
769
+ new_sql._raw_expression = self._raw_expression
770
+ new_sql._statement_config = self._statement_config
771
+ new_sql._dialect = self._dialect
772
+ new_sql._is_many = self._is_many
773
+ new_sql._is_script = self._is_script
774
+ new_sql._original_parameters = ()
775
+ new_sql._pooled = True
776
+
777
+ # Reset mutable state
778
+ new_sql._compiled_from_cache = self._processed_state is not Empty
779
+ new_sql._processed_state = self._processed_state if self._processed_state is not Empty else Empty
780
+ new_sql._hash = None
781
+ new_sql._filters = self._filters.copy()
782
+ new_sql._named_parameters = {}
783
+ new_sql._positional_parameters = []
784
+ new_sql._sql_param_counters = self._sql_param_counters.copy()
785
+
786
+ return new_sql
787
+
788
+ def _build_processed_state(
789
+ self,
790
+ *,
791
+ compiled_sql: str,
792
+ execution_parameters: Any,
793
+ parsed_expression: "exp.Expr | None",
794
+ operation_type: "OperationType",
795
+ input_named_parameters: "tuple[str, ...] | None",
796
+ applied_wrap_types: bool,
797
+ filter_hash: int,
798
+ parameter_fingerprint: Any | None,
799
+ parameter_casts: "dict[int, str] | None",
800
+ parameter_profile: "ParameterProfile | None",
801
+ operation_profile: "OperationProfile | None",
802
+ validation_errors: "list[str] | None",
803
+ is_many: bool,
804
+ ) -> "ProcessedState":
805
+ state = get_processed_state_pool().acquire()
806
+ ProcessedState.__init__(
807
+ state,
808
+ compiled_sql=compiled_sql,
809
+ execution_parameters=execution_parameters,
810
+ parsed_expression=parsed_expression,
811
+ operation_type=operation_type,
812
+ input_named_parameters=input_named_parameters,
813
+ applied_wrap_types=applied_wrap_types,
814
+ filter_hash=filter_hash,
815
+ parameter_fingerprint=parameter_fingerprint,
816
+ parameter_casts=parameter_casts,
817
+ validation_errors=validation_errors,
818
+ parameter_profile=parameter_profile,
819
+ operation_profile=operation_profile,
820
+ is_many=is_many,
821
+ )
822
+ return state
823
+
824
+ def _handle_compile_failure(self, error: Exception) -> ProcessedState:
825
+ import traceback
826
+
827
+ traceback.print_exc()
828
+ logger.debug("Processing failed, using fallback: %s", error)
829
+ params = self._named_parameters or self._positional_parameters
830
+ return self._build_processed_state(
831
+ compiled_sql=self._raw_sql,
832
+ execution_parameters=self._named_parameters or self._positional_parameters,
833
+ parsed_expression=None,
834
+ operation_type="COMMAND",
835
+ input_named_parameters=(),
836
+ applied_wrap_types=False,
837
+ filter_hash=hash_filters(self._filters),
838
+ parameter_fingerprint=structural_fingerprint(params, is_many=self._is_many),
839
+ parameter_casts={},
840
+ parameter_profile=ParameterProfile.empty(),
841
+ operation_profile=OperationProfile.empty(),
842
+ validation_errors=[str(error)],
843
+ is_many=self._is_many,
844
+ )
845
+
846
+ # ==========================================================================
847
+ # Parameter Generation Helpers
848
+ # ==========================================================================
849
+
850
+ def _generate_sql_param_name(self, base_name: str) -> str:
851
+ """Generate unique parameter name with _sqlspec_ prefix.
852
+
853
+ Uses _sqlspec_ prefix to avoid collision with user-provided parameters.
854
+ Auto-generated parameters are namespaced to prevent conflicts.
855
+
856
+ Args:
857
+ base_name: The base name for the parameter (e.g., column name)
858
+
859
+ Returns:
860
+ A unique parameter name that doesn't exist in current parameters
861
+ """
862
+ prefixed_base = f"_sqlspec_{base_name}"
863
+ current_index = self._sql_param_counters.get(prefixed_base, 0)
864
+
865
+ if prefixed_base not in self._named_parameters:
866
+ self._sql_param_counters[prefixed_base] = current_index
867
+ return prefixed_base
868
+
869
+ next_index = current_index + 1
870
+ candidate = f"{prefixed_base}_{next_index}"
871
+
872
+ while candidate in self._named_parameters:
873
+ next_index += 1
874
+ if next_index > _MAX_PARAM_COLLISION_ATTEMPTS:
875
+ return f"{prefixed_base}_{uuid.uuid4().hex[:8]}"
876
+ candidate = f"{prefixed_base}_{next_index}"
877
+
878
+ self._sql_param_counters[prefixed_base] = next_index
879
+ return candidate
880
+
881
+ def _get_or_parse_expression(self) -> exp.Expr:
882
+ """Get the current expression or parse the raw SQL.
883
+
884
+ Prefers cached parsed expression over re-parsing raw SQL.
885
+
886
+ Returns:
887
+ The SQLGlot expression for this statement
888
+ """
889
+ # First check processed state for parsed expression
890
+ if self._processed_state is not Empty and self._processed_state.parsed_expression is not None:
891
+ return self._processed_state.parsed_expression.copy()
892
+ # Then check statement_expression (from compilation)
893
+ if self.statement_expression is not None:
894
+ return self.statement_expression.copy()
895
+ # Then check raw_expression (from construction)
896
+ if self._raw_expression is not None:
897
+ return self._raw_expression.copy()
898
+ # Fall back to parsing if enabled
899
+ if not self._statement_config.enable_parsing:
900
+ return exp.Select().from_(f"({self._raw_sql})")
901
+ try:
902
+ parsed = sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
903
+ if isinstance(parsed, exp.Expr):
904
+ return parsed
905
+ return exp.Select().from_(f"({self._raw_sql})")
906
+ except ParseError:
907
+ return exp.Select().from_(f"({self._raw_sql})")
908
+
909
+ def _create_modified_copy_with_expression(self, new_expr: "exp.Expr") -> "SQL":
910
+ """Create a new SQL instance with a modified expression.
911
+
912
+ Args:
913
+ new_expr: The new SQLGlot expression
914
+
915
+ Returns:
916
+ New SQL instance with the expression and copied state
917
+ """
918
+ new_sql = SQL(
919
+ new_expr, *self._original_parameters, statement_config=self._statement_config, is_many=self._is_many
920
+ )
921
+ new_sql._named_parameters.update(self._named_parameters)
922
+ new_sql._positional_parameters = self._positional_parameters.copy()
923
+ new_sql._filters = self._filters.copy()
924
+ new_sql._sql_param_counters = self._sql_param_counters.copy()
925
+ return new_sql
926
+
927
+ def add_named_parameter(self, name: str, value: Any) -> "SQL":
928
+ """Add a named parameter and return a new SQL instance.
929
+
930
+ Args:
931
+ name: Parameter name
932
+ value: Parameter value
933
+
934
+ Returns:
935
+ New SQL instance with the added parameter
936
+ """
937
+ original_params = self._original_parameters
938
+ config = self._statement_config
939
+ is_many = self._is_many
940
+ statement_seed = self._raw_expression or self._raw_sql
941
+ new_sql = SQL(statement_seed, *original_params, statement_config=config, is_many=is_many)
942
+ new_sql._named_parameters.update(self._named_parameters)
943
+ new_sql._named_parameters[name] = value
944
+ new_sql._positional_parameters = self._positional_parameters.copy()
945
+ new_sql._filters = self._filters.copy()
946
+ return new_sql
947
+
948
+ def where(self, condition: "str | exp.Expr") -> "SQL":
949
+ """Add WHERE condition to the SQL statement.
950
+
951
+ Args:
952
+ condition: WHERE condition as string or SQLGlot expression
953
+
954
+ Returns:
955
+ New SQL instance with the WHERE condition applied
956
+ """
957
+ if self.statement_expression is not None:
958
+ current_expr = self.statement_expression.copy()
959
+ elif not self._statement_config.enable_parsing:
960
+ current_expr = exp.Select().from_(f"({self._raw_sql})")
961
+ else:
962
+ try:
963
+ current_expr = sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
964
+ except ParseError:
965
+ subquery_sql = f"SELECT * FROM ({self._raw_sql}) AS subquery"
966
+ current_expr = sqlglot.parse_one(subquery_sql, dialect=self._dialect)
967
+
968
+ condition_expr: exp.Expr
969
+ if isinstance(condition, str):
970
+ if not self._statement_config.enable_parsing:
971
+ condition_expr = exp.Condition(this=condition)
972
+ else:
973
+ try:
974
+ condition_expr = sqlglot.parse_one(condition, dialect=self._dialect, into=exp.Condition)
975
+ except ParseError:
976
+ condition_expr = exp.Condition(this=condition)
977
+ else:
978
+ condition_expr = condition
979
+
980
+ if isinstance(current_expr, exp.Select) or supports_where(current_expr):
981
+ new_expr = current_expr.where(condition_expr, copy=False)
982
+ else:
983
+ new_expr = exp.Select().from_(current_expr).where(condition_expr, copy=False)
984
+
985
+ original_params = self._original_parameters
986
+ config = self._statement_config
987
+ is_many = self._is_many
988
+ new_sql = SQL(new_expr, *original_params, statement_config=config, is_many=is_many)
989
+
990
+ new_sql._named_parameters.update(self._named_parameters)
991
+ new_sql._positional_parameters = self._positional_parameters.copy()
992
+ new_sql._filters = self._filters.copy()
993
+ return new_sql
994
+
995
+ # ==========================================================================
996
+ # Parameterized WHERE Methods (using shared utilities)
997
+ # ==========================================================================
998
+
999
+ def where_eq(self, column: "str | exp.Column", value: Any) -> "SQL":
1000
+ """Add WHERE column = value condition.
1001
+
1002
+ Args:
1003
+ column: Column name or expression
1004
+ value: Value to compare against
1005
+
1006
+ Returns:
1007
+ New SQL instance with WHERE condition applied
1008
+ """
1009
+ expression = self._get_or_parse_expression()
1010
+ col_name = extract_column_name(column)
1011
+ param_name = self._generate_sql_param_name(col_name)
1012
+ condition = create_condition(column, param_name, expr_eq)
1013
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1014
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1015
+ new_sql._named_parameters[param_name] = value
1016
+ return new_sql
1017
+
1018
+ def where_neq(self, column: "str | exp.Column", value: Any) -> "SQL":
1019
+ """Add WHERE column != value condition.
1020
+
1021
+ Args:
1022
+ column: Column name or expression
1023
+ value: Value to compare against
1024
+
1025
+ Returns:
1026
+ New SQL instance with WHERE condition applied
1027
+ """
1028
+ expression = self._get_or_parse_expression()
1029
+ col_name = extract_column_name(column)
1030
+ param_name = self._generate_sql_param_name(col_name)
1031
+ condition = create_condition(column, param_name, expr_neq)
1032
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1033
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1034
+ new_sql._named_parameters[param_name] = value
1035
+ return new_sql
1036
+
1037
+ def where_lt(self, column: "str | exp.Column", value: Any) -> "SQL":
1038
+ """Add WHERE column < value condition.
1039
+
1040
+ Args:
1041
+ column: Column name or expression
1042
+ value: Value to compare against
1043
+
1044
+ Returns:
1045
+ New SQL instance with WHERE condition applied
1046
+ """
1047
+ expression = self._get_or_parse_expression()
1048
+ col_name = extract_column_name(column)
1049
+ param_name = self._generate_sql_param_name(col_name)
1050
+ condition = create_condition(column, param_name, expr_lt)
1051
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1052
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1053
+ new_sql._named_parameters[param_name] = value
1054
+ return new_sql
1055
+
1056
+ def where_lte(self, column: "str | exp.Column", value: Any) -> "SQL":
1057
+ """Add WHERE column <= value condition.
1058
+
1059
+ Args:
1060
+ column: Column name or expression
1061
+ value: Value to compare against
1062
+
1063
+ Returns:
1064
+ New SQL instance with WHERE condition applied
1065
+ """
1066
+ expression = self._get_or_parse_expression()
1067
+ col_name = extract_column_name(column)
1068
+ param_name = self._generate_sql_param_name(col_name)
1069
+ condition = create_condition(column, param_name, expr_lte)
1070
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1071
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1072
+ new_sql._named_parameters[param_name] = value
1073
+ return new_sql
1074
+
1075
+ def where_gt(self, column: "str | exp.Column", value: Any) -> "SQL":
1076
+ """Add WHERE column > value condition.
1077
+
1078
+ Args:
1079
+ column: Column name or expression
1080
+ value: Value to compare against
1081
+
1082
+ Returns:
1083
+ New SQL instance with WHERE condition applied
1084
+ """
1085
+ expression = self._get_or_parse_expression()
1086
+ col_name = extract_column_name(column)
1087
+ param_name = self._generate_sql_param_name(col_name)
1088
+ condition = create_condition(column, param_name, expr_gt)
1089
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1090
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1091
+ new_sql._named_parameters[param_name] = value
1092
+ return new_sql
1093
+
1094
+ def where_gte(self, column: "str | exp.Column", value: Any) -> "SQL":
1095
+ """Add WHERE column >= value condition.
1096
+
1097
+ Args:
1098
+ column: Column name or expression
1099
+ value: Value to compare against
1100
+
1101
+ Returns:
1102
+ New SQL instance with WHERE condition applied
1103
+ """
1104
+ expression = self._get_or_parse_expression()
1105
+ col_name = extract_column_name(column)
1106
+ param_name = self._generate_sql_param_name(col_name)
1107
+ condition = create_condition(column, param_name, expr_gte)
1108
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1109
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1110
+ new_sql._named_parameters[param_name] = value
1111
+ return new_sql
1112
+
1113
+ def where_like(self, column: "str | exp.Column", pattern: str) -> "SQL":
1114
+ """Add WHERE column LIKE pattern condition.
1115
+
1116
+ Args:
1117
+ column: Column name or expression
1118
+ pattern: LIKE pattern (e.g., '%search%')
1119
+
1120
+ Returns:
1121
+ New SQL instance with WHERE condition applied
1122
+ """
1123
+ expression = self._get_or_parse_expression()
1124
+ col_name = extract_column_name(column)
1125
+ param_name = self._generate_sql_param_name(col_name)
1126
+ condition = create_condition(column, param_name, expr_like)
1127
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1128
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1129
+ new_sql._named_parameters[param_name] = pattern
1130
+ return new_sql
1131
+
1132
+ def where_ilike(self, column: "str | exp.Column", pattern: str) -> "SQL":
1133
+ """Add WHERE column ILIKE pattern condition (case-insensitive).
1134
+
1135
+ Args:
1136
+ column: Column name or expression
1137
+ pattern: ILIKE pattern (e.g., '%search%')
1138
+
1139
+ Returns:
1140
+ New SQL instance with WHERE condition applied
1141
+ """
1142
+ expression = self._get_or_parse_expression()
1143
+ col_name = extract_column_name(column)
1144
+ param_name = self._generate_sql_param_name(col_name)
1145
+ condition = create_condition(column, param_name, expr_ilike)
1146
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1147
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1148
+ new_sql._named_parameters[param_name] = pattern
1149
+ return new_sql
1150
+
1151
+ def where_is_null(self, column: "str | exp.Column") -> "SQL":
1152
+ """Add WHERE column IS NULL condition.
1153
+
1154
+ Args:
1155
+ column: Column name or expression
1156
+
1157
+ Returns:
1158
+ New SQL instance with WHERE condition applied
1159
+ """
1160
+ expression = self._get_or_parse_expression()
1161
+ condition = create_condition(column, "_unused", expr_is_null)
1162
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1163
+ return self._create_modified_copy_with_expression(new_expr)
1164
+
1165
+ def where_is_not_null(self, column: "str | exp.Column") -> "SQL":
1166
+ """Add WHERE column IS NOT NULL condition.
1167
+
1168
+ Args:
1169
+ column: Column name or expression
1170
+
1171
+ Returns:
1172
+ New SQL instance with WHERE condition applied
1173
+ """
1174
+ expression = self._get_or_parse_expression()
1175
+ condition = create_condition(column, "_unused", expr_is_not_null)
1176
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1177
+ return self._create_modified_copy_with_expression(new_expr)
1178
+
1179
+ def where_in(self, column: "str | exp.Column", values: "Sequence[Any]") -> "SQL":
1180
+ """Add WHERE column IN (values) condition.
1181
+
1182
+ Args:
1183
+ column: Column name or expression
1184
+ values: Sequence of values for IN clause
1185
+
1186
+ Returns:
1187
+ New SQL instance with WHERE condition applied
1188
+ """
1189
+ if not values:
1190
+ expression = self._get_or_parse_expression()
1191
+ false_condition = exp.EQ(this=exp.Literal.number(1), expression=exp.Literal.number(0))
1192
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, false_condition))
1193
+ return self._create_modified_copy_with_expression(new_expr)
1194
+
1195
+ expression = self._get_or_parse_expression()
1196
+ col_name = extract_column_name(column)
1197
+
1198
+ param_names: list[str] = []
1199
+ param_values: dict[str, Any] = {}
1200
+ for i, val in enumerate(values):
1201
+ param_name = self._generate_sql_param_name(f"{col_name}_in_{i}")
1202
+ param_names.append(param_name)
1203
+ param_values[param_name] = val
1204
+
1205
+ condition = create_in_condition(column, param_names)
1206
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1207
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1208
+ new_sql._named_parameters.update(param_values)
1209
+ return new_sql
1210
+
1211
+ def where_not_in(self, column: "str | exp.Column", values: "Sequence[Any]") -> "SQL":
1212
+ """Add WHERE column NOT IN (values) condition.
1213
+
1214
+ Args:
1215
+ column: Column name or expression
1216
+ values: Sequence of values for NOT IN clause
1217
+
1218
+ Returns:
1219
+ New SQL instance with WHERE condition applied
1220
+ """
1221
+ if not values:
1222
+ return self
1223
+
1224
+ expression = self._get_or_parse_expression()
1225
+ col_name = extract_column_name(column)
1226
+
1227
+ param_names: list[str] = []
1228
+ param_values: dict[str, Any] = {}
1229
+ for i, val in enumerate(values):
1230
+ param_name = self._generate_sql_param_name(f"{col_name}_not_in_{i}")
1231
+ param_names.append(param_name)
1232
+ param_values[param_name] = val
1233
+
1234
+ condition = create_not_in_condition(column, param_names)
1235
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1236
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1237
+ new_sql._named_parameters.update(param_values)
1238
+ return new_sql
1239
+
1240
+ def where_between(self, column: "str | exp.Column", low: Any, high: Any) -> "SQL":
1241
+ """Add WHERE column BETWEEN low AND high condition.
1242
+
1243
+ Args:
1244
+ column: Column name or expression
1245
+ low: Lower bound value
1246
+ high: Upper bound value
1247
+
1248
+ Returns:
1249
+ New SQL instance with WHERE condition applied
1250
+ """
1251
+ expression = self._get_or_parse_expression()
1252
+ col_name = extract_column_name(column)
1253
+ low_param = self._generate_sql_param_name(f"{col_name}_low")
1254
+ high_param = self._generate_sql_param_name(f"{col_name}_high")
1255
+ condition = create_between_condition(column, low_param, high_param)
1256
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_where(e, condition))
1257
+ new_sql = self._create_modified_copy_with_expression(new_expr)
1258
+ new_sql._named_parameters[low_param] = low
1259
+ new_sql._named_parameters[high_param] = high
1260
+ return new_sql
1261
+
1262
+ def order_by(self, *items: "str | exp.Expr", desc: bool = False) -> "SQL":
1263
+ """Add ORDER BY clause to the SQL statement.
1264
+
1265
+ Args:
1266
+ *items: ORDER BY expressions as strings or SQLGlot expressions
1267
+ desc: Apply descending order to each item
1268
+
1269
+ Returns:
1270
+ New SQL instance with ORDER BY applied
1271
+ """
1272
+ if not items:
1273
+ return self
1274
+
1275
+ if self.statement_expression is not None:
1276
+ current_expr = self.statement_expression.copy()
1277
+ elif not self._statement_config.enable_parsing:
1278
+ current_expr = exp.Select().from_(f"({self._raw_sql})")
1279
+ else:
1280
+ try:
1281
+ current_expr = sqlglot.parse_one(self._raw_sql, dialect=self._dialect)
1282
+ except ParseError:
1283
+ current_expr = exp.Select().from_(f"({self._raw_sql})")
1284
+
1285
+ def parse_order_item(order_item: str) -> exp.Expr:
1286
+ normalized = order_item.strip()
1287
+ if not normalized:
1288
+ return exp.column(order_item)
1289
+
1290
+ if self._statement_config.enable_parsing:
1291
+ try:
1292
+ parsed = sqlglot.parse_one(normalized, dialect=self._dialect, into=exp.Ordered)
1293
+ except ParseError:
1294
+ parsed = None
1295
+ if parsed is not None:
1296
+ return parsed
1297
+
1298
+ parts = normalized.rsplit(None, 1)
1299
+ if len(parts) == _ORDER_PARTS_COUNT and parts[1].lower() in {"asc", "desc"}:
1300
+ base_expr = exp.column(parts[0]) if parts[0] else exp.column(normalized)
1301
+ return base_expr.desc() if parts[1].lower() == "desc" else base_expr.asc()
1302
+
1303
+ return exp.column(normalized)
1304
+
1305
+ new_expr: exp.Expr = current_expr
1306
+ for item in items:
1307
+ if isinstance(item, str):
1308
+ order_expr = parse_order_item(item)
1309
+ if desc and not isinstance(order_expr, exp.Ordered):
1310
+ order_expr = order_expr.desc()
1311
+ else:
1312
+ order_expr = item.desc() if desc and not isinstance(item, exp.Ordered) else item
1313
+ if isinstance(new_expr, exp.Select):
1314
+ new_expr = new_expr.order_by(order_expr, copy=False)
1315
+ else:
1316
+ new_expr = exp.Select().from_(new_expr).order_by(order_expr)
1317
+
1318
+ original_params = self._original_parameters
1319
+ config = self._statement_config
1320
+ is_many = self._is_many
1321
+ stmt_expr: exp.Expr = new_expr if isinstance(new_expr, exp.Expr) else exp.Select().from_(new_expr)
1322
+ new_sql = SQL(stmt_expr, *original_params, statement_config=config, is_many=is_many)
1323
+
1324
+ new_sql._named_parameters.update(self._named_parameters)
1325
+ new_sql._positional_parameters = self._positional_parameters.copy()
1326
+ new_sql._filters = self._filters.copy()
1327
+ return new_sql
1328
+
1329
+ # ==========================================================================
1330
+ # Pagination Methods
1331
+ # ==========================================================================
1332
+
1333
+ def limit(self, value: int) -> "SQL":
1334
+ """Add LIMIT clause to the SQL statement.
1335
+
1336
+ Args:
1337
+ value: Maximum number of rows to return
1338
+
1339
+ Returns:
1340
+ New SQL instance with LIMIT applied
1341
+
1342
+ Raises:
1343
+ SQLSpecError: If statement is not a SELECT
1344
+ """
1345
+ expression = self._get_or_parse_expression()
1346
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_limit(e, value))
1347
+ return self._create_modified_copy_with_expression(new_expr)
1348
+
1349
+ def offset(self, value: int) -> "SQL":
1350
+ """Add OFFSET clause to the SQL statement.
1351
+
1352
+ Args:
1353
+ value: Number of rows to skip
1354
+
1355
+ Returns:
1356
+ New SQL instance with OFFSET applied
1357
+
1358
+ Raises:
1359
+ SQLSpecError: If statement is not a SELECT
1360
+ """
1361
+ expression = self._get_or_parse_expression()
1362
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_offset(e, value))
1363
+ return self._create_modified_copy_with_expression(new_expr)
1364
+
1365
+ def paginate(self, page: int, page_size: int) -> "SQL":
1366
+ """Add LIMIT and OFFSET for pagination.
1367
+
1368
+ Args:
1369
+ page: Page number (1-indexed)
1370
+ page_size: Number of items per page
1371
+
1372
+ Returns:
1373
+ New SQL instance with LIMIT and OFFSET applied
1374
+
1375
+ Example:
1376
+ # Get page 3 with 20 items per page
1377
+ stmt = SQL("SELECT * FROM users").paginate(3, 20)
1378
+ # Results in: SELECT * FROM users LIMIT 20 OFFSET 40
1379
+ """
1380
+ if page < 1:
1381
+ msg = "paginate page must be >= 1"
1382
+ raise sqlspec.exceptions.SQLSpecError(msg)
1383
+ if page_size < 1:
1384
+ msg = "paginate page_size must be >= 1"
1385
+ raise sqlspec.exceptions.SQLSpecError(msg)
1386
+ offset_value = (page - 1) * page_size
1387
+ return self.limit(page_size).offset(offset_value)
1388
+
1389
+ # ==========================================================================
1390
+ # Column Projection Methods
1391
+ # ==========================================================================
1392
+
1393
+ def select_only(self, *columns: "str | exp.Expr", prune_columns: bool | None = None) -> "SQL":
1394
+ """Replace SELECT columns with only the specified columns.
1395
+
1396
+ This is useful for narrowing down the columns returned by a query
1397
+ without modifying the FROM clause or WHERE conditions.
1398
+
1399
+ Args:
1400
+ *columns: Column names or expressions to select
1401
+ prune_columns: Remove unused columns from subqueries. When True,
1402
+ applies SQLGlot's qualify and pushdown_projections optimizations.
1403
+ Defaults to the config's enable_column_pruning setting.
1404
+
1405
+ Returns:
1406
+ New SQL instance with only the specified columns
1407
+
1408
+ Example:
1409
+ stmt = SQL("SELECT * FROM users WHERE active = 1")
1410
+ narrow = stmt.select_only("id", "name", "email")
1411
+ # Results in: SELECT id, name, email FROM users WHERE active = 1
1412
+
1413
+ # With column pruning on a subquery:
1414
+ stmt = SQL("SELECT * FROM (SELECT id, name, email, created_at FROM users) AS u")
1415
+ narrow = stmt.select_only("id", "name", prune_columns=True)
1416
+ # Results in: SELECT id, name FROM (SELECT id, name FROM users) AS u
1417
+ """
1418
+ if not columns:
1419
+ return self
1420
+
1421
+ expression = self._get_or_parse_expression()
1422
+ new_expr = safe_modify_with_cte(expression, lambda e: apply_select_only(e, columns))
1423
+
1424
+ # Determine whether to apply column pruning
1425
+ should_prune = prune_columns if prune_columns is not None else self._statement_config.enable_column_pruning
1426
+
1427
+ if should_prune:
1428
+ if self._statement_config.enable_caching:
1429
+ # Use stable content-based key instead of object id (which can be reused after GC)
1430
+ expr_sql = new_expr.sql(dialect=self._dialect)
1431
+ cache_key = f"prune:{hashlib.blake2b(expr_sql.encode(), digest_size=8).hexdigest()}"
1432
+ else:
1433
+ cache_key = None
1434
+ new_expr = apply_column_pruning(new_expr, dialect=self._dialect, cache_key=cache_key)
1435
+
1436
+ return self._create_modified_copy_with_expression(new_expr)
1437
+
1438
+ def explain(self, analyze: bool = False, verbose: bool = False, format: "str | None" = None) -> "SQL":
1439
+ """Create an EXPLAIN statement for this SQL.
1440
+
1441
+ Wraps the current SQL statement in an EXPLAIN clause with
1442
+ dialect-aware syntax generation.
1443
+
1444
+ Args:
1445
+ analyze: Execute the statement and show actual runtime statistics
1446
+ verbose: Show additional information
1447
+ format: Output format (TEXT, JSON, XML, YAML, TREE, TRADITIONAL)
1448
+
1449
+ Returns:
1450
+ New SQL instance containing the EXPLAIN statement
1451
+
1452
+ Examples:
1453
+ Basic EXPLAIN:
1454
+ stmt = SQL("SELECT * FROM users")
1455
+ explain_stmt = stmt.explain()
1456
+
1457
+ With options:
1458
+ explain_stmt = stmt.explain(analyze=True, format="json")
1459
+ """
1460
+ from sqlspec.builder import Explain
1461
+
1462
+ fmt = None
1463
+ if format is not None:
1464
+ fmt = ExplainFormat(format.lower())
1465
+
1466
+ options = ExplainOptions(analyze=analyze, verbose=verbose, format=fmt)
1467
+
1468
+ explain_builder = Explain(self, dialect=self._dialect, options=options)
1469
+ return explain_builder.build()
1470
+
1471
+ def builder(self, dialect: "DialectType | None" = None) -> "QueryBuilder":
1472
+ """Create a query builder seeded from this SQL statement.
1473
+
1474
+ Args:
1475
+ dialect: Optional SQL dialect override for parsing and rendering.
1476
+
1477
+ Returns:
1478
+ QueryBuilder instance initialized with the parsed statement.
1479
+
1480
+ Raises:
1481
+ SQLBuilderError: If the statement cannot be parsed.
1482
+
1483
+ Notes:
1484
+ Statements outside the DML set return an ExpressionBuilder without
1485
+ DML-specific helper methods.
1486
+ """
1487
+ if self._is_many:
1488
+ msg = "QueryBuilder does not support execute_many SQL statements."
1489
+ raise sqlspec.exceptions.SQLBuilderError(msg)
1490
+
1491
+ from sqlspec.builder import Delete, ExpressionBuilder, Insert, Merge, Select, Update
1492
+
1493
+ builder_dialect = dialect or self._dialect
1494
+ converter = self._statement_config.parameter_converter or ParameterConverter(
1495
+ self._statement_config.parameter_validator
1496
+ )
1497
+ raw_params = self.parameters
1498
+ converted_sql, converted_params = converter.convert_placeholder_style(
1499
+ self._raw_sql, raw_params, ParameterStyle.NAMED_COLON, is_many=False
1500
+ )
1501
+
1502
+ if self._raw_expression is not None and converted_sql == self._raw_sql and (builder_dialect == self._dialect):
1503
+ expression = self._raw_expression.copy()
1504
+ else:
1505
+ try:
1506
+ expression = sqlglot.parse_one(converted_sql, dialect=builder_dialect)
1507
+ except ParseError as exc:
1508
+ msg = f"Failed to parse SQL for builder: {exc}"
1509
+ raise sqlspec.exceptions.SQLBuilderError(msg) from exc
1510
+
1511
+ base_expression = expression
1512
+ ctes: list[exp.CTE] | None = None
1513
+ if isinstance(expression, exp.With):
1514
+ if expression.this is None:
1515
+ msg = "WITH expression does not include a base statement."
1516
+ raise sqlspec.exceptions.SQLBuilderError(msg)
1517
+ base_expression = expression.this
1518
+ ctes = list(expression.expressions)
1519
+
1520
+ builder: QueryBuilder
1521
+ if isinstance(base_expression, (exp.Select, exp.Union, exp.Except, exp.Intersect, exp.Values)):
1522
+ builder = Select(dialect=builder_dialect)
1523
+ builder.set_expression(base_expression.copy())
1524
+ elif isinstance(base_expression, exp.Insert):
1525
+ builder = Insert(dialect=builder_dialect)
1526
+ builder.set_expression(base_expression.copy())
1527
+ elif isinstance(base_expression, exp.Update):
1528
+ builder = Update(dialect=builder_dialect)
1529
+ builder.set_expression(base_expression.copy())
1530
+ elif isinstance(base_expression, exp.Delete):
1531
+ builder = Delete(dialect=builder_dialect)
1532
+ builder.set_expression(base_expression.copy())
1533
+ elif isinstance(base_expression, exp.Merge):
1534
+ builder = Merge(dialect=builder_dialect)
1535
+ builder.set_expression(base_expression.copy())
1536
+ else:
1537
+ copied = base_expression.copy()
1538
+ if not isinstance(copied, exp.Expression):
1539
+ msg = f"Unsupported expression type for builder: {type(copied).__name__}"
1540
+ raise sqlspec.exceptions.SQLBuilderError(msg)
1541
+ builder = ExpressionBuilder(copied, dialect=builder_dialect)
1542
+
1543
+ if ctes:
1544
+ builder.load_ctes(ctes)
1545
+
1546
+ if isinstance(converted_params, Mapping):
1547
+ builder.load_parameters(converted_params)
1548
+ return builder
1549
+
1550
+ if (
1551
+ converted_params
1552
+ and isinstance(converted_params, Sequence)
1553
+ and not isinstance(converted_params, (str, bytes, bytearray))
1554
+ ):
1555
+ param_info = converter.validator.extract_parameters(converted_sql)
1556
+ param_map: dict[str, Any] = {}
1557
+ for index, param in enumerate(param_info):
1558
+ if index >= len(converted_params):
1559
+ break
1560
+ param_name = param.name or f"param_{param.ordinal}"
1561
+ param_map[param_name] = converted_params[index]
1562
+ builder.load_parameters(param_map)
1563
+
1564
+ return builder
1565
+
1566
+ def __hash__(self) -> int:
1567
+ """Hash value computation."""
1568
+ if self._hash is None:
1569
+ positional_tuple = tuple(self._positional_parameters)
1570
+ named_tuple = tuple(sorted(self._named_parameters.items())) if self._named_parameters else ()
1571
+ raw_sql = self._raw_sql
1572
+ is_many = self._is_many
1573
+ is_script = self._is_script
1574
+ self._hash = hash((raw_sql, positional_tuple, named_tuple, is_many, is_script))
1575
+ return self._hash
1576
+
1577
+ def __eq__(self, other: object) -> bool:
1578
+ """Equality comparison."""
1579
+ if not isinstance(other, SQL):
1580
+ return False
1581
+ return (
1582
+ self._raw_sql == other._raw_sql
1583
+ and self._positional_parameters == other._positional_parameters
1584
+ and self._named_parameters == other._named_parameters
1585
+ and self._is_many == other._is_many
1586
+ and self._is_script == other._is_script
1587
+ )
1588
+
1589
+ def __repr__(self) -> str:
1590
+ """String representation."""
1591
+ params_parts = []
1592
+ if self._positional_parameters:
1593
+ params_parts.append(f"params={self._positional_parameters}")
1594
+ if self._named_parameters:
1595
+ params_parts.append(f"named_params={self._named_parameters}")
1596
+ params_str = f", {', '.join(params_parts)}" if params_parts else ""
1597
+
1598
+ flags = []
1599
+ if self._is_many:
1600
+ flags.append("is_many")
1601
+ if self._is_script:
1602
+ flags.append("is_script")
1603
+ flags_str = f", {', '.join(flags)}" if flags else ""
1604
+
1605
+ return f"SQL({self._raw_sql!r}{params_str}{flags_str})"
1606
+
1607
+
1608
+ @mypyc_attr(allow_interpreted_subclasses=False)
1609
+ class StatementConfig:
1610
+ """Configuration for SQL statement processing.
1611
+
1612
+ Controls SQL parsing, validation, transformations, parameter handling,
1613
+ and other processing options for SQL statements.
1614
+ """
1615
+
1616
+ __slots__ = (*SQL_CONFIG_SLOTS, "_has_transformers", "_has_output_transformer", "_user_statement_transformers")
1617
+
1618
+ def __init__(
1619
+ self,
1620
+ parameter_config: "ParameterStyleConfig | None" = None,
1621
+ enable_parsing: bool = True,
1622
+ enable_validation: bool = True,
1623
+ enable_transformations: bool = True,
1624
+ enable_analysis: bool = False,
1625
+ enable_expression_simplification: bool = False,
1626
+ enable_column_pruning: bool = False,
1627
+ enable_parameter_type_wrapping: bool = True,
1628
+ enable_caching: bool = True,
1629
+ parameter_converter: "ParameterConverter | None" = None,
1630
+ parameter_validator: "ParameterValidator | None" = None,
1631
+ dialect: "DialectType | None" = None,
1632
+ execution_mode: "str | None" = None,
1633
+ execution_args: "dict[str, Any] | None" = None,
1634
+ output_transformer: "Callable[[str, Any], tuple[str, Any]] | None" = None,
1635
+ statement_transformers: "Sequence[Callable[[exp.Expr, Any], tuple[exp.Expr, Any]]] | None" = None,
1636
+ enable_sqlcommenter: bool = False,
1637
+ sqlcommenter_attributes: "dict[str, str | None] | None" = None,
1638
+ sqlcommenter_enable_traceparent: bool = False,
1639
+ sqlcommenter_enable_context: bool = False,
1640
+ ) -> None:
1641
+ """Initialize StatementConfig.
1642
+
1643
+ Args:
1644
+ parameter_config: Parameter style configuration
1645
+ enable_parsing: Enable SQL parsing
1646
+ enable_validation: Run SQL validators
1647
+ enable_transformations: Apply SQL transformers
1648
+ enable_analysis: Run SQL analyzers
1649
+ enable_expression_simplification: Apply expression simplification
1650
+ enable_column_pruning: Remove unused columns from subqueries during select_only
1651
+ enable_parameter_type_wrapping: Wrap parameters with type information
1652
+ enable_caching: Cache processed SQL statements
1653
+ parameter_converter: Handles parameter style conversions
1654
+ parameter_validator: Validates parameter usage and styles
1655
+ dialect: SQL dialect
1656
+ execution_mode: Special execution mode
1657
+ execution_args: Arguments for special execution modes
1658
+ output_transformer: Optional output transformation function
1659
+ statement_transformers: Optional AST transformers executed during compilation
1660
+ enable_sqlcommenter: Auto-append Google SQLCommenter attributes to SQL
1661
+ sqlcommenter_attributes: Static key-value pairs for SQLCommenter comments
1662
+ sqlcommenter_enable_traceparent: Auto-populate W3C traceparent from OpenTelemetry
1663
+ sqlcommenter_enable_context: Read request-scoped attrs from SQLCommenterContext
1664
+ """
1665
+ self.enable_parsing = enable_parsing
1666
+ self.enable_validation = enable_validation
1667
+ self.enable_transformations = enable_transformations
1668
+ self.enable_analysis = enable_analysis
1669
+ self.enable_expression_simplification = enable_expression_simplification
1670
+ self.enable_column_pruning = enable_column_pruning
1671
+ self.enable_parameter_type_wrapping = enable_parameter_type_wrapping
1672
+ self.enable_caching = enable_caching
1673
+ if parameter_converter is None:
1674
+ if parameter_validator is None:
1675
+ parameter_validator = ParameterValidator()
1676
+ self.parameter_converter = ParameterConverter(parameter_validator)
1677
+ else:
1678
+ self.parameter_converter = parameter_converter
1679
+
1680
+ if parameter_validator is None:
1681
+ self.parameter_validator = self.parameter_converter.validator
1682
+ else:
1683
+ self.parameter_validator = parameter_validator
1684
+ self.parameter_converter.validator = parameter_validator
1685
+ self.parameter_config = parameter_config or ParameterStyleConfig(
1686
+ default_parameter_style=ParameterStyle.QMARK, supported_parameter_styles={ParameterStyle.QMARK}
1687
+ )
1688
+
1689
+ self.dialect = dialect
1690
+ self.execution_mode = execution_mode
1691
+ self.execution_args = execution_args
1692
+ self.enable_sqlcommenter = enable_sqlcommenter
1693
+ self.sqlcommenter_attributes = sqlcommenter_attributes
1694
+ self.sqlcommenter_enable_traceparent = sqlcommenter_enable_traceparent
1695
+ self.sqlcommenter_enable_context = sqlcommenter_enable_context
1696
+ self.output_transformer = output_transformer
1697
+
1698
+ self._user_statement_transformers = tuple(statement_transformers) if statement_transformers else ()
1699
+ all_transformers: list[Callable[..., Any]] = list(self._user_statement_transformers)
1700
+ if enable_sqlcommenter:
1701
+ effective_attrs = dict(sqlcommenter_attributes) if sqlcommenter_attributes else {}
1702
+ if "db_driver" not in effective_attrs and dialect:
1703
+ effective_attrs["db_driver"] = resolve_db_system(str(dialect))
1704
+ self.sqlcommenter_attributes = effective_attrs or None
1705
+
1706
+ sc_transformer = create_sqlcommenter_statement_transformer(
1707
+ attributes=effective_attrs,
1708
+ enable_traceparent=sqlcommenter_enable_traceparent,
1709
+ enable_context=sqlcommenter_enable_context,
1710
+ )
1711
+ all_transformers.append(sc_transformer)
1712
+
1713
+ self.statement_transformers = tuple(all_transformers)
1714
+ self._fingerprint_cache: str | None = None
1715
+ self._hash_cache: int | None = None
1716
+ self._is_frozen = False
1717
+ self._has_transformers = bool(self.statement_transformers)
1718
+ self._has_output_transformer = self.output_transformer is not None
1719
+
1720
+ def freeze(self) -> None:
1721
+ """Mark the configuration as immutable to enable caching."""
1722
+ self._is_frozen = True
1723
+
1724
+ def replace(self, **kwargs: Any) -> "StatementConfig":
1725
+ """Immutable update pattern.
1726
+
1727
+ Args:
1728
+ **kwargs: Attributes to update
1729
+
1730
+ Returns:
1731
+ New StatementConfig instance with updated attributes
1732
+ """
1733
+ for key in kwargs:
1734
+ if key not in SQL_CONFIG_SLOTS:
1735
+ msg = f"{key!r} is not a field in {type(self).__name__}"
1736
+ raise TypeError(msg)
1737
+
1738
+ current_kwargs: dict[str, Any] = {
1739
+ "parameter_config": self.parameter_config,
1740
+ "enable_parsing": self.enable_parsing,
1741
+ "enable_validation": self.enable_validation,
1742
+ "enable_transformations": self.enable_transformations,
1743
+ "enable_analysis": self.enable_analysis,
1744
+ "enable_expression_simplification": self.enable_expression_simplification,
1745
+ "enable_column_pruning": self.enable_column_pruning,
1746
+ "enable_parameter_type_wrapping": self.enable_parameter_type_wrapping,
1747
+ "enable_caching": self.enable_caching,
1748
+ "parameter_converter": self.parameter_converter,
1749
+ "parameter_validator": self.parameter_validator,
1750
+ "dialect": self.dialect,
1751
+ "execution_mode": self.execution_mode,
1752
+ "execution_args": self.execution_args,
1753
+ "output_transformer": self.output_transformer,
1754
+ "statement_transformers": self._user_statement_transformers,
1755
+ "enable_sqlcommenter": self.enable_sqlcommenter,
1756
+ "sqlcommenter_attributes": self.sqlcommenter_attributes,
1757
+ "sqlcommenter_enable_traceparent": self.sqlcommenter_enable_traceparent,
1758
+ "sqlcommenter_enable_context": self.sqlcommenter_enable_context,
1759
+ }
1760
+ current_kwargs.update(kwargs)
1761
+ return type(self)(**current_kwargs)
1762
+
1763
+ def __hash__(self) -> int:
1764
+ """Hash based on configuration settings."""
1765
+ if self._hash_cache is None:
1766
+ self._hash_cache = hash((
1767
+ self.enable_parsing,
1768
+ self.enable_validation,
1769
+ self.enable_transformations,
1770
+ self.enable_analysis,
1771
+ self.enable_expression_simplification,
1772
+ self.enable_column_pruning,
1773
+ self.enable_parameter_type_wrapping,
1774
+ self.enable_caching,
1775
+ str(self.dialect),
1776
+ self.parameter_config.hash(),
1777
+ self.execution_mode,
1778
+ self.output_transformer,
1779
+ self.statement_transformers,
1780
+ ))
1781
+ return self._hash_cache
1782
+
1783
+ def __repr__(self) -> str:
1784
+ """String representation of the StatementConfig instance."""
1785
+ field_strs = [
1786
+ f"parameter_config={self.parameter_config!r}",
1787
+ f"enable_parsing={self.enable_parsing!r}",
1788
+ f"enable_validation={self.enable_validation!r}",
1789
+ f"enable_transformations={self.enable_transformations!r}",
1790
+ f"enable_analysis={self.enable_analysis!r}",
1791
+ f"enable_expression_simplification={self.enable_expression_simplification!r}",
1792
+ f"enable_column_pruning={self.enable_column_pruning!r}",
1793
+ f"enable_parameter_type_wrapping={self.enable_parameter_type_wrapping!r}",
1794
+ f"enable_caching={self.enable_caching!r}",
1795
+ f"parameter_converter={self.parameter_converter!r}",
1796
+ f"parameter_validator={self.parameter_validator!r}",
1797
+ f"dialect={self.dialect!r}",
1798
+ f"execution_mode={self.execution_mode!r}",
1799
+ f"execution_args={self.execution_args!r}",
1800
+ f"output_transformer={self.output_transformer!r}",
1801
+ f"statement_transformers={self.statement_transformers!r}",
1802
+ ]
1803
+ return f"{self.__class__.__name__}({', '.join(field_strs)})"
1804
+
1805
+ def __eq__(self, other: object) -> bool:
1806
+ """Equality comparison."""
1807
+ if not isinstance(other, type(self)):
1808
+ return False
1809
+
1810
+ if not self._compare_parameter_configs(self.parameter_config, other.parameter_config):
1811
+ return False
1812
+
1813
+ return (
1814
+ self.enable_parsing == other.enable_parsing
1815
+ and self.enable_validation == other.enable_validation
1816
+ and self.enable_transformations == other.enable_transformations
1817
+ and self.enable_analysis == other.enable_analysis
1818
+ and self.enable_expression_simplification == other.enable_expression_simplification
1819
+ and self.enable_column_pruning == other.enable_column_pruning
1820
+ and self.enable_parameter_type_wrapping == other.enable_parameter_type_wrapping
1821
+ and self.enable_caching == other.enable_caching
1822
+ and self.dialect == other.dialect
1823
+ and self.execution_mode == other.execution_mode
1824
+ and self.execution_args == other.execution_args
1825
+ and self.output_transformer == other.output_transformer
1826
+ and self.statement_transformers == other.statement_transformers
1827
+ )
1828
+
1829
+ def _compare_parameter_configs(self, config1: Any, config2: Any) -> bool:
1830
+ """Compare parameter configs."""
1831
+ return bool(
1832
+ config1.default_parameter_style == config2.default_parameter_style
1833
+ and config1.supported_parameter_styles == config2.supported_parameter_styles
1834
+ and config1.supported_execution_parameter_styles == config2.supported_execution_parameter_styles
1835
+ )
1836
+
1837
+
1838
+ _DEFAULT_CONFIG: "StatementConfig | None" = None
1839
+
1840
+
1841
+ def get_default_config() -> StatementConfig:
1842
+ """Get default statement configuration.
1843
+
1844
+ Returns:
1845
+ Cached StatementConfig singleton with default settings.
1846
+ """
1847
+ global _DEFAULT_CONFIG
1848
+ if _DEFAULT_CONFIG is None:
1849
+ _DEFAULT_CONFIG = StatementConfig()
1850
+ _DEFAULT_CONFIG.freeze()
1851
+ return _DEFAULT_CONFIG
1852
+
1853
+
1854
+ def get_default_parameter_config() -> ParameterStyleConfig:
1855
+ """Get default parameter configuration.
1856
+
1857
+ Returns:
1858
+ ParameterStyleConfig with QMARK style as default
1859
+ """
1860
+ return ParameterStyleConfig(
1861
+ default_parameter_style=ParameterStyle.QMARK, supported_parameter_styles={ParameterStyle.QMARK}
1862
+ )
1863
+
1864
+
1865
+ Statement: TypeAlias = str | exp.Expr | SQL