sqlspec 0.26.0__py3-none-any.whl → 0.28.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.

Potentially problematic release.


This version of sqlspec might be problematic. Click here for more details.

Files changed (212) hide show
  1. sqlspec/__init__.py +7 -15
  2. sqlspec/_serialization.py +55 -25
  3. sqlspec/_typing.py +155 -52
  4. sqlspec/adapters/adbc/_types.py +1 -1
  5. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  6. sqlspec/adapters/adbc/adk/store.py +880 -0
  7. sqlspec/adapters/adbc/config.py +62 -12
  8. sqlspec/adapters/adbc/data_dictionary.py +74 -2
  9. sqlspec/adapters/adbc/driver.py +226 -58
  10. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  11. sqlspec/adapters/adbc/litestar/store.py +504 -0
  12. sqlspec/adapters/adbc/type_converter.py +44 -50
  13. sqlspec/adapters/aiosqlite/_types.py +1 -1
  14. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  15. sqlspec/adapters/aiosqlite/adk/store.py +536 -0
  16. sqlspec/adapters/aiosqlite/config.py +86 -16
  17. sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
  18. sqlspec/adapters/aiosqlite/driver.py +127 -38
  19. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  21. sqlspec/adapters/aiosqlite/pool.py +7 -7
  22. sqlspec/adapters/asyncmy/__init__.py +7 -1
  23. sqlspec/adapters/asyncmy/_types.py +1 -1
  24. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  25. sqlspec/adapters/asyncmy/adk/store.py +503 -0
  26. sqlspec/adapters/asyncmy/config.py +59 -17
  27. sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
  28. sqlspec/adapters/asyncmy/driver.py +293 -62
  29. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  31. sqlspec/adapters/asyncpg/__init__.py +2 -1
  32. sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
  33. sqlspec/adapters/asyncpg/_types.py +11 -7
  34. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  35. sqlspec/adapters/asyncpg/adk/store.py +460 -0
  36. sqlspec/adapters/asyncpg/config.py +57 -36
  37. sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
  38. sqlspec/adapters/asyncpg/driver.py +153 -23
  39. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  41. sqlspec/adapters/bigquery/_types.py +1 -1
  42. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  43. sqlspec/adapters/bigquery/adk/store.py +585 -0
  44. sqlspec/adapters/bigquery/config.py +36 -11
  45. sqlspec/adapters/bigquery/data_dictionary.py +42 -2
  46. sqlspec/adapters/bigquery/driver.py +489 -144
  47. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  48. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  49. sqlspec/adapters/bigquery/type_converter.py +55 -23
  50. sqlspec/adapters/duckdb/_types.py +2 -2
  51. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  52. sqlspec/adapters/duckdb/adk/store.py +563 -0
  53. sqlspec/adapters/duckdb/config.py +79 -21
  54. sqlspec/adapters/duckdb/data_dictionary.py +41 -2
  55. sqlspec/adapters/duckdb/driver.py +225 -44
  56. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  57. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  58. sqlspec/adapters/duckdb/pool.py +5 -5
  59. sqlspec/adapters/duckdb/type_converter.py +51 -21
  60. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  61. sqlspec/adapters/oracledb/_types.py +20 -2
  62. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  63. sqlspec/adapters/oracledb/adk/store.py +1628 -0
  64. sqlspec/adapters/oracledb/config.py +120 -36
  65. sqlspec/adapters/oracledb/data_dictionary.py +87 -20
  66. sqlspec/adapters/oracledb/driver.py +475 -86
  67. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  68. sqlspec/adapters/oracledb/litestar/store.py +765 -0
  69. sqlspec/adapters/oracledb/migrations.py +316 -25
  70. sqlspec/adapters/oracledb/type_converter.py +91 -16
  71. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  72. sqlspec/adapters/psqlpy/_types.py +2 -1
  73. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  74. sqlspec/adapters/psqlpy/adk/store.py +483 -0
  75. sqlspec/adapters/psqlpy/config.py +45 -19
  76. sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
  77. sqlspec/adapters/psqlpy/driver.py +108 -41
  78. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  79. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  80. sqlspec/adapters/psqlpy/type_converter.py +40 -11
  81. sqlspec/adapters/psycopg/_type_handlers.py +80 -0
  82. sqlspec/adapters/psycopg/_types.py +2 -1
  83. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  84. sqlspec/adapters/psycopg/adk/store.py +962 -0
  85. sqlspec/adapters/psycopg/config.py +65 -37
  86. sqlspec/adapters/psycopg/data_dictionary.py +91 -3
  87. sqlspec/adapters/psycopg/driver.py +200 -78
  88. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  90. sqlspec/adapters/sqlite/__init__.py +2 -1
  91. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  92. sqlspec/adapters/sqlite/_types.py +1 -1
  93. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  94. sqlspec/adapters/sqlite/adk/store.py +582 -0
  95. sqlspec/adapters/sqlite/config.py +85 -16
  96. sqlspec/adapters/sqlite/data_dictionary.py +34 -2
  97. sqlspec/adapters/sqlite/driver.py +120 -52
  98. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  99. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  100. sqlspec/adapters/sqlite/pool.py +5 -5
  101. sqlspec/base.py +45 -26
  102. sqlspec/builder/__init__.py +73 -4
  103. sqlspec/builder/_base.py +91 -58
  104. sqlspec/builder/_column.py +5 -5
  105. sqlspec/builder/_ddl.py +98 -89
  106. sqlspec/builder/_delete.py +5 -4
  107. sqlspec/builder/_dml.py +388 -0
  108. sqlspec/{_sql.py → builder/_factory.py} +41 -44
  109. sqlspec/builder/_insert.py +5 -82
  110. sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
  111. sqlspec/builder/_merge.py +446 -11
  112. sqlspec/builder/_parsing_utils.py +9 -11
  113. sqlspec/builder/_select.py +1313 -25
  114. sqlspec/builder/_update.py +11 -42
  115. sqlspec/cli.py +76 -69
  116. sqlspec/config.py +331 -62
  117. sqlspec/core/__init__.py +5 -4
  118. sqlspec/core/cache.py +18 -18
  119. sqlspec/core/compiler.py +6 -8
  120. sqlspec/core/filters.py +55 -47
  121. sqlspec/core/hashing.py +9 -9
  122. sqlspec/core/parameters.py +76 -45
  123. sqlspec/core/result.py +234 -47
  124. sqlspec/core/splitter.py +16 -17
  125. sqlspec/core/statement.py +32 -31
  126. sqlspec/core/type_conversion.py +3 -2
  127. sqlspec/driver/__init__.py +1 -3
  128. sqlspec/driver/_async.py +183 -160
  129. sqlspec/driver/_common.py +197 -109
  130. sqlspec/driver/_sync.py +189 -161
  131. sqlspec/driver/mixins/_result_tools.py +20 -236
  132. sqlspec/driver/mixins/_sql_translator.py +4 -4
  133. sqlspec/exceptions.py +70 -7
  134. sqlspec/extensions/adk/__init__.py +53 -0
  135. sqlspec/extensions/adk/_types.py +51 -0
  136. sqlspec/extensions/adk/converters.py +172 -0
  137. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  138. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  139. sqlspec/extensions/adk/service.py +181 -0
  140. sqlspec/extensions/adk/store.py +536 -0
  141. sqlspec/extensions/aiosql/adapter.py +69 -61
  142. sqlspec/extensions/fastapi/__init__.py +21 -0
  143. sqlspec/extensions/fastapi/extension.py +331 -0
  144. sqlspec/extensions/fastapi/providers.py +543 -0
  145. sqlspec/extensions/flask/__init__.py +36 -0
  146. sqlspec/extensions/flask/_state.py +71 -0
  147. sqlspec/extensions/flask/_utils.py +40 -0
  148. sqlspec/extensions/flask/extension.py +389 -0
  149. sqlspec/extensions/litestar/__init__.py +21 -4
  150. sqlspec/extensions/litestar/cli.py +54 -10
  151. sqlspec/extensions/litestar/config.py +56 -266
  152. sqlspec/extensions/litestar/handlers.py +46 -17
  153. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  154. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  155. sqlspec/extensions/litestar/plugin.py +349 -224
  156. sqlspec/extensions/litestar/providers.py +25 -25
  157. sqlspec/extensions/litestar/store.py +265 -0
  158. sqlspec/extensions/starlette/__init__.py +10 -0
  159. sqlspec/extensions/starlette/_state.py +25 -0
  160. sqlspec/extensions/starlette/_utils.py +52 -0
  161. sqlspec/extensions/starlette/extension.py +254 -0
  162. sqlspec/extensions/starlette/middleware.py +154 -0
  163. sqlspec/loader.py +30 -49
  164. sqlspec/migrations/base.py +200 -76
  165. sqlspec/migrations/commands.py +591 -62
  166. sqlspec/migrations/context.py +6 -9
  167. sqlspec/migrations/fix.py +199 -0
  168. sqlspec/migrations/loaders.py +47 -19
  169. sqlspec/migrations/runner.py +241 -75
  170. sqlspec/migrations/tracker.py +237 -21
  171. sqlspec/migrations/utils.py +51 -3
  172. sqlspec/migrations/validation.py +177 -0
  173. sqlspec/protocols.py +106 -36
  174. sqlspec/storage/_utils.py +85 -0
  175. sqlspec/storage/backends/fsspec.py +133 -107
  176. sqlspec/storage/backends/local.py +78 -51
  177. sqlspec/storage/backends/obstore.py +276 -168
  178. sqlspec/storage/registry.py +75 -39
  179. sqlspec/typing.py +30 -84
  180. sqlspec/utils/__init__.py +25 -4
  181. sqlspec/utils/arrow_helpers.py +81 -0
  182. sqlspec/utils/config_resolver.py +6 -6
  183. sqlspec/utils/correlation.py +4 -5
  184. sqlspec/utils/data_transformation.py +3 -2
  185. sqlspec/utils/deprecation.py +9 -8
  186. sqlspec/utils/fixtures.py +4 -4
  187. sqlspec/utils/logging.py +46 -6
  188. sqlspec/utils/module_loader.py +205 -5
  189. sqlspec/utils/portal.py +311 -0
  190. sqlspec/utils/schema.py +288 -0
  191. sqlspec/utils/serializers.py +113 -4
  192. sqlspec/utils/sync_tools.py +36 -22
  193. sqlspec/utils/text.py +1 -2
  194. sqlspec/utils/type_guards.py +136 -20
  195. sqlspec/utils/version.py +433 -0
  196. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
  197. sqlspec-0.28.0.dist-info/RECORD +221 -0
  198. sqlspec/builder/mixins/__init__.py +0 -55
  199. sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
  200. sqlspec/builder/mixins/_delete_operations.py +0 -50
  201. sqlspec/builder/mixins/_insert_operations.py +0 -282
  202. sqlspec/builder/mixins/_merge_operations.py +0 -698
  203. sqlspec/builder/mixins/_order_limit_operations.py +0 -145
  204. sqlspec/builder/mixins/_pivot_operations.py +0 -157
  205. sqlspec/builder/mixins/_select_operations.py +0 -930
  206. sqlspec/builder/mixins/_update_operations.py +0 -199
  207. sqlspec/builder/mixins/_where_clause.py +0 -1298
  208. sqlspec-0.26.0.dist-info/RECORD +0 -157
  209. sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
  210. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
  211. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
  212. {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
sqlspec/config.py CHANGED
@@ -1,6 +1,7 @@
1
1
  from abc import ABC, abstractmethod
2
+ from collections.abc import Callable
2
3
  from pathlib import Path
3
- from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, Optional, TypeVar, Union, cast
4
+ from typing import TYPE_CHECKING, Any, ClassVar, Generic, Literal, TypeVar, cast
4
5
 
5
6
  from typing_extensions import NotRequired, TypedDict
6
7
 
@@ -19,35 +20,39 @@ if TYPE_CHECKING:
19
20
 
20
21
 
21
22
  __all__ = (
23
+ "ADKConfig",
22
24
  "AsyncConfigT",
23
25
  "AsyncDatabaseConfig",
24
26
  "ConfigT",
25
27
  "DatabaseConfigProtocol",
26
28
  "DriverT",
29
+ "FlaskConfig",
27
30
  "LifecycleConfig",
31
+ "LitestarConfig",
28
32
  "MigrationConfig",
29
33
  "NoPoolAsyncConfig",
30
34
  "NoPoolSyncConfig",
35
+ "StarletteConfig",
31
36
  "SyncConfigT",
32
37
  "SyncDatabaseConfig",
33
38
  )
34
39
 
35
- AsyncConfigT = TypeVar("AsyncConfigT", bound="Union[AsyncDatabaseConfig[Any, Any, Any], NoPoolAsyncConfig[Any, Any]]")
36
- SyncConfigT = TypeVar("SyncConfigT", bound="Union[SyncDatabaseConfig[Any, Any, Any], NoPoolSyncConfig[Any, Any]]")
40
+ AsyncConfigT = TypeVar("AsyncConfigT", bound="AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any]")
41
+ SyncConfigT = TypeVar("SyncConfigT", bound="SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any]")
37
42
  ConfigT = TypeVar(
38
43
  "ConfigT",
39
- bound="Union[Union[AsyncDatabaseConfig[Any, Any, Any], NoPoolAsyncConfig[Any, Any]], SyncDatabaseConfig[Any, Any, Any], NoPoolSyncConfig[Any, Any]]",
44
+ bound="AsyncDatabaseConfig[Any, Any, Any] | NoPoolAsyncConfig[Any, Any] | SyncDatabaseConfig[Any, Any, Any] | NoPoolSyncConfig[Any, Any]",
40
45
  )
41
46
 
42
47
  # Define TypeVars for Generic classes
43
48
  ConnectionT = TypeVar("ConnectionT")
44
49
  PoolT = TypeVar("PoolT")
45
- DriverT = TypeVar("DriverT", bound="Union[SyncDriverAdapterBase, AsyncDriverAdapterBase]")
50
+ DriverT = TypeVar("DriverT", bound="SyncDriverAdapterBase | AsyncDriverAdapterBase")
46
51
 
47
52
  logger = get_logger("config")
48
53
 
49
54
 
50
- class LifecycleConfig(TypedDict, total=False):
55
+ class LifecycleConfig(TypedDict):
51
56
  """Lifecycle hooks for database adapters.
52
57
 
53
58
  Each hook accepts a list of callables to support multiple handlers.
@@ -64,14 +69,14 @@ class LifecycleConfig(TypedDict, total=False):
64
69
  on_error: NotRequired[list[Callable[[Exception, str, dict], None]]]
65
70
 
66
71
 
67
- class MigrationConfig(TypedDict, total=False):
72
+ class MigrationConfig(TypedDict):
68
73
  """Configuration options for database migrations.
69
74
 
70
75
  All fields are optional with default values.
71
76
  """
72
77
 
73
- script_location: NotRequired[str]
74
- """Path to the migrations directory. Defaults to 'migrations'."""
78
+ script_location: NotRequired["str | Path"]
79
+ """Path to the migrations directory. Accepts string or Path object. Defaults to 'migrations'."""
75
80
 
76
81
  version_table_name: NotRequired[str]
77
82
  """Name of the table used to track applied migrations. Defaults to 'sqlspec_migrations'."""
@@ -82,6 +87,262 @@ class MigrationConfig(TypedDict, total=False):
82
87
  enabled: NotRequired[bool]
83
88
  """Whether this configuration should be included in CLI operations. Defaults to True."""
84
89
 
90
+ auto_sync: NotRequired[bool]
91
+ """Enable automatic version reconciliation during upgrade. When enabled (default), SQLSpec automatically updates database tracking when migrations are renamed from timestamp to sequential format. Defaults to True."""
92
+
93
+ strict_ordering: NotRequired[bool]
94
+ """Enforce strict migration ordering. When enabled, prevents out-of-order migrations from being applied. Defaults to False."""
95
+
96
+ include_extensions: NotRequired["list[str]"]
97
+ """List of extension names whose migrations should be included. Extension migrations maintain separate versioning and are prefixed with 'ext_{name}_'."""
98
+
99
+ transactional: NotRequired[bool]
100
+ """Wrap migrations in transactions when supported. When enabled (default for adapters that support it), each migration runs in a transaction that is committed on success or rolled back on failure. This prevents partial migrations from leaving the database in an inconsistent state. Requires adapter support for transactional DDL. Defaults to True for PostgreSQL, SQLite, and DuckDB; False for MySQL, Oracle, and BigQuery. Individual migrations can override this with a '-- transactional: false' comment."""
101
+
102
+
103
+ class FlaskConfig(TypedDict):
104
+ """Configuration options for Flask SQLSpec extension.
105
+
106
+ All fields are optional with sensible defaults. Use in extension_config["flask"]:
107
+
108
+ Example:
109
+ from sqlspec.adapters.asyncpg import AsyncpgConfig
110
+
111
+ config = AsyncpgConfig(
112
+ pool_config={"dsn": "postgresql://localhost/mydb"},
113
+ extension_config={
114
+ "flask": {
115
+ "commit_mode": "autocommit",
116
+ "session_key": "db"
117
+ }
118
+ }
119
+ )
120
+
121
+ Notes:
122
+ This TypedDict provides type safety for extension config.
123
+ Flask extension uses g object for request-scoped storage.
124
+ """
125
+
126
+ connection_key: NotRequired[str]
127
+ """Key for storing connection in Flask g object. Default: auto-generated from session_key."""
128
+
129
+ session_key: NotRequired[str]
130
+ """Key for accessing session via plugin.get_session(). Default: 'db_session'."""
131
+
132
+ commit_mode: NotRequired[Literal["manual", "autocommit", "autocommit_include_redirect"]]
133
+ """Transaction commit mode. Default: 'manual'.
134
+ - manual: No automatic commits, user handles explicitly
135
+ - autocommit: Commits on 2xx status, rollback otherwise
136
+ - autocommit_include_redirect: Commits on 2xx-3xx status, rollback otherwise
137
+ """
138
+
139
+ extra_commit_statuses: NotRequired[set[int]]
140
+ """Additional HTTP status codes that trigger commit. Default: None."""
141
+
142
+ extra_rollback_statuses: NotRequired[set[int]]
143
+ """Additional HTTP status codes that trigger rollback. Default: None."""
144
+
145
+
146
+ class LitestarConfig(TypedDict):
147
+ """Configuration options for Litestar SQLSpec plugin.
148
+
149
+ All fields are optional with sensible defaults.
150
+ """
151
+
152
+ connection_key: NotRequired[str]
153
+ """Key for storing connection in ASGI scope. Default: 'db_connection'"""
154
+
155
+ pool_key: NotRequired[str]
156
+ """Key for storing connection pool in application state. Default: 'db_pool'"""
157
+
158
+ session_key: NotRequired[str]
159
+ """Key for storing session in ASGI scope. Default: 'db_session'"""
160
+
161
+ commit_mode: NotRequired[Literal["manual", "autocommit", "autocommit_include_redirect"]]
162
+ """Transaction commit mode. Default: 'manual'"""
163
+
164
+ enable_correlation_middleware: NotRequired[bool]
165
+ """Enable request correlation ID middleware. Default: True"""
166
+
167
+ extra_commit_statuses: NotRequired[set[int]]
168
+ """Additional HTTP status codes that trigger commit. Default: set()"""
169
+
170
+ extra_rollback_statuses: NotRequired[set[int]]
171
+ """Additional HTTP status codes that trigger rollback. Default: set()"""
172
+
173
+
174
+ class StarletteConfig(TypedDict):
175
+ """Configuration options for Starlette and FastAPI extensions.
176
+
177
+ All fields are optional with sensible defaults. Use in extension_config["starlette"]:
178
+
179
+ Example:
180
+ from sqlspec.adapters.asyncpg import AsyncpgConfig
181
+
182
+ config = AsyncpgConfig(
183
+ pool_config={"dsn": "postgresql://localhost/mydb"},
184
+ extension_config={
185
+ "starlette": {
186
+ "commit_mode": "autocommit",
187
+ "session_key": "db"
188
+ }
189
+ }
190
+ )
191
+
192
+ Notes:
193
+ Both Starlette and FastAPI extensions use the "starlette" key.
194
+ This TypedDict provides type safety for extension config.
195
+ """
196
+
197
+ connection_key: NotRequired[str]
198
+ """Key for storing connection in request.state. Default: 'db_connection'"""
199
+
200
+ pool_key: NotRequired[str]
201
+ """Key for storing connection pool in app.state. Default: 'db_pool'"""
202
+
203
+ session_key: NotRequired[str]
204
+ """Key for storing session in request.state. Default: 'db_session'"""
205
+
206
+ commit_mode: NotRequired[Literal["manual", "autocommit", "autocommit_include_redirect"]]
207
+ """Transaction commit mode. Default: 'manual'
208
+
209
+ - manual: No automatic commit/rollback
210
+ - autocommit: Commit on 2xx, rollback otherwise
211
+ - autocommit_include_redirect: Commit on 2xx-3xx, rollback otherwise
212
+ """
213
+
214
+ extra_commit_statuses: NotRequired[set[int]]
215
+ """Additional HTTP status codes that trigger commit. Default: set()
216
+
217
+ Example:
218
+ extra_commit_statuses={201, 202}
219
+ """
220
+
221
+ extra_rollback_statuses: NotRequired[set[int]]
222
+ """Additional HTTP status codes that trigger rollback. Default: set()
223
+
224
+ Example:
225
+ extra_rollback_statuses={409}
226
+ """
227
+
228
+
229
+ class ADKConfig(TypedDict):
230
+ """Configuration options for ADK session store extension.
231
+
232
+ All fields are optional with sensible defaults. Use in extension_config["adk"]:
233
+
234
+ Example:
235
+ from sqlspec.adapters.asyncpg import AsyncpgConfig
236
+
237
+ config = AsyncpgConfig(
238
+ pool_config={"dsn": "postgresql://localhost/mydb"},
239
+ extension_config={
240
+ "adk": {
241
+ "session_table": "my_sessions",
242
+ "events_table": "my_events",
243
+ "owner_id_column": "tenant_id INTEGER REFERENCES tenants(id)"
244
+ }
245
+ }
246
+ )
247
+
248
+ Notes:
249
+ This TypedDict provides type safety for extension config but is not required.
250
+ You can use plain dicts as well.
251
+ """
252
+
253
+ session_table: NotRequired[str]
254
+ """Name of the sessions table. Default: 'adk_sessions'
255
+
256
+ Examples:
257
+ "agent_sessions"
258
+ "my_app_sessions"
259
+ "tenant_acme_sessions"
260
+ """
261
+
262
+ events_table: NotRequired[str]
263
+ """Name of the events table. Default: 'adk_events'
264
+
265
+ Examples:
266
+ "agent_events"
267
+ "my_app_events"
268
+ "tenant_acme_events"
269
+ """
270
+
271
+ owner_id_column: NotRequired[str]
272
+ """Optional owner ID column definition to link sessions to a user, tenant, team, or other entity.
273
+
274
+ Format: "column_name TYPE [NOT NULL] REFERENCES table(column) [options...]"
275
+
276
+ The entire definition is passed through to DDL verbatim. We only parse
277
+ the column name (first word) for use in INSERT/SELECT statements.
278
+
279
+ Supports:
280
+ - Foreign key constraints: REFERENCES table(column)
281
+ - Nullable or NOT NULL
282
+ - CASCADE options: ON DELETE CASCADE, ON UPDATE CASCADE
283
+ - Dialect-specific options (DEFERRABLE, ENABLE VALIDATE, etc.)
284
+ - Plain columns without FK (just extra column storage)
285
+
286
+ Examples:
287
+ PostgreSQL with UUID FK:
288
+ "account_id UUID NOT NULL REFERENCES users(id) ON DELETE CASCADE"
289
+
290
+ MySQL with BIGINT FK:
291
+ "user_id BIGINT NOT NULL REFERENCES users(id) ON DELETE RESTRICT"
292
+
293
+ Oracle with NUMBER FK:
294
+ "user_id NUMBER(10) REFERENCES users(id) ENABLE VALIDATE"
295
+
296
+ SQLite with INTEGER FK:
297
+ "tenant_id INTEGER NOT NULL REFERENCES tenants(id)"
298
+
299
+ Nullable FK (optional relationship):
300
+ "workspace_id UUID REFERENCES workspaces(id) ON DELETE SET NULL"
301
+
302
+ No FK (just extra column):
303
+ "organization_name VARCHAR(128) NOT NULL"
304
+
305
+ Deferred constraint (PostgreSQL):
306
+ "user_id UUID REFERENCES users(id) DEFERRABLE INITIALLY DEFERRED"
307
+
308
+ Notes:
309
+ - Column name (first word) is extracted for INSERT/SELECT queries
310
+ - Rest of definition is passed through to CREATE TABLE DDL
311
+ - Database validates the DDL syntax (fail-fast on errors)
312
+ - Works with all database dialects (PostgreSQL, MySQL, SQLite, Oracle, etc.)
313
+ """
314
+
315
+ in_memory: NotRequired[bool]
316
+ """Enable in-memory table storage (Oracle-specific). Default: False.
317
+
318
+ When enabled, tables are created with the INMEMORY clause for Oracle Database,
319
+ which stores table data in columnar format in memory for faster query performance.
320
+
321
+ This is an Oracle-specific feature that requires:
322
+ - Oracle Database 12.1.0.2 or higher
323
+ - Database In-Memory option license (Enterprise Edition)
324
+ - Sufficient INMEMORY_SIZE configured in the database instance
325
+
326
+ Other database adapters ignore this setting.
327
+
328
+ Examples:
329
+ Oracle with in-memory enabled:
330
+ config = OracleAsyncConfig(
331
+ pool_config={"dsn": "oracle://..."},
332
+ extension_config={
333
+ "adk": {
334
+ "in_memory": True
335
+ }
336
+ }
337
+ )
338
+
339
+ Notes:
340
+ - Improves query performance for analytics (10-100x faster)
341
+ - Tables created with INMEMORY clause
342
+ - Requires Oracle Database In-Memory option license
343
+ - Ignored by non-Oracle adapters
344
+ """
345
+
85
346
 
86
347
  class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
87
348
  """Protocol defining the interface for database configurations."""
@@ -97,19 +358,21 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
97
358
  )
98
359
 
99
360
  _migration_loader: "SQLFileLoader"
100
- _migration_commands: "Union[SyncMigrationCommands, AsyncMigrationCommands]"
361
+ _migration_commands: "SyncMigrationCommands | AsyncMigrationCommands"
101
362
  driver_type: "ClassVar[type[Any]]"
102
363
  connection_type: "ClassVar[type[Any]]"
103
364
  is_async: "ClassVar[bool]" = False
104
365
  supports_connection_pooling: "ClassVar[bool]" = False
366
+ supports_transactional_ddl: "ClassVar[bool]" = False
105
367
  supports_native_arrow_import: "ClassVar[bool]" = False
106
368
  supports_native_arrow_export: "ClassVar[bool]" = False
107
369
  supports_native_parquet_import: "ClassVar[bool]" = False
108
370
  supports_native_parquet_export: "ClassVar[bool]" = False
109
- bind_key: "Optional[str]"
371
+ bind_key: "str | None"
110
372
  statement_config: "StatementConfig"
111
- pool_instance: "Optional[PoolT]"
112
- migration_config: "Union[dict[str, Any], MigrationConfig]"
373
+ pool_instance: "PoolT | None"
374
+ migration_config: "dict[str, Any] | MigrationConfig"
375
+ driver_features: "dict[str, Any]"
113
376
 
114
377
  def __hash__(self) -> int:
115
378
  return id(self)
@@ -124,38 +387,38 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
124
387
  return f"{type(self).__name__}({parts})"
125
388
 
126
389
  @abstractmethod
127
- def create_connection(self) -> "Union[ConnectionT, Awaitable[ConnectionT]]":
390
+ def create_connection(self) -> "ConnectionT | Awaitable[ConnectionT]":
128
391
  """Create and return a new database connection."""
129
392
  raise NotImplementedError
130
393
 
131
394
  @abstractmethod
132
395
  def provide_connection(
133
396
  self, *args: Any, **kwargs: Any
134
- ) -> "Union[AbstractContextManager[ConnectionT], AbstractAsyncContextManager[ConnectionT]]":
397
+ ) -> "AbstractContextManager[ConnectionT] | AbstractAsyncContextManager[ConnectionT]":
135
398
  """Provide a database connection context manager."""
136
399
  raise NotImplementedError
137
400
 
138
401
  @abstractmethod
139
402
  def provide_session(
140
403
  self, *args: Any, **kwargs: Any
141
- ) -> "Union[AbstractContextManager[DriverT], AbstractAsyncContextManager[DriverT]]":
404
+ ) -> "AbstractContextManager[DriverT] | AbstractAsyncContextManager[DriverT]":
142
405
  """Provide a database session context manager."""
143
406
  raise NotImplementedError
144
407
 
145
408
  @abstractmethod
146
- def create_pool(self) -> "Union[PoolT, Awaitable[PoolT]]":
409
+ def create_pool(self) -> "PoolT | Awaitable[PoolT]":
147
410
  """Create and return connection pool."""
148
411
  raise NotImplementedError
149
412
 
150
413
  @abstractmethod
151
- def close_pool(self) -> "Optional[Awaitable[None]]":
414
+ def close_pool(self) -> "Awaitable[None] | None":
152
415
  """Terminate the connection pool."""
153
416
  raise NotImplementedError
154
417
 
155
418
  @abstractmethod
156
419
  def provide_pool(
157
420
  self, *args: Any, **kwargs: Any
158
- ) -> "Union[PoolT, Awaitable[PoolT], AbstractContextManager[PoolT], AbstractAsyncContextManager[PoolT]]":
421
+ ) -> "PoolT | Awaitable[PoolT] | AbstractContextManager[PoolT] | AbstractAsyncContextManager[PoolT]":
159
422
  """Provide pool instance."""
160
423
  raise NotImplementedError
161
424
 
@@ -181,7 +444,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
181
444
  from sqlspec.migrations.commands import create_migration_commands
182
445
 
183
446
  self._migration_loader = SQLFileLoader()
184
- self._migration_commands = create_migration_commands(self) # type: ignore[arg-type]
447
+ self._migration_commands = create_migration_commands(self) # pyright: ignore
185
448
 
186
449
  def _ensure_migration_loader(self) -> "SQLFileLoader":
187
450
  """Get the migration SQL loader and auto-load files if needed.
@@ -193,8 +456,6 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
193
456
  migration_config = self.migration_config or {}
194
457
  script_location = migration_config.get("script_location", "migrations")
195
458
 
196
- from pathlib import Path
197
-
198
459
  migration_path = Path(script_location)
199
460
  if migration_path.exists() and not self._migration_loader.list_files():
200
461
  self._migration_loader.load_sql(migration_path)
@@ -202,7 +463,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
202
463
 
203
464
  return self._migration_loader
204
465
 
205
- def _ensure_migration_commands(self) -> "Union[SyncMigrationCommands, AsyncMigrationCommands]":
466
+ def _ensure_migration_commands(self) -> "SyncMigrationCommands | AsyncMigrationCommands":
206
467
  """Get the migration commands instance.
207
468
 
208
469
  Returns:
@@ -221,7 +482,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
221
482
  """
222
483
  return self._ensure_migration_loader()
223
484
 
224
- def load_migration_sql_files(self, *paths: "Union[str, Path]") -> None:
485
+ def load_migration_sql_files(self, *paths: "str | Path") -> None:
225
486
  """Load additional migration SQL files from specified paths.
226
487
 
227
488
  Args:
@@ -237,7 +498,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
237
498
  else:
238
499
  logger.warning("Migration path does not exist: %s", path_obj)
239
500
 
240
- def get_migration_commands(self) -> "Union[SyncMigrationCommands, AsyncMigrationCommands]":
501
+ def get_migration_commands(self) -> "SyncMigrationCommands | AsyncMigrationCommands":
241
502
  """Get migration commands for this configuration.
242
503
 
243
504
  Returns:
@@ -265,7 +526,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
265
526
 
266
527
  await cast("AsyncMigrationCommands", commands).downgrade(revision)
267
528
 
268
- async def get_current_migration(self, verbose: bool = False) -> "Optional[str]":
529
+ async def get_current_migration(self, verbose: bool = False) -> "str | None":
269
530
  """Get the current migration version.
270
531
 
271
532
  Args:
@@ -289,7 +550,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
289
550
 
290
551
  await cast("AsyncMigrationCommands", commands).revision(message, file_type)
291
552
 
292
- async def init_migrations(self, directory: "Optional[str]" = None, package: bool = True) -> None:
553
+ async def init_migrations(self, directory: "str | None" = None, package: bool = True) -> None:
293
554
  """Initialize migration directory structure.
294
555
 
295
556
  Args:
@@ -298,7 +559,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
298
559
  """
299
560
  if directory is None:
300
561
  migration_config = self.migration_config or {}
301
- directory = migration_config.get("script_location") or "migrations"
562
+ directory = str(migration_config.get("script_location") or "migrations")
302
563
 
303
564
  commands = self._ensure_migration_commands()
304
565
  assert directory is not None
@@ -309,7 +570,7 @@ class DatabaseConfigProtocol(ABC, Generic[ConnectionT, PoolT, DriverT]):
309
570
  class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
310
571
  """Base class for sync database configurations that do not implement a pool."""
311
572
 
312
- __slots__ = ("connection_config",)
573
+ __slots__ = ("connection_config", "extension_config")
313
574
  is_async: "ClassVar[bool]" = False
314
575
  supports_connection_pooling: "ClassVar[bool]" = False
315
576
  migration_tracker_type: "ClassVar[type[Any]]" = SyncMigrationTracker
@@ -317,16 +578,18 @@ class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
317
578
  def __init__(
318
579
  self,
319
580
  *,
320
- connection_config: Optional[dict[str, Any]] = None,
321
- migration_config: "Optional[Union[dict[str, Any], MigrationConfig]]" = None,
322
- statement_config: "Optional[StatementConfig]" = None,
323
- driver_features: "Optional[dict[str, Any]]" = None,
324
- bind_key: "Optional[str]" = None,
581
+ connection_config: dict[str, Any] | None = None,
582
+ migration_config: "dict[str, Any] | MigrationConfig | None" = None,
583
+ statement_config: "StatementConfig | None" = None,
584
+ driver_features: "dict[str, Any] | None" = None,
585
+ bind_key: "str | None" = None,
586
+ extension_config: "dict[str, dict[str, Any]] | None" = None,
325
587
  ) -> None:
326
588
  self.bind_key = bind_key
327
589
  self.pool_instance = None
328
590
  self.connection_config = connection_config or {}
329
- self.migration_config: Union[dict[str, Any], MigrationConfig] = migration_config or {}
591
+ self.extension_config: dict[str, dict[str, Any]] = extension_config or {}
592
+ self.migration_config: dict[str, Any] | MigrationConfig = migration_config or {}
330
593
  self._initialize_migration_components()
331
594
 
332
595
  if statement_config is None:
@@ -347,7 +610,7 @@ class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
347
610
  raise NotImplementedError
348
611
 
349
612
  def provide_session(
350
- self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
613
+ self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
351
614
  ) -> "AbstractContextManager[DriverT]":
352
615
  """Provide a database session context manager."""
353
616
  raise NotImplementedError
@@ -365,7 +628,7 @@ class NoPoolSyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
365
628
  class NoPoolAsyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
366
629
  """Base class for async database configurations that do not implement a pool."""
367
630
 
368
- __slots__ = ("connection_config",)
631
+ __slots__ = ("connection_config", "extension_config")
369
632
  is_async: "ClassVar[bool]" = True
370
633
  supports_connection_pooling: "ClassVar[bool]" = False
371
634
  migration_tracker_type: "ClassVar[type[Any]]" = AsyncMigrationTracker
@@ -373,16 +636,18 @@ class NoPoolAsyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
373
636
  def __init__(
374
637
  self,
375
638
  *,
376
- connection_config: "Optional[dict[str, Any]]" = None,
377
- migration_config: "Optional[Union[dict[str, Any], MigrationConfig]]" = None,
378
- statement_config: "Optional[StatementConfig]" = None,
379
- driver_features: "Optional[dict[str, Any]]" = None,
380
- bind_key: "Optional[str]" = None,
639
+ connection_config: "dict[str, Any] | None" = None,
640
+ migration_config: "dict[str, Any] | MigrationConfig | None" = None,
641
+ statement_config: "StatementConfig | None" = None,
642
+ driver_features: "dict[str, Any] | None" = None,
643
+ bind_key: "str | None" = None,
644
+ extension_config: "dict[str, dict[str, Any]] | None" = None,
381
645
  ) -> None:
382
646
  self.bind_key = bind_key
383
647
  self.pool_instance = None
384
648
  self.connection_config = connection_config or {}
385
- self.migration_config: Union[dict[str, Any], MigrationConfig] = migration_config or {}
649
+ self.extension_config: dict[str, dict[str, Any]] = extension_config or {}
650
+ self.migration_config: dict[str, Any] | MigrationConfig = migration_config or {}
386
651
  self._initialize_migration_components()
387
652
 
388
653
  if statement_config is None:
@@ -403,7 +668,7 @@ class NoPoolAsyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
403
668
  raise NotImplementedError
404
669
 
405
670
  def provide_session(
406
- self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
671
+ self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
407
672
  ) -> "AbstractAsyncContextManager[DriverT]":
408
673
  """Provide a database session context manager."""
409
674
  raise NotImplementedError
@@ -421,7 +686,7 @@ class NoPoolAsyncConfig(DatabaseConfigProtocol[ConnectionT, None, DriverT]):
421
686
  class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
422
687
  """Base class for sync database configurations with connection pooling."""
423
688
 
424
- __slots__ = ("pool_config",)
689
+ __slots__ = ("extension_config", "pool_config")
425
690
  is_async: "ClassVar[bool]" = False
426
691
  supports_connection_pooling: "ClassVar[bool]" = True
427
692
  migration_tracker_type: "ClassVar[type[Any]]" = SyncMigrationTracker
@@ -429,17 +694,19 @@ class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
429
694
  def __init__(
430
695
  self,
431
696
  *,
432
- pool_config: "Optional[dict[str, Any]]" = None,
433
- pool_instance: "Optional[PoolT]" = None,
434
- migration_config: "Optional[Union[dict[str, Any], MigrationConfig]]" = None,
435
- statement_config: "Optional[StatementConfig]" = None,
436
- driver_features: "Optional[dict[str, Any]]" = None,
437
- bind_key: "Optional[str]" = None,
697
+ pool_config: "dict[str, Any] | None" = None,
698
+ pool_instance: "PoolT | None" = None,
699
+ migration_config: "dict[str, Any] | MigrationConfig | None" = None,
700
+ statement_config: "StatementConfig | None" = None,
701
+ driver_features: "dict[str, Any] | None" = None,
702
+ bind_key: "str | None" = None,
703
+ extension_config: "dict[str, dict[str, Any]] | None" = None,
438
704
  ) -> None:
439
705
  self.bind_key = bind_key
440
706
  self.pool_instance = pool_instance
441
707
  self.pool_config = pool_config or {}
442
- self.migration_config: Union[dict[str, Any], MigrationConfig] = migration_config or {}
708
+ self.extension_config: dict[str, dict[str, Any]] = extension_config or {}
709
+ self.migration_config: dict[str, Any] | MigrationConfig = migration_config or {}
443
710
  self._initialize_migration_components()
444
711
 
445
712
  if statement_config is None:
@@ -481,7 +748,7 @@ class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
481
748
  raise NotImplementedError
482
749
 
483
750
  def provide_session(
484
- self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
751
+ self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
485
752
  ) -> "AbstractContextManager[DriverT]":
486
753
  """Provide a database session context manager."""
487
754
  raise NotImplementedError
@@ -500,7 +767,7 @@ class SyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
500
767
  class AsyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
501
768
  """Base class for async database configurations with connection pooling."""
502
769
 
503
- __slots__ = ("pool_config",)
770
+ __slots__ = ("extension_config", "pool_config")
504
771
  is_async: "ClassVar[bool]" = True
505
772
  supports_connection_pooling: "ClassVar[bool]" = True
506
773
  migration_tracker_type: "ClassVar[type[Any]]" = AsyncMigrationTracker
@@ -508,17 +775,19 @@ class AsyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
508
775
  def __init__(
509
776
  self,
510
777
  *,
511
- pool_config: "Optional[dict[str, Any]]" = None,
512
- pool_instance: "Optional[PoolT]" = None,
513
- migration_config: "Optional[Union[dict[str, Any], MigrationConfig]]" = None,
514
- statement_config: "Optional[StatementConfig]" = None,
515
- driver_features: "Optional[dict[str, Any]]" = None,
516
- bind_key: "Optional[str]" = None,
778
+ pool_config: "dict[str, Any] | None" = None,
779
+ pool_instance: "PoolT | None" = None,
780
+ migration_config: "dict[str, Any] | MigrationConfig | None" = None,
781
+ statement_config: "StatementConfig | None" = None,
782
+ driver_features: "dict[str, Any] | None" = None,
783
+ bind_key: "str | None" = None,
784
+ extension_config: "dict[str, dict[str, Any]] | None" = None,
517
785
  ) -> None:
518
786
  self.bind_key = bind_key
519
787
  self.pool_instance = pool_instance
520
788
  self.pool_config = pool_config or {}
521
- self.migration_config: Union[dict[str, Any], MigrationConfig] = migration_config or {}
789
+ self.extension_config: dict[str, dict[str, Any]] = extension_config or {}
790
+ self.migration_config: dict[str, Any] | MigrationConfig = migration_config or {}
522
791
  self._initialize_migration_components()
523
792
 
524
793
  if statement_config is None:
@@ -562,7 +831,7 @@ class AsyncDatabaseConfig(DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]):
562
831
  raise NotImplementedError
563
832
 
564
833
  def provide_session(
565
- self, *args: Any, statement_config: "Optional[StatementConfig]" = None, **kwargs: Any
834
+ self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
566
835
  ) -> "AbstractAsyncContextManager[DriverT]":
567
836
  """Provide a database session context manager."""
568
837
  raise NotImplementedError