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,263 @@
1
+ # dialects/sqlite/dml.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
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+ from typing import List
11
+ from typing import Optional
12
+ from typing import Tuple
13
+ from typing import Union
14
+
15
+ from .._typing import _OnConflictIndexElementsT
16
+ from .._typing import _OnConflictIndexWhereT
17
+ from .._typing import _OnConflictSetT
18
+ from .._typing import _OnConflictWhereT
19
+ from ... import util
20
+ from ...sql import coercions
21
+ from ...sql import roles
22
+ from ...sql import schema
23
+ from ...sql._typing import _DMLTableArgument
24
+ from ...sql.base import _exclusive_against
25
+ from ...sql.base import _generative
26
+ from ...sql.base import ColumnCollection
27
+ from ...sql.base import ReadOnlyColumnCollection
28
+ from ...sql.dml import Insert as StandardInsert
29
+ from ...sql.elements import ClauseElement
30
+ from ...sql.elements import ColumnElement
31
+ from ...sql.elements import KeyedColumnElement
32
+ from ...sql.elements import TextClause
33
+ from ...sql.expression import alias
34
+ from ...util.typing import Self
35
+
36
+ __all__ = ("Insert", "insert")
37
+
38
+
39
+ def insert(table: _DMLTableArgument) -> Insert:
40
+ """Construct a sqlite-specific variant :class:`_sqlite.Insert`
41
+ construct.
42
+
43
+ .. container:: inherited_member
44
+
45
+ The :func:`sqlalchemy.dialects.sqlite.insert` function creates
46
+ a :class:`sqlalchemy.dialects.sqlite.Insert`. This class is based
47
+ on the dialect-agnostic :class:`_sql.Insert` construct which may
48
+ be constructed using the :func:`_sql.insert` function in
49
+ SQLAlchemy Core.
50
+
51
+ The :class:`_sqlite.Insert` construct includes additional methods
52
+ :meth:`_sqlite.Insert.on_conflict_do_update`,
53
+ :meth:`_sqlite.Insert.on_conflict_do_nothing`.
54
+
55
+ """
56
+ return Insert(table)
57
+
58
+
59
+ class Insert(StandardInsert):
60
+ """SQLite-specific implementation of INSERT.
61
+
62
+ Adds methods for SQLite-specific syntaxes such as ON CONFLICT.
63
+
64
+ The :class:`_sqlite.Insert` object is created using the
65
+ :func:`sqlalchemy.dialects.sqlite.insert` function.
66
+
67
+ .. versionadded:: 1.4
68
+
69
+ .. seealso::
70
+
71
+ :ref:`sqlite_on_conflict_insert`
72
+
73
+ """
74
+
75
+ stringify_dialect = "sqlite"
76
+ inherit_cache = False
77
+
78
+ @util.memoized_property
79
+ def excluded(
80
+ self,
81
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
82
+ """Provide the ``excluded`` namespace for an ON CONFLICT statement
83
+
84
+ SQLite's ON CONFLICT clause allows reference to the row that would
85
+ be inserted, known as ``excluded``. This attribute provides
86
+ all columns in this row to be referenceable.
87
+
88
+ .. tip:: The :attr:`_sqlite.Insert.excluded` attribute is an instance
89
+ of :class:`_expression.ColumnCollection`, which provides an
90
+ interface the same as that of the :attr:`_schema.Table.c`
91
+ collection described at :ref:`metadata_tables_and_columns`.
92
+ With this collection, ordinary names are accessible like attributes
93
+ (e.g. ``stmt.excluded.some_column``), but special names and
94
+ dictionary method names should be accessed using indexed access,
95
+ such as ``stmt.excluded["column name"]`` or
96
+ ``stmt.excluded["values"]``. See the docstring for
97
+ :class:`_expression.ColumnCollection` for further examples.
98
+
99
+ """
100
+ return alias(self.table, name="excluded").columns
101
+
102
+ _on_conflict_exclusive = _exclusive_against(
103
+ "_post_values_clause",
104
+ msgs={
105
+ "_post_values_clause": "This Insert construct already has "
106
+ "an ON CONFLICT clause established"
107
+ },
108
+ )
109
+
110
+ @_generative
111
+ @_on_conflict_exclusive
112
+ def on_conflict_do_update(
113
+ self,
114
+ index_elements: _OnConflictIndexElementsT = None,
115
+ index_where: _OnConflictIndexWhereT = None,
116
+ set_: _OnConflictSetT = None,
117
+ where: _OnConflictWhereT = None,
118
+ ) -> Self:
119
+ r"""
120
+ Specifies a DO UPDATE SET action for ON CONFLICT clause.
121
+
122
+ :param index_elements:
123
+ A sequence consisting of string column names, :class:`_schema.Column`
124
+ objects, or other column expression objects that will be used
125
+ to infer a target index or unique constraint.
126
+
127
+ :param index_where:
128
+ Additional WHERE criterion that can be used to infer a
129
+ conditional target index.
130
+
131
+ :param set\_:
132
+ A dictionary or other mapping object
133
+ where the keys are either names of columns in the target table,
134
+ or :class:`_schema.Column` objects or other ORM-mapped columns
135
+ matching that of the target table, and expressions or literals
136
+ as values, specifying the ``SET`` actions to take.
137
+
138
+ .. versionadded:: 1.4 The
139
+ :paramref:`_sqlite.Insert.on_conflict_do_update.set_`
140
+ parameter supports :class:`_schema.Column` objects from the target
141
+ :class:`_schema.Table` as keys.
142
+
143
+ .. warning:: This dictionary does **not** take into account
144
+ Python-specified default UPDATE values or generation functions,
145
+ e.g. those specified using :paramref:`_schema.Column.onupdate`.
146
+ These values will not be exercised for an ON CONFLICT style of
147
+ UPDATE, unless they are manually specified in the
148
+ :paramref:`.Insert.on_conflict_do_update.set_` dictionary.
149
+
150
+ :param where:
151
+ Optional argument. An expression object representing a ``WHERE``
152
+ clause that restricts the rows affected by ``DO UPDATE SET``. Rows not
153
+ meeting the ``WHERE`` condition will not be updated (effectively a
154
+ ``DO NOTHING`` for those rows).
155
+
156
+ """
157
+
158
+ self._post_values_clause = OnConflictDoUpdate(
159
+ index_elements, index_where, set_, where
160
+ )
161
+ return self
162
+
163
+ @_generative
164
+ @_on_conflict_exclusive
165
+ def on_conflict_do_nothing(
166
+ self,
167
+ index_elements: _OnConflictIndexElementsT = None,
168
+ index_where: _OnConflictIndexWhereT = None,
169
+ ) -> Self:
170
+ """
171
+ Specifies a DO NOTHING action for ON CONFLICT clause.
172
+
173
+ :param index_elements:
174
+ A sequence consisting of string column names, :class:`_schema.Column`
175
+ objects, or other column expression objects that will be used
176
+ to infer a target index or unique constraint.
177
+
178
+ :param index_where:
179
+ Additional WHERE criterion that can be used to infer a
180
+ conditional target index.
181
+
182
+ """
183
+
184
+ self._post_values_clause = OnConflictDoNothing(
185
+ index_elements, index_where
186
+ )
187
+ return self
188
+
189
+
190
+ class OnConflictClause(ClauseElement):
191
+ stringify_dialect = "sqlite"
192
+
193
+ inferred_target_elements: Optional[List[Union[str, schema.Column[Any]]]]
194
+ inferred_target_whereclause: Optional[
195
+ Union[ColumnElement[Any], TextClause]
196
+ ]
197
+
198
+ def __init__(
199
+ self,
200
+ index_elements: _OnConflictIndexElementsT = None,
201
+ index_where: _OnConflictIndexWhereT = None,
202
+ ):
203
+ if index_elements is not None:
204
+ self.inferred_target_elements = [
205
+ coercions.expect(roles.DDLConstraintColumnRole, column)
206
+ for column in index_elements
207
+ ]
208
+ self.inferred_target_whereclause = (
209
+ coercions.expect(
210
+ roles.WhereHavingRole,
211
+ index_where,
212
+ )
213
+ if index_where is not None
214
+ else None
215
+ )
216
+ else:
217
+ self.inferred_target_elements = (
218
+ self.inferred_target_whereclause
219
+ ) = None
220
+
221
+
222
+ class OnConflictDoNothing(OnConflictClause):
223
+ __visit_name__ = "on_conflict_do_nothing"
224
+
225
+
226
+ class OnConflictDoUpdate(OnConflictClause):
227
+ __visit_name__ = "on_conflict_do_update"
228
+
229
+ update_values_to_set: List[Tuple[Union[schema.Column[Any], str], Any]]
230
+ update_whereclause: Optional[ColumnElement[Any]]
231
+
232
+ def __init__(
233
+ self,
234
+ index_elements: _OnConflictIndexElementsT = None,
235
+ index_where: _OnConflictIndexWhereT = None,
236
+ set_: _OnConflictSetT = None,
237
+ where: _OnConflictWhereT = None,
238
+ ):
239
+ super().__init__(
240
+ index_elements=index_elements,
241
+ index_where=index_where,
242
+ )
243
+
244
+ if isinstance(set_, dict):
245
+ if not set_:
246
+ raise ValueError("set parameter dictionary must not be empty")
247
+ elif isinstance(set_, ColumnCollection):
248
+ set_ = dict(set_)
249
+ else:
250
+ raise ValueError(
251
+ "set parameter must be a non-empty dictionary "
252
+ "or a ColumnCollection such as the `.c.` collection "
253
+ "of a Table object"
254
+ )
255
+ self.update_values_to_set = [
256
+ (coercions.expect(roles.DMLColumnRole, key), value)
257
+ for key, value in set_.items()
258
+ ]
259
+ self.update_whereclause = (
260
+ coercions.expect(roles.WhereHavingRole, where)
261
+ if where is not None
262
+ else None
263
+ )
@@ -0,0 +1,92 @@
1
+ # dialects/sqlite/json.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
+
9
+ from ... import types as sqltypes
10
+
11
+
12
+ class JSON(sqltypes.JSON):
13
+ """SQLite JSON type.
14
+
15
+ SQLite supports JSON as of version 3.9 through its JSON1_ extension. Note
16
+ that JSON1_ is a
17
+ `loadable extension <https://www.sqlite.org/loadext.html>`_ and as such
18
+ may not be available, or may require run-time loading.
19
+
20
+ :class:`_sqlite.JSON` is used automatically whenever the base
21
+ :class:`_types.JSON` datatype is used against a SQLite backend.
22
+
23
+ .. seealso::
24
+
25
+ :class:`_types.JSON` - main documentation for the generic
26
+ cross-platform JSON datatype.
27
+
28
+ The :class:`_sqlite.JSON` type supports persistence of JSON values
29
+ as well as the core index operations provided by :class:`_types.JSON`
30
+ datatype, by adapting the operations to render the ``JSON_EXTRACT``
31
+ function wrapped in the ``JSON_QUOTE`` function at the database level.
32
+ Extracted values are quoted in order to ensure that the results are
33
+ always JSON string values.
34
+
35
+
36
+ .. versionadded:: 1.3
37
+
38
+
39
+ .. _JSON1: https://www.sqlite.org/json1.html
40
+
41
+ """
42
+
43
+
44
+ # Note: these objects currently match exactly those of MySQL, however since
45
+ # these are not generalizable to all JSON implementations, remain separately
46
+ # implemented for each dialect.
47
+ class _FormatTypeMixin:
48
+ def _format_value(self, value):
49
+ raise NotImplementedError()
50
+
51
+ def bind_processor(self, dialect):
52
+ super_proc = self.string_bind_processor(dialect)
53
+
54
+ def process(value):
55
+ value = self._format_value(value)
56
+ if super_proc:
57
+ value = super_proc(value)
58
+ return value
59
+
60
+ return process
61
+
62
+ def literal_processor(self, dialect):
63
+ super_proc = self.string_literal_processor(dialect)
64
+
65
+ def process(value):
66
+ value = self._format_value(value)
67
+ if super_proc:
68
+ value = super_proc(value)
69
+ return value
70
+
71
+ return process
72
+
73
+
74
+ class JSONIndexType(_FormatTypeMixin, sqltypes.JSON.JSONIndexType):
75
+ def _format_value(self, value):
76
+ if isinstance(value, int):
77
+ value = "$[%s]" % value
78
+ else:
79
+ value = '$."%s"' % value
80
+ return value
81
+
82
+
83
+ class JSONPathType(_FormatTypeMixin, sqltypes.JSON.JSONPathType):
84
+ def _format_value(self, value):
85
+ return "$%s" % (
86
+ "".join(
87
+ [
88
+ "[%s]" % elem if isinstance(elem, int) else '."%s"' % elem
89
+ for elem in value
90
+ ]
91
+ )
92
+ )
@@ -0,0 +1,229 @@
1
+ # dialects/sqlite/provision.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
+
9
+ import os
10
+ import re
11
+
12
+ from ... import event
13
+ from ... import exc
14
+ from ...engine import url as sa_url
15
+ from ...testing import config
16
+ from ...testing.provision import create_db
17
+ from ...testing.provision import drop_db
18
+ from ...testing.provision import follower_url_from_main
19
+ from ...testing.provision import generate_driver_url
20
+ from ...testing.provision import log
21
+ from ...testing.provision import post_configure_engine
22
+ from ...testing.provision import post_configure_testing_engine
23
+ from ...testing.provision import run_reap_dbs
24
+ from ...testing.provision import stop_test_class_outside_fixtures
25
+ from ...testing.provision import temp_table_keyword_args
26
+ from ...testing.provision import upsert
27
+
28
+ # TODO: I can't get this to build dynamically with pytest-xdist procs
29
+ _drivernames = {
30
+ "pysqlite",
31
+ "aiosqlite",
32
+ "pysqlcipher",
33
+ "pysqlite_numeric",
34
+ "pysqlite_dollar",
35
+ }
36
+
37
+
38
+ def _format_url(url, driver, ident):
39
+ """given a sqlite url + desired driver + ident, make a canonical
40
+ URL out of it
41
+
42
+ """
43
+ url = sa_url.make_url(url)
44
+
45
+ if driver is None:
46
+ driver = url.get_driver_name()
47
+
48
+ filename = url.database
49
+
50
+ needs_enc = driver == "pysqlcipher"
51
+ name_token = None
52
+
53
+ if filename and filename != ":memory:":
54
+ assert "test_schema" not in filename
55
+ tokens = re.split(r"[_\.]", filename)
56
+
57
+ for token in tokens:
58
+ if token in _drivernames:
59
+ if driver is None:
60
+ driver = token
61
+ continue
62
+ elif token in ("db", "enc"):
63
+ continue
64
+ elif name_token is None:
65
+ name_token = token.strip("_")
66
+
67
+ assert name_token, f"sqlite filename has no name token: {url.database}"
68
+
69
+ new_filename = f"{name_token}_{driver}"
70
+ if ident:
71
+ new_filename += f"_{ident}"
72
+ new_filename += ".db"
73
+ if needs_enc:
74
+ new_filename += ".enc"
75
+ url = url.set(database=new_filename)
76
+
77
+ if needs_enc:
78
+ url = url.set(password="test")
79
+
80
+ url = url.set(drivername="sqlite+%s" % (driver,))
81
+
82
+ return url
83
+
84
+
85
+ @generate_driver_url.for_db("sqlite")
86
+ def generate_driver_url(url, driver, query_str):
87
+ url = _format_url(url, driver, None)
88
+
89
+ try:
90
+ url.get_dialect()
91
+ except exc.NoSuchModuleError:
92
+ return None
93
+ else:
94
+ return url
95
+
96
+
97
+ @follower_url_from_main.for_db("sqlite")
98
+ def _sqlite_follower_url_from_main(url, ident):
99
+ return _format_url(url, None, ident)
100
+
101
+
102
+ @post_configure_engine.for_db("sqlite")
103
+ def _sqlite_post_configure_engine(url, engine, follower_ident):
104
+ from sqlalchemy import event
105
+
106
+ if follower_ident:
107
+ attach_path = f"{follower_ident}_{engine.driver}_test_schema.db"
108
+ else:
109
+ attach_path = f"{engine.driver}_test_schema.db"
110
+
111
+ @event.listens_for(engine, "connect")
112
+ def connect(dbapi_connection, connection_record):
113
+ # use file DBs in all cases, memory acts kind of strangely
114
+ # as an attached
115
+
116
+ # NOTE! this has to be done *per connection*. New sqlite connection,
117
+ # as we get with say, QueuePool, the attaches are gone.
118
+ # so schemes to delete those attached files have to be done at the
119
+ # filesystem level and not rely upon what attachments are in a
120
+ # particular SQLite connection
121
+ dbapi_connection.execute(
122
+ f'ATTACH DATABASE "{attach_path}" AS test_schema'
123
+ )
124
+
125
+ @event.listens_for(engine, "engine_disposed")
126
+ def dispose(engine):
127
+ """most databases should be dropped using
128
+ stop_test_class_outside_fixtures
129
+
130
+ however a few tests like AttachedDBTest might not get triggered on
131
+ that main hook
132
+
133
+ """
134
+
135
+ if os.path.exists(attach_path):
136
+ os.remove(attach_path)
137
+
138
+ filename = engine.url.database
139
+
140
+ if filename and filename != ":memory:" and os.path.exists(filename):
141
+ os.remove(filename)
142
+
143
+
144
+ @post_configure_testing_engine.for_db("sqlite")
145
+ def _sqlite_post_configure_testing_engine(url, engine, options, scope):
146
+
147
+ sqlite_savepoint = options.get("sqlite_savepoint", False)
148
+ sqlite_share_pool = options.get("sqlite_share_pool", False)
149
+
150
+ if sqlite_savepoint and engine.name == "sqlite":
151
+ # apply SQLite savepoint workaround
152
+ @event.listens_for(engine, "connect")
153
+ def do_connect(dbapi_connection, connection_record):
154
+ dbapi_connection.isolation_level = None
155
+
156
+ @event.listens_for(engine, "begin")
157
+ def do_begin(conn):
158
+ conn.exec_driver_sql("BEGIN")
159
+
160
+ if sqlite_share_pool:
161
+ # SingletonThreadPool, StaticPool both support "transfer"
162
+ # so a new pool can share the same SQLite connection
163
+ # (single thread only)
164
+ if hasattr(engine.pool, "_transfer_from"):
165
+ options["use_reaper"] = False
166
+ engine.pool._transfer_from(config.db.pool)
167
+
168
+
169
+ @create_db.for_db("sqlite")
170
+ def _sqlite_create_db(cfg, eng, ident):
171
+ pass
172
+
173
+
174
+ @drop_db.for_db("sqlite")
175
+ def _sqlite_drop_db(cfg, eng, ident):
176
+ _drop_dbs_w_ident(eng.url.database, eng.driver, ident)
177
+
178
+
179
+ def _drop_dbs_w_ident(databasename, driver, ident):
180
+ for path in os.listdir("."):
181
+ fname, ext = os.path.split(path)
182
+ if ident in fname and ext in [".db", ".db.enc"]:
183
+ log.info("deleting SQLite database file: %s", path)
184
+ os.remove(path)
185
+
186
+
187
+ @stop_test_class_outside_fixtures.for_db("sqlite")
188
+ def stop_test_class_outside_fixtures(config, db, cls):
189
+ db.dispose()
190
+
191
+
192
+ @temp_table_keyword_args.for_db("sqlite")
193
+ def _sqlite_temp_table_keyword_args(cfg, eng):
194
+ return {"prefixes": ["TEMPORARY"]}
195
+
196
+
197
+ @run_reap_dbs.for_db("sqlite")
198
+ def _reap_sqlite_dbs(url, idents):
199
+ log.info("db reaper connecting to %r", url)
200
+ log.info("identifiers in file: %s", ", ".join(idents))
201
+ url = sa_url.make_url(url)
202
+ for ident in idents:
203
+ for drivername in _drivernames:
204
+ _drop_dbs_w_ident(url.database, drivername, ident)
205
+
206
+
207
+ @upsert.for_db("sqlite")
208
+ def _upsert(
209
+ cfg,
210
+ table,
211
+ returning,
212
+ *,
213
+ set_lambda=None,
214
+ sort_by_parameter_order=False,
215
+ index_elements=None,
216
+ ):
217
+ from sqlalchemy.dialects.sqlite import insert
218
+
219
+ stmt = insert(table)
220
+
221
+ if set_lambda:
222
+ stmt = stmt.on_conflict_do_update(set_=set_lambda(stmt.excluded))
223
+ else:
224
+ stmt = stmt.on_conflict_do_nothing()
225
+
226
+ stmt = stmt.returning(
227
+ *returning, sort_by_parameter_order=sort_by_parameter_order
228
+ )
229
+ return stmt