SQLAlchemy 2.1.0b2__cp313-cp313t-win_arm64.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 (270) hide show
  1. sqlalchemy/__init__.py +298 -0
  2. sqlalchemy/connectors/__init__.py +18 -0
  3. sqlalchemy/connectors/aioodbc.py +171 -0
  4. sqlalchemy/connectors/asyncio.py +476 -0
  5. sqlalchemy/connectors/pyodbc.py +250 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +30 -0
  8. sqlalchemy/dialects/mssql/__init__.py +89 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4166 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +285 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +220 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +126 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +698 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +226 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +214 -0
  21. sqlalchemy/dialects/mysql/base.py +3877 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +106 -0
  23. sqlalchemy/dialects/mysql/dml.py +279 -0
  24. sqlalchemy/dialects/mysql/enumerated.py +277 -0
  25. sqlalchemy/dialects/mysql/expression.py +146 -0
  26. sqlalchemy/dialects/mysql/json.py +92 -0
  27. sqlalchemy/dialects/mysql/mariadb.py +67 -0
  28. sqlalchemy/dialects/mysql/mariadbconnector.py +330 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +296 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +312 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +157 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +156 -0
  34. sqlalchemy/dialects/mysql/reflection.py +724 -0
  35. sqlalchemy/dialects/mysql/reserved_words.py +570 -0
  36. sqlalchemy/dialects/mysql/types.py +845 -0
  37. sqlalchemy/dialects/oracle/__init__.py +85 -0
  38. sqlalchemy/dialects/oracle/base.py +3977 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1601 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +158 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +909 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +368 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +171 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +229 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1323 -0
  50. sqlalchemy/dialects/postgresql/base.py +5789 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +593 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +423 -0
  55. sqlalchemy/dialects/postgresql/json.py +408 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +521 -0
  57. sqlalchemy/dialects/postgresql/operators.py +130 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +670 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +344 -0
  60. sqlalchemy/dialects/postgresql/provision.py +184 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +799 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +61 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +57 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +321 -0
  68. sqlalchemy/dialects/sqlite/base.py +3063 -0
  69. sqlalchemy/dialects/sqlite/dml.py +279 -0
  70. sqlalchemy/dialects/sqlite/json.py +100 -0
  71. sqlalchemy/dialects/sqlite/provision.py +229 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +161 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +754 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp313t-win_arm64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp313t-win_arm64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +633 -0
  80. sqlalchemy/engine/_row_cy.cp313t-win_arm64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp313t-win_arm64.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3354 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2421 -0
  88. sqlalchemy/engine/default.py +2402 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3495 -0
  91. sqlalchemy/engine/mock.py +134 -0
  92. sqlalchemy/engine/processors.py +82 -0
  93. sqlalchemy/engine/reflection.py +2100 -0
  94. sqlalchemy/engine/result.py +1966 -0
  95. sqlalchemy/engine/row.py +397 -0
  96. sqlalchemy/engine/strategies.py +16 -0
  97. sqlalchemy/engine/url.py +922 -0
  98. sqlalchemy/engine/util.py +156 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +674 -0
  102. sqlalchemy/event/base.py +472 -0
  103. sqlalchemy/event/legacy.py +258 -0
  104. sqlalchemy/event/registry.py +390 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +922 -0
  107. sqlalchemy/ext/__init__.py +11 -0
  108. sqlalchemy/ext/associationproxy.py +2072 -0
  109. sqlalchemy/ext/asyncio/__init__.py +29 -0
  110. sqlalchemy/ext/asyncio/base.py +281 -0
  111. sqlalchemy/ext/asyncio/engine.py +1487 -0
  112. sqlalchemy/ext/asyncio/exc.py +21 -0
  113. sqlalchemy/ext/asyncio/result.py +994 -0
  114. sqlalchemy/ext/asyncio/scoping.py +1679 -0
  115. sqlalchemy/ext/asyncio/session.py +2007 -0
  116. sqlalchemy/ext/automap.py +1701 -0
  117. sqlalchemy/ext/baked.py +559 -0
  118. sqlalchemy/ext/compiler.py +600 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +560 -0
  121. sqlalchemy/ext/horizontal_shard.py +481 -0
  122. sqlalchemy/ext/hybrid.py +1877 -0
  123. sqlalchemy/ext/indexable.py +364 -0
  124. sqlalchemy/ext/instrumentation.py +450 -0
  125. sqlalchemy/ext/mutable.py +1081 -0
  126. sqlalchemy/ext/orderinglist.py +439 -0
  127. sqlalchemy/ext/serializer.py +185 -0
  128. sqlalchemy/future/__init__.py +16 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +174 -0
  131. sqlalchemy/log.py +283 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +179 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +976 -0
  137. sqlalchemy/orm/bulk_persistence.py +2152 -0
  138. sqlalchemy/orm/clsregistry.py +582 -0
  139. sqlalchemy/orm/collections.py +1568 -0
  140. sqlalchemy/orm/context.py +3471 -0
  141. sqlalchemy/orm/decl_api.py +2280 -0
  142. sqlalchemy/orm/decl_base.py +2309 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +307 -0
  146. sqlalchemy/orm/evaluator.py +379 -0
  147. sqlalchemy/orm/events.py +3386 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +746 -0
  151. sqlalchemy/orm/interfaces.py +1589 -0
  152. sqlalchemy/orm/loading.py +1684 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4411 -0
  155. sqlalchemy/orm/path_registry.py +829 -0
  156. sqlalchemy/orm/persistence.py +1789 -0
  157. sqlalchemy/orm/properties.py +973 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3570 -0
  160. sqlalchemy/orm/scoping.py +2232 -0
  161. sqlalchemy/orm/session.py +5403 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3492 -0
  165. sqlalchemy/orm/strategy_options.py +2562 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +798 -0
  168. sqlalchemy/orm/util.py +2438 -0
  169. sqlalchemy/orm/writeonly.py +694 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1522 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +582 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +74 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +397 -0
  178. sqlalchemy/sql/_dml_constructors.py +132 -0
  179. sqlalchemy/sql/_elements_constructors.py +2164 -0
  180. sqlalchemy/sql/_orm_types.py +20 -0
  181. sqlalchemy/sql/_selectable_constructors.py +840 -0
  182. sqlalchemy/sql/_typing.py +487 -0
  183. sqlalchemy/sql/_util_cy.cp313t-win_arm64.pyd +0 -0
  184. sqlalchemy/sql/_util_cy.py +127 -0
  185. sqlalchemy/sql/annotation.py +590 -0
  186. sqlalchemy/sql/base.py +2699 -0
  187. sqlalchemy/sql/cache_key.py +1066 -0
  188. sqlalchemy/sql/coercions.py +1373 -0
  189. sqlalchemy/sql/compiler.py +8327 -0
  190. sqlalchemy/sql/crud.py +1815 -0
  191. sqlalchemy/sql/ddl.py +1928 -0
  192. sqlalchemy/sql/default_comparator.py +654 -0
  193. sqlalchemy/sql/dml.py +1977 -0
  194. sqlalchemy/sql/elements.py +6033 -0
  195. sqlalchemy/sql/events.py +458 -0
  196. sqlalchemy/sql/expression.py +172 -0
  197. sqlalchemy/sql/functions.py +2305 -0
  198. sqlalchemy/sql/lambdas.py +1443 -0
  199. sqlalchemy/sql/naming.py +209 -0
  200. sqlalchemy/sql/operators.py +2897 -0
  201. sqlalchemy/sql/roles.py +332 -0
  202. sqlalchemy/sql/schema.py +6703 -0
  203. sqlalchemy/sql/selectable.py +7553 -0
  204. sqlalchemy/sql/sqltypes.py +4093 -0
  205. sqlalchemy/sql/traversals.py +1042 -0
  206. sqlalchemy/sql/type_api.py +2446 -0
  207. sqlalchemy/sql/util.py +1495 -0
  208. sqlalchemy/sql/visitors.py +1157 -0
  209. sqlalchemy/testing/__init__.py +96 -0
  210. sqlalchemy/testing/assertions.py +1007 -0
  211. sqlalchemy/testing/assertsql.py +519 -0
  212. sqlalchemy/testing/asyncio.py +128 -0
  213. sqlalchemy/testing/config.py +440 -0
  214. sqlalchemy/testing/engines.py +483 -0
  215. sqlalchemy/testing/entities.py +117 -0
  216. sqlalchemy/testing/exclusions.py +476 -0
  217. sqlalchemy/testing/fixtures/__init__.py +30 -0
  218. sqlalchemy/testing/fixtures/base.py +384 -0
  219. sqlalchemy/testing/fixtures/mypy.py +247 -0
  220. sqlalchemy/testing/fixtures/orm.py +227 -0
  221. sqlalchemy/testing/fixtures/sql.py +538 -0
  222. sqlalchemy/testing/pickleable.py +155 -0
  223. sqlalchemy/testing/plugin/__init__.py +6 -0
  224. sqlalchemy/testing/plugin/bootstrap.py +51 -0
  225. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  226. sqlalchemy/testing/plugin/pytestplugin.py +892 -0
  227. sqlalchemy/testing/profiling.py +329 -0
  228. sqlalchemy/testing/provision.py +613 -0
  229. sqlalchemy/testing/requirements.py +1978 -0
  230. sqlalchemy/testing/schema.py +198 -0
  231. sqlalchemy/testing/suite/__init__.py +19 -0
  232. sqlalchemy/testing/suite/test_cte.py +237 -0
  233. sqlalchemy/testing/suite/test_ddl.py +420 -0
  234. sqlalchemy/testing/suite/test_dialect.py +776 -0
  235. sqlalchemy/testing/suite/test_insert.py +630 -0
  236. sqlalchemy/testing/suite/test_reflection.py +3557 -0
  237. sqlalchemy/testing/suite/test_results.py +660 -0
  238. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  239. sqlalchemy/testing/suite/test_select.py +2112 -0
  240. sqlalchemy/testing/suite/test_sequence.py +317 -0
  241. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  242. sqlalchemy/testing/suite/test_types.py +2271 -0
  243. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  244. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  245. sqlalchemy/testing/util.py +535 -0
  246. sqlalchemy/testing/warnings.py +52 -0
  247. sqlalchemy/types.py +76 -0
  248. sqlalchemy/util/__init__.py +158 -0
  249. sqlalchemy/util/_collections.py +688 -0
  250. sqlalchemy/util/_collections_cy.cp313t-win_arm64.pyd +0 -0
  251. sqlalchemy/util/_collections_cy.pxd +8 -0
  252. sqlalchemy/util/_collections_cy.py +516 -0
  253. sqlalchemy/util/_has_cython.py +46 -0
  254. sqlalchemy/util/_immutabledict_cy.cp313t-win_arm64.pyd +0 -0
  255. sqlalchemy/util/_immutabledict_cy.py +240 -0
  256. sqlalchemy/util/compat.py +299 -0
  257. sqlalchemy/util/concurrency.py +322 -0
  258. sqlalchemy/util/cython.py +79 -0
  259. sqlalchemy/util/deprecations.py +401 -0
  260. sqlalchemy/util/langhelpers.py +2320 -0
  261. sqlalchemy/util/preloaded.py +152 -0
  262. sqlalchemy/util/queue.py +304 -0
  263. sqlalchemy/util/tool_support.py +201 -0
  264. sqlalchemy/util/topological.py +120 -0
  265. sqlalchemy/util/typing.py +711 -0
  266. sqlalchemy-2.1.0b2.dist-info/METADATA +269 -0
  267. sqlalchemy-2.1.0b2.dist-info/RECORD +270 -0
  268. sqlalchemy-2.1.0b2.dist-info/WHEEL +5 -0
  269. sqlalchemy-2.1.0b2.dist-info/licenses/LICENSE +19 -0
  270. sqlalchemy-2.1.0b2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,279 @@
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 Dict
11
+ from typing import List
12
+ from typing import Optional
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 ColumnCollection
26
+ from ...sql.base import ReadOnlyColumnCollection
27
+ from ...sql.base import SyntaxExtension
28
+ from ...sql.dml import _DMLColumnElement
29
+ from ...sql.dml import Insert as StandardInsert
30
+ from ...sql.elements import ClauseElement
31
+ from ...sql.elements import ColumnElement
32
+ from ...sql.elements import KeyedColumnElement
33
+ from ...sql.elements import TextClause
34
+ from ...sql.expression import alias
35
+ from ...sql.sqltypes import NULLTYPE
36
+ from ...sql.visitors import InternalTraversal
37
+ from ...util.typing import Self
38
+
39
+ __all__ = ("Insert", "insert")
40
+
41
+
42
+ def insert(table: _DMLTableArgument) -> Insert:
43
+ """Construct a sqlite-specific variant :class:`_sqlite.Insert`
44
+ construct.
45
+
46
+ .. container:: inherited_member
47
+
48
+ The :func:`sqlalchemy.dialects.sqlite.insert` function creates
49
+ a :class:`sqlalchemy.dialects.sqlite.Insert`. This class is based
50
+ on the dialect-agnostic :class:`_sql.Insert` construct which may
51
+ be constructed using the :func:`_sql.insert` function in
52
+ SQLAlchemy Core.
53
+
54
+ The :class:`_sqlite.Insert` construct includes additional methods
55
+ :meth:`_sqlite.Insert.on_conflict_do_update`,
56
+ :meth:`_sqlite.Insert.on_conflict_do_nothing`.
57
+
58
+ """
59
+ return Insert(table)
60
+
61
+
62
+ class Insert(StandardInsert):
63
+ """SQLite-specific implementation of INSERT.
64
+
65
+ Adds methods for SQLite-specific syntaxes such as ON CONFLICT.
66
+
67
+ The :class:`_sqlite.Insert` object is created using the
68
+ :func:`sqlalchemy.dialects.sqlite.insert` function.
69
+
70
+ .. versionadded:: 1.4
71
+
72
+ .. seealso::
73
+
74
+ :ref:`sqlite_on_conflict_insert`
75
+
76
+ """
77
+
78
+ stringify_dialect = "sqlite"
79
+ inherit_cache = True
80
+
81
+ @util.memoized_property
82
+ def excluded(
83
+ self,
84
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
85
+ """Provide the ``excluded`` namespace for an ON CONFLICT statement
86
+
87
+ SQLite's ON CONFLICT clause allows reference to the row that would
88
+ be inserted, known as ``excluded``. This attribute provides
89
+ all columns in this row to be referenceable.
90
+
91
+ .. tip:: The :attr:`_sqlite.Insert.excluded` attribute is an instance
92
+ of :class:`_expression.ColumnCollection`, which provides an
93
+ interface the same as that of the :attr:`_schema.Table.c`
94
+ collection described at :ref:`metadata_tables_and_columns`.
95
+ With this collection, ordinary names are accessible like attributes
96
+ (e.g. ``stmt.excluded.some_column``), but special names and
97
+ dictionary method names should be accessed using indexed access,
98
+ such as ``stmt.excluded["column name"]`` or
99
+ ``stmt.excluded["values"]``. See the docstring for
100
+ :class:`_expression.ColumnCollection` for further examples.
101
+
102
+ """
103
+ return alias(self.table, name="excluded").columns
104
+
105
+ _on_conflict_exclusive = _exclusive_against(
106
+ "_post_values_clause",
107
+ msgs={
108
+ "_post_values_clause": "This Insert construct already has "
109
+ "an ON CONFLICT clause established"
110
+ },
111
+ )
112
+
113
+ @_on_conflict_exclusive
114
+ def on_conflict_do_update(
115
+ self,
116
+ index_elements: _OnConflictIndexElementsT = None,
117
+ index_where: _OnConflictIndexWhereT = None,
118
+ set_: _OnConflictSetT = None,
119
+ where: _OnConflictWhereT = None,
120
+ ) -> Self:
121
+ r"""
122
+ Specifies a DO UPDATE SET action for ON CONFLICT clause.
123
+
124
+ :param index_elements:
125
+ A sequence consisting of string column names, :class:`_schema.Column`
126
+ objects, or other column expression objects that will be used
127
+ to infer a target index or unique constraint.
128
+
129
+ :param index_where:
130
+ Additional WHERE criterion that can be used to infer a
131
+ conditional target index.
132
+
133
+ :param set\_:
134
+ A dictionary or other mapping object
135
+ where the keys are either names of columns in the target table,
136
+ or :class:`_schema.Column` objects or other ORM-mapped columns
137
+ matching that of the target table, and expressions or literals
138
+ as values, specifying the ``SET`` actions to take.
139
+
140
+ .. versionadded:: 1.4 The
141
+ :paramref:`_sqlite.Insert.on_conflict_do_update.set_`
142
+ parameter supports :class:`_schema.Column` objects from the target
143
+ :class:`_schema.Table` as keys.
144
+
145
+ .. warning:: This dictionary does **not** take into account
146
+ Python-specified default UPDATE values or generation functions,
147
+ e.g. those specified using :paramref:`_schema.Column.onupdate`.
148
+ These values will not be exercised for an ON CONFLICT style of
149
+ UPDATE, unless they are manually specified in the
150
+ :paramref:`.Insert.on_conflict_do_update.set_` dictionary.
151
+
152
+ :param where:
153
+ Optional argument. An expression object representing a ``WHERE``
154
+ clause that restricts the rows affected by ``DO UPDATE SET``. Rows not
155
+ meeting the ``WHERE`` condition will not be updated (effectively a
156
+ ``DO NOTHING`` for those rows).
157
+
158
+ """
159
+
160
+ return self.ext(
161
+ OnConflictDoUpdate(index_elements, index_where, set_, where)
162
+ )
163
+
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
+ return self.ext(OnConflictDoNothing(index_elements, index_where))
185
+
186
+
187
+ class OnConflictClause(SyntaxExtension, ClauseElement):
188
+ stringify_dialect = "sqlite"
189
+
190
+ inferred_target_elements: Optional[List[Union[str, schema.Column[Any]]]]
191
+ inferred_target_whereclause: Optional[
192
+ Union[ColumnElement[Any], TextClause]
193
+ ]
194
+
195
+ _traverse_internals = [
196
+ ("inferred_target_elements", InternalTraversal.dp_multi_list),
197
+ ("inferred_target_whereclause", InternalTraversal.dp_clauseelement),
198
+ ]
199
+
200
+ def __init__(
201
+ self,
202
+ index_elements: _OnConflictIndexElementsT = None,
203
+ index_where: _OnConflictIndexWhereT = None,
204
+ ):
205
+ if index_elements is not None:
206
+ self.inferred_target_elements = [
207
+ coercions.expect(roles.DDLConstraintColumnRole, column)
208
+ for column in index_elements
209
+ ]
210
+ self.inferred_target_whereclause = (
211
+ coercions.expect(
212
+ roles.WhereHavingRole,
213
+ index_where,
214
+ )
215
+ if index_where is not None
216
+ else None
217
+ )
218
+ else:
219
+ self.inferred_target_elements = (
220
+ self.inferred_target_whereclause
221
+ ) = None
222
+
223
+ def apply_to_insert(self, insert_stmt: StandardInsert) -> None:
224
+ insert_stmt.apply_syntax_extension_point(
225
+ self.append_replacing_same_type, "post_values"
226
+ )
227
+
228
+
229
+ class OnConflictDoNothing(OnConflictClause):
230
+ __visit_name__ = "on_conflict_do_nothing"
231
+
232
+ inherit_cache = True
233
+
234
+
235
+ class OnConflictDoUpdate(OnConflictClause):
236
+ __visit_name__ = "on_conflict_do_update"
237
+
238
+ update_values_to_set: Dict[_DMLColumnElement, ColumnElement[Any]]
239
+ update_whereclause: Optional[ColumnElement[Any]]
240
+
241
+ _traverse_internals = OnConflictClause._traverse_internals + [
242
+ ("update_values_to_set", InternalTraversal.dp_dml_values),
243
+ ("update_whereclause", InternalTraversal.dp_clauseelement),
244
+ ]
245
+
246
+ def __init__(
247
+ self,
248
+ index_elements: _OnConflictIndexElementsT = None,
249
+ index_where: _OnConflictIndexWhereT = None,
250
+ set_: _OnConflictSetT = None,
251
+ where: _OnConflictWhereT = None,
252
+ ):
253
+ super().__init__(
254
+ index_elements=index_elements,
255
+ index_where=index_where,
256
+ )
257
+
258
+ if isinstance(set_, dict):
259
+ if not set_:
260
+ raise ValueError("set parameter dictionary must not be empty")
261
+ elif isinstance(set_, ColumnCollection):
262
+ set_ = dict(set_)
263
+ else:
264
+ raise ValueError(
265
+ "set parameter must be a non-empty dictionary "
266
+ "or a ColumnCollection such as the `.c.` collection "
267
+ "of a Table object"
268
+ )
269
+ self.update_values_to_set = {
270
+ coercions.expect(roles.DMLColumnRole, k): coercions.expect(
271
+ roles.ExpressionElementRole, v, type_=NULLTYPE, is_crud=True
272
+ )
273
+ for k, v in set_.items()
274
+ }
275
+ self.update_whereclause = (
276
+ coercions.expect(roles.WhereHavingRole, where)
277
+ if where is not None
278
+ else None
279
+ )
@@ -0,0 +1,100 @@
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
+ from __future__ import annotations
8
+
9
+ from typing import Any
10
+ from typing import TYPE_CHECKING
11
+
12
+ from ... import types as sqltypes
13
+ from ...sql.sqltypes import _T_JSON
14
+
15
+ if TYPE_CHECKING:
16
+ from ...engine.interfaces import Dialect
17
+ from ...sql.type_api import _BindProcessorType
18
+ from ...sql.type_api import _LiteralProcessorType
19
+
20
+
21
+ class JSON(sqltypes.JSON[_T_JSON]):
22
+ """SQLite JSON type.
23
+
24
+ SQLite supports JSON as of version 3.9 through its JSON1_ extension. Note
25
+ that JSON1_ is a
26
+ `loadable extension <https://www.sqlite.org/loadext.html>`_ and as such
27
+ may not be available, or may require run-time loading.
28
+
29
+ :class:`_sqlite.JSON` is used automatically whenever the base
30
+ :class:`_types.JSON` datatype is used against a SQLite backend.
31
+
32
+ .. seealso::
33
+
34
+ :class:`_types.JSON` - main documentation for the generic
35
+ cross-platform JSON datatype.
36
+
37
+ The :class:`_sqlite.JSON` type supports persistence of JSON values
38
+ as well as the core index operations provided by :class:`_types.JSON`
39
+ datatype, by adapting the operations to render the ``JSON_EXTRACT``
40
+ function wrapped in the ``JSON_QUOTE`` function at the database level.
41
+ Extracted values are quoted in order to ensure that the results are
42
+ always JSON string values.
43
+
44
+
45
+ .. _JSON1: https://www.sqlite.org/json1.html
46
+
47
+ """
48
+
49
+
50
+ # Note: these objects currently match exactly those of MySQL, however since
51
+ # these are not generalizable to all JSON implementations, remain separately
52
+ # implemented for each dialect.
53
+ class _FormatTypeMixin:
54
+ def _format_value(self, value: Any) -> str:
55
+ raise NotImplementedError()
56
+
57
+ def bind_processor(self, dialect: Dialect) -> _BindProcessorType[Any]:
58
+ super_proc = self.string_bind_processor(dialect) # type: ignore[attr-defined] # noqa: E501
59
+
60
+ def process(value: Any) -> Any:
61
+ value = self._format_value(value)
62
+ if super_proc:
63
+ value = super_proc(value)
64
+ return value
65
+
66
+ return process
67
+
68
+ def literal_processor(
69
+ self, dialect: Dialect
70
+ ) -> _LiteralProcessorType[Any]:
71
+ super_proc = self.string_literal_processor(dialect) # type: ignore[attr-defined] # noqa: E501
72
+
73
+ def process(value: Any) -> str:
74
+ value = self._format_value(value)
75
+ if super_proc:
76
+ value = super_proc(value)
77
+ return value # type: ignore[no-any-return]
78
+
79
+ return process
80
+
81
+
82
+ class JSONIndexType(_FormatTypeMixin, sqltypes.JSON.JSONIndexType):
83
+ def _format_value(self, value: Any) -> str:
84
+ if isinstance(value, int):
85
+ formatted_value = "$[%s]" % value
86
+ else:
87
+ formatted_value = '$."%s"' % value
88
+ return formatted_value
89
+
90
+
91
+ class JSONPathType(_FormatTypeMixin, sqltypes.JSON.JSONPathType):
92
+ def _format_value(self, value: Any) -> str:
93
+ return "$%s" % (
94
+ "".join(
95
+ [
96
+ "[%s]" % elem if isinstance(elem, int) else '."%s"' % elem
97
+ for elem in value
98
+ ]
99
+ )
100
+ )
@@ -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