SQLAlchemy 2.1.0rc1__cp315-cp315-win_amd64.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-win_amd64.pyd +0 -0
  77. sqlalchemy/engine/_processors_cy.py +92 -0
  78. sqlalchemy/engine/_result_cy.cp315-win_amd64.pyd +0 -0
  79. sqlalchemy/engine/_result_cy.py +711 -0
  80. sqlalchemy/engine/_row_cy.cp315-win_amd64.pyd +0 -0
  81. sqlalchemy/engine/_row_cy.py +232 -0
  82. sqlalchemy/engine/_util_cy.cp315-win_amd64.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-win_amd64.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-win_amd64.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-win_amd64.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-win_amd64.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,3103 @@
1
+ # dialects/sqlite/base.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
+
10
+ r'''
11
+ .. dialect:: sqlite
12
+ :name: SQLite
13
+ :normal_support: 3.12+
14
+ :best_effort: 3.7.16+
15
+
16
+ .. _sqlite_datetime:
17
+
18
+ Date and Time Types
19
+ -------------------
20
+
21
+ SQLite does not have built-in DATE, TIME, or DATETIME types, and pysqlite does
22
+ not provide out of the box functionality for translating values between Python
23
+ `datetime` objects and a SQLite-supported format. SQLAlchemy's own
24
+ :class:`~sqlalchemy.types.DateTime` and related types provide date formatting
25
+ and parsing functionality when SQLite is used. The implementation classes are
26
+ :class:`_sqlite.DATETIME`, :class:`_sqlite.DATE` and :class:`_sqlite.TIME`.
27
+ These types represent dates and times as ISO formatted strings, which also
28
+ nicely support ordering. There's no reliance on typical "libc" internals for
29
+ these functions so historical dates are fully supported.
30
+
31
+ Ensuring Text affinity
32
+ ^^^^^^^^^^^^^^^^^^^^^^
33
+
34
+ The DDL rendered for these types is the standard ``DATE``, ``TIME``
35
+ and ``DATETIME`` indicators. However, custom storage formats can also be
36
+ applied to these types. When the
37
+ storage format is detected as containing no alpha characters, the DDL for
38
+ these types is rendered as ``DATE_CHAR``, ``TIME_CHAR``, and ``DATETIME_CHAR``,
39
+ so that the column continues to have textual affinity.
40
+
41
+ .. seealso::
42
+
43
+ `Type Affinity <https://www.sqlite.org/datatype3.html#affinity>`_ -
44
+ in the SQLite documentation
45
+
46
+ .. _sqlite_autoincrement:
47
+
48
+ SQLite Auto Incrementing Behavior
49
+ ----------------------------------
50
+
51
+ Background on SQLite's autoincrement is at: https://sqlite.org/autoinc.html
52
+
53
+ Key concepts:
54
+
55
+ * SQLite has an implicit "auto increment" feature that takes place for any
56
+ non-composite primary-key column that is specifically created using
57
+ "INTEGER PRIMARY KEY" for the type + primary key.
58
+
59
+ * SQLite also has an explicit "AUTOINCREMENT" keyword, that is **not**
60
+ equivalent to the implicit autoincrement feature; this keyword is not
61
+ recommended for general use. SQLAlchemy does not render this keyword
62
+ unless a special SQLite-specific directive is used (see below). However,
63
+ it still requires that the column's type is named "INTEGER".
64
+
65
+ Using the AUTOINCREMENT Keyword
66
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
67
+
68
+ To specifically render the AUTOINCREMENT keyword on the primary key column
69
+ when rendering DDL, add the flag ``sqlite_autoincrement=True`` to the Table
70
+ construct::
71
+
72
+ Table(
73
+ "sometable",
74
+ metadata,
75
+ Column("id", Integer, primary_key=True),
76
+ sqlite_autoincrement=True,
77
+ )
78
+
79
+ Allowing autoincrement behavior SQLAlchemy types other than Integer/INTEGER
80
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
81
+
82
+ SQLite's typing model is based on naming conventions. Among other things, this
83
+ means that any type name which contains the substring ``"INT"`` will be
84
+ determined to be of "integer affinity". A type named ``"BIGINT"``,
85
+ ``"SPECIAL_INT"`` or even ``"XYZINTQPR"``, will be considered by SQLite to be
86
+ of "integer" affinity. However, **the SQLite autoincrement feature, whether
87
+ implicitly or explicitly enabled, requires that the name of the column's type
88
+ is exactly the string "INTEGER"**. Therefore, if an application uses a type
89
+ like :class:`.BigInteger` for a primary key, on SQLite this type will need to
90
+ be rendered as the name ``"INTEGER"`` when emitting the initial ``CREATE
91
+ TABLE`` statement in order for the autoincrement behavior to be available.
92
+
93
+ One approach to achieve this is to use :class:`.Integer` on SQLite
94
+ only using :meth:`.TypeEngine.with_variant`::
95
+
96
+ table = Table(
97
+ "my_table",
98
+ metadata,
99
+ Column(
100
+ "id",
101
+ BigInteger().with_variant(Integer, "sqlite"),
102
+ primary_key=True,
103
+ ),
104
+ )
105
+
106
+ Another is to use a subclass of :class:`.BigInteger` that overrides its DDL
107
+ name to be ``INTEGER`` when compiled against SQLite::
108
+
109
+ from sqlalchemy import BigInteger
110
+ from sqlalchemy.ext.compiler import compiles
111
+
112
+
113
+ class SLBigInteger(BigInteger):
114
+ pass
115
+
116
+
117
+ @compiles(SLBigInteger, "sqlite")
118
+ def bi_c(element, compiler, **kw):
119
+ return "INTEGER"
120
+
121
+
122
+ @compiles(SLBigInteger)
123
+ def bi_c(element, compiler, **kw):
124
+ return compiler.visit_BIGINT(element, **kw)
125
+
126
+
127
+ table = Table(
128
+ "my_table", metadata, Column("id", SLBigInteger(), primary_key=True)
129
+ )
130
+
131
+ .. seealso::
132
+
133
+ :meth:`.TypeEngine.with_variant`
134
+
135
+ :ref:`sqlalchemy.ext.compiler_toplevel`
136
+
137
+ `Datatypes In SQLite Version 3 <https://sqlite.org/datatype3.html>`_
138
+
139
+ .. _sqlite_transactions:
140
+
141
+ Transactions with SQLite and the sqlite3 driver
142
+ -----------------------------------------------
143
+
144
+ As a file-based database, SQLite's approach to transactions differs from
145
+ traditional databases in many ways. Additionally, the ``sqlite3`` driver
146
+ standard with Python (as well as the async version ``aiosqlite`` which builds
147
+ on top of it) has several quirks, workarounds, and API features in the
148
+ area of transaction control, all of which generally need to be addressed when
149
+ constructing a SQLAlchemy application that uses SQLite.
150
+
151
+ Legacy Transaction Mode with the sqlite3 driver
152
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
153
+
154
+ The most important aspect of transaction handling with the sqlite3 driver is
155
+ that it defaults (which will continue through Python 3.15 before being
156
+ removed in Python 3.16) to legacy transactional behavior which does
157
+ not strictly follow :pep:`249`. The way in which the driver diverges from the
158
+ PEP is that it does not "begin" a transaction automatically as dictated by
159
+ :pep:`249` except in the case of DML statements, e.g. INSERT, UPDATE, and
160
+ DELETE. Normally, :pep:`249` dictates that a BEGIN must be emitted upon
161
+ the first SQL statement of any kind, so that all subsequent operations will
162
+ be established within a transaction until ``connection.commit()`` has been
163
+ called. The ``sqlite3`` driver, in an effort to be easier to use in
164
+ highly concurrent environments, skips this step for DQL (e.g. SELECT) statements,
165
+ and also skips it for DDL (e.g. CREATE TABLE etc.) statements for more legacy
166
+ reasons. Statements such as SAVEPOINT are also skipped.
167
+
168
+ In modern versions of the ``sqlite3`` driver as of Python 3.12, this legacy
169
+ mode of operation is referred to as
170
+ `"legacy transaction control" <https://docs.python.org/3/library/sqlite3.html#sqlite3-transaction-control-isolation-level>`_, and is in
171
+ effect by default due to the ``Connection.autocommit`` parameter being set to
172
+ the constant ``sqlite3.LEGACY_TRANSACTION_CONTROL``. Prior to Python 3.12,
173
+ the ``Connection.autocommit`` attribute did not exist.
174
+
175
+ The implications of legacy transaction mode include:
176
+
177
+ * **Incorrect support for transactional DDL** - statements like CREATE TABLE, ALTER TABLE,
178
+ CREATE INDEX etc. will not automatically BEGIN a transaction if one were not
179
+ started already, leading to the changes by each statement being
180
+ "autocommitted" immediately unless BEGIN were otherwise emitted first. Very
181
+ old (pre Python 3.6) versions of SQLite would also force a COMMIT for these
182
+ operations even if a transaction were present, however this is no longer the
183
+ case.
184
+ * **SERIALIZABLE behavior not fully functional** - SQLite's transaction isolation
185
+ behavior is normally consistent with SERIALIZABLE isolation, as it is a file-
186
+ based system that locks the database file entirely for write operations,
187
+ preventing COMMIT until all reader transactions (and associated file locks)
188
+ have completed. However, sqlite3's legacy transaction mode fails to emit BEGIN for SELECT
189
+ statements, which causes these SELECT statements to no longer be "repeatable",
190
+ failing one of the consistency guarantees of SERIALIZABLE.
191
+ * **Incorrect behavior for SAVEPOINT** - as the SAVEPOINT statement does not
192
+ imply a BEGIN, a new SAVEPOINT emitted before a BEGIN will function on its
193
+ own but fails to participate in the enclosing transaction, meaning a ROLLBACK
194
+ of the transaction will not rollback elements that were part of a released
195
+ savepoint.
196
+
197
+ Legacy transaction mode first existed in order to facilitate working around
198
+ SQLite's file locks. Because SQLite relies upon whole-file locks, it is easy to
199
+ get "database is locked" errors, particularly when newer features like "write
200
+ ahead logging" are disabled. This is a key reason why ``sqlite3``'s legacy
201
+ transaction mode is still the default mode of operation; disabling it will
202
+ produce behavior that is more susceptible to locked database errors. However
203
+ note that **legacy transaction mode will no longer be the default** in a future
204
+ Python version (3.16 as of this writing).
205
+
206
+ .. _sqlite_enabling_transactions:
207
+
208
+ Enabling Non-Legacy SQLite Transactional Modes with the sqlite3 or aiosqlite driver
209
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
210
+
211
+ Current SQLAlchemy support allows either for setting the
212
+ ``.Connection.autocommit`` attribute, most directly by using a
213
+ :func:`._sa.create_engine` parameter, or if on an older version of Python where
214
+ the attribute is not available, using event hooks to control the behavior of
215
+ BEGIN.
216
+
217
+ * **Enabling modern sqlite3 transaction control via the autocommit connect parameter** (Python 3.12 and above)
218
+
219
+ To use SQLite in the mode described at `Transaction control via the autocommit attribute <https://docs.python.org/3/library/sqlite3.html#transaction-control-via-the-autocommit-attribute>`_,
220
+ the most straightforward approach is to set the attribute to its recommended value
221
+ of ``False`` at the connect level using :paramref:`_sa.create_engine.connect_args``::
222
+
223
+ from sqlalchemy import create_engine
224
+
225
+ engine = create_engine(
226
+ "sqlite:///myfile.db", connect_args={"autocommit": False}
227
+ )
228
+
229
+ This parameter is also passed through when using the aiosqlite driver::
230
+
231
+ from sqlalchemy.ext.asyncio import create_async_engine
232
+
233
+ engine = create_async_engine(
234
+ "sqlite+aiosqlite:///myfile.db", connect_args={"autocommit": False}
235
+ )
236
+
237
+ The parameter can also be set at the attribute level using the :meth:`.PoolEvents.connect`
238
+ event hook, however this will only work for sqlite3, as aiosqlite does not yet expose this
239
+ attribute on its ``Connection`` object::
240
+
241
+ from sqlalchemy import create_engine, event
242
+
243
+ engine = create_engine("sqlite:///myfile.db")
244
+
245
+
246
+ @event.listens_for(engine, "connect")
247
+ def do_connect(dbapi_connection, connection_record):
248
+ # enable autocommit=False mode
249
+ dbapi_connection.autocommit = False
250
+
251
+ * **Using SQLAlchemy to emit BEGIN in lieu of SQLite's transaction control** (all Python versions, sqlite3 and aiosqlite)
252
+
253
+ For older versions of ``sqlite3`` or for cross-compatibility with older and
254
+ newer versions, SQLAlchemy can also take over the job of transaction control.
255
+ This is achieved by using the :meth:`.ConnectionEvents.begin` hook
256
+ to emit the "BEGIN" command directly, while also disabling SQLite's control
257
+ of this command using the :meth:`.PoolEvents.connect` event hook to set the
258
+ ``Connection.isolation_level`` attribute to ``None``::
259
+
260
+
261
+ from sqlalchemy import create_engine, event
262
+
263
+ engine = create_engine("sqlite:///myfile.db")
264
+
265
+
266
+ @event.listens_for(engine, "connect")
267
+ def do_connect(dbapi_connection, connection_record):
268
+ # disable sqlite3's emitting of the BEGIN statement entirely.
269
+ dbapi_connection.isolation_level = None
270
+
271
+
272
+ @event.listens_for(engine, "begin")
273
+ def do_begin(conn):
274
+ # emit our own BEGIN. sqlite3 still emits COMMIT/ROLLBACK correctly
275
+ conn.exec_driver_sql("BEGIN")
276
+
277
+ When using the asyncio variant ``aiosqlite``, refer to ``engine.sync_engine``
278
+ as in the example below::
279
+
280
+ from sqlalchemy import create_engine, event
281
+ from sqlalchemy.ext.asyncio import create_async_engine
282
+
283
+ engine = create_async_engine("sqlite+aiosqlite:///myfile.db")
284
+
285
+
286
+ @event.listens_for(engine.sync_engine, "connect")
287
+ def do_connect(dbapi_connection, connection_record):
288
+ # disable aiosqlite's emitting of the BEGIN statement entirely.
289
+ dbapi_connection.isolation_level = None
290
+
291
+
292
+ @event.listens_for(engine.sync_engine, "begin")
293
+ def do_begin(conn):
294
+ # emit our own BEGIN. aiosqlite still emits COMMIT/ROLLBACK correctly
295
+ conn.exec_driver_sql("BEGIN")
296
+
297
+ .. _sqlite_isolation_level:
298
+
299
+ Using SQLAlchemy's Driver Level AUTOCOMMIT Feature with SQLite
300
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
301
+
302
+ SQLAlchemy has a comprehensive database isolation feature with optional
303
+ autocommit support that is introduced in the section :ref:`dbapi_autocommit`.
304
+
305
+ For the ``sqlite3`` and ``aiosqlite`` drivers, SQLAlchemy only includes
306
+ built-in support for "AUTOCOMMIT". Note that this mode is currently incompatible
307
+ with the non-legacy isolation mode hooks documented in the previous
308
+ section at :ref:`sqlite_enabling_transactions`.
309
+
310
+ To use the ``sqlite3`` driver with SQLAlchemy driver-level autocommit,
311
+ create an engine setting the :paramref:`_sa.create_engine.isolation_level`
312
+ parameter to "AUTOCOMMIT"::
313
+
314
+ eng = create_engine("sqlite:///myfile.db", isolation_level="AUTOCOMMIT")
315
+
316
+ When using the above mode, any event hooks that set the sqlite3 ``Connection.autocommit``
317
+ parameter away from its default of ``sqlite3.LEGACY_TRANSACTION_CONTROL``
318
+ as well as hooks that emit ``BEGIN`` should be disabled.
319
+
320
+ Additional Reading for SQLite / sqlite3 transaction control
321
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
322
+
323
+ Links with important information on SQLite, the sqlite3 driver,
324
+ as well as long historical conversations on how things got to their current state:
325
+
326
+ * `Isolation in SQLite <https://www.sqlite.org/isolation.html>`_ - on the SQLite website
327
+ * `Transaction control <https://docs.python.org/3/library/sqlite3.html#transaction-control>`_ - describes the sqlite3 autocommit attribute as well
328
+ as the legacy isolation_level attribute.
329
+ * `sqlite3 SELECT does not BEGIN a transaction, but should according to spec <https://github.com/python/cpython/issues/54133>`_ - imported Python standard library issue on github
330
+ * `sqlite3 module breaks transactions and potentially corrupts data <https://github.com/python/cpython/issues/54949>`_ - imported Python standard library issue on github
331
+
332
+
333
+ INSERT/UPDATE/DELETE...RETURNING
334
+ ---------------------------------
335
+
336
+ The SQLite dialect supports SQLite 3.35's ``INSERT|UPDATE|DELETE..RETURNING``
337
+ syntax. ``INSERT..RETURNING`` may be used
338
+ automatically in some cases in order to fetch newly generated identifiers in
339
+ place of the traditional approach of using ``cursor.lastrowid``, however
340
+ ``cursor.lastrowid`` is currently still preferred for simple single-statement
341
+ cases for its better performance.
342
+
343
+ To specify an explicit ``RETURNING`` clause, use the
344
+ :meth:`._UpdateBase.returning` method on a per-statement basis::
345
+
346
+ # INSERT..RETURNING
347
+ result = connection.execute(
348
+ table.insert().values(name="foo").returning(table.c.col1, table.c.col2)
349
+ )
350
+ print(result.all())
351
+
352
+ # UPDATE..RETURNING
353
+ result = connection.execute(
354
+ table.update()
355
+ .where(table.c.name == "foo")
356
+ .values(name="bar")
357
+ .returning(table.c.col1, table.c.col2)
358
+ )
359
+ print(result.all())
360
+
361
+ # DELETE..RETURNING
362
+ result = connection.execute(
363
+ table.delete()
364
+ .where(table.c.name == "foo")
365
+ .returning(table.c.col1, table.c.col2)
366
+ )
367
+ print(result.all())
368
+
369
+ .. versionadded:: 2.0 Added support for SQLite RETURNING
370
+
371
+
372
+ .. _sqlite_foreign_keys:
373
+
374
+ Foreign Key Support
375
+ -------------------
376
+
377
+ SQLite supports FOREIGN KEY syntax when emitting CREATE statements for tables,
378
+ however by default these constraints have no effect on the operation of the
379
+ table.
380
+
381
+ Constraint checking on SQLite has three prerequisites:
382
+
383
+ * At least version 3.6.19 of SQLite must be in use
384
+ * The SQLite library must be compiled *without* the SQLITE_OMIT_FOREIGN_KEY
385
+ or SQLITE_OMIT_TRIGGER symbols enabled.
386
+ * The ``PRAGMA foreign_keys = ON`` statement must be emitted on all
387
+ connections before use -- including the initial call to
388
+ :meth:`sqlalchemy.schema.MetaData.create_all`.
389
+
390
+ SQLAlchemy allows for the ``PRAGMA`` statement to be emitted automatically for
391
+ new connections through the usage of events::
392
+
393
+ from sqlalchemy.engine import Engine
394
+ from sqlalchemy import event
395
+
396
+
397
+ @event.listens_for(Engine, "connect")
398
+ def set_sqlite_pragma(dbapi_connection, connection_record):
399
+ # the sqlite3 driver will not set PRAGMA foreign_keys
400
+ # if autocommit=False; set to True temporarily
401
+ ac = dbapi_connection.autocommit
402
+ dbapi_connection.autocommit = True
403
+
404
+ cursor = dbapi_connection.cursor()
405
+ cursor.execute("PRAGMA foreign_keys=ON")
406
+ cursor.close()
407
+
408
+ # restore previous autocommit setting
409
+ dbapi_connection.autocommit = ac
410
+
411
+ .. warning::
412
+
413
+ When SQLite foreign keys are enabled, it is **not possible**
414
+ to emit CREATE or DROP statements for tables that contain
415
+ mutually-dependent foreign key constraints;
416
+ to emit the DDL for these tables requires that ALTER TABLE be used to
417
+ create or drop these constraints separately, for which SQLite has
418
+ no support.
419
+
420
+ .. seealso::
421
+
422
+ `SQLite Foreign Key Support <https://www.sqlite.org/foreignkeys.html>`_
423
+ - on the SQLite web site.
424
+
425
+ :ref:`event_toplevel` - SQLAlchemy event API.
426
+
427
+ :ref:`use_alter` - more information on SQLAlchemy's facilities for handling
428
+ mutually-dependent foreign key constraints.
429
+
430
+ .. _sqlite_on_conflict_ddl:
431
+
432
+ ON CONFLICT support for constraints
433
+ -----------------------------------
434
+
435
+ .. seealso:: This section describes the :term:`DDL` version of "ON CONFLICT" for
436
+ SQLite, which occurs within a CREATE TABLE statement. For "ON CONFLICT" as
437
+ applied to an INSERT statement, see :ref:`sqlite_on_conflict_insert`.
438
+
439
+ SQLite supports a non-standard DDL clause known as ON CONFLICT which can be applied
440
+ to primary key, unique, check, and not null constraints. In DDL, it is
441
+ rendered either within the "CONSTRAINT" clause or within the column definition
442
+ itself depending on the location of the target constraint. To render this
443
+ clause within DDL, the extension parameter ``sqlite_on_conflict`` can be
444
+ specified with a string conflict resolution algorithm within the
445
+ :class:`.PrimaryKeyConstraint`, :class:`.UniqueConstraint`,
446
+ :class:`.CheckConstraint` objects. Within the :class:`_schema.Column` object,
447
+ there
448
+ are individual parameters ``sqlite_on_conflict_not_null``,
449
+ ``sqlite_on_conflict_primary_key``, ``sqlite_on_conflict_unique`` which each
450
+ correspond to the three types of relevant constraint types that can be
451
+ indicated from a :class:`_schema.Column` object.
452
+
453
+ .. seealso::
454
+
455
+ `ON CONFLICT <https://www.sqlite.org/lang_conflict.html>`_ - in the SQLite
456
+ documentation
457
+
458
+ The ``sqlite_on_conflict`` parameters accept a string argument which is just
459
+ the resolution name to be chosen, which on SQLite can be one of ROLLBACK,
460
+ ABORT, FAIL, IGNORE, and REPLACE. For example, to add a UNIQUE constraint
461
+ that specifies the IGNORE algorithm::
462
+
463
+ some_table = Table(
464
+ "some_table",
465
+ metadata,
466
+ Column("id", Integer, primary_key=True),
467
+ Column("data", Integer),
468
+ UniqueConstraint("id", "data", sqlite_on_conflict="IGNORE"),
469
+ )
470
+
471
+ The above renders CREATE TABLE DDL as:
472
+
473
+ .. sourcecode:: sql
474
+
475
+ CREATE TABLE some_table (
476
+ id INTEGER NOT NULL,
477
+ data INTEGER,
478
+ PRIMARY KEY (id),
479
+ UNIQUE (id, data) ON CONFLICT IGNORE
480
+ )
481
+
482
+
483
+ When using the :paramref:`_schema.Column.unique`
484
+ flag to add a UNIQUE constraint
485
+ to a single column, the ``sqlite_on_conflict_unique`` parameter can
486
+ be added to the :class:`_schema.Column` as well, which will be added to the
487
+ UNIQUE constraint in the DDL::
488
+
489
+ some_table = Table(
490
+ "some_table",
491
+ metadata,
492
+ Column("id", Integer, primary_key=True),
493
+ Column(
494
+ "data", Integer, unique=True, sqlite_on_conflict_unique="IGNORE"
495
+ ),
496
+ )
497
+
498
+ rendering:
499
+
500
+ .. sourcecode:: sql
501
+
502
+ CREATE TABLE some_table (
503
+ id INTEGER NOT NULL,
504
+ data INTEGER,
505
+ PRIMARY KEY (id),
506
+ UNIQUE (data) ON CONFLICT IGNORE
507
+ )
508
+
509
+ To apply the FAIL algorithm for a NOT NULL constraint,
510
+ ``sqlite_on_conflict_not_null`` is used::
511
+
512
+ some_table = Table(
513
+ "some_table",
514
+ metadata,
515
+ Column("id", Integer, primary_key=True),
516
+ Column(
517
+ "data", Integer, nullable=False, sqlite_on_conflict_not_null="FAIL"
518
+ ),
519
+ )
520
+
521
+ this renders the column inline ON CONFLICT phrase:
522
+
523
+ .. sourcecode:: sql
524
+
525
+ CREATE TABLE some_table (
526
+ id INTEGER NOT NULL,
527
+ data INTEGER NOT NULL ON CONFLICT FAIL,
528
+ PRIMARY KEY (id)
529
+ )
530
+
531
+
532
+ Similarly, for an inline primary key, use ``sqlite_on_conflict_primary_key``::
533
+
534
+ some_table = Table(
535
+ "some_table",
536
+ metadata,
537
+ Column(
538
+ "id",
539
+ Integer,
540
+ primary_key=True,
541
+ sqlite_on_conflict_primary_key="FAIL",
542
+ ),
543
+ )
544
+
545
+ SQLAlchemy renders the PRIMARY KEY constraint separately, so the conflict
546
+ resolution algorithm is applied to the constraint itself:
547
+
548
+ .. sourcecode:: sql
549
+
550
+ CREATE TABLE some_table (
551
+ id INTEGER NOT NULL,
552
+ PRIMARY KEY (id) ON CONFLICT FAIL
553
+ )
554
+
555
+ .. _sqlite_on_conflict_insert:
556
+
557
+ INSERT...ON CONFLICT (Upsert)
558
+ -----------------------------
559
+
560
+ .. seealso:: This section describes the :term:`DML` version of "ON CONFLICT" for
561
+ SQLite, which occurs within an INSERT statement. For "ON CONFLICT" as
562
+ applied to a CREATE TABLE statement, see :ref:`sqlite_on_conflict_ddl`.
563
+
564
+ From version 3.24.0 onwards, SQLite supports "upserts" (update or insert)
565
+ of rows into a table via the ``ON CONFLICT`` clause of the ``INSERT``
566
+ statement. A candidate row will only be inserted if that row does not violate
567
+ any unique or primary key constraints. In the case of a unique constraint violation, a
568
+ secondary action can occur which can be either "DO UPDATE", indicating that
569
+ the data in the target row should be updated, or "DO NOTHING", which indicates
570
+ to silently skip this row.
571
+
572
+ Conflicts are determined using columns that are part of existing unique
573
+ constraints and indexes. These constraints are identified by stating the
574
+ columns and conditions that comprise the indexes.
575
+
576
+ SQLAlchemy provides ``ON CONFLICT`` support via the SQLite-specific
577
+ :func:`_sqlite.insert()` function, which provides
578
+ the generative methods :meth:`_sqlite.Insert.on_conflict_do_update`
579
+ and :meth:`_sqlite.Insert.on_conflict_do_nothing`:
580
+
581
+ .. sourcecode:: pycon+sql
582
+
583
+ >>> from sqlalchemy.dialects.sqlite import insert
584
+
585
+ >>> insert_stmt = insert(my_table).values(
586
+ ... id="some_existing_id", data="inserted value"
587
+ ... )
588
+
589
+ >>> do_update_stmt = insert_stmt.on_conflict_do_update(
590
+ ... index_elements=["id"], set_=dict(data="updated value")
591
+ ... )
592
+
593
+ >>> print(do_update_stmt)
594
+ {printsql}INSERT INTO my_table (id, data) VALUES (?, ?)
595
+ ON CONFLICT (id) DO UPDATE SET data = ?{stop}
596
+
597
+ >>> do_nothing_stmt = insert_stmt.on_conflict_do_nothing(index_elements=["id"])
598
+
599
+ >>> print(do_nothing_stmt)
600
+ {printsql}INSERT INTO my_table (id, data) VALUES (?, ?)
601
+ ON CONFLICT (id) DO NOTHING
602
+
603
+ .. versionadded:: 1.4
604
+
605
+ .. seealso::
606
+
607
+ `Upsert
608
+ <https://sqlite.org/lang_UPSERT.html>`_
609
+ - in the SQLite documentation.
610
+
611
+
612
+ Specifying the Target
613
+ ^^^^^^^^^^^^^^^^^^^^^
614
+
615
+ Both methods supply the "target" of the conflict using column inference:
616
+
617
+ * The :paramref:`_sqlite.Insert.on_conflict_do_update.index_elements` argument
618
+ specifies a sequence containing string column names, :class:`_schema.Column`
619
+ objects, and/or SQL expression elements, which would identify a unique index
620
+ or unique constraint.
621
+
622
+ * When using :paramref:`_sqlite.Insert.on_conflict_do_update.index_elements`
623
+ to infer an index, a partial index can be inferred by also specifying the
624
+ :paramref:`_sqlite.Insert.on_conflict_do_update.index_where` parameter:
625
+
626
+ .. sourcecode:: pycon+sql
627
+
628
+ >>> stmt = insert(my_table).values(user_email="a@b.com", data="inserted data")
629
+
630
+ >>> do_update_stmt = stmt.on_conflict_do_update(
631
+ ... index_elements=[my_table.c.user_email],
632
+ ... index_where=my_table.c.user_email.like("%@gmail.com"),
633
+ ... set_=dict(data=stmt.excluded.data),
634
+ ... )
635
+
636
+ >>> print(do_update_stmt)
637
+ {printsql}INSERT INTO my_table (data, user_email) VALUES (?, ?)
638
+ ON CONFLICT (user_email)
639
+ WHERE user_email LIKE '%@gmail.com'
640
+ DO UPDATE SET data = excluded.data
641
+
642
+ The SET Clause
643
+ ^^^^^^^^^^^^^^^
644
+
645
+ ``ON CONFLICT...DO UPDATE`` is used to perform an update of the already
646
+ existing row, using any combination of new values as well as values
647
+ from the proposed insertion. These values are specified using the
648
+ :paramref:`_sqlite.Insert.on_conflict_do_update.set_` parameter. This
649
+ parameter accepts a dictionary which consists of direct values
650
+ for UPDATE:
651
+
652
+ .. sourcecode:: pycon+sql
653
+
654
+ >>> stmt = insert(my_table).values(id="some_id", data="inserted value")
655
+
656
+ >>> do_update_stmt = stmt.on_conflict_do_update(
657
+ ... index_elements=["id"], set_=dict(data="updated value")
658
+ ... )
659
+
660
+ >>> print(do_update_stmt)
661
+ {printsql}INSERT INTO my_table (id, data) VALUES (?, ?)
662
+ ON CONFLICT (id) DO UPDATE SET data = ?
663
+
664
+ .. warning::
665
+
666
+ The :meth:`_sqlite.Insert.on_conflict_do_update` method does **not** take
667
+ into account Python-side default UPDATE values or generation functions,
668
+ e.g. those specified using :paramref:`_schema.Column.onupdate`. These
669
+ values will not be exercised for an ON CONFLICT style of UPDATE, unless
670
+ they are manually specified in the
671
+ :paramref:`_sqlite.Insert.on_conflict_do_update.set_` dictionary.
672
+
673
+ Updating using the Excluded INSERT Values
674
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
675
+
676
+ In order to refer to the proposed insertion row, the special alias
677
+ :attr:`~.sqlite.Insert.excluded` is available as an attribute on
678
+ the :class:`_sqlite.Insert` object; this object creates an "excluded." prefix
679
+ on a column, that informs the DO UPDATE to update the row with the value that
680
+ would have been inserted had the constraint not failed:
681
+
682
+ .. sourcecode:: pycon+sql
683
+
684
+ >>> stmt = insert(my_table).values(
685
+ ... id="some_id", data="inserted value", author="jlh"
686
+ ... )
687
+
688
+ >>> do_update_stmt = stmt.on_conflict_do_update(
689
+ ... index_elements=["id"],
690
+ ... set_=dict(data="updated value", author=stmt.excluded.author),
691
+ ... )
692
+
693
+ >>> print(do_update_stmt)
694
+ {printsql}INSERT INTO my_table (id, data, author) VALUES (?, ?, ?)
695
+ ON CONFLICT (id) DO UPDATE SET data = ?, author = excluded.author
696
+
697
+ Additional WHERE Criteria
698
+ ^^^^^^^^^^^^^^^^^^^^^^^^^
699
+
700
+ The :meth:`_sqlite.Insert.on_conflict_do_update` method also accepts
701
+ a WHERE clause using the :paramref:`_sqlite.Insert.on_conflict_do_update.where`
702
+ parameter, which will limit those rows which receive an UPDATE:
703
+
704
+ .. sourcecode:: pycon+sql
705
+
706
+ >>> stmt = insert(my_table).values(
707
+ ... id="some_id", data="inserted value", author="jlh"
708
+ ... )
709
+
710
+ >>> on_update_stmt = stmt.on_conflict_do_update(
711
+ ... index_elements=["id"],
712
+ ... set_=dict(data="updated value", author=stmt.excluded.author),
713
+ ... where=(my_table.c.status == 2),
714
+ ... )
715
+ >>> print(on_update_stmt)
716
+ {printsql}INSERT INTO my_table (id, data, author) VALUES (?, ?, ?)
717
+ ON CONFLICT (id) DO UPDATE SET data = ?, author = excluded.author
718
+ WHERE my_table.status = ?
719
+
720
+
721
+ Skipping Rows with DO NOTHING
722
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
723
+
724
+ ``ON CONFLICT`` may be used to skip inserting a row entirely
725
+ if any conflict with a unique constraint occurs; below this is illustrated
726
+ using the :meth:`_sqlite.Insert.on_conflict_do_nothing` method:
727
+
728
+ .. sourcecode:: pycon+sql
729
+
730
+ >>> stmt = insert(my_table).values(id="some_id", data="inserted value")
731
+ >>> stmt = stmt.on_conflict_do_nothing(index_elements=["id"])
732
+ >>> print(stmt)
733
+ {printsql}INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT (id) DO NOTHING
734
+
735
+
736
+ If ``DO NOTHING`` is used without specifying any columns or constraint,
737
+ it has the effect of skipping the INSERT for any unique violation which
738
+ occurs:
739
+
740
+ .. sourcecode:: pycon+sql
741
+
742
+ >>> stmt = insert(my_table).values(id="some_id", data="inserted value")
743
+ >>> stmt = stmt.on_conflict_do_nothing()
744
+ >>> print(stmt)
745
+ {printsql}INSERT INTO my_table (id, data) VALUES (?, ?) ON CONFLICT DO NOTHING
746
+
747
+ .. _sqlite_on_conflict_multiple:
748
+
749
+ Specifying Multiple ON CONFLICT Clauses
750
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
751
+
752
+ SQLite accepts more than one ``ON CONFLICT`` clause within a single INSERT
753
+ statement. The :meth:`_sqlite.Insert.on_conflict_do_update` and
754
+ :meth:`_sqlite.Insert.on_conflict_do_nothing` methods may therefore be
755
+ invoked repeatedly against the same construct, and may be combined with each
756
+ other; each clause renders in the order in which it was established:
757
+
758
+ .. sourcecode:: pycon+sql
759
+
760
+ >>> stmt = insert(my_table).values(id="some_id", data="inserted value")
761
+ >>> stmt = stmt.on_conflict_do_update(
762
+ ... index_elements=["id"], set_=dict(data="updated value")
763
+ ... ).on_conflict_do_nothing(index_elements=["data"])
764
+ >>> print(stmt)
765
+ {printsql}INSERT INTO my_table (id, data) VALUES (?, ?)
766
+ ON CONFLICT (id) DO UPDATE SET data = ?
767
+ ON CONFLICT (data) DO NOTHING
768
+
769
+ SQLite tests the clauses in the order given, and applies at most one of them
770
+ to any particular row, that being the first clause whose conflict target
771
+ matches the constraint that was violated.
772
+
773
+ Only the last ``ON CONFLICT`` clause of a statement may omit its conflict
774
+ target, in which case it fires for any unique violation not already captured
775
+ by a preceding clause. A :meth:`_sqlite.Insert.on_conflict_do_nothing` call
776
+ that omits
777
+ :paramref:`_sqlite.Insert.on_conflict_do_nothing.index_elements` must
778
+ therefore be the last clause established, else
779
+ :class:`.InvalidRequestError` is raised:
780
+
781
+ .. sourcecode:: pycon+sql
782
+
783
+ >>> stmt = insert(my_table).values(id="some_id", data="inserted value")
784
+ >>> stmt = stmt.on_conflict_do_update(
785
+ ... index_elements=["id"], set_=dict(data="updated value")
786
+ ... ).on_conflict_do_nothing()
787
+ >>> print(stmt)
788
+ {printsql}INSERT INTO my_table (id, data) VALUES (?, ?)
789
+ ON CONFLICT (id) DO UPDATE SET data = ?
790
+ ON CONFLICT DO NOTHING
791
+
792
+ .. versionadded:: 2.1 Multiple ``ON CONFLICT`` clauses may be established
793
+ on a single :class:`_sqlite.Insert` construct.
794
+
795
+ .. _sqlite_type_reflection:
796
+
797
+ Type Reflection
798
+ ---------------
799
+
800
+ SQLite types are unlike those of most other database backends, in that
801
+ the string name of the type usually does not correspond to a "type" in a
802
+ one-to-one fashion. Instead, SQLite links per-column typing behavior
803
+ to one of five so-called "type affinities" based on a string matching
804
+ pattern for the type.
805
+
806
+ SQLAlchemy's reflection process, when inspecting types, uses a simple
807
+ lookup table to link the keywords returned to provided SQLAlchemy types.
808
+ This lookup table is present within the SQLite dialect as it is for all
809
+ other dialects. However, the SQLite dialect has a different "fallback"
810
+ routine for when a particular type name is not located in the lookup map;
811
+ it instead implements the SQLite "type affinity" scheme located at
812
+ https://www.sqlite.org/datatype3.html section 2.1.
813
+
814
+ The provided typemap will make direct associations from an exact string
815
+ name match for the following types:
816
+
817
+ :class:`_types.BIGINT`, :class:`_types.BLOB`,
818
+ :class:`_types.BOOLEAN`, :class:`_types.BOOLEAN`,
819
+ :class:`_types.CHAR`, :class:`_types.DATE`,
820
+ :class:`_types.DATETIME`, :class:`_types.FLOAT`,
821
+ :class:`_types.DECIMAL`, :class:`_types.FLOAT`,
822
+ :class:`_types.INTEGER`, :class:`_types.INTEGER`,
823
+ :class:`_types.NUMERIC`, :class:`_types.REAL`,
824
+ :class:`_types.SMALLINT`, :class:`_types.TEXT`,
825
+ :class:`_types.TIME`, :class:`_types.TIMESTAMP`,
826
+ :class:`_types.VARCHAR`, :class:`_types.NVARCHAR`,
827
+ :class:`_types.NCHAR`
828
+
829
+ When a type name does not match one of the above types, the "type affinity"
830
+ lookup is used instead:
831
+
832
+ * :class:`_types.INTEGER` is returned if the type name includes the
833
+ string ``INT``
834
+ * :class:`_types.TEXT` is returned if the type name includes the
835
+ string ``CHAR``, ``CLOB`` or ``TEXT``
836
+ * :class:`_types.NullType` is returned if the type name includes the
837
+ string ``BLOB``
838
+ * :class:`_types.REAL` is returned if the type name includes the string
839
+ ``REAL``, ``FLOA`` or ``DOUB``.
840
+ * Otherwise, the :class:`_types.NUMERIC` type is used.
841
+
842
+ .. _sqlite_partial_index:
843
+
844
+ Partial Indexes
845
+ ---------------
846
+
847
+ A partial index, e.g. one which uses a WHERE clause, can be specified
848
+ with the DDL system using the argument ``sqlite_where``::
849
+
850
+ tbl = Table("testtbl", m, Column("data", Integer))
851
+ idx = Index(
852
+ "test_idx1",
853
+ tbl.c.data,
854
+ sqlite_where=and_(tbl.c.data > 5, tbl.c.data < 10),
855
+ )
856
+
857
+ The index will be rendered at create time as:
858
+
859
+ .. sourcecode:: sql
860
+
861
+ CREATE INDEX test_idx1 ON testtbl (data)
862
+ WHERE data > 5 AND data < 10
863
+
864
+ .. _sqlite_dotted_column_names:
865
+
866
+ Dotted Column Names
867
+ -------------------
868
+
869
+ Using table or column names that explicitly have periods in them is
870
+ **not recommended**. While this is generally a bad idea for relational
871
+ databases in general, as the dot is a syntactically significant character,
872
+ the SQLite driver up until version **3.10.0** of SQLite has a bug which
873
+ requires that SQLAlchemy filter out these dots in result sets.
874
+
875
+ The bug, entirely outside of SQLAlchemy, can be illustrated thusly::
876
+
877
+ import sqlite3
878
+
879
+ assert sqlite3.sqlite_version_info < (
880
+ 3,
881
+ 10,
882
+ 0,
883
+ ), "bug is fixed in this version"
884
+
885
+ conn = sqlite3.connect(":memory:")
886
+ cursor = conn.cursor()
887
+
888
+ cursor.execute("create table x (a integer, b integer)")
889
+ cursor.execute("insert into x (a, b) values (1, 1)")
890
+ cursor.execute("insert into x (a, b) values (2, 2)")
891
+
892
+ cursor.execute("select x.a, x.b from x")
893
+ assert [c[0] for c in cursor.description] == ["a", "b"]
894
+
895
+ cursor.execute("""
896
+ select x.a, x.b from x where a=1
897
+ union
898
+ select x.a, x.b from x where a=2
899
+ """)
900
+ assert [c[0] for c in cursor.description] == ["a", "b"], [
901
+ c[0] for c in cursor.description
902
+ ]
903
+
904
+ The second assertion fails:
905
+
906
+ .. sourcecode:: text
907
+
908
+ Traceback (most recent call last):
909
+ File "test.py", line 19, in <module>
910
+ [c[0] for c in cursor.description]
911
+ AssertionError: ['x.a', 'x.b']
912
+
913
+ Where above, the driver incorrectly reports the names of the columns
914
+ including the name of the table, which is entirely inconsistent vs.
915
+ when the UNION is not present.
916
+
917
+ SQLAlchemy relies upon column names being predictable in how they match
918
+ to the original statement, so the SQLAlchemy dialect has no choice but
919
+ to filter these out::
920
+
921
+
922
+ from sqlalchemy import create_engine
923
+
924
+ eng = create_engine("sqlite://")
925
+ conn = eng.connect()
926
+
927
+ conn.exec_driver_sql("create table x (a integer, b integer)")
928
+ conn.exec_driver_sql("insert into x (a, b) values (1, 1)")
929
+ conn.exec_driver_sql("insert into x (a, b) values (2, 2)")
930
+
931
+ result = conn.exec_driver_sql("select x.a, x.b from x")
932
+ assert result.keys() == ["a", "b"]
933
+
934
+ result = conn.exec_driver_sql("""
935
+ select x.a, x.b from x where a=1
936
+ union
937
+ select x.a, x.b from x where a=2
938
+ """)
939
+ assert result.keys() == ["a", "b"]
940
+
941
+ Note that above, even though SQLAlchemy filters out the dots, *both
942
+ names are still addressable*::
943
+
944
+ >>> row = result.first()
945
+ >>> row["a"]
946
+ 1
947
+ >>> row["x.a"]
948
+ 1
949
+ >>> row["b"]
950
+ 1
951
+ >>> row["x.b"]
952
+ 1
953
+
954
+ Therefore, the workaround applied by SQLAlchemy only impacts
955
+ :meth:`_engine.CursorResult.keys` and :meth:`.Row.keys()` in the public API. In
956
+ the very specific case where an application is forced to use column names that
957
+ contain dots, and the functionality of :meth:`_engine.CursorResult.keys` and
958
+ :meth:`.Row.keys()` is required to return these dotted names unmodified,
959
+ the ``sqlite_raw_colnames`` execution option may be provided, either on a
960
+ per-:class:`_engine.Connection` basis::
961
+
962
+ result = conn.execution_options(sqlite_raw_colnames=True).exec_driver_sql(
963
+ """
964
+ select x.a, x.b from x where a=1
965
+ union
966
+ select x.a, x.b from x where a=2
967
+ """
968
+ )
969
+ assert result.keys() == ["x.a", "x.b"]
970
+
971
+ or on a per-:class:`_engine.Engine` basis::
972
+
973
+ engine = create_engine(
974
+ "sqlite://", execution_options={"sqlite_raw_colnames": True}
975
+ )
976
+
977
+ When using the per-:class:`_engine.Engine` execution option, note that
978
+ **Core and ORM queries that use UNION may not function properly**.
979
+
980
+ SQLite-specific table options
981
+ -----------------------------
982
+
983
+ One option for CREATE TABLE is supported directly by the SQLite
984
+ dialect in conjunction with the :class:`_schema.Table` construct:
985
+
986
+ * ``WITHOUT ROWID``::
987
+
988
+ Table("some_table", metadata, ..., sqlite_with_rowid=False)
989
+
990
+ *
991
+ ``STRICT``::
992
+
993
+ Table("some_table", metadata, ..., sqlite_strict=True)
994
+
995
+ .. versionadded:: 2.0.37
996
+
997
+ .. seealso::
998
+
999
+ `SQLite CREATE TABLE options
1000
+ <https://www.sqlite.org/lang_createtable.html>`_
1001
+
1002
+ .. _sqlite_include_internal:
1003
+
1004
+ Reflecting internal schema tables
1005
+ ----------------------------------
1006
+
1007
+ Reflection methods that return lists of tables will omit so-called
1008
+ "SQLite internal schema object" names, which are considered by SQLite
1009
+ as any object name that is prefixed with ``sqlite_``. An example of
1010
+ such an object is the ``sqlite_sequence`` table that's generated when
1011
+ the ``AUTOINCREMENT`` column parameter is used. In order to return
1012
+ these objects, the parameter ``sqlite_include_internal=True`` may be
1013
+ passed to methods such as :meth:`_schema.MetaData.reflect` or
1014
+ :meth:`.Inspector.get_table_names`.
1015
+
1016
+ .. versionadded:: 2.0 Added the ``sqlite_include_internal=True`` parameter.
1017
+ Previously, these tables were not ignored by SQLAlchemy reflection
1018
+ methods.
1019
+
1020
+ .. note::
1021
+
1022
+ The ``sqlite_include_internal`` parameter does not refer to the
1023
+ "system" tables that are present in schemas such as ``sqlite_master``.
1024
+
1025
+ .. seealso::
1026
+
1027
+ `SQLite Internal Schema Objects <https://www.sqlite.org/fileformat2.html#intschema>`_ - in the SQLite
1028
+ documentation.
1029
+
1030
+ ''' # noqa
1031
+
1032
+ from __future__ import annotations
1033
+
1034
+ import datetime
1035
+ import numbers
1036
+ import re
1037
+ from typing import Any
1038
+ from typing import Callable
1039
+ from typing import Optional
1040
+ from typing import TYPE_CHECKING
1041
+
1042
+ from .json import JSON
1043
+ from .json import JSONB
1044
+ from .json import JSONIndexType
1045
+ from .json import JSONPathType
1046
+ from ... import exc
1047
+ from ... import schema as sa_schema
1048
+ from ... import sql
1049
+ from ... import text
1050
+ from ... import types as sqltypes
1051
+ from ... import util
1052
+ from ...engine import default
1053
+ from ...engine import processors
1054
+ from ...engine import reflection
1055
+ from ...engine.reflection import ReflectionDefaults
1056
+ from ...sql import coercions
1057
+ from ...sql import compiler
1058
+ from ...sql import ddl as sa_ddl
1059
+ from ...sql import elements
1060
+ from ...sql import roles
1061
+ from ...sql import schema
1062
+ from ...types import BLOB # noqa
1063
+ from ...types import BOOLEAN # noqa
1064
+ from ...types import CHAR # noqa
1065
+ from ...types import DECIMAL # noqa
1066
+ from ...types import FLOAT # noqa
1067
+ from ...types import INTEGER # noqa
1068
+ from ...types import NUMERIC # noqa
1069
+ from ...types import REAL # noqa
1070
+ from ...types import SMALLINT # noqa
1071
+ from ...types import TEXT # noqa
1072
+ from ...types import TIMESTAMP # noqa
1073
+ from ...types import VARCHAR # noqa
1074
+
1075
+ if TYPE_CHECKING:
1076
+ from ...engine.interfaces import DBAPIConnection
1077
+ from ...engine.interfaces import Dialect
1078
+ from ...engine.interfaces import IsolationLevel
1079
+ from ...sql.sqltypes import _JSON_VALUE
1080
+ from ...sql.type_api import _BindProcessorType
1081
+ from ...sql.type_api import _ResultProcessorType
1082
+
1083
+
1084
+ class _SQliteJson(JSON):
1085
+ def result_processor(self, dialect, coltype):
1086
+ default_processor = super().result_processor(dialect, coltype)
1087
+
1088
+ def process(value):
1089
+ try:
1090
+ return default_processor(value)
1091
+ except TypeError:
1092
+ if isinstance(value, numbers.Number):
1093
+ return value
1094
+ else:
1095
+ raise
1096
+
1097
+ return process
1098
+
1099
+
1100
+ class _DateTimeMixin:
1101
+ _reg = None
1102
+ _storage_format = None
1103
+
1104
+ def __init__(self, storage_format=None, regexp=None, **kw):
1105
+ super().__init__(**kw)
1106
+ if regexp is not None:
1107
+ self._reg = re.compile(regexp)
1108
+ if storage_format is not None:
1109
+ self._storage_format = storage_format
1110
+
1111
+ @property
1112
+ def format_is_text_affinity(self):
1113
+ """return True if the storage format will automatically imply
1114
+ a TEXT affinity.
1115
+
1116
+ If the storage format contains no non-numeric characters,
1117
+ it will imply a NUMERIC storage format on SQLite; in this case,
1118
+ the type will generate its DDL as DATE_CHAR, DATETIME_CHAR,
1119
+ TIME_CHAR.
1120
+
1121
+ """
1122
+ spec = self._storage_format % {
1123
+ "year": 0,
1124
+ "month": 0,
1125
+ "day": 0,
1126
+ "hour": 0,
1127
+ "minute": 0,
1128
+ "second": 0,
1129
+ "microsecond": 0,
1130
+ }
1131
+ return bool(re.search(r"[^0-9]", spec))
1132
+
1133
+ def adapt(self, cls, **kw):
1134
+ if issubclass(cls, _DateTimeMixin):
1135
+ if self._storage_format:
1136
+ kw["storage_format"] = self._storage_format
1137
+ if self._reg:
1138
+ kw["regexp"] = self._reg
1139
+ return super().adapt(cls, **kw)
1140
+
1141
+ def literal_processor(self, dialect):
1142
+ bp = self.bind_processor(dialect)
1143
+
1144
+ def process(value):
1145
+ return "'%s'" % bp(value)
1146
+
1147
+ return process
1148
+
1149
+
1150
+ class DATETIME(_DateTimeMixin, sqltypes.DateTime):
1151
+ r"""Represent a Python datetime object in SQLite using a string.
1152
+
1153
+ The default string storage format is::
1154
+
1155
+ "%(year)04d-%(month)02d-%(day)02d %(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
1156
+
1157
+ e.g.:
1158
+
1159
+ .. sourcecode:: text
1160
+
1161
+ 2021-03-15 12:05:57.105542
1162
+
1163
+ The incoming storage format is by default parsed using the
1164
+ Python ``datetime.fromisoformat()`` function.
1165
+
1166
+ .. versionchanged:: 2.0 ``datetime.fromisoformat()`` is used for default
1167
+ datetime string parsing.
1168
+
1169
+ The storage format can be customized to some degree using the
1170
+ ``storage_format`` and ``regexp`` parameters, such as::
1171
+
1172
+ import re
1173
+ from sqlalchemy.dialects.sqlite import DATETIME
1174
+
1175
+ dt = DATETIME(
1176
+ storage_format=(
1177
+ "%(year)04d/%(month)02d/%(day)02d %(hour)02d:%(minute)02d:%(second)02d"
1178
+ ),
1179
+ regexp=r"(\d+)/(\d+)/(\d+) (\d+):(\d+):(\d+)",
1180
+ )
1181
+
1182
+ :param truncate_microseconds: when ``True`` microseconds will be truncated
1183
+ from the datetime. Can't be specified together with ``storage_format``
1184
+ or ``regexp``.
1185
+
1186
+ :param storage_format: format string which will be applied to the dict
1187
+ with keys year, month, day, hour, minute, second, and microsecond.
1188
+
1189
+ :param regexp: regular expression which will be applied to incoming result
1190
+ rows, replacing the use of ``datetime.fromisoformat()`` to parse incoming
1191
+ strings. If the regexp contains named groups, the resulting match dict is
1192
+ applied to the Python datetime() constructor as keyword arguments.
1193
+ Otherwise, if positional groups are used, the datetime() constructor
1194
+ is called with positional arguments via
1195
+ ``*map(int, match_obj.groups(0))``.
1196
+
1197
+ """ # noqa
1198
+
1199
+ _storage_format = (
1200
+ "%(year)04d-%(month)02d-%(day)02d "
1201
+ "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
1202
+ )
1203
+
1204
+ def __init__(self, *args, **kwargs):
1205
+ truncate_microseconds = kwargs.pop("truncate_microseconds", False)
1206
+ super().__init__(*args, **kwargs)
1207
+ if truncate_microseconds:
1208
+ assert "storage_format" not in kwargs, (
1209
+ "You can specify only "
1210
+ "one of truncate_microseconds or storage_format."
1211
+ )
1212
+ assert "regexp" not in kwargs, (
1213
+ "You can specify only one of "
1214
+ "truncate_microseconds or regexp."
1215
+ )
1216
+ self._storage_format = (
1217
+ "%(year)04d-%(month)02d-%(day)02d "
1218
+ "%(hour)02d:%(minute)02d:%(second)02d"
1219
+ )
1220
+
1221
+ def bind_processor(
1222
+ self, dialect: Dialect
1223
+ ) -> Optional[_BindProcessorType[Any]]:
1224
+ datetime_datetime = datetime.datetime
1225
+ datetime_date = datetime.date
1226
+ format_ = self._storage_format
1227
+
1228
+ def process(value):
1229
+ if value is None:
1230
+ return None
1231
+ elif isinstance(value, datetime_datetime):
1232
+ return format_ % {
1233
+ "year": value.year,
1234
+ "month": value.month,
1235
+ "day": value.day,
1236
+ "hour": value.hour,
1237
+ "minute": value.minute,
1238
+ "second": value.second,
1239
+ "microsecond": value.microsecond,
1240
+ }
1241
+ elif isinstance(value, datetime_date):
1242
+ return format_ % {
1243
+ "year": value.year,
1244
+ "month": value.month,
1245
+ "day": value.day,
1246
+ "hour": 0,
1247
+ "minute": 0,
1248
+ "second": 0,
1249
+ "microsecond": 0,
1250
+ }
1251
+ else:
1252
+ raise TypeError(
1253
+ "SQLite DateTime type only accepts Python "
1254
+ "datetime and date objects as input."
1255
+ )
1256
+
1257
+ return process
1258
+
1259
+ def result_processor(
1260
+ self, dialect: Dialect, coltype: object
1261
+ ) -> Optional[_ResultProcessorType[Any]]:
1262
+ if self._reg:
1263
+ return processors.str_to_datetime_processor_factory(
1264
+ self._reg, datetime.datetime
1265
+ )
1266
+ else:
1267
+ return processors.str_to_datetime
1268
+
1269
+
1270
+ class DATE(_DateTimeMixin, sqltypes.Date):
1271
+ r"""Represent a Python date object in SQLite using a string.
1272
+
1273
+ The default string storage format is::
1274
+
1275
+ "%(year)04d-%(month)02d-%(day)02d"
1276
+
1277
+ e.g.:
1278
+
1279
+ .. sourcecode:: text
1280
+
1281
+ 2011-03-15
1282
+
1283
+ The incoming storage format is by default parsed using the
1284
+ Python ``date.fromisoformat()`` function.
1285
+
1286
+ .. versionchanged:: 2.0 ``date.fromisoformat()`` is used for default
1287
+ date string parsing.
1288
+
1289
+
1290
+ The storage format can be customized to some degree using the
1291
+ ``storage_format`` and ``regexp`` parameters, such as::
1292
+
1293
+ import re
1294
+ from sqlalchemy.dialects.sqlite import DATE
1295
+
1296
+ d = DATE(
1297
+ storage_format="%(month)02d/%(day)02d/%(year)04d",
1298
+ regexp=re.compile("(?P<month>\d+)/(?P<day>\d+)/(?P<year>\d+)"),
1299
+ )
1300
+
1301
+ :param storage_format: format string which will be applied to the
1302
+ dict with keys year, month, and day.
1303
+
1304
+ :param regexp: regular expression which will be applied to
1305
+ incoming result rows, replacing the use of ``date.fromisoformat()`` to
1306
+ parse incoming strings. If the regexp contains named groups, the resulting
1307
+ match dict is applied to the Python date() constructor as keyword
1308
+ arguments. Otherwise, if positional groups are used, the date()
1309
+ constructor is called with positional arguments via
1310
+ ``*map(int, match_obj.groups(0))``.
1311
+
1312
+ """
1313
+
1314
+ _storage_format = "%(year)04d-%(month)02d-%(day)02d"
1315
+
1316
+ def bind_processor(
1317
+ self, dialect: Dialect
1318
+ ) -> Optional[_BindProcessorType[Any]]:
1319
+ datetime_date = datetime.date
1320
+ format_ = self._storage_format
1321
+
1322
+ def process(value):
1323
+ if value is None:
1324
+ return None
1325
+ elif isinstance(value, datetime_date):
1326
+ return format_ % {
1327
+ "year": value.year,
1328
+ "month": value.month,
1329
+ "day": value.day,
1330
+ }
1331
+ else:
1332
+ raise TypeError(
1333
+ "SQLite Date type only accepts Python "
1334
+ "date objects as input."
1335
+ )
1336
+
1337
+ return process
1338
+
1339
+ def result_processor(
1340
+ self, dialect: Dialect, coltype: object
1341
+ ) -> Optional[_ResultProcessorType[Any]]:
1342
+ if self._reg:
1343
+ return processors.str_to_datetime_processor_factory(
1344
+ self._reg, datetime.date
1345
+ )
1346
+ else:
1347
+ return processors.str_to_date
1348
+
1349
+
1350
+ class TIME(_DateTimeMixin, sqltypes.Time):
1351
+ r"""Represent a Python time object in SQLite using a string.
1352
+
1353
+ The default string storage format is::
1354
+
1355
+ "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
1356
+
1357
+ e.g.:
1358
+
1359
+ .. sourcecode:: text
1360
+
1361
+ 12:05:57.10558
1362
+
1363
+ The incoming storage format is by default parsed using the
1364
+ Python ``time.fromisoformat()`` function.
1365
+
1366
+ .. versionchanged:: 2.0 ``time.fromisoformat()`` is used for default
1367
+ time string parsing.
1368
+
1369
+ The storage format can be customized to some degree using the
1370
+ ``storage_format`` and ``regexp`` parameters, such as::
1371
+
1372
+ import re
1373
+ from sqlalchemy.dialects.sqlite import TIME
1374
+
1375
+ t = TIME(
1376
+ storage_format="%(hour)02d-%(minute)02d-%(second)02d-%(microsecond)06d",
1377
+ regexp=re.compile("(\d+)-(\d+)-(\d+)-(?:-(\d+))?"),
1378
+ )
1379
+
1380
+ :param truncate_microseconds: when ``True`` microseconds will be truncated
1381
+ from the time. Can't be specified together with ``storage_format``
1382
+ or ``regexp``.
1383
+
1384
+ :param storage_format: format string which will be applied to the dict
1385
+ with keys hour, minute, second, and microsecond.
1386
+
1387
+ :param regexp: regular expression which will be applied to incoming result
1388
+ rows, replacing the use of ``datetime.fromisoformat()`` to parse incoming
1389
+ strings. If the regexp contains named groups, the resulting match dict is
1390
+ applied to the Python time() constructor as keyword arguments. Otherwise,
1391
+ if positional groups are used, the time() constructor is called with
1392
+ positional arguments via ``*map(int, match_obj.groups(0))``.
1393
+
1394
+ """
1395
+
1396
+ _storage_format = "%(hour)02d:%(minute)02d:%(second)02d.%(microsecond)06d"
1397
+
1398
+ def __init__(self, *args, **kwargs):
1399
+ truncate_microseconds = kwargs.pop("truncate_microseconds", False)
1400
+ super().__init__(*args, **kwargs)
1401
+ if truncate_microseconds:
1402
+ assert "storage_format" not in kwargs, (
1403
+ "You can specify only "
1404
+ "one of truncate_microseconds or storage_format."
1405
+ )
1406
+ assert "regexp" not in kwargs, (
1407
+ "You can specify only one of "
1408
+ "truncate_microseconds or regexp."
1409
+ )
1410
+ self._storage_format = "%(hour)02d:%(minute)02d:%(second)02d"
1411
+
1412
+ def bind_processor(self, dialect):
1413
+ datetime_time = datetime.time
1414
+ format_ = self._storage_format
1415
+
1416
+ def process(value):
1417
+ if value is None:
1418
+ return None
1419
+ elif isinstance(value, datetime_time):
1420
+ return format_ % {
1421
+ "hour": value.hour,
1422
+ "minute": value.minute,
1423
+ "second": value.second,
1424
+ "microsecond": value.microsecond,
1425
+ }
1426
+ else:
1427
+ raise TypeError(
1428
+ "SQLite Time type only accepts Python "
1429
+ "time objects as input."
1430
+ )
1431
+
1432
+ return process
1433
+
1434
+ def result_processor(self, dialect, coltype):
1435
+ if self._reg:
1436
+ return processors.str_to_datetime_processor_factory(
1437
+ self._reg, datetime.time
1438
+ )
1439
+ else:
1440
+ return processors.str_to_time
1441
+
1442
+
1443
+ colspecs = {
1444
+ sqltypes.Date: DATE,
1445
+ sqltypes.DateTime: DATETIME,
1446
+ sqltypes.JSON: _SQliteJson,
1447
+ sqltypes.JSON.JSONIndexType: JSONIndexType,
1448
+ sqltypes.JSON.JSONPathType: JSONPathType,
1449
+ sqltypes.Time: TIME,
1450
+ JSONB: JSONB,
1451
+ }
1452
+
1453
+ ischema_names = {
1454
+ "BIGINT": sqltypes.BIGINT,
1455
+ "BLOB": sqltypes.BLOB,
1456
+ "BOOL": sqltypes.BOOLEAN,
1457
+ "BOOLEAN": sqltypes.BOOLEAN,
1458
+ "CHAR": sqltypes.CHAR,
1459
+ "DATE": sqltypes.DATE,
1460
+ "DATE_CHAR": sqltypes.DATE,
1461
+ "DATETIME": sqltypes.DATETIME,
1462
+ "DATETIME_CHAR": sqltypes.DATETIME,
1463
+ "DOUBLE": sqltypes.DOUBLE,
1464
+ "DECIMAL": sqltypes.DECIMAL,
1465
+ "FLOAT": sqltypes.FLOAT,
1466
+ "INT": sqltypes.INTEGER,
1467
+ "INTEGER": sqltypes.INTEGER,
1468
+ "JSON": JSON,
1469
+ "JSONB": JSONB,
1470
+ "NUMERIC": sqltypes.NUMERIC,
1471
+ "REAL": sqltypes.REAL,
1472
+ "SMALLINT": sqltypes.SMALLINT,
1473
+ "TEXT": sqltypes.TEXT,
1474
+ "TIME": sqltypes.TIME,
1475
+ "TIME_CHAR": sqltypes.TIME,
1476
+ "TIMESTAMP": sqltypes.TIMESTAMP,
1477
+ "VARCHAR": sqltypes.VARCHAR,
1478
+ "NVARCHAR": sqltypes.NVARCHAR,
1479
+ "NCHAR": sqltypes.NCHAR,
1480
+ }
1481
+
1482
+
1483
+ class SQLiteCompiler(compiler.SQLCompiler):
1484
+ extract_map = util.update_copy(
1485
+ compiler.SQLCompiler.extract_map,
1486
+ {
1487
+ "month": "%m",
1488
+ "day": "%d",
1489
+ "year": "%Y",
1490
+ "second": "%S",
1491
+ "hour": "%H",
1492
+ "doy": "%j",
1493
+ "minute": "%M",
1494
+ "epoch": "%s",
1495
+ "dow": "%w",
1496
+ "week": "%W",
1497
+ },
1498
+ )
1499
+
1500
+ def visit_truediv_binary(self, binary, operator, **kw):
1501
+ return (
1502
+ self.process(binary.left, **kw)
1503
+ + " / "
1504
+ + "(%s + 0.0)" % self.process(binary.right, **kw)
1505
+ )
1506
+
1507
+ def visit_now_func(self, fn, **kw):
1508
+ return "CURRENT_TIMESTAMP"
1509
+
1510
+ def visit_localtimestamp_func(self, func, **kw):
1511
+ return "DATETIME(CURRENT_TIMESTAMP, 'localtime')"
1512
+
1513
+ def visit_true(self, expr, **kw):
1514
+ return "1"
1515
+
1516
+ def visit_false(self, expr, **kw):
1517
+ return "0"
1518
+
1519
+ def visit_char_length_func(self, fn, **kw):
1520
+ return "length%s" % self.function_argspec(fn)
1521
+
1522
+ def visit_aggregate_strings_func(self, fn, **kw):
1523
+ return super().visit_aggregate_strings_func(
1524
+ fn, use_function_name="group_concat", **kw
1525
+ )
1526
+
1527
+ def visit_cast(self, cast, **kwargs):
1528
+ if self.dialect.supports_cast:
1529
+ return super().visit_cast(cast, **kwargs)
1530
+ else:
1531
+ return self.process(cast.clause, **kwargs)
1532
+
1533
+ def visit_extract(self, extract, **kw):
1534
+ try:
1535
+ return "CAST(STRFTIME('%s', %s) AS INTEGER)" % (
1536
+ self.extract_map[extract.field],
1537
+ self.process(extract.expr, **kw),
1538
+ )
1539
+ except KeyError as err:
1540
+ raise exc.CompileError(
1541
+ "%s is not a valid extract argument." % extract.field
1542
+ ) from err
1543
+
1544
+ def returning_clause(
1545
+ self,
1546
+ stmt,
1547
+ returning_cols,
1548
+ *,
1549
+ populate_result_map,
1550
+ **kw,
1551
+ ):
1552
+ kw["include_table"] = False
1553
+ return super().returning_clause(
1554
+ stmt, returning_cols, populate_result_map=populate_result_map, **kw
1555
+ )
1556
+
1557
+ def limit_clause(self, select, **kw):
1558
+ text = ""
1559
+ if select._limit_clause is not None:
1560
+ text += "\n LIMIT " + self.process(select._limit_clause, **kw)
1561
+ if select._offset_clause is not None:
1562
+ if select._limit_clause is None:
1563
+ text += "\n LIMIT " + self.process(sql.literal(-1))
1564
+ text += " OFFSET " + self.process(select._offset_clause, **kw)
1565
+ else:
1566
+ text += " OFFSET " + self.process(sql.literal(0), **kw)
1567
+ return text
1568
+
1569
+ def for_update_clause(self, select, **kw):
1570
+ # sqlite has no "FOR UPDATE" AFAICT
1571
+ return ""
1572
+
1573
+ def update_from_clause(
1574
+ self, update_stmt, from_table, extra_froms, from_hints, **kw
1575
+ ):
1576
+ kw["asfrom"] = True
1577
+ return "FROM " + ", ".join(
1578
+ t._compiler_dispatch(self, fromhints=from_hints, **kw)
1579
+ for t in extra_froms
1580
+ )
1581
+
1582
+ def visit_is_distinct_from_binary(self, binary, operator, **kw):
1583
+ return "%s IS NOT %s" % (
1584
+ self.process(binary.left),
1585
+ self.process(binary.right),
1586
+ )
1587
+
1588
+ def visit_is_not_distinct_from_binary(self, binary, operator, **kw):
1589
+ return "%s IS %s" % (
1590
+ self.process(binary.left),
1591
+ self.process(binary.right),
1592
+ )
1593
+
1594
+ def visit_json_getitem_op_binary(
1595
+ self, binary, operator, _cast_applied=False, **kw
1596
+ ):
1597
+ if (
1598
+ not _cast_applied
1599
+ and binary.type._type_affinity is not sqltypes.JSON
1600
+ ):
1601
+ kw["_cast_applied"] = True
1602
+ return self.process(sql.cast(binary, binary.type), **kw)
1603
+
1604
+ if binary.type._type_affinity is sqltypes.JSON:
1605
+ expr = "JSON_QUOTE(JSON_EXTRACT(%s, %s))"
1606
+ else:
1607
+ expr = "JSON_EXTRACT(%s, %s)"
1608
+
1609
+ return expr % (
1610
+ self.process(binary.left, **kw),
1611
+ self.process(binary.right, **kw),
1612
+ )
1613
+
1614
+ def visit_json_path_getitem_op_binary(
1615
+ self, binary, operator, _cast_applied=False, **kw
1616
+ ):
1617
+ if (
1618
+ not _cast_applied
1619
+ and binary.type._type_affinity is not sqltypes.JSON
1620
+ ):
1621
+ kw["_cast_applied"] = True
1622
+ return self.process(sql.cast(binary, binary.type), **kw)
1623
+
1624
+ if binary.type._type_affinity is sqltypes.JSON:
1625
+ expr = "JSON_QUOTE(JSON_EXTRACT(%s, %s))"
1626
+ else:
1627
+ expr = "JSON_EXTRACT(%s, %s)"
1628
+
1629
+ return expr % (
1630
+ self.process(binary.left, **kw),
1631
+ self.process(binary.right, **kw),
1632
+ )
1633
+
1634
+ def visit_empty_set_op_expr(self, type_, expand_op, **kw):
1635
+ # slightly old SQLite versions don't seem to be able to handle
1636
+ # the empty set impl
1637
+ return self.visit_empty_set_expr(type_)
1638
+
1639
+ def visit_empty_set_expr(self, element_types, **kw):
1640
+ return "SELECT %s FROM (SELECT %s) WHERE 1!=1" % (
1641
+ ", ".join("1" for type_ in element_types or [INTEGER()]),
1642
+ ", ".join("1" for type_ in element_types or [INTEGER()]),
1643
+ )
1644
+
1645
+ def visit_regexp_match_op_binary(self, binary, operator, **kw):
1646
+ return self._generate_generic_binary(binary, " REGEXP ", **kw)
1647
+
1648
+ def visit_not_regexp_match_op_binary(self, binary, operator, **kw):
1649
+ return self._generate_generic_binary(binary, " NOT REGEXP ", **kw)
1650
+
1651
+ def _on_conflict_target(self, clause, **kw):
1652
+ if clause.inferred_target_elements is not None:
1653
+ target_text = "(%s)" % ", ".join(
1654
+ (
1655
+ self.preparer.quote(c)
1656
+ if isinstance(c, str)
1657
+ else self.process(c, include_table=False, use_schema=False)
1658
+ )
1659
+ for c in clause.inferred_target_elements
1660
+ )
1661
+ if clause.inferred_target_whereclause is not None:
1662
+ whereclause_kw = dict(kw)
1663
+ whereclause_kw.update(
1664
+ include_table=False,
1665
+ use_schema=False,
1666
+ literal_execute=True,
1667
+ )
1668
+ target_text += " WHERE %s" % self.process(
1669
+ clause.inferred_target_whereclause,
1670
+ **whereclause_kw,
1671
+ )
1672
+
1673
+ else:
1674
+ target_text = ""
1675
+
1676
+ return target_text
1677
+
1678
+ def visit_on_conflict_do_nothing(self, on_conflict, **kw):
1679
+ target_text = self._on_conflict_target(on_conflict, **kw)
1680
+
1681
+ if target_text:
1682
+ return "ON CONFLICT %s DO NOTHING" % target_text
1683
+ else:
1684
+ return "ON CONFLICT DO NOTHING"
1685
+
1686
+ def visit_on_conflict_do_update(self, on_conflict, **kw):
1687
+ clause = on_conflict
1688
+
1689
+ target_text = self._on_conflict_target(on_conflict, **kw)
1690
+
1691
+ action_set_ops = []
1692
+
1693
+ set_parameters = dict(clause.update_values_to_set)
1694
+ # create a list of column assignment clauses as tuples
1695
+
1696
+ insert_statement = self.stack[-1]["selectable"]
1697
+ cols = insert_statement.table.c
1698
+ set_kw = dict(kw)
1699
+ set_kw.update(use_schema=False)
1700
+ for c in cols:
1701
+ col_key = c.key
1702
+
1703
+ if col_key in set_parameters:
1704
+ value = set_parameters.pop(col_key)
1705
+ elif c in set_parameters:
1706
+ value = set_parameters.pop(c)
1707
+ else:
1708
+ continue
1709
+
1710
+ if (
1711
+ isinstance(value, elements.BindParameter)
1712
+ and value.type._isnull
1713
+ ):
1714
+ value = value._with_binary_element_type(c.type)
1715
+
1716
+ value_text = self.process(
1717
+ value.self_group(), is_upsert_set=True, **set_kw
1718
+ )
1719
+
1720
+ key_text = self.preparer.quote(c.name)
1721
+ action_set_ops.append("%s = %s" % (key_text, value_text))
1722
+
1723
+ # check for names that don't match columns
1724
+ if set_parameters:
1725
+ util.warn(
1726
+ "Additional column names not matching "
1727
+ "any column keys in table '%s': %s"
1728
+ % (
1729
+ self.current_executable.table.name,
1730
+ (", ".join("'%s'" % c for c in set_parameters)),
1731
+ )
1732
+ )
1733
+ for k, v in set_parameters.items():
1734
+ key_text = (
1735
+ self.preparer.quote(k)
1736
+ if isinstance(k, str)
1737
+ else self.process(k, **set_kw)
1738
+ )
1739
+ value_text = self.process(
1740
+ coercions.expect(roles.ExpressionElementRole, v),
1741
+ is_upsert_set=True,
1742
+ **set_kw,
1743
+ )
1744
+ action_set_ops.append("%s = %s" % (key_text, value_text))
1745
+
1746
+ action_text = ", ".join(action_set_ops)
1747
+ if clause.update_whereclause is not None:
1748
+ where_kw = dict(kw)
1749
+ where_kw.update(include_table=True, use_schema=False)
1750
+ action_text += " WHERE %s" % self.process(
1751
+ clause.update_whereclause, **where_kw
1752
+ )
1753
+
1754
+ return "ON CONFLICT %s DO UPDATE SET %s" % (target_text, action_text)
1755
+
1756
+ def visit_bitwise_xor_op_binary(self, binary, operator, **kw):
1757
+ # sqlite has no xor. Use "a XOR b" = "(a | b) - (a & b)".
1758
+ kw["eager_grouping"] = True
1759
+ or_ = self._generate_generic_binary(binary, " | ", **kw)
1760
+ and_ = self._generate_generic_binary(binary, " & ", **kw)
1761
+ return f"({or_} - {and_})"
1762
+
1763
+
1764
+ class SQLiteDDLCompiler(compiler.DDLCompiler):
1765
+ def get_column_specification(self, column, **kwargs):
1766
+ coltype = self.dialect.type_compiler_instance.process(
1767
+ column.type, type_expression=column
1768
+ )
1769
+ colspec = self.preparer.format_column(column) + " " + coltype
1770
+ default = self.get_column_default_string(column)
1771
+ if default is not None:
1772
+
1773
+ if not re.match(r"""^\s*[\'\"\(]""", default) and re.match(
1774
+ r".*\W.*", default
1775
+ ):
1776
+ colspec += f" DEFAULT ({default})"
1777
+ else:
1778
+ colspec += f" DEFAULT {default}"
1779
+
1780
+ if not column.nullable:
1781
+ colspec += " NOT NULL"
1782
+
1783
+ on_conflict_clause = column.dialect_options["sqlite"][
1784
+ "on_conflict_not_null"
1785
+ ]
1786
+ if on_conflict_clause is not None:
1787
+ colspec += " ON CONFLICT " + on_conflict_clause
1788
+
1789
+ if column.primary_key:
1790
+ if (
1791
+ column.autoincrement is True
1792
+ and len(column.table.primary_key.columns) != 1
1793
+ ):
1794
+ raise exc.CompileError(
1795
+ "SQLite does not support autoincrement for "
1796
+ "composite primary keys"
1797
+ )
1798
+
1799
+ if (
1800
+ column.table.dialect_options["sqlite"]["autoincrement"]
1801
+ and len(column.table.primary_key.columns) == 1
1802
+ and issubclass(column.type._type_affinity, sqltypes.Integer)
1803
+ and not column.foreign_keys
1804
+ ):
1805
+ colspec += " PRIMARY KEY"
1806
+
1807
+ on_conflict_clause = column.dialect_options["sqlite"][
1808
+ "on_conflict_primary_key"
1809
+ ]
1810
+ if on_conflict_clause is not None:
1811
+ colspec += " ON CONFLICT " + on_conflict_clause
1812
+
1813
+ colspec += " AUTOINCREMENT"
1814
+
1815
+ if column.computed is not None:
1816
+ colspec += " " + self.process(column.computed)
1817
+
1818
+ return colspec
1819
+
1820
+ def visit_primary_key_constraint(self, constraint, **kw):
1821
+ # for columns with sqlite_autoincrement=True,
1822
+ # the PRIMARY KEY constraint can only be inline
1823
+ # with the column itself.
1824
+ if len(constraint.columns) == 1:
1825
+ c = list(constraint)[0]
1826
+ if (
1827
+ c.primary_key
1828
+ and c.table.dialect_options["sqlite"]["autoincrement"]
1829
+ and issubclass(c.type._type_affinity, sqltypes.Integer)
1830
+ and not c.foreign_keys
1831
+ ):
1832
+ return None
1833
+
1834
+ text = super().visit_primary_key_constraint(constraint)
1835
+
1836
+ on_conflict_clause = constraint.dialect_options["sqlite"][
1837
+ "on_conflict"
1838
+ ]
1839
+ if on_conflict_clause is None and len(constraint.columns) == 1:
1840
+ on_conflict_clause = list(constraint)[0].dialect_options["sqlite"][
1841
+ "on_conflict_primary_key"
1842
+ ]
1843
+
1844
+ if on_conflict_clause is not None:
1845
+ text += " ON CONFLICT " + on_conflict_clause
1846
+
1847
+ return text
1848
+
1849
+ def visit_unique_constraint(self, constraint, **kw):
1850
+ text = super().visit_unique_constraint(constraint)
1851
+
1852
+ on_conflict_clause = constraint.dialect_options["sqlite"][
1853
+ "on_conflict"
1854
+ ]
1855
+ if on_conflict_clause is None and len(constraint.columns) == 1:
1856
+ col1 = list(constraint)[0]
1857
+ if isinstance(col1, schema.SchemaItem):
1858
+ on_conflict_clause = list(constraint)[0].dialect_options[
1859
+ "sqlite"
1860
+ ]["on_conflict_unique"]
1861
+
1862
+ if on_conflict_clause is not None:
1863
+ text += " ON CONFLICT " + on_conflict_clause
1864
+
1865
+ return text
1866
+
1867
+ def visit_check_constraint(self, constraint, **kw):
1868
+ text = super().visit_check_constraint(constraint)
1869
+
1870
+ on_conflict_clause = constraint.dialect_options["sqlite"][
1871
+ "on_conflict"
1872
+ ]
1873
+
1874
+ if on_conflict_clause is not None:
1875
+ text += " ON CONFLICT " + on_conflict_clause
1876
+
1877
+ return text
1878
+
1879
+ def visit_column_check_constraint(self, constraint, **kw):
1880
+ text = super().visit_column_check_constraint(constraint)
1881
+
1882
+ if constraint.dialect_options["sqlite"]["on_conflict"] is not None:
1883
+ raise exc.CompileError(
1884
+ "SQLite does not support on conflict clause for "
1885
+ "column check constraint"
1886
+ )
1887
+
1888
+ return text
1889
+
1890
+ def visit_foreign_key_constraint(self, constraint, **kw):
1891
+ local_table = constraint.elements[0].parent.table
1892
+ remote_table = constraint.elements[0].column.table
1893
+
1894
+ if local_table.schema != remote_table.schema:
1895
+ return None
1896
+ else:
1897
+ return super().visit_foreign_key_constraint(constraint)
1898
+
1899
+ def define_constraint_remote_table(self, constraint, table, preparer):
1900
+ """Format the remote table clause of a CREATE CONSTRAINT clause."""
1901
+
1902
+ return preparer.format_table(table, use_schema=False)
1903
+
1904
+ def visit_create_index(
1905
+ self, create, include_schema=False, include_table_schema=True, **kw
1906
+ ):
1907
+ index = create.element
1908
+ self._verify_index_table(index)
1909
+ preparer = self.preparer
1910
+ text = "CREATE "
1911
+ if index.unique:
1912
+ text += "UNIQUE "
1913
+
1914
+ text += "INDEX "
1915
+
1916
+ if create.if_not_exists:
1917
+ text += "IF NOT EXISTS "
1918
+
1919
+ text += "%s ON %s (%s)" % (
1920
+ self._prepared_index_name(index, include_schema=True),
1921
+ preparer.format_table(index.table, use_schema=False),
1922
+ ", ".join(
1923
+ self.sql_compiler.process(
1924
+ expr, include_table=False, literal_binds=True
1925
+ )
1926
+ for expr in index.expressions
1927
+ ),
1928
+ )
1929
+
1930
+ whereclause = index.dialect_options["sqlite"]["where"]
1931
+ if whereclause is not None:
1932
+ where_compiled = self.sql_compiler.process(
1933
+ whereclause, include_table=False, literal_binds=True
1934
+ )
1935
+ text += " WHERE " + where_compiled
1936
+
1937
+ return text
1938
+
1939
+ def post_create_table(self, table):
1940
+ table_options = []
1941
+
1942
+ if not table.dialect_options["sqlite"]["with_rowid"]:
1943
+ table_options.append("WITHOUT ROWID")
1944
+
1945
+ if table.dialect_options["sqlite"]["strict"]:
1946
+ table_options.append("STRICT")
1947
+
1948
+ if table_options:
1949
+ return "\n " + ",\n ".join(table_options)
1950
+ else:
1951
+ return ""
1952
+
1953
+ def visit_create_view(self, create, **kw):
1954
+ """Handle SQLite if_not_exists dialect option for CREATE VIEW."""
1955
+ # Get the if_not_exists dialect option from the CreateView object
1956
+ if_not_exists = create.dialect_options["sqlite"].get(
1957
+ "if_not_exists", False
1958
+ )
1959
+
1960
+ # Pass if_not_exists through kw to the parent's _generate_table_select
1961
+ kw["if_not_exists"] = if_not_exists
1962
+ return super().visit_create_view(create, **kw)
1963
+
1964
+
1965
+ class SQLiteTypeCompiler(compiler.GenericTypeCompiler):
1966
+ def visit_large_binary(self, type_, **kw):
1967
+ return self.visit_BLOB(type_)
1968
+
1969
+ def visit_DATETIME(self, type_, **kw):
1970
+ if (
1971
+ not isinstance(type_, _DateTimeMixin)
1972
+ or type_.format_is_text_affinity
1973
+ ):
1974
+ return super().visit_DATETIME(type_)
1975
+ else:
1976
+ return "DATETIME_CHAR"
1977
+
1978
+ def visit_DATE(self, type_, **kw):
1979
+ if (
1980
+ not isinstance(type_, _DateTimeMixin)
1981
+ or type_.format_is_text_affinity
1982
+ ):
1983
+ return super().visit_DATE(type_)
1984
+ else:
1985
+ return "DATE_CHAR"
1986
+
1987
+ def visit_TIME(self, type_, **kw):
1988
+ if (
1989
+ not isinstance(type_, _DateTimeMixin)
1990
+ or type_.format_is_text_affinity
1991
+ ):
1992
+ return super().visit_TIME(type_)
1993
+ else:
1994
+ return "TIME_CHAR"
1995
+
1996
+ def visit_JSON(self, type_, **kw):
1997
+ # note this name provides NUMERIC affinity, not TEXT.
1998
+ # should not be an issue unless the JSON value consists of a single
1999
+ # numeric value. JSONTEXT can be used if this case is required.
2000
+ return "JSON"
2001
+
2002
+ def visit_JSONB(self, type_, **kw):
2003
+ return "JSONB"
2004
+
2005
+
2006
+ class SQLiteIdentifierPreparer(compiler.IdentifierPreparer):
2007
+ reserved_words = {
2008
+ "add",
2009
+ "after",
2010
+ "all",
2011
+ "alter",
2012
+ "analyze",
2013
+ "and",
2014
+ "as",
2015
+ "asc",
2016
+ "attach",
2017
+ "autoincrement",
2018
+ "before",
2019
+ "begin",
2020
+ "between",
2021
+ "by",
2022
+ "cascade",
2023
+ "case",
2024
+ "cast",
2025
+ "check",
2026
+ "collate",
2027
+ "column",
2028
+ "commit",
2029
+ "conflict",
2030
+ "constraint",
2031
+ "create",
2032
+ "cross",
2033
+ "current_date",
2034
+ "current_time",
2035
+ "current_timestamp",
2036
+ "database",
2037
+ "default",
2038
+ "deferrable",
2039
+ "deferred",
2040
+ "delete",
2041
+ "desc",
2042
+ "detach",
2043
+ "distinct",
2044
+ "drop",
2045
+ "each",
2046
+ "else",
2047
+ "end",
2048
+ "escape",
2049
+ "except",
2050
+ "exclusive",
2051
+ "exists",
2052
+ "explain",
2053
+ "false",
2054
+ "fail",
2055
+ "for",
2056
+ "foreign",
2057
+ "from",
2058
+ "full",
2059
+ "glob",
2060
+ "group",
2061
+ "having",
2062
+ "if",
2063
+ "ignore",
2064
+ "immediate",
2065
+ "in",
2066
+ "index",
2067
+ "indexed",
2068
+ "initially",
2069
+ "inner",
2070
+ "insert",
2071
+ "instead",
2072
+ "intersect",
2073
+ "into",
2074
+ "is",
2075
+ "isnull",
2076
+ "join",
2077
+ "key",
2078
+ "left",
2079
+ "like",
2080
+ "limit",
2081
+ "match",
2082
+ "natural",
2083
+ "not",
2084
+ "notnull",
2085
+ "null",
2086
+ "of",
2087
+ "offset",
2088
+ "on",
2089
+ "or",
2090
+ "order",
2091
+ "outer",
2092
+ "plan",
2093
+ "pragma",
2094
+ "primary",
2095
+ "query",
2096
+ "raise",
2097
+ "references",
2098
+ "reindex",
2099
+ "rename",
2100
+ "replace",
2101
+ "restrict",
2102
+ "right",
2103
+ "rollback",
2104
+ "row",
2105
+ "select",
2106
+ "set",
2107
+ "table",
2108
+ "temp",
2109
+ "temporary",
2110
+ "then",
2111
+ "to",
2112
+ "transaction",
2113
+ "trigger",
2114
+ "true",
2115
+ "union",
2116
+ "unique",
2117
+ "update",
2118
+ "using",
2119
+ "vacuum",
2120
+ "values",
2121
+ "view",
2122
+ "virtual",
2123
+ "when",
2124
+ "where",
2125
+ }
2126
+
2127
+
2128
+ class SQLiteExecutionContext(default.DefaultExecutionContext):
2129
+ @util.memoized_property
2130
+ def _preserve_raw_colnames(self):
2131
+ return (
2132
+ not self.dialect._broken_dotted_colnames
2133
+ or self.execution_options.get("sqlite_raw_colnames", False)
2134
+ )
2135
+
2136
+ def _translate_colname(self, colname):
2137
+ # TODO: detect SQLite version 3.10.0 or greater;
2138
+ # see [ticket:3633]
2139
+
2140
+ # adjust for dotted column names. SQLite
2141
+ # in the case of UNION may store col names as
2142
+ # "tablename.colname", or if using an attached database,
2143
+ # "database.tablename.colname", in cursor.description
2144
+ if not self._preserve_raw_colnames and "." in colname:
2145
+ return colname.split(".")[-1], colname
2146
+ else:
2147
+ return colname, None
2148
+
2149
+
2150
+ class SQLiteDialect(default.DefaultDialect):
2151
+ name = "sqlite"
2152
+ supports_alter = False
2153
+
2154
+ # SQlite supports "DEFAULT VALUES" but *does not* support
2155
+ # "VALUES (DEFAULT)"
2156
+ supports_default_values = True
2157
+ supports_default_metavalue = False
2158
+
2159
+ # sqlite issue:
2160
+ # https://github.com/python/cpython/issues/93421
2161
+ # note this parameter is no longer used by the ORM or default dialect
2162
+ # see #9414
2163
+ supports_sane_rowcount_returning = False
2164
+
2165
+ supports_empty_insert = False
2166
+ supports_cast = True
2167
+ supports_multivalues_insert = True
2168
+ use_insertmanyvalues = True
2169
+ tuple_in_values = True
2170
+ supports_statement_cache = True
2171
+ insert_null_pk_still_autoincrements = True
2172
+ insert_returning = True
2173
+ update_returning = True
2174
+ update_returning_multifrom = True
2175
+ delete_returning = True
2176
+ update_returning_multifrom = True
2177
+
2178
+ supports_default_metavalue = True
2179
+ """dialect supports INSERT... VALUES (DEFAULT) syntax"""
2180
+
2181
+ default_metavalue_token = "NULL"
2182
+ """for INSERT... VALUES (DEFAULT) syntax, the token to put in the
2183
+ parenthesis."""
2184
+
2185
+ default_paramstyle = "qmark"
2186
+ execution_ctx_cls = SQLiteExecutionContext
2187
+ statement_compiler = SQLiteCompiler
2188
+ ddl_compiler = SQLiteDDLCompiler
2189
+ type_compiler_cls = SQLiteTypeCompiler
2190
+ preparer = SQLiteIdentifierPreparer
2191
+ ischema_names = ischema_names
2192
+ colspecs = colspecs
2193
+
2194
+ construct_arguments = [
2195
+ (
2196
+ sa_schema.Table,
2197
+ {
2198
+ "autoincrement": False,
2199
+ "with_rowid": True,
2200
+ "strict": False,
2201
+ },
2202
+ ),
2203
+ (sa_schema.Index, {"where": None}),
2204
+ (
2205
+ sa_schema.Column,
2206
+ {
2207
+ "on_conflict_primary_key": None,
2208
+ "on_conflict_not_null": None,
2209
+ "on_conflict_unique": None,
2210
+ },
2211
+ ),
2212
+ (sa_schema.Constraint, {"on_conflict": None}),
2213
+ (sa_ddl.CreateView, {"if_not_exists": False}),
2214
+ ]
2215
+
2216
+ _broken_fk_pragma_quotes = False
2217
+ _broken_dotted_colnames = False
2218
+
2219
+ def __init__(
2220
+ self,
2221
+ native_datetime: bool = False,
2222
+ json_serializer: Callable[[_JSON_VALUE], str] | None = None,
2223
+ json_deserializer: Callable[[str], _JSON_VALUE] | None = None,
2224
+ **kwargs: Any,
2225
+ ) -> None:
2226
+ default.DefaultDialect.__init__(self, **kwargs)
2227
+
2228
+ self._json_serializer = json_serializer
2229
+ self._json_deserializer = json_deserializer
2230
+
2231
+ # this flag used by pysqlite dialect, and perhaps others in the
2232
+ # future, to indicate the driver is handling date/timestamp
2233
+ # conversions (and perhaps datetime/time as well on some hypothetical
2234
+ # driver ?)
2235
+ self.native_datetime = native_datetime
2236
+
2237
+ if self.dbapi is not None:
2238
+ if self.dbapi.sqlite_version_info < (3, 7, 16):
2239
+ util.warn(
2240
+ "SQLite version %s is older than 3.7.16, and will not "
2241
+ "support right nested joins, as are sometimes used in "
2242
+ "more complex ORM scenarios. SQLAlchemy 1.4 and above "
2243
+ "no longer tries to rewrite these joins."
2244
+ % (self.dbapi.sqlite_version_info,)
2245
+ )
2246
+
2247
+ # NOTE: python 3.7 on fedora for me has SQLite 3.34.1. These
2248
+ # version checks are getting very stale.
2249
+ self._broken_dotted_colnames = self.dbapi.sqlite_version_info < (
2250
+ 3,
2251
+ 10,
2252
+ 0,
2253
+ )
2254
+ self.supports_default_values = self.dbapi.sqlite_version_info >= (
2255
+ 3,
2256
+ 3,
2257
+ 8,
2258
+ )
2259
+ self.supports_cast = self.dbapi.sqlite_version_info >= (3, 2, 3)
2260
+ self.supports_multivalues_insert = (
2261
+ # https://www.sqlite.org/releaselog/3_7_11.html
2262
+ self.dbapi.sqlite_version_info
2263
+ >= (3, 7, 11)
2264
+ )
2265
+ # see https://www.sqlalchemy.org/trac/ticket/2568
2266
+ # as well as https://www.sqlite.org/src/info/600482d161
2267
+ self._broken_fk_pragma_quotes = self.dbapi.sqlite_version_info < (
2268
+ 3,
2269
+ 6,
2270
+ 14,
2271
+ )
2272
+
2273
+ if self.dbapi.sqlite_version_info < (3, 35):
2274
+ self.update_returning = self.delete_returning = (
2275
+ self.insert_returning
2276
+ ) = False
2277
+
2278
+ if self.dbapi.sqlite_version_info < (3, 32, 0):
2279
+ # https://www.sqlite.org/limits.html
2280
+ self.insertmanyvalues_max_parameters = 999
2281
+
2282
+ _isolation_lookup = util.immutabledict(
2283
+ {"READ UNCOMMITTED": 1, "SERIALIZABLE": 0}
2284
+ )
2285
+
2286
+ def get_isolation_level_values(self, dbapi_connection):
2287
+ return list(self._isolation_lookup)
2288
+
2289
+ def set_isolation_level(
2290
+ self, dbapi_connection: DBAPIConnection, level: IsolationLevel
2291
+ ) -> None:
2292
+ isolation_level = self._isolation_lookup[level]
2293
+
2294
+ cursor = dbapi_connection.cursor()
2295
+ cursor.execute(f"PRAGMA read_uncommitted = {isolation_level}")
2296
+ cursor.close()
2297
+
2298
+ def get_isolation_level(self, dbapi_connection):
2299
+ cursor = dbapi_connection.cursor()
2300
+ cursor.execute("PRAGMA read_uncommitted")
2301
+ res = cursor.fetchone()
2302
+ if res:
2303
+ value = res[0]
2304
+ else:
2305
+ # https://www.sqlite.org/changes.html#version_3_3_3
2306
+ # "Optional READ UNCOMMITTED isolation (instead of the
2307
+ # default isolation level of SERIALIZABLE) and
2308
+ # table level locking when database connections
2309
+ # share a common cache.""
2310
+ # pre-SQLite 3.3.0 default to 0
2311
+ value = 0
2312
+ cursor.close()
2313
+ if value == 0:
2314
+ return "SERIALIZABLE"
2315
+ elif value == 1:
2316
+ return "READ UNCOMMITTED"
2317
+ else:
2318
+ assert False, "Unknown isolation level %s" % value
2319
+
2320
+ @reflection.cache
2321
+ def get_schema_names(self, connection, **kw):
2322
+ s = "PRAGMA database_list"
2323
+ dl = connection.exec_driver_sql(s)
2324
+
2325
+ return [db[1] for db in dl if db[1] != "temp"]
2326
+
2327
+ def _format_schema(self, schema, table_name):
2328
+ if schema is not None:
2329
+ qschema = self.identifier_preparer.quote_identifier(schema)
2330
+ name = f"{qschema}.{table_name}"
2331
+ else:
2332
+ name = table_name
2333
+ return name
2334
+
2335
+ def _sqlite_main_query(
2336
+ self,
2337
+ table: str,
2338
+ type_: str,
2339
+ schema: Optional[str],
2340
+ sqlite_include_internal: bool,
2341
+ ):
2342
+ main = self._format_schema(schema, table)
2343
+ if not sqlite_include_internal:
2344
+ filter_table = " AND name NOT LIKE 'sqlite~_%' ESCAPE '~'"
2345
+ else:
2346
+ filter_table = ""
2347
+ query = (
2348
+ f"SELECT name FROM {main} "
2349
+ f"WHERE type='{type_}'{filter_table} "
2350
+ "ORDER BY name"
2351
+ )
2352
+ return query
2353
+
2354
+ @reflection.cache
2355
+ def get_table_names(
2356
+ self, connection, schema=None, sqlite_include_internal=False, **kw
2357
+ ):
2358
+ query = self._sqlite_main_query(
2359
+ "sqlite_master", "table", schema, sqlite_include_internal
2360
+ )
2361
+ names = connection.exec_driver_sql(query).scalars().all()
2362
+ return names
2363
+
2364
+ @reflection.cache
2365
+ def get_temp_table_names(
2366
+ self, connection, sqlite_include_internal=False, **kw
2367
+ ):
2368
+ query = self._sqlite_main_query(
2369
+ "sqlite_temp_master", "table", None, sqlite_include_internal
2370
+ )
2371
+ names = connection.exec_driver_sql(query).scalars().all()
2372
+ return names
2373
+
2374
+ @reflection.cache
2375
+ def get_temp_view_names(
2376
+ self, connection, sqlite_include_internal=False, **kw
2377
+ ):
2378
+ query = self._sqlite_main_query(
2379
+ "sqlite_temp_master", "view", None, sqlite_include_internal
2380
+ )
2381
+ names = connection.exec_driver_sql(query).scalars().all()
2382
+ return names
2383
+
2384
+ @reflection.cache
2385
+ def has_table(self, connection, table_name, schema=None, **kw):
2386
+ self._ensure_has_table_connection(connection)
2387
+
2388
+ if schema is not None and schema not in self.get_schema_names(
2389
+ connection, **kw
2390
+ ):
2391
+ return False
2392
+
2393
+ info = self._get_table_pragma(
2394
+ connection, "table_info", table_name, schema=schema
2395
+ )
2396
+ return bool(info)
2397
+
2398
+ def _get_default_schema_name(self, connection):
2399
+ return "main"
2400
+
2401
+ @reflection.cache
2402
+ def get_view_names(
2403
+ self, connection, schema=None, sqlite_include_internal=False, **kw
2404
+ ):
2405
+ query = self._sqlite_main_query(
2406
+ "sqlite_master", "view", schema, sqlite_include_internal
2407
+ )
2408
+ names = connection.exec_driver_sql(query).scalars().all()
2409
+ return names
2410
+
2411
+ @reflection.cache
2412
+ def get_view_definition(self, connection, view_name, schema=None, **kw):
2413
+ if schema is not None:
2414
+ qschema = self.identifier_preparer.quote_identifier(schema)
2415
+ master = f"{qschema}.sqlite_master"
2416
+ s = ("SELECT sql FROM %s WHERE name = ? AND type='view'") % (
2417
+ master,
2418
+ )
2419
+ rs = connection.exec_driver_sql(s, (view_name,))
2420
+ else:
2421
+ try:
2422
+ s = (
2423
+ "SELECT sql FROM "
2424
+ " (SELECT * FROM sqlite_master UNION ALL "
2425
+ " SELECT * FROM sqlite_temp_master) "
2426
+ "WHERE name = ? "
2427
+ "AND type='view'"
2428
+ )
2429
+ rs = connection.exec_driver_sql(s, (view_name,))
2430
+ except exc.DBAPIError:
2431
+ s = (
2432
+ "SELECT sql FROM sqlite_master WHERE name = ? "
2433
+ "AND type='view'"
2434
+ )
2435
+ rs = connection.exec_driver_sql(s, (view_name,))
2436
+
2437
+ result = rs.fetchall()
2438
+ if result:
2439
+ return result[0].sql
2440
+ else:
2441
+ raise exc.NoSuchTableError(
2442
+ f"{schema}.{view_name}" if schema else view_name
2443
+ )
2444
+
2445
+ @reflection.cache
2446
+ def get_columns(self, connection, table_name, schema=None, **kw):
2447
+ pragma = "table_info"
2448
+ # computed columns are threaded as hidden, they require table_xinfo
2449
+ if self.server_version_info >= (3, 31):
2450
+ pragma = "table_xinfo"
2451
+ info = self._get_table_pragma(
2452
+ connection, pragma, table_name, schema=schema
2453
+ )
2454
+ columns = []
2455
+ tablesql = None
2456
+ for row in info:
2457
+ name = row[1]
2458
+ type_ = row[2].upper()
2459
+ nullable = not row[3]
2460
+ default = row[4]
2461
+ primary_key = row[5]
2462
+ hidden = row[6] if pragma == "table_xinfo" else 0
2463
+
2464
+ # hidden has value 0 for normal columns, 1 for hidden columns,
2465
+ # 2 for computed virtual columns and 3 for computed stored columns
2466
+ # https://www.sqlite.org/src/info/069351b85f9a706f60d3e98fbc8aaf40c374356b967c0464aede30ead3d9d18b
2467
+ if hidden == 1:
2468
+ continue
2469
+
2470
+ generated = bool(hidden)
2471
+ persisted = hidden == 3
2472
+
2473
+ if tablesql is None and generated:
2474
+ tablesql = self._get_table_sql(
2475
+ connection, table_name, schema, **kw
2476
+ )
2477
+ # remove create table
2478
+ match = re.match(
2479
+ (
2480
+ r"create table .*?\((.*)\)"
2481
+ r"(?:\s*,?\s*(?:WITHOUT\s+ROWID|STRICT))*$"
2482
+ ),
2483
+ tablesql.strip(),
2484
+ re.DOTALL | re.IGNORECASE,
2485
+ )
2486
+ assert match, f"create table not found in {tablesql}"
2487
+ tablesql = match.group(1).strip()
2488
+
2489
+ columns.append(
2490
+ self._get_column_info(
2491
+ name,
2492
+ type_,
2493
+ nullable,
2494
+ default,
2495
+ primary_key,
2496
+ generated,
2497
+ persisted,
2498
+ tablesql,
2499
+ )
2500
+ )
2501
+ if columns:
2502
+ return columns
2503
+ elif not self.has_table(connection, table_name, schema):
2504
+ raise exc.NoSuchTableError(
2505
+ f"{schema}.{table_name}" if schema else table_name
2506
+ )
2507
+ else:
2508
+ return ReflectionDefaults.columns()
2509
+
2510
+ def _get_column_info(
2511
+ self,
2512
+ name,
2513
+ type_,
2514
+ nullable,
2515
+ default,
2516
+ primary_key,
2517
+ generated,
2518
+ persisted,
2519
+ tablesql,
2520
+ ):
2521
+ if generated:
2522
+ # the type of a column "cc INTEGER GENERATED ALWAYS AS (1 + 42)"
2523
+ # somehow is "INTEGER GENERATED ALWAYS"
2524
+ type_ = re.sub("generated", "", type_, flags=re.IGNORECASE)
2525
+ type_ = re.sub("always", "", type_, flags=re.IGNORECASE).strip()
2526
+
2527
+ coltype = self._resolve_type_affinity(type_)
2528
+
2529
+ if default is not None:
2530
+ default = str(default)
2531
+
2532
+ colspec = {
2533
+ "name": name,
2534
+ "type": coltype,
2535
+ "nullable": nullable,
2536
+ "default": default,
2537
+ "primary_key": primary_key,
2538
+ }
2539
+ if generated:
2540
+ sqltext = ""
2541
+ if tablesql:
2542
+ pattern = (
2543
+ r"[^,]*\s+GENERATED\s+ALWAYS\s+AS"
2544
+ r"\s+\((.*)\)\s*(?:virtual|stored)?"
2545
+ )
2546
+ match = re.search(
2547
+ re.escape(name) + pattern, tablesql, re.IGNORECASE
2548
+ )
2549
+ if match:
2550
+ sqltext = match.group(1)
2551
+ colspec["computed"] = {"sqltext": sqltext, "persisted": persisted}
2552
+ return colspec
2553
+
2554
+ def _resolve_type_affinity(self, type_):
2555
+ """Return a data type from a reflected column, using affinity rules.
2556
+
2557
+ SQLite's goal for universal compatibility introduces some complexity
2558
+ during reflection, as a column's defined type might not actually be a
2559
+ type that SQLite understands - or indeed, my not be defined *at all*.
2560
+ Internally, SQLite handles this with a 'data type affinity' for each
2561
+ column definition, mapping to one of 'TEXT', 'NUMERIC', 'INTEGER',
2562
+ 'REAL', or 'NONE' (raw bits). The algorithm that determines this is
2563
+ listed in https://www.sqlite.org/datatype3.html section 2.1.
2564
+
2565
+ This method allows SQLAlchemy to support that algorithm, while still
2566
+ providing access to smarter reflection utilities by recognizing
2567
+ column definitions that SQLite only supports through affinity (like
2568
+ DATE and DOUBLE).
2569
+
2570
+ """
2571
+ match = re.match(r"([\w ]+)(\(.*?\))?", type_)
2572
+ if match:
2573
+ coltype = match.group(1)
2574
+ args = match.group(2)
2575
+ else:
2576
+ coltype = ""
2577
+ args = ""
2578
+
2579
+ if coltype in self.ischema_names:
2580
+ coltype = self.ischema_names[coltype]
2581
+ elif "INT" in coltype:
2582
+ coltype = sqltypes.INTEGER
2583
+ elif "CHAR" in coltype or "CLOB" in coltype or "TEXT" in coltype:
2584
+ coltype = sqltypes.TEXT
2585
+ elif "BLOB" in coltype or not coltype:
2586
+ coltype = sqltypes.NullType
2587
+ elif "REAL" in coltype or "FLOA" in coltype or "DOUB" in coltype:
2588
+ coltype = sqltypes.REAL
2589
+ else:
2590
+ coltype = sqltypes.NUMERIC
2591
+
2592
+ if args is not None:
2593
+ args = re.findall(r"(\d+)", args)
2594
+ try:
2595
+ coltype = coltype(*[int(a) for a in args])
2596
+ except TypeError:
2597
+ util.warn(
2598
+ "Could not instantiate type %s with "
2599
+ "reflected arguments %s; using no arguments."
2600
+ % (coltype, args)
2601
+ )
2602
+ coltype = coltype()
2603
+ else:
2604
+ coltype = coltype()
2605
+
2606
+ return coltype
2607
+
2608
+ @reflection.cache
2609
+ def get_pk_constraint(self, connection, table_name, schema=None, **kw):
2610
+ constraint_name = None
2611
+ table_data = self._get_table_sql(connection, table_name, schema=schema)
2612
+ if table_data:
2613
+ PK_PATTERN = r'CONSTRAINT\s+(?:"(.+?)"|(\w+))\s+PRIMARY\s+KEY'
2614
+ result = re.search(PK_PATTERN, table_data, re.I)
2615
+ if result:
2616
+ constraint_name = result.group(1) or result.group(2)
2617
+ else:
2618
+ constraint_name = None
2619
+
2620
+ cols = self.get_columns(connection, table_name, schema, **kw)
2621
+ # consider only pk columns. This also avoids sorting the cached
2622
+ # value returned by get_columns
2623
+ cols = [col for col in cols if col.get("primary_key", 0) > 0]
2624
+ cols.sort(key=lambda col: col.get("primary_key"))
2625
+ pkeys = [col["name"] for col in cols]
2626
+
2627
+ if pkeys:
2628
+ return {"constrained_columns": pkeys, "name": constraint_name}
2629
+ else:
2630
+ return ReflectionDefaults.pk_constraint()
2631
+
2632
+ @reflection.cache
2633
+ def get_foreign_keys(self, connection, table_name, schema=None, **kw):
2634
+ # sqlite makes this *extremely difficult*.
2635
+ # First, use the pragma to get the actual FKs.
2636
+ pragma_fks = self._get_table_pragma(
2637
+ connection, "foreign_key_list", table_name, schema=schema
2638
+ )
2639
+
2640
+ fks = {}
2641
+
2642
+ for row in pragma_fks:
2643
+ numerical_id, rtbl, lcol, rcol = (row[0], row[2], row[3], row[4])
2644
+
2645
+ if not rcol:
2646
+ # no referred column, which means it was not named in the
2647
+ # original DDL. The referred columns of the foreign key
2648
+ # constraint are therefore the primary key of the referred
2649
+ # table.
2650
+ try:
2651
+ referred_pk = self.get_pk_constraint(
2652
+ connection, rtbl, schema=schema, **kw
2653
+ )
2654
+ referred_columns = referred_pk["constrained_columns"]
2655
+ except exc.NoSuchTableError:
2656
+ # ignore not existing parents
2657
+ referred_columns = []
2658
+ else:
2659
+ # note we use this list only if this is the first column
2660
+ # in the constraint. for subsequent columns we ignore the
2661
+ # list and append "rcol" if present.
2662
+ referred_columns = []
2663
+
2664
+ if self._broken_fk_pragma_quotes:
2665
+ rtbl = re.sub(r"^[\"\[`\']|[\"\]`\']$", "", rtbl)
2666
+
2667
+ if numerical_id in fks:
2668
+ fk = fks[numerical_id]
2669
+ else:
2670
+ fk = fks[numerical_id] = {
2671
+ "name": None,
2672
+ "constrained_columns": [],
2673
+ "referred_schema": schema,
2674
+ "referred_table": rtbl,
2675
+ "referred_columns": referred_columns,
2676
+ "options": {},
2677
+ }
2678
+ fks[numerical_id] = fk
2679
+
2680
+ fk["constrained_columns"].append(lcol)
2681
+
2682
+ if rcol:
2683
+ fk["referred_columns"].append(rcol)
2684
+
2685
+ def fk_sig(constrained_columns, referred_table, referred_columns):
2686
+ return (
2687
+ tuple(constrained_columns)
2688
+ + (referred_table,)
2689
+ + tuple(referred_columns)
2690
+ )
2691
+
2692
+ # then, parse the actual SQL and attempt to find DDL that matches
2693
+ # the names as well. SQLite saves the DDL in whatever format
2694
+ # it was typed in as, so need to be liberal here.
2695
+
2696
+ keys_by_signature = {
2697
+ fk_sig(
2698
+ fk["constrained_columns"],
2699
+ fk["referred_table"],
2700
+ fk["referred_columns"],
2701
+ ): fk
2702
+ for fk in fks.values()
2703
+ }
2704
+
2705
+ table_data = self._get_table_sql(connection, table_name, schema=schema)
2706
+
2707
+ def parse_fks():
2708
+ if table_data is None:
2709
+ # system tables, etc.
2710
+ return
2711
+
2712
+ # note that we already have the FKs from PRAGMA above. This whole
2713
+ # regexp thing is trying to locate additional detail about the
2714
+ # FKs, namely the name of the constraint and other options.
2715
+ # so parsing the columns is really about matching it up to what
2716
+ # we already have.
2717
+ FK_PATTERN = (
2718
+ r'(?:CONSTRAINT\s+(?:"(.+?)"|(\w+))\s+)?'
2719
+ r"FOREIGN\s+KEY\s*\(\s*(.+?)\s*\)\s+"
2720
+ r'REFERENCES\s+(?:(?:"(.+?)")|([a-z0-9_]+))\s*\(\s*((?:(?:"[^"]+"|[a-z0-9_]+)\s*(?:,\s*)?)+)\)\s*' # noqa: E501
2721
+ r"((?:ON\s+(?:DELETE|UPDATE)\s+"
2722
+ r"(?:SET\s+NULL|SET\s+DEFAULT|CASCADE|RESTRICT|"
2723
+ r"NO\s+ACTION)\s*)*)"
2724
+ r"((?:NOT\s+)?DEFERRABLE)?"
2725
+ r"(?:\s+INITIALLY\s+(DEFERRED|IMMEDIATE))?"
2726
+ )
2727
+ for match in re.finditer(FK_PATTERN, table_data, re.I):
2728
+ (
2729
+ constraint_quoted_name,
2730
+ constraint_name,
2731
+ constrained_columns,
2732
+ referred_quoted_name,
2733
+ referred_name,
2734
+ referred_columns,
2735
+ onupdatedelete,
2736
+ deferrable,
2737
+ initially,
2738
+ ) = match.group(1, 2, 3, 4, 5, 6, 7, 8, 9)
2739
+ constraint_name = constraint_quoted_name or constraint_name
2740
+ constrained_columns = list(
2741
+ self._find_cols_in_sig(constrained_columns)
2742
+ )
2743
+ if not referred_columns:
2744
+ referred_columns = constrained_columns
2745
+ else:
2746
+ referred_columns = list(
2747
+ self._find_cols_in_sig(referred_columns)
2748
+ )
2749
+ referred_name = referred_quoted_name or referred_name
2750
+ options = {}
2751
+
2752
+ # a newline may separate the words of an
2753
+ # ON DELETE / ON UPDATE clause; normalize to single
2754
+ # spaces so the tokens below compare correctly
2755
+ onupdatedelete = re.sub(
2756
+ r"\s+", " ", onupdatedelete.upper()
2757
+ ).strip()
2758
+ for token in re.split(r" *\bON\b *", onupdatedelete):
2759
+ if token.startswith("DELETE"):
2760
+ ondelete = token[6:].strip()
2761
+ if ondelete and ondelete != "NO ACTION":
2762
+ options["ondelete"] = ondelete
2763
+ elif token.startswith("UPDATE"):
2764
+ onupdate = token[6:].strip()
2765
+ if onupdate and onupdate != "NO ACTION":
2766
+ options["onupdate"] = onupdate
2767
+
2768
+ if deferrable:
2769
+ options["deferrable"] = "NOT" not in deferrable.upper()
2770
+ if initially:
2771
+ options["initially"] = initially.upper()
2772
+
2773
+ yield (
2774
+ constraint_name,
2775
+ constrained_columns,
2776
+ referred_name,
2777
+ referred_columns,
2778
+ options,
2779
+ )
2780
+
2781
+ fkeys = []
2782
+
2783
+ for (
2784
+ constraint_name,
2785
+ constrained_columns,
2786
+ referred_name,
2787
+ referred_columns,
2788
+ options,
2789
+ ) in parse_fks():
2790
+ sig = fk_sig(constrained_columns, referred_name, referred_columns)
2791
+ if sig not in keys_by_signature:
2792
+ util.warn(
2793
+ "WARNING: SQL-parsed foreign key constraint "
2794
+ "'%s' could not be located in PRAGMA "
2795
+ "foreign_keys for table %s" % (sig, table_name)
2796
+ )
2797
+ continue
2798
+ key = keys_by_signature.pop(sig)
2799
+ key["name"] = constraint_name
2800
+ key["options"] = options
2801
+ fkeys.append(key)
2802
+ # assume the remainders are the unnamed, inline constraints, just
2803
+ # use them as is as it's extremely difficult to parse inline
2804
+ # constraints
2805
+ fkeys.extend(keys_by_signature.values())
2806
+ if fkeys:
2807
+ return fkeys
2808
+ else:
2809
+ return ReflectionDefaults.foreign_keys()
2810
+
2811
+ def _find_cols_in_sig(self, sig):
2812
+ for match in re.finditer(r'(?:"(.+?)")|([a-z0-9_]+)', sig, re.I):
2813
+ yield match.group(1) or match.group(2)
2814
+
2815
+ @reflection.cache
2816
+ def get_unique_constraints(
2817
+ self, connection, table_name, schema=None, **kw
2818
+ ):
2819
+ auto_index_by_sig = {}
2820
+ for idx in self.get_indexes(
2821
+ connection,
2822
+ table_name,
2823
+ schema=schema,
2824
+ include_auto_indexes=True,
2825
+ **kw,
2826
+ ):
2827
+ if not idx["name"].startswith("sqlite_autoindex"):
2828
+ continue
2829
+ sig = tuple(idx["column_names"])
2830
+ auto_index_by_sig[sig] = idx
2831
+
2832
+ table_data = self._get_table_sql(
2833
+ connection, table_name, schema=schema, **kw
2834
+ )
2835
+ unique_constraints = []
2836
+
2837
+ def parse_uqs():
2838
+ if table_data is None:
2839
+ return
2840
+ UNIQUE_PATTERN = (
2841
+ r'(?:CONSTRAINT\s+(?:"(.+?)"|(\w+))\s+)?UNIQUE\s*\((.+?)\)'
2842
+ )
2843
+ INLINE_UNIQUE_PATTERN = (
2844
+ r'(?:(".+?")|(?:[\[`])?([a-z0-9_]+)(?:[\]`])?)[\t ]'
2845
+ r"+[a-z0-9_]+(?:[\t ]+[a-z0-9_]+)*?[\t ]+UNIQUE"
2846
+ )
2847
+
2848
+ for match in re.finditer(UNIQUE_PATTERN, table_data, re.I):
2849
+ quoted_name, unquoted_name, cols = match.group(1, 2, 3)
2850
+ name = quoted_name or unquoted_name
2851
+ yield name, list(self._find_cols_in_sig(cols))
2852
+
2853
+ # we need to match inlines as well, as we seek to differentiate
2854
+ # a UNIQUE constraint from a UNIQUE INDEX, even though these
2855
+ # are kind of the same thing :)
2856
+ for match in re.finditer(INLINE_UNIQUE_PATTERN, table_data, re.I):
2857
+ cols = list(
2858
+ self._find_cols_in_sig(match.group(1) or match.group(2))
2859
+ )
2860
+ yield None, cols
2861
+
2862
+ for name, cols in parse_uqs():
2863
+ sig = tuple(cols)
2864
+ if sig in auto_index_by_sig:
2865
+ auto_index_by_sig.pop(sig)
2866
+ parsed_constraint = {"name": name, "column_names": cols}
2867
+ unique_constraints.append(parsed_constraint)
2868
+ # NOTE: auto_index_by_sig might not be empty here,
2869
+ # the PRIMARY KEY may have an entry.
2870
+ if unique_constraints:
2871
+ return unique_constraints
2872
+ else:
2873
+ return ReflectionDefaults.unique_constraints()
2874
+
2875
+ @reflection.cache
2876
+ def get_check_constraints(self, connection, table_name, schema=None, **kw):
2877
+ table_data = self._get_table_sql(
2878
+ connection, table_name, schema=schema, **kw
2879
+ )
2880
+
2881
+ # Extract CHECK constraints by properly handling balanced parentheses
2882
+ # and avoiding false matches when CHECK/CONSTRAINT appear in table
2883
+ # names. See #12924 for context.
2884
+ #
2885
+ # SQLite supports 4 identifier quote styles (see
2886
+ # sqlite.org/lang_keywords.html):
2887
+ # - Double quotes "..." (standard SQL)
2888
+ # - Brackets [...] (MS Access/SQL Server compatibility)
2889
+ # - Backticks `...` (MySQL compatibility)
2890
+ # - Single quotes '...' (SQLite extension)
2891
+ #
2892
+ # NOTE: there is not currently a way to parse CHECK constraints that
2893
+ # contain newlines as the approach here relies upon each individual
2894
+ # CHECK constraint being on a single line by itself. This necessarily
2895
+ # makes assumptions as to how the CREATE TABLE was emitted.
2896
+ CHECK_PATTERN = re.compile(
2897
+ r"""
2898
+ (?<![A-Za-z0-9_]) # Negative lookbehind: ensure CHECK is not
2899
+ # part of an identifier (e.g., table name
2900
+ # like "tableCHECK")
2901
+
2902
+ (?: # Optional CONSTRAINT clause
2903
+ CONSTRAINT\s+
2904
+ ( # Group 1: Constraint name (quoted or unquoted)
2905
+ "(?:[^"]|"")+" # Double-quoted: "name" or "na""me"
2906
+ |'(?:[^']|'')+' # Single-quoted: 'name' or 'na''me'
2907
+ |\[(?:[^\]]|\]\])+\] # Bracket-quoted: [name] or [na]]me]
2908
+ |`(?:[^`]|``)+` # Backtick-quoted: `name` or `na``me`
2909
+ |\S+ # Unquoted: simple_name
2910
+ )
2911
+ \s+
2912
+ )?
2913
+
2914
+ CHECK\s*\( # CHECK keyword followed by opening paren
2915
+ """,
2916
+ re.VERBOSE | re.IGNORECASE,
2917
+ )
2918
+ cks = []
2919
+
2920
+ for match in re.finditer(CHECK_PATTERN, table_data or ""):
2921
+ constraint_name = match.group(1)
2922
+
2923
+ if constraint_name:
2924
+ # Remove surrounding quotes if present
2925
+ # Double quotes: "name" -> name
2926
+ # Single quotes: 'name' -> name
2927
+ # Brackets: [name] -> name
2928
+ # Backticks: `name` -> name
2929
+ constraint_name = re.sub(
2930
+ r'^(["\'`])(.+)\1$|^\[(.+)\]$',
2931
+ lambda m: m.group(2) or m.group(3),
2932
+ constraint_name,
2933
+ flags=re.DOTALL,
2934
+ )
2935
+
2936
+ # Find the matching closing parenthesis with quote-aware paren
2937
+ # counting. ``match.end() - 1`` is the position of the ``(``
2938
+ # that opened the CHECK clause; ``match.end()`` is the first
2939
+ # character of the constraint body.
2940
+ close = util.find_matching_paren(table_data, match.end() - 1)
2941
+ if close is not None:
2942
+ sqltext = table_data[match.end() : close].strip()
2943
+ cks.append({"sqltext": sqltext, "name": constraint_name})
2944
+
2945
+ cks.sort(key=lambda d: d["name"] or "~") # sort None as last
2946
+ if cks:
2947
+ return cks
2948
+ else:
2949
+ return ReflectionDefaults.check_constraints()
2950
+
2951
+ @reflection.cache
2952
+ def get_indexes(self, connection, table_name, schema=None, **kw):
2953
+ pragma_indexes = self._get_table_pragma(
2954
+ connection, "index_list", table_name, schema=schema
2955
+ )
2956
+ indexes = []
2957
+
2958
+ # regular expression to extract the filter predicate of a partial
2959
+ # index. this could fail to extract the predicate correctly on
2960
+ # indexes created like
2961
+ # CREATE INDEX i ON t (col || ') where') WHERE col <> ''
2962
+ # but as this function does not support expression-based indexes
2963
+ # this case does not occur.
2964
+ partial_pred_re = re.compile(r"\)\s+where\s+(.+)", re.IGNORECASE)
2965
+
2966
+ if schema:
2967
+ schema_expr = "%s." % self.identifier_preparer.quote_identifier(
2968
+ schema
2969
+ )
2970
+ else:
2971
+ schema_expr = ""
2972
+
2973
+ include_auto_indexes = kw.pop("include_auto_indexes", False)
2974
+ for row in pragma_indexes:
2975
+ # ignore implicit primary key index.
2976
+ # https://www.mail-archive.com/sqlite-users@sqlite.org/msg30517.html
2977
+ if not include_auto_indexes and row[1].startswith(
2978
+ "sqlite_autoindex"
2979
+ ):
2980
+ continue
2981
+ indexes.append(
2982
+ dict(
2983
+ name=row[1],
2984
+ column_names=[],
2985
+ unique=row[2],
2986
+ dialect_options={},
2987
+ )
2988
+ )
2989
+
2990
+ # check partial indexes
2991
+ if len(row) >= 5 and row[4]:
2992
+ s = (
2993
+ "SELECT sql FROM %(schema)ssqlite_master "
2994
+ "WHERE name = ? "
2995
+ "AND type = 'index'" % {"schema": schema_expr}
2996
+ )
2997
+ rs = connection.exec_driver_sql(s, (row[1],))
2998
+ index_sql = rs.scalar()
2999
+ predicate_match = partial_pred_re.search(index_sql)
3000
+ if predicate_match is None:
3001
+ # unless the regex is broken this case shouldn't happen
3002
+ # because we know this is a partial index, so the
3003
+ # definition sql should match the regex
3004
+ util.warn(
3005
+ "Failed to look up filter predicate of "
3006
+ "partial index %s" % row[1]
3007
+ )
3008
+ else:
3009
+ predicate = predicate_match.group(1)
3010
+ indexes[-1]["dialect_options"]["sqlite_where"] = text(
3011
+ predicate
3012
+ )
3013
+
3014
+ # loop thru unique indexes to get the column names.
3015
+ for idx in list(indexes):
3016
+ pragma_index = self._get_table_pragma(
3017
+ connection, "index_info", idx["name"], schema=schema
3018
+ )
3019
+
3020
+ for row in pragma_index:
3021
+ if row[2] is None:
3022
+ util.warn(
3023
+ "Skipped unsupported reflection of "
3024
+ "expression-based index %s" % idx["name"]
3025
+ )
3026
+ indexes.remove(idx)
3027
+ break
3028
+ else:
3029
+ idx["column_names"].append(row[2])
3030
+
3031
+ indexes.sort(key=lambda d: d["name"] or "~") # sort None as last
3032
+ if indexes:
3033
+ return indexes
3034
+ elif not self.has_table(connection, table_name, schema):
3035
+ raise exc.NoSuchTableError(
3036
+ f"{schema}.{table_name}" if schema else table_name
3037
+ )
3038
+ else:
3039
+ return ReflectionDefaults.indexes()
3040
+
3041
+ def _is_sys_table(self, table_name):
3042
+ return table_name in {
3043
+ "sqlite_schema",
3044
+ "sqlite_master",
3045
+ "sqlite_temp_schema",
3046
+ "sqlite_temp_master",
3047
+ }
3048
+
3049
+ @reflection.cache
3050
+ def _get_table_sql(self, connection, table_name, schema=None, **kw):
3051
+ if schema:
3052
+ schema_expr = "%s." % (
3053
+ self.identifier_preparer.quote_identifier(schema)
3054
+ )
3055
+ else:
3056
+ schema_expr = ""
3057
+ try:
3058
+ s = (
3059
+ "SELECT sql FROM "
3060
+ " (SELECT * FROM %(schema)ssqlite_master UNION ALL "
3061
+ " SELECT * FROM %(schema)ssqlite_temp_master) "
3062
+ "WHERE name = ? "
3063
+ "AND type in ('table', 'view')" % {"schema": schema_expr}
3064
+ )
3065
+ rs = connection.exec_driver_sql(s, (table_name,))
3066
+ except exc.DBAPIError:
3067
+ s = (
3068
+ "SELECT sql FROM %(schema)ssqlite_master "
3069
+ "WHERE name = ? "
3070
+ "AND type in ('table', 'view')" % {"schema": schema_expr}
3071
+ )
3072
+ rs = connection.exec_driver_sql(s, (table_name,))
3073
+ value = rs.scalar()
3074
+ if value is None and not self._is_sys_table(table_name):
3075
+ raise exc.NoSuchTableError(f"{schema_expr}{table_name}")
3076
+ return value
3077
+
3078
+ def _get_table_pragma(self, connection, pragma, table_name, schema=None):
3079
+ quote = self.identifier_preparer.quote_identifier
3080
+ if schema is not None:
3081
+ statements = [f"PRAGMA {quote(schema)}."]
3082
+ else:
3083
+ # because PRAGMA looks in all attached databases if no schema
3084
+ # given, need to specify "main" schema, however since we want
3085
+ # 'temp' tables in the same namespace as 'main', need to run
3086
+ # the PRAGMA twice
3087
+ statements = ["PRAGMA main.", "PRAGMA temp."]
3088
+
3089
+ qtable = quote(table_name)
3090
+ for statement in statements:
3091
+ statement = f"{statement}{pragma}({qtable})"
3092
+ cursor = connection.exec_driver_sql(statement)
3093
+ if not cursor._soft_closed:
3094
+ # work around SQLite issue whereby cursor.description
3095
+ # is blank when PRAGMA returns no rows:
3096
+ # https://www.sqlite.org/cvstrac/tktview?tn=1884
3097
+ result = cursor.fetchall()
3098
+ else:
3099
+ result = []
3100
+ if result:
3101
+ return result
3102
+ else:
3103
+ return []