sqlspec 0.64.0__py3-none-any.whl → 0.65.0__py3-none-any.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 (219) hide show
  1. sqlspec/_typing.py +2 -0
  2. sqlspec/adapters/adbc/_typing.py +1 -2
  3. sqlspec/adapters/adbc/adk/store.py +130 -59
  4. sqlspec/adapters/adbc/config.py +7 -0
  5. sqlspec/adapters/adbc/core.py +42 -14
  6. sqlspec/adapters/adbc/driver.py +10 -4
  7. sqlspec/adapters/aiomysql/_typing.py +2 -0
  8. sqlspec/adapters/aiomysql/adk/store.py +7 -4
  9. sqlspec/adapters/aiomysql/config.py +38 -11
  10. sqlspec/adapters/aiomysql/core.py +45 -520
  11. sqlspec/adapters/aiomysql/data_dictionary.py +4 -290
  12. sqlspec/adapters/aiomysql/driver.py +16 -14
  13. sqlspec/adapters/aiosqlite/__init__.py +2 -0
  14. sqlspec/adapters/aiosqlite/_typing.py +1 -2
  15. sqlspec/adapters/aiosqlite/adk/store.py +105 -76
  16. sqlspec/adapters/aiosqlite/config.py +27 -61
  17. sqlspec/adapters/aiosqlite/core.py +155 -16
  18. sqlspec/adapters/aiosqlite/data_dictionary.py +0 -1
  19. sqlspec/adapters/aiosqlite/driver.py +82 -38
  20. sqlspec/adapters/aiosqlite/events/store.py +4 -3
  21. sqlspec/adapters/aiosqlite/litestar/store.py +16 -13
  22. sqlspec/adapters/aiosqlite/pool.py +80 -77
  23. sqlspec/adapters/arrow_odbc/_typing.py +1 -2
  24. sqlspec/adapters/arrow_odbc/adk/store.py +511 -168
  25. sqlspec/adapters/arrow_odbc/config.py +15 -2
  26. sqlspec/adapters/arrow_odbc/core.py +229 -19
  27. sqlspec/adapters/arrow_odbc/data_dictionary.py +10 -9
  28. sqlspec/adapters/arrow_odbc/driver.py +210 -85
  29. sqlspec/adapters/arrow_odbc/events/store.py +43 -18
  30. sqlspec/adapters/arrow_odbc/litestar/store.py +222 -54
  31. sqlspec/adapters/arrow_odbc/type_converter.py +9 -0
  32. sqlspec/adapters/asyncmy/_typing.py +2 -2
  33. sqlspec/adapters/asyncmy/adk/store.py +7 -4
  34. sqlspec/adapters/asyncmy/config.py +30 -11
  35. sqlspec/adapters/asyncmy/core.py +46 -536
  36. sqlspec/adapters/asyncmy/data_dictionary.py +4 -290
  37. sqlspec/adapters/asyncmy/driver.py +10 -10
  38. sqlspec/adapters/asyncpg/_typing.py +3 -5
  39. sqlspec/adapters/asyncpg/adk/store.py +13 -5
  40. sqlspec/adapters/asyncpg/config.py +32 -13
  41. sqlspec/adapters/asyncpg/core.py +26 -12
  42. sqlspec/adapters/asyncpg/driver.py +26 -12
  43. sqlspec/adapters/asyncpg/litestar/store.py +17 -10
  44. sqlspec/adapters/bigquery/_typing.py +9 -15
  45. sqlspec/adapters/bigquery/config.py +24 -7
  46. sqlspec/adapters/bigquery/core.py +16 -26
  47. sqlspec/adapters/bigquery/driver.py +80 -58
  48. sqlspec/adapters/bigquery/litestar/store.py +19 -5
  49. sqlspec/adapters/cockroach_asyncpg/_typing.py +3 -5
  50. sqlspec/adapters/cockroach_asyncpg/config.py +4 -2
  51. sqlspec/adapters/cockroach_asyncpg/core.py +27 -0
  52. sqlspec/adapters/cockroach_psycopg/_typing.py +3 -5
  53. sqlspec/adapters/cockroach_psycopg/adk/store.py +36 -9
  54. sqlspec/adapters/cockroach_psycopg/config.py +6 -29
  55. sqlspec/adapters/cockroach_psycopg/core.py +46 -0
  56. sqlspec/adapters/cockroach_psycopg/driver.py +16 -5
  57. sqlspec/adapters/db2/__init__.py +31 -0
  58. sqlspec/adapters/db2/_typing.py +311 -0
  59. sqlspec/adapters/db2/adk/__init__.py +10 -0
  60. sqlspec/adapters/db2/adk/store.py +1334 -0
  61. sqlspec/adapters/db2/config.py +460 -0
  62. sqlspec/adapters/db2/core.py +753 -0
  63. sqlspec/adapters/db2/data_dictionary.py +478 -0
  64. sqlspec/adapters/db2/driver.py +674 -0
  65. sqlspec/adapters/db2/events/__init__.py +6 -0
  66. sqlspec/adapters/db2/events/config.py +9 -0
  67. sqlspec/adapters/db2/events/store.py +67 -0
  68. sqlspec/adapters/db2/litestar/__init__.py +6 -0
  69. sqlspec/adapters/db2/litestar/config.py +12 -0
  70. sqlspec/adapters/db2/litestar/store.py +465 -0
  71. sqlspec/adapters/db2/migrations.py +191 -0
  72. sqlspec/adapters/db2/pool.py +564 -0
  73. sqlspec/adapters/duckdb/_typing.py +1 -2
  74. sqlspec/adapters/duckdb/adk/store.py +30 -15
  75. sqlspec/adapters/duckdb/config.py +23 -42
  76. sqlspec/adapters/duckdb/core.py +26 -2
  77. sqlspec/adapters/duckdb/driver.py +38 -29
  78. sqlspec/adapters/duckdb/litestar/store.py +3 -3
  79. sqlspec/adapters/mssql_python/adk/store.py +39 -17
  80. sqlspec/adapters/mssql_python/config.py +1 -0
  81. sqlspec/adapters/mssql_python/core.py +55 -17
  82. sqlspec/adapters/mssql_python/data_dictionary.py +1 -41
  83. sqlspec/adapters/mssql_python/driver.py +93 -15
  84. sqlspec/adapters/mssql_python/events/store.py +5 -24
  85. sqlspec/adapters/mssql_python/litestar/store.py +18 -47
  86. sqlspec/adapters/mssql_python/pool.py +3 -2
  87. sqlspec/adapters/mysql_common.py +502 -0
  88. sqlspec/adapters/mysqlconnector/_typing.py +6 -0
  89. sqlspec/adapters/mysqlconnector/adk/store.py +14 -8
  90. sqlspec/adapters/mysqlconnector/config.py +92 -20
  91. sqlspec/adapters/mysqlconnector/core.py +56 -461
  92. sqlspec/adapters/mysqlconnector/data_dictionary.py +6 -549
  93. sqlspec/adapters/mysqlconnector/driver.py +23 -16
  94. sqlspec/adapters/oracledb/__init__.py +7 -2
  95. sqlspec/adapters/oracledb/_json_handlers.py +46 -16
  96. sqlspec/adapters/oracledb/_storage.py +6 -4
  97. sqlspec/adapters/oracledb/adk/store.py +237 -268
  98. sqlspec/adapters/oracledb/config.py +34 -32
  99. sqlspec/adapters/oracledb/core.py +88 -87
  100. sqlspec/adapters/oracledb/driver.py +36 -13
  101. sqlspec/adapters/oracledb/events/store.py +10 -10
  102. sqlspec/adapters/oracledb/litestar/store.py +121 -202
  103. sqlspec/adapters/oracledb/migrations.py +1 -5
  104. sqlspec/adapters/oracledb/type_converter.py +10 -7
  105. sqlspec/adapters/psqlpy/_typing.py +2 -1
  106. sqlspec/adapters/psqlpy/adk/store.py +73 -39
  107. sqlspec/adapters/psqlpy/config.py +22 -12
  108. sqlspec/adapters/psqlpy/core.py +11 -5
  109. sqlspec/adapters/psqlpy/litestar/store.py +20 -18
  110. sqlspec/adapters/psqlpy/type_converter.py +17 -3
  111. sqlspec/adapters/psycopg/_typing.py +5 -2
  112. sqlspec/adapters/psycopg/adk/store.py +60 -18
  113. sqlspec/adapters/psycopg/config.py +40 -16
  114. sqlspec/adapters/psycopg/driver.py +36 -40
  115. sqlspec/adapters/psycopg/type_converter.py +2 -6
  116. sqlspec/adapters/pymssql/adk/store.py +18 -25
  117. sqlspec/adapters/pymssql/config.py +3 -0
  118. sqlspec/adapters/pymssql/core.py +31 -16
  119. sqlspec/adapters/pymssql/data_dictionary.py +1 -41
  120. sqlspec/adapters/pymssql/driver.py +4 -3
  121. sqlspec/adapters/pymssql/events/store.py +5 -24
  122. sqlspec/adapters/pymssql/litestar/store.py +18 -47
  123. sqlspec/adapters/pymssql/pool.py +2 -6
  124. sqlspec/adapters/pymysql/_typing.py +12 -25
  125. sqlspec/adapters/pymysql/adk/store.py +7 -4
  126. sqlspec/adapters/pymysql/config.py +3 -5
  127. sqlspec/adapters/pymysql/core.py +44 -464
  128. sqlspec/adapters/pymysql/data_dictionary.py +5 -289
  129. sqlspec/adapters/pymysql/driver.py +13 -8
  130. sqlspec/adapters/pymysql/litestar/store.py +1 -6
  131. sqlspec/adapters/pymysql/pool.py +16 -8
  132. sqlspec/adapters/spanner/adk/store.py +25 -22
  133. sqlspec/adapters/spanner/config.py +22 -10
  134. sqlspec/adapters/spanner/core.py +2 -7
  135. sqlspec/adapters/spanner/data_dictionary.py +132 -18
  136. sqlspec/adapters/spanner/driver.py +56 -18
  137. sqlspec/adapters/spanner/type_converter.py +43 -21
  138. sqlspec/adapters/sqlite/__init__.py +2 -0
  139. sqlspec/adapters/sqlite/_typing.py +1 -2
  140. sqlspec/adapters/sqlite/adk/store.py +78 -75
  141. sqlspec/adapters/sqlite/config.py +28 -63
  142. sqlspec/adapters/sqlite/core.py +103 -28
  143. sqlspec/adapters/sqlite/data_dictionary.py +0 -2
  144. sqlspec/adapters/sqlite/driver.py +88 -45
  145. sqlspec/adapters/sqlite/events/store.py +4 -3
  146. sqlspec/adapters/sqlite/litestar/store.py +12 -12
  147. sqlspec/adapters/sqlite/pool.py +74 -80
  148. sqlspec/builder/_base.py +67 -52
  149. sqlspec/builder/_ddl.py +25 -10
  150. sqlspec/builder/_dml.py +18 -7
  151. sqlspec/builder/_factory.py +3 -3
  152. sqlspec/builder/_merge.py +104 -4
  153. sqlspec/builder/_parsing_utils.py +37 -2
  154. sqlspec/builder/_select.py +1 -1
  155. sqlspec/core/query_modifiers.py +2 -2
  156. sqlspec/core/splitter.py +9 -0
  157. sqlspec/core/statement.py +8 -9
  158. sqlspec/data_dictionary/_registry.py +3 -0
  159. sqlspec/data_dictionary/_types.py +9 -1
  160. sqlspec/data_dictionary/dialects/__init__.py +2 -0
  161. sqlspec/data_dictionary/dialects/db2/__init__.py +31 -0
  162. sqlspec/data_dictionary/dialects/db2/config.py +172 -0
  163. sqlspec/data_dictionary/dialects/db2/sql/columns.sql +66 -0
  164. sqlspec/data_dictionary/dialects/db2/sql/constraints.sql +49 -0
  165. sqlspec/data_dictionary/dialects/db2/sql/foreign_keys.sql +43 -0
  166. sqlspec/data_dictionary/dialects/db2/sql/indexes.sql +39 -0
  167. sqlspec/data_dictionary/dialects/db2/sql/objects.sql +53 -0
  168. sqlspec/data_dictionary/dialects/db2/sql/schemas.sql +17 -0
  169. sqlspec/data_dictionary/dialects/db2/sql/tables.sql +87 -0
  170. sqlspec/data_dictionary/dialects/db2/sql/version.sql +8 -0
  171. sqlspec/data_dictionary/dialects/db2/sql/views.sql +15 -0
  172. sqlspec/data_dictionary/dialects/mssql/__init__.py +2 -0
  173. sqlspec/data_dictionary/dialects/mssql/config.py +41 -1
  174. sqlspec/data_dictionary/dialects/mysql/__init__.py +3 -0
  175. sqlspec/data_dictionary/dialects/mysql/dictionary.py +583 -0
  176. sqlspec/data_dictionary/dialects/spanner/config.py +2 -0
  177. sqlspec/data_dictionary/dialects/spanner/sql/foreign_keys.sql +23 -2
  178. sqlspec/data_dictionary/dialects/spanner/sql/googlesql/columns.sql +39 -16
  179. sqlspec/data_dictionary/dialects/spanner/sql/googlesql/constraints.sql +32 -0
  180. sqlspec/data_dictionary/dialects/spanner/sql/googlesql/foreign_keys.sql +91 -0
  181. sqlspec/data_dictionary/dialects/spanner/sql/googlesql/indexes.sql +36 -3
  182. sqlspec/data_dictionary/dialects/spanner/sql/googlesql/tables.sql +23 -8
  183. sqlspec/data_dictionary/dialects/spanner/sql/postgresql/change_streams.sql +28 -0
  184. sqlspec/data_dictionary/dialects/spanner/sql/postgresql/columns.sql +34 -0
  185. sqlspec/data_dictionary/dialects/spanner/sql/postgresql/constraints.sql +63 -0
  186. sqlspec/data_dictionary/dialects/spanner/sql/postgresql/foreign_keys.sql +91 -0
  187. sqlspec/data_dictionary/dialects/spanner/sql/postgresql/indexes.sql +52 -0
  188. sqlspec/data_dictionary/dialects/spanner/sql/postgresql/sequences.sql +14 -0
  189. sqlspec/data_dictionary/dialects/spanner/sql/postgresql/system.sql +27 -0
  190. sqlspec/data_dictionary/dialects/spanner/sql/postgresql/tables.sql +22 -0
  191. sqlspec/dialects/__init__.py +3 -1
  192. sqlspec/dialects/db2/__init__.py +110 -0
  193. sqlspec/dialects/db2/_generators.py +352 -0
  194. sqlspec/dialects/db2/_parsers.py +558 -0
  195. sqlspec/dialects/db2/_transforms.py +164 -0
  196. sqlspec/dialects/spanner/_expressions.py +78 -0
  197. sqlspec/dialects/spanner/_generators.py +481 -22
  198. sqlspec/dialects/spanner/_parsers.py +393 -57
  199. sqlspec/dialects/spanner/_spangres.py +19 -22
  200. sqlspec/dialects/spanner/_spanner.py +27 -23
  201. sqlspec/driver/_common.py +7 -7
  202. sqlspec/extensions/adk/memory/converters.py +8 -8
  203. sqlspec/extensions/adk/memory/service.py +99 -3
  204. sqlspec/extensions/adk/service.py +9 -11
  205. sqlspec/extensions/events/_queue.py +3 -1
  206. sqlspec/extensions/events/primitives.py +11 -2
  207. sqlspec/migrations/base.py +61 -59
  208. sqlspec/migrations/schema.py +56 -24
  209. sqlspec/migrations/tracker.py +3 -6
  210. sqlspec/observability/_common.py +1 -0
  211. sqlspec/protocols.py +1 -0
  212. sqlspec/typing.py +2 -0
  213. sqlspec/utils/fixtures.py +284 -114
  214. sqlspec/utils/text.py +1 -1
  215. {sqlspec-0.64.0.dist-info → sqlspec-0.65.0.dist-info}/METADATA +3 -1
  216. {sqlspec-0.64.0.dist-info → sqlspec-0.65.0.dist-info}/RECORD +219 -176
  217. {sqlspec-0.64.0.dist-info → sqlspec-0.65.0.dist-info}/WHEEL +1 -1
  218. {sqlspec-0.64.0.dist-info → sqlspec-0.65.0.dist-info}/entry_points.txt +1 -0
  219. {sqlspec-0.64.0.dist-info → sqlspec-0.65.0.dist-info}/licenses/LICENSE +0 -0
sqlspec/_typing.py CHANGED
@@ -50,6 +50,7 @@ __all__ = (
50
50
  "CATTRS_INSTALLED",
51
51
  "CLOUD_SQL_CONNECTOR_INSTALLED",
52
52
  "FSSPEC_INSTALLED",
53
+ "IBM_DB_INSTALLED",
53
54
  "LITESTAR_INSTALLED",
54
55
  "MSGSPEC_INSTALLED",
55
56
  "NANOID_INSTALLED",
@@ -666,6 +667,7 @@ ATTRS_INSTALLED = dependency_flag("attrs")
666
667
  CATTRS_INSTALLED = dependency_flag("cattrs")
667
668
  CLOUD_SQL_CONNECTOR_INSTALLED = dependency_flag("google.cloud.sql.connector")
668
669
  FSSPEC_INSTALLED = dependency_flag("fsspec")
670
+ IBM_DB_INSTALLED = dependency_flag("ibm_db")
669
671
  LITESTAR_INSTALLED = dependency_flag("litestar")
670
672
  MSGSPEC_INSTALLED = dependency_flag("msgspec")
671
673
  NUMPY_INSTALLED = dependency_flag("numpy")
@@ -31,8 +31,7 @@ if TYPE_CHECKING:
31
31
  AdbcConnection: TypeAlias = _AdbcConnection
32
32
  AdbcRawCursor: TypeAlias = _AdbcRawCursor
33
33
  AdbcNativeError: TypeAlias = _AdbcNativeError
34
-
35
- if not TYPE_CHECKING:
34
+ else:
36
35
  AdbcConnection = _AdbcConnection
37
36
  AdbcRawCursor = _AdbcRawCursor
38
37
  AdbcNativeError = _AdbcNativeError
@@ -1,10 +1,16 @@
1
1
  """ADBC ADK store for Google Agent Development Kit session/event storage."""
2
2
 
3
3
  import contextlib
4
- import re
5
4
  from datetime import datetime, timedelta, timezone
5
+ from functools import lru_cache
6
6
  from typing import TYPE_CHECKING, Any, Final, Literal
7
7
 
8
+ from sqlspec.adapters.adbc.core import (
9
+ driver_kind_from_driver_name,
10
+ is_shared_object_driver,
11
+ resolve_dialect_from_config,
12
+ )
13
+ from sqlspec.core.parameters._validator import PARAMETER_REGEX
8
14
  from sqlspec.extensions.adk import BaseSyncADKStore, StoredEvent, StoredSession, normalize_session_list_options
9
15
  from sqlspec.extensions.adk.memory.store import BaseSyncADKMemoryStore
10
16
  from sqlspec.utils.logging import get_logger
@@ -36,6 +42,21 @@ ADBC_TABLE_NOT_FOUND_PATTERNS: Final = (
36
42
  )
37
43
 
38
44
 
45
+ @lru_cache(maxsize=256)
46
+ def _postgresql_store_sql(sql: str) -> str:
47
+ """Cache positional SQL conversion without changing quoted or commented text."""
48
+ segments: list[str] = []
49
+ last_end = 0
50
+ index = 0
51
+ for match in PARAMETER_REGEX.finditer(sql):
52
+ if match.lastgroup == "qmark":
53
+ index += 1
54
+ segments.extend((sql[last_end : match.start()], f"${index}"))
55
+ last_end = match.end()
56
+ segments.append(sql[last_end:])
57
+ return "".join(segments)
58
+
59
+
39
60
  class AdbcADKStore(BaseSyncADKStore["AdbcConfig"]):
40
61
  """ADBC synchronous ADK store for Arrow Database Connectivity.
41
62
 
@@ -192,15 +213,28 @@ class AdbcADKStore(BaseSyncADKStore["AdbcConfig"]):
192
213
  Returns:
193
214
  Dialect identifier for DDL generation.
194
215
  """
195
- driver_name = self._config.connection_config.get("driver_name", "").lower()
196
-
197
- if "postgres" in driver_name:
216
+ driver_name = self._config.connection_config.get("driver_name", "")
217
+ if (
218
+ isinstance(driver_name, str)
219
+ and driver_name
220
+ and driver_kind_from_driver_name(driver_name) is None
221
+ and not is_shared_object_driver(driver_name)
222
+ ):
223
+ logger.warning(
224
+ "Unknown ADBC driver: %s. Using generic SQL dialect. "
225
+ "Consider using a direct adapter for better performance.",
226
+ driver_name,
227
+ )
228
+ return DIALECT_GENERIC
229
+
230
+ resolved = resolve_dialect_from_config(self._config.connection_config)
231
+ if resolved == "postgres":
198
232
  return DIALECT_POSTGRESQL
199
- if "sqlite" in driver_name:
233
+ if resolved == "sqlite":
200
234
  return DIALECT_SQLITE
201
- if "duckdb" in driver_name:
235
+ if resolved == "duckdb":
202
236
  return DIALECT_DUCKDB
203
- if "snowflake" in driver_name:
237
+ if resolved == "snowflake":
204
238
  return DIALECT_SNOWFLAKE
205
239
 
206
240
  logger.warning(
@@ -605,14 +639,7 @@ class AdbcADKStore(BaseSyncADKStore["AdbcConfig"]):
605
639
  """Return SQL with dialect-appropriate positional placeholders."""
606
640
  if self._dialect != DIALECT_POSTGRESQL:
607
641
  return sql
608
- index = 0
609
-
610
- def replace_placeholder(_match: Any) -> str:
611
- nonlocal index
612
- index += 1
613
- return f"${index}"
614
-
615
- return re.sub(r"\?", replace_placeholder, sql)
642
+ return _postgresql_store_sql(sql)
616
643
 
617
644
  def _execute(self, cursor: Any, sql: str, params: "tuple[Any, ...] | list[Any]") -> Any:
618
645
  """Execute parameterized SQL using the current ADBC dialect's placeholder style."""
@@ -961,37 +988,41 @@ class AdbcADKStore(BaseSyncADKStore["AdbcConfig"]):
961
988
  with self._config.provide_connection() as conn:
962
989
  cursor = conn.cursor()
963
990
  try:
964
- self._execute(
965
- cursor,
966
- insert_sql,
967
- (
968
- event_record["id"],
969
- event_record["app_name"],
970
- event_record["user_id"],
971
- event_record["session_id"],
972
- event_record["invocation_id"],
973
- event_record["timestamp"],
974
- event_data,
975
- ),
976
- )
977
991
  self._execute(cursor, update_sql, (state_json, app_name, user_id, session_id))
978
- if app_state is not None:
979
- self._execute(cursor, delete_app_state_sql, (app_name,))
980
- self._execute(
981
- cursor,
982
- insert_app_state_sql,
983
- (app_name, self._serialize_state(app_state), datetime.now(timezone.utc)),
984
- )
985
- if user_state is not None:
986
- self._execute(cursor, delete_user_state_sql, (app_name, user_id))
992
+ self._execute(cursor, select_sql, (app_name, user_id, session_id))
993
+ row = cursor.fetchone()
994
+ if row is not None:
987
995
  self._execute(
988
996
  cursor,
989
- insert_user_state_sql,
990
- (app_name, user_id, self._serialize_state(user_state), datetime.now(timezone.utc)),
997
+ insert_sql,
998
+ (
999
+ event_record["id"],
1000
+ event_record["app_name"],
1001
+ event_record["user_id"],
1002
+ event_record["session_id"],
1003
+ event_record["invocation_id"],
1004
+ event_record["timestamp"],
1005
+ event_data,
1006
+ ),
991
1007
  )
992
- self._execute(cursor, select_sql, (app_name, user_id, session_id))
993
- row = cursor.fetchone()
994
- conn.commit()
1008
+ if app_state is not None:
1009
+ self._execute(cursor, delete_app_state_sql, (app_name,))
1010
+ self._execute(
1011
+ cursor,
1012
+ insert_app_state_sql,
1013
+ (app_name, self._serialize_state(app_state), datetime.now(timezone.utc)),
1014
+ )
1015
+ if user_state is not None:
1016
+ self._execute(cursor, delete_user_state_sql, (app_name, user_id))
1017
+ self._execute(
1018
+ cursor,
1019
+ insert_user_state_sql,
1020
+ (app_name, user_id, self._serialize_state(user_state), datetime.now(timezone.utc)),
1021
+ )
1022
+ conn.commit()
1023
+ else:
1024
+ with contextlib.suppress(Exception):
1025
+ conn.rollback()
995
1026
  except Exception:
996
1027
  with contextlib.suppress(Exception):
997
1028
  conn.rollback()
@@ -1277,6 +1308,9 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1277
1308
 
1278
1309
  def create_tables(self) -> None:
1279
1310
  """Create tables if they don't exist."""
1311
+ if not self._enabled:
1312
+ return
1313
+
1280
1314
  if not self.create_schema_enabled:
1281
1315
  self.reconcile_schema()
1282
1316
  return
@@ -1308,18 +1342,43 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1308
1342
  return self._delete_entries_older_than(days, app_name, scope)
1309
1343
 
1310
1344
  def _detect_dialect(self) -> str:
1311
- driver_name = self._config.connection_config.get("driver_name", "").lower()
1312
- if "postgres" in driver_name:
1345
+ driver_name = self._config.connection_config.get("driver_name", "")
1346
+ if (
1347
+ isinstance(driver_name, str)
1348
+ and driver_name
1349
+ and driver_kind_from_driver_name(driver_name) is None
1350
+ and not is_shared_object_driver(driver_name)
1351
+ ):
1352
+ logger.warning("Unknown ADBC driver: %s. Using generic SQL dialect.", driver_name)
1353
+ return DIALECT_GENERIC
1354
+
1355
+ resolved = resolve_dialect_from_config(self._config.connection_config)
1356
+ if resolved == "postgres":
1313
1357
  return DIALECT_POSTGRESQL
1314
- if "sqlite" in driver_name:
1358
+ if resolved == "sqlite":
1315
1359
  return DIALECT_SQLITE
1316
- if "duckdb" in driver_name:
1360
+ if resolved == "duckdb":
1317
1361
  return DIALECT_DUCKDB
1318
- if "snowflake" in driver_name:
1362
+ if resolved == "snowflake":
1319
1363
  return DIALECT_SNOWFLAKE
1320
1364
  logger.warning("Unknown ADBC driver: %s. Using generic SQL dialect.", driver_name)
1321
1365
  return DIALECT_GENERIC
1322
1366
 
1367
+ def _format_sql(self, sql: str) -> str:
1368
+ """Return SQL with dialect-appropriate positional placeholders."""
1369
+ if self._dialect != DIALECT_POSTGRESQL:
1370
+ return sql
1371
+ return _postgresql_store_sql(sql)
1372
+
1373
+ def _execute(self, cursor: Any, sql: str, params: "tuple[Any, ...] | list[Any]") -> Any:
1374
+ """Execute parameterized SQL using the current ADBC dialect's placeholder style."""
1375
+ return cursor.execute(self._format_sql(sql), params)
1376
+
1377
+ def _json_placeholder(self) -> str:
1378
+ if self._dialect == DIALECT_POSTGRESQL:
1379
+ return "?::jsonb"
1380
+ return "?"
1381
+
1323
1382
  def _serialize_json_field(self, value: Any) -> "str | None":
1324
1383
  if value is None:
1325
1384
  return None
@@ -1484,6 +1543,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1484
1543
 
1485
1544
  inserted_count = 0
1486
1545
  use_returning = self._dialect in {DIALECT_SQLITE, DIALECT_POSTGRESQL, DIALECT_DUCKDB}
1546
+ json_placeholder = self._json_placeholder()
1487
1547
 
1488
1548
  if self._owner_id_column_name:
1489
1549
  if use_returning:
@@ -1493,7 +1553,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1493
1553
  {self._owner_id_column_name}, timestamp, content_json, content_text,
1494
1554
  metadata_json, inserted_at
1495
1555
  ) VALUES (
1496
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
1556
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, {json_placeholder}, ?, {json_placeholder}, ?
1497
1557
  ) ON CONFLICT(event_id) DO NOTHING RETURNING 1
1498
1558
  """
1499
1559
  else:
@@ -1503,7 +1563,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1503
1563
  {self._owner_id_column_name}, timestamp, content_json, content_text,
1504
1564
  metadata_json, inserted_at
1505
1565
  ) VALUES (
1506
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
1566
+ ?, ?, ?, ?, ?, ?, ?, ?, ?, {json_placeholder}, ?, {json_placeholder}, ?
1507
1567
  )
1508
1568
  """
1509
1569
  elif use_returning:
@@ -1512,7 +1572,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1512
1572
  id, session_id, app_name, user_id, scope, event_id, author,
1513
1573
  timestamp, content_json, content_text, metadata_json, inserted_at
1514
1574
  ) VALUES (
1515
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
1575
+ ?, ?, ?, ?, ?, ?, ?, ?, {json_placeholder}, ?, {json_placeholder}, ?
1516
1576
  ) ON CONFLICT(event_id) DO NOTHING RETURNING 1
1517
1577
  """
1518
1578
  else:
@@ -1521,7 +1581,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1521
1581
  id, session_id, app_name, user_id, scope, event_id, author,
1522
1582
  timestamp, content_json, content_text, metadata_json, inserted_at
1523
1583
  ) VALUES (
1524
- ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?
1584
+ ?, ?, ?, ?, ?, ?, ?, ?, {json_placeholder}, ?, {json_placeholder}, ?
1525
1585
  )
1526
1586
  """
1527
1587
 
@@ -1530,7 +1590,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1530
1590
  try:
1531
1591
  for entry in entries:
1532
1592
  content_json = self._serialize_json_field(entry["content_json"])
1533
- metadata_json = self._serialize_json_field(entry["metadata_json"])
1593
+ metadata_json = self._serialize_json_field(entry.get("metadata_json"))
1534
1594
  params: tuple[Any, ...]
1535
1595
  if self._owner_id_column_name:
1536
1596
  params = (
@@ -1540,7 +1600,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1540
1600
  entry["user_id"],
1541
1601
  entry.get("scope", "user"),
1542
1602
  entry["event_id"],
1543
- entry["author"],
1603
+ entry.get("author"),
1544
1604
  owner_id,
1545
1605
  self._encode_timestamp(entry["timestamp"]),
1546
1606
  content_json,
@@ -1556,7 +1616,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1556
1616
  entry["user_id"],
1557
1617
  entry.get("scope", "user"),
1558
1618
  entry["event_id"],
1559
- entry["author"],
1619
+ entry.get("author"),
1560
1620
  self._encode_timestamp(entry["timestamp"]),
1561
1621
  content_json,
1562
1622
  entry["content_text"],
@@ -1564,12 +1624,12 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1564
1624
  self._encode_timestamp(entry["inserted_at"]),
1565
1625
  )
1566
1626
  if use_returning:
1567
- cursor.execute(sql, params)
1627
+ self._execute(cursor, sql, params)
1568
1628
  if cursor.fetchone():
1569
1629
  inserted_count += 1
1570
1630
  else:
1571
1631
  try:
1572
- cursor.execute(sql, params)
1632
+ self._execute(cursor, sql, params)
1573
1633
  inserted_count += 1
1574
1634
  except Exception as exc:
1575
1635
  exc_str = str(exc).lower()
@@ -1594,6 +1654,9 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1594
1654
  msg = "Memory store is disabled"
1595
1655
  raise RuntimeError(msg)
1596
1656
 
1657
+ if not query or not query.strip():
1658
+ return []
1659
+
1597
1660
  if self._use_fts:
1598
1661
  logger.warning("ADBC memory store does not support FTS, falling back to simple search")
1599
1662
 
@@ -1615,7 +1678,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1615
1678
  with self._config.provide_connection() as conn:
1616
1679
  cursor = conn.cursor()
1617
1680
  try:
1618
- cursor.execute(sql, (*scope_params, pattern, effective_limit))
1681
+ self._execute(cursor, sql, (*scope_params, pattern, effective_limit))
1619
1682
  rows = cursor.fetchall()
1620
1683
  finally:
1621
1684
  cursor.close()
@@ -1628,6 +1691,10 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1628
1691
  return self._rows_to_records(rows)
1629
1692
 
1630
1693
  def _delete_entries_by_session(self, session_id: str) -> int:
1694
+ if not self._enabled:
1695
+ msg = "Memory store is disabled"
1696
+ raise RuntimeError(msg)
1697
+
1631
1698
  use_returning = self._dialect in {DIALECT_SQLITE, DIALECT_POSTGRESQL, DIALECT_DUCKDB}
1632
1699
  if use_returning:
1633
1700
  sql = f"DELETE FROM {self._memory_table} WHERE session_id = ? RETURNING 1"
@@ -1636,7 +1703,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1636
1703
  with self._config.provide_connection() as conn:
1637
1704
  cursor = conn.cursor()
1638
1705
  try:
1639
- cursor.execute(sql, (session_id,))
1706
+ self._execute(cursor, sql, (session_id,))
1640
1707
  if use_returning:
1641
1708
  deleted_rows = cursor.fetchall()
1642
1709
  conn.commit()
@@ -1647,6 +1714,10 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1647
1714
  cursor.close()
1648
1715
 
1649
1716
  def _delete_entries_older_than(self, days: int, app_name: "str | None" = None, scope: "str | None" = None) -> int:
1717
+ if not self._enabled:
1718
+ msg = "Memory store is disabled"
1719
+ raise RuntimeError(msg)
1720
+
1650
1721
  cutoff = self._encode_timestamp(datetime.now(timezone.utc) - timedelta(days=days))
1651
1722
  use_returning = self._dialect in {DIALECT_SQLITE, DIALECT_POSTGRESQL, DIALECT_DUCKDB}
1652
1723
  clauses = ["inserted_at < ?"]
@@ -1665,7 +1736,7 @@ class AdbcADKMemoryStore(BaseSyncADKMemoryStore["AdbcConfig"]):
1665
1736
  with self._config.provide_connection() as conn:
1666
1737
  cursor = conn.cursor()
1667
1738
  try:
1668
- cursor.execute(sql, tuple(params))
1739
+ self._execute(cursor, sql, tuple(params))
1669
1740
  if use_returning:
1670
1741
  deleted_rows = cursor.fetchall()
1671
1742
  conn.commit()
@@ -76,8 +76,15 @@ class AdbcConnectionParams(TypedDict):
76
76
  role: NotRequired[str]
77
77
  authorization_header: NotRequired[str]
78
78
  grpc_options: NotRequired[dict[str, Any]]
79
+ grpc_max_message_size: NotRequired[int]
80
+ fetch_timeout: NotRequired[float]
79
81
  gizmosql_backend: NotRequired[str]
80
82
  tls_skip_verify: NotRequired[bool]
83
+ tls_root_certs: NotRequired[str]
84
+ mtls_cert_chain: NotRequired[str]
85
+ mtls_private_key: NotRequired[str]
86
+ with_cookie_middleware: NotRequired[bool]
87
+ headers: NotRequired[dict[str, str]]
81
88
  extra: NotRequired[dict[str, Any]]
82
89
 
83
90
 
@@ -11,6 +11,7 @@ import sqlglot
11
11
  from sqlglot import exp
12
12
  from sqlglot.errors import ParseError
13
13
 
14
+ import sqlspec.adapters.adbc._typing as _adbc_typing
14
15
  from sqlspec.adapters.adbc.type_converter import get_adbc_type_converter
15
16
  from sqlspec.core import (
16
17
  DriverParameterProfile,
@@ -181,6 +182,14 @@ _DRIVER_MANAGER_CONNECT: Final[str] = "adbc_driver_manager.dbapi.connect"
181
182
  _FLIGHTSQL_DB_KWARGS_FIELDS: "tuple[str, ...]" = ("username", "password")
182
183
  _FLIGHTSQL_TLS_SKIP_VERIFY_KEY: Final[str] = "adbc.flight.sql.client_option.tls_skip_verify"
183
184
  _FLIGHTSQL_AUTHORIZATION_HEADER_KEY: Final[str] = "adbc.flight.sql.authorization_header"
185
+ _FLIGHTSQL_WITH_MAX_MSG_SIZE_KEY: Final[str] = "adbc.flight.sql.client_option.with_max_msg_size"
186
+ _FLIGHTSQL_TIMEOUT_QUERY_KEY: Final[str] = "adbc.flight.sql.rpc.timeout_seconds.query"
187
+ _FLIGHTSQL_TIMEOUT_FETCH_KEY: Final[str] = "adbc.flight.sql.rpc.timeout_seconds.fetch"
188
+ _FLIGHTSQL_TLS_ROOT_CERTS_KEY: Final[str] = "adbc.flight.sql.client_option.tls_root_certs"
189
+ _FLIGHTSQL_MTLS_CERT_CHAIN_KEY: Final[str] = "adbc.flight.sql.client_option.mtls_cert_chain"
190
+ _FLIGHTSQL_MTLS_PRIVATE_KEY_KEY: Final[str] = "adbc.flight.sql.client_option.mtls_private_key"
191
+ _FLIGHTSQL_WITH_COOKIE_MIDDLEWARE_KEY: Final[str] = "adbc.flight.sql.rpc.with_cookie_middleware"
192
+ _FLIGHTSQL_RPC_CALL_HEADER_PREFIX: Final[str] = "adbc.flight.sql.rpc.call_header."
184
193
  _SQLSTATE_CLASS_CODE_LEN = 2
185
194
  _SQLSTATE_DESCRIPTIONS: dict[str, str] = {
186
195
  "23": "integrity constraint violation",
@@ -685,7 +694,6 @@ def create_mapped_exception(error: Any, *, logger: Any | None = None) -> SQLSpec
685
694
  sqlstate = sqlstate_attr if sqlstate_attr is not None else None
686
695
 
687
696
  if sqlstate:
688
- # Use centralized SQLSTATE mapping for specific codes
689
697
  if sqlstate == "23505":
690
698
  return _create_adbc_error(error, UniqueViolationError, "unique constraint violation")
691
699
  if sqlstate == "23503":
@@ -695,7 +703,6 @@ def create_mapped_exception(error: Any, *, logger: Any | None = None) -> SQLSpec
695
703
  if sqlstate == "23514":
696
704
  return _create_adbc_error(error, CheckViolationError, "check constraint violation")
697
705
 
698
- # Deadlock and serialization errors
699
706
  if sqlstate == "40P01":
700
707
  return _create_adbc_error(error, DeadlockError, "deadlock detected")
701
708
  if sqlstate == "40001":
@@ -705,25 +712,20 @@ def create_mapped_exception(error: Any, *, logger: Any | None = None) -> SQLSpec
705
712
  termination_class = _classify_timeout_or_cancellation(str(error)) or OperationalError
706
713
  return _create_adbc_error(error, termination_class, "query terminated")
707
714
 
708
- # Permission errors
709
715
  if sqlstate == "42501":
710
716
  return _create_adbc_error(error, PermissionDeniedError, "insufficient privilege")
711
717
  if sqlstate == "28000":
712
718
  return _create_adbc_error(error, PermissionDeniedError, "invalid authorization")
713
719
 
714
- # Use centralized mapping for SQLSTATE class prefixes
715
720
  exc_class = map_sqlstate_to_exception(sqlstate)
716
721
  if exc_class is not None and exc_class is not SQLSpecError:
717
722
  description = _get_sqlstate_description(sqlstate)
718
723
  return _create_adbc_error(error, exc_class, description)
719
724
 
720
- # Fallback for unmapped SQLSTATE codes
721
725
  return _create_adbc_error(error, SQLSpecError, "database error")
722
726
 
723
- # Message-based fallback when no SQLSTATE is available
724
727
  error_msg = str(error).lower()
725
728
 
726
- # Constraint violations
727
729
  if "unique" in error_msg or "duplicate" in error_msg:
728
730
  return _create_adbc_error(error, UniqueViolationError, "unique constraint violation")
729
731
  if "foreign key" in error_msg:
@@ -735,7 +737,6 @@ def create_mapped_exception(error: Any, *, logger: Any | None = None) -> SQLSpec
735
737
  if "constraint" in error_msg:
736
738
  return _create_adbc_error(error, IntegrityError, "integrity constraint violation")
737
739
 
738
- # Deadlock/lock patterns
739
740
  if "deadlock" in error_msg:
740
741
  return _create_adbc_error(error, DeadlockError, "deadlock detected")
741
742
  if "serialization" in error_msg or "concurrent update" in error_msg:
@@ -744,15 +745,12 @@ def create_mapped_exception(error: Any, *, logger: Any | None = None) -> SQLSpec
744
745
  if message_class := _classify_timeout_or_cancellation(error_msg):
745
746
  return _create_adbc_error(error, message_class, "query terminated")
746
747
 
747
- # Permission patterns
748
748
  if "permission" in error_msg or "denied" in error_msg or "unauthorized" in error_msg:
749
749
  return _create_adbc_error(error, PermissionDeniedError, "permission denied")
750
750
 
751
- # Syntax errors
752
751
  if "syntax" in error_msg:
753
752
  return _create_adbc_error(error, SQLParsingError, "SQL parsing error")
754
753
 
755
- # Connection errors
756
754
  if "connection" in error_msg or "connect" in error_msg:
757
755
  return _create_adbc_error(error, DatabaseConnectionError, "connection error")
758
756
 
@@ -1154,7 +1152,7 @@ def _rewrite_postgres_uuid_placeholders(
1154
1152
 
1155
1153
  for expression in expressions:
1156
1154
  expression.transform(wrap_parameter, copy=False)
1157
- rewritten = "; ".join(expression.sql(dialect=dialect) for expression in expressions)
1155
+ rewritten = "; ".join(expression.sql(dialect=dialect, copy=False) for expression in expressions)
1158
1156
  return rewritten, effective_scalars, effective_arrays
1159
1157
 
1160
1158
 
@@ -1388,10 +1386,10 @@ def _prepare_batch_with_casts(
1388
1386
 
1389
1387
  def _resolve_flightsql_db_kwargs_keys() -> "tuple[str, str]":
1390
1388
  try:
1391
- from sqlspec.adapters.adbc._typing import AdbcFlightSqlDatabaseOptions as DatabaseOptions
1389
+ database_options = _adbc_typing.AdbcFlightSqlDatabaseOptions
1392
1390
  except ImportError:
1393
1391
  return _FLIGHTSQL_TLS_SKIP_VERIFY_KEY, _FLIGHTSQL_AUTHORIZATION_HEADER_KEY
1394
- return DatabaseOptions.TLS_SKIP_VERIFY.value, DatabaseOptions.AUTHORIZATION_HEADER.value
1392
+ return database_options.TLS_SKIP_VERIFY.value, database_options.AUTHORIZATION_HEADER.value
1395
1393
 
1396
1394
 
1397
1395
  def _lift_flightsql_db_kwargs(config: "dict[str, Any]") -> None:
@@ -1412,6 +1410,36 @@ def _lift_flightsql_db_kwargs(config: "dict[str, Any]") -> None:
1412
1410
  if "authorization_header" in config:
1413
1411
  db_kwargs_dict.setdefault(authorization_header_key, config.pop("authorization_header"))
1414
1412
 
1413
+ if "grpc_max_message_size" in config:
1414
+ db_kwargs_dict.setdefault(_FLIGHTSQL_WITH_MAX_MSG_SIZE_KEY, str(config.pop("grpc_max_message_size")))
1415
+
1416
+ if "query_timeout" in config:
1417
+ db_kwargs_dict.setdefault(_FLIGHTSQL_TIMEOUT_QUERY_KEY, str(config.pop("query_timeout")))
1418
+
1419
+ if "fetch_timeout" in config:
1420
+ db_kwargs_dict.setdefault(_FLIGHTSQL_TIMEOUT_FETCH_KEY, str(config.pop("fetch_timeout")))
1421
+
1422
+ if "tls_root_certs" in config:
1423
+ db_kwargs_dict.setdefault(_FLIGHTSQL_TLS_ROOT_CERTS_KEY, str(config.pop("tls_root_certs")))
1424
+
1425
+ if "mtls_cert_chain" in config:
1426
+ db_kwargs_dict.setdefault(_FLIGHTSQL_MTLS_CERT_CHAIN_KEY, str(config.pop("mtls_cert_chain")))
1427
+
1428
+ if "mtls_private_key" in config:
1429
+ db_kwargs_dict.setdefault(_FLIGHTSQL_MTLS_PRIVATE_KEY_KEY, str(config.pop("mtls_private_key")))
1430
+
1431
+ if "with_cookie_middleware" in config:
1432
+ val = config.pop("with_cookie_middleware")
1433
+ db_kwargs_dict.setdefault(
1434
+ _FLIGHTSQL_WITH_COOKIE_MIDDLEWARE_KEY, str(val).lower() if isinstance(val, bool) else str(val)
1435
+ )
1436
+
1437
+ if "headers" in config:
1438
+ headers = config.pop("headers")
1439
+ if isinstance(headers, dict):
1440
+ for hdr_key, hdr_val in headers.items():
1441
+ db_kwargs_dict.setdefault(f"{_FLIGHTSQL_RPC_CALL_HEADER_PREFIX}{hdr_key}", str(hdr_val))
1442
+
1415
1443
  config.pop("gizmosql_backend", None)
1416
1444
  if db_kwargs_dict:
1417
1445
  config["db_kwargs"] = db_kwargs_dict
@@ -257,7 +257,6 @@ class AdbcDriver(SyncDriverAdapterBase):
257
257
 
258
258
  try:
259
259
  if not prepared_parameters:
260
- cursor._rowcount = 0 # pyright: ignore[reportPrivateUsage]
261
260
  row_count = 0
262
261
  elif isinstance(prepared_parameters, (list, tuple)) and prepared_parameters:
263
262
  parameter_count = len(prepared_parameters)
@@ -496,7 +495,8 @@ class AdbcDriver(SyncDriverAdapterBase):
496
495
  cursor = cursor_manager.__enter__()
497
496
  try:
498
497
  sql, driver_params = self._compiled_sql(prepared_statement, config)
499
- cursor.execute(sql, driver_params or ())
498
+ execute_parameters = normalize_postgres_empty_parameters(self._dialect_name, driver_params)
499
+ cursor.execute(sql, parameters=execute_parameters)
500
500
  fetch_record_batch = getattr(cursor, "fetch_record_batch", None)
501
501
  if fetch_record_batch is None:
502
502
  arrow_table = cursor.fetch_arrow_table()
@@ -512,6 +512,7 @@ class AdbcDriver(SyncDriverAdapterBase):
512
512
  else:
513
513
  reader = fetch_record_batch()
514
514
  except Exception:
515
+ handle_postgres_rollback(self._dialect_name, cursor, logger)
515
516
  cursor_manager.__exit__(None, None, None)
516
517
  cursor = None
517
518
  raise
@@ -537,8 +538,13 @@ class AdbcDriver(SyncDriverAdapterBase):
537
538
  raise DatabaseConnectionError(msg)
538
539
 
539
540
  sql, driver_params = self._compiled_sql(prepared_statement, config)
540
- cursor.execute(sql, driver_params or ())
541
- arrow_table = cursor.fetch_arrow_table()
541
+ execute_parameters = normalize_postgres_empty_parameters(self._dialect_name, driver_params)
542
+ try:
543
+ cursor.execute(sql, parameters=execute_parameters)
544
+ arrow_table = cursor.fetch_arrow_table()
545
+ except Exception:
546
+ handle_postgres_rollback(self._dialect_name, cursor, logger)
547
+ raise
542
548
 
543
549
  arrow_result = build_arrow_result_from_table(
544
550
  prepared_statement,
@@ -39,6 +39,8 @@ if TYPE_CHECKING:
39
39
 
40
40
  def close(self) -> object: ...
41
41
 
42
+ def get_transaction_status(self) -> bool: ...
43
+
42
44
  class AiomysqlModuleProtocol(Protocol):
43
45
  async def create_pool(self, **kwargs: Any) -> "AiomysqlPool": ...
44
46
 
@@ -579,12 +579,12 @@ class AiomysqlADKMemoryStore(BaseAsyncADKMemoryStore["AiomysqlConfig"]):
579
579
  entry["user_id"],
580
580
  entry.get("scope", "user"),
581
581
  entry["event_id"],
582
- entry["author"],
582
+ entry.get("author"),
583
583
  owner_id,
584
584
  entry["timestamp"],
585
585
  to_json(entry["content_json"]),
586
586
  entry["content_text"],
587
- to_json(entry["metadata_json"]),
587
+ to_json(entry["metadata_json"]) if entry.get("metadata_json") is not None else None,
588
588
  entry["inserted_at"],
589
589
  )
590
590
  else:
@@ -595,11 +595,11 @@ class AiomysqlADKMemoryStore(BaseAsyncADKMemoryStore["AiomysqlConfig"]):
595
595
  entry["user_id"],
596
596
  entry.get("scope", "user"),
597
597
  entry["event_id"],
598
- entry["author"],
598
+ entry.get("author"),
599
599
  entry["timestamp"],
600
600
  to_json(entry["content_json"]),
601
601
  entry["content_text"],
602
- to_json(entry["metadata_json"]),
602
+ to_json(entry["metadata_json"]) if entry.get("metadata_json") is not None else None,
603
603
  entry["inserted_at"],
604
604
  )
605
605
  await cursor.execute(sql, params)
@@ -655,6 +655,9 @@ class AiomysqlADKMemoryStore(BaseAsyncADKMemoryStore["AiomysqlConfig"]):
655
655
  records: list[StoredMemory] = []
656
656
  for row in rows:
657
657
  rec = cast("StoredMemory", dict(zip(columns, row, strict=False)))
658
+ rec["content_json"] = _json_dict(rec.get("content_json"))
659
+ metadata_val = rec.get("metadata_json")
660
+ rec["metadata_json"] = _json_dict(metadata_val) if metadata_val is not None else None
658
661
  rec["embedding"] = None
659
662
  records.append(rec)
660
663
  return records