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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (531) hide show
  1. ac8f31065839703b4e70__mypyc.cpython-310-aarch64-linux-gnu.so +0 -0
  2. sqlspec/__init__.py +140 -0
  3. sqlspec/__main__.py +12 -0
  4. sqlspec/__metadata__.py +14 -0
  5. sqlspec/_serialization.py +315 -0
  6. sqlspec/_typing.py +700 -0
  7. sqlspec/adapters/__init__.py +0 -0
  8. sqlspec/adapters/adbc/__init__.py +5 -0
  9. sqlspec/adapters/adbc/_typing.py +82 -0
  10. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  11. sqlspec/adapters/adbc/adk/store.py +1273 -0
  12. sqlspec/adapters/adbc/config.py +295 -0
  13. sqlspec/adapters/adbc/core.cpython-310-aarch64-linux-gnu.so +0 -0
  14. sqlspec/adapters/adbc/core.py +735 -0
  15. sqlspec/adapters/adbc/data_dictionary.py +334 -0
  16. sqlspec/adapters/adbc/driver.py +529 -0
  17. sqlspec/adapters/adbc/events/__init__.py +5 -0
  18. sqlspec/adapters/adbc/events/store.py +285 -0
  19. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  20. sqlspec/adapters/adbc/litestar/store.py +502 -0
  21. sqlspec/adapters/adbc/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  22. sqlspec/adapters/adbc/type_converter.py +140 -0
  23. sqlspec/adapters/aiosqlite/__init__.py +25 -0
  24. sqlspec/adapters/aiosqlite/_typing.py +82 -0
  25. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  26. sqlspec/adapters/aiosqlite/adk/store.py +818 -0
  27. sqlspec/adapters/aiosqlite/config.py +334 -0
  28. sqlspec/adapters/aiosqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
  29. sqlspec/adapters/aiosqlite/core.py +315 -0
  30. sqlspec/adapters/aiosqlite/data_dictionary.py +208 -0
  31. sqlspec/adapters/aiosqlite/driver.py +313 -0
  32. sqlspec/adapters/aiosqlite/events/__init__.py +5 -0
  33. sqlspec/adapters/aiosqlite/events/store.py +20 -0
  34. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  35. sqlspec/adapters/aiosqlite/litestar/store.py +279 -0
  36. sqlspec/adapters/aiosqlite/pool.py +533 -0
  37. sqlspec/adapters/asyncmy/__init__.py +21 -0
  38. sqlspec/adapters/asyncmy/_typing.py +87 -0
  39. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  40. sqlspec/adapters/asyncmy/adk/store.py +703 -0
  41. sqlspec/adapters/asyncmy/config.py +302 -0
  42. sqlspec/adapters/asyncmy/core.cpython-310-aarch64-linux-gnu.so +0 -0
  43. sqlspec/adapters/asyncmy/core.py +360 -0
  44. sqlspec/adapters/asyncmy/data_dictionary.py +124 -0
  45. sqlspec/adapters/asyncmy/driver.py +383 -0
  46. sqlspec/adapters/asyncmy/events/__init__.py +5 -0
  47. sqlspec/adapters/asyncmy/events/store.py +104 -0
  48. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  49. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  50. sqlspec/adapters/asyncpg/__init__.py +19 -0
  51. sqlspec/adapters/asyncpg/_typing.py +88 -0
  52. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  53. sqlspec/adapters/asyncpg/adk/store.py +748 -0
  54. sqlspec/adapters/asyncpg/config.py +569 -0
  55. sqlspec/adapters/asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
  56. sqlspec/adapters/asyncpg/core.py +367 -0
  57. sqlspec/adapters/asyncpg/data_dictionary.py +162 -0
  58. sqlspec/adapters/asyncpg/driver.py +487 -0
  59. sqlspec/adapters/asyncpg/events/__init__.py +6 -0
  60. sqlspec/adapters/asyncpg/events/backend.py +286 -0
  61. sqlspec/adapters/asyncpg/events/store.py +40 -0
  62. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  63. sqlspec/adapters/asyncpg/litestar/store.py +251 -0
  64. sqlspec/adapters/bigquery/__init__.py +14 -0
  65. sqlspec/adapters/bigquery/_typing.py +86 -0
  66. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  67. sqlspec/adapters/bigquery/adk/store.py +827 -0
  68. sqlspec/adapters/bigquery/config.py +353 -0
  69. sqlspec/adapters/bigquery/core.cpython-310-aarch64-linux-gnu.so +0 -0
  70. sqlspec/adapters/bigquery/core.py +715 -0
  71. sqlspec/adapters/bigquery/data_dictionary.py +128 -0
  72. sqlspec/adapters/bigquery/driver.py +548 -0
  73. sqlspec/adapters/bigquery/events/__init__.py +5 -0
  74. sqlspec/adapters/bigquery/events/store.py +139 -0
  75. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  76. sqlspec/adapters/bigquery/litestar/store.py +325 -0
  77. sqlspec/adapters/bigquery/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  78. sqlspec/adapters/bigquery/type_converter.py +107 -0
  79. sqlspec/adapters/cockroach_asyncpg/__init__.py +24 -0
  80. sqlspec/adapters/cockroach_asyncpg/_typing.py +72 -0
  81. sqlspec/adapters/cockroach_asyncpg/adk/__init__.py +3 -0
  82. sqlspec/adapters/cockroach_asyncpg/adk/store.py +410 -0
  83. sqlspec/adapters/cockroach_asyncpg/config.py +238 -0
  84. sqlspec/adapters/cockroach_asyncpg/core.cpython-310-aarch64-linux-gnu.so +0 -0
  85. sqlspec/adapters/cockroach_asyncpg/core.py +55 -0
  86. sqlspec/adapters/cockroach_asyncpg/data_dictionary.py +107 -0
  87. sqlspec/adapters/cockroach_asyncpg/driver.py +144 -0
  88. sqlspec/adapters/cockroach_asyncpg/events/__init__.py +3 -0
  89. sqlspec/adapters/cockroach_asyncpg/events/store.py +20 -0
  90. sqlspec/adapters/cockroach_asyncpg/litestar/__init__.py +3 -0
  91. sqlspec/adapters/cockroach_asyncpg/litestar/store.py +142 -0
  92. sqlspec/adapters/cockroach_psycopg/__init__.py +38 -0
  93. sqlspec/adapters/cockroach_psycopg/_typing.py +129 -0
  94. sqlspec/adapters/cockroach_psycopg/adk/__init__.py +13 -0
  95. sqlspec/adapters/cockroach_psycopg/adk/store.py +868 -0
  96. sqlspec/adapters/cockroach_psycopg/config.py +484 -0
  97. sqlspec/adapters/cockroach_psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
  98. sqlspec/adapters/cockroach_psycopg/core.py +63 -0
  99. sqlspec/adapters/cockroach_psycopg/data_dictionary.py +215 -0
  100. sqlspec/adapters/cockroach_psycopg/driver.py +284 -0
  101. sqlspec/adapters/cockroach_psycopg/events/__init__.py +6 -0
  102. sqlspec/adapters/cockroach_psycopg/events/store.py +34 -0
  103. sqlspec/adapters/cockroach_psycopg/litestar/__init__.py +3 -0
  104. sqlspec/adapters/cockroach_psycopg/litestar/store.py +325 -0
  105. sqlspec/adapters/duckdb/__init__.py +25 -0
  106. sqlspec/adapters/duckdb/_typing.py +81 -0
  107. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  108. sqlspec/adapters/duckdb/adk/store.py +850 -0
  109. sqlspec/adapters/duckdb/config.py +463 -0
  110. sqlspec/adapters/duckdb/core.cpython-310-aarch64-linux-gnu.so +0 -0
  111. sqlspec/adapters/duckdb/core.py +257 -0
  112. sqlspec/adapters/duckdb/data_dictionary.py +140 -0
  113. sqlspec/adapters/duckdb/driver.py +430 -0
  114. sqlspec/adapters/duckdb/events/__init__.py +5 -0
  115. sqlspec/adapters/duckdb/events/store.py +57 -0
  116. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  117. sqlspec/adapters/duckdb/litestar/store.py +330 -0
  118. sqlspec/adapters/duckdb/pool.py +293 -0
  119. sqlspec/adapters/duckdb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  120. sqlspec/adapters/duckdb/type_converter.py +118 -0
  121. sqlspec/adapters/mock/__init__.py +72 -0
  122. sqlspec/adapters/mock/_typing.py +147 -0
  123. sqlspec/adapters/mock/config.py +483 -0
  124. sqlspec/adapters/mock/core.py +319 -0
  125. sqlspec/adapters/mock/data_dictionary.py +366 -0
  126. sqlspec/adapters/mock/driver.py +721 -0
  127. sqlspec/adapters/mysqlconnector/__init__.py +36 -0
  128. sqlspec/adapters/mysqlconnector/_typing.py +141 -0
  129. sqlspec/adapters/mysqlconnector/adk/__init__.py +15 -0
  130. sqlspec/adapters/mysqlconnector/adk/store.py +1060 -0
  131. sqlspec/adapters/mysqlconnector/config.py +394 -0
  132. sqlspec/adapters/mysqlconnector/core.cpython-310-aarch64-linux-gnu.so +0 -0
  133. sqlspec/adapters/mysqlconnector/core.py +303 -0
  134. sqlspec/adapters/mysqlconnector/data_dictionary.py +235 -0
  135. sqlspec/adapters/mysqlconnector/driver.py +483 -0
  136. sqlspec/adapters/mysqlconnector/events/__init__.py +8 -0
  137. sqlspec/adapters/mysqlconnector/events/store.py +98 -0
  138. sqlspec/adapters/mysqlconnector/litestar/__init__.py +5 -0
  139. sqlspec/adapters/mysqlconnector/litestar/store.py +426 -0
  140. sqlspec/adapters/oracledb/__init__.py +60 -0
  141. sqlspec/adapters/oracledb/_numpy_handlers.py +141 -0
  142. sqlspec/adapters/oracledb/_typing.py +182 -0
  143. sqlspec/adapters/oracledb/_uuid_handlers.py +166 -0
  144. sqlspec/adapters/oracledb/adk/__init__.py +10 -0
  145. sqlspec/adapters/oracledb/adk/store.py +2369 -0
  146. sqlspec/adapters/oracledb/config.py +550 -0
  147. sqlspec/adapters/oracledb/core.cpython-310-aarch64-linux-gnu.so +0 -0
  148. sqlspec/adapters/oracledb/core.py +543 -0
  149. sqlspec/adapters/oracledb/data_dictionary.py +536 -0
  150. sqlspec/adapters/oracledb/driver.py +1229 -0
  151. sqlspec/adapters/oracledb/events/__init__.py +16 -0
  152. sqlspec/adapters/oracledb/events/backend.py +347 -0
  153. sqlspec/adapters/oracledb/events/store.py +420 -0
  154. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  155. sqlspec/adapters/oracledb/litestar/store.py +781 -0
  156. sqlspec/adapters/oracledb/migrations.py +535 -0
  157. sqlspec/adapters/oracledb/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  158. sqlspec/adapters/oracledb/type_converter.py +211 -0
  159. sqlspec/adapters/psqlpy/__init__.py +17 -0
  160. sqlspec/adapters/psqlpy/_typing.py +79 -0
  161. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  162. sqlspec/adapters/psqlpy/adk/store.py +766 -0
  163. sqlspec/adapters/psqlpy/config.py +304 -0
  164. sqlspec/adapters/psqlpy/core.cpython-310-aarch64-linux-gnu.so +0 -0
  165. sqlspec/adapters/psqlpy/core.py +480 -0
  166. sqlspec/adapters/psqlpy/data_dictionary.py +126 -0
  167. sqlspec/adapters/psqlpy/driver.py +438 -0
  168. sqlspec/adapters/psqlpy/events/__init__.py +6 -0
  169. sqlspec/adapters/psqlpy/events/backend.py +310 -0
  170. sqlspec/adapters/psqlpy/events/store.py +20 -0
  171. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  172. sqlspec/adapters/psqlpy/litestar/store.py +270 -0
  173. sqlspec/adapters/psqlpy/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  174. sqlspec/adapters/psqlpy/type_converter.py +113 -0
  175. sqlspec/adapters/psycopg/__init__.py +32 -0
  176. sqlspec/adapters/psycopg/_typing.py +164 -0
  177. sqlspec/adapters/psycopg/adk/__init__.py +10 -0
  178. sqlspec/adapters/psycopg/adk/store.py +1387 -0
  179. sqlspec/adapters/psycopg/config.py +576 -0
  180. sqlspec/adapters/psycopg/core.cpython-310-aarch64-linux-gnu.so +0 -0
  181. sqlspec/adapters/psycopg/core.py +450 -0
  182. sqlspec/adapters/psycopg/data_dictionary.py +289 -0
  183. sqlspec/adapters/psycopg/driver.py +975 -0
  184. sqlspec/adapters/psycopg/events/__init__.py +20 -0
  185. sqlspec/adapters/psycopg/events/backend.py +458 -0
  186. sqlspec/adapters/psycopg/events/store.py +42 -0
  187. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  188. sqlspec/adapters/psycopg/litestar/store.py +552 -0
  189. sqlspec/adapters/psycopg/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  190. sqlspec/adapters/psycopg/type_converter.py +93 -0
  191. sqlspec/adapters/pymysql/__init__.py +21 -0
  192. sqlspec/adapters/pymysql/_typing.py +71 -0
  193. sqlspec/adapters/pymysql/adk/__init__.py +5 -0
  194. sqlspec/adapters/pymysql/adk/store.py +540 -0
  195. sqlspec/adapters/pymysql/config.py +195 -0
  196. sqlspec/adapters/pymysql/core.cpython-310-aarch64-linux-gnu.so +0 -0
  197. sqlspec/adapters/pymysql/core.py +299 -0
  198. sqlspec/adapters/pymysql/data_dictionary.py +122 -0
  199. sqlspec/adapters/pymysql/driver.py +259 -0
  200. sqlspec/adapters/pymysql/events/__init__.py +5 -0
  201. sqlspec/adapters/pymysql/events/store.py +50 -0
  202. sqlspec/adapters/pymysql/litestar/__init__.py +5 -0
  203. sqlspec/adapters/pymysql/litestar/store.py +232 -0
  204. sqlspec/adapters/pymysql/pool.py +137 -0
  205. sqlspec/adapters/spanner/__init__.py +40 -0
  206. sqlspec/adapters/spanner/_typing.py +86 -0
  207. sqlspec/adapters/spanner/adk/__init__.py +5 -0
  208. sqlspec/adapters/spanner/adk/store.py +732 -0
  209. sqlspec/adapters/spanner/config.py +352 -0
  210. sqlspec/adapters/spanner/core.cpython-310-aarch64-linux-gnu.so +0 -0
  211. sqlspec/adapters/spanner/core.py +188 -0
  212. sqlspec/adapters/spanner/data_dictionary.py +120 -0
  213. sqlspec/adapters/spanner/dialect/__init__.py +6 -0
  214. sqlspec/adapters/spanner/dialect/_spangres.py +57 -0
  215. sqlspec/adapters/spanner/dialect/_spanner.py +130 -0
  216. sqlspec/adapters/spanner/driver.py +373 -0
  217. sqlspec/adapters/spanner/events/__init__.py +5 -0
  218. sqlspec/adapters/spanner/events/store.py +187 -0
  219. sqlspec/adapters/spanner/litestar/__init__.py +5 -0
  220. sqlspec/adapters/spanner/litestar/store.py +291 -0
  221. sqlspec/adapters/spanner/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  222. sqlspec/adapters/spanner/type_converter.py +331 -0
  223. sqlspec/adapters/sqlite/__init__.py +19 -0
  224. sqlspec/adapters/sqlite/_typing.py +80 -0
  225. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  226. sqlspec/adapters/sqlite/adk/store.py +958 -0
  227. sqlspec/adapters/sqlite/config.py +280 -0
  228. sqlspec/adapters/sqlite/core.cpython-310-aarch64-linux-gnu.so +0 -0
  229. sqlspec/adapters/sqlite/core.py +312 -0
  230. sqlspec/adapters/sqlite/data_dictionary.py +202 -0
  231. sqlspec/adapters/sqlite/driver.py +359 -0
  232. sqlspec/adapters/sqlite/events/__init__.py +5 -0
  233. sqlspec/adapters/sqlite/events/store.py +20 -0
  234. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  235. sqlspec/adapters/sqlite/litestar/store.py +316 -0
  236. sqlspec/adapters/sqlite/pool.py +198 -0
  237. sqlspec/adapters/sqlite/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  238. sqlspec/adapters/sqlite/type_converter.py +114 -0
  239. sqlspec/base.py +747 -0
  240. sqlspec/builder/__init__.py +179 -0
  241. sqlspec/builder/_base.cpython-310-aarch64-linux-gnu.so +0 -0
  242. sqlspec/builder/_base.py +1022 -0
  243. sqlspec/builder/_column.cpython-310-aarch64-linux-gnu.so +0 -0
  244. sqlspec/builder/_column.py +521 -0
  245. sqlspec/builder/_ddl.cpython-310-aarch64-linux-gnu.so +0 -0
  246. sqlspec/builder/_ddl.py +1642 -0
  247. sqlspec/builder/_delete.cpython-310-aarch64-linux-gnu.so +0 -0
  248. sqlspec/builder/_delete.py +95 -0
  249. sqlspec/builder/_dml.cpython-310-aarch64-linux-gnu.so +0 -0
  250. sqlspec/builder/_dml.py +365 -0
  251. sqlspec/builder/_explain.cpython-310-aarch64-linux-gnu.so +0 -0
  252. sqlspec/builder/_explain.py +579 -0
  253. sqlspec/builder/_expression_wrappers.cpython-310-aarch64-linux-gnu.so +0 -0
  254. sqlspec/builder/_expression_wrappers.py +46 -0
  255. sqlspec/builder/_factory.cpython-310-aarch64-linux-gnu.so +0 -0
  256. sqlspec/builder/_factory.py +1697 -0
  257. sqlspec/builder/_insert.cpython-310-aarch64-linux-gnu.so +0 -0
  258. sqlspec/builder/_insert.py +328 -0
  259. sqlspec/builder/_join.cpython-310-aarch64-linux-gnu.so +0 -0
  260. sqlspec/builder/_join.py +499 -0
  261. sqlspec/builder/_merge.cpython-310-aarch64-linux-gnu.so +0 -0
  262. sqlspec/builder/_merge.py +821 -0
  263. sqlspec/builder/_parsing_utils.cpython-310-aarch64-linux-gnu.so +0 -0
  264. sqlspec/builder/_parsing_utils.py +297 -0
  265. sqlspec/builder/_select.cpython-310-aarch64-linux-gnu.so +0 -0
  266. sqlspec/builder/_select.py +1660 -0
  267. sqlspec/builder/_temporal.cpython-310-aarch64-linux-gnu.so +0 -0
  268. sqlspec/builder/_temporal.py +139 -0
  269. sqlspec/builder/_update.cpython-310-aarch64-linux-gnu.so +0 -0
  270. sqlspec/builder/_update.py +173 -0
  271. sqlspec/builder/_vector_expressions.py +267 -0
  272. sqlspec/cli.py +911 -0
  273. sqlspec/config.py +1755 -0
  274. sqlspec/core/__init__.py +374 -0
  275. sqlspec/core/_correlation.cpython-310-aarch64-linux-gnu.so +0 -0
  276. sqlspec/core/_correlation.py +176 -0
  277. sqlspec/core/cache.cpython-310-aarch64-linux-gnu.so +0 -0
  278. sqlspec/core/cache.py +1069 -0
  279. sqlspec/core/compiler.cpython-310-aarch64-linux-gnu.so +0 -0
  280. sqlspec/core/compiler.py +954 -0
  281. sqlspec/core/explain.cpython-310-aarch64-linux-gnu.so +0 -0
  282. sqlspec/core/explain.py +275 -0
  283. sqlspec/core/filters.cpython-310-aarch64-linux-gnu.so +0 -0
  284. sqlspec/core/filters.py +952 -0
  285. sqlspec/core/hashing.cpython-310-aarch64-linux-gnu.so +0 -0
  286. sqlspec/core/hashing.py +262 -0
  287. sqlspec/core/metrics.cpython-310-aarch64-linux-gnu.so +0 -0
  288. sqlspec/core/metrics.py +83 -0
  289. sqlspec/core/parameters/__init__.py +71 -0
  290. sqlspec/core/parameters/_alignment.cpython-310-aarch64-linux-gnu.so +0 -0
  291. sqlspec/core/parameters/_alignment.py +270 -0
  292. sqlspec/core/parameters/_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  293. sqlspec/core/parameters/_converter.py +543 -0
  294. sqlspec/core/parameters/_processor.cpython-310-aarch64-linux-gnu.so +0 -0
  295. sqlspec/core/parameters/_processor.py +505 -0
  296. sqlspec/core/parameters/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
  297. sqlspec/core/parameters/_registry.py +206 -0
  298. sqlspec/core/parameters/_transformers.cpython-310-aarch64-linux-gnu.so +0 -0
  299. sqlspec/core/parameters/_transformers.py +292 -0
  300. sqlspec/core/parameters/_types.cpython-310-aarch64-linux-gnu.so +0 -0
  301. sqlspec/core/parameters/_types.py +499 -0
  302. sqlspec/core/parameters/_validator.cpython-310-aarch64-linux-gnu.so +0 -0
  303. sqlspec/core/parameters/_validator.py +180 -0
  304. sqlspec/core/pipeline.cpython-310-aarch64-linux-gnu.so +0 -0
  305. sqlspec/core/pipeline.py +319 -0
  306. sqlspec/core/query_modifiers.cpython-310-aarch64-linux-gnu.so +0 -0
  307. sqlspec/core/query_modifiers.py +437 -0
  308. sqlspec/core/result/__init__.py +23 -0
  309. sqlspec/core/result/_base.cpython-310-aarch64-linux-gnu.so +0 -0
  310. sqlspec/core/result/_base.py +1121 -0
  311. sqlspec/core/result/_io.cpython-310-aarch64-linux-gnu.so +0 -0
  312. sqlspec/core/result/_io.py +28 -0
  313. sqlspec/core/splitter.cpython-310-aarch64-linux-gnu.so +0 -0
  314. sqlspec/core/splitter.py +966 -0
  315. sqlspec/core/stack.cpython-310-aarch64-linux-gnu.so +0 -0
  316. sqlspec/core/stack.py +163 -0
  317. sqlspec/core/statement.cpython-310-aarch64-linux-gnu.so +0 -0
  318. sqlspec/core/statement.py +1503 -0
  319. sqlspec/core/type_converter.cpython-310-aarch64-linux-gnu.so +0 -0
  320. sqlspec/core/type_converter.py +339 -0
  321. sqlspec/data_dictionary/__init__.py +22 -0
  322. sqlspec/data_dictionary/_loader.py +123 -0
  323. sqlspec/data_dictionary/_registry.cpython-310-aarch64-linux-gnu.so +0 -0
  324. sqlspec/data_dictionary/_registry.py +74 -0
  325. sqlspec/data_dictionary/_types.cpython-310-aarch64-linux-gnu.so +0 -0
  326. sqlspec/data_dictionary/_types.py +121 -0
  327. sqlspec/data_dictionary/dialects/__init__.py +21 -0
  328. sqlspec/data_dictionary/dialects/bigquery.cpython-310-aarch64-linux-gnu.so +0 -0
  329. sqlspec/data_dictionary/dialects/bigquery.py +49 -0
  330. sqlspec/data_dictionary/dialects/cockroachdb.cpython-310-aarch64-linux-gnu.so +0 -0
  331. sqlspec/data_dictionary/dialects/cockroachdb.py +43 -0
  332. sqlspec/data_dictionary/dialects/duckdb.cpython-310-aarch64-linux-gnu.so +0 -0
  333. sqlspec/data_dictionary/dialects/duckdb.py +47 -0
  334. sqlspec/data_dictionary/dialects/mysql.cpython-310-aarch64-linux-gnu.so +0 -0
  335. sqlspec/data_dictionary/dialects/mysql.py +42 -0
  336. sqlspec/data_dictionary/dialects/oracle.cpython-310-aarch64-linux-gnu.so +0 -0
  337. sqlspec/data_dictionary/dialects/oracle.py +34 -0
  338. sqlspec/data_dictionary/dialects/postgres.cpython-310-aarch64-linux-gnu.so +0 -0
  339. sqlspec/data_dictionary/dialects/postgres.py +46 -0
  340. sqlspec/data_dictionary/dialects/spanner.cpython-310-aarch64-linux-gnu.so +0 -0
  341. sqlspec/data_dictionary/dialects/spanner.py +37 -0
  342. sqlspec/data_dictionary/dialects/sqlite.cpython-310-aarch64-linux-gnu.so +0 -0
  343. sqlspec/data_dictionary/dialects/sqlite.py +42 -0
  344. sqlspec/data_dictionary/sql/.gitkeep +0 -0
  345. sqlspec/data_dictionary/sql/bigquery/columns.sql +23 -0
  346. sqlspec/data_dictionary/sql/bigquery/foreign_keys.sql +34 -0
  347. sqlspec/data_dictionary/sql/bigquery/indexes.sql +19 -0
  348. sqlspec/data_dictionary/sql/bigquery/tables.sql +33 -0
  349. sqlspec/data_dictionary/sql/bigquery/version.sql +3 -0
  350. sqlspec/data_dictionary/sql/cockroachdb/columns.sql +34 -0
  351. sqlspec/data_dictionary/sql/cockroachdb/foreign_keys.sql +40 -0
  352. sqlspec/data_dictionary/sql/cockroachdb/indexes.sql +32 -0
  353. sqlspec/data_dictionary/sql/cockroachdb/tables.sql +44 -0
  354. sqlspec/data_dictionary/sql/cockroachdb/version.sql +3 -0
  355. sqlspec/data_dictionary/sql/duckdb/columns.sql +23 -0
  356. sqlspec/data_dictionary/sql/duckdb/foreign_keys.sql +36 -0
  357. sqlspec/data_dictionary/sql/duckdb/indexes.sql +19 -0
  358. sqlspec/data_dictionary/sql/duckdb/tables.sql +38 -0
  359. sqlspec/data_dictionary/sql/duckdb/version.sql +3 -0
  360. sqlspec/data_dictionary/sql/mysql/columns.sql +23 -0
  361. sqlspec/data_dictionary/sql/mysql/foreign_keys.sql +28 -0
  362. sqlspec/data_dictionary/sql/mysql/indexes.sql +26 -0
  363. sqlspec/data_dictionary/sql/mysql/tables.sql +33 -0
  364. sqlspec/data_dictionary/sql/mysql/version.sql +3 -0
  365. sqlspec/data_dictionary/sql/oracle/columns.sql +23 -0
  366. sqlspec/data_dictionary/sql/oracle/foreign_keys.sql +48 -0
  367. sqlspec/data_dictionary/sql/oracle/indexes.sql +44 -0
  368. sqlspec/data_dictionary/sql/oracle/tables.sql +25 -0
  369. sqlspec/data_dictionary/sql/oracle/version.sql +20 -0
  370. sqlspec/data_dictionary/sql/postgres/columns.sql +34 -0
  371. sqlspec/data_dictionary/sql/postgres/foreign_keys.sql +40 -0
  372. sqlspec/data_dictionary/sql/postgres/indexes.sql +56 -0
  373. sqlspec/data_dictionary/sql/postgres/tables.sql +44 -0
  374. sqlspec/data_dictionary/sql/postgres/version.sql +3 -0
  375. sqlspec/data_dictionary/sql/spanner/columns.sql +23 -0
  376. sqlspec/data_dictionary/sql/spanner/foreign_keys.sql +70 -0
  377. sqlspec/data_dictionary/sql/spanner/indexes.sql +30 -0
  378. sqlspec/data_dictionary/sql/spanner/tables.sql +9 -0
  379. sqlspec/data_dictionary/sql/spanner/version.sql +3 -0
  380. sqlspec/data_dictionary/sql/sqlite/columns.sql +23 -0
  381. sqlspec/data_dictionary/sql/sqlite/foreign_keys.sql +22 -0
  382. sqlspec/data_dictionary/sql/sqlite/indexes.sql +7 -0
  383. sqlspec/data_dictionary/sql/sqlite/tables.sql +28 -0
  384. sqlspec/data_dictionary/sql/sqlite/version.sql +3 -0
  385. sqlspec/driver/__init__.py +32 -0
  386. sqlspec/driver/_async.cpython-310-aarch64-linux-gnu.so +0 -0
  387. sqlspec/driver/_async.py +1737 -0
  388. sqlspec/driver/_common.cpython-310-aarch64-linux-gnu.so +0 -0
  389. sqlspec/driver/_common.py +1478 -0
  390. sqlspec/driver/_sql_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
  391. sqlspec/driver/_sql_helpers.py +148 -0
  392. sqlspec/driver/_storage_helpers.cpython-310-aarch64-linux-gnu.so +0 -0
  393. sqlspec/driver/_storage_helpers.py +144 -0
  394. sqlspec/driver/_sync.cpython-310-aarch64-linux-gnu.so +0 -0
  395. sqlspec/driver/_sync.py +1710 -0
  396. sqlspec/exceptions.py +338 -0
  397. sqlspec/extensions/__init__.py +0 -0
  398. sqlspec/extensions/adk/__init__.py +70 -0
  399. sqlspec/extensions/adk/_types.py +51 -0
  400. sqlspec/extensions/adk/converters.py +172 -0
  401. sqlspec/extensions/adk/memory/__init__.py +69 -0
  402. sqlspec/extensions/adk/memory/_types.py +30 -0
  403. sqlspec/extensions/adk/memory/converters.py +149 -0
  404. sqlspec/extensions/adk/memory/service.py +217 -0
  405. sqlspec/extensions/adk/memory/store.py +569 -0
  406. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +246 -0
  407. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  408. sqlspec/extensions/adk/service.py +225 -0
  409. sqlspec/extensions/adk/store.py +567 -0
  410. sqlspec/extensions/events/__init__.py +51 -0
  411. sqlspec/extensions/events/_channel.py +703 -0
  412. sqlspec/extensions/events/_hints.py +45 -0
  413. sqlspec/extensions/events/_models.py +23 -0
  414. sqlspec/extensions/events/_payload.py +69 -0
  415. sqlspec/extensions/events/_protocols.py +134 -0
  416. sqlspec/extensions/events/_queue.py +461 -0
  417. sqlspec/extensions/events/_store.py +209 -0
  418. sqlspec/extensions/events/migrations/0001_create_event_queue.py +59 -0
  419. sqlspec/extensions/events/migrations/__init__.py +3 -0
  420. sqlspec/extensions/fastapi/__init__.py +19 -0
  421. sqlspec/extensions/fastapi/extension.py +351 -0
  422. sqlspec/extensions/fastapi/providers.py +607 -0
  423. sqlspec/extensions/flask/__init__.py +37 -0
  424. sqlspec/extensions/flask/_state.py +76 -0
  425. sqlspec/extensions/flask/_utils.py +71 -0
  426. sqlspec/extensions/flask/extension.py +519 -0
  427. sqlspec/extensions/litestar/__init__.py +28 -0
  428. sqlspec/extensions/litestar/_utils.py +52 -0
  429. sqlspec/extensions/litestar/channels.py +165 -0
  430. sqlspec/extensions/litestar/cli.py +102 -0
  431. sqlspec/extensions/litestar/config.py +90 -0
  432. sqlspec/extensions/litestar/handlers.py +316 -0
  433. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  434. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  435. sqlspec/extensions/litestar/plugin.py +671 -0
  436. sqlspec/extensions/litestar/providers.py +526 -0
  437. sqlspec/extensions/litestar/store.py +296 -0
  438. sqlspec/extensions/otel/__init__.py +58 -0
  439. sqlspec/extensions/prometheus/__init__.py +113 -0
  440. sqlspec/extensions/starlette/__init__.py +19 -0
  441. sqlspec/extensions/starlette/_state.py +30 -0
  442. sqlspec/extensions/starlette/_utils.py +96 -0
  443. sqlspec/extensions/starlette/extension.py +346 -0
  444. sqlspec/extensions/starlette/middleware.py +235 -0
  445. sqlspec/loader.cpython-310-aarch64-linux-gnu.so +0 -0
  446. sqlspec/loader.py +702 -0
  447. sqlspec/migrations/__init__.py +36 -0
  448. sqlspec/migrations/base.py +731 -0
  449. sqlspec/migrations/commands.py +1232 -0
  450. sqlspec/migrations/context.py +157 -0
  451. sqlspec/migrations/fix.py +204 -0
  452. sqlspec/migrations/loaders.py +443 -0
  453. sqlspec/migrations/runner.py +1172 -0
  454. sqlspec/migrations/templates.py +234 -0
  455. sqlspec/migrations/tracker.py +611 -0
  456. sqlspec/migrations/utils.py +256 -0
  457. sqlspec/migrations/validation.py +207 -0
  458. sqlspec/migrations/version.py +446 -0
  459. sqlspec/observability/__init__.py +55 -0
  460. sqlspec/observability/_common.cpython-310-aarch64-linux-gnu.so +0 -0
  461. sqlspec/observability/_common.py +77 -0
  462. sqlspec/observability/_config.cpython-310-aarch64-linux-gnu.so +0 -0
  463. sqlspec/observability/_config.py +348 -0
  464. sqlspec/observability/_diagnostics.cpython-310-aarch64-linux-gnu.so +0 -0
  465. sqlspec/observability/_diagnostics.py +74 -0
  466. sqlspec/observability/_dispatcher.cpython-310-aarch64-linux-gnu.so +0 -0
  467. sqlspec/observability/_dispatcher.py +152 -0
  468. sqlspec/observability/_formatters/__init__.py +13 -0
  469. sqlspec/observability/_formatters/_aws.cpython-310-aarch64-linux-gnu.so +0 -0
  470. sqlspec/observability/_formatters/_aws.py +102 -0
  471. sqlspec/observability/_formatters/_azure.cpython-310-aarch64-linux-gnu.so +0 -0
  472. sqlspec/observability/_formatters/_azure.py +96 -0
  473. sqlspec/observability/_formatters/_base.cpython-310-aarch64-linux-gnu.so +0 -0
  474. sqlspec/observability/_formatters/_base.py +57 -0
  475. sqlspec/observability/_formatters/_gcp.cpython-310-aarch64-linux-gnu.so +0 -0
  476. sqlspec/observability/_formatters/_gcp.py +131 -0
  477. sqlspec/observability/_formatting.py +58 -0
  478. sqlspec/observability/_observer.cpython-310-aarch64-linux-gnu.so +0 -0
  479. sqlspec/observability/_observer.py +357 -0
  480. sqlspec/observability/_runtime.cpython-310-aarch64-linux-gnu.so +0 -0
  481. sqlspec/observability/_runtime.py +420 -0
  482. sqlspec/observability/_sampling.cpython-310-aarch64-linux-gnu.so +0 -0
  483. sqlspec/observability/_sampling.py +188 -0
  484. sqlspec/observability/_spans.cpython-310-aarch64-linux-gnu.so +0 -0
  485. sqlspec/observability/_spans.py +161 -0
  486. sqlspec/protocols.py +916 -0
  487. sqlspec/py.typed +0 -0
  488. sqlspec/storage/__init__.py +48 -0
  489. sqlspec/storage/_utils.py +104 -0
  490. sqlspec/storage/backends/__init__.py +1 -0
  491. sqlspec/storage/backends/base.py +253 -0
  492. sqlspec/storage/backends/fsspec.py +529 -0
  493. sqlspec/storage/backends/local.py +441 -0
  494. sqlspec/storage/backends/obstore.py +916 -0
  495. sqlspec/storage/errors.py +104 -0
  496. sqlspec/storage/pipeline.py +582 -0
  497. sqlspec/storage/registry.py +301 -0
  498. sqlspec/typing.py +395 -0
  499. sqlspec/utils/__init__.py +7 -0
  500. sqlspec/utils/arrow_helpers.py +318 -0
  501. sqlspec/utils/config_tools.py +332 -0
  502. sqlspec/utils/correlation.cpython-310-aarch64-linux-gnu.so +0 -0
  503. sqlspec/utils/correlation.py +134 -0
  504. sqlspec/utils/deprecation.py +190 -0
  505. sqlspec/utils/fixtures.cpython-310-aarch64-linux-gnu.so +0 -0
  506. sqlspec/utils/fixtures.py +258 -0
  507. sqlspec/utils/logging.py +222 -0
  508. sqlspec/utils/module_loader.py +306 -0
  509. sqlspec/utils/portal.cpython-310-aarch64-linux-gnu.so +0 -0
  510. sqlspec/utils/portal.py +375 -0
  511. sqlspec/utils/schema.cpython-310-aarch64-linux-gnu.so +0 -0
  512. sqlspec/utils/schema.py +485 -0
  513. sqlspec/utils/serializers.cpython-310-aarch64-linux-gnu.so +0 -0
  514. sqlspec/utils/serializers.py +408 -0
  515. sqlspec/utils/singleton.cpython-310-aarch64-linux-gnu.so +0 -0
  516. sqlspec/utils/singleton.py +41 -0
  517. sqlspec/utils/sync_tools.cpython-310-aarch64-linux-gnu.so +0 -0
  518. sqlspec/utils/sync_tools.py +311 -0
  519. sqlspec/utils/text.cpython-310-aarch64-linux-gnu.so +0 -0
  520. sqlspec/utils/text.py +108 -0
  521. sqlspec/utils/type_converters.cpython-310-aarch64-linux-gnu.so +0 -0
  522. sqlspec/utils/type_converters.py +128 -0
  523. sqlspec/utils/type_guards.cpython-310-aarch64-linux-gnu.so +0 -0
  524. sqlspec/utils/type_guards.py +1360 -0
  525. sqlspec/utils/uuids.cpython-310-aarch64-linux-gnu.so +0 -0
  526. sqlspec/utils/uuids.py +225 -0
  527. sqlspec-0.36.0.dist-info/METADATA +205 -0
  528. sqlspec-0.36.0.dist-info/RECORD +531 -0
  529. sqlspec-0.36.0.dist-info/WHEEL +7 -0
  530. sqlspec-0.36.0.dist-info/entry_points.txt +2 -0
  531. sqlspec-0.36.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,330 @@
1
+ """DuckDB sync session store for Litestar integration."""
2
+
3
+ from datetime import datetime, timedelta, timezone
4
+ from typing import TYPE_CHECKING
5
+
6
+ from sqlspec.extensions.litestar.store import BaseSQLSpecStore
7
+ from sqlspec.utils.sync_tools import async_
8
+
9
+ if TYPE_CHECKING:
10
+ from sqlspec.adapters.duckdb.config import DuckDBConfig
11
+
12
+
13
+ __all__ = ("DuckdbStore",)
14
+
15
+
16
+ class DuckdbStore(BaseSQLSpecStore["DuckDBConfig"]):
17
+ """DuckDB session store using synchronous DuckDB driver.
18
+
19
+ Implements server-side session storage for Litestar using DuckDB
20
+ via the synchronous duckdb driver. Uses Litestar's sync_to_thread
21
+ utility to provide an async interface compatible with the Store protocol.
22
+
23
+ Provides efficient session management with:
24
+ - Sync operations wrapped for async compatibility
25
+ - INSERT OR REPLACE for UPSERT functionality
26
+ - Native TIMESTAMP type support
27
+ - Automatic expiration handling
28
+ - Efficient cleanup of expired sessions
29
+ - Columnar storage optimized for analytical queries
30
+
31
+ Note:
32
+ DuckDB is primarily designed for analytical (OLAP) workloads.
33
+ For high-concurrency OLTP session stores, consider PostgreSQL adapters.
34
+
35
+ Args:
36
+ config: DuckDBConfig instance.
37
+
38
+ Example:
39
+ from sqlspec.adapters.duckdb import DuckDBConfig
40
+ from sqlspec.adapters.duckdb.litestar.store import DuckdbStore
41
+
42
+ config = DuckDBConfig()
43
+ store = DuckdbStore(config)
44
+ await store.create_table()
45
+ """
46
+
47
+ __slots__ = ()
48
+
49
+ def __init__(self, config: "DuckDBConfig") -> None:
50
+ """Initialize DuckDB session store.
51
+
52
+ Args:
53
+ config: DuckDBConfig instance.
54
+
55
+ Notes:
56
+ Table name is read from config.extension_config["litestar"]["session_table"].
57
+ """
58
+ super().__init__(config)
59
+
60
+ def _get_create_table_sql(self) -> str:
61
+ """Get DuckDB CREATE TABLE SQL.
62
+
63
+ Returns:
64
+ SQL statement to create the sessions table with proper indexes.
65
+
66
+ Notes:
67
+ - Uses TIMESTAMP type for expires_at (DuckDB native datetime type)
68
+ - TIMESTAMP supports ISO 8601 format and direct comparisons
69
+ - Columnar storage makes this efficient for analytical queries
70
+ - DuckDB does not support partial indexes, so full index is created
71
+ """
72
+ return f"""
73
+ CREATE TABLE IF NOT EXISTS {self._table_name} (
74
+ session_id VARCHAR PRIMARY KEY,
75
+ data BLOB NOT NULL,
76
+ expires_at TIMESTAMP,
77
+ created_at TIMESTAMP DEFAULT NOW(),
78
+ updated_at TIMESTAMP DEFAULT NOW()
79
+ );
80
+ CREATE INDEX IF NOT EXISTS idx_{self._table_name}_expires_at
81
+ ON {self._table_name}(expires_at);
82
+ """
83
+
84
+ def _get_drop_table_sql(self) -> "list[str]":
85
+ """Get DuckDB DROP TABLE SQL statements.
86
+
87
+ Returns:
88
+ List of SQL statements to drop indexes and table.
89
+ """
90
+ return [f"DROP INDEX IF EXISTS idx_{self._table_name}_expires_at", f"DROP TABLE IF EXISTS {self._table_name}"]
91
+
92
+ def _datetime_to_timestamp(self, dt: "datetime | None") -> "str | None":
93
+ """Convert datetime to ISO 8601 string for DuckDB TIMESTAMP storage.
94
+
95
+ Args:
96
+ dt: Datetime to convert (must be UTC-aware).
97
+
98
+ Returns:
99
+ ISO 8601 formatted string, or None if dt is None.
100
+
101
+ Notes:
102
+ DuckDB's TIMESTAMP type accepts ISO 8601 format strings.
103
+ This enables efficient storage and comparison operations.
104
+ """
105
+ if dt is None:
106
+ return None
107
+ return dt.isoformat()
108
+
109
+ def _timestamp_to_datetime(self, ts: "str | datetime | None") -> "datetime | None":
110
+ """Convert TIMESTAMP string back to datetime.
111
+
112
+ Args:
113
+ ts: ISO 8601 timestamp string or datetime object.
114
+
115
+ Returns:
116
+ UTC-aware datetime, or None if ts is None.
117
+ """
118
+ if ts is None:
119
+ return None
120
+ if isinstance(ts, datetime):
121
+ if ts.tzinfo is None:
122
+ return ts.replace(tzinfo=timezone.utc)
123
+ return ts
124
+ dt = datetime.fromisoformat(ts)
125
+ if dt.tzinfo is None:
126
+ dt = dt.replace(tzinfo=timezone.utc)
127
+ return dt
128
+
129
+ def _create_table(self) -> None:
130
+ """Synchronous implementation of create_table."""
131
+ sql = self._get_create_table_sql()
132
+ with self._config.provide_session() as driver:
133
+ driver.execute_script(sql)
134
+ self._log_table_created()
135
+
136
+ async def create_table(self) -> None:
137
+ """Create the session table if it doesn't exist."""
138
+ await async_(self._create_table)()
139
+
140
+ def _get(self, key: str, renew_for: "int | timedelta | None" = None) -> "bytes | None":
141
+ """Synchronous implementation of get."""
142
+ sql = f"""
143
+ SELECT data, expires_at FROM {self._table_name}
144
+ WHERE session_id = ?
145
+ AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP)
146
+ """
147
+
148
+ with self._config.provide_connection() as conn:
149
+ cursor = conn.execute(sql, (key,))
150
+ row = cursor.fetchone()
151
+
152
+ if row is None:
153
+ return None
154
+
155
+ data, expires_at_str = row
156
+
157
+ if renew_for is not None and expires_at_str is not None:
158
+ new_expires_at = self._calculate_expires_at(renew_for)
159
+ new_expires_at_str = self._datetime_to_timestamp(new_expires_at)
160
+ if new_expires_at_str is not None:
161
+ update_sql = f"""
162
+ UPDATE {self._table_name}
163
+ SET expires_at = ?, updated_at = NOW()
164
+ WHERE session_id = ?
165
+ """
166
+ conn.execute(update_sql, (new_expires_at_str, key))
167
+ conn.commit()
168
+
169
+ return bytes(data)
170
+
171
+ async def get(self, key: str, renew_for: "int | timedelta | None" = None) -> "bytes | None":
172
+ """Get a session value by key.
173
+
174
+ Args:
175
+ key: Session ID to retrieve.
176
+ renew_for: If given, renew the expiry time for this duration.
177
+
178
+ Returns:
179
+ Session data as bytes if found and not expired, None otherwise.
180
+ """
181
+ return await async_(self._get)(key, renew_for)
182
+
183
+ def _set(self, key: str, value: "str | bytes", expires_in: "int | timedelta | None" = None) -> None:
184
+ """Synchronous implementation of set.
185
+
186
+ Notes:
187
+ Stores expires_at as TIMESTAMP (ISO 8601 string) for DuckDB native support.
188
+ Uses INSERT ON CONFLICT instead of INSERT OR REPLACE to ensure all columns
189
+ are properly updated. created_at uses DEFAULT on insert, updated_at gets
190
+ current timestamp on both insert and update.
191
+ """
192
+ data = self._value_to_bytes(value)
193
+ expires_at = self._calculate_expires_at(expires_in)
194
+ expires_at_str = self._datetime_to_timestamp(expires_at)
195
+
196
+ sql = f"""
197
+ INSERT INTO {self._table_name} (session_id, data, expires_at)
198
+ VALUES (?, ?, ?)
199
+ ON CONFLICT (session_id)
200
+ DO UPDATE SET
201
+ data = EXCLUDED.data,
202
+ expires_at = EXCLUDED.expires_at,
203
+ updated_at = NOW()
204
+ """
205
+
206
+ with self._config.provide_connection() as conn:
207
+ conn.execute(sql, (key, data, expires_at_str))
208
+ conn.commit()
209
+
210
+ async def set(self, key: str, value: "str | bytes", expires_in: "int | timedelta | None" = None) -> None:
211
+ """Store a session value.
212
+
213
+ Args:
214
+ key: Session ID.
215
+ value: Session data.
216
+ expires_in: Time until expiration.
217
+ """
218
+ await async_(self._set)(key, value, expires_in)
219
+
220
+ def _delete(self, key: str) -> None:
221
+ """Synchronous implementation of delete."""
222
+ sql = f"DELETE FROM {self._table_name} WHERE session_id = ?"
223
+
224
+ with self._config.provide_connection() as conn:
225
+ conn.execute(sql, (key,))
226
+ conn.commit()
227
+
228
+ async def delete(self, key: str) -> None:
229
+ """Delete a session by key.
230
+
231
+ Args:
232
+ key: Session ID to delete.
233
+ """
234
+ await async_(self._delete)(key)
235
+
236
+ def _delete_all(self) -> None:
237
+ """Synchronous implementation of delete_all."""
238
+ sql = f"DELETE FROM {self._table_name}"
239
+
240
+ with self._config.provide_connection() as conn:
241
+ conn.execute(sql)
242
+ conn.commit()
243
+ self._log_delete_all()
244
+
245
+ async def delete_all(self) -> None:
246
+ """Delete all sessions from the store."""
247
+ await async_(self._delete_all)()
248
+
249
+ def _exists(self, key: str) -> bool:
250
+ """Synchronous implementation of exists."""
251
+ sql = f"""
252
+ SELECT 1 FROM {self._table_name}
253
+ WHERE session_id = ?
254
+ AND (expires_at IS NULL OR expires_at > CURRENT_TIMESTAMP)
255
+ """
256
+
257
+ with self._config.provide_connection() as conn:
258
+ cursor = conn.execute(sql, (key,))
259
+ result = cursor.fetchone()
260
+ return result is not None
261
+
262
+ async def exists(self, key: str) -> bool:
263
+ """Check if a session key exists and is not expired.
264
+
265
+ Args:
266
+ key: Session ID to check.
267
+
268
+ Returns:
269
+ True if the session exists and is not expired.
270
+ """
271
+ return await async_(self._exists)(key)
272
+
273
+ def _expires_in(self, key: str) -> "int | None":
274
+ """Synchronous implementation of expires_in."""
275
+ sql = f"""
276
+ SELECT expires_at FROM {self._table_name}
277
+ WHERE session_id = ?
278
+ """
279
+
280
+ with self._config.provide_connection() as conn:
281
+ cursor = conn.execute(sql, (key,))
282
+ row = cursor.fetchone()
283
+
284
+ if row is None or row[0] is None:
285
+ return None
286
+
287
+ expires_at_str = row[0]
288
+ expires_at = self._timestamp_to_datetime(expires_at_str)
289
+
290
+ if expires_at is None:
291
+ return None
292
+
293
+ now = datetime.now(timezone.utc)
294
+
295
+ if expires_at <= now:
296
+ return 0
297
+
298
+ delta = expires_at - now
299
+ return int(delta.total_seconds())
300
+
301
+ async def expires_in(self, key: str) -> "int | None":
302
+ """Get the time in seconds until the session expires.
303
+
304
+ Args:
305
+ key: Session ID to check.
306
+
307
+ Returns:
308
+ Seconds until expiration, or None if no expiry or key doesn't exist.
309
+ """
310
+ return await async_(self._expires_in)(key)
311
+
312
+ def _delete_expired(self) -> int:
313
+ """Synchronous implementation of delete_expired."""
314
+ sql = f"DELETE FROM {self._table_name} WHERE expires_at <= CURRENT_TIMESTAMP"
315
+
316
+ with self._config.provide_connection() as conn:
317
+ cursor = conn.execute(sql)
318
+ count = cursor.fetchone()
319
+ row_count = count[0] if count else 0
320
+ if row_count > 0:
321
+ self._log_delete_expired(row_count)
322
+ return row_count
323
+
324
+ async def delete_expired(self) -> int:
325
+ """Delete all expired sessions.
326
+
327
+ Returns:
328
+ Number of sessions deleted.
329
+ """
330
+ return await async_(self._delete_expired)()
@@ -0,0 +1,293 @@
1
+ """DuckDB connection pool with thread-local connections."""
2
+
3
+ import threading
4
+ import time
5
+ from contextlib import contextmanager, suppress
6
+ from typing import TYPE_CHECKING, Any, Final, cast
7
+
8
+ import duckdb
9
+
10
+ from sqlspec.adapters.duckdb._typing import DuckDBConnection
11
+ from sqlspec.utils.logging import get_logger
12
+
13
+ if TYPE_CHECKING:
14
+ from collections.abc import Callable, Generator
15
+
16
+
17
+ logger = get_logger(__name__)
18
+
19
+ DEFAULT_MIN_POOL: Final[int] = 1
20
+ DEFAULT_MAX_POOL: Final[int] = 4
21
+ POOL_TIMEOUT: Final[float] = 30.0
22
+ POOL_RECYCLE: Final[int] = 86400
23
+ HEALTH_CHECK_INTERVAL: Final[float] = 30.0
24
+
25
+ __all__ = ("DuckDBConnectionPool",)
26
+
27
+
28
+ class DuckDBConnectionPool:
29
+ """Thread-local connection manager for DuckDB.
30
+
31
+ Uses thread-local storage to ensure each thread gets its own DuckDB connection,
32
+ preventing the thread-safety issues that cause segmentation faults when
33
+ multiple cursors share the same connection concurrently.
34
+
35
+ This design trades traditional pooling for thread safety, which is essential
36
+ for DuckDB since connections and cursors are not thread-safe.
37
+ """
38
+
39
+ __slots__ = (
40
+ "_connection_config",
41
+ "_connection_times",
42
+ "_created_connections",
43
+ "_extension_flags",
44
+ "_extensions",
45
+ "_health_check_interval",
46
+ "_lock",
47
+ "_on_connection_create",
48
+ "_recycle",
49
+ "_secrets",
50
+ "_thread_local",
51
+ )
52
+
53
+ def __init__(
54
+ self,
55
+ connection_config: "dict[str, Any]",
56
+ pool_recycle_seconds: int = POOL_RECYCLE,
57
+ health_check_interval: float = HEALTH_CHECK_INTERVAL,
58
+ extensions: "list[dict[str, Any]] | None" = None,
59
+ extension_flags: "dict[str, Any] | None" = None,
60
+ secrets: "list[dict[str, Any]] | None" = None,
61
+ on_connection_create: "Callable[[DuckDBConnection], None] | None" = None,
62
+ ) -> None:
63
+ """Initialize the thread-local connection manager.
64
+
65
+ Args:
66
+ connection_config: DuckDB connection configuration
67
+ pool_recycle_seconds: Connection recycle time in seconds
68
+ health_check_interval: Seconds of idle time before running health check
69
+ extensions: List of extensions to install/load
70
+ extension_flags: Connection-level SET statements applied after creation
71
+ secrets: List of secrets to create
72
+ on_connection_create: Callback executed when connection is created
73
+ """
74
+ self._connection_config = connection_config
75
+ self._recycle = pool_recycle_seconds
76
+ self._health_check_interval = health_check_interval
77
+ self._extensions = extensions or []
78
+ self._extension_flags = extension_flags or {}
79
+ self._secrets = secrets or []
80
+ self._on_connection_create = on_connection_create
81
+ self._thread_local = threading.local()
82
+ self._lock = threading.RLock()
83
+ self._created_connections = 0
84
+ self._connection_times: dict[int, float] = {}
85
+
86
+ def _create_connection(self) -> DuckDBConnection:
87
+ """Create a new DuckDB connection with extensions and secrets."""
88
+ connect_parameters = {}
89
+ config_dict = {}
90
+
91
+ for key, value in self._connection_config.items():
92
+ if key in {"database", "read_only"}:
93
+ connect_parameters[key] = value
94
+ else:
95
+ config_dict[key] = value
96
+
97
+ if config_dict:
98
+ connect_parameters["config"] = config_dict
99
+
100
+ connection = duckdb.connect(**connect_parameters)
101
+
102
+ self._apply_extension_flags(connection)
103
+
104
+ for ext_config in self._extensions:
105
+ ext_name = ext_config.get("name")
106
+ if not ext_name:
107
+ continue
108
+
109
+ install_kwargs = {}
110
+ if "version" in ext_config:
111
+ install_kwargs["version"] = ext_config["version"]
112
+ if "repository" in ext_config:
113
+ install_kwargs["repository"] = ext_config["repository"]
114
+ if ext_config.get("force_install", False):
115
+ install_kwargs["force_install"] = True
116
+
117
+ try:
118
+ if install_kwargs:
119
+ connection.install_extension(ext_name, **install_kwargs)
120
+ connection.load_extension(ext_name)
121
+ except Exception as e:
122
+ logger.debug("Failed to load DuckDB extension %s: %s", ext_name, e)
123
+
124
+ for secret_config in self._secrets:
125
+ secret_type = secret_config.get("secret_type")
126
+ secret_name = secret_config.get("name")
127
+ secret_value = secret_config.get("value")
128
+
129
+ if not (secret_type and secret_name and secret_value):
130
+ continue
131
+
132
+ value_pairs = []
133
+ for key, value in secret_value.items():
134
+ escaped_value = str(value).replace("'", "''")
135
+ value_pairs.append(f"'{key}' = '{escaped_value}'")
136
+ value_string = ", ".join(value_pairs)
137
+ scope_clause = ""
138
+ if "scope" in secret_config:
139
+ scope_clause = f" SCOPE '{secret_config['scope']}'"
140
+
141
+ sql = f"""
142
+ CREATE SECRET {secret_name} (
143
+ TYPE {secret_type},
144
+ {value_string}
145
+ ){scope_clause}
146
+ """
147
+ with suppress(Exception):
148
+ connection.execute(sql)
149
+
150
+ if self._on_connection_create:
151
+ with suppress(Exception):
152
+ self._on_connection_create(connection)
153
+
154
+ conn_id = id(connection)
155
+ with self._lock:
156
+ self._created_connections += 1
157
+ self._connection_times[conn_id] = time.time()
158
+
159
+ return connection
160
+
161
+ def _apply_extension_flags(self, connection: DuckDBConnection) -> None:
162
+ """Apply connection-level extension flags via SET statements."""
163
+
164
+ if not self._extension_flags:
165
+ return
166
+
167
+ for key, value in self._extension_flags.items():
168
+ if not key or not key.replace("_", "").isalnum():
169
+ continue
170
+
171
+ normalized = self._normalize_flag_value(value)
172
+ try:
173
+ connection.execute(f"SET {key} = {normalized}")
174
+ except Exception as exc: # pragma: no cover - best-effort guard
175
+ logger.debug("Failed to set DuckDB flag %s: %s", key, exc)
176
+
177
+ @staticmethod
178
+ def _normalize_flag_value(value: Any) -> str:
179
+ """Convert Python value to DuckDB SET literal."""
180
+
181
+ if isinstance(value, bool):
182
+ return "TRUE" if value else "FALSE"
183
+ if isinstance(value, (int, float)):
184
+ return str(value)
185
+ escaped = str(value).replace("'", "''")
186
+ return f"'{escaped}'"
187
+
188
+ def _get_thread_connection(self) -> DuckDBConnection:
189
+ """Get or create a connection for the current thread.
190
+
191
+ Each thread gets its own dedicated DuckDB connection to prevent
192
+ thread-safety issues with concurrent cursor operations.
193
+ """
194
+ thread_state = self._thread_local.__dict__
195
+ if "connection" not in thread_state:
196
+ self._thread_local.connection = self._create_connection()
197
+ self._thread_local.created_at = time.time()
198
+ self._thread_local.last_used = time.time()
199
+ return cast("DuckDBConnection", self._thread_local.connection)
200
+
201
+ if self._recycle > 0 and time.time() - self._thread_local.created_at > self._recycle:
202
+ with suppress(Exception):
203
+ self._thread_local.connection.close()
204
+ self._thread_local.connection = self._create_connection()
205
+ self._thread_local.created_at = time.time()
206
+ self._thread_local.last_used = time.time()
207
+ return cast("DuckDBConnection", self._thread_local.connection)
208
+
209
+ idle_time = time.time() - thread_state.get("last_used", 0)
210
+ if idle_time > self._health_check_interval and not self._is_connection_alive(self._thread_local.connection):
211
+ logger.debug("DuckDB connection failed health check after %.1fs idle, recreating", idle_time)
212
+ with suppress(Exception):
213
+ self._thread_local.connection.close()
214
+ self._thread_local.connection = self._create_connection()
215
+ self._thread_local.created_at = time.time()
216
+
217
+ self._thread_local.last_used = time.time()
218
+ return cast("DuckDBConnection", self._thread_local.connection)
219
+
220
+ def _close_thread_connection(self) -> None:
221
+ """Close the connection for the current thread."""
222
+ thread_state = self._thread_local.__dict__
223
+ if "connection" in thread_state:
224
+ with suppress(Exception):
225
+ self._thread_local.connection.close()
226
+ del self._thread_local.connection
227
+ if "created_at" in thread_state:
228
+ del self._thread_local.created_at
229
+ if "last_used" in thread_state:
230
+ del self._thread_local.last_used
231
+
232
+ def _is_connection_alive(self, connection: DuckDBConnection) -> bool:
233
+ """Check if a connection is still alive and usable.
234
+
235
+ Args:
236
+ connection: Connection to check
237
+
238
+ Returns:
239
+ True if connection is alive, False otherwise
240
+ """
241
+ try:
242
+ cursor = connection.cursor()
243
+ cursor.close()
244
+ except Exception:
245
+ return False
246
+ return True
247
+
248
+ @contextmanager
249
+ def get_connection(self) -> "Generator[DuckDBConnection, None, None]":
250
+ """Get a thread-local connection.
251
+
252
+ Each thread gets its own dedicated DuckDB connection to prevent
253
+ thread-safety issues with concurrent cursor operations.
254
+
255
+ Yields:
256
+ DuckDBConnection: A thread-local connection.
257
+ """
258
+ connection = self._get_thread_connection()
259
+ try:
260
+ yield connection
261
+ except Exception:
262
+ self._close_thread_connection()
263
+ raise
264
+
265
+ def close(self) -> None:
266
+ """Close the thread-local connection if it exists."""
267
+ self._close_thread_connection()
268
+
269
+ def size(self) -> int:
270
+ """Get current pool size (always 1 for thread-local)."""
271
+ return 1 if "connection" in self._thread_local.__dict__ else 0
272
+
273
+ def checked_out(self) -> int:
274
+ """Get number of checked out connections (always 0 for thread-local)."""
275
+ return 0
276
+
277
+ def acquire(self) -> DuckDBConnection:
278
+ """Acquire a thread-local connection.
279
+
280
+ Each thread gets its own dedicated DuckDB connection to prevent
281
+ thread-safety issues with concurrent cursor operations.
282
+
283
+ Returns:
284
+ DuckDBConnection: A thread-local connection
285
+ """
286
+ return self._get_thread_connection()
287
+
288
+ def release(self, connection: DuckDBConnection) -> None:
289
+ """Release a connection (no-op for thread-local connections).
290
+
291
+ Args:
292
+ connection: The connection to release (ignored)
293
+ """