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,758 @@
1
+ """Spanner ADK store."""
2
+
3
+ from collections.abc import Iterable
4
+ from datetime import datetime, timedelta, timezone
5
+ from typing import TYPE_CHECKING, Any, ClassVar, Protocol, cast
6
+
7
+ from google.cloud.spanner_v1 import param_types
8
+
9
+ from sqlspec.adapters.spanner.config import SpannerSyncConfig
10
+ from sqlspec.extensions.adk import BaseAsyncADKStore, EventRecord, SessionRecord
11
+ from sqlspec.extensions.adk.memory.store import BaseAsyncADKMemoryStore
12
+ from sqlspec.protocols import SpannerParamTypesProtocol
13
+ from sqlspec.utils.serializers import from_json, to_json
14
+ from sqlspec.utils.sync_tools import async_, run_
15
+
16
+ if TYPE_CHECKING:
17
+ from google.cloud.spanner_v1.database import Database
18
+ from google.cloud.spanner_v1.transaction import Transaction
19
+
20
+ from sqlspec.config import ADKConfig
21
+ from sqlspec.extensions.adk import MemoryRecord
22
+ SPANNER_PARAM_TYPES: SpannerParamTypesProtocol = cast("SpannerParamTypesProtocol", param_types)
23
+
24
+ __all__ = ("SpannerSyncADKMemoryStore", "SpannerSyncADKStore")
25
+
26
+
27
+ class SpannerSyncADKStore(BaseAsyncADKStore[SpannerSyncConfig]):
28
+ """Spanner ADK store backed by synchronous Spanner client."""
29
+
30
+ connector_name: ClassVar[str] = "spanner"
31
+
32
+ def __init__(self, config: SpannerSyncConfig) -> None:
33
+ super().__init__(config)
34
+ adk_config = cast("dict[str, Any]", config.extension_config.get("adk", {}))
35
+ self._shard_count: int = int(adk_config.get("shard_count", 0)) if adk_config.get("shard_count") else 0
36
+ self._session_table_options: str | None = adk_config.get("session_table_options")
37
+ self._events_table_options: str | None = adk_config.get("events_table_options")
38
+ self._expires_index_options: str | None = adk_config.get("expires_index_options")
39
+ self._session_row_deletion_policy = _spanner_row_deletion_policy(
40
+ adk_config, "session_ttl_seconds", "create_time"
41
+ )
42
+ self._events_row_deletion_policy = _spanner_row_deletion_policy(adk_config, "event_ttl_seconds", "timestamp")
43
+
44
+ def _database(self) -> "Database":
45
+ return self._config.get_database()
46
+
47
+ def _run_read(
48
+ self, sql: str, params: "dict[str, Any] | None" = None, types: "dict[str, Any] | None" = None
49
+ ) -> "list[Any]":
50
+ with self._config.provide_connection() as snapshot:
51
+ result_set = cast("Any", snapshot).execute_sql(sql, params=params, param_types=types)
52
+ return list(result_set)
53
+
54
+ def _run_write(self, statements: "list[tuple[str, dict[str, Any], dict[str, Any]]]") -> None:
55
+ self._database().run_in_transaction(_SpannerWriteJob(statements)) # type: ignore[no-untyped-call]
56
+
57
+ def _session_param_types(self, include_owner: bool) -> "dict[str, Any]":
58
+ json_type = _json_param_type()
59
+ types: dict[str, Any] = {
60
+ "id": SPANNER_PARAM_TYPES.STRING,
61
+ "app_name": SPANNER_PARAM_TYPES.STRING,
62
+ "user_id": SPANNER_PARAM_TYPES.STRING,
63
+ "state": json_type,
64
+ }
65
+ if include_owner and self._owner_id_column_name:
66
+ types["owner_id"] = SPANNER_PARAM_TYPES.STRING
67
+ return types
68
+
69
+ def _event_param_types(self) -> "dict[str, Any]":
70
+ json_type = _json_param_type()
71
+ return {
72
+ "session_id": SPANNER_PARAM_TYPES.STRING,
73
+ "invocation_id": SPANNER_PARAM_TYPES.STRING,
74
+ "author": SPANNER_PARAM_TYPES.STRING,
75
+ "timestamp": SPANNER_PARAM_TYPES.TIMESTAMP,
76
+ "event_json": json_type,
77
+ }
78
+
79
+ def _decode_state(self, raw: Any) -> Any:
80
+ if isinstance(raw, str):
81
+ return from_json(raw)
82
+ return raw
83
+
84
+ def _decode_json(self, raw: Any) -> Any:
85
+ if raw is None:
86
+ return None
87
+ if isinstance(raw, str):
88
+ return from_json(raw)
89
+ return raw
90
+
91
+ def _create_session(
92
+ self, session_id: str, app_name: str, user_id: str, state: "dict[str, Any]", owner_id: "Any | None" = None
93
+ ) -> SessionRecord:
94
+ state_json = to_json(state)
95
+ params: dict[str, Any] = {"id": session_id, "app_name": app_name, "user_id": user_id, "state": state_json}
96
+ columns = "id, app_name, user_id, state, create_time, update_time"
97
+ values = "@id, @app_name, @user_id, @state, PENDING_COMMIT_TIMESTAMP(), PENDING_COMMIT_TIMESTAMP()"
98
+ if self._owner_id_column_name:
99
+ params["owner_id"] = owner_id
100
+ columns = f"id, app_name, user_id, {self._owner_id_column_name}, state, create_time, update_time"
101
+ values = (
102
+ "@id, @app_name, @user_id, @owner_id, @state, PENDING_COMMIT_TIMESTAMP(), PENDING_COMMIT_TIMESTAMP()"
103
+ )
104
+
105
+ sql = f"""
106
+ INSERT INTO {self._session_table} ({columns})
107
+ VALUES ({values})
108
+ """
109
+ self._run_write([(sql, params, self._session_param_types(self._owner_id_column_name is not None))])
110
+
111
+ return {
112
+ "id": session_id,
113
+ "app_name": app_name,
114
+ "user_id": user_id,
115
+ "state": state,
116
+ "create_time": datetime.now(timezone.utc),
117
+ "update_time": datetime.now(timezone.utc),
118
+ }
119
+
120
+ async def create_session(
121
+ self, session_id: str, app_name: str, user_id: str, state: "dict[str, Any]", owner_id: "Any | None" = None
122
+ ) -> SessionRecord:
123
+ """Create a new session."""
124
+ return await async_(self._create_session)(session_id, app_name, user_id, state, owner_id)
125
+
126
+ def _get_session(self, session_id: str) -> "SessionRecord | None":
127
+ sql = f"""
128
+ SELECT id, app_name, user_id, state, create_time, update_time{", " + self._owner_id_column_name if self._owner_id_column_name else ""}
129
+ FROM {self._session_table}
130
+ WHERE id = @id
131
+ """
132
+ if self._shard_count > 1:
133
+ sql = f"{sql} AND shard_id = MOD(FARM_FINGERPRINT(@id), {self._shard_count})"
134
+ sql = f"{sql} LIMIT 1"
135
+ params = {"id": session_id}
136
+ rows = self._run_read(sql, params, {"id": SPANNER_PARAM_TYPES.STRING})
137
+ if not rows:
138
+ return None
139
+
140
+ row = rows[0]
141
+ state_value = self._decode_state(row[3])
142
+ record: SessionRecord = {
143
+ "id": row[0],
144
+ "app_name": row[1],
145
+ "user_id": row[2],
146
+ "state": state_value,
147
+ "create_time": row[4],
148
+ "update_time": row[5],
149
+ }
150
+ return record
151
+
152
+ async def get_session(self, session_id: str) -> "SessionRecord | None":
153
+ """Get session by ID."""
154
+ return await async_(self._get_session)(session_id)
155
+
156
+ def _update_session_state(self, session_id: str, state: "dict[str, Any]") -> None:
157
+ params = {"id": session_id, "state": to_json(state)}
158
+ json_type = _json_param_type()
159
+ sql = f"""
160
+ UPDATE {self._session_table}
161
+ SET state = @state, update_time = PENDING_COMMIT_TIMESTAMP()
162
+ WHERE id = @id
163
+ """
164
+ if self._shard_count > 1:
165
+ sql = f"{sql} AND shard_id = MOD(FARM_FINGERPRINT(@id), {self._shard_count})"
166
+ self._run_write([(sql, params, {"id": SPANNER_PARAM_TYPES.STRING, "state": json_type})])
167
+
168
+ async def update_session_state(self, session_id: str, state: "dict[str, Any]") -> None:
169
+ """Update session state."""
170
+ await async_(self._update_session_state)(session_id, state)
171
+
172
+ def _list_sessions(self, app_name: str, user_id: "str | None" = None) -> "list[SessionRecord]":
173
+ sql = f"""
174
+ SELECT id, app_name, user_id, state, create_time, update_time{", " + self._owner_id_column_name if self._owner_id_column_name else ""}
175
+ FROM {self._session_table}
176
+ WHERE app_name = @app_name
177
+ """
178
+ params: dict[str, Any] = {"app_name": app_name}
179
+ types: dict[str, Any] = {"app_name": SPANNER_PARAM_TYPES.STRING}
180
+ if user_id is not None:
181
+ sql = f"{sql} AND user_id = @user_id"
182
+ params["user_id"] = user_id
183
+ types["user_id"] = SPANNER_PARAM_TYPES.STRING
184
+ if self._shard_count > 1:
185
+ sql = f"{sql} AND shard_id = MOD(FARM_FINGERPRINT(id), {self._shard_count})"
186
+ sql = f"{sql} ORDER BY update_time DESC"
187
+
188
+ rows = self._run_read(sql, params, types)
189
+ records: list[SessionRecord] = []
190
+ for row in rows:
191
+ state_value = self._decode_state(row[3])
192
+ record: SessionRecord = {
193
+ "id": row[0],
194
+ "app_name": row[1],
195
+ "user_id": row[2],
196
+ "state": state_value,
197
+ "create_time": row[4],
198
+ "update_time": row[5],
199
+ }
200
+ records.append(record)
201
+ return records
202
+
203
+ async def list_sessions(self, app_name: str, user_id: str | None = None) -> "list[SessionRecord]":
204
+ """List sessions for an app."""
205
+ return await async_(self._list_sessions)(app_name, user_id)
206
+
207
+ def _delete_session(self, session_id: str) -> None:
208
+ shard_clause = (
209
+ f" AND shard_id = MOD(FARM_FINGERPRINT(@session_id), {self._shard_count})" if self._shard_count > 1 else ""
210
+ )
211
+ delete_events_sql = f"DELETE FROM {self._events_table} WHERE session_id = @session_id{shard_clause}"
212
+ delete_session_sql = f"DELETE FROM {self._session_table} WHERE id = @session_id{shard_clause}"
213
+ params = {"session_id": session_id}
214
+ types = {"session_id": SPANNER_PARAM_TYPES.STRING}
215
+ self._run_write([(delete_events_sql, params, types), (delete_session_sql, params, types)])
216
+
217
+ async def delete_session(self, session_id: str) -> None:
218
+ """Delete session and associated events."""
219
+ await async_(self._delete_session)(session_id)
220
+
221
+ def _append_event_and_update_state(
222
+ self, event_record: "EventRecord", session_id: str, state: "dict[str, Any]"
223
+ ) -> SessionRecord:
224
+ """Atomically insert an event and update session state in one transaction.
225
+
226
+ Both the event INSERT and the session state UPDATE execute within a single
227
+ Spanner transaction so they succeed or fail together. A follow-up
228
+ single-use read returns the SessionRecord; we can't capture update_time
229
+ inside the write txn because PENDING_COMMIT_TIMESTAMP() only materialises
230
+ on commit.
231
+
232
+ Args:
233
+ event_record: Event record to store.
234
+ session_id: Session whose state should be updated.
235
+ state: Post-append durable state snapshot.
236
+ """
237
+ event_params: dict[str, Any] = {
238
+ "session_id": event_record["session_id"],
239
+ "invocation_id": event_record["invocation_id"],
240
+ "author": event_record["author"],
241
+ "timestamp": event_record["timestamp"],
242
+ "event_json": to_json(event_record["event_json"]),
243
+ }
244
+ insert_sql = f"""
245
+ INSERT INTO {self._events_table} (session_id, invocation_id, author, timestamp, event_json)
246
+ VALUES (@session_id, @invocation_id, @author, @timestamp, @event_json)
247
+ """
248
+
249
+ json_type = _json_param_type()
250
+ state_params: dict[str, Any] = {"id": session_id, "state": to_json(state)}
251
+ update_sql = f"""
252
+ UPDATE {self._session_table}
253
+ SET state = @state, update_time = PENDING_COMMIT_TIMESTAMP()
254
+ WHERE id = @id
255
+ """
256
+ if self._shard_count > 1:
257
+ update_sql = f"{update_sql} AND shard_id = MOD(FARM_FINGERPRINT(@id), {self._shard_count})"
258
+
259
+ self._run_write([
260
+ (insert_sql, event_params, self._event_param_types()),
261
+ (update_sql, state_params, {"id": SPANNER_PARAM_TYPES.STRING, "state": json_type}),
262
+ ])
263
+
264
+ record = self._get_session(session_id)
265
+ if record is None:
266
+ msg = f"Session {session_id} not found during append_event_and_update_state."
267
+ raise ValueError(msg)
268
+ return record
269
+
270
+ async def append_event_and_update_state(
271
+ self, event_record: EventRecord, session_id: str, state: "dict[str, Any]"
272
+ ) -> SessionRecord:
273
+ """Atomically append an event and update the session's durable state."""
274
+ return await async_(self._append_event_and_update_state)(event_record, session_id, state)
275
+
276
+ def _insert_event(self, event_record: "EventRecord") -> None:
277
+ event_params: dict[str, Any] = {
278
+ "session_id": event_record["session_id"],
279
+ "invocation_id": event_record["invocation_id"],
280
+ "author": event_record["author"],
281
+ "timestamp": event_record["timestamp"],
282
+ "event_json": to_json(event_record["event_json"]),
283
+ }
284
+ insert_sql = f"""
285
+ INSERT INTO {self._events_table} (session_id, invocation_id, author, timestamp, event_json)
286
+ VALUES (@session_id, @invocation_id, @author, @timestamp, @event_json)
287
+ """
288
+ self._run_write([(insert_sql, event_params, self._event_param_types())])
289
+
290
+ def _get_events(
291
+ self, session_id: str, after_timestamp: "datetime | None" = None, limit: "int | None" = None
292
+ ) -> "list[EventRecord]":
293
+ sql = f"""
294
+ SELECT session_id, invocation_id, author, timestamp, event_json
295
+ FROM {self._events_table}
296
+ WHERE session_id = @session_id
297
+ """
298
+ if self._shard_count > 1:
299
+ sql = f"{sql} AND shard_id = MOD(FARM_FINGERPRINT(@session_id), {self._shard_count})"
300
+ params: dict[str, Any] = {"session_id": session_id}
301
+ types: dict[str, Any] = {"session_id": SPANNER_PARAM_TYPES.STRING}
302
+ if after_timestamp is not None:
303
+ sql = f"{sql} AND timestamp > @after_timestamp"
304
+ params["after_timestamp"] = after_timestamp
305
+ types["after_timestamp"] = SPANNER_PARAM_TYPES.TIMESTAMP
306
+ sql = f"{sql} ORDER BY timestamp ASC"
307
+ if limit is not None:
308
+ sql = f"{sql} LIMIT @limit"
309
+ params["limit"] = limit
310
+ types["limit"] = SPANNER_PARAM_TYPES.INT64
311
+ rows = self._run_read(sql, params, types)
312
+ return [
313
+ {
314
+ "session_id": row[0],
315
+ "invocation_id": row[1] or "",
316
+ "author": row[2] or "",
317
+ "timestamp": row[3],
318
+ "event_json": row[4],
319
+ }
320
+ for row in rows
321
+ ]
322
+
323
+ async def get_events(
324
+ self, session_id: str, after_timestamp: "datetime | None" = None, limit: "int | None" = None
325
+ ) -> "list[EventRecord]":
326
+ """Get events for a session."""
327
+ return await async_(self._get_events)(session_id, after_timestamp, limit)
328
+
329
+ def _append_event(self, event_record: EventRecord) -> None:
330
+ """Synchronous implementation of append_event."""
331
+ self._insert_event(event_record)
332
+
333
+ async def append_event(self, event_record: EventRecord) -> None:
334
+ """Append an event to a session."""
335
+ await async_(self._append_event)(event_record)
336
+
337
+ def _create_tables(self) -> None:
338
+ database = self._database()
339
+ existing_tables = {t.table_id for t in database.list_tables()} # type: ignore[no-untyped-call]
340
+
341
+ ddl_statements: list[str] = []
342
+ if self._session_table not in existing_tables:
343
+ ddl_statements.append(run_(self._get_create_sessions_table_sql)())
344
+ if self._events_table not in existing_tables:
345
+ ddl_statements.append(run_(self._get_create_events_table_sql)())
346
+
347
+ if ddl_statements:
348
+ database.update_ddl(ddl_statements).result(300) # type: ignore[no-untyped-call]
349
+
350
+ async def create_tables(self) -> None:
351
+ """Create tables if they don't exist."""
352
+ await async_(self._create_tables)()
353
+
354
+ async def _get_create_sessions_table_sql(self) -> str:
355
+ owner_line = ""
356
+ if self._owner_id_column_ddl:
357
+ owner_line = f",\n {self._owner_id_column_ddl}"
358
+ shard_column = ""
359
+ pk = "PRIMARY KEY (id)"
360
+ if self._shard_count > 1:
361
+ shard_column = f",\n shard_id INT64 AS (MOD(FARM_FINGERPRINT(id), {self._shard_count})) STORED"
362
+ pk = "PRIMARY KEY (shard_id, id)"
363
+ options = ""
364
+ if self._session_table_options:
365
+ options = f"\nOPTIONS ({self._session_table_options})"
366
+ return f"""
367
+ CREATE TABLE {self._session_table} (
368
+ id STRING(128) NOT NULL,
369
+ app_name STRING(128) NOT NULL,
370
+ user_id STRING(128) NOT NULL{owner_line},
371
+ state JSON NOT NULL,
372
+ create_time TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
373
+ update_time TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true){shard_column}
374
+ ) {pk}{options}{self._session_row_deletion_policy}
375
+ """
376
+
377
+ async def _get_create_events_table_sql(self) -> str:
378
+ shard_column = ""
379
+ pk = "PRIMARY KEY (session_id, timestamp)"
380
+ if self._shard_count > 1:
381
+ shard_column = f",\n shard_id INT64 AS (MOD(FARM_FINGERPRINT(session_id), {self._shard_count})) STORED"
382
+ pk = "PRIMARY KEY (shard_id, session_id, timestamp)"
383
+ options = ""
384
+ if self._events_table_options:
385
+ options = f"\nOPTIONS ({self._events_table_options})"
386
+ return f"""
387
+ CREATE TABLE {self._events_table} (
388
+ session_id STRING(128) NOT NULL,
389
+ invocation_id STRING(256) NOT NULL,
390
+ author STRING(128) NOT NULL,
391
+ timestamp TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
392
+ event_json JSON NOT NULL{shard_column}
393
+ ) {pk}{options}{self._events_row_deletion_policy}
394
+ """
395
+
396
+ def _get_drop_tables_sql(self) -> "list[str]":
397
+ return [f"DROP TABLE {self._events_table}", f"DROP TABLE {self._session_table}"]
398
+
399
+
400
+ class SpannerSyncADKMemoryStore(BaseAsyncADKMemoryStore[SpannerSyncConfig]):
401
+ """Spanner ADK memory store backed by synchronous Spanner client."""
402
+
403
+ connector_name: ClassVar[str] = "spanner"
404
+
405
+ def __init__(self, config: SpannerSyncConfig) -> None:
406
+ super().__init__(config)
407
+ adk_config = cast("ADKConfig", config.extension_config.get("adk", {}))
408
+ shard_count = adk_config.get("shard_count")
409
+ self._shard_count = int(shard_count) if isinstance(shard_count, int) else 0
410
+ self._memory_table_options: str | None = adk_config.get("memory_table_options")
411
+ self._memory_row_deletion_policy = _spanner_row_deletion_policy(
412
+ cast("dict[str, Any]", adk_config), "memory_ttl_seconds", "inserted_at"
413
+ )
414
+
415
+ def _database(self) -> "Database":
416
+ return self._config.get_database()
417
+
418
+ def _run_read(
419
+ self, sql: str, params: "dict[str, Any] | None" = None, types: "dict[str, Any] | None" = None
420
+ ) -> "list[Any]":
421
+ with self._config.provide_connection() as snapshot:
422
+ reader = cast("_SpannerReadProtocol", snapshot)
423
+ result_set = reader.execute_sql(sql, params=params, param_types=types)
424
+ return list(result_set)
425
+
426
+ def _run_write(self, statements: "list[tuple[str, dict[str, Any], dict[str, Any]]]") -> None:
427
+ self._database().run_in_transaction(_SpannerMemoryWriteJob(statements)) # type: ignore[no-untyped-call]
428
+
429
+ def _execute_update(self, sql: str, params: "dict[str, Any]", types: "dict[str, Any]") -> int:
430
+ return int(self._database().run_in_transaction(_SpannerMemoryUpdateJob(sql, params, types))) # type: ignore[no-untyped-call]
431
+
432
+ def _memory_param_types(self, include_owner: bool) -> "dict[str, Any]":
433
+ types: dict[str, Any] = {
434
+ "id": SPANNER_PARAM_TYPES.STRING,
435
+ "session_id": SPANNER_PARAM_TYPES.STRING,
436
+ "app_name": SPANNER_PARAM_TYPES.STRING,
437
+ "user_id": SPANNER_PARAM_TYPES.STRING,
438
+ "event_id": SPANNER_PARAM_TYPES.STRING,
439
+ "author": SPANNER_PARAM_TYPES.STRING,
440
+ "timestamp": SPANNER_PARAM_TYPES.TIMESTAMP,
441
+ "content_json": _json_param_type(),
442
+ "content_text": SPANNER_PARAM_TYPES.STRING,
443
+ "metadata_json": _json_param_type(),
444
+ "inserted_at": SPANNER_PARAM_TYPES.TIMESTAMP,
445
+ }
446
+ if include_owner and self._owner_id_column_name:
447
+ types["owner_id"] = SPANNER_PARAM_TYPES.STRING
448
+ return types
449
+
450
+ def _decode_json(self, raw: Any) -> Any:
451
+ if raw is None:
452
+ return None
453
+ if isinstance(raw, str):
454
+ return from_json(raw)
455
+ return raw
456
+
457
+ def _create_tables(self) -> None:
458
+ if not self._enabled:
459
+ return
460
+
461
+ database = self._database()
462
+ existing_tables = {t.table_id for t in database.list_tables()} # type: ignore[no-untyped-call]
463
+
464
+ ddl_statements: list[str] = []
465
+ if self._memory_table not in existing_tables:
466
+ ddl_statements.extend(run_(self._get_create_memory_table_sql)())
467
+
468
+ if ddl_statements:
469
+ database.update_ddl(ddl_statements).result(300) # type: ignore[no-untyped-call]
470
+
471
+ async def create_tables(self) -> None:
472
+ """Create tables if they don't exist."""
473
+ await async_(self._create_tables)()
474
+
475
+ async def _get_create_memory_table_sql(self) -> "list[str]":
476
+ owner_line = ""
477
+ if self._owner_id_column_ddl:
478
+ owner_line = f",\n {self._owner_id_column_ddl}"
479
+
480
+ fts_column_line = ""
481
+ fts_index = ""
482
+ if self._use_fts:
483
+ fts_column_line = "\n content_tokens TOKENLIST AS (TOKENIZE_FULLTEXT(content_text)) HIDDEN"
484
+ fts_index = f"CREATE SEARCH INDEX idx_{self._memory_table}_fts ON {self._memory_table}(content_tokens)"
485
+
486
+ shard_column = ""
487
+ pk = "PRIMARY KEY (id)"
488
+ if self._shard_count > 1:
489
+ shard_column = f",\n shard_id INT64 AS (MOD(FARM_FINGERPRINT(id), {self._shard_count})) STORED"
490
+ pk = "PRIMARY KEY (shard_id, id)"
491
+ options = ""
492
+ if self._memory_table_options:
493
+ options = f"\nOPTIONS ({self._memory_table_options})"
494
+
495
+ table_sql = f"""
496
+ CREATE TABLE {self._memory_table} (
497
+ id STRING(128) NOT NULL,
498
+ session_id STRING(128) NOT NULL,
499
+ app_name STRING(128) NOT NULL,
500
+ user_id STRING(128) NOT NULL,
501
+ event_id STRING(128) NOT NULL,
502
+ author STRING(256){owner_line},
503
+ timestamp TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true),
504
+ content_json JSON NOT NULL,
505
+ content_text STRING(MAX) NOT NULL,
506
+ metadata_json JSON,
507
+ inserted_at TIMESTAMP NOT NULL OPTIONS (allow_commit_timestamp=true){fts_column_line}{shard_column}
508
+ ) {pk}{options}{self._memory_row_deletion_policy}
509
+ """
510
+
511
+ app_user_idx = (
512
+ f"CREATE INDEX idx_{self._memory_table}_app_user_time "
513
+ f"ON {self._memory_table}(app_name, user_id, timestamp DESC)"
514
+ )
515
+ session_idx = f"CREATE INDEX idx_{self._memory_table}_session ON {self._memory_table}(session_id)"
516
+
517
+ statements = [table_sql, app_user_idx, session_idx]
518
+ if fts_index:
519
+ statements.append(fts_index)
520
+ return statements
521
+
522
+ def _get_drop_memory_table_sql(self) -> "list[str]":
523
+ """Get SQL to drop the memory table and its indexes.
524
+
525
+ Returns:
526
+ List of SQL statements to drop the memory table and associated indexes.
527
+ """
528
+ statements: list[str] = []
529
+ if self._use_fts:
530
+ statements.append(f"DROP SEARCH INDEX idx_{self._memory_table}_fts")
531
+ statements.extend([
532
+ f"DROP INDEX idx_{self._memory_table}_session",
533
+ f"DROP INDEX idx_{self._memory_table}_app_user_time",
534
+ f"DROP TABLE {self._memory_table}",
535
+ ])
536
+ return statements
537
+
538
+ def _insert_memory_entries(self, entries: "list[MemoryRecord]", owner_id: "object | None" = None) -> int:
539
+ if not self._enabled:
540
+ msg = "Memory store is disabled"
541
+ raise RuntimeError(msg)
542
+
543
+ if not entries:
544
+ return 0
545
+
546
+ inserted_count = 0
547
+ statements: list[tuple[str, dict[str, Any], dict[str, Any]]] = []
548
+
549
+ owner_column = f", {self._owner_id_column_name}" if self._owner_id_column_name else ""
550
+ owner_param = ", @owner_id" if self._owner_id_column_name else ""
551
+
552
+ insert_sql = f"""
553
+ INSERT INTO {self._memory_table} (
554
+ id, session_id, app_name, user_id, event_id, author{owner_column},
555
+ timestamp, content_json, content_text, metadata_json, inserted_at
556
+ ) VALUES (
557
+ @id, @session_id, @app_name, @user_id, @event_id, @author{owner_param},
558
+ @timestamp, @content_json, @content_text, @metadata_json, @inserted_at
559
+ )
560
+ """
561
+
562
+ for entry in entries:
563
+ if self._event_exists(entry["event_id"]):
564
+ continue
565
+ params = {
566
+ "id": entry["id"],
567
+ "session_id": entry["session_id"],
568
+ "app_name": entry["app_name"],
569
+ "user_id": entry["user_id"],
570
+ "event_id": entry["event_id"],
571
+ "author": entry["author"],
572
+ "timestamp": entry["timestamp"],
573
+ "content_json": to_json(entry["content_json"]),
574
+ "content_text": entry["content_text"],
575
+ "metadata_json": to_json(entry["metadata_json"]) if entry["metadata_json"] is not None else None,
576
+ "inserted_at": entry["inserted_at"],
577
+ }
578
+ if self._owner_id_column_name:
579
+ params["owner_id"] = str(owner_id) if owner_id is not None else None
580
+ statements.append((insert_sql, params, self._memory_param_types(self._owner_id_column_name is not None)))
581
+ inserted_count += 1
582
+
583
+ if statements:
584
+ self._run_write(statements)
585
+ return inserted_count
586
+
587
+ async def insert_memory_entries(self, entries: "list[MemoryRecord]", owner_id: "object | None" = None) -> int:
588
+ """Bulk insert memory entries with deduplication."""
589
+ return await async_(self._insert_memory_entries)(entries, owner_id)
590
+
591
+ def _event_exists(self, event_id: str) -> bool:
592
+ sql = f"SELECT event_id FROM {self._memory_table} WHERE event_id = @event_id LIMIT 1"
593
+ rows = self._run_read(sql, {"event_id": event_id}, {"event_id": SPANNER_PARAM_TYPES.STRING})
594
+ return bool(rows)
595
+
596
+ def _search_entries(
597
+ self, query: str, app_name: str, user_id: str, limit: "int | None" = None
598
+ ) -> "list[MemoryRecord]":
599
+ if not self._enabled:
600
+ msg = "Memory store is disabled"
601
+ raise RuntimeError(msg)
602
+
603
+ effective_limit = limit if limit is not None else self._max_results
604
+
605
+ if self._use_fts:
606
+ return self._search_entries_fts(query, app_name, user_id, effective_limit)
607
+ return self._search_entries_simple(query, app_name, user_id, effective_limit)
608
+
609
+ async def search_entries(
610
+ self, query: str, app_name: str, user_id: str, limit: "int | None" = None
611
+ ) -> "list[MemoryRecord]":
612
+ """Search memory entries by text query."""
613
+ return await async_(self._search_entries)(query, app_name, user_id, limit)
614
+
615
+ def _search_entries_fts(self, query: str, app_name: str, user_id: str, limit: int) -> "list[MemoryRecord]":
616
+ sql = f"""
617
+ SELECT id, session_id, app_name, user_id, event_id, author,
618
+ timestamp, content_json, content_text, metadata_json, inserted_at
619
+ FROM {self._memory_table}
620
+ WHERE app_name = @app_name
621
+ AND user_id = @user_id
622
+ AND SEARCH(content_tokens, @query)
623
+ ORDER BY timestamp DESC
624
+ LIMIT @limit
625
+ """
626
+ params = {"app_name": app_name, "user_id": user_id, "query": query, "limit": limit}
627
+ types = {
628
+ "app_name": SPANNER_PARAM_TYPES.STRING,
629
+ "user_id": SPANNER_PARAM_TYPES.STRING,
630
+ "query": SPANNER_PARAM_TYPES.STRING,
631
+ "limit": SPANNER_PARAM_TYPES.INT64,
632
+ }
633
+ rows = self._run_read(sql, params, types)
634
+ return self._rows_to_records(rows)
635
+
636
+ def _search_entries_simple(self, query: str, app_name: str, user_id: str, limit: int) -> "list[MemoryRecord]":
637
+ sql = f"""
638
+ SELECT id, session_id, app_name, user_id, event_id, author,
639
+ timestamp, content_json, content_text, metadata_json, inserted_at
640
+ FROM {self._memory_table}
641
+ WHERE app_name = @app_name
642
+ AND user_id = @user_id
643
+ AND LOWER(content_text) LIKE @pattern
644
+ ORDER BY timestamp DESC
645
+ LIMIT @limit
646
+ """
647
+ pattern = f"%{query.lower()}%"
648
+ params = {"app_name": app_name, "user_id": user_id, "pattern": pattern, "limit": limit}
649
+ types = {
650
+ "app_name": SPANNER_PARAM_TYPES.STRING,
651
+ "user_id": SPANNER_PARAM_TYPES.STRING,
652
+ "pattern": SPANNER_PARAM_TYPES.STRING,
653
+ "limit": SPANNER_PARAM_TYPES.INT64,
654
+ }
655
+ rows = self._run_read(sql, params, types)
656
+ return self._rows_to_records(rows)
657
+
658
+ def _delete_entries_by_session(self, session_id: str) -> int:
659
+ sql = f"DELETE FROM {self._memory_table} WHERE session_id = @session_id"
660
+ params = {"session_id": session_id}
661
+ types = {"session_id": SPANNER_PARAM_TYPES.STRING}
662
+ return self._execute_update(sql, params, types)
663
+
664
+ async def delete_entries_by_session(self, session_id: str) -> int:
665
+ """Delete all memory entries for a specific session."""
666
+ return await async_(self._delete_entries_by_session)(session_id)
667
+
668
+ def _delete_entries_older_than(self, days: int) -> int:
669
+ cutoff = datetime.now(timezone.utc) - timedelta(days=days)
670
+ sql = f"DELETE FROM {self._memory_table} WHERE inserted_at < @cutoff"
671
+ params = {"cutoff": cutoff}
672
+ types = {"cutoff": SPANNER_PARAM_TYPES.TIMESTAMP}
673
+ return self._execute_update(sql, params, types)
674
+
675
+ async def delete_entries_older_than(self, days: int) -> int:
676
+ """Delete memory entries older than specified days."""
677
+ return await async_(self._delete_entries_older_than)(days)
678
+
679
+ def _rows_to_records(self, rows: "list[Any]") -> "list[MemoryRecord]":
680
+ return [
681
+ {
682
+ "id": row[0],
683
+ "session_id": row[1],
684
+ "app_name": row[2],
685
+ "user_id": row[3],
686
+ "event_id": row[4],
687
+ "author": row[5],
688
+ "timestamp": row[6],
689
+ "content_json": self._decode_json(row[7]),
690
+ "content_text": row[8],
691
+ "metadata_json": self._decode_json(row[9]),
692
+ "inserted_at": row[10],
693
+ }
694
+ for row in rows
695
+ ]
696
+
697
+
698
+ def _json_param_type() -> Any:
699
+ try:
700
+ return SPANNER_PARAM_TYPES.JSON
701
+ except AttributeError:
702
+ return SPANNER_PARAM_TYPES.STRING
703
+
704
+
705
+ def _spanner_ttl_days(ttl_seconds: Any) -> int:
706
+ if not isinstance(ttl_seconds, int) or ttl_seconds <= 0:
707
+ return 0
708
+ return max(1, (ttl_seconds + 86_399) // 86_400)
709
+
710
+
711
+ def _spanner_row_deletion_policy(adk_config: dict[str, Any], ttl_key: str, column: str) -> str:
712
+ retention = adk_config.get("retention")
713
+ if not isinstance(retention, dict):
714
+ return ""
715
+ ttl_days = _spanner_ttl_days(retention.get(ttl_key))
716
+ if ttl_days == 0:
717
+ return ""
718
+ return f"\nROW DELETION POLICY (OLDER_THAN({column}, INTERVAL {ttl_days} DAY))"
719
+
720
+
721
+ class _SpannerWriteJob:
722
+ __slots__ = ("_statements",)
723
+
724
+ def __init__(self, statements: "list[tuple[str, dict[str, Any], dict[str, Any]]]") -> None:
725
+ self._statements = statements
726
+
727
+ def __call__(self, transaction: "Transaction") -> None:
728
+ for sql, params, types in self._statements:
729
+ transaction.execute_update(sql, params=params, param_types=types) # type: ignore[no-untyped-call]
730
+
731
+
732
+ class _SpannerMemoryWriteJob:
733
+ __slots__ = ("_statements",)
734
+
735
+ def __init__(self, statements: "list[tuple[str, dict[str, Any], dict[str, Any]]]") -> None:
736
+ self._statements = statements
737
+
738
+ def __call__(self, transaction: "Transaction") -> None:
739
+ for sql, params, types in self._statements:
740
+ transaction.execute_update(sql, params=params, param_types=types) # type: ignore[no-untyped-call]
741
+
742
+
743
+ class _SpannerMemoryUpdateJob:
744
+ __slots__ = ("_params", "_sql", "_types")
745
+
746
+ def __init__(self, sql: str, params: "dict[str, Any]", types: "dict[str, Any]") -> None:
747
+ self._sql = sql
748
+ self._params = params
749
+ self._types = types
750
+
751
+ def __call__(self, transaction: "Transaction") -> int:
752
+ return int(transaction.execute_update(self._sql, params=self._params, param_types=self._types)) # type: ignore[no-untyped-call]
753
+
754
+
755
+ class _SpannerReadProtocol(Protocol):
756
+ def execute_sql(
757
+ self, sql: str, params: "dict[str, Any] | None" = None, param_types: "dict[str, Any] | None" = None
758
+ ) -> Iterable[Any]: ...