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,425 @@
1
+ # dialects/postgresql/array.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
+
10
+ from __future__ import annotations
11
+
12
+ import re
13
+ from typing import Any
14
+ from typing import Optional
15
+ from typing import TypeVar
16
+
17
+ from .operators import CONTAINED_BY
18
+ from .operators import CONTAINS
19
+ from .operators import OVERLAP
20
+ from ... import types as sqltypes
21
+ from ... import util
22
+ from ...sql import expression
23
+ from ...sql import operators
24
+ from ...sql._typing import _TypeEngineArgument
25
+
26
+
27
+ _T = TypeVar("_T", bound=Any)
28
+
29
+
30
+ def Any(other, arrexpr, operator=operators.eq):
31
+ """A synonym for the ARRAY-level :meth:`.ARRAY.Comparator.any` method.
32
+ See that method for details.
33
+
34
+ """
35
+
36
+ return arrexpr.any(other, operator)
37
+
38
+
39
+ def All(other, arrexpr, operator=operators.eq):
40
+ """A synonym for the ARRAY-level :meth:`.ARRAY.Comparator.all` method.
41
+ See that method for details.
42
+
43
+ """
44
+
45
+ return arrexpr.all(other, operator)
46
+
47
+
48
+ class array(expression.ExpressionClauseList[_T]):
49
+ """A PostgreSQL ARRAY literal.
50
+
51
+ This is used to produce ARRAY literals in SQL expressions, e.g.::
52
+
53
+ from sqlalchemy.dialects.postgresql import array
54
+ from sqlalchemy.dialects import postgresql
55
+ from sqlalchemy import select, func
56
+
57
+ stmt = select(array([1,2]) + array([3,4,5]))
58
+
59
+ print(stmt.compile(dialect=postgresql.dialect()))
60
+
61
+ Produces the SQL::
62
+
63
+ SELECT ARRAY[%(param_1)s, %(param_2)s] ||
64
+ ARRAY[%(param_3)s, %(param_4)s, %(param_5)s]) AS anon_1
65
+
66
+ An instance of :class:`.array` will always have the datatype
67
+ :class:`_types.ARRAY`. The "inner" type of the array is inferred from
68
+ the values present, unless the ``type_`` keyword argument is passed::
69
+
70
+ array(['foo', 'bar'], type_=CHAR)
71
+
72
+ Multidimensional arrays are produced by nesting :class:`.array` constructs.
73
+ The dimensionality of the final :class:`_types.ARRAY`
74
+ type is calculated by
75
+ recursively adding the dimensions of the inner :class:`_types.ARRAY`
76
+ type::
77
+
78
+ stmt = select(
79
+ array([
80
+ array([1, 2]), array([3, 4]), array([column('q'), column('x')])
81
+ ])
82
+ )
83
+ print(stmt.compile(dialect=postgresql.dialect()))
84
+
85
+ Produces::
86
+
87
+ SELECT ARRAY[ARRAY[%(param_1)s, %(param_2)s],
88
+ ARRAY[%(param_3)s, %(param_4)s], ARRAY[q, x]] AS anon_1
89
+
90
+ .. versionadded:: 1.3.6 added support for multidimensional array literals
91
+
92
+ .. seealso::
93
+
94
+ :class:`_postgresql.ARRAY`
95
+
96
+ """
97
+
98
+ __visit_name__ = "array"
99
+
100
+ stringify_dialect = "postgresql"
101
+ inherit_cache = True
102
+
103
+ def __init__(self, clauses, **kw):
104
+ type_arg = kw.pop("type_", None)
105
+ super().__init__(operators.comma_op, *clauses, **kw)
106
+
107
+ self._type_tuple = [arg.type for arg in self.clauses]
108
+
109
+ main_type = (
110
+ type_arg
111
+ if type_arg is not None
112
+ else self._type_tuple[0] if self._type_tuple else sqltypes.NULLTYPE
113
+ )
114
+
115
+ if isinstance(main_type, ARRAY):
116
+ self.type = ARRAY(
117
+ main_type.item_type,
118
+ dimensions=(
119
+ main_type.dimensions + 1
120
+ if main_type.dimensions is not None
121
+ else 2
122
+ ),
123
+ )
124
+ else:
125
+ self.type = ARRAY(main_type)
126
+
127
+ @property
128
+ def _select_iterable(self):
129
+ return (self,)
130
+
131
+ def _bind_param(self, operator, obj, _assume_scalar=False, type_=None):
132
+ if _assume_scalar or operator is operators.getitem:
133
+ return expression.BindParameter(
134
+ None,
135
+ obj,
136
+ _compared_to_operator=operator,
137
+ type_=type_,
138
+ _compared_to_type=self.type,
139
+ unique=True,
140
+ )
141
+
142
+ else:
143
+ return array(
144
+ [
145
+ self._bind_param(
146
+ operator, o, _assume_scalar=True, type_=type_
147
+ )
148
+ for o in obj
149
+ ]
150
+ )
151
+
152
+ def self_group(self, against=None):
153
+ if against in (operators.any_op, operators.all_op, operators.getitem):
154
+ return expression.Grouping(self)
155
+ else:
156
+ return self
157
+
158
+
159
+ class ARRAY(sqltypes.ARRAY):
160
+ """PostgreSQL ARRAY type.
161
+
162
+ The :class:`_postgresql.ARRAY` type is constructed in the same way
163
+ as the core :class:`_types.ARRAY` type; a member type is required, and a
164
+ number of dimensions is recommended if the type is to be used for more
165
+ than one dimension::
166
+
167
+ from sqlalchemy.dialects import postgresql
168
+
169
+ mytable = Table("mytable", metadata,
170
+ Column("data", postgresql.ARRAY(Integer, dimensions=2))
171
+ )
172
+
173
+ The :class:`_postgresql.ARRAY` type provides all operations defined on the
174
+ core :class:`_types.ARRAY` type, including support for "dimensions",
175
+ indexed access, and simple matching such as
176
+ :meth:`.types.ARRAY.Comparator.any` and
177
+ :meth:`.types.ARRAY.Comparator.all`. :class:`_postgresql.ARRAY`
178
+ class also
179
+ provides PostgreSQL-specific methods for containment operations, including
180
+ :meth:`.postgresql.ARRAY.Comparator.contains`
181
+ :meth:`.postgresql.ARRAY.Comparator.contained_by`, and
182
+ :meth:`.postgresql.ARRAY.Comparator.overlap`, e.g.::
183
+
184
+ mytable.c.data.contains([1, 2])
185
+
186
+ Indexed access is one-based by default, to match that of PostgreSQL;
187
+ for zero-based indexed access, set
188
+ :paramref:`_postgresql.ARRAY.zero_indexes`.
189
+
190
+ Additionally, the :class:`_postgresql.ARRAY`
191
+ type does not work directly in
192
+ conjunction with the :class:`.ENUM` type. For a workaround, see the
193
+ special type at :ref:`postgresql_array_of_enum`.
194
+
195
+ .. container:: topic
196
+
197
+ **Detecting Changes in ARRAY columns when using the ORM**
198
+
199
+ The :class:`_postgresql.ARRAY` type, when used with the SQLAlchemy ORM,
200
+ does not detect in-place mutations to the array. In order to detect
201
+ these, the :mod:`sqlalchemy.ext.mutable` extension must be used, using
202
+ the :class:`.MutableList` class::
203
+
204
+ from sqlalchemy.dialects.postgresql import ARRAY
205
+ from sqlalchemy.ext.mutable import MutableList
206
+
207
+ class SomeOrmClass(Base):
208
+ # ...
209
+
210
+ data = Column(MutableList.as_mutable(ARRAY(Integer)))
211
+
212
+ This extension will allow "in-place" changes such to the array
213
+ such as ``.append()`` to produce events which will be detected by the
214
+ unit of work. Note that changes to elements **inside** the array,
215
+ including subarrays that are mutated in place, are **not** detected.
216
+
217
+ Alternatively, assigning a new array value to an ORM element that
218
+ replaces the old one will always trigger a change event.
219
+
220
+ .. seealso::
221
+
222
+ :class:`_types.ARRAY` - base array type
223
+
224
+ :class:`_postgresql.array` - produces a literal array value.
225
+
226
+ """
227
+
228
+ def __init__(
229
+ self,
230
+ item_type: _TypeEngineArgument[Any],
231
+ as_tuple: bool = False,
232
+ dimensions: Optional[int] = None,
233
+ zero_indexes: bool = False,
234
+ ):
235
+ """Construct an ARRAY.
236
+
237
+ E.g.::
238
+
239
+ Column('myarray', ARRAY(Integer))
240
+
241
+ Arguments are:
242
+
243
+ :param item_type: The data type of items of this array. Note that
244
+ dimensionality is irrelevant here, so multi-dimensional arrays like
245
+ ``INTEGER[][]``, are constructed as ``ARRAY(Integer)``, not as
246
+ ``ARRAY(ARRAY(Integer))`` or such.
247
+
248
+ :param as_tuple=False: Specify whether return results
249
+ should be converted to tuples from lists. DBAPIs such
250
+ as psycopg2 return lists by default. When tuples are
251
+ returned, the results are hashable.
252
+
253
+ :param dimensions: if non-None, the ARRAY will assume a fixed
254
+ number of dimensions. This will cause the DDL emitted for this
255
+ ARRAY to include the exact number of bracket clauses ``[]``,
256
+ and will also optimize the performance of the type overall.
257
+ Note that PG arrays are always implicitly "non-dimensioned",
258
+ meaning they can store any number of dimensions no matter how
259
+ they were declared.
260
+
261
+ :param zero_indexes=False: when True, index values will be converted
262
+ between Python zero-based and PostgreSQL one-based indexes, e.g.
263
+ a value of one will be added to all index values before passing
264
+ to the database.
265
+
266
+ """
267
+ if isinstance(item_type, ARRAY):
268
+ raise ValueError(
269
+ "Do not nest ARRAY types; ARRAY(basetype) "
270
+ "handles multi-dimensional arrays of basetype"
271
+ )
272
+ if isinstance(item_type, type):
273
+ item_type = item_type()
274
+ self.item_type = item_type
275
+ self.as_tuple = as_tuple
276
+ self.dimensions = dimensions
277
+ self.zero_indexes = zero_indexes
278
+
279
+ class Comparator(sqltypes.ARRAY.Comparator):
280
+ """Define comparison operations for :class:`_types.ARRAY`.
281
+
282
+ Note that these operations are in addition to those provided
283
+ by the base :class:`.types.ARRAY.Comparator` class, including
284
+ :meth:`.types.ARRAY.Comparator.any` and
285
+ :meth:`.types.ARRAY.Comparator.all`.
286
+
287
+ """
288
+
289
+ def contains(self, other, **kwargs):
290
+ """Boolean expression. Test if elements are a superset of the
291
+ elements of the argument array expression.
292
+
293
+ kwargs may be ignored by this operator but are required for API
294
+ conformance.
295
+ """
296
+ return self.operate(CONTAINS, other, result_type=sqltypes.Boolean)
297
+
298
+ def contained_by(self, other):
299
+ """Boolean expression. Test if elements are a proper subset of the
300
+ elements of the argument array expression.
301
+ """
302
+ return self.operate(
303
+ CONTAINED_BY, other, result_type=sqltypes.Boolean
304
+ )
305
+
306
+ def overlap(self, other):
307
+ """Boolean expression. Test if array has elements in common with
308
+ an argument array expression.
309
+ """
310
+ return self.operate(OVERLAP, other, result_type=sqltypes.Boolean)
311
+
312
+ comparator_factory = Comparator
313
+
314
+ @property
315
+ def hashable(self):
316
+ return self.as_tuple
317
+
318
+ @property
319
+ def python_type(self):
320
+ return list
321
+
322
+ def compare_values(self, x, y):
323
+ return x == y
324
+
325
+ @util.memoized_property
326
+ def _against_native_enum(self):
327
+ return (
328
+ isinstance(self.item_type, sqltypes.Enum)
329
+ and self.item_type.native_enum
330
+ )
331
+
332
+ def literal_processor(self, dialect):
333
+ item_proc = self.item_type.dialect_impl(dialect).literal_processor(
334
+ dialect
335
+ )
336
+ if item_proc is None:
337
+ return None
338
+
339
+ def to_str(elements):
340
+ return f"ARRAY[{', '.join(elements)}]"
341
+
342
+ def process(value):
343
+ inner = self._apply_item_processor(
344
+ value, item_proc, self.dimensions, to_str
345
+ )
346
+ return inner
347
+
348
+ return process
349
+
350
+ def bind_processor(self, dialect):
351
+ item_proc = self.item_type.dialect_impl(dialect).bind_processor(
352
+ dialect
353
+ )
354
+
355
+ def process(value):
356
+ if value is None:
357
+ return value
358
+ else:
359
+ return self._apply_item_processor(
360
+ value, item_proc, self.dimensions, list
361
+ )
362
+
363
+ return process
364
+
365
+ def result_processor(self, dialect, coltype):
366
+ item_proc = self.item_type.dialect_impl(dialect).result_processor(
367
+ dialect, coltype
368
+ )
369
+
370
+ def process(value):
371
+ if value is None:
372
+ return value
373
+ else:
374
+ return self._apply_item_processor(
375
+ value,
376
+ item_proc,
377
+ self.dimensions,
378
+ tuple if self.as_tuple else list,
379
+ )
380
+
381
+ if self._against_native_enum:
382
+ super_rp = process
383
+ pattern = re.compile(r"^{(.*)}$")
384
+
385
+ def handle_raw_string(value):
386
+ inner = pattern.match(value).group(1)
387
+ return _split_enum_values(inner)
388
+
389
+ def process(value):
390
+ if value is None:
391
+ return value
392
+ # isinstance(value, str) is required to handle
393
+ # the case where a TypeDecorator for and Array of Enum is
394
+ # used like was required in sa < 1.3.17
395
+ return super_rp(
396
+ handle_raw_string(value)
397
+ if isinstance(value, str)
398
+ else value
399
+ )
400
+
401
+ return process
402
+
403
+
404
+ def _split_enum_values(array_string):
405
+ if '"' not in array_string:
406
+ # no escape char is present so it can just split on the comma
407
+ return array_string.split(",") if array_string else []
408
+
409
+ # handles quoted strings from:
410
+ # r'abc,"quoted","also\\\\quoted", "quoted, comma", "esc \" quot", qpr'
411
+ # returns
412
+ # ['abc', 'quoted', 'also\\quoted', 'quoted, comma', 'esc " quot', 'qpr']
413
+ text = array_string.replace(r"\"", "_$ESC_QUOTE$_")
414
+ text = text.replace(r"\\", "\\")
415
+ result = []
416
+ on_quotes = re.split(r'(")', text)
417
+ in_quotes = False
418
+ for tok in on_quotes:
419
+ if tok == '"':
420
+ in_quotes = not in_quotes
421
+ elif in_quotes:
422
+ result.append(tok.replace("_$ESC_QUOTE$_", '"'))
423
+ else:
424
+ result.extend(re.findall(r"([^\s,]+),?", tok))
425
+ return result