SQLAlchemy 2.1.0rc1__cp315-cp315-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 (276) hide show
  1. sqlalchemy/__init__.py +299 -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 +248 -0
  6. sqlalchemy/dialects/__init__.py +62 -0
  7. sqlalchemy/dialects/_typing.py +29 -0
  8. sqlalchemy/dialects/mssql/__init__.py +88 -0
  9. sqlalchemy/dialects/mssql/aioodbc.py +63 -0
  10. sqlalchemy/dialects/mssql/base.py +4833 -0
  11. sqlalchemy/dialects/mssql/information_schema.py +345 -0
  12. sqlalchemy/dialects/mssql/json.py +140 -0
  13. sqlalchemy/dialects/mssql/mssqlpython.py +242 -0
  14. sqlalchemy/dialects/mssql/provision.py +196 -0
  15. sqlalchemy/dialects/mssql/pymssql.py +130 -0
  16. sqlalchemy/dialects/mssql/pyodbc.py +697 -0
  17. sqlalchemy/dialects/mysql/__init__.py +106 -0
  18. sqlalchemy/dialects/mysql/_mariadb_shim.py +312 -0
  19. sqlalchemy/dialects/mysql/aiomysql.py +260 -0
  20. sqlalchemy/dialects/mysql/asyncmy.py +241 -0
  21. sqlalchemy/dialects/mysql/base.py +3896 -0
  22. sqlalchemy/dialects/mysql/cymysql.py +107 -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 +314 -0
  29. sqlalchemy/dialects/mysql/mysqlconnector.py +291 -0
  30. sqlalchemy/dialects/mysql/mysqldb.py +318 -0
  31. sqlalchemy/dialects/mysql/provision.py +153 -0
  32. sqlalchemy/dialects/mysql/pymysql.py +188 -0
  33. sqlalchemy/dialects/mysql/pyodbc.py +157 -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 +3847 -0
  39. sqlalchemy/dialects/oracle/cx_oracle.py +1736 -0
  40. sqlalchemy/dialects/oracle/dictionary.py +507 -0
  41. sqlalchemy/dialects/oracle/json.py +157 -0
  42. sqlalchemy/dialects/oracle/oracledb.py +898 -0
  43. sqlalchemy/dialects/oracle/provision.py +288 -0
  44. sqlalchemy/dialects/oracle/types.py +367 -0
  45. sqlalchemy/dialects/oracle/vector.py +366 -0
  46. sqlalchemy/dialects/postgresql/__init__.py +170 -0
  47. sqlalchemy/dialects/postgresql/_psycopg_common.py +232 -0
  48. sqlalchemy/dialects/postgresql/array.py +534 -0
  49. sqlalchemy/dialects/postgresql/asyncpg.py +1318 -0
  50. sqlalchemy/dialects/postgresql/base.py +5935 -0
  51. sqlalchemy/dialects/postgresql/bitstring.py +327 -0
  52. sqlalchemy/dialects/postgresql/dml.py +360 -0
  53. sqlalchemy/dialects/postgresql/ext.py +599 -0
  54. sqlalchemy/dialects/postgresql/hstore.py +422 -0
  55. sqlalchemy/dialects/postgresql/json.py +411 -0
  56. sqlalchemy/dialects/postgresql/named_types.py +535 -0
  57. sqlalchemy/dialects/postgresql/operators.py +129 -0
  58. sqlalchemy/dialects/postgresql/pg8000.py +655 -0
  59. sqlalchemy/dialects/postgresql/pg_catalog.py +345 -0
  60. sqlalchemy/dialects/postgresql/provision.py +202 -0
  61. sqlalchemy/dialects/postgresql/psycopg.py +800 -0
  62. sqlalchemy/dialects/postgresql/psycopg2.py +860 -0
  63. sqlalchemy/dialects/postgresql/psycopg2cffi.py +62 -0
  64. sqlalchemy/dialects/postgresql/ranges.py +1002 -0
  65. sqlalchemy/dialects/postgresql/types.py +388 -0
  66. sqlalchemy/dialects/sqlite/__init__.py +59 -0
  67. sqlalchemy/dialects/sqlite/aiosqlite.py +375 -0
  68. sqlalchemy/dialects/sqlite/base.py +3103 -0
  69. sqlalchemy/dialects/sqlite/dml.py +314 -0
  70. sqlalchemy/dialects/sqlite/json.py +134 -0
  71. sqlalchemy/dialects/sqlite/provision.py +237 -0
  72. sqlalchemy/dialects/sqlite/pysqlcipher.py +166 -0
  73. sqlalchemy/dialects/sqlite/pysqlite.py +959 -0
  74. sqlalchemy/dialects/type_migration_guidelines.txt +145 -0
  75. sqlalchemy/engine/__init__.py +62 -0
  76. sqlalchemy/engine/_processors_cy.cp315-win32.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win32.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win32.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win32.pyd +0 -0
  83. sqlalchemy/engine/_util_cy.py +136 -0
  84. sqlalchemy/engine/base.py +3357 -0
  85. sqlalchemy/engine/characteristics.py +155 -0
  86. sqlalchemy/engine/create.py +877 -0
  87. sqlalchemy/engine/cursor.py +2425 -0
  88. sqlalchemy/engine/default.py +2627 -0
  89. sqlalchemy/engine/events.py +965 -0
  90. sqlalchemy/engine/interfaces.py +3636 -0
  91. sqlalchemy/engine/mock.py +133 -0
  92. sqlalchemy/engine/processors.py +83 -0
  93. sqlalchemy/engine/reflection.py +2141 -0
  94. sqlalchemy/engine/result.py +2012 -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 +164 -0
  99. sqlalchemy/event/__init__.py +26 -0
  100. sqlalchemy/event/api.py +220 -0
  101. sqlalchemy/event/attr.py +675 -0
  102. sqlalchemy/event/base.py +473 -0
  103. sqlalchemy/event/legacy.py +259 -0
  104. sqlalchemy/event/registry.py +391 -0
  105. sqlalchemy/events.py +17 -0
  106. sqlalchemy/exc.py +939 -0
  107. sqlalchemy/ext/__init__.py +10 -0
  108. sqlalchemy/ext/associationproxy.py +2073 -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 +2006 -0
  116. sqlalchemy/ext/automap.py +1702 -0
  117. sqlalchemy/ext/baked.py +558 -0
  118. sqlalchemy/ext/compiler.py +601 -0
  119. sqlalchemy/ext/declarative/__init__.py +65 -0
  120. sqlalchemy/ext/declarative/extensions.py +561 -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 +440 -0
  127. sqlalchemy/ext/serializer.py +184 -0
  128. sqlalchemy/future/__init__.py +17 -0
  129. sqlalchemy/future/engine.py +15 -0
  130. sqlalchemy/inspection.py +188 -0
  131. sqlalchemy/log.py +279 -0
  132. sqlalchemy/orm/__init__.py +176 -0
  133. sqlalchemy/orm/_orm_constructors.py +2694 -0
  134. sqlalchemy/orm/_typing.py +180 -0
  135. sqlalchemy/orm/attributes.py +2868 -0
  136. sqlalchemy/orm/base.py +991 -0
  137. sqlalchemy/orm/bulk_persistence.py +2168 -0
  138. sqlalchemy/orm/clsregistry.py +630 -0
  139. sqlalchemy/orm/collections.py +1569 -0
  140. sqlalchemy/orm/context.py +3475 -0
  141. sqlalchemy/orm/decl_api.py +2283 -0
  142. sqlalchemy/orm/decl_base.py +2320 -0
  143. sqlalchemy/orm/dependency.py +1306 -0
  144. sqlalchemy/orm/descriptor_props.py +1183 -0
  145. sqlalchemy/orm/dynamic.py +306 -0
  146. sqlalchemy/orm/evaluator.py +378 -0
  147. sqlalchemy/orm/events.py +3387 -0
  148. sqlalchemy/orm/exc.py +237 -0
  149. sqlalchemy/orm/identity.py +302 -0
  150. sqlalchemy/orm/instrumentation.py +749 -0
  151. sqlalchemy/orm/interfaces.py +1595 -0
  152. sqlalchemy/orm/loading.py +1712 -0
  153. sqlalchemy/orm/mapped_collection.py +557 -0
  154. sqlalchemy/orm/mapper.py +4465 -0
  155. sqlalchemy/orm/path_registry.py +907 -0
  156. sqlalchemy/orm/persistence.py +1790 -0
  157. sqlalchemy/orm/properties.py +972 -0
  158. sqlalchemy/orm/query.py +3528 -0
  159. sqlalchemy/orm/relationships.py +3608 -0
  160. sqlalchemy/orm/scoping.py +2233 -0
  161. sqlalchemy/orm/session.py +5468 -0
  162. sqlalchemy/orm/state.py +1175 -0
  163. sqlalchemy/orm/state_changes.py +196 -0
  164. sqlalchemy/orm/strategies.py +3552 -0
  165. sqlalchemy/orm/strategy_options.py +2648 -0
  166. sqlalchemy/orm/sync.py +164 -0
  167. sqlalchemy/orm/unitofwork.py +797 -0
  168. sqlalchemy/orm/util.py +2461 -0
  169. sqlalchemy/orm/writeonly.py +701 -0
  170. sqlalchemy/pool/__init__.py +41 -0
  171. sqlalchemy/pool/base.py +1540 -0
  172. sqlalchemy/pool/events.py +375 -0
  173. sqlalchemy/pool/impl.py +583 -0
  174. sqlalchemy/py.typed +0 -0
  175. sqlalchemy/schema.py +75 -0
  176. sqlalchemy/sql/__init__.py +156 -0
  177. sqlalchemy/sql/_annotated_cols.py +402 -0
  178. sqlalchemy/sql/_cache_key_cy.cp315-win32.pyd +0 -0
  179. sqlalchemy/sql/_cache_key_cy.py +363 -0
  180. sqlalchemy/sql/_dml_constructors.py +132 -0
  181. sqlalchemy/sql/_elements_constructors.py +2190 -0
  182. sqlalchemy/sql/_orm_types.py +19 -0
  183. sqlalchemy/sql/_selectable_constructors.py +840 -0
  184. sqlalchemy/sql/_typing.py +500 -0
  185. sqlalchemy/sql/_util_cy.cp315-win32.pyd +0 -0
  186. sqlalchemy/sql/_util_cy.pxd +11 -0
  187. sqlalchemy/sql/_util_cy.py +127 -0
  188. sqlalchemy/sql/annotation.py +590 -0
  189. sqlalchemy/sql/base.py +2702 -0
  190. sqlalchemy/sql/cache_key.py +915 -0
  191. sqlalchemy/sql/coercions.py +1373 -0
  192. sqlalchemy/sql/compiler.py +8453 -0
  193. sqlalchemy/sql/crud.py +1816 -0
  194. sqlalchemy/sql/ddl.py +1962 -0
  195. sqlalchemy/sql/default_comparator.py +660 -0
  196. sqlalchemy/sql/dml.py +2018 -0
  197. sqlalchemy/sql/elements.py +6057 -0
  198. sqlalchemy/sql/events.py +458 -0
  199. sqlalchemy/sql/expression.py +171 -0
  200. sqlalchemy/sql/functions.py +2380 -0
  201. sqlalchemy/sql/lambdas.py +1442 -0
  202. sqlalchemy/sql/naming.py +204 -0
  203. sqlalchemy/sql/operators.py +2909 -0
  204. sqlalchemy/sql/roles.py +332 -0
  205. sqlalchemy/sql/schema.py +7075 -0
  206. sqlalchemy/sql/selectable.py +7634 -0
  207. sqlalchemy/sql/sqltypes.py +4130 -0
  208. sqlalchemy/sql/traversals.py +1041 -0
  209. sqlalchemy/sql/type_api.py +2450 -0
  210. sqlalchemy/sql/util.py +1496 -0
  211. sqlalchemy/sql/visitors.py +1153 -0
  212. sqlalchemy/testing/__init__.py +97 -0
  213. sqlalchemy/testing/assertions.py +1007 -0
  214. sqlalchemy/testing/assertsql.py +519 -0
  215. sqlalchemy/testing/asyncio.py +128 -0
  216. sqlalchemy/testing/cancellation.py +237 -0
  217. sqlalchemy/testing/config.py +440 -0
  218. sqlalchemy/testing/engines.py +482 -0
  219. sqlalchemy/testing/entities.py +117 -0
  220. sqlalchemy/testing/exclusions.py +501 -0
  221. sqlalchemy/testing/fixtures/__init__.py +30 -0
  222. sqlalchemy/testing/fixtures/base.py +426 -0
  223. sqlalchemy/testing/fixtures/mypy.py +247 -0
  224. sqlalchemy/testing/fixtures/orm.py +227 -0
  225. sqlalchemy/testing/fixtures/sql.py +538 -0
  226. sqlalchemy/testing/pickleable.py +155 -0
  227. sqlalchemy/testing/plugin/__init__.py +6 -0
  228. sqlalchemy/testing/plugin/bootstrap.py +50 -0
  229. sqlalchemy/testing/plugin/plugin_base.py +828 -0
  230. sqlalchemy/testing/plugin/pytestplugin.py +896 -0
  231. sqlalchemy/testing/profiles_file.py +350 -0
  232. sqlalchemy/testing/profiling.py +294 -0
  233. sqlalchemy/testing/provision.py +633 -0
  234. sqlalchemy/testing/requirements.py +1971 -0
  235. sqlalchemy/testing/schema.py +198 -0
  236. sqlalchemy/testing/suite/__init__.py +19 -0
  237. sqlalchemy/testing/suite/test_cte.py +237 -0
  238. sqlalchemy/testing/suite/test_ddl.py +420 -0
  239. sqlalchemy/testing/suite/test_dialect.py +776 -0
  240. sqlalchemy/testing/suite/test_insert.py +630 -0
  241. sqlalchemy/testing/suite/test_reflection.py +3815 -0
  242. sqlalchemy/testing/suite/test_results.py +660 -0
  243. sqlalchemy/testing/suite/test_rowcount.py +258 -0
  244. sqlalchemy/testing/suite/test_select.py +2112 -0
  245. sqlalchemy/testing/suite/test_sequence.py +317 -0
  246. sqlalchemy/testing/suite/test_table_via_select.py +686 -0
  247. sqlalchemy/testing/suite/test_types.py +2271 -0
  248. sqlalchemy/testing/suite/test_unicode_ddl.py +189 -0
  249. sqlalchemy/testing/suite/test_update_delete.py +139 -0
  250. sqlalchemy/testing/util.py +575 -0
  251. sqlalchemy/testing/warnings.py +52 -0
  252. sqlalchemy/types.py +75 -0
  253. sqlalchemy/util/__init__.py +165 -0
  254. sqlalchemy/util/_collections.py +688 -0
  255. sqlalchemy/util/_collections_cy.cp315-win32.pyd +0 -0
  256. sqlalchemy/util/_collections_cy.pxd +8 -0
  257. sqlalchemy/util/_collections_cy.py +516 -0
  258. sqlalchemy/util/_has_cython.py +48 -0
  259. sqlalchemy/util/_immutabledict_cy.cp315-win32.pyd +0 -0
  260. sqlalchemy/util/_immutabledict_cy.py +240 -0
  261. sqlalchemy/util/compat.py +298 -0
  262. sqlalchemy/util/concurrency.py +272 -0
  263. sqlalchemy/util/cython.py +95 -0
  264. sqlalchemy/util/deprecations.py +401 -0
  265. sqlalchemy/util/langhelpers.py +2797 -0
  266. sqlalchemy/util/preloaded.py +153 -0
  267. sqlalchemy/util/queue.py +304 -0
  268. sqlalchemy/util/tool_support.py +202 -0
  269. sqlalchemy/util/topological.py +120 -0
  270. sqlalchemy/util/typing.py +709 -0
  271. sqlalchemy-2.1.0rc1.dist-info/METADATA +270 -0
  272. sqlalchemy-2.1.0rc1.dist-info/RECORD +276 -0
  273. sqlalchemy-2.1.0rc1.dist-info/WHEEL +5 -0
  274. sqlalchemy-2.1.0rc1.dist-info/licenses/AUTHORS +30 -0
  275. sqlalchemy-2.1.0rc1.dist-info/licenses/LICENSE +19 -0
  276. sqlalchemy-2.1.0rc1.dist-info/top_level.txt +1 -0
@@ -0,0 +1,314 @@
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 Sequence
14
+ from typing import Union
15
+
16
+ from .._typing import _OnConflictIndexElementsT
17
+ from .._typing import _OnConflictIndexWhereT
18
+ from .._typing import _OnConflictSetT
19
+ from .._typing import _OnConflictWhereT
20
+ from ... import exc
21
+ from ... import util
22
+ from ...sql import coercions
23
+ from ...sql import roles
24
+ from ...sql import schema
25
+ from ...sql._typing import _DMLTableArgument
26
+ from ...sql.base import ColumnCollection
27
+ from ...sql.base import ReadOnlyColumnCollection
28
+ from ...sql.base import SyntaxExtension
29
+ from ...sql.dml import _DMLColumnElement
30
+ from ...sql.dml import Insert as StandardInsert
31
+ from ...sql.elements import ClauseElement
32
+ from ...sql.elements import ColumnElement
33
+ from ...sql.elements import KeyedColumnElement
34
+ from ...sql.elements import TextClause
35
+ from ...sql.expression import alias
36
+ from ...sql.sqltypes import NULLTYPE
37
+ from ...sql.visitors import InternalTraversal
38
+ from ...util.typing import Self
39
+
40
+ __all__ = ("Insert", "insert")
41
+
42
+
43
+ def insert(table: _DMLTableArgument) -> Insert:
44
+ """Construct a sqlite-specific variant :class:`_sqlite.Insert`
45
+ construct.
46
+
47
+ .. container:: inherited_member
48
+
49
+ The :func:`sqlalchemy.dialects.sqlite.insert` function creates
50
+ a :class:`sqlalchemy.dialects.sqlite.Insert`. This class is based
51
+ on the dialect-agnostic :class:`_sql.Insert` construct which may
52
+ be constructed using the :func:`_sql.insert` function in
53
+ SQLAlchemy Core.
54
+
55
+ The :class:`_sqlite.Insert` construct includes additional methods
56
+ :meth:`_sqlite.Insert.on_conflict_do_update`,
57
+ :meth:`_sqlite.Insert.on_conflict_do_nothing`.
58
+
59
+ """
60
+ return Insert(table)
61
+
62
+
63
+ class Insert(StandardInsert):
64
+ """SQLite-specific implementation of INSERT.
65
+
66
+ Adds methods for SQLite-specific syntaxes such as ON CONFLICT.
67
+
68
+ The :class:`_sqlite.Insert` object is created using the
69
+ :func:`sqlalchemy.dialects.sqlite.insert` function.
70
+
71
+ .. versionadded:: 1.4
72
+
73
+ .. seealso::
74
+
75
+ :ref:`sqlite_on_conflict_insert`
76
+
77
+ """
78
+
79
+ stringify_dialect = "sqlite"
80
+ inherit_cache = True
81
+
82
+ @util.memoized_property
83
+ def excluded(
84
+ self,
85
+ ) -> ReadOnlyColumnCollection[str, KeyedColumnElement[Any]]:
86
+ """Provide the ``excluded`` namespace for an ON CONFLICT statement
87
+
88
+ SQLite's ON CONFLICT clause allows reference to the row that would
89
+ be inserted, known as ``excluded``. This attribute provides
90
+ all columns in this row to be referenceable.
91
+
92
+ .. tip:: The :attr:`_sqlite.Insert.excluded` attribute is an instance
93
+ of :class:`_expression.ColumnCollection`, which provides an
94
+ interface the same as that of the :attr:`_schema.Table.c`
95
+ collection described at :ref:`metadata_tables_and_columns`.
96
+ With this collection, ordinary names are accessible like attributes
97
+ (e.g. ``stmt.excluded.some_column``), but special names and
98
+ dictionary method names should be accessed using indexed access,
99
+ such as ``stmt.excluded["column name"]`` or
100
+ ``stmt.excluded["values"]``. See the docstring for
101
+ :class:`_expression.ColumnCollection` for further examples.
102
+
103
+ """
104
+ return alias(self.table, name="excluded").columns
105
+
106
+ def on_conflict_do_update(
107
+ self,
108
+ index_elements: _OnConflictIndexElementsT = None,
109
+ index_where: _OnConflictIndexWhereT = None,
110
+ set_: _OnConflictSetT = None,
111
+ where: _OnConflictWhereT = None,
112
+ ) -> Self:
113
+ r"""
114
+ Specifies a DO UPDATE SET action for ON CONFLICT clause.
115
+
116
+ This method may be invoked more than once against the same
117
+ :class:`_sqlite.Insert` construct, where each ``ON CONFLICT`` clause
118
+ renders in the order in which it was established.
119
+
120
+ .. versionadded:: 2.1 Multiple ``ON CONFLICT`` clauses may be
121
+ established on a single :class:`_sqlite.Insert` construct.
122
+
123
+ .. seealso::
124
+
125
+ :ref:`sqlite_on_conflict_multiple`
126
+
127
+ :param index_elements:
128
+ A sequence consisting of string column names, :class:`_schema.Column`
129
+ objects, or other column expression objects that will be used
130
+ to infer a target index or unique constraint.
131
+
132
+ :param index_where:
133
+ Additional WHERE criterion that can be used to infer a
134
+ conditional target index.
135
+
136
+ :param set\_:
137
+ A dictionary or other mapping object
138
+ where the keys are either names of columns in the target table,
139
+ or :class:`_schema.Column` objects or other ORM-mapped columns
140
+ matching that of the target table, and expressions or literals
141
+ as values, specifying the ``SET`` actions to take.
142
+
143
+ .. versionadded:: 1.4 The
144
+ :paramref:`_sqlite.Insert.on_conflict_do_update.set_`
145
+ parameter supports :class:`_schema.Column` objects from the target
146
+ :class:`_schema.Table` as keys.
147
+
148
+ .. warning:: This dictionary does **not** take into account
149
+ Python-specified default UPDATE values or generation functions,
150
+ e.g. those specified using :paramref:`_schema.Column.onupdate`.
151
+ These values will not be exercised for an ON CONFLICT style of
152
+ UPDATE, unless they are manually specified in the
153
+ :paramref:`.Insert.on_conflict_do_update.set_` dictionary.
154
+
155
+ :param where:
156
+ Optional argument. An expression object representing a ``WHERE``
157
+ clause that restricts the rows affected by ``DO UPDATE SET``. Rows not
158
+ meeting the ``WHERE`` condition will not be updated (effectively a
159
+ ``DO NOTHING`` for those rows).
160
+
161
+ """
162
+
163
+ return self.ext(
164
+ OnConflictDoUpdate(index_elements, index_where, set_, where)
165
+ )
166
+
167
+ def on_conflict_do_nothing(
168
+ self,
169
+ index_elements: _OnConflictIndexElementsT = None,
170
+ index_where: _OnConflictIndexWhereT = None,
171
+ ) -> Self:
172
+ """
173
+ Specifies a DO NOTHING action for ON CONFLICT clause.
174
+
175
+ This method may be invoked more than once against the same
176
+ :class:`_sqlite.Insert` construct, and may be combined with
177
+ :meth:`_sqlite.Insert.on_conflict_do_update`, where each
178
+ ``ON CONFLICT`` clause renders in the order in which it was
179
+ established. As SQLite allows only the last ``ON CONFLICT`` clause
180
+ of a statement to omit its conflict target, a call that omits
181
+ :paramref:`_sqlite.Insert.on_conflict_do_nothing.index_elements`
182
+ must be the last clause established, else
183
+ :class:`.InvalidRequestError` is raised.
184
+
185
+ .. versionadded:: 2.1 Multiple ``ON CONFLICT`` clauses may be
186
+ established on a single :class:`_sqlite.Insert` construct.
187
+
188
+ .. seealso::
189
+
190
+ :ref:`sqlite_on_conflict_multiple`
191
+
192
+ :param index_elements:
193
+ A sequence consisting of string column names, :class:`_schema.Column`
194
+ objects, or other column expression objects that will be used
195
+ to infer a target index or unique constraint.
196
+
197
+ :param index_where:
198
+ Additional WHERE criterion that can be used to infer a
199
+ conditional target index.
200
+
201
+ """
202
+
203
+ return self.ext(OnConflictDoNothing(index_elements, index_where))
204
+
205
+
206
+ class OnConflictClause(SyntaxExtension, ClauseElement):
207
+ stringify_dialect = "sqlite"
208
+
209
+ inferred_target_elements: Optional[List[Union[str, schema.Column[Any]]]]
210
+ inferred_target_whereclause: Optional[
211
+ Union[ColumnElement[Any], TextClause]
212
+ ]
213
+
214
+ _traverse_internals = [
215
+ ("inferred_target_elements", InternalTraversal.dp_multi_list),
216
+ ("inferred_target_whereclause", InternalTraversal.dp_clauseelement),
217
+ ]
218
+
219
+ def __init__(
220
+ self,
221
+ index_elements: _OnConflictIndexElementsT = None,
222
+ index_where: _OnConflictIndexWhereT = None,
223
+ ):
224
+ if index_elements is not None:
225
+ self.inferred_target_elements = [
226
+ coercions.expect(roles.DDLConstraintColumnRole, column)
227
+ for column in index_elements
228
+ ]
229
+ self.inferred_target_whereclause = (
230
+ coercions.expect(
231
+ roles.WhereHavingRole,
232
+ index_where,
233
+ )
234
+ if index_where is not None
235
+ else None
236
+ )
237
+ else:
238
+ self.inferred_target_elements = (
239
+ self.inferred_target_whereclause
240
+ ) = None
241
+
242
+ def _append_to_existing(
243
+ self, existing: Sequence[ClauseElement]
244
+ ) -> Sequence[ClauseElement]:
245
+ if existing:
246
+ last = existing[-1]
247
+ if (
248
+ isinstance(last, OnConflictClause)
249
+ and last.inferred_target_elements is None
250
+ ):
251
+ raise exc.InvalidRequestError(
252
+ "This Insert construct already has an ON CONFLICT "
253
+ "clause that omits a conflict target; such a clause "
254
+ "must be the last ON CONFLICT clause in the statement"
255
+ )
256
+ return [*existing, self]
257
+
258
+ def apply_to_insert(self, insert_stmt: StandardInsert) -> None:
259
+ insert_stmt.apply_syntax_extension_point(
260
+ self._append_to_existing, "post_values"
261
+ )
262
+
263
+
264
+ class OnConflictDoNothing(OnConflictClause):
265
+ __visit_name__ = "on_conflict_do_nothing"
266
+
267
+ inherit_cache = True
268
+
269
+
270
+ class OnConflictDoUpdate(OnConflictClause):
271
+ __visit_name__ = "on_conflict_do_update"
272
+
273
+ update_values_to_set: Dict[_DMLColumnElement, ColumnElement[Any]]
274
+ update_whereclause: Optional[ColumnElement[Any]]
275
+
276
+ _traverse_internals = OnConflictClause._traverse_internals + [
277
+ ("update_values_to_set", InternalTraversal.dp_dml_values),
278
+ ("update_whereclause", InternalTraversal.dp_clauseelement),
279
+ ]
280
+
281
+ def __init__(
282
+ self,
283
+ index_elements: _OnConflictIndexElementsT = None,
284
+ index_where: _OnConflictIndexWhereT = None,
285
+ set_: _OnConflictSetT = None,
286
+ where: _OnConflictWhereT = None,
287
+ ):
288
+ super().__init__(
289
+ index_elements=index_elements,
290
+ index_where=index_where,
291
+ )
292
+
293
+ if isinstance(set_, dict):
294
+ if not set_:
295
+ raise ValueError("set parameter dictionary must not be empty")
296
+ elif isinstance(set_, ColumnCollection):
297
+ set_ = dict(set_)
298
+ else:
299
+ raise ValueError(
300
+ "set parameter must be a non-empty dictionary "
301
+ "or a ColumnCollection such as the `.c.` collection "
302
+ "of a Table object"
303
+ )
304
+ self.update_values_to_set = {
305
+ coercions.expect(roles.DMLColumnRole, k): coercions.expect(
306
+ roles.ExpressionElementRole, v, type_=NULLTYPE, is_crud=True
307
+ )
308
+ for k, v in set_.items()
309
+ }
310
+ self.update_whereclause = (
311
+ coercions.expect(roles.WhereHavingRole, where)
312
+ if where is not None
313
+ else None
314
+ )
@@ -0,0 +1,134 @@
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 import func
14
+ from ...sql.sqltypes import _T_JSON
15
+
16
+ if TYPE_CHECKING:
17
+ from ...engine.interfaces import Dialect
18
+ from ...sql.elements import ColumnElement
19
+ from ...sql.type_api import _BindProcessorType
20
+ from ...sql.type_api import _LiteralProcessorType
21
+
22
+
23
+ class JSON(sqltypes.JSON[_T_JSON]):
24
+ """SQLite JSON type.
25
+
26
+ SQLite supports JSON as of version 3.9 through its JSON1_ extension. Note
27
+ that JSON1_ is a
28
+ `loadable extension <https://www.sqlite.org/loadext.html>`_ and as such
29
+ may not be available, or may require run-time loading.
30
+
31
+ :class:`_sqlite.JSON` is used automatically whenever the base
32
+ :class:`_types.JSON` datatype is used against a SQLite backend.
33
+
34
+ .. seealso::
35
+
36
+ :class:`_types.JSON` - main documentation for the generic
37
+ cross-platform JSON datatype.
38
+
39
+ The :class:`_sqlite.JSON` type supports persistence of JSON values
40
+ as well as the core index operations provided by :class:`_types.JSON`
41
+ datatype, by adapting the operations to render the ``JSON_EXTRACT``
42
+ function wrapped in the ``JSON_QUOTE`` function at the database level.
43
+ Extracted values are quoted in order to ensure that the results are
44
+ always JSON string values.
45
+
46
+
47
+ .. _JSON1: https://www.sqlite.org/json1.html
48
+
49
+ """
50
+
51
+
52
+ class JSONB(JSON[_T_JSON]):
53
+ """SQLite JSONB type.
54
+
55
+ Stores JSON data in SQLite's binary JSONB format, available as of
56
+ SQLite version 3.45.0. The binary format is more compact and faster
57
+ to parse than the text-based :class:`_sqlite.JSON` type.
58
+
59
+ Values are transparently stored using the ``jsonb()`` SQL function and
60
+ retrieved as text JSON via the ``json()`` SQL function, so the Python
61
+ side behaves identically to :class:`_sqlite.JSON`.
62
+
63
+ .. versionadded:: 2.1b3
64
+
65
+ .. seealso::
66
+
67
+ :class:`_sqlite.JSON`
68
+
69
+ https://sqlite.org/jsonb.html
70
+
71
+ """
72
+
73
+ __visit_name__ = "JSONB"
74
+
75
+ def bind_expression(
76
+ self, bindvalue: ColumnElement[Any]
77
+ ) -> ColumnElement[Any]:
78
+ return func.jsonb(bindvalue, type_=self)
79
+
80
+ def column_expression(self, col: ColumnElement[Any]) -> ColumnElement[Any]:
81
+ return func.json(col, type_=self)
82
+
83
+
84
+ # Note: these objects currently match exactly those of MySQL, however since
85
+ # these are not generalizable to all JSON implementations, remain separately
86
+ # implemented for each dialect.
87
+ class _FormatTypeMixin:
88
+ def _format_value(self, value: Any) -> str:
89
+ raise NotImplementedError()
90
+
91
+ def bind_processor(self, dialect: Dialect) -> _BindProcessorType[Any]:
92
+ super_proc = self.string_bind_processor(dialect) # type: ignore[attr-defined] # noqa: E501
93
+
94
+ def process(value: Any) -> Any:
95
+ value = self._format_value(value)
96
+ if super_proc:
97
+ value = super_proc(value)
98
+ return value
99
+
100
+ return process
101
+
102
+ def literal_processor(
103
+ self, dialect: Dialect
104
+ ) -> _LiteralProcessorType[Any]:
105
+ super_proc = self.string_literal_processor(dialect) # type: ignore[attr-defined] # noqa: E501
106
+
107
+ def process(value: Any) -> str:
108
+ value = self._format_value(value)
109
+ if super_proc:
110
+ value = super_proc(value)
111
+ return value # type: ignore[no-any-return]
112
+
113
+ return process
114
+
115
+
116
+ class JSONIndexType(_FormatTypeMixin, sqltypes.JSON.JSONIndexType):
117
+ def _format_value(self, value: Any) -> str:
118
+ if isinstance(value, int):
119
+ formatted_value = "$[%s]" % value
120
+ else:
121
+ formatted_value = '$."%s"' % value
122
+ return formatted_value
123
+
124
+
125
+ class JSONPathType(_FormatTypeMixin, sqltypes.JSON.JSONPathType):
126
+ def _format_value(self, value: Any) -> str:
127
+ return "$%s" % (
128
+ "".join(
129
+ [
130
+ "[%s]" % elem if isinstance(elem, int) else '."%s"' % elem
131
+ for elem in value
132
+ ]
133
+ )
134
+ )
@@ -0,0 +1,237 @@
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 profile_platform_tokens
24
+ from ...testing.provision import run_reap_dbs
25
+ from ...testing.provision import stop_test_class_outside_fixtures
26
+ from ...testing.provision import temp_table_keyword_args
27
+ from ...testing.provision import upsert
28
+
29
+ # TODO: I can't get this to build dynamically with pytest-xdist procs
30
+ _drivernames = {
31
+ "pysqlite",
32
+ "aiosqlite",
33
+ "pysqlcipher",
34
+ "pysqlite_numeric",
35
+ "pysqlite_dollar",
36
+ }
37
+
38
+
39
+ def _format_url(url, driver, ident):
40
+ """given a sqlite url + desired driver + ident, make a canonical
41
+ URL out of it
42
+
43
+ """
44
+ url = sa_url.make_url(url)
45
+
46
+ if driver is None:
47
+ driver = url.get_driver_name()
48
+
49
+ filename = url.database
50
+
51
+ needs_enc = driver == "pysqlcipher"
52
+ name_token = None
53
+
54
+ if filename and filename != ":memory:":
55
+ assert "test_schema" not in filename
56
+ tokens = re.split(r"[_\.]", filename)
57
+
58
+ for token in tokens:
59
+ if token in _drivernames:
60
+ if driver is None:
61
+ driver = token
62
+ continue
63
+ elif token in ("db", "enc"):
64
+ continue
65
+ elif name_token is None:
66
+ name_token = token.strip("_")
67
+
68
+ assert name_token, f"sqlite filename has no name token: {url.database}"
69
+
70
+ new_filename = f"{name_token}_{driver}"
71
+ if ident:
72
+ new_filename += f"_{ident}"
73
+ new_filename += ".db"
74
+ if needs_enc:
75
+ new_filename += ".enc"
76
+ url = url.set(database=new_filename)
77
+
78
+ if needs_enc:
79
+ url = url.set(password="test")
80
+
81
+ url = url.set(drivername="sqlite+%s" % (driver,))
82
+
83
+ return url
84
+
85
+
86
+ @generate_driver_url.for_db("sqlite")
87
+ def generate_driver_url(url, driver, query_str):
88
+ url = _format_url(url, driver, None)
89
+
90
+ try:
91
+ url.get_dialect()
92
+ except exc.NoSuchModuleError:
93
+ return None
94
+ else:
95
+ return url
96
+
97
+
98
+ @follower_url_from_main.for_db("sqlite")
99
+ def _sqlite_follower_url_from_main(url, ident):
100
+ return _format_url(url, None, ident)
101
+
102
+
103
+ @post_configure_engine.for_db("sqlite")
104
+ def _sqlite_post_configure_engine(url, engine, follower_ident):
105
+ from sqlalchemy import event
106
+
107
+ if follower_ident:
108
+ attach_path = f"{follower_ident}_{engine.driver}_test_schema.db"
109
+ else:
110
+ attach_path = f"{engine.driver}_test_schema.db"
111
+
112
+ @event.listens_for(engine, "connect")
113
+ def connect(dbapi_connection, connection_record):
114
+ # use file DBs in all cases, memory acts kind of strangely
115
+ # as an attached
116
+
117
+ # NOTE! this has to be done *per connection*. New sqlite connection,
118
+ # as we get with say, QueuePool, the attaches are gone.
119
+ # so schemes to delete those attached files have to be done at the
120
+ # filesystem level and not rely upon what attachments are in a
121
+ # particular SQLite connection
122
+ dbapi_connection.execute(
123
+ f'ATTACH DATABASE "{attach_path}" AS test_schema'
124
+ )
125
+
126
+ @event.listens_for(engine, "engine_disposed")
127
+ def dispose(engine):
128
+ """most databases should be dropped using
129
+ stop_test_class_outside_fixtures
130
+
131
+ however a few tests like AttachedDBTest might not get triggered on
132
+ that main hook
133
+
134
+ """
135
+
136
+ if os.path.exists(attach_path):
137
+ os.remove(attach_path)
138
+
139
+ filename = engine.url.database
140
+
141
+ if filename and filename != ":memory:" and os.path.exists(filename):
142
+ os.remove(filename)
143
+
144
+
145
+ @post_configure_testing_engine.for_db("sqlite")
146
+ def _sqlite_post_configure_testing_engine(url, engine, options, scope):
147
+
148
+ sqlite_savepoint = options.get("sqlite_savepoint", False)
149
+ sqlite_share_pool = options.get("sqlite_share_pool", False)
150
+
151
+ if sqlite_savepoint and engine.name == "sqlite":
152
+ # apply SQLite savepoint workaround
153
+ @event.listens_for(engine, "connect")
154
+ def do_connect(dbapi_connection, connection_record):
155
+ dbapi_connection.isolation_level = None
156
+
157
+ @event.listens_for(engine, "begin")
158
+ def do_begin(conn):
159
+ conn.exec_driver_sql("BEGIN")
160
+
161
+ if sqlite_share_pool:
162
+ # SingletonThreadPool, StaticPool both support "transfer"
163
+ # so a new pool can share the same SQLite connection
164
+ # (single thread only)
165
+ if hasattr(engine.pool, "_transfer_from"):
166
+ options["use_reaper"] = False
167
+ engine.pool._transfer_from(config.db.pool)
168
+
169
+
170
+ @create_db.for_db("sqlite")
171
+ def _sqlite_create_db(cfg, eng, ident):
172
+ pass
173
+
174
+
175
+ @drop_db.for_db("sqlite")
176
+ def _sqlite_drop_db(cfg, eng, ident):
177
+ _drop_dbs_w_ident(eng.url.database, eng.driver, ident)
178
+
179
+
180
+ def _drop_dbs_w_ident(databasename, driver, ident):
181
+ for path in os.listdir("."):
182
+ fname, ext = os.path.split(path)
183
+ if ident in fname and ext in [".db", ".db.enc"]:
184
+ log.info("deleting SQLite database file: %s", path)
185
+ os.remove(path)
186
+
187
+
188
+ @stop_test_class_outside_fixtures.for_db("sqlite")
189
+ def stop_test_class_outside_fixtures(config, db, cls):
190
+ db.dispose()
191
+
192
+
193
+ @temp_table_keyword_args.for_db("sqlite")
194
+ def _sqlite_temp_table_keyword_args(cfg, eng):
195
+ return {"prefixes": ["TEMPORARY"]}
196
+
197
+
198
+ @run_reap_dbs.for_db("sqlite")
199
+ def _reap_sqlite_dbs(url, idents):
200
+ log.info("db reaper connecting to %r", url)
201
+ log.info("identifiers in file: %s", ", ".join(idents))
202
+ url = sa_url.make_url(url)
203
+ for ident in idents:
204
+ for drivername in _drivernames:
205
+ _drop_dbs_w_ident(url.database, drivername, ident)
206
+
207
+
208
+ @upsert.for_db("sqlite")
209
+ def _upsert(
210
+ cfg,
211
+ table,
212
+ returning,
213
+ *,
214
+ set_lambda=None,
215
+ sort_by_parameter_order=False,
216
+ index_elements=None,
217
+ ):
218
+ from sqlalchemy.dialects.sqlite import insert
219
+
220
+ stmt = insert(table)
221
+
222
+ if set_lambda:
223
+ stmt = stmt.on_conflict_do_update(set_=set_lambda(stmt.excluded))
224
+ else:
225
+ stmt = stmt.on_conflict_do_nothing()
226
+
227
+ stmt = stmt.returning(
228
+ *returning, sort_by_parameter_order=sort_by_parameter_order
229
+ )
230
+ return stmt
231
+
232
+
233
+ @profile_platform_tokens.for_db("sqlite")
234
+ def _sqlite_profile_platform_tokens(eng):
235
+ # a file based database goes through the filesystem for every page it
236
+ # reads, which a ``:memory:`` database doesn't
237
+ return ("file",) if eng.dialect._is_url_file_db(eng.url) else ()