SQLAlchemy 2.0.36__cp313-cp313-win32.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 (273) hide show
  1. SQLAlchemy-2.0.36.dist-info/LICENSE +19 -0
  2. SQLAlchemy-2.0.36.dist-info/METADATA +243 -0
  3. SQLAlchemy-2.0.36.dist-info/RECORD +273 -0
  4. SQLAlchemy-2.0.36.dist-info/WHEEL +5 -0
  5. SQLAlchemy-2.0.36.dist-info/top_level.txt +1 -0
  6. sqlalchemy/__init__.py +294 -0
  7. sqlalchemy/connectors/__init__.py +18 -0
  8. sqlalchemy/connectors/aioodbc.py +174 -0
  9. sqlalchemy/connectors/asyncio.py +213 -0
  10. sqlalchemy/connectors/pyodbc.py +249 -0
  11. sqlalchemy/cyextension/__init__.py +6 -0
  12. sqlalchemy/cyextension/collections.cp313-win32.pyd +0 -0
  13. sqlalchemy/cyextension/collections.pyx +409 -0
  14. sqlalchemy/cyextension/immutabledict.cp313-win32.pyd +0 -0
  15. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  16. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  17. sqlalchemy/cyextension/processors.cp313-win32.pyd +0 -0
  18. sqlalchemy/cyextension/processors.pyx +68 -0
  19. sqlalchemy/cyextension/resultproxy.cp313-win32.pyd +0 -0
  20. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  21. sqlalchemy/cyextension/util.cp313-win32.pyd +0 -0
  22. sqlalchemy/cyextension/util.pyx +91 -0
  23. sqlalchemy/dialects/__init__.py +61 -0
  24. sqlalchemy/dialects/_typing.py +25 -0
  25. sqlalchemy/dialects/mssql/__init__.py +88 -0
  26. sqlalchemy/dialects/mssql/aioodbc.py +64 -0
  27. sqlalchemy/dialects/mssql/base.py +4010 -0
  28. sqlalchemy/dialects/mssql/information_schema.py +254 -0
  29. sqlalchemy/dialects/mssql/json.py +133 -0
  30. sqlalchemy/dialects/mssql/provision.py +162 -0
  31. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  32. sqlalchemy/dialects/mssql/pyodbc.py +745 -0
  33. sqlalchemy/dialects/mysql/__init__.py +101 -0
  34. sqlalchemy/dialects/mysql/aiomysql.py +333 -0
  35. sqlalchemy/dialects/mysql/asyncmy.py +337 -0
  36. sqlalchemy/dialects/mysql/base.py +3494 -0
  37. sqlalchemy/dialects/mysql/cymysql.py +84 -0
  38. sqlalchemy/dialects/mysql/dml.py +219 -0
  39. sqlalchemy/dialects/mysql/enumerated.py +244 -0
  40. sqlalchemy/dialects/mysql/expression.py +141 -0
  41. sqlalchemy/dialects/mysql/json.py +81 -0
  42. sqlalchemy/dialects/mysql/mariadb.py +32 -0
  43. sqlalchemy/dialects/mysql/mariadbconnector.py +277 -0
  44. sqlalchemy/dialects/mysql/mysqlconnector.py +180 -0
  45. sqlalchemy/dialects/mysql/mysqldb.py +303 -0
  46. sqlalchemy/dialects/mysql/provision.py +110 -0
  47. sqlalchemy/dialects/mysql/pymysql.py +137 -0
  48. sqlalchemy/dialects/mysql/pyodbc.py +138 -0
  49. sqlalchemy/dialects/mysql/reflection.py +677 -0
  50. sqlalchemy/dialects/mysql/reserved_words.py +571 -0
  51. sqlalchemy/dialects/mysql/types.py +774 -0
  52. sqlalchemy/dialects/oracle/__init__.py +67 -0
  53. sqlalchemy/dialects/oracle/base.py +3271 -0
  54. sqlalchemy/dialects/oracle/cx_oracle.py +1483 -0
  55. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  56. sqlalchemy/dialects/oracle/oracledb.py +431 -0
  57. sqlalchemy/dialects/oracle/provision.py +220 -0
  58. sqlalchemy/dialects/oracle/types.py +287 -0
  59. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  60. sqlalchemy/dialects/postgresql/_psycopg_common.py +187 -0
  61. sqlalchemy/dialects/postgresql/array.py +425 -0
  62. sqlalchemy/dialects/postgresql/asyncpg.py +1274 -0
  63. sqlalchemy/dialects/postgresql/base.py +5008 -0
  64. sqlalchemy/dialects/postgresql/dml.py +310 -0
  65. sqlalchemy/dialects/postgresql/ext.py +496 -0
  66. sqlalchemy/dialects/postgresql/hstore.py +397 -0
  67. sqlalchemy/dialects/postgresql/json.py +333 -0
  68. sqlalchemy/dialects/postgresql/named_types.py +509 -0
  69. sqlalchemy/dialects/postgresql/operators.py +129 -0
  70. sqlalchemy/dialects/postgresql/pg8000.py +662 -0
  71. sqlalchemy/dialects/postgresql/pg_catalog.py +300 -0
  72. sqlalchemy/dialects/postgresql/provision.py +175 -0
  73. sqlalchemy/dialects/postgresql/psycopg.py +772 -0
  74. sqlalchemy/dialects/postgresql/psycopg2.py +886 -0
  75. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  76. sqlalchemy/dialects/postgresql/ranges.py +1029 -0
  77. sqlalchemy/dialects/postgresql/types.py +303 -0
  78. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  79. sqlalchemy/dialects/sqlite/aiosqlite.py +396 -0
  80. sqlalchemy/dialects/sqlite/base.py +2805 -0
  81. sqlalchemy/dialects/sqlite/dml.py +240 -0
  82. sqlalchemy/dialects/sqlite/json.py +92 -0
  83. sqlalchemy/dialects/sqlite/provision.py +198 -0
  84. sqlalchemy/dialects/sqlite/pysqlcipher.py +155 -0
  85. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  86. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  87. sqlalchemy/engine/__init__.py +62 -0
  88. sqlalchemy/engine/_py_processors.py +136 -0
  89. sqlalchemy/engine/_py_row.py +128 -0
  90. sqlalchemy/engine/_py_util.py +74 -0
  91. sqlalchemy/engine/base.py +3375 -0
  92. sqlalchemy/engine/characteristics.py +155 -0
  93. sqlalchemy/engine/create.py +875 -0
  94. sqlalchemy/engine/cursor.py +2181 -0
  95. sqlalchemy/engine/default.py +2365 -0
  96. sqlalchemy/engine/events.py +951 -0
  97. sqlalchemy/engine/interfaces.py +3403 -0
  98. sqlalchemy/engine/mock.py +131 -0
  99. sqlalchemy/engine/processors.py +61 -0
  100. sqlalchemy/engine/reflection.py +2098 -0
  101. sqlalchemy/engine/result.py +2382 -0
  102. sqlalchemy/engine/row.py +401 -0
  103. sqlalchemy/engine/strategies.py +19 -0
  104. sqlalchemy/engine/url.py +910 -0
  105. sqlalchemy/engine/util.py +167 -0
  106. sqlalchemy/event/__init__.py +25 -0
  107. sqlalchemy/event/api.py +225 -0
  108. sqlalchemy/event/attr.py +655 -0
  109. sqlalchemy/event/base.py +470 -0
  110. sqlalchemy/event/legacy.py +246 -0
  111. sqlalchemy/event/registry.py +386 -0
  112. sqlalchemy/events.py +17 -0
  113. sqlalchemy/exc.py +830 -0
  114. sqlalchemy/ext/__init__.py +11 -0
  115. sqlalchemy/ext/associationproxy.py +2013 -0
  116. sqlalchemy/ext/asyncio/__init__.py +25 -0
  117. sqlalchemy/ext/asyncio/base.py +279 -0
  118. sqlalchemy/ext/asyncio/engine.py +1466 -0
  119. sqlalchemy/ext/asyncio/exc.py +21 -0
  120. sqlalchemy/ext/asyncio/result.py +961 -0
  121. sqlalchemy/ext/asyncio/scoping.py +1614 -0
  122. sqlalchemy/ext/asyncio/session.py +1936 -0
  123. sqlalchemy/ext/automap.py +1691 -0
  124. sqlalchemy/ext/baked.py +574 -0
  125. sqlalchemy/ext/compiler.py +570 -0
  126. sqlalchemy/ext/declarative/__init__.py +65 -0
  127. sqlalchemy/ext/declarative/extensions.py +548 -0
  128. sqlalchemy/ext/horizontal_shard.py +481 -0
  129. sqlalchemy/ext/hybrid.py +1514 -0
  130. sqlalchemy/ext/indexable.py +341 -0
  131. sqlalchemy/ext/instrumentation.py +450 -0
  132. sqlalchemy/ext/mutable.py +1073 -0
  133. sqlalchemy/ext/mypy/__init__.py +6 -0
  134. sqlalchemy/ext/mypy/apply.py +320 -0
  135. sqlalchemy/ext/mypy/decl_class.py +515 -0
  136. sqlalchemy/ext/mypy/infer.py +590 -0
  137. sqlalchemy/ext/mypy/names.py +335 -0
  138. sqlalchemy/ext/mypy/plugin.py +303 -0
  139. sqlalchemy/ext/mypy/util.py +357 -0
  140. sqlalchemy/ext/orderinglist.py +416 -0
  141. sqlalchemy/ext/serializer.py +181 -0
  142. sqlalchemy/future/__init__.py +16 -0
  143. sqlalchemy/future/engine.py +15 -0
  144. sqlalchemy/inspection.py +174 -0
  145. sqlalchemy/log.py +288 -0
  146. sqlalchemy/orm/__init__.py +170 -0
  147. sqlalchemy/orm/_orm_constructors.py +2571 -0
  148. sqlalchemy/orm/_typing.py +179 -0
  149. sqlalchemy/orm/attributes.py +2835 -0
  150. sqlalchemy/orm/base.py +973 -0
  151. sqlalchemy/orm/bulk_persistence.py +2123 -0
  152. sqlalchemy/orm/clsregistry.py +571 -0
  153. sqlalchemy/orm/collections.py +1620 -0
  154. sqlalchemy/orm/context.py +3268 -0
  155. sqlalchemy/orm/decl_api.py +1883 -0
  156. sqlalchemy/orm/decl_base.py +2190 -0
  157. sqlalchemy/orm/dependency.py +1304 -0
  158. sqlalchemy/orm/descriptor_props.py +1076 -0
  159. sqlalchemy/orm/dynamic.py +300 -0
  160. sqlalchemy/orm/evaluator.py +379 -0
  161. sqlalchemy/orm/events.py +3261 -0
  162. sqlalchemy/orm/exc.py +228 -0
  163. sqlalchemy/orm/identity.py +302 -0
  164. sqlalchemy/orm/instrumentation.py +754 -0
  165. sqlalchemy/orm/interfaces.py +1474 -0
  166. sqlalchemy/orm/loading.py +1682 -0
  167. sqlalchemy/orm/mapped_collection.py +557 -0
  168. sqlalchemy/orm/mapper.py +4432 -0
  169. sqlalchemy/orm/path_registry.py +811 -0
  170. sqlalchemy/orm/persistence.py +1782 -0
  171. sqlalchemy/orm/properties.py +886 -0
  172. sqlalchemy/orm/query.py +3396 -0
  173. sqlalchemy/orm/relationships.py +3500 -0
  174. sqlalchemy/orm/scoping.py +2165 -0
  175. sqlalchemy/orm/session.py +5301 -0
  176. sqlalchemy/orm/state.py +1143 -0
  177. sqlalchemy/orm/state_changes.py +198 -0
  178. sqlalchemy/orm/strategies.py +3473 -0
  179. sqlalchemy/orm/strategy_options.py +2569 -0
  180. sqlalchemy/orm/sync.py +164 -0
  181. sqlalchemy/orm/unitofwork.py +796 -0
  182. sqlalchemy/orm/util.py +2424 -0
  183. sqlalchemy/orm/writeonly.py +678 -0
  184. sqlalchemy/pool/__init__.py +44 -0
  185. sqlalchemy/pool/base.py +1515 -0
  186. sqlalchemy/pool/events.py +370 -0
  187. sqlalchemy/pool/impl.py +581 -0
  188. sqlalchemy/py.typed +0 -0
  189. sqlalchemy/schema.py +70 -0
  190. sqlalchemy/sql/__init__.py +145 -0
  191. sqlalchemy/sql/_dml_constructors.py +140 -0
  192. sqlalchemy/sql/_elements_constructors.py +1850 -0
  193. sqlalchemy/sql/_orm_types.py +20 -0
  194. sqlalchemy/sql/_py_util.py +75 -0
  195. sqlalchemy/sql/_selectable_constructors.py +635 -0
  196. sqlalchemy/sql/_typing.py +460 -0
  197. sqlalchemy/sql/annotation.py +585 -0
  198. sqlalchemy/sql/base.py +2185 -0
  199. sqlalchemy/sql/cache_key.py +1057 -0
  200. sqlalchemy/sql/coercions.py +1405 -0
  201. sqlalchemy/sql/compiler.py +7818 -0
  202. sqlalchemy/sql/crud.py +1669 -0
  203. sqlalchemy/sql/ddl.py +1378 -0
  204. sqlalchemy/sql/default_comparator.py +552 -0
  205. sqlalchemy/sql/dml.py +1817 -0
  206. sqlalchemy/sql/elements.py +5499 -0
  207. sqlalchemy/sql/events.py +455 -0
  208. sqlalchemy/sql/expression.py +162 -0
  209. sqlalchemy/sql/functions.py +2055 -0
  210. sqlalchemy/sql/lambdas.py +1449 -0
  211. sqlalchemy/sql/naming.py +212 -0
  212. sqlalchemy/sql/operators.py +2579 -0
  213. sqlalchemy/sql/roles.py +323 -0
  214. sqlalchemy/sql/schema.py +6158 -0
  215. sqlalchemy/sql/selectable.py +7004 -0
  216. sqlalchemy/sql/sqltypes.py +3827 -0
  217. sqlalchemy/sql/traversals.py +1024 -0
  218. sqlalchemy/sql/type_api.py +2339 -0
  219. sqlalchemy/sql/util.py +1486 -0
  220. sqlalchemy/sql/visitors.py +1165 -0
  221. sqlalchemy/testing/__init__.py +96 -0
  222. sqlalchemy/testing/assertions.py +989 -0
  223. sqlalchemy/testing/assertsql.py +516 -0
  224. sqlalchemy/testing/asyncio.py +135 -0
  225. sqlalchemy/testing/config.py +427 -0
  226. sqlalchemy/testing/engines.py +472 -0
  227. sqlalchemy/testing/entities.py +117 -0
  228. sqlalchemy/testing/exclusions.py +435 -0
  229. sqlalchemy/testing/fixtures/__init__.py +28 -0
  230. sqlalchemy/testing/fixtures/base.py +366 -0
  231. sqlalchemy/testing/fixtures/mypy.py +312 -0
  232. sqlalchemy/testing/fixtures/orm.py +227 -0
  233. sqlalchemy/testing/fixtures/sql.py +503 -0
  234. sqlalchemy/testing/pickleable.py +155 -0
  235. sqlalchemy/testing/plugin/__init__.py +6 -0
  236. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  237. sqlalchemy/testing/plugin/plugin_base.py +779 -0
  238. sqlalchemy/testing/plugin/pytestplugin.py +868 -0
  239. sqlalchemy/testing/profiling.py +324 -0
  240. sqlalchemy/testing/provision.py +496 -0
  241. sqlalchemy/testing/requirements.py +1818 -0
  242. sqlalchemy/testing/schema.py +224 -0
  243. sqlalchemy/testing/suite/__init__.py +19 -0
  244. sqlalchemy/testing/suite/test_cte.py +211 -0
  245. sqlalchemy/testing/suite/test_ddl.py +389 -0
  246. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  247. sqlalchemy/testing/suite/test_dialect.py +740 -0
  248. sqlalchemy/testing/suite/test_insert.py +630 -0
  249. sqlalchemy/testing/suite/test_reflection.py +3225 -0
  250. sqlalchemy/testing/suite/test_results.py +502 -0
  251. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  252. sqlalchemy/testing/suite/test_select.py +1999 -0
  253. sqlalchemy/testing/suite/test_sequence.py +317 -0
  254. sqlalchemy/testing/suite/test_types.py +2141 -0
  255. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  256. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  257. sqlalchemy/testing/util.py +537 -0
  258. sqlalchemy/testing/warnings.py +52 -0
  259. sqlalchemy/types.py +76 -0
  260. sqlalchemy/util/__init__.py +160 -0
  261. sqlalchemy/util/_collections.py +715 -0
  262. sqlalchemy/util/_concurrency_py3k.py +288 -0
  263. sqlalchemy/util/_has_cy.py +40 -0
  264. sqlalchemy/util/_py_collections.py +541 -0
  265. sqlalchemy/util/compat.py +301 -0
  266. sqlalchemy/util/concurrency.py +108 -0
  267. sqlalchemy/util/deprecations.py +401 -0
  268. sqlalchemy/util/langhelpers.py +2218 -0
  269. sqlalchemy/util/preloaded.py +150 -0
  270. sqlalchemy/util/queue.py +322 -0
  271. sqlalchemy/util/tool_support.py +201 -0
  272. sqlalchemy/util/topological.py +120 -0
  273. sqlalchemy/util/typing.py +629 -0
@@ -0,0 +1,772 @@
1
+ # dialects/postgresql/psycopg.py
2
+ # Copyright (C) 2005-2024 the SQLAlchemy authors and contributors
3
+ # <see AUTHORS file>
4
+ #
5
+ # This module is part of SQLAlchemy and is released under
6
+ # the MIT License: https://www.opensource.org/licenses/mit-license.php
7
+ # mypy: ignore-errors
8
+
9
+ r"""
10
+ .. dialect:: postgresql+psycopg
11
+ :name: psycopg (a.k.a. psycopg 3)
12
+ :dbapi: psycopg
13
+ :connectstring: postgresql+psycopg://user:password@host:port/dbname[?key=value&key=value...]
14
+ :url: https://pypi.org/project/psycopg/
15
+
16
+ ``psycopg`` is the package and module name for version 3 of the ``psycopg``
17
+ database driver, formerly known as ``psycopg2``. This driver is different
18
+ enough from its ``psycopg2`` predecessor that SQLAlchemy supports it
19
+ via a totally separate dialect; support for ``psycopg2`` is expected to remain
20
+ for as long as that package continues to function for modern Python versions,
21
+ and also remains the default dialect for the ``postgresql://`` dialect
22
+ series.
23
+
24
+ The SQLAlchemy ``psycopg`` dialect provides both a sync and an async
25
+ implementation under the same dialect name. The proper version is
26
+ selected depending on how the engine is created:
27
+
28
+ * calling :func:`_sa.create_engine` with ``postgresql+psycopg://...`` will
29
+ automatically select the sync version, e.g.::
30
+
31
+ from sqlalchemy import create_engine
32
+ sync_engine = create_engine("postgresql+psycopg://scott:tiger@localhost/test")
33
+
34
+ * calling :func:`_asyncio.create_async_engine` with
35
+ ``postgresql+psycopg://...`` will automatically select the async version,
36
+ e.g.::
37
+
38
+ from sqlalchemy.ext.asyncio import create_async_engine
39
+ asyncio_engine = create_async_engine("postgresql+psycopg://scott:tiger@localhost/test")
40
+
41
+ The asyncio version of the dialect may also be specified explicitly using the
42
+ ``psycopg_async`` suffix, as::
43
+
44
+ from sqlalchemy.ext.asyncio import create_async_engine
45
+ asyncio_engine = create_async_engine("postgresql+psycopg_async://scott:tiger@localhost/test")
46
+
47
+ .. seealso::
48
+
49
+ :ref:`postgresql_psycopg2` - The SQLAlchemy ``psycopg``
50
+ dialect shares most of its behavior with the ``psycopg2`` dialect.
51
+ Further documentation is available there.
52
+
53
+ Using a different Cursor class
54
+ ------------------------------
55
+
56
+ One of the differences between ``psycopg`` and the older ``psycopg2``
57
+ is how bound parameters are handled: ``psycopg2`` would bind them
58
+ client side, while ``psycopg`` by default will bind them server side.
59
+
60
+ It's possible to configure ``psycopg`` to do client side binding by
61
+ specifying the ``cursor_factory`` to be ``ClientCursor`` when creating
62
+ the engine::
63
+
64
+ from psycopg import ClientCursor
65
+
66
+ client_side_engine = create_engine(
67
+ "postgresql+psycopg://...",
68
+ connect_args={"cursor_factory": ClientCursor},
69
+ )
70
+
71
+ Similarly when using an async engine the ``AsyncClientCursor`` can be
72
+ specified::
73
+
74
+ from psycopg import AsyncClientCursor
75
+
76
+ client_side_engine = create_async_engine(
77
+ "postgresql+psycopg://...",
78
+ connect_args={"cursor_factory": AsyncClientCursor},
79
+ )
80
+
81
+ .. seealso::
82
+
83
+ `Client-side-binding cursors <https://www.psycopg.org/psycopg3/docs/advanced/cursors.html#client-side-binding-cursors>`_
84
+
85
+ """ # noqa
86
+ from __future__ import annotations
87
+
88
+ from collections import deque
89
+ import logging
90
+ import re
91
+ from typing import cast
92
+ from typing import TYPE_CHECKING
93
+
94
+ from . import ranges
95
+ from ._psycopg_common import _PGDialect_common_psycopg
96
+ from ._psycopg_common import _PGExecutionContext_common_psycopg
97
+ from .base import INTERVAL
98
+ from .base import PGCompiler
99
+ from .base import PGIdentifierPreparer
100
+ from .base import REGCONFIG
101
+ from .json import JSON
102
+ from .json import JSONB
103
+ from .json import JSONPathType
104
+ from .types import CITEXT
105
+ from ... import pool
106
+ from ... import util
107
+ from ...engine import AdaptedConnection
108
+ from ...sql import sqltypes
109
+ from ...util.concurrency import await_fallback
110
+ from ...util.concurrency import await_only
111
+
112
+ if TYPE_CHECKING:
113
+ from typing import Iterable
114
+
115
+ from psycopg import AsyncConnection
116
+
117
+ logger = logging.getLogger("sqlalchemy.dialects.postgresql")
118
+
119
+
120
+ class _PGString(sqltypes.String):
121
+ render_bind_cast = True
122
+
123
+
124
+ class _PGREGCONFIG(REGCONFIG):
125
+ render_bind_cast = True
126
+
127
+
128
+ class _PGJSON(JSON):
129
+ def bind_processor(self, dialect):
130
+ return self._make_bind_processor(None, dialect._psycopg_Json)
131
+
132
+ def result_processor(self, dialect, coltype):
133
+ return None
134
+
135
+
136
+ class _PGJSONB(JSONB):
137
+ def bind_processor(self, dialect):
138
+ return self._make_bind_processor(None, dialect._psycopg_Jsonb)
139
+
140
+ def result_processor(self, dialect, coltype):
141
+ return None
142
+
143
+
144
+ class _PGJSONIntIndexType(sqltypes.JSON.JSONIntIndexType):
145
+ __visit_name__ = "json_int_index"
146
+
147
+ render_bind_cast = True
148
+
149
+
150
+ class _PGJSONStrIndexType(sqltypes.JSON.JSONStrIndexType):
151
+ __visit_name__ = "json_str_index"
152
+
153
+ render_bind_cast = True
154
+
155
+
156
+ class _PGJSONPathType(JSONPathType):
157
+ pass
158
+
159
+
160
+ class _PGInterval(INTERVAL):
161
+ render_bind_cast = True
162
+
163
+
164
+ class _PGTimeStamp(sqltypes.DateTime):
165
+ render_bind_cast = True
166
+
167
+
168
+ class _PGDate(sqltypes.Date):
169
+ render_bind_cast = True
170
+
171
+
172
+ class _PGTime(sqltypes.Time):
173
+ render_bind_cast = True
174
+
175
+
176
+ class _PGInteger(sqltypes.Integer):
177
+ render_bind_cast = True
178
+
179
+
180
+ class _PGSmallInteger(sqltypes.SmallInteger):
181
+ render_bind_cast = True
182
+
183
+
184
+ class _PGNullType(sqltypes.NullType):
185
+ render_bind_cast = True
186
+
187
+
188
+ class _PGBigInteger(sqltypes.BigInteger):
189
+ render_bind_cast = True
190
+
191
+
192
+ class _PGBoolean(sqltypes.Boolean):
193
+ render_bind_cast = True
194
+
195
+
196
+ class _PsycopgRange(ranges.AbstractSingleRangeImpl):
197
+ def bind_processor(self, dialect):
198
+ psycopg_Range = cast(PGDialect_psycopg, dialect)._psycopg_Range
199
+
200
+ def to_range(value):
201
+ if isinstance(value, ranges.Range):
202
+ value = psycopg_Range(
203
+ value.lower, value.upper, value.bounds, value.empty
204
+ )
205
+ return value
206
+
207
+ return to_range
208
+
209
+ def result_processor(self, dialect, coltype):
210
+ def to_range(value):
211
+ if value is not None:
212
+ value = ranges.Range(
213
+ value._lower,
214
+ value._upper,
215
+ bounds=value._bounds if value._bounds else "[)",
216
+ empty=not value._bounds,
217
+ )
218
+ return value
219
+
220
+ return to_range
221
+
222
+
223
+ class _PsycopgMultiRange(ranges.AbstractMultiRangeImpl):
224
+ def bind_processor(self, dialect):
225
+ psycopg_Range = cast(PGDialect_psycopg, dialect)._psycopg_Range
226
+ psycopg_Multirange = cast(
227
+ PGDialect_psycopg, dialect
228
+ )._psycopg_Multirange
229
+
230
+ NoneType = type(None)
231
+
232
+ def to_range(value):
233
+ if isinstance(value, (str, NoneType, psycopg_Multirange)):
234
+ return value
235
+
236
+ return psycopg_Multirange(
237
+ [
238
+ psycopg_Range(
239
+ element.lower,
240
+ element.upper,
241
+ element.bounds,
242
+ element.empty,
243
+ )
244
+ for element in cast("Iterable[ranges.Range]", value)
245
+ ]
246
+ )
247
+
248
+ return to_range
249
+
250
+ def result_processor(self, dialect, coltype):
251
+ def to_range(value):
252
+ if value is None:
253
+ return None
254
+ else:
255
+ return ranges.MultiRange(
256
+ ranges.Range(
257
+ elem._lower,
258
+ elem._upper,
259
+ bounds=elem._bounds if elem._bounds else "[)",
260
+ empty=not elem._bounds,
261
+ )
262
+ for elem in value
263
+ )
264
+
265
+ return to_range
266
+
267
+
268
+ class PGExecutionContext_psycopg(_PGExecutionContext_common_psycopg):
269
+ pass
270
+
271
+
272
+ class PGCompiler_psycopg(PGCompiler):
273
+ pass
274
+
275
+
276
+ class PGIdentifierPreparer_psycopg(PGIdentifierPreparer):
277
+ pass
278
+
279
+
280
+ def _log_notices(diagnostic):
281
+ logger.info("%s: %s", diagnostic.severity, diagnostic.message_primary)
282
+
283
+
284
+ class PGDialect_psycopg(_PGDialect_common_psycopg):
285
+ driver = "psycopg"
286
+
287
+ supports_statement_cache = True
288
+ supports_server_side_cursors = True
289
+ default_paramstyle = "pyformat"
290
+ supports_sane_multi_rowcount = True
291
+
292
+ execution_ctx_cls = PGExecutionContext_psycopg
293
+ statement_compiler = PGCompiler_psycopg
294
+ preparer = PGIdentifierPreparer_psycopg
295
+ psycopg_version = (0, 0)
296
+
297
+ _has_native_hstore = True
298
+ _psycopg_adapters_map = None
299
+
300
+ colspecs = util.update_copy(
301
+ _PGDialect_common_psycopg.colspecs,
302
+ {
303
+ sqltypes.String: _PGString,
304
+ REGCONFIG: _PGREGCONFIG,
305
+ JSON: _PGJSON,
306
+ CITEXT: CITEXT,
307
+ sqltypes.JSON: _PGJSON,
308
+ JSONB: _PGJSONB,
309
+ sqltypes.JSON.JSONPathType: _PGJSONPathType,
310
+ sqltypes.JSON.JSONIntIndexType: _PGJSONIntIndexType,
311
+ sqltypes.JSON.JSONStrIndexType: _PGJSONStrIndexType,
312
+ sqltypes.Interval: _PGInterval,
313
+ INTERVAL: _PGInterval,
314
+ sqltypes.Date: _PGDate,
315
+ sqltypes.DateTime: _PGTimeStamp,
316
+ sqltypes.Time: _PGTime,
317
+ sqltypes.Integer: _PGInteger,
318
+ sqltypes.SmallInteger: _PGSmallInteger,
319
+ sqltypes.BigInteger: _PGBigInteger,
320
+ ranges.AbstractSingleRange: _PsycopgRange,
321
+ ranges.AbstractMultiRange: _PsycopgMultiRange,
322
+ },
323
+ )
324
+
325
+ def __init__(self, **kwargs):
326
+ super().__init__(**kwargs)
327
+
328
+ if self.dbapi:
329
+ m = re.match(r"(\d+)\.(\d+)(?:\.(\d+))?", self.dbapi.__version__)
330
+ if m:
331
+ self.psycopg_version = tuple(
332
+ int(x) for x in m.group(1, 2, 3) if x is not None
333
+ )
334
+
335
+ if self.psycopg_version < (3, 0, 2):
336
+ raise ImportError(
337
+ "psycopg version 3.0.2 or higher is required."
338
+ )
339
+
340
+ from psycopg.adapt import AdaptersMap
341
+
342
+ self._psycopg_adapters_map = adapters_map = AdaptersMap(
343
+ self.dbapi.adapters
344
+ )
345
+
346
+ if self._native_inet_types is False:
347
+ import psycopg.types.string
348
+
349
+ adapters_map.register_loader(
350
+ "inet", psycopg.types.string.TextLoader
351
+ )
352
+ adapters_map.register_loader(
353
+ "cidr", psycopg.types.string.TextLoader
354
+ )
355
+
356
+ if self._json_deserializer:
357
+ from psycopg.types.json import set_json_loads
358
+
359
+ set_json_loads(self._json_deserializer, adapters_map)
360
+
361
+ if self._json_serializer:
362
+ from psycopg.types.json import set_json_dumps
363
+
364
+ set_json_dumps(self._json_serializer, adapters_map)
365
+
366
+ def create_connect_args(self, url):
367
+ # see https://github.com/psycopg/psycopg/issues/83
368
+ cargs, cparams = super().create_connect_args(url)
369
+
370
+ if self._psycopg_adapters_map:
371
+ cparams["context"] = self._psycopg_adapters_map
372
+ if self.client_encoding is not None:
373
+ cparams["client_encoding"] = self.client_encoding
374
+ return cargs, cparams
375
+
376
+ def _type_info_fetch(self, connection, name):
377
+ from psycopg.types import TypeInfo
378
+
379
+ return TypeInfo.fetch(connection.connection.driver_connection, name)
380
+
381
+ def initialize(self, connection):
382
+ super().initialize(connection)
383
+
384
+ # PGDialect.initialize() checks server version for <= 8.2 and sets
385
+ # this flag to False if so
386
+ if not self.insert_returning:
387
+ self.insert_executemany_returning = False
388
+
389
+ # HSTORE can't be registered until we have a connection so that
390
+ # we can look up its OID, so we set up this adapter in
391
+ # initialize()
392
+ if self.use_native_hstore:
393
+ info = self._type_info_fetch(connection, "hstore")
394
+ self._has_native_hstore = info is not None
395
+ if self._has_native_hstore:
396
+ from psycopg.types.hstore import register_hstore
397
+
398
+ # register the adapter for connections made subsequent to
399
+ # this one
400
+ register_hstore(info, self._psycopg_adapters_map)
401
+
402
+ # register the adapter for this connection
403
+ register_hstore(info, connection.connection)
404
+
405
+ @classmethod
406
+ def import_dbapi(cls):
407
+ import psycopg
408
+
409
+ return psycopg
410
+
411
+ @classmethod
412
+ def get_async_dialect_cls(cls, url):
413
+ return PGDialectAsync_psycopg
414
+
415
+ @util.memoized_property
416
+ def _isolation_lookup(self):
417
+ return {
418
+ "READ COMMITTED": self.dbapi.IsolationLevel.READ_COMMITTED,
419
+ "READ UNCOMMITTED": self.dbapi.IsolationLevel.READ_UNCOMMITTED,
420
+ "REPEATABLE READ": self.dbapi.IsolationLevel.REPEATABLE_READ,
421
+ "SERIALIZABLE": self.dbapi.IsolationLevel.SERIALIZABLE,
422
+ }
423
+
424
+ @util.memoized_property
425
+ def _psycopg_Json(self):
426
+ from psycopg.types import json
427
+
428
+ return json.Json
429
+
430
+ @util.memoized_property
431
+ def _psycopg_Jsonb(self):
432
+ from psycopg.types import json
433
+
434
+ return json.Jsonb
435
+
436
+ @util.memoized_property
437
+ def _psycopg_TransactionStatus(self):
438
+ from psycopg.pq import TransactionStatus
439
+
440
+ return TransactionStatus
441
+
442
+ @util.memoized_property
443
+ def _psycopg_Range(self):
444
+ from psycopg.types.range import Range
445
+
446
+ return Range
447
+
448
+ @util.memoized_property
449
+ def _psycopg_Multirange(self):
450
+ from psycopg.types.multirange import Multirange
451
+
452
+ return Multirange
453
+
454
+ def _do_isolation_level(self, connection, autocommit, isolation_level):
455
+ connection.autocommit = autocommit
456
+ connection.isolation_level = isolation_level
457
+
458
+ def get_isolation_level(self, dbapi_connection):
459
+ status_before = dbapi_connection.info.transaction_status
460
+ value = super().get_isolation_level(dbapi_connection)
461
+
462
+ # don't rely on psycopg providing enum symbols, compare with
463
+ # eq/ne
464
+ if status_before == self._psycopg_TransactionStatus.IDLE:
465
+ dbapi_connection.rollback()
466
+ return value
467
+
468
+ def set_isolation_level(self, dbapi_connection, level):
469
+ if level == "AUTOCOMMIT":
470
+ self._do_isolation_level(
471
+ dbapi_connection, autocommit=True, isolation_level=None
472
+ )
473
+ else:
474
+ self._do_isolation_level(
475
+ dbapi_connection,
476
+ autocommit=False,
477
+ isolation_level=self._isolation_lookup[level],
478
+ )
479
+
480
+ def set_readonly(self, connection, value):
481
+ connection.read_only = value
482
+
483
+ def get_readonly(self, connection):
484
+ return connection.read_only
485
+
486
+ def on_connect(self):
487
+ def notices(conn):
488
+ conn.add_notice_handler(_log_notices)
489
+
490
+ fns = [notices]
491
+
492
+ if self.isolation_level is not None:
493
+
494
+ def on_connect(conn):
495
+ self.set_isolation_level(conn, self.isolation_level)
496
+
497
+ fns.append(on_connect)
498
+
499
+ # fns always has the notices function
500
+ def on_connect(conn):
501
+ for fn in fns:
502
+ fn(conn)
503
+
504
+ return on_connect
505
+
506
+ def is_disconnect(self, e, connection, cursor):
507
+ if isinstance(e, self.dbapi.Error) and connection is not None:
508
+ if connection.closed or connection.broken:
509
+ return True
510
+ return False
511
+
512
+ def _do_prepared_twophase(self, connection, command, recover=False):
513
+ dbapi_conn = connection.connection.dbapi_connection
514
+ if (
515
+ recover
516
+ # don't rely on psycopg providing enum symbols, compare with
517
+ # eq/ne
518
+ or dbapi_conn.info.transaction_status
519
+ != self._psycopg_TransactionStatus.IDLE
520
+ ):
521
+ dbapi_conn.rollback()
522
+ before_autocommit = dbapi_conn.autocommit
523
+ try:
524
+ if not before_autocommit:
525
+ self._do_autocommit(dbapi_conn, True)
526
+ dbapi_conn.execute(command)
527
+ finally:
528
+ if not before_autocommit:
529
+ self._do_autocommit(dbapi_conn, before_autocommit)
530
+
531
+ def do_rollback_twophase(
532
+ self, connection, xid, is_prepared=True, recover=False
533
+ ):
534
+ if is_prepared:
535
+ self._do_prepared_twophase(
536
+ connection, f"ROLLBACK PREPARED '{xid}'", recover=recover
537
+ )
538
+ else:
539
+ self.do_rollback(connection.connection)
540
+
541
+ def do_commit_twophase(
542
+ self, connection, xid, is_prepared=True, recover=False
543
+ ):
544
+ if is_prepared:
545
+ self._do_prepared_twophase(
546
+ connection, f"COMMIT PREPARED '{xid}'", recover=recover
547
+ )
548
+ else:
549
+ self.do_commit(connection.connection)
550
+
551
+ @util.memoized_property
552
+ def _dialect_specific_select_one(self):
553
+ return ";"
554
+
555
+
556
+ class AsyncAdapt_psycopg_cursor:
557
+ __slots__ = ("_cursor", "await_", "_rows")
558
+
559
+ _psycopg_ExecStatus = None
560
+
561
+ def __init__(self, cursor, await_) -> None:
562
+ self._cursor = cursor
563
+ self.await_ = await_
564
+ self._rows = deque()
565
+
566
+ def __getattr__(self, name):
567
+ return getattr(self._cursor, name)
568
+
569
+ @property
570
+ def arraysize(self):
571
+ return self._cursor.arraysize
572
+
573
+ @arraysize.setter
574
+ def arraysize(self, value):
575
+ self._cursor.arraysize = value
576
+
577
+ def close(self):
578
+ self._rows.clear()
579
+ # Normal cursor just call _close() in a non-sync way.
580
+ self._cursor._close()
581
+
582
+ def execute(self, query, params=None, **kw):
583
+ result = self.await_(self._cursor.execute(query, params, **kw))
584
+ # sqlalchemy result is not async, so need to pull all rows here
585
+ res = self._cursor.pgresult
586
+
587
+ # don't rely on psycopg providing enum symbols, compare with
588
+ # eq/ne
589
+ if res and res.status == self._psycopg_ExecStatus.TUPLES_OK:
590
+ rows = self.await_(self._cursor.fetchall())
591
+ self._rows = deque(rows)
592
+ return result
593
+
594
+ def executemany(self, query, params_seq):
595
+ return self.await_(self._cursor.executemany(query, params_seq))
596
+
597
+ def __iter__(self):
598
+ while self._rows:
599
+ yield self._rows.popleft()
600
+
601
+ def fetchone(self):
602
+ if self._rows:
603
+ return self._rows.popleft()
604
+ else:
605
+ return None
606
+
607
+ def fetchmany(self, size=None):
608
+ if size is None:
609
+ size = self._cursor.arraysize
610
+
611
+ rr = self._rows
612
+ return [rr.popleft() for _ in range(min(size, len(rr)))]
613
+
614
+ def fetchall(self):
615
+ retval = list(self._rows)
616
+ self._rows.clear()
617
+ return retval
618
+
619
+
620
+ class AsyncAdapt_psycopg_ss_cursor(AsyncAdapt_psycopg_cursor):
621
+ def execute(self, query, params=None, **kw):
622
+ self.await_(self._cursor.execute(query, params, **kw))
623
+ return self
624
+
625
+ def close(self):
626
+ self.await_(self._cursor.close())
627
+
628
+ def fetchone(self):
629
+ return self.await_(self._cursor.fetchone())
630
+
631
+ def fetchmany(self, size=0):
632
+ return self.await_(self._cursor.fetchmany(size))
633
+
634
+ def fetchall(self):
635
+ return self.await_(self._cursor.fetchall())
636
+
637
+ def __iter__(self):
638
+ iterator = self._cursor.__aiter__()
639
+ while True:
640
+ try:
641
+ yield self.await_(iterator.__anext__())
642
+ except StopAsyncIteration:
643
+ break
644
+
645
+
646
+ class AsyncAdapt_psycopg_connection(AdaptedConnection):
647
+ _connection: AsyncConnection
648
+ __slots__ = ()
649
+ await_ = staticmethod(await_only)
650
+
651
+ def __init__(self, connection) -> None:
652
+ self._connection = connection
653
+
654
+ def __getattr__(self, name):
655
+ return getattr(self._connection, name)
656
+
657
+ def execute(self, query, params=None, **kw):
658
+ cursor = self.await_(self._connection.execute(query, params, **kw))
659
+ return AsyncAdapt_psycopg_cursor(cursor, self.await_)
660
+
661
+ def cursor(self, *args, **kw):
662
+ cursor = self._connection.cursor(*args, **kw)
663
+ if hasattr(cursor, "name"):
664
+ return AsyncAdapt_psycopg_ss_cursor(cursor, self.await_)
665
+ else:
666
+ return AsyncAdapt_psycopg_cursor(cursor, self.await_)
667
+
668
+ def commit(self):
669
+ self.await_(self._connection.commit())
670
+
671
+ def rollback(self):
672
+ self.await_(self._connection.rollback())
673
+
674
+ def close(self):
675
+ self.await_(self._connection.close())
676
+
677
+ @property
678
+ def autocommit(self):
679
+ return self._connection.autocommit
680
+
681
+ @autocommit.setter
682
+ def autocommit(self, value):
683
+ self.set_autocommit(value)
684
+
685
+ def set_autocommit(self, value):
686
+ self.await_(self._connection.set_autocommit(value))
687
+
688
+ def set_isolation_level(self, value):
689
+ self.await_(self._connection.set_isolation_level(value))
690
+
691
+ def set_read_only(self, value):
692
+ self.await_(self._connection.set_read_only(value))
693
+
694
+ def set_deferrable(self, value):
695
+ self.await_(self._connection.set_deferrable(value))
696
+
697
+
698
+ class AsyncAdaptFallback_psycopg_connection(AsyncAdapt_psycopg_connection):
699
+ __slots__ = ()
700
+ await_ = staticmethod(await_fallback)
701
+
702
+
703
+ class PsycopgAdaptDBAPI:
704
+ def __init__(self, psycopg) -> None:
705
+ self.psycopg = psycopg
706
+
707
+ for k, v in self.psycopg.__dict__.items():
708
+ if k != "connect":
709
+ self.__dict__[k] = v
710
+
711
+ def connect(self, *arg, **kw):
712
+ async_fallback = kw.pop("async_fallback", False)
713
+ creator_fn = kw.pop(
714
+ "async_creator_fn", self.psycopg.AsyncConnection.connect
715
+ )
716
+ if util.asbool(async_fallback):
717
+ return AsyncAdaptFallback_psycopg_connection(
718
+ await_fallback(creator_fn(*arg, **kw))
719
+ )
720
+ else:
721
+ return AsyncAdapt_psycopg_connection(
722
+ await_only(creator_fn(*arg, **kw))
723
+ )
724
+
725
+
726
+ class PGDialectAsync_psycopg(PGDialect_psycopg):
727
+ is_async = True
728
+ supports_statement_cache = True
729
+
730
+ @classmethod
731
+ def import_dbapi(cls):
732
+ import psycopg
733
+ from psycopg.pq import ExecStatus
734
+
735
+ AsyncAdapt_psycopg_cursor._psycopg_ExecStatus = ExecStatus
736
+
737
+ return PsycopgAdaptDBAPI(psycopg)
738
+
739
+ @classmethod
740
+ def get_pool_class(cls, url):
741
+ async_fallback = url.query.get("async_fallback", False)
742
+
743
+ if util.asbool(async_fallback):
744
+ return pool.FallbackAsyncAdaptedQueuePool
745
+ else:
746
+ return pool.AsyncAdaptedQueuePool
747
+
748
+ def _type_info_fetch(self, connection, name):
749
+ from psycopg.types import TypeInfo
750
+
751
+ adapted = connection.connection
752
+ return adapted.await_(TypeInfo.fetch(adapted.driver_connection, name))
753
+
754
+ def _do_isolation_level(self, connection, autocommit, isolation_level):
755
+ connection.set_autocommit(autocommit)
756
+ connection.set_isolation_level(isolation_level)
757
+
758
+ def _do_autocommit(self, connection, value):
759
+ connection.set_autocommit(value)
760
+
761
+ def set_readonly(self, connection, value):
762
+ connection.set_read_only(value)
763
+
764
+ def set_deferrable(self, connection, value):
765
+ connection.set_deferrable(value)
766
+
767
+ def get_driver_connection(self, connection):
768
+ return connection._connection
769
+
770
+
771
+ dialect = PGDialect_psycopg
772
+ dialect_async = PGDialectAsync_psycopg