SQLAlchemy 2.0.47__cp313-cp313t-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 (274) hide show
  1. sqlalchemy/__init__.py +283 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +184 -0
  4. sqlalchemy/connectors/asyncio.py +429 -0
  5. sqlalchemy/connectors/pyodbc.py +250 -0
  6. sqlalchemy/cyextension/__init__.py +6 -0
  7. sqlalchemy/cyextension/collections.cp313t-win32.pyd +0 -0
  8. sqlalchemy/cyextension/collections.pyx +409 -0
  9. sqlalchemy/cyextension/immutabledict.cp313t-win32.pyd +0 -0
  10. sqlalchemy/cyextension/immutabledict.pxd +8 -0
  11. sqlalchemy/cyextension/immutabledict.pyx +133 -0
  12. sqlalchemy/cyextension/processors.cp313t-win32.pyd +0 -0
  13. sqlalchemy/cyextension/processors.pyx +68 -0
  14. sqlalchemy/cyextension/resultproxy.cp313t-win32.pyd +0 -0
  15. sqlalchemy/cyextension/resultproxy.pyx +102 -0
  16. sqlalchemy/cyextension/util.cp313t-win32.pyd +0 -0
  17. sqlalchemy/cyextension/util.pyx +90 -0
  18. sqlalchemy/dialects/__init__.py +62 -0
  19. sqlalchemy/dialects/_typing.py +30 -0
  20. sqlalchemy/dialects/mssql/__init__.py +88 -0
  21. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  22. sqlalchemy/dialects/mssql/base.py +4093 -0
  23. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  24. sqlalchemy/dialects/mssql/json.py +129 -0
  25. sqlalchemy/dialects/mssql/provision.py +185 -0
  26. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  27. sqlalchemy/dialects/mssql/pyodbc.py +760 -0
  28. sqlalchemy/dialects/mysql/__init__.py +104 -0
  29. sqlalchemy/dialects/mysql/aiomysql.py +250 -0
  30. sqlalchemy/dialects/mysql/asyncmy.py +231 -0
  31. sqlalchemy/dialects/mysql/base.py +3949 -0
  32. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  33. sqlalchemy/dialects/mysql/dml.py +225 -0
  34. sqlalchemy/dialects/mysql/enumerated.py +282 -0
  35. sqlalchemy/dialects/mysql/expression.py +146 -0
  36. sqlalchemy/dialects/mysql/json.py +91 -0
  37. sqlalchemy/dialects/mysql/mariadb.py +72 -0
  38. sqlalchemy/dialects/mysql/mariadbconnector.py +322 -0
  39. sqlalchemy/dialects/mysql/mysqlconnector.py +302 -0
  40. sqlalchemy/dialects/mysql/mysqldb.py +314 -0
  41. sqlalchemy/dialects/mysql/provision.py +153 -0
  42. sqlalchemy/dialects/mysql/pymysql.py +158 -0
  43. sqlalchemy/dialects/mysql/pyodbc.py +157 -0
  44. sqlalchemy/dialects/mysql/reflection.py +727 -0
  45. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  46. sqlalchemy/dialects/mysql/types.py +835 -0
  47. sqlalchemy/dialects/oracle/__init__.py +81 -0
  48. sqlalchemy/dialects/oracle/base.py +3802 -0
  49. sqlalchemy/dialects/oracle/cx_oracle.py +1555 -0
  50. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  51. sqlalchemy/dialects/oracle/oracledb.py +941 -0
  52. sqlalchemy/dialects/oracle/provision.py +297 -0
  53. sqlalchemy/dialects/oracle/types.py +316 -0
  54. sqlalchemy/dialects/oracle/vector.py +365 -0
  55. sqlalchemy/dialects/postgresql/__init__.py +167 -0
  56. sqlalchemy/dialects/postgresql/_psycopg_common.py +189 -0
  57. sqlalchemy/dialects/postgresql/array.py +519 -0
  58. sqlalchemy/dialects/postgresql/asyncpg.py +1284 -0
  59. sqlalchemy/dialects/postgresql/base.py +5378 -0
  60. sqlalchemy/dialects/postgresql/dml.py +339 -0
  61. sqlalchemy/dialects/postgresql/ext.py +540 -0
  62. sqlalchemy/dialects/postgresql/hstore.py +406 -0
  63. sqlalchemy/dialects/postgresql/json.py +404 -0
  64. sqlalchemy/dialects/postgresql/named_types.py +524 -0
  65. sqlalchemy/dialects/postgresql/operators.py +129 -0
  66. sqlalchemy/dialects/postgresql/pg8000.py +669 -0
  67. sqlalchemy/dialects/postgresql/pg_catalog.py +326 -0
  68. sqlalchemy/dialects/postgresql/provision.py +183 -0
  69. sqlalchemy/dialects/postgresql/psycopg.py +862 -0
  70. sqlalchemy/dialects/postgresql/psycopg2.py +892 -0
  71. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  72. sqlalchemy/dialects/postgresql/ranges.py +1031 -0
  73. sqlalchemy/dialects/postgresql/types.py +313 -0
  74. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  75. sqlalchemy/dialects/sqlite/aiosqlite.py +482 -0
  76. sqlalchemy/dialects/sqlite/base.py +3056 -0
  77. sqlalchemy/dialects/sqlite/dml.py +263 -0
  78. sqlalchemy/dialects/sqlite/json.py +92 -0
  79. sqlalchemy/dialects/sqlite/provision.py +229 -0
  80. sqlalchemy/dialects/sqlite/pysqlcipher.py +157 -0
  81. sqlalchemy/dialects/sqlite/pysqlite.py +756 -0
  82. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  83. sqlalchemy/engine/__init__.py +62 -0
  84. sqlalchemy/engine/_py_processors.py +136 -0
  85. sqlalchemy/engine/_py_row.py +128 -0
  86. sqlalchemy/engine/_py_util.py +74 -0
  87. sqlalchemy/engine/base.py +3390 -0
  88. sqlalchemy/engine/characteristics.py +155 -0
  89. sqlalchemy/engine/create.py +893 -0
  90. sqlalchemy/engine/cursor.py +2298 -0
  91. sqlalchemy/engine/default.py +2394 -0
  92. sqlalchemy/engine/events.py +965 -0
  93. sqlalchemy/engine/interfaces.py +3471 -0
  94. sqlalchemy/engine/mock.py +134 -0
  95. sqlalchemy/engine/processors.py +61 -0
  96. sqlalchemy/engine/reflection.py +2102 -0
  97. sqlalchemy/engine/result.py +2399 -0
  98. sqlalchemy/engine/row.py +400 -0
  99. sqlalchemy/engine/strategies.py +16 -0
  100. sqlalchemy/engine/url.py +924 -0
  101. sqlalchemy/engine/util.py +167 -0
  102. sqlalchemy/event/__init__.py +26 -0
  103. sqlalchemy/event/api.py +220 -0
  104. sqlalchemy/event/attr.py +676 -0
  105. sqlalchemy/event/base.py +472 -0
  106. sqlalchemy/event/legacy.py +258 -0
  107. sqlalchemy/event/registry.py +390 -0
  108. sqlalchemy/events.py +17 -0
  109. sqlalchemy/exc.py +832 -0
  110. sqlalchemy/ext/__init__.py +11 -0
  111. sqlalchemy/ext/associationproxy.py +2027 -0
  112. sqlalchemy/ext/asyncio/__init__.py +25 -0
  113. sqlalchemy/ext/asyncio/base.py +281 -0
  114. sqlalchemy/ext/asyncio/engine.py +1471 -0
  115. sqlalchemy/ext/asyncio/exc.py +21 -0
  116. sqlalchemy/ext/asyncio/result.py +965 -0
  117. sqlalchemy/ext/asyncio/scoping.py +1599 -0
  118. sqlalchemy/ext/asyncio/session.py +1947 -0
  119. sqlalchemy/ext/automap.py +1701 -0
  120. sqlalchemy/ext/baked.py +570 -0
  121. sqlalchemy/ext/compiler.py +600 -0
  122. sqlalchemy/ext/declarative/__init__.py +65 -0
  123. sqlalchemy/ext/declarative/extensions.py +564 -0
  124. sqlalchemy/ext/horizontal_shard.py +478 -0
  125. sqlalchemy/ext/hybrid.py +1535 -0
  126. sqlalchemy/ext/indexable.py +364 -0
  127. sqlalchemy/ext/instrumentation.py +450 -0
  128. sqlalchemy/ext/mutable.py +1085 -0
  129. sqlalchemy/ext/mypy/__init__.py +6 -0
  130. sqlalchemy/ext/mypy/apply.py +324 -0
  131. sqlalchemy/ext/mypy/decl_class.py +515 -0
  132. sqlalchemy/ext/mypy/infer.py +590 -0
  133. sqlalchemy/ext/mypy/names.py +335 -0
  134. sqlalchemy/ext/mypy/plugin.py +303 -0
  135. sqlalchemy/ext/mypy/util.py +357 -0
  136. sqlalchemy/ext/orderinglist.py +439 -0
  137. sqlalchemy/ext/serializer.py +185 -0
  138. sqlalchemy/future/__init__.py +16 -0
  139. sqlalchemy/future/engine.py +15 -0
  140. sqlalchemy/inspection.py +174 -0
  141. sqlalchemy/log.py +288 -0
  142. sqlalchemy/orm/__init__.py +171 -0
  143. sqlalchemy/orm/_orm_constructors.py +2661 -0
  144. sqlalchemy/orm/_typing.py +179 -0
  145. sqlalchemy/orm/attributes.py +2845 -0
  146. sqlalchemy/orm/base.py +971 -0
  147. sqlalchemy/orm/bulk_persistence.py +2135 -0
  148. sqlalchemy/orm/clsregistry.py +571 -0
  149. sqlalchemy/orm/collections.py +1627 -0
  150. sqlalchemy/orm/context.py +3334 -0
  151. sqlalchemy/orm/decl_api.py +2004 -0
  152. sqlalchemy/orm/decl_base.py +2192 -0
  153. sqlalchemy/orm/dependency.py +1302 -0
  154. sqlalchemy/orm/descriptor_props.py +1092 -0
  155. sqlalchemy/orm/dynamic.py +300 -0
  156. sqlalchemy/orm/evaluator.py +379 -0
  157. sqlalchemy/orm/events.py +3252 -0
  158. sqlalchemy/orm/exc.py +237 -0
  159. sqlalchemy/orm/identity.py +302 -0
  160. sqlalchemy/orm/instrumentation.py +754 -0
  161. sqlalchemy/orm/interfaces.py +1496 -0
  162. sqlalchemy/orm/loading.py +1686 -0
  163. sqlalchemy/orm/mapped_collection.py +557 -0
  164. sqlalchemy/orm/mapper.py +4444 -0
  165. sqlalchemy/orm/path_registry.py +809 -0
  166. sqlalchemy/orm/persistence.py +1788 -0
  167. sqlalchemy/orm/properties.py +935 -0
  168. sqlalchemy/orm/query.py +3459 -0
  169. sqlalchemy/orm/relationships.py +3508 -0
  170. sqlalchemy/orm/scoping.py +2148 -0
  171. sqlalchemy/orm/session.py +5280 -0
  172. sqlalchemy/orm/state.py +1168 -0
  173. sqlalchemy/orm/state_changes.py +196 -0
  174. sqlalchemy/orm/strategies.py +3470 -0
  175. sqlalchemy/orm/strategy_options.py +2568 -0
  176. sqlalchemy/orm/sync.py +164 -0
  177. sqlalchemy/orm/unitofwork.py +796 -0
  178. sqlalchemy/orm/util.py +2403 -0
  179. sqlalchemy/orm/writeonly.py +674 -0
  180. sqlalchemy/pool/__init__.py +44 -0
  181. sqlalchemy/pool/base.py +1524 -0
  182. sqlalchemy/pool/events.py +375 -0
  183. sqlalchemy/pool/impl.py +588 -0
  184. sqlalchemy/py.typed +0 -0
  185. sqlalchemy/schema.py +69 -0
  186. sqlalchemy/sql/__init__.py +145 -0
  187. sqlalchemy/sql/_dml_constructors.py +132 -0
  188. sqlalchemy/sql/_elements_constructors.py +1872 -0
  189. sqlalchemy/sql/_orm_types.py +20 -0
  190. sqlalchemy/sql/_py_util.py +75 -0
  191. sqlalchemy/sql/_selectable_constructors.py +763 -0
  192. sqlalchemy/sql/_typing.py +482 -0
  193. sqlalchemy/sql/annotation.py +587 -0
  194. sqlalchemy/sql/base.py +2293 -0
  195. sqlalchemy/sql/cache_key.py +1057 -0
  196. sqlalchemy/sql/coercions.py +1404 -0
  197. sqlalchemy/sql/compiler.py +8081 -0
  198. sqlalchemy/sql/crud.py +1752 -0
  199. sqlalchemy/sql/ddl.py +1444 -0
  200. sqlalchemy/sql/default_comparator.py +551 -0
  201. sqlalchemy/sql/dml.py +1850 -0
  202. sqlalchemy/sql/elements.py +5589 -0
  203. sqlalchemy/sql/events.py +458 -0
  204. sqlalchemy/sql/expression.py +159 -0
  205. sqlalchemy/sql/functions.py +2158 -0
  206. sqlalchemy/sql/lambdas.py +1442 -0
  207. sqlalchemy/sql/naming.py +209 -0
  208. sqlalchemy/sql/operators.py +2623 -0
  209. sqlalchemy/sql/roles.py +323 -0
  210. sqlalchemy/sql/schema.py +6222 -0
  211. sqlalchemy/sql/selectable.py +7265 -0
  212. sqlalchemy/sql/sqltypes.py +3930 -0
  213. sqlalchemy/sql/traversals.py +1024 -0
  214. sqlalchemy/sql/type_api.py +2368 -0
  215. sqlalchemy/sql/util.py +1485 -0
  216. sqlalchemy/sql/visitors.py +1164 -0
  217. sqlalchemy/testing/__init__.py +96 -0
  218. sqlalchemy/testing/assertions.py +994 -0
  219. sqlalchemy/testing/assertsql.py +520 -0
  220. sqlalchemy/testing/asyncio.py +135 -0
  221. sqlalchemy/testing/config.py +434 -0
  222. sqlalchemy/testing/engines.py +483 -0
  223. sqlalchemy/testing/entities.py +117 -0
  224. sqlalchemy/testing/exclusions.py +476 -0
  225. sqlalchemy/testing/fixtures/__init__.py +28 -0
  226. sqlalchemy/testing/fixtures/base.py +384 -0
  227. sqlalchemy/testing/fixtures/mypy.py +332 -0
  228. sqlalchemy/testing/fixtures/orm.py +227 -0
  229. sqlalchemy/testing/fixtures/sql.py +482 -0
  230. sqlalchemy/testing/pickleable.py +155 -0
  231. sqlalchemy/testing/plugin/__init__.py +6 -0
  232. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  233. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  234. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  235. sqlalchemy/testing/profiling.py +329 -0
  236. sqlalchemy/testing/provision.py +603 -0
  237. sqlalchemy/testing/requirements.py +1945 -0
  238. sqlalchemy/testing/schema.py +198 -0
  239. sqlalchemy/testing/suite/__init__.py +19 -0
  240. sqlalchemy/testing/suite/test_cte.py +237 -0
  241. sqlalchemy/testing/suite/test_ddl.py +389 -0
  242. sqlalchemy/testing/suite/test_deprecations.py +153 -0
  243. sqlalchemy/testing/suite/test_dialect.py +776 -0
  244. sqlalchemy/testing/suite/test_insert.py +630 -0
  245. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  246. sqlalchemy/testing/suite/test_results.py +504 -0
  247. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  248. sqlalchemy/testing/suite/test_select.py +2010 -0
  249. sqlalchemy/testing/suite/test_sequence.py +317 -0
  250. sqlalchemy/testing/suite/test_types.py +2147 -0
  251. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  252. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  253. sqlalchemy/testing/util.py +535 -0
  254. sqlalchemy/testing/warnings.py +52 -0
  255. sqlalchemy/types.py +74 -0
  256. sqlalchemy/util/__init__.py +162 -0
  257. sqlalchemy/util/_collections.py +712 -0
  258. sqlalchemy/util/_concurrency_py3k.py +288 -0
  259. sqlalchemy/util/_has_cy.py +40 -0
  260. sqlalchemy/util/_py_collections.py +541 -0
  261. sqlalchemy/util/compat.py +421 -0
  262. sqlalchemy/util/concurrency.py +110 -0
  263. sqlalchemy/util/deprecations.py +401 -0
  264. sqlalchemy/util/langhelpers.py +2203 -0
  265. sqlalchemy/util/preloaded.py +150 -0
  266. sqlalchemy/util/queue.py +322 -0
  267. sqlalchemy/util/tool_support.py +201 -0
  268. sqlalchemy/util/topological.py +120 -0
  269. sqlalchemy/util/typing.py +734 -0
  270. sqlalchemy-2.0.47.dist-info/METADATA +243 -0
  271. sqlalchemy-2.0.47.dist-info/RECORD +274 -0
  272. sqlalchemy-2.0.47.dist-info/WHEEL +5 -0
  273. sqlalchemy-2.0.47.dist-info/licenses/LICENSE +19 -0
  274. sqlalchemy-2.0.47.dist-info/top_level.txt +1 -0
@@ -0,0 +1,189 @@
1
+ # dialects/postgresql/_psycopg_common.py
2
+ # Copyright (C) 2005-2026 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
+ from __future__ import annotations
9
+
10
+ import decimal
11
+
12
+ from .array import ARRAY as PGARRAY
13
+ from .base import _DECIMAL_TYPES
14
+ from .base import _FLOAT_TYPES
15
+ from .base import _INT_TYPES
16
+ from .base import PGDialect
17
+ from .base import PGExecutionContext
18
+ from .hstore import HSTORE
19
+ from .pg_catalog import _SpaceVector
20
+ from .pg_catalog import INT2VECTOR
21
+ from .pg_catalog import OIDVECTOR
22
+ from ... import exc
23
+ from ... import types as sqltypes
24
+ from ... import util
25
+ from ...engine import processors
26
+
27
+ _server_side_id = util.counter()
28
+
29
+
30
+ class _PsycopgNumeric(sqltypes.Numeric):
31
+ def bind_processor(self, dialect):
32
+ return None
33
+
34
+ def result_processor(self, dialect, coltype):
35
+ if self.asdecimal:
36
+ if coltype in _FLOAT_TYPES:
37
+ return processors.to_decimal_processor_factory(
38
+ decimal.Decimal, self._effective_decimal_return_scale
39
+ )
40
+ elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES:
41
+ # psycopg returns Decimal natively for 1700
42
+ return None
43
+ else:
44
+ raise exc.InvalidRequestError(
45
+ "Unknown PG numeric type: %d" % coltype
46
+ )
47
+ else:
48
+ if coltype in _FLOAT_TYPES:
49
+ # psycopg returns float natively for 701
50
+ return None
51
+ elif coltype in _DECIMAL_TYPES or coltype in _INT_TYPES:
52
+ return processors.to_float
53
+ else:
54
+ raise exc.InvalidRequestError(
55
+ "Unknown PG numeric type: %d" % coltype
56
+ )
57
+
58
+
59
+ class _PsycopgFloat(_PsycopgNumeric):
60
+ __visit_name__ = "float"
61
+
62
+
63
+ class _PsycopgHStore(HSTORE):
64
+ def bind_processor(self, dialect):
65
+ if dialect._has_native_hstore:
66
+ return None
67
+ else:
68
+ return super().bind_processor(dialect)
69
+
70
+ def result_processor(self, dialect, coltype):
71
+ if dialect._has_native_hstore:
72
+ return None
73
+ else:
74
+ return super().result_processor(dialect, coltype)
75
+
76
+
77
+ class _PsycopgARRAY(PGARRAY):
78
+ render_bind_cast = True
79
+
80
+
81
+ class _PsycopgINT2VECTOR(_SpaceVector, INT2VECTOR):
82
+ pass
83
+
84
+
85
+ class _PsycopgOIDVECTOR(_SpaceVector, OIDVECTOR):
86
+ pass
87
+
88
+
89
+ class _PGExecutionContext_common_psycopg(PGExecutionContext):
90
+ def create_server_side_cursor(self):
91
+ # use server-side cursors:
92
+ # psycopg
93
+ # https://www.psycopg.org/psycopg3/docs/advanced/cursors.html#server-side-cursors
94
+ # psycopg2
95
+ # https://www.psycopg.org/docs/usage.html#server-side-cursors
96
+ ident = "c_%s_%s" % (hex(id(self))[2:], hex(_server_side_id())[2:])
97
+ return self._dbapi_connection.cursor(ident)
98
+
99
+
100
+ class _PGDialect_common_psycopg(PGDialect):
101
+ supports_statement_cache = True
102
+ supports_server_side_cursors = True
103
+
104
+ default_paramstyle = "pyformat"
105
+
106
+ _has_native_hstore = True
107
+
108
+ colspecs = util.update_copy(
109
+ PGDialect.colspecs,
110
+ {
111
+ sqltypes.Numeric: _PsycopgNumeric,
112
+ sqltypes.Float: _PsycopgFloat,
113
+ HSTORE: _PsycopgHStore,
114
+ sqltypes.ARRAY: _PsycopgARRAY,
115
+ INT2VECTOR: _PsycopgINT2VECTOR,
116
+ OIDVECTOR: _PsycopgOIDVECTOR,
117
+ },
118
+ )
119
+
120
+ def __init__(
121
+ self,
122
+ client_encoding=None,
123
+ use_native_hstore=True,
124
+ **kwargs,
125
+ ):
126
+ PGDialect.__init__(self, **kwargs)
127
+ if not use_native_hstore:
128
+ self._has_native_hstore = False
129
+ self.use_native_hstore = use_native_hstore
130
+ self.client_encoding = client_encoding
131
+
132
+ def create_connect_args(self, url):
133
+ opts = url.translate_connect_args(username="user", database="dbname")
134
+
135
+ multihosts, multiports = self._split_multihost_from_url(url)
136
+
137
+ if opts or url.query:
138
+ if not opts:
139
+ opts = {}
140
+ if "port" in opts:
141
+ opts["port"] = int(opts["port"])
142
+ opts.update(url.query)
143
+
144
+ if multihosts:
145
+ opts["host"] = ",".join(multihosts)
146
+ comma_ports = ",".join(str(p) if p else "" for p in multiports)
147
+ if comma_ports:
148
+ opts["port"] = comma_ports
149
+ return ([], opts)
150
+ else:
151
+ # no connection arguments whatsoever; psycopg2.connect()
152
+ # requires that "dsn" be present as a blank string.
153
+ return ([""], opts)
154
+
155
+ def get_isolation_level_values(self, dbapi_connection):
156
+ return (
157
+ "AUTOCOMMIT",
158
+ "READ COMMITTED",
159
+ "READ UNCOMMITTED",
160
+ "REPEATABLE READ",
161
+ "SERIALIZABLE",
162
+ )
163
+
164
+ def set_deferrable(self, connection, value):
165
+ connection.deferrable = value
166
+
167
+ def get_deferrable(self, connection):
168
+ return connection.deferrable
169
+
170
+ def _do_autocommit(self, connection, value):
171
+ connection.autocommit = value
172
+
173
+ def detect_autocommit_setting(self, dbapi_connection):
174
+ return bool(dbapi_connection.autocommit)
175
+
176
+ def do_ping(self, dbapi_connection):
177
+ before_autocommit = dbapi_connection.autocommit
178
+
179
+ if not before_autocommit:
180
+ dbapi_connection.autocommit = True
181
+ cursor = dbapi_connection.cursor()
182
+ try:
183
+ cursor.execute(self._dialect_specific_select_one)
184
+ finally:
185
+ cursor.close()
186
+ if not before_autocommit and not dbapi_connection.closed:
187
+ dbapi_connection.autocommit = before_autocommit
188
+
189
+ return True
@@ -0,0 +1,519 @@
1
+ # dialects/postgresql/array.py
2
+ # Copyright (C) 2005-2026 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
+
8
+
9
+ from __future__ import annotations
10
+
11
+ import re
12
+ from typing import Any as typing_Any
13
+ from typing import Iterable
14
+ from typing import Optional
15
+ from typing import Sequence
16
+ from typing import TYPE_CHECKING
17
+ from typing import TypeVar
18
+ from typing import Union
19
+
20
+ from .operators import CONTAINED_BY
21
+ from .operators import CONTAINS
22
+ from .operators import OVERLAP
23
+ from ... import types as sqltypes
24
+ from ... import util
25
+ from ...sql import expression
26
+ from ...sql import operators
27
+ from ...sql.visitors import InternalTraversal
28
+
29
+ if TYPE_CHECKING:
30
+ from ...engine.interfaces import Dialect
31
+ from ...sql._typing import _ColumnExpressionArgument
32
+ from ...sql._typing import _TypeEngineArgument
33
+ from ...sql.elements import ColumnElement
34
+ from ...sql.elements import Grouping
35
+ from ...sql.expression import BindParameter
36
+ from ...sql.operators import OperatorType
37
+ from ...sql.selectable import _SelectIterable
38
+ from ...sql.type_api import _BindProcessorType
39
+ from ...sql.type_api import _LiteralProcessorType
40
+ from ...sql.type_api import _ResultProcessorType
41
+ from ...sql.type_api import TypeEngine
42
+ from ...sql.visitors import _TraverseInternalsType
43
+ from ...util.typing import Self
44
+
45
+
46
+ _T = TypeVar("_T", bound=typing_Any)
47
+ _CT = TypeVar("_CT", bound=typing_Any)
48
+
49
+
50
+ def Any(
51
+ other: typing_Any,
52
+ arrexpr: _ColumnExpressionArgument[_T],
53
+ operator: OperatorType = operators.eq,
54
+ ) -> ColumnElement[bool]:
55
+ """A synonym for the ARRAY-level :meth:`.ARRAY.Comparator.any` method.
56
+ See that method for details.
57
+
58
+ """
59
+
60
+ return arrexpr.any(other, operator) # type: ignore[no-any-return, union-attr] # noqa: E501
61
+
62
+
63
+ def All(
64
+ other: typing_Any,
65
+ arrexpr: _ColumnExpressionArgument[_T],
66
+ operator: OperatorType = operators.eq,
67
+ ) -> ColumnElement[bool]:
68
+ """A synonym for the ARRAY-level :meth:`.ARRAY.Comparator.all` method.
69
+ See that method for details.
70
+
71
+ """
72
+
73
+ return arrexpr.all(other, operator) # type: ignore[no-any-return, union-attr] # noqa: E501
74
+
75
+
76
+ class array(expression.ExpressionClauseList[_T]):
77
+ """A PostgreSQL ARRAY literal.
78
+
79
+ This is used to produce ARRAY literals in SQL expressions, e.g.::
80
+
81
+ from sqlalchemy.dialects.postgresql import array
82
+ from sqlalchemy.dialects import postgresql
83
+ from sqlalchemy import select, func
84
+
85
+ stmt = select(array([1, 2]) + array([3, 4, 5]))
86
+
87
+ print(stmt.compile(dialect=postgresql.dialect()))
88
+
89
+ Produces the SQL:
90
+
91
+ .. sourcecode:: sql
92
+
93
+ SELECT ARRAY[%(param_1)s, %(param_2)s] ||
94
+ ARRAY[%(param_3)s, %(param_4)s, %(param_5)s]) AS anon_1
95
+
96
+ An instance of :class:`.array` will always have the datatype
97
+ :class:`_types.ARRAY`. The "inner" type of the array is inferred from the
98
+ values present, unless the :paramref:`_postgresql.array.type_` keyword
99
+ argument is passed::
100
+
101
+ array(["foo", "bar"], type_=CHAR)
102
+
103
+ When constructing an empty array, the :paramref:`_postgresql.array.type_`
104
+ argument is particularly important as PostgreSQL server typically requires
105
+ a cast to be rendered for the inner type in order to render an empty array.
106
+ SQLAlchemy's compilation for the empty array will produce this cast so
107
+ that::
108
+
109
+ stmt = array([], type_=Integer)
110
+ print(stmt.compile(dialect=postgresql.dialect()))
111
+
112
+ Produces:
113
+
114
+ .. sourcecode:: sql
115
+
116
+ ARRAY[]::INTEGER[]
117
+
118
+ As required by PostgreSQL for empty arrays.
119
+
120
+ .. versionadded:: 2.0.40 added support to render empty PostgreSQL array
121
+ literals with a required cast.
122
+
123
+ Multidimensional arrays are produced by nesting :class:`.array` constructs.
124
+ The dimensionality of the final :class:`_types.ARRAY`
125
+ type is calculated by
126
+ recursively adding the dimensions of the inner :class:`_types.ARRAY`
127
+ type::
128
+
129
+ stmt = select(
130
+ array(
131
+ [array([1, 2]), array([3, 4]), array([column("q"), column("x")])]
132
+ )
133
+ )
134
+ print(stmt.compile(dialect=postgresql.dialect()))
135
+
136
+ Produces:
137
+
138
+ .. sourcecode:: sql
139
+
140
+ SELECT ARRAY[
141
+ ARRAY[%(param_1)s, %(param_2)s],
142
+ ARRAY[%(param_3)s, %(param_4)s],
143
+ ARRAY[q, x]
144
+ ] AS anon_1
145
+
146
+ .. versionadded:: 1.3.6 added support for multidimensional array literals
147
+
148
+ .. seealso::
149
+
150
+ :class:`_postgresql.ARRAY`
151
+
152
+ """ # noqa: E501
153
+
154
+ __visit_name__ = "array"
155
+
156
+ stringify_dialect = "postgresql"
157
+
158
+ _traverse_internals: _TraverseInternalsType = [
159
+ ("clauses", InternalTraversal.dp_clauseelement_tuple),
160
+ ("type", InternalTraversal.dp_type),
161
+ ]
162
+
163
+ def __init__(
164
+ self,
165
+ clauses: Iterable[_T],
166
+ *,
167
+ type_: Optional[_TypeEngineArgument[_T]] = None,
168
+ **kw: typing_Any,
169
+ ):
170
+ r"""Construct an ARRAY literal.
171
+
172
+ :param clauses: iterable, such as a list, containing elements to be
173
+ rendered in the array
174
+ :param type\_: optional type. If omitted, the type is inferred
175
+ from the contents of the array.
176
+
177
+ """
178
+ super().__init__(operators.comma_op, *clauses, **kw)
179
+
180
+ main_type = (
181
+ type_
182
+ if type_ is not None
183
+ else self.clauses[0].type if self.clauses else sqltypes.NULLTYPE
184
+ )
185
+
186
+ if isinstance(main_type, ARRAY):
187
+ self.type = ARRAY(
188
+ main_type.item_type,
189
+ dimensions=(
190
+ main_type.dimensions + 1
191
+ if main_type.dimensions is not None
192
+ else 2
193
+ ),
194
+ ) # type: ignore[assignment]
195
+ else:
196
+ self.type = ARRAY(main_type) # type: ignore[assignment]
197
+
198
+ @property
199
+ def _select_iterable(self) -> _SelectIterable:
200
+ return (self,)
201
+
202
+ def _bind_param(
203
+ self,
204
+ operator: OperatorType,
205
+ obj: typing_Any,
206
+ type_: Optional[TypeEngine[_T]] = None,
207
+ _assume_scalar: bool = False,
208
+ ) -> BindParameter[_T]:
209
+ if _assume_scalar or operator is operators.getitem:
210
+ return expression.BindParameter(
211
+ None,
212
+ obj,
213
+ _compared_to_operator=operator,
214
+ type_=type_,
215
+ _compared_to_type=self.type,
216
+ unique=True,
217
+ )
218
+
219
+ else:
220
+ return array(
221
+ [
222
+ self._bind_param(
223
+ operator, o, _assume_scalar=True, type_=type_
224
+ )
225
+ for o in obj
226
+ ]
227
+ ) # type: ignore[return-value]
228
+
229
+ def self_group(
230
+ self, against: Optional[OperatorType] = None
231
+ ) -> Union[Self, Grouping[_T]]:
232
+ if against in (operators.any_op, operators.all_op, operators.getitem):
233
+ return expression.Grouping(self)
234
+ else:
235
+ return self
236
+
237
+
238
+ class ARRAY(sqltypes.ARRAY[_T]):
239
+ """PostgreSQL ARRAY type.
240
+
241
+ The :class:`_postgresql.ARRAY` type is constructed in the same way
242
+ as the core :class:`_types.ARRAY` type; a member type is required, and a
243
+ number of dimensions is recommended if the type is to be used for more
244
+ than one dimension::
245
+
246
+ from sqlalchemy.dialects import postgresql
247
+
248
+ mytable = Table(
249
+ "mytable",
250
+ metadata,
251
+ Column("data", postgresql.ARRAY(Integer, dimensions=2)),
252
+ )
253
+
254
+ The :class:`_postgresql.ARRAY` type provides all operations defined on the
255
+ core :class:`_types.ARRAY` type, including support for "dimensions",
256
+ indexed access, and simple matching such as
257
+ :meth:`.types.ARRAY.Comparator.any` and
258
+ :meth:`.types.ARRAY.Comparator.all`. :class:`_postgresql.ARRAY`
259
+ class also
260
+ provides PostgreSQL-specific methods for containment operations, including
261
+ :meth:`.postgresql.ARRAY.Comparator.contains`
262
+ :meth:`.postgresql.ARRAY.Comparator.contained_by`, and
263
+ :meth:`.postgresql.ARRAY.Comparator.overlap`, e.g.::
264
+
265
+ mytable.c.data.contains([1, 2])
266
+
267
+ Indexed access is one-based by default, to match that of PostgreSQL;
268
+ for zero-based indexed access, set
269
+ :paramref:`_postgresql.ARRAY.zero_indexes`.
270
+
271
+ Additionally, the :class:`_postgresql.ARRAY`
272
+ type does not work directly in
273
+ conjunction with the :class:`.ENUM` type. For a workaround, see the
274
+ special type at :ref:`postgresql_array_of_enum`.
275
+
276
+ .. container:: topic
277
+
278
+ **Detecting Changes in ARRAY columns when using the ORM**
279
+
280
+ The :class:`_postgresql.ARRAY` type, when used with the SQLAlchemy ORM,
281
+ does not detect in-place mutations to the array. In order to detect
282
+ these, the :mod:`sqlalchemy.ext.mutable` extension must be used, using
283
+ the :class:`.MutableList` class::
284
+
285
+ from sqlalchemy.dialects.postgresql import ARRAY
286
+ from sqlalchemy.ext.mutable import MutableList
287
+
288
+
289
+ class SomeOrmClass(Base):
290
+ # ...
291
+
292
+ data = Column(MutableList.as_mutable(ARRAY(Integer)))
293
+
294
+ This extension will allow "in-place" changes such to the array
295
+ such as ``.append()`` to produce events which will be detected by the
296
+ unit of work. Note that changes to elements **inside** the array,
297
+ including subarrays that are mutated in place, are **not** detected.
298
+
299
+ Alternatively, assigning a new array value to an ORM element that
300
+ replaces the old one will always trigger a change event.
301
+
302
+ .. seealso::
303
+
304
+ :class:`_types.ARRAY` - base array type
305
+
306
+ :class:`_postgresql.array` - produces a literal array value.
307
+
308
+ """
309
+
310
+ def __init__(
311
+ self,
312
+ item_type: _TypeEngineArgument[_T],
313
+ as_tuple: bool = False,
314
+ dimensions: Optional[int] = None,
315
+ zero_indexes: bool = False,
316
+ ):
317
+ """Construct an ARRAY.
318
+
319
+ E.g.::
320
+
321
+ Column("myarray", ARRAY(Integer))
322
+
323
+ Arguments are:
324
+
325
+ :param item_type: The data type of items of this array. Note that
326
+ dimensionality is irrelevant here, so multi-dimensional arrays like
327
+ ``INTEGER[][]``, are constructed as ``ARRAY(Integer)``, not as
328
+ ``ARRAY(ARRAY(Integer))`` or such.
329
+
330
+ :param as_tuple=False: Specify whether return results
331
+ should be converted to tuples from lists. DBAPIs such
332
+ as psycopg2 return lists by default. When tuples are
333
+ returned, the results are hashable.
334
+
335
+ :param dimensions: if non-None, the ARRAY will assume a fixed
336
+ number of dimensions. This will cause the DDL emitted for this
337
+ ARRAY to include the exact number of bracket clauses ``[]``,
338
+ and will also optimize the performance of the type overall.
339
+ Note that PG arrays are always implicitly "non-dimensioned",
340
+ meaning they can store any number of dimensions no matter how
341
+ they were declared.
342
+
343
+ :param zero_indexes=False: when True, index values will be converted
344
+ between Python zero-based and PostgreSQL one-based indexes, e.g.
345
+ a value of one will be added to all index values before passing
346
+ to the database.
347
+
348
+ """
349
+ if isinstance(item_type, ARRAY):
350
+ raise ValueError(
351
+ "Do not nest ARRAY types; ARRAY(basetype) "
352
+ "handles multi-dimensional arrays of basetype"
353
+ )
354
+ if isinstance(item_type, type):
355
+ item_type = item_type()
356
+ self.item_type = item_type
357
+ self.as_tuple = as_tuple
358
+ self.dimensions = dimensions
359
+ self.zero_indexes = zero_indexes
360
+
361
+ class Comparator(sqltypes.ARRAY.Comparator[_CT]):
362
+ """Define comparison operations for :class:`_types.ARRAY`.
363
+
364
+ Note that these operations are in addition to those provided
365
+ by the base :class:`.types.ARRAY.Comparator` class, including
366
+ :meth:`.types.ARRAY.Comparator.any` and
367
+ :meth:`.types.ARRAY.Comparator.all`.
368
+
369
+ """
370
+
371
+ def contains(
372
+ self, other: typing_Any, **kwargs: typing_Any
373
+ ) -> ColumnElement[bool]:
374
+ """Boolean expression. Test if elements are a superset of the
375
+ elements of the argument array expression.
376
+
377
+ kwargs may be ignored by this operator but are required for API
378
+ conformance.
379
+ """
380
+ return self.operate(CONTAINS, other, result_type=sqltypes.Boolean)
381
+
382
+ def contained_by(self, other: typing_Any) -> ColumnElement[bool]:
383
+ """Boolean expression. Test if elements are a proper subset of the
384
+ elements of the argument array expression.
385
+ """
386
+ return self.operate(
387
+ CONTAINED_BY, other, result_type=sqltypes.Boolean
388
+ )
389
+
390
+ def overlap(self, other: typing_Any) -> ColumnElement[bool]:
391
+ """Boolean expression. Test if array has elements in common with
392
+ an argument array expression.
393
+ """
394
+ return self.operate(OVERLAP, other, result_type=sqltypes.Boolean)
395
+
396
+ comparator_factory = Comparator
397
+
398
+ @util.memoized_property
399
+ def _against_native_enum(self) -> bool:
400
+ return (
401
+ isinstance(self.item_type, sqltypes.Enum)
402
+ and self.item_type.native_enum
403
+ )
404
+
405
+ def literal_processor(
406
+ self, dialect: Dialect
407
+ ) -> Optional[_LiteralProcessorType[_T]]:
408
+ item_proc = self.item_type.dialect_impl(dialect).literal_processor(
409
+ dialect
410
+ )
411
+ if item_proc is None:
412
+ return None
413
+
414
+ def to_str(elements: Iterable[typing_Any]) -> str:
415
+ return f"ARRAY[{', '.join(elements)}]"
416
+
417
+ def process(value: Sequence[typing_Any]) -> str:
418
+ inner = self._apply_item_processor(
419
+ value, item_proc, self.dimensions, to_str
420
+ )
421
+ return inner
422
+
423
+ return process
424
+
425
+ def bind_processor(
426
+ self, dialect: Dialect
427
+ ) -> Optional[_BindProcessorType[Sequence[typing_Any]]]:
428
+ item_proc = self.item_type.dialect_impl(dialect).bind_processor(
429
+ dialect
430
+ )
431
+
432
+ def process(
433
+ value: Optional[Sequence[typing_Any]],
434
+ ) -> Optional[list[typing_Any]]:
435
+ if value is None:
436
+ return value
437
+ else:
438
+ return self._apply_item_processor(
439
+ value, item_proc, self.dimensions, list
440
+ )
441
+
442
+ return process
443
+
444
+ def result_processor(
445
+ self, dialect: Dialect, coltype: object
446
+ ) -> _ResultProcessorType[Sequence[typing_Any]]:
447
+ item_proc = self.item_type.dialect_impl(dialect).result_processor(
448
+ dialect, coltype
449
+ )
450
+
451
+ def process(
452
+ value: Sequence[typing_Any],
453
+ ) -> Optional[Sequence[typing_Any]]:
454
+ if value is None:
455
+ return value
456
+ else:
457
+ return self._apply_item_processor(
458
+ value,
459
+ item_proc,
460
+ self.dimensions,
461
+ tuple if self.as_tuple else list,
462
+ )
463
+
464
+ if self._against_native_enum:
465
+ super_rp = process
466
+ pattern = re.compile(r"^{(.*)}$")
467
+
468
+ def handle_raw_string(value: str) -> Sequence[Optional[str]]:
469
+ inner = pattern.match(value).group(1) # type: ignore[union-attr] # noqa: E501
470
+ return _split_enum_values(inner)
471
+
472
+ def process(
473
+ value: Sequence[typing_Any],
474
+ ) -> Optional[Sequence[typing_Any]]:
475
+ if value is None:
476
+ return value
477
+ # isinstance(value, str) is required to handle
478
+ # the case where a TypeDecorator for and Array of Enum is
479
+ # used like was required in sa < 1.3.17
480
+ return super_rp(
481
+ handle_raw_string(value)
482
+ if isinstance(value, str)
483
+ else value
484
+ )
485
+
486
+ return process
487
+
488
+
489
+ def _split_enum_values(array_string: str) -> Sequence[Optional[str]]:
490
+ if '"' not in array_string:
491
+ # no escape char is present so it can just split on the comma
492
+ return [
493
+ r if r != "NULL" else None
494
+ for r in (array_string.split(",") if array_string else [])
495
+ ]
496
+
497
+ # handles quoted strings from:
498
+ # r'abc,"quoted","also\\\\quoted", "quoted, comma", "esc \" quot", qpr'
499
+ # returns
500
+ # ['abc', 'quoted', 'also\\quoted', 'quoted, comma', 'esc " quot', 'qpr']
501
+ text = array_string.replace(r"\"", "_$ESC_QUOTE$_")
502
+ text = text.replace(r"\\", "\\")
503
+ result = []
504
+ on_quotes = re.split(r'(")', text)
505
+ in_quotes = False
506
+ for tok in on_quotes:
507
+ if tok == '"':
508
+ in_quotes = not in_quotes
509
+ elif in_quotes:
510
+ result.append(tok.replace("_$ESC_QUOTE$_", '"'))
511
+ else:
512
+ # interpret NULL (without quotes!) as None
513
+ result.extend(
514
+ [
515
+ r if r != "NULL" else None
516
+ for r in re.findall(r"([^\s,]+),?", tok)
517
+ ]
518
+ )
519
+ return result