sqlspec 0.32.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 (262) hide show
  1. sqlspec/__init__.py +104 -0
  2. sqlspec/__main__.py +12 -0
  3. sqlspec/__metadata__.py +14 -0
  4. sqlspec/_serialization.py +312 -0
  5. sqlspec/_typing.py +784 -0
  6. sqlspec/adapters/__init__.py +0 -0
  7. sqlspec/adapters/adbc/__init__.py +5 -0
  8. sqlspec/adapters/adbc/_types.py +12 -0
  9. sqlspec/adapters/adbc/adk/__init__.py +5 -0
  10. sqlspec/adapters/adbc/adk/store.py +880 -0
  11. sqlspec/adapters/adbc/config.py +436 -0
  12. sqlspec/adapters/adbc/data_dictionary.py +537 -0
  13. sqlspec/adapters/adbc/driver.py +841 -0
  14. sqlspec/adapters/adbc/litestar/__init__.py +5 -0
  15. sqlspec/adapters/adbc/litestar/store.py +504 -0
  16. sqlspec/adapters/adbc/type_converter.py +153 -0
  17. sqlspec/adapters/aiosqlite/__init__.py +29 -0
  18. sqlspec/adapters/aiosqlite/_types.py +13 -0
  19. sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
  20. sqlspec/adapters/aiosqlite/adk/store.py +536 -0
  21. sqlspec/adapters/aiosqlite/config.py +310 -0
  22. sqlspec/adapters/aiosqlite/data_dictionary.py +260 -0
  23. sqlspec/adapters/aiosqlite/driver.py +463 -0
  24. sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
  25. sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
  26. sqlspec/adapters/aiosqlite/pool.py +500 -0
  27. sqlspec/adapters/asyncmy/__init__.py +25 -0
  28. sqlspec/adapters/asyncmy/_types.py +12 -0
  29. sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
  30. sqlspec/adapters/asyncmy/adk/store.py +503 -0
  31. sqlspec/adapters/asyncmy/config.py +246 -0
  32. sqlspec/adapters/asyncmy/data_dictionary.py +241 -0
  33. sqlspec/adapters/asyncmy/driver.py +632 -0
  34. sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
  35. sqlspec/adapters/asyncmy/litestar/store.py +296 -0
  36. sqlspec/adapters/asyncpg/__init__.py +23 -0
  37. sqlspec/adapters/asyncpg/_type_handlers.py +76 -0
  38. sqlspec/adapters/asyncpg/_types.py +23 -0
  39. sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
  40. sqlspec/adapters/asyncpg/adk/store.py +460 -0
  41. sqlspec/adapters/asyncpg/config.py +464 -0
  42. sqlspec/adapters/asyncpg/data_dictionary.py +321 -0
  43. sqlspec/adapters/asyncpg/driver.py +720 -0
  44. sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
  45. sqlspec/adapters/asyncpg/litestar/store.py +253 -0
  46. sqlspec/adapters/bigquery/__init__.py +18 -0
  47. sqlspec/adapters/bigquery/_types.py +12 -0
  48. sqlspec/adapters/bigquery/adk/__init__.py +5 -0
  49. sqlspec/adapters/bigquery/adk/store.py +585 -0
  50. sqlspec/adapters/bigquery/config.py +298 -0
  51. sqlspec/adapters/bigquery/data_dictionary.py +256 -0
  52. sqlspec/adapters/bigquery/driver.py +1073 -0
  53. sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
  54. sqlspec/adapters/bigquery/litestar/store.py +327 -0
  55. sqlspec/adapters/bigquery/type_converter.py +125 -0
  56. sqlspec/adapters/duckdb/__init__.py +24 -0
  57. sqlspec/adapters/duckdb/_types.py +12 -0
  58. sqlspec/adapters/duckdb/adk/__init__.py +14 -0
  59. sqlspec/adapters/duckdb/adk/store.py +563 -0
  60. sqlspec/adapters/duckdb/config.py +396 -0
  61. sqlspec/adapters/duckdb/data_dictionary.py +264 -0
  62. sqlspec/adapters/duckdb/driver.py +604 -0
  63. sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
  64. sqlspec/adapters/duckdb/litestar/store.py +332 -0
  65. sqlspec/adapters/duckdb/pool.py +273 -0
  66. sqlspec/adapters/duckdb/type_converter.py +133 -0
  67. sqlspec/adapters/oracledb/__init__.py +32 -0
  68. sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
  69. sqlspec/adapters/oracledb/_types.py +39 -0
  70. sqlspec/adapters/oracledb/_uuid_handlers.py +130 -0
  71. sqlspec/adapters/oracledb/adk/__init__.py +5 -0
  72. sqlspec/adapters/oracledb/adk/store.py +1632 -0
  73. sqlspec/adapters/oracledb/config.py +469 -0
  74. sqlspec/adapters/oracledb/data_dictionary.py +717 -0
  75. sqlspec/adapters/oracledb/driver.py +1493 -0
  76. sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
  77. sqlspec/adapters/oracledb/litestar/store.py +765 -0
  78. sqlspec/adapters/oracledb/migrations.py +532 -0
  79. sqlspec/adapters/oracledb/type_converter.py +207 -0
  80. sqlspec/adapters/psqlpy/__init__.py +16 -0
  81. sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
  82. sqlspec/adapters/psqlpy/_types.py +12 -0
  83. sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
  84. sqlspec/adapters/psqlpy/adk/store.py +483 -0
  85. sqlspec/adapters/psqlpy/config.py +271 -0
  86. sqlspec/adapters/psqlpy/data_dictionary.py +179 -0
  87. sqlspec/adapters/psqlpy/driver.py +892 -0
  88. sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
  89. sqlspec/adapters/psqlpy/litestar/store.py +272 -0
  90. sqlspec/adapters/psqlpy/type_converter.py +102 -0
  91. sqlspec/adapters/psycopg/__init__.py +32 -0
  92. sqlspec/adapters/psycopg/_type_handlers.py +90 -0
  93. sqlspec/adapters/psycopg/_types.py +18 -0
  94. sqlspec/adapters/psycopg/adk/__init__.py +5 -0
  95. sqlspec/adapters/psycopg/adk/store.py +962 -0
  96. sqlspec/adapters/psycopg/config.py +487 -0
  97. sqlspec/adapters/psycopg/data_dictionary.py +630 -0
  98. sqlspec/adapters/psycopg/driver.py +1336 -0
  99. sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
  100. sqlspec/adapters/psycopg/litestar/store.py +554 -0
  101. sqlspec/adapters/spanner/__init__.py +38 -0
  102. sqlspec/adapters/spanner/_type_handlers.py +186 -0
  103. sqlspec/adapters/spanner/_types.py +12 -0
  104. sqlspec/adapters/spanner/adk/__init__.py +5 -0
  105. sqlspec/adapters/spanner/adk/store.py +435 -0
  106. sqlspec/adapters/spanner/config.py +241 -0
  107. sqlspec/adapters/spanner/data_dictionary.py +95 -0
  108. sqlspec/adapters/spanner/dialect/__init__.py +6 -0
  109. sqlspec/adapters/spanner/dialect/_spangres.py +52 -0
  110. sqlspec/adapters/spanner/dialect/_spanner.py +123 -0
  111. sqlspec/adapters/spanner/driver.py +366 -0
  112. sqlspec/adapters/spanner/litestar/__init__.py +5 -0
  113. sqlspec/adapters/spanner/litestar/store.py +266 -0
  114. sqlspec/adapters/spanner/type_converter.py +46 -0
  115. sqlspec/adapters/sqlite/__init__.py +18 -0
  116. sqlspec/adapters/sqlite/_type_handlers.py +86 -0
  117. sqlspec/adapters/sqlite/_types.py +11 -0
  118. sqlspec/adapters/sqlite/adk/__init__.py +5 -0
  119. sqlspec/adapters/sqlite/adk/store.py +582 -0
  120. sqlspec/adapters/sqlite/config.py +221 -0
  121. sqlspec/adapters/sqlite/data_dictionary.py +256 -0
  122. sqlspec/adapters/sqlite/driver.py +527 -0
  123. sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
  124. sqlspec/adapters/sqlite/litestar/store.py +318 -0
  125. sqlspec/adapters/sqlite/pool.py +140 -0
  126. sqlspec/base.py +811 -0
  127. sqlspec/builder/__init__.py +146 -0
  128. sqlspec/builder/_base.py +900 -0
  129. sqlspec/builder/_column.py +517 -0
  130. sqlspec/builder/_ddl.py +1642 -0
  131. sqlspec/builder/_delete.py +84 -0
  132. sqlspec/builder/_dml.py +381 -0
  133. sqlspec/builder/_expression_wrappers.py +46 -0
  134. sqlspec/builder/_factory.py +1537 -0
  135. sqlspec/builder/_insert.py +315 -0
  136. sqlspec/builder/_join.py +375 -0
  137. sqlspec/builder/_merge.py +848 -0
  138. sqlspec/builder/_parsing_utils.py +297 -0
  139. sqlspec/builder/_select.py +1615 -0
  140. sqlspec/builder/_update.py +161 -0
  141. sqlspec/builder/_vector_expressions.py +259 -0
  142. sqlspec/cli.py +764 -0
  143. sqlspec/config.py +1540 -0
  144. sqlspec/core/__init__.py +305 -0
  145. sqlspec/core/cache.py +785 -0
  146. sqlspec/core/compiler.py +603 -0
  147. sqlspec/core/filters.py +872 -0
  148. sqlspec/core/hashing.py +274 -0
  149. sqlspec/core/metrics.py +83 -0
  150. sqlspec/core/parameters/__init__.py +64 -0
  151. sqlspec/core/parameters/_alignment.py +266 -0
  152. sqlspec/core/parameters/_converter.py +413 -0
  153. sqlspec/core/parameters/_processor.py +341 -0
  154. sqlspec/core/parameters/_registry.py +201 -0
  155. sqlspec/core/parameters/_transformers.py +226 -0
  156. sqlspec/core/parameters/_types.py +430 -0
  157. sqlspec/core/parameters/_validator.py +123 -0
  158. sqlspec/core/pipeline.py +187 -0
  159. sqlspec/core/result.py +1124 -0
  160. sqlspec/core/splitter.py +940 -0
  161. sqlspec/core/stack.py +163 -0
  162. sqlspec/core/statement.py +835 -0
  163. sqlspec/core/type_conversion.py +235 -0
  164. sqlspec/driver/__init__.py +36 -0
  165. sqlspec/driver/_async.py +1027 -0
  166. sqlspec/driver/_common.py +1236 -0
  167. sqlspec/driver/_sync.py +1025 -0
  168. sqlspec/driver/mixins/__init__.py +7 -0
  169. sqlspec/driver/mixins/_result_tools.py +61 -0
  170. sqlspec/driver/mixins/_sql_translator.py +122 -0
  171. sqlspec/driver/mixins/_storage.py +311 -0
  172. sqlspec/exceptions.py +321 -0
  173. sqlspec/extensions/__init__.py +0 -0
  174. sqlspec/extensions/adk/__init__.py +53 -0
  175. sqlspec/extensions/adk/_types.py +51 -0
  176. sqlspec/extensions/adk/converters.py +172 -0
  177. sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
  178. sqlspec/extensions/adk/migrations/__init__.py +0 -0
  179. sqlspec/extensions/adk/service.py +181 -0
  180. sqlspec/extensions/adk/store.py +536 -0
  181. sqlspec/extensions/aiosql/__init__.py +10 -0
  182. sqlspec/extensions/aiosql/adapter.py +471 -0
  183. sqlspec/extensions/fastapi/__init__.py +19 -0
  184. sqlspec/extensions/fastapi/extension.py +341 -0
  185. sqlspec/extensions/fastapi/providers.py +543 -0
  186. sqlspec/extensions/flask/__init__.py +36 -0
  187. sqlspec/extensions/flask/_state.py +72 -0
  188. sqlspec/extensions/flask/_utils.py +40 -0
  189. sqlspec/extensions/flask/extension.py +402 -0
  190. sqlspec/extensions/litestar/__init__.py +23 -0
  191. sqlspec/extensions/litestar/_utils.py +52 -0
  192. sqlspec/extensions/litestar/cli.py +92 -0
  193. sqlspec/extensions/litestar/config.py +90 -0
  194. sqlspec/extensions/litestar/handlers.py +316 -0
  195. sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
  196. sqlspec/extensions/litestar/migrations/__init__.py +3 -0
  197. sqlspec/extensions/litestar/plugin.py +638 -0
  198. sqlspec/extensions/litestar/providers.py +454 -0
  199. sqlspec/extensions/litestar/store.py +265 -0
  200. sqlspec/extensions/otel/__init__.py +58 -0
  201. sqlspec/extensions/prometheus/__init__.py +107 -0
  202. sqlspec/extensions/starlette/__init__.py +10 -0
  203. sqlspec/extensions/starlette/_state.py +26 -0
  204. sqlspec/extensions/starlette/_utils.py +52 -0
  205. sqlspec/extensions/starlette/extension.py +257 -0
  206. sqlspec/extensions/starlette/middleware.py +154 -0
  207. sqlspec/loader.py +716 -0
  208. sqlspec/migrations/__init__.py +36 -0
  209. sqlspec/migrations/base.py +728 -0
  210. sqlspec/migrations/commands.py +1140 -0
  211. sqlspec/migrations/context.py +142 -0
  212. sqlspec/migrations/fix.py +203 -0
  213. sqlspec/migrations/loaders.py +450 -0
  214. sqlspec/migrations/runner.py +1024 -0
  215. sqlspec/migrations/templates.py +234 -0
  216. sqlspec/migrations/tracker.py +403 -0
  217. sqlspec/migrations/utils.py +256 -0
  218. sqlspec/migrations/validation.py +203 -0
  219. sqlspec/observability/__init__.py +22 -0
  220. sqlspec/observability/_config.py +228 -0
  221. sqlspec/observability/_diagnostics.py +67 -0
  222. sqlspec/observability/_dispatcher.py +151 -0
  223. sqlspec/observability/_observer.py +180 -0
  224. sqlspec/observability/_runtime.py +381 -0
  225. sqlspec/observability/_spans.py +158 -0
  226. sqlspec/protocols.py +530 -0
  227. sqlspec/py.typed +0 -0
  228. sqlspec/storage/__init__.py +46 -0
  229. sqlspec/storage/_utils.py +104 -0
  230. sqlspec/storage/backends/__init__.py +1 -0
  231. sqlspec/storage/backends/base.py +163 -0
  232. sqlspec/storage/backends/fsspec.py +398 -0
  233. sqlspec/storage/backends/local.py +377 -0
  234. sqlspec/storage/backends/obstore.py +580 -0
  235. sqlspec/storage/errors.py +104 -0
  236. sqlspec/storage/pipeline.py +604 -0
  237. sqlspec/storage/registry.py +289 -0
  238. sqlspec/typing.py +219 -0
  239. sqlspec/utils/__init__.py +31 -0
  240. sqlspec/utils/arrow_helpers.py +95 -0
  241. sqlspec/utils/config_resolver.py +153 -0
  242. sqlspec/utils/correlation.py +132 -0
  243. sqlspec/utils/data_transformation.py +114 -0
  244. sqlspec/utils/dependencies.py +79 -0
  245. sqlspec/utils/deprecation.py +113 -0
  246. sqlspec/utils/fixtures.py +250 -0
  247. sqlspec/utils/logging.py +172 -0
  248. sqlspec/utils/module_loader.py +273 -0
  249. sqlspec/utils/portal.py +325 -0
  250. sqlspec/utils/schema.py +288 -0
  251. sqlspec/utils/serializers.py +396 -0
  252. sqlspec/utils/singleton.py +41 -0
  253. sqlspec/utils/sync_tools.py +277 -0
  254. sqlspec/utils/text.py +108 -0
  255. sqlspec/utils/type_converters.py +99 -0
  256. sqlspec/utils/type_guards.py +1324 -0
  257. sqlspec/utils/version.py +444 -0
  258. sqlspec-0.32.0.dist-info/METADATA +202 -0
  259. sqlspec-0.32.0.dist-info/RECORD +262 -0
  260. sqlspec-0.32.0.dist-info/WHEEL +4 -0
  261. sqlspec-0.32.0.dist-info/entry_points.txt +2 -0
  262. sqlspec-0.32.0.dist-info/licenses/LICENSE +21 -0
@@ -0,0 +1,464 @@
1
+ """AsyncPG database configuration with direct field-based configuration."""
2
+
3
+ import logging
4
+ from collections.abc import Callable
5
+ from contextlib import asynccontextmanager
6
+ from typing import TYPE_CHECKING, Any, ClassVar, TypedDict
7
+
8
+ from asyncpg import Connection, Record
9
+ from asyncpg import create_pool as asyncpg_create_pool
10
+ from asyncpg.connection import ConnectionMeta
11
+ from asyncpg.pool import Pool, PoolConnectionProxy, PoolConnectionProxyMeta
12
+ from typing_extensions import NotRequired
13
+
14
+ from sqlspec.adapters.asyncpg._type_handlers import register_json_codecs, register_pgvector_support
15
+ from sqlspec.adapters.asyncpg._types import AsyncpgConnection, AsyncpgPool, AsyncpgPreparedStatement
16
+ from sqlspec.adapters.asyncpg.driver import (
17
+ AsyncpgCursor,
18
+ AsyncpgDriver,
19
+ AsyncpgExceptionHandler,
20
+ asyncpg_statement_config,
21
+ build_asyncpg_statement_config,
22
+ )
23
+ from sqlspec.config import AsyncDatabaseConfig, ExtensionConfigs
24
+ from sqlspec.exceptions import ImproperConfigurationError
25
+ from sqlspec.typing import ALLOYDB_CONNECTOR_INSTALLED, CLOUD_SQL_CONNECTOR_INSTALLED, PGVECTOR_INSTALLED
26
+ from sqlspec.utils.serializers import from_json, to_json
27
+
28
+ if TYPE_CHECKING:
29
+ from asyncio.events import AbstractEventLoop
30
+ from collections.abc import AsyncGenerator, Awaitable
31
+
32
+ from sqlspec.core import StatementConfig
33
+ from sqlspec.observability import ObservabilityConfig
34
+
35
+
36
+ __all__ = ("AsyncpgConfig", "AsyncpgConnectionConfig", "AsyncpgDriverFeatures", "AsyncpgPoolConfig")
37
+
38
+ logger = logging.getLogger("sqlspec")
39
+
40
+
41
+ class AsyncpgConnectionConfig(TypedDict):
42
+ """TypedDict for AsyncPG connection parameters."""
43
+
44
+ dsn: NotRequired[str]
45
+ host: NotRequired[str]
46
+ port: NotRequired[int]
47
+ user: NotRequired[str]
48
+ password: NotRequired[str]
49
+ database: NotRequired[str]
50
+ ssl: NotRequired[Any]
51
+ passfile: NotRequired[str]
52
+ direct_tls: NotRequired[bool]
53
+ connect_timeout: NotRequired[float]
54
+ command_timeout: NotRequired[float]
55
+ statement_cache_size: NotRequired[int]
56
+ max_cached_statement_lifetime: NotRequired[int]
57
+ max_cacheable_statement_size: NotRequired[int]
58
+ server_settings: NotRequired[dict[str, str]]
59
+
60
+
61
+ class AsyncpgPoolConfig(AsyncpgConnectionConfig):
62
+ """TypedDict for AsyncPG pool parameters, inheriting connection parameters."""
63
+
64
+ min_size: NotRequired[int]
65
+ max_size: NotRequired[int]
66
+ max_queries: NotRequired[int]
67
+ max_inactive_connection_lifetime: NotRequired[float]
68
+ setup: NotRequired["Callable[[AsyncpgConnection], Awaitable[None]]"]
69
+ init: NotRequired["Callable[[AsyncpgConnection], Awaitable[None]]"]
70
+ loop: NotRequired["AbstractEventLoop"]
71
+ connection_class: NotRequired[type["AsyncpgConnection"]]
72
+ record_class: NotRequired[type[Record]]
73
+ extra: NotRequired[dict[str, Any]]
74
+
75
+
76
+ class AsyncpgDriverFeatures(TypedDict):
77
+ """AsyncPG driver feature flags.
78
+
79
+ json_serializer: Custom JSON serializer function for PostgreSQL JSON/JSONB types.
80
+ Defaults to sqlspec.utils.serializers.to_json.
81
+ Use for performance optimization (e.g., orjson) or custom encoding behavior.
82
+ Applied when enable_json_codecs is True.
83
+ json_deserializer: Custom JSON deserializer function for PostgreSQL JSON/JSONB types.
84
+ Defaults to sqlspec.utils.serializers.from_json.
85
+ Use for performance optimization (e.g., orjson) or custom decoding behavior.
86
+ Applied when enable_json_codecs is True.
87
+ enable_json_codecs: Enable automatic JSON/JSONB codec registration on connections.
88
+ Defaults to True for seamless Python dict/list to PostgreSQL JSON/JSONB conversion.
89
+ Set to False to disable automatic codec registration (manual handling required).
90
+ enable_pgvector: Enable pgvector extension support for vector similarity search.
91
+ Requires pgvector-python package (pip install pgvector) and PostgreSQL with pgvector extension.
92
+ Defaults to True when pgvector-python is installed.
93
+ Provides automatic conversion between Python objects and PostgreSQL vector types.
94
+ Enables vector similarity operations and index support.
95
+ enable_cloud_sql: Enable Google Cloud SQL connector integration.
96
+ Requires cloud-sql-python-connector package.
97
+ Defaults to False (explicit opt-in required).
98
+ Auto-configures IAM authentication, SSL, and IP routing.
99
+ Mutually exclusive with enable_alloydb.
100
+ cloud_sql_instance: Cloud SQL instance connection name.
101
+ Format: "project:region:instance"
102
+ Required when enable_cloud_sql is True.
103
+ cloud_sql_enable_iam_auth: Enable IAM database authentication.
104
+ Defaults to False for passwordless authentication.
105
+ When False, requires user/password in pool_config.
106
+ cloud_sql_ip_type: IP address type for connection.
107
+ Options: "PUBLIC", "PRIVATE", "PSC"
108
+ Defaults to "PRIVATE".
109
+ enable_alloydb: Enable Google AlloyDB connector integration.
110
+ Requires cloud-alloydb-python-connector package.
111
+ Defaults to False (explicit opt-in required).
112
+ Auto-configures IAM authentication and private networking.
113
+ Mutually exclusive with enable_cloud_sql.
114
+ alloydb_instance_uri: AlloyDB instance URI.
115
+ Format: "projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE"
116
+ Required when enable_alloydb is True.
117
+ alloydb_enable_iam_auth: Enable IAM database authentication.
118
+ Defaults to False for passwordless authentication.
119
+ alloydb_ip_type: IP address type for connection.
120
+ Options: "PUBLIC", "PRIVATE", "PSC"
121
+ Defaults to "PRIVATE".
122
+ """
123
+
124
+ json_serializer: NotRequired[Callable[[Any], str]]
125
+ json_deserializer: NotRequired[Callable[[str], Any]]
126
+ enable_json_codecs: NotRequired[bool]
127
+ enable_pgvector: NotRequired[bool]
128
+ enable_cloud_sql: NotRequired[bool]
129
+ cloud_sql_instance: NotRequired[str]
130
+ cloud_sql_enable_iam_auth: NotRequired[bool]
131
+ cloud_sql_ip_type: NotRequired[str]
132
+ enable_alloydb: NotRequired[bool]
133
+ alloydb_instance_uri: NotRequired[str]
134
+ alloydb_enable_iam_auth: NotRequired[bool]
135
+ alloydb_ip_type: NotRequired[str]
136
+
137
+
138
+ class AsyncpgConfig(AsyncDatabaseConfig[AsyncpgConnection, "Pool[Record]", AsyncpgDriver]):
139
+ """Configuration for AsyncPG database connections using TypedDict."""
140
+
141
+ driver_type: "ClassVar[type[AsyncpgDriver]]" = AsyncpgDriver
142
+ connection_type: "ClassVar[type[AsyncpgConnection]]" = type(AsyncpgConnection) # type: ignore[assignment]
143
+ supports_transactional_ddl: "ClassVar[bool]" = True
144
+ supports_native_arrow_export: "ClassVar[bool]" = True
145
+ supports_native_arrow_import: "ClassVar[bool]" = True
146
+ supports_native_parquet_export: "ClassVar[bool]" = True
147
+ supports_native_parquet_import: "ClassVar[bool]" = True
148
+
149
+ def __init__(
150
+ self,
151
+ *,
152
+ pool_config: "AsyncpgPoolConfig | dict[str, Any] | None" = None,
153
+ pool_instance: "Pool[Record] | None" = None,
154
+ migration_config: "dict[str, Any] | None" = None,
155
+ statement_config: "StatementConfig | None" = None,
156
+ driver_features: "AsyncpgDriverFeatures | dict[str, Any] | None" = None,
157
+ bind_key: "str | None" = None,
158
+ extension_config: "ExtensionConfigs | None" = None,
159
+ observability_config: "ObservabilityConfig | None" = None,
160
+ ) -> None:
161
+ """Initialize AsyncPG configuration.
162
+
163
+ Args:
164
+ pool_config: Pool configuration parameters (TypedDict or dict)
165
+ pool_instance: Existing pool instance to use
166
+ migration_config: Migration configuration
167
+ statement_config: Statement configuration override
168
+ driver_features: Driver features configuration (TypedDict or dict)
169
+ bind_key: Optional unique identifier for this configuration
170
+ extension_config: Extension-specific configuration (e.g., Litestar plugin settings)
171
+ observability_config: Adapter-level observability overrides for lifecycle hooks and observers
172
+ """
173
+ features_dict: dict[str, Any] = dict(driver_features) if driver_features else {}
174
+
175
+ serializer = features_dict.setdefault("json_serializer", to_json)
176
+ deserializer = features_dict.setdefault("json_deserializer", from_json)
177
+ features_dict.setdefault("enable_json_codecs", True)
178
+ features_dict.setdefault("enable_pgvector", PGVECTOR_INSTALLED)
179
+ features_dict.setdefault("enable_cloud_sql", False)
180
+ features_dict.setdefault("enable_alloydb", False)
181
+
182
+ base_statement_config = statement_config or build_asyncpg_statement_config(
183
+ json_serializer=serializer, json_deserializer=deserializer
184
+ )
185
+
186
+ super().__init__(
187
+ pool_config=dict(pool_config) if pool_config else {},
188
+ pool_instance=pool_instance,
189
+ migration_config=migration_config,
190
+ statement_config=base_statement_config,
191
+ driver_features=features_dict,
192
+ bind_key=bind_key,
193
+ extension_config=extension_config,
194
+ observability_config=observability_config,
195
+ )
196
+
197
+ self._cloud_sql_connector: Any | None = None
198
+ self._alloydb_connector: Any | None = None
199
+
200
+ self._validate_connector_config()
201
+
202
+ def _validate_connector_config(self) -> None:
203
+ """Validate Google Cloud connector configuration.
204
+
205
+ Raises:
206
+ ImproperConfigurationError: If configuration is invalid.
207
+ """
208
+ enable_cloud_sql = self.driver_features.get("enable_cloud_sql", False)
209
+ enable_alloydb = self.driver_features.get("enable_alloydb", False)
210
+
211
+ if enable_cloud_sql and enable_alloydb:
212
+ msg = "Cannot enable both Cloud SQL and AlloyDB connectors simultaneously. Use separate configs for each database."
213
+ raise ImproperConfigurationError(msg)
214
+
215
+ if enable_cloud_sql:
216
+ if not CLOUD_SQL_CONNECTOR_INSTALLED:
217
+ msg = "cloud-sql-python-connector package not installed. Install with: pip install cloud-sql-python-connector"
218
+ raise ImproperConfigurationError(msg)
219
+
220
+ instance = self.driver_features.get("cloud_sql_instance")
221
+ if not instance:
222
+ msg = "cloud_sql_instance required when enable_cloud_sql is True. Format: 'project:region:instance'"
223
+ raise ImproperConfigurationError(msg)
224
+
225
+ cloud_sql_instance_parts_expected = 2
226
+ if instance.count(":") != cloud_sql_instance_parts_expected:
227
+ msg = f"Invalid Cloud SQL instance format: {instance}. Expected format: 'project:region:instance'"
228
+ raise ImproperConfigurationError(msg)
229
+
230
+ elif enable_alloydb:
231
+ if not ALLOYDB_CONNECTOR_INSTALLED:
232
+ msg = "cloud-alloydb-python-connector package not installed. Install with: pip install cloud-alloydb-python-connector"
233
+ raise ImproperConfigurationError(msg)
234
+
235
+ instance_uri = self.driver_features.get("alloydb_instance_uri")
236
+ if not instance_uri:
237
+ msg = "alloydb_instance_uri required when enable_alloydb is True. Format: 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE'"
238
+ raise ImproperConfigurationError(msg)
239
+
240
+ if not instance_uri.startswith("projects/"):
241
+ msg = f"Invalid AlloyDB instance URI format: {instance_uri}. Expected format: 'projects/PROJECT/locations/REGION/clusters/CLUSTER/instances/INSTANCE'"
242
+ raise ImproperConfigurationError(msg)
243
+
244
+ def _get_pool_config_dict(self) -> "dict[str, Any]":
245
+ """Get pool configuration as plain dict for external library.
246
+
247
+ Returns:
248
+ Dictionary with pool parameters, filtering out None values.
249
+ """
250
+ config: dict[str, Any] = dict(self.pool_config)
251
+ extras = config.pop("extra", {})
252
+ config.update(extras)
253
+ return {k: v for k, v in config.items() if v is not None}
254
+
255
+ def _setup_cloud_sql_connector(self, config: "dict[str, Any]") -> None:
256
+ """Setup Cloud SQL connector and configure pool for connection factory pattern.
257
+
258
+ Args:
259
+ config: Pool configuration dictionary to modify in-place.
260
+ """
261
+ from google.cloud.sql.connector import Connector # type: ignore[import-untyped,unused-ignore]
262
+
263
+ self._cloud_sql_connector = Connector()
264
+
265
+ user = config.get("user")
266
+ password = config.get("password")
267
+ database = config.get("database")
268
+
269
+ async def get_conn() -> "AsyncpgConnection":
270
+ conn_kwargs: dict[str, Any] = {
271
+ "instance_connection_string": self.driver_features["cloud_sql_instance"],
272
+ "driver": "asyncpg",
273
+ "enable_iam_auth": self.driver_features.get("cloud_sql_enable_iam_auth", False),
274
+ "ip_type": self.driver_features.get("cloud_sql_ip_type", "PRIVATE"),
275
+ }
276
+
277
+ if user:
278
+ conn_kwargs["user"] = user
279
+ if password:
280
+ conn_kwargs["password"] = password
281
+ if database:
282
+ conn_kwargs["db"] = database
283
+
284
+ conn: AsyncpgConnection = await self._cloud_sql_connector.connect_async(**conn_kwargs) # type: ignore[union-attr]
285
+ return conn
286
+
287
+ for key in ("dsn", "host", "port", "user", "password", "database"):
288
+ config.pop(key, None)
289
+
290
+ config["connect"] = get_conn
291
+
292
+ def _setup_alloydb_connector(self, config: "dict[str, Any]") -> None:
293
+ """Setup AlloyDB connector and configure pool for connection factory pattern.
294
+
295
+ Args:
296
+ config: Pool configuration dictionary to modify in-place.
297
+ """
298
+ from google.cloud.alloydb.connector import AsyncConnector # type: ignore[import-untyped,unused-ignore]
299
+
300
+ self._alloydb_connector = AsyncConnector()
301
+
302
+ user = config.get("user")
303
+ password = config.get("password")
304
+ database = config.get("database")
305
+
306
+ async def get_conn() -> "AsyncpgConnection":
307
+ conn_kwargs: dict[str, Any] = {
308
+ "instance_uri": self.driver_features["alloydb_instance_uri"],
309
+ "driver": "asyncpg",
310
+ "enable_iam_auth": self.driver_features.get("alloydb_enable_iam_auth", False),
311
+ "ip_type": self.driver_features.get("alloydb_ip_type", "PRIVATE"),
312
+ }
313
+
314
+ if user:
315
+ conn_kwargs["user"] = user
316
+ if password:
317
+ conn_kwargs["password"] = password
318
+ if database:
319
+ conn_kwargs["db"] = database
320
+
321
+ conn: AsyncpgConnection = await self._alloydb_connector.connect(**conn_kwargs) # type: ignore[union-attr]
322
+ return conn
323
+
324
+ for key in ("dsn", "host", "port", "user", "password", "database"):
325
+ config.pop(key, None)
326
+
327
+ config["connect"] = get_conn
328
+
329
+ async def _create_pool(self) -> "Pool[Record]":
330
+ """Create the actual async connection pool."""
331
+ config = self._get_pool_config_dict()
332
+
333
+ if self.driver_features.get("enable_cloud_sql", False):
334
+ self._setup_cloud_sql_connector(config)
335
+ elif self.driver_features.get("enable_alloydb", False):
336
+ self._setup_alloydb_connector(config)
337
+
338
+ config.setdefault("init", self._init_connection)
339
+
340
+ return await asyncpg_create_pool(**config)
341
+
342
+ async def _init_connection(self, connection: "AsyncpgConnection") -> None:
343
+ """Initialize connection with JSON codecs and pgvector support.
344
+
345
+ Args:
346
+ connection: AsyncPG connection to initialize.
347
+ """
348
+ if self.driver_features.get("enable_json_codecs", True):
349
+ await register_json_codecs(
350
+ connection,
351
+ encoder=self.driver_features.get("json_serializer", to_json),
352
+ decoder=self.driver_features.get("json_deserializer", from_json),
353
+ )
354
+
355
+ if self.driver_features.get("enable_pgvector", False):
356
+ await register_pgvector_support(connection)
357
+
358
+ async def _close_pool(self) -> None:
359
+ """Close the actual async connection pool and cleanup connectors."""
360
+ if self.pool_instance:
361
+ await self.pool_instance.close()
362
+
363
+ if self._cloud_sql_connector is not None:
364
+ await self._cloud_sql_connector.close_async()
365
+ self._cloud_sql_connector = None
366
+
367
+ if self._alloydb_connector is not None:
368
+ await self._alloydb_connector.close()
369
+ self._alloydb_connector = None
370
+
371
+ async def close_pool(self) -> None:
372
+ """Close the connection pool."""
373
+ await self._close_pool()
374
+
375
+ async def create_connection(self) -> "AsyncpgConnection":
376
+ """Create a single async connection from the pool.
377
+
378
+ Returns:
379
+ An AsyncPG connection instance.
380
+ """
381
+ if self.pool_instance is None:
382
+ self.pool_instance = await self._create_pool()
383
+ return await self.pool_instance.acquire()
384
+
385
+ @asynccontextmanager
386
+ async def provide_connection(self, *args: Any, **kwargs: Any) -> "AsyncGenerator[AsyncpgConnection, None]":
387
+ """Provide an async connection context manager.
388
+
389
+ Args:
390
+ *args: Additional arguments.
391
+ **kwargs: Additional keyword arguments.
392
+
393
+ Yields:
394
+ An AsyncPG connection instance.
395
+ """
396
+ if self.pool_instance is None:
397
+ self.pool_instance = await self._create_pool()
398
+ connection = None
399
+ try:
400
+ connection = await self.pool_instance.acquire()
401
+ yield connection
402
+ finally:
403
+ if connection is not None:
404
+ await self.pool_instance.release(connection)
405
+
406
+ @asynccontextmanager
407
+ async def provide_session(
408
+ self, *args: Any, statement_config: "StatementConfig | None" = None, **kwargs: Any
409
+ ) -> "AsyncGenerator[AsyncpgDriver, None]":
410
+ """Provide an async driver session context manager.
411
+
412
+ Args:
413
+ *args: Additional arguments.
414
+ statement_config: Optional statement configuration override.
415
+ **kwargs: Additional keyword arguments.
416
+
417
+ Yields:
418
+ An AsyncpgDriver instance.
419
+ """
420
+ async with self.provide_connection(*args, **kwargs) as connection:
421
+ final_statement_config = statement_config or self.statement_config or asyncpg_statement_config
422
+ driver = self.driver_type(
423
+ connection=connection, statement_config=final_statement_config, driver_features=self.driver_features
424
+ )
425
+ yield self._prepare_driver(driver)
426
+
427
+ async def provide_pool(self, *args: Any, **kwargs: Any) -> "Pool[Record]":
428
+ """Provide async pool instance.
429
+
430
+ Returns:
431
+ The async connection pool.
432
+ """
433
+ if not self.pool_instance:
434
+ self.pool_instance = await self.create_pool()
435
+ return self.pool_instance
436
+
437
+ def get_signature_namespace(self) -> "dict[str, Any]":
438
+ """Get the signature namespace for AsyncPG types.
439
+
440
+ This provides all AsyncPG-specific types that Litestar needs to recognize
441
+ to avoid serialization attempts.
442
+
443
+ Returns:
444
+ Dictionary mapping type names to types.
445
+ """
446
+
447
+ namespace = super().get_signature_namespace()
448
+ namespace.update({
449
+ "Connection": Connection,
450
+ "Pool": Pool,
451
+ "PoolConnectionProxy": PoolConnectionProxy,
452
+ "PoolConnectionProxyMeta": PoolConnectionProxyMeta,
453
+ "ConnectionMeta": ConnectionMeta,
454
+ "Record": Record,
455
+ "AsyncpgConnection": AsyncpgConnection,
456
+ "AsyncpgConnectionConfig": AsyncpgConnectionConfig,
457
+ "AsyncpgCursor": AsyncpgCursor,
458
+ "AsyncpgDriver": AsyncpgDriver,
459
+ "AsyncpgExceptionHandler": AsyncpgExceptionHandler,
460
+ "AsyncpgPool": AsyncpgPool,
461
+ "AsyncpgPoolConfig": AsyncpgPoolConfig,
462
+ "AsyncpgPreparedStatement": AsyncpgPreparedStatement,
463
+ })
464
+ return namespace