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,1106 @@
1
+ """Psycopg ADK store for Google Agent Development Kit session/event storage."""
2
+
3
+ from typing import TYPE_CHECKING, Any
4
+
5
+ from psycopg import errors
6
+ from psycopg import sql as pg_sql
7
+ from psycopg.types.json import Jsonb
8
+
9
+ from sqlspec.extensions.adk import BaseAsyncADKStore, EventRecord, SessionRecord
10
+ from sqlspec.extensions.adk.memory.store import BaseAsyncADKMemoryStore
11
+ from sqlspec.utils.logging import get_logger
12
+ from sqlspec.utils.sync_tools import async_, run_
13
+
14
+ if TYPE_CHECKING:
15
+ from datetime import datetime
16
+
17
+ from sqlspec.adapters.psycopg.config import PsycopgAsyncConfig, PsycopgSyncConfig
18
+ from sqlspec.extensions.adk import MemoryRecord
19
+
20
+
21
+ __all__ = ("PsycopgAsyncADKMemoryStore", "PsycopgAsyncADKStore", "PsycopgSyncADKMemoryStore", "PsycopgSyncADKStore")
22
+
23
+ logger = get_logger("sqlspec.adapters.psycopg.adk.store")
24
+
25
+
26
+ def _build_insert_params(entry: "MemoryRecord") -> "tuple[object, ...]":
27
+ return (
28
+ entry["id"],
29
+ entry["session_id"],
30
+ entry["app_name"],
31
+ entry["user_id"],
32
+ entry["event_id"],
33
+ entry["author"],
34
+ entry["timestamp"],
35
+ Jsonb(entry["content_json"]),
36
+ entry["content_text"],
37
+ Jsonb(entry["metadata_json"]) if entry["metadata_json"] is not None else None,
38
+ entry["inserted_at"],
39
+ )
40
+
41
+
42
+ def _build_insert_params_with_owner(entry: "MemoryRecord", owner_id: "object | None") -> "tuple[object, ...]":
43
+ return (
44
+ entry["id"],
45
+ entry["session_id"],
46
+ entry["app_name"],
47
+ entry["user_id"],
48
+ entry["event_id"],
49
+ entry["author"],
50
+ owner_id,
51
+ entry["timestamp"],
52
+ Jsonb(entry["content_json"]),
53
+ entry["content_text"],
54
+ Jsonb(entry["metadata_json"]) if entry["metadata_json"] is not None else None,
55
+ entry["inserted_at"],
56
+ )
57
+
58
+
59
+ class PsycopgAsyncADKStore(BaseAsyncADKStore["PsycopgAsyncConfig"]):
60
+ """PostgreSQL ADK store using Psycopg3 async driver.
61
+
62
+ Implements session and event storage for Google Agent Development Kit
63
+ using PostgreSQL via psycopg3 with native async/await support.
64
+ Events are stored as a single JSONB blob (``event_json``) alongside
65
+ indexed scalar columns for efficient querying.
66
+
67
+ Provides:
68
+ - Session state management with JSONB storage
69
+ - Full-fidelity event storage via ``event_json`` JSONB column
70
+ - Atomic ``append_event_and_update_state`` for durable session mutations
71
+ - Microsecond-precision timestamps with TIMESTAMPTZ
72
+ - Foreign key constraints with cascade delete
73
+ - GIN indexes for JSONB queries
74
+ - HOT updates with FILLFACTOR 80
75
+
76
+ Args:
77
+ config: PsycopgAsyncConfig with extension_config["adk"] settings.
78
+ """
79
+
80
+ __slots__ = ()
81
+
82
+ def __init__(self, config: "PsycopgAsyncConfig") -> None:
83
+ super().__init__(config)
84
+
85
+ async def _get_create_sessions_table_sql(self) -> str:
86
+ owner_id_line = ""
87
+ if self._owner_id_column_ddl:
88
+ owner_id_line = f",\n {self._owner_id_column_ddl}"
89
+
90
+ return f"""
91
+ CREATE TABLE IF NOT EXISTS {self._session_table} (
92
+ id VARCHAR(128) PRIMARY KEY,
93
+ app_name VARCHAR(128) NOT NULL,
94
+ user_id VARCHAR(128) NOT NULL{owner_id_line},
95
+ state JSONB NOT NULL DEFAULT '{{}}'::jsonb,
96
+ create_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
97
+ update_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
98
+ ) WITH (fillfactor = 80);
99
+
100
+ CREATE INDEX IF NOT EXISTS idx_{self._session_table}_app_user
101
+ ON {self._session_table}(app_name, user_id);
102
+
103
+ CREATE INDEX IF NOT EXISTS idx_{self._session_table}_update_time
104
+ ON {self._session_table}(update_time DESC);
105
+
106
+ CREATE INDEX IF NOT EXISTS idx_{self._session_table}_state
107
+ ON {self._session_table} USING GIN (state)
108
+ WHERE state != '{{}}'::jsonb;
109
+ """
110
+
111
+ async def _get_create_events_table_sql(self) -> str:
112
+ return f"""
113
+ CREATE TABLE IF NOT EXISTS {self._events_table} (
114
+ session_id VARCHAR(128) NOT NULL,
115
+ invocation_id VARCHAR(256) NOT NULL,
116
+ author VARCHAR(256) NOT NULL,
117
+ timestamp TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
118
+ event_json JSONB NOT NULL,
119
+ FOREIGN KEY (session_id) REFERENCES {self._session_table}(id) ON DELETE CASCADE
120
+ ) WITH (fillfactor = 80);
121
+
122
+ CREATE INDEX IF NOT EXISTS idx_{self._events_table}_session
123
+ ON {self._events_table}(session_id, timestamp ASC);
124
+ """
125
+
126
+ def _get_drop_tables_sql(self) -> "list[str]":
127
+ return [f"DROP TABLE IF EXISTS {self._events_table}", f"DROP TABLE IF EXISTS {self._session_table}"]
128
+
129
+ async def create_tables(self) -> None:
130
+ async with self._config.provide_session() as driver:
131
+ await driver.execute_script(await self._get_create_sessions_table_sql())
132
+ await driver.execute_script(await self._get_create_events_table_sql())
133
+
134
+ async def create_session(
135
+ self, session_id: str, app_name: str, user_id: str, state: "dict[str, Any]", owner_id: "Any | None" = None
136
+ ) -> SessionRecord:
137
+ params: tuple[Any, ...]
138
+ if self._owner_id_column_name:
139
+ query = pg_sql.SQL("""
140
+ INSERT INTO {table} (id, app_name, user_id, {owner_id_col}, state, create_time, update_time)
141
+ VALUES (%s, %s, %s, %s, %s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
142
+ """).format(
143
+ table=pg_sql.Identifier(self._session_table), owner_id_col=pg_sql.Identifier(self._owner_id_column_name)
144
+ )
145
+ params = (session_id, app_name, user_id, owner_id, Jsonb(state))
146
+ else:
147
+ query = pg_sql.SQL("""
148
+ INSERT INTO {table} (id, app_name, user_id, state, create_time, update_time)
149
+ VALUES (%s, %s, %s, %s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
150
+ """).format(table=pg_sql.Identifier(self._session_table))
151
+ params = (session_id, app_name, user_id, Jsonb(state))
152
+
153
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
154
+ await cur.execute(query, params)
155
+
156
+ return await self.get_session(session_id) # type: ignore[return-value]
157
+
158
+ async def get_session(self, session_id: str) -> "SessionRecord | None":
159
+ query = pg_sql.SQL("""
160
+ SELECT id, app_name, user_id, state, create_time, update_time
161
+ FROM {table}
162
+ WHERE id = %s
163
+ """).format(table=pg_sql.Identifier(self._session_table))
164
+
165
+ try:
166
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
167
+ await cur.execute(query, (session_id,))
168
+ row = await cur.fetchone()
169
+
170
+ if row is None:
171
+ return None
172
+
173
+ return SessionRecord(
174
+ id=row["id"],
175
+ app_name=row["app_name"],
176
+ user_id=row["user_id"],
177
+ state=row["state"],
178
+ create_time=row["create_time"],
179
+ update_time=row["update_time"],
180
+ )
181
+ except errors.UndefinedTable:
182
+ return None
183
+
184
+ async def update_session_state(self, session_id: str, state: "dict[str, Any]") -> None:
185
+ query = pg_sql.SQL("""
186
+ UPDATE {table}
187
+ SET state = %s, update_time = CURRENT_TIMESTAMP
188
+ WHERE id = %s
189
+ """).format(table=pg_sql.Identifier(self._session_table))
190
+
191
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
192
+ await cur.execute(query, (Jsonb(state), session_id))
193
+
194
+ async def delete_session(self, session_id: str) -> None:
195
+ query = pg_sql.SQL("DELETE FROM {table} WHERE id = %s").format(table=pg_sql.Identifier(self._session_table))
196
+
197
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
198
+ await cur.execute(query, (session_id,))
199
+
200
+ async def list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
201
+ if user_id is None:
202
+ query = pg_sql.SQL("""
203
+ SELECT id, app_name, user_id, state, create_time, update_time
204
+ FROM {table}
205
+ WHERE app_name = %s
206
+ ORDER BY update_time DESC
207
+ """).format(table=pg_sql.Identifier(self._session_table))
208
+ params: tuple[str, ...] = (app_name,)
209
+ else:
210
+ query = pg_sql.SQL("""
211
+ SELECT id, app_name, user_id, state, create_time, update_time
212
+ FROM {table}
213
+ WHERE app_name = %s AND user_id = %s
214
+ ORDER BY update_time DESC
215
+ """).format(table=pg_sql.Identifier(self._session_table))
216
+ params = (app_name, user_id)
217
+
218
+ try:
219
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
220
+ await cur.execute(query, params)
221
+ rows = await cur.fetchall()
222
+
223
+ return [
224
+ SessionRecord(
225
+ id=row["id"],
226
+ app_name=row["app_name"],
227
+ user_id=row["user_id"],
228
+ state=row["state"],
229
+ create_time=row["create_time"],
230
+ update_time=row["update_time"],
231
+ )
232
+ for row in rows
233
+ ]
234
+ except errors.UndefinedTable:
235
+ return []
236
+
237
+ async def append_event(self, event_record: EventRecord) -> None:
238
+ query = pg_sql.SQL("""
239
+ INSERT INTO {table} (
240
+ session_id, invocation_id, author, timestamp, event_json
241
+ ) VALUES (%s, %s, %s, %s, %s)
242
+ """).format(table=pg_sql.Identifier(self._events_table))
243
+
244
+ event_json_value = event_record["event_json"]
245
+ jsonb_value = Jsonb(event_json_value) if isinstance(event_json_value, dict) else event_json_value
246
+
247
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
248
+ await cur.execute(
249
+ query,
250
+ (
251
+ event_record["session_id"],
252
+ event_record["invocation_id"],
253
+ event_record["author"],
254
+ event_record["timestamp"],
255
+ jsonb_value,
256
+ ),
257
+ )
258
+
259
+ async def append_event_and_update_state(
260
+ self, event_record: EventRecord, session_id: str, state: "dict[str, Any]"
261
+ ) -> SessionRecord:
262
+ insert_query = pg_sql.SQL("""
263
+ INSERT INTO {table} (
264
+ session_id, invocation_id, author, timestamp, event_json
265
+ ) VALUES (%s, %s, %s, %s, %s)
266
+ """).format(table=pg_sql.Identifier(self._events_table))
267
+
268
+ update_query = pg_sql.SQL("""
269
+ UPDATE {table}
270
+ SET state = %s, update_time = CURRENT_TIMESTAMP
271
+ WHERE id = %s
272
+ RETURNING id, app_name, user_id, state, create_time, update_time
273
+ """).format(table=pg_sql.Identifier(self._session_table))
274
+
275
+ event_json_value = event_record["event_json"]
276
+ jsonb_value = Jsonb(event_json_value) if isinstance(event_json_value, dict) else event_json_value
277
+
278
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
279
+ await cur.execute(
280
+ insert_query,
281
+ (
282
+ event_record["session_id"],
283
+ event_record["invocation_id"],
284
+ event_record["author"],
285
+ event_record["timestamp"],
286
+ jsonb_value,
287
+ ),
288
+ )
289
+ await cur.execute(update_query, (Jsonb(state), session_id))
290
+ row = await cur.fetchone()
291
+ await conn.commit()
292
+
293
+ if row is None:
294
+ msg = f"Session {session_id} not found during append_event_and_update_state."
295
+ raise ValueError(msg)
296
+
297
+ return SessionRecord(
298
+ id=row["id"],
299
+ app_name=row["app_name"],
300
+ user_id=row["user_id"],
301
+ state=row["state"],
302
+ create_time=row["create_time"],
303
+ update_time=row["update_time"],
304
+ )
305
+
306
+ async def get_events(
307
+ self, session_id: str, after_timestamp: "datetime | None" = None, limit: "int | None" = None
308
+ ) -> "list[EventRecord]":
309
+ where_clauses = ["session_id = %s"]
310
+ params: list[Any] = [session_id]
311
+
312
+ if after_timestamp is not None:
313
+ where_clauses.append("timestamp > %s")
314
+ params.append(after_timestamp)
315
+
316
+ where_clause = " AND ".join(where_clauses)
317
+ if limit:
318
+ params.append(limit)
319
+
320
+ query = pg_sql.SQL(
321
+ """
322
+ SELECT session_id, invocation_id, author, timestamp, event_json
323
+ FROM {table}
324
+ WHERE {where_clause}
325
+ ORDER BY timestamp ASC{limit_clause}
326
+ """
327
+ ).format(
328
+ table=pg_sql.Identifier(self._events_table),
329
+ where_clause=pg_sql.SQL(where_clause), # pyright: ignore[reportArgumentType]
330
+ limit_clause=pg_sql.SQL(" LIMIT %s" if limit else ""), # pyright: ignore[reportArgumentType]
331
+ )
332
+
333
+ try:
334
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
335
+ await cur.execute(query, tuple(params))
336
+ rows = await cur.fetchall()
337
+
338
+ return [
339
+ EventRecord(
340
+ session_id=row["session_id"],
341
+ invocation_id=row["invocation_id"],
342
+ author=row["author"],
343
+ timestamp=row["timestamp"],
344
+ event_json=row["event_json"],
345
+ )
346
+ for row in rows
347
+ ]
348
+ except errors.UndefinedTable:
349
+ return []
350
+
351
+
352
+ class PsycopgSyncADKStore(BaseAsyncADKStore["PsycopgSyncConfig"]):
353
+ """PostgreSQL synchronous ADK store using Psycopg3 driver.
354
+
355
+ Implements session and event storage for Google Agent Development Kit
356
+ using PostgreSQL via psycopg3 with synchronous execution.
357
+ Events are stored as a single JSONB blob (``event_json``) alongside
358
+ indexed scalar columns for efficient querying.
359
+
360
+ Provides:
361
+ - Session state management with JSONB storage
362
+ - Full-fidelity event storage via ``event_json`` JSONB column
363
+ - Atomic ``create_event_and_update_state`` for durable session mutations
364
+ - Microsecond-precision timestamps with TIMESTAMPTZ
365
+ - Foreign key constraints with cascade delete
366
+ - GIN indexes for JSONB queries
367
+ - HOT updates with FILLFACTOR 80
368
+
369
+ Args:
370
+ config: PsycopgSyncConfig with extension_config["adk"] settings.
371
+ """
372
+
373
+ __slots__ = ()
374
+
375
+ def __init__(self, config: "PsycopgSyncConfig") -> None:
376
+ super().__init__(config)
377
+
378
+ async def _get_create_sessions_table_sql(self) -> str:
379
+ owner_id_line = ""
380
+ if self._owner_id_column_ddl:
381
+ owner_id_line = f",\n {self._owner_id_column_ddl}"
382
+
383
+ return f"""
384
+ CREATE TABLE IF NOT EXISTS {self._session_table} (
385
+ id VARCHAR(128) PRIMARY KEY,
386
+ app_name VARCHAR(128) NOT NULL,
387
+ user_id VARCHAR(128) NOT NULL{owner_id_line},
388
+ state JSONB NOT NULL DEFAULT '{{}}'::jsonb,
389
+ create_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
390
+ update_time TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
391
+ ) WITH (fillfactor = 80);
392
+
393
+ CREATE INDEX IF NOT EXISTS idx_{self._session_table}_app_user
394
+ ON {self._session_table}(app_name, user_id);
395
+
396
+ CREATE INDEX IF NOT EXISTS idx_{self._session_table}_update_time
397
+ ON {self._session_table}(update_time DESC);
398
+
399
+ CREATE INDEX IF NOT EXISTS idx_{self._session_table}_state
400
+ ON {self._session_table} USING GIN (state)
401
+ WHERE state != '{{}}'::jsonb;
402
+ """
403
+
404
+ async def _get_create_events_table_sql(self) -> str:
405
+ return f"""
406
+ CREATE TABLE IF NOT EXISTS {self._events_table} (
407
+ session_id VARCHAR(128) NOT NULL,
408
+ invocation_id VARCHAR(256) NOT NULL,
409
+ author VARCHAR(256) NOT NULL,
410
+ timestamp TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP,
411
+ event_json JSONB NOT NULL,
412
+ FOREIGN KEY (session_id) REFERENCES {self._session_table}(id) ON DELETE CASCADE
413
+ ) WITH (fillfactor = 80);
414
+
415
+ CREATE INDEX IF NOT EXISTS idx_{self._events_table}_session
416
+ ON {self._events_table}(session_id, timestamp ASC);
417
+ """
418
+
419
+ def _get_drop_tables_sql(self) -> "list[str]":
420
+ return [f"DROP TABLE IF EXISTS {self._events_table}", f"DROP TABLE IF EXISTS {self._session_table}"]
421
+
422
+ def _create_tables(self) -> None:
423
+ with self._config.provide_session() as driver:
424
+ driver.execute_script(run_(self._get_create_sessions_table_sql)())
425
+ driver.execute_script(run_(self._get_create_events_table_sql)())
426
+
427
+ async def create_tables(self) -> None:
428
+ """Create tables if they don't exist."""
429
+ await async_(self._create_tables)()
430
+
431
+ def _create_session(
432
+ self, session_id: str, app_name: str, user_id: str, state: "dict[str, Any]", owner_id: "Any | None" = None
433
+ ) -> SessionRecord:
434
+ params: tuple[Any, ...]
435
+ if self._owner_id_column_name:
436
+ query = pg_sql.SQL("""
437
+ INSERT INTO {table} (id, app_name, user_id, {owner_id_col}, state, create_time, update_time)
438
+ VALUES (%s, %s, %s, %s, %s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
439
+ """).format(
440
+ table=pg_sql.Identifier(self._session_table), owner_id_col=pg_sql.Identifier(self._owner_id_column_name)
441
+ )
442
+ params = (session_id, app_name, user_id, owner_id, Jsonb(state))
443
+ else:
444
+ query = pg_sql.SQL("""
445
+ INSERT INTO {table} (id, app_name, user_id, state, create_time, update_time)
446
+ VALUES (%s, %s, %s, %s, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP)
447
+ """).format(table=pg_sql.Identifier(self._session_table))
448
+ params = (session_id, app_name, user_id, Jsonb(state))
449
+
450
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
451
+ cur.execute(query, params)
452
+
453
+ result = self._get_session(session_id)
454
+ if result is None:
455
+ msg = "Failed to fetch created session"
456
+ raise RuntimeError(msg)
457
+ return result
458
+
459
+ async def create_session(
460
+ self, session_id: str, app_name: str, user_id: str, state: "dict[str, Any]", owner_id: "Any | None" = None
461
+ ) -> SessionRecord:
462
+ """Create a new session."""
463
+ return await async_(self._create_session)(session_id, app_name, user_id, state, owner_id)
464
+
465
+ def _get_session(self, session_id: str) -> "SessionRecord | None":
466
+ query = pg_sql.SQL("""
467
+ SELECT id, app_name, user_id, state, create_time, update_time
468
+ FROM {table}
469
+ WHERE id = %s
470
+ """).format(table=pg_sql.Identifier(self._session_table))
471
+
472
+ try:
473
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
474
+ cur.execute(query, (session_id,))
475
+ row = cur.fetchone()
476
+
477
+ if row is None:
478
+ return None
479
+
480
+ return SessionRecord(
481
+ id=row["id"],
482
+ app_name=row["app_name"],
483
+ user_id=row["user_id"],
484
+ state=row["state"],
485
+ create_time=row["create_time"],
486
+ update_time=row["update_time"],
487
+ )
488
+ except errors.UndefinedTable:
489
+ return None
490
+
491
+ async def get_session(self, session_id: str) -> "SessionRecord | None":
492
+ """Get session by ID."""
493
+ return await async_(self._get_session)(session_id)
494
+
495
+ def _update_session_state(self, session_id: str, state: "dict[str, Any]") -> None:
496
+ query = pg_sql.SQL("""
497
+ UPDATE {table}
498
+ SET state = %s, update_time = CURRENT_TIMESTAMP
499
+ WHERE id = %s
500
+ """).format(table=pg_sql.Identifier(self._session_table))
501
+
502
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
503
+ cur.execute(query, (Jsonb(state), session_id))
504
+
505
+ async def update_session_state(self, session_id: str, state: "dict[str, Any]") -> None:
506
+ """Update session state."""
507
+ await async_(self._update_session_state)(session_id, state)
508
+
509
+ def _delete_session(self, session_id: str) -> None:
510
+ query = pg_sql.SQL("DELETE FROM {table} WHERE id = %s").format(table=pg_sql.Identifier(self._session_table))
511
+
512
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
513
+ cur.execute(query, (session_id,))
514
+
515
+ async def delete_session(self, session_id: str) -> None:
516
+ """Delete session and associated events."""
517
+ await async_(self._delete_session)(session_id)
518
+
519
+ def _list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
520
+ if user_id is None:
521
+ query = pg_sql.SQL("""
522
+ SELECT id, app_name, user_id, state, create_time, update_time
523
+ FROM {table}
524
+ WHERE app_name = %s
525
+ ORDER BY update_time DESC
526
+ """).format(table=pg_sql.Identifier(self._session_table))
527
+ params: tuple[str, ...] = (app_name,)
528
+ else:
529
+ query = pg_sql.SQL("""
530
+ SELECT id, app_name, user_id, state, create_time, update_time
531
+ FROM {table}
532
+ WHERE app_name = %s AND user_id = %s
533
+ ORDER BY update_time DESC
534
+ """).format(table=pg_sql.Identifier(self._session_table))
535
+ params = (app_name, user_id)
536
+
537
+ try:
538
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
539
+ cur.execute(query, params)
540
+ rows = cur.fetchall()
541
+
542
+ return [
543
+ SessionRecord(
544
+ id=row["id"],
545
+ app_name=row["app_name"],
546
+ user_id=row["user_id"],
547
+ state=row["state"],
548
+ create_time=row["create_time"],
549
+ update_time=row["update_time"],
550
+ )
551
+ for row in rows
552
+ ]
553
+ except errors.UndefinedTable:
554
+ return []
555
+
556
+ async def list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
557
+ """List sessions for an app."""
558
+ return await async_(self._list_sessions)(app_name, user_id)
559
+
560
+ def _insert_event(self, event_record: EventRecord) -> None:
561
+ insert_query = pg_sql.SQL("""
562
+ INSERT INTO {table} (
563
+ session_id, invocation_id, author, timestamp, event_json
564
+ ) VALUES (%s, %s, %s, %s, %s)
565
+ """).format(table=pg_sql.Identifier(self._events_table))
566
+
567
+ event_json_value = event_record["event_json"]
568
+ jsonb_value = Jsonb(event_json_value) if isinstance(event_json_value, dict) else event_json_value
569
+
570
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
571
+ cur.execute(
572
+ insert_query,
573
+ (
574
+ event_record["session_id"],
575
+ event_record["invocation_id"],
576
+ event_record["author"],
577
+ event_record["timestamp"],
578
+ jsonb_value,
579
+ ),
580
+ )
581
+ conn.commit()
582
+
583
+ def _append_event_and_update_state(
584
+ self, event_record: EventRecord, session_id: str, state: "dict[str, Any]"
585
+ ) -> SessionRecord:
586
+ insert_query = pg_sql.SQL("""
587
+ INSERT INTO {table} (
588
+ session_id, invocation_id, author, timestamp, event_json
589
+ ) VALUES (%s, %s, %s, %s, %s)
590
+ """).format(table=pg_sql.Identifier(self._events_table))
591
+
592
+ update_query = pg_sql.SQL("""
593
+ UPDATE {table}
594
+ SET state = %s, update_time = CURRENT_TIMESTAMP
595
+ WHERE id = %s
596
+ RETURNING id, app_name, user_id, state, create_time, update_time
597
+ """).format(table=pg_sql.Identifier(self._session_table))
598
+
599
+ event_json_value = event_record["event_json"]
600
+ jsonb_value = Jsonb(event_json_value) if isinstance(event_json_value, dict) else event_json_value
601
+
602
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
603
+ cur.execute(
604
+ insert_query,
605
+ (
606
+ event_record["session_id"],
607
+ event_record["invocation_id"],
608
+ event_record["author"],
609
+ event_record["timestamp"],
610
+ jsonb_value,
611
+ ),
612
+ )
613
+ cur.execute(update_query, (Jsonb(state), session_id))
614
+ row = cur.fetchone()
615
+ conn.commit()
616
+
617
+ if row is None:
618
+ msg = f"Session {session_id} not found during append_event_and_update_state."
619
+ raise ValueError(msg)
620
+
621
+ return SessionRecord(
622
+ id=row["id"],
623
+ app_name=row["app_name"],
624
+ user_id=row["user_id"],
625
+ state=row["state"],
626
+ create_time=row["create_time"],
627
+ update_time=row["update_time"],
628
+ )
629
+
630
+ async def append_event_and_update_state(
631
+ self, event_record: EventRecord, session_id: str, state: "dict[str, Any]"
632
+ ) -> SessionRecord:
633
+ """Atomically append an event and update the session's durable state."""
634
+ return await async_(self._append_event_and_update_state)(event_record, session_id, state)
635
+
636
+ def _get_events(
637
+ self, session_id: str, after_timestamp: "datetime | None" = None, limit: "int | None" = None
638
+ ) -> "list[EventRecord]":
639
+ where_clauses = ["session_id = %s"]
640
+ params: list[Any] = [session_id]
641
+
642
+ if after_timestamp is not None:
643
+ where_clauses.append("timestamp > %s")
644
+ params.append(after_timestamp)
645
+
646
+ where_clause = " AND ".join(where_clauses)
647
+ if limit:
648
+ params.append(limit)
649
+
650
+ query = pg_sql.SQL(
651
+ """
652
+ SELECT session_id, invocation_id, author, timestamp, event_json
653
+ FROM {table}
654
+ WHERE {where_clause}
655
+ ORDER BY timestamp ASC{limit_clause}
656
+ """
657
+ ).format(
658
+ table=pg_sql.Identifier(self._events_table),
659
+ where_clause=pg_sql.SQL(where_clause), # pyright: ignore[reportArgumentType]
660
+ limit_clause=pg_sql.SQL(" LIMIT %s" if limit else ""), # pyright: ignore[reportArgumentType]
661
+ )
662
+
663
+ try:
664
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
665
+ cur.execute(query, tuple(params))
666
+ rows = cur.fetchall()
667
+
668
+ return [
669
+ EventRecord(
670
+ session_id=row["session_id"],
671
+ invocation_id=row["invocation_id"],
672
+ author=row["author"],
673
+ timestamp=row["timestamp"],
674
+ event_json=row["event_json"],
675
+ )
676
+ for row in rows
677
+ ]
678
+ except errors.UndefinedTable:
679
+ return []
680
+
681
+ async def get_events(
682
+ self, session_id: str, after_timestamp: "datetime | None" = None, limit: "int | None" = None
683
+ ) -> "list[EventRecord]":
684
+ """Get events for a session."""
685
+ return await async_(self._get_events)(session_id, after_timestamp, limit)
686
+
687
+ def _append_event(self, event_record: EventRecord) -> None:
688
+ """Synchronous implementation of append_event."""
689
+ self._insert_event(event_record)
690
+
691
+ async def append_event(self, event_record: EventRecord) -> None:
692
+ """Append an event to a session."""
693
+ await async_(self._append_event)(event_record)
694
+
695
+
696
+ class PsycopgAsyncADKMemoryStore(BaseAsyncADKMemoryStore["PsycopgAsyncConfig"]):
697
+ """PostgreSQL ADK memory store using Psycopg3 async driver."""
698
+
699
+ __slots__ = ()
700
+
701
+ def __init__(self, config: "PsycopgAsyncConfig") -> None:
702
+ """Initialize Psycopg async memory store."""
703
+ super().__init__(config)
704
+
705
+ async def _get_create_memory_table_sql(self) -> str:
706
+ """Get PostgreSQL CREATE TABLE SQL for memory entries."""
707
+ owner_id_line = ""
708
+ if self._owner_id_column_ddl:
709
+ owner_id_line = f",\n {self._owner_id_column_ddl}"
710
+
711
+ fts_index = ""
712
+ if self._use_fts:
713
+ fts_index = f"""
714
+ CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_fts
715
+ ON {self._memory_table} USING GIN (to_tsvector('english', content_text));
716
+ """
717
+
718
+ return f"""
719
+ CREATE TABLE IF NOT EXISTS {self._memory_table} (
720
+ id VARCHAR(128) PRIMARY KEY,
721
+ session_id VARCHAR(128) NOT NULL,
722
+ app_name VARCHAR(128) NOT NULL,
723
+ user_id VARCHAR(128) NOT NULL,
724
+ event_id VARCHAR(128) NOT NULL UNIQUE,
725
+ author VARCHAR(256){owner_id_line},
726
+ timestamp TIMESTAMPTZ NOT NULL,
727
+ content_json JSONB NOT NULL,
728
+ content_text TEXT NOT NULL,
729
+ metadata_json JSONB,
730
+ inserted_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
731
+ );
732
+
733
+ CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_app_user_time
734
+ ON {self._memory_table}(app_name, user_id, timestamp DESC);
735
+
736
+ CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_session
737
+ ON {self._memory_table}(session_id);
738
+ {fts_index}
739
+ """
740
+
741
+ def _get_drop_memory_table_sql(self) -> "list[str]":
742
+ """Get PostgreSQL DROP TABLE SQL statements."""
743
+ return [f"DROP TABLE IF EXISTS {self._memory_table}"]
744
+
745
+ async def create_tables(self) -> None:
746
+ """Create the memory table and indexes if they don't exist."""
747
+ if not self._enabled:
748
+ return
749
+
750
+ async with self._config.provide_session() as driver:
751
+ await driver.execute_script(await self._get_create_memory_table_sql())
752
+
753
+ async def insert_memory_entries(self, entries: "list[MemoryRecord]", owner_id: "object | None" = None) -> int:
754
+ """Bulk insert memory entries with deduplication."""
755
+ if not self._enabled:
756
+ msg = "Memory store is disabled"
757
+ raise RuntimeError(msg)
758
+
759
+ if not entries:
760
+ return 0
761
+
762
+ inserted_count = 0
763
+ if self._owner_id_column_name:
764
+ query = pg_sql.SQL("""
765
+ INSERT INTO {table} (
766
+ id, session_id, app_name, user_id, event_id, author,
767
+ {owner_id_col}, timestamp, content_json, content_text,
768
+ metadata_json, inserted_at
769
+ ) VALUES (
770
+ %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
771
+ )
772
+ ON CONFLICT (event_id) DO NOTHING
773
+ """).format(
774
+ table=pg_sql.Identifier(self._memory_table), owner_id_col=pg_sql.Identifier(self._owner_id_column_name)
775
+ )
776
+ else:
777
+ query = pg_sql.SQL("""
778
+ INSERT INTO {table} (
779
+ id, session_id, app_name, user_id, event_id, author,
780
+ timestamp, content_json, content_text, metadata_json, inserted_at
781
+ ) VALUES (
782
+ %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
783
+ )
784
+ ON CONFLICT (event_id) DO NOTHING
785
+ """).format(table=pg_sql.Identifier(self._memory_table))
786
+
787
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
788
+ for entry in entries:
789
+ if self._owner_id_column_name:
790
+ await cur.execute(query, _build_insert_params_with_owner(entry, owner_id))
791
+ else:
792
+ await cur.execute(query, _build_insert_params(entry))
793
+ if cur.rowcount and cur.rowcount > 0:
794
+ inserted_count += cur.rowcount
795
+
796
+ return inserted_count
797
+
798
+ async def search_entries(
799
+ self, query: str, app_name: str, user_id: str, limit: "int | None" = None
800
+ ) -> "list[MemoryRecord]":
801
+ """Search memory entries by text query."""
802
+ if not self._enabled:
803
+ msg = "Memory store is disabled"
804
+ raise RuntimeError(msg)
805
+
806
+ effective_limit = limit if limit is not None else self._max_results
807
+
808
+ try:
809
+ if self._use_fts:
810
+ try:
811
+ return await self._search_entries_fts(query, app_name, user_id, effective_limit)
812
+ except Exception as exc: # pragma: no cover - defensive fallback
813
+ logger.warning("FTS search failed; falling back to simple search: %s", exc)
814
+ return await self._search_entries_simple(query, app_name, user_id, effective_limit)
815
+ except errors.UndefinedTable:
816
+ return []
817
+
818
+ async def _search_entries_fts(self, query: str, app_name: str, user_id: str, limit: int) -> "list[MemoryRecord]":
819
+ sql = pg_sql.SQL(
820
+ """
821
+ SELECT id, session_id, app_name, user_id, event_id, author,
822
+ timestamp, content_json, content_text, metadata_json, inserted_at,
823
+ ts_rank(to_tsvector('english', content_text), plainto_tsquery('english', %s)) as rank
824
+ FROM {table}
825
+ WHERE app_name = %s
826
+ AND user_id = %s
827
+ AND to_tsvector('english', content_text) @@ plainto_tsquery('english', %s)
828
+ ORDER BY rank DESC, timestamp DESC
829
+ LIMIT %s
830
+ """
831
+ ).format(table=pg_sql.Identifier(self._memory_table))
832
+ params: tuple[str, str, str, str, int] = (query, app_name, user_id, query, limit)
833
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
834
+ await cur.execute(sql, params)
835
+ rows = await cur.fetchall()
836
+ return _rows_to_records(rows)
837
+
838
+ async def _search_entries_simple(self, query: str, app_name: str, user_id: str, limit: int) -> "list[MemoryRecord]":
839
+ sql = pg_sql.SQL(
840
+ """
841
+ SELECT id, session_id, app_name, user_id, event_id, author,
842
+ timestamp, content_json, content_text, metadata_json, inserted_at
843
+ FROM {table}
844
+ WHERE app_name = %s
845
+ AND user_id = %s
846
+ AND content_text ILIKE %s
847
+ ORDER BY timestamp DESC
848
+ LIMIT %s
849
+ """
850
+ ).format(table=pg_sql.Identifier(self._memory_table))
851
+ pattern = f"%{query}%"
852
+ params: tuple[str, str, str, int] = (app_name, user_id, pattern, limit)
853
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
854
+ await cur.execute(sql, params)
855
+ rows = await cur.fetchall()
856
+ return _rows_to_records(rows)
857
+
858
+ async def delete_entries_by_session(self, session_id: str) -> int:
859
+ """Delete all memory entries for a specific session."""
860
+ sql = pg_sql.SQL("DELETE FROM {table} WHERE session_id = %s").format(
861
+ table=pg_sql.Identifier(self._memory_table)
862
+ )
863
+
864
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
865
+ await cur.execute(sql, (session_id,))
866
+ return cur.rowcount if cur.rowcount and cur.rowcount > 0 else 0
867
+
868
+ async def delete_entries_older_than(self, days: int) -> int:
869
+ """Delete memory entries older than specified days."""
870
+ sql = pg_sql.SQL(
871
+ """
872
+ DELETE FROM {table}
873
+ WHERE inserted_at < CURRENT_TIMESTAMP - {interval}::interval
874
+ """
875
+ ).format(table=pg_sql.Identifier(self._memory_table), interval=pg_sql.Literal(f"{days} days"))
876
+
877
+ async with self._config.provide_connection() as conn, conn.cursor() as cur:
878
+ await cur.execute(sql)
879
+ return cur.rowcount if cur.rowcount and cur.rowcount > 0 else 0
880
+
881
+
882
+ class PsycopgSyncADKMemoryStore(BaseAsyncADKMemoryStore["PsycopgSyncConfig"]):
883
+ """PostgreSQL ADK memory store using Psycopg3 sync driver."""
884
+
885
+ __slots__ = ()
886
+
887
+ def __init__(self, config: "PsycopgSyncConfig") -> None:
888
+ """Initialize Psycopg sync memory store."""
889
+ super().__init__(config)
890
+
891
+ async def _get_create_memory_table_sql(self) -> str:
892
+ """Get PostgreSQL CREATE TABLE SQL for memory entries."""
893
+ owner_id_line = ""
894
+ if self._owner_id_column_ddl:
895
+ owner_id_line = f",\n {self._owner_id_column_ddl}"
896
+
897
+ fts_index = ""
898
+ if self._use_fts:
899
+ fts_index = f"""
900
+ CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_fts
901
+ ON {self._memory_table} USING GIN (to_tsvector('english', content_text));
902
+ """
903
+
904
+ return f"""
905
+ CREATE TABLE IF NOT EXISTS {self._memory_table} (
906
+ id VARCHAR(128) PRIMARY KEY,
907
+ session_id VARCHAR(128) NOT NULL,
908
+ app_name VARCHAR(128) NOT NULL,
909
+ user_id VARCHAR(128) NOT NULL,
910
+ event_id VARCHAR(128) NOT NULL UNIQUE,
911
+ author VARCHAR(256){owner_id_line},
912
+ timestamp TIMESTAMPTZ NOT NULL,
913
+ content_json JSONB NOT NULL,
914
+ content_text TEXT NOT NULL,
915
+ metadata_json JSONB,
916
+ inserted_at TIMESTAMPTZ NOT NULL DEFAULT CURRENT_TIMESTAMP
917
+ );
918
+
919
+ CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_app_user_time
920
+ ON {self._memory_table}(app_name, user_id, timestamp DESC);
921
+
922
+ CREATE INDEX IF NOT EXISTS idx_{self._memory_table}_session
923
+ ON {self._memory_table}(session_id);
924
+ {fts_index}
925
+ """
926
+
927
+ def _get_drop_memory_table_sql(self) -> "list[str]":
928
+ """Get PostgreSQL DROP TABLE SQL statements."""
929
+ return [f"DROP TABLE IF EXISTS {self._memory_table}"]
930
+
931
+ def _create_tables(self) -> None:
932
+ """Create the memory table and indexes if they don't exist."""
933
+ if not self._enabled:
934
+ return
935
+
936
+ with self._config.provide_session() as driver:
937
+ driver.execute_script(run_(self._get_create_memory_table_sql)())
938
+
939
+ async def create_tables(self) -> None:
940
+ """Create tables if they don't exist."""
941
+ await async_(self._create_tables)()
942
+
943
+ def _insert_memory_entries(self, entries: "list[MemoryRecord]", owner_id: "object | None" = None) -> int:
944
+ """Bulk insert memory entries with deduplication."""
945
+ if not self._enabled:
946
+ msg = "Memory store is disabled"
947
+ raise RuntimeError(msg)
948
+
949
+ if not entries:
950
+ return 0
951
+
952
+ inserted_count = 0
953
+ if self._owner_id_column_name:
954
+ query = pg_sql.SQL("""
955
+ INSERT INTO {table} (
956
+ id, session_id, app_name, user_id, event_id, author,
957
+ {owner_id_col}, timestamp, content_json, content_text,
958
+ metadata_json, inserted_at
959
+ ) VALUES (
960
+ %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
961
+ )
962
+ ON CONFLICT (event_id) DO NOTHING
963
+ """).format(
964
+ table=pg_sql.Identifier(self._memory_table), owner_id_col=pg_sql.Identifier(self._owner_id_column_name)
965
+ )
966
+ else:
967
+ query = pg_sql.SQL("""
968
+ INSERT INTO {table} (
969
+ id, session_id, app_name, user_id, event_id, author,
970
+ timestamp, content_json, content_text, metadata_json, inserted_at
971
+ ) VALUES (
972
+ %s, %s, %s, %s, %s, %s, %s, %s, %s, %s, %s
973
+ )
974
+ ON CONFLICT (event_id) DO NOTHING
975
+ """).format(table=pg_sql.Identifier(self._memory_table))
976
+
977
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
978
+ for entry in entries:
979
+ if self._owner_id_column_name:
980
+ cur.execute(query, _build_insert_params_with_owner(entry, owner_id))
981
+ else:
982
+ cur.execute(query, _build_insert_params(entry))
983
+ if cur.rowcount and cur.rowcount > 0:
984
+ inserted_count += cur.rowcount
985
+
986
+ return inserted_count
987
+
988
+ async def insert_memory_entries(self, entries: "list[MemoryRecord]", owner_id: "object | None" = None) -> int:
989
+ """Bulk insert memory entries with deduplication."""
990
+ return await async_(self._insert_memory_entries)(entries, owner_id)
991
+
992
+ def _search_entries(
993
+ self, query: str, app_name: str, user_id: str, limit: "int | None" = None
994
+ ) -> "list[MemoryRecord]":
995
+ """Search memory entries by text query."""
996
+ if not self._enabled:
997
+ msg = "Memory store is disabled"
998
+ raise RuntimeError(msg)
999
+
1000
+ effective_limit = limit if limit is not None else self._max_results
1001
+
1002
+ try:
1003
+ if self._use_fts:
1004
+ try:
1005
+ return self._search_entries_fts(query, app_name, user_id, effective_limit)
1006
+ except Exception as exc: # pragma: no cover - defensive fallback
1007
+ logger.warning("FTS search failed; falling back to simple search: %s", exc)
1008
+ return self._search_entries_simple(query, app_name, user_id, effective_limit)
1009
+ except errors.UndefinedTable:
1010
+ return []
1011
+
1012
+ async def search_entries(
1013
+ self, query: str, app_name: str, user_id: str, limit: "int | None" = None
1014
+ ) -> "list[MemoryRecord]":
1015
+ """Search memory entries by text query."""
1016
+ return await async_(self._search_entries)(query, app_name, user_id, limit)
1017
+
1018
+ def _search_entries_fts(self, query: str, app_name: str, user_id: str, limit: int) -> "list[MemoryRecord]":
1019
+ sql = pg_sql.SQL(
1020
+ """
1021
+ SELECT id, session_id, app_name, user_id, event_id, author,
1022
+ timestamp, content_json, content_text, metadata_json, inserted_at,
1023
+ ts_rank(to_tsvector('english', content_text), plainto_tsquery('english', %s)) as rank
1024
+ FROM {table}
1025
+ WHERE app_name = %s
1026
+ AND user_id = %s
1027
+ AND to_tsvector('english', content_text) @@ plainto_tsquery('english', %s)
1028
+ ORDER BY rank DESC, timestamp DESC
1029
+ LIMIT %s
1030
+ """
1031
+ ).format(table=pg_sql.Identifier(self._memory_table))
1032
+ params: tuple[str, str, str, str, int] = (query, app_name, user_id, query, limit)
1033
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
1034
+ cur.execute(sql, params)
1035
+ rows = cur.fetchall()
1036
+ return _rows_to_records(rows)
1037
+
1038
+ def _search_entries_simple(self, query: str, app_name: str, user_id: str, limit: int) -> "list[MemoryRecord]":
1039
+ sql = pg_sql.SQL(
1040
+ """
1041
+ SELECT id, session_id, app_name, user_id, event_id, author,
1042
+ timestamp, content_json, content_text, metadata_json, inserted_at
1043
+ FROM {table}
1044
+ WHERE app_name = %s
1045
+ AND user_id = %s
1046
+ AND content_text ILIKE %s
1047
+ ORDER BY timestamp DESC
1048
+ LIMIT %s
1049
+ """
1050
+ ).format(table=pg_sql.Identifier(self._memory_table))
1051
+ pattern = f"%{query}%"
1052
+ params: tuple[str, str, str, int] = (app_name, user_id, pattern, limit)
1053
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
1054
+ cur.execute(sql, params)
1055
+ rows = cur.fetchall()
1056
+ return _rows_to_records(rows)
1057
+
1058
+ def _delete_entries_by_session(self, session_id: str) -> int:
1059
+ """Delete all memory entries for a specific session."""
1060
+ sql = pg_sql.SQL("DELETE FROM {table} WHERE session_id = %s").format(
1061
+ table=pg_sql.Identifier(self._memory_table)
1062
+ )
1063
+
1064
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
1065
+ cur.execute(sql, (session_id,))
1066
+ return cur.rowcount if cur.rowcount and cur.rowcount > 0 else 0
1067
+
1068
+ async def delete_entries_by_session(self, session_id: str) -> int:
1069
+ """Delete all memory entries for a specific session."""
1070
+ return await async_(self._delete_entries_by_session)(session_id)
1071
+
1072
+ def _delete_entries_older_than(self, days: int) -> int:
1073
+ """Delete memory entries older than specified days."""
1074
+ sql = pg_sql.SQL(
1075
+ """
1076
+ DELETE FROM {table}
1077
+ WHERE inserted_at < CURRENT_TIMESTAMP - {interval}::interval
1078
+ """
1079
+ ).format(table=pg_sql.Identifier(self._memory_table), interval=pg_sql.Literal(f"{days} days"))
1080
+
1081
+ with self._config.provide_connection() as conn, conn.cursor() as cur:
1082
+ cur.execute(sql)
1083
+ return cur.rowcount if cur.rowcount and cur.rowcount > 0 else 0
1084
+
1085
+ async def delete_entries_older_than(self, days: int) -> int:
1086
+ """Delete memory entries older than specified days."""
1087
+ return await async_(self._delete_entries_older_than)(days)
1088
+
1089
+
1090
+ def _rows_to_records(rows: "list[Any]") -> "list[MemoryRecord]":
1091
+ return [
1092
+ {
1093
+ "id": row["id"],
1094
+ "session_id": row["session_id"],
1095
+ "app_name": row["app_name"],
1096
+ "user_id": row["user_id"],
1097
+ "event_id": row["event_id"],
1098
+ "author": row["author"],
1099
+ "timestamp": row["timestamp"],
1100
+ "content_json": row["content_json"],
1101
+ "content_text": row["content_text"],
1102
+ "metadata_json": row["metadata_json"],
1103
+ "inserted_at": row["inserted_at"],
1104
+ }
1105
+ for row in rows
1106
+ ]